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}