chore(release): v0.3.12 — vision AI 응답 robust parse

vision model 의 markdown fence / prose 섞인 응답에서 JSON 추출 fallback.
prompt 에 title 한국어 / kebab tags / JSON-only 출력 명시 강화.

- LocalOllamaProvider: parseJsonLoose 헬퍼 (첫 { ~ 마지막 } 추출)
- visionPrompt: 4 규칙 + markdown fence 금지 명시
- 단위 +2 (fence 추출 + prose 추출)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
th-kim0823
2026-05-12 13:32:12 +09:00
parent d3cf018f62
commit 713553a038
6 changed files with 73 additions and 9 deletions

View File

@@ -51,7 +51,24 @@ describe('LocalOllamaProvider', () => {
});
await expect(
new LocalOllamaProvider().generate({ text: 'x', todayKst: '2026-04-26', dueDateCandidates: [] })
).rejects.toThrow(/json/i);
).rejects.toThrow(/unparseable/i);
});
it('v0.3.11 — generate extracts JSON from markdown fence', async () => {
// vision model 이 ```json ... ``` 형태로 응답하는 경우 fallback 으로 추출.
mock.get('http://localhost:11434').intercept({ path: '/api/generate', method: 'POST' }).reply(200, {
response: '```json\n{"title":"회의","summary":"a\\nb\\nc","tags":["meet"]}\n```'
});
const r = await new LocalOllamaProvider().generate({ text: 'x', todayKst: '2026-04-26', dueDateCandidates: [] });
expect(r.title).toBe('회의');
});
it('v0.3.11 — generate extracts JSON when prose 가 앞뒤로 섞임', async () => {
mock.get('http://localhost:11434').intercept({ path: '/api/generate', method: 'POST' }).reply(200, {
response: 'Here is the response:\n{"title":"회의","summary":"a\\nb\\nc","tags":[]}\nDone.'
});
const r = await new LocalOllamaProvider().generate({ text: 'x', todayKst: '2026-04-26', dueDateCandidates: [] });
expect(r.title).toBe('회의');
});
it('generate aborts on timeout', async () => {