fix(knowledge): 保证笔记创建与索引更新一致性
This commit is contained in:
@@ -9,6 +9,7 @@ from __future__ import annotations
|
||||
|
||||
import json
|
||||
import sqlite3
|
||||
from contextlib import nullcontext
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
|
||||
@@ -235,14 +236,19 @@ def get_block_hits(block_ids: list[str]) -> list[BlockHit]:
|
||||
conn.close()
|
||||
|
||||
|
||||
def set_index_meta(kv: dict[str, str]) -> None:
|
||||
conn = connect()
|
||||
def set_index_meta(
|
||||
kv: dict[str, str], *, conn: sqlite3.Connection | None = None
|
||||
) -> None:
|
||||
"""写入索引元信息;传入连接时加入调用方现有事务。"""
|
||||
owns = conn is None
|
||||
conn = conn or connect()
|
||||
try:
|
||||
with transaction(conn):
|
||||
with transaction(conn) if owns else nullcontext():
|
||||
for key, value in kv.items():
|
||||
conn.execute("INSERT OR REPLACE INTO index_meta (key, value) VALUES (?, ?)", (key, value))
|
||||
finally:
|
||||
conn.close()
|
||||
if owns:
|
||||
conn.close()
|
||||
|
||||
|
||||
def get_index_meta() -> dict[str, str]:
|
||||
|
||||
@@ -83,6 +83,22 @@ def _write_markdown(rel_path: str, markdown: str) -> None:
|
||||
path.write_text(markdown, encoding="utf-8")
|
||||
|
||||
|
||||
def _create_markdown(rel_path: str, markdown: str) -> None:
|
||||
"""排他创建 Markdown;目标已存在时返回资源冲突,不覆盖用户文件。"""
|
||||
path = _abs_path(rel_path)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
try:
|
||||
with path.open("x", encoding="utf-8") as handle:
|
||||
handle.write(markdown)
|
||||
except FileExistsError as exc:
|
||||
raise ApiError(
|
||||
409,
|
||||
"RESOURCE_CONFLICT",
|
||||
"a note already exists at this path",
|
||||
{"file_path": rel_path},
|
||||
) from exc
|
||||
|
||||
|
||||
def _delete_markdown(rel_path: str) -> None:
|
||||
path = _abs_path(rel_path)
|
||||
if path.exists():
|
||||
@@ -123,9 +139,12 @@ async def index_note(parsed: ParsedNote) -> None:
|
||||
if block.block_id in missing_ids
|
||||
]
|
||||
await vector_store.upsert(records, conn=conn)
|
||||
repository.set_index_meta(
|
||||
{"embedding_model": embedding.model_id, "embedding_dim": str(embedding.dim)},
|
||||
conn=conn,
|
||||
)
|
||||
finally:
|
||||
conn.close()
|
||||
repository.set_index_meta({"embedding_model": embedding.model_id, "embedding_dim": str(embedding.dim)})
|
||||
|
||||
|
||||
async def create_note(*, title: str, markdown: str, folder: str | None, tags: list[str]) -> Note:
|
||||
@@ -137,7 +156,14 @@ async def create_note(*, title: str, markdown: str, folder: str | None, tags: li
|
||||
created_at=now, updated_at=now,
|
||||
)
|
||||
parsed.title = title # 显式传入的 title 优先于正文推导(与 update_note 保持一致)
|
||||
_write_markdown(rel_path, markdown)
|
||||
if repository.get_note_record(parsed.note_id) is not None:
|
||||
raise ApiError(
|
||||
409,
|
||||
"RESOURCE_CONFLICT",
|
||||
"a note already exists at this path",
|
||||
{"note_id": parsed.note_id, "file_path": rel_path},
|
||||
)
|
||||
_create_markdown(rel_path, markdown)
|
||||
try:
|
||||
await index_note(parsed)
|
||||
except BaseException:
|
||||
|
||||
Reference in New Issue
Block a user