feat: add zeroclaw-robot-kit crate for AI-powered robotics
Standalone robot toolkit providing AI agents with physical world interaction. Features: - 6 tools: drive, look, listen, speak, sense, emote - Multiple backends: ROS2, serial, GPIO, mock - Independent SafetyMonitor with E-stop, collision avoidance - Designed for Raspberry Pi 5 + Ollama offline operation - 55 unit/integration tests - Complete Pi 5 hardware setup guide
This commit is contained in:
parent
431287184b
commit
0dfc707c49
18 changed files with 4444 additions and 9 deletions
123
crates/robot-kit/src/traits.rs
Normal file
123
crates/robot-kit/src/traits.rs
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
//! Tool trait definition
|
||||
//!
|
||||
//! This defines the interface that all robot tools implement.
|
||||
//! It is compatible with ZeroClaw's Tool trait but standalone.
|
||||
|
||||
use async_trait::async_trait;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
/// Result of a tool execution
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ToolResult {
|
||||
/// Whether the tool executed successfully
|
||||
pub success: bool,
|
||||
/// Output from the tool (human-readable)
|
||||
pub output: String,
|
||||
/// Error message if failed
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
impl ToolResult {
|
||||
/// Create a successful result
|
||||
pub fn success(output: impl Into<String>) -> Self {
|
||||
Self {
|
||||
success: true,
|
||||
output: output.into(),
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a failed result
|
||||
pub fn error(error: impl Into<String>) -> Self {
|
||||
Self {
|
||||
success: false,
|
||||
output: String::new(),
|
||||
error: Some(error.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a failed result with partial output
|
||||
pub fn partial(output: impl Into<String>, error: impl Into<String>) -> Self {
|
||||
Self {
|
||||
success: false,
|
||||
output: output.into(),
|
||||
error: Some(error.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Description of a tool for LLM function calling
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ToolSpec {
|
||||
/// Tool name (used in function calls)
|
||||
pub name: String,
|
||||
/// Human-readable description
|
||||
pub description: String,
|
||||
/// JSON Schema for parameters
|
||||
pub parameters: Value,
|
||||
}
|
||||
|
||||
/// Core tool trait
|
||||
///
|
||||
/// Implement this trait to create a new tool that can be used
|
||||
/// by an AI agent to interact with the robot hardware.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// use zeroclaw_robot_kit::{Tool, ToolResult};
|
||||
/// use async_trait::async_trait;
|
||||
/// use serde_json::{json, Value};
|
||||
///
|
||||
/// pub struct BeepTool;
|
||||
///
|
||||
/// #[async_trait]
|
||||
/// impl Tool for BeepTool {
|
||||
/// fn name(&self) -> &str { "beep" }
|
||||
///
|
||||
/// fn description(&self) -> &str { "Make a beep sound" }
|
||||
///
|
||||
/// fn parameters_schema(&self) -> Value {
|
||||
/// json!({
|
||||
/// "type": "object",
|
||||
/// "properties": {
|
||||
/// "frequency": { "type": "number", "description": "Hz" }
|
||||
/// }
|
||||
/// })
|
||||
/// }
|
||||
///
|
||||
/// async fn execute(&self, args: Value) -> anyhow::Result<ToolResult> {
|
||||
/// let freq = args["frequency"].as_f64().unwrap_or(440.0);
|
||||
/// // Play beep...
|
||||
/// Ok(ToolResult::success(format!("Beeped at {}Hz", freq)))
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[async_trait]
|
||||
pub trait Tool: Send + Sync {
|
||||
/// Tool name (used in LLM function calling)
|
||||
fn name(&self) -> &str;
|
||||
|
||||
/// Human-readable description of what this tool does
|
||||
fn description(&self) -> &str;
|
||||
|
||||
/// JSON Schema describing the tool's parameters
|
||||
///
|
||||
/// This is used by the LLM to understand how to call the tool.
|
||||
fn parameters_schema(&self) -> Value;
|
||||
|
||||
/// Execute the tool with the given arguments
|
||||
///
|
||||
/// Arguments are passed as JSON matching the parameters_schema.
|
||||
async fn execute(&self, args: Value) -> anyhow::Result<ToolResult>;
|
||||
|
||||
/// Get the full specification for LLM registration
|
||||
fn spec(&self) -> ToolSpec {
|
||||
ToolSpec {
|
||||
name: self.name().to_string(),
|
||||
description: self.description().to_string(),
|
||||
parameters: self.parameters_schema(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue