mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-03-16 04:17:49 +08:00
# RAGFlow Go Implementation Plan 🚀 This repository tracks the progress of porting RAGFlow to Go. We'll implement core features and provide performance comparisons between Python and Go versions. ## Implementation Checklist - [x] User Management APIs - [x] Dataset Management Operations - [x] Retrieval Test - [x] Chat Management Operations - [x] Infinity Go SDK --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com> Co-authored-by: Yingfeng Zhang <yingfeng.zhang@gmail.com>
206 lines
5.5 KiB
Go
206 lines
5.5 KiB
Go
//
|
|
// Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
|
|
package infinity
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"ragflow/internal/engine/types"
|
|
)
|
|
|
|
// SearchRequest Infinity search request (legacy, kept for backward compatibility)
|
|
type SearchRequest struct {
|
|
TableName string
|
|
ColumnNames []string
|
|
MatchText *MatchTextExpr
|
|
MatchDense *MatchDenseExpr
|
|
Fusion *FusionExpr
|
|
Offset int
|
|
Limit int
|
|
Filter map[string]interface{}
|
|
}
|
|
|
|
// SearchResponse Infinity search response
|
|
type SearchResponse struct {
|
|
Rows []map[string]interface{}
|
|
Total int64
|
|
}
|
|
|
|
// MatchTextExpr text match expression
|
|
type MatchTextExpr struct {
|
|
Fields []string
|
|
MatchingText string
|
|
TopN int
|
|
ExtraOptions map[string]interface{}
|
|
}
|
|
|
|
// MatchDenseExpr vector match expression
|
|
type MatchDenseExpr struct {
|
|
VectorColumnName string
|
|
EmbeddingData []float64
|
|
EmbeddingDataType string
|
|
DistanceType string
|
|
TopN int
|
|
ExtraOptions map[string]interface{}
|
|
}
|
|
|
|
// FusionExpr fusion expression
|
|
type FusionExpr struct {
|
|
Method string
|
|
TopN int
|
|
Weights []float64
|
|
FusionParams map[string]interface{}
|
|
}
|
|
|
|
// Search executes search (supports both unified engine.SearchRequest and legacy SearchRequest)
|
|
func (e *infinityEngine) Search(ctx context.Context, req interface{}) (interface{}, error) {
|
|
switch searchReq := req.(type) {
|
|
case *types.SearchRequest:
|
|
return e.searchUnified(ctx, searchReq)
|
|
case *SearchRequest:
|
|
return e.searchLegacy(ctx, searchReq)
|
|
default:
|
|
return nil, fmt.Errorf("invalid search request type: %T", req)
|
|
}
|
|
}
|
|
|
|
// searchUnified handles the unified engine.SearchRequest
|
|
func (e *infinityEngine) searchUnified(ctx context.Context, req *types.SearchRequest) (*types.SearchResponse, error) {
|
|
if len(req.IndexNames) == 0 {
|
|
return nil, fmt.Errorf("index names cannot be empty")
|
|
}
|
|
|
|
// For Infinity, we use the first index name as table name
|
|
tableName := req.IndexNames[0]
|
|
|
|
// Get retrieval parameters with defaults
|
|
similarityThreshold := req.SimilarityThreshold
|
|
if similarityThreshold <= 0 {
|
|
similarityThreshold = 0.1
|
|
}
|
|
|
|
topK := req.TopK
|
|
if topK <= 0 {
|
|
topK = 1024
|
|
}
|
|
|
|
vectorSimilarityWeight := req.VectorSimilarityWeight
|
|
if vectorSimilarityWeight < 0 || vectorSimilarityWeight > 1 {
|
|
vectorSimilarityWeight = 0.3
|
|
}
|
|
|
|
pageSize := req.Size
|
|
if pageSize <= 0 {
|
|
pageSize = 30
|
|
}
|
|
|
|
offset := (req.Page - 1) * pageSize
|
|
if offset < 0 {
|
|
offset = 0
|
|
}
|
|
|
|
// Build search request
|
|
searchReq := &SearchRequest{
|
|
TableName: tableName,
|
|
Limit: pageSize,
|
|
Offset: offset,
|
|
Filter: buildInfinityFilters(req.KbIDs, req.DocIDs),
|
|
}
|
|
|
|
// Add text match (question is always required)
|
|
searchReq.MatchText = &MatchTextExpr{
|
|
Fields: []string{"title_tks", "content_ltks"},
|
|
MatchingText: req.Question,
|
|
TopN: topK,
|
|
}
|
|
|
|
// Add vector match if vector is provided and not keyword-only mode
|
|
if !req.KeywordOnly && len(req.Vector) > 0 {
|
|
fieldName := buildInfinityVectorFieldName(req.Vector)
|
|
searchReq.MatchDense = &MatchDenseExpr{
|
|
VectorColumnName: fieldName,
|
|
EmbeddingData: req.Vector,
|
|
EmbeddingDataType: "float",
|
|
DistanceType: "cosine",
|
|
TopN: topK,
|
|
ExtraOptions: map[string]interface{}{
|
|
"similarity": similarityThreshold,
|
|
},
|
|
}
|
|
// Infinity uses weighted_sum fusion with weights
|
|
searchReq.Fusion = &FusionExpr{
|
|
Method: "weighted_sum",
|
|
TopN: topK,
|
|
Weights: []float64{
|
|
1.0 - vectorSimilarityWeight, // text weight
|
|
vectorSimilarityWeight, // vector weight
|
|
},
|
|
}
|
|
}
|
|
|
|
// Execute the actual search (would call Infinity SDK here)
|
|
// For now, return not implemented
|
|
return nil, fmt.Errorf("infinity search unified not implemented: waiting for official Go SDK")
|
|
}
|
|
|
|
// searchLegacy handles the legacy infinity.SearchRequest (backward compatibility)
|
|
func (e *infinityEngine) searchLegacy(ctx context.Context, req *SearchRequest) (*SearchResponse, error) {
|
|
// This would contain the actual Infinity search implementation
|
|
return nil, fmt.Errorf("infinity search legacy not implemented: waiting for official Go SDK")
|
|
}
|
|
|
|
// buildInfinityFilters builds filter conditions for Infinity
|
|
func buildInfinityFilters(kbIDs []string, docIDs []string) map[string]interface{} {
|
|
filters := make(map[string]interface{})
|
|
|
|
// kb_id filter
|
|
if len(kbIDs) > 0 {
|
|
if len(kbIDs) == 1 {
|
|
filters["kb_id"] = kbIDs[0]
|
|
} else {
|
|
filters["kb_id"] = kbIDs
|
|
}
|
|
}
|
|
|
|
// doc_id filter
|
|
if len(docIDs) > 0 {
|
|
if len(docIDs) == 1 {
|
|
filters["doc_id"] = docIDs[0]
|
|
} else {
|
|
filters["doc_id"] = docIDs
|
|
}
|
|
}
|
|
|
|
// available_int filter (default to 1 for available chunks)
|
|
filters["available_int"] = 1
|
|
|
|
return filters
|
|
}
|
|
|
|
// buildInfinityVectorFieldName builds vector field name based on dimension
|
|
func buildInfinityVectorFieldName(vector []float64) string {
|
|
dimension := len(vector)
|
|
var fieldBuilder strings.Builder
|
|
fieldBuilder.WriteString("q_")
|
|
fieldBuilder.WriteString(strconv.Itoa(dimension))
|
|
fieldBuilder.WriteString("_vec")
|
|
return fieldBuilder.String()
|
|
}
|