Couche d'abstraction multi-backend pour appels LLM : Anthropic natif, LiteLLM (OpenAI/Gemini/Ollama/vLLM/OpenRouter), claude -p dev-only.
Project description
apicol
Couche d'abstraction Python pour appeler un LLM via plusieurs backends interchangeables. Une interface unifiée au format OpenAI pour parler à : API Anthropic native (avec caching, thinking, citations), LiteLLM (qui route vers OpenAI, Gemini, Mistral, Ollama, vLLM, LM Studio, OpenRouter et 100+ providers), et Claude Code CLI (claude -p, usage dev local uniquement).
Sélection du backend par variables d'environnement ou par objet Client configurable. Plusieurs backends peuvent cohabiter dans le même process (bench, fallback applicatif, comparaison).
Statut
v0.1.0 — alpha. Implémentation complète des trois backends (Anthropic natif, LiteLLM, claude -p dev-only), surface publique stable, ≥95 % de couverture. Streaming et tool calls reportés à v0.2/v0.3. Documentation associée :
README.md— ce fichierCLAUDE.md— instructions pour Claude Code travaillant sur ce repoARCHITECTURE.md— décisions techniques structurantesSPEC.md— contrat d'API publiqueCHANGELOG.md— historique des versionspyproject.toml— métadonnées du packagedocs/prd/— PRD-001 (architecture deux niveaux), PRD-002 (séparationclaude -p), PRD-003 (multi-backend simultané)
Pourquoi cette lib
LiteLLM résout déjà 90 % du problème (interface unifiée, 100+ providers, format OpenAI partout). apicol ajoute deux choses que LiteLLM ne fait pas bien :
- Un chemin Anthropic natif préservé. Le compatibility layer OpenAI d'Anthropic ne supporte ni le prompt caching ni le détail du thinking. Pour les usages long-context avec caching agressif, il faut le SDK Anthropic natif.
apicolexpose explicitement cette voie quandAPICOL_TYPE=anthropic, via une méthodeclient.anthropic_native(). - Un backend
claude -ppour le dev local interactif. Strictement marqué « dev only » et exposé via une fonction séparée — pas routé dans l'interface unifiée — pour éviter toute confusion avec un usage programmatique qui violerait les TOS Anthropic (voir PRD-002).
Le reste (OpenAI, Gemini, modèles locaux, OpenRouter) est délégué à LiteLLM sans réinventer la roue.
Installation
Depuis PyPI (cible v0.1.0)
pip install apicol
Depuis le repo Git (phase alpha)
# Dernière version de main
pip install git+https://github.com/JP/apicol.git
# Tag spécifique
pip install git+https://github.com/JP/apicol.git@v0.1.0
# Branche dev
pip install git+https://github.com/JP/apicol.git@dev
Installation editable (dev local)
git clone https://github.com/JP/apicol.git
cd apicol
pip install -e ".[dev]" # base + outils dev (pytest, ruff, mypy)
Ajout en dépendance d'un projet tiers
Dans pyproject.toml (par exemple celui d'Athanor) :
[project]
dependencies = [
"apicol>=0.1.0",
# OU depuis git en phase alpha
"apicol @ git+https://github.com/JP/apicol.git@main",
]
Dans requirements.txt :
apicol>=0.1.0
Avec uv :
uv add apicol
# ou depuis git
uv add "apicol @ git+https://github.com/JP/apicol.git"
Dépendances installées
apicol installe automatiquement :
anthropic(SDK natif pour le backend Anthropic et l'échappatoire native)litellm(délégation pour tous les autres backends)
Python ≥ 3.10 requis.
Variables d'environnement
| Variable | Valeurs | Rôle |
|---|---|---|
APICOL_TYPE |
anthropic, litellm |
Backend par défaut (utilisé par les fonctions globales chat, achat) |
APICOL_KEY |
string | Clé d'API par défaut |
APICOL_MODEL |
string | Modèle par défaut (ex. claude-opus-4-7, openai/gpt-5, ollama/qwen3:32b) |
APICOL_URL |
URL (optionnel) | Endpoint custom (vLLM local, LM Studio, gateway, etc.) |
Pour claude_cli_chat() aucune variable n'est requise.
Pour un usage multi-backend simultané, ces variables ne sont pas suffisantes — on construit des Client explicites (voir Usage ci-dessous).
Usage
Mode simple — fonctions globales et env vars
import os
import apicol
os.environ["APICOL_TYPE"] = "anthropic"
os.environ["APICOL_KEY"] = "sk-ant-..."
os.environ["APICOL_MODEL"] = "claude-opus-4-7"
response = apicol.chat(
messages=[{"role": "user", "content": "Bonjour"}],
reasoning_effort="medium",
)
print(response["choices"][0]["message"]["content"])
Bascule de backend sans toucher au code applicatif :
export APICOL_TYPE=litellm
export APICOL_MODEL=openai/gpt-5
export APICOL_KEY=sk-...
Mode async — achat
import asyncio, apicol
async def main():
response = await apicol.achat(
messages=[{"role": "user", "content": "Bonjour"}],
)
return response
asyncio.run(main())
Mode multi-backend — objet Client
Pour avoir plusieurs configurations actives simultanément dans le même process (bench, fallback applicatif, comparaison) :
import apicol
claude = apicol.Client(
backend="anthropic",
api_key="sk-ant-...",
model="claude-opus-4-7",
)
gpt = apicol.Client(
backend="litellm",
api_key="sk-...",
model="openai/gpt-5",
)
qwen_local = apicol.Client(
backend="litellm",
model="ollama/qwen3:32b",
base_url="http://localhost:11434",
)
prompt = [{"role": "user", "content": "Explique la relativité en 3 phrases"}]
# Bench parallèle
for name, client in [("claude", claude), ("gpt", gpt), ("qwen", qwen_local)]:
response = client.chat(prompt)
print(f"\n=== {name} ===\n{response['choices'][0]['message']['content']}")
Un Client est immutable après construction. Pour changer de configuration, on crée un nouveau client. Cette rigidité simplifie le raisonnement multi-instance.
Mode multi-backend async — AsyncClient
import asyncio, apicol
claude = apicol.AsyncClient(backend="anthropic", api_key="...", model="claude-opus-4-7")
gpt = apicol.AsyncClient(backend="litellm", api_key="...", model="openai/gpt-5")
async def bench():
return await asyncio.gather(
claude.chat(prompt),
gpt.chat(prompt),
)
claude_resp, gpt_resp = asyncio.run(bench())
Accès au SDK Anthropic natif (features avancées)
Pour le prompt caching avec breakpoints fins, les citations, le PDF input, le batch API, ou tout autre feature non-portable :
import apicol
# Via un Client
claude = apicol.Client(backend="anthropic", api_key="...", model="claude-opus-4-7")
native = claude.anthropic_native() # → anthropic.Anthropic préconfiguré
response = native.messages.create(
model="claude-opus-4-7",
max_tokens=4096,
system=[
{
"type": "text",
"text": gros_document,
"cache_control": {"type": "ephemeral"},
}
],
messages=[{"role": "user", "content": "Question sur le document"}],
)
# À partir d'ici, tu utilises le SDK Anthropic avec TOUTES ses features.
# Variante depuis les env vars (alias rétrocompatible)
native = apicol.anthropic_client() # équivalent à Client().anthropic_native()
Backend dev only — claude_cli_chat
⚠️ Dev only. Cette fonction est destinée à un usage personnel interactif (un script local que tu lances toi-même). Elle n'est pas destinée à servir une charge programmatique routée selon coût/dispo — un tel usage enfreint les TOS de Claude Pro/Max. Voir PRD-002 pour le détail.
import apicol
# Aucune variable APICOL_* requise — utilise le `claude` CLI authentifié localement
response = apicol.claude_cli_chat(
messages=[{"role": "user", "content": "Résume CHANGELOG.md du dossier courant"}],
)
print(response["choices"][0]["message"]["content"])
Cette fonction est volontairement séparée des fonctions globales chat() / achat() et des objets Client / AsyncClient : aucun chemin de routage ne mène automatiquement vers claude -p.
Paramètres unifiés (niveau 1)
Les méthodes Client.chat(), AsyncClient.chat() et les fonctions globales chat(), achat() acceptent tous les paramètres suivants :
| Paramètre | Type | Comportement |
|---|---|---|
messages |
list[dict] |
Format OpenAI standard |
model |
str | None |
Override du modèle du Client / des env vars |
temperature |
float | None |
0.0 à 2.0, sémantique OpenAI |
max_tokens |
int | None |
Default 4096 |
reasoning_effort |
"none" | "low" | "medium" | "high" |
Mappé selon backend (voir SPEC) |
extra_body |
dict |
Passthrough silencieux vers le SDK sous-jacent (ex. cache_control Anthropic) |
Détails complets et table de mapping reasoning_effort → thinking dans SPEC.md.
Hors périmètre
- Pas de cost tracking, virtual keys, rate limiting, dashboard. Utiliser le proxy LiteLLM standalone pour ça.
- Pas de routage automatique avec fallback/loadbalance au niveau de la lib. Pour ces patterns, l'application orchestre ses
Clientà la main. - Pas de support des modalités exotiques (audio, embeddings, fine-tuning) en v0.1. Chat completion uniquement.
- Pas de streaming en v0.1 (reporté à v0.2 — interface d'events unifiée à concevoir).
- Pas de support des tool calls en v0.1 (reporté à v0.3).
- Pas de routage automatique vers
claude -p. Volontaire — voir PRD-002.
Licence
À définir (MIT probablement).
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 apicol-0.1.0.tar.gz.
File metadata
- Download URL: apicol-0.1.0.tar.gz
- Upload date:
- Size: 270.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f8e0bcefa25210f43a01a39613036cd4d9358c8e85cdf7c9e9a4d8bbeac255bd
|
|
| MD5 |
a6f6376285e23be15fe565e91894c4a6
|
|
| BLAKE2b-256 |
7722b42b4143ac39b7ba134f7b5fabef23d63235bce25e883e6af04330544c49
|
Provenance
The following attestation bundles were made for apicol-0.1.0.tar.gz:
Publisher:
release.yml on Sandjab/apicol
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
apicol-0.1.0.tar.gz -
Subject digest:
f8e0bcefa25210f43a01a39613036cd4d9358c8e85cdf7c9e9a4d8bbeac255bd - Sigstore transparency entry: 1539397564
- Sigstore integration time:
-
Permalink:
Sandjab/apicol@f11fc789ca8219d2a1dad3c5e28cc923836ff980 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Sandjab
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f11fc789ca8219d2a1dad3c5e28cc923836ff980 -
Trigger Event:
push
-
Statement type:
File details
Details for the file apicol-0.1.0-py3-none-any.whl.
File metadata
- Download URL: apicol-0.1.0-py3-none-any.whl
- Upload date:
- Size: 16.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
258dd5e96b361daa84c7c99f2339812b6a041cd803cd63d9701a2fef15f32728
|
|
| MD5 |
2a40eaa8dc2188114dce1152cb0796e5
|
|
| BLAKE2b-256 |
254eb0c282790e6b011ce552f12ac9ec9e722142daa9ca31e1c2af62c74de519
|
Provenance
The following attestation bundles were made for apicol-0.1.0-py3-none-any.whl:
Publisher:
release.yml on Sandjab/apicol
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
apicol-0.1.0-py3-none-any.whl -
Subject digest:
258dd5e96b361daa84c7c99f2339812b6a041cd803cd63d9701a2fef15f32728 - Sigstore transparency entry: 1539397622
- Sigstore integration time:
-
Permalink:
Sandjab/apicol@f11fc789ca8219d2a1dad3c5e28cc923836ff980 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Sandjab
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f11fc789ca8219d2a1dad3c5e28cc923836ff980 -
Trigger Event:
push
-
Statement type: