openrouter-rust 0.1.0

A modular, type-safe Rust client for the OpenRouter API
Documentation
//! # OpenRouter Rust Client
//!
//! A comprehensive, type-safe Rust client library for the [OpenRouter API](https://openrouter.ai/).
//!
//! ## Features
//!
//! - **Chat Completions API**: Full support for `/v1/chat/completions` endpoint
//! - **Responses API (Beta)**: Support for the new `/v1/responses` endpoint
//! - **Anthropic Messages API**: Support for `/v1/messages` endpoint
//! - **Embeddings API**: Text and image embeddings support
//! - **Streaming**: Real-time streaming responses using Server-Sent Events
//! - **Models API**: List and filter available models
//! - **Providers API**: List available providers
//! - **Generation API**: Retrieve generation metadata
//! - **Type-Safe**: Comprehensive types for all API requests and responses
//! - **Builder Pattern**: Ergonomic builder APIs for constructing requests
//! - **Error Handling**: Detailed error types for different failure scenarios
//!
//! ## Quick Start
//!
//! ```rust,no_run
//! use openrouter_rust::{OpenRouterClient, ChatCompletionBuilder};
//!
//! #[tokio::main]
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
//!     // Create a client
//!     let client = OpenRouterClient::builder()
//!         .api_key("your-api-key")
//!         .build()?;
//!
//!     // Build a request
//!     let request = ChatCompletionBuilder::new("openai/gpt-3.5-turbo")
//!         .user_message("What is the meaning of life?")
//!         .build();
//!
//!     // Send the request
//!     let response = client.chat_completion(request).await?;
//!     
//!     if let Some(ref content) = response.choices[0].message.content {
//!         println!("Response: {}", content);
//!     }
//!     
//!     Ok(())
//! }
//! ```
//!
//! ## Feature Flags
//!
//! - `chat` (default): Enable chat completions API
//! - `responses`: Enable Responses API (Beta)
//! - `streaming` (default): Enable streaming support
//! - `embeddings`: Enable embeddings API
//! - `anthropic`: Enable Anthropic Messages API
//! - `providers`: Enable providers API
//! - `models`: Enable models API
//! - `generations`: Enable generation metadata API
//!
//! ## Examples
//!
//! See the `examples/` directory for more comprehensive examples.

pub mod client;
pub mod error;
pub mod types;

#[cfg(feature = "chat")]
pub mod chat;

#[cfg(feature = "responses")]
pub mod responses;

#[cfg(feature = "streaming")]
pub mod streaming;

#[cfg(feature = "embeddings")]
pub mod embeddings;

#[cfg(feature = "anthropic")]
pub mod anthropic;

#[cfg(feature = "providers")]
pub mod providers;

#[cfg(feature = "models")]
pub mod models;

#[cfg(feature = "generations")]
pub mod generations;

// Re-export commonly used items
pub use client::{OpenRouterClient, OpenRouterClientBuilder};
pub use error::{OpenRouterError, Result};

#[cfg(feature = "chat")]
pub use chat::{
    ChatCompletionBuilder,
    ChatCompletionRequest,
    ChatCompletionResponse,
    ChatCompletionBuilder as ChatRequestBuilder,
    Choice,
};

#[cfg(feature = "responses")]
pub use responses::{
    ResponsesRequestBuilder,
    ResponsesRequest,
    ResponsesResponse,
    ResponsesRequestBuilder as ResponseBuilder,
    InputItem, InputRole, InputContent, OutputItem, OutputContent,
    ReasoningSummary, ReasoningConfig,
};

#[cfg(feature = "streaming")]
pub use streaming::{
    ChatCompletionStream,
    ChatCompletionChunk,
    StreamingChoice,
    DeltaMessage,
    collect_stream,
};

#[cfg(feature = "embeddings")]
pub use embeddings::{
    EmbeddingBuilder,
    EmbeddingRequest,
    EmbeddingResponse,
    EmbeddingData,
    EmbeddingDataItem,
    EmbeddingUsage,
    EmbeddingModel,
    EmbeddingInput,
    EmbeddingEncodingFormat,
};

#[cfg(feature = "anthropic")]
pub use anthropic::{
    AnthropicMessageBuilder,
    AnthropicMessageRequest,
    AnthropicMessageResponse,
    AnthropicMessageParam,
    AnthropicRole,
    AnthropicContentItem,
    AnthropicResponseContent,
    AnthropicUsage,
    AnthropicTool,
    AnthropicToolChoice,
    AnthropicThinking,
};

#[cfg(feature = "providers")]
pub use providers::{
    Provider,
    ProvidersResponse,
};

#[cfg(feature = "models")]
pub use models::{
    Model,
    ModelsResponse,
    ModelsCountResponse,
    PublicPricing,
    ModelArchitecture,
    TopProviderInfo,
    PerRequestLimits,
    DefaultParameters,
    ListModelsParams,
};

#[cfg(feature = "generations")]
pub use generations::{
    GenerationResponse,
    GenerationData,
    ProviderResponse,
};

pub use types::{
    Message,
    Role,
    Function,
    Tool,
    ToolChoice,
    ResponseFormat,
    Usage,
    Plugin,
    ProviderPreferences,
};

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_error_display() {
        let err = OpenRouterError::ConfigError("test error".to_string());
        assert_eq!(err.to_string(), "Configuration error: test error");
    }

    #[test]
    fn test_role_serialization() {
        let role = Role::User;
        let json = serde_json::to_string(&role).unwrap();
        assert_eq!(json, "\"user\"");

        let role = Role::Assistant;
        let json = serde_json::to_string(&role).unwrap();
        assert_eq!(json, "\"assistant\"");
    }

    #[test]
    fn test_role_deserialization() {
        let role: Role = serde_json::from_str("\"system\"").unwrap();
        assert!(matches!(role, Role::System));
    }

    #[test]
    fn test_usage_default() {
        let usage = Usage {
            prompt_tokens: 10,
            completion_tokens: 20,
            total_tokens: 30,
            prompt_tokens_details: None,
            completion_tokens_details: None,
            cost: None,
        };

        assert_eq!(usage.prompt_tokens, 10);
        assert_eq!(usage.completion_tokens, 20);
        assert_eq!(usage.total_tokens, 30);
    }

    #[test]
    fn test_message_builder_pattern() {
        let msg = Message {
            role: Role::User,
            content: Some("Hello".to_string()),
            name: None,
            tool_calls: None,
        };

        assert!(matches!(msg.role, Role::User));
        assert_eq!(msg.content, Some("Hello".to_string()));
    }
}