mirror of
https://github.com/lukaszraczylo/claude-mnemonic.git
synced 2026-06-09 23:59:40 +00:00
Move from chroma to sqlitevec with local embedding
This commit is contained in:
@@ -0,0 +1,234 @@
|
||||
// Package sqlitevec provides sqlite-vec based vector database integration for claude-mnemonic.
|
||||
package sqlitevec
|
||||
|
||||
// DocType represents the type of document stored in the vector table.
|
||||
type DocType string
|
||||
|
||||
const (
|
||||
DocTypeObservation DocType = "observation"
|
||||
DocTypeSessionSummary DocType = "session_summary"
|
||||
DocTypeUserPrompt DocType = "user_prompt"
|
||||
)
|
||||
|
||||
// Document represents a document to store with vector embedding.
|
||||
type Document struct {
|
||||
ID string
|
||||
Content string
|
||||
Metadata map[string]any
|
||||
}
|
||||
|
||||
// QueryResult represents a search result from vector search.
|
||||
type QueryResult struct {
|
||||
ID string
|
||||
Distance float64
|
||||
Metadata map[string]any
|
||||
}
|
||||
|
||||
// ExtractedIDs contains SQLite IDs extracted from query results, grouped by document type.
|
||||
type ExtractedIDs struct {
|
||||
ObservationIDs []int64
|
||||
SummaryIDs []int64
|
||||
PromptIDs []int64
|
||||
}
|
||||
|
||||
// BuildWhereFilter creates a where filter map for vector queries.
|
||||
// If docType is empty, no doc_type filter is added.
|
||||
func BuildWhereFilter(docType DocType, project string) map[string]interface{} {
|
||||
where := make(map[string]interface{})
|
||||
if docType != "" {
|
||||
where["doc_type"] = string(docType)
|
||||
}
|
||||
if project != "" {
|
||||
where["project"] = project
|
||||
}
|
||||
return where
|
||||
}
|
||||
|
||||
// ExtractIDsByDocType extracts SQLite IDs from query results,
|
||||
// grouped by document type and deduplicated.
|
||||
func ExtractIDsByDocType(results []QueryResult) *ExtractedIDs {
|
||||
ids := &ExtractedIDs{}
|
||||
seenObs := make(map[int64]bool)
|
||||
seenSummary := make(map[int64]bool)
|
||||
seenPrompt := make(map[int64]bool)
|
||||
|
||||
for _, result := range results {
|
||||
sqliteID, ok := result.Metadata["sqlite_id"].(float64)
|
||||
if !ok {
|
||||
// Try int64 directly
|
||||
if id, ok := result.Metadata["sqlite_id"].(int64); ok {
|
||||
sqliteID = float64(id)
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
id := int64(sqliteID)
|
||||
|
||||
docType, _ := result.Metadata["doc_type"].(string)
|
||||
switch docType {
|
||||
case string(DocTypeObservation):
|
||||
if !seenObs[id] {
|
||||
seenObs[id] = true
|
||||
ids.ObservationIDs = append(ids.ObservationIDs, id)
|
||||
}
|
||||
case string(DocTypeSessionSummary):
|
||||
if !seenSummary[id] {
|
||||
seenSummary[id] = true
|
||||
ids.SummaryIDs = append(ids.SummaryIDs, id)
|
||||
}
|
||||
case string(DocTypeUserPrompt):
|
||||
if !seenPrompt[id] {
|
||||
seenPrompt[id] = true
|
||||
ids.PromptIDs = append(ids.PromptIDs, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ids
|
||||
}
|
||||
|
||||
// ExtractObservationIDs extracts observation SQLite IDs from query results,
|
||||
// optionally filtering by project or including global scope.
|
||||
func ExtractObservationIDs(results []QueryResult, project string) []int64 {
|
||||
var ids []int64
|
||||
seen := make(map[int64]bool)
|
||||
|
||||
for _, result := range results {
|
||||
sqliteID, ok := result.Metadata["sqlite_id"].(float64)
|
||||
if !ok {
|
||||
if id, ok := result.Metadata["sqlite_id"].(int64); ok {
|
||||
sqliteID = float64(id)
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
id := int64(sqliteID)
|
||||
|
||||
docType, _ := result.Metadata["doc_type"].(string)
|
||||
if docType != string(DocTypeObservation) {
|
||||
continue
|
||||
}
|
||||
|
||||
if project != "" {
|
||||
proj, _ := result.Metadata["project"].(string)
|
||||
scope, _ := result.Metadata["scope"].(string)
|
||||
if proj != project && scope != "global" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if !seen[id] {
|
||||
seen[id] = true
|
||||
ids = append(ids, id)
|
||||
}
|
||||
}
|
||||
|
||||
return ids
|
||||
}
|
||||
|
||||
// ExtractSummaryIDs extracts session summary SQLite IDs from query results.
|
||||
func ExtractSummaryIDs(results []QueryResult, project string) []int64 {
|
||||
var ids []int64
|
||||
seen := make(map[int64]bool)
|
||||
|
||||
for _, result := range results {
|
||||
sqliteID, ok := result.Metadata["sqlite_id"].(float64)
|
||||
if !ok {
|
||||
if id, ok := result.Metadata["sqlite_id"].(int64); ok {
|
||||
sqliteID = float64(id)
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
id := int64(sqliteID)
|
||||
|
||||
docType, _ := result.Metadata["doc_type"].(string)
|
||||
if docType != string(DocTypeSessionSummary) {
|
||||
continue
|
||||
}
|
||||
|
||||
if project != "" {
|
||||
proj, _ := result.Metadata["project"].(string)
|
||||
if proj != project {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if !seen[id] {
|
||||
seen[id] = true
|
||||
ids = append(ids, id)
|
||||
}
|
||||
}
|
||||
|
||||
return ids
|
||||
}
|
||||
|
||||
// ExtractPromptIDs extracts user prompt SQLite IDs from query results.
|
||||
func ExtractPromptIDs(results []QueryResult, project string) []int64 {
|
||||
var ids []int64
|
||||
seen := make(map[int64]bool)
|
||||
|
||||
for _, result := range results {
|
||||
sqliteID, ok := result.Metadata["sqlite_id"].(float64)
|
||||
if !ok {
|
||||
if id, ok := result.Metadata["sqlite_id"].(int64); ok {
|
||||
sqliteID = float64(id)
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
id := int64(sqliteID)
|
||||
|
||||
docType, _ := result.Metadata["doc_type"].(string)
|
||||
if docType != string(DocTypeUserPrompt) {
|
||||
continue
|
||||
}
|
||||
|
||||
if project != "" {
|
||||
proj, _ := result.Metadata["project"].(string)
|
||||
if proj != project {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if !seen[id] {
|
||||
seen[id] = true
|
||||
ids = append(ids, id)
|
||||
}
|
||||
}
|
||||
|
||||
return ids
|
||||
}
|
||||
|
||||
// Helper functions for metadata manipulation
|
||||
|
||||
func copyMetadata(base map[string]any, key string, value any) map[string]any {
|
||||
result := make(map[string]any, len(base)+1)
|
||||
for k, v := range base {
|
||||
result[k] = v
|
||||
}
|
||||
result[key] = value
|
||||
return result
|
||||
}
|
||||
|
||||
func copyMetadataMulti(base map[string]any, extra map[string]any) map[string]any {
|
||||
result := make(map[string]any, len(base)+len(extra))
|
||||
for k, v := range base {
|
||||
result[k] = v
|
||||
}
|
||||
for k, v := range extra {
|
||||
result[k] = v
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func joinStrings(strs []string, sep string) string {
|
||||
if len(strs) == 0 {
|
||||
return ""
|
||||
}
|
||||
result := strs[0]
|
||||
for i := 1; i < len(strs); i++ {
|
||||
result += sep + strs[i]
|
||||
}
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user