sdpy-kit 0.6.2__tar.gz → 0.6.4__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.6.2 → sdpy_kit-0.6.4}/PKG-INFO +40 -9
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/README.md +39 -8
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/__init__.py +1 -1
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/ai/embedding_service.py +57 -1
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/ai/profiles.py +3 -1
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/ai/runner.py +5 -3
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/db/mysql_kit.py +42 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/db/pg_kit.py +44 -1
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/db/sqlite_kit.py +29 -1
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/docs/md2doc_kit.py +540 -14
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/.gitignore +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/ai/__init__.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/ai/base_service.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/ai/chat_service.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/ai/exceptions.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/ai/failover.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/ai/image_service.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/ai/model_builder.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/ai/model_client.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/ai/pool.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/ai/rerank_service.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/ai/types.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/common_kit.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/config/__init__.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/config/kit_config.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/config/log_config.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/crawl/__init__.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/crawl/article_io.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/crawl/article_models.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/crawl/deepseek_kit.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/crawl/web_article_kit.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/crawl/wx_article_kit.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/db/__init__.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/db/audit.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/db/base_db_kit.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/db/crud.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/db/engine.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/db/exceptions.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/db/models/__init__.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/db/models/base.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/db/query.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/db/session.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/db/transaction.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/docs/__init__.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/docs/anydoc_kit.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/docs/docx_kit.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/docs/marker_kit.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/docs/mineru_kit.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/docs/rc_docparser_kit.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/file_kit.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/laya_kit.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/license_kit.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/kit/redis_kit.py +0 -0
- {sdpy_kit-0.6.2 → sdpy_kit-0.6.4}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sdpy-kit
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.4
|
|
4
4
|
Summary: SDPY Kit——统一数据库访问层(PG/MySQL/SQLite)与 AI/Redis/文件/文档/向量工具集
|
|
5
5
|
Author-email: Clark <changhongyuan@126.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -98,7 +98,7 @@ SDPY 统一工具集(Kit 体系),供 Python 项目开箱即用:
|
|
|
98
98
|
| `kit.docs.anydoc_kit` | `AnyDocKit` | docx / pdf / xlsx 等 → Markdown | `anydoc` |
|
|
99
99
|
| `kit.docs.docx_kit` | `DocxKit` | Word 模板动态填充(章节 / 列表 / 表格) | `docx` |
|
|
100
100
|
| `kit.docs.rc_docparser_kit` | `RcDocParserKit` | 离线 docx → Markdown + Images(无模型) | `docx-parse` |
|
|
101
|
-
| `kit.docs.md2doc_kit` | `Md2DocKit` | Markdown → Word / PDF / HTML | `md2doc` |
|
|
101
|
+
| `kit.docs.md2doc_kit` | `Md2DocKit` | Markdown → Word / PDF / HTML(含图片) | `md2doc` |
|
|
102
102
|
| `kit.docs.mineru_kit` | `MinerUKit` | MinerU 文档解析(精准 + Agent 轻量) | `mineru` |
|
|
103
103
|
| `kit.docs.marker_kit` | `MarkerKit` | 本地解析(PDF / 图片 → Markdown + Images) | `marker` |
|
|
104
104
|
| `kit.laya_kit` | `LayaKit` | 类型化决策:choice / score / noul,返回判断结果 + 置信度 | `laya` |
|
|
@@ -125,7 +125,7 @@ uv add sdpy-kit
|
|
|
125
125
|
| `sqliteai` | SQLiteKit 向量方法(numpy / sqliteai-vector,SQLite 向量扩展) | `pip install "sdpy-kit[sqliteai]"` |
|
|
126
126
|
| `redis` | RedisKit | `pip install "sdpy-kit[redis]"` |
|
|
127
127
|
| `ai` | AI 服务池(httpx + python-dotenv) | `pip install "sdpy-kit[ai]"` |
|
|
128
|
-
| `md2doc` | Md2DocKit:Markdown → Word / PDF / HTML(pimd / fpdf2
|
|
128
|
+
| `md2doc` | Md2DocKit:Markdown → Word / PDF / HTML(pimd / fpdf2,含图片) | `pip install "sdpy-kit[md2doc]"` |
|
|
129
129
|
| `anydoc` | AnyDocKit 文档转 Markdown(firecrawl-anydoc + pikepdf) | `pip install "sdpy-kit[anydoc]"` |
|
|
130
130
|
| `docx` | DocxKit 模板动态填充(docxtpl) | `pip install "sdpy-kit[docx]"` |
|
|
131
131
|
| `docx-parse` | RcDocParserKit 离线 docx 解析(rc-docparser → Markdown + images/) | `pip install "sdpy-kit[docx-parse]"` |
|
|
@@ -451,6 +451,7 @@ with SQLiteKit.transaction() as session: # 多操作原子提
|
|
|
451
451
|
| `insert_vectors(table_name, vectors, ids=None, extra_data=None, id_col="id", column="embedding") -> int` | 批量插入向量(**自成一事务并提交**);`ids=None` 时由 Kit 生成雪花 ID;接受 `list[list[float]]` 或二维 numpy 数组 |
|
|
452
452
|
| `build_insert_vectors(table_name, vectors, ids=None, extra_data=None, id_col="id", column="embedding") -> list[tuple[str, list[dict]]]` | **只拼装、不执行、不管事务**:返回按列集分组的 `(SQL, 参数列表)`,供调用方在**自有事务**内写向量行(见「自有事务内写向量」) |
|
|
453
453
|
| `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`** |
|
|
454
|
+
| `vector_column_dimension(table_name, column="embedding") -> int \| None` | 查列**声明的维度**:PG 为精确维度(`vector(1024)`→1024);MySQL 为**最大上限**(未指定维度落 `2048`);SQLite 恒 `None`(无元数据)。列不存在 / 非向量列 / 无维度声明 → `None`,不抛异常。口径差异由 `VECTOR_CAPABILITIES["dimension_kind"]` 声明(`"exact"` / `"max"` / `None`) |
|
|
454
455
|
| `vector_version() -> str` | 向量后端版本;不可用时返回 `"unavailable"`(MySQL 侧为服务端版本号) |
|
|
455
456
|
|
|
456
457
|
**`id_col` / `column`(逃生口)**:让**自建 DDL 的向量表**也能读写/检索。需要复合主键、额外约束、
|
|
@@ -473,12 +474,17 @@ with SQLiteKit.transaction() as session: # 多操作原子提
|
|
|
473
474
|
| 默认 `distance_type` | `"L2"` | `"L2"`(三库统一,不按后端覆写) | `"L2"`(形参保留,调用即抛错) |
|
|
474
475
|
| 非法取值 | 抛 `ValueError` | 抛 `ValueError` | `search_vectors` 抛 `VectorNotSupportedError` |
|
|
475
476
|
| `distance` 口径 | `exact=True` 真值 L2 / `exact=False` 量化(×255) | 真实距离 | — |
|
|
476
|
-
| 维度约束 | `CHECK`(仅新表;旧表靠方法内校验) | 强制(不匹配即报错;建表时回读比对) |
|
|
477
|
+
| 维度约束 | `CHECK`(仅新表;旧表靠方法内校验) | 强制(不匹配即报错;建表时回读比对) | **上限**(`VECTOR(N)` 的 N 是最大维度,写入须 ≤ N,超限报 `1406 Data too long`) |
|
|
478
|
+
| 维度查询 | 恒 `None`(无元数据) | 精确维度(`dimension_kind="exact"`) | 最大上限(`dimension_kind="max"`) |
|
|
477
479
|
| `filter_sql` 与 `top_k` | **先过滤再取 top_k**(与 PG 一致) | **先过滤再取 top_k** | 不适用 |
|
|
478
480
|
| 索引 | 无(精确路径为全表扫描) | kit 不建索引;**自建后须 `exact=False` 才会被使用** | 无法建(B-Tree 拒绝向量列) |
|
|
479
481
|
|
|
480
482
|
> MySQL `DISTANCE()` / `VECTOR_DISTANCE()` 仅 HeatWave on OCI 与 MySQL AI 提供
|
|
481
483
|
> (Community 实测报 `1305 FUNCTION ... does not exist`);故 `MySqlKit` 只覆盖存储侧。
|
|
484
|
+
>
|
|
485
|
+
> 维度上限(插件侧实测 pgvector 0.8.7):`vector` ≤ **16000** 维;hnsw / ivfflat 索引
|
|
486
|
+
> ≤ **2000** 维;`halfvec` 表达式索引 ≤ **4000** 维。因此 **1024 维表可建 HNSW,
|
|
487
|
+
> 2560 / 4096 维表建不了**。
|
|
482
488
|
|
|
483
489
|
**主键与 id(三库统一)**:向量表主键均为 `BIGINT`(SQLite 用等价的 64 位 `INTEGER PRIMARY KEY`),
|
|
484
490
|
**不使用数据库自增**;`ids=None` 时由 Kit 用 `CommonKit.generate_snowflake_id()` 生成雪花 ID
|
|
@@ -513,7 +519,11 @@ MySqlKit.insert_vectors("images", [[0.1] * 384, [0.2] * 384])
|
|
|
513
519
|
PgKit.VECTOR_CAPABILITIES
|
|
514
520
|
# {'store': True, 'search': True, 'exact': True,
|
|
515
521
|
# 'distances': ('COSINE', 'L2', 'L1', 'DOT', 'SQUARED_L2'),
|
|
516
|
-
# 'distance_kind': 'true', 'fast_distance_kind': 'true'
|
|
522
|
+
# 'distance_kind': 'true', 'fast_distance_kind': 'true',
|
|
523
|
+
# 'dimension_kind': 'exact'} # MySQL='max' / SQLite=None
|
|
524
|
+
|
|
525
|
+
# 查询库列声明的维度(守卫「配置 vs 库列」用;查不到返回 None)
|
|
526
|
+
PgKit.vector_column_dimension("images", "embedding") # 384
|
|
517
527
|
|
|
518
528
|
# 自建 DDL 的表:用 id_col / column 接入
|
|
519
529
|
PgKit.execute("CREATE TABLE my_vec (vid BIGINT PRIMARY KEY, vec vector(384))")
|
|
@@ -812,12 +822,27 @@ await service.close()
|
|
|
812
822
|
| 类 | 方法 |
|
|
813
823
|
|----|------|
|
|
814
824
|
| `ChatService` | `ask(messages, *, temperature=None, max_tokens=None, deadline=None) -> str`、`complete(prompt, *, ...) -> str`、`stream(messages, *, ...) -> AsyncIterator[str]` |
|
|
815
|
-
| `EmbeddingService` | `embed_text(text) -> list[float]`、`embed_texts(texts, *, batch_size=None, concurrency=4) -> list[list[float]]`、`dimension`
|
|
825
|
+
| `EmbeddingService` | `embed_text(text) -> list[float]`、`embed_texts(texts, *, batch_size=None, concurrency=4) -> list[list[float]]`、`dimension` 属性(**目标维度**,见下) |
|
|
816
826
|
| `RerankService` | `rerank(query, documents, *, top_n=None) -> list[RerankHit]`(已按 score 降序) |
|
|
817
827
|
| `ImageService` | `generate(prompt, *, size=None, n=1, return_base64=False, ratio=None) -> str` |
|
|
818
828
|
| `ModelBuilder` | `build(model_type, model=None, *, api_key=None)` 及各类型专用 `build_chat / build_embedding / build_rerank / build_image` |
|
|
819
829
|
| `BaseAIService` | `profile` 属性、`await close()`、支持 `async with` |
|
|
820
830
|
|
|
831
|
+
**embedding 维度契约**:`ai_models.json` 里 embedding 档案的 `dimension` 是**目标向量维度**
|
|
832
|
+
(不是模型原生维度),`EmbeddingService` 保证输出恒等于它:
|
|
833
|
+
|
|
834
|
+
| 模型返回维 vs `dimension` | 行为 |
|
|
835
|
+
|---|---|
|
|
836
|
+
| 相等 | 原样返回(不归一化,逐字节不变) |
|
|
837
|
+
| 更大 | **截断到该维度并做 L2 归一化**(依赖模型的 MRL 支持;非 MRL 模型截断后质量会退化) |
|
|
838
|
+
| 更小 | 抛 `AIResponseError`(降维只能变小,不能放大) |
|
|
839
|
+
|
|
840
|
+
用途:让「建表维度 = 配置维度 = 输出维度」共用一个数值——
|
|
841
|
+
`create_vector_table(t, dims)` 与 `vector_column_dimension(t, col)` 都取该配置值,
|
|
842
|
+
换用原生维度更大的模型时无需重构向量列(换模型后需全量重新入库)。
|
|
843
|
+
|
|
844
|
+
> ⚠ 启用候选链时,**链内各档案的 `dimension` 必须一致**:故障切换后输出维度跟随被选中档案的配置。
|
|
845
|
+
|
|
821
846
|
### 3. 异常
|
|
822
847
|
|
|
823
848
|
```python
|
|
@@ -1051,12 +1076,18 @@ Md2DocKit.convert_md_to_pdf("report.md", mainfont="KaiTi")
|
|
|
1051
1076
|
|
|
1052
1077
|
| 方法 | 说明 |
|
|
1053
1078
|
|------|------|
|
|
1054
|
-
| `convert_md_to_docx(src_path, dest_path=None, *, generate_toc=False, mainfont="宋体", reference_doc=None) -> str` | Markdown → Word
|
|
1055
|
-
| `convert_md_to_html(src_path, dest_path=None, *, title=None) -> str` | Markdown → 独立 HTML5
|
|
1056
|
-
| `convert_md_to_pdf(src_path, dest_path=None, *, mainfont="SimSun") -> str` | Markdown → PDF
|
|
1079
|
+
| `convert_md_to_docx(src_path, dest_path=None, *, generate_toc=False, mainfont="宋体", reference_doc=None) -> str` | Markdown → Word(可插目录、统一字体、图片内嵌) |
|
|
1080
|
+
| `convert_md_to_html(src_path, dest_path=None, *, title=None) -> str` | Markdown → 独立 HTML5 文件(图片输出 `<figure><img>`) |
|
|
1081
|
+
| `convert_md_to_pdf(src_path, dest_path=None, *, mainfont="SimSun") -> str` | Markdown → PDF(实验性,内部使用;图片等比限宽居中) |
|
|
1057
1082
|
|
|
1058
1083
|
> `dest_path=None` 时输出到源文件同目录;同一源文件不支持并发转换。
|
|
1059
1084
|
|
|
1085
|
+
**图片**:`` 以 Markdown 源文件所在目录为基准解析,宽度等比限幅
|
|
1086
|
+
(96dpi 还原自然尺寸、不放大),超高先分页/按版心收紧;缺图、越界路径、非图片
|
|
1087
|
+
格式一律回落 `alt` 文本,不抛异常。远程地址在 PDF / Word 中不下载(回落 alt),
|
|
1088
|
+
在 HTML 中直出 `<img src="https://…">` 交给浏览器。HTML 输出保留相对路径,需与
|
|
1089
|
+
`images/` 目录保持同层级才能在浏览器中显示。
|
|
1090
|
+
|
|
1060
1091
|
---
|
|
1061
1092
|
|
|
1062
1093
|
## kit.docs.mineru_kit —— MinerU 文档解析
|
|
@@ -22,7 +22,7 @@ SDPY 统一工具集(Kit 体系),供 Python 项目开箱即用:
|
|
|
22
22
|
| `kit.docs.anydoc_kit` | `AnyDocKit` | docx / pdf / xlsx 等 → Markdown | `anydoc` |
|
|
23
23
|
| `kit.docs.docx_kit` | `DocxKit` | Word 模板动态填充(章节 / 列表 / 表格) | `docx` |
|
|
24
24
|
| `kit.docs.rc_docparser_kit` | `RcDocParserKit` | 离线 docx → Markdown + Images(无模型) | `docx-parse` |
|
|
25
|
-
| `kit.docs.md2doc_kit` | `Md2DocKit` | Markdown → Word / PDF / HTML | `md2doc` |
|
|
25
|
+
| `kit.docs.md2doc_kit` | `Md2DocKit` | Markdown → Word / PDF / HTML(含图片) | `md2doc` |
|
|
26
26
|
| `kit.docs.mineru_kit` | `MinerUKit` | MinerU 文档解析(精准 + Agent 轻量) | `mineru` |
|
|
27
27
|
| `kit.docs.marker_kit` | `MarkerKit` | 本地解析(PDF / 图片 → Markdown + Images) | `marker` |
|
|
28
28
|
| `kit.laya_kit` | `LayaKit` | 类型化决策:choice / score / noul,返回判断结果 + 置信度 | `laya` |
|
|
@@ -49,7 +49,7 @@ uv add sdpy-kit
|
|
|
49
49
|
| `sqliteai` | SQLiteKit 向量方法(numpy / sqliteai-vector,SQLite 向量扩展) | `pip install "sdpy-kit[sqliteai]"` |
|
|
50
50
|
| `redis` | RedisKit | `pip install "sdpy-kit[redis]"` |
|
|
51
51
|
| `ai` | AI 服务池(httpx + python-dotenv) | `pip install "sdpy-kit[ai]"` |
|
|
52
|
-
| `md2doc` | Md2DocKit:Markdown → Word / PDF / HTML(pimd / fpdf2
|
|
52
|
+
| `md2doc` | Md2DocKit:Markdown → Word / PDF / HTML(pimd / fpdf2,含图片) | `pip install "sdpy-kit[md2doc]"` |
|
|
53
53
|
| `anydoc` | AnyDocKit 文档转 Markdown(firecrawl-anydoc + pikepdf) | `pip install "sdpy-kit[anydoc]"` |
|
|
54
54
|
| `docx` | DocxKit 模板动态填充(docxtpl) | `pip install "sdpy-kit[docx]"` |
|
|
55
55
|
| `docx-parse` | RcDocParserKit 离线 docx 解析(rc-docparser → Markdown + images/) | `pip install "sdpy-kit[docx-parse]"` |
|
|
@@ -375,6 +375,7 @@ with SQLiteKit.transaction() as session: # 多操作原子提
|
|
|
375
375
|
| `insert_vectors(table_name, vectors, ids=None, extra_data=None, id_col="id", column="embedding") -> int` | 批量插入向量(**自成一事务并提交**);`ids=None` 时由 Kit 生成雪花 ID;接受 `list[list[float]]` 或二维 numpy 数组 |
|
|
376
376
|
| `build_insert_vectors(table_name, vectors, ids=None, extra_data=None, id_col="id", column="embedding") -> list[tuple[str, list[dict]]]` | **只拼装、不执行、不管事务**:返回按列集分组的 `(SQL, 参数列表)`,供调用方在**自有事务**内写向量行(见「自有事务内写向量」) |
|
|
377
377
|
| `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`** |
|
|
378
|
+
| `vector_column_dimension(table_name, column="embedding") -> int \| None` | 查列**声明的维度**:PG 为精确维度(`vector(1024)`→1024);MySQL 为**最大上限**(未指定维度落 `2048`);SQLite 恒 `None`(无元数据)。列不存在 / 非向量列 / 无维度声明 → `None`,不抛异常。口径差异由 `VECTOR_CAPABILITIES["dimension_kind"]` 声明(`"exact"` / `"max"` / `None`) |
|
|
378
379
|
| `vector_version() -> str` | 向量后端版本;不可用时返回 `"unavailable"`(MySQL 侧为服务端版本号) |
|
|
379
380
|
|
|
380
381
|
**`id_col` / `column`(逃生口)**:让**自建 DDL 的向量表**也能读写/检索。需要复合主键、额外约束、
|
|
@@ -397,12 +398,17 @@ with SQLiteKit.transaction() as session: # 多操作原子提
|
|
|
397
398
|
| 默认 `distance_type` | `"L2"` | `"L2"`(三库统一,不按后端覆写) | `"L2"`(形参保留,调用即抛错) |
|
|
398
399
|
| 非法取值 | 抛 `ValueError` | 抛 `ValueError` | `search_vectors` 抛 `VectorNotSupportedError` |
|
|
399
400
|
| `distance` 口径 | `exact=True` 真值 L2 / `exact=False` 量化(×255) | 真实距离 | — |
|
|
400
|
-
| 维度约束 | `CHECK`(仅新表;旧表靠方法内校验) | 强制(不匹配即报错;建表时回读比对) |
|
|
401
|
+
| 维度约束 | `CHECK`(仅新表;旧表靠方法内校验) | 强制(不匹配即报错;建表时回读比对) | **上限**(`VECTOR(N)` 的 N 是最大维度,写入须 ≤ N,超限报 `1406 Data too long`) |
|
|
402
|
+
| 维度查询 | 恒 `None`(无元数据) | 精确维度(`dimension_kind="exact"`) | 最大上限(`dimension_kind="max"`) |
|
|
401
403
|
| `filter_sql` 与 `top_k` | **先过滤再取 top_k**(与 PG 一致) | **先过滤再取 top_k** | 不适用 |
|
|
402
404
|
| 索引 | 无(精确路径为全表扫描) | kit 不建索引;**自建后须 `exact=False` 才会被使用** | 无法建(B-Tree 拒绝向量列) |
|
|
403
405
|
|
|
404
406
|
> MySQL `DISTANCE()` / `VECTOR_DISTANCE()` 仅 HeatWave on OCI 与 MySQL AI 提供
|
|
405
407
|
> (Community 实测报 `1305 FUNCTION ... does not exist`);故 `MySqlKit` 只覆盖存储侧。
|
|
408
|
+
>
|
|
409
|
+
> 维度上限(插件侧实测 pgvector 0.8.7):`vector` ≤ **16000** 维;hnsw / ivfflat 索引
|
|
410
|
+
> ≤ **2000** 维;`halfvec` 表达式索引 ≤ **4000** 维。因此 **1024 维表可建 HNSW,
|
|
411
|
+
> 2560 / 4096 维表建不了**。
|
|
406
412
|
|
|
407
413
|
**主键与 id(三库统一)**:向量表主键均为 `BIGINT`(SQLite 用等价的 64 位 `INTEGER PRIMARY KEY`),
|
|
408
414
|
**不使用数据库自增**;`ids=None` 时由 Kit 用 `CommonKit.generate_snowflake_id()` 生成雪花 ID
|
|
@@ -437,7 +443,11 @@ MySqlKit.insert_vectors("images", [[0.1] * 384, [0.2] * 384])
|
|
|
437
443
|
PgKit.VECTOR_CAPABILITIES
|
|
438
444
|
# {'store': True, 'search': True, 'exact': True,
|
|
439
445
|
# 'distances': ('COSINE', 'L2', 'L1', 'DOT', 'SQUARED_L2'),
|
|
440
|
-
# 'distance_kind': 'true', 'fast_distance_kind': 'true'
|
|
446
|
+
# 'distance_kind': 'true', 'fast_distance_kind': 'true',
|
|
447
|
+
# 'dimension_kind': 'exact'} # MySQL='max' / SQLite=None
|
|
448
|
+
|
|
449
|
+
# 查询库列声明的维度(守卫「配置 vs 库列」用;查不到返回 None)
|
|
450
|
+
PgKit.vector_column_dimension("images", "embedding") # 384
|
|
441
451
|
|
|
442
452
|
# 自建 DDL 的表:用 id_col / column 接入
|
|
443
453
|
PgKit.execute("CREATE TABLE my_vec (vid BIGINT PRIMARY KEY, vec vector(384))")
|
|
@@ -736,12 +746,27 @@ await service.close()
|
|
|
736
746
|
| 类 | 方法 |
|
|
737
747
|
|----|------|
|
|
738
748
|
| `ChatService` | `ask(messages, *, temperature=None, max_tokens=None, deadline=None) -> str`、`complete(prompt, *, ...) -> str`、`stream(messages, *, ...) -> AsyncIterator[str]` |
|
|
739
|
-
| `EmbeddingService` | `embed_text(text) -> list[float]`、`embed_texts(texts, *, batch_size=None, concurrency=4) -> list[list[float]]`、`dimension`
|
|
749
|
+
| `EmbeddingService` | `embed_text(text) -> list[float]`、`embed_texts(texts, *, batch_size=None, concurrency=4) -> list[list[float]]`、`dimension` 属性(**目标维度**,见下) |
|
|
740
750
|
| `RerankService` | `rerank(query, documents, *, top_n=None) -> list[RerankHit]`(已按 score 降序) |
|
|
741
751
|
| `ImageService` | `generate(prompt, *, size=None, n=1, return_base64=False, ratio=None) -> str` |
|
|
742
752
|
| `ModelBuilder` | `build(model_type, model=None, *, api_key=None)` 及各类型专用 `build_chat / build_embedding / build_rerank / build_image` |
|
|
743
753
|
| `BaseAIService` | `profile` 属性、`await close()`、支持 `async with` |
|
|
744
754
|
|
|
755
|
+
**embedding 维度契约**:`ai_models.json` 里 embedding 档案的 `dimension` 是**目标向量维度**
|
|
756
|
+
(不是模型原生维度),`EmbeddingService` 保证输出恒等于它:
|
|
757
|
+
|
|
758
|
+
| 模型返回维 vs `dimension` | 行为 |
|
|
759
|
+
|---|---|
|
|
760
|
+
| 相等 | 原样返回(不归一化,逐字节不变) |
|
|
761
|
+
| 更大 | **截断到该维度并做 L2 归一化**(依赖模型的 MRL 支持;非 MRL 模型截断后质量会退化) |
|
|
762
|
+
| 更小 | 抛 `AIResponseError`(降维只能变小,不能放大) |
|
|
763
|
+
|
|
764
|
+
用途:让「建表维度 = 配置维度 = 输出维度」共用一个数值——
|
|
765
|
+
`create_vector_table(t, dims)` 与 `vector_column_dimension(t, col)` 都取该配置值,
|
|
766
|
+
换用原生维度更大的模型时无需重构向量列(换模型后需全量重新入库)。
|
|
767
|
+
|
|
768
|
+
> ⚠ 启用候选链时,**链内各档案的 `dimension` 必须一致**:故障切换后输出维度跟随被选中档案的配置。
|
|
769
|
+
|
|
745
770
|
### 3. 异常
|
|
746
771
|
|
|
747
772
|
```python
|
|
@@ -975,12 +1000,18 @@ Md2DocKit.convert_md_to_pdf("report.md", mainfont="KaiTi")
|
|
|
975
1000
|
|
|
976
1001
|
| 方法 | 说明 |
|
|
977
1002
|
|------|------|
|
|
978
|
-
| `convert_md_to_docx(src_path, dest_path=None, *, generate_toc=False, mainfont="宋体", reference_doc=None) -> str` | Markdown → Word
|
|
979
|
-
| `convert_md_to_html(src_path, dest_path=None, *, title=None) -> str` | Markdown → 独立 HTML5
|
|
980
|
-
| `convert_md_to_pdf(src_path, dest_path=None, *, mainfont="SimSun") -> str` | Markdown → PDF
|
|
1003
|
+
| `convert_md_to_docx(src_path, dest_path=None, *, generate_toc=False, mainfont="宋体", reference_doc=None) -> str` | Markdown → Word(可插目录、统一字体、图片内嵌) |
|
|
1004
|
+
| `convert_md_to_html(src_path, dest_path=None, *, title=None) -> str` | Markdown → 独立 HTML5 文件(图片输出 `<figure><img>`) |
|
|
1005
|
+
| `convert_md_to_pdf(src_path, dest_path=None, *, mainfont="SimSun") -> str` | Markdown → PDF(实验性,内部使用;图片等比限宽居中) |
|
|
981
1006
|
|
|
982
1007
|
> `dest_path=None` 时输出到源文件同目录;同一源文件不支持并发转换。
|
|
983
1008
|
|
|
1009
|
+
**图片**:`` 以 Markdown 源文件所在目录为基准解析,宽度等比限幅
|
|
1010
|
+
(96dpi 还原自然尺寸、不放大),超高先分页/按版心收紧;缺图、越界路径、非图片
|
|
1011
|
+
格式一律回落 `alt` 文本,不抛异常。远程地址在 PDF / Word 中不下载(回落 alt),
|
|
1012
|
+
在 HTML 中直出 `<img src="https://…">` 交给浏览器。HTML 输出保留相对路径,需与
|
|
1013
|
+
`images/` 目录保持同层级才能在浏览器中显示。
|
|
1014
|
+
|
|
984
1015
|
---
|
|
985
1016
|
|
|
986
1017
|
## kit.docs.mineru_kit —— MinerU 文档解析
|
|
@@ -19,6 +19,7 @@ Author: Clark Chang
|
|
|
19
19
|
|
|
20
20
|
import asyncio
|
|
21
21
|
import itertools
|
|
22
|
+
import math
|
|
22
23
|
import time
|
|
23
24
|
from dataclasses import dataclass
|
|
24
25
|
from typing import cast, override
|
|
@@ -48,12 +49,28 @@ class EmbeddingService(BaseAIService[EmbeddingRequest, list[list[float]]]):
|
|
|
48
49
|
|
|
49
50
|
公网 API 有限流限速前提,通过分批 + 可选并发(信号量)控制压力。
|
|
50
51
|
信号量在运行中的事件循环内惰性创建,避免构造期绑定循环的隐患。
|
|
52
|
+
|
|
53
|
+
**维度契约**:``profile.dimension`` 是**目标向量维度**(不是「模型原生维度」),
|
|
54
|
+
本服务保证输出恒等于它:
|
|
55
|
+
|
|
56
|
+
- 模型返回维 == ``dimension`` → 原样返回(零计算,逐字节不变);
|
|
57
|
+
- 模型返回维 > ``dimension`` → **截断到该维度并做 L2 归一化**(依赖模型的 MRL
|
|
58
|
+
支持;非 MRL 模型截断后质量会退化);
|
|
59
|
+
- 模型返回维 < ``dimension`` → 抛 `AIResponseError`(降维只能变小,不能放大)。
|
|
60
|
+
|
|
61
|
+
这样「建表维度 = 配置维度 = 输出维度」可由同一个配置值统一,
|
|
62
|
+
换模型(原生维度更大)时无需重构向量列。
|
|
63
|
+
|
|
64
|
+
⚠ 候选链内各档案的 ``dimension`` 必须一致:故障切换后输出维度跟随
|
|
65
|
+
被选中档案的配置,不一致会导致同一业务表写入不同维度的向量。
|
|
51
66
|
"""
|
|
52
67
|
|
|
53
68
|
def __init__(self, profile: ModelProfile) -> None:
|
|
54
69
|
super().__init__(profile)
|
|
55
70
|
if profile.dimension is None:
|
|
56
71
|
raise ValueError(f"模型 {profile.model} 未配置 dimension,无法用于向量化")
|
|
72
|
+
# 仅用于「截断只提示一次」,不参与任何行为分支
|
|
73
|
+
self._truncate_logged = False
|
|
57
74
|
|
|
58
75
|
@property
|
|
59
76
|
def dimension(self) -> int:
|
|
@@ -62,6 +79,45 @@ class EmbeddingService(BaseAIService[EmbeddingRequest, list[list[float]]]):
|
|
|
62
79
|
raise AIResponseError("dimension 在构造期已校验非空,不应缺失")
|
|
63
80
|
return dim
|
|
64
81
|
|
|
82
|
+
def _fit_dimension(self, vector: list[float]) -> list[float]:
|
|
83
|
+
"""把模型输出对齐到配置的目标维度
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
vector: 模型原始输出的向量
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
目标维度的向量:等长时原样返回;超长时截断并 L2 归一化
|
|
90
|
+
|
|
91
|
+
Raises:
|
|
92
|
+
AIResponseError: 输出维度**不足**配置维度(无法放大),
|
|
93
|
+
或截断后前 N 维全为 0(无法归一化)
|
|
94
|
+
"""
|
|
95
|
+
target = self.dimension
|
|
96
|
+
if len(vector) == target:
|
|
97
|
+
return vector
|
|
98
|
+
if len(vector) < target:
|
|
99
|
+
raise AIResponseError(
|
|
100
|
+
f"向量维度不足:模型 {self._profile.model} 返回 {len(vector)} 维,"
|
|
101
|
+
f"配置 dimension={target}。降维只能变小不能放大,"
|
|
102
|
+
f"请改用原生维度 >= {target} 的模型,"
|
|
103
|
+
f"或调低 dimension 并重建向量表(表内既有向量需重新入库)。"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
head = vector[:target]
|
|
107
|
+
norm = math.sqrt(sum(value * value for value in head))
|
|
108
|
+
if norm == 0.0:
|
|
109
|
+
raise AIResponseError(
|
|
110
|
+
f"模型 {self._profile.model} 返回向量的前 {target} 维全为 0,"
|
|
111
|
+
f"无法按配置 dimension={target} 归一化;请检查模型输出是否正常。"
|
|
112
|
+
)
|
|
113
|
+
if not self._truncate_logged:
|
|
114
|
+
logger.info(
|
|
115
|
+
f"向量已按配置截断:{len(vector)} → {target} 维(含 L2 归一化)。"
|
|
116
|
+
f"截断依赖模型的 MRL 支持,非 MRL 模型质量会退化"
|
|
117
|
+
)
|
|
118
|
+
self._truncate_logged = True
|
|
119
|
+
return [value / norm for value in head]
|
|
120
|
+
|
|
65
121
|
@override
|
|
66
122
|
def _build_payload(self, request: EmbeddingRequest) -> JSONObject:
|
|
67
123
|
return cast(
|
|
@@ -92,7 +148,7 @@ class EmbeddingService(BaseAIService[EmbeddingRequest, list[list[float]]]):
|
|
|
92
148
|
f"向量化响应 embedding 元素非法: {str(item)[:200]}"
|
|
93
149
|
)
|
|
94
150
|
floats.append(float(value))
|
|
95
|
-
vectors.append(floats)
|
|
151
|
+
vectors.append(self._fit_dimension(floats))
|
|
96
152
|
return vectors
|
|
97
153
|
|
|
98
154
|
async def embed_text(self, text: str) -> list[float]:
|
|
@@ -71,7 +71,9 @@ class ModelProfile:
|
|
|
71
71
|
timeout: 请求超时秒数
|
|
72
72
|
max_tokens: 单次请求输出上限(chat 用;None 表示由 provider 默认值决定)
|
|
73
73
|
retries: 5xx 重试次数(超时不重试,候选链切换才是有效重试)
|
|
74
|
-
dimension:
|
|
74
|
+
dimension: **目标**向量维度(仅 embedding 有效)。`EmbeddingService`
|
|
75
|
+
保证输出恒等于该值:模型原生维度更大时截断(含 L2 归一化),
|
|
76
|
+
更小时报错;建表与维度守卫均应以它为唯一来源
|
|
75
77
|
batch_size: 单次请求最大文本数(仅 embedding 有效)
|
|
76
78
|
"""
|
|
77
79
|
|
|
@@ -784,9 +784,11 @@ class ReloadResult:
|
|
|
784
784
|
Attributes:
|
|
785
785
|
types: 实际重建的模型类型(全量重载为 4 类型的排序列表)
|
|
786
786
|
embedding_model_changed: embedding 默认档案的 ``model`` 或
|
|
787
|
-
``dimension``
|
|
788
|
-
|
|
789
|
-
|
|
787
|
+
``dimension`` 是否变化。``dimension`` 是**目标向量维度**
|
|
788
|
+
(`EmbeddingService` 保证输出等于它),故它变化会直接改变
|
|
789
|
+
输出维度;已建向量库的维度与存量向量不会随重载迁移,
|
|
790
|
+
变化时调用方应评估是否需要重新入库(kit 只上报不拦截——
|
|
791
|
+
新建库场景下更换 embedding 是合法操作)
|
|
790
792
|
previous_embedding_model: 重载前 embedding 默认档案的 model
|
|
791
793
|
current_embedding_model: 重载后 embedding 默认档案的 model
|
|
792
794
|
"""
|
|
@@ -59,6 +59,8 @@ from __future__ import annotations
|
|
|
59
59
|
- ``VECTOR_CAPABILITIES`` 已声明上述能力,调用方可 feature-detect 而无需 try/except。
|
|
60
60
|
"""
|
|
61
61
|
|
|
62
|
+
import re
|
|
63
|
+
|
|
62
64
|
from sqlalchemy import text
|
|
63
65
|
|
|
64
66
|
from kit.config.log_config import get_default_logger
|
|
@@ -96,6 +98,7 @@ class MySqlKit(BaseDbKit):
|
|
|
96
98
|
"distances": (),
|
|
97
99
|
"distance_kind": None,
|
|
98
100
|
"fast_distance_kind": None,
|
|
101
|
+
"dimension_kind": "max", # VECTOR(N) 的 N 是「最大维度上限」,非必须相等
|
|
99
102
|
}
|
|
100
103
|
|
|
101
104
|
@classmethod
|
|
@@ -124,6 +127,45 @@ class MySqlKit(BaseDbKit):
|
|
|
124
127
|
|
|
125
128
|
# ==================== 向量操作(MySQL 9.x 原生 VECTOR 类型) ====================
|
|
126
129
|
|
|
130
|
+
@classmethod
|
|
131
|
+
def vector_column_dimension(
|
|
132
|
+
cls, table_name: str, column: str = "embedding"
|
|
133
|
+
) -> int | None:
|
|
134
|
+
"""查询向量列的**声明维度上限**(MySQL 9.x 原生 ``VECTOR``)
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
table_name: 表名(当前连接的库内,即 ``DATABASE()``)
|
|
138
|
+
column: 向量列名(默认 ``embedding``)
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
列声明的维度上限;列不存在或不是 ``VECTOR`` 类型时返回 ``None``
|
|
142
|
+
|
|
143
|
+
Note:
|
|
144
|
+
- MySQL 的 ``VECTOR(N)`` 是**上限语义**:写入维度 ≤ N 即可(实测 9.7.2:
|
|
145
|
+
``VECTOR(1024)`` 写入 1024 维成功,写入 1025 维报 ``1406 Data too long``)。
|
|
146
|
+
故 ``VECTOR_CAPABILITIES["dimension_kind"] == "max"``,
|
|
147
|
+
**不能**用「配置维度 != 列值」判定不一致。
|
|
148
|
+
- 建表时未指定维度的 ``VECTOR`` 由服务端落为 ``vector(2048)``(实测 9.7.2),
|
|
149
|
+
因此本后端不存在「无维度」列,``None`` 只表示「列不是向量列」。
|
|
150
|
+
- 维度元数据只在 ``information_schema.COLUMNS.COLUMN_TYPE`` 里(形如
|
|
151
|
+
``vector(1024)``),需按文本解析。
|
|
152
|
+
"""
|
|
153
|
+
rows = cls.query(
|
|
154
|
+
"SELECT COLUMN_TYPE AS column_type "
|
|
155
|
+
"FROM information_schema.COLUMNS "
|
|
156
|
+
"WHERE TABLE_SCHEMA = DATABASE() "
|
|
157
|
+
"AND TABLE_NAME = :table_name "
|
|
158
|
+
"AND COLUMN_NAME = :column",
|
|
159
|
+
{"table_name": table_name, "column": column},
|
|
160
|
+
)
|
|
161
|
+
if not rows:
|
|
162
|
+
return None
|
|
163
|
+
value = rows[0]["column_type"]
|
|
164
|
+
if not isinstance(value, str):
|
|
165
|
+
return None
|
|
166
|
+
matched = re.fullmatch(r"vector\((\d+)\)", value.strip(), re.IGNORECASE)
|
|
167
|
+
return int(matched.group(1)) if matched else None
|
|
168
|
+
|
|
127
169
|
@classmethod
|
|
128
170
|
def _ensure_vector_supported(cls) -> None:
|
|
129
171
|
"""确保服务端支持 VECTOR 类型(需 MySQL 9.x)
|
|
@@ -27,7 +27,7 @@ from __future__ import annotations
|
|
|
27
27
|
|
|
28
28
|
本类继承 BaseDbKit,复用全部共享实现(连接管理 / ORM / 原生 SQL / 原生表操作 /
|
|
29
29
|
事务 / 表操作),并覆写 PostgreSQL 专属的 get_table_info,另提供基于 pgvector 的
|
|
30
|
-
|
|
30
|
+
5 个向量方法(签名与 SQLiteKit / MySqlKit 完全一致)。
|
|
31
31
|
|
|
32
32
|
使用方式::
|
|
33
33
|
|
|
@@ -108,6 +108,7 @@ class PgKit(BaseDbKit):
|
|
|
108
108
|
"distances": SUPPORTED_DISTANCE_TYPES,
|
|
109
109
|
"distance_kind": "true", # exact=True 与 False 下 distance 都是真值
|
|
110
110
|
"fast_distance_kind": "true", # 近似仅影响候选集(索引),数值仍是真值
|
|
111
|
+
"dimension_kind": "exact", # vector_column_dimension 返回「必须相等」的精确维度
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
@classmethod
|
|
@@ -149,6 +150,48 @@ class PgKit(BaseDbKit):
|
|
|
149
150
|
|
|
150
151
|
# ==================== 向量操作(pgvector) ====================
|
|
151
152
|
|
|
153
|
+
@classmethod
|
|
154
|
+
def vector_column_dimension(
|
|
155
|
+
cls, table_name: str, column: str = "embedding"
|
|
156
|
+
) -> int | None:
|
|
157
|
+
"""查询向量列的声明维度(pgvector 的 ``vector`` 类型)
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
table_name: 表名(**非限定名**,按 ``search_path`` 解析,与 `get_table_info` 同源)
|
|
161
|
+
column: 向量列名(默认 ``embedding``)
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
列声明的维度;下列情况返回 ``None``:列不存在、不是 ``vector`` 类型、
|
|
165
|
+
建为**无维度**的 ``vector``
|
|
166
|
+
|
|
167
|
+
Note:
|
|
168
|
+
- pgvector 把维度存在 ``pg_attribute.atttypmod``,值**直接等于维度**
|
|
169
|
+
(实测 pgvector 0.8.7:``vector(1024)`` → 1024,无 varchar 那样的 +4 偏移)。
|
|
170
|
+
- 必须联 ``pg_type`` 校验类型名:``varchar(20)`` / ``numeric`` 等同样有
|
|
171
|
+
``atttypmod``,不判类型会误报出维度。
|
|
172
|
+
- 本后端维度是**硬约束**(写入维度必须相等),故
|
|
173
|
+
``VECTOR_CAPABILITIES["dimension_kind"] == "exact"``。
|
|
174
|
+
- 用 ``to_regclass()`` 定位(表不存在返回 NULL 而非报错);
|
|
175
|
+
**不要**改用 ``CAST(:表名 AS regclass)``。
|
|
176
|
+
"""
|
|
177
|
+
rows = cls.query(
|
|
178
|
+
"SELECT a.atttypmod AS typmod "
|
|
179
|
+
"FROM pg_attribute a "
|
|
180
|
+
"JOIN pg_type t ON t.oid = a.atttypid "
|
|
181
|
+
"WHERE a.attrelid = to_regclass(:table_name) "
|
|
182
|
+
"AND a.attname = :column "
|
|
183
|
+
"AND a.attnum > 0 "
|
|
184
|
+
"AND NOT a.attisdropped "
|
|
185
|
+
"AND t.typname = 'vector'",
|
|
186
|
+
{"table_name": table_name, "column": column},
|
|
187
|
+
)
|
|
188
|
+
if not rows:
|
|
189
|
+
return None
|
|
190
|
+
typmod = rows[0]["typmod"]
|
|
191
|
+
if not isinstance(typmod, int) or typmod <= 0:
|
|
192
|
+
return None
|
|
193
|
+
return typmod
|
|
194
|
+
|
|
152
195
|
@classmethod
|
|
153
196
|
def _ensure_vector_extension(cls) -> None:
|
|
154
197
|
"""确保 pgvector 扩展可用(缺失则尝试创建)
|
|
@@ -26,7 +26,7 @@ from __future__ import annotations
|
|
|
26
26
|
业务代码无需管理 session/transaction,直接调用即可。
|
|
27
27
|
|
|
28
28
|
本类继承 BaseDbKit,复用全部共享实现(连接管理 / ORM / 原生 SQL / 原生表操作 /
|
|
29
|
-
事务 / 表操作),并额外提供基于 sqlite-vector 扩展的
|
|
29
|
+
事务 / 表操作),并额外提供基于 sqlite-vector 扩展的 5 个向量方法,以及 2 个
|
|
30
30
|
**编码原语**(`encode_vector` / `decode_vector`,仅本后端具备)。
|
|
31
31
|
|
|
32
32
|
使用方式::
|
|
@@ -98,6 +98,7 @@ class SQLiteKit(BaseDbKit):
|
|
|
98
98
|
"distances": SUPPORTED_DISTANCE_TYPES, # 支持的距离类型
|
|
99
99
|
"distance_kind": "true", # exact=True 时的 distance 口径
|
|
100
100
|
"fast_distance_kind": "quantized_l2_x255", # exact=False 时的 distance 口径
|
|
101
|
+
"dimension_kind": None, # 无维度元数据,vector_column_dimension 恒返回 None
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
@classmethod
|
|
@@ -176,6 +177,33 @@ class SQLiteKit(BaseDbKit):
|
|
|
176
177
|
|
|
177
178
|
return [float(x) for x in np.frombuffer(blob, dtype=_VECTOR_DTYPE)]
|
|
178
179
|
|
|
180
|
+
@classmethod
|
|
181
|
+
def vector_column_dimension(
|
|
182
|
+
cls, table_name: str, column: str = "embedding"
|
|
183
|
+
) -> int | None:
|
|
184
|
+
"""查询向量列的声明维度,**本后端恒返回 None**
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
table_name: 表名(未使用,仅为保持三库签名一致)
|
|
188
|
+
column: 向量列名(未使用,同上)
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
恒为 ``None``
|
|
192
|
+
|
|
193
|
+
Note:
|
|
194
|
+
SQLite 没有向量类型,也没有维度元数据:`create_vector_table` 把维度落实为
|
|
195
|
+
``CHECK (length(column) = dims * 4)``,写入时由数据库强制,但**查不出来**。
|
|
196
|
+
|
|
197
|
+
不解析 ``sqlite_master.sql`` 里的 CHECK 文本:列名转义、既有表无该约束、
|
|
198
|
+
文本格式都可能变形,反解不可靠。
|
|
199
|
+
|
|
200
|
+
守卫建议:维度约束已由 ``CHECK`` 承担(写入维度不符即报错),无需前置探测;
|
|
201
|
+
若确需提前校验,可读一行数据的 ``length(column) / 4`` 推断(空表无法推断)。
|
|
202
|
+
|
|
203
|
+
``VECTOR_CAPABILITIES["dimension_kind"] is None`` 表明本后端不支持该查询。
|
|
204
|
+
"""
|
|
205
|
+
return None
|
|
206
|
+
|
|
179
207
|
@classmethod
|
|
180
208
|
def create_vector_table(
|
|
181
209
|
cls,
|