Skip to main content

runifold_tool/
error.rs

1use std::collections::BTreeMap;
2
3use runifold_core::RetrySafety;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6use thiserror::Error;
7
8/// Normalized tool failure category.
9#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
10#[non_exhaustive]
11pub enum ToolErrorKind {
12    /// The requested tool is not registered.
13    NotFound,
14    /// Input failed tool-level validation.
15    InvalidInput,
16    /// The owning run was not granted the tool capability.
17    CapabilityDenied,
18    /// Execution was cancelled.
19    Cancelled,
20    /// The invocation deadline elapsed.
21    DeadlineExceeded,
22    /// Tool implementation failed.
23    Execution,
24    /// Tool output violated its declared contract.
25    InvalidOutput,
26}
27
28/// Structured tool execution error.
29#[derive(Clone, Debug, Deserialize, Error, PartialEq, Serialize)]
30#[error("{kind:?}: {message}")]
31pub struct ToolError {
32    /// Normalized category.
33    pub kind: ToolErrorKind,
34    /// Safe human-readable explanation.
35    pub message: String,
36    /// Retry-safety classification.
37    pub retry_safety: RetrySafety,
38    /// Namespaced diagnostic metadata.
39    pub metadata: BTreeMap<String, Value>,
40}
41
42impl ToolError {
43    /// Creates a local tool error with unknown retry safety.
44    pub fn local(kind: ToolErrorKind, message: impl Into<String>) -> Self {
45        Self {
46            kind,
47            message: message.into(),
48            retry_safety: RetrySafety::Unknown,
49            metadata: BTreeMap::new(),
50        }
51    }
52}
53
54/// Explicit conversion from an application error into a safe Tool failure.
55///
56/// Implementations decide which message is safe for model and operator
57/// visibility, as well as the normalized kind and retry-safety classification.
58pub trait IntoToolError {
59    /// Converts this application failure into the canonical Tool error.
60    fn into_tool_error(self) -> ToolError;
61}
62
63impl IntoToolError for ToolError {
64    fn into_tool_error(self) -> ToolError {
65        self
66    }
67}
68
69/// Failure to add a tool to a registry.
70#[derive(Clone, Debug, Eq, Error, PartialEq)]
71#[non_exhaustive]
72pub enum ToolRegistrationError {
73    /// Tool names must not be blank.
74    #[error("tool name cannot be empty")]
75    EmptyName,
76    /// A different tool already owns this model-facing name.
77    #[error("tool `{0}` is already registered")]
78    DuplicateName(String),
79    /// A declared input or output schema could not be compiled.
80    #[error("tool `{tool}` has an invalid {direction} schema: {message}")]
81    InvalidSchema {
82        /// Tool name.
83        tool: String,
84        /// Schema direction (`input` or `output`).
85        direction: &'static str,
86        /// Safe compiler diagnostic.
87        message: String,
88    },
89}