From ea204944d654fc97e01fc9e6b9158d8837953ff7 Mon Sep 17 00:00:00 2001 From: Sam Machin Date: Wed, 14 Oct 2026 12:05:23 +0100 Subject: [PATCH] bump schema, add new verbs, regen sdk --- AGENTS.md | 7 +- README.md | 2 +- scripts/regen_models.py | 87 ++++++- scripts/sync_schema.py | 2 +- .../_models/_generated/__init__.py | 4 +- .../_models/_generated/callbacks/__init__.py | 2 +- .../_generated/callbacks/agent_turn.py | 2 +- .../_models/_generated/callbacks/amd.py | 2 +- .../_models/_generated/callbacks/base.py | 8 +- .../_generated/callbacks/call_status.py | 17 +- .../_generated/callbacks/conference.py | 2 +- .../_generated/callbacks/conference_status.py | 2 +- .../_generated/callbacks/conference_wait.py | 2 +- .../_models/_generated/callbacks/dequeue.py | 2 +- .../_models/_generated/callbacks/dial.py | 2 +- .../_models/_generated/callbacks/dial_dtmf.py | 2 +- .../_models/_generated/callbacks/dial_hold.py | 2 +- .../_generated/callbacks/dial_refer.py | 2 +- .../_models/_generated/callbacks/enqueue.py | 2 +- .../_generated/callbacks/enqueue_wait.py | 2 +- .../_models/_generated/callbacks/gather.py | 2 +- .../_generated/callbacks/gather_partial.py | 2 +- .../_models/_generated/callbacks/listen.py | 2 +- .../_models/_generated/callbacks/llm.py | 2 +- .../_models/_generated/callbacks/message.py | 2 +- .../_models/_generated/callbacks/play.py | 2 +- .../_generated/callbacks/session_new.py | 2 +- .../_generated/callbacks/session_reconnect.py | 2 +- .../_generated/callbacks/session_redirect.py | 2 +- .../_models/_generated/callbacks/sip_refer.py | 2 +- .../_generated/callbacks/sip_refer_event.py | 2 +- .../_generated/callbacks/sip_request.py | 2 +- .../_generated/callbacks/transcribe.py | 2 +- .../callbacks/transcribe_translation.py | 2 +- .../_models/_generated/callbacks/transfer.py | 61 +++++ .../callbacks/tts_streaming_event.py | 2 +- .../_generated/callbacks/verb_status.py | 2 +- .../_models/_generated/components/__init__.py | 2 +- .../_generated/components/actionHook.py | 2 +- .../components/actionHookDelayAction.py | 2 +- .../_models/_generated/components/amd.py | 2 +- .../_models/_generated/components/auth.py | 2 +- .../components/bidirectionalAudio.py | 2 +- .../_generated/components/fillerNoise.py | 2 +- .../_models/_generated/components/handoff.py | 59 +++++ .../_models/_generated/components/hangup.py | 23 ++ .../_models/_generated/components/llm_base.py | 40 +-- .../_generated/components/recognizer.py | 35 +-- .../recognizer_assemblyAiOptions.py | 27 +- .../components/recognizer_awsOptions.py | 2 +- .../components/recognizer_azureOptions.py | 2 +- .../components/recognizer_cobaltOptions.py | 43 ---- .../components/recognizer_customOptions.py | 2 +- .../components/recognizer_deepgramOptions.py | 6 +- .../recognizer_elevenlabsOptions.py | 2 +- .../components/recognizer_gladiaOptions.py | 6 +- .../components/recognizer_googleOptions.py | 29 ++- .../components/recognizer_gradiumOptions.py | 81 ++++++ .../components/recognizer_houndifyOptions.py | 14 +- .../components/recognizer_ibmOptions.py | 2 +- .../components/recognizer_modulateOptions.py | 51 ++++ .../components/recognizer_nuanceOptions.py | 226 ----------------- .../components/recognizer_nvidiaOptions.py | 2 +- .../components/recognizer_openaiOptions.py | 40 ++- .../components/recognizer_sonioxOptions.py | 86 ++++++- .../recognizer_speechmaticsOptions.py | 27 +- .../components/recognizer_verbioOptions.py | 55 ---- .../components/recognizer_xaiOptions.py | 53 ++++ .../components/recognizer_zoomOptions.py | 57 +++++ .../_generated/components/synthesizer.py | 10 +- .../_models/_generated/components/target.py | 2 +- .../_generated/components/transferOptions.py | 176 +++++++++++++ .../_models/_generated/components/vad.py | 2 +- .../_models/_generated/jambonz_app.py | 14 +- .../_models/_generated/verbs/__init__.py | 2 +- .../_models/_generated/verbs/agent.py | 233 ++++++++++++++++- .../_models/_generated/verbs/alert.py | 2 +- .../_models/_generated/verbs/answer.py | 2 +- .../_models/_generated/verbs/conference.py | 10 +- .../_models/_generated/verbs/config.py | 2 +- .../_models/_generated/verbs/deepgram_s2s.py | 2 +- .../_models/_generated/verbs/dequeue.py | 2 +- .../_models/_generated/verbs/dial.py | 20 +- .../_models/_generated/verbs/dialogflow.py | 22 +- .../_models/_generated/verbs/dtmf.py | 2 +- .../_models/_generated/verbs/dub.py | 4 +- .../_generated/verbs/elevenlabs_s2s.py | 2 +- .../_models/_generated/verbs/enqueue.py | 2 +- .../_models/_generated/verbs/gather.py | 2 +- .../_models/_generated/verbs/google_s2s.py | 2 +- .../_models/_generated/verbs/gptlive_s2s.py | 126 +++++++++ .../_models/_generated/verbs/hangup.py | 2 +- .../_models/_generated/verbs/leave.py | 2 +- .../_models/_generated/verbs/listen.py | 2 +- .../_models/_generated/verbs/llm.py | 16 +- .../_models/_generated/verbs/message.py | 2 +- .../_models/_generated/verbs/openai_s2s.py | 2 +- .../_models/_generated/verbs/pause.py | 2 +- .../_models/_generated/verbs/play.py | 2 +- .../_models/_generated/verbs/qwen_s2s.py | 44 ++++ .../_models/_generated/verbs/redirect.py | 2 +- .../_models/_generated/verbs/rest_dial.py | 6 +- .../_models/_generated/verbs/room.py | 16 ++ .../_models/_generated/verbs/s2s.py | 16 +- .../_models/_generated/verbs/say.py | 2 +- .../_models/_generated/verbs/sip_decline.py | 2 +- .../_models/_generated/verbs/sip_refer.py | 2 +- .../_models/_generated/verbs/sip_request.py | 2 +- .../_models/_generated/verbs/stream.py | 2 +- .../_models/_generated/verbs/tag.py | 2 +- .../_models/_generated/verbs/transcribe.py | 16 +- .../_models/_generated/verbs/transfer.py | 51 ++++ .../_models/_generated/verbs/ultravox_s2s.py | 2 +- .../_models/_generated/verbs/voicelive_s2s.py | 126 +++++++++ src/jambonz_sdk/_models/_registry.py | 13 +- src/jambonz_sdk/components/__init__.py | 28 +- .../schema/callbacks/base.schema.json | 3 +- .../schema/callbacks/transfer.schema.json | 31 +++ .../schema/components/handoff.schema.json | 45 ++++ .../schema/components/hangup.schema.json | 14 + .../schema/components/llm-base.schema.json | 26 +- .../recognizer-assemblyAiOptions.schema.json | 14 +- .../recognizer-cobaltOptions.schema.json | 34 --- .../recognizer-gladiaOptions.schema.json | 4 +- .../recognizer-googleOptions.schema.json | 15 +- .../recognizer-gradiumOptions.schema.json | 78 ++++++ .../recognizer-modulateOptions.schema.json | 42 +++ .../recognizer-nuanceOptions.schema.json | 150 ----------- .../recognizer-openaiOptions.schema.json | 32 +++ .../recognizer-sonioxOptions.schema.json | 76 +++++- ...recognizer-speechmaticsOptions.schema.json | 23 +- .../recognizer-verbioOptions.schema.json | 46 ---- .../recognizer-xaiOptions.schema.json | 44 ++++ .../recognizer-zoomOptions.schema.json | 45 ++++ .../schema/components/recognizer.schema.json | 9 +- .../schema/components/synthesizer.schema.json | 2 +- .../components/transferOptions.schema.json | 177 +++++++++++++ .../schema/jambonz-app.schema.json | 4 + .../schema/verbs/agent.schema.json | 54 ++-- .../schema/verbs/conference.schema.json | 9 +- src/jambonz_sdk/schema/verbs/dial.schema.json | 11 + .../schema/verbs/dialogflow.schema.json | 17 +- .../schema/verbs/gptlive_s2s.schema.json | 240 ++++++++++++++++++ .../schema/verbs/qwen_s2s.schema.json | 45 ++++ .../schema/verbs/rest_dial.schema.json | 6 + src/jambonz_sdk/schema/verbs/room.schema.json | 8 + .../schema/verbs/transfer.schema.json | 91 +++++++ .../schema/verbs/voicelive_s2s.schema.json | 146 +++++++++++ src/jambonz_sdk/verb_builder.pyi | 146 ++++++++++- src/jambonz_sdk/verb_registry.py | 13 + src/jambonz_sdk/verbs/__init__.py | 10 + tests/unit/test_verb_builder.py | 9 + 152 files changed, 3150 insertions(+), 802 deletions(-) create mode 100644 src/jambonz_sdk/_models/_generated/callbacks/transfer.py create mode 100644 src/jambonz_sdk/_models/_generated/components/handoff.py create mode 100644 src/jambonz_sdk/_models/_generated/components/hangup.py delete mode 100644 src/jambonz_sdk/_models/_generated/components/recognizer_cobaltOptions.py create mode 100644 src/jambonz_sdk/_models/_generated/components/recognizer_gradiumOptions.py create mode 100644 src/jambonz_sdk/_models/_generated/components/recognizer_modulateOptions.py delete mode 100644 src/jambonz_sdk/_models/_generated/components/recognizer_nuanceOptions.py delete mode 100644 src/jambonz_sdk/_models/_generated/components/recognizer_verbioOptions.py create mode 100644 src/jambonz_sdk/_models/_generated/components/recognizer_xaiOptions.py create mode 100644 src/jambonz_sdk/_models/_generated/components/recognizer_zoomOptions.py create mode 100644 src/jambonz_sdk/_models/_generated/components/transferOptions.py create mode 100644 src/jambonz_sdk/_models/_generated/verbs/gptlive_s2s.py create mode 100644 src/jambonz_sdk/_models/_generated/verbs/qwen_s2s.py create mode 100644 src/jambonz_sdk/_models/_generated/verbs/room.py create mode 100644 src/jambonz_sdk/_models/_generated/verbs/transfer.py create mode 100644 src/jambonz_sdk/_models/_generated/verbs/voicelive_s2s.py create mode 100644 src/jambonz_sdk/schema/callbacks/transfer.schema.json create mode 100644 src/jambonz_sdk/schema/components/handoff.schema.json create mode 100644 src/jambonz_sdk/schema/components/hangup.schema.json delete mode 100644 src/jambonz_sdk/schema/components/recognizer-cobaltOptions.schema.json create mode 100644 src/jambonz_sdk/schema/components/recognizer-gradiumOptions.schema.json create mode 100644 src/jambonz_sdk/schema/components/recognizer-modulateOptions.schema.json delete mode 100644 src/jambonz_sdk/schema/components/recognizer-nuanceOptions.schema.json delete mode 100644 src/jambonz_sdk/schema/components/recognizer-verbioOptions.schema.json create mode 100644 src/jambonz_sdk/schema/components/recognizer-xaiOptions.schema.json create mode 100644 src/jambonz_sdk/schema/components/recognizer-zoomOptions.schema.json create mode 100644 src/jambonz_sdk/schema/components/transferOptions.schema.json create mode 100644 src/jambonz_sdk/schema/verbs/gptlive_s2s.schema.json create mode 100644 src/jambonz_sdk/schema/verbs/qwen_s2s.schema.json create mode 100644 src/jambonz_sdk/schema/verbs/room.schema.json create mode 100644 src/jambonz_sdk/schema/verbs/transfer.schema.json create mode 100644 src/jambonz_sdk/schema/verbs/voicelive_s2s.schema.json diff --git a/AGENTS.md b/AGENTS.md index f13d2eb..c4215c3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,7 +22,7 @@ A jambonz application controls phone calls by returning **arrays of verbs** — - **Webhook (HTTP)**: Your server receives POST requests and returns JSON verb arrays. Stateless and simple. - **WebSocket**: Persistent bidirectional connection. Required for real-time LLM agents, audio streaming, and TTS token streaming. -**IMPORTANT**: Any application that uses a speech-to-speech verb (`openai_s2s`, `google_s2s`, `deepgram_s2s`, `ultravox_s2s`, `elevenlabs_s2s`, `s2s`, or `agent`) MUST use WebSocket transport. +**IMPORTANT**: Any application that uses a speech-to-speech verb (`openai_s2s`, `google_s2s`, `deepgram_s2s`, `ultravox_s2s`, `elevenlabs_s2s`, `gptlive_s2s`, `qwen_s2s`, `voicelive_s2s`, `s2s`, or `agent`) MUST use WebSocket transport. ## Core Verbs @@ -32,7 +32,7 @@ A jambonz application controls phone calls by returning **arrays of verbs** — - **gather** — Collect speech (STT) and/or DTMF input. ### AI & Real-time -- **openai_s2s** / **google_s2s** / **deepgram_s2s** / **ultravox_s2s** / **elevenlabs_s2s** — Vendor-specific LLM voice conversation. +- **openai_s2s** / **google_s2s** / **deepgram_s2s** / **ultravox_s2s** / **elevenlabs_s2s** / **gptlive_s2s** / **qwen_s2s** / **voicelive_s2s** — Vendor-specific LLM voice conversation. - **s2s** — Generic LLM voice conversation (use when vendor is determined at runtime). - **agent** — Higher-level voice AI agent with integrated turn detection. - **dialogflow** — Google Dialogflow agent. @@ -41,7 +41,8 @@ A jambonz application controls phone calls by returning **arrays of verbs** — ### Call Control - **dial** — Place an outbound call and bridge. -- **conference** — Multi-party conference room. +- **conference** / **room** — Multi-party conference room (`room` is a synonym for `conference`; prefer it in new apps). +- **transfer** — Blind or warm (parked/three-way) transfer with built-in failure handling. - **enqueue** / **dequeue** — Call queuing. - **hangup** — End the call. - **redirect** — Transfer control to a different webhook. diff --git a/README.md b/README.md index d48bd82..c857744 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ VerbDef("new_verb", "new_verb", doc="Description.") ## Features -- **All 31 jambonz verbs**: say, play, gather, dial, conference, enqueue/dequeue, hangup, pause, redirect, config, tag, dtmf, dub, message, alert, answer, leave, listen/stream, transcribe, openai_s2s, google_s2s, deepgram_s2s, elevenlabs_s2s, ultravox_s2s, s2s, llm, dialogflow, agent, sip_decline, sip_request, sip_refer +- **All jambonz verbs**: say, play, gather, dial, conference/room, transfer, enqueue/dequeue, hangup, pause, redirect, config, tag, dtmf, dub, message, alert, answer, leave, listen/stream, transcribe, openai_s2s, google_s2s, deepgram_s2s, elevenlabs_s2s, ultravox_s2s, gptlive_s2s, qwen_s2s, voicelive_s2s, s2s, llm, dialogflow, agent, sip_decline, sip_request, sip_refer - **Fluent chainable API**: `.say(...).gather(...).hangup()` - **Webhook transport**: `WebhookResponse` for HTTP apps (works with aiohttp, FastAPI, Flask, etc.) - **WebSocket transport**: `create_endpoint` with `Session`, event handling, `send()`/`reply()` diff --git a/scripts/regen_models.py b/scripts/regen_models.py index 0f3d95b..6ba6d20 100644 --- a/scripts/regen_models.py +++ b/scripts/regen_models.py @@ -79,7 +79,23 @@ NESTED_OVERRIDES: list[tuple[str, str, str, str, str]] = [ ("verbs/gather.py", "Gather", "say", ".say", "Say"), ("verbs/gather.py", "Gather", "play", ".play", "Play"), - ("verbs/agent.py", "Agent", "llm", ".llm", "LLM"), + # NB: ``Agent.llm`` used to be an untyped object here, but the schema + # now defines it inline, so codegen emits a proper nested ``Llm`` model + # and no override is needed. +] + +# Verbs whose ``verb`` property is a multi-value ``enum`` rather than a +# ``const`` (because the verb has a synonym, e.g. ``room`` for +# ``conference``). datamodel-code-generator mishandles this shape: it +# emits a dangling ``Verb(Enum)`` class and a broken field annotation +# (``verb: Literal["conference#"]`` with no default). This table repairs +# the field into a proper multi-value ``Literal`` with the canonical value +# as the default, so the model validates and serializes correctly and the +# verb registry can index it under every synonym. +# +# Format: (module_path, class_name, canonical_verb, all_verbs) +VERB_ENUM_FIXUPS: list[tuple[str, str, str, list[str]]] = [ + ("verbs/conference.py", "Conference", "conference", ["conference", "room"]), ] # Nested classes the re-export heuristic wouldn't pick up (because they @@ -313,6 +329,57 @@ def apply_class_validators(output_dir: Path) -> None: path.write_text(source) +def apply_verb_enum_fixups(output_dir: Path) -> None: + """Repair verb fields generated from a multi-value ``enum`` discriminator. + + See ``VERB_ENUM_FIXUPS`` for why this is needed. For each target this: + + - drops the dangling ``class Verb(Enum): ...`` block the generator emits, + - drops the now-unused ``from enum import Enum`` import, + - rewrites the broken ``verb: Literal[...]`` field into + ``verb: Literal["a", "b", ...] = ""``. + """ + for rel_path, class_name, canonical, all_verbs in VERB_ENUM_FIXUPS: + path = output_dir / rel_path + if not path.is_file(): + print(f" warning: verb-enum-fixup target {rel_path} missing — skipping") + continue + source = path.read_text() + + # Drop the dangling ``class Verb(Enum): ...`` block (runs until the + # next blank line separating top-level statements). + source = re.sub( + r"\nclass Verb\(Enum\):\n(?:[ \t]+.*\n)+", + "\n", + source, + count=1, + ) + # Drop the now-unused Enum import. + source = re.sub(r"^from enum import Enum\n", "", source, flags=re.MULTILINE) + + # Rewrite the verb field inside the target class. + literal = ", ".join(f'"{v}"' for v in all_verbs) + replacement = f' verb: Literal[{literal}] = "{canonical}"\n' + class_start = source.find(f"class {class_name}(") + if class_start < 0: + print(f" warning: class {class_name} not found in {rel_path}") + continue + next_class = re.search(r"^class\s+\w+\(", source[class_start + 1:], re.MULTILINE) + class_end = class_start + 1 + (next_class.start() if next_class else len(source)) + head, body, tail = source[:class_start], source[class_start:class_end], source[class_end:] + new_body, n = re.subn( + r"^ verb: Literal\[[^\n]*\]\n", + replacement, + body, + count=1, + flags=re.MULTILINE, + ) + if n == 0: + print(f" warning: verb field for {class_name} not found in {rel_path}") + continue + path.write_text(head + new_body + tail) + + def apply_nested_overrides(output_dir: Path) -> None: """Rewrite specific ``dict[str, Any]`` fields to reference real models.""" for rel_path, class_name, field_name, target_module, target_class in NESTED_OVERRIDES: @@ -395,12 +462,19 @@ def _primary_class_of(path: Path, source: str) -> str | None: Falls back to the last model-class declared in the file. """ stem = path.stem - # Collect every model/root class declared in the module. - keep_bases = ("JambonzModel", "LlmBaseProperties") + # Collect every model/root class declared in the module. A class is + # a model unless it subclasses a non-model base like ``Enum``. This + # keeps classes that extend ``JambonzModel`` directly as well as + # those extending another generated model (e.g. vendor s2s verbs + # extend ``LlmBaseProperties``; ``Transfer``/``Handoff`` extend + # ``TransferOptions``), while dropping generated enums. + non_model_bases = ("Enum", "str", "int", "float", "IntEnum", "StrEnum") classes: list[str] = [] for name, base in class_re.findall(source): - if base in keep_bases or base.startswith("RootModel"): - classes.append(name) + short_base = base.rsplit(".", 1)[-1] + if short_base in non_model_bases: + continue + classes.append(name) if not classes: return None @@ -550,6 +624,9 @@ def main() -> None: postprocess_file(path) print(f" post-processed {len(py_files)} files") + apply_verb_enum_fixups(OUTPUT_DIR) + print(" applied verb-enum fixups") + apply_nested_overrides(OUTPUT_DIR) print(" applied nested-object overrides") diff --git a/scripts/sync_schema.py b/scripts/sync_schema.py index a03183f..a191a5f 100644 --- a/scripts/sync_schema.py +++ b/scripts/sync_schema.py @@ -22,7 +22,7 @@ from pathlib import Path # ── Pin the schema version here ────────────────────────────────────── -SCHEMA_VERSION = "v0.3.8" +SCHEMA_VERSION = "v0.4.23" # ──────────────────────────────────────────────────────────────────── DEST = Path(__file__).resolve().parent.parent / "src" / "jambonz_sdk" / "schema" diff --git a/src/jambonz_sdk/_models/_generated/__init__.py b/src/jambonz_sdk/_models/_generated/__init__.py index 5360f4e..e9a408f 100644 --- a/src/jambonz_sdk/_models/_generated/__init__.py +++ b/src/jambonz_sdk/_models/_generated/__init__.py @@ -1,4 +1,4 @@ -__jambonz_schema_version__ = "v0.2.1" +__jambonz_schema_version__ = "v0.4.23" # generated by datamodel-codegen: # filename: schema -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 diff --git a/src/jambonz_sdk/_models/_generated/callbacks/__init__.py b/src/jambonz_sdk/_models/_generated/callbacks/__init__.py index abf1e3b..97fa6b2 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/__init__.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/__init__.py @@ -1,3 +1,3 @@ # generated by datamodel-codegen: # filename: schema -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 diff --git a/src/jambonz_sdk/_models/_generated/callbacks/agent_turn.py b/src/jambonz_sdk/_models/_generated/callbacks/agent_turn.py index 306089b..20d2777 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/agent_turn.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/agent_turn.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/agent-turn -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/amd.py b/src/jambonz_sdk/_models/_generated/callbacks/amd.py index a4370e2..c6ceab5 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/amd.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/amd.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/amd -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/base.py b/src/jambonz_sdk/_models/_generated/callbacks/base.py index 947a9a8..894b6c2 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/base.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/base.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/base -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations @@ -86,7 +86,11 @@ class ActionhookBasePayload(JambonzModel): """ sip_reason: str | None = None """ - SIP reason phrase (e.g. 'OK', 'Busy Here'). + SIP reason phrase from the SIP status line (e.g. 'OK', 'Busy Here'). Not the RFC 3326 Reason header - see sip_reason_header. + """ + sip_reason_header: str | None = None + """ + RFC 3326 Reason header of the SIP message that caused this status change (e.g. 'Q.850;cause=18'), if it carried one; absent otherwise. Carriers fronting ISDN/E1 PRI trunks put the authoritative disconnect cause here rather than in the status line, and the same SIP status can carry different causes - a 503 may be cause=38 (network out of order) or cause=41 (temporary failure) - so the status code alone cannot classify the outcome. Present on whichever event observed it: a final failure response, a BYE, or a CANCEL. Two caveats: the header is re-serialized in transit, which normalizes the optional whitespace RFC 3326 allows around ';' (a carrier's 'Q.850 ;cause=18' arrives as 'Q.850;cause=18'), so parse tolerantly rather than string-matching; and a message may carry more than one Reason header, in which case they arrive comma-separated - split on commas outside quotes, since a text="..." value may contain one. """ trace_id: str | None = None """ diff --git a/src/jambonz_sdk/_models/_generated/callbacks/call_status.py b/src/jambonz_sdk/_models/_generated/callbacks/call_status.py index 36b5b62..0b1391d 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/call_status.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/call_status.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/call-status -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations @@ -11,6 +11,15 @@ from .base import ActionhookBasePayload +class Direction(Enum): + """ + Call direction. 'inbound' = A-leg (original incoming call to the application). 'outbound' = B-leg (call placed by the dial verb). Use this field to identify which leg generated the status event, especially when capturing the B-leg's call_sid for mid-call control. + """ + + inbound = "inbound" + outbound = "outbound" + + class CallTerminationBy(Enum): """ Who terminated the call. 'caller' if the remote party hung up, 'jambonz' if the call was ended by the application (e.g. hangup verb, REST API). Present only on the final status event (completed/failed). @@ -23,11 +32,17 @@ class CallTerminationBy(Enum): class CallStatusWebhookPayload(ActionhookBasePayload): """ Payload sent to the call status webhook URL whenever the call state changes (e.g. trying, in-progress, completed). The status webhook is configured at the application level in jambonz. Multiple status events are sent over the life of a call. The final event (completed or failed) includes additional fields like duration and termination cause. + + **Capturing B-leg call_sid:** When using the dial verb to bridge calls, status events are sent for both legs. The A-leg (original inbound call) has `direction: 'inbound'`. The B-leg (outbound dialed call) has `direction: 'outbound'`. To capture the B-leg's call_sid for later use (e.g., injecting commands to the B-leg), listen for status events where `direction === 'outbound'` and extract the `call_sid` field. """ model_config = ConfigDict( extra="allow", ) + direction: Direction | None = None + """ + Call direction. 'inbound' = A-leg (original incoming call to the application). 'outbound' = B-leg (call placed by the dial verb). Use this field to identify which leg generated the status event, especially when capturing the B-leg's call_sid for mid-call control. + """ call_termination_by: CallTerminationBy | None = None """ Who terminated the call. 'caller' if the remote party hung up, 'jambonz' if the call was ended by the application (e.g. hangup verb, REST API). Present only on the final status event (completed/failed). diff --git a/src/jambonz_sdk/_models/_generated/callbacks/conference.py b/src/jambonz_sdk/_models/_generated/callbacks/conference.py index 479cb92..6b51603 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/conference.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/conference.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/conference -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/conference_status.py b/src/jambonz_sdk/_models/_generated/callbacks/conference_status.py index 3c3dea1..54596a4 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/conference_status.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/conference_status.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/conference-status -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/conference_wait.py b/src/jambonz_sdk/_models/_generated/callbacks/conference_wait.py index e51bfb3..bde0564 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/conference_wait.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/conference_wait.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/conference-wait -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/dequeue.py b/src/jambonz_sdk/_models/_generated/callbacks/dequeue.py index 3a8d0c2..c3f50b1 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/dequeue.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/dequeue.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/dequeue -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/dial.py b/src/jambonz_sdk/_models/_generated/callbacks/dial.py index 57f5e83..217a532 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/dial.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/dial.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/dial -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/dial_dtmf.py b/src/jambonz_sdk/_models/_generated/callbacks/dial_dtmf.py index 257bd53..ddb0e07 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/dial_dtmf.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/dial_dtmf.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/dial-dtmf -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/dial_hold.py b/src/jambonz_sdk/_models/_generated/callbacks/dial_hold.py index 33be5a6..0326068 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/dial_hold.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/dial_hold.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/dial-hold -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/dial_refer.py b/src/jambonz_sdk/_models/_generated/callbacks/dial_refer.py index 6f12efc..276a7d1 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/dial_refer.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/dial_refer.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/dial-refer -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/enqueue.py b/src/jambonz_sdk/_models/_generated/callbacks/enqueue.py index 3af84e4..6212156 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/enqueue.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/enqueue.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/enqueue -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/enqueue_wait.py b/src/jambonz_sdk/_models/_generated/callbacks/enqueue_wait.py index 85127ee..6fe9464 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/enqueue_wait.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/enqueue_wait.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/enqueue-wait -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/gather.py b/src/jambonz_sdk/_models/_generated/callbacks/gather.py index 144c650..e23c7d9 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/gather.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/gather.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/gather -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/gather_partial.py b/src/jambonz_sdk/_models/_generated/callbacks/gather_partial.py index f7ff762..a3b8ba3 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/gather_partial.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/gather_partial.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/gather-partial -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/listen.py b/src/jambonz_sdk/_models/_generated/callbacks/listen.py index 138dd5c..07c3244 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/listen.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/listen.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/listen -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/llm.py b/src/jambonz_sdk/_models/_generated/callbacks/llm.py index 8e8afd4..f7b97cf 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/llm.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/llm.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/llm -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/message.py b/src/jambonz_sdk/_models/_generated/callbacks/message.py index 0ad2951..123613a 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/message.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/message.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/message -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/play.py b/src/jambonz_sdk/_models/_generated/callbacks/play.py index 7af1b98..a5b086c 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/play.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/play.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/play -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/session_new.py b/src/jambonz_sdk/_models/_generated/callbacks/session_new.py index 0d95e95..d0da608 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/session_new.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/session_new.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/session-new -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/session_reconnect.py b/src/jambonz_sdk/_models/_generated/callbacks/session_reconnect.py index e6deb51..7736063 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/session_reconnect.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/session_reconnect.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/session-reconnect -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/session_redirect.py b/src/jambonz_sdk/_models/_generated/callbacks/session_redirect.py index 71202bc..ca4698a 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/session_redirect.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/session_redirect.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/session-redirect -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/sip_refer.py b/src/jambonz_sdk/_models/_generated/callbacks/sip_refer.py index c2c1d97..eb2ac73 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/sip_refer.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/sip_refer.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/sip-refer -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/sip_refer_event.py b/src/jambonz_sdk/_models/_generated/callbacks/sip_refer_event.py index bc52895..b1d7378 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/sip_refer_event.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/sip_refer_event.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/sip-refer-event -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/sip_request.py b/src/jambonz_sdk/_models/_generated/callbacks/sip_request.py index b009875..5960843 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/sip_request.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/sip_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/sip-request -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/transcribe.py b/src/jambonz_sdk/_models/_generated/callbacks/transcribe.py index 15ec4c6..5bcf17d 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/transcribe.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/transcribe.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/transcribe -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/transcribe_translation.py b/src/jambonz_sdk/_models/_generated/callbacks/transcribe_translation.py index 6f279e7..5546dee 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/transcribe_translation.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/transcribe_translation.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/transcribe-translation -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/transfer.py b/src/jambonz_sdk/_models/_generated/callbacks/transfer.py new file mode 100644 index 0000000..2c1b4f3 --- /dev/null +++ b/src/jambonz_sdk/_models/_generated/callbacks/transfer.py @@ -0,0 +1,61 @@ +# generated by datamodel-codegen: +# filename: callbacks/transfer +# timestamp: 2026-10-14T10:55:09+00:00 + +from __future__ import annotations + +from enum import Enum + +from pydantic import ConfigDict + +from .base import ActionhookBasePayload + + +class TransferResult(Enum): + """ + Outcome of the transfer: 'bridged' means the destination answered and the call was connected; 'returned' means the call was handed back to the caller flow; 'voicemail' means the caller was routed to voicemail; 'failed' means a protocol-level error occurred. + """ + + bridged = "bridged" + returned = "returned" + voicemail = "voicemail" + failed = "failed" + + +class TransferReason(Enum): + """ + Why the transfer ended with its result: 'completed' means it ran to completion; 'no-answer' means the destination did not answer in time; 'busy' means the destination returned a busy response; 'declined' means the confirm gate or AMD rejected the call; 'caller-abandoned' means the caller hung up before bridging; 'error' means a protocol-level failure occurred. + """ + + completed = "completed" + no_answer = "no-answer" + busy = "busy" + declined = "declined" + caller_abandoned = "caller-abandoned" + error = "error" + + +class TransferActionhookPayload(ActionhookBasePayload): + """ + Payload sent to the actionHook when a transfer verb resolves. Extends the base callback payload with transfer-specific outcome fields. + """ + + model_config = ConfigDict( + extra="allow", + ) + transfer_result: TransferResult | None = None + """ + Outcome of the transfer: 'bridged' means the destination answered and the call was connected; 'returned' means the call was handed back to the caller flow; 'voicemail' means the caller was routed to voicemail; 'failed' means a protocol-level error occurred. + """ + transfer_reason: TransferReason | None = None + """ + Why the transfer ended with its result: 'completed' means it ran to completion; 'no-answer' means the destination did not answer in time; 'busy' means the destination returned a busy response; 'declined' means the confirm gate or AMD rejected the call; 'caller-abandoned' means the caller hung up before bridging; 'error' means a protocol-level failure occurred. + """ + dial_call_sid: str | None = None + """ + Call SID of the destination (human) leg, when one was created during the transfer attempt. + """ + sip_status: int | None = None + """ + SIP status code from the destination leg, when applicable (e.g. 200, 486, 487). + """ diff --git a/src/jambonz_sdk/_models/_generated/callbacks/tts_streaming_event.py b/src/jambonz_sdk/_models/_generated/callbacks/tts_streaming_event.py index 872f57e..04d7b79 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/tts_streaming_event.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/tts_streaming_event.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/tts-streaming-event -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/callbacks/verb_status.py b/src/jambonz_sdk/_models/_generated/callbacks/verb_status.py index ea79e08..fe79459 100644 --- a/src/jambonz_sdk/_models/_generated/callbacks/verb_status.py +++ b/src/jambonz_sdk/_models/_generated/callbacks/verb_status.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: callbacks/verb-status -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/components/__init__.py b/src/jambonz_sdk/_models/_generated/components/__init__.py index abf1e3b..97fa6b2 100644 --- a/src/jambonz_sdk/_models/_generated/components/__init__.py +++ b/src/jambonz_sdk/_models/_generated/components/__init__.py @@ -1,3 +1,3 @@ # generated by datamodel-codegen: # filename: schema -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 diff --git a/src/jambonz_sdk/_models/_generated/components/actionHook.py b/src/jambonz_sdk/_models/_generated/components/actionHook.py index 9ba0583..3b43b80 100644 --- a/src/jambonz_sdk/_models/_generated/components/actionHook.py +++ b/src/jambonz_sdk/_models/_generated/components/actionHook.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: components/actionHook -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/components/actionHookDelayAction.py b/src/jambonz_sdk/_models/_generated/components/actionHookDelayAction.py index fb6fb77..bfeeb81 100644 --- a/src/jambonz_sdk/_models/_generated/components/actionHookDelayAction.py +++ b/src/jambonz_sdk/_models/_generated/components/actionHookDelayAction.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: components/actionHookDelayAction -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/components/amd.py b/src/jambonz_sdk/_models/_generated/components/amd.py index 8a7b422..504d350 100644 --- a/src/jambonz_sdk/_models/_generated/components/amd.py +++ b/src/jambonz_sdk/_models/_generated/components/amd.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: components/amd -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/components/auth.py b/src/jambonz_sdk/_models/_generated/components/auth.py index a730bff..0d3ec94 100644 --- a/src/jambonz_sdk/_models/_generated/components/auth.py +++ b/src/jambonz_sdk/_models/_generated/components/auth.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: components/auth -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/components/bidirectionalAudio.py b/src/jambonz_sdk/_models/_generated/components/bidirectionalAudio.py index f070128..33e19a7 100644 --- a/src/jambonz_sdk/_models/_generated/components/bidirectionalAudio.py +++ b/src/jambonz_sdk/_models/_generated/components/bidirectionalAudio.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: components/bidirectionalAudio -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/components/fillerNoise.py b/src/jambonz_sdk/_models/_generated/components/fillerNoise.py index a29fe59..b9c8ebf 100644 --- a/src/jambonz_sdk/_models/_generated/components/fillerNoise.py +++ b/src/jambonz_sdk/_models/_generated/components/fillerNoise.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: components/fillerNoise -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/components/handoff.py b/src/jambonz_sdk/_models/_generated/components/handoff.py new file mode 100644 index 0000000..445e665 --- /dev/null +++ b/src/jambonz_sdk/_models/_generated/components/handoff.py @@ -0,0 +1,59 @@ +# generated by datamodel-codegen: +# filename: components/handoff +# timestamp: 2026-10-14T10:55:09+00:00 + +from __future__ import annotations + +from enum import Enum + +from pydantic import ConfigDict, Field + +from jambonz_sdk._models.base import JambonzModel + +from . import synthesizer +from .transferOptions import TransferOptions + + +class Brief(Enum): + """ + 'auto' = LLM writes the summary; 'none' = no spoken brief; {template} = guidance for the LLM's summary. + """ + + auto = "auto" + none = "none" + + +class Brief1(JambonzModel): + """ + 'auto' = LLM writes the summary; 'none' = no spoken brief; {template} = guidance for the LLM's summary. + """ + + model_config = ConfigDict( + extra="forbid", + ) + template: str + + +class Handoff(TransferOptions): + """ + Declarative transfer-to-human config for conversational verbs (agent and s2s); runtime injects a transfer-to-human tool and runs the packaged choreography when the model calls it. + """ + + brief: Brief | Brief1 | None = Field(default="auto", validate_default=True) + """ + 'auto' = LLM writes the summary; 'none' = no spoken brief; {template} = guidance for the LLM's summary. + """ + brief_synthesizer: synthesizer.Synthesizer | None = Field( + default=None, alias="briefSynthesizer" + ) + """ + Optional voice/vendor for the spoken brief. Defaults to the session synthesizer. + """ + tool_name: str | None = Field(default="transfer_to_human", alias="toolName") + """ + Override the injected tool name. + """ + tool_description: str | None = Field(default=None, alias="toolDescription") + """ + Override the injected tool description shown to the LLM. + """ diff --git a/src/jambonz_sdk/_models/_generated/components/hangup.py b/src/jambonz_sdk/_models/_generated/components/hangup.py new file mode 100644 index 0000000..5b855a9 --- /dev/null +++ b/src/jambonz_sdk/_models/_generated/components/hangup.py @@ -0,0 +1,23 @@ +# generated by datamodel-codegen: +# filename: components/hangup +# timestamp: 2026-10-14T10:55:09+00:00 + +from __future__ import annotations + +from pydantic import ConfigDict + +from jambonz_sdk._models.base import JambonzModel + + +class Hangup(JambonzModel): + """ + Enable the built-in hangup tool for conversational verbs (agent and s2s). When present, the runtime injects a 'hangup' tool; when the model calls it, the call is ended with a reason placed in an X-Reason SIP header. The LLM-supplied reason wins, falling back to the app-supplied default below. + """ + + model_config = ConfigDict( + extra="forbid", + ) + reason: str | None = None + """ + Default reason placed in the X-Reason SIP header when the call is hung up. Used as a fallback when the LLM does not supply its own reason. + """ diff --git a/src/jambonz_sdk/_models/_generated/components/llm_base.py b/src/jambonz_sdk/_models/_generated/components/llm_base.py index 3c466f0..4abade0 100644 --- a/src/jambonz_sdk/_models/_generated/components/llm_base.py +++ b/src/jambonz_sdk/_models/_generated/components/llm_base.py @@ -1,16 +1,18 @@ # generated by datamodel-codegen: # filename: components/llm-base -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations from typing import Any -from pydantic import ConfigDict, Field +from pydantic import ConfigDict, Field, conint from jambonz_sdk._models.base import JambonzModel from . import actionHook +from . import handoff as handoff_1 +from . import hangup as hangup_1 class Auth(JambonzModel): @@ -51,19 +53,7 @@ class LlmBaseProperties(JambonzModel): """ An optional unique identifier for this verb instance. """ - vendor: str | None = Field( - default=None, - examples=[ - "openai", - "anthropic", - "google", - "groq", - "deepseek", - "deepgram", - "ultravox", - "custom", - ], - ) + vendor: str | None = None """ The LLM vendor to use. """ @@ -115,7 +105,27 @@ class LlmBaseProperties(JambonzModel): """ A webhook invoked when the LLM calls a tool/function. Receives the tool name and arguments, and should return the tool result. """ + handoff: handoff_1.Handoff | None = None + """ + Declarative transfer-to-human config for s2s verbs. When present, the runtime injects a 'transfer_to_human' tool and runs the packaged transfer choreography when the model calls it. + """ + hangup: hangup_1.Hangup | None = None + """ + Enable the built-in hangup tool for s2s verbs. When present, the runtime injects a 'hangup' tool; when the model calls it, the call is ended with a reason placed in an X-Reason SIP header (LLM-supplied reason wins, falling back to hangup.reason). + """ events: list[str] | None = None """ List of event types to receive via the eventHook. """ + response_timeout_ms: conint(ge=1) | None = Field(default=None, alias="responseTimeoutMs") + """ + Opt-in per-response stall watchdog (s2s vendors that support app-driven turn-taking, e.g. openai). Milliseconds to wait for a solicited response to be acknowledged before declaring a stall. When the timer expires a '{type:"response.timeout"}' event is sent to the eventHook. Omit or set to 0 to disable (the default). + """ + cancel_on_response_timeout: bool | None = Field(default=False, alias="cancelOnResponseTimeout") + """ + When a response stall is detected (see responseTimeoutMs), cancel the in-progress server-side response so the next turn does not collide with an active response. Has no effect unless responseTimeoutMs is set. + """ + cancel_on_barge_in: bool | None = Field(default=False, alias="cancelOnBargeIn") + """ + On caller barge-in, cancel the in-progress server-side response. Intended for app-driven turn-taking configurations where the vendor does not cancel the response itself (server-VAD configurations are unaffected). + """ diff --git a/src/jambonz_sdk/_models/_generated/components/recognizer.py b/src/jambonz_sdk/_models/_generated/components/recognizer.py index 7fdd44c..697fe69 100644 --- a/src/jambonz_sdk/_models/_generated/components/recognizer.py +++ b/src/jambonz_sdk/_models/_generated/components/recognizer.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: components/recognizer -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations @@ -14,20 +14,21 @@ recognizer_assemblyAiOptions, recognizer_awsOptions, recognizer_azureOptions, - recognizer_cobaltOptions, recognizer_customOptions, recognizer_deepgramOptions, recognizer_elevenlabsOptions, recognizer_gladiaOptions, recognizer_googleOptions, + recognizer_gradiumOptions, recognizer_houndifyOptions, recognizer_ibmOptions, - recognizer_nuanceOptions, + recognizer_modulateOptions, recognizer_nvidiaOptions, recognizer_openaiOptions, recognizer_sonioxOptions, recognizer_speechmaticsOptions, - recognizer_verbioOptions, + recognizer_xaiOptions, + recognizer_zoomOptions, ) from . import vad as vad_1 @@ -93,18 +94,19 @@ class Recognizer(JambonzModel): "aws", "microsoft", "deepgram", - "nuance", "ibm", "nvidia", "soniox", - "cobalt", + "xai", + "modulate", + "zoom", + "gradium", "assemblyai", "speechmatics", "openai", "houndify", "gladia", "elevenlabs", - "verbio", "custom", ], ) @@ -291,9 +293,6 @@ class Recognizer(JambonzModel): azure_options: recognizer_azureOptions.AzureRecognizerOptions | None = Field( default=None, alias="azureOptions" ) - nuance_options: recognizer_nuanceOptions.NuanceRecognizerOptions | None = Field( - default=None, alias="nuanceOptions" - ) ibm_options: recognizer_ibmOptions.IbmRecognizerOptions | None = Field( default=None, alias="ibmOptions" ) @@ -303,8 +302,17 @@ class Recognizer(JambonzModel): soniox_options: recognizer_sonioxOptions.SonioxRecognizerOptions | None = Field( default=None, alias="sonioxOptions" ) - cobalt_options: recognizer_cobaltOptions.CobaltRecognizerOptions | None = Field( - default=None, alias="cobaltOptions" + xai_options: recognizer_xaiOptions.XaiRecognizerOptions | None = Field( + default=None, alias="xaiOptions" + ) + modulate_options: recognizer_modulateOptions.ModulateRecognizerOptions | None = Field( + default=None, alias="modulateOptions" + ) + zoom_options: recognizer_zoomOptions.ZoomRecognizerOptions | None = Field( + default=None, alias="zoomOptions" + ) + gradium_options: recognizer_gradiumOptions.GradiumRecognizerOptions | None = Field( + default=None, alias="gradiumOptions" ) assembly_ai_options: recognizer_assemblyAiOptions.AssemblyaiRecognizerOptions | None = Field( default=None, alias="assemblyAiOptions" @@ -324,9 +332,6 @@ class Recognizer(JambonzModel): elevenlabs_options: recognizer_elevenlabsOptions.ElevenlabsRecognizerOptions | None = Field( default=None, alias="elevenlabsOptions" ) - verbio_options: recognizer_verbioOptions.VerbioRecognizerOptions | None = Field( - default=None, alias="verbioOptions" - ) custom_options: recognizer_customOptions.CustomRecognizerOptions | None = Field( default=None, alias="customOptions" ) diff --git a/src/jambonz_sdk/_models/_generated/components/recognizer_assemblyAiOptions.py b/src/jambonz_sdk/_models/_generated/components/recognizer_assemblyAiOptions.py index 1bbcbf7..d8e1743 100644 --- a/src/jambonz_sdk/_models/_generated/components/recognizer_assemblyAiOptions.py +++ b/src/jambonz_sdk/_models/_generated/components/recognizer_assemblyAiOptions.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: components/recognizer-assemblyAiOptions -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations @@ -20,6 +20,15 @@ class ServiceVersion(Enum): v3 = "v3" +class VoiceFocus(Enum): + """ + Isolate the primary voice and suppress background noise before transcription. Use 'near-field' for phones and headsets, 'far-field' for distant microphones. Default: off. + """ + + near_field = "near-field" + far_field = "far-field" + + class AssemblyaiRecognizerOptions(JambonzModel): """ AssemblyAI-specific STT options. Only applies when recognizer vendor is 'assemblyai'. @@ -38,7 +47,7 @@ class AssemblyaiRecognizerOptions(JambonzModel): """ speech_model: str | None = Field(default=None, alias="speechModel") """ - AssemblyAI speech model to use for recognition. + AssemblyAI speech model to use for recognition, e.g. 'universal-3-6-pro', 'universal-3-5-pro' or 'u3-rt-pro'. """ format_turns: bool | None = Field(default=None, alias="formatTurns") """ @@ -54,15 +63,15 @@ class AssemblyaiRecognizerOptions(JambonzModel): default=None, alias="minEndOfTurnSilenceWhenConfident" ) """ - Minimum silence duration (seconds) to trigger end-of-turn when confidence is met. + Minimum silence duration (milliseconds) to trigger end-of-turn when confidence is met. Default: 400. """ max_turn_silence: float | None = Field(default=None, alias="maxTurnSilence") """ - Maximum silence duration (seconds) before forcing end-of-turn. + Maximum silence duration (milliseconds) before forcing end-of-turn. Default: 1280. """ min_turn_silence: float | None = Field(default=None, alias="minTurnSilence") """ - Minimum silence duration (seconds) before allowing end-of-turn. + Minimum silence duration (milliseconds) before allowing end-of-turn. """ keyterms: list[str] | None = None """ @@ -84,3 +93,11 @@ class AssemblyaiRecognizerOptions(JambonzModel): """ Timeout (seconds) for inactivity before closing the stream. """ + language_codes: list[str] | None = Field(default=None, alias="languageCodes") + """ + Languages to bias transcription toward (e.g. ['en', 'es']); code-switching among them is still allowed. Universal-3.5 Pro and Universal-3.6 Pro only. Default: all supported languages. + """ + voice_focus: VoiceFocus | None = Field(default=None, alias="voiceFocus") + """ + Isolate the primary voice and suppress background noise before transcription. Use 'near-field' for phones and headsets, 'far-field' for distant microphones. Default: off. + """ diff --git a/src/jambonz_sdk/_models/_generated/components/recognizer_awsOptions.py b/src/jambonz_sdk/_models/_generated/components/recognizer_awsOptions.py index 26f839a..5b0599d 100644 --- a/src/jambonz_sdk/_models/_generated/components/recognizer_awsOptions.py +++ b/src/jambonz_sdk/_models/_generated/components/recognizer_awsOptions.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: components/recognizer-awsOptions -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/components/recognizer_azureOptions.py b/src/jambonz_sdk/_models/_generated/components/recognizer_azureOptions.py index 5ffd42e..c99a0fa 100644 --- a/src/jambonz_sdk/_models/_generated/components/recognizer_azureOptions.py +++ b/src/jambonz_sdk/_models/_generated/components/recognizer_azureOptions.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: components/recognizer-azureOptions -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/components/recognizer_cobaltOptions.py b/src/jambonz_sdk/_models/_generated/components/recognizer_cobaltOptions.py deleted file mode 100644 index 3325aae..0000000 --- a/src/jambonz_sdk/_models/_generated/components/recognizer_cobaltOptions.py +++ /dev/null @@ -1,43 +0,0 @@ -# generated by datamodel-codegen: -# filename: components/recognizer-cobaltOptions -# timestamp: 2026-05-19T13:39:55+00:00 - -from __future__ import annotations - -from pydantic import ConfigDict, Field - -from jambonz_sdk._models.base import JambonzModel - - -class CobaltRecognizerOptions(JambonzModel): - """ - Cobalt-specific STT options. Only applies when recognizer vendor is 'cobalt'. - """ - - model_config = ConfigDict( - extra="forbid", - ) - server_uri: str | None = Field(default=None, alias="serverUri") - """ - Cobalt server URI. - """ - enable_confusion_network: bool | None = Field(default=None, alias="enableConfusionNetwork") - """ - Enable confusion network output. - """ - metadata: str | None = None - """ - Metadata string to pass to the server. - """ - compiled_context_data: str | None = Field(default=None, alias="compiledContextData") - """ - Compiled context data for biasing recognition. - """ - word_time_offsets: bool | None = Field(default=None, alias="wordTimeOffsets") - """ - Include word-level timestamps. - """ - context_token: str | None = Field(default=None, alias="contextToken") - """ - Context token for server-side context. - """ diff --git a/src/jambonz_sdk/_models/_generated/components/recognizer_customOptions.py b/src/jambonz_sdk/_models/_generated/components/recognizer_customOptions.py index 4b19065..90f24fb 100644 --- a/src/jambonz_sdk/_models/_generated/components/recognizer_customOptions.py +++ b/src/jambonz_sdk/_models/_generated/components/recognizer_customOptions.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: components/recognizer-customOptions -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/components/recognizer_deepgramOptions.py b/src/jambonz_sdk/_models/_generated/components/recognizer_deepgramOptions.py index 4beb87c..83d231c 100644 --- a/src/jambonz_sdk/_models/_generated/components/recognizer_deepgramOptions.py +++ b/src/jambonz_sdk/_models/_generated/components/recognizer_deepgramOptions.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: components/recognizer-deepgramOptions -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations @@ -162,3 +162,7 @@ class DeepgramRecognizerOptions(JambonzModel): """ Eager end-of-turn threshold for faster response. """ + language_hints: list[str] | None = Field(default=None, alias="languageHints") + """ + Language hints for Deepgram Flux Multilingual. BCP-47 codes (e.g. 'en', 'es', 'fr'). Biases transcription toward specified languages. + """ diff --git a/src/jambonz_sdk/_models/_generated/components/recognizer_elevenlabsOptions.py b/src/jambonz_sdk/_models/_generated/components/recognizer_elevenlabsOptions.py index 6334ed8..38c4029 100644 --- a/src/jambonz_sdk/_models/_generated/components/recognizer_elevenlabsOptions.py +++ b/src/jambonz_sdk/_models/_generated/components/recognizer_elevenlabsOptions.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: components/recognizer-elevenlabsOptions -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/components/recognizer_gladiaOptions.py b/src/jambonz_sdk/_models/_generated/components/recognizer_gladiaOptions.py index 8a7019e..65df02e 100644 --- a/src/jambonz_sdk/_models/_generated/components/recognizer_gladiaOptions.py +++ b/src/jambonz_sdk/_models/_generated/components/recognizer_gladiaOptions.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: components/recognizer-gladiaOptions -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations @@ -11,9 +11,9 @@ class GladiaRecognizerOptions(JambonzModel): """ - Gladia-specific STT options. Only applies when recognizer vendor is 'gladia'. See Gladia API documentation for available options. + Gladia-specific STT options. Only applies when recognizer vendor is 'gladia'. Passed through into the Gladia live session request (e.g. language_config, pre_processing, realtime_processing); see the Gladia API documentation for available options. """ model_config = ConfigDict( - extra="forbid", + extra="allow", ) diff --git a/src/jambonz_sdk/_models/_generated/components/recognizer_googleOptions.py b/src/jambonz_sdk/_models/_generated/components/recognizer_googleOptions.py index 69312bd..6b9323f 100644 --- a/src/jambonz_sdk/_models/_generated/components/recognizer_googleOptions.py +++ b/src/jambonz_sdk/_models/_generated/components/recognizer_googleOptions.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: components/recognizer-googleOptions -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations @@ -12,9 +12,18 @@ from jambonz_sdk._models.base import JambonzModel +class Mode(Enum): + """ + Gemini transcription style; applies only to the gemini models. VERBATIM transcribes as spoken; SMART cleans up disfluencies and applies formatting. + """ + + verbatim = "VERBATIM" + smart = "SMART" + + class ServiceVersion(Enum): """ - Google Speech-to-Text API version. + Cloud Speech-to-Text API version. Ignored by the gemini models, which are a different API. """ v1 = "v1" @@ -29,14 +38,28 @@ class GoogleRecognizerOptions(JambonzModel): model_config = ConfigDict( extra="forbid", ) + mode: Mode | None = None + """ + Gemini transcription style; applies only to the gemini models. VERBATIM transcribes as spoken; SMART cleans up disfluencies and applies formatting. + """ + custom_vocabulary: list[str] | None = Field( + default=None, alias="customVocabulary", max_length=1000 + ) + """ + Up to 1000 terms, acronyms, brand names or proper nouns to bias recognition towards; applies only to the gemini models. + """ service_version: ServiceVersion | None = Field(default=None, alias="serviceVersion") """ - Google Speech-to-Text API version. + Cloud Speech-to-Text API version. Ignored by the gemini models, which are a different API. """ recognizer_id: str | None = Field(default=None, alias="recognizerId") """ ID of a Google Speech recognizer resource (v2 only). """ + parent_path: str | None = Field(default=None, alias="parentPath") + """ + Parent resource path for the Cloud Speech-to-Text v2 recognizer resource, e.g. 'projects/{project}/locations/{location}'. A location other than global also routes the request to that region's endpoint. + """ speech_start_timeout_ms: float | None = Field(default=None, alias="speechStartTimeoutMs") """ Timeout in milliseconds to wait for speech to start. diff --git a/src/jambonz_sdk/_models/_generated/components/recognizer_gradiumOptions.py b/src/jambonz_sdk/_models/_generated/components/recognizer_gradiumOptions.py new file mode 100644 index 0000000..6937ce8 --- /dev/null +++ b/src/jambonz_sdk/_models/_generated/components/recognizer_gradiumOptions.py @@ -0,0 +1,81 @@ +# generated by datamodel-codegen: +# filename: components/recognizer-gradiumOptions +# timestamp: 2026-10-14T10:55:09+00:00 + +from __future__ import annotations + +from pydantic import ConfigDict, Field, PositiveFloat, confloat, conint + +from jambonz_sdk._models.base import JambonzModel + + +class TurnDetection(JambonzModel): + """ + Tunes when a final transcript is cut. Gradium reports, every 80 ms, the probability that the speaker stays silent over several horizons; the turn ends when the chosen horizon's probability exceeds the threshold. + """ + + model_config = ConfigDict( + extra="forbid", + ) + threshold: confloat(lt=1.0, gt=0.0) | None = None + """ + Inactivity probability that ends a turn. Default 0.5; lower ends turns sooner. + """ + horizon: PositiveFloat | None = Field(default=None, examples=[0.5, 1, 2, 3]) + """ + Prediction horizon in seconds to read the probability from (Gradium reports 0.5, 1, 2 and 3). Defaults to the longest; a shorter horizon ends turns sooner. + """ + + +class GradiumRecognizerOptions(JambonzModel): + """ + Gradium streaming STT options. Only applies when recognizer vendor is 'gradium'. Gradium streams words as they are recognized (sent as interim results when interim is enabled); a final transcript is produced when Gradium's semantic VAD predicts the speaker has finished, and in the agent verb each final ends the user's turn. Transcripts are reported with confidence 1.0. Supported languages: en, fr, es, pt, de; any other language uses Gradium's automatic language detection. + """ + + model_config = ConfigDict( + extra="forbid", + ) + api_key: str | None = Field(default=None, alias="apiKey") + """ + Gradium API key. Overrides the key configured in jambonz. + """ + model: str | None = Field(default=None, examples=["default"]) + """ + STT model name. Defaults to 'default', Gradium's current production model. + """ + delay_in_frames: conint(ge=7, le=56) | None = Field(default=None, alias="delayInFrames") + """ + How far the model looks ahead before emitting a word, in 80 ms frames. Lower is faster, higher is more accurate. Gradium's default is 10. + """ + temp: confloat(ge=0.0, le=1.5) | None = None + """ + Sampling temperature for the transcript text. 0 is greedy (most deterministic). + """ + padding_bonus: confloat(ge=-4.0, le=4.0) | None = Field(default=None, alias="paddingBonus") + """ + Biases the model toward emitting words sooner (negative) or later (positive). Default 0. + """ + keywords: list[str] | None = Field(default=None, max_length=500) + """ + Words to boost (brand names, jargon). Each entry must be a single word; multi-word entries are split. When omitted, recognizer.hints are used. + """ + keyword_boost: float | None = Field(default=None, alias="keywordBoost") + """ + How strongly to favor the keywords. Gradium recommends 3; 4 is the practical ceiling, negative values suppress words. When omitted with hints, recognizer.hintsBoost is used. + """ + keyword_boost_id: str | None = Field(default=None, alias="keywordBoostId") + """ + Id of a keyword dictionary saved in Gradium Studio. Replaces keywords and hints. Gradium does not reject an unknown id: it is accepted silently and transcripts lose casing, punctuation and number formatting, so check the id. + """ + turn_detection: TurnDetection | None = None + """ + Tunes when a final transcript is cut. Gradium reports, every 80 ms, the probability that the speaker stays silent over several horizons; the turn ends when the chosen horizon's probability exceeds the threshold. + """ + gradium_stt_uri: str | None = Field(default=None, alias="gradiumSttUri") + """ + Custom host to connect to instead of api.gradium.ai, e.g. eu.api.gradium.ai or us.api.gradium.ai to pin data residency, or a proxy. Defaults to the host of the credential's API URL. + """ + gradium_stt_use_tls: bool | None = Field(default=None, alias="gradiumSttUseTls") + """ + Use TLS when connecting to a custom host. Default true. + """ diff --git a/src/jambonz_sdk/_models/_generated/components/recognizer_houndifyOptions.py b/src/jambonz_sdk/_models/_generated/components/recognizer_houndifyOptions.py index 0015156..b3f4900 100644 --- a/src/jambonz_sdk/_models/_generated/components/recognizer_houndifyOptions.py +++ b/src/jambonz_sdk/_models/_generated/components/recognizer_houndifyOptions.py @@ -1,12 +1,12 @@ # generated by datamodel-codegen: # filename: components/recognizer-houndifyOptions -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations from typing import Any -from pydantic import ConfigDict, Field +from pydantic import ConfigDict, Field, confloat from jambonz_sdk._models.base import JambonzModel @@ -167,3 +167,13 @@ class HoundifyRecognizerOptions(JambonzModel): """ Absolute timeout for audio queries. """ + eoq_threshold: confloat(ge=0.0, le=1.0) | None = Field(default=None, alias="eoqThreshold") + """ + End-of-query likelihood threshold (0.0-1.0) to trigger end of speech when segmentation is disabled. Default 0.8, set to 0 to disable. + """ + vad_stop_threshold: confloat(ge=0.0, le=1.0) | None = Field( + default=None, alias="vadStopThreshold" + ) + """ + VAD probability threshold to trigger end of speech when segmentation is disabled. When VAD drops below this value after speech is detected, streaming stops. Default 0.05, set to 0 to disable. + """ diff --git a/src/jambonz_sdk/_models/_generated/components/recognizer_ibmOptions.py b/src/jambonz_sdk/_models/_generated/components/recognizer_ibmOptions.py index e185667..0cb7ab5 100644 --- a/src/jambonz_sdk/_models/_generated/components/recognizer_ibmOptions.py +++ b/src/jambonz_sdk/_models/_generated/components/recognizer_ibmOptions.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: components/recognizer-ibmOptions -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/components/recognizer_modulateOptions.py b/src/jambonz_sdk/_models/_generated/components/recognizer_modulateOptions.py new file mode 100644 index 0000000..51818ab --- /dev/null +++ b/src/jambonz_sdk/_models/_generated/components/recognizer_modulateOptions.py @@ -0,0 +1,51 @@ +# generated by datamodel-codegen: +# filename: components/recognizer-modulateOptions +# timestamp: 2026-10-14T10:55:09+00:00 + +from __future__ import annotations + +from pydantic import ConfigDict, Field + +from jambonz_sdk._models.base import JambonzModel + + +class ModulateRecognizerOptions(JambonzModel): + """ + Modulate-specific STT options. Only applies when recognizer vendor is 'modulate'. Modulate does not return confidence scores; transcripts are reported with confidence 1.0. The model is selected with the top-level recognizer 'model' property: 'velma-2-stt-streaming' (multilingual, the default) or 'velma-2-stt-streaming-english-v2' (low-latency English). + """ + + model_config = ConfigDict( + extra="forbid", + ) + api_key: str | None = Field(default=None, alias="apiKey") + """ + Modulate API key. Overrides the key configured in jambonz. + """ + endpointing: bool | None = None + """ + Only applies to model 'velma-2-stt-streaming-english-v2': emit a final transcript for each pause-delimited speech segment. Defaults to true in jambonz; set false to receive a single final transcript when the stream ends. + """ + emotion_signal: bool | None = Field(default=None, alias="emotionSignal") + """ + Only applies to model 'velma-2-stt-streaming': detect the speaker's emotional tone for each utterance. + """ + accent_signal: bool | None = Field(default=None, alias="accentSignal") + """ + Only applies to model 'velma-2-stt-streaming': identify the speaker's accent for each utterance. + """ + deepfake_signal: bool | None = Field(default=None, alias="deepfakeSignal") + """ + Only applies to model 'velma-2-stt-streaming': score each utterance for synthetic (deepfake) voice likelihood. + """ + pii_phi_tagging: bool | None = Field(default=None, alias="piiPhiTagging") + """ + Only applies to model 'velma-2-stt-streaming': wrap detected PII/PHI in tags within the transcript text. + """ + modulate_stt_uri: str | None = Field(default=None, alias="modulateSttUri") + """ + Custom host to connect to instead of platform.modulate.ai. + """ + modulate_stt_use_tls: bool | None = Field(default=None, alias="modulateSttUseTls") + """ + Use TLS when connecting to a custom host. Default true. + """ diff --git a/src/jambonz_sdk/_models/_generated/components/recognizer_nuanceOptions.py b/src/jambonz_sdk/_models/_generated/components/recognizer_nuanceOptions.py deleted file mode 100644 index 7c44cbc..0000000 --- a/src/jambonz_sdk/_models/_generated/components/recognizer_nuanceOptions.py +++ /dev/null @@ -1,226 +0,0 @@ -# generated by datamodel-codegen: -# filename: components/recognizer-nuanceOptions -# timestamp: 2026-05-19T13:39:55+00:00 - -from __future__ import annotations - -from enum import Enum -from typing import Any - -from pydantic import ConfigDict, Field - -from jambonz_sdk._models.base import JambonzModel - - -class UtteranceDetectionMode(Enum): - """ - How utterance boundaries are detected. - """ - - single = "single" - multiple = "multiple" - disabled = "disabled" - - -class ResultType(Enum): - """ - Type of results to return. - """ - - final = "final" - partial = "partial" - immutable_partial = "immutable_partial" - - -class Formatting(JambonzModel): - """ - Formatting options for recognition results. - """ - - scheme: str - """ - Formatting scheme name. - """ - options: dict[str, Any] - """ - Scheme-specific formatting options. - """ - - -class Type(Enum): - undefined_resource_type = "undefined_resource_type" - wordset = "wordset" - compiled_wordset = "compiled_wordset" - domain_lm = "domain_lm" - speaker_profile = "speaker_profile" - grammar = "grammar" - settings = "settings" - - -class ExternalReference(JambonzModel): - """ - External resource reference. - """ - - type: Type | None = None - uri: str | None = None - max_load_failures: bool | None = Field(default=None, alias="maxLoadFailures") - request_timeout_ms: float | None = Field(default=None, alias="requestTimeoutMs") - headers: dict[str, Any] | None = None - - -class WeightName(Enum): - default_weight = "defaultWeight" - lowest = "lowest" - low = "low" - medium = "medium" - high = "high" - highest = "highest" - - -class Reuse(Enum): - undefined_reuse = "undefined_reuse" - low_reuse = "low_reuse" - high_reuse = "high_reuse" - - -class Resource(JambonzModel): - external_reference: ExternalReference | None = Field(default=None, alias="externalReference") - """ - External resource reference. - """ - inline_wordset: str | None = Field(default=None, alias="inlineWordset") - """ - Inline wordset JSON string. - """ - builtin: str | None = None - """ - Built-in grammar name. - """ - inline_grammar: str | None = Field(default=None, alias="inlineGrammar") - """ - Inline SRGS grammar. - """ - wakeup_word: list[str] | None = Field(default=None, alias="wakeupWord") - """ - Wakeup words. - """ - weight_name: WeightName | None = Field(default=None, alias="weightName") - weight_value: float | None = Field(default=None, alias="weightValue") - reuse: Reuse | None = None - - -class NuanceRecognizerOptions(JambonzModel): - """ - Nuance Mix specific options. Only applies when recognizer vendor is 'nuance'. - """ - - model_config = ConfigDict( - extra="forbid", - ) - client_id: str | None = Field(default=None, alias="clientId") - """ - Nuance Mix client ID. - """ - secret: str | None = None - """ - Nuance Mix client secret. - """ - krypton_endpoint: str | None = Field(default=None, alias="kryptonEndpoint") - """ - Custom Nuance Krypton endpoint URL. - """ - topic: str | None = None - """ - Recognition topic (domain). - """ - utterance_detection_mode: UtteranceDetectionMode | None = Field( - default=None, alias="utteranceDetectionMode" - ) - """ - How utterance boundaries are detected. - """ - punctuation: bool | None = None - """ - Enable automatic punctuation. - """ - profanity_filter: bool | None = Field(default=None, alias="profanityFilter") - """ - Filter profanity from results. - """ - include_tokenization: bool | None = Field(default=None, alias="includeTokenization") - """ - Include tokenization data in results. - """ - discard_speaker_adaptation: bool | None = Field(default=None, alias="discardSpeakerAdaptation") - """ - Discard speaker adaptation data. - """ - suppress_call_recording: bool | None = Field(default=None, alias="suppressCallRecording") - """ - Suppress call recording on the Nuance side. - """ - mask_load_failures: bool | None = Field(default=None, alias="maskLoadFailures") - """ - Mask resource load failures. - """ - suppress_initial_capitalization: bool | None = Field( - default=None, alias="suppressInitialCapitalization" - ) - """ - Suppress initial capitalization of results. - """ - allow_zero_base_lm_weight: bool | None = Field(default=None, alias="allowZeroBaseLmWeight") - """ - Allow zero base language model weight. - """ - filter_wakeup_word: bool | None = Field(default=None, alias="filterWakeupWord") - """ - Filter wakeup words from results. - """ - result_type: ResultType | None = Field(default=None, alias="resultType") - """ - Type of results to return. - """ - no_input_timeout_ms: float | None = Field(default=None, alias="noInputTimeoutMs") - """ - Timeout in milliseconds before no-input event. - """ - recognition_timeout_ms: float | None = Field(default=None, alias="recognitionTimeoutMs") - """ - Maximum recognition duration in milliseconds. - """ - utterance_end_silence_ms: float | None = Field(default=None, alias="utteranceEndSilenceMs") - """ - Silence duration in milliseconds to detect end of utterance. - """ - max_hypotheses: float | None = Field(default=None, alias="maxHypotheses") - """ - Maximum number of recognition hypotheses to return. - """ - speech_domain: str | None = Field(default=None, alias="speechDomain") - """ - Speech domain for optimized recognition. - """ - formatting: Formatting | None = None - """ - Formatting options for recognition results. - """ - client_data: dict[str, Any] | None = Field(default=None, alias="clientData") - """ - Custom client data to pass to Nuance. - """ - user_id: str | None = Field(default=None, alias="userId") - """ - User ID for speaker adaptation. - """ - speech_detection_sensitivity: float | None = Field( - default=None, alias="speechDetectionSensitivity" - ) - """ - Speech detection sensitivity (0-1). - """ - resources: list[Resource] | None = None - """ - Array of Nuance recognition resources (grammars, wordsets, etc.). - """ diff --git a/src/jambonz_sdk/_models/_generated/components/recognizer_nvidiaOptions.py b/src/jambonz_sdk/_models/_generated/components/recognizer_nvidiaOptions.py index 58ccc3b..00173d5 100644 --- a/src/jambonz_sdk/_models/_generated/components/recognizer_nvidiaOptions.py +++ b/src/jambonz_sdk/_models/_generated/components/recognizer_nvidiaOptions.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: components/recognizer-nvidiaOptions -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/components/recognizer_openaiOptions.py b/src/jambonz_sdk/_models/_generated/components/recognizer_openaiOptions.py index 9137764..8d161e6 100644 --- a/src/jambonz_sdk/_models/_generated/components/recognizer_openaiOptions.py +++ b/src/jambonz_sdk/_models/_generated/components/recognizer_openaiOptions.py @@ -1,12 +1,12 @@ # generated by datamodel-codegen: # filename: components/recognizer-openaiOptions -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations from enum import Enum -from pydantic import ConfigDict, Field +from pydantic import ConfigDict, Field, conint from jambonz_sdk._models.base import JambonzModel @@ -28,6 +28,18 @@ class PromptTemplates(JambonzModel): """ +class Delay(Enum): + """ + Latency/accuracy trade-off for 'gpt-live-transcribe': lower emits partial text sooner, higher gives the model more audio context and can improve accuracy. + """ + + minimal = "minimal" + low = "low" + medium = "medium" + high = "high" + xhigh = "xhigh" + + class InputAudioNoiseReduction(Enum): """ Input audio noise reduction mode. @@ -113,6 +125,18 @@ class OpenaiRecognizerOptions(JambonzModel): """ Language code for recognition. """ + languages: list[str] | None = Field(default=None, min_length=1) + """ + Language hints for 'gpt-live-transcribe', which takes a list rather than a single code (e.g. ['en','fr']). Overrides the recognizer language; do not combine with 'language'. + """ + keywords: list[str] | None = None + """ + Literal term hints (product names, acronyms, medications) for 'gpt-live-transcribe'. Hints, not required output. Defaults to the recognizer's 'hints' when unset. + """ + delay: Delay | None = None + """ + Latency/accuracy trade-off for 'gpt-live-transcribe': lower emits partial text sooner, higher gives the model more audio context and can improve accuracy. + """ input_audio_noise_reduction: InputAudioNoiseReduction | None = None """ Input audio noise reduction mode. @@ -121,3 +145,15 @@ class OpenaiRecognizerOptions(JambonzModel): """ Turn detection configuration for the OpenAI Realtime API. """ + vad_mode: conint(ge=0, le=3) | None = Field(default=None, alias="vadMode") + """ + Local VAD aggressiveness (0-3). Used only with the client-endpointed models ('gpt-live-transcribe', 'gpt-realtime-whisper'), which reject server turn detection and rely on jambonz to end each turn. + """ + vad_silence_ms: conint(ge=1) | None = Field(default=None, alias="vadSilenceMs") + """ + Milliseconds of silence to detect end-of-speech for local VAD. Used only with the client-endpointed models. + """ + vad_voice_ms: conint(ge=1) | None = Field(default=None, alias="vadVoiceMs") + """ + Milliseconds of voice to detect start-of-speech for local VAD. Used only with the client-endpointed models. + """ diff --git a/src/jambonz_sdk/_models/_generated/components/recognizer_sonioxOptions.py b/src/jambonz_sdk/_models/_generated/components/recognizer_sonioxOptions.py index 536f762..95e6bd4 100644 --- a/src/jambonz_sdk/_models/_generated/components/recognizer_sonioxOptions.py +++ b/src/jambonz_sdk/_models/_generated/components/recognizer_sonioxOptions.py @@ -1,16 +1,58 @@ # generated by datamodel-codegen: # filename: components/recognizer-sonioxOptions -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations from typing import Any -from pydantic import ConfigDict, Field +from pydantic import ConfigDict, Field, confloat, conint from jambonz_sdk._models.base import JambonzModel +class GeneralItem(JambonzModel): + model_config = ConfigDict( + extra="forbid", + ) + key: str + value: str + + +class TranslationTerm(JambonzModel): + model_config = ConfigDict( + extra="forbid", + ) + source: str + target: str + + +class Context(JambonzModel): + """ + Soniox v5 recognition context to improve accuracy (max ~8000 tokens). See https://soniox.com/docs/stt/concepts/context. When supplied this takes precedence outright over the 'speechContext' and 'hints' shortcuts. + """ + + model_config = ConfigDict( + extra="forbid", + ) + general: list[GeneralItem] | None = None + """ + Structured key/value metadata describing the domain, topic, participants, setting, etc. + """ + text: str | None = None + """ + Free-form background text (history of prior interactions, reference documents, meeting notes, summaries). + """ + terms: list[str] | None = None + """ + Domain-specific vocabulary to boost recognition. + """ + translation_terms: list[TranslationTerm] | None = None + """ + Source/target term mappings; only used when translation is enabled. + """ + + class Storage(JambonzModel): """ Soniox storage configuration for persisting transcripts. @@ -56,19 +98,55 @@ class SonioxRecognizerOptions(JambonzModel): """ model: str | None = None """ - Soniox recognition model. + Soniox recognition model (e.g. 'stt-rt-v5'). + """ + language_hints: list[str] | None = Field(default=None, alias="languageHints") + """ + Language hints for the v5 multilingual model, as ISO language codes (e.g. ['en','es']). Improves accuracy and speeds up language detection. + """ + enable_language_identification: bool | None = Field( + default=None, alias="enableLanguageIdentification" + ) + """ + Enable language identification (v5); each recognized token is tagged with its detected language. + """ + enable_speaker_diarization: bool | None = Field(default=None, alias="enableSpeakerDiarization") + """ + Enable speaker diarization (v5); each recognized token is tagged with a speaker. """ endpoint_detection: bool | None = Field(default=None, alias="endpointDetection") """ Enable endpoint detection. """ + endpoint_sensitivity: confloat(ge=-1.0, le=1.0) | None = Field( + default=None, alias="endpointSensitivity" + ) + """ + Endpoint detection sensitivity (v5), -1.0 to 1.0 (default 0.0). Higher values finalize endpoints faster; lower values are more conservative. + """ + max_endpoint_delay_ms: conint(ge=500, le=3000) | None = Field( + default=None, alias="maxEndpointDelayMs" + ) + """ + Maximum delay in milliseconds before an endpoint is forced (v5), 500-3000 (default 2000). + """ + max_non_final_tokens_duration_ms: conint(ge=0) | None = Field( + default=None, alias="maxNonFinalTokensDurationMs" + ) + """ + Maximum duration in milliseconds that tokens may remain non-final before forced finalization (v5). + """ profanity_filter: bool | None = Field(default=None, alias="profanityFilter") """ Filter profanity from results. """ + context: Context | None = None + """ + Soniox v5 recognition context to improve accuracy (max ~8000 tokens). See https://soniox.com/docs/stt/concepts/context. When supplied this takes precedence outright over the 'speechContext' and 'hints' shortcuts. + """ speech_context: str | None = Field(default=None, alias="speechContext") """ - Speech context for improved recognition. + Shortcut for context.text (free-text background). Ignored when the full 'context' object is supplied. The generic recognizer 'hints' are likewise sent as context.terms unless 'context' is supplied. """ client_request_reference: str | None = Field(default=None, alias="clientRequestReference") """ diff --git a/src/jambonz_sdk/_models/_generated/components/recognizer_speechmaticsOptions.py b/src/jambonz_sdk/_models/_generated/components/recognizer_speechmaticsOptions.py index e47050d..526d62f 100644 --- a/src/jambonz_sdk/_models/_generated/components/recognizer_speechmaticsOptions.py +++ b/src/jambonz_sdk/_models/_generated/components/recognizer_speechmaticsOptions.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: components/recognizer-speechmaticsOptions -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations from enum import Enum from typing import Any -from pydantic import ConfigDict +from pydantic import ConfigDict, Field from jambonz_sdk._models.base import JambonzModel @@ -50,8 +50,23 @@ class AudioFilteringConfig(JambonzModel): volume_threshold: float +class Replacement(JambonzModel): + model_config = ConfigDict( + extra="forbid", + ) + from_: str = Field(..., alias="from") + to: str + + class TranscriptFilteringConfig(JambonzModel): - remove_disfluencies: bool + model_config = ConfigDict( + extra="forbid", + ) + remove_disfluencies: bool | None = None + replacements: list[Replacement] | None = None + """ + Word replacement rules applied to the transcript. 'from' accepts a plain word or a /regex/. + """ class TranscriptionConfig(JambonzModel): @@ -96,7 +111,11 @@ class TranscriptionConfig(JambonzModel): punctuation_overrides: PunctuationOverrides | None = None operating_point: str | None = None """ - Operating point (standard or enhanced). + Deprecated by Speechmatics in favor of 'model'. Operating point (standard or enhanced). + """ + model: str | None = None + """ + Model selection (replaces operating_point), e.g. standard, enhanced, or melia-1. """ enable_entities: bool | None = None """ diff --git a/src/jambonz_sdk/_models/_generated/components/recognizer_verbioOptions.py b/src/jambonz_sdk/_models/_generated/components/recognizer_verbioOptions.py deleted file mode 100644 index d824d96..0000000 --- a/src/jambonz_sdk/_models/_generated/components/recognizer_verbioOptions.py +++ /dev/null @@ -1,55 +0,0 @@ -# generated by datamodel-codegen: -# filename: components/recognizer-verbioOptions -# timestamp: 2026-05-19T13:39:55+00:00 - -from __future__ import annotations - -from pydantic import ConfigDict - -from jambonz_sdk._models.base import JambonzModel - - -class VerbioRecognizerOptions(JambonzModel): - """ - Verbio-specific STT options. Only applies when recognizer vendor is 'verbio'. - """ - - model_config = ConfigDict( - extra="forbid", - ) - enable_formatting: bool | None = None - """ - Enable text formatting of results. - """ - enable_diarization: bool | None = None - """ - Enable speaker diarization. - """ - topic: float | None = None - """ - Topic ID for domain-specific recognition. - """ - inline_grammar: str | None = None - """ - Inline SRGS grammar for constrained recognition. - """ - grammar_uri: str | None = None - """ - URI of an external grammar resource. - """ - label: str | None = None - """ - Label for the recognition session. - """ - recognition_timeout: float | None = None - """ - Maximum recognition duration in seconds. - """ - speech_complete_timeout: float | None = None - """ - Silence duration in seconds after complete speech. - """ - speech_incomplete_timeout: float | None = None - """ - Silence duration in seconds after incomplete speech. - """ diff --git a/src/jambonz_sdk/_models/_generated/components/recognizer_xaiOptions.py b/src/jambonz_sdk/_models/_generated/components/recognizer_xaiOptions.py new file mode 100644 index 0000000..22b78d8 --- /dev/null +++ b/src/jambonz_sdk/_models/_generated/components/recognizer_xaiOptions.py @@ -0,0 +1,53 @@ +# generated by datamodel-codegen: +# filename: components/recognizer-xaiOptions +# timestamp: 2026-10-14T10:55:09+00:00 + +from __future__ import annotations + +from pydantic import ConfigDict, Field + +from jambonz_sdk._models.base import JambonzModel + + +class XaiRecognizerOptions(JambonzModel): + """ + xAI-specific STT options. Only applies when recognizer vendor is 'xai'. + """ + + model_config = ConfigDict( + extra="forbid", + ) + api_key: str | None = Field(default=None, alias="apiKey") + """ + xAI API key. Overrides the key configured in jambonz. + """ + model: str | None = Field( + default=None, examples=["grok-voice-transcribe-2.0", "grok-voice-transcribe-1.0"] + ) + """ + Transcription model to use, e.g. 'grok-voice-transcribe-2.0'. Defaults to whatever xAI currently defaults to. + """ + endpointing: int | None = None + """ + Milliseconds of silence before an utterance is considered final. Range 0-5000. + """ + diarize: bool | None = None + """ + Enable speaker diarization. + """ + filler_words: bool | None = Field(default=None, alias="fillerWords") + """ + Include filler words (um, uh) in transcript. + """ + keyterms: list[str] | None = None + """ + Key terms to boost recognition for. + """ + smart_turn: float | None = Field(default=None, alias="smartTurn") + """ + End-of-turn confidence threshold, between 0.0 and 1.0. + """ + smart_turn_timeout: int | None = Field(default=None, alias="smartTurnTimeout") + """ + Maximum milliseconds of silence to wait before forcing an end-of-turn final result. Range 1-5000. + """ diff --git a/src/jambonz_sdk/_models/_generated/components/recognizer_zoomOptions.py b/src/jambonz_sdk/_models/_generated/components/recognizer_zoomOptions.py new file mode 100644 index 0000000..53db1c6 --- /dev/null +++ b/src/jambonz_sdk/_models/_generated/components/recognizer_zoomOptions.py @@ -0,0 +1,57 @@ +# generated by datamodel-codegen: +# filename: components/recognizer-zoomOptions +# timestamp: 2026-10-14T10:55:09+00:00 + +from __future__ import annotations + +from pydantic import ConfigDict, Field, confloat, conint + +from jambonz_sdk._models.base import JambonzModel + + +class TurnDetection(JambonzModel): + """ + Tunes Zoom's server-side turn detection, which is silence-based: a turn ends after silence_duration_ms of silence, whether or not the sentence is complete. Out-of-range values make Zoom end the session, so they are bounded here. + """ + + model_config = ConfigDict( + extra="forbid", + ) + threshold: confloat(ge=0.2, le=0.9) | None = None + """ + Speech detection threshold. Higher values need louder or clearer speech to start a turn. + """ + prefix_padding_ms: conint(ge=0) | None = None + """ + Milliseconds of audio before detected speech to include in the turn. + """ + silence_duration_ms: conint(gt=200) | None = None + """ + Milliseconds of silence that end a turn. Zoom's default is about 350; raise it so a mid-sentence pause does not split the turn. + """ + + +class ZoomRecognizerOptions(JambonzModel): + """ + Zoom Scribe (Live mode) STT options. Only applies when recognizer vendor is 'zoom'. Zoom segments turns server-side (silence-based) and returns one final transcript per speech turn; in the agent verb each one ends the user's turn: there are no interim results and no confidence scores (transcripts are reported with confidence 1.0). Supported languages: en-US, zh-CN, ja-JP, es-ES, it-IT, fr-FR, de-DE, ar-SA, ar-AE, pt-BR, pt-PT. + """ + + model_config = ConfigDict( + extra="forbid", + ) + api_key: str | None = Field(default=None, alias="apiKey") + """ + Zoom Build platform API key (or JWT). Overrides the key configured in jambonz. + """ + turn_detection: TurnDetection | None = None + """ + Tunes Zoom's server-side turn detection, which is silence-based: a turn ends after silence_duration_ms of silence, whether or not the sentence is complete. Out-of-range values make Zoom end the session, so they are bounded here. + """ + zoom_stt_uri: str | None = Field(default=None, alias="zoomSttUri") + """ + Custom host to connect to instead of api.zoom.us (e.g. a proxy). + """ + zoom_stt_use_tls: bool | None = Field(default=None, alias="zoomSttUseTls") + """ + Use TLS when connecting to a custom host. Default true. + """ diff --git a/src/jambonz_sdk/_models/_generated/components/synthesizer.py b/src/jambonz_sdk/_models/_generated/components/synthesizer.py index f490ff1..27c94b3 100644 --- a/src/jambonz_sdk/_models/_generated/components/synthesizer.py +++ b/src/jambonz_sdk/_models/_generated/components/synthesizer.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: components/synthesizer -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations @@ -46,13 +46,17 @@ class Synthesizer(JambonzModel): "microsoft", "elevenlabs", "cartesia", + "fishaudio", + "gradium", + "kugelaudio", + "nineninesix", + "speechify", "deepgram", + "deepgramflux", "ibm", - "nuance", "nvidia", "wellsaid", "whisper", - "verbio", "custom", ], ) diff --git a/src/jambonz_sdk/_models/_generated/components/target.py b/src/jambonz_sdk/_models/_generated/components/target.py index 9165c2c..f1aa330 100644 --- a/src/jambonz_sdk/_models/_generated/components/target.py +++ b/src/jambonz_sdk/_models/_generated/components/target.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: components/target -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/components/transferOptions.py b/src/jambonz_sdk/_models/_generated/components/transferOptions.py new file mode 100644 index 0000000..bddd05e --- /dev/null +++ b/src/jambonz_sdk/_models/_generated/components/transferOptions.py @@ -0,0 +1,176 @@ +# generated by datamodel-codegen: +# filename: components/transferOptions +# timestamp: 2026-10-14T10:55:09+00:00 + +from __future__ import annotations + +from enum import Enum + +from pydantic import ConfigDict, Field + +from jambonz_sdk._models.base import JambonzModel + +from . import actionHook +from . import amd as amd_1 +from . import target as target_1 + + +class Mode(Enum): + """ + Transfer mode: 'blind' hands off immediately without waiting for the destination to accept; 'warm' allows the agent to brief the destination before connecting the caller. + """ + + blind = "blind" + warm = "warm" + + +class BlindMethod(Enum): + """ + blind only: 'refer' sends a SIP REFER to the remote end; 'dial' places a bridged outbound call. Not auto-detected — must be set explicitly when the default 'refer' is not appropriate. + """ + + refer = "refer" + dial = "dial" + + +class Confirm(JambonzModel): + """ + Optional human-side acceptance gate. The destination hears a prompt and must press the specified digit to accept. A wrong digit, timeout, or hangup is treated as a 'decline' disposition. + """ + + model_config = ConfigDict( + extra="forbid", + ) + prompt: str + """ + Text or URL of the prompt played to the destination. + """ + digit: str + """ + Single DTMF digit the destination must press to accept the transfer. + """ + + +class OnNoAnswer(Enum): + """ + Action when the destination does not answer within timeout. + """ + + return_ = "return" + voicemail = "voicemail" + hangup = "hangup" + + +class OnBusy(Enum): + """ + Action when the destination returns a busy signal. + """ + + return_ = "return" + voicemail = "voicemail" + hangup = "hangup" + + +class OnDecline(Enum): + """ + Action when the destination declines the transfer (confirm gate failed, AMD detected machine, or explicit rejection). + """ + + return_ = "return" + voicemail = "voicemail" + hangup = "hangup" + + +class OnFailure(Enum): + """ + Action when a protocol-level error occurs on the destination leg. + """ + + return_ = "return" + voicemail = "voicemail" + hangup = "hangup" + + +class Disposition(JambonzModel): + """ + Per-outcome fallback actions when the transfer does not complete normally. + """ + + model_config = ConfigDict( + extra="forbid", + ) + on_no_answer: OnNoAnswer | None = Field(default="return", alias="onNoAnswer") + """ + Action when the destination does not answer within timeout. + """ + on_busy: OnBusy | None = Field(default="return", alias="onBusy") + """ + Action when the destination returns a busy signal. + """ + on_decline: OnDecline | None = Field(default="return", alias="onDecline") + """ + Action when the destination declines the transfer (confirm gate failed, AMD detected machine, or explicit rejection). + """ + on_failure: OnFailure | None = Field(default="return", alias="onFailure") + """ + Action when a protocol-level error occurs on the destination leg. + """ + voicemail_url: str | None = Field(default=None, alias="voicemailUrl") + """ + SIP URI or HTTP URL to route the caller to voicemail. Required when any disposition value is 'voicemail'. + """ + + +class TransferOptions(JambonzModel): + """ + Shared configuration block for blind and warm transfer choreography, including destination targeting, caller handling, confirmation gates, and failure dispositions. Reused by the transfer verb and the handoff component. + """ + + mode: Mode + """ + Transfer mode: 'blind' hands off immediately without waiting for the destination to accept; 'warm' allows the agent to brief the destination before connecting the caller. + """ + blind_method: BlindMethod | None = Field(default="refer", alias="blindMethod") + """ + blind only: 'refer' sends a SIP REFER to the remote end; 'dial' places a bridged outbound call. Not auto-detected — must be set explicitly when the default 'refer' is not appropriate. + """ + caller_present: bool | None = Field(default=False, alias="callerPresent") + """ + warm only: when true the caller is added to a three-way conference and can hear the brief; when false the caller is parked (on hold) and does not hear the conversation between agent and destination. + """ + target: list[target_1.Target] = Field(..., min_length=1) + """ + One or more call destinations. All targets are rung simultaneously; the first to answer wins. + """ + caller_id: str | None = Field(default=None, alias="callerId") + """ + Caller ID presented to the transfer destination. + """ + on_hold_hook: actionHook.ActionHook | None = Field(default=None, alias="onHoldHook") + """ + warm/parked only: verbs to execute for the parked caller while the agent briefs the destination (e.g. play hold music). Only invoked when callerPresent is false. + """ + timeout: float | None = 30 + """ + Seconds to wait for the destination to answer before applying the onNoAnswer disposition. + """ + confirm: Confirm | None = None + """ + Optional human-side acceptance gate. The destination hears a prompt and must press the specified digit to accept. A wrong digit, timeout, or hangup is treated as a 'decline' disposition. + """ + amd: amd_1.AnsweringMachineDetection | None = None + """ + warm only: optional answering-machine detection run on the destination leg. Off by default; a 'machine' result is treated as a 'decline' disposition. + """ + disposition: Disposition | None = None + """ + Per-outcome fallback actions when the transfer does not complete normally. + """ + action_hook: actionHook.ActionHook | None = Field(default=None, alias="actionHook") + """ + Fires when the transfer resolves — whether the call was bridged, returned to the caller, sent to voicemail, or failed. + """ + event_hook: actionHook.ActionHook | None = Field(default=None, alias="eventHook") + """ + Optional hook for in-progress transfer events (e.g. destination ringing, brief started). + """ diff --git a/src/jambonz_sdk/_models/_generated/components/vad.py b/src/jambonz_sdk/_models/_generated/components/vad.py index 83d98c6..f3ad361 100644 --- a/src/jambonz_sdk/_models/_generated/components/vad.py +++ b/src/jambonz_sdk/_models/_generated/components/vad.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: components/vad -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/jambonz_app.py b/src/jambonz_sdk/_models/_generated/jambonz_app.py index 72df1d2..4e5b4da 100644 --- a/src/jambonz_sdk/_models/_generated/jambonz_app.py +++ b/src/jambonz_sdk/_models/_generated/jambonz_app.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: jambonz-app -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations @@ -22,6 +22,7 @@ enqueue, gather, google_s2s, + gptlive_s2s, hangup, leave, listen, @@ -30,6 +31,7 @@ openai_s2s, pause, play, + qwen_s2s, redirect, rest_dial, s2s, @@ -40,7 +42,9 @@ stream, tag, transcribe, + transfer, ultravox_s2s, + voicelive_s2s, ) @@ -62,8 +66,12 @@ class Verb( | elevenlabs_s2s.ElevenlabsS2S | deepgram_s2s.DeepgramS2S | ultravox_s2s.UltravoxS2S + | qwen_s2s.QwenS2S + | gptlive_s2s.GptLiveS2S + | voicelive_s2s.AzureVoiceLiveS2S | dialogflow.Dialogflow | agent.Agent + | transfer.Transfer | conference.Conference | transcribe.Transcribe | enqueue.Enqueue @@ -99,8 +107,12 @@ class Verb( | elevenlabs_s2s.ElevenlabsS2S | deepgram_s2s.DeepgramS2S | ultravox_s2s.UltravoxS2S + | qwen_s2s.QwenS2S + | gptlive_s2s.GptLiveS2S + | voicelive_s2s.AzureVoiceLiveS2S | dialogflow.Dialogflow | agent.Agent + | transfer.Transfer | conference.Conference | transcribe.Transcribe | enqueue.Enqueue diff --git a/src/jambonz_sdk/_models/_generated/verbs/__init__.py b/src/jambonz_sdk/_models/_generated/verbs/__init__.py index abf1e3b..97fa6b2 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/__init__.py +++ b/src/jambonz_sdk/_models/_generated/verbs/__init__.py @@ -1,3 +1,3 @@ # generated by datamodel-codegen: # filename: schema -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 diff --git a/src/jambonz_sdk/_models/_generated/verbs/agent.py b/src/jambonz_sdk/_models/_generated/verbs/agent.py index f708b82..6e85d95 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/agent.py +++ b/src/jambonz_sdk/_models/_generated/verbs/agent.py @@ -1,18 +1,27 @@ # generated by datamodel-codegen: # filename: verbs/agent -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations from enum import Enum from typing import Any, Literal -from pydantic import ConfigDict, Field, confloat +from pydantic import ConfigDict, Field, confloat, conint from jambonz_sdk._models.base import JambonzModel from ..components import actionHook, recognizer, synthesizer -from .llm import LLM +from ..components import handoff as handoff_1 +from ..components import hangup as hangup_1 + + +class AutoLockLanguage(Enum): + """ + When using Deepgram Flux Multilingual, automatically adjust STT language hints and switch TTS voice based on detected language. Values: false (disabled), true (lock on first utterance), 'always' (continuously adapt on every turn). Default: false. + """ + + always = "always" class TurnDetection(Enum): @@ -54,6 +63,15 @@ class TurnDetection1(JambonzModel): """ +class Strategy(Enum): + """ + How interruptions are detected. 'vad' (default): speech onset tentatively interrupts the assistant and is confirmed after minSpeechDuration of sustained speech, otherwise the assistant resumes. 'interruptPrediction': an ML model scores whether caller speech is a genuine interruption attempt vs backchannel (e.g. 'uh-huh'), so the assistant is never tentatively paused and backchannel does not cut it off; requires a provisioned vendor API key. + """ + + vad = "vad" + interrupt_prediction = "interruptPrediction" + + class BargeIn(JambonzModel): """ Controls whether and how the user can interrupt the assistant while it is speaking. @@ -66,9 +84,21 @@ class BargeIn(JambonzModel): """ Allow the user to interrupt the assistant while it is speaking. Default: true. """ + strategy: Strategy | None = "vad" + """ + How interruptions are detected. 'vad' (default): speech onset tentatively interrupts the assistant and is confirmed after minSpeechDuration of sustained speech, otherwise the assistant resumes. 'interruptPrediction': an ML model scores whether caller speech is a genuine interruption attempt vs backchannel (e.g. 'uh-huh'), so the assistant is never tentatively paused and backchannel does not cut it off; requires a provisioned vendor API key. + """ + vendor: str | None = None + """ + Detection vendor for strategy 'interruptPrediction'; defaults to 'krisp' (currently the only option). Not used with strategy 'vad'. + """ + threshold: confloat(ge=0.0, le=1.0) | None = None + """ + Probability threshold for interruptPrediction (0-1). Higher values require stronger evidence before interrupting the assistant. Default: 0.5. + """ min_speech_duration: confloat(ge=0.0) | None = Field(default=0.5, alias="minSpeechDuration") """ - Seconds of detected speech required before confirming an interruption. Prevents brief noises from cutting off the assistant. Default: 0.5 + Seconds of detected speech required before confirming an interruption. Prevents brief noises from cutting off the assistant. Applies to strategy 'vad' only. Default: 0.5 """ sticky: bool | None = False """ @@ -76,6 +106,42 @@ class BargeIn(JambonzModel): """ +class Auth(JambonzModel): + """ + Optional inline credentials. When omitted, feature-server looks up credentials by (vendor, label) from the database. + """ + + model_config = ConfigDict( + extra="allow", + ) + api_key: str | None = Field(default=None, alias="apiKey") + + +class ConnectOptions(JambonzModel): + """ + SDK-level client options. + """ + + model_config = ConfigDict( + extra="forbid", + ) + timeout: confloat(ge=0.0) | None = None + max_retries: conint(ge=0) | None = Field(default=None, alias="maxRetries") + endpoint: str | None = None + base_url: str | None = Field(default=None, alias="baseURL") + + +class ReasoningEffort(Enum): + """ + Vendor-neutral thinking/reasoning effort. Mapped per-vendor by the LLM adapter (Gemini thinkingLevel, OpenAI reasoning_effort, Anthropic extended thinking); ignored by vendors without a native equivalent. 'minimal' minimizes thinking for lowest TTFT on latency-sensitive voice turns. + """ + + minimal = "minimal" + low = "low" + medium = "medium" + high = "high" + + class NoiseIsolation(Enum): """ Shorthand — enable noise isolation with the specified vendor using default settings. @@ -135,6 +201,139 @@ class McpServer(JambonzModel): """ +class Role(Enum): + system = "system" + user = "user" + assistant = "assistant" + tool = "tool" + + +class LlmMessage(JambonzModel): + """ + A conversation-history message. The library normalizes content to a string; adapters may carry vendor-native shapes internally. + """ + + model_config = ConfigDict( + extra="allow", + ) + role: Role + content: Any + + +class Type(Enum): + """ + Filler mode. 'audio' plays a looping audio file (killed immediately when tool returns). 'backchannel' uses TTS to speak short phrases like 'one moment...' or 'checking that...' (allowed to complete before next action). + """ + + audio = "audio" + backchannel = "backchannel" + + +class ToolFillerConfig(JambonzModel): + type: Type + """ + Filler mode. 'audio' plays a looping audio file (killed immediately when tool returns). 'backchannel' uses TTS to speak short phrases like 'one moment...' or 'checking that...' (allowed to complete before next action). + """ + start_delay_secs: confloat(ge=0.0) | None = Field(default=2, alias="startDelaySecs") + """ + Seconds to wait after tool invocation before playing filler. Prevents filler on fast tools. Default: 2 + """ + url: str | None = None + """ + Audio file URL (for type='audio'). Should be loopable. Playback stops immediately when tool returns. + """ + style: str | None = None + """ + Free-form style hint for LLM-generated phrases (for type='backchannel'). Examples: 'casual', 'professional', 'friendly and patient'. The LLM interprets this when generating filler phrases at call start. + """ + escalation_secs: confloat(ge=0.0) | None = Field(default=10, alias="escalationSecs") + """ + Seconds from tool invocation before generating a longer explanatory response (for type='backchannel'). Useful for tools that may take 10+ seconds. Default: 10 + """ + + +class LanguageConfig(JambonzModel): + model_config = ConfigDict( + extra="forbid", + ) + tts: synthesizer.Synthesizer | None = None + """ + TTS config override for this language. Merged with default tts. + """ + + +class LlmOptions(JambonzModel): + """ + Per-call LLM configuration. + """ + + model_config = ConfigDict( + extra="forbid", + ) + system_prompt: str | None = Field(default=None, alias="systemPrompt") + """ + System prompt for the model. Placed vendor-appropriately (top-level for Anthropic/Bedrock, config.systemInstruction for Gemini, role:'system' for OpenAI-compatibles). + """ + messages: list[LlmMessage] | None = None + """ + Seed conversation history. A role:'system' entry is extracted into systemPrompt internally. + """ + initial_messages: list[LlmMessage] | None = Field(default=None, alias="initialMessages") + """ + Alias of 'messages' (historical). + """ + max_tokens: conint(ge=1) | None = Field(default=None, alias="maxTokens") + """ + Maximum tokens the model may generate per turn. + """ + temperature: confloat(ge=0.0) | None = None + """ + Sampling temperature. + """ + reasoning_effort: ReasoningEffort | None = Field(default=None, alias="reasoningEffort") + """ + Vendor-neutral thinking/reasoning effort. Mapped per-vendor by the LLM adapter (Gemini thinkingLevel, OpenAI reasoning_effort, Anthropic extended thinking); ignored by vendors without a native equivalent. 'minimal' minimizes thinking for lowest TTFT on latency-sensitive voice turns. + """ + tools: list[dict[str, Any]] | None = None + """ + Tool / function definitions available to the model. The MCP-flat shape `{name, description, parameters}` is canonical; the OpenAI-wrapped form `{type:'function', function:{...}}` is also accepted. + """ + + +class Llm(JambonzModel): + """ + LLM configuration for the agent. Optional: when omitted, or when `vendor`/`model` are omitted, they fall back to the application's configured default LLM (like `stt`/`tts`). + """ + + model_config = ConfigDict( + extra="forbid", + ) + vendor: str | None = None + """ + LLM vendor id. Must match a `@jambonz/llm` registered adapter. Optional: falls back to the application's default LLM vendor. + """ + model: str | None = None + """ + Vendor-specific model id (e.g. 'gpt-4o', 'claude-sonnet-4-5-20250929'). Optional: falls back to the application's default LLM model. + """ + label: str | None = None + """ + Optional label to disambiguate when the account has multiple credentials for the same vendor. + """ + auth: Auth | None = None + """ + Optional inline credentials. When omitted, feature-server looks up credentials by (vendor, label) from the database. + """ + connect_options: ConnectOptions | None = Field(default=None, alias="connectOptions") + """ + SDK-level client options. + """ + llm_options: LlmOptions | None = Field(default=None, alias="llmOptions") + """ + Per-call LLM configuration. + """ + + class Agent(JambonzModel): """ Configures a complete voice AI agent by wiring together STT → LLM → TTS with integrated turn detection. Provides a higher-level abstraction than manually orchestrating the individual components. Optimized for building voice AI agents with proper turn-taking behavior. @@ -153,6 +352,16 @@ class Agent(JambonzModel): """ Text-to-speech configuration for the agent. """ + auto_lock_language: bool | AutoLockLanguage | None = Field( + default=False, alias="autoLockLanguage" + ) + """ + When using Deepgram Flux Multilingual, automatically adjust STT language hints and switch TTS voice based on detected language. Values: false (disabled), true (lock on first utterance), 'always' (continuously adapt on every turn). Default: false. + """ + language_config: dict[str, LanguageConfig] | None = Field(default=None, alias="languageConfig") + """ + Per-language overrides for TTS. Keys are BCP-47 language codes. When autoLockLanguage detects a language switch, the agent uses the corresponding config. + """ turn_detection: TurnDetection | TurnDetection1 | None = Field( default="stt", alias="turnDetection", validate_default=True ) @@ -167,9 +376,9 @@ class Agent(JambonzModel): """ Seconds to wait after the assistant finishes speaking before prompting the user to respond. 0 disables. Default: 0. """ - llm: LLM + llm: Llm | None = None """ - LLM configuration for the agent. See the 'llm' verb schema for details. + LLM configuration for the agent. Optional: when omitted, or when `vendor`/`model` are omitted, they fall back to the application's configured default LLM (like `stt`/`tts`). """ action_hook: actionHook.ActionHook | None = Field(default=None, alias="actionHook") """ @@ -201,3 +410,15 @@ class Agent(JambonzModel): """ External MCP servers that provide tools to the LLM. The agent connects at startup via SSE, discovers available tools, and makes them callable by the LLM. """ + handoff: handoff_1.Handoff | None = None + """ + Declarative transfer-to-human config. When present, the runtime injects a 'transfer_to_human' tool into the agent's toolset and runs the packaged transfer choreography (blind/warm) when the LLM calls it. + """ + hangup: hangup_1.Hangup | None = None + """ + Enable the built-in hangup tool. When present, the runtime injects a 'hangup' tool into the agent's toolset; when the LLM calls it, the call is ended with a reason placed in an X-Reason SIP header (LLM-supplied reason wins, falling back to hangup.reason). + """ + tool_filler: Literal[False] | ToolFillerConfig | None = Field(default=None, alias="toolFiller") + """ + Configuration for playing filler audio while tool calls are in progress. Prevents silence during long-running tool executions. + """ diff --git a/src/jambonz_sdk/_models/_generated/verbs/alert.py b/src/jambonz_sdk/_models/_generated/verbs/alert.py index a675033..978f4c7 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/alert.py +++ b/src/jambonz_sdk/_models/_generated/verbs/alert.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/alert -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/answer.py b/src/jambonz_sdk/_models/_generated/verbs/answer.py index fa26290..6a53584 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/answer.py +++ b/src/jambonz_sdk/_models/_generated/verbs/answer.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/answer -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/conference.py b/src/jambonz_sdk/_models/_generated/verbs/conference.py index 0cead5a..8d39ca3 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/conference.py +++ b/src/jambonz_sdk/_models/_generated/verbs/conference.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/conference -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations @@ -18,7 +18,7 @@ class Conference(JambonzModel): Places the caller into a multi-party conference room. Multiple callers in the same named conference can speak to each other. Supports features like muting, recording, waiting rooms, and participant limits. """ - verb: Literal["conference"] = "conference" + verb: Literal["conference", "room"] = "conference" id: str | None = None """ An optional unique identifier for this verb instance. @@ -85,7 +85,11 @@ class Conference(JambonzModel): """ listen: dict[str, Any] | None = None """ - Audio streaming configuration for the conference. + Audio streaming configuration for the conference. `stream` is the preferred synonym. + """ + stream: dict[str, Any] | None = None + """ + Fork the room's mixed audio to a WebSocket (preferred synonym for `listen`). """ distribute_dtmf: bool | None = Field(default=None, alias="distributeDtmf") """ diff --git a/src/jambonz_sdk/_models/_generated/verbs/config.py b/src/jambonz_sdk/_models/_generated/verbs/config.py index 74d391c..ceeb1d2 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/config.py +++ b/src/jambonz_sdk/_models/_generated/verbs/config.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/config -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/deepgram_s2s.py b/src/jambonz_sdk/_models/_generated/verbs/deepgram_s2s.py index 9401feb..8bd8e08 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/deepgram_s2s.py +++ b/src/jambonz_sdk/_models/_generated/verbs/deepgram_s2s.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/deepgram_s2s -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/dequeue.py b/src/jambonz_sdk/_models/_generated/verbs/dequeue.py index 9b6f485..770fafd 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/dequeue.py +++ b/src/jambonz_sdk/_models/_generated/verbs/dequeue.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/dequeue -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/dial.py b/src/jambonz_sdk/_models/_generated/verbs/dial.py index ac175aa..49d5e34 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/dial.py +++ b/src/jambonz_sdk/_models/_generated/verbs/dial.py @@ -1,9 +1,10 @@ # generated by datamodel-codegen: # filename: verbs/dial -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations +from enum import Enum from typing import Any, Literal from pydantic import Field @@ -16,6 +17,15 @@ from . import dub as dub_1 +class SrtpEncryption(Enum): + """ + Request SRTP (encrypted media) on the outbound call. 'sdes' negotiates keys via SDES crypto attributes in the SDP (used with sips:/TLS SIP targets such as LiveKit); 'dtls' negotiates keys via DTLS-SRTP. Applies to SIP URI targets. + """ + + sdes = "sdes" + dtls = "dtls" + + class Dial(JambonzModel): """ Initiates an outbound call to one or more targets and bridges the caller to the first target that answers. Targets can be phone numbers (PSTN), SIP endpoints, registered users, or Microsoft Teams users. Supports simultaneous ringing, call screening, recording, and DTMF capture during the bridged call. @@ -81,6 +91,10 @@ class Dial(JambonzModel): """ If true, keep media anchored through the jambonz media server even if a direct media path is possible. Required for features like recording, listen, and DTMF capture during bridged calls. """ + codecs: list[str] | None = Field(default=None, min_length=1) + """ + Codec names, in preference order, to offer on the outbound INVITE (e.g. ["G722", "PCMU"]). Names are case-insensitive. When omitted, the media server offers its defaults with the inbound leg's codec first so the bridged legs match. If any name is not supported by the media server the whole list is ignored, an error is logged and an alert is written, and default negotiation is used. + """ exit_media_path: bool | None = Field(default=None, alias="exitMediaPath") """ If true, remove jambonz from the media path after the call is bridged. Reduces latency but disables mid-call features like recording and DTMF capture. @@ -131,3 +145,7 @@ class Dial(JambonzModel): """ If true, forward the P-Asserted-Identity header from the inbound call to the outbound call. """ + srtp_encryption: SrtpEncryption | None = Field(default=None, alias="srtpEncryption") + """ + Request SRTP (encrypted media) on the outbound call. 'sdes' negotiates keys via SDES crypto attributes in the SDP (used with sips:/TLS SIP targets such as LiveKit); 'dtls' negotiates keys via DTLS-SRTP. Applies to SIP URI targets. + """ diff --git a/src/jambonz_sdk/_models/_generated/verbs/dialogflow.py b/src/jambonz_sdk/_models/_generated/verbs/dialogflow.py index ec71cdd..5ff022b 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/dialogflow.py +++ b/src/jambonz_sdk/_models/_generated/verbs/dialogflow.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: verbs/dialogflow -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations from enum import Enum from typing import Any, Literal -from pydantic import ConfigDict, Field +from pydantic import ConfigDict, Field, constr from jambonz_sdk._models.base import JambonzModel @@ -73,11 +73,15 @@ class Dialogflow(JambonzModel): """ agent: str | None = None """ - The Dialogflow agent ID. Required for CX agents. + The Dialogflow agent ID for CX, or the app ID for CES. Required for both. """ environment: str | None = None """ - The Dialogflow environment to use. + The Dialogflow environment to use (CX). + """ + deployment: str | None = None + """ + The deployment to use (CES). Omit to use the app's default. """ region: str | None = None """ @@ -91,6 +95,12 @@ class Dialogflow(JambonzModel): """ The language code for the conversation (e.g. 'en-US'). """ + session_id: constr(pattern=r"^[A-Za-z0-9_-]{1,200}$") | None = Field( + default=None, alias="sessionId" + ) + """ + Names the conversation on the Dialogflow side. Reuse the same value on a later dialogflow verb in the same call to return the caller to the same conversation, with the history the agent had built up - for example after handing the caller to a dial leg and back. Defaults to the call sid. + """ action_hook: actionHook.ActionHook | None = Field(default=None, alias="actionHook") """ A webhook invoked when the Dialogflow session ends. @@ -99,6 +109,10 @@ class Dialogflow(JambonzModel): """ A webhook invoked for Dialogflow events during the conversation. """ + tool_hook: actionHook.ActionHook | None = Field(default=None, alias="toolHook") + """ + A webhook invoked when the agent requests a client-side tool call (CX and CES). Respond with {outputParameters} (or {error}) to resume the conversation. The posted tool_call is each model's native shape: CX sends {tool, action, input_parameters}; CES sends {id, display_name, tool, args}. CES echoes its per-call id automatically, so you need not return it. + """ events: list[str] | None = None """ List of event types to receive via the eventHook. diff --git a/src/jambonz_sdk/_models/_generated/verbs/dtmf.py b/src/jambonz_sdk/_models/_generated/verbs/dtmf.py index 8902658..374f741 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/dtmf.py +++ b/src/jambonz_sdk/_models/_generated/verbs/dtmf.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/dtmf -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/dub.py b/src/jambonz_sdk/_models/_generated/verbs/dub.py index 20a219f..1d05fc4 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/dub.py +++ b/src/jambonz_sdk/_models/_generated/verbs/dub.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/dub -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations @@ -27,6 +27,8 @@ class Action(Enum): class Dub(JambonzModel): """ Manages audio dubbing tracks on a call. Allows adding, removing, and controlling auxiliary audio tracks that are mixed into the call audio. Used for background music, coaching whispers, or injecting audio from external sources. + + **Track Routing:** Tracks are heard by the party on whose call leg they are created. A dub verb in the main verb stack (A-leg) creates tracks heard by the caller. A dub verb nested in the dial verb's `dub` array creates tracks heard by the callee. When using injectCommand to play/say on a track from a different call leg, pass the target call's `call_sid` as the third argument to `session.injectCommand()` to route the command to the correct leg. """ verb: Literal["dub"] = "dub" diff --git a/src/jambonz_sdk/_models/_generated/verbs/elevenlabs_s2s.py b/src/jambonz_sdk/_models/_generated/verbs/elevenlabs_s2s.py index 9986a11..1a54588 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/elevenlabs_s2s.py +++ b/src/jambonz_sdk/_models/_generated/verbs/elevenlabs_s2s.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/elevenlabs_s2s -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/enqueue.py b/src/jambonz_sdk/_models/_generated/verbs/enqueue.py index 9493695..ac2016e 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/enqueue.py +++ b/src/jambonz_sdk/_models/_generated/verbs/enqueue.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/enqueue -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/gather.py b/src/jambonz_sdk/_models/_generated/verbs/gather.py index 1bbd315..6844068 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/gather.py +++ b/src/jambonz_sdk/_models/_generated/verbs/gather.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/gather -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/google_s2s.py b/src/jambonz_sdk/_models/_generated/verbs/google_s2s.py index 7d41a60..5ae5126 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/google_s2s.py +++ b/src/jambonz_sdk/_models/_generated/verbs/google_s2s.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/google_s2s -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/gptlive_s2s.py b/src/jambonz_sdk/_models/_generated/verbs/gptlive_s2s.py new file mode 100644 index 0000000..598fb48 --- /dev/null +++ b/src/jambonz_sdk/_models/_generated/verbs/gptlive_s2s.py @@ -0,0 +1,126 @@ +# generated by datamodel-codegen: +# filename: verbs/gptlive_s2s +# timestamp: 2026-10-14T10:55:09+00:00 + +from __future__ import annotations + +from enum import Enum +from typing import Any, Literal + +from pydantic import ConfigDict, Field, constr + +from jambonz_sdk._models.base import JambonzModel + +from ..components.llm_base import LlmBaseProperties + + +class Auth(JambonzModel): + """ + OpenAI credentials. + """ + + model_config = ConfigDict( + extra="allow", + ) + api_key: constr(min_length=1) = Field(..., alias="apiKey") + """ + The OpenAI API key. Required — the session cannot be authenticated without it. + """ + + +class Type(Enum): + """ + 'client' asks the application for free-form text context; 'responses' runs a Responses API turn that can make function calls. Tools — mcpServers, handoff, hangup — require 'responses'. + """ + + client = "client" + responses = "responses" + + +class Responses(JambonzModel): + """ + Configuration for a Responses-targeted delegation. Required when type is 'responses'. + """ + + model_config = ConfigDict( + extra="allow", + ) + model: str = Field(..., examples=["gpt-5.5"]) + """ + The Responses-side model the delegated turns run on (e.g. gpt-5.5). Required by the server, and distinct from the GPT Live voice model in the connection URL. + """ + tools: list[dict[str, Any]] | None = None + """ + Function tools in the flat Responses shape: {type:'function', name, description, parameters}. jambonz prepends the injected handoff/hangup/MCP tools here. + """ + instructions: str | None = None + + +class Delegation(JambonzModel): + """ + How the model delegates work it cannot do itself. Replacing this mid-call requires the complete object. + """ + + model_config = ConfigDict( + extra="allow", + ) + type: Type | None = None + """ + 'client' asks the application for free-form text context; 'responses' runs a Responses API turn that can make function calls. Tools — mcpServers, handoff, hangup — require 'responses'. + """ + responses: Responses | None = None + """ + Configuration for a Responses-targeted delegation. Required when type is 'responses'. + """ + tools: Any | None = None + """ + Not allowed here: a Responses delegation's tools belong at delegation.responses.tools. Tools placed at this level are rejected by the server at session start. + """ + + +class SessionUpdate(JambonzModel): + """ + Startup configuration, sent as the first session.update client event. Required: the server withholds session.started — and therefore caller audio — until it arrives. + """ + + model_config = ConfigDict( + extra="allow", + ) + model: Any | None = None + """ + Not allowed: the model travels in the connection URL. Set the verb's top-level 'model' property instead. + """ + delegation: Delegation | None = None + """ + How the model delegates work it cannot do itself. Replacing this mid-call requires the complete object. + """ + + +class LlmOptions(JambonzModel): + model_config = ConfigDict( + extra="allow", + ) + session_update: SessionUpdate + """ + Startup configuration, sent as the first session.update client event. Required: the server withholds session.started — and therefore caller audio — until it arrives. + """ + + +class GptLiveS2S(LlmBaseProperties): + """ + Shortcut for 'llm' with vendor automatically set to 'gptlive'. Connects the caller to OpenAI's GPT Live API (limited-access alpha, e.g. model gpt-live-1-boulder-alpha) for real-time speech-to-speech voice conversation. This is a different wire protocol from the OpenAI Realtime API used by 'openai_s2s': llmOptions takes only a session_update (there is no response_create — the model starts and drives the conversation itself), and work the model wants done arrives as a delegation. Set session_update.delegation.type to 'client' to be asked for free-form text context (answer with a delegation.context.append via llm:update), or to 'responses' to run a Responses API turn that can make function calls (answer with delegation.function_call_output.create). A 'responses' delegation carries a required nested object, delegation.responses, whose 'model' is mandatory and whose 'tools' take the flat Responses function shape. Tool support — mcpServers, handoff and hangup — requires delegation.type 'responses' with delegation.responses.model set. auth takes the OpenAI apiKey; the model is carried in the connection URL, so do not set session_update.model. + """ + + verb: Literal["gptlive_s2s"] = "gptlive_s2s" + """ + The verb name. + """ + vendor: Literal["gptlive"] = "gptlive" + """ + The LLM vendor (always 'gptlive' for this shortcut). + """ + auth: Auth + """ + OpenAI credentials. + """ + llm_options: LlmOptions = Field(..., alias="llmOptions") diff --git a/src/jambonz_sdk/_models/_generated/verbs/hangup.py b/src/jambonz_sdk/_models/_generated/verbs/hangup.py index 914cdc6..875a6bb 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/hangup.py +++ b/src/jambonz_sdk/_models/_generated/verbs/hangup.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/hangup -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/leave.py b/src/jambonz_sdk/_models/_generated/verbs/leave.py index 911906a..893e7e6 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/leave.py +++ b/src/jambonz_sdk/_models/_generated/verbs/leave.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/leave -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/listen.py b/src/jambonz_sdk/_models/_generated/verbs/listen.py index 5505909..5bc302f 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/listen.py +++ b/src/jambonz_sdk/_models/_generated/verbs/listen.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/listen -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/llm.py b/src/jambonz_sdk/_models/_generated/verbs/llm.py index a618850..f6a0fee 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/llm.py +++ b/src/jambonz_sdk/_models/_generated/verbs/llm.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/llm -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations @@ -20,19 +20,7 @@ class LLM(LlmBaseProperties): """ The verb name. """ - vendor: str = Field( - ..., - examples=[ - "openai", - "anthropic", - "google", - "groq", - "deepseek", - "deepgram", - "ultravox", - "custom", - ], - ) + vendor: str """ The LLM vendor to use. """ diff --git a/src/jambonz_sdk/_models/_generated/verbs/message.py b/src/jambonz_sdk/_models/_generated/verbs/message.py index c67d0a1..f133ee1 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/message.py +++ b/src/jambonz_sdk/_models/_generated/verbs/message.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/message -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/openai_s2s.py b/src/jambonz_sdk/_models/_generated/verbs/openai_s2s.py index 6147628..6e1eeec 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/openai_s2s.py +++ b/src/jambonz_sdk/_models/_generated/verbs/openai_s2s.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/openai_s2s -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/pause.py b/src/jambonz_sdk/_models/_generated/verbs/pause.py index e50e558..583f7b7 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/pause.py +++ b/src/jambonz_sdk/_models/_generated/verbs/pause.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/pause -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/play.py b/src/jambonz_sdk/_models/_generated/verbs/play.py index cdb5908..9f3276d 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/play.py +++ b/src/jambonz_sdk/_models/_generated/verbs/play.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/play -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/qwen_s2s.py b/src/jambonz_sdk/_models/_generated/verbs/qwen_s2s.py new file mode 100644 index 0000000..9dedd20 --- /dev/null +++ b/src/jambonz_sdk/_models/_generated/verbs/qwen_s2s.py @@ -0,0 +1,44 @@ +# generated by datamodel-codegen: +# filename: verbs/qwen_s2s +# timestamp: 2026-10-14T10:55:09+00:00 + +from __future__ import annotations + +from typing import Any, Literal + +from pydantic import Field + +from ..components.llm_base import LlmBaseProperties + + +class QwenS2S(LlmBaseProperties): + """ + Shortcut for 'llm' with vendor automatically set to 'qwen'. Connects the caller to an Alibaba Qwen Omni-Realtime model (e.g. qwen3.5-omni-plus-realtime, qwen3.5-omni-flash-realtime) for real-time speech-to-speech voice conversation. auth takes the DashScope apiKey (required; keys are region-bound) and an optional host — default dashscope-intl.aliyuncs.com (international); set a workspace-scoped host (ws-..maas.aliyuncs.com) or dashscope.aliyuncs.com for the China (Beijing) region. + """ + + verb: Literal["qwen_s2s"] = "qwen_s2s" + """ + The verb name. + """ + vendor: Literal["qwen"] = "qwen" + """ + The LLM vendor (always 'qwen' for this shortcut). + """ + llm_options: dict[str, Any] = Field( + ..., + alias="llmOptions", + examples=[ + { + "messages": [ + { + "role": "system", + "content": "You are a helpful customer service agent for Acme Corp.", + } + ], + "temperature": 0.7, + } + ], + ) + """ + Configuration passed to the LLM including the system prompt, temperature, tools/functions, and other model parameters. The structure varies by vendor but typically includes 'messages' (conversation history), 'temperature', 'tools' (function definitions), and 'maxTokens'. + """ diff --git a/src/jambonz_sdk/_models/_generated/verbs/redirect.py b/src/jambonz_sdk/_models/_generated/verbs/redirect.py index a72ce85..d3c7c31 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/redirect.py +++ b/src/jambonz_sdk/_models/_generated/verbs/redirect.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/redirect -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/rest_dial.py b/src/jambonz_sdk/_models/_generated/verbs/rest_dial.py index f424889..ec391ea 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/rest_dial.py +++ b/src/jambonz_sdk/_models/_generated/verbs/rest_dial.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/rest:dial -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations @@ -86,3 +86,7 @@ class RestDial(JambonzModel): """ Maximum call duration in seconds. """ + codecs: list[str] | None = Field(default=None, min_length=1) + """ + Codec names, in preference order, to offer on the outbound INVITE (e.g. ["G722", "PCMU"]). Names are case-insensitive. When omitted the media server offers its default codecs. If any name is not supported by the media server the whole list is ignored, an error is logged and an alert is written, and default negotiation is used. + """ diff --git a/src/jambonz_sdk/_models/_generated/verbs/room.py b/src/jambonz_sdk/_models/_generated/verbs/room.py new file mode 100644 index 0000000..457e36e --- /dev/null +++ b/src/jambonz_sdk/_models/_generated/verbs/room.py @@ -0,0 +1,16 @@ +# generated by datamodel-codegen: +# filename: verbs/room +# timestamp: 2026-10-14T10:55:09+00:00 + +from __future__ import annotations + +from pydantic import Field, RootModel + +from . import conference + + +class Room(RootModel[conference.Conference]): + root: conference.Conference = Field(..., title="Room") + """ + Places the caller into a multi-party room. `room` is a synonym for `conference` with identical shape and behavior, using the Voice-AI-friendly name; prefer `room` in new applications. The shape is defined entirely by the conference verb (referenced here) so the two never drift. + """ diff --git a/src/jambonz_sdk/_models/_generated/verbs/s2s.py b/src/jambonz_sdk/_models/_generated/verbs/s2s.py index 8c9462b..71ba4a2 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/s2s.py +++ b/src/jambonz_sdk/_models/_generated/verbs/s2s.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/s2s -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations @@ -20,19 +20,7 @@ class S2S(LlmBaseProperties): """ The verb name. """ - vendor: str = Field( - ..., - examples=[ - "openai", - "anthropic", - "google", - "groq", - "deepseek", - "deepgram", - "ultravox", - "custom", - ], - ) + vendor: str """ The LLM vendor to use. """ diff --git a/src/jambonz_sdk/_models/_generated/verbs/say.py b/src/jambonz_sdk/_models/_generated/verbs/say.py index 936a206..929cae7 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/say.py +++ b/src/jambonz_sdk/_models/_generated/verbs/say.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/say -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/sip_decline.py b/src/jambonz_sdk/_models/_generated/verbs/sip_decline.py index 26d3fcf..f9c971a 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/sip_decline.py +++ b/src/jambonz_sdk/_models/_generated/verbs/sip_decline.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/sip-decline -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/sip_refer.py b/src/jambonz_sdk/_models/_generated/verbs/sip_refer.py index fb26476..81fe81f 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/sip_refer.py +++ b/src/jambonz_sdk/_models/_generated/verbs/sip_refer.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/sip-refer -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/sip_request.py b/src/jambonz_sdk/_models/_generated/verbs/sip_request.py index 7f348c6..ff430ad 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/sip_request.py +++ b/src/jambonz_sdk/_models/_generated/verbs/sip_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/sip-request -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/stream.py b/src/jambonz_sdk/_models/_generated/verbs/stream.py index e35c429..9d70b1b 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/stream.py +++ b/src/jambonz_sdk/_models/_generated/verbs/stream.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/stream -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/tag.py b/src/jambonz_sdk/_models/_generated/verbs/tag.py index 1864815..1bc7918 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/tag.py +++ b/src/jambonz_sdk/_models/_generated/verbs/tag.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/tag -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/transcribe.py b/src/jambonz_sdk/_models/_generated/verbs/transcribe.py index 3e784b3..87fcd0a 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/transcribe.py +++ b/src/jambonz_sdk/_models/_generated/verbs/transcribe.py @@ -1,9 +1,10 @@ # generated by datamodel-codegen: # filename: verbs/transcribe -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations +from enum import Enum from typing import Literal from pydantic import Field @@ -13,6 +14,15 @@ from ..components import recognizer as recognizer_1 +class Channel(Enum): + """ + Specific audio channel to transcribe. Channel 1 = near-end (local party's audio, i.e. caller on A-leg or callee on B-leg). Channel 2 = far-end (remote party's audio). When transcribe is nested in the dial verb, omitting channel captures both legs mixed; specifying channel: 2 isolates the B-leg's inbound audio. + """ + + number_1 = 1 + number_2 = 2 + + class Transcribe(JambonzModel): """ Enables real-time transcription of the call audio. Transcription results are sent to the transcriptionHook as they are produced. Runs as a background process — subsequent verbs execute immediately while transcription continues. @@ -43,7 +53,7 @@ class Transcribe(JambonzModel): """ If true, begin transcribing before the call is answered. """ - channel: float | None = None + channel: Channel | None = None """ - Specific audio channel to transcribe. + Specific audio channel to transcribe. Channel 1 = near-end (local party's audio, i.e. caller on A-leg or callee on B-leg). Channel 2 = far-end (remote party's audio). When transcribe is nested in the dial verb, omitting channel captures both legs mixed; specifying channel: 2 isolates the B-leg's inbound audio. """ diff --git a/src/jambonz_sdk/_models/_generated/verbs/transfer.py b/src/jambonz_sdk/_models/_generated/verbs/transfer.py new file mode 100644 index 0000000..480e3f0 --- /dev/null +++ b/src/jambonz_sdk/_models/_generated/verbs/transfer.py @@ -0,0 +1,51 @@ +# generated by datamodel-codegen: +# filename: verbs/transfer +# timestamp: 2026-10-14T10:55:09+00:00 + +from __future__ import annotations + +from typing import Literal + +from pydantic import ConfigDict + +from jambonz_sdk._models.base import JambonzModel + +from ..components import synthesizer as synthesizer_1 +from ..components.transferOptions import TransferOptions + + +class Brief(JambonzModel): + """ + warm only. The spoken summary delivered to the destination (human). + """ + + model_config = ConfigDict( + extra="forbid", + ) + text: str + """ + The spoken briefing text. + """ + synthesizer: synthesizer_1.Synthesizer | None = None + """ + Optional voice/vendor for the brief. Defaults to the session synthesizer. + """ + + +class Transfer(TransferOptions): + """ + Packages blind and warm parked/three-way transfer with built-in failure handling. Composes transferOptions for the shared transfer choreography and adds a verb identifier, an optional instance id, and a brief object for warm transfers where the app author supplies the literal spoken summary delivered to the destination. + """ + + verb: Literal["transfer"] = "transfer" + """ + The verb name. + """ + id: str | None = None + """ + An optional unique identifier for this verb instance. + """ + brief: Brief | None = None + """ + warm only. The spoken summary delivered to the destination (human). + """ diff --git a/src/jambonz_sdk/_models/_generated/verbs/ultravox_s2s.py b/src/jambonz_sdk/_models/_generated/verbs/ultravox_s2s.py index 67040ce..bab63c6 100644 --- a/src/jambonz_sdk/_models/_generated/verbs/ultravox_s2s.py +++ b/src/jambonz_sdk/_models/_generated/verbs/ultravox_s2s.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: verbs/ultravox_s2s -# timestamp: 2026-05-19T13:39:55+00:00 +# timestamp: 2026-10-14T10:55:09+00:00 from __future__ import annotations diff --git a/src/jambonz_sdk/_models/_generated/verbs/voicelive_s2s.py b/src/jambonz_sdk/_models/_generated/verbs/voicelive_s2s.py new file mode 100644 index 0000000..0e12f66 --- /dev/null +++ b/src/jambonz_sdk/_models/_generated/verbs/voicelive_s2s.py @@ -0,0 +1,126 @@ +# generated by datamodel-codegen: +# filename: verbs/voicelive_s2s +# timestamp: 2026-10-14T10:55:09+00:00 + +from __future__ import annotations + +from typing import Any, Literal + +from pydantic import ConfigDict, Field, constr + +from jambonz_sdk._models.base import JambonzModel + +from ..components.llm_base import LlmBaseProperties + + +class ConnectOptions(JambonzModel): + """ + Connection options for the Voice Live endpoint. + """ + + model_config = ConfigDict( + extra="allow", + ) + host: str + """ + Required. The Foundry/Speech resource hostname, e.g. my-resource.services.ai.azure.com. + """ + api_version: str | None = Field( + default=None, alias="apiVersion", examples=["2026-04-10", "2026-07-15"] + ) + """ + The Voice Live api-version. Defaults to 2026-04-10. + """ + agent_id: str | None = Field(default=None, alias="agentId") + """ + Foundry Agent Service agent id. When set, the session is addressed by agent instead of model. + """ + project_id: str | None = Field(default=None, alias="projectId") + """ + Foundry project id, used together with agentId. + """ + path: str | None = None + """ + Overrides the entire websocket path, including the query string. Rarely needed. + """ + + +class Auth(JambonzModel): + """ + Either apiKey (sent as the api-key query parameter) or accessToken (a Microsoft Entra ID token sent as a Bearer header). + """ + + model_config = ConfigDict( + extra="allow", + ) + api_key: constr(min_length=1) = Field(..., alias="apiKey") + """ + Azure Voice Live API key. + """ + access_token: constr(min_length=1) | None = Field(default=None, alias="accessToken") + """ + Microsoft Entra ID token for the https://ai.azure.com/.default scope. + """ + + +class Auth6(JambonzModel): + """ + Either apiKey (sent as the api-key query parameter) or accessToken (a Microsoft Entra ID token sent as a Bearer header). + """ + + model_config = ConfigDict( + extra="allow", + ) + api_key: constr(min_length=1) | None = Field(default=None, alias="apiKey") + """ + Azure Voice Live API key. + """ + access_token: constr(min_length=1) = Field(..., alias="accessToken") + """ + Microsoft Entra ID token for the https://ai.azure.com/.default scope. + """ + + +class LlmOptions(JambonzModel): + """ + session_update is sent verbatim in Voice Live's flat session shape; response_create triggers the first response. + """ + + model_config = ConfigDict( + extra="allow", + ) + session_update: dict[str, Any] + """ + The flat Voice Live session body. + """ + response_create: dict[str, Any] + """ + The initial response.create body. + """ + + +class AzureVoiceLiveS2S(LlmBaseProperties): + """ + Shortcut for 'llm' with vendor automatically set to 'voicelive'. Connects the caller to the Azure Voice Live API for real-time speech-to-speech conversation. connectOptions.host is required and names your Microsoft Foundry or Azure Speech resource (e.g. my-resource.services.ai.azure.com). auth takes either apiKey (sent as the api-key query parameter) or accessToken (a Microsoft Entra ID token, sent as a Bearer header). llmOptions.session_update is required and is sent verbatim in Voice Live's flat session shape, so Azure-only settings such as azure_semantic_vad, azure_deep_noise_suppression and Azure TTS voices are supported. + """ + + verb: Literal["voicelive_s2s"] = "voicelive_s2s" + """ + The verb name. + """ + vendor: Literal["voicelive"] = "voicelive" + """ + The LLM vendor (always 'voicelive' for this shortcut). + """ + connect_options: ConnectOptions = Field(..., alias="connectOptions") + """ + Connection options for the Voice Live endpoint. + """ + auth: Auth | Auth6 + """ + Either apiKey (sent as the api-key query parameter) or accessToken (a Microsoft Entra ID token sent as a Bearer header). + """ + llm_options: LlmOptions = Field(..., alias="llmOptions") + """ + session_update is sent verbatim in Voice Live's flat session shape; response_create triggers the first response. + """ diff --git a/src/jambonz_sdk/_models/_registry.py b/src/jambonz_sdk/_models/_registry.py index 2cd62aa..82f82a4 100644 --- a/src/jambonz_sdk/_models/_registry.py +++ b/src/jambonz_sdk/_models/_registry.py @@ -13,6 +13,7 @@ import importlib import logging import pkgutil +import typing from typing import Any from pydantic import BaseModel @@ -44,9 +45,19 @@ def _build_registry() -> dict[str, type[BaseModel]]: verb_field: Any = attr.model_fields.get("verb") if verb_field is None: continue + # A verb field may be a single-value ``Literal["say"]`` or a + # multi-value ``Literal["conference", "room"]`` (a verb with + # synonyms). Index the model under every literal value, plus + # its string default, so each JSON verb name resolves here. + keys: set[str] = set() + for arg in typing.get_args(verb_field.annotation): + if isinstance(arg, str): + keys.add(arg) default = verb_field.default if isinstance(default, str): - registry[default] = attr + keys.add(default) + for key in keys: + registry[key] = attr return registry diff --git a/src/jambonz_sdk/components/__init__.py b/src/jambonz_sdk/components/__init__.py index 3dbde06..923026b 100644 --- a/src/jambonz_sdk/components/__init__.py +++ b/src/jambonz_sdk/components/__init__.py @@ -15,6 +15,8 @@ from jambonz_sdk._models._generated.components.auth import Auth from jambonz_sdk._models._generated.components.bidirectionalAudio import BidirectionalAudio from jambonz_sdk._models._generated.components.fillerNoise import FillerNoise +from jambonz_sdk._models._generated.components.handoff import Handoff +from jambonz_sdk._models._generated.components.hangup import Hangup from jambonz_sdk._models._generated.components.llm_base import LlmBaseProperties from jambonz_sdk._models._generated.components.recognizer import Recognizer from jambonz_sdk._models._generated.components.recognizer_assemblyAiOptions import ( @@ -22,9 +24,6 @@ ) from jambonz_sdk._models._generated.components.recognizer_awsOptions import AwsRecognizerOptions from jambonz_sdk._models._generated.components.recognizer_azureOptions import AzureRecognizerOptions -from jambonz_sdk._models._generated.components.recognizer_cobaltOptions import ( - CobaltRecognizerOptions, -) from jambonz_sdk._models._generated.components.recognizer_customOptions import ( CustomRecognizerOptions, ) @@ -40,12 +39,15 @@ from jambonz_sdk._models._generated.components.recognizer_googleOptions import ( GoogleRecognizerOptions, ) +from jambonz_sdk._models._generated.components.recognizer_gradiumOptions import ( + GradiumRecognizerOptions, +) from jambonz_sdk._models._generated.components.recognizer_houndifyOptions import ( HoundifyRecognizerOptions, ) from jambonz_sdk._models._generated.components.recognizer_ibmOptions import IbmRecognizerOptions -from jambonz_sdk._models._generated.components.recognizer_nuanceOptions import ( - NuanceRecognizerOptions, +from jambonz_sdk._models._generated.components.recognizer_modulateOptions import ( + ModulateRecognizerOptions, ) from jambonz_sdk._models._generated.components.recognizer_nvidiaOptions import ( NvidiaRecognizerOptions, @@ -59,11 +61,11 @@ from jambonz_sdk._models._generated.components.recognizer_speechmaticsOptions import ( SpeechmaticsRecognizerOptions, ) -from jambonz_sdk._models._generated.components.recognizer_verbioOptions import ( - VerbioRecognizerOptions, -) +from jambonz_sdk._models._generated.components.recognizer_xaiOptions import XaiRecognizerOptions +from jambonz_sdk._models._generated.components.recognizer_zoomOptions import ZoomRecognizerOptions from jambonz_sdk._models._generated.components.synthesizer import Synthesizer from jambonz_sdk._models._generated.components.target import Target +from jambonz_sdk._models._generated.components.transferOptions import TransferOptions from jambonz_sdk._models._generated.components.vad import VAD from jambonz_sdk._models._generated.verbs.agent import BargeIn, McpServer @@ -77,18 +79,20 @@ "AzureRecognizerOptions", "BargeIn", "BidirectionalAudio", - "CobaltRecognizerOptions", "CustomRecognizerOptions", "DeepgramRecognizerOptions", "ElevenlabsRecognizerOptions", "FillerNoise", "GladiaRecognizerOptions", "GoogleRecognizerOptions", + "GradiumRecognizerOptions", + "Handoff", + "Hangup", "HoundifyRecognizerOptions", "IbmRecognizerOptions", "LlmBaseProperties", "McpServer", - "NuanceRecognizerOptions", + "ModulateRecognizerOptions", "NvidiaRecognizerOptions", "OpenaiRecognizerOptions", "Recognizer", @@ -96,6 +100,8 @@ "SpeechmaticsRecognizerOptions", "Synthesizer", "Target", + "TransferOptions", "VAD", - "VerbioRecognizerOptions", + "XaiRecognizerOptions", + "ZoomRecognizerOptions", ] diff --git a/src/jambonz_sdk/schema/callbacks/base.schema.json b/src/jambonz_sdk/schema/callbacks/base.schema.json index 6457daa..dd62425 100644 --- a/src/jambonz_sdk/schema/callbacks/base.schema.json +++ b/src/jambonz_sdk/schema/callbacks/base.schema.json @@ -19,7 +19,8 @@ "description": "Current call state." }, "sip_status": { "type": "integer", "description": "SIP response code (e.g. 200, 486)." }, - "sip_reason": { "type": "string", "description": "SIP reason phrase (e.g. 'OK', 'Busy Here')." }, + "sip_reason": { "type": "string", "description": "SIP reason phrase from the SIP status line (e.g. 'OK', 'Busy Here'). Not the RFC 3326 Reason header - see sip_reason_header." }, + "sip_reason_header": { "type": "string", "description": "RFC 3326 Reason header of the SIP message that caused this status change (e.g. 'Q.850;cause=18'), if it carried one; absent otherwise. Carriers fronting ISDN/E1 PRI trunks put the authoritative disconnect cause here rather than in the status line, and the same SIP status can carry different causes - a 503 may be cause=38 (network out of order) or cause=41 (temporary failure) - so the status code alone cannot classify the outcome. Present on whichever event observed it: a final failure response, a BYE, or a CANCEL. Two caveats: the header is re-serialized in transit, which normalizes the optional whitespace RFC 3326 allows around ';' (a carrier's 'Q.850 ;cause=18' arrives as 'Q.850;cause=18'), so parse tolerantly rather than string-matching; and a message may carry more than one Reason header, in which case they arrive comma-separated - split on commas outside quotes, since a text=\"...\" value may contain one." }, "trace_id": { "type": "string", "description": "Distributed tracing identifier for correlating logs across jambonz components." }, "originating_sip_ip": { "type": "string", "description": "IP address of the originating SIP trunk." }, "originating_sip_trunk_name": { "type": "string", "description": "Name of the originating SIP trunk as configured in jambonz." }, diff --git a/src/jambonz_sdk/schema/callbacks/transfer.schema.json b/src/jambonz_sdk/schema/callbacks/transfer.schema.json new file mode 100644 index 0000000..7cfa3ae --- /dev/null +++ b/src/jambonz_sdk/schema/callbacks/transfer.schema.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://jambonz.org/schema/callbacks/transfer", + "title": "Transfer ActionHook Payload", + "description": "Payload sent to the actionHook when a transfer verb resolves. Extends the base callback payload with transfer-specific outcome fields.", + "allOf": [ + { "$ref": "base" } + ], + "type": "object", + "properties": { + "transfer_result": { + "type": "string", + "enum": ["bridged", "returned", "voicemail", "failed"], + "description": "Outcome of the transfer: 'bridged' means the destination answered and the call was connected; 'returned' means the call was handed back to the caller flow; 'voicemail' means the caller was routed to voicemail; 'failed' means a protocol-level error occurred." + }, + "transfer_reason": { + "type": "string", + "enum": ["completed", "no-answer", "busy", "declined", "caller-abandoned", "error"], + "description": "Why the transfer ended with its result: 'completed' means it ran to completion; 'no-answer' means the destination did not answer in time; 'busy' means the destination returned a busy response; 'declined' means the confirm gate or AMD rejected the call; 'caller-abandoned' means the caller hung up before bridging; 'error' means a protocol-level failure occurred." + }, + "dial_call_sid": { + "type": "string", + "description": "Call SID of the destination (human) leg, when one was created during the transfer attempt." + }, + "sip_status": { + "type": "integer", + "description": "SIP status code from the destination leg, when applicable (e.g. 200, 486, 487)." + } + }, + "additionalProperties": true +} diff --git a/src/jambonz_sdk/schema/components/handoff.schema.json b/src/jambonz_sdk/schema/components/handoff.schema.json new file mode 100644 index 0000000..8cbc200 --- /dev/null +++ b/src/jambonz_sdk/schema/components/handoff.schema.json @@ -0,0 +1,45 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://jambonz.org/schema/components/handoff", + "title": "Handoff", + "description": "Declarative transfer-to-human config for conversational verbs (agent and s2s); runtime injects a transfer-to-human tool and runs the packaged choreography when the model calls it.", + "type": "object", + "allOf": [ + { "$ref": "transferOptions" } + ], + "properties": { + "brief": { + "oneOf": [ + { + "type": "string", + "enum": ["auto", "none"] + }, + { + "type": "object", + "properties": { + "template": { + "type": "string" + } + }, + "required": ["template"], + "additionalProperties": false + } + ], + "default": "auto", + "description": "'auto' = LLM writes the summary; 'none' = no spoken brief; {template} = guidance for the LLM's summary." + }, + "briefSynthesizer": { + "$ref": "synthesizer", + "description": "Optional voice/vendor for the spoken brief. Defaults to the session synthesizer." + }, + "toolName": { + "type": "string", + "default": "transfer_to_human", + "description": "Override the injected tool name." + }, + "toolDescription": { + "type": "string", + "description": "Override the injected tool description shown to the LLM." + } + } +} diff --git a/src/jambonz_sdk/schema/components/hangup.schema.json b/src/jambonz_sdk/schema/components/hangup.schema.json new file mode 100644 index 0000000..1185e2f --- /dev/null +++ b/src/jambonz_sdk/schema/components/hangup.schema.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://jambonz.org/schema/components/hangup", + "title": "Hangup", + "description": "Enable the built-in hangup tool for conversational verbs (agent and s2s). When present, the runtime injects a 'hangup' tool; when the model calls it, the call is ended with a reason placed in an X-Reason SIP header. The LLM-supplied reason wins, falling back to the app-supplied default below.", + "type": "object", + "properties": { + "reason": { + "type": "string", + "description": "Default reason placed in the X-Reason SIP header when the call is hung up. Used as a fallback when the LLM does not supply its own reason." + } + }, + "additionalProperties": false +} diff --git a/src/jambonz_sdk/schema/components/llm-base.schema.json b/src/jambonz_sdk/schema/components/llm-base.schema.json index 63dda3a..7a89489 100644 --- a/src/jambonz_sdk/schema/components/llm-base.schema.json +++ b/src/jambonz_sdk/schema/components/llm-base.schema.json @@ -11,8 +11,7 @@ }, "vendor": { "type": "string", - "description": "The LLM vendor to use.", - "examples": ["openai", "anthropic", "google", "groq", "deepseek", "baseten", "deepgram", "ultravox", "custom"] + "description": "The LLM vendor to use." }, "model": { "type": "string", @@ -85,10 +84,33 @@ "$ref": "actionHook", "description": "A webhook invoked when the LLM calls a tool/function. Receives the tool name and arguments, and should return the tool result." }, + "handoff": { + "$ref": "handoff", + "description": "Declarative transfer-to-human config for s2s verbs. When present, the runtime injects a 'transfer_to_human' tool and runs the packaged transfer choreography when the model calls it." + }, + "hangup": { + "$ref": "hangup", + "description": "Enable the built-in hangup tool for s2s verbs. When present, the runtime injects a 'hangup' tool; when the model calls it, the call is ended with a reason placed in an X-Reason SIP header (LLM-supplied reason wins, falling back to hangup.reason)." + }, "events": { "type": "array", "items": { "type": "string" }, "description": "List of event types to receive via the eventHook." + }, + "responseTimeoutMs": { + "type": "integer", + "minimum": 1, + "description": "Opt-in per-response stall watchdog (s2s vendors that support app-driven turn-taking, e.g. openai). Milliseconds to wait for a solicited response to be acknowledged before declaring a stall. When the timer expires a '{type:\"response.timeout\"}' event is sent to the eventHook. Omit or set to 0 to disable (the default)." + }, + "cancelOnResponseTimeout": { + "type": "boolean", + "default": false, + "description": "When a response stall is detected (see responseTimeoutMs), cancel the in-progress server-side response so the next turn does not collide with an active response. Has no effect unless responseTimeoutMs is set." + }, + "cancelOnBargeIn": { + "type": "boolean", + "default": false, + "description": "On caller barge-in, cancel the in-progress server-side response. Intended for app-driven turn-taking configurations where the vendor does not cancel the response itself (server-VAD configurations are unaffected)." } } } diff --git a/src/jambonz_sdk/schema/components/recognizer-assemblyAiOptions.schema.json b/src/jambonz_sdk/schema/components/recognizer-assemblyAiOptions.schema.json index 59f6b60..ad498f6 100644 --- a/src/jambonz_sdk/schema/components/recognizer-assemblyAiOptions.schema.json +++ b/src/jambonz_sdk/schema/components/recognizer-assemblyAiOptions.schema.json @@ -16,7 +16,7 @@ }, "speechModel": { "type": "string", - "description": "AssemblyAI speech model to use for recognition." + "description": "AssemblyAI speech model to use for recognition, e.g. 'universal-3-6-pro', 'universal-3-5-pro' or 'u3-rt-pro'." }, "formatTurns": { "type": "boolean", @@ -60,6 +60,18 @@ "inactivityTimeout": { "type": "number", "description": "Timeout (seconds) for inactivity before closing the stream." + }, + "languageCodes": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Languages to bias transcription toward (e.g. ['en', 'es']); code-switching among them is still allowed. Universal-3.5 Pro and Universal-3.6 Pro only. Default: all supported languages." + }, + "voiceFocus": { + "type": "string", + "enum": ["near-field", "far-field"], + "description": "Isolate the primary voice and suppress background noise before transcription. Use 'near-field' for phones and headsets, 'far-field' for distant microphones. Default: off." } }, "additionalProperties": false diff --git a/src/jambonz_sdk/schema/components/recognizer-cobaltOptions.schema.json b/src/jambonz_sdk/schema/components/recognizer-cobaltOptions.schema.json deleted file mode 100644 index 23a764d..0000000 --- a/src/jambonz_sdk/schema/components/recognizer-cobaltOptions.schema.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://jambonz.org/schema/components/recognizer-cobaltOptions", - "title": "Cobalt Recognizer Options", - "description": "Cobalt-specific STT options. Only applies when recognizer vendor is 'cobalt'.", - "type": "object", - "properties": { - "serverUri": { - "type": "string", - "description": "Cobalt server URI." - }, - "enableConfusionNetwork": { - "type": "boolean", - "description": "Enable confusion network output." - }, - "metadata": { - "type": "string", - "description": "Metadata string to pass to the server." - }, - "compiledContextData": { - "type": "string", - "description": "Compiled context data for biasing recognition." - }, - "wordTimeOffsets": { - "type": "boolean", - "description": "Include word-level timestamps." - }, - "contextToken": { - "type": "string", - "description": "Context token for server-side context." - } - }, - "additionalProperties": false -} diff --git a/src/jambonz_sdk/schema/components/recognizer-gladiaOptions.schema.json b/src/jambonz_sdk/schema/components/recognizer-gladiaOptions.schema.json index 06b5f2f..683f37e 100644 --- a/src/jambonz_sdk/schema/components/recognizer-gladiaOptions.schema.json +++ b/src/jambonz_sdk/schema/components/recognizer-gladiaOptions.schema.json @@ -2,7 +2,7 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://jambonz.org/schema/components/recognizer-gladiaOptions", "title": "Gladia Recognizer Options", - "description": "Gladia-specific STT options. Only applies when recognizer vendor is 'gladia'. See Gladia API documentation for available options.", + "description": "Gladia-specific STT options. Only applies when recognizer vendor is 'gladia'. Passed through into the Gladia live session request (e.g. language_config, pre_processing, realtime_processing); see the Gladia API documentation for available options.", "type": "object", - "additionalProperties": false + "additionalProperties": true } diff --git a/src/jambonz_sdk/schema/components/recognizer-googleOptions.schema.json b/src/jambonz_sdk/schema/components/recognizer-googleOptions.schema.json index 1cf6539..942ebdb 100644 --- a/src/jambonz_sdk/schema/components/recognizer-googleOptions.schema.json +++ b/src/jambonz_sdk/schema/components/recognizer-googleOptions.schema.json @@ -5,10 +5,21 @@ "description": "Google Speech-to-Text specific options. Only applies when recognizer vendor is 'google'.", "type": "object", "properties": { + "mode": { + "type": "string", + "enum": ["VERBATIM", "SMART"], + "description": "Gemini transcription style; applies only to the gemini models. VERBATIM transcribes as spoken; SMART cleans up disfluencies and applies formatting." + }, + "customVocabulary": { + "type": "array", + "items": {"type": "string"}, + "maxItems": 1000, + "description": "Up to 1000 terms, acronyms, brand names or proper nouns to bias recognition towards; applies only to the gemini models." + }, "serviceVersion": { "type": "string", "enum": ["v1", "v2"], - "description": "Google Speech-to-Text API version." + "description": "Cloud Speech-to-Text API version. Ignored by the gemini models, which are a different API." }, "recognizerId": { "type": "string", @@ -16,7 +27,7 @@ }, "parentPath": { "type": "string", - "description": "Parent resource path for the Google Speech recognizer (v2 only), e.g. 'projects/{project}/locations/{location}'." + "description": "Parent resource path for the Cloud Speech-to-Text v2 recognizer resource, e.g. 'projects/{project}/locations/{location}'. A location other than global also routes the request to that region's endpoint." }, "speechStartTimeoutMs": { "type": "number", diff --git a/src/jambonz_sdk/schema/components/recognizer-gradiumOptions.schema.json b/src/jambonz_sdk/schema/components/recognizer-gradiumOptions.schema.json new file mode 100644 index 0000000..e7e353a --- /dev/null +++ b/src/jambonz_sdk/schema/components/recognizer-gradiumOptions.schema.json @@ -0,0 +1,78 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://jambonz.org/schema/components/recognizer-gradiumOptions", + "title": "Gradium Recognizer Options", + "description": "Gradium streaming STT options. Only applies when recognizer vendor is 'gradium'. Gradium streams words as they are recognized (sent as interim results when interim is enabled); a final transcript is produced when Gradium's semantic VAD predicts the speaker has finished, and in the agent verb each final ends the user's turn. Transcripts are reported with confidence 1.0. Supported languages: en, fr, es, pt, de; any other language uses Gradium's automatic language detection.", + "type": "object", + "properties": { + "apiKey": { + "type": "string", + "description": "Gradium API key. Overrides the key configured in jambonz." + }, + "model": { + "type": "string", + "description": "STT model name. Defaults to 'default', Gradium's current production model.", + "examples": ["default"] + }, + "delayInFrames": { + "type": "integer", + "minimum": 7, + "maximum": 56, + "description": "How far the model looks ahead before emitting a word, in 80 ms frames. Lower is faster, higher is more accurate. Gradium's default is 10." + }, + "temp": { + "type": "number", + "minimum": 0, + "maximum": 1.5, + "description": "Sampling temperature for the transcript text. 0 is greedy (most deterministic)." + }, + "paddingBonus": { + "type": "number", + "minimum": -4, + "maximum": 4, + "description": "Biases the model toward emitting words sooner (negative) or later (positive). Default 0." + }, + "keywords": { + "type": "array", + "items": { "type": "string" }, + "maxItems": 500, + "description": "Words to boost (brand names, jargon). Each entry must be a single word; multi-word entries are split. When omitted, recognizer.hints are used." + }, + "keywordBoost": { + "type": "number", + "description": "How strongly to favor the keywords. Gradium recommends 3; 4 is the practical ceiling, negative values suppress words. When omitted with hints, recognizer.hintsBoost is used." + }, + "keywordBoostId": { + "type": "string", + "description": "Id of a keyword dictionary saved in Gradium Studio. Replaces keywords and hints. Gradium does not reject an unknown id: it is accepted silently and transcripts lose casing, punctuation and number formatting, so check the id." + }, + "turn_detection": { + "type": "object", + "description": "Tunes when a final transcript is cut. Gradium reports, every 80 ms, the probability that the speaker stays silent over several horizons; the turn ends when the chosen horizon's probability exceeds the threshold.", + "properties": { + "threshold": { + "type": "number", + "exclusiveMinimum": 0, + "exclusiveMaximum": 1, + "description": "Inactivity probability that ends a turn. Default 0.5; lower ends turns sooner." + }, + "horizon": { + "type": "number", + "exclusiveMinimum": 0, + "description": "Prediction horizon in seconds to read the probability from (Gradium reports 0.5, 1, 2 and 3). Defaults to the longest; a shorter horizon ends turns sooner.", + "examples": [0.5, 1, 2, 3] + } + }, + "additionalProperties": false + }, + "gradiumSttUri": { + "type": "string", + "description": "Custom host to connect to instead of api.gradium.ai, e.g. eu.api.gradium.ai or us.api.gradium.ai to pin data residency, or a proxy. Defaults to the host of the credential's API URL." + }, + "gradiumSttUseTls": { + "type": "boolean", + "description": "Use TLS when connecting to a custom host. Default true." + } + }, + "additionalProperties": false +} diff --git a/src/jambonz_sdk/schema/components/recognizer-modulateOptions.schema.json b/src/jambonz_sdk/schema/components/recognizer-modulateOptions.schema.json new file mode 100644 index 0000000..7243928 --- /dev/null +++ b/src/jambonz_sdk/schema/components/recognizer-modulateOptions.schema.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://jambonz.org/schema/components/recognizer-modulateOptions", + "title": "Modulate Recognizer Options", + "description": "Modulate-specific STT options. Only applies when recognizer vendor is 'modulate'. Modulate does not return confidence scores; transcripts are reported with confidence 1.0. The model is selected with the top-level recognizer 'model' property: 'velma-2-stt-streaming' (multilingual, the default) or 'velma-2-stt-streaming-english-v2' (low-latency English).", + "type": "object", + "properties": { + "apiKey": { + "type": "string", + "description": "Modulate API key. Overrides the key configured in jambonz." + }, + "endpointing": { + "type": "boolean", + "description": "Only applies to model 'velma-2-stt-streaming-english-v2': emit a final transcript for each pause-delimited speech segment. Defaults to true in jambonz; set false to receive a single final transcript when the stream ends." + }, + "emotionSignal": { + "type": "boolean", + "description": "Only applies to model 'velma-2-stt-streaming': detect the speaker's emotional tone for each utterance." + }, + "accentSignal": { + "type": "boolean", + "description": "Only applies to model 'velma-2-stt-streaming': identify the speaker's accent for each utterance." + }, + "deepfakeSignal": { + "type": "boolean", + "description": "Only applies to model 'velma-2-stt-streaming': score each utterance for synthetic (deepfake) voice likelihood." + }, + "piiPhiTagging": { + "type": "boolean", + "description": "Only applies to model 'velma-2-stt-streaming': wrap detected PII/PHI in tags within the transcript text." + }, + "modulateSttUri": { + "type": "string", + "description": "Custom host to connect to instead of platform.modulate.ai." + }, + "modulateSttUseTls": { + "type": "boolean", + "description": "Use TLS when connecting to a custom host. Default true." + } + }, + "additionalProperties": false +} diff --git a/src/jambonz_sdk/schema/components/recognizer-nuanceOptions.schema.json b/src/jambonz_sdk/schema/components/recognizer-nuanceOptions.schema.json deleted file mode 100644 index c484c26..0000000 --- a/src/jambonz_sdk/schema/components/recognizer-nuanceOptions.schema.json +++ /dev/null @@ -1,150 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://jambonz.org/schema/components/recognizer-nuanceOptions", - "title": "Nuance Recognizer Options", - "description": "Nuance Mix specific options. Only applies when recognizer vendor is 'nuance'.", - "type": "object", - "properties": { - "clientId": { - "type": "string", - "description": "Nuance Mix client ID." - }, - "secret": { - "type": "string", - "description": "Nuance Mix client secret." - }, - "kryptonEndpoint": { - "type": "string", - "description": "Custom Nuance Krypton endpoint URL." - }, - "topic": { - "type": "string", - "description": "Recognition topic (domain)." - }, - "utteranceDetectionMode": { - "type": "string", - "enum": ["single", "multiple", "disabled"], - "description": "How utterance boundaries are detected." - }, - "punctuation": { - "type": "boolean", - "description": "Enable automatic punctuation." - }, - "profanityFilter": { - "type": "boolean", - "description": "Filter profanity from results." - }, - "includeTokenization": { - "type": "boolean", - "description": "Include tokenization data in results." - }, - "discardSpeakerAdaptation": { - "type": "boolean", - "description": "Discard speaker adaptation data." - }, - "suppressCallRecording": { - "type": "boolean", - "description": "Suppress call recording on the Nuance side." - }, - "maskLoadFailures": { - "type": "boolean", - "description": "Mask resource load failures." - }, - "suppressInitialCapitalization": { - "type": "boolean", - "description": "Suppress initial capitalization of results." - }, - "allowZeroBaseLmWeight": { - "type": "boolean", - "description": "Allow zero base language model weight." - }, - "filterWakeupWord": { - "type": "boolean", - "description": "Filter wakeup words from results." - }, - "resultType": { - "type": "string", - "enum": ["final", "partial", "immutable_partial"], - "description": "Type of results to return." - }, - "noInputTimeoutMs": { - "type": "number", - "description": "Timeout in milliseconds before no-input event." - }, - "recognitionTimeoutMs": { - "type": "number", - "description": "Maximum recognition duration in milliseconds." - }, - "utteranceEndSilenceMs": { - "type": "number", - "description": "Silence duration in milliseconds to detect end of utterance." - }, - "maxHypotheses": { - "type": "number", - "description": "Maximum number of recognition hypotheses to return." - }, - "speechDomain": { - "type": "string", - "description": "Speech domain for optimized recognition." - }, - "formatting": { - "type": "object", - "description": "Formatting options for recognition results.", - "properties": { - "scheme": { "type": "string", "description": "Formatting scheme name." }, - "options": { "type": "object", "description": "Scheme-specific formatting options." } - }, - "required": ["scheme", "options"] - }, - "clientData": { - "type": "object", - "description": "Custom client data to pass to Nuance.", - "additionalProperties": true - }, - "userId": { - "type": "string", - "description": "User ID for speaker adaptation." - }, - "speechDetectionSensitivity": { - "type": "number", - "description": "Speech detection sensitivity (0-1)." - }, - "resources": { - "type": "array", - "description": "Array of Nuance recognition resources (grammars, wordsets, etc.).", - "items": { - "type": "object", - "properties": { - "externalReference": { - "type": "object", - "description": "External resource reference.", - "properties": { - "type": { - "type": "string", - "enum": ["undefined_resource_type", "wordset", "compiled_wordset", "domain_lm", "speaker_profile", "grammar", "settings"] - }, - "uri": { "type": "string" }, - "maxLoadFailures": { "type": "boolean" }, - "requestTimeoutMs": { "type": "number" }, - "headers": { "type": "object" } - } - }, - "inlineWordset": { "type": "string", "description": "Inline wordset JSON string." }, - "builtin": { "type": "string", "description": "Built-in grammar name." }, - "inlineGrammar": { "type": "string", "description": "Inline SRGS grammar." }, - "wakeupWord": { "type": "array", "items": { "type": "string" }, "description": "Wakeup words." }, - "weightName": { - "type": "string", - "enum": ["defaultWeight", "lowest", "low", "medium", "high", "highest"] - }, - "weightValue": { "type": "number" }, - "reuse": { - "type": "string", - "enum": ["undefined_reuse", "low_reuse", "high_reuse"] - } - } - } - } - }, - "additionalProperties": false -} diff --git a/src/jambonz_sdk/schema/components/recognizer-openaiOptions.schema.json b/src/jambonz_sdk/schema/components/recognizer-openaiOptions.schema.json index 7ae4a2d..9d701f5 100644 --- a/src/jambonz_sdk/schema/components/recognizer-openaiOptions.schema.json +++ b/src/jambonz_sdk/schema/components/recognizer-openaiOptions.schema.json @@ -29,6 +29,22 @@ "type": "string", "description": "Language code for recognition." }, + "languages": { + "type": "array", + "items": { "type": "string" }, + "minItems": 1, + "description": "Language hints for 'gpt-live-transcribe', which takes a list rather than a single code (e.g. ['en','fr']). Overrides the recognizer language; do not combine with 'language'." + }, + "keywords": { + "type": "array", + "items": { "type": "string" }, + "description": "Literal term hints (product names, acronyms, medications) for 'gpt-live-transcribe'. Hints, not required output. Defaults to the recognizer's 'hints' when unset." + }, + "delay": { + "type": "string", + "enum": ["minimal", "low", "medium", "high", "xhigh"], + "description": "Latency/accuracy trade-off for 'gpt-live-transcribe': lower emits partial text sooner, higher gives the model more audio context and can improve accuracy." + }, "input_audio_noise_reduction": { "type": "string", "enum": ["near_field", "far_field"], @@ -53,6 +69,22 @@ "silence_duration_ms": { "type": "number", "description": "Milliseconds of silence to detect end of speech." } }, "required": ["type"] + }, + "vadMode": { + "type": "integer", + "minimum": 0, + "maximum": 3, + "description": "Local VAD aggressiveness (0-3). Used only with the client-endpointed models ('gpt-live-transcribe', 'gpt-realtime-whisper'), which reject server turn detection and rely on jambonz to end each turn." + }, + "vadSilenceMs": { + "type": "integer", + "minimum": 1, + "description": "Milliseconds of silence to detect end-of-speech for local VAD. Used only with the client-endpointed models." + }, + "vadVoiceMs": { + "type": "integer", + "minimum": 1, + "description": "Milliseconds of voice to detect start-of-speech for local VAD. Used only with the client-endpointed models." } }, "additionalProperties": false diff --git a/src/jambonz_sdk/schema/components/recognizer-sonioxOptions.schema.json b/src/jambonz_sdk/schema/components/recognizer-sonioxOptions.schema.json index fe6dced..fba4d3a 100644 --- a/src/jambonz_sdk/schema/components/recognizer-sonioxOptions.schema.json +++ b/src/jambonz_sdk/schema/components/recognizer-sonioxOptions.schema.json @@ -11,19 +11,91 @@ }, "model": { "type": "string", - "description": "Soniox recognition model." + "description": "Soniox recognition model (e.g. 'stt-rt-v5')." + }, + "languageHints": { + "type": "array", + "items": { "type": "string" }, + "description": "Language hints for the v5 multilingual model, as ISO language codes (e.g. ['en','es']). Improves accuracy and speeds up language detection." + }, + "enableLanguageIdentification": { + "type": "boolean", + "description": "Enable language identification (v5); each recognized token is tagged with its detected language." + }, + "enableSpeakerDiarization": { + "type": "boolean", + "description": "Enable speaker diarization (v5); each recognized token is tagged with a speaker." }, "endpointDetection": { "type": "boolean", "description": "Enable endpoint detection." }, + "endpointSensitivity": { + "type": "number", + "minimum": -1, + "maximum": 1, + "description": "Endpoint detection sensitivity (v5), -1.0 to 1.0 (default 0.0). Higher values finalize endpoints faster; lower values are more conservative." + }, + "maxEndpointDelayMs": { + "type": "integer", + "minimum": 500, + "maximum": 3000, + "description": "Maximum delay in milliseconds before an endpoint is forced (v5), 500-3000 (default 2000)." + }, + "maxNonFinalTokensDurationMs": { + "type": "integer", + "minimum": 0, + "description": "Maximum duration in milliseconds that tokens may remain non-final before forced finalization (v5)." + }, "profanityFilter": { "type": "boolean", "description": "Filter profanity from results." }, + "context": { + "type": "object", + "additionalProperties": false, + "description": "Soniox v5 recognition context to improve accuracy (max ~8000 tokens). See https://soniox.com/docs/stt/concepts/context. When supplied this takes precedence outright over the 'speechContext' and 'hints' shortcuts.", + "properties": { + "general": { + "type": "array", + "description": "Structured key/value metadata describing the domain, topic, participants, setting, etc.", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { "type": "string" }, + "value": { "type": "string" } + }, + "required": ["key", "value"] + } + }, + "text": { + "type": "string", + "description": "Free-form background text (history of prior interactions, reference documents, meeting notes, summaries)." + }, + "terms": { + "type": "array", + "items": { "type": "string" }, + "description": "Domain-specific vocabulary to boost recognition." + }, + "translation_terms": { + "type": "array", + "description": "Source/target term mappings; only used when translation is enabled.", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "source": { "type": "string" }, + "target": { "type": "string" } + }, + "required": ["source", "target"] + } + } + } + }, "speechContext": { "type": "string", - "description": "Speech context for improved recognition." + "description": "Shortcut for context.text (free-text background). Ignored when the full 'context' object is supplied. The generic recognizer 'hints' are likewise sent as context.terms unless 'context' is supplied." }, "clientRequestReference": { "type": "string", diff --git a/src/jambonz_sdk/schema/components/recognizer-speechmaticsOptions.schema.json b/src/jambonz_sdk/schema/components/recognizer-speechmaticsOptions.schema.json index 9fb193f..beaa111 100644 --- a/src/jambonz_sdk/schema/components/recognizer-speechmaticsOptions.schema.json +++ b/src/jambonz_sdk/schema/components/recognizer-speechmaticsOptions.schema.json @@ -50,7 +50,11 @@ "sensitivity": { "type": "number" } } }, - "operating_point": { "type": "string", "description": "Operating point (standard or enhanced)." }, + "operating_point": { "type": "string", "description": "Deprecated by Speechmatics in favor of 'model'. Operating point (standard or enhanced)." }, + "model": { + "type": "string", + "description": "Model selection (replaces operating_point), e.g. standard, enhanced, or melia-1." + }, "enable_entities": { "type": "boolean", "description": "Enable entity detection." }, "audio_filtering_config": { "type": "object", @@ -62,9 +66,22 @@ "transcript_filtering_config": { "type": "object", "properties": { - "remove_disfluencies": { "type": "boolean" } + "remove_disfluencies": { "type": "boolean" }, + "replacements": { + "type": "array", + "description": "Word replacement rules applied to the transcript. 'from' accepts a plain word or a /regex/.", + "items": { + "type": "object", + "properties": { + "from": { "type": "string" }, + "to": { "type": "string" } + }, + "required": ["from", "to"], + "additionalProperties": false + } + } }, - "required": ["remove_disfluencies"] + "additionalProperties": false } } }, diff --git a/src/jambonz_sdk/schema/components/recognizer-verbioOptions.schema.json b/src/jambonz_sdk/schema/components/recognizer-verbioOptions.schema.json deleted file mode 100644 index 2b0c883..0000000 --- a/src/jambonz_sdk/schema/components/recognizer-verbioOptions.schema.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://jambonz.org/schema/components/recognizer-verbioOptions", - "title": "Verbio Recognizer Options", - "description": "Verbio-specific STT options. Only applies when recognizer vendor is 'verbio'.", - "type": "object", - "properties": { - "enable_formatting": { - "type": "boolean", - "description": "Enable text formatting of results." - }, - "enable_diarization": { - "type": "boolean", - "description": "Enable speaker diarization." - }, - "topic": { - "type": "number", - "description": "Topic ID for domain-specific recognition." - }, - "inline_grammar": { - "type": "string", - "description": "Inline SRGS grammar for constrained recognition." - }, - "grammar_uri": { - "type": "string", - "description": "URI of an external grammar resource." - }, - "label": { - "type": "string", - "description": "Label for the recognition session." - }, - "recognition_timeout": { - "type": "number", - "description": "Maximum recognition duration in seconds." - }, - "speech_complete_timeout": { - "type": "number", - "description": "Silence duration in seconds after complete speech." - }, - "speech_incomplete_timeout": { - "type": "number", - "description": "Silence duration in seconds after incomplete speech." - } - }, - "additionalProperties": false -} diff --git a/src/jambonz_sdk/schema/components/recognizer-xaiOptions.schema.json b/src/jambonz_sdk/schema/components/recognizer-xaiOptions.schema.json new file mode 100644 index 0000000..75265bc --- /dev/null +++ b/src/jambonz_sdk/schema/components/recognizer-xaiOptions.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://jambonz.org/schema/components/recognizer-xaiOptions", + "title": "xAI Recognizer Options", + "description": "xAI-specific STT options. Only applies when recognizer vendor is 'xai'.", + "type": "object", + "properties": { + "apiKey": { + "type": "string", + "description": "xAI API key. Overrides the key configured in jambonz." + }, + "model": { + "type": "string", + "description": "Transcription model to use, e.g. 'grok-voice-transcribe-2.0'. Defaults to whatever xAI currently defaults to.", + "examples": ["grok-voice-transcribe-2.0", "grok-voice-transcribe-1.0"] + }, + "endpointing": { + "type": "integer", + "description": "Milliseconds of silence before an utterance is considered final. Range 0-5000." + }, + "diarize": { + "type": "boolean", + "description": "Enable speaker diarization." + }, + "fillerWords": { + "type": "boolean", + "description": "Include filler words (um, uh) in transcript." + }, + "keyterms": { + "type": "array", + "items": { "type": "string" }, + "description": "Key terms to boost recognition for." + }, + "smartTurn": { + "type": "number", + "description": "End-of-turn confidence threshold, between 0.0 and 1.0." + }, + "smartTurnTimeout": { + "type": "integer", + "description": "Maximum milliseconds of silence to wait before forcing an end-of-turn final result. Range 1-5000." + } + }, + "additionalProperties": false +} diff --git a/src/jambonz_sdk/schema/components/recognizer-zoomOptions.schema.json b/src/jambonz_sdk/schema/components/recognizer-zoomOptions.schema.json new file mode 100644 index 0000000..ce56466 --- /dev/null +++ b/src/jambonz_sdk/schema/components/recognizer-zoomOptions.schema.json @@ -0,0 +1,45 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://jambonz.org/schema/components/recognizer-zoomOptions", + "title": "Zoom Recognizer Options", + "description": "Zoom Scribe (Live mode) STT options. Only applies when recognizer vendor is 'zoom'. Zoom segments turns server-side (silence-based) and returns one final transcript per speech turn; in the agent verb each one ends the user's turn: there are no interim results and no confidence scores (transcripts are reported with confidence 1.0). Supported languages: en-US, zh-CN, ja-JP, es-ES, it-IT, fr-FR, de-DE, ar-SA, ar-AE, pt-BR, pt-PT.", + "type": "object", + "properties": { + "apiKey": { + "type": "string", + "description": "Zoom Build platform API key (or JWT). Overrides the key configured in jambonz." + }, + "turn_detection": { + "type": "object", + "description": "Tunes Zoom's server-side turn detection, which is silence-based: a turn ends after silence_duration_ms of silence, whether or not the sentence is complete. Out-of-range values make Zoom end the session, so they are bounded here.", + "properties": { + "threshold": { + "type": "number", + "minimum": 0.2, + "maximum": 0.9, + "description": "Speech detection threshold. Higher values need louder or clearer speech to start a turn." + }, + "prefix_padding_ms": { + "type": "integer", + "minimum": 0, + "description": "Milliseconds of audio before detected speech to include in the turn." + }, + "silence_duration_ms": { + "type": "integer", + "exclusiveMinimum": 200, + "description": "Milliseconds of silence that end a turn. Zoom's default is about 350; raise it so a mid-sentence pause does not split the turn." + } + }, + "additionalProperties": false + }, + "zoomSttUri": { + "type": "string", + "description": "Custom host to connect to instead of api.zoom.us (e.g. a proxy)." + }, + "zoomSttUseTls": { + "type": "boolean", + "description": "Use TLS when connecting to a custom host. Default true." + } + }, + "additionalProperties": false +} diff --git a/src/jambonz_sdk/schema/components/recognizer.schema.json b/src/jambonz_sdk/schema/components/recognizer.schema.json index 702b41f..8c7e1b3 100644 --- a/src/jambonz_sdk/schema/components/recognizer.schema.json +++ b/src/jambonz_sdk/schema/components/recognizer.schema.json @@ -8,7 +8,7 @@ "vendor": { "type": "string", "description": "The STT vendor to use. Must match a vendor configured in the jambonz platform.", - "examples": ["google", "aws", "microsoft", "deepgram", "nuance", "ibm", "nvidia", "soniox", "cobalt", "assemblyai", "speechmatics", "openai", "houndify", "gladia", "elevenlabs", "verbio", "custom"] + "examples": ["google", "aws", "microsoft", "deepgram", "ibm", "nvidia", "soniox", "xai", "modulate", "zoom", "gradium", "assemblyai", "speechmatics", "openai", "houndify", "gladia", "elevenlabs", "custom"] }, "label": { "type": "string", @@ -195,18 +195,19 @@ "googleOptions": { "$ref": "recognizer-googleOptions" }, "awsOptions": { "$ref": "recognizer-awsOptions" }, "azureOptions": { "$ref": "recognizer-azureOptions" }, - "nuanceOptions": { "$ref": "recognizer-nuanceOptions" }, "ibmOptions": { "$ref": "recognizer-ibmOptions" }, "nvidiaOptions": { "$ref": "recognizer-nvidiaOptions" }, "sonioxOptions": { "$ref": "recognizer-sonioxOptions" }, - "cobaltOptions": { "$ref": "recognizer-cobaltOptions" }, + "xaiOptions": { "$ref": "recognizer-xaiOptions" }, + "modulateOptions": { "$ref": "recognizer-modulateOptions" }, + "zoomOptions": { "$ref": "recognizer-zoomOptions" }, + "gradiumOptions": { "$ref": "recognizer-gradiumOptions" }, "assemblyAiOptions": { "$ref": "recognizer-assemblyAiOptions" }, "speechmaticsOptions": { "$ref": "recognizer-speechmaticsOptions" }, "openaiOptions": { "$ref": "recognizer-openaiOptions" }, "houndifyOptions": { "$ref": "recognizer-houndifyOptions" }, "gladiaOptions": { "$ref": "recognizer-gladiaOptions" }, "elevenlabsOptions": { "$ref": "recognizer-elevenlabsOptions" }, - "verbioOptions": { "$ref": "recognizer-verbioOptions" }, "customOptions": { "$ref": "recognizer-customOptions" } }, "required": ["vendor"], diff --git a/src/jambonz_sdk/schema/components/synthesizer.schema.json b/src/jambonz_sdk/schema/components/synthesizer.schema.json index c938efc..c88ac54 100644 --- a/src/jambonz_sdk/schema/components/synthesizer.schema.json +++ b/src/jambonz_sdk/schema/components/synthesizer.schema.json @@ -8,7 +8,7 @@ "vendor": { "type": "string", "description": "The TTS vendor to use. Must match a vendor configured in the jambonz platform.", - "examples": ["google", "aws", "microsoft", "elevenlabs", "cartesia", "deepgram", "ibm", "nuance", "nvidia", "wellsaid", "whisper", "verbio", "custom"] + "examples": ["google", "aws", "microsoft", "elevenlabs", "cartesia", "fishaudio", "gradium", "kugelaudio", "nineninesix", "speechify", "deepgram", "deepgramflux", "ibm", "nvidia", "wellsaid", "whisper", "custom"] }, "label": { "type": "string", diff --git a/src/jambonz_sdk/schema/components/transferOptions.schema.json b/src/jambonz_sdk/schema/components/transferOptions.schema.json new file mode 100644 index 0000000..6476642 --- /dev/null +++ b/src/jambonz_sdk/schema/components/transferOptions.schema.json @@ -0,0 +1,177 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://jambonz.org/schema/components/transferOptions", + "title": "Transfer Options", + "description": "Shared configuration block for blind and warm transfer choreography, including destination targeting, caller handling, confirmation gates, and failure dispositions. Reused by the transfer verb and the handoff component.", + "type": "object", + "properties": { + "mode": { + "type": "string", + "enum": ["blind", "warm"], + "description": "Transfer mode: 'blind' hands off immediately without waiting for the destination to accept; 'warm' allows the agent to brief the destination before connecting the caller." + }, + "blindMethod": { + "type": "string", + "enum": ["refer", "dial"], + "default": "refer", + "description": "blind only: 'refer' sends a SIP REFER to the remote end; 'dial' places a bridged outbound call. Not auto-detected — must be set explicitly when the default 'refer' is not appropriate." + }, + "callerPresent": { + "type": "boolean", + "default": false, + "description": "warm only: when true the caller is added to a three-way conference and can hear the brief; when false the caller is parked (on hold) and does not hear the conversation between agent and destination." + }, + "target": { + "type": "array", + "items": { "$ref": "target" }, + "minItems": 1, + "description": "One or more call destinations. All targets are rung simultaneously; the first to answer wins." + }, + "callerId": { + "type": "string", + "description": "Caller ID presented to the transfer destination." + }, + "onHoldHook": { + "$ref": "actionHook", + "description": "warm/parked only: verbs to execute for the parked caller while the agent briefs the destination (e.g. play hold music). Only invoked when callerPresent is false." + }, + "timeout": { + "type": "number", + "default": 30, + "description": "Seconds to wait for the destination to answer before applying the onNoAnswer disposition." + }, + "confirm": { + "type": "object", + "description": "Optional human-side acceptance gate. The destination hears a prompt and must press the specified digit to accept. A wrong digit, timeout, or hangup is treated as a 'decline' disposition.", + "properties": { + "prompt": { "type": "string", "description": "Text or URL of the prompt played to the destination." }, + "digit": { "type": "string", "description": "Single DTMF digit the destination must press to accept the transfer." } + }, + "required": ["prompt", "digit"], + "additionalProperties": false + }, + "amd": { + "$ref": "amd", + "description": "warm only: optional answering-machine detection run on the destination leg. Off by default; a 'machine' result is treated as a 'decline' disposition." + }, + "disposition": { + "type": "object", + "description": "Per-outcome fallback actions when the transfer does not complete normally.", + "properties": { + "onNoAnswer": { + "type": "string", + "enum": ["return", "voicemail", "hangup"], + "default": "return", + "description": "Action when the destination does not answer within timeout." + }, + "onBusy": { + "type": "string", + "enum": ["return", "voicemail", "hangup"], + "default": "return", + "description": "Action when the destination returns a busy signal." + }, + "onDecline": { + "type": "string", + "enum": ["return", "voicemail", "hangup"], + "default": "return", + "description": "Action when the destination declines the transfer (confirm gate failed, AMD detected machine, or explicit rejection)." + }, + "onFailure": { + "type": "string", + "enum": ["return", "voicemail", "hangup"], + "default": "return", + "description": "Action when a protocol-level error occurs on the destination leg." + }, + "voicemailUrl": { + "type": "string", + "format": "uri", + "description": "SIP URI or HTTP URL to route the caller to voicemail. Required when any disposition value is 'voicemail'." + } + }, + "additionalProperties": false + }, + "actionHook": { + "$ref": "actionHook", + "description": "Fires when the transfer resolves — whether the call was bridged, returned to the caller, sent to voicemail, or failed." + }, + "eventHook": { + "$ref": "actionHook", + "description": "Optional hook for in-progress transfer events (e.g. destination ringing, brief started)." + } + }, + "required": ["mode", "target"], + "allOf": [ + { + "if": { + "required": ["disposition"], + "properties": { + "disposition": { + "required": ["onNoAnswer"], + "properties": { + "onNoAnswer": { "const": "voicemail" } + } + } + } + }, + "then": { + "properties": { + "disposition": { "required": ["voicemailUrl"] } + } + } + }, + { + "if": { + "required": ["disposition"], + "properties": { + "disposition": { + "required": ["onBusy"], + "properties": { + "onBusy": { "const": "voicemail" } + } + } + } + }, + "then": { + "properties": { + "disposition": { "required": ["voicemailUrl"] } + } + } + }, + { + "if": { + "required": ["disposition"], + "properties": { + "disposition": { + "required": ["onDecline"], + "properties": { + "onDecline": { "const": "voicemail" } + } + } + } + }, + "then": { + "properties": { + "disposition": { "required": ["voicemailUrl"] } + } + } + }, + { + "if": { + "required": ["disposition"], + "properties": { + "disposition": { + "required": ["onFailure"], + "properties": { + "onFailure": { "const": "voicemail" } + } + } + } + }, + "then": { + "properties": { + "disposition": { "required": ["voicemailUrl"] } + } + } + } + ] +} diff --git a/src/jambonz_sdk/schema/jambonz-app.schema.json b/src/jambonz_sdk/schema/jambonz-app.schema.json index 5182022..a8af4bf 100644 --- a/src/jambonz_sdk/schema/jambonz-app.schema.json +++ b/src/jambonz_sdk/schema/jambonz-app.schema.json @@ -27,8 +27,12 @@ { "$ref": "verbs/elevenlabs_s2s" }, { "$ref": "verbs/deepgram_s2s" }, { "$ref": "verbs/ultravox_s2s" }, + { "$ref": "verbs/qwen_s2s" }, + { "$ref": "verbs/gptlive_s2s" }, + { "$ref": "verbs/voicelive_s2s" }, { "$ref": "verbs/dialogflow" }, { "$ref": "verbs/agent" }, + { "$ref": "verbs/transfer" }, { "$ref": "verbs/conference" }, { "$ref": "verbs/transcribe" }, { "$ref": "verbs/enqueue" }, diff --git a/src/jambonz_sdk/schema/verbs/agent.schema.json b/src/jambonz_sdk/schema/verbs/agent.schema.json index 8f060b2..e38dcf4 100644 --- a/src/jambonz_sdk/schema/verbs/agent.schema.json +++ b/src/jambonz_sdk/schema/verbs/agent.schema.json @@ -86,10 +86,26 @@ "description": "Allow the user to interrupt the assistant while it is speaking. Default: true.", "default": true }, + "strategy": { + "type": "string", + "enum": ["vad", "interruptPrediction"], + "default": "vad", + "description": "How interruptions are detected. 'vad' (default): speech onset tentatively interrupts the assistant and is confirmed after minSpeechDuration of sustained speech, otherwise the assistant resumes. 'interruptPrediction': an ML model scores whether caller speech is a genuine interruption attempt vs backchannel (e.g. 'uh-huh'), so the assistant is never tentatively paused and backchannel does not cut it off; requires a provisioned vendor API key." + }, + "vendor": { + "type": "string", + "description": "Detection vendor for strategy 'interruptPrediction'; defaults to 'krisp' (currently the only option). Not used with strategy 'vad'." + }, + "threshold": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Probability threshold for interruptPrediction (0-1). Higher values require stronger evidence before interrupting the assistant. Default: 0.5." + }, "minSpeechDuration": { "type": "number", "minimum": 0, - "description": "Seconds of detected speech required before confirming an interruption. Prevents brief noises from cutting off the assistant. Default: 0.5", + "description": "Seconds of detected speech required before confirming an interruption. Prevents brief noises from cutting off the assistant. Applies to strategy 'vad' only. Default: 0.5", "default": 0.5 }, "sticky": { @@ -108,29 +124,15 @@ }, "llm": { "type": "object", - "description": "LLM configuration for the agent.", - "required": ["vendor", "model"], + "description": "LLM configuration for the agent. Optional: when omitted, or when `vendor`/`model` are omitted, they fall back to the application's configured default LLM (like `stt`/`tts`).", "properties": { "vendor": { "type": "string", - "enum": [ - "openai", - "anthropic", - "google", - "vertex-gemini", - "vertex-openai", - "bedrock", - "deepseek", - "baseten", - "azure-openai", - "groq", - "huggingface" - ], - "description": "LLM vendor id. Must match a `@jambonz/llm` registered adapter." + "description": "LLM vendor id. Must match a `@jambonz/llm` registered adapter. Optional: falls back to the application's default LLM vendor." }, "model": { "type": "string", - "description": "Vendor-specific model id (e.g. 'gpt-4o', 'claude-sonnet-4-5-20250929')." + "description": "Vendor-specific model id (e.g. 'gpt-4o', 'claude-sonnet-4-5-20250929'). Optional: falls back to the application's default LLM model." }, "label": { "type": "string", @@ -183,6 +185,11 @@ "minimum": 0, "description": "Sampling temperature." }, + "reasoningEffort": { + "type": "string", + "enum": ["minimal", "low", "medium", "high"], + "description": "Vendor-neutral thinking/reasoning effort. Mapped per-vendor by the LLM adapter (Gemini thinkingLevel, OpenAI reasoning_effort, Anthropic extended thinking); ignored by vendors without a native equivalent. 'minimal' minimizes thinking for lowest TTFT on latency-sensitive voice turns." + }, "tools": { "type": "array", "description": "Tool / function definitions available to the model. The MCP-flat shape `{name, description, parameters}` is canonical; the OpenAI-wrapped form `{type:'function', function:{...}}` is also accepted.", @@ -280,6 +287,14 @@ }, "description": "External MCP servers that provide tools to the LLM. The agent connects at startup via SSE, discovers available tools, and makes them callable by the LLM." }, + "handoff": { + "$ref": "../components/handoff", + "description": "Declarative transfer-to-human config. When present, the runtime injects a 'transfer_to_human' tool into the agent's toolset and runs the packaged transfer choreography (blind/warm) when the LLM calls it." + }, + "hangup": { + "$ref": "../components/hangup", + "description": "Enable the built-in hangup tool. When present, the runtime injects a 'hangup' tool into the agent's toolset; when the LLM calls it, the call is ended with a reason placed in an X-Reason SIP header (LLM-supplied reason wins, falling back to hangup.reason)." + }, "toolFiller": { "oneOf": [ { "type": "boolean", "const": false }, @@ -288,9 +303,6 @@ "description": "Configuration for playing filler audio while tool calls are in progress. Prevents silence during long-running tool executions." } }, - "required": [ - "llm" - ], "$defs": { "llmMessage": { "type": "object", diff --git a/src/jambonz_sdk/schema/verbs/conference.schema.json b/src/jambonz_sdk/schema/verbs/conference.schema.json index 55654d3..d49b9c3 100644 --- a/src/jambonz_sdk/schema/verbs/conference.schema.json +++ b/src/jambonz_sdk/schema/verbs/conference.schema.json @@ -7,7 +7,7 @@ "type": "object", "properties": { "verb": { - "const": "conference" + "enum": ["conference", "room"] }, "id": { "type": "string", @@ -83,7 +83,12 @@ }, "listen": { "type": "object", - "description": "Audio streaming configuration for the conference.", + "description": "Audio streaming configuration for the conference. `stream` is the preferred synonym.", + "additionalProperties": true + }, + "stream": { + "type": "object", + "description": "Fork the room's mixed audio to a WebSocket (preferred synonym for `listen`).", "additionalProperties": true }, "distributeDtmf": { diff --git a/src/jambonz_sdk/schema/verbs/dial.schema.json b/src/jambonz_sdk/schema/verbs/dial.schema.json index 8d03edc..e8c2c0e 100644 --- a/src/jambonz_sdk/schema/verbs/dial.schema.json +++ b/src/jambonz_sdk/schema/verbs/dial.schema.json @@ -88,6 +88,12 @@ "type": "boolean", "description": "If true, keep media anchored through the jambonz media server even if a direct media path is possible. Required for features like recording, listen, and DTMF capture during bridged calls." }, + "codecs": { + "type": "array", + "items": { "type": "string" }, + "minItems": 1, + "description": "Codec names, in preference order, to offer on the outbound INVITE (e.g. [\"G722\", \"PCMU\"]). Names are case-insensitive. When omitted, the media server offers its defaults with the inbound leg's codec first so the bridged legs match. If any name is not supported by the media server the whole list is ignored, an error is logged and an alert is written, and default negotiation is used." + }, "exitMediaPath": { "type": "boolean", "description": "If true, remove jambonz from the media path after the call is bridged. Reduces latency but disables mid-call features like recording and DTMF capture." @@ -163,6 +169,11 @@ "forwardPAI": { "type": "boolean", "description": "If true, forward the P-Asserted-Identity header from the inbound call to the outbound call." + }, + "srtpEncryption": { + "type": "string", + "enum": ["sdes", "dtls"], + "description": "Request SRTP (encrypted media) on the outbound call. 'sdes' negotiates keys via SDES crypto attributes in the SDP (used with sips:/TLS SIP targets such as LiveKit); 'dtls' negotiates keys via DTLS-SRTP. Applies to SIP URI targets." } }, "required": [ diff --git a/src/jambonz_sdk/schema/verbs/dialogflow.schema.json b/src/jambonz_sdk/schema/verbs/dialogflow.schema.json index a02b347..d73e8eb 100644 --- a/src/jambonz_sdk/schema/verbs/dialogflow.schema.json +++ b/src/jambonz_sdk/schema/verbs/dialogflow.schema.json @@ -32,11 +32,15 @@ }, "agent": { "type": "string", - "description": "The Dialogflow agent ID. Required for CX agents." + "description": "The Dialogflow agent ID for CX, or the app ID for CES. Required for both." }, "environment": { "type": "string", - "description": "The Dialogflow environment to use." + "description": "The Dialogflow environment to use (CX)." + }, + "deployment": { + "type": "string", + "description": "The deployment to use (CES). Omit to use the app's default." }, "region": { "type": "string", @@ -55,6 +59,11 @@ "type": "string", "description": "The language code for the conversation (e.g. 'en-US')." }, + "sessionId": { + "type": "string", + "pattern": "^[A-Za-z0-9_-]{1,200}$", + "description": "Names the conversation on the Dialogflow side. Reuse the same value on a later dialogflow verb in the same call to return the caller to the same conversation, with the history the agent had built up - for example after handing the caller to a dial leg and back. Defaults to the call sid." + }, "actionHook": { "$ref": "../components/actionHook", "description": "A webhook invoked when the Dialogflow session ends." @@ -63,6 +72,10 @@ "$ref": "../components/actionHook", "description": "A webhook invoked for Dialogflow events during the conversation." }, + "toolHook": { + "$ref": "../components/actionHook", + "description": "A webhook invoked when the agent requests a client-side tool call (CX and CES). Respond with {outputParameters} (or {error}) to resume the conversation. The posted tool_call is each model's native shape: CX sends {tool, action, input_parameters}; CES sends {id, display_name, tool, args}. CES echoes its per-call id automatically, so you need not return it." + }, "events": { "type": "array", "items": { diff --git a/src/jambonz_sdk/schema/verbs/gptlive_s2s.schema.json b/src/jambonz_sdk/schema/verbs/gptlive_s2s.schema.json new file mode 100644 index 0000000..9541b4a --- /dev/null +++ b/src/jambonz_sdk/schema/verbs/gptlive_s2s.schema.json @@ -0,0 +1,240 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://jambonz.org/schema/verbs/gptlive_s2s", + "minVersion": "11.0.4", + "title": "GPT Live S2S", + "description": "Shortcut for 'llm' with vendor automatically set to 'gptlive'. Connects the caller to OpenAI's GPT Live API (limited-access alpha, e.g. model gpt-live-1-boulder-alpha) for real-time speech-to-speech voice conversation. This is a different wire protocol from the OpenAI Realtime API used by 'openai_s2s': llmOptions takes only a session_update (there is no response_create \u2014 the model starts and drives the conversation itself), and work the model wants done arrives as a delegation. Set session_update.delegation.type to 'client' to be asked for free-form text context (answer with a delegation.context.append via llm:update), or to 'responses' to run a Responses API turn that can make function calls (answer with delegation.function_call_output.create). A 'responses' delegation carries a required nested object, delegation.responses, whose 'model' is mandatory and whose 'tools' take the flat Responses function shape. Tool support \u2014 mcpServers, handoff and hangup \u2014 requires delegation.type 'responses' with delegation.responses.model set. auth takes the OpenAI apiKey; the model is carried in the connection URL, so do not set session_update.model.", + "type": "object", + "allOf": [ + { + "$ref": "../components/llm-base" + }, + { + "$comment": "handoff, hangup and mcpServers need a function-calling channel, which only a Responses delegation provides. The feature-server throws when the verb executes otherwise, dropping the call \u2014 catch it at validation instead.", + "if": { + "anyOf": [ + { + "required": [ + "handoff" + ] + }, + { + "required": [ + "hangup" + ] + }, + { + "required": [ + "mcpServers" + ] + } + ] + }, + "then": { + "properties": { + "llmOptions": { + "properties": { + "session_update": { + "properties": { + "delegation": { + "properties": { + "type": { + "const": "responses" + } + }, + "required": [ + "type", + "responses" + ] + } + }, + "required": [ + "delegation" + ] + } + } + } + } + } + } + ], + "properties": { + "verb": { + "const": "gptlive_s2s", + "description": "The verb name." + }, + "vendor": { + "type": "string", + "const": "gptlive", + "description": "The LLM vendor (always 'gptlive' for this shortcut)." + }, + "auth": { + "type": "object", + "description": "OpenAI credentials.", + "properties": { + "apiKey": { + "type": "string", + "description": "The OpenAI API key. Required \u2014 the session cannot be authenticated without it.", + "minLength": 1 + } + }, + "required": [ + "apiKey" + ], + "additionalProperties": true + }, + "llmOptions": { + "type": "object", + "properties": { + "session_update": { + "type": "object", + "description": "Startup configuration, sent as the first session.update client event. Required: the server withholds session.started \u2014 and therefore caller audio \u2014 until it arrives.", + "properties": { + "model": { + "not": {}, + "description": "Not allowed: the model travels in the connection URL. Set the verb's top-level 'model' property instead." + }, + "delegation": { + "type": "object", + "description": "How the model delegates work it cannot do itself. Replacing this mid-call requires the complete object.", + "properties": { + "type": { + "type": "string", + "enum": [ + "client", + "responses" + ], + "description": "'client' asks the application for free-form text context; 'responses' runs a Responses API turn that can make function calls. Tools \u2014 mcpServers, handoff, hangup \u2014 require 'responses'." + }, + "responses": { + "type": "object", + "description": "Configuration for a Responses-targeted delegation. Required when type is 'responses'.", + "properties": { + "model": { + "type": "string", + "description": "The Responses-side model the delegated turns run on (e.g. gpt-5.5). Required by the server, and distinct from the GPT Live voice model in the connection URL.", + "examples": [ + "gpt-5.5" + ] + }, + "tools": { + "type": "array", + "items": { + "type": "object" + }, + "description": "Function tools in the flat Responses shape: {type:'function', name, description, parameters}. jambonz prepends the injected handoff/hangup/MCP tools here." + }, + "instructions": { + "type": "string" + } + }, + "required": [ + "model" + ], + "additionalProperties": true + }, + "tools": { + "not": {}, + "description": "Not allowed here: a Responses delegation's tools belong at delegation.responses.tools. Tools placed at this level are rejected by the server at session start." + } + }, + "additionalProperties": true, + "if": { + "properties": { + "type": { + "const": "responses" + } + }, + "required": [ + "type" + ] + }, + "then": { + "required": [ + "responses" + ] + } + } + }, + "additionalProperties": true + } + }, + "required": [ + "session_update" + ], + "additionalProperties": true + } + }, + "required": [ + "auth", + "llmOptions" + ], + "examples": [ + { + "verb": "gptlive_s2s", + "model": "gpt-live-1-boulder-alpha", + "auth": { + "apiKey": "sk-..." + }, + "llmOptions": { + "session_update": { + "instructions": "You are a helpful voice assistant. Be concise.", + "audio": { + "output": { + "voice": "marin" + } + }, + "delegation": { + "type": "client" + } + } + }, + "eventHook": "/s2s-event", + "actionHook": "/s2s-complete" + }, + { + "verb": "gptlive_s2s", + "model": "gpt-live-1-boulder-alpha", + "auth": { + "apiKey": "sk-..." + }, + "llmOptions": { + "session_update": { + "instructions": "You are a helpful voice assistant. Be concise.", + "audio": { + "output": { + "voice": "marin" + } + }, + "delegation": { + "type": "responses", + "responses": { + "model": "gpt-5.5", + "tools": [ + { + "type": "function", + "name": "get_weather", + "description": "Get the current weather for a city", + "parameters": { + "type": "object", + "properties": { + "city": { + "type": "string" + } + }, + "required": [ + "city" + ] + } + } + ] + } + } + } + }, + "toolHook": "/s2s-tool-call", + "eventHook": "/s2s-event", + "actionHook": "/s2s-complete" + } + ] +} diff --git a/src/jambonz_sdk/schema/verbs/qwen_s2s.schema.json b/src/jambonz_sdk/schema/verbs/qwen_s2s.schema.json new file mode 100644 index 0000000..8589b26 --- /dev/null +++ b/src/jambonz_sdk/schema/verbs/qwen_s2s.schema.json @@ -0,0 +1,45 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://jambonz.org/schema/verbs/qwen_s2s", + "minVersion": "0.4.0", + "title": "Qwen S2S", + "description": "Shortcut for 'llm' with vendor automatically set to 'qwen'. Connects the caller to an Alibaba Qwen Omni-Realtime model (e.g. qwen3.5-omni-plus-realtime, qwen3.5-omni-flash-realtime) for real-time speech-to-speech voice conversation. auth takes the DashScope apiKey (required; keys are region-bound) and an optional host — default dashscope-intl.aliyuncs.com (international); set a workspace-scoped host (ws-..maas.aliyuncs.com) or dashscope.aliyuncs.com for the China (Beijing) region.", + "type": "object", + "allOf": [ + { + "$ref": "../components/llm-base" + } + ], + "properties": { + "verb": { + "const": "qwen_s2s", + "description": "The verb name." + }, + "vendor": { + "type": "string", + "const": "qwen", + "description": "The LLM vendor (always 'qwen' for this shortcut)." + } + }, + "required": [ + "llmOptions" + ], + "examples": [ + { + "verb": "qwen_s2s", + "model": "qwen3.5-omni-flash-realtime", + "auth": { + "apiKey": "sk-...", + "host": "ws-abc123.ap-southeast-1.maas.aliyuncs.com" + }, + "llmOptions": { + "session_update": { + "voice": "Ethan", + "instructions": "You are a helpful voice assistant.", + "turn_detection": { "type": "semantic_vad" } + } + }, + "actionHook": "/s2s-complete" + } + ] +} diff --git a/src/jambonz_sdk/schema/verbs/rest_dial.schema.json b/src/jambonz_sdk/schema/verbs/rest_dial.schema.json index 8e31b59..0a83b3f 100644 --- a/src/jambonz_sdk/schema/verbs/rest_dial.schema.json +++ b/src/jambonz_sdk/schema/verbs/rest_dial.schema.json @@ -103,6 +103,12 @@ "timeLimit": { "type": "number", "description": "Maximum call duration in seconds." + }, + "codecs": { + "type": "array", + "items": { "type": "string" }, + "minItems": 1, + "description": "Codec names, in preference order, to offer on the outbound INVITE (e.g. [\"G722\", \"PCMU\"]). Names are case-insensitive. When omitted the media server offers its default codecs. If any name is not supported by the media server the whole list is ignored, an error is logged and an alert is written, and default negotiation is used." } }, "required": [ diff --git a/src/jambonz_sdk/schema/verbs/room.schema.json b/src/jambonz_sdk/schema/verbs/room.schema.json new file mode 100644 index 0000000..c7fa43c --- /dev/null +++ b/src/jambonz_sdk/schema/verbs/room.schema.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://jambonz.org/schema/verbs/room", + "minVersion": "0.9.6", + "title": "Room", + "description": "Places the caller into a multi-party room. `room` is a synonym for `conference` with identical shape and behavior, using the Voice-AI-friendly name; prefer `room` in new applications. The shape is defined entirely by the conference verb (referenced here) so the two never drift.", + "$ref": "conference" +} diff --git a/src/jambonz_sdk/schema/verbs/transfer.schema.json b/src/jambonz_sdk/schema/verbs/transfer.schema.json new file mode 100644 index 0000000..b0d9f49 --- /dev/null +++ b/src/jambonz_sdk/schema/verbs/transfer.schema.json @@ -0,0 +1,91 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://jambonz.org/schema/verbs/transfer", + "minVersion": "10.3.0", + "title": "Transfer", + "description": "Packages blind and warm parked/three-way transfer with built-in failure handling. Composes transferOptions for the shared transfer choreography and adds a verb identifier, an optional instance id, and a brief object for warm transfers where the app author supplies the literal spoken summary delivered to the destination.", + "type": "object", + "allOf": [ + { "$ref": "../components/transferOptions" } + ], + "properties": { + "verb": { + "const": "transfer", + "description": "The verb name." + }, + "id": { + "type": "string", + "description": "An optional unique identifier for this verb instance." + }, + "brief": { + "type": "object", + "description": "warm only. The spoken summary delivered to the destination (human).", + "properties": { + "text": { + "type": "string", + "description": "The spoken briefing text." + }, + "synthesizer": { + "$ref": "../components/synthesizer", + "description": "Optional voice/vendor for the brief. Defaults to the session synthesizer." + } + }, + "required": ["text"], + "additionalProperties": false + } + }, + "examples": [ + { + "verb": "transfer", + "mode": "blind", + "target": [ + { + "type": "phone", + "number": "+15085551212" + } + ] + }, + { + "verb": "transfer", + "mode": "warm", + "callerPresent": false, + "target": [ + { + "type": "phone", + "number": "+15085559876" + } + ], + "brief": { + "text": "Hello, I have a customer on the line who needs help with their account." + }, + "onHoldHook": "/hold-music", + "disposition": { + "onNoAnswer": "return", + "onBusy": "return" + } + }, + { + "verb": "transfer", + "mode": "warm", + "callerPresent": true, + "target": [ + { + "type": "phone", + "number": "+15085559876" + } + ], + "brief": { + "text": "Connecting you now — the customer is on the line and can hear us.", + "synthesizer": { + "vendor": "google", + "language": "en-US" + } + }, + "confirm": { + "prompt": "Press 1 to accept the transfer.", + "digit": "1" + }, + "actionHook": "/transfer-complete" + } + ] +} diff --git a/src/jambonz_sdk/schema/verbs/voicelive_s2s.schema.json b/src/jambonz_sdk/schema/verbs/voicelive_s2s.schema.json new file mode 100644 index 0000000..f9919c0 --- /dev/null +++ b/src/jambonz_sdk/schema/verbs/voicelive_s2s.schema.json @@ -0,0 +1,146 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://jambonz.org/schema/verbs/voicelive_s2s", + "minVersion": "11.1.0", + "title": "Azure Voice Live S2S", + "description": "Shortcut for 'llm' with vendor automatically set to 'voicelive'. Connects the caller to the Azure Voice Live API for real-time speech-to-speech conversation. connectOptions.host is required and names your Microsoft Foundry or Azure Speech resource (e.g. my-resource.services.ai.azure.com). auth takes either apiKey (sent as the api-key query parameter) or accessToken (a Microsoft Entra ID token, sent as a Bearer header). llmOptions.session_update is required and is sent verbatim in Voice Live's flat session shape, so Azure-only settings such as azure_semantic_vad, azure_deep_noise_suppression and Azure TTS voices are supported.", + "type": "object", + "allOf": [ + { + "$ref": "../components/llm-base" + } + ], + "properties": { + "verb": { + "const": "voicelive_s2s", + "description": "The verb name." + }, + "vendor": { + "type": "string", + "const": "voicelive", + "description": "The LLM vendor (always 'voicelive' for this shortcut)." + }, + "connectOptions": { + "type": "object", + "description": "Connection options for the Voice Live endpoint.", + "properties": { + "host": { + "type": "string", + "description": "Required. The Foundry/Speech resource hostname, e.g. my-resource.services.ai.azure.com." + }, + "apiVersion": { + "type": "string", + "description": "The Voice Live api-version. Defaults to 2026-04-10.", + "examples": [ + "2026-04-10", + "2026-07-15" + ] + }, + "agentId": { + "type": "string", + "description": "Foundry Agent Service agent id. When set, the session is addressed by agent instead of model." + }, + "projectId": { + "type": "string", + "description": "Foundry project id, used together with agentId." + }, + "path": { + "type": "string", + "description": "Overrides the entire websocket path, including the query string. Rarely needed." + } + }, + "required": [ + "host" + ], + "additionalProperties": true + }, + "auth": { + "type": "object", + "description": "Either apiKey (sent as the api-key query parameter) or accessToken (a Microsoft Entra ID token sent as a Bearer header).", + "properties": { + "apiKey": { + "type": "string", + "minLength": 1, + "description": "Azure Voice Live API key." + }, + "accessToken": { + "type": "string", + "minLength": 1, + "description": "Microsoft Entra ID token for the https://ai.azure.com/.default scope." + } + }, + "anyOf": [ + { + "required": [ + "apiKey" + ] + }, + { + "required": [ + "accessToken" + ] + } + ], + "additionalProperties": true + }, + "llmOptions": { + "type": "object", + "description": "session_update is sent verbatim in Voice Live's flat session shape; response_create triggers the first response.", + "properties": { + "session_update": { + "type": "object", + "description": "The flat Voice Live session body." + }, + "response_create": { + "type": "object", + "description": "The initial response.create body." + } + }, + "required": [ + "session_update", + "response_create" + ], + "additionalProperties": true + } + }, + "required": [ + "auth", + "connectOptions", + "llmOptions" + ], + "examples": [ + { + "verb": "voicelive_s2s", + "model": "gpt-realtime", + "auth": { + "apiKey": "..." + }, + "connectOptions": { + "host": "my-resource.services.ai.azure.com" + }, + "llmOptions": { + "session_update": { + "modalities": [ + "text", + "audio" + ], + "instructions": "You are a helpful voice assistant.", + "voice": { + "name": "en-US-Ava:DragonHDLatestNeural", + "type": "azure-standard" + }, + "turn_detection": { + "type": "azure_semantic_vad", + "silence_duration_ms": 500, + "remove_filler_words": true + }, + "input_audio_noise_reduction": { + "type": "azure_deep_noise_suppression" + } + }, + "response_create": {} + }, + "actionHook": "/s2s-complete" + } + ] +} diff --git a/src/jambonz_sdk/verb_builder.pyi b/src/jambonz_sdk/verb_builder.pyi index f0e01cb..d2cbd2e 100644 --- a/src/jambonz_sdk/verb_builder.pyi +++ b/src/jambonz_sdk/verb_builder.pyi @@ -214,6 +214,54 @@ class VerbBuilder: """ ... + def gptlive_s2s( + self, arg: Any = ..., /, + **kwargs: Any, + ) -> Self: + """Connect caller to GPT Live for real-time voice conversation. + + Required: llmOptions, vendor + + Args: + arg: a typed model instance or a dict payload (alternative to kwargs). + + Returns: + self for chaining. + """ + ... + + def qwen_s2s( + self, arg: Any = ..., /, + **kwargs: Any, + ) -> Self: + """Connect caller to Qwen for real-time voice conversation. + + Required: llmOptions, vendor + + Args: + arg: a typed model instance or a dict payload (alternative to kwargs). + + Returns: + self for chaining. + """ + ... + + def voicelive_s2s( + self, arg: Any = ..., /, + **kwargs: Any, + ) -> Self: + """Connect caller to Azure Voice Live for real-time voice conversation. + + Required: llmOptions, vendor + + Args: + arg: a typed model instance or a dict payload (alternative to kwargs). + + Returns: + self for chaining. + """ + ... + def s2s( self, arg: Any = ..., /, **kwargs: Any, @@ -253,11 +301,14 @@ class VerbBuilder: project: str = ..., agent: str = ..., environment: str = ..., + deployment: str = ..., region: str = ..., model: str = ..., lang: str = ..., + sessionId: str = ..., actionHook: Any = ..., eventHook: Any = ..., + toolHook: Any = ..., events: list[Any] = ..., welcomeEvent: str = ..., welcomeEventParams: dict[str, Any] = ..., @@ -281,11 +332,14 @@ class VerbBuilder: project: str (required) agent: str environment: str + deployment: str region: str model: str lang: str (required) + sessionId: str actionHook: Any eventHook: Any + toolHook: Any events: list[Any] welcomeEvent: str welcomeEventParams: dict[str, Any] @@ -307,6 +361,8 @@ class VerbBuilder: id: str = ..., stt: Any = ..., tts: Any = ..., + autoLockLanguage: Any = ..., + languageConfig: dict[str, Any] = ..., turnDetection: Any = ..., bargeIn: dict[str, Any] = ..., noResponseTimeout: int | float = ..., @@ -318,21 +374,24 @@ class VerbBuilder: earlyGeneration: bool = ..., noiseIsolation: Any = ..., mcpServers: list[Any] = ..., + handoff: Any = ..., + hangup: Any = ..., + toolFiller: Any = ..., **kwargs: Any, ) -> Self: """Integrated STT → LLM → TTS voice AI agent. - Required: llm - Args: arg: a typed model instance or a dict payload (alternative to kwargs). id: str stt: Any tts: Any + autoLockLanguage: Any + languageConfig: dict[str, Any] turnDetection: Any bargeIn: dict[str, Any] noResponseTimeout: int | float - llm: dict[str, Any] (required) + llm: dict[str, Any] actionHook: Any eventHook: Any toolHook: Any @@ -340,6 +399,9 @@ class VerbBuilder: earlyGeneration: bool noiseIsolation: Any mcpServers: list[Any] + handoff: Any + hangup: Any + toolFiller: Any Returns: self for chaining. @@ -490,6 +552,7 @@ class VerbBuilder: dtmfHook: Any = ..., headers: dict[str, Any] = ..., anchorMedia: bool = ..., + codecs: list[Any] = ..., exitMediaPath: bool = ..., boostAudioSignal: Any = ..., listen: dict[str, Any] = ..., @@ -502,6 +565,7 @@ class VerbBuilder: dub: list[Any] = ..., tag: dict[str, Any] = ..., forwardPAI: bool = ..., + srtpEncryption: str = ..., **kwargs: Any, ) -> Self: """Place outbound call and bridge to current caller. @@ -524,6 +588,7 @@ class VerbBuilder: dtmfHook: Any headers: dict[str, Any] anchorMedia: bool + codecs: list[Any] exitMediaPath: bool boostAudioSignal: Any listen: dict[str, Any] @@ -536,6 +601,7 @@ class VerbBuilder: dub: list[Any] tag: dict[str, Any] forwardPAI: bool + srtpEncryption: str Returns: self for chaining. @@ -561,6 +627,7 @@ class VerbBuilder: enterHook: Any = ..., record: dict[str, Any] = ..., listen: dict[str, Any] = ..., + stream: dict[str, Any] = ..., distributeDtmf: bool = ..., **kwargs: Any, ) -> Self: @@ -587,6 +654,7 @@ class VerbBuilder: enterHook: Any record: dict[str, Any] listen: dict[str, Any] + stream: dict[str, Any] distributeDtmf: bool Returns: @@ -594,6 +662,78 @@ class VerbBuilder: """ ... + def room( + self, arg: Any = ..., /, + id: str = ..., + name: str = ..., + beep: bool = ..., + memberTag: str = ..., + speakOnlyTo: str = ..., + startConferenceOnEnter: bool = ..., + endConferenceOnExit: bool = ..., + endConferenceDuration: int | float = ..., + maxParticipants: int | float = ..., + joinMuted: bool = ..., + actionHook: Any = ..., + waitHook: Any = ..., + statusEvents: list[Any] = ..., + statusHook: Any = ..., + enterHook: Any = ..., + record: dict[str, Any] = ..., + listen: dict[str, Any] = ..., + stream: dict[str, Any] = ..., + distributeDtmf: bool = ..., + **kwargs: Any, + ) -> Self: + """Place caller into a multi-party room (synonym for conference; prefer in new apps). + + Required: name + + Args: + arg: a typed model instance or a dict payload (alternative to kwargs). + id: str + name: str (required) + beep: bool + memberTag: str + speakOnlyTo: str + startConferenceOnEnter: bool + endConferenceOnExit: bool + endConferenceDuration: int | float + maxParticipants: int | float + joinMuted: bool + actionHook: Any + waitHook: Any + statusEvents: list[Any] + statusHook: Any + enterHook: Any + record: dict[str, Any] + listen: dict[str, Any] + stream: dict[str, Any] + distributeDtmf: bool + + Returns: + self for chaining. + """ + ... + + def transfer( + self, arg: Any = ..., /, + id: str = ..., + brief: dict[str, Any] = ..., + **kwargs: Any, + ) -> Self: + """Blind or warm transfer with built-in failure handling. + + Args: + arg: a typed model instance or a dict payload (alternative to kwargs). + id: str + brief: dict[str, Any] + + Returns: + self for chaining. + """ + ... + def enqueue( self, arg: Any = ..., /, id: str = ..., diff --git a/src/jambonz_sdk/verb_registry.py b/src/jambonz_sdk/verb_registry.py index f97f8f1..e480dda 100644 --- a/src/jambonz_sdk/verb_registry.py +++ b/src/jambonz_sdk/verb_registry.py @@ -64,6 +64,15 @@ def __post_init__(self) -> None: VerbDef("llm", "ultravox_s2s", json_verb="ultravox_s2s", doc="Connect caller to Ultravox for real-time voice conversation.", inject={"vendor": "ultravox"}), + VerbDef("llm", "gptlive_s2s", json_verb="gptlive_s2s", + doc="Connect caller to GPT Live for real-time voice conversation.", + inject={"vendor": "gptlive"}), + VerbDef("llm", "qwen_s2s", json_verb="qwen_s2s", + doc="Connect caller to Qwen for real-time voice conversation.", + inject={"vendor": "qwen"}), + VerbDef("llm", "voicelive_s2s", json_verb="voicelive_s2s", + doc="Connect caller to Azure Voice Live for real-time voice conversation.", + inject={"vendor": "voicelive"}), VerbDef("llm", "s2s", json_verb="s2s", doc="Generic S2S verb (use when vendor is determined at runtime)."), VerbDef("llm", "llm", doc="Legacy LLM verb (prefer s2s or vendor-specific shortcuts)."), @@ -79,6 +88,10 @@ def __post_init__(self) -> None: # Call Control VerbDef("dial", "dial", doc="Place outbound call and bridge to current caller."), VerbDef("conference", "conference", doc="Place caller into a multi-party conference room."), + VerbDef("conference", "room", json_verb="room", + doc="Place caller into a multi-party room (synonym for conference; prefer in new apps).", + inject={"verb": "room"}), + VerbDef("transfer", "transfer", doc="Blind or warm transfer with built-in failure handling."), VerbDef("enqueue", "enqueue", doc="Place caller into a named call queue."), VerbDef("dequeue", "dequeue", doc="Remove caller from a queue and bridge."), VerbDef("hangup", "hangup", doc="Terminate the call."), diff --git a/src/jambonz_sdk/verbs/__init__.py b/src/jambonz_sdk/verbs/__init__.py index 7b62622..bf159be 100644 --- a/src/jambonz_sdk/verbs/__init__.py +++ b/src/jambonz_sdk/verbs/__init__.py @@ -24,6 +24,7 @@ from jambonz_sdk._models._generated.verbs.enqueue import Enqueue from jambonz_sdk._models._generated.verbs.gather import Gather from jambonz_sdk._models._generated.verbs.google_s2s import GoogleS2S +from jambonz_sdk._models._generated.verbs.gptlive_s2s import GptLiveS2S from jambonz_sdk._models._generated.verbs.hangup import Hangup from jambonz_sdk._models._generated.verbs.leave import Leave from jambonz_sdk._models._generated.verbs.listen import Listen @@ -32,8 +33,10 @@ from jambonz_sdk._models._generated.verbs.openai_s2s import OpenaiS2S from jambonz_sdk._models._generated.verbs.pause import Pause from jambonz_sdk._models._generated.verbs.play import Play +from jambonz_sdk._models._generated.verbs.qwen_s2s import QwenS2S from jambonz_sdk._models._generated.verbs.redirect import Redirect from jambonz_sdk._models._generated.verbs.rest_dial import RestDial +from jambonz_sdk._models._generated.verbs.room import Room from jambonz_sdk._models._generated.verbs.s2s import S2S from jambonz_sdk._models._generated.verbs.say import Say from jambonz_sdk._models._generated.verbs.sip_decline import SipDecline @@ -42,12 +45,15 @@ from jambonz_sdk._models._generated.verbs.stream import Stream from jambonz_sdk._models._generated.verbs.tag import Tag from jambonz_sdk._models._generated.verbs.transcribe import Transcribe +from jambonz_sdk._models._generated.verbs.transfer import Transfer from jambonz_sdk._models._generated.verbs.ultravox_s2s import UltravoxS2S +from jambonz_sdk._models._generated.verbs.voicelive_s2s import AzureVoiceLiveS2S __all__ = [ "Agent", "Alert", "Answer", + "AzureVoiceLiveS2S", "Conference", "Config", "DTMF", @@ -60,6 +66,7 @@ "Enqueue", "Gather", "GoogleS2S", + "GptLiveS2S", "Hangup", "LLM", "Leave", @@ -68,8 +75,10 @@ "OpenaiS2S", "Pause", "Play", + "QwenS2S", "Redirect", "RestDial", + "Room", "S2S", "Say", "SipDecline", @@ -78,5 +87,6 @@ "Stream", "Tag", "Transcribe", + "Transfer", "UltravoxS2S", ] diff --git a/tests/unit/test_verb_builder.py b/tests/unit/test_verb_builder.py index d12ece5..e560da1 100644 --- a/tests/unit/test_verb_builder.py +++ b/tests/unit/test_verb_builder.py @@ -38,6 +38,13 @@ "deepgram_s2s": {"llmOptions": {}}, "elevenlabs_s2s": {"auth": {"agent_id": "agent-123"}}, "ultravox_s2s": {"llmOptions": {}}, + "gptlive_s2s": {"auth": {"apiKey": "x"}, "llmOptions": {"session_update": {}}}, + "qwen_s2s": {"llmOptions": {}}, + "voicelive_s2s": { + "auth": {"apiKey": "x"}, + "connectOptions": {"host": "example.azure.com"}, + "llmOptions": {"session_update": {}, "response_create": {}}, + }, "s2s": {"vendor": "openai", "llmOptions": {}}, "llm": {"vendor": "openai", "llmOptions": {}}, "dialogflow": {"project": "p", "lang": "en-US", "credentials": "{}"}, @@ -47,6 +54,8 @@ "transcribe": {}, "dial": {"target": [{"type": "phone", "number": "+15085551212"}]}, "conference": {"name": "room"}, + "room": {"name": "room"}, + "transfer": {"mode": "blind", "target": [{"type": "phone", "number": "+15085551212"}]}, "enqueue": {"name": "q"}, "dequeue": {"name": "q"}, "hangup": {},