signalwire-agents 0.1.5__tar.gz → 0.1.6__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.
Files changed (40) hide show
  1. {signalwire_agents-0.1.5/signalwire_agents.egg-info → signalwire_agents-0.1.6}/PKG-INFO +1 -1
  2. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/pyproject.toml +1 -1
  3. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/__init__.py +1 -1
  4. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/core/agent_base.py +16 -0
  5. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/core/swml_service.py +105 -1
  6. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6/signalwire_agents.egg-info}/PKG-INFO +1 -1
  7. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/LICENSE +0 -0
  8. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/README.md +0 -0
  9. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/schema.json +0 -0
  10. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/setup.cfg +0 -0
  11. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/setup.py +0 -0
  12. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/agent_server.py +0 -0
  13. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/core/__init__.py +0 -0
  14. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/core/function_result.py +0 -0
  15. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/core/pom_builder.py +0 -0
  16. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/core/security/__init__.py +0 -0
  17. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/core/security/session_manager.py +0 -0
  18. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/core/state/__init__.py +0 -0
  19. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/core/state/file_state_manager.py +0 -0
  20. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/core/state/state_manager.py +0 -0
  21. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/core/swaig_function.py +0 -0
  22. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/core/swml_builder.py +0 -0
  23. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/core/swml_handler.py +0 -0
  24. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/core/swml_renderer.py +0 -0
  25. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/prefabs/__init__.py +0 -0
  26. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/prefabs/concierge.py +0 -0
  27. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/prefabs/faq_bot.py +0 -0
  28. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/prefabs/info_gatherer.py +0 -0
  29. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/prefabs/receptionist.py +0 -0
  30. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/prefabs/survey.py +0 -0
  31. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/schema.json +0 -0
  32. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/utils/__init__.py +0 -0
  33. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/utils/pom_utils.py +0 -0
  34. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/utils/schema_utils.py +0 -0
  35. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/utils/token_generators.py +0 -0
  36. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents/utils/validators.py +0 -0
  37. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents.egg-info/SOURCES.txt +0 -0
  38. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents.egg-info/dependency_links.txt +0 -0
  39. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/signalwire_agents.egg-info/requires.txt +0 -0
  40. {signalwire_agents-0.1.5 → signalwire_agents-0.1.6}/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.5
3
+ Version: 0.1.6
4
4
  Summary: SignalWire AI Agents SDK
5
5
  Author-email: SignalWire Team <info@signalwire.com>
6
6
  Project-URL: Homepage, https://github.com/signalwire/signalwire-ai-agents
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "signalwire_agents"
7
- version = "0.1.5"
7
+ version = "0.1.6"
8
8
  description = "SignalWire AI Agents SDK"
9
9
  authors = [
10
10
  {name = "SignalWire Team", email = "info@signalwire.com"}
@@ -14,7 +14,7 @@ SignalWire AI Agents SDK
14
14
  A package for building AI agents using SignalWire's AI and SWML capabilities.
15
15
  """
16
16
 
17
- __version__ = "0.1.5"
17
+ __version__ = "0.1.6"
18
18
 
19
19
  # Import core classes for easier access
20
20
  from signalwire_agents.core.agent_base import AgentBase
@@ -1172,6 +1172,22 @@ class AgentBase(SWMLService):
1172
1172
 
1173
1173
  async def _handle_root_request(self, request: Request):
1174
1174
  """Handle GET/POST requests to the root endpoint"""
1175
+ # Auto-detect proxy on first request if not explicitly configured
1176
+ if not getattr(self, '_proxy_detection_done', False) and not getattr(self, '_proxy_url_base', None):
1177
+ # Check for proxy headers
1178
+ forwarded_host = request.headers.get("X-Forwarded-Host")
1179
+ forwarded_proto = request.headers.get("X-Forwarded-Proto", "http")
1180
+
1181
+ if forwarded_host:
1182
+ self._proxy_url_base = f"{forwarded_proto}://{forwarded_host}"
1183
+ self.log.info("proxy_auto_detected", proxy_url_base=self._proxy_url_base,
1184
+ source="X-Forwarded headers")
1185
+ self._proxy_detection_done = True
1186
+ # If no explicit proxy headers, try the parent class detection method if it exists
1187
+ elif hasattr(super(), '_detect_proxy_from_request'):
1188
+ super()._detect_proxy_from_request(request)
1189
+ self._proxy_detection_done = True
1190
+
1175
1191
  # Check if this is a callback path request
1176
1192
  callback_path = getattr(request.state, "callback_path", None)
1177
1193
 
@@ -126,6 +126,11 @@ class SWMLService:
126
126
  self.ssl_enabled = False
127
127
  self.domain = None
128
128
 
129
+ # Initialize proxy detection attributes
130
+ self._proxy_url_base = os.environ.get('SWML_PROXY_URL_BASE')
131
+ self._proxy_detection_done = False
132
+ self._proxy_debug = os.environ.get('SWML_PROXY_DEBUG', '').lower() in ('true', '1', 'yes')
133
+
129
134
  # Initialize logger for this instance
130
135
  self.log = logger.bind(service=name)
131
136
  self.log.info("service_initializing", route=self.route, host=host, port=port)
@@ -691,6 +696,11 @@ class SWMLService:
691
696
  Returns:
692
697
  Response with SWML document or error
693
698
  """
699
+ # Auto-detect proxy on first request if not explicitly configured
700
+ if not self._proxy_detection_done and not self._proxy_url_base:
701
+ self._detect_proxy_from_request(request)
702
+ self._proxy_detection_done = True
703
+
694
704
  # Check auth
695
705
  if not self._check_basic_auth(request):
696
706
  response.headers["WWW-Authenticate"] = "Basic"
@@ -1054,4 +1064,98 @@ class SWMLService:
1054
1064
  params = "&".join([f"{k}={v}" for k, v in filtered_params.items()])
1055
1065
  url = f"{url}?{params}"
1056
1066
 
1057
- return url
1067
+ return url
1068
+
1069
+ def _detect_proxy_from_request(self, request: Request) -> None:
1070
+ """
1071
+ Detect if we're behind a proxy by examining request headers
1072
+ and auto-configure proxy_url_base if needed
1073
+
1074
+ Args:
1075
+ request: FastAPI Request object
1076
+ """
1077
+ # First check for standard X-Forwarded headers (used by most proxies including ngrok)
1078
+ forwarded_host = request.headers.get("X-Forwarded-Host")
1079
+ forwarded_proto = request.headers.get("X-Forwarded-Proto", "http")
1080
+
1081
+ if forwarded_host:
1082
+ # Direct X-Forwarded-* headers - most common case
1083
+ self._proxy_url_base = f"{forwarded_proto}://{forwarded_host}"
1084
+ self.log.info("proxy_auto_detected", proxy_url_base=self._proxy_url_base,
1085
+ source="X-Forwarded headers")
1086
+ return
1087
+
1088
+ # If no standard headers, check other proxy detection methods
1089
+
1090
+ # Check for Forwarded header (RFC 7239)
1091
+ forwarded = request.headers.get("Forwarded")
1092
+ if forwarded:
1093
+ # Parse RFC 7239 Forwarded header
1094
+ try:
1095
+ # Extract host and proto from Forwarded: for=X;host=Y;proto=Z
1096
+ parts = [p.strip() for p in forwarded.split(';')]
1097
+ host_part = next((p for p in parts if p.startswith("host=")), None)
1098
+ proto_part = next((p for p in parts if p.startswith("proto=")), None)
1099
+
1100
+ if host_part:
1101
+ host = host_part.split('=', 1)[1].strip('"')
1102
+ proto = proto_part.split('=', 1)[1].strip('"') if proto_part else "http"
1103
+ self._proxy_url_base = f"{proto}://{host}"
1104
+ self.log.info("proxy_auto_detected", proxy_url_base=self._proxy_url_base,
1105
+ source="Forwarded header")
1106
+ return
1107
+ except Exception as e:
1108
+ self.log.warning("forwarded_header_parse_error", error=str(e))
1109
+
1110
+ # Try to detect from the URL itself for transparent proxies
1111
+ if str(request.url).startswith(("https://", "http://")) and not any(
1112
+ str(request.url).startswith(f"http://{h}") for h in ["localhost", "127.0.0.1", self.host, "0.0.0.0"]
1113
+ ):
1114
+ # This is likely a transparent proxy - extract base URL
1115
+ parsed = urlparse(str(request.url))
1116
+ base_url = f"{parsed.scheme}://{parsed.netloc}"
1117
+ self._proxy_url_base = base_url
1118
+ self.log.info("proxy_auto_detected", proxy_url_base=base_url,
1119
+ source="request URL (transparent proxy)")
1120
+ return
1121
+
1122
+ # Check for other common proxy setups
1123
+ original_host = request.headers.get("X-Original-Host") or request.headers.get("Host")
1124
+ if original_host:
1125
+ # Only use Host if it doesn't look like our local server
1126
+ local_hosts = [self.host, "localhost", "127.0.0.1", "0.0.0.0"]
1127
+ local_port = f":{self.port}"
1128
+
1129
+ # If host doesn't look like local server or doesn't contain our port
1130
+ if not any(h in original_host for h in local_hosts) and local_port not in original_host:
1131
+ proto = "https" if request.url.scheme == "https" else "http"
1132
+ self._proxy_url_base = f"{proto}://{original_host}"
1133
+ self.log.info("proxy_auto_detected", proxy_url_base=self._proxy_url_base,
1134
+ source="Host header")
1135
+ return
1136
+
1137
+ # If forward_for header exists, we're likely behind a proxy but couldn't determine the URL
1138
+ forwarded_for = request.headers.get("X-Forwarded-For")
1139
+ if forwarded_for:
1140
+ self.log.warning("proxy_detected_but_url_unknown",
1141
+ client_ip=forwarded_for,
1142
+ message="Proxy detected via X-Forwarded-For header but could not determine public URL")
1143
+
1144
+ # No proxy detected, or unable to determine the public URL
1145
+ if self._proxy_debug:
1146
+ self.log.info("proxy_detection_failed",
1147
+ message="Could not auto-detect proxy. If you are behind a proxy, set SWML_PROXY_URL_BASE manually.")
1148
+
1149
+ def manual_set_proxy_url(self, proxy_url: str) -> None:
1150
+ """
1151
+ Manually set the proxy URL base for webhook callbacks
1152
+
1153
+ This can be called at runtime to set or update the proxy URL
1154
+
1155
+ Args:
1156
+ proxy_url: The base URL to use for webhooks (e.g., https://example.ngrok.io)
1157
+ """
1158
+ if proxy_url:
1159
+ self._proxy_url_base = proxy_url.rstrip('/')
1160
+ self.log.info("proxy_url_manually_set", proxy_url_base=self._proxy_url_base)
1161
+ self._proxy_detection_done = True
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: signalwire_agents
3
- Version: 0.1.5
3
+ Version: 0.1.6
4
4
  Summary: SignalWire AI Agents SDK
5
5
  Author-email: SignalWire Team <info@signalwire.com>
6
6
  Project-URL: Homepage, https://github.com/signalwire/signalwire-ai-agents