dacp 0.3.1__py3-none-any.whl → 0.3.3__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.
- dacp/__init__.py +44 -5
- dacp/intelligence.py +230 -305
- dacp/json_parser.py +232 -0
- dacp/llm.py +13 -6
- dacp/logging_config.py +17 -16
- dacp/main.py +7 -13
- dacp/orchestrator.py +230 -182
- dacp/tools.py +64 -45
- dacp/workflow.py +409 -0
- dacp/workflow_runtime.py +508 -0
- {dacp-0.3.1.dist-info → dacp-0.3.3.dist-info}/METADATA +342 -1
- dacp-0.3.3.dist-info/RECORD +18 -0
- dacp-0.3.1.dist-info/RECORD +0 -15
- {dacp-0.3.1.dist-info → dacp-0.3.3.dist-info}/WHEEL +0 -0
- {dacp-0.3.1.dist-info → dacp-0.3.3.dist-info}/licenses/LICENSE +0 -0
- {dacp-0.3.1.dist-info → dacp-0.3.3.dist-info}/top_level.txt +0 -0
dacp/orchestrator.py
CHANGED
@@ -1,248 +1,296 @@
|
|
1
1
|
"""
|
2
|
-
DACP Orchestrator -
|
2
|
+
DACP Orchestrator - Agent management and message routing.
|
3
|
+
|
4
|
+
This module provides the core orchestrator functionality for managing agents
|
5
|
+
and routing messages between them.
|
3
6
|
"""
|
4
7
|
|
5
8
|
import logging
|
6
9
|
import time
|
7
10
|
from typing import Dict, Any, List, Optional
|
8
|
-
|
9
|
-
from .
|
10
|
-
|
11
|
-
is_tool_request,
|
12
|
-
get_tool_request,
|
13
|
-
wrap_tool_result,
|
14
|
-
is_final_response,
|
15
|
-
get_final_response,
|
16
|
-
)
|
17
|
-
|
18
|
-
# Set up logger for this module
|
11
|
+
|
12
|
+
from .tools import execute_tool
|
13
|
+
|
19
14
|
logger = logging.getLogger("dacp.orchestrator")
|
20
15
|
|
21
16
|
|
22
17
|
class Agent:
|
23
|
-
"""
|
24
|
-
|
18
|
+
"""
|
19
|
+
Base agent class that all DACP agents should inherit from.
|
20
|
+
|
21
|
+
This provides the standard interface for agent communication.
|
22
|
+
"""
|
23
|
+
|
25
24
|
def handle_message(self, message: Dict[str, Any]) -> Dict[str, Any]:
|
26
|
-
"""
|
25
|
+
"""
|
26
|
+
Handle incoming messages.
|
27
|
+
|
28
|
+
Args:
|
29
|
+
message: Message dictionary containing task and parameters
|
30
|
+
|
31
|
+
Returns:
|
32
|
+
Response dictionary with either 'response', 'tool_request', or 'error'
|
33
|
+
"""
|
27
34
|
raise NotImplementedError("Agents must implement handle_message method")
|
28
35
|
|
29
36
|
|
30
37
|
class Orchestrator:
|
31
38
|
"""
|
32
39
|
Central orchestrator for managing agents and routing messages.
|
40
|
+
|
41
|
+
The orchestrator handles agent registration, message routing, tool execution,
|
42
|
+
and conversation history management.
|
33
43
|
"""
|
34
|
-
|
35
|
-
def __init__(self):
|
36
|
-
"""Initialize
|
37
|
-
self.agents: Dict[str,
|
44
|
+
|
45
|
+
def __init__(self, session_id: Optional[str] = None):
|
46
|
+
"""Initialize orchestrator with optional session ID."""
|
47
|
+
self.agents: Dict[str, Agent] = {}
|
48
|
+
self.session_id = session_id or f"session_{int(time.time())}"
|
38
49
|
self.conversation_history: List[Dict[str, Any]] = []
|
39
|
-
|
50
|
+
|
40
51
|
logger.info(f"🎭 Orchestrator initialized with session ID: {self.session_id}")
|
41
|
-
|
42
|
-
def register_agent(self,
|
52
|
+
|
53
|
+
def register_agent(self, name: str, agent: Agent) -> None:
|
43
54
|
"""
|
44
55
|
Register an agent with the orchestrator.
|
45
|
-
|
56
|
+
|
46
57
|
Args:
|
47
|
-
|
48
|
-
agent: Agent instance
|
58
|
+
name: Unique name for the agent
|
59
|
+
agent: Agent instance implementing the Agent interface
|
49
60
|
"""
|
50
|
-
if not
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
61
|
+
if not isinstance(agent, Agent):
|
62
|
+
raise ValueError("Agent must inherit from dacp.Agent base class")
|
63
|
+
|
64
|
+
self.agents[name] = agent
|
65
|
+
logger.info(
|
66
|
+
f"✅ Agent '{name}' registered successfully "
|
67
|
+
f"(type: {type(agent).__name__})"
|
68
|
+
)
|
56
69
|
logger.debug(f"📊 Total registered agents: {len(self.agents)}")
|
57
|
-
|
58
|
-
def unregister_agent(self,
|
70
|
+
|
71
|
+
def unregister_agent(self, name: str) -> bool:
|
59
72
|
"""
|
60
73
|
Unregister an agent from the orchestrator.
|
61
|
-
|
74
|
+
|
62
75
|
Args:
|
63
|
-
|
64
|
-
|
76
|
+
name: Name of the agent to unregister
|
77
|
+
|
65
78
|
Returns:
|
66
|
-
True if agent was
|
79
|
+
True if agent was unregistered, False if not found
|
67
80
|
"""
|
68
|
-
if
|
69
|
-
|
70
|
-
|
71
|
-
logger.
|
72
|
-
logger.debug(f"📊 Remaining registered agents: {len(self.agents)}")
|
81
|
+
if name in self.agents:
|
82
|
+
del self.agents[name]
|
83
|
+
logger.info(f"🗑️ Agent '{name}' unregistered successfully")
|
84
|
+
logger.debug(f"📊 Remaining agents: {len(self.agents)}")
|
73
85
|
return True
|
74
86
|
else:
|
75
|
-
logger.warning(f"⚠️
|
87
|
+
logger.warning(f"⚠️ Agent '{name}' not found for unregistration")
|
76
88
|
return False
|
77
|
-
|
78
|
-
def
|
89
|
+
|
90
|
+
def list_agents(self) -> List[str]:
|
91
|
+
"""Get list of registered agent names."""
|
92
|
+
return list(self.agents.keys())
|
93
|
+
|
94
|
+
def send_message(self, agent_name: str, message: Dict[str, Any]) -> Dict[str, Any]:
|
79
95
|
"""
|
80
96
|
Send a message to a specific agent.
|
81
|
-
|
97
|
+
|
82
98
|
Args:
|
83
|
-
|
84
|
-
message: Message to send
|
85
|
-
|
99
|
+
agent_name: Name of the target agent
|
100
|
+
message: Message dictionary to send
|
101
|
+
|
86
102
|
Returns:
|
87
|
-
Response from the agent
|
103
|
+
Response from the agent after processing
|
88
104
|
"""
|
89
|
-
|
105
|
+
start_time = time.time()
|
106
|
+
|
107
|
+
logger.info(f"📨 Sending message to agent '{agent_name}'")
|
90
108
|
logger.debug(f"📋 Message content: {message}")
|
91
|
-
|
92
|
-
if
|
93
|
-
error_msg = f"Agent '{
|
109
|
+
|
110
|
+
if agent_name not in self.agents:
|
111
|
+
error_msg = f"Agent '{agent_name}' not found"
|
94
112
|
logger.error(f"❌ {error_msg}")
|
95
|
-
logger.debug(f"📊 Available agents: {list(self.agents.keys())}")
|
96
113
|
return {"error": error_msg}
|
97
|
-
|
98
|
-
agent = self.agents[
|
99
|
-
|
114
|
+
|
115
|
+
agent = self.agents[agent_name]
|
116
|
+
|
100
117
|
try:
|
101
|
-
|
102
|
-
|
103
|
-
|
118
|
+
logger.debug(f"🔄 Calling handle_message on agent '{agent_name}'")
|
119
|
+
|
120
|
+
# Call the agent's message handler
|
104
121
|
response = agent.handle_message(message)
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
logger.info(f"🛠️ Executing tool: '{tool_name}' with args: {tool_args}")
|
115
|
-
|
116
|
-
if tool_name in TOOL_REGISTRY:
|
117
|
-
try:
|
118
|
-
tool_start_time = time.time()
|
119
|
-
tool_result = run_tool(tool_name, tool_args)
|
120
|
-
tool_execution_time = time.time() - tool_start_time
|
121
|
-
|
122
|
-
logger.info(f"✅ Tool '{tool_name}' executed successfully in {tool_execution_time:.3f}s")
|
123
|
-
logger.debug(f"🔧 Tool result: {tool_result}")
|
124
|
-
|
125
|
-
wrapped_result = wrap_tool_result(tool_name, tool_result)
|
126
|
-
|
127
|
-
# Log the conversation
|
128
|
-
self._log_conversation(agent_id, message, wrapped_result, tool_used=tool_name)
|
129
|
-
|
130
|
-
return wrapped_result
|
131
|
-
|
132
|
-
except Exception as e:
|
133
|
-
error_msg = f"Tool '{tool_name}' execution failed: {str(e)}"
|
134
|
-
logger.error(f"❌ {error_msg}")
|
135
|
-
error_response = {"error": error_msg}
|
136
|
-
self._log_conversation(agent_id, message, error_response, tool_used=tool_name)
|
137
|
-
return error_response
|
122
|
+
|
123
|
+
# Handle Pydantic models by converting to dict
|
124
|
+
if hasattr(response, 'model_dump'):
|
125
|
+
logger.debug(f"📊 Converting Pydantic model to dict: {type(response).__name__}")
|
126
|
+
response = response.model_dump()
|
127
|
+
elif not isinstance(response, dict):
|
128
|
+
logger.debug(f"📊 Converting response to dict: {type(response)}")
|
129
|
+
if hasattr(response, '__dict__'):
|
130
|
+
response = response.__dict__
|
138
131
|
else:
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
132
|
+
response = {"result": str(response)}
|
133
|
+
|
134
|
+
duration = time.time() - start_time
|
135
|
+
logger.info(f"✅ Agent '{agent_name}' responded in {duration:.3f}s")
|
136
|
+
logger.debug(f"📤 Agent response: {response}")
|
137
|
+
|
138
|
+
# Check if agent requested tool execution
|
139
|
+
if isinstance(response, dict) and "tool_request" in response:
|
140
|
+
logger.info(f"🔧 Agent '{agent_name}' requested tool execution")
|
141
|
+
response = self._handle_tool_request(
|
142
|
+
agent_name, response["tool_request"]
|
143
|
+
)
|
144
|
+
|
145
|
+
# Log the conversation
|
146
|
+
self._log_conversation(agent_name, message, response, duration)
|
147
|
+
|
148
148
|
return response
|
149
|
-
|
149
|
+
|
150
150
|
except Exception as e:
|
151
|
-
|
151
|
+
duration = time.time() - start_time
|
152
|
+
error_msg = f"Error in agent '{agent_name}': {type(e).__name__}: {e}"
|
152
153
|
logger.error(f"❌ {error_msg}")
|
153
|
-
logger.debug(
|
154
|
+
logger.debug("💥 Exception details", exc_info=True)
|
155
|
+
|
154
156
|
error_response = {"error": error_msg}
|
155
|
-
self._log_conversation(
|
157
|
+
self._log_conversation(agent_name, message, error_response, duration)
|
158
|
+
|
156
159
|
return error_response
|
157
|
-
|
158
|
-
def broadcast_message(self, message: Dict[str, Any]
|
160
|
+
|
161
|
+
def broadcast_message(self, message: Dict[str, Any]) -> Dict[str, Any]:
|
159
162
|
"""
|
160
|
-
Send a message to all registered agents
|
161
|
-
|
163
|
+
Send a message to all registered agents.
|
164
|
+
|
162
165
|
Args:
|
163
|
-
message: Message to broadcast
|
164
|
-
|
165
|
-
|
166
|
+
message: Message dictionary to broadcast
|
167
|
+
|
166
168
|
Returns:
|
167
|
-
|
169
|
+
Dictionary mapping agent names to their responses
|
168
170
|
"""
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
logger.info(f"📡 Broadcasting message to {len(target_agents)} agents")
|
173
|
-
logger.debug(f"🎯 Target agents: {target_agents}")
|
174
|
-
if exclude_agents:
|
175
|
-
logger.debug(f"🚫 Excluded agents: {exclude_agents}")
|
176
|
-
|
171
|
+
logger.info(f"📢 Broadcasting message to {len(self.agents)} agents")
|
172
|
+
logger.debug(f"📋 Broadcast message: {message}")
|
173
|
+
|
177
174
|
responses = {}
|
178
175
|
start_time = time.time()
|
179
|
-
|
180
|
-
for
|
181
|
-
logger.debug(f"📨 Broadcasting to agent '{
|
182
|
-
responses[
|
183
|
-
|
184
|
-
|
185
|
-
logger.info(
|
186
|
-
|
176
|
+
|
177
|
+
for agent_name in self.agents:
|
178
|
+
logger.debug(f"📨 Broadcasting to agent '{agent_name}'")
|
179
|
+
responses[agent_name] = self.send_message(agent_name, message)
|
180
|
+
|
181
|
+
duration = time.time() - start_time
|
182
|
+
logger.info(
|
183
|
+
f"✅ Broadcast completed in {duration:.3f}s "
|
184
|
+
f"({len(responses)} responses)"
|
185
|
+
)
|
186
|
+
|
187
187
|
return responses
|
188
|
-
|
189
|
-
def
|
188
|
+
|
189
|
+
def _handle_tool_request(
|
190
|
+
self, agent_name: str, tool_request: Dict[str, Any]
|
191
|
+
) -> Dict[str, Any]:
|
190
192
|
"""
|
191
|
-
|
192
|
-
|
193
|
+
Handle tool execution request from an agent.
|
194
|
+
|
193
195
|
Args:
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
List of conversation entries
|
198
|
-
"""
|
199
|
-
if agent_id is None:
|
200
|
-
logger.debug(f"📚 Retrieving full conversation history ({len(self.conversation_history)} entries)")
|
201
|
-
return self.conversation_history.copy()
|
202
|
-
else:
|
203
|
-
filtered_history = [
|
204
|
-
entry for entry in self.conversation_history
|
205
|
-
if entry.get("agent_id") == agent_id
|
206
|
-
]
|
207
|
-
logger.debug(f"📚 Retrieving conversation history for '{agent_id}' ({len(filtered_history)} entries)")
|
208
|
-
return filtered_history
|
209
|
-
|
210
|
-
def clear_history(self) -> None:
|
211
|
-
"""Clear the conversation history."""
|
212
|
-
old_count = len(self.conversation_history)
|
213
|
-
self.conversation_history.clear()
|
214
|
-
logger.info(f"🗑️ Conversation history cleared ({old_count} entries removed)")
|
215
|
-
|
216
|
-
def get_session_info(self) -> Dict[str, Any]:
|
217
|
-
"""
|
218
|
-
Get current session information.
|
219
|
-
|
196
|
+
agent_name: Name of the requesting agent
|
197
|
+
tool_request: Tool request dictionary with 'name' and 'args'
|
198
|
+
|
220
199
|
Returns:
|
221
|
-
|
200
|
+
Tool execution result
|
222
201
|
"""
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
"
|
228
|
-
|
229
|
-
logger.
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
202
|
+
tool_name = tool_request.get("name")
|
203
|
+
tool_args = tool_request.get("args", {})
|
204
|
+
|
205
|
+
if not tool_name:
|
206
|
+
return {"error": "Tool name is required"}
|
207
|
+
|
208
|
+
logger.info(f"🛠️ Executing tool: '{tool_name}' with args: {tool_args}")
|
209
|
+
|
210
|
+
start_time = time.time()
|
211
|
+
|
212
|
+
try:
|
213
|
+
result = execute_tool(tool_name, tool_args)
|
214
|
+
duration = time.time() - start_time
|
215
|
+
|
216
|
+
logger.info(
|
217
|
+
f"✅ Tool '{tool_name}' executed successfully in {duration:.3f}s"
|
218
|
+
)
|
219
|
+
logger.debug(f"🔧 Tool result: {result}")
|
220
|
+
|
221
|
+
return {"tool_result": {"name": tool_name, "result": result}}
|
222
|
+
|
223
|
+
except Exception as e:
|
224
|
+
duration = time.time() - start_time
|
225
|
+
error_msg = f"Tool '{tool_name}' failed: {type(e).__name__}: {e}"
|
226
|
+
logger.error(f"❌ {error_msg}")
|
227
|
+
|
228
|
+
return {"error": error_msg}
|
229
|
+
|
230
|
+
def _log_conversation(
|
231
|
+
self,
|
232
|
+
agent_name: str,
|
233
|
+
message: Dict[str, Any],
|
234
|
+
response: Dict[str, Any],
|
235
|
+
duration: float,
|
236
|
+
) -> None:
|
237
|
+
"""Log conversation entry to history."""
|
238
|
+
logger.debug("💾 Logging conversation entry")
|
239
|
+
|
234
240
|
entry = {
|
235
241
|
"timestamp": time.time(),
|
236
|
-
"
|
242
|
+
"session_id": self.session_id,
|
243
|
+
"agent": agent_name,
|
244
|
+
"agent_name": agent_name,
|
237
245
|
"message": message,
|
238
246
|
"response": response,
|
239
|
-
"
|
247
|
+
"duration": duration,
|
240
248
|
}
|
241
|
-
|
242
|
-
if tool_used:
|
243
|
-
entry["tool_used"] = tool_used
|
244
|
-
logger.debug(f"💾 Logging conversation with tool usage: {tool_used}")
|
245
|
-
else:
|
246
|
-
logger.debug(f"💾 Logging conversation entry")
|
247
|
-
|
249
|
+
|
248
250
|
self.conversation_history.append(entry)
|
251
|
+
|
252
|
+
# Keep history manageable (last 1000 entries)
|
253
|
+
if len(self.conversation_history) > 1000:
|
254
|
+
self.conversation_history = self.conversation_history[-1000:]
|
255
|
+
logger.debug("🗂️ Conversation history trimmed to 1000 entries")
|
256
|
+
|
257
|
+
def get_conversation_history(
|
258
|
+
self, limit: Optional[int] = None
|
259
|
+
) -> List[Dict[str, Any]]:
|
260
|
+
"""
|
261
|
+
Get conversation history.
|
262
|
+
|
263
|
+
Args:
|
264
|
+
limit: Maximum number of entries to return (None for all)
|
265
|
+
|
266
|
+
Returns:
|
267
|
+
List of conversation entries
|
268
|
+
"""
|
269
|
+
if limit is None:
|
270
|
+
return self.conversation_history.copy()
|
271
|
+
else:
|
272
|
+
return self.conversation_history[-limit:].copy()
|
273
|
+
|
274
|
+
def clear_conversation_history(self) -> None:
|
275
|
+
"""Clear conversation history."""
|
276
|
+
self.conversation_history.clear()
|
277
|
+
logger.info("🗑️ Conversation history cleared")
|
278
|
+
|
279
|
+
def get_session_metadata(self) -> Dict[str, Any]:
|
280
|
+
"""Get session metadata and statistics."""
|
281
|
+
return {
|
282
|
+
"session_id": self.session_id,
|
283
|
+
"registered_agents": len(self.agents),
|
284
|
+
"agent_names": list(self.agents.keys()),
|
285
|
+
"conversation_entries": len(self.conversation_history),
|
286
|
+
"start_time": (
|
287
|
+
self.conversation_history[0]["timestamp"]
|
288
|
+
if self.conversation_history
|
289
|
+
else None
|
290
|
+
),
|
291
|
+
"last_activity": (
|
292
|
+
self.conversation_history[-1]["timestamp"]
|
293
|
+
if self.conversation_history
|
294
|
+
else None
|
295
|
+
),
|
296
|
+
}
|
dacp/tools.py
CHANGED
@@ -1,86 +1,105 @@
|
|
1
|
+
"""
|
2
|
+
DACP Tools - Built-in tool implementations.
|
3
|
+
|
4
|
+
This module provides the core tool functionality including tool registry,
|
5
|
+
execution, and built-in tools like file_writer.
|
6
|
+
"""
|
7
|
+
|
1
8
|
import logging
|
2
|
-
from typing import Dict, Any, Callable
|
3
9
|
from pathlib import Path
|
10
|
+
from typing import Dict, Any, Callable
|
4
11
|
|
5
|
-
# Set up logger for this module
|
6
12
|
logger = logging.getLogger("dacp.tools")
|
7
13
|
|
8
|
-
|
14
|
+
# Global tool registry
|
15
|
+
TOOL_REGISTRY: Dict[str, Callable[[Dict[str, Any]], Dict[str, Any]]] = {}
|
9
16
|
|
10
17
|
|
11
|
-
def register_tool(
|
12
|
-
"""
|
13
|
-
|
14
|
-
|
15
|
-
|
18
|
+
def register_tool(name: str, func: Callable[[Dict[str, Any]], Dict[str, Any]]) -> None:
|
19
|
+
"""
|
20
|
+
Register a tool function.
|
21
|
+
|
22
|
+
Args:
|
23
|
+
name: Name of the tool
|
24
|
+
func: Function that takes args dict and returns result dict
|
25
|
+
"""
|
26
|
+
TOOL_REGISTRY[name] = func
|
27
|
+
logger.info(f"🔧 Tool '{name}' registered")
|
16
28
|
|
17
29
|
|
18
|
-
def
|
19
|
-
"""
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
30
|
+
def execute_tool(name: str, args: Dict[str, Any]) -> Dict[str, Any]:
|
31
|
+
"""
|
32
|
+
Execute a tool by name with given arguments.
|
33
|
+
|
34
|
+
Args:
|
35
|
+
name: Name of the tool to execute
|
36
|
+
args: Arguments to pass to the tool
|
37
|
+
|
38
|
+
Returns:
|
39
|
+
Tool execution result
|
40
|
+
|
41
|
+
Raises:
|
42
|
+
ValueError: If tool is not found
|
43
|
+
"""
|
44
|
+
if name not in TOOL_REGISTRY:
|
45
|
+
available_tools = list(TOOL_REGISTRY.keys())
|
46
|
+
logger.error(
|
47
|
+
f"❌ Tool '{name}' not found. " f"Available tools: {available_tools}"
|
48
|
+
)
|
49
|
+
raise ValueError(f"Tool '{name}' not found. Available tools: {available_tools}")
|
50
|
+
|
51
|
+
logger.debug(f"🛠️ Executing tool '{name}' with args: {args}")
|
24
52
|
|
25
|
-
tool_func = TOOL_REGISTRY[tool_id]
|
26
|
-
logger.debug(f"🛠️ Executing tool '{tool_id}' with args: {args}")
|
27
|
-
|
28
|
-
import time
|
29
|
-
start_time = time.time()
|
30
|
-
|
31
53
|
try:
|
32
|
-
result =
|
33
|
-
|
34
|
-
logger.info(f"✅ Tool '{tool_id}' executed successfully in {execution_time:.3f}s")
|
35
|
-
logger.debug(f"🔧 Tool result: {result}")
|
54
|
+
result = TOOL_REGISTRY[name](args)
|
55
|
+
logger.debug(f"✅ Tool '{name}' completed successfully")
|
36
56
|
return result
|
37
57
|
except Exception as e:
|
38
|
-
|
39
|
-
logger.error(f"❌ Tool '{tool_id}' failed after {execution_time:.3f}s: {type(e).__name__}: {e}")
|
58
|
+
logger.error(f"❌ Tool '{name}' failed: {type(e).__name__}: {e}")
|
40
59
|
raise
|
41
60
|
|
42
61
|
|
43
|
-
def file_writer(
|
62
|
+
def file_writer(args: Dict[str, Any]) -> Dict[str, Any]:
|
44
63
|
"""
|
45
|
-
Write content to a file, creating
|
64
|
+
Write content to a file, creating directories as needed.
|
46
65
|
|
47
66
|
Args:
|
48
|
-
|
49
|
-
content: Content to write to the file
|
67
|
+
args: Dictionary containing 'path' and 'content'
|
50
68
|
|
51
69
|
Returns:
|
52
|
-
|
70
|
+
Success status and file information
|
53
71
|
"""
|
54
|
-
|
55
|
-
|
72
|
+
path = args.get("path")
|
73
|
+
content = args.get("content", "")
|
74
|
+
|
75
|
+
if not path:
|
76
|
+
raise ValueError("file_writer requires 'path' argument")
|
77
|
+
|
56
78
|
try:
|
57
79
|
# Create parent directories if they don't exist
|
58
|
-
|
59
|
-
|
60
|
-
logger.debug(f"📁 Creating parent directories: {parent_dir}")
|
61
|
-
parent_dir.mkdir(parents=True, exist_ok=True)
|
80
|
+
file_path = Path(path)
|
81
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
62
82
|
|
63
|
-
# Write
|
64
|
-
with open(
|
83
|
+
# Write content to file
|
84
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
65
85
|
f.write(content)
|
66
86
|
|
67
87
|
logger.info(f"✅ File written successfully: {path}")
|
88
|
+
|
68
89
|
return {
|
69
90
|
"success": True,
|
70
|
-
"path":
|
91
|
+
"path": str(file_path),
|
71
92
|
"message": f"Successfully wrote {len(content)} characters to {path}",
|
72
93
|
}
|
94
|
+
|
73
95
|
except Exception as e:
|
74
|
-
logger.error(f"❌ Failed to write file {path}: {
|
96
|
+
logger.error(f"❌ Failed to write file {path}: {e}")
|
75
97
|
return {
|
76
98
|
"success": False,
|
77
99
|
"path": path,
|
78
100
|
"error": str(e),
|
79
|
-
"message": f"Failed to write to {path}: {e}",
|
80
101
|
}
|
81
102
|
|
82
103
|
|
83
|
-
# Register
|
84
|
-
logger.debug("🏗️ Registering built-in tools...")
|
104
|
+
# Register built-in tools
|
85
105
|
register_tool("file_writer", file_writer)
|
86
|
-
logger.debug("✅ Built-in tools registration complete")
|