Flush 在流结束时调用,把 buffer 中剩余的、未闭合的 也尝试解析一次。 典型场景:模型在末尾输出了 {...} 但忘了写结束标签。
()
| 104 | // Flush 在流结束时调用,把 buffer 中剩余的、未闭合的 <tool_call> 也尝试解析一次。 |
| 105 | // 典型场景:模型在末尾输出了 <tool_call>{...} 但忘了写结束标签。 |
| 106 | func (p *Parser) Flush() (textDelta string, toolCalls []official.ToolCall) { |
| 107 | remaining := p.buffer |
| 108 | p.buffer = "" |
| 109 | if remaining == "" { |
| 110 | return "", nil |
| 111 | } |
| 112 | if p.inside { |
| 113 | // 还在工具块里 —— 尝试解析剩余内容 |
| 114 | if tc := buildToolCallFromRaw(remaining); tc != nil { |
| 115 | toolCalls = append(toolCalls, *tc) |
| 116 | p.emittedCount++ |
| 117 | return "", toolCalls |
| 118 | } |
| 119 | // 解析不出来 —— 把 <tool_call> 标签也作为文本回吐,避免吞掉 |
| 120 | if p.emittedCount == 0 { |
| 121 | return StartTag + remaining, nil |
| 122 | } |
| 123 | return "", nil |
| 124 | } |
| 125 | // 不在工具块里 —— 看剩余文本是否整体就是一个 JSON |
| 126 | if p.emittedCount == 0 { |
| 127 | if tc := buildToolCallFromRaw(remaining); tc != nil { |
| 128 | toolCalls = append(toolCalls, *tc) |
| 129 | p.emittedCount++ |
| 130 | return "", toolCalls |
| 131 | } |
| 132 | if !p.emittedText { |
| 133 | return remaining, nil |
| 134 | } |
| 135 | } |
| 136 | return "", nil |
| 137 | } |
| 138 | |
| 139 | // normalize 把模型常见的标签变体(<tool_calls>、<tool call> 等)统一为 |
| 140 | // <tool_call> / </tool_call>。 |