providers-kit 0.0.2

Portable enum-dispatch Kit over multiple model provider APIs.
Documentation
use serde_json::{Value, json};

use crate::KitError;
use crate::error::ValidationViolation;
use crate::hosted::HostedTool;

pub(crate) struct AnthropicHostedPlanner;

impl AnthropicHostedPlanner {
    pub(crate) const fn new() -> Self {
        Self
    }

    pub(crate) fn validate<'a>(
        &self,
        model: &str,
        tools: impl IntoIterator<Item = &'a HostedTool>,
    ) -> Result<(), KitError> {
        let mut has_hosted = false;
        for tool in tools {
            has_hosted = true;
            if matches!(tool, HostedTool::FileSearch { .. }) {
                return Err(KitError::unsupported(
                    ValidationViolation::Unsupported {
                        field: "tools.file_search",
                        rule: "hosted file search unavailable",
                    },
                    "Anthropic hosted file search",
                ));
            }
        }
        if has_hosted
            && !matches!(
                model,
                "claude-fable-5-1"
                    | "claude-opus-5-5"
                    | "claude-sonnet-5-5"
                    | "claude-haiku-5-5"
                    | "claude-fable-5"
                    | "claude-mythos-5"
                    | "claude-opus-5"
                    | "claude-sonnet-5"
                    | "claude-opus-4-8"
                    | "claude-opus-4-7"
                    | "claude-opus-4-6"
                    | "claude-sonnet-4-6"
                    | "claude-opus-4-5-20251101"
                    | "claude-sonnet-4-5-20250929"
                    | "claude-haiku-4-5"
                    | "claude-haiku-4-5-20251001"
            )
        {
            return Err(KitError::unsupported(
                ValidationViolation::Unsupported {
                    field: "tools.hosted",
                    rule: "hosted tools unavailable for selected model",
                },
                format!("Anthropic hosted tools for exact model `{model}`"),
            ));
        }
        Ok(())
    }

    pub(crate) fn render_declaration(&self, tool: &HostedTool) -> Result<Value, KitError> {
        match tool {
            HostedTool::WebSearch => Ok(json!({
                "type": "web_search_20250305",
                "name": "web_search",
            })),
            HostedTool::UrlContext => Ok(json!({
                "type": "web_fetch_20250910",
                "name": "web_fetch",
            })),
            HostedTool::CodeExecution => Ok(json!({
                "type": "code_execution_20250825",
                "name": "code_execution",
            })),
            HostedTool::FileSearch { .. } => Err(KitError::unsupported(
                ValidationViolation::Unsupported {
                    field: "tools.file_search",
                    rule: "hosted file search unavailable",
                },
                "Anthropic hosted file search",
            )),
        }
    }
}