Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions open-sse/services/claudeCodeCompatible.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { createHash, randomUUID } from "node:crypto";

import { prepareClaudeRequest } from "../translator/helpers/claudeHelper.ts";
import {
applyClaudeOAuthLexicalRewrite,
prepareClaudeRequest,
} from "../translator/helpers/claudeHelper.ts";

export const CLAUDE_CODE_COMPATIBLE_PREFIX = "anthropic-compatible-cc-";
export const CLAUDE_CODE_COMPATIBLE_DEFAULT_CHAT_PATH = "/v1/messages?beta=true";
Expand Down Expand Up @@ -180,11 +183,13 @@ export function buildClaudeCodeCompatibleRequest({
const toolChoice =
tools.length > 0
? buildClaudeCodeCompatibleToolChoice(
normalizedBody?.["tool_choice"] ?? sourceBody?.["tool_choice"]
preparedClaudeBody?.tool_choice ??
normalizedBody?.["tool_choice"] ??
sourceBody?.["tool_choice"]
)
: undefined;

return {
const requestBody = {
model,
messages,
system,
Expand Down Expand Up @@ -217,6 +222,13 @@ export function buildClaudeCodeCompatibleRequest({
...(toolChoice ? { tool_choice: toolChoice } : {}),
...(stream ? { stream: true } : {}),
};

const lexicalRewrite = applyClaudeOAuthLexicalRewrite(requestBody);
if (lexicalRewrite.toolNameMap) {
lexicalRewrite.body._toolNameMap = lexicalRewrite.toolNameMap;
}

return lexicalRewrite.body;
}

export function resolveClaudeCodeCompatibleEffort(
Expand Down Expand Up @@ -539,6 +551,7 @@ function prepareClaudeCodeCompatibleBody(
system: normalizeClaudeSystemInput(claudeBody.system),
messages: normalizeClaudeMessageInput(claudeBody.messages),
tools: normalizeClaudeToolInput(claudeBody.tools),
tool_choice: readRecord(claudeBody.tool_choice) || claudeBody.tool_choice,
thinking: readRecord(claudeBody.thinking) || claudeBody.thinking,
},
CLAUDE_CODE_COMPATIBLE_PREFIX,
Expand Down
35 changes: 31 additions & 4 deletions tests/unit/claude-code-compatible-request.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -109,16 +109,29 @@ test("buildClaudeCodeCompatibleRequest covers normalized OpenAI-style messages,
test("buildClaudeCodeCompatibleRequest covers Claude-native bodies and cache-control stripping", () => {
const stripped = buildClaudeCodeCompatibleRequest({
claudeBody: {
system: [{ type: "text", text: "sys", cache_control: { type: "ephemeral" } }],
system: [
{
type: "text",
text: "sys <directories>src/</directories>",
cache_control: { type: "ephemeral" },
},
],
messages: [
{
role: "assistant",
content: [{ type: "text", text: "prefill", cache_control: { type: "ephemeral" } }],
content: [
{ type: "text", text: "prefill", cache_control: { type: "ephemeral" } },
{ type: "tool_use", id: "call_1", name: "background_output", input: {} },
],
},
{
role: "user",
content: [
{ type: "text", text: "ask", cache_control: { type: "ephemeral" } },
{
type: "text",
text: "ask background_cancel",
cache_control: { type: "ephemeral" },
},
{
type: "image",
source: { type: "base64", media_type: "image/png", data: "abc" },
Expand All @@ -132,8 +145,14 @@ test("buildClaudeCodeCompatibleRequest covers Claude-native bodies and cache-con
},
],
tools: [
{ name: "toolA", input_schema: { type: "object" }, cache_control: { type: "ephemeral" } },
{
name: "background_cancel",
description: "Cancel background_output work",
input_schema: { type: "object" },
cache_control: { type: "ephemeral" },
},
],
tool_choice: { type: "tool", name: "background_cancel" },
thinking: { type: "enabled", budget_tokens: 12 },
},
model: "claude-sonnet-4-6",
Expand Down Expand Up @@ -171,8 +190,16 @@ test("buildClaudeCodeCompatibleRequest covers Claude-native bodies and cache-con
assert.equal(JSON.parse(stripped.metadata.user_id).session_id, "explicit-session");
assert.equal(stripped.messages.at(-1).role, "user");
assert.equal(stripped.messages[0].content[0].cache_control, undefined);
assert.equal(stripped.messages[0].content[1].name, "background_result");
assert.equal(stripped.messages[1].content[0].text, "ask background_stop");
assert.equal(stripped.system.at(-1).cache_control, undefined);
assert.equal(stripped.system.at(-1).text, "sys directories:\nsrc/");
assert.equal(stripped.tools[0].cache_control, undefined);
assert.equal(stripped.tools[0].name, "background_stop");
assert.equal(stripped.tools[0].description, "Cancel background_result work");
assert.deepEqual(stripped.tool_choice, { type: "tool", name: "background_stop" });
assert.equal(stripped._toolNameMap.get("background_result"), "background_output");
assert.equal(stripped._toolNameMap.get("background_stop"), "background_cancel");
assert.equal(preserved.messages[0].content[0].cache_control.type, "ephemeral");
assert.equal(preserved.system.at(-1).cache_control.type, "ephemeral");
assert.equal(preserved.tools[0].cache_control.type, "ephemeral");
Expand Down
Loading