signalwire-agents 0.1.14__py3-none-any.whl → 0.1.16__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/core/agent_base.py +440 -43
- signalwire_agents/core/function_result.py +1 -1
- signalwire_agents/core/logging_config.py +19 -4
- {signalwire_agents-0.1.14.dist-info → signalwire_agents-0.1.16.dist-info}/METADATA +1 -1
- {signalwire_agents-0.1.14.dist-info → signalwire_agents-0.1.16.dist-info}/RECORD +11 -11
- {signalwire_agents-0.1.14.data → signalwire_agents-0.1.16.data}/data/schema.json +0 -0
- {signalwire_agents-0.1.14.dist-info → signalwire_agents-0.1.16.dist-info}/WHEEL +0 -0
- {signalwire_agents-0.1.14.dist-info → signalwire_agents-0.1.16.dist-info}/entry_points.txt +0 -0
- {signalwire_agents-0.1.14.dist-info → signalwire_agents-0.1.16.dist-info}/licenses/LICENSE +0 -0
- {signalwire_agents-0.1.14.dist-info → signalwire_agents-0.1.16.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__ = "0.1.
|
21
|
+
__version__ = "0.1.16"
|
22
22
|
|
23
23
|
# Import core classes for easier access
|
24
24
|
from .core.agent_base import AgentBase
|
@@ -25,7 +25,7 @@ import re
|
|
25
25
|
import signal
|
26
26
|
import sys
|
27
27
|
from typing import Optional, Union, List, Dict, Any, Tuple, Callable, Type
|
28
|
-
from urllib.parse import urlparse, urlencode
|
28
|
+
from urllib.parse import urlparse, urlencode, urlunparse
|
29
29
|
|
30
30
|
try:
|
31
31
|
import fastapi
|
@@ -1299,51 +1299,61 @@ class AgentBase(SWMLService):
|
|
1299
1299
|
script_name = os.getenv('SCRIPT_NAME', '')
|
1300
1300
|
base_url = f"{protocol}://{host}{script_name}"
|
1301
1301
|
elif mode == 'lambda':
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1302
|
+
# AWS Lambda Function URL format
|
1303
|
+
lambda_url = os.getenv('AWS_LAMBDA_FUNCTION_URL')
|
1304
|
+
if lambda_url:
|
1305
|
+
base_url = lambda_url.rstrip('/')
|
1305
1306
|
else:
|
1306
|
-
|
1307
|
-
|
1308
|
-
|
1309
|
-
|
1310
|
-
|
1311
|
-
|
1312
|
-
|
1313
|
-
|
1314
|
-
|
1315
|
-
|
1316
|
-
|
1317
|
-
|
1318
|
-
base_url = function_url
|
1307
|
+
# Fallback construction for Lambda
|
1308
|
+
region = os.getenv('AWS_REGION', 'us-east-1')
|
1309
|
+
function_name = os.getenv('AWS_LAMBDA_FUNCTION_NAME', 'unknown')
|
1310
|
+
base_url = f"https://{function_name}.lambda-url.{region}.on.aws"
|
1311
|
+
elif mode == 'google_cloud_function':
|
1312
|
+
# Google Cloud Functions URL format
|
1313
|
+
project_id = os.getenv('GOOGLE_CLOUD_PROJECT') or os.getenv('GCP_PROJECT')
|
1314
|
+
region = os.getenv('FUNCTION_REGION') or os.getenv('GOOGLE_CLOUD_REGION', 'us-central1')
|
1315
|
+
service_name = os.getenv('K_SERVICE') or os.getenv('FUNCTION_TARGET', 'unknown')
|
1316
|
+
|
1317
|
+
if project_id:
|
1318
|
+
base_url = f"https://{region}-{project_id}.cloudfunctions.net/{service_name}"
|
1319
1319
|
else:
|
1320
|
-
|
1321
|
-
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1325
|
-
|
1326
|
-
|
1327
|
-
|
1328
|
-
|
1329
|
-
else:
|
1330
|
-
# Server mode - preserve existing logic
|
1331
|
-
if self._proxy_url_base:
|
1332
|
-
proxy_base = self._proxy_url_base.rstrip('/')
|
1333
|
-
route = self.route if self.route.startswith('/') else f"/{self.route}"
|
1334
|
-
base_url = f"{proxy_base}{route}"
|
1320
|
+
# Fallback for local testing or incomplete environment
|
1321
|
+
base_url = f"https://localhost:8080"
|
1322
|
+
elif mode == 'azure_function':
|
1323
|
+
# Azure Functions URL format
|
1324
|
+
function_app_name = os.getenv('WEBSITE_SITE_NAME') or os.getenv('AZURE_FUNCTIONS_APP_NAME')
|
1325
|
+
function_name = os.getenv('AZURE_FUNCTION_NAME', 'unknown')
|
1326
|
+
|
1327
|
+
if function_app_name:
|
1328
|
+
base_url = f"https://{function_app_name}.azurewebsites.net/api/{function_name}"
|
1335
1329
|
else:
|
1336
|
-
|
1337
|
-
|
1338
|
-
|
1339
|
-
|
1340
|
-
|
1341
|
-
|
1342
|
-
|
1330
|
+
# Fallback for local testing
|
1331
|
+
base_url = f"https://localhost:7071/api/{function_name}"
|
1332
|
+
else:
|
1333
|
+
# Server mode
|
1334
|
+
protocol = 'https' if self.ssl_cert and self.ssl_key else 'http'
|
1335
|
+
base_url = f"{protocol}://{self.host}:{self.port}"
|
1336
|
+
|
1337
|
+
# Add route if not already included (for server mode)
|
1338
|
+
if mode == 'server' and self.route and not base_url.endswith(self.route):
|
1339
|
+
base_url = f"{base_url}/{self.route.lstrip('/')}"
|
1340
|
+
|
1341
|
+
# Add authentication if requested
|
1343
1342
|
if include_auth:
|
1344
|
-
username, password = self.
|
1345
|
-
|
1346
|
-
|
1343
|
+
username, password = self.get_basic_auth_credentials()
|
1344
|
+
if username and password:
|
1345
|
+
# Parse URL to insert auth
|
1346
|
+
from urllib.parse import urlparse, urlunparse
|
1347
|
+
parsed = urlparse(base_url)
|
1348
|
+
# Reconstruct with auth
|
1349
|
+
base_url = urlunparse((
|
1350
|
+
parsed.scheme,
|
1351
|
+
f"{username}:{password}@{parsed.netloc}",
|
1352
|
+
parsed.path,
|
1353
|
+
parsed.params,
|
1354
|
+
parsed.query,
|
1355
|
+
parsed.fragment
|
1356
|
+
))
|
1347
1357
|
|
1348
1358
|
return base_url
|
1349
1359
|
|
@@ -1878,6 +1888,124 @@ class AgentBase(SWMLService):
|
|
1878
1888
|
else:
|
1879
1889
|
raise
|
1880
1890
|
|
1891
|
+
def _check_cgi_auth(self) -> bool:
|
1892
|
+
"""
|
1893
|
+
Check basic auth in CGI mode using environment variables
|
1894
|
+
|
1895
|
+
Returns:
|
1896
|
+
True if auth is valid, False otherwise
|
1897
|
+
"""
|
1898
|
+
# Check for HTTP_AUTHORIZATION environment variable
|
1899
|
+
auth_header = os.getenv('HTTP_AUTHORIZATION')
|
1900
|
+
if not auth_header:
|
1901
|
+
# Also check for REMOTE_USER (if web server handled auth)
|
1902
|
+
remote_user = os.getenv('REMOTE_USER')
|
1903
|
+
if remote_user:
|
1904
|
+
# If web server handled auth, trust it
|
1905
|
+
return True
|
1906
|
+
return False
|
1907
|
+
|
1908
|
+
if not auth_header.startswith('Basic '):
|
1909
|
+
return False
|
1910
|
+
|
1911
|
+
try:
|
1912
|
+
# Decode the base64 credentials
|
1913
|
+
credentials = base64.b64decode(auth_header[6:]).decode("utf-8")
|
1914
|
+
username, password = credentials.split(":", 1)
|
1915
|
+
return self.validate_basic_auth(username, password)
|
1916
|
+
except Exception:
|
1917
|
+
return False
|
1918
|
+
|
1919
|
+
def _send_cgi_auth_challenge(self) -> str:
|
1920
|
+
"""
|
1921
|
+
Send authentication challenge in CGI mode
|
1922
|
+
|
1923
|
+
Returns:
|
1924
|
+
HTTP response with 401 status and WWW-Authenticate header
|
1925
|
+
"""
|
1926
|
+
# In CGI, we need to output the complete HTTP response
|
1927
|
+
response = "Status: 401 Unauthorized\r\n"
|
1928
|
+
response += "WWW-Authenticate: Basic realm=\"SignalWire Agent\"\r\n"
|
1929
|
+
response += "Content-Type: application/json\r\n"
|
1930
|
+
response += "\r\n"
|
1931
|
+
response += json.dumps({"error": "Unauthorized"})
|
1932
|
+
return response
|
1933
|
+
|
1934
|
+
def _check_lambda_auth(self, event) -> bool:
|
1935
|
+
"""
|
1936
|
+
Check basic auth in Lambda mode using event headers
|
1937
|
+
|
1938
|
+
Args:
|
1939
|
+
event: Lambda event object containing headers
|
1940
|
+
|
1941
|
+
Returns:
|
1942
|
+
True if auth is valid, False otherwise
|
1943
|
+
"""
|
1944
|
+
if not event or 'headers' not in event:
|
1945
|
+
return False
|
1946
|
+
|
1947
|
+
headers = event['headers']
|
1948
|
+
|
1949
|
+
# Check for authorization header (case-insensitive)
|
1950
|
+
auth_header = None
|
1951
|
+
for key, value in headers.items():
|
1952
|
+
if key.lower() == 'authorization':
|
1953
|
+
auth_header = value
|
1954
|
+
break
|
1955
|
+
|
1956
|
+
if not auth_header or not auth_header.startswith('Basic '):
|
1957
|
+
return False
|
1958
|
+
|
1959
|
+
try:
|
1960
|
+
# Decode the base64 credentials
|
1961
|
+
credentials = base64.b64decode(auth_header[6:]).decode("utf-8")
|
1962
|
+
username, password = credentials.split(":", 1)
|
1963
|
+
return self.validate_basic_auth(username, password)
|
1964
|
+
except Exception:
|
1965
|
+
return False
|
1966
|
+
|
1967
|
+
def _send_lambda_auth_challenge(self) -> dict:
|
1968
|
+
"""
|
1969
|
+
Send authentication challenge in Lambda mode
|
1970
|
+
|
1971
|
+
Returns:
|
1972
|
+
Lambda response with 401 status and WWW-Authenticate header
|
1973
|
+
"""
|
1974
|
+
return {
|
1975
|
+
"statusCode": 401,
|
1976
|
+
"headers": {
|
1977
|
+
"WWW-Authenticate": "Basic realm=\"SignalWire Agent\"",
|
1978
|
+
"Content-Type": "application/json"
|
1979
|
+
},
|
1980
|
+
"body": json.dumps({"error": "Unauthorized"})
|
1981
|
+
}
|
1982
|
+
|
1983
|
+
def _check_cloud_function_auth(self, request) -> bool:
|
1984
|
+
"""
|
1985
|
+
Check basic auth in Cloud Function mode
|
1986
|
+
|
1987
|
+
Args:
|
1988
|
+
request: Cloud Function request object
|
1989
|
+
|
1990
|
+
Returns:
|
1991
|
+
True if auth is valid, False otherwise
|
1992
|
+
"""
|
1993
|
+
# This would need to be implemented based on the specific
|
1994
|
+
# cloud function framework being used (Flask, etc.)
|
1995
|
+
# For now, return True to maintain existing behavior
|
1996
|
+
return True
|
1997
|
+
|
1998
|
+
def _send_cloud_function_auth_challenge(self):
|
1999
|
+
"""
|
2000
|
+
Send authentication challenge in Cloud Function mode
|
2001
|
+
|
2002
|
+
Returns:
|
2003
|
+
Cloud Function response with 401 status
|
2004
|
+
"""
|
2005
|
+
# This would need to be implemented based on the specific
|
2006
|
+
# cloud function framework being used
|
2007
|
+
return {"error": "Unauthorized", "status": 401}
|
2008
|
+
|
1881
2009
|
def handle_serverless_request(self, event=None, context=None, mode=None):
|
1882
2010
|
"""
|
1883
2011
|
Handle serverless environment requests (CGI, Lambda, Cloud Functions)
|
@@ -1895,6 +2023,10 @@ class AgentBase(SWMLService):
|
|
1895
2023
|
|
1896
2024
|
try:
|
1897
2025
|
if mode == 'cgi':
|
2026
|
+
# Check authentication in CGI mode
|
2027
|
+
if not self._check_cgi_auth():
|
2028
|
+
return self._send_cgi_auth_challenge()
|
2029
|
+
|
1898
2030
|
path_info = os.getenv('PATH_INFO', '').strip('/')
|
1899
2031
|
if not path_info:
|
1900
2032
|
return self._render_swml()
|
@@ -1930,6 +2062,10 @@ class AgentBase(SWMLService):
|
|
1930
2062
|
return self._execute_swaig_function(path_info, args, call_id, raw_data)
|
1931
2063
|
|
1932
2064
|
elif mode == 'lambda':
|
2065
|
+
# Check authentication in Lambda mode
|
2066
|
+
if not self._check_lambda_auth(event):
|
2067
|
+
return self._send_lambda_auth_challenge()
|
2068
|
+
|
1933
2069
|
if event:
|
1934
2070
|
path = event.get('pathParameters', {}).get('proxy', '') if event.get('pathParameters') else ''
|
1935
2071
|
if not path:
|
@@ -1984,7 +2120,26 @@ class AgentBase(SWMLService):
|
|
1984
2120
|
"body": swml_response
|
1985
2121
|
}
|
1986
2122
|
|
1987
|
-
elif mode
|
2123
|
+
elif mode == 'google_cloud_function':
|
2124
|
+
# Check authentication in Google Cloud Functions mode
|
2125
|
+
if not self._check_google_cloud_function_auth(event):
|
2126
|
+
return self._send_google_cloud_function_auth_challenge()
|
2127
|
+
|
2128
|
+
return self._handle_google_cloud_function_request(event)
|
2129
|
+
|
2130
|
+
elif mode == 'azure_function':
|
2131
|
+
# Check authentication in Azure Functions mode
|
2132
|
+
if not self._check_azure_function_auth(event):
|
2133
|
+
return self._send_azure_function_auth_challenge()
|
2134
|
+
|
2135
|
+
return self._handle_azure_function_request(event)
|
2136
|
+
|
2137
|
+
elif mode in ['cloud_function']:
|
2138
|
+
# Legacy cloud function mode - deprecated
|
2139
|
+
# Check authentication in Cloud Function mode
|
2140
|
+
if not self._check_cloud_function_auth(event):
|
2141
|
+
return self._send_cloud_function_auth_challenge()
|
2142
|
+
|
1988
2143
|
return self._handle_cloud_function_request(event)
|
1989
2144
|
|
1990
2145
|
except Exception as e:
|
@@ -3410,3 +3565,245 @@ class AgentBase(SWMLService):
|
|
3410
3565
|
def has_skill(self, skill_name: str) -> bool:
|
3411
3566
|
"""Check if skill is loaded"""
|
3412
3567
|
return self.skill_manager.has_skill(skill_name)
|
3568
|
+
|
3569
|
+
def _check_google_cloud_function_auth(self, request) -> bool:
|
3570
|
+
"""
|
3571
|
+
Check basic auth in Google Cloud Functions mode using request headers
|
3572
|
+
|
3573
|
+
Args:
|
3574
|
+
request: Flask request object or similar containing headers
|
3575
|
+
|
3576
|
+
Returns:
|
3577
|
+
True if auth is valid, False otherwise
|
3578
|
+
"""
|
3579
|
+
if not hasattr(request, 'headers'):
|
3580
|
+
return False
|
3581
|
+
|
3582
|
+
# Check for authorization header (case-insensitive)
|
3583
|
+
auth_header = None
|
3584
|
+
for key in request.headers:
|
3585
|
+
if key.lower() == 'authorization':
|
3586
|
+
auth_header = request.headers[key]
|
3587
|
+
break
|
3588
|
+
|
3589
|
+
if not auth_header or not auth_header.startswith('Basic '):
|
3590
|
+
return False
|
3591
|
+
|
3592
|
+
try:
|
3593
|
+
import base64
|
3594
|
+
encoded_credentials = auth_header[6:] # Remove 'Basic '
|
3595
|
+
decoded_credentials = base64.b64decode(encoded_credentials).decode('utf-8')
|
3596
|
+
provided_username, provided_password = decoded_credentials.split(':', 1)
|
3597
|
+
|
3598
|
+
expected_username, expected_password = self.get_basic_auth_credentials()
|
3599
|
+
return (provided_username == expected_username and
|
3600
|
+
provided_password == expected_password)
|
3601
|
+
except Exception:
|
3602
|
+
return False
|
3603
|
+
|
3604
|
+
def _check_azure_function_auth(self, req) -> bool:
|
3605
|
+
"""
|
3606
|
+
Check basic auth in Azure Functions mode using request object
|
3607
|
+
|
3608
|
+
Args:
|
3609
|
+
req: Azure Functions request object containing headers
|
3610
|
+
|
3611
|
+
Returns:
|
3612
|
+
True if auth is valid, False otherwise
|
3613
|
+
"""
|
3614
|
+
if not hasattr(req, 'headers'):
|
3615
|
+
return False
|
3616
|
+
|
3617
|
+
# Check for authorization header (case-insensitive)
|
3618
|
+
auth_header = None
|
3619
|
+
for key, value in req.headers.items():
|
3620
|
+
if key.lower() == 'authorization':
|
3621
|
+
auth_header = value
|
3622
|
+
break
|
3623
|
+
|
3624
|
+
if not auth_header or not auth_header.startswith('Basic '):
|
3625
|
+
return False
|
3626
|
+
|
3627
|
+
try:
|
3628
|
+
import base64
|
3629
|
+
encoded_credentials = auth_header[6:] # Remove 'Basic '
|
3630
|
+
decoded_credentials = base64.b64decode(encoded_credentials).decode('utf-8')
|
3631
|
+
provided_username, provided_password = decoded_credentials.split(':', 1)
|
3632
|
+
|
3633
|
+
expected_username, expected_password = self.get_basic_auth_credentials()
|
3634
|
+
return (provided_username == expected_username and
|
3635
|
+
provided_password == expected_password)
|
3636
|
+
except Exception:
|
3637
|
+
return False
|
3638
|
+
|
3639
|
+
def _send_google_cloud_function_auth_challenge(self):
|
3640
|
+
"""
|
3641
|
+
Send authentication challenge in Google Cloud Functions mode
|
3642
|
+
|
3643
|
+
Returns:
|
3644
|
+
Flask-compatible response with 401 status and WWW-Authenticate header
|
3645
|
+
"""
|
3646
|
+
from flask import Response
|
3647
|
+
return Response(
|
3648
|
+
response=json.dumps({"error": "Unauthorized"}),
|
3649
|
+
status=401,
|
3650
|
+
headers={
|
3651
|
+
"WWW-Authenticate": "Basic realm=\"SignalWire Agent\"",
|
3652
|
+
"Content-Type": "application/json"
|
3653
|
+
}
|
3654
|
+
)
|
3655
|
+
|
3656
|
+
def _send_azure_function_auth_challenge(self):
|
3657
|
+
"""
|
3658
|
+
Send authentication challenge in Azure Functions mode
|
3659
|
+
|
3660
|
+
Returns:
|
3661
|
+
Azure Functions response with 401 status and WWW-Authenticate header
|
3662
|
+
"""
|
3663
|
+
import azure.functions as func
|
3664
|
+
return func.HttpResponse(
|
3665
|
+
body=json.dumps({"error": "Unauthorized"}),
|
3666
|
+
status_code=401,
|
3667
|
+
headers={
|
3668
|
+
"WWW-Authenticate": "Basic realm=\"SignalWire Agent\"",
|
3669
|
+
"Content-Type": "application/json"
|
3670
|
+
}
|
3671
|
+
)
|
3672
|
+
|
3673
|
+
def _handle_google_cloud_function_request(self, request):
|
3674
|
+
"""
|
3675
|
+
Handle Google Cloud Functions specific requests
|
3676
|
+
|
3677
|
+
Args:
|
3678
|
+
request: Flask request object from Google Cloud Functions
|
3679
|
+
|
3680
|
+
Returns:
|
3681
|
+
Flask response object
|
3682
|
+
"""
|
3683
|
+
try:
|
3684
|
+
# Get the path from the request
|
3685
|
+
path = request.path.strip('/')
|
3686
|
+
|
3687
|
+
if not path:
|
3688
|
+
# Root request - return SWML
|
3689
|
+
swml_response = self._render_swml()
|
3690
|
+
from flask import Response
|
3691
|
+
return Response(
|
3692
|
+
response=swml_response,
|
3693
|
+
status=200,
|
3694
|
+
headers={"Content-Type": "application/json"}
|
3695
|
+
)
|
3696
|
+
else:
|
3697
|
+
# SWAIG function call
|
3698
|
+
args = {}
|
3699
|
+
call_id = None
|
3700
|
+
raw_data = None
|
3701
|
+
|
3702
|
+
# Parse request data
|
3703
|
+
if request.method == 'POST':
|
3704
|
+
try:
|
3705
|
+
if request.is_json:
|
3706
|
+
raw_data = request.get_json()
|
3707
|
+
else:
|
3708
|
+
raw_data = json.loads(request.get_data(as_text=True))
|
3709
|
+
|
3710
|
+
call_id = raw_data.get("call_id")
|
3711
|
+
|
3712
|
+
# Extract arguments like the FastAPI handler does
|
3713
|
+
if "argument" in raw_data and isinstance(raw_data["argument"], dict):
|
3714
|
+
if "parsed" in raw_data["argument"] and isinstance(raw_data["argument"]["parsed"], list) and raw_data["argument"]["parsed"]:
|
3715
|
+
args = raw_data["argument"]["parsed"][0]
|
3716
|
+
elif "raw" in raw_data["argument"]:
|
3717
|
+
try:
|
3718
|
+
args = json.loads(raw_data["argument"]["raw"])
|
3719
|
+
except Exception:
|
3720
|
+
pass
|
3721
|
+
except Exception:
|
3722
|
+
# If parsing fails, continue with empty args
|
3723
|
+
pass
|
3724
|
+
|
3725
|
+
result = self._execute_swaig_function(path, args, call_id, raw_data)
|
3726
|
+
from flask import Response
|
3727
|
+
return Response(
|
3728
|
+
response=json.dumps(result) if isinstance(result, dict) else str(result),
|
3729
|
+
status=200,
|
3730
|
+
headers={"Content-Type": "application/json"}
|
3731
|
+
)
|
3732
|
+
|
3733
|
+
except Exception as e:
|
3734
|
+
import logging
|
3735
|
+
logging.error(f"Error in Google Cloud Function request handler: {e}")
|
3736
|
+
from flask import Response
|
3737
|
+
return Response(
|
3738
|
+
response=json.dumps({"error": str(e)}),
|
3739
|
+
status=500,
|
3740
|
+
headers={"Content-Type": "application/json"}
|
3741
|
+
)
|
3742
|
+
|
3743
|
+
def _handle_azure_function_request(self, req):
|
3744
|
+
"""
|
3745
|
+
Handle Azure Functions specific requests
|
3746
|
+
|
3747
|
+
Args:
|
3748
|
+
req: Azure Functions HttpRequest object
|
3749
|
+
|
3750
|
+
Returns:
|
3751
|
+
Azure Functions HttpResponse object
|
3752
|
+
"""
|
3753
|
+
try:
|
3754
|
+
import azure.functions as func
|
3755
|
+
|
3756
|
+
# Get the path from the request
|
3757
|
+
path = req.url.split('/')[-1] if req.url else ''
|
3758
|
+
|
3759
|
+
if not path or path == 'api':
|
3760
|
+
# Root request - return SWML
|
3761
|
+
swml_response = self._render_swml()
|
3762
|
+
return func.HttpResponse(
|
3763
|
+
body=swml_response,
|
3764
|
+
status_code=200,
|
3765
|
+
headers={"Content-Type": "application/json"}
|
3766
|
+
)
|
3767
|
+
else:
|
3768
|
+
# SWAIG function call
|
3769
|
+
args = {}
|
3770
|
+
call_id = None
|
3771
|
+
raw_data = None
|
3772
|
+
|
3773
|
+
# Parse request data
|
3774
|
+
if req.method == 'POST':
|
3775
|
+
try:
|
3776
|
+
body = req.get_body()
|
3777
|
+
if body:
|
3778
|
+
raw_data = json.loads(body.decode('utf-8'))
|
3779
|
+
call_id = raw_data.get("call_id")
|
3780
|
+
|
3781
|
+
# Extract arguments like the FastAPI handler does
|
3782
|
+
if "argument" in raw_data and isinstance(raw_data["argument"], dict):
|
3783
|
+
if "parsed" in raw_data["argument"] and isinstance(raw_data["argument"]["parsed"], list) and raw_data["argument"]["parsed"]:
|
3784
|
+
args = raw_data["argument"]["parsed"][0]
|
3785
|
+
elif "raw" in raw_data["argument"]:
|
3786
|
+
try:
|
3787
|
+
args = json.loads(raw_data["argument"]["raw"])
|
3788
|
+
except Exception:
|
3789
|
+
pass
|
3790
|
+
except Exception:
|
3791
|
+
# If parsing fails, continue with empty args
|
3792
|
+
pass
|
3793
|
+
|
3794
|
+
result = self._execute_swaig_function(path, args, call_id, raw_data)
|
3795
|
+
return func.HttpResponse(
|
3796
|
+
body=json.dumps(result) if isinstance(result, dict) else str(result),
|
3797
|
+
status_code=200,
|
3798
|
+
headers={"Content-Type": "application/json"}
|
3799
|
+
)
|
3800
|
+
|
3801
|
+
except Exception as e:
|
3802
|
+
import logging
|
3803
|
+
logging.error(f"Error in Azure Function request handler: {e}")
|
3804
|
+
import azure.functions as func
|
3805
|
+
return func.HttpResponse(
|
3806
|
+
body=json.dumps({"error": str(e)}),
|
3807
|
+
status_code=500,
|
3808
|
+
headers={"Content-Type": "application/json"}
|
3809
|
+
)
|
@@ -127,19 +127,34 @@ class BoundStructuredLoggerWrapper(StructuredLoggerWrapper):
|
|
127
127
|
return BoundStructuredLoggerWrapper(self._logger, new_bound_data)
|
128
128
|
|
129
129
|
|
130
|
-
def get_execution_mode()
|
130
|
+
def get_execution_mode():
|
131
131
|
"""
|
132
132
|
Determine the execution mode based on environment variables
|
133
133
|
|
134
134
|
Returns:
|
135
|
-
'cgi'
|
136
|
-
'lambda' if running in AWS Lambda
|
137
|
-
'server' for normal server mode
|
135
|
+
str: 'server', 'cgi', 'lambda', 'google_cloud_function', 'azure_function', or 'unknown'
|
138
136
|
"""
|
137
|
+
# Check for CGI environment
|
139
138
|
if os.getenv('GATEWAY_INTERFACE'):
|
140
139
|
return 'cgi'
|
140
|
+
|
141
|
+
# Check for AWS Lambda environment
|
141
142
|
if os.getenv('AWS_LAMBDA_FUNCTION_NAME') or os.getenv('LAMBDA_TASK_ROOT'):
|
142
143
|
return 'lambda'
|
144
|
+
|
145
|
+
# Check for Google Cloud Functions environment
|
146
|
+
if (os.getenv('FUNCTION_TARGET') or
|
147
|
+
os.getenv('K_SERVICE') or
|
148
|
+
os.getenv('GOOGLE_CLOUD_PROJECT')):
|
149
|
+
return 'google_cloud_function'
|
150
|
+
|
151
|
+
# Check for Azure Functions environment
|
152
|
+
if (os.getenv('AZURE_FUNCTIONS_ENVIRONMENT') or
|
153
|
+
os.getenv('FUNCTIONS_WORKER_RUNTIME') or
|
154
|
+
os.getenv('AzureWebJobsStorage')):
|
155
|
+
return 'azure_function'
|
156
|
+
|
157
|
+
# Default to server mode
|
143
158
|
return 'server'
|
144
159
|
|
145
160
|
|
@@ -1,15 +1,15 @@
|
|
1
|
-
signalwire_agents/__init__.py,sha256=
|
1
|
+
signalwire_agents/__init__.py,sha256=uXl3_WmqkymgMz9EDcAcLEXVXEbdL928PiK8--bJvYI,2707
|
2
2
|
signalwire_agents/agent_server.py,sha256=3Or8rIMAqW750V-XitBUMgOpW9BAIXmKXoGq7LkejAA,24988
|
3
3
|
signalwire_agents/schema.json,sha256=M8Mn6pQda2P9jhbmkALrLr1wt-fRuhYRqdmEi9Rbhqk,178075
|
4
4
|
signalwire_agents/cli/__init__.py,sha256=Iy2BfWDWBEZoA1cyHTDsooBSVMx4vH5Ddhr3sEuFe8c,197
|
5
5
|
signalwire_agents/cli/build_search.py,sha256=PnGoIZVfIbSF21rb0m0_ceIL-8lJx59_qXrrl8l2yBE,22340
|
6
6
|
signalwire_agents/cli/test_swaig.py,sha256=CqfdBWE8_fiFnFWHbv9ouU1eUA_rGUUHwyOf_iUaZFU,101851
|
7
7
|
signalwire_agents/core/__init__.py,sha256=mVDLbpq1pg_WwiqsQR28NNZwJ6-VUXFIfg-vN7pk0ew,806
|
8
|
-
signalwire_agents/core/agent_base.py,sha256=
|
8
|
+
signalwire_agents/core/agent_base.py,sha256=0MA1i6honMKfyEIyCrVgS7WAR-iL3v3n8u0ZI8B-hHA,155944
|
9
9
|
signalwire_agents/core/contexts.py,sha256=h7hra4xoiKAUdVyJhcKggl8X9EoqwTVWBmNMp-sEsuc,9598
|
10
10
|
signalwire_agents/core/data_map.py,sha256=U-HLEZQomWf-UI0-nLAE8g1oyRdE5bU_WxQpboI2YI4,17695
|
11
|
-
signalwire_agents/core/function_result.py,sha256=
|
12
|
-
signalwire_agents/core/logging_config.py,sha256=
|
11
|
+
signalwire_agents/core/function_result.py,sha256=GGmd2gAB9uJFFyldrhC86LCmpCnccU1bb-3gZVW2rxE,45360
|
12
|
+
signalwire_agents/core/logging_config.py,sha256=x4d_RAjBjVpJOFA2vXnPP2dNr13BZHz091J5rGpC77Y,13142
|
13
13
|
signalwire_agents/core/pom_builder.py,sha256=ywuiIfP8BeLBPo_G4X1teZlG6zTCMkW71CZnmyoDTAQ,6636
|
14
14
|
signalwire_agents/core/skill_base.py,sha256=lOpVTLhD9NjStF7Lxh6bAQUGa3DpNYV4agXJRakRuX0,4258
|
15
15
|
signalwire_agents/core/skill_manager.py,sha256=XWq4MeDQ3kmM4Th8qI-Gx5klokDHNZ-K9cChJPW-zmQ,8113
|
@@ -58,10 +58,10 @@ signalwire_agents/utils/pom_utils.py,sha256=4Mr7baQ_xR_hfJ72YxQRAT_GFa663YjFX_Pu
|
|
58
58
|
signalwire_agents/utils/schema_utils.py,sha256=i4okv_O9bUApwT_jJf4Yoij3bLCrGrW3DC-vzSy2RuY,16392
|
59
59
|
signalwire_agents/utils/token_generators.py,sha256=4Mr7baQ_xR_hfJ72YxQRAT_GFa663YjFX_PumJ35Xds,191
|
60
60
|
signalwire_agents/utils/validators.py,sha256=4Mr7baQ_xR_hfJ72YxQRAT_GFa663YjFX_PumJ35Xds,191
|
61
|
-
signalwire_agents-0.1.
|
62
|
-
signalwire_agents-0.1.
|
63
|
-
signalwire_agents-0.1.
|
64
|
-
signalwire_agents-0.1.
|
65
|
-
signalwire_agents-0.1.
|
66
|
-
signalwire_agents-0.1.
|
67
|
-
signalwire_agents-0.1.
|
61
|
+
signalwire_agents-0.1.16.data/data/schema.json,sha256=M8Mn6pQda2P9jhbmkALrLr1wt-fRuhYRqdmEi9Rbhqk,178075
|
62
|
+
signalwire_agents-0.1.16.dist-info/licenses/LICENSE,sha256=NYvAsB-rTcSvG9cqHt9EUHAWLiA9YzM4Qfz-mPdvDR0,1067
|
63
|
+
signalwire_agents-0.1.16.dist-info/METADATA,sha256=Wa9zmHC-iN-0ftjlPqeLdcjDsAhLpBT2b2WBCeajnkY,34572
|
64
|
+
signalwire_agents-0.1.16.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
65
|
+
signalwire_agents-0.1.16.dist-info/entry_points.txt,sha256=LRwltbVfaKUFMYmQoMxJGTT_-iQm0ftzXK0xPfD64Is,138
|
66
|
+
signalwire_agents-0.1.16.dist-info/top_level.txt,sha256=kDGS6ZYv84K9P5Kyg9_S8P_pbUXoHkso0On_DB5bbWc,18
|
67
|
+
signalwire_agents-0.1.16.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|