diff --git a/examples/jiter/README.md b/examples/jiter/README.md
new file mode 100644
index 0000000..16eeab7
--- /dev/null
+++ b/examples/jiter/README.md
@@ -0,0 +1,18 @@
+# jiter Examples
+
+Each sub-directory contains a self-contained example. The order in
+which the examples are to appear is specified in `order.json` (an
+array of directory names in the expected order).
+
+In each example directory you'll find:
+
+* `config.toml` - must conform to the specification outlined here:
+ https://docs.pyscript.net/latest/user-guide/configuration/ This is
+ parsed and ultimately turned into a JSON representation as part of
+ the package's API object.
+* `setup.py` - Python code for contextual and environmental setup,
+ NOT SEEN BY THE END USER, but is run before the `code.py` code is
+ evaluated. Allows us to create useful (IPython) shims, avoid
+ repeating boilerplate and whatnot.
+* `code.py` - the actual code added to the editor which forms the
+ practical example of using the package.
diff --git a/examples/jiter/from_json_basics/code.py b/examples/jiter/from_json_basics/code.py
new file mode 100644
index 0000000..1c051b5
--- /dev/null
+++ b/examples/jiter/from_json_basics/code.py
@@ -0,0 +1,54 @@
+"""
+A first look at jiter, a fast JSON parser from the Pydantic team.
+
+The whole API is essentially one function: `jiter.from_json`. It takes
+a `bytes` object (not a `str`) and returns native Python values, just
+like the standard library's `json.loads`. See the project page for
+details: https://github.com/pydantic/jiter
+"""
+from IPython.core.display import display, HTML
+
+import jiter
+
+
+# jiter.from_json expects bytes -- note the leading `b`.
+weather_report = b"""
+{
+ "station": "Kew Gardens",
+ "recorded_at": "2026-04-12T09:00:00Z",
+ "temperature_c": 14.2,
+ "humidity_pct": 71,
+ "observers": ["Ada", "Grace", "Lin"],
+ "is_raining": false
+}
+"""
+
+heading("1. Parse JSON bytes into a Python dict")
+note(
+ "jiter returns ordinary Python values: dicts, lists, strings, "
+ "numbers, booleans, and None. Below, we parse a small weather "
+ "report and pull values out by key."
+)
+
+report = jiter.from_json(weather_report)
+
+display(report, append=True)
+note(
+ f"Station: {report['station']}. "
+ f"Temperature: {report['temperature_c']} °C. "
+ f"Observers: {', '.join(report['observers'])}."
+)
+
+heading("2. The string cache")
+note(
+ "By default, jiter caches parsed strings to speed up repeated "
+ "keys and values. You can inspect and reset that cache."
+)
+
+# Parse a few records that share keys, so the cache gets a workout.
+for _ in range(5):
+ jiter.from_json(weather_report)
+
+note(f"String cache size after parsing: {jiter.cache_usage()} bytes.")
+jiter.cache_clear()
+note(f"After cache_clear(): {jiter.cache_usage()} bytes.")
diff --git a/examples/jiter/from_json_basics/config.toml b/examples/jiter/from_json_basics/config.toml
new file mode 100644
index 0000000..9fecf48
--- /dev/null
+++ b/examples/jiter/from_json_basics/config.toml
@@ -0,0 +1 @@
+packages = ["jiter"]
diff --git a/examples/jiter/from_json_basics/setup.py b/examples/jiter/from_json_basics/setup.py
new file mode 100644
index 0000000..b4f3ee1
--- /dev/null
+++ b/examples/jiter/from_json_basics/setup.py
@@ -0,0 +1,41 @@
+"""
+Shim IPython's display API onto PyScript so example code written in a
+Jupyter/IPython idiom runs unmodified in the browser.
+"""
+
+import sys
+import types
+import js
+from pyscript import window, HTML, display as _display
+
+js.alert = window.alert
+
+
+def display(*args, **kwargs):
+ """Wrap pyscript.display so output lands in the example target."""
+ return _display(
+ *args, **kwargs, target=__pyscript_display_target__,
+ )
+
+
+ipython = types.ModuleType("IPython")
+core = types.ModuleType("IPython.core")
+core_display = types.ModuleType("IPython.core.display")
+core_display.display = display
+core_display.HTML = HTML
+ipython.core = core
+core.display = core_display
+ipython.get_ipython = lambda: None
+ipython.display = core_display
+sys.modules["IPython"] = ipython
+sys.modules["IPython.core"] = core
+sys.modules["IPython.core.display"] = core_display
+sys.modules["IPython.display"] = core_display
+
+
+def heading(text, level=2):
+ display(HTML(f"
{text}
"), append=True) diff --git a/examples/jiter/order.json b/examples/jiter/order.json new file mode 100644 index 0000000..f2acbe0 --- /dev/null +++ b/examples/jiter/order.json @@ -0,0 +1,5 @@ +[ + "from_json_basics", + "partial_json_streaming", + "precision_and_duplicates" +] diff --git a/examples/jiter/partial_json_streaming/code.py b/examples/jiter/partial_json_streaming/code.py new file mode 100644 index 0000000..8bd0859 --- /dev/null +++ b/examples/jiter/partial_json_streaming/code.py @@ -0,0 +1,46 @@ +# --------------------------------------------------------------------- +# Partial JSON: parsing a stream that hasn't finished arriving. +# --------------------------------------------------------------------- + +heading("Parsing partial JSON as it streams in") +note( + "When you're reading JSON from a network response (think: an LLM " + "token stream), you often want to render what you have so far. " + "jiter'spartial_mode handles a truncated payload "
+ "without raising."
+)
+
+# Imagine these are progressively longer prefixes of a single response
+# arriving over the wire, one chunk at a time.
+full_response = (
+ b'{"model": "demo-1", "choices": ['
+ b'{"role": "assistant", "content": "Hello, traveller! Welcome to jiter."}'
+ b']}'
+)
+checkpoints = [full_response[:n] for n in (20, 55, 95, len(full_response))]
+
+heading("Default: incomplete input is an error", level=3)
+try:
+ jiter.from_json(checkpoints[1])
+except ValueError as exc:
+ note(f"Got ValueError: {exc}")
+
+heading("partial_mode=True: drop the trailing incomplete value", level=3)
+note(
+ "Useful when you want a clean, fully-formed object so far. The "
+ "still-arriving last field is silently omitted."
+)
+for i, chunk in enumerate(checkpoints, start=1):
+ parsed = jiter.from_json(chunk, partial_mode=True)
+ display(HTML(f"After chunk {i} ({len(chunk)} bytes):"), append=True)
+ display(parsed, append=True)
+
+heading('partial_mode="trailing-strings": keep the in-flight string', level=3)
+note(
+ "Perfect for showing a token-by-token assistant reply: the "
+ "partially-received string is included as-is."
+)
+for i, chunk in enumerate(checkpoints, start=1):
+ parsed = jiter.from_json(chunk, partial_mode="trailing-strings")
+ display(HTML(f"After chunk {i}:"), append=True)
+ display(parsed, append=True)
diff --git a/examples/jiter/partial_json_streaming/config.toml b/examples/jiter/partial_json_streaming/config.toml
new file mode 100644
index 0000000..9fecf48
--- /dev/null
+++ b/examples/jiter/partial_json_streaming/config.toml
@@ -0,0 +1 @@
+packages = ["jiter"]
diff --git a/examples/jiter/partial_json_streaming/setup.py b/examples/jiter/partial_json_streaming/setup.py
new file mode 100644
index 0000000..cedc558
--- /dev/null
+++ b/examples/jiter/partial_json_streaming/setup.py
@@ -0,0 +1,20 @@
+"""Lighter setup for the second example: same names, no IPython shim."""
+import js
+from pyscript import window, HTML, display as _display
+
+js.alert = window.alert
+
+
+def display(*args, **kwargs):
+ return _display(*args, **kwargs, target=__pyscript_display_target__)
+
+
+def heading(text, level=2):
+ display(HTML(f"{text}
"), append=True) + + +import jiter diff --git a/examples/jiter/precision_and_duplicates/code.py b/examples/jiter/precision_and_duplicates/code.py new file mode 100644 index 0000000..9e08eb4 --- /dev/null +++ b/examples/jiter/precision_and_duplicates/code.py @@ -0,0 +1,59 @@ +# --------------------------------------------------------------------- +# Numeric precision and strict-mode options. +# --------------------------------------------------------------------- +import jiter +from decimal import Decimal + + +heading("Decimals and duplicate-key detection") +note( + "When JSON carries money or scientific values, the default " + "float conversion can lose precision. jiter lets "
+ "you opt into Decimal output. Separately, "
+ "catch_duplicate_keys turns a silent overwrite "
+ "into a loud error."
+)
+
+# A ledger entry whose amount has more digits than a 64-bit float
+# can faithfully store.
+ledger_entry = b'{"account": "ACME-001", "amount": 1234567890.1234567890}'
+
+heading("Default: amount comes back as a float", level=3)
+default_parse = jiter.from_json(ledger_entry)
+display(default_parse, append=True)
+note(
+ f"Type: {type(default_parse['amount']).__name__}. "
+ f"Notice the trailing digits have been rounded."
+)
+
+heading('float_mode="decimal": exact decimal arithmetic', level=3)
+decimal_parse = jiter.from_json(ledger_entry, float_mode="decimal")
+display(decimal_parse, append=True)
+note(
+ f"Type: {type(decimal_parse['amount']).__name__}. "
+ f"Now we can sum many of these without drift."
+)
+
+# Add up 1000 copies to show Decimal stays exact.
+total = sum(
+ jiter.from_json(ledger_entry, float_mode="decimal")["amount"]
+ for _ in range(1000)
+)
+note(f"Sum of 1000 entries (Decimal): {total}")
+
+heading("Catching duplicate keys", level=3)
+note(
+ "By default, JSON parsers silently let later keys win. That can "
+ "hide bugs in upstream data. Pass catch_duplicate_keys=True "
+ "to fail fast instead."
+)
+
+suspect_payload = b'{"user_id": 1, "user_id": 2, "name": "Robin"}'
+
+permissive = jiter.from_json(suspect_payload)
+note(f"Permissive parse (last value wins): {permissive}")
+
+try:
+ jiter.from_json(suspect_payload, catch_duplicate_keys=True)
+except ValueError as exc:
+ note(f"Strict parse raised ValueError: {exc}")
diff --git a/examples/jiter/precision_and_duplicates/config.toml b/examples/jiter/precision_and_duplicates/config.toml
new file mode 100644
index 0000000..9fecf48
--- /dev/null
+++ b/examples/jiter/precision_and_duplicates/config.toml
@@ -0,0 +1 @@
+packages = ["jiter"]
diff --git a/examples/jiter/precision_and_duplicates/setup.py b/examples/jiter/precision_and_duplicates/setup.py
new file mode 100644
index 0000000..0bf3fc9
--- /dev/null
+++ b/examples/jiter/precision_and_duplicates/setup.py
@@ -0,0 +1,18 @@
+"""Lighter setup: same names, no IPython shim."""
+import js
+from pyscript import window, HTML, display as _display
+
+js.alert = window.alert
+
+
+def display(*args, **kwargs):
+ return _display(*args, **kwargs, target=__pyscript_display_target__)
+
+
+def heading(text, level=2):
+ display(HTML(f"{text}
"), append=True) +