A Claif provider for Anthropic Claude, compatible with the OpenAI Responses API.
Project description
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:
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_cla-1.0.31.tar.gz.
File metadata
- Download URL: claif_cla-1.0.31.tar.gz
- Upload date:
- Size: 56.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: python-httpx/0.28.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5028578e4cdd0eb3a2ffdbe5cf1d866927e9ab062cd0e4332fe6ed2e084a6334
|
|
| MD5 |
86f52767506d53b99bab8644aeaa085e
|
|
| BLAKE2b-256 |
b56be0b8c77a0eb000cdc86400304836e597946b6ec6f56076a48d38364ca8a0
|
File details
Details for the file claif_cla-1.0.31-py3-none-any.whl.
File metadata
- Download URL: claif_cla-1.0.31-py3-none-any.whl
- Upload date:
- Size: 15.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: python-httpx/0.28.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d8d58342baa57d8becb396cb4fd8ee7ee91f0cee85349f2395b8dfab9ecc7173
|
|
| MD5 |
47093ee5e70504f3430307f50a6d3ed4
|
|
| BLAKE2b-256 |
7c14779f788a3c52c8a4a9e8ee96bf4c4fb6b6505f3064c67899c1d3ad1933b1
|