Skip to content

Commit 34fabea

Browse files
test(mimo): add advanced streaming scenario tests for Mimo provider
Rebased onto main. The completePrompt coverage from the original PR is now provided by the merged Zoo-Code-Org#210, so this focuses on its net-new contribution: 4 advanced streaming resilience tests (multi-chunk text concatenation, interleaved reasoning + tool calls, missing usage in final chunk, and zero-valued cache tokens). 49 tests pass.
1 parent 3bd1a80 commit 34fabea

1 file changed

Lines changed: 175 additions & 0 deletions

File tree

src/api/providers/__tests__/mimo.spec.ts

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -950,4 +950,179 @@ describe("MimoHandler", () => {
950950
expect(params.model).toBe("mimo-v2.5")
951951
})
952952
})
953+
954+
describe("advanced streaming scenarios", () => {
955+
it("should handle stream with multiple text chunks concatenated", async () => {
956+
mockCreate.mockImplementationOnce(async () => ({
957+
[Symbol.asyncIterator]: async function* () {
958+
yield {
959+
choices: [{ delta: { content: "Hello" }, index: 0 }],
960+
usage: null,
961+
}
962+
yield {
963+
choices: [{ delta: { content: " world" }, index: 0 }],
964+
usage: null,
965+
}
966+
yield {
967+
choices: [{ delta: { content: "!" }, index: 0 }],
968+
usage: null,
969+
}
970+
yield {
971+
choices: [{ delta: {}, index: 0, finish_reason: "stop" }],
972+
usage: { prompt_tokens: 10, completion_tokens: 3, total_tokens: 13 },
973+
}
974+
},
975+
}))
976+
977+
const messages: Anthropic.Messages.MessageParam[] = [
978+
{ role: "user", content: [{ type: "text", text: "Hi" }] },
979+
]
980+
981+
const chunks: any[] = []
982+
const stream = handler.createMessage("System prompt", messages)
983+
for await (const chunk of stream) {
984+
chunks.push(chunk)
985+
}
986+
987+
const textChunks = chunks.filter((c) => c.type === "text")
988+
expect(textChunks).toHaveLength(3)
989+
expect(textChunks.map((c: any) => c.text).join("")).toBe("Hello world!")
990+
})
991+
992+
it("should handle stream with both reasoning and tool calls", async () => {
993+
mockCreate.mockImplementationOnce(async () => ({
994+
[Symbol.asyncIterator]: async function* () {
995+
yield {
996+
choices: [{ delta: { reasoning_content: "Let me think" }, index: 0 }],
997+
usage: null,
998+
}
999+
yield {
1000+
choices: [{ delta: { reasoning_content: " about this" }, index: 0 }],
1001+
usage: null,
1002+
}
1003+
yield {
1004+
choices: [{ delta: { content: "I'll read it" }, index: 0 }],
1005+
usage: null,
1006+
}
1007+
yield {
1008+
choices: [
1009+
{
1010+
delta: {
1011+
tool_calls: [
1012+
{
1013+
index: 0,
1014+
id: "call_read",
1015+
function: { name: "read_file", arguments: '{"path":"test.ts"}' },
1016+
},
1017+
],
1018+
},
1019+
index: 0,
1020+
},
1021+
],
1022+
usage: null,
1023+
}
1024+
yield {
1025+
choices: [{ delta: {}, index: 0, finish_reason: "tool_calls" }],
1026+
usage: { prompt_tokens: 20, completion_tokens: 15, total_tokens: 35 },
1027+
}
1028+
},
1029+
}))
1030+
1031+
const messages: Anthropic.Messages.MessageParam[] = [
1032+
{ role: "user", content: [{ type: "text", text: "Read test.ts" }] },
1033+
]
1034+
1035+
const chunks: any[] = []
1036+
const stream = handler.createMessage("System prompt", messages)
1037+
for await (const chunk of stream) {
1038+
chunks.push(chunk)
1039+
}
1040+
1041+
const reasoningChunks = chunks.filter((c) => c.type === "reasoning")
1042+
expect(reasoningChunks).toHaveLength(2)
1043+
expect(reasoningChunks.map((c: any) => c.text).join("")).toBe("Let me think about this")
1044+
1045+
const textChunks = chunks.filter((c) => c.type === "text")
1046+
expect(textChunks).toHaveLength(1)
1047+
expect(textChunks[0].text).toBe("I'll read it")
1048+
1049+
const toolChunks = chunks.filter((c) => c.type === "tool_call_partial")
1050+
expect(toolChunks).toHaveLength(1)
1051+
expect(toolChunks[0].id).toBe("call_read")
1052+
expect(toolChunks[0].name).toBe("read_file")
1053+
})
1054+
1055+
it("should handle stream with no usage in final chunk", async () => {
1056+
mockCreate.mockImplementationOnce(async () => ({
1057+
[Symbol.asyncIterator]: async function* () {
1058+
yield {
1059+
choices: [{ delta: { content: "Done" }, index: 0 }],
1060+
usage: null,
1061+
}
1062+
yield {
1063+
choices: [{ delta: {}, index: 0, finish_reason: "stop" }],
1064+
usage: null,
1065+
}
1066+
},
1067+
}))
1068+
1069+
const messages: Anthropic.Messages.MessageParam[] = [
1070+
{ role: "user", content: [{ type: "text", text: "Hello" }] },
1071+
]
1072+
1073+
const chunks: any[] = []
1074+
const stream = handler.createMessage("System prompt", messages)
1075+
for await (const chunk of stream) {
1076+
chunks.push(chunk)
1077+
}
1078+
1079+
const usageChunks = chunks.filter((c) => c.type === "usage")
1080+
expect(usageChunks).toHaveLength(0)
1081+
1082+
const textChunks = chunks.filter((c) => c.type === "text")
1083+
expect(textChunks).toHaveLength(1)
1084+
expect(textChunks[0].text).toBe("Done")
1085+
})
1086+
1087+
it("should handle stream with zero cache tokens in usage", async () => {
1088+
mockCreate.mockImplementationOnce(async () => ({
1089+
[Symbol.asyncIterator]: async function* () {
1090+
yield {
1091+
choices: [{ delta: { content: "Hi" }, index: 0 }],
1092+
usage: null,
1093+
}
1094+
yield {
1095+
choices: [{ delta: {}, index: 0, finish_reason: "stop" }],
1096+
usage: {
1097+
prompt_tokens: 50,
1098+
completion_tokens: 10,
1099+
total_tokens: 60,
1100+
prompt_tokens_details: {
1101+
cache_write_tokens: 0,
1102+
cached_tokens: 0,
1103+
},
1104+
},
1105+
}
1106+
},
1107+
}))
1108+
1109+
const messages: Anthropic.Messages.MessageParam[] = [
1110+
{ role: "user", content: [{ type: "text", text: "Hello" }] },
1111+
]
1112+
1113+
const chunks: any[] = []
1114+
const stream = handler.createMessage("System prompt", messages)
1115+
for await (const chunk of stream) {
1116+
chunks.push(chunk)
1117+
}
1118+
1119+
const usageChunks = chunks.filter((c) => c.type === "usage")
1120+
expect(usageChunks).toHaveLength(1)
1121+
expect(usageChunks[0].inputTokens).toBe(50)
1122+
expect(usageChunks[0].outputTokens).toBe(10)
1123+
// Handler uses `|| undefined` so zero-valued cache tokens are omitted
1124+
expect(usageChunks[0].cacheWriteTokens).toBeUndefined()
1125+
expect(usageChunks[0].cacheReadTokens).toBeUndefined()
1126+
})
1127+
})
9531128
})

0 commit comments

Comments
 (0)