solana-agent 20.1.2__py3-none-any.whl → 31.4.0__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.
- solana_agent/__init__.py +10 -5
- solana_agent/adapters/ffmpeg_transcoder.py +375 -0
- solana_agent/adapters/mongodb_adapter.py +15 -2
- solana_agent/adapters/openai_adapter.py +679 -0
- solana_agent/adapters/openai_realtime_ws.py +1813 -0
- solana_agent/adapters/pinecone_adapter.py +543 -0
- solana_agent/cli.py +128 -0
- solana_agent/client/solana_agent.py +180 -20
- solana_agent/domains/agent.py +13 -13
- solana_agent/domains/routing.py +18 -8
- solana_agent/factories/agent_factory.py +239 -38
- solana_agent/guardrails/pii.py +107 -0
- solana_agent/interfaces/client/client.py +95 -12
- solana_agent/interfaces/guardrails/guardrails.py +26 -0
- solana_agent/interfaces/plugins/plugins.py +2 -1
- solana_agent/interfaces/providers/__init__.py +0 -0
- solana_agent/interfaces/providers/audio.py +40 -0
- solana_agent/interfaces/providers/data_storage.py +9 -2
- solana_agent/interfaces/providers/llm.py +86 -9
- solana_agent/interfaces/providers/memory.py +13 -1
- solana_agent/interfaces/providers/realtime.py +212 -0
- solana_agent/interfaces/providers/vector_storage.py +53 -0
- solana_agent/interfaces/services/agent.py +27 -12
- solana_agent/interfaces/services/knowledge_base.py +59 -0
- solana_agent/interfaces/services/query.py +41 -8
- solana_agent/interfaces/services/routing.py +0 -1
- solana_agent/plugins/manager.py +37 -16
- solana_agent/plugins/registry.py +34 -19
- solana_agent/plugins/tools/__init__.py +0 -5
- solana_agent/plugins/tools/auto_tool.py +1 -0
- solana_agent/repositories/memory.py +332 -111
- solana_agent/services/__init__.py +1 -1
- solana_agent/services/agent.py +390 -241
- solana_agent/services/knowledge_base.py +768 -0
- solana_agent/services/query.py +1858 -153
- solana_agent/services/realtime.py +626 -0
- solana_agent/services/routing.py +104 -51
- solana_agent-31.4.0.dist-info/METADATA +1070 -0
- solana_agent-31.4.0.dist-info/RECORD +49 -0
- {solana_agent-20.1.2.dist-info → solana_agent-31.4.0.dist-info}/WHEEL +1 -1
- solana_agent-31.4.0.dist-info/entry_points.txt +3 -0
- solana_agent/adapters/llm_adapter.py +0 -160
- solana_agent-20.1.2.dist-info/METADATA +0 -464
- solana_agent-20.1.2.dist-info/RECORD +0 -35
- {solana_agent-20.1.2.dist-info → solana_agent-31.4.0.dist-info/licenses}/LICENSE +0 -0
|
@@ -4,14 +4,19 @@ Simplified client interface for interacting with the Solana Agent system.
|
|
|
4
4
|
This module provides a clean API for end users to interact with
|
|
5
5
|
the agent system without dealing with internal implementation details.
|
|
6
6
|
"""
|
|
7
|
+
|
|
7
8
|
import json
|
|
8
9
|
import importlib.util
|
|
9
|
-
from typing import AsyncGenerator, Dict, Any, Literal, Optional, Union
|
|
10
|
+
from typing import AsyncGenerator, Dict, Any, List, Literal, Optional, Type, Union
|
|
11
|
+
|
|
12
|
+
from pydantic import BaseModel
|
|
10
13
|
|
|
11
14
|
from solana_agent.factories.agent_factory import SolanaAgentFactory
|
|
12
15
|
from solana_agent.interfaces.client.client import SolanaAgent as SolanaAgentInterface
|
|
13
16
|
from solana_agent.interfaces.plugins.plugins import Tool
|
|
17
|
+
from solana_agent.services.knowledge_base import KnowledgeBaseService
|
|
14
18
|
from solana_agent.interfaces.services.routing import RoutingService as RoutingInterface
|
|
19
|
+
from solana_agent.interfaces.providers.realtime import RealtimeChunk
|
|
15
20
|
|
|
16
21
|
|
|
17
22
|
class SolanaAgent(SolanaAgentInterface):
|
|
@@ -33,8 +38,7 @@ class SolanaAgent(SolanaAgentInterface):
|
|
|
33
38
|
config = json.load(f)
|
|
34
39
|
else:
|
|
35
40
|
# Assume it's a Python file
|
|
36
|
-
spec = importlib.util.spec_from_file_location(
|
|
37
|
-
"config", config_path)
|
|
41
|
+
spec = importlib.util.spec_from_file_location("config", config_path)
|
|
38
42
|
config_module = importlib.util.module_from_spec(spec)
|
|
39
43
|
spec.loader.exec_module(config_module)
|
|
40
44
|
config = config_module.config
|
|
@@ -46,29 +50,72 @@ class SolanaAgent(SolanaAgentInterface):
|
|
|
46
50
|
user_id: str,
|
|
47
51
|
message: Union[str, bytes],
|
|
48
52
|
prompt: Optional[str] = None,
|
|
53
|
+
capture_schema: Optional[Dict[str, Any]] = None,
|
|
54
|
+
capture_name: Optional[str] = None,
|
|
49
55
|
output_format: Literal["text", "audio"] = "text",
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
# Realtime (WebSocket) options — used when realtime=True
|
|
57
|
+
realtime: bool = False,
|
|
58
|
+
vad: Optional[bool] = False,
|
|
59
|
+
rt_encode_input: bool = False,
|
|
60
|
+
rt_encode_output: bool = False,
|
|
61
|
+
rt_output_modalities: Optional[List[Literal["audio", "text"]]] = None,
|
|
62
|
+
rt_voice: Literal[
|
|
63
|
+
"alloy",
|
|
64
|
+
"ash",
|
|
65
|
+
"ballad",
|
|
66
|
+
"cedar",
|
|
67
|
+
"coral",
|
|
68
|
+
"echo",
|
|
69
|
+
"marin",
|
|
70
|
+
"sage",
|
|
71
|
+
"shimmer",
|
|
72
|
+
"verse",
|
|
73
|
+
] = "marin",
|
|
74
|
+
audio_voice: Literal[
|
|
75
|
+
"alloy",
|
|
76
|
+
"ash",
|
|
77
|
+
"ballad",
|
|
78
|
+
"coral",
|
|
79
|
+
"echo",
|
|
80
|
+
"fable",
|
|
81
|
+
"onyx",
|
|
82
|
+
"nova",
|
|
83
|
+
"sage",
|
|
84
|
+
"shimmer",
|
|
85
|
+
] = "nova",
|
|
86
|
+
audio_output_format: Literal[
|
|
87
|
+
"mp3", "opus", "aac", "flac", "wav", "pcm"
|
|
88
|
+
] = "aac",
|
|
55
89
|
audio_input_format: Literal[
|
|
56
90
|
"flac", "mp3", "mp4", "mpeg", "mpga", "m4a", "ogg", "wav", "webm"
|
|
57
91
|
] = "mp4",
|
|
58
92
|
router: Optional[RoutingInterface] = None,
|
|
59
|
-
|
|
60
|
-
|
|
93
|
+
images: Optional[List[Union[str, bytes]]] = None,
|
|
94
|
+
output_model: Optional[Type[BaseModel]] = None,
|
|
95
|
+
) -> AsyncGenerator[
|
|
96
|
+
Union[str, bytes, BaseModel, RealtimeChunk], None
|
|
97
|
+
]: # pragma: no cover
|
|
98
|
+
"""Process a user message (text or audio) and optional images, returning the response stream.
|
|
61
99
|
|
|
62
100
|
Args:
|
|
63
101
|
user_id: User ID
|
|
64
102
|
message: Text message or audio bytes
|
|
65
103
|
prompt: Optional prompt for the agent
|
|
66
104
|
output_format: Response format ("text" or "audio")
|
|
105
|
+
capture_schema: Optional Pydantic schema for structured output
|
|
106
|
+
capture_name: Optional name for structured output capture
|
|
107
|
+
realtime: Whether to use realtime (WebSocket) processing
|
|
108
|
+
vad: Whether to use voice activity detection (for audio input)
|
|
109
|
+
rt_encode_input: Whether to re-encode input audio for compatibility
|
|
110
|
+
rt_encode_output: Whether to re-encode output audio for compatibility
|
|
111
|
+
rt_output_modalities: Modalities to return in realtime (default both if None)
|
|
112
|
+
rt_voice: Voice to use for realtime audio output
|
|
67
113
|
audio_voice: Voice to use for audio output
|
|
68
|
-
audio_instructions: Not used currently
|
|
69
114
|
audio_output_format: Audio output format
|
|
70
115
|
audio_input_format: Audio input format
|
|
71
116
|
router: Optional routing service for processing
|
|
117
|
+
images: Optional list of image URLs (str) or image bytes.
|
|
118
|
+
output_model: Optional Pydantic model for structured output
|
|
72
119
|
|
|
73
120
|
Returns:
|
|
74
121
|
Async generator yielding response chunks (text strings or audio bytes)
|
|
@@ -76,13 +123,22 @@ class SolanaAgent(SolanaAgentInterface):
|
|
|
76
123
|
async for chunk in self.query_service.process(
|
|
77
124
|
user_id=user_id,
|
|
78
125
|
query=message,
|
|
126
|
+
images=images,
|
|
79
127
|
output_format=output_format,
|
|
128
|
+
realtime=realtime,
|
|
129
|
+
vad=vad,
|
|
130
|
+
rt_encode_input=rt_encode_input,
|
|
131
|
+
rt_encode_output=rt_encode_output,
|
|
132
|
+
rt_output_modalities=rt_output_modalities,
|
|
133
|
+
rt_voice=rt_voice,
|
|
80
134
|
audio_voice=audio_voice,
|
|
81
|
-
audio_instructions=audio_instructions,
|
|
82
135
|
audio_output_format=audio_output_format,
|
|
83
136
|
audio_input_format=audio_input_format,
|
|
84
137
|
prompt=prompt,
|
|
85
138
|
router=router,
|
|
139
|
+
output_model=output_model,
|
|
140
|
+
capture_schema=capture_schema,
|
|
141
|
+
capture_name=capture_name,
|
|
86
142
|
):
|
|
87
143
|
yield chunk
|
|
88
144
|
|
|
@@ -100,7 +156,7 @@ class SolanaAgent(SolanaAgentInterface):
|
|
|
100
156
|
user_id: str,
|
|
101
157
|
page_num: int = 1,
|
|
102
158
|
page_size: int = 20,
|
|
103
|
-
sort_order: str = "desc" # "asc" for oldest-first, "desc" for newest-first
|
|
159
|
+
sort_order: str = "desc", # "asc" for oldest-first, "desc" for newest-first
|
|
104
160
|
) -> Dict[str, Any]: # pragma: no cover
|
|
105
161
|
"""
|
|
106
162
|
Get paginated message history for a user.
|
|
@@ -118,21 +174,125 @@ class SolanaAgent(SolanaAgentInterface):
|
|
|
118
174
|
user_id, page_num, page_size, sort_order
|
|
119
175
|
)
|
|
120
176
|
|
|
121
|
-
def register_tool(self, tool: Tool) -> bool:
|
|
177
|
+
def register_tool(self, agent_name: str, tool: Tool) -> bool:
|
|
122
178
|
"""
|
|
123
179
|
Register a tool with the agent system.
|
|
124
180
|
|
|
125
181
|
Args:
|
|
182
|
+
agent_name: Name of the agent to register the tool with
|
|
126
183
|
tool: Tool instance to register
|
|
127
184
|
|
|
128
185
|
Returns:
|
|
129
186
|
True if successful, False
|
|
130
187
|
"""
|
|
131
|
-
success = self.query_service.agent_service.tool_registry.register_tool(
|
|
132
|
-
tool)
|
|
188
|
+
success = self.query_service.agent_service.tool_registry.register_tool(tool)
|
|
133
189
|
if success:
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
agent_name, tool.name)
|
|
190
|
+
self.query_service.agent_service.assign_tool_for_agent(
|
|
191
|
+
agent_name, tool.name
|
|
192
|
+
)
|
|
138
193
|
return success
|
|
194
|
+
|
|
195
|
+
def _ensure_kb(self) -> KnowledgeBaseService:
|
|
196
|
+
"""Checks if the knowledge base service is available and returns it."""
|
|
197
|
+
if (
|
|
198
|
+
hasattr(self.query_service, "knowledge_base")
|
|
199
|
+
and self.query_service.knowledge_base
|
|
200
|
+
):
|
|
201
|
+
return self.query_service.knowledge_base
|
|
202
|
+
else:
|
|
203
|
+
raise AttributeError("Knowledge base service not configured or available.")
|
|
204
|
+
|
|
205
|
+
async def kb_add_document(
|
|
206
|
+
self,
|
|
207
|
+
text: str,
|
|
208
|
+
metadata: Dict[str, Any],
|
|
209
|
+
document_id: Optional[str] = None,
|
|
210
|
+
namespace: Optional[str] = None,
|
|
211
|
+
) -> str:
|
|
212
|
+
"""
|
|
213
|
+
Add a document to the knowledge base.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
text: Document text content.
|
|
217
|
+
metadata: Document metadata.
|
|
218
|
+
document_id: Optional document ID.
|
|
219
|
+
namespace: Optional Pinecone namespace.
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
The document ID.
|
|
223
|
+
"""
|
|
224
|
+
kb = self._ensure_kb()
|
|
225
|
+
return await kb.add_document(text, metadata, document_id, namespace)
|
|
226
|
+
|
|
227
|
+
async def kb_query(
|
|
228
|
+
self,
|
|
229
|
+
query_text: str,
|
|
230
|
+
filter: Optional[Dict[str, Any]] = None,
|
|
231
|
+
top_k: int = 5,
|
|
232
|
+
namespace: Optional[str] = None,
|
|
233
|
+
include_content: bool = True,
|
|
234
|
+
include_metadata: bool = True,
|
|
235
|
+
) -> List[Dict[str, Any]]:
|
|
236
|
+
"""
|
|
237
|
+
Query the knowledge base.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
query_text: Search query text.
|
|
241
|
+
filter: Optional filter criteria.
|
|
242
|
+
top_k: Maximum number of results.
|
|
243
|
+
namespace: Optional Pinecone namespace.
|
|
244
|
+
include_content: Include document content in results.
|
|
245
|
+
include_metadata: Include document metadata in results.
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
List of matching documents.
|
|
249
|
+
"""
|
|
250
|
+
kb = self._ensure_kb()
|
|
251
|
+
return await kb.query(
|
|
252
|
+
query_text, filter, top_k, namespace, include_content, include_metadata
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
async def kb_delete_document(
|
|
256
|
+
self, document_id: str, namespace: Optional[str] = None
|
|
257
|
+
) -> bool:
|
|
258
|
+
"""
|
|
259
|
+
Delete a document from the knowledge base.
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
document_id: ID of document to delete.
|
|
263
|
+
namespace: Optional Pinecone namespace.
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
True if successful.
|
|
267
|
+
"""
|
|
268
|
+
kb = self._ensure_kb()
|
|
269
|
+
return await kb.delete_document(document_id, namespace)
|
|
270
|
+
|
|
271
|
+
async def kb_add_pdf_document(
|
|
272
|
+
self,
|
|
273
|
+
pdf_data: Union[bytes, str],
|
|
274
|
+
metadata: Dict[str, Any],
|
|
275
|
+
document_id: Optional[str] = None,
|
|
276
|
+
namespace: Optional[str] = None,
|
|
277
|
+
chunk_batch_size: int = 50,
|
|
278
|
+
) -> str:
|
|
279
|
+
"""
|
|
280
|
+
Add a PDF document to the knowledge base via the client.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
pdf_data: PDF content as bytes or a path to the PDF file.
|
|
284
|
+
metadata: Document metadata.
|
|
285
|
+
document_id: Optional parent document ID.
|
|
286
|
+
namespace: Optional Pinecone namespace for chunks.
|
|
287
|
+
chunk_batch_size: Batch size for upserting chunks.
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
The parent document ID.
|
|
291
|
+
"""
|
|
292
|
+
kb = self._ensure_kb()
|
|
293
|
+
# Type check added for clarity, though handled in service
|
|
294
|
+
if not isinstance(pdf_data, (bytes, str)):
|
|
295
|
+
raise TypeError("pdf_data must be bytes or a file path string.")
|
|
296
|
+
return await kb.add_pdf_document(
|
|
297
|
+
pdf_data, metadata, document_id, namespace, chunk_batch_size
|
|
298
|
+
)
|
solana_agent/domains/agent.py
CHANGED
|
@@ -4,25 +4,20 @@ Domain models for AI and human agents.
|
|
|
4
4
|
This module defines the core domain models for representing
|
|
5
5
|
AI agents, human agents, and business mission/values.
|
|
6
6
|
"""
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
from typing import List, Dict, Optional, Any
|
|
8
9
|
from pydantic import BaseModel, Field, field_validator
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class BusinessMission(BaseModel):
|
|
12
13
|
"""Business mission and values to guide agent behavior."""
|
|
13
14
|
|
|
14
|
-
mission: str = Field(...,
|
|
15
|
-
description="Business mission statement")
|
|
15
|
+
mission: str = Field(..., description="Business mission statement")
|
|
16
16
|
values: List[Dict[str, str]] = Field(
|
|
17
|
-
default_factory=list,
|
|
18
|
-
description="Business values as name-description pairs"
|
|
19
|
-
)
|
|
20
|
-
goals: List[str] = Field(
|
|
21
|
-
default_factory=list,
|
|
22
|
-
description="Business goals"
|
|
17
|
+
default_factory=list, description="Business values as name-description pairs"
|
|
23
18
|
)
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
goals: List[str] = Field(default_factory=list, description="Business goals")
|
|
20
|
+
voice: str = Field(None, description="Business voice or tone")
|
|
26
21
|
|
|
27
22
|
@field_validator("mission")
|
|
28
23
|
@classmethod
|
|
@@ -56,9 +51,14 @@ class AIAgent(BaseModel):
|
|
|
56
51
|
model_config = {"arbitrary_types_allowed": True}
|
|
57
52
|
|
|
58
53
|
name: str = Field(..., description="Unique agent identifier name")
|
|
59
|
-
instructions: str = Field(...,
|
|
60
|
-
description="Base instructions for the agent")
|
|
54
|
+
instructions: str = Field(..., description="Base instructions for the agent")
|
|
61
55
|
specialization: str = Field(..., description="Agent's specialized domain")
|
|
56
|
+
capture_name: Optional[str] = Field(
|
|
57
|
+
default=None, description="Optional capture name for structured data"
|
|
58
|
+
)
|
|
59
|
+
capture_schema: Optional[Dict[str, Any]] = Field(
|
|
60
|
+
default=None, description="Optional JSON schema for structured capture"
|
|
61
|
+
)
|
|
62
62
|
|
|
63
63
|
@field_validator("name", "specialization")
|
|
64
64
|
@classmethod
|
solana_agent/domains/routing.py
CHANGED
|
@@ -4,11 +4,21 @@ from pydantic import BaseModel, Field
|
|
|
4
4
|
|
|
5
5
|
class QueryAnalysis(BaseModel):
|
|
6
6
|
"""Analysis of a user query for routing purposes."""
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
|
|
8
|
+
primary_agent: str = Field(
|
|
9
|
+
...,
|
|
10
|
+
description="Name of the primary agent that should handle this query (must be one of the available agent names)",
|
|
11
|
+
)
|
|
12
|
+
secondary_agents: List[str] = Field(
|
|
13
|
+
default_factory=list,
|
|
14
|
+
description="Names of secondary agents that might be helpful (must be from the available agent names)",
|
|
15
|
+
)
|
|
16
|
+
complexity_level: int = Field(
|
|
17
|
+
default=1, description="Complexity level (1-5)", ge=1, le=5
|
|
18
|
+
)
|
|
19
|
+
topics: List[str] = Field(
|
|
20
|
+
default_factory=list, description="Key topics in the query"
|
|
21
|
+
)
|
|
22
|
+
confidence: float = Field(
|
|
23
|
+
default=0.5, description="Confidence in the analysis", ge=0.0, le=1.0
|
|
24
|
+
)
|