fix(platform): fix SSE parser and prevent empty Discord messages

- Fix streamChat SSE parser: backend sends type=text_delta with delta
  field, not type=text with content field
- Collect full response before posting to Discord — empty async
  generators caused 'Cannot send empty message' spam that hit rate limits
- Show fallback message if CoPilot returns no text content
This commit is contained in:
Bentlybro
2026-04-02 14:13:57 +00:00
parent 3806f58e6b
commit 8aeb782ccb
2 changed files with 23 additions and 9 deletions

View File

@@ -206,9 +206,22 @@ async function handleCoPilotMessage(
console.log(`[bot] Created session ${sessionId} for user ${userId.slice(-8)}`);
}
// Stream CoPilot response — thread.post() accepts AsyncIterable<string>
// Stream CoPilot response — collect chunks, then post.
// We collect first because thread.post() with an empty stream
// causes Discord "Cannot send an empty message" errors.
const stream = api.streamChat(userId, text, sessionId);
await thread.post(stream);
let response = "";
for await (const chunk of stream) {
response += chunk;
}
if (response.trim()) {
await thread.post(response);
} else {
await thread.post(
"I processed your message but didn't generate a response. Please try again."
);
}
} catch (err: unknown) {
const errMsg = err instanceof Error ? err.message : String(err);
console.error(`[bot] CoPilot error for user ${userId.slice(-8)}:`, errMsg);

View File

@@ -193,15 +193,16 @@ export class PlatformAPI {
try {
const parsed = JSON.parse(data);
if (parsed.type === "text" && parsed.content) {
yield parsed.content;
} else if (typeof parsed === "string") {
yield parsed;
// Backend sends: text_delta (streaming chunks), text_start/text_end,
// start/finish (lifecycle), step_start/step_finish, error, etc.
if (parsed.type === "text_delta" && parsed.delta) {
yield parsed.delta;
} else if (parsed.type === "error" && parsed.content) {
yield `Error: ${parsed.content}`;
}
// Ignore start/finish/step lifecycle events — they carry no text
} catch {
if (data && data !== "[DONE]") {
yield data;
}
// Non-JSON data line — skip
}
}
}