from __future__ import annotations
"""
Core conversion utilities for TOON <-> JSON and system prompt handling.
This module is intentionally small and dependency-light: it relies on the
`toons` library, which implements the official TOON format with an API that
mirrors Python's standard `json` module.
"""
from typing import Any
import json
import toons
def json_to_toon(data: Any, *, indent: int | None = None) -> str:
"""
Convert a Python object (typically parsed from JSON) into TOON text.
Parameters
----------
data:
Any JSON-serialisable Python object (dict, list, str, int, float, bool, None).
indent:
Optional indentation level to pass through to `toons.dumps`. Using a small
non-zero indent (for example 2) can make the output more readable while still
remaining token-efficient. If ``None`` (the default), `toons` chooses its
own compact representation.
Returns
-------
str
TOON representation of the input data.
Raises
------
TypeError
If ``data`` cannot be serialised as JSON (e.g. contains non‑serialisable
objects such as open file handles, sets, or custom classes).
"""
# Validate that the object is at least JSON-serialisable. This provides
# a clear and familiar error message instead of letting lower-level
# TOON errors surface directly.
try:
json.dumps(data)
except (TypeError, ValueError) as exc: # pragma: no cover - message is what's important
raise TypeError(
"json_to_toon expected a JSON-serialisable object "
"(dict, list, str, int, float, bool, or None). "
f"Got {type(data).__name__}: {exc}"
) from exc
if indent is not None and not isinstance(indent, int):
raise TypeError(
"json_to_toon 'indent' must be an int number of spaces or None, "
f"got {type(indent).__name__}."
)
# `toons.dumps` expects `indent` to be an integer when provided; passing
# `None` through directly can raise a TypeError in some versions, so we
# only include the argument when it is explicitly set.
if indent is None:
return toons.dumps(data)
return toons.dumps(data, indent=indent)
def toon_to_json(text: str) -> Any:
"""
Parse TOON text into a Python object that can be serialised as JSON.
Parameters
----------
text:
TOON-formatted string, as produced by :func:`json_to_toon` or any other
TOON-compliant producer.
Returns
-------
Any
Python object representation of the input TOON.
Raises
------
TypeError
If ``text`` is not a string.
ValueError
If the TOON text is invalid and cannot be parsed.
"""
if not isinstance(text, str):
raise TypeError(
"toon_to_json expected 'text' to be a str containing TOON data, "
f"got {type(text).__name__}."
)
try:
return toons.loads(text)
except Exception as exc: # pragma: no cover - error type may vary by toons version
raise ValueError(f"Failed to parse TOON text: {exc}") from exc
def system_prompt_to_toon(prompt: str) -> str:
"""
Wrap a system prompt in a minimal TOON structure.
The prompt is represented as a small TOON document with a single field
named ``system_prompt``. This keeps things simple and explicit while
still benefiting from TOON's compact syntax for larger prompts.
Parameters
----------
prompt:
Raw system prompt text.
Returns
-------
str
TOON-formatted representation of the prompt.
Raises
------
TypeError
If ``prompt`` is not a string.
"""
if not isinstance(prompt, str):
raise TypeError(
"system_prompt_to_toon expected 'prompt' to be a string, "
f"got {type(prompt).__name__}."
)
payload = {"system_prompt": prompt}
return json_to_toon(payload)