claif_cla - Claude Provider for Claif
A Claif provider for Anthropic's Claude with full OpenAI client API compatibility. This package wraps the claude-code-sdk to provide a consistent interface following the client.chat.completions.create() pattern.
Features
- OpenAI Client API Compatible: Use the familiar
client.chat.completions.create()pattern - Full Type Safety: Returns standard
ChatCompletionandChatCompletionChunkobjects - Streaming Support: Real-time streaming with proper chunk handling
- Session Management: Persistent conversation history with atomic operations
- Tool Approval: Fine-grained control over MCP tool usage
- Response Caching: Intelligent caching to reduce API costs
- Fire-based CLI: Rich terminal interface with multiple output formats
Quickstart
# Install
pip install claif_cla
# Basic usage - OpenAI compatible
python -c "
from claif_cla import ClaudeClient
client = ClaudeClient()
response = client.chat.completions.create(
messages=[{'role': 'user', 'content': 'Hello Claude!'}],
model='claude-3-5-sonnet-20241022'
)
print(response.choices[0].message.content)
"
# CLI usage
claif-cla query "Explain quantum computing"
claif-cla chat --model claude-3-opus-20240229
What is claif_cla?
claif_cla is a Python wrapper that integrates Anthropic's Claude into the Claif framework with full OpenAI client API compatibility. It provides a thin layer over the claude_code_sdk package, adding session management, tool approval strategies, and response caching while maintaining full compatibility with Claude's capabilities.
Key Features:
- Session persistence - Save and restore conversations across sessions
- Tool approval strategies - Fine-grained control over MCP tool usage
- Response caching - Reduce API costs with intelligent caching
- Rich CLI - Beautiful terminal interface with Fire framework
- Async support - Full async/await for efficient operations
- Type safety - Comprehensive type hints throughout
Installation
Basic Installation
# Core package only
pip install claif_cla
# With Claif framework
pip install claif claif_cla
Installing Claude CLI
The Claude CLI can be installed automatically:
# Using claif_cla installer
python -m claif_cla.install
# Or manually via npm
npm install -g @anthropic-ai/claude-code
# Or using Claif's installer
pip install claif && claif install claude
Development Installation
git clone https://github.com/twardoch/claif_cla.git
cd claif_cla
pip install -e ".[dev,test]"
Usage
Basic Usage (OpenAI-Compatible)
from claif_cla import ClaudeClient
# Initialize the client
client = ClaudeClient(
api_key="your-api-key" # Optional, uses ANTHROPIC_API_KEY env var
)
# Create a chat completion - exactly like OpenAI
response = client.chat.completions.create(
model="claude-3-5-sonnet-20241022",
messages=[
{"role": "system", "content": "You are a helpful assistant"},
{"role": "user", "content": "Explain machine learning"}
],
temperature=0.7,
max_tokens=1000
)
# Access the response
print(response.choices[0].message.content)
print(f"Model: {response.model}")
print(f"Usage: {response.usage}")
Streaming Responses
from claif_cla import ClaudeClient
client = ClaudeClient()
# Stream responses in real-time
stream = client.chat.completions.create(
model="claude-3-5-sonnet-20241022",
messages=[
{"role": "user", "content": "Write a haiku about programming"}
],
stream=True
)
# Process streaming chunks
for chunk in stream:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
CLI Usage
# Basic query
claif-cla query "What is the theory of relativity?"
# With specific model
claif-cla query "Explain Python decorators" --model claude-3-opus-20240229
# Interactive chat mode
claif-cla chat --model claude-3-5-sonnet-20241022
# List available models
claif-cla models
# JSON output
claif-cla query "Hello" --json-output
Session Management
# List all sessions
python -m claif_cla.cli session list
# Create new session with optional metadata
python -m claif_cla.cli session create --metadata '{"project": "my-app"}'
# Show session messages
python -m claif_cla.cli session show SESSION_ID
# Continue existing session
python -m claif_cla.cli ask "Continue our discussion" --session SESSION_ID
# Export session to file
python -m claif_cla.cli session export SESSION_ID --format markdown --output chat.md
python -m claif_cla.cli session export SESSION_ID --format json --output chat.json
# Delete session
python -m claif_cla.cli session delete SESSION_ID
Advanced Features
# Benchmark response time
python -m claif_cla.cli benchmark "Complex analysis task" --iterations 5
# Set approval strategy for tools
python -m claif_cla.cli ask "Analyze this file" --approval allow_list --allowed-tools "read_file,search"
# Use with caching
python -m claif_cla.cli ask "Expensive computation" --cache --cache-ttl 3600
# Verbose mode for debugging
python -m claif_cla.cli ask "Debug this" --verbose
Testing
The claif_cla package includes comprehensive tests to ensure robust functionality:
Running Tests
# Install with test dependencies
pip install -e ".[test]"
# Run all tests
uvx hatch test
# Run specific test modules
uvx hatch test -- tests/test_functional.py -v
uvx hatch test -- tests/test_client.py -v
# Run with coverage
uvx hatch test -- --cov=src/claif_cla --cov-report=html
Test Structure
tests/
├── test_functional.py # End-to-end functionality tests
├── test_client.py # Client API tests
├── test_session.py # Session management tests
├── test_approval.py # Tool approval strategy tests
├── test_wrapper.py # Caching and retry tests
└── conftest.py # Test fixtures and configuration
Example Test Usage
The functional tests demonstrate how to use claif_cla effectively:
# Test basic query functionality
def test_basic_query():
client = ClaudeClient(api_key="test-key")
response = client.chat.completions.create(
model="claude-3-5-sonnet-20241022",
messages=[{"role": "user", "content": "Hello Claude"}]
)
assert isinstance(response, ChatCompletion)
assert response.choices[0].message.role == "assistant"
assert len(response.choices[0].message.content) > 0
# Test streaming responses
def test_streaming():
client = ClaudeClient()
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
# Reconstruct full message
content = "".join(
chunk.choices[0].delta.content or ""
for chunk in chunks
if chunk.choices and chunk.choices[0].delta.content
)
assert len(content) > 0
# Test parameter passing
def test_with_parameters():
client = ClaudeClient()
response = client.chat.completions.create(
model="claude-3-5-sonnet-20241022",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Write a hello world function"}
],
temperature=0.7,
max_tokens=100
)
assert response.model == "claude-3-5-sonnet-20241022"
assert response.usage.total_tokens > 0
Mock Testing for CI/CD
The tests use comprehensive mocking to work in CI environments without requiring actual API keys:
from unittest.mock import patch, MagicMock
@patch("claif_cla.client.ClaudeCodeClient")
def test_client_initialization(mock_client_class):
mock_client = MagicMock()
mock_client_class.return_value = mock_client
client = ClaudeClient(api_key="test-key", timeout=300)
# Verify proper initialization
mock_client_class.assert_called_once_with(
api_key="test-key",
timeout=300
)
API Compatibility
This package is fully compatible with the OpenAI Python client API:
# You can use it as a drop-in replacement
from claif_cla import ClaudeClient as OpenAI
client = OpenAI()
# Now use exactly like the OpenAI client
response = client.chat.completions.create(
model="claude-3-5-sonnet-20241022",
messages=[{"role": "user", "content": "Hello!"}]
)
Migration from Old Async API
If you were using the old async-based Claif API:
# Old API (deprecated)
import asyncio
from claif_cla import query
from claif.common import ClaifOptions
async def old_way():
async for message in query("Hello, Claude!"):
print(f"{message.role}: {message.content}")
# New API (OpenAI-compatible)
from claif_cla import ClaudeClient
def new_way():
client = ClaudeClient()
response = client.chat.completions.create(
messages=[{"role": "user", "content": "Hello, Claude!"}],
model="claude-3-5-sonnet-20241022"
)
print(response.choices[0].message.content)
Key Changes
- Synchronous by default: No more
async/awaitfor basic usage - OpenAI-compatible structure:
client.chat.completions.create()pattern - Standard message format:
[{"role": "user", "content": "..."}] - Streaming support: Use
stream=Truefor real-time responses - Type-safe responses: Returns
ChatCompletionobjects from OpenAI types
Session Management
from claif_cla.session import SessionManager, Session
from claif.common import Message, MessageRole
# Initialize session manager
session_mgr = SessionManager()
# Create new session
session_id = session_mgr.create_session(
metadata={"project": "my-app", "user": "john"}
)
# Add messages to session
user_msg = Message(
role=MessageRole.USER,
content="What is machine learning?"
)
session_mgr.add_message(session_id, user_msg)
# Get session history
session = session_mgr.get_session(session_id)
for msg in session.messages:
print(f"{msg.role}: {msg.content}")
# Export session
markdown_export = session_mgr.export_session(session_id, export_format="markdown")
print(markdown_export)
# Save session to disk
session_mgr.save_session(session_id)
Tool Approval Strategies
from claif_cla.approval import create_approval_strategy
# Strategy 1: Allow specific tools only
safe_tools = create_approval_strategy("allow_list", {
"allowed_tools": ["read_file", "list_files", "search"]
})
# Strategy 2: Deny dangerous tools
deny_dangerous = create_approval_strategy("deny_list", {
"denied_tools": ["delete_file", "execute_command", "write_file"]
})
# Strategy 3: Pattern-based approval
patterns = create_approval_strategy("pattern", {
"patterns": ["read_.*", "list_.*", "search_.*"],
"deny": False # Allow matching patterns
})
# Strategy 4: Threshold-based (by risk score)
threshold = create_approval_strategy("threshold", {
"max_risk": 3 # Only allow tools with risk <= 3
})
# Use strategy in query
from claif_cla import ClaudeCodeOptions
options = ClaudeCodeOptions(
tool_approval_strategy=safe_tools,
model="claude-3-opus-20240229"
)
Response Caching
from claif_cla.wrapper import ClaudeWrapper, ResponseCache
# Create wrapper with caching
wrapper = ClaudeWrapper(
cache_ttl=3600, # 1 hour cache
enable_cache=True
)
# First call - hits API
response1 = await wrapper.query("Expensive analysis", options)
# Second call - returns from cache
response2 = await wrapper.query("Expensive analysis", options)
# Clear cache if needed
wrapper.cache.clear()
Using with Claif Framework
from claif import query as claif_query, Provider, ClaifOptions
# Query through Claif framework
options = ClaifOptions(
provider=Provider.CLAUDE,
model="claude-3-opus-20240229",
temperature=0.5
)
async for message in claif_query("Hello from Claif!", options):
print(message.content)
How It Works
Architecture Overview
┌─────────────────────────────┐
│ User Application │
├─────────────────────────────┤
│ claif_cla CLI │ ← Fire-based CLI with rich output
├─────────────────────────────┤
│ claif_cla Core │ ← Main query function & types
├─────────────────────────────┤
│ Session Manager │ ← Conversation persistence
├─────────────────────────────┤
│ Approval Strategies │ ← Tool usage control
├─────────────────────────────┤
│ Response Cache │ ← Cost optimization
├─────────────────────────────┤
│ claude_code_sdk │ ← Anthropic's SDK
└─────────────────────────────┘
Core Components
Main Module (__init__.py)
The entry point that provides the query function:
async def query(
prompt: str,
options: ClaifOptions | None = None
) -> AsyncIterator[Message]:
"""Query Claude with Claif-compatible interface."""
# Convert Claif options to Claude options
claude_options = _convert_options(options)
# Delegate to claude_code_sdk
async for message in claude_query(prompt, claude_options):
yield message
Key features:
- Thin wrapper design for minimal overhead
- Option conversion between Claif and Claude formats
- Direct message passthrough
- Loguru-based debug logging
CLI Module (cli.py)
Fire-based command-line interface with rich formatting:
class ClaudeCLI:
def ask(self, prompt: str, **kwargs):
"""Ask Claude a question."""
def stream(self, prompt: str, **kwargs):
"""Stream responses in real-time."""
def interactive(self):
"""Start interactive chat session."""
def session(self, action: str, **kwargs):
"""Manage conversation sessions."""
Commands:
ask- Single query with optionsstream- Real-time streaming responsesinteractive- Chat mode with historysession- CRUD operations for conversationshealth- Check Claude availabilitybenchmark- Performance testing
Session Module (session.py)
Persistent conversation management:
@dataclass
class Session:
id: str
messages: list[Message]
metadata: dict[str, Any]
created_at: datetime
updated_at: datetime
class SessionManager:
def __init__(self, session_dir: Path | None = None):
self.session_dir = session_dir or Path.home() / ".claif" / "sessions"
Features:
- JSON-based session storage
- Atomic file operations
- Session templates (coding, analysis, creative)
- Export to Markdown/JSON formats
- Metadata for organization
Approval Module (approval.py)
Fine-grained control over MCP tool usage:
class ApprovalStrategy(ABC):
@abstractmethod
def should_approve(self, tool_name: str, tool_metadata: dict) -> bool:
"""Decide if tool should be approved."""
# Eight concrete strategies:
1. AllowAllStrategy - Approve everything
2. DenyAllStrategy - Deny everything
3. AllowListStrategy - Only allow specific tools
4. DenyListStrategy - Deny specific tools
5. PatternStrategy - Regex-based approval
6. ThresholdStrategy - Risk score based
7. CategoryStrategy - Approve by category
8. CompositeStrategy - Combine multiple strategies
Factory function for easy creation:
strategy = create_approval_strategy("allow_list", {
"allowed_tools": ["read_file", "search"]
})
Wrapper Module (wrapper.py)
Enhanced functionality around claude_code_sdk:
class ResponseCache:
"""SHA256-based response caching."""
def get_cache_key(self, prompt: str, options: dict) -> str:
data = json.dumps({"prompt": prompt, "options": options})
return hashlib.sha256(data.encode()).hexdigest()
class ClaudeWrapper:
"""Adds retry logic and caching."""
async def query_with_retry(self, prompt: str, options, max_retries=3):
for attempt in range(max_retries):
try:
return await self._query(prompt, options)
except Exception as e:
if attempt == max_retries - 1:
raise
await asyncio.sleep(2 ** attempt)
Features:
- SHA256-based cache keys
- TTL support for cache entries
- Exponential backoff retry logic
- Graceful error handling
Code Structure
claif_cla/
├── src/claif_cla/
│ ├── __init__.py # Main query function and exports
│ ├── cli.py # Fire-based CLI interface
│ ├── wrapper.py # Caching and retry logic
│ ├── session.py # Session management
│ ├── approval.py # Tool approval strategies
│ └── install.py # CLI installation helper
├── tests/
│ ├── test_session.py # Session tests
│ ├── test_approval.py # Strategy tests
│ └── test_wrapper.py # Cache tests
├── pyproject.toml # Package configuration
├── README.md # This file
└── CLAUDE.md # Development guide
Message Flow
- User Input → CLI command or API call
- Option Conversion → ClaifOptions → ClaudeCodeOptions
- Session Check → Load existing session if specified
- Cache Lookup → Check for cached response
- SDK Call → Forward to claude_code_sdk
- Tool Approval → Apply strategy if tools requested
- Response Stream → Yield messages as they arrive
- Cache Storage → Store response if caching enabled
- Session Update → Save messages to session
- Output Format → Display with rich formatting
Configuration
Environment variables:
ANTHROPIC_API_KEY- Required for Claude APICLAIF_SESSION_DIR- Custom session directoryCLAIF_CACHE_TTL- Default cache durationCLAIF_DEFAULT_MODEL- Default Claude model
Config file (~/.claif/config.json):
{
"providers": {
"claude": {
"model": "claude-3-opus-20240229",
"api_key_env": "ANTHROPIC_API_KEY",
"cache_ttl": 3600,
"enable_cache": true
}
}
}
Installation with Bun
claif_cla includes an installer that uses Bun for fast installation:
# install.py
def install_claude():
"""Install Claude Code CLI using bun."""
# 1. Ensure bun is installed
# 2. Install @anthropic-ai/claude-code globally
# 3. Bundle with bun compile
# 4. Copy to ~/.local/bin
Benefits:
- 10x faster than npm
- Creates standalone executables
- No Node.js conflicts
- Cross-platform support
Why Use claif_cla?
1. Minimal Overhead
- Thin wrapper adds < 100ms latency
- Direct SDK passthrough
- No unnecessary abstractions
2. Session Persistence
- Continue conversations across runs
- Export chat history
- Organize with metadata
3. Tool Control
- Eight approval strategies
- Combine strategies
- Custom implementations
4. Cost Optimization
- Response caching
- Retry logic
- Request deduplication
5. Developer Experience
- Type hints everywhere
- Rich CLI output
- Comprehensive logging
- Easy integration
Contributing
See CLAUDE.md for development guidelines.
Development Setup
# Clone repository
git clone https://github.com/twardoch/claif_cla.git
cd claif_cla
# Install with dev dependencies
pip install -e ".[dev,test]"
# Run tests
pytest
# Code quality
ruff format src/claif_cla tests
ruff check src/claif_cla tests
mypy src/claif_cla
Testing
# Unit tests
pytest tests/test_session.py -v
pytest tests/test_approval.py -v
# Integration tests
pytest tests/test_integration.py -v
# Coverage report
pytest --cov=src/claif_cla --cov-report=html
License
MIT License - see LICENSE file for details.
Copyright (c) 2025 Adam Twardoch
Links
claif_cla Resources
- GitHub Repository - Source code
- PyPI Package - Latest release
- Issue Tracker - Bug reports
- Discussions - Q&A
Related Projects
Claif Ecosystem:
Upstream Projects:
- Claude Code - Anthropic's CLI
- claude-code-sdk - Python SDK
- Anthropic API - API documentation
Tools & Libraries:
Release files for claif_cla 1.0.31
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_cla-1.0.31.tar.gz | 56.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| claif_cla-1.0.31-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 72.3 kB
Release files / claif_cla-1.0.31.tar.gz
| Download URL | claif_cla-1.0.31.tar.gz |
|---|---|
| Size | 56.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5028578e4cdd0eb3a2ffdbe5cf1d866927e9ab062cd0e4332fe6ed2e084a6334
|
|
BLAKE2b-256 checksum How to use checksums |
b56be0b8c77a0eb000cdc86400304836e597946b6ec6f56076a48d38364ca8a0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
python-httpx/0.28.1
|
Release files / claif_cla-1.0.31-py3-none-any.whl
| Download URL | claif_cla-1.0.31-py3-none-any.whl |
|---|---|
| Size | 15.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d8d58342baa57d8becb396cb4fd8ee7ee91f0cee85349f2395b8dfab9ecc7173
|
|
BLAKE2b-256 checksum How to use checksums |
7c14779f788a3c52c8a4a9e8ee96bf4c4fb6b6505f3064c67899c1d3ad1933b1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
python-httpx/0.28.1
|