Skip to content

Commit 3d4016f

Browse files
pescnclaude
andauthored
fix(adapters): forward extra_body params and return reasoning_content properly (#75)
* fix(adapters): forward extra_body params and return reasoning_content properly Request adapters had a KNOWN_FIELDS set that excluded recognized fields (e.g. response_format, presence_penalty, seed) from extraParams passthrough, but those fields were never mapped in parse() either — causing them to be silently dropped when forwarding to upstream providers. Rename to MAPPED_FIELDS containing only fields actually consumed by parse(), so all other fields flow through extraParams to the upstream provider. The response adapter for OpenAI Chat wrapped reasoning_content in <think> tags inside the content field. This was originally only used for DB logging, but was incorrectly carried over into client response serialization during the adapter refactor (09a0ce9). Restore the original behavior: return reasoning_content as a separate field for client responses, keep <think> wrapping only for database storage. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: combine extractTextContent and extractReasoningContent into single pass Merge two separate functions that iterated over the same content blocks array into a single extractContent() function that returns both text and reasoning in one pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 8a145b3 commit 3d4016f

4 files changed

Lines changed: 27 additions & 36 deletions

File tree

backend/src/adapters/request/anthropic.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,12 @@ interface AnthropicRequest {
7070
}
7171

7272
// =============================================================================
73-
// Known Fields (for extracting extra body)
73+
// Mapped Fields (fields explicitly extracted by parse(), excluded from extraParams)
74+
// All other fields (e.g. metadata) are passed through via extraParams
75+
// to the upstream provider.
7476
// =============================================================================
7577

76-
const KNOWN_FIELDS = new Set([
78+
const MAPPED_FIELDS = new Set([
7779
"model",
7880
"messages",
7981
"system",
@@ -85,7 +87,6 @@ const KNOWN_FIELDS = new Set([
8587
"stop_sequences",
8688
"tools",
8789
"tool_choice",
88-
"metadata",
8990
]);
9091

9192
// =============================================================================
@@ -343,7 +344,7 @@ export const anthropicRequestAdapter: RequestAdapter<AnthropicRequest> = {
343344
let hasExtra = false;
344345

345346
for (const [key, value] of Object.entries(body)) {
346-
if (!KNOWN_FIELDS.has(key)) {
347+
if (!MAPPED_FIELDS.has(key)) {
347348
extra[key] = value;
348349
hasExtra = true;
349350
}

backend/src/adapters/request/openai-chat.ts

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -112,30 +112,22 @@ interface OpenAIChatRequest {
112112
}
113113

114114
// =============================================================================
115-
// Known Fields (for extracting extra body)
115+
// Mapped Fields (fields explicitly extracted by parse(), excluded from extraParams)
116+
// All other fields (e.g. response_format, presence_penalty, seed, etc.)
117+
// are passed through via extraParams to the upstream provider.
116118
// =============================================================================
117119

118-
const KNOWN_FIELDS = new Set([
120+
const MAPPED_FIELDS = new Set([
119121
"model",
120122
"messages",
121123
"max_tokens",
122124
"max_completion_tokens",
123125
"temperature",
124126
"top_p",
125-
"n",
126127
"stream",
127-
"stream_options",
128128
"stop",
129129
"tools",
130130
"tool_choice",
131-
"presence_penalty",
132-
"frequency_penalty",
133-
"logit_bias",
134-
"logprobs",
135-
"top_logprobs",
136-
"user",
137-
"seed",
138-
"response_format",
139131
]);
140132

141133
// =============================================================================
@@ -340,7 +332,7 @@ export const openaiChatRequestAdapter: RequestAdapter<OpenAIChatRequest> = {
340332
let hasExtra = false;
341333

342334
for (const [key, value] of Object.entries(body)) {
343-
if (!KNOWN_FIELDS.has(key)) {
335+
if (!MAPPED_FIELDS.has(key)) {
344336
extra[key] = value;
345337
hasExtra = true;
346338
}

backend/src/adapters/request/openai-response.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -109,24 +109,21 @@ interface ResponseApiRequest {
109109
}
110110

111111
// =============================================================================
112-
// Known Fields (for extracting extra body)
112+
// Mapped Fields (fields explicitly extracted by parse(), excluded from extraParams)
113+
// All other fields (e.g. modalities, parallel_tool_calls, store, metadata, etc.)
114+
// are passed through via extraParams to the upstream provider.
113115
// =============================================================================
114116

115-
const KNOWN_FIELDS = new Set([
117+
const MAPPED_FIELDS = new Set([
116118
"model",
117119
"input",
118120
"instructions",
119-
"modalities",
120121
"max_output_tokens",
121122
"temperature",
122123
"top_p",
123124
"stream",
124125
"tools",
125126
"tool_choice",
126-
"parallel_tool_calls",
127-
"previous_response_id",
128-
"store",
129-
"metadata",
130127
]);
131128

132129
// =============================================================================
@@ -298,7 +295,7 @@ export const openaiResponseRequestAdapter: RequestAdapter<ResponseApiRequest> =
298295
let hasExtra = false;
299296

300297
for (const [key, value] of Object.entries(body)) {
301-
if (!KNOWN_FIELDS.has(key)) {
298+
if (!MAPPED_FIELDS.has(key)) {
302299
extra[key] = value;
303300
hasExtra = true;
304301
}

backend/src/adapters/response/openai-chat.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ interface OpenAIChatChoice {
3535
interface OpenAIChatMessage {
3636
role: "assistant";
3737
content: string | null;
38+
reasoning_content?: string | null;
3839
tool_calls?: OpenAIToolCall[];
3940
refusal?: string | null;
4041
}
@@ -113,9 +114,11 @@ function convertStopReason(stopReason: StopReason): string | null {
113114
}
114115

115116
/**
116-
* Extract text content from internal content blocks
117+
* Extract text and reasoning content from internal content blocks
117118
*/
118-
function extractTextContent(content: InternalContentBlock[]): string {
119+
function extractContent(
120+
content: InternalContentBlock[],
121+
): { text: string; reasoning?: string } {
119122
const textParts: string[] = [];
120123
const thinkingParts: string[] = [];
121124

@@ -127,14 +130,10 @@ function extractTextContent(content: InternalContentBlock[]): string {
127130
}
128131
}
129132

130-
// Prepend thinking content wrapped in <think> tags if present
131-
let result = "";
132-
if (thinkingParts.length > 0) {
133-
result += `<think>${thinkingParts.join("")}</think>\n`;
134-
}
135-
result += textParts.join("");
136-
137-
return result;
133+
return {
134+
text: textParts.join(""),
135+
reasoning: thinkingParts.length > 0 ? thinkingParts.join("") : undefined,
136+
};
138137
}
139138

140139
/**
@@ -168,7 +167,8 @@ export const openaiChatResponseAdapter: ResponseAdapter<OpenAIChatCompletion> =
168167
format: "openai-chat",
169168

170169
serialize(response: InternalResponse): OpenAIChatCompletion {
171-
const content = extractTextContent(response.content);
170+
const { text: content, reasoning: reasoningContent } =
171+
extractContent(response.content);
172172
const toolCalls = convertToolCalls(response.content);
173173

174174
return {
@@ -182,6 +182,7 @@ export const openaiChatResponseAdapter: ResponseAdapter<OpenAIChatCompletion> =
182182
message: {
183183
role: "assistant",
184184
content: content || null,
185+
...(reasoningContent !== undefined && { reasoning_content: reasoningContent }),
185186
tool_calls: toolCalls,
186187
},
187188
finish_reason: convertStopReason(response.stopReason),

0 commit comments

Comments
 (0)