lc-cli 0.1.3

LLM Client - A fast Rust-based LLM CLI tool with provider management and chat sessions
Documentation
# Configuration

Learn how to configure LC library for different providers and customize settings.

## Configuration Loading

LC library uses the same configuration system as the CLI tool. Configuration is stored in TOML format.

### Default Configuration Path

```rust
use lc_cli::Config;

// Loads from ~/.config/lc/config.toml (Linux/macOS)
// or %APPDATA%/lc/config.toml (Windows)
let config = Config::load()?;
```

### Custom Configuration Path

```rust
use lc_cli::Config;

let config = Config::load_from_path("/path/to/your/config.toml")?;
```

### Programmatic Configuration

```rust
use lc_cli::{Config, ProviderConfig};
use std::collections::HashMap;

let mut providers = HashMap::new();
providers.insert("openai".to_string(), ProviderConfig {
    api_key: Some("your-api-key".to_string()),
    base_url: Some("https://api.openai.com/v1".to_string()),
    model: Some("gpt-4".to_string()),
    ..Default::default()
});

let config = Config {
    providers,
    default_provider: Some("openai".to_string()),
    ..Default::default()
};
```

## Provider Setup

### OpenAI Configuration

```rust
use lc_cli::{Config, ProviderConfig};

// In your config.toml or programmatically:
let openai_config = ProviderConfig {
    api_key: Some("sk-your-openai-key".to_string()),
    base_url: Some("https://api.openai.com/v1".to_string()),
    model: Some("gpt-4".to_string()),
    max_tokens: Some(2000),
    temperature: Some(0.7),
    ..Default::default()
};
```

### Anthropic Configuration

```rust
let anthropic_config = ProviderConfig {
    api_key: Some("your-anthropic-key".to_string()),
    base_url: Some("https://api.anthropic.com".to_string()),
    model: Some("claude-3-sonnet-20240229".to_string()),
    max_tokens: Some(4000),
    temperature: Some(0.3),
    ..Default::default()
};
```

### Google Gemini Configuration

```rust
let gemini_config = ProviderConfig {
    api_key: Some("your-gemini-key".to_string()),
    base_url: Some("https://generativelanguage.googleapis.com/v1beta".to_string()),
    model: Some("gemini-pro".to_string()),
    ..Default::default()
};
```

## Configuration Directory Structure

LC uses a per-provider configuration system with separate files for each provider:

### Configuration Directory Locations

- **macOS**: `~/Library/Application Support/lc/`
- **Linux**: `~/.local/share/lc/`
- **Windows**: `%LOCALAPPDATA%/lc/`

### Directory Structure

```
lc/
├── config.toml              # Main config (defaults, aliases, templates)
├── keys.toml                # Centralized API keys and auth
├── providers/               # Individual provider configurations
│   ├── openai.toml
│   ├── anthropic.toml
│   └── gemini.toml
└── logs.db                  # Chat history database
```

### Main Config File (`config.toml`)

```toml
default_provider = "openai"
default_model = "gpt-4"

# Global defaults
max_tokens = 2000
temperature = 0.7
stream = false

# Aliases for quick access
[aliases]
fast = "openai:gpt-3.5-turbo"
smart = "anthropic:claude-3-sonnet-20240229"

# Templates for reusable prompts
[templates]
code_review = "Review this code for best practices and potential issues:"
summarize = "Provide a concise summary of the following:"
```

### Provider Config Files (`providers/provider_name.toml`)

Each provider has its own configuration file:

**`providers/openai.toml`**:
```toml
endpoint = "https://api.openai.com/v1"
models_path = "/models"
chat_path = "/chat/completions"
images_path = "/images/generations"
embeddings_path = "/embeddings"
speech_path = "/audio/speech"
models = ["gpt-4", "gpt-3.5-turbo"]
```

**`providers/anthropic.toml`**:
```toml
endpoint = "https://api.anthropic.com"
models_path = "/v1/models"
chat_path = "/v1/messages"
models = ["claude-3-sonnet-20240229", "claude-3-haiku-20240307"]
```

### API Keys (`keys.toml`)

API keys are stored separately for security:

```toml
[api_keys]
openai = "sk-your-openai-key"
anthropic = "your-anthropic-key"
gemini = "your-gemini-key"

# Custom headers for authentication
[custom_headers.some_provider]
"X-API-Key" = "your-custom-key"
"Authorization" = "Bearer your-token"
```

## Using Configuration in Library Code

### Loading Configuration

```rust
use lc_cli::Config;

// Load all configuration (main config + providers + keys)
let config = Config::load()?;

// Access configuration
println!("Default provider: {:?}", config.default_provider);
println!("Available providers: {:?}", config.providers.keys().collect::<Vec<_>>());

// Check if a provider exists
if config.has_provider("openai") {
    println!("OpenAI is configured");
}
```

### Working with Provider Configurations

```rust
use lc_cli::{Config, chat};

// Get provider with authentication (combines provider config + API keys)
let mut config = Config::load()?;
let provider_config = config.get_provider_with_auth("openai")?;

// Create authenticated client
let client = chat::create_authenticated_client(&mut config, "openai").await?;
```

### Environment Variables

You can override API keys using environment variables:

```bash
# These will be used instead of keys.toml
export OPENAI_API_KEY="sk-your-key"
export ANTHROPIC_API_KEY="your-key"
export GEMINI_API_KEY="your-key"
```

### Programmatic Configuration

For library usage, you can also create configurations programmatically:

```rust
use lc_cli::{Config, ProviderConfig};
use std::collections::HashMap;

// Create a minimal config programmatically
let mut providers = HashMap::new();
providers.insert("openai".to_string(), ProviderConfig {
    endpoint: "https://api.openai.com/v1".to_string(),
    api_key: Some(std::env::var("OPENAI_API_KEY")?),
    models: vec!["gpt-4".to_string(), "gpt-3.5-turbo".to_string()],
    models_path: "/models".to_string(),
    chat_path: "/chat/completions".to_string(),
    ..Default::default()
});

let config = Config {
    providers,
    default_provider: Some("openai".to_string()),
    ..Default::default()
};
```

## Configuration Validation

```rust
use lc_cli::{Config, error::LcError};

async fn validate_config() -> Result<(), LcError> {
    let config = Config::load()?;
    
    // Check if default provider is configured
    if let Some(default_provider) = &config.default_provider {
        if let Some(provider_config) = config.providers.get(default_provider) {
            if provider_config.api_key.is_none() {
                return Err(LcError::Config(
                    format!("No API key configured for provider: {}", default_provider)
                ));
            }
            println!("✅ Provider {} is properly configured", default_provider);
        } else {
            return Err(LcError::Config(
                format!("Default provider {} not found in configuration", default_provider)
            ));
        }
    } else {
        return Err(LcError::Config("No default provider configured".to_string()));
    }
    
    Ok(())
}
```

## Dynamic Provider Selection

```rust
use lc_cli::{Config, OpenAIClient, provider::AnthropicClient};

async fn use_specific_provider(provider_name: &str) -> Result<String, Box<dyn std::error::Error>> {
    let config = Config::load()?;
    
    let response = match provider_name {
        "openai" => {
            let client = OpenAIClient::new(&config)?;
            let request = create_chat_request("gpt-4", "Hello!");
            client.send_chat_request(request).await?
        },
        "anthropic" => {
            let client = AnthropicClient::new(&config)?;
            let request = create_chat_request("claude-3-sonnet-20240229", "Hello!");
            client.send_chat_request(request).await?
        },
        _ => return Err(format!("Unsupported provider: {}", provider_name).into()),
    };
    
    Ok(response.content)
}

fn create_chat_request(model: &str, content: &str) -> ChatRequest {
    ChatRequest {
        messages: vec![Message {
            role: "user".to_string(),
            content: content.to_string(),
        }],
        model: model.to_string(),
        ..Default::default()
    }
}
```

## Configuration Best Practices

### 1. Keep API Keys Secure

```rust
// ❌ Don't hardcode API keys
let config = ProviderConfig {
    api_key: Some("sk-hardcoded-key".to_string()),
    ..Default::default()
};

// ✅ Use environment variables or config files
let config = ProviderConfig {
    api_key: std::env::var("OPENAI_API_KEY").ok(),
    ..Default::default()
};
```

### 2. Use Default Values

```rust
use lc_cli::{Config, ProviderConfig};

let config = Config {
    providers: {
        let mut providers = std::collections::HashMap::new();
        providers.insert("openai".to_string(), ProviderConfig {
            api_key: std::env::var("OPENAI_API_KEY").ok(),
            model: Some("gpt-4".to_string()),
            // Use defaults for other fields
            ..Default::default()
        });
        providers
    },
    default_provider: Some("openai".to_string()),
    ..Default::default()
};
```

### 3. Handle Missing Configuration Gracefully

```rust
use lc_cli::{Config, error::LcError};

async fn robust_config_loading() -> Result<Config, LcError> {
    match Config::load() {
        Ok(config) => {
            // Validate that we have at least one provider configured
            if config.providers.is_empty() {
                return Err(LcError::Config("No providers configured".to_string()));
            }
            Ok(config)
        },
        Err(_) => {
            // Create a minimal default configuration
            println!("No config found, using environment variables");
            let mut providers = std::collections::HashMap::new();
            
            if let Ok(api_key) = std::env::var("OPENAI_API_KEY") {
                providers.insert("openai".to_string(), ProviderConfig {
                    api_key: Some(api_key),
                    model: Some("gpt-4".to_string()),
                    ..Default::default()
                });
            }
            
            if providers.is_empty() {
                return Err(LcError::Config("No API keys found in environment".to_string()));
            }
            
            Ok(Config {
                providers,
                default_provider: Some("openai".to_string()),
                ..Default::default()
            })
        }
    }
}
```

## Next Steps

- [**Advanced Features**]advanced-features.md - Vector databases, sessions, and templates
- [**Basic Usage**]basic-usage.md - Go back to basic examples