dd-llm
Shared LLM abstraction layer for Digital Duck projects.
Zero core dependencies. Adapters lazy-import their SDKs only when used.
Install
pip install -e . # zero deps — claude_cli adapter works out of the box
pip install -e ".[openai]" # + OpenAI SDK (also covers openrouter, ollama)
pip install -e ".[anthropic]" # + Anthropic SDK
pip install -e ".[gemini]" # + Google GenAI SDK
pip install -e ".[momagrid]" # + httpx (Momagrid hub)
pip install -e ".[bedrock]" # + boto3
pip install -e ".[all]" # all provider SDKs
Quick Start
from dd_llm import call_llm
# Uses LLM_PROVIDER env var (default: "openai")
response = call_llm("What is 2+2?")
# Specify provider
response = call_llm("Hello", provider="claude_cli")
# With messages
response = call_llm(messages=[
{"role": "system", "content": "You are helpful."},
{"role": "user", "content": "Hi"},
])
Built-in Adapters
| Name | Class | Needs | Notes |
|---|---|---|---|
claude_cli |
ClaudeCLIAdapter |
the claude CLI |
Dev provider, $0 via Claude Code subscription. Prompt goes in via stdin; ANTHROPIC_API_KEY is stripped from the child so it cannot bill the paid API; limit errors carry error_type == "ModelOverloaded" |
gemini_cli |
GeminiCLIAdapter |
the gemini CLI |
Same idea for Gemini CLI |
openai |
OpenAIAdapter |
openai |
Direct OpenAI API |
anthropic |
AnthropicAdapter |
anthropic |
Direct Anthropic API |
gemini |
GeminiAdapter |
google-genai |
Direct Google API |
vertex |
VertexAdapter |
google-genai |
Gemini on Vertex AI (ADC auth) |
openrouter |
OpenAIAdapter (configured) |
openai |
OpenAI-compatible endpoint |
ollama |
OpenAIAdapter (configured) |
openai |
Local OpenAI-compatible endpoint |
deepseek |
OpenAIAdapter (configured) |
openai, DEEPSEEK_API_KEY |
DeepSeek API |
qwen |
OpenAIAdapter (configured) |
openai, DASHSCOPE_API_KEY |
Alibaba DashScope |
azure_openai |
AzureOpenAIAdapter |
openai, AZURE_OPENAI_* |
model is the deployment name |
bedrock |
BedrockAdapter |
boto3 |
Converse API; estimates cost |
momagrid |
MomagridAdapter |
httpx |
Decentralised inference via a Momagrid hub; fills response_worker |
echo |
EchoAdapter |
none | Returns the prompt; for tests |
dd_llm.model_compatible(adapter, model) says whether a model name is plausible for an adapter
(claude_cli only speaks claude-*, openrouter models contain a slash, ...), so callers can drop a
stray model hint instead of making a doomed call.
dd_llm.parse_llm_spec("claude_cli:claude-haiku-5-5") returns ("claude_cli", "claude-haiku-5-5"). It is the
parser behind the --llm ADAPTER:MODEL flag of the CLIs built on dd-llm (grafr, spl3): only the first :
splits, so ollama:gemma3:12b and openrouter:meta/llama-3 work, and a bare claude_cli gives ("claude_cli", None).
Custom Adapters
from dd_llm import LLMAdapter, LLMResponse, register_adapter, call_llm
class MyAdapter(LLMAdapter):
def call(self, prompt="", *, messages=None, **kwargs):
result = my_internal_api(prompt)
return LLMResponse(content=result, success=True, provider="my_api", model="v1")
register_adapter("my_api", MyAdapter)
# Now usable everywhere
response = call_llm("hello", provider="my_api")
UnifiedLLMProvider
Multi-provider client with retry (exponential backoff + jitter) and automatic fallback to alternative providers.
from dd_llm import UnifiedLLMProvider
provider = UnifiedLLMProvider(
primary_provider="openai",
fallback_providers=["anthropic", "ollama"],
max_retries=3,
)
result = provider.call("Explain quantum computing")
if result.success:
print(result.content)
print(f"Provider: {result.provider}, Model: {result.model}")
print(f"Tokens: {result.input_tokens} in, {result.output_tokens} out")
Environment Variables
| Variable | Description | Default |
|---|---|---|
LLM_PROVIDER |
Primary provider name | openai |
LLM_MODEL |
Default model (all providers) | per-provider |
LLM_MODEL_OPENAI |
Override model for OpenAI | gpt-4o |
LLM_MODEL_ANTHROPIC |
Override model for Anthropic | claude-sonnet-4-5-20250929 |
LLM_MODEL_GEMINI |
Override model for Gemini | gemini-2.0-flash |
LLM_MAX_RETRIES |
Max retries per provider | 3 |
LLM_INITIAL_WAIT |
Initial backoff (seconds) | 1 |
LLM_MAX_WAIT |
Max backoff (seconds) | 30 |
OPENAI_API_KEY |
OpenAI API key | — |
ANTHROPIC_API_KEY |
Anthropic API key | — |
GEMINI_API_KEY |
Google Gemini API key | — |
OPENROUTER_API_KEY |
OpenRouter API key | — |
OLLAMA_HOST |
Ollama base URL | http://localhost:11434 |
DEEPSEEK_API_KEY, DASHSCOPE_API_KEY |
DeepSeek, Qwen keys | — |
AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_API_KEY, AZURE_OPENAI_API_VERSION |
Azure OpenAI | — |
MOMAGRID_HUB_URL, MOMAGRID_API_KEY |
Momagrid hub (else ~/.igrid/config.yaml) |
http://localhost:9000 |
GOOGLE_CLOUD_PROJECT, GOOGLE_CLOUD_LOCATION |
Vertex AI | — |
License
MIT
Metadata
Release files for dd-llm 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| dd_llm-0.2.0.tar.gz | 25.6 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| dd_llm-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 53.0 kB
Release files / dd_llm-0.2.0.tar.gz
| Download URL | dd_llm-0.2.0.tar.gz |
|---|---|
| Size | 25.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cb50ff8bf179ee4606ec658ab1822c5d7c8b2b62f4e7115c6568e9b1921c6cd0
|
|
BLAKE2b-256 checksum How to use checksums |
0af87f2d2ec35277a1ce591dda652a8629d704b91ed784ab3041b71910a2da29
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.5
|
Release files / dd_llm-0.2.0-py3-none-any.whl
| Download URL | dd_llm-0.2.0-py3-none-any.whl |
|---|---|
| Size | 27.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
03e5427ef02808daee2e77da775e38e758db2cdb50615f41bab16d22e1a1f3a5
|
|
BLAKE2b-256 checksum How to use checksums |
b4a66cdc59f230f497435af9b93f251e7a86941a5b95667638e9c91604986fd6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.5
|
Security for dd-llm 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.