dify-client Node.js SDK 实战指南:在 Node.js 应用中集成 Dify 的聊天、补全、工作流与知识库能力
本文基于 Dify 仓库 sdks/nodejs-client 目录下的官方 README 与配套源码,系统讲解 dify-client SDK 的安装、客户端体系、核心配置参数、流式响应处理、重试与错误处理机制。读完本文后,你可以将该 SDK 直接接入 Node.js 服务,完成聊天/补全消息、工作流运行、知识库管理与 RAG Pipeline 调用,并理解其底层的 HTTP 传输、重试退避与错误映射实现。
快速开始
dify-client 是 Dify 官方提供的 Node.js SDK,用于以编程方式调用 Dify API。安装方式如下:
npm install dify-client
从 package.json 可以确认该包的关键发布属性:
- 包名
dify-client,当前版本 3.1.0,MIT 协议,作者 LangGenius; - 要求运行环境
node >= 18.0.0(engines字段约束); - 包声明
"type": "module",即纯 ESM 包,入口为./dist/index.js,类型声明为./dist/index.d.ts。这意味着它通过 ESimport语法引入,与 CJS 项目混用时需注意互操作性。
六个客户端及其定位
SDK 在 src/index.ts 中统一导出六个客户端类,各自对应 Dify API 的一组路由:
| 客户端 | 继承关系 | 典型职责 | 所需凭证 |
|---|---|---|---|
DifyClient |
基类 | 应用核心能力:应用参数、消息反馈、文件上传/预览、文本转语音/语音转文本、应用信息(/meta、/info、/site) |
App API Token |
ChatClient |
extends DifyClient |
对话消息(阻塞/流式)、停止生成、会话管理与会话变量、推荐问题、标注回复(Annotation) | App API Token |
CompletionClient |
extends DifyClient |
文本补全消息、停止补全 | App API Token |
WorkflowClient |
extends DifyClient |
运行/停止工作流(/workflows/run、/workflows/tasks/{taskId}/stop) |
App API Token |
KnowledgeBaseClient |
extends DifyClient |
知识库(Dataset)CRUD、文档/分段/子块管理、命中测试、RAG Pipeline 运行 | Dataset API Token |
WorkspaceClient |
extends DifyClient |
工作区模型查询,如 getModelsByType('text-embedding'),底层请求 GET /workspaces/current/models/model-types/{modelType}(见 workspace.ts) |
Dataset API Token |
README 中的关键提醒在源码中可以得到印证:App 端点使用 App API Token,而知识库与工作区端点使用 Dataset API Token,两者不通用;构造客户端时把对应的 key 传入构造函数即可,如:
import {
DifyClient,
ChatClient,
CompletionClient,
WorkflowClient,
KnowledgeBaseClient,
WorkspaceClient,
} from 'dify-client'
const API_KEY = 'your-app-api-key'
const DATASET_API_KEY = 'your-dataset-api-key'
const chatClient = new ChatClient(API_KEY) // 应用 API token
const kbClient = new KnowledgeBaseClient(DATASET_API_KEY) // 知识库 API token
const workspaceClient = new WorkspaceClient(DATASET_API_KEY)
每个客户端构造函数都接受 string(API key)或 DifyClientConfig 对象,见 base.ts 中的 toConfig 归一化逻辑。
典型集成代码(完整示例)
以下示例继承自官方 README,覆盖应用核心能力、补全、聊天(流式)、Chatflow、工作流运行、知识库与 RAG Pipeline:
const user = 'random-user-id'
const query = 'Please tell me a short story in 10 words or less.'
// App core
await client.getApplicationParameters(user)
await client.messageFeedback('message-id', 'like', user)
// Completion (blocking)
await completionClient.createCompletionMessage({
inputs: { query },
user,
response_mode: 'blocking',
})
// Chat (streaming)
const stream = await chatClient.createChatMessage({
inputs: {},
query,
user,
response_mode: 'streaming',
})
for await (const event of stream) {
console.log(event.event, event.data)
}
// Chatflow (advanced chat via workflow_id)
await chatClient.createChatMessage({
inputs: {},
query,
user,
workflow_id: 'workflow-id',
response_mode: 'blocking',
})
// Workflow run (blocking or streaming)
await workflowClient.run({
inputs: { query },
user,
response_mode: 'blocking',
})
// Knowledge base (dataset token required)
await kbClient.listDatasets({ page: 1, limit: 20 })
await kbClient.createDataset({ name: 'KB', indexing_technique: 'economy' })
// RAG pipeline (may require service API route registration)
const pipelineStream = await kbClient.runPipeline('dataset-id', {
inputs: {},
datasource_type: 'online_document',
datasource_info_list: [],
start_node_id: 'start-node-id',
is_published: true,
response_mode: 'streaming',
})
for await (const event of pipelineStream) {
console.log(event.data)
}
// Workspace models (dataset token required)
await workspaceClient.getModelsByType('text-embedding')
示例中值得注意的几个细节:
user是必填的稳定标识:createChatMessage、createCompletionMessage、消息反馈等请求都要求非空user字段。SDK 在 validation.ts 层面通过ensureNonEmptyString做前置校验,空值会在发出 HTTP 请求前直接抛出ValidationError,而不是等服务端报错。response_mode决定返回形态:'blocking'返回Promise<DifyResponse<T>>;'streaming'返回Promise<DifyStream<T>>(AsyncIterable)。ChatClient.createChatMessage会根据payload.response_mode === 'streaming'自动选择requestStream还是普通request,见 chat.ts。messageFeedback支持双签名:既可用位置参数messageFeedback(messageId, 'like', user, content?),也可传对象{ messageId, user, rating, content },两种形式在 base.ts 中以重载实现。- Chatflow 场景通过请求体里的
workflow_id指定要走的工作流版本,这是 Chat 应用接入 Chatflow 的方式。
核心配置参数:DifyClientConfig
所有客户端共享同一套配置对象,类型定义见 types/common.ts,默认值在同一文件中集中声明:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
apiKey |
string |
必填 | Bearer Token,随每个请求以 Authorization: Bearer <apiKey> 发送 |
baseUrl |
string |
https://api.dify.ai/v1 |
API 基础地址;私有化部署时替换为你的 <部署域名>/v1 |
timeout |
number(秒) |
60 |
单次请求超时,通过 AbortController 实现(见下) |
maxRetries |
number |
3 |
最大重试次数 |
retryDelay |
number(秒) |
1 |
重试基础延迟,指数退避的底数 |
enableLogging |
boolean |
false |
开启后在控制台打印每次请求/响应/重试日志 |
配置归一化逻辑在 http/client.ts 的 normalizeSettings 中:省略的字段会逐项回落到上述默认值,因此 new ChatClient(API_KEY) 等价于 new ChatClient({ apiKey: API_KEY, baseUrl: 'https://api.dify.ai/v1', timeout: 60, maxRetries: 3, retryDelay: 1 })。
对于私有化部署(Docker Compose、VPC 或自建环境),baseUrl 应指向你部署实例的 API 前缀,例如 https://your-domain/v1;URL 拼接由 buildRequestUrl 完成,会自动去除 baseUrl 尾部多余斜杠并合并 query 参数。
另外,运行在 Node 环境且调用方未显式指定时,SDK 会附加 User-Agent: dify-client-node 请求头(见 http/client.ts)。
流式响应:DifyStream 的迭代与聚合
流式接口的返回类型定义在 types/common.ts:
type DifyStream<T> = AsyncIterable<StreamEvent<T>> & {
data: Readable // 底层 Node Readable 流
status: number
headers: Headers
requestId?: string
toText(): Promise<string> // 消费完整个流并聚合为文本
toReadable(): Readable
}
这带来三种使用姿势:
- 逐事件消费:
for await (const event of stream),每个事件形如{ event?, data, raw },event是 SSE 事件名(如message、message_end),data是已解析的负载; - 一次性聚合:
const full = await stream.toText(),适合只需要完整结果的场景; - 透传给下游:
stream.toReadable()拿到 NodeReadable,可直接pipe到 HTTP 响应或文件流,做 SSE 转发而不经内存缓冲。
流式解析的 SSE 编解码实现在 http/sse.ts,请求侧则由 HttpClient.requestStream 以 responseType: 'stream' 发起,将 fetch 的 Web 流转换为 Node Readable 后再交给 SSE 解析器。
重试、超时与错误体系
SDK 的传输层在 http/client.ts 与 http/retry.ts 中,具备以下行为:
超时:每次请求创建独立的 AbortController,超过 timeout 秒即 abort,并映射为 TimeoutError;请求结束后定时器会清理。
重试策略(shouldRetry + getRetryDelayMs):
- 仅对
TimeoutError、NetworkError、RateLimitError三类错误重试,业务错误(401/422 等)不会盲目重试; - 延迟为指数退避加随机抖动:
retryDelay × 2^(attempt-1) + [0, retryDelay)秒; - 若服务端返回
Retry-After响应头(支持秒数或 HTTP Date 两种格式),重试将优先遵循该值; - 关键约束:只有 replayable 请求体(JSON、字符串、二进制等可重新序列化内容)才会重试;文件上传的 FormData 流与 pipeable stream 因不可重放,失败时直接抛出,避免半截上传被重发。
错误类层级(定义于 errors/dify-error.ts):
DifyError (statusCode / responseBody / requestId / retryAfter / cause)
├── APIError
│ ├── AuthenticationError // HTTP 401
│ ├── RateLimitError // HTTP 429,附带解析出的 retryAfter(秒)
│ └── ValidationError // HTTP 422
├── NetworkError // 网络层故障
├── TimeoutError // 请求超时
└── FileUploadError // 上传类请求的 400 / FormData 校验失败
状态码到异常类的映射逻辑在 mapHttpError 中:401 → AuthenticationError,429 → RateLimitError(并解析 Retry-After),422 → ValidationError,上传路径上的 400 → FileUploadError,其余 → APIError。每个错误都携带 requestId(从响应头 x-request-id / x-requestid 提取),便于向 Dify 侧报障时定位请求。
统一响应包装:所有非流式请求返回 DifyResponse<T> = { data, status, headers, requestId? },即 data 是解析后的 JSON,外层还保留了 HTTP 状态与请求追踪信息,方便审计与日志。
知识库与工作区端点
KnowledgeBaseClient(knowledge-base.ts)封装了以 Dataset API Token 为凭证的知识库能力,包括:
listDatasets({ page, limit, keyword, includeAll, tagIds })/createDataset({ name, indexing_technique, ... })/getDataset/updateDataset/ 删除;- 文档、分段(Segment)、子块(Child Chunk)、元数据(Metadata)的增删改查;
hitTesting命中测试,用于调优检索效果;runPipeline(datasetId, request):以流式方式运行 RAG Pipeline,请求体包含datasource_type、datasource_info_list、start_node_id、is_published等字段,返回的同样是可for await迭代的DifyStream。README 提示该能力可能需要在服务端注册 Service API 路由,实际可用性以部署配置为准。
WorkspaceClient 目前提供 getModelsByType(modelType),查询当前工作区指定类型(如 text-embedding)下可用的模型列表,返回结构定义见 types/workspace.ts。
测试与发布维护
SDK 采用 Vite/vitest 工具链,测试与源码同目录组织:src/ 下的 *.test.ts 覆盖各客户端的构造、校验与请求组装逻辑,tests/http.integration.test.ts 提供 HTTP 层集成测试。常用脚本(见 package.json):
pnpm --filter dify-client test # 运行测试(vp test)
pnpm --filter dify-client build # 构建 dist/index.js 与类型声明
pnpm --filter dify-client publish:check # ./scripts/publish.sh --dry-run,发布前干跑
按 README 的 Maintainers 说明:由于包内依赖使用了 pnpm 的 catalog: 协议,发布前必须先在工作区根目录执行 pnpm install,再通过 scripts/publish.sh 完成 dry-run 与正式发布,确保 catalog: 依赖在发布前被正确解析。
参考文件索引
- 文档与元信息:sdks/nodejs-client/README.md、sdks/nodejs-client/package.json
- 导出与路由表:sdks/nodejs-client/src/index.ts
- 客户端基类与通用能力:sdks/nodejs-client/src/client/base.ts
- 聊天客户端:sdks/nodejs-client/src/client/chat.ts
- HTTP 传输、超时与重试:sdks/nodejs-client/src/http/client.ts、sdks/nodejs-client/src/http/retry.ts
- 配置默认值与流式类型:sdks/nodejs-client/src/types/common.ts
- 错误类体系:sdks/nodejs-client/src/errors/dify-error.ts
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 StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00