sync: collector/ 同步自 inkstone/arcrun-rag@1c31ac2(桌面小幫手 0.18.46)

This commit is contained in:
2026-08-28 05:07:35 +08:00
parent 80877af60c
commit e8800d18ef
12 changed files with 540 additions and 46 deletions
+28 -4
View File
@@ -541,6 +541,7 @@ func (c *DirectConfig) postJSON(step callStep, url string, body any) (int, strin
if err != nil {
return 0, "", gate.record(err)
}
gate.ok() // #153:回來了就把「連續逾時」的計數歸零——否則「連續」兩個字是假的
defer resp.Body.Close()
// 🔴 讀 64KB 而不是 1KB:觸發端點的回應是一層外殼包著工作流的輸出,
// 而**失敗的證據住在殼裡面**(見 triggeroutcome.go)。1KB 會把 JSON 切斷 ⇒
@@ -998,8 +999,7 @@ func RunDirectOnce(cfg *DirectConfig, dryRun bool) ([]DirectResult, int, *Trigge
// 使用者才看得到「為什麼」而不只是「幾份」。
// 2026-08-07:額度冷卻的 skip 訊息(quotaState.noticeNow().Combined()
// 用「會自動恢復」當識別字——同樣要讓使用者看得到原因,不是只看到「幾份」。
if strings.Contains(r.Error, "後重試") || strings.Contains(r.Error, "已暫停自動重試") ||
strings.Contains(r.Error, "會自動恢復") {
if explainsWhySkipped(r.Error) {
st.Failures = append(st.Failures, ExtractFail{
Path: r.Path,
Error: shortError(r.Error),
@@ -1482,6 +1482,29 @@ func runDirectOnceRoot(cfg *DirectConfig, root string, dryRun bool, qs *quotaSta
now := runNow.Unix()
// 🔴 `inkstone/arcrun-rag#153` 第二輪:**樹算出來就立刻落地本機,排在所有雲端呼叫之前。**
//
// 為什麼非搬到這裡不可(2026-08-28 用 `ARCRUN_TRACE=1` 量出來的,不是推測):
// 對一個**健康**的雲端實例,每一發呼叫實測 24〜33 秒
//(資料夾總覽 26.3s/目錄索引 24.0・24.1・23.5s/送出一份筆記 33.2・44.6s)。
// 一個 12 層、10 個檔的資料夾,一輪就是十幾分鐘;leo 真正的設定是三個帳號、
// 好幾千個檔,一輪是好幾小時。**沒有任何一發超過 300 秒,所以逾時與斷路器都不會動**
// ——這不是卡住,是這一輪還沒輪到寫它。
//
// 而這棵樹是**純本機、秒級**算出來的(就在上面那次 Scan 的產物上),
// 它沒有理由排在十幾分鐘的雲端佇列後面。搬到這裡之後,使用者的資料夾結構
// 在開跑幾秒內就是對的,即使這一輪還要再跑一小時。
//
// 內容一個位元都沒變:BuildFolderTree 吃的是 payload.DirStatsm.Entries
// payload.AllExcludedDirsplan,而從 Scan() 到這裡之間**沒有任何東西動過 m.Entries**
//(動它的是下面「removed 暫時放回」那段,本來就在原位置之後)。
// 送上雲端那一發(syncFolderTree)**維持在原來的位置**,用的就是這一棵。
tree := BuildFolderTree(absRoot, cfg.libraryFor(absRoot), payload.DirStats, m.Entries,
payload.AllExcludedDirs, plan, runNow)
if !dryRun {
PublishFolderTreeNow(cfg.Manifest, root, tree, runNow)
}
// 結構先行(InkStoneCo#432026-08-15):掃描一結束(純本機、免費、秒級)就先把
// 「這個資料夾有哪些檔案/最近改了什麼」送上知識庫,**不等 LLM 萃取、不受額度影響**
// ——走 rag_ingest_card(零 LLM 的機械收口),所以刻意放在:
@@ -1517,8 +1540,6 @@ func runDirectOnceRoot(cfg *DirectConfig, root string, dryRun bool, qs *quotaSta
// 再加第四個**只屬於它**的:**空資料夾一個事件都不會有**arcrun-rag#106 的情境本身),
// 所以它不能被任何「有事件才做」的閘擋住——分子分母都由現況算出,靜止時
// 內容雜湊自然擋住重送,不需要事件當第二道閘。
tree := BuildFolderTree(absRoot, cfg.libraryFor(absRoot), payload.DirStats, m.Entries,
payload.AllExcludedDirs, plan, runNow)
if treeRes := syncFolderTree(cfg, absRoot, m, tree, dryRun, runNow); treeRes != nil {
results = append(results, *treeRes)
if treeRes.Status != "planned" {
@@ -1678,6 +1699,9 @@ func runDirectOnceRoot(cfg *DirectConfig, root string, dryRun bool, qs *quotaSta
xgate := cfg.openGate(stepExtractDoc)
cards, xerr = ExtractWithWorkersAI(cfg.CypherURL, cfg.APIKey, absRoot, ev.Path, cardOrigin)
xgate.release()
if xerr == nil {
xgate.ok()
}
xerr = xgate.record(xerr) // 只有「等到超時」會被記帳,其餘錯誤原樣往下走
case "gemma":
cards, xerr = ExtractWithGemma(cfg.GeminiAPIKey, cfg.LLMModel, absRoot, ev.Path, cardOrigin)