모바일 앱
8f5f2a0입니다.gallery는 Google AI Edge의 온디바이스 생성형 AI 기능을 체험하는 실험적 앱입니다. 모델을 선택하고 준비한 다음, 휴대전화에서 답변을 생성하거나 사진·영상의 의미를 검색합니다. 이 저장소에서 배울 핵심은 AI를 호출하는 한 줄이 아니라, 모델의 준비부터 사용과 정리까지 이어지는 앱의 전체 흐름입니다.
일반적인 클라우드 챗봇에서는 서버가 모델을 실행합니다. 여기서는 LlmChatModelHelper.initialize()가 기기의 모델 파일 경로로 EngineConfig를 만들고, LiteRT-LM의 Engine과 Conversation을 생성합니다. 모델 계산을 담당하는 네이티브 엔진은 외부 의존성이며, 저장소의 Kotlin 코드는 그 엔진에 화면·데이터·도구를 연결합니다.
모델은 요리사가 익힌 지식, LiteRT-LM은 조리 기계, Kotlin 앱은 주문표·재료 창고·진행 상황판입니다. 조리 기계가 좋아도 재료가 없거나 주문이 겹치면 음식이 제대로 나오지 않습니다.
그래서 모델 다운로드 실패, 대화 중단, 사진 입력 변환, 메모리 해제까지 읽어야 합니다. 그 연결부가 이 저장소의 공부거리입니다.
| 작업 | 입력과 출력 | 대표 구현 |
|---|---|---|
| 생성 | 질문·이미지·오디오를 받아 답변이나 함수 호출을 생성 | Android/src/app/src/main/java/com/google/ai/edge/gallery/ui/llmchat/, Android/src/app/src/main/java/com/google/ai/edge/gallery/agent/, Android/src/app/src/main/java/com/google/ai/edge/gallery/runtime/ |
| 검색 | 텍스트·미디어를 벡터로 바꾸고 비슷한 사진·영상 구간을 찾음 | Android/src/app/src/main/java/com/google/ai/edge/gallery/services/semanticretrieval/, Android/src/app/src/main/java/com/google/ai/edge/gallery/customtasks/videomomentfinder/ |
| 행동 | 모델의 함수 호출을 Android 동작이나 MCP 요청으로 연결 | Android/src/app/src/main/java/com/google/ai/edge/gallery/customtasks/mobileactions/, Android/src/app/src/main/java/com/google/ai/edge/gallery/tools/ |
Kotlin 구현은 Android/src/app/src/main/java/com/google/ai/edge/gallery/ 아래에 모여 있습니다. 파일 경로는 저장소 루트 기준입니다.
버튼을 눌렀을 때 어느 ViewModel이 요청을 받고, 어떤 실행기와 런타임을 거쳐, 어떤 이벤트로 화면이 바뀌는지 설명해 보세요. 모든 파일을 외우는 것보다 이 연결을 설명하는 편이 더 유용합니다.
2026-10-23 GitHub Trending Kotlin 월간 5위, 스타 25,076개라는 지표는 관심의 크기를 보여 줍니다. 다만 이 수치만으로 사람들이 어떤 기능 때문에 방문했는지는 알 수 없습니다. 아래 이유들은 README의 기능 구성과 실제 코드를 근거로 한 관심 요인에 대한 해석입니다.
README는 Gallery를 온디바이스 생성형 AI 체험 앱으로 소개합니다. LiteRT-LM 공식 개요와 앱의 Kotlin 구현을 함께 읽으면 런타임을 앱에 연결하는 방식을 배울 수 있습니다.
2026-10-23 GitHub 기준 최신 릴리스는 1.0.20 (2026-10-06)입니다. README는 EmbeddingGemma 2 지원과 Instant Media Search·Video Moment Finder를 새 기능으로 소개합니다. 채팅 밖의 온디바이스 활용을 보여 주는 구체적인 업데이트이며, 트렌딩 관심의 배경을 이해할 단서입니다.
gallery의 장점은 모든 모델에서 가장 빠르다는 증명보다, 모바일 AI 기능을 하나의 앱으로 묶은 통합 예제를 제공한다는 데 있습니다. 아래 표는 제품의 역할을 비교한 것이며, 동일 기기에서 측정한 속도 순위가 아닙니다.
| 비교 축 | gallery | 클라우드 챗봇형 앱 | llama.cpp 중심 개발 |
|---|---|---|---|
| 계산 위치 | 핵심 모델 추론은 기기 안 | 일반적으로 서비스 서버 | 로컬 실행 중심, 구성에 따라 서버 제공 |
| 출발점 | 완성된 모바일 기능과 화면 | 계정·API·서비스 기능 | 추론 엔진과 실행 도구 |
| 학습 강점 | Compose·모델 수명·미디어·도구 연결 | 요청·응답과 서비스 연동 | C/C++ 엔진·모델 포맷·실행 최적화 |
| 앱 기능 확장 | Android Intent, 스킬, MCP 연결 예제 | 서비스마다 지원 범위가 다름 | 필요한 앱 UI와 권한 처리를 별도로 설계 |
| 감수할 제약 | 기기 메모리·지원 모델·드라이버 차이 | 통신·서버 정책·서비스 비용 | 앱으로 완성하는 통합 작업 |
llama.cpp 공식 저장소는 C/C++ LLM 추론 프로젝트입니다. Gallery와 어느 쪽이 무조건 우월한지 묻기보다, 모바일 제품 연결부를 배우려는지, 엔진 자체를 배우려는지 먼저 정하는 편이 좋습니다.
| 문서 표현 | 소스에서 확인할 점 | 학습할 판단 |
|---|---|---|
| 완전 오프라인 | 추론 경로 외에 GitHub 모델 목록, Hugging Face 다운로드, 원격 MCP 요청이 있음 | 네트워크 사용 여부는 기능별로 판단 |
| 벤치마크 | 계측 클래스가 있지만 현재 MetricsTracker.create()에서 비활성화 | 구현 존재와 활성 상태를 구분 |
| 여러 플랫폼 | 앱 배포 안내는 있으나 이 커밋에는 Android 앱 소스만 있음 | 지원 제품 범위와 공개 소스 범위를 구분 |
| 도구용 문맥 제약 | mcp/README.md는 4k~10k를 언급하지만 1.0.20 목록의 Gemma 4 설명은 최대 32K | 모델별 설정·실제 엔진 예산을 따로 확인 |
식당 간판의 “포장 가능”이 모든 메뉴가 같은 포장 용기에 담긴다는 뜻은 아닙니다. “오프라인 AI”도 모델 계산, 파일 준비, 외부 도구를 각각 나눠 읽어야 정확합니다.
버전의 기본 출처는 Android/src/gradle/libs.versions.toml, 실제 적용 여부는 Android/src/app/build.gradle.kts입니다. 아래 값은 모두 커밋 8f5f2a0 기준입니다. 버전 카탈로그는 여러 의존성의 이름과 버전을 한곳에 모아 두는 장부입니다.
| 기술 | 선언 버전 | 이 앱에서 하는 일 |
|---|---|---|
| Kotlin | 2.2.21 | 화면·상태·실행기·도구 연결의 주 언어 |
| Compose | BOM 2026.02.00 | 상태를 받아 화면을 구성, Material 3 사용 |
| Activity | 1.10.1 | Compose 화면과 Android Activity 연결 |
| Lifecycle | 2.8.7 | ViewModel·화면 생명주기 관련 의존성 |
| Navigation | 2.8.9 | GalleryNavHost를 통한 화면 이동 |
| CameraX | 1.4.2 | 카메라 입력과 수명 관리 |
| Media3 | 1.11.1 | 영상 재생·변환·내보내기 연결 |
| Coil / WebKit | 2.7.0 / 1.14.0 | 이미지 표시 / 스킬의 WebView 연동 |
2026.02.00은 모든 Compose 라이브러리의 개별 버전이 아닙니다. 선언형 UI는 “이 버튼의 글자를 바꿔라”보다 “현재 상태라면 이런 화면이어야 한다”고 표현하는 방식입니다.이 저장소에는 별도 웹 API 서버나 모델 학습 서버 프로젝트가 없습니다. 서버 앱에서 백엔드가 맡을 계산·데이터 책임을 Android 프로세스 내부의 런타임과 저장소가 담당합니다. 원격 MCP 서버는 필요할 때 연결하는 외부 구성요소입니다.
| 기술 | 선언 버전 | 책임 |
|---|---|---|
| LiteRT-LM | 0.18.0 | litertlm-android의 엔진·대화·함수 호출 |
| ML Kit Prompt | 1.0.0-beta2 | AICore 기반 생성 경로의 SDK |
| MediaPipe | Vision 1.0.0, Retrieval 1.1.0 | 임베딩·의미 검색과 벡터 저장 연동 |
| WorkManager | 2.12.0 | 모델 파일의 백그라운드 다운로드 |
| DataStore | 1.1.7 | 설정·사용자 데이터·스킬 등 로컬 저장 |
| Protobuf | 런타임·protoc 4.26.1 | .proto 기반 데이터 직렬화 |
| Hilt | 2.60.1 | 실행기·저장소·다운로더 의존성 조립 |
| MCP / Ktor | 0.8.0 / 3.4.3 | 외부 도구 서버 연결과 HTTP 클라이언트 |
| AppAuth | 0.11.1 | Hugging Face OAuth 인증 흐름 |
| JSON 처리 | Gson 2.14.0, Moshi 1.15.2 | 모델 목록·도구 인자 등 구조화된 데이터 처리 |
| Firebase | BOM 33.16.0 | Analytics·Messaging 의존성, 구성 여부에 주의 |
kotlinx-serialization-json은 1.7.3, serialization 플러그인은 2.0.21로 선언되어 있습니다. Kotlin reflect와 일부 metadata 의존성은 2.4.0이며 -Xskip-metadata-version-check도 있습니다. 이 조합을 “전부 Kotlin 2.2.21”로 요약하면 중요한 차이가 사라집니다.
| 항목 | 소스의 설정 | 초보자가 구별할 점 |
|---|---|---|
| Android 빌드 | AGP 9.0.1, Gradle 9.2.1 | AGP는 Android용 플러그인, Gradle은 빌드 실행 도구 |
| SDK | minSdk=31, compile·target 37 | 설치 최소 OS와 컴파일·대상 API는 다른 값 |
| Java | CI Temurin 21, 코드·JVM 타깃 11 | 빌드 도구를 돌리는 JDK와 생성 코드 타깃은 다름 |
| 앱 식별 | versionName=1.0.20, versionCode=46 | 표시용 버전과 업데이트 판정용 정수 |
| 코드 생성 | KSP 2.3.6, Protobuf 플러그인 0.10.0 | Hilt·Moshi·데이터 클래스 관련 생성 단계 |
| 빌드 메모리 | org.gradle.jvmargs=-Xmx2048m | Gradle JVM 힙 상한이며 개발 PC 전체 권장 RAM은 아님 |
.github/workflows/build_android.yaml은 Android 변경 시 Ubuntu에서 SDK Platform 37을 준비하고 ./gradlew assembleRelease를 호출합니다. 별도 테스트 실행 단계나 APK 업로드 단계는 없습니다. JUnit·Espresso 의존성이 선언되어 있다는 사실만으로 CI가 테스트를 돌린다고 볼 수 없습니다.
.github/workflows/static.yml은 skills/를 staging/skills/에 담아 GitHub Pages로 배포합니다. 이것은 모델 추론 서버 배포가 아니라 스킬 정적 파일 배포입니다. package.json·Cargo.toml·go.mod·pyproject.toml·Dockerfile 중심의 프로젝트 구조는 확인되지 않습니다.
카탈로그에 적힌 이름을 찾고, 앱 빌드 파일의 implementation(...)과 플러그인 적용을 확인한 뒤, 소스의 import를 찾으세요. “선언되어 있다”에서 “어디에 쓰이는가”까지 가야 스택 지도가 완성됩니다.
다음은 Android 핵심 경로를 단순화한 구조도입니다. 스킬과 MCP는 Agent Chat에서 확장되는 경로이며, 모든 일반 채팅이 항상 외부 도구를 거치는 것은 아닙니다.
화면, 실행기, 런타임, 저장소를 구분하면 모델이나 기능이 늘어날 때 무엇을 바꿔야 하는지 설명할 수 있습니다. 각 계층을 완벽하게 독립시킨 교과서라고 보기보다, 실제 연결 방식과 예외를 배우는 사례로 읽어야 합니다.
Android/src/app/src/main/java/com/google/ai/edge/gallery/ui/llmchat/LlmChatScreen.kt의 LlmChatScreen()은 ChatViewWrapper에 ViewModel·콜백·입력 옵션을 전달합니다. Android/src/app/src/main/java/com/google/ai/edge/gallery/ui/common/chat/ChatViewModel.kt는 내부의 MutableStateFlow를 외부에 StateFlow로 노출하고, addMessage()에서 새 상태를 만듭니다.
// ChatViewModel.kt의 핵심 선언
private val _uiState = MutableStateFlow(createUiState())
val uiState: StateFlow<ChatUiState> = _uiState.asStateFlow()
// addMessage()는 메시지 목록과 맵을 복사한 뒤 상태를 갱신한다.
// 핵심 동작을 읽기 쉽게 요약한 표현
_uiState.update { state ->
state.copy(messagesByModel = newMessagesByModel)
}
읽는 쪽에는 상태 관찰 통로를, 바꾸는 쪽에는 갱신 함수를 둡니다. 다만 ChatUiState 내부에는 MutableList도 있으므로, 외부에서 바꿀 수 없는 Flow라고 해서 모든 중첩 데이터가 자동으로 불변인 것은 아닙니다. 복사해 갱신하는 실제 함수를 함께 읽으세요.
이 구조는 Compose 공식 아키텍처 문서의 “상태는 아래로, 이벤트는 위로” 흐름과 연결됩니다. 이 패턴의 이점은 화면에 다운로드·추론 세부 구현을 모두 넣지 않아도 된다는 것입니다.
Android/src/app/src/main/java/com/google/ai/edge/gallery/runtime/LlmModelHelper.kt는 initialize(), resetConversation(), runInference(), stopResponse(), cleanUp()라는 공통 계약을 정의합니다. Android/src/app/src/main/java/com/google/ai/edge/gallery/runtime/ModelHelperExt.kt의 확장 프로퍼티가 모델에 맞는 구현을 고릅니다.
// ModelHelperExt.kt의 선택 순서를 요약
val Model.runtimeHelper: LlmModelHelper
get() {
testingModelHelper?.let { return it }
if (this.isAiCore) return AICoreModelHelper
return LlmChatModelHelper
}
이는 같은 일을 서로 다른 방법으로 수행하는 전략 패턴으로 읽을 수 있습니다. 다운로드에도 Android/src/app/src/main/java/com/google/ai/edge/gallery/data/ModelDownloader.kt라는 계약이 있고, Android/src/app/src/main/java/com/google/ai/edge/gallery/di/AppModule.kt는 @IntoMap·@RuntimeTypeKey로 LiteRT-LM과 AICore 다운로더를 등록합니다.
테스트용 testingModelHelper가 먼저 선택된다는 점도 읽을 만합니다. 비싼 모델을 매번 로드하지 않고 가짜 구현으로 흐름을 확인할 수 있는 접점이지만, 전역 가변 상태이므로 동시에 사용하는 테스트 사이의 간섭도 고려해야 합니다.
Android/src/app/src/main/java/com/google/ai/edge/gallery/agent/DefaultAgentRuntimeExecutor.kt의 executeStream()은 callbackFlow를 반환합니다. 런타임이 보내는 응답 조각을 StreamToken으로, 종료를 LoopTerminated로 바꿉니다. 정의는 Android/src/app/src/main/java/com/google/ai/edge/gallery/agent/AgentEvent.kt의 sealed interface에 모여 있습니다.
// executeStream()의 결과 콜백을 단순화한 흐름
resultListener = { partialResult, done, partialThinking ->
emitEvent(AgentEvent.StreamToken(
token = partialResult, thinking = partialThinking, done = done
))
if (done) {
emitEvent(AgentEvent.LoopTerminated(finalResponse.toString()))
close()
}
}
실제 코드는 제어용 접두사를 거르고 응답을 누적하며, 오류·중단·문맥 압축 이벤트도 보냅니다. 이 덕분에 ViewModel은 엔진의 콜백 규칙 대신 앱이 이해하는 사건 목록을 처리합니다. 응답 조각 하나가 반드시 토큰 하나나 글자 하나인 것도 아닙니다.
취소 경로는 따로 확인해야 합니다. executeStream()의 awaitClose 블록 자체는 비어 있습니다. 명시적인 interrupt()는 세션 관리자의 stopResponse()를 거쳐 런타임 중단으로 이어집니다. Flow 수집 취소만 보고 네이티브 추론도 무조건 멈춘다고 단정하면 안 됩니다.
Android/src/app/src/main/java/com/google/ai/edge/gallery/data/DownloadRepository.kt의 DefaultDownloadRepository.downloadModel()은 URL·파일명·버전·부가 파일 정보를 WorkManager 입력으로 만듭니다. 파일 바이트를 읽는 일은 Android/src/app/src/main/java/com/google/ai/edge/gallery/worker/DownloadWorker.kt의 doWork()가 맡습니다.
// DownloadRepository.kt의 실제 작업 등록
workManager.enqueueUniqueWork(
model.name,
ExistingWorkPolicy.REPLACE,
downloadWorkRequest,
)
같은 모델 이름으로 다시 요청하면 기존 작업을 교체하는 정책입니다. Worker는 임시 파일을 사용하고, 재개 시 HTTP Range를 붙이며, 약 200ms 간격으로 진행률을 알립니다. 완료 뒤 임시 확장자를 없애고 필요하면 압축을 풉니다.
WorkManager 공식 문서는 앱이 화면 밖으로 나간 뒤에도 유지할 작업을 이 라이브러리의 사용 대상으로 설명합니다. 다만 라이브러리를 쓴다고 모든 네트워크 예외가 자동 해결되지는 않습니다. 이 Worker는 IOException에서 Result.failure()를 반환하며 자동 재시도 반환과는 다릅니다.
Android/src/app/src/main/java/com/google/ai/edge/gallery/ui/llmchat/LlmChatModelHelper.kt의 LlmModelInstance는 Engine과 Conversation을 따로 보관합니다. resetConversation()은 기존 대화를 닫고 같은 엔진에서 새 대화를 만들고, cleanUp()은 모델 자원 정리를 담당합니다.
엔진은 무거운 조리 기계, 대화는 그 기계로 처리하는 주문 한 묶음입니다. 주문 내역을 비운다고 기계를 매번 새로 설치할 필요는 없습니다. 반대로 퇴근할 때는 주문표만 버릴 것이 아니라 기계 전원도 꺼야 합니다.
이 분리는 앱의 반응 속도와 메모리에 영향을 줍니다. 새 대화가 필요할 때 엔진을 재사용하는 경로와, 모델 자체를 바꿔 자원을 회수하는 경로를 각각 따라가면 “초기화”와 “자원 해제”를 구체적으로 이해할 수 있습니다.
model_allowlists/1_0_20.json의 Gemma 4는 maxContextLength=32000, maxTokens=4000을 선언합니다. Android/src/app/src/main/java/com/google/ai/edge/gallery/data/ModelAllowlist.kt가 이를 설정 생성 함수로 넘기고, Android/src/app/src/main/java/com/google/ai/edge/gallery/data/Config.kt의 createLlmChatConfigs()는 최대 문맥 값이 있으면 2,000부터 그 값까지 조절하는 슬라이더를 만듭니다. 이 목록의 Gemma 4 두 항목은 기본값이 4,000입니다.
// LlmChatModelHelper.initialize()에서 엔진 예산으로 이어지는 값
val maxTokens = model.getIntConfigValue(
key = ConfigKeys.MAX_TOKENS,
defaultValue = DEFAULT_MAX_TOKEN,
)
// 아래는 EngineConfig의 관련 인자만 발췌한 표현
EngineConfig(
modelPath = modelPath,
backend = preferredBackend,
maxNumTokens = maxTokens,
)
따라서 모델 설명의 “최대 32K”를 모든 실행이 처음부터 32K를 사용한다는 뜻으로 읽으면 안 됩니다. 이름이 maxTokens라고 해서 이 경로에서 순수 출력 길이만 나타낸다고 해석하는 것도 부정확합니다. 실제로 어떤 엔진 인자에 연결되는지 확인해야 합니다.
Android/src/app/src/main/java/com/google/ai/edge/gallery/agent/sessions/SummarizationContextCompactor.kt는 같은 설정값의 75%를 넘는 문맥에서 압축을 검토합니다. 요약을 생성한 뒤 대화를 다시 만들며, 자동 실행 여부는 기능 플래그·작업별 설정 등에도 달려 있습니다. 문맥이 길어졌다고 언제나 자동 요약되는 것은 아닙니다.
저장소 루트에는 Android 앱, 버전별 모델 목록, 스킬 예제, 사용 문서가 있습니다. iOS용 모델 목록 파일은 있지만 Swift 앱 프로젝트는 확인되지 않습니다. README의 앱 배포 안내를 그대로 소스 디렉토리 목록으로 옮기지 않는 것이 중요합니다.
| 폴더 | 찾아볼 질문 | 첫 파일 |
|---|---|---|
ui | 입력과 화면 상태는 어떻게 이어지는가? | Android/src/app/src/main/java/com/google/ai/edge/gallery/ui/llmchat/LlmChatViewModel.kt |
agent | 한 번의 실행과 여러 대화 세션을 누가 관리하는가? | DefaultAgentRuntimeExecutor.kt |
runtime | 모델 종류가 달라도 무엇을 공통으로 호출하는가? | LlmModelHelper.kt |
data | 설정·모델 정보·다운로드 정책을 어디에 모으는가? | BackendSpec.kt, DownloadRepository.kt |
services | 미디어를 검색 가능한 형태로 어떻게 바꾸는가? | Android/src/app/src/main/java/com/google/ai/edge/gallery/services/semanticretrieval/SemanticRetrievalService.kt |
customtasks | 기존 기반 위에 새 기능을 어떻게 추가하는가? | Android/src/app/src/main/java/com/google/ai/edge/gallery/customtasks/examplecustomtask/ExampleCustomTask.kt |
tools | 모델이 제안한 행동은 어디서 실행되는가? | RunMcpTool.kt, LoadSkillTool.kt |
아래는 .git을 제외한 파일의 물리적 줄 수입니다. 빈 줄·주석·라이선스 문구도 포함하며, 실행문 개수나 개발 난이도를 뜻하지 않습니다. HTML 안에 포함된 JavaScript는 HTML 줄 수에 들어갑니다.
| 종류 | 파일 수 | 물리적 줄 수 |
|---|---|---|
Kotlin .kt | 326 | 85,786 |
Gradle Kotlin .kts | 3 | 230 |
| XML | 38 | 5,976 |
| HTML | 27 | 4,203 |
| JavaScript / CSS | 5 / 1 | 230 / 16 |
| Protobuf | 12 | 844 |
| JSON | 20 | 3,104 |
| Markdown | 38 | 1,964 |
| YAML | 3 | 132 |
Git 관리 디렉토리를 제외한 전체 파일은 635개입니다. Kotlin 구현은 Android 앱의 주 소스 경로에 모여 있습니다. 별도 Android/src/app/src/test/·Android/src/app/src/androidTest/ 디렉토리는 이 커밋에서 확인되지 않습니다.
처음에는 GalleryApp.kt → LlmChatViewModel.kt → DefaultAgentRuntimeExecutor.kt → LlmChatModelHelper.kt 순서로 읽으세요. 저장소 전체를 훑기보다 한 기능의 세로 경로를 먼저 완성하는 방법입니다.
Android/src/app/src/main/java/com/google/ai/edge/gallery/data/ModelDownloadStatus.kt는 다운로드 상태를 NOT_DOWNLOADED, PARTIALLY_DOWNLOADED, IN_PROGRESS, UNZIPPING, SUCCEEDED, FAILED, UNAVAILABLE로 나눕니다. 완료 여부를 Boolean 하나로 관리하면 표현하기 어려운 상황들입니다.
// ModelDownloadStatus.kt의 데이터 구조 일부
data class ModelDownloadStatus(
val status: ModelDownloadStatusType,
val totalBytes: Long = 0,
val receivedBytes: Long = 0,
val errorMessage: String = "",
// 나머지 속성 생략
)
배울 것: enum은 정해진 선택지를, sealed interface는 이벤트마다 다른 데이터를 표현합니다. 작은 실습: “파일은 받았지만 압축 해제 중”과 “이 기기에서 모델 사용 불가”의 화면 문구를 각각 써 보세요. 타입이 UI 문구와 버튼 상태를 설계하는 도구라는 점이 보입니다.
LlmChatViewModelBase.generateResponse()는 코루틴에서 실행기를 사용하고, DefaultLlmSessionManager는 세션별 Mutex로 세션 변경과 문맥 압축을 조율합니다. SemanticRetrievalService.retrieveEntitiesInternal()은 I/O 디스패처로 검색을 옮기고, CancellationException을 별도로 다시 던집니다.
실습 아이디어: 초기화 중 화면 이탈, 생성 중 중지, 세션 복원을 연속으로 누르는 상황을 종이에 그려 보세요. 각 상황에서 UI 작업, Flow 수집, 네이티브 엔진 중 무엇이 멈춰야 하는지 적습니다. 취소 예외를 일반 오류로 바꾸지 않는 이유도 함께 설명해 보세요.
Android/src/app/src/main/java/com/google/ai/edge/gallery/services/semanticretrieval/OnDeviceEmbedder.kt의 인터페이스는 텍스트·이미지 등을 FloatArray로 바꿉니다. UniversalOnDeviceEmbedder는 MediaPipe UniversalEmbedder를 감싸고, 모델의 실제 입력 지원 여부와 문맥·영상 토큰 한도를 ModelInfo에서 읽습니다.
SemanticRetrievalService는 SqliteVectorStore와 SemanticRetriever를 연결합니다. GemmaEmbeddingModelStore.DEFAULT_EMBEDDING_DIMENSION은 768입니다. 초기화 실패나 Context 부재 때는 메모리 저장소로 대체하는 경로가 있으므로, 모든 실행 환경에서 영속 저장이 보장된다고 보면 안 됩니다.
답변을 만들어 내는 모델과 관련 자료를 찾아주는 모델을 구분하면, 모든 AI 기능에 대형 채팅 모델이 필요하지 않다는 점을 이해할 수 있습니다.
영상 예제: Android/src/app/src/main/java/com/google/ai/edge/gallery/customtasks/videomomentfinder/VideoMomentFinderViewModel.kt는 기본 분석 구간 2초, 구간당 프레임 2개, 구간 겹침 0초, 기본 검색 결과 5개를 선언합니다. searchForMoment()는 parent_id 메타데이터 필터로 선택한 영상의 레코드만 검색합니다.
이 필터가 검색 전에 적용되는 것이 중요합니다. 전체 영상에서 상위 5개를 고른 뒤 현재 영상만 남기면, 현재 영상의 좋은 후보가 처음부터 빠질 수 있습니다. 실습 아이디어: 영상 두 편의 가상 점수를 만들어 “필터 후 상위 K개”와 “상위 K개 후 필터”를 비교해 보세요.
Android/src/app/src/main/java/com/google/ai/edge/gallery/customtasks/mobileactions/MobileActionsTools.kt의 turnOnFlashlight()는 직접 카메라 API를 실행하지 않습니다. FlashlightOnAction을 콜백으로 넘기고, MobileActionsViewModel.performAction()이 구체 동작으로 연결합니다.
// MobileActionsTools.kt의 핵심 동작
@Tool(description = "Turns the flashlight on")
fun turnOnFlashlight(): Map<String, String> {
onFunctionCalled(FlashlightOnAction())
return mapOf("result" to "success")
}
코드의 영문 설명은 모델에게 제공하는 원래 메타데이터입니다. 이 함수의 success 반환만으로 실제 손전등 켜기가 성공했다고 판정할 수는 없습니다. 도구 계층의 요청 접수와 운영체제 동작의 성공을 따로 추적하는 설계가 왜 필요한지 배울 수 있습니다.
Android/src/app/src/main/java/com/google/ai/edge/gallery/tools/RunMcpTool.kt의 runMcpTool()은 서버 도구별 alwaysAllow를 확인합니다. 매번 허용이 필요한 도구라면 AskMcpToolCallPermissionAction을 보내고 결과를 기다린 뒤 client.callTool()을 호출합니다. 모델이 함수를 선택했다는 사실이 사용자 권한을 대신하지 않습니다.
skills/README.md는 스킬의 이름·설명으로 후보를 고르고 자세한 지침을 나중에 불러오는 흐름을 설명합니다. 실제 Android/src/app/src/main/java/com/google/ai/edge/gallery/tools/LoadSkillTool.kt의 loadSkill()은 SkillsProvider.loadSkill()과 getSkillContent()로 지침을 가져옵니다.
이 방식은 모든 지침을 처음부터 넣는 비용을 줄입니다. 스킬은 모델 가중치를 바꾸지 않으며, 지침·도구·자원을 묶어 기존 모델이 할 일을 안내합니다. JavaScript 스킬은 숨겨진 WebView의 ai_edge_gallery_get_result 진입점을 사용하고, 네이티브 기능은 Intent와 연결됩니다.
실습 아이디어: 기존 스킬의 이름·설명만 보고 어떤 질문에 선택될지 예상한 뒤, 본문의 지침과 비교해 보세요. 너무 넓은 설명 두 개가 있으면 모델이 왜 혼동할 수 있는지도 적어 봅니다. 외부 API를 사용하는 스킬은 별도 통신 기능입니다.
Android/src/app/src/main/java/com/google/ai/edge/gallery/di/AppModule.kt는 settings.pb, user_data.pb, skills.pb, benchmark_results.pb 등의 DataStore를 만듭니다. 스키마는 Android/src/app/src/main/proto/에 있고, 대화 저장은 Android/src/app/src/main/java/com/google/ai/edge/gallery/data/ChatSessionRepository.kt와 세션 관리자를 따라가면 됩니다.
DefaultLlmSessionManager.saveSessionHistory()는 세션 ID, 시각, 원래 모델, 작업 ID, 메시지를 묶습니다. 대화 기록을 디스크에 저장하는 것과 엔진 내부의 KV 캐시를 유지하는 것은 다른 일입니다. 앱을 다시 열 때 기록을 읽어 런타임 대화를 복원하는 연결이 필요합니다.
대화 기록은 회의록이고 KV 캐시는 회의 중 사람들의 작업 메모입니다. 회의록 파일을 보관했다고 다음 회의 때 모두가 같은 작업 메모를 그대로 갖고 있는 것은 아닙니다. 복원·요약·재입력이라는 별도 과정이 필요합니다.
Android/src/app/src/main/java/com/google/ai/edge/gallery/common/metrics/MetricsTracker.kt에는 첫 응답 시간, 생성 속도, 메모리·전력 등을 모으는 인터페이스와 구현이 있습니다. 그러나 create()의 지역 변수 enableInferenceMetrics는 false여서 현재 경로는 NoOpMetricsTracker()를 반환합니다.
// MetricsTracker.create()의 현재 분기
val enableInferenceMetrics = false
if (!enableInferenceMetrics) {
return NoOpMetricsTracker()
}
또한 LlmChatModelHelper.initialize()의 enableBenchmark도 false입니다. README의 벤치마크 소개만으로 이 커밋의 모든 계측이 켜져 있다고 설명하면 안 됩니다. 실습 아이디어: 첫 응답 시간과 이후 생성 속도가 각각 어떤 사용자 경험을 설명하는지, 수치 없이 측정 계획부터 작성해 보세요.
Android/src/app/build.gradle.kts의 minSdk=31은 Android 12 이상을 뜻합니다. README 기준으로 iOS는 17 이상이며, iOS 앱 빌드 프로젝트는 이 저장소에 포함되어 있지 않습니다.
Android 최소 버전을 만족해도, 선택한 모델의 메모리와 가속기 요구를 만족한다는 뜻은 아닙니다. 모델 파일 크기는 디스크 사용량이고, 실행 중 메모리에는 가중치·캐시·입력 처리·앱 화면 비용이 함께 들어갑니다.
아래는 model_allowlists/1_0_20.json의 일부입니다. 파일 크기는 sizeInBytes를 십진 GB·MB로 반올림했습니다. 메모리는 목록의 minDeviceMemoryInGb 값이며 실측 최대 사용량이나 모든 기기에서의 성공 보장이 아닙니다.
| 모델 | 파일 크기 | 목록의 메모리 기준 | 주요 용도 |
|---|---|---|---|
| Gemma3-1B-IT | 약 584MB | 6GB | 텍스트 채팅·Prompt Lab |
| Qwen2.5-1.5B | 약 1.60GB | 6GB | 텍스트 채팅 |
| Gemma-4-E2B | 약 2.59GB | 8GB | 텍스트·이미지·오디오·Agent Chat |
| Gemma-4-E4B | 약 3.66GB | 12GB | 멀티모달·도구 사용 |
| EmbeddingGemma-2 | 약 485MB | 해당 항목에 명시 없음 | 사진·영상 의미 검색 |
| MobileActions-270M | 약 289MB | 6GB | 기기 행동 함수 호출 |
EmbeddingGemma-2 항목에 메모리 수치가 없다는 것은 무제한 지원이라는 뜻이 아닙니다. 사진 샘플 부가 파일만 약 15.3MB이며, 사용자 영상 복사본·썸네일·벡터 DB·내보낸 클립은 모델 파일과 별도로 저장 공간을 사용합니다.
Android/src/app/src/main/java/com/google/ai/edge/gallery/ui/common/MemoryWarning.kt의 isMemoryLow()는 이 기준과 기기 총 메모리를 비교하고, API 34 이상에서는 advertisedMem을 사용합니다. 경고 창에는 계속 진행하는 선택지도 있습니다. 이 검사는 현재 남은 여유 메모리를 실시간으로 보장하는 검사나 강제 실행 차단과는 다릅니다.
Android/src/app/src/main/java/com/google/ai/edge/gallery/data/BackendSpec.kt는 가속기 목록의 첫 항목을 기본값으로 봅니다. 실제 LiteRT-LM 초기화에서는 주 계산·비전·오디오 백엔드를 따로 정하며, 오디오는 해당 경로에서 Backend.CPU()를 사용합니다. “GPU 모델”이라고 모든 입력 처리가 GPU로만 실행되는 것은 아닙니다.
Android/src/app/src/main/java/com/google/ai/edge/gallery/common/HardwareUtils.kt에는 isOldMaliGpu(), isTensorG3OrHigher()처럼 기기 문자열과 코드네임을 판별하는 함수가 있습니다. 모델·SoC·드라이버 조합을 다뤄야 한다는 흔적이지, 이 함수만으로 모든 성능이나 호환성이 증명되는 것은 아닙니다.
AndroidManifest.xml의 NPU와 카메라 기능은 required=false입니다. 반면 AICore 경로는 시스템 서비스와 지원 기기 조건을 별도로 검사합니다. 파일 기반 모델의 지원 범위와 AICore 지원 범위를 하나로 합치지 마세요.
DEVELOPMENT.md는 Hugging Face 개발 앱 설정을 안내합니다. Android/src/app/src/main/java/com/google/ai/edge/gallery/common/ProjectConfig.kt의 client ID·redirect URI와 Gradle의 redirect scheme이 아직 자리표시자입니다.6GB·8GB·12GB는 모델 목록의 개별 기준입니다. 전체 앱에 공통인 보장 사양이나 속도 측정치가 아닙니다. 대화 길이, 이미지·오디오 입력, 다른 앱의 점유, 발열에 따라 사용 경험은 달라집니다.
처음 두 과제는 소스를 읽기만 해도 진행할 수 있습니다. 구현 과제는 별도 학습용 사본에서 진행하고, 모델·기기 준비가 필요한 검증과 순수 로직 검증을 구분하세요. 각 과제는 정답 코드보다 설계 판단을 설명하는 데 목적이 있습니다.
대상: Android/src/app/src/main/java/com/google/ai/edge/gallery/ui/modelmanager/ModelManagerViewModel.kt의 loadModelAllowlist(), model_allowlists/1_0_20.json.
BuildConfig.VERSION_NAME이 1.0.20에서 1_0_20으로 변환되는 줄을 찾습니다.완료 조건: 왜 루트 model_allowlist.json을 현재 모델 목록으로 읽으면 안 되는지 설명하고, 첫 실행에 목록과 캐시가 모두 없을 때의 실패 경로를 표시합니다.
대상: LlmChatViewModel.kt, DefaultAgentRuntimeExecutor.kt, AgentEvent.kt, LlmChatModelHelper.kt.
완료 조건: done=true와 성공 여부를 무조건 같은 뜻으로 취급하지 않고, 취소가 결과 콜백을 통해 종료되는 경로도 확인합니다.
대상: Android/src/app/src/main/java/com/google/ai/edge/gallery/customtasks/mobileactions/Actions.kt, MobileActionsTools.kt, MobileActionsViewModel.kt.
@Tool 함수, performAction()의 처리 책임을 나눕니다.완료 조건: 모델 없이 Action을 전달해도 화면 처리 로직을 확인할 수 있고, 도구 요청 접수와 실제 처리 완료를 각각 설명합니다. Function_Calling_Guide.md의 예제 이름은 실제 소스의 ToolSet 등과 대조합니다.
대상: VideoMomentFinderViewModel.searchForMoment(), SemanticRetrievalService.retrieveEntities().
parent_id로 먼저 거르는 경우를 비교합니다.완료 조건: 결과 0개일 때 임베딩을 불필요하게 두 번 계산하지 않는 이유를 설명하고, 영상 A 검색에 B의 구간이 섞이지 않는 검증 조건을 제시합니다.
대상: Android/src/app/src/main/java/com/google/ai/edge/gallery/worker/DownloadWorker.kt의 doWork(). 현재 코드는 일부 파일이 있으면 Range를 요청하고, HTTP 200과 206을 모두 허용하며 출력 스트림을 append 모드로 엽니다.
완료 조건: 정상 재개와 전체 재다운로드의 최종 바이트가 각각 원본과 같아야 합니다. 파일 길이 검증, 체크섬 검증, 성공 처리 시점을 분리해 설명하면 더 좋습니다.
실습의 완료 기준을 눈에 보이는 결과와 실패 조건으로 적으면, 코드를 따라 치는 공부가 설계를 검증하는 공부로 바뀝니다.
주당 4~6시간을 가정한 제안입니다. Kotlin 문법이 낯설면 첫 주를 두 주로 늘려도 됩니다. 매주 “무엇을 읽었다”보다 무엇을 설명하거나 검증할 수 있게 되었는지를 결과물로 남기세요.
| 주차 | 학습 주제·출발 파일 | 직접 만들 결과물 |
|---|---|---|
| 1주차 | Kotlin의 data class, enum, sealed 타입, 확장 프로퍼티.ModelDownloadStatus.kt, AgentEvent.kt, ModelHelperExt.kt. |
모델 다운로드·초기화·추론의 상태표. 각 상태의 진입 원인과 다음 상태를 한 줄씩 작성. |
| 2주차 | Compose, ViewModel, StateFlow, 단방향 흐름.GalleryApp.kt, LlmChatScreen.kt, ChatViewModel.kt. |
가짜 응답을 표시하는 작은 화면 설계. 상태 변경 책임이 어디에 있는지 설명. |
| 3주차 | 코루틴, 콜백 변환, 취소, 세션 동시성.DefaultAgentRuntimeExecutor.kt, DefaultLlmSessionManager.kt. |
정상 종료·오류·사용자 취소의 순서도. 취소가 네이티브 엔진에 전달되는 경로 표시. |
| 4주차 | 모델 파일, 가속기, 대화 수명, 자원 회수.LlmChatModelHelper.kt, BackendSpec.kt, DownloadWorker.kt. |
엔진 재사용과 대화 초기화의 비교표. 다운로드 재개에 대한 가짜 서버 응답 검증 계획. |
| 5주차 | 임베딩, 유사도, 벡터 저장, 미디어 창 분할.OnDeviceEmbedder.kt, SemanticRetrievalService.kt. |
영상 A·B의 메타데이터 검색 예제. 필터 위치와 상위 K개 선택의 차이 설명. |
| 6주차 | 도구 스키마, 스킬 지침, 권한, 문맥 예산.RunMcpTool.kt, LoadSkillTool.kt, SummarizationContextCompactor.kt. |
부작용 없는 도구 하나의 설계서. 입력 검증·권한·실패 결과·문맥 비용을 명시. |
기능 하나의 입력·상태·실패·자원 회수까지 설명하는 경험이, 라이브러리 이름을 많이 아는 것보다 오래 남는 학습이 됩니다.
| 관심 분야 | 더 배울 기술 | gallery에서 이어지는 질문 |
|---|---|---|
| Android 앱 | 화면 생명주기, 저장소 경계, 접근성, 프로파일링 | 긴 작업 중 화면 이탈·복귀 시 상태를 어떻게 복구할까? |
| 추론 엔진 | 양자화, KV 캐시, 연산 그래프, 가속기 백엔드 | 왜 파일 크기와 실행 메모리·속도가 비례하지 않을까? |
| 검색·RAG | 임베딩 평가, 필터, 색인 버전 관리, 검색 품질 | 모델을 바꾸면 기존 사진 벡터를 그대로 써도 될까? |
| 에이전트 | 도구 스키마, 권한, 문맥 압축, 결과 검증 | 모델의 함수 선택을 어떻게 검증 가능한 동작으로 바꿀까? |
화면 전체를 그대로 가져오기 전에 런타임 계약, 이벤트 타입, 다운로드 정책, 저장 스키마 중 필요한 것을 골라 보세요. 각 부품이 기대하는 Context·디스패처·권한·외부 서비스를 적으면 숨은 의존성까지 드러납니다.
| 용어 | 쉬운 설명 |
|---|---|
| LLM | 문맥을 보고 다음 토큰을 예측하며 문장을 만드는 대규모 언어 모델. 이 앱에서는 대화와 함수 호출의 판단에 쓰입니다. |
| 가중치 | 학습 결과가 담긴 숫자들. 내려받는 모델 파일의 핵심 내용이며 앱 소스와 별도로 배포됩니다. |
| 추론 / 학습 | 배운 모델로 결과를 만드는 일 / 모델의 숫자를 조정하는 일. Gallery의 주 역할은 추론 통합입니다. |
| LiteRT-LM | LiteRT 기반 LLM 실행을 연결하는 계층. 앱은 엔진·대화 API를 통해 모델을 사용합니다. |
| AICore | Android 시스템이 관리하는 모델 기능을 이용하는 경로. 앱이 파일을 직접 관리하는 경로와 준비·삭제 정책이 다릅니다. |
| 양자화 | 모델 숫자를 더 적은 비트로 표현해 크기와 계산 비용을 줄이는 방법. 정확도와 실행 특성의 변화도 함께 살펴야 합니다. |
| 토큰 | 모델이 처리하는 텍스트 등의 단위. 한국어 한 글자나 단어 하나와 항상 같지는 않습니다. |
| 문맥 창 | 모델이 한 번에 참고할 수 있는 정보의 범위. 질문뿐 아니라 이전 대화·도구 설명·미디어 입력도 예산을 사용합니다. |
| KV 캐시 | 이전 문맥 계산의 중간 결과를 보관해 다음 생성을 돕는 메모리. 대화 기록 파일과는 다릅니다. |
| 문맥 압축 | 긴 대화를 요약해 더 짧은 문맥으로 다시 시작하는 방식. 중요한 세부 사항이 빠질 가능성도 있습니다. |
| 멀티모달 | 텍스트 외에 이미지·오디오 같은 여러 형태의 입력을 다루는 것. 각 모델의 실제 지원 여부를 확인해야 합니다. |
| 임베딩 | 내용을 의미 비교에 사용할 숫자 배열로 바꾸는 것. 문장·사진을 같은 검색 공간에서 비교하는 데 활용합니다. |
| 벡터 저장소 | 벡터와 ID·부가 정보를 보관하고 비슷한 벡터를 찾는 저장 계층. 이 코드에는 SQLite·메모리 구현 경로가 있습니다. |
| 코사인 유사도 | 두 벡터의 방향이 얼마나 비슷한지 비교하는 값. 코드의 직접 검색 대체 경로에서 점수 계산에 쓰입니다. |
| 상위 K개 | 점수가 높은 결과 K개만 고르는 방식. K를 늘려도 결과가 모두 관련 있는 것은 아닙니다. |
| 메타데이터 | 본문 외의 부가 정보. 영상 ID·시작 시각·끝 시각은 검색 범위를 정하고 결과를 재생하는 데 필요합니다. |
| RAG | 관련 자료를 검색한 뒤 생성 모델이 그 자료를 참고하게 하는 구성. 벡터 검색만 존재한다고 전체 RAG가 완성되는 것은 아닙니다. |
| Compose | 현재 상태를 바탕으로 Android 화면을 기술하는 UI 도구. 상태 변화가 필요한 화면 갱신으로 이어집니다. |
| ViewModel | 화면이 사용할 상태와 동작을 보관하는 계층. 엔진 API를 화면 요소마다 직접 호출하지 않도록 연결을 맡습니다. |
| StateFlow / Flow | 최신 상태를 관찰하는 통로 / 시간에 따라 전달되는 값의 흐름. 상태와 사건의 성격을 구분해 선택합니다. |
| callbackFlow | 콜백으로 들어오는 값을 Flow 형태로 연결하는 도구. 외부 작업 중단까지 자동으로 해결해 주는 것은 아닙니다. |
| Hilt | 필요한 객체와 그 의존성을 조립해 전달하는 Android 도구. 이 앱에서는 저장소·실행기 등을 연결합니다. |
| Repository | 데이터 접근·저장 정책을 모아 사용하는 쪽에 일관된 통로를 제공하는 구성요소. |
| WorkManager | 화면 밖에서도 유지할 백그라운드 작업을 예약·관찰하는 도구. 다운로드의 HTTP 세부 처리는 Worker 코드가 맡습니다. |
| Proto DataStore | 미리 정의한 Protobuf 구조로 로컬 데이터를 저장하는 방식. 서버 DB와 별개입니다. |
| 함수 호출 | 모델이 도구 이름과 인자를 생성하고 앱이 실제 함수를 실행하는 방식. 모델이 직접 OS 권한을 얻는 것은 아닙니다. |
| Android Intent | 화면 열기·메일 작성 등 운영체제와 다른 앱에 작업을 요청하는 메시지. 요청과 최종 동작 완료는 구별합니다. |
| MCP | AI 앱이 외부 도구·데이터 서버와 통신하기 위한 규약. Gallery는 도구 서버에 연결하는 클라이언트 역할을 합니다. |
| 스킬 | 기능의 이름·설명·상세 지침·자원을 묶은 모듈. 모델 학습 없이 작업 방식을 안내합니다. |
| OAuth | 외부 서비스의 접근 권한을 위임받는 인증·인가 흐름. Hugging Face 로그인 설정에 사용합니다. |
| TTFT | 요청 후 첫 응답 토큰이 나올 때까지 걸린 시간. 첫 반응의 체감 속도와 관련됩니다. |
| Prefill / Decode | 입력 문맥을 먼저 처리하는 단계 / 이후 출력 토큰을 생성하는 단계. 서로 다른 병목이 생길 수 있습니다. |
| BOM / AGP | 라이브러리 버전 묶음의 기준표 / Android 빌드를 위한 Gradle 플러그인. 앱 자체 버전과 구분합니다. |
| HTTP Range | 파일의 특정 바이트 구간을 요청하는 방식. 서버 응답이 실제로 그 구간인지 확인해야 안전하게 이어받을 수 있습니다. |
용어를 외운 뒤에는 다시 함수로 돌아오세요. 예를 들어 “문맥 압축”을 이해했다면 SummarizationContextCompactor.compactContextIfNeeded()에서 현재 토큰 수 확인, 요약 생성, 대화 재설정, 실패 후 재시도 간격 처리를 차례로 찾아보는 식입니다.
아래 링크는 분석 기준 커밋에 고정되어 있습니다. 기본 브랜치의 같은 경로는 이후 구현이 달라질 수 있습니다.
| 확인할 내용 | 파일·핵심 함수 |
|---|---|
| 실제 버전 | libs.versions.toml, app/build.gradle.kts. |
| 화면 상태 | ChatViewModel.kt의 addMessage()·uiState. |
| 실행 이벤트 | DefaultAgentRuntimeExecutor.kt의 executeStream()·interrupt(). |
| 추론 수명 | LlmChatModelHelper.kt의 initialize()·runInference()·cleanUp(). |
| 다운로드 | DownloadWorker.kt의 doWork() — 임시 파일·Range·진행률 알림. |
| 의미 검색 | SemanticRetrievalService.kt의 retrieveEntitiesInternal()·메타데이터 변환. |
| 도구 권한 | RunMcpTool.kt의 runMcpTool() — 허용 확인 후 원격 호출. |
| 메모리 기준 | 1_0_20.json의 sizeInBytes·minDeviceMemoryInGb. |
| 계측 활성 상태 | MetricsTracker.kt의 create() — 현재 NoOpMetricsTracker 반환. |
| 항목 | 상태 | 해석·기준 |
|---|---|---|
| 선정 순위 | Kotlin 월간 5위 | 2026-10-23 GitHub Trending 기준 |
| 스타 / 포크 | 25,076 / 2,766 | 2026-10-23 GitHub 기준, 이후 변동 가능 |
| 생성일 | 2025-03-31 | GitHub 저장소 생성일 |
| 최근 push | 2026-10-23 | 저장소 메타데이터의 갱신 시각 |
| 최신 릴리스 | 1.0.20 · 2026-10-06 | 분석일 GitHub 기준 |
| 분석 기준 커밋 | 8f5f2a0 · 2026-10-13 | 소스 분석 기준 HEAD, 메시지 Internal changes |
| 앱 버전 설정 | 1.0.20 / 코드 46 | Android/src/app/build.gradle.kts |
| 주 언어 | Kotlin | Android 앱 구현이 분석의 중심 |
| 코드 규모 | Kotlin 85,786줄·326개 파일 | 빈 줄·주석 포함, Gradle Kotlin 230줄은 별도 |
| 기타 텍스트 | XML 5,976줄·HTML 4,203줄 | 리소스·스킬 포함, HTML 내부 JavaScript는 HTML 줄 수에 포함 |
| 플랫폼 범위 | Android 앱 소스 확인 | README는 iOS·macOS 배포도 안내 |
| 테스트·CI | APK 조립 워크플로 있음 | 독립 테스트 소스 디렉토리·CI 테스트 단계는 확인되지 않음 |
| 라이선스 | Apache-2.0 | 앱 저장소 기준. 모델 가중치의 이용 조건은 각 모델에서 확인 |
| 제품 단계 | 실험적 베타 | README 안내, MCP도 실험 단계로 명시 |
최근 push 날짜, 릴리스 날짜, 소스 분석 기준 커밋 날짜는 서로 다른 정보를 나타냅니다. 이 글의 코드 설명은 8f5f2a0에 고정하고, 인기 지표와 릴리스 상태는 분석일 기준으로 읽으면 됩니다.
성능: 소스 구조만으로 특정 기기의 속도·발열·메모리 안정성을 보장할 수 없습니다. 현재 계측 기본 경로도 비활성화되어 있습니다. 비교하려면 모델 버전·가속기·입력·대화 길이·측정 조건을 맞춰야 합니다.
오프라인·데이터: 로컬 추론과 외부 통신은 별개입니다. 목록·모델 다운로드, 외부 스킬·MCP, 구성에 따라 가능한 Analytics를 구분해야 합니다. Analytics.kt는 Firebase 미설정 시 예외를 처리하며, 앱 빌드 파일의 google-services 플러그인도 기본 적용이 꺼져 있습니다.
코드·문서: README의 플랫폼 소개와 공개 앱 소스 범위, 벤치마크 소개와 활성 상태, 문서의 문맥 수치와 모델별 설정이 다릅니다. 예전 목록·가이드의 표기를 최신 구현으로 그대로 대체하지 마세요.
정확성·권한: 모델이 생성한 설명이나 함수 인자가 항상 맞는 것은 아닙니다. 입력 검증·실행 결과 확인·사용자 권한은 앱의 책임이며, 외부 스킬과 도구는 신뢰할 수 있는 범위에서 선택해야 합니다.
배포: 앱의 Apache-2.0과 모델의 이용 조건은 별도입니다. 또한 이 커밋의 release 빌드는 debug 서명 설정을 참조하므로, 실제 배포에는 서명·인증·구성 값을 검토해야 합니다.