674e1b4fa2
leo 逐行複核 fix/embed-backfill-d68 後點出的破口+二次裁決(票上全文見 Leo/Arcrun#85): 一、向量化優先序(今天寫的立刻/本週在跑的先跑/有查詢紀錄的庫優先/半年前慢慢跑)表達 不出來——策略要能從外面(工作流)指定,不能焊死在資料層。新增 embed.ts 的 `SelectionCriteria`(owner_id/source/library/since/until),backfillEmbeddings 與 reconcileEmbedGeneration 共用同一套形狀;「按庫」那一層現在有資料可用即可運作(見下)。 二、世代核對(reconcileEmbedGeneration)不打 AI 但逐筆寫 D1,47 萬筆候選 ≈ 4.7 倍 D1 100,000 rows/日免費額度,先前零保護。新增 actions/maintenance-quota.ts(單一 entries 列/日的共用計數器,精神同 execution-log.ts/embed.ts 既有慣例,不新增表)。 三、leo 二度裁決:「標庫」與「時間分層」其實是一件事,判定標準要從第一天同時容納兩者, 不能先做一半再回頭改。新增 actions/library-backfill.ts 的 backfillEntryLibraryTags—— 呼叫端(ingest/daemon/Arcrun#87)決定要貼哪個庫、用 page_names(Gitea 原稿卡名精準 點名,leo 定案的正解)或 source_prefix/page_name_prefix 過渡 fallback 篩選候選,base 只負責安全、節流地寫入。owner_id 刻意必填(leo 點出「補錯 owner 等於白做」——實查卡片 掛在 owner_id=bfezv28v,換成 'leo' 查卻是空的)。 D69:reconcile 與標庫 backfill 共用同一顆「今天還剩多少 D1 寫入額度」計數器(不共用的話 其中一個會把另一個的閘繞過去);新增 POST /entries/backfill-library + GET .../status, 擴充 POST /embed/backfill 與 /embed/reconcile 吃 library/since/until 參數。 測試:92 → 39 個新增/擴充案例覆蓋 since/until/library 篩選、reconcile 額度真的擋 (含「拿掉 cap 會變紅」反向驗證)、標庫 backfill 冪等/owner_id 必填/page_names 精準比對、 以及兩個操作共用同一顆額度計數器的跨模組驗證(雙向:先 reconcile 耗盡再標庫、反之亦然)。 kbdb 全套 192 個測試綠燈,tsc --noEmit 除既有 auth.test.ts 舊缺陷外無新增錯誤。 紅線:未併 main、未部署、未動任何實例的 is_embedded 旗標(只在本地 SQLite 測試治具跑過)。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
90 lines
3.7 KiB
TypeScript
90 lines
3.7 KiB
TypeScript
// KBDB Base types. Base depends on D1 only.
|
||
// Optional modules add their own bindings (embed: VECTORIZE+AI). Base never references them.
|
||
|
||
export type Bindings = {
|
||
DB: D1Database;
|
||
ENVIRONMENT: string;
|
||
// Auth guard (t115 二修, fail-closed): provisioned by the installer automatically.
|
||
// NOT set → writes (POST/PATCH/DELETE/PUT) rejected 401; reads pass with a warning
|
||
// (upgrade-window grace so read-only workflows don't break before both workers are
|
||
// updated together).
|
||
// SET → all non-health routes require `Authorization: Bearer <token>`.
|
||
// cypher-executor sends this via kbdbBase(); portal/webhooks/recipes send it inline.
|
||
KBDB_INTERNAL_TOKEN?: string;
|
||
// Optional embed module (issue #7 / SDD T2.4). Present ONLY when the self-host opened
|
||
// semantic search (kbdb_embed:true → deploy injects [[vectorize]] + [ai]). Base never
|
||
// requires them; code checks `if (env.VECTORIZE && env.AI)` before touching embed.
|
||
VECTORIZE?: VectorizeIndex;
|
||
AI?: Ai;
|
||
// 嵌入模型(Arcrun#59)。未設=用 embed.ts 的預設。設成別的模型時,**Vectorize index 的
|
||
// dimensions 必須跟著對**(維度不合 upsert 會被 CF 拒絕),且換模型必須換 index:
|
||
// 不同模型的向量不可共存於同一個 index(比對出來是垃圾),詳見 embed.ts 檔頭。
|
||
EMBED_MODEL?: string;
|
||
// execution_log 每日軟上限(KV 額度事故修復,2026-08-07;A2 自我降級,見
|
||
// kbdb/src/actions/execution-log.ts DEFAULT_DAILY_LIMIT 說明)。未設 → 20000
|
||
// (D1 100,000 rows written/日的 20%,留 80% 給知識卡 entries)。
|
||
EXECUTION_LOG_DAILY_WRITE_LIMIT?: string;
|
||
// embed backfill 每日軟上限(D68,2026-08-11:補算向量照時間新到舊、且每天有額度上限)。
|
||
// backfill 與「寫入即嵌」「萃取」共用同一份 Workers AI 每日 10,000 免費 neurons(見頂層
|
||
// wiki ops-facts.md);backfill 是背景低優先動作,自設軟上限不把當天額度燒光。未設 → 見
|
||
// kbdb/src/embed.ts DEFAULT_BACKFILL_DAILY_LIMIT 說明(含選值算式,非拍腦袋)。
|
||
EMBED_BACKFILL_DAILY_LIMIT?: string;
|
||
// 背景維護寫入(reconcile 世代核對 + 標庫 backfill)共用的 D1 每日寫入軟上限
|
||
// (Arcrun#85 D69 修法,2026-08-11:兩者都是「多筆 D1 row write、不打 AI」的操作,
|
||
// 各自不設防都會單獨燒穿 D1 100,000 rows/日免費額度——reconcile 47 萬筆 candidate
|
||
// ≈ 4.7 倍全日額度,已在票上實測;標庫 backfill 同樣是逐筆 D1 write,若各管各的,
|
||
// 補標庫時會把 reconcile 的閘繞過去。兩者共用同一顆「今天還剩多少」計數器。
|
||
// 未設 → 見 kbdb/src/actions/maintenance-quota.ts DEFAULT_MAINTENANCE_DAILY_WRITE_LIMIT。
|
||
KBDB_MAINTENANCE_DAILY_WRITE_LIMIT?: string;
|
||
};
|
||
|
||
export type EntryType =
|
||
| 'block'
|
||
| 'value'
|
||
| 'template'
|
||
| 'slot'
|
||
| 'project'
|
||
| 'workflow'
|
||
| 'recipe_stat'
|
||
| 'execution_log'
|
||
| 'execution_log_usage'
|
||
| 'embed_backfill_usage'
|
||
| 'kbdb_maintenance_usage';
|
||
|
||
export interface Entry {
|
||
id: string;
|
||
content: string | null;
|
||
entry_type: EntryType | string;
|
||
owner_id: string | null;
|
||
parent_id: string | null;
|
||
page_name: string | null;
|
||
refs_json: string;
|
||
tags_json: string;
|
||
task_status: string | null;
|
||
content_hash: string | null;
|
||
is_embedded: number;
|
||
confidence: number | null;
|
||
metadata_json: string | null;
|
||
created_at: number;
|
||
updated_at: number;
|
||
}
|
||
|
||
export interface Template {
|
||
id: string;
|
||
name: string;
|
||
description: string | null;
|
||
slots_json: string;
|
||
created_by: string | null;
|
||
created_at: number;
|
||
updated_at: number;
|
||
}
|
||
|
||
export interface EntryValue {
|
||
id: string;
|
||
record_id: string;
|
||
template_id: string;
|
||
slot_name: string;
|
||
entry_id: string;
|
||
created_at: number;
|
||
}
|