LobeHub 官方 TypeScript SDK 全解析:@lobehub/sdk 的资源化 API 客户端架构与工程实践
@lobehub/sdk 是 LobeHub 面向其 REST API 的官方 TypeScript SDK,它以 OpenAPI 规范为唯一事实来源,通过代码生成器产出"资源风格"(resource-style)的类型安全客户端。本篇文章将带你完整掌握它的安装方式、资源方法命名规则、自定义部署接入、SSE 流式响应的读取技巧,并结合仓库源码与测试用例,剖析其从规范到生成的工程链路、零依赖 HTTP 运行时设计以及自动化发布流程。读完你既能在自己项目中快速接入 LobeHub API,也能复用它"OpenAPI 驱动 SDK"的可维护工程范式。
本文基于仓库 packages/sdk/README.md 并结合 packages/sdk 下源码展开,仓库为只读,以下所有内容均为查看与使用方式。
一、SDK 概览:一份 OpenAPI 规范驱动的资源化客户端
@lobehub/sdk 的全部类型与接口并非手工维护,而是由位于 packages/openapi/openapi.yml 的 OpenAPI 3 规范经 @hey-api/openapi-ts 代码生成而来,配置见 packages/sdk/openapi-ts.config.ts。规范中的服务地址(servers)明确指向 https://app.lobehub.com(见 packages/openapi/openapi.yml),所有路径均以 /api/v1/... 为前缀。
SDK 的核心设计目标有两个:
- 全类型化:每一个路径、参数、请求体与响应类型都直接来自
openapi.yml,编译期即可校验请求是否合法; - 零运行时依赖:HTTP 运行时被打包进 SDK 内部(inlined),不额外引入第三方请求库。
生成的代码被提交进仓库(packages/sdk/src/generated/),因此发布到 npm 的包不依赖"安装时再生成"这一步骤,用户拿到即可运行。
当前包版本为
1.0.0,标记为private: true(见 packages/sdk/package.json),仅在自动化发布流程中由工作流临时改写为公开可发布形态,具体见后文"发布"章节。
二、安装
npm install @lobehub/sdk
如使用 pnpm 等包管理器,等价命令为 pnpm add @lobehub/sdk。该包导出 ESM 产物并携带完整声明文件,发布形态在 .github/workflows/release-sdk.yml 中被改写为 main: ./dist/index.mjs、types: ./dist/index.d.mts(详见仓库发布脚本处理逻辑)。
三、快速上手:创建客户端并调用首个接口
SDK 的入口是 createLobeHub() 工厂函数,它接收一个配置对象并返回根实例 LobeHub:
import { createLobeHub } from '@lobehub/sdk';
const lobehub = createLobeHub({
// LobeHub API Key(形如 `sk-lh-...`)或一个 OIDC JWT
apiKey: process.env.LOBEHUB_API_KEY!,
});
const me = await lobehub.users.me(); // 当前登录用户
const { data, error } = await lobehub.agents.list(); // 拉取 Agent 列表
const agent = await lobehub.agents.get({ path: { id: 'agt_...' } }); // 按 ID 获取 Agent
await lobehub.agentGroups.create({ body: { name: 'My group' } }); // 创建分组
await lobehub.files.uploadBatch({ body: { files: [/* … */] } }); // 批量上传文件
1. createLobeHub 的配置项与认证实现
工厂函数定义在 packages/sdk/src/index.ts,其签名如下:
| 配置项 | 类型 | 说明 |
|---|---|---|
apiKey |
string(必填) |
LobeHub API Key(sk-lh-... 前缀)或 OIDC JWT,用于 Bearer 认证 |
baseURL |
string(可选) |
API 服务地址,默认指向 DEFAULT_BASE_URL = 'https://app.lobehub.com' |
| 其余配置 | 继承自 Config |
任何底层 fetch 客户端支持的可选项,如自定义 fetch、headers 等 |
从源码实现看,apiKey 会被包装进认证回调 auth: () => apiKey,从而在每次请求中以 HTTP Authorization: Bearer <apiKey> 头发送。生成代码中每个接口都声明了 security: [{ scheme: 'bearer', type: 'http' }](例如 sdk.gen.ts 中 Health.check 的定义),与 createLobeHub 的默认 Bearer 行为一一对应。
2. 工厂函数做了什么
packages/sdk/src/index.ts 的核心逻辑很简洁:
export const createLobeHub = (options: LobeHubOptions): LobeHub => {
const { apiKey, baseURL = DEFAULT_BASE_URL, ...clientConfig } = options;
const config: Config = { ...clientConfig, auth: () => apiKey, baseUrl: baseURL };
return new LobeHub({ client: createClient(createConfig(config)) });
};
即:合并配置 → 绑定认证回调与默认域名 → 通过 createClient(createConfig(...)) 构造底层 fetch 客户端 → 注入到 LobeHub 根实例。
LobeHub 根实例(packages/sdk/src/generated/sdk.gen.ts 起)内部以懒加载 getter 按需实例化每一个资源组(如 agents、files),并把同一个 client 注入所有子资源,保证它们共享同一套配置、认证与请求拦截链。由于每个 SDK 实例都持有独立客户端,多个 createLobeHub 实例之间互不串扰——这一点在 packages/sdk/src/index.test.ts 的 keeps client instances isolated between SDK instances 用例中有直接验证。
四、资源与方法的命名规则
SDK 的"资源风格"体现在:每个 API 的顶级路径段(segment)对应根实例上的一个资源对象,方法名遵循一套机械的、可推导的规则。规则定义在 packages/sdk/openapi-ts.config.ts,其注释给出了完整的推导逻辑:
| HTTP 方法 + 路径形态 | 生成的默认方法名 |
|---|---|
GET 资源集合 |
list |
GET 单资源 .../{id} |
get |
POST 资源集合 |
create |
PATCH / PUT 单资源 |
update |
DELETE .../{id} |
delete |
| 附加静态子路径段 | 按 PascalCase 追加(如 GET .../{id}/chunks → listChunks、GET .../{id}/url → getUrl) |
此外,针对"按机械规则生成的名称不够达意"的少数接口,配置中维护了一张显式覆盖表 METHOD_NAME_OVERRIDES(见 packages/sdk/openapi-ts.config.ts),举例:
| 原始路径 | 覆盖后的方法名 |
|---|---|
GET /api/v1/health |
health.check |
GET /api/v1/users/me |
users.me |
POST /api/v1/files/batches |
files.uploadBatch |
POST /api/v1/files/queries |
files.query |
POST /api/v1/files/{id}/parses |
files.parse |
POST /api/v1/knowledge-bases/{id}/files/batch |
knowledgeBases.addFiles |
DELETE /api/v1/knowledge-bases/{id}/files/batch |
knowledgeBases.removeFiles |
POST /api/v1/knowledge-bases/{id}/files/move |
knowledgeBases.moveFiles |
POST /api/v1/messages/replies |
messages.createReply |
DELETE /api/v1/messages |
messages.deleteMany(注释说明:这是按所选 id 批量删除,语义并非"删除全部") |
openapi-ts.config.ts 中的 nesting 函数将每个 operation 归组到其资源对象下:取路径第三段(去掉 /api/v1/ 前缀后的资源根)并转为 camelCase 作为资源名。
当前版本全部资源组
从根实例的 getter 集合(packages/sdk/src/generated/sdk.gen.ts)与生成的资源类可以确认,当前 SDK 共暴露以下资源对象(README 列出的基础集合之外,还包含从规范同步生成的管理类资源):
- 基础业务:
health、agents、agentGroups、files、knowledgeBases、messages、messageTranslations、topics、models、providers、permissions、roles、users - 交互与评估:
responses、chat、eval、usage - 接入与密钥:
apiKeys、mcpServers
各资源的实际方法可在 packages/sdk/src/generated/sdk.gen.ts 中逐一核对。以 Files 为例,其方法包括 list / create(表单上传)/ get / update / delete / getUrl / parse / listChunks / createChunks / uploadBatch / query;以 Roles 为例,还包括权限子资源方法 listPermissions / updatePermissions / deletePermissions;Users 同样包含角色管理子方法 listRoles / updateRoles / deleteRoles。在生成资源类中,能直接看到每个方法对应的真实 HTTP 语义,例如 files.create 使用 formDataBodySerializer 且 'Content-Type': null(交由浏览器自动填充 multipart/form-data 边界)。
五、接入自定义部署与底层客户端配置
默认客户端指向 LobeHub 云服务 https://app.lobehub.com。若你自托管 LobeHub 或在本地点服务上联调,可通过 baseURL 将请求指向其他部署实例:
const lobehub = createLobeHub({
apiKey: 'sk-lh-...',
baseURL: 'http://localhost:3010',
});
由于 createLobeHub 接受底层客户端的全部可选配置(Config 类型),你还可以透传:
fetch:自定义 fetch 实现(单元测试即通过注入的 mock fetch 捕获请求断言,见 packages/sdk/src/index.test.ts);headers:客户端级默认请求头(支持普通对象、Headers实例与[key, value]元组数组等合法HeadersInit形态);- 其他与请求序列化、响应解析相关的底层选项。
仓库在客户端构建上做了一层重要的健壮性兜底:针对 @hey-api/openapi-ts 0.99 生成的 headers 处理在 HeadersInit 某些合法形态下会出错的问题,packages/sdk/scripts/generate-sdk.ts 在生成后自动重写两处代码:
- 客户端合并 headers 时,将元组数组先经由
new Headers()归一化,避免Object.entries把[['X-Trace','1']]变成数字键; - 写操作方法把对象展开合并改写为
mergeMethodHeaders(...)辅助函数,将各种HeadersInit形态都归一为普通对象,从而保证表单上传方法上的'Content-Type': null删除哨兵能存活到客户端最终合并阶段,正确移除客户端默认的Content-Type: application/json。
上述行为均有测试锁定:preserves per-call Headers instances and tuple arrays on write methods、normalizes tuple-array headers at client level and on read methods、drops a client-default Content-Type on form-data uploads(见 packages/sdk/src/index.test.ts)。
关于类型与错误处理
- 原始规范中的全部类型(path / body / response 等)可从
@lobehub/sdk/types子路径导入,映射文件为 packages/sdk/src/generated/types.gen.ts; - 每个方法都接受可选的
ThrowOnError泛型开关:默认为false,此时调用返回{ data, error, response }风格的RequestResult,需要自行判断error;可显式指定{ throwOnError: true }让请求失败时直接抛错。
六、流式响应(SSE)
调用大模型相关的 responses.create 接口时,若在请求体中设置 stream: true,服务端将以 Server-Sent Events(SSE)的方式推送结果。默认情况下客户端会按 JSON 解析响应,要读取原始事件流,需要配合 parseAs: 'stream' 选项:
const { response } = await lobehub.responses.create({
body: { input: '…', model: '…', stream: true },
parseAs: 'stream',
});
for await (const chunk of response.body!) {
// 逐块解码 SSE 数据
}
这样可以直接消费 response.body 的可读流。仓库为后续"一等公民"的强类型 SSE 方法预留了基础设施:packages/sdk/src/generated/core/serverSentEvents.gen.ts 中已包含事件流客户端的类型定义(如 onSseEvent、onSseError、sseMaxRetryAttempts、sseMaxRetryDelay、sseDefaultRetryDelay 等),README 也明确指出:"具备规范响应 schema 的一等类型化 SSE 方法将在后续提供",即当前属于底层手工解码阶段。
七、面向维护者的开发流程
作为包维护者或希望二次开发 SDK 的贡献者,packages/sdk/README.md 提供了三个核心命令:
| 命令 | 作用 |
|---|---|
bun generate |
基于 ../openapi/openapi.yml 重新生成 src/generated/(规范变更后执行;命名规则集中在 openapi-ts.config.ts) |
bun run build |
通过 tsdown 构建 ESM 与 d.ts 声明到 dist/ |
bun run test |
运行单元测试 + 生成产物漂移检查(drift check) |
在仓库中对应实现为 packages/sdk/scripts/generate-sdk.ts 与 packages/sdk/scripts/generate-sdk.test.ts。生成脚本支持两种模式:
bun scripts/generate-sdk.ts:清空src/generated/后按openapi-ts.config.ts重新生成,再执行上文提到的 headers 重写修补;bun scripts/generate-sdk.ts --check:在临时目录中重新生成一份输出,与仓库已提交的src/generated/做逐字节比对,不一致即退出并提示 "Runbun generatein packages/sdk and commit the result"。
测试脚本 generate-sdk.test.ts 以子进程方式执行 --check,确保 API 路由变更时提交的生成产物不会"静默漂移"。
需要特别说明 CI 边界:这个包的漂移检查没有接入常规 PR 流水线,只在本机执行 bun run test 与发布工作流内运行(README 与测试注释均明确说明"not wired into PR CI")。对这类强规范的仓库而言,这是一条值得借鉴的取舍——把成本较高的全量比对放在发布前把关,而不是每次 PR 都执行。
八、自动化发布:canary 驱动的 npm 发布流水线
@lobehub/sdk 的发布完全由 GitHub Actions 工作流 .github/workflows/release-sdk.yml 自动化完成,无需人工干预 npm 版本号。核心机制如下:
- 触发条件:向
canary分支推送且改动涉及packages/sdk/**时自动触发;也提供手动workflow_dispatch入口; - 版本策略:以包内
package.json的基础版本(当前为1.0)拼接 UTC 时间戳,生成1.0.<UTC timestamp>作为发布版本,保证每次发布版本单调递增; - 质量闸门:流水线依次执行
bun scripts/generate-sdk.ts --check(产物漂移检查)、pnpm --filter @lobehub/sdk test(单元测试)、pnpm --filter @lobehub/sdk build(构建),三道关卡全部通过才进入发布; - 发布前准备:脚本将
private、devDependencies、scripts等字段移除,把exports中指向./src/*.ts的入口改写为./dist/*.mjs(并生成对应.d.mts声明),写入repository与files: ['dist']后执行npm publish --provenance --access public; - 供应链安全:发布步骤申请了
id-token: write权限并携带--provenance,即 npm 包会附带可验证的来源证明(SLSA provenance)。
因此,向 canary 分支合入一次 SDK 改动后,规范的发布路径是:bun generate 重新生成并提交生成产物,推送即触发发布——工作流内的 --check 漂移检查正是为了守护最终发布物的正确性。
九、实践要点小结
- 默认即云端:不传
baseURL时全部请求发往https://app.lobehub.com,本地自托管务必显式传baseURL; - 认证无感知:
apiKey在createLobeHub内自动成为 Bearer 头,也可直接传入 OIDC JWT; - 方法与资源名可推导:绝大多数方法是
list/get/create/update/delete + PascalCase 子段的机械产物,个别语义不直观的接口(如files.uploadBatch、knowledgeBases.addFiles、messages.deleteMany)可在 packages/sdk/openapi-ts.config.ts 的覆盖表中查到真名; - 流式响应需手工解码:
responses.create+stream: true需配parseAs: 'stream'读取response.body,强类型 SSE 支持仍在路线图中; - 类型即文档:全部请求/响应类型可从
@lobehub/sdk/types导入,需要深挖字段时直接阅读 packages/sdk/src/generated/types.gen.ts,它是 OpenAPI 规范的可读投影。
如需了解某个接口的完整字段与取值,最权威的参照永远是上游规范文件 packages/openapi/openapi.yml,SDK 与其保持一致是发布流水线的硬性约束。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00