mirror of
https://github.com/decolua/9router.git
synced 2026-05-08 12:01:28 +00:00
feat: Add OpenAI API response_format support for structured JSON output
Translates OpenAI response_format parameter into Claude-compatible system prompt instructions, enabling structured JSON output for json_schema and json_object types. Co-authored-by: Nick Roth <nlr06886@gmail.com> Made-with: Cursor
This commit is contained in:
@@ -100,6 +100,21 @@ export function openaiToClaudeRequest(model, body, stream) {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle response_format for JSON mode
|
||||
if (body.response_format) {
|
||||
const responseFormat = body.response_format;
|
||||
if (responseFormat.type === "json_schema" && responseFormat.json_schema?.schema) {
|
||||
const schemaJson = JSON.stringify(responseFormat.json_schema.schema, null, 2);
|
||||
systemParts.push(`You must respond with valid JSON that strictly follows this JSON schema:
|
||||
\`\`\`json
|
||||
${schemaJson}
|
||||
\`\`\`
|
||||
Respond ONLY with the JSON object, no other text.`);
|
||||
} else if (responseFormat.type === "json_object") {
|
||||
systemParts.push("You must respond with valid JSON. Respond ONLY with a JSON object, no other text.");
|
||||
}
|
||||
}
|
||||
|
||||
// System with Claude Code prompt and cache_control
|
||||
const claudeCodePrompt = { type: "text", text: CLAUDE_SYSTEM_PROMPT };
|
||||
|
||||
|
||||
124
tests/unit/openai-to-claude.test.js
Normal file
124
tests/unit/openai-to-claude.test.js
Normal file
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* Unit tests for open-sse/translator/request/openai-to-claude.js
|
||||
*
|
||||
* Tests cover:
|
||||
* - openaiToClaudeRequest() - OpenAI to Claude request translation
|
||||
* - Response format handling (json_schema, json_object)
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { openaiToClaudeRequest } from "../../open-sse/translator/request/openai-to-claude.js";
|
||||
|
||||
describe("openaiToClaudeRequest", () => {
|
||||
describe("response_format handling", () => {
|
||||
it("should inject JSON schema instructions for json_schema type", () => {
|
||||
const body = {
|
||||
messages: [{ role: "user", content: "What is 2+2?" }],
|
||||
response_format: {
|
||||
type: "json_schema",
|
||||
json_schema: {
|
||||
name: "math_response",
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
answer: { type: "number" },
|
||||
explanation: { type: "string" }
|
||||
},
|
||||
required: ["answer", "explanation"]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const result = openaiToClaudeRequest("claude-sonnet-4.5", body, false);
|
||||
|
||||
// Should have system array with instructions
|
||||
expect(result.system).toBeDefined();
|
||||
expect(Array.isArray(result.system)).toBe(true);
|
||||
|
||||
// Check that system prompt includes schema
|
||||
const systemText = result.system
|
||||
.filter(s => s.type === "text")
|
||||
.map(s => s.text)
|
||||
.join("\n");
|
||||
|
||||
expect(systemText).toContain("You must respond with valid JSON");
|
||||
expect(systemText).toContain("\"answer\"");
|
||||
expect(systemText).toContain("\"explanation\"");
|
||||
expect(systemText).toContain("Respond ONLY with the JSON object");
|
||||
});
|
||||
|
||||
it("should inject basic JSON instructions for json_object type", () => {
|
||||
const body = {
|
||||
messages: [{ role: "user", content: "Give me a JSON object" }],
|
||||
response_format: {
|
||||
type: "json_object"
|
||||
}
|
||||
};
|
||||
|
||||
const result = openaiToClaudeRequest("claude-sonnet-4.5", body, false);
|
||||
|
||||
// Should have system array with instructions
|
||||
expect(result.system).toBeDefined();
|
||||
expect(Array.isArray(result.system)).toBe(true);
|
||||
|
||||
const systemText = result.system
|
||||
.filter(s => s.type === "text")
|
||||
.map(s => s.text)
|
||||
.join("\n");
|
||||
|
||||
expect(systemText).toContain("You must respond with valid JSON");
|
||||
expect(systemText).toContain("Respond ONLY with a JSON object");
|
||||
});
|
||||
|
||||
it("should not modify system prompt when response_format is missing", () => {
|
||||
const body = {
|
||||
messages: [{ role: "user", content: "Hello" }]
|
||||
};
|
||||
|
||||
const result = openaiToClaudeRequest("claude-sonnet-4.5", body, false);
|
||||
|
||||
// Should have system but without JSON instructions
|
||||
expect(result.system).toBeDefined();
|
||||
|
||||
const systemText = result.system
|
||||
.filter(s => s.type === "text")
|
||||
.map(s => s.text)
|
||||
.join("\n");
|
||||
|
||||
// Should NOT contain JSON-specific instructions
|
||||
expect(systemText).not.toContain("You must respond with valid JSON");
|
||||
});
|
||||
|
||||
it("should preserve existing system messages when adding response_format", () => {
|
||||
const body = {
|
||||
messages: [
|
||||
{ role: "system", content: "You are a helpful math tutor." },
|
||||
{ role: "user", content: "What is 2+2?" }
|
||||
],
|
||||
response_format: {
|
||||
type: "json_schema",
|
||||
json_schema: {
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
result: { type: "number" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const result = openaiToClaudeRequest("claude-sonnet-4.5", body, false);
|
||||
|
||||
// Should preserve original system message
|
||||
const systemText = result.system
|
||||
.filter(s => s.type === "text")
|
||||
.map(s => s.text)
|
||||
.join("\n");
|
||||
|
||||
expect(systemText).toContain("You are a helpful math tutor");
|
||||
expect(systemText).toContain("You must respond with valid JSON");
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user