@xdarkicex/openclaw-memory-libravdb 1.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -0
- package/docs/README.md +14 -0
- package/docs/architecture-decisions/README.md +6 -0
- package/docs/architecture-decisions/adr-001-onnx-over-ollama.md +21 -0
- package/docs/architecture-decisions/adr-002-libravdb-over-lancedb.md +19 -0
- package/docs/architecture-decisions/adr-003-convex-gating-over-threshold.md +27 -0
- package/docs/architecture-decisions/adr-004-sidecar-over-native-ts.md +21 -0
- package/docs/architecture.md +188 -0
- package/docs/contributing.md +76 -0
- package/docs/dependencies.md +38 -0
- package/docs/embedding-profiles.md +42 -0
- package/docs/gating.md +329 -0
- package/docs/implementation.md +381 -0
- package/docs/installation.md +272 -0
- package/docs/mathematics.md +695 -0
- package/docs/models.md +63 -0
- package/docs/problem.md +64 -0
- package/docs/security.md +86 -0
- package/openclaw.plugin.json +84 -0
- package/package.json +41 -0
- package/scripts/build-sidecar.sh +30 -0
- package/scripts/postinstall.js +169 -0
- package/scripts/setup.sh +20 -0
- package/scripts/setup.ts +505 -0
- package/scripts/sidecar-release.d.ts +4 -0
- package/scripts/sidecar-release.js +17 -0
- package/sidecar/cmd/inspect_onnx/main.go +105 -0
- package/sidecar/compact/gate.go +273 -0
- package/sidecar/compact/gate_test.go +85 -0
- package/sidecar/compact/summarize.go +345 -0
- package/sidecar/compact/summarize_test.go +319 -0
- package/sidecar/compact/tokens.go +11 -0
- package/sidecar/config/config.go +119 -0
- package/sidecar/config/config_test.go +75 -0
- package/sidecar/embed/engine.go +696 -0
- package/sidecar/embed/engine_test.go +349 -0
- package/sidecar/embed/matryoshka.go +93 -0
- package/sidecar/embed/matryoshka_test.go +150 -0
- package/sidecar/embed/onnx_local.go +319 -0
- package/sidecar/embed/onnx_local_test.go +159 -0
- package/sidecar/embed/profile_contract_test.go +71 -0
- package/sidecar/embed/profile_eval_test.go +923 -0
- package/sidecar/embed/profiles.go +39 -0
- package/sidecar/go.mod +21 -0
- package/sidecar/go.sum +30 -0
- package/sidecar/health/check.go +33 -0
- package/sidecar/health/check_test.go +55 -0
- package/sidecar/main.go +151 -0
- package/sidecar/model/encoder.go +222 -0
- package/sidecar/model/registry.go +262 -0
- package/sidecar/model/registry_test.go +102 -0
- package/sidecar/model/seq2seq.go +133 -0
- package/sidecar/server/rpc.go +343 -0
- package/sidecar/server/rpc_test.go +350 -0
- package/sidecar/server/transport.go +160 -0
- package/sidecar/store/libravdb.go +676 -0
- package/sidecar/store/libravdb_test.go +472 -0
- package/sidecar/summarize/engine.go +360 -0
- package/sidecar/summarize/engine_test.go +148 -0
- package/sidecar/summarize/onnx_local.go +494 -0
- package/sidecar/summarize/onnx_local_test.go +48 -0
- package/sidecar/summarize/profiles.go +52 -0
- package/sidecar/summarize/tokenizer.go +13 -0
- package/sidecar/summarize/tokenizer_hf.go +76 -0
- package/sidecar/summarize/util.go +13 -0
- package/src/cli.ts +205 -0
- package/src/context-engine.ts +195 -0
- package/src/index.ts +27 -0
- package/src/memory-provider.ts +24 -0
- package/src/openclaw-plugin-sdk.d.ts +53 -0
- package/src/plugin-runtime.ts +67 -0
- package/src/recall-cache.ts +34 -0
- package/src/recall-utils.ts +22 -0
- package/src/rpc.ts +84 -0
- package/src/scoring.ts +58 -0
- package/src/sidecar.ts +506 -0
- package/src/tokens.ts +36 -0
- package/src/types.ts +146 -0
- package/tsconfig.json +20 -0
- package/tsconfig.tests.json +12 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
package compact
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"context"
|
|
6
|
+
"errors"
|
|
7
|
+
"log"
|
|
8
|
+
"testing"
|
|
9
|
+
|
|
10
|
+
"github.com/xDarkicex/openclaw-memory-libravdb/sidecar/store"
|
|
11
|
+
"github.com/xDarkicex/openclaw-memory-libravdb/sidecar/summarize"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
type fakeStore struct {
|
|
15
|
+
results []store.SearchResult
|
|
16
|
+
insertCalls []insertCall
|
|
17
|
+
deleteCalls []deleteCall
|
|
18
|
+
deleteErr error
|
|
19
|
+
listErr error
|
|
20
|
+
insertErr error
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type insertCall struct {
|
|
24
|
+
collection string
|
|
25
|
+
id string
|
|
26
|
+
text string
|
|
27
|
+
meta map[string]any
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type deleteCall struct {
|
|
31
|
+
collection string
|
|
32
|
+
ids []string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type fakeSummarizer struct {
|
|
36
|
+
summaries []summarize.Summary
|
|
37
|
+
err error
|
|
38
|
+
calls [][]summarize.Turn
|
|
39
|
+
mode string
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
func (f *fakeStore) ListByMeta(_ context.Context, collection, key, value string) ([]store.SearchResult, error) {
|
|
43
|
+
if f.listErr != nil {
|
|
44
|
+
return nil, f.listErr
|
|
45
|
+
}
|
|
46
|
+
return append([]store.SearchResult(nil), f.results...), nil
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
func (f *fakeStore) InsertText(_ context.Context, collection, id, text string, meta map[string]any) error {
|
|
50
|
+
if f.insertErr != nil {
|
|
51
|
+
return f.insertErr
|
|
52
|
+
}
|
|
53
|
+
f.insertCalls = append(f.insertCalls, insertCall{
|
|
54
|
+
collection: collection,
|
|
55
|
+
id: id,
|
|
56
|
+
text: text,
|
|
57
|
+
meta: cloneMeta(meta),
|
|
58
|
+
})
|
|
59
|
+
return nil
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
func (f *fakeStore) DeleteBatch(_ context.Context, collection string, ids []string) error {
|
|
63
|
+
f.deleteCalls = append(f.deleteCalls, deleteCall{
|
|
64
|
+
collection: collection,
|
|
65
|
+
ids: append([]string(nil), ids...),
|
|
66
|
+
})
|
|
67
|
+
return f.deleteErr
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
func (f *fakeSummarizer) Summarize(_ context.Context, turns []summarize.Turn, _ summarize.SummaryOpts) (summarize.Summary, error) {
|
|
71
|
+
f.calls = append(f.calls, append([]summarize.Turn(nil), turns...))
|
|
72
|
+
if f.err != nil {
|
|
73
|
+
return summarize.Summary{}, f.err
|
|
74
|
+
}
|
|
75
|
+
index := len(f.calls) - 1
|
|
76
|
+
if index < len(f.summaries) {
|
|
77
|
+
return f.summaries[index], nil
|
|
78
|
+
}
|
|
79
|
+
sourceIDs := make([]string, 0, len(turns))
|
|
80
|
+
for _, turn := range turns {
|
|
81
|
+
sourceIDs = append(sourceIDs, turn.ID)
|
|
82
|
+
}
|
|
83
|
+
return summarize.Summary{
|
|
84
|
+
Text: "summary",
|
|
85
|
+
SourceIDs: sourceIDs,
|
|
86
|
+
Method: "extractive",
|
|
87
|
+
TokenCount: 3,
|
|
88
|
+
Confidence: 0.8,
|
|
89
|
+
}, nil
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
func (f *fakeSummarizer) Profile() summarize.Profile { return summarize.Profile{Backend: "extractive"} }
|
|
93
|
+
func (f *fakeSummarizer) Warmup(context.Context) error { return nil }
|
|
94
|
+
func (f *fakeSummarizer) Unload() {}
|
|
95
|
+
func (f *fakeSummarizer) Close() error { return nil }
|
|
96
|
+
func (f *fakeSummarizer) Ready() bool { return true }
|
|
97
|
+
func (f *fakeSummarizer) Reason() string { return "" }
|
|
98
|
+
func (f *fakeSummarizer) Mode() string {
|
|
99
|
+
if f.mode != "" {
|
|
100
|
+
return f.mode
|
|
101
|
+
}
|
|
102
|
+
return "extractive"
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
func TestCompactSessionSkipsBelowThresholdWithoutForce(t *testing.T) {
|
|
106
|
+
st := &fakeStore{
|
|
107
|
+
results: []store.SearchResult{
|
|
108
|
+
{ID: "a", Text: "alpha", Metadata: map[string]any{"sessionId": "s1", "ts": int64(10)}},
|
|
109
|
+
{ID: "b", Text: "beta", Metadata: map[string]any{"sessionId": "s1", "ts": int64(20)}},
|
|
110
|
+
},
|
|
111
|
+
}
|
|
112
|
+
sum := &fakeSummarizer{}
|
|
113
|
+
|
|
114
|
+
got, err := CompactSession(context.Background(), st, sum, nil, "s1", false, 20)
|
|
115
|
+
if err != nil {
|
|
116
|
+
t.Fatalf("CompactSession() error = %v", err)
|
|
117
|
+
}
|
|
118
|
+
if got.DidCompact {
|
|
119
|
+
t.Fatalf("expected no compaction below threshold, got %+v", got)
|
|
120
|
+
}
|
|
121
|
+
if len(sum.calls) != 0 || len(st.insertCalls) != 0 || len(st.deleteCalls) != 0 {
|
|
122
|
+
t.Fatalf("expected no summarizer/store writes, got calls=%d inserts=%d deletes=%d", len(sum.calls), len(st.insertCalls), len(st.deleteCalls))
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
func TestCompactSessionPartitionsDeterministicallyByTimestamp(t *testing.T) {
|
|
127
|
+
st := &fakeStore{
|
|
128
|
+
results: []store.SearchResult{
|
|
129
|
+
{ID: "c", Text: "third", Metadata: map[string]any{"sessionId": "s1", "ts": int64(30)}},
|
|
130
|
+
{ID: "a", Text: "first", Metadata: map[string]any{"sessionId": "s1", "ts": int64(10)}},
|
|
131
|
+
{ID: "d", Text: "fourth", Metadata: map[string]any{"sessionId": "s1", "ts": int64(40)}},
|
|
132
|
+
{ID: "b", Text: "second", Metadata: map[string]any{"sessionId": "s1", "ts": int64(20)}},
|
|
133
|
+
},
|
|
134
|
+
}
|
|
135
|
+
sum := &fakeSummarizer{
|
|
136
|
+
summaries: []summarize.Summary{
|
|
137
|
+
{Text: "summary-1", SourceIDs: []string{"a", "b"}, Method: "extractive", TokenCount: 2, Confidence: 0.6},
|
|
138
|
+
{Text: "summary-2", SourceIDs: []string{"c", "d"}, Method: "extractive", TokenCount: 2, Confidence: 0.8},
|
|
139
|
+
},
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
got, err := CompactSession(context.Background(), st, sum, nil, "s1", true, 2)
|
|
143
|
+
if err != nil {
|
|
144
|
+
t.Fatalf("CompactSession() error = %v", err)
|
|
145
|
+
}
|
|
146
|
+
if !got.DidCompact || got.ClustersFormed != 2 || got.TurnsRemoved != 4 {
|
|
147
|
+
t.Fatalf("unexpected result: %+v", got)
|
|
148
|
+
}
|
|
149
|
+
if got.SummaryMethod != "extractive" {
|
|
150
|
+
t.Fatalf("unexpected summary method: %+v", got)
|
|
151
|
+
}
|
|
152
|
+
if got.MeanConfidence != 0.7 {
|
|
153
|
+
t.Fatalf("expected mean confidence 0.7, got %f", got.MeanConfidence)
|
|
154
|
+
}
|
|
155
|
+
if len(sum.calls) != 2 {
|
|
156
|
+
t.Fatalf("expected 2 summarize calls, got %d", len(sum.calls))
|
|
157
|
+
}
|
|
158
|
+
assertTurnIDs(t, sum.calls[0], []string{"a", "b"})
|
|
159
|
+
assertTurnIDs(t, sum.calls[1], []string{"c", "d"})
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
func TestCompactSessionInsertsBeforeDeleteAndPreservesDataOnDeleteFailure(t *testing.T) {
|
|
163
|
+
st := &fakeStore{
|
|
164
|
+
results: []store.SearchResult{
|
|
165
|
+
{ID: "a", Text: "alpha", Metadata: map[string]any{"sessionId": "s1", "ts": int64(10), "userId": "u1"}},
|
|
166
|
+
{ID: "b", Text: "beta", Metadata: map[string]any{"sessionId": "s1", "ts": int64(20), "userId": "u1"}},
|
|
167
|
+
},
|
|
168
|
+
deleteErr: errors.New("boom"),
|
|
169
|
+
}
|
|
170
|
+
sum := &fakeSummarizer{
|
|
171
|
+
summaries: []summarize.Summary{
|
|
172
|
+
{Text: "summary-1", SourceIDs: []string{"a", "b"}, Method: "extractive", TokenCount: 5, Confidence: 0.75},
|
|
173
|
+
},
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
got, err := CompactSession(context.Background(), st, sum, nil, "s1", true, 20)
|
|
177
|
+
if err != nil {
|
|
178
|
+
t.Fatalf("CompactSession() error = %v", err)
|
|
179
|
+
}
|
|
180
|
+
if len(st.insertCalls) != 1 {
|
|
181
|
+
t.Fatalf("expected summary insert before delete, got %d insert calls", len(st.insertCalls))
|
|
182
|
+
}
|
|
183
|
+
if len(st.deleteCalls) != 1 {
|
|
184
|
+
t.Fatalf("expected delete attempt after insert, got %d delete calls", len(st.deleteCalls))
|
|
185
|
+
}
|
|
186
|
+
if got.TurnsRemoved != 0 {
|
|
187
|
+
t.Fatalf("expected no removed turns when delete fails, got %+v", got)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
meta := st.insertCalls[0].meta
|
|
191
|
+
if meta["type"] != "summary" {
|
|
192
|
+
t.Fatalf("expected summary metadata type, got %+v", meta)
|
|
193
|
+
}
|
|
194
|
+
if meta["method"] != "extractive" {
|
|
195
|
+
t.Fatalf("expected method metadata, got %+v", meta)
|
|
196
|
+
}
|
|
197
|
+
if meta["confidence"] != 0.75 {
|
|
198
|
+
t.Fatalf("expected confidence metadata, got %+v", meta)
|
|
199
|
+
}
|
|
200
|
+
if meta["decay_rate"] != 0.25 {
|
|
201
|
+
t.Fatalf("expected decay rate metadata, got %+v", meta)
|
|
202
|
+
}
|
|
203
|
+
if meta["userId"] != "u1" {
|
|
204
|
+
t.Fatalf("expected userId carried forward, got %+v", meta)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
sourceIDs, ok := meta["source_ids"].([]string)
|
|
208
|
+
if !ok {
|
|
209
|
+
t.Fatalf("expected source_ids to be []string, got %T", meta["source_ids"])
|
|
210
|
+
}
|
|
211
|
+
if len(sourceIDs) != 2 || sourceIDs[0] != "a" || sourceIDs[1] != "b" {
|
|
212
|
+
t.Fatalf("unexpected source_ids: %+v", sourceIDs)
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
func TestCompactSessionPreservesSourceTurnsWhenInsertFails(t *testing.T) {
|
|
217
|
+
st := &fakeStore{
|
|
218
|
+
results: []store.SearchResult{
|
|
219
|
+
{ID: "a", Text: "alpha", Metadata: map[string]any{"sessionId": "s1", "ts": int64(10)}},
|
|
220
|
+
{ID: "b", Text: "beta", Metadata: map[string]any{"sessionId": "s1", "ts": int64(20)}},
|
|
221
|
+
},
|
|
222
|
+
insertErr: errors.New("insert failed"),
|
|
223
|
+
}
|
|
224
|
+
sum := &fakeSummarizer{}
|
|
225
|
+
|
|
226
|
+
_, err := CompactSession(context.Background(), st, sum, nil, "s1", true, 20)
|
|
227
|
+
if err == nil {
|
|
228
|
+
t.Fatalf("expected insert failure")
|
|
229
|
+
}
|
|
230
|
+
if len(st.deleteCalls) != 0 {
|
|
231
|
+
t.Fatalf("expected no delete call when insert fails, got %d", len(st.deleteCalls))
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
func TestCompactSessionRoutesHighGatingClustersToAbstractive(t *testing.T) {
|
|
236
|
+
st := &fakeStore{
|
|
237
|
+
results: []store.SearchResult{
|
|
238
|
+
{ID: "a", Text: "alpha", Metadata: map[string]any{"sessionId": "s1", "ts": int64(10), "gating_score": 0.8}},
|
|
239
|
+
{ID: "b", Text: "beta", Metadata: map[string]any{"sessionId": "s1", "ts": int64(20), "gating_score": 0.7}},
|
|
240
|
+
},
|
|
241
|
+
}
|
|
242
|
+
extractive := &fakeSummarizer{
|
|
243
|
+
summaries: []summarize.Summary{{Text: "extractive-summary", Method: "extractive", TokenCount: 2, Confidence: 0.5}},
|
|
244
|
+
mode: "extractive",
|
|
245
|
+
}
|
|
246
|
+
abstractive := &fakeSummarizer{
|
|
247
|
+
summaries: []summarize.Summary{{Text: "abstractive-summary", Method: "onnx-t5", TokenCount: 3, Confidence: 0.9}},
|
|
248
|
+
mode: "onnx-local",
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
got, err := CompactSession(context.Background(), st, extractive, abstractive, "s1", true, 20)
|
|
252
|
+
if err != nil {
|
|
253
|
+
t.Fatalf("CompactSession() error = %v", err)
|
|
254
|
+
}
|
|
255
|
+
if !got.DidCompact {
|
|
256
|
+
t.Fatalf("expected compaction, got %+v", got)
|
|
257
|
+
}
|
|
258
|
+
if len(abstractive.calls) != 1 {
|
|
259
|
+
t.Fatalf("expected abstractive summarizer to be used, got %d calls", len(abstractive.calls))
|
|
260
|
+
}
|
|
261
|
+
if len(extractive.calls) != 0 {
|
|
262
|
+
t.Fatalf("expected extractive summarizer to be skipped, got %d calls", len(extractive.calls))
|
|
263
|
+
}
|
|
264
|
+
if got.SummaryMethod != "onnx-t5" {
|
|
265
|
+
t.Fatalf("expected onnx-t5 method, got %+v", got)
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
func TestCompactSessionRoutesMissingGatingScoreToExtractiveAndLogsDecision(t *testing.T) {
|
|
270
|
+
st := &fakeStore{
|
|
271
|
+
results: []store.SearchResult{
|
|
272
|
+
{ID: "a", Text: "alpha", Metadata: map[string]any{"sessionId": "s1", "ts": int64(10)}},
|
|
273
|
+
{ID: "b", Text: "beta", Metadata: map[string]any{"sessionId": "s1", "ts": int64(20)}},
|
|
274
|
+
},
|
|
275
|
+
}
|
|
276
|
+
extractive := &fakeSummarizer{
|
|
277
|
+
summaries: []summarize.Summary{{Text: "extractive-summary", Method: "extractive", TokenCount: 2, Confidence: 0.5}},
|
|
278
|
+
mode: "extractive",
|
|
279
|
+
}
|
|
280
|
+
abstractive := &fakeSummarizer{
|
|
281
|
+
summaries: []summarize.Summary{{Text: "abstractive-summary", Method: "onnx-t5", TokenCount: 3, Confidence: 0.9}},
|
|
282
|
+
mode: "onnx-local",
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
var buf bytes.Buffer
|
|
286
|
+
prevWriter := log.Writer()
|
|
287
|
+
log.SetOutput(&buf)
|
|
288
|
+
defer log.SetOutput(prevWriter)
|
|
289
|
+
|
|
290
|
+
got, err := CompactSession(context.Background(), st, extractive, abstractive, "s1", true, 20)
|
|
291
|
+
if err != nil {
|
|
292
|
+
t.Fatalf("CompactSession() error = %v", err)
|
|
293
|
+
}
|
|
294
|
+
if !got.DidCompact {
|
|
295
|
+
t.Fatalf("expected compaction, got %+v", got)
|
|
296
|
+
}
|
|
297
|
+
if len(extractive.calls) != 1 {
|
|
298
|
+
t.Fatalf("expected extractive summarizer to be used, got %d calls", len(extractive.calls))
|
|
299
|
+
}
|
|
300
|
+
if len(abstractive.calls) != 0 {
|
|
301
|
+
t.Fatalf("expected abstractive summarizer to be skipped, got %d calls", len(abstractive.calls))
|
|
302
|
+
}
|
|
303
|
+
logged := buf.String()
|
|
304
|
+
if !bytes.Contains([]byte(logged), []byte("cluster_id=0")) || !bytes.Contains([]byte(logged), []byte("mean_gating_score=0.000")) || !bytes.Contains([]byte(logged), []byte("summarizer_used=extractive")) {
|
|
305
|
+
t.Fatalf("expected routing telemetry log, got %q", logged)
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
func assertTurnIDs(t *testing.T, turns []summarize.Turn, want []string) {
|
|
310
|
+
t.Helper()
|
|
311
|
+
if len(turns) != len(want) {
|
|
312
|
+
t.Fatalf("unexpected turns length: got %d want %d", len(turns), len(want))
|
|
313
|
+
}
|
|
314
|
+
for i, turn := range turns {
|
|
315
|
+
if turn.ID != want[i] {
|
|
316
|
+
t.Fatalf("unexpected turn order at %d: got %q want %q", i, turn.ID, want[i])
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
package config
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"os/user"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"strconv"
|
|
8
|
+
"strings"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
type Config struct {
|
|
12
|
+
DBPath string
|
|
13
|
+
ONNXRuntimePath string
|
|
14
|
+
EmbeddingBackend string
|
|
15
|
+
EmbeddingProfile string
|
|
16
|
+
FallbackProfile string
|
|
17
|
+
EmbeddingModelPath string
|
|
18
|
+
EmbeddingTokenizerPath string
|
|
19
|
+
EmbeddingDimensions int
|
|
20
|
+
EmbeddingNormalize bool
|
|
21
|
+
SummarizerBackend string
|
|
22
|
+
SummarizerProfile string
|
|
23
|
+
SummarizerRuntimePath string
|
|
24
|
+
SummarizerModelPath string
|
|
25
|
+
SummarizerTokenizerPath string
|
|
26
|
+
SummarizerModel string
|
|
27
|
+
SummarizerEndpoint string
|
|
28
|
+
GatingW1c float64
|
|
29
|
+
GatingW2c float64
|
|
30
|
+
GatingW3c float64
|
|
31
|
+
GatingW1t float64
|
|
32
|
+
GatingW2t float64
|
|
33
|
+
GatingW3t float64
|
|
34
|
+
GatingTechNorm float64
|
|
35
|
+
GatingThreshold float64
|
|
36
|
+
GatingCentroidK int
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
func FromEnv() Config {
|
|
40
|
+
return Config{
|
|
41
|
+
DBPath: envOrDefault("LIBRAVDB_DB_PATH", defaultDBPath()),
|
|
42
|
+
ONNXRuntimePath: os.Getenv("LIBRAVDB_ONNX_RUNTIME"),
|
|
43
|
+
EmbeddingBackend: envOrDefault("LIBRAVDB_EMBEDDING_BACKEND", "bundled"),
|
|
44
|
+
EmbeddingProfile: envOrDefault("LIBRAVDB_EMBEDDING_PROFILE", "nomic-embed-text-v1.5"),
|
|
45
|
+
FallbackProfile: envOrDefault("LIBRAVDB_FALLBACK_PROFILE", "all-minilm-l6-v2"),
|
|
46
|
+
EmbeddingModelPath: os.Getenv("LIBRAVDB_EMBEDDING_MODEL"),
|
|
47
|
+
EmbeddingTokenizerPath: os.Getenv("LIBRAVDB_EMBEDDING_TOKENIZER"),
|
|
48
|
+
EmbeddingDimensions: envIntOrDefault("LIBRAVDB_EMBEDDING_DIMENSIONS", 0),
|
|
49
|
+
EmbeddingNormalize: envBoolOrDefault("LIBRAVDB_EMBEDDING_NORMALIZE", true),
|
|
50
|
+
SummarizerBackend: envOrDefault("LIBRAVDB_SUMMARIZER_BACKEND", "bundled"),
|
|
51
|
+
SummarizerProfile: strings.TrimSpace(os.Getenv("LIBRAVDB_SUMMARIZER_PROFILE")),
|
|
52
|
+
SummarizerRuntimePath: os.Getenv("LIBRAVDB_SUMMARIZER_RUNTIME"),
|
|
53
|
+
SummarizerModelPath: os.Getenv("LIBRAVDB_SUMMARIZER_MODEL_PATH"),
|
|
54
|
+
SummarizerTokenizerPath: os.Getenv("LIBRAVDB_SUMMARIZER_TOKENIZER"),
|
|
55
|
+
SummarizerModel: os.Getenv("LIBRAVDB_SUMMARIZER_MODEL"),
|
|
56
|
+
SummarizerEndpoint: os.Getenv("LIBRAVDB_SUMMARIZER_ENDPOINT"),
|
|
57
|
+
GatingW1c: envFloatOrDefault("LIBRAVDB_GATING_W1C", 0.35),
|
|
58
|
+
GatingW2c: envFloatOrDefault("LIBRAVDB_GATING_W2C", 0.40),
|
|
59
|
+
GatingW3c: envFloatOrDefault("LIBRAVDB_GATING_W3C", 0.25),
|
|
60
|
+
GatingW1t: envFloatOrDefault("LIBRAVDB_GATING_W1T", 0.40),
|
|
61
|
+
GatingW2t: envFloatOrDefault("LIBRAVDB_GATING_W2T", 0.35),
|
|
62
|
+
GatingW3t: envFloatOrDefault("LIBRAVDB_GATING_W3T", 0.25),
|
|
63
|
+
GatingTechNorm: envFloatOrDefault("LIBRAVDB_GATING_TECH_NORM", 1.5),
|
|
64
|
+
GatingThreshold: envFloatOrDefault("LIBRAVDB_GATING_THRESHOLD", 0.35),
|
|
65
|
+
GatingCentroidK: envIntOrDefault("LIBRAVDB_GATING_CENTROID_K", 10),
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
func envOrDefault(key, fallback string) string {
|
|
70
|
+
value := strings.TrimSpace(os.Getenv(key))
|
|
71
|
+
if value == "" {
|
|
72
|
+
return fallback
|
|
73
|
+
}
|
|
74
|
+
return value
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
func envIntOrDefault(key string, fallback int) int {
|
|
78
|
+
raw := strings.TrimSpace(os.Getenv(key))
|
|
79
|
+
if raw == "" {
|
|
80
|
+
return fallback
|
|
81
|
+
}
|
|
82
|
+
value, err := strconv.Atoi(raw)
|
|
83
|
+
if err != nil || value <= 0 {
|
|
84
|
+
return fallback
|
|
85
|
+
}
|
|
86
|
+
return value
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
func envBoolOrDefault(key string, fallback bool) bool {
|
|
90
|
+
raw := strings.TrimSpace(os.Getenv(key))
|
|
91
|
+
if raw == "" {
|
|
92
|
+
return fallback
|
|
93
|
+
}
|
|
94
|
+
value, err := strconv.ParseBool(raw)
|
|
95
|
+
if err != nil {
|
|
96
|
+
return fallback
|
|
97
|
+
}
|
|
98
|
+
return value
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
func envFloatOrDefault(key string, fallback float64) float64 {
|
|
102
|
+
raw := strings.TrimSpace(os.Getenv(key))
|
|
103
|
+
if raw == "" {
|
|
104
|
+
return fallback
|
|
105
|
+
}
|
|
106
|
+
value, err := strconv.ParseFloat(raw, 64)
|
|
107
|
+
if err != nil {
|
|
108
|
+
return fallback
|
|
109
|
+
}
|
|
110
|
+
return value
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
func defaultDBPath() string {
|
|
114
|
+
currentUser, err := user.Current()
|
|
115
|
+
if err != nil || strings.TrimSpace(currentUser.HomeDir) == "" {
|
|
116
|
+
return "./libravdb-data"
|
|
117
|
+
}
|
|
118
|
+
return filepath.Join(currentUser.HomeDir, ".clawdb", "data")
|
|
119
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
package config
|
|
2
|
+
|
|
3
|
+
import "testing"
|
|
4
|
+
|
|
5
|
+
func TestFromEnvDefaults(t *testing.T) {
|
|
6
|
+
t.Setenv("LIBRAVDB_DB_PATH", "")
|
|
7
|
+
t.Setenv("LIBRAVDB_ONNX_RUNTIME", "")
|
|
8
|
+
t.Setenv("LIBRAVDB_EMBEDDING_BACKEND", "")
|
|
9
|
+
t.Setenv("LIBRAVDB_EMBEDDING_PROFILE", "")
|
|
10
|
+
t.Setenv("LIBRAVDB_EMBEDDING_MODEL", "")
|
|
11
|
+
t.Setenv("LIBRAVDB_EMBEDDING_TOKENIZER", "")
|
|
12
|
+
t.Setenv("LIBRAVDB_EMBEDDING_DIMENSIONS", "")
|
|
13
|
+
t.Setenv("LIBRAVDB_EMBEDDING_NORMALIZE", "")
|
|
14
|
+
|
|
15
|
+
cfg := FromEnv()
|
|
16
|
+
if cfg.DBPath == "" {
|
|
17
|
+
t.Fatalf("expected non-empty default db path")
|
|
18
|
+
}
|
|
19
|
+
if cfg.EmbeddingBackend != "bundled" {
|
|
20
|
+
t.Fatalf("expected bundled backend, got %q", cfg.EmbeddingBackend)
|
|
21
|
+
}
|
|
22
|
+
if cfg.EmbeddingProfile != "nomic-embed-text-v1.5" {
|
|
23
|
+
t.Fatalf("expected Nomic default profile, got %q", cfg.EmbeddingProfile)
|
|
24
|
+
}
|
|
25
|
+
if cfg.FallbackProfile != "all-minilm-l6-v2" {
|
|
26
|
+
t.Fatalf("expected MiniLM fallback profile, got %q", cfg.FallbackProfile)
|
|
27
|
+
}
|
|
28
|
+
if cfg.EmbeddingDimensions != 0 {
|
|
29
|
+
t.Fatalf("expected unspecified dimensions to default to 0, got %d", cfg.EmbeddingDimensions)
|
|
30
|
+
}
|
|
31
|
+
if !cfg.EmbeddingNormalize {
|
|
32
|
+
t.Fatalf("expected normalize=true by default")
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
func TestFromEnvReadsPowerUserEmbeddingSettings(t *testing.T) {
|
|
37
|
+
t.Setenv("LIBRAVDB_DB_PATH", "/tmp/libravdb")
|
|
38
|
+
t.Setenv("LIBRAVDB_ONNX_RUNTIME", "/opt/onnx/libonnxruntime.so")
|
|
39
|
+
t.Setenv("LIBRAVDB_EMBEDDING_BACKEND", "custom-local")
|
|
40
|
+
t.Setenv("LIBRAVDB_EMBEDDING_PROFILE", "nomic-embed-text-v1.5")
|
|
41
|
+
t.Setenv("LIBRAVDB_FALLBACK_PROFILE", "all-minilm-l6-v2")
|
|
42
|
+
t.Setenv("LIBRAVDB_EMBEDDING_MODEL", "/models/custom.onnx")
|
|
43
|
+
t.Setenv("LIBRAVDB_EMBEDDING_TOKENIZER", "/models/tokenizer.json")
|
|
44
|
+
t.Setenv("LIBRAVDB_EMBEDDING_DIMENSIONS", "768")
|
|
45
|
+
t.Setenv("LIBRAVDB_EMBEDDING_NORMALIZE", "false")
|
|
46
|
+
|
|
47
|
+
cfg := FromEnv()
|
|
48
|
+
if cfg.DBPath != "/tmp/libravdb" {
|
|
49
|
+
t.Fatalf("unexpected db path %q", cfg.DBPath)
|
|
50
|
+
}
|
|
51
|
+
if cfg.ONNXRuntimePath != "/opt/onnx/libonnxruntime.so" {
|
|
52
|
+
t.Fatalf("unexpected runtime path %q", cfg.ONNXRuntimePath)
|
|
53
|
+
}
|
|
54
|
+
if cfg.EmbeddingBackend != "custom-local" {
|
|
55
|
+
t.Fatalf("unexpected backend %q", cfg.EmbeddingBackend)
|
|
56
|
+
}
|
|
57
|
+
if cfg.EmbeddingProfile != "nomic-embed-text-v1.5" {
|
|
58
|
+
t.Fatalf("unexpected profile %q", cfg.EmbeddingProfile)
|
|
59
|
+
}
|
|
60
|
+
if cfg.FallbackProfile != "all-minilm-l6-v2" {
|
|
61
|
+
t.Fatalf("unexpected fallback profile %q", cfg.FallbackProfile)
|
|
62
|
+
}
|
|
63
|
+
if cfg.EmbeddingModelPath != "/models/custom.onnx" {
|
|
64
|
+
t.Fatalf("unexpected model path %q", cfg.EmbeddingModelPath)
|
|
65
|
+
}
|
|
66
|
+
if cfg.EmbeddingTokenizerPath != "/models/tokenizer.json" {
|
|
67
|
+
t.Fatalf("unexpected tokenizer path %q", cfg.EmbeddingTokenizerPath)
|
|
68
|
+
}
|
|
69
|
+
if cfg.EmbeddingDimensions != 768 {
|
|
70
|
+
t.Fatalf("unexpected dimensions %d", cfg.EmbeddingDimensions)
|
|
71
|
+
}
|
|
72
|
+
if cfg.EmbeddingNormalize {
|
|
73
|
+
t.Fatalf("expected normalize=false")
|
|
74
|
+
}
|
|
75
|
+
}
|