signalwire-agents 0.1.11__py3-none-any.whl → 0.1.13__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 (26) hide show
  1. signalwire_agents/__init__.py +5 -1
  2. signalwire_agents/agent_server.py +222 -13
  3. signalwire_agents/cli/build_search.py +457 -0
  4. signalwire_agents/cli/test_swaig.py +177 -113
  5. signalwire_agents/core/agent_base.py +1 -3
  6. signalwire_agents/core/logging_config.py +232 -0
  7. signalwire_agents/core/swaig_function.py +2 -3
  8. signalwire_agents/core/swml_renderer.py +43 -28
  9. signalwire_agents/search/__init__.py +131 -0
  10. signalwire_agents/search/document_processor.py +764 -0
  11. signalwire_agents/search/index_builder.py +534 -0
  12. signalwire_agents/search/query_processor.py +371 -0
  13. signalwire_agents/search/search_engine.py +383 -0
  14. signalwire_agents/search/search_service.py +251 -0
  15. signalwire_agents/skills/native_vector_search/__init__.py +1 -0
  16. signalwire_agents/skills/native_vector_search/skill.py +352 -0
  17. signalwire_agents/skills/registry.py +2 -15
  18. signalwire_agents/utils/__init__.py +13 -1
  19. {signalwire_agents-0.1.11.dist-info → signalwire_agents-0.1.13.dist-info}/METADATA +110 -3
  20. {signalwire_agents-0.1.11.dist-info → signalwire_agents-0.1.13.dist-info}/RECORD +25 -16
  21. {signalwire_agents-0.1.11.dist-info → signalwire_agents-0.1.13.dist-info}/entry_points.txt +1 -0
  22. signalwire_agents/utils/serverless.py +0 -38
  23. {signalwire_agents-0.1.11.data → signalwire_agents-0.1.13.data}/data/schema.json +0 -0
  24. {signalwire_agents-0.1.11.dist-info → signalwire_agents-0.1.13.dist-info}/WHEEL +0 -0
  25. {signalwire_agents-0.1.11.dist-info → signalwire_agents-0.1.13.dist-info}/licenses/LICENSE +0 -0
  26. {signalwire_agents-0.1.11.dist-info → signalwire_agents-0.1.13.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,352 @@
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
+ import os
11
+ import tempfile
12
+ import shutil
13
+ from typing import Dict, Any, Optional, List
14
+ from pathlib import Path
15
+
16
+ from signalwire_agents.core.skill_base import SkillBase
17
+ from signalwire_agents.core.function_result import SwaigFunctionResult
18
+
19
+ class NativeVectorSearchSkill(SkillBase):
20
+ """Native vector search capability using local document indexes or remote search servers"""
21
+
22
+ SKILL_NAME = "native_vector_search"
23
+ SKILL_DESCRIPTION = "Search document indexes using vector similarity and keyword search (local or remote)"
24
+ SKILL_VERSION = "1.0.0"
25
+ REQUIRED_PACKAGES = [] # Optional packages checked at runtime
26
+ REQUIRED_ENV_VARS = [] # No required env vars since all config comes from params
27
+
28
+ # Enable multiple instances support
29
+ SUPPORTS_MULTIPLE_INSTANCES = True
30
+
31
+ def get_instance_key(self) -> str:
32
+ """
33
+ Get the key used to track this skill instance
34
+
35
+ For native vector search, we use the tool name to differentiate instances
36
+ """
37
+ tool_name = self.params.get('tool_name', 'search_knowledge')
38
+ index_file = self.params.get('index_file', 'default')
39
+ return f"{self.SKILL_NAME}_{tool_name}_{index_file}"
40
+
41
+ def setup(self) -> bool:
42
+ """Setup the native vector search skill"""
43
+
44
+ # Check if search functionality is available
45
+ try:
46
+ from signalwire_agents.search import IndexBuilder, SearchEngine
47
+ from signalwire_agents.search.query_processor import preprocess_query
48
+ self.search_available = True
49
+ except ImportError as e:
50
+ self.search_available = False
51
+ self.import_error = str(e)
52
+ self.logger.warning(f"Search dependencies not available: {e}")
53
+ # Don't fail setup - we'll provide helpful error messages at runtime
54
+
55
+ # Get configuration
56
+ self.tool_name = self.params.get('tool_name', 'search_knowledge')
57
+ self.index_file = self.params.get('index_file')
58
+ self.build_index = self.params.get('build_index', False)
59
+ self.source_dir = self.params.get('source_dir')
60
+ self.count = self.params.get('count', 5)
61
+ self.distance_threshold = self.params.get('distance_threshold', 0.0)
62
+ self.tags = self.params.get('tags', [])
63
+ self.no_results_message = self.params.get(
64
+ 'no_results_message',
65
+ "No information found for '{query}'"
66
+ )
67
+ self.response_prefix = self.params.get('response_prefix', '')
68
+ self.response_postfix = self.params.get('response_postfix', '')
69
+
70
+ # Remote search server configuration
71
+ self.remote_url = self.params.get('remote_url') # e.g., "http://localhost:8001"
72
+ self.index_name = self.params.get('index_name', 'default') # For remote searches
73
+
74
+ # SWAIG fields for function fillers
75
+ self.swaig_fields = self.params.get('swaig_fields', {})
76
+
77
+ # NLP backend configuration
78
+ self.nlp_backend = self.params.get('nlp_backend', 'nltk') # Default to faster NLTK
79
+ if self.nlp_backend not in ['nltk', 'spacy']:
80
+ self.logger.warning(f"Invalid nlp_backend '{self.nlp_backend}', using 'nltk'")
81
+ self.nlp_backend = 'nltk'
82
+
83
+ # Auto-build index if requested and search is available
84
+ if self.build_index and self.source_dir and self.search_available:
85
+ if not self.index_file:
86
+ # Generate index filename from source directory
87
+ source_name = Path(self.source_dir).name
88
+ self.index_file = f"{source_name}.swsearch"
89
+
90
+ # Build index if it doesn't exist
91
+ if not os.path.exists(self.index_file):
92
+ try:
93
+ self.logger.info(f"Building search index from {self.source_dir}...")
94
+ from signalwire_agents.search import IndexBuilder
95
+
96
+ builder = IndexBuilder(verbose=self.params.get('verbose', False))
97
+ builder.build_index(
98
+ source_dir=self.source_dir,
99
+ output_file=self.index_file,
100
+ file_types=self.params.get('file_types', ['md', 'txt']),
101
+ exclude_patterns=self.params.get('exclude_patterns'),
102
+ tags=self.params.get('global_tags')
103
+ )
104
+ self.logger.info(f"Search index created: {self.index_file}")
105
+ except Exception as e:
106
+ self.logger.error(f"Failed to build search index: {e}")
107
+ self.search_available = False
108
+
109
+ # Initialize search engine
110
+ self.search_engine = None
111
+ if self.search_available and self.index_file and os.path.exists(self.index_file):
112
+ try:
113
+ from signalwire_agents.search import SearchEngine
114
+ self.search_engine = SearchEngine(self.index_file)
115
+ except Exception as e:
116
+ self.logger.error(f"Failed to load search index {self.index_file}: {e}")
117
+ self.search_available = False
118
+
119
+ # Check if we should use remote search mode
120
+ self.use_remote = bool(self.remote_url)
121
+ if self.use_remote:
122
+ self.logger.info(f"Using remote search server: {self.remote_url}")
123
+ # Test remote connection
124
+ try:
125
+ import requests
126
+ response = requests.get(f"{self.remote_url}/health", timeout=5)
127
+ if response.status_code == 200:
128
+ self.logger.info("Remote search server is available")
129
+ self.search_available = True
130
+ else:
131
+ self.logger.error(f"Remote search server returned status {response.status_code}")
132
+ self.search_available = False
133
+ except Exception as e:
134
+ self.logger.error(f"Failed to connect to remote search server: {e}")
135
+ self.search_available = False
136
+
137
+ return True
138
+
139
+ def register_tools(self) -> None:
140
+ """Register native vector search tool with the agent"""
141
+
142
+ # Get description from params or use default
143
+ description = self.params.get(
144
+ 'description',
145
+ 'Search the local knowledge base for information'
146
+ )
147
+
148
+ self.agent.define_tool(
149
+ name=self.tool_name,
150
+ description=description,
151
+ parameters={
152
+ "query": {
153
+ "type": "string",
154
+ "description": "Search query or question"
155
+ },
156
+ "count": {
157
+ "type": "integer",
158
+ "description": f"Number of results to return (default: {self.count})",
159
+ "default": self.count
160
+ }
161
+ },
162
+ handler=self._search_handler,
163
+ **self.swaig_fields
164
+ )
165
+
166
+ def _search_handler(self, args, raw_data):
167
+ """Handle search requests"""
168
+
169
+ if not self.search_available:
170
+ return SwaigFunctionResult(
171
+ f"Search functionality is not available. {getattr(self, 'import_error', '')}\n"
172
+ f"Install with: pip install signalwire-agents[search]"
173
+ )
174
+
175
+ if not self.use_remote and not self.search_engine:
176
+ return SwaigFunctionResult(
177
+ f"Search index not available. "
178
+ f"{'Index file not found: ' + (self.index_file or 'not specified') if self.index_file else 'No index file configured'}"
179
+ )
180
+
181
+ query = args.get('query', '').strip()
182
+ if not query:
183
+ return SwaigFunctionResult("Please provide a search query.")
184
+
185
+ count = args.get('count', self.count)
186
+
187
+ try:
188
+ # Preprocess the query
189
+ from signalwire_agents.search.query_processor import preprocess_query
190
+ enhanced = preprocess_query(query, language='en', vector=True, nlp_backend=self.nlp_backend)
191
+
192
+ # Perform search (local or remote)
193
+ if self.use_remote:
194
+ results = self._search_remote(query, enhanced, count)
195
+ else:
196
+ results = self.search_engine.search(
197
+ query_vector=enhanced.get('vector', []),
198
+ enhanced_text=enhanced['enhanced_text'],
199
+ count=count,
200
+ distance_threshold=self.distance_threshold,
201
+ tags=self.tags
202
+ )
203
+
204
+ if not results:
205
+ no_results_msg = self.no_results_message.format(query=query)
206
+ if self.response_prefix:
207
+ no_results_msg = f"{self.response_prefix} {no_results_msg}"
208
+ if self.response_postfix:
209
+ no_results_msg = f"{no_results_msg} {self.response_postfix}"
210
+ return SwaigFunctionResult(no_results_msg)
211
+
212
+ # Format results
213
+ response_parts = []
214
+
215
+ # Add response prefix if configured
216
+ if self.response_prefix:
217
+ response_parts.append(self.response_prefix)
218
+
219
+ response_parts.append(f"Found {len(results)} relevant results for '{query}':\n")
220
+
221
+ for i, result in enumerate(results, 1):
222
+ filename = result['metadata']['filename']
223
+ section = result['metadata'].get('section', '')
224
+ score = result['score']
225
+ content = result['content']
226
+
227
+ result_text = f"**Result {i}** (from {filename}"
228
+ if section:
229
+ result_text += f", section: {section}"
230
+ result_text += f", relevance: {score:.2f})\n{content}\n"
231
+
232
+ response_parts.append(result_text)
233
+
234
+ # Add response postfix if configured
235
+ if self.response_postfix:
236
+ response_parts.append(self.response_postfix)
237
+
238
+ return SwaigFunctionResult("\n".join(response_parts))
239
+
240
+ except Exception as e:
241
+ return SwaigFunctionResult(f"Search error: {str(e)}")
242
+
243
+ def _search_remote(self, query: str, enhanced: dict, count: int) -> list:
244
+ """Perform search using remote search server"""
245
+ try:
246
+ import requests
247
+
248
+ search_request = {
249
+ "query": query,
250
+ "index_name": self.index_name,
251
+ "count": count,
252
+ "distance": self.distance_threshold,
253
+ "tags": self.tags,
254
+ "language": "en"
255
+ }
256
+
257
+ response = requests.post(
258
+ f"{self.remote_url}/search",
259
+ json=search_request,
260
+ timeout=30
261
+ )
262
+
263
+ if response.status_code == 200:
264
+ data = response.json()
265
+ # Convert remote response format to local format
266
+ results = []
267
+ for result in data.get('results', []):
268
+ results.append({
269
+ 'content': result['content'],
270
+ 'score': result['score'],
271
+ 'metadata': result['metadata']
272
+ })
273
+ return results
274
+ else:
275
+ self.logger.error(f"Remote search failed with status {response.status_code}: {response.text}")
276
+ return []
277
+
278
+ except Exception as e:
279
+ self.logger.error(f"Remote search error: {e}")
280
+ return []
281
+
282
+ def get_hints(self) -> List[str]:
283
+ """Return speech recognition hints for this skill"""
284
+ hints = [
285
+ "search",
286
+ "find",
287
+ "look up",
288
+ "documentation",
289
+ "knowledge base"
290
+ ]
291
+
292
+ # Add custom hints from params
293
+ custom_hints = self.params.get('hints', [])
294
+ hints.extend(custom_hints)
295
+
296
+ return hints
297
+
298
+ def get_global_data(self) -> Dict[str, Any]:
299
+ """Return data to add to agent's global context"""
300
+ global_data = {}
301
+
302
+ if self.search_engine:
303
+ try:
304
+ stats = self.search_engine.get_stats()
305
+ global_data['search_stats'] = stats
306
+ except:
307
+ pass
308
+
309
+ return global_data
310
+
311
+ def get_prompt_sections(self) -> List[Dict[str, Any]]:
312
+ """Return prompt sections to add to agent"""
313
+ search_mode = "remote search server" if self.use_remote else "local document indexes"
314
+ return [
315
+ {
316
+ "title": "Document Search",
317
+ "body": f"You can search {search_mode} using the {self.tool_name} tool.",
318
+ "bullets": [
319
+ f"Use the {self.tool_name} tool when users ask questions about topics that might be in the indexed documents",
320
+ "Search for relevant information using clear, specific queries",
321
+ "Provide helpful summaries of the search results",
322
+ "If no results are found, suggest the user try rephrasing their question or ask about different topics"
323
+ ]
324
+ }
325
+ ]
326
+
327
+ def _add_prompt_section(self, agent):
328
+ """Add prompt section to agent (called during skill loading)"""
329
+ try:
330
+ agent.prompt_add_section(
331
+ title="Local Document Search",
332
+ body=f"You can search local document indexes using the {self.tool_name} tool.",
333
+ bullets=[
334
+ f"Use the {self.tool_name} tool when users ask questions about topics that might be in the indexed documents",
335
+ "Search for relevant information using clear, specific queries",
336
+ "Provide helpful summaries of the search results",
337
+ "If no results are found, suggest the user try rephrasing their question or ask about different topics"
338
+ ]
339
+ )
340
+ except Exception as e:
341
+ self.logger.error(f"Failed to add prompt section: {e}")
342
+ # Continue without the prompt section
343
+
344
+ def cleanup(self) -> None:
345
+ """Cleanup when skill is removed or agent shuts down"""
346
+ # Clean up any temporary files if we created them
347
+ if hasattr(self, '_temp_dirs'):
348
+ for temp_dir in self._temp_dirs:
349
+ try:
350
+ shutil.rmtree(temp_dir)
351
+ except:
352
+ pass
@@ -15,28 +15,15 @@ import sys
15
15
  from typing import Dict, List, Type, Optional
16
16
  from pathlib import Path
17
17
 
18
- try:
19
- import structlog
20
- logger_available = True
21
- except ImportError:
22
- import logging
23
- logger_available = False
24
-
25
18
  from signalwire_agents.core.skill_base import SkillBase
19
+ from signalwire_agents.core.logging_config import get_logger
26
20
 
27
21
  class SkillRegistry:
28
22
  """Global registry for discovering and managing skills"""
29
23
 
30
24
  def __init__(self):
31
25
  self._skills: Dict[str, Type[SkillBase]] = {}
32
-
33
- # Use structlog if available, fallback to logging
34
- if logger_available:
35
- self.logger = structlog.get_logger("skill_registry")
36
- else:
37
- import logging
38
- self.logger = logging.getLogger("skill_registry")
39
-
26
+ self.logger = get_logger("skill_registry")
40
27
  self._discovered = False
41
28
 
42
29
  def discover_skills(self) -> None:
@@ -7,5 +7,17 @@ Licensed under the MIT License.
7
7
  See LICENSE file in the project root for full license information.
8
8
  """
9
9
 
10
- from .serverless import get_execution_mode, is_serverless_mode
10
+ from .schema_utils import SchemaUtils
11
+ from signalwire_agents.core.logging_config import get_execution_mode
12
+
13
+ def is_serverless_mode() -> bool:
14
+ """
15
+ Check if running in any serverless environment.
16
+
17
+ Returns:
18
+ bool: True if in serverless mode, False if in server mode
19
+ """
20
+ return get_execution_mode() != 'server'
21
+
22
+ __all__ = ["SchemaUtils", "get_execution_mode", "is_serverless_mode"]
11
23
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: signalwire_agents
3
- Version: 0.1.11
3
+ Version: 0.1.13
4
4
  Summary: SignalWire AI Agents SDK
5
5
  Author-email: SignalWire Team <info@signalwire.com>
6
6
  Project-URL: Homepage, https://github.com/signalwire/signalwire-ai-agents
@@ -26,6 +26,42 @@ Requires-Dist: structlog==25.3.0
26
26
  Requires-Dist: uvicorn==0.34.2
27
27
  Requires-Dist: beautifulsoup4==4.12.3
28
28
  Requires-Dist: pytz==2023.3
29
+ Provides-Extra: search
30
+ Requires-Dist: sentence-transformers>=2.2.0; extra == "search"
31
+ Requires-Dist: scikit-learn>=1.3.0; extra == "search"
32
+ Requires-Dist: nltk>=3.8; extra == "search"
33
+ Requires-Dist: numpy>=1.24.0; extra == "search"
34
+ Provides-Extra: search-full
35
+ Requires-Dist: sentence-transformers>=2.2.0; extra == "search-full"
36
+ Requires-Dist: scikit-learn>=1.3.0; extra == "search-full"
37
+ Requires-Dist: nltk>=3.8; extra == "search-full"
38
+ Requires-Dist: numpy>=1.24.0; extra == "search-full"
39
+ Requires-Dist: pdfplumber>=0.9.0; extra == "search-full"
40
+ Requires-Dist: python-docx>=0.8.11; extra == "search-full"
41
+ Requires-Dist: markdown>=3.4.0; extra == "search-full"
42
+ Requires-Dist: striprtf>=0.0.26; extra == "search-full"
43
+ Requires-Dist: openpyxl>=3.1.0; extra == "search-full"
44
+ Requires-Dist: python-pptx>=0.6.21; extra == "search-full"
45
+ Requires-Dist: python-magic>=0.4.27; extra == "search-full"
46
+ Provides-Extra: search-nlp
47
+ Requires-Dist: sentence-transformers>=2.2.0; extra == "search-nlp"
48
+ Requires-Dist: scikit-learn>=1.3.0; extra == "search-nlp"
49
+ Requires-Dist: nltk>=3.8; extra == "search-nlp"
50
+ Requires-Dist: numpy>=1.24.0; extra == "search-nlp"
51
+ Requires-Dist: spacy>=3.6.0; extra == "search-nlp"
52
+ Provides-Extra: search-all
53
+ Requires-Dist: sentence-transformers>=2.2.0; extra == "search-all"
54
+ Requires-Dist: scikit-learn>=1.3.0; extra == "search-all"
55
+ Requires-Dist: nltk>=3.8; extra == "search-all"
56
+ Requires-Dist: numpy>=1.24.0; extra == "search-all"
57
+ Requires-Dist: spacy>=3.6.0; extra == "search-all"
58
+ Requires-Dist: pdfplumber>=0.9.0; extra == "search-all"
59
+ Requires-Dist: python-docx>=0.8.11; extra == "search-all"
60
+ Requires-Dist: markdown>=3.4.0; extra == "search-all"
61
+ Requires-Dist: striprtf>=0.0.26; extra == "search-all"
62
+ Requires-Dist: openpyxl>=3.1.0; extra == "search-all"
63
+ Requires-Dist: python-pptx>=0.6.21; extra == "search-all"
64
+ Requires-Dist: python-magic>=0.4.27; extra == "search-all"
29
65
  Dynamic: license-file
30
66
 
31
67
  # SignalWire AI Agent SDK
@@ -45,6 +81,7 @@ A Python SDK for creating, hosting, and securing SignalWire AI agents as microse
45
81
  - **Prefab Archetypes**: Ready-to-use agent types for common scenarios
46
82
  - **Multi-Agent Support**: Host multiple agents on a single server
47
83
  - **Modular Skills System**: Add capabilities to agents with simple one-liner calls
84
+ - **Local Search System**: Offline document search with vector similarity and keyword search
48
85
 
49
86
  ## Skills System
50
87
 
@@ -124,6 +161,7 @@ agent.serve()
124
161
  - **datetime**: Current date and time with timezone support
125
162
  - **math**: Safe mathematical expression evaluation
126
163
  - **datasphere**: SignalWire DataSphere knowledge search (supports multiple instances)
164
+ - **native_vector_search**: Offline document search with vector similarity and keyword search
127
165
 
128
166
  ### Benefits
129
167
 
@@ -424,10 +462,73 @@ For detailed documentation and advanced examples, see [Contexts and Steps Guide]
424
462
 
425
463
  ## Installation
426
464
 
465
+ ### Basic Installation
466
+
427
467
  ```bash
428
468
  pip install signalwire-agents
429
469
  ```
430
470
 
471
+ ### Optional Search Functionality
472
+
473
+ The SDK includes optional local search capabilities that can be installed separately to avoid adding large dependencies to the base installation:
474
+
475
+ #### Search Installation Options
476
+
477
+ ```bash
478
+ # Basic search (vector search + keyword search)
479
+ pip install signalwire-agents[search]
480
+
481
+ # Full search with document processing (PDF, DOCX, etc.)
482
+ pip install signalwire-agents[search-full]
483
+
484
+ # Advanced NLP features (includes spaCy)
485
+ pip install signalwire-agents[search-nlp]
486
+
487
+ # All search features
488
+ pip install signalwire-agents[search-all]
489
+ ```
490
+
491
+ #### What Each Option Includes
492
+
493
+ | Option | Size | Features |
494
+ |--------|------|----------|
495
+ | `search` | ~500MB | Vector embeddings, keyword search, basic text processing |
496
+ | `search-full` | ~600MB | + PDF, DOCX, Excel, PowerPoint, HTML, Markdown processing |
497
+ | `search-nlp` | ~600MB | + Advanced spaCy NLP features |
498
+ | `search-all` | ~700MB | All search features combined |
499
+
500
+ #### Search Features
501
+
502
+ - **Local/Offline Search**: No external API dependencies
503
+ - **Hybrid Search**: Vector similarity + keyword search
504
+ - **Smart Document Processing**: Markdown, Python, PDF, DOCX, etc.
505
+ - **Multiple Languages**: English, Spanish, with extensible framework
506
+ - **CLI Tools**: Build search indexes from document directories
507
+ - **HTTP API**: Standalone or embedded search service
508
+
509
+ #### Usage Example
510
+
511
+ ```python
512
+ # Only available with search extras installed
513
+ from signalwire_agents.search import IndexBuilder, SearchEngine
514
+
515
+ # Build search index
516
+ builder = IndexBuilder()
517
+ builder.build_index(
518
+ source_dir="./docs",
519
+ output_file="knowledge.swsearch",
520
+ file_types=['md', 'txt', 'pdf']
521
+ )
522
+
523
+ # Search documents
524
+ engine = SearchEngine("knowledge.swsearch")
525
+ results = engine.search(
526
+ query_vector=embeddings,
527
+ enhanced_text="search query",
528
+ count=5
529
+ )
530
+ ```
531
+
431
532
  ## Quick Start
432
533
 
433
534
  ```python
@@ -634,9 +735,12 @@ To enable HTTPS directly (without a reverse proxy), set `SWML_SSL_ENABLED` to "t
634
735
 
635
736
  ## Testing
636
737
 
637
- The SDK includes a powerful `swaig-test` CLI tool for comprehensive local testing and serverless environment simulation.
738
+ The SDK includes powerful CLI tools for development and testing:
739
+
740
+ - **`swaig-test`**: Comprehensive local testing and serverless environment simulation
741
+ - **`sw-search`**: Build local search indexes from document directories and search within them
638
742
 
639
- ### Local Testing
743
+ ### Local Testing with swaig-test
640
744
 
641
745
  Test your agents locally without deployment:
642
746
 
@@ -748,6 +852,9 @@ The package includes comprehensive documentation in the `docs/` directory:
748
852
  - [Agent Guide](docs/agent_guide.md) - Detailed guide to creating and customizing agents, including dynamic configuration
749
853
  - [Architecture](docs/architecture.md) - Overview of the SDK architecture and core concepts
750
854
  - [SWML Service Guide](docs/swml_service_guide.md) - Guide to the underlying SWML service
855
+ - [Local Search System](docs/search-system.md) - Complete guide to the local search system with vector similarity and keyword search
856
+ - [Skills System](docs/skills_system.md) - Detailed documentation on the modular skills system
857
+ - [CLI Tools](docs/cli.md) - Command-line interface tools for development and testing
751
858
 
752
859
  These documents provide in-depth explanations of the features, APIs, and usage patterns.
753
860
 
@@ -1,20 +1,22 @@
1
- signalwire_agents/__init__.py,sha256=mEPuH4ErsZlh9JLfubfSeGV0I4KuoQpWSJRRP2Sn3-8,1945
2
- signalwire_agents/agent_server.py,sha256=izHQGIGnThVhBNtcDKEakS54uew4rn82bnwKIZL8M34,15519
1
+ signalwire_agents/__init__.py,sha256=IY7WpXHkFSNDItkEDkJpsfz3a4Bsw5aotPRtrHqJne4,2093
2
+ signalwire_agents/agent_server.py,sha256=3Or8rIMAqW750V-XitBUMgOpW9BAIXmKXoGq7LkejAA,24988
3
3
  signalwire_agents/schema.json,sha256=M8Mn6pQda2P9jhbmkALrLr1wt-fRuhYRqdmEi9Rbhqk,178075
4
4
  signalwire_agents/cli/__init__.py,sha256=Iy2BfWDWBEZoA1cyHTDsooBSVMx4vH5Ddhr3sEuFe8c,197
5
- signalwire_agents/cli/test_swaig.py,sha256=PZYb87-lzdi_Q9qIfEjUG989tRxme9B-6kWSKfFujLw,96208
5
+ signalwire_agents/cli/build_search.py,sha256=FXEgZEDn_2-7sJGQ07LYUxT9MGySUN-kpUbCCPBYxWk,15966
6
+ signalwire_agents/cli/test_swaig.py,sha256=tBl2r-MqxKDiAVMfaDOntR9OMFE3I0ff51oGMlRQsl8,100237
6
7
  signalwire_agents/core/__init__.py,sha256=mVDLbpq1pg_WwiqsQR28NNZwJ6-VUXFIfg-vN7pk0ew,806
7
- signalwire_agents/core/agent_base.py,sha256=aFrF2oZOwWogLbJwF3Ezit_auliurW5U5pPqDYxNtdE,141709
8
+ signalwire_agents/core/agent_base.py,sha256=uZoHeSivokoBL67CiseKur4L9JCq0Pzu-BDZ9hiNOYE,141686
8
9
  signalwire_agents/core/contexts.py,sha256=h7hra4xoiKAUdVyJhcKggl8X9EoqwTVWBmNMp-sEsuc,9598
9
10
  signalwire_agents/core/data_map.py,sha256=U-HLEZQomWf-UI0-nLAE8g1oyRdE5bU_WxQpboI2YI4,17695
10
11
  signalwire_agents/core/function_result.py,sha256=SP46vPAliEzwh3JeeSxmLD_f7_ixRxMBtpSl3t7jieU,45370
12
+ signalwire_agents/core/logging_config.py,sha256=ATO_Zo8Kc6Ts1wU39ewliF_dKAy6M91I9q-KiilsLyk,7507
11
13
  signalwire_agents/core/pom_builder.py,sha256=ywuiIfP8BeLBPo_G4X1teZlG6zTCMkW71CZnmyoDTAQ,6636
12
14
  signalwire_agents/core/skill_base.py,sha256=lOpVTLhD9NjStF7Lxh6bAQUGa3DpNYV4agXJRakRuX0,4258
13
15
  signalwire_agents/core/skill_manager.py,sha256=6vAzkWYeycilX-5T1B039sf8s11BfRh6ASstt70wWsw,8074
14
- signalwire_agents/core/swaig_function.py,sha256=OG76Hrtbc8H1mPR5opTmNUEOo0yOMbpbMfZMZjG69Ek,6874
16
+ signalwire_agents/core/swaig_function.py,sha256=3xJrrQVxCZX-DssLkdjaui_psTUzahkzAsQ1EyRVMFk,6837
15
17
  signalwire_agents/core/swml_builder.py,sha256=Q1ikU9pedgjW888mjbqDFv-jMDvDZ-tZgfyMfu4qQN0,6719
16
18
  signalwire_agents/core/swml_handler.py,sha256=C8NvMpNdFg9UiEzPwMmMXMn8X6w10IShKjBJ8VSITBk,8189
17
- signalwire_agents/core/swml_renderer.py,sha256=iobMWWoBR7dkHndI3Qlwf4C0fg2p7DmAU2Rb7ZmmLhA,13891
19
+ signalwire_agents/core/swml_renderer.py,sha256=u6HnbOC0NUnPpr6uz0FkXNEqcTF2OjWDui6gceD1Bhs,14773
18
20
  signalwire_agents/core/swml_service.py,sha256=3yhYfowgdKphjf2OLJYuaPsE6Paewe6rAO3e7F7NPic,49797
19
21
  signalwire_agents/core/security/__init__.py,sha256=4Mr7baQ_xR_hfJ72YxQRAT_GFa663YjFX_PumJ35Xds,191
20
22
  signalwire_agents/core/security/session_manager.py,sha256=s5hXYcFnrsYFoyo-zcN7EJy-wInZQI_cWTBHX9MxHR4,9164
@@ -27,8 +29,14 @@ signalwire_agents/prefabs/faq_bot.py,sha256=NrUn5AGmtdzYTyxTHPt8BZ14ZN1sh4xKA2SV
27
29
  signalwire_agents/prefabs/info_gatherer.py,sha256=dr9UUgNGX7MIKdCMth3jDVLf6OrHov5G6_zIuNvnrOY,15468
28
30
  signalwire_agents/prefabs/receptionist.py,sha256=8EyQ6M0Egs3g7KKWukHFiO9UPoVUxT4MLkvyT3V8o64,10585
29
31
  signalwire_agents/prefabs/survey.py,sha256=IIIQfgvMlfVNjEEEdWUn4lAJqVsCDlBsIAkOJ1ckyAE,14796
32
+ signalwire_agents/search/__init__.py,sha256=x7saU_MDbhoOIzcvCT1-gnqyH2rrMpzB4ZUqk-av-lI,3958
33
+ signalwire_agents/search/document_processor.py,sha256=uqBvL8EGCou81ack7T49H0MNC7NkIRbciPtQecb1Jjo,31204
34
+ signalwire_agents/search/index_builder.py,sha256=wAfDtK4YgnbHiG-syvEzbNGUHN92PMy1gMnhG_rXAUM,21143
35
+ signalwire_agents/search/query_processor.py,sha256=1o8dEHVG52ci1e1PQxm5a0_1FV4zdqYBex_qcyJabRs,13926
36
+ signalwire_agents/search/search_engine.py,sha256=KFF33aPsBdwb2N1aTJmAA0xo3KPANoXjwV53uSxih9o,14798
37
+ signalwire_agents/search/search_service.py,sha256=Src_REgjkcddHBDCaLv2BMJnDSxcE4XV_1U8YStJOh8,8719
30
38
  signalwire_agents/skills/__init__.py,sha256=xfxrQ0i-aTRomHiCsqelU4RlNlHPJFPgPu-UBDaBOqA,340
31
- signalwire_agents/skills/registry.py,sha256=BBYEh-9j6FCy5RMegsat7wyMnB-Upj3e1HiqU6e4H4k,4109
39
+ signalwire_agents/skills/registry.py,sha256=UrFhZJdc_8Y9k9KCSRZtt7QemafDNtuvyuheAXShjuU,3821
32
40
  signalwire_agents/skills/datasphere/__init__.py,sha256=SJJlmeMSeezjINPgkuWN1XzDPN_Z3GzZ_StzO1BtxQs,257
33
41
  signalwire_agents/skills/datasphere/skill.py,sha256=L6GrGwej3sKPcHljKBNf4it5g4DaGzR18KlQx65_XKg,9598
34
42
  signalwire_agents/skills/datasphere_serverless/__init__.py,sha256=65hu8_0eqiczLSZ-aJgASpMQqTUjzTQUI1fC8GI7qTI,70
@@ -39,20 +47,21 @@ signalwire_agents/skills/joke/__init__.py,sha256=R-iS9UMMvOdpkxL9aooVik16eCddJw1
39
47
  signalwire_agents/skills/joke/skill.py,sha256=AFaf6fMy0sxUPJHvcnf3CWMuPqpJP4ODscUexMadEcU,3381
40
48
  signalwire_agents/skills/math/__init__.py,sha256=lGAFWEmJH2fuwkuZUdDTY5dmucrIwtjfNT8bE2hOSP8,39
41
49
  signalwire_agents/skills/math/skill.py,sha256=5sErd5x1rFHJg2GlmdJB3LvrmvTNOrZsA2jRnG67Zw8,3342
50
+ signalwire_agents/skills/native_vector_search/__init__.py,sha256=buvncVoH5u8MJA0SLlz1JQgIuyBTQW5aql-ydnc7Wh8,29
51
+ signalwire_agents/skills/native_vector_search/skill.py,sha256=jpXah7teIljgTfl2mW73VMlBwEKXS-s4Cws6XCS2JxU,14846
42
52
  signalwire_agents/skills/web_search/__init__.py,sha256=wJlptYDExYw-nxZJVzlTLOgkKkDOLUUt1ZdoLt44ixs,45
43
53
  signalwire_agents/skills/web_search/skill.py,sha256=GkfhG3Vz2HxOv91TvVuA4e_4b5cuswUpnJDLdaZW37k,10304
44
54
  signalwire_agents/skills/wikipedia/__init__.py,sha256=8Db_aE0ly7QoXg7n2RDvCqKupkyR-UYlK9uFUnGNCE8,184
45
55
  signalwire_agents/skills/wikipedia/skill.py,sha256=Q_HWJoG2RkQuZMgWxD9czuzEor79Gy1tjo6ywVzec84,6877
46
- signalwire_agents/utils/__init__.py,sha256=7KO-y334QOjvKFOur3HIi6ruqJJbQdHpLygTMIuRkNk,255
56
+ signalwire_agents/utils/__init__.py,sha256=1KVsHzwgfktSXHe3vqSRGImjtIE58szwD2FHHoFBtvY,601
47
57
  signalwire_agents/utils/pom_utils.py,sha256=4Mr7baQ_xR_hfJ72YxQRAT_GFa663YjFX_PumJ35Xds,191
48
58
  signalwire_agents/utils/schema_utils.py,sha256=i4okv_O9bUApwT_jJf4Yoij3bLCrGrW3DC-vzSy2RuY,16392
49
- signalwire_agents/utils/serverless.py,sha256=5kEdoN8Sngv-x4pyk4wRrn8SafJEm6LWqkM2wfO7v2k,979
50
59
  signalwire_agents/utils/token_generators.py,sha256=4Mr7baQ_xR_hfJ72YxQRAT_GFa663YjFX_PumJ35Xds,191
51
60
  signalwire_agents/utils/validators.py,sha256=4Mr7baQ_xR_hfJ72YxQRAT_GFa663YjFX_PumJ35Xds,191
52
- signalwire_agents-0.1.11.data/data/schema.json,sha256=M8Mn6pQda2P9jhbmkALrLr1wt-fRuhYRqdmEi9Rbhqk,178075
53
- signalwire_agents-0.1.11.dist-info/licenses/LICENSE,sha256=NYvAsB-rTcSvG9cqHt9EUHAWLiA9YzM4Qfz-mPdvDR0,1067
54
- signalwire_agents-0.1.11.dist-info/METADATA,sha256=KULCAfQ9MtDQXG_GhR0gGksIbjaRU5il26oBLduL-Hk,30205
55
- signalwire_agents-0.1.11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
56
- signalwire_agents-0.1.11.dist-info/entry_points.txt,sha256=pPs-ulmQRldQzQh-uscgrXyUOSn9-9ceplwg5awBa5o,84
57
- signalwire_agents-0.1.11.dist-info/top_level.txt,sha256=kDGS6ZYv84K9P5Kyg9_S8P_pbUXoHkso0On_DB5bbWc,18
58
- signalwire_agents-0.1.11.dist-info/RECORD,,
61
+ signalwire_agents-0.1.13.data/data/schema.json,sha256=M8Mn6pQda2P9jhbmkALrLr1wt-fRuhYRqdmEi9Rbhqk,178075
62
+ signalwire_agents-0.1.13.dist-info/licenses/LICENSE,sha256=NYvAsB-rTcSvG9cqHt9EUHAWLiA9YzM4Qfz-mPdvDR0,1067
63
+ signalwire_agents-0.1.13.dist-info/METADATA,sha256=wdck4xWPqwblSSKcHWW52H7HIGta1R9-8_H4IlRzo0k,34572
64
+ signalwire_agents-0.1.13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
65
+ signalwire_agents-0.1.13.dist-info/entry_points.txt,sha256=7RBxC9wwFdsqP7g1F4JzsJ3AIHsjGtb3h0mxEGmANlI,151
66
+ signalwire_agents-0.1.13.dist-info/top_level.txt,sha256=kDGS6ZYv84K9P5Kyg9_S8P_pbUXoHkso0On_DB5bbWc,18
67
+ signalwire_agents-0.1.13.dist-info/RECORD,,
@@ -1,2 +1,3 @@
1
1
  [console_scripts]
2
+ sw-search = signalwire_agents.cli.build_search:console_entry_point
2
3
  swaig-test = signalwire_agents.cli.test_swaig:console_entry_point