signalwire-agents 0.1.10__py3-none-any.whl → 0.1.12__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 +43 -4
- signalwire_agents/agent_server.py +268 -15
- signalwire_agents/cli/__init__.py +9 -0
- signalwire_agents/cli/build_search.py +457 -0
- signalwire_agents/cli/test_swaig.py +2609 -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/logging_config.py +232 -0
- signalwire_agents/core/skill_base.py +27 -37
- 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/search/__init__.py +131 -0
- signalwire_agents/search/document_processor.py +764 -0
- signalwire_agents/search/index_builder.py +534 -0
- signalwire_agents/search/query_processor.py +371 -0
- signalwire_agents/search/search_engine.py +383 -0
- signalwire_agents/search/search_service.py +251 -0
- 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 +9 -5
- signalwire_agents/skills/joke/__init__.py +1 -0
- signalwire_agents/skills/joke/skill.py +88 -0
- signalwire_agents/skills/math/skill.py +9 -6
- signalwire_agents/skills/native_vector_search/__init__.py +1 -0
- signalwire_agents/skills/native_vector_search/skill.py +352 -0
- signalwire_agents/skills/registry.py +10 -4
- signalwire_agents/skills/web_search/skill.py +57 -21
- signalwire_agents/skills/wikipedia/__init__.py +9 -0
- signalwire_agents/skills/wikipedia/skill.py +180 -0
- signalwire_agents/utils/__init__.py +14 -0
- signalwire_agents/utils/schema_utils.py +111 -44
- signalwire_agents-0.1.12.dist-info/METADATA +863 -0
- signalwire_agents-0.1.12.dist-info/RECORD +67 -0
- {signalwire_agents-0.1.10.dist-info → signalwire_agents-0.1.12.dist-info}/WHEEL +1 -1
- signalwire_agents-0.1.12.dist-info/entry_points.txt +3 -0
- signalwire_agents-0.1.10.dist-info/METADATA +0 -319
- signalwire_agents-0.1.10.dist-info/RECORD +0 -44
- {signalwire_agents-0.1.10.data → signalwire_agents-0.1.12.data}/data/schema.json +0 -0
- {signalwire_agents-0.1.10.dist-info → signalwire_agents-0.1.12.dist-info}/licenses/LICENSE +0 -0
- {signalwire_agents-0.1.10.dist-info → signalwire_agents-0.1.12.dist-info}/top_level.txt +0 -0
@@ -1,3 +1,4 @@
|
|
1
|
+
#!/usr/bin/env python3
|
1
2
|
"""
|
2
3
|
Copyright (c) 2025 SignalWire
|
3
4
|
|
@@ -7,11 +8,9 @@ Licensed under the MIT License.
|
|
7
8
|
See LICENSE file in the project root for full license information.
|
8
9
|
"""
|
9
10
|
|
11
|
+
# -*- coding: utf-8 -*-
|
10
12
|
"""
|
11
|
-
|
12
|
-
|
13
|
-
This class provides the foundation for creating and serving SWML documents.
|
14
|
-
It handles schema validation, document creation, and web service functionality.
|
13
|
+
Base SWML Service for SignalWire Agents
|
15
14
|
"""
|
16
15
|
|
17
16
|
import os
|
@@ -37,11 +36,11 @@ try:
|
|
37
36
|
structlog.stdlib.add_logger_name,
|
38
37
|
structlog.stdlib.add_log_level,
|
39
38
|
structlog.stdlib.PositionalArgumentsFormatter(),
|
40
|
-
structlog.processors.TimeStamper(fmt="
|
39
|
+
structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S"),
|
41
40
|
structlog.processors.StackInfoRenderer(),
|
42
41
|
structlog.processors.format_exc_info,
|
43
42
|
structlog.processors.UnicodeDecoder(),
|
44
|
-
structlog.
|
43
|
+
structlog.dev.ConsoleRenderer()
|
45
44
|
],
|
46
45
|
context_class=dict,
|
47
46
|
logger_factory=structlog.stdlib.LoggerFactory(),
|
@@ -188,17 +187,21 @@ class SWMLService:
|
|
188
187
|
"""
|
189
188
|
Create auto-vivified methods for all verbs at initialization time
|
190
189
|
"""
|
191
|
-
|
190
|
+
self.log.debug("creating_verb_methods")
|
192
191
|
|
193
192
|
# Get all verb names from the schema
|
193
|
+
if not self.schema_utils:
|
194
|
+
self.log.warning("no_schema_utils_available")
|
195
|
+
return
|
196
|
+
|
194
197
|
verb_names = self.schema_utils.get_all_verb_names()
|
195
|
-
|
198
|
+
self.log.debug("found_verbs_in_schema", count=len(verb_names))
|
196
199
|
|
197
200
|
# Create a method for each verb
|
198
201
|
for verb_name in verb_names:
|
199
202
|
# Skip verbs that already have specific methods
|
200
203
|
if hasattr(self, verb_name):
|
201
|
-
|
204
|
+
self.log.debug("skipping_verb_has_method", verb=verb_name)
|
202
205
|
continue
|
203
206
|
|
204
207
|
# Handle sleep verb specially since it takes an integer directly
|
@@ -210,7 +213,7 @@ class SWMLService:
|
|
210
213
|
Args:
|
211
214
|
duration: The amount of time to sleep in milliseconds
|
212
215
|
"""
|
213
|
-
|
216
|
+
self.log.debug("executing_sleep_verb", duration=duration)
|
214
217
|
# Sleep verb takes a direct integer parameter in SWML
|
215
218
|
if duration is not None:
|
216
219
|
return self_instance.add_verb("sleep", duration)
|
@@ -226,7 +229,7 @@ class SWMLService:
|
|
226
229
|
# Also cache it for later
|
227
230
|
self._verb_methods_cache[verb_name] = sleep_method
|
228
231
|
|
229
|
-
|
232
|
+
self.log.debug("created_special_method", verb=verb_name)
|
230
233
|
continue
|
231
234
|
|
232
235
|
# Generate the method implementation for normal verbs
|
@@ -235,7 +238,7 @@ class SWMLService:
|
|
235
238
|
"""
|
236
239
|
Dynamically generated method for SWML verb
|
237
240
|
"""
|
238
|
-
|
241
|
+
self.log.debug("executing_verb_method", verb=name, kwargs_count=len(kwargs))
|
239
242
|
config = {}
|
240
243
|
for key, value in kwargs.items():
|
241
244
|
if value is not None:
|
@@ -260,7 +263,7 @@ class SWMLService:
|
|
260
263
|
# Also cache it for later
|
261
264
|
self._verb_methods_cache[verb_name] = method
|
262
265
|
|
263
|
-
|
266
|
+
self.log.debug("created_verb_method", verb=verb_name)
|
264
267
|
|
265
268
|
def __getattr__(self, name: str) -> Any:
|
266
269
|
"""
|
@@ -280,21 +283,26 @@ class SWMLService:
|
|
280
283
|
Raises:
|
281
284
|
AttributeError: If name is not a valid SWML verb
|
282
285
|
"""
|
283
|
-
|
286
|
+
self.log.debug("getattr_called", attribute=name)
|
284
287
|
|
285
288
|
# Simple version to match our test script
|
286
289
|
# First check if this is a valid SWML verb
|
290
|
+
if not self.schema_utils:
|
291
|
+
msg = f"'{self.__class__.__name__}' object has no attribute '{name}' (no schema available)"
|
292
|
+
self.log.debug("getattr_no_schema", attribute=name)
|
293
|
+
raise AttributeError(msg)
|
294
|
+
|
287
295
|
verb_names = self.schema_utils.get_all_verb_names()
|
288
296
|
|
289
297
|
if name in verb_names:
|
290
|
-
|
298
|
+
self.log.debug("getattr_valid_verb", verb=name)
|
291
299
|
|
292
300
|
# Check if we already have this method in the cache
|
293
301
|
if not hasattr(self, '_verb_methods_cache'):
|
294
302
|
self._verb_methods_cache = {}
|
295
303
|
|
296
304
|
if name in self._verb_methods_cache:
|
297
|
-
|
305
|
+
self.log.debug("getattr_cached_method", verb=name)
|
298
306
|
return types.MethodType(self._verb_methods_cache[name], self)
|
299
307
|
|
300
308
|
# Handle sleep verb specially since it takes an integer directly
|
@@ -306,7 +314,7 @@ class SWMLService:
|
|
306
314
|
Args:
|
307
315
|
duration: The amount of time to sleep in milliseconds
|
308
316
|
"""
|
309
|
-
|
317
|
+
self.log.debug("executing_sleep_method", duration=duration)
|
310
318
|
# Sleep verb takes a direct integer parameter in SWML
|
311
319
|
if duration is not None:
|
312
320
|
return self_instance.add_verb("sleep", duration)
|
@@ -317,7 +325,7 @@ class SWMLService:
|
|
317
325
|
raise TypeError("sleep() missing required argument: 'duration'")
|
318
326
|
|
319
327
|
# Cache the method for future use
|
320
|
-
|
328
|
+
self.log.debug("caching_sleep_method", verb=name)
|
321
329
|
self._verb_methods_cache[name] = sleep_method
|
322
330
|
|
323
331
|
# Return the bound method
|
@@ -328,7 +336,7 @@ class SWMLService:
|
|
328
336
|
"""
|
329
337
|
Dynamically generated method for SWML verb
|
330
338
|
"""
|
331
|
-
|
339
|
+
self.log.debug("executing_dynamic_verb", verb=name, kwargs_count=len(kwargs))
|
332
340
|
config = {}
|
333
341
|
for key, value in kwargs.items():
|
334
342
|
if value is not None:
|
@@ -343,7 +351,7 @@ class SWMLService:
|
|
343
351
|
verb_method.__doc__ = f"Add the {name} verb to the document."
|
344
352
|
|
345
353
|
# Cache the method for future use
|
346
|
-
|
354
|
+
self.log.debug("caching_verb_method", verb=name)
|
347
355
|
self._verb_methods_cache[name] = verb_method
|
348
356
|
|
349
357
|
# Return the bound method
|
@@ -351,7 +359,7 @@ class SWMLService:
|
|
351
359
|
|
352
360
|
# Not a valid verb
|
353
361
|
msg = f"'{self.__class__.__name__}' object has no attribute '{name}'"
|
354
|
-
|
362
|
+
self.log.debug("getattr_invalid_attribute", attribute=name, error=msg)
|
355
363
|
raise AttributeError(msg)
|
356
364
|
|
357
365
|
def _find_schema_path(self) -> Optional[str]:
|
@@ -823,7 +831,7 @@ class SWMLService:
|
|
823
831
|
route_with_slash = route_path + "/"
|
824
832
|
|
825
833
|
# Log the incoming path for debugging
|
826
|
-
|
834
|
+
self.log.debug("catch_all_route_hit", path=full_path, route=route_path)
|
827
835
|
|
828
836
|
# Check for exact match to our route (without trailing slash)
|
829
837
|
if full_path == route_path:
|
@@ -851,21 +859,21 @@ class SWMLService:
|
|
851
859
|
return await self._handle_request(request, response)
|
852
860
|
|
853
861
|
# Not our route or not matching our patterns
|
854
|
-
|
862
|
+
self.log.debug("no_route_match", path=full_path)
|
855
863
|
return {"error": "Path not found"}
|
856
864
|
|
857
|
-
#
|
858
|
-
|
865
|
+
# Log all routes for debugging
|
866
|
+
self.log.debug("registered_routes", service=self.name)
|
859
867
|
for route in app.routes:
|
860
868
|
if hasattr(route, "path"):
|
861
|
-
|
869
|
+
self.log.debug("route_registered", path=route.path)
|
862
870
|
|
863
871
|
self._app = app
|
864
872
|
|
865
873
|
host = host or self.host
|
866
874
|
port = port or self.port
|
867
875
|
|
868
|
-
#
|
876
|
+
# Get the auth credentials
|
869
877
|
username, password = self._basic_auth
|
870
878
|
|
871
879
|
# Use correct protocol and host in displayed URL
|
@@ -878,12 +886,13 @@ class SWMLService:
|
|
878
886
|
username=username,
|
879
887
|
password_length=len(password))
|
880
888
|
|
889
|
+
# Print user-friendly startup message (keep for UX)
|
881
890
|
print(f"Service '{self.name}' is available at:")
|
882
891
|
print(f"URL: {protocol}://{display_host}{self.route}")
|
883
892
|
print(f"URL with trailing slash: {protocol}://{display_host}{self.route}/")
|
884
893
|
print(f"Basic Auth: {username}:{password}")
|
885
894
|
|
886
|
-
# Check if SIP routing is enabled and
|
895
|
+
# Check if SIP routing is enabled and log additional info
|
887
896
|
if self._routing_callbacks:
|
888
897
|
print(f"Callback endpoints:")
|
889
898
|
for path in self._routing_callbacks:
|
@@ -0,0 +1,131 @@
|
|
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
|
+
"""SignalWire Agents Local Search Module
|
11
|
+
|
12
|
+
This module provides local search capabilities for the SignalWire Agents SDK.
|
13
|
+
It requires additional dependencies that can be installed with:
|
14
|
+
|
15
|
+
pip install signalwire-agents[search] # Basic search
|
16
|
+
pip install signalwire-agents[search-full] # + Document processing
|
17
|
+
pip install signalwire-agents[search-nlp] # + Advanced NLP
|
18
|
+
pip install signalwire-agents[search-all] # All features
|
19
|
+
"""
|
20
|
+
|
21
|
+
import warnings
|
22
|
+
|
23
|
+
# Check for core search dependencies
|
24
|
+
_SEARCH_AVAILABLE = True
|
25
|
+
_MISSING_DEPS = []
|
26
|
+
|
27
|
+
try:
|
28
|
+
import numpy
|
29
|
+
except ImportError:
|
30
|
+
_SEARCH_AVAILABLE = False
|
31
|
+
_MISSING_DEPS.append('numpy')
|
32
|
+
|
33
|
+
try:
|
34
|
+
import sklearn
|
35
|
+
except ImportError:
|
36
|
+
_SEARCH_AVAILABLE = False
|
37
|
+
_MISSING_DEPS.append('scikit-learn')
|
38
|
+
|
39
|
+
try:
|
40
|
+
import sentence_transformers
|
41
|
+
except ImportError:
|
42
|
+
_SEARCH_AVAILABLE = False
|
43
|
+
_MISSING_DEPS.append('sentence-transformers')
|
44
|
+
|
45
|
+
try:
|
46
|
+
import nltk
|
47
|
+
except ImportError:
|
48
|
+
_SEARCH_AVAILABLE = False
|
49
|
+
_MISSING_DEPS.append('nltk')
|
50
|
+
|
51
|
+
def _check_search_dependencies():
|
52
|
+
"""Check if search dependencies are available and provide helpful error message"""
|
53
|
+
if not _SEARCH_AVAILABLE:
|
54
|
+
missing = ', '.join(_MISSING_DEPS)
|
55
|
+
raise ImportError(
|
56
|
+
f"Search functionality requires additional dependencies: {missing}\n"
|
57
|
+
f"Install with: pip install signalwire-agents[search]\n"
|
58
|
+
f"For full features: pip install signalwire-agents[search-all]"
|
59
|
+
)
|
60
|
+
|
61
|
+
# Conditional imports based on available dependencies
|
62
|
+
__all__ = []
|
63
|
+
|
64
|
+
if _SEARCH_AVAILABLE:
|
65
|
+
try:
|
66
|
+
from .query_processor import preprocess_query, preprocess_document_content
|
67
|
+
from .document_processor import DocumentProcessor
|
68
|
+
from .index_builder import IndexBuilder
|
69
|
+
from .search_engine import SearchEngine
|
70
|
+
from .search_service import SearchService
|
71
|
+
|
72
|
+
__all__ = [
|
73
|
+
'preprocess_query',
|
74
|
+
'preprocess_document_content',
|
75
|
+
'DocumentProcessor',
|
76
|
+
'IndexBuilder',
|
77
|
+
'SearchEngine',
|
78
|
+
'SearchService'
|
79
|
+
]
|
80
|
+
except ImportError as e:
|
81
|
+
# Some search components failed to import
|
82
|
+
warnings.warn(
|
83
|
+
f"Some search components failed to import: {e}\n"
|
84
|
+
f"For full search functionality, install: pip install signalwire-agents[search-all]",
|
85
|
+
ImportWarning
|
86
|
+
)
|
87
|
+
|
88
|
+
# Try to import what we can
|
89
|
+
try:
|
90
|
+
from .query_processor import preprocess_query, preprocess_document_content
|
91
|
+
__all__.extend(['preprocess_query', 'preprocess_document_content'])
|
92
|
+
except ImportError:
|
93
|
+
pass
|
94
|
+
|
95
|
+
try:
|
96
|
+
from .document_processor import DocumentProcessor
|
97
|
+
__all__.append('DocumentProcessor')
|
98
|
+
except ImportError:
|
99
|
+
pass
|
100
|
+
else:
|
101
|
+
# Provide stub functions that give helpful error messages
|
102
|
+
def preprocess_query(*args, **kwargs):
|
103
|
+
_check_search_dependencies()
|
104
|
+
|
105
|
+
def preprocess_document_content(*args, **kwargs):
|
106
|
+
_check_search_dependencies()
|
107
|
+
|
108
|
+
class DocumentProcessor:
|
109
|
+
def __init__(self, *args, **kwargs):
|
110
|
+
_check_search_dependencies()
|
111
|
+
|
112
|
+
class IndexBuilder:
|
113
|
+
def __init__(self, *args, **kwargs):
|
114
|
+
_check_search_dependencies()
|
115
|
+
|
116
|
+
class SearchEngine:
|
117
|
+
def __init__(self, *args, **kwargs):
|
118
|
+
_check_search_dependencies()
|
119
|
+
|
120
|
+
class SearchService:
|
121
|
+
def __init__(self, *args, **kwargs):
|
122
|
+
_check_search_dependencies()
|
123
|
+
|
124
|
+
__all__ = [
|
125
|
+
'preprocess_query',
|
126
|
+
'preprocess_document_content',
|
127
|
+
'DocumentProcessor',
|
128
|
+
'IndexBuilder',
|
129
|
+
'SearchEngine',
|
130
|
+
'SearchService'
|
131
|
+
]
|