Refactor reasoning handling and metadata management in responses

- Updated chat stream tests to clarify reasoning behavior and ensure correct handling of include lists.
- Enhanced responses proxy route to normalize include lists and apply default reasoning includes based on request context.
- Improved transformer metadata handling, ensuring proper serialization and deserialization of metadata records.
- Added tests for preserving reasoning signatures and managing citations in OpenAI-compatible responses.
- Refined the logic for injecting default includes in responses and ensured compatibility with codex surface defaults.
This commit is contained in:
cita
2026-03-08 23:03:33 +08:00
parent 7bd3114ea0
commit 3dcf45851f
12 changed files with 830 additions and 56 deletions
@@ -358,6 +358,41 @@ describe('convertOpenAiBodyToAnthropicMessagesBody', () => {
]);
});
it('preserves top-level reasoning_signature when rebuilding assistant thinking blocks for messages fallback', () => {
const body = convertOpenAiBodyToAnthropicMessagesBody(
{
model: 'gpt-5',
messages: [
{
role: 'assistant',
content: 'final answer',
reasoning_content: 'internal only',
reasoning_signature: 'metapi:anthropic-signature:sig-top-level-1',
},
],
},
'claude-opus-4-6',
false,
);
expect(body.messages).toEqual([
{
role: 'assistant',
content: [
{
type: 'thinking',
thinking: 'internal only',
signature: 'sig-top-level-1',
},
{
type: 'text',
text: 'final answer',
},
],
},
]);
});
it('strips cache_control from thinking blocks and empty text blocks', () => {
const body = convertOpenAiBodyToAnthropicMessagesBody(
{
@@ -301,9 +301,10 @@ function sanitizeAnthropicContentBlock(item: Record<string, unknown>): Record<st
if (type === 'thinking' || type === 'redacted_thinking' || type === 'reasoning') {
const text = asTrimmedString(item.thinking ?? item.text ?? item.content ?? item.data);
if (!text) return null;
const signature = type === 'redacted_thinking' ? undefined : resolveAnthropicThinkingSignature(item);
if (signature === null) return null;
const hasThinkingCarrier = type !== 'redacted_thinking' && signature !== undefined;
if (!text && !hasThinkingCarrier) return null;
const next: Record<string, unknown> = { ...item };
if (type === 'redacted_thinking') {
next.type = 'redacted_thinking';
@@ -772,6 +773,15 @@ export function convertOpenAiBodyToAnthropicMessagesBody(
if (role !== 'assistant') return;
const contentBlocks = convertOpenAiContentToAnthropicBlocks(item.content);
const reasoningCarrier = sanitizeAnthropicContentBlock({
type: 'thinking',
thinking: asTrimmedString(item.reasoning_content ?? item.reasoning),
reasoning_signature: item.reasoning_signature,
signature: item.signature,
});
if (reasoningCarrier) {
contentBlocks.unshift(reasoningCarrier);
}
const rawToolCalls = Array.isArray(item.tool_calls) ? item.tool_calls : [];
for (let index = 0; index < rawToolCalls.length; index += 1) {