signalwire-agents 1.0.2__tar.gz → 1.0.4__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 (144) hide show
  1. {signalwire_agents-1.0.2/signalwire_agents.egg-info → signalwire_agents-1.0.4}/PKG-INFO +1 -1
  2. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/pyproject.toml +1 -1
  3. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/__init__.py +1 -1
  4. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/agent_base.py +221 -14
  5. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/schema.json +165 -4
  6. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4/signalwire_agents.egg-info}/PKG-INFO +1 -1
  7. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/LICENSE +0 -0
  8. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/README.md +0 -0
  9. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/setup.cfg +0 -0
  10. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/setup.py +0 -0
  11. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/agent_server.py +0 -0
  12. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/agents/bedrock.py +0 -0
  13. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/cli/__init__.py +0 -0
  14. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/cli/build_search.py +0 -0
  15. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/cli/config.py +0 -0
  16. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/cli/core/__init__.py +0 -0
  17. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/cli/core/agent_loader.py +0 -0
  18. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/cli/core/argparse_helpers.py +0 -0
  19. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/cli/core/dynamic_config.py +0 -0
  20. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/cli/core/service_loader.py +0 -0
  21. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/cli/execution/__init__.py +0 -0
  22. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/cli/execution/datamap_exec.py +0 -0
  23. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/cli/execution/webhook_exec.py +0 -0
  24. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/cli/output/__init__.py +0 -0
  25. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/cli/output/output_formatter.py +0 -0
  26. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/cli/output/swml_dump.py +0 -0
  27. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/cli/simulation/__init__.py +0 -0
  28. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/cli/simulation/data_generation.py +0 -0
  29. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/cli/simulation/data_overrides.py +0 -0
  30. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/cli/simulation/mock_env.py +0 -0
  31. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/cli/swaig_test_wrapper.py +0 -0
  32. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/cli/test_swaig.py +0 -0
  33. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/cli/types.py +0 -0
  34. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/__init__.py +0 -0
  35. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/agent/__init__.py +0 -0
  36. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/agent/config/__init__.py +0 -0
  37. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/agent/deployment/__init__.py +0 -0
  38. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/agent/deployment/handlers/__init__.py +0 -0
  39. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/agent/prompt/__init__.py +0 -0
  40. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/agent/prompt/manager.py +0 -0
  41. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/agent/routing/__init__.py +0 -0
  42. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/agent/security/__init__.py +0 -0
  43. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/agent/swml/__init__.py +0 -0
  44. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/agent/tools/__init__.py +0 -0
  45. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/agent/tools/decorator.py +0 -0
  46. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/agent/tools/registry.py +0 -0
  47. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/auth_handler.py +0 -0
  48. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/config_loader.py +0 -0
  49. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/contexts.py +0 -0
  50. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/data_map.py +0 -0
  51. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/function_result.py +0 -0
  52. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/logging_config.py +0 -0
  53. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/mixins/__init__.py +0 -0
  54. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/mixins/ai_config_mixin.py +0 -0
  55. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/mixins/auth_mixin.py +0 -0
  56. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/mixins/prompt_mixin.py +0 -0
  57. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/mixins/serverless_mixin.py +0 -0
  58. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/mixins/skill_mixin.py +0 -0
  59. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/mixins/state_mixin.py +0 -0
  60. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/mixins/tool_mixin.py +0 -0
  61. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/mixins/web_mixin.py +0 -0
  62. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/pom_builder.py +0 -0
  63. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/security/__init__.py +0 -0
  64. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/security/session_manager.py +0 -0
  65. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/security_config.py +0 -0
  66. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/skill_base.py +0 -0
  67. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/skill_manager.py +0 -0
  68. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/swaig_function.py +0 -0
  69. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/swml_builder.py +0 -0
  70. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/swml_handler.py +0 -0
  71. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/swml_renderer.py +0 -0
  72. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/core/swml_service.py +0 -0
  73. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/prefabs/__init__.py +0 -0
  74. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/prefabs/concierge.py +0 -0
  75. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/prefabs/faq_bot.py +0 -0
  76. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/prefabs/info_gatherer.py +0 -0
  77. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/prefabs/receptionist.py +0 -0
  78. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/prefabs/survey.py +0 -0
  79. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/search/__init__.py +0 -0
  80. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/search/document_processor.py +0 -0
  81. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/search/index_builder.py +0 -0
  82. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/search/migration.py +0 -0
  83. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/search/models.py +0 -0
  84. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/search/pgvector_backend.py +0 -0
  85. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/search/query_processor.py +0 -0
  86. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/search/search_engine.py +0 -0
  87. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/search/search_service.py +0 -0
  88. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/README.md +0 -0
  89. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/__init__.py +0 -0
  90. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/api_ninjas_trivia/README.md +0 -0
  91. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/api_ninjas_trivia/__init__.py +0 -0
  92. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/api_ninjas_trivia/skill.py +0 -0
  93. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/datasphere/README.md +0 -0
  94. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/datasphere/__init__.py +0 -0
  95. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/datasphere/skill.py +0 -0
  96. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/datasphere_serverless/README.md +0 -0
  97. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/datasphere_serverless/__init__.py +0 -0
  98. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/datasphere_serverless/skill.py +0 -0
  99. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/datetime/README.md +0 -0
  100. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/datetime/__init__.py +0 -0
  101. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/datetime/skill.py +0 -0
  102. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/joke/README.md +0 -0
  103. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/joke/__init__.py +0 -0
  104. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/joke/skill.py +0 -0
  105. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/math/README.md +0 -0
  106. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/math/__init__.py +0 -0
  107. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/math/skill.py +0 -0
  108. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/mcp_gateway/README.md +0 -0
  109. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/mcp_gateway/__init__.py +0 -0
  110. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/mcp_gateway/skill.py +0 -0
  111. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/native_vector_search/README.md +0 -0
  112. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/native_vector_search/__init__.py +0 -0
  113. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/native_vector_search/skill.py +0 -0
  114. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/play_background_file/README.md +0 -0
  115. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/play_background_file/__init__.py +0 -0
  116. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/play_background_file/skill.py +0 -0
  117. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/registry.py +0 -0
  118. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/spider/README.md +0 -0
  119. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/spider/__init__.py +0 -0
  120. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/spider/skill.py +0 -0
  121. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/swml_transfer/README.md +0 -0
  122. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/swml_transfer/__init__.py +0 -0
  123. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/swml_transfer/skill.py +0 -0
  124. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/weather_api/README.md +0 -0
  125. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/weather_api/__init__.py +0 -0
  126. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/weather_api/skill.py +0 -0
  127. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/web_search/README.md +0 -0
  128. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/web_search/__init__.py +0 -0
  129. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/web_search/skill.py +0 -0
  130. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/wikipedia_search/README.md +0 -0
  131. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/wikipedia_search/__init__.py +0 -0
  132. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/skills/wikipedia_search/skill.py +0 -0
  133. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/utils/__init__.py +0 -0
  134. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/utils/pom_utils.py +0 -0
  135. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/utils/schema_utils.py +0 -0
  136. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/utils/token_generators.py +0 -0
  137. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/utils/validators.py +0 -0
  138. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/web/__init__.py +0 -0
  139. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents/web/web_service.py +0 -0
  140. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents.egg-info/SOURCES.txt +0 -0
  141. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents.egg-info/dependency_links.txt +0 -0
  142. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents.egg-info/entry_points.txt +0 -0
  143. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/signalwire_agents.egg-info/requires.txt +0 -0
  144. {signalwire_agents-1.0.2 → signalwire_agents-1.0.4}/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.2
3
+ Version: 1.0.4
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.2"
7
+ version = "1.0.4"
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.2"
21
+ __version__ = "1.0.4"
22
22
 
23
23
  # Import core classes for easier access
24
24
  from .core.agent_base import AgentBase
@@ -266,7 +266,21 @@ class AgentBase(
266
266
 
267
267
  # Initialize SWAIG query params for dynamic config
268
268
  self._swaig_query_params = {}
269
-
269
+
270
+ # Initialize verb insertion points for call flow customization
271
+ self._pre_answer_verbs = [] # Verbs to run before answer (e.g., ringback, screening)
272
+ self._answer_config = {} # Configuration for the answer verb
273
+ self._post_answer_verbs = [] # Verbs to run after answer, before AI (e.g., announcements)
274
+ self._post_ai_verbs = [] # Verbs to run after AI ends (e.g., cleanup, transfers)
275
+
276
+ # Verb categories for pre-answer validation
277
+ _PRE_ANSWER_SAFE_VERBS = {
278
+ "transfer", "execute", "return", "label", "goto", "request",
279
+ "switch", "cond", "if", "eval", "set", "unset", "hangup",
280
+ "send_sms", "sleep", "stop_record_call", "stop_denoise", "stop_tap"
281
+ }
282
+ _AUTO_ANSWER_VERBS = {"play", "connect"}
283
+
270
284
  @staticmethod
271
285
  def _load_service_config(config_file: Optional[str], service_name: str) -> dict:
272
286
  """Load service configuration from config file if available"""
@@ -380,14 +394,166 @@ class AgentBase(
380
394
  def on_summary(self, summary: Optional[Dict[str, Any]], raw_data: Optional[Dict[str, Any]] = None) -> None:
381
395
  """
382
396
  Called when a post-prompt summary is received
383
-
397
+
384
398
  Args:
385
399
  summary: The summary object or None if no summary was found
386
400
  raw_data: The complete raw POST data from the request
387
401
  """
388
402
  # Default implementation does nothing
389
403
  pass
390
-
404
+
405
+ # ==================== Call Flow Verb Insertion Methods ====================
406
+
407
+ def add_pre_answer_verb(self, verb_name: str, config: Dict[str, Any]) -> 'AgentBase':
408
+ """
409
+ Add a verb to run before the call is answered.
410
+
411
+ Pre-answer verbs execute while the call is still ringing. Only certain
412
+ verbs are safe to use before answering:
413
+
414
+ Safe verbs: transfer, execute, return, label, goto, request, switch,
415
+ cond, if, eval, set, unset, hangup, send_sms, sleep,
416
+ stop_record_call, stop_denoise, stop_tap
417
+
418
+ Verbs with auto_answer option (play, connect): Must include
419
+ "auto_answer": False in config to prevent automatic answering.
420
+
421
+ Args:
422
+ verb_name: The SWML verb name (e.g., "play", "sleep", "request")
423
+ config: Verb configuration dictionary
424
+
425
+ Returns:
426
+ Self for method chaining
427
+
428
+ Raises:
429
+ ValueError: If verb is not safe for pre-answer use
430
+
431
+ Example:
432
+ # Play ringback tone before answering
433
+ agent.add_pre_answer_verb("play", {
434
+ "urls": ["ring:us"],
435
+ "auto_answer": False
436
+ })
437
+ """
438
+ # Validate verb is safe for pre-answer use
439
+ if verb_name in self._AUTO_ANSWER_VERBS:
440
+ if not config.get("auto_answer") is False:
441
+ self.log.warning(
442
+ "pre_answer_verb_will_answer",
443
+ verb=verb_name,
444
+ hint=f"Add 'auto_answer': False to prevent {verb_name} from answering the call"
445
+ )
446
+ elif verb_name not in self._PRE_ANSWER_SAFE_VERBS:
447
+ raise ValueError(
448
+ f"Verb '{verb_name}' is not safe for pre-answer use. "
449
+ f"Safe verbs: {', '.join(sorted(self._PRE_ANSWER_SAFE_VERBS))}"
450
+ )
451
+
452
+ self._pre_answer_verbs.append((verb_name, config))
453
+ return self
454
+
455
+ def add_answer_verb(self, config: Optional[Dict[str, Any]] = None) -> 'AgentBase':
456
+ """
457
+ Configure the answer verb.
458
+
459
+ The answer verb connects the call. Use this method to customize
460
+ answer behavior, such as setting max_duration.
461
+
462
+ Args:
463
+ config: Optional answer verb configuration (e.g., {"max_duration": 3600})
464
+
465
+ Returns:
466
+ Self for method chaining
467
+
468
+ Example:
469
+ # Set maximum call duration to 1 hour
470
+ agent.add_answer_verb({"max_duration": 3600})
471
+ """
472
+ self._answer_config = config or {}
473
+ return self
474
+
475
+ def add_post_answer_verb(self, verb_name: str, config: Dict[str, Any]) -> 'AgentBase':
476
+ """
477
+ Add a verb to run after the call is answered but before the AI starts.
478
+
479
+ Post-answer verbs run after the call is connected. Common uses include
480
+ welcome messages, legal disclaimers, and hold music.
481
+
482
+ Args:
483
+ verb_name: The SWML verb name (e.g., "play", "sleep")
484
+ config: Verb configuration dictionary
485
+
486
+ Returns:
487
+ Self for method chaining
488
+
489
+ Example:
490
+ # Play welcome message
491
+ agent.add_post_answer_verb("play", {
492
+ "url": "say:Welcome to Acme Corporation."
493
+ })
494
+ # Brief pause
495
+ agent.add_post_answer_verb("sleep", {"time": 500})
496
+ """
497
+ self._post_answer_verbs.append((verb_name, config))
498
+ return self
499
+
500
+ def add_post_ai_verb(self, verb_name: str, config: Dict[str, Any]) -> 'AgentBase':
501
+ """
502
+ Add a verb to run after the AI conversation ends.
503
+
504
+ Post-AI verbs run when the AI completes its conversation. Common uses
505
+ include clean disconnects, transfers, and logging.
506
+
507
+ Args:
508
+ verb_name: The SWML verb name (e.g., "hangup", "transfer", "request")
509
+ config: Verb configuration dictionary
510
+
511
+ Returns:
512
+ Self for method chaining
513
+
514
+ Example:
515
+ # Log call completion and hang up
516
+ agent.add_post_ai_verb("request", {
517
+ "url": "https://api.example.com/call-complete",
518
+ "method": "POST"
519
+ })
520
+ agent.add_post_ai_verb("hangup", {})
521
+ """
522
+ self._post_ai_verbs.append((verb_name, config))
523
+ return self
524
+
525
+ def clear_pre_answer_verbs(self) -> 'AgentBase':
526
+ """
527
+ Remove all pre-answer verbs.
528
+
529
+ Returns:
530
+ Self for method chaining
531
+ """
532
+ self._pre_answer_verbs = []
533
+ return self
534
+
535
+ def clear_post_answer_verbs(self) -> 'AgentBase':
536
+ """
537
+ Remove all post-answer verbs.
538
+
539
+ Returns:
540
+ Self for method chaining
541
+ """
542
+ self._post_answer_verbs = []
543
+ return self
544
+
545
+ def clear_post_ai_verbs(self) -> 'AgentBase':
546
+ """
547
+ Remove all post-AI verbs.
548
+
549
+ Returns:
550
+ Self for method chaining
551
+ """
552
+ self._post_ai_verbs = []
553
+ return self
554
+
555
+ # ==================== End Call Flow Verb Insertion Methods ====================
556
+
391
557
  def enable_sip_routing(self, auto_map: bool = True, path: str = "/sip") -> 'AgentBase':
392
558
  """
393
559
  Enable SIP-based routing for this agent
@@ -746,15 +912,29 @@ class AgentBase(
746
912
  if hasattr(agent_to_use, '_post_prompt_url_override') and agent_to_use._post_prompt_url_override:
747
913
  post_prompt_url = agent_to_use._post_prompt_url_override
748
914
 
749
- # Add answer verb with auto-answer enabled
750
- agent_to_use.add_verb("answer", {})
751
-
752
- # Add recording if enabled
915
+ # ========== PHASE 1: PRE-ANSWER VERBS ==========
916
+ # These run while the call is still ringing
917
+ for verb_name, verb_config in agent_to_use._pre_answer_verbs:
918
+ agent_to_use.add_verb(verb_name, verb_config)
919
+
920
+ # ========== PHASE 2: ANSWER VERB ==========
921
+ # Only add answer verb if auto_answer is enabled
922
+ if agent_to_use._auto_answer:
923
+ agent_to_use.add_verb("answer", agent_to_use._answer_config)
924
+
925
+ # ========== PHASE 3: POST-ANSWER VERBS ==========
926
+ # These run after answer but before AI
927
+
928
+ # Add recording if enabled (this is a post-answer verb)
753
929
  if agent_to_use._record_call:
754
930
  agent_to_use.add_verb("record_call", {
755
931
  "format": agent_to_use._record_format,
756
932
  "stereo": agent_to_use._record_stereo
757
933
  })
934
+
935
+ # Add user-defined post-answer verbs
936
+ for verb_name, verb_config in agent_to_use._post_answer_verbs:
937
+ agent_to_use.add_verb(verb_name, verb_config)
758
938
 
759
939
  # Use the AI verb handler to build and validate the AI verb config
760
940
  ai_config = {}
@@ -881,10 +1061,16 @@ class AgentBase(
881
1061
 
882
1062
  if agent_to_use._global_data and "global_data" not in ai_config:
883
1063
  ai_config["global_data"] = agent_to_use._global_data
884
-
1064
+
1065
+ # ========== PHASE 4: AI VERB ==========
885
1066
  # Add the AI verb to the document
886
1067
  agent_to_use.add_verb("ai", ai_config)
887
-
1068
+
1069
+ # ========== PHASE 5: POST-AI VERBS ==========
1070
+ # These run after the AI conversation ends
1071
+ for verb_name, verb_config in agent_to_use._post_ai_verbs:
1072
+ agent_to_use.add_verb(verb_name, verb_config)
1073
+
888
1074
  # Apply any modifications from the callback to agent state
889
1075
  if modifications and isinstance(modifications, dict):
890
1076
  # Handle global_data modifications by updating the AI config directly
@@ -900,16 +1086,31 @@ class AgentBase(
900
1086
 
901
1087
  # Clear and rebuild the document with the modified AI config
902
1088
  agent_to_use.reset_document()
903
- agent_to_use.add_verb("answer", {})
904
-
905
- # Add recording if enabled
1089
+
1090
+ # Rebuild with 5-phase approach
1091
+ # PHASE 1: Pre-answer verbs
1092
+ for verb_name, verb_config in agent_to_use._pre_answer_verbs:
1093
+ agent_to_use.add_verb(verb_name, verb_config)
1094
+
1095
+ # PHASE 2: Answer verb (if auto_answer enabled)
1096
+ if agent_to_use._auto_answer:
1097
+ agent_to_use.add_verb("answer", agent_to_use._answer_config)
1098
+
1099
+ # PHASE 3: Post-answer verbs
906
1100
  if agent_to_use._record_call:
907
1101
  agent_to_use.add_verb("record_call", {
908
1102
  "format": agent_to_use._record_format,
909
1103
  "stereo": agent_to_use._record_stereo
910
1104
  })
911
-
1105
+ for verb_name, verb_config in agent_to_use._post_answer_verbs:
1106
+ agent_to_use.add_verb(verb_name, verb_config)
1107
+
1108
+ # PHASE 4: AI verb
912
1109
  agent_to_use.add_verb("ai", ai_config)
1110
+
1111
+ # PHASE 5: Post-AI verbs
1112
+ for verb_name, verb_config in agent_to_use._post_ai_verbs:
1113
+ agent_to_use.add_verb(verb_name, verb_config)
913
1114
 
914
1115
  # Return the rendered document as a string
915
1116
  return agent_to_use.render_document()
@@ -1015,7 +1216,13 @@ class AgentBase(
1015
1216
  ephemeral_agent._pronounce = copy.deepcopy(self._pronounce)
1016
1217
  ephemeral_agent._global_data = copy.deepcopy(self._global_data)
1017
1218
  ephemeral_agent._function_includes = copy.deepcopy(self._function_includes)
1018
-
1219
+
1220
+ # Deep copy verb insertion points for call flow customization
1221
+ ephemeral_agent._pre_answer_verbs = copy.deepcopy(self._pre_answer_verbs)
1222
+ ephemeral_agent._answer_config = copy.deepcopy(self._answer_config)
1223
+ ephemeral_agent._post_answer_verbs = copy.deepcopy(self._post_answer_verbs)
1224
+ ephemeral_agent._post_ai_verbs = copy.deepcopy(self._post_ai_verbs)
1225
+
1019
1226
  # Deep copy LLM parameters
1020
1227
  ephemeral_agent._prompt_llm_params = copy.deepcopy(self._prompt_llm_params)
1021
1228
  ephemeral_agent._post_prompt_llm_params = copy.deepcopy(self._post_prompt_llm_params)
@@ -60,6 +60,9 @@
60
60
  {
61
61
  "$ref": "#/$defs/Denoise"
62
62
  },
63
+ {
64
+ "$ref": "#/$defs/EnterQueue"
65
+ },
63
66
  {
64
67
  "$ref": "#/$defs/Execute"
65
68
  },
@@ -303,6 +306,23 @@
303
306
  },
304
307
  "title": "Denoise object"
305
308
  },
309
+ "EnterQueue": {
310
+ "type": "object",
311
+ "properties": {
312
+ "enter_queue": {
313
+ "$ref": "#/$defs/EnterQueueObject",
314
+ "description": "Place the call in a queue.",
315
+ "title": "enter_queue"
316
+ }
317
+ },
318
+ "required": [
319
+ "enter_queue"
320
+ ],
321
+ "unevaluatedProperties": {
322
+ "not": {}
323
+ },
324
+ "title": "EnterQueue object"
325
+ },
306
326
  "Execute": {
307
327
  "type": "object",
308
328
  "properties": {
@@ -311,7 +331,7 @@
311
331
  "properties": {
312
332
  "dest": {
313
333
  "type": "string",
314
- "description": "Specifies what to execute. The value can be one of: \n<section_name> - section in the current document to execute.\nhttps://<URL>\" - URL pointing to the document to execute. Sends HTTP POST."
334
+ "description": "Specifies what to execute. The value can be one of:\n- `<section_name>` - section in the current document to execute\n- A URL (http or https) that returns a SWML document - Sends HTTP POST\n- An inline SWML document (as a JSON string)"
315
335
  },
316
336
  "params": {
317
337
  "type": "object",
@@ -1561,7 +1581,7 @@
1561
1581
  "properties": {
1562
1582
  "dest": {
1563
1583
  "type": "string",
1564
- "description": "Specifies where to transfer the call. The value can be one of:\n - `<section_name>` - section in the SWML document to jump to.\n - `https://<URL>` - URL pointing to the document to transfer to. Sends HTTP POST."
1584
+ "description": "Specifies where to transfer the call. The value can be one of:\n- `<section_name>` - section in the SWML document to jump to\n- A URL (http or https) that returns a SWML document - Sends HTTP POST\n- An inline SWML document (as a JSON string)"
1565
1585
  },
1566
1586
  "params": {
1567
1587
  "type": "object",
@@ -1642,7 +1662,7 @@
1642
1662
  "payment_connector_url": {
1643
1663
  "type": "string",
1644
1664
  "format": "uri",
1645
- "description": "The URL to make POST requests with all the gathered payment details.\nThis URL is used to process the final payment transaction and return the results through the response.\n\nVisit https://developer.signalwire.com//swml/methods/pay/payment_connector_url for more important information."
1665
+ "description": "The URL to make POST requests with all the gathered payment details.\nThis URL is used to process the final payment transaction and return the results through the response.\n\nVisit https://developer.signalwire.com/swml/methods/pay/payment_connector_url for more important information."
1646
1666
  },
1647
1667
  "charge_amount": {
1648
1668
  "type": "string",
@@ -2223,6 +2243,17 @@
2223
2243
  "format": "uri",
2224
2244
  "description": "http or https URL to deliver call status events"
2225
2245
  },
2246
+ "transfer_after_bridge": {
2247
+ "anyOf": [
2248
+ {
2249
+ "type": "string"
2250
+ },
2251
+ {
2252
+ "$ref": "#/$defs/SWMLVar"
2253
+ }
2254
+ ],
2255
+ "description": "SWML to execute after the bridge completes. This defines what should happen after the call is connected and the bridge ends.\nCan be either:\n- A URL (http or https) that returns a SWML document\n- An inline SWML document (as a JSON string)\n\n**Note:** This parameter is REQUIRED when connecting to a queue (when `to` starts with \"queue:\")"
2256
+ },
2226
2257
  "call_state_events": {
2227
2258
  "type": "array",
2228
2259
  "items": {
@@ -2235,7 +2266,7 @@
2235
2266
  },
2236
2267
  "to": {
2237
2268
  "type": "string",
2238
- "description": "The SIP URI or phone number to dial."
2269
+ "description": "Destination to dial. Can be:\n- Phone number in E.164 format (e.g., \"+15552345678\")\n- SIP URI (e.g., \"sip:alice@example.com\")\n- Call Fabric Resource address (e.g., \"/public/test_room\")\n- Queue (e.g., \"queue:support\")"
2239
2270
  }
2240
2271
  },
2241
2272
  "required": [
@@ -2398,6 +2429,17 @@
2398
2429
  "format": "uri",
2399
2430
  "description": "http or https URL to deliver call status events"
2400
2431
  },
2432
+ "transfer_after_bridge": {
2433
+ "anyOf": [
2434
+ {
2435
+ "type": "string"
2436
+ },
2437
+ {
2438
+ "$ref": "#/$defs/SWMLVar"
2439
+ }
2440
+ ],
2441
+ "description": "SWML to execute after the bridge completes. This defines what should happen after the call is connected and the bridge ends.\nCan be either:\n- A URL (http or https) that returns a SWML document\n- An inline SWML document (as a JSON string)\n\n**Note:** This parameter is REQUIRED when connecting to a queue (when `to` starts with \"queue:\")"
2442
+ },
2401
2443
  "call_state_events": {
2402
2444
  "type": "array",
2403
2445
  "items": {
@@ -2575,6 +2617,17 @@
2575
2617
  "format": "uri",
2576
2618
  "description": "http or https URL to deliver call status events"
2577
2619
  },
2620
+ "transfer_after_bridge": {
2621
+ "anyOf": [
2622
+ {
2623
+ "type": "string"
2624
+ },
2625
+ {
2626
+ "$ref": "#/$defs/SWMLVar"
2627
+ }
2628
+ ],
2629
+ "description": "SWML to execute after the bridge completes. This defines what should happen after the call is connected and the bridge ends.\nCan be either:\n- A URL (http or https) that returns a SWML document\n- An inline SWML document (as a JSON string)\n\n**Note:** This parameter is REQUIRED when connecting to a queue (when `to` starts with \"queue:\")"
2630
+ },
2578
2631
  "call_state_events": {
2579
2632
  "type": "array",
2580
2633
  "items": {
@@ -2753,6 +2806,17 @@
2753
2806
  "format": "uri",
2754
2807
  "description": "http or https URL to deliver call status events"
2755
2808
  },
2809
+ "transfer_after_bridge": {
2810
+ "anyOf": [
2811
+ {
2812
+ "type": "string"
2813
+ },
2814
+ {
2815
+ "$ref": "#/$defs/SWMLVar"
2816
+ }
2817
+ ],
2818
+ "description": "SWML to execute after the bridge completes. This defines what should happen after the call is connected and the bridge ends.\nCan be either:\n- A URL (http or https) that returns a SWML document\n- An inline SWML document (as a JSON string)\n\n**Note:** This parameter is REQUIRED when connecting to a queue (when `to` starts with \"queue:\")"
2819
+ },
2756
2820
  "call_state_events": {
2757
2821
  "type": "array",
2758
2822
  "items": {
@@ -2782,6 +2846,63 @@
2782
2846
  },
2783
2847
  "title": "ConnectDeviceSerialParallel object"
2784
2848
  },
2849
+ "EnterQueueObject": {
2850
+ "type": "object",
2851
+ "properties": {
2852
+ "queue_name": {
2853
+ "type": "string",
2854
+ "description": "Name of the queue to enter."
2855
+ },
2856
+ "transfer_after_bridge": {
2857
+ "anyOf": [
2858
+ {
2859
+ "type": "string"
2860
+ },
2861
+ {
2862
+ "$ref": "#/$defs/SWMLVar"
2863
+ }
2864
+ ],
2865
+ "description": "SWML to execute after the bridge completes. This defines what should happen after the call is connected to an agent and the bridge ends.\nCan be either:\n- A URL (http or https) that returns a SWML document\n- An inline SWML document (as a JSON string)"
2866
+ },
2867
+ "status_url": {
2868
+ "type": "string",
2869
+ "format": "uri",
2870
+ "description": "HTTP or HTTPS URL to deliver queue status events. Default not set"
2871
+ },
2872
+ "wait_url": {
2873
+ "anyOf": [
2874
+ {
2875
+ "type": "string",
2876
+ "format": "uri"
2877
+ },
2878
+ {
2879
+ "$ref": "#/$defs/SWMLVar"
2880
+ }
2881
+ ],
2882
+ "description": "URL for media to play while waiting in the queue. Default hold music will be played if not set"
2883
+ },
2884
+ "wait_time": {
2885
+ "anyOf": [
2886
+ {
2887
+ "type": "integer"
2888
+ },
2889
+ {
2890
+ "$ref": "#/$defs/SWMLVar"
2891
+ }
2892
+ ],
2893
+ "minimum": 1,
2894
+ "description": "Maximum time in seconds to wait in the queue before timeout. Default `3600`"
2895
+ }
2896
+ },
2897
+ "required": [
2898
+ "queue_name",
2899
+ "transfer_after_bridge"
2900
+ ],
2901
+ "unevaluatedProperties": {
2902
+ "not": {}
2903
+ },
2904
+ "title": "EnterQueueObject object"
2905
+ },
2785
2906
  "ExecuteSwitch": {
2786
2907
  "type": "object",
2787
2908
  "properties": {
@@ -8665,6 +8786,12 @@
8665
8786
  {
8666
8787
  "$ref": "#/$defs/SWMLAction"
8667
8788
  },
8789
+ {
8790
+ "$ref": "#/$defs/ChangeContextAction"
8791
+ },
8792
+ {
8793
+ "$ref": "#/$defs/ChangeStepAction"
8794
+ },
8668
8795
  {
8669
8796
  "$ref": "#/$defs/ContextSwitchAction"
8670
8797
  },
@@ -8860,6 +8987,40 @@
8860
8987
  },
8861
8988
  "title": "SWMLAction object"
8862
8989
  },
8990
+ "ChangeContextAction": {
8991
+ "type": "object",
8992
+ "properties": {
8993
+ "change_context": {
8994
+ "type": "string",
8995
+ "description": "The name of the context to switch to. The context must be defined in the AI's prompt.contexts configuration.",
8996
+ "title": "change_context"
8997
+ }
8998
+ },
8999
+ "required": [
9000
+ "change_context"
9001
+ ],
9002
+ "unevaluatedProperties": {
9003
+ "not": {}
9004
+ },
9005
+ "title": "ChangeContextAction object"
9006
+ },
9007
+ "ChangeStepAction": {
9008
+ "type": "object",
9009
+ "properties": {
9010
+ "change_step": {
9011
+ "type": "string",
9012
+ "description": "The name of the step to switch to. The step must be defined in the current context's steps array.",
9013
+ "title": "change_step"
9014
+ }
9015
+ },
9016
+ "required": [
9017
+ "change_step"
9018
+ ],
9019
+ "unevaluatedProperties": {
9020
+ "not": {}
9021
+ },
9022
+ "title": "ChangeStepAction object"
9023
+ },
8863
9024
  "ContextSwitchAction": {
8864
9025
  "type": "object",
8865
9026
  "properties": {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: signalwire_agents
3
- Version: 1.0.2
3
+ Version: 1.0.4
4
4
  Summary: SignalWire AI Agents SDK
5
5
  Author-email: SignalWire Team <info@signalwire.com>
6
6
  License: MIT