jambonz-python-sdk 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. jambonz_python_sdk-0.2.0.dist-info/METADATA +179 -0
  2. jambonz_python_sdk-0.2.0.dist-info/RECORD +119 -0
  3. jambonz_python_sdk-0.2.0.dist-info/WHEEL +4 -0
  4. jambonz_sdk/__init__.py +52 -0
  5. jambonz_sdk/_signature.py +73 -0
  6. jambonz_sdk/client/__init__.py +15 -0
  7. jambonz_sdk/client/api.py +241 -0
  8. jambonz_sdk/schema/callbacks/amd.schema.json +50 -0
  9. jambonz_sdk/schema/callbacks/base.schema.json +29 -0
  10. jambonz_sdk/schema/callbacks/call-status.schema.json +22 -0
  11. jambonz_sdk/schema/callbacks/conference-status.schema.json +24 -0
  12. jambonz_sdk/schema/callbacks/conference-wait.schema.json +11 -0
  13. jambonz_sdk/schema/callbacks/conference.schema.json +11 -0
  14. jambonz_sdk/schema/callbacks/dequeue.schema.json +19 -0
  15. jambonz_sdk/schema/callbacks/dial-dtmf.schema.json +18 -0
  16. jambonz_sdk/schema/callbacks/dial-hold.schema.json +22 -0
  17. jambonz_sdk/schema/callbacks/dial-refer.schema.json +28 -0
  18. jambonz_sdk/schema/callbacks/dial.schema.json +31 -0
  19. jambonz_sdk/schema/callbacks/enqueue-wait.schema.json +17 -0
  20. jambonz_sdk/schema/callbacks/enqueue.schema.json +27 -0
  21. jambonz_sdk/schema/callbacks/gather-partial.schema.json +54 -0
  22. jambonz_sdk/schema/callbacks/gather.schema.json +60 -0
  23. jambonz_sdk/schema/callbacks/listen.schema.json +21 -0
  24. jambonz_sdk/schema/callbacks/llm.schema.json +30 -0
  25. jambonz_sdk/schema/callbacks/message.schema.json +35 -0
  26. jambonz_sdk/schema/callbacks/pipeline-turn.schema.json +109 -0
  27. jambonz_sdk/schema/callbacks/play.schema.json +36 -0
  28. jambonz_sdk/schema/callbacks/session-new.schema.json +143 -0
  29. jambonz_sdk/schema/callbacks/session-reconnect.schema.json +9 -0
  30. jambonz_sdk/schema/callbacks/session-redirect.schema.json +38 -0
  31. jambonz_sdk/schema/callbacks/sip-refer-event.schema.json +20 -0
  32. jambonz_sdk/schema/callbacks/sip-refer.schema.json +22 -0
  33. jambonz_sdk/schema/callbacks/sip-request.schema.json +27 -0
  34. jambonz_sdk/schema/callbacks/transcribe-translation.schema.json +24 -0
  35. jambonz_sdk/schema/callbacks/transcribe.schema.json +46 -0
  36. jambonz_sdk/schema/callbacks/tts-streaming-event.schema.json +77 -0
  37. jambonz_sdk/schema/callbacks/verb-status.schema.json +57 -0
  38. jambonz_sdk/schema/components/actionHook.schema.json +36 -0
  39. jambonz_sdk/schema/components/actionHookDelayAction.schema.json +37 -0
  40. jambonz_sdk/schema/components/amd.schema.json +68 -0
  41. jambonz_sdk/schema/components/auth.schema.json +18 -0
  42. jambonz_sdk/schema/components/bidirectionalAudio.schema.json +22 -0
  43. jambonz_sdk/schema/components/fillerNoise.schema.json +25 -0
  44. jambonz_sdk/schema/components/llm-base.schema.json +94 -0
  45. jambonz_sdk/schema/components/recognizer-assemblyAiOptions.schema.json +66 -0
  46. jambonz_sdk/schema/components/recognizer-awsOptions.schema.json +52 -0
  47. jambonz_sdk/schema/components/recognizer-azureOptions.schema.json +32 -0
  48. jambonz_sdk/schema/components/recognizer-cobaltOptions.schema.json +34 -0
  49. jambonz_sdk/schema/components/recognizer-customOptions.schema.json +27 -0
  50. jambonz_sdk/schema/components/recognizer-deepgramOptions.schema.json +147 -0
  51. jambonz_sdk/schema/components/recognizer-elevenlabsOptions.schema.json +39 -0
  52. jambonz_sdk/schema/components/recognizer-gladiaOptions.schema.json +8 -0
  53. jambonz_sdk/schema/components/recognizer-googleOptions.schema.json +35 -0
  54. jambonz_sdk/schema/components/recognizer-houndifyOptions.schema.json +53 -0
  55. jambonz_sdk/schema/components/recognizer-ibmOptions.schema.json +54 -0
  56. jambonz_sdk/schema/components/recognizer-nuanceOptions.schema.json +150 -0
  57. jambonz_sdk/schema/components/recognizer-nvidiaOptions.schema.json +39 -0
  58. jambonz_sdk/schema/components/recognizer-openaiOptions.schema.json +59 -0
  59. jambonz_sdk/schema/components/recognizer-sonioxOptions.schema.json +46 -0
  60. jambonz_sdk/schema/components/recognizer-speechmaticsOptions.schema.json +100 -0
  61. jambonz_sdk/schema/components/recognizer-verbioOptions.schema.json +46 -0
  62. jambonz_sdk/schema/components/recognizer.schema.json +216 -0
  63. jambonz_sdk/schema/components/synthesizer.schema.json +82 -0
  64. jambonz_sdk/schema/components/target.schema.json +105 -0
  65. jambonz_sdk/schema/components/vad.schema.json +48 -0
  66. jambonz_sdk/schema/jambonz-app.schema.json +113 -0
  67. jambonz_sdk/schema/verbs/alert.schema.json +34 -0
  68. jambonz_sdk/schema/verbs/answer.schema.json +22 -0
  69. jambonz_sdk/schema/verbs/conference.schema.json +107 -0
  70. jambonz_sdk/schema/verbs/config.schema.json +221 -0
  71. jambonz_sdk/schema/verbs/deepgram_s2s.schema.json +81 -0
  72. jambonz_sdk/schema/verbs/dequeue.schema.json +51 -0
  73. jambonz_sdk/schema/verbs/dial.schema.json +200 -0
  74. jambonz_sdk/schema/verbs/dialogflow.schema.json +148 -0
  75. jambonz_sdk/schema/verbs/dtmf.schema.json +49 -0
  76. jambonz_sdk/schema/verbs/dub.schema.json +103 -0
  77. jambonz_sdk/schema/verbs/elevenlabs_s2s.schema.json +81 -0
  78. jambonz_sdk/schema/verbs/enqueue.schema.json +53 -0
  79. jambonz_sdk/schema/verbs/gather.schema.json +190 -0
  80. jambonz_sdk/schema/verbs/google_s2s.schema.json +42 -0
  81. jambonz_sdk/schema/verbs/hangup.schema.json +36 -0
  82. jambonz_sdk/schema/verbs/leave.schema.json +22 -0
  83. jambonz_sdk/schema/verbs/listen.schema.json +127 -0
  84. jambonz_sdk/schema/verbs/llm.schema.json +44 -0
  85. jambonz_sdk/schema/verbs/message.schema.json +82 -0
  86. jambonz_sdk/schema/verbs/openai_s2s.schema.json +42 -0
  87. jambonz_sdk/schema/verbs/pause.schema.json +36 -0
  88. jambonz_sdk/schema/verbs/pipeline.schema.json +240 -0
  89. jambonz_sdk/schema/verbs/play.schema.json +96 -0
  90. jambonz_sdk/schema/verbs/redirect.schema.json +34 -0
  91. jambonz_sdk/schema/verbs/rest_dial.schema.json +113 -0
  92. jambonz_sdk/schema/verbs/s2s.schema.json +39 -0
  93. jambonz_sdk/schema/verbs/say.schema.json +107 -0
  94. jambonz_sdk/schema/verbs/sip-decline.schema.json +58 -0
  95. jambonz_sdk/schema/verbs/sip-refer.schema.json +58 -0
  96. jambonz_sdk/schema/verbs/sip-request.schema.json +54 -0
  97. jambonz_sdk/schema/verbs/stream.schema.json +103 -0
  98. jambonz_sdk/schema/verbs/tag.schema.json +41 -0
  99. jambonz_sdk/schema/verbs/transcribe.schema.json +57 -0
  100. jambonz_sdk/schema/verbs/ultravox_s2s.schema.json +41 -0
  101. jambonz_sdk/types/__init__.py +139 -0
  102. jambonz_sdk/types/components.py +250 -0
  103. jambonz_sdk/types/rest.py +59 -0
  104. jambonz_sdk/types/session.py +55 -0
  105. jambonz_sdk/types/verbs.py +572 -0
  106. jambonz_sdk/validator.py +107 -0
  107. jambonz_sdk/verb_builder.py +316 -0
  108. jambonz_sdk/verb_builder.pyi +1133 -0
  109. jambonz_sdk/verb_registry.py +102 -0
  110. jambonz_sdk/webhook/__init__.py +10 -0
  111. jambonz_sdk/webhook/middleware.py +63 -0
  112. jambonz_sdk/webhook/response.py +43 -0
  113. jambonz_sdk/websocket/__init__.py +15 -0
  114. jambonz_sdk/websocket/audio_client.py +11 -0
  115. jambonz_sdk/websocket/audio_stream.py +151 -0
  116. jambonz_sdk/websocket/client.py +165 -0
  117. jambonz_sdk/websocket/endpoint.py +193 -0
  118. jambonz_sdk/websocket/router.py +87 -0
  119. jambonz_sdk/websocket/session.py +259 -0
@@ -0,0 +1,316 @@
1
+ """VerbBuilder base class with auto-generated chainable verb methods.
2
+
3
+ Methods are generated at import time from JSON Schema files + the verb registry.
4
+ When the schema changes, the SDK automatically picks up new parameters —
5
+ no manual method signatures to maintain.
6
+
7
+ Each generated method has a real ``inspect.Signature`` with typed parameters
8
+ so IDEs (VS Code, PyCharm) show proper autocomplete and type hints.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import inspect
14
+ import json
15
+ import logging
16
+ import sys
17
+ from pathlib import Path
18
+ from typing import Any, Union
19
+
20
+ if sys.version_info >= (3, 11):
21
+ from typing import Self
22
+ else:
23
+ from typing_extensions import Self
24
+
25
+ from jambonz_sdk.types.verbs import AnyVerb
26
+ from jambonz_sdk.verb_registry import VERB_DEFS, VerbDef
27
+
28
+ logger = logging.getLogger("jambonz_sdk.verb_builder")
29
+
30
+ # ── JSON Schema type → Python type mapping ────────────────────────────
31
+
32
+ _TYPE_ANNOTATION_MAP: dict[str, type | object] = {
33
+ "string": str,
34
+ "number": Union[int, float],
35
+ "integer": int,
36
+ "boolean": bool,
37
+ "object": dict,
38
+ "array": list,
39
+ }
40
+
41
+ _TYPE_STR_MAP: dict[str, str] = {
42
+ "string": "str",
43
+ "number": "int | float",
44
+ "integer": "int",
45
+ "boolean": "bool",
46
+ "object": "dict[str, Any]",
47
+ "array": "list[Any]",
48
+ }
49
+
50
+
51
+ def _resolve_type(prop_schema: Any) -> type | object:
52
+ """Convert a JSON Schema property definition to a Python type annotation."""
53
+ if isinstance(prop_schema, str):
54
+ # Backward compat: simple type string (shouldn't happen with JSON Schema)
55
+ return _TYPE_ANNOTATION_MAP.get(prop_schema, Any)
56
+
57
+ if not isinstance(prop_schema, dict):
58
+ return Any
59
+
60
+ # $ref → component reference → dict
61
+ if "$ref" in prop_schema:
62
+ return dict
63
+
64
+ # const → the type of the const value
65
+ if "const" in prop_schema:
66
+ val = prop_schema["const"]
67
+ return type(val)
68
+
69
+ # oneOf → union of the branch types
70
+ if "oneOf" in prop_schema:
71
+ parts: list[type | object] = []
72
+ for branch in prop_schema["oneOf"]:
73
+ resolved = _resolve_type(branch)
74
+ if resolved is Union[int, float]:
75
+ parts.extend([int, float])
76
+ elif resolved not in parts:
77
+ parts.append(resolved)
78
+ # Deduplicate while preserving order
79
+ seen: set[type | object] = set()
80
+ unique = []
81
+ for p in parts:
82
+ if p not in seen:
83
+ seen.add(p)
84
+ unique.append(p)
85
+ if len(unique) == 1:
86
+ return unique[0]
87
+ return Union[tuple(unique)]
88
+
89
+ # Simple type
90
+ schema_type = prop_schema.get("type")
91
+ if isinstance(schema_type, str):
92
+ return _TYPE_ANNOTATION_MAP.get(schema_type, Any)
93
+
94
+ # Array of types
95
+ if isinstance(schema_type, list):
96
+ parts = []
97
+ for t in schema_type:
98
+ resolved = _TYPE_ANNOTATION_MAP.get(t, Any)
99
+ if resolved is Union[int, float]:
100
+ parts.extend([int, float])
101
+ elif resolved not in parts:
102
+ parts.append(resolved)
103
+ if len(parts) == 1:
104
+ return parts[0]
105
+ return Union[tuple(parts)]
106
+
107
+ return Any
108
+
109
+
110
+ def _python_type_str(prop_schema: Any) -> str:
111
+ """Convert a JSON Schema property definition to a human-readable type string."""
112
+ if isinstance(prop_schema, str):
113
+ return _TYPE_STR_MAP.get(prop_schema, "Any")
114
+
115
+ if not isinstance(prop_schema, dict):
116
+ return "Any"
117
+
118
+ if "$ref" in prop_schema:
119
+ return "dict[str, Any]"
120
+
121
+ if "const" in prop_schema:
122
+ return repr(type(prop_schema["const"]).__name__)
123
+
124
+ if "oneOf" in prop_schema:
125
+ parts = [_python_type_str(branch) for branch in prop_schema["oneOf"]]
126
+ return " | ".join(dict.fromkeys(parts))
127
+
128
+ schema_type = prop_schema.get("type")
129
+ if isinstance(schema_type, str):
130
+ return _TYPE_STR_MAP.get(schema_type, "Any")
131
+ if isinstance(schema_type, list):
132
+ parts = [_TYPE_STR_MAP.get(t, "Any") for t in schema_type]
133
+ return " | ".join(dict.fromkeys(parts))
134
+
135
+ return "Any"
136
+
137
+
138
+ # ── Load JSON Schemas ──────────────────────────────────────────────────
139
+
140
+ def _load_schemas() -> dict[str, Any]:
141
+ """Load verb JSON Schemas bundled alongside this package.
142
+
143
+ Returns a dict mapping verb spec names (e.g. 'say', 'sip:decline')
144
+ to their schema dicts, with a 'properties' key and optionally 'required'.
145
+ """
146
+ schema_dir = Path(__file__).resolve().parent / "schema" / "verbs"
147
+ schemas: dict[str, Any] = {}
148
+
149
+ if not schema_dir.is_dir():
150
+ logger.warning("Schema directory not found: %s", schema_dir)
151
+ return schemas
152
+
153
+ for schema_file in sorted(schema_dir.glob("*.schema.json")):
154
+ with schema_file.open() as f:
155
+ schema = json.load(f)
156
+
157
+ # Derive the spec name from the $id or filename
158
+ schema_id = schema.get("$id", "")
159
+ if schema_id:
160
+ # e.g. "https://jambonz.org/schema/verbs/say" → "say"
161
+ # e.g. "https://jambonz.org/schema/verbs/sip:decline" → "sip:decline"
162
+ spec_name = schema_id.rsplit("/", 1)[-1]
163
+ else:
164
+ # Fallback: filename without .schema.json
165
+ spec_name = schema_file.stem.replace(".schema", "")
166
+
167
+ # Collect properties, skipping 'verb' (it's a const, not a user param)
168
+ properties = {}
169
+ for prop_name, prop_def in schema.get("properties", {}).items():
170
+ if prop_name == "verb":
171
+ continue
172
+ properties[prop_name] = prop_def
173
+
174
+ # Handle allOf (used by vendor-specific s2s verbs that extend llm-base)
175
+ for entry in schema.get("allOf", []):
176
+ if "properties" in entry:
177
+ for prop_name, prop_def in entry["properties"].items():
178
+ if prop_name == "verb":
179
+ continue
180
+ properties[prop_name] = prop_def
181
+
182
+ schemas[spec_name] = {
183
+ "properties": properties,
184
+ "required": schema.get("required", []),
185
+ }
186
+
187
+ return schemas
188
+
189
+
190
+ _SPECS: dict[str, Any] = _load_schemas()
191
+
192
+
193
+ # ── Method factory ──────────────────────────────────────────────────
194
+
195
+ def _make_verb_method(verb_def: VerbDef, spec: dict[str, Any]) -> Any:
196
+ """Create a verb method with a real typed signature from the spec.
197
+
198
+ Each generated method has:
199
+ - ``inspect.Signature`` with keyword-only parameters (default ``None``)
200
+ - ``__annotations__`` with resolved Python types (not ``Any``)
201
+ - Docstring with parameter types and required markers
202
+ """
203
+ properties = spec.get("properties", {})
204
+ required = set(spec.get("required", []))
205
+ json_verb = verb_def.json_verb
206
+ inject = verb_def.inject
207
+
208
+ def verb_method(self: VerbBuilder, **kwargs: Any) -> Self:
209
+ data: dict[str, Any] = {}
210
+ if inject:
211
+ data.update(inject)
212
+ for key, value in kwargs.items():
213
+ if value is None:
214
+ continue
215
+ if key == "from_":
216
+ data["from"] = value
217
+ else:
218
+ data[key] = value
219
+ verb: dict[str, Any] = {"verb": json_verb, **data}
220
+ self._verbs.append(verb) # type: ignore[arg-type]
221
+ return self
222
+
223
+ # ── Build inspect.Signature with typed keyword-only params ──────
224
+ params = [inspect.Parameter("self", inspect.Parameter.POSITIONAL_OR_KEYWORD)]
225
+ annotations: dict[str, Any] = {}
226
+
227
+ for prop_name, prop_spec in properties.items():
228
+ py_name = "from_" if prop_name == "from" else prop_name
229
+ py_type = _resolve_type(prop_spec)
230
+ params.append(inspect.Parameter(
231
+ py_name,
232
+ inspect.Parameter.KEYWORD_ONLY,
233
+ default=None,
234
+ annotation=py_type,
235
+ ))
236
+ annotations[py_name] = py_type
237
+
238
+ # Add **kwargs for forward compatibility
239
+ params.append(inspect.Parameter("kwargs", inspect.Parameter.VAR_KEYWORD))
240
+ annotations["return"] = Self
241
+
242
+ verb_method.__signature__ = inspect.Signature(params)
243
+ verb_method.__annotations__ = annotations
244
+
245
+ # ── Build docstring ─────────────────────────────────────────────
246
+ doc_lines = [verb_def.doc, ""]
247
+ if required:
248
+ doc_lines.append(f"Required: {', '.join(sorted(required))}")
249
+ doc_lines.append("")
250
+ doc_lines.append("Args:")
251
+ for prop_name, prop_spec in properties.items():
252
+ py_name = "from_" if prop_name == "from" else prop_name
253
+ py_type_str = _python_type_str(prop_spec)
254
+ req_marker = " **(required)**" if prop_name in required else ""
255
+ doc_lines.append(f" {py_name} ({py_type_str}):{req_marker}")
256
+ doc_lines.append("")
257
+ doc_lines.append("Returns:")
258
+ doc_lines.append(" self for chaining.")
259
+
260
+ verb_method.__doc__ = "\n".join(doc_lines)
261
+ verb_method.__name__ = verb_def.method_name
262
+ verb_method.__qualname__ = f"VerbBuilder.{verb_def.method_name}"
263
+
264
+ return verb_method
265
+
266
+
267
+ # ── VerbBuilder class ───────────────────────────────────────────────
268
+
269
+ class VerbBuilder:
270
+ """Builds an ordered list of jambonz verbs using a fluent API.
271
+
272
+ All verb methods are auto-generated from JSON Schema files and accept
273
+ keyword arguments matching the verb's specification. Methods return
274
+ ``self`` for chaining.
275
+
276
+ Example::
277
+
278
+ builder = VerbBuilder()
279
+ verbs = (
280
+ builder
281
+ .say(text="Hello!")
282
+ .pause(length=1)
283
+ .gather(input=["speech"], actionHook="/result", timeout=10)
284
+ .hangup()
285
+ .to_list()
286
+ )
287
+ """
288
+
289
+ def __init__(self) -> None:
290
+ self._verbs: list[AnyVerb] = []
291
+
292
+ def to_list(self) -> list[AnyVerb]:
293
+ """Return the verb list and reset the builder."""
294
+ verbs = list(self._verbs)
295
+ self._verbs = []
296
+ return verbs
297
+
298
+
299
+ # ── Attach generated methods to VerbBuilder ─────────────────────────
300
+
301
+ def _build_methods() -> None:
302
+ """Generate and attach verb methods to VerbBuilder from schemas + registry."""
303
+ for verb_def in VERB_DEFS:
304
+ spec = _SPECS.get(verb_def.spec_name)
305
+ if spec is None:
306
+ logger.warning(
307
+ "Schema for '%s' not found for method '%s' — skipping",
308
+ verb_def.spec_name,
309
+ verb_def.method_name,
310
+ )
311
+ continue
312
+ method = _make_verb_method(verb_def, spec)
313
+ setattr(VerbBuilder, verb_def.method_name, method)
314
+
315
+
316
+ _build_methods()