internal/pluginhost/model_stream_bridge.go
Rama main · réplica de lectura del repositorio original.
package pluginhost
import (
"context"
"fmt"
"strconv"
"strings"
"sync"
"sync/atomic"
"github.com/router-for-me/CLIProxyAPI/v8/sdk/api/handlers"
)
type modelStreamBridge struct {
next atomic.Uint64
mu sync.Mutex
streams map[string]modelStreamEntry
inflight map[uint64]modelStreamInflight
}
type modelStreamInflight struct {
pluginID string
callbackPluginID string
instance *hostCallbackInstance
callbackInstance *hostCallbackInstance
cancel context.CancelFunc
}
type modelStreamEntry struct {
pluginID string
callbackPluginID string
instance *hostCallbackInstance
callbackInstance *hostCallbackInstance
ownerCallbackID string
chunks <-chan handlers.ModelExecutionChunk
cancel context.CancelFunc
stopScopeCleanup func()
}
func newModelStreamBridge() *modelStreamBridge {
return &modelStreamBridge{
streams: make(map[string]modelStreamEntry),
inflight: make(map[uint64]modelStreamInflight),
}
}
func (b *modelStreamBridge) begin(pluginID, callbackPluginID string, instance, callbackInstance *hostCallbackInstance, cancel context.CancelFunc) (uint64, bool) {
if b == nil {
if cancel != nil {
cancel()
}
return 0, false
}
pluginID = strings.TrimSpace(pluginID)
callbackPluginID = strings.TrimSpace(callbackPluginID)
b.mu.Lock()
if (instance != nil && instance.closed.Load()) || (callbackInstance != nil && callbackInstance.closed.Load()) {
b.mu.Unlock()
if cancel != nil {
cancel()
}
return 0, false
}
if b.inflight == nil {
b.inflight = make(map[uint64]modelStreamInflight)
}
id := b.next.Add(1)
b.inflight[id] = modelStreamInflight{
pluginID: pluginID,
callbackPluginID: callbackPluginID,
instance: instance,
callbackInstance: callbackInstance,
cancel: cancel,
}
b.mu.Unlock()
return id, true
}
func (b *modelStreamBridge) abort(inflightID uint64) {
if b == nil || inflightID == 0 {
return
}
b.mu.Lock()
entry, ok := b.inflight[inflightID]
delete(b.inflight, inflightID)
b.mu.Unlock()
if ok && entry.cancel != nil {
entry.cancel()
}
}
func (b *modelStreamBridge) complete(inflightID uint64, pluginID string, instance *hostCallbackInstance, ownerCallbackID string, chunks <-chan handlers.ModelExecutionChunk, cancel context.CancelFunc) string {
if b == nil || chunks == nil {
b.abort(inflightID)
if cancel != nil {
cancel()
}
return ""
}
id := strconv.FormatUint(b.next.Add(1), 10)
pluginID = strings.TrimSpace(pluginID)
b.mu.Lock()
inflightEntry, stillInflight := b.inflight[inflightID]
delete(b.inflight, inflightID)
if !stillInflight || (instance != nil && instance.closed.Load()) || (inflightEntry.callbackInstance != nil && inflightEntry.callbackInstance.closed.Load()) {
b.mu.Unlock()
if cancel != nil {
cancel()
}
return ""
}
if b.streams == nil {
b.streams = make(map[string]modelStreamEntry)
}
b.streams[id] = modelStreamEntry{
pluginID: pluginID,
callbackPluginID: inflightEntry.callbackPluginID,
instance: instance,
callbackInstance: inflightEntry.callbackInstance,
ownerCallbackID: ownerCallbackID,
chunks: chunks,
cancel: cancel,
}
b.mu.Unlock()
return id
}
func (b *modelStreamBridge) open(pluginID string, instance *hostCallbackInstance, ownerCallbackID string, chunks <-chan handlers.ModelExecutionChunk, cancel context.CancelFunc) string {
if b == nil || chunks == nil {
if cancel != nil {
cancel()
}
return ""
}
id := strconv.FormatUint(b.next.Add(1), 10)
pluginID = strings.TrimSpace(pluginID)
b.mu.Lock()
if instance != nil && instance.closed.Load() {
b.mu.Unlock()
if cancel != nil {
cancel()
}
return ""
}
if b.streams == nil {
b.streams = make(map[string]modelStreamEntry)
}
b.streams[id] = modelStreamEntry{
pluginID: pluginID,
instance: instance,
ownerCallbackID: ownerCallbackID,
chunks: chunks,
cancel: cancel,
}
b.mu.Unlock()
return id
}
func (b *modelStreamBridge) setScopeCleanup(pluginID string, instance *hostCallbackInstance, id string, stop func()) bool {
if b == nil || stop == nil {
if stop != nil {
stop()
}
return false
}
id = strings.TrimSpace(id)
pluginID = strings.TrimSpace(pluginID)
b.mu.Lock()
entry, ok := b.streams[id]
if ok && entry.pluginID == pluginID && entry.instance == instance && entry.stopScopeCleanup == nil {
entry.stopScopeCleanup = stop
b.streams[id] = entry
b.mu.Unlock()
return true
}
b.mu.Unlock()
stop()
return false
}
func (b *modelStreamBridge) read(ctx context.Context, pluginID string, instance *hostCallbackInstance, id string) (handlers.ModelExecutionChunk, bool, error) {
if b == nil {
return handlers.ModelExecutionChunk{}, true, fmt.Errorf("model stream bridge is unavailable")
}
id = strings.TrimSpace(id)
if id == "" {
return handlers.ModelExecutionChunk{}, true, fmt.Errorf("model stream id is required")
}
pluginID = strings.TrimSpace(pluginID)
b.mu.Lock()
entry, ok := b.streams[id]
b.mu.Unlock()
if !ok || entry.chunks == nil {
return handlers.ModelExecutionChunk{}, true, nil
}
if entry.pluginID != pluginID || entry.instance != instance {
return handlers.ModelExecutionChunk{}, true, fmt.Errorf("model stream %s is not open", id)
}
if ctx == nil {
ctx = context.Background()
}
select {
case <-ctx.Done():
b.close(pluginID, instance, id)
return handlers.ModelExecutionChunk{}, true, ctx.Err()
case chunk, okRead := <-entry.chunks:
if !okRead {
b.close(pluginID, instance, id)
return handlers.ModelExecutionChunk{}, true, nil
}
if chunk.Err != nil {
b.close(pluginID, instance, id)
return chunk, true, nil
}
return chunk, false, nil
}
}
func (b *modelStreamBridge) close(pluginID string, instance *hostCallbackInstance, id string) {
if b == nil {
return
}
id = strings.TrimSpace(id)
if id == "" {
return
}
pluginID = strings.TrimSpace(pluginID)
b.mu.Lock()
entry, ok := b.streams[id]
if !ok || entry.pluginID != pluginID || entry.instance != instance {
b.mu.Unlock()
return
}
delete(b.streams, id)
b.mu.Unlock()
closeModelStreamEntry(entry)
}
func (b *modelStreamBridge) closeInstance(pluginID string, instance *hostCallbackInstance) {
if instance == nil {
b.closePlugin(pluginID)
return
}
instance.closed.Store(true)
if b == nil {
return
}
b.closeMatching(pluginID, instance, false)
}
func (b *modelStreamBridge) closePlugin(pluginID string) {
if b == nil || strings.TrimSpace(pluginID) == "" {
return
}
b.closeMatching(pluginID, nil, false)
}
func (b *modelStreamBridge) closeAll() {
if b == nil {
return
}
b.closeMatching("", nil, true)
}
func matchesModelStreamOwner(pluginID, callbackPluginID string, instance, callbackInstance *hostCallbackInstance, targetPluginID string, targetInstance *hostCallbackInstance) bool {
pluginMatch := pluginID == targetPluginID || (pluginID == "" && callbackPluginID == targetPluginID)
if !pluginMatch {
return false
}
if targetInstance == nil {
return true
}
return instance == targetInstance || (instance == nil && callbackInstance == targetInstance)
}
func (b *modelStreamBridge) closeMatching(pluginID string, instance *hostCallbackInstance, all bool) {
pluginID = strings.TrimSpace(pluginID)
b.mu.Lock()
entries := make([]modelStreamEntry, 0)
for id, entry := range b.streams {
if !all && !matchesModelStreamOwner(entry.pluginID, entry.callbackPluginID, entry.instance, entry.callbackInstance, pluginID, instance) {
continue
}
if all {
if entry.instance != nil {
entry.instance.closed.Store(true)
}
if entry.callbackInstance != nil {
entry.callbackInstance.closed.Store(true)
}
}
delete(b.streams, id)
entries = append(entries, entry)
}
inflightCancels := make([]context.CancelFunc, 0)
for id, entry := range b.inflight {
if !all && !matchesModelStreamOwner(entry.pluginID, entry.callbackPluginID, entry.instance, entry.callbackInstance, pluginID, instance) {
continue
}
if all {
if entry.instance != nil {
entry.instance.closed.Store(true)
}
if entry.callbackInstance != nil {
entry.callbackInstance.closed.Store(true)
}
}
delete(b.inflight, id)
if entry.cancel != nil {
inflightCancels = append(inflightCancels, entry.cancel)
}
}
b.mu.Unlock()
for _, cancel := range inflightCancels {
cancel()
}
for _, entry := range entries {
closeModelStreamEntry(entry)
}
}
func closeModelStreamEntry(entry modelStreamEntry) {
if entry.stopScopeCleanup != nil {
entry.stopScopeCleanup()
}
if entry.cancel != nil {
entry.cancel()
}
}