signalwire-agents 0.1.0__py3-none-any.whl → 0.1.1__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 +10 -1
- signalwire_agents/agent_server.py +73 -44
- {signalwire_agents-0.1.0.dist-info → signalwire_agents-0.1.1.dist-info}/METADATA +75 -30
- signalwire_agents-0.1.1.dist-info/RECORD +9 -0
- {signalwire_agents-0.1.0.dist-info → signalwire_agents-0.1.1.dist-info}/WHEEL +1 -1
- signalwire_agents-0.1.1.dist-info/licenses/LICENSE +21 -0
- signalwire_agents/core/__init__.py +0 -20
- signalwire_agents/core/agent_base.py +0 -2449
- signalwire_agents/core/function_result.py +0 -104
- signalwire_agents/core/pom_builder.py +0 -195
- signalwire_agents/core/security/__init__.py +0 -0
- signalwire_agents/core/security/session_manager.py +0 -170
- signalwire_agents/core/state/__init__.py +0 -8
- signalwire_agents/core/state/file_state_manager.py +0 -210
- signalwire_agents/core/state/state_manager.py +0 -92
- signalwire_agents/core/swaig_function.py +0 -163
- signalwire_agents/core/swml_builder.py +0 -205
- signalwire_agents/core/swml_handler.py +0 -218
- signalwire_agents/core/swml_renderer.py +0 -359
- signalwire_agents/core/swml_service.py +0 -1009
- signalwire_agents/prefabs/__init__.py +0 -15
- signalwire_agents/prefabs/concierge.py +0 -276
- signalwire_agents/prefabs/faq_bot.py +0 -314
- signalwire_agents/prefabs/info_gatherer.py +0 -253
- signalwire_agents/prefabs/survey.py +0 -387
- signalwire_agents/utils/__init__.py +0 -0
- signalwire_agents/utils/pom_utils.py +0 -0
- signalwire_agents/utils/schema_utils.py +0 -348
- signalwire_agents/utils/token_generators.py +0 -0
- signalwire_agents/utils/validators.py +0 -0
- signalwire_agents-0.1.0.dist-info/RECORD +0 -32
- {signalwire_agents-0.1.0.data → signalwire_agents-0.1.1.data}/data/schema.json +0 -0
- {signalwire_agents-0.1.0.dist-info → signalwire_agents-0.1.1.dist-info}/top_level.txt +0 -0
@@ -1,92 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Abstract base class for state management
|
3
|
-
"""
|
4
|
-
|
5
|
-
from abc import ABC, abstractmethod
|
6
|
-
from typing import Dict, Any, Optional
|
7
|
-
|
8
|
-
|
9
|
-
class StateManager(ABC):
|
10
|
-
"""
|
11
|
-
Abstract base class for state management
|
12
|
-
|
13
|
-
This defines the interface that all state manager implementations
|
14
|
-
must follow. State managers are responsible for storing, retrieving,
|
15
|
-
and managing call-specific state data.
|
16
|
-
"""
|
17
|
-
|
18
|
-
@abstractmethod
|
19
|
-
def store(self, call_id: str, data: Dict[str, Any]) -> bool:
|
20
|
-
"""
|
21
|
-
Store state data for a call
|
22
|
-
|
23
|
-
Args:
|
24
|
-
call_id: Unique identifier for the call
|
25
|
-
data: Dictionary of state data to store
|
26
|
-
|
27
|
-
Returns:
|
28
|
-
True if successful, False otherwise
|
29
|
-
"""
|
30
|
-
pass
|
31
|
-
|
32
|
-
@abstractmethod
|
33
|
-
def retrieve(self, call_id: str) -> Optional[Dict[str, Any]]:
|
34
|
-
"""
|
35
|
-
Retrieve state data for a call
|
36
|
-
|
37
|
-
Args:
|
38
|
-
call_id: Unique identifier for the call
|
39
|
-
|
40
|
-
Returns:
|
41
|
-
Dictionary of state data or None if not found
|
42
|
-
"""
|
43
|
-
pass
|
44
|
-
|
45
|
-
@abstractmethod
|
46
|
-
def update(self, call_id: str, data: Dict[str, Any]) -> bool:
|
47
|
-
"""
|
48
|
-
Update state data for a call
|
49
|
-
|
50
|
-
Args:
|
51
|
-
call_id: Unique identifier for the call
|
52
|
-
data: Dictionary of state data to update (merged with existing)
|
53
|
-
|
54
|
-
Returns:
|
55
|
-
True if successful, False otherwise
|
56
|
-
"""
|
57
|
-
pass
|
58
|
-
|
59
|
-
@abstractmethod
|
60
|
-
def delete(self, call_id: str) -> bool:
|
61
|
-
"""
|
62
|
-
Delete state data for a call
|
63
|
-
|
64
|
-
Args:
|
65
|
-
call_id: Unique identifier for the call
|
66
|
-
|
67
|
-
Returns:
|
68
|
-
True if successful, False otherwise
|
69
|
-
"""
|
70
|
-
pass
|
71
|
-
|
72
|
-
@abstractmethod
|
73
|
-
def cleanup_expired(self) -> int:
|
74
|
-
"""
|
75
|
-
Clean up expired state data
|
76
|
-
|
77
|
-
Returns:
|
78
|
-
Number of expired items cleaned up
|
79
|
-
"""
|
80
|
-
pass
|
81
|
-
|
82
|
-
def exists(self, call_id: str) -> bool:
|
83
|
-
"""
|
84
|
-
Check if state exists for a call
|
85
|
-
|
86
|
-
Args:
|
87
|
-
call_id: Unique identifier for the call
|
88
|
-
|
89
|
-
Returns:
|
90
|
-
True if state exists, False otherwise
|
91
|
-
"""
|
92
|
-
return self.retrieve(call_id) is not None
|
@@ -1,163 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
SwaigFunction class for defining and managing SWAIG function interfaces
|
3
|
-
"""
|
4
|
-
|
5
|
-
from typing import Dict, Any, Optional, Callable, List, Type, Union
|
6
|
-
import inspect
|
7
|
-
import logging
|
8
|
-
|
9
|
-
|
10
|
-
class SWAIGFunction:
|
11
|
-
"""
|
12
|
-
Represents a SWAIG function for AI integration
|
13
|
-
"""
|
14
|
-
def __init__(
|
15
|
-
self,
|
16
|
-
name: str,
|
17
|
-
handler: Callable,
|
18
|
-
description: str,
|
19
|
-
parameters: Dict[str, Dict] = None,
|
20
|
-
secure: bool = False,
|
21
|
-
fillers: Optional[Dict[str, List[str]]] = None
|
22
|
-
):
|
23
|
-
"""
|
24
|
-
Initialize a new SWAIG function
|
25
|
-
|
26
|
-
Args:
|
27
|
-
name: Name of the function to appear in SWML
|
28
|
-
handler: Function to call when this SWAIG function is invoked
|
29
|
-
description: Human-readable description of the function
|
30
|
-
parameters: Dictionary of parameters, keys are parameter names, values are param definitions
|
31
|
-
secure: Whether this function requires token validation
|
32
|
-
fillers: Optional dictionary of filler phrases by language code
|
33
|
-
"""
|
34
|
-
self.name = name
|
35
|
-
self.handler = handler
|
36
|
-
self.description = description
|
37
|
-
self.parameters = parameters or {}
|
38
|
-
self.secure = secure
|
39
|
-
self.fillers = fillers
|
40
|
-
|
41
|
-
def _ensure_parameter_structure(self) -> Dict:
|
42
|
-
"""
|
43
|
-
Ensure the parameters are correctly structured for SWML
|
44
|
-
|
45
|
-
Returns:
|
46
|
-
Parameters dict with correct structure
|
47
|
-
"""
|
48
|
-
if not self.parameters:
|
49
|
-
return {"type": "object", "properties": {}}
|
50
|
-
|
51
|
-
# Check if we already have the correct structure
|
52
|
-
if "type" in self.parameters and "properties" in self.parameters:
|
53
|
-
return self.parameters
|
54
|
-
|
55
|
-
# Otherwise, wrap the parameters in the expected structure
|
56
|
-
return {
|
57
|
-
"type": "object",
|
58
|
-
"properties": self.parameters
|
59
|
-
}
|
60
|
-
|
61
|
-
def __call__(self, *args, **kwargs):
|
62
|
-
"""
|
63
|
-
Call the underlying handler function
|
64
|
-
"""
|
65
|
-
return self.handler(*args, **kwargs)
|
66
|
-
|
67
|
-
def execute(self, args: Dict[str, Any], raw_data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
68
|
-
"""
|
69
|
-
Execute the function with the given arguments
|
70
|
-
|
71
|
-
Args:
|
72
|
-
args: Parsed arguments for the function
|
73
|
-
raw_data: Optional raw request data
|
74
|
-
|
75
|
-
Returns:
|
76
|
-
Function result as a dictionary (from SwaigFunctionResult.to_dict())
|
77
|
-
"""
|
78
|
-
try:
|
79
|
-
# Raw data is mandatory, but we'll handle the case where it's null for robustness
|
80
|
-
if raw_data is None:
|
81
|
-
raw_data = {} # Provide an empty dict as fallback
|
82
|
-
|
83
|
-
# Call the handler with both args and raw_data
|
84
|
-
result = self.handler(args, raw_data)
|
85
|
-
|
86
|
-
# Import here to avoid circular imports
|
87
|
-
from signalwire_agents.core.function_result import SwaigFunctionResult
|
88
|
-
|
89
|
-
# Handle different result types - everything must end up as a SwaigFunctionResult
|
90
|
-
if isinstance(result, SwaigFunctionResult):
|
91
|
-
# Already a SwaigFunctionResult - just convert to dict
|
92
|
-
return result.to_dict()
|
93
|
-
elif isinstance(result, dict) and "response" in result:
|
94
|
-
# Already in the correct format - use as is
|
95
|
-
return result
|
96
|
-
elif isinstance(result, dict):
|
97
|
-
# Dictionary without response - create a SwaigFunctionResult
|
98
|
-
return SwaigFunctionResult("Function completed successfully").to_dict()
|
99
|
-
else:
|
100
|
-
# String or other type - create a SwaigFunctionResult with the string representation
|
101
|
-
return SwaigFunctionResult(str(result)).to_dict()
|
102
|
-
|
103
|
-
except Exception as e:
|
104
|
-
# Log the error for debugging but don't expose details to the AI
|
105
|
-
logging.error(f"Error executing SWAIG function {self.name}: {str(e)}")
|
106
|
-
# Return a generic error message
|
107
|
-
return SwaigFunctionResult(
|
108
|
-
"Sorry, I couldn't complete that action. Please try again or contact support if the issue persists."
|
109
|
-
).to_dict()
|
110
|
-
|
111
|
-
def validate_args(self, args: Dict[str, Any]) -> bool:
|
112
|
-
"""
|
113
|
-
Validate the arguments against the parameter schema
|
114
|
-
|
115
|
-
Args:
|
116
|
-
args: Arguments to validate
|
117
|
-
|
118
|
-
Returns:
|
119
|
-
True if valid, False otherwise
|
120
|
-
"""
|
121
|
-
# TODO: Implement JSON Schema validation
|
122
|
-
return True
|
123
|
-
|
124
|
-
def to_swaig(self, base_url: str, token: Optional[str] = None, call_id: Optional[str] = None, include_auth: bool = True) -> Dict[str, Any]:
|
125
|
-
"""
|
126
|
-
Convert this function to a SWAIG-compatible JSON object for SWML
|
127
|
-
|
128
|
-
Args:
|
129
|
-
base_url: Base URL for the webhook
|
130
|
-
token: Optional auth token to include
|
131
|
-
call_id: Optional call ID for session tracking
|
132
|
-
include_auth: Whether to include auth credentials in URL
|
133
|
-
|
134
|
-
Returns:
|
135
|
-
Dictionary representation for the SWAIG array in SWML
|
136
|
-
"""
|
137
|
-
# All functions use a single /swaig endpoint
|
138
|
-
url = f"{base_url}/swaig"
|
139
|
-
|
140
|
-
# Add token and call_id parameters if provided
|
141
|
-
if token and call_id:
|
142
|
-
url = f"{url}?token={token}&call_id={call_id}"
|
143
|
-
|
144
|
-
# Create properly structured function definition
|
145
|
-
function_def = {
|
146
|
-
"function": self.name,
|
147
|
-
"description": self.description,
|
148
|
-
"parameters": self._ensure_parameter_structure(),
|
149
|
-
}
|
150
|
-
|
151
|
-
# Only add web_hook_url if not using defaults
|
152
|
-
# This will be handled by the defaults section in the SWAIG array
|
153
|
-
if url:
|
154
|
-
function_def["web_hook_url"] = url
|
155
|
-
|
156
|
-
# Add fillers if provided
|
157
|
-
if self.fillers and len(self.fillers) > 0:
|
158
|
-
function_def["fillers"] = self.fillers
|
159
|
-
|
160
|
-
return function_def
|
161
|
-
|
162
|
-
# Add an alias for backward compatibility
|
163
|
-
SwaigFunction = SWAIGFunction
|
@@ -1,205 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
SWML Builder - Fluent API for building SWML documents
|
3
|
-
|
4
|
-
This module provides a fluent builder API for creating SWML documents.
|
5
|
-
It allows for chaining method calls to build up a document step by step.
|
6
|
-
"""
|
7
|
-
|
8
|
-
from typing import Dict, List, Any, Optional, Union, Self, TypeVar
|
9
|
-
|
10
|
-
from signalwire_agents.core.swml_service import SWMLService
|
11
|
-
|
12
|
-
|
13
|
-
T = TypeVar('T', bound='SWMLBuilder')
|
14
|
-
|
15
|
-
|
16
|
-
class SWMLBuilder:
|
17
|
-
"""
|
18
|
-
Fluent builder for SWML documents
|
19
|
-
|
20
|
-
This class provides a fluent interface for building SWML documents
|
21
|
-
by chaining method calls. It delegates to an underlying SWMLService
|
22
|
-
instance for the actual document creation.
|
23
|
-
"""
|
24
|
-
|
25
|
-
def __init__(self, service: SWMLService):
|
26
|
-
"""
|
27
|
-
Initialize with a SWMLService instance
|
28
|
-
|
29
|
-
Args:
|
30
|
-
service: The SWMLService to delegate to
|
31
|
-
"""
|
32
|
-
self.service = service
|
33
|
-
|
34
|
-
def answer(self, max_duration: Optional[int] = None, codecs: Optional[str] = None) -> Self:
|
35
|
-
"""
|
36
|
-
Add an 'answer' verb to the main section
|
37
|
-
|
38
|
-
Args:
|
39
|
-
max_duration: Maximum duration in seconds
|
40
|
-
codecs: Comma-separated list of codecs
|
41
|
-
|
42
|
-
Returns:
|
43
|
-
Self for method chaining
|
44
|
-
"""
|
45
|
-
self.service.add_answer_verb(max_duration, codecs)
|
46
|
-
return self
|
47
|
-
|
48
|
-
def hangup(self, reason: Optional[str] = None) -> Self:
|
49
|
-
"""
|
50
|
-
Add a 'hangup' verb to the main section
|
51
|
-
|
52
|
-
Args:
|
53
|
-
reason: Optional reason for hangup
|
54
|
-
|
55
|
-
Returns:
|
56
|
-
Self for method chaining
|
57
|
-
"""
|
58
|
-
self.service.add_hangup_verb(reason)
|
59
|
-
return self
|
60
|
-
|
61
|
-
def ai(self,
|
62
|
-
prompt_text: Optional[str] = None,
|
63
|
-
prompt_pom: Optional[List[Dict[str, Any]]] = None,
|
64
|
-
post_prompt: Optional[str] = None,
|
65
|
-
post_prompt_url: Optional[str] = None,
|
66
|
-
swaig: Optional[Dict[str, Any]] = None,
|
67
|
-
**kwargs) -> Self:
|
68
|
-
"""
|
69
|
-
Add an 'ai' verb to the main section
|
70
|
-
|
71
|
-
Args:
|
72
|
-
prompt_text: Text prompt for the AI (mutually exclusive with prompt_pom)
|
73
|
-
prompt_pom: POM structure for the AI prompt (mutually exclusive with prompt_text)
|
74
|
-
post_prompt: Optional post-prompt text
|
75
|
-
post_prompt_url: Optional URL for post-prompt processing
|
76
|
-
swaig: Optional SWAIG configuration
|
77
|
-
**kwargs: Additional AI parameters
|
78
|
-
|
79
|
-
Returns:
|
80
|
-
Self for method chaining
|
81
|
-
"""
|
82
|
-
self.service.add_ai_verb(
|
83
|
-
prompt_text=prompt_text,
|
84
|
-
prompt_pom=prompt_pom,
|
85
|
-
post_prompt=post_prompt,
|
86
|
-
post_prompt_url=post_prompt_url,
|
87
|
-
swaig=swaig,
|
88
|
-
**kwargs
|
89
|
-
)
|
90
|
-
return self
|
91
|
-
|
92
|
-
def play(self, url: Optional[str] = None, urls: Optional[List[str]] = None,
|
93
|
-
volume: Optional[float] = None, say_voice: Optional[str] = None,
|
94
|
-
say_language: Optional[str] = None, say_gender: Optional[str] = None,
|
95
|
-
auto_answer: Optional[bool] = None) -> Self:
|
96
|
-
"""
|
97
|
-
Add a 'play' verb to the main section
|
98
|
-
|
99
|
-
Args:
|
100
|
-
url: Single URL to play (mutually exclusive with urls)
|
101
|
-
urls: List of URLs to play (mutually exclusive with url)
|
102
|
-
volume: Volume level (-40 to 40)
|
103
|
-
say_voice: Voice for text-to-speech
|
104
|
-
say_language: Language for text-to-speech
|
105
|
-
say_gender: Gender for text-to-speech
|
106
|
-
auto_answer: Whether to auto-answer the call
|
107
|
-
|
108
|
-
Returns:
|
109
|
-
Self for method chaining
|
110
|
-
"""
|
111
|
-
# Create base config
|
112
|
-
config = {}
|
113
|
-
|
114
|
-
# Add play config (either single URL or list)
|
115
|
-
if url is not None:
|
116
|
-
config["url"] = url
|
117
|
-
elif urls is not None:
|
118
|
-
config["urls"] = urls
|
119
|
-
else:
|
120
|
-
raise ValueError("Either url or urls must be provided")
|
121
|
-
|
122
|
-
# Add optional parameters
|
123
|
-
if volume is not None:
|
124
|
-
config["volume"] = volume
|
125
|
-
if say_voice is not None:
|
126
|
-
config["say_voice"] = say_voice
|
127
|
-
if say_language is not None:
|
128
|
-
config["say_language"] = say_language
|
129
|
-
if say_gender is not None:
|
130
|
-
config["say_gender"] = say_gender
|
131
|
-
if auto_answer is not None:
|
132
|
-
config["auto_answer"] = auto_answer
|
133
|
-
|
134
|
-
# Add the verb
|
135
|
-
self.service.add_verb("play", config)
|
136
|
-
return self
|
137
|
-
|
138
|
-
def say(self, text: str, voice: Optional[str] = None,
|
139
|
-
language: Optional[str] = None, gender: Optional[str] = None,
|
140
|
-
volume: Optional[float] = None) -> Self:
|
141
|
-
"""
|
142
|
-
Add a 'play' verb with say: prefix for text-to-speech
|
143
|
-
|
144
|
-
Args:
|
145
|
-
text: Text to speak
|
146
|
-
voice: Voice for text-to-speech
|
147
|
-
language: Language for text-to-speech
|
148
|
-
gender: Gender for text-to-speech
|
149
|
-
volume: Volume level (-40 to 40)
|
150
|
-
|
151
|
-
Returns:
|
152
|
-
Self for method chaining
|
153
|
-
"""
|
154
|
-
# Create play config with say: prefix
|
155
|
-
url = f"say:{text}"
|
156
|
-
|
157
|
-
# Add the verb
|
158
|
-
return self.play(
|
159
|
-
url=url,
|
160
|
-
say_voice=voice,
|
161
|
-
say_language=language,
|
162
|
-
say_gender=gender,
|
163
|
-
volume=volume
|
164
|
-
)
|
165
|
-
|
166
|
-
def add_section(self, section_name: str) -> Self:
|
167
|
-
"""
|
168
|
-
Add a new section to the document
|
169
|
-
|
170
|
-
Args:
|
171
|
-
section_name: Name of the section to add
|
172
|
-
|
173
|
-
Returns:
|
174
|
-
Self for method chaining
|
175
|
-
"""
|
176
|
-
self.service.add_section(section_name)
|
177
|
-
return self
|
178
|
-
|
179
|
-
def build(self) -> Dict[str, Any]:
|
180
|
-
"""
|
181
|
-
Build and return the SWML document
|
182
|
-
|
183
|
-
Returns:
|
184
|
-
The complete SWML document as a dictionary
|
185
|
-
"""
|
186
|
-
return self.service.get_document()
|
187
|
-
|
188
|
-
def render(self) -> str:
|
189
|
-
"""
|
190
|
-
Build and render the SWML document as a JSON string
|
191
|
-
|
192
|
-
Returns:
|
193
|
-
The complete SWML document as a JSON string
|
194
|
-
"""
|
195
|
-
return self.service.render_document()
|
196
|
-
|
197
|
-
def reset(self) -> Self:
|
198
|
-
"""
|
199
|
-
Reset the document to an empty state
|
200
|
-
|
201
|
-
Returns:
|
202
|
-
Self for method chaining
|
203
|
-
"""
|
204
|
-
self.service.reset_document()
|
205
|
-
return self
|
@@ -1,218 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
SWML Verb Handlers - Interface and implementations for SWML verb handling
|
3
|
-
|
4
|
-
This module defines the base interface for SWML verb handlers and provides
|
5
|
-
implementations for specific verbs that require special handling.
|
6
|
-
"""
|
7
|
-
|
8
|
-
from abc import ABC, abstractmethod
|
9
|
-
from typing import Dict, List, Any, Optional, Tuple
|
10
|
-
|
11
|
-
|
12
|
-
class SWMLVerbHandler(ABC):
|
13
|
-
"""
|
14
|
-
Base interface for SWML verb handlers
|
15
|
-
|
16
|
-
This abstract class defines the interface that all SWML verb handlers
|
17
|
-
must implement. Verb handlers provide specialized logic for complex
|
18
|
-
SWML verbs that cannot be handled generically.
|
19
|
-
"""
|
20
|
-
|
21
|
-
@abstractmethod
|
22
|
-
def get_verb_name(self) -> str:
|
23
|
-
"""
|
24
|
-
Get the name of the verb this handler handles
|
25
|
-
|
26
|
-
Returns:
|
27
|
-
The verb name as a string
|
28
|
-
"""
|
29
|
-
pass
|
30
|
-
|
31
|
-
@abstractmethod
|
32
|
-
def validate_config(self, config: Dict[str, Any]) -> Tuple[bool, List[str]]:
|
33
|
-
"""
|
34
|
-
Validate the configuration for this verb
|
35
|
-
|
36
|
-
Args:
|
37
|
-
config: The configuration dictionary for this verb
|
38
|
-
|
39
|
-
Returns:
|
40
|
-
(is_valid, error_messages) tuple
|
41
|
-
"""
|
42
|
-
pass
|
43
|
-
|
44
|
-
@abstractmethod
|
45
|
-
def build_config(self, **kwargs) -> Dict[str, Any]:
|
46
|
-
"""
|
47
|
-
Build a configuration for this verb from the provided arguments
|
48
|
-
|
49
|
-
Args:
|
50
|
-
**kwargs: Keyword arguments specific to this verb
|
51
|
-
|
52
|
-
Returns:
|
53
|
-
Configuration dictionary
|
54
|
-
"""
|
55
|
-
pass
|
56
|
-
|
57
|
-
|
58
|
-
class AIVerbHandler(SWMLVerbHandler):
|
59
|
-
"""
|
60
|
-
Handler for the SWML 'ai' verb
|
61
|
-
|
62
|
-
The 'ai' verb is complex and requires specialized handling, particularly
|
63
|
-
for managing prompts, SWAIG functions, and AI configurations.
|
64
|
-
"""
|
65
|
-
|
66
|
-
def get_verb_name(self) -> str:
|
67
|
-
"""
|
68
|
-
Get the name of the verb this handler handles
|
69
|
-
|
70
|
-
Returns:
|
71
|
-
"ai" as the verb name
|
72
|
-
"""
|
73
|
-
return "ai"
|
74
|
-
|
75
|
-
def validate_config(self, config: Dict[str, Any]) -> Tuple[bool, List[str]]:
|
76
|
-
"""
|
77
|
-
Validate the configuration for the AI verb
|
78
|
-
|
79
|
-
Args:
|
80
|
-
config: The configuration dictionary for the AI verb
|
81
|
-
|
82
|
-
Returns:
|
83
|
-
(is_valid, error_messages) tuple
|
84
|
-
"""
|
85
|
-
errors = []
|
86
|
-
|
87
|
-
# Check required fields
|
88
|
-
if "prompt" not in config:
|
89
|
-
errors.append("Missing required field 'prompt'")
|
90
|
-
|
91
|
-
# Validate prompt structure if present
|
92
|
-
if "prompt" in config:
|
93
|
-
prompt = config["prompt"]
|
94
|
-
if not isinstance(prompt, dict):
|
95
|
-
errors.append("'prompt' must be an object")
|
96
|
-
elif "text" not in prompt and "pom" not in prompt:
|
97
|
-
errors.append("'prompt' must contain either 'text' or 'pom'")
|
98
|
-
|
99
|
-
# Validate SWAIG structure if present
|
100
|
-
if "SWAIG" in config:
|
101
|
-
swaig = config["SWAIG"]
|
102
|
-
if not isinstance(swaig, dict):
|
103
|
-
errors.append("'SWAIG' must be an object")
|
104
|
-
|
105
|
-
return len(errors) == 0, errors
|
106
|
-
|
107
|
-
def build_config(self,
|
108
|
-
prompt_text: Optional[str] = None,
|
109
|
-
prompt_pom: Optional[List[Dict[str, Any]]] = None,
|
110
|
-
post_prompt: Optional[str] = None,
|
111
|
-
post_prompt_url: Optional[str] = None,
|
112
|
-
swaig: Optional[Dict[str, Any]] = None,
|
113
|
-
**kwargs) -> Dict[str, Any]:
|
114
|
-
"""
|
115
|
-
Build a configuration for the AI verb
|
116
|
-
|
117
|
-
Args:
|
118
|
-
prompt_text: Text prompt for the AI (mutually exclusive with prompt_pom)
|
119
|
-
prompt_pom: POM structure for the AI prompt (mutually exclusive with prompt_text)
|
120
|
-
post_prompt: Optional post-prompt text
|
121
|
-
post_prompt_url: Optional URL for post-prompt processing
|
122
|
-
swaig: Optional SWAIG configuration
|
123
|
-
**kwargs: Additional AI parameters
|
124
|
-
|
125
|
-
Returns:
|
126
|
-
AI verb configuration dictionary
|
127
|
-
"""
|
128
|
-
config = {}
|
129
|
-
|
130
|
-
# Add prompt (either text or POM)
|
131
|
-
if prompt_text is not None:
|
132
|
-
config["prompt"] = {"text": prompt_text}
|
133
|
-
elif prompt_pom is not None:
|
134
|
-
config["prompt"] = {"pom": prompt_pom}
|
135
|
-
else:
|
136
|
-
raise ValueError("Either prompt_text or prompt_pom must be provided")
|
137
|
-
|
138
|
-
# Add post-prompt if provided
|
139
|
-
if post_prompt is not None:
|
140
|
-
config["post_prompt"] = {"text": post_prompt}
|
141
|
-
|
142
|
-
# Add post-prompt URL if provided
|
143
|
-
if post_prompt_url is not None:
|
144
|
-
config["post_prompt_url"] = post_prompt_url
|
145
|
-
|
146
|
-
# Add SWAIG if provided
|
147
|
-
if swaig is not None:
|
148
|
-
config["SWAIG"] = swaig
|
149
|
-
|
150
|
-
# Add any additional parameters
|
151
|
-
if "params" not in config:
|
152
|
-
config["params"] = {}
|
153
|
-
|
154
|
-
for key, value in kwargs.items():
|
155
|
-
# Special handling for certain parameters
|
156
|
-
if key == "languages":
|
157
|
-
config["languages"] = value
|
158
|
-
elif key == "hints":
|
159
|
-
config["hints"] = value
|
160
|
-
elif key == "pronounce":
|
161
|
-
config["pronounce"] = value
|
162
|
-
elif key == "global_data":
|
163
|
-
config["global_data"] = value
|
164
|
-
else:
|
165
|
-
# Add to params object
|
166
|
-
config["params"][key] = value
|
167
|
-
|
168
|
-
return config
|
169
|
-
|
170
|
-
|
171
|
-
class VerbHandlerRegistry:
|
172
|
-
"""
|
173
|
-
Registry for SWML verb handlers
|
174
|
-
|
175
|
-
This class maintains a registry of handlers for special SWML verbs
|
176
|
-
and provides methods for accessing and using them.
|
177
|
-
"""
|
178
|
-
|
179
|
-
def __init__(self):
|
180
|
-
"""Initialize the registry with default handlers"""
|
181
|
-
self._handlers = {}
|
182
|
-
|
183
|
-
# Register default handlers
|
184
|
-
self.register_handler(AIVerbHandler())
|
185
|
-
|
186
|
-
def register_handler(self, handler: SWMLVerbHandler) -> None:
|
187
|
-
"""
|
188
|
-
Register a new verb handler
|
189
|
-
|
190
|
-
Args:
|
191
|
-
handler: The handler to register
|
192
|
-
"""
|
193
|
-
verb_name = handler.get_verb_name()
|
194
|
-
self._handlers[verb_name] = handler
|
195
|
-
|
196
|
-
def get_handler(self, verb_name: str) -> Optional[SWMLVerbHandler]:
|
197
|
-
"""
|
198
|
-
Get the handler for a specific verb
|
199
|
-
|
200
|
-
Args:
|
201
|
-
verb_name: The name of the verb
|
202
|
-
|
203
|
-
Returns:
|
204
|
-
The handler if found, None otherwise
|
205
|
-
"""
|
206
|
-
return self._handlers.get(verb_name)
|
207
|
-
|
208
|
-
def has_handler(self, verb_name: str) -> bool:
|
209
|
-
"""
|
210
|
-
Check if a handler exists for a specific verb
|
211
|
-
|
212
|
-
Args:
|
213
|
-
verb_name: The name of the verb
|
214
|
-
|
215
|
-
Returns:
|
216
|
-
True if a handler exists, False otherwise
|
217
|
-
"""
|
218
|
-
return verb_name in self._handlers
|