ai-model-unified-sdk
v0.1.1
Published
Unified provider SDK demo with native Node.js adapters.
Downloads
20
Maintainers
Readme
ai-model-unified-sdk
背景
在接入多家大模型服务时,不同厂商通常在以下方面存在差异:
- 调用入口和参数命名不一致
- 流式输出格式不同(SSE 字段、增量内容位置不同)
- 鉴权方式、模型名称、错误结构存在差别
这些差异会让上层业务代码重复处理适配逻辑,导致开发效率下降、维护成本升高。
本项目旨在提供一个统一的 SDK 抽象层,屏蔽底层厂商差异,让业务方通过一致接口完成模型调用。
设计思路
本项目采用“统一入口 + 厂商适配 + 协议约束”的分层设计:
- 统一入口层:对外暴露稳定 API(如统一创建模型实例、统一聊天调用方法)。
- 工厂层:根据 provider 配置动态创建具体适配器,避免业务代码直接依赖某个厂商实现。
- 适配器层:将统一请求转换为厂商请求,再将厂商响应规范化为统一输出。
- 核心能力层:沉淀通用能力,如流式处理、SSE 解析、错误处理和环境变量读取。
- 规范层:通过 provider spec 明确适配器契约,保证新增厂商时行为一致、扩展可控。
整体目标是:
- 对业务透明:切换模型服务尽量不改业务代码
- 对扩展友好:新增厂商仅需新增适配器并遵循规范
- 对维护稳定:核心逻辑集中复用,减少重复实现
设计模式
项目中主要体现了以下设计模式:
1) 工厂模式(Factory Pattern)
- 通过工厂模块按
provider选择并创建对应适配器实例。 - 优点:将“对象创建”与“对象使用”解耦,业务层无需了解具体类名和初始化细节。
2) 适配器模式(Adapter Pattern)
- 每个厂商的适配器负责协议转换:统一输入 -> 厂商格式,厂商输出 -> 统一格式。
- 优点:隔离外部差异,统一上层调用方式,降低供应商切换成本。
3) 策略模式(Strategy Pattern)
- 不同 provider 的处理逻辑可视为可替换策略,在运行时根据配置切换。
- 优点:避免大量
if/else侵入业务流程,增强可扩展性与可测试性。
4) 模板方法思想(Template Method, 轻量体现)
- 统一调用流程保持稳定(参数准备 -> 发起请求 -> 解析响应 -> 返回结果),差异步骤在适配器中重写。
- 优点:保证主流程一致性,同时允许局部差异化实现。
使用方法
1) 安装与运行前提
- 运行环境:
node >= 18 - 包名:
ai-model-unified-sdk - 安装(示例):
npm i ai-model-unified-sdk - 同时支持
require(CommonJS)和import(ESM)
2) 配置环境变量(推荐)
项目内已有示例文件 .env.example,你需要复制并填写为 .env(或放到 demo/.env)。
至少要为你所选的 DEMO_PROVIDER 对应的 provider 配齐以下字段:
alibaba:ALIBABA_API_KEY、ALIBABA_BASE_URL、ALIBABA_MODELbaidu:BAIDU_API_KEY、BAIDU_BASE_URL、BAIDU_MODELzhipu:ZHIPU_API_KEY、ZHIPU_BASE_URL、ZHIPU_MODEL
说明:
DEMO_PROVIDER默认为alibaba- SDK 在运行时会读取当前工作目录下的
.env(或demo/.env),并作为默认配置
3) 用 SDK 进行流式对话(streamChat)
SDK 目前以“单轮 prompt -> 一次对话”的方式工作(底层会把 prompt 作为 messages: [{ role: 'user', content: prompt }] 发送)。
下面示例演示如何接收流式事件并打印文本增量:
require('dotenv').config();
const { streamChat } = require('ai-model-unified-sdk');
async function main() {
const stream = await streamChat({
provider: 'zhipu',
prompt: '用两句话介绍什么是统一模型网关。',
// model: 'glm-4-flash', // 可选:优先 options.model,其次环境变量,再次默认值
});
for await (const event of stream) {
if (event.type === 'text-delta') {
process.stdout.write(event.text);
}
if (event.type === 'finish') {
console.log(`\n[finish] reason=${event.reason}`);
}
if (event.type === 'usage') {
console.log('\n[usage]', event.usage);
}
}
}
main().catch(err => {
console.error('failed:', err?.message ?? err);
process.exitCode = 1;
});如果你使用 ESM(例如 .mjs 文件,或项目 package.json 中 type=module),可直接使用 import:
import { streamChat } from 'ai-model-unified-sdk';
const stream = await streamChat({
provider: 'zhipu',
prompt: '请用一句话介绍统一模型 SDK。',
});
for await (const event of stream) {
if (event.type === 'text-delta') process.stdout.write(event.text);
}流式事件 event.type 取值:
text-delta:增量文本片段,字段:event.textfinish:结束事件,字段:event.reason(例如stop或厂商的 finish_reason)usage:用量信息(透传厂商返回的json.usage),字段:event.usage
4) 辅助方法:streamChatToStdout
如果你只想“边生成边输出”,可以直接用 streamChatToStdout:
require('dotenv').config();
const { streamChatToStdout } = require('ai-model-unified-sdk');
async function main() {
const output = await streamChatToStdout({
provider: 'alibaba',
prompt: '用简洁中文介绍你自己,并给出三条能力要点。',
});
console.log('\nfinishReason:', output.finishReason);
console.log('usage:', output.usage);
}
main().catch(err => {
console.error('failed:', err?.message ?? err);
process.exitCode = 1;
});5) 命令行 demo
包内定义了命令 ai-model-unified-sdk-demo(底层会调用 streamChatToStdout)。
示例:
npx ai-model-unified-sdk-demo --provider zhipu --model glm-4-flash --prompt "你好,帮我写一段介绍。"也可以直接用本仓库运行(假设在仓库根目录):
node index.js --provider zhipu --model glm-4-flash --prompt "你好,帮我写一段介绍。"可扩展建议
- 新增 provider 时,优先复用
src/core与src/utils的通用能力。 - 在
src/spec/provider-spec.js中补充能力声明,保持契约清晰。 - 为新适配器补充最小可运行示例,验证普通调用与流式调用均可工作。
目录参考
当前项目核心目录可理解为:
src/factory:统一创建入口src/adapters:各厂商适配实现src/core:通用核心能力(如流式会话)src/spec:适配协议约束src/utils:环境变量、SSE 等通用工具examples:示例调用
目标总结
该 SDK 的核心价值是:以统一接口承载多模型服务接入复杂度,让上层业务聚焦在产品能力而非供应商细节。
