pantheon-opencode 1.5.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,489 +0,0 @@
1
- #!/usr/bin/env python3
2
- """WS3 TOON codec (PR #94) — Token-Oriented Object Notation.
3
-
4
- Minimal deterministic encoding for board signals, checkpoints and KV
5
- payloads. Semantics identical to JSON; structurally smaller (no braces,
6
- no per-key quotes, no commas); JSON fallback when the parser is
7
- absent/fails.
8
-
9
- Only the standard library (``json`` + ``re``) — prohibited: external
10
- gateway, generative embedding.
11
-
12
- Encoding rules:
13
-
14
- - Scalars: ``None``/``True``/``False`` → ``null``/``true``/``false``;
15
- numbers → shortest form; strings bare when unambiguous, else
16
- JSON-quoted (quotes/backslash/newline, leading/trailing space,
17
- ``null``/``true``/``false``/numeric lookalikes, ``{}``/``[]``).
18
- - Dicts: one ``key: value`` line each; nested values on deeper indent
19
- (2 spaces); empty dict → ``{}``.
20
- - Lists of scalars: ``- item`` lines; empty list → ``[]``.
21
- - Lists of dicts with uniform keys: ``@table k1|k2`` header + ``v1|v2``
22
- rows (savings are per payload class — ~11% on content-dominated board
23
- records up to ~47% on large tabular checkpoints).
24
- - Dict values never need quoting for ``:`` (split is on the FIRST colon);
25
- list items / top-level scalars that look like ``key: ...`` are quoted.
26
- """
27
-
28
- from __future__ import annotations
29
-
30
- import json
31
- import re
32
- from dataclasses import dataclass
33
- from typing import Any
34
-
35
- TABLE_MARKER = "@table "
36
-
37
- #: Default cap on nested-block depth accepted by ``toon_decode`` (DoS guard:
38
- #: hostile deeply-nested input fails with ``ValueError``, never RecursionError).
39
- TOON_MAX_DEPTH = 100
40
-
41
- #: Default cap on input length accepted by ``toon_decode`` (DoS guard).
42
- TOON_MAX_CHARS = 1_000_000
43
-
44
- _BARE_KEY_RE = re.compile(r"^[A-Za-z0-9_][A-Za-z0-9_.@+~/-]*$")
45
- _INT_RE = re.compile(r"^-?\d+$")
46
- _FLOAT_RE = re.compile(r"^-?(?:\d+\.\d+|\d+\.\d*[eE][+-]?\d+|\d+[eE][+-]?\d+)$")
47
- _KEYLIKE_RE = re.compile(
48
- r'^(?:"(?:[^"\\]|\\.)*"|[A-Za-z0-9_][A-Za-z0-9_.@+~/\-]*)\s*:(?:\s+|$)'
49
- )
50
-
51
- _RESERVED_WORDS = frozenset({"null", "true", "false", "{}", "[]"})
52
-
53
-
54
- def _looks_numeric(text: str) -> bool:
55
- return bool(_INT_RE.match(text) or _FLOAT_RE.match(text))
56
-
57
-
58
- def _quote(text: str) -> str:
59
- return json.dumps(text, ensure_ascii=False)
60
-
61
-
62
- def _bare_value(text: str, *, in_table: bool = False) -> bool:
63
- """Whether a string value is unambiguous without quotes in VALUE position.
64
-
65
- In table cells (``in_table=True``) a bare ``|`` would split the row, so
66
- values containing ``|`` must be quoted.
67
- """
68
- if not text or text != text.strip():
69
- return False
70
- if text in _RESERVED_WORDS or _looks_numeric(text):
71
- return False
72
- if in_table and "|" in text:
73
- return False
74
- return not ('"' in text or "\\" in text or "\n" in text or "\r" in text)
75
-
76
-
77
- def _encode_scalar(
78
- value: Any, *, item_position: bool = False, table_cell: bool = False
79
- ) -> str:
80
- if value is None:
81
- return "null"
82
- if isinstance(value, bool):
83
- return "true" if value else "false"
84
- if isinstance(value, int):
85
- return str(value)
86
- if isinstance(value, float):
87
- return repr(value)
88
- text = str(value)
89
- if _bare_value(text, in_table=table_cell) and not (
90
- item_position and _KEYLIKE_RE.match(text)
91
- ):
92
- # "- key: ..." reads as a dict item — quote to keep the string.
93
- return text
94
- return _quote(text)
95
-
96
-
97
- def _encode_key(key: str) -> str:
98
- if _BARE_KEY_RE.match(key):
99
- return key
100
- return _quote(key)
101
-
102
-
103
- def _is_uniform_dict_list(value: Any) -> bool:
104
- if not isinstance(value, list) or not value:
105
- return False
106
- if not all(isinstance(item, dict) for item in value):
107
- return False
108
- first_keys = list(value[0].keys())
109
- if not first_keys:
110
- return False
111
- return all(list(item.keys()) == first_keys for item in value)
112
-
113
-
114
- def _split_cells(line: str) -> list[str]:
115
- """Split a table row on ``|`` outside double quotes."""
116
- cells: list[str] = []
117
- current: list[str] = []
118
- in_quotes = False
119
- escaped = False
120
- for char in line:
121
- if escaped:
122
- current.append(char)
123
- escaped = False
124
- elif char == "\\" and in_quotes:
125
- current.append(char)
126
- escaped = True
127
- elif char == '"':
128
- current.append(char)
129
- in_quotes = not in_quotes
130
- elif char == "|" and not in_quotes:
131
- cells.append("".join(current))
132
- current = []
133
- else:
134
- current.append(char)
135
- cells.append("".join(current))
136
- return cells
137
-
138
-
139
- def toon_encode(value: Any) -> str:
140
- """Encode ``value`` to a TOON string (deterministic, no spaces wasted)."""
141
- return "\n".join(_encode_value(value, 0))
142
-
143
-
144
- def _encode_value(value: Any, indent: int) -> list[str]:
145
- pad = " " * indent
146
- if value is None or isinstance(value, (bool, int, float, str)):
147
- return [pad + _encode_scalar(value)]
148
- if isinstance(value, dict):
149
- return _encode_dict(value, pad, indent)
150
- if isinstance(value, list):
151
- return _encode_list(value, pad, indent)
152
- raise TypeError(f"TOON cannot encode {type(value).__name__}")
153
-
154
-
155
- def _encode_dict(value: dict[Any, Any], pad: str, indent: int) -> list[str]:
156
- if not value:
157
- return [pad + "{}"]
158
- lines: list[str] = []
159
- for key, item in value.items():
160
- encoded_key = _encode_key(str(key))
161
- if item is None or isinstance(item, (bool, int, float, str)):
162
- lines.append(f"{pad}{encoded_key}: {_encode_scalar(item)}")
163
- elif isinstance(item, dict) and not item:
164
- lines.append(f"{pad}{encoded_key}: {{}}")
165
- elif isinstance(item, list) and not item:
166
- lines.append(f"{pad}{encoded_key}: []")
167
- else:
168
- lines.append(f"{pad}{encoded_key}:")
169
- lines.extend(_encode_value(item, indent + 2))
170
- return lines
171
-
172
-
173
- def _encode_list(value: list[Any], pad: str, indent: int) -> list[str]:
174
- if not value:
175
- return [pad + "[]"]
176
- if _is_uniform_dict_list(value):
177
- return _encode_table(value, pad)
178
- lines: list[str] = []
179
- for item in value:
180
- lines.extend(_encode_item(item, indent))
181
- return lines
182
-
183
-
184
- def _encode_table(value: list[Any], pad: str) -> list[str]:
185
- first = value[0]
186
- assert isinstance(first, dict)
187
- keys = list(first.keys())
188
- lines = [pad + TABLE_MARKER + "|".join(_encode_key(str(k)) for k in keys)]
189
- for item in value:
190
- assert isinstance(item, dict)
191
- cells = [
192
- _encode_scalar(item[k], item_position=True, table_cell=True) for k in keys
193
- ]
194
- lines.append(pad + "|".join(cells))
195
- return lines
196
-
197
-
198
- def _encode_item(item: Any, indent: int) -> list[str]:
199
- pad = " " * indent
200
- if item is None or isinstance(item, (bool, int, float, str)):
201
- return [f"{pad}- {_encode_scalar(item, item_position=True)}"]
202
- if isinstance(item, dict) and not item:
203
- return [f"{pad}- {{}}"]
204
- if isinstance(item, list) and not item:
205
- return [f"{pad}- []"]
206
- sub = _encode_value(item, indent + 2)
207
- first = sub[0].strip()
208
- return [f"{pad}- {first}", *sub[1:]]
209
-
210
-
211
- def _parse_scalar(token: str) -> Any:
212
- if token == "null":
213
- return None
214
- if token in {"true", "false"}:
215
- return token == "true"
216
- if token in {"{}", "[]"}:
217
- return {} if token == "{}" else []
218
- if _looks_numeric(token):
219
- return int(token) if _INT_RE.match(token) else float(token)
220
- if token.startswith('"'):
221
- return json.loads(token)
222
- return token
223
-
224
-
225
- def _indent_of(line: str) -> int:
226
- return len(line) - len(line.lstrip(" "))
227
-
228
-
229
- def _split_key_value(text: str) -> tuple[str, str]:
230
- """Split ``key: value`` on the FIRST colon (values may contain colons)."""
231
- if text.startswith('"'):
232
- try:
233
- key, end = json.JSONDecoder().raw_decode(text)
234
- except json.JSONDecodeError as exc:
235
- raise ValueError(f"TOON: line without ':' separator: {text!r}") from exc
236
- rest_text = text[end:].lstrip()
237
- if not rest_text.startswith(":"):
238
- raise ValueError(f"TOON: line without ':' separator: {text!r}")
239
- if not isinstance(key, str) or not key:
240
- raise ValueError(f"TOON: empty key in line: {text!r}")
241
- return key, rest_text[1:].strip()
242
- idx = text.find(":")
243
- if idx < 0:
244
- raise ValueError(f"TOON: line without ':' separator: {text!r}")
245
- raw_key = text[:idx].strip()
246
- if not raw_key:
247
- raise ValueError(f"TOON: empty key in line: {text!r}")
248
- key = raw_key
249
- rest = text[idx + 1 :].strip()
250
- return key, rest
251
-
252
-
253
- def toon_decode(
254
- text: str, *, max_depth: int = TOON_MAX_DEPTH, max_chars: int = TOON_MAX_CHARS
255
- ) -> Any:
256
- """Decode a TOON string back to the identical value (raises ValueError).
257
-
258
- ``max_depth``/``max_chars`` are DoS guards: oversized inputs fail with a
259
- controlled ``ValueError`` instead of ``RecursionError``/memory blowup.
260
- """
261
- if len(text) > max_chars:
262
- raise ValueError(
263
- f"TOON: input too large ({len(text)} chars > max_chars={max_chars})"
264
- )
265
- lines = [line for line in text.split("\n") if line.strip() != ""]
266
- if not lines:
267
- raise ValueError("TOON: empty input")
268
- stripped = lines[0].strip()
269
- if stripped.startswith("{") or stripped.startswith("["):
270
- try:
271
- return json.loads(text)
272
- except json.JSONDecodeError as exc:
273
- raise ValueError(f"TOON: invalid JSON fallback: {exc}") from exc
274
- value, next_idx = _parse_block(lines, 0, 0, 0, max_depth)
275
- if next_idx != len(lines):
276
- raise ValueError(f"TOON: trailing content at line {next_idx + 1}")
277
- return value
278
-
279
-
280
- def _parse_block(
281
- lines: list[str], idx: int, indent: int, depth: int, max_depth: int
282
- ) -> tuple[Any, int]:
283
- if depth > max_depth:
284
- raise ValueError(f"TOON: max_depth={max_depth} exceeded")
285
- stripped = lines[idx].strip()
286
- if stripped.startswith("@table "):
287
- return _parse_table(lines, idx, indent)
288
- if stripped.startswith("- ") or stripped == "-":
289
- return _parse_list(lines, idx, indent, depth, max_depth)
290
- return _parse_dict(lines, idx, indent, depth, max_depth)
291
-
292
-
293
- def _parse_table(
294
- lines: list[str], idx: int, indent: int
295
- ) -> tuple[list[dict[str, Any]], int]:
296
- header = lines[idx].strip()[len(TABLE_MARKER) :]
297
- keys = [
298
- _parse_scalar(cell) if cell.startswith('"') else cell
299
- for cell in _split_cells(header)
300
- ]
301
- if not keys or any(not k for k in keys):
302
- raise ValueError(f"TOON: bad @table header at line {idx + 1}")
303
- rows: list[dict[str, Any]] = []
304
- idx += 1
305
- while idx < len(lines) and _indent_of(lines[idx]) == indent:
306
- row_stripped = lines[idx].strip()
307
- if row_stripped.startswith(("@table ", "- ")) or row_stripped == "-":
308
- break
309
- if ":" in row_stripped and _KEYLIKE_RE.match(row_stripped):
310
- break
311
- cells = _split_cells(row_stripped)
312
- if len(cells) != len(keys):
313
- raise ValueError(f"TOON: row width mismatch at line {idx + 1}")
314
- rows.append({k: _parse_scalar(c) for k, c in zip(keys, cells, strict=True)})
315
- idx += 1
316
- return rows, idx
317
-
318
-
319
- def _parse_dict(
320
- lines: list[str], idx: int, indent: int, depth: int, max_depth: int
321
- ) -> tuple[dict[str, Any], int]:
322
- result: dict[str, Any] = {}
323
- while idx < len(lines):
324
- line = lines[idx]
325
- if line.strip() == "" or _indent_of(line) != indent:
326
- break
327
- stripped = line.strip()
328
- if stripped.startswith(("@table ", "- ")) or stripped == "-":
329
- break
330
- key, rest = _split_key_value(stripped)
331
- if rest == "":
332
- idx += 1
333
- if idx >= len(lines) or _indent_of(lines[idx]) <= indent:
334
- raise ValueError(f"TOON: missing nested block for key {key!r}")
335
- nested, idx = _parse_block(
336
- lines, idx, _indent_of(lines[idx]), depth + 1, max_depth
337
- )
338
- result[key] = nested
339
- else:
340
- result[key] = _parse_scalar(rest)
341
- idx += 1
342
- return result, idx
343
-
344
-
345
- @dataclass
346
- class _ListParseContext:
347
- """Shared cursor for ``- key: value`` list-item parsing (PLR0913)."""
348
-
349
- lines: list[str]
350
- indent: int
351
- depth: int
352
- max_depth: int
353
-
354
-
355
- def _parse_list_continuation(
356
- ctx: _ListParseContext, idx: int, item: dict[str, Any]
357
- ) -> int:
358
- """Consume ``indent+`` continuation lines (``key: value``) of a dict item."""
359
- lines, indent, depth, max_depth = ctx.lines, ctx.indent, ctx.depth, ctx.max_depth
360
- while idx < len(lines) and _indent_of(lines[idx]) > indent:
361
- cont = lines[idx].strip()
362
- sub_key, sub_rest = _split_key_value(cont)
363
- if sub_rest == "":
364
- idx += 1
365
- if idx >= len(lines) or _indent_of(lines[idx]) <= _indent_of(
366
- lines[idx - 1]
367
- ):
368
- raise ValueError(f"TOON: missing nested block for key {sub_key!r}")
369
- nested, idx = _parse_block(
370
- lines, idx, _indent_of(lines[idx]), depth + 1, max_depth
371
- )
372
- item[sub_key] = nested
373
- else:
374
- item[sub_key] = _parse_scalar(sub_rest)
375
- idx += 1
376
- return idx
377
-
378
-
379
- def _parse_list_dict_item(
380
- ctx: _ListParseContext, idx: int, rest: str
381
- ) -> tuple[dict[str, Any], int]:
382
- """Parse a ``- key: value`` dict item plus its continuation lines."""
383
- lines, indent, depth, max_depth = ctx.lines, ctx.indent, ctx.depth, ctx.max_depth
384
- key, sub_rest = _split_key_value(rest)
385
- item: dict[str, Any] = {}
386
- if sub_rest == "":
387
- idx += 1
388
- if idx >= len(lines) or _indent_of(lines[idx]) <= indent:
389
- raise ValueError(f"TOON: missing nested block for key {key!r}")
390
- nested, idx = _parse_block(
391
- lines, idx, _indent_of(lines[idx]), depth + 1, max_depth
392
- )
393
- item[key] = nested
394
- else:
395
- item[key] = _parse_scalar(sub_rest)
396
- idx += 1
397
- idx = _parse_list_continuation(ctx, idx, item)
398
- return item, idx
399
-
400
-
401
- def _parse_list(
402
- lines: list[str], idx: int, indent: int, depth: int, max_depth: int
403
- ) -> tuple[list[Any], int]:
404
- items: list[Any] = []
405
- while idx < len(lines):
406
- line = lines[idx]
407
- if _indent_of(line) != indent:
408
- break
409
- stripped = line.strip()
410
- if not (stripped.startswith("- ") or stripped == "-"):
411
- break
412
- rest = stripped[1:].strip()
413
- if rest == "":
414
- idx += 1
415
- if idx >= len(lines) or _indent_of(lines[idx]) <= indent:
416
- raise ValueError(
417
- f"TOON: missing nested block in list at line {idx + 1}"
418
- )
419
- nested, idx = _parse_block(
420
- lines, idx, _indent_of(lines[idx]), depth + 1, max_depth
421
- )
422
- items.append(nested)
423
- continue
424
- if _KEYLIKE_RE.match(rest):
425
- ctx = _ListParseContext(lines, indent, depth, max_depth)
426
- item, idx = _parse_list_dict_item(ctx, idx, rest)
427
- items.append(item)
428
- else:
429
- items.append(_parse_scalar(rest))
430
- idx += 1
431
- return items, idx
432
-
433
-
434
- def toon_decode_auto(
435
- text: str, *, max_depth: int = TOON_MAX_DEPTH, max_chars: int = TOON_MAX_CHARS
436
- ) -> Any:
437
- """Decode TOON, falling back to JSON (mirrors "parser absent" mode).
438
-
439
- Raises ValueError with a consistent ``TOON:`` message when the input is
440
- neither valid TOON nor valid JSON (never leaks the raw JSON error).
441
- """
442
- stripped = text.strip()
443
- if stripped.startswith("{") or stripped.startswith("["):
444
- try:
445
- return json.loads(text)
446
- except json.JSONDecodeError as exc:
447
- raise ValueError(f"TOON: invalid JSON fallback: {exc}") from exc
448
- try:
449
- return toon_decode(text, max_depth=max_depth, max_chars=max_chars)
450
- except ValueError:
451
- pass
452
- try:
453
- return json.loads(text)
454
- except json.JSONDecodeError as exc:
455
- raise ValueError(
456
- f"TOON: invalid input (neither TOON nor JSON): {stripped[:60]!r}"
457
- ) from exc
458
-
459
-
460
- def _chars_to_tokens(chars: int) -> int:
461
- """Deterministic token estimate: ``ceil(chars / 4)`` (WS3 metering basis)."""
462
- if chars <= 0:
463
- return 0
464
- return -(-chars // 4)
465
-
466
-
467
- def toon_size_report(value: Any) -> dict[str, Any]:
468
- """Compare JSON vs TOON sizes for ``value``.
469
-
470
- Reports BOTH chars and tokens: token ratios on tiny payloads suffer
471
- ceiling bias (``ceil`` rounds 161→41 vs 143→36), so the char columns are
472
- the honest basis and the token columns show what metering will debit.
473
- """
474
- compact_json = json.dumps(value, ensure_ascii=False, separators=(",", ":"))
475
- toon = toon_encode(value)
476
- ratio = len(toon) / len(compact_json) if compact_json else 1.0
477
- json_tokens = _chars_to_tokens(len(compact_json))
478
- toon_tokens = _chars_to_tokens(len(toon))
479
- token_ratio = toon_tokens / json_tokens if json_tokens else 1.0
480
- return {
481
- "json_chars": len(compact_json),
482
- "toon_chars": len(toon),
483
- "ratio": round(ratio, 4),
484
- "saved_pct": round((1.0 - ratio) * 100.0, 2),
485
- "json_tokens": json_tokens,
486
- "toon_tokens": toon_tokens,
487
- "token_ratio": round(token_ratio, 4),
488
- "token_saved_pct": round((1.0 - token_ratio) * 100.0, 2),
489
- }