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
@@ -0,0 +1,10 @@
1
+ """
2
+ Copyright (c) 2025 SignalWire
3
+
4
+ This file is part of the SignalWire AI Agents SDK.
5
+
6
+ Licensed under the MIT License.
7
+ See LICENSE file in the project root for full license information.
8
+ """
9
+
10
+ """Output formatting and display modules"""
@@ -0,0 +1,255 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Copyright (c) 2025 SignalWire
4
+
5
+ This file is part of the SignalWire AI Agents SDK.
6
+
7
+ Licensed under the MIT License.
8
+ See LICENSE file in the project root for full license information.
9
+ """
10
+
11
+ """
12
+ Display agent/tools and format results
13
+ """
14
+
15
+ import json
16
+ from typing import Any, TYPE_CHECKING
17
+
18
+ if TYPE_CHECKING:
19
+ from signalwire_agents.core.agent_base import AgentBase
20
+ from signalwire_agents.core.function_result import SwaigFunctionResult
21
+
22
+
23
+ def display_agent_tools(agent: 'AgentBase', verbose: bool = False) -> None:
24
+ """
25
+ Display the available SWAIG functions for an agent
26
+
27
+ Args:
28
+ agent: The agent instance
29
+ verbose: Whether to show verbose details
30
+ """
31
+ print("\nAvailable SWAIG functions:")
32
+ # Try to access functions from the tool registry
33
+ functions = {}
34
+ if hasattr(agent, '_tool_registry') and hasattr(agent._tool_registry, '_swaig_functions'):
35
+ functions = agent._tool_registry._swaig_functions
36
+ elif hasattr(agent, '_swaig_functions'):
37
+ functions = agent._swaig_functions
38
+
39
+ if functions:
40
+ for name, func in functions.items():
41
+ if isinstance(func, dict):
42
+ # DataMap function
43
+ description = func.get('description', 'DataMap function (serverless)')
44
+ print(f" {name} - {description}")
45
+
46
+ # Show parameters for DataMap functions
47
+ if 'parameters' in func and func['parameters']:
48
+ params = func['parameters']
49
+ # Handle both formats: direct properties dict or full schema
50
+ if 'properties' in params:
51
+ properties = params['properties']
52
+ required_fields = params.get('required', [])
53
+ else:
54
+ properties = params
55
+ required_fields = []
56
+
57
+ if properties:
58
+ print(f" Parameters:")
59
+ for param_name, param_def in properties.items():
60
+ param_type = param_def.get('type', 'unknown')
61
+ param_desc = param_def.get('description', 'No description')
62
+ is_required = param_name in required_fields
63
+ required_marker = " (required)" if is_required else ""
64
+
65
+ # Build constraint details
66
+ constraints = []
67
+
68
+ # Enum values
69
+ if 'enum' in param_def:
70
+ constraints.append(f"options: {', '.join(map(str, param_def['enum']))}")
71
+
72
+ # Numeric constraints
73
+ if 'minimum' in param_def:
74
+ constraints.append(f"min: {param_def['minimum']}")
75
+ if 'maximum' in param_def:
76
+ constraints.append(f"max: {param_def['maximum']}")
77
+ if 'exclusiveMinimum' in param_def:
78
+ constraints.append(f"min (exclusive): {param_def['exclusiveMinimum']}")
79
+ if 'exclusiveMaximum' in param_def:
80
+ constraints.append(f"max (exclusive): {param_def['exclusiveMaximum']}")
81
+ if 'multipleOf' in param_def:
82
+ constraints.append(f"multiple of: {param_def['multipleOf']}")
83
+
84
+ # String constraints
85
+ if 'minLength' in param_def:
86
+ constraints.append(f"min length: {param_def['minLength']}")
87
+ if 'maxLength' in param_def:
88
+ constraints.append(f"max length: {param_def['maxLength']}")
89
+ if 'pattern' in param_def:
90
+ constraints.append(f"pattern: {param_def['pattern']}")
91
+ if 'format' in param_def:
92
+ constraints.append(f"format: {param_def['format']}")
93
+
94
+ # Array constraints
95
+ if param_type == 'array':
96
+ if 'minItems' in param_def:
97
+ constraints.append(f"min items: {param_def['minItems']}")
98
+ if 'maxItems' in param_def:
99
+ constraints.append(f"max items: {param_def['maxItems']}")
100
+ if 'uniqueItems' in param_def and param_def['uniqueItems']:
101
+ constraints.append("unique items")
102
+ if 'items' in param_def and 'type' in param_def['items']:
103
+ constraints.append(f"item type: {param_def['items']['type']}")
104
+
105
+ # Default value
106
+ if 'default' in param_def:
107
+ constraints.append(f"default: {param_def['default']}")
108
+
109
+ # Format the type with constraints
110
+ if constraints:
111
+ param_type_full = f"{param_type} [{', '.join(constraints)}]"
112
+ else:
113
+ param_type_full = param_type
114
+
115
+ print(f" {param_name} ({param_type_full}){required_marker}: {param_desc}")
116
+ else:
117
+ print(f" Parameters: None")
118
+ else:
119
+ print(f" Parameters: None")
120
+
121
+ if verbose:
122
+ print(f" Config: {json.dumps(func, indent=6)}")
123
+ else:
124
+ # Regular SWAIG function
125
+ func_type = ""
126
+ if hasattr(func, 'webhook_url') and func.webhook_url and func.is_external:
127
+ func_type = " (EXTERNAL webhook)"
128
+ elif hasattr(func, 'webhook_url') and func.webhook_url:
129
+ func_type = " (webhook)"
130
+ else:
131
+ func_type = " (LOCAL webhook)"
132
+
133
+ print(f" {name} - {func.description}{func_type}")
134
+
135
+ # Show external URL if applicable
136
+ if hasattr(func, 'webhook_url') and func.webhook_url and func.is_external:
137
+ print(f" External URL: {func.webhook_url}")
138
+
139
+ # Show parameters
140
+ if hasattr(func, 'parameters') and func.parameters:
141
+ params = func.parameters
142
+ # Handle both formats: direct properties dict or full schema
143
+ if 'properties' in params:
144
+ properties = params['properties']
145
+ required_fields = params.get('required', [])
146
+ else:
147
+ properties = params
148
+ required_fields = []
149
+
150
+ if properties:
151
+ print(f" Parameters:")
152
+ for param_name, param_def in properties.items():
153
+ param_type = param_def.get('type', 'unknown')
154
+ param_desc = param_def.get('description', 'No description')
155
+ is_required = param_name in required_fields
156
+ required_marker = " (required)" if is_required else ""
157
+
158
+ # Build constraint details
159
+ constraints = []
160
+
161
+ # Enum values
162
+ if 'enum' in param_def:
163
+ constraints.append(f"options: {', '.join(map(str, param_def['enum']))}")
164
+
165
+ # Numeric constraints
166
+ if 'minimum' in param_def:
167
+ constraints.append(f"min: {param_def['minimum']}")
168
+ if 'maximum' in param_def:
169
+ constraints.append(f"max: {param_def['maximum']}")
170
+ if 'exclusiveMinimum' in param_def:
171
+ constraints.append(f"min (exclusive): {param_def['exclusiveMinimum']}")
172
+ if 'exclusiveMaximum' in param_def:
173
+ constraints.append(f"max (exclusive): {param_def['exclusiveMaximum']}")
174
+ if 'multipleOf' in param_def:
175
+ constraints.append(f"multiple of: {param_def['multipleOf']}")
176
+
177
+ # String constraints
178
+ if 'minLength' in param_def:
179
+ constraints.append(f"min length: {param_def['minLength']}")
180
+ if 'maxLength' in param_def:
181
+ constraints.append(f"max length: {param_def['maxLength']}")
182
+ if 'pattern' in param_def:
183
+ constraints.append(f"pattern: {param_def['pattern']}")
184
+ if 'format' in param_def:
185
+ constraints.append(f"format: {param_def['format']}")
186
+
187
+ # Array constraints
188
+ if param_type == 'array':
189
+ if 'minItems' in param_def:
190
+ constraints.append(f"min items: {param_def['minItems']}")
191
+ if 'maxItems' in param_def:
192
+ constraints.append(f"max items: {param_def['maxItems']}")
193
+ if 'uniqueItems' in param_def and param_def['uniqueItems']:
194
+ constraints.append("unique items")
195
+ if 'items' in param_def and 'type' in param_def['items']:
196
+ constraints.append(f"item type: {param_def['items']['type']}")
197
+
198
+ # Default value
199
+ if 'default' in param_def:
200
+ constraints.append(f"default: {param_def['default']}")
201
+
202
+ # Format the type with constraints
203
+ if constraints:
204
+ param_type_full = f"{param_type} [{', '.join(constraints)}]"
205
+ else:
206
+ param_type_full = param_type
207
+
208
+ print(f" {param_name} ({param_type_full}){required_marker}: {param_desc}")
209
+ else:
210
+ print(f" Parameters: None")
211
+ else:
212
+ print(f" Parameters: None")
213
+
214
+ if verbose:
215
+ print(f" Function object: {func}")
216
+ else:
217
+ print(" No SWAIG functions registered")
218
+
219
+
220
+ def format_result(result: Any) -> str:
221
+ """
222
+ Format the result of a SWAIG function call for display
223
+
224
+ Args:
225
+ result: The result from the SWAIG function
226
+
227
+ Returns:
228
+ Formatted string representation
229
+ """
230
+ # Import here to avoid circular imports
231
+ from signalwire_agents.core.function_result import SwaigFunctionResult
232
+
233
+ if isinstance(result, SwaigFunctionResult):
234
+ output = [f"SwaigFunctionResult: {result.response}"]
235
+
236
+ # Show actions if present
237
+ if hasattr(result, 'action') and result.action:
238
+ output.append("\nActions:")
239
+ for action in result.action:
240
+ output.append(json.dumps(action, indent=2))
241
+
242
+ # Show post_process flag if set
243
+ if hasattr(result, 'post_process') and result.post_process:
244
+ output.append(f"\nPost-process: {result.post_process}")
245
+
246
+ return "\n".join(output)
247
+ elif isinstance(result, dict):
248
+ if 'response' in result:
249
+ return f"Response: {result['response']}"
250
+ else:
251
+ return f"Dict: {json.dumps(result, indent=2)}"
252
+ elif isinstance(result, str):
253
+ return f"String: {result}"
254
+ else:
255
+ return f"Other ({type(result).__name__}): {result}"
@@ -0,0 +1,186 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Copyright (c) 2025 SignalWire
4
+
5
+ This file is part of the SignalWire AI Agents SDK.
6
+
7
+ Licensed under the MIT License.
8
+ See LICENSE file in the project root for full license information.
9
+ """
10
+
11
+ """
12
+ Handle SWML document dumping
13
+ """
14
+
15
+ import sys
16
+ import json
17
+ import warnings
18
+ import argparse
19
+ from typing import TYPE_CHECKING
20
+ from ..simulation.data_generation import generate_fake_swml_post_data
21
+ from ..simulation.data_overrides import apply_convenience_mappings, apply_overrides
22
+ from ..simulation.mock_env import create_mock_request
23
+ from ..core.dynamic_config import apply_dynamic_config
24
+
25
+ if TYPE_CHECKING:
26
+ from signalwire_agents.core.agent_base import AgentBase
27
+
28
+
29
+ # Store the original print function for raw mode
30
+ original_print = print
31
+
32
+
33
+ def setup_output_suppression():
34
+ """Set up output suppression for SWML dumping"""
35
+ # The central logging system is already configured via environment variable
36
+ # Just suppress any remaining warnings
37
+ warnings.filterwarnings("ignore")
38
+
39
+ # Capture and suppress print statements
40
+ def suppressed_print(*args, **kwargs):
41
+ # If file is specified (like stderr), allow it
42
+ if 'file' in kwargs and kwargs['file'] is not sys.stdout:
43
+ original_print(*args, **kwargs)
44
+ else:
45
+ # Suppress stdout prints
46
+ pass
47
+
48
+ # Replace print function globally
49
+ import builtins
50
+ builtins.print = suppressed_print
51
+
52
+
53
+ def handle_dump_swml(agent: 'AgentBase', args: argparse.Namespace) -> int:
54
+ """
55
+ Handle SWML dumping with fake post_data and mock request support
56
+
57
+ Args:
58
+ agent: The loaded agent instance
59
+ args: Parsed CLI arguments
60
+
61
+ Returns:
62
+ Exit code (0 for success, 1 for error)
63
+ """
64
+ if not args.raw:
65
+ if args.verbose:
66
+ print(f"Agent: {agent.get_name()}")
67
+ print(f"Route: {agent.route}")
68
+
69
+ # Show loaded skills
70
+ skills = agent.list_skills()
71
+ if skills:
72
+ print(f"Skills: {', '.join(skills)}")
73
+
74
+ # Show available functions
75
+ functions = agent._tool_registry.get_all_functions() if hasattr(agent, '_tool_registry') else {}
76
+ if functions:
77
+ print(f"Functions: {', '.join(functions.keys())}")
78
+
79
+ print("-" * 60)
80
+
81
+ try:
82
+ # Generate fake SWML post_data
83
+ post_data = generate_fake_swml_post_data(
84
+ call_type=args.call_type,
85
+ call_direction=args.call_direction,
86
+ call_state=args.call_state
87
+ )
88
+
89
+ # Apply convenience mappings from CLI args
90
+ post_data = apply_convenience_mappings(post_data, args)
91
+
92
+ # Apply explicit overrides
93
+ post_data = apply_overrides(post_data, args.override, args.override_json)
94
+
95
+ # Parse headers for mock request
96
+ headers = {}
97
+ for header in args.header:
98
+ if '=' in header:
99
+ key, value = header.split('=', 1)
100
+ headers[key] = value
101
+
102
+ # Parse query params for mock request (separate from userVariables)
103
+ query_params = {}
104
+ if args.query_params:
105
+ try:
106
+ query_params = json.loads(args.query_params)
107
+ except json.JSONDecodeError as e:
108
+ if not args.raw:
109
+ print(f"Warning: Invalid JSON in --query-params: {e}")
110
+
111
+ # Parse request body
112
+ request_body = {}
113
+ if args.body:
114
+ try:
115
+ request_body = json.loads(args.body)
116
+ except json.JSONDecodeError as e:
117
+ if not args.raw:
118
+ print(f"Warning: Invalid JSON in --body: {e}")
119
+
120
+ # Create mock request object
121
+ mock_request = create_mock_request(
122
+ method=args.method,
123
+ headers=headers,
124
+ query_params=query_params,
125
+ body=request_body
126
+ )
127
+
128
+ if args.verbose and not args.raw:
129
+ print(f"Using fake SWML post_data:")
130
+ print(json.dumps(post_data, indent=2))
131
+ print(f"\nMock request headers: {dict(mock_request.headers.items())}")
132
+ print(f"Mock request query params: {dict(mock_request.query_params.items())}")
133
+ print(f"Mock request method: {mock_request.method}")
134
+ print("-" * 60)
135
+
136
+ # Apply dynamic configuration with the mock request
137
+ apply_dynamic_config(agent, mock_request, verbose=args.verbose and not args.raw)
138
+
139
+ # For dynamic agents, call on_swml_request if available
140
+ if hasattr(agent, 'on_swml_request'):
141
+ try:
142
+ # Dynamic agents expect (request_data, callback_path, request)
143
+ call_id = post_data.get('call', {}).get('call_id', 'test-call-id')
144
+ modifications = agent.on_swml_request(post_data, "/swml", mock_request)
145
+
146
+ if args.verbose and not args.raw:
147
+ print(f"Dynamic agent modifications: {modifications}")
148
+
149
+ # Generate SWML with modifications
150
+ swml_doc = agent._render_swml(call_id, modifications)
151
+ except Exception as e:
152
+ if args.verbose and not args.raw:
153
+ print(f"Dynamic agent callback failed, falling back to static SWML: {e}")
154
+ # Fall back to static SWML generation
155
+ swml_doc = agent._render_swml()
156
+ else:
157
+ # Static agent - generate SWML normally
158
+ swml_doc = agent._render_swml()
159
+
160
+ if args.raw:
161
+ # Output only the raw JSON for piping to jq/yq
162
+ original_print(swml_doc)
163
+ else:
164
+ # Output formatted JSON (like raw but pretty-printed)
165
+ try:
166
+ swml_parsed = json.loads(swml_doc)
167
+ original_print(json.dumps(swml_parsed, indent=2))
168
+ except json.JSONDecodeError:
169
+ # If not valid JSON, show raw
170
+ original_print(swml_doc)
171
+
172
+ return 0
173
+
174
+ except Exception as e:
175
+ if args.raw:
176
+ # For raw mode, output error to stderr to not interfere with JSON output
177
+ original_print(f"Error generating SWML: {e}", file=sys.stderr)
178
+ if args.verbose:
179
+ import traceback
180
+ traceback.print_exc(file=sys.stderr)
181
+ else:
182
+ print(f"Error generating SWML: {e}")
183
+ if args.verbose:
184
+ import traceback
185
+ traceback.print_exc()
186
+ return 1
@@ -0,0 +1,10 @@
1
+ """
2
+ Copyright (c) 2025 SignalWire
3
+
4
+ This file is part of the SignalWire AI Agents SDK.
5
+
6
+ Licensed under the MIT License.
7
+ See LICENSE file in the project root for full license information.
8
+ """
9
+
10
+ """Simulation and mock environment modules"""