Skip to content

Commit 5b5abb8

Browse files
committed
feat: initialize local API debug helper
0 parents  commit 5b5abb8

27 files changed

Lines changed: 2277 additions & 0 deletions
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Build API Debug Helper APK
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches: ["main"]
7+
paths:
8+
- "app/**"
9+
- "gradle/**"
10+
- "build.gradle.kts"
11+
- "settings.gradle.kts"
12+
- "gradle.properties"
13+
- "gradlew"
14+
- ".github/workflows/build_android.yaml"
15+
pull_request:
16+
branches: ["main"]
17+
paths:
18+
- "app/**"
19+
- "gradle/**"
20+
- "build.gradle.kts"
21+
- "settings.gradle.kts"
22+
- "gradle.properties"
23+
- "gradlew"
24+
- ".github/workflows/build_android.yaml"
25+
26+
jobs:
27+
build_apk:
28+
name: Build release APK
29+
runs-on: ubuntu-latest
30+
31+
steps:
32+
- name: Checkout source
33+
uses: actions/checkout@v4
34+
35+
- name: Set up Java
36+
uses: actions/setup-java@v4
37+
with:
38+
distribution: temurin
39+
java-version: "17"
40+
41+
- name: Set up Gradle
42+
uses: gradle/actions/setup-gradle@v4
43+
44+
- name: Make Gradle wrapper executable
45+
run: chmod +x ./gradlew
46+
47+
- name: Build release APK
48+
run: ./gradlew :app:assembleRelease
49+
50+
- name: Upload unsigned release APK
51+
uses: actions/upload-artifact@v4
52+
with:
53+
name: api-debug-helper-release-unsigned
54+
path: app/build/outputs/apk/release/app-release-unsigned.apk

.gitignore

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Gradle
2+
.gradle/
3+
build/
4+
**/build/
5+
6+
# Android local files
7+
local.properties
8+
*.iml
9+
.idea/
10+
11+
# APK and signing outputs
12+
*.apk
13+
*.aab
14+
*.jks
15+
*.keystore
16+
app-release-signed.apk
17+
18+
# Kotlin / Java
19+
*.class
20+
21+
# OS files
22+
.DS_Store
23+
Thumbs.db
24+
25+
# Logs
26+
*.log

README.md

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# Local API Debug Helper
2+
3+
用于测试 OpenAI-compatible local API server 的 Android 调试工具。目标服务项目是 [google-ai-edge-gallery-local-api](https://github.com/bugroom/google-ai-edge-gallery-local-api)
4+
5+
这个仓库只维护 API 调试助手本身,适合独立构建、独立发布 APK、独立记录问题。
6+
7+
## 目录
8+
9+
- [功能](#功能)
10+
- [目标服务](#目标服务)
11+
- [快速使用](#快速使用)
12+
- [构建说明](#构建说明)
13+
- [GitHub Actions](#github-actions)
14+
- [项目结构](#项目结构)
15+
- [故障排查](#故障排查)
16+
- [License](#license)
17+
18+
## 功能
19+
20+
- 配置 API 服务地址、端口和 API Key。
21+
- 调用 `GET /health` 检查服务状态。
22+
- 调用 `GET /v1/models` 获取模型列表。
23+
- 调用 `POST /v1/chat/completions` 发送聊天补全请求。
24+
- 支持非流式响应。
25+
- 支持 SSE 流式响应和 `data: [DONE]` 结束事件。
26+
- 提供请求历史记录。
27+
- 提供应用内日志页面,支持复制日志,便于排查问题。
28+
29+
## 目标服务
30+
31+
推荐搭配以下服务端项目使用:
32+
33+
```text
34+
https://github.com/bugroom/google-ai-edge-gallery-local-api
35+
```
36+
37+
目标服务支持的主要端点:
38+
39+
| 方法 | 路径 | 用途 |
40+
|------|------|------|
41+
| GET | `/health` | 健康检查 |
42+
| GET | `/v1/models` | 模型列表 |
43+
| POST | `/v1/chat/completions` | 聊天补全 |
44+
45+
## 快速使用
46+
47+
1. 在 Google AI Edge Gallery 本地 API 版中下载 LLM 模型。
48+
2. 从 Gallery 侧栏进入 `API Server`
49+
3. 设置默认模型、采样参数和推理后端。
50+
4. 本机测试可使用 `127.0.0.1`,局域网测试将 Gallery Host 设置为 `0.0.0.0`
51+
5. 按需启用 API Key。
52+
6. 启动 API Server。
53+
7. 打开 Local API Debug Helper。
54+
8. 输入 Host、Port 和 API Key。
55+
9. 依次测试健康检查、模型列表和聊天补全。
56+
57+
局域网测试时,调试助手中填写的是运行 Gallery 服务端设备的局域网 IP。
58+
59+
## 构建说明
60+
61+
### 环境要求
62+
63+
- Android SDK 34
64+
- JDK 17 或更新版本
65+
- Gradle wrapper 已包含在仓库中
66+
67+
### 本地构建
68+
69+
```bash
70+
# Build release APK
71+
./gradlew :app:assembleRelease
72+
```
73+
74+
Release APK 输出路径:
75+
76+
```text
77+
app/build/outputs/apk/release/app-release-unsigned.apk
78+
```
79+
80+
Debug APK 构建:
81+
82+
```bash
83+
# Build debug APK
84+
./gradlew :app:assembleDebug
85+
```
86+
87+
Debug APK 输出路径:
88+
89+
```text
90+
app/build/outputs/apk/debug/app-debug.apk
91+
```
92+
93+
## GitHub Actions
94+
95+
工作流文件:
96+
97+
```text
98+
.github/workflows/build_android.yaml
99+
```
100+
101+
当前工作流会在以下场景触发:
102+
103+
- 手动运行 `workflow_dispatch`
104+
- 推送到 `main` 且修改 Android 项目文件。
105+
- 针对 `main` 的 Pull Request 且修改 Android 项目文件。
106+
107+
构建命令:
108+
109+
```bash
110+
./gradlew :app:assembleRelease
111+
```
112+
113+
上传 artifact:
114+
115+
```text
116+
api-debug-helper-release-unsigned
117+
```
118+
119+
artifact 对应文件:
120+
121+
```text
122+
app/build/outputs/apk/release/app-release-unsigned.apk
123+
```
124+
125+
下载方式:
126+
127+
1. 打开 GitHub 仓库的 `Actions` 页面。
128+
2. 进入一次 `Build API Debug Helper APK` workflow run。
129+
3.`Artifacts` 区域下载 `api-debug-helper-release-unsigned`
130+
131+
## 项目结构
132+
133+
```text
134+
app/src/main/java/com/api/debug/helper/
135+
```
136+
137+
| 目录 | 说明 |
138+
|------|------|
139+
| `api/` | OkHttp API 调用封装 |
140+
| `data/` | 请求、响应、配置和历史记录模型 |
141+
| `ui/` | Compose UI 和 ViewModel |
142+
| `ui/theme/` | Material3 主题配置 |
143+
| `util/` | 应用内日志工具 |
144+
145+
## 故障排查
146+
147+
### 连接失败
148+
149+
- 确认 Gallery API Server 已启动。
150+
- 本机测试使用 `127.0.0.1`
151+
- 局域网测试使用 Gallery 设备的局域网 IP。
152+
- 局域网测试时 Gallery Host 应设置为 `0.0.0.0`
153+
- 启用 API Key 时,确认调试助手中填写了相同 Key。
154+
155+
### 模型列表为空
156+
157+
- 确认 Gallery 中已下载 LLM 模型。
158+
- 重新点击模型列表加载按钮。
159+
- 检查 Gallery 端日志中的 `LOCAL_API event=models_list`
160+
161+
### 聊天请求耗时较长
162+
163+
- 首次请求可能触发模型初始化。
164+
- 大模型在移动设备上推理耗时更长。
165+
- 可尝试降低 `max_tokens` 或切换推理后端。
166+
167+
### 流式响应没有结束
168+
169+
- 确认服务端发送 `data: [DONE]`
170+
- 查看调试助手日志页中的 `Chunks``ParseErrors`
171+
- 确认服务端没有在推理过程中被系统回收。
172+
173+
## License
174+
175+
Apache License 2.0

app/build.gradle.kts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
plugins {
2+
id("com.android.application")
3+
id("org.jetbrains.kotlin.android")
4+
id("org.jetbrains.kotlin.plugin.serialization") version "1.9.22"
5+
id("com.google.devtools.ksp") version "1.9.22-1.0.16"
6+
}
7+
8+
android {
9+
namespace = "com.api.debug.helper"
10+
compileSdk = 34
11+
12+
defaultConfig {
13+
applicationId = "com.api.debug.helper"
14+
minSdk = 24
15+
targetSdk = 34
16+
versionCode = 1
17+
versionName = "1.0"
18+
19+
vectorDrawables {
20+
useSupportLibrary = true
21+
}
22+
}
23+
24+
buildTypes {
25+
release {
26+
isMinifyEnabled = false
27+
proguardFiles(
28+
getDefaultProguardFile("proguard-android-optimize.txt"),
29+
"proguard-rules.pro"
30+
)
31+
}
32+
}
33+
34+
compileOptions {
35+
sourceCompatibility = JavaVersion.VERSION_1_8
36+
targetCompatibility = JavaVersion.VERSION_1_8
37+
}
38+
39+
kotlinOptions {
40+
jvmTarget = "1.8"
41+
}
42+
43+
buildFeatures {
44+
compose = true
45+
}
46+
47+
composeOptions {
48+
kotlinCompilerExtensionVersion = "1.5.10"
49+
}
50+
51+
packaging {
52+
resources {
53+
excludes += "/META-INF/{AL2.0,LGPL2.1}"
54+
}
55+
}
56+
}
57+
58+
dependencies {
59+
implementation("androidx.core:core-ktx:1.12.0")
60+
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
61+
implementation("androidx.activity:activity-compose:1.8.2")
62+
implementation(platform("androidx.compose:compose-bom:2024.03.00"))
63+
implementation("androidx.compose.ui:ui")
64+
implementation("androidx.compose.ui:ui-graphics")
65+
implementation("androidx.compose.ui:ui-tooling-preview")
66+
implementation("androidx.compose.material3:material3")
67+
68+
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0")
69+
implementation("androidx.navigation:navigation-compose:2.7.7")
70+
71+
implementation("com.squareup.okhttp3:okhttp:4.12.0")
72+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0")
73+
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
74+
75+
implementation("androidx.room:room-runtime:2.6.1")
76+
implementation("androidx.room:room-ktx:2.6.1")
77+
ksp("androidx.room:room-compiler:2.6.1")
78+
79+
debugImplementation("androidx.compose.ui:ui-tooling")
80+
debugImplementation("androidx.compose.ui:ui-test-manifest")
81+
}

app/proguard-rules.pro

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Add project specific ProGuard rules here.
2+
-keepattributes *Annotation*
3+
-keepattributes Signature
4+
-keepattributes SourceFile,LineNumberTable
5+
-keep public class * extends android.app.Activity
6+
-keep public class * extends androidx.fragment.app.Fragment
7+
-keep public class * extends android.app.Application
8+
-keep public class * extends android.app.Service
9+
-keep public class * extends android.content.BroadcastReceiver
10+
-keep public class * extends android.content.ContentProvider
11+
-keep class * extends androidx.** { *; }

app/src/main/AndroidManifest.xml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:tools="http://schemas.android.com/tools">
4+
5+
<uses-permission android:name="android.permission.INTERNET" />
6+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
7+
8+
<application
9+
android:allowBackup="true"
10+
android:icon="@mipmap/ic_launcher"
11+
android:label="@string/app_name"
12+
android:roundIcon="@mipmap/ic_launcher_round"
13+
android:supportsRtl="true"
14+
android:theme="@style/Theme.ApiDebugHelper"
15+
android:usesCleartextTraffic="true"
16+
tools:targetApi="31">
17+
<activity
18+
android:name=".ui.MainActivity"
19+
android:exported="true"
20+
android:theme="@style/Theme.ApiDebugHelper">
21+
<intent-filter>
22+
<action android:name="android.intent.action.MAIN" />
23+
<category android:name="android.intent.category.LAUNCHER" />
24+
</intent-filter>
25+
</activity>
26+
</application>
27+
28+
</manifest>

0 commit comments

Comments
 (0)