diff --git a/app/lib/main.dart b/app/lib/main.dart index ac2c052..e3c38c2 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -40,13 +40,21 @@ class _LazyLlmService implements LlmService { LlmService? _delegate; Future _resolve() async { - if (_delegate != null) return _delegate!; final avail = await lifecycle.checkAvailability(); final path = await meta.find(AiMetaKeys.modelPath); - if (avail == ModelAvailability.ready && path != null) { - _delegate = GemmaLlmService(modelPath: path); - } else { - _delegate = MockLlmService(); + final wantGemma = avail == ModelAvailability.ready && path != null; + // Re-resolve every call so opt-in / opt-out state changes are reflected + // without an app restart. Repeat-resolve of the same kind reuses the + // cached instance (Gemma's flutter_gemma installModel is idempotent; + // Mock has no setup), but the kind itself flips when availability does. + final keep = _delegate != null && + (wantGemma == (_delegate is GemmaLlmService)) && + (!wantGemma || + (_delegate as GemmaLlmService).modelPath == path); + if (!keep) { + _delegate = wantGemma + ? GemmaLlmService(modelPath: path) + : MockLlmService(); } return _delegate!; } diff --git a/docs/reference/215-ai-frame-suggest.md b/docs/reference/215-ai-frame-suggest.md index 97edfd7..3830f78 100644 --- a/docs/reference/215-ai-frame-suggest.md +++ b/docs/reference/215-ai-frame-suggest.md @@ -181,13 +181,13 @@ abstract class LlmService { ## 8. 알려진 제약 -- **#218 AC-6 (디바이스 게이트)**: 저사양 단말 (RAM < 4GB) 에서는 SettingsScreen 의 AI 토글 disabled + "이 단말의 RAM 이 부족합니다 (필요: 4GB+)" 안내. `device_info_plus` 로 `AndroidDeviceInfo.systemFeatures` / `totalMem` 조회. iOS 는 #218 범위 밖. +- **#218 AC-6 (디바이스 게이트)**: 저사양 단말 (RAM < 4GB) 에서는 SettingsScreen 의 AI 토글 disabled + "이 단말에서는 AI 도움을 사용할 수 없어요 (RAM 4GB 이상 필요)" 안내. RAM 조회 = MethodChannel `life_helper/device_caps` → `MainActivity.kt` 에서 `ActivityManager.MemoryInfo.totalMem`. `device_info_plus` 도 deps 에 있지만 RAM 임계 (4GB) 측정엔 미사용 (해당 패키지는 `isLowRamDevice` ≈ 1GB 만 제공). iOS 는 #218 범위 밖. - **#218 AC-7 (실 단말 E2E)**: cold-start ≤ 8s / opt-in→ready→suggestFrame 시나리오는 실 Android 8GB+ 단말 필요. Host CI 는 검증 불가 → 별도 디바이스 랩 단계. -- **F1**: 설계서 §8.8 / §9 의 "60초 idle 시 `unload()`" 미구현. 운영 모니터링 후 #222 등으로 후속 — 단발 호출 후 즉시 unload 가 안전한 기본값. +- **F1**: 설계서 §8.8 / §9 의 "60초 idle 시 `unload()`" 미구현. #219 별도 이슈 — 단발 호출 후 즉시 unload 가 안전한 기본값. - **#221 AC10 corpus 품질 평가**: 30+ 한국어 라이브 입력 corpus 로 L2 ≥ 70% / L3 ≥ 50% 측정. 실 모델 통합 후 별도 이슈. ## 9. 다음 단계 / 확장 포인트 -- **#219 ProGuard rules 정제** + **#220 lifecycle observer 기반 unload** + **#221 corpus 평가** + **#222 멀티 모델 슬롯 (E2B + E4B)**: #215 follow-up 5 이슈 중 #218 다음 작업들. +- **#215 follow-up 4 이슈** (#218 다음): **#219** 60s idle auto-unload (F1), **#220** purge hardening (F2), **#221** AC10 한국어 corpus 품질 평가 (≥70%), **#222** production keystore / Play Store 준비. - 시나리오 #2~#6 (앵커 추출 / dose variants / if-then / lapse 구조화 / 주간 요약): 모두 `LlmService.generateStructured` 에 새 schema 추가하는 형태로 확장. 도메인 함수는 `lib/domain/ai/` 에 신규 파일. - 멀티 모델 슬롯 (E2B + E4B): ADR-0004 후보.