[Developer] #218 Real Gemma 4 E2B integration via flutter_gemma 0.16.5

Implements the OQ-1 follow-up to #215 v0.2.0: replace the placeholder
GemmaLlmService stub with a real flutter_gemma 0.16.5 backend driving
Gemma 4 E2B (litert-community/gemma-4-E2B-it-litert-lm, 2.41GB).

Highlights:
- GemmaLlmService.load → FlutterGemma.initialize + installModel.fromFile +
  getActiveModel; idempotent + FileSystemException on missing file.
- generateStructured uses Gemma 4 native function calling via
  createChat(tools: [Tool(...)], toolChoice: required). Stream parsed by
  collectFunctionCall — first FCR wins, ParallelFCR first-call wins,
  TextResponse/ThinkingResponse skipped, errors sanitized to prevent
  prompt leakage.
- main.dart wires _LazyLlmService adapter that resolves to GemmaLlmService
  when ModelLifecycle reports ready, MockLlmService otherwise.
- ai_providers.dart pins real model URL + SHA-256 (181938...39a63c).
- F2 hardening: ModelLifecycle.purge wraps each delete + meta remove in
  try/catch so a single OS-level flake cannot block opt-out.
- Android: INTERNET / FOREGROUND_SERVICE / POST_NOTIFICATIONS permissions
  + R8 proguard-rules.pro keeping MediaPipe / LiteRT / TFLite / protobuf
  JNI entry points (release builds otherwise crash on first inference).

Design-First: fn-gemma_llm_service.md updated to v2 — §C
(_appendSchemaInstruction) deprecated after reading flutter_gemma
0.16.5 source (Gemma 4 SDK injects tool declarations via template;
prompt-side append would double-wrap).

Tests:
- 10 new unit tests for collectFunctionCall covering all 8 fn-spec
  cases + 2 ParallelFunctionCallResponse paths.
- All 81 existing tests still pass.
- flutter analyze: 0 issues.

Refs #218

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 15:18:08 +09:00
parent a1f3c5f85d
commit 9a9eb2abd5
14 changed files with 646 additions and 175 deletions

View File

@@ -20,7 +20,7 @@ life-helper 는 사용자가 입력한 자유 문장(예: "술 끊고 싶어")
1. 하단 탭에서 **설정** 진입.
2. "AI 도움 켜기" 토글 탭.
3. 동의 다이얼로그가 뜹니다:
- **파일 크기 ≈ 1.5GB** (Gemma 4 E2B Q4_0 모델 — 단말에 한 번만 다운로드)
- **파일 크기 ≈ 2.4GB** (Gemma 4 E2B Q4_0 모델 — 단말에 한 번만 다운로드)
- **WiFi 연결 권장** (셀룰러 대역폭 절약)
- 모든 처리는 단말 — 입력 텍스트 외부 송출 없음
4. **"동의하고 다운로드"** 탭 → 백그라운드 다운로드 시작.
@@ -68,9 +68,9 @@ life-helper 는 사용자가 입력한 자유 문장(예: "술 끊고 싶어")
1. **설정** → "AI 도움 켜기" 토글 OFF.
2. 확인 다이얼로그:
- 모델 파일이 단말에서 **즉시 삭제** 됩니다.
-1.5GB 의 저장공간이 확보됩니다.
-2.4GB 의 저장공간이 확보됩니다.
- 다시 켜면 다시 다운로드해야 합니다.
3. **"끄고 삭제"** 탭 → "공간 확보됨 1500 MB" 토스트.
3. **"끄고 삭제"** 탭 → "공간 확보됨 2469 MB" 토스트.
진행 중인 다운로드가 있어도 깔끔히 중단되고, `.tmp` 임시 파일까지 함께 삭제됩니다.
@@ -86,7 +86,7 @@ A. 직접 입력란을 고쳐 쓰면 됩니다. AI 제안은 채우기 도우미
A. 언제든 끌 수 있고, 끄면 즉시 삭제됩니다. 다시 켜면 다시 받아야 한다는 점만 유의하세요.
**Q. v0.2.0 에서 다운로드가 항상 실패합니다.**
A. v0.2.0 은 모델 URL 이 미확정 (OQ-1) 인 상태로 출시되어, 실제 다운로드는 의도된 graceful 실패 경로로 안내됩니다. 실 모델 통합은 후속 버전 (v0.3.x) 에서 제공됩니다. 그동안 수동 입력 경로는 정상 동작합니다.
A. v0.2.0 은 모델 URL 이 미확정 (OQ-1) 인 상태로 출시되어, 실제 다운로드는 의도된 graceful 실패 경로로 안내됩니다. **v0.3.0 부터 실 Gemma 4 E2B 모델 (HuggingFace) 다운로드가 활성화되었습니다.** 그동안 수동 입력 경로는 정상 동작합니다.
## 관련 문서