A unified CLI and Python library for interacting with local and remote LLMs, compatible with the OpenAI Responses API.
Project description
Claif - Command Line Artificial Intelligence Framework
Quickstart
# Install with all providers and query Claude
pip install claif[all]
claif query "Explain quantum computing in one sentence"
# Or install specific providers
pip install claif claif_cla # Just Claude
pip install claif claif_gem # Just Gemini
pip install claif claif_cod # Just Codex/OpenAI
# Stream responses in real-time
claif stream "Tell me a story" --provider gemini
# Query all providers in parallel
claif parallel "What is the meaning of life?" --compare
What is Claif?
Claif is a unified Python framework for interacting with multiple AI language model providers through a consistent interface. It lets you seamlessly switch between Claude (Anthropic), Gemini (Google), and Codex (OpenAI) without changing your code.
Key Features:
- Single API for all AI providers - write once, use any model
- Plugin-based architecture - providers are discovered dynamically
- Rich CLI with Fire framework and beautiful terminal output
- Full async support for concurrent operations
- Type-safe with comprehensive hints throughout
- MCP server for tool integration
- Auto-install - missing provider CLIs are installed on first use
Installation
Basic Installation
# Core framework only
pip install claif
# With all providers (recommended)
pip install claif[all]
Installing Specific Providers
# Claude provider (wraps Claude Code SDK)
pip install claif claif_cla
# Gemini provider (wraps Gemini CLI)
pip install claif claif_gem
# Codex provider (wraps OpenAI Codex CLI)
pip install claif claif_cod
Installing Provider CLIs
Claif can auto-install missing CLIs, or you can install them manually:
# Auto-install all CLIs
claif install
# Or install specific CLIs
claif install claude
claif install gemini
claif install codex
# Install via npm (if preferred)
npm install -g @anthropic-ai/claude-code
npm install -g @google/gemini-cli
npm install -g @openai/codex
Development Installation
git clone https://github.com/twardoch/claif.git
cd claif
pip install -e ".[dev,test]"
CLI Usage
Claif provides a comprehensive command-line interface built with Fire that shows clean, focused output by default. Use --verbose for detailed logging.
Basic Commands
# Query default provider (Claude)
claif query "What is Python?"
# Query specific provider
claif query "Explain recursion" --provider gemini
# Query with options
claif query "Write a haiku about coding" --temperature 0.7 --max-tokens 50
# Stream responses in real-time with live display
claif stream "Tell me a story" --provider claude
# Query a random provider
claif random "Tell me a programming joke"
# Query all providers in parallel
claif parallel "What is AI?" --compare # Side-by-side comparison
Provider Management
# List providers with their status
claif providers list
# Check health of all providers
claif providers status
# Install provider CLIs
claif install # All providers
claif install claude # Specific provider
# Check installation status
claif status
Configuration
# Show current configuration
claif config show
# Set configuration values
claif config set default_provider=gemini
claif config set cache_enabled=true
claif config set output_format=markdown
# Save configuration to file
claif config save
MCP Server
# Start the MCP (Model Context Protocol) server
claif server --host localhost --port 8000 --reload
# The server provides tools:
# - claif_query: Query specific provider
# - claif_query_random: Query random provider
# - claif_query_all: Query all providers
# - claif_list_providers: List available providers
# - claif_health_check: Check provider health
Python API Usage
Basic Usage
import asyncio
from claif import query, ClaifOptions, Provider
async def main():
# Simple query using default provider
async for message in query("Hello, world!"):
print(message.content)
# Query specific provider
options = ClaifOptions(provider=Provider.GEMINI)
async for message in query("Explain Python decorators", options):
print(f"[{message.role}]: {message.content}")
asyncio.run(main())
Advanced Options
from claif import query, ClaifOptions, Provider
options = ClaifOptions(
provider=Provider.CLAUDE,
model="claude-3-opus-20240229",
temperature=0.7,
max_tokens=1000,
system_prompt="You are an expert Python developer",
timeout=60,
cache=True,
verbose=False # Clean output by default
)
async def get_code_review():
async for message in query("Review this code: def fib(n): return fib(n-1) + fib(n-2)", options):
print(message.content)
asyncio.run(get_code_review())
Parallel Queries
from claif import query_all, query_random
# Query all providers in parallel
async def compare_providers():
async for results in query_all("What is machine learning?"):
for provider, messages in results.items():
print(f"\n{provider.value.upper()}:")
for msg in messages:
print(msg.content)
# Query random provider
async def get_random_response():
async for message in query_random("Tell me a programming joke"):
print(message.content)
asyncio.run(compare_providers())
Using the Client Class
from claif.client import ClaifClient
from claif import ClaifOptions, Provider
# Create client instance
client = ClaifClient()
# List available providers
providers = client.list_providers()
print(f"Available: {[p.value for p in providers]}")
# Query with auto-install
# If Claude CLI is missing, it will be installed automatically
options = ClaifOptions(provider=Provider.CLAUDE)
async def query_with_client():
async for message in client.query("Explain asyncio", options):
print(message.content)
asyncio.run(query_with_client())
How It Works
Architecture Overview
Claif uses a layered architecture that separates concerns and enables provider independence:
┌─────────────────────────────┐
│ User Application │
├─────────────────────────────┤
│ Claif CLI │ ← Fire-based CLI with rich output
├─────────────────────────────┤
│ Claif Client │ ← Async client with provider routing
├─────────────────────────────┤
│ Common Types/Utils │ ← Shared data structures and utilities
├─────────────────────────────┤
│ Provider Wrappers │ ← Simple adapters for provider packages
├─────┬─────┬─────┬──────────┤
│claif_cla│claif_gem│claif_cod│ ← Actual provider implementations
└─────┴─────┴─────┴──────────┘
Core Components
Common Module (src/claif/common/)
types.py - Core data structures:
class Message:
role: MessageRole # USER, ASSISTANT, SYSTEM
content: str | list[TextBlock | ToolUseBlock | ToolResultBlock]
class Provider(Enum):
CLAUDE = "claude"
GEMINI = "gemini"
CODEX = "codex"
class ClaifOptions:
provider: Provider | None
model: str | None
temperature: float | None
max_tokens: int | None
system_prompt: str | None
timeout: int | None
cache: bool = True
verbose: bool = False
config.py - Hierarchical configuration:
- Default values → Config files → Environment vars → CLI args
- Locations:
~/.claif/config.json,~/.config/claif/config.json,./claif.json - Provider-specific settings with API key management
errors.py - Exception hierarchy:
ClaifError→ Base exceptionProviderError→ Provider-specific failuresConfigurationError→ Configuration issuesTransportError→ Communication errorsTimeoutError→ Operation timeouts
install.py - Auto-installation support:
def install_provider(provider, package, exec_name):
# 1. Install npm package using bun
# 2. Bundle executable
# 3. Install to ~/.local/bin
Providers Module (src/claif/providers/)
Simple wrapper classes that delegate to provider packages:
# claude.py
from claif_cla import query as claude_query
class ClaudeProvider:
async def query(self, prompt: str, options: ClaifOptions) -> AsyncIterator[Message]:
logger.debug(f"Querying Claude with prompt: {prompt[:50]}...")
async for message in claude_query(prompt, options):
yield message
Each provider:
- Imports the actual provider package (
claif_cla,claif_gem,claif_cod) - Implements the same
queryinterface - Handles provider-specific option conversion
Client Module (src/claif/client.py)
The main client with auto-install support:
class ClaifClient:
def __init__(self):
self.providers = {
Provider.CLAUDE: ClaudeProvider(),
Provider.GEMINI: GeminiProvider(),
Provider.CODEX: CodexProvider(),
}
async def query(self, prompt: str, options: ClaifOptions) -> AsyncIterator[Message]:
try:
# Route to provider
async for message in provider.query(prompt, options):
yield message
except Exception as e:
if _is_cli_missing_error(e):
# Auto-install missing CLI
install_result = install_func()
if install_result.get("installed"):
# Retry query
async for message in provider.query(prompt, options):
yield message
Key features:
- Routes queries to appropriate providers
- Auto-installs missing provider CLIs
- Implements
query,query_random, andquery_all - Handles errors with graceful fallbacks
CLI Module (src/claif/cli.py)
Fire-based CLI with rich terminal output:
class ClaifCLI:
def __init__(self, config_file=None, verbose=False):
# Clean output by default
logger.remove()
if verbose:
logger.add(sys.stderr, level="DEBUG")
else:
# Only errors go to stderr in non-verbose mode
logger.add(sys.stderr, level="ERROR")
Main commands:
query- Execute queries with optionsstream- Live streaming with rich.Live displayrandom- Query random providerparallel- Query all providers (with--comparefor side-by-side)session- Start provider-specific interactive sessioninstall- Install provider CLIs with bun bundlingconfig- Manage settingsserver- Start MCP server
Server Module (src/claif/server.py)
FastMCP server implementation:
server = FastMCP("Claif MCP Server")
@server.tool()
async def claif_query(prompt: str, provider: str = None) -> list[dict]:
"""Query specific AI provider."""
options = ClaifOptions(provider=Provider(provider) if provider else None)
messages = []
async for message in query(prompt, options):
messages.append(message.to_dict())
return messages
# Additional tools:
# - claif_query_random: Random provider selection
# - claif_query_all: Parallel queries
# - claif_list_providers: Available providers
# - claif_health_check: Provider status
Install Module (src/claif/install.py)
Handles CLI installation and bundling:
def install_claude() -> dict:
"""Install Claude Code CLI."""
return install_provider(
provider="claude",
package="@anthropic-ai/claude-code",
exec_name="claude"
)
# Installation process:
# 1. Install npm package globally using bun
# 2. Bundle with bun compile for fast startup
# 3. Copy to ~/.local/bin
# 4. Handle platform-specific paths
Key features:
- Uses bun for fast npm installs
- Creates bundled executables
- Platform-aware installation paths
- Automatic PATH checking
Configuration System
Hierarchical configuration loading:
- Default values in
Configdataclass - Config files (first found wins):
~/.claif/config.json~/.config/claif/config.json./claif.json
- Environment variables:
CLAIF_DEFAULT_PROVIDERCLAIF_VERBOSECLAIF_CACHE_ENABLEDCLAIF_SESSION_DIR
- CLI arguments (highest priority)
Example configuration:
{
"default_provider": "claude",
"providers": {
"claude": {
"enabled": true,
"model": "claude-3-opus-20240229",
"api_key_env": "ANTHROPIC_API_KEY"
},
"gemini": {
"enabled": true,
"model": "gemini-2.5-pro",
"api_key_env": "GEMINI_API_KEY"
},
"codex": {
"enabled": true,
"model": "o4-mini"
}
},
"cache_enabled": true,
"cache_ttl": 3600,
"output_format": "text"
}
Plugin System
Python's entry points for dynamic provider discovery:
# In src/__init__.py
class PluginFinder:
"""Enables imports like: from claif import claude"""
def find_spec(cls, fullname, path, target=None):
if fullname.startswith("claif."):
# Look up via entry points
eps = metadata.entry_points(group="claif.plugins")
for ep in eps:
if ep.name == plugin_name:
return ModuleSpec(fullname, Loader())
Provider packages register themselves:
# In provider's pyproject.toml
[project.entry-points."claif.plugins"]
claude = "claif_cla"
gemini = "claif_gem"
codex = "claif_cod"
Message Flow
- User Input → CLI command or API call
- Options Parsing → Create
ClaifOptionswith configuration - Provider Selection → Choose provider based on options
- Query Routing → Client routes to appropriate provider wrapper
- CLI Check → Auto-install if CLI missing
- Provider Execution → Provider package handles actual call
- Response Streaming → Yield
Messageobjects as they arrive - Output Formatting → Display with rich or return structured data
Code Structure
claif/
├── src/
│ ├── __init__.py # Plugin system initialization
│ └── claif/
│ ├── __init__.py # Public API exports
│ ├── common/ # Shared components
│ │ ├── __init__.py
│ │ ├── types.py # Core data structures
│ │ ├── config.py # Configuration management
│ │ ├── errors.py # Exception hierarchy
│ │ ├── install.py # CLI installation utilities
│ │ └── utils.py # Formatting and helpers
│ ├── providers/ # Provider wrappers
│ │ ├── __init__.py
│ │ ├── claude.py # Claude wrapper
│ │ ├── gemini.py # Gemini wrapper
│ │ └── codex.py # Codex wrapper
│ ├── client.py # Main client implementation
│ ├── cli.py # Fire CLI interface
│ ├── server.py # FastMCP server
│ └── install.py # Provider CLI installation
├── tests/ # Test suite
├── pyproject.toml # Package configuration
└── README.md # This file
Key Implementation Details
Async Everywhere: All I/O operations use async/await for efficiency
Message Types: Flexible content that can be string or structured blocks:
class Message:
role: MessageRole
content: str | list[TextBlock | ToolUseBlock | ToolResultBlock]
Provider Adapters: Thin wrappers that maintain provider-specific features:
# Each provider wrapper is ~20 lines
# Imports provider package
# Converts options if needed
# Yields messages unchanged
Auto-Install Logic: Missing CLIs trigger automatic installation:
if _is_cli_missing_error(e):
install_func = _get_provider_install_function(provider)
if install_func():
# Retry query with fresh provider instance
Clean Output: Logging configured to minimize noise:
# Verbose OFF: Only errors to stderr
# Verbose ON: Full debug logging
# AI responses always go to stdout
Installation Details: Bun and Node
Claif uses a hybrid approach for installing provider CLIs:
Why Bun?
Bun is used for:
- Fast npm installs - 10x faster than npm
- Binary bundling - Creates standalone executables
- Cross-platform - Works on Windows, macOS, Linux
- Minimal dependencies - Single binary, no Node.js required
Installation Process
# 1. Bun installs the npm package globally
bun add -g @anthropic-ai/claude-code
# 2. Bundle script creates optimized binary
bun build claude-wrapper.ts --compile --outfile dist/claude
# 3. Binary is copied to ~/.local/bin
cp dist/claude ~/.local/bin/
Manual Installation Options
If you prefer Node.js:
# Using npm
npm install -g @anthropic-ai/claude-code
npm install -g @google/gemini-cli
npm install -g @openai/codex
# Using yarn
yarn global add @anthropic-ai/claude-code
# Using pnpm
pnpm add -g @anthropic-ai/claude-code
Bundle Benefits
- Faster startup - No Node.js initialization
- Smaller size - Single file vs node_modules
- No conflicts - Isolated from system Node.js
- Portable - Copy binary anywhere
Troubleshooting
# Check if CLIs are found
claif status
# Manually set CLI paths
export CLAUDE_CLI_PATH=/usr/local/bin/claude
export GEMINI_CLI_PATH=/usr/local/bin/gemini
export CODEX_CLI_PATH=/usr/local/bin/codex
# Skip auto-install and use existing CLIs
claif config set auto_install=false
Why Use Claif?
1. Provider Independence
- Write once, use any AI model
- Switch providers with a single parameter
- Compare responses side-by-side
- No vendor lock-in
2. Zero Friction
- Auto-installs missing CLIs on first use
- Clean output - only show what matters
- Smart defaults that just work
- Async for speed
3. Developer Experience
- Full type hints and IDE support
- Rich CLI with progress indicators
- Comprehensive error messages
- Fast MCP server for tool integration
4. Production Ready
- Battle-tested error handling
- Timeout protection
- Response caching (coming soon)
- Configurable everything
5. Extensible
- Plugin architecture for new providers
- Clean API for custom integrations
- Well-documented codebase
- Active development
Contributing
Based on the development guide in CLAUDE.md:
Development Setup
# Clone the repository
git clone https://github.com/twardoch/claif.git
cd claif
# Install with dev dependencies
pip install -e ".[dev,test]"
# Install pre-commit hooks
pre-commit install
Running Tests
The Claif framework includes comprehensive tests to ensure robust functionality:
# Install with test dependencies
pip install -e ".[test]"
# Run all tests
uvx hatch test
# Run specific test modules
uvx hatch test -- tests/test_functional_simple.py -v
uvx hatch test -- tests/test_client.py -v
# Run with coverage
uvx hatch test -- --cov=src/claif --cov-report=html
Test Structure
tests/
├── test_functional_simple.py # Simple functional tests (no provider dependencies)
├── test_client.py # Core client functionality tests
├── test_providers.py # Provider discovery and loading tests
├── test_config.py # Configuration management tests
└── conftest.py # Test fixtures and configuration
Example Test Usage
The functional tests demonstrate the unified client interface:
# Test provider auto-detection
def test_provider_auto_detection():
# Auto-detects Claude if ANTHROPIC_API_KEY is set
with patch.dict(os.environ, {"ANTHROPIC_API_KEY": "test-key"}):
client = ClaifClient()
assert client.provider == "claude"
# Test unified API across providers
def test_unified_chat_api():
client = ClaifClient(provider="claude")
response = client.chat.completions.create(
model="claude-3-5-sonnet-20241022",
messages=[{"role": "user", "content": "Hello"}]
)
assert isinstance(response, ChatCompletion)
assert response.choices[0].message.role == "assistant"
# Test provider delegation
def test_provider_delegation():
with patch("claif_cla.client.ClaudeClient") as mock_claude:
mock_client = MagicMock()
mock_claude.return_value = mock_client
client = ClaifClient(provider="claude")
client.chat.completions.create(
model="claude-3-5-sonnet-20241022",
messages=[{"role": "user", "content": "Test"}]
)
# Verify delegation to provider client
mock_client.chat.completions.create.assert_called_once()
# Test streaming support
def test_streaming():
client = ClaifClient(provider="claude")
stream = client.chat.completions.create(
model="claude-3-5-sonnet-20241022",
messages=[{"role": "user", "content": "Count to 3"}],
stream=True
)
chunks = list(stream)
assert len(chunks) > 0
assert all(isinstance(chunk, ChatCompletionChunk) for chunk in chunks)
Testing Provider Packages
Each provider package (claif_cla, claif_gem, claif_cod) includes its own comprehensive test suite. The main claif package includes simple functional tests that don't require provider dependencies for CI/CD compatibility.
For full integration testing with providers:
# Install all providers
pip install claif[all]
# Run provider-specific tests
cd claif_cla && uvx hatch test
cd claif_gem && uvx hatch test
cd claif_cod && uvx hatch test
# Run integration tests
uvx hatch test -- tests/integration/ -v
Code Quality
# Format code
ruff format src tests
# Lint code
ruff check src tests --fix
# Type checking
mypy src/claif
# Run all checks (as per CLAUDE.md)
fd -e py -x ruff format {}
fd -e py -x ruff check --fix --unsafe-fixes {}
python -m pytest
Making Changes
- Check existing utilities in
claif.commonbefore implementing - Maintain API compatibility - this is a plugin framework
- Add tests for new functionality
- Update documentation in docstrings and README
- Follow style - 120 char lines, descriptive names
Release Process
# Update version (uses hatch-vcs)
git tag v1.0.7
# Build distribution
python -m build
# Upload to PyPI
twine upload dist/*
License
MIT License - see LICENSE file for details.
Copyright (c) 2025 Adam Twardoch
Links
Claif Ecosystem
Core Framework:
- GitHub: twardoch/claif - This repository
- PyPI: claif - Core package
- Documentation - Full docs
- Issues - Bug reports & features
Provider Packages:
- claif_cla - Claude provider (wraps claude-code-sdk)
- claif_gem - Gemini provider (wraps Gemini CLI)
- claif_cod - Codex provider (wraps OpenAI Codex CLI)
Upstream Projects
Provider CLIs:
- Claude Code - Anthropic's official CLI
- claude-code-sdk - Python SDK for Claude
- Gemini CLI - Google's Gemini CLI
- OpenAI Codex CLI - OpenAI's code generation CLI
Related Tools:
- Fire - Python CLI framework
- Rich - Terminal formatting
- FastMCP - MCP server framework
- Bun - Fast JavaScript runtime used for bundling
Resources
Documentation:
- Anthropic Docs - Claude documentation
- Google AI Studio - Gemini documentation
- OpenAI Platform - OpenAI documentation
Community:
- Discussions - Q&A and ideas
- Twitter: @adamtwar - Author updates
Articles & Tutorials:
- Building a Unified AI CLI - Design decisions
- Plugin Architecture in Python - Technical deep dive
- Async Patterns for AI APIs - Performance guide
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file claif-1.0.33.tar.gz.
File metadata
- Download URL: claif-1.0.33.tar.gz
- Upload date:
- Size: 52.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: python-httpx/0.28.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5d1d989923b96111dc299e7d9d0ec82f6772e2499b298b6cd6857dff846bfeea
|
|
| MD5 |
612b9b5d482cb166fb3287d9f5a2be00
|
|
| BLAKE2b-256 |
f43a4e2e891bcc970a8d193e19ea1a0e374ceb6e2435eee31c414fe2f0353991
|
File details
Details for the file claif-1.0.33-py3-none-any.whl.
File metadata
- Download URL: claif-1.0.33-py3-none-any.whl
- Upload date:
- Size: 16.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: python-httpx/0.28.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c40eb0da885fdba66f0f1910d815784880dc963b506e5c3d17a3eda0c722b07f
|
|
| MD5 |
9b9828f4fda3d22f2e11097b983ebc9e
|
|
| BLAKE2b-256 |
a252b186080fcd000c800f6f279bde507ca53044335d0b3c60505dd6851ca668
|