chore(release): v0.3.14 — AI fail 원인 가시화
- NoteCard: failed 노트에 <details> "원인 보기" 접힘 섹션 추가. ai_error 전체 노출 (wrap + word-break). - AiWorker: markAiFailed 시 [reason] provider prefix 추가. 사용자가 timeout/schema/other 카테고리 + 모델명 즉시 식별. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
27
CHANGELOG.md
27
CHANGELOG.md
@@ -3,6 +3,33 @@
|
|||||||
본 파일은 Inkling 의 버전별 사용자 영향 변경 사항을 기록한다.
|
본 파일은 Inkling 의 버전별 사용자 영향 변경 사항을 기록한다.
|
||||||
형식은 [Keep a Changelog](https://keepachangelog.com/) 를 느슨하게 따른다.
|
형식은 [Keep a Changelog](https://keepachangelog.com/) 를 느슨하게 따른다.
|
||||||
|
|
||||||
|
## [0.3.14] — 2026-05-12
|
||||||
|
|
||||||
|
AI 처리 fail 원인 가시화. 이전엔 ai_error 가 NoteCard tooltip (title attribute) 에만 있어 사용자가 마우스 오버해야 보이는 데다 raw 메시지만 노출 → 무엇이 fail 했는지 불명.
|
||||||
|
|
||||||
|
### 수정
|
||||||
|
|
||||||
|
- **NoteCard failed 노트에 "원인 보기" 접힘 섹션 (P1).** `<details>` summary 클릭하면 `<pre>` 로 `ai_error` 전체 노출. wrap + word-break 적용. 사용자가 직접 메시지를 보고 모델/네트워크/JSON 등 fail 카테고리 진단 가능.
|
||||||
|
- **`ai_error` 에 reason + provider name prefix 추가.** AiWorker 의 markAiFailed 시 `[schema|other] local-ollama/gemma4:26b\n<원본 message>` 형식. 사용자가 어느 카테고리에서, 어느 모델로 실패했는지 즉시 식별. log 의 ai.failed 에도 reason/provider 필드 함께 출력.
|
||||||
|
|
||||||
|
### 게이트
|
||||||
|
|
||||||
|
- 단위 752 PASS (ai_error 포맷 변경은 test 영향 없음 — 기존 test 가 정확한 prefix 매칭 안 함)
|
||||||
|
- typecheck 0 errors
|
||||||
|
- 신규 npm dependency 0
|
||||||
|
|
||||||
|
### 사용자 안내
|
||||||
|
|
||||||
|
이미지 AI 처리가 fail 한다면 NoteCard 의 "정리 보류" 옆 "원인 보기" 클릭 → 표시되는 메시지로:
|
||||||
|
- `[timeout] ...` → vision 모델 cold-start 가 5분 초과. `ollama run gemma4:26b` 으로 한번 warm-up 후 재시도
|
||||||
|
- `[schema] title must contain Korean characters` → vision 모델이 영어 title 반환. prompt 가 한국어 강조했지만 일부 모델은 여전히 영어. `gemma3:27b` 등 다른 vision 모델로 대체 고려
|
||||||
|
- `[schema] unparseable response: ...` → vision 모델 JSON 출력 안 따름. v0.3.12 의 loose parse 가 실패한 경우
|
||||||
|
- `[other] missing response field` → Ollama 가 빈 응답 반환. 모델 자체 문제
|
||||||
|
|
||||||
|
### 업그레이드
|
||||||
|
|
||||||
|
v0.3.13 인스톨러 위에 v0.3.14 인스톨러를 같은 위치에 실행하면 in-place 업그레이드.
|
||||||
|
|
||||||
## [0.3.13] — 2026-05-12
|
## [0.3.13] — 2026-05-12
|
||||||
|
|
||||||
대형 vision 모델 (gemma4:26b 등) 의 cold-start timeout 으로 인한 AI 처리 실패 fix.
|
대형 vision 모델 (gemma4:26b 등) 의 cold-start timeout 으로 인한 AI 처리 실패 fix.
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "inkling",
|
"name": "inkling",
|
||||||
"version": "0.3.13",
|
"version": "0.3.14",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "inkling",
|
"name": "inkling",
|
||||||
"version": "0.3.13",
|
"version": "0.3.14",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"better-sqlite3": "12.9.0",
|
"better-sqlite3": "12.9.0",
|
||||||
"electron-log": "5.2.0",
|
"electron-log": "5.2.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "inkling",
|
"name": "inkling",
|
||||||
"version": "0.3.13",
|
"version": "0.3.14",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Inkling — local-first 한 줄 보관 도구",
|
"description": "Inkling — local-first 한 줄 보관 도구",
|
||||||
"author": "altair823 <dlsrks0734@gmail.com>",
|
"author": "altair823 <dlsrks0734@gmail.com>",
|
||||||
|
|||||||
@@ -233,8 +233,13 @@ export class AiWorker {
|
|||||||
const nextRunAt = new Date(Date.now() + (this.backoffsMs[attempt + 1] ?? 0)).toISOString();
|
const nextRunAt = new Date(Date.now() + (this.backoffsMs[attempt + 1] ?? 0)).toISOString();
|
||||||
this.repo.incrementJobAttempt(job.noteId, nextRunAt, msg);
|
this.repo.incrementJobAttempt(job.noteId, nextRunAt, msg);
|
||||||
if (isLast) {
|
if (isLast) {
|
||||||
this.repo.markAiFailed(job.noteId, msg);
|
// v0.3.14 — ai_error 에 reason + provider name prefix 추가. NoteCard 의 "원인 보기"
|
||||||
this.logger.error('ai.failed', { noteId: job.noteId, err: msg });
|
// 가 사용자에게 보여주는 raw 메시지에 context (timeout/unreachable/schema/other +
|
||||||
|
// 어느 모델이 fail 했는지) 가 포함되어 진단성 향상.
|
||||||
|
const provider = this.holder.get().name;
|
||||||
|
const annotated = `[${reason}] ${provider}\n${msg}`;
|
||||||
|
this.repo.markAiFailed(job.noteId, annotated);
|
||||||
|
this.logger.error('ai.failed', { noteId: job.noteId, err: msg, reason, provider });
|
||||||
if (this.telemetry) {
|
if (this.telemetry) {
|
||||||
await this.telemetry.emit({
|
await this.telemetry.emit({
|
||||||
kind: 'ai_failed',
|
kind: 'ai_failed',
|
||||||
|
|||||||
@@ -236,8 +236,9 @@ export function NoteCard({ note, onDeleted, onUpdated, mode = 'inbox', onRestore
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{local.aiStatus === 'failed' && (
|
{local.aiStatus === 'failed' && (
|
||||||
<div style={{ marginTop: 4, display: 'flex', alignItems: 'center', gap: 8 }}>
|
<div style={{ marginTop: 4 }}>
|
||||||
<div title={local.aiError ?? ''} style={{ fontSize: 16, fontWeight: 600, color: '#a55' }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||||
|
<div style={{ fontSize: 16, fontWeight: 600, color: '#a55' }}>
|
||||||
정리 보류 — 원문은 안전합니다
|
정리 보류 — 원문은 안전합니다
|
||||||
</div>
|
</div>
|
||||||
{/* v0.3.9 — per-note 재시도 UI. FailedBanner 의 일괄 재시도와 별개. */}
|
{/* v0.3.9 — per-note 재시도 UI. FailedBanner 의 일괄 재시도와 별개. */}
|
||||||
@@ -254,6 +255,22 @@ export function NoteCard({ note, onDeleted, onUpdated, mode = 'inbox', onRestore
|
|||||||
재시도
|
재시도
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
{/* v0.3.14 — fail 원인 inline 표시. ai_error 의 raw message 가 그대로 사용자에게
|
||||||
|
보여서 디버깅 + 모델/네트워크 이슈 진단 가능. 너무 길면 <details> 로 접힘. */}
|
||||||
|
{local.aiError !== null && local.aiError.length > 0 && (
|
||||||
|
<details style={{ marginTop: 4 }}>
|
||||||
|
<summary style={{ fontSize: 12, color: '#a55', cursor: 'pointer' }}>
|
||||||
|
원인 보기
|
||||||
|
</summary>
|
||||||
|
<pre style={{
|
||||||
|
fontSize: 11, color: '#666', background: '#fff0f0', padding: 6,
|
||||||
|
borderRadius: 4, marginTop: 4, whiteSpace: 'pre-wrap', wordBreak: 'break-word'
|
||||||
|
}}>
|
||||||
|
{local.aiError}
|
||||||
|
</pre>
|
||||||
|
</details>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* v0.2.9 Cut B Task 13 — ai_status='disabled': raw_text 첫 줄 fallback title.
|
{/* v0.2.9 Cut B Task 13 — ai_status='disabled': raw_text 첫 줄 fallback title.
|
||||||
summary/tags 는 hide. 원문은 아래 "원문 보기" 영역에서 항상 표시. */}
|
summary/tags 는 hide. 원문은 아래 "원문 보기" 영역에서 항상 표시. */}
|
||||||
|
|||||||
Reference in New Issue
Block a user