signalwire-agents 0.1.27__tar.gz → 0.1.29__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.27 → signalwire_agents-0.1.29}/CHANGELOG.md +8 -0
- {signalwire_agents-0.1.27/signalwire_agents.egg-info → signalwire_agents-0.1.29}/PKG-INFO +1 -59
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/README.md +0 -58
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/pyproject.toml +1 -1
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/__init__.py +1 -4
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/cli/config.py +11 -1
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/cli/simulation/data_overrides.py +6 -2
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/cli/test_swaig.py +6 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/agent_base.py +1 -12
- signalwire_agents-0.1.29/signalwire_agents/core/auth_handler.py +233 -0
- signalwire_agents-0.1.29/signalwire_agents/core/config_loader.py +259 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/contexts.py +75 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/mixins/state_mixin.py +1 -67
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/mixins/tool_mixin.py +0 -65
- signalwire_agents-0.1.29/signalwire_agents/core/security_config.py +333 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/swml_service.py +19 -25
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/prefabs/concierge.py +0 -3
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/prefabs/faq_bot.py +0 -3
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/prefabs/info_gatherer.py +0 -3
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/prefabs/receptionist.py +0 -3
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/prefabs/survey.py +0 -3
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/search/search_service.py +200 -11
- signalwire_agents-0.1.29/signalwire_agents/skills/mcp_gateway/README.md +230 -0
- signalwire_agents-0.1.29/signalwire_agents/skills/mcp_gateway/__init__.py +1 -0
- signalwire_agents-0.1.29/signalwire_agents/skills/mcp_gateway/skill.py +339 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29/signalwire_agents.egg-info}/PKG-INFO +1 -59
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents.egg-info/SOURCES.txt +6 -3
- signalwire_agents-0.1.27/signalwire_agents/core/state/__init__.py +0 -17
- signalwire_agents-0.1.27/signalwire_agents/core/state/file_state_manager.py +0 -219
- signalwire_agents-0.1.27/signalwire_agents/core/state/state_manager.py +0 -101
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/LICENSE +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/requirements-dev.txt +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/requirements.txt +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/schema.json +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/setup.cfg +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/setup.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/agent_server.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/cli/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/cli/build_search.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/cli/core/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/cli/core/agent_loader.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/cli/core/argparse_helpers.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/cli/core/dynamic_config.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/cli/execution/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/cli/execution/datamap_exec.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/cli/execution/webhook_exec.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/cli/output/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/cli/output/output_formatter.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/cli/output/swml_dump.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/cli/simulation/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/cli/simulation/data_generation.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/cli/simulation/mock_env.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/cli/types.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/agent/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/agent/config/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/agent/deployment/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/agent/deployment/handlers/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/agent/prompt/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/agent/prompt/manager.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/agent/routing/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/agent/security/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/agent/swml/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/agent/tools/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/agent/tools/decorator.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/agent/tools/registry.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/data_map.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/function_result.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/logging_config.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/mixins/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/mixins/ai_config_mixin.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/mixins/auth_mixin.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/mixins/prompt_mixin.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/mixins/serverless_mixin.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/mixins/skill_mixin.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/mixins/web_mixin.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/pom_builder.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/security/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/security/session_manager.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/skill_base.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/skill_manager.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/swaig_function.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/swml_builder.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/swml_handler.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/core/swml_renderer.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/prefabs/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/schema.json +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/search/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/search/document_processor.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/search/index_builder.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/search/query_processor.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/search/search_engine.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/README.md +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/api_ninjas_trivia/README.md +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/api_ninjas_trivia/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/api_ninjas_trivia/skill.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/datasphere/README.md +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/datasphere/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/datasphere/skill.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/datasphere_serverless/README.md +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/datasphere_serverless/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/datasphere_serverless/skill.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/datetime/README.md +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/datetime/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/datetime/skill.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/joke/README.md +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/joke/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/joke/skill.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/math/README.md +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/math/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/math/skill.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/native_vector_search/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/native_vector_search/skill.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/play_background_file/README.md +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/play_background_file/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/play_background_file/skill.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/registry.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/spider/README.md +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/spider/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/spider/skill.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/swml_transfer/README.md +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/swml_transfer/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/swml_transfer/skill.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/weather_api/README.md +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/weather_api/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/weather_api/skill.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/web_search/README.md +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/web_search/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/web_search/skill.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/wikipedia_search/README.md +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/wikipedia_search/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/skills/wikipedia_search/skill.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/utils/__init__.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/utils/pom_utils.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/utils/schema_utils.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/utils/token_generators.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents/utils/validators.py +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents.egg-info/dependency_links.txt +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents.egg-info/entry_points.txt +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents.egg-info/requires.txt +0 -0
- {signalwire_agents-0.1.27 → signalwire_agents-0.1.29}/signalwire_agents.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: signalwire_agents
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.29
|
4
4
|
Summary: SignalWire AI Agents SDK
|
5
5
|
Author-email: SignalWire Team <info@signalwire.com>
|
6
6
|
License: MIT
|
@@ -593,64 +593,6 @@ if __name__ == "__main__":
|
|
593
593
|
agent.serve(host="0.0.0.0", port=8000)
|
594
594
|
```
|
595
595
|
|
596
|
-
## Using State Management
|
597
|
-
|
598
|
-
```python
|
599
|
-
from signalwire_agents import AgentBase
|
600
|
-
from signalwire_agents.core.function_result import SwaigFunctionResult
|
601
|
-
from signalwire_agents.core.state import FileStateManager
|
602
|
-
|
603
|
-
class StatefulAgent(AgentBase):
|
604
|
-
def __init__(self):
|
605
|
-
# Configure state management
|
606
|
-
state_manager = FileStateManager(storage_dir="./state_data")
|
607
|
-
|
608
|
-
super().__init__(
|
609
|
-
name="stateful",
|
610
|
-
route="/stateful",
|
611
|
-
enable_state_tracking=True, # Enable state tracking
|
612
|
-
state_manager=state_manager # Use custom state manager
|
613
|
-
)
|
614
|
-
|
615
|
-
# When enable_state_tracking=True, startup_hook and hangup_hook
|
616
|
-
# are automatically registered to track session lifecycle
|
617
|
-
|
618
|
-
# Custom tool for accessing and updating state
|
619
|
-
@AgentBase.tool(
|
620
|
-
name="save_preference",
|
621
|
-
description="Save a user preference",
|
622
|
-
parameters={
|
623
|
-
"preference_name": {
|
624
|
-
"type": "string",
|
625
|
-
"description": "Name of the preference to save"
|
626
|
-
},
|
627
|
-
"preference_value": {
|
628
|
-
"type": "string",
|
629
|
-
"description": "Value of the preference"
|
630
|
-
}
|
631
|
-
}
|
632
|
-
)
|
633
|
-
def save_preference(self, args, raw_data):
|
634
|
-
# Get the call ID from the raw data
|
635
|
-
call_id = raw_data.get("call_id")
|
636
|
-
|
637
|
-
if call_id:
|
638
|
-
# Get current state or empty dict if none exists
|
639
|
-
state = self.get_state(call_id) or {}
|
640
|
-
|
641
|
-
# Update the state
|
642
|
-
preferences = state.get("preferences", {})
|
643
|
-
preferences[args.get("preference_name")] = args.get("preference_value")
|
644
|
-
state["preferences"] = preferences
|
645
|
-
|
646
|
-
# Save the updated state
|
647
|
-
self.update_state(call_id, state)
|
648
|
-
|
649
|
-
return SwaigFunctionResult("Preference saved successfully")
|
650
|
-
else:
|
651
|
-
return SwaigFunctionResult("Could not save preference: No call ID")
|
652
|
-
```
|
653
|
-
|
654
596
|
## Using Prefab Agents
|
655
597
|
|
656
598
|
```python
|
@@ -519,64 +519,6 @@ if __name__ == "__main__":
|
|
519
519
|
agent.serve(host="0.0.0.0", port=8000)
|
520
520
|
```
|
521
521
|
|
522
|
-
## Using State Management
|
523
|
-
|
524
|
-
```python
|
525
|
-
from signalwire_agents import AgentBase
|
526
|
-
from signalwire_agents.core.function_result import SwaigFunctionResult
|
527
|
-
from signalwire_agents.core.state import FileStateManager
|
528
|
-
|
529
|
-
class StatefulAgent(AgentBase):
|
530
|
-
def __init__(self):
|
531
|
-
# Configure state management
|
532
|
-
state_manager = FileStateManager(storage_dir="./state_data")
|
533
|
-
|
534
|
-
super().__init__(
|
535
|
-
name="stateful",
|
536
|
-
route="/stateful",
|
537
|
-
enable_state_tracking=True, # Enable state tracking
|
538
|
-
state_manager=state_manager # Use custom state manager
|
539
|
-
)
|
540
|
-
|
541
|
-
# When enable_state_tracking=True, startup_hook and hangup_hook
|
542
|
-
# are automatically registered to track session lifecycle
|
543
|
-
|
544
|
-
# Custom tool for accessing and updating state
|
545
|
-
@AgentBase.tool(
|
546
|
-
name="save_preference",
|
547
|
-
description="Save a user preference",
|
548
|
-
parameters={
|
549
|
-
"preference_name": {
|
550
|
-
"type": "string",
|
551
|
-
"description": "Name of the preference to save"
|
552
|
-
},
|
553
|
-
"preference_value": {
|
554
|
-
"type": "string",
|
555
|
-
"description": "Value of the preference"
|
556
|
-
}
|
557
|
-
}
|
558
|
-
)
|
559
|
-
def save_preference(self, args, raw_data):
|
560
|
-
# Get the call ID from the raw data
|
561
|
-
call_id = raw_data.get("call_id")
|
562
|
-
|
563
|
-
if call_id:
|
564
|
-
# Get current state or empty dict if none exists
|
565
|
-
state = self.get_state(call_id) or {}
|
566
|
-
|
567
|
-
# Update the state
|
568
|
-
preferences = state.get("preferences", {})
|
569
|
-
preferences[args.get("preference_name")] = args.get("preference_value")
|
570
|
-
state["preferences"] = preferences
|
571
|
-
|
572
|
-
# Save the updated state
|
573
|
-
self.update_state(call_id, state)
|
574
|
-
|
575
|
-
return SwaigFunctionResult("Preference saved successfully")
|
576
|
-
else:
|
577
|
-
return SwaigFunctionResult("Could not save preference: No call ID")
|
578
|
-
```
|
579
|
-
|
580
522
|
## Using Prefab Agents
|
581
523
|
|
582
524
|
```python
|
@@ -18,13 +18,12 @@ 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.29"
|
22
22
|
|
23
23
|
# Import core classes for easier access
|
24
24
|
from .core.agent_base import AgentBase
|
25
25
|
from .core.contexts import ContextBuilder, Context, Step, create_simple_context
|
26
26
|
from .core.data_map import DataMap, create_simple_api_tool, create_expression_tool
|
27
|
-
from .core.state import StateManager, FileStateManager
|
28
27
|
from signalwire_agents.agent_server import AgentServer
|
29
28
|
from signalwire_agents.core.swml_service import SWMLService
|
30
29
|
from signalwire_agents.core.swml_builder import SWMLBuilder
|
@@ -68,8 +67,6 @@ __all__ = [
|
|
68
67
|
"AgentServer",
|
69
68
|
"SWMLService",
|
70
69
|
"SWMLBuilder",
|
71
|
-
"StateManager",
|
72
|
-
"FileStateManager",
|
73
70
|
"SwaigFunctionResult",
|
74
71
|
"SWAIGFunction",
|
75
72
|
"DataMap",
|
@@ -41,12 +41,22 @@ ERROR_FUNCTION_NOT_FOUND = "Function '{function_name}' not found in agent"
|
|
41
41
|
ERROR_CGI_HOST_REQUIRED = "CGI simulation requires --cgi-host"
|
42
42
|
|
43
43
|
# Help messages
|
44
|
-
HELP_DESCRIPTION = "Test SWAIG functions and generate SWML documents for SignalWire AI agents
|
44
|
+
HELP_DESCRIPTION = """Test SWAIG functions and generate SWML documents for SignalWire AI agents
|
45
|
+
|
46
|
+
IMPORTANT: When using --exec, ALL options (like --call-id, --verbose, etc.) must come BEFORE --exec.
|
47
|
+
Everything after --exec <function_name> is treated as arguments to the function."""
|
48
|
+
|
45
49
|
HELP_EPILOG_SHORT = """
|
46
50
|
examples:
|
47
51
|
# Execute a function
|
48
52
|
%(prog)s agent.py --exec search --query "test" --limit 5
|
49
53
|
|
54
|
+
# Execute with persistent session (--call-id MUST come BEFORE --exec)
|
55
|
+
%(prog)s agent.py --call-id my-session --exec add_todo --text "Buy milk"
|
56
|
+
|
57
|
+
# WRONG: This won't work! --call-id is treated as a function argument
|
58
|
+
%(prog)s agent.py --exec add_todo --text "Buy milk" --call-id my-session
|
59
|
+
|
50
60
|
# Generate SWML
|
51
61
|
%(prog)s agent.py --dump-swml --raw | jq '.'
|
52
62
|
|
@@ -120,8 +120,12 @@ def apply_convenience_mappings(data: Dict[str, Any], args: argparse.Namespace) -
|
|
120
120
|
|
121
121
|
# Map high-level arguments to specific paths
|
122
122
|
if hasattr(args, 'call_id') and args.call_id:
|
123
|
-
|
124
|
-
|
123
|
+
# Set at root level for SWAIG functions
|
124
|
+
data["call_id"] = args.call_id
|
125
|
+
# Also set in call object if it exists
|
126
|
+
if "call" in data:
|
127
|
+
set_nested_value(data, "call.call_id", args.call_id)
|
128
|
+
set_nested_value(data, "call.tag", args.call_id) # tag often matches call_id
|
125
129
|
|
126
130
|
if hasattr(args, 'project_id') and args.project_id:
|
127
131
|
set_nested_value(data, "call.project_id", args.project_id)
|
@@ -727,6 +727,12 @@ def main():
|
|
727
727
|
# Default behavior - minimal data
|
728
728
|
post_data = generate_minimal_post_data(args.tool_name, function_args)
|
729
729
|
|
730
|
+
# Apply convenience mappings from CLI args (e.g., --call-id)
|
731
|
+
post_data = apply_convenience_mappings(post_data, args)
|
732
|
+
|
733
|
+
# Apply explicit overrides
|
734
|
+
post_data = apply_overrides(post_data, args.override, args.override_json)
|
735
|
+
|
730
736
|
if args.verbose:
|
731
737
|
print(f"Post data: {json.dumps(post_data, indent=2)}")
|
732
738
|
print("-" * 60)
|
@@ -49,7 +49,6 @@ from signalwire_agents.core.swaig_function import SWAIGFunction
|
|
49
49
|
from signalwire_agents.core.function_result import SwaigFunctionResult
|
50
50
|
from signalwire_agents.core.swml_renderer import SwmlRenderer
|
51
51
|
from signalwire_agents.core.security.session_manager import SessionManager
|
52
|
-
from signalwire_agents.core.state import StateManager, FileStateManager
|
53
52
|
from signalwire_agents.core.swml_service import SWMLService
|
54
53
|
from signalwire_agents.core.swml_handler import AIVerbHandler
|
55
54
|
from signalwire_agents.core.skill_manager import SkillManager
|
@@ -113,13 +112,11 @@ class AgentBase(
|
|
113
112
|
port: int = 3000,
|
114
113
|
basic_auth: Optional[Tuple[str, str]] = None,
|
115
114
|
use_pom: bool = True,
|
116
|
-
enable_state_tracking: bool = False,
|
117
115
|
token_expiry_secs: int = 3600,
|
118
116
|
auto_answer: bool = True,
|
119
117
|
record_call: bool = False,
|
120
118
|
record_format: str = "mp4",
|
121
119
|
record_stereo: bool = True,
|
122
|
-
state_manager: Optional[StateManager] = None,
|
123
120
|
default_webhook_url: Optional[str] = None,
|
124
121
|
agent_id: Optional[str] = None,
|
125
122
|
native_functions: Optional[List[str]] = None,
|
@@ -138,13 +135,11 @@ class AgentBase(
|
|
138
135
|
port: Port to bind the web server to
|
139
136
|
basic_auth: Optional (username, password) tuple for basic auth
|
140
137
|
use_pom: Whether to use POM for prompt building
|
141
|
-
enable_state_tracking: Whether to register startup_hook and hangup_hook SWAIG functions to track conversation state
|
142
138
|
token_expiry_secs: Seconds until tokens expire
|
143
139
|
auto_answer: Whether to automatically answer calls
|
144
140
|
record_call: Whether to record calls
|
145
141
|
record_format: Recording format
|
146
142
|
record_stereo: Whether to record in stereo
|
147
|
-
state_manager: Optional state manager for this agent
|
148
143
|
default_webhook_url: Optional default webhook URL for all SWAIG functions
|
149
144
|
agent_id: Optional unique ID for this agent, generated if not provided
|
150
145
|
native_functions: Optional list of native functions to include in the SWAIG object
|
@@ -207,7 +202,6 @@ class AgentBase(
|
|
207
202
|
|
208
203
|
# Initialize session manager
|
209
204
|
self._session_manager = SessionManager(token_expiry_secs=token_expiry_secs)
|
210
|
-
self._enable_state_tracking = enable_state_tracking
|
211
205
|
|
212
206
|
# URL override variables
|
213
207
|
self._web_hook_url_override = None
|
@@ -229,8 +223,6 @@ class AgentBase(
|
|
229
223
|
# Process declarative PROMPT_SECTIONS if defined in subclass
|
230
224
|
self._process_prompt_sections()
|
231
225
|
|
232
|
-
# Initialize state manager
|
233
|
-
self._state_manager = state_manager or FileStateManager()
|
234
226
|
|
235
227
|
# Process class-decorated tools (using @AgentBase.tool)
|
236
228
|
self._tool_registry.register_class_decorated_tools()
|
@@ -238,9 +230,6 @@ class AgentBase(
|
|
238
230
|
# Add native_functions parameter
|
239
231
|
self.native_functions = native_functions or []
|
240
232
|
|
241
|
-
# Register state tracking tools if enabled
|
242
|
-
if enable_state_tracking:
|
243
|
-
self._register_state_tracking_tools()
|
244
233
|
|
245
234
|
# Initialize new configuration containers
|
246
235
|
self._hints = []
|
@@ -581,7 +570,7 @@ class AgentBase(
|
|
581
570
|
post_prompt = agent_to_use.get_post_prompt()
|
582
571
|
|
583
572
|
# Generate a call ID if needed
|
584
|
-
if
|
573
|
+
if call_id is None:
|
585
574
|
call_id = agent_to_use._session_manager.create_session()
|
586
575
|
|
587
576
|
# Start with any SWAIG query params that were set
|
@@ -0,0 +1,233 @@
|
|
1
|
+
"""
|
2
|
+
Copyright (c) 2025 SignalWire
|
3
|
+
|
4
|
+
This file is part of the SignalWire AI Agents SDK.
|
5
|
+
|
6
|
+
Licensed under the MIT License.
|
7
|
+
See LICENSE file in the project root for full license information.
|
8
|
+
"""
|
9
|
+
|
10
|
+
import secrets
|
11
|
+
from typing import Optional, Tuple, Dict, Any, Callable
|
12
|
+
from functools import wraps
|
13
|
+
|
14
|
+
try:
|
15
|
+
from fastapi import HTTPException, Depends
|
16
|
+
from fastapi.security import HTTPBasic, HTTPBasicCredentials, HTTPBearer, HTTPAuthorizationCredentials
|
17
|
+
except ImportError:
|
18
|
+
HTTPException = None
|
19
|
+
Depends = None
|
20
|
+
HTTPBasic = None
|
21
|
+
HTTPBasicCredentials = None
|
22
|
+
HTTPBearer = None
|
23
|
+
HTTPAuthorizationCredentials = None
|
24
|
+
|
25
|
+
from signalwire_agents.core.logging_config import get_logger
|
26
|
+
|
27
|
+
logger = get_logger("auth_handler")
|
28
|
+
|
29
|
+
|
30
|
+
class AuthHandler:
|
31
|
+
"""
|
32
|
+
Unified authentication handler supporting multiple auth methods.
|
33
|
+
|
34
|
+
This class provides a clean pattern for handling Basic Auth, Bearer tokens,
|
35
|
+
and API keys across all SignalWire services.
|
36
|
+
"""
|
37
|
+
|
38
|
+
def __init__(self, security_config: 'SecurityConfig'):
|
39
|
+
"""
|
40
|
+
Initialize auth handler with security configuration.
|
41
|
+
|
42
|
+
Args:
|
43
|
+
security_config: SecurityConfig instance with auth settings
|
44
|
+
"""
|
45
|
+
self.security_config = security_config
|
46
|
+
self.basic_auth = HTTPBasic() if HTTPBasic else None
|
47
|
+
self.bearer_auth = HTTPBearer(auto_error=False) if HTTPBearer else None
|
48
|
+
|
49
|
+
# Get auth methods from config
|
50
|
+
self._setup_auth_methods()
|
51
|
+
|
52
|
+
def _setup_auth_methods(self):
|
53
|
+
"""Setup enabled authentication methods from config"""
|
54
|
+
self.auth_methods = {}
|
55
|
+
|
56
|
+
# Basic auth (always available for backward compatibility)
|
57
|
+
username, password = self.security_config.get_basic_auth()
|
58
|
+
self.auth_methods['basic'] = {
|
59
|
+
'enabled': True,
|
60
|
+
'username': username,
|
61
|
+
'password': password
|
62
|
+
}
|
63
|
+
|
64
|
+
# Bearer token (if configured)
|
65
|
+
bearer_token = getattr(self.security_config, 'bearer_token', None)
|
66
|
+
if bearer_token:
|
67
|
+
self.auth_methods['bearer'] = {
|
68
|
+
'enabled': True,
|
69
|
+
'token': bearer_token
|
70
|
+
}
|
71
|
+
|
72
|
+
# API key (if configured)
|
73
|
+
api_key = getattr(self.security_config, 'api_key', None)
|
74
|
+
if api_key:
|
75
|
+
self.auth_methods['api_key'] = {
|
76
|
+
'enabled': True,
|
77
|
+
'key': api_key,
|
78
|
+
'header': getattr(self.security_config, 'api_key_header', 'X-API-Key')
|
79
|
+
}
|
80
|
+
|
81
|
+
def verify_basic_auth(self, credentials: HTTPBasicCredentials) -> bool:
|
82
|
+
"""Verify basic auth credentials"""
|
83
|
+
if not self.auth_methods.get('basic', {}).get('enabled'):
|
84
|
+
return False
|
85
|
+
|
86
|
+
basic_config = self.auth_methods['basic']
|
87
|
+
username_correct = secrets.compare_digest(
|
88
|
+
credentials.username, basic_config['username']
|
89
|
+
)
|
90
|
+
password_correct = secrets.compare_digest(
|
91
|
+
credentials.password, basic_config['password']
|
92
|
+
)
|
93
|
+
|
94
|
+
return username_correct and password_correct
|
95
|
+
|
96
|
+
def verify_bearer_token(self, credentials: HTTPAuthorizationCredentials) -> bool:
|
97
|
+
"""Verify bearer token"""
|
98
|
+
if not self.auth_methods.get('bearer', {}).get('enabled'):
|
99
|
+
return False
|
100
|
+
|
101
|
+
bearer_config = self.auth_methods['bearer']
|
102
|
+
return secrets.compare_digest(
|
103
|
+
credentials.credentials, bearer_config['token']
|
104
|
+
)
|
105
|
+
|
106
|
+
def verify_api_key(self, api_key: str) -> bool:
|
107
|
+
"""Verify API key"""
|
108
|
+
if not self.auth_methods.get('api_key', {}).get('enabled'):
|
109
|
+
return False
|
110
|
+
|
111
|
+
api_config = self.auth_methods['api_key']
|
112
|
+
return secrets.compare_digest(api_key, api_config['key'])
|
113
|
+
|
114
|
+
def get_fastapi_dependency(self, optional: bool = False):
|
115
|
+
"""
|
116
|
+
Get FastAPI dependency for authentication.
|
117
|
+
|
118
|
+
Args:
|
119
|
+
optional: If True, authentication is optional
|
120
|
+
|
121
|
+
Returns:
|
122
|
+
FastAPI dependency function
|
123
|
+
"""
|
124
|
+
if not Depends:
|
125
|
+
return None
|
126
|
+
|
127
|
+
async def auth_dependency(
|
128
|
+
basic_credentials: Optional[HTTPBasicCredentials] = Depends(self.basic_auth) if self.basic_auth else None,
|
129
|
+
bearer_credentials: Optional[HTTPAuthorizationCredentials] = Depends(self.bearer_auth) if self.bearer_auth else None,
|
130
|
+
api_key: Optional[str] = None # Get from header in request
|
131
|
+
):
|
132
|
+
# Try each auth method
|
133
|
+
authenticated = False
|
134
|
+
auth_method = None
|
135
|
+
|
136
|
+
# Try bearer token first (if provided)
|
137
|
+
if bearer_credentials and self.verify_bearer_token(bearer_credentials):
|
138
|
+
authenticated = True
|
139
|
+
auth_method = 'bearer'
|
140
|
+
|
141
|
+
# Try basic auth
|
142
|
+
elif basic_credentials and self.verify_basic_auth(basic_credentials):
|
143
|
+
authenticated = True
|
144
|
+
auth_method = 'basic'
|
145
|
+
|
146
|
+
# Try API key (would need to be extracted from request headers)
|
147
|
+
# This is a simplified version - in practice, you'd get it from request
|
148
|
+
|
149
|
+
if not authenticated and not optional:
|
150
|
+
raise HTTPException(
|
151
|
+
status_code=401,
|
152
|
+
detail="Invalid authentication credentials",
|
153
|
+
headers={"WWW-Authenticate": "Basic"},
|
154
|
+
)
|
155
|
+
|
156
|
+
return {'authenticated': authenticated, 'method': auth_method}
|
157
|
+
|
158
|
+
return auth_dependency
|
159
|
+
|
160
|
+
def flask_decorator(self, f: Callable) -> Callable:
|
161
|
+
"""
|
162
|
+
Flask decorator for authentication.
|
163
|
+
|
164
|
+
This provides compatibility with Flask-based services like MCP Gateway.
|
165
|
+
"""
|
166
|
+
@wraps(f)
|
167
|
+
def decorated(*args, **kwargs):
|
168
|
+
from flask import request, Response
|
169
|
+
|
170
|
+
# Try Bearer token first
|
171
|
+
auth_header = request.headers.get('Authorization', '')
|
172
|
+
|
173
|
+
if auth_header.startswith('Bearer ') and self.auth_methods.get('bearer', {}).get('enabled'):
|
174
|
+
token = auth_header[7:]
|
175
|
+
if secrets.compare_digest(token, self.auth_methods['bearer']['token']):
|
176
|
+
return f(*args, **kwargs)
|
177
|
+
|
178
|
+
# Try API key
|
179
|
+
if self.auth_methods.get('api_key', {}).get('enabled'):
|
180
|
+
api_config = self.auth_methods['api_key']
|
181
|
+
api_key = request.headers.get(api_config['header'])
|
182
|
+
if api_key and secrets.compare_digest(api_key, api_config['key']):
|
183
|
+
return f(*args, **kwargs)
|
184
|
+
|
185
|
+
# Fall back to Basic auth
|
186
|
+
auth = request.authorization
|
187
|
+
if auth and self.auth_methods.get('basic', {}).get('enabled'):
|
188
|
+
basic_config = self.auth_methods['basic']
|
189
|
+
if auth.username == basic_config['username'] and \
|
190
|
+
auth.password == basic_config['password']:
|
191
|
+
return f(*args, **kwargs)
|
192
|
+
|
193
|
+
# Authentication failed
|
194
|
+
logger.warning(
|
195
|
+
"auth_failed",
|
196
|
+
ip=request.remote_addr,
|
197
|
+
method=request.method,
|
198
|
+
path=request.path
|
199
|
+
)
|
200
|
+
|
201
|
+
return Response(
|
202
|
+
'Authentication required',
|
203
|
+
401,
|
204
|
+
{'WWW-Authenticate': 'Basic realm="SignalWire Service"'}
|
205
|
+
)
|
206
|
+
|
207
|
+
return decorated
|
208
|
+
|
209
|
+
def get_auth_info(self) -> Dict[str, Any]:
|
210
|
+
"""Get information about configured auth methods"""
|
211
|
+
info = {}
|
212
|
+
|
213
|
+
if self.auth_methods.get('basic', {}).get('enabled'):
|
214
|
+
info['basic'] = {
|
215
|
+
'enabled': True,
|
216
|
+
'username': self.auth_methods['basic']['username']
|
217
|
+
}
|
218
|
+
|
219
|
+
if self.auth_methods.get('bearer', {}).get('enabled'):
|
220
|
+
info['bearer'] = {
|
221
|
+
'enabled': True,
|
222
|
+
'hint': 'Use Authorization: Bearer <token>'
|
223
|
+
}
|
224
|
+
|
225
|
+
if self.auth_methods.get('api_key', {}).get('enabled'):
|
226
|
+
api_config = self.auth_methods['api_key']
|
227
|
+
info['api_key'] = {
|
228
|
+
'enabled': True,
|
229
|
+
'header': api_config['header'],
|
230
|
+
'hint': f'Use {api_config["header"]}: <key>'
|
231
|
+
}
|
232
|
+
|
233
|
+
return info
|