mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-03-18 21:30:01 +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>
142 lines
3.9 KiB
Go
142 lines
3.9 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 utility
|
|
|
|
import (
|
|
"container/list"
|
|
"sync"
|
|
)
|
|
|
|
// EmbeddingLRU is a thread-safe LRU cache for embeddings.
|
|
// The key is a combination of question and embedding ID.
|
|
type EmbeddingLRU struct {
|
|
capacity int
|
|
cache map[string]*list.Element
|
|
list *list.List
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
// entry holds the key and value in the LRU cache.
|
|
type entry struct {
|
|
key string
|
|
value []float64
|
|
}
|
|
|
|
// NewEmbeddingLRU creates a new EmbeddingLRU with the given capacity.
|
|
func NewEmbeddingLRU(capacity int) *EmbeddingLRU {
|
|
return &EmbeddingLRU{
|
|
capacity: capacity,
|
|
cache: make(map[string]*list.Element),
|
|
list: list.New(),
|
|
}
|
|
}
|
|
|
|
// buildKey creates a composite key from question and embedding ID.
|
|
func buildKey(question, embeddingID string) string {
|
|
// Use a delimiter that is unlikely to appear in the strings.
|
|
// If needed, a more robust key generation can be implemented.
|
|
return question + "::" + embeddingID
|
|
}
|
|
|
|
// Get retrieves the embedding for the given question and embedding ID.
|
|
// Returns the embedding and true if found, otherwise nil and false.
|
|
func (lru *EmbeddingLRU) Get(question, embeddingID string) ([]float64, bool) {
|
|
key := buildKey(question, embeddingID)
|
|
lru.mu.RLock()
|
|
defer lru.mu.RUnlock()
|
|
|
|
if elem, ok := lru.cache[key]; ok {
|
|
// Move to front (most recently used)
|
|
lru.list.MoveToFront(elem)
|
|
ent := elem.Value.(*entry)
|
|
// Return a copy to prevent external modification of cached slice
|
|
embedding := make([]float64, len(ent.value))
|
|
copy(embedding, ent.value)
|
|
return embedding, true
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
// Put stores an embedding for the given question and embedding ID.
|
|
// If the key already exists, its value is updated and moved to front.
|
|
// If the cache is at capacity, the least recently used item is evicted.
|
|
func (lru *EmbeddingLRU) Put(question, embeddingID string, embedding []float64) {
|
|
key := buildKey(question, embeddingID)
|
|
lru.mu.Lock()
|
|
defer lru.mu.Unlock()
|
|
|
|
// If key exists, update value and move to front
|
|
if elem, ok := lru.cache[key]; ok {
|
|
lru.list.MoveToFront(elem)
|
|
ent := elem.Value.(*entry)
|
|
// Replace the embedding slice
|
|
ent.value = make([]float64, len(embedding))
|
|
copy(ent.value, embedding)
|
|
return
|
|
}
|
|
|
|
// Add new entry
|
|
ent := &entry{key: key, value: make([]float64, len(embedding))}
|
|
copy(ent.value, embedding)
|
|
elem := lru.list.PushFront(ent)
|
|
lru.cache[key] = elem
|
|
|
|
// Evict if capacity exceeded
|
|
if lru.list.Len() > lru.capacity {
|
|
lru.evictOldest()
|
|
}
|
|
}
|
|
|
|
// evictOldest removes the least recently used item from the cache.
|
|
// Must be called with lock held.
|
|
func (lru *EmbeddingLRU) evictOldest() {
|
|
elem := lru.list.Back()
|
|
if elem != nil {
|
|
lru.list.Remove(elem)
|
|
ent := elem.Value.(*entry)
|
|
delete(lru.cache, ent.key)
|
|
}
|
|
}
|
|
|
|
// Remove removes the embedding for the given question and embedding ID.
|
|
func (lru *EmbeddingLRU) Remove(question, embeddingID string) {
|
|
key := buildKey(question, embeddingID)
|
|
lru.mu.Lock()
|
|
defer lru.mu.Unlock()
|
|
|
|
if elem, ok := lru.cache[key]; ok {
|
|
lru.list.Remove(elem)
|
|
delete(lru.cache, key)
|
|
}
|
|
}
|
|
|
|
// Clear removes all items from the cache.
|
|
func (lru *EmbeddingLRU) Clear() {
|
|
lru.mu.Lock()
|
|
defer lru.mu.Unlock()
|
|
|
|
lru.cache = make(map[string]*list.Element)
|
|
lru.list.Init()
|
|
}
|
|
|
|
// Len returns the number of items in the cache.
|
|
func (lru *EmbeddingLRU) Len() int {
|
|
lru.mu.RLock()
|
|
defer lru.mu.RUnlock()
|
|
return lru.list.Len()
|
|
}
|