solo-storage 0.5.0

Solo: SQLite + SQLCipher persistence layer
Documentation
// SPDX-License-Identifier: Apache-2.0

//! Production [`LlmClient`](solo_core::LlmClient) backends.
//!
//! Solo's stub LLM lives in `solo_steward::test_support` (deterministic,
//! no-network). This module is the production side: real backends that
//! talk to hosted or local models via HTTP / FFI / candle.
//!
//! v0.2.0 shipped **Anthropic Claude** ([`AnthropicClient`]). v0.3
//! adds **OpenAI Chat Completions** ([`OpenAIClient`]) as a sibling
//! — same `LlmClient` trait, different wire format. Future backends:
//!
//!   - **candle-Qwen3-Coder local** — offline default per ADR-0002.
//!     ~30 GB weights, GPU detection, download flow. Likely lands as
//!     a sibling crate (`solo-llm-candle`?) given the ramp.
//!
//! All backends implement `solo_core::LlmClient` so the
//! `solo_steward::Steward` interactions stay backend-agnostic.
//!
//! ## Selection precedence
//!
//! When more than one backend's env var is set, the CLI's startup
//! glue ([`crate::llm`] consumers in `solo-cli`) prefers
//! Anthropic over OpenAI. Setting `OPENAI_API_KEY` only takes
//! effect when `ANTHROPIC_API_KEY` is unset/empty. This keeps the
//! v0.2 default behavior stable for users who already had
//! Anthropic configured before upgrading.

pub mod anthropic;
pub mod ollama;
pub mod openai;
pub mod retry;

pub use anthropic::{AnthropicClient, build_anthropic_client_from_env};
pub use ollama::{OllamaClient, is_ollama_base_url};
pub use openai::{OpenAIClient, build_openai_client_from_env};
pub use retry::RetryConfig;

use std::sync::Arc;

use solo_core::{LlmClient, Result};

/// Build an [`LlmClient`] from environment variables, applying the
/// precedence documented at the module level: **Anthropic first**,
/// then **OpenAI**, then `None`.
///
/// Returns:
///
///   - `Ok(Some(client))` if either provider's env var is set.
///   - `Ok(None)` if neither is set/non-empty (caller proceeds
///     without a Steward — clustering-only consolidation).
///   - `Err(_)` if an env var is set but the HTTP client can't be
///     built (rare — TLS init failure).
///
/// Single source of truth — both `solo daemon` (consolidate timer)
/// and one-shot CLI flows use this helper, so the selection rule
/// stays identical across surfaces.
pub fn build_llm_client_from_env() -> Result<Option<Arc<dyn LlmClient>>> {
    if let Some(client) = build_anthropic_client_from_env()? {
        return Ok(Some(client));
    }
    if let Some(client) = build_openai_client_from_env()? {
        return Ok(Some(client));
    }
    Ok(None)
}