fix: drop assistant prefill for cc bridge
This commit is contained in:
@@ -270,6 +270,31 @@ function buildClaudeCodeCompatibleMessages(messages: MessageLike[]) {
|
||||
merged.push({ role: message.role, content: [...message.content] });
|
||||
}
|
||||
|
||||
// CC-compatible sites we tested reject assistant-prefill shaped requests even
|
||||
// when Anthropic would normally allow them. Keep assistant/model history, but
|
||||
// drop trailing assistant turns so the upstream request ends on a user turn.
|
||||
while (merged.length > 0 && merged[merged.length - 1].role === "assistant") {
|
||||
merged.pop();
|
||||
}
|
||||
|
||||
if (merged.length === 0) {
|
||||
const fallbackText = converted
|
||||
.flatMap((message) => message.content)
|
||||
.map((block) => toNonEmptyString(block.text))
|
||||
.filter(Boolean)
|
||||
.join("\n")
|
||||
.trim();
|
||||
|
||||
if (fallbackText) {
|
||||
return [
|
||||
{
|
||||
role: "user" as const,
|
||||
content: [{ type: "text", text: fallbackText, cache_control: { type: "ephemeral" } }],
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = merged.length - 1; i >= 0; i--) {
|
||||
if (merged[i].role !== "user") continue;
|
||||
const lastBlock = merged[i].content[merged[i].content.length - 1];
|
||||
|
||||
@@ -51,7 +51,7 @@ test.after(() => {
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
test("buildClaudeCodeCompatibleRequest keeps order/text while mapping unsupported roles", () => {
|
||||
test("buildClaudeCodeCompatibleRequest keeps prior role history while dropping trailing assistant prefill", () => {
|
||||
const payload = buildClaudeCodeCompatibleRequest({
|
||||
sourceBody: {
|
||||
reasoning_effort: "xhigh",
|
||||
@@ -79,6 +79,7 @@ test("buildClaudeCodeCompatibleRequest keeps order/text while mapping unsupporte
|
||||
{ role: "user", content: [{ type: "text", text: "u1" }, { type: "image_url" }] },
|
||||
{ role: "model", content: "a1" },
|
||||
{ role: "user", content: [{ type: "text", text: "u2" }, { type: "tool_result" }] },
|
||||
{ role: "model", content: "prefill" },
|
||||
],
|
||||
},
|
||||
model: "claude-sonnet-4-6",
|
||||
@@ -120,6 +121,26 @@ test("buildClaudeCodeCompatibleRequest keeps order/text while mapping unsupporte
|
||||
assert.equal(JSON.parse(payload.metadata.user_id).session_id, "session-1");
|
||||
});
|
||||
|
||||
test("buildClaudeCodeCompatibleRequest falls back to a user turn when the source only has assistant/model text", () => {
|
||||
const payload = buildClaudeCodeCompatibleRequest({
|
||||
sourceBody: {
|
||||
messages: [{ role: "model", content: "draft" }],
|
||||
},
|
||||
normalizedBody: {
|
||||
messages: [{ role: "model", content: "draft" }],
|
||||
},
|
||||
model: "claude-sonnet-4-6",
|
||||
sessionId: "session-only-assistant",
|
||||
});
|
||||
|
||||
assert.deepEqual(payload.messages, [
|
||||
{
|
||||
role: "user",
|
||||
content: [{ type: "text", text: "draft", cache_control: { type: "ephemeral" } }],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test("buildClaudeCodeCompatibleRequest honors token priority fields", () => {
|
||||
const payload = buildClaudeCodeCompatibleRequest({
|
||||
sourceBody: { max_completion_tokens: 321 },
|
||||
|
||||
Reference in New Issue
Block a user