mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-09 15:59:30 +00:00
refactor(providers): share Qwen chat-template thinking patch
This commit is contained in:
parent
6f17c4cc6d
commit
2ba9d6eabe
7 changed files with 63 additions and 43 deletions
|
|
@ -515,7 +515,7 @@ API key auth, and dynamic model resolution.
|
|||
|
||||
- `openclaw/plugin-sdk/provider-model-shared` - `ProviderReplayFamily`, `buildProviderReplayFamilyHooks(...)`, and the raw replay builders (`buildOpenAICompatibleReplayPolicy`, `buildAnthropicReplayPolicyForModel`, `buildGoogleGeminiReplayPolicy`, `buildHybridAnthropicOrOpenAIReplayPolicy`). Also exports Gemini replay helpers (`sanitizeGoogleGeminiReplayHistory`, `resolveTaggedReasoningOutputMode`) and endpoint/model helpers (`resolveProviderEndpoint`, `normalizeProviderId`, `normalizeGooglePreviewModelId`).
|
||||
- `openclaw/plugin-sdk/provider-stream` - `ProviderStreamFamily`, `buildProviderStreamFamilyHooks(...)`, `composeProviderStreamWrappers(...)`, plus the shared OpenAI/Codex wrappers (`createOpenAIAttributionHeadersWrapper`, `createOpenAIFastModeWrapper`, `createOpenAIServiceTierWrapper`, `createOpenAIResponsesContextManagementWrapper`, `createCodexNativeWebSearchWrapper`), DeepSeek V4 OpenAI-compatible wrapper (`createDeepSeekV4OpenAICompatibleThinkingWrapper`), Anthropic Messages thinking prefill cleanup (`createAnthropicThinkingPrefillPayloadWrapper`), plain-text tool-call compat (`createPlainTextToolCallCompatWrapper`), and shared proxy/provider wrappers (`createOpenRouterWrapper`, `createToolStreamWrapper`, `createMinimaxFastModeWrapper`).
|
||||
- `openclaw/plugin-sdk/provider-stream-shared` - lightweight payload and event wrappers for hot provider paths, including `createOpenAICompatibleCompletionsThinkingOffWrapper`, `createPayloadPatchStreamWrapper`, and `createPlainTextToolCallCompatWrapper`.
|
||||
- `openclaw/plugin-sdk/provider-stream-shared` - lightweight payload and event wrappers for hot provider paths, including `createOpenAICompatibleCompletionsThinkingOffWrapper`, `createPayloadPatchStreamWrapper`, `createPlainTextToolCallCompatWrapper`, and `setQwenChatTemplateThinking(...)`.
|
||||
- `openclaw/plugin-sdk/provider-tools` - `ProviderToolCompatFamily`, `buildProviderToolCompatFamilyHooks("deepseek" | "gemini" | "openai")`, and underlying provider schema helpers.
|
||||
|
||||
For Gemini-family providers, keep the reasoning-output mode aligned with
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ and pairing-path families.
|
|||
| `plugin-sdk/provider-tools` | `ProviderToolCompatFamily`, `buildProviderToolCompatFamilyHooks`, and DeepSeek/Gemini/OpenAI schema cleanup + diagnostics |
|
||||
| `plugin-sdk/provider-usage` | Provider usage snapshot types, shared usage fetch helpers, and provider fetchers such as `fetchClaudeUsage` |
|
||||
| `plugin-sdk/provider-stream` | `ProviderStreamFamily`, `buildProviderStreamFamilyHooks`, `composeProviderStreamWrappers`, stream wrapper types, plain-text tool-call compat, and shared Anthropic/Bedrock/DeepSeek V4/Google/Kilocode/Moonshot/OpenAI/OpenRouter/Z.A.I/MiniMax/Copilot wrapper helpers |
|
||||
| `plugin-sdk/provider-stream-shared` | Public shared provider stream wrapper helpers including `composeProviderStreamWrappers`, `createOpenAICompatibleCompletionsThinkingOffWrapper`, `createPlainTextToolCallCompatWrapper`, `createPayloadPatchStreamWrapper`, `createToolStreamWrapper`, and Anthropic/DeepSeek/OpenAI-compatible stream utilities |
|
||||
| `plugin-sdk/provider-stream-shared` | Public shared provider stream wrapper helpers including `composeProviderStreamWrappers`, `createOpenAICompatibleCompletionsThinkingOffWrapper`, `createPlainTextToolCallCompatWrapper`, `createPayloadPatchStreamWrapper`, `createToolStreamWrapper`, `setQwenChatTemplateThinking`, and Anthropic/DeepSeek/OpenAI-compatible stream utilities |
|
||||
| `plugin-sdk/provider-transport-runtime` | Native provider transport helpers such as guarded fetch, transport message transforms, and writable transport event streams |
|
||||
| `plugin-sdk/provider-onboard` | Onboarding config patch helpers |
|
||||
| `plugin-sdk/global-singleton` | Process-local singleton/map/cache helpers |
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { normalizeProviderId } from "openclaw/plugin-sdk/provider-model-shared";
|
|||
import {
|
||||
createPayloadPatchStreamWrapper,
|
||||
isOpenAICompatibleThinkingEnabled,
|
||||
setQwenChatTemplateThinking,
|
||||
} from "openclaw/plugin-sdk/provider-stream-shared";
|
||||
|
||||
type QwenThinkingLevel = ProviderWrapStreamFnContext["thinkingLevel"];
|
||||
|
|
@ -68,25 +69,6 @@ function patchQwenOAuthPayload(payload: Record<string, unknown>): void {
|
|||
payload.vl_high_resolution_images = true;
|
||||
}
|
||||
|
||||
function setQwenChatTemplateThinking(payload: Record<string, unknown>, enabled: boolean): void {
|
||||
const existing = payload.chat_template_kwargs;
|
||||
if (existing && typeof existing === "object" && !Array.isArray(existing)) {
|
||||
const next: Record<string, unknown> = {
|
||||
...(existing as Record<string, unknown>),
|
||||
enable_thinking: enabled,
|
||||
};
|
||||
if (!Object.hasOwn(next, "preserve_thinking")) {
|
||||
next.preserve_thinking = true;
|
||||
}
|
||||
payload.chat_template_kwargs = next;
|
||||
return;
|
||||
}
|
||||
payload.chat_template_kwargs = {
|
||||
enable_thinking: enabled,
|
||||
preserve_thinking: true,
|
||||
};
|
||||
}
|
||||
|
||||
function readQwenThinkingFormatFromModel(model: Parameters<StreamFn>[0]): QwenThinkingFormat {
|
||||
if (model.api !== "openai-completions") {
|
||||
return undefined;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { normalizeProviderId } from "openclaw/plugin-sdk/provider-model-shared";
|
|||
import {
|
||||
createPayloadPatchStreamWrapper,
|
||||
isOpenAICompatibleThinkingEnabled,
|
||||
setQwenChatTemplateThinking,
|
||||
} from "openclaw/plugin-sdk/provider-stream-shared";
|
||||
import {
|
||||
resolveVllmQwenThinkingFormatFromCompat,
|
||||
|
|
@ -23,25 +24,6 @@ function resolveVllmQwenThinkingFormat(
|
|||
return resolveVllmQwenThinkingFormatFromCompat(ctx.model?.compat);
|
||||
}
|
||||
|
||||
function setQwenChatTemplateThinking(payload: Record<string, unknown>, enabled: boolean): void {
|
||||
const existing = payload.chat_template_kwargs;
|
||||
if (existing && typeof existing === "object" && !Array.isArray(existing)) {
|
||||
const next: Record<string, unknown> = {
|
||||
...(existing as Record<string, unknown>),
|
||||
enable_thinking: enabled,
|
||||
};
|
||||
if (!Object.hasOwn(next, "preserve_thinking")) {
|
||||
next.preserve_thinking = true;
|
||||
}
|
||||
payload.chat_template_kwargs = next;
|
||||
return;
|
||||
}
|
||||
payload.chat_template_kwargs = {
|
||||
enable_thinking: enabled,
|
||||
preserve_thinking: true,
|
||||
};
|
||||
}
|
||||
|
||||
function isVllmNemotronModel(model: { api?: unknown; provider?: unknown; id?: unknown }): boolean {
|
||||
return (
|
||||
model.api === "openai-completions" &&
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ const defaultPublicDeprecatedExportsByEntrypointBudget = Object.freeze({
|
|||
"provider-auth-login": 3,
|
||||
"provider-model-shared": 29,
|
||||
"provider-stream-family": 40,
|
||||
"provider-stream-shared": 28,
|
||||
"provider-stream-shared": 29,
|
||||
"provider-stream": 40,
|
||||
"provider-web-search": 1,
|
||||
"provider-zai-endpoint": 3,
|
||||
|
|
@ -163,8 +163,8 @@ let publicDeprecatedExportsByEntrypointBudget;
|
|||
try {
|
||||
budgets = {
|
||||
publicEntrypoints: readBudgetEnv("OPENCLAW_PLUGIN_SDK_MAX_PUBLIC_ENTRYPOINTS", 321),
|
||||
publicExports: readBudgetEnv("OPENCLAW_PLUGIN_SDK_MAX_PUBLIC_EXPORTS", 10327),
|
||||
publicFunctionExports: readBudgetEnv("OPENCLAW_PLUGIN_SDK_MAX_PUBLIC_FUNCTION_EXPORTS", 5184),
|
||||
publicExports: readBudgetEnv("OPENCLAW_PLUGIN_SDK_MAX_PUBLIC_EXPORTS", 10328),
|
||||
publicFunctionExports: readBudgetEnv("OPENCLAW_PLUGIN_SDK_MAX_PUBLIC_FUNCTION_EXPORTS", 5185),
|
||||
publicDeprecatedExports: readBudgetEnv(
|
||||
"OPENCLAW_PLUGIN_SDK_MAX_PUBLIC_DEPRECATED_EXPORTS",
|
||||
3247,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {
|
|||
createPlainTextToolCallCompatWrapper,
|
||||
defaultToolStreamExtraParams,
|
||||
isOpenAICompatibleThinkingEnabled,
|
||||
setQwenChatTemplateThinking,
|
||||
stripTrailingAnthropicAssistantPrefillWhenThinking,
|
||||
} from "./provider-stream-shared.js";
|
||||
|
||||
|
|
@ -160,6 +161,38 @@ describe("isOpenAICompatibleThinkingEnabled", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("setQwenChatTemplateThinking", () => {
|
||||
it("preserves existing chat-template kwargs and enables thinking", () => {
|
||||
const payload = {
|
||||
chat_template_kwargs: {
|
||||
custom_flag: "keep",
|
||||
preserve_thinking: false,
|
||||
},
|
||||
};
|
||||
|
||||
setQwenChatTemplateThinking(payload, true);
|
||||
|
||||
expect(payload.chat_template_kwargs).toEqual({
|
||||
custom_flag: "keep",
|
||||
preserve_thinking: false,
|
||||
enable_thinking: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("creates the required chat-template kwargs when absent", () => {
|
||||
const payload: Record<string, unknown> = {};
|
||||
|
||||
setQwenChatTemplateThinking(payload, false);
|
||||
|
||||
expect(payload).toEqual({
|
||||
chat_template_kwargs: {
|
||||
enable_thinking: false,
|
||||
preserve_thinking: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("createDeepSeekV4OpenAICompatibleThinkingWrapper", () => {
|
||||
it("backfills reasoning_content on every replayed assistant message when thinking is enabled", () => {
|
||||
const payload = {
|
||||
|
|
|
|||
|
|
@ -411,6 +411,29 @@ export function isOpenAICompatibleThinkingEnabled(params: {
|
|||
return normalized !== "off" && normalized !== "none";
|
||||
}
|
||||
|
||||
/** Applies Qwen chat-template thinking flags without discarding provider-specific kwargs. */
|
||||
export function setQwenChatTemplateThinking(
|
||||
payload: Record<string, unknown>,
|
||||
enabled: boolean,
|
||||
): void {
|
||||
const existing = payload.chat_template_kwargs;
|
||||
if (existing && typeof existing === "object" && !Array.isArray(existing)) {
|
||||
const next: Record<string, unknown> = {
|
||||
...(existing as Record<string, unknown>),
|
||||
enable_thinking: enabled,
|
||||
};
|
||||
if (!Object.hasOwn(next, "preserve_thinking")) {
|
||||
next.preserve_thinking = true;
|
||||
}
|
||||
payload.chat_template_kwargs = next;
|
||||
return;
|
||||
}
|
||||
payload.chat_template_kwargs = {
|
||||
enable_thinking: enabled,
|
||||
preserve_thinking: true,
|
||||
};
|
||||
}
|
||||
|
||||
/** @deprecated DeepSeek provider stream helper; do not use from third-party plugins. */
|
||||
export type DeepSeekV4ThinkingLevel = ProviderWrapStreamFnContext["thinkingLevel"];
|
||||
/** @deprecated DeepSeek provider stream helper; do not use from third-party plugins. */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue