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.
Files changed (46) hide show
  1. signalwire_agents/__init__.py +43 -4
  2. signalwire_agents/agent_server.py +268 -15
  3. signalwire_agents/cli/__init__.py +9 -0
  4. signalwire_agents/cli/build_search.py +457 -0
  5. signalwire_agents/cli/test_swaig.py +2609 -0
  6. signalwire_agents/core/agent_base.py +691 -82
  7. signalwire_agents/core/contexts.py +289 -0
  8. signalwire_agents/core/data_map.py +499 -0
  9. signalwire_agents/core/function_result.py +57 -10
  10. signalwire_agents/core/logging_config.py +232 -0
  11. signalwire_agents/core/skill_base.py +27 -37
  12. signalwire_agents/core/skill_manager.py +89 -23
  13. signalwire_agents/core/swaig_function.py +13 -1
  14. signalwire_agents/core/swml_handler.py +37 -13
  15. signalwire_agents/core/swml_service.py +37 -28
  16. signalwire_agents/search/__init__.py +131 -0
  17. signalwire_agents/search/document_processor.py +764 -0
  18. signalwire_agents/search/index_builder.py +534 -0
  19. signalwire_agents/search/query_processor.py +371 -0
  20. signalwire_agents/search/search_engine.py +383 -0
  21. signalwire_agents/search/search_service.py +251 -0
  22. signalwire_agents/skills/datasphere/__init__.py +12 -0
  23. signalwire_agents/skills/datasphere/skill.py +229 -0
  24. signalwire_agents/skills/datasphere_serverless/__init__.py +1 -0
  25. signalwire_agents/skills/datasphere_serverless/skill.py +156 -0
  26. signalwire_agents/skills/datetime/skill.py +9 -5
  27. signalwire_agents/skills/joke/__init__.py +1 -0
  28. signalwire_agents/skills/joke/skill.py +88 -0
  29. signalwire_agents/skills/math/skill.py +9 -6
  30. signalwire_agents/skills/native_vector_search/__init__.py +1 -0
  31. signalwire_agents/skills/native_vector_search/skill.py +352 -0
  32. signalwire_agents/skills/registry.py +10 -4
  33. signalwire_agents/skills/web_search/skill.py +57 -21
  34. signalwire_agents/skills/wikipedia/__init__.py +9 -0
  35. signalwire_agents/skills/wikipedia/skill.py +180 -0
  36. signalwire_agents/utils/__init__.py +14 -0
  37. signalwire_agents/utils/schema_utils.py +111 -44
  38. signalwire_agents-0.1.12.dist-info/METADATA +863 -0
  39. signalwire_agents-0.1.12.dist-info/RECORD +67 -0
  40. {signalwire_agents-0.1.10.dist-info → signalwire_agents-0.1.12.dist-info}/WHEEL +1 -1
  41. signalwire_agents-0.1.12.dist-info/entry_points.txt +3 -0
  42. signalwire_agents-0.1.10.dist-info/METADATA +0 -319
  43. signalwire_agents-0.1.10.dist-info/RECORD +0 -44
  44. {signalwire_agents-0.1.10.data → signalwire_agents-0.1.12.data}/data/schema.json +0 -0
  45. {signalwire_agents-0.1.10.dist-info → signalwire_agents-0.1.12.dist-info}/licenses/LICENSE +0 -0
  46. {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
- SWMLService - Base class for SWML document creation and serving
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="iso"),
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.processors.JSONRenderer()
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
- print("Creating auto-vivified methods for all verbs")
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
- print(f"Found {len(verb_names)} verbs in schema")
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
- print(f"Skipping {verb_name} - already has a method")
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
- print(f"Executing auto-vivified method for 'sleep'")
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
- print(f"Created special method for {verb_name}")
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
- print(f"Executing auto-vivified method for '{name}'")
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
- print(f"Created method for {verb_name}")
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
- print(f"DEBUG: __getattr__ called for '{name}'")
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
- print(f"DEBUG: '{name}' is a valid verb")
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
- print(f"DEBUG: Using cached method for '{name}'")
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
- print(f"DEBUG: Executing auto-vivified method for 'sleep'")
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
- print(f"DEBUG: Caching special method for '{name}'")
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
- print(f"DEBUG: Executing auto-vivified method for '{name}'")
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
- print(f"DEBUG: Caching method for '{name}'")
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
- print(f"DEBUG: {msg}")
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
- print(f"Catch-all received: '{full_path}', route: '{route_path}'")
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
- print(f"No match for path: '{full_path}'")
862
+ self.log.debug("no_route_match", path=full_path)
855
863
  return {"error": "Path not found"}
856
864
 
857
- # Print all routes for debugging
858
- print(f"All routes for {self.name}:")
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
- print(f" {route.path}")
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
- # Print the auth credentials
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 print additional info
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
+ ]