init: initial commit

This commit is contained in:
Blizzard
2026-04-07 17:35:09 +08:00
commit 680ecc320f
129 changed files with 10562 additions and 0 deletions
+93
View File
@@ -0,0 +1,93 @@
package vector
import (
"context"
"fmt"
"engimind/internal/models"
)
// ContextChunk is a search result with source metadata.
type ContextChunk struct {
Text string `json:"text"`
SourceID string `json:"sourceId"`
Score float32 `json:"score"`
}
// RAGService orchestrates embedding + vector search for retrieval.
type RAGService struct {
embedding *EmbeddingService
store *QdrantStore
}
// NewRAGService creates a RAG service.
func NewRAGService(embedding *EmbeddingService, store *QdrantStore) *RAGService {
return &RAGService{embedding: embedding, store: store}
}
// CollectionName returns the Qdrant collection name for a project.
func CollectionName(projectID string) string {
return fmt.Sprintf("engimind_%s", projectID)
}
// IndexDocument chunks and indexes a parsed document.
func (s *RAGService) IndexDocument(ctx context.Context, projectID string, source models.SourceFile, content string, embeddingCfg EmbeddingConfig) error {
colName := CollectionName(projectID)
if err := s.store.EnsureCollection(ctx, colName); err != nil {
return err
}
textChunks := ChunkText(content, 500, 50)
var chunks []Chunk
for i, text := range textChunks {
vec, err := s.embedding.GetEmbedding(
text, embeddingCfg.BaseURL, embeddingCfg.Model,
embeddingCfg.APIKey, embeddingCfg.Provider,
)
if err != nil {
return fmt.Errorf("embed chunk %d: %w", i, err)
}
chunks = append(chunks, Chunk{
ID: fmt.Sprintf("%s-chunk-%d", source.ID, i),
SourceID: source.ID,
Text: text,
Vector: vec,
})
}
return s.store.Insert(ctx, colName, chunks)
}
// SearchContext retrieves relevant text chunks for a query.
func (s *RAGService) SearchContext(ctx context.Context, projectID, question string, topK int, embeddingCfg EmbeddingConfig) ([]ContextChunk, error) {
queryVec, err := s.embedding.GetEmbedding(
question, embeddingCfg.BaseURL, embeddingCfg.Model,
embeddingCfg.APIKey, embeddingCfg.Provider,
)
if err != nil {
return nil, fmt.Errorf("embed query: %w", err)
}
colName := CollectionName(projectID)
results, err := s.store.Search(ctx, colName, queryVec, uint64(topK))
if err != nil {
return nil, err
}
contextChunks := make([]ContextChunk, len(results))
for i, r := range results {
contextChunks[i] = ContextChunk{
Text: r.Text,
SourceID: r.SourceID,
}
}
return contextChunks, nil
}
// EmbeddingConfig holds the config needed to call an embedding API.
type EmbeddingConfig struct {
BaseURL string
Model string
APIKey string
Provider string
}