solana-agent 12.0.0__py3-none-any.whl → 14.0.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.
- solana_agent/__init__.py +33 -1
- solana_agent/adapters/__init__.py +6 -0
- solana_agent/adapters/llm_adapter.py +123 -0
- solana_agent/adapters/mongodb_adapter.py +69 -0
- solana_agent/client/__init__.py +0 -0
- solana_agent/client/solana_agent.py +75 -0
- solana_agent/domains/__init__.py +6 -0
- solana_agent/domains/agents.py +80 -0
- solana_agent/domains/routing.py +14 -0
- solana_agent/factories/__init__.py +0 -0
- solana_agent/factories/agent_factory.py +148 -0
- solana_agent/interfaces/__init__.py +11 -0
- solana_agent/interfaces/plugins/plugins.py +118 -0
- solana_agent/interfaces/providers/data_storage.py +53 -0
- solana_agent/interfaces/providers/llm.py +34 -0
- solana_agent/interfaces/providers/memory.py +38 -0
- solana_agent/interfaces/repositories/agent.py +33 -0
- solana_agent/interfaces/services/agent.py +41 -0
- solana_agent/interfaces/services/query.py +11 -0
- solana_agent/interfaces/services/routing.py +19 -0
- solana_agent/plugins/__init__.py +6 -0
- solana_agent/plugins/manager.py +147 -0
- solana_agent/plugins/registry.py +64 -0
- solana_agent/plugins/tools/__init__.py +10 -0
- solana_agent/plugins/tools/auto_tool.py +47 -0
- solana_agent/repositories/__init__.py +6 -0
- solana_agent/repositories/agent.py +99 -0
- solana_agent/repositories/memory.py +134 -0
- solana_agent/services/__init__.py +6 -0
- solana_agent/services/agent.py +323 -0
- solana_agent/services/query.py +223 -0
- solana_agent/services/routing.py +149 -0
- solana_agent-14.0.1.dist-info/METADATA +126 -0
- solana_agent-14.0.1.dist-info/RECORD +36 -0
- solana_agent/ai.py +0 -8015
- solana_agent-12.0.0.dist-info/METADATA +0 -395
- solana_agent-12.0.0.dist-info/RECORD +0 -6
- {solana_agent-12.0.0.dist-info → solana_agent-14.0.1.dist-info}/LICENSE +0 -0
- {solana_agent-12.0.0.dist-info → solana_agent-14.0.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,99 @@
|
|
1
|
+
"""
|
2
|
+
MongoDB implementation of the agent repository.
|
3
|
+
"""
|
4
|
+
from typing import List, Optional, Any
|
5
|
+
|
6
|
+
from solana_agent.domains.agents import AIAgent
|
7
|
+
from solana_agent.interfaces.repositories.agent import AgentRepository
|
8
|
+
|
9
|
+
|
10
|
+
class MongoAgentRepository(AgentRepository):
|
11
|
+
"""MongoDB implementation of the AgentRepository interface."""
|
12
|
+
|
13
|
+
def __init__(self, db_adapter):
|
14
|
+
"""Initialize the repository with a database adapter."""
|
15
|
+
self.db = db_adapter
|
16
|
+
self.ai_agents_collection = "agents"
|
17
|
+
|
18
|
+
# Ensure collections exist
|
19
|
+
self.db.create_collection(self.ai_agents_collection)
|
20
|
+
|
21
|
+
# Create indexes
|
22
|
+
self.db.create_index(self.ai_agents_collection,
|
23
|
+
[("name", 1)], unique=True)
|
24
|
+
|
25
|
+
def get_ai_agent_by_name(self, name: str) -> Optional[AIAgent]:
|
26
|
+
"""Get an AI agent by name.
|
27
|
+
|
28
|
+
Args:
|
29
|
+
name: The name of the AI agent
|
30
|
+
|
31
|
+
Returns:
|
32
|
+
The AI agent or None if not found
|
33
|
+
"""
|
34
|
+
# Query the AI agents collection for a document with matching name
|
35
|
+
doc = self.db.find_one(self.ai_agents_collection, {"name": name})
|
36
|
+
|
37
|
+
# If no document found, return None
|
38
|
+
if not doc:
|
39
|
+
return None
|
40
|
+
|
41
|
+
# Convert the document to an AIAgent domain model
|
42
|
+
try:
|
43
|
+
return AIAgent.model_validate(doc)
|
44
|
+
except Exception as e:
|
45
|
+
print(f"Error parsing AI agent with name {name}: {e}")
|
46
|
+
return None
|
47
|
+
|
48
|
+
def get_ai_agent(self, name: str) -> Optional[AIAgent]:
|
49
|
+
"""Get an AI agent by name."""
|
50
|
+
doc = self.db.find_one(self.ai_agents_collection, {"name": name})
|
51
|
+
if not doc:
|
52
|
+
return None
|
53
|
+
|
54
|
+
return AIAgent.model_validate(doc)
|
55
|
+
|
56
|
+
def get_all_ai_agents(self) -> List[AIAgent]:
|
57
|
+
"""Get all AI agents in the system.
|
58
|
+
|
59
|
+
Returns:
|
60
|
+
List of all AI agents
|
61
|
+
"""
|
62
|
+
# Query all documents from the AI agents collection
|
63
|
+
docs = self.db.find(self.ai_agents_collection, {})
|
64
|
+
|
65
|
+
# Convert each document to an AIAgent domain model
|
66
|
+
ai_agents = []
|
67
|
+
for doc in docs:
|
68
|
+
try:
|
69
|
+
agent = AIAgent.model_validate(doc)
|
70
|
+
ai_agents.append(agent)
|
71
|
+
except Exception as e:
|
72
|
+
# Log the error but continue processing other agents
|
73
|
+
print(f"Error parsing AI agent from database: {e}")
|
74
|
+
|
75
|
+
return ai_agents
|
76
|
+
|
77
|
+
def save_ai_agent(self, agent: AIAgent) -> bool:
|
78
|
+
"""Save an AI agent."""
|
79
|
+
doc = agent.model_dump()
|
80
|
+
|
81
|
+
# Check if agent already exists
|
82
|
+
existing = self.db.find_one(
|
83
|
+
self.ai_agents_collection, {"name": agent.name})
|
84
|
+
|
85
|
+
if existing:
|
86
|
+
# Update existing agent
|
87
|
+
return self.db.update_one(
|
88
|
+
self.ai_agents_collection,
|
89
|
+
{"name": agent.name},
|
90
|
+
{"$set": doc}
|
91
|
+
)
|
92
|
+
else:
|
93
|
+
# Create new agent
|
94
|
+
self.db.insert_one(self.ai_agents_collection, doc)
|
95
|
+
return True
|
96
|
+
|
97
|
+
def delete_ai_agent(self, name: str) -> bool:
|
98
|
+
"""Delete an AI agent."""
|
99
|
+
return self.db.delete_one(self.ai_agents_collection, {"name": name})
|
@@ -0,0 +1,134 @@
|
|
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.interfaces.providers.data_storage import DataStorageProvider
|
8
|
+
|
9
|
+
|
10
|
+
class MemoryRepository(MemoryProvider):
|
11
|
+
"""Combined Zep and MongoDB implementation of MemoryProvider."""
|
12
|
+
|
13
|
+
def __init__(
|
14
|
+
self,
|
15
|
+
mongo_adapter: DataStorageProvider,
|
16
|
+
zep_api_key: Optional[str] = None,
|
17
|
+
zep_base_url: Optional[str] = None
|
18
|
+
):
|
19
|
+
"""Initialize the combined memory provider."""
|
20
|
+
# Initialize MongoDB
|
21
|
+
self.mongo = mongo_adapter
|
22
|
+
self.collection = "conversations"
|
23
|
+
|
24
|
+
# Ensure MongoDB collection and indexes
|
25
|
+
self.mongo.create_collection(self.collection)
|
26
|
+
self.mongo.create_index(self.collection, [("user_id", 1)])
|
27
|
+
self.mongo.create_index(self.collection, [("timestamp", 1)])
|
28
|
+
|
29
|
+
# Initialize Zep
|
30
|
+
if zep_api_key and not zep_base_url:
|
31
|
+
self.zep = AsyncZepCloud(api_key=zep_api_key)
|
32
|
+
elif zep_api_key and zep_base_url:
|
33
|
+
self.zep = AsyncZep(api_key=zep_api_key, base_url=zep_base_url)
|
34
|
+
else:
|
35
|
+
self.zep = AsyncZep(base_url="http://localhost:8000")
|
36
|
+
|
37
|
+
async def store(self, user_id: str, messages: List[Dict[str, Any]]) -> None:
|
38
|
+
"""Store messages in both Zep and MongoDB."""
|
39
|
+
# Store in MongoDB as single document
|
40
|
+
try:
|
41
|
+
# Extract user and assistant messages
|
42
|
+
user_message = next(msg["content"]
|
43
|
+
for msg in messages if msg["role"] == "user")
|
44
|
+
assistant_message = next(
|
45
|
+
msg["content"] for msg in messages if msg["role"] == "assistant")
|
46
|
+
|
47
|
+
doc = {
|
48
|
+
"user_id": user_id,
|
49
|
+
"user_message": self._truncate(user_message),
|
50
|
+
"assistant_message": self._truncate(assistant_message),
|
51
|
+
"timestamp": datetime.now(timezone.utc)
|
52
|
+
}
|
53
|
+
self.mongo.insert_one(self.collection, doc)
|
54
|
+
except Exception as e:
|
55
|
+
print(f"MongoDB storage error: {e}")
|
56
|
+
|
57
|
+
# Store in Zep with role-based format
|
58
|
+
try:
|
59
|
+
try:
|
60
|
+
await self.zep.user.add(user_id=user_id)
|
61
|
+
except Exception:
|
62
|
+
pass
|
63
|
+
try:
|
64
|
+
await self.zep.memory.add_session(
|
65
|
+
session_id=user_id,
|
66
|
+
user_id=user_id,
|
67
|
+
)
|
68
|
+
except Exception:
|
69
|
+
pass
|
70
|
+
|
71
|
+
zep_messages = [
|
72
|
+
Message(
|
73
|
+
role=msg["role"],
|
74
|
+
role_type=msg["role"],
|
75
|
+
content=self._truncate(msg["content"])
|
76
|
+
)
|
77
|
+
for msg in messages
|
78
|
+
]
|
79
|
+
await self.zep.memory.add(session_id=user_id, messages=zep_messages)
|
80
|
+
except Exception as e:
|
81
|
+
print(f"Zep storage error: {e}")
|
82
|
+
|
83
|
+
async def retrieve(self, user_id: str) -> str:
|
84
|
+
"""Retrieve memory context from Zep only."""
|
85
|
+
try:
|
86
|
+
memory = await self.zep.memory.get(session_id=user_id)
|
87
|
+
|
88
|
+
return memory.context
|
89
|
+
|
90
|
+
except Exception as e:
|
91
|
+
print(f"Error retrieving Zep memory: {e}")
|
92
|
+
return ""
|
93
|
+
|
94
|
+
async def delete(self, user_id: str) -> None:
|
95
|
+
"""Delete memory from both systems."""
|
96
|
+
try:
|
97
|
+
self.mongo.delete_many(
|
98
|
+
self.collection,
|
99
|
+
{"user_id": user_id}
|
100
|
+
)
|
101
|
+
except Exception as e:
|
102
|
+
print(f"MongoDB deletion error: {e}")
|
103
|
+
|
104
|
+
try:
|
105
|
+
await self.zep.memory.delete(session_id=user_id)
|
106
|
+
await self.zep.user.delete(user_id=user_id)
|
107
|
+
except Exception as e:
|
108
|
+
print(f"Zep deletion error: {e}")
|
109
|
+
|
110
|
+
def find(
|
111
|
+
self,
|
112
|
+
collection: str,
|
113
|
+
query: Dict,
|
114
|
+
sort: Optional[List[Tuple]] = None,
|
115
|
+
limit: int = 0,
|
116
|
+
skip: int = 0
|
117
|
+
) -> List[Dict]:
|
118
|
+
"""Find documents matching query."""
|
119
|
+
return self.mongo.find(collection, query, sort=sort, limit=limit, skip=skip)
|
120
|
+
|
121
|
+
def count_documents(self, collection: str, query: Dict) -> int:
|
122
|
+
return self.mongo.count_documents(collection, query)
|
123
|
+
|
124
|
+
def _truncate(self, text: str, limit: int = 2500) -> str:
|
125
|
+
"""Truncate text to be within limits."""
|
126
|
+
if len(text) <= limit:
|
127
|
+
return text
|
128
|
+
|
129
|
+
truncated = text[:limit]
|
130
|
+
last_period = truncated.rfind(".")
|
131
|
+
if last_period > limit * 0.8:
|
132
|
+
return truncated[:last_period + 1]
|
133
|
+
|
134
|
+
return truncated + "..."
|
@@ -0,0 +1,323 @@
|
|
1
|
+
"""
|
2
|
+
Agent service implementation.
|
3
|
+
|
4
|
+
This service manages AI and human agents, their registration, tool assignments,
|
5
|
+
and response generation.
|
6
|
+
"""
|
7
|
+
import datetime as main_datetime
|
8
|
+
from datetime import datetime
|
9
|
+
import json
|
10
|
+
from typing import AsyncGenerator, Dict, List, Optional, Any
|
11
|
+
|
12
|
+
from solana_agent.interfaces.services.agent import AgentService as AgentServiceInterface
|
13
|
+
from solana_agent.interfaces.providers.llm import LLMProvider
|
14
|
+
from solana_agent.interfaces.repositories.agent import AgentRepository
|
15
|
+
from solana_agent.interfaces.plugins.plugins import ToolRegistry
|
16
|
+
from solana_agent.domains.agents import AIAgent, OrganizationMission
|
17
|
+
|
18
|
+
|
19
|
+
class AgentService(AgentServiceInterface):
|
20
|
+
"""Service for managing agents and generating responses."""
|
21
|
+
|
22
|
+
def __init__(
|
23
|
+
self,
|
24
|
+
llm_provider: LLMProvider,
|
25
|
+
agent_repository: AgentRepository,
|
26
|
+
organization_mission: Optional[OrganizationMission] = None,
|
27
|
+
config: Optional[Dict[str, Any]] = None,
|
28
|
+
tool_registry: Optional[ToolRegistry] = None,
|
29
|
+
):
|
30
|
+
"""Initialize the agent service.
|
31
|
+
|
32
|
+
Args:
|
33
|
+
llm_provider: Provider for language model interactions
|
34
|
+
agent_repository: Repository for agent data
|
35
|
+
organization_mission: Optional organization mission and values
|
36
|
+
config: Optional service configuration
|
37
|
+
"""
|
38
|
+
self.llm_provider = llm_provider
|
39
|
+
self.agent_repository = agent_repository
|
40
|
+
self.organization_mission = organization_mission
|
41
|
+
self.config = config or {}
|
42
|
+
|
43
|
+
# Initialize tool registry with concrete implementation
|
44
|
+
if tool_registry:
|
45
|
+
self.tool_registry = tool_registry
|
46
|
+
else:
|
47
|
+
# Import the concrete implementation
|
48
|
+
from solana_agent.plugins.registry import ToolRegistry as ConcreteToolRegistry
|
49
|
+
self.tool_registry = ConcreteToolRegistry()
|
50
|
+
|
51
|
+
# Will be set by factory if plugin system is enabled
|
52
|
+
self.plugin_manager = None
|
53
|
+
|
54
|
+
def register_ai_agent(
|
55
|
+
self, name: str, instructions: str, specialization: str, model: str = "gpt-4o-mini"
|
56
|
+
) -> None:
|
57
|
+
"""Register an AI agent with its specialization.
|
58
|
+
|
59
|
+
Args:
|
60
|
+
name: Agent name
|
61
|
+
instructions: Agent instructions
|
62
|
+
specialization: Agent specialization
|
63
|
+
model: LLM model to use
|
64
|
+
"""
|
65
|
+
agent = AIAgent(
|
66
|
+
name=name,
|
67
|
+
instructions=instructions,
|
68
|
+
specialization=specialization,
|
69
|
+
model=model
|
70
|
+
)
|
71
|
+
self.agent_repository.save_ai_agent(agent)
|
72
|
+
|
73
|
+
def get_agent_system_prompt(self, agent_name: str) -> str:
|
74
|
+
"""Get the system prompt for an agent.
|
75
|
+
|
76
|
+
Args:
|
77
|
+
agent_name: Agent name
|
78
|
+
|
79
|
+
Returns:
|
80
|
+
System prompt
|
81
|
+
"""
|
82
|
+
agent = self.agent_repository.get_ai_agent_by_name(agent_name)
|
83
|
+
|
84
|
+
# Build system prompt
|
85
|
+
system_prompt = f"You are {agent.name}, an AI assistant with the following instructions:\n\n"
|
86
|
+
system_prompt += agent.instructions
|
87
|
+
|
88
|
+
# add current time
|
89
|
+
system_prompt += f"\n\nThe current time is {datetime.now(tz=main_datetime.timezone.utc)}\n\n."
|
90
|
+
|
91
|
+
# Add mission and values if available
|
92
|
+
if self.organization_mission:
|
93
|
+
system_prompt += f"\n\nORGANIZATION MISSION:\n{self.organization_mission.mission_statement}"
|
94
|
+
|
95
|
+
if self.organization_mission.values:
|
96
|
+
values_text = "\n".join([
|
97
|
+
f"- {value.get('name', '')}: {value.get('description', '')}"
|
98
|
+
for value in self.organization_mission.values
|
99
|
+
])
|
100
|
+
system_prompt += f"\n\nORGANIZATION VALUES:\n{values_text}"
|
101
|
+
|
102
|
+
# Add organization goals if available
|
103
|
+
if self.organization_mission and self.organization_mission.goals:
|
104
|
+
goals_text = "\n".join(
|
105
|
+
[f"- {goal}" for goal in self.organization_mission.goals])
|
106
|
+
system_prompt += f"\n\nORGANIZATION GOALS:\n{goals_text}"
|
107
|
+
|
108
|
+
return system_prompt
|
109
|
+
|
110
|
+
def get_all_ai_agents(self) -> Dict[str, AIAgent]:
|
111
|
+
"""Get all registered AI agents.
|
112
|
+
|
113
|
+
Returns:
|
114
|
+
Dictionary mapping agent names to agents
|
115
|
+
"""
|
116
|
+
agents = self.agent_repository.get_all_ai_agents()
|
117
|
+
return {agent.name: agent for agent in agents}
|
118
|
+
|
119
|
+
def get_specializations(self) -> Dict[str, str]:
|
120
|
+
"""Get all registered specializations.
|
121
|
+
|
122
|
+
Returns:
|
123
|
+
Dictionary mapping specialization names to descriptions
|
124
|
+
"""
|
125
|
+
specializations = {}
|
126
|
+
|
127
|
+
# Gather from AI agents
|
128
|
+
ai_agents = self.agent_repository.get_all_ai_agents()
|
129
|
+
for agent in ai_agents:
|
130
|
+
if agent.specialization:
|
131
|
+
specializations[agent.specialization] = f"AI expertise in {agent.specialization}"
|
132
|
+
|
133
|
+
return specializations
|
134
|
+
|
135
|
+
def assign_tool_for_agent(self, agent_name: str, tool_name: str) -> bool:
|
136
|
+
"""Assign a tool to an agent.
|
137
|
+
|
138
|
+
Args:
|
139
|
+
agent_name: Agent name
|
140
|
+
tool_name: Tool name
|
141
|
+
|
142
|
+
Returns:
|
143
|
+
True if assignment was successful
|
144
|
+
"""
|
145
|
+
return self.tool_registry.assign_tool_to_agent(agent_name, tool_name)
|
146
|
+
|
147
|
+
def get_agent_tools(self, agent_name: str) -> List[Dict[str, Any]]:
|
148
|
+
"""Get tools available to an agent.
|
149
|
+
|
150
|
+
Args:
|
151
|
+
agent_name: Agent name
|
152
|
+
|
153
|
+
Returns:
|
154
|
+
List of tool configurations
|
155
|
+
"""
|
156
|
+
return self.tool_registry.get_agent_tools(agent_name)
|
157
|
+
|
158
|
+
def execute_tool(self, agent_name: str, tool_name: str, parameters: Dict[str, Any]) -> Dict[str, Any]:
|
159
|
+
"""Execute a tool on behalf of an agent.
|
160
|
+
|
161
|
+
Args:
|
162
|
+
agent_name: Agent name
|
163
|
+
tool_name: Tool name
|
164
|
+
parameters: Tool parameters
|
165
|
+
|
166
|
+
Returns:
|
167
|
+
Tool execution result
|
168
|
+
"""
|
169
|
+
if not self.tool_registry:
|
170
|
+
return {"status": "error", "message": "Tool registry not available"}
|
171
|
+
|
172
|
+
tool = self.tool_registry.get_tool(tool_name)
|
173
|
+
if not tool:
|
174
|
+
return {"status": "error", "message": f"Tool '{tool_name}' not found"}
|
175
|
+
|
176
|
+
# Check if agent has access to this tool
|
177
|
+
agent_tools = self.tool_registry.get_agent_tools(agent_name)
|
178
|
+
if not any(t.get("name") == tool_name for t in agent_tools):
|
179
|
+
return {
|
180
|
+
"status": "error",
|
181
|
+
"message": f"Agent '{agent_name}' doesn't have access to tool '{tool_name}'"
|
182
|
+
}
|
183
|
+
|
184
|
+
try:
|
185
|
+
return tool.execute(**parameters)
|
186
|
+
except Exception as e:
|
187
|
+
return {"status": "error", "message": f"Error executing tool: {str(e)}"}
|
188
|
+
|
189
|
+
async def generate_response(
|
190
|
+
self,
|
191
|
+
agent_name: str,
|
192
|
+
user_id: str,
|
193
|
+
query: str,
|
194
|
+
memory_context: str = "",
|
195
|
+
**kwargs
|
196
|
+
) -> AsyncGenerator[str, None]: # pragma: no cover
|
197
|
+
"""Generate a response with tool execution support."""
|
198
|
+
agent = self.agent_repository.get_ai_agent_by_name(agent_name)
|
199
|
+
if not agent:
|
200
|
+
yield f"Agent '{agent_name}' not found."
|
201
|
+
return
|
202
|
+
|
203
|
+
# Get system prompt and add tool instructions
|
204
|
+
system_prompt = self.get_agent_system_prompt(agent_name)
|
205
|
+
if self.tool_registry:
|
206
|
+
tool_usage_prompt = self._get_tool_usage_prompt(agent_name)
|
207
|
+
if tool_usage_prompt:
|
208
|
+
system_prompt = f"{system_prompt}\n\n{tool_usage_prompt}"
|
209
|
+
|
210
|
+
# Add User ID context
|
211
|
+
system_prompt += f"\n\n User ID: {user_id}"
|
212
|
+
|
213
|
+
# Add memory context
|
214
|
+
if memory_context:
|
215
|
+
system_prompt += f"\n\n Memory Context: {memory_context}"
|
216
|
+
|
217
|
+
try:
|
218
|
+
json_response = ""
|
219
|
+
is_json = False
|
220
|
+
|
221
|
+
async for chunk in self.llm_provider.generate_text(
|
222
|
+
user_id=user_id,
|
223
|
+
prompt=query,
|
224
|
+
system_prompt=system_prompt,
|
225
|
+
model=agent.model,
|
226
|
+
needs_search=True, # Enable web search by default
|
227
|
+
**kwargs
|
228
|
+
):
|
229
|
+
# Check for JSON start
|
230
|
+
if chunk.strip().startswith("{"):
|
231
|
+
is_json = True
|
232
|
+
json_response = chunk
|
233
|
+
continue
|
234
|
+
|
235
|
+
# Collect JSON or yield normal text
|
236
|
+
if is_json:
|
237
|
+
json_response += chunk
|
238
|
+
try:
|
239
|
+
# Try to parse complete JSON
|
240
|
+
data = json.loads(json_response)
|
241
|
+
|
242
|
+
# Handle tool call
|
243
|
+
if "tool_call" in data:
|
244
|
+
tool_data = data["tool_call"]
|
245
|
+
tool_name = tool_data.get("name")
|
246
|
+
parameters = tool_data.get("parameters", {})
|
247
|
+
|
248
|
+
if tool_name:
|
249
|
+
result = self.execute_tool(
|
250
|
+
agent_name, tool_name, parameters)
|
251
|
+
if result.get("status") == "success":
|
252
|
+
yield result.get("result", "")
|
253
|
+
else:
|
254
|
+
yield f"I apologize, but I encountered an issue: {result.get('message', 'Unknown error')}"
|
255
|
+
break
|
256
|
+
else:
|
257
|
+
# If JSON but not a tool call, yield as text
|
258
|
+
yield json_response
|
259
|
+
break
|
260
|
+
except json.JSONDecodeError:
|
261
|
+
# Not complete JSON yet, keep collecting
|
262
|
+
continue
|
263
|
+
else:
|
264
|
+
yield chunk
|
265
|
+
|
266
|
+
except Exception as e:
|
267
|
+
print(f"Error in generate_response: {str(e)}")
|
268
|
+
import traceback
|
269
|
+
print(traceback.format_exc())
|
270
|
+
yield f"I apologize, but I encountered an error: {str(e)}"
|
271
|
+
|
272
|
+
def _get_tool_usage_prompt(self, agent_name: str) -> str:
|
273
|
+
"""Generate JSON-based instructions for tool usage."""
|
274
|
+
# Get tools assigned to this agent
|
275
|
+
tools = self.get_agent_tools(agent_name)
|
276
|
+
if not tools:
|
277
|
+
return ""
|
278
|
+
|
279
|
+
# Get actual tool names
|
280
|
+
available_tool_names = [tool.get("name", "") for tool in tools]
|
281
|
+
tools_json = json.dumps(tools, indent=2)
|
282
|
+
|
283
|
+
# Create tool example if search is available
|
284
|
+
tool_example = ""
|
285
|
+
if "search_internet" in available_tool_names:
|
286
|
+
tool_example = """
|
287
|
+
For latest news query:
|
288
|
+
{
|
289
|
+
"tool_call": {
|
290
|
+
"name": "search_internet",
|
291
|
+
"parameters": {
|
292
|
+
"query": "latest Solana blockchain news March 2025"
|
293
|
+
}
|
294
|
+
}
|
295
|
+
}"""
|
296
|
+
|
297
|
+
return f"""
|
298
|
+
AVAILABLE TOOLS:
|
299
|
+
{tools_json}
|
300
|
+
|
301
|
+
TOOL USAGE FORMAT:
|
302
|
+
{{
|
303
|
+
"tool_call": {{
|
304
|
+
"name": "<one_of:{', '.join(available_tool_names)}>",
|
305
|
+
"parameters": {{
|
306
|
+
// parameters as specified in tool definition above
|
307
|
+
}}
|
308
|
+
}}
|
309
|
+
}}
|
310
|
+
|
311
|
+
{tool_example if tool_example else ''}
|
312
|
+
|
313
|
+
RESPONSE RULES:
|
314
|
+
1. For tool usage:
|
315
|
+
- Only use tools from the AVAILABLE TOOLS list above
|
316
|
+
- Follow the exact parameter format shown in the tool definition
|
317
|
+
- Include "March 2025" in any search queries for current information
|
318
|
+
|
319
|
+
2. Format Requirements:
|
320
|
+
- Return ONLY the JSON object for tool calls
|
321
|
+
- No explanation text before or after
|
322
|
+
- Use exact tool names as shown in AVAILABLE TOOLS
|
323
|
+
"""
|