solana-agent 19.1.0__tar.gz → 20.0.0__tar.gz
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-19.1.0 → solana_agent-20.0.0}/PKG-INFO +6 -4
- {solana_agent-19.1.0 → solana_agent-20.0.0}/pyproject.toml +11 -5
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/client/solana_agent.py +9 -20
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/factories/agent_factory.py +13 -16
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/plugins/manager.py +29 -30
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/plugins/registry.py +20 -3
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/plugins/tools/auto_tool.py +2 -0
- solana_agent-20.0.0/solana_agent/repositories/memory.py +199 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/services/agent.py +5 -2
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/services/routing.py +1 -1
- solana_agent-19.1.0/solana_agent/repositories/memory.py +0 -154
- {solana_agent-19.1.0 → solana_agent-20.0.0}/LICENSE +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/README.md +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/__init__.py +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/adapters/__init__.py +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/adapters/llm_adapter.py +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/adapters/mongodb_adapter.py +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/client/__init__.py +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/domains/__init__.py +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/domains/agent.py +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/domains/routing.py +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/factories/__init__.py +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/interfaces/__init__.py +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/interfaces/client/client.py +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/interfaces/plugins/plugins.py +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/interfaces/providers/data_storage.py +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/interfaces/providers/llm.py +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/interfaces/providers/memory.py +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/interfaces/services/agent.py +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/interfaces/services/query.py +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/interfaces/services/routing.py +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/plugins/__init__.py +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/plugins/tools/__init__.py +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/repositories/__init__.py +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/services/__init__.py +0 -0
- {solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/services/query.py +0 -0
@@ -1,23 +1,25 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: solana-agent
|
3
|
-
Version:
|
3
|
+
Version: 20.0.0
|
4
4
|
Summary: Agentic IQ
|
5
5
|
License: MIT
|
6
6
|
Keywords: ai,openai,ai agents,agi
|
7
7
|
Author: Bevan Hunt
|
8
8
|
Author-email: bevan@bevanhunt.com
|
9
9
|
Requires-Python: >=3.12,<4.0
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
11
|
+
Classifier: Intended Audience :: Developers
|
10
12
|
Classifier: License :: OSI Approved :: MIT License
|
11
13
|
Classifier: Programming Language :: Python :: 3
|
12
14
|
Classifier: Programming Language :: Python :: 3.12
|
13
15
|
Classifier: Programming Language :: Python :: 3.13
|
14
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
15
16
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
16
|
-
Requires-Dist: openai (>=1.
|
17
|
-
Requires-Dist: pydantic (>=2.11.
|
17
|
+
Requires-Dist: openai (>=1.68.2,<2.0.0)
|
18
|
+
Requires-Dist: pydantic (>=2.11.1,<3.0.0)
|
18
19
|
Requires-Dist: pymongo (>=4.11.3,<5.0.0)
|
19
20
|
Requires-Dist: zep-cloud (>=2.8.0,<3.0.0)
|
20
21
|
Requires-Dist: zep-python (>=2.0.2,<3.0.0)
|
22
|
+
Project-URL: Documentation, https://docs.solana-agent.com
|
21
23
|
Project-URL: Repository, https://github.com/truemagic-coder/solana-agent
|
22
24
|
Description-Content-Type: text/markdown
|
23
25
|
|
@@ -1,14 +1,18 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "solana-agent"
|
3
|
-
version = "
|
3
|
+
version = "20.0.0"
|
4
4
|
description = "Agentic IQ"
|
5
5
|
authors = ["Bevan Hunt <bevan@bevanhunt.com>"]
|
6
6
|
license = "MIT"
|
7
7
|
readme = "README.md"
|
8
8
|
repository = "https://github.com/truemagic-coder/solana-agent"
|
9
|
+
documentation = "https://docs.solana-agent.com"
|
9
10
|
keywords = ["ai", "openai", "ai agents", "agi"]
|
10
11
|
classifiers = [
|
11
|
-
"
|
12
|
+
"Development Status :: 5 - Production/Stable",
|
13
|
+
"Intended Audience :: Developers",
|
14
|
+
"Programming Language :: Python :: 3.12",
|
15
|
+
"Programming Language :: Python :: 3.13",
|
12
16
|
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
13
17
|
]
|
14
18
|
packages = [{ include = "solana_agent" }]
|
@@ -18,21 +22,23 @@ python_paths = [".", "tests"]
|
|
18
22
|
|
19
23
|
[tool.poetry.dependencies]
|
20
24
|
python = ">=3.12,<4.0"
|
21
|
-
openai = "^1.
|
22
|
-
pydantic = "^2.11.
|
25
|
+
openai = "^1.68.2"
|
26
|
+
pydantic = "^2.11.1"
|
23
27
|
pymongo = "^4.11.3"
|
24
28
|
zep-cloud = "^2.8.0"
|
25
29
|
zep-python = "^2.0.2"
|
26
30
|
|
27
|
-
[tool.poetry.dev
|
31
|
+
[tool.poetry.group.dev.dependencies]
|
28
32
|
pytest = "^8.3.5"
|
29
33
|
pytest-cov = "^6.0.0"
|
30
34
|
pytest-asyncio = "^0.26.0"
|
35
|
+
pytest-mock = "^3.14.0"
|
31
36
|
pytest-github-actions-annotate-failures = "^0.3.0"
|
32
37
|
sphinx = "^8.2.3"
|
33
38
|
sphinx-rtd-theme = "^3.0.2"
|
34
39
|
myst-parser = "^4.0.1"
|
35
40
|
sphinx-autobuild = "^2024.10.3"
|
41
|
+
mongomock = "^4.3.0"
|
36
42
|
|
37
43
|
[build-system]
|
38
44
|
requires = ["poetry-core>=1.0.0"]
|
@@ -97,7 +97,7 @@ class SolanaAgent(SolanaAgentInterface):
|
|
97
97
|
page_num: int = 1,
|
98
98
|
page_size: int = 20,
|
99
99
|
sort_order: str = "desc" # "asc" for oldest-first, "desc" for newest-first
|
100
|
-
) -> Dict[str, Any]:
|
100
|
+
) -> Dict[str, Any]: # pragma: no cover
|
101
101
|
"""
|
102
102
|
Get paginated message history for a user.
|
103
103
|
|
@@ -124,22 +124,11 @@ class SolanaAgent(SolanaAgentInterface):
|
|
124
124
|
Returns:
|
125
125
|
True if successful, False
|
126
126
|
"""
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
agents = self.query_service.agent_service.get_all_ai_agents()
|
136
|
-
for agent_name in agents:
|
137
|
-
print(f"Assigning {tool.name} to agent {agent_name}")
|
138
|
-
self.query_service.agent_service.assign_tool_for_agent(
|
139
|
-
agent_name, tool.name)
|
140
|
-
return success
|
141
|
-
except Exception as e:
|
142
|
-
print(f"Error in register_tool: {str(e)}")
|
143
|
-
import traceback
|
144
|
-
print(traceback.format_exc())
|
145
|
-
return False
|
127
|
+
success = self.query_service.agent_service.tool_registry.register_tool(
|
128
|
+
tool)
|
129
|
+
if success:
|
130
|
+
agents = self.query_service.agent_service.get_all_ai_agents()
|
131
|
+
for agent_name in agents:
|
132
|
+
self.query_service.agent_service.assign_tool_for_agent(
|
133
|
+
agent_name, tool.name)
|
134
|
+
return success
|
@@ -109,8 +109,12 @@ class SolanaAgentFactory:
|
|
109
109
|
config=config,
|
110
110
|
tool_registry=agent_service.tool_registry
|
111
111
|
)
|
112
|
-
|
113
|
-
|
112
|
+
try:
|
113
|
+
loaded_plugins = agent_service.plugin_manager.load_plugins()
|
114
|
+
print(f"Loaded {loaded_plugins} plugins")
|
115
|
+
except Exception as e:
|
116
|
+
print(f"Error loading plugins: {e}")
|
117
|
+
loaded_plugins = 0
|
114
118
|
|
115
119
|
# Register predefined agents
|
116
120
|
for agent_config in config.get("agents", []):
|
@@ -125,25 +129,18 @@ class SolanaAgentFactory:
|
|
125
129
|
for tool_name in agent_config["tools"]:
|
126
130
|
print(
|
127
131
|
f"Available tools before registering {tool_name}: {agent_service.tool_registry.list_all_tools()}")
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
f"Successfully registered {tool_name} for agent {agent_config['name']}")
|
134
|
-
except ValueError as e:
|
135
|
-
print(
|
136
|
-
f"Error registering tool {tool_name} for agent {agent_config['name']}: {e}")
|
132
|
+
agent_service.assign_tool_for_agent(
|
133
|
+
agent_config["name"], tool_name
|
134
|
+
)
|
135
|
+
print(
|
136
|
+
f"Successfully registered {tool_name} for agent {agent_config['name']}")
|
137
137
|
|
138
138
|
# Global tool registrations
|
139
139
|
if "agent_tools" in config:
|
140
140
|
for agent_name, tools in config["agent_tools"].items():
|
141
141
|
for tool_name in tools:
|
142
|
-
|
143
|
-
|
144
|
-
agent_name, tool_name)
|
145
|
-
except ValueError as e:
|
146
|
-
print(f"Error registering tool: {e}")
|
142
|
+
agent_service.assign_tool_for_agent(
|
143
|
+
agent_name, tool_name)
|
147
144
|
|
148
145
|
# Create and return the query service
|
149
146
|
query_service = QueryService(
|
@@ -35,19 +35,21 @@ class PluginManager(PluginManagerInterface):
|
|
35
35
|
True if registration succeeded, False otherwise
|
36
36
|
"""
|
37
37
|
try:
|
38
|
-
#
|
39
|
-
self._plugins[plugin.name] = plugin
|
40
|
-
|
41
|
-
# Initialize the plugin with the tool registry
|
38
|
+
# Initialize the plugin with the tool registry first
|
42
39
|
plugin.initialize(self.tool_registry)
|
43
40
|
|
44
|
-
#
|
45
|
-
print(f"Configuring plugin {plugin.name} with config")
|
41
|
+
# Then configure the plugin
|
46
42
|
plugin.configure(self.config)
|
47
43
|
|
44
|
+
# Only store plugin if both initialize and configure succeed
|
45
|
+
self._plugins[plugin.name] = plugin
|
46
|
+
print(f"Successfully registered plugin {plugin.name}")
|
48
47
|
return True
|
48
|
+
|
49
49
|
except Exception as e:
|
50
50
|
print(f"Error registering plugin {plugin.name}: {e}")
|
51
|
+
# Remove plugin from registry if it was added
|
52
|
+
self._plugins.pop(plugin.name, None)
|
51
53
|
return False
|
52
54
|
|
53
55
|
def load_plugins(self) -> List[str]:
|
@@ -59,30 +61,27 @@ class PluginManager(PluginManagerInterface):
|
|
59
61
|
loaded_plugins = []
|
60
62
|
|
61
63
|
# Discover plugins through entry points
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
print(f"Error loading plugin {entry_point.name}: {e}")
|
84
|
-
except Exception as e:
|
85
|
-
print(f"Error discovering plugins: {e}")
|
64
|
+
for entry_point in importlib.metadata.entry_points(group='solana_agent.plugins'):
|
65
|
+
# Skip if this entry point has already been loaded
|
66
|
+
entry_point_id = f"{entry_point.name}:{entry_point.value}"
|
67
|
+
if entry_point_id in PluginManager._loaded_entry_points:
|
68
|
+
print(
|
69
|
+
f"Skipping already loaded plugin: {entry_point.name}")
|
70
|
+
continue
|
71
|
+
|
72
|
+
try:
|
73
|
+
print(f"Found plugin entry point: {entry_point.name}")
|
74
|
+
PluginManager._loaded_entry_points.add(entry_point_id)
|
75
|
+
plugin_factory = entry_point.load()
|
76
|
+
plugin = plugin_factory()
|
77
|
+
|
78
|
+
# Register the plugin
|
79
|
+
if self.register_plugin(plugin):
|
80
|
+
# Use entry_point.name instead of plugin.name
|
81
|
+
loaded_plugins.append(entry_point.name)
|
82
|
+
|
83
|
+
except Exception as e:
|
84
|
+
print(f"Error loading plugin {entry_point.name}: {e}")
|
86
85
|
|
87
86
|
return loaded_plugins
|
88
87
|
|
@@ -76,6 +76,23 @@ class ToolRegistry(ToolRegistryInterface):
|
|
76
76
|
return list(self._tools.keys())
|
77
77
|
|
78
78
|
def configure_all_tools(self, config: Dict[str, Any]) -> None:
|
79
|
-
"""Configure all registered tools with
|
80
|
-
|
81
|
-
|
79
|
+
"""Configure all registered tools with new configuration.
|
80
|
+
|
81
|
+
Args:
|
82
|
+
config: Configuration dictionary to apply
|
83
|
+
"""
|
84
|
+
self._config.update(config)
|
85
|
+
configure_errors = []
|
86
|
+
|
87
|
+
for name, tool in self._tools.items():
|
88
|
+
try:
|
89
|
+
print(f"Configuring tool: {name}")
|
90
|
+
tool.configure(self._config)
|
91
|
+
except Exception as e:
|
92
|
+
print(f"Error configuring tool {name}: {e}")
|
93
|
+
configure_errors.append((name, str(e)))
|
94
|
+
|
95
|
+
if configure_errors:
|
96
|
+
print("The following tools failed to configure:")
|
97
|
+
for name, error in configure_errors:
|
98
|
+
print(f"- {name}: {error}")
|
@@ -34,6 +34,8 @@ class AutoTool(Tool):
|
|
34
34
|
|
35
35
|
def configure(self, config: Dict[str, Any]) -> None:
|
36
36
|
"""Configure the tool with settings from config."""
|
37
|
+
if config is None:
|
38
|
+
raise TypeError("Config cannot be None")
|
37
39
|
self._config = config
|
38
40
|
|
39
41
|
def get_schema(self) -> Dict[str, Any]:
|
@@ -0,0 +1,199 @@
|
|
1
|
+
from typing import List, Dict, Any, Optional, Tuple
|
2
|
+
from datetime import datetime, timezone
|
3
|
+
from zep_cloud.client import AsyncZep as AsyncZepCloud
|
4
|
+
from zep_python.client import AsyncZep
|
5
|
+
from zep_cloud.types import Message
|
6
|
+
from solana_agent.interfaces.providers.memory import MemoryProvider
|
7
|
+
from solana_agent.adapters.mongodb_adapter import MongoDBAdapter
|
8
|
+
|
9
|
+
|
10
|
+
class MemoryRepository(MemoryProvider):
|
11
|
+
"""Combined Zep and MongoDB implementation of MemoryProvider."""
|
12
|
+
|
13
|
+
def __init__(
|
14
|
+
self,
|
15
|
+
mongo_adapter: Optional[MongoDBAdapter] = None,
|
16
|
+
zep_api_key: Optional[str] = None,
|
17
|
+
zep_base_url: Optional[str] = None
|
18
|
+
):
|
19
|
+
"""Initialize the combined memory provider."""
|
20
|
+
if not mongo_adapter:
|
21
|
+
self.mongo = None
|
22
|
+
self.collection = None
|
23
|
+
else:
|
24
|
+
# Initialize MongoDB
|
25
|
+
self.mongo = mongo_adapter
|
26
|
+
self.collection = "conversations"
|
27
|
+
|
28
|
+
try:
|
29
|
+
# Ensure MongoDB collection and indexes
|
30
|
+
self.mongo.create_collection(self.collection)
|
31
|
+
self.mongo.create_index(self.collection, [("user_id", 1)])
|
32
|
+
self.mongo.create_index(self.collection, [("timestamp", 1)])
|
33
|
+
except Exception as e:
|
34
|
+
print(f"Error initializing MongoDB: {e}")
|
35
|
+
|
36
|
+
# Initialize Zep
|
37
|
+
if zep_api_key and not zep_base_url:
|
38
|
+
self.zep = AsyncZepCloud(api_key=zep_api_key)
|
39
|
+
elif zep_api_key and zep_base_url:
|
40
|
+
self.zep = AsyncZep(api_key=zep_api_key, base_url=zep_base_url)
|
41
|
+
else:
|
42
|
+
self.zep = None
|
43
|
+
|
44
|
+
async def store(self, user_id: str, messages: List[Dict[str, Any]]) -> None:
|
45
|
+
"""Store messages in both Zep and MongoDB."""
|
46
|
+
if not user_id:
|
47
|
+
raise ValueError("User ID cannot be None or empty")
|
48
|
+
if not messages or not isinstance(messages, list):
|
49
|
+
raise ValueError("Messages must be a non-empty list")
|
50
|
+
if not all(isinstance(msg, dict) and "role" in msg and "content" in msg for msg in messages):
|
51
|
+
raise ValueError(
|
52
|
+
"All messages must be dictionaries with 'role' and 'content' keys")
|
53
|
+
for msg in messages:
|
54
|
+
if msg["role"] not in ["user", "assistant"]:
|
55
|
+
raise ValueError(
|
56
|
+
f"Invalid role '{msg['role']}' in message. Only 'user' and 'assistant' roles are accepted.")
|
57
|
+
|
58
|
+
# Store in MongoDB
|
59
|
+
if self.mongo and len(messages) >= 2:
|
60
|
+
try:
|
61
|
+
# Get last user and assistant messages
|
62
|
+
user_msg = None
|
63
|
+
assistant_msg = None
|
64
|
+
for msg in reversed(messages):
|
65
|
+
if msg.get("role") == "user" and not user_msg:
|
66
|
+
user_msg = msg.get("content")
|
67
|
+
elif msg.get("role") == "assistant" and not assistant_msg:
|
68
|
+
assistant_msg = msg.get("content")
|
69
|
+
if user_msg and assistant_msg:
|
70
|
+
break
|
71
|
+
|
72
|
+
if user_msg and assistant_msg:
|
73
|
+
# Store truncated messages
|
74
|
+
doc = {
|
75
|
+
"user_id": user_id,
|
76
|
+
"user_message": self._truncate(user_msg),
|
77
|
+
"assistant_message": self._truncate(assistant_msg),
|
78
|
+
"timestamp": datetime.now(timezone.utc)
|
79
|
+
}
|
80
|
+
self.mongo.insert_one(self.collection, doc)
|
81
|
+
except Exception as e:
|
82
|
+
print(f"MongoDB storage error: {e}")
|
83
|
+
|
84
|
+
# Store in Zep
|
85
|
+
if not self.zep:
|
86
|
+
return
|
87
|
+
|
88
|
+
try:
|
89
|
+
await self.zep.user.add(user_id=user_id)
|
90
|
+
except Exception as e:
|
91
|
+
print(f"Zep user addition error: {e}")
|
92
|
+
|
93
|
+
try:
|
94
|
+
await self.zep.memory.add_session(session_id=user_id, user_id=user_id)
|
95
|
+
except Exception as e:
|
96
|
+
print(f"Zep session creation error: {e}")
|
97
|
+
|
98
|
+
# Convert messages to Zep format
|
99
|
+
zep_messages = []
|
100
|
+
for msg in messages:
|
101
|
+
if "role" in msg and "content" in msg:
|
102
|
+
zep_msg = Message(
|
103
|
+
role=msg["role"],
|
104
|
+
content=msg["content"],
|
105
|
+
role_type=msg["role"],
|
106
|
+
)
|
107
|
+
zep_messages.append(zep_msg)
|
108
|
+
|
109
|
+
# Add messages to Zep memory
|
110
|
+
if zep_messages:
|
111
|
+
try:
|
112
|
+
await self.zep.memory.add(
|
113
|
+
session_id=user_id,
|
114
|
+
messages=zep_messages
|
115
|
+
)
|
116
|
+
except Exception as e:
|
117
|
+
print(f"Zep memory addition error: {e}")
|
118
|
+
|
119
|
+
async def retrieve(self, user_id: str) -> str:
|
120
|
+
"""Retrieve memory context from Zep only."""
|
121
|
+
if not self.zep:
|
122
|
+
return ""
|
123
|
+
|
124
|
+
try:
|
125
|
+
memory = await self.zep.memory.get(session_id=user_id)
|
126
|
+
if memory is None or not hasattr(memory, 'context') or memory.context is None:
|
127
|
+
return ""
|
128
|
+
return memory.context
|
129
|
+
|
130
|
+
except Exception as e:
|
131
|
+
print(f"Error retrieving Zep memory: {e}")
|
132
|
+
return ""
|
133
|
+
|
134
|
+
async def delete(self, user_id: str) -> None:
|
135
|
+
"""Delete memory from both systems."""
|
136
|
+
if self.mongo:
|
137
|
+
try:
|
138
|
+
self.mongo.delete_all(
|
139
|
+
self.collection,
|
140
|
+
{"user_id": user_id}
|
141
|
+
)
|
142
|
+
except Exception as e:
|
143
|
+
print(f"MongoDB deletion error: {e}")
|
144
|
+
|
145
|
+
if not self.zep:
|
146
|
+
return
|
147
|
+
|
148
|
+
try:
|
149
|
+
await self.zep.memory.delete(session_id=user_id)
|
150
|
+
except Exception as e:
|
151
|
+
print(f"Zep memory deletion error: {e}")
|
152
|
+
|
153
|
+
try:
|
154
|
+
await self.zep.user.delete(user_id=user_id)
|
155
|
+
except Exception as e:
|
156
|
+
print(f"Zep user deletion error: {e}")
|
157
|
+
|
158
|
+
def find(
|
159
|
+
self,
|
160
|
+
collection: str,
|
161
|
+
query: Dict,
|
162
|
+
sort: Optional[List[Tuple]] = None,
|
163
|
+
limit: int = 0,
|
164
|
+
skip: int = 0
|
165
|
+
) -> List[Dict]: # pragma: no cover
|
166
|
+
"""Find documents in MongoDB."""
|
167
|
+
if not self.mongo:
|
168
|
+
return []
|
169
|
+
|
170
|
+
try:
|
171
|
+
return self.mongo.find(collection, query, sort=sort, limit=limit, skip=skip)
|
172
|
+
except Exception as e:
|
173
|
+
print(f"MongoDB find error: {e}")
|
174
|
+
return []
|
175
|
+
|
176
|
+
def count_documents(self, collection: str, query: Dict) -> int:
|
177
|
+
"""Count documents in MongoDB."""
|
178
|
+
if not self.mongo:
|
179
|
+
return 0
|
180
|
+
return self.mongo.count_documents(collection, query)
|
181
|
+
|
182
|
+
def _truncate(self, text: str, limit: int = 2500) -> str:
|
183
|
+
"""Truncate text to be within limits."""
|
184
|
+
if text is None:
|
185
|
+
raise AttributeError("Cannot truncate None text")
|
186
|
+
|
187
|
+
if not text:
|
188
|
+
return ""
|
189
|
+
|
190
|
+
if len(text) <= limit:
|
191
|
+
return text
|
192
|
+
|
193
|
+
# Try to truncate at last period before limit
|
194
|
+
last_period = text.rfind('.', 0, limit)
|
195
|
+
if last_period > 0:
|
196
|
+
return text[:last_period + 1]
|
197
|
+
|
198
|
+
# If no period found, truncate at limit and add ellipsis
|
199
|
+
return text[:limit] + "..."
|
@@ -13,6 +13,7 @@ from typing import AsyncGenerator, Dict, List, Literal, Optional, Any, Union
|
|
13
13
|
from solana_agent.interfaces.services.agent import AgentService as AgentServiceInterface
|
14
14
|
from solana_agent.interfaces.providers.llm import LLMProvider
|
15
15
|
from solana_agent.interfaces.plugins.plugins import ToolRegistry as ToolRegistryInterface
|
16
|
+
from solana_agent.plugins.manager import PluginManager
|
16
17
|
from solana_agent.plugins.registry import ToolRegistry
|
17
18
|
from solana_agent.domains.agent import AIAgent, BusinessMission
|
18
19
|
|
@@ -40,8 +41,10 @@ class AgentService(AgentServiceInterface):
|
|
40
41
|
self.tool_registry = ToolRegistry(config=self.config)
|
41
42
|
self.agents: List[AIAgent] = []
|
42
43
|
|
43
|
-
|
44
|
-
|
44
|
+
self.plugin_manager = PluginManager(
|
45
|
+
config=self.config,
|
46
|
+
tool_registry=self.tool_registry,
|
47
|
+
)
|
45
48
|
|
46
49
|
def register_ai_agent(
|
47
50
|
self, name: str, instructions: str, specialization: str,
|
@@ -1,154 +0,0 @@
|
|
1
|
-
from typing import List, Dict, Any, Optional, Tuple
|
2
|
-
from datetime import datetime, timezone
|
3
|
-
from zep_cloud.client import AsyncZep as AsyncZepCloud
|
4
|
-
from zep_python.client import AsyncZep
|
5
|
-
from zep_cloud.types import Message
|
6
|
-
from solana_agent.interfaces.providers.memory import MemoryProvider
|
7
|
-
from solana_agent.adapters.mongodb_adapter import MongoDBAdapter
|
8
|
-
|
9
|
-
|
10
|
-
class MemoryRepository(MemoryProvider):
|
11
|
-
"""Combined Zep and MongoDB implementation of MemoryProvider."""
|
12
|
-
|
13
|
-
def __init__(
|
14
|
-
self,
|
15
|
-
mongo_adapter: Optional[MongoDBAdapter] = None,
|
16
|
-
zep_api_key: Optional[str] = None,
|
17
|
-
zep_base_url: Optional[str] = None
|
18
|
-
):
|
19
|
-
"""Initialize the combined memory provider."""
|
20
|
-
if not mongo_adapter:
|
21
|
-
self.mongo = None
|
22
|
-
self.collection = None
|
23
|
-
else:
|
24
|
-
# Initialize MongoDB
|
25
|
-
self.mongo = mongo_adapter
|
26
|
-
self.collection = "conversations"
|
27
|
-
|
28
|
-
# Ensure MongoDB collection and indexes
|
29
|
-
self.mongo.create_collection(self.collection)
|
30
|
-
self.mongo.create_index(self.collection, [("user_id", 1)])
|
31
|
-
self.mongo.create_index(self.collection, [("timestamp", 1)])
|
32
|
-
|
33
|
-
# Initialize Zep
|
34
|
-
if zep_api_key and not zep_base_url:
|
35
|
-
self.zep = AsyncZepCloud(api_key=zep_api_key)
|
36
|
-
elif zep_api_key and zep_base_url:
|
37
|
-
self.zep = AsyncZep(api_key=zep_api_key, base_url=zep_base_url)
|
38
|
-
else:
|
39
|
-
self.zep = None
|
40
|
-
|
41
|
-
async def store(self, user_id: str, messages: List[Dict[str, Any]]) -> None:
|
42
|
-
"""Store messages in both Zep and MongoDB."""
|
43
|
-
# Store in MongoDB as single document
|
44
|
-
if self.mongo:
|
45
|
-
try:
|
46
|
-
# Extract user and assistant messages
|
47
|
-
user_message = next(msg["content"]
|
48
|
-
for msg in messages if msg["role"] == "user")
|
49
|
-
assistant_message = next(
|
50
|
-
msg["content"] for msg in messages if msg["role"] == "assistant")
|
51
|
-
|
52
|
-
doc = {
|
53
|
-
"user_id": user_id,
|
54
|
-
"user_message": user_message,
|
55
|
-
"assistant_message": assistant_message,
|
56
|
-
"timestamp": datetime.now(timezone.utc)
|
57
|
-
}
|
58
|
-
self.mongo.insert_one(self.collection, doc)
|
59
|
-
except Exception as e:
|
60
|
-
print(f"MongoDB storage error: {e}")
|
61
|
-
|
62
|
-
# Store in Zep with role-based format
|
63
|
-
if not self.zep:
|
64
|
-
return
|
65
|
-
|
66
|
-
try:
|
67
|
-
try:
|
68
|
-
await self.zep.user.add(user_id=user_id)
|
69
|
-
except Exception:
|
70
|
-
pass
|
71
|
-
try:
|
72
|
-
await self.zep.memory.add_session(
|
73
|
-
session_id=user_id,
|
74
|
-
user_id=user_id,
|
75
|
-
)
|
76
|
-
except Exception:
|
77
|
-
pass
|
78
|
-
|
79
|
-
zep_messages = [
|
80
|
-
Message(
|
81
|
-
role=msg["role"],
|
82
|
-
role_type=msg["role"],
|
83
|
-
content=self._truncate(msg["content"])
|
84
|
-
)
|
85
|
-
for msg in messages
|
86
|
-
]
|
87
|
-
await self.zep.memory.add(session_id=user_id, messages=zep_messages)
|
88
|
-
except Exception as e:
|
89
|
-
print(f"Zep storage error: {e}")
|
90
|
-
|
91
|
-
async def retrieve(self, user_id: str) -> str:
|
92
|
-
"""Retrieve memory context from Zep only."""
|
93
|
-
if not self.zep:
|
94
|
-
return ""
|
95
|
-
|
96
|
-
try:
|
97
|
-
memory = await self.zep.memory.get(session_id=user_id)
|
98
|
-
|
99
|
-
return memory.context
|
100
|
-
|
101
|
-
except Exception as e:
|
102
|
-
print(f"Error retrieving Zep memory: {e}")
|
103
|
-
return ""
|
104
|
-
|
105
|
-
async def delete(self, user_id: str) -> None:
|
106
|
-
"""Delete memory from both systems."""
|
107
|
-
if self.mongo:
|
108
|
-
try:
|
109
|
-
self.mongo.delete_all(
|
110
|
-
self.collection,
|
111
|
-
{"user_id": user_id}
|
112
|
-
)
|
113
|
-
except Exception as e:
|
114
|
-
print(f"MongoDB deletion error: {e}")
|
115
|
-
|
116
|
-
if not self.zep:
|
117
|
-
return
|
118
|
-
|
119
|
-
try:
|
120
|
-
await self.zep.memory.delete(session_id=user_id)
|
121
|
-
await self.zep.user.delete(user_id=user_id)
|
122
|
-
except Exception as e:
|
123
|
-
print(f"Zep deletion error: {e}")
|
124
|
-
|
125
|
-
def find(
|
126
|
-
self,
|
127
|
-
collection: str,
|
128
|
-
query: Dict,
|
129
|
-
sort: Optional[List[Tuple]] = None,
|
130
|
-
limit: int = 0,
|
131
|
-
skip: int = 0
|
132
|
-
) -> List[Dict]:
|
133
|
-
"""Find documents matching query."""
|
134
|
-
if not self.mongo:
|
135
|
-
return []
|
136
|
-
return self.mongo.find(collection, query, sort=sort, limit=limit, skip=skip)
|
137
|
-
|
138
|
-
def count_documents(self, collection: str, query: Dict) -> int:
|
139
|
-
"""Count documents matching query."""
|
140
|
-
if not self.mongo:
|
141
|
-
return 0
|
142
|
-
return self.mongo.count_documents(collection, query)
|
143
|
-
|
144
|
-
def _truncate(self, text: str, limit: int = 2500) -> str:
|
145
|
-
"""Truncate text to be within limits."""
|
146
|
-
if len(text) <= limit:
|
147
|
-
return text
|
148
|
-
|
149
|
-
truncated = text[:limit]
|
150
|
-
last_period = truncated.rfind(".")
|
151
|
-
if last_period > limit * 0.8:
|
152
|
-
return truncated[:last_period + 1]
|
153
|
-
|
154
|
-
return truncated + "..."
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{solana_agent-19.1.0 → solana_agent-20.0.0}/solana_agent/interfaces/providers/data_storage.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|