my-llm-py 是一个 Python 异步 LLM 协议适配库,支持 OpenAI Chat、OpenAI Responses、Anthropic、Gemini 和 Ollama 五种协议自由组合。
用一种协议请求另一种协议的 API,按输入协议返回结果。标准协议直接转换,非标准协议通过配置兼容。
OpenAI Chat 格式输入 → 任意协议 API → OpenAI Chat 格式输出
消息、工具和流事件一并转换。更换上游 API,业务层继续使用原有协议收发数据。
主要功能与亮点
| 功能 | 说明 |
|---|---|
| 跨协议调用 | 标准协议直接转换,输入输出协议与上游 API 独立选择。 |
| 标准消息翻译 | 只翻译已识别的协议字段;工具参数、结果和 JSON Schema 等开放字典保留内部键值。 |
| 统一工具调用 | 转换工具定义、调用和结果;流式参数自动组装,响应成功结束后交付完整调用。工具由应用执行。 |
| 流式与完整回复 | 支持文本、可见思考增量;流结束后通过 stream.result 获取完整消息。 |
| 多模态转换 | 支持用户图片、工具结果图片与文本;目标无法表达时按语义损失策略处理。 |
| 配置兼容 | 非标准协议通过配置兼容,适配服务与模型的参数、字段路径及思考内容等差异,配置可拓展。 |
| 独立格式转换 | 无需调用模型,即可转换已支持的消息、工具和完整流事件列表。 |
| 中间模型直连 | 显式 stdio_protocol="middleware",用具体模型收发消息,跳过两端不需要的转换。 |
| 异步并发 | 复用官方 SDK 客户端,请求状态独立;支持取消、提前退出和资源清理。 |
| 协议扩展 | 实现四个抽象基类并注册,即可接入现有调用与转换流程。 |
输入接受标准字典或可转为字典的官方 SDK 对象,输出为所选输入协议的标准字典。
也可直接使用中间模型,输入 Message / FunctionTool,返回 AssistantMessage / MessageEvent。
文本、图片、思维链、工具与 usage 通过具体的 ProtocolMiddleWare 翻译;audio、file、用户 metadata 等未支持字段静默丢弃。
已支持字段无法表达时默认抛 SemanticLossError,可选择 ignore 或双向原位 hook,详见迁移指南。
Profile 用 Parameter 声明字段路径、默认值与 source,Config 控制代码分支;继承不能扩大固定中间协议的支持集合。
字段 None 继承配置;default_value=None 是实际值,已有 None 不会被默认值覆盖;跨协议来回翻译不保证无损。
跨协议转换受目标协议的表达能力限制,详见兼容边界。
新增协议通过 MyCodec.register(CodecFactory) 在启动时注册,协议默认值随 Codec 声明;
无需修改公共入口或协议枚举,详见接入指南。
参数路径统一使用 $. 前缀,例如 $.reasoning.effort;非标准请求参数通过 SDK 的 extra_body 等正式扩展入口配置。
完整生成输入和已解析配置统一经过 Codec,Decoder 区分客户端参数、请求参数与本地控制项;
连接配置按实例准备,请求配置按调用准备,Transport 负责官方 SDK 调用和响应关闭。
详细职责见架构文档。
安装
需要 Python 3.12+。
pip install 'my-llm-py[all]'
# 或 uv add 'my-llm-py[all]'
推荐使用 [all] 安装全部 SDK。也可以按需选择,支持用逗号组合多个 extra:
pip install 'my-llm-py[openai]'
pip install 'my-llm-py[anthropic]'
pip install 'my-llm-py[google-genai]'
pip install 'my-llm-py[ollama]'
pip install 'my-llm-py[openai,anthropic]'
pip install my-llm-py 仅安装核心依赖。每种输入、输出及上游协议都需要对应 SDK;
例如 Chat 输入调用 Anthropic API 需要 [openai,anthropic]。
中间模型 stdio 只需要安装上游对应 SDK。
未安装的协议不会注册,显式选择时会提示安装命令。
项目开发环境执行 uv sync 默认安装开发工具和全部 SDK。
仅安装指定 SDK 可使用 uv sync --no-default-groups --extra openai。
快速开始
以下示例使用 OpenAI Chat 格式调用 Anthropic Messages API,返回 Chat assistant 消息。
设置 ANTHROPIC_API_KEY 和 LLM_MODEL 环境变量后运行,模型 ID 填写服务支持的值。
import asyncio
import os
from my_llm import Attribute, Config, ModelProfile, MyLLM, Parameter, ProviderProfile
async def main():
async with MyLLM(
provider=ProviderProfile(
protocol=Config(default_value="anthropic"), # 实际请求的 API 协议
api_key=Parameter(
path="$.api_key",
default_value=os.environ["ANTHROPIC_API_KEY"],
),
),
model=ModelProfile(
model=Attribute(default_value=os.environ["LLM_MODEL"]),
max_tokens=Parameter(
path="$.max_tokens",
default_value=4096,
),
),
stdio_protocol="openai_chat", # 输入和输出均使用 Chat 协议
) as llm:
message = await llm.generate(messages=[{"role": "user", "content": "你好"}])
print(message["content"])
asyncio.run(main())
更换 provider.protocol 及对应连接、模型配置,即可切换上游 API;stdio_protocol 决定业务层使用的格式。
建议像示例一样显式指定 stdio_protocol。省略时仅在消息与工具整批唯一匹配一种协议时自动绑定;
零匹配或多种协议都匹配时抛出 MessageValidationError。普通 role/content 文本存在协议歧义,需要显式指定。
流式调用
在上例的 async with MyLLM(stdio_protocol="openai_chat", ...) 内调用下面的函数,流事件也按 Chat (openai_chat) 协议返回:
async def print_answer(llm):
stream = await llm.generate(
messages=[{"role": "user", "content": "你好"}],
stream=True,
)
async with stream:
async for event in stream:
for choice in event.get("choices", []):
print(choice["delta"].get("content", ""), end="", flush=True)
return stream.result
使用 async with 管理实例和流。传入工具前,在 ModelProfile 中设置
supports_function_calling=Config(default_value=True)。
独立格式转换
MyCodec 可离线转换协议格式,无需 API 密钥:
from my_llm import MyCodec
converted = MyCodec().convert(
[{"role": "user", "content": "你好"}],
input_format="openai_chat",
target_format="gemini",
)
print(converted["messages"])
# [{"role": "user", "parts": [{"text": "你好"}]}]
支持的协议
以下标识均可用于 ProviderProfile.protocol 和 stdio_protocol:
| 协议标识 | 对应 API |
|---|---|
openai_chat |
OpenAI Chat Completions 及兼容服务 |
openai_responses |
OpenAI Responses 及兼容服务 |
anthropic |
Anthropic Messages |
gemini |
Gemini GenerateContent |
ollama |
Ollama Chat |
同一实例的消息与工具使用同一种输入输出协议。
已通过端到端验证的协议组合
3×4 十二条真实 API 路径验证(2026-10-25),补跑后 36 个场景全部通过。
| 输入/输出协议 ↓ \ 上游 API → | Chat Completions | Responses | Anthropic Messages | Gemini Content | Ollama |
|---|---|---|---|---|---|
openai_chat |
✅ 已验证 | ✅ 已验证 | ✅ 已验证 | ➖ 离线验证 | ➖ 离线验证 |
openai_responses |
✅ 已验证 | ✅ 已验证 | ✅ 已验证 | ➖ 离线验证 | ➖ 离线验证 |
anthropic |
✅ 已验证 | ✅ 已验证 | ✅ 已验证 | ➖ 离线验证 | ➖ 离线验证 |
middleware |
✅ 已验证 | ✅ 已验证 | ✅ 已验证 | ➖ 离线验证 | ➖ 离线验证 |
gemini |
➖ 离线验证 | ➖ 离线验证 | ➖ 离线验证 | ➖ 离线验证 | ➖ 离线验证 |
ollama |
➖ 离线验证 | ➖ 离线验证 | ➖ 离线验证 | ➖ 离线验证 | ➖ 离线验证 |
每条路径覆盖文本汇总、流式增量与汇总一致性,以及工具调用、参数组装和工具结果回传。
汇总模式也通过上游 SSE 执行,本轮未单独验证供应商的 HTTP 非流式响应。
实测中 Chat、Anthropic 的关闭思考参数未完全阻止返回思考块。脚本默认 ignore,
目标无法表达的思维或签名按损失策略处理,思维不混入正文;正文和完整流式正文要求精确匹配。
接入配置、复跑方法、失败说明及补跑结果见 SenseNova 端到端测试指南。
安装最新的开发版本 (main 分支)
git clone https://github.com/JJJJSTIYYYY/my-llm.git
cd ./my-llm
uv sync --group dev --locked
uv run pytest -q
uv run ruff check src tests examples
uv run ruff format --check src tests examples
uv run python examples/local_demo.py
uv build
文档
项目采用 Apache License 2.0。
Metadata
Release files for my-llm-py 1.3.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 | |
|---|---|---|---|---|
| my_llm_py-1.3.0.tar.gz | 463.3 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| my_llm_py-1.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 601.0 kB
Release files / my_llm_py-1.3.0.tar.gz
| Download URL | my_llm_py-1.3.0.tar.gz |
|---|---|
| Size | 463.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1ecfe26578f1e55d02363f2ed831509521cb136df398b48f8c7a3d2bcee37fdb
|
|
BLAKE2b-256 checksum How to use checksums |
d1ba0ec9f84537f95ccc983d0b8ee4b0e286ce519bae68b28b3fecef87fd1aba
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.12
|
Release files / my_llm_py-1.3.0-py3-none-any.whl
| Download URL | my_llm_py-1.3.0-py3-none-any.whl |
|---|---|
| Size | 137.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3b472043150075c422135d00a7b84700f7f8a7b1e0e6e42d33ace1b396625d77
|
|
BLAKE2b-256 checksum How to use checksums |
2c0e051275205e756508dff6e53234a4abb2a78553888c85e6f0ab58707fc7f6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.12
|
Security for my-llm-py 1.3.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.