🎨 悠米(chatgpt) 服务功能优化

🎨 悠米(chatgpt) 服务功能优化
This commit is contained in:
ronger 2023-07-20 13:39:08 +08:00 committed by GitHub
commit 7aca99c8d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 111 additions and 9 deletions

View File

@ -152,7 +152,7 @@
<dependency> <dependency>
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId> <artifactId>fastjson</artifactId>
<version>2.0.20</version> <version>2.0.25</version>
</dependency> </dependency>
<!-- shiro权限控制框架 --> <!-- shiro权限控制框架 -->
<dependency> <dependency>
@ -305,12 +305,12 @@
<dependency> <dependency>
<groupId>cn.hutool</groupId> <groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId> <artifactId>hutool-core</artifactId>
<version>5.8.11</version> <version>5.8.19</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>cn.hutool</groupId> <groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId> <artifactId>hutool-http</artifactId>
<version>5.8.11</version> <version>5.8.19</version>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -1,6 +1,6 @@
package com.rymcu.forest.config; package com.rymcu.forest.config;
import com.alibaba.fastjson.support.spring.FastJsonJsonView; import com.alibaba.fastjson.support.spring.annotation.FastJsonView;
import com.rymcu.forest.core.exception.BusinessException; import com.rymcu.forest.core.exception.BusinessException;
import com.rymcu.forest.core.exception.ServiceException; import com.rymcu.forest.core.exception.ServiceException;
import com.rymcu.forest.core.exception.TransactionException; import com.rymcu.forest.core.exception.TransactionException;
@ -47,7 +47,7 @@ public class BaseExceptionHandler {
result = new GlobalResult<>(ResultCode.UNAUTHORIZED); result = new GlobalResult<>(ResultCode.UNAUTHORIZED);
logger.info("用户无权限"); logger.info("用户无权限");
} else if (ex instanceof UnknownAccountException) { } else if (ex instanceof UnknownAccountException) {
// 账号或密码错误 // 未知账号
result = new GlobalResult<>(ResultCode.UNKNOWN_ACCOUNT); result = new GlobalResult<>(ResultCode.UNKNOWN_ACCOUNT);
logger.info(ex.getMessage()); logger.info(ex.getMessage());
} else if (ex instanceof AccountException) { } else if (ex instanceof AccountException) {
@ -91,7 +91,7 @@ public class BaseExceptionHandler {
return result; return result;
} else { } else {
ModelAndView mv = new ModelAndView(); ModelAndView mv = new ModelAndView();
FastJsonJsonView view = new FastJsonJsonView(); FastJsonView view = new FastJsonView();
Map<String, Object> attributes = new HashMap(2); Map<String, Object> attributes = new HashMap(2);
if (ex instanceof UnauthenticatedException) { if (ex instanceof UnauthenticatedException) {
attributes.put("code", ResultCode.UNAUTHENTICATED.getCode()); attributes.put("code", ResultCode.UNAUTHENTICATED.getCode());
@ -99,6 +99,16 @@ public class BaseExceptionHandler {
} else if (ex instanceof UnauthorizedException) { } else if (ex instanceof UnauthorizedException) {
attributes.put("code", ResultCode.UNAUTHORIZED.getCode()); attributes.put("code", ResultCode.UNAUTHORIZED.getCode());
attributes.put("message", ResultCode.UNAUTHORIZED.getMessage()); attributes.put("message", ResultCode.UNAUTHORIZED.getMessage());
} else if (ex instanceof UnknownAccountException) {
// 未知账号
attributes.put("code", ResultCode.UNKNOWN_ACCOUNT.getCode());
attributes.put("message", ex.getMessage());
logger.info(ex.getMessage());
} else if (ex instanceof AccountException) {
// 账号或密码错误
attributes.put("code", ResultCode.INCORRECT_ACCOUNT_OR_PASSWORD.getCode());
attributes.put("message", ex.getMessage());
logger.info(ex.getMessage());
} else if (ex instanceof ServiceException) { } else if (ex instanceof ServiceException) {
//业务失败的异常账号或密码错误 //业务失败的异常账号或密码错误
attributes.put("code", ((ServiceException) ex).getCode()); attributes.put("code", ((ServiceException) ex).getCode());

View File

@ -4,8 +4,10 @@ import com.alibaba.fastjson.JSONObject;
import com.rymcu.forest.core.result.GlobalResult; import com.rymcu.forest.core.result.GlobalResult;
import com.rymcu.forest.core.result.GlobalResultGenerator; import com.rymcu.forest.core.result.GlobalResultGenerator;
import com.rymcu.forest.entity.User; import com.rymcu.forest.entity.User;
import com.rymcu.forest.openai.entity.ChatMessageModel;
import com.rymcu.forest.openai.service.OpenAiService; import com.rymcu.forest.openai.service.OpenAiService;
import com.rymcu.forest.openai.service.SseService; import com.rymcu.forest.openai.service.SseService;
import com.rymcu.forest.util.Html2TextUtil;
import com.rymcu.forest.util.UserUtils; import com.rymcu.forest.util.UserUtils;
import com.theokanning.openai.completion.chat.ChatCompletionChoice; import com.theokanning.openai.completion.chat.ChatCompletionChoice;
import com.theokanning.openai.completion.chat.ChatCompletionChunk; import com.theokanning.openai.completion.chat.ChatCompletionChunk;
@ -13,6 +15,7 @@ import com.theokanning.openai.completion.chat.ChatCompletionRequest;
import com.theokanning.openai.completion.chat.ChatMessage; import com.theokanning.openai.completion.chat.ChatMessage;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
@ -22,6 +25,7 @@ import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.time.Duration; import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
/** /**
@ -50,9 +54,36 @@ public class OpenAiController {
ChatMessage chatMessage = new ChatMessage("user", message); ChatMessage chatMessage = new ChatMessage("user", message);
List<ChatMessage> list = new ArrayList<>(4); List<ChatMessage> list = new ArrayList<>(4);
list.add(chatMessage); list.add(chatMessage);
return sendMessage(user, list);
}
@PostMapping("/new-chat")
public GlobalResult newChat(@RequestBody List<ChatMessageModel> messages) {
if (messages.isEmpty()) {
throw new IllegalArgumentException("参数异常!");
}
User user = UserUtils.getCurrentUserByToken();
Collections.reverse(messages);
List<ChatMessage> list = new ArrayList<>(messages.size());
if (messages.size() > 4) {
messages = messages.subList(messages.size() - 4, messages.size());
}
if (messages.size() >= 4 && messages.size() % 4 == 0) {
ChatMessage message = new ChatMessage("system", "简单总结一下你和用户的对话, 用作后续的上下文提示 prompt, 控制在 200 字内");
list.add(message);
}
messages.forEach(chatMessageModel -> {
ChatMessage message = new ChatMessage(chatMessageModel.getRole(), Html2TextUtil.getContent(chatMessageModel.getContent()));
list.add(message);
});
return sendMessage(user, list);
}
@NotNull
private GlobalResult sendMessage(User user, List<ChatMessage> list) {
OpenAiService service = new OpenAiService(token, Duration.ofSeconds(180)); OpenAiService service = new OpenAiService(token, Duration.ofSeconds(180));
ChatCompletionRequest completionRequest = ChatCompletionRequest.builder() ChatCompletionRequest completionRequest = ChatCompletionRequest.builder()
.model("gpt-3.5-turbo") .model("gpt-3.5-turbo-16k-0613")
.stream(true) .stream(true)
.messages(list) .messages(list)
.build(); .build();

View File

@ -0,0 +1,26 @@
package com.rymcu.forest.openai.entity;
import lombok.Data;
/**
* Created on 2023/7/16 14:52.
*
* @author ronger
* @email ronger-x@outlook.com
* @desc : com.rymcu.forest.openai.entity
*/
@Data
public class ChatMessageModel {
Long dataId;
String to;
String from;
Integer dataType;
String content;
String role;
}

View File

@ -0,0 +1,29 @@
package com.rymcu.forest.openai.service;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
/**
* OkHttp Interceptor that adds an ip address header
* @author ronger
*/
public class IpAddressInterceptor implements Interceptor {
private final String ip;
IpAddressInterceptor(String ip) {
this.ip = ip;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request()
.newBuilder()
.header("x-forwarded-for", ip)
.build();
return chain.proceed(request);
}
}

View File

@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy; import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.rymcu.forest.util.SpringContextHolder; import com.rymcu.forest.util.SpringContextHolder;
import com.rymcu.forest.util.Utils;
import com.theokanning.openai.DeleteResult; import com.theokanning.openai.DeleteResult;
import com.theokanning.openai.OpenAiApi; import com.theokanning.openai.OpenAiApi;
import com.theokanning.openai.OpenAiError; import com.theokanning.openai.OpenAiError;
@ -30,18 +31,20 @@ import com.theokanning.openai.image.ImageResult;
import com.theokanning.openai.model.Model; import com.theokanning.openai.model.Model;
import com.theokanning.openai.moderation.ModerationRequest; import com.theokanning.openai.moderation.ModerationRequest;
import com.theokanning.openai.moderation.ModerationResult; import com.theokanning.openai.moderation.ModerationResult;
import io.reactivex.BackpressureStrategy; import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.Single; import io.reactivex.Single;
import okhttp3.*; import okhttp3.*;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import retrofit2.Call;
import retrofit2.HttpException; import retrofit2.HttpException;
import retrofit2.Retrofit; import retrofit2.Retrofit;
import retrofit2.Call;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.jackson.JacksonConverterFactory; import retrofit2.converter.jackson.JacksonConverterFactory;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException; import java.io.IOException;
import java.time.Duration; import java.time.Duration;
import java.util.List; import java.util.List;
@ -336,8 +339,11 @@ public class OpenAiService {
} }
public static OkHttpClient defaultClient(String token, Duration timeout) { public static OkHttpClient defaultClient(String token, Duration timeout) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String ip = Utils.getIpAddress(request);
return new OkHttpClient.Builder() return new OkHttpClient.Builder()
.addInterceptor(new AuthenticationInterceptor(token)) .addInterceptor(new AuthenticationInterceptor(token))
.addInterceptor(new IpAddressInterceptor(ip))
.connectionPool(new ConnectionPool(5, 1, TimeUnit.SECONDS)) .connectionPool(new ConnectionPool(5, 1, TimeUnit.SECONDS))
.readTimeout(timeout.toMillis(), TimeUnit.MILLISECONDS) .readTimeout(timeout.toMillis(), TimeUnit.MILLISECONDS)
.build(); .build();