signalwire-agents 1.0.13__py3-none-any.whl → 1.0.15__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 +1 -1
- signalwire_agents/agent_server.py +80 -61
- signalwire_agents/cli/dokku.py +945 -151
- {signalwire_agents-1.0.13.dist-info → signalwire_agents-1.0.15.dist-info}/METADATA +1 -1
- {signalwire_agents-1.0.13.dist-info → signalwire_agents-1.0.15.dist-info}/RECORD +12 -12
- {signalwire_agents-1.0.13.data → signalwire_agents-1.0.15.data}/data/share/man/man1/sw-agent-init.1 +0 -0
- {signalwire_agents-1.0.13.data → signalwire_agents-1.0.15.data}/data/share/man/man1/sw-search.1 +0 -0
- {signalwire_agents-1.0.13.data → signalwire_agents-1.0.15.data}/data/share/man/man1/swaig-test.1 +0 -0
- {signalwire_agents-1.0.13.dist-info → signalwire_agents-1.0.15.dist-info}/WHEEL +0 -0
- {signalwire_agents-1.0.13.dist-info → signalwire_agents-1.0.15.dist-info}/entry_points.txt +0 -0
- {signalwire_agents-1.0.13.dist-info → signalwire_agents-1.0.15.dist-info}/licenses/LICENSE +0 -0
- {signalwire_agents-1.0.13.dist-info → signalwire_agents-1.0.15.dist-info}/top_level.txt +0 -0
signalwire_agents/__init__.py
CHANGED
|
@@ -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__ = "1.0.
|
|
21
|
+
__version__ = "1.0.15"
|
|
22
22
|
|
|
23
23
|
# Import core classes for easier access
|
|
24
24
|
from .core.agent_base import AgentBase
|
|
@@ -76,6 +76,13 @@ class AgentServer:
|
|
|
76
76
|
# Register health endpoints immediately so they're available
|
|
77
77
|
# whether using server.run() or server.app with gunicorn
|
|
78
78
|
self._register_health_endpoints()
|
|
79
|
+
|
|
80
|
+
# Register catch-all handler on startup (not in __init__) so it runs AFTER
|
|
81
|
+
# all other routes are registered. This ensures custom routes like /get_token
|
|
82
|
+
# don't get overshadowed by the catch-all /{full_path:path} route.
|
|
83
|
+
@self.app.on_event("startup")
|
|
84
|
+
async def _setup_catch_all():
|
|
85
|
+
self._register_catch_all_handler()
|
|
79
86
|
|
|
80
87
|
def register(self, agent: AgentBase, route: Optional[str] = None) -> None:
|
|
81
88
|
"""
|
|
@@ -104,12 +111,12 @@ class AgentServer:
|
|
|
104
111
|
|
|
105
112
|
# Store the agent
|
|
106
113
|
self.agents[route] = agent
|
|
107
|
-
|
|
114
|
+
|
|
108
115
|
# Get the router and register it using the standard approach
|
|
109
116
|
# The agent's router already handles both trailing slash versions properly
|
|
110
117
|
router = agent.as_router()
|
|
111
118
|
self.app.include_router(router, prefix=route)
|
|
112
|
-
|
|
119
|
+
|
|
113
120
|
self.logger.info(f"Registered agent '{agent.get_name()}' at route '{route}'")
|
|
114
121
|
|
|
115
122
|
# If SIP routing is enabled and auto-mapping is on, register SIP usernames for this agent
|
|
@@ -550,65 +557,6 @@ class AgentServer:
|
|
|
550
557
|
if not self.agents:
|
|
551
558
|
self.logger.warning("Starting server with no registered agents")
|
|
552
559
|
|
|
553
|
-
# Add catch-all route handler to handle both trailing slash and non-trailing slash versions
|
|
554
|
-
@self.app.get("/{full_path:path}")
|
|
555
|
-
@self.app.post("/{full_path:path}")
|
|
556
|
-
async def handle_all_routes(request: Request, full_path: str):
|
|
557
|
-
"""Handle requests that don't match registered routes (e.g. /matti instead of /matti/)"""
|
|
558
|
-
# Check if this path maps to one of our registered agents
|
|
559
|
-
for route, agent in self.agents.items():
|
|
560
|
-
# Check for exact match with registered route
|
|
561
|
-
if full_path == route.lstrip("/"):
|
|
562
|
-
# This is a request to an agent's root without trailing slash
|
|
563
|
-
return await agent._handle_root_request(request)
|
|
564
|
-
elif full_path.startswith(route.lstrip("/") + "/"):
|
|
565
|
-
# This is a request to an agent's sub-path
|
|
566
|
-
relative_path = full_path[len(route.lstrip("/")):]
|
|
567
|
-
relative_path = relative_path.lstrip("/")
|
|
568
|
-
|
|
569
|
-
# Route to appropriate handler based on path
|
|
570
|
-
if not relative_path or relative_path == "/":
|
|
571
|
-
return await agent._handle_root_request(request)
|
|
572
|
-
|
|
573
|
-
clean_path = relative_path.rstrip("/")
|
|
574
|
-
if clean_path == "debug":
|
|
575
|
-
return await agent._handle_debug_request(request)
|
|
576
|
-
elif clean_path == "swaig":
|
|
577
|
-
from fastapi import Response
|
|
578
|
-
return await agent._handle_swaig_request(request, Response())
|
|
579
|
-
elif clean_path == "post_prompt":
|
|
580
|
-
return await agent._handle_post_prompt_request(request)
|
|
581
|
-
elif clean_path == "check_for_input":
|
|
582
|
-
return await agent._handle_check_for_input_request(request)
|
|
583
|
-
|
|
584
|
-
# Check for custom routing callbacks
|
|
585
|
-
if hasattr(agent, '_routing_callbacks'):
|
|
586
|
-
for callback_path, callback_fn in agent._routing_callbacks.items():
|
|
587
|
-
cb_path_clean = callback_path.strip("/")
|
|
588
|
-
if clean_path == cb_path_clean:
|
|
589
|
-
request.state.callback_path = callback_path
|
|
590
|
-
return await agent._handle_root_request(request)
|
|
591
|
-
|
|
592
|
-
# No matching agent - check for static files
|
|
593
|
-
if hasattr(self, '_static_directories'):
|
|
594
|
-
# Check each static directory route
|
|
595
|
-
for static_route, static_dir in self._static_directories.items():
|
|
596
|
-
# For root static route, serve any unmatched path
|
|
597
|
-
if static_route == "" or static_route == "/":
|
|
598
|
-
response = self._serve_static_file(full_path, "")
|
|
599
|
-
if response:
|
|
600
|
-
return response
|
|
601
|
-
# For prefixed static routes, check if path matches
|
|
602
|
-
elif full_path.startswith(static_route.lstrip("/") + "/") or full_path == static_route.lstrip("/"):
|
|
603
|
-
relative_path = full_path[len(static_route.lstrip("/")):].lstrip("/")
|
|
604
|
-
response = self._serve_static_file(relative_path, static_route)
|
|
605
|
-
if response:
|
|
606
|
-
return response
|
|
607
|
-
|
|
608
|
-
# No matching agent or static file found
|
|
609
|
-
from fastapi import HTTPException
|
|
610
|
-
raise HTTPException(status_code=404, detail="Not Found")
|
|
611
|
-
|
|
612
560
|
# Set host and port
|
|
613
561
|
host = host or self.host
|
|
614
562
|
port = port or self.port
|
|
@@ -738,6 +686,7 @@ class AgentServer:
|
|
|
738
686
|
|
|
739
687
|
self.logger.info(f"Serving static files from '{directory}' at route '{route or '/'}'")
|
|
740
688
|
|
|
689
|
+
|
|
741
690
|
def _serve_static_file(self, file_path: str, route: str = "/") -> Optional[Response]:
|
|
742
691
|
"""
|
|
743
692
|
Internal method to serve a static file.
|
|
@@ -785,3 +734,73 @@ class AgentServer:
|
|
|
785
734
|
return None
|
|
786
735
|
|
|
787
736
|
return FileResponse(full_path)
|
|
737
|
+
|
|
738
|
+
def _register_catch_all_handler(self) -> None:
|
|
739
|
+
"""
|
|
740
|
+
Register catch-all route handler for agent routing and static files.
|
|
741
|
+
|
|
742
|
+
This handler is needed for:
|
|
743
|
+
1. Routing requests without trailing slashes to agents (e.g., /santa instead of /santa/)
|
|
744
|
+
2. Serving static files from directories registered with serve_static_files()
|
|
745
|
+
|
|
746
|
+
Called via startup event (not __init__) to ensure it runs AFTER all other routes
|
|
747
|
+
are registered. This prevents the catch-all from overshadowing custom routes
|
|
748
|
+
like /get_token that users may add to server.app.
|
|
749
|
+
"""
|
|
750
|
+
@self.app.get("/{full_path:path}")
|
|
751
|
+
@self.app.post("/{full_path:path}")
|
|
752
|
+
async def handle_all_routes(request: Request, full_path: str):
|
|
753
|
+
"""Handle requests that don't match registered routes (e.g. /matti instead of /matti/)"""
|
|
754
|
+
# Check if this path maps to one of our registered agents
|
|
755
|
+
for route, agent in self.agents.items():
|
|
756
|
+
# Check for exact match with registered route
|
|
757
|
+
if full_path == route.lstrip("/"):
|
|
758
|
+
# This is a request to an agent's root without trailing slash
|
|
759
|
+
return await agent._handle_root_request(request)
|
|
760
|
+
elif full_path.startswith(route.lstrip("/") + "/"):
|
|
761
|
+
# This is a request to an agent's sub-path
|
|
762
|
+
relative_path = full_path[len(route.lstrip("/")):]
|
|
763
|
+
relative_path = relative_path.lstrip("/")
|
|
764
|
+
|
|
765
|
+
# Route to appropriate handler based on path
|
|
766
|
+
if not relative_path or relative_path == "/":
|
|
767
|
+
return await agent._handle_root_request(request)
|
|
768
|
+
|
|
769
|
+
clean_path = relative_path.rstrip("/")
|
|
770
|
+
if clean_path == "debug":
|
|
771
|
+
return await agent._handle_debug_request(request)
|
|
772
|
+
elif clean_path == "swaig":
|
|
773
|
+
from fastapi import Response
|
|
774
|
+
return await agent._handle_swaig_request(request, Response())
|
|
775
|
+
elif clean_path == "post_prompt":
|
|
776
|
+
return await agent._handle_post_prompt_request(request)
|
|
777
|
+
elif clean_path == "check_for_input":
|
|
778
|
+
return await agent._handle_check_for_input_request(request)
|
|
779
|
+
|
|
780
|
+
# Check for custom routing callbacks
|
|
781
|
+
if hasattr(agent, '_routing_callbacks'):
|
|
782
|
+
for callback_path, callback_fn in agent._routing_callbacks.items():
|
|
783
|
+
cb_path_clean = callback_path.strip("/")
|
|
784
|
+
if clean_path == cb_path_clean:
|
|
785
|
+
request.state.callback_path = callback_path
|
|
786
|
+
return await agent._handle_root_request(request)
|
|
787
|
+
|
|
788
|
+
# No matching agent - check for static files
|
|
789
|
+
if hasattr(self, '_static_directories'):
|
|
790
|
+
# Check each static directory route
|
|
791
|
+
for static_route, static_dir in self._static_directories.items():
|
|
792
|
+
# For root static route, serve any unmatched path
|
|
793
|
+
if static_route == "" or static_route == "/":
|
|
794
|
+
response = self._serve_static_file(full_path, "")
|
|
795
|
+
if response:
|
|
796
|
+
return response
|
|
797
|
+
# For prefixed static routes, check if path matches
|
|
798
|
+
elif full_path.startswith(static_route.lstrip("/") + "/") or full_path == static_route.lstrip("/"):
|
|
799
|
+
relative_path = full_path[len(static_route.lstrip("/")):].lstrip("/")
|
|
800
|
+
response = self._serve_static_file(relative_path, static_route)
|
|
801
|
+
if response:
|
|
802
|
+
return response
|
|
803
|
+
|
|
804
|
+
# No matching agent or static file found
|
|
805
|
+
from fastapi import HTTPException
|
|
806
|
+
raise HTTPException(status_code=404, detail="Not Found")
|