baa-conductor

git clone 

commit
d58f880
parent
1bddb21
author
im_wower
date
2026-03-28 02:09:55 +0800 CST
fix: keep buffered SSE full_text text-only
2 files changed,  +58, -9
M apps/conductor-daemon/src/index.test.js
+44, -1
 1@@ -1472,6 +1472,20 @@ function createBrowserBridgeStub() {
 2                 "",
 3                 "event: completion",
 4                 'data: {"type":"completion","completion":"world"}',
 5+                "",
 6+                "event: completion",
 7+                'data: {"type":"completion","completion":"","id":"chatcompl_01-buffered","stop":"\\n\\nHuman:","log_id":"log_01-buffered","messageLimit":{"type":"within_limit","resetsAt":"2026-03-28T00:00:00.000Z"}}',
 8+                ""
 9+              ].join("\n")
10+            });
11+          }
12+
13+          if (input.path === "/backend-api/conversation-buffered-smoke") {
14+            return buildApiResponse({
15+              id: input.id,
16+              body: [
17+                "event: message",
18+                'data: {"message":{"id":"msg-chatgpt-buffered-1","author":{"role":"assistant"},"content":{"content_type":"text","parts":["Buffered ChatGPT answer"]}}}',
19                 ""
20               ].join("\n")
21             });
22@@ -2738,13 +2752,41 @@ test("handleConductorHttpRequest serves the migrated local business endpoints fr
23     assert.equal(bufferedSsePayload.data.request_mode, "api_request");
24     assert.equal(bufferedSsePayload.data.proxy.path, "/api/stream-buffered-smoke");
25     assert.equal(bufferedSsePayload.data.response.content_type, "text/event-stream");
26-    assert.equal(bufferedSsePayload.data.response.events.length, 2);
27+    assert.equal(bufferedSsePayload.data.response.events.length, 3);
28     assert.equal(bufferedSsePayload.data.response.events[0].event, "completion");
29     assert.equal(bufferedSsePayload.data.response.events[0].data.type, "completion");
30     assert.equal(bufferedSsePayload.data.response.events[0].data.completion, "Hello ");
31     assert.equal(bufferedSsePayload.data.response.events[1].data.completion, "world");
32+    assert.equal(bufferedSsePayload.data.response.events[2].data.completion, "");
33+    assert.equal(bufferedSsePayload.data.response.events[2].data.id, "chatcompl_01-buffered");
34+    assert.equal(bufferedSsePayload.data.response.events[2].data.stop, "\n\nHuman:");
35     assert.equal(bufferedSsePayload.data.response.full_text, "Hello world");
36 
37+    const chatgptBufferedSseResponse = await handleConductorHttpRequest(
38+      {
39+        body: JSON.stringify({
40+          platform: "chatgpt",
41+          method: "GET",
42+          path: "/backend-api/conversation-buffered-smoke",
43+          requestId: "browser-chatgpt-buffered-sse-123"
44+        }),
45+        method: "POST",
46+        path: "/v1/browser/request"
47+      },
48+      localApiContext
49+    );
50+    assert.equal(chatgptBufferedSseResponse.status, 200);
51+    const chatgptBufferedSsePayload = parseJsonBody(chatgptBufferedSseResponse);
52+    assert.equal(chatgptBufferedSsePayload.data.request_mode, "api_request");
53+    assert.equal(chatgptBufferedSsePayload.data.proxy.path, "/backend-api/conversation-buffered-smoke");
54+    assert.equal(chatgptBufferedSsePayload.data.response.content_type, "text/event-stream");
55+    assert.equal(chatgptBufferedSsePayload.data.response.events[0].event, "message");
56+    assert.equal(
57+      chatgptBufferedSsePayload.data.response.events[0].data.message.content.parts[0],
58+      "Buffered ChatGPT answer"
59+    );
60+    assert.equal(chatgptBufferedSsePayload.data.response.full_text, "Buffered ChatGPT answer");
61+
62     const chatgptBufferedResponse = await handleConductorHttpRequest(
63       {
64         body: JSON.stringify({
65@@ -3206,6 +3248,7 @@ test("handleConductorHttpRequest serves the migrated local business endpoints fr
66       "apiRequest:GET:/api/organizations/org-1/chat_conversations",
67       "apiRequest:POST:/api/organizations/org-1/chat_conversations/conv-1/completion",
68       "apiRequest:GET:/api/stream-buffered-smoke",
69+      "apiRequest:GET:/backend-api/conversation-buffered-smoke",
70       "apiRequest:GET:/backend-api/models",
71       "apiRequest:GET:/api/organizations",
72       "apiRequest:GET:/api/organizations/org-1/chat_conversations",
M apps/conductor-daemon/src/local-api.ts
+14, -8
 1@@ -1392,6 +1392,18 @@ function isLikelyBufferedSseText(value: string): boolean {
 2   return prefixedLineCount > 0;
 3 }
 4 
 5+const BUFFERED_SSE_TEXT_PATH_KEYS = [
 6+  "text",
 7+  "value",
 8+  "content",
 9+  "message",
10+  "markdown",
11+  "completion",
12+  "delta",
13+  "parts",
14+  "choices"
15+] as const;
16+
17 function extractBufferedSseTextFragments(value: JsonValue | null): string[] {
18   if (typeof value === "string") {
19     return value === "" ? [] : [value];
20@@ -1405,16 +1417,10 @@ function extractBufferedSseTextFragments(value: JsonValue | null): string[] {
21     return [];
22   }
23 
24-  const directKeys = ["text", "value", "content", "message", "markdown", "completion"];
25-  const directValues = directKeys.flatMap((fieldName) =>
26+  // Only follow fields that are known to contain text or wrap nested text.
27+  return BUFFERED_SSE_TEXT_PATH_KEYS.flatMap((fieldName) =>
28     extractBufferedSseTextFragments(value[fieldName] as JsonValue)
29   );
30-
31-  if (directValues.length > 0) {
32-    return directValues;
33-  }
34-
35-  return Object.values(value).flatMap((entry) => extractBufferedSseTextFragments(entry as JsonValue));
36 }
37 
38 function parseBufferedSseProxyBody(body: string): JsonObject | null {