From eea7ba53455a85cf29a71bd2142c265ba228cfd6 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Tue, 14 Apr 2026 17:13:16 +0100 Subject: [PATCH] fix(plugin-sdk): share canonical stream hook families --- .../.generated/plugin-sdk-api-baseline.sha256 | 4 +-- extensions/google/provider-hooks.ts | 4 +-- extensions/kilocode/index.ts | 3 +- extensions/minimax/provider-registration.ts | 3 +- extensions/moonshot/index.ts | 3 +- extensions/openrouter/stream.ts | 4 +-- extensions/zai/index.ts | 5 ++- src/plugin-sdk/provider-stream.test.ts | 31 ++++++++++++++----- src/plugin-sdk/provider-stream.ts | 12 +++++++ 9 files changed, 46 insertions(+), 23 deletions(-) diff --git a/docs/.generated/plugin-sdk-api-baseline.sha256 b/docs/.generated/plugin-sdk-api-baseline.sha256 index a6afbcdb88f..371c47785d6 100644 --- a/docs/.generated/plugin-sdk-api-baseline.sha256 +++ b/docs/.generated/plugin-sdk-api-baseline.sha256 @@ -1,2 +1,2 @@ -57ae55ced0f486941716dcfc569af480e964421ae50f0304665bd6ce2fa77521 plugin-sdk-api-baseline.json -6b3e30a125af7d4ae061c288c8f7bc268b676c1dcd602dec56fbd66bfef2ca72 plugin-sdk-api-baseline.jsonl +a1e765cf426077085975f1f00847026b71f301cad35cb9168713e2b6249c4a47 plugin-sdk-api-baseline.json +9f1cdbe8d9bfbd582edb671729c4c09e578fb1940e787cfd6aa82dee0bdf5de7 plugin-sdk-api-baseline.jsonl diff --git a/extensions/google/provider-hooks.ts b/extensions/google/provider-hooks.ts index a72f2bcd762..6a2544fd27d 100644 --- a/extensions/google/provider-hooks.ts +++ b/extensions/google/provider-hooks.ts @@ -1,9 +1,9 @@ import { buildProviderReplayFamilyHooks } from "openclaw/plugin-sdk/provider-model-shared"; -import { buildProviderStreamFamilyHooks } from "openclaw/plugin-sdk/provider-stream-family"; +import { GOOGLE_THINKING_STREAM_HOOKS } from "openclaw/plugin-sdk/provider-stream-family"; export const GOOGLE_GEMINI_PROVIDER_HOOKS = { ...buildProviderReplayFamilyHooks({ family: "google-gemini", }), - ...buildProviderStreamFamilyHooks("google-thinking"), + ...GOOGLE_THINKING_STREAM_HOOKS, }; diff --git a/extensions/kilocode/index.ts b/extensions/kilocode/index.ts index 074cdcb498a..5d801008526 100644 --- a/extensions/kilocode/index.ts +++ b/extensions/kilocode/index.ts @@ -1,12 +1,11 @@ import { readConfiguredProviderCatalogEntries } from "openclaw/plugin-sdk/provider-catalog-shared"; import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry"; import { PASSTHROUGH_GEMINI_REPLAY_HOOKS } from "openclaw/plugin-sdk/provider-model-shared"; -import { buildProviderStreamFamilyHooks } from "openclaw/plugin-sdk/provider-stream-family"; +import { KILOCODE_THINKING_STREAM_HOOKS } from "openclaw/plugin-sdk/provider-stream-family"; import { applyKilocodeConfig, KILOCODE_DEFAULT_MODEL_REF } from "./onboard.js"; import { buildKilocodeProviderWithDiscovery } from "./provider-catalog.js"; const PROVIDER_ID = "kilocode"; -const KILOCODE_THINKING_STREAM_HOOKS = buildProviderStreamFamilyHooks("kilocode-thinking"); export default defineSingleProviderPluginEntry({ id: PROVIDER_ID, diff --git a/extensions/minimax/provider-registration.ts b/extensions/minimax/provider-registration.ts index a406afc43b7..812e102d30f 100644 --- a/extensions/minimax/provider-registration.ts +++ b/extensions/minimax/provider-registration.ts @@ -13,7 +13,7 @@ import { import { buildOauthProviderAuthResult } from "openclaw/plugin-sdk/provider-auth"; import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key"; import { buildProviderReplayFamilyHooks } from "openclaw/plugin-sdk/provider-model-shared"; -import { buildProviderStreamFamilyHooks } from "openclaw/plugin-sdk/provider-stream-family"; +import { MINIMAX_FAST_MODE_STREAM_HOOKS } from "openclaw/plugin-sdk/provider-stream-family"; import { fetchMinimaxUsage } from "openclaw/plugin-sdk/provider-usage"; import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { isMiniMaxModernModelId, MINIMAX_DEFAULT_MODEL_ID } from "./api.js"; @@ -42,7 +42,6 @@ const HYBRID_ANTHROPIC_OPENAI_REPLAY_HOOKS = buildProviderReplayFamilyHooks({ family: "hybrid-anthropic-openai", anthropicModelDropThinkingBlocks: true, }); -const MINIMAX_FAST_MODE_STREAM_HOOKS = buildProviderStreamFamilyHooks("minimax-fast-mode"); const MINIMAX_PROVIDER_HOOKS = { ...HYBRID_ANTHROPIC_OPENAI_REPLAY_HOOKS, ...MINIMAX_FAST_MODE_STREAM_HOOKS, diff --git a/extensions/moonshot/index.ts b/extensions/moonshot/index.ts index 1734ac63872..022f79c30b3 100644 --- a/extensions/moonshot/index.ts +++ b/extensions/moonshot/index.ts @@ -1,6 +1,6 @@ import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry"; import { OPENAI_COMPATIBLE_REPLAY_HOOKS } from "openclaw/plugin-sdk/provider-model-shared"; -import { buildProviderStreamFamilyHooks } from "openclaw/plugin-sdk/provider-stream-family"; +import { MOONSHOT_THINKING_STREAM_HOOKS } from "openclaw/plugin-sdk/provider-stream-family"; import { applyMoonshotNativeStreamingUsageCompat } from "./api.js"; import { moonshotMediaUnderstandingProvider } from "./media-understanding-provider.js"; import { @@ -12,7 +12,6 @@ import { buildMoonshotProvider } from "./provider-catalog.js"; import { createKimiWebSearchProvider } from "./src/kimi-web-search-provider.js"; const PROVIDER_ID = "moonshot"; -const MOONSHOT_THINKING_STREAM_HOOKS = buildProviderStreamFamilyHooks("moonshot-thinking"); export default defineSingleProviderPluginEntry({ id: PROVIDER_ID, diff --git a/extensions/openrouter/stream.ts b/extensions/openrouter/stream.ts index d0f196d5487..88303c238c3 100644 --- a/extensions/openrouter/stream.ts +++ b/extensions/openrouter/stream.ts @@ -1,8 +1,6 @@ import type { StreamFn } from "@mariozechner/pi-agent-core"; import type { ProviderWrapStreamFnContext } from "openclaw/plugin-sdk/plugin-entry"; -import { buildProviderStreamFamilyHooks } from "openclaw/plugin-sdk/provider-stream-family"; - -const OPENROUTER_THINKING_STREAM_HOOKS = buildProviderStreamFamilyHooks("openrouter-thinking"); +import { OPENROUTER_THINKING_STREAM_HOOKS } from "openclaw/plugin-sdk/provider-stream-family"; function injectOpenRouterRouting( baseStreamFn: StreamFn | undefined, diff --git a/extensions/zai/index.ts b/extensions/zai/index.ts index faf0c5fe4c0..7245817776c 100644 --- a/extensions/zai/index.ts +++ b/extensions/zai/index.ts @@ -20,7 +20,7 @@ import { normalizeModelCompat, OPENAI_COMPATIBLE_REPLAY_HOOKS, } from "openclaw/plugin-sdk/provider-model-shared"; -import { buildProviderStreamFamilyHooks } from "openclaw/plugin-sdk/provider-stream-family"; +import { TOOL_STREAM_DEFAULT_ON_HOOKS } from "openclaw/plugin-sdk/provider-stream-family"; import { defaultToolStreamExtraParams } from "openclaw/plugin-sdk/provider-stream-shared"; import { fetchZaiUsage, resolveLegacyPiAgentAccessToken } from "openclaw/plugin-sdk/provider-usage"; import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime"; @@ -32,7 +32,6 @@ import { applyZaiConfig, applyZaiProviderConfig, ZAI_DEFAULT_MODEL_REF } from ". const PROVIDER_ID = "zai"; const GLM5_TEMPLATE_MODEL_ID = "glm-4.7"; const PROFILE_ID = "zai:default"; -const ZAI_TOOL_STREAM_HOOKS = buildProviderStreamFamilyHooks("tool-stream-default-on"); function resolveGlm5ForwardCompatModel( ctx: ProviderResolveDynamicModelContext, @@ -280,7 +279,7 @@ export default definePluginEntry({ resolveDynamicModel: (ctx) => resolveGlm5ForwardCompatModel(ctx), ...OPENAI_COMPATIBLE_REPLAY_HOOKS, prepareExtraParams: (ctx) => defaultToolStreamExtraParams(ctx.extraParams), - ...ZAI_TOOL_STREAM_HOOKS, + ...TOOL_STREAM_DEFAULT_ON_HOOKS, isBinaryThinking: () => true, isModernModelRef: ({ modelId }) => { const lower = normalizeLowercaseStringOrEmpty(modelId); diff --git a/src/plugin-sdk/provider-stream.test.ts b/src/plugin-sdk/provider-stream.test.ts index 60c738fc7c6..0f064990198 100644 --- a/src/plugin-sdk/provider-stream.test.ts +++ b/src/plugin-sdk/provider-stream.test.ts @@ -10,6 +10,13 @@ import { composeProviderStreamWrappers, createMoonshotThinkingWrapper, createToolStreamWrapper, + GOOGLE_THINKING_STREAM_HOOKS, + KILOCODE_THINKING_STREAM_HOOKS, + MINIMAX_FAST_MODE_STREAM_HOOKS, + MOONSHOT_THINKING_STREAM_HOOKS, + OPENAI_RESPONSES_STREAM_HOOKS, + OPENROUTER_THINKING_STREAM_HOOKS, + TOOL_STREAM_DEFAULT_ON_HOOKS, } from "./provider-stream.js"; function requireWrapStreamFn( @@ -89,7 +96,7 @@ describe("buildProviderStreamFamilyHooks", () => { return {} as never; }; - const googleHooks = buildProviderStreamFamilyHooks("google-thinking"); + const googleHooks = GOOGLE_THINKING_STREAM_HOOKS; const googleStream = requireStreamFn( requireWrapStreamFn(googleHooks.wrapStreamFn)({ streamFn: baseStreamFn, @@ -109,7 +116,7 @@ describe("buildProviderStreamFamilyHooks", () => { ).thinkingConfig as Record; expect(googleThinkingConfig).not.toHaveProperty("thinkingBudget"); - const minimaxHooks = buildProviderStreamFamilyHooks("minimax-fast-mode"); + const minimaxHooks = MINIMAX_FAST_MODE_STREAM_HOOKS; const minimaxStream = requireStreamFn( requireWrapStreamFn(minimaxHooks.wrapStreamFn)({ streamFn: baseStreamFn, @@ -127,7 +134,7 @@ describe("buildProviderStreamFamilyHooks", () => { ); expect(capturedModelId).toBe("MiniMax-M2.7-highspeed"); - const kilocodeHooks = buildProviderStreamFamilyHooks("kilocode-thinking"); + const kilocodeHooks = KILOCODE_THINKING_STREAM_HOOKS; void requireStreamFn( requireWrapStreamFn(kilocodeHooks.wrapStreamFn)({ streamFn: baseStreamFn, @@ -152,7 +159,7 @@ describe("buildProviderStreamFamilyHooks", () => { }); expect(capturedPayload).not.toHaveProperty("reasoning"); - const moonshotHooks = buildProviderStreamFamilyHooks("moonshot-thinking"); + const moonshotHooks = MOONSHOT_THINKING_STREAM_HOOKS; const moonshotStream = requireStreamFn( requireWrapStreamFn(moonshotHooks.wrapStreamFn)({ streamFn: baseStreamFn, @@ -165,7 +172,7 @@ describe("buildProviderStreamFamilyHooks", () => { thinking: { type: "disabled" }, }); - const openAiHooks = buildProviderStreamFamilyHooks("openai-responses-defaults"); + const openAiHooks = OPENAI_RESPONSES_STREAM_HOOKS; void requireStreamFn( requireWrapStreamFn(openAiHooks.wrapStreamFn)({ streamFn: baseStreamFn, @@ -189,7 +196,7 @@ describe("buildProviderStreamFamilyHooks", () => { }); expect(capturedHeaders).toBeDefined(); - const openRouterHooks = buildProviderStreamFamilyHooks("openrouter-thinking"); + const openRouterHooks = OPENROUTER_THINKING_STREAM_HOOKS; void requireStreamFn( requireWrapStreamFn(openRouterHooks.wrapStreamFn)({ streamFn: baseStreamFn, @@ -214,7 +221,7 @@ describe("buildProviderStreamFamilyHooks", () => { }); expect(capturedPayload).not.toHaveProperty("reasoning"); - const toolStreamHooks = buildProviderStreamFamilyHooks("tool-stream-default-on"); + const toolStreamHooks = TOOL_STREAM_DEFAULT_ON_HOOKS; const toolStreamDefault = requireStreamFn( requireWrapStreamFn(toolStreamHooks.wrapStreamFn)({ streamFn: baseStreamFn, @@ -239,4 +246,14 @@ describe("buildProviderStreamFamilyHooks", () => { }); expect(capturedPayload).not.toHaveProperty("tool_stream"); }); + + it("exposes canonical stream hook constants for reused families", () => { + expect(GOOGLE_THINKING_STREAM_HOOKS.wrapStreamFn).toBeTypeOf("function"); + expect(KILOCODE_THINKING_STREAM_HOOKS.wrapStreamFn).toBeTypeOf("function"); + expect(MINIMAX_FAST_MODE_STREAM_HOOKS.wrapStreamFn).toBeTypeOf("function"); + expect(MOONSHOT_THINKING_STREAM_HOOKS.wrapStreamFn).toBeTypeOf("function"); + expect(OPENAI_RESPONSES_STREAM_HOOKS.wrapStreamFn).toBeTypeOf("function"); + expect(OPENROUTER_THINKING_STREAM_HOOKS.wrapStreamFn).toBeTypeOf("function"); + expect(TOOL_STREAM_DEFAULT_ON_HOOKS.wrapStreamFn).toBeTypeOf("function"); + }); }); diff --git a/src/plugin-sdk/provider-stream.ts b/src/plugin-sdk/provider-stream.ts index 212af939efb..92c5798a930 100644 --- a/src/plugin-sdk/provider-stream.ts +++ b/src/plugin-sdk/provider-stream.ts @@ -139,6 +139,18 @@ export function buildProviderStreamFamilyHooks( throw new Error("Unsupported provider stream family"); } +export const GOOGLE_THINKING_STREAM_HOOKS = buildProviderStreamFamilyHooks("google-thinking"); +export const KILOCODE_THINKING_STREAM_HOOKS = buildProviderStreamFamilyHooks("kilocode-thinking"); +export const MOONSHOT_THINKING_STREAM_HOOKS = buildProviderStreamFamilyHooks("moonshot-thinking"); +export const MINIMAX_FAST_MODE_STREAM_HOOKS = buildProviderStreamFamilyHooks("minimax-fast-mode"); +export const OPENAI_RESPONSES_STREAM_HOOKS = buildProviderStreamFamilyHooks( + "openai-responses-defaults", +); +export const OPENROUTER_THINKING_STREAM_HOOKS = + buildProviderStreamFamilyHooks("openrouter-thinking"); +export const TOOL_STREAM_DEFAULT_ON_HOOKS = + buildProviderStreamFamilyHooks("tool-stream-default-on"); + // Public stream-wrapper helpers for provider plugins. export {