From 81fae12a8cdf92bf7414fd2a1c02c89532e63e64 Mon Sep 17 00:00:00 2001 From: altair823 Date: Sun, 10 May 2026 05:07:55 +0900 Subject: [PATCH] fix(v031): endpoint resolution + 5MB fast-fail (final review fix) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit final code review (Opus) 발견 minor issues 중 valuable 2건: 1. settings:refresh-vision-cache 가 settings.ollama.endpoint 만 체크 — env / default fallback 누락. dev 환경 (env var only) 사용자가 manual 다시 감지 시 'no_endpoint' silent fail. → index.ts 의 resolvedEndpoint 와 동일 fallback 체인 (settings → env → DEFAULT_OLLAMA_ENDPOINT). 2. AiWorker 의 5MB cap 이 readFile + base64 변환 후 throw — retry 마다 동일 비용 반복. note.media[].bytes 가 DB 에 이미 있으니 readFile 전 fast-fail. 비용 절감 + 동일 회로 (markAiFailed 도달). 회귀 test 영향 없음 (기존 5MB throw 시나리오 그대로 — fast-fail 도 throw 분기 동일). --- src/main/ai/AiWorker.ts | 9 ++++++--- src/main/ipc/settingsApi.ts | 11 +++++++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/main/ai/AiWorker.ts b/src/main/ai/AiWorker.ts index 73a4b83..76718dc 100644 --- a/src/main/ai/AiWorker.ts +++ b/src/main/ai/AiWorker.ts @@ -140,15 +140,18 @@ export class AiWorker { const candidates = parseAllCandidates(note.rawText, todayDate); const vocab = this.repo.getTopUsedTags(VOCAB_TOP_N); // v0.3.1 Cut F — vision path: visionModel + note.media → base64 images + // final review fix: note.media[].bytes 로 fast-fail (readFile/base64 비용 회피). + // 5MB cap 초과 시 throw → AiWorker 의 'other' 분기 → markAiFailed 도달. const visionModel = this.settings ? await this.settings.getVisionModel() : null; let images: Array<{ base64: string; mime: string }> | undefined; if (visionModel && note.media.length > 0 && this.mediaStore) { + const oversize = note.media.find((m) => m.bytes > 5 * 1024 * 1024); + if (oversize) { + throw new Error(`image ${oversize.relPath} exceeds 5MB cap (${oversize.bytes} bytes)`); + } images = await Promise.all( note.media.map(async (m) => { const buf = await readFile(this.mediaStore!.absolutePath(m.relPath)); - if (buf.byteLength > 5 * 1024 * 1024) { - throw new Error(`image ${m.relPath} exceeds 5MB cap`); - } return { base64: buf.toString('base64'), mime: m.mime }; }) ); diff --git a/src/main/ipc/settingsApi.ts b/src/main/ipc/settingsApi.ts index 8294a11..1214ef5 100644 --- a/src/main/ipc/settingsApi.ts +++ b/src/main/ipc/settingsApi.ts @@ -14,6 +14,7 @@ import type { SyncTimer } from '../services/SyncTimer.js'; import { collectAutostartState } from '../services/AutostartDiagnostic.js'; import { getInboxWindow as getInboxWindowSingleton } from '../windows/inboxWindow.js'; import { refreshVisionCache } from '../services/VisionDetect.js'; +import { DEFAULT_OLLAMA_ENDPOINT } from '../../shared/constants.js'; /** * 외부 (트레이 / second-instance / 기타 main 프로세스 호출자) 에서 inbox 창에 view 전환을 @@ -395,11 +396,13 @@ export function registerSettingsApi(deps?: SettingsIpcDeps): void { }); ipcMain.handle('settings:refresh-vision-cache', async () => { + // Cut F final review fix — index.ts 의 resolvedEndpoint (settings → env → default) + // 와 동일한 fallback 체인 사용. settings.ollama 미설정 + env / default 만 있는 dev + // 환경에서도 manual "다시 감지" 가 동작하도록. const all = await deps.settings.getAll(); - const endpoint = all.ollama?.endpoint; - if (!endpoint) { - return { ok: false as const, reason: 'no_endpoint' }; - } + const endpoint = all.ollama?.endpoint + ?? process.env.INKLING_OLLAMA_ENDPOINT + ?? DEFAULT_OLLAMA_ENDPOINT; return refreshVisionCache({ settings: deps.settings, endpoint }); }); }