signalwire-agents 0.1.13__py3-none-any.whl → 1.0.17.dev4__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 (143) hide show
  1. signalwire_agents/__init__.py +99 -15
  2. signalwire_agents/agent_server.py +248 -60
  3. signalwire_agents/agents/bedrock.py +296 -0
  4. signalwire_agents/cli/__init__.py +9 -0
  5. signalwire_agents/cli/build_search.py +951 -41
  6. signalwire_agents/cli/config.py +80 -0
  7. signalwire_agents/cli/core/__init__.py +10 -0
  8. signalwire_agents/cli/core/agent_loader.py +470 -0
  9. signalwire_agents/cli/core/argparse_helpers.py +179 -0
  10. signalwire_agents/cli/core/dynamic_config.py +71 -0
  11. signalwire_agents/cli/core/service_loader.py +303 -0
  12. signalwire_agents/cli/dokku.py +2320 -0
  13. signalwire_agents/cli/execution/__init__.py +10 -0
  14. signalwire_agents/cli/execution/datamap_exec.py +446 -0
  15. signalwire_agents/cli/execution/webhook_exec.py +134 -0
  16. signalwire_agents/cli/init_project.py +2636 -0
  17. signalwire_agents/cli/output/__init__.py +10 -0
  18. signalwire_agents/cli/output/output_formatter.py +255 -0
  19. signalwire_agents/cli/output/swml_dump.py +186 -0
  20. signalwire_agents/cli/simulation/__init__.py +10 -0
  21. signalwire_agents/cli/simulation/data_generation.py +374 -0
  22. signalwire_agents/cli/simulation/data_overrides.py +200 -0
  23. signalwire_agents/cli/simulation/mock_env.py +282 -0
  24. signalwire_agents/cli/swaig_test_wrapper.py +52 -0
  25. signalwire_agents/cli/test_swaig.py +566 -2366
  26. signalwire_agents/cli/types.py +81 -0
  27. signalwire_agents/core/__init__.py +2 -2
  28. signalwire_agents/core/agent/__init__.py +12 -0
  29. signalwire_agents/core/agent/config/__init__.py +12 -0
  30. signalwire_agents/core/agent/deployment/__init__.py +9 -0
  31. signalwire_agents/core/agent/deployment/handlers/__init__.py +9 -0
  32. signalwire_agents/core/agent/prompt/__init__.py +14 -0
  33. signalwire_agents/core/agent/prompt/manager.py +306 -0
  34. signalwire_agents/core/agent/routing/__init__.py +9 -0
  35. signalwire_agents/core/agent/security/__init__.py +9 -0
  36. signalwire_agents/core/agent/swml/__init__.py +9 -0
  37. signalwire_agents/core/agent/tools/__init__.py +15 -0
  38. signalwire_agents/core/agent/tools/decorator.py +97 -0
  39. signalwire_agents/core/agent/tools/registry.py +210 -0
  40. signalwire_agents/core/agent_base.py +845 -2916
  41. signalwire_agents/core/auth_handler.py +233 -0
  42. signalwire_agents/core/config_loader.py +259 -0
  43. signalwire_agents/core/contexts.py +418 -0
  44. signalwire_agents/core/data_map.py +3 -15
  45. signalwire_agents/core/function_result.py +116 -44
  46. signalwire_agents/core/logging_config.py +162 -18
  47. signalwire_agents/core/mixins/__init__.py +28 -0
  48. signalwire_agents/core/mixins/ai_config_mixin.py +442 -0
  49. signalwire_agents/core/mixins/auth_mixin.py +280 -0
  50. signalwire_agents/core/mixins/prompt_mixin.py +358 -0
  51. signalwire_agents/core/mixins/serverless_mixin.py +460 -0
  52. signalwire_agents/core/mixins/skill_mixin.py +55 -0
  53. signalwire_agents/core/mixins/state_mixin.py +153 -0
  54. signalwire_agents/core/mixins/tool_mixin.py +230 -0
  55. signalwire_agents/core/mixins/web_mixin.py +1142 -0
  56. signalwire_agents/core/security_config.py +333 -0
  57. signalwire_agents/core/skill_base.py +84 -1
  58. signalwire_agents/core/skill_manager.py +62 -20
  59. signalwire_agents/core/swaig_function.py +18 -5
  60. signalwire_agents/core/swml_builder.py +207 -11
  61. signalwire_agents/core/swml_handler.py +27 -21
  62. signalwire_agents/core/swml_renderer.py +123 -312
  63. signalwire_agents/core/swml_service.py +171 -203
  64. signalwire_agents/mcp_gateway/__init__.py +29 -0
  65. signalwire_agents/mcp_gateway/gateway_service.py +564 -0
  66. signalwire_agents/mcp_gateway/mcp_manager.py +513 -0
  67. signalwire_agents/mcp_gateway/session_manager.py +218 -0
  68. signalwire_agents/prefabs/concierge.py +0 -3
  69. signalwire_agents/prefabs/faq_bot.py +0 -3
  70. signalwire_agents/prefabs/info_gatherer.py +0 -3
  71. signalwire_agents/prefabs/receptionist.py +0 -3
  72. signalwire_agents/prefabs/survey.py +0 -3
  73. signalwire_agents/schema.json +9218 -5489
  74. signalwire_agents/search/__init__.py +7 -1
  75. signalwire_agents/search/document_processor.py +490 -31
  76. signalwire_agents/search/index_builder.py +307 -37
  77. signalwire_agents/search/migration.py +418 -0
  78. signalwire_agents/search/models.py +30 -0
  79. signalwire_agents/search/pgvector_backend.py +748 -0
  80. signalwire_agents/search/query_processor.py +162 -31
  81. signalwire_agents/search/search_engine.py +916 -35
  82. signalwire_agents/search/search_service.py +376 -53
  83. signalwire_agents/skills/README.md +452 -0
  84. signalwire_agents/skills/__init__.py +14 -2
  85. signalwire_agents/skills/api_ninjas_trivia/README.md +215 -0
  86. signalwire_agents/skills/api_ninjas_trivia/__init__.py +12 -0
  87. signalwire_agents/skills/api_ninjas_trivia/skill.py +237 -0
  88. signalwire_agents/skills/datasphere/README.md +210 -0
  89. signalwire_agents/skills/datasphere/skill.py +84 -3
  90. signalwire_agents/skills/datasphere_serverless/README.md +258 -0
  91. signalwire_agents/skills/datasphere_serverless/__init__.py +9 -0
  92. signalwire_agents/skills/datasphere_serverless/skill.py +82 -1
  93. signalwire_agents/skills/datetime/README.md +132 -0
  94. signalwire_agents/skills/datetime/__init__.py +9 -0
  95. signalwire_agents/skills/datetime/skill.py +20 -7
  96. signalwire_agents/skills/joke/README.md +149 -0
  97. signalwire_agents/skills/joke/__init__.py +9 -0
  98. signalwire_agents/skills/joke/skill.py +21 -0
  99. signalwire_agents/skills/math/README.md +161 -0
  100. signalwire_agents/skills/math/__init__.py +9 -0
  101. signalwire_agents/skills/math/skill.py +18 -4
  102. signalwire_agents/skills/mcp_gateway/README.md +230 -0
  103. signalwire_agents/skills/mcp_gateway/__init__.py +10 -0
  104. signalwire_agents/skills/mcp_gateway/skill.py +421 -0
  105. signalwire_agents/skills/native_vector_search/README.md +210 -0
  106. signalwire_agents/skills/native_vector_search/__init__.py +9 -0
  107. signalwire_agents/skills/native_vector_search/skill.py +569 -101
  108. signalwire_agents/skills/play_background_file/README.md +218 -0
  109. signalwire_agents/skills/play_background_file/__init__.py +12 -0
  110. signalwire_agents/skills/play_background_file/skill.py +242 -0
  111. signalwire_agents/skills/registry.py +395 -40
  112. signalwire_agents/skills/spider/README.md +236 -0
  113. signalwire_agents/skills/spider/__init__.py +13 -0
  114. signalwire_agents/skills/spider/skill.py +598 -0
  115. signalwire_agents/skills/swml_transfer/README.md +395 -0
  116. signalwire_agents/skills/swml_transfer/__init__.py +10 -0
  117. signalwire_agents/skills/swml_transfer/skill.py +359 -0
  118. signalwire_agents/skills/weather_api/README.md +178 -0
  119. signalwire_agents/skills/weather_api/__init__.py +12 -0
  120. signalwire_agents/skills/weather_api/skill.py +191 -0
  121. signalwire_agents/skills/web_search/README.md +163 -0
  122. signalwire_agents/skills/web_search/__init__.py +9 -0
  123. signalwire_agents/skills/web_search/skill.py +586 -112
  124. signalwire_agents/skills/wikipedia_search/README.md +228 -0
  125. signalwire_agents/{core/state → skills/wikipedia_search}/__init__.py +5 -4
  126. signalwire_agents/skills/{wikipedia → wikipedia_search}/skill.py +33 -3
  127. signalwire_agents/web/__init__.py +17 -0
  128. signalwire_agents/web/web_service.py +559 -0
  129. signalwire_agents-1.0.17.dev4.data/data/share/man/man1/sw-agent-init.1 +400 -0
  130. signalwire_agents-1.0.17.dev4.data/data/share/man/man1/sw-search.1 +483 -0
  131. signalwire_agents-1.0.17.dev4.data/data/share/man/man1/swaig-test.1 +308 -0
  132. {signalwire_agents-0.1.13.dist-info → signalwire_agents-1.0.17.dev4.dist-info}/METADATA +347 -215
  133. signalwire_agents-1.0.17.dev4.dist-info/RECORD +147 -0
  134. signalwire_agents-1.0.17.dev4.dist-info/entry_points.txt +6 -0
  135. signalwire_agents/core/state/file_state_manager.py +0 -219
  136. signalwire_agents/core/state/state_manager.py +0 -101
  137. signalwire_agents/skills/wikipedia/__init__.py +0 -9
  138. signalwire_agents-0.1.13.data/data/schema.json +0 -5611
  139. signalwire_agents-0.1.13.dist-info/RECORD +0 -67
  140. signalwire_agents-0.1.13.dist-info/entry_points.txt +0 -3
  141. {signalwire_agents-0.1.13.dist-info → signalwire_agents-1.0.17.dev4.dist-info}/WHEEL +0 -0
  142. {signalwire_agents-0.1.13.dist-info → signalwire_agents-1.0.17.dev4.dist-info}/licenses/LICENSE +0 -0
  143. {signalwire_agents-0.1.13.dist-info → signalwire_agents-1.0.17.dev4.dist-info}/top_level.txt +0 -0
@@ -14,6 +14,7 @@ This module provides a fluent builder API for creating SWML documents.
14
14
  It allows for chaining method calls to build up a document step by step.
15
15
  """
16
16
 
17
+ import types
17
18
  from typing import Dict, List, Any, Optional, Union, TypeVar
18
19
  try:
19
20
  from typing import Self # Python 3.11+
@@ -43,6 +44,12 @@ class SWMLBuilder:
43
44
  service: The SWMLService to delegate to
44
45
  """
45
46
  self.service = service
47
+
48
+ # Dictionary to cache dynamically created methods
49
+ self._verb_methods_cache = {}
50
+
51
+ # Create auto-vivified methods for all verbs
52
+ self._create_verb_methods()
46
53
 
47
54
  def answer(self, max_duration: Optional[int] = None, codecs: Optional[str] = None) -> Self:
48
55
  """
@@ -55,7 +62,12 @@ class SWMLBuilder:
55
62
  Returns:
56
63
  Self for method chaining
57
64
  """
58
- self.service.add_answer_verb(max_duration, codecs)
65
+ config = {}
66
+ if max_duration is not None:
67
+ config["max_duration"] = max_duration
68
+ if codecs is not None:
69
+ config["codecs"] = codecs
70
+ self.service.add_verb("answer", config)
59
71
  return self
60
72
 
61
73
  def hangup(self, reason: Optional[str] = None) -> Self:
@@ -68,7 +80,10 @@ class SWMLBuilder:
68
80
  Returns:
69
81
  Self for method chaining
70
82
  """
71
- self.service.add_hangup_verb(reason)
83
+ config = {}
84
+ if reason is not None:
85
+ config["reason"] = reason
86
+ self.service.add_verb("hangup", config)
72
87
  return self
73
88
 
74
89
  def ai(self,
@@ -92,14 +107,26 @@ class SWMLBuilder:
92
107
  Returns:
93
108
  Self for method chaining
94
109
  """
95
- self.service.add_ai_verb(
96
- prompt_text=prompt_text,
97
- prompt_pom=prompt_pom,
98
- post_prompt=post_prompt,
99
- post_prompt_url=post_prompt_url,
100
- swaig=swaig,
101
- **kwargs
102
- )
110
+ config = {}
111
+
112
+ # Handle prompt (either text or POM, but not both)
113
+ if prompt_text is not None:
114
+ config["prompt"] = prompt_text
115
+ elif prompt_pom is not None:
116
+ config["prompt"] = prompt_pom
117
+
118
+ # Add optional parameters
119
+ if post_prompt is not None:
120
+ config["post_prompt"] = post_prompt
121
+ if post_prompt_url is not None:
122
+ config["post_prompt_url"] = post_prompt_url
123
+ if swaig is not None:
124
+ config["SWAIG"] = swaig
125
+
126
+ # Add any additional kwargs
127
+ config.update(kwargs)
128
+
129
+ self.service.add_verb("ai", config)
103
130
  return self
104
131
 
105
132
  def play(self, url: Optional[str] = None, urls: Optional[List[str]] = None,
@@ -215,4 +242,173 @@ class SWMLBuilder:
215
242
  Self for method chaining
216
243
  """
217
244
  self.service.reset_document()
218
- return self
245
+ return self
246
+
247
+ def _create_verb_methods(self) -> None:
248
+ """
249
+ Create auto-vivified methods for all verbs at initialization time
250
+
251
+ This creates methods for all SWML verbs defined in the schema,
252
+ allowing for fluent chaining like builder.denoise().goto().play()
253
+ """
254
+ # Get all verb names from the schema
255
+ if not hasattr(self.service, 'schema_utils') or not self.service.schema_utils:
256
+ return
257
+
258
+ verb_names = self.service.schema_utils.get_all_verb_names()
259
+
260
+ # Create a method for each verb
261
+ for verb_name in verb_names:
262
+ # Skip verbs that already have specific methods
263
+ if hasattr(self, verb_name):
264
+ continue
265
+
266
+ # Handle sleep verb specially since it takes an integer directly
267
+ if verb_name == "sleep":
268
+ def sleep_method(self_instance, duration=None, **kwargs):
269
+ """
270
+ Add the sleep verb to the document.
271
+
272
+ Args:
273
+ duration: The amount of time to sleep in milliseconds
274
+ """
275
+ # Sleep verb takes a direct integer parameter in SWML
276
+ if duration is not None:
277
+ self_instance.service.add_verb("sleep", duration)
278
+ elif kwargs:
279
+ # Try to get the value from kwargs
280
+ self_instance.service.add_verb("sleep", next(iter(kwargs.values())))
281
+ else:
282
+ raise TypeError("sleep() missing required argument: 'duration'")
283
+ return self_instance
284
+
285
+ # Set it as an attribute of self
286
+ setattr(self, verb_name, types.MethodType(sleep_method, self))
287
+
288
+ # Also cache it for later
289
+ self._verb_methods_cache[verb_name] = sleep_method
290
+ continue
291
+
292
+ # Generate the method implementation for normal verbs
293
+ def make_verb_method(name):
294
+ def verb_method(self_instance, **kwargs):
295
+ """
296
+ Dynamically generated method for SWML verb - returns self for chaining
297
+ """
298
+ config = {}
299
+ for key, value in kwargs.items():
300
+ if value is not None:
301
+ config[key] = value
302
+ self_instance.service.add_verb(name, config)
303
+ return self_instance
304
+
305
+ # Add docstring to the method
306
+ if hasattr(self.service.schema_utils, 'get_verb_properties'):
307
+ verb_properties = self.service.schema_utils.get_verb_properties(name)
308
+ if "description" in verb_properties:
309
+ verb_method.__doc__ = f"Add the {name} verb to the document.\n\n{verb_properties['description']}\n\nReturns: Self for method chaining"
310
+ else:
311
+ verb_method.__doc__ = f"Add the {name} verb to the document.\n\nReturns: Self for method chaining"
312
+ else:
313
+ verb_method.__doc__ = f"Add the {name} verb to the document.\n\nReturns: Self for method chaining"
314
+
315
+ return verb_method
316
+
317
+ # Create the method with closure over the verb name
318
+ method = make_verb_method(verb_name)
319
+
320
+ # Set it as an attribute of self
321
+ setattr(self, verb_name, types.MethodType(method, self))
322
+
323
+ # Also cache it for later
324
+ self._verb_methods_cache[verb_name] = method
325
+
326
+ def __getattr__(self, name: str) -> Any:
327
+ """
328
+ Dynamically generate and return SWML verb methods when accessed
329
+
330
+ This method is called when an attribute lookup fails through the normal
331
+ mechanisms. It checks if the attribute name corresponds to a SWML verb
332
+ defined in the schema, and if so, dynamically creates a method for that verb.
333
+
334
+ Args:
335
+ name: The name of the attribute being accessed
336
+
337
+ Returns:
338
+ The dynamically created verb method if name is a valid SWML verb,
339
+ otherwise raises AttributeError
340
+
341
+ Raises:
342
+ AttributeError: If name is not a valid SWML verb
343
+ """
344
+ # First check if this is a valid SWML verb
345
+ if not hasattr(self.service, 'schema_utils') or not self.service.schema_utils:
346
+ msg = f"'{self.__class__.__name__}' object has no attribute '{name}' (no schema available)"
347
+ raise AttributeError(msg)
348
+
349
+ verb_names = self.service.schema_utils.get_all_verb_names()
350
+
351
+ if name in verb_names:
352
+ # Check if we already have this method in the cache
353
+ if not hasattr(self, '_verb_methods_cache'):
354
+ self._verb_methods_cache = {}
355
+
356
+ if name in self._verb_methods_cache:
357
+ return types.MethodType(self._verb_methods_cache[name], self)
358
+
359
+ # Handle sleep verb specially since it takes an integer directly
360
+ if name == "sleep":
361
+ def sleep_method(self_instance, duration=None, **kwargs):
362
+ """
363
+ Add the sleep verb to the document.
364
+
365
+ Args:
366
+ duration: The amount of time to sleep in milliseconds
367
+ """
368
+ # Sleep verb takes a direct integer parameter in SWML
369
+ if duration is not None:
370
+ self_instance.service.add_verb("sleep", duration)
371
+ elif kwargs:
372
+ # Try to get the value from kwargs
373
+ self_instance.service.add_verb("sleep", next(iter(kwargs.values())))
374
+ else:
375
+ raise TypeError("sleep() missing required argument: 'duration'")
376
+ return self_instance
377
+
378
+ # Cache the method for future use
379
+ self._verb_methods_cache[name] = sleep_method
380
+
381
+ # Return the bound method
382
+ return types.MethodType(sleep_method, self)
383
+
384
+ # Generate the method implementation for normal verbs
385
+ def verb_method(self_instance, **kwargs):
386
+ """
387
+ Dynamically generated method for SWML verb - returns self for chaining
388
+ """
389
+ config = {}
390
+ for key, value in kwargs.items():
391
+ if value is not None:
392
+ config[key] = value
393
+ self_instance.service.add_verb(name, config)
394
+ return self_instance
395
+
396
+ # Add docstring to the method
397
+ if hasattr(self.service.schema_utils, 'get_verb_properties'):
398
+ verb_properties = self.service.schema_utils.get_verb_properties(name)
399
+ if "description" in verb_properties:
400
+ verb_method.__doc__ = f"Add the {name} verb to the document.\n\n{verb_properties['description']}\n\nReturns: Self for method chaining"
401
+ else:
402
+ verb_method.__doc__ = f"Add the {name} verb to the document.\n\nReturns: Self for method chaining"
403
+ else:
404
+ verb_method.__doc__ = f"Add the {name} verb to the document.\n\nReturns: Self for method chaining"
405
+
406
+ # Cache the method for future use
407
+ self._verb_methods_cache[name] = verb_method
408
+
409
+ # Return the bound method
410
+ return types.MethodType(verb_method, self)
411
+
412
+ # Not a valid verb
413
+ msg = f"'{self.__class__.__name__}' object has no attribute '{name}'"
414
+ raise AttributeError(msg)
@@ -103,19 +103,19 @@ class AIVerbHandler(SWMLVerbHandler):
103
103
  errors.append("'prompt' must be an object")
104
104
  return False, errors
105
105
 
106
- # Check that prompt contains one of: text, pom, or contexts
106
+ # Check that prompt contains either text or pom (required)
107
107
  has_text = "text" in prompt
108
108
  has_pom = "pom" in prompt
109
109
  has_contexts = "contexts" in prompt
110
110
 
111
- options_count = sum([has_text, has_pom, has_contexts])
111
+ # Require either text or pom (mutually exclusive)
112
+ base_prompt_count = sum([has_text, has_pom])
113
+ if base_prompt_count == 0:
114
+ errors.append("'prompt' must contain either 'text' or 'pom' as base prompt")
115
+ elif base_prompt_count > 1:
116
+ errors.append("'prompt' can only contain one of: 'text' or 'pom' (mutually exclusive)")
112
117
 
113
- if options_count == 0:
114
- errors.append("'prompt' must contain one of: 'text', 'pom', or 'contexts'")
115
- elif options_count > 1:
116
- errors.append("'prompt' can only contain one of: 'text', 'pom', or 'contexts'")
117
-
118
- # Validate contexts structure if present
118
+ # Contexts are optional and can be combined with text or pom
119
119
  if has_contexts:
120
120
  contexts = prompt["contexts"]
121
121
  if not isinstance(contexts, dict):
@@ -141,9 +141,9 @@ class AIVerbHandler(SWMLVerbHandler):
141
141
  Build a configuration for the AI verb
142
142
 
143
143
  Args:
144
- prompt_text: Text prompt for the AI (mutually exclusive with prompt_pom and contexts)
145
- prompt_pom: POM structure for the AI prompt (mutually exclusive with prompt_text and contexts)
146
- contexts: Contexts and steps configuration (mutually exclusive with prompt_text and prompt_pom)
144
+ prompt_text: Text prompt for the AI (mutually exclusive with prompt_pom)
145
+ prompt_pom: POM structure for the AI prompt (mutually exclusive with prompt_text)
146
+ contexts: Optional contexts and steps configuration (can be combined with text or pom)
147
147
  post_prompt: Optional post-prompt text
148
148
  post_prompt_url: Optional URL for post-prompt processing
149
149
  swaig: Optional SWAIG configuration
@@ -154,19 +154,25 @@ class AIVerbHandler(SWMLVerbHandler):
154
154
  """
155
155
  config = {}
156
156
 
157
- # Add prompt (either text, POM, or contexts - mutually exclusive)
158
- prompt_options_count = sum(x is not None for x in [prompt_text, prompt_pom, contexts])
159
- if prompt_options_count == 0:
160
- raise ValueError("One of prompt_text, prompt_pom, or contexts must be provided")
161
- elif prompt_options_count > 1:
162
- raise ValueError("prompt_text, prompt_pom, and contexts are mutually exclusive")
157
+ # Require either text or pom as base prompt (mutually exclusive)
158
+ base_prompt_count = sum(x is not None for x in [prompt_text, prompt_pom])
159
+ if base_prompt_count == 0:
160
+ raise ValueError("Either prompt_text or prompt_pom must be provided as base prompt")
161
+ elif base_prompt_count > 1:
162
+ raise ValueError("prompt_text and prompt_pom are mutually exclusive")
163
163
 
164
+ # Build prompt object with base prompt
165
+ prompt_config = {}
164
166
  if prompt_text is not None:
165
- config["prompt"] = {"text": prompt_text}
167
+ prompt_config["text"] = prompt_text
166
168
  elif prompt_pom is not None:
167
- config["prompt"] = {"pom": prompt_pom}
168
- elif contexts is not None:
169
- config["prompt"] = {"contexts": contexts}
169
+ prompt_config["pom"] = prompt_pom
170
+
171
+ # Add contexts if provided (optional, activates steps feature)
172
+ if contexts is not None:
173
+ prompt_config["contexts"] = contexts
174
+
175
+ config["prompt"] = prompt_config
170
176
 
171
177
  # Add post-prompt if provided
172
178
  if post_prompt is not None: