File size: 1,771 Bytes
80ffd2e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | package stream
import "strings"
// ToolTagPrefixes are possible tool call tag prefixes
var ToolTagPrefixes = []string{
"<function_calls",
"<tool>",
"<tool ",
"<tools>",
"<tools ",
}
// TextBuffer manages text buffering for streaming
type TextBuffer struct {
PendingText string
ToolCallDetected bool
}
// NewTextBuffer creates a new text buffer
func NewTextBuffer() *TextBuffer {
return &TextBuffer{
PendingText: "",
ToolCallDetected: false,
}
}
// Add adds text to the buffer
func (b *TextBuffer) Add(text string) {
b.PendingText += text
}
// FlushSafeText flushes safe text that won't be part of a tool call tag
func (b *TextBuffer) FlushSafeText(emitFunc func(string)) {
if b.PendingText == "" || b.ToolCallDetected {
return
}
safeEndIndex := len(b.PendingText)
// Check each tool call tag prefix
for _, prefix := range ToolTagPrefixes {
for i := 1; i <= len(prefix); i++ {
partialTag := prefix[:i]
idx := strings.LastIndex(b.PendingText, partialTag)
if idx != -1 && idx+len(partialTag) == len(b.PendingText) {
// Partial tag at end, keep it
if idx < safeEndIndex {
safeEndIndex = idx
}
}
}
}
if safeEndIndex > 0 {
safeText := b.PendingText[:safeEndIndex]
if safeText != "" {
emitFunc(safeText)
}
b.PendingText = b.PendingText[safeEndIndex:]
}
}
// FlushAll flushes all pending text
func (b *TextBuffer) FlushAll(emitFunc func(string)) {
if b.PendingText != "" {
emitFunc(b.PendingText)
b.PendingText = ""
}
}
// Clear clears the buffer
func (b *TextBuffer) Clear() {
b.PendingText = ""
}
// IsEmpty checks if buffer is empty
func (b *TextBuffer) IsEmpty() bool {
return b.PendingText == ""
} |