claif


Nameclaif JSON
Version 1.0.32 PyPI version JSON
download
home_pageNone
SummaryA unified CLI and Python library for interacting with local and remote LLMs, compatible with the OpenAI Responses API.
upload_time2025-07-29 01:49:45
maintainerNone
docs_urlNone
authorNone
requires_python>=3.10
licenseMIT
keywords ai artificial-intelligence claif claude cli codex command-line gemini llm lm-studio openai responses-api
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage
            # Claif - Command Line Artificial Intelligence Framework

## Quickstart

```bash
# 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

```bash
# Core framework only
pip install claif

# With all providers (recommended)
pip install claif[all]
```

### Installing Specific Providers

```bash
# 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:

```bash
# 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

```bash
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

```bash
# 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

```bash
# 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

```bash
# 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

```bash
# 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

```python
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

```python
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

```python
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

```python
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:
```python
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 exception
- `ProviderError` → Provider-specific failures
- `ConfigurationError` → Configuration issues  
- `TransportError` → Communication errors
- `TimeoutError` → Operation timeouts

**install.py** - Auto-installation support:
```python
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:

```python
# 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 `query` interface
- Handles provider-specific option conversion

#### Client Module (`src/claif/client.py`)

The main client with auto-install support:

```python
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`, and `query_all`
- Handles errors with graceful fallbacks

#### CLI Module (`src/claif/cli.py`)

Fire-based CLI with rich terminal output:

```python
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 options
- `stream` - Live streaming with rich.Live display
- `random` - Query random provider
- `parallel` - Query all providers (with `--compare` for side-by-side)
- `session` - Start provider-specific interactive session
- `install` - Install provider CLIs with bun bundling
- `config` - Manage settings
- `server` - Start MCP server

#### Server Module (`src/claif/server.py`)

FastMCP server implementation:

```python
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:

```python
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:

1. **Default values** in `Config` dataclass
2. **Config files** (first found wins):
   - `~/.claif/config.json`
   - `~/.config/claif/config.json`
   - `./claif.json`
3. **Environment variables**: 
   - `CLAIF_DEFAULT_PROVIDER`
   - `CLAIF_VERBOSE`
   - `CLAIF_CACHE_ENABLED`
   - `CLAIF_SESSION_DIR`
4. **CLI arguments** (highest priority)

Example configuration:

```json
{
    "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:

```python
# 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:

```toml
# In provider's pyproject.toml
[project.entry-points."claif.plugins"]
claude = "claif_cla"
gemini = "claif_gem" 
codex = "claif_cod"
```

### Message Flow

1. **User Input** → CLI command or API call
2. **Options Parsing** → Create `ClaifOptions` with configuration
3. **Provider Selection** → Choose provider based on options
4. **Query Routing** → Client routes to appropriate provider wrapper
5. **CLI Check** → Auto-install if CLI missing
6. **Provider Execution** → Provider package handles actual call
7. **Response Streaming** → Yield `Message` objects as they arrive
8. **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:
```python
class Message:
    role: MessageRole
    content: str | list[TextBlock | ToolUseBlock | ToolResultBlock]
```

**Provider Adapters**: Thin wrappers that maintain provider-specific features:
```python
# Each provider wrapper is ~20 lines
# Imports provider package
# Converts options if needed  
# Yields messages unchanged
```

**Auto-Install Logic**: Missing CLIs trigger automatic installation:
```python
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:
```python
# 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](https://bun.sh) is used for:
1. **Fast npm installs** - 10x faster than npm
2. **Binary bundling** - Creates standalone executables
3. **Cross-platform** - Works on Windows, macOS, Linux
4. **Minimal dependencies** - Single binary, no Node.js required

### Installation Process

```bash
# 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:
```bash
# 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

1. **Faster startup** - No Node.js initialization
2. **Smaller size** - Single file vs node_modules
3. **No conflicts** - Isolated from system Node.js
4. **Portable** - Copy binary anywhere

### Troubleshooting

```bash
# 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](CLAUDE.md):

### Development Setup

```bash
# 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

```bash
# Run all tests
pytest

# Run with coverage
pytest --cov=src/claif --cov-report=html

# Run specific test
pytest tests/test_client.py -v

# Run integration tests (requires provider packages)
pytest tests/integration/ -v
```

### Code Quality

```bash
# 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

1. **Check existing utilities** in `claif.common` before implementing
2. **Maintain API compatibility** - this is a plugin framework
3. **Add tests** for new functionality
4. **Update documentation** in docstrings and README
5. **Follow style** - 120 char lines, descriptive names

### Release Process

```bash
# 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](LICENSE) file for details.

Copyright (c) 2025 Adam Twardoch

## Links

### Claif Ecosystem

**Core Framework:**
- [GitHub: twardoch/claif](https://github.com/twardoch/claif) - This repository
- [PyPI: claif](https://pypi.org/project/claif/) - Core package
- [Documentation](https://github.com/twardoch/claif#readme) - Full docs
- [Issues](https://github.com/twardoch/claif/issues) - Bug reports & features

**Provider Packages:**
- [claif_cla](https://github.com/twardoch/claif_cla/) - Claude provider (wraps claude-code-sdk)
- [claif_gem](https://github.com/twardoch/claif_gem/) - Gemini provider (wraps Gemini CLI)  
- [claif_cod](https://github.com/twardoch/claif_cod/) - Codex provider (wraps OpenAI Codex CLI)

### Upstream Projects

**Provider CLIs:**
- [Claude Code](https://github.com/anthropics/claude-code) - Anthropic's official CLI
- [claude-code-sdk](https://github.com/anthropics/claude-code-sdk-python) - Python SDK for Claude
- [Gemini CLI](https://github.com/google-gemini/gemini-cli/) - Google's Gemini CLI
- [OpenAI Codex CLI](https://github.com/openai/codex) - OpenAI's code generation CLI

**Related Tools:**
- [Fire](https://github.com/google/python-fire) - Python CLI framework
- [Rich](https://github.com/Textualize/rich) - Terminal formatting
- [FastMCP](https://github.com/fixie-ai/fastmcp) - MCP server framework
- [Bun](https://bun.sh) - Fast JavaScript runtime used for bundling

### Resources

**Documentation:**
- [Anthropic Docs](https://docs.anthropic.com/) - Claude documentation
- [Google AI Studio](https://ai.google.dev/) - Gemini documentation
- [OpenAI Platform](https://platform.openai.com/) - OpenAI documentation

**Community:**
- [Discussions](https://github.com/twardoch/claif/discussions) - Q&A and ideas
- [Twitter: @adamtwar](https://twitter.com/adamtwar) - Author updates

**Articles & Tutorials:**
- [Building a Unified AI CLI](https://example.com) - Design decisions
- [Plugin Architecture in Python](https://example.com) - Technical deep dive
- [Async Patterns for AI APIs](https://example.com) - Performance guide
            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "claif",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": null,
    "keywords": "ai, artificial-intelligence, claif, claude, cli, codex, command-line, gemini, llm, lm-studio, openai, responses-api",
    "author": null,
    "author_email": "Adam Twardoch <adam+github@twardoch.com>",
    "download_url": "https://files.pythonhosted.org/packages/66/eb/602e049cdf1dcd821dc639ad2c0d942b7d8f8ce669652bdadca0d953bded/claif-1.0.32.tar.gz",
    "platform": null,
    "description": "# Claif - Command Line Artificial Intelligence Framework\n\n## Quickstart\n\n```bash\n# Install with all providers and query Claude\npip install claif[all]\nclaif query \"Explain quantum computing in one sentence\"\n\n# Or install specific providers\npip install claif claif_cla  # Just Claude\npip install claif claif_gem  # Just Gemini  \npip install claif claif_cod  # Just Codex/OpenAI\n\n# Stream responses in real-time\nclaif stream \"Tell me a story\" --provider gemini\n\n# Query all providers in parallel\nclaif parallel \"What is the meaning of life?\" --compare\n```\n\n## What is Claif?\n\nClaif 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.\n\n**Key Features:**\n- **Single API** for all AI providers - write once, use any model\n- **Plugin-based architecture** - providers are discovered dynamically  \n- **Rich CLI** with Fire framework and beautiful terminal output\n- **Full async support** for concurrent operations\n- **Type-safe** with comprehensive hints throughout\n- **MCP server** for tool integration\n- **Auto-install** - missing provider CLIs are installed on first use\n\n## Installation\n\n### Basic Installation\n\n```bash\n# Core framework only\npip install claif\n\n# With all providers (recommended)\npip install claif[all]\n```\n\n### Installing Specific Providers\n\n```bash\n# Claude provider (wraps Claude Code SDK)\npip install claif claif_cla\n\n# Gemini provider (wraps Gemini CLI)  \npip install claif claif_gem\n\n# Codex provider (wraps OpenAI Codex CLI)\npip install claif claif_cod\n```\n\n### Installing Provider CLIs\n\nClaif can auto-install missing CLIs, or you can install them manually:\n\n```bash\n# Auto-install all CLIs\nclaif install\n\n# Or install specific CLIs\nclaif install claude\nclaif install gemini\nclaif install codex\n\n# Install via npm (if preferred)\nnpm install -g @anthropic-ai/claude-code\nnpm install -g @google/gemini-cli\nnpm install -g @openai/codex\n```\n\n### Development Installation\n\n```bash\ngit clone https://github.com/twardoch/claif.git\ncd claif\npip install -e \".[dev,test]\"\n```\n\n## CLI Usage\n\nClaif provides a comprehensive command-line interface built with Fire that shows clean, focused output by default. Use `--verbose` for detailed logging.\n\n### Basic Commands\n\n```bash\n# Query default provider (Claude)\nclaif query \"What is Python?\"\n\n# Query specific provider\nclaif query \"Explain recursion\" --provider gemini\n\n# Query with options\nclaif query \"Write a haiku about coding\" --temperature 0.7 --max-tokens 50\n\n# Stream responses in real-time with live display\nclaif stream \"Tell me a story\" --provider claude\n\n# Query a random provider\nclaif random \"Tell me a programming joke\"\n\n# Query all providers in parallel\nclaif parallel \"What is AI?\" --compare  # Side-by-side comparison\n```\n\n### Provider Management\n\n```bash\n# List providers with their status\nclaif providers list\n\n# Check health of all providers\nclaif providers status\n\n# Install provider CLIs\nclaif install         # All providers\nclaif install claude  # Specific provider\n\n# Check installation status\nclaif status\n```\n\n### Configuration\n\n```bash\n# Show current configuration\nclaif config show\n\n# Set configuration values\nclaif config set default_provider=gemini\nclaif config set cache_enabled=true\nclaif config set output_format=markdown\n\n# Save configuration to file\nclaif config save\n```\n\n### MCP Server\n\n```bash\n# Start the MCP (Model Context Protocol) server\nclaif server --host localhost --port 8000 --reload\n\n# The server provides tools:\n# - claif_query: Query specific provider\n# - claif_query_random: Query random provider  \n# - claif_query_all: Query all providers\n# - claif_list_providers: List available providers\n# - claif_health_check: Check provider health\n```\n\n## Python API Usage\n\n### Basic Usage\n\n```python\nimport asyncio\nfrom claif import query, ClaifOptions, Provider\n\nasync def main():\n    # Simple query using default provider\n    async for message in query(\"Hello, world!\"):\n        print(message.content)\n    \n    # Query specific provider  \n    options = ClaifOptions(provider=Provider.GEMINI)\n    async for message in query(\"Explain Python decorators\", options):\n        print(f\"[{message.role}]: {message.content}\")\n\nasyncio.run(main())\n```\n\n### Advanced Options\n\n```python\nfrom claif import query, ClaifOptions, Provider\n\noptions = ClaifOptions(\n    provider=Provider.CLAUDE,\n    model=\"claude-3-opus-20240229\",\n    temperature=0.7,\n    max_tokens=1000,\n    system_prompt=\"You are an expert Python developer\",\n    timeout=60,\n    cache=True,\n    verbose=False  # Clean output by default\n)\n\nasync def get_code_review():\n    async for message in query(\"Review this code: def fib(n): return fib(n-1) + fib(n-2)\", options):\n        print(message.content)\n\nasyncio.run(get_code_review())\n```\n\n### Parallel Queries\n\n```python\nfrom claif import query_all, query_random\n\n# Query all providers in parallel\nasync def compare_providers():\n    async for results in query_all(\"What is machine learning?\"):\n        for provider, messages in results.items():\n            print(f\"\\n{provider.value.upper()}:\")\n            for msg in messages:\n                print(msg.content)\n\n# Query random provider\nasync def get_random_response():\n    async for message in query_random(\"Tell me a programming joke\"):\n        print(message.content)\n\nasyncio.run(compare_providers())\n```\n\n### Using the Client Class\n\n```python\nfrom claif.client import ClaifClient\nfrom claif import ClaifOptions, Provider\n\n# Create client instance\nclient = ClaifClient()\n\n# List available providers\nproviders = client.list_providers()\nprint(f\"Available: {[p.value for p in providers]}\")\n\n# Query with auto-install\n# If Claude CLI is missing, it will be installed automatically\noptions = ClaifOptions(provider=Provider.CLAUDE)\n\nasync def query_with_client():\n    async for message in client.query(\"Explain asyncio\", options):\n        print(message.content)\n\nasyncio.run(query_with_client())\n```\n\n## How It Works\n\n### Architecture Overview\n\nClaif uses a layered architecture that separates concerns and enables provider independence:\n\n```\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502      User Application       \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502       Claif CLI           \u2502 \u2190 Fire-based CLI with rich output\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502     Claif Client          \u2502 \u2190 Async client with provider routing\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502    Common Types/Utils      \u2502 \u2190 Shared data structures and utilities\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502    Provider Wrappers       \u2502 \u2190 Simple adapters for provider packages\n\u251c\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502claif_cla\u2502claif_gem\u2502claif_cod\u2502 \u2190 Actual provider implementations\n\u2514\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n### Core Components\n\n#### Common Module (`src/claif/common/`)\n\n**types.py** - Core data structures:\n```python\nclass Message:\n    role: MessageRole  # USER, ASSISTANT, SYSTEM\n    content: str | list[TextBlock | ToolUseBlock | ToolResultBlock]\n\nclass Provider(Enum):\n    CLAUDE = \"claude\"\n    GEMINI = \"gemini\" \n    CODEX = \"codex\"\n\nclass ClaifOptions:\n    provider: Provider | None\n    model: str | None\n    temperature: float | None\n    max_tokens: int | None\n    system_prompt: str | None\n    timeout: int | None\n    cache: bool = True\n    verbose: bool = False\n```\n\n**config.py** - Hierarchical configuration:\n- Default values \u2192 Config files \u2192 Environment vars \u2192 CLI args\n- Locations: `~/.claif/config.json`, `~/.config/claif/config.json`, `./claif.json`\n- Provider-specific settings with API key management\n\n**errors.py** - Exception hierarchy:\n- `ClaifError` \u2192 Base exception\n- `ProviderError` \u2192 Provider-specific failures\n- `ConfigurationError` \u2192 Configuration issues  \n- `TransportError` \u2192 Communication errors\n- `TimeoutError` \u2192 Operation timeouts\n\n**install.py** - Auto-installation support:\n```python\ndef install_provider(provider, package, exec_name):\n    # 1. Install npm package using bun\n    # 2. Bundle executable\n    # 3. Install to ~/.local/bin\n```\n\n#### Providers Module (`src/claif/providers/`)\n\nSimple wrapper classes that delegate to provider packages:\n\n```python\n# claude.py\nfrom claif_cla import query as claude_query\n\nclass ClaudeProvider:\n    async def query(self, prompt: str, options: ClaifOptions) -> AsyncIterator[Message]:\n        logger.debug(f\"Querying Claude with prompt: {prompt[:50]}...\")\n        async for message in claude_query(prompt, options):\n            yield message\n```\n\nEach provider:\n- Imports the actual provider package (`claif_cla`, `claif_gem`, `claif_cod`)\n- Implements the same `query` interface\n- Handles provider-specific option conversion\n\n#### Client Module (`src/claif/client.py`)\n\nThe main client with auto-install support:\n\n```python\nclass ClaifClient:\n    def __init__(self):\n        self.providers = {\n            Provider.CLAUDE: ClaudeProvider(),\n            Provider.GEMINI: GeminiProvider(),\n            Provider.CODEX: CodexProvider(),\n        }\n    \n    async def query(self, prompt: str, options: ClaifOptions) -> AsyncIterator[Message]:\n        try:\n            # Route to provider\n            async for message in provider.query(prompt, options):\n                yield message\n        except Exception as e:\n            if _is_cli_missing_error(e):\n                # Auto-install missing CLI\n                install_result = install_func()\n                if install_result.get(\"installed\"):\n                    # Retry query\n                    async for message in provider.query(prompt, options):\n                        yield message\n```\n\nKey features:\n- Routes queries to appropriate providers\n- Auto-installs missing provider CLIs\n- Implements `query`, `query_random`, and `query_all`\n- Handles errors with graceful fallbacks\n\n#### CLI Module (`src/claif/cli.py`)\n\nFire-based CLI with rich terminal output:\n\n```python\nclass ClaifCLI:\n    def __init__(self, config_file=None, verbose=False):\n        # Clean output by default\n        logger.remove()\n        if verbose:\n            logger.add(sys.stderr, level=\"DEBUG\")\n        else:\n            # Only errors go to stderr in non-verbose mode\n            logger.add(sys.stderr, level=\"ERROR\")\n```\n\nMain commands:\n- `query` - Execute queries with options\n- `stream` - Live streaming with rich.Live display\n- `random` - Query random provider\n- `parallel` - Query all providers (with `--compare` for side-by-side)\n- `session` - Start provider-specific interactive session\n- `install` - Install provider CLIs with bun bundling\n- `config` - Manage settings\n- `server` - Start MCP server\n\n#### Server Module (`src/claif/server.py`)\n\nFastMCP server implementation:\n\n```python\nserver = FastMCP(\"Claif MCP Server\")\n\n@server.tool()\nasync def claif_query(prompt: str, provider: str = None) -> list[dict]:\n    \"\"\"Query specific AI provider.\"\"\"\n    options = ClaifOptions(provider=Provider(provider) if provider else None)\n    messages = []\n    async for message in query(prompt, options):\n        messages.append(message.to_dict())\n    return messages\n\n# Additional tools:\n# - claif_query_random: Random provider selection\n# - claif_query_all: Parallel queries\n# - claif_list_providers: Available providers\n# - claif_health_check: Provider status\n```\n\n#### Install Module (`src/claif/install.py`)\n\nHandles CLI installation and bundling:\n\n```python\ndef install_claude() -> dict:\n    \"\"\"Install Claude Code CLI.\"\"\"\n    return install_provider(\n        provider=\"claude\",\n        package=\"@anthropic-ai/claude-code\", \n        exec_name=\"claude\"\n    )\n\n# Installation process:\n# 1. Install npm package globally using bun\n# 2. Bundle with bun compile for fast startup\n# 3. Copy to ~/.local/bin\n# 4. Handle platform-specific paths\n```\n\nKey features:\n- Uses bun for fast npm installs\n- Creates bundled executables\n- Platform-aware installation paths\n- Automatic PATH checking\n\n### Configuration System\n\nHierarchical configuration loading:\n\n1. **Default values** in `Config` dataclass\n2. **Config files** (first found wins):\n   - `~/.claif/config.json`\n   - `~/.config/claif/config.json`\n   - `./claif.json`\n3. **Environment variables**: \n   - `CLAIF_DEFAULT_PROVIDER`\n   - `CLAIF_VERBOSE`\n   - `CLAIF_CACHE_ENABLED`\n   - `CLAIF_SESSION_DIR`\n4. **CLI arguments** (highest priority)\n\nExample configuration:\n\n```json\n{\n    \"default_provider\": \"claude\",\n    \"providers\": {\n        \"claude\": {\n            \"enabled\": true,\n            \"model\": \"claude-3-opus-20240229\",\n            \"api_key_env\": \"ANTHROPIC_API_KEY\"\n        },\n        \"gemini\": {\n            \"enabled\": true,\n            \"model\": \"gemini-2.5-pro\",\n            \"api_key_env\": \"GEMINI_API_KEY\"\n        },\n        \"codex\": {\n            \"enabled\": true,\n            \"model\": \"o4-mini\"\n        }\n    },\n    \"cache_enabled\": true,\n    \"cache_ttl\": 3600,\n    \"output_format\": \"text\"\n}\n```\n\n### Plugin System\n\nPython's entry points for dynamic provider discovery:\n\n```python\n# In src/__init__.py\nclass PluginFinder:\n    \"\"\"Enables imports like: from claif import claude\"\"\"\n    def find_spec(cls, fullname, path, target=None):\n        if fullname.startswith(\"claif.\"):\n            # Look up via entry points\n            eps = metadata.entry_points(group=\"claif.plugins\")\n            for ep in eps:\n                if ep.name == plugin_name:\n                    return ModuleSpec(fullname, Loader())\n```\n\nProvider packages register themselves:\n\n```toml\n# In provider's pyproject.toml\n[project.entry-points.\"claif.plugins\"]\nclaude = \"claif_cla\"\ngemini = \"claif_gem\" \ncodex = \"claif_cod\"\n```\n\n### Message Flow\n\n1. **User Input** \u2192 CLI command or API call\n2. **Options Parsing** \u2192 Create `ClaifOptions` with configuration\n3. **Provider Selection** \u2192 Choose provider based on options\n4. **Query Routing** \u2192 Client routes to appropriate provider wrapper\n5. **CLI Check** \u2192 Auto-install if CLI missing\n6. **Provider Execution** \u2192 Provider package handles actual call\n7. **Response Streaming** \u2192 Yield `Message` objects as they arrive\n8. **Output Formatting** \u2192 Display with rich or return structured data\n\n### Code Structure\n\n```\nclaif/\n\u251c\u2500\u2500 src/\n\u2502   \u251c\u2500\u2500 __init__.py              # Plugin system initialization\n\u2502   \u2514\u2500\u2500 claif/\n\u2502       \u251c\u2500\u2500 __init__.py          # Public API exports\n\u2502       \u251c\u2500\u2500 common/              # Shared components\n\u2502       \u2502   \u251c\u2500\u2500 __init__.py      \n\u2502       \u2502   \u251c\u2500\u2500 types.py         # Core data structures\n\u2502       \u2502   \u251c\u2500\u2500 config.py        # Configuration management\n\u2502       \u2502   \u251c\u2500\u2500 errors.py        # Exception hierarchy\n\u2502       \u2502   \u251c\u2500\u2500 install.py       # CLI installation utilities\n\u2502       \u2502   \u2514\u2500\u2500 utils.py         # Formatting and helpers\n\u2502       \u251c\u2500\u2500 providers/           # Provider wrappers\n\u2502       \u2502   \u251c\u2500\u2500 __init__.py      \n\u2502       \u2502   \u251c\u2500\u2500 claude.py        # Claude wrapper\n\u2502       \u2502   \u251c\u2500\u2500 gemini.py        # Gemini wrapper\n\u2502       \u2502   \u2514\u2500\u2500 codex.py         # Codex wrapper\n\u2502       \u251c\u2500\u2500 client.py            # Main client implementation\n\u2502       \u251c\u2500\u2500 cli.py               # Fire CLI interface\n\u2502       \u251c\u2500\u2500 server.py            # FastMCP server\n\u2502       \u2514\u2500\u2500 install.py           # Provider CLI installation\n\u251c\u2500\u2500 tests/                       # Test suite\n\u251c\u2500\u2500 pyproject.toml               # Package configuration\n\u2514\u2500\u2500 README.md                    # This file\n```\n\n### Key Implementation Details\n\n**Async Everywhere**: All I/O operations use async/await for efficiency\n\n**Message Types**: Flexible content that can be string or structured blocks:\n```python\nclass Message:\n    role: MessageRole\n    content: str | list[TextBlock | ToolUseBlock | ToolResultBlock]\n```\n\n**Provider Adapters**: Thin wrappers that maintain provider-specific features:\n```python\n# Each provider wrapper is ~20 lines\n# Imports provider package\n# Converts options if needed  \n# Yields messages unchanged\n```\n\n**Auto-Install Logic**: Missing CLIs trigger automatic installation:\n```python\nif _is_cli_missing_error(e):\n    install_func = _get_provider_install_function(provider)\n    if install_func():\n        # Retry query with fresh provider instance\n```\n\n**Clean Output**: Logging configured to minimize noise:\n```python\n# Verbose OFF: Only errors to stderr\n# Verbose ON: Full debug logging\n# AI responses always go to stdout\n```\n\n## Installation Details: Bun and Node\n\nClaif uses a hybrid approach for installing provider CLIs:\n\n### Why Bun?\n\n[Bun](https://bun.sh) is used for:\n1. **Fast npm installs** - 10x faster than npm\n2. **Binary bundling** - Creates standalone executables\n3. **Cross-platform** - Works on Windows, macOS, Linux\n4. **Minimal dependencies** - Single binary, no Node.js required\n\n### Installation Process\n\n```bash\n# 1. Bun installs the npm package globally\nbun add -g @anthropic-ai/claude-code\n\n# 2. Bundle script creates optimized binary\nbun build claude-wrapper.ts --compile --outfile dist/claude\n\n# 3. Binary is copied to ~/.local/bin\ncp dist/claude ~/.local/bin/\n```\n\n### Manual Installation Options\n\nIf you prefer Node.js:\n```bash\n# Using npm\nnpm install -g @anthropic-ai/claude-code\nnpm install -g @google/gemini-cli  \nnpm install -g @openai/codex\n\n# Using yarn\nyarn global add @anthropic-ai/claude-code\n\n# Using pnpm\npnpm add -g @anthropic-ai/claude-code\n```\n\n### Bundle Benefits\n\n1. **Faster startup** - No Node.js initialization\n2. **Smaller size** - Single file vs node_modules\n3. **No conflicts** - Isolated from system Node.js\n4. **Portable** - Copy binary anywhere\n\n### Troubleshooting\n\n```bash\n# Check if CLIs are found\nclaif status\n\n# Manually set CLI paths\nexport CLAUDE_CLI_PATH=/usr/local/bin/claude\nexport GEMINI_CLI_PATH=/usr/local/bin/gemini\nexport CODEX_CLI_PATH=/usr/local/bin/codex\n\n# Skip auto-install and use existing CLIs\nclaif config set auto_install=false\n```\n\n## Why Use Claif?\n\n### 1. **Provider Independence**\n- Write once, use any AI model\n- Switch providers with a single parameter\n- Compare responses side-by-side\n- No vendor lock-in\n\n### 2. **Zero Friction**\n- Auto-installs missing CLIs on first use\n- Clean output - only show what matters\n- Smart defaults that just work\n- Async for speed\n\n### 3. **Developer Experience**  \n- Full type hints and IDE support\n- Rich CLI with progress indicators\n- Comprehensive error messages\n- Fast MCP server for tool integration\n\n### 4. **Production Ready**\n- Battle-tested error handling\n- Timeout protection\n- Response caching (coming soon)\n- Configurable everything\n\n### 5. **Extensible**\n- Plugin architecture for new providers\n- Clean API for custom integrations  \n- Well-documented codebase\n- Active development\n\n## Contributing\n\nBased on the development guide in [CLAUDE.md](CLAUDE.md):\n\n### Development Setup\n\n```bash\n# Clone the repository\ngit clone https://github.com/twardoch/claif.git\ncd claif\n\n# Install with dev dependencies\npip install -e \".[dev,test]\"\n\n# Install pre-commit hooks\npre-commit install\n```\n\n### Running Tests\n\n```bash\n# Run all tests\npytest\n\n# Run with coverage\npytest --cov=src/claif --cov-report=html\n\n# Run specific test\npytest tests/test_client.py -v\n\n# Run integration tests (requires provider packages)\npytest tests/integration/ -v\n```\n\n### Code Quality\n\n```bash\n# Format code\nruff format src tests\n\n# Lint code  \nruff check src tests --fix\n\n# Type checking\nmypy src/claif\n\n# Run all checks (as per CLAUDE.md)\nfd -e py -x ruff format {}\nfd -e py -x ruff check --fix --unsafe-fixes {}\npython -m pytest\n```\n\n### Making Changes\n\n1. **Check existing utilities** in `claif.common` before implementing\n2. **Maintain API compatibility** - this is a plugin framework\n3. **Add tests** for new functionality\n4. **Update documentation** in docstrings and README\n5. **Follow style** - 120 char lines, descriptive names\n\n### Release Process\n\n```bash\n# Update version (uses hatch-vcs)\ngit tag v1.0.7\n\n# Build distribution\npython -m build\n\n# Upload to PyPI\ntwine upload dist/*\n```\n\n## License\n\nMIT License - see [LICENSE](LICENSE) file for details.\n\nCopyright (c) 2025 Adam Twardoch\n\n## Links\n\n### Claif Ecosystem\n\n**Core Framework:**\n- [GitHub: twardoch/claif](https://github.com/twardoch/claif) - This repository\n- [PyPI: claif](https://pypi.org/project/claif/) - Core package\n- [Documentation](https://github.com/twardoch/claif#readme) - Full docs\n- [Issues](https://github.com/twardoch/claif/issues) - Bug reports & features\n\n**Provider Packages:**\n- [claif_cla](https://github.com/twardoch/claif_cla/) - Claude provider (wraps claude-code-sdk)\n- [claif_gem](https://github.com/twardoch/claif_gem/) - Gemini provider (wraps Gemini CLI)  \n- [claif_cod](https://github.com/twardoch/claif_cod/) - Codex provider (wraps OpenAI Codex CLI)\n\n### Upstream Projects\n\n**Provider CLIs:**\n- [Claude Code](https://github.com/anthropics/claude-code) - Anthropic's official CLI\n- [claude-code-sdk](https://github.com/anthropics/claude-code-sdk-python) - Python SDK for Claude\n- [Gemini CLI](https://github.com/google-gemini/gemini-cli/) - Google's Gemini CLI\n- [OpenAI Codex CLI](https://github.com/openai/codex) - OpenAI's code generation CLI\n\n**Related Tools:**\n- [Fire](https://github.com/google/python-fire) - Python CLI framework\n- [Rich](https://github.com/Textualize/rich) - Terminal formatting\n- [FastMCP](https://github.com/fixie-ai/fastmcp) - MCP server framework\n- [Bun](https://bun.sh) - Fast JavaScript runtime used for bundling\n\n### Resources\n\n**Documentation:**\n- [Anthropic Docs](https://docs.anthropic.com/) - Claude documentation\n- [Google AI Studio](https://ai.google.dev/) - Gemini documentation\n- [OpenAI Platform](https://platform.openai.com/) - OpenAI documentation\n\n**Community:**\n- [Discussions](https://github.com/twardoch/claif/discussions) - Q&A and ideas\n- [Twitter: @adamtwar](https://twitter.com/adamtwar) - Author updates\n\n**Articles & Tutorials:**\n- [Building a Unified AI CLI](https://example.com) - Design decisions\n- [Plugin Architecture in Python](https://example.com) - Technical deep dive\n- [Async Patterns for AI APIs](https://example.com) - Performance guide",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "A unified CLI and Python library for interacting with local and remote LLMs, compatible with the OpenAI Responses API.",
    "version": "1.0.32",
    "project_urls": {
        "Documentation": "https://github.com/twardoch/claif#readme",
        "Issues": "https://github.com/twardoch/claif/issues",
        "Source": "https://github.com/twardoch/claif"
    },
    "split_keywords": [
        "ai",
        " artificial-intelligence",
        " claif",
        " claude",
        " cli",
        " codex",
        " command-line",
        " gemini",
        " llm",
        " lm-studio",
        " openai",
        " responses-api"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "1af720c326353a6dee614b4524dfccb4dcefece60cb238ab3bf21593ba269d09",
                "md5": "35b3cac8fd3afb3626762311992dec80",
                "sha256": "0d70b50b8a48b31ba88962f9fcccaab5225ed933dd3f1181922ba53135a83cdd"
            },
            "downloads": -1,
            "filename": "claif-1.0.32-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "35b3cac8fd3afb3626762311992dec80",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10",
            "size": 16035,
            "upload_time": "2025-07-29T01:49:46",
            "upload_time_iso_8601": "2025-07-29T01:49:46.986037Z",
            "url": "https://files.pythonhosted.org/packages/1a/f7/20c326353a6dee614b4524dfccb4dcefece60cb238ab3bf21593ba269d09/claif-1.0.32-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "66eb602e049cdf1dcd821dc639ad2c0d942b7d8f8ce669652bdadca0d953bded",
                "md5": "3513f5b2b97e16c5423fc52fc1927a77",
                "sha256": "b4e3b01c5627c4c5333bde9de428d2f58f73e0cb79ae0b01a06a392abc1a9105"
            },
            "downloads": -1,
            "filename": "claif-1.0.32.tar.gz",
            "has_sig": false,
            "md5_digest": "3513f5b2b97e16c5423fc52fc1927a77",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 48645,
            "upload_time": "2025-07-29T01:49:45",
            "upload_time_iso_8601": "2025-07-29T01:49:45.707019Z",
            "url": "https://files.pythonhosted.org/packages/66/eb/602e049cdf1dcd821dc639ad2c0d942b7d8f8ce669652bdadca0d953bded/claif-1.0.32.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-29 01:49:45",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "twardoch",
    "github_project": "claif#readme",
    "travis_ci": false,
    "coveralls": true,
    "github_actions": true,
    "lcname": "claif"
}
        
Elapsed time: 0.72088s