signalwire-agents 0.1.13__py3-none-any.whl → 1.0.17.dev4__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 +99 -15
- signalwire_agents/agent_server.py +248 -60
- signalwire_agents/agents/bedrock.py +296 -0
- signalwire_agents/cli/__init__.py +9 -0
- signalwire_agents/cli/build_search.py +951 -41
- signalwire_agents/cli/config.py +80 -0
- signalwire_agents/cli/core/__init__.py +10 -0
- signalwire_agents/cli/core/agent_loader.py +470 -0
- signalwire_agents/cli/core/argparse_helpers.py +179 -0
- signalwire_agents/cli/core/dynamic_config.py +71 -0
- signalwire_agents/cli/core/service_loader.py +303 -0
- signalwire_agents/cli/dokku.py +2320 -0
- signalwire_agents/cli/execution/__init__.py +10 -0
- signalwire_agents/cli/execution/datamap_exec.py +446 -0
- signalwire_agents/cli/execution/webhook_exec.py +134 -0
- signalwire_agents/cli/init_project.py +2636 -0
- signalwire_agents/cli/output/__init__.py +10 -0
- signalwire_agents/cli/output/output_formatter.py +255 -0
- signalwire_agents/cli/output/swml_dump.py +186 -0
- signalwire_agents/cli/simulation/__init__.py +10 -0
- signalwire_agents/cli/simulation/data_generation.py +374 -0
- signalwire_agents/cli/simulation/data_overrides.py +200 -0
- signalwire_agents/cli/simulation/mock_env.py +282 -0
- signalwire_agents/cli/swaig_test_wrapper.py +52 -0
- signalwire_agents/cli/test_swaig.py +566 -2366
- signalwire_agents/cli/types.py +81 -0
- signalwire_agents/core/__init__.py +2 -2
- signalwire_agents/core/agent/__init__.py +12 -0
- signalwire_agents/core/agent/config/__init__.py +12 -0
- signalwire_agents/core/agent/deployment/__init__.py +9 -0
- signalwire_agents/core/agent/deployment/handlers/__init__.py +9 -0
- signalwire_agents/core/agent/prompt/__init__.py +14 -0
- signalwire_agents/core/agent/prompt/manager.py +306 -0
- signalwire_agents/core/agent/routing/__init__.py +9 -0
- signalwire_agents/core/agent/security/__init__.py +9 -0
- signalwire_agents/core/agent/swml/__init__.py +9 -0
- signalwire_agents/core/agent/tools/__init__.py +15 -0
- signalwire_agents/core/agent/tools/decorator.py +97 -0
- signalwire_agents/core/agent/tools/registry.py +210 -0
- signalwire_agents/core/agent_base.py +845 -2916
- signalwire_agents/core/auth_handler.py +233 -0
- signalwire_agents/core/config_loader.py +259 -0
- signalwire_agents/core/contexts.py +418 -0
- signalwire_agents/core/data_map.py +3 -15
- signalwire_agents/core/function_result.py +116 -44
- signalwire_agents/core/logging_config.py +162 -18
- signalwire_agents/core/mixins/__init__.py +28 -0
- signalwire_agents/core/mixins/ai_config_mixin.py +442 -0
- signalwire_agents/core/mixins/auth_mixin.py +280 -0
- signalwire_agents/core/mixins/prompt_mixin.py +358 -0
- signalwire_agents/core/mixins/serverless_mixin.py +460 -0
- signalwire_agents/core/mixins/skill_mixin.py +55 -0
- signalwire_agents/core/mixins/state_mixin.py +153 -0
- signalwire_agents/core/mixins/tool_mixin.py +230 -0
- signalwire_agents/core/mixins/web_mixin.py +1142 -0
- signalwire_agents/core/security_config.py +333 -0
- signalwire_agents/core/skill_base.py +84 -1
- signalwire_agents/core/skill_manager.py +62 -20
- signalwire_agents/core/swaig_function.py +18 -5
- signalwire_agents/core/swml_builder.py +207 -11
- signalwire_agents/core/swml_handler.py +27 -21
- signalwire_agents/core/swml_renderer.py +123 -312
- signalwire_agents/core/swml_service.py +171 -203
- signalwire_agents/mcp_gateway/__init__.py +29 -0
- signalwire_agents/mcp_gateway/gateway_service.py +564 -0
- signalwire_agents/mcp_gateway/mcp_manager.py +513 -0
- signalwire_agents/mcp_gateway/session_manager.py +218 -0
- signalwire_agents/prefabs/concierge.py +0 -3
- signalwire_agents/prefabs/faq_bot.py +0 -3
- signalwire_agents/prefabs/info_gatherer.py +0 -3
- signalwire_agents/prefabs/receptionist.py +0 -3
- signalwire_agents/prefabs/survey.py +0 -3
- signalwire_agents/schema.json +9218 -5489
- signalwire_agents/search/__init__.py +7 -1
- signalwire_agents/search/document_processor.py +490 -31
- signalwire_agents/search/index_builder.py +307 -37
- signalwire_agents/search/migration.py +418 -0
- signalwire_agents/search/models.py +30 -0
- signalwire_agents/search/pgvector_backend.py +748 -0
- signalwire_agents/search/query_processor.py +162 -31
- signalwire_agents/search/search_engine.py +916 -35
- signalwire_agents/search/search_service.py +376 -53
- signalwire_agents/skills/README.md +452 -0
- signalwire_agents/skills/__init__.py +14 -2
- signalwire_agents/skills/api_ninjas_trivia/README.md +215 -0
- signalwire_agents/skills/api_ninjas_trivia/__init__.py +12 -0
- signalwire_agents/skills/api_ninjas_trivia/skill.py +237 -0
- signalwire_agents/skills/datasphere/README.md +210 -0
- signalwire_agents/skills/datasphere/skill.py +84 -3
- signalwire_agents/skills/datasphere_serverless/README.md +258 -0
- signalwire_agents/skills/datasphere_serverless/__init__.py +9 -0
- signalwire_agents/skills/datasphere_serverless/skill.py +82 -1
- signalwire_agents/skills/datetime/README.md +132 -0
- signalwire_agents/skills/datetime/__init__.py +9 -0
- signalwire_agents/skills/datetime/skill.py +20 -7
- signalwire_agents/skills/joke/README.md +149 -0
- signalwire_agents/skills/joke/__init__.py +9 -0
- signalwire_agents/skills/joke/skill.py +21 -0
- signalwire_agents/skills/math/README.md +161 -0
- signalwire_agents/skills/math/__init__.py +9 -0
- signalwire_agents/skills/math/skill.py +18 -4
- signalwire_agents/skills/mcp_gateway/README.md +230 -0
- signalwire_agents/skills/mcp_gateway/__init__.py +10 -0
- signalwire_agents/skills/mcp_gateway/skill.py +421 -0
- signalwire_agents/skills/native_vector_search/README.md +210 -0
- signalwire_agents/skills/native_vector_search/__init__.py +9 -0
- signalwire_agents/skills/native_vector_search/skill.py +569 -101
- signalwire_agents/skills/play_background_file/README.md +218 -0
- signalwire_agents/skills/play_background_file/__init__.py +12 -0
- signalwire_agents/skills/play_background_file/skill.py +242 -0
- signalwire_agents/skills/registry.py +395 -40
- signalwire_agents/skills/spider/README.md +236 -0
- signalwire_agents/skills/spider/__init__.py +13 -0
- signalwire_agents/skills/spider/skill.py +598 -0
- signalwire_agents/skills/swml_transfer/README.md +395 -0
- signalwire_agents/skills/swml_transfer/__init__.py +10 -0
- signalwire_agents/skills/swml_transfer/skill.py +359 -0
- signalwire_agents/skills/weather_api/README.md +178 -0
- signalwire_agents/skills/weather_api/__init__.py +12 -0
- signalwire_agents/skills/weather_api/skill.py +191 -0
- signalwire_agents/skills/web_search/README.md +163 -0
- signalwire_agents/skills/web_search/__init__.py +9 -0
- signalwire_agents/skills/web_search/skill.py +586 -112
- signalwire_agents/skills/wikipedia_search/README.md +228 -0
- signalwire_agents/{core/state → skills/wikipedia_search}/__init__.py +5 -4
- signalwire_agents/skills/{wikipedia → wikipedia_search}/skill.py +33 -3
- signalwire_agents/web/__init__.py +17 -0
- signalwire_agents/web/web_service.py +559 -0
- signalwire_agents-1.0.17.dev4.data/data/share/man/man1/sw-agent-init.1 +400 -0
- signalwire_agents-1.0.17.dev4.data/data/share/man/man1/sw-search.1 +483 -0
- signalwire_agents-1.0.17.dev4.data/data/share/man/man1/swaig-test.1 +308 -0
- {signalwire_agents-0.1.13.dist-info → signalwire_agents-1.0.17.dev4.dist-info}/METADATA +347 -215
- signalwire_agents-1.0.17.dev4.dist-info/RECORD +147 -0
- signalwire_agents-1.0.17.dev4.dist-info/entry_points.txt +6 -0
- signalwire_agents/core/state/file_state_manager.py +0 -219
- signalwire_agents/core/state/state_manager.py +0 -101
- signalwire_agents/skills/wikipedia/__init__.py +0 -9
- signalwire_agents-0.1.13.data/data/schema.json +0 -5611
- signalwire_agents-0.1.13.dist-info/RECORD +0 -67
- signalwire_agents-0.1.13.dist-info/entry_points.txt +0 -3
- {signalwire_agents-0.1.13.dist-info → signalwire_agents-1.0.17.dev4.dist-info}/WHEEL +0 -0
- {signalwire_agents-0.1.13.dist-info → signalwire_agents-1.0.17.dev4.dist-info}/licenses/LICENSE +0 -0
- {signalwire_agents-0.1.13.dist-info → signalwire_agents-1.0.17.dev4.dist-info}/top_level.txt +0 -0
|
@@ -188,7 +188,7 @@ class SwaigFunctionResult:
|
|
|
188
188
|
self.action.append(swml_action)
|
|
189
189
|
return self
|
|
190
190
|
|
|
191
|
-
def swml_transfer(self, dest: str, ai_response: str) -> 'SwaigFunctionResult':
|
|
191
|
+
def swml_transfer(self, dest: str, ai_response: str, final: bool = True) -> 'SwaigFunctionResult':
|
|
192
192
|
"""
|
|
193
193
|
Add a SWML transfer action with AI response setup for when transfer completes.
|
|
194
194
|
|
|
@@ -202,22 +202,28 @@ class SwaigFunctionResult:
|
|
|
202
202
|
Args:
|
|
203
203
|
dest: Destination URL for the transfer (SWML endpoint, SIP address, etc.)
|
|
204
204
|
ai_response: Message the AI should say when transfer completes and control returns
|
|
205
|
+
final: Whether this is a permanent transfer (True) or temporary (False).
|
|
206
|
+
Defaults to True for permanent transfers (same as connect method).
|
|
205
207
|
|
|
206
208
|
Returns:
|
|
207
209
|
Self for method chaining
|
|
208
210
|
|
|
209
211
|
Example:
|
|
210
|
-
#
|
|
212
|
+
# Permanent transfer (default)
|
|
211
213
|
result = (
|
|
212
214
|
SwaigFunctionResult("I'm transferring you to support", post_process=True)
|
|
213
215
|
.swml_transfer(
|
|
214
216
|
"https://support.example.com/swml",
|
|
215
|
-
"
|
|
217
|
+
"Goodbye!" # Won't be used since final=True by default
|
|
216
218
|
)
|
|
217
219
|
)
|
|
218
220
|
|
|
219
|
-
#
|
|
220
|
-
result.swml_transfer(
|
|
221
|
+
# Temporary transfer with return
|
|
222
|
+
result.swml_transfer(
|
|
223
|
+
dest,
|
|
224
|
+
"The support call is complete. How else can I help?",
|
|
225
|
+
final=False
|
|
226
|
+
)
|
|
221
227
|
"""
|
|
222
228
|
# Create the SWML action structure directly
|
|
223
229
|
swml_action = {
|
|
@@ -229,7 +235,8 @@ class SwaigFunctionResult:
|
|
|
229
235
|
{"transfer": {"dest": dest}}
|
|
230
236
|
]
|
|
231
237
|
}
|
|
232
|
-
}
|
|
238
|
+
},
|
|
239
|
+
"transfer": str(final).lower() # Convert boolean to "true"/"false" string
|
|
233
240
|
}
|
|
234
241
|
|
|
235
242
|
# Add to actions list directly
|
|
@@ -251,9 +258,89 @@ class SwaigFunctionResult:
|
|
|
251
258
|
Returns:
|
|
252
259
|
self for method chaining
|
|
253
260
|
"""
|
|
254
|
-
|
|
255
|
-
return self.add_action("set_global_data", action)
|
|
261
|
+
return self.add_action("set_global_data", data)
|
|
256
262
|
|
|
263
|
+
def swml_user_event(self, event_data: Dict[str, Any]) -> 'SwaigFunctionResult':
|
|
264
|
+
"""
|
|
265
|
+
Send a user event through SWML to update the client UI.
|
|
266
|
+
|
|
267
|
+
This is a convenience method for sending user events to connected clients,
|
|
268
|
+
commonly used for real-time UI updates in interactive applications.
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
event_data: Dictionary containing the event type and any associated data
|
|
272
|
+
Example: {"type": "cards_dealt", "player_hand": [...], "score": 21}
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
Self for method chaining
|
|
276
|
+
|
|
277
|
+
Example:
|
|
278
|
+
result = (
|
|
279
|
+
SwaigFunctionResult("You have blackjack!")
|
|
280
|
+
.swml_user_event({
|
|
281
|
+
"type": "cards_dealt",
|
|
282
|
+
"player_hand": player_cards,
|
|
283
|
+
"dealer_hand": dealer_cards,
|
|
284
|
+
"player_score": 21
|
|
285
|
+
})
|
|
286
|
+
)
|
|
287
|
+
"""
|
|
288
|
+
swml_action = {
|
|
289
|
+
"sections": {
|
|
290
|
+
"main": [{
|
|
291
|
+
"user_event": {
|
|
292
|
+
"event": event_data
|
|
293
|
+
}
|
|
294
|
+
}]
|
|
295
|
+
},
|
|
296
|
+
"version": "1.0.0"
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return self.add_action("SWML", swml_action)
|
|
300
|
+
|
|
301
|
+
def swml_change_step(self, step_name: str) -> 'SwaigFunctionResult':
|
|
302
|
+
"""
|
|
303
|
+
Change the conversation step in the AI agent's workflow.
|
|
304
|
+
|
|
305
|
+
This is a convenience method for transitioning between conversation steps,
|
|
306
|
+
allowing dynamic workflow control based on user interactions or game state.
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
step_name: Name of the step to transition to (e.g., "betting", "playing", "hand_complete")
|
|
310
|
+
|
|
311
|
+
Returns:
|
|
312
|
+
Self for method chaining
|
|
313
|
+
|
|
314
|
+
Example:
|
|
315
|
+
result = (
|
|
316
|
+
SwaigFunctionResult("Starting a new hand")
|
|
317
|
+
.swml_change_step("betting")
|
|
318
|
+
.swml_user_event({"type": "game_reset", "chips": 1000})
|
|
319
|
+
)
|
|
320
|
+
"""
|
|
321
|
+
return self.add_action("change_step", step_name)
|
|
322
|
+
|
|
323
|
+
def swml_change_context(self, context_name: str) -> 'SwaigFunctionResult':
|
|
324
|
+
"""
|
|
325
|
+
Change the conversation context in the AI agent's workflow.
|
|
326
|
+
|
|
327
|
+
This is a convenience method for switching between different conversation contexts,
|
|
328
|
+
useful for agents that handle multiple distinct workflows or service modes.
|
|
329
|
+
|
|
330
|
+
Args:
|
|
331
|
+
context_name: Name of the context to transition to (e.g., "support", "sales", "technical")
|
|
332
|
+
|
|
333
|
+
Returns:
|
|
334
|
+
Self for method chaining
|
|
335
|
+
|
|
336
|
+
Example:
|
|
337
|
+
result = (
|
|
338
|
+
SwaigFunctionResult("Transferring you to technical support")
|
|
339
|
+
.swml_change_context("technical_support")
|
|
340
|
+
)
|
|
341
|
+
"""
|
|
342
|
+
return self.add_action("change_context", context_name)
|
|
343
|
+
|
|
257
344
|
def execute_swml(self, swml_content, transfer: bool = False) -> 'SwaigFunctionResult':
|
|
258
345
|
"""
|
|
259
346
|
Execute SWML content with optional transfer behavior.
|
|
@@ -281,7 +368,7 @@ class SwaigFunctionResult:
|
|
|
281
368
|
else:
|
|
282
369
|
raise TypeError("swml_content must be string, dict, or SWML object")
|
|
283
370
|
|
|
284
|
-
action =
|
|
371
|
+
action = swml_data
|
|
285
372
|
if transfer:
|
|
286
373
|
action["transfer"] = "true"
|
|
287
374
|
|
|
@@ -294,8 +381,7 @@ class SwaigFunctionResult:
|
|
|
294
381
|
Returns:
|
|
295
382
|
self for method chaining
|
|
296
383
|
"""
|
|
297
|
-
|
|
298
|
-
return self.add_action("hangup", action)
|
|
384
|
+
return self.add_action("hangup", True)
|
|
299
385
|
|
|
300
386
|
def hold(self, timeout: int = 300) -> 'SwaigFunctionResult':
|
|
301
387
|
"""
|
|
@@ -309,8 +395,7 @@ class SwaigFunctionResult:
|
|
|
309
395
|
"""
|
|
310
396
|
# Clamp timeout to valid range
|
|
311
397
|
timeout = max(0, min(timeout, 900))
|
|
312
|
-
|
|
313
|
-
return self.add_action("hold", action)
|
|
398
|
+
return self.add_action("hold", timeout)
|
|
314
399
|
|
|
315
400
|
def wait_for_user(self, enabled: Optional[bool] = None, timeout: Optional[int] = None, answer_first: bool = False) -> 'SwaigFunctionResult':
|
|
316
401
|
"""
|
|
@@ -333,8 +418,7 @@ class SwaigFunctionResult:
|
|
|
333
418
|
else:
|
|
334
419
|
wait_value = True
|
|
335
420
|
|
|
336
|
-
|
|
337
|
-
return self.add_action("wait_for_user", action)
|
|
421
|
+
return self.add_action("wait_for_user", wait_value)
|
|
338
422
|
|
|
339
423
|
def stop(self) -> 'SwaigFunctionResult':
|
|
340
424
|
"""
|
|
@@ -343,8 +427,7 @@ class SwaigFunctionResult:
|
|
|
343
427
|
Returns:
|
|
344
428
|
self for method chaining
|
|
345
429
|
"""
|
|
346
|
-
|
|
347
|
-
return self.add_action("stop", action)
|
|
430
|
+
return self.add_action("stop", True)
|
|
348
431
|
|
|
349
432
|
def say(self, text: str) -> 'SwaigFunctionResult':
|
|
350
433
|
"""
|
|
@@ -356,8 +439,7 @@ class SwaigFunctionResult:
|
|
|
356
439
|
Returns:
|
|
357
440
|
self for method chaining
|
|
358
441
|
"""
|
|
359
|
-
|
|
360
|
-
return self.add_action("say", action)
|
|
442
|
+
return self.add_action("say", text)
|
|
361
443
|
|
|
362
444
|
def play_background_file(self, filename: str, wait: bool = False) -> 'SwaigFunctionResult':
|
|
363
445
|
"""
|
|
@@ -395,8 +477,7 @@ class SwaigFunctionResult:
|
|
|
395
477
|
Returns:
|
|
396
478
|
self for method chaining
|
|
397
479
|
"""
|
|
398
|
-
|
|
399
|
-
return self.add_action("end_of_speech_timeout", action)
|
|
480
|
+
return self.add_action("end_of_speech_timeout", milliseconds)
|
|
400
481
|
|
|
401
482
|
def set_speech_event_timeout(self, milliseconds: int) -> 'SwaigFunctionResult':
|
|
402
483
|
"""
|
|
@@ -409,8 +490,7 @@ class SwaigFunctionResult:
|
|
|
409
490
|
Returns:
|
|
410
491
|
self for method chaining
|
|
411
492
|
"""
|
|
412
|
-
|
|
413
|
-
return self.add_action("speech_event_timeout", action)
|
|
493
|
+
return self.add_action("speech_event_timeout", milliseconds)
|
|
414
494
|
|
|
415
495
|
def remove_global_data(self, keys: Union[str, List[str]]) -> 'SwaigFunctionResult':
|
|
416
496
|
"""
|
|
@@ -422,8 +502,7 @@ class SwaigFunctionResult:
|
|
|
422
502
|
Returns:
|
|
423
503
|
self for method chaining
|
|
424
504
|
"""
|
|
425
|
-
|
|
426
|
-
return self.add_action("unset_global_data", action)
|
|
505
|
+
return self.add_action("unset_global_data", keys)
|
|
427
506
|
|
|
428
507
|
def set_metadata(self, data: Dict[str, Any]) -> 'SwaigFunctionResult':
|
|
429
508
|
"""
|
|
@@ -435,8 +514,7 @@ class SwaigFunctionResult:
|
|
|
435
514
|
Returns:
|
|
436
515
|
self for method chaining
|
|
437
516
|
"""
|
|
438
|
-
|
|
439
|
-
return self.add_action("set_meta_data", action)
|
|
517
|
+
return self.add_action("set_meta_data", data)
|
|
440
518
|
|
|
441
519
|
def remove_metadata(self, keys: Union[str, List[str]]) -> 'SwaigFunctionResult':
|
|
442
520
|
"""
|
|
@@ -448,8 +526,7 @@ class SwaigFunctionResult:
|
|
|
448
526
|
Returns:
|
|
449
527
|
self for method chaining
|
|
450
528
|
"""
|
|
451
|
-
|
|
452
|
-
return self.add_action("unset_meta_data", action)
|
|
529
|
+
return self.add_action("unset_meta_data", keys)
|
|
453
530
|
|
|
454
531
|
def toggle_functions(self, function_toggles: List[Dict[str, Any]]) -> 'SwaigFunctionResult':
|
|
455
532
|
"""
|
|
@@ -461,8 +538,7 @@ class SwaigFunctionResult:
|
|
|
461
538
|
Returns:
|
|
462
539
|
self for method chaining
|
|
463
540
|
"""
|
|
464
|
-
|
|
465
|
-
return self.add_action("toggle_functions", action)
|
|
541
|
+
return self.add_action("toggle_functions", function_toggles)
|
|
466
542
|
|
|
467
543
|
def enable_functions_on_timeout(self, enabled: bool = True) -> 'SwaigFunctionResult':
|
|
468
544
|
"""
|
|
@@ -474,8 +550,7 @@ class SwaigFunctionResult:
|
|
|
474
550
|
Returns:
|
|
475
551
|
self for method chaining
|
|
476
552
|
"""
|
|
477
|
-
|
|
478
|
-
return self.add_action("functions_on_speaker_timeout", action)
|
|
553
|
+
return self.add_action("functions_on_speaker_timeout", enabled)
|
|
479
554
|
|
|
480
555
|
def enable_extensive_data(self, enabled: bool = True) -> 'SwaigFunctionResult':
|
|
481
556
|
"""
|
|
@@ -488,8 +563,7 @@ class SwaigFunctionResult:
|
|
|
488
563
|
Returns:
|
|
489
564
|
self for method chaining
|
|
490
565
|
"""
|
|
491
|
-
|
|
492
|
-
return self.add_action("extensive_data", action)
|
|
566
|
+
return self.add_action("extensive_data", enabled)
|
|
493
567
|
|
|
494
568
|
def update_settings(self, settings: Dict[str, Any]) -> 'SwaigFunctionResult':
|
|
495
569
|
"""
|
|
@@ -510,8 +584,7 @@ class SwaigFunctionResult:
|
|
|
510
584
|
Returns:
|
|
511
585
|
self for method chaining
|
|
512
586
|
"""
|
|
513
|
-
|
|
514
|
-
return self.add_action("settings", action)
|
|
587
|
+
return self.add_action("settings", settings)
|
|
515
588
|
|
|
516
589
|
def switch_context(self, system_prompt: Optional[str] = None, user_prompt: Optional[str] = None,
|
|
517
590
|
consolidate: bool = False, full_reset: bool = False) -> 'SwaigFunctionResult':
|
|
@@ -529,7 +602,7 @@ class SwaigFunctionResult:
|
|
|
529
602
|
"""
|
|
530
603
|
if system_prompt and not user_prompt and not consolidate and not full_reset:
|
|
531
604
|
# Simple string context switch
|
|
532
|
-
|
|
605
|
+
return self.add_action("context_switch", system_prompt)
|
|
533
606
|
else:
|
|
534
607
|
# Advanced object context switch
|
|
535
608
|
context_data = {}
|
|
@@ -541,9 +614,7 @@ class SwaigFunctionResult:
|
|
|
541
614
|
context_data["consolidate"] = True
|
|
542
615
|
if full_reset:
|
|
543
616
|
context_data["full_reset"] = True
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
return self.add_action("context_switch", action)
|
|
617
|
+
return self.add_action("context_switch", context_data)
|
|
547
618
|
|
|
548
619
|
def simulate_user_input(self, text: str) -> 'SwaigFunctionResult':
|
|
549
620
|
"""
|
|
@@ -555,8 +626,7 @@ class SwaigFunctionResult:
|
|
|
555
626
|
Returns:
|
|
556
627
|
self for method chaining
|
|
557
628
|
"""
|
|
558
|
-
|
|
559
|
-
return self.add_action("user_input", action)
|
|
629
|
+
return self.add_action("user_input", text)
|
|
560
630
|
|
|
561
631
|
def send_sms(self, to_number: str, from_number: str, body: Optional[str] = None,
|
|
562
632
|
media: Optional[List[str]] = None, tags: Optional[List[str]] = None,
|
|
@@ -622,7 +692,8 @@ class SwaigFunctionResult:
|
|
|
622
692
|
currency: str = "usd", language: str = "en-US", voice: str = "woman",
|
|
623
693
|
description: Optional[str] = None, valid_card_types: str = "visa mastercard amex",
|
|
624
694
|
parameters: Optional[List[Dict[str, str]]] = None,
|
|
625
|
-
prompts: Optional[List[Dict[str, Any]]] = None
|
|
695
|
+
prompts: Optional[List[Dict[str, Any]]] = None,
|
|
696
|
+
ai_response: Optional[str] = "The payment status is ${pay_result}, do not mention anything else about collecting payment if successful.") -> 'SwaigFunctionResult':
|
|
626
697
|
"""
|
|
627
698
|
Process payment using SWML pay action.
|
|
628
699
|
|
|
@@ -690,6 +761,7 @@ class SwaigFunctionResult:
|
|
|
690
761
|
"version": "1.0.0",
|
|
691
762
|
"sections": {
|
|
692
763
|
"main": [
|
|
764
|
+
{"set": {"ai_response": ai_response}},
|
|
693
765
|
{"pay": pay_params}
|
|
694
766
|
]
|
|
695
767
|
}
|
|
@@ -89,28 +89,85 @@ class StructuredLoggerWrapper:
|
|
|
89
89
|
# Also support the 'warn' alias
|
|
90
90
|
warn = warning
|
|
91
91
|
|
|
92
|
+
def bind(self, **kwargs) -> 'StructuredLoggerWrapper':
|
|
93
|
+
"""
|
|
94
|
+
Create a new logger instance with bound context data
|
|
95
|
+
|
|
96
|
+
This maintains compatibility with structlog's bind() method.
|
|
97
|
+
The bound data will be included in all subsequent log messages.
|
|
98
|
+
"""
|
|
99
|
+
# Create a new wrapper that includes the bound context
|
|
100
|
+
return BoundStructuredLoggerWrapper(self._logger, kwargs)
|
|
101
|
+
|
|
92
102
|
# Support direct access to underlying logger attributes if needed
|
|
93
103
|
def __getattr__(self, name: str) -> Any:
|
|
94
104
|
"""Delegate any unknown attributes to the underlying logger"""
|
|
95
105
|
return getattr(self._logger, name)
|
|
96
106
|
|
|
97
107
|
|
|
98
|
-
|
|
108
|
+
class BoundStructuredLoggerWrapper(StructuredLoggerWrapper):
|
|
109
|
+
"""
|
|
110
|
+
A structured logger wrapper that includes bound context data in all messages
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
def __init__(self, logger: logging.Logger, bound_data: Dict[str, Any]):
|
|
114
|
+
super().__init__(logger)
|
|
115
|
+
self._bound_data = bound_data
|
|
116
|
+
|
|
117
|
+
def _format_structured_message(self, message: str, **kwargs) -> str:
|
|
118
|
+
"""Format a message with both bound data and additional keyword arguments"""
|
|
119
|
+
# Combine bound data with additional kwargs
|
|
120
|
+
all_kwargs = {**self._bound_data, **kwargs}
|
|
121
|
+
return super()._format_structured_message(message, **all_kwargs)
|
|
122
|
+
|
|
123
|
+
def bind(self, **kwargs) -> 'BoundStructuredLoggerWrapper':
|
|
124
|
+
"""Create a new logger with additional bound context"""
|
|
125
|
+
# Combine existing bound data with new data
|
|
126
|
+
new_bound_data = {**self._bound_data, **kwargs}
|
|
127
|
+
return BoundStructuredLoggerWrapper(self._logger, new_bound_data)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def get_execution_mode():
|
|
99
131
|
"""
|
|
100
132
|
Determine the execution mode based on environment variables
|
|
101
133
|
|
|
102
134
|
Returns:
|
|
103
|
-
'cgi'
|
|
104
|
-
'lambda' if running in AWS Lambda
|
|
105
|
-
'server' for normal server mode
|
|
135
|
+
str: 'server', 'cgi', 'lambda', 'google_cloud_function', 'azure_function', or 'unknown'
|
|
106
136
|
"""
|
|
137
|
+
# Check for CGI environment
|
|
107
138
|
if os.getenv('GATEWAY_INTERFACE'):
|
|
108
139
|
return 'cgi'
|
|
140
|
+
|
|
141
|
+
# Check for AWS Lambda environment
|
|
109
142
|
if os.getenv('AWS_LAMBDA_FUNCTION_NAME') or os.getenv('LAMBDA_TASK_ROOT'):
|
|
110
143
|
return 'lambda'
|
|
144
|
+
|
|
145
|
+
# Check for Google Cloud Functions environment
|
|
146
|
+
if (os.getenv('FUNCTION_TARGET') or
|
|
147
|
+
os.getenv('K_SERVICE') or
|
|
148
|
+
os.getenv('GOOGLE_CLOUD_PROJECT')):
|
|
149
|
+
return 'google_cloud_function'
|
|
150
|
+
|
|
151
|
+
# Check for Azure Functions environment
|
|
152
|
+
if (os.getenv('AZURE_FUNCTIONS_ENVIRONMENT') or
|
|
153
|
+
os.getenv('FUNCTIONS_WORKER_RUNTIME') or
|
|
154
|
+
os.getenv('AzureWebJobsStorage')):
|
|
155
|
+
return 'azure_function'
|
|
156
|
+
|
|
157
|
+
# Default to server mode
|
|
111
158
|
return 'server'
|
|
112
159
|
|
|
113
160
|
|
|
161
|
+
def reset_logging_configuration():
|
|
162
|
+
"""
|
|
163
|
+
Reset the logging configuration flag to allow reconfiguration
|
|
164
|
+
|
|
165
|
+
This is useful when environment variables change after initial configuration.
|
|
166
|
+
"""
|
|
167
|
+
global _logging_configured
|
|
168
|
+
_logging_configured = False
|
|
169
|
+
|
|
170
|
+
|
|
114
171
|
def configure_logging():
|
|
115
172
|
"""
|
|
116
173
|
Configure logging system once, globally, based on environment variables
|
|
@@ -182,31 +239,39 @@ def _configure_off_mode():
|
|
|
182
239
|
|
|
183
240
|
|
|
184
241
|
def _configure_stderr_mode(log_level: str):
|
|
185
|
-
"""Configure logging to stderr"""
|
|
242
|
+
"""Configure logging to stderr with colored formatting"""
|
|
186
243
|
# Clear existing handlers
|
|
187
244
|
logging.getLogger().handlers.clear()
|
|
188
245
|
|
|
189
246
|
# Convert log level
|
|
190
247
|
numeric_level = getattr(logging, log_level.upper(), logging.INFO)
|
|
191
248
|
|
|
192
|
-
#
|
|
193
|
-
logging.
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
)
|
|
249
|
+
# Create handler with colored formatter
|
|
250
|
+
handler = logging.StreamHandler(sys.stderr)
|
|
251
|
+
handler.setFormatter(ColoredFormatter())
|
|
252
|
+
|
|
253
|
+
# Configure root logger
|
|
254
|
+
root_logger = logging.getLogger()
|
|
255
|
+
root_logger.setLevel(numeric_level)
|
|
256
|
+
root_logger.addHandler(handler)
|
|
198
257
|
|
|
199
258
|
|
|
200
259
|
def _configure_default_mode(log_level: str):
|
|
201
|
-
"""Configure standard logging behavior"""
|
|
260
|
+
"""Configure standard logging behavior with colored formatting"""
|
|
261
|
+
# Clear existing handlers
|
|
262
|
+
logging.getLogger().handlers.clear()
|
|
263
|
+
|
|
202
264
|
# Convert log level
|
|
203
265
|
numeric_level = getattr(logging, log_level.upper(), logging.INFO)
|
|
204
266
|
|
|
205
|
-
#
|
|
206
|
-
logging.
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
267
|
+
# Create handler with colored formatter
|
|
268
|
+
handler = logging.StreamHandler()
|
|
269
|
+
handler.setFormatter(ColoredFormatter())
|
|
270
|
+
|
|
271
|
+
# Configure root logger
|
|
272
|
+
root_logger = logging.getLogger()
|
|
273
|
+
root_logger.setLevel(numeric_level)
|
|
274
|
+
root_logger.addHandler(handler)
|
|
210
275
|
|
|
211
276
|
|
|
212
277
|
def get_logger(name: str) -> StructuredLoggerWrapper:
|
|
@@ -229,4 +294,83 @@ def get_logger(name: str) -> StructuredLoggerWrapper:
|
|
|
229
294
|
python_logger = logging.getLogger(name)
|
|
230
295
|
|
|
231
296
|
# Wrap it with our structured logging interface
|
|
232
|
-
return StructuredLoggerWrapper(python_logger)
|
|
297
|
+
return StructuredLoggerWrapper(python_logger)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
class ColoredFormatter(logging.Formatter):
|
|
301
|
+
"""
|
|
302
|
+
A beautiful colored logging formatter that makes logs easy to read and visually appealing
|
|
303
|
+
"""
|
|
304
|
+
|
|
305
|
+
# ANSI color codes
|
|
306
|
+
COLORS = {
|
|
307
|
+
'DEBUG': '\033[36m', # Cyan
|
|
308
|
+
'INFO': '\033[32m', # Green
|
|
309
|
+
'WARNING': '\033[33m', # Yellow
|
|
310
|
+
'ERROR': '\033[31m', # Red
|
|
311
|
+
'CRITICAL': '\033[35m', # Magenta
|
|
312
|
+
'RESET': '\033[0m', # Reset
|
|
313
|
+
'BOLD': '\033[1m', # Bold
|
|
314
|
+
'DIM': '\033[2m', # Dim
|
|
315
|
+
'WHITE': '\033[37m', # White
|
|
316
|
+
'BLUE': '\033[34m', # Blue
|
|
317
|
+
'BLACK': '\033[30m', # Black (for brackets)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
def __init__(self):
|
|
321
|
+
super().__init__()
|
|
322
|
+
|
|
323
|
+
def format(self, record):
|
|
324
|
+
# Check if we should use colors (not in raw mode, and stdout is a tty)
|
|
325
|
+
use_colors = (
|
|
326
|
+
hasattr(sys.stdout, 'isatty') and sys.stdout.isatty() and
|
|
327
|
+
os.getenv('SIGNALWIRE_LOG_MODE') != 'off' and
|
|
328
|
+
'--raw' not in sys.argv and '--dump-swml' not in sys.argv
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
if use_colors:
|
|
332
|
+
# Get colors
|
|
333
|
+
level_color = self.COLORS.get(record.levelname, self.COLORS['WHITE'])
|
|
334
|
+
reset = self.COLORS['RESET']
|
|
335
|
+
dim = self.COLORS['DIM']
|
|
336
|
+
bold = self.COLORS['BOLD']
|
|
337
|
+
blue = self.COLORS['BLUE']
|
|
338
|
+
black = self.COLORS['BLACK']
|
|
339
|
+
|
|
340
|
+
# Format timestamp in a compact, readable way
|
|
341
|
+
timestamp = self.formatTime(record, '%H:%M:%S')
|
|
342
|
+
|
|
343
|
+
# Format level with appropriate color and consistent width
|
|
344
|
+
level_name = f"{level_color}{record.levelname:<8}{reset}"
|
|
345
|
+
|
|
346
|
+
# Format logger name - keep it short and readable
|
|
347
|
+
logger_name = record.name
|
|
348
|
+
if len(logger_name) > 15:
|
|
349
|
+
# Truncate long logger names but keep the end (most specific part)
|
|
350
|
+
logger_name = "..." + logger_name[-12:]
|
|
351
|
+
|
|
352
|
+
# Get function and line info if available
|
|
353
|
+
func_info = ""
|
|
354
|
+
if hasattr(record, 'funcName') and hasattr(record, 'lineno'):
|
|
355
|
+
func_name = getattr(record, 'funcName', '')
|
|
356
|
+
line_no = getattr(record, 'lineno', 0)
|
|
357
|
+
if func_name and func_name != '<module>':
|
|
358
|
+
func_info = f" {dim}({func_name}:{line_no}){reset}"
|
|
359
|
+
|
|
360
|
+
# Format the message
|
|
361
|
+
message = record.getMessage()
|
|
362
|
+
|
|
363
|
+
# Create the final formatted message with a clean, readable layout
|
|
364
|
+
formatted = (
|
|
365
|
+
f"{black}[{reset}{dim}{timestamp}{reset}{black}]{reset} "
|
|
366
|
+
f"{level_name} "
|
|
367
|
+
f"{blue}{logger_name:<15}{reset}"
|
|
368
|
+
f"{func_info} "
|
|
369
|
+
f"{message}"
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
return formatted
|
|
373
|
+
else:
|
|
374
|
+
# Non-colored format (fallback for files, pipes, etc.)
|
|
375
|
+
timestamp = self.formatTime(record, '%Y-%m-%d %H:%M:%S')
|
|
376
|
+
return f"{timestamp} {record.levelname:<8} {record.name} {record.getMessage()}"
|
|
@@ -0,0 +1,28 @@
|
|
|
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
|
+
from .prompt_mixin import PromptMixin
|
|
11
|
+
from .tool_mixin import ToolMixin
|
|
12
|
+
from .web_mixin import WebMixin
|
|
13
|
+
from .auth_mixin import AuthMixin
|
|
14
|
+
from .skill_mixin import SkillMixin
|
|
15
|
+
from .ai_config_mixin import AIConfigMixin
|
|
16
|
+
from .serverless_mixin import ServerlessMixin
|
|
17
|
+
from .state_mixin import StateMixin
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
'PromptMixin',
|
|
21
|
+
'ToolMixin',
|
|
22
|
+
'WebMixin',
|
|
23
|
+
'AuthMixin',
|
|
24
|
+
'SkillMixin',
|
|
25
|
+
'AIConfigMixin',
|
|
26
|
+
'ServerlessMixin',
|
|
27
|
+
'StateMixin'
|
|
28
|
+
]
|