solana-agent 27.3.6__py3-none-any.whl → 27.3.8__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 +1 -3
- solana_agent/adapters/mongodb_adapter.py +5 -2
- solana_agent/adapters/openai_adapter.py +32 -27
- solana_agent/adapters/pinecone_adapter.py +91 -63
- solana_agent/client/solana_agent.py +38 -23
- solana_agent/domains/agent.py +7 -13
- solana_agent/domains/routing.py +5 -5
- solana_agent/factories/agent_factory.py +49 -34
- solana_agent/interfaces/client/client.py +22 -13
- solana_agent/interfaces/plugins/plugins.py +2 -1
- solana_agent/interfaces/providers/data_storage.py +9 -2
- solana_agent/interfaces/providers/llm.py +26 -12
- solana_agent/interfaces/providers/memory.py +1 -1
- solana_agent/interfaces/providers/vector_storage.py +3 -9
- solana_agent/interfaces/services/agent.py +21 -6
- solana_agent/interfaces/services/knowledge_base.py +6 -8
- solana_agent/interfaces/services/query.py +16 -5
- solana_agent/interfaces/services/routing.py +0 -1
- solana_agent/plugins/manager.py +14 -9
- solana_agent/plugins/registry.py +13 -11
- solana_agent/plugins/tools/__init__.py +0 -5
- solana_agent/plugins/tools/auto_tool.py +1 -0
- solana_agent/repositories/memory.py +20 -22
- solana_agent/services/__init__.py +1 -1
- solana_agent/services/agent.py +119 -89
- solana_agent/services/knowledge_base.py +182 -131
- solana_agent/services/query.py +48 -24
- solana_agent/services/routing.py +30 -18
- {solana_agent-27.3.6.dist-info → solana_agent-27.3.8.dist-info}/METADATA +6 -5
- solana_agent-27.3.8.dist-info/RECORD +39 -0
- solana_agent-27.3.6.dist-info/RECORD +0 -39
- {solana_agent-27.3.6.dist-info → solana_agent-27.3.8.dist-info}/LICENSE +0 -0
- {solana_agent-27.3.6.dist-info → solana_agent-27.3.8.dist-info}/WHEEL +0 -0
@@ -8,7 +8,9 @@ class AgentService(ABC):
|
|
8
8
|
"""Interface for agent management and response generation."""
|
9
9
|
|
10
10
|
@abstractmethod
|
11
|
-
def register_ai_agent(
|
11
|
+
def register_ai_agent(
|
12
|
+
self, name: str, instructions: str, specialization: str
|
13
|
+
) -> None:
|
12
14
|
"""Register an AI agent with its specialization."""
|
13
15
|
pass
|
14
16
|
|
@@ -25,11 +27,22 @@ class AgentService(ABC):
|
|
25
27
|
query: Union[str, bytes],
|
26
28
|
memory_context: str = "",
|
27
29
|
output_format: Literal["text", "audio"] = "text",
|
28
|
-
audio_voice: Literal[
|
29
|
-
|
30
|
+
audio_voice: Literal[
|
31
|
+
"alloy",
|
32
|
+
"ash",
|
33
|
+
"ballad",
|
34
|
+
"coral",
|
35
|
+
"echo",
|
36
|
+
"fable",
|
37
|
+
"onyx",
|
38
|
+
"nova",
|
39
|
+
"sage",
|
40
|
+
"shimmer",
|
41
|
+
] = "nova",
|
30
42
|
audio_instructions: str = "You speak in a friendly and helpful manner.",
|
31
|
-
audio_output_format: Literal[
|
32
|
-
|
43
|
+
audio_output_format: Literal[
|
44
|
+
"mp3", "opus", "aac", "flac", "wav", "pcm"
|
45
|
+
] = "aac",
|
33
46
|
prompt: Optional[str] = None,
|
34
47
|
) -> AsyncGenerator[Union[str, bytes], None]:
|
35
48
|
"""Generate a response from an agent."""
|
@@ -46,7 +59,9 @@ class AgentService(ABC):
|
|
46
59
|
pass
|
47
60
|
|
48
61
|
@abstractmethod
|
49
|
-
async def execute_tool(
|
62
|
+
async def execute_tool(
|
63
|
+
self, agent_name: str, tool_name: str, parameters: Dict[str, Any]
|
64
|
+
) -> Dict[str, Any]:
|
50
65
|
"""Execute a tool on behalf of an agent."""
|
51
66
|
pass
|
52
67
|
|
@@ -13,7 +13,7 @@ class KnowledgeBaseService(ABC):
|
|
13
13
|
text: str,
|
14
14
|
metadata: Dict[str, Any],
|
15
15
|
document_id: Optional[str] = None,
|
16
|
-
namespace: Optional[str] = None
|
16
|
+
namespace: Optional[str] = None,
|
17
17
|
) -> str:
|
18
18
|
"""
|
19
19
|
Add a document to the knowledge base.
|
@@ -28,7 +28,7 @@ class KnowledgeBaseService(ABC):
|
|
28
28
|
top_k: int = 5,
|
29
29
|
namespace: Optional[str] = None,
|
30
30
|
include_content: bool = True,
|
31
|
-
include_metadata: bool = True
|
31
|
+
include_metadata: bool = True,
|
32
32
|
) -> List[Dict[str, Any]]:
|
33
33
|
"""
|
34
34
|
Query the knowledge base with semantic search.
|
@@ -37,9 +37,7 @@ class KnowledgeBaseService(ABC):
|
|
37
37
|
|
38
38
|
@abstractmethod
|
39
39
|
async def delete_document(
|
40
|
-
self,
|
41
|
-
document_id: str,
|
42
|
-
namespace: Optional[str] = None
|
40
|
+
self, document_id: str, namespace: Optional[str] = None
|
43
41
|
) -> bool:
|
44
42
|
"""
|
45
43
|
Delete a document from the knowledge base.
|
@@ -52,7 +50,7 @@ class KnowledgeBaseService(ABC):
|
|
52
50
|
document_id: str,
|
53
51
|
text: Optional[str] = None,
|
54
52
|
metadata: Optional[Dict[str, Any]] = None,
|
55
|
-
namespace: Optional[str] = None
|
53
|
+
namespace: Optional[str] = None,
|
56
54
|
) -> bool:
|
57
55
|
"""
|
58
56
|
Update an existing document in the knowledge base.
|
@@ -64,7 +62,7 @@ class KnowledgeBaseService(ABC):
|
|
64
62
|
self,
|
65
63
|
documents: List[Dict[str, Any]],
|
66
64
|
namespace: Optional[str] = None,
|
67
|
-
batch_size: int = 50
|
65
|
+
batch_size: int = 50,
|
68
66
|
) -> List[str]:
|
69
67
|
"""
|
70
68
|
Add multiple documents in batches.
|
@@ -78,7 +76,7 @@ class KnowledgeBaseService(ABC):
|
|
78
76
|
metadata: Dict[str, Any],
|
79
77
|
document_id: Optional[str] = None,
|
80
78
|
namespace: Optional[str] = None,
|
81
|
-
chunk_batch_size: int = 50
|
79
|
+
chunk_batch_size: int = 50,
|
82
80
|
) -> str:
|
83
81
|
"""
|
84
82
|
Add a PDF document to the knowledge base.
|
@@ -13,11 +13,22 @@ class QueryService(ABC):
|
|
13
13
|
user_id: str,
|
14
14
|
query: Union[str, bytes],
|
15
15
|
output_format: Literal["text", "audio"] = "text",
|
16
|
-
audio_voice: Literal[
|
17
|
-
|
16
|
+
audio_voice: Literal[
|
17
|
+
"alloy",
|
18
|
+
"ash",
|
19
|
+
"ballad",
|
20
|
+
"coral",
|
21
|
+
"echo",
|
22
|
+
"fable",
|
23
|
+
"onyx",
|
24
|
+
"nova",
|
25
|
+
"sage",
|
26
|
+
"shimmer",
|
27
|
+
] = "nova",
|
18
28
|
audio_instructions: str = "You speak in a friendly and helpful manner.",
|
19
|
-
audio_output_format: Literal[
|
20
|
-
|
29
|
+
audio_output_format: Literal[
|
30
|
+
"mp3", "opus", "aac", "flac", "wav", "pcm"
|
31
|
+
] = "aac",
|
21
32
|
audio_input_format: Literal[
|
22
33
|
"flac", "mp3", "mp4", "mpeg", "mpga", "m4a", "ogg", "wav", "webm"
|
23
34
|
] = "mp4",
|
@@ -33,7 +44,7 @@ class QueryService(ABC):
|
|
33
44
|
user_id: str,
|
34
45
|
page_num: int = 1,
|
35
46
|
page_size: int = 20,
|
36
|
-
sort_order: str = "desc" # "asc" for oldest-first, "desc" for newest-first
|
47
|
+
sort_order: str = "desc", # "asc" for oldest-first, "desc" for newest-first
|
37
48
|
) -> Dict[str, Any]:
|
38
49
|
"""Get paginated message history for a user."""
|
39
50
|
pass
|
solana_agent/plugins/manager.py
CHANGED
@@ -4,11 +4,14 @@ Plugin manager for the Solana Agent system.
|
|
4
4
|
This module implements the concrete PluginManager that discovers,
|
5
5
|
loads, and manages plugins.
|
6
6
|
"""
|
7
|
+
|
7
8
|
import importlib
|
8
9
|
from typing import Dict, List, Any, Optional
|
9
10
|
import importlib.metadata
|
10
11
|
|
11
|
-
from solana_agent.interfaces.plugins.plugins import
|
12
|
+
from solana_agent.interfaces.plugins.plugins import (
|
13
|
+
PluginManager as PluginManagerInterface,
|
14
|
+
)
|
12
15
|
from solana_agent.interfaces.plugins.plugins import Plugin
|
13
16
|
from solana_agent.plugins.registry import ToolRegistry
|
14
17
|
|
@@ -19,7 +22,11 @@ class PluginManager(PluginManagerInterface):
|
|
19
22
|
# Class variable to track loaded entry points
|
20
23
|
_loaded_entry_points = set()
|
21
24
|
|
22
|
-
def __init__(
|
25
|
+
def __init__(
|
26
|
+
self,
|
27
|
+
config: Optional[Dict[str, Any]] = None,
|
28
|
+
tool_registry: Optional[ToolRegistry] = None,
|
29
|
+
):
|
23
30
|
"""Initialize with optional configuration and tool registry."""
|
24
31
|
self.config = config or {}
|
25
32
|
self.tool_registry = tool_registry or ToolRegistry()
|
@@ -61,12 +68,13 @@ class PluginManager(PluginManagerInterface):
|
|
61
68
|
loaded_plugins = []
|
62
69
|
|
63
70
|
# Discover plugins through entry points
|
64
|
-
for entry_point in importlib.metadata.entry_points(
|
71
|
+
for entry_point in importlib.metadata.entry_points(
|
72
|
+
group="solana_agent.plugins"
|
73
|
+
):
|
65
74
|
# Skip if this entry point has already been loaded
|
66
75
|
entry_point_id = f"{entry_point.name}:{entry_point.value}"
|
67
76
|
if entry_point_id in PluginManager._loaded_entry_points:
|
68
|
-
print(
|
69
|
-
f"Skipping already loaded plugin: {entry_point.name}")
|
77
|
+
print(f"Skipping already loaded plugin: {entry_point.name}")
|
70
78
|
continue
|
71
79
|
|
72
80
|
try:
|
@@ -103,10 +111,7 @@ class PluginManager(PluginManagerInterface):
|
|
103
111
|
List of plugin details dictionaries
|
104
112
|
"""
|
105
113
|
return [
|
106
|
-
{
|
107
|
-
"name": plugin.name,
|
108
|
-
"description": plugin.description
|
109
|
-
}
|
114
|
+
{"name": plugin.name, "description": plugin.description}
|
110
115
|
for plugin in self._plugins.values()
|
111
116
|
]
|
112
117
|
|
solana_agent/plugins/registry.py
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
"""
|
2
2
|
Tool registry for the Solana Agent system.
|
3
3
|
|
4
|
-
This module implements the concrete ToolRegistry that manages tools
|
4
|
+
This module implements the concrete ToolRegistry that manages tools
|
5
5
|
and their access permissions.
|
6
6
|
"""
|
7
|
+
|
7
8
|
from typing import Dict, List, Any, Optional
|
8
9
|
|
9
|
-
from solana_agent.interfaces.plugins.plugins import
|
10
|
+
from solana_agent.interfaces.plugins.plugins import (
|
11
|
+
ToolRegistry as ToolRegistryInterface,
|
12
|
+
)
|
10
13
|
from solana_agent.interfaces.plugins.plugins import Tool
|
11
14
|
|
12
15
|
|
@@ -39,7 +42,8 @@ class ToolRegistry(ToolRegistryInterface):
|
|
39
42
|
"""Give an agent access to a specific tool."""
|
40
43
|
if tool_name not in self._tools:
|
41
44
|
print(
|
42
|
-
f"Error: Tool {tool_name} is not registered. Available tools: {list(self._tools.keys())}"
|
45
|
+
f"Error: Tool {tool_name} is not registered. Available tools: {list(self._tools.keys())}"
|
46
|
+
)
|
43
47
|
return False
|
44
48
|
|
45
49
|
# Initialize agent's tool list if not exists
|
@@ -47,12 +51,10 @@ class ToolRegistry(ToolRegistryInterface):
|
|
47
51
|
self._agent_tools[agent_name] = [tool_name]
|
48
52
|
elif tool_name not in self._agent_tools[agent_name]:
|
49
53
|
# Add new tool to existing list
|
50
|
-
self._agent_tools[agent_name] = [
|
51
|
-
*self._agent_tools[agent_name], tool_name]
|
54
|
+
self._agent_tools[agent_name] = [*self._agent_tools[agent_name], tool_name]
|
52
55
|
|
53
56
|
print(f"Successfully assigned tool {tool_name} to agent {agent_name}")
|
54
|
-
print(
|
55
|
-
f"Agent {agent_name} now has access to: {self._agent_tools[agent_name]}")
|
57
|
+
print(f"Agent {agent_name} now has access to: {self._agent_tools[agent_name]}")
|
56
58
|
|
57
59
|
return True
|
58
60
|
|
@@ -63,12 +65,12 @@ class ToolRegistry(ToolRegistryInterface):
|
|
63
65
|
{
|
64
66
|
"name": name,
|
65
67
|
"description": self._tools[name].description,
|
66
|
-
"parameters": self._tools[name].get_schema()
|
68
|
+
"parameters": self._tools[name].get_schema(),
|
67
69
|
}
|
68
|
-
for name in tool_names
|
70
|
+
for name in tool_names
|
71
|
+
if name in self._tools
|
69
72
|
]
|
70
|
-
print(
|
71
|
-
f"Tools available to agent {agent_name}: {[t['name'] for t in tools]}")
|
73
|
+
print(f"Tools available to agent {agent_name}: {[t['name'] for t in tools]}")
|
72
74
|
return tools
|
73
75
|
|
74
76
|
def list_all_tools(self) -> List[str]:
|
@@ -4,6 +4,7 @@ AutoTool implementation for the Solana Agent system.
|
|
4
4
|
This module provides the base AutoTool class that implements the Tool interface
|
5
5
|
and can be extended to create custom tools.
|
6
6
|
"""
|
7
|
+
|
7
8
|
from typing import Dict, Any
|
8
9
|
|
9
10
|
from solana_agent.interfaces.plugins.plugins import Tool
|
@@ -43,13 +43,18 @@ class MemoryRepository(MemoryProvider):
|
|
43
43
|
raise ValueError("User ID cannot be None or empty")
|
44
44
|
if not messages or not isinstance(messages, list):
|
45
45
|
raise ValueError("Messages must be a non-empty list")
|
46
|
-
if not all(
|
46
|
+
if not all(
|
47
|
+
isinstance(msg, dict) and "role" in msg and "content" in msg
|
48
|
+
for msg in messages
|
49
|
+
):
|
47
50
|
raise ValueError(
|
48
|
-
"All messages must be dictionaries with 'role' and 'content' keys"
|
51
|
+
"All messages must be dictionaries with 'role' and 'content' keys"
|
52
|
+
)
|
49
53
|
for msg in messages:
|
50
54
|
if msg["role"] not in ["user", "assistant"]:
|
51
55
|
raise ValueError(
|
52
|
-
f"Invalid role '{msg['role']}' in message. Only 'user' and 'assistant' roles are accepted."
|
56
|
+
f"Invalid role '{msg['role']}' in message. Only 'user' and 'assistant' roles are accepted."
|
57
|
+
)
|
53
58
|
|
54
59
|
# Store in MongoDB
|
55
60
|
if self.mongo and len(messages) >= 2:
|
@@ -71,7 +76,7 @@ class MemoryRepository(MemoryProvider):
|
|
71
76
|
"user_id": user_id,
|
72
77
|
"user_message": user_msg,
|
73
78
|
"assistant_message": assistant_msg,
|
74
|
-
"timestamp": datetime.now(timezone.utc)
|
79
|
+
"timestamp": datetime.now(timezone.utc),
|
75
80
|
}
|
76
81
|
self.mongo.insert_one(self.collection, doc)
|
77
82
|
except Exception as e:
|
@@ -96,11 +101,8 @@ class MemoryRepository(MemoryProvider):
|
|
96
101
|
# Add messages to Zep memory
|
97
102
|
if zep_messages:
|
98
103
|
try:
|
99
|
-
await self.zep.memory.add(
|
100
|
-
|
101
|
-
messages=zep_messages
|
102
|
-
)
|
103
|
-
except Exception as e:
|
104
|
+
await self.zep.memory.add(session_id=user_id, messages=zep_messages)
|
105
|
+
except Exception:
|
104
106
|
try:
|
105
107
|
try:
|
106
108
|
await self.zep.user.add(user_id=user_id)
|
@@ -108,13 +110,12 @@ class MemoryRepository(MemoryProvider):
|
|
108
110
|
print(f"Zep user addition error: {e}")
|
109
111
|
|
110
112
|
try:
|
111
|
-
await self.zep.memory.add_session(
|
113
|
+
await self.zep.memory.add_session(
|
114
|
+
session_id=user_id, user_id=user_id
|
115
|
+
)
|
112
116
|
except Exception as e:
|
113
117
|
print(f"Zep session creation error: {e}")
|
114
|
-
await self.zep.memory.add(
|
115
|
-
session_id=user_id,
|
116
|
-
messages=zep_messages
|
117
|
-
)
|
118
|
+
await self.zep.memory.add(session_id=user_id, messages=zep_messages)
|
118
119
|
except Exception as e:
|
119
120
|
print(f"Zep memory addition error: {e}")
|
120
121
|
return
|
@@ -137,10 +138,7 @@ class MemoryRepository(MemoryProvider):
|
|
137
138
|
"""Delete memory from both systems."""
|
138
139
|
if self.mongo:
|
139
140
|
try:
|
140
|
-
self.mongo.delete_all(
|
141
|
-
self.collection,
|
142
|
-
{"user_id": user_id}
|
143
|
-
)
|
141
|
+
self.mongo.delete_all(self.collection, {"user_id": user_id})
|
144
142
|
except Exception as e:
|
145
143
|
print(f"MongoDB deletion error: {e}")
|
146
144
|
|
@@ -163,7 +161,7 @@ class MemoryRepository(MemoryProvider):
|
|
163
161
|
query: Dict,
|
164
162
|
sort: Optional[List[Tuple]] = None,
|
165
163
|
limit: int = 0,
|
166
|
-
skip: int = 0
|
164
|
+
skip: int = 0,
|
167
165
|
) -> List[Dict]: # pragma: no cover
|
168
166
|
"""Find documents in MongoDB."""
|
169
167
|
if not self.mongo:
|
@@ -193,9 +191,9 @@ class MemoryRepository(MemoryProvider):
|
|
193
191
|
return text
|
194
192
|
|
195
193
|
# Try to truncate at last period before limit
|
196
|
-
last_period = text.rfind(
|
194
|
+
last_period = text.rfind(".", 0, limit)
|
197
195
|
if last_period > 0:
|
198
|
-
return text[:last_period + 1]
|
196
|
+
return text[: last_period + 1]
|
199
197
|
|
200
198
|
# If no period found, truncate at limit and add ellipsis
|
201
|
-
return text[:limit-3] + "..."
|
199
|
+
return text[: limit - 3] + "..."
|