devops-models 0.1.0

Typed serde models for DevOps configuration formats: Kubernetes, Docker Compose, GitLab CI, GitHub Actions, Prometheus, Alertmanager, Helm, Ansible, and OpenAPI.
Documentation
//! LLM provider configuration.
//!
//! Defines which LLM backend to use and how to reach it.  This module
//! contains only pure data types — no HTTP code, no async runtime.

use serde::{Deserialize, Serialize};

/// Supported LLM providers.
///
/// Each variant maps to a different API schema and default endpoint.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum LlmProvider {
    /// Anthropic Claude — uses the Messages API (`/v1/messages`).
    Anthropic,
    /// OpenAI — uses the Chat Completions API (`/v1/chat/completions`).
    OpenAI,
    /// Ollama — self-hosted, OpenAI-compatible endpoint on `localhost:11434`.
    Ollama,
}

/// Complete configuration for an LLM client.
///
/// Use [`endpoint`](LlmConfig::endpoint) to resolve the effective API URL,
/// which respects any custom `base_url` override.
///
/// # Example
///
/// ```rust
/// use devops_models::llm::provider::{LlmConfig, LlmProvider};
///
/// let config = LlmConfig {
///     provider: LlmProvider::Anthropic,
///     api_key: "sk-ant-...".to_string(),
///     model: "claude-sonnet-4-20250514".to_string(),
///     base_url: None,
/// };
///
/// assert!(config.endpoint().contains("anthropic.com"));
/// ```
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LlmConfig {
    /// Which LLM backend to target.
    pub provider: LlmProvider,
    /// API key (or empty string for Ollama / local setups).
    pub api_key: String,
    /// Model identifier (e.g. `"claude-sonnet-4-20250514"`, `"gpt-4o"`).
    pub model: String,
    /// Optional URL override.  When `None`, the provider's default endpoint is used.
    #[serde(default)]
    pub base_url: Option<String>,
}

impl LlmConfig {
    /// Resolve the effective API endpoint URL.
    ///
    /// Returns `base_url` if set, otherwise the provider's default endpoint.
    ///
    /// # Example
    ///
    /// ```rust
    /// use devops_models::llm::provider::{LlmConfig, LlmProvider};
    ///
    /// let config = LlmConfig {
    ///     provider: LlmProvider::Ollama,
    ///     api_key: String::new(),
    ///     model: "qwen3:8b".to_string(),
    ///     base_url: Some("http://my-ollama:11434/v1/chat/completions".to_string()),
    /// };
    /// assert_eq!(config.endpoint(), "http://my-ollama:11434/v1/chat/completions");
    /// ```
    pub fn endpoint(&self) -> String {
        match &self.base_url {
            Some(url) => url.clone(),
            None => match self.provider {
                LlmProvider::Anthropic => "https://api.anthropic.com/v1/messages".to_string(),
                LlmProvider::OpenAI => "https://api.openai.com/v1/chat/completions".to_string(),
                LlmProvider::Ollama => "http://localhost:11434/v1/chat/completions".to_string(),
            },
        }
    }

    /// Return the recommended default model identifier for this provider.
    ///
    /// This is a convenience helper for new configurations; callers should
    /// always allow the user to override with a specific model string.
    pub fn default_model(&self) -> &str {
        match self.provider {
            LlmProvider::Anthropic => "claude-sonnet-4-20250514",
            LlmProvider::OpenAI => "gpt-4o",
            LlmProvider::Ollama => "qwen3:8b",
        }
    }
}

impl Default for LlmConfig {
    fn default() -> Self {
        Self {
            provider: LlmProvider::Ollama,
            api_key: String::new(),
            model: "qwen3:8b".to_string(),
            base_url: None,
        }
    }
}