import { afterEach, describe, expect, it } from "bun:test";
import { getBundledModel } from "../src/models";
import { streamOpenAICompletions } from "../src/providers/openai-completions";
import type { Context, Model } from "../src/types";
const originalFetch = global.fetch;
afterEach(() => {
global.fetch = originalFetch;
});
function createSseResponse(events: unknown[]): Response {
const payload = `${events
.map(event => `data: ${typeof event === "string" ? event : JSON.stringify(event)}`)
.join("\n\n")}\n\n`;
return new Response(payload, {
status: 200,
headers: { "content-type": "text/event-stream" },
});
}
function createMockFetch(events: unknown[]): typeof fetch {
async function mockFetch(_input: string | URL | Request, _init?: RequestInit): Promise<Response> {
return createSseResponse(events);
}
return Object.assign(mockFetch, { preconnect: originalFetch.preconnect });
}
function baseContext(): Context {
return {
messages: [
{
role: "user",
content: "hello",
timestamp: Date.now(),
},
],
};
}
describe("issue #911 - Mistral Medium 3.5 array content parts", () => {
const model: Model<"openai-completions"> = {
...getBundledModel("mistral", "mistral-medium-2604"),
};
it("normalizes array-of-parts delta.content into the assembled text without [object Object]", async () => {
global.fetch = createMockFetch([
{
id: "chatcmpl-mistral-1",
object: "chat.completion.chunk",
created: 0,
model: model.id,
choices: [
{
index: 0,
delta: { content: [{ type: "text", text: "Hello" }] },
},
],
},
{
id: "chatcmpl-mistral-1",
object: "chat.completion.chunk",
created: 0,
model: model.id,
choices: [
{
index: 0,
delta: { content: [{ type: "text", text: ", world" }] },
},
],
},
{
id: "chatcmpl-mistral-1",
object: "chat.completion.chunk",
created: 0,
model: model.id,
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
},
"[DONE]",
]);
const result = await streamOpenAICompletions(model, baseContext(), { apiKey: "test-key" }).result();
const text = result.content
.filter(b => b.type === "text")
.map(b => (b as { text: string }).text)
.join("");
expect(text).not.toContain("[object Object]");
expect(text).toBe("Hello, world");
});
it("handles mixed string and array-of-parts content shapes within one stream", async () => {
global.fetch = createMockFetch([
{
id: "chatcmpl-mistral-2",
object: "chat.completion.chunk",
created: 0,
model: model.id,
choices: [{ index: 0, delta: { content: "plain " } }],
},
{
id: "chatcmpl-mistral-2",
object: "chat.completion.chunk",
created: 0,
model: model.id,
choices: [
{
index: 0,
delta: {
content: [
{ type: "text", text: "and " },
{ type: "text", text: "typed" },
],
},
},
],
},
{
id: "chatcmpl-mistral-2",
object: "chat.completion.chunk",
created: 0,
model: model.id,
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
},
"[DONE]",
]);
const result = await streamOpenAICompletions(model, baseContext(), { apiKey: "test-key" }).result();
const text = result.content
.filter(b => b.type === "text")
.map(b => (b as { text: string }).text)
.join("");
expect(text).not.toContain("[object Object]");
expect(text).toBe("plain and typed");
});
});