teleton 0.5.1 → 0.6.0
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 +239 -101
- package/dist/chunk-2QUJLHCZ.js +362 -0
- package/dist/chunk-4IPJ25HE.js +2839 -0
- package/dist/{chunk-RRB6BWU7.js → chunk-6L6KGATM.js} +1042 -3039
- package/dist/{chunk-BYTHCDZA.js → chunk-ADCMUNYU.js} +65 -496
- package/dist/chunk-D5I7GBV7.js +322 -0
- package/dist/chunk-ECSCVEQQ.js +139 -0
- package/dist/chunk-GDCODBNO.js +72 -0
- package/dist/{chunk-O4R7V5Y2.js → chunk-RO62LO6Z.js} +11 -1
- package/dist/cli/index.js +346 -22
- package/dist/index.js +7 -4
- package/dist/{memory-657W5AS6.js → memory-TVDOGQXS.js} +14 -10
- package/dist/{migrate-PMB2JVXH.js → migrate-QIEMPOMT.js} +5 -2
- package/dist/{server-BQY7CM2N.js → server-RSWVCVY3.js} +805 -26
- package/dist/{task-dependency-resolver-TRPILAHM.js → task-dependency-resolver-72DLY2HV.js} +1 -1
- package/dist/{task-executor-N7XNVK5N.js → task-executor-VXB6DAV2.js} +1 -1
- package/dist/tool-index-DKI2ZNOU.js +245 -0
- package/dist/web/assets/index-BNhrx9S1.js +67 -0
- package/dist/web/assets/index-CqrrRLOh.css +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +16 -16
- package/dist/chunk-5WWR4CU3.js +0 -124
- package/dist/web/assets/index-CDMbujHf.css +0 -1
- package/dist/web/assets/index-DDX8oQ2z.js +0 -67
|
@@ -1,143 +1,24 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
CachedEmbeddingProvider,
|
|
3
|
+
createEmbeddingProvider,
|
|
4
|
+
hashText,
|
|
5
|
+
serializeEmbedding
|
|
6
|
+
} from "./chunk-D5I7GBV7.js";
|
|
7
|
+
import {
|
|
8
|
+
JOURNAL_SCHEMA
|
|
9
|
+
} from "./chunk-ECSCVEQQ.js";
|
|
4
10
|
import {
|
|
5
|
-
EMBEDDING_CACHE_EVICTION_INTERVAL,
|
|
6
|
-
EMBEDDING_CACHE_EVICTION_RATIO,
|
|
7
|
-
EMBEDDING_CACHE_MAX_ENTRIES,
|
|
8
|
-
EMBEDDING_CACHE_TTL_DAYS,
|
|
9
11
|
HYBRID_SEARCH_MIN_SCORE,
|
|
10
12
|
KNOWLEDGE_CHUNK_SIZE,
|
|
11
13
|
SQLITE_CACHE_SIZE_KB,
|
|
12
|
-
SQLITE_MMAP_SIZE
|
|
13
|
-
|
|
14
|
-
} from "./chunk-O4R7V5Y2.js";
|
|
15
|
-
import {
|
|
16
|
-
TELETON_ROOT
|
|
17
|
-
} from "./chunk-EYWNOHMJ.js";
|
|
14
|
+
SQLITE_MMAP_SIZE
|
|
15
|
+
} from "./chunk-RO62LO6Z.js";
|
|
18
16
|
|
|
19
17
|
// src/memory/database.ts
|
|
20
|
-
import Database2 from "better-sqlite3";
|
|
21
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync2, chmodSync as chmodSync2 } from "fs";
|
|
22
|
-
import { dirname as dirname2 } from "path";
|
|
23
|
-
import * as sqliteVec from "sqlite-vec";
|
|
24
|
-
|
|
25
|
-
// src/utils/module-db.ts
|
|
26
18
|
import Database from "better-sqlite3";
|
|
27
19
|
import { existsSync, mkdirSync, chmodSync } from "fs";
|
|
28
|
-
import { dirname
|
|
29
|
-
|
|
30
|
-
CREATE TABLE IF NOT EXISTS journal (
|
|
31
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
32
|
-
timestamp INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
33
|
-
type TEXT NOT NULL CHECK(type IN ('trade', 'gift', 'middleman', 'kol')),
|
|
34
|
-
action TEXT NOT NULL,
|
|
35
|
-
asset_from TEXT,
|
|
36
|
-
asset_to TEXT,
|
|
37
|
-
amount_from REAL,
|
|
38
|
-
amount_to REAL,
|
|
39
|
-
price_ton REAL,
|
|
40
|
-
counterparty TEXT,
|
|
41
|
-
platform TEXT,
|
|
42
|
-
reasoning TEXT,
|
|
43
|
-
outcome TEXT CHECK(outcome IN ('pending', 'profit', 'loss', 'neutral', 'cancelled')),
|
|
44
|
-
pnl_ton REAL,
|
|
45
|
-
pnl_pct REAL,
|
|
46
|
-
tx_hash TEXT,
|
|
47
|
-
tool_used TEXT,
|
|
48
|
-
chat_id TEXT,
|
|
49
|
-
user_id INTEGER,
|
|
50
|
-
closed_at INTEGER,
|
|
51
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
CREATE INDEX IF NOT EXISTS idx_journal_type ON journal(type);
|
|
55
|
-
CREATE INDEX IF NOT EXISTS idx_journal_timestamp ON journal(timestamp DESC);
|
|
56
|
-
CREATE INDEX IF NOT EXISTS idx_journal_asset_from ON journal(asset_from);
|
|
57
|
-
CREATE INDEX IF NOT EXISTS idx_journal_outcome ON journal(outcome);
|
|
58
|
-
CREATE INDEX IF NOT EXISTS idx_journal_type_timestamp ON journal(type, timestamp DESC);
|
|
59
|
-
`;
|
|
60
|
-
var USED_TRANSACTIONS_SCHEMA = `
|
|
61
|
-
CREATE TABLE IF NOT EXISTS used_transactions (
|
|
62
|
-
tx_hash TEXT PRIMARY KEY,
|
|
63
|
-
user_id TEXT NOT NULL,
|
|
64
|
-
amount REAL NOT NULL,
|
|
65
|
-
game_type TEXT NOT NULL,
|
|
66
|
-
used_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
CREATE INDEX IF NOT EXISTS idx_used_tx_user ON used_transactions(user_id);
|
|
70
|
-
CREATE INDEX IF NOT EXISTS idx_used_tx_used_at ON used_transactions(used_at);
|
|
71
|
-
`;
|
|
72
|
-
function openModuleDb(path) {
|
|
73
|
-
const dir = dirname(path);
|
|
74
|
-
if (!existsSync(dir)) {
|
|
75
|
-
mkdirSync(dir, { recursive: true });
|
|
76
|
-
}
|
|
77
|
-
const db = new Database(path);
|
|
78
|
-
try {
|
|
79
|
-
chmodSync(path, 384);
|
|
80
|
-
} catch {
|
|
81
|
-
}
|
|
82
|
-
db.pragma("journal_mode = WAL");
|
|
83
|
-
return db;
|
|
84
|
-
}
|
|
85
|
-
function createDbWrapper(getDb, moduleName) {
|
|
86
|
-
return function withDb(executor) {
|
|
87
|
-
return (params, context) => {
|
|
88
|
-
const moduleDb = getDb();
|
|
89
|
-
if (!moduleDb) {
|
|
90
|
-
return Promise.resolve({
|
|
91
|
-
success: false,
|
|
92
|
-
error: `${moduleName} module not started`
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
return executor(params, { ...context, db: moduleDb });
|
|
96
|
-
};
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
var MAIN_DB_PATH = join(TELETON_ROOT, "memory.db");
|
|
100
|
-
function migrateFromMainDb(moduleDb, tables) {
|
|
101
|
-
let totalMigrated = 0;
|
|
102
|
-
for (const table of tables) {
|
|
103
|
-
if (!/^[a-z_]+$/.test(table)) {
|
|
104
|
-
throw new Error(`Invalid table name for migration: "${table}"`);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
for (const table of tables) {
|
|
108
|
-
try {
|
|
109
|
-
const row = moduleDb.prepare(`SELECT COUNT(*) as c FROM ${table}`).get();
|
|
110
|
-
if (row.c > 0) return 0;
|
|
111
|
-
} catch {
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
if (!existsSync(MAIN_DB_PATH)) return 0;
|
|
116
|
-
try {
|
|
117
|
-
moduleDb.exec(`ATTACH DATABASE '${MAIN_DB_PATH}' AS main_db`);
|
|
118
|
-
for (const table of tables) {
|
|
119
|
-
try {
|
|
120
|
-
const exists = moduleDb.prepare(`SELECT name FROM main_db.sqlite_master WHERE type='table' AND name=?`).get(table);
|
|
121
|
-
if (!exists) continue;
|
|
122
|
-
const src = moduleDb.prepare(`SELECT COUNT(*) as c FROM main_db.${table}`).get();
|
|
123
|
-
if (src.c === 0) continue;
|
|
124
|
-
moduleDb.exec(`INSERT OR IGNORE INTO ${table} SELECT * FROM main_db.${table}`);
|
|
125
|
-
totalMigrated += src.c;
|
|
126
|
-
console.log(` \u{1F4E6} Migrated ${src.c} rows from memory.db \u2192 ${table}`);
|
|
127
|
-
} catch (e) {
|
|
128
|
-
console.warn(` \u26A0\uFE0F Could not migrate table ${table}:`, e);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
moduleDb.exec(`DETACH DATABASE main_db`);
|
|
132
|
-
} catch (e) {
|
|
133
|
-
console.warn(`\u26A0\uFE0F Migration from memory.db failed:`, e);
|
|
134
|
-
try {
|
|
135
|
-
moduleDb.exec(`DETACH DATABASE main_db`);
|
|
136
|
-
} catch {
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return totalMigrated;
|
|
140
|
-
}
|
|
20
|
+
import { dirname } from "path";
|
|
21
|
+
import * as sqliteVec from "sqlite-vec";
|
|
141
22
|
|
|
142
23
|
// src/memory/schema.ts
|
|
143
24
|
function compareSemver(a, b) {
|
|
@@ -428,7 +309,7 @@ function setSchemaVersion(db, version) {
|
|
|
428
309
|
`
|
|
429
310
|
).run(version);
|
|
430
311
|
}
|
|
431
|
-
var CURRENT_SCHEMA_VERSION = "1.
|
|
312
|
+
var CURRENT_SCHEMA_VERSION = "1.11.0";
|
|
432
313
|
function runMigrations(db) {
|
|
433
314
|
const currentVersion = getSchemaVersion(db);
|
|
434
315
|
if (!currentVersion || versionLessThan(currentVersion, "1.1.0")) {
|
|
@@ -576,6 +457,45 @@ function runMigrations(db) {
|
|
|
576
457
|
throw error;
|
|
577
458
|
}
|
|
578
459
|
}
|
|
460
|
+
if (!currentVersion || versionLessThan(currentVersion, "1.11.0")) {
|
|
461
|
+
console.log("\u{1F504} Running migration 1.11.0: Add tool_index tables for Tool RAG");
|
|
462
|
+
try {
|
|
463
|
+
db.exec(`
|
|
464
|
+
CREATE TABLE IF NOT EXISTS tool_index (
|
|
465
|
+
name TEXT PRIMARY KEY,
|
|
466
|
+
description TEXT NOT NULL,
|
|
467
|
+
search_text TEXT NOT NULL,
|
|
468
|
+
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS tool_index_fts USING fts5(
|
|
472
|
+
search_text,
|
|
473
|
+
name UNINDEXED,
|
|
474
|
+
content='tool_index',
|
|
475
|
+
content_rowid='rowid'
|
|
476
|
+
);
|
|
477
|
+
|
|
478
|
+
CREATE TRIGGER IF NOT EXISTS tool_index_fts_insert AFTER INSERT ON tool_index BEGIN
|
|
479
|
+
INSERT INTO tool_index_fts(rowid, search_text, name)
|
|
480
|
+
VALUES (new.rowid, new.search_text, new.name);
|
|
481
|
+
END;
|
|
482
|
+
|
|
483
|
+
CREATE TRIGGER IF NOT EXISTS tool_index_fts_delete AFTER DELETE ON tool_index BEGIN
|
|
484
|
+
DELETE FROM tool_index_fts WHERE rowid = old.rowid;
|
|
485
|
+
END;
|
|
486
|
+
|
|
487
|
+
CREATE TRIGGER IF NOT EXISTS tool_index_fts_update AFTER UPDATE ON tool_index BEGIN
|
|
488
|
+
DELETE FROM tool_index_fts WHERE rowid = old.rowid;
|
|
489
|
+
INSERT INTO tool_index_fts(rowid, search_text, name)
|
|
490
|
+
VALUES (new.rowid, new.search_text, new.name);
|
|
491
|
+
END;
|
|
492
|
+
`);
|
|
493
|
+
console.log("\u2705 Migration 1.11.0 complete: tool_index tables created");
|
|
494
|
+
} catch (error) {
|
|
495
|
+
console.error("\u274C Migration 1.11.0 failed:", error);
|
|
496
|
+
throw error;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
579
499
|
setSchemaVersion(db, CURRENT_SCHEMA_VERSION);
|
|
580
500
|
}
|
|
581
501
|
|
|
@@ -586,15 +506,15 @@ var MemoryDatabase = class {
|
|
|
586
506
|
vectorReady = false;
|
|
587
507
|
constructor(config) {
|
|
588
508
|
this.config = config;
|
|
589
|
-
const dir =
|
|
590
|
-
if (!
|
|
591
|
-
|
|
509
|
+
const dir = dirname(config.path);
|
|
510
|
+
if (!existsSync(dir)) {
|
|
511
|
+
mkdirSync(dir, { recursive: true });
|
|
592
512
|
}
|
|
593
|
-
this.db = new
|
|
513
|
+
this.db = new Database(config.path, {
|
|
594
514
|
verbose: process.env.DEBUG_SQL ? console.log : void 0
|
|
595
515
|
});
|
|
596
516
|
try {
|
|
597
|
-
|
|
517
|
+
chmodSync(config.path, 384);
|
|
598
518
|
} catch {
|
|
599
519
|
}
|
|
600
520
|
this.db.pragma("journal_mode = WAL");
|
|
@@ -743,339 +663,9 @@ function closeDatabase() {
|
|
|
743
663
|
}
|
|
744
664
|
}
|
|
745
665
|
|
|
746
|
-
// src/memory/embeddings/index.ts
|
|
747
|
-
import { createHash } from "crypto";
|
|
748
|
-
|
|
749
|
-
// src/memory/embeddings/provider.ts
|
|
750
|
-
var NoopEmbeddingProvider = class {
|
|
751
|
-
id = "noop";
|
|
752
|
-
model = "none";
|
|
753
|
-
dimensions = 0;
|
|
754
|
-
async embedQuery(_text) {
|
|
755
|
-
return [];
|
|
756
|
-
}
|
|
757
|
-
async embedBatch(_texts) {
|
|
758
|
-
return [];
|
|
759
|
-
}
|
|
760
|
-
};
|
|
761
|
-
|
|
762
|
-
// src/utils/fetch.ts
|
|
763
|
-
var DEFAULT_TIMEOUT_MS = DEFAULT_FETCH_TIMEOUT_MS;
|
|
764
|
-
function fetchWithTimeout(url, init) {
|
|
765
|
-
const { timeoutMs = DEFAULT_TIMEOUT_MS, ...fetchInit } = init ?? {};
|
|
766
|
-
if (fetchInit.signal) {
|
|
767
|
-
return fetch(url, fetchInit);
|
|
768
|
-
}
|
|
769
|
-
return fetch(url, {
|
|
770
|
-
...fetchInit,
|
|
771
|
-
signal: AbortSignal.timeout(timeoutMs)
|
|
772
|
-
});
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
// src/constants/api-endpoints.ts
|
|
776
|
-
var TONAPI_BASE_URL = "https://tonapi.io/v2";
|
|
777
|
-
var _tonapiKey;
|
|
778
|
-
function setTonapiKey(key) {
|
|
779
|
-
_tonapiKey = key;
|
|
780
|
-
}
|
|
781
|
-
function tonapiHeaders() {
|
|
782
|
-
const headers = { Accept: "application/json" };
|
|
783
|
-
if (_tonapiKey) {
|
|
784
|
-
headers["Authorization"] = `Bearer ${_tonapiKey}`;
|
|
785
|
-
}
|
|
786
|
-
return headers;
|
|
787
|
-
}
|
|
788
|
-
var TONAPI_MAX_RPS = 5;
|
|
789
|
-
var _tonapiTimestamps = [];
|
|
790
|
-
async function waitForTonapiSlot() {
|
|
791
|
-
const clean = () => {
|
|
792
|
-
const cutoff = Date.now() - 1e3;
|
|
793
|
-
while (_tonapiTimestamps.length > 0 && _tonapiTimestamps[0] <= cutoff) {
|
|
794
|
-
_tonapiTimestamps.shift();
|
|
795
|
-
}
|
|
796
|
-
};
|
|
797
|
-
clean();
|
|
798
|
-
if (_tonapiTimestamps.length >= TONAPI_MAX_RPS) {
|
|
799
|
-
const waitMs = _tonapiTimestamps[0] + 1e3 - Date.now() + 50;
|
|
800
|
-
if (waitMs > 0) await new Promise((r) => setTimeout(r, waitMs));
|
|
801
|
-
clean();
|
|
802
|
-
}
|
|
803
|
-
_tonapiTimestamps.push(Date.now());
|
|
804
|
-
}
|
|
805
|
-
async function tonapiFetch(path, init) {
|
|
806
|
-
await waitForTonapiSlot();
|
|
807
|
-
return fetchWithTimeout(`${TONAPI_BASE_URL}${path}`, {
|
|
808
|
-
...init,
|
|
809
|
-
headers: { ...tonapiHeaders(), ...init?.headers }
|
|
810
|
-
});
|
|
811
|
-
}
|
|
812
|
-
var STONFI_API_BASE_URL = "https://api.ston.fi/v1";
|
|
813
|
-
var GECKOTERMINAL_API_URL = "https://api.geckoterminal.com/api/v2";
|
|
814
|
-
var COINGECKO_API_URL = "https://api.coingecko.com/api/v3";
|
|
815
|
-
var OPENAI_TTS_URL = "https://api.openai.com/v1/audio/speech";
|
|
816
|
-
var ELEVENLABS_TTS_URL = "https://api.elevenlabs.io/v1/text-to-speech";
|
|
817
|
-
var VOYAGE_API_URL = "https://api.voyageai.com/v1";
|
|
818
|
-
|
|
819
|
-
// src/memory/embeddings/anthropic.ts
|
|
820
|
-
var AnthropicEmbeddingProvider = class {
|
|
821
|
-
id = "anthropic";
|
|
822
|
-
model;
|
|
823
|
-
dimensions;
|
|
824
|
-
apiKey;
|
|
825
|
-
baseUrl = VOYAGE_API_URL;
|
|
826
|
-
constructor(config) {
|
|
827
|
-
this.apiKey = config.apiKey;
|
|
828
|
-
this.model = config.model ?? "voyage-3-lite";
|
|
829
|
-
const dims = {
|
|
830
|
-
"voyage-3": 1024,
|
|
831
|
-
"voyage-3-lite": 512,
|
|
832
|
-
"voyage-code-3": 1024,
|
|
833
|
-
"voyage-finance-2": 1024,
|
|
834
|
-
"voyage-multilingual-2": 1024,
|
|
835
|
-
"voyage-law-2": 1024
|
|
836
|
-
};
|
|
837
|
-
this.dimensions = dims[this.model] ?? 512;
|
|
838
|
-
}
|
|
839
|
-
async embedQuery(text) {
|
|
840
|
-
const result = await this.embed([text]);
|
|
841
|
-
return result[0] ?? [];
|
|
842
|
-
}
|
|
843
|
-
async embedBatch(texts) {
|
|
844
|
-
if (texts.length === 0) return [];
|
|
845
|
-
const batchSize = VOYAGE_BATCH_SIZE;
|
|
846
|
-
const results = [];
|
|
847
|
-
for (let i = 0; i < texts.length; i += batchSize) {
|
|
848
|
-
const batch = texts.slice(i, i + batchSize);
|
|
849
|
-
const embeddings = await this.embed(batch);
|
|
850
|
-
results.push(...embeddings);
|
|
851
|
-
}
|
|
852
|
-
return results;
|
|
853
|
-
}
|
|
854
|
-
async embed(texts) {
|
|
855
|
-
const response = await fetchWithTimeout(`${this.baseUrl}/embeddings`, {
|
|
856
|
-
method: "POST",
|
|
857
|
-
headers: {
|
|
858
|
-
"Content-Type": "application/json",
|
|
859
|
-
Authorization: `Bearer ${this.apiKey}`
|
|
860
|
-
},
|
|
861
|
-
body: JSON.stringify({
|
|
862
|
-
input: texts,
|
|
863
|
-
model: this.model,
|
|
864
|
-
input_type: "document"
|
|
865
|
-
})
|
|
866
|
-
});
|
|
867
|
-
if (!response.ok) {
|
|
868
|
-
const error = await response.text();
|
|
869
|
-
throw new Error(`Voyage API error: ${response.status} ${error}`);
|
|
870
|
-
}
|
|
871
|
-
const data = await response.json();
|
|
872
|
-
return data.data.map((item) => item.embedding);
|
|
873
|
-
}
|
|
874
|
-
};
|
|
875
|
-
|
|
876
|
-
// src/memory/embeddings/local.ts
|
|
877
|
-
import { pipeline } from "@huggingface/transformers";
|
|
878
|
-
var extractorPromise = null;
|
|
879
|
-
function getExtractor(model) {
|
|
880
|
-
if (!extractorPromise) {
|
|
881
|
-
console.log(`\u{1F4E6} Loading local embedding model: ${model} \u2026`);
|
|
882
|
-
extractorPromise = pipeline("feature-extraction", model, {
|
|
883
|
-
dtype: "fp32"
|
|
884
|
-
}).then((ext) => {
|
|
885
|
-
console.log(`\u2705 Local embedding model ready`);
|
|
886
|
-
return ext;
|
|
887
|
-
}).catch((err) => {
|
|
888
|
-
console.error(`\u274C Failed to load embedding model: ${err.message}`);
|
|
889
|
-
extractorPromise = null;
|
|
890
|
-
throw err;
|
|
891
|
-
});
|
|
892
|
-
}
|
|
893
|
-
return extractorPromise;
|
|
894
|
-
}
|
|
895
|
-
var LocalEmbeddingProvider = class {
|
|
896
|
-
id = "local";
|
|
897
|
-
model;
|
|
898
|
-
dimensions;
|
|
899
|
-
constructor(config) {
|
|
900
|
-
this.model = config.model || "Xenova/all-MiniLM-L6-v2";
|
|
901
|
-
this.dimensions = 384;
|
|
902
|
-
}
|
|
903
|
-
async embedQuery(text) {
|
|
904
|
-
const extractor = await getExtractor(this.model);
|
|
905
|
-
const output = await extractor(text, { pooling: "mean", normalize: true });
|
|
906
|
-
return Array.from(output.data);
|
|
907
|
-
}
|
|
908
|
-
async embedBatch(texts) {
|
|
909
|
-
if (texts.length === 0) return [];
|
|
910
|
-
const extractor = await getExtractor(this.model);
|
|
911
|
-
const output = await extractor(texts, { pooling: "mean", normalize: true });
|
|
912
|
-
const data = output.data;
|
|
913
|
-
const dims = this.dimensions;
|
|
914
|
-
const results = [];
|
|
915
|
-
for (let i = 0; i < texts.length; i++) {
|
|
916
|
-
results.push(Array.from(data.slice(i * dims, (i + 1) * dims)));
|
|
917
|
-
}
|
|
918
|
-
return results;
|
|
919
|
-
}
|
|
920
|
-
};
|
|
921
|
-
|
|
922
|
-
// src/memory/embeddings/cached.ts
|
|
923
|
-
var CachedEmbeddingProvider = class {
|
|
924
|
-
constructor(inner, db) {
|
|
925
|
-
this.inner = inner;
|
|
926
|
-
this.db = db;
|
|
927
|
-
this.id = inner.id;
|
|
928
|
-
this.model = inner.model;
|
|
929
|
-
this.dimensions = inner.dimensions;
|
|
930
|
-
}
|
|
931
|
-
id;
|
|
932
|
-
model;
|
|
933
|
-
dimensions;
|
|
934
|
-
hits = 0;
|
|
935
|
-
misses = 0;
|
|
936
|
-
ops = 0;
|
|
937
|
-
cacheGet(hash) {
|
|
938
|
-
return this.db.prepare(
|
|
939
|
-
`SELECT embedding FROM embedding_cache WHERE hash = ? AND model = ? AND provider = ?`
|
|
940
|
-
).get(hash, this.model, this.id);
|
|
941
|
-
}
|
|
942
|
-
cachePut(hash, blob) {
|
|
943
|
-
this.db.prepare(
|
|
944
|
-
`INSERT OR REPLACE INTO embedding_cache (hash, embedding, model, provider, dims, created_at, accessed_at)
|
|
945
|
-
VALUES (?, ?, ?, ?, ?, unixepoch(), unixepoch())`
|
|
946
|
-
).run(hash, blob, this.model, this.id, this.dimensions);
|
|
947
|
-
}
|
|
948
|
-
cacheTouch(hash) {
|
|
949
|
-
this.db.prepare(
|
|
950
|
-
`UPDATE embedding_cache SET accessed_at = unixepoch() WHERE hash = ? AND model = ? AND provider = ?`
|
|
951
|
-
).run(hash, this.model, this.id);
|
|
952
|
-
}
|
|
953
|
-
async embedQuery(text) {
|
|
954
|
-
const hash = hashText(text);
|
|
955
|
-
const row = this.cacheGet(hash);
|
|
956
|
-
if (row) {
|
|
957
|
-
this.hits++;
|
|
958
|
-
this.cacheTouch(hash);
|
|
959
|
-
this.tick();
|
|
960
|
-
return deserializeEmbedding(row.embedding);
|
|
961
|
-
}
|
|
962
|
-
this.misses++;
|
|
963
|
-
const embedding = await this.inner.embedQuery(text);
|
|
964
|
-
this.cachePut(hash, serializeEmbedding(embedding));
|
|
965
|
-
this.tick();
|
|
966
|
-
return embedding;
|
|
967
|
-
}
|
|
968
|
-
async embedBatch(texts) {
|
|
969
|
-
if (texts.length === 0) return [];
|
|
970
|
-
const hashes = texts.map(hashText);
|
|
971
|
-
const results = new Array(texts.length).fill(null);
|
|
972
|
-
const missIndices = [];
|
|
973
|
-
const missTexts = [];
|
|
974
|
-
for (let i = 0; i < texts.length; i++) {
|
|
975
|
-
const row = this.cacheGet(hashes[i]);
|
|
976
|
-
if (row) {
|
|
977
|
-
this.hits++;
|
|
978
|
-
this.cacheTouch(hashes[i]);
|
|
979
|
-
results[i] = deserializeEmbedding(row.embedding);
|
|
980
|
-
} else {
|
|
981
|
-
this.misses++;
|
|
982
|
-
missIndices.push(i);
|
|
983
|
-
missTexts.push(texts[i]);
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
if (missTexts.length > 0) {
|
|
987
|
-
const newEmbeddings = await this.inner.embedBatch(missTexts);
|
|
988
|
-
for (let j = 0; j < missIndices.length; j++) {
|
|
989
|
-
const idx = missIndices[j];
|
|
990
|
-
const embedding = newEmbeddings[j] ?? [];
|
|
991
|
-
results[idx] = embedding;
|
|
992
|
-
if (embedding.length > 0) {
|
|
993
|
-
this.cachePut(hashes[idx], serializeEmbedding(embedding));
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
this.ops += texts.length;
|
|
998
|
-
this.maybeEvict();
|
|
999
|
-
this.maybeLogStats();
|
|
1000
|
-
return results;
|
|
1001
|
-
}
|
|
1002
|
-
tick() {
|
|
1003
|
-
this.ops++;
|
|
1004
|
-
this.maybeEvict();
|
|
1005
|
-
this.maybeLogStats();
|
|
1006
|
-
}
|
|
1007
|
-
maybeLogStats() {
|
|
1008
|
-
const total = this.hits + this.misses;
|
|
1009
|
-
if (total > 0 && total % 100 === 0) {
|
|
1010
|
-
const rate = (this.hits / total * 100).toFixed(0);
|
|
1011
|
-
console.log(
|
|
1012
|
-
`\u{1F4CA} Embedding cache: ${this.hits} hits, ${this.misses} misses (${rate}% hit rate)`
|
|
1013
|
-
);
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
maybeEvict() {
|
|
1017
|
-
if (this.ops % EMBEDDING_CACHE_EVICTION_INTERVAL !== 0) return;
|
|
1018
|
-
try {
|
|
1019
|
-
const cutoff = Math.floor(Date.now() / 1e3) - EMBEDDING_CACHE_TTL_DAYS * 86400;
|
|
1020
|
-
this.db.prepare(`DELETE FROM embedding_cache WHERE accessed_at < ?`).run(cutoff);
|
|
1021
|
-
const count = this.db.prepare(`SELECT COUNT(*) as cnt FROM embedding_cache`).get().cnt;
|
|
1022
|
-
if (count > EMBEDDING_CACHE_MAX_ENTRIES) {
|
|
1023
|
-
const toDelete = Math.ceil(count * EMBEDDING_CACHE_EVICTION_RATIO);
|
|
1024
|
-
this.db.prepare(
|
|
1025
|
-
`DELETE FROM embedding_cache WHERE (hash, model, provider) IN (
|
|
1026
|
-
SELECT hash, model, provider FROM embedding_cache ORDER BY accessed_at ASC LIMIT ?
|
|
1027
|
-
)`
|
|
1028
|
-
).run(toDelete);
|
|
1029
|
-
console.log(`\u{1F9F9} Embedding cache eviction: removed ${toDelete} entries (${count} total)`);
|
|
1030
|
-
}
|
|
1031
|
-
} catch (err) {
|
|
1032
|
-
console.warn("\u26A0\uFE0F Embedding cache eviction error:", err);
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
};
|
|
1036
|
-
|
|
1037
|
-
// src/memory/embeddings/index.ts
|
|
1038
|
-
function createEmbeddingProvider(config) {
|
|
1039
|
-
switch (config.provider) {
|
|
1040
|
-
case "anthropic":
|
|
1041
|
-
if (!config.apiKey) {
|
|
1042
|
-
throw new Error("API key required for Anthropic embedding provider");
|
|
1043
|
-
}
|
|
1044
|
-
return new AnthropicEmbeddingProvider({
|
|
1045
|
-
apiKey: config.apiKey,
|
|
1046
|
-
model: config.model
|
|
1047
|
-
});
|
|
1048
|
-
case "local":
|
|
1049
|
-
return new LocalEmbeddingProvider({
|
|
1050
|
-
model: config.model
|
|
1051
|
-
});
|
|
1052
|
-
case "none":
|
|
1053
|
-
return new NoopEmbeddingProvider();
|
|
1054
|
-
default:
|
|
1055
|
-
throw new Error(`Unknown embedding provider: ${config.provider}`);
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
function hashText(text) {
|
|
1059
|
-
return createHash("sha256").update(text).digest("hex");
|
|
1060
|
-
}
|
|
1061
|
-
function serializeEmbedding(embedding) {
|
|
1062
|
-
return Buffer.from(new Float32Array(embedding).buffer);
|
|
1063
|
-
}
|
|
1064
|
-
function deserializeEmbedding(data) {
|
|
1065
|
-
try {
|
|
1066
|
-
if (Buffer.isBuffer(data)) {
|
|
1067
|
-
const floats = new Float32Array(data.buffer, data.byteOffset, data.byteLength / 4);
|
|
1068
|
-
return Array.from(floats);
|
|
1069
|
-
}
|
|
1070
|
-
return JSON.parse(data);
|
|
1071
|
-
} catch {
|
|
1072
|
-
return [];
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
666
|
// src/memory/agent/knowledge.ts
|
|
1077
|
-
import { readFileSync, existsSync as
|
|
1078
|
-
import { join
|
|
667
|
+
import { readFileSync, existsSync as existsSync2, readdirSync, statSync } from "fs";
|
|
668
|
+
import { join } from "path";
|
|
1079
669
|
var KnowledgeIndexer = class {
|
|
1080
670
|
constructor(db, workspaceDir, embedder, vectorEnabled) {
|
|
1081
671
|
this.db = db;
|
|
@@ -1098,7 +688,7 @@ var KnowledgeIndexer = class {
|
|
|
1098
688
|
return { indexed, skipped };
|
|
1099
689
|
}
|
|
1100
690
|
async indexFile(absPath) {
|
|
1101
|
-
if (!
|
|
691
|
+
if (!existsSync2(absPath) || !absPath.endsWith(".md")) {
|
|
1102
692
|
return false;
|
|
1103
693
|
}
|
|
1104
694
|
const content = readFileSync(absPath, "utf-8");
|
|
@@ -1146,15 +736,15 @@ var KnowledgeIndexer = class {
|
|
|
1146
736
|
}
|
|
1147
737
|
listMemoryFiles() {
|
|
1148
738
|
const files = [];
|
|
1149
|
-
const memoryMd =
|
|
1150
|
-
if (
|
|
739
|
+
const memoryMd = join(this.workspaceDir, "MEMORY.md");
|
|
740
|
+
if (existsSync2(memoryMd)) {
|
|
1151
741
|
files.push(memoryMd);
|
|
1152
742
|
}
|
|
1153
|
-
const memoryDir =
|
|
1154
|
-
if (
|
|
743
|
+
const memoryDir = join(this.workspaceDir, "memory");
|
|
744
|
+
if (existsSync2(memoryDir)) {
|
|
1155
745
|
const entries = readdirSync(memoryDir);
|
|
1156
746
|
for (const entry of entries) {
|
|
1157
|
-
const absPath =
|
|
747
|
+
const absPath = join(memoryDir, entry);
|
|
1158
748
|
if (statSync(absPath).isFile() && entry.endsWith(".md")) {
|
|
1159
749
|
files.push(absPath);
|
|
1160
750
|
}
|
|
@@ -1978,11 +1568,6 @@ function initializeMemory(config) {
|
|
|
1978
1568
|
}
|
|
1979
1569
|
|
|
1980
1570
|
export {
|
|
1981
|
-
JOURNAL_SCHEMA,
|
|
1982
|
-
USED_TRANSACTIONS_SCHEMA,
|
|
1983
|
-
openModuleDb,
|
|
1984
|
-
createDbWrapper,
|
|
1985
|
-
migrateFromMainDb,
|
|
1986
1571
|
ensureSchema,
|
|
1987
1572
|
ensureVectorTables,
|
|
1988
1573
|
getSchemaVersion,
|
|
@@ -1992,22 +1577,6 @@ export {
|
|
|
1992
1577
|
MemoryDatabase,
|
|
1993
1578
|
getDatabase,
|
|
1994
1579
|
closeDatabase,
|
|
1995
|
-
NoopEmbeddingProvider,
|
|
1996
|
-
fetchWithTimeout,
|
|
1997
|
-
setTonapiKey,
|
|
1998
|
-
tonapiFetch,
|
|
1999
|
-
STONFI_API_BASE_URL,
|
|
2000
|
-
GECKOTERMINAL_API_URL,
|
|
2001
|
-
COINGECKO_API_URL,
|
|
2002
|
-
OPENAI_TTS_URL,
|
|
2003
|
-
ELEVENLABS_TTS_URL,
|
|
2004
|
-
AnthropicEmbeddingProvider,
|
|
2005
|
-
LocalEmbeddingProvider,
|
|
2006
|
-
CachedEmbeddingProvider,
|
|
2007
|
-
createEmbeddingProvider,
|
|
2008
|
-
hashText,
|
|
2009
|
-
serializeEmbedding,
|
|
2010
|
-
deserializeEmbedding,
|
|
2011
1580
|
KnowledgeIndexer,
|
|
2012
1581
|
SessionStore,
|
|
2013
1582
|
MessageStore,
|