signalwire-agents 1.0.8__tar.gz → 1.0.10__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 (149) hide show
  1. {signalwire_agents-1.0.8/signalwire_agents.egg-info → signalwire_agents-1.0.10}/PKG-INFO +1 -1
  2. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/pyproject.toml +1 -1
  3. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/__init__.py +1 -1
  4. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/agent_base.py +21 -2
  5. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/mixins/auth_mixin.py +6 -13
  6. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/mixins/serverless_mixin.py +204 -112
  7. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/mixins/web_mixin.py +1 -1
  8. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10/signalwire_agents.egg-info}/PKG-INFO +1 -1
  9. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/LICENSE +0 -0
  10. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/README.md +0 -0
  11. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/man/sw-agent-init.1 +0 -0
  12. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/man/sw-search.1 +0 -0
  13. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/man/swaig-test.1 +0 -0
  14. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/setup.cfg +0 -0
  15. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/setup.py +0 -0
  16. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/agent_server.py +0 -0
  17. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/agents/bedrock.py +0 -0
  18. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/cli/__init__.py +0 -0
  19. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/cli/build_search.py +0 -0
  20. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/cli/config.py +0 -0
  21. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/cli/core/__init__.py +0 -0
  22. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/cli/core/agent_loader.py +0 -0
  23. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/cli/core/argparse_helpers.py +0 -0
  24. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/cli/core/dynamic_config.py +0 -0
  25. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/cli/core/service_loader.py +0 -0
  26. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/cli/execution/__init__.py +0 -0
  27. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/cli/execution/datamap_exec.py +0 -0
  28. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/cli/execution/webhook_exec.py +0 -0
  29. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/cli/init_project.py +0 -0
  30. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/cli/output/__init__.py +0 -0
  31. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/cli/output/output_formatter.py +0 -0
  32. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/cli/output/swml_dump.py +0 -0
  33. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/cli/simulation/__init__.py +0 -0
  34. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/cli/simulation/data_generation.py +0 -0
  35. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/cli/simulation/data_overrides.py +0 -0
  36. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/cli/simulation/mock_env.py +0 -0
  37. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/cli/swaig_test_wrapper.py +0 -0
  38. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/cli/test_swaig.py +0 -0
  39. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/cli/types.py +0 -0
  40. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/__init__.py +0 -0
  41. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/agent/__init__.py +0 -0
  42. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/agent/config/__init__.py +0 -0
  43. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/agent/deployment/__init__.py +0 -0
  44. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/agent/deployment/handlers/__init__.py +0 -0
  45. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/agent/prompt/__init__.py +0 -0
  46. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/agent/prompt/manager.py +0 -0
  47. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/agent/routing/__init__.py +0 -0
  48. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/agent/security/__init__.py +0 -0
  49. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/agent/swml/__init__.py +0 -0
  50. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/agent/tools/__init__.py +0 -0
  51. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/agent/tools/decorator.py +0 -0
  52. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/agent/tools/registry.py +0 -0
  53. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/auth_handler.py +0 -0
  54. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/config_loader.py +0 -0
  55. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/contexts.py +0 -0
  56. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/data_map.py +0 -0
  57. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/function_result.py +0 -0
  58. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/logging_config.py +0 -0
  59. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/mixins/__init__.py +0 -0
  60. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/mixins/ai_config_mixin.py +0 -0
  61. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/mixins/prompt_mixin.py +0 -0
  62. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/mixins/skill_mixin.py +0 -0
  63. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/mixins/state_mixin.py +0 -0
  64. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/mixins/tool_mixin.py +0 -0
  65. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/pom_builder.py +0 -0
  66. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/security/__init__.py +0 -0
  67. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/security/session_manager.py +0 -0
  68. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/security_config.py +0 -0
  69. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/skill_base.py +0 -0
  70. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/skill_manager.py +0 -0
  71. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/swaig_function.py +0 -0
  72. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/swml_builder.py +0 -0
  73. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/swml_handler.py +0 -0
  74. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/swml_renderer.py +0 -0
  75. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/core/swml_service.py +0 -0
  76. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/prefabs/__init__.py +0 -0
  77. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/prefabs/concierge.py +0 -0
  78. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/prefabs/faq_bot.py +0 -0
  79. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/prefabs/info_gatherer.py +0 -0
  80. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/prefabs/receptionist.py +0 -0
  81. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/prefabs/survey.py +0 -0
  82. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/schema.json +0 -0
  83. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/search/__init__.py +0 -0
  84. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/search/document_processor.py +0 -0
  85. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/search/index_builder.py +0 -0
  86. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/search/migration.py +0 -0
  87. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/search/models.py +0 -0
  88. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/search/pgvector_backend.py +0 -0
  89. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/search/query_processor.py +0 -0
  90. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/search/search_engine.py +0 -0
  91. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/search/search_service.py +0 -0
  92. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/README.md +0 -0
  93. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/__init__.py +0 -0
  94. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/api_ninjas_trivia/README.md +0 -0
  95. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/api_ninjas_trivia/__init__.py +0 -0
  96. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/api_ninjas_trivia/skill.py +0 -0
  97. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/datasphere/README.md +0 -0
  98. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/datasphere/__init__.py +0 -0
  99. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/datasphere/skill.py +0 -0
  100. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/datasphere_serverless/README.md +0 -0
  101. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/datasphere_serverless/__init__.py +0 -0
  102. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/datasphere_serverless/skill.py +0 -0
  103. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/datetime/README.md +0 -0
  104. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/datetime/__init__.py +0 -0
  105. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/datetime/skill.py +0 -0
  106. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/joke/README.md +0 -0
  107. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/joke/__init__.py +0 -0
  108. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/joke/skill.py +0 -0
  109. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/math/README.md +0 -0
  110. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/math/__init__.py +0 -0
  111. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/math/skill.py +0 -0
  112. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/mcp_gateway/README.md +0 -0
  113. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/mcp_gateway/__init__.py +0 -0
  114. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/mcp_gateway/skill.py +0 -0
  115. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/native_vector_search/README.md +0 -0
  116. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/native_vector_search/__init__.py +0 -0
  117. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/native_vector_search/skill.py +0 -0
  118. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/play_background_file/README.md +0 -0
  119. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/play_background_file/__init__.py +0 -0
  120. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/play_background_file/skill.py +0 -0
  121. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/registry.py +0 -0
  122. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/spider/README.md +0 -0
  123. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/spider/__init__.py +0 -0
  124. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/spider/skill.py +0 -0
  125. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/swml_transfer/README.md +0 -0
  126. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/swml_transfer/__init__.py +0 -0
  127. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/swml_transfer/skill.py +0 -0
  128. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/weather_api/README.md +0 -0
  129. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/weather_api/__init__.py +0 -0
  130. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/weather_api/skill.py +0 -0
  131. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/web_search/README.md +0 -0
  132. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/web_search/__init__.py +0 -0
  133. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/web_search/skill.py +0 -0
  134. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/wikipedia_search/README.md +0 -0
  135. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/wikipedia_search/__init__.py +0 -0
  136. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/skills/wikipedia_search/skill.py +0 -0
  137. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/utils/__init__.py +0 -0
  138. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/utils/pom_utils.py +0 -0
  139. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/utils/schema_utils.py +0 -0
  140. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/utils/token_generators.py +0 -0
  141. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/utils/validators.py +0 -0
  142. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/web/__init__.py +0 -0
  143. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents/web/web_service.py +0 -0
  144. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents.egg-info/SOURCES.txt +0 -0
  145. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents.egg-info/dependency_links.txt +0 -0
  146. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents.egg-info/entry_points.txt +0 -0
  147. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents.egg-info/requires.txt +0 -0
  148. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/signalwire_agents.egg-info/top_level.txt +0 -0
  149. {signalwire_agents-1.0.8 → signalwire_agents-1.0.10}/tests/test_examples.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: signalwire_agents
3
- Version: 1.0.8
3
+ Version: 1.0.10
4
4
  Summary: SignalWire AI Agents SDK
5
5
  Author-email: SignalWire Team <info@signalwire.com>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "signalwire_agents"
7
- version = "1.0.8"
7
+ version = "1.0.10"
8
8
  description = "SignalWire AI Agents SDK"
9
9
  authors = [
10
10
  {name = "SignalWire Team", email = "info@signalwire.com"}
@@ -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.8"
21
+ __version__ = "1.0.10"
22
22
 
23
23
  # Import core classes for easier access
24
24
  from .core.agent_base import AgentBase
@@ -317,13 +317,32 @@ class AgentBase(
317
317
  def get_full_url(self, include_auth: bool = False) -> str:
318
318
  """
319
319
  Get the full URL for this agent's endpoint
320
-
320
+
321
321
  Args:
322
322
  include_auth: Whether to include authentication credentials in the URL
323
-
323
+
324
324
  Returns:
325
325
  Full URL including host, port, and route (with auth if requested)
326
326
  """
327
+ # If _proxy_url_base is set (e.g., from request URL detection), use it
328
+ if hasattr(self, '_proxy_url_base') and self._proxy_url_base:
329
+ base_url = self._proxy_url_base.rstrip('/')
330
+ # Add authentication if requested
331
+ if include_auth:
332
+ username, password = self.get_basic_auth_credentials()
333
+ if username and password:
334
+ from urllib.parse import urlparse, urlunparse
335
+ parsed = urlparse(base_url)
336
+ base_url = urlunparse((
337
+ parsed.scheme,
338
+ f"{username}:{password}@{parsed.netloc}",
339
+ parsed.path,
340
+ parsed.params,
341
+ parsed.query,
342
+ parsed.fragment
343
+ ))
344
+ return base_url
345
+
327
346
  mode = get_execution_mode()
328
347
 
329
348
  if mode == 'cgi':
@@ -196,11 +196,8 @@ class AuthMixin:
196
196
  return False
197
197
 
198
198
  # Check for authorization header (case-insensitive)
199
- auth_header = None
200
- for key in request.headers:
201
- if key.lower() == 'authorization':
202
- auth_header = request.headers[key]
203
- break
199
+ # Flask headers can be accessed directly with .get() which is case-insensitive
200
+ auth_header = request.headers.get('Authorization')
204
201
 
205
202
  if not auth_header or not auth_header.startswith('Basic '):
206
203
  return False
@@ -246,14 +243,10 @@ class AuthMixin:
246
243
  """
247
244
  if not hasattr(req, 'headers'):
248
245
  return False
249
-
250
- # Check for authorization header (case-insensitive)
251
- auth_header = None
252
- for key, value in req.headers.items():
253
- if key.lower() == 'authorization':
254
- auth_header = value
255
- break
256
-
246
+
247
+ # Check for authorization header - use .get() which works with both dict and Flask headers
248
+ auth_header = req.headers.get('Authorization')
249
+
257
250
  if not auth_header or not auth_header.startswith('Basic '):
258
251
  return False
259
252
 
@@ -80,52 +80,76 @@ class ServerlessMixin:
80
80
  # Check authentication in Lambda mode
81
81
  if not self._check_lambda_auth(event):
82
82
  return self._send_lambda_auth_challenge()
83
-
83
+
84
84
  if event:
85
- path = event.get('pathParameters', {}).get('proxy', '') if event.get('pathParameters') else ''
86
- if not path:
87
- swml_response = self._render_swml()
85
+ # Support both HTTP API (v2) and REST API (v1) payload formats
86
+ # HTTP API v2 uses rawPath, REST API v1 uses pathParameters.proxy
87
+ path = event.get('rawPath', '').strip('/')
88
+ if not path and event.get('pathParameters'):
89
+ path = event.get('pathParameters', {}).get('proxy', '')
90
+
91
+ # Parse request body if present
92
+ args = {}
93
+ call_id = None
94
+ raw_data = None
95
+ function_name = None
96
+
97
+ body_content = event.get('body')
98
+ if body_content:
99
+ try:
100
+ if event.get('isBase64Encoded'):
101
+ import base64
102
+ body_content = base64.b64decode(body_content).decode('utf-8')
103
+ if isinstance(body_content, str):
104
+ raw_data = json.loads(body_content)
105
+ else:
106
+ raw_data = body_content
107
+
108
+ call_id = raw_data.get("call_id")
109
+ function_name = raw_data.get("function")
110
+
111
+ # Extract arguments like the FastAPI handler does
112
+ if "argument" in raw_data and isinstance(raw_data["argument"], dict):
113
+ if "parsed" in raw_data["argument"] and isinstance(raw_data["argument"]["parsed"], list) and raw_data["argument"]["parsed"]:
114
+ args = raw_data["argument"]["parsed"][0]
115
+ elif "raw" in raw_data["argument"]:
116
+ try:
117
+ args = json.loads(raw_data["argument"]["raw"])
118
+ except Exception:
119
+ pass
120
+ except Exception:
121
+ # If parsing fails, continue with empty args
122
+ pass
123
+
124
+ # Determine if this is a SWAIG function call
125
+ # Case 1: /swaig endpoint with function name in body
126
+ # Case 2: /{function_name} path-based routing
127
+ # Case 3: Root path - return SWML
128
+
129
+ if path in ('swaig', 'swaig/') and function_name:
130
+ # /swaig endpoint with function name in body
131
+ result = self._execute_swaig_function(function_name, args, call_id, raw_data)
88
132
  return {
89
133
  "statusCode": 200,
90
134
  "headers": {"Content-Type": "application/json"},
91
- "body": swml_response
135
+ "body": json.dumps(result) if isinstance(result, dict) else str(result)
92
136
  }
93
- else:
94
- # Parse Lambda event for SWAIG function call
95
- args = {}
96
- call_id = None
97
- raw_data = None
98
-
99
- # Parse request body if present
100
- body_content = event.get('body')
101
- if body_content:
102
- try:
103
- if isinstance(body_content, str):
104
- raw_data = json.loads(body_content)
105
- else:
106
- raw_data = body_content
107
-
108
- call_id = raw_data.get("call_id")
109
-
110
- # Extract arguments like the FastAPI handler does
111
- if "argument" in raw_data and isinstance(raw_data["argument"], dict):
112
- if "parsed" in raw_data["argument"] and isinstance(raw_data["argument"]["parsed"], list) and raw_data["argument"]["parsed"]:
113
- args = raw_data["argument"]["parsed"][0]
114
- elif "raw" in raw_data["argument"]:
115
- try:
116
- args = json.loads(raw_data["argument"]["raw"])
117
- except Exception:
118
- pass
119
- except Exception:
120
- # If parsing fails, continue with empty args
121
- pass
122
-
137
+ elif path and path not in ('', 'swaig', 'swaig/'):
138
+ # Path-based function routing (e.g., /say_hello)
123
139
  result = self._execute_swaig_function(path, args, call_id, raw_data)
124
140
  return {
125
141
  "statusCode": 200,
126
142
  "headers": {"Content-Type": "application/json"},
127
143
  "body": json.dumps(result) if isinstance(result, dict) else str(result)
128
144
  }
145
+ else:
146
+ # Root path or /swaig without function - return SWML
147
+ swml_response = self._render_swml()
148
+ return {
149
+ "statusCode": 200,
150
+ "headers": {"Content-Type": "application/json"},
151
+ "body": swml_response
152
+ }
129
153
  else:
130
154
  # Handle case when event is None (direct Lambda call with no event)
131
155
  swml_response = self._render_swml()
@@ -152,7 +176,9 @@ class ServerlessMixin:
152
176
 
153
177
  except Exception as e:
154
178
  import logging
179
+ import traceback
155
180
  logging.error(f"Error in serverless request handler: {e}")
181
+ logging.error(f"Traceback: {traceback.format_exc()}")
156
182
  if mode == 'lambda':
157
183
  return {
158
184
  "statusCode": 500,
@@ -232,63 +258,91 @@ class ServerlessMixin:
232
258
  def _handle_google_cloud_function_request(self, request):
233
259
  """
234
260
  Handle Google Cloud Functions specific requests
235
-
261
+
236
262
  Args:
237
263
  request: Flask request object from Google Cloud Functions
238
-
264
+
239
265
  Returns:
240
266
  Flask response object
241
267
  """
242
268
  try:
269
+ from urllib.parse import urlparse
270
+
243
271
  # Get the path from the request
244
272
  path = request.path.strip('/')
245
-
246
- if not path:
247
- # Root request - return SWML
248
- swml_response = self._render_swml()
249
- from flask import Response
273
+
274
+ # Try to detect and set the base URL from the request for webhook URLs
275
+ base_url = None
276
+ if hasattr(request, 'url') and request.url:
277
+ parsed = urlparse(request.url)
278
+ # Get the base URL without the path
279
+ base_url = f"{parsed.scheme}://{parsed.netloc}"
280
+
281
+ # Set the proxy URL base so SWML renders correct webhook URLs
282
+ if base_url and not getattr(self, '_proxy_url_base_from_env', False):
283
+ self._proxy_url_base = base_url
284
+
285
+ # Parse request body if present
286
+ args = {}
287
+ call_id = None
288
+ raw_data = None
289
+ function_name = None
290
+
291
+ if request.method == 'POST':
292
+ try:
293
+ if request.is_json:
294
+ raw_data = request.get_json()
295
+ else:
296
+ raw_data = json.loads(request.get_data(as_text=True))
297
+
298
+ call_id = raw_data.get("call_id")
299
+ function_name = raw_data.get("function")
300
+
301
+ # Extract arguments like the FastAPI handler does
302
+ if "argument" in raw_data and isinstance(raw_data["argument"], dict):
303
+ if "parsed" in raw_data["argument"] and isinstance(raw_data["argument"]["parsed"], list) and raw_data["argument"]["parsed"]:
304
+ args = raw_data["argument"]["parsed"][0]
305
+ elif "raw" in raw_data["argument"]:
306
+ try:
307
+ args = json.loads(raw_data["argument"]["raw"])
308
+ except Exception:
309
+ pass
310
+ except Exception:
311
+ # If parsing fails, continue with empty args
312
+ pass
313
+
314
+ # Determine if this is a SWAIG function call
315
+ # Case 1: /swaig endpoint with function name in body
316
+ # Case 2: /{function_name} path-based routing
317
+ # Case 3: Root path - return SWML
318
+
319
+ from flask import Response
320
+
321
+ if path in ('swaig', 'swaig/') and function_name:
322
+ # /swaig endpoint with function name in body
323
+ result = self._execute_swaig_function(function_name, args, call_id, raw_data)
250
324
  return Response(
251
- response=swml_response,
325
+ response=json.dumps(result) if isinstance(result, dict) else str(result),
252
326
  status=200,
253
327
  headers={"Content-Type": "application/json"}
254
328
  )
255
- else:
256
- # SWAIG function call
257
- args = {}
258
- call_id = None
259
- raw_data = None
260
-
261
- # Parse request data
262
- if request.method == 'POST':
263
- try:
264
- if request.is_json:
265
- raw_data = request.get_json()
266
- else:
267
- raw_data = json.loads(request.get_data(as_text=True))
268
-
269
- call_id = raw_data.get("call_id")
270
-
271
- # Extract arguments like the FastAPI handler does
272
- if "argument" in raw_data and isinstance(raw_data["argument"], dict):
273
- if "parsed" in raw_data["argument"] and isinstance(raw_data["argument"]["parsed"], list) and raw_data["argument"]["parsed"]:
274
- args = raw_data["argument"]["parsed"][0]
275
- elif "raw" in raw_data["argument"]:
276
- try:
277
- args = json.loads(raw_data["argument"]["raw"])
278
- except Exception:
279
- pass
280
- except Exception:
281
- # If parsing fails, continue with empty args
282
- pass
283
-
329
+ elif path and path not in ('', 'swaig', 'swaig/'):
330
+ # Path-based function routing (e.g., /say_hello)
284
331
  result = self._execute_swaig_function(path, args, call_id, raw_data)
285
- from flask import Response
286
332
  return Response(
287
333
  response=json.dumps(result) if isinstance(result, dict) else str(result),
288
334
  status=200,
289
335
  headers={"Content-Type": "application/json"}
290
336
  )
291
-
337
+ else:
338
+ # Root path or /swaig without function - return SWML
339
+ swml_response = self._render_swml()
340
+ return Response(
341
+ response=swml_response,
342
+ status=200,
343
+ headers={"Content-Type": "application/json"}
344
+ )
345
+
292
346
  except Exception as e:
293
347
  import logging
294
348
  logging.error(f"Error in Google Cloud Function request handler: {e}")
@@ -302,61 +356,99 @@ class ServerlessMixin:
302
356
  def _handle_azure_function_request(self, req):
303
357
  """
304
358
  Handle Azure Functions specific requests
305
-
359
+
306
360
  Args:
307
361
  req: Azure Functions HttpRequest object
308
-
362
+
309
363
  Returns:
310
364
  Azure Functions HttpResponse object
311
365
  """
312
366
  try:
313
367
  import azure.functions as func
314
-
315
- # Get the path from the request
316
- path = req.url.split('/')[-1] if req.url else ''
317
-
318
- if not path or path == 'api':
319
- # Root request - return SWML
320
- swml_response = self._render_swml()
368
+ from urllib.parse import urlparse
369
+
370
+ # Get the path from the request URL
371
+ # Azure Functions URLs look like: https://app.azurewebsites.net/api/function_name/path
372
+ path = ''
373
+ base_url = None
374
+ if req.url:
375
+ parsed = urlparse(req.url)
376
+ # Full path after /api/ e.g. "function_app" or "function_app/swaig"
377
+ url_parts = req.url.split('/api/')
378
+ if len(url_parts) > 1:
379
+ full_path = url_parts[1].strip('/')
380
+ # Split into function name and sub-path
381
+ path_parts = full_path.split('/', 1)
382
+ function_app_name = path_parts[0] if path_parts else ''
383
+ path = path_parts[1] if len(path_parts) > 1 else ''
384
+
385
+ # Base URL includes the function app name for webhook URLs
386
+ # e.g., https://app.azurewebsites.net/api/function_app
387
+ base_url = f"{parsed.scheme}://{parsed.netloc}/api/{function_app_name}"
388
+ else:
389
+ base_url = f"{parsed.scheme}://{parsed.netloc}/api"
390
+
391
+ # Set the proxy URL base so SWML renders correct webhook URLs
392
+ if base_url and not getattr(self, '_proxy_url_base_from_env', False):
393
+ self._proxy_url_base = base_url
394
+
395
+ # Parse request body if present
396
+ args = {}
397
+ call_id = None
398
+ raw_data = None
399
+ function_name = None
400
+
401
+ if req.method == 'POST':
402
+ try:
403
+ body = req.get_body()
404
+ if body:
405
+ raw_data = json.loads(body.decode('utf-8'))
406
+ call_id = raw_data.get("call_id")
407
+ function_name = raw_data.get("function")
408
+
409
+ # Extract arguments like the FastAPI handler does
410
+ if "argument" in raw_data and isinstance(raw_data["argument"], dict):
411
+ if "parsed" in raw_data["argument"] and isinstance(raw_data["argument"]["parsed"], list) and raw_data["argument"]["parsed"]:
412
+ args = raw_data["argument"]["parsed"][0]
413
+ elif "raw" in raw_data["argument"]:
414
+ try:
415
+ args = json.loads(raw_data["argument"]["raw"])
416
+ except Exception:
417
+ pass
418
+ except Exception:
419
+ # If parsing fails, continue with empty args
420
+ pass
421
+
422
+ # Determine if this is a SWAIG function call
423
+ # Case 1: /swaig endpoint with function name in body
424
+ # Case 2: /{function_name} path-based routing
425
+ # Case 3: Root path - return SWML
426
+
427
+ if path in ('swaig', 'swaig/') and function_name:
428
+ # /swaig endpoint with function name in body
429
+ result = self._execute_swaig_function(function_name, args, call_id, raw_data)
321
430
  return func.HttpResponse(
322
- body=swml_response,
431
+ body=json.dumps(result) if isinstance(result, dict) else str(result),
323
432
  status_code=200,
324
433
  headers={"Content-Type": "application/json"}
325
434
  )
326
- else:
327
- # SWAIG function call
328
- args = {}
329
- call_id = None
330
- raw_data = None
331
-
332
- # Parse request data
333
- if req.method == 'POST':
334
- try:
335
- body = req.get_body()
336
- if body:
337
- raw_data = json.loads(body.decode('utf-8'))
338
- call_id = raw_data.get("call_id")
339
-
340
- # Extract arguments like the FastAPI handler does
341
- if "argument" in raw_data and isinstance(raw_data["argument"], dict):
342
- if "parsed" in raw_data["argument"] and isinstance(raw_data["argument"]["parsed"], list) and raw_data["argument"]["parsed"]:
343
- args = raw_data["argument"]["parsed"][0]
344
- elif "raw" in raw_data["argument"]:
345
- try:
346
- args = json.loads(raw_data["argument"]["raw"])
347
- except Exception:
348
- pass
349
- except Exception:
350
- # If parsing fails, continue with empty args
351
- pass
352
-
435
+ elif path and path not in ('', 'api', 'swaig', 'swaig/'):
436
+ # Path-based function routing (e.g., /say_hello)
353
437
  result = self._execute_swaig_function(path, args, call_id, raw_data)
354
438
  return func.HttpResponse(
355
439
  body=json.dumps(result) if isinstance(result, dict) else str(result),
356
440
  status_code=200,
357
441
  headers={"Content-Type": "application/json"}
358
442
  )
359
-
443
+ else:
444
+ # Root path or /swaig without function - return SWML
445
+ swml_response = self._render_swml()
446
+ return func.HttpResponse(
447
+ body=swml_response,
448
+ status_code=200,
449
+ headers={"Content-Type": "application/json"}
450
+ )
451
+
360
452
  except Exception as e:
361
453
  import logging
362
454
  logging.error(f"Error in Azure Function request handler: {e}")
@@ -302,7 +302,7 @@ class WebMixin:
302
302
  response = self.handle_serverless_request(event, context, mode)
303
303
  print(response)
304
304
  return response
305
- elif mode == 'lambda':
305
+ elif mode in ['lambda', 'google_cloud_function']:
306
306
  return self.handle_serverless_request(event, context, mode)
307
307
  else:
308
308
  # Server mode - use existing serve method
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: signalwire_agents
3
- Version: 1.0.8
3
+ Version: 1.0.10
4
4
  Summary: SignalWire AI Agents SDK
5
5
  Author-email: SignalWire Team <info@signalwire.com>
6
6
  License: MIT