signalwire-agents 0.1.6__py3-none-any.whl → 1.0.7__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 (140) hide show
  1. signalwire_agents/__init__.py +130 -4
  2. signalwire_agents/agent_server.py +438 -32
  3. signalwire_agents/agents/bedrock.py +296 -0
  4. signalwire_agents/cli/__init__.py +18 -0
  5. signalwire_agents/cli/build_search.py +1367 -0
  6. signalwire_agents/cli/config.py +80 -0
  7. signalwire_agents/cli/core/__init__.py +10 -0
  8. signalwire_agents/cli/core/agent_loader.py +470 -0
  9. signalwire_agents/cli/core/argparse_helpers.py +179 -0
  10. signalwire_agents/cli/core/dynamic_config.py +71 -0
  11. signalwire_agents/cli/core/service_loader.py +303 -0
  12. signalwire_agents/cli/execution/__init__.py +10 -0
  13. signalwire_agents/cli/execution/datamap_exec.py +446 -0
  14. signalwire_agents/cli/execution/webhook_exec.py +134 -0
  15. signalwire_agents/cli/init_project.py +1225 -0
  16. signalwire_agents/cli/output/__init__.py +10 -0
  17. signalwire_agents/cli/output/output_formatter.py +255 -0
  18. signalwire_agents/cli/output/swml_dump.py +186 -0
  19. signalwire_agents/cli/simulation/__init__.py +10 -0
  20. signalwire_agents/cli/simulation/data_generation.py +374 -0
  21. signalwire_agents/cli/simulation/data_overrides.py +200 -0
  22. signalwire_agents/cli/simulation/mock_env.py +282 -0
  23. signalwire_agents/cli/swaig_test_wrapper.py +52 -0
  24. signalwire_agents/cli/test_swaig.py +809 -0
  25. signalwire_agents/cli/types.py +81 -0
  26. signalwire_agents/core/__init__.py +2 -2
  27. signalwire_agents/core/agent/__init__.py +12 -0
  28. signalwire_agents/core/agent/config/__init__.py +12 -0
  29. signalwire_agents/core/agent/deployment/__init__.py +9 -0
  30. signalwire_agents/core/agent/deployment/handlers/__init__.py +9 -0
  31. signalwire_agents/core/agent/prompt/__init__.py +14 -0
  32. signalwire_agents/core/agent/prompt/manager.py +306 -0
  33. signalwire_agents/core/agent/routing/__init__.py +9 -0
  34. signalwire_agents/core/agent/security/__init__.py +9 -0
  35. signalwire_agents/core/agent/swml/__init__.py +9 -0
  36. signalwire_agents/core/agent/tools/__init__.py +15 -0
  37. signalwire_agents/core/agent/tools/decorator.py +97 -0
  38. signalwire_agents/core/agent/tools/registry.py +210 -0
  39. signalwire_agents/core/agent_base.py +959 -2166
  40. signalwire_agents/core/auth_handler.py +233 -0
  41. signalwire_agents/core/config_loader.py +259 -0
  42. signalwire_agents/core/contexts.py +707 -0
  43. signalwire_agents/core/data_map.py +487 -0
  44. signalwire_agents/core/function_result.py +1150 -1
  45. signalwire_agents/core/logging_config.py +376 -0
  46. signalwire_agents/core/mixins/__init__.py +28 -0
  47. signalwire_agents/core/mixins/ai_config_mixin.py +442 -0
  48. signalwire_agents/core/mixins/auth_mixin.py +287 -0
  49. signalwire_agents/core/mixins/prompt_mixin.py +358 -0
  50. signalwire_agents/core/mixins/serverless_mixin.py +368 -0
  51. signalwire_agents/core/mixins/skill_mixin.py +55 -0
  52. signalwire_agents/core/mixins/state_mixin.py +153 -0
  53. signalwire_agents/core/mixins/tool_mixin.py +230 -0
  54. signalwire_agents/core/mixins/web_mixin.py +1134 -0
  55. signalwire_agents/core/security/session_manager.py +174 -86
  56. signalwire_agents/core/security_config.py +333 -0
  57. signalwire_agents/core/skill_base.py +200 -0
  58. signalwire_agents/core/skill_manager.py +244 -0
  59. signalwire_agents/core/swaig_function.py +33 -9
  60. signalwire_agents/core/swml_builder.py +212 -12
  61. signalwire_agents/core/swml_handler.py +43 -13
  62. signalwire_agents/core/swml_renderer.py +123 -297
  63. signalwire_agents/core/swml_service.py +277 -260
  64. signalwire_agents/prefabs/concierge.py +6 -2
  65. signalwire_agents/prefabs/info_gatherer.py +149 -33
  66. signalwire_agents/prefabs/receptionist.py +14 -22
  67. signalwire_agents/prefabs/survey.py +6 -2
  68. signalwire_agents/schema.json +9218 -5489
  69. signalwire_agents/search/__init__.py +137 -0
  70. signalwire_agents/search/document_processor.py +1223 -0
  71. signalwire_agents/search/index_builder.py +804 -0
  72. signalwire_agents/search/migration.py +418 -0
  73. signalwire_agents/search/models.py +30 -0
  74. signalwire_agents/search/pgvector_backend.py +752 -0
  75. signalwire_agents/search/query_processor.py +502 -0
  76. signalwire_agents/search/search_engine.py +1264 -0
  77. signalwire_agents/search/search_service.py +574 -0
  78. signalwire_agents/skills/README.md +452 -0
  79. signalwire_agents/skills/__init__.py +23 -0
  80. signalwire_agents/skills/api_ninjas_trivia/README.md +215 -0
  81. signalwire_agents/skills/api_ninjas_trivia/__init__.py +12 -0
  82. signalwire_agents/skills/api_ninjas_trivia/skill.py +237 -0
  83. signalwire_agents/skills/datasphere/README.md +210 -0
  84. signalwire_agents/skills/datasphere/__init__.py +12 -0
  85. signalwire_agents/skills/datasphere/skill.py +310 -0
  86. signalwire_agents/skills/datasphere_serverless/README.md +258 -0
  87. signalwire_agents/skills/datasphere_serverless/__init__.py +10 -0
  88. signalwire_agents/skills/datasphere_serverless/skill.py +237 -0
  89. signalwire_agents/skills/datetime/README.md +132 -0
  90. signalwire_agents/skills/datetime/__init__.py +10 -0
  91. signalwire_agents/skills/datetime/skill.py +126 -0
  92. signalwire_agents/skills/joke/README.md +149 -0
  93. signalwire_agents/skills/joke/__init__.py +10 -0
  94. signalwire_agents/skills/joke/skill.py +109 -0
  95. signalwire_agents/skills/math/README.md +161 -0
  96. signalwire_agents/skills/math/__init__.py +10 -0
  97. signalwire_agents/skills/math/skill.py +105 -0
  98. signalwire_agents/skills/mcp_gateway/README.md +230 -0
  99. signalwire_agents/skills/mcp_gateway/__init__.py +10 -0
  100. signalwire_agents/skills/mcp_gateway/skill.py +421 -0
  101. signalwire_agents/skills/native_vector_search/README.md +210 -0
  102. signalwire_agents/skills/native_vector_search/__init__.py +10 -0
  103. signalwire_agents/skills/native_vector_search/skill.py +820 -0
  104. signalwire_agents/skills/play_background_file/README.md +218 -0
  105. signalwire_agents/skills/play_background_file/__init__.py +12 -0
  106. signalwire_agents/skills/play_background_file/skill.py +242 -0
  107. signalwire_agents/skills/registry.py +459 -0
  108. signalwire_agents/skills/spider/README.md +236 -0
  109. signalwire_agents/skills/spider/__init__.py +13 -0
  110. signalwire_agents/skills/spider/skill.py +598 -0
  111. signalwire_agents/skills/swml_transfer/README.md +395 -0
  112. signalwire_agents/skills/swml_transfer/__init__.py +10 -0
  113. signalwire_agents/skills/swml_transfer/skill.py +359 -0
  114. signalwire_agents/skills/weather_api/README.md +178 -0
  115. signalwire_agents/skills/weather_api/__init__.py +12 -0
  116. signalwire_agents/skills/weather_api/skill.py +191 -0
  117. signalwire_agents/skills/web_search/README.md +163 -0
  118. signalwire_agents/skills/web_search/__init__.py +10 -0
  119. signalwire_agents/skills/web_search/skill.py +739 -0
  120. signalwire_agents/skills/wikipedia_search/README.md +228 -0
  121. signalwire_agents/{core/state → skills/wikipedia_search}/__init__.py +5 -4
  122. signalwire_agents/skills/wikipedia_search/skill.py +210 -0
  123. signalwire_agents/utils/__init__.py +14 -0
  124. signalwire_agents/utils/schema_utils.py +111 -44
  125. signalwire_agents/web/__init__.py +17 -0
  126. signalwire_agents/web/web_service.py +559 -0
  127. signalwire_agents-1.0.7.data/data/share/man/man1/sw-agent-init.1 +307 -0
  128. signalwire_agents-1.0.7.data/data/share/man/man1/sw-search.1 +483 -0
  129. signalwire_agents-1.0.7.data/data/share/man/man1/swaig-test.1 +308 -0
  130. signalwire_agents-1.0.7.dist-info/METADATA +992 -0
  131. signalwire_agents-1.0.7.dist-info/RECORD +142 -0
  132. {signalwire_agents-0.1.6.dist-info → signalwire_agents-1.0.7.dist-info}/WHEEL +1 -1
  133. signalwire_agents-1.0.7.dist-info/entry_points.txt +4 -0
  134. signalwire_agents/core/state/file_state_manager.py +0 -219
  135. signalwire_agents/core/state/state_manager.py +0 -101
  136. signalwire_agents-0.1.6.data/data/schema.json +0 -5611
  137. signalwire_agents-0.1.6.dist-info/METADATA +0 -199
  138. signalwire_agents-0.1.6.dist-info/RECORD +0 -34
  139. {signalwire_agents-0.1.6.dist-info → signalwire_agents-1.0.7.dist-info}/licenses/LICENSE +0 -0
  140. {signalwire_agents-0.1.6.dist-info → signalwire_agents-1.0.7.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,310 @@
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 requests
11
+ import json
12
+ from typing import Optional, List, Dict, Any
13
+
14
+ from signalwire_agents.core.skill_base import SkillBase
15
+ from signalwire_agents.core.function_result import SwaigFunctionResult
16
+
17
+ class DataSphereSkill(SkillBase):
18
+ """SignalWire DataSphere knowledge search capability"""
19
+
20
+ SKILL_NAME = "datasphere"
21
+ SKILL_DESCRIPTION = "Search knowledge using SignalWire DataSphere RAG stack"
22
+ SKILL_VERSION = "1.0.0"
23
+ REQUIRED_PACKAGES = ["requests"]
24
+ REQUIRED_ENV_VARS = [] # No required env vars since all config comes from params
25
+
26
+ # Enable multiple instances support
27
+ SUPPORTS_MULTIPLE_INSTANCES = True
28
+
29
+ @classmethod
30
+ def get_parameter_schema(cls) -> Dict[str, Dict[str, Any]]:
31
+ """Get parameter schema for DataSphere skill"""
32
+ schema = super().get_parameter_schema()
33
+ schema.update({
34
+ "space_name": {
35
+ "type": "string",
36
+ "description": "SignalWire space name (e.g., 'mycompany' from mycompany.signalwire.com)",
37
+ "required": True
38
+ },
39
+ "project_id": {
40
+ "type": "string",
41
+ "description": "SignalWire project ID",
42
+ "required": True,
43
+ "env_var": "SIGNALWIRE_PROJECT_ID"
44
+ },
45
+ "token": {
46
+ "type": "string",
47
+ "description": "SignalWire API token",
48
+ "required": True,
49
+ "hidden": True,
50
+ "env_var": "SIGNALWIRE_TOKEN"
51
+ },
52
+ "document_id": {
53
+ "type": "string",
54
+ "description": "DataSphere document ID to search within",
55
+ "required": True
56
+ },
57
+ "count": {
58
+ "type": "integer",
59
+ "description": "Number of search results to return",
60
+ "default": 1,
61
+ "required": False,
62
+ "minimum": 1,
63
+ "maximum": 10
64
+ },
65
+ "distance": {
66
+ "type": "number",
67
+ "description": "Maximum distance threshold for results (lower is more relevant)",
68
+ "default": 3.0,
69
+ "required": False,
70
+ "minimum": 0.0,
71
+ "maximum": 10.0
72
+ },
73
+ "tags": {
74
+ "type": "array",
75
+ "description": "Tags to filter search results",
76
+ "required": False,
77
+ "items": {
78
+ "type": "string"
79
+ }
80
+ },
81
+ "language": {
82
+ "type": "string",
83
+ "description": "Language code for query expansion (e.g., 'en', 'es')",
84
+ "required": False
85
+ },
86
+ "pos_to_expand": {
87
+ "type": "array",
88
+ "description": "Parts of speech to expand with synonyms",
89
+ "required": False,
90
+ "items": {
91
+ "type": "string",
92
+ "enum": ["NOUN", "VERB", "ADJ", "ADV"]
93
+ }
94
+ },
95
+ "max_synonyms": {
96
+ "type": "integer",
97
+ "description": "Maximum number of synonyms to use for query expansion",
98
+ "required": False,
99
+ "minimum": 1,
100
+ "maximum": 10
101
+ },
102
+ "no_results_message": {
103
+ "type": "string",
104
+ "description": "Message to return when no results are found",
105
+ "default": "I couldn't find any relevant information for '{query}' in the knowledge base. Try rephrasing your question or asking about a different topic.",
106
+ "required": False
107
+ }
108
+ })
109
+ return schema
110
+
111
+ def get_instance_key(self) -> str:
112
+ """
113
+ Get the key used to track this skill instance
114
+
115
+ For DataSphere, we use 'search_knowledge' as the default tool name instead of 'datasphere'
116
+ """
117
+ tool_name = self.params.get('tool_name', 'search_knowledge')
118
+ return f"{self.SKILL_NAME}_{tool_name}"
119
+
120
+ def setup(self) -> bool:
121
+ """Setup the datasphere skill"""
122
+ # Validate required parameters
123
+ required_params = ['space_name', 'project_id', 'token', 'document_id']
124
+ missing_params = [param for param in required_params if not self.params.get(param)]
125
+ if missing_params:
126
+ self.logger.error(f"Missing required parameters: {missing_params}")
127
+ return False
128
+
129
+ # Set required parameters
130
+ self.space_name = self.params['space_name']
131
+ self.project_id = self.params['project_id']
132
+ self.token = self.params['token']
133
+ self.document_id = self.params['document_id']
134
+
135
+ # Set optional parameters with defaults
136
+ self.count = self.params.get('count', 1)
137
+ self.distance = self.params.get('distance', 3.0)
138
+ self.tags = self.params.get('tags', None) # None means don't include in request
139
+ self.language = self.params.get('language', None) # None means don't include in request
140
+ self.pos_to_expand = self.params.get('pos_to_expand', None) # None means don't include in request
141
+ self.max_synonyms = self.params.get('max_synonyms', None) # None means don't include in request
142
+
143
+ # Tool name (for multiple instances)
144
+ self.tool_name = self.params.get('tool_name', 'search_knowledge')
145
+
146
+ # No results message
147
+ self.no_results_message = self.params.get('no_results_message',
148
+ "I couldn't find any relevant information for '{query}' in the knowledge base. "
149
+ "Try rephrasing your question or asking about a different topic."
150
+ )
151
+
152
+ # Build API URL
153
+ self.api_url = f"https://{self.space_name}.signalwire.com/api/datasphere/documents/search"
154
+
155
+ # Setup session for requests
156
+ self.session = requests.Session()
157
+
158
+ return True
159
+
160
+ def register_tools(self) -> None:
161
+ """Register knowledge search tool with the agent"""
162
+ self.define_tool(
163
+ name=self.tool_name,
164
+ description="Search the knowledge base for information on any topic and return relevant results",
165
+ parameters={
166
+ "query": {
167
+ "type": "string",
168
+ "description": "The search query - what information you're looking for in the knowledge base"
169
+ }
170
+ },
171
+ handler=self._search_knowledge_handler
172
+ )
173
+
174
+ def _search_knowledge_handler(self, args, raw_data):
175
+ """Handler for knowledge search tool"""
176
+ query = args.get("query", "").strip()
177
+
178
+ if not query:
179
+ return SwaigFunctionResult(
180
+ "Please provide a search query. What would you like me to search for in the knowledge base?"
181
+ )
182
+
183
+ self.logger.info(f"DataSphere search requested: '{query}' (document: {self.document_id})")
184
+
185
+ # Build request payload
186
+ payload = {
187
+ "document_id": self.document_id,
188
+ "query_string": query,
189
+ "distance": self.distance,
190
+ "count": self.count
191
+ }
192
+
193
+ # Add optional parameters only if they were provided
194
+ if self.tags is not None:
195
+ payload["tags"] = self.tags
196
+ if self.language is not None:
197
+ payload["language"] = self.language
198
+ if self.pos_to_expand is not None:
199
+ payload["pos_to_expand"] = self.pos_to_expand
200
+ if self.max_synonyms is not None:
201
+ payload["max_synonyms"] = self.max_synonyms
202
+
203
+ try:
204
+ # Make API request
205
+ response = self.session.post(
206
+ self.api_url,
207
+ auth=(self.project_id, self.token),
208
+ headers={
209
+ 'Content-Type': 'application/json',
210
+ 'Accept': 'application/json'
211
+ },
212
+ json=payload,
213
+ timeout=30
214
+ )
215
+
216
+ response.raise_for_status()
217
+ data = response.json()
218
+
219
+ # Check if we have valid response data
220
+ if not data or not isinstance(data, dict):
221
+ self.logger.warning(f"DataSphere API returned invalid data: {data}")
222
+ formatted_message = self.no_results_message.format(query=query) if '{query}' in self.no_results_message else self.no_results_message
223
+ return SwaigFunctionResult(formatted_message)
224
+
225
+ # Extract results - DataSphere API returns 'chunks', not 'results'
226
+ chunks = data.get('chunks', [])
227
+
228
+ if not chunks:
229
+ formatted_message = self.no_results_message.format(query=query) if '{query}' in self.no_results_message else self.no_results_message
230
+ return SwaigFunctionResult(formatted_message)
231
+
232
+ # Format the results
233
+ formatted_results = self._format_search_results(query, chunks)
234
+ return SwaigFunctionResult(formatted_results)
235
+
236
+ except requests.exceptions.Timeout:
237
+ self.logger.error("DataSphere API request timed out")
238
+ return SwaigFunctionResult(
239
+ "Sorry, the knowledge search timed out. Please try again."
240
+ )
241
+ except requests.exceptions.HTTPError as e:
242
+ self.logger.error(f"DataSphere API HTTP error: {e}")
243
+ return SwaigFunctionResult(
244
+ "Sorry, there was an error accessing the knowledge base. Please try again later."
245
+ )
246
+ except Exception as e:
247
+ self.logger.error(f"Error performing DataSphere search: {e}")
248
+ return SwaigFunctionResult(
249
+ "Sorry, I encountered an error while searching the knowledge base. Please try again later."
250
+ )
251
+
252
+ def _format_search_results(self, query: str, chunks: List[Dict[str, Any]]) -> str:
253
+ """Format search results for display"""
254
+ if len(chunks) == 1:
255
+ result_text = f"I found 1 result for '{query}':\n\n"
256
+ else:
257
+ result_text = f"I found {len(chunks)} results for '{query}':\n\n"
258
+
259
+ formatted_results = []
260
+
261
+ for i, chunk in enumerate(chunks, 1):
262
+ result_content = f"=== RESULT {i} ===\n"
263
+
264
+ # DataSphere API returns chunks with 'text' field
265
+ if 'text' in chunk:
266
+ result_content += chunk['text']
267
+ elif 'content' in chunk:
268
+ result_content += chunk['content']
269
+ elif 'chunk' in chunk:
270
+ result_content += chunk['chunk']
271
+ else:
272
+ # Fallback to the entire result as JSON if we don't recognize the format
273
+ result_content += json.dumps(chunk, indent=2)
274
+
275
+ result_content += f"\n{'='*50}\n\n"
276
+ formatted_results.append(result_content)
277
+
278
+ return result_text + '\n'.join(formatted_results)
279
+
280
+ def get_hints(self) -> List[str]:
281
+ """Return speech recognition hints"""
282
+ # Currently no hints provided, but you could add them like:
283
+ # return [
284
+ # "knowledge", "search", "information", "database", "find",
285
+ # "look up", "research", "query", "datasphere", "document"
286
+ # ]
287
+ return []
288
+
289
+ def get_global_data(self) -> Dict[str, Any]:
290
+ """Return global data for agent context"""
291
+ return {
292
+ "datasphere_enabled": True,
293
+ "document_id": self.document_id,
294
+ "knowledge_provider": "SignalWire DataSphere"
295
+ }
296
+
297
+ def get_prompt_sections(self) -> List[Dict[str, Any]]:
298
+ """Return prompt sections to add to agent"""
299
+ return [
300
+ {
301
+ "title": "Knowledge Search Capability",
302
+ "body": f"You can search a knowledge base for information using the {self.tool_name} tool.",
303
+ "bullets": [
304
+ f"Use the {self.tool_name} tool when users ask for information that might be in the knowledge base",
305
+ "Search for relevant information using clear, specific queries",
306
+ "Summarize search results in a clear, helpful way",
307
+ "If no results are found, suggest the user try rephrasing their question"
308
+ ]
309
+ }
310
+ ]
@@ -0,0 +1,258 @@
1
+ # DataSphere Serverless Skill
2
+
3
+ The datasphere_serverless skill provides knowledge search capabilities using SignalWire DataSphere's RAG (Retrieval-Augmented Generation) stack with serverless execution via DataMap. This skill offers the same functionality as the standard datasphere skill but executes on SignalWire servers rather than your agent server.
4
+
5
+ ## Features
6
+
7
+ - **Serverless Execution**: Runs on SignalWire infrastructure via DataMap
8
+ - **SignalWire DataSphere Integration**: Vector-based knowledge search
9
+ - **Identical API**: Same parameters and functionality as the standard datasphere skill
10
+ - **Multi-language Support**: Synonym expansion and language-specific search
11
+ - **Tag-based Filtering**: Targeted searches using document tags
12
+ - **Custom No-results Messages**: Configurable response templates
13
+ - **Multiple Instance Support**: Search different knowledge bases with different configurations
14
+ - **No Webhook Infrastructure**: No need to expose HTTP endpoints
15
+
16
+ ## Requirements
17
+
18
+ - **Packages**: None (DataMap handles API calls serverlessly)
19
+ - **SignalWire Account**: DataSphere-enabled space with uploaded documents
20
+
21
+ ## Parameters
22
+
23
+ ### Required Parameters
24
+
25
+ - `space_name` (string): SignalWire space name
26
+ - `project_id` (string): SignalWire project ID
27
+ - `token` (string): SignalWire authentication token
28
+ - `document_id` (string): DataSphere document ID to search
29
+
30
+ ### Optional Parameters
31
+
32
+ - `count` (integer, default: 1): Number of search results to return
33
+ - `distance` (float, default: 3.0): Distance threshold for search matching (lower = more similar)
34
+ - `tags` (list): List of tags to filter search results
35
+ - `language` (string): Language code to limit search (e.g., "en", "es")
36
+ - `pos_to_expand` (list): Parts of speech for synonym expansion (e.g., ["NOUN", "VERB"])
37
+ - `max_synonyms` (integer): Maximum number of synonyms to use for each word
38
+ - `tool_name` (string, default: "search_knowledge"): Custom name for the search tool (enables multiple instances)
39
+ - `no_results_message` (string): Custom message when no results are found
40
+ - Default: "I couldn't find any relevant information for '{query}' in the knowledge base. Try rephrasing your question or asking about a different topic."
41
+ - Use `{query}` as placeholder for the search query
42
+
43
+ ### Advanced Parameters
44
+
45
+ - `swaig_fields` (dict): Additional SWAIG function configuration
46
+ - `secure` (boolean): Override security settings
47
+ - `fillers` (dict): Language-specific filler phrases during search
48
+ - Any other SWAIG function parameters
49
+
50
+ ## Tools Created
51
+
52
+ - **Default**: `search_knowledge` - Search the knowledge base for information
53
+ - **Custom**: Uses the `tool_name` parameter value
54
+
55
+ ## Usage Examples
56
+
57
+ ### Basic Usage
58
+
59
+ ```python
60
+ # Minimal configuration - same as standard datasphere skill
61
+ agent.add_skill("datasphere_serverless", {
62
+ "space_name": "my-space",
63
+ "project_id": "my-project-id",
64
+ "token": "my-token",
65
+ "document_id": "my-document-id"
66
+ })
67
+ ```
68
+
69
+ ### Advanced Configuration
70
+
71
+ ```python
72
+ # Comprehensive search with filtering - identical to standard skill
73
+ agent.add_skill("datasphere_serverless", {
74
+ "space_name": "my-space",
75
+ "project_id": "my-project-id",
76
+ "token": "my-token",
77
+ "document_id": "my-document-id",
78
+ "count": 3,
79
+ "distance": 5.0,
80
+ "tags": ["FAQ", "Support"],
81
+ "language": "en",
82
+ "pos_to_expand": ["NOUN", "VERB"],
83
+ "max_synonyms": 3,
84
+ "no_results_message": "I couldn't find information about '{query}' in our support documentation."
85
+ })
86
+ ```
87
+
88
+ ### Multiple Instances
89
+
90
+ ```python
91
+ # Product documentation search
92
+ agent.add_skill("datasphere_serverless", {
93
+ "space_name": "my-space",
94
+ "project_id": "my-project-id",
95
+ "token": "my-token",
96
+ "document_id": "product-docs-id",
97
+ "tool_name": "search_product_docs",
98
+ "tags": ["Products", "Features"],
99
+ "count": 2
100
+ })
101
+
102
+ # Support knowledge base search
103
+ agent.add_skill("datasphere_serverless", {
104
+ "space_name": "my-space",
105
+ "project_id": "my-project-id",
106
+ "token": "my-token",
107
+ "document_id": "support-kb-id",
108
+ "tool_name": "search_support",
109
+ "tags": ["Support", "Troubleshooting"],
110
+ "count": 3,
111
+ "distance": 4.0
112
+ })
113
+ ```
114
+
115
+ ## DataMap Implementation Details
116
+
117
+ This skill demonstrates advanced DataMap usage patterns:
118
+
119
+ ### 1. **Serverless API Integration**
120
+ - API calls execute on SignalWire servers, not your agent server
121
+ - No webhook endpoints required
122
+ - Built-in authentication and error handling
123
+
124
+ ### 2. **Dynamic Request Building**
125
+ ```python
126
+ webhook_body = {
127
+ "document_id": self.document_id,
128
+ "query_string": "${args.query}", # Dynamic from user input
129
+ "distance": self.distance, # Static from configuration
130
+ "count": self.count # Static from configuration
131
+ }
132
+
133
+ # Optional parameters added conditionally
134
+ if self.tags is not None:
135
+ webhook_body["tags"] = self.tags
136
+ ```
137
+
138
+ ### 3. **Response Processing with Foreach**
139
+ ```python
140
+ .foreach({
141
+ "input_key": "results", # API response key containing array
142
+ "output_key": "formatted_results", # Name for built string
143
+ "max": self.count, # Limit processing
144
+ "append": "=== RESULT ${this.index} ===\n${this.content}\n========\n\n"
145
+ })
146
+ ```
147
+
148
+ The `foreach` mechanism:
149
+ - Iterates over the `results` array from DataSphere API response
150
+ - For each result, expands `${this.content}` with the result's content field
151
+ - Builds a concatenated string stored as `formatted_results`
152
+ - Limits processing to `max` items
153
+
154
+ ### 4. **Variable Expansion in Output**
155
+ ```python
156
+ .output(SwaigFunctionResult('I found ${results.length} result(s) for "${args.query}":\n\n${formatted_results}'))
157
+ ```
158
+
159
+ References:
160
+ - `${results.length}`: Number of results from API
161
+ - `${args.query}`: User's search query
162
+ - `${formatted_results}`: String built by foreach
163
+
164
+ ### 5. **Error Handling**
165
+ ```python
166
+ .error_keys(['error', 'message'])
167
+ .fallback_output(SwaigFunctionResult(self.no_results_message.replace('{query}', '${args.query}')))
168
+ ```
169
+
170
+ ## Comparison: Standard vs Serverless
171
+
172
+ | Feature | Standard DataSphere | DataSphere Serverless |
173
+ |---------|-------------------|---------------------|
174
+ | **Execution** | Agent server | SignalWire servers |
175
+ | **Parameters** | Identical | Identical |
176
+ | **Functionality** | Full Python logic | DataMap templates |
177
+ | **Performance** | Agent server load | No agent server load |
178
+ | **Deployment** | Webhook infrastructure | No infrastructure needed |
179
+ | **Response Formatting** | Custom Python code | DataMap foreach/templates |
180
+ | **Error Handling** | Granular Python exceptions | DataMap error keys |
181
+ | **Use Case** | Complex custom logic | Standard API integration |
182
+
183
+ ## When to Use Serverless vs Standard
184
+
185
+ ### **Use DataSphere Serverless When:**
186
+ - You want simple deployment without webhook infrastructure
187
+ - Performance on agent server is a concern
188
+ - Standard response formatting is sufficient
189
+ - You prefer serverless execution model
190
+
191
+ ### **Use Standard DataSphere When:**
192
+ - You need complex custom response formatting
193
+ - You want granular error handling with different messages per error type
194
+ - You need runtime decision-making logic
195
+ - You want full control over the search process
196
+
197
+ ## Benefits of DataMap Implementation
198
+
199
+ 1. **Simplified Deployment**: No HTTP endpoints to expose or manage
200
+ 2. **Better Performance**: Executes on SignalWire infrastructure
201
+ 3. **Reduced Complexity**: Declarative configuration vs imperative code
202
+ 4. **Automatic Scaling**: SignalWire handles execution scaling
203
+ 5. **Built-in Reliability**: Server-side execution with built-in retry logic
204
+
205
+ ## How It Works
206
+
207
+ 1. **Configuration**: Skill parameters are validated and stored during setup
208
+ 2. **Tool Registration**: DataMap configuration is built with static values from setup
209
+ 3. **Execution**: When called, DataMap executes on SignalWire servers:
210
+ - Makes POST request to DataSphere API with user's query
211
+ - Processes response array using foreach mechanism
212
+ - Formats results using template expansion
213
+ - Returns formatted response to agent
214
+ 4. **Response**: Agent receives formatted results without any local processing
215
+
216
+ ## Multiple Instance Support
217
+
218
+ Like the standard datasphere skill, this supports multiple instances:
219
+
220
+ - Each instance creates a tool with a unique name (`tool_name` parameter)
221
+ - Different configurations per instance (different documents, tags, etc.)
222
+ - Instance tracking via `get_instance_key()` method
223
+ - Same agent can search multiple knowledge bases
224
+
225
+ ## Error Handling
226
+
227
+ - **API Errors**: Handled by `error_keys` configuration
228
+ - **No Results**: Uses `fallback_output` with custom message
229
+ - **Invalid Parameters**: Validated during skill setup
230
+ - **Timeout/Network**: Handled by SignalWire infrastructure
231
+
232
+ ## Troubleshooting
233
+
234
+ ### Common Issues
235
+
236
+ 1. **"Missing required parameters"**
237
+ - Ensure all required parameters are provided
238
+ - Check parameter names match exactly
239
+
240
+ 2. **"No results found"**
241
+ - Verify document_id exists and is accessible
242
+ - Check distance threshold isn't too restrictive
243
+ - Ensure tags match document tags if specified
244
+
245
+ 3. **"Authentication failed"**
246
+ - Verify project_id and token are correct
247
+ - Ensure token has DataSphere permissions
248
+
249
+ ### Debugging
250
+
251
+ Enable debug logging to see DataMap execution:
252
+
253
+ ```python
254
+ import logging
255
+ logging.basicConfig(level=logging.DEBUG)
256
+ ```
257
+
258
+ DataMap execution details are logged by the SignalWire server infrastructure.
@@ -0,0 +1,10 @@
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
+ """DataSphere Serverless Skill for SignalWire Agents using DataMap"""