LiteLLM Rust 核心扩展指南:如何为 litellm-rust 新增一个 Provider 或 Route
在 LiteLLM 的 Rust 核心(litellm-rust/)中新增一个 provider 或 route 有一套固定的工程范式:每个 route 的完整实现都收敛在 crates/core/src/<route>/ 目录下,宿主(axum 网关 ai-gateway 或 Python 桥 python-bridge)只负责调用该 route 的唯一入口函数。本文基于仓库中的 ADDING_A_PROVIDER.md 与 CLAUDE.md,结合源码中的参考实现,讲清楚一个 route 的四个组成部分、provider 转换契约的接口细节,以及新增代码必须遵守的编码规范与检查命令,读完你可以独立完成"Rust 侧新 provider 接入 + Python 薄分发"的全流程。
整体架构:为什么一切都在 litellm-core 里
从 CLAUDE.md 的 Crate 划分看,整个 Rust 工作区由四个 crate 组成:
litellm-core:就是 Rust 版的 LiteLLM SDK,负责真正发起 LLM 调用。litellm.messages()的 Rust 等价物是litellm_core::messages::messages(request).await—— 你传入请求,它完成 provider 解析、请求/响应转换、HTTP 调用,返回一个类型化的非流式响应;litellm-ai-gateway:架在 core 前面的 HTTP/WebSocket 服务器,其 axum 路由只读取 HTTP 请求、挑选部署(deployment),然后调用 core 的入口函数;litellm-python-bridge:把 core 暴露给 Python SDK,负责把 Python 对象 marshal 成 Rust 结构体后调用同一个入口函数;litellm-python-interop:存放 Python 面向的 PyO3 原语,供其他 Rust 代码共享。
一个关键边界规则:crate 是"层"或"共享基座",不是"路由"——新增功能时应该加模块,而不是新建 crate。route 级 Rust 结构与 Python 侧的职责一一对应:core/src/<route>/ 端到端拥有该 route,包括以 route 命名的公开入口函数(mod.rs)、请求/响应类型(types.rs)、provider 模板 trait(transformation.rs)、provider/鉴权/URL 解析(prepare.rs)、共享 HTTP 客户端(client.rs)、真正执行调用的 handler(handler.rs)。参考实现是 core/src/messages。
四步法:新增一个 provider/route 的完整流程
ADDING_A_PROVIDER.md 把新增工作归纳为四个部分,下面逐一展开,并给出源码级依据。
第 1 步:入口函数(Entrypoint)
每个 route 的 mod.rs 中定义 pub async fn <route>(request) -> CoreResult<Response>,它是 Rust 版的 litellm.<route>();如果该 route 支持流式,再提供一个 <route>_stream 变体。这是宿主唯一接触的东西——ai-gateway 和 python-bridge 都只调用 core 入口函数。
以参考实现 messages/mod.rs 为例:
pub async fn messages(request: MessagesRequest<'_>) -> Result<AnthropicMessagesResponse, Error> {
execute_messages_provider_call(request).await
}
pub async fn messages_stream(request: MessagesRequest<'_>) -> Result<reqwest::Response, Error> {
execute_messages_provider_stream(request).await
}
非流式入口返回类型化响应;流式入口则把上游的原始 reqwest::Response 直接交回给宿主,由宿主负责把它拼接(splice)给自己的调用方。宿主因此不持有任何 provider 逻辑,流式也保持同样的形状。
Python 桥的实际调用形态可见 python-bridge/src/routes/messages.rs:它先通过 bridge_route! 宏声明 messages/amessages 两个绑定、校验必填参数(model、body)与可选参数(api_key、api_base、custom_llm_provider、extra_headers、timeout_seconds),然后把字段组装成 MessagesRequest 并调用 run_messages(...)(即 litellm_core::messages::messages)。
第 2 步:转换契约(Transform Contract)
<route>/transformation.rs 定义一个 …ProviderConfig trait,它描述"URL 构建 + 请求/响应转换"的契约,相关类型放在 types.rs。以 messages/transformation.rs 中的 AnthropicMessagesProviderConfig 为例,它的必备方法与可选覆写包括:
| 方法 | 职责 | 默认行为 |
|---|---|---|
complete_url(api_base, model, env_lookup) |
构建最终请求 URL | 无默认,必须实现 |
resolve_api_key(api_key, env_lookup) |
解析 API key(参数优先,其次环境变量) | 无默认,必须实现 |
auth_strategy() |
决定鉴权头 | MessagesAuthStrategy::Header("x-api-key") |
accepts_bearer_auth() |
是否接受 Bearer 鉴权 | false |
default_headers() |
附加的默认请求头 | anthropic-version + content-type |
transform_request(request) |
请求体转换 | 原样透传 |
transform_response(model, response) |
响应转换 | 原样透传 |
其中 env_lookup 是一个闭包参数(&dyn Fn(&str) -> Option<String>),由 prepare.rs 注入 |key| std::env::var(key).ok()。这正对应 CLAUDE.md 中的边界规则:core 中允许读环境变量仅限于 route 的 prepare.rs 内的凭证兜底(即 env_lookup 闭包),镜像的是 Python SDK 在调用方没传 key 时回退环境变量的行为;其余一切配置都由宿主解析后传入。
第 3 步:Provider 配置实现
在 crates/core/src/providers/<provider>/<route>/transformation.rs 中把该 trait 实现为一个 const <PROVIDER>_<ROUTE>_CONFIG,目录结构镜像 Python 侧的 provider 树(litellm/llms/<provider>/<route>/),并必须补齐 parity 单元测试。
providers/anthropic/messages/transformation.rs 是标准范本:
const ANTHROPIC_API_KEY_ENV: &str = "ANTHROPIC_API_KEY";
const ANTHROPIC_API_BASE_ENV: &str = "ANTHROPIC_API_BASE";
const DEFAULT_ANTHROPIC_API_BASE: &str = "https://api.anthropic.com";
const MESSAGES_PATH_SUFFIX: &str = "/v1/messages";
pub struct AnthropicMessagesConfig;
pub const ANTHROPIC_MESSAGES_CONFIG: AnthropicMessagesConfig = AnthropicMessagesConfig;
URL 构建逻辑(complete_anthropic_url)展示了几个值得注意的细节:
- 参数
api_base优先,其次环境变量ANTHROPIC_API_BASE,最后落到默认https://api.anthropic.com; - 空白值视为不存在:
non_empty辅助函数会先trim再过滤空串,这对应 CLAUDE.md 的生产标准"把空或纯空白凭证、URL、配置值视为缺失"; - 若 base 已经以
/v1/messages结尾则原样返回,否则追加后缀,并先trim_end_matches('/')去掉尾部斜杠。
API key 解析遵循"参数 → 环境变量 → 报错"三级顺序,缺失时返回类型化的 Error::Auth 而非 panic。同文件内还附带了一组单元测试(默认 URL、自定义 base 追加后缀、完整端点不重复追加、env 兜底、key 优先级),这正是文档所要求的"add parity unit tests"的落地形式。
第 4 步:Prepare + Handler
prepare.rs 负责"决定调用什么、怎么认证",handler.rs 负责"真正发请求"。以 messages 路由为例,调用链是:
prepare 阶段(messages/prepare.rs):
get_custom_llm_provider(model, custom_llm_provider)解析 provider/model,解析失败返回Error::InvalidProvider;- 通过工厂函数拿到对应 provider 的
…ProviderConfig; validate_environment构建上游请求头:若调用方已通过extra_headers自带鉴权头(命中auth_strategy的头名,或在允许 Bearer 时自带Authorization: Bearer),则跳过 key 解析;否则调用resolve_api_key并按策略生成authorization: Bearer <key>或自定义头(如x-api-key);最后按default_headers()补齐缺失的默认头;- 将
request.body反序列化为类型化请求,经transform_request转换后再序列化; config.complete_url生成最终 URL;- 打包为
ProviderMessagesRequest { provider, model, config, url, body, upstream_headers, timeout }交给 handler。
其中第 2 步的"工厂函数"是新增 provider 时的关键挂接点——messages/common_utils.rs:
pub(super) fn messages_provider_config(
provider: &str,
) -> Option<&'static dyn AnthropicMessagesProviderConfig> {
match provider {
"anthropic" => Some(&ANTHROPIC_MESSAGES_CONFIG),
"azure_ai" => Some(&AZURE_ANTHROPIC_MESSAGES_CONFIG),
_ => None,
}
}
新增一个支持 messages 路由的 provider,本质上就是在这里加一个 match 分支并指向你实现的那个 const <PROVIDER>_MESSAGES_CONFIG。
handler 阶段(messages/handler.rs):
- 调用
prepare_provider_request; - 用共享复用的
http_client()发起 POST(遵守"复用 HTTP 客户端、禁止每请求新建客户端"的网络 I/O 规则),逐条附加上游请求头,并在存在timeout时设置请求级超时; - 非 2xx 状态码返回
Error::Http { status, body },且错误 body 经truncate_error_body截断/脱敏后才越过边界——对应"错误信息必须有用但最小化数据"的标准; - 成功时把响应 JSON 反序列化为
AnthropicMessagesResponse,再经transform_response归一化后返回。
流式变体 execute_messages_provider_stream 在成功时直接把原始 reqwest::Response 返回给宿主;非流式与流式的差异由此清晰:core 始终拥有 provider 调用,宿主只决定如何消费响应。
编码规范:先找基座,不要复制粘贴
ADDING_A_PROVIDER.md 的 "Coding standards" 部分(与 CLAUDE.md 的 Provider Coding Standards 同源)给出了三条硬性纪律:
- 写新逻辑之前先找可继承的基座。当变更是"同一行为再支持一个 provider/endpoint/integration"时,代码库几乎总已存在共享抽象——例如 Python 侧
litellm/llms/base_llm/的 providerBaseConfig转换类、litellm_core_utils/的共享辅助函数、类型化请求/响应模型、工厂函数。先用搜索找到它,然后通过继承或组合添加新变体,只覆写真正不同的部分(模型名、参数映射、鉴权)。 - 绝不复制现有实现再原地修改,也绝不手写一个基座已提供逻辑的平行版本。如果你发现自己写下"第二个副本",停下来抽取基座:把共享形态放在一处,让两个调用点都变成它的薄变体。好抽象的检验标准是:添加下一个 provider 只需要几行声明式代码,而不是一整个重复流程的新文件。
- 只在行为真正不同时偏离基座,并在 PR 中明确说明偏离原因。
此外,CLAUDE.md 补充了若干与新增 provider 直接相关的生产标准:
- parity 必须靠测试证明:每个 provider 转换都要有单元测试,覆盖支持参数过滤、请求体形状、响应归一化、缺失/null 字段、错误输入;
- 通过 Python 暴露 Rust 路径时,需补 Python 测试证明 disabled / enabled / bridge 不可用回退三种行为;
- 对用户/provider 输入避免 panic,返回类型化错误,由宿主映射为 Python 异常或 HTTP 响应;
- 常量(魔法数、固定字符串)统一放 crate 级
constants.rs(如litellm-rust/crates/core/src/constants.rs),是 Pythonlitellm/constants.py的 Rust 镜像; - 关于 Rust-only 新 provider:允许不写 Python 参照实现,此时 Python 接口是无回退的薄分发(在
litellm/llms/<provider>/<route>/下建薄 dispatch 类,绝不把 provider 分发塞进litellm/main.py),并在 PR 中显式声明 rust-only 选择。
收尾:注册模块与跑检查
新增文件后还有两件事(见 ADDING_A_PROVIDER.md 末尾的 "Calling" 小节):
- 在
lib.rs/mod.rs中注册新模块。core 的模块清单在 core/src/lib.rs,例如pub mod messages;、pub mod providers;等,新增 route 或 provider 模块后需在此(或对应的子mod.rs)声明,使其成为litellm_core公开 API 的一部分。 - 永远不要让宿主直连 provider。文档明确写着:宿主调用 core 入口——Python 桥和
ai-gateway路由服务都调用litellm_core::messages::messages——绝不要往ai-gateway里加 provider handler。
然后运行 CLAUDE.md "Checks" 一节列出的命令(与 CI 对 litellm-rust/ 下变更执行的检查一致):
cd litellm-rust
cargo fmt --check
cargo clippy --workspace --all-targets -- -D warnings
cargo clippy -p litellm-core --all-targets --features bedrock-auth -- -D warnings
# ai-gateway 二进制与服务端代码在 `server` feature 之后
cargo clippy -p litellm-ai-gateway --all-targets --all-features -- -D warnings
cargo test --workspace
cargo test -p litellm-core --features bedrock-auth
# `auth`、`routes`、`state`、`realtime` 测试只存在于 `server` feature 下
cargo test -p litellm-ai-gateway --features server
格式上,rustfmt 默认风格即官方 Rust 风格指南,CI 对每个 PR 跑 cargo fmt --check,不要自行另配 rustfmt.toml。
小结
为 litellm-rust 接入新 provider 的路径可以概括为一句话:入口函数收敛在 core/src/<route>/mod.rs,转换契约定义在同目录的 transformation.rs,provider 差异实现为 providers/<provider>/<route>/transformation.rs 里的一个 const 配置并注册进工厂函数,prepare 负责解析与鉴权、handler 负责共享客户端调用,宿主永远只调入口函数。配合"先找基座、测试证明 parity、检查命令全绿"的三条纪律,新增一个 provider 的增量成本被压缩为几行声明式代码与一组单元测试。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00