Skip to content

Commit 97fd038

Browse files
Jasmine ZhangJasmine Zhang
authored andcommitted
fix(openai): surface chat completions refusal text
1 parent 5fd5bf2 commit 97fd038

2 files changed

Lines changed: 83 additions & 0 deletions

File tree

src/agents/openai-transport-stream.test.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2728,6 +2728,86 @@ describe("openai transport stream", () => {
27282728
expect(output.responseId).toBe("resp_azure_text");
27292729
});
27302730

2731+
it("surfaces refusal-only OpenAI-compatible delta chunks as visible text", async () => {
2732+
const model = {
2733+
id: "glm-5",
2734+
name: "GLM-5",
2735+
api: "openai-completions",
2736+
provider: "vllm",
2737+
baseUrl: "http://localhost:8000/v1",
2738+
reasoning: false,
2739+
input: ["text"],
2740+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
2741+
contextWindow: 128000,
2742+
maxTokens: 4096,
2743+
} satisfies Model<"openai-completions">;
2744+
const output = createAssistantOutput(model);
2745+
2746+
async function* mockStream() {
2747+
yield {
2748+
id: "chatcmpl-vllm",
2749+
object: "chat.completion.chunk" as const,
2750+
created: 1775425651,
2751+
model: "glm-5",
2752+
choices: [
2753+
{
2754+
index: 0,
2755+
delta: { role: "assistant" as const, refusal: "I can't help with that." },
2756+
logprobs: null,
2757+
finish_reason: "stop" as const,
2758+
},
2759+
],
2760+
};
2761+
}
2762+
2763+
await testing.processOpenAICompletionsStream(mockStream(), output, model, { push() {} });
2764+
2765+
expect(output.content).toStrictEqual([{ type: "text", text: "I can't help with that." }]);
2766+
expect(output.stopReason).toBe("stop");
2767+
});
2768+
2769+
it("surfaces refusal-only aggregated OpenAI-compatible messages as visible text", async () => {
2770+
const model = {
2771+
id: "glm-5",
2772+
name: "GLM-5",
2773+
api: "openai-completions",
2774+
provider: "vllm",
2775+
baseUrl: "http://localhost:8000/v1",
2776+
reasoning: false,
2777+
input: ["text"],
2778+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
2779+
contextWindow: 128000,
2780+
maxTokens: 4096,
2781+
} satisfies Model<"openai-completions">;
2782+
const output = createAssistantOutput(model);
2783+
2784+
async function* mockStream() {
2785+
yield {
2786+
id: "chatcmpl-vllm",
2787+
object: "chat.completion" as const,
2788+
created: 1775425651,
2789+
model: "glm-5",
2790+
choices: [
2791+
{
2792+
index: 0,
2793+
message: {
2794+
role: "assistant" as const,
2795+
content: null,
2796+
refusal: "I can't help with that.",
2797+
},
2798+
logprobs: null,
2799+
finish_reason: "stop" as const,
2800+
},
2801+
],
2802+
};
2803+
}
2804+
2805+
await testing.processOpenAICompletionsStream(mockStream(), output, model, { push() {} });
2806+
2807+
expect(output.content).toStrictEqual([{ type: "text", text: "I can't help with that." }]);
2808+
expect(output.stopReason).toBe("stop");
2809+
});
2810+
27312811
it("skips null and non-object OpenAI-compatible stream chunks", async () => {
27322812
const model = {
27332813
id: "glm-5",

src/agents/openai-transport-stream.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3166,6 +3166,9 @@ async function processOpenAICompletionsStream(
31663166
}
31673167
}
31683168
}
3169+
if (typeof choiceDelta.refusal === "string" && choiceDelta.refusal.length > 0) {
3170+
appendFilteredVisibleTextDelta(choiceDelta.refusal);
3171+
}
31693172
for (const reasoningDelta of reasoningDeltas) {
31703173
if (reasoningDelta.kind === "thinking" && !emitReasoning) {
31713174
continue;

0 commit comments

Comments
 (0)