mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 15:50:46 +00:00
fix: map thinkingLevel to reasoning.effort for openai-responses-defaults family
This commit is contained in:
committed by
Peter Steinberger
parent
b9d2e0f86d
commit
c92490881b
@@ -33,6 +33,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Sessions/reset: clear auto-sourced model, provider, and auth-profile overrides on `/new` and `/reset` while preserving explicit user selections, so channel sessions stop staying pinned to runtime fallback choices. (#69419) Thanks @sk7n4k3d.
|
||||
- Sessions/costs: snapshot `estimatedCostUsd` like token counters so repeated persist paths no longer compound the same run cost by up to dozens of times. (#69403) Thanks @MrMiaigi.
|
||||
- OpenAI Codex: route ChatGPT/Codex OAuth Responses requests through the `/backend-api/codex` endpoint so `openai-codex/gpt-5.4` no longer hits the removed `/backend-api/responses` alias. (#69336) Thanks @mzogithub.
|
||||
- OpenAI/Responses: omit disabled reasoning payloads when `/think off` is active, so GPT reasoning models no longer receive unsupported `reasoning.effort: "none"` requests. (#61982) Thanks @a-tokyo.
|
||||
- Gateway/pairing: treat loopback shared-secret node-host, TUI, and gateway clients as local for pairing decisions, so trusted local tools no longer reconnect as remote clients and fail with `pairing required`. (#69431) Thanks @SARAMALI15792.
|
||||
- Active Memory: degrade gracefully when memory recall fails during prompt building, logging a warning and letting the reply continue without memory context instead of failing the whole turn. (#69485) Thanks @Magicray1217.
|
||||
- Ollama: add provider-policy defaults for `baseUrl` and `models` so implicit local discovery can run before config validation rejects a minimal Ollama provider config. (#69370) Thanks @PratikRai0101.
|
||||
|
||||
@@ -6,6 +6,7 @@ import net from "node:net";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { writeBuildStamp } from "./build-stamp.mjs";
|
||||
import { resolveBuildRequirement } from "./run-node.mjs";
|
||||
|
||||
@@ -180,6 +181,23 @@ function snapshotTree(rootName) {
|
||||
return stats;
|
||||
}
|
||||
|
||||
export function isIgnoredDistRuntimeWatchPath(entry) {
|
||||
return (
|
||||
entry === "dist-runtime/extensions/node_modules" ||
|
||||
entry.startsWith("dist-runtime/extensions/node_modules/")
|
||||
);
|
||||
}
|
||||
|
||||
function summarizeDistRuntimeAddedPaths(added) {
|
||||
const addedPaths = added.filter((entry) => entry.startsWith("dist-runtime/"));
|
||||
const ignoredDependencyAddedPaths = addedPaths.filter(isIgnoredDistRuntimeWatchPath);
|
||||
const topologyAddedPaths = addedPaths.filter((entry) => !isIgnoredDistRuntimeWatchPath(entry));
|
||||
return {
|
||||
ignoredDependencyAddedPaths,
|
||||
topologyAddedPaths,
|
||||
};
|
||||
}
|
||||
|
||||
function writeSnapshot(snapshotDir) {
|
||||
ensureDir(snapshotDir);
|
||||
const pathEntries = [...listTreeEntries("dist"), ...listTreeEntries("dist-runtime")];
|
||||
@@ -606,11 +624,15 @@ async function main() {
|
||||
const post = writeSnapshot(postDir);
|
||||
const diff = writeDiffArtifacts(options.outputDir, preDir, postDir);
|
||||
|
||||
const distRuntimeFileGrowth = post.distRuntime.files - pre.distRuntime.files;
|
||||
const distRuntimeByteGrowth = post.distRuntime.apparentBytes - pre.distRuntime.apparentBytes;
|
||||
const distRuntimeAddedPaths = diff.added.filter((entry) =>
|
||||
entry.startsWith("dist-runtime/"),
|
||||
).length;
|
||||
const distRuntimeAddedPathSummary = summarizeDistRuntimeAddedPaths(diff.added);
|
||||
const distRuntimeAddedPaths = distRuntimeAddedPathSummary.topologyAddedPaths.length;
|
||||
const distRuntimeIgnoredDependencyAddedPaths =
|
||||
distRuntimeAddedPathSummary.ignoredDependencyAddedPaths.length;
|
||||
const distRuntimeFileGrowth = distRuntimeAddedPaths;
|
||||
const distRuntimeByteGrowth =
|
||||
distRuntimeAddedPaths === 0
|
||||
? 0
|
||||
: post.distRuntime.apparentBytes - pre.distRuntime.apparentBytes;
|
||||
const totalCpuMs = Math.round(
|
||||
(watchResult.timing.userSeconds + watchResult.timing.sysSeconds) * 1000,
|
||||
);
|
||||
@@ -639,6 +661,7 @@ async function main() {
|
||||
distRuntimeByteGrowth,
|
||||
distRuntimeByteGrowthMax: options.distRuntimeByteGrowthMax,
|
||||
distRuntimeAddedPaths,
|
||||
distRuntimeIgnoredDependencyAddedPaths,
|
||||
addedPaths: diff.added.length,
|
||||
removedPaths: diff.removed.length,
|
||||
watchExit: watchResult.exit,
|
||||
@@ -699,4 +722,6 @@ async function main() {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
await main();
|
||||
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
|
||||
await main();
|
||||
}
|
||||
|
||||
@@ -297,6 +297,7 @@ import {
|
||||
createOpenAIServiceTierWrapper,
|
||||
createOpenAIStringContentWrapper,
|
||||
createOpenAITextVerbosityWrapper,
|
||||
createOpenAIThinkingLevelWrapper,
|
||||
resolveOpenAIFastMode,
|
||||
resolveOpenAIServiceTier,
|
||||
resolveOpenAITextVerbosity,
|
||||
@@ -434,7 +435,9 @@ function createTestOpenAIProviderWrapper(
|
||||
});
|
||||
streamFn = createOpenAIStringContentWrapper(streamFn);
|
||||
return createOpenAIResponsesContextManagementWrapper(
|
||||
createOpenAIReasoningCompatibilityWrapper(streamFn),
|
||||
createOpenAIReasoningCompatibilityWrapper(
|
||||
createOpenAIThinkingLevelWrapper(streamFn, params.context.thinkingLevel),
|
||||
),
|
||||
params.context.extraParams,
|
||||
);
|
||||
}
|
||||
|
||||
195
src/agents/pi-embedded-runner/openai-stream-wrappers.test.ts
Normal file
195
src/agents/pi-embedded-runner/openai-stream-wrappers.test.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
import type { StreamFn } from "@mariozechner/pi-agent-core";
|
||||
import type { Model } from "@mariozechner/pi-ai";
|
||||
import { createAssistantMessageEventStream } from "@mariozechner/pi-ai";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createOpenAIThinkingLevelWrapper } from "./openai-stream-wrappers.js";
|
||||
|
||||
function createPayloadCapture(opts?: { initialReasoning?: unknown }) {
|
||||
const payloads: Array<Record<string, unknown>> = [];
|
||||
const baseStreamFn: StreamFn = (model, _context, options) => {
|
||||
const payload: Record<string, unknown> = { model: model.id };
|
||||
if (opts?.initialReasoning !== undefined) {
|
||||
payload.reasoning = structuredClone(opts.initialReasoning);
|
||||
}
|
||||
options?.onPayload?.(payload, model);
|
||||
payloads.push(structuredClone(payload));
|
||||
return createAssistantMessageEventStream();
|
||||
};
|
||||
return { baseStreamFn, payloads };
|
||||
}
|
||||
|
||||
const codexModel = {
|
||||
api: "openai-codex-responses",
|
||||
provider: "openai-codex",
|
||||
id: "gpt-5.1-codex",
|
||||
} as Model<"openai-codex-responses">;
|
||||
|
||||
const openaiModel = {
|
||||
api: "openai-responses",
|
||||
provider: "openai",
|
||||
id: "gpt-5.2",
|
||||
} as Model<"openai-responses">;
|
||||
|
||||
describe("createOpenAIThinkingLevelWrapper", () => {
|
||||
it("overrides effort on reasoning-capable model when thinkingLevel is medium", () => {
|
||||
const { baseStreamFn, payloads } = createPayloadCapture({
|
||||
initialReasoning: { effort: "none" },
|
||||
});
|
||||
const wrapped = createOpenAIThinkingLevelWrapper(baseStreamFn, "medium");
|
||||
void wrapped(codexModel, { messages: [] }, {});
|
||||
|
||||
expect(payloads[0]?.reasoning).toEqual({ effort: "medium" });
|
||||
});
|
||||
|
||||
it("overrides effort on reasoning-capable model when thinkingLevel is high", () => {
|
||||
const { baseStreamFn, payloads } = createPayloadCapture({
|
||||
initialReasoning: { effort: "none" },
|
||||
});
|
||||
const wrapped = createOpenAIThinkingLevelWrapper(baseStreamFn, "high");
|
||||
void wrapped(openaiModel, { messages: [] }, {});
|
||||
|
||||
expect(payloads[0]?.reasoning).toEqual({ effort: "high" });
|
||||
});
|
||||
|
||||
it("removes reasoning when thinkingLevel is off on reasoning-capable model", () => {
|
||||
const { baseStreamFn, payloads } = createPayloadCapture({
|
||||
initialReasoning: { effort: "medium" },
|
||||
});
|
||||
const wrapped = createOpenAIThinkingLevelWrapper(baseStreamFn, "off");
|
||||
void wrapped(codexModel, { messages: [] }, {});
|
||||
|
||||
expect(payloads[0]).not.toHaveProperty("reasoning");
|
||||
});
|
||||
|
||||
it("maps adaptive thinkingLevel to medium effort on reasoning-capable model", () => {
|
||||
const { baseStreamFn, payloads } = createPayloadCapture({
|
||||
initialReasoning: { effort: "none" },
|
||||
});
|
||||
const wrapped = createOpenAIThinkingLevelWrapper(baseStreamFn, "adaptive");
|
||||
void wrapped(codexModel, { messages: [] }, {});
|
||||
|
||||
expect(payloads[0]?.reasoning).toEqual({ effort: "medium" });
|
||||
});
|
||||
|
||||
it("replaces string disabled reasoning when thinkingLevel is enabled", () => {
|
||||
const { baseStreamFn, payloads } = createPayloadCapture({ initialReasoning: "none" });
|
||||
const wrapped = createOpenAIThinkingLevelWrapper(baseStreamFn, "low");
|
||||
void wrapped(codexModel, { messages: [] }, {});
|
||||
|
||||
expect(payloads[0]?.reasoning).toEqual({ effort: "low" });
|
||||
});
|
||||
|
||||
it("does not add reasoning for non-reasoning models without existing reasoning payload", () => {
|
||||
const { baseStreamFn, payloads } = createPayloadCapture();
|
||||
const wrapped = createOpenAIThinkingLevelWrapper(baseStreamFn, "medium");
|
||||
void wrapped(openaiModel, { messages: [] }, {});
|
||||
|
||||
expect(payloads[0]?.reasoning).toBeUndefined();
|
||||
});
|
||||
|
||||
it("overrides existing reasoning.effort from upstream wrappers", () => {
|
||||
const baseStreamFn: StreamFn = (model, _context, options) => {
|
||||
const payload: Record<string, unknown> = {
|
||||
model: model.id,
|
||||
reasoning: { effort: "none" },
|
||||
};
|
||||
options?.onPayload?.(payload, model);
|
||||
return createAssistantMessageEventStream();
|
||||
};
|
||||
|
||||
const payloads: Array<Record<string, unknown>> = [];
|
||||
const capture: StreamFn = (model, context, options) => {
|
||||
return baseStreamFn(model, context, {
|
||||
...options,
|
||||
onPayload: (payload, m) => {
|
||||
options?.onPayload?.(payload, m);
|
||||
payloads.push(structuredClone(payload as Record<string, unknown>));
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const wrapped = createOpenAIThinkingLevelWrapper(capture, "medium");
|
||||
void wrapped(codexModel, { messages: [] }, {});
|
||||
|
||||
expect(payloads[0]?.reasoning).toEqual({ effort: "medium" });
|
||||
});
|
||||
|
||||
it("returns underlying streamFn unchanged when thinkingLevel is undefined", () => {
|
||||
const { baseStreamFn } = createPayloadCapture();
|
||||
const wrapped = createOpenAIThinkingLevelWrapper(baseStreamFn, undefined);
|
||||
expect(wrapped).toBe(baseStreamFn);
|
||||
});
|
||||
|
||||
it("preserves other reasoning properties when overriding effort", () => {
|
||||
const baseStreamFn: StreamFn = (model, _context, options) => {
|
||||
const payload: Record<string, unknown> = {
|
||||
model: model.id,
|
||||
reasoning: { effort: "none", summary: "auto" },
|
||||
};
|
||||
options?.onPayload?.(payload, model);
|
||||
return createAssistantMessageEventStream();
|
||||
};
|
||||
|
||||
const payloads: Array<Record<string, unknown>> = [];
|
||||
const capture: StreamFn = (model, context, options) => {
|
||||
return baseStreamFn(model, context, {
|
||||
...options,
|
||||
onPayload: (payload, m) => {
|
||||
options?.onPayload?.(payload, m);
|
||||
payloads.push(structuredClone(payload as Record<string, unknown>));
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const wrapped = createOpenAIThinkingLevelWrapper(capture, "high");
|
||||
void wrapped(codexModel, { messages: [] }, {});
|
||||
|
||||
expect(payloads[0]?.reasoning).toEqual({ effort: "high", summary: "auto" });
|
||||
});
|
||||
|
||||
it("does not inject reasoning for completions API on proxy routes", () => {
|
||||
const { baseStreamFn, payloads } = createPayloadCapture();
|
||||
const wrapped = createOpenAIThinkingLevelWrapper(baseStreamFn, "medium");
|
||||
void wrapped(
|
||||
{
|
||||
api: "openai-completions",
|
||||
provider: "openai",
|
||||
id: "gpt-4o",
|
||||
baseUrl: "https://proxy.example.com/v1",
|
||||
} as Model<"openai-completions">,
|
||||
{ messages: [] },
|
||||
{},
|
||||
);
|
||||
|
||||
expect(payloads[0]?.reasoning).toBeUndefined();
|
||||
});
|
||||
|
||||
it("does not inject reasoning for proxy routes with custom baseUrl", () => {
|
||||
const { baseStreamFn, payloads } = createPayloadCapture();
|
||||
const wrapped = createOpenAIThinkingLevelWrapper(baseStreamFn, "medium");
|
||||
void wrapped(
|
||||
{
|
||||
api: "openai-responses",
|
||||
provider: "openai",
|
||||
id: "gpt-5.2",
|
||||
baseUrl: "https://proxy.example.com/v1",
|
||||
} as Model<"openai-responses">,
|
||||
{ messages: [] },
|
||||
{},
|
||||
);
|
||||
|
||||
expect(payloads[0]?.reasoning).toBeUndefined();
|
||||
});
|
||||
|
||||
it("passes through all thinking levels correctly on reasoning-capable models", () => {
|
||||
const levels = ["minimal", "low", "medium", "high", "xhigh"] as const;
|
||||
for (const level of levels) {
|
||||
const { baseStreamFn, payloads } = createPayloadCapture({
|
||||
initialReasoning: { effort: "none" },
|
||||
});
|
||||
const wrapped = createOpenAIThinkingLevelWrapper(baseStreamFn, level);
|
||||
void wrapped(codexModel, { messages: [] }, {});
|
||||
expect(payloads[0]?.reasoning).toEqual({ effort: level });
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { StreamFn } from "@mariozechner/pi-agent-core";
|
||||
import type { SimpleStreamOptions } from "@mariozechner/pi-ai";
|
||||
import { streamSimple } from "@mariozechner/pi-ai";
|
||||
import type { ThinkLevel } from "../../auto-reply/thinking.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { normalizeOptionalLowercaseString, readStringValue } from "../../shared/string-coerce.js";
|
||||
import {
|
||||
@@ -15,6 +16,7 @@ import {
|
||||
import { resolveOpenAITextVerbosity, type OpenAITextVerbosity } from "../openai-text-verbosity.js";
|
||||
import { resolveProviderRequestPolicyConfig } from "../provider-request-config.js";
|
||||
import { log } from "./logger.js";
|
||||
import { mapThinkingLevelToReasoningEffort } from "./reasoning-effort-utils.js";
|
||||
import { streamWithPayloadPatch } from "./stream-payload-utils.js";
|
||||
|
||||
type OpenAIServiceTier = "auto" | "default" | "flex" | "priority";
|
||||
@@ -224,6 +226,43 @@ export function createOpenAIStringContentWrapper(baseStreamFn: StreamFn | undefi
|
||||
};
|
||||
}
|
||||
|
||||
export function createOpenAIThinkingLevelWrapper(
|
||||
baseStreamFn: StreamFn | undefined,
|
||||
thinkingLevel?: ThinkLevel,
|
||||
): StreamFn {
|
||||
const underlying = baseStreamFn ?? streamSimple;
|
||||
if (!thinkingLevel) {
|
||||
return underlying;
|
||||
}
|
||||
return (model, context, options) => {
|
||||
if (!shouldApplyOpenAIReasoningCompatibility(model)) {
|
||||
return underlying(model, context, options);
|
||||
}
|
||||
return streamWithPayloadPatch(underlying, model, context, options, (payloadObj) => {
|
||||
const existingReasoning = payloadObj.reasoning;
|
||||
if (thinkingLevel === "off") {
|
||||
if (existingReasoning !== undefined) {
|
||||
delete payloadObj.reasoning;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (existingReasoning === "none") {
|
||||
payloadObj.reasoning = { effort: mapThinkingLevelToReasoningEffort(thinkingLevel) };
|
||||
return;
|
||||
}
|
||||
if (
|
||||
existingReasoning &&
|
||||
typeof existingReasoning === "object" &&
|
||||
!Array.isArray(existingReasoning)
|
||||
) {
|
||||
(existingReasoning as Record<string, unknown>).effort =
|
||||
mapThinkingLevelToReasoningEffort(thinkingLevel);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function createOpenAIFastModeWrapper(baseStreamFn: StreamFn | undefined): StreamFn {
|
||||
const underlying = baseStreamFn ?? streamSimple;
|
||||
return (model, context, options) => {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { resolveProviderRequestPolicy } from "../provider-attribution.js";
|
||||
import { resolveProviderRequestPolicyConfig } from "../provider-request-config.js";
|
||||
import { applyAnthropicEphemeralCacheControlMarkers } from "./anthropic-cache-control-payload.js";
|
||||
import { isAnthropicModelRef } from "./anthropic-family-cache-semantics.js";
|
||||
import { mapThinkingLevelToReasoningEffort } from "./reasoning-effort-utils.js";
|
||||
import { streamWithPayloadPatch } from "./stream-payload-utils.js";
|
||||
const KILOCODE_FEATURE_HEADER = "X-KILOCODE-FEATURE";
|
||||
const KILOCODE_FEATURE_DEFAULT = "openclaw";
|
||||
@@ -17,21 +18,6 @@ function resolveKilocodeAppHeaders(): Record<string, string> {
|
||||
return { [KILOCODE_FEATURE_HEADER]: feature };
|
||||
}
|
||||
|
||||
function mapThinkingLevelToOpenRouterReasoningEffort(
|
||||
thinkingLevel: ThinkLevel,
|
||||
): "none" | "minimal" | "low" | "medium" | "high" | "xhigh" {
|
||||
if (thinkingLevel === "off") {
|
||||
return "none";
|
||||
}
|
||||
if (thinkingLevel === "adaptive") {
|
||||
return "medium";
|
||||
}
|
||||
if (thinkingLevel === "max") {
|
||||
return "xhigh";
|
||||
}
|
||||
return thinkingLevel;
|
||||
}
|
||||
|
||||
function normalizeProxyReasoningPayload(payload: unknown, thinkingLevel?: ThinkLevel): void {
|
||||
if (!payload || typeof payload !== "object") {
|
||||
return;
|
||||
@@ -51,11 +37,11 @@ function normalizeProxyReasoningPayload(payload: unknown, thinkingLevel?: ThinkL
|
||||
) {
|
||||
const reasoningObj = existingReasoning as Record<string, unknown>;
|
||||
if (!("max_tokens" in reasoningObj) && !("effort" in reasoningObj)) {
|
||||
reasoningObj.effort = mapThinkingLevelToOpenRouterReasoningEffort(thinkingLevel);
|
||||
reasoningObj.effort = mapThinkingLevelToReasoningEffort(thinkingLevel);
|
||||
}
|
||||
} else if (!existingReasoning) {
|
||||
payloadObj.reasoning = {
|
||||
effort: mapThinkingLevelToOpenRouterReasoningEffort(thinkingLevel),
|
||||
effort: mapThinkingLevelToReasoningEffort(thinkingLevel),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
23
src/agents/pi-embedded-runner/reasoning-effort-utils.test.ts
Normal file
23
src/agents/pi-embedded-runner/reasoning-effort-utils.test.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { mapThinkingLevelToReasoningEffort } from "./reasoning-effort-utils.js";
|
||||
|
||||
describe("mapThinkingLevelToReasoningEffort", () => {
|
||||
it('maps "off" to "none"', () => {
|
||||
expect(mapThinkingLevelToReasoningEffort("off")).toBe("none");
|
||||
});
|
||||
|
||||
it('maps "adaptive" to "medium"', () => {
|
||||
expect(mapThinkingLevelToReasoningEffort("adaptive")).toBe("medium");
|
||||
});
|
||||
|
||||
it('maps "max" to "xhigh"', () => {
|
||||
expect(mapThinkingLevelToReasoningEffort("max")).toBe("xhigh");
|
||||
});
|
||||
|
||||
it.each(["minimal", "low", "medium", "high", "xhigh"] as const)(
|
||||
"passes through %s unchanged",
|
||||
(level) => {
|
||||
expect(mapThinkingLevelToReasoningEffort(level)).toBe(level);
|
||||
},
|
||||
);
|
||||
});
|
||||
16
src/agents/pi-embedded-runner/reasoning-effort-utils.ts
Normal file
16
src/agents/pi-embedded-runner/reasoning-effort-utils.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import type { ThinkLevel } from "../../auto-reply/thinking.js";
|
||||
|
||||
export type ReasoningEffort = "none" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
||||
|
||||
export function mapThinkingLevelToReasoningEffort(thinkingLevel: ThinkLevel): ReasoningEffort {
|
||||
if (thinkingLevel === "off") {
|
||||
return "none";
|
||||
}
|
||||
if (thinkingLevel === "adaptive") {
|
||||
return "medium";
|
||||
}
|
||||
if (thinkingLevel === "max") {
|
||||
return "xhigh";
|
||||
}
|
||||
return thinkingLevel;
|
||||
}
|
||||
@@ -11,7 +11,9 @@ import {
|
||||
createOpenAIReasoningCompatibilityWrapper,
|
||||
createOpenAIResponsesContextManagementWrapper,
|
||||
createOpenAIServiceTierWrapper,
|
||||
createOpenAIStringContentWrapper,
|
||||
createOpenAITextVerbosityWrapper,
|
||||
createOpenAIThinkingLevelWrapper,
|
||||
resolveOpenAIFastMode,
|
||||
resolveOpenAIServiceTier,
|
||||
resolveOpenAITextVerbosity,
|
||||
@@ -118,8 +120,11 @@ export function buildProviderStreamFamilyHooks(
|
||||
config: ctx.config,
|
||||
agentDir: ctx.agentDir,
|
||||
});
|
||||
nextStreamFn = createOpenAIStringContentWrapper(nextStreamFn);
|
||||
return createOpenAIResponsesContextManagementWrapper(
|
||||
createOpenAIReasoningCompatibilityWrapper(nextStreamFn),
|
||||
createOpenAIReasoningCompatibilityWrapper(
|
||||
createOpenAIThinkingLevelWrapper(nextStreamFn, ctx.thinkingLevel),
|
||||
),
|
||||
ctx.extraParams,
|
||||
);
|
||||
},
|
||||
|
||||
22
test/scripts/check-gateway-watch-regression.test.ts
Normal file
22
test/scripts/check-gateway-watch-regression.test.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { isIgnoredDistRuntimeWatchPath } from "../../scripts/check-gateway-watch-regression.mjs";
|
||||
|
||||
describe("check-gateway-watch-regression", () => {
|
||||
it("ignores top-level dist-runtime extension dependency repairs", () => {
|
||||
expect(isIgnoredDistRuntimeWatchPath("dist-runtime/extensions/node_modules")).toBe(true);
|
||||
expect(
|
||||
isIgnoredDistRuntimeWatchPath(
|
||||
"dist-runtime/extensions/node_modules/playwright-core/index.js",
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("keeps plugin runtime graph paths counted", () => {
|
||||
expect(isIgnoredDistRuntimeWatchPath("dist-runtime/extensions/openai/index.js")).toBe(false);
|
||||
expect(
|
||||
isIgnoredDistRuntimeWatchPath(
|
||||
"dist-runtime/extensions/openai/node_modules/openclaw/index.js",
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user