llm-tool 0.10.5

Framework-agnostic Rust tool definitions for LLM agents
Documentation
//! Internal implementation detail module for macro-generated code.
//!
//! **Not public API** — exported only for macro expansions generated by `#[llm_tool]`,
//! `#[llm_prompt]`, and `#[llm_resource]`.

#[doc(hidden)]
pub mod __private {
    // Re-exports for generated code to work in no_std contexts.
    #[cfg(not(feature = "std"))]
    pub use alloc::borrow::Cow;
    #[cfg(not(feature = "std"))]
    use alloc::{
        format,
        string::{String, ToString},
    };
    pub use core::{convert::Into, result::Result};
    #[cfg(feature = "std")]
    pub use std::borrow::Cow;
    /// Lazy initializer — [`std::sync::LazyLock`] under `std`,
    /// [`spin::LazyLock`] under `no_std`.
    #[cfg(feature = "std")]
    pub use std::sync::LazyLock as Lazy;

    /// Lazy initializer — [`std::sync::LazyLock`] under `std`,
    /// [`spin::LazyLock`] under `no_std`.
    #[cfg(not(feature = "std"))]
    pub use spin::LazyLock as Lazy;

    use crate::types::{Json, Sanitized, ToolError, ToolOutput, Untrusted};

    /// Report a runtime tool-description template render failure.
    ///
    /// Called by `#[llm_tool(..., context = ...)]`-generated `description()`
    /// code: on a render error the tool falls back to its static description
    /// body rather than panicking. Logs to stderr under `std`; a no-op under
    /// `no_std` (where no logger is available).
    #[cfg(feature = "std")]
    pub fn log_description_render_error(tool: &str, err: &dyn core::fmt::Display) {
        tracing::warn!(
            tool,
            error = %err,
            "llm-tool: tool description template failed to render; falling back to static description"
        );
    }

    /// `no_std` no-op counterpart of the `std` logger above.
    #[cfg(not(feature = "std"))]
    #[inline]
    pub fn log_description_render_error(_tool: &str, _err: &dyn core::fmt::Display) {}

    /// Wrapper enabling compile-time method dispatch for tool output conversion.
    pub struct Wrap<T>(pub T);

    // ── Inherent methods (highest priority in method resolution) ──

    impl Wrap<ToolOutput> {
        /// `ToolOutput` → identity pass-through.
        pub fn __convert(self) -> Result<ToolOutput, ToolError> {
            Ok(self.0)
        }
    }

    impl Wrap<String> {
        /// `String` → wrap as plain text (no JSON encoding).
        pub fn __convert(self) -> Result<ToolOutput, ToolError> {
            Ok(ToolOutput::new(self.0))
        }
        pub fn __convert_prompt(self) -> Result<crate::types::PromptOutput, ToolError> {
            Ok(crate::types::PromptOutput::user(self.0))
        }
        pub fn __convert_resource(
            self,
            uri: &str,
            mime_type: Option<&str>,
        ) -> Result<crate::types::ResourceOutput, ToolError> {
            Ok(crate::types::ResourceOutput::text(uri, mime_type, self.0))
        }
    }

    impl Wrap<&str> {
        /// `&str` → wrap as plain text (no JSON encoding).
        pub fn __convert(self) -> Result<ToolOutput, ToolError> {
            Ok(ToolOutput::new(self.0))
        }
        pub fn __convert_prompt(self) -> Result<crate::types::PromptOutput, ToolError> {
            Ok(crate::types::PromptOutput::user(self.0))
        }
        pub fn __convert_resource(
            self,
            uri: &str,
            mime_type: Option<&str>,
        ) -> Result<crate::types::ResourceOutput, ToolError> {
            Ok(crate::types::ResourceOutput::text(uri, mime_type, self.0))
        }
    }

    impl Wrap<Sanitized<ToolOutput>> {
        /// `Sanitized<ToolOutput>` → neutralize control tokens on the inner `ToolOutput`.
        pub fn __convert(self) -> Result<ToolOutput, ToolError> {
            Ok(self.0.0.sanitized())
        }
    }

    impl Wrap<Sanitized<String>> {
        /// `Sanitized<String>` → wrap as plain text and neutralize control tokens.
        pub fn __convert(self) -> Result<ToolOutput, ToolError> {
            Ok(ToolOutput::new(self.0.0).sanitized())
        }
    }

    impl Wrap<Sanitized<&str>> {
        /// `Sanitized<&str>` → wrap as plain text and neutralize control tokens.
        pub fn __convert(self) -> Result<ToolOutput, ToolError> {
            Ok(ToolOutput::new(self.0.0).sanitized())
        }
    }

    impl Wrap<Untrusted<ToolOutput>> {
        /// `Untrusted<ToolOutput>` → quarantine the inner `ToolOutput`.
        pub fn __convert(self) -> Result<ToolOutput, ToolError> {
            Ok(self.0.0.quarantined())
        }
    }

    impl Wrap<Untrusted<String>> {
        /// `Untrusted<String>` → wrap as plain text and quarantine.
        pub fn __convert(self) -> Result<ToolOutput, ToolError> {
            Ok(ToolOutput::new(self.0.0).quarantined())
        }
    }

    impl Wrap<Untrusted<&str>> {
        /// `Untrusted<&str>` → wrap as plain text and quarantine.
        pub fn __convert(self) -> Result<ToolOutput, ToolError> {
            Ok(ToolOutput::new(self.0.0).quarantined())
        }
    }

    impl Wrap<crate::types::PromptOutput> {
        pub fn __convert_prompt(self) -> Result<crate::types::PromptOutput, ToolError> {
            Ok(self.0)
        }
    }

    impl Wrap<crate::types::ResourceOutput> {
        pub fn __convert_resource(
            self,
            _uri: &str,
            _mime_type: Option<&str>,
        ) -> Result<crate::types::ResourceOutput, ToolError> {
            Ok(self.0)
        }
    }

    fn serialize_to_tool_output<T: serde::Serialize>(val: &T) -> Result<ToolOutput, ToolError> {
        let json_value = serde_json::to_value(val)
            .map_err(|e| ToolError::new(format!("serialization failed: {e}")))?;
        let content = json_value.to_string();
        match json_value {
            serde_json::Value::Object(map) => Ok(ToolOutput {
                content,
                metadata: map.into_iter().collect(),
            }),
            _ => Ok(ToolOutput::new(content)),
        }
    }

    impl<T: serde::Serialize> Wrap<Json<T>> {
        /// `Json<T>` → serialize to JSON string.
        pub fn __convert(self) -> Result<ToolOutput, ToolError> {
            serialize_to_tool_output(&self.0.0)
        }
    }

    impl<T: serde::Serialize> Wrap<Sanitized<Json<T>>> {
        /// `Sanitized<Json<T>>` → serialize to JSON string and neutralize control tokens.
        pub fn __convert(self) -> Result<ToolOutput, ToolError> {
            serialize_to_tool_output(&Sanitized(self.0.0.0)).map(ToolOutput::sanitized)
        }
    }

    impl<T: serde::Serialize> Wrap<Untrusted<Json<T>>> {
        /// `Untrusted<Json<T>>` → serialize to JSON string and quarantine.
        pub fn __convert(self) -> Result<ToolOutput, ToolError> {
            serialize_to_tool_output(&self.0.0.0).map(ToolOutput::quarantined)
        }
    }

    // ── Trait fallback (lower priority in method resolution) ──

    /// Fallback conversion for any `T: Serialize` not covered by inherent methods.
    ///
    /// Method resolution checks:
    /// 1. Inherent methods on `Wrap<...>` (`ToolOutput`, `String`, `&str`, `Json<T>`,
    ///    and their `Sanitized<...>` / `Untrusted<...>` counterparts).
    /// 2. Value-receiver `SerializeFallback` impls on `Wrap<Sanitized<T>>` and
    ///    `Wrap<Untrusted<T>>` for arbitrary `T: Serialize`.
    /// 3. Autoref-receiver `SerializeFallback` impl on `&Wrap<T>` for arbitrary `T: Serialize`.
    pub trait SerializeFallback {
        /// Serialize `self` to JSON and wrap as [`ToolOutput`].
        fn __convert(self) -> Result<ToolOutput, ToolError>;
    }

    impl<T: serde::Serialize> SerializeFallback for Wrap<Sanitized<T>> {
        fn __convert(self) -> Result<ToolOutput, ToolError> {
            serialize_to_tool_output(&self.0).map(ToolOutput::sanitized)
        }
    }

    impl<T: serde::Serialize> SerializeFallback for Wrap<Untrusted<T>> {
        fn __convert(self) -> Result<ToolOutput, ToolError> {
            serialize_to_tool_output(&self.0.0).map(ToolOutput::quarantined)
        }
    }

    impl<T: serde::Serialize> SerializeFallback for &Wrap<T> {
        fn __convert(self) -> Result<ToolOutput, ToolError> {
            serialize_to_tool_output(&self.0)
        }
    }
}