Skip to content

Commit

Permalink
修复: API Host导致闪退、Vision模型无法访问网络、软件后台运行时无法接收分享;添加: 将gpt-4o-mini加入模型列表;…
Browse files Browse the repository at this point in the history
…版本号修改为 v1.11.1
  • Loading branch information
Skythinker616 committed Sep 25, 2024
1 parent 107780d commit 11e531b
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 91 deletions.
21 changes: 15 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@

</div>

这是一个基于ChatGPT的安卓端语音助手,允许用户通过手机音量键从任意界面唤起并直接进行语音交流,用最快捷的方式询问并获取回复
这是一个基于 ChatGPT API 的安卓端语音助手,允许用户通过手机音量键从任意界面唤起并直接进行语音交流,用最快捷的方式询问并获取回复

<div align=center>
<font size=3>
<b>免费聊天 · 语音交互 · 支持联网 · 支持识图</b>
<b>快捷唤起 · 语音交互 · 支持联网 · 支持识图</b>
</font>
</div>

Expand Down Expand Up @@ -52,15 +52,15 @@

本软件通过OpenAI API获取回复,在国内使用时可以用第三方转发服务,如[Chatanywhere](https://github.com/chatanywhere/GPT_API_free),其目前提供免费和付费服务,具体使用方法见[下述说明](#使用方法)

> 注:Chatanywhere注册需要GitHub账号,因此注册时需要能够登录GitHub的网络环境
> 注:注册Chatanywhere免费服务需要GitHub账号,因此注册时需要能够登录GitHub的网络环境
### 费用说明

本软件不会收取任何费用,用户能够免费使用各项功能,但如果有特殊需求,使用的下述第三方服务**可能**会产生费用:

1. ChatGPT调用费用

- 以Chatanywhere为例,目前其**免费服务**限制对`gpt-3.5-turbo`模型的调用频率不超过**100请求/天/IP&Key**,若需要更高的调用频率或`gpt-4`模型,需要选择付费服务
- 以Chatanywhere为例,目前其**免费服务**限制对`gpt-3.5-turbo`、`gpt-4o-mini`等模型的调用频率不超过**200请求/天/IP&Key**,若需要更高的调用频率或更好的模型,需要选择付费服务

2. 语音识别接口费用

Expand Down Expand Up @@ -178,9 +178,9 @@

程序使用的是OpenAI API,需要用户在设置中填入自己的API_KEY,可以选择使用官方服务或第三方转发服务

- **使用Chatanywhere转发服务**国内推荐
- **使用Chatanywhere转发服务**国内可用

Chatanywhere提供了免费和付费的OpenAI API转发服务,目前免费服务限制100请求/天/IP&Key调用频率,付费服务则无限制,可以在国内直接访问,用户可以参照其[项目主页](https://github.com/chatanywhere/GPT_API_free)获取地址和KEY填入设置中
Chatanywhere提供了免费和付费的OpenAI API转发服务,目前免费服务限制200请求/天/IP&Key调用频率,付费服务则无限制,可以在国内直接访问,用户可以参照其[项目主页](https://github.com/chatanywhere/GPT_API_free)获取地址和KEY填入设置中

- **使用官方服务**

Expand Down Expand Up @@ -304,6 +304,14 @@ A: 为防止滥用,仓库中的Key开启了包名和签名验证,因此如

---

## TODO

- 支持调整随机性(Temperature)等参数
- 支持提问、回复长度限制
- 支持消息压缩

---

## 测试环境

已测试的机型:
Expand All @@ -316,6 +324,7 @@ A: 为防止滥用,仓库中的Key开启了包名和签名验证,因此如
| 华为 Mate 30 | HarmonyOS 3.0.0 | Android 12 | 1.6.0 |
| 华为 Mate 30 | HarmonyOS 4.0 | Android 12 | 1.8.0 |
| 荣耀 Magic 4 | MagicOS 7.0 | Android 13 | 1.9.1 |
| 荣耀 Magic 6 | MagicOS 8.0 | Android 14 | 1.11.1 |
| 红米 K20 Pro | MIUI 12.5.6 | Android 11 | 1.5.0 |
| 红米 K60 Pro | MIUI 14.0.23 | Android 13 | 1.7.0 |
| 小米 13 | MIUI 14.0.5 | Android 14 | 1.10.0 |
Expand Down
15 changes: 12 additions & 3 deletions README_en.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ This software does not incur any charges; users can use all features for free. H

1. ChatGPT API usage fees

- For example, with Chatanywhere, the **free service** limits the call frequency to `gpt-3.5-turbo` model to no more than **100 requests/day/IP & Key**. This is sufficient for personal use. If a higher call frequency or the `gpt-4` model is required, you can choose the paid service.
- For example, with Chatanywhere, the **free service** limits the call frequency to `gpt-3.5-turbo` model to no more than **200 requests/day/IP & Key**. This is sufficient for personal use. If a higher call frequency or the `gpt-4` model is required, you can choose the paid service.

2. Speech recognition API fees

Expand Down Expand Up @@ -179,7 +179,7 @@ The program uses the OpenAI API and requires users to enter their own API_KEY in

- **Using the Chatanywhere forwarding service** (recommended for use in China)

Chatanywhere provides free and paid OpenAI API forwarding services. The free service currently limits the call frequency to `gpt-3.5-turbo` model to no more than **100 requests/day/IP&Key**. The paid service has no limitations. Users can refer to their [project homepage](https://github.com/chatanywhere/GPT_API_free) to obtain the address and key and fill in the settings.
Chatanywhere provides free and paid OpenAI API forwarding services. The free service currently limits the call frequency to `gpt-3.5-turbo` model to no more than **200 requests/day/IP&Key**. The paid service has no limitations. Users can refer to their [project homepage](https://github.com/chatanywhere/GPT_API_free) to obtain the address and key and fill in the settings.

- **Using the official service**

Expand Down Expand Up @@ -302,6 +302,14 @@ A: To prevent abuse, the keys in the repository have package name and signature

---

## TODO

- Support adjusting api parameters such as temperature
- Support question and reply length restrictions
- Support message compression

---

## Test Environment

Tested models:
Expand All @@ -310,10 +318,11 @@ Tested models:
| :-------------: | :-------------: | :-------------: | :-------------: |
| Honor 7C | EMUI 8.0.0 | Android 8 | 1.9.1 |
| Honor 20 | HarmonyOS 3.0.0 | Android 10 | 1.9.1 |
| Honor 20 | HarmonyOS 4.0 | Android 10 | 1.10.0 |
| Honor 20 | HarmonyOS 4.0 | Android 10 | 1.11.0 |
| Huawei Mate 30 | HarmonyOS 3.0.0 | Android 12 | 1.6.0 |
| Huawei Mate 30 | HarmonyOS 4.0 | Android 12 | 1.8.0 |
| Honor Magic 4 | MagicOS 7.0 | Android 13 | 1.9.1 |
| Honor Magic 6 | MagicOS 8.0 | Android 14 | 1.11.1 |
| Redmi K20 Pro | MIUI 12.5.6 | Android 11 | 1.5.0 |
| Redmi K60 Pro | MIUI 14.0.23 | Android 13 | 1.7.0 |
| Xiaomi 13 | MIUI 14.0.5 | Android 14 | 1.10.0 |
Expand Down
2 changes: 1 addition & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ android {
minSdk 26
targetSdk 32
versionCode 1
versionName "1.11.0"
versionName "1.11.1"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down
45 changes: 28 additions & 17 deletions app/src/main/java/com/skythinker/gptassistant/ChatApiClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,8 @@ public ChatApiClient(Context context, String url, String apiKey, String model, O

// 向GPT发送消息列表
public void sendPromptList(List<ChatMessage> promptList) {
if(url.isEmpty()) {
listener.onError("请在设置中填写服务器地址");
return;
} else if(apiKey.isEmpty()) {
listener.onError("请在设置中填写ApiKey");
return;
} else if(chatGPT == null) {
listener.onError("ChatGPT初始化失败");
if(url.isEmpty() || apiKey.isEmpty() || chatGPT == null) {
listener.onError(context.getString(R.string.text_gpt_conf_error));
return;
}

Expand Down Expand Up @@ -153,10 +147,19 @@ public void sendPromptList(List<ChatMessage> promptList) {
}
}

chatCompletion = ChatCompletionWithPicture.builder()
.messages(messageList)
.model(model.replaceAll("\\*$","")) // 去掉自定义Vision模型结尾的*号
.build();
if (!functions.isEmpty()) { // 如果有函数列表,则将函数列表传入
chatCompletion = ChatCompletionWithPicture.builder()
.messages(messageList)
.model(model.replaceAll("\\*$","")) // 去掉自定义Vision模型结尾的*号
.functions(functions)
.functionCall("auto")
.build();
} else {
chatCompletion = ChatCompletionWithPicture.builder()
.messages(messageList)
.model(model.replaceAll("\\*$","")) // 去掉自定义Vision模型结尾的*号
.build();
}
}

callingFuncName = callingFuncArg = "";
Expand Down Expand Up @@ -243,11 +246,19 @@ public void setApiInfo(String url, String apiKey) {
}
this.url = url;
this.apiKey = apiKey;
chatGPT = new OpenAiStreamClient.Builder()
.apiKey(Arrays.asList(apiKey))
.apiHost(url)
.okHttpClient(httpClient)
.build();
try {
chatGPT = new OpenAiStreamClient.Builder()
.apiKey(Arrays.asList(apiKey))
.apiHost(url)
.okHttpClient(httpClient)
.build();
} catch (Exception e) {
String err = context.getString(R.string.text_gpt_conf_error);
if(e.getMessage() != null) {
err += ": " + e.getMessage();
}
listener.onError(err);
}
}

// 获取当前是否正在请求GPT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public static void showToast(Context context, int resId, boolean isLong) {
}

public static boolean checkVisionSupport(String model) {
final String[] specialVisionModels = {"gpt-4-turbo", "gpt-4o"}; // 支持识图但不包含"vision"的模型
final String[] specialVisionModels = {"gpt-4-turbo", "gpt-4o", "gpt-4o-mini"}; // 支持识图但不包含"vision"的模型
return model.contains("vision") || Arrays.asList(specialVisionModels).contains(model) || model.endsWith("*");
}
}
106 changes: 50 additions & 56 deletions app/src/main/java/com/skythinker/gptassistant/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,21 +73,6 @@
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONException;
import cn.hutool.json.JSONObject;
import io.noties.markwon.AbstractMarkwonPlugin;
import io.noties.markwon.Markwon;
import io.noties.markwon.MarkwonConfiguration;
import io.noties.markwon.ext.latex.JLatexMathPlugin;
import io.noties.markwon.ext.tables.TableAwareMovementMethod;
import io.noties.markwon.ext.tables.TablePlugin;
import io.noties.markwon.image.ImageSize;
import io.noties.markwon.image.ImageSizeResolverDef;
import io.noties.markwon.image.ImagesPlugin;
import io.noties.markwon.inlineparser.MarkwonInlineParserPlugin;
import io.noties.markwon.linkify.LinkifyPlugin;
import io.noties.markwon.movement.MovementMethodPlugin;
import io.noties.markwon.syntax.Prism4jThemeDefault;
import io.noties.markwon.syntax.SyntaxHighlightPlugin;
import io.noties.prism4j.Prism4j;
import io.noties.prism4j.annotations.PrismBundle;

import com.skythinker.gptassistant.ChatManager.ChatMessage.ChatRole;
Expand Down Expand Up @@ -213,47 +198,7 @@ public void onError(String utteranceId) {
svChatArea = findViewById(R.id.sv_chat_list);
llChatList = findViewById(R.id.ll_chat_list);

// 处理启动Intent
Intent activityIntent = getIntent();
if(activityIntent != null){
String action = activityIntent.getAction();
if(Intent.ACTION_PROCESS_TEXT.equals(action)) { // 全局上下文菜单
String text = activityIntent.getStringExtra(Intent.EXTRA_PROCESS_TEXT);
if(text != null){
etUserInput.setText(text);
}
} else if(Intent.ACTION_SEND.equals(action)) { // 分享图片
String type = activityIntent.getType();
if(type != null && type.startsWith("image/")) {
Uri imageUri = activityIntent.getParcelableExtra(Intent.EXTRA_STREAM); // 获取图片Uri
if(imageUri != null) {
try {
// 获取图片Bitmap并缩放
Bitmap bitmap = (Bitmap) BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
selectedImageBitmap = bitmap;
if(GlobalDataHolder.getLimitVisionSize()) {
if (bitmap.getWidth() < bitmap.getHeight())
selectedImageBitmap = resizeBitmap(bitmap, 512, 2048);
else
selectedImageBitmap = resizeBitmap(bitmap, 2048, 512);
} else {
selectedImageBitmap = resizeBitmap(bitmap, 2048, 2048);
}
btImage.setImageResource(R.drawable.image_enabled);
if(!GlobalUtils.checkVisionSupport(GlobalDataHolder.getGptModel()))
Toast.makeText(this, R.string.toast_use_vision_model, Toast.LENGTH_LONG).show();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
} else if(type != null && type.equals("text/plain")) { // 分享文本
String text = activityIntent.getStringExtra(Intent.EXTRA_TEXT);
if(text != null){
etUserInput.setText(text);
}
}
}
}
handleShareIntent(getIntent()); // 处理分享的文本/图片

chatManager = new ChatManager(this); // 初始化聊天记录管理器
ChatMessage.setContext(this); // 设置聊天消息的上下文(用于读写文件)
Expand Down Expand Up @@ -1288,6 +1233,49 @@ private void clearChatListView() {
llChatList.addView(tv);
}

// 处理启动Intent
private void handleShareIntent(Intent intent) {
if(intent != null){
String action = intent.getAction();
if(Intent.ACTION_PROCESS_TEXT.equals(action)) { // 全局上下文菜单
String text = intent.getStringExtra(Intent.EXTRA_PROCESS_TEXT);
if(text != null){
etUserInput.setText(text);
}
} else if(Intent.ACTION_SEND.equals(action)) { // 分享图片
String type = intent.getType();
if(type != null && type.startsWith("image/")) {
Uri imageUri = intent.getParcelableExtra(Intent.EXTRA_STREAM); // 获取图片Uri
if(imageUri != null) {
try {
// 获取图片Bitmap并缩放
Bitmap bitmap = (Bitmap) BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
selectedImageBitmap = bitmap;
if(GlobalDataHolder.getLimitVisionSize()) {
if (bitmap.getWidth() < bitmap.getHeight())
selectedImageBitmap = resizeBitmap(bitmap, 512, 2048);
else
selectedImageBitmap = resizeBitmap(bitmap, 2048, 512);
} else {
selectedImageBitmap = resizeBitmap(bitmap, 2048, 2048);
}
btImage.setImageResource(R.drawable.image_enabled);
if(!GlobalUtils.checkVisionSupport(GlobalDataHolder.getGptModel()))
Toast.makeText(this, R.string.toast_use_vision_model, Toast.LENGTH_LONG).show();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
} else if(type != null && type.equals("text/plain")) { // 分享文本
String text = intent.getStringExtra(Intent.EXTRA_TEXT);
if(text != null){
etUserInput.setText(text);
}
}
}
}
}

// 转换dp为px
private int dpToPx(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
Expand Down Expand Up @@ -1341,6 +1329,12 @@ private void requestPermission() {
}
}

@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
handleShareIntent(intent);
}

@Override
protected void onResume() {
super.onResume();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,13 @@ public void setApiInfo(String url, String apiKey) {
}
this.url = url;
this.apiKey = apiKey;
chatGPT = new OpenAiClient.Builder()
.apiKey(Arrays.asList(apiKey))
.apiHost(url)
.okHttpClient(httpClient)
.build();
try {
chatGPT = new OpenAiClient.Builder()
.apiKey(Arrays.asList(apiKey))
.apiHost(url)
.okHttpClient(httpClient)
.build();
} catch (Exception ignored) { }
}

public String getWhisperResult(File file) throws Exception{
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/res/values-zh-rCN/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
<string name="text_google_asr_unavailable">Google 语音识别服务不可用</string>
<string name="dialog_asr_select_help">中国大陆:推荐使用华为(免费)或百度(付费)接口\n\n海外使用:可以使用谷歌(免费)或Whisper(付费)接口</string>
<string name="dialog_asr_select_help_title">语音识别选用说明</string>
<string name="text_whisper_param_error">请先配置OpenAI接口参数</string>
<string name="text_whisper_param_error">请检查OpenAI接口参数配置</string>
<string name="text_google_asr_typing_error">请设置Google为语音输入引擎</string>
<string name="text_google_asr_permission_error">请在系统设置中允许Google的录音权限</string>
<string name="text_hms_asr_failed_error">\n可能由于作者关闭了免费HMS语音服务,或在新版本中更新了参数,若该错误持续发生,请尝试更新版本或使用其他语音识别接口</string>
Expand All @@ -127,4 +127,5 @@
<string name="toast_voice_chat_tts_off">请先打开语音播报开关</string>
<string name="dialog_custom_model_help_title">自定义模型说明</string>
<string name="dialog_custom_model_help">可输入多个自定义模型名称,自定义模型也会显示在下拉列表中\n\n多个模型通过英文分号分隔,可在模型结尾添加星号表示支持识图功能\n\n示例:gpt-4o*;gpt-4-0613</string>
<string name="text_gpt_conf_error">GPT客户端错误,请检查API配置</string>
</resources>
Loading

0 comments on commit 11e531b

Please sign in to comment.