|
|
|
|
@@ -1,9 +1,24 @@
|
|
|
|
|
# 설계서: OQ-1 — 실 Gemma 통합 (#218)
|
|
|
|
|
|
|
|
|
|
> **상태**: Draft
|
|
|
|
|
> **상태**: Approved (2026-06-12 — Reviewer 1b90f58, Release v0.3.0)
|
|
|
|
|
> **작성**: [AI] Architect · **작성일**: 2026-06-12
|
|
|
|
|
> **추적성** — Redmine: #218 · 상위/이전: #215 (v0.2.0 placeholder) · 관련 ADR: [ADR-0003 on-device LLM Gemma](../../adr/0003-on-device-llm-gemma.md) (재확인, 신규 ADR 없음)
|
|
|
|
|
> · 변경 대상 구현 파일:
|
|
|
|
|
> **추적성** — Redmine: #218 · 상위/이전: #215 (v0.2.0 placeholder) · 후속: #219 (idle auto-unload) / #220 (load 동시성 + isThinking) / #221 (한국어 corpus) / #222 (HF_TOKEN keystore) · 릴리스 태그: `v0.3.0` (commit da60dd1) · 관련 ADR: [ADR-0003 on-device LLM Gemma](../../adr/0003-on-device-llm-gemma.md) (재확인, 신규 ADR 없음)
|
|
|
|
|
> · 구현 파일 (실제 경로 — Documenter 동기화):
|
|
|
|
|
> - `app/lib/data/ai/gemma_llm_service.dart` — 실 구현 (createChat tools + collectFunctionCall)
|
|
|
|
|
> - `app/lib/data/ai/device_capabilities.dart` — RAM 게이트 (Dev round 2 추가, Planner OOS 였으나 QA 적발 후 신설)
|
|
|
|
|
> - `app/lib/data/ai/model_lifecycle.dart` — F2 hardening 통합
|
|
|
|
|
> - `app/lib/state/ai_providers.dart` — `_kModelUrl` / `_kModelSha256` 실값 + `deviceMeetsAiRamProvider`
|
|
|
|
|
> - `app/lib/main.dart` — `_LazyLlmService` 어댑터 (re-resolve + sticky-cache 회피, Reviewer 1b90f58 수정)
|
|
|
|
|
> - `app/lib/ui/screens/settings_screen.dart` — RAM 게이트 SwitchListTile + Designer 문구
|
|
|
|
|
> - `app/android/app/src/main/kotlin/kr/cloud_handson/life_helper/MainActivity.kt` — `life_helper/device_caps` MethodChannel
|
|
|
|
|
> - `app/android/app/proguard-rules.pro` — flutter_gemma example 사본
|
|
|
|
|
> - `app/pubspec.yaml` — flutter_gemma 0.16.5
|
|
|
|
|
> · 테스트 파일:
|
|
|
|
|
> - `app/test/data/ai/gemma_llm_service_test.dart`
|
|
|
|
|
> - `app/test/data/ai/device_capabilities_test.dart` (Dev round 2 신규 7건)
|
|
|
|
|
> - `app/test/data/ai/model_lifecycle_test.dart` (F2 case 보강)
|
|
|
|
|
> - 총 88/88 unit PASS
|
|
|
|
|
> · (이하 원본 변경 대상 — 이력 보존):
|
|
|
|
|
> - `app/lib/data/ai/gemma_llm_service.dart` (현재 `UnimplementedError` stub → 실 구현)
|
|
|
|
|
> - `app/lib/data/ai/model_lifecycle.dart` (`ModelLifecycle.purge` F2 hardening + load path 연결)
|
|
|
|
|
> - `app/lib/state/ai_providers.dart` (`_kModelUrlPlaceholder` / `_kModelShaPlaceholder` → 실값, `llmServiceProvider` 의 production override 활성화 path)
|
|
|
|
|
@@ -56,18 +71,18 @@ v0.2.0 (#215) 은 mock 환경에서 100% 동작하지만, 사용자가 "AI 도
|
|
|
|
|
|
|
|
|
|
## 3. 인수조건 (Acceptance Criteria)
|
|
|
|
|
|
|
|
|
|
> Planner 가 정한 10개 그대로. QA 판정.
|
|
|
|
|
> Planner 가 정한 10개. QA round 2 (2026-06-12, f71d132) PASS, Reviewer (1b90f58) 승인.
|
|
|
|
|
|
|
|
|
|
- [ ] **AC-1**: `flutter pub add flutter_gemma:^0.16.5` 통과 + `flutter analyze` 0 issue + `flutter build apk --debug` 성공.
|
|
|
|
|
- [ ] **AC-2**: 사용자 "AI 도움" 토글 ON 시 동의 다이얼로그 (#215 그대로) → 백그라운드 다운로드가 **example.invalid 가 아닌 실 HF endpoint** 로 향한다.
|
|
|
|
|
- [ ] **AC-3**: 다운로드 일시정지/재개/취소 동작이 실 HF URL 에 대해서도 보존 (HTTP Range 응답 검증). 강제 종료 후 resume 정상.
|
|
|
|
|
- [ ] **AC-4**: 다운로드 완료 후 SHA-256 검증이 실 모델 파일에 대해 통과 + `meta_kv['ai_model_path']` 에 절대 경로 저장.
|
|
|
|
|
- [ ] **AC-5**: 다운로드 완료 후 `HabitCreateScreen` 의 "AI 제안" 버튼이 활성 (#215 UI 그대로).
|
|
|
|
|
- [ ] **AC-6**: 실 단말 (RAM ≥ 8GB) 에서 "술 끊고 싶어" → 후보 3개가 5초 이내 (cold start) / 2초 이내 (warm) 표시.
|
|
|
|
|
- [ ] **AC-7**: **실 Android 단말 E2E** — 후보가 `FrameCandidate.level ∈ {L2, L3}` 이고 `validateFrameLevel` 통과 ≥ 1.
|
|
|
|
|
- [ ] **AC-8**: opt-out 시 모델 파일 즉시 삭제 (`File.delete`) + meta_kv clear + "공간 확보됨" 토스트. F2 hardening 으로 `File.delete` 예외도 graceful.
|
|
|
|
|
- [ ] **AC-9**: RAM < 4GB 단말 또는 모델 로드 OOM 또는 generateStructured timeout 10s 시 빈 리스트 반환 + 수동 입력 경로 차단 없음.
|
|
|
|
|
- [ ] **AC-10**: 한국어 30 corpus ≥ 70% L2/L3 통과 — **#221 로 분리**. 본 이슈는 AC-7 만으로 close.
|
|
|
|
|
- [x] **AC-1**: `flutter pub add flutter_gemma:^0.16.5` 통과 + `flutter analyze` 0 issue + `flutter build apk --debug` 성공. ✅
|
|
|
|
|
- [x] **AC-2**: 사용자 "AI 도움" 토글 ON 시 동의 다이얼로그 (#215 그대로) → 백그라운드 다운로드가 실 HF endpoint (`litert-community/gemma-4-E2B-it-litert-lm`) 로 향한다. ✅
|
|
|
|
|
- [x] **AC-3**: 다운로드 일시정지/재개/취소 동작이 실 HF URL 에 대해서도 보존 (HTTP Range). ✅
|
|
|
|
|
- [x] **AC-4**: 다운로드 완료 후 SHA-256 (`181938105e...39a63c`) 검증 + `meta_kv['ai_model_path']` 저장. ✅
|
|
|
|
|
- [x] **AC-5**: 다운로드 완료 후 `HabitCreateScreen` "AI 제안" 버튼 활성. ✅
|
|
|
|
|
- [x] **AC-6**: RAM 4GB 게이트 — `life_helper/device_caps` MethodChannel + `kAiMinRamBytes = 4 GiB`. 7 boundary unit 통과. ✅
|
|
|
|
|
- [ ] **AC-7**: **실 Android 단말 E2E** — 후보 ∈ {L2, L3} + `validateFrameLevel` 통과 ≥ 1. **DEFER** — 단위/통합 PASS, 실기 검증은 사용자 권고 (#218 노트에 가이드 첨부). 결과 도착 시 본 항목 갱신 + Redmine 노트 보강.
|
|
|
|
|
- [x] **AC-8**: opt-out 시 즉시 삭제 + meta clear + 토스트. F2 try/catch 적용. ✅
|
|
|
|
|
- [x] **AC-9**: RAM < 4GB / OOM / timeout 10s 시 빈 리스트 + 수동 입력 경로 보존. ✅
|
|
|
|
|
- [⊘] **AC-10**: 한국어 30 corpus ≥ 70% — **#221 로 분리** (out-of-scope).
|
|
|
|
|
|
|
|
|
|
## 4. 컨텍스트 & 제약
|
|
|
|
|
|
|
|
|
|
|