Luigit
repositories / smith

smith

There are many coding harnesses - but this one is fast

owned by admin

smith/src/tool.rs

Raw
//! Provider-neutral tool contracts and cancellation (`SMH-SPEC-SPEC0001`).

use crate::id::ToolCallId;
use crate::structured::{ToolArguments, ToolSchema};
use serde::{Deserialize, Serialize};

/// Tool specification (provider-neutral definition).
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
pub struct ToolMetadata {
    /// Tool name.
    pub name: String,
    /// Human-readable description.
    pub description: String,
    /// Input schema for validation.
    pub input_schema: ToolSchema,
}

impl ToolMetadata {
    /// Metadata with an empty object schema.
    #[must_use]
    pub fn new<N: Into<String>, D: Into<String>>(name: N, description: D) -> Self {
        Self {
            name: name.into(),
            description: description.into(),
            input_schema: ToolSchema::empty_object(),
        }
    }

    /// Replace the input schema.
    /// Argument data cannot stand in for a schema.
    ///
    /// ```compile_fail
    /// use smith::{ToolArguments, ToolMetadata};
    /// ToolMetadata::new("read", "Read a file").with_schema(ToolArguments::empty_object());
    /// ```
    #[must_use]
    pub fn with_schema(mut self, schema: ToolSchema) -> Self {
        self.input_schema = schema;
        self
    }
}

/// Tool call issued by the agent.
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
pub struct ToolCall {
    /// Call identifier.
    pub call_id: ToolCallId,
    /// Tool name.
    pub name: String,
    /// Arguments passed to the tool.
    pub input: ToolArguments,
}

/// Tool execution result.
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
pub struct ToolResult {
    /// Matching call identifier.
    pub call_id: ToolCallId,
    /// Whether the tool succeeded.
    pub ok: bool,
    /// Output text or serialized result.
    pub output: String,
}

/// Shared, thread-safe cancellation handle for active effects.
/// Clone into a running effect; `cancel` from the owner thread.
#[derive(Clone, Debug, Default)]
pub struct CancelHandle {
    flag: std::sync::Arc<std::sync::atomic::AtomicBool>,
}

impl CancelHandle {
    /// A handle that starts uncancelled.
    #[must_use]
    pub fn new() -> Self {
        Self::default()
    }

    /// Request cancellation; observed by the owning effect.
    pub fn cancel(&self) {
        self.flag.store(true, std::sync::atomic::Ordering::Release);
    }

    /// Whether cancellation was requested.
    #[must_use]
    pub fn is_cancelled(&self) -> bool {
        self.flag.load(std::sync::atomic::Ordering::Acquire)
    }
}