Skip to content

Commit 80b7dbb

Browse files
author
Sagid Magomedov
committed
test(tag-matcher): add tests for unmatched closing tags
Add two regression tests that verify depth never goes negative: 1. stray closer with no opener "final</think>text" → stays text 2. duplicate closer after a proper close "<think>thinking</think>final</think>text" → second </think> stays text Both cases ensure we only decrement depth and pop activeTagNames when depth > 0, preventing underflow and treating the extra tag as plain text.
1 parent 17784d3 commit 80b7dbb

1 file changed

Lines changed: 49 additions & 0 deletions

File tree

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

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,55 @@ describe("OpenAiHandler", () => {
685685
])
686686
})
687687

688+
it("should treat stray closing tag as plain text when no tag is open", async () => {
689+
mockCreate.mockImplementationOnce(() => ({
690+
[Symbol.asyncIterator]: () => ({
691+
next: vi
692+
.fn()
693+
.mockResolvedValueOnce({
694+
done: false,
695+
value: { choices: [{ delta: { content: "final</think>text" } }] },
696+
})
697+
.mockResolvedValueOnce({ done: true }),
698+
}),
699+
}))
700+
701+
const stream = handler.createMessage(systemPrompt, messages)
702+
const chunks: any[] = []
703+
for await (const chunk of stream) {
704+
chunks.push(chunk)
705+
}
706+
707+
expect(chunks).toEqual([{ type: "text", text: "final</think>text" }])
708+
})
709+
710+
it("should treat extra closing tag after a closed block as plain text", async () => {
711+
mockCreate.mockImplementationOnce(() => ({
712+
[Symbol.asyncIterator]: () => ({
713+
next: vi
714+
.fn()
715+
.mockResolvedValueOnce({
716+
done: false,
717+
value: {
718+
choices: [{ delta: { content: "<think>thinking</think>final</think>text" } }],
719+
},
720+
})
721+
.mockResolvedValueOnce({ done: true }),
722+
}),
723+
}))
724+
725+
const stream = handler.createMessage(systemPrompt, messages)
726+
const chunks: any[] = []
727+
for await (const chunk of stream) {
728+
chunks.push(chunk)
729+
}
730+
731+
expect(chunks).toEqual([
732+
{ type: "reasoning", text: "thinking" },
733+
{ type: "text", text: "final</think>text" },
734+
])
735+
})
736+
688737
it("should handle <think> tags that start at beginning of stream", async () => {
689738
mockCreate.mockImplementationOnce(() => ({
690739
[Symbol.asyncIterator]: () => ({

0 commit comments

Comments
 (0)