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.
- signalwire_agents/__init__.py +99 -15
- signalwire_agents/agent_server.py +248 -60
- signalwire_agents/agents/bedrock.py +296 -0
- signalwire_agents/cli/__init__.py +9 -0
- signalwire_agents/cli/build_search.py +951 -41
- signalwire_agents/cli/config.py +80 -0
- signalwire_agents/cli/core/__init__.py +10 -0
- signalwire_agents/cli/core/agent_loader.py +470 -0
- signalwire_agents/cli/core/argparse_helpers.py +179 -0
- signalwire_agents/cli/core/dynamic_config.py +71 -0
- signalwire_agents/cli/core/service_loader.py +303 -0
- signalwire_agents/cli/dokku.py +2320 -0
- signalwire_agents/cli/execution/__init__.py +10 -0
- signalwire_agents/cli/execution/datamap_exec.py +446 -0
- signalwire_agents/cli/execution/webhook_exec.py +134 -0
- signalwire_agents/cli/init_project.py +2636 -0
- signalwire_agents/cli/output/__init__.py +10 -0
- signalwire_agents/cli/output/output_formatter.py +255 -0
- signalwire_agents/cli/output/swml_dump.py +186 -0
- signalwire_agents/cli/simulation/__init__.py +10 -0
- signalwire_agents/cli/simulation/data_generation.py +374 -0
- signalwire_agents/cli/simulation/data_overrides.py +200 -0
- signalwire_agents/cli/simulation/mock_env.py +282 -0
- signalwire_agents/cli/swaig_test_wrapper.py +52 -0
- signalwire_agents/cli/test_swaig.py +566 -2366
- signalwire_agents/cli/types.py +81 -0
- signalwire_agents/core/__init__.py +2 -2
- signalwire_agents/core/agent/__init__.py +12 -0
- signalwire_agents/core/agent/config/__init__.py +12 -0
- signalwire_agents/core/agent/deployment/__init__.py +9 -0
- signalwire_agents/core/agent/deployment/handlers/__init__.py +9 -0
- signalwire_agents/core/agent/prompt/__init__.py +14 -0
- signalwire_agents/core/agent/prompt/manager.py +306 -0
- signalwire_agents/core/agent/routing/__init__.py +9 -0
- signalwire_agents/core/agent/security/__init__.py +9 -0
- signalwire_agents/core/agent/swml/__init__.py +9 -0
- signalwire_agents/core/agent/tools/__init__.py +15 -0
- signalwire_agents/core/agent/tools/decorator.py +97 -0
- signalwire_agents/core/agent/tools/registry.py +210 -0
- signalwire_agents/core/agent_base.py +845 -2916
- signalwire_agents/core/auth_handler.py +233 -0
- signalwire_agents/core/config_loader.py +259 -0
- signalwire_agents/core/contexts.py +418 -0
- signalwire_agents/core/data_map.py +3 -15
- signalwire_agents/core/function_result.py +116 -44
- signalwire_agents/core/logging_config.py +162 -18
- signalwire_agents/core/mixins/__init__.py +28 -0
- signalwire_agents/core/mixins/ai_config_mixin.py +442 -0
- signalwire_agents/core/mixins/auth_mixin.py +280 -0
- signalwire_agents/core/mixins/prompt_mixin.py +358 -0
- signalwire_agents/core/mixins/serverless_mixin.py +460 -0
- signalwire_agents/core/mixins/skill_mixin.py +55 -0
- signalwire_agents/core/mixins/state_mixin.py +153 -0
- signalwire_agents/core/mixins/tool_mixin.py +230 -0
- signalwire_agents/core/mixins/web_mixin.py +1142 -0
- signalwire_agents/core/security_config.py +333 -0
- signalwire_agents/core/skill_base.py +84 -1
- signalwire_agents/core/skill_manager.py +62 -20
- signalwire_agents/core/swaig_function.py +18 -5
- signalwire_agents/core/swml_builder.py +207 -11
- signalwire_agents/core/swml_handler.py +27 -21
- signalwire_agents/core/swml_renderer.py +123 -312
- signalwire_agents/core/swml_service.py +171 -203
- signalwire_agents/mcp_gateway/__init__.py +29 -0
- signalwire_agents/mcp_gateway/gateway_service.py +564 -0
- signalwire_agents/mcp_gateway/mcp_manager.py +513 -0
- signalwire_agents/mcp_gateway/session_manager.py +218 -0
- signalwire_agents/prefabs/concierge.py +0 -3
- signalwire_agents/prefabs/faq_bot.py +0 -3
- signalwire_agents/prefabs/info_gatherer.py +0 -3
- signalwire_agents/prefabs/receptionist.py +0 -3
- signalwire_agents/prefabs/survey.py +0 -3
- signalwire_agents/schema.json +9218 -5489
- signalwire_agents/search/__init__.py +7 -1
- signalwire_agents/search/document_processor.py +490 -31
- signalwire_agents/search/index_builder.py +307 -37
- signalwire_agents/search/migration.py +418 -0
- signalwire_agents/search/models.py +30 -0
- signalwire_agents/search/pgvector_backend.py +748 -0
- signalwire_agents/search/query_processor.py +162 -31
- signalwire_agents/search/search_engine.py +916 -35
- signalwire_agents/search/search_service.py +376 -53
- signalwire_agents/skills/README.md +452 -0
- signalwire_agents/skills/__init__.py +14 -2
- signalwire_agents/skills/api_ninjas_trivia/README.md +215 -0
- signalwire_agents/skills/api_ninjas_trivia/__init__.py +12 -0
- signalwire_agents/skills/api_ninjas_trivia/skill.py +237 -0
- signalwire_agents/skills/datasphere/README.md +210 -0
- signalwire_agents/skills/datasphere/skill.py +84 -3
- signalwire_agents/skills/datasphere_serverless/README.md +258 -0
- signalwire_agents/skills/datasphere_serverless/__init__.py +9 -0
- signalwire_agents/skills/datasphere_serverless/skill.py +82 -1
- signalwire_agents/skills/datetime/README.md +132 -0
- signalwire_agents/skills/datetime/__init__.py +9 -0
- signalwire_agents/skills/datetime/skill.py +20 -7
- signalwire_agents/skills/joke/README.md +149 -0
- signalwire_agents/skills/joke/__init__.py +9 -0
- signalwire_agents/skills/joke/skill.py +21 -0
- signalwire_agents/skills/math/README.md +161 -0
- signalwire_agents/skills/math/__init__.py +9 -0
- signalwire_agents/skills/math/skill.py +18 -4
- signalwire_agents/skills/mcp_gateway/README.md +230 -0
- signalwire_agents/skills/mcp_gateway/__init__.py +10 -0
- signalwire_agents/skills/mcp_gateway/skill.py +421 -0
- signalwire_agents/skills/native_vector_search/README.md +210 -0
- signalwire_agents/skills/native_vector_search/__init__.py +9 -0
- signalwire_agents/skills/native_vector_search/skill.py +569 -101
- signalwire_agents/skills/play_background_file/README.md +218 -0
- signalwire_agents/skills/play_background_file/__init__.py +12 -0
- signalwire_agents/skills/play_background_file/skill.py +242 -0
- signalwire_agents/skills/registry.py +395 -40
- signalwire_agents/skills/spider/README.md +236 -0
- signalwire_agents/skills/spider/__init__.py +13 -0
- signalwire_agents/skills/spider/skill.py +598 -0
- signalwire_agents/skills/swml_transfer/README.md +395 -0
- signalwire_agents/skills/swml_transfer/__init__.py +10 -0
- signalwire_agents/skills/swml_transfer/skill.py +359 -0
- signalwire_agents/skills/weather_api/README.md +178 -0
- signalwire_agents/skills/weather_api/__init__.py +12 -0
- signalwire_agents/skills/weather_api/skill.py +191 -0
- signalwire_agents/skills/web_search/README.md +163 -0
- signalwire_agents/skills/web_search/__init__.py +9 -0
- signalwire_agents/skills/web_search/skill.py +586 -112
- signalwire_agents/skills/wikipedia_search/README.md +228 -0
- signalwire_agents/{core/state → skills/wikipedia_search}/__init__.py +5 -4
- signalwire_agents/skills/{wikipedia → wikipedia_search}/skill.py +33 -3
- signalwire_agents/web/__init__.py +17 -0
- signalwire_agents/web/web_service.py +559 -0
- signalwire_agents-1.0.17.dev4.data/data/share/man/man1/sw-agent-init.1 +400 -0
- signalwire_agents-1.0.17.dev4.data/data/share/man/man1/sw-search.1 +483 -0
- signalwire_agents-1.0.17.dev4.data/data/share/man/man1/swaig-test.1 +308 -0
- {signalwire_agents-0.1.13.dist-info → signalwire_agents-1.0.17.dev4.dist-info}/METADATA +347 -215
- signalwire_agents-1.0.17.dev4.dist-info/RECORD +147 -0
- signalwire_agents-1.0.17.dev4.dist-info/entry_points.txt +6 -0
- signalwire_agents/core/state/file_state_manager.py +0 -219
- signalwire_agents/core/state/state_manager.py +0 -101
- signalwire_agents/skills/wikipedia/__init__.py +0 -9
- signalwire_agents-0.1.13.data/data/schema.json +0 -5611
- signalwire_agents-0.1.13.dist-info/RECORD +0 -67
- signalwire_agents-0.1.13.dist-info/entry_points.txt +0 -3
- {signalwire_agents-0.1.13.dist-info → signalwire_agents-1.0.17.dev4.dist-info}/WHEEL +0 -0
- {signalwire_agents-0.1.13.dist-info → signalwire_agents-1.0.17.dev4.dist-info}/licenses/LICENSE +0 -0
- {signalwire_agents-0.1.13.dist-info → signalwire_agents-1.0.17.dev4.dist-info}/top_level.txt +0 -0
|
@@ -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
|