refactor(open-sse): add reasoningDelta helper, dedup thinking deltas (B4)
Centralize the reasoning_content delta shape (optional assistant role) used by claude/gemini/kiro/codex/commandcode response translators. Keeps the cross-format convention consistent for future translators. Output byte-for-byte identical; golden + gate clean. Ollama left as-is (mutates existing delta object). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
6
open-sse/translator/helpers/reasoningHelper.js
Normal file
6
open-sse/translator/helpers/reasoningHelper.js
Normal file
@@ -0,0 +1,6 @@
|
||||
// Build OpenAI delta carrying reasoning_content (optional leading assistant role)
|
||||
export function reasoningDelta(text, withRole = false) {
|
||||
return withRole
|
||||
? { role: "assistant", reasoning_content: text }
|
||||
: { reasoning_content: text };
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { register } from "../index.js";
|
||||
import { FORMATS } from "../formats.js";
|
||||
import { buildChunk } from "../helpers/chunkBuilder.js";
|
||||
import { buildUsage } from "../helpers/usageHelper.js";
|
||||
import { reasoningDelta } from "../helpers/reasoningHelper.js";
|
||||
|
||||
// Create OpenAI chunk helper
|
||||
function createChunk(state, delta, finishReason = null) {
|
||||
@@ -67,7 +68,7 @@ export function claudeToOpenAIResponse(chunk, state) {
|
||||
if (delta?.type === "text_delta" && delta.text) {
|
||||
results.push(createChunk(state, { content: delta.text }));
|
||||
} else if (delta?.type === "thinking_delta" && delta.thinking) {
|
||||
results.push(createChunk(state, { reasoning_content: delta.thinking }));
|
||||
results.push(createChunk(state, reasoningDelta(delta.thinking)));
|
||||
} else if (delta?.type === "input_json_delta" && delta.partial_json) {
|
||||
const toolCall = state.toolCalls.get(chunk.index);
|
||||
if (toolCall) {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
import { register } from "../index.js";
|
||||
import { FORMATS } from "../formats.js";
|
||||
import { buildChunk } from "../helpers/chunkBuilder.js";
|
||||
import { reasoningDelta } from "../helpers/reasoningHelper.js";
|
||||
|
||||
function ensureState(state, model) {
|
||||
if (!state.responseId) {
|
||||
@@ -96,9 +97,7 @@ export function convertCommandCodeToOpenAI(chunk, state) {
|
||||
const text = event.text || "";
|
||||
if (!text) break;
|
||||
// Map reasoning to OpenAI "reasoning_content" field (used by deepseek-reasoner-style clients).
|
||||
const delta = state.chunkIndex === 0
|
||||
? { role: "assistant", reasoning_content: text }
|
||||
: { reasoning_content: text };
|
||||
const delta = reasoningDelta(text, state.chunkIndex === 0);
|
||||
state.chunkIndex++;
|
||||
out.push(makeChunk(state, delta));
|
||||
break;
|
||||
|
||||
@@ -2,6 +2,7 @@ import { register } from "../index.js";
|
||||
import { FORMATS } from "../formats.js";
|
||||
import { buildChunk } from "../helpers/chunkBuilder.js";
|
||||
import { buildUsage } from "../helpers/usageHelper.js";
|
||||
import { reasoningDelta } from "../helpers/reasoningHelper.js";
|
||||
|
||||
// Build chunk meta for current gemini state
|
||||
function chunkMeta(state) {
|
||||
@@ -42,7 +43,7 @@ export function geminiToOpenAIResponse(chunk, state) {
|
||||
if (hasTextContent) {
|
||||
results.push(buildChunk(
|
||||
chunkMeta(state),
|
||||
isThought ? { reasoning_content: part.text } : { content: part.text },
|
||||
isThought ? reasoningDelta(part.text) : { content: part.text },
|
||||
null
|
||||
));
|
||||
}
|
||||
@@ -78,7 +79,7 @@ export function geminiToOpenAIResponse(chunk, state) {
|
||||
if (part.text !== undefined && part.text !== "") {
|
||||
results.push(buildChunk(
|
||||
chunkMeta(state),
|
||||
isThought ? { reasoning_content: part.text } : { content: part.text },
|
||||
isThought ? reasoningDelta(part.text) : { content: part.text },
|
||||
null
|
||||
));
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { register } from "../index.js";
|
||||
import { FORMATS } from "../formats.js";
|
||||
import { buildChunk } from "../helpers/chunkBuilder.js";
|
||||
import { fallbackToolCallId } from "../helpers/toolCallHelper.js";
|
||||
import { reasoningDelta } from "../helpers/reasoningHelper.js";
|
||||
|
||||
// Build chunk meta for current kiro state
|
||||
function chunkMeta(state) {
|
||||
@@ -94,10 +95,7 @@ export function convertKiroToOpenAI(chunk, state) {
|
||||
: (reasoning.text || reasoning.content || data.content || "");
|
||||
if (!content) return null;
|
||||
|
||||
const openaiChunk = buildChunk(chunkMeta(state), {
|
||||
...(state.chunkIndex === 0 ? { role: "assistant" } : {}),
|
||||
reasoning_content: content
|
||||
}, null);
|
||||
const openaiChunk = buildChunk(chunkMeta(state), reasoningDelta(content, state.chunkIndex === 0), null);
|
||||
|
||||
state.chunkIndex++;
|
||||
return openaiChunk;
|
||||
|
||||
@@ -7,6 +7,7 @@ import { FORMATS } from "../formats.js";
|
||||
import { buildChunk } from "../helpers/chunkBuilder.js";
|
||||
import { buildUsage } from "../helpers/usageHelper.js";
|
||||
import { fallbackToolCallId } from "../helpers/toolCallHelper.js";
|
||||
import { reasoningDelta } from "../helpers/reasoningHelper.js";
|
||||
|
||||
/**
|
||||
* Translate OpenAI chunk to Responses API events
|
||||
@@ -519,7 +520,7 @@ export function openaiResponsesToOpenAIResponse(chunk, state) {
|
||||
if (!delta) return null;
|
||||
return buildChunk(
|
||||
{ id: state.chatId, created: state.created, model: state.model || "unknown" },
|
||||
{ reasoning_content: delta }
|
||||
reasoningDelta(delta)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user