Metadata-Version: 2.4
Name: qx-llms
Version: 0.1.7
Summary: A simple LLM client factory utility for use across QX applications
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Requires-Python: >=3.12
Description-Content-Type: text/markdown
Requires-Dist: langchain-core==1.2.7
Requires-Dist: langchain-community==0.4.1
Requires-Dist: langchain-openai==1.1.7
Requires-Dist: langchain-anthropic==1.3.1
Requires-Dist: langchain-google-genai==4.2.0
Requires-Dist: langchain-deepseek==1.0.1
Requires-Dist: langchain-ollama==1.0.1
Requires-Dist: openai-agents
Requires-Dist: pydantic
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Requires-Dist: pytest-asyncio; extra == "test"

# qx-llms

A unified LLM client factory and model registry for use across QX applications. Provides a consistent interface for working with multiple LLM providers through both LangChain and OpenAI Agents SDK.

## Installation

```bash
pip install qx-llms
```

Or install from source:

```bash
pip install -e .
```

## Features

- **Multi-provider support** - OpenAI, Anthropic, Google Gemini, DeepSeek, and more
- **Two factory interfaces** - LangChain clients and OpenAI Agents SDK models
- **Model registry** - Centralized model definitions with capabilities and credit costs
- **Fake models for testing** - Mock implementations that don't make API calls
- **Graceful fallbacks** - Optional silent failure with dummy models

## Quick Start

### LangChain Factory

```python
from qx_llms.factories.langchain_client_factory import get_llm_client, get_embeddings_client

# Get a chat model
llm = get_llm_client(model_name="gpt-4o", provider="openai")
response = llm.invoke("Hello, world!")

# Get an embeddings model
embeddings = get_embeddings_client(model_name="text-embedding-3-small", provider="openai")
vectors = embeddings.embed_query("Hello, world!")
```

### OpenAI Agents Factory

```python
from qx_llms.factories.openai_client_factory import get_openai_agents_model

# Get a model for use with OpenAI Agents SDK
model = get_openai_agents_model(provider="openai", model_name="gpt-4o")
```

### Model Registry

```python
from qx_llms.model_registry import (
    get_llm_by_name,
    get_llm_options,
    get_llm_credit_mapping,
    get_embedding_by_name,
    get_embedding_options,
    ModelProvider,
)

# Get a specific chat model's details
model = get_llm_by_name("gpt-4o")
print(model.name, model.provider, model.credits)

# Get all chat models with specific capabilities
options = get_llm_options(
    providers=[ModelProvider.OPENAI, ModelProvider.ANTHROPIC],
    structured_output=True,
    tool_use=True,
)

# Get credit costs for billing
credits = get_llm_credit_mapping(credit_multiplier=2)

# Get a specific embedding model's details
embedding = get_embedding_by_name("text-embedding-3-large")
print(embedding.name, embedding.dimensions, embedding.multimodal)

# Get all embedding models with specific capabilities
embedding_options = get_embedding_options(
    providers=[ModelProvider.OPENAI],
    dimensions=1536,
)
```

## Supported Providers

### LangChain Factory

| Provider | Environment Variables | Description |
|----------|----------------------|-------------|
| `openai` | `OPENAI_API_KEY` | OpenAI API |
| `azure_openai` | `AZURE_OPENAI_API_KEY`, `AZURE_OPENAI_ENDPOINT` | Azure OpenAI |
| `anthropic` | `ANTHROPIC_API_KEY` | Anthropic Claude |
| `gemini` | `GEMINI_API_KEY` | Google Gemini |
| `deepseek` | `DEEPSEEK_API_KEY` | DeepSeek |
| `openai_endpoint` | `OPENAI_ENDPOINT_API_KEY`, `OPENAI_ENDPOINT_BASE_URL` | Custom OpenAI-compatible endpoint |
| `ollama` | `OLLAMA_BASE_URL` (optional) | Local Ollama |
| `lmstudio` | `LMSTUDIO_BASE_URL` (optional) | Local LM Studio |
| `fake` | None | Fake model for testing |

### OpenAI Agents Factory

| Provider | Environment Variables | Description |
|----------|----------------------|-------------|
| `openai` | `OPENAI_API_KEY` | OpenAI Responses API |
| `deepseek` | `DEEPSEEK_API_KEY` | DeepSeek |
| `openrouter` | `OPENROUTER_API_KEY` | OpenRouter |
| `gemini` | `GEMINI_API_KEY` | Google Gemini |
| `anthropic` | `ANTHROPIC_API_KEY` | Anthropic |
| `perplexity` | `PERPLEXITY_API_KEY` | Perplexity |
| `huggingface` | `HUGGINGFACE_API_KEY` | Hugging Face Inference |
| `local` | `LOCAL_MODEL_URL` | Local models (Ollama, etc.) |
| `azure_openai` | `AZURE_OPENAI_API_KEY`, `AZURE_OPENAI_ENDPOINT` | Azure OpenAI |
| `fake` | None | Fake model for testing |

## Testing with Fake Models

Both factories provide fake models that return static responses without making API calls:

```python
# LangChain fake model
from qx_llms.factories.langchain_client_factory import get_llm_client

fake_llm = get_llm_client(model_name="fake", provider="fake")

# OpenAI Agents fake model
from qx_llms.factories.openai_client_factory import get_openai_agents_model

fake_model = get_openai_agents_model(provider="fake")
response = await fake_model.get_response(...)  # Returns "fake response"
```

## Model Registry

The model registry provides a centralized definition of available models with their capabilities.

### Chat Models

```python
from qx_llms.model_registry import ChatModel, ModelProvider

# Each chat model has these attributes:
# - name: str              - Model identifier (e.g., "gpt-4o")
# - provider: ModelProvider - Provider enum
# - structured_output: bool - Supports JSON schema output
# - tool_use: bool         - Supports function calling
# - vision: bool           - Supports image input
# - accepts_temperature: bool - Supports temperature parameter
# - credits: int           - Base credit cost per request
```

### Embedding Models

```python
from qx_llms.model_registry import EmbeddingModel, ModelProvider

# Each embedding model has these attributes:
# - name: str              - Model identifier (e.g., "text-embedding-3-large")
# - provider: ModelProvider - Provider enum
# - credits: int           - Base credit cost per request
# - dimensions: int        - Output vector size (768, 1536, 3072, etc.)
# - multimodal: bool       - Can embed images (e.g., CLIP, OpenAI multimodal)
```

### Filtering Chat Models

```python
from qx_llms.model_registry import get_llm_options, ModelProvider

# Get only models that support structured output and vision
models = get_llm_options(
    structured_output=True,
    vision=True,
)

# Get models from specific providers
openai_models = get_llm_options(providers=[ModelProvider.OPENAI])
```

### Filtering Embedding Models

```python
from qx_llms.model_registry import get_embedding_options, ModelProvider

# Get only embedding models with specific dimensions
models = get_embedding_options(dimensions=1536)

# Get multimodal embedding models
multimodal_models = get_embedding_options(multimodal=True)

# Get embedding models from specific providers
openai_embeddings = get_embedding_options(providers=[ModelProvider.OPENAI])
```

## Error Handling

Both factories support graceful fallbacks:

```python
# Raises ValueError if provider is invalid or API key is missing
llm = get_llm_client(model_name="gpt-4o", provider="openai", fail_silently=False)

# Returns a dummy model instead of raising
llm = get_llm_client(model_name="gpt-4o", provider="openai", fail_silently=True)
```

## Development

### Running Tests

```bash
pip install -e ".[test]"
pytest tests/ -v
```

## Requirements

- Python >= 3.12
- See `requirements.txt` for dependencies
