Merge PR #8002: fix(web-search): parse double-encoded Z.AI results (@roboomp)
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
- Fixed task subagent assistant turns being omitted from the per-model TPS/TTFT aggregates shown by `/models`. ([#8022](https://github.com/can1357/oh-my-pi/issues/8022))
|
||||
- Fixed terminal-title spinner writes consuming CPU during WSL/ConPTY agent waits by using the same static working separator as native Windows ([#8012](https://github.com/can1357/oh-my-pi/issues/8012)).
|
||||
- Fixed long-running sessions leaking memory for every completed keep-alive `task`/scout subagent: a disposed (parked) subagent's `AgentSession` stayed pinned through the lifecycle adoption record's reviver closure, and `dispose()` never released the message array, append-only provider transcript, session-manager entries, or the raw-SSE debug buffer, so heavy transcripts and captured provider wire frames accumulated for the process lifetime ([#8003](https://github.com/can1357/oh-my-pi/issues/8003)).
|
||||
- Fixed Z.AI web search dropping sources and exposing raw JSON when MCP responses double-encode content text ([#8000](https://github.com/can1357/oh-my-pi/issues/8000)).
|
||||
|
||||
## [17.2.11] - 2026-08-07
|
||||
|
||||
|
||||
@@ -353,11 +353,20 @@ function parseSearchPayload(rawResult: unknown): {
|
||||
for (const part of content) {
|
||||
const text = isRecord(part) ? asString(part.text) : null;
|
||||
if (!text) continue;
|
||||
textParts.push(text);
|
||||
try {
|
||||
candidates.push(JSON.parse(text));
|
||||
let parsed: unknown = JSON.parse(text);
|
||||
if (typeof parsed === "string") {
|
||||
try {
|
||||
parsed = JSON.parse(parsed);
|
||||
} catch {
|
||||
// The decoded string is answer text rather than another JSON payload.
|
||||
}
|
||||
}
|
||||
candidates.push(parsed);
|
||||
if (getSearchResults(parsed).length === 0) textParts.push(text);
|
||||
} catch {
|
||||
// Not JSON payload; keep as fallback answer text.
|
||||
// Non-JSON content is preserved as answer text.
|
||||
textParts.push(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ interface CapturedRequest {
|
||||
}
|
||||
|
||||
describe("Z.AI web search provider", () => {
|
||||
it("initializes a Streamable HTTP MCP session before calling web_search_prime", async () => {
|
||||
it("initializes a Streamable HTTP MCP session and parses doubly encoded results", async () => {
|
||||
const capturedRequests: CapturedRequest[] = [];
|
||||
const fetchImpl: FetchImpl = (_input, init) => {
|
||||
const request = {
|
||||
@@ -53,16 +53,20 @@ describe("Z.AI web search provider", () => {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify({
|
||||
search_result: [
|
||||
text: JSON.stringify(
|
||||
JSON.stringify([
|
||||
{
|
||||
title: "Z.AI search result",
|
||||
content: "Search result content",
|
||||
link: "https://example.com/zai",
|
||||
media: "Example",
|
||||
},
|
||||
],
|
||||
}),
|
||||
]),
|
||||
),
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
text: "Plain prose answer.",
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -107,6 +111,7 @@ describe("Z.AI web search provider", () => {
|
||||
author: "Example",
|
||||
},
|
||||
]);
|
||||
expect(response.answer).toBe("Plain prose answer.");
|
||||
});
|
||||
|
||||
function createMcpFetch(): { fetchImpl: FetchImpl; capturedRequests: CapturedRequest[] } {
|
||||
|
||||
Reference in New Issue
Block a user