agentlang 0.9.6 → 0.9.8
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/out/cli/main.d.ts.map +1 -1
- package/out/cli/main.js +8 -3
- package/out/cli/main.js.map +1 -1
- package/out/extension/main.cjs +250 -250
- package/out/extension/main.cjs.map +2 -2
- package/out/language/generated/ast.js +1 -0
- package/out/language/generated/ast.js.map +1 -1
- package/out/language/main.cjs +2420 -776
- package/out/language/main.cjs.map +4 -4
- package/out/runtime/docs.d.ts.map +1 -1
- package/out/runtime/docs.js +109 -7
- package/out/runtime/docs.js.map +1 -1
- package/out/runtime/embeddings/chunker.d.ts +9 -0
- package/out/runtime/embeddings/chunker.d.ts.map +1 -0
- package/out/runtime/embeddings/chunker.js +41 -0
- package/out/runtime/embeddings/chunker.js.map +1 -0
- package/out/runtime/embeddings/index.d.ts +6 -0
- package/out/runtime/embeddings/index.d.ts.map +1 -0
- package/out/runtime/embeddings/index.js +6 -0
- package/out/runtime/embeddings/index.js.map +1 -0
- package/out/runtime/embeddings/openai.d.ts +15 -0
- package/out/runtime/embeddings/openai.d.ts.map +1 -0
- package/out/runtime/embeddings/openai.js +34 -0
- package/out/runtime/embeddings/openai.js.map +1 -0
- package/out/runtime/embeddings/provider.d.ts +20 -0
- package/out/runtime/embeddings/provider.d.ts.map +1 -0
- package/out/runtime/embeddings/provider.js +17 -0
- package/out/runtime/embeddings/provider.js.map +1 -0
- package/out/runtime/embeddings/registry.d.ts +3 -0
- package/out/runtime/embeddings/registry.d.ts.map +1 -0
- package/out/runtime/embeddings/registry.js +16 -0
- package/out/runtime/embeddings/registry.js.map +1 -0
- package/out/runtime/interpreter.d.ts.map +1 -1
- package/out/runtime/interpreter.js +8 -4
- package/out/runtime/interpreter.js.map +1 -1
- package/out/runtime/module.d.ts +24 -2
- package/out/runtime/module.d.ts.map +1 -1
- package/out/runtime/module.js +94 -7
- package/out/runtime/module.js.map +1 -1
- package/out/runtime/modules/ai.d.ts +2 -0
- package/out/runtime/modules/ai.d.ts.map +1 -1
- package/out/runtime/modules/ai.js +81 -18
- package/out/runtime/modules/ai.js.map +1 -1
- package/out/runtime/resolvers/sqldb/database.d.ts +1 -1
- package/out/runtime/resolvers/sqldb/database.d.ts.map +1 -1
- package/out/runtime/resolvers/sqldb/database.js +177 -51
- package/out/runtime/resolvers/sqldb/database.js.map +1 -1
- package/out/runtime/resolvers/sqldb/impl.d.ts +21 -1
- package/out/runtime/resolvers/sqldb/impl.d.ts.map +1 -1
- package/out/runtime/resolvers/sqldb/impl.js +176 -45
- package/out/runtime/resolvers/sqldb/impl.js.map +1 -1
- package/package.json +188 -185
- package/public/pdf.worker.mjs +65152 -0
- package/src/cli/main.ts +7 -2
- package/src/language/generated/ast.ts +1 -1
- package/src/runtime/docs.ts +120 -9
- package/src/runtime/embeddings/chunker.ts +50 -0
- package/src/runtime/embeddings/index.ts +5 -0
- package/src/runtime/embeddings/openai.ts +49 -0
- package/src/runtime/embeddings/provider.ts +37 -0
- package/src/runtime/embeddings/registry.ts +17 -0
- package/src/runtime/interpreter.ts +16 -12
- package/src/runtime/module.ts +148 -40
- package/src/runtime/modules/ai.ts +100 -20
- package/src/runtime/resolvers/sqldb/database.ts +190 -59
- package/src/runtime/resolvers/sqldb/impl.ts +235 -58
- package/out/setupClassic.d.ts +0 -98
- package/out/setupClassic.d.ts.map +0 -1
- package/out/setupClassic.js +0 -38
- package/out/setupClassic.js.map +0 -1
- package/out/setupCommon.d.ts +0 -2
- package/out/setupCommon.d.ts.map +0 -1
- package/out/setupCommon.js +0 -33
- package/out/setupCommon.js.map +0 -1
- package/out/setupExtended.d.ts +0 -40
- package/out/setupExtended.d.ts.map +0 -1
- package/out/setupExtended.js +0 -67
- package/out/setupExtended.js.map +0 -1
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
newInstanceAttributes,
|
|
32
32
|
Record,
|
|
33
33
|
Retry,
|
|
34
|
+
resolveDocumentAliases,
|
|
34
35
|
} from '../module.js';
|
|
35
36
|
import { provider } from '../agents/registry.js';
|
|
36
37
|
import {
|
|
@@ -60,7 +61,6 @@ import {
|
|
|
60
61
|
newAgentScenario,
|
|
61
62
|
PlannerInstructions,
|
|
62
63
|
} from '../agents/common.js';
|
|
63
|
-
import { PathAttributeNameQuery } from '../defs.js';
|
|
64
64
|
import { logger } from '../logger.js';
|
|
65
65
|
import { FlowStep } from '../agents/flows.js';
|
|
66
66
|
import Handlebars from 'handlebars';
|
|
@@ -74,6 +74,27 @@ export const AgentLearnerType = 'learner';
|
|
|
74
74
|
|
|
75
75
|
const AgentEvalType = 'eval';
|
|
76
76
|
|
|
77
|
+
function buildEmbeddingConfig(): object {
|
|
78
|
+
const config: any = {
|
|
79
|
+
provider: process.env.AGENTLANG_EMBEDDING_PROVIDER || 'openai',
|
|
80
|
+
model: process.env.AGENTLANG_EMBEDDING_MODEL || 'text-embedding-3-small',
|
|
81
|
+
chunkSize: 1000,
|
|
82
|
+
chunkOverlap: 200,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
if (process.env.AGENTLANG_EMBEDDING_CHUNKSIZE) {
|
|
86
|
+
config.chunkSize = parseInt(process.env.AGENTLANG_EMBEDDING_CHUNKSIZE, 1000);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (process.env.AGENTLANG_EMBEDDING_CHUNKOVERLAP) {
|
|
90
|
+
config.chunkOverlap = parseInt(process.env.AGENTLANG_EMBEDDING_CHUNKOVERLAP, 200);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return config;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const embeddingConfig = JSON.stringify(buildEmbeddingConfig());
|
|
97
|
+
|
|
77
98
|
export default `module ${CoreAIModuleName}
|
|
78
99
|
|
|
79
100
|
import "./modules/ai.js" @as ai
|
|
@@ -117,7 +138,7 @@ workflow saveAgentChatSession {
|
|
|
117
138
|
entity Document {
|
|
118
139
|
title String @id,
|
|
119
140
|
content String,
|
|
120
|
-
@meta {"fullTextSearch": "*"}
|
|
141
|
+
@meta {"fullTextSearch": "*", "embeddingConfig": ${embeddingConfig}}
|
|
121
142
|
}
|
|
122
143
|
|
|
123
144
|
event doc {
|
|
@@ -980,34 +1001,93 @@ Only return a pure JSON object with no extra text, annotations etc.`;
|
|
|
980
1001
|
|
|
981
1002
|
private async maybeAddRelevantDocuments(message: string, env: Environment): Promise<string> {
|
|
982
1003
|
if (this.documents && this.documents.length > 0) {
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1004
|
+
try {
|
|
1005
|
+
const docNames = this.documents.split(',').map(d => d.trim());
|
|
1006
|
+
const docTitles = resolveDocumentAliases(docNames);
|
|
1007
|
+
|
|
1008
|
+
const searchQuery = message;
|
|
1009
|
+
|
|
1010
|
+
try {
|
|
1011
|
+
const semanticResult: any[] = await parseHelper(
|
|
1012
|
+
`{${CoreAIModuleName}/Document {content? "${searchQuery.replace(/"/g, '\\"')}"}}`,
|
|
991
1013
|
env
|
|
992
1014
|
);
|
|
993
|
-
|
|
994
|
-
|
|
1015
|
+
|
|
1016
|
+
if (semanticResult && semanticResult.length > 0) {
|
|
1017
|
+
const docs: Instance[] = [];
|
|
1018
|
+
for (const doc of semanticResult) {
|
|
1019
|
+
const docTitle = doc.lookup ? doc.lookup('title') : doc.title;
|
|
1020
|
+
if (AgentInstance.docTitlesMatch(docTitle, docTitles)) {
|
|
1021
|
+
docs.push(
|
|
1022
|
+
doc instanceof Instance
|
|
1023
|
+
? doc
|
|
1024
|
+
: Instance.newWithAttributes(doc, new Map(Object.entries(doc)))
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
if (docs.length > 0) {
|
|
1030
|
+
return message.concat('\n\nRelevant context from documents:\n').concat(
|
|
1031
|
+
docs
|
|
1032
|
+
.map((v: Instance) => {
|
|
1033
|
+
return `Document: ${v.lookup('title')}\n${v.lookup('content') as string}`;
|
|
1034
|
+
})
|
|
1035
|
+
.join('\n\n---\n\n')
|
|
1036
|
+
);
|
|
1037
|
+
}
|
|
995
1038
|
}
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
docs
|
|
1000
|
-
.map((v: Instance) => {
|
|
1001
|
-
return v.lookup('content');
|
|
1002
|
-
})
|
|
1003
|
-
.join('\n')
|
|
1039
|
+
} catch (semanticErr) {
|
|
1040
|
+
logger.debug(
|
|
1041
|
+
`Semantic search is not available, falling back to title-based filtering: ${semanticErr}`
|
|
1004
1042
|
);
|
|
1005
1043
|
}
|
|
1044
|
+
|
|
1045
|
+
const result: any[] = await parseHelper(`{${CoreAIModuleName}/Document? {}}`, env);
|
|
1046
|
+
if (result && result.length > 0) {
|
|
1047
|
+
const docs: Instance[] = [];
|
|
1048
|
+
for (let i = 0; i < result.length; ++i) {
|
|
1049
|
+
const v: any = result[i];
|
|
1050
|
+
const docTitle: string | undefined = AgentInstance.getDocumentTitle(v);
|
|
1051
|
+
|
|
1052
|
+
if (docTitle && docTitles.includes(docTitle)) {
|
|
1053
|
+
if (v instanceof Instance) {
|
|
1054
|
+
docs.push(v);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
if (docs.length > 0) {
|
|
1060
|
+
return message.concat('\n\nRelevant context from documents:\n').concat(
|
|
1061
|
+
docs
|
|
1062
|
+
.map((v: Instance) => {
|
|
1063
|
+
return v.lookup('content') as string;
|
|
1064
|
+
})
|
|
1065
|
+
.join('\n\n')
|
|
1066
|
+
);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
} catch (err) {
|
|
1070
|
+
logger.debug(`Error retrieving documents: ${err}`);
|
|
1006
1071
|
}
|
|
1007
1072
|
}
|
|
1008
1073
|
return message;
|
|
1009
1074
|
}
|
|
1010
1075
|
|
|
1076
|
+
private static docTitlesMatch(title: string | undefined, docNames: string[]): boolean {
|
|
1077
|
+
return title !== undefined && docNames.includes(title);
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
private static getDocumentTitle(doc: any): string | undefined {
|
|
1081
|
+
if (typeof doc.lookup === 'function') {
|
|
1082
|
+
return doc.lookup('title') as string | undefined;
|
|
1083
|
+
} else if (doc.attributes) {
|
|
1084
|
+
return doc.attributes.get('title') as string | undefined;
|
|
1085
|
+
} else if (doc.title) {
|
|
1086
|
+
return doc.title;
|
|
1087
|
+
}
|
|
1088
|
+
return undefined;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1011
1091
|
private static ToolsCache = new Map<string, string>();
|
|
1012
1092
|
|
|
1013
1093
|
private toolsAsString(): string {
|
|
@@ -49,6 +49,67 @@ import { AppConfig } from '../../state.js';
|
|
|
49
49
|
|
|
50
50
|
export let defaultDataSource: DataSource | undefined;
|
|
51
51
|
|
|
52
|
+
// Detect browser environment
|
|
53
|
+
function isBrowser(): boolean {
|
|
54
|
+
// window for DOM pages, self+importScripts for web workers
|
|
55
|
+
return (
|
|
56
|
+
(typeof window !== 'undefined' && typeof (window as any).document !== 'undefined') ||
|
|
57
|
+
(typeof self !== 'undefined' && typeof (self as any).importScripts === 'function')
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// SQLite WASM with built-in sqlite-vec for browsers
|
|
62
|
+
// Loaded from CDN - this has sqlite-vec statically compiled in
|
|
63
|
+
let sqliteVecWasmModule: any = null;
|
|
64
|
+
|
|
65
|
+
async function loadSqliteVecWasm(): Promise<any> {
|
|
66
|
+
if (sqliteVecWasmModule) {
|
|
67
|
+
return sqliteVecWasmModule;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
// Use dynamic import with string to prevent bundlers from analyzing
|
|
72
|
+
const cdnUrl = 'https://cdn.jsdelivr.net/npm/sqlite-vec-wasm-demo@latest/sqlite3.mjs';
|
|
73
|
+
const module = await import(/* @vite-ignore */ cdnUrl);
|
|
74
|
+
sqliteVecWasmModule = await module.default();
|
|
75
|
+
return sqliteVecWasmModule;
|
|
76
|
+
} catch (err) {
|
|
77
|
+
logger.warn('Failed to load sqlite-vec WASM:', err);
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Helper to load sqlite-vec based on environment
|
|
83
|
+
async function loadSqliteVec(): Promise<any> {
|
|
84
|
+
// In browser, use WASM version with built-in sqlite-vec
|
|
85
|
+
// In Node.js, use the npm package
|
|
86
|
+
if (isBrowser()) {
|
|
87
|
+
const wasmModule = await loadSqliteVecWasm();
|
|
88
|
+
if (wasmModule) {
|
|
89
|
+
// WASM version has sqlite-vec built-in, no need to call load()
|
|
90
|
+
// Return a compatible interface
|
|
91
|
+
return {
|
|
92
|
+
load: () => {
|
|
93
|
+
// No-op: sqlite-vec is already loaded in WASM version
|
|
94
|
+
logger.info('sqlite-vec WASM loaded (built-in)');
|
|
95
|
+
},
|
|
96
|
+
// Expose the WASM module for direct use if needed
|
|
97
|
+
_wasmModule: wasmModule,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Node.js: use npm package
|
|
104
|
+
try {
|
|
105
|
+
// Use variable to prevent bundlers from statically analyzing this import
|
|
106
|
+
const moduleName = 'sqlite-vec';
|
|
107
|
+
return await import(/* @vite-ignore */ moduleName);
|
|
108
|
+
} catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
52
113
|
export class DbContext {
|
|
53
114
|
txnId: string | undefined;
|
|
54
115
|
authInfo: ResolverAuthInfo;
|
|
@@ -228,9 +289,10 @@ function makeSqliteDataSource(
|
|
|
228
289
|
): DataSource {
|
|
229
290
|
const synchronize = needSync();
|
|
230
291
|
//const runMigrations = isRuntimeMode_migration() || isRuntimeMode_undo_migration() || !synchronize;
|
|
231
|
-
|
|
292
|
+
const dbPath = config?.dbname || mkDbName();
|
|
293
|
+
const ds = new DataSource({
|
|
232
294
|
type: 'better-sqlite3',
|
|
233
|
-
database:
|
|
295
|
+
database: dbPath,
|
|
234
296
|
synchronize: synchronize,
|
|
235
297
|
entities: entities,
|
|
236
298
|
migrationsRun: false,
|
|
@@ -240,6 +302,25 @@ function makeSqliteDataSource(
|
|
|
240
302
|
undefined: 'ignore',
|
|
241
303
|
},
|
|
242
304
|
});
|
|
305
|
+
const originalInit = ds.initialize.bind(ds);
|
|
306
|
+
ds.initialize = async () => {
|
|
307
|
+
const res = await originalInit();
|
|
308
|
+
try {
|
|
309
|
+
const sqliteVec = await loadSqliteVec();
|
|
310
|
+
const driver = ds.driver as any;
|
|
311
|
+
const db = driver.databaseConnection || driver.nativeDatabase;
|
|
312
|
+
if (db && sqliteVec?.load) {
|
|
313
|
+
sqliteVec.load(db);
|
|
314
|
+
logger.info('sqlite-vec extension loaded successfully');
|
|
315
|
+
}
|
|
316
|
+
} catch (err: any) {
|
|
317
|
+
logger.warn(
|
|
318
|
+
`Failed to load sqlite-vec extension: ${err.message}. Vector operations may not be available.`
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
return res;
|
|
322
|
+
};
|
|
323
|
+
return ds;
|
|
243
324
|
}
|
|
244
325
|
|
|
245
326
|
async function execMigrationSql(dataSource: DataSource, sql: string[]) {
|
|
@@ -283,14 +364,6 @@ async function maybeHandleMigrations(dataSource: DataSource) {
|
|
|
283
364
|
}
|
|
284
365
|
}
|
|
285
366
|
|
|
286
|
-
function isBrowser(): boolean {
|
|
287
|
-
// window for DOM pages, self+importScripts for web workers
|
|
288
|
-
return (
|
|
289
|
-
(typeof window !== 'undefined' && typeof (window as any).document !== 'undefined') ||
|
|
290
|
-
(typeof self !== 'undefined' && typeof (self as any).importScripts === 'function')
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
367
|
function defaultLocateFile(file: string): string {
|
|
295
368
|
// Out-of-the-box: use the official CDN in browsers.
|
|
296
369
|
if (isBrowser()) {
|
|
@@ -371,9 +444,14 @@ export function isUsingSqljs(): boolean {
|
|
|
371
444
|
return getDbType(AppConfig?.store) == 'sqljs';
|
|
372
445
|
}
|
|
373
446
|
|
|
374
|
-
export function isVectorStoreSupported(): boolean {
|
|
375
|
-
|
|
376
|
-
|
|
447
|
+
export async function isVectorStoreSupported(): Promise<boolean> {
|
|
448
|
+
const dbType = getDbType(AppConfig?.store);
|
|
449
|
+
if (dbType === 'postgres') return true;
|
|
450
|
+
if (dbType === 'sqlite') {
|
|
451
|
+
const sqliteVecModule = await loadSqliteVec();
|
|
452
|
+
return !!sqliteVecModule;
|
|
453
|
+
}
|
|
454
|
+
return false;
|
|
377
455
|
}
|
|
378
456
|
|
|
379
457
|
export async function initDatabase(config: DatabaseConfig | undefined) {
|
|
@@ -442,50 +520,69 @@ export async function addRowForFullTextSearch(
|
|
|
442
520
|
vect: number[],
|
|
443
521
|
ctx: DbContext
|
|
444
522
|
) {
|
|
445
|
-
if (!isVectorStoreSupported()) return;
|
|
523
|
+
if (!(await isVectorStoreSupported())) return;
|
|
446
524
|
try {
|
|
447
525
|
const vecTableName = tableName + VectorSuffix;
|
|
448
526
|
const qb = getDatasourceForTransaction(ctx.txnId).createQueryBuilder();
|
|
449
527
|
const tenantId = await ctx.getTenantId();
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
528
|
+
const dbType = getDbType(AppConfig?.store);
|
|
529
|
+
if (dbType === 'postgres') {
|
|
530
|
+
const { default: pgvector } = await import('pgvector');
|
|
531
|
+
await qb
|
|
532
|
+
.insert()
|
|
533
|
+
.into(vecTableName)
|
|
534
|
+
.values([{ id: id, embedding: pgvector.toSql(vect), __tenant__: tenantId }])
|
|
535
|
+
.execute();
|
|
536
|
+
} else {
|
|
537
|
+
await qb
|
|
538
|
+
.insert()
|
|
539
|
+
.into(vecTableName)
|
|
540
|
+
.values([{ id: id, embedding: new Float32Array(vect) }])
|
|
541
|
+
.execute();
|
|
542
|
+
}
|
|
456
543
|
} catch (err: any) {
|
|
457
544
|
logger.error(`Failed to add row to vector store - ${err}`);
|
|
458
545
|
}
|
|
459
546
|
}
|
|
460
547
|
|
|
461
548
|
export async function initVectorStore(tableNames: string[], ctx: DbContext) {
|
|
462
|
-
if (!isVectorStoreSupported()) {
|
|
549
|
+
if (!(await isVectorStoreSupported())) {
|
|
463
550
|
logger.info(`Vector store not supported for ${getDbType(AppConfig?.store)}, skipping init...`);
|
|
464
551
|
return;
|
|
465
552
|
}
|
|
553
|
+
const dbType = getDbType(AppConfig?.store);
|
|
466
554
|
let notInited = true;
|
|
467
|
-
|
|
555
|
+
for (const vecTableName of tableNames) {
|
|
468
556
|
const vecRepo = getDatasourceForTransaction(ctx.txnId).getRepository(vecTableName);
|
|
469
|
-
if (
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
557
|
+
if (dbType === 'postgres') {
|
|
558
|
+
if (notInited) {
|
|
559
|
+
let failure = false;
|
|
560
|
+
try {
|
|
561
|
+
await vecRepo.query('CREATE EXTENSION IF NOT EXISTS vector');
|
|
562
|
+
} catch (err: any) {
|
|
563
|
+
logger.error(`Failed to initialize vector store - ${err}`);
|
|
564
|
+
failure = true;
|
|
565
|
+
}
|
|
566
|
+
if (failure) continue;
|
|
567
|
+
notInited = false;
|
|
476
568
|
}
|
|
477
|
-
|
|
478
|
-
|
|
569
|
+
await vecRepo.query(
|
|
570
|
+
`CREATE TABLE IF NOT EXISTS ${vecTableName} (
|
|
571
|
+
id varchar PRIMARY KEY,
|
|
572
|
+
embedding vector(${DefaultVectorDimension}),
|
|
573
|
+
${TenantAttributeName} varchar,
|
|
574
|
+
__is_deleted__ boolean default false
|
|
575
|
+
)`
|
|
576
|
+
);
|
|
577
|
+
} else {
|
|
578
|
+
// sqlite-vec - vec0 doesn't support type declarations for metadata columns
|
|
579
|
+
await vecRepo.query(
|
|
580
|
+
`CREATE VIRTUAL TABLE IF NOT EXISTS ${vecTableName} USING vec0(
|
|
581
|
+
id TEXT PRIMARY KEY,
|
|
582
|
+
embedding FLOAT[${DefaultVectorDimension}])`
|
|
583
|
+
);
|
|
479
584
|
}
|
|
480
|
-
|
|
481
|
-
`CREATE TABLE IF NOT EXISTS ${vecTableName} (
|
|
482
|
-
id varchar PRIMARY KEY,
|
|
483
|
-
embedding vector(${DefaultVectorDimension}),
|
|
484
|
-
${TenantAttributeName} varchar,
|
|
485
|
-
__is_deleted__ boolean default false
|
|
486
|
-
)`
|
|
487
|
-
);
|
|
488
|
-
});
|
|
585
|
+
}
|
|
489
586
|
}
|
|
490
587
|
|
|
491
588
|
export async function vectorStoreSearch(
|
|
@@ -494,7 +591,7 @@ export async function vectorStoreSearch(
|
|
|
494
591
|
limit: number,
|
|
495
592
|
ctx: DbContext
|
|
496
593
|
): Promise<any> {
|
|
497
|
-
if (!isVectorStoreSupported()) {
|
|
594
|
+
if (!(await isVectorStoreSupported())) {
|
|
498
595
|
// Not supported on sqljs/sqlite
|
|
499
596
|
return [];
|
|
500
597
|
}
|
|
@@ -508,18 +605,31 @@ export async function vectorStoreSearch(
|
|
|
508
605
|
}
|
|
509
606
|
const vecTableName = tableName + VectorSuffix;
|
|
510
607
|
const qb = getDatasourceForTransaction(ctx.txnId).getRepository(tableName).manager;
|
|
511
|
-
const
|
|
512
|
-
let ownersJoinCond: string = '';
|
|
608
|
+
const dbType = getDbType(AppConfig?.store);
|
|
513
609
|
const tenantId = await ctx.getTenantId();
|
|
610
|
+
let ownersJoinCond: string = '';
|
|
514
611
|
if (!hasGlobalPerms) {
|
|
515
612
|
const ot = ownersTable(tableName);
|
|
516
613
|
ownersJoinCond = `inner join ${ot} on
|
|
517
|
-
${ot}.path = ${vecTableName}.id and ${ot}.user_id = '${ctx.authInfo.userId}' and ${ot}.r = true
|
|
518
|
-
and ${ot}.${TenantAttributeName} = '${tenantId}'
|
|
614
|
+
${ot}.path = ${vecTableName}.id and ${ot}.user_id = '${ctx.authInfo.userId}' and ${ot}.r = true
|
|
615
|
+
and ${ot}.${TenantAttributeName} = '${tenantId}'`;
|
|
616
|
+
}
|
|
617
|
+
if (dbType === 'postgres') {
|
|
618
|
+
const { default: pgvector } = await import('pgvector');
|
|
619
|
+
const sql = `select ${vecTableName}.id from ${vecTableName} ${ownersJoinCond} order by embedding <-> $1 LIMIT ${limit}`;
|
|
620
|
+
const args = pgvector.toSql(searchVec);
|
|
621
|
+
return await qb.query(sql, [args]);
|
|
622
|
+
} else {
|
|
623
|
+
// sqlite-vec - join with main table to filter by tenant
|
|
624
|
+
const alias = tableName.toLowerCase();
|
|
625
|
+
const sql = `SELECT ${vecTableName}.id FROM ${vecTableName}
|
|
626
|
+
INNER JOIN ${tableName} ${alias} ON ${alias}.${PathAttributeName} = ${vecTableName}.id
|
|
627
|
+
${ownersJoinCond}
|
|
628
|
+
WHERE ${alias}.${TenantAttributeName} = '${tenantId}' AND ${alias}.${DeletedFlagAttributeName} = false AND ${vecTableName}.embedding MATCH $1
|
|
629
|
+
LIMIT ${limit}`;
|
|
630
|
+
const args = new Float32Array(searchVec);
|
|
631
|
+
return await qb.query(sql, [args]);
|
|
519
632
|
}
|
|
520
|
-
const sql = `select ${vecTableName}.id from ${vecTableName} ${ownersJoinCond} order by embedding <-> $1 LIMIT ${limit}`;
|
|
521
|
-
const args = pgvector.toSql(searchVec);
|
|
522
|
-
return await qb.query(sql, [args]);
|
|
523
633
|
} catch (err: any) {
|
|
524
634
|
logger.error(`Vector store search failed - ${err}`);
|
|
525
635
|
return [];
|
|
@@ -531,16 +641,30 @@ export async function vectorStoreSearchEntryExists(
|
|
|
531
641
|
id: string,
|
|
532
642
|
ctx: DbContext
|
|
533
643
|
): Promise<boolean> {
|
|
534
|
-
if (!isVectorStoreSupported()) return false;
|
|
644
|
+
if (!(await isVectorStoreSupported())) return false;
|
|
535
645
|
try {
|
|
536
646
|
const qb = getDatasourceForTransaction(ctx.txnId).getRepository(tableName).manager;
|
|
537
647
|
const vecTableName = tableName + VectorSuffix;
|
|
648
|
+
const dbType = getDbType(AppConfig?.store);
|
|
538
649
|
const tenantId = await ctx.getTenantId();
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
[
|
|
542
|
-
|
|
543
|
-
|
|
650
|
+
|
|
651
|
+
if (dbType === 'postgres') {
|
|
652
|
+
const result: any[] = await qb.query(
|
|
653
|
+
`select id from ${vecTableName} where id = $1 and ${TenantAttributeName} = '${tenantId}'`,
|
|
654
|
+
[id]
|
|
655
|
+
);
|
|
656
|
+
return result !== null && result.length > 0;
|
|
657
|
+
} else {
|
|
658
|
+
// sqlite-vec - join with main table to verify tenant
|
|
659
|
+
const alias = tableName.toLowerCase();
|
|
660
|
+
const result: any[] = await qb.query(
|
|
661
|
+
`SELECT ${vecTableName}.id FROM ${vecTableName}
|
|
662
|
+
INNER JOIN ${tableName} ${alias} ON ${alias}.${PathAttributeName} = ${vecTableName}.id
|
|
663
|
+
WHERE ${vecTableName}.id = $1 AND ${alias}.${TenantAttributeName} = '${tenantId}'`,
|
|
664
|
+
[id]
|
|
665
|
+
);
|
|
666
|
+
return result !== null && result.length > 0;
|
|
667
|
+
}
|
|
544
668
|
} catch (err: any) {
|
|
545
669
|
logger.error(`Vector store search failed - ${err}`);
|
|
546
670
|
}
|
|
@@ -548,15 +672,22 @@ export async function vectorStoreSearchEntryExists(
|
|
|
548
672
|
}
|
|
549
673
|
|
|
550
674
|
export async function deleteFullTextSearchEntry(tableName: string, id: string, ctx: DbContext) {
|
|
551
|
-
if (!isVectorStoreSupported()) return;
|
|
675
|
+
if (!(await isVectorStoreSupported())) return;
|
|
552
676
|
try {
|
|
553
677
|
const qb = getDatasourceForTransaction(ctx.txnId).getRepository(tableName).manager;
|
|
554
678
|
const vecTableName = tableName + VectorSuffix;
|
|
679
|
+
const dbType = getDbType(AppConfig?.store);
|
|
555
680
|
const tenantId = await ctx.getTenantId();
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
681
|
+
|
|
682
|
+
if (dbType === 'postgres') {
|
|
683
|
+
await qb.query(
|
|
684
|
+
`delete from ${vecTableName} where id = $1 and ${TenantAttributeName} = '${tenantId}'`,
|
|
685
|
+
[id]
|
|
686
|
+
);
|
|
687
|
+
} else {
|
|
688
|
+
// sqlite-vec - delete just by id (ownership verified by caller)
|
|
689
|
+
await qb.query(`delete from ${vecTableName} where id = $1`, [id]);
|
|
690
|
+
}
|
|
560
691
|
} catch (err: any) {
|
|
561
692
|
logger.error(`Vector store delete failed - ${err}`);
|
|
562
693
|
}
|