package main import ( "context" "fmt" "strconv" "github.com/qdrant/go-client/qdrant" ) const collectionName = "knowledge" type entry struct { ID int64 `json:"id"` Note string `json:"note"` Repos []string `json:"repos,omitempty"` Host string `json:"host,omitempty"` Tags []string `json:"tags,omitempty"` User string `json:"user,omitempty"` Context string `json:"context,omitempty"` Created string `json:"created"` } type searchResult struct { Entry entry `json:",inline"` Score float32 `json:"score"` } type qdrantStore struct { client *qdrant.Client embedder *embedder } func newQdrantStore(client *qdrant.Client, embedder *embedder) *qdrantStore { return &qdrantStore{client: client, embedder: embedder} } func (s *qdrantStore) ensureCollection(ctx context.Context) error { exists, err := s.client.CollectionExists(ctx, collectionName) if err != nil { return fmt.Errorf("check collection exists: %w", err) } if exists { return nil } err = s.client.CreateCollection(ctx, &qdrant.CreateCollection{ CollectionName: collectionName, VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{ Size: 1536, Distance: qdrant.Distance_Cosine, }), }) if err != nil { return fmt.Errorf("create collection: %w", err) } return nil } func (s *qdrantStore) index(ctx context.Context, id int64, note string) error { vec, err := s.embedder.embedText(ctx, note) if err != nil { return fmt.Errorf("embed text: %w", err) } payload := map[string]*qdrant.Value{ "id": qdrant.NewValueString(strconv.FormatInt(id, 10)), "content": qdrant.NewValueString(note), } _, err = s.client.Upsert(ctx, &qdrant.UpsertPoints{ CollectionName: collectionName, Points: []*qdrant.PointStruct{ { Id: qdrant.NewIDNum(uint64(id)), Vectors: qdrant.NewVectors(vec...), Payload: payload, }, }, }) if err != nil { return fmt.Errorf("upsert point: %w", err) } return nil } func (s *qdrantStore) search(ctx context.Context, query string, limit uint64) ([]searchResult, error) { vec, err := s.embedder.embedText(ctx, query) if err != nil { return nil, fmt.Errorf("embed query: %w", err) } if limit == 0 { limit = 5 } results, err := s.client.Query(ctx, &qdrant.QueryPoints{ CollectionName: collectionName, Query: qdrant.NewQueryDense(vec), Limit: &limit, WithPayload: qdrant.NewWithPayload(true), }) if err != nil { return nil, fmt.Errorf("query points: %w", err) } var searchResults []searchResult for _, pt := range results { r := searchResult{ Score: pt.Score, } if payload := pt.GetPayload(); payload != nil { if v, ok := payload["id"]; ok { if n, err := strconv.ParseInt(v.GetStringValue(), 10, 64); err == nil { r.Entry.ID = n } } } searchResults = append(searchResults, r) } return searchResults, nil }