From fab1f9e26ca3645ab81529de93eb59276cf11d6f Mon Sep 17 00:00:00 2001 From: wangbing Date: Tue, 2 Sep 2025 17:52:00 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E5=A4=87=E4=BB=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xyz/wbsite/achat/config/ChatConfig.java | 23 ++ .../xyz/wbsite/achat/core/Attachment.java | 30 +++ .../java/xyz/wbsite/achat/core/Prompt.java | 17 ++ .../java/xyz/wbsite/achat/core/Result.java | 209 ++++++++++++++++++ .../java/xyz/wbsite/achat/core/Session.java | 126 +++++++++++ .../wbsite/achat/core/base/Attachment.java | 30 --- .../xyz/wbsite/achat/core/base/Prompt.java | 17 -- .../xyz/wbsite/achat/core/base/Result.java | 209 ------------------ .../xyz/wbsite/achat/core/base/Session.java | 126 ----------- .../achat/core/chat/ChatCompletionChunk.java | 170 ++++++++++++++ .../core/chat/ChatCompletionRequest.java | 123 +++++++++++ .../core/chat/ChatCompletionResponse.java | 201 +++++++++++++++++ .../achat/core/chat/CompletionRequest.java | 67 ++++++ .../achat/core/chat/CompletionResponse.java | 197 +++++++++++++++++ .../achat/core/event/CompleteEvent.java | 125 +++++++++-- .../wbsite/achat/core/event/PartialEvent.java | 109 ++++++--- .../wbsite/achat/core/event/StartEvent.java | 66 ++++++ .../achat/core/message/MessageSseEmitter.java | 176 --------------- .../achat/core/message/StreamEmitter.java | 206 +++++++++++++++++ .../xyz/wbsite/achat/core/model/Model.java | 80 +++++++ .../achat/core/model/ModelListResponse.java | 21 ++ .../core/service/ChatCompletionGenerator.java | 18 ++ .../achat/core/service/ChatService.java | 16 ++ .../achat/core/service/MessageGenerator.java | 18 -- .../service/impl/ChatServiceSampleImpl.java | 110 +++++++++ 25 files changed, 1863 insertions(+), 627 deletions(-) create mode 100644 src/main/java/xyz/wbsite/achat/config/ChatConfig.java create mode 100644 src/main/java/xyz/wbsite/achat/core/Attachment.java create mode 100644 src/main/java/xyz/wbsite/achat/core/Prompt.java create mode 100644 src/main/java/xyz/wbsite/achat/core/Result.java create mode 100644 src/main/java/xyz/wbsite/achat/core/Session.java delete mode 100644 src/main/java/xyz/wbsite/achat/core/base/Attachment.java delete mode 100644 src/main/java/xyz/wbsite/achat/core/base/Prompt.java delete mode 100644 src/main/java/xyz/wbsite/achat/core/base/Result.java delete mode 100644 src/main/java/xyz/wbsite/achat/core/base/Session.java create mode 100644 src/main/java/xyz/wbsite/achat/core/chat/ChatCompletionChunk.java create mode 100644 src/main/java/xyz/wbsite/achat/core/chat/ChatCompletionRequest.java create mode 100644 src/main/java/xyz/wbsite/achat/core/chat/ChatCompletionResponse.java create mode 100644 src/main/java/xyz/wbsite/achat/core/chat/CompletionRequest.java create mode 100644 src/main/java/xyz/wbsite/achat/core/chat/CompletionResponse.java create mode 100644 src/main/java/xyz/wbsite/achat/core/event/StartEvent.java delete mode 100644 src/main/java/xyz/wbsite/achat/core/message/MessageSseEmitter.java create mode 100644 src/main/java/xyz/wbsite/achat/core/message/StreamEmitter.java create mode 100644 src/main/java/xyz/wbsite/achat/core/model/Model.java create mode 100644 src/main/java/xyz/wbsite/achat/core/model/ModelListResponse.java create mode 100644 src/main/java/xyz/wbsite/achat/core/service/ChatCompletionGenerator.java create mode 100644 src/main/java/xyz/wbsite/achat/core/service/ChatService.java delete mode 100644 src/main/java/xyz/wbsite/achat/core/service/MessageGenerator.java create mode 100644 src/main/java/xyz/wbsite/achat/core/service/impl/ChatServiceSampleImpl.java diff --git a/src/main/java/xyz/wbsite/achat/config/ChatConfig.java b/src/main/java/xyz/wbsite/achat/config/ChatConfig.java new file mode 100644 index 0000000..903d648 --- /dev/null +++ b/src/main/java/xyz/wbsite/achat/config/ChatConfig.java @@ -0,0 +1,23 @@ +package xyz.wbsite.achat.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import xyz.wbsite.achat.core.service.ChatService; +import xyz.wbsite.achat.core.service.impl.ChatServiceSampleImpl; + +/** + * 对话配置 + * + * @author wangbing + * @version 0.0.1 + * @since 1.8 + */ +@Configuration +public class ChatConfig implements WebMvcConfigurer { + + @Bean + public ChatService chatService() { + return new ChatServiceSampleImpl(); + } +} diff --git a/src/main/java/xyz/wbsite/achat/core/Attachment.java b/src/main/java/xyz/wbsite/achat/core/Attachment.java new file mode 100644 index 0000000..39d3487 --- /dev/null +++ b/src/main/java/xyz/wbsite/achat/core/Attachment.java @@ -0,0 +1,30 @@ +//package xyz.wbsite.achat.core; +// +///** +// * 附件 +// * +// * @author wangbing +// * @version 0.0.1 +// * @since 1.8 +// */ +//public class Attachment { +// private String filename; +// +// private String fid; +// +// public String getFilename() { +// return filename; +// } +// +// public void setFilename(String filename) { +// this.filename = filename; +// } +// +// public String getFid() { +// return fid; +// } +// +// public void setFid(String fid) { +// this.fid = fid; +// } +//} diff --git a/src/main/java/xyz/wbsite/achat/core/Prompt.java b/src/main/java/xyz/wbsite/achat/core/Prompt.java new file mode 100644 index 0000000..657d56d --- /dev/null +++ b/src/main/java/xyz/wbsite/achat/core/Prompt.java @@ -0,0 +1,17 @@ +//package xyz.wbsite.achat.core; +// +//public class Prompt { +// +// /** +// * 提示词 +// */ +// private String prompt; +// +// public String getPrompt() { +// return prompt; +// } +// +// public void setPrompt(String prompt) { +// this.prompt = prompt; +// } +//} diff --git a/src/main/java/xyz/wbsite/achat/core/Result.java b/src/main/java/xyz/wbsite/achat/core/Result.java new file mode 100644 index 0000000..1d37924 --- /dev/null +++ b/src/main/java/xyz/wbsite/achat/core/Result.java @@ -0,0 +1,209 @@ +//package xyz.wbsite.achat.core; +// +//import java.util.HashMap; +//import java.util.Map; +// +///** +// * 接口响应结果基类 +// * 泛型支持的数据响应封装,提供统一的响应格式和错误处理 +// * +// * @author wangbing +// * @version 0.0.1 +// * @since 1.8 +// */ +//public class Result { +// +// /** +// * 响应状态码 +// */ +// private int code = 200; +// +// /** +// * 响应消息 +// */ +// private String message = "success"; +// +// /** +// * 响应数据 +// */ +// private T data; +// +// /** +// * 是否成功 +// */ +// private boolean success = true; +// +// /** +// * 错误详情,用于表单验证等场景 +// */ +// private Map errors; +// +// /** +// * 请求ID,用于问题追踪 +// */ +// private String requestId; +// +// /** +// * 响应时间戳 +// */ +// private long timestamp = System.currentTimeMillis(); +// +// public int getCode() { +// return code; +// } +// +// public Result setCode(int code) { +// this.code = code; +// return this; +// } +// +// public String getMessage() { +// return message; +// } +// +// public Result setMessage(String message) { +// this.message = message; +// return this; +// } +// +// public T getData() { +// return data; +// } +// +// public Result setData(T data) { +// this.data = data; +// return this; +// } +// +// public boolean isSuccess() { +// return success; +// } +// +// public Result setSuccess(boolean success) { +// this.success = success; +// return this; +// } +// +// public Map getErrors() { +// return errors; +// } +// +// public Result setErrors(Map errors) { +// this.errors = errors; +// return this; +// } +// +// public String getRequestId() { +// return requestId; +// } +// +// public Result setRequestId(String requestId) { +// this.requestId = requestId; +// return this; +// } +// +// public long getTimestamp() { +// return timestamp; +// } +// +// public Result setTimestamp(long timestamp) { +// this.timestamp = timestamp; +// return this; +// } +// +// /** +// * 添加字段级别的错误信息 +// * +// * @param field 字段名 +// * @param error 错误信息 +// * @return 当前结果对象,支持链式调用 +// */ +// public Result addError(String field, String error) { +// if (errors == null) { +// errors = new HashMap<>(); +// } +// errors.put(field, error); +// return this; +// } +// +// /** +// * 返回成功信息 +// * +// * @return 结果 +// */ +// public static Result success() { +// return new Result<>(); +// } +// +// /** +// * 返回带数据的成功信息 +// * +// * @param data 响应数据 +// * @return 结果 +// */ +// public static Result success(T data) { +// Result result = new Result<>(); +// result.setData(data); +// return result; +// } +// +// /** +// * 返回错误信息 +// * +// * @param message 错误信息 +// * @return 错误信息对象 +// */ +// public static Result error(String message) { +// Result result = new Result<>(); +// result.message = message; +// result.code = 500; +// result.success = false; +// return result; +// } +// +// /** +// * 返回错误信息 +// * +// * @param code 错误码 +// * @param message 错误信息 +// * @return 错误信息对象 +// */ +// public static Result error(int code, String message) { +// Result result = new Result<>(); +// result.code = code; +// result.message = message; +// result.success = false; +// return result; +// } +// +// /** +// * 返回带数据的错误信息 +// * +// * @param code 错误码 +// * @param message 错误信息 +// * @param data 错误相关数据 +// * @return 错误信息对象 +// */ +// public static Result error(int code, String message, T data) { +// Result result = new Result<>(); +// result.code = code; +// result.message = message; +// result.success = false; +// result.data = data; +// return result; +// } +// +// /** +// * 从异常创建错误响应 +// * +// * @param e 异常 +// * @return 错误信息对象 +// */ +// public static Result error(Exception e) { +// Result result = new Result<>(); +// result.code = 500; +// result.message = e.getMessage() != null ? e.getMessage() : "系统异常"; +// result.success = false; +// return result; +// } +//} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/achat/core/Session.java b/src/main/java/xyz/wbsite/achat/core/Session.java new file mode 100644 index 0000000..9e862e8 --- /dev/null +++ b/src/main/java/xyz/wbsite/achat/core/Session.java @@ -0,0 +1,126 @@ +//package xyz.wbsite.achat.core; +// +// +///** +// * 会话 +// * +// * @author wangbing +// * @version 0.0.1 +// * @since 1.8 +// */ +//public class Session { +// /** +// * 主键 +// */ +// private String id; +// /** +// * 用户ID +// */ +// private String uid; +// private String title; +// private String model; +// private String prompt; +// private String temperature; +// private String topP; +// private String frequencyPenalty; +// private String presencePenalty; +// private String maxTokens; +// private String lastTime; +// private String lastMessage; +// +// public String getId() { +// return id; +// } +// +// public void setId(String id) { +// this.id = id; +// } +// +// public String getUid() { +// return uid; +// } +// +// public void setUid(String uid) { +// this.uid = uid; +// } +// +// public String getTitle() { +// return title; +// } +// +// public void setTitle(String title) { +// this.title = title; +// } +// +// public String getModel() { +// return model; +// } +// +// public void setModel(String model) { +// this.model = model; +// } +// +// public String getPrompt() { +// return prompt; +// } +// +// public void setPrompt(String prompt) { +// this.prompt = prompt; +// } +// +// public String getTemperature() { +// return temperature; +// } +// +// public void setTemperature(String temperature) { +// this.temperature = temperature; +// } +// +// public String getTopP() { +// return topP; +// } +// +// public void setTopP(String topP) { +// this.topP = topP; +// } +// +// public String getFrequencyPenalty() { +// return frequencyPenalty; +// } +// +// public void setFrequencyPenalty(String frequencyPenalty) { +// this.frequencyPenalty = frequencyPenalty; +// } +// +// public String getPresencePenalty() { +// return presencePenalty; +// } +// +// public void setPresencePenalty(String presencePenalty) { +// this.presencePenalty = presencePenalty; +// } +// +// public String getMaxTokens() { +// return maxTokens; +// } +// +// public void setMaxTokens(String maxTokens) { +// this.maxTokens = maxTokens; +// } +// +// public String getLastTime() { +// return lastTime; +// } +// +// public void setLastTime(String lastTime) { +// this.lastTime = lastTime; +// } +// +// public String getLastMessage() { +// return lastMessage; +// } +// +// public void setLastMessage(String lastMessage) { +// this.lastMessage = lastMessage; +// } +//} diff --git a/src/main/java/xyz/wbsite/achat/core/base/Attachment.java b/src/main/java/xyz/wbsite/achat/core/base/Attachment.java deleted file mode 100644 index 0549f73..0000000 --- a/src/main/java/xyz/wbsite/achat/core/base/Attachment.java +++ /dev/null @@ -1,30 +0,0 @@ -package xyz.wbsite.achat.core.base; - -/** - * 附件 - * - * @author wangbing - * @version 0.0.1 - * @since 1.8 - */ -public class Attachment { - private String filename; - - private String fid; - - public String getFilename() { - return filename; - } - - public void setFilename(String filename) { - this.filename = filename; - } - - public String getFid() { - return fid; - } - - public void setFid(String fid) { - this.fid = fid; - } -} diff --git a/src/main/java/xyz/wbsite/achat/core/base/Prompt.java b/src/main/java/xyz/wbsite/achat/core/base/Prompt.java deleted file mode 100644 index 7241df2..0000000 --- a/src/main/java/xyz/wbsite/achat/core/base/Prompt.java +++ /dev/null @@ -1,17 +0,0 @@ -package xyz.wbsite.achat.core.base; - -public class Prompt { - - /** - * 提示词 - */ - private String prompt; - - public String getPrompt() { - return prompt; - } - - public void setPrompt(String prompt) { - this.prompt = prompt; - } -} diff --git a/src/main/java/xyz/wbsite/achat/core/base/Result.java b/src/main/java/xyz/wbsite/achat/core/base/Result.java deleted file mode 100644 index 4ab91f7..0000000 --- a/src/main/java/xyz/wbsite/achat/core/base/Result.java +++ /dev/null @@ -1,209 +0,0 @@ -package xyz.wbsite.achat.core.base; - -import java.util.HashMap; -import java.util.Map; - -/** - * 接口响应结果基类 - * 泛型支持的数据响应封装,提供统一的响应格式和错误处理 - * - * @author wangbing - * @version 0.0.1 - * @since 1.8 - */ -public class Result { - - /** - * 响应状态码 - */ - private int code = 200; - - /** - * 响应消息 - */ - private String message = "success"; - - /** - * 响应数据 - */ - private T data; - - /** - * 是否成功 - */ - private boolean success = true; - - /** - * 错误详情,用于表单验证等场景 - */ - private Map errors; - - /** - * 请求ID,用于问题追踪 - */ - private String requestId; - - /** - * 响应时间戳 - */ - private long timestamp = System.currentTimeMillis(); - - public int getCode() { - return code; - } - - public Result setCode(int code) { - this.code = code; - return this; - } - - public String getMessage() { - return message; - } - - public Result setMessage(String message) { - this.message = message; - return this; - } - - public T getData() { - return data; - } - - public Result setData(T data) { - this.data = data; - return this; - } - - public boolean isSuccess() { - return success; - } - - public Result setSuccess(boolean success) { - this.success = success; - return this; - } - - public Map getErrors() { - return errors; - } - - public Result setErrors(Map errors) { - this.errors = errors; - return this; - } - - public String getRequestId() { - return requestId; - } - - public Result setRequestId(String requestId) { - this.requestId = requestId; - return this; - } - - public long getTimestamp() { - return timestamp; - } - - public Result setTimestamp(long timestamp) { - this.timestamp = timestamp; - return this; - } - - /** - * 添加字段级别的错误信息 - * - * @param field 字段名 - * @param error 错误信息 - * @return 当前结果对象,支持链式调用 - */ - public Result addError(String field, String error) { - if (errors == null) { - errors = new HashMap<>(); - } - errors.put(field, error); - return this; - } - - /** - * 返回成功信息 - * - * @return 结果 - */ - public static Result success() { - return new Result<>(); - } - - /** - * 返回带数据的成功信息 - * - * @param data 响应数据 - * @return 结果 - */ - public static Result success(T data) { - Result result = new Result<>(); - result.setData(data); - return result; - } - - /** - * 返回错误信息 - * - * @param message 错误信息 - * @return 错误信息对象 - */ - public static Result error(String message) { - Result result = new Result<>(); - result.message = message; - result.code = 500; - result.success = false; - return result; - } - - /** - * 返回错误信息 - * - * @param code 错误码 - * @param message 错误信息 - * @return 错误信息对象 - */ - public static Result error(int code, String message) { - Result result = new Result<>(); - result.code = code; - result.message = message; - result.success = false; - return result; - } - - /** - * 返回带数据的错误信息 - * - * @param code 错误码 - * @param message 错误信息 - * @param data 错误相关数据 - * @return 错误信息对象 - */ - public static Result error(int code, String message, T data) { - Result result = new Result<>(); - result.code = code; - result.message = message; - result.success = false; - result.data = data; - return result; - } - - /** - * 从异常创建错误响应 - * - * @param e 异常 - * @return 错误信息对象 - */ - public static Result error(Exception e) { - Result result = new Result<>(); - result.code = 500; - result.message = e.getMessage() != null ? e.getMessage() : "系统异常"; - result.success = false; - return result; - } -} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/achat/core/base/Session.java b/src/main/java/xyz/wbsite/achat/core/base/Session.java deleted file mode 100644 index d7be327..0000000 --- a/src/main/java/xyz/wbsite/achat/core/base/Session.java +++ /dev/null @@ -1,126 +0,0 @@ -package xyz.wbsite.achat.core.base; - - -/** - * 会话 - * - * @author wangbing - * @version 0.0.1 - * @since 1.8 - */ -public class Session { - /** - * 主键 - */ - private String id; - /** - * 用户ID - */ - private String uid; - private String title; - private String model; - private String prompt; - private String temperature; - private String topP; - private String frequencyPenalty; - private String presencePenalty; - private String maxTokens; - private String lastTime; - private String lastMessage; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getUid() { - return uid; - } - - public void setUid(String uid) { - this.uid = uid; - } - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getModel() { - return model; - } - - public void setModel(String model) { - this.model = model; - } - - public String getPrompt() { - return prompt; - } - - public void setPrompt(String prompt) { - this.prompt = prompt; - } - - public String getTemperature() { - return temperature; - } - - public void setTemperature(String temperature) { - this.temperature = temperature; - } - - public String getTopP() { - return topP; - } - - public void setTopP(String topP) { - this.topP = topP; - } - - public String getFrequencyPenalty() { - return frequencyPenalty; - } - - public void setFrequencyPenalty(String frequencyPenalty) { - this.frequencyPenalty = frequencyPenalty; - } - - public String getPresencePenalty() { - return presencePenalty; - } - - public void setPresencePenalty(String presencePenalty) { - this.presencePenalty = presencePenalty; - } - - public String getMaxTokens() { - return maxTokens; - } - - public void setMaxTokens(String maxTokens) { - this.maxTokens = maxTokens; - } - - public String getLastTime() { - return lastTime; - } - - public void setLastTime(String lastTime) { - this.lastTime = lastTime; - } - - public String getLastMessage() { - return lastMessage; - } - - public void setLastMessage(String lastMessage) { - this.lastMessage = lastMessage; - } -} diff --git a/src/main/java/xyz/wbsite/achat/core/chat/ChatCompletionChunk.java b/src/main/java/xyz/wbsite/achat/core/chat/ChatCompletionChunk.java new file mode 100644 index 0000000..bc239e7 --- /dev/null +++ b/src/main/java/xyz/wbsite/achat/core/chat/ChatCompletionChunk.java @@ -0,0 +1,170 @@ +package xyz.wbsite.achat.core.chat; + + +import java.util.ArrayList; +import java.util.List; + +/** + * OpenAI聊天完成流式响应块 - 符合OpenAI官方API规范 + * + * @author wangbing + * @version 0.0.1 + * @since 1.8 + */ +public class ChatCompletionChunk { + private String id; + private String object; + private long created; + private String model; + private List choices; + + private ChatCompletionChunk(Builder builder) { + this.id = builder.id; + this.object = builder.object; + this.created = builder.created; + this.model = builder.model; + this.choices = builder.choices; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String id; + private String object; + private long created; + private String model; + private List choices = new ArrayList<>(); + + public Builder id(String id) { + this.id = id; + return this; + } + + public Builder object(String object) { + this.object = object; + return this; + } + + public Builder created(long created) { + this.created = created; + return this; + } + + public Builder model(String model) { + this.model = model; + return this; + } + + public Builder choices(List choices) { + this.choices = choices; + return this; + } + + // 使用lambda表达式构建choices列表,优化代码可读性 + public Builder withChoices(java.util.function.Consumer> choicesConsumer) { + choicesConsumer.accept(this.choices); + return this; + } + + // 构建ChatCompletionChunk对象 + public ChatCompletionChunk build() { + return new ChatCompletionChunk(this); + } + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getObject() { + return object; + } + + public void setObject(String object) { + this.object = object; + } + + public long getCreated() { + return created; + } + + public void setCreated(long created) { + this.created = created; + } + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public List getChoices() { + return choices; + } + + public void setChoices(List choices) { + this.choices = choices; + } + + public static class Choice { + private int index = 0; + private Delta delta; + private String finish_reason; + + public Integer getIndex() { + return index; + } + + public void setIndex(Integer index) { + this.index = index; + } + + public Delta getDelta() { + return delta; + } + + public void setDelta(Delta delta) { + this.delta = delta; + } + + public String getFinish_reason() { + return finish_reason; + } + + public void setFinish_reason(String finish_reason) { + this.finish_reason = finish_reason; + } + } + + /** + * 增量内容对象 + */ + public static class Delta { + private String role; + private String content; + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + } +} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/achat/core/chat/ChatCompletionRequest.java b/src/main/java/xyz/wbsite/achat/core/chat/ChatCompletionRequest.java new file mode 100644 index 0000000..75d740f --- /dev/null +++ b/src/main/java/xyz/wbsite/achat/core/chat/ChatCompletionRequest.java @@ -0,0 +1,123 @@ +package xyz.wbsite.achat.core.chat; + +import xyz.wbsite.achat.core.message.Message; + +import java.util.List; + +/** + * OpenAI聊天完成请求 - 符合OpenAI官方API规范 + * + * @author wangbing + * @version 0.0.1 + * @since 1.8 + */ +public class ChatCompletionRequest { + private String model; + private List messages; + private Double temperature; + private Double top_p; + private Integer n; + private Boolean stream; + private List stop; + private Integer max_tokens; + private Double presence_penalty; + private Double frequency_penalty; + private Object logit_bias; + private String user; + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public List getMessages() { + return messages; + } + + public void setMessages(List messages) { + this.messages = messages; + } + + public Double getTemperature() { + return temperature; + } + + public void setTemperature(Double temperature) { + this.temperature = temperature; + } + + public Double getTop_p() { + return top_p; + } + + public void setTop_p(Double top_p) { + this.top_p = top_p; + } + + public Integer getN() { + return n; + } + + public void setN(Integer n) { + this.n = n; + } + + public Boolean getStream() { + return stream; + } + + public void setStream(Boolean stream) { + this.stream = stream; + } + + public List getStop() { + return stop; + } + + public void setStop(List stop) { + this.stop = stop; + } + + public Integer getMax_tokens() { + return max_tokens; + } + + public void setMax_tokens(Integer max_tokens) { + this.max_tokens = max_tokens; + } + + public Double getPresence_penalty() { + return presence_penalty; + } + + public void setPresence_penalty(Double presence_penalty) { + this.presence_penalty = presence_penalty; + } + + public Double getFrequency_penalty() { + return frequency_penalty; + } + + public void setFrequency_penalty(Double frequency_penalty) { + this.frequency_penalty = frequency_penalty; + } + + public Object getLogit_bias() { + return logit_bias; + } + + public void setLogit_bias(Object logit_bias) { + this.logit_bias = logit_bias; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } +} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/achat/core/chat/ChatCompletionResponse.java b/src/main/java/xyz/wbsite/achat/core/chat/ChatCompletionResponse.java new file mode 100644 index 0000000..e62983b --- /dev/null +++ b/src/main/java/xyz/wbsite/achat/core/chat/ChatCompletionResponse.java @@ -0,0 +1,201 @@ +package xyz.wbsite.achat.core.chat; + +import xyz.wbsite.achat.core.message.Message; + +import java.util.ArrayList; +import java.util.List; + +/** + * OpenAI聊天完成响应 - 符合OpenAI官方API规范 + * + * @author wangbing + * @version 0.0.1 + * @since 1.8 + */ +public class ChatCompletionResponse { + private String id; + private String object; + private long created; + private String model; + private List choices; + private Usage usage; + + // 无参构造函数 + public ChatCompletionResponse() { + } + + // 私有构造函数,用于Builder模式 + private ChatCompletionResponse(Builder builder) { + this.id = builder.id; + this.object = builder.object; + this.created = builder.created; + this.model = builder.model; + this.choices = builder.choices; + this.usage = builder.usage; + } + + // 静态builder方法,返回Builder实例 + public static Builder builder() { + return new Builder(); + } + + // Builder内部类 + public static class Builder { + private String id; + private String object; + private long created; + private String model; + private List choices = new ArrayList<>(); + private Usage usage; + + public Builder id(String id) { + this.id = id; + return this; + } + + public Builder object(String object) { + this.object = object; + return this; + } + + public Builder created(long created) { + this.created = created; + return this; + } + + public Builder model(String model) { + this.model = model; + return this; + } + + // 设置整个choices列表 + public Builder choices(List choices) { + this.choices = choices; + return this; + } + + // 使用lambda表达式构建choices列表,优化代码可读性 + public Builder withChoices(java.util.function.Consumer> choicesConsumer) { + choicesConsumer.accept(this.choices); + return this; + } + + public Builder usage(Usage usage) { + this.usage = usage; + return this; + } + + public ChatCompletionResponse build() { + return new ChatCompletionResponse(this); + } + } + + // Getters and Setters + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getObject() { + return object; + } + + public void setObject(String object) { + this.object = object; + } + + public long getCreated() { + return created; + } + + public void setCreated(long created) { + this.created = created; + } + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public List getChoices() { + return choices; + } + + public void setChoices(List choices) { + this.choices = choices; + } + + public Usage getUsage() { + return usage; + } + + public void setUsage(Usage usage) { + this.usage = usage; + } + + public static class Choice { + private int index = 0; + private Message message; + private String finish_reason; + + public Integer getIndex() { + return index; + } + + public void setIndex(Integer index) { + this.index = index; + } + + public Message getMessage() { + return message; + } + + public void setMessage(Message message) { + this.message = message; + } + + public String getFinish_reason() { + return finish_reason; + } + + public void setFinish_reason(String finish_reason) { + this.finish_reason = finish_reason; + } + } + + public static class Usage { + private int prompt_tokens; + private int completion_tokens; + private int total_tokens; + + public int getPrompt_tokens() { + return prompt_tokens; + } + + public void setPrompt_tokens(int prompt_tokens) { + this.prompt_tokens = prompt_tokens; + } + + public int getCompletion_tokens() { + return completion_tokens; + } + + public void setCompletion_tokens(int completion_tokens) { + this.completion_tokens = completion_tokens; + } + + public int getTotal_tokens() { + return total_tokens; + } + + public void setTotal_tokens(int total_tokens) { + this.total_tokens = total_tokens; + } + } +} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/achat/core/chat/CompletionRequest.java b/src/main/java/xyz/wbsite/achat/core/chat/CompletionRequest.java new file mode 100644 index 0000000..939c369 --- /dev/null +++ b/src/main/java/xyz/wbsite/achat/core/chat/CompletionRequest.java @@ -0,0 +1,67 @@ +package xyz.wbsite.achat.core.chat; + +/** + * 文本补全请求 + * + * @author wangbing + * @version 0.0.1 + * @since 1.8 + */ +public class CompletionRequest { + private String model; + private String prompt; + private boolean stream; + + private Double temperature; + private Integer max_tokens; + private Double top_p; + private Integer n; + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public String getPrompt() { + return prompt; + } + + public void setPrompt(String prompt) { + this.prompt = prompt; + } + + public Integer getMax_tokens() { + return max_tokens; + } + + public void setMax_tokens(Integer max_tokens) { + this.max_tokens = max_tokens; + } + + public Double getTemperature() { + return temperature; + } + + public void setTemperature(Double temperature) { + this.temperature = temperature; + } + + public Double getTop_p() { + return top_p; + } + + public void setTop_p(Double top_p) { + this.top_p = top_p; + } + + public Integer getN() { + return n; + } + + public void setN(Integer n) { + this.n = n; + } +} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/achat/core/chat/CompletionResponse.java b/src/main/java/xyz/wbsite/achat/core/chat/CompletionResponse.java new file mode 100644 index 0000000..cd49dd7 --- /dev/null +++ b/src/main/java/xyz/wbsite/achat/core/chat/CompletionResponse.java @@ -0,0 +1,197 @@ +package xyz.wbsite.achat.core.chat; + +import xyz.wbsite.achat.core.message.Message; + +import java.util.ArrayList; +import java.util.List; + +/** + * OpenAI聊天完成响应 - 符合OpenAI官方API规范 + * + * @author wangbing + * @version 0.0.1 + * @since 1.8 + */ +public class CompletionResponse { + private String id; + private String object; + private long created; + private String model; + private List choices; + private Usage usage; + + private CompletionResponse(Builder builder) { + this.id = builder.id; + this.object = builder.object; + this.created = builder.created; + this.model = builder.model; + this.choices = builder.choices; + this.usage = builder.usage; + } + + // 静态builder方法,返回Builder实例 + public static Builder builder() { + return new Builder(); + } + + // Builder内部类 + public static class Builder { + private String id; + private String object; + private long created; + private String model; + private List choices = new ArrayList<>(); + private Usage usage; + + public Builder id(String id) { + this.id = id; + return this; + } + + public Builder object(String object) { + this.object = object; + return this; + } + + public Builder created(long created) { + this.created = created; + return this; + } + + public Builder model(String model) { + this.model = model; + return this; + } + + // 设置整个choices列表 + public Builder choices(List choices) { + this.choices = choices; + return this; + } + + // 使用lambda表达式构建choices列表,优化代码可读性 + public Builder withChoices(java.util.function.Consumer> choicesConsumer) { + choicesConsumer.accept(this.choices); + return this; + } + + public Builder usage(Usage usage) { + this.usage = usage; + return this; + } + + // 构建CompletionResponse对象 + public CompletionResponse build() { + return new CompletionResponse(this); + } + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getObject() { + return object; + } + + public void setObject(String object) { + this.object = object; + } + + public long getCreated() { + return created; + } + + public void setCreated(long created) { + this.created = created; + } + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public List getChoices() { + return choices; + } + + public void setChoices(List choices) { + this.choices = choices; + } + + public Usage getUsage() { + return usage; + } + + public void setUsage(Usage usage) { + this.usage = usage; + } + + + public static class Choice { + private int index = 0; + private Message message; + private String finish_reason; + + public Integer getIndex() { + return index; + } + + public void setIndex(Integer index) { + this.index = index; + } + + public Message getMessage() { + return message; + } + + public void setMessage(Message message) { + this.message = message; + } + + public String getFinish_reason() { + return finish_reason; + } + + public void setFinish_reason(String finish_reason) { + this.finish_reason = finish_reason; + } + } + + public static class Usage { + private int prompt_tokens; + private int completion_tokens; + private int total_tokens; + + public int getPrompt_tokens() { + return prompt_tokens; + } + + public void setPrompt_tokens(int prompt_tokens) { + this.prompt_tokens = prompt_tokens; + } + + public int getCompletion_tokens() { + return completion_tokens; + } + + public void setCompletion_tokens(int completion_tokens) { + this.completion_tokens = completion_tokens; + } + + public int getTotal_tokens() { + return total_tokens; + } + + public void setTotal_tokens(int total_tokens) { + this.total_tokens = total_tokens; + } + } +} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/achat/core/event/CompleteEvent.java b/src/main/java/xyz/wbsite/achat/core/event/CompleteEvent.java index 3b9864d..e00f277 100644 --- a/src/main/java/xyz/wbsite/achat/core/event/CompleteEvent.java +++ b/src/main/java/xyz/wbsite/achat/core/event/CompleteEvent.java @@ -1,22 +1,103 @@ -package xyz.wbsite.achat.core.event; - -import xyz.wbsite.achat.core.base.Event; - -/** - * 完成事件 - * - * @author wangbing - * @version 0.0.1 - * @since 1.8 - */ -public class CompleteEvent extends Event { - - public CompleteEvent(String sid) { - super(sid); - setType("complete"); - } - - public static CompleteEvent of(String sid) { - return new CompleteEvent(sid); - } -} +//package xyz.wbsite.achat.core.event; +// +//import xyz.wbsite.achat.core.Event; +// +///** +// * 完成事件 +// * 用于标识流式响应的结束 +// * +// * @author wangbing +// * @version 0.0.1 +// * @since 1.8 +// */ +//public class CompleteEvent extends Event { +// +// /** +// * 完整响应内容 +// */ +// private String content; +// +// /** +// * 构造函数 +// * +// * @param sid 会话ID +// */ +// public CompleteEvent(String sid) { +// super(); +// this.setSid(sid); +// this.setObject("chat.completion"); +// } +// +// /** +// * 生成的令牌总数 +// */ +// private Integer completionTokens; +// +// /** +// * 提示词的令牌数量 +// */ +// private Integer promptTokens; +// +// /** +// * 总令牌数量 +// */ +// private Integer totalTokens; +// +// /** +// * 完成状态 +// */ +// private String finishReason; +// +// /** +// * 生成用时(毫秒) +// */ +// private Long generationTime; +// +// public String getContent() { +// return content; +// } +// +// public void setContent(String content) { +// this.content = content; +// } +// +// public Integer getCompletionTokens() { +// return completionTokens; +// } +// +// public void setCompletionTokens(Integer completionTokens) { +// this.completionTokens = completionTokens; +// } +// +// public Integer getPromptTokens() { +// return promptTokens; +// } +// +// public void setPromptTokens(Integer promptTokens) { +// this.promptTokens = promptTokens; +// } +// +// public Integer getTotalTokens() { +// return totalTokens; +// } +// +// public void setTotalTokens(Integer totalTokens) { +// this.totalTokens = totalTokens; +// } +// +// public String getFinishReason() { +// return finishReason; +// } +// +// public void setFinishReason(String finishReason) { +// this.finishReason = finishReason; +// } +// +// public Long getGenerationTime() { +// return generationTime; +// } +// +// public void setGenerationTime(Long generationTime) { +// this.generationTime = generationTime; +// } +//} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/achat/core/event/PartialEvent.java b/src/main/java/xyz/wbsite/achat/core/event/PartialEvent.java index dd6ac2b..0e01846 100644 --- a/src/main/java/xyz/wbsite/achat/core/event/PartialEvent.java +++ b/src/main/java/xyz/wbsite/achat/core/event/PartialEvent.java @@ -1,29 +1,80 @@ -package xyz.wbsite.achat.core.event; - -import xyz.wbsite.achat.core.base.Event; - -/** - * 部分消息事件 - *

- * 用于流式会话下表示AI回复的部分消息 - * - * @author wangbing - * @version 0.0.1 - * @since 1.8 - */ -public class PartialEvent extends Event { - public PartialEvent(String sid) { - super(sid); - setType("partial"); - } - - public PartialEvent(String sid, String partial) { - super(sid); - setType("partial"); - setText(partial); - } - - public static PartialEvent of(String sid, String partial) { - return new PartialEvent(sid, partial); - } -} +//package xyz.wbsite.achat.core.event; +// +// +//import xyz.wbsite.achat.core.Event; +// +///** +// * 分段内容事件 +// * 用于推送流式响应的部分内容 +// * +// * @author wangbing +// * @version 0.0.1 +// * @since 1.8 +// */ +//public class PartialEvent extends Event { +// +// /** +// * 分段内容 +// */ +// private String content; +// +// /** +// * 构造函数 +// * +// * @param sid 会话ID +// * @param partial 部分内容 +// */ +// public PartialEvent(String sid, String partial) { +// super(); +// this.setSid(sid); +// this.content = partial; +// this.setObject("chat.completion.chunk"); +// } +// +// /** +// * 是否是最后一段 +// */ +// private Boolean isFinal; +// +// /** +// * 完成率,范围0-1 +// */ +// private Double finishRate; +// +// /** +// * 当前累积的生成令牌数量 +// */ +// private Integer completionTokens; +// +// public String getContent() { +// return content; +// } +// +// public void setContent(String content) { +// this.content = content; +// } +// +// public Boolean getIsFinal() { +// return isFinal; +// } +// +// public void setIsFinal(Boolean isFinal) { +// this.isFinal = isFinal; +// } +// +// public Double getFinishRate() { +// return finishRate; +// } +// +// public void setFinishRate(Double finishRate) { +// this.finishRate = finishRate; +// } +// +// public Integer getCompletionTokens() { +// return completionTokens; +// } +// +// public void setCompletionTokens(Integer completionTokens) { +// this.completionTokens = completionTokens; +// } +//} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/achat/core/event/StartEvent.java b/src/main/java/xyz/wbsite/achat/core/event/StartEvent.java new file mode 100644 index 0000000..fadeae2 --- /dev/null +++ b/src/main/java/xyz/wbsite/achat/core/event/StartEvent.java @@ -0,0 +1,66 @@ +//package xyz.wbsite.achat.core.event; +// +//import xyz.wbsite.achat.core.Event; +// +///** +// * 开始推送事件 +// * 用于标识流式响应的开始 +// * +// * @author wangbing +// * @version 0.0.1 +// * @since 1.8 +// */ +//public class StartEvent extends Event { +// +// /** +// * 提示词令牌数量 +// */ +// private Integer promptTokens; +// +// /** +// * 最大令牌数量限制 +// */ +// private Integer maxTokens; +// +// /** +// * 温度参数 +// */ +// private Double temperature; +// +// /** +// * 随机种子 +// */ +// private Integer seed; +// +// public Integer getPromptTokens() { +// return promptTokens; +// } +// +// public void setPromptTokens(Integer promptTokens) { +// this.promptTokens = promptTokens; +// } +// +// public Integer getMaxTokens() { +// return maxTokens; +// } +// +// public void setMaxTokens(Integer maxTokens) { +// this.maxTokens = maxTokens; +// } +// +// public Double getTemperature() { +// return temperature; +// } +// +// public void setTemperature(Double temperature) { +// this.temperature = temperature; +// } +// +// public Integer getSeed() { +// return seed; +// } +// +// public void setSeed(Integer seed) { +// this.seed = seed; +// } +//} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/achat/core/message/MessageSseEmitter.java b/src/main/java/xyz/wbsite/achat/core/message/MessageSseEmitter.java deleted file mode 100644 index c873a05..0000000 --- a/src/main/java/xyz/wbsite/achat/core/message/MessageSseEmitter.java +++ /dev/null @@ -1,176 +0,0 @@ -package xyz.wbsite.achat.core.message; - -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import xyz.wbsite.achat.core.base.Event; -import xyz.wbsite.achat.core.event.CompleteEvent; -import xyz.wbsite.achat.core.event.PartialEvent; -import xyz.wbsite.achat.core.prompt.MessagePrompt; -import xyz.wbsite.achat.core.service.MessageGenerator; - -import java.io.IOException; - -/** - * 对话SSE发射器 - * 只负责SSE通信,不包含业务逻辑 - * - * @author wangbing - * @version 0.0.1 - * @since 1.8 - */ -public class MessageSseEmitter extends SseEmitter { - - /** - * 用户消息 - */ - private MessagePrompt messagePrompt; - - /** - * 是否完成 - */ - private boolean complete; - - /** - * AI回答 - */ - private final StringBuilder answer = new StringBuilder(); - - /** - * 消息处理器 - */ - private MessageGenerator messageGenerator; - - /** - * 构造函数 - * - * @param message 消息对象 - */ - public MessageSseEmitter(MessagePrompt message, MessageGenerator processor) { - super(0L); - this.messagePrompt = message; - this.messageGenerator = processor; -// TaskUtil.taskAsync(task); - } - - /** - * 消息处理任务 - */ -// public Runnable task = () -> { -// try { -// // 检查会话是否存在 -// if (!messageProcessor.checkSessionExists(message.getChatId(), uid)) { -// this.sendMessage(createPartialMessage("当前会话不存在,请刷新后再试!")); -// return; -// } -// -// // 更新新会话的标题(如果为空) -// messageProcessor.updateSessionTitleIfEmpty(message.getChatId(), message.getText(), uid); -// -// // 保存本次用户消息 -// messageProcessor.saveUserMessage(message.getChatId(), message.getText(), uid); -// -// // 根据是否有附件选择不同的处理方式 -// TokenStream tokenStream; -// if (this.hasAttachment()) { -// String attachment = messageProcessor.parseAttachment(message.getAttachments(), uid); -// tokenStream = messageProcessor.createAssistantStreamWithAttachment( -// message.getChatId(), message.getText(), attachment, uid); -// } else { -// tokenStream = messageProcessor.createAssistantStream( -// message.getChatId(), message.getText(), uid); -// } -// -// // 设置流回调 -// tokenStream -// .onPartialResponse(this::onPartialResponse) -// .onCompleteResponse(this::onCompleteResponse) -// .onError(this::onError) -// .start(); -// } catch (Exception e) { -// onError(e); -// } -// }; - - /** - * 错误处理 - */ - private void onError(Throwable e) { - this.sendMessage(createPartialMessage("" + e.getMessage())); - this.answer.append("" + e.getMessage()); - this.onCompleteResponse(null); - } - - /** - * 部分响应处理 - */ - public void onPartialResponse(String msg) { - if (complete) { - return; - } - this.sendMessage(createPartialMessage(msg)); - this.answer.append(msg); - } - - /** - * 完成响应处理 - */ - public void onCompleteResponse(Object chatResponse) { - if (this.complete) { - return; - } - // 推送结束 - this.sendMessage(createCompleteMessage()); - // 关闭链接 - this.complete(); - } - - /** - * 创建部分消息事件 - */ - private Event createPartialMessage(String partial) { - return new PartialEvent(messagePrompt.getSid(), partial); - } - - /** - * 创建完成消息事件 - */ - private Event createCompleteMessage() { - return new CompleteEvent(messagePrompt.getSid()); - } - - /** - * 重写send方法,处理异常 - */ - @Override - public void send(SseEventBuilder builder) throws IOException { - try { - super.send(builder); - } catch (Exception e) { - complete = true; - } - } - - /** - * 发送消息 - */ - private void sendMessage(Event message) { - try { - this.send(message); - } catch (Exception e) { - complete = true; - } - } - - /** - * 获取完成状态 - */ - public boolean isComplete() { - return complete; - } - - /** - * 设置完成状态 - */ - public void setComplete(boolean complete) { - this.complete = complete; - } -} diff --git a/src/main/java/xyz/wbsite/achat/core/message/StreamEmitter.java b/src/main/java/xyz/wbsite/achat/core/message/StreamEmitter.java new file mode 100644 index 0000000..65150c3 --- /dev/null +++ b/src/main/java/xyz/wbsite/achat/core/message/StreamEmitter.java @@ -0,0 +1,206 @@ +package xyz.wbsite.achat.core.message; + +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; +import xyz.wbsite.achat.core.chat.ChatCompletionRequest; +import xyz.wbsite.achat.core.service.ChatCompletionGenerator; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * 流式对话生成器 + * + * @author wangbing + * @version 0.0.1 + * @since 1.8 + */ +public class StreamEmitter extends SseEmitter { + + /** + * 用户消息 + */ + private ChatCompletionRequest chatCompletionRequest; + + /** + * 是否完成 + */ + private boolean complete; + + /** + * AI回答 + */ + private final StringBuilder answer = new StringBuilder(); + + /** + * 消息处理器 + */ + private ChatCompletionGenerator chatCompletionGenerator; + + /** + * 线程池,用于执行消息生成器 + */ + private static final ExecutorService messageExecutor = Executors.newFixedThreadPool( + Math.max(4, Runtime.getRuntime().availableProcessors()), + r -> { + Thread thread = new Thread(r, "message-generator-thread"); + thread.setDaemon(true); + return thread; + } + ); + + private StreamEmitter(Builder builder) { + super(Long.MAX_VALUE); + this.chatCompletionRequest = builder.chatCompletionRequest; + this.chatCompletionGenerator = builder.chatCompletionGenerator; + + // 使用线程池执行MessageGenerator + if (this.chatCompletionGenerator != null && this.chatCompletionRequest != null) { + messageExecutor.execute(() -> { + try { + this.chatCompletionGenerator.on(this, this.chatCompletionRequest); + } catch (Exception e) { + onError(e); + } finally { + if (!isComplete()) { + onCompleteResponse(null); + } + } + }); + } + } + + // 静态builder方法,返回Builder实例 + public static Builder builder() { + return new Builder(); + } + + // Builder内部类 + public static class Builder { + private ChatCompletionRequest chatCompletionRequest; + private ChatCompletionGenerator chatCompletionGenerator; + + public Builder chatCompletionRequest(ChatCompletionRequest chatCompletionRequest) { + this.chatCompletionRequest = chatCompletionRequest; + return this; + } + + public Builder chatCompletionGenerator(ChatCompletionGenerator chatCompletionGenerator) { + this.chatCompletionGenerator = chatCompletionGenerator; + return this; + } + + // 构建StreamEmitter对象 + public StreamEmitter build() { + return new StreamEmitter(this); + } + } + + // 保留原有的构造函数以保持向后兼容 + public StreamEmitter(ChatCompletionRequest chatCompletionRequest, ChatCompletionGenerator chatCompletionGenerator) { + super(Long.MAX_VALUE); + this.chatCompletionRequest = chatCompletionRequest; + this.chatCompletionGenerator = chatCompletionGenerator; + + // 使用线程池执行MessageGenerator + if (chatCompletionGenerator != null && chatCompletionRequest != null) { + messageExecutor.execute(() -> { + try { + chatCompletionGenerator.on(this, chatCompletionRequest); + } catch (Exception e) { + onError(e); + } finally { + if (!isComplete()) { + onCompleteResponse(null); + } + } + }); + } + } + + /** + * 错误处理 + */ + private void onError(Throwable e) { +// this.sendMessage(createPartialMessage("" + e.getMessage())); + this.answer.append("" + e.getMessage()); + this.onCompleteResponse(null); + } + + /** + * 部分响应处理 + */ + public void onPartialResponse(String msg) { + if (complete) { + return; + } +// this.sendMessage(createPartialMessage(msg)); + this.answer.append(msg); + } + + /** + * 完成响应处理 + */ + public void onCompleteResponse(Object chatResponse) { + if (this.complete) { + return; + } + // 推送结束 +// this.sendMessage(createCompleteMessage()); + // 关闭链接 + this.complete(); + } + +// /** +// * 创建部分消息事件 +// */ +// private Event createPartialMessage(String partial) { +// return new PartialEvent(messagePrompt.getSid(), partial); +// } +// +// /** +// * 创建完成消息事件 +// */ +// private Event createCompleteMessage() { +// return new CompleteEvent(messagePrompt.getSid()); +// } + + /** + * 重写send方法,处理异常 + */ + @Override + public void send(SseEventBuilder builder) { + try { + super.send(builder); + } catch (Exception e) { + complete = true; + } + } + + public void callStart() { + } + + /** + * 发送消息 + */ + private void sendMessage(Object message) { + try { + this.send(message); + } catch (Exception e) { + complete = true; + } + } + + /** + * 获取完成状态 + */ + public boolean isComplete() { + return complete; + } + + /** + * 设置完成状态 + */ + public void setComplete(boolean complete) { + this.complete = complete; + } +} diff --git a/src/main/java/xyz/wbsite/achat/core/model/Model.java b/src/main/java/xyz/wbsite/achat/core/model/Model.java new file mode 100644 index 0000000..d77d74d --- /dev/null +++ b/src/main/java/xyz/wbsite/achat/core/model/Model.java @@ -0,0 +1,80 @@ +package xyz.wbsite.achat.core.model; + +import java.util.List; + +/** + * OpenAI模型对象 - 符合OpenAI官方API规范 + * + * @author wangbing + * @version 0.0.1 + * @since 1.8 + */ +public class Model { + private String id; + private String object; + private long created; + private String owned_by; + private List permission; + private String root; + private String parent; + + // Getters and Setters + public String getId() { return id; } + public void setId(String id) { this.id = id; } + public String getObject() { return object; } + public void setObject(String object) { this.object = object; } + public long getCreated() { return created; } + public void setCreated(long created) { this.created = created; } + public String getOwned_by() { return owned_by; } + public void setOwned_by(String owned_by) { this.owned_by = owned_by; } + public List getPermission() { return permission; } + public void setPermission(List permission) { this.permission = permission; } + public String getRoot() { return root; } + public void setRoot(String root) { this.root = root; } + public String getParent() { return parent; } + public void setParent(String parent) { this.parent = parent; } + + /** + * 模型权限对象 + */ + public static class Permission { + private String id; + private String object; + private long created; + private boolean allow_create_engine; + private boolean allow_sampling; + private boolean allow_logprobs; + private boolean allow_search_indices; + private boolean allow_view; + private boolean allow_fine_tuning; + private String organization; + private String group; + private boolean is_blocking; + + // Getters and Setters + public String getId() { return id; } + public void setId(String id) { this.id = id; } + public String getObject() { return object; } + public void setObject(String object) { this.object = object; } + public long getCreated() { return created; } + public void setCreated(long created) { this.created = created; } + public boolean isAllow_create_engine() { return allow_create_engine; } + public void setAllow_create_engine(boolean allow_create_engine) { this.allow_create_engine = allow_create_engine; } + public boolean isAllow_sampling() { return allow_sampling; } + public void setAllow_sampling(boolean allow_sampling) { this.allow_sampling = allow_sampling; } + public boolean isAllow_logprobs() { return allow_logprobs; } + public void setAllow_logprobs(boolean allow_logprobs) { this.allow_logprobs = allow_logprobs; } + public boolean isAllow_search_indices() { return allow_search_indices; } + public void setAllow_search_indices(boolean allow_search_indices) { this.allow_search_indices = allow_search_indices; } + public boolean isAllow_view() { return allow_view; } + public void setAllow_view(boolean allow_view) { this.allow_view = allow_view; } + public boolean isAllow_fine_tuning() { return allow_fine_tuning; } + public void setAllow_fine_tuning(boolean allow_fine_tuning) { this.allow_fine_tuning = allow_fine_tuning; } + public String getOrganization() { return organization; } + public void setOrganization(String organization) { this.organization = organization; } + public String getGroup() { return group; } + public void setGroup(String group) { this.group = group; } + public boolean isIs_blocking() { return is_blocking; } + public void setIs_blocking(boolean is_blocking) { this.is_blocking = is_blocking; } + } +} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/achat/core/model/ModelListResponse.java b/src/main/java/xyz/wbsite/achat/core/model/ModelListResponse.java new file mode 100644 index 0000000..9024d8f --- /dev/null +++ b/src/main/java/xyz/wbsite/achat/core/model/ModelListResponse.java @@ -0,0 +1,21 @@ +package xyz.wbsite.achat.core.model; + +import java.util.List; + +/** + * OpenAI模型列表响应 - 符合OpenAI官方API规范 + * + * @author wangbing + * @version 0.0.1 + * @since 1.8 + */ +public class ModelListResponse { + private String object; + private List data; + + // Getters and Setters + public String getObject() { return object; } + public void setObject(String object) { this.object = object; } + public List getData() { return data; } + public void setData(List data) { this.data = data; } +} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/achat/core/service/ChatCompletionGenerator.java b/src/main/java/xyz/wbsite/achat/core/service/ChatCompletionGenerator.java new file mode 100644 index 0000000..894568e --- /dev/null +++ b/src/main/java/xyz/wbsite/achat/core/service/ChatCompletionGenerator.java @@ -0,0 +1,18 @@ +package xyz.wbsite.achat.core.service; + +import xyz.wbsite.achat.core.chat.ChatCompletionRequest; +import xyz.wbsite.achat.core.message.StreamEmitter; + +/** + * 推理生成器 + *

+ * 抽象出来用于生成消息的实现层 + * + * @author wangbing + * @version 0.0.1 + * @since 1.8 + */ +public interface ChatCompletionGenerator { + + void on(StreamEmitter emitter, ChatCompletionRequest chatCompletionRequest); +} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/achat/core/service/ChatService.java b/src/main/java/xyz/wbsite/achat/core/service/ChatService.java new file mode 100644 index 0000000..583eade --- /dev/null +++ b/src/main/java/xyz/wbsite/achat/core/service/ChatService.java @@ -0,0 +1,16 @@ +package xyz.wbsite.achat.core.service; + +import xyz.wbsite.achat.core.chat.ChatCompletionRequest; +import xyz.wbsite.achat.core.chat.ChatCompletionResponse; +import xyz.wbsite.achat.core.chat.CompletionRequest; +import xyz.wbsite.achat.core.chat.CompletionResponse; +import xyz.wbsite.achat.core.message.StreamEmitter; + +public interface ChatService { + + CompletionResponse prompt(CompletionRequest request); + + ChatCompletionResponse chat(ChatCompletionRequest request); + + StreamEmitter streamChat(ChatCompletionRequest request); +} diff --git a/src/main/java/xyz/wbsite/achat/core/service/MessageGenerator.java b/src/main/java/xyz/wbsite/achat/core/service/MessageGenerator.java deleted file mode 100644 index c08ef2c..0000000 --- a/src/main/java/xyz/wbsite/achat/core/service/MessageGenerator.java +++ /dev/null @@ -1,18 +0,0 @@ -package xyz.wbsite.achat.core.service; - -import xyz.wbsite.achat.core.message.MessageSseEmitter; -import xyz.wbsite.achat.core.base.Message; - -/** - * 消息生成器 - *

- * 抽象出来用于生成消息的实现层 - * - * @author wangbing - * @version 0.0.1 - * @since 1.8 - */ -public interface MessageGenerator { - - void on(MessageSseEmitter emitter, Message message); -} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/achat/core/service/impl/ChatServiceSampleImpl.java b/src/main/java/xyz/wbsite/achat/core/service/impl/ChatServiceSampleImpl.java new file mode 100644 index 0000000..0c5ce4d --- /dev/null +++ b/src/main/java/xyz/wbsite/achat/core/service/impl/ChatServiceSampleImpl.java @@ -0,0 +1,110 @@ +package xyz.wbsite.achat.core.service.impl; + +import xyz.wbsite.achat.core.chat.ChatCompletionRequest; +import xyz.wbsite.achat.core.chat.ChatCompletionResponse; +import xyz.wbsite.achat.core.chat.CompletionRequest; +import xyz.wbsite.achat.core.chat.CompletionResponse; +import xyz.wbsite.achat.core.message.Message; +import xyz.wbsite.achat.core.message.Role; +import xyz.wbsite.achat.core.message.StreamEmitter; +import xyz.wbsite.achat.core.service.ChatService; + +import java.util.ArrayList; +import java.util.List; + +public class ChatServiceSampleImpl implements ChatService { + @Override + public CompletionResponse prompt(CompletionRequest request) { + CompletionResponse response = new CompletionResponse(); + response.setObject("chat.completion"); + response.setCreated(System.currentTimeMillis() / 1000); + response.setModel(request.getModel()); + +// List choices = new ArrayList<>(); +// choices.add(Choice.builder().index(0).message(Message.builder().role(Role.ASSISTANT).content("您好,我还没有接入AI,请接入后再试!").build()).finish_reason("stop").build()); +// response.setChoices(choices); +// response.setUsage(Usage.builder().prompt_tokens(10).completion_tokens(20).total_tokens(30).build()); + + return CompletionResponse.builder() + .id("chatcmpl-" + System.currentTimeMillis()) + .object("chat.completion") + .created(System.currentTimeMillis() / 1000) + .model(request.getModel()) + .withChoices(choices -> { +// Choice choice1 = new Choice(); + CompletionResponse.Choice + choice1.setIndex(0); + choice1.setMessage(new Message()); + choice1.setFinish_reason("stop"); + choices.add(choice1); + + Choice choice2 = new Choice(); + choice2.setIndex(1); + choice2.setMessage(new Message()); + choice2.setFinish_reason("stop"); + choices.add(choice2); + }) + + .usage(CompletionResponse.UsageBuilder().prompt_tokens(10).completion_tokens(10).total_tokens(10).build()) + .build(); + } + + @Override + public ChatCompletionResponse chat(ChatCompletionRequest request) { + ChatCompletionResponse response = new ChatCompletionResponse(); + response.setId("chatcmpl-" + System.currentTimeMillis()); + response.setObject("chat.completion"); + response.setCreated(System.currentTimeMillis() / 1000); + response.setModel(request.getModel()); + + List choices = new ArrayList<>(); + choices.add(Choice.builder().index(0).message(Message.builder().role(Role.ASSISTANT).content("您好,我还没有接入AI,请接入后再试!").build()).finish_reason("stop").build()); + response.setChoices(choices); + response.setUsage(Usage.builder().prompt_tokens(10).completion_tokens(20).total_tokens(30).build()); + return response; + } + + @Override + public StreamEmitter streamChat(ChatCompletionRequest request) { + // 验证请求参数 + if (request.getModel() == null) { + throw new IllegalArgumentException("模型不能为空"); + } + if (request.getMessages() == null || request.getMessages().isEmpty()) { + throw new IllegalArgumentException("消息不能为空"); + } + return StreamEmitter.builder() + .chatCompletionRequest(request) + .build(); + +// SseEmitter emitter = new SseEmitter(Long.MAX_VALUE); + // 在单独的线程中处理流式响应 +// new Thread(() -> { +// try { +// // 模拟流式响应的逻辑 +// // 实际应用中应从服务层获取流式数据并发送 +// String id = "chatcmpl-" + System.currentTimeMillis(); +// long created = System.currentTimeMillis() / 1000; +// String model = request.getModel(); +// +// // 发送初始数据块 +// ChatCompletionChunk chunk = new ChatCompletionChunk(); +// chunk.setId(id); +// chunk.setObject("chat.completion.chunk"); +// chunk.setCreated(created); +// chunk.setModel(model); +// // chunk.setChoices(/* 实际的选择项列表 */); +// emitter.send(chunk, MediaType.APPLICATION_JSON); +// +// // 发送更多数据块... +// +// // 发送完成信号 +// emitter.complete(); +// } catch (Exception e) { +// emitter.completeWithError(e); +// } +// }).start(); +// +// return emitter; + } +}