@an5/adapters 0.2.13 → 0.2.14
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 +10 -9
- package/dist/an5Adapter.d.ts +5 -1
- package/dist/an5Adapter.js +10 -5
- package/dist/sqlite/vector.js +3 -7
- package/dotnet/An5Adapter.cs +14 -1
- package/java/pom.xml +1 -1
- package/java/src/main/java/an5/adapters/An5TableClient.java +0 -1
- package/kotlin/build.gradle.kts +1 -1
- package/native/sqlite/README.md +1 -1
- package/package.json +1 -1
- package/python/base/vectors.py +9 -12
- package/typescript/src/an5Adapter.ts +11 -4
- package/typescript/src/sqlite/vector.ts +6 -10
package/README.md
CHANGED
|
@@ -68,7 +68,7 @@ dragged in three drivers would not fit.
|
|
|
68
68
|
<dependency>
|
|
69
69
|
<groupId>io.github.an5orm</groupId>
|
|
70
70
|
<artifactId>an5-adapters-java</artifactId>
|
|
71
|
-
<version>0.2.
|
|
71
|
+
<version>0.2.14</version>
|
|
72
72
|
</dependency>
|
|
73
73
|
```
|
|
74
74
|
|
|
@@ -91,7 +91,7 @@ rather than reimplementing it, so the dialect rules and the where builder exist
|
|
|
91
91
|
|
|
92
92
|
```kotlin
|
|
93
93
|
dependencies {
|
|
94
|
-
implementation("io.github.an5orm:an5-adapters-kotlin:0.2.
|
|
94
|
+
implementation("io.github.an5orm:an5-adapters-kotlin:0.2.14")
|
|
95
95
|
}
|
|
96
96
|
```
|
|
97
97
|
|
|
@@ -115,7 +115,7 @@ input. It needs the repository secrets `MAVEN_CENTRAL_USERNAME` and `MAVEN_CENTR
|
|
|
115
115
|
### Swift
|
|
116
116
|
|
|
117
117
|
```swift
|
|
118
|
-
.package(url: "https://github.com/an5ORM/an5Adapters.git", from: "0.2.
|
|
118
|
+
.package(url: "https://github.com/an5ORM/an5Adapters.git", from: "0.2.14")
|
|
119
119
|
```
|
|
120
120
|
|
|
121
121
|
A SwiftPM package that links the **system** SQLite — the database every Apple platform
|
|
@@ -169,7 +169,7 @@ returns the NBase hits — id, metadata and `distance` — instead of table rows
|
|
|
169
169
|
With an `nbase://` connection string, search returns the hits themselves:
|
|
170
170
|
|
|
171
171
|
```ts
|
|
172
|
-
const hits = await db.
|
|
172
|
+
const hits = await db.article.vectorSearch({ vector: queryEmbedding, take: 5 });
|
|
173
173
|
// hits[0] = { id: 'Article:42', an5Id: '42', an5Model: 'Article', distance: 0.11 }
|
|
174
174
|
```
|
|
175
175
|
|
|
@@ -185,10 +185,10 @@ const db = createAn5Adapter({
|
|
|
185
185
|
nbase: 'nbase://localhost:1307',
|
|
186
186
|
});
|
|
187
187
|
|
|
188
|
-
await db.
|
|
188
|
+
await db.article.indexVectorsInNBase({ vectorField: 'embedding' });
|
|
189
189
|
// → { indexed: 1200 }
|
|
190
190
|
|
|
191
|
-
const hits = await db.
|
|
191
|
+
const hits = await db.article.vectorSearch({ vector: queryEmbedding, take: 5 });
|
|
192
192
|
// hits[0] = { id, title, ..., distance }
|
|
193
193
|
```
|
|
194
194
|
|
|
@@ -229,9 +229,9 @@ that already holds JSON text (`'[0.1, 0.2]'`) keeps working, so nothing has to
|
|
|
229
229
|
be migrated.
|
|
230
230
|
|
|
231
231
|
```ts
|
|
232
|
-
await db.
|
|
232
|
+
await db.article.create({ data: { title: '…', embedding: queryEmbedding } });
|
|
233
233
|
|
|
234
|
-
const row = await db.
|
|
234
|
+
const row = await db.article.findFirst({ where: { title: '…' } });
|
|
235
235
|
row.embedding; // number[]
|
|
236
236
|
```
|
|
237
237
|
|
|
@@ -260,7 +260,8 @@ const db = createAn5Adapter({
|
|
|
260
260
|
```
|
|
261
261
|
|
|
262
262
|
`vectorStrategy` pins one strategy instead of probing — `'auto'` (the default),
|
|
263
|
-
`'sqlite-vec'`, `'udf'`, `'sql'` or `'memory'`.
|
|
263
|
+
`'sqlite-vec'`, `'udf'`, `'sql'` or `'memory'`. In TypeScript it can also be
|
|
264
|
+
supplied per query on `vectorSearch`. Every other runtime implements
|
|
264
265
|
the same four strategies with the same order; `sqlx` (Rust) and JDBC (Java,
|
|
265
266
|
Kotlin) use `sql` for JSON text and `memory` for BLOB columns without
|
|
266
267
|
driver-specific vector functions.
|
package/dist/an5Adapter.d.ts
CHANGED
|
@@ -134,9 +134,12 @@ export declare class ViewClient<T = any> {
|
|
|
134
134
|
export declare class An5AdapterTx {
|
|
135
135
|
private readonly handle;
|
|
136
136
|
readonly dialect: Dialect;
|
|
137
|
+
private readonly parentVectorSupport;
|
|
138
|
+
readonly vectorStrategy?: VectorStrategyPreference | undefined;
|
|
137
139
|
[key: string]: any;
|
|
138
140
|
private closed;
|
|
139
|
-
constructor(handle: TransactionHandle, dialect: Dialect);
|
|
141
|
+
constructor(handle: TransactionHandle, dialect: Dialect, parentVectorSupport?: SqliteVectorSupport | null, vectorStrategy?: VectorStrategyPreference | undefined);
|
|
142
|
+
sqliteVectorSupport(): Promise<SqliteVectorSupport | null>;
|
|
140
143
|
exec<T = any>(query: string, params?: Record<string, any>): Promise<T[]>;
|
|
141
144
|
_executeRaw(query: string, params?: Record<string, any>): Promise<number>;
|
|
142
145
|
$commit(): Promise<void>;
|
|
@@ -271,6 +274,7 @@ export declare class AdapterTableClient<T = any> {
|
|
|
271
274
|
vectorField?: string;
|
|
272
275
|
distanceMetric?: 'cosine' | 'euclidean' | 'dot';
|
|
273
276
|
vectorElementType?: 'float32' | 'float16' | 'uint8';
|
|
277
|
+
vectorStrategy?: VectorStrategyPreference;
|
|
274
278
|
}): Promise<(T & {
|
|
275
279
|
distance: number;
|
|
276
280
|
})[]>;
|
package/dist/an5Adapter.js
CHANGED
|
@@ -360,7 +360,7 @@ class An5Adapter {
|
|
|
360
360
|
}
|
|
361
361
|
/** `vectorStrategy` from the config, so a project can pin one strategy. */
|
|
362
362
|
get vectorStrategy() {
|
|
363
|
-
return this._engineConfig?.vectorStrategy;
|
|
363
|
+
return this._engineConfig?.vectorStrategy ?? this.engine?.vectorSupport?.options?.vectorStrategy;
|
|
364
364
|
}
|
|
365
365
|
/** INTERNAL: used by AdapterTableClient for DML statements needing row count */
|
|
366
366
|
async _executeRaw(query, params) {
|
|
@@ -449,7 +449,7 @@ class An5Adapter {
|
|
|
449
449
|
return Promise.all(fn);
|
|
450
450
|
const engine = await this.requireEngine();
|
|
451
451
|
const handle = await engine.beginTransaction();
|
|
452
|
-
const txAdapter = new An5AdapterTx(handle, this.dialect);
|
|
452
|
+
const txAdapter = new An5AdapterTx(handle, this.dialect, engine.vectorSupport ?? null, this.vectorStrategy);
|
|
453
453
|
try {
|
|
454
454
|
const result = await fn(txAdapter);
|
|
455
455
|
await handle.commit();
|
|
@@ -465,7 +465,7 @@ class An5Adapter {
|
|
|
465
465
|
throw new Error('Interactive transactions are not supported by the Google Sheets adapter');
|
|
466
466
|
}
|
|
467
467
|
const engine = await this.requireEngine();
|
|
468
|
-
return new An5AdapterTx(await engine.beginTransaction(), this.dialect);
|
|
468
|
+
return new An5AdapterTx(await engine.beginTransaction(), this.dialect, engine.vectorSupport ?? null, this.vectorStrategy);
|
|
469
469
|
}
|
|
470
470
|
table(modelName) {
|
|
471
471
|
if (this.sheetsAdapter)
|
|
@@ -633,12 +633,17 @@ class ViewClient {
|
|
|
633
633
|
exports.ViewClient = ViewClient;
|
|
634
634
|
// ─── Transaction-scoped adapter ────────────────────────────────────────────────────
|
|
635
635
|
class An5AdapterTx {
|
|
636
|
-
constructor(handle, dialect) {
|
|
636
|
+
constructor(handle, dialect, parentVectorSupport = null, vectorStrategy) {
|
|
637
637
|
this.handle = handle;
|
|
638
638
|
this.dialect = dialect;
|
|
639
|
+
this.parentVectorSupport = parentVectorSupport;
|
|
640
|
+
this.vectorStrategy = vectorStrategy;
|
|
639
641
|
this.closed = false;
|
|
640
642
|
return (0, metadata_1.createAdapterProxy)(this, (name) => this.table(name));
|
|
641
643
|
}
|
|
644
|
+
async sqliteVectorSupport() {
|
|
645
|
+
return this.dialect === 'sqlite' ? this.parentVectorSupport : null;
|
|
646
|
+
}
|
|
642
647
|
async exec(query, params) {
|
|
643
648
|
this.ensureOpen();
|
|
644
649
|
return this.handle.exec(query, params);
|
|
@@ -1412,7 +1417,7 @@ class AdapterTableClient {
|
|
|
1412
1417
|
take,
|
|
1413
1418
|
tail: whereSql ? ` WHERE ${whereSql}` : '',
|
|
1414
1419
|
params,
|
|
1415
|
-
preference: this.adapter.vectorStrategy,
|
|
1420
|
+
preference: args.vectorStrategy ?? this.adapter.vectorStrategy ?? support?.options?.vectorStrategy,
|
|
1416
1421
|
});
|
|
1417
1422
|
if (ranked)
|
|
1418
1423
|
return this.decodeVectorFields(ranked.rows);
|
package/dist/sqlite/vector.js
CHANGED
|
@@ -351,12 +351,8 @@ class SqliteVectorSupport {
|
|
|
351
351
|
* since taken new float32 rows would otherwise rank just the old ones.
|
|
352
352
|
*/
|
|
353
353
|
async hasBinaryRows(exec, table, column, tail, params) {
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
const rows = await exec(`SELECT 1 FROM ${table}${tail ? `${tail} AND` : ' WHERE'} typeof(${column}) = 'blob' LIMIT 1`, params).catch(() => []);
|
|
357
|
-
this.binaryRows.set(key, rows.length > 0);
|
|
358
|
-
}
|
|
359
|
-
return this.binaryRows.get(key) === true;
|
|
354
|
+
const rows = await exec(`SELECT 1 FROM ${table}${tail ? `${tail} AND` : ' WHERE'} typeof(${column}) = 'blob' LIMIT 1`, params).catch(() => []);
|
|
355
|
+
return rows.length > 0;
|
|
360
356
|
}
|
|
361
357
|
reset() {
|
|
362
358
|
this.caps = null;
|
|
@@ -406,7 +402,7 @@ async function runSqliteVectorSearch(args) {
|
|
|
406
402
|
for (const strategy of planSqliteVectorStrategies(caps, declaredType, args.preference)) {
|
|
407
403
|
if (strategy === 'sql') {
|
|
408
404
|
const binary = args.vectorSupport
|
|
409
|
-
? await args.vectorSupport.hasBinaryRows(args.exec, args.
|
|
405
|
+
? await args.vectorSupport.hasBinaryRows(args.exec, args.table, args.column, args.tail ?? '', args.params)
|
|
410
406
|
: (await args.exec(`SELECT 1 FROM ${args.table}${args.tail ? `${args.tail} AND` : ' WHERE'} typeof(${args.column}) = 'blob' LIMIT 1`, args.params)).length > 0;
|
|
411
407
|
if (binary)
|
|
412
408
|
continue;
|
package/dotnet/An5Adapter.cs
CHANGED
|
@@ -908,7 +908,20 @@ namespace An5Orm
|
|
|
908
908
|
public List<(T Item, double Distance)> VectorSearch(VectorSearchArgs args)
|
|
909
909
|
{
|
|
910
910
|
args ??= new VectorSearchArgs();
|
|
911
|
-
|
|
911
|
+
Dictionary<string, object> parameters = null;
|
|
912
|
+
string whereSql = null;
|
|
913
|
+
if (args.Where != null)
|
|
914
|
+
{
|
|
915
|
+
parameters = new Dictionary<string, object>();
|
|
916
|
+
whereSql = SqlBuilder.ParseWhere(args.Where, parameters, _dialect);
|
|
917
|
+
}
|
|
918
|
+
return VectorSearch(
|
|
919
|
+
args.Vector,
|
|
920
|
+
args.Take,
|
|
921
|
+
string.IsNullOrEmpty(whereSql) ? null : whereSql,
|
|
922
|
+
parameters,
|
|
923
|
+
args.VectorField,
|
|
924
|
+
args.DistanceMetric);
|
|
912
925
|
}
|
|
913
926
|
|
|
914
927
|
public T FindFirst(string whereClause = null, Dictionary<string, object> parameters = null, string orderBy = null)
|
package/java/pom.xml
CHANGED
|
@@ -486,7 +486,6 @@ public class An5TableClient {
|
|
|
486
486
|
int limit = take == null ? 10 : take;
|
|
487
487
|
String field = vectorField == null || vectorField.isEmpty() ? "embedding" : vectorField;
|
|
488
488
|
String distanceMetric = metric == null || metric.isEmpty() ? "cosine" : metric;
|
|
489
|
-
int dimension = vector.length;
|
|
490
489
|
|
|
491
490
|
Map<String, Object> params = new LinkedHashMap<String, Object>();
|
|
492
491
|
SqlBuilder.Where clause = SqlBuilder.parseWhere(where, dialect, params, "");
|
package/kotlin/build.gradle.kts
CHANGED
|
@@ -9,7 +9,7 @@ plugins {
|
|
|
9
9
|
// phone app cannot resolve is not a mobile runtime. Maven consumers can still use the
|
|
10
10
|
// published Maven coordinates; this module publishes both.
|
|
11
11
|
group = "io.github.an5orm"
|
|
12
|
-
version = "0.2.
|
|
12
|
+
version = "0.2.14"
|
|
13
13
|
|
|
14
14
|
repositories {
|
|
15
15
|
// The Java runtime is a sibling checkout, not a published artifact during development:
|
package/native/sqlite/README.md
CHANGED
|
@@ -63,7 +63,7 @@ const db = createAn5Adapter({
|
|
|
63
63
|
connectionString: 'sqlite:///app.db',
|
|
64
64
|
sqliteVec: '/absolute/path/an5_vector.so', // .dylib / .dll on other platforms
|
|
65
65
|
});
|
|
66
|
-
await db.
|
|
66
|
+
await db.document.vectorSearch({ vector: query, take: 10 });
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
```python
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@an5/adapters",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.14",
|
|
4
4
|
"description": "Standalone language adapters for AN5 ORM - TypeScript, Python, .NET, Go, Rust, Java, Kotlin, Swift, and Google Sheets query execution.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
package/python/base/vectors.py
CHANGED
|
@@ -313,17 +313,14 @@ class SqliteVectorSupport:
|
|
|
313
313
|
Checked only for the `sql` strategy, where a legacy JSON column that has
|
|
314
314
|
since taken new float32 rows would otherwise rank just the old ones.
|
|
315
315
|
"""
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
except Exception:
|
|
325
|
-
self._has_binary_rows[key] = False
|
|
326
|
-
return self._has_binary_rows[key]
|
|
316
|
+
clause = (tail + " AND ") if tail else " WHERE "
|
|
317
|
+
try:
|
|
318
|
+
rows = exec_fn(
|
|
319
|
+
f"SELECT 1 FROM {table}{clause}typeof({column}) = 'blob' LIMIT 1",
|
|
320
|
+
list(params))
|
|
321
|
+
return bool(rows)
|
|
322
|
+
except Exception:
|
|
323
|
+
return False
|
|
327
324
|
|
|
328
325
|
def capabilities(self) -> Dict[str, bool]:
|
|
329
326
|
"""Probes once and caches the answer."""
|
|
@@ -415,7 +412,7 @@ def run_sqlite_vector_search(
|
|
|
415
412
|
else read_sqlite_column_type(exec_fn, raw_table, raw_column))
|
|
416
413
|
for strategy in plan_sqlite_vector_strategies(caps, declared, preference):
|
|
417
414
|
if strategy == STRATEGY_SQL:
|
|
418
|
-
binary = (support.has_binary_rows(exec_fn,
|
|
415
|
+
binary = (support.has_binary_rows(exec_fn, table, column, tail, list(params or []))
|
|
419
416
|
if support is not None
|
|
420
417
|
else bool(exec_fn("SELECT 1 FROM " + table + (tail + " AND " if tail else " WHERE ")
|
|
421
418
|
+ f"typeof({column}) = 'blob' LIMIT 1", list(params or []))))
|
|
@@ -411,7 +411,7 @@ export class An5Adapter {
|
|
|
411
411
|
|
|
412
412
|
/** `vectorStrategy` from the config, so a project can pin one strategy. */
|
|
413
413
|
get vectorStrategy(): VectorStrategyPreference | undefined {
|
|
414
|
-
return this._engineConfig?.vectorStrategy;
|
|
414
|
+
return this._engineConfig?.vectorStrategy ?? (this.engine as any)?.vectorSupport?.options?.vectorStrategy;
|
|
415
415
|
}
|
|
416
416
|
|
|
417
417
|
/** INTERNAL: used by AdapterTableClient for DML statements needing row count */
|
|
@@ -500,7 +500,7 @@ export class An5Adapter {
|
|
|
500
500
|
if (Array.isArray(fn)) return Promise.all(fn);
|
|
501
501
|
const engine = await this.requireEngine();
|
|
502
502
|
const handle = await engine.beginTransaction();
|
|
503
|
-
const txAdapter = new An5AdapterTx(handle, this.dialect);
|
|
503
|
+
const txAdapter = new An5AdapterTx(handle, this.dialect, engine.vectorSupport ?? null, this.vectorStrategy);
|
|
504
504
|
try {
|
|
505
505
|
const result = await fn(txAdapter);
|
|
506
506
|
await handle.commit();
|
|
@@ -516,7 +516,7 @@ export class An5Adapter {
|
|
|
516
516
|
throw new Error('Interactive transactions are not supported by the Google Sheets adapter');
|
|
517
517
|
}
|
|
518
518
|
const engine = await this.requireEngine();
|
|
519
|
-
return new An5AdapterTx(await engine.beginTransaction(), this.dialect);
|
|
519
|
+
return new An5AdapterTx(await engine.beginTransaction(), this.dialect, engine.vectorSupport ?? null, this.vectorStrategy);
|
|
520
520
|
}
|
|
521
521
|
|
|
522
522
|
table<T = any>(modelName: string): AdapterTableClient<T> | SheetsTableClient<T> {
|
|
@@ -714,10 +714,16 @@ export class An5AdapterTx {
|
|
|
714
714
|
constructor(
|
|
715
715
|
private readonly handle: TransactionHandle,
|
|
716
716
|
public readonly dialect: Dialect,
|
|
717
|
+
private readonly parentVectorSupport: SqliteVectorSupport | null = null,
|
|
718
|
+
public readonly vectorStrategy?: VectorStrategyPreference | undefined,
|
|
717
719
|
) {
|
|
718
720
|
return createAdapterProxy(this, (name) => this.table(name));
|
|
719
721
|
}
|
|
720
722
|
|
|
723
|
+
async sqliteVectorSupport(): Promise<SqliteVectorSupport | null> {
|
|
724
|
+
return this.dialect === 'sqlite' ? this.parentVectorSupport : null;
|
|
725
|
+
}
|
|
726
|
+
|
|
721
727
|
async exec<T = any>(query: string, params?: Record<string, any>): Promise<T[]> {
|
|
722
728
|
this.ensureOpen();
|
|
723
729
|
return this.handle.exec<T>(query, params);
|
|
@@ -1462,6 +1468,7 @@ export class AdapterTableClient<T = any> {
|
|
|
1462
1468
|
vectorField?: string;
|
|
1463
1469
|
distanceMetric?: 'cosine' | 'euclidean' | 'dot';
|
|
1464
1470
|
vectorElementType?: 'float32' | 'float16' | 'uint8';
|
|
1471
|
+
vectorStrategy?: VectorStrategyPreference;
|
|
1465
1472
|
}): Promise<(T & { distance: number })[]> {
|
|
1466
1473
|
const vectorField = args.vectorField || 'embedding';
|
|
1467
1474
|
const METRICS = ['cosine', 'euclidean', 'dot'];
|
|
@@ -1521,7 +1528,7 @@ export class AdapterTableClient<T = any> {
|
|
|
1521
1528
|
take,
|
|
1522
1529
|
tail: whereSql ? ` WHERE ${whereSql}` : '',
|
|
1523
1530
|
params,
|
|
1524
|
-
preference: (this.adapter as any).vectorStrategy,
|
|
1531
|
+
preference: args.vectorStrategy ?? (this.adapter as any).vectorStrategy ?? support?.options?.vectorStrategy,
|
|
1525
1532
|
});
|
|
1526
1533
|
if (ranked) return this.decodeVectorFields(ranked.rows) as (T & { distance: number })[];
|
|
1527
1534
|
// No in-database strategy: score the column in the client instead.
|
|
@@ -422,15 +422,11 @@ export class SqliteVectorSupport {
|
|
|
422
422
|
tail: string,
|
|
423
423
|
params: Record<string, any>,
|
|
424
424
|
): Promise<boolean> {
|
|
425
|
-
const
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
).catch(() => []);
|
|
431
|
-
this.binaryRows.set(key, rows.length > 0);
|
|
432
|
-
}
|
|
433
|
-
return this.binaryRows.get(key) === true;
|
|
425
|
+
const rows = await exec(
|
|
426
|
+
`SELECT 1 FROM ${table}${tail ? `${tail} AND` : ' WHERE'} typeof(${column}) = 'blob' LIMIT 1`,
|
|
427
|
+
params,
|
|
428
|
+
).catch(() => []);
|
|
429
|
+
return rows.length > 0;
|
|
434
430
|
}
|
|
435
431
|
|
|
436
432
|
reset(): void {
|
|
@@ -510,7 +506,7 @@ export async function runSqliteVectorSearch(
|
|
|
510
506
|
for (const strategy of planSqliteVectorStrategies(caps, declaredType, args.preference)) {
|
|
511
507
|
if (strategy === 'sql') {
|
|
512
508
|
const binary = args.vectorSupport
|
|
513
|
-
? await args.vectorSupport.hasBinaryRows(args.exec, args.
|
|
509
|
+
? await args.vectorSupport.hasBinaryRows(args.exec, args.table, args.column, args.tail ?? '', args.params)
|
|
514
510
|
: (await args.exec(
|
|
515
511
|
`SELECT 1 FROM ${args.table}${args.tail ? `${args.tail} AND` : ' WHERE'} typeof(${args.column}) = 'blob' LIMIT 1`,
|
|
516
512
|
args.params,
|