mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
e00e85765b
This PR merges code from `coder/aibridge` repository into `coder/coder`. It was split into 4 PRs for easier review but stacked PRs will need to be merged into this PR so all checks pass. * https://github.com/coder/coder/pull/24190 -> raw code copy (this PR, before merging PRs on top of it, it was just 1 commit: https://github.com/coder/coder/commit/70d33f33200c7e77df910957595715f81f9bec24) * https://github.com/coder/coder/pull/24570 -> update imports in `coder/coder` to use copied code * https://github.com/coder/coder/pull/24586 -> linter fixes and CI integration (also added README.md) * https://github.com/coder/coder/pull/24571 -> added exclude to scripts/check_emdash.sh check Original PR message (before PR squash): Moves coder/aibridge code into coder/coder repository. Omitted files: - `go.mod`, `go.sum`, `.gitignore`, `.github/workflows/ci.yml,` `Makefile`, `LICENSE`, `README.md` (modified README.md is added later) - `.github`, `example`, `buildinfo,` `scripts` directories Simple verification script (will list omitted files) ``` tmp=$(mktemp -d) echo "$tmp" git clone --depth=1 https://github.com/coder/aibridge "$tmp/aibridge" git clone --depth=1 --branch pb/aibridge-code-move https://github.com/coder/coder "$tmp/coder" diff -rq --exclude=.git "$tmp/aibridge" "$tmp/coder/aibridge" # rm -rf "$tmp" ```
125 lines
2.4 KiB
Go
125 lines
2.4 KiB
Go
package aibridge
|
|
|
|
import (
|
|
"bufio"
|
|
"io"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
const (
|
|
SSEEventTypeMessage = "message"
|
|
SSEEventTypeError = "error"
|
|
SSEEventTypePing = "ping"
|
|
)
|
|
|
|
type SSEEvent struct {
|
|
Type string
|
|
Data string
|
|
ID string
|
|
Retry int
|
|
}
|
|
|
|
type SSEParser struct {
|
|
events map[string][]SSEEvent
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
func NewSSEParser() *SSEParser {
|
|
return &SSEParser{
|
|
events: make(map[string][]SSEEvent),
|
|
}
|
|
}
|
|
|
|
func (p *SSEParser) Parse(reader io.Reader) error {
|
|
scanner := bufio.NewScanner(reader)
|
|
|
|
var currentEvent SSEEvent
|
|
var dataLines []string
|
|
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
|
|
// Empty line indicates end of event
|
|
if line == "" {
|
|
if len(dataLines) > 0 {
|
|
currentEvent.Data = strings.Join(dataLines, "\n")
|
|
}
|
|
|
|
// Default to message type if no event type specified
|
|
if currentEvent.Type == "" {
|
|
currentEvent.Type = SSEEventTypeMessage
|
|
}
|
|
|
|
// Store the event
|
|
p.mu.Lock()
|
|
p.events[currentEvent.Type] = append(p.events[currentEvent.Type], currentEvent)
|
|
p.mu.Unlock()
|
|
|
|
// Reset for next event
|
|
currentEvent = SSEEvent{}
|
|
dataLines = nil
|
|
continue
|
|
}
|
|
|
|
// Skip comments
|
|
if strings.HasPrefix(line, ":") {
|
|
continue
|
|
}
|
|
|
|
// Parse field:value format
|
|
if colonIndex := strings.Index(line, ":"); colonIndex != -1 {
|
|
field := line[:colonIndex]
|
|
value := line[colonIndex+1:]
|
|
|
|
// Remove leading space from value if present
|
|
if len(value) > 0 && value[0] == ' ' {
|
|
value = value[1:]
|
|
}
|
|
|
|
switch field {
|
|
case "event":
|
|
currentEvent.Type = value
|
|
case "data":
|
|
dataLines = append(dataLines, value)
|
|
case "id":
|
|
currentEvent.ID = value
|
|
case "retry":
|
|
if retryMs, err := strconv.Atoi(value); err == nil {
|
|
currentEvent.Retry = retryMs
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return scanner.Err()
|
|
}
|
|
|
|
func (p *SSEParser) EventsByType(eventType string) []SSEEvent {
|
|
p.mu.RLock()
|
|
defer p.mu.RUnlock()
|
|
|
|
events := p.events[eventType]
|
|
result := make([]SSEEvent, len(events))
|
|
copy(result, events)
|
|
return result
|
|
}
|
|
|
|
func (p *SSEParser) MessageEvents() []SSEEvent {
|
|
return p.EventsByType(SSEEventTypeMessage)
|
|
}
|
|
|
|
func (p *SSEParser) AllEvents() map[string][]SSEEvent {
|
|
p.mu.RLock()
|
|
defer p.mu.RUnlock()
|
|
|
|
result := make(map[string][]SSEEvent)
|
|
for eventType, events := range p.events {
|
|
eventsCopy := make([]SSEEvent, len(events))
|
|
copy(eventsCopy, events)
|
|
result[eventType] = eventsCopy
|
|
}
|
|
return result
|
|
}
|