From d741ce5af9e864d452220fd4432c164e87e19b61 Mon Sep 17 00:00:00 2001 From: dash <1549469775@qq.com> Date: Sun, 26 Apr 2026 16:33:46 +0800 Subject: [PATCH] feat: implement DeepSeek thinking mode with reasoning effort support Add new features to the xllm package, including the ability to enable a thinking mode and specify reasoning effort for DeepSeek-compatible providers. Update the environment configuration and demo implementations to showcase these features. Enhance the README and documentation to reflect the new functionality and usage examples. --- packages/example/.env.example | 7 +- packages/example/src/xllm/demos/catalog.ts | 8 ++ .../example/src/xllm/demos/stream-with-tools.ts | 8 +- packages/example/src/xllm/demos/thinking.ts | 76 +++++++++++++++ packages/example/src/xllm/env.ts | 8 +- packages/xllm/README.md | 20 ++++ packages/xllm/docs/LLM-USAGE.md | 33 ++++++- packages/xllm/src/client/chat-with-tools.ts | 4 + packages/xllm/src/client/stream-with-tools.ts | 11 ++- packages/xllm/src/core/types.ts | 31 ++++++ packages/xllm/src/index.test.ts | 105 ++++++++++++++++++++- packages/xllm/src/index.ts | 2 + .../src/providers/openai-compatible.adapter.ts | 50 +++++++++- tsconfig.tsbuildinfo | 1 - 14 files changed, 349 insertions(+), 15 deletions(-) create mode 100644 packages/example/src/xllm/demos/thinking.ts delete mode 100644 tsconfig.tsbuildinfo diff --git a/packages/example/.env.example b/packages/example/.env.example index 5dd81fe..cc570c3 100644 --- a/packages/example/.env.example +++ b/packages/example/.env.example @@ -6,6 +6,9 @@ VITE_XLLM_PROVIDER_2=openai-compatible VITE_XLLM_MODEL_2=gpt-4o-mini VITE_XLLM_API_KEY_2= VITE_XLLM_BASE_URL_2= -VITE_XLLM_DEMO_IMAGE_URSL= -VITE_XLLM_TOOL_ERROR_TRATEGY=throw +VITE_XLLM_DEMO_IMAGE_URL= +VITE_XLLM_TOOL_ERROR_STRATEGY=throw VITE_XLLM_FORCE_TOOL_FAILURE=0 +# DeepSeek 思考模式:enabled | disabled;强度 high | medium | low | max | xhigh(@dm/xllm 会映射为 high/max) +VITE_XLLM_THINKING=enabled +VITE_XLLM_REASONING_EFFORT=high diff --git a/packages/example/src/xllm/demos/catalog.ts b/packages/example/src/xllm/demos/catalog.ts index ed35f66..88664ff 100644 --- a/packages/example/src/xllm/demos/catalog.ts +++ b/packages/example/src/xllm/demos/catalog.ts @@ -4,6 +4,7 @@ import { runGenerateDemo } from "./generate"; import { runRequestSwitchDemo } from "./request-switch"; import { runStreamDemo } from "./stream"; import { runStreamWithToolsDemo } from "./stream-with-tools"; +import { runThinkingDemo } from "./thinking"; import { runToolsMultimodalDemo } from "./tools-multimodal"; import type { DemoLog, XllmClient } from "./types"; @@ -40,6 +41,13 @@ export const xllmDemoCatalog: DemoDefinition[] = [ run: (client, log) => runGenerateDemo(client, log), }, { + id: "thinking", + title: "DeepSeek 思考模式", + description: + "thinking + reasoning_effort;流式区分 reasoning.delta 与 text.delta;generate 可读 reasoning。需 provider=deepseek(见 .env.example)。", + run: (client, log, ctx) => runThinkingDemo(client, log, ctx), + }, + { id: "request-switch", title: "请求级切换 provider / model", description: "单次请求覆盖默认 client 的 provider、model、apiKey(需配置 *_2 环境变量)。", diff --git a/packages/example/src/xllm/demos/stream-with-tools.ts b/packages/example/src/xllm/demos/stream-with-tools.ts index c83f6e7..41d8107 100644 --- a/packages/example/src/xllm/demos/stream-with-tools.ts +++ b/packages/example/src/xllm/demos/stream-with-tools.ts @@ -13,6 +13,8 @@ export async function runStreamWithToolsDemo(client: XllmClient, log: DemoLog): tools: [{ name: "get_weather", parameters: { type: "object", properties: { city: { type: "string" } } } }], toolChoice: "auto", toolErrorStrategy, + thinking: { type: "enabled" }, + reasoningEffort: "high", }, { get_weather: (args) => { @@ -22,9 +24,9 @@ export async function runStreamWithToolsDemo(client: XllmClient, log: DemoLog): return { city: typeof args === "object" && - args !== null && - "city" in args && - typeof (args as { city?: unknown }).city === "string" + args !== null && + "city" in args && + typeof (args as { city?: unknown }).city === "string" ? (args as { city: string }).city : "Beijing", weather: "晴", diff --git a/packages/example/src/xllm/demos/thinking.ts b/packages/example/src/xllm/demos/thinking.ts new file mode 100644 index 0000000..e8078f0 --- /dev/null +++ b/packages/example/src/xllm/demos/thinking.ts @@ -0,0 +1,76 @@ +import type { XMessage, XProviderName, XReasoningEffortInput } from "@dm/xllm"; +import { xllmReasoningEffort, xllmThinkingType } from "../env"; +import type { DemoLog, XllmClient } from "./types"; + +function thinkingRequestOptions(provider: XProviderName): { + thinking?: { type: "enabled" | "disabled" }; + reasoningEffort?: XReasoningEffortInput; +} { + if (provider !== "deepseek") { + return {}; + } + return { + thinking: { type: xllmThinkingType }, + reasoningEffort: xllmReasoningEffort ?? "high", + }; +} + +export async function runThinkingDemo( + client: XllmClient, + log: DemoLog, + ctx: { provider: XProviderName; model: string }, +): Promise { + log("========== DeepSeek 思考模式(thinking + reasoning_effort)=========="); + const extra = thinkingRequestOptions(ctx.provider); + if (Object.keys(extra).length === 0) { + log( + `[thinking] 当前默认 provider 为「${ctx.provider}」,非 deepseek 时不自动附带 thinking 参数(避免部分网关拒绝未知字段)。将 VITE_XLLM_PROVIDER 设为 deepseek 后再试,或只看下方说明。`, + ); + log("[thinking] 库内字段:thinking + reasoningEffort → 请求体 thinking、reasoning_effort;流式事件 reasoning.delta。"); + return; + } + + log( + `[thinking] 请求参数: thinking=${JSON.stringify(extra.thinking)}, reasoningEffort=${extra.reasoningEffort ?? "high"}`, + ); + + let reasoning = ""; + let text = ""; + log("--- stream() ---"); + const messages = [ + { role: "user", content: [{ type: "text", text: "用一句话说明「思考模式」对回答质量的帮助,不要分点。" }] }, + ] as XMessage[]; + for await (const event of client.stream({ + messages, + ...extra, + })) { + if (event.type === "response.start") { + log(`[stream] start: ${event.provider} ${event.model}`); + } + if (event.type === "reasoning.delta") { + reasoning += event.text; + log(`[stream] reasoning.delta: ${event.text}`); + } + if (event.type === "text.delta") { + text += event.text; + log(`[stream] text.delta: ${event.text}`); + } + if (event.type === "response.done") { + log(`[stream] done (finishReason=${event.finishReason ?? ""})`); + } + } + log(`[stream] reasoning 拼接长度: ${reasoning.length},正文: ${text}`); + messages.push({ role: "assistant", content: [{ type: "text", text: text }] }); + log("--- generate() ---"); + messages.push({ role: "user", content: [{ type: "text", text: "同上问题,再答一次,更短。" }] }); + const result = await client.generate({ + messages, + ...extra, + }); + if (result.reasoning) { + log(`[generate] reasoning (前 200 字): ${result.reasoning.slice(0, 200)}${result.reasoning.length > 200 ? "…" : ""}`); + } else { + log("[generate] reasoning: (本响应未返回 reasoning_content,属供应商行为)"); + } + log(`[generate] text: ${result.text}`); +} diff --git a/packages/example/src/xllm/env.ts b/packages/example/src/xllm/env.ts index 648bb07..b786ae7 100644 --- a/packages/example/src/xllm/env.ts +++ b/packages/example/src/xllm/env.ts @@ -1,4 +1,4 @@ -import type { XProviderName, XToolErrorStrategy } from "@dm/xllm"; +import type { XProviderName, XReasoningEffortInput, XToolErrorStrategy } from "@dm/xllm"; const env = (import.meta as unknown as { env?: Record }).env ?? {}; @@ -22,3 +22,9 @@ export const demoImageUrl = export const hasPrimaryConfig = Boolean(primaryApiKey); export const hasSecondaryConfig = Boolean(secondaryProvider && secondaryModel && secondaryApiKey); + +/** 思考模式开关:请求体 `thinking.type`(DeepSeek 等);默认 enabled */ +export const xllmThinkingType = (env.VITE_XLLM_THINKING as "enabled" | "disabled" | undefined) ?? "enabled"; + +/** 思考强度,映射为请求体 `reasoning_effort`;不填时示例内默认 high */ +export const xllmReasoningEffort = env.VITE_XLLM_REASONING_EFFORT as XReasoningEffortInput | undefined; diff --git a/packages/xllm/README.md b/packages/xllm/README.md index 6e964e9..f5a3e3a 100644 --- a/packages/xllm/README.md +++ b/packages/xllm/README.md @@ -6,6 +6,7 @@ - 流式输出(`for await...of`) - OpenAI-Compatible 与 DeepSeek 供应商适配 +- DeepSeek 思考模式:`thinking`、`reasoning_effort` 与流式 `reasoning.delta` - 文本、多模态输入(图片 URL)、工具调用结构统一 ## 快速开始 @@ -65,3 +66,22 @@ for await (const event of xllm.streamWithTools( if (event.type === "text.delta") process.stdout.write(event.text); } ``` + +## DeepSeek 思考模式(Thinking) + +`thinking` / `reasoningEffort` 仅适用于 **OpenAI Chat Completions 兼容体** 里采用这些字段名的网关(如 DeepSeek 文档)。其它厂商字段不同时,请用请求上的 **`providerExtras`** 合并任意 JSON 根字段,或扩展适配器。 + +请求里设置 `thinking` 与 `reasoningEffort`,会写入 Chat Completions 的 `thinking`、`reasoning_effort`(`low`/`medium`→`high`,`xhigh`→`max`)。流式下思考增量为 `reasoning.delta`,正文为 `text.delta`。 + +```ts +for await (const event of xllm.stream({ + messages: [{ role: "user", content: [{ type: "text", text: "…" }] }], + thinking: { type: "enabled" }, + reasoningEffort: "high", +})) { + if (event.type === "reasoning.delta") process.stderr.write(event.text); + if (event.type === "text.delta") process.stdout.write(event.text); +} +``` + +详见 [docs/LLM-USAGE.md](./docs/LLM-USAGE.md) 第 5.1 节。 diff --git a/packages/xllm/docs/LLM-USAGE.md b/packages/xllm/docs/LLM-USAGE.md index 53faff5..9876291 100644 --- a/packages/xllm/docs/LLM-USAGE.md +++ b/packages/xllm/docs/LLM-USAGE.md @@ -23,7 +23,7 @@ import { createXllm, XllmError } from "@dm/xllm"; ### 2.2 类型导出(`export type`) -`XClientOptions`, `XRequest`, `XResponse`, `XStreamEvent`, `XMessage`, `XContentPart`, `XProviderName`, `XToolDefinition`, `XToolCall`, `XToolChoice`, `XToolExecutor`, `XToolExecutorMap`, `XToolErrorStrategy`, `XChatWithToolsOptions`, `XChatWithToolsResult`, `XUsage` +`XClientOptions`, `XRequest`, `XResponse`, `XStreamEvent`, `XMessage`, `XContentPart`, `XProviderName`, `XThinkingMode`, `XReasoningEffortInput`, `XToolDefinition`, `XToolCall`, `XToolChoice`, `XToolExecutor`, `XToolExecutorMap`, `XToolErrorStrategy`, `XChatWithToolsOptions`, `XChatWithToolsResult`, `XUsage` --- @@ -87,6 +87,28 @@ const xllm = createXllm(options?: XClientOptions): XllmClient; --- +## 5.1 思考模式(Thinking / CoT)— **非全供应商通用** + +`thinking` / `reasoningEffort` **不是**抽象意义上的「全市场通用配置」:它们对应 **OpenAI Chat Completions 兼容 JSON** 里部分厂商采用的字段名(DeepSeek 官方文档中的 OpenAI 形态即如此)。**其它供应商若使用不同字段名、不同嵌套路径或不同 API**,有两种做法: + +1. **`providerExtras`**(推荐先做):在 `XRequest.providerExtras` 里写入任意键值,库会在构建完标准字段后 **`Object.assign` 合并进请求体根对象**(**后合并者可覆盖**同名标准字段)。用于传入厂商文档要求的专有参数,而无需立刻新增适配器。 +2. **新 `ProviderAdapter`**:协议差异大(路径、鉴权、流式格式都不同)时,在 `packages/xllm` 内实现并注册适配器。 + +与 DeepSeek 文档一致的字段映射如下(兼容体): + +| `XRequest` 字段 | 请求体字段 | 说明 | +|-----------------|------------|------| +| `thinking?: { type: "enabled" \| "disabled" }` | `thinking` | 思考开关;不传则由服务端默认(一般为 enabled) | +| `reasoningEffort?: "low" \| "medium" \| "high" \| "max" \| "xhigh"` | `reasoning_effort` | 仅发送 **`high`** 或 **`max`**:`low` / `medium` → `high`,`xhigh` → `max` | + +**流式**:若供应商在 SSE 的 `choices[].delta` 中返回 `reasoning_content`(或 `reasoning` 字符串),库会发出 `XStreamEvent`:`{ type: "reasoning.delta", text: string }`,与 `{ type: "text.delta", ... }` 区分。 + +**非流式**:若 `message` 中含 `reasoning_content` 或 `reasoning`,会填入 `XResponse.reasoning`;正文仍在 `XResponse.text`。 + +`provider: "deepseek"` 与 `provider: "openai-compatible"` 均走同一适配器请求体逻辑;其它 **同形态** 网关若支持相同字段,亦可使用 `thinking` / `reasoningEffort`;否则请用 **`providerExtras`**。 + +--- + ## 6. 请求与消息模型 ### 6.1 `XRequest`(核心字段) @@ -96,11 +118,13 @@ const xllm = createXllm(options?: XClientOptions): XllmClient; - `toolChoice?: "auto" | "none" | { name: string }` - `temperature?`, `topP?`, `maxTokens?`, `metadata?` - 可覆盖:`provider?`, `model?`, `apiKey?`, `baseURL?` +- `thinking?`, `reasoningEffort?`:见 §5.1(非通用,部分兼容厂商) +- `providerExtras?`:合并进 Chat Completions 请求 JSON 根对象(见 §5.1) - `stream?`:由 `generate`/`stream` 内部控制,调用方一般不必依赖此字段语义 ### 6.2 `XMessage` -- `system` | `user` | `assistant`:`content: XContentPart[]`;`assistant` 可选 `toolCalls?: XToolCall[]`(**协议要求**:在 `role: "tool"` 之前,assistant 需带对应 `tool_calls`;使用 `chatWithTools` / `streamWithTools` 时由库自动写入) +- `system` | `user` | `assistant`:`content: XContentPart[]`;`assistant` 可选 `toolCalls?: XToolCall[]`(**协议要求**:在 `role: "tool"` 之前,assistant 需带对应 `tool_calls`;使用 `chatWithTools` / `streamWithTools` 时由库自动写入);`assistant` 还可选 **`reasoningContent?: string`**,序列化为 **`reasoning_content`**(思考模式 + 工具调用时,部分供应商要求后续轮完整回传) - `tool`:**必须**包含 `toolCallId: string`,且与上一轮 assistant 的 `tool_calls[].id` 对应 ### 6.3 `XContentPart` @@ -126,6 +150,7 @@ const xllm = createXllm(options?: XClientOptions): XllmClient; |--------|------| | `response.start` | 流开始,含 `provider`, `model`, `requestId?` | | `text.delta` | 文本增量 | +| `reasoning.delta` | 思考链增量(如 `reasoning_content`) | | `tool_call.delta` | 工具参数增量 | | `tool_call.done` | 单个工具调用片段结束 | | `response.usage` | 用量 | @@ -148,10 +173,11 @@ const xllm = createXllm(options?: XClientOptions): XllmClient; - 额外选项:`maxRounds?`(默认 `5`),`toolErrorStrategy?`(默认 `"throw"`) - 返回:`{ response, rounds, toolCallsExecuted }` - 行为:每轮 `generate`;若有 `toolCalls`,执行 `executors[name]`,将结果写入 tool 消息并追加历史,直到无工具调用或超出 `maxRounds` +- **思考模式 + 工具调用(DeepSeek)**:若本轮 `XResponse.reasoning` 有值(来自 `message.reasoning_content`),库会把同一全文写入历史 assistant 的 `reasoningContent`,下一轮请求序列化为 `reasoning_content`,满足「后续请求须完整回传」的要求。 ### 8.3 `streamWithTools`(流式闭环) -- 每轮消费完整 `stream`;收集 `tool_call.done` 与拼接的 `assistantText`;执行工具后追加 `assistant`(含 `toolCalls`)与 `tool` 消息,再进入下一轮 +- 每轮消费完整 `stream`;收集 `tool_call.done` 与拼接的 `assistantText`;**同时拼接本轮全部 `reasoning.delta` 为 `reasoningContent`**;执行工具后追加 `assistant`(含 `toolCalls` 与可选 `reasoningContent`)与 `tool` 消息,再进入下一轮 - 事件仍原样 `yield` 给调用方 ### 8.4 `XToolExecutor` 与 `XToolExecutorMap` @@ -226,6 +252,7 @@ const xllm = createXllm({ - [ ] 工具闭环是否优先使用 `chatWithTools` / `streamWithTools`,避免手写 `tool_calls` 顺序错误? - [ ] 多模态是否确认目标模型支持,否则仅发 `text`? - [ ] 工具失败策略是否为生产环境显式选择 `toolErrorStrategy`? +- [ ] 思考类参数是否确认与供应商文档一致?否则是否改用 `providerExtras`? --- diff --git a/packages/xllm/src/client/chat-with-tools.ts b/packages/xllm/src/client/chat-with-tools.ts index 39693f2..28cc856 100644 --- a/packages/xllm/src/client/chat-with-tools.ts +++ b/packages/xllm/src/client/chat-with-tools.ts @@ -40,6 +40,9 @@ export const chatWithTools = async ( model: input.model, apiKey: input.apiKey, baseURL: input.baseURL, + thinking: input.thinking, + reasoningEffort: input.reasoningEffort, + providerExtras: input.providerExtras, }; while (rounds < maxRounds) { @@ -57,6 +60,7 @@ export const chatWithTools = async ( role: "assistant", content: [{ type: "text", text: response.text }], toolCalls: response.toolCalls, + ...(response.reasoning ? { reasoningContent: response.reasoning } : {}), }; const toolMessages: XMessage[] = []; diff --git a/packages/xllm/src/client/stream-with-tools.ts b/packages/xllm/src/client/stream-with-tools.ts index 87803c4..5dee2de 100644 --- a/packages/xllm/src/client/stream-with-tools.ts +++ b/packages/xllm/src/client/stream-with-tools.ts @@ -32,6 +32,7 @@ export const streamWithTools = async function* ( rounds += 1; const toolCalls: XToolCall[] = []; let assistantText = ""; + let assistantReasoning = ""; for await (const event of stream(options, { ...input, @@ -41,6 +42,9 @@ export const streamWithTools = async function* ( if (event.type === "text.delta") { assistantText += event.text; } + if (event.type === "reasoning.delta") { + assistantReasoning += event.text; + } if (event.type === "tool_call.done") { toolCalls.push(event.toolCall); } @@ -67,7 +71,12 @@ export const streamWithTools = async function* ( currentMessages = [ ...currentMessages, - { role: "assistant", content: [{ type: "text", text: assistantText }], toolCalls }, + { + role: "assistant", + content: [{ type: "text", text: assistantText }], + toolCalls, + ...(assistantReasoning ? { reasoningContent: assistantReasoning } : {}), + }, ...toolMessages, ]; } diff --git a/packages/xllm/src/core/types.ts b/packages/xllm/src/core/types.ts index 6803702..75da46c 100644 --- a/packages/xllm/src/core/types.ts +++ b/packages/xllm/src/core/types.ts @@ -11,6 +11,11 @@ export type XMessage = role: "system" | "user" | "assistant"; content: XContentPart[]; toolCalls?: XToolCall[]; + /** + * 思考模式下、含工具调用的 assistant 轮次:DeepSeek 等要求后续请求完整回传 `reasoning_content`。 + * 序列化为供应商消息字段 `reasoning_content`(仅 assistant 有效)。 + */ + reasoningContent?: string; } | { role: "tool"; @@ -36,6 +41,15 @@ export type XToolErrorStrategy = "throw" | "return_tool_error_message" | "skip"; export type XToolChoice = "auto" | "none" | { name: string }; +/** DeepSeek 等兼容接口:OpenAI 形态下的思考模式开关,对应请求体 `thinking`。 */ +export type XThinkingMode = { type: "enabled" | "disabled" }; + +/** + * DeepSeek 等:`reasoning_effort` 输入档位。 + * 供应商侧映射:low / medium → high,xhigh → max(与官方说明一致)。 + */ +export type XReasoningEffortInput = "low" | "medium" | "high" | "max" | "xhigh"; + export interface XRequest { messages: XMessage[]; tools?: XToolDefinition[]; @@ -49,6 +63,20 @@ export interface XRequest { model?: string; apiKey?: string; baseURL?: string; + /** + * 思考模式(**非通用**):仅适用于在 OpenAI Chat Completions 兼容体里使用 + * `thinking` 字段的供应商(如 DeepSeek 文档所述)。其它厂商若字段名不同,请用 `providerExtras` 或单独适配器。 + */ + thinking?: XThinkingMode; + /** + * 思考强度(**非通用**):写入请求体 `reasoning_effort`(库内映射为 `high` / `max`)。 + * 其它厂商请用 `providerExtras`。 + */ + reasoningEffort?: XReasoningEffortInput; + /** + * 合并进请求 JSON 根对象的额外字段(**最后一层合并**,可覆盖同名标准字段;用于各供应商专有参数)。 + */ + providerExtras?: Record; } export interface XUsage { @@ -59,6 +87,8 @@ export interface XUsage { export interface XResponse { text: string; + /** 思考链 / 推理过程全文(若供应商在 message 中返回 `reasoning_content` 等) */ + reasoning?: string; toolCalls: XToolCall[]; usage?: XUsage; provider: XProviderName; @@ -69,6 +99,7 @@ export interface XResponse { export type XStreamEvent = | { type: "response.start"; provider: XProviderName; model: string; requestId?: string } + | { type: "reasoning.delta"; text: string } | { type: "text.delta"; text: string } | { type: "tool_call.delta"; id: string; name?: string; argumentsDelta?: string } | { type: "tool_call.done"; toolCall: XToolCall } diff --git a/packages/xllm/src/index.test.ts b/packages/xllm/src/index.test.ts index e00139a..32ddf68 100644 --- a/packages/xllm/src/index.test.ts +++ b/packages/xllm/src/index.test.ts @@ -104,6 +104,7 @@ describe("xllm", () => { { message: { content: "", + reasoning_content: "need weather tool for Shanghai", tool_calls: [ { id: "call_1", @@ -153,13 +154,20 @@ describe("xllm", () => { expect(secondRequestBody?.messages?.some((msg: any) => Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0)).toBe( true, ); + const assistantWithReasoning = secondRequestBody?.messages?.find( + (msg: any) => msg.role === "assistant" && typeof msg.reasoning_content === "string", + ); + expect(assistantWithReasoning?.reasoning_content).toBe("need weather tool for Shanghai"); expect(result.toolCallsExecuted).toBe(1); expect(result.response.text).toContain("上海"); }); it("streams with tool loop and yields final text", async () => { let call = 0; + let secondBody: any = undefined; const sseToolRound = [ + 'data: {"id":"r1","model":"deepseek-chat","choices":[{"delta":{"reasoning_content":"cot "},"finish_reason":null}]}\n\n', + 'data: {"id":"r1","model":"deepseek-chat","choices":[{"delta":{"reasoning_content":"here"},"finish_reason":null}]}\n\n', 'data: {"id":"r1","model":"deepseek-chat","choices":[{"delta":{"tool_calls":[{"index":0,"id":"call_1","function":{"name":"get_weather","arguments":"{\\"city\\":\\"Shanghai\\"}"}}]},"finish_reason":"tool_calls"}]}\n\n', "data: [DONE]\n\n", ]; @@ -167,8 +175,11 @@ describe("xllm", () => { 'data: {"id":"r2","model":"deepseek-chat","choices":[{"delta":{"content":"上海今天多云。"},"finish_reason":"stop"}]}\n\n', "data: [DONE]\n\n", ]; - const mockFetch: typeof fetch = async () => { + const mockFetch: typeof fetch = async (_input, init) => { call += 1; + if (call === 2 && typeof init?.body === "string") { + secondBody = JSON.parse(init.body); + } const frames = call === 1 ? sseToolRound : sseFinalRound; return new Response(toSSEBody(frames), { status: 200, @@ -195,6 +206,8 @@ describe("xllm", () => { } expect(call).toBe(2); expect(text).toContain("上海"); + const asst = secondBody?.messages?.find((m: any) => m.role === "assistant" && m.reasoning_content); + expect(asst?.reasoning_content).toBe("cot here"); }); it("returns tool error message when strategy is return_tool_error_message", async () => { @@ -246,4 +259,94 @@ describe("xllm", () => { expect(result.toolCallsExecuted).toBe(1); expect(result.response.text).toContain("工具错误"); }); + + it("sends thinking and reasoning_effort for DeepSeek-style bodies", async () => { + const bodies: Array> = []; + const mockFetch: typeof fetch = async (_input, init) => { + bodies.push(typeof init?.body === "string" ? JSON.parse(init.body) : {}); + return toJsonResponse({ + model: "deepseek-chat", + choices: [{ message: { content: "ok" }, finish_reason: "stop" }], + }); + }; + const client = createXllm({ provider: "deepseek", apiKey: "test", fetch: mockFetch }); + await client.generate({ + messages: [{ role: "user", content: [{ type: "text", text: "hi" }] }], + thinking: { type: "disabled" }, + reasoningEffort: "medium", + }); + expect(bodies[0]?.thinking).toEqual({ type: "disabled" }); + expect(bodies[0]?.reasoning_effort).toBe("high"); + await client.generate({ + messages: [{ role: "user", content: [{ type: "text", text: "hi" }] }], + reasoningEffort: "xhigh", + }); + expect(bodies[1]?.reasoning_effort).toBe("max"); + }); + + it("parses reasoning_content in non-stream response", async () => { + const mockFetch: typeof fetch = async () => + toJsonResponse({ + choices: [ + { + message: { content: "final", reasoning_content: "chain of thought" }, + finish_reason: "stop", + }, + ], + }); + const client = createXllm({ provider: "deepseek", apiKey: "test", fetch: mockFetch }); + const result = await client.generate({ + messages: [{ role: "user", content: [{ type: "text", text: "q" }] }], + }); + expect(result.text).toBe("final"); + expect(result.reasoning).toBe("chain of thought"); + }); + + it("streams reasoning.delta when delta.reasoning_content is present", async () => { + const sseFrames = [ + 'data: {"id":"r1","model":"deepseek-chat","choices":[{"delta":{"reasoning_content":"think"},"finish_reason":null}]}\n\n', + 'data: {"id":"r1","model":"deepseek-chat","choices":[{"delta":{"content":"out"},"finish_reason":"stop"}]}\n\n', + "data: [DONE]\n\n", + ]; + const mockFetch: typeof fetch = async () => + new Response(toSSEBody(sseFrames), { + status: 200, + headers: { "content-type": "text/event-stream" }, + }); + const client = createXllm({ provider: "deepseek", apiKey: "test", fetch: mockFetch }); + const events: XStreamEvent[] = []; + for await (const event of client.stream({ + messages: [{ role: "user", content: [{ type: "text", text: "hi" }] }], + })) { + events.push(event); + } + const reasoning = events + .filter((e): e is Extract => e.type === "reasoning.delta") + .map((e) => e.text) + .join(""); + const text = events + .filter((e): e is Extract => e.type === "text.delta") + .map((e) => e.text) + .join(""); + expect(reasoning).toBe("think"); + expect(text).toBe("out"); + }); + + it("merges providerExtras into request body last", async () => { + let body: Record = {}; + const mockFetch: typeof fetch = async (_input, init) => { + body = typeof init?.body === "string" ? JSON.parse(init.body) : {}; + return toJsonResponse({ + choices: [{ message: { content: "ok" }, finish_reason: "stop" }], + }); + }; + const client = createXllm({ provider: "openai-compatible", apiKey: "test", fetch: mockFetch }); + await client.generate({ + messages: [{ role: "user", content: [{ type: "text", text: "hi" }] }], + temperature: 0.5, + providerExtras: { temperature: 0.9, custom_vendor_flag: true }, + }); + expect(body.temperature).toBe(0.9); + expect(body.custom_vendor_flag).toBe(true); + }); }); diff --git a/packages/xllm/src/index.ts b/packages/xllm/src/index.ts index 28e7f06..f29f5b5 100644 --- a/packages/xllm/src/index.ts +++ b/packages/xllm/src/index.ts @@ -7,9 +7,11 @@ export type { XContentPart, XMessage, XProviderName, + XReasoningEffortInput, XRequest, XResponse, XStreamEvent, + XThinkingMode, XToolCall, XToolChoice, XToolDefinition, diff --git a/packages/xllm/src/providers/openai-compatible.adapter.ts b/packages/xllm/src/providers/openai-compatible.adapter.ts index 081d8f6..c78fb5d 100644 --- a/packages/xllm/src/providers/openai-compatible.adapter.ts +++ b/packages/xllm/src/providers/openai-compatible.adapter.ts @@ -3,6 +3,7 @@ import type { XContentPart, XMessage, XProviderName, + XReasoningEffortInput, XRequest, XResponse, XStreamEvent, @@ -37,7 +38,7 @@ const toProviderMessage = (message: XMessage): Record => { } if (message.role === "assistant" && Array.isArray(message.toolCalls) && message.toolCalls.length > 0) { - return { + const out: Record = { role: "assistant", content: toProviderContent(message.content), tool_calls: message.toolCalls.map((toolCall) => ({ @@ -49,12 +50,20 @@ const toProviderMessage = (message: XMessage): Record => { }, })), }; + if (message.reasoningContent) { + out.reasoning_content = message.reasoningContent; + } + return out; } - return { + const base: Record = { role: message.role, content: toProviderContent(message.content), }; + if (message.role === "assistant" && message.reasoningContent) { + base.reasoning_content = message.reasoningContent; + } + return base; }; const toUsage = (usage: any): XUsage | undefined => { @@ -78,6 +87,21 @@ const mapToolChoice = (toolChoice: XRequest["toolChoice"]): unknown => { return { type: "function", function: { name: toolChoice.name } }; }; +/** DeepSeek:low/medium → high,xhigh → max,与官方兼容说明一致。 */ +const normalizeReasoningEffortForProvider = (effort: XReasoningEffortInput): "high" | "max" => { + if (effort === "max" || effort === "xhigh") return "max"; + return "high"; +}; + +const extractReasoningText = (message: Record | undefined): string | undefined => { + if (!message) return undefined; + const rc = message.reasoning_content; + if (typeof rc === "string" && rc.length > 0) return rc; + const r = message.reasoning; + if (typeof r === "string" && r.length > 0) return r; + return undefined; +}; + export const openAICompatibleAdapter: ProviderAdapter = { name: "openai-compatible", @@ -104,6 +128,17 @@ export const openAICompatibleAdapter: ProviderAdapter = { body.tool_choice = mapToolChoice(input.toolChoice); } + if (input.thinking) { + body.thinking = input.thinking; + } + if (input.reasoningEffort) { + body.reasoning_effort = normalizeReasoningEffortForProvider(input.reasoningEffort); + } + + if (input.providerExtras) { + Object.assign(body, input.providerExtras); + } + return { method: "POST", url: `${config.baseURL || DEFAULT_OPENAI_COMPATIBLE_BASE_URL}/chat/completions`, @@ -121,8 +156,10 @@ export const openAICompatibleAdapter: ProviderAdapter = { const choice = response?.choices?.[0] ?? {}; const message = choice?.message ?? {}; + const msg = message as Record | undefined; return { - text: message?.content ?? "", + text: (message as { content?: string })?.content ?? "", + reasoning: extractReasoningText(msg), toolCalls: Array.isArray(message?.tool_calls) ? message.tool_calls.map(toToolCall) : [], usage: toUsage(response?.usage), provider, @@ -150,6 +187,13 @@ export const openAICompatibleAdapter: ProviderAdapter = { if (!choice) return events; const delta = choice.delta ?? {}; + if (typeof delta.reasoning_content === "string" && delta.reasoning_content.length > 0) { + events.push({ type: "reasoning.delta", text: delta.reasoning_content }); + } + if (typeof (delta as { reasoning?: string }).reasoning === "string") { + const r = (delta as { reasoning: string }).reasoning; + if (r.length > 0) events.push({ type: "reasoning.delta", text: r }); + } if (typeof delta.content === "string" && delta.content.length > 0) { events.push({ type: "text.delta", text: delta.content }); } diff --git a/tsconfig.tsbuildinfo b/tsconfig.tsbuildinfo deleted file mode 100644 index 5174016..0000000 --- a/tsconfig.tsbuildinfo +++ /dev/null @@ -1 +0,0 @@ -{"fileNames":["./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es5.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.dom.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/.bun/typescript@6.0.2/node_modules/typescript/lib/lib.decorators.legacy.d.ts","./packages/core/src/event.ts","./node_modules/.bun/@vitest+pretty-format@4.1.4/node_modules/@vitest/pretty-format/dist/index.d.ts","./node_modules/.bun/@vitest+utils@4.1.4/node_modules/@vitest/utils/dist/display.d.ts","./node_modules/.bun/@vitest+utils@4.1.4/node_modules/@vitest/utils/dist/types.d.ts","./node_modules/.bun/@vitest+utils@4.1.4/node_modules/@vitest/utils/dist/helpers.d.ts","./node_modules/.bun/@vitest+utils@4.1.4/node_modules/@vitest/utils/dist/timers.d.ts","./node_modules/.bun/@vitest+utils@4.1.4/node_modules/@vitest/utils/dist/index.d.ts","./node_modules/.bun/@vitest+utils@4.1.4/node_modules/@vitest/utils/dist/types.d-bcelap-c.d.ts","./node_modules/.bun/@vitest+utils@4.1.4/node_modules/@vitest/utils/dist/diff.d.ts","./node_modules/.bun/@vitest+runner@4.1.4/node_modules/@vitest/runner/dist/tasks.d-bh0ijn67.d.ts","./node_modules/.bun/@vitest+runner@4.1.4/node_modules/@vitest/runner/dist/index.d.ts","./node_modules/.bun/vitest@4.1.4+1e0cb1bedf0614c6/node_modules/vitest/dist/chunks/traces.d.402v_yfi.d.ts","./node_modules/.bun/vite@8.0.8+1e0cb1bedf0614c6/node_modules/vite/types/hmrpayload.d.ts","./node_modules/.bun/vite@8.0.8+1e0cb1bedf0614c6/node_modules/vite/dist/node/chunks/modulerunnertransport.d.ts","./node_modules/.bun/vite@8.0.8+1e0cb1bedf0614c6/node_modules/vite/types/customevent.d.ts","./node_modules/.bun/vite@8.0.8+1e0cb1bedf0614c6/node_modules/vite/types/hot.d.ts","./node_modules/.bun/vite@8.0.8+1e0cb1bedf0614c6/node_modules/vite/dist/node/module-runner.d.ts","./node_modules/.bun/@vitest+snapshot@4.1.4/node_modules/@vitest/snapshot/dist/environment.d-dojxxzv9.d.ts","./node_modules/.bun/@vitest+snapshot@4.1.4/node_modules/@vitest/snapshot/dist/rawsnapshot.d-d_x3-62x.d.ts","./node_modules/.bun/@vitest+snapshot@4.1.4/node_modules/@vitest/snapshot/dist/index.d.ts","./node_modules/.bun/vitest@4.1.4+1e0cb1bedf0614c6/node_modules/vitest/dist/chunks/config.d.chuh6-ad.d.ts","./node_modules/.bun/vitest@4.1.4+1e0cb1bedf0614c6/node_modules/vitest/dist/chunks/environment.d.crsxczp1.d.ts","./node_modules/.bun/vitest@4.1.4+1e0cb1bedf0614c6/node_modules/vitest/dist/chunks/rpc.d.bfmwpdph.d.ts","./node_modules/.bun/vitest@4.1.4+1e0cb1bedf0614c6/node_modules/vitest/dist/chunks/worker.d.ccknuvi5.d.ts","./node_modules/.bun/vitest@4.1.4+1e0cb1bedf0614c6/node_modules/vitest/dist/chunks/browser.d.c0zgu1u9.d.ts","./node_modules/.bun/@vitest+spy@4.1.4/node_modules/@vitest/spy/optional-types.d.ts","./node_modules/.bun/@vitest+spy@4.1.4/node_modules/@vitest/spy/dist/index.d.ts","./node_modules/.bun/tinyrainbow@3.1.0/node_modules/tinyrainbow/dist/index.d.ts","./node_modules/.bun/@standard-schema+spec@1.1.0/node_modules/@standard-schema/spec/dist/index.d.ts","./node_modules/.bun/@types+deep-eql@4.0.2/node_modules/@types/deep-eql/index.d.ts","./node_modules/.bun/assertion-error@2.0.1/node_modules/assertion-error/index.d.ts","./node_modules/.bun/@types+chai@5.2.3/node_modules/@types/chai/index.d.ts","./node_modules/.bun/@vitest+expect@4.1.4/node_modules/@vitest/expect/dist/index.d.ts","./node_modules/.bun/@vitest+runner@4.1.4/node_modules/@vitest/runner/dist/utils.d.ts","./node_modules/.bun/tinybench@2.9.0/node_modules/tinybench/dist/index.d.ts","./node_modules/.bun/vitest@4.1.4+1e0cb1bedf0614c6/node_modules/vitest/dist/chunks/benchmark.d.daahlpsq.d.ts","./node_modules/.bun/vitest@4.1.4+1e0cb1bedf0614c6/node_modules/vitest/dist/chunks/global.d.d74z04p1.d.ts","./node_modules/.bun/vitest@4.1.4+1e0cb1bedf0614c6/node_modules/vitest/optional-runtime-types.d.ts","./node_modules/.bun/@vitest+mocker@4.1.4+6ab6fbb2174f3c3e/node_modules/@vitest/mocker/dist/types.d-bji5eawu.d.ts","./node_modules/.bun/@vitest+mocker@4.1.4+6ab6fbb2174f3c3e/node_modules/@vitest/mocker/dist/index.d-b41z0auw.d.ts","./node_modules/.bun/@vitest+mocker@4.1.4+6ab6fbb2174f3c3e/node_modules/@vitest/mocker/dist/index.d.ts","./node_modules/.bun/vitest@4.1.4+1e0cb1bedf0614c6/node_modules/vitest/dist/chunks/suite.d.udjtyagw.d.ts","./node_modules/.bun/vitest@4.1.4+1e0cb1bedf0614c6/node_modules/vitest/dist/chunks/evaluatedmodules.d.bxj5omdx.d.ts","./node_modules/.bun/vitest@4.1.4+1e0cb1bedf0614c6/node_modules/vitest/dist/runners.d.ts","./node_modules/.bun/expect-type@1.3.0/node_modules/expect-type/dist/utils.d.ts","./node_modules/.bun/expect-type@1.3.0/node_modules/expect-type/dist/overloads.d.ts","./node_modules/.bun/expect-type@1.3.0/node_modules/expect-type/dist/branding.d.ts","./node_modules/.bun/expect-type@1.3.0/node_modules/expect-type/dist/messages.d.ts","./node_modules/.bun/expect-type@1.3.0/node_modules/expect-type/dist/index.d.ts","./node_modules/.bun/vitest@4.1.4+1e0cb1bedf0614c6/node_modules/vitest/dist/index.d.ts","./packages/core/src/index.test.ts","./packages/core/src/index.ts","./packages/dx/src/index.ts","./packages/xllm/src/core/types.ts","./packages/xllm/src/core/errors.ts","./packages/xllm/src/providers/types.ts","./packages/xllm/src/providers/openai-compatible.adapter.ts","./packages/xllm/src/providers/deepseek.adapter.ts","./packages/xllm/src/providers/registry.ts","./packages/xllm/src/runtime/config.ts","./packages/xllm/src/runtime/http.ts","./packages/xllm/src/client/generate.ts","./packages/xllm/src/client/tool-loop-shared.ts","./packages/xllm/src/client/chat-with-tools.ts","./packages/xllm/src/runtime/sse.ts","./packages/xllm/src/client/stream.ts","./packages/xllm/src/client/stream-with-tools.ts","./packages/xllm/src/client/create-xllm.ts","./packages/xllm/src/index.ts","./packages/example/src/xllm/env.ts","./packages/example/src/xllm/demos/types.ts","./packages/example/src/xllm/demos/generate.ts","./packages/example/src/xllm/demos/request-switch.ts","./packages/example/src/xllm/demos/stream.ts","./packages/example/src/xllm/demos/stream-with-tools.ts","./packages/example/src/xllm/capabilities.ts","./packages/example/src/xllm/demos/tools-multimodal.ts","./packages/example/src/xllm/demos/catalog.ts","./packages/example/src/xllm-app.ts","./packages/example/src/index.ts","./packages/example/src/sm.ts","./packages/xllm/src/index.test.ts"],"fileIdsList":[[89,90],[62,68,86,87,88,91],[98],[98,99],[66,68,69],[66,68],[66],[61,66,77,78],[61,66,77],[85],[61,67],[61],[63],[61,62,63,64,65],[104,105],[104,105,106,107],[104,106],[104],[72],[72,73,74,75],[74],[70,93,94,96],[70,71,83,96],[61,68,70,79,96],[76],[61,70,79,82,92,95,96],[70,71,76,79,96],[70,93,94,95,96],[70,76,80,81,82,96],[61,66,68,70,71,76,79,80,81,82,83,84,86,92,93,94,95,96,97,100,101,102,103,108],[61,68,70,71,79,80,93,94,95,96,101],[109],[60],[111],[138],[129,137],[128],[128,129,130,131,132,133,134,136],[130],[129,130],[128,129,130,135],[113,114,121,122],[113,121,123,125,126],[113,118,119,120],[113,114,122,125],[113,114,115,118,119,120,124],[113,114],[109,128],[113,114,127],[114,115,116],[113,114,115],[113,115,116,117],[114,115]],"fileInfos":[{"version":"bcd24271a113971ba9eb71ff8cb01bc6b0f872a85c23fdbe5d93065b375933cd","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"3f88bedbeb09c6f5a6645cb24c7c55f1aa22d19ae96c8e6959cbd8b85a707bc6","signature":false,"impliedFormat":1},{"version":"7fe93b39b810eadd916be8db880dd7f0f7012a5cc6ffb62de8f62a2117fa6f1f","signature":false,"impliedFormat":1},{"version":"bb0074cc08b84a2374af33d8bf044b80851ccc9e719a5e202eacf40db2c31600","signature":false,"impliedFormat":1},{"version":"1a7daebe4f45fb03d9ec53d60008fbf9ac45a697fdc89e4ce218bc94b94f94d6","signature":false,"impliedFormat":1},{"version":"f94b133a3cb14a288803be545ac2683e0d0ff6661bcd37e31aaaec54fc382aed","signature":false,"impliedFormat":1},{"version":"f59d0650799f8782fd74cf73c19223730c6d1b9198671b1c5b3a38e1188b5953","signature":false,"impliedFormat":1},{"version":"8a15b4607d9a499e2dbeed9ec0d3c0d7372c850b2d5f1fb259e8f6d41d468a84","signature":false,"impliedFormat":1},{"version":"26e0fe14baee4e127f4365d1ae0b276f400562e45e19e35fd2d4c296684715e6","signature":false,"impliedFormat":1},{"version":"d6b1eba8496bdd0eed6fc8a685768fe01b2da4a0388b5fe7df558290bffcf32f","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"7f57fc4404ff020bc45b9c620aff2b40f700b95fe31164024c453a5e3c163c54","signature":false,"impliedFormat":1},{"version":"eadcffda2aa84802c73938e589b9e58248d74c59cb7fcbca6474e3435ac15504","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"105ba8ff7ba746404fe1a2e189d1d3d2e0eb29a08c18dded791af02f29fb4711","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"00343ca5b2e3d48fa5df1db6e32ea2a59afab09590274a6cccb1dbae82e60c7c","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"ebd9f816d4002697cb2864bea1f0b70a103124e18a8cd9645eeccc09bdf80ab4","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"2c1afac30a01772cd2a9a298a7ce7706b5892e447bb46bdbeef720f7b5da77ad","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"7b0225f483e4fa685625ebe43dd584bb7973bbd84e66a6ba7bbe175ee1048b4f","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"c0a4b8ac6ce74679c1da2b3795296f5896e31c38e888469a8e0f99dc3305de60","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"3084a7b5f569088e0146533a00830e206565de65cae2239509168b11434cd84f","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"c5079c53f0f141a0698faa903e76cb41cd664e3efb01cc17a5c46ec2eb0bef42","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"32cafbc484dea6b0ab62cf8473182bbcb23020d70845b406f80b7526f38ae862","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"fca4cdcb6d6c5ef18a869003d02c9f0fd95df8cfaf6eb431cd3376bc034cad36","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"b93ec88115de9a9dc1b602291b85baf825c85666bf25985cc5f698073892b467","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"f5c06dcc3fe849fcb297c247865a161f995cc29de7aa823afdd75aaaddc1419b","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"b77e16112127a4b169ef0b8c3a4d730edf459c5f25fe52d5e436a6919206c4d7","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"fbffd9337146eff822c7c00acbb78b01ea7ea23987f6c961eba689349e744f8c","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"a995c0e49b721312f74fdfb89e4ba29bd9824c770bbb4021d74d2bf560e4c6bd","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"c7b3542146734342e440a84b213384bfa188835537ddbda50d30766f0593aff9","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"ce6180fa19b1cccd07ee7f7dbb9a367ac19c0ed160573e4686425060b6df7f57","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"3f02e2476bccb9dbe21280d6090f0df17d2f66b74711489415a8aa4df73c9675","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"45e3ab34c1c013c8ab2dc1ba4c80c780744b13b5676800ae2e3be27ae862c40c","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"805c86f6cca8d7702a62a844856dbaa2a3fd2abef0536e65d48732441dde5b5b","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"e42e397f1a5a77994f0185fd1466520691456c772d06bf843e5084ceb879a0ad","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"f4c2b41f90c95b1c532ecc874bd3c111865793b23aebcc1c3cbbabcd5d76ffb0","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"ab26191cfad5b66afa11b8bf935ef1cd88fabfcb28d30b2dfa6fad877d050332","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"2088bc26531e38fb05eedac2951480db5309f6be3fa4a08d2221abb0f5b4200d","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"cb9d366c425fea79716a8fb3af0d78e6b22ebbab3bd64d25063b42dc9f531c1e","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"500934a8089c26d57ebdb688fc9757389bb6207a3c8f0674d68efa900d2abb34","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"689da16f46e647cef0d64b0def88910e818a5877ca5379ede156ca3afb780ac3","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"bc21cc8b6fee4f4c2440d08035b7ea3c06b3511314c8bab6bef7a92de58a2593","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"7ca53d13d2957003abb47922a71866ba7cb2068f8d154877c596d63c359fed25","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"54725f8c4df3d900cb4dac84b64689ce29548da0b4e9b7c2de61d41c79293611","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"e5594bc3076ac29e6c1ebda77939bc4c8833de72f654b6e376862c0473199323","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"2f3eb332c2d73e729f3364fcc0c2b375e72a121e8157d25a82d67a138c83a95c","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"6f4427f9642ce8d500970e4e69d1397f64072ab73b97e476b4002a646ac743b1","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"48915f327cd1dea4d7bd358d9dc7732f58f9e1626a29cc0c05c8c692419d9bb7","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"b7bf9377723203b5a6a4b920164df22d56a43f593269ba6ae1fdc97774b68855","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"db9709688f82c9e5f65a119c64d835f906efe5f559d08b11642d56eb85b79357","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"4b25b8c874acd1a4cf8444c3617e037d444d19080ac9f634b405583fd10ce1f7","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"37be57d7c90cf1f8112ee2636a068d8fd181289f82b744160ec56a7dc158a9f5","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"a917a49ac94cd26b754ab84e113369a75d1a47a710661d7cd25e961cc797065f","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"6d3261badeb7843d157ef3e6f5d1427d0eeb0af0cf9df84a62cfd29fd47ac86e","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"195daca651dde22f2167ac0d0a05e215308119a3100f5e6268e8317d05a92526","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"8b11e4285cd2bb164a4dc09248bdec69e9842517db4ca47c1ba913011e44ff2f","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"0508571a52475e245b02bc50fa1394065a0a3d05277fbf5120c3784b85651799","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"8f9af488f510c3015af3cc8c267a9e9d96c4dd38a1fdff0e11dc5a544711415b","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"fc611fea8d30ea72c6bbfb599c9b4d393ce22e2f5bfef2172534781e7d138104","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"1ce14b81c5cc821994aa8ec1d42b220dd41b27fcc06373bce3958af7421b77d4","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"b3a048b3e9302ef9a34ef4ebb9aecfb28b66abb3bce577206a79fee559c230da","signature":false,"affectsGlobalScope":true,"impliedFormat":1},{"version":"f5db38608b96a16a1b25b6adfd34a86bbad4f35da7a64b16990ba98868d8b05e","signature":false},{"version":"3a582c6e8906f5b094ccf0de6cc6f4f8a54b05a34f52517aba5c9c7f704f6b28","signature":false,"impliedFormat":99},{"version":"0528f6d21f7a02d4092895090d2dd86104bd5a3e79eced96d5a1a7dd90943d17","signature":false,"impliedFormat":99},{"version":"b5ce343886d23392be9c8280e9f24a87f1d7d3667f6672c2fe4aa61fa4ece7d4","signature":false,"impliedFormat":99},{"version":"72ce5b734c05da85c85a6f6dc05823b051d6aa41acaedeeb1d17c72f3b4efa72","signature":false,"impliedFormat":99},{"version":"b0857bb28fd5236ace84280f79a25093f919fd0eff13e47cc26ea03de60a7294","signature":false,"impliedFormat":99},{"version":"5e43e0824f10cd8c48e7a8c5c673638488925a12c31f0f9e0957965c290eb14c","signature":false,"impliedFormat":99},{"version":"ef13c73d6157a32933c612d476c1524dd674cf5b9a88571d7d6a0d147544d529","signature":false,"impliedFormat":99},{"version":"3b0a56d056d81a011e484b9c05d5e430711aaecd561a788bad1d0498aad782c7","signature":false,"impliedFormat":99},{"version":"9443967db823b66d1682be7fc66392be7c7924e10c3e54900f456341e94591a6","signature":false,"impliedFormat":99},{"version":"424f71d1fae96ac2e878af92345bb87bea1d29f757228fbc190133b305643f2c","signature":false,"impliedFormat":99},{"version":"ac3d263474022e9a14c43f588f485d549641d839b159ecc971978b90f34bdf6b","signature":false,"impliedFormat":99},{"version":"3b89216a7e38a454985ad17bb2ff85792837dc812f2a89fa5f60ad0a2e216fa7","signature":false,"impliedFormat":99},{"version":"16fe60bb544cfedfd2b5bb2f7d0b3957be7978706d57d9f06edc9c0c8dbdba23","signature":false,"impliedFormat":99},{"version":"82179358c2d9d7347f1602dc9300039a2250e483137b38ebf31d4d2e5519c181","signature":false,"impliedFormat":99},{"version":"4e003c868b0d8f8ad200b96cbc653e18e513fa23e1c19c4fe3cc25d4394efc47","signature":false,"impliedFormat":99},{"version":"091546ac9077cddcd7b9479cc2e0c677238bf13e39eab4b13e75046c3328df93","signature":false,"impliedFormat":99},{"version":"42a12f2faa483c9b48195ed794d22698162274e755f6e07219c2351c4f08d732","signature":false,"impliedFormat":99},{"version":"ec0c42bb0f465e4993f2bc68a6ce9df9a2dcbc7b83e21748f82f1b69561938e3","signature":false,"impliedFormat":99},{"version":"f50ff37a9cbbe74475f426474d9827083c7c2c138a954d28f1690df338f69291","signature":false,"impliedFormat":99},{"version":"6bb6d57454370324434bcf355942dee45b0e0d8ab0fa3e98bafe8a30718273b4","signature":false,"impliedFormat":99},{"version":"bcbd3becd08b4515225880abea0dbfbbf0d1181ce3af8f18f72f61edbe4febfb","signature":false,"impliedFormat":99},{"version":"a86701e56b10a6d1ef9b2ecaeedbab94ed7b957a646cd71fd09d02b323c6d3d7","signature":false,"impliedFormat":99},{"version":"976932e3807786cdae46ed5dfcd02c44f3fa25c157a0e8392f5a2dabb9a14a4e","signature":false,"impliedFormat":99},{"version":"59b7a8ec1781284f6602af48487b68fc3baadf34cb4cbcbb31f213b6712fac34","signature":false,"impliedFormat":99},{"version":"c76c02846ba7d40b9b3488f0e8d75d02cbdee2f0bc5fcd55dd3bd2e1457646ea","signature":false,"impliedFormat":99},{"version":"4ead13a482c539b77394b2a97e3b877b809eac596390371cea490286f53b996a","signature":false,"impliedFormat":99},{"version":"06db2f8ba1d1dfacf04529cb731081ab23f133f29c7608ebdfbcab356996827c","signature":false,"impliedFormat":99},{"version":"bdd14f07b4eca0b4b5203b85b8dbc4d084c749fa590bee5ea613e1641dcd3b29","signature":false,"impliedFormat":99},{"version":"427fe2004642504828c1476d0af4270e6ad4db6de78c0b5da3e4c5ca95052a99","signature":false,"impliedFormat":1},{"version":"2eeffcee5c1661ddca53353929558037b8cf305ffb86a803512982f99bcab50d","signature":false,"impliedFormat":99},{"version":"9afb4cb864d297e4092a79ee2871b5d3143ea14153f62ef0bb04ede25f432030","signature":false,"affectsGlobalScope":true,"impliedFormat":99},{"version":"5c935b7fc4ddc1410ea1cd7cd4e35ed106a6e4920dd27a9480a40fd224359dc3","signature":false,"affectsGlobalScope":true,"impliedFormat":99},{"version":"ed9bb55ddcbebd5cb3eee991f57ff21438546ee40ee1c310281bd12a6c7cf65b","signature":false,"impliedFormat":99},{"version":"69bf2422313487956e4dacf049f30cb91b34968912058d244cb19e4baa24da97","signature":false,"impliedFormat":99},{"version":"6987dfb4b0c4e02112cc4e548e7a77b3d9ddfeffa8c8a2db13ceac361a4567d9","signature":false,"impliedFormat":99},{"version":"4c3d12ac5744ff4ba2e1ce97ec307f09d726b4cfcfd5eff3315ccc080d620fb9","signature":false,"impliedFormat":99},{"version":"c76c02846ba7d40b9b3488f0e8d75d02cbdee2f0bc5fcd55dd3bd2e1457646ea","signature":false,"impliedFormat":99},{"version":"5e2ba3d18d78aebbde1f34bde356e41e9c76eeaeaeee56a37036596a9eff4211","signature":false,"impliedFormat":99},{"version":"8280ae8ccc0493b32d1742d585357ab9f0a508ea050af25a5a20d64010d0a5cf","signature":false,"impliedFormat":99},{"version":"7adfd9f9056ecd4ae6c65fde2a98654960c662714c73f048478959d04c09e144","signature":false,"impliedFormat":99},{"version":"32b35cf0dc3a1b1a7118b61c34ce2ad1a29695851679f9ec34e0776f2ece2a69","signature":false,"impliedFormat":99},{"version":"b413fbc6658fe2774f8bf9a15cf4c53e586fc38a2d5256b3b9647da242c14389","signature":false,"impliedFormat":99},{"version":"abdaf8c2f20089a6b23a6287007ed16f9cf76d0045ce2973a5f8508c87286d21","signature":false,"impliedFormat":99},{"version":"c30a41267fc04c6518b17e55dcb2b810f267af4314b0b6d7df1c33a76ce1b330","signature":false,"impliedFormat":1},{"version":"72422d0bac4076912385d0c10911b82e4694fc106e2d70added091f88f0824ba","signature":false,"impliedFormat":1},{"version":"da251b82c25bee1d93f9fd80c5a61d945da4f708ca21285541d7aff83ecb8200","signature":false,"impliedFormat":1},{"version":"64db14db2bf37ac089766fdb3c7e1160fabc10e9929bc2deeede7237e4419fc8","signature":false,"impliedFormat":1},{"version":"98b94085c9f78eba36d3d2314affe973e8994f99864b8708122750788825c771","signature":false,"impliedFormat":1},{"version":"8c9917efcdf61e9b9a73ac1e289c612f12db33519ca1445cca41865f7887c737","signature":false,"impliedFormat":99},{"version":"2813ec0f1e4d518d17b1a73361cffeb875cfe809104e851fec721a93961f7b91","signature":false},{"version":"1de89f3ef6365eb499452b2b302f7f2905d641427c4b86adaa318b4ce9f1e044","signature":false},{"version":"5d34f1de1a0e9fb879c0b7a987bf66c046d4f0978f6bd4d44a90018472bfc4b0","signature":false},{"version":"84b4223ccaa5053b24b9ed099633322b442464785cf4b17ca56bdced7d145ea0","signature":false},{"version":"0792f31e34c8331ba6bcc066bc42d470ec4a0303cbc45d138bab630a255aa1ae","signature":false},{"version":"e34006b27dcc314ebf1a8cf97c1578c04177f666e8c443d027a43ca3a6db605c","signature":false},{"version":"0d853721c031c1de541b5ae36c067d629f2dcc0a0c1a8752c17a4d9a950f2d6d","signature":false},{"version":"ff2748fe961ef5ee70f3a8256a3dae6225a04bc78c5e99ae9a09211e8c992737","signature":false},{"version":"b969f135cdb532d0cff8eb3c0b4ee323844c4bd9cf4ef85af290a5f0f9999141","signature":false},{"version":"b49a99deed74ae90b8d3c39c9e00b15b42dd9c8adc122a86cc612f93342036ef","signature":false},{"version":"109d3cd0d9892e28d6ad31358cb66b424b809a03af4b7314685daf880d450a03","signature":false},{"version":"db85fafede692ca4d91926d6315bee7123cc388f4e04d2a9faefde2bce6a3e3c","signature":false},{"version":"4badc7fb8e0319220886a4e5fcda4b730d22e9848e569ff2589024786aeccc49","signature":false},{"version":"945699fe4c2d6993106422a53baffad22aa9ad3d8dfcbefedcf86837336c9e1e","signature":false},{"version":"ad82a15db477e901bb146c319946813da1249958df32cbbf1d1cceb92749f0b6","signature":false},{"version":"609e76208b737e4523d09c24c2dce2356a495ffe290148ed820db9771e93d0cc","signature":false},{"version":"3dcde56099667e722011da7c17c7572f94304060d9f29ff4b5986a01cc743e52","signature":false},{"version":"316b25010a3d015ebd1c0fe3347c7eb3b35ca12618cabd92b02274f5d9385052","signature":false},{"version":"24a678bb7e46a6f1652c9e067290eb834e1b4f82262d667d39c525f643586a2a","signature":false},{"version":"354166849f7600335bfb45dd49081c6d8de4f4618f44dfa5ff6111f673b941e9","signature":false},{"version":"845de9214cbdfeda6d1511b2b108d6ba9a10943bd8ddec10ada013bf1037feb0","signature":false},{"version":"f7fbd27a12991436a90761cf8d801543b43f74844bf161958a0052f79164867e","signature":false},{"version":"75b0324c1ff07d8df9ed748dd527334be053ad1cb4e448287ee2c1ddbf4212a6","signature":false},{"version":"cf8b0df88a8967c4f00be4588ddfedf870db0abd8a97cf30456f7f9377ad5d2c","signature":false},{"version":"f458f63b874429b9a22f0ecf20cfa595cba235804b8500c01392fc8c505e911e","signature":false},{"version":"4bd7fe2cabeb7aeea9d950b01e52dfbeb119909245a9c988dcb4b6bd1726fbce","signature":false},{"version":"4bf4fac01cb012ec03745fb353fd7aa21c3fd98a0dc5b73d33e07910fa95a96c","signature":false},{"version":"779ade48246316dbb19834f2a44e7e7e153cc79bb21a050dbefdf45daa365f0a","signature":false},{"version":"eab22344f171bd9124a15c9f0da1228dfd650194d13d6343875f10041ac48d1a","signature":false},{"version":"f67af2fa8f6f05a7b386d31fb23e35b2f078ea240221619f8c7857e6ed6ff553","signature":false},{"version":"c5fdfc3851bf3408c389a841e672cb66e3e7a53b624de971c2e12fe17b61d11e","signature":false},{"version":"bb762708335e5cc0e4bc71633d5442d05cd621dc7dc30976e3b587ecce625c5b","signature":false}],"root":[60,[110,141]],"options":{"allowImportingTsExtensions":false,"allowSyntheticDefaultImports":true,"composite":true,"esModuleInterop":true,"module":99,"noFallthroughCasesInSwitch":true,"noImplicitAny":true,"noImplicitReturns":true,"noUnusedLocals":true,"noUnusedParameters":true,"skipLibCheck":true,"strict":true,"verbatimModuleSyntax":false},"referencedMap":[[91,1],[92,2],[99,3],[100,4],[70,5],[69,6],[93,5],[77,7],[79,8],[78,9],[86,10],[68,11],[62,12],[64,13],[66,14],[67,12],[106,15],[108,16],[107,17],[105,18],[73,19],[76,20],[74,19],[75,21],[95,22],[84,23],[80,24],[81,7],[102,25],[96,26],[82,27],[101,28],[83,29],[109,30],[103,31],[110,32],[111,33],[112,34],[139,35],[138,36],[135,37],[137,38],[131,39],[132,40],[134,40],[133,39],[136,41],[130,37],[129,37],[123,42],[127,43],[121,44],[126,45],[125,46],[122,47],[141,48],[128,49],[117,50],[116,51],[118,52],[115,47],[119,47],[120,53]],"changeFileSet":[88,91,89,92,99,100,98,61,70,69,93,77,79,78,86,85,68,62,64,66,65,67,63,90,106,108,107,105,104,94,87,58,59,10,11,13,12,2,14,15,16,17,18,19,20,21,3,22,23,4,24,28,25,26,27,29,30,31,5,32,33,34,35,6,39,36,37,38,40,7,41,46,47,42,43,44,45,8,51,48,49,50,52,9,53,54,55,57,56,1,73,76,74,72,75,95,84,80,81,102,96,82,101,71,83,109,103,97,60,110,111,112,139,140,138,135,137,131,132,134,133,136,130,129,123,127,121,126,125,122,114,113,141,128,117,116,118,115,119,120,124],"version":"6.0.2"} \ No newline at end of file