@autolabz/llmapi-sdk
v0.1.9
Published
AutoLab LLM API SDK - typed client for llmapi-service
Readme
@autolabz/llmapi-sdk
AutoLab LLM API SDK - 用于 llmapi-service 的类型化客户端
安装
npm install @autolabz/llmapi-sdk兼容性
✅ 完全支持 Node.js ESM 模式
本包已针对 Node.js ESM 规范进行优化,可以在 "type": "module" 的项目中无缝使用,无需任何额外配置或运行时工具(如 tsx)。
// package.json
{
"type": "module"
}// 可以直接使用 ESM 导入
import { createLLMClient } from '@autolabz/llmapi-sdk';快速开始
import { createLLMClient } from '@autolabz/llmapi-sdk';
const client = createLLMClient({
baseURL: 'https://api.autolab.example.com/llmapi',
auth: {
getAccessToken: () => localStorage.getItem('access_token'),
getClientId: () => 'your-client-id',
refreshAccessToken: async () => {
// 实现 token 刷新逻辑
return newToken;
},
onUnauthorized: () => {
// 处理未授权情况
}
}
});
// 非流式调用 - 获取完整响应对象
const response = await client.chat({
model: 'gpt-4o-mini-2024-07-18',
messages: [{ role: 'user', content: 'Hello!' }]
});
// 非流式调用 - 直接获取内容字符串
const content = await client.getChatContent({
model: 'gpt-4o-mini-2024-07-18',
messages: [{ role: 'user', content: 'Hello!' }]
});
console.log(content); // "Hello from AutoLab! How can I assist you today?"
// 流式调用
await client.chatStream(
{
model: 'gpt-4o-mini-2024-07-18',
messages: [{ role: 'user', content: 'Hello!' }],
stream: true
},
{
onMessage: (data) => console.log(data),
onDone: () => console.log('Done'),
onError: (err) => console.error(err)
}
);API 方法
client.health()
健康检查接口。
返回值:
{
status: 'ok' | 'degraded' | 'down';
version?: string;
time?: string;
db?: 'ok' | 'error';
latencyMs?: number;
}client.chat(request)
非流式聊天接口。
请求参数:
{
model: string;
messages: Array<{ role: string; content: any }>;
temperature?: number;
max_tokens?: number;
}返回形式:
返回一个 Promise,resolve 为完整的聊天响应对象:
{
"id": "chatcmpl-CWlq5L6lqXNG3OFmDOcY0pNg4PfjV",
"object": "chat.completion",
"created": 1761926425,
"model": "gpt-4o-mini-2024-07-18",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello from AutoLab! How can I assist you today?",
"refusal": null,
"annotations": []
},
"logprobs": null,
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 13,
"completion_tokens": 13,
"total_tokens": 26,
"prompt_tokens_details": {
"cached_tokens": 0,
"audio_tokens": 0
},
"completion_tokens_details": {
"reasoning_tokens": 0,
"audio_tokens": 0,
"accepted_prediction_tokens": 0,
"rejected_prediction_tokens": 0
}
},
"system_fingerprint": "fp_efad92c60b"
}响应字段说明:
id: 聊天完成的唯一标识符object: 对象类型,固定为"chat.completion"created: Unix 时间戳,表示创建时间model: 使用的模型名称choices: 生成的回复列表index: 回复的索引message: 消息对象role: 角色,通常为"assistant"content: 生成的内容refusal: 拒绝信息(如果有)annotations: 注释列表
logprobs: 对数概率信息finish_reason: 完成原因("stop"表示正常结束)
usage: Token 使用统计prompt_tokens: 提示词使用的 token 数completion_tokens: 生成内容使用的 token 数total_tokens: 总 token 数prompt_tokens_details: 提示词 token 详情completion_tokens_details: 生成 token 详情
system_fingerprint: 系统指纹
client.getChatContent(request)
便捷方法:直接获取聊天内容字符串,而不是完整的响应对象。
请求参数:
{
model: string;
messages: Array<{ role: string; content: any }>;
temperature?: number;
max_tokens?: number;
}返回形式:
返回一个 Promise,resolve 为聊天内容字符串:
const content = await client.getChatContent({
model: 'gpt-4o-mini-2024-07-18',
messages: [{ role: 'user', content: 'Hello!' }]
});
// content = "Hello from AutoLab! How can I assist you today?"说明:
- 这是对
client.chat()的封装,内部调用chat()方法 - 自动提取
response.choices[0].message.content字段 - 如果内容不存在,返回空字符串
"" - 适用于只需要内容字符串而不关心其他元数据的场景
client.chatStream(request, handlers)
流式聊天接口。
请求参数:
{
model: string;
messages: Array<{ role: string; content: any }>;
stream: true; // 必须为 true
temperature?: number;
max_tokens?: number;
}处理器参数:
{
onEvent?: (line: string) => void; // 原始 SSE 事件行
onMessage?: (data: any) => void; // 解析后的 JSON 数据
onDone?: () => void; // 流结束时调用
onError?: (err: Error) => void; // 错误处理
}返回形式:
流式返回通过 Server-Sent Events (SSE) 格式推送,每条消息以 data: 开头,后跟 JSON 数据。通过 onMessage 回调接收解析后的数据。
数据流示例:
data: {"id":"","object":"","created":0,"model":"gpt-4o-mini-2024-07-18","choices":[]}
data: {"id":"chatcmpl-CWvlnHBwh3VlWjxyX2QHVu9N1JvIO","object":"chat.completion.chunk","created":1761964599,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_efad92c60b","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":{},"obfuscation":"lL"}
data: {"id":"chatcmpl-CWvlnHBwh3VlWjxyX2QHVu9N1JvIO","object":"chat.completion.chunk","created":1761964599,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_efad92c60b","choices":[{"index":0,"delta":{"content":"Hello"},"logprobs":null,"finish_reason":null}],"usage":{},"obfuscation":"sCV1ilTpoj6SqxS"}
data: {"id":"chatcmpl-CWvlnHBwh3VlWjxyX2QHVu9N1JvIO","object":"chat.completion.chunk","created":1761964599,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_efad92c60b","choices":[{"index":0,"delta":{"content":" from"},"logprobs":null,"finish_reason":null}],"usage":{},"obfuscation":"EJAbKIM9yxRvoaX"}
data: {"id":"chatcmpl-CWvlnHBwh3VlWjxyX2QHVu9N1JvIO","object":"chat.completion.chunk","created":1761964599,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_efad92c60b","choices":[{"index":0,"delta":{"content":" Auto"},"logprobs":null,"finish_reason":null}],"usage":{},"obfuscation":"CtikDiQd1fp4ub7"}
data: {"id":"chatcmpl-CWvlnHBwh3VlWjxyX2QHVu9N1JvIO","object":"chat.completion.chunk","created":1761964599,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_efad92c60b","choices":[{"index":0,"delta":{"content":"Lab"},"logprobs":null,"finish_reason":null}],"usage":{},"obfuscation":"L"}
data: {"id":"chatcmpl-CWvlnHBwh3VlWjxyX2QHVu9N1JvIO","object":"chat.completion.chunk","created":1761964599,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_efad92c60b","choices":[{"index":0,"delta":{"content":"!"},"logprobs":null,"finish_reason":null}],"usage":{},"obfuscation":"35c"}
data: {"id":"chatcmpl-CWvlnHBwh3VlWjxyX2QHVu9N1JvIO","object":"chat.completion.chunk","created":1761964599,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_efad92c60b","choices":[{"index":0,"delta":{"content":" How"},"logprobs":null,"finish_reason":null}],"usage":{},"obfuscation":""}
data: {"id":"chatcmpl-CWvlnHBwh3VlWjxyX2QHVu9N1JvIO","object":"chat.completion.chunk","created":1761964599,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_efad92c60b","choices":[{"index":0,"delta":{"content":" can"},"logprobs":null,"finish_reason":null}],"usage":{},"obfuscation":""}
data: {"id":"chatcmpl-CWvlnHBwh3VlWjxyX2QHVu9N1JvIO","object":"chat.completion.chunk","created":1761964599,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_efad92c60b","choices":[{"index":0,"delta":{"content":" I"},"logprobs":null,"finish_reason":null}],"usage":{},"obfuscation":"Om"}
data: {"id":"chatcmpl-CWvlnHBwh3VlWjxyX2QHVu9N1JvIO","object":"chat.completion.chunk","created":1761964599,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_efad92c60b","choices":[{"index":0,"delta":{"content":" assist"},"logprobs":null,"finish_reason":null}],"usage":{},"obfuscation":"0dugxG8TLaxA7"}
data: {"id":"chatcmpl-CWvlnHBwh3VlWjxyX2QHVu9N1JvIO","object":"chat.completion.chunk","created":1761964599,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_efad92c60b","choices":[{"index":0,"delta":{"content":" you"},"logprobs":null,"finish_reason":null}],"usage":{},"obfuscation":""}
data: {"id":"chatcmpl-CWvlnHBwh3VlWjxyX2QHVu9N1JvIO","object":"chat.completion.chunk","created":1761964599,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_efad92c60b","choices":[{"index":0,"delta":{"content":" today"},"logprobs":null,"finish_reason":null}],"usage":{},"obfuscation":"dCoTwLg7cyoFUI"}
data: {"id":"chatcmpl-CWvlnHBwh3VlWjxyX2QHVu9N1JvIO","object":"chat.completion.chunk","created":1761964599,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_efad92c60b","choices":[{"index":0,"delta":{"content":"?"},"logprobs":null,"finish_reason":null}],"usage":{},"obfuscation":"vxb"}
data: {"id":"chatcmpl-CWvlnHBwh3VlWjxyX2QHVu9N1JvIO","object":"chat.completion.chunk","created":1761964599,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_efad92c60b","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":{},"obfuscation":"R4p7CUTPYt7cDZ"}
data: {"id":"chatcmpl-CWvlnHBwh3VlWjxyX2QHVu9N1JvIO","object":"chat.completion.chunk","created":1761964599,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_efad92c60b","choices":[],"usage":{"prompt_tokens":13,"completion_tokens":13,"total_tokens":26,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"OKV"}
data: [DONE]流式响应字段说明:
每个流式数据块(chunk)包含:
id: 聊天完成的唯一标识符object: 对象类型,固定为"chat.completion.chunk"created: Unix 时间戳model: 使用的模型名称system_fingerprint: 系统指纹choices: 生成的回复列表index: 回复的索引delta: 增量内容对象role: 角色(仅在第一个 chunk 中)content: 当前 chunk 的内容片段
logprobs: 对数概率信息finish_reason: 完成原因(最后一个 chunk 中为"stop")
usage: Token 使用统计(仅在最后一个数据块中)obfuscation: 混淆字段(可忽略)
流式数据特点:
- 第一个数据块:通常为空或包含初始化信息
- 中间数据块:每个块的
delta.content包含一个文本片段 - 倒数第二个数据块:包含
finish_reason: "stop"表示生成结束 - 倒数第一个数据块:包含
usage字段,提供完整的 token 使用统计 - 最后一行:
data: [DONE]标记流结束
使用示例:
let fullContent = '';
await client.chatStream(
{
model: 'gpt-4o-mini-2024-07-18',
messages: [{ role: 'user', content: 'Hello!' }],
stream: true
},
{
onMessage: (data) => {
// 提取并累积内容
if (data.choices?.[0]?.delta?.content) {
fullContent += data.choices[0].delta.content;
console.log('收到内容:', data.choices[0].delta.content);
}
// 检查是否完成
if (data.choices?.[0]?.finish_reason === 'stop') {
console.log('生成完成');
}
// 获取使用统计
if (data.usage) {
console.log('Token 使用:', data.usage);
}
},
onDone: () => {
console.log('完整内容:', fullContent);
},
onError: (err) => {
console.error('错误:', err);
}
}
);配置选项
LLMClientConfig
{
baseURL: string; // API 基础 URL
auth: AuthBridge; // 认证桥接器
retry?: RetryPolicy; // 重试策略
defaultHeaders?: Record<string, string>; // 默认请求头
}AuthBridge
{
getAccessToken(): string | null | Promise<string | null>;
getClientId(): string | null | Promise<string | null>;
refreshAccessToken(): Promise<string>;
onUnauthorized?: () => void | Promise<void>;
}RetryPolicy
{
maxAttempts?: number; // 最大重试次数,默认 3
baseDelayMs?: number; // 基础延迟毫秒数,默认 250
}错误处理
SDK 会自动处理以下情况:
- 401 未授权:自动调用
refreshAccessToken()刷新 token 并重试 - 网络错误:根据
RetryPolicy进行重试 - 请求失败:抛出
LLMServiceError异常
许可证
根据项目根目录的许可证文件确定。
