opencode-cursor-agent/internal/parser/stream.go

111 lines
2.8 KiB
Go
Raw Normal View History

2026-03-30 14:09:15 +00:00
package parser
import "encoding/json"
type StreamParser func(line string)
2026-04-01 00:53:34 +00:00
type Parser struct {
Parse StreamParser
Flush func()
}
// CreateStreamParser 建立串流解析器(向後相容,不傳遞 thinking
func CreateStreamParser(onText func(string), onDone func()) Parser {
return CreateStreamParserWithThinking(onText, nil, onDone)
}
// CreateStreamParserWithThinking 建立串流解析器,支援思考過程輸出。
// onThinking 可為 nil表示忽略思考過程。
func CreateStreamParserWithThinking(onText func(string), onThinking func(string), onDone func()) Parser {
2026-04-02 13:54:28 +00:00
// accumulated 是所有已輸出內容的串接
2026-04-01 00:53:34 +00:00
accumulatedText := ""
accumulatedThinking := ""
2026-03-30 14:09:15 +00:00
done := false
2026-04-01 00:53:34 +00:00
parse := func(line string) {
2026-03-30 14:09:15 +00:00
if done {
return
}
var obj struct {
Type string `json:"type"`
Subtype string `json:"subtype"`
Message *struct {
Content []struct {
2026-04-01 00:53:34 +00:00
Type string `json:"type"`
Text string `json:"text"`
Thinking string `json:"thinking"`
2026-03-30 14:09:15 +00:00
} `json:"content"`
} `json:"message"`
}
if err := json.Unmarshal([]byte(line), &obj); err != nil {
return
}
if obj.Type == "assistant" && obj.Message != nil {
2026-04-01 00:53:34 +00:00
fullText := ""
fullThinking := ""
2026-03-30 14:09:15 +00:00
for _, p := range obj.Message.Content {
2026-04-01 00:53:34 +00:00
switch p.Type {
case "text":
if p.Text != "" {
fullText += p.Text
}
case "thinking":
if p.Thinking != "" {
fullThinking += p.Thinking
}
2026-03-30 14:09:15 +00:00
}
}
2026-04-01 00:53:34 +00:00
2026-04-02 13:54:28 +00:00
// 處理思考過程(不因去重而 return避免跳過同行的文字內容
if onThinking != nil && fullThinking != "" && fullThinking != accumulatedThinking {
// 增量模式:新內容以 accumulated 為前綴
if len(fullThinking) >= len(accumulatedThinking) && fullThinking[:len(accumulatedThinking)] == accumulatedThinking {
2026-04-01 00:53:34 +00:00
delta := fullThinking[len(accumulatedThinking):]
2026-04-02 13:54:28 +00:00
if delta != "" {
onThinking(delta)
}
2026-04-01 00:53:34 +00:00
accumulatedThinking = fullThinking
} else {
2026-04-02 13:54:28 +00:00
// 獨立片段:直接輸出,但 accumulated 要串接
2026-04-01 00:53:34 +00:00
onThinking(fullThinking)
2026-04-02 13:54:28 +00:00
accumulatedThinking = accumulatedThinking + fullThinking
2026-04-01 00:53:34 +00:00
}
}
2026-04-02 13:54:28 +00:00
// 處理一般文字
if fullText == "" || fullText == accumulatedText {
2026-03-30 14:09:15 +00:00
return
}
2026-04-02 13:54:28 +00:00
// 增量模式:新內容以 accumulated 為前綴
if len(fullText) >= len(accumulatedText) && fullText[:len(accumulatedText)] == accumulatedText {
2026-04-01 00:53:34 +00:00
delta := fullText[len(accumulatedText):]
2026-04-02 13:54:28 +00:00
if delta != "" {
onText(delta)
}
2026-04-01 00:53:34 +00:00
accumulatedText = fullText
2026-03-30 14:09:15 +00:00
} else {
2026-04-02 13:54:28 +00:00
// 獨立片段:直接輸出,但 accumulated 要串接
2026-04-01 00:53:34 +00:00
onText(fullText)
2026-04-02 13:54:28 +00:00
accumulatedText = accumulatedText + fullText
2026-03-30 14:09:15 +00:00
}
}
if obj.Type == "result" && obj.Subtype == "success" {
done = true
onDone()
}
}
2026-04-01 00:53:34 +00:00
flush := func() {
if !done {
done = true
onDone()
}
}
return Parser{Parse: parse, Flush: flush}
2026-03-30 14:09:15 +00:00
}