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
21 changes: 12 additions & 9 deletions open-sse/translator/helpers/schemaCoercion.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isDeepSeekReasoningModel } from "../../services/reasoningCache.ts";
import { isDeepSeekReasoningModel, requiresReasoningReplay } from "../../services/reasoningCache.ts";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The import isDeepSeekReasoningModel is no longer used in this file. It was replaced by requiresReasoningReplay in the injectEmptyReasoningContentForToolCalls function. Removing unused imports helps maintain code cleanliness.

Suggested change
import { isDeepSeekReasoningModel, requiresReasoningReplay } from "../../services/reasoningCache.ts";
import { requiresReasoningReplay } from "../../services/reasoningCache.ts";


/**
* Shared sanitizers for tool payloads that arrive from IDEs/SDKs with
Expand Down Expand Up @@ -201,14 +201,17 @@ export function injectEmptyReasoningContentForToolCalls(
provider: unknown,
model: unknown
): unknown {
if (
!Array.isArray(messages) ||
!isDeepSeekReasoningModel({
provider: String(provider ?? ""),
model: String(model ?? ""),
thinkingEnabled: true,
})
) {
const normalizedProvider = String(provider ?? "");
const normalizedModel = String(model ?? "");

// Check if this provider/model requires reasoning replay (DeepSeek V4, Kimi K2, etc.)
const needsReasoning = requiresReasoningReplay({
provider: normalizedProvider,
model: normalizedModel,
thinkingEnabled: true,
});

if (!Array.isArray(messages) || !needsReasoning) {
return messages;
}

Expand Down
94 changes: 93 additions & 1 deletion tests/unit/schema-coercion.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,17 @@ test("injectEmptyReasoningContentForToolCalls supports DeepSeek V4 models across
}
});

test("injectEmptyReasoningContentForToolCalls skips non-DeepSeek V4 models", () => {
test("injectEmptyReasoningContentForToolCalls skips non-reasoning models", () => {
const messages = [{ role: "assistant", tool_calls: [{ id: "call_1" }] }];

const result = injectEmptyReasoningContentForToolCalls(
messages,
"deepseek",
"deepseek-v3"
) as Array<{ reasoning_content?: string }>;

assert.equal(result[0].reasoning_content, undefined);
});
Comment on lines +134 to +144

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This change introduces a syntax error in the test file. While a new test block was added, the header for the previous test (formerly at line 134) was removed, but its body (lines 145-154) was left dangling. This results in orphaned code and an unmatched closing brace }); at line 154, which will prevent the test suite from running.

Please remove the redundant code block from lines 145 to 154 to restore the file's integrity.

const messages = [{ role: "assistant", tool_calls: [{ id: "call_1" }] }];

const result = injectEmptyReasoningContentForToolCalls(
Expand All @@ -142,3 +152,85 @@ test("injectEmptyReasoningContentForToolCalls skips non-DeepSeek V4 models", ()

assert.equal(result[0].reasoning_content, undefined);
});

test("injectEmptyReasoningContentForToolCalls supports Kimi K2 models", () => {
const messages = [
{ role: "user", content: "hello" },
{ role: "assistant", tool_calls: [{ id: "call_1" }] },
];

for (const model of ["kimi-k2", "kimi-k2.5", "kimi-k2.6", "kimi-k2-thinking"]) {
const result = injectEmptyReasoningContentForToolCalls(
messages,
"moonshot",
model
) as Array<{ reasoning_content?: string }>;

assert.equal(result[1].reasoning_content, "", `should inject reasoning_content for ${model}`);
}
});

test("injectEmptyReasoningContentForToolCalls supports Kimi K2 via Qoder provider", () => {
const messages = [
{ role: "user", content: "hello" },
{ role: "assistant", tool_calls: [{ id: "call_1" }] },
];

const result = injectEmptyReasoningContentForToolCalls(
messages,
"qoder",
"kimi-k2-thinking"
) as Array<{ reasoning_content?: string }>;

assert.equal(result[1].reasoning_content, "");
});

test("injectEmptyReasoningContentForToolCalls supports all reasoning replay providers", () => {
const messages = [
{ role: "user", content: "hello" },
{ role: "assistant", tool_calls: [{ id: "call_1" }] },
];

const reasoningProviders = [
{ provider: "deepseek", model: "deepseek-chat" },
{ provider: "opencode-go", model: "some-model" },
{ provider: "siliconflow", model: "deepseek-r1" },
{ provider: "nebius", model: "qwq-32b" },
{ provider: "deepinfra", model: "deepseek-reasoner" },
{ provider: "sambanova", model: "qwen3-thinking" },
{ provider: "fireworks", model: "glm-5-thinking" },
{ provider: "together", model: "mimo-v2.5" },
{ provider: "xiaomi-mimo", model: "mimo-v2.5-pro" },
];

for (const { provider, model } of reasoningProviders) {
const result = injectEmptyReasoningContentForToolCalls(
messages,
provider,
model
) as Array<{ reasoning_content?: string }>;

assert.equal(result[1].reasoning_content, "", `should inject reasoning_content for ${provider}/${model}`);
}
});

test("injectEmptyReasoningContentForToolCalls skips non-reasoning models", () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This test shares the same name as the one defined on line 134. Duplicate test names can lead to confusing test reports and make it harder to identify which specific test failed. Consider using a more descriptive name for this comprehensive test case.

test("injectEmptyReasoningContentForToolCalls skips various non-reasoning models and providers", () => {

const messages = [{ role: "assistant", tool_calls: [{ id: "call_1" }] }];

const nonReasoningModels = [
{ provider: "openai", model: "gpt-4" },
{ provider: "anthropic", model: "claude-sonnet-4" },
{ provider: "google", model: "gemini-pro" },
{ provider: "groq", model: "llama-3" },
];

for (const { provider, model } of nonReasoningModels) {
const result = injectEmptyReasoningContentForToolCalls(
messages,
provider,
model
) as Array<{ reasoning_content?: string }>;

assert.equal(result[0].reasoning_content, undefined, `should NOT inject reasoning_content for ${provider}/${model}`);
}
});