Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions open-sse/services/combo/validateQuality.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,28 @@ export function toRetryAfterDisplayValue(value: ComboRetryAfter): string | Date
return new Date(value);
}

function responsesApiOutputHasContent(output: unknown): boolean {
return (
Array.isArray(output) &&
output.some((item) => {
if (!item || typeof item !== "object") return false;
const record = item as Record<string, unknown>;
if (record.type !== "message") return Boolean(record.type);
const content = record.content;
return (
Array.isArray(content) &&
content.some(
(part) =>
!!part &&
typeof part === "object" &&
typeof (part as Record<string, unknown>).text === "string" &&
((part as Record<string, string>).text as string).length > 0
)
);
})
);
}

/**
* Validate that a successful (HTTP 200) non-streaming response actually contains
* meaningful content. Returns { valid: true } or { valid: false, reason }.
Expand Down Expand Up @@ -271,6 +293,22 @@ export async function validateResponseQuality(
}

const choices = json?.choices;
if (json?.object === "response") {
if (!responsesApiOutputHasContent(json.output)) return { valid: false, reason: "empty_choices" };
const status = typeof json.status === "string" ? json.status : "";
if (status && !["completed", "done"].includes(status)) {
return { valid: false, reason: "no_terminal" };
}
return {
valid: true,
clonedResponse: new Response(text, {
status: response.status,
statusText: response.statusText,
headers: response.headers,
}),
};
}

if (!Array.isArray(choices) || choices.length === 0) {
if (json?.output || json?.result || json?.data || json?.response) return { valid: true };
if (json?.error) {
Expand Down
27 changes: 26 additions & 1 deletion tests/unit/validate-response-quality.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import test from "node:test";
import assert from "assert";
import { validateResponseQuality } from "../../open-sse/services/combo";
import { validateResponseQuality } from "../../open-sse/services/combo/validateQuality.ts";

function makeResponse(body: string, contentType = "text/plain") {
return {
Expand All @@ -25,3 +25,28 @@ test("returns valid=false for non-JSON non-SSE text", async () => {
const res = await validateResponseQuality(makeResponse("Hello world"), false, {});
assert.strictEqual(res.valid, false);
});

test("returns valid=false for Responses API bodies with no output items", async () => {
const res = await validateResponseQuality(
makeResponse(JSON.stringify({ object: "response", status: "completed", output: [] }), "application/json"),
false,
{}
);
assert.strictEqual(res.valid, false);
});

test("returns valid=true for Responses API bodies with structural output", async () => {
const res = await validateResponseQuality(
makeResponse(
JSON.stringify({
object: "response",
status: "completed",
output: [{ type: "function_call", name: "lookup", arguments: "{}" }],
}),
"application/json"
),
false,
{}
);
assert.strictEqual(res.valid, true);
});
Loading