package pregel import ( "context" "fmt" "slices" "sync" "ragflow/internal/harness/graph/channels" "ragflow/internal/harness/graph/types" ) // ChannelWrite represents a write operation to channels. // It encapsulates the logic for writing state updates to multiple channels. type ChannelWrite struct { registry *channels.Registry entries []*ChannelWriteEntry transformer WriteTransformer validator WriteValidator mu sync.RWMutex } // ChannelWriteEntry represents a single write operation. type ChannelWriteEntry struct { Channel string Value any Overwrite bool Node string Metadata map[string]any } // WriteTransformer transforms write values before applying them. type WriteTransformer interface { Transform(entry *ChannelWriteEntry) (*ChannelWriteEntry, error) } // WriteValidator validates write operations. type WriteValidator interface { Validate(entry *ChannelWriteEntry) error } // NewChannelWrite creates a new channel write operation. func NewChannelWrite(registry *channels.Registry, opts ...ChannelWriteOption) *ChannelWrite { cw := &ChannelWrite{ registry: registry, entries: make([]*ChannelWriteEntry, 0), transformer: &IdentityWriteTransformer{}, validator: &NoOpValidator{}, } for _, opt := range opts { opt(cw) } return cw } // ChannelWriteOption configures a ChannelWrite. type ChannelWriteOption func(*ChannelWrite) // WithWriteTransformer sets the write transformer. func WithWriteTransformer(transformer WriteTransformer) ChannelWriteOption { return func(cw *ChannelWrite) { cw.transformer = transformer } } // WithValidator sets the write validator. func WithValidator(validator WriteValidator) ChannelWriteOption { return func(cw *ChannelWrite) { cw.validator = validator } } // AddEntry adds a write entry. func (cw *ChannelWrite) AddEntry(entry *ChannelWriteEntry) { cw.mu.Lock() defer cw.mu.Unlock() cw.entries = append(cw.entries, entry) } // AddEntries adds multiple write entries. func (cw *ChannelWrite) AddEntries(entries ...*ChannelWriteEntry) { cw.mu.Lock() defer cw.mu.Unlock() cw.entries = append(cw.entries, entries...) } // WriteTo adds a simple write to a channel. func (cw *ChannelWrite) WriteTo(channel string, value any) { cw.AddEntry(&ChannelWriteEntry{ Channel: channel, Value: value, Overwrite: false, }) } // Overwrite overwrites a channel with a value. func (cw *ChannelWrite) Overwrite(channel string, value any) { cw.AddEntry(&ChannelWriteEntry{ Channel: channel, Value: value, Overwrite: true, }) } // WriteNode writes from a specific node. func (cw *ChannelWrite) WriteNode(node string, channel string, value any) { cw.AddEntry(&ChannelWriteEntry{ Channel: channel, Value: value, Overwrite: false, Node: node, }) } // Write executes all write operations. func (cw *ChannelWrite) Write(ctx context.Context) (map[string]bool, error) { cw.mu.Lock() defer cw.mu.Unlock() updated := make(map[string]bool) for _, entry := range cw.entries { // Validate if cw.validator != nil { if err := cw.validator.Validate(entry); err != nil { return nil, fmt.Errorf("validation failed for channel %s: %w", entry.Channel, err) } } // Transform transformed := entry if cw.transformer != nil { var err error transformed, err = cw.transformer.Transform(entry) if err != nil { return nil, fmt.Errorf("transformation failed for channel %s: %w", entry.Channel, err) } } // Apply write if ch, ok := cw.registry.Get(transformed.Channel); ok { // Check for Overwrite wrapper value := transformed.Value if transformed.Overwrite { value = &types.Overwrite{Value: value} } wasUpdated, err := ch.Update([]any{value}) if err != nil { return nil, fmt.Errorf("failed to update channel %s: %w", transformed.Channel, err) } if wasUpdated { updated[transformed.Channel] = true } } else { return nil, fmt.Errorf("channel not found: %s", transformed.Channel) } } // Clear entries after write cw.entries = make([]*ChannelWriteEntry, 0) return updated, nil } // Clear clears all pending write entries. func (cw *ChannelWrite) Clear() { cw.mu.Lock() defer cw.mu.Unlock() cw.entries = make([]*ChannelWriteEntry, 0) } // EntryCount returns the number of pending entries. func (cw *ChannelWrite) EntryCount() int { cw.mu.RLock() defer cw.mu.RUnlock() return len(cw.entries) } // GetEntries returns a copy of all entries. func (cw *ChannelWrite) GetEntries() []*ChannelWriteEntry { cw.mu.RLock() defer cw.mu.RUnlock() entries := make([]*ChannelWriteEntry, len(cw.entries)) copy(entries, cw.entries) return entries } // ==================== Write Transformers ==================== // IdentityWriteTransformer doesn't transform. type IdentityWriteTransformer struct{} func (t *IdentityWriteTransformer) Transform(entry *ChannelWriteEntry) (*ChannelWriteEntry, error) { return entry, nil } // MappingWriteTransformer maps channel names. type MappingWriteTransformer struct { mappings map[string]string } // NewMappingWriteTransformer creates a transformer that maps channel names. func NewMappingWriteTransformer(mappings map[string]string) *MappingWriteTransformer { return &MappingWriteTransformer{mappings: mappings} } func (t *MappingWriteTransformer) Transform(entry *ChannelWriteEntry) (*ChannelWriteEntry, error) { if newName, ok := t.mappings[entry.Channel]; ok { transformed := *entry transformed.Channel = newName return &transformed, nil } return entry, nil } // PrefixWriteTransformer adds a prefix to channel names. type PrefixWriteTransformer struct { prefix string } // NewPrefixWriteTransformer creates a transformer that adds a prefix. func NewPrefixWriteTransformer(prefix string) *PrefixWriteTransformer { return &PrefixWriteTransformer{prefix: prefix} } func (t *PrefixWriteTransformer) Transform(entry *ChannelWriteEntry) (*ChannelWriteEntry, error) { transformed := *entry transformed.Channel = t.prefix + entry.Channel return &transformed, nil } // MetadataWriteTransformer adds metadata to entries. type MetadataWriteTransformer struct { metadata map[string]any } // NewMetadataWriteTransformer creates a transformer that adds metadata. func NewMetadataWriteTransformer(metadata map[string]any) *MetadataWriteTransformer { return &MetadataWriteTransformer{metadata: metadata} } func (t *MetadataWriteTransformer) Transform(entry *ChannelWriteEntry) (*ChannelWriteEntry, error) { transformed := *entry if transformed.Metadata == nil { transformed.Metadata = make(map[string]any) } for k, v := range t.metadata { transformed.Metadata[k] = v } return &transformed, nil } // NodeWriteTransformer adds node information to entries. type NodeWriteTransformer struct { node string } // NewNodeWriteTransformer creates a transformer that adds node info. func NewNodeWriteTransformer(node string) *NodeWriteTransformer { return &NodeWriteTransformer{node: node} } func (t *NodeWriteTransformer) Transform(entry *ChannelWriteEntry) (*ChannelWriteEntry, error) { if entry.Node == "" { transformed := *entry transformed.Node = t.node return &transformed, nil } return entry, nil } // FilterWriteTransformer filters entries based on a predicate. type FilterWriteTransformer struct { predicate func(*ChannelWriteEntry) bool } // NewFilterWriteTransformer creates a transformer that filters entries. func NewFilterWriteTransformer(predicate func(*ChannelWriteEntry) bool) *FilterWriteTransformer { return &FilterWriteTransformer{predicate: predicate} } func (t *FilterWriteTransformer) Transform(entry *ChannelWriteEntry) (*ChannelWriteEntry, error) { if t.predicate != nil && !t.predicate(entry) { return nil, &WriteSkipError{Channel: entry.Channel} } return entry, nil } // ==================== Write Validators ==================== // NoOpValidator doesn't validate. type NoOpValidator struct{} func (v *NoOpValidator) Validate(entry *ChannelWriteEntry) error { return nil } // TypeWriteValidator validates value types. type TypeWriteValidator struct { types map[string]any } // NewTypeWriteValidator creates a validator for value types. func NewTypeWriteValidator(types map[string]any) *TypeWriteValidator { return &TypeWriteValidator{types: types} } func (v *TypeWriteValidator) Validate(entry *ChannelWriteEntry) error { if expectedType, ok := v.types[entry.Channel]; ok { if entry.Value != nil && fmt.Sprintf("%T", entry.Value) != fmt.Sprintf("%T", expectedType) { return &WriteValidationError{ Channel: entry.Channel, Message: fmt.Sprintf("expected type %T, got %T", expectedType, entry.Value), } } } return nil } // NonNullWriteValidator ensures values are not nil. type NonNullWriteValidator struct { whitelist []string } // NewNonNullWriteValidator creates a validator that rejects nil values. func NewNonNullWriteValidator(whitelist ...string) *NonNullWriteValidator { return &NonNullWriteValidator{whitelist: whitelist} } func (v *NonNullWriteValidator) Validate(entry *ChannelWriteEntry) error { if slices.Contains(v.whitelist, entry.Channel) { return nil } if entry.Value == nil { return &WriteValidationError{ Channel: entry.Channel, Message: "value cannot be nil", } } return nil } // LengthWriteValidator validates slice/string lengths. type LengthWriteValidator struct { minLengths map[string]int maxLengths map[string]int } // NewLengthWriteValidator creates a validator for lengths. func NewLengthWriteValidator(minLengths, maxLengths map[string]int) *LengthWriteValidator { return &LengthWriteValidator{ minLengths: minLengths, maxLengths: maxLengths, } } func (v *LengthWriteValidator) Validate(entry *ChannelWriteEntry) error { var length int switch val := entry.Value.(type) { case []any: length = len(val) case string: length = len(val) case map[string]any: length = len(val) default: return nil } if min, ok := v.minLengths[entry.Channel]; ok && length < min { return &WriteValidationError{ Channel: entry.Channel, Message: fmt.Sprintf("length %d is less than minimum %d", length, min), } } if max, ok := v.maxLengths[entry.Channel]; ok && length > max { return &WriteValidationError{ Channel: entry.Channel, Message: fmt.Sprintf("length %d exceeds maximum %d", length, max), } } return nil } // ==================== Write Batches ==================== // WriteBatch represents a batch of write operations. type WriteBatch struct { entries []*ChannelWriteEntry } // NewWriteBatch creates a new write batch. func NewWriteBatch() *WriteBatch { return &WriteBatch{ entries: make([]*ChannelWriteEntry, 0), } } // Add adds an entry to the batch. func (b *WriteBatch) Add(entry *ChannelWriteEntry) { b.entries = append(b.entries, entry) } // WriteTo adds a simple write to the batch. func (b *WriteBatch) WriteTo(channel string, value any) { b.Add(&ChannelWriteEntry{ Channel: channel, Value: value, Overwrite: false, }) } // Overwrite adds an overwrite to the batch. func (b *WriteBatch) Overwrite(channel string, value any) { b.Add(&ChannelWriteEntry{ Channel: channel, Value: value, Overwrite: true, }) } // Entries returns all entries in the batch. func (b *WriteBatch) Entries() []*ChannelWriteEntry { return b.entries } // Size returns the number of entries in the batch. func (b *WriteBatch) Size() int { return len(b.entries) } // Clear clears all entries. func (b *WriteBatch) Clear() { b.entries = make([]*ChannelWriteEntry, 0) } // ==================== Write Context ==================== // WriteContext represents the context of a write operation. type WriteContext struct { Node string Step int Writer *ChannelWrite Batches map[string]*WriteBatch } // NewWriteContext creates a new write context. func NewWriteContext(node string, step int, writer *ChannelWrite) *WriteContext { return &WriteContext{ Node: node, Step: step, Writer: writer, Batches: make(map[string]*WriteBatch), } } // CreateBatch creates a new named batch. func (wc *WriteContext) CreateBatch(name string) *WriteBatch { batch := NewWriteBatch() wc.Batches[name] = batch return batch } // GetBatch gets an existing batch. func (wc *WriteContext) GetBatch(name string) *WriteBatch { return wc.Batches[name] } // Flush writes all batches to the main writer. func (wc *WriteContext) Flush(ctx context.Context) (map[string]bool, error) { for _, batch := range wc.Batches { wc.Writer.AddEntries(batch.Entries()...) } return wc.Writer.Write(ctx) } // ==================== Errors ==================== // WriteValidationError represents a validation error. type WriteValidationError struct { Channel string Message string } func (e *WriteValidationError) Error() string { return fmt.Sprintf("write validation error for channel %s: %s", e.Channel, e.Message) } // WriteSkipError indicates an entry should be skipped. type WriteSkipError struct { Channel string } func (e *WriteSkipError) Error() string { return fmt.Sprintf("write skipped for channel %s", e.Channel) } // IsWriteSkipError checks if an error is a skip error. func IsWriteSkipError(err error) bool { _, ok := err.(*WriteSkipError) return ok }