Unified LLM API with automatic model discovery, structured errors, and composable middleware
Project description
统一的 Python LLM API。多厂商、中间件管道、异步优先、零必选依赖。
Built with DeepSeek V4 Pro — 本项目代码由 DeepSeek V4 Pro 辅助生成。
特性
- 统一接口 — 一套 API 调用 OpenAI、Anthropic、Google Gemini、AWS Bedrock、Mistral、DeepSeek、xAI 等 30+ 厂商
- 822 个预置模型 — 从 models.dev 自动拉取,开箱即用
- 双协议支持 — 厂商支持多套 API 时(如 Xiaomi 同时支持 OpenAI 和 Anthropic 协议),同时提供两种变体
- 流式事件 — 异步迭代器逐块返回文本、思维链、工具调用
- 中间件管道 — 日志、重试、费用上限,可自由组合或自定义
- 结构化错误 — 12 种
ErrorCategory,统一处理速率限制、上下文溢出等 - 跨厂商上下文传递 — 在不同模型之间无缝切换对话
@tool装饰器 — 从类型标注自动生成 JSON Schema- 零必选依赖 —
httpx/boto3按需懒加载 - 完整类型标注 —
py.typed+ mypy strict 通过
安装
pip install dino-ai-py
按厂商安装可选依赖:
pip install "dino-ai-py[openai]" # OpenAI / DeepSeek / Groq / xAI / Together / Mistral 等
pip install "dino-ai-py[anthropic]" # Anthropic Claude
pip install "dino-ai-py[google]" # Google Gemini
pip install "dino-ai-py[bedrock]" # AWS Bedrock (boto3)
pip install "dino-ai-py[all]" # 全部
快速开始
import asyncio
from dino_ai import DinoClient, Context, UserMessage
from dino_ai.providers.openai_completions import OpenAICompletionsProvider
from dino_ai.providers.anthropic import AnthropicProvider
# 创建客户端,注册你需要的厂商
client = DinoClient(providers=[
OpenAICompletionsProvider(),
AnthropicProvider(),
])
async def main():
context = Context(messages=[UserMessage(content="用一句话解释量子纠缠")])
# 方式 1: 直接 await 获取完整回复
message = await client.complete("gpt-4o", context)
print(message.text)
# 方式 2: 流式逐块接收
stream = client.stream("claude-sonnet-4-0", context)
async for event in stream:
print(event)
asyncio.run(main())
核心概念
模型
822 个模型常量,按厂商分组。每个厂商一个独立文件,按需导入:
from dino_ai.models import Openai, Anthropic, Google, Deepseek
model = Openai.GPT_4O # gpt-4o
model = Anthropic.CLAUDE_SONNET_4_0 # claude-sonnet-4-0
model = Google.GEMINI_2_5_PRO # gemini-2.5-pro
model = Deepseek.DEEPSEEK_V4_PRO # deepseek-v4-pro
message = await client.complete("gpt-4o", context)
每个 Model 包含能力声明、上下文窗口、定价等元信息:
model = Openai.GPT_4O
print(model.capabilities.vision) # True
print(model.limits.context_window) # 128000
print(model.pricing.input) # 2.5 ($/M tokens)
浏览所有模型:
from dino_ai.models import ALL_MODELS
# 列出所有厂商
providers = sorted(set(m.provider for m in ALL_MODELS))
print(providers)
# 按厂商筛选
openai_models = [m for m in ALL_MODELS if m.provider == "openai"]
for m in openai_models[:5]:
print(f"{m.id}: {m.name}")
双协议厂商 — 同一个厂商的不同 API 协议作为独立 provider:
from dino_ai.models._xiaomi import Xiaomi # OpenAI 兼容协议
from dino_ai.models._xiaomi_anthropic import XiaomiAnthropic # Anthropic 协议
# 模型列表相同,API 协议和 base URL 不同
print(Xiaomi.MIMO_V2_5_PRO.api) # openai-completions
print(XiaomiAnthropic.MIMO_V2_5_PRO.api) # anthropic-messages
上下文与消息
from dino_ai import Context, UserMessage, AssistantMessage, ToolResultMessage, Tool, TextContent
# 简单对话
context = Context(
system_prompt="你是一个翻译助手。",
messages=[UserMessage(content="Translate 'hello' to Japanese")],
)
# 多轮对话
context = Context(messages=[
UserMessage(content="What is 2+2?"),
AssistantMessage(content=[TextContent(text="4")]),
UserMessage(content="And 3+3?"),
])
# 带工具定义
context = Context(
messages=[UserMessage(content="What's the weather in Tokyo?")],
tools=[
Tool(name="get_weather", description="Get weather for a city",
parameters={"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}),
],
)
流式事件
stream() 返回 EventStream,可异步迭代,事件类型包括:
| 事件 | 说明 |
|---|---|
StreamStart |
流开始 |
TextStart / TextDelta / TextEnd |
文本内容块 |
ThinkingStart / ThinkingDelta / ThinkingEnd |
思维链/推理 |
ToolCallStart / ToolCallDelta / ToolCallEnd |
工具调用 |
StreamDone |
流完成,携带最终 AssistantMessage |
StreamError |
流错误,携带 DinoError |
stream = client.stream(model, context)
async for event in stream:
match event:
case TextDelta(text=t):
print(t, end="", flush=True)
case ToolCallEnd(tool_call=tc):
print(f"\n工具调用: {tc.name}({tc.arguments})")
case StreamDone(message=msg):
print(f"\n用量: {msg.usage}")
# 或直接等待最终结果
message = await stream.result()
@tool 装饰器
从函数签名自动生成 Tool 对象:
from dino_ai import tool
from typing import Annotated
@tool
def get_weather(
city: Annotated[str, "城市名"],
units: Annotated[str, "温度单位"] = "celsius",
) -> str:
"""获取指定城市的天气。"""
return f"{city}: 25 {units}"
# 生成 Tool 对象用于 Context
t = get_weather.as_tool()
# Tool(name="get_weather", description="获取指定城市的天气。", parameters={...})
中间件
中间件在请求前后拦截,可组合使用:
from dino_ai import DinoClient, LoggingMiddleware, RetryMiddleware, CostGuardMiddleware
client = DinoClient(
providers=[...],
middleware=[
LoggingMiddleware(), # 记录请求日志
RetryMiddleware(max_retries=3), # 可重试错误自动重试
CostGuardMiddleware(max_cost_usd=1.0), # 累计费用超限后拒绝请求
],
)
自定义中间件实现 Middleware 协议:
from dino_ai import Middleware, MiddlewareChain, EventStream, Model, Context, StreamOptions
class TimingMiddleware:
def intercept(
self,
model: Model,
context: Context,
options: StreamOptions | None,
chain: MiddlewareChain,
) -> EventStream:
import time
start = time.monotonic()
stream = chain(model, context, options)
from dino_ai.builtin_middleware import _wrap_stream
return _wrap_stream(stream, on_done=lambda e: print(f"耗时: {time.monotonic() - start:.2f}s"))
结构化错误
所有厂商错误统一映射为 DinoError:
from dino_ai import ErrorCategory
stream = client.stream(model, context)
message = await stream.result()
if message.error:
match message.error.category:
case ErrorCategory.RATE_LIMITED:
print(f"被限流,{message.error.retry_after_ms}ms 后重试")
case ErrorCategory.CONTEXT_OVERFLOW:
print("上下文太长,需要截断")
case ErrorCategory.AUTH_FAILURE:
print("API key 无效")
case _:
print(f"错误: {message.error.message}")
12 种错误类别:CONTEXT_OVERFLOW · RATE_LIMITED · AUTH_FAILURE · QUOTA_EXCEEDED · INVALID_REQUEST · MODEL_NOT_FOUND · CONTENT_FILTERED · NETWORK_ERROR · SERVER_ERROR · ABORTED · PROVIDER_ERROR · UNKNOWN
跨厂商上下文传递
使用 transform_messages() 在不同厂商之间转移对话历史:
from dino_ai import transform_messages
# OpenAI 模型生成的回复(可能含 thinking block)
message = await client.complete("gpt-4o", context)
context.messages.append(message)
context.messages.append(UserMessage(content="继续"))
# 无缝切换到 Claude 继续对话
# transform_messages 自动处理 thinking block 格式差异
message = await client.complete("claude-sonnet-4-0", context)
SimpleStream — 统一推理级别
不同厂商的 thinking/reasoning 参数各不相同。stream_simple() 提供统一的 ThinkingLevel 抽象:
from dino_ai import SimpleStreamOptions, ThinkingLevel
# 用统一的方式请求推理
options = SimpleStreamOptions(reasoning=ThinkingLevel.HIGH)
# 同一套代码,不同模型自动适配
await client.complete_simple("gpt-5.1-codex", context, options) # OpenAI reasoning_effort
await client.complete_simple("claude-sonnet-4-0", context, options) # Anthropic budget_tokens
await client.complete_simple("gemini-2.5-pro", context, options) # Google thinking budget
支持的厂商
| 厂商 | API 类型 | Provider 类 |
|---|---|---|
| OpenAI | openai-completions |
OpenAICompletionsProvider |
| Anthropic | anthropic-messages |
AnthropicProvider |
| Google Gemini | google-generative-ai |
GoogleProvider |
| AWS Bedrock | bedrock-converse-stream |
BedrockProvider |
| Azure OpenAI | azure-openai-responses |
AzureOpenAIProvider |
| DeepSeek | openai-completions |
OpenAICompletionsProvider |
| Groq | openai-completions |
OpenAICompletionsProvider |
| xAI (Grok) | openai-completions |
OpenAICompletionsProvider |
| Mistral | openai-completions |
OpenAICompletionsProvider |
| Together | openai-completions |
OpenAICompletionsProvider |
| OpenRouter | openai-completions |
OpenAICompletionsProvider |
| Fireworks | anthropic-messages |
AnthropicProvider |
| Cerebras | openai-completions |
OpenAICompletionsProvider |
| HuggingFace | openai-completions |
OpenAICompletionsProvider |
| Xiaomi/MiMo | openai-completions / anthropic-messages |
双协议,自动提供两种 variant |
| 阿里云 (DashScope) | openai-completions |
OpenAICompletionsProvider |
| 百川 | openai-completions |
OpenAICompletionsProvider |
| 豆包 | openai-completions |
OpenAICompletionsProvider |
| Kimi | anthropic-messages |
AnthropicProvider |
| 阶跃星辰 | openai-completions |
OpenAICompletionsProvider |
| 智谱 | openai-completions |
OpenAICompletionsProvider |
| MiniMax | anthropic-messages |
AnthropicProvider |
| Moonshot | openai-completions |
OpenAICompletionsProvider |
| SiliconFlow | openai-completions |
OpenAICompletionsProvider |
OpenAI 兼容的厂商共用 OpenAICompletionsProvider,Anthropic 兼容的厂商共用 AnthropicProvider,通过内置 profile 自动适配各家差异(thinking 格式、max_tokens 字段名、usage 上报等)。
环境变量
各厂商的 API key 通过环境变量配置,也可在 StreamOptions.api_key 中传入:
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
GOOGLE_API_KEY=AIza...
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...
AWS_REGION=us-east-1
AZURE_OPENAI_API_KEY=...
DEEPSEEK_API_KEY=...
XAI_API_KEY=...
GROQ_API_KEY=...
MISTRAL_API_KEY=...
TOGETHER_API_KEY=...
OPENROUTER_API_KEY=...
开发
# 安装开发依赖
pip install -e ".[dev]"
# 运行测试
pytest
# 类型检查
mypy src/dino_ai
# 代码风格
ruff check src/ tests/
许可证
Project details
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 dino_ai_py-0.2.1.tar.gz.
File metadata
- Download URL: dino_ai_py-0.2.1.tar.gz
- Upload date:
- Size: 215.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
11a524c3594de9e91ae1152507310e144855529774d1eaa4f75e85fd20cee8be
|
|
| MD5 |
2bba0b2347af3cd8d7bb8af1d11716b7
|
|
| BLAKE2b-256 |
ec4ac2607295f2a54d09076046800d271f32fd2fb49d2cc8eba0ac314207d085
|
File details
Details for the file dino_ai_py-0.2.1-py3-none-any.whl.
File metadata
- Download URL: dino_ai_py-0.2.1-py3-none-any.whl
- Upload date:
- Size: 122.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8e68a1d972da20bb6345ca7986fdfc3c3a9a1094b4b0c0248e9304dad999a36a
|
|
| MD5 |
221e487ff10fef956efd3e03a5f4be57
|
|
| BLAKE2b-256 |
cd0181157680f64cbd2aca5fbd20495c9a6d8633e01d119e0426f4801a8149f3
|