rullst 1.0.2

O framework fullstack definitivo para Rust, com foco em DX, velocidade e segurança.
Documentation
use crate::ai::{AiError, AiProvider, Message};
use async_trait::async_trait;

pub struct AnthropicProvider {
    api_key: String,
    model: String,
    client: reqwest::Client,
}

impl AnthropicProvider {
    pub fn new(api_key: impl Into<String>) -> Self {
        Self {
            api_key: api_key.into(),
            model: "claude-3-5-sonnet-latest".to_string(),
            client: reqwest::Client::new(),
        }
    }

    pub fn with_model(mut self, model: impl Into<String>) -> Self {
        self.model = model.into();
        self
    }
}

#[async_trait]
impl AiProvider for AnthropicProvider {
    async fn prompt(&self, text: &str) -> Result<String, AiError> {
        let messages = vec![Message::user(text)];
        self.chat(&messages).await
    }

    async fn chat(&self, messages: &[Message]) -> Result<String, AiError> {
        let url = "https://api.anthropic.com/v1/messages";

        let mut system_text = None;
        let mut chat_messages = Vec::new();

        for msg in messages {
            if msg.role == "system" {
                system_text = Some(msg.content.clone());
            } else {
                let role = match msg.role.as_str() {
                    "assistant" => "assistant",
                    _ => "user",
                };
                chat_messages.push(serde_json::json!({
                    "role": role,
                    "content": msg.content,
                }));
            }
        }

        let mut body = serde_json::json!({
            "model": self.model,
            "max_tokens": 1024,
            "messages": chat_messages,
        });

        if let Some(sys_prompt) = system_text
            && let Some(obj) = body.as_object_mut()
        {
            obj.insert("system".to_string(), serde_json::json!(sys_prompt));
        }

        let res = self
            .client
            .post(url)
            .header("x-api-key", &self.api_key)
            .header("anthropic-version", "2023-06-01")
            .header("content-type", "application/json")
            .json(&body)
            .send()
            .await?;

        if !res.status().is_success() {
            let status = res.status();
            let err_text = res.text().await.unwrap_or_default();
            return Err(AiError::ApiError(format!(
                "Anthropic error status {}: {}",
                status, err_text
            )));
        }

        let json: serde_json::Value = res.json().await?;
        let content = json["content"][0]["text"].as_str().ok_or_else(|| {
            AiError::ApiError("No text returned from Anthropic response".to_string())
        })?;

        Ok(content.to_string())
    }

    async fn embed(&self, _text: &str) -> Result<Vec<f32>, AiError> {
        Err(AiError::Other(
            "Anthropic does not support native text embeddings. Please use OpenAiProvider, GeminiProvider, or OllamaProvider for embeddings.".to_string(),
        ))
    }
}