signalwire-agents 0.1.0__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.
Files changed (32) hide show
  1. signalwire_agents/__init__.py +17 -0
  2. signalwire_agents/agent_server.py +336 -0
  3. signalwire_agents/core/__init__.py +20 -0
  4. signalwire_agents/core/agent_base.py +2449 -0
  5. signalwire_agents/core/function_result.py +104 -0
  6. signalwire_agents/core/pom_builder.py +195 -0
  7. signalwire_agents/core/security/__init__.py +0 -0
  8. signalwire_agents/core/security/session_manager.py +170 -0
  9. signalwire_agents/core/state/__init__.py +8 -0
  10. signalwire_agents/core/state/file_state_manager.py +210 -0
  11. signalwire_agents/core/state/state_manager.py +92 -0
  12. signalwire_agents/core/swaig_function.py +163 -0
  13. signalwire_agents/core/swml_builder.py +205 -0
  14. signalwire_agents/core/swml_handler.py +218 -0
  15. signalwire_agents/core/swml_renderer.py +359 -0
  16. signalwire_agents/core/swml_service.py +1009 -0
  17. signalwire_agents/prefabs/__init__.py +15 -0
  18. signalwire_agents/prefabs/concierge.py +276 -0
  19. signalwire_agents/prefabs/faq_bot.py +314 -0
  20. signalwire_agents/prefabs/info_gatherer.py +253 -0
  21. signalwire_agents/prefabs/survey.py +387 -0
  22. signalwire_agents/schema.json +5611 -0
  23. signalwire_agents/utils/__init__.py +0 -0
  24. signalwire_agents/utils/pom_utils.py +0 -0
  25. signalwire_agents/utils/schema_utils.py +348 -0
  26. signalwire_agents/utils/token_generators.py +0 -0
  27. signalwire_agents/utils/validators.py +0 -0
  28. signalwire_agents-0.1.0.data/data/schema.json +5611 -0
  29. signalwire_agents-0.1.0.dist-info/METADATA +154 -0
  30. signalwire_agents-0.1.0.dist-info/RECORD +32 -0
  31. signalwire_agents-0.1.0.dist-info/WHEEL +5 -0
  32. signalwire_agents-0.1.0.dist-info/top_level.txt +1 -0
File without changes
File without changes
@@ -0,0 +1,348 @@
1
+ """
2
+ Utilities for working with the SWML JSON schema.
3
+
4
+ This module provides functions for loading, parsing, and validating SWML schemas.
5
+ It also provides utilities for working with SWML documents based on the schema.
6
+ """
7
+
8
+ import json
9
+ import os
10
+ import re
11
+ from typing import Dict, List, Any, Optional, Set, Tuple, Callable
12
+
13
+
14
+ class SchemaUtils:
15
+ """
16
+ Utilities for working with SWML JSON schema.
17
+
18
+ This class provides methods for:
19
+ - Loading and parsing schema files
20
+ - Extracting verb definitions
21
+ - Validating SWML objects against the schema
22
+ - Generating helpers for schema operations
23
+ """
24
+
25
+ def __init__(self, schema_path: Optional[str] = None):
26
+ """
27
+ Initialize the SchemaUtils with the provided schema
28
+
29
+ Args:
30
+ schema_path: Path to the schema file (optional)
31
+ """
32
+ self.schema_path = schema_path
33
+ if not self.schema_path:
34
+ self.schema_path = self._get_default_schema_path()
35
+ print(f"No schema_path provided, using default: {self.schema_path}")
36
+
37
+ self.schema = self.load_schema()
38
+ self.verbs = self._extract_verb_definitions()
39
+ print(f"Extracted {len(self.verbs)} verbs from schema")
40
+ if self.verbs:
41
+ print(f"First few verbs: {list(self.verbs.keys())[:5]}")
42
+
43
+ def _get_default_schema_path(self) -> str:
44
+ """
45
+ Get the default path to the schema file
46
+
47
+ Returns:
48
+ Path to the schema file
49
+ """
50
+ # Default path is the schema.json in the root directory
51
+ package_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
52
+ default_path = os.path.join(package_dir, "schema.json")
53
+ print(f"Default schema path: {default_path}")
54
+ print(f"Path exists: {os.path.exists(default_path)}")
55
+
56
+ return default_path
57
+
58
+ def load_schema(self) -> Dict[str, Any]:
59
+ """
60
+ Load the JSON schema from the specified path
61
+
62
+ Returns:
63
+ The schema as a dictionary
64
+ """
65
+ if not self.schema_path:
66
+ print(f"Warning: No schema path provided. Using empty schema.")
67
+ return {}
68
+
69
+ try:
70
+ print(f"Attempting to load schema from: {self.schema_path}")
71
+ print(f"File exists: {os.path.exists(self.schema_path)}")
72
+
73
+ if os.path.exists(self.schema_path):
74
+ with open(self.schema_path, "r") as f:
75
+ schema = json.load(f)
76
+ print(f"Successfully loaded schema from {self.schema_path}")
77
+ print(f"Schema has {len(schema.keys()) if schema else 0} top-level keys")
78
+ if "$defs" in schema:
79
+ print(f"Schema has {len(schema['$defs'])} definitions")
80
+ return schema
81
+ else:
82
+ print(f"Schema file not found at {self.schema_path}")
83
+ return {}
84
+ except (FileNotFoundError, json.JSONDecodeError) as e:
85
+ print(f"Error loading schema: {e}")
86
+ print(f"Using empty schema as fallback")
87
+ return {}
88
+
89
+ def _extract_verb_definitions(self) -> Dict[str, Dict[str, Any]]:
90
+ """
91
+ Extract verb definitions from the schema
92
+
93
+ Returns:
94
+ A dictionary mapping verb names to their definitions
95
+ """
96
+ verbs = {}
97
+
98
+ # Extract from SWMLMethod anyOf
99
+ if "$defs" in self.schema and "SWMLMethod" in self.schema["$defs"]:
100
+ swml_method = self.schema["$defs"]["SWMLMethod"]
101
+ print(f"Found SWMLMethod in schema with keys: {swml_method.keys()}")
102
+
103
+ if "anyOf" in swml_method:
104
+ print(f"Found anyOf in SWMLMethod with {len(swml_method['anyOf'])} items")
105
+
106
+ for ref in swml_method["anyOf"]:
107
+ if "$ref" in ref:
108
+ # Extract the verb name from the reference
109
+ verb_ref = ref["$ref"]
110
+ verb_name = verb_ref.split("/")[-1]
111
+ print(f"Processing verb reference: {verb_ref} -> {verb_name}")
112
+
113
+ # Look up the verb definition
114
+ if verb_name in self.schema["$defs"]:
115
+ verb_def = self.schema["$defs"][verb_name]
116
+
117
+ # Extract the actual verb name (lowercase)
118
+ if "properties" in verb_def:
119
+ prop_names = list(verb_def["properties"].keys())
120
+ if prop_names:
121
+ actual_verb = prop_names[0]
122
+ verbs[actual_verb] = {
123
+ "name": actual_verb,
124
+ "schema_name": verb_name,
125
+ "definition": verb_def
126
+ }
127
+ print(f"Added verb: {actual_verb}")
128
+ else:
129
+ print(f"Missing $defs or SWMLMethod in schema")
130
+ if "$defs" in self.schema:
131
+ print(f"Available definitions: {list(self.schema['$defs'].keys())}")
132
+
133
+ return verbs
134
+
135
+ def get_verb_properties(self, verb_name: str) -> Dict[str, Any]:
136
+ """
137
+ Get the properties for a specific verb
138
+
139
+ Args:
140
+ verb_name: The name of the verb (e.g., "ai", "answer", etc.)
141
+
142
+ Returns:
143
+ The properties for the verb or an empty dict if not found
144
+ """
145
+ if verb_name in self.verbs:
146
+ verb_def = self.verbs[verb_name]["definition"]
147
+ if "properties" in verb_def and verb_name in verb_def["properties"]:
148
+ return verb_def["properties"][verb_name]
149
+ return {}
150
+
151
+ def get_verb_required_properties(self, verb_name: str) -> List[str]:
152
+ """
153
+ Get the required properties for a specific verb
154
+
155
+ Args:
156
+ verb_name: The name of the verb (e.g., "ai", "answer", etc.)
157
+
158
+ Returns:
159
+ List of required property names for the verb or an empty list if not found
160
+ """
161
+ if verb_name in self.verbs:
162
+ verb_def = self.verbs[verb_name]["definition"]
163
+ if "properties" in verb_def and verb_name in verb_def["properties"]:
164
+ verb_props = verb_def["properties"][verb_name]
165
+ return verb_props.get("required", [])
166
+ return []
167
+
168
+ def validate_verb(self, verb_name: str, verb_config: Dict[str, Any]) -> Tuple[bool, List[str]]:
169
+ """
170
+ Validate a verb configuration against the schema
171
+
172
+ Args:
173
+ verb_name: The name of the verb (e.g., "ai", "answer", etc.)
174
+ verb_config: The configuration for the verb
175
+
176
+ Returns:
177
+ (is_valid, error_messages) tuple
178
+ """
179
+ # Simple validation for now - can be enhanced with more complete JSON Schema validation
180
+ errors = []
181
+
182
+ # Check if the verb exists in the schema
183
+ if verb_name not in self.verbs:
184
+ errors.append(f"Unknown verb: {verb_name}")
185
+ return False, errors
186
+
187
+ # Get the required properties for this verb
188
+ required_props = self.get_verb_required_properties(verb_name)
189
+
190
+ # Check if all required properties are present
191
+ for prop in required_props:
192
+ if prop not in verb_config:
193
+ errors.append(f"Missing required property '{prop}' for verb '{verb_name}'")
194
+
195
+ # Return validation result
196
+ return len(errors) == 0, errors
197
+
198
+ def get_all_verb_names(self) -> List[str]:
199
+ """
200
+ Get all verb names defined in the schema
201
+
202
+ Returns:
203
+ List of verb names
204
+ """
205
+ return list(self.verbs.keys())
206
+
207
+ def get_verb_parameters(self, verb_name: str) -> Dict[str, Any]:
208
+ """
209
+ Get the parameter definitions for a specific verb
210
+
211
+ Args:
212
+ verb_name: The name of the verb (e.g., "ai", "answer", etc.)
213
+
214
+ Returns:
215
+ Dictionary mapping parameter names to their definitions
216
+ """
217
+ properties = self.get_verb_properties(verb_name)
218
+ if "properties" in properties:
219
+ return properties["properties"]
220
+ return {}
221
+
222
+ def generate_method_signature(self, verb_name: str) -> str:
223
+ """
224
+ Generate a Python method signature for a verb
225
+
226
+ Args:
227
+ verb_name: The name of the verb
228
+
229
+ Returns:
230
+ A Python method signature string
231
+ """
232
+ # Get the verb properties
233
+ verb_props = self.get_verb_properties(verb_name)
234
+
235
+ # Get verb parameters
236
+ verb_params = self.get_verb_parameters(verb_name)
237
+
238
+ # Get required parameters
239
+ required_params = self.get_verb_required_properties(verb_name)
240
+
241
+ # Initialize method parameters
242
+ param_list = ["self"]
243
+
244
+ # Add the parameters
245
+ for param_name, param_def in verb_params.items():
246
+ # Check if this is a required parameter
247
+ is_required = param_name in required_params
248
+
249
+ # Determine parameter type annotation
250
+ param_type = self._get_type_annotation(param_def)
251
+
252
+ # Add default value if not required
253
+ if is_required:
254
+ param_list.append(f"{param_name}: {param_type}")
255
+ else:
256
+ param_list.append(f"{param_name}: Optional[{param_type}] = None")
257
+
258
+ # Add **kwargs at the end
259
+ param_list.append("**kwargs")
260
+
261
+ # Generate method docstring
262
+ docstring = f'"""\n Add the {verb_name} verb to the current document\n \n'
263
+
264
+ # Add parameter documentation
265
+ for param_name, param_def in verb_params.items():
266
+ description = param_def.get("description", "")
267
+ # Clean up the description for docstring
268
+ description = description.replace('\n', ' ').strip()
269
+ docstring += f" Args:\n {param_name}: {description}\n"
270
+
271
+ # Add return documentation
272
+ docstring += f' \n Returns:\n True if the verb was added successfully, False otherwise\n """\n'
273
+
274
+ # Create the full method signature with docstring
275
+ method_signature = f"def {verb_name}({', '.join(param_list)}) -> bool:\n{docstring}"
276
+
277
+ return method_signature
278
+
279
+ def generate_method_body(self, verb_name: str) -> str:
280
+ """
281
+ Generate the method body implementation for a verb
282
+
283
+ Args:
284
+ verb_name: The name of the verb
285
+
286
+ Returns:
287
+ The method body as a string
288
+ """
289
+ # Get verb parameters
290
+ verb_params = self.get_verb_parameters(verb_name)
291
+
292
+ body = []
293
+ body.append(" # Prepare the configuration")
294
+ body.append(" config = {}")
295
+
296
+ # Add handling for each parameter
297
+ for param_name in verb_params.keys():
298
+ body.append(f" if {param_name} is not None:")
299
+ body.append(f" config['{param_name}'] = {param_name}")
300
+
301
+ # Add handling for kwargs
302
+ body.append(" # Add any additional parameters from kwargs")
303
+ body.append(" for key, value in kwargs.items():")
304
+ body.append(" if value is not None:")
305
+ body.append(" config[key] = value")
306
+
307
+ # Add the call to add_verb
308
+ body.append("")
309
+ body.append(f" # Add the {verb_name} verb")
310
+ body.append(f" return self.add_verb('{verb_name}', config)")
311
+
312
+ return "\n".join(body)
313
+
314
+ def _get_type_annotation(self, param_def: Dict[str, Any]) -> str:
315
+ """
316
+ Get the Python type annotation for a parameter
317
+
318
+ Args:
319
+ param_def: Parameter definition from the schema
320
+
321
+ Returns:
322
+ Python type annotation as a string
323
+ """
324
+ schema_type = param_def.get("type")
325
+
326
+ if schema_type == "string":
327
+ return "str"
328
+ elif schema_type == "integer":
329
+ return "int"
330
+ elif schema_type == "number":
331
+ return "float"
332
+ elif schema_type == "boolean":
333
+ return "bool"
334
+ elif schema_type == "array":
335
+ item_type = "Any"
336
+ if "items" in param_def:
337
+ item_def = param_def["items"]
338
+ item_type = self._get_type_annotation(item_def)
339
+ return f"List[{item_type}]"
340
+ elif schema_type == "object":
341
+ return "Dict[str, Any]"
342
+ else:
343
+ # Handle complex types or oneOf/anyOf
344
+ if "anyOf" in param_def or "oneOf" in param_def:
345
+ return "Any"
346
+ if "$ref" in param_def:
347
+ return "Any" # Could be enhanced to resolve references
348
+ return "Any"
File without changes
File without changes