Files
kebab/crates/kebab-store-vector/Cargo.toml
altair823 2ad99ab491 chore: PR #233 리뷰 반영 — 압축 트리거·플래그 이름·불필요 의존 정리
리뷰에서 나온 지적 중 실제로 고칠 값이 있는 4건 반영.

1) 압축 트리거를 인메모리 카운터 -> Lance 테이블 version 으로

   `upserts_since_compact: AtomicU64` 는 store 인스턴스 수명 동안만 살아
   있는데, `kebab ingest-file` 과 MCP `ingest_file`/`ingest_stdin` 은 호출마다
   새 App(따라서 새 store)을 연다. 한 번에 수만 건 넣는 ingest 에서만 512 에
   닿고, 한 건씩 넣는 경로에서는 카운터가 매번 0 으로 되돌아가 압축이 영영
   안 돌았다 — PR 이 잡겠다던 fragment 누적이 그 경로에 그대로 남는 셈.

   Lance 의 `table.version()` 은 테이블에 저장돼 프로세스를 넘어 단조 증가
   하므로 그걸 기준으로 바꿨다. 부수적으로 struct 에서 카운터 필드와
   AtomicU64 import 가 사라졌다.

2) `--fail-under` -> `--max-drop`

   관용적으로 `--fail-under 0.9` 는 "지표가 0.9 미만이면 실패" 로 읽히는데
   실제 의미는 "0.9 이상 떨어지면 실패" 였다. 그대로 두면 CI 에 하한이라고
   믿고 적은 값이 아무것도 안 막는다 — recall 0.95 -> 0.10 붕괴도 낙폭
   0.85 < 0.9 라 통과. 새로 노출되는 표면이라 지금이 바꿀 수 있는 시점이다.
   음수·nan 은 시작 시점에 거부한다(그대로 두면 게이트가 무력화됨).

3) `chrono` 직접 의존 제거

   lancedb 가 `chrono::Duration` 을 이미 re-export 한다
   (lancedb-0.23.1/src/table.rs:85). 앞 커밋이 Cargo.toml 에 적은
   "lancedb 는 chrono 를 re-export 하지 않는다" 는 사실이 아니었다.

4) 안전성 근거 주석 정정 + 측정치 통일

   `delete_unverified: true` 의 근거를 "kebab ingest 는 단일 동기 프로세스"
   라고 단언했으나, 이 리포는 `kebab mcp` 를 preferred 통합 표면으로 배포하고
   그 서버는 ingest 까지 노출한다. 락도 없다. 단언 대신 전제와 그 전제가
   코드로 강제되지 않는다는 사실, 그리고 이 플래그 없이는 신규 색인에서
   아무것도 회수되지 않는다는 trade-off 를 그대로 적었다.
   압축 소요는 사본 측정치(102초) 대신 실 테이블 측정치(153초)로 통일.

`is_multiple_of` 는 MSRV(1.85) 보다 높은 1.87 안정화라 `%` 로 대체.

검증: 워크스페이스 186 결과 전부 통과 · 실패 0. `--max-drop` 3경로 실증
(통과 exit 0 / 잘못된 값 exit 2 / 회귀 exit 1). 회귀 경로에서 "측정 불가"
가드가 실제 run 쌍으로 발동하는 것도 확인.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017c9JwQq8ZkGvYjpKXMiDhF
2026-08-16 14:56:26 +09:00

59 lines
2.5 KiB
TOML

[package]
name = "kebab-store-vector"
version = { workspace = true }
edition = { workspace = true }
rust-version = { workspace = true }
license = { workspace = true }
repository = { workspace = true }
description = "LanceDB-backed VectorStore for kb (§5.6 embedding_records, §6.3 lancedb tables, §7.2 VectorStore)"
[dependencies]
kebab-core = { path = "../kebab-core" }
kebab-config = { path = "../kebab-config" }
# kb-store-sqlite is allowed for the embedding_records writers only
# (P3-3 spec: "Allowed dep `kb-store-sqlite` for writing/reading rows in
# embedding_records"). The Two-phase upsert flow uses
# `put_embedding_records_pending` + `mark_embedding_records_committed`.
kebab-store-sqlite = { path = "../kebab-store-sqlite" }
# LanceDB embedded vector store. `default-features=false` opts out of
# the cloud object-store integrations (aws / gcs / azure / dynamodb /
# oss); kb is always-local for v1, so dragging in those SDKs would just
# inflate the build.
lancedb = { workspace = true }
arrow = { workspace = true }
arrow-array = { workspace = true }
arrow-schema = { workspace = true }
# Embedded async runtime. The VectorStore trait is sync (§7.2) but
# LanceDB's Rust API is async-only; we own a current-thread
# tokio::Runtime and `block_on` per trait method. current-thread saves
# the two worker threads a multi-thread runtime would spawn — kb-app
# already serializes vector ops behind its own job scheduler so the
# extra parallelism wouldn't be exploited.
tokio = { workspace = true }
# `try_collect` for streaming Lance query results into a Vec<RecordBatch>.
futures = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
tracing = { workspace = true }
thiserror = { workspace = true }
anyhow = { workspace = true }
blake3 = { workspace = true }
time = { workspace = true }
[dev-dependencies]
tempfile = { workspace = true }
serde_json = { workspace = true }
# Integration tests seed `documents` / `chunks` fixtures by raw SQL
# (no kb-parse-md / kb-normalize / kb-chunk dep) so they can construct
# adversarial filter / dim-mismatch states. rusqlite is a `[dev-]`
# dep only — the runtime crate uses kb-store-sqlite's typed surface
# (`filter_chunks`, `put_embedding_records_pending`, …) and does not
# touch rusqlite directly (P3-3 spec: kb-store-vector must not list
# rusqlite/globset as direct deps).
rusqlite = { workspace = true }
[lints]
workspace = true