From 754a24d52a1cba5228a2e4288b93422be54aaa58 Mon Sep 17 00:00:00 2001 From: Nick Roth Date: Fri, 13 Mar 2026 10:13:02 +0700 Subject: [PATCH] fix: SSE data: [DONE] sentinel + response_format for Claude via GitHub - Guard data: [DONE] in github.js TransformStream with stream === true - Inject response_format as system prompt for Claude models via GitHub executor Note: stream.js guards skipped, createSSEStream is only called for true streaming paths. Cherry-picked and adapted from PR #286 by @rothnic https://github.com/decolua/9router/pull/286 Made-with: Cursor --- open-sse/executors/github.js | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/open-sse/executors/github.js b/open-sse/executors/github.js index 7188e575..f7f72a4a 100644 --- a/open-sse/executors/github.js +++ b/open-sse/executors/github.js @@ -44,6 +44,36 @@ export class GithubExecutor extends BaseExecutor { if (!body?.messages) return body; const sanitized = { ...body }; + + // Handle response_format for Claude models via GitHub + // GitHub's internal translation doesn't respect response_format, so we inject it as a system prompt + // AND prepend a reminder to the last user message for maximum effectiveness + if (body.response_format && body.model?.includes('claude')) { + const responseFormat = body.response_format; + let systemInstruction = ''; + if (responseFormat.type === 'json_schema' && responseFormat.json_schema?.schema) { + systemInstruction = 'CRITICAL: You must ONLY output raw JSON. Never use markdown code blocks. Never use backticks. Never wrap JSON in triple backticks. Output ONLY the raw JSON object.'; + } else if (responseFormat.type === 'json_object') { + systemInstruction = 'CRITICAL: You must ONLY output raw JSON. Never use markdown code blocks. Never use backticks.'; + } + if (systemInstruction) { + // Add to system message + const systemIdx = body.messages.findIndex(m => m.role === 'system'); + if (systemIdx >= 0) { + body.messages[systemIdx].content = systemInstruction + '\n\n' + body.messages[systemIdx].content; + } else { + body.messages.unshift({ role: 'system', content: systemInstruction }); + } + + // Also prepend to the last user message as a reminder + const lastUserIdx = body.messages.map((m, i) => m.role === 'user' ? i : -1).filter(i => i >= 0).pop(); + if (lastUserIdx >= 0) { + const userMsg = body.messages[lastUserIdx]; + const userContent = typeof userMsg.content === 'string' ? userMsg.content : JSON.stringify(userMsg.content); + userMsg.content = 'Respond with ONLY raw JSON (no markdown, no backticks, no code blocks): ' + userContent; + } + } + } sanitized.messages = body.messages.map(msg => { // assistant messages with only tool_calls have content: null — leave as-is if (!msg.content) return msg; @@ -143,7 +173,7 @@ export class GithubExecutor extends BaseExecutor { const parsed = parseSSELine(trimmed); if (!parsed) continue; - if (parsed.done) { + if (parsed.done && stream === true) { controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n")); continue; }