Skip to content

Commit 9d59dbc

Browse files
committed
feat(autoagents): implement stateful tools with GraphToolExecutor integration
Replaces stubbed tool implementations with real executor calls. Changes: - graph_tools.rs: Add executor field to all 6 tools, implement actual execution - GetTransitiveDependencies, GetReverseDependencies, TraceCallChain - DetectCycles, CalculateCoupling, GetHubNodes - All tools now call self.executor.execute_sync() with proper args - codegraph_agent.rs: Remove #[agent] macro and CodeGraphAgent struct - Keep only CodeGraphAgentOutput with #[AgentOutput] derive - Tools can't be auto-instantiated, require manual construction - agent_builder.rs: Manual tool construction in build() method - Create Vec<Box<dyn ToolT>> with all 6 tools - Pass Arc<GraphToolExecutorAdapter> to each tool constructor - Use ReActAgent::with_tools(tools) instead of macro - mod.rs: Remove CodeGraphAgent from re-exports This completes Task 16's stubbed implementations, making all AutoAgents tools functional with real SurrealDB graph queries.
1 parent e085dd7 commit 9d59dbc

File tree

4 files changed

+145
-53
lines changed

4 files changed

+145
-53
lines changed

crates/codegraph-mcp/src/autoagents/agent_builder.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,16 +174,18 @@ impl ChatResponse for CodeGraphChatResponse {
174174
// Agent Builder
175175
// ============================================================================
176176

177-
use crate::autoagents::codegraph_agent::CodeGraphAgent;
177+
use crate::autoagents::codegraph_agent::CodeGraphAgentOutput;
178178
use crate::autoagents::tier_plugin::TierAwarePromptPlugin;
179179
use crate::autoagents::tools::tool_executor_adapter::GraphToolFactory;
180+
use crate::autoagents::tools::graph_tools::*;
180181
use crate::{AnalysisType, GraphToolExecutor};
181182
use crate::context_aware_limits::ContextTier;
182183

183184
use autoagents::core::agent::AgentBuilder;
184185
use autoagents::core::agent::prebuilt::executor::ReActAgent;
185186
use autoagents::core::agent::memory::SlidingWindowMemory;
186187
use autoagents::core::error::Error as AutoAgentsError;
188+
use autoagents::core::tool::ToolT;
187189

188190
/// Builder for CodeGraph AutoAgents workflows
189191
pub struct CodeGraphAgentBuilder {
@@ -219,8 +221,21 @@ impl CodeGraphAgentBuilder {
219221
let memory_size = tier_plugin.get_max_iterations() * 2;
220222
let memory = Box::new(SlidingWindowMemory::new(memory_size));
221223

222-
// Build ReAct agent with our tools
223-
let react_agent = ReActAgent::new(CodeGraphAgent::default());
224+
// Get executor adapter for tool construction
225+
let executor_adapter = self.tool_factory.adapter();
226+
227+
// Manually construct all 6 tools with the executor
228+
let tools: Vec<Box<dyn ToolT>> = vec![
229+
Box::new(GetTransitiveDependencies::new(executor_adapter.clone())),
230+
Box::new(GetReverseDependencies::new(executor_adapter.clone())),
231+
Box::new(TraceCallChain::new(executor_adapter.clone())),
232+
Box::new(DetectCycles::new(executor_adapter.clone())),
233+
Box::new(CalculateCoupling::new(executor_adapter.clone())),
234+
Box::new(GetHubNodes::new(executor_adapter.clone())),
235+
];
236+
237+
// Build ReAct agent with manually constructed tools
238+
let react_agent = ReActAgent::with_tools(tools);
224239

225240
// Build full agent with configuration
226241
let agent = AgentBuilder::new(react_agent)

crates/codegraph-mcp/src/autoagents/codegraph_agent.rs

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,23 @@
11
// ABOUTME: CodeGraph agent definition for AutoAgents ReAct workflow
2-
// ABOUTME: Defines tools, output format, and behavior for graph analysis
2+
// ABOUTME: Defines output format and behavior for graph analysis (tools registered manually)
33

44
use autoagents::core::agent::prebuilt::executor::ReActAgentOutput;
5-
use autoagents_derive::{agent, AgentHooks, AgentOutput};
5+
use autoagents_derive::AgentOutput;
66
use serde::{Deserialize, Serialize};
77

8-
use crate::autoagents::tools::graph_tools::*;
9-
108
/// CodeGraph agent output format
119
#[derive(Debug, Serialize, Deserialize, AgentOutput)]
1210
pub struct CodeGraphAgentOutput {
1311
#[output(description = "Final answer to the query")]
14-
answer: String,
12+
pub answer: String,
1513

1614
#[output(description = "Key findings from graph analysis")]
17-
findings: String,
15+
pub findings: String,
1816

1917
#[output(description = "Number of analysis steps performed")]
20-
steps_taken: String,
18+
pub steps_taken: String,
2119
}
2220

23-
/// CodeGraph agent for code analysis via graph traversal
24-
#[agent(
25-
name = "codegraph_agent",
26-
description = "You are a code analysis agent with access to graph database tools. \
27-
Analyze code dependencies, call chains, and architectural patterns.",
28-
tools = [
29-
GetTransitiveDependencies,
30-
GetReverseDependencies,
31-
TraceCallChain,
32-
DetectCycles,
33-
CalculateCoupling,
34-
GetHubNodes
35-
],
36-
output = CodeGraphAgentOutput,
37-
)]
38-
#[derive(Default, Clone, AgentHooks)]
39-
pub struct CodeGraphAgent {}
40-
4121
impl From<ReActAgentOutput> for CodeGraphAgentOutput {
4222
fn from(output: ReActAgentOutput) -> Self {
4323
let resp = output.response;

crates/codegraph-mcp/src/autoagents/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ pub use tier_plugin::TierAwarePromptPlugin;
2020
#[cfg(feature = "autoagents-experimental")]
2121
pub use agent_builder::{CodeGraphChatAdapter, CodeGraphAgentBuilder, AgentHandle};
2222
#[cfg(feature = "autoagents-experimental")]
23-
pub use codegraph_agent::{CodeGraphAgent, CodeGraphAgentOutput};
23+
pub use codegraph_agent::CodeGraphAgentOutput;
2424
#[cfg(feature = "autoagents-experimental")]
2525
pub use executor::{CodeGraphExecutor, CodeGraphExecutorBuilder, ExecutorError};

crates/codegraph-mcp/src/autoagents/tools/graph_tools.rs

Lines changed: 121 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// ABOUTME: AutoAgents tool definitions for SurrealDB graph analysis
2-
// ABOUTME: Type-safe wrappers using AutoAgents derive macros
2+
// ABOUTME: Type-safe wrappers using AutoAgents derive macros with stateful executor access
33

44
use autoagents::core::tool::{ToolCallError, ToolInputT, ToolRuntime, ToolT};
55
use autoagents_derive::{tool, ToolInput};
@@ -36,22 +36,32 @@ fn default_depth() -> i32 {
3636
Follows dependency edges recursively to find all nodes this node depends on.",
3737
input = GetTransitiveDependenciesArgs,
3838
)]
39-
pub struct GetTransitiveDependencies {}
39+
pub struct GetTransitiveDependencies {
40+
executor: Arc<GraphToolExecutorAdapter>,
41+
}
42+
43+
impl GetTransitiveDependencies {
44+
pub fn new(executor: Arc<GraphToolExecutorAdapter>) -> Self {
45+
Self { executor }
46+
}
47+
}
4048

4149
#[async_trait::async_trait]
4250
impl ToolRuntime for GetTransitiveDependencies {
4351
async fn execute(&self, args: serde_json::Value) -> Result<serde_json::Value, ToolCallError> {
4452
let typed_args: GetTransitiveDependenciesArgs = serde_json::from_value(args)?;
4553

46-
// TODO: Need executor instance - for now return placeholder
47-
Ok(serde_json::json!({
48-
"status": "not_implemented",
49-
"params": {
54+
// Call the actual executor
55+
let result = self.executor.execute_sync(
56+
"get_transitive_dependencies",
57+
serde_json::json!({
5058
"node_id": typed_args.node_id,
5159
"edge_type": typed_args.edge_type,
52-
"depth": typed_args.depth,
53-
}
54-
}))
60+
"depth": typed_args.depth
61+
})
62+
).map_err(|e| ToolCallError::ExecutionFailed(e))?;
63+
64+
Ok(result)
5565
}
5666
}
5767

@@ -74,13 +84,31 @@ pub struct GetReverseDependenciesArgs {
7484
description = "Get all nodes that depend on the specified node. Useful for impact analysis.",
7585
input = GetReverseDependenciesArgs,
7686
)]
77-
pub struct GetReverseDependencies {}
87+
pub struct GetReverseDependencies {
88+
executor: Arc<GraphToolExecutorAdapter>,
89+
}
90+
91+
impl GetReverseDependencies {
92+
pub fn new(executor: Arc<GraphToolExecutorAdapter>) -> Self {
93+
Self { executor }
94+
}
95+
}
7896

7997
#[async_trait::async_trait]
8098
impl ToolRuntime for GetReverseDependencies {
8199
async fn execute(&self, args: serde_json::Value) -> Result<serde_json::Value, ToolCallError> {
82-
let _typed_args: GetReverseDependenciesArgs = serde_json::from_value(args)?;
83-
Ok(serde_json::json!({"status": "not_implemented"}))
100+
let typed_args: GetReverseDependenciesArgs = serde_json::from_value(args)?;
101+
102+
let result = self.executor.execute_sync(
103+
"get_reverse_dependencies",
104+
serde_json::json!({
105+
"node_id": typed_args.node_id,
106+
"edge_type": typed_args.edge_type,
107+
"depth": typed_args.depth
108+
})
109+
).map_err(|e| ToolCallError::ExecutionFailed(e))?;
110+
111+
Ok(result)
84112
}
85113
}
86114

@@ -104,13 +132,30 @@ fn default_call_chain_depth() -> i32 {
104132
description = "Trace the execution flow from a starting function through all called functions.",
105133
input = TraceCallChainArgs,
106134
)]
107-
pub struct TraceCallChain {}
135+
pub struct TraceCallChain {
136+
executor: Arc<GraphToolExecutorAdapter>,
137+
}
138+
139+
impl TraceCallChain {
140+
pub fn new(executor: Arc<GraphToolExecutorAdapter>) -> Self {
141+
Self { executor }
142+
}
143+
}
108144

109145
#[async_trait::async_trait]
110146
impl ToolRuntime for TraceCallChain {
111147
async fn execute(&self, args: serde_json::Value) -> Result<serde_json::Value, ToolCallError> {
112-
let _typed_args: TraceCallChainArgs = serde_json::from_value(args)?;
113-
Ok(serde_json::json!({"status": "not_implemented"}))
148+
let typed_args: TraceCallChainArgs = serde_json::from_value(args)?;
149+
150+
let result = self.executor.execute_sync(
151+
"trace_call_chain",
152+
serde_json::json!({
153+
"start_node_id": typed_args.start_node_id,
154+
"max_depth": typed_args.max_depth
155+
})
156+
).map_err(|e| ToolCallError::ExecutionFailed(e))?;
157+
158+
Ok(result)
114159
}
115160
}
116161

@@ -135,13 +180,30 @@ fn default_max_cycle_length() -> i32 {
135180
description = "Detect circular dependencies and cycles in the codebase graph.",
136181
input = DetectCyclesArgs,
137182
)]
138-
pub struct DetectCycles {}
183+
pub struct DetectCycles {
184+
executor: Arc<GraphToolExecutorAdapter>,
185+
}
186+
187+
impl DetectCycles {
188+
pub fn new(executor: Arc<GraphToolExecutorAdapter>) -> Self {
189+
Self { executor }
190+
}
191+
}
139192

140193
#[async_trait::async_trait]
141194
impl ToolRuntime for DetectCycles {
142195
async fn execute(&self, args: serde_json::Value) -> Result<serde_json::Value, ToolCallError> {
143-
let _typed_args: DetectCyclesArgs = serde_json::from_value(args)?;
144-
Ok(serde_json::json!({"status": "not_implemented"}))
196+
let typed_args: DetectCyclesArgs = serde_json::from_value(args)?;
197+
198+
let result = self.executor.execute_sync(
199+
"detect_cycles",
200+
serde_json::json!({
201+
"edge_type": typed_args.edge_type,
202+
"max_cycle_length": typed_args.max_cycle_length
203+
})
204+
).map_err(|e| ToolCallError::ExecutionFailed(e))?;
205+
206+
Ok(result)
145207
}
146208
}
147209

@@ -161,13 +223,30 @@ pub struct CalculateCouplingArgs {
161223
description = "Calculate afferent/efferent coupling and instability metrics for a node.",
162224
input = CalculateCouplingArgs,
163225
)]
164-
pub struct CalculateCoupling {}
226+
pub struct CalculateCoupling {
227+
executor: Arc<GraphToolExecutorAdapter>,
228+
}
229+
230+
impl CalculateCoupling {
231+
pub fn new(executor: Arc<GraphToolExecutorAdapter>) -> Self {
232+
Self { executor }
233+
}
234+
}
165235

166236
#[async_trait::async_trait]
167237
impl ToolRuntime for CalculateCoupling {
168238
async fn execute(&self, args: serde_json::Value) -> Result<serde_json::Value, ToolCallError> {
169-
let _typed_args: CalculateCouplingArgs = serde_json::from_value(args)?;
170-
Ok(serde_json::json!({"status": "not_implemented"}))
239+
let typed_args: CalculateCouplingArgs = serde_json::from_value(args)?;
240+
241+
let result = self.executor.execute_sync(
242+
"calculate_coupling",
243+
serde_json::json!({
244+
"node_id": typed_args.node_id,
245+
"edge_type": typed_args.edge_type
246+
})
247+
).map_err(|e| ToolCallError::ExecutionFailed(e))?;
248+
249+
Ok(result)
171250
}
172251
}
173252

@@ -199,13 +278,31 @@ fn default_limit() -> i32 {
199278
description = "Find highly connected hub nodes in the dependency graph.",
200279
input = GetHubNodesArgs,
201280
)]
202-
pub struct GetHubNodes {}
281+
pub struct GetHubNodes {
282+
executor: Arc<GraphToolExecutorAdapter>,
283+
}
284+
285+
impl GetHubNodes {
286+
pub fn new(executor: Arc<GraphToolExecutorAdapter>) -> Self {
287+
Self { executor }
288+
}
289+
}
203290

204291
#[async_trait::async_trait]
205292
impl ToolRuntime for GetHubNodes {
206293
async fn execute(&self, args: serde_json::Value) -> Result<serde_json::Value, ToolCallError> {
207-
let _typed_args: GetHubNodesArgs = serde_json::from_value(args)?;
208-
Ok(serde_json::json!({"status": "not_implemented"}))
294+
let typed_args: GetHubNodesArgs = serde_json::from_value(args)?;
295+
296+
let result = self.executor.execute_sync(
297+
"get_hub_nodes",
298+
serde_json::json!({
299+
"edge_type": typed_args.edge_type,
300+
"min_connections": typed_args.min_connections,
301+
"limit": typed_args.limit
302+
})
303+
).map_err(|e| ToolCallError::ExecutionFailed(e))?;
304+
305+
Ok(result)
209306
}
210307
}
211308

0 commit comments

Comments
 (0)