signalwire-agents 0.1.23__tar.gz → 0.1.25__tar.gz
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-0.1.25/CHANGELOG.md +124 -0
- {signalwire_agents-0.1.23/signalwire_agents.egg-info → signalwire_agents-0.1.25}/PKG-INFO +47 -2
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/README.md +43 -1
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/pyproject.toml +12 -7
- signalwire_agents-0.1.25/requirements-dev.txt +16 -0
- signalwire_agents-0.1.25/requirements.txt +11 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/__init__.py +1 -1
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/agent_server.py +2 -1
- signalwire_agents-0.1.25/signalwire_agents/cli/config.py +61 -0
- signalwire_agents-0.1.25/signalwire_agents/cli/core/__init__.py +1 -0
- signalwire_agents-0.1.25/signalwire_agents/cli/core/agent_loader.py +254 -0
- signalwire_agents-0.1.25/signalwire_agents/cli/core/argparse_helpers.py +164 -0
- signalwire_agents-0.1.25/signalwire_agents/cli/core/dynamic_config.py +62 -0
- signalwire_agents-0.1.25/signalwire_agents/cli/execution/__init__.py +1 -0
- signalwire_agents-0.1.25/signalwire_agents/cli/execution/datamap_exec.py +437 -0
- signalwire_agents-0.1.25/signalwire_agents/cli/execution/webhook_exec.py +125 -0
- signalwire_agents-0.1.25/signalwire_agents/cli/output/__init__.py +1 -0
- signalwire_agents-0.1.25/signalwire_agents/cli/output/output_formatter.py +132 -0
- signalwire_agents-0.1.25/signalwire_agents/cli/output/swml_dump.py +177 -0
- signalwire_agents-0.1.25/signalwire_agents/cli/simulation/__init__.py +1 -0
- signalwire_agents-0.1.25/signalwire_agents/cli/simulation/data_generation.py +365 -0
- signalwire_agents-0.1.25/signalwire_agents/cli/simulation/data_overrides.py +187 -0
- signalwire_agents-0.1.25/signalwire_agents/cli/simulation/mock_env.py +271 -0
- signalwire_agents-0.1.25/signalwire_agents/cli/test_swaig.py +780 -0
- signalwire_agents-0.1.25/signalwire_agents/cli/types.py +72 -0
- signalwire_agents-0.1.25/signalwire_agents/core/agent/__init__.py +12 -0
- signalwire_agents-0.1.25/signalwire_agents/core/agent/config/__init__.py +12 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/agent/prompt/manager.py +25 -7
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/agent/tools/decorator.py +2 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/agent/tools/registry.py +8 -0
- signalwire_agents-0.1.25/signalwire_agents/core/agent_base.py +1039 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/function_result.py +31 -42
- signalwire_agents-0.1.25/signalwire_agents/core/mixins/__init__.py +28 -0
- signalwire_agents-0.1.25/signalwire_agents/core/mixins/ai_config_mixin.py +373 -0
- signalwire_agents-0.1.25/signalwire_agents/core/mixins/auth_mixin.py +287 -0
- signalwire_agents-0.1.25/signalwire_agents/core/mixins/prompt_mixin.py +345 -0
- signalwire_agents-0.1.25/signalwire_agents/core/mixins/serverless_mixin.py +368 -0
- signalwire_agents-0.1.25/signalwire_agents/core/mixins/skill_mixin.py +55 -0
- signalwire_agents-0.1.25/signalwire_agents/core/mixins/state_mixin.py +219 -0
- signalwire_agents-0.1.25/signalwire_agents/core/mixins/tool_mixin.py +295 -0
- signalwire_agents-0.1.25/signalwire_agents/core/mixins/web_mixin.py +1130 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/skill_manager.py +3 -1
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/swaig_function.py +10 -1
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/swml_service.py +140 -58
- signalwire_agents-0.1.25/signalwire_agents/skills/README.md +452 -0
- signalwire_agents-0.1.25/signalwire_agents/skills/api_ninjas_trivia/README.md +215 -0
- signalwire_agents-0.1.25/signalwire_agents/skills/datasphere/README.md +210 -0
- signalwire_agents-0.1.25/signalwire_agents/skills/datasphere_serverless/README.md +258 -0
- signalwire_agents-0.1.25/signalwire_agents/skills/datetime/README.md +132 -0
- signalwire_agents-0.1.25/signalwire_agents/skills/joke/README.md +149 -0
- signalwire_agents-0.1.25/signalwire_agents/skills/math/README.md +161 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/native_vector_search/skill.py +33 -13
- signalwire_agents-0.1.25/signalwire_agents/skills/play_background_file/README.md +218 -0
- signalwire_agents-0.1.25/signalwire_agents/skills/spider/README.md +236 -0
- signalwire_agents-0.1.25/signalwire_agents/skills/spider/__init__.py +4 -0
- signalwire_agents-0.1.25/signalwire_agents/skills/spider/skill.py +479 -0
- signalwire_agents-0.1.25/signalwire_agents/skills/swml_transfer/README.md +395 -0
- signalwire_agents-0.1.25/signalwire_agents/skills/swml_transfer/__init__.py +1 -0
- signalwire_agents-0.1.25/signalwire_agents/skills/swml_transfer/skill.py +257 -0
- signalwire_agents-0.1.25/signalwire_agents/skills/weather_api/README.md +178 -0
- signalwire_agents-0.1.25/signalwire_agents/skills/web_search/README.md +163 -0
- signalwire_agents-0.1.25/signalwire_agents/skills/wikipedia_search/README.md +228 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25/signalwire_agents.egg-info}/PKG-INFO +47 -2
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents.egg-info/SOURCES.txt +45 -1
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents.egg-info/entry_points.txt +1 -1
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents.egg-info/requires.txt +1 -0
- signalwire_agents-0.1.23/signalwire_agents/cli/test_swaig.py +0 -2797
- signalwire_agents-0.1.23/signalwire_agents/core/agent/__init__.py +0 -14
- signalwire_agents-0.1.23/signalwire_agents/core/agent/config/__init__.py +0 -14
- signalwire_agents-0.1.23/signalwire_agents/core/agent/config/ephemeral.py +0 -176
- signalwire_agents-0.1.23/signalwire_agents/core/agent_base.py +0 -3600
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/LICENSE +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/schema.json +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/setup.cfg +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/setup.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/cli/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/cli/build_search.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/agent/deployment/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/agent/deployment/handlers/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/agent/prompt/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/agent/routing/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/agent/security/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/agent/swml/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/agent/tools/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/contexts.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/data_map.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/logging_config.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/pom_builder.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/security/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/security/session_manager.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/skill_base.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/state/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/state/file_state_manager.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/state/state_manager.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/swml_builder.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/swml_handler.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/core/swml_renderer.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/prefabs/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/prefabs/concierge.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/prefabs/faq_bot.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/prefabs/info_gatherer.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/prefabs/receptionist.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/prefabs/survey.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/schema.json +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/search/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/search/document_processor.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/search/index_builder.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/search/query_processor.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/search/search_engine.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/search/search_service.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/api_ninjas_trivia/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/api_ninjas_trivia/skill.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/datasphere/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/datasphere/skill.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/datasphere_serverless/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/datasphere_serverless/skill.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/datetime/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/datetime/skill.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/joke/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/joke/skill.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/math/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/math/skill.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/native_vector_search/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/play_background_file/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/play_background_file/skill.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/registry.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/weather_api/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/weather_api/skill.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/web_search/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/web_search/skill.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/wikipedia_search/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/skills/wikipedia_search/skill.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/utils/__init__.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/utils/pom_utils.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/utils/schema_utils.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/utils/token_generators.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents/utils/validators.py +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents.egg-info/dependency_links.txt +0 -0
- {signalwire_agents-0.1.23 → signalwire_agents-0.1.25}/signalwire_agents.egg-info/top_level.txt +0 -0
@@ -0,0 +1,124 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## [0.1.25] - 2025-06-19
|
4
|
+
|
5
|
+
- Version bump
|
6
|
+
|
7
|
+
## [0.1.24] - 2025-06-19
|
8
|
+
|
9
|
+
- Version bump
|
10
|
+
|
11
|
+
## [0.1.23] - 2025-06-17
|
12
|
+
|
13
|
+
- Version bump
|
14
|
+
|
15
|
+
## [0.1.22] - 2025-06-17
|
16
|
+
|
17
|
+
- Version bump
|
18
|
+
|
19
|
+
## [0.1.21] - 2025-06-12
|
20
|
+
|
21
|
+
- Version bump
|
22
|
+
|
23
|
+
## [0.1.20] - 2025-06-10
|
24
|
+
|
25
|
+
- Version bump
|
26
|
+
|
27
|
+
## [0.1.19] - 2025-06-09
|
28
|
+
|
29
|
+
- Version bump
|
30
|
+
|
31
|
+
## [0.1.18] - 2025-06-09
|
32
|
+
|
33
|
+
- Version bump
|
34
|
+
|
35
|
+
## [0.1.17] - 2025-06-02
|
36
|
+
|
37
|
+
- Version bump
|
38
|
+
|
39
|
+
## [0.1.16] - 2025-06-02
|
40
|
+
|
41
|
+
- Version bump
|
42
|
+
|
43
|
+
## [0.1.15] - 2025-05-30
|
44
|
+
|
45
|
+
- Version bump
|
46
|
+
|
47
|
+
## [0.1.14] - 2025-05-29
|
48
|
+
|
49
|
+
- Version bump
|
50
|
+
|
51
|
+
## [0.1.13] - 2025-05-29
|
52
|
+
|
53
|
+
- Version bump
|
54
|
+
|
55
|
+
## [0.1.12] - 2025-05-28
|
56
|
+
|
57
|
+
- Version bump
|
58
|
+
|
59
|
+
## [0.1.11] - 2025-05-28
|
60
|
+
|
61
|
+
- Version bump
|
62
|
+
|
63
|
+
## [0.1.10] - 2025-05-28
|
64
|
+
|
65
|
+
- Version bump
|
66
|
+
|
67
|
+
## [0.1.9] - 2025-05-24
|
68
|
+
|
69
|
+
- Version bump
|
70
|
+
|
71
|
+
## [0.1.8] - 2025-05-23
|
72
|
+
|
73
|
+
- Version bump
|
74
|
+
|
75
|
+
## [0.1.7] - 2025-05-17
|
76
|
+
|
77
|
+
- Version bump
|
78
|
+
|
79
|
+
## [0.1.6] - 2025-05-16
|
80
|
+
|
81
|
+
- Version bump
|
82
|
+
|
83
|
+
## [0.1.5] - 2025-05-16
|
84
|
+
|
85
|
+
- Version bump
|
86
|
+
|
87
|
+
## [0.1.4] - 2025-05-16
|
88
|
+
|
89
|
+
- Version bump
|
90
|
+
|
91
|
+
## [Unreleased]
|
92
|
+
|
93
|
+
### Added
|
94
|
+
|
95
|
+
- **SIP Routing:** Added support for routing SIP requests based on username
|
96
|
+
- `SWMLService.extract_sip_username()` method for extracting SIP usernames from request bodies
|
97
|
+
- `register_routing_callback()` method for SWMLService to handle custom routing
|
98
|
+
- `enable_sip_routing()` method for AgentBase to automatically handle SIP requests
|
99
|
+
- `register_sip_username()` method for AgentBase to register custom SIP usernames
|
100
|
+
- `setup_sip_routing()` method for AgentServer to enable centralized SIP routing
|
101
|
+
- Added global `/sip` endpoint for both SWMLService and AgentBase when SIP routing is enabled
|
102
|
+
- Example: Added SIP routing to simple_agent.py
|
103
|
+
- Documentation: Added docs/sip_routing.md with full documentation
|
104
|
+
|
105
|
+
### Fixed
|
106
|
+
|
107
|
+
- Fixed the `sleep` verb auto-vivification to properly handle direct integer values
|
108
|
+
|
109
|
+
### Changed
|
110
|
+
|
111
|
+
- **Dynamic Method Generation:** Implemented auto-vivification in SWMLService class for more intuitive verb usage
|
112
|
+
- Now you can use `service.play(url="say:Hello")` instead of `service.add_verb("play", {"url": "say:Hello"})`
|
113
|
+
- All verbs from the schema are automatically available as methods
|
114
|
+
- Special handling for the `sleep` verb which takes a direct integer value
|
115
|
+
|
116
|
+
## [0.1.2] - 2023-04-15
|
117
|
+
|
118
|
+
### Added
|
119
|
+
|
120
|
+
- Initial release of the SignalWire AI Agent SDK
|
121
|
+
- Base classes for building and serving AI Agents
|
122
|
+
- SWML document creation and manipulation
|
123
|
+
- Web server for handling agent requests
|
124
|
+
- Documentation and examples
|
@@ -1,9 +1,11 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: signalwire_agents
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.25
|
4
4
|
Summary: SignalWire AI Agents SDK
|
5
5
|
Author-email: SignalWire Team <info@signalwire.com>
|
6
|
+
License: MIT
|
6
7
|
Project-URL: Homepage, https://github.com/signalwire/signalwire-agents
|
8
|
+
Keywords: signalwire,ai,agents,voice,telephony,swaig,swml
|
7
9
|
Classifier: Development Status :: 4 - Beta
|
8
10
|
Classifier: Intended Audience :: Developers
|
9
11
|
Classifier: License :: OSI Approved :: MIT License
|
@@ -26,6 +28,7 @@ Requires-Dist: structlog==25.3.0
|
|
26
28
|
Requires-Dist: uvicorn==0.34.2
|
27
29
|
Requires-Dist: beautifulsoup4==4.12.3
|
28
30
|
Requires-Dist: pytz==2023.3
|
31
|
+
Requires-Dist: lxml>=4.9.0
|
29
32
|
Provides-Extra: search-queryonly
|
30
33
|
Requires-Dist: numpy>=1.24.0; extra == "search-queryonly"
|
31
34
|
Requires-Dist: scikit-learn>=1.3.0; extra == "search-queryonly"
|
@@ -735,7 +738,49 @@ class DynamicAgent(AgentBase):
|
|
735
738
|
- **Localization**: Language and cultural adaptation based on user location
|
736
739
|
- **Dynamic Pricing**: Adjust features and capabilities based on subscription tiers
|
737
740
|
|
738
|
-
|
741
|
+
### Preserving Dynamic State in SWAIG Callbacks
|
742
|
+
|
743
|
+
When using dynamic configuration to add skills or tools based on request parameters, there's a challenge: SWAIG webhook callbacks are separate HTTP requests that won't have the original query parameters. The SDK provides `add_swaig_query_params()` to solve this:
|
744
|
+
|
745
|
+
```python
|
746
|
+
class DynamicAgent(AgentBase):
|
747
|
+
def __init__(self):
|
748
|
+
super().__init__(name="dynamic-agent", route="/agent")
|
749
|
+
self.set_dynamic_config_callback(self.configure_per_request)
|
750
|
+
|
751
|
+
def configure_per_request(self, query_params, body_params, headers, agent):
|
752
|
+
tier = query_params.get('tier', 'basic')
|
753
|
+
region = query_params.get('region', 'us-east')
|
754
|
+
|
755
|
+
if tier == 'premium':
|
756
|
+
# Add premium skills dynamically
|
757
|
+
agent.add_skill('advanced_search', {
|
758
|
+
'api_key': 'your-api-key',
|
759
|
+
'num_results': 5
|
760
|
+
})
|
761
|
+
|
762
|
+
# IMPORTANT: Preserve parameters for SWAIG callbacks
|
763
|
+
agent.add_swaig_query_params({
|
764
|
+
'tier': tier,
|
765
|
+
'region': region
|
766
|
+
})
|
767
|
+
|
768
|
+
# Now when SignalWire calls the SWAIG webhook, these params
|
769
|
+
# will be included, triggering the same dynamic configuration
|
770
|
+
|
771
|
+
# Initial request: GET /agent?tier=premium®ion=eu-west
|
772
|
+
# SWAIG callback: POST /swaig/?tier=premium®ion=eu-west
|
773
|
+
# Result: Premium skills are available in both requests!
|
774
|
+
```
|
775
|
+
|
776
|
+
**Key Points:**
|
777
|
+
|
778
|
+
- **Problem**: Dynamically added skills/tools won't exist during SWAIG callbacks without the original request parameters
|
779
|
+
- **Solution**: Use `add_swaig_query_params()` to include critical parameters in all SWAIG webhook URLs
|
780
|
+
- **Clear State**: Use `clear_swaig_query_params()` if needed to reset parameters between requests
|
781
|
+
- **Token Safety**: The SDK automatically renames security tokens from `token` to `__token` to avoid parameter collisions
|
782
|
+
|
783
|
+
This ensures that any dynamic configuration based on request parameters is consistently applied across the initial SWML request and all subsequent SWAIG function callbacks.
|
739
784
|
|
740
785
|
For detailed documentation and advanced examples, see the [Agent Guide](docs/agent_guide.md#dynamic-agent-configuration).
|
741
786
|
|
@@ -664,7 +664,49 @@ class DynamicAgent(AgentBase):
|
|
664
664
|
- **Localization**: Language and cultural adaptation based on user location
|
665
665
|
- **Dynamic Pricing**: Adjust features and capabilities based on subscription tiers
|
666
666
|
|
667
|
-
|
667
|
+
### Preserving Dynamic State in SWAIG Callbacks
|
668
|
+
|
669
|
+
When using dynamic configuration to add skills or tools based on request parameters, there's a challenge: SWAIG webhook callbacks are separate HTTP requests that won't have the original query parameters. The SDK provides `add_swaig_query_params()` to solve this:
|
670
|
+
|
671
|
+
```python
|
672
|
+
class DynamicAgent(AgentBase):
|
673
|
+
def __init__(self):
|
674
|
+
super().__init__(name="dynamic-agent", route="/agent")
|
675
|
+
self.set_dynamic_config_callback(self.configure_per_request)
|
676
|
+
|
677
|
+
def configure_per_request(self, query_params, body_params, headers, agent):
|
678
|
+
tier = query_params.get('tier', 'basic')
|
679
|
+
region = query_params.get('region', 'us-east')
|
680
|
+
|
681
|
+
if tier == 'premium':
|
682
|
+
# Add premium skills dynamically
|
683
|
+
agent.add_skill('advanced_search', {
|
684
|
+
'api_key': 'your-api-key',
|
685
|
+
'num_results': 5
|
686
|
+
})
|
687
|
+
|
688
|
+
# IMPORTANT: Preserve parameters for SWAIG callbacks
|
689
|
+
agent.add_swaig_query_params({
|
690
|
+
'tier': tier,
|
691
|
+
'region': region
|
692
|
+
})
|
693
|
+
|
694
|
+
# Now when SignalWire calls the SWAIG webhook, these params
|
695
|
+
# will be included, triggering the same dynamic configuration
|
696
|
+
|
697
|
+
# Initial request: GET /agent?tier=premium®ion=eu-west
|
698
|
+
# SWAIG callback: POST /swaig/?tier=premium®ion=eu-west
|
699
|
+
# Result: Premium skills are available in both requests!
|
700
|
+
```
|
701
|
+
|
702
|
+
**Key Points:**
|
703
|
+
|
704
|
+
- **Problem**: Dynamically added skills/tools won't exist during SWAIG callbacks without the original request parameters
|
705
|
+
- **Solution**: Use `add_swaig_query_params()` to include critical parameters in all SWAIG webhook URLs
|
706
|
+
- **Clear State**: Use `clear_swaig_query_params()` if needed to reset parameters between requests
|
707
|
+
- **Token Safety**: The SDK automatically renames security tokens from `token` to `__token` to avoid parameter collisions
|
708
|
+
|
709
|
+
This ensures that any dynamic configuration based on request parameters is consistently applied across the initial SWML request and all subsequent SWAIG function callbacks.
|
668
710
|
|
669
711
|
For detailed documentation and advanced examples, see the [Agent Guide](docs/agent_guide.md#dynamic-agent-configuration).
|
670
712
|
|
@@ -1,15 +1,17 @@
|
|
1
1
|
[build-system]
|
2
|
-
requires = ["setuptools>=
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
3
3
|
build-backend = "setuptools.build_meta"
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "signalwire_agents"
|
7
|
-
version = "0.1.
|
7
|
+
version = "0.1.25"
|
8
8
|
description = "SignalWire AI Agents SDK"
|
9
9
|
authors = [
|
10
10
|
{name = "SignalWire Team", email = "info@signalwire.com"}
|
11
11
|
]
|
12
12
|
readme = "README.md"
|
13
|
+
license = {text = "MIT"}
|
14
|
+
keywords = ["signalwire", "ai", "agents", "voice", "telephony", "swaig", "swml"]
|
13
15
|
requires-python = ">=3.7"
|
14
16
|
classifiers = [
|
15
17
|
"Development Status :: 4 - Beta",
|
@@ -33,6 +35,7 @@ dependencies = [
|
|
33
35
|
"uvicorn==0.34.2",
|
34
36
|
"beautifulsoup4==4.12.3",
|
35
37
|
"pytz==2023.3",
|
38
|
+
"lxml>=4.9.0",
|
36
39
|
]
|
37
40
|
|
38
41
|
# Optional dependencies for search functionality
|
@@ -98,7 +101,7 @@ search-all = [
|
|
98
101
|
Homepage = "https://github.com/signalwire/signalwire-agents"
|
99
102
|
|
100
103
|
[project.scripts]
|
101
|
-
swaig-test = "signalwire_agents.cli.test_swaig:
|
104
|
+
swaig-test = "signalwire_agents.cli.test_swaig:main"
|
102
105
|
sw-search = "signalwire_agents.cli.build_search:console_entry_point"
|
103
106
|
|
104
107
|
[tool.setuptools]
|
@@ -106,7 +109,9 @@ packages = {find = {where = ["."], include = ["signalwire_agents*"]}}
|
|
106
109
|
include-package-data = true
|
107
110
|
|
108
111
|
[tool.setuptools.package-data]
|
109
|
-
signalwire_agents = [
|
110
|
-
|
111
|
-
|
112
|
-
"."
|
112
|
+
signalwire_agents = [
|
113
|
+
"schema.json",
|
114
|
+
"**/*.json",
|
115
|
+
"skills/*/README.md",
|
116
|
+
"skills/README.md"
|
117
|
+
]
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Development and Testing Dependencies
|
2
|
+
pytest>=7.4.0
|
3
|
+
pytest-cov>=4.1.0
|
4
|
+
pytest-asyncio>=0.21.0
|
5
|
+
pytest-mock>=3.11.0
|
6
|
+
pytest-xdist>=3.3.0 # For parallel test execution
|
7
|
+
pytest-html>=3.2.0 # For HTML test reports
|
8
|
+
pytest-timeout>=2.1.0 # For test timeouts
|
9
|
+
coverage>=7.2.0
|
10
|
+
mock>=5.1.0
|
11
|
+
responses>=0.23.0 # For mocking HTTP requests
|
12
|
+
freezegun>=1.2.0 # For mocking datetime
|
13
|
+
factory-boy>=3.3.0 # For test data factories
|
14
|
+
faker>=19.0.0 # For generating fake data
|
15
|
+
httpx>=0.24.0 # For async HTTP testing
|
16
|
+
aiofiles>=23.0.0 # For async file operations in tests
|
@@ -18,7 +18,7 @@ A package for building AI agents using SignalWire's AI and SWML capabilities.
|
|
18
18
|
from .core.logging_config import configure_logging
|
19
19
|
configure_logging()
|
20
20
|
|
21
|
-
__version__ = "0.1.
|
21
|
+
__version__ = "0.1.25"
|
22
22
|
|
23
23
|
# Import core classes for easier access
|
24
24
|
from .core.agent_base import AgentBase
|
@@ -610,8 +610,9 @@ class AgentServer:
|
|
610
610
|
self.logger.info(f"Starting server on {protocol}://{display_host}")
|
611
611
|
for route, agent in self.agents.items():
|
612
612
|
username, password = agent.get_basic_auth_credentials()
|
613
|
+
agent_url = agent.get_full_url(include_auth=False)
|
613
614
|
self.logger.info(f"Agent '{agent.get_name()}' available at:")
|
614
|
-
self.logger.info(f"URL: {
|
615
|
+
self.logger.info(f"URL: {agent_url}")
|
615
616
|
self.logger.info(f"Basic Auth: {username}:{password}")
|
616
617
|
|
617
618
|
# Start the server with or without SSL
|
@@ -0,0 +1,61 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Configuration constants for the CLI tools
|
4
|
+
"""
|
5
|
+
|
6
|
+
# Default values for call configuration
|
7
|
+
DEFAULT_CALL_TYPE = "webrtc"
|
8
|
+
DEFAULT_CALL_DIRECTION = "inbound"
|
9
|
+
DEFAULT_CALL_STATE = "created"
|
10
|
+
DEFAULT_HTTP_METHOD = "POST"
|
11
|
+
DEFAULT_TOKEN_EXPIRY = 3600
|
12
|
+
|
13
|
+
# Default fake data values
|
14
|
+
DEFAULT_PROJECT_ID = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
|
15
|
+
DEFAULT_SPACE_ID = "zzzzzzzz-yyyy-xxxx-wwww-vvvvvvvvvvvv"
|
16
|
+
DEFAULT_SPACE_NAME = "example-space"
|
17
|
+
DEFAULT_ENVIRONMENT = "production"
|
18
|
+
|
19
|
+
# Request timeouts
|
20
|
+
HTTP_REQUEST_TIMEOUT = 30
|
21
|
+
|
22
|
+
# Output formatting
|
23
|
+
RESULT_LINE_SEP = "-" * 60
|
24
|
+
|
25
|
+
# Platform-specific constants
|
26
|
+
SERVERLESS_PLATFORMS = ["lambda", "cgi", "cloud_function", "azure_function"]
|
27
|
+
|
28
|
+
# AWS Lambda defaults
|
29
|
+
AWS_DEFAULT_REGION = "us-east-1"
|
30
|
+
AWS_DEFAULT_STAGE = "prod"
|
31
|
+
|
32
|
+
# Google Cloud defaults
|
33
|
+
GCP_DEFAULT_REGION = "us-central1"
|
34
|
+
|
35
|
+
# Error messages
|
36
|
+
ERROR_MISSING_AGENT = "Error: Missing required argument."
|
37
|
+
ERROR_MULTIPLE_AGENTS = "Multiple agents found in file. Please specify --agent-class"
|
38
|
+
ERROR_NO_AGENTS = "No agents found in file: {file_path}"
|
39
|
+
ERROR_AGENT_NOT_FOUND = "Agent class '{class_name}' not found in file: {file_path}"
|
40
|
+
ERROR_FUNCTION_NOT_FOUND = "Function '{function_name}' not found in agent"
|
41
|
+
ERROR_CGI_HOST_REQUIRED = "CGI simulation requires --cgi-host"
|
42
|
+
|
43
|
+
# Help messages
|
44
|
+
HELP_DESCRIPTION = "Test SWAIG functions and generate SWML documents for SignalWire AI agents"
|
45
|
+
HELP_EPILOG_SHORT = """
|
46
|
+
examples:
|
47
|
+
# Execute a function
|
48
|
+
%(prog)s agent.py --exec search --query "test" --limit 5
|
49
|
+
|
50
|
+
# Generate SWML
|
51
|
+
%(prog)s agent.py --dump-swml --raw | jq '.'
|
52
|
+
|
53
|
+
# Test with specific agent
|
54
|
+
%(prog)s multi_agent.py --agent-class MattiAgent --list-tools
|
55
|
+
|
56
|
+
# Simulate serverless
|
57
|
+
%(prog)s agent.py --simulate-serverless lambda --exec my_function
|
58
|
+
|
59
|
+
For platform-specific options: %(prog)s --help-platforms
|
60
|
+
For more examples: %(prog)s --help-examples
|
61
|
+
"""
|
@@ -0,0 +1 @@
|
|
1
|
+
"""Core functionality for CLI tools"""
|
@@ -0,0 +1,254 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Agent discovery and loading functionality
|
4
|
+
"""
|
5
|
+
|
6
|
+
import importlib.util
|
7
|
+
from pathlib import Path
|
8
|
+
from typing import List, Dict, Any, Optional
|
9
|
+
|
10
|
+
# Import after checking if available
|
11
|
+
try:
|
12
|
+
from signalwire_agents.core.agent_base import AgentBase
|
13
|
+
AGENT_BASE_AVAILABLE = True
|
14
|
+
except ImportError:
|
15
|
+
AgentBase = None
|
16
|
+
AGENT_BASE_AVAILABLE = False
|
17
|
+
|
18
|
+
|
19
|
+
def discover_agents_in_file(agent_path: str) -> List[Dict[str, Any]]:
|
20
|
+
"""
|
21
|
+
Discover all available agents in a Python file without instantiating them
|
22
|
+
|
23
|
+
Args:
|
24
|
+
agent_path: Path to the Python file containing agents
|
25
|
+
|
26
|
+
Returns:
|
27
|
+
List of dictionaries with agent information
|
28
|
+
|
29
|
+
Raises:
|
30
|
+
ImportError: If the file cannot be imported
|
31
|
+
FileNotFoundError: If the file doesn't exist
|
32
|
+
"""
|
33
|
+
if not AGENT_BASE_AVAILABLE:
|
34
|
+
raise ImportError("AgentBase not available. Please install signalwire-agents package.")
|
35
|
+
|
36
|
+
agent_path = Path(agent_path).resolve()
|
37
|
+
|
38
|
+
if not agent_path.exists():
|
39
|
+
raise FileNotFoundError(f"Agent file not found: {agent_path}")
|
40
|
+
|
41
|
+
if not agent_path.suffix == '.py':
|
42
|
+
raise ValueError(f"Agent file must be a Python file (.py): {agent_path}")
|
43
|
+
|
44
|
+
# Load the module, but prevent main() execution by setting __name__ to something other than "__main__"
|
45
|
+
spec = importlib.util.spec_from_file_location("agent_module", agent_path)
|
46
|
+
module = importlib.util.module_from_spec(spec)
|
47
|
+
|
48
|
+
try:
|
49
|
+
# Set __name__ to prevent if __name__ == "__main__": blocks from running
|
50
|
+
module.__name__ = "agent_module"
|
51
|
+
spec.loader.exec_module(module)
|
52
|
+
except Exception as e:
|
53
|
+
raise ImportError(f"Failed to load agent module: {e}")
|
54
|
+
|
55
|
+
agents_found = []
|
56
|
+
|
57
|
+
# Look for AgentBase instances
|
58
|
+
for name, obj in vars(module).items():
|
59
|
+
if isinstance(obj, AgentBase):
|
60
|
+
agents_found.append({
|
61
|
+
'name': name,
|
62
|
+
'class_name': obj.__class__.__name__,
|
63
|
+
'type': 'instance',
|
64
|
+
'agent_name': getattr(obj, 'name', 'Unknown'),
|
65
|
+
'route': getattr(obj, 'route', 'Unknown'),
|
66
|
+
'description': obj.__class__.__doc__,
|
67
|
+
'object': obj
|
68
|
+
})
|
69
|
+
|
70
|
+
# Look for AgentBase subclasses (that could be instantiated)
|
71
|
+
for name, obj in vars(module).items():
|
72
|
+
if (isinstance(obj, type) and
|
73
|
+
issubclass(obj, AgentBase) and
|
74
|
+
obj != AgentBase):
|
75
|
+
# Check if we already found an instance of this class
|
76
|
+
instance_found = any(agent['class_name'] == name for agent in agents_found)
|
77
|
+
if not instance_found:
|
78
|
+
try:
|
79
|
+
# Try to get class information without instantiating
|
80
|
+
agent_info = {
|
81
|
+
'name': name,
|
82
|
+
'class_name': name,
|
83
|
+
'type': 'class',
|
84
|
+
'agent_name': 'Unknown (not instantiated)',
|
85
|
+
'route': 'Unknown (not instantiated)',
|
86
|
+
'description': obj.__doc__,
|
87
|
+
'object': obj
|
88
|
+
}
|
89
|
+
agents_found.append(agent_info)
|
90
|
+
except Exception:
|
91
|
+
# If we can't get info, still record that the class exists
|
92
|
+
agents_found.append({
|
93
|
+
'name': name,
|
94
|
+
'class_name': name,
|
95
|
+
'type': 'class',
|
96
|
+
'agent_name': 'Unknown (not instantiated)',
|
97
|
+
'route': 'Unknown (not instantiated)',
|
98
|
+
'description': obj.__doc__ or 'No description available',
|
99
|
+
'object': obj
|
100
|
+
})
|
101
|
+
|
102
|
+
return agents_found
|
103
|
+
|
104
|
+
|
105
|
+
def load_agent_from_file(agent_path: str, agent_class_name: Optional[str] = None) -> 'AgentBase':
|
106
|
+
"""
|
107
|
+
Load an agent from a Python file
|
108
|
+
|
109
|
+
Args:
|
110
|
+
agent_path: Path to the Python file containing the agent
|
111
|
+
agent_class_name: Optional name of the agent class to instantiate
|
112
|
+
|
113
|
+
Returns:
|
114
|
+
AgentBase instance
|
115
|
+
|
116
|
+
Raises:
|
117
|
+
ImportError: If the file cannot be imported
|
118
|
+
ValueError: If no agent is found in the file
|
119
|
+
"""
|
120
|
+
if not AGENT_BASE_AVAILABLE:
|
121
|
+
raise ImportError("AgentBase not available. Please install signalwire-agents package.")
|
122
|
+
|
123
|
+
agent_path = Path(agent_path).resolve()
|
124
|
+
|
125
|
+
if not agent_path.exists():
|
126
|
+
raise FileNotFoundError(f"Agent file not found: {agent_path}")
|
127
|
+
|
128
|
+
if not agent_path.suffix == '.py':
|
129
|
+
raise ValueError(f"Agent file must be a Python file (.py): {agent_path}")
|
130
|
+
|
131
|
+
# Load the module, but prevent main() execution by setting __name__ to something other than "__main__"
|
132
|
+
spec = importlib.util.spec_from_file_location("agent_module", agent_path)
|
133
|
+
module = importlib.util.module_from_spec(spec)
|
134
|
+
|
135
|
+
try:
|
136
|
+
# Set __name__ to prevent if __name__ == "__main__": blocks from running
|
137
|
+
module.__name__ = "agent_module"
|
138
|
+
spec.loader.exec_module(module)
|
139
|
+
except Exception as e:
|
140
|
+
raise ImportError(f"Failed to load agent module: {e}")
|
141
|
+
|
142
|
+
# Find the agent instance
|
143
|
+
agent = None
|
144
|
+
|
145
|
+
# If agent_class_name is specified, try to instantiate that specific class first
|
146
|
+
if agent_class_name:
|
147
|
+
if hasattr(module, agent_class_name):
|
148
|
+
obj = getattr(module, agent_class_name)
|
149
|
+
if isinstance(obj, type) and issubclass(obj, AgentBase) and obj != AgentBase:
|
150
|
+
try:
|
151
|
+
agent = obj()
|
152
|
+
if agent and not agent.route.endswith('dummy'): # Avoid test agents with dummy routes
|
153
|
+
pass # Successfully created specific agent
|
154
|
+
else:
|
155
|
+
agent = obj() # Create anyway if requested specifically
|
156
|
+
except Exception as e:
|
157
|
+
raise ValueError(f"Failed to instantiate agent class '{agent_class_name}': {e}")
|
158
|
+
else:
|
159
|
+
raise ValueError(f"'{agent_class_name}' is not a valid AgentBase subclass")
|
160
|
+
else:
|
161
|
+
raise ValueError(f"Agent class '{agent_class_name}' not found in {agent_path}")
|
162
|
+
|
163
|
+
# Strategy 1: Look for 'agent' variable (most common pattern)
|
164
|
+
if agent is None and hasattr(module, 'agent') and isinstance(module.agent, AgentBase):
|
165
|
+
agent = module.agent
|
166
|
+
|
167
|
+
# Strategy 2: Look for any AgentBase instance in module globals
|
168
|
+
if agent is None:
|
169
|
+
agents_found = []
|
170
|
+
for name, obj in vars(module).items():
|
171
|
+
if isinstance(obj, AgentBase):
|
172
|
+
agents_found.append((name, obj))
|
173
|
+
|
174
|
+
if len(agents_found) == 1:
|
175
|
+
agent = agents_found[0][1]
|
176
|
+
elif len(agents_found) > 1:
|
177
|
+
# Multiple agents found, prefer one named 'agent'
|
178
|
+
for name, obj in agents_found:
|
179
|
+
if name == 'agent':
|
180
|
+
agent = obj
|
181
|
+
break
|
182
|
+
# If no 'agent' variable, use the first one
|
183
|
+
if agent is None:
|
184
|
+
agent = agents_found[0][1]
|
185
|
+
print(f"Warning: Multiple agents found, using '{agents_found[0][0]}'")
|
186
|
+
print(f"Hint: Use --agent-class parameter to choose specific agent")
|
187
|
+
|
188
|
+
# Strategy 3: Look for AgentBase subclass and try to instantiate it
|
189
|
+
if agent is None:
|
190
|
+
agent_classes_found = []
|
191
|
+
for name, obj in vars(module).items():
|
192
|
+
if (isinstance(obj, type) and
|
193
|
+
issubclass(obj, AgentBase) and
|
194
|
+
obj != AgentBase):
|
195
|
+
agent_classes_found.append((name, obj))
|
196
|
+
|
197
|
+
if len(agent_classes_found) == 1:
|
198
|
+
try:
|
199
|
+
agent = agent_classes_found[0][1]()
|
200
|
+
except Exception as e:
|
201
|
+
print(f"Warning: Failed to instantiate {agent_classes_found[0][0]}: {e}")
|
202
|
+
elif len(agent_classes_found) > 1:
|
203
|
+
# Multiple agent classes found
|
204
|
+
class_names = [name for name, _ in agent_classes_found]
|
205
|
+
raise ValueError(f"Multiple agent classes found: {', '.join(class_names)}. "
|
206
|
+
f"Please specify which agent class to use with --agent-class parameter. "
|
207
|
+
f"Usage: swaig-test {agent_path} [tool_name] [args] --agent-class <AgentClassName>")
|
208
|
+
else:
|
209
|
+
# Try instantiating any AgentBase class we can find
|
210
|
+
for name, obj in vars(module).items():
|
211
|
+
if (isinstance(obj, type) and
|
212
|
+
issubclass(obj, AgentBase) and
|
213
|
+
obj != AgentBase):
|
214
|
+
try:
|
215
|
+
agent = obj()
|
216
|
+
break
|
217
|
+
except Exception as e:
|
218
|
+
print(f"Warning: Failed to instantiate {name}: {e}")
|
219
|
+
|
220
|
+
# Strategy 4: Try calling a modified main() function that doesn't start the server
|
221
|
+
if agent is None and hasattr(module, 'main'):
|
222
|
+
print("Warning: No agent instance found, attempting to call main() without server startup")
|
223
|
+
try:
|
224
|
+
# Temporarily patch AgentBase.serve to prevent server startup
|
225
|
+
original_serve = AgentBase.serve
|
226
|
+
captured_agent = []
|
227
|
+
|
228
|
+
def mock_serve(self, *args, **kwargs):
|
229
|
+
captured_agent.append(self)
|
230
|
+
print(f" (Intercepted serve() call, agent captured for testing)")
|
231
|
+
return self
|
232
|
+
|
233
|
+
AgentBase.serve = mock_serve
|
234
|
+
|
235
|
+
try:
|
236
|
+
result = module.main()
|
237
|
+
if isinstance(result, AgentBase):
|
238
|
+
agent = result
|
239
|
+
elif captured_agent:
|
240
|
+
agent = captured_agent[0]
|
241
|
+
finally:
|
242
|
+
# Restore original serve method
|
243
|
+
AgentBase.serve = original_serve
|
244
|
+
|
245
|
+
except Exception as e:
|
246
|
+
print(f"Warning: Failed to call main() function: {e}")
|
247
|
+
|
248
|
+
if agent is None:
|
249
|
+
raise ValueError(f"No agent found in {agent_path}. The file must contain either:\n"
|
250
|
+
f"- An AgentBase instance (e.g., agent = MyAgent())\n"
|
251
|
+
f"- An AgentBase subclass that can be instantiated\n"
|
252
|
+
f"- A main() function that creates and returns an agent")
|
253
|
+
|
254
|
+
return agent
|