* If `s` ends with a dangling `<…` that has no closing `>` yet AND could still grow into a * suppressible marker (a thinking/tool open tag, or a stray closer), cut it. This is the * stateless analog of StreamDisplayFilter's tail-buffering: without it, a partial * tag like `<thinki` would
(s: string)
| 63 | * too (it resolves on the next frame). |
| 64 | */ |
| 65 | function trimTrailingPartialTag(s: string): string { |
| 66 | const lt = s.lastIndexOf('<'); |
| 67 | if (lt === -1) return s; |
| 68 | const tail = s.slice(lt); |
| 69 | if (tail.includes('>')) return s; // complete tag — handled by the block/closer passes |
| 70 | const lower = tail.toLowerCase(); |
| 71 | const openForms = ['<thinking>', '<think>', '<reasoning>', '<reflection>', |
| 72 | '<function_call>', '<tool_call>', '<tool_use>', '<tool>']; |
| 73 | const couldGrow = |
| 74 | openForms.some(t => t.startsWith(lower)) || |
| 75 | '<function='.startsWith(lower) || |
| 76 | /^<function=[a-z_][\w.-]*$/i.test(tail) || |
| 77 | STRAY_CLOSERS.some(c => c.startsWith(lower)); |
| 78 | return couldGrow ? s.slice(0, lt).trimEnd() : s; |
| 79 | } |
| 80 | |
| 81 | /** |
| 82 | * Strip thinking for display from a FULL accumulated string (not a stream). |
no outgoing calls
no test coverage detected