mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 13:38:31 +00:00
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
This commit is contained in:
@@ -44,6 +44,36 @@ export class GithubExecutor extends BaseExecutor {
|
|||||||
if (!body?.messages) return body;
|
if (!body?.messages) return body;
|
||||||
|
|
||||||
const sanitized = { ...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 => {
|
sanitized.messages = body.messages.map(msg => {
|
||||||
// assistant messages with only tool_calls have content: null — leave as-is
|
// assistant messages with only tool_calls have content: null — leave as-is
|
||||||
if (!msg.content) return msg;
|
if (!msg.content) return msg;
|
||||||
@@ -143,7 +173,7 @@ export class GithubExecutor extends BaseExecutor {
|
|||||||
const parsed = parseSSELine(trimmed);
|
const parsed = parseSSELine(trimmed);
|
||||||
if (!parsed) continue;
|
if (!parsed) continue;
|
||||||
|
|
||||||
if (parsed.done) {
|
if (parsed.done && stream === true) {
|
||||||
controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n"));
|
controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n"));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user