signalwire-agents 1.0.4__tar.gz → 1.0.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 (145) hide show
  1. {signalwire_agents-1.0.4/signalwire_agents.egg-info → signalwire_agents-1.0.6}/PKG-INFO +2 -2
  2. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/pyproject.toml +3 -3
  3. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/__init__.py +1 -1
  4. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/agent_base.py +4 -3
  5. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6/signalwire_agents.egg-info}/PKG-INFO +2 -2
  6. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents.egg-info/SOURCES.txt +2 -1
  7. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents.egg-info/requires.txt +1 -1
  8. signalwire_agents-1.0.6/tests/test_examples.py +510 -0
  9. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/LICENSE +0 -0
  10. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/README.md +0 -0
  11. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/setup.cfg +0 -0
  12. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/setup.py +0 -0
  13. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/agent_server.py +0 -0
  14. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/agents/bedrock.py +0 -0
  15. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/cli/__init__.py +0 -0
  16. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/cli/build_search.py +0 -0
  17. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/cli/config.py +0 -0
  18. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/cli/core/__init__.py +0 -0
  19. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/cli/core/agent_loader.py +0 -0
  20. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/cli/core/argparse_helpers.py +0 -0
  21. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/cli/core/dynamic_config.py +0 -0
  22. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/cli/core/service_loader.py +0 -0
  23. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/cli/execution/__init__.py +0 -0
  24. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/cli/execution/datamap_exec.py +0 -0
  25. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/cli/execution/webhook_exec.py +0 -0
  26. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/cli/output/__init__.py +0 -0
  27. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/cli/output/output_formatter.py +0 -0
  28. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/cli/output/swml_dump.py +0 -0
  29. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/cli/simulation/__init__.py +0 -0
  30. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/cli/simulation/data_generation.py +0 -0
  31. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/cli/simulation/data_overrides.py +0 -0
  32. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/cli/simulation/mock_env.py +0 -0
  33. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/cli/swaig_test_wrapper.py +0 -0
  34. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/cli/test_swaig.py +0 -0
  35. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/cli/types.py +0 -0
  36. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/__init__.py +0 -0
  37. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/agent/__init__.py +0 -0
  38. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/agent/config/__init__.py +0 -0
  39. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/agent/deployment/__init__.py +0 -0
  40. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/agent/deployment/handlers/__init__.py +0 -0
  41. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/agent/prompt/__init__.py +0 -0
  42. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/agent/prompt/manager.py +0 -0
  43. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/agent/routing/__init__.py +0 -0
  44. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/agent/security/__init__.py +0 -0
  45. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/agent/swml/__init__.py +0 -0
  46. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/agent/tools/__init__.py +0 -0
  47. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/agent/tools/decorator.py +0 -0
  48. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/agent/tools/registry.py +0 -0
  49. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/auth_handler.py +0 -0
  50. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/config_loader.py +0 -0
  51. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/contexts.py +0 -0
  52. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/data_map.py +0 -0
  53. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/function_result.py +0 -0
  54. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/logging_config.py +0 -0
  55. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/mixins/__init__.py +0 -0
  56. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/mixins/ai_config_mixin.py +0 -0
  57. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/mixins/auth_mixin.py +0 -0
  58. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/mixins/prompt_mixin.py +0 -0
  59. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/mixins/serverless_mixin.py +0 -0
  60. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/mixins/skill_mixin.py +0 -0
  61. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/mixins/state_mixin.py +0 -0
  62. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/mixins/tool_mixin.py +0 -0
  63. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/mixins/web_mixin.py +0 -0
  64. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/pom_builder.py +0 -0
  65. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/security/__init__.py +0 -0
  66. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/security/session_manager.py +0 -0
  67. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/security_config.py +0 -0
  68. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/skill_base.py +0 -0
  69. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/skill_manager.py +0 -0
  70. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/swaig_function.py +0 -0
  71. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/swml_builder.py +0 -0
  72. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/swml_handler.py +0 -0
  73. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/swml_renderer.py +0 -0
  74. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/core/swml_service.py +0 -0
  75. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/prefabs/__init__.py +0 -0
  76. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/prefabs/concierge.py +0 -0
  77. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/prefabs/faq_bot.py +0 -0
  78. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/prefabs/info_gatherer.py +0 -0
  79. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/prefabs/receptionist.py +0 -0
  80. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/prefabs/survey.py +0 -0
  81. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/schema.json +0 -0
  82. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/search/__init__.py +0 -0
  83. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/search/document_processor.py +0 -0
  84. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/search/index_builder.py +0 -0
  85. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/search/migration.py +0 -0
  86. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/search/models.py +0 -0
  87. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/search/pgvector_backend.py +0 -0
  88. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/search/query_processor.py +0 -0
  89. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/search/search_engine.py +0 -0
  90. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/search/search_service.py +0 -0
  91. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/README.md +0 -0
  92. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/__init__.py +0 -0
  93. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/api_ninjas_trivia/README.md +0 -0
  94. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/api_ninjas_trivia/__init__.py +0 -0
  95. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/api_ninjas_trivia/skill.py +0 -0
  96. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/datasphere/README.md +0 -0
  97. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/datasphere/__init__.py +0 -0
  98. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/datasphere/skill.py +0 -0
  99. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/datasphere_serverless/README.md +0 -0
  100. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/datasphere_serverless/__init__.py +0 -0
  101. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/datasphere_serverless/skill.py +0 -0
  102. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/datetime/README.md +0 -0
  103. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/datetime/__init__.py +0 -0
  104. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/datetime/skill.py +0 -0
  105. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/joke/README.md +0 -0
  106. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/joke/__init__.py +0 -0
  107. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/joke/skill.py +0 -0
  108. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/math/README.md +0 -0
  109. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/math/__init__.py +0 -0
  110. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/math/skill.py +0 -0
  111. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/mcp_gateway/README.md +0 -0
  112. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/mcp_gateway/__init__.py +0 -0
  113. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/mcp_gateway/skill.py +0 -0
  114. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/native_vector_search/README.md +0 -0
  115. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/native_vector_search/__init__.py +0 -0
  116. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/native_vector_search/skill.py +0 -0
  117. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/play_background_file/README.md +0 -0
  118. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/play_background_file/__init__.py +0 -0
  119. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/play_background_file/skill.py +0 -0
  120. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/registry.py +0 -0
  121. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/spider/README.md +0 -0
  122. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/spider/__init__.py +0 -0
  123. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/spider/skill.py +0 -0
  124. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/swml_transfer/README.md +0 -0
  125. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/swml_transfer/__init__.py +0 -0
  126. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/swml_transfer/skill.py +0 -0
  127. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/weather_api/README.md +0 -0
  128. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/weather_api/__init__.py +0 -0
  129. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/weather_api/skill.py +0 -0
  130. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/web_search/README.md +0 -0
  131. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/web_search/__init__.py +0 -0
  132. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/web_search/skill.py +0 -0
  133. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/wikipedia_search/README.md +0 -0
  134. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/wikipedia_search/__init__.py +0 -0
  135. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/skills/wikipedia_search/skill.py +0 -0
  136. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/utils/__init__.py +0 -0
  137. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/utils/pom_utils.py +0 -0
  138. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/utils/schema_utils.py +0 -0
  139. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/utils/token_generators.py +0 -0
  140. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/utils/validators.py +0 -0
  141. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/web/__init__.py +0 -0
  142. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents/web/web_service.py +0 -0
  143. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents.egg-info/dependency_links.txt +0 -0
  144. {signalwire_agents-1.0.4 → signalwire_agents-1.0.6}/signalwire_agents.egg-info/entry_points.txt +0 -0
  145. {signalwire_agents-1.0.4 → signalwire_agents-1.0.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: 1.0.4
3
+ Version: 1.0.6
4
4
  Summary: SignalWire AI Agents SDK
5
5
  Author-email: SignalWire Team <info@signalwire.com>
6
6
  License: MIT
@@ -22,7 +22,7 @@ Requires-Dist: fastapi>=0.115.12
22
22
  Requires-Dist: pydantic>=2.11.4
23
23
  Requires-Dist: PyYAML>=6.0.2
24
24
  Requires-Dist: Requests>=2.32.3
25
- Requires-Dist: setuptools>=66.1.1
25
+ Requires-Dist: setuptools<81,>=66.1.1
26
26
  Requires-Dist: signalwire_pom>=2.7.1
27
27
  Requires-Dist: structlog>=25.3.0
28
28
  Requires-Dist: uvicorn>=0.34.2
@@ -1,10 +1,10 @@
1
1
  [build-system]
2
- requires = ["setuptools>=61.0", "wheel"]
2
+ requires = ["setuptools>=61.0,<81", "wheel"]
3
3
  build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "signalwire_agents"
7
- version = "1.0.4"
7
+ version = "1.0.6"
8
8
  description = "SignalWire AI Agents SDK"
9
9
  authors = [
10
10
  {name = "SignalWire Team", email = "info@signalwire.com"}
@@ -29,7 +29,7 @@ dependencies = [
29
29
  "pydantic>=2.11.4",
30
30
  "PyYAML>=6.0.2",
31
31
  "Requests>=2.32.3",
32
- "setuptools>=66.1.1",
32
+ "setuptools>=66.1.1,<81",
33
33
  "signalwire_pom>=2.7.1",
34
34
  "structlog>=25.3.0",
35
35
  "uvicorn>=0.34.2",
@@ -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.4"
21
+ __version__ = "1.0.6"
22
22
 
23
23
  # Import core classes for easier access
24
24
  from .core.agent_base import AgentBase
@@ -1231,9 +1231,10 @@ class AgentBase(
1231
1231
  if hasattr(self, '_internal_fillers'):
1232
1232
  ephemeral_agent._internal_fillers = copy.deepcopy(self._internal_fillers)
1233
1233
 
1234
- # Copy contexts builder state if it exists
1235
- if hasattr(self, '_contexts_builder'):
1236
- ephemeral_agent._contexts_builder = copy.deepcopy(self._contexts_builder)
1234
+ # Don't deep copy _contexts_builder - it has a circular reference to the agent
1235
+ # The contexts are already copied via _prompt_manager._contexts (below)
1236
+ # Just copy the flag indicating contexts are defined
1237
+ if hasattr(self, '_contexts_defined'):
1237
1238
  ephemeral_agent._contexts_defined = self._contexts_defined
1238
1239
 
1239
1240
  # Deep copy the POM object if it exists to prevent sharing prompt sections
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: signalwire_agents
3
- Version: 1.0.4
3
+ Version: 1.0.6
4
4
  Summary: SignalWire AI Agents SDK
5
5
  Author-email: SignalWire Team <info@signalwire.com>
6
6
  License: MIT
@@ -22,7 +22,7 @@ Requires-Dist: fastapi>=0.115.12
22
22
  Requires-Dist: pydantic>=2.11.4
23
23
  Requires-Dist: PyYAML>=6.0.2
24
24
  Requires-Dist: Requests>=2.32.3
25
- Requires-Dist: setuptools>=66.1.1
25
+ Requires-Dist: setuptools<81,>=66.1.1
26
26
  Requires-Dist: signalwire_pom>=2.7.1
27
27
  Requires-Dist: structlog>=25.3.0
28
28
  Requires-Dist: uvicorn>=0.34.2
@@ -139,4 +139,5 @@ signalwire_agents/utils/schema_utils.py
139
139
  signalwire_agents/utils/token_generators.py
140
140
  signalwire_agents/utils/validators.py
141
141
  signalwire_agents/web/__init__.py
142
- signalwire_agents/web/web_service.py
142
+ signalwire_agents/web/web_service.py
143
+ tests/test_examples.py
@@ -2,7 +2,7 @@ fastapi>=0.115.12
2
2
  pydantic>=2.11.4
3
3
  PyYAML>=6.0.2
4
4
  Requests>=2.32.3
5
- setuptools>=66.1.1
5
+ setuptools<81,>=66.1.1
6
6
  signalwire_pom>=2.7.1
7
7
  structlog>=25.3.0
8
8
  uvicorn>=0.34.2
@@ -0,0 +1,510 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Test suite for all examples in the examples/ directory.
4
+
5
+ This test suite verifies that all example files:
6
+ 1. Can be loaded without import errors
7
+ 2. Generate valid SWML output
8
+ 3. Have properly defined tools/functions
9
+
10
+ Usage:
11
+ pytest tests/test_examples.py -v
12
+ pytest tests/test_examples.py -v -k "test_agent"
13
+ pytest tests/test_examples.py -v --tb=short
14
+ """
15
+
16
+ import subprocess
17
+ import json
18
+ import sys
19
+ from pathlib import Path
20
+
21
+ import pytest
22
+
23
+ # Get the examples directory
24
+ REPO_ROOT = Path(__file__).parent.parent
25
+ EXAMPLES_DIR = REPO_ROOT / "examples"
26
+
27
+
28
+ def run_swaig_test(agent_path: Path, *args, timeout: int = 30) -> tuple:
29
+ """
30
+ Run swaig-test on an agent file and return (returncode, stdout, stderr).
31
+ """
32
+ cmd = [sys.executable, "-m", "signalwire_agents.cli.swaig_test_wrapper", str(agent_path)] + list(args)
33
+ try:
34
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
35
+ return result.returncode, result.stdout, result.stderr
36
+ except subprocess.TimeoutExpired:
37
+ return -1, "", "Timeout expired"
38
+
39
+
40
+ def get_swml_json(agent_path: Path) -> dict:
41
+ """
42
+ Get SWML JSON output from an agent file.
43
+ """
44
+ returncode, stdout, stderr = run_swaig_test(agent_path, "--dump-swml", "--raw")
45
+ if returncode != 0:
46
+ pytest.fail(f"swaig-test failed for {agent_path}:\nstderr: {stderr}\nstdout: {stdout}")
47
+ try:
48
+ return json.loads(stdout)
49
+ except json.JSONDecodeError as e:
50
+ pytest.fail(f"Invalid JSON from {agent_path}: {e}\nOutput: {stdout}")
51
+
52
+
53
+ def list_tools(agent_path: Path) -> list:
54
+ """
55
+ List tools available in an agent.
56
+ """
57
+ returncode, stdout, stderr = run_swaig_test(agent_path, "--list-tools")
58
+ if returncode != 0:
59
+ return []
60
+ tools = []
61
+ for line in stdout.split('\n'):
62
+ line = line.strip()
63
+ if ' - ' in line and not line.startswith('Parameters:'):
64
+ parts = line.split(' - ')
65
+ if parts:
66
+ tool_name = parts[0].strip()
67
+ if tool_name and not tool_name.startswith('('):
68
+ tools.append(tool_name)
69
+ return tools
70
+
71
+
72
+ # Examples that require API keys or special environment variables
73
+ EXAMPLES_REQUIRING_CREDENTIALS = {
74
+ "joke_agent.py": "API_NINJAS_KEY",
75
+ "joke_skill_demo.py": "API_NINJAS_KEY",
76
+ "web_search_agent.py": "GOOGLE_SEARCH_API_KEY",
77
+ "datasphere_serverless_env_demo.py": "SIGNALWIRE_SPACE_NAME",
78
+ "datasphere_webhook_env_demo.py": "SIGNALWIRE_SPACE_NAME",
79
+ "env_auth_simple.py": "SWML_BASIC_AUTH_USER",
80
+ }
81
+
82
+ # Examples that are not standard agents (demos, tests, interactive)
83
+ NON_STANDARD_EXAMPLES = {
84
+ "swml_service_example.py", # Interactive menu
85
+ "basic_swml_service.py", # Has its own CLI
86
+ "dynamic_swml_service.py", # Has its own CLI
87
+ "auto_vivified_example.py", # Has its own CLI
88
+ "record_call_example.py", # Demo structure
89
+ "room_and_sip_example.py", # Demo structure
90
+ "tap_example.py", # Demo structure
91
+ "advanced_datamap_demo.py", # Demo structure
92
+ "search_server_standalone.py", # Not an agent
93
+ "test_lambda_handler.py", # Test file
94
+ }
95
+
96
+ # Examples requiring optional dependencies
97
+ EXAMPLES_REQUIRING_DEPS = {
98
+ "lambda_agent.py": "mangum",
99
+ }
100
+
101
+
102
+ class TestBasicAgentExamples:
103
+ """Test basic agent examples that should load cleanly."""
104
+
105
+ @pytest.mark.parametrize("agent_file", [
106
+ "simple_agent.py",
107
+ "simple_static_agent.py",
108
+ "simple_dynamic_agent.py",
109
+ "simple_dynamic_enhanced.py",
110
+ "declarative_agent.py",
111
+ "faq_bot_agent.py",
112
+ ])
113
+ def test_basic_agents_load(self, agent_file):
114
+ """Test basic agent examples can be loaded."""
115
+ agent_path = EXAMPLES_DIR / agent_file
116
+ if not agent_path.exists():
117
+ pytest.skip(f"Agent file not found: {agent_file}")
118
+ returncode, stdout, stderr = run_swaig_test(agent_path, "--list-tools")
119
+ assert returncode == 0, f"Failed to load {agent_file}:\nstderr: {stderr}\nstdout: {stdout}"
120
+
121
+ @pytest.mark.parametrize("agent_file", [
122
+ "simple_agent.py",
123
+ "simple_static_agent.py",
124
+ "declarative_agent.py",
125
+ ])
126
+ def test_basic_agents_generate_valid_swml(self, agent_file):
127
+ """Test basic agents generate valid SWML."""
128
+ agent_path = EXAMPLES_DIR / agent_file
129
+ if not agent_path.exists():
130
+ pytest.skip(f"Agent file not found: {agent_file}")
131
+
132
+ swml = get_swml_json(agent_path)
133
+ assert "version" in swml, "SWML missing 'version'"
134
+ assert "sections" in swml, "SWML missing 'sections'"
135
+ assert "main" in swml["sections"], "SWML missing 'sections.main'"
136
+
137
+
138
+ class TestContextsExamples:
139
+ """Test context and workflow examples."""
140
+
141
+ @pytest.mark.parametrize("agent_file", [
142
+ "contexts_demo.py",
143
+ "info_gatherer_example.py",
144
+ "dynamic_info_gatherer_example.py",
145
+ ])
146
+ def test_contexts_agents_load(self, agent_file):
147
+ """Test context-based agents can be loaded."""
148
+ agent_path = EXAMPLES_DIR / agent_file
149
+ if not agent_path.exists():
150
+ pytest.skip(f"Agent file not found: {agent_file}")
151
+ returncode, stdout, stderr = run_swaig_test(agent_path, "--list-tools")
152
+ assert returncode == 0, f"Failed to load {agent_file}:\nstderr: {stderr}\nstdout: {stdout}"
153
+
154
+ def test_survey_agent_multi_class(self):
155
+ """Test survey_agent_example.py with explicit agent class."""
156
+ agent_path = EXAMPLES_DIR / "survey_agent_example.py"
157
+ if not agent_path.exists():
158
+ pytest.skip("survey_agent_example.py not found")
159
+ # This file has multiple agent classes - test with specific one
160
+ returncode, stdout, stderr = run_swaig_test(agent_path, "--agent-class", "ProductSurveyAgent", "--list-tools")
161
+ assert returncode == 0, f"Failed to load ProductSurveyAgent:\nstderr: {stderr}\nstdout: {stdout}"
162
+
163
+
164
+ class TestDataMapExamples:
165
+ """Test DataMap examples."""
166
+
167
+ @pytest.mark.parametrize("agent_file", [
168
+ "data_map_demo.py",
169
+ ])
170
+ def test_datamap_agents_load(self, agent_file):
171
+ """Test DataMap agents can be loaded."""
172
+ agent_path = EXAMPLES_DIR / agent_file
173
+ if not agent_path.exists():
174
+ pytest.skip(f"Agent file not found: {agent_file}")
175
+ if agent_file in NON_STANDARD_EXAMPLES:
176
+ pytest.skip(f"Skipping {agent_file} - non-standard agent structure")
177
+ returncode, stdout, stderr = run_swaig_test(agent_path, "--list-tools")
178
+ assert returncode == 0, f"Failed to load {agent_file}:\nstderr: {stderr}\nstdout: {stdout}"
179
+
180
+
181
+ class TestSkillsExamples:
182
+ """Test skills-related examples."""
183
+
184
+ @pytest.mark.parametrize("agent_file", [
185
+ "skills_demo.py",
186
+ "wikipedia_demo.py",
187
+ ])
188
+ def test_skills_agents_load(self, agent_file):
189
+ """Test skills agents can be loaded."""
190
+ agent_path = EXAMPLES_DIR / agent_file
191
+ if not agent_path.exists():
192
+ pytest.skip(f"Agent file not found: {agent_file}")
193
+ if agent_file in EXAMPLES_REQUIRING_CREDENTIALS:
194
+ pytest.skip(f"Skipping {agent_file} - requires {EXAMPLES_REQUIRING_CREDENTIALS[agent_file]}")
195
+ returncode, stdout, stderr = run_swaig_test(agent_path, "--list-tools")
196
+ # Skills may require env vars, so we accept load failure with specific error
197
+ if returncode != 0:
198
+ # Check if it's a missing env var error (expected for some skills)
199
+ if "GOOGLE_SEARCH" in stderr or "API_KEY" in stderr or "env" in stderr.lower():
200
+ pytest.skip(f"Skipping {agent_file} - requires API keys")
201
+ assert returncode == 0, f"Failed to load {agent_file}:\nstderr: {stderr}\nstdout: {stdout}"
202
+
203
+
204
+ class TestWebSearchExamples:
205
+ """Test web search examples (may require API keys)."""
206
+
207
+ @pytest.mark.parametrize("agent_file", [
208
+ "web_search_multi_instance_demo.py",
209
+ ])
210
+ def test_web_search_agents_load(self, agent_file):
211
+ """Test web search agents can be loaded (may skip if no API keys)."""
212
+ agent_path = EXAMPLES_DIR / agent_file
213
+ if not agent_path.exists():
214
+ pytest.skip(f"Agent file not found: {agent_file}")
215
+ returncode, stdout, stderr = run_swaig_test(agent_path, "--list-tools")
216
+ if returncode != 0:
217
+ if "GOOGLE_SEARCH" in stderr or "API_KEY" in stderr or "GOOGLE_SEARCH" in stdout:
218
+ pytest.skip(f"Skipping {agent_file} - requires Google API keys")
219
+ assert returncode == 0, f"Failed to load {agent_file}:\nstderr: {stderr}\nstdout: {stdout}"
220
+
221
+
222
+ class TestDatasphereExamples:
223
+ """Test Datasphere examples (may require env vars)."""
224
+
225
+ @pytest.mark.parametrize("agent_file", [
226
+ "datasphere_serverless_demo.py",
227
+ "datasphere_multi_instance_demo.py",
228
+ ])
229
+ def test_datasphere_agents_load(self, agent_file):
230
+ """Test Datasphere agents can be loaded (may skip if no credentials)."""
231
+ agent_path = EXAMPLES_DIR / agent_file
232
+ if not agent_path.exists():
233
+ pytest.skip(f"Agent file not found: {agent_file}")
234
+ returncode, stdout, stderr = run_swaig_test(agent_path, "--list-tools")
235
+ if returncode != 0:
236
+ if "SIGNALWIRE" in stderr or "credentials" in stderr.lower() or "SIGNALWIRE" in stdout:
237
+ pytest.skip(f"Skipping {agent_file} - requires SignalWire credentials")
238
+ assert returncode == 0, f"Failed to load {agent_file}:\nstderr: {stderr}\nstdout: {stdout}"
239
+
240
+
241
+ class TestSWAIGFeaturesExamples:
242
+ """Test SWAIG feature examples."""
243
+
244
+ @pytest.mark.parametrize("agent_file", [
245
+ "swaig_features_agent.py",
246
+ ])
247
+ def test_swaig_features_agents_load(self, agent_file):
248
+ """Test SWAIG feature agents can be loaded."""
249
+ agent_path = EXAMPLES_DIR / agent_file
250
+ if not agent_path.exists():
251
+ pytest.skip(f"Agent file not found: {agent_file}")
252
+ if agent_file in NON_STANDARD_EXAMPLES:
253
+ pytest.skip(f"Skipping {agent_file} - non-standard agent structure")
254
+ returncode, stdout, stderr = run_swaig_test(agent_path, "--list-tools")
255
+ assert returncode == 0, f"Failed to load {agent_file}:\nstderr: {stderr}\nstdout: {stdout}"
256
+
257
+
258
+ class TestSWMLServiceExamples:
259
+ """Test SWML service examples."""
260
+
261
+ @pytest.mark.parametrize("agent_file", [
262
+ "swml_service_routing_example.py",
263
+ ])
264
+ def test_swml_service_agents_load(self, agent_file):
265
+ """Test SWML service examples can be loaded."""
266
+ agent_path = EXAMPLES_DIR / agent_file
267
+ if not agent_path.exists():
268
+ pytest.skip(f"Agent file not found: {agent_file}")
269
+ if agent_file in NON_STANDARD_EXAMPLES:
270
+ pytest.skip(f"Skipping {agent_file} - non-standard agent structure")
271
+ returncode, stdout, stderr = run_swaig_test(agent_path, "--list-tools")
272
+ assert returncode == 0, f"Failed to load {agent_file}:\nstderr: {stderr}\nstdout: {stdout}"
273
+
274
+
275
+ class TestDeploymentExamples:
276
+ """Test deployment-related examples."""
277
+
278
+ @pytest.mark.parametrize("agent_file", [
279
+ "kubernetes_ready_agent.py",
280
+ "custom_path_agent.py",
281
+ ])
282
+ def test_deployment_agents_load(self, agent_file):
283
+ """Test deployment examples can be loaded."""
284
+ agent_path = EXAMPLES_DIR / agent_file
285
+ if not agent_path.exists():
286
+ pytest.skip(f"Agent file not found: {agent_file}")
287
+ if agent_file in EXAMPLES_REQUIRING_DEPS:
288
+ pytest.skip(f"Skipping {agent_file} - requires {EXAMPLES_REQUIRING_DEPS[agent_file]}")
289
+ returncode, stdout, stderr = run_swaig_test(agent_path, "--list-tools")
290
+ assert returncode == 0, f"Failed to load {agent_file}:\nstderr: {stderr}\nstdout: {stdout}"
291
+
292
+
293
+ class TestMultiAgentExamples:
294
+ """Test multi-agent examples."""
295
+
296
+ def test_multi_agent_server_load(self):
297
+ """Test multi-agent server can be loaded."""
298
+ agent_path = EXAMPLES_DIR / "multi_agent_server.py"
299
+ if not agent_path.exists():
300
+ pytest.skip("multi_agent_server.py not found")
301
+ # Multi-agent files may need --agent flag
302
+ returncode, stdout, stderr = run_swaig_test(agent_path, "--list-agents")
303
+ # Should show agents or indicate multiple agents
304
+ assert returncode == 0 or "multiple" in stdout.lower() or "agent" in stdout.lower(), \
305
+ f"Failed to list agents:\nstderr: {stderr}\nstdout: {stdout}"
306
+
307
+ def test_multi_endpoint_agent_load(self):
308
+ """Test multi-endpoint agent can be loaded."""
309
+ agent_path = EXAMPLES_DIR / "multi_endpoint_agent.py"
310
+ if not agent_path.exists():
311
+ pytest.skip("multi_endpoint_agent.py not found")
312
+ returncode, stdout, stderr = run_swaig_test(agent_path, "--list-tools")
313
+ assert returncode == 0, f"Failed to load multi_endpoint_agent.py:\nstderr: {stderr}\nstdout: {stdout}"
314
+
315
+
316
+ class TestPrefabExamples:
317
+ """Test prefab agent examples."""
318
+
319
+ @pytest.mark.parametrize("agent_file", [
320
+ "concierge_agent_example.py",
321
+ "receptionist_agent_example.py",
322
+ ])
323
+ def test_prefab_agents_load(self, agent_file):
324
+ """Test prefab examples can be loaded."""
325
+ agent_path = EXAMPLES_DIR / agent_file
326
+ if not agent_path.exists():
327
+ pytest.skip(f"Agent file not found: {agent_file}")
328
+ returncode, stdout, stderr = run_swaig_test(agent_path, "--list-tools")
329
+ assert returncode == 0, f"Failed to load {agent_file}:\nstderr: {stderr}\nstdout: {stdout}"
330
+
331
+
332
+ class TestDynamicConfigExamples:
333
+ """Test dynamic configuration examples."""
334
+
335
+ @pytest.mark.parametrize("agent_file", [
336
+ "comprehensive_dynamic_agent.py",
337
+ ])
338
+ def test_dynamic_config_agents_load(self, agent_file):
339
+ """Test dynamic config examples can be loaded."""
340
+ agent_path = EXAMPLES_DIR / agent_file
341
+ if not agent_path.exists():
342
+ pytest.skip(f"Agent file not found: {agent_file}")
343
+ if agent_file in NON_STANDARD_EXAMPLES:
344
+ pytest.skip(f"Skipping {agent_file} - non-standard agent structure")
345
+ returncode, stdout, stderr = run_swaig_test(agent_path, "--list-tools")
346
+ assert returncode == 0, f"Failed to load {agent_file}:\nstderr: {stderr}\nstdout: {stdout}"
347
+
348
+
349
+ class TestAuthExamples:
350
+ """Test authentication examples."""
351
+
352
+ @pytest.mark.parametrize("agent_file", [
353
+ "env_auth_test.py",
354
+ ])
355
+ def test_auth_agents_load(self, agent_file):
356
+ """Test auth examples can be loaded."""
357
+ agent_path = EXAMPLES_DIR / agent_file
358
+ if not agent_path.exists():
359
+ pytest.skip(f"Agent file not found: {agent_file}")
360
+ if agent_file in EXAMPLES_REQUIRING_CREDENTIALS:
361
+ pytest.skip(f"Skipping {agent_file} - requires {EXAMPLES_REQUIRING_CREDENTIALS[agent_file]}")
362
+ returncode, stdout, stderr = run_swaig_test(agent_path, "--list-tools")
363
+ assert returncode == 0, f"Failed to load {agent_file}:\nstderr: {stderr}\nstdout: {stdout}"
364
+
365
+
366
+ class TestSearchExamples:
367
+ """Test search-related examples (may require index files)."""
368
+
369
+ @pytest.mark.parametrize("agent_file", [
370
+ "sigmond_simple.py",
371
+ "sigmond_native_search.py",
372
+ "sigmond_remote_search.py",
373
+ ])
374
+ def test_search_agents_load(self, agent_file):
375
+ """Test search agents can be loaded (may skip if no index)."""
376
+ agent_path = EXAMPLES_DIR / agent_file
377
+ if not agent_path.exists():
378
+ pytest.skip(f"Agent file not found: {agent_file}")
379
+ returncode, stdout, stderr = run_swaig_test(agent_path, "--list-tools")
380
+ if returncode != 0:
381
+ if "index" in stderr.lower() or "swsearch" in stderr.lower():
382
+ pytest.skip(f"Skipping {agent_file} - requires search index")
383
+ assert returncode == 0, f"Failed to load {agent_file}:\nstderr: {stderr}\nstdout: {stdout}"
384
+
385
+
386
+ class TestBedrockExamples:
387
+ """Test AWS Bedrock examples (require AWS credentials)."""
388
+
389
+ @pytest.mark.parametrize("agent_file", [
390
+ "bedrock_agent_run.py",
391
+ "bedrock_agent_test.py",
392
+ "bedrock_server_test.py",
393
+ ])
394
+ def test_bedrock_agents_load(self, agent_file):
395
+ """Test Bedrock agents can be loaded (skip if no AWS credentials)."""
396
+ agent_path = EXAMPLES_DIR / agent_file
397
+ if not agent_path.exists():
398
+ pytest.skip(f"Agent file not found: {agent_file}")
399
+ returncode, stdout, stderr = run_swaig_test(agent_path, "--list-tools")
400
+ if returncode != 0:
401
+ if "AWS" in stderr or "boto" in stderr or "credentials" in stderr.lower():
402
+ pytest.skip(f"Skipping {agent_file} - requires AWS credentials")
403
+ # bedrock_with_skills.py has a skill loading issue
404
+ if "Skill" in stdout and "not found" in stdout:
405
+ pytest.skip(f"Skipping {agent_file} - skill loading issue")
406
+ assert returncode == 0, f"Failed to load {agent_file}:\nstderr: {stderr}\nstdout: {stdout}"
407
+
408
+
409
+ class TestSpecialExamples:
410
+ """Test special/edge case examples."""
411
+
412
+ def test_search_server_standalone(self):
413
+ """Test search server standalone (not an agent, may fail gracefully)."""
414
+ agent_path = EXAMPLES_DIR / "search_server_standalone.py"
415
+ if not agent_path.exists():
416
+ pytest.skip("search_server_standalone.py not found")
417
+ # This is a search server, not an agent - may not work with swaig-test
418
+ returncode, stdout, stderr = run_swaig_test(agent_path, "--list-tools")
419
+ # Accept if it loads or fails with expected message
420
+ if returncode != 0:
421
+ if "not an agent" in stderr.lower() or "no agent" in stderr.lower():
422
+ pytest.skip("search_server_standalone.py is not an agent file")
423
+
424
+ def test_lambda_handler(self):
425
+ """Test lambda handler example."""
426
+ agent_path = EXAMPLES_DIR / "test_lambda_handler.py"
427
+ if not agent_path.exists():
428
+ pytest.skip("test_lambda_handler.py not found")
429
+ # This is a test file, may not export an agent directly
430
+ returncode, stdout, stderr = run_swaig_test(agent_path, "--list-tools")
431
+ # Accept load or skip if it's not a standard agent
432
+ if returncode != 0:
433
+ if "no agent" in stderr.lower() or "not found" in stderr.lower():
434
+ pytest.skip("test_lambda_handler.py doesn't export a standard agent")
435
+
436
+
437
+ class TestSWMLGeneration:
438
+ """Test that key examples generate valid SWML."""
439
+
440
+ @pytest.mark.parametrize("agent_file", [
441
+ "simple_agent.py",
442
+ "contexts_demo.py",
443
+ "swaig_features_agent.py",
444
+ "declarative_agent.py",
445
+ ])
446
+ def test_swml_has_ai_section(self, agent_file):
447
+ """Test SWML has AI configuration."""
448
+ agent_path = EXAMPLES_DIR / agent_file
449
+ if not agent_path.exists():
450
+ pytest.skip(f"Agent file not found: {agent_file}")
451
+
452
+ swml = get_swml_json(agent_path)
453
+ main_section = swml.get("sections", {}).get("main", [])
454
+
455
+ # Find AI verb in main section
456
+ ai_found = False
457
+ for verb in main_section:
458
+ if "ai" in verb:
459
+ ai_found = True
460
+ break
461
+
462
+ assert ai_found, f"SWML for {agent_file} missing 'ai' verb"
463
+
464
+
465
+ class TestToolsPresence:
466
+ """Test that specific agents have expected tools."""
467
+
468
+ def test_simple_agent_has_tools(self):
469
+ """Test simple_agent has expected tools."""
470
+ agent_path = EXAMPLES_DIR / "simple_agent.py"
471
+ if not agent_path.exists():
472
+ pytest.skip("simple_agent.py not found")
473
+
474
+ tools = list_tools(agent_path)
475
+ # simple_agent.py defines get_time and get_weather
476
+ assert "get_time" in tools, f"Missing get_time tool. Found: {tools}"
477
+ assert "get_weather" in tools, f"Missing get_weather tool. Found: {tools}"
478
+
479
+ def test_swaig_features_agent_has_tools(self):
480
+ """Test swaig_features_agent has expected tools."""
481
+ agent_path = EXAMPLES_DIR / "swaig_features_agent.py"
482
+ if not agent_path.exists():
483
+ pytest.skip("swaig_features_agent.py not found")
484
+
485
+ tools = list_tools(agent_path)
486
+ assert "get_time" in tools, f"Missing get_time tool. Found: {tools}"
487
+ assert "get_weather" in tools, f"Missing get_weather tool. Found: {tools}"
488
+
489
+
490
+ class TestServerlessSimulation:
491
+ """Test serverless environment simulation."""
492
+
493
+ @pytest.mark.parametrize("platform", ["lambda", "cloud_function"])
494
+ def test_serverless_swml_generation(self, platform):
495
+ """Test SWML generation in serverless simulation."""
496
+ agent_path = EXAMPLES_DIR / "simple_agent.py"
497
+ if not agent_path.exists():
498
+ pytest.skip("simple_agent.py not found")
499
+
500
+ returncode, stdout, stderr = run_swaig_test(
501
+ agent_path, "--simulate-serverless", platform, "--dump-swml", "--raw"
502
+ )
503
+ assert returncode == 0, f"Serverless simulation failed for {platform}:\nstderr: {stderr}"
504
+
505
+ swml = json.loads(stdout)
506
+ assert "version" in swml, f"Invalid SWML for {platform}"
507
+
508
+
509
+ if __name__ == "__main__":
510
+ pytest.main([__file__, "-v"])