Skip to main content

Unified LLM API with automatic model discovery, structured errors, and composable middleware

Project description

## 🦖 Dino Ai

统一的 Python LLM API。多厂商、中间件管道、异步优先、零必选依赖。

Built with DeepSeek V4 Pro — 本项目代码由 DeepSeek V4 Pro 辅助生成。

English

特性

  • 统一接口 — 一套 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/

许可证

Apache-2.0

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

dino_ai_py-0.2.0.tar.gz (215.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dino_ai_py-0.2.0-py3-none-any.whl (122.3 kB view details)

Uploaded Python 3

File details

Details for the file dino_ai_py-0.2.0.tar.gz.

File metadata

  • Download URL: dino_ai_py-0.2.0.tar.gz
  • Upload date:
  • Size: 215.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for dino_ai_py-0.2.0.tar.gz
Algorithm Hash digest
SHA256 3b6d8cd3cd64b7fb6a44e932bc0b5aeffd574e8b2c0dedd2837a8e61dc923be1
MD5 ada106e8f888805e0a2fe0eff763fada
BLAKE2b-256 d28c78a22016e4bd02a7b75a4947828705c4d2d7ff2305f9a9be842c1f5b33e1

See more details on using hashes here.

File details

Details for the file dino_ai_py-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: dino_ai_py-0.2.0-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

Hashes for dino_ai_py-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 267f50dba8a2feb48bf1e912c078d75d7a4ccf74357a49eca3326ecd5cb21e62
MD5 a29d545924a2985a7af019ba08867e0a
BLAKE2b-256 cd47c8fb8964728d68937477790fd0b11b5b4546f8298b050b2392cf8fca18b3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page