signalwire-agents 0.1.37__tar.gz → 0.1.39__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 (139) hide show
  1. {signalwire_agents-0.1.37/signalwire_agents.egg-info → signalwire_agents-0.1.39}/PKG-INFO +53 -1
  2. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/README.md +47 -0
  3. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/pyproject.toml +10 -1
  4. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/__init__.py +1 -1
  5. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/cli/build_search.py +95 -19
  6. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/agent_base.py +81 -7
  7. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/mixins/ai_config_mixin.py +120 -0
  8. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/skill_manager.py +47 -0
  9. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/search/index_builder.py +105 -10
  10. signalwire_agents-0.1.39/signalwire_agents/search/pgvector_backend.py +523 -0
  11. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/search/search_engine.py +41 -4
  12. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/search/search_service.py +86 -35
  13. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/api_ninjas_trivia/skill.py +37 -1
  14. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/datasphere/skill.py +82 -0
  15. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/datasphere_serverless/skill.py +82 -0
  16. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/joke/skill.py +21 -0
  17. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/mcp_gateway/skill.py +82 -0
  18. signalwire_agents-0.1.39/signalwire_agents/skills/native_vector_search/README.md +210 -0
  19. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/native_vector_search/skill.py +197 -7
  20. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/play_background_file/skill.py +36 -0
  21. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/registry.py +36 -0
  22. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/spider/skill.py +113 -0
  23. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/swml_transfer/skill.py +90 -0
  24. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/weather_api/skill.py +28 -0
  25. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/wikipedia_search/skill.py +22 -0
  26. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39/signalwire_agents.egg-info}/PKG-INFO +53 -1
  27. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents.egg-info/SOURCES.txt +2 -0
  28. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents.egg-info/requires.txt +6 -0
  29. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/LICENSE +0 -0
  30. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/setup.cfg +0 -0
  31. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/setup.py +0 -0
  32. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/agent_server.py +0 -0
  33. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/cli/__init__.py +0 -0
  34. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/cli/config.py +0 -0
  35. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/cli/core/__init__.py +0 -0
  36. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/cli/core/agent_loader.py +0 -0
  37. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/cli/core/argparse_helpers.py +0 -0
  38. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/cli/core/dynamic_config.py +0 -0
  39. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/cli/core/service_loader.py +0 -0
  40. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/cli/execution/__init__.py +0 -0
  41. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/cli/execution/datamap_exec.py +0 -0
  42. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/cli/execution/webhook_exec.py +0 -0
  43. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/cli/output/__init__.py +0 -0
  44. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/cli/output/output_formatter.py +0 -0
  45. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/cli/output/swml_dump.py +0 -0
  46. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/cli/simulation/__init__.py +0 -0
  47. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/cli/simulation/data_generation.py +0 -0
  48. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/cli/simulation/data_overrides.py +0 -0
  49. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/cli/simulation/mock_env.py +0 -0
  50. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/cli/swaig_test_wrapper.py +0 -0
  51. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/cli/test_swaig.py +0 -0
  52. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/cli/types.py +0 -0
  53. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/__init__.py +0 -0
  54. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/agent/__init__.py +0 -0
  55. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/agent/config/__init__.py +0 -0
  56. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/agent/deployment/__init__.py +0 -0
  57. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/agent/deployment/handlers/__init__.py +0 -0
  58. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/agent/prompt/__init__.py +0 -0
  59. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/agent/prompt/manager.py +0 -0
  60. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/agent/routing/__init__.py +0 -0
  61. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/agent/security/__init__.py +0 -0
  62. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/agent/swml/__init__.py +0 -0
  63. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/agent/tools/__init__.py +0 -0
  64. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/agent/tools/decorator.py +0 -0
  65. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/agent/tools/registry.py +0 -0
  66. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/auth_handler.py +0 -0
  67. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/config_loader.py +0 -0
  68. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/contexts.py +0 -0
  69. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/data_map.py +0 -0
  70. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/function_result.py +0 -0
  71. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/logging_config.py +0 -0
  72. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/mixins/__init__.py +0 -0
  73. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/mixins/auth_mixin.py +0 -0
  74. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/mixins/prompt_mixin.py +0 -0
  75. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/mixins/serverless_mixin.py +0 -0
  76. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/mixins/skill_mixin.py +0 -0
  77. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/mixins/state_mixin.py +0 -0
  78. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/mixins/tool_mixin.py +0 -0
  79. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/mixins/web_mixin.py +0 -0
  80. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/pom_builder.py +0 -0
  81. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/security/__init__.py +0 -0
  82. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/security/session_manager.py +0 -0
  83. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/security_config.py +0 -0
  84. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/skill_base.py +0 -0
  85. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/swaig_function.py +0 -0
  86. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/swml_builder.py +0 -0
  87. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/swml_handler.py +0 -0
  88. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/swml_renderer.py +0 -0
  89. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/core/swml_service.py +0 -0
  90. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/prefabs/__init__.py +0 -0
  91. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/prefabs/concierge.py +0 -0
  92. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/prefabs/faq_bot.py +0 -0
  93. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/prefabs/info_gatherer.py +0 -0
  94. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/prefabs/receptionist.py +0 -0
  95. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/prefabs/survey.py +0 -0
  96. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/schema.json +0 -0
  97. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/search/__init__.py +0 -0
  98. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/search/document_processor.py +0 -0
  99. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/search/query_processor.py +0 -0
  100. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/README.md +0 -0
  101. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/__init__.py +0 -0
  102. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/api_ninjas_trivia/README.md +0 -0
  103. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/api_ninjas_trivia/__init__.py +0 -0
  104. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/datasphere/README.md +0 -0
  105. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/datasphere/__init__.py +0 -0
  106. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/datasphere_serverless/README.md +0 -0
  107. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/datasphere_serverless/__init__.py +0 -0
  108. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/datetime/README.md +0 -0
  109. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/datetime/__init__.py +0 -0
  110. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/datetime/skill.py +0 -0
  111. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/joke/README.md +0 -0
  112. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/joke/__init__.py +0 -0
  113. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/math/README.md +0 -0
  114. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/math/__init__.py +0 -0
  115. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/math/skill.py +0 -0
  116. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/mcp_gateway/README.md +0 -0
  117. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/mcp_gateway/__init__.py +0 -0
  118. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/native_vector_search/__init__.py +0 -0
  119. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/play_background_file/README.md +0 -0
  120. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/play_background_file/__init__.py +0 -0
  121. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/spider/README.md +0 -0
  122. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/spider/__init__.py +0 -0
  123. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/swml_transfer/README.md +0 -0
  124. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/swml_transfer/__init__.py +0 -0
  125. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/weather_api/README.md +0 -0
  126. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/weather_api/__init__.py +0 -0
  127. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/web_search/README.md +0 -0
  128. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/web_search/__init__.py +0 -0
  129. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/web_search/skill.py +0 -0
  130. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/wikipedia_search/README.md +0 -0
  131. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/skills/wikipedia_search/__init__.py +0 -0
  132. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/utils/__init__.py +0 -0
  133. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/utils/pom_utils.py +0 -0
  134. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/utils/schema_utils.py +0 -0
  135. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/utils/token_generators.py +0 -0
  136. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents/utils/validators.py +0 -0
  137. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents.egg-info/dependency_links.txt +0 -0
  138. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents.egg-info/entry_points.txt +0 -0
  139. {signalwire_agents-0.1.37 → signalwire_agents-0.1.39}/signalwire_agents.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: signalwire_agents
3
- Version: 0.1.37
3
+ Version: 0.1.39
4
4
  Summary: SignalWire AI Agents SDK
5
5
  Author-email: SignalWire Team <info@signalwire.com>
6
6
  License: MIT
@@ -70,6 +70,11 @@ Requires-Dist: striprtf>=0.0.26; extra == "search-all"
70
70
  Requires-Dist: openpyxl>=3.1.0; extra == "search-all"
71
71
  Requires-Dist: python-pptx>=0.6.21; extra == "search-all"
72
72
  Requires-Dist: python-magic>=0.4.27; extra == "search-all"
73
+ Requires-Dist: psycopg2-binary>=2.9.0; extra == "search-all"
74
+ Requires-Dist: pgvector>=0.2.0; extra == "search-all"
75
+ Provides-Extra: pgvector
76
+ Requires-Dist: psycopg2-binary>=2.9.0; extra == "pgvector"
77
+ Requires-Dist: pgvector>=0.2.0; extra == "pgvector"
73
78
  Provides-Extra: all
74
79
  Requires-Dist: sentence-transformers>=2.2.0; extra == "all"
75
80
  Requires-Dist: scikit-learn>=1.3.0; extra == "all"
@@ -643,6 +648,53 @@ if __name__ == "__main__":
643
648
  agent.serve(host="0.0.0.0", port=8000)
644
649
  ```
645
650
 
651
+ ### Customizing LLM Parameters
652
+
653
+ The SDK allows you to customize LLM parameters for both the main prompt and post-prompt, giving you fine control over the AI's behavior:
654
+
655
+ ```python
656
+ from signalwire_agents import AgentBase
657
+
658
+ class PreciseAgent(AgentBase):
659
+ def __init__(self):
660
+ super().__init__(name="precise", route="/precise")
661
+
662
+ # Configure the agent's personality
663
+ self.prompt_add_section("Role", "You are a precise technical assistant.")
664
+ self.prompt_add_section("Instructions", "Provide accurate, detailed information.")
665
+
666
+ # Set custom LLM parameters for the main prompt
667
+ self.set_prompt_llm_params(
668
+ temperature=0.3, # Low temperature for more consistent responses
669
+ top_p=0.9, # Slightly reduced for focused responses
670
+ barge_confidence=0.7, # Moderate interruption threshold
671
+ presence_penalty=0.1, # Slight penalty for repetition
672
+ frequency_penalty=0.2 # Encourage varied vocabulary
673
+ )
674
+
675
+ # Set post-prompt for summaries
676
+ self.set_post_prompt("Provide a concise summary of the key points discussed.")
677
+
678
+ # Different parameters for post-prompt (summaries should be even more focused)
679
+ self.set_post_prompt_llm_params(
680
+ temperature=0.2, # Very low for consistent summaries
681
+ top_p=0.85 # More focused token selection
682
+ )
683
+
684
+ agent = PreciseAgent()
685
+ agent.serve()
686
+ ```
687
+
688
+ #### Available LLM Parameters
689
+
690
+ - **temperature** (0.0-1.5): Controls randomness. Lower = more focused, higher = more creative
691
+ - **top_p** (0.0-1.0): Nucleus sampling. Lower = more focused on likely tokens
692
+ - **barge_confidence** (0.0-1.0): ASR confidence to interrupt. Higher = harder to interrupt
693
+ - **presence_penalty** (-2.0-2.0): Topic diversity. Positive = new topics
694
+ - **frequency_penalty** (-2.0-2.0): Repetition control. Positive = varied vocabulary
695
+
696
+ For more details on LLM parameter tuning, see [LLM Parameters Guide](docs/llm_parameters.md).
697
+
646
698
  ### Using Prefab Agents
647
699
 
648
700
  ```python
@@ -556,6 +556,53 @@ if __name__ == "__main__":
556
556
  agent.serve(host="0.0.0.0", port=8000)
557
557
  ```
558
558
 
559
+ ### Customizing LLM Parameters
560
+
561
+ The SDK allows you to customize LLM parameters for both the main prompt and post-prompt, giving you fine control over the AI's behavior:
562
+
563
+ ```python
564
+ from signalwire_agents import AgentBase
565
+
566
+ class PreciseAgent(AgentBase):
567
+ def __init__(self):
568
+ super().__init__(name="precise", route="/precise")
569
+
570
+ # Configure the agent's personality
571
+ self.prompt_add_section("Role", "You are a precise technical assistant.")
572
+ self.prompt_add_section("Instructions", "Provide accurate, detailed information.")
573
+
574
+ # Set custom LLM parameters for the main prompt
575
+ self.set_prompt_llm_params(
576
+ temperature=0.3, # Low temperature for more consistent responses
577
+ top_p=0.9, # Slightly reduced for focused responses
578
+ barge_confidence=0.7, # Moderate interruption threshold
579
+ presence_penalty=0.1, # Slight penalty for repetition
580
+ frequency_penalty=0.2 # Encourage varied vocabulary
581
+ )
582
+
583
+ # Set post-prompt for summaries
584
+ self.set_post_prompt("Provide a concise summary of the key points discussed.")
585
+
586
+ # Different parameters for post-prompt (summaries should be even more focused)
587
+ self.set_post_prompt_llm_params(
588
+ temperature=0.2, # Very low for consistent summaries
589
+ top_p=0.85 # More focused token selection
590
+ )
591
+
592
+ agent = PreciseAgent()
593
+ agent.serve()
594
+ ```
595
+
596
+ #### Available LLM Parameters
597
+
598
+ - **temperature** (0.0-1.5): Controls randomness. Lower = more focused, higher = more creative
599
+ - **top_p** (0.0-1.0): Nucleus sampling. Lower = more focused on likely tokens
600
+ - **barge_confidence** (0.0-1.0): ASR confidence to interrupt. Higher = harder to interrupt
601
+ - **presence_penalty** (-2.0-2.0): Topic diversity. Positive = new topics
602
+ - **frequency_penalty** (-2.0-2.0): Repetition control. Positive = varied vocabulary
603
+
604
+ For more details on LLM parameter tuning, see [LLM Parameters Guide](docs/llm_parameters.md).
605
+
559
606
  ### Using Prefab Agents
560
607
 
561
608
  ```python
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "signalwire_agents"
7
- version = "0.1.37"
7
+ version = "0.1.39"
8
8
  description = "SignalWire AI Agents SDK"
9
9
  authors = [
10
10
  {name = "SignalWire Team", email = "info@signalwire.com"}
@@ -95,6 +95,15 @@ search-all = [
95
95
  "openpyxl>=3.1.0",
96
96
  "python-pptx>=0.6.21",
97
97
  "python-magic>=0.4.27",
98
+ # pgvector support
99
+ "psycopg2-binary>=2.9.0",
100
+ "pgvector>=0.2.0",
101
+ ]
102
+
103
+ # PostgreSQL pgvector support
104
+ pgvector = [
105
+ "psycopg2-binary>=2.9.0",
106
+ "pgvector>=0.2.0",
98
107
  ]
99
108
 
100
109
  # All optional dependencies
@@ -18,7 +18,7 @@ A package for building AI agents using SignalWire's AI and SWML capabilities.
18
18
  from .core.logging_config import configure_logging
19
19
  configure_logging()
20
20
 
21
- __version__ = "0.1.37"
21
+ __version__ = "0.1.39"
22
22
 
23
23
  # Import core classes for easier access
24
24
  from .core.agent_base import AgentBase
@@ -89,6 +89,24 @@ Examples:
89
89
  # Search via remote API
90
90
  sw-search remote http://localhost:8001 "how to create an agent" --index-name docs
91
91
  sw-search remote localhost:8001 "API reference" --index-name docs --count 3 --verbose
92
+
93
+ # PostgreSQL pgvector backend
94
+ sw-search ./docs \\
95
+ --backend pgvector \\
96
+ --connection-string "postgresql://user:pass@localhost/knowledge" \\
97
+ --output docs_collection
98
+
99
+ # Overwrite existing pgvector collection
100
+ sw-search ./docs \\
101
+ --backend pgvector \\
102
+ --connection-string "postgresql://user:pass@localhost/knowledge" \\
103
+ --output docs_collection \\
104
+ --overwrite
105
+
106
+ # Search in pgvector collection
107
+ sw-search search docs_collection "how to create an agent" \\
108
+ --backend pgvector \\
109
+ --connection-string "postgresql://user:pass@localhost/knowledge"
92
110
  """
93
111
  )
94
112
 
@@ -100,7 +118,25 @@ Examples:
100
118
 
101
119
  parser.add_argument(
102
120
  '--output',
103
- help='Output .swsearch file (default: sources.swsearch)'
121
+ help='Output .swsearch file (default: sources.swsearch) or collection name for pgvector'
122
+ )
123
+
124
+ parser.add_argument(
125
+ '--backend',
126
+ choices=['sqlite', 'pgvector'],
127
+ default='sqlite',
128
+ help='Storage backend to use (default: sqlite)'
129
+ )
130
+
131
+ parser.add_argument(
132
+ '--connection-string',
133
+ help='PostgreSQL connection string for pgvector backend'
134
+ )
135
+
136
+ parser.add_argument(
137
+ '--overwrite',
138
+ action='store_true',
139
+ help='Overwrite existing collection (pgvector backend only)'
104
140
  )
105
141
 
106
142
  parser.add_argument(
@@ -213,18 +249,31 @@ Examples:
213
249
  print("Error: No valid sources found")
214
250
  sys.exit(1)
215
251
 
252
+ # Validate backend configuration
253
+ if args.backend == 'pgvector' and not args.connection_string:
254
+ print("Error: --connection-string is required for pgvector backend")
255
+ sys.exit(1)
256
+
216
257
  # Default output filename
217
258
  if not args.output:
218
- if len(valid_sources) == 1:
219
- # Single source - use its name
220
- source_name = valid_sources[0].stem if valid_sources[0].is_file() else valid_sources[0].name
221
- args.output = f"{source_name}.swsearch"
259
+ if args.backend == 'sqlite':
260
+ if len(valid_sources) == 1:
261
+ # Single source - use its name
262
+ source_name = valid_sources[0].stem if valid_sources[0].is_file() else valid_sources[0].name
263
+ args.output = f"{source_name}.swsearch"
264
+ else:
265
+ # Multiple sources - use generic name
266
+ args.output = "sources.swsearch"
222
267
  else:
223
- # Multiple sources - use generic name
224
- args.output = "sources.swsearch"
268
+ # For pgvector, use a default collection name
269
+ if len(valid_sources) == 1:
270
+ source_name = valid_sources[0].stem if valid_sources[0].is_file() else valid_sources[0].name
271
+ args.output = source_name
272
+ else:
273
+ args.output = "documents"
225
274
 
226
- # Ensure output has .swsearch extension
227
- if not args.output.endswith('.swsearch'):
275
+ # Ensure output has .swsearch extension for sqlite
276
+ if args.backend == 'sqlite' and not args.output.endswith('.swsearch'):
228
277
  args.output += '.swsearch'
229
278
 
230
279
  # Parse lists
@@ -235,8 +284,13 @@ Examples:
235
284
 
236
285
  if args.verbose:
237
286
  print(f"Building search index:")
287
+ print(f" Backend: {args.backend}")
238
288
  print(f" Sources: {[str(s) for s in valid_sources]}")
239
- print(f" Output: {args.output}")
289
+ if args.backend == 'sqlite':
290
+ print(f" Output file: {args.output}")
291
+ else:
292
+ print(f" Collection name: {args.output}")
293
+ print(f" Connection: {args.connection_string}")
240
294
  print(f" File types (for directories): {file_types}")
241
295
  print(f" Exclude patterns: {exclude_patterns}")
242
296
  print(f" Languages: {languages}")
@@ -278,7 +332,9 @@ Examples:
278
332
  index_nlp_backend=args.index_nlp_backend,
279
333
  verbose=args.verbose,
280
334
  semantic_threshold=args.semantic_threshold,
281
- topic_threshold=args.topic_threshold
335
+ topic_threshold=args.topic_threshold,
336
+ backend=args.backend,
337
+ connection_string=args.connection_string
282
338
  )
283
339
 
284
340
  # Build index with multiple sources
@@ -288,7 +344,8 @@ Examples:
288
344
  file_types=file_types,
289
345
  exclude_patterns=exclude_patterns,
290
346
  languages=languages,
291
- tags=tags
347
+ tags=tags,
348
+ overwrite=args.overwrite if args.backend == 'pgvector' else False
292
349
  )
293
350
 
294
351
  # Validate if requested
@@ -307,7 +364,11 @@ Examples:
307
364
  print(f"✗ Index validation failed: {validation['error']}")
308
365
  sys.exit(1)
309
366
 
310
- print(f"\n✓ Search index created successfully: {args.output}")
367
+ if args.backend == 'sqlite':
368
+ print(f"\n✓ Search index created successfully: {args.output}")
369
+ else:
370
+ print(f"\n✓ Search collection created successfully: {args.output}")
371
+ print(f" Connection: {args.connection_string}")
311
372
 
312
373
  except KeyboardInterrupt:
313
374
  print("\n\nBuild interrupted by user")
@@ -359,9 +420,12 @@ def validate_command():
359
420
 
360
421
  def search_command():
361
422
  """Search within an existing search index"""
362
- parser = argparse.ArgumentParser(description='Search within a .swsearch index file')
363
- parser.add_argument('index_file', help='Path to .swsearch file to search')
423
+ parser = argparse.ArgumentParser(description='Search within a .swsearch index file or pgvector collection')
424
+ parser.add_argument('index_source', help='Path to .swsearch file or collection name for pgvector')
364
425
  parser.add_argument('query', help='Search query')
426
+ parser.add_argument('--backend', choices=['sqlite', 'pgvector'], default='sqlite',
427
+ help='Storage backend (default: sqlite)')
428
+ parser.add_argument('--connection-string', help='PostgreSQL connection string for pgvector backend')
365
429
  parser.add_argument('--count', type=int, default=5, help='Number of results to return (default: 5)')
366
430
  parser.add_argument('--distance-threshold', type=float, default=0.0, help='Minimum similarity score (default: 0.0)')
367
431
  parser.add_argument('--tags', help='Comma-separated tags to filter by')
@@ -373,8 +437,13 @@ def search_command():
373
437
 
374
438
  args = parser.parse_args()
375
439
 
376
- if not Path(args.index_file).exists():
377
- print(f"Error: Index file does not exist: {args.index_file}")
440
+ # Validate backend configuration
441
+ if args.backend == 'pgvector' and not args.connection_string:
442
+ print("Error: --connection-string is required for pgvector backend")
443
+ sys.exit(1)
444
+
445
+ if args.backend == 'sqlite' and not Path(args.index_source).exists():
446
+ print(f"Error: Index file does not exist: {args.index_source}")
378
447
  sys.exit(1)
379
448
 
380
449
  try:
@@ -389,9 +458,16 @@ def search_command():
389
458
 
390
459
  # Load search engine
391
460
  if args.verbose:
392
- print(f"Loading search index: {args.index_file}")
461
+ if args.backend == 'sqlite':
462
+ print(f"Loading search index: {args.index_source}")
463
+ else:
464
+ print(f"Connecting to pgvector collection: {args.index_source}")
393
465
 
394
- engine = SearchEngine(args.index_file)
466
+ if args.backend == 'sqlite':
467
+ engine = SearchEngine(backend='sqlite', index_path=args.index_source)
468
+ else:
469
+ engine = SearchEngine(backend='pgvector', connection_string=args.connection_string,
470
+ collection_name=args.index_source)
395
471
 
396
472
  # Get index stats
397
473
  stats = engine.get_stats()
@@ -121,9 +121,10 @@ class AgentBase(
121
121
  agent_id: Optional[str] = None,
122
122
  native_functions: Optional[List[str]] = None,
123
123
  schema_path: Optional[str] = None,
124
- suppress_logs: bool = False,
124
+ suppress_logs: bool = False,
125
125
  enable_post_prompt_override: bool = False,
126
- check_for_input_override: bool = False
126
+ check_for_input_override: bool = False,
127
+ config_file: Optional[str] = None
127
128
  ):
128
129
  """
129
130
  Initialize a new agent
@@ -147,6 +148,7 @@ class AgentBase(
147
148
  suppress_logs: Whether to suppress structured logs
148
149
  enable_post_prompt_override: Whether to enable post-prompt override
149
150
  check_for_input_override: Whether to enable check-for-input override
151
+ config_file: Optional path to configuration file
150
152
  """
151
153
  # Import SWMLService here to avoid circular imports
152
154
  from signalwire_agents.core.swml_service import SWMLService
@@ -154,14 +156,24 @@ class AgentBase(
154
156
  # If schema_path is not provided, we'll let SWMLService find it through its _find_schema_path method
155
157
  # which will be called in its __init__
156
158
 
159
+ # Load service configuration from config file before initializing SWMLService
160
+ service_config = self._load_service_config(config_file, name)
161
+
162
+ # Apply service config values, with constructor parameters taking precedence
163
+ final_route = route if route != "/" else service_config.get('route', route)
164
+ final_host = host if host != "0.0.0.0" else service_config.get('host', host)
165
+ final_port = port if port != 3000 else service_config.get('port', port)
166
+ final_name = service_config.get('name', name)
167
+
157
168
  # Initialize the SWMLService base class
158
169
  super().__init__(
159
- name=name,
160
- route=route,
161
- host=host,
162
- port=port,
170
+ name=final_name,
171
+ route=final_route,
172
+ host=final_host,
173
+ port=final_port,
163
174
  basic_auth=basic_auth,
164
- schema_path=schema_path
175
+ schema_path=schema_path,
176
+ config_file=config_file
165
177
  )
166
178
 
167
179
  # Log the schema path if found and not suppressing logs
@@ -238,6 +250,20 @@ class AgentBase(
238
250
  self._params = {}
239
251
  self._global_data = {}
240
252
  self._function_includes = []
253
+ # Initialize with default LLM params
254
+ self._prompt_llm_params = {
255
+ 'temperature': 0.3,
256
+ 'top_p': 1.0,
257
+ 'barge_confidence': 0.0,
258
+ 'presence_penalty': 0.1,
259
+ 'frequency_penalty': 0.1
260
+ }
261
+ self._post_prompt_llm_params = {
262
+ 'temperature': 0.0,
263
+ 'top_p': 1.0,
264
+ 'presence_penalty': 0.0,
265
+ 'frequency_penalty': 0.0
266
+ }
241
267
 
242
268
  # Dynamic configuration callback
243
269
  self._dynamic_config_callback = None
@@ -248,6 +274,30 @@ class AgentBase(
248
274
  # Initialize contexts system
249
275
  self._contexts_builder = None
250
276
  self._contexts_defined = False
277
+
278
+ @staticmethod
279
+ def _load_service_config(config_file: Optional[str], service_name: str) -> dict:
280
+ """Load service configuration from config file if available"""
281
+ from signalwire_agents.core.config_loader import ConfigLoader
282
+
283
+ # Find config file
284
+ if not config_file:
285
+ config_file = ConfigLoader.find_config_file(service_name)
286
+
287
+ if not config_file:
288
+ return {}
289
+
290
+ # Load config
291
+ config_loader = ConfigLoader([config_file])
292
+ if not config_loader.has_config():
293
+ return {}
294
+
295
+ # Get service section
296
+ service_config = config_loader.get_section('service')
297
+ if service_config:
298
+ return service_config
299
+
300
+ return {}
251
301
 
252
302
  # Initialize SWAIG query params for dynamic config
253
303
  self._swaig_query_params = {}
@@ -763,6 +813,30 @@ class AgentBase(
763
813
  # Add global_data if any
764
814
  if agent_to_use._global_data:
765
815
  ai_config["global_data"] = agent_to_use._global_data
816
+
817
+ # Always add LLM parameters to prompt
818
+ if "prompt" in ai_config:
819
+ # Update existing prompt with LLM params
820
+ if isinstance(ai_config["prompt"], dict):
821
+ ai_config["prompt"].update(agent_to_use._prompt_llm_params)
822
+ elif isinstance(ai_config["prompt"], str):
823
+ # Convert string prompt to dict format
824
+ ai_config["prompt"] = {
825
+ "text": ai_config["prompt"],
826
+ **agent_to_use._prompt_llm_params
827
+ }
828
+
829
+ # Always add LLM parameters to post_prompt if post_prompt exists
830
+ if post_prompt and "post_prompt" in ai_config:
831
+ # Update existing post_prompt with LLM params
832
+ if isinstance(ai_config["post_prompt"], dict):
833
+ ai_config["post_prompt"].update(agent_to_use._post_prompt_llm_params)
834
+ elif isinstance(ai_config["post_prompt"], str):
835
+ # Convert string post_prompt to dict format
836
+ ai_config["post_prompt"] = {
837
+ "text": ai_config["post_prompt"],
838
+ **agent_to_use._post_prompt_llm_params
839
+ }
766
840
 
767
841
  except ValueError as e:
768
842
  if not agent_to_use._suppress_logs:
@@ -370,4 +370,124 @@ class AIConfigMixin:
370
370
  valid_includes.append(include)
371
371
 
372
372
  self._function_includes = valid_includes
373
+ return self
374
+
375
+ def set_prompt_llm_params(
376
+ self,
377
+ temperature: Optional[float] = None,
378
+ top_p: Optional[float] = None,
379
+ barge_confidence: Optional[float] = None,
380
+ presence_penalty: Optional[float] = None,
381
+ frequency_penalty: Optional[float] = None
382
+ ) -> 'AgentBase':
383
+ """
384
+ Set LLM parameters for the main prompt.
385
+
386
+ Args:
387
+ temperature: Randomness setting (0.0-1.5). Lower values make output more deterministic.
388
+ Default: 0.3
389
+ top_p: Alternative to temperature (0.0-1.0). Controls nucleus sampling.
390
+ Default: 1.0
391
+ barge_confidence: ASR confidence to interrupt (0.0-1.0). Higher values make it harder to interrupt.
392
+ Default: 0.0
393
+ presence_penalty: Topic diversity (-2.0 to 2.0). Positive values encourage new topics.
394
+ Default: 0.1
395
+ frequency_penalty: Repetition control (-2.0 to 2.0). Positive values reduce repetition.
396
+ Default: 0.1
397
+
398
+ Returns:
399
+ Self for method chaining
400
+
401
+ Example:
402
+ agent.set_prompt_llm_params(
403
+ temperature=0.7,
404
+ top_p=0.9,
405
+ barge_confidence=0.6
406
+ )
407
+ """
408
+ # Validate and set temperature
409
+ if temperature is not None:
410
+ if not 0.0 <= temperature <= 1.5:
411
+ raise ValueError("temperature must be between 0.0 and 1.5")
412
+ self._prompt_llm_params['temperature'] = temperature
413
+
414
+ # Validate and set top_p
415
+ if top_p is not None:
416
+ if not 0.0 <= top_p <= 1.0:
417
+ raise ValueError("top_p must be between 0.0 and 1.0")
418
+ self._prompt_llm_params['top_p'] = top_p
419
+
420
+ # Validate and set barge_confidence
421
+ if barge_confidence is not None:
422
+ if not 0.0 <= barge_confidence <= 1.0:
423
+ raise ValueError("barge_confidence must be between 0.0 and 1.0")
424
+ self._prompt_llm_params['barge_confidence'] = barge_confidence
425
+
426
+ # Validate and set presence_penalty
427
+ if presence_penalty is not None:
428
+ if not -2.0 <= presence_penalty <= 2.0:
429
+ raise ValueError("presence_penalty must be between -2.0 and 2.0")
430
+ self._prompt_llm_params['presence_penalty'] = presence_penalty
431
+
432
+ # Validate and set frequency_penalty
433
+ if frequency_penalty is not None:
434
+ if not -2.0 <= frequency_penalty <= 2.0:
435
+ raise ValueError("frequency_penalty must be between -2.0 and 2.0")
436
+ self._prompt_llm_params['frequency_penalty'] = frequency_penalty
437
+
438
+ return self
439
+
440
+ def set_post_prompt_llm_params(
441
+ self,
442
+ temperature: Optional[float] = None,
443
+ top_p: Optional[float] = None,
444
+ presence_penalty: Optional[float] = None,
445
+ frequency_penalty: Optional[float] = None
446
+ ) -> 'AgentBase':
447
+ """
448
+ Set LLM parameters for the post-prompt.
449
+
450
+ Args:
451
+ temperature: Randomness setting (0.0-1.5). Lower values make output more deterministic.
452
+ Default: 0.0
453
+ top_p: Alternative to temperature (0.0-1.0). Controls nucleus sampling.
454
+ Default: 1.0
455
+ presence_penalty: Topic diversity (-2.0 to 2.0). Positive values encourage new topics.
456
+ Default: 0.0
457
+ frequency_penalty: Repetition control (-2.0 to 2.0). Positive values reduce repetition.
458
+ Default: 0.0
459
+
460
+ Returns:
461
+ Self for method chaining
462
+
463
+ Example:
464
+ agent.set_post_prompt_llm_params(
465
+ temperature=0.5, # More deterministic for post-prompt
466
+ top_p=0.9
467
+ )
468
+ """
469
+ # Validate and set temperature
470
+ if temperature is not None:
471
+ if not 0.0 <= temperature <= 1.5:
472
+ raise ValueError("temperature must be between 0.0 and 1.5")
473
+ self._post_prompt_llm_params['temperature'] = temperature
474
+
475
+ # Validate and set top_p
476
+ if top_p is not None:
477
+ if not 0.0 <= top_p <= 1.0:
478
+ raise ValueError("top_p must be between 0.0 and 1.0")
479
+ self._post_prompt_llm_params['top_p'] = top_p
480
+
481
+ # Validate and set presence_penalty
482
+ if presence_penalty is not None:
483
+ if not -2.0 <= presence_penalty <= 2.0:
484
+ raise ValueError("presence_penalty must be between -2.0 and 2.0")
485
+ self._post_prompt_llm_params['presence_penalty'] = presence_penalty
486
+
487
+ # Validate and set frequency_penalty
488
+ if frequency_penalty is not None:
489
+ if not -2.0 <= frequency_penalty <= 2.0:
490
+ raise ValueError("frequency_penalty must be between -2.0 and 2.0")
491
+ self._post_prompt_llm_params['frequency_penalty'] = frequency_penalty
492
+
373
493
  return self
@@ -45,6 +45,53 @@ class SkillManager:
45
45
  self.logger.error(error_msg)
46
46
  return False, error_msg
47
47
 
48
+ # Validate that the skill has a proper parameter schema
49
+ if not hasattr(skill_class, 'get_parameter_schema') or not callable(getattr(skill_class, 'get_parameter_schema')):
50
+ error_msg = f"Skill '{skill_name}' must have get_parameter_schema() classmethod"
51
+ self.logger.error(error_msg)
52
+ return False, error_msg
53
+
54
+ try:
55
+ # Validate the parameter schema
56
+ schema = skill_class.get_parameter_schema()
57
+ if not isinstance(schema, dict):
58
+ error_msg = f"Skill '{skill_name}'.get_parameter_schema() must return a dictionary"
59
+ self.logger.error(error_msg)
60
+ return False, error_msg
61
+
62
+ # Ensure it's not an empty schema
63
+ if not schema:
64
+ error_msg = f"Skill '{skill_name}'.get_parameter_schema() returned empty dictionary"
65
+ self.logger.error(error_msg)
66
+ return False, error_msg
67
+
68
+ # Check if the skill has overridden the method
69
+ from signalwire_agents.core.skill_base import SkillBase
70
+ skill_method = getattr(skill_class, 'get_parameter_schema', None)
71
+ base_method = getattr(SkillBase, 'get_parameter_schema', None)
72
+
73
+ if skill_method and base_method:
74
+ # For class methods, check the underlying function
75
+ skill_func = skill_method.__func__ if hasattr(skill_method, '__func__') else skill_method
76
+ base_func = base_method.__func__ if hasattr(base_method, '__func__') else base_method
77
+
78
+ if skill_func is base_func:
79
+ # Get base schema to check if skill added any parameters
80
+ base_schema = SkillBase.get_parameter_schema()
81
+ if set(schema.keys()) == set(base_schema.keys()):
82
+ error_msg = f"Skill '{skill_name}' must override get_parameter_schema() to define its specific parameters"
83
+ self.logger.error(error_msg)
84
+ return False, error_msg
85
+
86
+ except AttributeError as e:
87
+ error_msg = f"Skill '{skill_name}' must properly implement get_parameter_schema() classmethod"
88
+ self.logger.error(error_msg)
89
+ return False, error_msg
90
+ except Exception as e:
91
+ error_msg = f"Skill '{skill_name}'.get_parameter_schema() failed: {e}"
92
+ self.logger.error(error_msg)
93
+ return False, error_msg
94
+
48
95
  try:
49
96
  # Create skill instance with parameters to get the instance key
50
97
  skill_instance = skill_class(self.agent, params)