npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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: 混淆字段(可忽略)

流式数据特点:

  1. 第一个数据块:通常为空或包含初始化信息
  2. 中间数据块:每个块的 delta.content 包含一个文本片段
  3. 倒数第二个数据块:包含 finish_reason: "stop" 表示生成结束
  4. 倒数第一个数据块:包含 usage 字段,提供完整的 token 使用统计
  5. 最后一行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 会自动处理以下情况:

  1. 401 未授权:自动调用 refreshAccessToken() 刷新 token 并重试
  2. 网络错误:根据 RetryPolicy 进行重试
  3. 请求失败:抛出 LLMServiceError 异常

许可证

根据项目根目录的许可证文件确定。

相关链接