File size: 2,160 Bytes
119e586
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
(ns snapkitty.lisp.knowledge.qdrant
  "Qdrant vector database client — ENFORCED auth"
  (:require [promesa.core :as p]
            ["node-fetch" :default fetch]))

(defn http-request [cfg method path {:keys [body]}]
  "Make authenticated HTTP request to Qdrant — REQUIRES API KEY"
  (let [url (str (:qdrant-url cfg) path)
        headers {"Content-Type" "application/json"
                 "api-key" (:qdrant-key cfg)}
        options (clj->js (merge {:method method
                                 :headers headers}
                                (when body {:body (js/JSON.stringify body)})))]
    (p/let [response (fetch url options)]
      (if (.-ok response)
        (.json response)
        (throw (ex-info (str "Qdrant error: " (.-status response))
                        {:status (.-status response)}))))))

(defn ensure-collection! [cfg]
  "Create collection if it doesn't exist"
  (let [coll-name (:collection-name cfg)]
    (p/let [_ (http-request cfg "PUT"
                            (str "/collections/" coll-name)
                            {:body {:vectors {:size 384
                                             :distance "Cosine"}}})]
      (js/console.log (str "[qdrant] Collection '" coll-name "' ready")))))

(defn upsert-point! [cfg coll-id vector {:keys [id payload]}]
  "Insert/update vector with metadata"
  (http-request cfg "PUT"
                (str "/collections/" coll-id "/points")
                {:body {:points [{:id id
                                 :vector vector
                                 :payload payload}]}}))

(defn search-points [cfg coll-id query-vector {:keys [limit filter]}]
  "Search for nearest vectors"
  (http-request cfg "POST"
                (str "/collections/" coll-id "/points/search")
                {:body {:vector query-vector
                       :limit (or limit 10)
                       :filter filter}}))

(defn delete-point! [cfg coll-id point-id]
  "Delete a point by ID"
  (http-request cfg "POST"
                (str "/collections/" coll-id "/points/delete")
                {:body {:points_selector {:ids [point-id]}}}))