From 7cde8329bc33801248d6aafa2a4dd46dc86e5683 Mon Sep 17 00:00:00 2001 From: Frank Date: Mon, 24 Aug 2026 13:19:53 -0400 Subject: [PATCH] update model parser --- .../app/src/routes/zen/util/requestBody.ts | 80 +++++++++++++++---- packages/console/app/test/requestBody.test.ts | 15 ++++ 2 files changed, 78 insertions(+), 17 deletions(-) diff --git a/packages/console/app/src/routes/zen/util/requestBody.ts b/packages/console/app/src/routes/zen/util/requestBody.ts index 458faf553e7..a3970dedf13 100644 --- a/packages/console/app/src/routes/zen/util/requestBody.ts +++ b/packages/console/app/src/routes/zen/util/requestBody.ts @@ -7,38 +7,84 @@ export async function prepareRequestBody(body: ReadableStream) { const decoder = new TextDecoder() let text = "" let done = false - let searchFrom = 0 let bom = 0 - let match: RegExpExecArray | null = null - const pattern = /("model"\s*:\s*")([^"]+)"/g + let index = 0 + let depth = 0 + let stringStart = -1 + let escaped = false + let phase: "key" | "colon" | "value" | "comma" = "key" + let key = "" + let found: { model: string; start: number; end: number } | undefined - while (!done && !match) { + const scan = () => { + while (index < text.length && !found) { + const char = text[index] + if (stringStart >= 0) { + if (escaped) escaped = false + else if (char === "\\") escaped = true + else if (char === '"') { + if (depth === 1 && phase === "key") { + key = JSON.parse(text.slice(stringStart, index + 1)) + phase = "colon" + } else if (depth === 1 && phase === "value") { + if (key === "model") { + const start = bom + utf8Length(text, stringStart + 1) + found = { + model: JSON.parse(text.slice(stringStart, index + 1)), + start, + end: bom + utf8Length(text, index), + } + } + phase = "comma" + } + stringStart = -1 + } + index++ + continue + } + + if (char === '"') { + stringStart = index++ + continue + } + if (char === "{" || char === "[") { + if (depth === 1 && phase === "value") phase = "comma" + depth++ + index++ + continue + } + if (char === "}" || char === "]") { + depth-- + index++ + continue + } + if (depth !== 1) { + index++ + continue + } + if (char === ":" && phase === "colon") phase = "value" + else if (char === "," && phase === "comma") phase = "key" + else if (phase === "value" && !/\s/.test(char)) phase = "comma" + index++ + } + } + + while (!done && !found) { const next = await reader.read() done = next.done if (!next.value) continue if (!chunks.length && next.value[0] === 0xef && next.value[1] === 0xbb && next.value[2] === 0xbf) bom = 3 chunks.push(next.value) text += decoder.decode(next.value, { stream: true }) - pattern.lastIndex = searchFrom - match = pattern.exec(text) - searchFrom = Math.max(0, text.length - 256) + scan() } if (done) { text += decoder.decode() - if (!match) { - pattern.lastIndex = searchFrom - match = pattern.exec(text) - } + scan() } - const found = (() => { - if (!match) return - const start = bom + utf8Length(text, match.index + match[1].length) - return { model: match[2], start, end: start + utf8Length(match[2], match[2].length) } - })() const preview = text.substring(0, 300) text = "" - match = null let used = false return { diff --git a/packages/console/app/test/requestBody.test.ts b/packages/console/app/test/requestBody.test.ts index 52d86297b4e..db3c81b9181 100644 --- a/packages/console/app/test/requestBody.test.ts +++ b/packages/console/app/test/requestBody.test.ts @@ -32,6 +32,21 @@ describe("Zen request body streaming", () => { }) }) + test("ignores model fields nested before the root model", async () => { + const body = new Blob([ + '{"metadata":{"model":"ox-alpha-free"},"model":"glm-5.3","messages":[],"stream":false}', + ]).stream() + const request = await prepareRequestBody(body) + + expect(request.model).toBe("glm-5.3") + expect(JSON.parse(await new Response(request.stream("provider-model", false)).text())).toEqual({ + metadata: { model: "ox-alpha-free" }, + model: "provider-model", + messages: [], + stream: false, + }) + }) + test("appends stream usage options at the end of the request", async () => { const body = new Blob(['{"model":"client-model","stream":true,"messages":[]} ']).stream() const request = await prepareRequestBody(body)