signalwire-agents 0.1.6__py3-none-any.whl → 1.0.7__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 +130 -4
- signalwire_agents/agent_server.py +438 -32
- signalwire_agents/agents/bedrock.py +296 -0
- signalwire_agents/cli/__init__.py +18 -0
- signalwire_agents/cli/build_search.py +1367 -0
- 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/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 +1225 -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 +809 -0
- 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 +959 -2166
- signalwire_agents/core/auth_handler.py +233 -0
- signalwire_agents/core/config_loader.py +259 -0
- signalwire_agents/core/contexts.py +707 -0
- signalwire_agents/core/data_map.py +487 -0
- signalwire_agents/core/function_result.py +1150 -1
- signalwire_agents/core/logging_config.py +376 -0
- 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 +287 -0
- signalwire_agents/core/mixins/prompt_mixin.py +358 -0
- signalwire_agents/core/mixins/serverless_mixin.py +368 -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 +1134 -0
- signalwire_agents/core/security/session_manager.py +174 -86
- signalwire_agents/core/security_config.py +333 -0
- signalwire_agents/core/skill_base.py +200 -0
- signalwire_agents/core/skill_manager.py +244 -0
- signalwire_agents/core/swaig_function.py +33 -9
- signalwire_agents/core/swml_builder.py +212 -12
- signalwire_agents/core/swml_handler.py +43 -13
- signalwire_agents/core/swml_renderer.py +123 -297
- signalwire_agents/core/swml_service.py +277 -260
- signalwire_agents/prefabs/concierge.py +6 -2
- signalwire_agents/prefabs/info_gatherer.py +149 -33
- signalwire_agents/prefabs/receptionist.py +14 -22
- signalwire_agents/prefabs/survey.py +6 -2
- signalwire_agents/schema.json +9218 -5489
- signalwire_agents/search/__init__.py +137 -0
- signalwire_agents/search/document_processor.py +1223 -0
- signalwire_agents/search/index_builder.py +804 -0
- signalwire_agents/search/migration.py +418 -0
- signalwire_agents/search/models.py +30 -0
- signalwire_agents/search/pgvector_backend.py +752 -0
- signalwire_agents/search/query_processor.py +502 -0
- signalwire_agents/search/search_engine.py +1264 -0
- signalwire_agents/search/search_service.py +574 -0
- signalwire_agents/skills/README.md +452 -0
- signalwire_agents/skills/__init__.py +23 -0
- 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/__init__.py +12 -0
- signalwire_agents/skills/datasphere/skill.py +310 -0
- signalwire_agents/skills/datasphere_serverless/README.md +258 -0
- signalwire_agents/skills/datasphere_serverless/__init__.py +10 -0
- signalwire_agents/skills/datasphere_serverless/skill.py +237 -0
- signalwire_agents/skills/datetime/README.md +132 -0
- signalwire_agents/skills/datetime/__init__.py +10 -0
- signalwire_agents/skills/datetime/skill.py +126 -0
- signalwire_agents/skills/joke/README.md +149 -0
- signalwire_agents/skills/joke/__init__.py +10 -0
- signalwire_agents/skills/joke/skill.py +109 -0
- signalwire_agents/skills/math/README.md +161 -0
- signalwire_agents/skills/math/__init__.py +10 -0
- signalwire_agents/skills/math/skill.py +105 -0
- 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 +10 -0
- signalwire_agents/skills/native_vector_search/skill.py +820 -0
- 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 +459 -0
- 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 +10 -0
- signalwire_agents/skills/web_search/skill.py +739 -0
- signalwire_agents/skills/wikipedia_search/README.md +228 -0
- signalwire_agents/{core/state → skills/wikipedia_search}/__init__.py +5 -4
- signalwire_agents/skills/wikipedia_search/skill.py +210 -0
- signalwire_agents/utils/__init__.py +14 -0
- signalwire_agents/utils/schema_utils.py +111 -44
- signalwire_agents/web/__init__.py +17 -0
- signalwire_agents/web/web_service.py +559 -0
- signalwire_agents-1.0.7.data/data/share/man/man1/sw-agent-init.1 +307 -0
- signalwire_agents-1.0.7.data/data/share/man/man1/sw-search.1 +483 -0
- signalwire_agents-1.0.7.data/data/share/man/man1/swaig-test.1 +308 -0
- signalwire_agents-1.0.7.dist-info/METADATA +992 -0
- signalwire_agents-1.0.7.dist-info/RECORD +142 -0
- {signalwire_agents-0.1.6.dist-info → signalwire_agents-1.0.7.dist-info}/WHEEL +1 -1
- signalwire_agents-1.0.7.dist-info/entry_points.txt +4 -0
- signalwire_agents/core/state/file_state_manager.py +0 -219
- signalwire_agents/core/state/state_manager.py +0 -101
- signalwire_agents-0.1.6.data/data/schema.json +0 -5611
- signalwire_agents-0.1.6.dist-info/METADATA +0 -199
- signalwire_agents-0.1.6.dist-info/RECORD +0 -34
- {signalwire_agents-0.1.6.dist-info → signalwire_agents-1.0.7.dist-info}/licenses/LICENSE +0 -0
- {signalwire_agents-0.1.6.dist-info → signalwire_agents-1.0.7.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,809 @@
|
|
|
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
|
+
SWAIG Function CLI Testing Tool
|
|
13
|
+
|
|
14
|
+
This tool loads an agent application and calls SWAIG functions with comprehensive
|
|
15
|
+
simulation of the SignalWire environment. It supports both webhook and DataMap functions.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
# CRITICAL: Set environment variable BEFORE any imports to suppress logs for --raw and --dump-swml
|
|
19
|
+
import sys
|
|
20
|
+
import os
|
|
21
|
+
if "--raw" in sys.argv or "--dump-swml" in sys.argv:
|
|
22
|
+
os.environ['SIGNALWIRE_LOG_MODE'] = 'off'
|
|
23
|
+
|
|
24
|
+
import json
|
|
25
|
+
import argparse
|
|
26
|
+
import warnings
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from typing import Dict, Any, Optional
|
|
29
|
+
|
|
30
|
+
# Import submodules
|
|
31
|
+
from .config import (
|
|
32
|
+
ERROR_MISSING_AGENT, ERROR_MULTIPLE_AGENTS, ERROR_NO_AGENTS,
|
|
33
|
+
ERROR_AGENT_NOT_FOUND, ERROR_FUNCTION_NOT_FOUND, ERROR_CGI_HOST_REQUIRED,
|
|
34
|
+
HELP_DESCRIPTION, HELP_EPILOG_SHORT
|
|
35
|
+
)
|
|
36
|
+
from .core.argparse_helpers import CustomArgumentParser, parse_function_arguments
|
|
37
|
+
from .core.agent_loader import discover_agents_in_file, load_agent_from_file, load_service_from_file
|
|
38
|
+
from .core.dynamic_config import apply_dynamic_config
|
|
39
|
+
from .simulation.mock_env import ServerlessSimulator, create_mock_request, load_env_file
|
|
40
|
+
from .simulation.data_generation import (
|
|
41
|
+
generate_fake_swml_post_data, generate_comprehensive_post_data,
|
|
42
|
+
generate_minimal_post_data
|
|
43
|
+
)
|
|
44
|
+
from .simulation.data_overrides import apply_overrides, apply_convenience_mappings
|
|
45
|
+
from .execution.datamap_exec import execute_datamap_function
|
|
46
|
+
from .execution.webhook_exec import execute_external_webhook_function
|
|
47
|
+
from .output.swml_dump import handle_dump_swml, setup_output_suppression
|
|
48
|
+
from .output.output_formatter import display_agent_tools, format_result
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def print_help_platforms():
|
|
52
|
+
"""Print detailed help for serverless platform options"""
|
|
53
|
+
print("""
|
|
54
|
+
Serverless Platform Configuration Options
|
|
55
|
+
========================================
|
|
56
|
+
|
|
57
|
+
AWS Lambda Configuration:
|
|
58
|
+
--aws-function-name NAME AWS Lambda function name (overrides default)
|
|
59
|
+
--aws-function-url URL AWS Lambda function URL (overrides default)
|
|
60
|
+
--aws-region REGION AWS region (overrides default)
|
|
61
|
+
--aws-api-gateway-id ID AWS API Gateway ID for API Gateway URLs
|
|
62
|
+
--aws-stage STAGE AWS API Gateway stage (default: prod)
|
|
63
|
+
|
|
64
|
+
CGI Configuration:
|
|
65
|
+
--cgi-host HOST CGI server hostname (required for CGI simulation)
|
|
66
|
+
--cgi-script-name NAME CGI script name/path (overrides default)
|
|
67
|
+
--cgi-https Use HTTPS for CGI URLs
|
|
68
|
+
--cgi-path-info PATH CGI PATH_INFO value
|
|
69
|
+
|
|
70
|
+
Google Cloud Platform Configuration:
|
|
71
|
+
--gcp-project ID Google Cloud project ID (overrides default)
|
|
72
|
+
--gcp-function-url URL Google Cloud Function URL (overrides default)
|
|
73
|
+
--gcp-region REGION Google Cloud region (overrides default)
|
|
74
|
+
--gcp-service NAME Google Cloud service name (overrides default)
|
|
75
|
+
|
|
76
|
+
Azure Functions Configuration:
|
|
77
|
+
--azure-env ENV Azure Functions environment (overrides default)
|
|
78
|
+
--azure-function-url URL Azure Function URL (overrides default)
|
|
79
|
+
|
|
80
|
+
Examples:
|
|
81
|
+
# AWS Lambda with custom configuration
|
|
82
|
+
swaig-test agent.py --simulate-serverless lambda \\
|
|
83
|
+
--aws-function-name prod-agent \\
|
|
84
|
+
--aws-region us-west-2 \\
|
|
85
|
+
--dump-swml
|
|
86
|
+
|
|
87
|
+
# CGI with HTTPS
|
|
88
|
+
swaig-test agent.py --simulate-serverless cgi \\
|
|
89
|
+
--cgi-host example.com \\
|
|
90
|
+
--cgi-https \\
|
|
91
|
+
--exec my_function
|
|
92
|
+
""")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def print_help_examples():
|
|
96
|
+
"""Print comprehensive usage examples"""
|
|
97
|
+
print("""
|
|
98
|
+
Comprehensive Usage Examples
|
|
99
|
+
===========================
|
|
100
|
+
|
|
101
|
+
Basic Function Testing
|
|
102
|
+
---------------------
|
|
103
|
+
# Test a function with CLI-style arguments
|
|
104
|
+
swaig-test agent.py --exec search --query "AI" --limit 5
|
|
105
|
+
|
|
106
|
+
# Test with verbose output
|
|
107
|
+
swaig-test agent.py --verbose --exec search --query "test"
|
|
108
|
+
|
|
109
|
+
# Legacy JSON syntax (still supported)
|
|
110
|
+
swaig-test agent.py search '{"query":"test"}'
|
|
111
|
+
|
|
112
|
+
SWML Document Generation
|
|
113
|
+
-----------------------
|
|
114
|
+
# Generate basic SWML
|
|
115
|
+
swaig-test agent.py --dump-swml
|
|
116
|
+
|
|
117
|
+
# Generate SWML with raw JSON output (for piping)
|
|
118
|
+
swaig-test agent.py --dump-swml --raw | jq '.'
|
|
119
|
+
|
|
120
|
+
# Extract specific fields with jq
|
|
121
|
+
swaig-test agent.py --dump-swml --raw | jq '.sections.main[1].ai.SWAIG.functions'
|
|
122
|
+
|
|
123
|
+
# Generate SWML with comprehensive fake data
|
|
124
|
+
swaig-test agent.py --dump-swml --fake-full-data
|
|
125
|
+
|
|
126
|
+
# Customize call configuration
|
|
127
|
+
swaig-test agent.py --dump-swml --call-type sip --from-number +15551234567
|
|
128
|
+
|
|
129
|
+
Multi-Agent Files
|
|
130
|
+
----------------
|
|
131
|
+
# List available agents
|
|
132
|
+
swaig-test multi_agent.py --list-agents
|
|
133
|
+
|
|
134
|
+
# Use specific agent
|
|
135
|
+
swaig-test multi_agent.py --agent-class MattiAgent --list-tools
|
|
136
|
+
swaig-test multi_agent.py --agent-class MattiAgent --exec transfer --name sigmond
|
|
137
|
+
|
|
138
|
+
Dynamic Agent Testing
|
|
139
|
+
--------------------
|
|
140
|
+
# Test with query parameters
|
|
141
|
+
swaig-test dynamic_agent.py --dump-swml --query-params '{"tier":"premium"}'
|
|
142
|
+
|
|
143
|
+
# Test with headers
|
|
144
|
+
swaig-test dynamic_agent.py --dump-swml --header "Authorization=Bearer token"
|
|
145
|
+
|
|
146
|
+
# Test with custom request body
|
|
147
|
+
swaig-test dynamic_agent.py --dump-swml --method POST --body '{"custom":"data"}'
|
|
148
|
+
|
|
149
|
+
# Combined dynamic configuration
|
|
150
|
+
swaig-test dynamic_agent.py --dump-swml \\
|
|
151
|
+
--query-params '{"tier":"premium","region":"eu"}' \\
|
|
152
|
+
--header "X-Customer-ID=12345" \\
|
|
153
|
+
--user-vars '{"preferences":{"language":"es"}}'
|
|
154
|
+
|
|
155
|
+
Serverless Environment Simulation
|
|
156
|
+
--------------------------------
|
|
157
|
+
# AWS Lambda simulation
|
|
158
|
+
swaig-test agent.py --simulate-serverless lambda --dump-swml
|
|
159
|
+
swaig-test agent.py --simulate-serverless lambda --exec my_function --param value
|
|
160
|
+
|
|
161
|
+
# With environment variables
|
|
162
|
+
swaig-test agent.py --simulate-serverless lambda \\
|
|
163
|
+
--env API_KEY=secret \\
|
|
164
|
+
--env DEBUG=1 \\
|
|
165
|
+
--exec my_function
|
|
166
|
+
|
|
167
|
+
# With environment file
|
|
168
|
+
swaig-test agent.py --simulate-serverless lambda \\
|
|
169
|
+
--env-file production.env \\
|
|
170
|
+
--exec my_function
|
|
171
|
+
|
|
172
|
+
# CGI simulation
|
|
173
|
+
swaig-test agent.py --simulate-serverless cgi \\
|
|
174
|
+
--cgi-host example.com \\
|
|
175
|
+
--cgi-https \\
|
|
176
|
+
--exec my_function
|
|
177
|
+
|
|
178
|
+
# Google Cloud Functions
|
|
179
|
+
swaig-test agent.py --simulate-serverless cloud_function \\
|
|
180
|
+
--gcp-project my-project \\
|
|
181
|
+
--exec my_function
|
|
182
|
+
|
|
183
|
+
# Azure Functions
|
|
184
|
+
swaig-test agent.py --simulate-serverless azure_function \\
|
|
185
|
+
--azure-env production \\
|
|
186
|
+
--exec my_function
|
|
187
|
+
|
|
188
|
+
Advanced Data Overrides
|
|
189
|
+
----------------------
|
|
190
|
+
# Override specific values
|
|
191
|
+
swaig-test agent.py --dump-swml \\
|
|
192
|
+
--override call.state=answered \\
|
|
193
|
+
--override call.timeout=60
|
|
194
|
+
|
|
195
|
+
# Override with JSON values
|
|
196
|
+
swaig-test agent.py --dump-swml \\
|
|
197
|
+
--override-json vars.custom='{"key":"value","nested":{"data":true}}'
|
|
198
|
+
|
|
199
|
+
# Combine multiple override types
|
|
200
|
+
swaig-test agent.py --dump-swml \\
|
|
201
|
+
--call-type sip \\
|
|
202
|
+
--user-vars '{"vip":"true"}' \\
|
|
203
|
+
--header "X-Source=test" \\
|
|
204
|
+
--override call.project_id=my-project \\
|
|
205
|
+
--verbose
|
|
206
|
+
|
|
207
|
+
Cross-Platform Testing
|
|
208
|
+
---------------------
|
|
209
|
+
# Test across all platforms
|
|
210
|
+
for platform in lambda cgi cloud_function azure_function; do
|
|
211
|
+
echo "Testing $platform..."
|
|
212
|
+
swaig-test agent.py --simulate-serverless $platform \\
|
|
213
|
+
--exec my_function --param value
|
|
214
|
+
done
|
|
215
|
+
|
|
216
|
+
# Compare webhook URLs across platforms
|
|
217
|
+
swaig-test agent.py --simulate-serverless lambda --dump-swml | grep web_hook_url
|
|
218
|
+
swaig-test agent.py --simulate-serverless cgi --cgi-host example.com --dump-swml | grep web_hook_url
|
|
219
|
+
""")
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def main():
|
|
223
|
+
"""Main entry point for the CLI tool"""
|
|
224
|
+
# Set up suppression early if we're dumping SWML
|
|
225
|
+
if "--dump-swml" in sys.argv:
|
|
226
|
+
setup_output_suppression()
|
|
227
|
+
|
|
228
|
+
# Check for help sections early
|
|
229
|
+
if "--help-platforms" in sys.argv:
|
|
230
|
+
print_help_platforms()
|
|
231
|
+
sys.exit(0)
|
|
232
|
+
|
|
233
|
+
if "--help-examples" in sys.argv:
|
|
234
|
+
print_help_examples()
|
|
235
|
+
sys.exit(0)
|
|
236
|
+
|
|
237
|
+
# Check for --exec and split arguments
|
|
238
|
+
cli_args = sys.argv[1:]
|
|
239
|
+
function_args_list = []
|
|
240
|
+
exec_function_name = None
|
|
241
|
+
|
|
242
|
+
if '--exec' in sys.argv:
|
|
243
|
+
exec_index = sys.argv.index('--exec')
|
|
244
|
+
if exec_index + 1 < len(sys.argv):
|
|
245
|
+
exec_function_name = sys.argv[exec_index + 1]
|
|
246
|
+
# CLI args: everything before --exec
|
|
247
|
+
cli_args = sys.argv[1:exec_index]
|
|
248
|
+
# Function args: everything after the function name
|
|
249
|
+
function_args_list = sys.argv[exec_index + 2:]
|
|
250
|
+
else:
|
|
251
|
+
print("Error: --exec requires a function name")
|
|
252
|
+
return 1
|
|
253
|
+
|
|
254
|
+
# Temporarily modify sys.argv for argparse (exclude --exec and its args)
|
|
255
|
+
original_argv = sys.argv[:]
|
|
256
|
+
sys.argv = [sys.argv[0]] + cli_args
|
|
257
|
+
|
|
258
|
+
parser = CustomArgumentParser(
|
|
259
|
+
description=HELP_DESCRIPTION,
|
|
260
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
261
|
+
usage="%(prog)s <agent_path> [options]",
|
|
262
|
+
epilog=HELP_EPILOG_SHORT
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
# Positional arguments
|
|
266
|
+
parser.add_argument(
|
|
267
|
+
"agent_path",
|
|
268
|
+
help="Path to Python file containing the agent"
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
# Common options
|
|
272
|
+
common = parser.add_argument_group('common options')
|
|
273
|
+
common.add_argument(
|
|
274
|
+
"-v", "--verbose",
|
|
275
|
+
action="store_true",
|
|
276
|
+
help="Enable verbose output"
|
|
277
|
+
)
|
|
278
|
+
common.add_argument(
|
|
279
|
+
"--raw",
|
|
280
|
+
action="store_true",
|
|
281
|
+
help="Output raw JSON only (for piping to jq)"
|
|
282
|
+
)
|
|
283
|
+
common.add_argument(
|
|
284
|
+
"--agent-class",
|
|
285
|
+
help="Specify agent class (required if file has multiple agents)"
|
|
286
|
+
)
|
|
287
|
+
common.add_argument(
|
|
288
|
+
"--route",
|
|
289
|
+
help="Specify service by route (e.g., /healthcare, /finance)"
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
# Actions (choose one)
|
|
293
|
+
actions = parser.add_argument_group('actions (choose one)')
|
|
294
|
+
actions.add_argument(
|
|
295
|
+
"--list-agents",
|
|
296
|
+
action="store_true",
|
|
297
|
+
help="List all agents in file"
|
|
298
|
+
)
|
|
299
|
+
actions.add_argument(
|
|
300
|
+
"--list-tools",
|
|
301
|
+
action="store_true",
|
|
302
|
+
help="List all tools in agent"
|
|
303
|
+
)
|
|
304
|
+
actions.add_argument(
|
|
305
|
+
"--dump-swml",
|
|
306
|
+
action="store_true",
|
|
307
|
+
help="Generate and output SWML document"
|
|
308
|
+
)
|
|
309
|
+
actions.add_argument(
|
|
310
|
+
"--exec",
|
|
311
|
+
metavar="FUNCTION",
|
|
312
|
+
help="Execute function with CLI args (e.g., --exec search --query 'AI')"
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
# Function execution options
|
|
316
|
+
func_group = parser.add_argument_group('function execution options')
|
|
317
|
+
func_group.add_argument(
|
|
318
|
+
"--custom-data",
|
|
319
|
+
help="JSON string with custom post_data overrides",
|
|
320
|
+
default="{}"
|
|
321
|
+
)
|
|
322
|
+
func_group.add_argument(
|
|
323
|
+
"--minimal",
|
|
324
|
+
action="store_true",
|
|
325
|
+
help="Use minimal post_data for function execution"
|
|
326
|
+
)
|
|
327
|
+
func_group.add_argument(
|
|
328
|
+
"--fake-full-data",
|
|
329
|
+
action="store_true",
|
|
330
|
+
help="Use comprehensive fake post_data"
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
# SWML generation options
|
|
334
|
+
swml_group = parser.add_argument_group('swml generation options')
|
|
335
|
+
swml_group.add_argument(
|
|
336
|
+
"--call-type",
|
|
337
|
+
choices=["sip", "webrtc"],
|
|
338
|
+
default="webrtc",
|
|
339
|
+
help="Call type (default: webrtc)"
|
|
340
|
+
)
|
|
341
|
+
swml_group.add_argument(
|
|
342
|
+
"--call-direction",
|
|
343
|
+
choices=["inbound", "outbound"],
|
|
344
|
+
default="inbound",
|
|
345
|
+
help="Call direction (default: inbound)"
|
|
346
|
+
)
|
|
347
|
+
swml_group.add_argument(
|
|
348
|
+
"--call-state",
|
|
349
|
+
default="created",
|
|
350
|
+
help="Call state (default: created)"
|
|
351
|
+
)
|
|
352
|
+
swml_group.add_argument(
|
|
353
|
+
"--from-number",
|
|
354
|
+
help="Override from number"
|
|
355
|
+
)
|
|
356
|
+
swml_group.add_argument(
|
|
357
|
+
"--to-extension",
|
|
358
|
+
help="Override to extension"
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
# Data customization
|
|
362
|
+
data_group = parser.add_argument_group('data customization')
|
|
363
|
+
data_group.add_argument(
|
|
364
|
+
"--user-vars",
|
|
365
|
+
help="JSON string for userVariables"
|
|
366
|
+
)
|
|
367
|
+
data_group.add_argument(
|
|
368
|
+
"--query-params",
|
|
369
|
+
help="JSON string for query parameters"
|
|
370
|
+
)
|
|
371
|
+
data_group.add_argument(
|
|
372
|
+
"--override",
|
|
373
|
+
action="append",
|
|
374
|
+
default=[],
|
|
375
|
+
help="Override value (e.g., --override call.state=answered)"
|
|
376
|
+
)
|
|
377
|
+
data_group.add_argument(
|
|
378
|
+
"--header",
|
|
379
|
+
action="append",
|
|
380
|
+
default=[],
|
|
381
|
+
help="Add HTTP header (e.g., --header Authorization=Bearer token)"
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
# Serverless simulation (basic)
|
|
385
|
+
serverless_group = parser.add_argument_group('serverless simulation (use --help-platforms for platform options)')
|
|
386
|
+
serverless_group.add_argument(
|
|
387
|
+
"--simulate-serverless",
|
|
388
|
+
choices=["lambda", "cgi", "cloud_function", "azure_function"],
|
|
389
|
+
help="Simulate serverless platform"
|
|
390
|
+
)
|
|
391
|
+
serverless_group.add_argument(
|
|
392
|
+
"--env",
|
|
393
|
+
action="append",
|
|
394
|
+
default=[],
|
|
395
|
+
help="Set environment variable (e.g., --env KEY=VALUE)"
|
|
396
|
+
)
|
|
397
|
+
serverless_group.add_argument(
|
|
398
|
+
"--env-file",
|
|
399
|
+
help="Load environment from file"
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
# Hidden/advanced options (not shown in main help)
|
|
403
|
+
parser.add_argument("--call-id", help=argparse.SUPPRESS)
|
|
404
|
+
parser.add_argument("--project-id", help=argparse.SUPPRESS)
|
|
405
|
+
parser.add_argument("--space-id", help=argparse.SUPPRESS)
|
|
406
|
+
parser.add_argument("--method", default="POST", help=argparse.SUPPRESS)
|
|
407
|
+
parser.add_argument("--body", help=argparse.SUPPRESS)
|
|
408
|
+
parser.add_argument("--override-json", action="append", default=[], help=argparse.SUPPRESS)
|
|
409
|
+
|
|
410
|
+
# Platform-specific options (hidden from main help)
|
|
411
|
+
parser.add_argument("--aws-function-name", help=argparse.SUPPRESS)
|
|
412
|
+
parser.add_argument("--aws-function-url", help=argparse.SUPPRESS)
|
|
413
|
+
parser.add_argument("--aws-region", help=argparse.SUPPRESS)
|
|
414
|
+
parser.add_argument("--aws-api-gateway-id", help=argparse.SUPPRESS)
|
|
415
|
+
parser.add_argument("--aws-stage", help=argparse.SUPPRESS)
|
|
416
|
+
parser.add_argument("--cgi-host", help=argparse.SUPPRESS)
|
|
417
|
+
parser.add_argument("--cgi-script-name", help=argparse.SUPPRESS)
|
|
418
|
+
parser.add_argument("--cgi-https", action="store_true", help=argparse.SUPPRESS)
|
|
419
|
+
parser.add_argument("--cgi-path-info", help=argparse.SUPPRESS)
|
|
420
|
+
parser.add_argument("--gcp-project", help=argparse.SUPPRESS)
|
|
421
|
+
parser.add_argument("--gcp-function-url", help=argparse.SUPPRESS)
|
|
422
|
+
parser.add_argument("--gcp-region", help=argparse.SUPPRESS)
|
|
423
|
+
parser.add_argument("--gcp-service", help=argparse.SUPPRESS)
|
|
424
|
+
parser.add_argument("--azure-env", help=argparse.SUPPRESS)
|
|
425
|
+
parser.add_argument("--azure-function-url", help=argparse.SUPPRESS)
|
|
426
|
+
|
|
427
|
+
# Help extension options
|
|
428
|
+
parser.add_argument(
|
|
429
|
+
"--help-platforms",
|
|
430
|
+
action="store_true",
|
|
431
|
+
help="Show platform-specific serverless options"
|
|
432
|
+
)
|
|
433
|
+
parser.add_argument(
|
|
434
|
+
"--help-examples",
|
|
435
|
+
action="store_true",
|
|
436
|
+
help="Show comprehensive usage examples"
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
args = parser.parse_args()
|
|
440
|
+
|
|
441
|
+
# Restore original sys.argv
|
|
442
|
+
sys.argv = original_argv
|
|
443
|
+
|
|
444
|
+
# Handle --exec vs positional tool_name
|
|
445
|
+
if exec_function_name:
|
|
446
|
+
args.tool_name = exec_function_name
|
|
447
|
+
else:
|
|
448
|
+
args.tool_name = None
|
|
449
|
+
|
|
450
|
+
# Validate arguments
|
|
451
|
+
if args.route and args.agent_class:
|
|
452
|
+
parser.error("Cannot specify both --route and --agent-class. Choose one.")
|
|
453
|
+
|
|
454
|
+
if not args.list_tools and not args.dump_swml and not args.list_agents:
|
|
455
|
+
if not args.tool_name:
|
|
456
|
+
# If no tool_name and no special flags, default to listing tools
|
|
457
|
+
args.list_tools = True
|
|
458
|
+
|
|
459
|
+
# ===== SERVERLESS SIMULATION SETUP =====
|
|
460
|
+
serverless_simulator = None
|
|
461
|
+
|
|
462
|
+
if args.simulate_serverless:
|
|
463
|
+
# Validate CGI requirements
|
|
464
|
+
if args.simulate_serverless == 'cgi' and not args.cgi_host:
|
|
465
|
+
parser.error(ERROR_CGI_HOST_REQUIRED)
|
|
466
|
+
|
|
467
|
+
# Collect environment variable overrides
|
|
468
|
+
env_overrides = {}
|
|
469
|
+
|
|
470
|
+
# Load from environment file first
|
|
471
|
+
if args.env_file:
|
|
472
|
+
try:
|
|
473
|
+
file_env = load_env_file(args.env_file)
|
|
474
|
+
env_overrides.update(file_env)
|
|
475
|
+
if args.verbose and not args.raw:
|
|
476
|
+
print(f"Loaded {len(file_env)} environment variables from {args.env_file}")
|
|
477
|
+
except FileNotFoundError as e:
|
|
478
|
+
print(f"Error: {e}")
|
|
479
|
+
return 1
|
|
480
|
+
|
|
481
|
+
# Apply individual env overrides
|
|
482
|
+
for env_var in args.env:
|
|
483
|
+
if '=' in env_var:
|
|
484
|
+
key, value = env_var.split('=', 1)
|
|
485
|
+
env_overrides[key] = value
|
|
486
|
+
|
|
487
|
+
# Apply platform-specific overrides
|
|
488
|
+
if args.simulate_serverless == 'lambda':
|
|
489
|
+
if args.aws_function_name:
|
|
490
|
+
env_overrides['AWS_LAMBDA_FUNCTION_NAME'] = args.aws_function_name
|
|
491
|
+
if args.aws_function_url:
|
|
492
|
+
env_overrides['AWS_LAMBDA_FUNCTION_URL'] = args.aws_function_url
|
|
493
|
+
if args.aws_region:
|
|
494
|
+
env_overrides['AWS_REGION'] = args.aws_region
|
|
495
|
+
elif args.simulate_serverless == 'cgi':
|
|
496
|
+
if args.cgi_host:
|
|
497
|
+
env_overrides['HTTP_HOST'] = args.cgi_host
|
|
498
|
+
env_overrides['SERVER_NAME'] = args.cgi_host
|
|
499
|
+
if args.cgi_script_name:
|
|
500
|
+
env_overrides['SCRIPT_NAME'] = args.cgi_script_name
|
|
501
|
+
if args.cgi_https:
|
|
502
|
+
env_overrides['HTTPS'] = 'on'
|
|
503
|
+
if args.cgi_path_info:
|
|
504
|
+
env_overrides['PATH_INFO'] = args.cgi_path_info
|
|
505
|
+
elif args.simulate_serverless == 'cloud_function':
|
|
506
|
+
if args.gcp_project:
|
|
507
|
+
env_overrides['GOOGLE_CLOUD_PROJECT'] = args.gcp_project
|
|
508
|
+
if args.gcp_function_url:
|
|
509
|
+
env_overrides['FUNCTION_URL'] = args.gcp_function_url
|
|
510
|
+
if args.gcp_region:
|
|
511
|
+
env_overrides['GOOGLE_CLOUD_REGION'] = args.gcp_region
|
|
512
|
+
if args.gcp_service:
|
|
513
|
+
env_overrides['K_SERVICE'] = args.gcp_service
|
|
514
|
+
elif args.simulate_serverless == 'azure_function':
|
|
515
|
+
if args.azure_env:
|
|
516
|
+
env_overrides['AZURE_FUNCTIONS_ENVIRONMENT'] = args.azure_env
|
|
517
|
+
if args.azure_function_url:
|
|
518
|
+
env_overrides['AZURE_FUNCTION_URL'] = args.azure_function_url
|
|
519
|
+
|
|
520
|
+
# Create and activate simulator
|
|
521
|
+
serverless_simulator = ServerlessSimulator(args.simulate_serverless, env_overrides)
|
|
522
|
+
serverless_simulator.activate(args.verbose and not args.raw)
|
|
523
|
+
|
|
524
|
+
# ===== MAIN EXECUTION =====
|
|
525
|
+
try:
|
|
526
|
+
# Check if agent file exists
|
|
527
|
+
agent_path = Path(args.agent_path)
|
|
528
|
+
if not agent_path.exists():
|
|
529
|
+
print(f"Error: Agent file not found: {args.agent_path}")
|
|
530
|
+
return 1
|
|
531
|
+
|
|
532
|
+
# Handle --list-agents
|
|
533
|
+
if args.list_agents:
|
|
534
|
+
try:
|
|
535
|
+
agents = discover_agents_in_file(args.agent_path)
|
|
536
|
+
if not agents:
|
|
537
|
+
print(ERROR_NO_AGENTS.format(file_path=args.agent_path))
|
|
538
|
+
return 1
|
|
539
|
+
|
|
540
|
+
print(f"\nAgents found in {args.agent_path}:")
|
|
541
|
+
for agent_info in agents:
|
|
542
|
+
agent_type = "instance" if agent_info['type'] == 'instance' else "class"
|
|
543
|
+
print(f" {agent_info['class_name']} ({agent_type})")
|
|
544
|
+
if agent_info['type'] == 'instance':
|
|
545
|
+
print(f" Name: {agent_info['agent_name']}")
|
|
546
|
+
print(f" Route: {agent_info['route']}")
|
|
547
|
+
if agent_info['description']:
|
|
548
|
+
# Clean up description
|
|
549
|
+
desc = agent_info['description'].strip()
|
|
550
|
+
if desc:
|
|
551
|
+
# Take first line only
|
|
552
|
+
desc_lines = desc.split('\n')
|
|
553
|
+
first_line = desc_lines[0].strip()
|
|
554
|
+
if first_line:
|
|
555
|
+
print(f" Description: {first_line}")
|
|
556
|
+
return 0
|
|
557
|
+
except Exception as e:
|
|
558
|
+
print(f"Error discovering agents: {e}")
|
|
559
|
+
if args.verbose:
|
|
560
|
+
import traceback
|
|
561
|
+
traceback.print_exc()
|
|
562
|
+
return 1
|
|
563
|
+
|
|
564
|
+
# Load the agent
|
|
565
|
+
try:
|
|
566
|
+
# Determine which identifier to use
|
|
567
|
+
service_identifier = args.route if args.route else args.agent_class
|
|
568
|
+
prefer_route = bool(args.route)
|
|
569
|
+
|
|
570
|
+
# Use load_service_from_file which handles both routes and class names
|
|
571
|
+
from signalwire_agents.cli.core.agent_loader import load_service_from_file
|
|
572
|
+
agent = load_service_from_file(args.agent_path, service_identifier, prefer_route)
|
|
573
|
+
except ValueError as e:
|
|
574
|
+
error_msg = str(e)
|
|
575
|
+
if "Multiple agent classes found" in error_msg and args.list_tools and not args.agent_class:
|
|
576
|
+
# When listing tools and multiple agents exist, show all agents with their tools
|
|
577
|
+
try:
|
|
578
|
+
agents = discover_agents_in_file(args.agent_path)
|
|
579
|
+
if agents:
|
|
580
|
+
print(f"\nMultiple agents found in {args.agent_path}:")
|
|
581
|
+
print("=" * 60)
|
|
582
|
+
|
|
583
|
+
for agent_info in agents:
|
|
584
|
+
if agent_info['type'] == 'class':
|
|
585
|
+
print(f"\n{agent_info['class_name']}:")
|
|
586
|
+
if agent_info['description']:
|
|
587
|
+
desc = agent_info['description'].strip().split('\n')[0]
|
|
588
|
+
if desc:
|
|
589
|
+
print(f" Description: {desc}")
|
|
590
|
+
|
|
591
|
+
# Try to load this specific agent and show its tools
|
|
592
|
+
try:
|
|
593
|
+
specific_agent = load_agent_from_file(args.agent_path, agent_info['class_name'])
|
|
594
|
+
|
|
595
|
+
# Apply dynamic configuration if the agent has it
|
|
596
|
+
# Create a basic mock request for dynamic config
|
|
597
|
+
try:
|
|
598
|
+
basic_mock_request = create_mock_request(
|
|
599
|
+
method="POST",
|
|
600
|
+
headers={},
|
|
601
|
+
query_params={},
|
|
602
|
+
body={}
|
|
603
|
+
)
|
|
604
|
+
apply_dynamic_config(specific_agent, basic_mock_request, verbose=False)
|
|
605
|
+
except Exception as dc_error:
|
|
606
|
+
if args.verbose:
|
|
607
|
+
print(f" (Warning: Dynamic config failed: {dc_error})")
|
|
608
|
+
|
|
609
|
+
functions = specific_agent._tool_registry.get_all_functions() if hasattr(specific_agent, '_tool_registry') else {}
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
if functions:
|
|
613
|
+
print(f" Tools:")
|
|
614
|
+
for name, func in functions.items():
|
|
615
|
+
if isinstance(func, dict):
|
|
616
|
+
description = func.get('description', 'DataMap function')
|
|
617
|
+
print(f" - {name}: {description}")
|
|
618
|
+
else:
|
|
619
|
+
print(f" - {name}: {func.description}")
|
|
620
|
+
else:
|
|
621
|
+
print(f" Tools: (none)")
|
|
622
|
+
except Exception as load_error:
|
|
623
|
+
print(f" Tools: (error loading agent: {load_error})")
|
|
624
|
+
if args.verbose:
|
|
625
|
+
import traceback
|
|
626
|
+
traceback.print_exc()
|
|
627
|
+
|
|
628
|
+
print("\n" + "=" * 60)
|
|
629
|
+
print(f"\nTo use a specific agent, run:")
|
|
630
|
+
print(f" swaig-test {args.agent_path} --agent-class <AgentClassName>")
|
|
631
|
+
print(f" swaig-test {args.agent_path} --route <route>")
|
|
632
|
+
return 0
|
|
633
|
+
except Exception as discover_error:
|
|
634
|
+
print(f"Error discovering agents: {discover_error}")
|
|
635
|
+
return 1
|
|
636
|
+
elif "Multiple agent classes found" in error_msg:
|
|
637
|
+
print(f"\n{ERROR_MULTIPLE_AGENTS}")
|
|
638
|
+
print(error_msg)
|
|
639
|
+
elif "not found" in error_msg and args.agent_class:
|
|
640
|
+
print(ERROR_AGENT_NOT_FOUND.format(
|
|
641
|
+
class_name=args.agent_class,
|
|
642
|
+
file_path=args.agent_path
|
|
643
|
+
))
|
|
644
|
+
else:
|
|
645
|
+
print(f"Error: {error_msg}")
|
|
646
|
+
return 1
|
|
647
|
+
|
|
648
|
+
# Create mock request for dynamic configuration
|
|
649
|
+
headers = {}
|
|
650
|
+
for header in args.header:
|
|
651
|
+
if '=' in header:
|
|
652
|
+
key, value = header.split('=', 1)
|
|
653
|
+
headers[key] = value
|
|
654
|
+
|
|
655
|
+
query_params = {}
|
|
656
|
+
if args.query_params:
|
|
657
|
+
try:
|
|
658
|
+
query_params = json.loads(args.query_params)
|
|
659
|
+
except json.JSONDecodeError as e:
|
|
660
|
+
if not args.raw:
|
|
661
|
+
print(f"Warning: Invalid JSON in --query-params: {e}")
|
|
662
|
+
|
|
663
|
+
request_body = {}
|
|
664
|
+
if args.body:
|
|
665
|
+
try:
|
|
666
|
+
request_body = json.loads(args.body)
|
|
667
|
+
except json.JSONDecodeError as e:
|
|
668
|
+
if not args.raw:
|
|
669
|
+
print(f"Warning: Invalid JSON in --body: {e}")
|
|
670
|
+
|
|
671
|
+
mock_request = create_mock_request(
|
|
672
|
+
method=args.method,
|
|
673
|
+
headers=headers,
|
|
674
|
+
query_params=query_params,
|
|
675
|
+
body=request_body
|
|
676
|
+
)
|
|
677
|
+
|
|
678
|
+
# Apply dynamic configuration
|
|
679
|
+
apply_dynamic_config(agent, mock_request, verbose=args.verbose and not args.raw)
|
|
680
|
+
|
|
681
|
+
# Handle --list-tools
|
|
682
|
+
if args.list_tools:
|
|
683
|
+
display_agent_tools(agent, verbose=args.verbose)
|
|
684
|
+
return 0
|
|
685
|
+
|
|
686
|
+
# Handle --dump-swml
|
|
687
|
+
if args.dump_swml:
|
|
688
|
+
return handle_dump_swml(agent, args)
|
|
689
|
+
|
|
690
|
+
# Handle function execution
|
|
691
|
+
if args.tool_name:
|
|
692
|
+
# Get the function
|
|
693
|
+
functions = agent._tool_registry.get_all_functions() if hasattr(agent, '_tool_registry') else {}
|
|
694
|
+
|
|
695
|
+
if args.tool_name not in functions:
|
|
696
|
+
print(ERROR_FUNCTION_NOT_FOUND.format(function_name=args.tool_name))
|
|
697
|
+
display_agent_tools(agent, verbose=False)
|
|
698
|
+
return 1
|
|
699
|
+
|
|
700
|
+
func = functions[args.tool_name]
|
|
701
|
+
|
|
702
|
+
# Parse function arguments
|
|
703
|
+
try:
|
|
704
|
+
function_args = parse_function_arguments(function_args_list, func)
|
|
705
|
+
except ValueError as e:
|
|
706
|
+
print(f"Error parsing arguments: {e}")
|
|
707
|
+
return 1
|
|
708
|
+
|
|
709
|
+
# Check if this is a DataMap function
|
|
710
|
+
is_datamap = isinstance(func, dict) and 'data_map' in func
|
|
711
|
+
|
|
712
|
+
# Check if this is an external webhook function
|
|
713
|
+
is_external_webhook = (hasattr(func, 'webhook_url') and
|
|
714
|
+
func.webhook_url and
|
|
715
|
+
hasattr(func, 'is_external') and
|
|
716
|
+
func.is_external)
|
|
717
|
+
|
|
718
|
+
if is_datamap:
|
|
719
|
+
if args.verbose:
|
|
720
|
+
print(f"\nCalling DataMap function: {args.tool_name}")
|
|
721
|
+
print(f"Arguments: {json.dumps(function_args, indent=2)}")
|
|
722
|
+
print(f"Function type: DataMap (serverless)")
|
|
723
|
+
print("-" * 60)
|
|
724
|
+
|
|
725
|
+
# Execute DataMap function
|
|
726
|
+
result = execute_datamap_function(func, function_args, args.verbose)
|
|
727
|
+
print("RESULT:")
|
|
728
|
+
print(format_result(result))
|
|
729
|
+
else:
|
|
730
|
+
# Regular SWAIG function
|
|
731
|
+
if args.verbose:
|
|
732
|
+
print(f"\nCalling function: {args.tool_name}")
|
|
733
|
+
print(f"Arguments: {json.dumps(function_args, indent=2)}")
|
|
734
|
+
if is_external_webhook:
|
|
735
|
+
print(f"Function type: EXTERNAL webhook")
|
|
736
|
+
print(f"External URL: {func.webhook_url}")
|
|
737
|
+
else:
|
|
738
|
+
print(f"Function type: LOCAL webhook")
|
|
739
|
+
|
|
740
|
+
# Generate post_data based on options
|
|
741
|
+
if args.minimal:
|
|
742
|
+
post_data = generate_minimal_post_data(args.tool_name, function_args)
|
|
743
|
+
if args.custom_data:
|
|
744
|
+
custom_data = json.loads(args.custom_data)
|
|
745
|
+
post_data.update(custom_data)
|
|
746
|
+
elif args.fake_full_data or args.custom_data:
|
|
747
|
+
custom_data = json.loads(args.custom_data) if args.custom_data else None
|
|
748
|
+
post_data = generate_comprehensive_post_data(args.tool_name, function_args, custom_data)
|
|
749
|
+
else:
|
|
750
|
+
# Default behavior - minimal data
|
|
751
|
+
post_data = generate_minimal_post_data(args.tool_name, function_args)
|
|
752
|
+
|
|
753
|
+
# Apply convenience mappings from CLI args (e.g., --call-id)
|
|
754
|
+
post_data = apply_convenience_mappings(post_data, args)
|
|
755
|
+
|
|
756
|
+
# Apply explicit overrides
|
|
757
|
+
post_data = apply_overrides(post_data, args.override, args.override_json)
|
|
758
|
+
|
|
759
|
+
if args.verbose:
|
|
760
|
+
print(f"Post data: {json.dumps(post_data, indent=2)}")
|
|
761
|
+
print("-" * 60)
|
|
762
|
+
|
|
763
|
+
# Call the function
|
|
764
|
+
try:
|
|
765
|
+
if is_external_webhook:
|
|
766
|
+
# For external webhook functions, make HTTP request to external service
|
|
767
|
+
result = execute_external_webhook_function(func, args.tool_name, function_args, post_data, args.verbose)
|
|
768
|
+
else:
|
|
769
|
+
# For local webhook functions, call the agent's handler
|
|
770
|
+
result = agent.on_function_call(args.tool_name, function_args, post_data)
|
|
771
|
+
|
|
772
|
+
print("RESULT:")
|
|
773
|
+
print(format_result(result))
|
|
774
|
+
|
|
775
|
+
if args.verbose:
|
|
776
|
+
print(f"\nRaw result type: {type(result).__name__}")
|
|
777
|
+
print(f"Raw result: {repr(result)}")
|
|
778
|
+
|
|
779
|
+
except Exception as e:
|
|
780
|
+
print(f"Error calling function: {e}")
|
|
781
|
+
if args.verbose:
|
|
782
|
+
import traceback
|
|
783
|
+
traceback.print_exc()
|
|
784
|
+
return 1
|
|
785
|
+
|
|
786
|
+
except Exception as e:
|
|
787
|
+
print(f"Error: {e}")
|
|
788
|
+
if args.verbose:
|
|
789
|
+
import traceback
|
|
790
|
+
traceback.print_exc()
|
|
791
|
+
return 1
|
|
792
|
+
finally:
|
|
793
|
+
# Clean up serverless simulation
|
|
794
|
+
if serverless_simulator:
|
|
795
|
+
serverless_simulator.deactivate(args.verbose and not args.raw)
|
|
796
|
+
|
|
797
|
+
return 0
|
|
798
|
+
|
|
799
|
+
|
|
800
|
+
def console_entry_point():
|
|
801
|
+
"""Console script entry point for pip installation"""
|
|
802
|
+
# Check for --dump-swml or --raw BEFORE imports happen
|
|
803
|
+
if "--raw" in sys.argv or "--dump-swml" in sys.argv:
|
|
804
|
+
os.environ['SIGNALWIRE_LOG_MODE'] = 'off'
|
|
805
|
+
sys.exit(main())
|
|
806
|
+
|
|
807
|
+
|
|
808
|
+
if __name__ == "__main__":
|
|
809
|
+
sys.exit(main())
|