From 4da1d6dad49c0f550036e67bb0395bf33ea66bb1 Mon Sep 17 00:00:00 2001 From: decolua Date: Sat, 13 Jun 2026 22:05:01 +0700 Subject: [PATCH] =?UTF-8?q?refactor(open-sse):=20D1c=20=E2=80=94=20forceSt?= =?UTF-8?q?ream=20hardcode=20=E2=86=92=20PROVIDERS=20schema=20(#5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chatCore providerRequiresStreaming: switch provider-name → PROVIDERS[provider].forceStream. Thêm forceStream:true vào registry openai/codex/commandcode. verify-providers allowlist added-fields (forceStream/urlSuffix verified bằng golden + runtime test riêng). Co-authored-by: Cursor --- open-sse/handlers/chatCore.js | 3 ++- open-sse/providers/registry/codex.js | 1 + open-sse/providers/registry/commandcode.js | 1 + open-sse/providers/registry/openai.js | 3 ++- tests/__baseline__/verify-providers.mjs | 8 +++++++- tests/unit/force-stream-config.test.js | 17 +++++++++++++++++ 6 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 tests/unit/force-stream-config.test.js diff --git a/open-sse/handlers/chatCore.js b/open-sse/handlers/chatCore.js index 65281cfa..18bffa24 100644 --- a/open-sse/handlers/chatCore.js +++ b/open-sse/handlers/chatCore.js @@ -7,6 +7,7 @@ import { createStreamController } from "../utils/streamHandler.js"; import { refreshWithRetry } from "../services/tokenRefresh.js"; import { createRequestLogger } from "../utils/requestLogger.js"; import { getModelTargetFormat, getModelStrip, getModelUpstreamId, getModelType, PROVIDER_ID_TO_ALIAS } from "../config/providerModels.js"; +import { PROVIDERS } from "../config/providers.js"; import { createErrorResult, parseUpstreamError, formatProviderError } from "../utils/error.js"; import { HTTP_STATUS } from "../config/runtimeConfig.js"; import { handleBypassRequest } from "../utils/bypassHandler.js"; @@ -59,7 +60,7 @@ export async function handleChatCore({ body, modelInfo, credentials, log, onCred } const clientRequestedStreaming = body.stream === true || sourceFormat === FORMATS.ANTIGRAVITY || sourceFormat === FORMATS.GEMINI || sourceFormat === FORMATS.GEMINI_CLI; - const providerRequiresStreaming = provider === "openai" || provider === "codex" || provider === "commandcode"; + const providerRequiresStreaming = PROVIDERS[provider]?.forceStream === true; let stream = providerRequiresStreaming ? true : (body.stream !== false); // DeepSeek-TUI: interactive TUI panel sends stream:true and needs SSE. diff --git a/open-sse/providers/registry/codex.js b/open-sse/providers/registry/codex.js index bbad11a5..245492d3 100644 --- a/open-sse/providers/registry/codex.js +++ b/open-sse/providers/registry/codex.js @@ -6,6 +6,7 @@ export default { transport: { baseUrl: "https://chatgpt.com/backend-api/codex/responses", format: "openai-responses", + forceStream: true, headers: { "originator": "codex_cli_rs", "User-Agent": "codex_cli_rs/0.136.0" diff --git a/open-sse/providers/registry/commandcode.js b/open-sse/providers/registry/commandcode.js index cd30088c..5b029a97 100644 --- a/open-sse/providers/registry/commandcode.js +++ b/open-sse/providers/registry/commandcode.js @@ -4,6 +4,7 @@ export default { "transport": { "baseUrl": "https://api.commandcode.ai/alpha/generate", "format": "commandcode", + "forceStream": true, "headers": { "x-command-code-version": "0.25.7", "x-cli-environment": "cli" diff --git a/open-sse/providers/registry/openai.js b/open-sse/providers/registry/openai.js index 80eeece0..aa5b5c34 100644 --- a/open-sse/providers/registry/openai.js +++ b/open-sse/providers/registry/openai.js @@ -2,7 +2,8 @@ export default { "id": "openai", "alias": "openai", "transport": { - "baseUrl": "https://api.openai.com/v1/chat/completions" + "baseUrl": "https://api.openai.com/v1/chat/completions", + "forceStream": true }, "models": [ { diff --git a/tests/__baseline__/verify-providers.mjs b/tests/__baseline__/verify-providers.mjs index f007226b..3b391bad 100644 --- a/tests/__baseline__/verify-providers.mjs +++ b/tests/__baseline__/verify-providers.mjs @@ -8,8 +8,14 @@ import { PROVIDERS } from "../../open-sse/config/providers.js"; const here = dirname(fileURLToPath(import.meta.url)); const baseline = JSON.parse(readFileSync(join(here, "providers-baseline.json"), "utf8")); -// Normalize via JSON roundtrip so function/undefined are dropped identically. +// Fields intentionally added during refactor (verified by dedicated runtime tests, not byte-baseline). +const ADDED_FIELDS = new Set(["forceStream", "urlSuffix"]); + +// Normalize via JSON roundtrip so function/undefined are dropped identically; drop added fields. const current = JSON.parse(JSON.stringify(PROVIDERS)); +for (const id of Object.keys(current)) { + for (const f of ADDED_FIELDS) delete current[id][f]; +} const diffs = []; const allIds = new Set([...Object.keys(baseline), ...Object.keys(current)]); diff --git a/tests/unit/force-stream-config.test.js b/tests/unit/force-stream-config.test.js new file mode 100644 index 00000000..960770a1 --- /dev/null +++ b/tests/unit/force-stream-config.test.js @@ -0,0 +1,17 @@ +// Guards forceStream moved from chatCore hardcode → PROVIDERS schema (#5). +import { describe, it, expect } from "vitest"; + +const FORCED = ["openai", "codex", "commandcode"]; + +describe("forceStream provider config", () => { + it("only openai/codex/commandcode force streaming", async () => { + const { PROVIDERS } = await import("../../open-sse/config/providers.js"); + for (const id of FORCED) { + expect(PROVIDERS[id]?.forceStream, `${id} forced`).toBe(true); + } + // a sample of others must NOT force + for (const id of ["deepseek", "claude", "gemini", "openrouter"]) { + expect(PROVIDERS[id]?.forceStream, `${id} not forced`).not.toBe(true); + } + }); +});