fix: address latest coderabbit review findings

This commit is contained in:
Cita
2026-03-23 15:24:00 +08:00
parent 426d9a538d
commit 764b9aa0e6
21 changed files with 472 additions and 46 deletions
@@ -75,6 +75,48 @@ describe('responses conversion single source of truth', () => {
},
]);
});
it('falls back to non-empty text and image sources when earlier compatibility fields are blank', () => {
const normalized = normalizeResponsesInputForCompatibility([
{
role: 'assistant',
content: '',
text: 'done',
},
{
role: 'user',
content: [
{ type: 'text', text: ' ', content: 'hello' },
{ type: 'image_url', image_url: ' ', url: 'https://example.com/image.png' },
],
},
]);
expect(normalized).toEqual([
expect.objectContaining({
type: 'message',
role: 'assistant',
text: 'done',
content: [{ type: 'output_text', text: 'done' }],
}),
expect.objectContaining({
type: 'message',
role: 'user',
content: [
expect.objectContaining({
type: 'input_text',
text: 'hello',
content: 'hello',
}),
expect.objectContaining({
type: 'input_image',
image_url: 'https://example.com/image.png',
url: 'https://example.com/image.png',
}),
],
}),
]);
});
});
describe('sanitizeResponsesBodyForProxy', () => {
@@ -8,6 +8,25 @@ function asTrimmedString(value: unknown): string {
return typeof value === 'string' ? value.trim() : '';
}
function firstNonEmptyTrimmedString(...values: unknown[]): string {
for (const value of values) {
const normalized = asTrimmedString(value);
if (normalized) return normalized;
}
return '';
}
function firstMeaningfulValue(...values: unknown[]): unknown {
for (const value of values) {
if (typeof value === 'string') {
if (value.trim()) return value;
continue;
}
if (value !== undefined && value !== null) return value;
}
return undefined;
}
function toTextBlockType(role: string): 'input_text' | 'output_text' {
return role === 'assistant' ? 'output_text' : 'input_text';
}
@@ -47,12 +66,12 @@ function normalizeResponsesContentItem(
const type = asTrimmedString(item.type).toLowerCase();
if (!type) {
const text = asTrimmedString(item.text ?? item.content ?? item.output_text);
const text = firstNonEmptyTrimmedString(item.text, item.content, item.output_text);
return text ? { type: toTextBlockType(role), text } : null;
}
if (type === 'input_text' || type === 'output_text' || type === 'text') {
const text = asTrimmedString(item.text ?? item.content ?? item.output_text);
const text = firstNonEmptyTrimmedString(item.text, item.content, item.output_text);
if (!text) return null;
return {
...item,
@@ -62,7 +81,7 @@ function normalizeResponsesContentItem(
}
if (type === 'input_image' || type === 'image_url') {
const imageUrl = normalizeImageUrlValue(item.image_url ?? item.url);
const imageUrl = normalizeImageUrlValue(item.image_url) ?? normalizeImageUrlValue(item.url);
if (!imageUrl) return null;
return {
...item,
@@ -124,7 +143,10 @@ export function normalizeResponsesMessageItem(item: Record<string, unknown>): Re
}
const role = asTrimmedString(item.role).toLowerCase() || 'user';
const normalizedContent = normalizeResponsesMessageContent(item.content ?? item.text, role);
const normalizedContent = normalizeResponsesMessageContent(
firstMeaningfulValue(item.content, item.text),
role,
);
if (type === 'message') {
return {
@@ -143,8 +165,8 @@ export function normalizeResponsesMessageItem(item: Record<string, unknown>): Re
};
}
if (typeof item.content === 'string') {
const text = item.content.trim();
if (typeof item.content === 'string' || typeof item.text === 'string') {
const text = firstNonEmptyTrimmedString(item.content, item.text);
return text ? toResponsesInputMessageFromText(text) : item;
}