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
@@ -8,12 +8,11 @@ See LICENSE file in the project root for full license information.
8
8
  """
9
9
 
10
10
  """
11
- SwmlRenderer for generating complete SWML documents for SignalWire AI Agents
11
+ SWML document rendering utilities for SignalWire AI Agents.
12
12
  """
13
13
 
14
- from typing import Dict, List, Any, Optional, Union
15
14
  import json
16
- import yaml
15
+ from typing import Dict, List, Any, Optional, Union
17
16
 
18
17
  from signalwire_agents.core.swml_service import SWMLService
19
18
  from signalwire_agents.core.swml_builder import SWMLBuilder
@@ -23,14 +22,13 @@ class SwmlRenderer:
23
22
  """
24
23
  Renders SWML documents for SignalWire AI Agents with AI and SWAIG components
25
24
 
26
- This class provides backward-compatible methods for rendering SWML documents
27
- while also supporting the new SWMLService architecture. It can work either
28
- standalone (legacy mode) or with a SWMLService instance.
25
+ This class provides methods for rendering SWML documents using the SWMLService architecture.
29
26
  """
30
27
 
31
28
  @staticmethod
32
29
  def render_swml(
33
30
  prompt: Union[str, List[Dict[str, Any]]],
31
+ service: SWMLService,
34
32
  post_prompt: Optional[str] = None,
35
33
  post_prompt_url: Optional[str] = None,
36
34
  swaig_functions: Optional[List[Dict[str, Any]]] = None,
@@ -43,341 +41,154 @@ class SwmlRenderer:
43
41
  record_format: str = "mp4",
44
42
  record_stereo: bool = True,
45
43
  format: str = "json",
46
- default_webhook_url: Optional[str] = None,
47
- service: Optional[SWMLService] = None
44
+ default_webhook_url: Optional[str] = None
48
45
  ) -> str:
49
46
  """
50
47
  Generate a complete SWML document with AI configuration
51
48
 
52
49
  Args:
53
- prompt: Either a string prompt or a POM in list-of-dict format
54
- post_prompt: Optional post-prompt text (for summary)
55
- post_prompt_url: URL to receive the post-prompt result
50
+ prompt: AI prompt text or POM structure
51
+ service: SWMLService instance to use for document building
52
+ post_prompt: Optional post-prompt text
53
+ post_prompt_url: Optional post-prompt URL
56
54
  swaig_functions: List of SWAIG function definitions
57
- startup_hook_url: URL for startup hook
58
- hangup_hook_url: URL for hangup hook
59
- prompt_is_pom: Whether prompt is a POM object or raw text
60
- params: Additional AI params (temperature, etc)
61
- add_answer: Whether to auto-add the answer block after AI
62
- record_call: Whether to add a record_call block
63
- record_format: Format for recording the call
55
+ startup_hook_url: Optional startup hook URL
56
+ hangup_hook_url: Optional hangup hook URL
57
+ prompt_is_pom: Whether prompt is POM format
58
+ params: Additional AI verb parameters
59
+ add_answer: Whether to add answer verb
60
+ record_call: Whether to add record_call verb
61
+ record_format: Recording format
64
62
  record_stereo: Whether to record in stereo
65
- format: Output format, 'json' or 'yaml'
66
- default_webhook_url: Optional default webhook URL for all SWAIG functions
67
- service: Optional SWMLService instance to use
63
+ format: Output format (json or yaml)
64
+ default_webhook_url: Default webhook URL for SWAIG functions
68
65
 
69
66
  Returns:
70
67
  SWML document as a string
71
68
  """
72
- # If we have a service, use it to build the document
73
- if service:
74
- # Create a builder for the service
75
- builder = SWMLBuilder(service)
76
-
77
- # Reset the document to start fresh
78
- builder.reset()
79
-
80
- # Add answer block if requested
81
- if add_answer:
82
- builder.answer()
83
-
84
- # Add record_call if requested
85
- if record_call:
86
- # TODO: Add record_call to builder API
87
- service.add_verb("record_call", {
88
- "format": record_format,
89
- "stereo": record_stereo
90
- })
91
-
92
- # Configure SWAIG object for AI verb
93
- swaig_config = {}
94
- functions = []
95
-
96
- # Add startup hook if provided
97
- if startup_hook_url:
98
- functions.append({
99
- "function": "startup_hook",
100
- "description": "Called when the call starts",
101
- "parameters": {
102
- "type": "object",
103
- "properties": {}
104
- },
105
- "web_hook_url": startup_hook_url
106
- })
107
-
108
- # Add hangup hook if provided
109
- if hangup_hook_url:
110
- functions.append({
111
- "function": "hangup_hook",
112
- "description": "Called when the call ends",
113
- "parameters": {
114
- "type": "object",
115
- "properties": {}
116
- },
117
- "web_hook_url": hangup_hook_url
118
- })
119
-
120
- # Add regular functions if provided
121
- if swaig_functions:
122
- for func in swaig_functions:
123
- # Skip special hooks as we've already added them
124
- if func.get("function") not in ["startup_hook", "hangup_hook"]:
125
- functions.append(func)
126
-
127
- # Only add SWAIG if we have functions or a default URL
128
- if functions or default_webhook_url:
129
- swaig_config = {}
130
-
131
- # Add defaults if we have a default webhook URL
132
- if default_webhook_url:
133
- swaig_config["defaults"] = {
134
- "web_hook_url": default_webhook_url
135
- }
136
-
137
- # Add functions if we have any
138
- if functions:
139
- swaig_config["functions"] = functions
140
-
141
- # Add AI verb with appropriate configuration
142
- builder.ai(
143
- prompt_text=None if prompt_is_pom else prompt,
144
- prompt_pom=prompt if prompt_is_pom else None,
145
- post_prompt=post_prompt,
146
- post_prompt_url=post_prompt_url,
147
- swaig=swaig_config if swaig_config else None,
148
- **(params or {})
149
- )
150
-
151
- # Get the document as a dictionary or string based on format
152
- if format.lower() == "yaml":
153
- import yaml
154
- return yaml.dump(builder.build(), sort_keys=False)
155
- else:
156
- return builder.render()
157
- else:
158
- # Legacy implementation (unchanged for backward compatibility)
159
- # Start building the SWML document
160
- swml = {
161
- "version": "1.0.0",
162
- "sections": {
163
- "main": []
164
- }
165
- }
166
-
167
- # Build the AI block
168
- ai_block = {
169
- "ai": {
170
- "prompt": {}
171
- }
172
- }
173
-
174
- # Set prompt based on type
175
- if prompt_is_pom:
176
- ai_block["ai"]["prompt"]["pom"] = prompt
177
- else:
178
- ai_block["ai"]["prompt"]["text"] = prompt
179
-
180
- # Add post_prompt if provided
181
- if post_prompt:
182
- ai_block["ai"]["post_prompt"] = {
183
- "text": post_prompt
184
- }
185
-
186
- # Add post_prompt_url if provided
187
- if post_prompt_url:
188
- ai_block["ai"]["post_prompt_url"] = post_prompt_url
189
-
190
- # SWAIG is a dictionary not an array (fix from old implementation)
191
- ai_block["ai"]["SWAIG"] = {}
192
-
69
+ # Use the service to build the document
70
+ builder = SWMLBuilder(service)
71
+
72
+ # Reset the document to start fresh
73
+ builder.reset()
74
+
75
+ # Add answer block if requested
76
+ if add_answer:
77
+ builder.answer()
78
+
79
+ # Add record_call if requested
80
+ if record_call:
81
+ service.add_verb("record_call", {
82
+ "format": record_format,
83
+ "stereo": record_stereo
84
+ })
85
+
86
+ # Configure SWAIG object for AI verb
87
+ swaig_config = {}
88
+ functions = []
89
+
90
+ # Add startup hook if provided
91
+ if startup_hook_url:
92
+ functions.append({
93
+ "function": "startup_hook",
94
+ "description": "Called when the call starts",
95
+ "parameters": {
96
+ "type": "object",
97
+ "properties": {}
98
+ },
99
+ "web_hook_url": startup_hook_url
100
+ })
101
+
102
+ # Add hangup hook if provided
103
+ if hangup_hook_url:
104
+ functions.append({
105
+ "function": "hangup_hook",
106
+ "description": "Called when the call ends",
107
+ "parameters": {
108
+ "type": "object",
109
+ "properties": {}
110
+ },
111
+ "web_hook_url": hangup_hook_url
112
+ })
113
+
114
+ # Add regular functions if provided
115
+ if swaig_functions:
116
+ for func in swaig_functions:
117
+ # Skip special hooks as we've already added them
118
+ if func.get("function") not in ["startup_hook", "hangup_hook"]:
119
+ functions.append(func)
120
+
121
+ # Only add SWAIG if we have functions or a default URL
122
+ if functions or default_webhook_url:
193
123
  # Add defaults if we have a default webhook URL
194
124
  if default_webhook_url:
195
- ai_block["ai"]["SWAIG"]["defaults"] = {
125
+ swaig_config["defaults"] = {
196
126
  "web_hook_url": default_webhook_url
197
127
  }
198
128
 
199
- # Collect all functions
200
- functions = []
201
-
202
- # Add SWAIG hooks if provided
203
- if startup_hook_url:
204
- startup_hook = {
205
- "function": "startup_hook",
206
- "description": "Called when the call starts",
207
- "parameters": {
208
- "type": "object",
209
- "properties": {}
210
- },
211
- "web_hook_url": startup_hook_url
212
- }
213
- functions.append(startup_hook)
214
-
215
- if hangup_hook_url:
216
- hangup_hook = {
217
- "function": "hangup_hook",
218
- "description": "Called when the call ends",
219
- "parameters": {
220
- "type": "object",
221
- "properties": {}
222
- },
223
- "web_hook_url": hangup_hook_url
224
- }
225
- functions.append(hangup_hook)
226
-
227
- # Add regular functions from the provided list
228
- if swaig_functions:
229
- for func in swaig_functions:
230
- # Skip special hooks as we've already added them
231
- if func.get("function") not in ["startup_hook", "hangup_hook"]:
232
- functions.append(func)
233
-
234
- # Add functions to SWAIG if we have any
129
+ # Add functions if we have any
235
130
  if functions:
236
- ai_block["ai"]["SWAIG"]["functions"] = functions
237
-
238
- # Add AI params if provided (but not rendering settings)
239
- if params:
240
- # Filter out non-AI parameters that should be separate SWML methods
241
- ai_params = {k: v for k, v in params.items()
242
- if k not in ["auto_answer", "record_call", "record_format", "record_stereo"]}
243
-
244
- # Only update if we have valid AI parameters
245
- if ai_params:
246
- ai_block["ai"]["params"] = ai_params
247
-
248
- # Start building the SWML blocks
249
- main_blocks = []
250
-
251
- # Add answer block first if requested (to answer the call)
252
- if add_answer:
253
- main_blocks.append({"answer": {}})
254
-
255
- # Add record_call block next if requested
256
- if record_call:
257
- main_blocks.append({
258
- "record_call": {
259
- "format": record_format,
260
- "stereo": record_stereo # SWML expects a boolean not a string
261
- }
262
- })
263
-
264
- # Add the AI block
265
- main_blocks.append(ai_block)
266
-
267
- # Set the main section to our ordered blocks
268
- swml["sections"]["main"] = main_blocks
269
-
270
- # Return in requested format
271
- if format.lower() == "yaml":
272
- import yaml
273
- return yaml.dump(swml, sort_keys=False)
274
- else:
275
- return json.dumps(swml, indent=2)
131
+ swaig_config["functions"] = functions
132
+
133
+ # Add AI verb with appropriate configuration
134
+ builder.ai(
135
+ prompt_text=None if prompt_is_pom else prompt,
136
+ prompt_pom=prompt if prompt_is_pom else None,
137
+ post_prompt=post_prompt,
138
+ post_prompt_url=post_prompt_url,
139
+ swaig=swaig_config if swaig_config else None,
140
+ **(params or {})
141
+ )
142
+
143
+ # Get the document as a dictionary or string based on format
144
+ if format.lower() == "yaml":
145
+ import yaml
146
+ return yaml.dump(builder.build(), sort_keys=False)
147
+ else:
148
+ return builder.render()
276
149
 
277
150
  @staticmethod
278
151
  def render_function_response_swml(
279
152
  response_text: str,
153
+ service: SWMLService,
280
154
  actions: Optional[List[Dict[str, Any]]] = None,
281
- format: str = "json",
282
- service: Optional[SWMLService] = None
155
+ format: str = "json"
283
156
  ) -> str:
284
157
  """
285
158
  Generate a SWML document for a function response
286
159
 
287
160
  Args:
288
- response_text: Text to say/display
289
- actions: List of SWML actions to execute
290
- format: Output format, 'json' or 'yaml'
291
- service: Optional SWMLService instance to use
161
+ response_text: Text response to include in the document
162
+ service: SWMLService instance to use
163
+ actions: Optional list of actions to perform
164
+ format: Output format (json or yaml)
292
165
 
293
166
  Returns:
294
167
  SWML document as a string
295
168
  """
296
- if service:
297
- # Use the service to build the document
298
- service.reset_document()
299
-
300
- # Add a play block for the response if provided
301
- if response_text:
302
- service.add_verb("play", {
303
- "url": f"say:{response_text}"
304
- })
305
-
306
- # Add any actions
307
- if actions:
308
- for action in actions:
309
- # Support both type-based actions and direct SWML verbs
310
- if "type" in action:
311
- # Type-based action format
312
- if action["type"] == "play":
313
- service.add_verb("play", {
314
- "url": action["url"]
315
- })
316
- elif action["type"] == "transfer":
317
- service.add_verb("connect", [
318
- {"to": action["dest"]}
319
- ])
320
- elif action["type"] == "hang_up":
321
- service.add_verb("hangup", {})
322
- # Additional action types could be added here
323
- else:
324
- # Direct SWML verb format
325
- for verb_name, verb_config in action.items():
326
- service.add_verb(verb_name, verb_config)
327
-
328
- # Return in requested format
329
- if format.lower() == "yaml":
330
- import yaml
331
- return yaml.dump(service.get_document(), sort_keys=False)
332
- else:
333
- return service.render_document()
169
+ # Use the service to build the document
170
+ service.reset_document()
171
+
172
+ # Add a play block for the response if provided
173
+ if response_text:
174
+ service.add_verb("play", {"text": response_text})
175
+
176
+ # Add any actions that were provided
177
+ if actions:
178
+ for action in actions:
179
+ if "play" in action:
180
+ service.add_verb("play", action["play"])
181
+ elif "hangup" in action:
182
+ service.add_verb("hangup", action["hangup"])
183
+ elif "transfer" in action:
184
+ service.add_verb("transfer", action["transfer"])
185
+ elif "ai" in action:
186
+ service.add_verb("ai", action["ai"])
187
+ # Add more action types as needed
188
+
189
+ # Get the document as a dictionary or string based on format
190
+ if format.lower() == "yaml":
191
+ import yaml
192
+ return yaml.dump(service.get_document(), sort_keys=False)
334
193
  else:
335
- # Legacy implementation (unchanged for backward compatibility)
336
- swml = {
337
- "version": "1.0.0",
338
- "sections": {
339
- "main": []
340
- }
341
- }
342
-
343
- # Add a play block for the response if provided
344
- if response_text:
345
- swml["sections"]["main"].append({
346
- "play": {
347
- "url": f"say:{response_text}"
348
- }
349
- })
350
-
351
- # Add any actions
352
- if actions:
353
- for action in actions:
354
- # Support both type-based actions and direct SWML verbs
355
- if "type" in action:
356
- # Type-based action format
357
- if action["type"] == "play":
358
- swml["sections"]["main"].append({
359
- "play": {
360
- "url": action["url"]
361
- }
362
- })
363
- elif action["type"] == "transfer":
364
- swml["sections"]["main"].append({
365
- "connect": [
366
- {"to": action["dest"]}
367
- ]
368
- })
369
- elif action["type"] == "hang_up":
370
- swml["sections"]["main"].append({
371
- "hangup": {}
372
- })
373
- # Additional action types could be added here
374
- else:
375
- # Direct SWML verb format - add the action as-is
376
- swml["sections"]["main"].append(action)
377
-
378
- # Return in requested format
379
- if format.lower() == "yaml":
380
- import yaml
381
- return yaml.dump(swml, sort_keys=False)
382
- else:
383
- return json.dumps(swml)
194
+ return service.render_document()