Aiyallm
Aiyallm is a lightweight Python gateway for OpenAI/Anthropic-style language and vision models.
Aiyallm does not ship a built-in model catalog and never guesses model names. Provider endpoints, model lists, and pricing remain caller-owned. API keys can be passed directly, read from a specific environment variable, or inferred from common vendor variables such as
DEEPSEEK_API_KEYandDASHSCOPE_API_KEY.
Table of Contents
- Features
- Install
- Quick Start
- Provider Configuration
- Routing
- Vision and Capability Filtering
- Usage Ledger
- Cost Calculation
- Streaming and Async
- Extending Providers
Features
- OpenAI and OpenAI-compatible providers
- Anthropic and Anthropic-compatible providers
- Chinese OpenAI-compatible providers such as DeepSeek, Qwen/DashScope, Moonshot/Kimi, Zhipu GLM, and SiliconFlow
- Explicit model selection,
provider/modelreferences, and provider route lists - Routing policies:
fallback,first,round_robin,random, andleast_used - Vision and multimodal message passthrough with capability filtering
- Sync and async chat and streaming
- Detailed token accounting with cache hit/miss fields
- Optional SQLite or JSONL usage persistence
- Optional cost calculation from an application-owned price book
Install
pip install -e .
Runtime dependencies are:
openaianthropichttpx
Quick Start
There are no default providers. Pass them explicitly.
from aiyallm import Aiyallm
client = Aiyallm(
providers=[
{
"name": "deepseek",
"account": "billing-deepseek",
"type": "openai_compatible",
"base_url": "https://api.deepseek.com",
"api_key": "your-deepseek-key",
"models": ["deepseek-chat", "deepseek-reasoner"],
}
]
)
response = client.chat(
"用一句话解释什么是路由。",
model="deepseek/deepseek-chat",
)
print(response.text)
print(response.usage)
modelsis optional when the provider allows unknown models. Configured providers default toallow_unknown_models=True, but the provider itself must still be declared.
Provider Configuration
Each provider accepts these common fields.
| Field | Meaning |
|---|---|
name |
Unique provider name used by provider/model routes |
account |
Optional billing or account name recorded in usage |
type |
openai, openai_compatible, anthropic, or anthropic_compatible |
base_url |
Endpoint for compatible services |
api_key |
Explicit API key |
api_key_env |
Optional environment variable name, or list of names, to read the key from |
api_key_prompt |
When True, prompt in the terminal if no key is found; defaults to False |
models |
Optional model names or {"id", "capabilities", ...} mappings |
allow_unknown_models |
Allow model IDs not listed in models; defaults to True |
timeout |
SDK timeout |
OpenAI-Compatible Examples
providers = [
{
"name": "deepseek",
"type": "openai_compatible",
"base_url": "https://api.deepseek.com",
# `api_key_env` is optional; DEEPSEEK_API_KEY is inferred from `name`.
},
{
"name": "qwen",
"type": "openai_compatible",
"base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1",
# DASHSCOPE_API_KEY or QWEN_API_KEY is inferred from `name`.
},
{
"name": "moonshot",
"type": "openai_compatible",
"base_url": "https://api.moonshot.cn/v1",
# MOONSHOT_API_KEY or KIMI_API_KEY is inferred from `name`.
},
{
"name": "glm",
"type": "openai_compatible",
"base_url": "https://open.bigmodel.cn/api/paas/v4",
# ZHIPU_API_KEY / ZHIPUAI_API_KEY / GLM_API_KEY is inferred from `name`.
},
]
Use the base_url from each provider's current documentation. Aiyallm does not hard-code any endpoint or model name.
Routing
Use provider/model to target a provider.
response = client.chat("Hello", model="qwen/qwen-plus")
Use a route list for fallback.
response = client.chat(
"Hello",
model=["deepseek/deepseek-chat", "qwen/qwen-plus"],
route_policy="fallback",
)
Available policies:
fallback- try candidates in orderfirst- use only the first candidateround_robin- rotate through candidatesrandom- choose a random candidateleast_used- choose the candidate with the lowest recorded token usage
The default policy is fallback.
Vision and Capability Filtering
Declare capabilities in models and pass require_vision=True when a request needs image input.
from aiyallm import Aiyallm
client = Aiyallm(
providers=[
{
"name": "qwen",
"type": "openai_compatible",
"base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1",
"api_key": "your-key",
"models": [
{"id": "qwen-vl-plus", "capabilities": ["text", "vision"]}
],
}
]
)
response = client.chat(
[
{
"role": "user",
"content": [
{"type": "text", "text": "What is in this image?"},
{
"type": "image_url",
"image_url": {"url": "https://example.com/image.jpg"},
},
],
}
],
model="qwen/qwen-vl-plus",
require_vision=True,
)
Usage Ledger
Every successful completion is recorded.
response = client.chat("Hello", model="deepseek/deepseek-chat")
print(response.usage.input_tokens)
print(response.usage.output_tokens)
print(response.usage.input_cache_hit_tokens)
print(response.usage.input_cache_miss_tokens)
print(response.usage.output_cache_hit_tokens)
print(response.usage.output_cache_miss_tokens)
print(response.usage.account)
print(response.usage.reasoning_tokens)
print(client.ledger.total())
print(client.ledger.by_model())
print(client.ledger.by_provider())
print(client.ledger.by_account())
client.ledger.export_jsonl("usage.jsonl")
For persistent accounting, pass a SQLite path.
from aiyallm import UsageLedger
ledger = UsageLedger(sqlite_path="data/usage.sqlite3")
client = Aiyallm(providers=providers, ledger=ledger)
Cost Calculation
Token counts are always recorded. Cost is calculated only when the application supplies a price.
from aiyallm import Aiyallm, Price, PriceBook
price_book = PriceBook(
{
"deepseek-chat": Price(
input_per_mtok=0.27,
output_per_mtok=1.10,
)
}
)
client = Aiyallm(providers=providers, price_book=price_book)
response = client.chat("Hello", model="deepseek/deepseek-chat")
print(response.usage.cost_usd)
Prices are US dollars per million tokens.
Streaming and Async
for chunk in client.stream("Hello", model="deepseek/deepseek-chat"):
print(chunk.content, end="", flush=True)
import asyncio
async def main():
response = await client.achat("Hello", model="deepseek/deepseek-chat")
print(response.text)
async for chunk in client.astream("Hello", model="deepseek/deepseek-chat"):
print(chunk.content, end="", flush=True)
asyncio.run(main())
Extending Providers
For a non-standard provider, subclass aiyallm.BaseProvider and implement complete, acomplete, stream, and astream.
from aiyallm import Aiyallm, BaseProvider
class MyProvider(BaseProvider):
def complete(self, request):
...
Then register the instance directly.
client = Aiyallm(providers=[MyProvider(name="my-provider", models=["my-model"])])
Metadata
Release files for aiyallm 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| aiyallm-0.1.2.tar.gz | 26.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| aiyallm-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 53.5 kB
Release files / aiyallm-0.1.2.tar.gz
| Download URL | aiyallm-0.1.2.tar.gz |
|---|---|
| Size | 26.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1c3e93ee5000d9a6eb1b8a1616549323c3332a41c26679f4c0a2ca13c9cde48f
|
|
BLAKE2b-256 checksum How to use checksums |
1b27805fb00fe9dd955ac4405dd5d2b462c7b85ec624ad3a7bf533e073bec770
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.3
|
Release files / aiyallm-0.1.2-py3-none-any.whl
| Download URL | aiyallm-0.1.2-py3-none-any.whl |
|---|---|
| Size | 27.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
642af09ac53ec2d0057d920729daedb2936acec2939a26f31f678e0e90e175f8
|
|
BLAKE2b-256 checksum How to use checksums |
a6a71ba30c6a4ff0d0ac7ae6637bccaa897e01f040f74975eb31bc2d3278dc3a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.3
|