signalwire-agents 0.1.20__py3-none-any.whl → 0.1.23__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- signalwire_agents/__init__.py +1 -1
- signalwire_agents/agent_server.py +50 -11
- signalwire_agents/core/__init__.py +2 -2
- signalwire_agents/core/agent/__init__.py +14 -0
- signalwire_agents/core/agent/config/__init__.py +14 -0
- signalwire_agents/core/agent/config/ephemeral.py +176 -0
- signalwire_agents/core/agent/deployment/__init__.py +0 -0
- signalwire_agents/core/agent/deployment/handlers/__init__.py +0 -0
- signalwire_agents/core/agent/prompt/__init__.py +14 -0
- signalwire_agents/core/agent/prompt/manager.py +288 -0
- signalwire_agents/core/agent/routing/__init__.py +0 -0
- signalwire_agents/core/agent/security/__init__.py +0 -0
- signalwire_agents/core/agent/swml/__init__.py +0 -0
- signalwire_agents/core/agent/tools/__init__.py +15 -0
- signalwire_agents/core/agent/tools/decorator.py +95 -0
- signalwire_agents/core/agent/tools/registry.py +192 -0
- signalwire_agents/core/agent_base.py +131 -413
- signalwire_agents/core/data_map.py +3 -15
- signalwire_agents/core/skill_manager.py +0 -17
- signalwire_agents/core/swaig_function.py +0 -2
- signalwire_agents/core/swml_builder.py +207 -11
- signalwire_agents/core/swml_renderer.py +123 -312
- signalwire_agents/core/swml_service.py +25 -94
- signalwire_agents/search/index_builder.py +1 -1
- signalwire_agents/skills/api_ninjas_trivia/__init__.py +3 -0
- signalwire_agents/skills/api_ninjas_trivia/skill.py +192 -0
- signalwire_agents/skills/play_background_file/__init__.py +3 -0
- signalwire_agents/skills/play_background_file/skill.py +197 -0
- signalwire_agents/skills/weather_api/__init__.py +3 -0
- signalwire_agents/skills/weather_api/skill.py +154 -0
- {signalwire_agents-0.1.20.dist-info → signalwire_agents-0.1.23.dist-info}/METADATA +5 -8
- {signalwire_agents-0.1.20.dist-info → signalwire_agents-0.1.23.dist-info}/RECORD +37 -18
- {signalwire_agents-0.1.20.data → signalwire_agents-0.1.23.data}/data/schema.json +0 -0
- {signalwire_agents-0.1.20.dist-info → signalwire_agents-0.1.23.dist-info}/WHEEL +0 -0
- {signalwire_agents-0.1.20.dist-info → signalwire_agents-0.1.23.dist-info}/entry_points.txt +0 -0
- {signalwire_agents-0.1.20.dist-info → signalwire_agents-0.1.23.dist-info}/licenses/LICENSE +0 -0
- {signalwire_agents-0.1.20.dist-info → signalwire_agents-0.1.23.dist-info}/top_level.txt +0 -0
signalwire_agents/__init__.py
CHANGED
@@ -18,7 +18,7 @@ A package for building AI agents using SignalWire's AI and SWML capabilities.
|
|
18
18
|
from .core.logging_config import configure_logging
|
19
19
|
configure_logging()
|
20
20
|
|
21
|
-
__version__ = "0.1.
|
21
|
+
__version__ = "0.1.23"
|
22
22
|
|
23
23
|
# Import core classes for easier access
|
24
24
|
from .core.agent_base import AgentBase
|
@@ -11,6 +11,7 @@ See LICENSE file in the project root for full license information.
|
|
11
11
|
AgentServer - Class for hosting multiple SignalWire AI Agents in a single server
|
12
12
|
"""
|
13
13
|
|
14
|
+
import os
|
14
15
|
import re
|
15
16
|
from typing import Dict, Any, Optional, List, Tuple, Callable
|
16
17
|
|
@@ -575,24 +576,62 @@ class AgentServer:
|
|
575
576
|
# No matching agent found
|
576
577
|
return {"error": "Not Found"}
|
577
578
|
|
578
|
-
#
|
579
|
+
# Set host and port
|
579
580
|
host = host or self.host
|
580
581
|
port = port or self.port
|
581
582
|
|
582
|
-
|
583
|
+
# Check for SSL configuration from environment variables
|
584
|
+
ssl_enabled_env = os.environ.get('SWML_SSL_ENABLED', '').lower()
|
585
|
+
ssl_enabled = ssl_enabled_env in ('true', '1', 'yes')
|
586
|
+
ssl_cert_path = os.environ.get('SWML_SSL_CERT_PATH')
|
587
|
+
ssl_key_path = os.environ.get('SWML_SSL_KEY_PATH')
|
588
|
+
domain = os.environ.get('SWML_DOMAIN')
|
589
|
+
|
590
|
+
# Validate SSL configuration if enabled
|
591
|
+
if ssl_enabled:
|
592
|
+
if not ssl_cert_path or not os.path.exists(ssl_cert_path):
|
593
|
+
self.logger.warning(f"SSL cert not found: {ssl_cert_path}")
|
594
|
+
ssl_enabled = False
|
595
|
+
elif not ssl_key_path or not os.path.exists(ssl_key_path):
|
596
|
+
self.logger.warning(f"SSL key not found: {ssl_key_path}")
|
597
|
+
ssl_enabled = False
|
598
|
+
|
599
|
+
# Update server info display with correct protocol
|
600
|
+
protocol = "https" if ssl_enabled else "http"
|
601
|
+
|
602
|
+
# Determine display host - include port unless it's the standard port for the protocol
|
603
|
+
if ssl_enabled and domain:
|
604
|
+
# Use domain, but include port if it's not the standard HTTPS port (443)
|
605
|
+
display_host = f"{domain}:{port}" if port != 443 else domain
|
606
|
+
else:
|
607
|
+
# Use host:port for HTTP or when no domain is specified
|
608
|
+
display_host = f"{host}:{port}"
|
609
|
+
|
610
|
+
self.logger.info(f"Starting server on {protocol}://{display_host}")
|
583
611
|
for route, agent in self.agents.items():
|
584
612
|
username, password = agent.get_basic_auth_credentials()
|
585
613
|
self.logger.info(f"Agent '{agent.get_name()}' available at:")
|
586
|
-
self.logger.info(f"URL:
|
614
|
+
self.logger.info(f"URL: {protocol}://{display_host}{route}")
|
587
615
|
self.logger.info(f"Basic Auth: {username}:{password}")
|
588
|
-
|
589
|
-
# Start the server
|
590
|
-
|
591
|
-
self.
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
616
|
+
|
617
|
+
# Start the server with or without SSL
|
618
|
+
if ssl_enabled and ssl_cert_path and ssl_key_path:
|
619
|
+
self.logger.info(f"Starting with SSL - cert: {ssl_cert_path}, key: {ssl_key_path}")
|
620
|
+
uvicorn.run(
|
621
|
+
self.app,
|
622
|
+
host=host,
|
623
|
+
port=port,
|
624
|
+
log_level=self.log_level,
|
625
|
+
ssl_certfile=ssl_cert_path,
|
626
|
+
ssl_keyfile=ssl_key_path
|
627
|
+
)
|
628
|
+
else:
|
629
|
+
uvicorn.run(
|
630
|
+
self.app,
|
631
|
+
host=host,
|
632
|
+
port=port,
|
633
|
+
log_level=self.log_level
|
634
|
+
)
|
596
635
|
|
597
636
|
def register_global_routing_callback(self, callback_fn: Callable[[Request, Dict[str, Any]], Optional[str]],
|
598
637
|
path: str) -> None:
|
@@ -13,7 +13,7 @@ Core components for SignalWire AI Agents
|
|
13
13
|
|
14
14
|
from signalwire_agents.core.agent_base import AgentBase
|
15
15
|
from signalwire_agents.core.function_result import SwaigFunctionResult
|
16
|
-
from signalwire_agents.core.swaig_function import
|
16
|
+
from signalwire_agents.core.swaig_function import SWAIGFunction
|
17
17
|
from signalwire_agents.core.swml_service import SWMLService
|
18
18
|
from signalwire_agents.core.swml_handler import SWMLVerbHandler, VerbHandlerRegistry
|
19
19
|
from signalwire_agents.core.swml_builder import SWMLBuilder
|
@@ -21,7 +21,7 @@ from signalwire_agents.core.swml_builder import SWMLBuilder
|
|
21
21
|
__all__ = [
|
22
22
|
'AgentBase',
|
23
23
|
'SwaigFunctionResult',
|
24
|
-
'
|
24
|
+
'SWAIGFunction',
|
25
25
|
'SWMLService',
|
26
26
|
'SWMLVerbHandler',
|
27
27
|
'VerbHandlerRegistry',
|
@@ -0,0 +1,14 @@
|
|
1
|
+
"""
|
2
|
+
Copyright (c) 2025 SignalWire
|
3
|
+
|
4
|
+
This file is part of the SignalWire AI Agents SDK.
|
5
|
+
|
6
|
+
Licensed under the MIT License.
|
7
|
+
See LICENSE file in the project root for full license information.
|
8
|
+
"""
|
9
|
+
|
10
|
+
"""Agent refactored components."""
|
11
|
+
|
12
|
+
from .config.ephemeral import EphemeralAgentConfig
|
13
|
+
|
14
|
+
__all__ = ['EphemeralAgentConfig']
|
@@ -0,0 +1,14 @@
|
|
1
|
+
"""
|
2
|
+
Copyright (c) 2025 SignalWire
|
3
|
+
|
4
|
+
This file is part of the SignalWire AI Agents SDK.
|
5
|
+
|
6
|
+
Licensed under the MIT License.
|
7
|
+
See LICENSE file in the project root for full license information.
|
8
|
+
"""
|
9
|
+
|
10
|
+
"""Configuration management modules."""
|
11
|
+
|
12
|
+
from .ephemeral import EphemeralAgentConfig
|
13
|
+
|
14
|
+
__all__ = ['EphemeralAgentConfig']
|
@@ -0,0 +1,176 @@
|
|
1
|
+
"""
|
2
|
+
Copyright (c) 2025 SignalWire
|
3
|
+
|
4
|
+
This file is part of the SignalWire AI Agents SDK.
|
5
|
+
|
6
|
+
Licensed under the MIT License.
|
7
|
+
See LICENSE file in the project root for full license information.
|
8
|
+
"""
|
9
|
+
|
10
|
+
"""Ephemeral agent configuration for dynamic per-request settings."""
|
11
|
+
|
12
|
+
from typing import Dict, Any, Optional, List
|
13
|
+
|
14
|
+
|
15
|
+
class EphemeralAgentConfig:
|
16
|
+
"""
|
17
|
+
An ephemeral configurator object that mimics AgentBase's configuration interface.
|
18
|
+
|
19
|
+
This allows dynamic configuration callbacks to use the same familiar methods
|
20
|
+
they would use during agent initialization, but for per-request configuration.
|
21
|
+
"""
|
22
|
+
|
23
|
+
def __init__(self):
|
24
|
+
# Initialize all configuration containers
|
25
|
+
self._hints = []
|
26
|
+
self._languages = []
|
27
|
+
self._pronounce = []
|
28
|
+
self._params = {}
|
29
|
+
self._global_data = {}
|
30
|
+
self._prompt_sections = []
|
31
|
+
self._raw_prompt = None
|
32
|
+
self._post_prompt = None
|
33
|
+
self._function_includes = []
|
34
|
+
self._native_functions = []
|
35
|
+
|
36
|
+
# Mirror all the AgentBase configuration methods
|
37
|
+
|
38
|
+
def add_hint(self, hint: str) -> 'EphemeralAgentConfig':
|
39
|
+
"""Add a simple string hint"""
|
40
|
+
if isinstance(hint, str) and hint:
|
41
|
+
self._hints.append(hint)
|
42
|
+
return self
|
43
|
+
|
44
|
+
def add_hints(self, hints: List[str]) -> 'EphemeralAgentConfig':
|
45
|
+
"""Add multiple string hints"""
|
46
|
+
if hints and isinstance(hints, list):
|
47
|
+
for hint in hints:
|
48
|
+
if isinstance(hint, str) and hint:
|
49
|
+
self._hints.append(hint)
|
50
|
+
return self
|
51
|
+
|
52
|
+
def add_language(self, name: str, code: str, voice: str, **kwargs) -> 'EphemeralAgentConfig':
|
53
|
+
"""Add a language configuration"""
|
54
|
+
language = {
|
55
|
+
"name": name,
|
56
|
+
"code": code,
|
57
|
+
"voice": voice
|
58
|
+
}
|
59
|
+
|
60
|
+
# Handle additional parameters
|
61
|
+
for key, value in kwargs.items():
|
62
|
+
if key in ["engine", "model", "speech_fillers", "function_fillers", "fillers"]:
|
63
|
+
language[key] = value
|
64
|
+
|
65
|
+
self._languages.append(language)
|
66
|
+
return self
|
67
|
+
|
68
|
+
def add_pronunciation(self, replace: str, with_text: str, ignore_case: bool = False) -> 'EphemeralAgentConfig':
|
69
|
+
"""Add a pronunciation rule"""
|
70
|
+
if replace and with_text:
|
71
|
+
rule = {"replace": replace, "with": with_text}
|
72
|
+
if ignore_case:
|
73
|
+
rule["ignore_case"] = True
|
74
|
+
self._pronounce.append(rule)
|
75
|
+
return self
|
76
|
+
|
77
|
+
def set_param(self, key: str, value: Any) -> 'EphemeralAgentConfig':
|
78
|
+
"""Set a single AI parameter"""
|
79
|
+
if key:
|
80
|
+
self._params[key] = value
|
81
|
+
return self
|
82
|
+
|
83
|
+
def set_params(self, params: Dict[str, Any]) -> 'EphemeralAgentConfig':
|
84
|
+
"""Set multiple AI parameters"""
|
85
|
+
if params and isinstance(params, dict):
|
86
|
+
self._params.update(params)
|
87
|
+
return self
|
88
|
+
|
89
|
+
def set_global_data(self, data: Dict[str, Any]) -> 'EphemeralAgentConfig':
|
90
|
+
"""Set global data"""
|
91
|
+
if data and isinstance(data, dict):
|
92
|
+
self._global_data = data
|
93
|
+
return self
|
94
|
+
|
95
|
+
def update_global_data(self, data: Dict[str, Any]) -> 'EphemeralAgentConfig':
|
96
|
+
"""Update global data"""
|
97
|
+
if data and isinstance(data, dict):
|
98
|
+
self._global_data.update(data)
|
99
|
+
return self
|
100
|
+
|
101
|
+
def set_prompt_text(self, text: str) -> 'EphemeralAgentConfig':
|
102
|
+
"""Set raw prompt text"""
|
103
|
+
self._raw_prompt = text
|
104
|
+
return self
|
105
|
+
|
106
|
+
def set_post_prompt(self, text: str) -> 'EphemeralAgentConfig':
|
107
|
+
"""Set post-prompt text"""
|
108
|
+
self._post_prompt = text
|
109
|
+
return self
|
110
|
+
|
111
|
+
def prompt_add_section(self, title: str, body: str = "", bullets: Optional[List[str]] = None, **kwargs) -> 'EphemeralAgentConfig':
|
112
|
+
"""Add a prompt section"""
|
113
|
+
section = {
|
114
|
+
"title": title,
|
115
|
+
"body": body
|
116
|
+
}
|
117
|
+
if bullets:
|
118
|
+
section["bullets"] = bullets
|
119
|
+
|
120
|
+
# Handle additional parameters
|
121
|
+
for key, value in kwargs.items():
|
122
|
+
if key in ["numbered", "numbered_bullets", "subsections"]:
|
123
|
+
section[key] = value
|
124
|
+
|
125
|
+
self._prompt_sections.append(section)
|
126
|
+
return self
|
127
|
+
|
128
|
+
def set_native_functions(self, function_names: List[str]) -> 'EphemeralAgentConfig':
|
129
|
+
"""Set native functions"""
|
130
|
+
if function_names and isinstance(function_names, list):
|
131
|
+
self._native_functions = [name for name in function_names if isinstance(name, str)]
|
132
|
+
return self
|
133
|
+
|
134
|
+
def add_function_include(self, url: str, functions: List[str], meta_data: Optional[Dict[str, Any]] = None) -> 'EphemeralAgentConfig':
|
135
|
+
"""Add a function include"""
|
136
|
+
if url and functions and isinstance(functions, list):
|
137
|
+
include = {"url": url, "functions": functions}
|
138
|
+
if meta_data and isinstance(meta_data, dict):
|
139
|
+
include["meta_data"] = meta_data
|
140
|
+
self._function_includes.append(include)
|
141
|
+
return self
|
142
|
+
|
143
|
+
def extract_config(self) -> Dict[str, Any]:
|
144
|
+
"""
|
145
|
+
Extract the configuration as a dictionary for applying to the real agent.
|
146
|
+
|
147
|
+
Returns:
|
148
|
+
Dictionary containing all the configuration changes
|
149
|
+
"""
|
150
|
+
config = {}
|
151
|
+
|
152
|
+
if self._hints:
|
153
|
+
config["hints"] = self._hints
|
154
|
+
if self._languages:
|
155
|
+
config["languages"] = self._languages
|
156
|
+
if self._pronounce:
|
157
|
+
config["pronounce"] = self._pronounce
|
158
|
+
if self._params:
|
159
|
+
config["params"] = self._params
|
160
|
+
if self._global_data:
|
161
|
+
config["global_data"] = self._global_data
|
162
|
+
if self._function_includes:
|
163
|
+
config["function_includes"] = self._function_includes
|
164
|
+
if self._native_functions:
|
165
|
+
config["native_functions"] = self._native_functions
|
166
|
+
|
167
|
+
# Handle prompt sections - these should be applied to the agent's POM, not as raw config
|
168
|
+
# The calling code should use these to build the prompt properly
|
169
|
+
if self._prompt_sections:
|
170
|
+
config["_ephemeral_prompt_sections"] = self._prompt_sections
|
171
|
+
if self._raw_prompt:
|
172
|
+
config["_ephemeral_raw_prompt"] = self._raw_prompt
|
173
|
+
if self._post_prompt:
|
174
|
+
config["_ephemeral_post_prompt"] = self._post_prompt
|
175
|
+
|
176
|
+
return config
|
File without changes
|
File without changes
|
@@ -0,0 +1,14 @@
|
|
1
|
+
"""
|
2
|
+
Copyright (c) 2025 SignalWire
|
3
|
+
|
4
|
+
This file is part of the SignalWire AI Agents SDK.
|
5
|
+
|
6
|
+
Licensed under the MIT License.
|
7
|
+
See LICENSE file in the project root for full license information.
|
8
|
+
"""
|
9
|
+
|
10
|
+
"""Prompt management modules."""
|
11
|
+
|
12
|
+
from .manager import PromptManager
|
13
|
+
|
14
|
+
__all__ = ['PromptManager']
|
@@ -0,0 +1,288 @@
|
|
1
|
+
"""
|
2
|
+
Copyright (c) 2025 SignalWire
|
3
|
+
|
4
|
+
This file is part of the SignalWire AI Agents SDK.
|
5
|
+
|
6
|
+
Licensed under the MIT License.
|
7
|
+
See LICENSE file in the project root for full license information.
|
8
|
+
"""
|
9
|
+
|
10
|
+
"""Prompt management functionality for AgentBase."""
|
11
|
+
|
12
|
+
from typing import Dict, Any, Optional, List, Union
|
13
|
+
import inspect
|
14
|
+
import logging
|
15
|
+
|
16
|
+
logger = logging.getLogger(__name__)
|
17
|
+
|
18
|
+
|
19
|
+
class PromptManager:
|
20
|
+
"""Manages prompt building and configuration."""
|
21
|
+
|
22
|
+
def __init__(self, agent):
|
23
|
+
"""
|
24
|
+
Initialize PromptManager with reference to parent agent.
|
25
|
+
|
26
|
+
Args:
|
27
|
+
agent: Parent AgentBase instance
|
28
|
+
"""
|
29
|
+
self.agent = agent
|
30
|
+
self._prompt_text = None
|
31
|
+
self._post_prompt_text = None
|
32
|
+
self._contexts = None
|
33
|
+
|
34
|
+
def _validate_prompt_mode_exclusivity(self):
|
35
|
+
"""
|
36
|
+
Check that only one prompt mode is in use.
|
37
|
+
|
38
|
+
Raises:
|
39
|
+
ValueError: If both prompt modes are in use
|
40
|
+
"""
|
41
|
+
if self._prompt_text and self.agent._use_pom and self.agent.pom:
|
42
|
+
pom_sections = self.agent.pom.to_dict() if hasattr(self.agent.pom, 'to_dict') else []
|
43
|
+
if pom_sections:
|
44
|
+
raise ValueError(
|
45
|
+
"Cannot use both prompt_text and POM sections. "
|
46
|
+
"Please use either set_prompt_text() OR the prompt_add_* methods, not both."
|
47
|
+
)
|
48
|
+
|
49
|
+
def _process_prompt_sections(self) -> Optional[Union[str, List[Dict[str, Any]]]]:
|
50
|
+
"""
|
51
|
+
Process prompt sections from POM or raw prompt text.
|
52
|
+
|
53
|
+
Returns:
|
54
|
+
String, List of section dictionaries, or None
|
55
|
+
"""
|
56
|
+
# First check if we have contexts - they take precedence
|
57
|
+
if self._contexts:
|
58
|
+
return None # Contexts handle their own prompt sections
|
59
|
+
|
60
|
+
# Check if we have a raw prompt text - return it directly
|
61
|
+
if self._prompt_text:
|
62
|
+
return self._prompt_text
|
63
|
+
|
64
|
+
# Otherwise use POM sections if available
|
65
|
+
if self.agent._use_pom and self.agent.pom:
|
66
|
+
sections = self.agent.pom.to_dict()
|
67
|
+
if sections:
|
68
|
+
return sections
|
69
|
+
|
70
|
+
return None
|
71
|
+
|
72
|
+
def define_contexts(self, contexts: Union[Dict[str, Any], Any]) -> None:
|
73
|
+
"""
|
74
|
+
Define contexts for the agent.
|
75
|
+
|
76
|
+
Args:
|
77
|
+
contexts: Context configuration (dict or ContextBuilder)
|
78
|
+
"""
|
79
|
+
if hasattr(contexts, 'to_dict'):
|
80
|
+
# It's a ContextBuilder
|
81
|
+
self._contexts = contexts.to_dict()
|
82
|
+
elif isinstance(contexts, dict):
|
83
|
+
# It's already a dictionary
|
84
|
+
self._contexts = contexts
|
85
|
+
else:
|
86
|
+
raise ValueError("contexts must be a dictionary or a ContextBuilder object")
|
87
|
+
|
88
|
+
logger.debug(f"Defined contexts: {self._contexts}")
|
89
|
+
|
90
|
+
def set_prompt_text(self, text: str) -> None:
|
91
|
+
"""
|
92
|
+
Set the agent's prompt as raw text.
|
93
|
+
|
94
|
+
Args:
|
95
|
+
text: Prompt text
|
96
|
+
"""
|
97
|
+
self._validate_prompt_mode_exclusivity()
|
98
|
+
self._prompt_text = text
|
99
|
+
logger.debug(f"Set prompt text: {text[:100]}...")
|
100
|
+
|
101
|
+
def set_post_prompt(self, text: str) -> None:
|
102
|
+
"""
|
103
|
+
Set the post-prompt text.
|
104
|
+
|
105
|
+
Args:
|
106
|
+
text: Post-prompt text
|
107
|
+
"""
|
108
|
+
self._post_prompt_text = text
|
109
|
+
logger.debug(f"Set post-prompt text: {text[:100]}...")
|
110
|
+
|
111
|
+
def set_prompt_pom(self, pom: List[Dict[str, Any]]) -> None:
|
112
|
+
"""
|
113
|
+
Set the prompt as a POM dictionary.
|
114
|
+
|
115
|
+
Args:
|
116
|
+
pom: POM dictionary structure
|
117
|
+
|
118
|
+
Raises:
|
119
|
+
ValueError: If use_pom is False
|
120
|
+
"""
|
121
|
+
if self.agent._use_pom:
|
122
|
+
self.agent.pom = pom
|
123
|
+
else:
|
124
|
+
raise ValueError("use_pom must be True to use set_prompt_pom")
|
125
|
+
|
126
|
+
def prompt_add_section(
|
127
|
+
self,
|
128
|
+
title: str,
|
129
|
+
body: str = "",
|
130
|
+
bullets: Optional[List[str]] = None,
|
131
|
+
numbered: bool = False,
|
132
|
+
numbered_bullets: bool = False,
|
133
|
+
subsections: Optional[List[Dict[str, Any]]] = None
|
134
|
+
) -> None:
|
135
|
+
"""
|
136
|
+
Add a section to the prompt.
|
137
|
+
|
138
|
+
Args:
|
139
|
+
title: Section title
|
140
|
+
body: Optional section body text
|
141
|
+
bullets: Optional list of bullet points
|
142
|
+
numbered: Whether this section should be numbered
|
143
|
+
numbered_bullets: Whether bullets should be numbered
|
144
|
+
subsections: Optional list of subsection objects
|
145
|
+
"""
|
146
|
+
self._validate_prompt_mode_exclusivity()
|
147
|
+
if self.agent._use_pom and self.agent.pom:
|
148
|
+
# Create parameters for add_section based on what's supported
|
149
|
+
kwargs = {}
|
150
|
+
|
151
|
+
# Start with basic parameters
|
152
|
+
kwargs['title'] = title
|
153
|
+
kwargs['body'] = body
|
154
|
+
if bullets:
|
155
|
+
kwargs['bullets'] = bullets
|
156
|
+
|
157
|
+
# Add optional parameters if they look supported
|
158
|
+
if hasattr(self.agent.pom, 'add_section'):
|
159
|
+
sig = inspect.signature(self.agent.pom.add_section)
|
160
|
+
if 'numbered' in sig.parameters:
|
161
|
+
kwargs['numbered'] = numbered
|
162
|
+
if 'numberedBullets' in sig.parameters:
|
163
|
+
kwargs['numberedBullets'] = numbered_bullets
|
164
|
+
|
165
|
+
# Create the section
|
166
|
+
section = self.agent.pom.add_section(**kwargs)
|
167
|
+
|
168
|
+
# Now add subsections if provided, by calling add_subsection on the section
|
169
|
+
if subsections:
|
170
|
+
for subsection in subsections:
|
171
|
+
if 'title' in subsection:
|
172
|
+
section.add_subsection(
|
173
|
+
title=subsection.get('title'),
|
174
|
+
body=subsection.get('body', ''),
|
175
|
+
bullets=subsection.get('bullets', [])
|
176
|
+
)
|
177
|
+
|
178
|
+
def prompt_add_to_section(
|
179
|
+
self,
|
180
|
+
title: str,
|
181
|
+
body: Optional[str] = None,
|
182
|
+
bullet: Optional[str] = None,
|
183
|
+
bullets: Optional[List[str]] = None
|
184
|
+
) -> None:
|
185
|
+
"""
|
186
|
+
Add content to an existing section (creating it if needed).
|
187
|
+
|
188
|
+
Args:
|
189
|
+
title: Section title
|
190
|
+
body: Optional text to append to section body
|
191
|
+
bullet: Optional single bullet point to add
|
192
|
+
bullets: Optional list of bullet points to add
|
193
|
+
"""
|
194
|
+
if self.agent._use_pom and self.agent.pom:
|
195
|
+
self.agent.pom.add_to_section(
|
196
|
+
title=title,
|
197
|
+
body=body,
|
198
|
+
bullet=bullet,
|
199
|
+
bullets=bullets
|
200
|
+
)
|
201
|
+
|
202
|
+
def prompt_add_subsection(
|
203
|
+
self,
|
204
|
+
parent_title: str,
|
205
|
+
title: str,
|
206
|
+
body: str = "",
|
207
|
+
bullets: Optional[List[str]] = None
|
208
|
+
) -> None:
|
209
|
+
"""
|
210
|
+
Add a subsection to an existing section (creating parent if needed).
|
211
|
+
|
212
|
+
Args:
|
213
|
+
parent_title: Parent section title
|
214
|
+
title: Subsection title
|
215
|
+
body: Optional subsection body text
|
216
|
+
bullets: Optional list of bullet points
|
217
|
+
"""
|
218
|
+
if self.agent._use_pom and self.agent.pom:
|
219
|
+
# First find or create the parent section
|
220
|
+
parent_section = None
|
221
|
+
|
222
|
+
# Try to find the parent section by title
|
223
|
+
if hasattr(self.agent.pom, 'sections'):
|
224
|
+
for section in self.agent.pom.sections:
|
225
|
+
if hasattr(section, 'title') and section.title == parent_title:
|
226
|
+
parent_section = section
|
227
|
+
break
|
228
|
+
|
229
|
+
# If parent section not found, create it
|
230
|
+
if not parent_section:
|
231
|
+
parent_section = self.agent.pom.add_section(title=parent_title)
|
232
|
+
|
233
|
+
# Now call add_subsection on the parent section object, not on POM
|
234
|
+
parent_section.add_subsection(
|
235
|
+
title=title,
|
236
|
+
body=body,
|
237
|
+
bullets=bullets or []
|
238
|
+
)
|
239
|
+
|
240
|
+
def prompt_has_section(self, title: str) -> bool:
|
241
|
+
"""
|
242
|
+
Check if a section exists in the prompt.
|
243
|
+
|
244
|
+
Args:
|
245
|
+
title: Section title to check
|
246
|
+
|
247
|
+
Returns:
|
248
|
+
True if section exists, False otherwise
|
249
|
+
"""
|
250
|
+
if self.agent._use_pom and self.agent.pom:
|
251
|
+
return self.agent.pom.has_section(title)
|
252
|
+
return False
|
253
|
+
|
254
|
+
def get_prompt(self) -> Optional[Union[str, List[Dict[str, Any]]]]:
|
255
|
+
"""
|
256
|
+
Get the prompt configuration.
|
257
|
+
|
258
|
+
Returns:
|
259
|
+
Prompt text or sections or None
|
260
|
+
"""
|
261
|
+
return self._process_prompt_sections()
|
262
|
+
|
263
|
+
def get_raw_prompt(self) -> Optional[str]:
|
264
|
+
"""
|
265
|
+
Get the raw prompt text if set.
|
266
|
+
|
267
|
+
Returns:
|
268
|
+
Raw prompt text or None
|
269
|
+
"""
|
270
|
+
return self._prompt_text
|
271
|
+
|
272
|
+
def get_post_prompt(self) -> Optional[str]:
|
273
|
+
"""
|
274
|
+
Get the post-prompt text.
|
275
|
+
|
276
|
+
Returns:
|
277
|
+
Post-prompt text or None
|
278
|
+
"""
|
279
|
+
return self._post_prompt_text
|
280
|
+
|
281
|
+
def get_contexts(self) -> Optional[Dict[str, Any]]:
|
282
|
+
"""
|
283
|
+
Get the contexts configuration.
|
284
|
+
|
285
|
+
Returns:
|
286
|
+
Contexts dict or None
|
287
|
+
"""
|
288
|
+
return self._contexts
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,15 @@
|
|
1
|
+
"""
|
2
|
+
Copyright (c) 2025 SignalWire
|
3
|
+
|
4
|
+
This file is part of the SignalWire AI Agents SDK.
|
5
|
+
|
6
|
+
Licensed under the MIT License.
|
7
|
+
See LICENSE file in the project root for full license information.
|
8
|
+
"""
|
9
|
+
|
10
|
+
"""Tool management modules."""
|
11
|
+
|
12
|
+
from .registry import ToolRegistry
|
13
|
+
from .decorator import ToolDecorator
|
14
|
+
|
15
|
+
__all__ = ['ToolRegistry', 'ToolDecorator']
|