mirror of
https://github.com/langgenius/dify-plugin-daemon.git
synced 2026-07-23 18:25:36 -04:00
191 lines
3.9 KiB
Markdown
191 lines
3.9 KiB
Markdown
# Stream Operations
|
|
|
|
Thread-safe, generic streaming for async producer-consumer patterns (`internal/utils/stream/`).
|
|
|
|
**Related Documentation:**
|
|
- [HTTP Requests](http-requests.md) - HTTP streams using `RequestAndParseStream`
|
|
- [Generic Types](generics.md) - Stream type safety patterns
|
|
- [Cache Operations](cache.md) - Pub/sub streaming patterns
|
|
|
|
## Core Concepts
|
|
|
|
The `Stream[T]` type provides buffered, type-safe communication between goroutines with backpressure control.
|
|
|
|
## Basic Usage
|
|
|
|
### Creation
|
|
```go
|
|
// Create with buffer size (max items before blocking)
|
|
s := stream.NewStream[T](128)
|
|
defer s.Close()
|
|
```
|
|
|
|
### Producer Operations
|
|
```go
|
|
// Non-blocking write (errors if full)
|
|
err := s.Write(data)
|
|
|
|
// Blocking write (waits for space)
|
|
s.WriteBlocking(data)
|
|
|
|
// Propagate errors
|
|
s.WriteError(err)
|
|
|
|
// Signal completion
|
|
s.Close()
|
|
```
|
|
|
|
### Consumer Operations
|
|
```go
|
|
// Standard iteration pattern
|
|
for s.Next() { // Blocks waiting for data/close
|
|
data, err := s.Read()
|
|
if err != nil {
|
|
if err == stream.ErrEmpty {
|
|
continue
|
|
}
|
|
// Handle actual error
|
|
break
|
|
}
|
|
// Process data
|
|
}
|
|
```
|
|
|
|
## Advanced Features
|
|
|
|
### Lifecycle Hooks
|
|
```go
|
|
// Cleanup on close
|
|
s.OnClose(func() {
|
|
// Release resources
|
|
})
|
|
|
|
// Pre-close operations
|
|
s.BeforeClose(func() {
|
|
// Finalize state
|
|
})
|
|
```
|
|
|
|
### Data Filtering
|
|
```go
|
|
// Add validation/transformation
|
|
s.Filter(func(data T) error {
|
|
if !isValid(data) {
|
|
return errors.New("invalid data")
|
|
}
|
|
return nil
|
|
})
|
|
```
|
|
|
|
### Async Processing
|
|
```go
|
|
// Process all items
|
|
err := s.Async(func(data T) {
|
|
processItem(data)
|
|
})
|
|
```
|
|
|
|
### Status Methods
|
|
```go
|
|
s.IsClosed() // Check if closed
|
|
s.Size() // Current buffer size
|
|
```
|
|
|
|
## Common Patterns
|
|
|
|
### Plugin Response Streaming
|
|
```go
|
|
func handlePluginResponse(pluginOutput <-chan Chunk) *stream.Stream[Chunk] {
|
|
response := stream.NewStream[Chunk](128)
|
|
|
|
go func() {
|
|
defer response.Close()
|
|
for chunk := range pluginOutput {
|
|
if err := response.Write(chunk); err != nil {
|
|
response.WriteError(err)
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
return response
|
|
}
|
|
```
|
|
|
|
### SSE Response Handler
|
|
```go
|
|
func handleSSE(ctx *gin.Context, dataStream *stream.Stream[[]byte]) {
|
|
ctx.Header("Content-Type", "text/event-stream")
|
|
|
|
for dataStream.Next() {
|
|
data, err := dataStream.Read()
|
|
if err != nil {
|
|
ctx.SSEvent("error", err.Error())
|
|
return
|
|
}
|
|
ctx.SSEvent("data", string(data))
|
|
ctx.Writer.Flush()
|
|
}
|
|
}
|
|
```
|
|
|
|
### Error Propagation
|
|
```go
|
|
func processWithValidation(input *stream.Stream[Data]) *stream.Stream[Result] {
|
|
output := stream.NewStream[Result](64)
|
|
|
|
go func() {
|
|
defer output.Close()
|
|
for input.Next() {
|
|
data, err := input.Read()
|
|
if err != nil {
|
|
output.WriteError(err)
|
|
return
|
|
}
|
|
|
|
result, err := validate(data)
|
|
if err != nil {
|
|
output.WriteError(err)
|
|
return
|
|
}
|
|
|
|
output.Write(result)
|
|
}
|
|
}()
|
|
|
|
return output
|
|
}
|
|
```
|
|
|
|
### File Chunk Assembly
|
|
```go
|
|
files := make(map[string]*bytes.Buffer)
|
|
|
|
for response.Next() {
|
|
chunk, _ := response.Read()
|
|
|
|
if chunk.Type == "blob_chunk" {
|
|
id := chunk.ID
|
|
if chunk.End {
|
|
// Complete file
|
|
completeFile := files[id].Bytes()
|
|
processFile(completeFile)
|
|
delete(files, id)
|
|
} else {
|
|
// Accumulate chunks
|
|
if files[id] == nil {
|
|
files[id] = bytes.NewBuffer(nil)
|
|
}
|
|
files[id].Write(chunk.Data)
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Implementation Details
|
|
|
|
- Uses `deque` for efficient FIFO operations
|
|
- Thread-safe with mutex protection
|
|
- Signal channel for blocking consumers
|
|
- Atomic operations for close state
|
|
- Condition variable for blocking writers |