fix(translator): map Claude "refusal" stop_reason to content_filter and surface its explanation

Anthropic's API-level refusal (streaming classifier / ToS) ends the stream
with stop_reason "refusal", stop_details carrying the reason, zero output
tokens and no content blocks. Map refusal to content_filter in both
directions, surface stop_details.explanation as message text, and add
CLAUDE_STOP.REFUSAL to schema.
This commit is contained in:
Welington
2026-09-22 15:25:09 +07:00
parent 1a02713150
commit 0f488c7027
5 changed files with 97 additions and 0 deletions

View File

@@ -40,6 +40,9 @@ describe("toOpenAIFinish - claude", () => {
["end_turn", "stop"],
["max_tokens", "length"],
["tool_use", "tool_calls"],
["stop_sequence", "stop"],
["refusal", "content_filter"],
["unknown_xyz", "stop"],
])("%s -> %s", (input, expected) => {
expect(toOpenAIFinish(input, "claude")).toBe(expected);
});
@@ -58,6 +61,9 @@ describe("fromOpenAIFinish round-trip - claude", () => {
it("tool_calls -> tool_use", () => {
expect(fromOpenAIFinish("tool_calls", "claude")).toBe("tool_use");
});
it("content_filter -> refusal", () => {
expect(fromOpenAIFinish("content_filter", "claude")).toBe("refusal");
});
it("length -> max_tokens", () => {
expect(fromOpenAIFinish("length", "claude")).toBe("max_tokens");
});