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
Release files for claif 1.0.33
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| claif-1.0.33.tar.gz | 52.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| claif-1.0.33-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 69.2 kB
Release files / claif-1.0.33.tar.gz
| Download URL | claif-1.0.33.tar.gz |
|---|---|
| Size | 52.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5d1d989923b96111dc299e7d9d0ec82f6772e2499b298b6cd6857dff846bfeea
|
|
BLAKE2b-256 checksum How to use checksums |
f43a4e2e891bcc970a8d193e19ea1a0e374ceb6e2435eee31c414fe2f0353991
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
python-httpx/0.28.1
|
Release files / claif-1.0.33-py3-none-any.whl
| Download URL | claif-1.0.33-py3-none-any.whl |
|---|---|
| Size | 16.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c40eb0da885fdba66f0f1910d815784880dc963b506e5c3d17a3eda0c722b07f
|
|
BLAKE2b-256 checksum How to use checksums |
a252b186080fcd000c800f6f279bde507ca53044335d0b3c60505dd6851ca668
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
python-httpx/0.28.1
|