# AIFlow Math Ink 0.6 Android runtime 이 모듈은 0.6 모델의 Android ODA 공개 경계다. ML Kit Digital Ink 모델을 교체하는 파일이 아니라, AIFlow가 변환한 두 LiteRT flatbuffer를 최신 `CompiledModel` API로 직접 실행한다. ## 공개 API ```kotlin val session = CompiledModelLiteRtSession.create( context = context, onlineAsset = "aiflow_math_ink_06_online.tflite", rasterAsset = "aiflow_math_ink_06_raster.tflite", modelVersion = "aiflow-math-ink-0.6", exactLabelCount = labels.size, ) val recognizer = AIFlowMathInk(session, labels) val online: SymbolResult = recognizer.recognizeOnline(strokes, InkCanvas(width, height)) val raster: SymbolResult = recognizer.recognizeRaster(bitmap) val debug: RasterDebugResult = recognizer.recognizeRasterDebug( RasterInput(128, 128, normalizedInk), ) ``` `SymbolResult.toServerPayload()`에는 다음 값만 들어간다. - `candidates[{token, probability}]` - `confidence` - `modelVersion` - `latencyMs` 원본 touch event, canonical tap, raster, virtual stroke는 payload에 들어가지 않는다. `recognizeRasterDebug`는 같은 로컬 inference에서 top-4의 128×2 좌표, 128×3 pen-state logits, progress, hypothesis log-probability를 반환한다. `RasterDebugResult`에는 server payload 변환 함수가 없으며 일반 `SymbolResult.toServerPayload()`에도 이 값은 포함되지 않는다. ## 입력 계약 - 원본 touch event는 `AIFlowInkV2.rawStrokes`에 로컬 보존 - 관측 timestamp는 6Hz로 재표본화 - timestamp가 없으면 `TimestampMode.CANONICAL`, 19번 중 18번째 `missing_mask=1` - 시작·끝·pen-up anchor 보존 - 6Hz tap과 TCN의 128-step feature를 분리 - Python 학습 전처리의 대표 4행×19채널과 절대오차 `1e-4` 이내 parity ## 빌드 Android Studio JBR과 Android SDK 35가 필요하다. ```powershell gradle :aiflow-math-ink-runtime:testDebugUnitTest gradle :aiflow-math-ink-runtime:assembleRelease ``` 현재 LiteRT dependency는 `com.google.ai.edge.litert:litert:2.1.6`, Kotlin은 2.3.0이다. 실제 `.tflite`가 아직 생성되지 않았으므로 CompiledModel 실기기 inference·latency·memory·battery 검증은 미완료다. ## Android 3-tier benchmark `AndroidBenchmarkRunner`는 동일한 online/raster model SHA-256 쌍과 representative tensor로 저가·중급·고급 기기를 각각 측정한다. 두 hash는 `online:\nraster:\n`의 SHA-256인 `model_bundle_sha256`으로도 결합해 모델 순서까지 고정한다. - warm-up: online/raster 각 20회 - 측정: online/raster 각 100회 교차 실행 - gate: online p95 ≤50ms, raster p95 ≤200ms, process PSS ≤100MiB - 배터리: charge counter의 측정 가능 여부와 실행 전후 차이를 기록 - 결과: `AndroidBenchmarkReport.toMap()`을 앱 계층에서 UTF-8 JSON으로 저장 - raster graph output: exact logits, coordinates, state logits, progress, hypothesis scores의 정확히 5개 세 기기 JSON은 다음 명령으로 합친다. ```powershell python scripts/summarize_math_ink_06_android_benchmarks.py ` --report low.json --report mid.json --report high.json ` --output android-3tier-summary.json ``` 요약기는 `low`·`mid`·`high`가 정확히 하나씩 존재하고 세 report의 `model_version`, online/raster 개별 SHA-256, bundle SHA-256이 같은지 확인한다. 모든 metric이 세 기기에서 통과해야 `android_release_gate_passed=true`가 된다. 이 값만으로 제품 승인을 만들 수 없도록 `product_validation=false`는 고정한다. 최종 제품 manifest는 P writer/device-disjoint release, 배포 raster 모델에 귀속된 vectorization 지표, LiteRT parity/model bundle, Android 3-tier summary를 모두 결합한다. 공개 Hugging Face의 Colab ZIP은 `R_public_conversion_only`이며 P student가 들어 있지 않다. 실제 제품용 변환은 로컬에서 `build_math_ink_06_p_mobile_colab_bundle.py`로 private ZIP을 만든 뒤 본인 Colab 세션에만 올린다. 이 private manifest는 `contains_p_data=true`, `public_upload_allowed=false`, `server_upload_permitted=false`를 강제한다. ```powershell python scripts/build_math_ink_06_product_release_manifest.py ` --p-release release_report.json ` --raster-validation raster-validation.json ` --model-bundle mobile-model-bundle.json ` --android-summary android-3tier-summary.json ` --output product-release.json ``` 공식 참고: - https://developers.google.com/edge/litert/android - https://developers.google.com/edge/litert/next/android_kotlin