sdpy-kit 0.2.1__tar.gz → 0.2.2__tar.gz
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.
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/PKG-INFO +62 -19
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/README.md +61 -18
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/__init__.py +1 -1
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/db/__init__.py +2 -1
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/db/base_db_kit.py +25 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/db/engine.py +15 -8
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/db/exceptions.py +11 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/db/mysql_kit.py +100 -42
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/db/pg_kit.py +143 -49
- sdpy_kit-0.2.2/kit/db/sqlite_kit.py +408 -0
- sdpy_kit-0.2.1/kit/db/sqlite_kit.py +0 -271
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/.gitignore +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/ai/__init__.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/ai/base_service.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/ai/chat_service.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/ai/embedding_service.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/ai/exceptions.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/ai/failover.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/ai/image_service.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/ai/model_builder.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/ai/model_client.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/ai/pool.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/ai/profiles.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/ai/rerank_service.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/ai/runner.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/ai/types.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/anydoc_kit.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/common_kit.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/config/__init__.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/config/kit_config.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/config/log_config.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/db/crud.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/db/models/__init__.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/db/models/base.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/db/query.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/db/session.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/db/transaction.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/docx_kit.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/file_kit.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/marker_kit.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/mcp_kit.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/md2doc_kit.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/mineru_kit.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/redis_kit.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/wx_article_kit.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/kit/zvec_kit.py +0 -0
- {sdpy_kit-0.2.1 → sdpy_kit-0.2.2}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sdpy-kit
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: SDPY Kit——统一数据库访问层(PG/MySQL/SQLite)与 AI/MCP/Redis/文件/文档/向量工具集
|
|
5
5
|
Author-email: Clark <changhongyuan@126.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -400,8 +400,9 @@ with SQLiteKit.transaction() as session: # 多操作原子提
|
|
|
400
400
|
```
|
|
401
401
|
|
|
402
402
|
> 切换数据库:把 import 的 `SQLiteKit` 换成 `PgKit` / `MySqlKit` 即可,其余代码不变。
|
|
403
|
-
>
|
|
404
|
-
>
|
|
403
|
+
> 注意三点例外:① `create_raw_table` 的列定义文本须按目标库方言改写;② 向量能力
|
|
404
|
+
> **三库同名同参但能力不同**(可由 `VECTOR_CAPABILITIES` feature-detect),尤其
|
|
405
|
+
> **MySQL 侧检索不可用**;③ `create_vector_table` 的 `dims` 为**必填**参数。
|
|
405
406
|
|
|
406
407
|
### 2. 向量方法(三库同名同参,能力差异化)
|
|
407
408
|
|
|
@@ -412,21 +413,34 @@ with SQLiteKit.transaction() as session: # 多操作原子提
|
|
|
412
413
|
|
|
413
414
|
| 方法 | 说明 |
|
|
414
415
|
|------|------|
|
|
415
|
-
| `create_vector_table(table_name, id_col="id",
|
|
416
|
-
| `insert_vectors(table_name, vectors, ids=None, extra_data=None) -> int` | 批量插入向量(单事务提交);`ids=None` 时由 Kit 生成雪花 ID |
|
|
417
|
-
| `search_vectors(table_name, query_vector, top_k=10, distance_type="
|
|
416
|
+
| `create_vector_table(table_name, dims, id_col="id", column="embedding", extra_cols=None) -> None` | 建含向量列的表;**`dims` 必填**(SQLite 用 `CHECK` 落实为数据库层约束;PG 硬约束并回读校验已存在表;MySQL 为上限语义) |
|
|
417
|
+
| `insert_vectors(table_name, vectors, ids=None, extra_data=None, id_col="id", column="embedding") -> int` | 批量插入向量(单事务提交);`ids=None` 时由 Kit 生成雪花 ID;接受 `list[list[float]]` 或二维 numpy 数组 |
|
|
418
|
+
| `search_vectors(table_name, query_vector, top_k=10, distance_type="L2", filter_sql=None, exact=True, id_col="id", column="embedding") -> list[dict]` | KNN 检索,按距离升序返回 `{"id", "distance"}`;`exact=True`(默认)禁止近似加速;**MySQL 侧抛 `VectorNotSupportedError`** |
|
|
418
419
|
| `vector_version() -> str` | 向量后端版本;不可用时返回 `"unavailable"`(MySQL 侧为服务端版本号) |
|
|
419
420
|
|
|
420
|
-
|
|
421
|
+
**`id_col` / `column`(逃生口)**:让**自建 DDL 的向量表**也能读写/检索。需要复合主键、额外约束、
|
|
422
|
+
分区或与既有业务表合表时,用 `execute()` 自建 DDL 再用这两个参数接入;SQLite 侧要求 `id_col`
|
|
423
|
+
为 rowid 别名(`INTEGER PRIMARY KEY`)。
|
|
424
|
+
|
|
425
|
+
**`exact` 参数(默认 `True` = 禁止近似加速)**:
|
|
426
|
+
|
|
427
|
+
| 后端 | `exact=True`(默认) | `exact=False` | 是否提速 |
|
|
428
|
+
|------|---------------------|---------------|----------|
|
|
429
|
+
| `PgKit` | 同事务 `SET LOCAL enable_indexscan = off`(有索引也精确;**代价:该查询不使用任何索引**,含过滤列索引) | 不干预查询计划(有索引即近似召回,`distance` 仍为真值) | ✅ 仅"大表 + 已建索引"时 |
|
|
430
|
+
| `SQLiteKit` | `vector_full_scan`(**真值 L2**) | `vector_quantize_scan`(量化 L2 = 真值 × 255) | ❌ **不提速**(每次调用重建量化) |
|
|
431
|
+
| `MySqlKit` | 不适用 | 同左 | — |
|
|
432
|
+
|
|
433
|
+
**能力差异(仅 L2 可跨库比较数值;float32 vs float64,需带容差)**:
|
|
421
434
|
|
|
422
435
|
| 项 | `SQLiteKit`(sqlite-vector) | `PgKit`(pgvector) | `MySqlKit`(9.x 原生) |
|
|
423
436
|
|----|------------------------------|---------------------|------------------------|
|
|
424
|
-
| 支持的 `distance_type` | **仅 L2
|
|
425
|
-
|
|
|
426
|
-
|
|
|
427
|
-
|
|
|
428
|
-
|
|
|
429
|
-
|
|
|
437
|
+
| 支持的 `distance_type` | **仅 L2** | `COSINE` / `L2` / `L1` / `DOT`(负内积) / `SQUARED_L2` | **无(检索不可用)** |
|
|
438
|
+
| 默认 `distance_type` | `"L2"` | `"L2"`(三库统一,不按后端覆写) | `"L2"`(形参保留,调用即抛错) |
|
|
439
|
+
| 非法取值 | 抛 `ValueError` | 抛 `ValueError` | `search_vectors` 抛 `VectorNotSupportedError` |
|
|
440
|
+
| `distance` 口径 | `exact=True` 真值 L2 / `exact=False` 量化(×255) | 真实距离 | — |
|
|
441
|
+
| 维度约束 | `CHECK`(仅新表;旧表靠方法内校验) | 强制(不匹配即报错;建表时回读比对) | 上限约束(超限报错) |
|
|
442
|
+
| `filter_sql` 与 `top_k` | **先过滤再取 top_k**(与 PG 一致) | **先过滤再取 top_k** | 不适用 |
|
|
443
|
+
| 索引 | 无(精确路径为全表扫描) | kit 不建索引;**自建后须 `exact=False` 才会被使用** | 无法建(B-Tree 拒绝向量列) |
|
|
430
444
|
|
|
431
445
|
> MySQL `DISTANCE()` / `VECTOR_DISTANCE()` 仅 HeatWave on OCI 与 MySQL AI 提供
|
|
432
446
|
> (Community 实测报 `1305 FUNCTION ... does not exist`);故 `MySqlKit` 只覆盖存储侧。
|
|
@@ -438,24 +452,53 @@ with SQLiteKit.transaction() as session: # 多操作原子提
|
|
|
438
452
|
多实例部署需各自 `CommonKit.configure(datacenter_id, machine_id)`(默认 `(1,1)` 会重复)。
|
|
439
453
|
|
|
440
454
|
```python
|
|
441
|
-
# SQLite
|
|
455
|
+
# SQLite(默认精确:distance 为真值 L2)
|
|
442
456
|
from kit.db.sqlite_kit import SQLiteKit
|
|
443
|
-
SQLiteKit.create_vector_table("images",
|
|
457
|
+
SQLiteKit.create_vector_table("images", 384) # dims 必填
|
|
444
458
|
SQLiteKit.insert_vectors("images", [[0.1] * 384, [0.2] * 384])
|
|
445
459
|
rows = SQLiteKit.search_vectors("images", [0.1] * 384, top_k=5)
|
|
460
|
+
rows = SQLiteKit.search_vectors("images", [0.1] * 384, top_k=5, exact=False) # 旧量化口径(不提速)
|
|
446
461
|
|
|
447
462
|
# PostgreSQL(签名相同;可选 COSINE/L2/L1/DOT/SQUARED_L2)
|
|
448
463
|
from kit.db.pg_kit import PgKit
|
|
449
|
-
PgKit.create_vector_table("images",
|
|
464
|
+
PgKit.create_vector_table("images", 384)
|
|
450
465
|
PgKit.insert_vectors("images", [[0.1] * 384, [0.2] * 384])
|
|
451
|
-
rows = PgKit.search_vectors("images", [0.1] * 384, top_k=5, distance_type="
|
|
466
|
+
rows = PgKit.search_vectors("images", [0.1] * 384, top_k=5, distance_type="COSINE")
|
|
467
|
+
# 大表 + 自建索引时才用 exact=False(否则索引不被使用,见上表)
|
|
468
|
+
PgKit.execute("CREATE INDEX ON images USING hnsw (embedding vector_cosine_ops)")
|
|
469
|
+
rows = PgKit.search_vectors("images", [0.1] * 384, top_k=5,
|
|
470
|
+
distance_type="COSINE", exact=False)
|
|
452
471
|
|
|
453
|
-
# MySQL(仅存储;search_vectors
|
|
472
|
+
# MySQL(仅存储;search_vectors 抛 VectorNotSupportedError)
|
|
454
473
|
from kit.db.mysql_kit import MySqlKit
|
|
455
|
-
MySqlKit.create_vector_table("images",
|
|
474
|
+
MySqlKit.create_vector_table("images", 384)
|
|
456
475
|
MySqlKit.insert_vectors("images", [[0.1] * 384, [0.2] * 384])
|
|
476
|
+
|
|
477
|
+
# 能力自省(无需 try/except)
|
|
478
|
+
PgKit.VECTOR_CAPABILITIES
|
|
479
|
+
# {'store': True, 'search': True, 'exact': True,
|
|
480
|
+
# 'distances': ('COSINE', 'L2', 'L1', 'DOT', 'SQUARED_L2'),
|
|
481
|
+
# 'distance_kind': 'true', 'fast_distance_kind': 'true'}
|
|
482
|
+
|
|
483
|
+
# 自建 DDL 的表:用 id_col / column 接入
|
|
484
|
+
PgKit.execute("CREATE TABLE my_vec (vid BIGINT PRIMARY KEY, vec vector(384))")
|
|
485
|
+
PgKit.insert_vectors("my_vec", [[0.1] * 384], ids=[1], id_col="vid", column="vec")
|
|
486
|
+
PgKit.search_vectors("my_vec", [0.1] * 384, top_k=5, id_col="vid", column="vec")
|
|
457
487
|
```
|
|
458
488
|
|
|
489
|
+
#### 0.2.2 行为变更(升级须知)
|
|
490
|
+
|
|
491
|
+
| 编号 | 变更 | 影响 |
|
|
492
|
+
|------|------|------|
|
|
493
|
+
| B1 | `search_vectors` 新增 `exact: bool = True`,默认**精确** | SQLite 返回的 `distance` 由量化值(≈L2×255)变为**真值 L2**;需旧口径请显式 `exact=False` |
|
|
494
|
+
| B2 | `filter_sql` 语义统一为**先过滤、再取 top_k** | 传 `filter_sql` 的调用返回值**变多/变全**(旧结果 ⊆ 新结果);曾自行放大 `top_k` 兜底的调用可收紧 |
|
|
495
|
+
| B3 | 默认 `distance_type` 由 `"COSINE"` 改为 `"L2"` | 依赖默认值的调用方请显式传 `distance_type="COSINE"` |
|
|
496
|
+
| B4 | `create_vector_table` 的 `dims` **必填**,参数顺序为 `(table_name, dims, id_col, column, extra_cols)` | `create_vector_table("t")` 类调用需补维度;位置传 `id_col` 的调用改为关键字 |
|
|
497
|
+
| B5 | MySQL 的 `search_vectors` 异常由 `NotImplementedError` 改为 `VectorNotSupportedError`(继承 `DBError`) | `except NotImplementedError` 需改为 `except VectorNotSupportedError`(或 `except DBError`) |
|
|
498
|
+
|
|
499
|
+
> 新增(非破坏性):`VECTOR_CAPABILITIES` 能力声明、`id_col` / `column` 逃生口参数、
|
|
500
|
+
> `insert_vectors` 支持二维 numpy 数组与批量提交。
|
|
501
|
+
|
|
459
502
|
### 3. CRUDBase(配合 session_scope,不自动提交)
|
|
460
503
|
|
|
461
504
|
```python
|
|
@@ -330,8 +330,9 @@ with SQLiteKit.transaction() as session: # 多操作原子提
|
|
|
330
330
|
```
|
|
331
331
|
|
|
332
332
|
> 切换数据库:把 import 的 `SQLiteKit` 换成 `PgKit` / `MySqlKit` 即可,其余代码不变。
|
|
333
|
-
>
|
|
334
|
-
>
|
|
333
|
+
> 注意三点例外:① `create_raw_table` 的列定义文本须按目标库方言改写;② 向量能力
|
|
334
|
+
> **三库同名同参但能力不同**(可由 `VECTOR_CAPABILITIES` feature-detect),尤其
|
|
335
|
+
> **MySQL 侧检索不可用**;③ `create_vector_table` 的 `dims` 为**必填**参数。
|
|
335
336
|
|
|
336
337
|
### 2. 向量方法(三库同名同参,能力差异化)
|
|
337
338
|
|
|
@@ -342,21 +343,34 @@ with SQLiteKit.transaction() as session: # 多操作原子提
|
|
|
342
343
|
|
|
343
344
|
| 方法 | 说明 |
|
|
344
345
|
|------|------|
|
|
345
|
-
| `create_vector_table(table_name, id_col="id",
|
|
346
|
-
| `insert_vectors(table_name, vectors, ids=None, extra_data=None) -> int` | 批量插入向量(单事务提交);`ids=None` 时由 Kit 生成雪花 ID |
|
|
347
|
-
| `search_vectors(table_name, query_vector, top_k=10, distance_type="
|
|
346
|
+
| `create_vector_table(table_name, dims, id_col="id", column="embedding", extra_cols=None) -> None` | 建含向量列的表;**`dims` 必填**(SQLite 用 `CHECK` 落实为数据库层约束;PG 硬约束并回读校验已存在表;MySQL 为上限语义) |
|
|
347
|
+
| `insert_vectors(table_name, vectors, ids=None, extra_data=None, id_col="id", column="embedding") -> int` | 批量插入向量(单事务提交);`ids=None` 时由 Kit 生成雪花 ID;接受 `list[list[float]]` 或二维 numpy 数组 |
|
|
348
|
+
| `search_vectors(table_name, query_vector, top_k=10, distance_type="L2", filter_sql=None, exact=True, id_col="id", column="embedding") -> list[dict]` | KNN 检索,按距离升序返回 `{"id", "distance"}`;`exact=True`(默认)禁止近似加速;**MySQL 侧抛 `VectorNotSupportedError`** |
|
|
348
349
|
| `vector_version() -> str` | 向量后端版本;不可用时返回 `"unavailable"`(MySQL 侧为服务端版本号) |
|
|
349
350
|
|
|
350
|
-
|
|
351
|
+
**`id_col` / `column`(逃生口)**:让**自建 DDL 的向量表**也能读写/检索。需要复合主键、额外约束、
|
|
352
|
+
分区或与既有业务表合表时,用 `execute()` 自建 DDL 再用这两个参数接入;SQLite 侧要求 `id_col`
|
|
353
|
+
为 rowid 别名(`INTEGER PRIMARY KEY`)。
|
|
354
|
+
|
|
355
|
+
**`exact` 参数(默认 `True` = 禁止近似加速)**:
|
|
356
|
+
|
|
357
|
+
| 后端 | `exact=True`(默认) | `exact=False` | 是否提速 |
|
|
358
|
+
|------|---------------------|---------------|----------|
|
|
359
|
+
| `PgKit` | 同事务 `SET LOCAL enable_indexscan = off`(有索引也精确;**代价:该查询不使用任何索引**,含过滤列索引) | 不干预查询计划(有索引即近似召回,`distance` 仍为真值) | ✅ 仅"大表 + 已建索引"时 |
|
|
360
|
+
| `SQLiteKit` | `vector_full_scan`(**真值 L2**) | `vector_quantize_scan`(量化 L2 = 真值 × 255) | ❌ **不提速**(每次调用重建量化) |
|
|
361
|
+
| `MySqlKit` | 不适用 | 同左 | — |
|
|
362
|
+
|
|
363
|
+
**能力差异(仅 L2 可跨库比较数值;float32 vs float64,需带容差)**:
|
|
351
364
|
|
|
352
365
|
| 项 | `SQLiteKit`(sqlite-vector) | `PgKit`(pgvector) | `MySqlKit`(9.x 原生) |
|
|
353
366
|
|----|------------------------------|---------------------|------------------------|
|
|
354
|
-
| 支持的 `distance_type` | **仅 L2
|
|
355
|
-
|
|
|
356
|
-
|
|
|
357
|
-
|
|
|
358
|
-
|
|
|
359
|
-
|
|
|
367
|
+
| 支持的 `distance_type` | **仅 L2** | `COSINE` / `L2` / `L1` / `DOT`(负内积) / `SQUARED_L2` | **无(检索不可用)** |
|
|
368
|
+
| 默认 `distance_type` | `"L2"` | `"L2"`(三库统一,不按后端覆写) | `"L2"`(形参保留,调用即抛错) |
|
|
369
|
+
| 非法取值 | 抛 `ValueError` | 抛 `ValueError` | `search_vectors` 抛 `VectorNotSupportedError` |
|
|
370
|
+
| `distance` 口径 | `exact=True` 真值 L2 / `exact=False` 量化(×255) | 真实距离 | — |
|
|
371
|
+
| 维度约束 | `CHECK`(仅新表;旧表靠方法内校验) | 强制(不匹配即报错;建表时回读比对) | 上限约束(超限报错) |
|
|
372
|
+
| `filter_sql` 与 `top_k` | **先过滤再取 top_k**(与 PG 一致) | **先过滤再取 top_k** | 不适用 |
|
|
373
|
+
| 索引 | 无(精确路径为全表扫描) | kit 不建索引;**自建后须 `exact=False` 才会被使用** | 无法建(B-Tree 拒绝向量列) |
|
|
360
374
|
|
|
361
375
|
> MySQL `DISTANCE()` / `VECTOR_DISTANCE()` 仅 HeatWave on OCI 与 MySQL AI 提供
|
|
362
376
|
> (Community 实测报 `1305 FUNCTION ... does not exist`);故 `MySqlKit` 只覆盖存储侧。
|
|
@@ -368,24 +382,53 @@ with SQLiteKit.transaction() as session: # 多操作原子提
|
|
|
368
382
|
多实例部署需各自 `CommonKit.configure(datacenter_id, machine_id)`(默认 `(1,1)` 会重复)。
|
|
369
383
|
|
|
370
384
|
```python
|
|
371
|
-
# SQLite
|
|
385
|
+
# SQLite(默认精确:distance 为真值 L2)
|
|
372
386
|
from kit.db.sqlite_kit import SQLiteKit
|
|
373
|
-
SQLiteKit.create_vector_table("images",
|
|
387
|
+
SQLiteKit.create_vector_table("images", 384) # dims 必填
|
|
374
388
|
SQLiteKit.insert_vectors("images", [[0.1] * 384, [0.2] * 384])
|
|
375
389
|
rows = SQLiteKit.search_vectors("images", [0.1] * 384, top_k=5)
|
|
390
|
+
rows = SQLiteKit.search_vectors("images", [0.1] * 384, top_k=5, exact=False) # 旧量化口径(不提速)
|
|
376
391
|
|
|
377
392
|
# PostgreSQL(签名相同;可选 COSINE/L2/L1/DOT/SQUARED_L2)
|
|
378
393
|
from kit.db.pg_kit import PgKit
|
|
379
|
-
PgKit.create_vector_table("images",
|
|
394
|
+
PgKit.create_vector_table("images", 384)
|
|
380
395
|
PgKit.insert_vectors("images", [[0.1] * 384, [0.2] * 384])
|
|
381
|
-
rows = PgKit.search_vectors("images", [0.1] * 384, top_k=5, distance_type="
|
|
396
|
+
rows = PgKit.search_vectors("images", [0.1] * 384, top_k=5, distance_type="COSINE")
|
|
397
|
+
# 大表 + 自建索引时才用 exact=False(否则索引不被使用,见上表)
|
|
398
|
+
PgKit.execute("CREATE INDEX ON images USING hnsw (embedding vector_cosine_ops)")
|
|
399
|
+
rows = PgKit.search_vectors("images", [0.1] * 384, top_k=5,
|
|
400
|
+
distance_type="COSINE", exact=False)
|
|
382
401
|
|
|
383
|
-
# MySQL(仅存储;search_vectors
|
|
402
|
+
# MySQL(仅存储;search_vectors 抛 VectorNotSupportedError)
|
|
384
403
|
from kit.db.mysql_kit import MySqlKit
|
|
385
|
-
MySqlKit.create_vector_table("images",
|
|
404
|
+
MySqlKit.create_vector_table("images", 384)
|
|
386
405
|
MySqlKit.insert_vectors("images", [[0.1] * 384, [0.2] * 384])
|
|
406
|
+
|
|
407
|
+
# 能力自省(无需 try/except)
|
|
408
|
+
PgKit.VECTOR_CAPABILITIES
|
|
409
|
+
# {'store': True, 'search': True, 'exact': True,
|
|
410
|
+
# 'distances': ('COSINE', 'L2', 'L1', 'DOT', 'SQUARED_L2'),
|
|
411
|
+
# 'distance_kind': 'true', 'fast_distance_kind': 'true'}
|
|
412
|
+
|
|
413
|
+
# 自建 DDL 的表:用 id_col / column 接入
|
|
414
|
+
PgKit.execute("CREATE TABLE my_vec (vid BIGINT PRIMARY KEY, vec vector(384))")
|
|
415
|
+
PgKit.insert_vectors("my_vec", [[0.1] * 384], ids=[1], id_col="vid", column="vec")
|
|
416
|
+
PgKit.search_vectors("my_vec", [0.1] * 384, top_k=5, id_col="vid", column="vec")
|
|
387
417
|
```
|
|
388
418
|
|
|
419
|
+
#### 0.2.2 行为变更(升级须知)
|
|
420
|
+
|
|
421
|
+
| 编号 | 变更 | 影响 |
|
|
422
|
+
|------|------|------|
|
|
423
|
+
| B1 | `search_vectors` 新增 `exact: bool = True`,默认**精确** | SQLite 返回的 `distance` 由量化值(≈L2×255)变为**真值 L2**;需旧口径请显式 `exact=False` |
|
|
424
|
+
| B2 | `filter_sql` 语义统一为**先过滤、再取 top_k** | 传 `filter_sql` 的调用返回值**变多/变全**(旧结果 ⊆ 新结果);曾自行放大 `top_k` 兜底的调用可收紧 |
|
|
425
|
+
| B3 | 默认 `distance_type` 由 `"COSINE"` 改为 `"L2"` | 依赖默认值的调用方请显式传 `distance_type="COSINE"` |
|
|
426
|
+
| B4 | `create_vector_table` 的 `dims` **必填**,参数顺序为 `(table_name, dims, id_col, column, extra_cols)` | `create_vector_table("t")` 类调用需补维度;位置传 `id_col` 的调用改为关键字 |
|
|
427
|
+
| B5 | MySQL 的 `search_vectors` 异常由 `NotImplementedError` 改为 `VectorNotSupportedError`(继承 `DBError`) | `except NotImplementedError` 需改为 `except VectorNotSupportedError`(或 `except DBError`) |
|
|
428
|
+
|
|
429
|
+
> 新增(非破坏性):`VECTOR_CAPABILITIES` 能力声明、`id_col` / `column` 逃生口参数、
|
|
430
|
+
> `insert_vectors` 支持二维 numpy 数组与批量提交。
|
|
431
|
+
|
|
389
432
|
### 3. CRUDBase(配合 session_scope,不自动提交)
|
|
390
433
|
|
|
391
434
|
```python
|
|
@@ -41,7 +41,7 @@ from kit.db.engine import (
|
|
|
41
41
|
list_tables,
|
|
42
42
|
table_exists,
|
|
43
43
|
)
|
|
44
|
-
from kit.db.exceptions import DBError, NotFoundError
|
|
44
|
+
from kit.db.exceptions import DBError, NotFoundError, VectorNotSupportedError
|
|
45
45
|
from kit.db.session import get_session, session_scope
|
|
46
46
|
from kit.db.transaction import transactional
|
|
47
47
|
|
|
@@ -63,6 +63,7 @@ __all__ = [
|
|
|
63
63
|
# 异常
|
|
64
64
|
"DBError",
|
|
65
65
|
"NotFoundError",
|
|
66
|
+
"VectorNotSupportedError",
|
|
66
67
|
# CRUD
|
|
67
68
|
"CRUDBase",
|
|
68
69
|
# 事务
|
|
@@ -70,6 +70,11 @@ logger = get_default_logger(__name__)
|
|
|
70
70
|
# 字段数据:列名到值的映射(object 覆盖全部 SQLModel 支持类型)
|
|
71
71
|
type FieldData = dict[str, object]
|
|
72
72
|
|
|
73
|
+
# 向量检索的默认距离类型:三库能力集的交集(SQLite 仅支持 L2),
|
|
74
|
+
# 由三个数据库 Kit 的 search_vectors 共用,避免各 Kit 默认值漂移。
|
|
75
|
+
# 默认值不得按后端覆写——否则同一份代码换 import 后会在静默中改变度量语义。
|
|
76
|
+
DEFAULT_DISTANCE_TYPE = "L2"
|
|
77
|
+
|
|
73
78
|
# CRUD 实例缓存:CRUDBase 无状态(仅存 model),按 model 复用避免重复实例化
|
|
74
79
|
# 值类型取宽基类 SQLModel,规避 PEP 695 类型参数上界约束
|
|
75
80
|
_crud_cache: dict[type, CRUDBase[SQLModel, SQLModel, SQLModel]] = {}
|
|
@@ -98,6 +103,26 @@ def to_vector_literal(vector: builtins.list[float]) -> str:
|
|
|
98
103
|
return "[" + ",".join(repr(float(value)) for value in vector) + "]"
|
|
99
104
|
|
|
100
105
|
|
|
106
|
+
def vector_insert_placeholders(
|
|
107
|
+
columns: builtins.list[str], id_col: str, column: str
|
|
108
|
+
) -> str:
|
|
109
|
+
"""生成向量表 INSERT 的占位符串(供三个数据库 Kit 的 insert_vectors 共用)
|
|
110
|
+
|
|
111
|
+
绑定参数名与列名**解耦**:id 列固定为 ``:id``、向量列固定为 ``:vec``、
|
|
112
|
+
其余列用 ``:列名``。这样调用方覆写 ``id_col`` / ``column``(自建 DDL 场景)时,
|
|
113
|
+
参数字典的键不受列名影响。
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
def _one(name: str) -> str:
|
|
117
|
+
if name == column:
|
|
118
|
+
return ":vec"
|
|
119
|
+
if name == id_col:
|
|
120
|
+
return ":id"
|
|
121
|
+
return f":{name}"
|
|
122
|
+
|
|
123
|
+
return ", ".join(_one(name) for name in columns)
|
|
124
|
+
|
|
125
|
+
|
|
101
126
|
def generate_snowflake_ids(count: int) -> builtins.list[int]:
|
|
102
127
|
"""生成 count 个雪花 ID(int64;当前 18 位,上限 19 位)
|
|
103
128
|
|
|
@@ -22,6 +22,10 @@ Author: Clark Chang
|
|
|
22
22
|
|
|
23
23
|
支持 PostgreSQL、MySQL、SQLite 三种数据库。
|
|
24
24
|
使用 threading.Lock 保护 _engines 字典,避免 TOCTOU 竞态。
|
|
25
|
+
|
|
26
|
+
`build_pg_url` / `build_mysql_url` / `build_sqlite_url` 为**公开**的连接串构造器:
|
|
27
|
+
除本模块创建引擎外,测试与外部工具(如迁移脚本)也应复用它们,避免各自手抄导致取值漂移
|
|
28
|
+
(历史问题:`tests/db/conftest.py` 曾手抄 MySQL 串,漏了 `MYSQL_USER` 覆盖)。
|
|
25
29
|
"""
|
|
26
30
|
|
|
27
31
|
import threading
|
|
@@ -46,8 +50,11 @@ _engines: dict[str, Engine] = {}
|
|
|
46
50
|
_lock = threading.Lock()
|
|
47
51
|
|
|
48
52
|
|
|
49
|
-
def
|
|
50
|
-
"""构建 PostgreSQL 连接字符串
|
|
53
|
+
def build_pg_url(settings: Settings) -> str:
|
|
54
|
+
"""构建 PostgreSQL 连接字符串
|
|
55
|
+
|
|
56
|
+
取值全部来自 `DB_*` 配置(密码经 `quote_plus` 转义)。
|
|
57
|
+
"""
|
|
51
58
|
password = urllib.parse.quote_plus(settings.DB_PASSWORD)
|
|
52
59
|
return (
|
|
53
60
|
f"postgresql://{settings.DB_USER}:{password}"
|
|
@@ -56,10 +63,10 @@ def _build_pg_url(settings: Settings) -> str:
|
|
|
56
63
|
)
|
|
57
64
|
|
|
58
65
|
|
|
59
|
-
def
|
|
66
|
+
def build_mysql_url(settings: Settings) -> str:
|
|
60
67
|
"""构建 MySQL 连接字符串
|
|
61
68
|
|
|
62
|
-
|
|
69
|
+
端口 / 用户 / 库名 / 字符集通过 `MYSQL_*` 覆盖,主机与密码共用 `DB_HOST` / `DB_PASSWORD`。
|
|
63
70
|
"""
|
|
64
71
|
password = urllib.parse.quote_plus(settings.DB_PASSWORD)
|
|
65
72
|
port = getattr(settings, "MYSQL_PORT", settings.DB_PORT)
|
|
@@ -73,7 +80,7 @@ def _build_mysql_url(settings: Settings) -> str:
|
|
|
73
80
|
)
|
|
74
81
|
|
|
75
82
|
|
|
76
|
-
def
|
|
83
|
+
def build_sqlite_url(settings: Settings) -> str:
|
|
77
84
|
"""构建 SQLite 连接字符串"""
|
|
78
85
|
db_path = getattr(settings, "SQLITE_DB_PATH", "./data/sqlite.db")
|
|
79
86
|
return f"sqlite:///{db_path}"
|
|
@@ -115,11 +122,11 @@ def _create_engine(db_type: str, **kwargs: object) -> Engine:
|
|
|
115
122
|
settings = get_settings()
|
|
116
123
|
|
|
117
124
|
if db_type == POSTGRESQL:
|
|
118
|
-
url =
|
|
125
|
+
url = build_pg_url(settings)
|
|
119
126
|
elif db_type == MYSQL:
|
|
120
|
-
url =
|
|
127
|
+
url = build_mysql_url(settings)
|
|
121
128
|
elif db_type == SQLITE:
|
|
122
|
-
url =
|
|
129
|
+
url = build_sqlite_url(settings)
|
|
123
130
|
else:
|
|
124
131
|
raise ValueError(f"不支持的数据库类型: {db_type}")
|
|
125
132
|
|
|
@@ -32,3 +32,14 @@ class DBError(SQLAlchemyError):
|
|
|
32
32
|
|
|
33
33
|
class NotFoundError(DBError):
|
|
34
34
|
"""记录不存在"""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class VectorNotSupportedError(DBError):
|
|
38
|
+
"""当前后端不支持所请求的向量能力
|
|
39
|
+
|
|
40
|
+
用于语义上"该后端没有这条能力路径"的场景(区别于"执行失败")。典型用法:
|
|
41
|
+
``MySqlKit.search_vectors`` 在 MySQL Community / Commercial 发行版上恒定抛出本异常
|
|
42
|
+
(未提供 `DISTANCE()` 距离函数,且 `VECTOR` 列无法建立索引)。
|
|
43
|
+
|
|
44
|
+
仅继承 :class:`DBError`,遵循项目「异常 ``*Error`` 继承 ``DBError``」的统一错误面约定。
|
|
45
|
+
"""
|