signalwire-agents 1.0.3__py3-none-any.whl → 1.0.4__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 +1 -1
- signalwire_agents/core/agent_base.py +221 -14
- {signalwire_agents-1.0.3.dist-info → signalwire_agents-1.0.4.dist-info}/METADATA +1 -1
- {signalwire_agents-1.0.3.dist-info → signalwire_agents-1.0.4.dist-info}/RECORD +8 -8
- {signalwire_agents-1.0.3.dist-info → signalwire_agents-1.0.4.dist-info}/WHEEL +0 -0
- {signalwire_agents-1.0.3.dist-info → signalwire_agents-1.0.4.dist-info}/entry_points.txt +0 -0
- {signalwire_agents-1.0.3.dist-info → signalwire_agents-1.0.4.dist-info}/licenses/LICENSE +0 -0
- {signalwire_agents-1.0.3.dist-info → signalwire_agents-1.0.4.dist-info}/top_level.txt +0 -0
signalwire_agents/__init__.py
CHANGED
|
@@ -18,7 +18,7 @@ A package for building AI agents using SignalWire's AI and SWML capabilities.
|
|
|
18
18
|
from .core.logging_config import configure_logging
|
|
19
19
|
configure_logging()
|
|
20
20
|
|
|
21
|
-
__version__ = "1.0.
|
|
21
|
+
__version__ = "1.0.4"
|
|
22
22
|
|
|
23
23
|
# Import core classes for easier access
|
|
24
24
|
from .core.agent_base import AgentBase
|
|
@@ -266,7 +266,21 @@ class AgentBase(
|
|
|
266
266
|
|
|
267
267
|
# Initialize SWAIG query params for dynamic config
|
|
268
268
|
self._swaig_query_params = {}
|
|
269
|
-
|
|
269
|
+
|
|
270
|
+
# Initialize verb insertion points for call flow customization
|
|
271
|
+
self._pre_answer_verbs = [] # Verbs to run before answer (e.g., ringback, screening)
|
|
272
|
+
self._answer_config = {} # Configuration for the answer verb
|
|
273
|
+
self._post_answer_verbs = [] # Verbs to run after answer, before AI (e.g., announcements)
|
|
274
|
+
self._post_ai_verbs = [] # Verbs to run after AI ends (e.g., cleanup, transfers)
|
|
275
|
+
|
|
276
|
+
# Verb categories for pre-answer validation
|
|
277
|
+
_PRE_ANSWER_SAFE_VERBS = {
|
|
278
|
+
"transfer", "execute", "return", "label", "goto", "request",
|
|
279
|
+
"switch", "cond", "if", "eval", "set", "unset", "hangup",
|
|
280
|
+
"send_sms", "sleep", "stop_record_call", "stop_denoise", "stop_tap"
|
|
281
|
+
}
|
|
282
|
+
_AUTO_ANSWER_VERBS = {"play", "connect"}
|
|
283
|
+
|
|
270
284
|
@staticmethod
|
|
271
285
|
def _load_service_config(config_file: Optional[str], service_name: str) -> dict:
|
|
272
286
|
"""Load service configuration from config file if available"""
|
|
@@ -380,14 +394,166 @@ class AgentBase(
|
|
|
380
394
|
def on_summary(self, summary: Optional[Dict[str, Any]], raw_data: Optional[Dict[str, Any]] = None) -> None:
|
|
381
395
|
"""
|
|
382
396
|
Called when a post-prompt summary is received
|
|
383
|
-
|
|
397
|
+
|
|
384
398
|
Args:
|
|
385
399
|
summary: The summary object or None if no summary was found
|
|
386
400
|
raw_data: The complete raw POST data from the request
|
|
387
401
|
"""
|
|
388
402
|
# Default implementation does nothing
|
|
389
403
|
pass
|
|
390
|
-
|
|
404
|
+
|
|
405
|
+
# ==================== Call Flow Verb Insertion Methods ====================
|
|
406
|
+
|
|
407
|
+
def add_pre_answer_verb(self, verb_name: str, config: Dict[str, Any]) -> 'AgentBase':
|
|
408
|
+
"""
|
|
409
|
+
Add a verb to run before the call is answered.
|
|
410
|
+
|
|
411
|
+
Pre-answer verbs execute while the call is still ringing. Only certain
|
|
412
|
+
verbs are safe to use before answering:
|
|
413
|
+
|
|
414
|
+
Safe verbs: transfer, execute, return, label, goto, request, switch,
|
|
415
|
+
cond, if, eval, set, unset, hangup, send_sms, sleep,
|
|
416
|
+
stop_record_call, stop_denoise, stop_tap
|
|
417
|
+
|
|
418
|
+
Verbs with auto_answer option (play, connect): Must include
|
|
419
|
+
"auto_answer": False in config to prevent automatic answering.
|
|
420
|
+
|
|
421
|
+
Args:
|
|
422
|
+
verb_name: The SWML verb name (e.g., "play", "sleep", "request")
|
|
423
|
+
config: Verb configuration dictionary
|
|
424
|
+
|
|
425
|
+
Returns:
|
|
426
|
+
Self for method chaining
|
|
427
|
+
|
|
428
|
+
Raises:
|
|
429
|
+
ValueError: If verb is not safe for pre-answer use
|
|
430
|
+
|
|
431
|
+
Example:
|
|
432
|
+
# Play ringback tone before answering
|
|
433
|
+
agent.add_pre_answer_verb("play", {
|
|
434
|
+
"urls": ["ring:us"],
|
|
435
|
+
"auto_answer": False
|
|
436
|
+
})
|
|
437
|
+
"""
|
|
438
|
+
# Validate verb is safe for pre-answer use
|
|
439
|
+
if verb_name in self._AUTO_ANSWER_VERBS:
|
|
440
|
+
if not config.get("auto_answer") is False:
|
|
441
|
+
self.log.warning(
|
|
442
|
+
"pre_answer_verb_will_answer",
|
|
443
|
+
verb=verb_name,
|
|
444
|
+
hint=f"Add 'auto_answer': False to prevent {verb_name} from answering the call"
|
|
445
|
+
)
|
|
446
|
+
elif verb_name not in self._PRE_ANSWER_SAFE_VERBS:
|
|
447
|
+
raise ValueError(
|
|
448
|
+
f"Verb '{verb_name}' is not safe for pre-answer use. "
|
|
449
|
+
f"Safe verbs: {', '.join(sorted(self._PRE_ANSWER_SAFE_VERBS))}"
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
self._pre_answer_verbs.append((verb_name, config))
|
|
453
|
+
return self
|
|
454
|
+
|
|
455
|
+
def add_answer_verb(self, config: Optional[Dict[str, Any]] = None) -> 'AgentBase':
|
|
456
|
+
"""
|
|
457
|
+
Configure the answer verb.
|
|
458
|
+
|
|
459
|
+
The answer verb connects the call. Use this method to customize
|
|
460
|
+
answer behavior, such as setting max_duration.
|
|
461
|
+
|
|
462
|
+
Args:
|
|
463
|
+
config: Optional answer verb configuration (e.g., {"max_duration": 3600})
|
|
464
|
+
|
|
465
|
+
Returns:
|
|
466
|
+
Self for method chaining
|
|
467
|
+
|
|
468
|
+
Example:
|
|
469
|
+
# Set maximum call duration to 1 hour
|
|
470
|
+
agent.add_answer_verb({"max_duration": 3600})
|
|
471
|
+
"""
|
|
472
|
+
self._answer_config = config or {}
|
|
473
|
+
return self
|
|
474
|
+
|
|
475
|
+
def add_post_answer_verb(self, verb_name: str, config: Dict[str, Any]) -> 'AgentBase':
|
|
476
|
+
"""
|
|
477
|
+
Add a verb to run after the call is answered but before the AI starts.
|
|
478
|
+
|
|
479
|
+
Post-answer verbs run after the call is connected. Common uses include
|
|
480
|
+
welcome messages, legal disclaimers, and hold music.
|
|
481
|
+
|
|
482
|
+
Args:
|
|
483
|
+
verb_name: The SWML verb name (e.g., "play", "sleep")
|
|
484
|
+
config: Verb configuration dictionary
|
|
485
|
+
|
|
486
|
+
Returns:
|
|
487
|
+
Self for method chaining
|
|
488
|
+
|
|
489
|
+
Example:
|
|
490
|
+
# Play welcome message
|
|
491
|
+
agent.add_post_answer_verb("play", {
|
|
492
|
+
"url": "say:Welcome to Acme Corporation."
|
|
493
|
+
})
|
|
494
|
+
# Brief pause
|
|
495
|
+
agent.add_post_answer_verb("sleep", {"time": 500})
|
|
496
|
+
"""
|
|
497
|
+
self._post_answer_verbs.append((verb_name, config))
|
|
498
|
+
return self
|
|
499
|
+
|
|
500
|
+
def add_post_ai_verb(self, verb_name: str, config: Dict[str, Any]) -> 'AgentBase':
|
|
501
|
+
"""
|
|
502
|
+
Add a verb to run after the AI conversation ends.
|
|
503
|
+
|
|
504
|
+
Post-AI verbs run when the AI completes its conversation. Common uses
|
|
505
|
+
include clean disconnects, transfers, and logging.
|
|
506
|
+
|
|
507
|
+
Args:
|
|
508
|
+
verb_name: The SWML verb name (e.g., "hangup", "transfer", "request")
|
|
509
|
+
config: Verb configuration dictionary
|
|
510
|
+
|
|
511
|
+
Returns:
|
|
512
|
+
Self for method chaining
|
|
513
|
+
|
|
514
|
+
Example:
|
|
515
|
+
# Log call completion and hang up
|
|
516
|
+
agent.add_post_ai_verb("request", {
|
|
517
|
+
"url": "https://api.example.com/call-complete",
|
|
518
|
+
"method": "POST"
|
|
519
|
+
})
|
|
520
|
+
agent.add_post_ai_verb("hangup", {})
|
|
521
|
+
"""
|
|
522
|
+
self._post_ai_verbs.append((verb_name, config))
|
|
523
|
+
return self
|
|
524
|
+
|
|
525
|
+
def clear_pre_answer_verbs(self) -> 'AgentBase':
|
|
526
|
+
"""
|
|
527
|
+
Remove all pre-answer verbs.
|
|
528
|
+
|
|
529
|
+
Returns:
|
|
530
|
+
Self for method chaining
|
|
531
|
+
"""
|
|
532
|
+
self._pre_answer_verbs = []
|
|
533
|
+
return self
|
|
534
|
+
|
|
535
|
+
def clear_post_answer_verbs(self) -> 'AgentBase':
|
|
536
|
+
"""
|
|
537
|
+
Remove all post-answer verbs.
|
|
538
|
+
|
|
539
|
+
Returns:
|
|
540
|
+
Self for method chaining
|
|
541
|
+
"""
|
|
542
|
+
self._post_answer_verbs = []
|
|
543
|
+
return self
|
|
544
|
+
|
|
545
|
+
def clear_post_ai_verbs(self) -> 'AgentBase':
|
|
546
|
+
"""
|
|
547
|
+
Remove all post-AI verbs.
|
|
548
|
+
|
|
549
|
+
Returns:
|
|
550
|
+
Self for method chaining
|
|
551
|
+
"""
|
|
552
|
+
self._post_ai_verbs = []
|
|
553
|
+
return self
|
|
554
|
+
|
|
555
|
+
# ==================== End Call Flow Verb Insertion Methods ====================
|
|
556
|
+
|
|
391
557
|
def enable_sip_routing(self, auto_map: bool = True, path: str = "/sip") -> 'AgentBase':
|
|
392
558
|
"""
|
|
393
559
|
Enable SIP-based routing for this agent
|
|
@@ -746,15 +912,29 @@ class AgentBase(
|
|
|
746
912
|
if hasattr(agent_to_use, '_post_prompt_url_override') and agent_to_use._post_prompt_url_override:
|
|
747
913
|
post_prompt_url = agent_to_use._post_prompt_url_override
|
|
748
914
|
|
|
749
|
-
#
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
915
|
+
# ========== PHASE 1: PRE-ANSWER VERBS ==========
|
|
916
|
+
# These run while the call is still ringing
|
|
917
|
+
for verb_name, verb_config in agent_to_use._pre_answer_verbs:
|
|
918
|
+
agent_to_use.add_verb(verb_name, verb_config)
|
|
919
|
+
|
|
920
|
+
# ========== PHASE 2: ANSWER VERB ==========
|
|
921
|
+
# Only add answer verb if auto_answer is enabled
|
|
922
|
+
if agent_to_use._auto_answer:
|
|
923
|
+
agent_to_use.add_verb("answer", agent_to_use._answer_config)
|
|
924
|
+
|
|
925
|
+
# ========== PHASE 3: POST-ANSWER VERBS ==========
|
|
926
|
+
# These run after answer but before AI
|
|
927
|
+
|
|
928
|
+
# Add recording if enabled (this is a post-answer verb)
|
|
753
929
|
if agent_to_use._record_call:
|
|
754
930
|
agent_to_use.add_verb("record_call", {
|
|
755
931
|
"format": agent_to_use._record_format,
|
|
756
932
|
"stereo": agent_to_use._record_stereo
|
|
757
933
|
})
|
|
934
|
+
|
|
935
|
+
# Add user-defined post-answer verbs
|
|
936
|
+
for verb_name, verb_config in agent_to_use._post_answer_verbs:
|
|
937
|
+
agent_to_use.add_verb(verb_name, verb_config)
|
|
758
938
|
|
|
759
939
|
# Use the AI verb handler to build and validate the AI verb config
|
|
760
940
|
ai_config = {}
|
|
@@ -881,10 +1061,16 @@ class AgentBase(
|
|
|
881
1061
|
|
|
882
1062
|
if agent_to_use._global_data and "global_data" not in ai_config:
|
|
883
1063
|
ai_config["global_data"] = agent_to_use._global_data
|
|
884
|
-
|
|
1064
|
+
|
|
1065
|
+
# ========== PHASE 4: AI VERB ==========
|
|
885
1066
|
# Add the AI verb to the document
|
|
886
1067
|
agent_to_use.add_verb("ai", ai_config)
|
|
887
|
-
|
|
1068
|
+
|
|
1069
|
+
# ========== PHASE 5: POST-AI VERBS ==========
|
|
1070
|
+
# These run after the AI conversation ends
|
|
1071
|
+
for verb_name, verb_config in agent_to_use._post_ai_verbs:
|
|
1072
|
+
agent_to_use.add_verb(verb_name, verb_config)
|
|
1073
|
+
|
|
888
1074
|
# Apply any modifications from the callback to agent state
|
|
889
1075
|
if modifications and isinstance(modifications, dict):
|
|
890
1076
|
# Handle global_data modifications by updating the AI config directly
|
|
@@ -900,16 +1086,31 @@ class AgentBase(
|
|
|
900
1086
|
|
|
901
1087
|
# Clear and rebuild the document with the modified AI config
|
|
902
1088
|
agent_to_use.reset_document()
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
#
|
|
1089
|
+
|
|
1090
|
+
# Rebuild with 5-phase approach
|
|
1091
|
+
# PHASE 1: Pre-answer verbs
|
|
1092
|
+
for verb_name, verb_config in agent_to_use._pre_answer_verbs:
|
|
1093
|
+
agent_to_use.add_verb(verb_name, verb_config)
|
|
1094
|
+
|
|
1095
|
+
# PHASE 2: Answer verb (if auto_answer enabled)
|
|
1096
|
+
if agent_to_use._auto_answer:
|
|
1097
|
+
agent_to_use.add_verb("answer", agent_to_use._answer_config)
|
|
1098
|
+
|
|
1099
|
+
# PHASE 3: Post-answer verbs
|
|
906
1100
|
if agent_to_use._record_call:
|
|
907
1101
|
agent_to_use.add_verb("record_call", {
|
|
908
1102
|
"format": agent_to_use._record_format,
|
|
909
1103
|
"stereo": agent_to_use._record_stereo
|
|
910
1104
|
})
|
|
911
|
-
|
|
1105
|
+
for verb_name, verb_config in agent_to_use._post_answer_verbs:
|
|
1106
|
+
agent_to_use.add_verb(verb_name, verb_config)
|
|
1107
|
+
|
|
1108
|
+
# PHASE 4: AI verb
|
|
912
1109
|
agent_to_use.add_verb("ai", ai_config)
|
|
1110
|
+
|
|
1111
|
+
# PHASE 5: Post-AI verbs
|
|
1112
|
+
for verb_name, verb_config in agent_to_use._post_ai_verbs:
|
|
1113
|
+
agent_to_use.add_verb(verb_name, verb_config)
|
|
913
1114
|
|
|
914
1115
|
# Return the rendered document as a string
|
|
915
1116
|
return agent_to_use.render_document()
|
|
@@ -1015,7 +1216,13 @@ class AgentBase(
|
|
|
1015
1216
|
ephemeral_agent._pronounce = copy.deepcopy(self._pronounce)
|
|
1016
1217
|
ephemeral_agent._global_data = copy.deepcopy(self._global_data)
|
|
1017
1218
|
ephemeral_agent._function_includes = copy.deepcopy(self._function_includes)
|
|
1018
|
-
|
|
1219
|
+
|
|
1220
|
+
# Deep copy verb insertion points for call flow customization
|
|
1221
|
+
ephemeral_agent._pre_answer_verbs = copy.deepcopy(self._pre_answer_verbs)
|
|
1222
|
+
ephemeral_agent._answer_config = copy.deepcopy(self._answer_config)
|
|
1223
|
+
ephemeral_agent._post_answer_verbs = copy.deepcopy(self._post_answer_verbs)
|
|
1224
|
+
ephemeral_agent._post_ai_verbs = copy.deepcopy(self._post_ai_verbs)
|
|
1225
|
+
|
|
1019
1226
|
# Deep copy LLM parameters
|
|
1020
1227
|
ephemeral_agent._prompt_llm_params = copy.deepcopy(self._prompt_llm_params)
|
|
1021
1228
|
ephemeral_agent._post_prompt_llm_params = copy.deepcopy(self._post_prompt_llm_params)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
signalwire_agents/__init__.py,sha256=
|
|
1
|
+
signalwire_agents/__init__.py,sha256=5qVGVe2EtGqEnTF9lP_URjauF3RmWdj8LK0rGhhmnVo,5030
|
|
2
2
|
signalwire_agents/agent_server.py,sha256=5GPt6XekVxep9esZY9s4MG6sOR-VF_m53U25r3WoML4,30964
|
|
3
3
|
signalwire_agents/schema.json,sha256=YQv4-KiegE00XvxoLMKAml6aCGitnt3kBq31ECxTHK8,385886
|
|
4
4
|
signalwire_agents/agents/bedrock.py,sha256=J582gooNtxtep4xdVOfyDzRtHp_XrurPMS93xf2Xod0,10836
|
|
@@ -24,7 +24,7 @@ signalwire_agents/cli/simulation/data_generation.py,sha256=pxa9aJ6XkI0O8yAIGvBTU
|
|
|
24
24
|
signalwire_agents/cli/simulation/data_overrides.py,sha256=3_3pT6j-q2gRufPX2bZ1BrmY7u1IdloLooKAJil33vI,6319
|
|
25
25
|
signalwire_agents/cli/simulation/mock_env.py,sha256=fvaR_xdLMm8AbpNUbTJOFG9THcti3Zds-0QNDbKMaYk,10249
|
|
26
26
|
signalwire_agents/core/__init__.py,sha256=xjPq8DmUnWYUG28sd17n430VWPmMH9oZ9W14gYwG96g,806
|
|
27
|
-
signalwire_agents/core/agent_base.py,sha256=
|
|
27
|
+
signalwire_agents/core/agent_base.py,sha256=oNcFZdEXfyD9bG1GlUVWLrNamHOuLLIZMPnKtDNB0zo,57806
|
|
28
28
|
signalwire_agents/core/auth_handler.py,sha256=jXrof9WZ1W9qqlQT9WElcmSRafL2kG7207x5SqWN9MU,8481
|
|
29
29
|
signalwire_agents/core/config_loader.py,sha256=rStVRRUaeMGrMc44ocr0diMQQARZhbKqwMqQ6kqUNos,8722
|
|
30
30
|
signalwire_agents/core/contexts.py,sha256=g9FgOGMfGCUWlm57YZcv7CvOf-Ub9FdKZIOMu14ADfE,24428
|
|
@@ -130,9 +130,9 @@ signalwire_agents/utils/token_generators.py,sha256=4Mr7baQ_xR_hfJ72YxQRAT_GFa663
|
|
|
130
130
|
signalwire_agents/utils/validators.py,sha256=4Mr7baQ_xR_hfJ72YxQRAT_GFa663YjFX_PumJ35Xds,191
|
|
131
131
|
signalwire_agents/web/__init__.py,sha256=XE_pSTY9Aalzr7J7wqFth1Zr3cccQHPPcF5HWNrOpz8,383
|
|
132
132
|
signalwire_agents/web/web_service.py,sha256=a2PSHJgX1tlZr0Iz1A1UouZjXEePJAZL632evvLVM38,21071
|
|
133
|
-
signalwire_agents-1.0.
|
|
134
|
-
signalwire_agents-1.0.
|
|
135
|
-
signalwire_agents-1.0.
|
|
136
|
-
signalwire_agents-1.0.
|
|
137
|
-
signalwire_agents-1.0.
|
|
138
|
-
signalwire_agents-1.0.
|
|
133
|
+
signalwire_agents-1.0.4.dist-info/licenses/LICENSE,sha256=NYvAsB-rTcSvG9cqHt9EUHAWLiA9YzM4Qfz-mPdvDR0,1067
|
|
134
|
+
signalwire_agents-1.0.4.dist-info/METADATA,sha256=tmkDuZ-fhZU2ezcahNLz4IRXf4SrXVMPk-SEcw5vohk,41595
|
|
135
|
+
signalwire_agents-1.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
136
|
+
signalwire_agents-1.0.4.dist-info/entry_points.txt,sha256=ZDT65zfTO_YyDzi_hwQbCxIhrUfu_t8RpNXMMXlUPWI,144
|
|
137
|
+
signalwire_agents-1.0.4.dist-info/top_level.txt,sha256=kDGS6ZYv84K9P5Kyg9_S8P_pbUXoHkso0On_DB5bbWc,18
|
|
138
|
+
signalwire_agents-1.0.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|