Observed since 21 Oct, 10:14 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
ai-coin
可插拔的 AI 接入层:统一多供应商/模型管理/连接测试/工具调用/结构化输出/流式/多模态(图音视频)
像游戏机的投币口:拧下来,装进任何工程就能用。 统一封装 API 接入 / 多供应商 / 模型管理 / 连接测试 / 工具调用 / 结构化输出 / 流式 / 多模态。
每次做聊天软件都要重写一遍"配置 API 提供商 + 发请求 + 解析响应"的代码,
而且 AI 经常不听话(返回的 JSON 残缺、裹在 markdown 里、被截断、tool call 参数解析崩掉)。
ai_coin 把这一切封装成一个库,调用方只需要几个方法。
pip install ai-coin # PyPI 发布后
# 或直接拷 ai_coin/ 目录进工程
from ai_coin import AICoin
ai = AICoin("config.db") # 配置存 SQLite,随文件走,可迁移
# ① 加供应商:用内置预设一键填充(DeepSeek/通义/Kimi/智谱/Claude/Gemini/ollama… 共 16 家)
ai.add_provider("DeepSeek", preset="deepseek", api_key="sk-xxx")
# 也可手填:
# ai.add_provider("自定义", "https://api.xxx.com/v1", "sk-xxx", api_type="openai_compatible")
# ② 管理模型:拉取或手工添加(预设已自动建好默认模型)
pid = ai.list_providers()[0].id
ai.pull_models(pid) # 从供应商 GET /models 拉取自动入库
ai.add_model(pid, "my-custom-model") # 手工加
# ③ 连接测试(可选哪个测哪个,结果写回库)
ok, msg = ai.test_model(1)
# ④ 一句话对话
reply = ai.chat(1, "你好")
add_provider(preset=...) 一键配置):
DeepSeek / OpenAI / Kimi(Moonshot) / 智谱GLM / 通义千问 / 豆包(火山) / MiniMax /
阶跃星辰 / Groq / xAI(Grok) / Mistral / Together / Ollama(本地) / Anthropic / Gemini / Azureapi_type="openai_compatible")openai_compatible / azure / anthropic / geminiai.list_presets() # 查看所有预设
| 方法 | 说明 |
|---|---|
add_provider(name, base_url, api_key, api_type, preset, models_endpoint, extra_headers) | 加供应商 |
list_providers() / get_provider(id) / update_provider(id, **kw) / remove_provider(id) | 供应商增删改查 |
pull_models(provider_id) | 从供应商拉取模型列表自动入库(去重) |
add_model(provider_id, name, capabilities={}) / remove_model(id) | 添加模型(upsert:已存在则更新显示名并合并 capabilities) |
list_models(provider_id=None) | 查询模型 |
test_model(model_id) / test_all() | 单个/全部模型连接测试 |
chat(model_id, messages, system, tools, json_schema, ...) | 对话(含重试) |
stream_chat(model_id, messages, ...) | 流式对话,逐段返回增量文本 |
ai.add_provider("DeepSeek", preset="deepseek", api_key="sk-xxx")
# → base_url / api_type 自动填好,deepseek-chat、deepseek-reasoner 自动入库
def get_weather(city: str) -> str:
return f"{city} 今天晴,25°C"
reply = ai.chat(model_id, "北京天气怎么样?", tools=[{
"name": "get_weather",
"description": "查询城市天气",
"parameters": {"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"]},
"function": get_weather, # 传 Python 函数,框架自动执行并回传
}])
框架自动处理多轮 tool_call → 执行 → 回传,直到 AI 输出最终回答。 工具参数解析失败不会崩,会作为错误回传给模型。
story = ai.chat(model_id, "编个3行小故事", json_schema={
"type": "object",
"properties": {"title": {"type": "string"}, "lines": {"type": "array"}},
"required": ["title", "lines"],
})
# 返回校验通过的 dict —— 不是可能残缺的文本
内部流程:response_format 强制 JSON → 从响应里捞 JSON(剥 markdown / 修复截断 /
括号补齐)→ 按 schema 校验,不合格自动带错误回喂 AI 重试,最多 4 轮。
for chunk in ai.stream_chat(model_id, "讲个故事"):
print(chunk, end="", flush=True)
推理模型(DeepSeek-R1、o1 系列等)的思考过程在 reasoning_content 里,
普通 chat() 会丢掉它。用 chat_full / stream_chat_full 拿原始输出:
# 非流式:完整结果(含思考过程 + token 用量)
full = ai.chat_full(model_id, "1+1=?")
full.content # 最终答案
full.reasoning_content # 思考过程
full.usage # token 用量
# 流式:原始增量块
for chunk in ai.stream_chat_full(model_id, "推理题"):
print(chunk.reasoning, end="") # 思考过程增量
print(chunk.content, end="") # 正文增量
旧接口 chat() / stream_chat() 仍只返回正文,完全向后兼容。
from ai_coin import image_part, audio_part, video_part, media_part
reply = ai.chat(model_id, {
"role": "user",
"content": [
{"type": "text", "text": "这张图里有什么?"},
image_part("photo.png"), # 图片(本地或 URL)
# audio_part("record.wav"), # 音频(本地文件)
# video_part("clip.mp4"), # 视频(本地或 URL)
# media_part("photo.png") # 或按扩展名自动识别类型
],
})
*_part() 返回 OpenAI 兼容的媒体片段;Anthropic 适配器会自动转成
它的 content block 格式(image / document),调用方无需关心协议差异。
add_model 是 upsert 语义,可安全重复调用:
ai.add_model(pid, "deepseek-chat", capabilities={"tools": True})
# 再次添加:补充 vision,不覆盖已有 tools
ai.add_model(pid, "deepseek-chat", capabilities={"vision": True})
# → capabilities == {"tools": True, "vision": True}
add_provider(preset=...) 预建的默认模型也一样——后续传入真实
display_name / capabilities 会合并进去,不会因为同名被忽略
(此前是 INSERT OR IGNORE,业务元数据会被静默丢掉,需先删后插绕过;
现在原生支持,无需绕过)。
ai.add_provider("定制", "https://api.xxx.com/v1", "sk", api_type="openai_compatible",
models_endpoint="https://api.xxx.com/v1/models", # 自定义拉模型URL
extra_headers={"X-Project": "my-app"}) # 额外请求头
from ai_coin import RateLimitError, AuthError, ContextOverflowError, TimeoutError
429 → 限流、401/403 → 鉴权失败、上下文超长 / 请求超时 / 网络错误,全部区分开。
ai_coin/
├── __init__.py # from ai_coin import AICoin
├── models.py # Provider / Model / ChatMessage 数据模型
├── store.py # SQLite 持久化(零依赖,单文件,线程安全,老库自动迁移)
├── providers.py # 供应商/模型管理 + 拉取列表 + 连接测试
├── presets.py # ★ 16 家供应商预设
├── adapters.py # OpenAI兼容 / Azure / Anthropic / Gemini 适配器(含流式)
├── json_fixer.py # JSON 提取 / 截断修复
├── core.py # AICoin 主类:对话/工具循环/结构化输出/重试/流式
└── errors.py # 错误分类
chat() 一个方法搞定适配、重试、工具、结构化输出本地开发用可编辑安装,改源码即时生效,不用手动同步 site-packages:
uv venv && uv pip install -e ".[dev]"
pytest -v # 48 个测试:mock 服务器端到端覆盖
注意:如果之前用
pip install ai-coin(非可编辑)装过,site-packages 里是 独立副本,源码改动不会生效。改用pip install -e ./uv pip install -e .后,import 到的就是源码目录,无需手动同步。
Download counts and popularity will appear here as the collector receives them.
| Package / target | Requirement | Relationship |
|---|---|---|
requestsEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | requests>=2.28 | runtime |
pytestEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | pytest>=7; extra == "dev" | runtime |
pytest-covEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | pytest-cov; extra == "dev" | runtime |
bdist_wheel · 29.18 KB · Python py3
sdist · 34.69 KB · Python source
Shared repository data · GitHub
Public sponsor counts have not been collected. These signals belong to the repository owner.
github · 1 observations shown. Missing days are unknown.
| Date / source period | Value | Observed at |
|---|---|---|
| 2026-10-21 | 0 | 2026-10-21T19:30:09.8989184Z |
Observed since 21 Oct, 10:14 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package_version:3df8377ee78dd2880087b3984d76d3d68bde15eccd0b2c9b38e80bc1a2ea2e1d