mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
fix: translate input_image↔image_url with detail preservation
This commit is contained in:
@@ -112,6 +112,16 @@ export function openaiResponsesToOpenAIRequest(
|
||||
if (contentItem.type === "output_text") {
|
||||
return { type: "text", text: toString(contentItem.text) };
|
||||
}
|
||||
if (contentItem.type === "input_image") {
|
||||
const imgResult: JsonRecord = {
|
||||
type: "image_url",
|
||||
image_url: { url: toString(contentItem.image_url) },
|
||||
};
|
||||
if (contentItem.detail !== undefined) {
|
||||
(imgResult.image_url as JsonRecord).detail = contentItem.detail;
|
||||
}
|
||||
return imgResult;
|
||||
}
|
||||
return contentValue;
|
||||
})
|
||||
: item.content;
|
||||
@@ -288,11 +298,15 @@ export function openaiToOpenAIResponsesRequest(
|
||||
return { type: "input_text", text: toString(contentItem.text) };
|
||||
}
|
||||
if (contentItem.type === "image_url") {
|
||||
const imgUrl = contentItem.image_url as string | { url?: string };
|
||||
return {
|
||||
const imgUrl = contentItem.image_url as string | { url?: string; detail?: string };
|
||||
const imgResult: JsonRecord = {
|
||||
type: "input_image",
|
||||
image_url: typeof imgUrl === "string" ? imgUrl : imgUrl?.url || "",
|
||||
};
|
||||
if (typeof imgUrl === "object" && imgUrl?.detail !== undefined) {
|
||||
imgResult.detail = imgUrl.detail;
|
||||
}
|
||||
return imgResult;
|
||||
}
|
||||
return contentValue;
|
||||
})
|
||||
|
||||
@@ -4,6 +4,9 @@ import assert from "node:assert/strict";
|
||||
const { convertResponsesApiFormat } = await import(
|
||||
"../../open-sse/translator/helpers/responsesApiHelper.ts"
|
||||
);
|
||||
const { openaiResponsesToOpenAIRequest, openaiToOpenAIResponsesRequest } = await import(
|
||||
"../../open-sse/translator/request/openai-responses.ts"
|
||||
);
|
||||
|
||||
test("convertResponsesApiFormat filters orphaned function_call_output items", () => {
|
||||
const body = {
|
||||
@@ -33,3 +36,84 @@ test("convertResponsesApiFormat skips function_call items with empty names", ()
|
||||
const assistantMsgs = result.messages.filter((m) => m.role === "assistant");
|
||||
assert.equal(assistantMsgs.length, 0);
|
||||
});
|
||||
|
||||
test("Responses→Chat: input_image converted to image_url with detail", () => {
|
||||
const body = {
|
||||
model: "gpt-4",
|
||||
input: [
|
||||
{
|
||||
type: "message",
|
||||
role: "user",
|
||||
content: [
|
||||
{ type: "input_text", text: "What is this?" },
|
||||
{ type: "input_image", image_url: "https://example.com/img.png", detail: "high" },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
const result = openaiResponsesToOpenAIRequest(null, body, null, null);
|
||||
const userMsg = result.messages.find((m) => m.role === "user");
|
||||
const imgPart = userMsg.content.find((c) => c.type === "image_url");
|
||||
assert.ok(imgPart, "should have image_url content part");
|
||||
assert.equal(imgPart.image_url.url, "https://example.com/img.png");
|
||||
assert.equal(imgPart.image_url.detail, "high");
|
||||
});
|
||||
|
||||
test("Responses→Chat: input_image without detail omits detail field", () => {
|
||||
const body = {
|
||||
model: "gpt-4",
|
||||
input: [
|
||||
{
|
||||
type: "message",
|
||||
role: "user",
|
||||
content: [{ type: "input_image", image_url: "https://example.com/img.png" }],
|
||||
},
|
||||
],
|
||||
};
|
||||
const result = openaiResponsesToOpenAIRequest(null, body, null, null);
|
||||
const userMsg = result.messages.find((m) => m.role === "user");
|
||||
const imgPart = userMsg.content.find((c) => c.type === "image_url");
|
||||
assert.ok(imgPart);
|
||||
assert.equal(imgPart.image_url.url, "https://example.com/img.png");
|
||||
assert.equal(imgPart.image_url.detail, undefined);
|
||||
});
|
||||
|
||||
test("Chat→Responses: image_url detail preserved as input_image", () => {
|
||||
const body = {
|
||||
model: "gpt-4",
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{ type: "text", text: "Describe" },
|
||||
{ type: "image_url", image_url: { url: "https://example.com/img.png", detail: "low" } },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
const result = openaiToOpenAIResponsesRequest("gpt-4", body, true, null);
|
||||
const userItem = result.input.find((i) => i.type === "message" && i.role === "user");
|
||||
const imgPart = userItem.content.find((c) => c.type === "input_image");
|
||||
assert.ok(imgPart, "should have input_image content part");
|
||||
assert.equal(imgPart.image_url, "https://example.com/img.png");
|
||||
assert.equal(imgPart.detail, "low");
|
||||
});
|
||||
|
||||
test("Chat→Responses: image_url without detail omits detail", () => {
|
||||
const body = {
|
||||
model: "gpt-4",
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{ type: "image_url", image_url: { url: "https://example.com/img.png" } },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
const result = openaiToOpenAIResponsesRequest("gpt-4", body, true, null);
|
||||
const userItem = result.input.find((i) => i.type === "message" && i.role === "user");
|
||||
const imgPart = userItem.content.find((c) => c.type === "input_image");
|
||||
assert.ok(imgPart);
|
||||
assert.equal(imgPart.detail, undefined);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user