Skip to content

Commit

Permalink
更换OpanAI封装库;新增支持打断回复;添加TTS断句,在GPT输出的同时进行播报;修正对话ScrollView保持在最底端逻辑;版本号…
Browse files Browse the repository at this point in the history
…修改为1.4.0
  • Loading branch information
Skythinker616 committed Oct 2, 2023
1 parent b114edb commit ca0d666
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 61 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ GPT Assistant 是一个基于ChatGPT的安卓端语音助手,允许用户通

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

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

Expand Down Expand Up @@ -128,8 +128,8 @@ Chatanywhere提供了免费和付费的OpenAI API转发服务,目前免费服

| 机型 | 系统版本 | Android 版本 | 本程序版本 |
| :--: | :-----: | :----------: | :-------: |
| 荣耀 7C | EMUI 8.0.0 | Android 8 | 1.3.0 |
| 荣耀 20 | HarmonyOS 3.0.0 | Android 10 | 1.3.1 |
| 荣耀 7C | EMUI 8.0.0 | Android 8 | 1.4.0 |
| 荣耀 20 | HarmonyOS 3.0.0 | Android 10 | 1.4.0 |
| 华为 Mate 30 | HarmonyOS 3.0.0 | Android 12 | 1.2.0 |
| 荣耀 Magic 4 | MagicOS 7.0 | Android 13 | 1.2.0 |
| 红米 K20 Pro | MIUI 12.5.6 | Android 11 | 1.3.0 |
Expand All @@ -153,4 +153,4 @@ Chatanywhere提供了免费和付费的OpenAI API转发服务,目前免费服
## 引用的开源项目

- [Markwon](https://github.com/noties/Markwon): Android上的Markdown渲染器
- [chatgpt-java](https://github.com/PlexPt/chatgpt-java): OpenAI API的Java封装
- [chatgpt-java](https://github.com/Grt1228/chatgpt-java): OpenAI API的Java封装
9 changes: 7 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ android {
minSdk 26
targetSdk 32
versionCode 1
versionName "1.3.2"
versionName "1.4.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
Expand All @@ -33,6 +33,11 @@ android {
absolutePaths false
baseline file("lint-baseline.xml")
}
configurations {
all {
exclude group: 'org.jetbrains', module: 'annotations'
}
}
}

dependencies {
Expand All @@ -52,5 +57,5 @@ dependencies {
implementation"io.noties.markwon:linkify:$markwon_version"
implementation"io.noties.markwon:syntax-highlight:$markwon_version"
annotationProcessor 'io.noties:prism4j-bundler:2.0.0'
implementation group: 'com.github.plexpt', name: 'chatgpt', version: '4.1.2'
implementation group: 'com.unfbx', name: 'chatgpt-java', version: '1.1.0'
}
107 changes: 74 additions & 33 deletions app/src/main/java/com/skythinker/gptassistant/ChatApiClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,23 @@
import android.util.Log;
import android.util.Pair;

import com.plexpt.chatgpt.ChatGPT;
import com.plexpt.chatgpt.ChatGPTStream;
import com.plexpt.chatgpt.entity.chat.ChatCompletion;
import com.plexpt.chatgpt.entity.chat.Message;
import com.plexpt.chatgpt.listener.AbstractStreamListener;
import com.plexpt.chatgpt.listener.ConsoleStreamListener;
import androidx.annotation.Nullable;

import com.unfbx.chatgpt.OpenAiStreamClient;
import com.unfbx.chatgpt.entity.chat.Message;
import com.unfbx.chatgpt.entity.chat.ChatCompletion;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import cn.hutool.json.JSONObject;
import okhttp3.OkHttpClient;
import okhttp3.Response;
import okhttp3.internal.http2.StreamResetException;
import okhttp3.sse.EventSource;
import okhttp3.sse.EventSourceListener;

public class ChatApiClient {
public interface OnReceiveListener {
Expand All @@ -32,12 +37,19 @@ public enum ChatRole {
String url = "";
String apiKey = "";
String model = "";
ChatGPTStream chatGPT = null;
OnReceiveListener listener = null;

OkHttpClient httpClient = null;
OpenAiStreamClient chatGPT = null;

public ChatApiClient(String url, String apiKey, String model, OnReceiveListener listener) {
this.listener = listener;
this.model = model;
httpClient = new OkHttpClient.Builder()
.connectTimeout(30, java.util.concurrent.TimeUnit.SECONDS)
.readTimeout(30, java.util.concurrent.TimeUnit.SECONDS)
.writeTimeout(30, java.util.concurrent.TimeUnit.SECONDS)
.build();
setApiInfo(url, apiKey);
}

Expand Down Expand Up @@ -68,44 +80,66 @@ public void sendPromptList(List<Pair<ChatRole, String>> promptList) {
ArrayList<Message> messages = new ArrayList<>();
for(Pair<ChatRole, String> prompt : promptList) {
if(prompt.first == ChatRole.SYSTEM) {
messages.add(Message.ofSystem(prompt.second));
messages.add(Message.builder().role(Message.Role.SYSTEM).content(prompt.second).build());
} else if(prompt.first == ChatRole.USER) {
messages.add(Message.of(prompt.second));
messages.add(Message.builder().role(Message.Role.USER).content(prompt.second).build());
} else if(prompt.first == ChatRole.ASSISTANT) {
messages.add(Message.ofAssistant(prompt.second));
messages.add(Message.builder().role(Message.Role.ASSISTANT).content(prompt.second).build());
}
}

ChatCompletion chatCompletion = ChatCompletion.builder()
.messages(messages)
.model(model)
.build();
chatGPT.streamChatCompletion(chatCompletion, new AbstractStreamListener() {

chatGPT.streamChatCompletion(chatCompletion, new EventSourceListener() {
@Override
public void onMsg(String message) {
listener.onReceive(message);
public void onOpen(EventSource eventSource, Response response) {
Log.d("ChatApiClient", "onOpen");
}

@Override
public void onError(Throwable throwable, String response) {
if(throwable != null) {
String err = throwable.toString();
listener.onError(err);
public void onEvent(EventSource eventSource, @Nullable String id, @Nullable String type, String data) {
if(data.equals("[DONE]")){
Log.d("ChatApiClient", "onEvent: DONE");
listener.onFinished();
} else {
if(response.length() > 300) {
response = response.substring(0, 300);
response += "...";
}
listener.onError(response);
String msg = ((JSONObject) (new JSONObject(data)).getJSONArray("choices").get(0)).getJSONObject("delta").getStr("content");
if(msg != null)
listener.onReceive(msg);
// Log.d("ChatApiClient", "onEvent: " + data);
}
}

@Override
public void onEvent(EventSource eventSource, String id, String type, String data) {
super.onEvent(eventSource, id, type, data);
if(data.equals("[DONE]")){
Log.d("ChatApiClient", "onEvent: DONE");
listener.onFinished();
public void onClosed(EventSource eventSource) {
Log.d("ChatApiClient", "onClosed");
}

@Override
public void onFailure(EventSource eventSource, @Nullable Throwable throwable, @Nullable Response response) {
if(throwable != null) {
if(throwable instanceof StreamResetException) {
Log.d("ChatApiClient", "onFailure: Cancelled");
listener.onFinished();
} else {
String err = throwable.toString();
listener.onError(err);
}
} else {
if(response != null && response.body() != null) {
try {
String err = response.body().string();
if(err.length() > 300) {
err = err.substring(0, 300);
err += "...";
}
listener.onError(err);
} catch (IOException ignore) { }
} else {
listener.onError("未知错误");
}
}
}
});
Expand All @@ -117,12 +151,19 @@ public void setApiInfo(String url, String apiKey) {
}
this.url = url;
this.apiKey = apiKey;
chatGPT = ChatGPTStream.builder()
.timeout(600)
.apiKey(apiKey)
.apiHost(url)
.build()
.init();
chatGPT = new OpenAiStreamClient.Builder()
.apiKey(Arrays.asList(apiKey))
.apiHost(url)
.okHttpClient(httpClient)
.build();
}

public boolean isStreaming() {
return httpClient.connectionPool().connectionCount() - httpClient.connectionPool().idleConnectionCount() > 0;
}

public void stop() {
httpClient.dispatcher().cancelAll();
}

public void setModel(String model) {
Expand Down
Loading

0 comments on commit ca0d666

Please sign in to comment.