Files
ragflow/internal/harness/graph/channels/binop.go
Yingfeng 706e0d2d06 Refactor harness framework (#16271)
### What problem does this PR solve?

- Tools management
- Pregel engine wrapper for better usage
- UT race
- Coding style

### Type of change

- [x] Refactoring
2026-06-23 20:18:04 +08:00

217 lines
5.0 KiB
Go

package channels
import (
"fmt"
"reflect"
"ragflow/internal/harness/graph/constants"
"ragflow/internal/harness/graph/errors"
)
// BinaryOperator is a function that combines two values into one.
type BinaryOperator func(a, b interface{}) interface{}
// BinaryOperatorAggregate stores the result of applying a binary operator to the current value and each new value.
type BinaryOperatorAggregate struct {
BaseChannel
value interface{}
operator BinaryOperator
}
// NewBinaryOperatorAggregate creates a new BinaryOperatorAggregate channel.
func NewBinaryOperatorAggregate(typ interface{}, operator BinaryOperator) *BinaryOperatorAggregate {
c := &BinaryOperatorAggregate{
BaseChannel: BaseChannel{Typ: typ},
operator: operator,
}
// Try to initialize with zero value
c.value = createZeroValue(typ)
return c
}
// createZeroValue creates a zero value for the given type.
func createZeroValue(typ interface{}) (result interface{}) {
result = Missing
if typ == nil {
return
}
rt := reflect.TypeOf(typ)
// Handle special collection types
switch rt.String() {
case "[]interface {}":
return make([]interface{}, 0)
case "map[string]interface {}":
return make(map[string]interface{})
}
// Try to create instance
if rt.Kind() == reflect.Ptr {
rt = rt.Elem()
}
if rt.Kind() == reflect.Slice {
return reflect.MakeSlice(rt, 0, 0).Interface()
}
if rt.Kind() == reflect.Map {
return reflect.MakeMap(rt).Interface()
}
// Use named return so the deferred recovery can return Missing.
defer func() {
if r := recover(); r != nil {
result = Missing
}
}()
// Create a zero value using reflect.Zero
zero := reflect.Zero(rt)
result = zero.Interface()
return
}
// Get returns the current value of the channel.
func (c *BinaryOperatorAggregate) Get() (interface{}, error) {
if IsMissing(c.value) {
return nil, &errors.EmptyChannelError{}
}
return c.value, nil
}
// IsAvailable returns true if the channel has a value.
func (c *BinaryOperatorAggregate) IsAvailable() bool {
return !IsMissing(c.value)
}
// isOverwrite checks if a value is an overwrite wrapper.
func isOverwrite(value interface{}) (bool, interface{}) {
if value == nil {
return false, nil
}
// Check for Overwrite type
type overwriter interface {
GetValue() interface{}
}
if ow, ok := value.(overwriter); ok {
return true, ow.GetValue()
}
// Check for map with __overwrite__ key
if m, ok := value.(map[string]interface{}); ok {
if len(m) == 1 {
if v, exists := m[constants.Overwrite]; exists {
return true, v
}
}
}
return false, nil
}
// Update updates the channel with values.
func (c *BinaryOperatorAggregate) Update(values []interface{}) (bool, error) {
if len(values) == 0 {
return false, nil
}
// Initialize with first value if empty
if IsMissing(c.value) {
c.value = values[0]
values = values[1:]
}
seenOverwrite := false
for _, value := range values {
isOver, overwriteValue := isOverwrite(value)
if isOver {
if seenOverwrite {
return false, &errors.InvalidUpdateError{
Message: "Can receive only one Overwrite value per super-step.",
}
}
c.value = overwriteValue
seenOverwrite = true
continue
}
if !seenOverwrite {
c.value = c.operator(c.value, value)
}
}
return true, nil
}
// Copy returns a copy of the channel.
func (c *BinaryOperatorAggregate) Copy() Channel {
newCh := NewBinaryOperatorAggregate(c.Typ, c.operator)
newCh.Key = c.Key
newCh.value = c.value
return newCh
}
// Checkpoint returns the current value.
func (c *BinaryOperatorAggregate) Checkpoint() interface{} {
return c.value
}
// FromCheckpoint restores the channel from a checkpoint.
func (c *BinaryOperatorAggregate) FromCheckpoint(checkpoint interface{}) Channel {
newCh := NewBinaryOperatorAggregate(c.Typ, c.operator)
newCh.Key = c.Key
if !IsMissing(checkpoint) {
newCh.value = checkpoint
}
return newCh
}
// String concatenation operator for BinaryOperatorAggregate.
func StringConcat(a, b interface{}) interface{} {
sa, ok1 := a.(string)
sb, ok2 := b.(string)
if !ok1 || !ok2 {
// Gracefully degrade: concatenate via fmt.Sprint.
return fmt.Sprint(a) + fmt.Sprint(b)
}
return sa + sb
}
// IntAdd is an integer addition operator for BinaryOperatorAggregate.
// Supports int and float64. On type mismatch, returns a unchanged (graceful degradation).
func IntAdd(a, b interface{}) interface{} {
if ai, ok := a.(int); ok {
if bi, ok := b.(int); ok {
return ai + bi
}
}
if af, ok := a.(float64); ok {
if bf, ok := b.(float64); ok {
return af + bf
}
}
// Graceful degradation: return a unchanged.
return a
}
// ListAppend appends two lists for BinaryOperatorAggregate.
func ListAppend(a, b interface{}) interface{} {
if al, ok := a.([]interface{}); ok {
if bl, ok := b.([]interface{}); ok {
result := make([]interface{}, len(al)+len(bl))
copy(result, al)
copy(result[len(al):], bl)
return result
}
result := make([]interface{}, len(al)+1)
copy(result, al)
result[len(al)] = b
return result
}
return []interface{}{a, b}
}