signalwire-agents 0.1.9__py3-none-any.whl → 0.1.11__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 +39 -4
- signalwire_agents/agent_server.py +46 -2
- signalwire_agents/cli/__init__.py +9 -0
- signalwire_agents/cli/test_swaig.py +2545 -0
- signalwire_agents/core/agent_base.py +691 -82
- signalwire_agents/core/contexts.py +289 -0
- signalwire_agents/core/data_map.py +499 -0
- signalwire_agents/core/function_result.py +57 -10
- signalwire_agents/core/skill_base.py +31 -1
- signalwire_agents/core/skill_manager.py +89 -23
- signalwire_agents/core/swaig_function.py +13 -1
- signalwire_agents/core/swml_handler.py +37 -13
- signalwire_agents/core/swml_service.py +37 -28
- signalwire_agents/skills/datasphere/__init__.py +12 -0
- signalwire_agents/skills/datasphere/skill.py +229 -0
- signalwire_agents/skills/datasphere_serverless/__init__.py +1 -0
- signalwire_agents/skills/datasphere_serverless/skill.py +156 -0
- signalwire_agents/skills/datetime/skill.py +7 -3
- signalwire_agents/skills/joke/__init__.py +1 -0
- signalwire_agents/skills/joke/skill.py +88 -0
- signalwire_agents/skills/math/skill.py +8 -5
- signalwire_agents/skills/registry.py +23 -4
- signalwire_agents/skills/web_search/skill.py +58 -33
- signalwire_agents/skills/wikipedia/__init__.py +9 -0
- signalwire_agents/skills/wikipedia/skill.py +180 -0
- signalwire_agents/utils/__init__.py +2 -0
- signalwire_agents/utils/schema_utils.py +111 -44
- signalwire_agents/utils/serverless.py +38 -0
- signalwire_agents-0.1.11.dist-info/METADATA +756 -0
- signalwire_agents-0.1.11.dist-info/RECORD +58 -0
- {signalwire_agents-0.1.9.dist-info → signalwire_agents-0.1.11.dist-info}/WHEEL +1 -1
- signalwire_agents-0.1.11.dist-info/entry_points.txt +2 -0
- signalwire_agents-0.1.9.dist-info/METADATA +0 -311
- signalwire_agents-0.1.9.dist-info/RECORD +0 -44
- {signalwire_agents-0.1.9.data → signalwire_agents-0.1.11.data}/data/schema.json +0 -0
- {signalwire_agents-0.1.9.dist-info → signalwire_agents-0.1.11.dist-info}/licenses/LICENSE +0 -0
- {signalwire_agents-0.1.9.dist-info → signalwire_agents-0.1.11.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,289 @@
|
|
1
|
+
"""
|
2
|
+
Contexts and Steps System for SignalWire Agents
|
3
|
+
|
4
|
+
This module provides an alternative to traditional POM-based prompts by allowing
|
5
|
+
agents to be defined as structured contexts with sequential steps. Each step
|
6
|
+
contains its own prompt, completion criteria, and function restrictions.
|
7
|
+
"""
|
8
|
+
|
9
|
+
from typing import Dict, List, Optional, Union, Any
|
10
|
+
|
11
|
+
|
12
|
+
class Step:
|
13
|
+
"""Represents a single step within a context"""
|
14
|
+
|
15
|
+
def __init__(self, name: str):
|
16
|
+
self.name = name
|
17
|
+
self._text: Optional[str] = None
|
18
|
+
self._step_criteria: Optional[str] = None
|
19
|
+
self._functions: Optional[Union[str, List[str]]] = None
|
20
|
+
self._valid_steps: Optional[List[str]] = None
|
21
|
+
|
22
|
+
# POM-style sections for rich prompts
|
23
|
+
self._sections: List[Dict[str, Any]] = []
|
24
|
+
|
25
|
+
def set_text(self, text: str) -> 'Step':
|
26
|
+
"""
|
27
|
+
Set the step's prompt text directly
|
28
|
+
|
29
|
+
Args:
|
30
|
+
text: The prompt text for this step
|
31
|
+
|
32
|
+
Returns:
|
33
|
+
Self for method chaining
|
34
|
+
"""
|
35
|
+
if self._sections:
|
36
|
+
raise ValueError("Cannot use set_text() when POM sections have been added. Use one approach or the other.")
|
37
|
+
self._text = text
|
38
|
+
return self
|
39
|
+
|
40
|
+
def add_section(self, title: str, body: str) -> 'Step':
|
41
|
+
"""
|
42
|
+
Add a POM section to the step
|
43
|
+
|
44
|
+
Args:
|
45
|
+
title: Section title
|
46
|
+
body: Section body text
|
47
|
+
|
48
|
+
Returns:
|
49
|
+
Self for method chaining
|
50
|
+
"""
|
51
|
+
if self._text is not None:
|
52
|
+
raise ValueError("Cannot add POM sections when set_text() has been used. Use one approach or the other.")
|
53
|
+
self._sections.append({"title": title, "body": body})
|
54
|
+
return self
|
55
|
+
|
56
|
+
def add_bullets(self, title: str, bullets: List[str]) -> 'Step':
|
57
|
+
"""
|
58
|
+
Add a POM section with bullet points
|
59
|
+
|
60
|
+
Args:
|
61
|
+
title: Section title
|
62
|
+
bullets: List of bullet points
|
63
|
+
|
64
|
+
Returns:
|
65
|
+
Self for method chaining
|
66
|
+
"""
|
67
|
+
if self._text is not None:
|
68
|
+
raise ValueError("Cannot add POM sections when set_text() has been used. Use one approach or the other.")
|
69
|
+
self._sections.append({"title": title, "bullets": bullets})
|
70
|
+
return self
|
71
|
+
|
72
|
+
def set_step_criteria(self, criteria: str) -> 'Step':
|
73
|
+
"""
|
74
|
+
Set the criteria for determining when this step is complete
|
75
|
+
|
76
|
+
Args:
|
77
|
+
criteria: Description of step completion criteria
|
78
|
+
|
79
|
+
Returns:
|
80
|
+
Self for method chaining
|
81
|
+
"""
|
82
|
+
self._step_criteria = criteria
|
83
|
+
return self
|
84
|
+
|
85
|
+
def set_functions(self, functions: Union[str, List[str]]) -> 'Step':
|
86
|
+
"""
|
87
|
+
Set which functions are available in this step
|
88
|
+
|
89
|
+
Args:
|
90
|
+
functions: "none" to disable all functions, or list of function names
|
91
|
+
|
92
|
+
Returns:
|
93
|
+
Self for method chaining
|
94
|
+
"""
|
95
|
+
self._functions = functions
|
96
|
+
return self
|
97
|
+
|
98
|
+
def set_valid_steps(self, steps: List[str]) -> 'Step':
|
99
|
+
"""
|
100
|
+
Set which steps can be navigated to from this step
|
101
|
+
|
102
|
+
Args:
|
103
|
+
steps: List of valid step names (include "next" for sequential flow)
|
104
|
+
|
105
|
+
Returns:
|
106
|
+
Self for method chaining
|
107
|
+
"""
|
108
|
+
self._valid_steps = steps
|
109
|
+
return self
|
110
|
+
|
111
|
+
def _render_text(self) -> str:
|
112
|
+
"""Render the step's prompt text"""
|
113
|
+
if self._text is not None:
|
114
|
+
return self._text
|
115
|
+
|
116
|
+
if not self._sections:
|
117
|
+
raise ValueError(f"Step '{self.name}' has no text or POM sections defined")
|
118
|
+
|
119
|
+
# Convert POM sections to markdown
|
120
|
+
markdown_parts = []
|
121
|
+
for section in self._sections:
|
122
|
+
if "bullets" in section:
|
123
|
+
markdown_parts.append(f"## {section['title']}")
|
124
|
+
for bullet in section["bullets"]:
|
125
|
+
markdown_parts.append(f"- {bullet}")
|
126
|
+
else:
|
127
|
+
markdown_parts.append(f"## {section['title']}")
|
128
|
+
markdown_parts.append(section["body"])
|
129
|
+
markdown_parts.append("") # Add spacing
|
130
|
+
|
131
|
+
return "\n".join(markdown_parts).strip()
|
132
|
+
|
133
|
+
def to_dict(self) -> Dict[str, Any]:
|
134
|
+
"""Convert step to dictionary for SWML generation"""
|
135
|
+
step_dict = {
|
136
|
+
"text": self._render_text()
|
137
|
+
}
|
138
|
+
|
139
|
+
if self._step_criteria:
|
140
|
+
step_dict["step_criteria"] = self._step_criteria
|
141
|
+
|
142
|
+
if self._functions is not None:
|
143
|
+
step_dict["functions"] = self._functions
|
144
|
+
|
145
|
+
if self._valid_steps is not None:
|
146
|
+
step_dict["valid_steps"] = self._valid_steps
|
147
|
+
|
148
|
+
return step_dict
|
149
|
+
|
150
|
+
|
151
|
+
class Context:
|
152
|
+
"""Represents a single context containing multiple steps"""
|
153
|
+
|
154
|
+
def __init__(self, name: str):
|
155
|
+
self.name = name
|
156
|
+
self._steps: Dict[str, Step] = {}
|
157
|
+
self._step_order: List[str] = []
|
158
|
+
self._valid_contexts: Optional[List[str]] = None
|
159
|
+
|
160
|
+
def add_step(self, name: str) -> Step:
|
161
|
+
"""
|
162
|
+
Add a new step to this context
|
163
|
+
|
164
|
+
Args:
|
165
|
+
name: Step name
|
166
|
+
|
167
|
+
Returns:
|
168
|
+
Step object for method chaining
|
169
|
+
"""
|
170
|
+
if name in self._steps:
|
171
|
+
raise ValueError(f"Step '{name}' already exists in context '{self.name}'")
|
172
|
+
|
173
|
+
step = Step(name)
|
174
|
+
self._steps[name] = step
|
175
|
+
self._step_order.append(name)
|
176
|
+
return step
|
177
|
+
|
178
|
+
def set_valid_contexts(self, contexts: List[str]) -> 'Context':
|
179
|
+
"""
|
180
|
+
Set which contexts can be navigated to from this context
|
181
|
+
|
182
|
+
Args:
|
183
|
+
contexts: List of valid context names
|
184
|
+
|
185
|
+
Returns:
|
186
|
+
Self for method chaining
|
187
|
+
"""
|
188
|
+
self._valid_contexts = contexts
|
189
|
+
return self
|
190
|
+
|
191
|
+
def to_dict(self) -> Dict[str, Any]:
|
192
|
+
"""Convert context to dictionary for SWML generation"""
|
193
|
+
if not self._steps:
|
194
|
+
raise ValueError(f"Context '{self.name}' has no steps defined")
|
195
|
+
|
196
|
+
context_dict = {
|
197
|
+
"steps": [self._steps[name].to_dict() for name in self._step_order]
|
198
|
+
}
|
199
|
+
|
200
|
+
if self._valid_contexts is not None:
|
201
|
+
context_dict["valid_contexts"] = self._valid_contexts
|
202
|
+
|
203
|
+
return context_dict
|
204
|
+
|
205
|
+
|
206
|
+
class ContextBuilder:
|
207
|
+
"""Main builder class for creating contexts and steps"""
|
208
|
+
|
209
|
+
def __init__(self, agent):
|
210
|
+
self._agent = agent
|
211
|
+
self._contexts: Dict[str, Context] = {}
|
212
|
+
self._context_order: List[str] = []
|
213
|
+
|
214
|
+
def add_context(self, name: str) -> Context:
|
215
|
+
"""
|
216
|
+
Add a new context
|
217
|
+
|
218
|
+
Args:
|
219
|
+
name: Context name
|
220
|
+
|
221
|
+
Returns:
|
222
|
+
Context object for method chaining
|
223
|
+
"""
|
224
|
+
if name in self._contexts:
|
225
|
+
raise ValueError(f"Context '{name}' already exists")
|
226
|
+
|
227
|
+
context = Context(name)
|
228
|
+
self._contexts[name] = context
|
229
|
+
self._context_order.append(name)
|
230
|
+
return context
|
231
|
+
|
232
|
+
def validate(self) -> None:
|
233
|
+
"""Validate the contexts configuration"""
|
234
|
+
if not self._contexts:
|
235
|
+
raise ValueError("At least one context must be defined")
|
236
|
+
|
237
|
+
# If only one context, it must be named "default"
|
238
|
+
if len(self._contexts) == 1:
|
239
|
+
context_name = list(self._contexts.keys())[0]
|
240
|
+
if context_name != "default":
|
241
|
+
raise ValueError("When using a single context, it must be named 'default'")
|
242
|
+
|
243
|
+
# Validate each context has at least one step
|
244
|
+
for context_name, context in self._contexts.items():
|
245
|
+
if not context._steps:
|
246
|
+
raise ValueError(f"Context '{context_name}' must have at least one step")
|
247
|
+
|
248
|
+
# Validate step references in valid_steps
|
249
|
+
for context_name, context in self._contexts.items():
|
250
|
+
for step_name, step in context._steps.items():
|
251
|
+
if step._valid_steps:
|
252
|
+
for valid_step in step._valid_steps:
|
253
|
+
if valid_step != "next" and valid_step not in context._steps:
|
254
|
+
raise ValueError(
|
255
|
+
f"Step '{step_name}' in context '{context_name}' "
|
256
|
+
f"references unknown step '{valid_step}'"
|
257
|
+
)
|
258
|
+
|
259
|
+
# Validate context references in valid_contexts
|
260
|
+
for context_name, context in self._contexts.items():
|
261
|
+
if context._valid_contexts:
|
262
|
+
for valid_context in context._valid_contexts:
|
263
|
+
if valid_context not in self._contexts:
|
264
|
+
raise ValueError(
|
265
|
+
f"Context '{context_name}' references unknown context '{valid_context}'"
|
266
|
+
)
|
267
|
+
|
268
|
+
def to_dict(self) -> Dict[str, Any]:
|
269
|
+
"""Convert all contexts to dictionary for SWML generation"""
|
270
|
+
self.validate()
|
271
|
+
|
272
|
+
return {
|
273
|
+
context_name: context.to_dict()
|
274
|
+
for context_name in self._context_order
|
275
|
+
for context_name, context in [(context_name, self._contexts[context_name])]
|
276
|
+
}
|
277
|
+
|
278
|
+
|
279
|
+
def create_simple_context(name: str = "default") -> Context:
|
280
|
+
"""
|
281
|
+
Helper function to create a simple single context
|
282
|
+
|
283
|
+
Args:
|
284
|
+
name: Context name (defaults to "default")
|
285
|
+
|
286
|
+
Returns:
|
287
|
+
Context object for method chaining
|
288
|
+
"""
|
289
|
+
return Context(name)
|