llm-tool 0.9.4

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, ToolError, ToolOutput};

    /// 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> {
        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<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)
        }
    }

    impl<T: serde::Serialize> Wrap<Json<T>> {
        /// `Json<T>` → serialize to JSON string.
        pub fn __convert(self) -> Result<ToolOutput, ToolError> {
            let json_value = serde_json::to_value(&self.0.0)
                .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)),
            }
        }
    }

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

    /// Fallback conversion for any `T: Serialize` not covered by inherent methods.
    ///
    /// The compiler checks inherent methods first, so `String` and `ToolOutput`
    /// use their inherent impls. Everything else falls through to this trait,
    /// which serializes the value to JSON.
    pub trait SerializeFallback {
        /// Serialize `self` to JSON and wrap as [`ToolOutput`].
        fn __convert(self) -> Result<ToolOutput, ToolError>;
    }

    impl<T: serde::Serialize> SerializeFallback for Wrap<T> {
        fn __convert(self) -> Result<ToolOutput, ToolError> {
            let json_value = serde_json::to_value(&self.0)
                .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)),
            }
        }
    }
}