Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -410,9 +410,9 @@ jambonz.say(text="Connecting you now.").dial(

## SDK Architecture

The SDK auto-generates verb methods from `specs.json` (from `@jambonz/verb-specifications`). When the spec changes, the SDK automatically picks up new parameters:
The SDK auto-generates verb methods from JSON Schema files (from `@jambonz/schema`). When the schema changes, the SDK automatically picks up new parameters:

1. `specs.json` — bundled verb/component specifications (synced from upstream)
1. `schema/verbs/*.schema.json` — bundled verb schemas (synced from upstream)
2. `verb_registry.py` — maps spec entries to Python methods + synonyms
3. `verb_builder.py` — generates methods at import time from specs + registry
4. `WebhookResponse` and `Session` both extend `VerbBuilder`
Expand Down
6 changes: 3 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ src/jambonz_sdk/
│ ├── verbs.py # All 26+ verb TypedDicts
│ ├── rest.py # REST API request/response types
│ └── session.py # Call session & WebSocket message types
├── verb_builder.py # VerbBuilder — methods auto-generated from specs.json
├── verb_builder.py # VerbBuilder — methods auto-generated from JSON Schema
├── verb_registry.py # Verb definitions: maps spec entries → Python methods
├── webhook/
│ ├── __init__.py
Expand All @@ -48,12 +48,12 @@ src/jambonz_sdk/
- **Transport-agnostic verb building**: Same verb methods on both `WebhookResponse` and `Session`
- **Fluent/chainable API**: All verb methods return `self` for method chaining
- **TypedDict for verb schemas**: Type-safe verb construction matching JSON schemas exactly
- **Auto-generated verb methods**: VerbBuilder methods are generated at import time from `specs.json` + `verb_registry.py` — when the spec changes, the SDK automatically picks up new parameters
- **Auto-generated verb methods**: VerbBuilder methods are generated at import time from JSON Schema files (`@jambonz/schema`) + `verb_registry.py` — when the schema changes, the SDK automatically picks up new parameters
- **aiohttp for both HTTP and WebSocket**: Single dependency for REST client and WS transport

## Verb System

The SDK supports all 26+ jambonz verbs. Verb methods on VerbBuilder are **auto-generated** from the shared `specs.json` (in `/Users/xhoaluu/jambonz/verb-specifications/specs.json`).
The SDK supports all 26+ jambonz verbs. Verb methods on VerbBuilder are **auto-generated** from JSON Schema files bundled from [`@jambonz/schema`](https://github.com/jambonz/schema).

### How verb generation works

Expand Down
43 changes: 35 additions & 8 deletions scripts/generate_stubs.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
"""Generate verb_builder.pyi stub file from specs.json + verb_registry.
"""Generate verb_builder.pyi stub file from JSON Schema + verb_registry.

This creates a .pyi type stub that IDEs (VS Code Pylance, PyCharm, mypy)
read for static type checking and autocomplete. Run this after syncing
specs.json or updating verb_registry.py.
the schema or updating verb_registry.py.

Usage:
python scripts/generate_stubs.py
Expand All @@ -19,10 +19,10 @@

from jambonz_sdk.verb_registry import VERB_DEFS

SPECS_PATH = SRC_DIR / "jambonz_sdk" / "specs.json"
SCHEMA_DIR = SRC_DIR / "jambonz_sdk" / "schema" / "verbs"
STUB_PATH = SRC_DIR / "jambonz_sdk" / "verb_builder.pyi"

# Maps specs.json type strings to Python type annotation strings for .pyi
# Maps JSON Schema type strings to Python type annotation strings for .pyi
TYPE_MAP = {
"string": "str",
"number": "int | float",
Expand All @@ -33,7 +33,7 @@


def resolve_type(spec_type) -> str:
"""Convert a specs.json type descriptor to a .pyi type string."""
"""Convert a JSON Schema type descriptor to a .pyi type string."""
if isinstance(spec_type, str):
if spec_type.startswith("#"):
return "dict[str, Any]"
Expand Down Expand Up @@ -61,9 +61,37 @@ def resolve_type(spec_type) -> str:
return "Any"


def _load_schemas() -> dict:
"""Load verb JSON Schemas from the bundled schema directory."""
schemas: dict = {}
for schema_file in sorted(SCHEMA_DIR.glob("*.schema.json")):
with schema_file.open() as f:
schema = json.load(f)
schema_id = schema.get("$id", "")
if schema_id:
spec_name = schema_id.rsplit("/", 1)[-1]
else:
spec_name = schema_file.stem.replace(".schema", "")
properties = {}
for prop_name, prop_def in schema.get("properties", {}).items():
if prop_name == "verb":
continue
properties[prop_name] = prop_def
for entry in schema.get("allOf", []):
if "properties" in entry:
for prop_name, prop_def in entry["properties"].items():
if prop_name == "verb":
continue
properties[prop_name] = prop_def
schemas[spec_name] = {
"properties": properties,
"required": schema.get("required", []),
}
return schemas


def generate() -> str:
with SPECS_PATH.open() as f:
specs = json.load(f)
specs = _load_schemas()

lines = [
'"""Auto-generated type stubs for VerbBuilder.',
Expand All @@ -75,7 +103,6 @@ def generate() -> str:
"",
"from jambonz_sdk.types.verbs import AnyVerb",
"",
"",
"class VerbBuilder:",
" _verbs: list[AnyVerb]",
"",
Expand Down
Loading
Loading