signalwire-agents 1.0.0__py3-none-any.whl → 1.0.2__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.
@@ -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.0"
21
+ __version__ = "1.0.2"
22
22
 
23
23
  # Import core classes for easier access
24
24
  from .core.agent_base import AgentBase
@@ -549,11 +549,11 @@ class AgentServer:
549
549
  # This is a request to an agent's sub-path
550
550
  relative_path = full_path[len(route.lstrip("/")):]
551
551
  relative_path = relative_path.lstrip("/")
552
-
552
+
553
553
  # Route to appropriate handler based on path
554
554
  if not relative_path or relative_path == "/":
555
555
  return await agent._handle_root_request(request)
556
-
556
+
557
557
  clean_path = relative_path.rstrip("/")
558
558
  if clean_path == "debug":
559
559
  return await agent._handle_debug_request(request)
@@ -564,7 +564,7 @@ class AgentServer:
564
564
  return await agent._handle_post_prompt_request(request)
565
565
  elif clean_path == "check_for_input":
566
566
  return await agent._handle_check_for_input_request(request)
567
-
567
+
568
568
  # Check for custom routing callbacks
569
569
  if hasattr(agent, '_routing_callbacks'):
570
570
  for callback_path, callback_fn in agent._routing_callbacks.items():
@@ -572,9 +572,26 @@ class AgentServer:
572
572
  if clean_path == cb_path_clean:
573
573
  request.state.callback_path = callback_path
574
574
  return await agent._handle_root_request(request)
575
-
576
- # No matching agent found
577
- return {"error": "Not Found"}
575
+
576
+ # No matching agent - check for static files
577
+ if hasattr(self, '_static_directories'):
578
+ # Check each static directory route
579
+ for static_route, static_dir in self._static_directories.items():
580
+ # For root static route, serve any unmatched path
581
+ if static_route == "" or static_route == "/":
582
+ response = self._serve_static_file(full_path, "")
583
+ if response:
584
+ return response
585
+ # For prefixed static routes, check if path matches
586
+ elif full_path.startswith(static_route.lstrip("/") + "/") or full_path == static_route.lstrip("/"):
587
+ relative_path = full_path[len(static_route.lstrip("/")):].lstrip("/")
588
+ response = self._serve_static_file(relative_path, static_route)
589
+ if response:
590
+ return response
591
+
592
+ # No matching agent or static file found
593
+ from fastapi import HTTPException
594
+ raise HTTPException(status_code=404, detail="Not Found")
578
595
 
579
596
  # Set host and port
580
597
  host = host or self.host
@@ -634,13 +651,13 @@ class AgentServer:
634
651
  log_level=self.log_level
635
652
  )
636
653
 
637
- def register_global_routing_callback(self, callback_fn: Callable[[Request, Dict[str, Any]], Optional[str]],
654
+ def register_global_routing_callback(self, callback_fn: Callable[[Request, Dict[str, Any]], Optional[str]],
638
655
  path: str) -> None:
639
656
  """
640
657
  Register a routing callback across all agents
641
-
658
+
642
659
  This allows you to add unified routing logic to all agents at the same path.
643
-
660
+
644
661
  Args:
645
662
  callback_fn: The callback function to register
646
663
  path: The path to register the callback at
@@ -648,11 +665,107 @@ class AgentServer:
648
665
  # Normalize the path
649
666
  if not path.startswith("/"):
650
667
  path = f"/{path}"
651
-
668
+
652
669
  path = path.rstrip("/")
653
-
670
+
654
671
  # Register with all existing agents
655
672
  for agent in self.agents.values():
656
673
  agent.register_routing_callback(callback_fn, path=path)
657
-
674
+
658
675
  self.logger.info(f"Registered global routing callback at {path} on all agents")
676
+
677
+ def serve_static_files(self, directory: str, route: str = "/") -> None:
678
+ """
679
+ Serve static files from a directory.
680
+
681
+ This method properly integrates static file serving with agent routes,
682
+ ensuring that agent routes take priority over static files.
683
+
684
+ Unlike using StaticFiles.mount("/", ...) directly on self.app, this method
685
+ uses explicit route handlers that work correctly with agent routes.
686
+
687
+ Args:
688
+ directory: Path to the directory containing static files
689
+ route: URL path prefix for static files (default: "/" for root)
690
+
691
+ Example:
692
+ server = AgentServer()
693
+ server.register(SupportAgent(), "/support")
694
+ server.serve_static_files("./web") # Serves at /
695
+ # /support -> SupportAgent
696
+ # /index.html -> ./web/index.html
697
+ # / -> ./web/index.html
698
+ """
699
+ from pathlib import Path
700
+ from fastapi.responses import FileResponse
701
+ from fastapi import HTTPException
702
+
703
+ # Normalize directory path
704
+ static_dir = Path(directory).resolve()
705
+
706
+ if not static_dir.exists():
707
+ raise ValueError(f"Directory does not exist: {directory}")
708
+
709
+ if not static_dir.is_dir():
710
+ raise ValueError(f"Path is not a directory: {directory}")
711
+
712
+ # Normalize route
713
+ if not route.startswith("/"):
714
+ route = f"/{route}"
715
+ route = route.rstrip("/")
716
+
717
+ # Store static directory config for use by catch-all handler
718
+ if not hasattr(self, '_static_directories'):
719
+ self._static_directories = {}
720
+
721
+ self._static_directories[route] = static_dir
722
+
723
+ self.logger.info(f"Serving static files from '{directory}' at route '{route or '/'}'")
724
+
725
+ def _serve_static_file(self, file_path: str, route: str = "/") -> Optional[Response]:
726
+ """
727
+ Internal method to serve a static file.
728
+
729
+ Args:
730
+ file_path: The requested file path
731
+ route: The route prefix
732
+
733
+ Returns:
734
+ FileResponse if file exists, None otherwise
735
+ """
736
+ from pathlib import Path
737
+ from fastapi.responses import FileResponse
738
+
739
+ if not hasattr(self, '_static_directories'):
740
+ return None
741
+
742
+ static_dir = self._static_directories.get(route)
743
+ if not static_dir:
744
+ return None
745
+
746
+ # Default to index.html for empty path
747
+ if not file_path:
748
+ file_path = "index.html"
749
+
750
+ full_path = static_dir / file_path
751
+
752
+ # Security: prevent path traversal
753
+ try:
754
+ full_path = full_path.resolve()
755
+ if not str(full_path).startswith(str(static_dir)):
756
+ return None
757
+ except Exception:
758
+ return None
759
+
760
+ # Handle directory requests
761
+ if full_path.is_dir():
762
+ index_path = full_path / "index.html"
763
+ if index_path.exists():
764
+ full_path = index_path
765
+ else:
766
+ return None
767
+
768
+ if not full_path.exists():
769
+ return None
770
+
771
+ return FileResponse(full_path)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: signalwire_agents
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: SignalWire AI Agents SDK
5
5
  Author-email: SignalWire Team <info@signalwire.com>
6
6
  License: MIT
@@ -1,5 +1,5 @@
1
- signalwire_agents/__init__.py,sha256=FbXZfIMPQae1SykAmQiMKxWQBMTwFguVxOHWnYNCzTw,5030
2
- signalwire_agents/agent_server.py,sha256=x9HyWia8D3r6KMqY-Q4DtNVivfJWLTx8B-KzUI8okuA,26880
1
+ signalwire_agents/__init__.py,sha256=Ik9ySmfseOgGLIgBOsWq2kktOjpB1yNgdIh1yJL5Fn4,5030
2
+ signalwire_agents/agent_server.py,sha256=5GPt6XekVxep9esZY9s4MG6sOR-VF_m53U25r3WoML4,30964
3
3
  signalwire_agents/schema.json,sha256=OSTe01OQW3WFnHGVnDP3dwIDBP1NWDFZkTJaeWi3f54,378569
4
4
  signalwire_agents/agents/bedrock.py,sha256=J582gooNtxtep4xdVOfyDzRtHp_XrurPMS93xf2Xod0,10836
5
5
  signalwire_agents/cli/__init__.py,sha256=XbxAQFaCIdGXIXJiriVBWoFPOJsC401u21588nO4TG8,388
@@ -130,9 +130,9 @@ signalwire_agents/utils/token_generators.py,sha256=4Mr7baQ_xR_hfJ72YxQRAT_GFa663
130
130
  signalwire_agents/utils/validators.py,sha256=4Mr7baQ_xR_hfJ72YxQRAT_GFa663YjFX_PumJ35Xds,191
131
131
  signalwire_agents/web/__init__.py,sha256=XE_pSTY9Aalzr7J7wqFth1Zr3cccQHPPcF5HWNrOpz8,383
132
132
  signalwire_agents/web/web_service.py,sha256=a2PSHJgX1tlZr0Iz1A1UouZjXEePJAZL632evvLVM38,21071
133
- signalwire_agents-1.0.0.dist-info/licenses/LICENSE,sha256=NYvAsB-rTcSvG9cqHt9EUHAWLiA9YzM4Qfz-mPdvDR0,1067
134
- signalwire_agents-1.0.0.dist-info/METADATA,sha256=aLHkC92V9iI6ovAt5XSQmSU9Buo7h7yBR0Bykhl39mY,41595
135
- signalwire_agents-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
136
- signalwire_agents-1.0.0.dist-info/entry_points.txt,sha256=ZDT65zfTO_YyDzi_hwQbCxIhrUfu_t8RpNXMMXlUPWI,144
137
- signalwire_agents-1.0.0.dist-info/top_level.txt,sha256=kDGS6ZYv84K9P5Kyg9_S8P_pbUXoHkso0On_DB5bbWc,18
138
- signalwire_agents-1.0.0.dist-info/RECORD,,
133
+ signalwire_agents-1.0.2.dist-info/licenses/LICENSE,sha256=NYvAsB-rTcSvG9cqHt9EUHAWLiA9YzM4Qfz-mPdvDR0,1067
134
+ signalwire_agents-1.0.2.dist-info/METADATA,sha256=H-Zd7PV62-L5AvcGMdol8MUFYLpquNJgiZ71jL1t10Q,41595
135
+ signalwire_agents-1.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
136
+ signalwire_agents-1.0.2.dist-info/entry_points.txt,sha256=ZDT65zfTO_YyDzi_hwQbCxIhrUfu_t8RpNXMMXlUPWI,144
137
+ signalwire_agents-1.0.2.dist-info/top_level.txt,sha256=kDGS6ZYv84K9P5Kyg9_S8P_pbUXoHkso0On_DB5bbWc,18
138
+ signalwire_agents-1.0.2.dist-info/RECORD,,