diff --git a/extensions/deepinfra/index.ts b/extensions/deepinfra/index.ts index e0f19efe415..b4b7c5f4d86 100644 --- a/extensions/deepinfra/index.ts +++ b/extensions/deepinfra/index.ts @@ -1,7 +1,11 @@ import { definePluginEntry } from "openclaw/plugin-sdk/core"; import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth"; import { buildSingleProviderApiKeyCatalog } from "openclaw/plugin-sdk/provider-catalog"; -import { createDeepInfraSystemCacheWrapper } from "openclaw/plugin-sdk/provider-stream"; +import { + createDeepInfraSystemCacheWrapper, + createDeepInfraWrapper, + isProxyReasoningUnsupported, +} from "openclaw/plugin-sdk/provider-stream"; import { applyDeepInfraConfig, DEEPINFRA_DEFAULT_MODEL_REF } from "./onboard.js"; import { buildDeepInfraProviderWithDiscovery } from "./provider-catalog.js"; @@ -66,7 +70,14 @@ export default definePluginEntry({ geminiThoughtSignatureModelHints: ["gemini"], dropThinkingBlockModelHints: ["claude"], }, - wrapStreamFn: (ctx) => createDeepInfraSystemCacheWrapper(ctx.streamFn), + wrapStreamFn: (ctx) => { + const thinkingLevel = isProxyReasoningUnsupported(ctx.modelId) + ? undefined + : ctx.thinkingLevel; + let streamFn = createDeepInfraWrapper(ctx.streamFn, thinkingLevel); + streamFn = createDeepInfraSystemCacheWrapper(streamFn); + return streamFn; + }, isCacheTtlEligible: (ctx) => isDeepInfraCacheTtlModel(ctx.modelId), }); }, diff --git a/src/agents/pi-embedded-runner/proxy-stream-wrappers.test.ts b/src/agents/pi-embedded-runner/proxy-stream-wrappers.test.ts index 487d90582ef..54a39a53aee 100644 --- a/src/agents/pi-embedded-runner/proxy-stream-wrappers.test.ts +++ b/src/agents/pi-embedded-runner/proxy-stream-wrappers.test.ts @@ -2,7 +2,7 @@ import type { StreamFn } from "@mariozechner/pi-agent-core"; import type { Context, Model } from "@mariozechner/pi-ai"; import { createAssistantMessageEventStream } from "@mariozechner/pi-ai"; import { describe, expect, it } from "vitest"; -import { createOpenRouterWrapper } from "./proxy-stream-wrappers.js"; +import { createDeepInfraWrapper, createOpenRouterWrapper } from "./proxy-stream-wrappers.js"; describe("proxy stream wrappers", () => { it("adds OpenRouter attribution headers to stream options", () => { @@ -35,4 +35,67 @@ describe("proxy stream wrappers", () => { }, ]); }); + + describe("createDeepInfraWrapper", () => { + function capturePayloads() { + const payloads: unknown[] = []; + const baseStreamFn: StreamFn = (_model, _context, options) => { + const payload = { model: "test" }; + options?.onPayload?.(payload, _model); + payloads.push(structuredClone(payload)); + return createAssistantMessageEventStream(); + }; + return { baseStreamFn, payloads }; + } + + const model = { + api: "openai-completions", + provider: "deepinfra", + id: "moonshotai/Kimi-K2.5", + } as Model<"openai-completions">; + const context: Context = { messages: [] }; + + it("injects reasoning effort when thinkingLevel is set", () => { + const { baseStreamFn, payloads } = capturePayloads(); + const wrapped = createDeepInfraWrapper(baseStreamFn, "high"); + void wrapped(model, context, {}); + + expect(payloads[0]).toEqual({ + model: "test", + reasoning: { effort: "high" }, + }); + }); + + it("maps 'off' to no reasoning field", () => { + const { baseStreamFn, payloads } = capturePayloads(); + const wrapped = createDeepInfraWrapper(baseStreamFn, "off"); + void wrapped(model, context, {}); + + expect(payloads[0]).toEqual({ model: "test" }); + }); + + it("does not inject reasoning when thinkingLevel is undefined", () => { + const { baseStreamFn, payloads } = capturePayloads(); + const wrapped = createDeepInfraWrapper(baseStreamFn, undefined); + void wrapped(model, context, {}); + + expect(payloads[0]).toEqual({ model: "test" }); + }); + + it("preserves existing onPayload callback", () => { + const { baseStreamFn } = capturePayloads(); + const wrapped = createDeepInfraWrapper(baseStreamFn, "low"); + const seen: unknown[] = []; + void wrapped(model, context, { + onPayload: (payload) => { + seen.push(structuredClone(payload)); + }, + }); + + expect(seen[0]).toEqual({ + model: "test", + reasoning: { effort: "low" }, + }); + }); + }); }); diff --git a/src/agents/pi-embedded-runner/proxy-stream-wrappers.ts b/src/agents/pi-embedded-runner/proxy-stream-wrappers.ts index 50568ba6efd..70ea42bec41 100644 --- a/src/agents/pi-embedded-runner/proxy-stream-wrappers.ts +++ b/src/agents/pi-embedded-runner/proxy-stream-wrappers.ts @@ -135,6 +135,23 @@ export function isProxyReasoningUnsupported(modelId: string): boolean { return modelId.toLowerCase().startsWith("x-ai/"); } +export function createDeepInfraWrapper( + baseStreamFn: StreamFn | undefined, + thinkingLevel?: ThinkLevel, +): StreamFn { + const underlying = baseStreamFn ?? streamSimple; + return (model, context, options) => { + const onPayload = options?.onPayload; + return underlying(model, context, { + ...options, + onPayload: (payload) => { + normalizeProxyReasoningPayload(payload, thinkingLevel); + return onPayload?.(payload, model); + }, + }); + }; +} + export function createKilocodeWrapper( baseStreamFn: StreamFn | undefined, thinkingLevel?: ThinkLevel, diff --git a/src/plugin-sdk/provider-stream.ts b/src/plugin-sdk/provider-stream.ts index 68df86e5116..74f9484e8e9 100644 --- a/src/plugin-sdk/provider-stream.ts +++ b/src/plugin-sdk/provider-stream.ts @@ -10,6 +10,7 @@ export { } from "../agents/pi-embedded-runner/google-stream-wrappers.js"; export { createDeepInfraSystemCacheWrapper, + createDeepInfraWrapper, createKilocodeWrapper, createOpenRouterSystemCacheWrapper, createOpenRouterWrapper,