Initial scaffold: GyanDoot v2.0 in Rust

Full Cargo workspace with 11 crates:
- gyandoot-core: Shared types, config, error types
- gyandoot-agent: Agent loop, tool execution, context compression
- gyandoot-providers: LLM provider abstraction (OpenAI, Anthropic, Ollama)
- gyandoot-memory: MemPalace persistent memory system
- gyandoot-platform: Platform adapters (Telegram, Discord, Webhook, CLI)
- gyandoot-security: Credential vault, sanitizer, threat detector
- gyandoot-kanban: Task decomposition, swarm orchestration, diagnostics
- gyandoot-subagents: Sub-agent spawning and orchestration
- gyandoot-skills: Skills discovery, loading, preprocessing, bundles
- gyandoot-profile: Multi-agent profile manager
- gyandoot-gateway: Gateway sessions, routing, access control

Plus: CLI entry point with clap, cross-platform installer script,
KANBAN.md, .gnpl/AGENTS.md

Workspace compiles clean: cargo check passes with 0 errors.
This commit is contained in:
2026-06-11 20:04:12 +05:30
commit 851c838ca5
53 changed files with 8037 additions and 0 deletions

View File

@@ -0,0 +1,146 @@
//! Tool execution subsystem.
//!
//! Discovers available tools, executes tool calls, and returns results.
use std::collections::HashMap;
use gyandoot_core::{ToolCall, ToolDefinition, ToolResult};
use tracing::{debug, error, warn};
/// Executes tool calls made by the LLM.
pub struct ToolExecutor {
tools: HashMap<String, Box<dyn Tool + Send + Sync>>,
}
/// Trait that all tools must implement.
#[async_trait::async_trait]
pub trait Tool: Send + Sync {
fn name(&self) -> &str;
fn description(&self) -> &str;
fn parameters(&self) -> serde_json::Value;
async fn execute(&self, args: serde_json::Value) -> anyhow::Result<String>;
}
impl ToolExecutor {
/// Create a new executor with no tools loaded.
pub fn new() -> Self {
Self {
tools: HashMap::new(),
}
}
/// Register a tool implementation.
pub fn register(&mut self, tool: Box<dyn Tool + Send + Sync>) {
let name = tool.name().to_string();
debug!("Registering tool: {}", name);
self.tools.insert(name, tool);
}
/// Return all registered tool definitions (for LLM function calling).
pub fn definitions(&self) -> Vec<ToolDefinition> {
self.tools
.values()
.map(|t| ToolDefinition {
name: t.name().to_string(),
description: t.description().to_string(),
parameters: t.parameters(),
})
.collect()
}
/// Execute a single tool call and return the result.
pub async fn execute(&self, call: &ToolCall) -> anyhow::Result<ToolResult> {
let span = tracing::span!(tracing::Level::INFO, "tool_exec", tool = %call.name);
let _guard = span.enter();
match self.tools.get(&call.name) {
Some(tool) => {
debug!("Executing tool '{}' with args: {}", call.name, call.arguments);
match tool.execute(call.arguments.clone()).await {
Ok(output) => {
debug!("Tool '{}' succeeded", call.name);
Ok(ToolResult {
tool_call_id: call.id.clone(),
success: true,
output,
error: None,
})
}
Err(e) => {
error!("Tool '{}' failed: {}", call.name, e);
Ok(ToolResult {
tool_call_id: call.id.clone(),
success: false,
output: String::new(),
error: Some(e.to_string()),
})
}
}
}
None => {
warn!("Tool '{}' not found", call.name);
Ok(ToolResult {
tool_call_id: call.id.clone(),
success: false,
output: String::new(),
error: Some(format!("Tool '{}' is not available", call.name)),
})
}
}
}
}
impl Default for ToolExecutor {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
struct EchoTool;
#[async_trait::async_trait]
impl Tool for EchoTool {
fn name(&self) -> &str {
"echo"
}
fn description(&self) -> &str {
"Echo back the input"
}
fn parameters(&self) -> serde_json::Value {
serde_json::json!({
"type": "object",
"properties": {
"message": {"type": "string"}
},
"required": ["message"]
})
}
async fn execute(&self, args: serde_json::Value) -> anyhow::Result<String> {
Ok(args["message"].as_str().unwrap_or("").to_string())
}
}
#[tokio::test]
async fn test_tool_registration_and_execution() {
let mut executor = ToolExecutor::new();
executor.register(Box::new(EchoTool));
let defs = executor.definitions();
assert_eq!(defs.len(), 1);
assert_eq!(defs[0].name, "echo");
let call = ToolCall {
id: "1".to_string(),
name: "echo".to_string(),
arguments: serde_json::json!({"message": "hello"}),
};
let result = executor.execute(&call).await.unwrap();
assert!(result.success);
assert_eq!(result.output, "hello");
}
}