tools;
+
+ /**
+ * 取值:String或者ToolChoiceObj
+ *
+ * @since 1.1.2
+ */
+ @JsonProperty("tool_choice")
+ private Object toolChoice;
+
+ /**
+ * 使用什么取样温度,0到2之间。较高的值(如0.8)将使输出更加随机,而较低的值(如0.2)将使输出更加集中和确定。
+ *
+ * We generally recommend altering this or but not both.top_p
+ */
+ @Builder.Default
+ private double temperature = 0.2;
+
+ /**
+ * 使用温度采样的替代方法称为核心采样,其中模型考虑具有top_p概率质量的令牌的结果。因此,0.1 意味着只考虑包含前 10% 概率质量的代币。
+ *
+ * 我们通常建议更改此设置,但不要同时更改两者。temperature
+ */
+ @JsonProperty("top_p")
+ @Builder.Default
+ private Double topP = 1d;
+
+
+ /**
+ * 为每个提示生成的完成次数。
+ */
+ @Builder.Default
+ private Integer n = 1;
+
+
+ /**
+ * 是否流式输出.
+ * default:false
+ */
+ @Builder.Default
+ private boolean stream = false;
+ /**
+ * 停止输出标识
+ */
+ private List stop;
+ /**
+ * 最大支持4096
+ */
+ @JsonProperty("max_tokens")
+ @Builder.Default
+ private Integer maxTokens = 2048;
+
+
+ @JsonProperty("presence_penalty")
+ @Builder.Default
+ private double presencePenalty = 0;
+
+ /**
+ * -2.0 ~~ 2.0
+ */
+ @JsonProperty("frequency_penalty")
+ @Builder.Default
+ private double frequencyPenalty = 0;
+
+ @JsonProperty("logit_bias")
+ private Map logitBias;
+ /**
+ * 用户唯一值,确保接口不被重复调用
+ */
+ private String user;
+
+ /**
+ * @since 1.1.2
+ */
+ private Integer seed;
+
+
+ /**
+ * 最新模型参考官方文档:
+ * 官方稳定模型列表
+ */
+ @Getter
+ @AllArgsConstructor
+ public enum Model {
+ /**
+ * gpt-3.5-turbo
+ */
+ GPT_3_5_TURBO("gpt-3.5-turbo"),
+ /**
+ * 临时模型,不建议使用,2023年9 月 13 日将被弃用
+ */
+ @Deprecated
+ GPT_3_5_TURBO_0301("gpt-3.5-turbo-0301"),
+ /**
+ * gpt-3.5-turbo-0613 支持函数
+ */
+ GPT_3_5_TURBO_1106("gpt-3.5-turbo-1106"),
+
+ GPT_3_5_TURBO_0613("gpt-3.5-turbo-0613"),
+ /**
+ * gpt-3.5-turbo-16k 超长上下文
+ */
+ GPT_3_5_TURBO_16K("gpt-3.5-turbo-16k"),
+ /**
+ * gpt-3.5-turbo-16k-0613 超长上下文 支持函数
+ */
+ GPT_3_5_TURBO_16K_0613("gpt-3.5-turbo-16k-0613"),
+ /**
+ * gpt-3.5-turbo-0125 超长上下文 支持函数
+ */
+ GPT_3_5_TURBO_0125("gpt-3.5-turbo-0125"),
+ /**
+ * GPT4.0
+ */
+ GPT_4("gpt-4"),
+ /**
+ * 临时模型,不建议使用,2023年9 月 13 日将被弃用
+ */
+ @Deprecated
+ GPT_4_0314("gpt-4-0314"),
+ /**
+ * GPT4.0 超长上下文
+ */
+ GPT_4_32K("gpt-4-32k"),
+ /**
+ * 临时模型,不建议使用,2023年9 月 13 日将被弃用
+ */
+ @Deprecated
+ GPT_4_32K_0314("gpt-4-32k-0314"),
+
+ /**
+ * gpt-4-0613,支持函数
+ */
+ GPT_4_0613("gpt-4-0613"),
+ /**
+ * gpt-4-0613,支持函数
+ */
+ GPT_4_32K_0613("gpt-4-32k-0613"),
+ /**
+ * 支持数组模式,支持function call,支持可重复输出
+ */
+ GPT_4_1106_PREVIEW("gpt-4-1106-preview"),
+ /**
+ * 支持图片
+ */
+ GPT_4_VISION_PREVIEW("gpt-4-vision-preview"),
+ /**
+ * gpt-4-0613,支持函数
+ */
+ GPT_4_0125_PREVIEW("gpt-4-0125-preview"),
+
+ /**
+ * GPT_4_ALL
+ */
+ GPT_4_ALL("gpt-4-all"),
+
+ GPT_4_GIZMO("gpt-4-gizmo"),
+
+ NET("net"),
+
+ CLAUDE_3_SONNET("claude-3-sonnet-20240229"),
+
+ GEMINI_PRO("gemini-pro"),
+
+ STABLE_DIFFUSION("stable-diffusion"),
+
+ SUNO_V3("suno-v3"),
+ ;
+ private final String name;
+ }
+
+ @Getter
+ @AllArgsConstructor
+ public enum ChatType {
+ /**
+ * 对话类型 - 输入
+ */
+ CHAT_IN("in"),
+ /**
+ * 对话类型 - 输出
+ */
+ CHAT_OUT("out"),
+
+ ;
+ private final String name;
+ }
+
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/BaseMessage.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/BaseMessage.java
new file mode 100644
index 0000000..9f2207e
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/BaseMessage.java
@@ -0,0 +1,84 @@
+package org.aibidding.common.chat.entity.chat;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.Getter;
+import org.aibidding.common.chat.entity.chat.tool.ToolCalls;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * @since 1.1.2
+ * 2023-03-02
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+@AllArgsConstructor
+public class BaseMessage implements Serializable {
+
+ /**
+ * 目前支持四个中角色参考官网,进行情景输入:
+ * https://platform.openai.com/docs/guides/chat/introduction
+ */
+ private String role;
+
+
+ private String name;
+
+ /**
+ * The tool calls generated by the model, such as function calls.
+ * @since 1.1.2
+ */
+ @JsonProperty("tool_calls")
+ private List toolCalls;
+
+ /**
+ * @since 1.1.2
+ */
+ @JsonProperty("tool_call_id")
+ private String toolCallId;
+
+ @Deprecated
+ @JsonProperty("function_call")
+ private FunctionCall functionCall;
+
+
+ /**
+ * 构造函数
+ *
+ * @param role 角色
+ * @param name name
+ * @param functionCall functionCall
+ */
+ public BaseMessage(String role, String name, FunctionCall functionCall) {
+ this.role = role;
+ this.name = name;
+ this.functionCall = functionCall;
+ }
+
+ public BaseMessage() {
+ }
+
+
+ @Getter
+ @AllArgsConstructor
+ public enum Role {
+
+ SYSTEM("system"),
+ USER("user"),
+ ASSISTANT("assistant"),
+ FUNCTION("function"),
+ TOOL("tool"),
+ ;
+ private final String name;
+ }
+
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/ChatChoice.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/ChatChoice.java
new file mode 100644
index 0000000..5993ac3
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/ChatChoice.java
@@ -0,0 +1,31 @@
+package org.aibidding.common.chat.entity.chat;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * @since 2023-03-02
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ChatChoice implements Serializable {
+ private long index;
+ /**
+ * 请求参数stream为true返回是delta
+ */
+ @JsonProperty("delta")
+ private Message delta;
+ /**
+ * 请求参数stream为false返回是message
+ */
+ @JsonProperty("message")
+ private Message message;
+ @JsonProperty("finish_reason")
+ private String finishReason;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/ChatCompletion.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/ChatCompletion.java
new file mode 100644
index 0000000..6d58743
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/ChatCompletion.java
@@ -0,0 +1,34 @@
+package org.aibidding.common.chat.entity.chat;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.NonNull;
+import lombok.experimental.SuperBuilder;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 描述: chat模型参数
+ *
+ * @author https:www.unfbx.com
+ * 2023-03-02
+ */
+@Data
+@SuperBuilder
+@Slf4j
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@NoArgsConstructor
+@AllArgsConstructor
+public class ChatCompletion extends BaseChatCompletion implements Serializable {
+
+ /**
+ * 问题描述
+ */
+ @NonNull
+ private List messages;
+
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/ChatCompletionResponse.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/ChatCompletionResponse.java
new file mode 100644
index 0000000..831dbc3
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/ChatCompletionResponse.java
@@ -0,0 +1,25 @@
+package org.aibidding.common.chat.entity.chat;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+import org.aibidding.common.chat.entity.common.Usage;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 描述: chat答案类
+ *
+ * @author https:www.unfbx.com
+ * 2023-03-02
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ChatCompletionResponse implements Serializable {
+ private String id;
+ private String object;
+ private long created;
+ private String model;
+ private List choices;
+ private Usage usage;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/ChatCompletionWithPicture.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/ChatCompletionWithPicture.java
new file mode 100644
index 0000000..812c608
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/ChatCompletionWithPicture.java
@@ -0,0 +1,32 @@
+package org.aibidding.common.chat.entity.chat;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.SuperBuilder;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 描述: chat模型附带图片的参数
+ *
+ * @author https:www.unfbx.com
+ * @since 1.1.2
+ * 2023-11-10
+ */
+@Data
+@SuperBuilder
+@Slf4j
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@NoArgsConstructor
+@AllArgsConstructor
+public class ChatCompletionWithPicture extends BaseChatCompletion implements Serializable {
+ /**
+ * 问题描述
+ */
+ private List messages;
+
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/Content.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/Content.java
new file mode 100644
index 0000000..e10942f
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/Content.java
@@ -0,0 +1,43 @@
+package org.aibidding.common.chat.entity.chat;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.*;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 描述:
+ *
+ * @author https://www.unfbx.com
+ * @since 1.1.2
+ * 2023-11-10
+ */
+@Data
+@Builder
+@Slf4j
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@NoArgsConstructor
+@AllArgsConstructor
+public class Content {
+ /**
+ * 输入类型:text、image_url
+ *
+ * @see Type
+ */
+ private String type;
+ private String text;
+ @JsonProperty("image_url")
+ private ImageUrl imageUrl;
+
+ /**
+ * 生成图片风格
+ */
+ @Getter
+ @AllArgsConstructor
+ public enum Type {
+ TEXT("text"),
+ IMAGE_URL("image_url"),
+ ;
+ private final String name;
+ }
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/FunctionCall.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/FunctionCall.java
new file mode 100644
index 0000000..4badc93
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/FunctionCall.java
@@ -0,0 +1,27 @@
+package org.aibidding.common.chat.entity.chat;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 描述:函数调用返回值
+ *
+ * @author https://www.unfbx.com
+ * @since 2023-06-14
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class FunctionCall {
+ /**
+ * 方法名
+ */
+ private String name;
+ /**
+ * 方法参数
+ */
+ private String arguments;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/Functions.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/Functions.java
new file mode 100644
index 0000000..2de7087
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/Functions.java
@@ -0,0 +1,46 @@
+package org.aibidding.common.chat.entity.chat;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 描述:方法参数实体类,实例数据如下
+ *
+ * {
+ * "name": "get_current_weather",
+ * "description": "Get the current weather in a given location",
+ * "parameters": {
+ * "type": "object",
+ * "properties": {
+ * "location": {
+ * "type": "string",
+ * "description": "The city and state, e.g. San Francisco, CA"
+ * },
+ * "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
+ * },
+ * "required": ["location"]
+ * },
+ * }
+ *
+ * @author https:www.unfbx.com
+ * @since 2023-06-14
+ */
+@Data
+@Builder
+public class Functions implements Serializable {
+ /**
+ * 方法名称
+ */
+ private String name;
+ /**
+ * 方法描述
+ */
+ private String description;
+ /**
+ * 方法参数
+ * 扩展参数可以继承Parameters自己实现,json格式的数据
+ */
+ private Parameters parameters;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/ImageUrl.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/ImageUrl.java
new file mode 100644
index 0000000..848779a
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/ImageUrl.java
@@ -0,0 +1,28 @@
+package org.aibidding.common.chat.entity.chat;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 描述:
+ *
+ * @author https://www.unfbx.com
+ * 2023-11-10
+ */
+@Data
+@Builder
+@Slf4j
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@NoArgsConstructor
+@AllArgsConstructor
+public class ImageUrl {
+ /**
+ * 图片地址,支持base64. eg: data:image/jpeg;base64,{base64_image}
+ * https://platform.openai.com/docs/guides/vision
+ */
+ private String url;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/Message.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/Message.java
new file mode 100644
index 0000000..f158310
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/Message.java
@@ -0,0 +1,107 @@
+package org.aibidding.common.chat.entity.chat;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.Data;
+import org.aibidding.common.chat.entity.chat.tool.ToolCalls;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * @since 2023-03-02
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Message extends BaseMessage implements Serializable {
+
+ private Object content;
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * 构造函数
+ *
+ * @param role 角色
+ * @param name name
+ * @param content content
+ * @param functionCall functionCall
+ */
+ public Message(String role, String name, String content, List toolCalls, String toolCallId, FunctionCall functionCall) {
+ this.content = content;
+ super.setRole(role);
+ super.setName(name);
+ super.setToolCalls(toolCalls);
+ super.setToolCallId(toolCallId);
+ super.setFunctionCall(functionCall);
+ }
+
+ public Message() {
+ }
+
+ private Message(Builder builder) {
+ setContent(builder.content);
+ super.setRole(builder.role);
+ super.setName(builder.name);
+ super.setFunctionCall(builder.functionCall);
+ super.setToolCalls(builder.toolCalls);
+ super.setToolCallId(builder.toolCallId);
+ }
+
+ public static final class Builder {
+ private String role;
+ private String content;
+ private String name;
+ private String toolCallId;
+ private List toolCalls;
+ private FunctionCall functionCall;
+
+ public Builder() {
+ }
+
+ public Builder role(Role role) {
+ this.role = role.getName();
+ return this;
+ }
+
+ public Builder role(String role) {
+ this.role = role;
+ return this;
+ }
+
+ public Builder content(String content) {
+ this.content = content;
+ return this;
+ }
+
+ public Builder name(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public Builder functionCall(FunctionCall functionCall) {
+ this.functionCall = functionCall;
+ return this;
+ }
+
+ public Builder toolCalls(List toolCalls) {
+ this.toolCalls = toolCalls;
+ return this;
+ }
+
+ public Builder toolCallId(String toolCallId) {
+ this.toolCallId = toolCallId;
+ return this;
+ }
+
+ public Message build() {
+ return new Message(this);
+ }
+ }
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/MessagePicture.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/MessagePicture.java
new file mode 100644
index 0000000..055d898
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/MessagePicture.java
@@ -0,0 +1,114 @@
+package org.aibidding.common.chat.entity.chat;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import org.aibidding.common.chat.entity.chat.tool.ToolCalls;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * @since 2023-03-02
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+@AllArgsConstructor
+public class MessagePicture extends BaseMessage implements Serializable {
+ /**
+ * Content数组支持多图片输入
+ * https://platform.openai.com/docs/guides/vision
+ */
+ private List content;
+
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * 构造函数
+ *
+ * @param role 角色
+ * @param name name
+ * @param content content
+ * @param functionCall functionCall
+ */
+ public MessagePicture(String role, String name, List content, List toolCalls, String toolCallId, FunctionCall functionCall) {
+ this.content = content;
+ super.setRole(role);
+ super.setName(name);
+ super.setToolCalls(toolCalls);
+ super.setToolCallId(toolCallId);
+ super.setFunctionCall(functionCall);
+ }
+
+ public MessagePicture() {
+ }
+
+ private MessagePicture(Builder builder) {
+ setContent(builder.content);
+ super.setRole(builder.role);
+ super.setName(builder.name);
+ super.setFunctionCall(builder.functionCall);
+ super.setToolCalls(builder.toolCalls);
+ super.setToolCallId(builder.toolCallId);
+ }
+
+ public static final class Builder {
+ private String role;
+ private List content;
+ private String name;
+ private String toolCallId;
+ private List toolCalls;
+ private FunctionCall functionCall;
+
+ public Builder() {
+ }
+
+ public Builder role(Role role) {
+ this.role = role.getName();
+ return this;
+ }
+
+ public Builder role(String role) {
+ this.role = role;
+ return this;
+ }
+
+ public Builder content(List content) {
+ this.content = content;
+ return this;
+ }
+
+ public Builder name(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public Builder functionCall(FunctionCall functionCall) {
+ this.functionCall = functionCall;
+ return this;
+ }
+
+ public Builder toolCalls(List toolCalls) {
+ this.toolCalls = toolCalls;
+ return this;
+ }
+
+ public Builder toolCallId(String toolCallId) {
+ this.toolCallId = toolCallId;
+ return this;
+ }
+
+ public MessagePicture build() {
+ return new MessagePicture(this);
+ }
+ }
+
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/Parameters.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/Parameters.java
new file mode 100644
index 0000000..4a1a6f1
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/Parameters.java
@@ -0,0 +1,42 @@
+package org.aibidding.common.chat.entity.chat;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+/**
+ * 描述:方法参数类,扩展参数可以继承Parameters自己实现
+ * 参考:
+ *
+ * {
+ * "type": "object",
+ * "properties": {
+ * "location": {
+ * "type": "string",
+ * "description": "The city and state, e.g. San Francisco, CA"
+ * },
+ * "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
+ * },
+ * "required": ["location"]
+ * }
+ *
+ * @author https:www.unfbx.com
+ * @since 2023-06-14
+ */
+@Data
+@Builder
+public class Parameters implements Serializable {
+ /**
+ * 参数类型
+ */
+ private String type;
+ /**
+ * 参数属性、描述
+ */
+ private Object properties;
+ /**
+ * 方法必输字段
+ */
+ private List required;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/ResponseFormat.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/ResponseFormat.java
new file mode 100644
index 0000000..4751630
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/ResponseFormat.java
@@ -0,0 +1,29 @@
+package org.aibidding.common.chat.entity.chat;
+
+import lombok.*;
+
+/**
+ * 指定模型必须输出的格式的对象。
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ResponseFormat {
+ /**
+ * 默认:text
+ *
+ * @see Type
+ */
+ private String type;
+
+ @Getter
+ @AllArgsConstructor
+ public enum Type {
+ JSON_OBJECT("json_object"),
+ TEXT("text"),
+ ;
+ private final String name;
+
+ }
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/tool/ToolCallFunction.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/tool/ToolCallFunction.java
new file mode 100644
index 0000000..694174c
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/tool/ToolCallFunction.java
@@ -0,0 +1,31 @@
+package org.aibidding.common.chat.entity.chat.tool;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * ToolCall 的 Function参数
+ * The function that the model called.
+ *
+ * @author https:www.unfbx.com
+ * @since 1.1.2
+ * 2023-11-09
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ToolCallFunction implements Serializable {
+ /**
+ * 方法名
+ */
+ private String name;
+ /**
+ * 方法参数
+ */
+ private String arguments;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/tool/ToolCalls.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/tool/ToolCalls.java
new file mode 100644
index 0000000..277f268
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/tool/ToolCalls.java
@@ -0,0 +1,37 @@
+package org.aibidding.common.chat.entity.chat.tool;
+
+import lombok.*;
+
+import java.io.Serializable;
+
+/**
+ * The tool calls generated by the model, such as function calls.
+ *
+ * @author unfbx
+ * @since 1.1.2
+ * 2023-11-09
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ToolCalls implements Serializable {
+ /**
+ * The ID of the tool call.
+ */
+ private String id;
+ /**
+ * The type of the tool. Currently, only function is supported.
+ */
+ private String type;
+
+ private ToolCallFunction function;
+
+ @Getter
+ @AllArgsConstructor
+ public enum Type {
+ FUNCTION("function"),
+ ;
+ private final String name;
+ }
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/tool/ToolChoice.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/tool/ToolChoice.java
new file mode 100644
index 0000000..fd9a66b
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/tool/ToolChoice.java
@@ -0,0 +1,27 @@
+package org.aibidding.common.chat.entity.chat.tool;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.Getter;
+
+import java.io.Serializable;
+
+/**
+ * choice和object同时存在是以object为准
+ *
+ * @author unfbx
+ * @since 1.1.2
+ * 2023-11-09
+ */
+@Data
+public class ToolChoice implements Serializable {
+
+ @Getter
+ @AllArgsConstructor
+ public enum Choice {
+ NONE("none"),
+ AUTO("auto"),
+ ;
+ private final String name;
+ }
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/tool/ToolChoiceObj.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/tool/ToolChoiceObj.java
new file mode 100644
index 0000000..887a94f
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/tool/ToolChoiceObj.java
@@ -0,0 +1,33 @@
+package org.aibidding.common.chat.entity.chat.tool;
+
+import lombok.*;
+
+/**
+ * @author unfbx
+ * @since 1.1.2
+ * 2023-11-09
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ToolChoiceObj {
+ /**
+ * 需要调用的方法名称
+ */
+ private ToolChoiceObjFunction function;
+ /**
+ * 工具的类型。目前仅支持函数。
+ *
+ * @see Type
+ */
+ private String type;
+
+ @Getter
+ @AllArgsConstructor
+ public enum Type {
+ FUNCTION("function"),
+ ;
+ private final String name;
+ }
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/tool/ToolChoiceObjFunction.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/tool/ToolChoiceObjFunction.java
new file mode 100644
index 0000000..4d89dec
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/tool/ToolChoiceObjFunction.java
@@ -0,0 +1,21 @@
+package org.aibidding.common.chat.entity.chat.tool;
+
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author unfbx
+ * @since 1.1.2
+ * 2023-11-09
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ToolChoiceObjFunction {
+
+ private String name;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/tool/Tools.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/tool/Tools.java
new file mode 100644
index 0000000..a6e3bd0
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/tool/Tools.java
@@ -0,0 +1,35 @@
+package org.aibidding.common.chat.entity.chat.tool;
+
+
+import lombok.*;
+
+import java.io.Serializable;
+
+/**
+ * @author unfbx
+ * @since 1.1.2
+ * 2023-11-09
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class Tools implements Serializable {
+
+ /**
+ * 目前只支持:function
+ *
+ * @see Type
+ */
+ private String type;
+
+ private ToolsFunction function;
+
+ @Getter
+ @AllArgsConstructor
+ public enum Type {
+ FUNCTION("function"),
+ ;
+ private final String name;
+ }
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/tool/ToolsFunction.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/tool/ToolsFunction.java
new file mode 100644
index 0000000..e4906f5
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/chat/tool/ToolsFunction.java
@@ -0,0 +1,36 @@
+package org.aibidding.common.chat.entity.chat.tool;
+
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.aibidding.common.chat.entity.chat.Parameters;
+
+import java.io.Serializable;
+
+/**
+ * @author unfbx
+ * @since 1.1.2
+ * 2023-11-09
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ToolsFunction implements Serializable {
+
+ /**
+ * 要调用的函数的名称。必须是 a-z、A-Z、0-9,或包含下划线和破折号,最大长度为 64
+ */
+ private String name;
+ /**
+ * 对函数功能的描述,模型使用它来选择何时以及如何调用该函数。
+ */
+ private String description;
+ /**
+ * 函数接受的参数,描述为 JSON Schema 对象
+ * 扩展参数可以继承Parameters自己实现,json格式的数据
+ */
+ private Parameters parameters;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/common/Choice.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/common/Choice.java
new file mode 100644
index 0000000..456dd7b
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/common/Choice.java
@@ -0,0 +1,23 @@
+package org.aibidding.common.chat.entity.common;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Choice implements Serializable {
+ private String text;
+ private long index;
+ private Object logprobs;
+ @JsonProperty("finish_reason")
+ private String finishReason;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/common/DeleteResponse.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/common/DeleteResponse.java
new file mode 100644
index 0000000..163d5e3
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/common/DeleteResponse.java
@@ -0,0 +1,20 @@
+package org.aibidding.common.chat.entity.common;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class DeleteResponse implements Serializable {
+ private String id;
+ private String object;
+ private boolean deleted;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/common/OpenAiResponse.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/common/OpenAiResponse.java
new file mode 100644
index 0000000..f042937
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/common/OpenAiResponse.java
@@ -0,0 +1,30 @@
+package org.aibidding.common.chat.entity.common;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class OpenAiResponse implements Serializable {
+ private String object;
+ private List data;
+ private Error error;
+
+
+ @Data
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public class Error {
+ private String message;
+ private String type;
+ private String param;
+ private String code;
+ }
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/common/Usage.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/common/Usage.java
new file mode 100644
index 0000000..a4e769e
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/common/Usage.java
@@ -0,0 +1,24 @@
+package org.aibidding.common.chat.entity.common;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Usage implements Serializable {
+ @JsonProperty("prompt_tokens")
+ private long promptTokens;
+ @JsonProperty("completion_tokens")
+ private long completionTokens;
+ @JsonProperty("total_tokens")
+ private long totalTokens;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/completions/Completion.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/completions/Completion.java
new file mode 100644
index 0000000..7ff7f00
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/completions/Completion.java
@@ -0,0 +1,125 @@
+package org.aibidding.common.chat.entity.completions;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.*;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 描述: 问题类
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-11
+ */
+@Data
+@Builder
+@Slf4j
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@NoArgsConstructor
+@AllArgsConstructor
+public class Completion implements Serializable {
+
+ @NonNull
+ @Builder.Default
+ private String model = Model.DAVINCI_003.getName();
+ /**
+ * 问题描述
+ */
+ @NonNull
+ private String prompt;
+ /**
+ * 完成输出后的后缀,用于格式化输出结果
+ */
+ private String suffix;
+
+ /**
+ * 最大支持4096
+ */
+ @JsonProperty("max_tokens")
+ @Builder.Default
+ private Integer maxTokens = 2048;
+ /**
+ * 使用什么取样温度,0到2之间。较高的值(如0.8)将使输出更加随机,而较低的值(如0.2)将使输出更加集中和确定。
+ *
+ * We generally recommend altering this or but not both.top_p
+ */
+ @Builder.Default
+ private double temperature = 0;
+
+ /**
+ * 使用温度采样的替代方法称为核心采样,其中模型考虑具有top_p概率质量的令牌的结果。因此,0.1 意味着只考虑包含前 10% 概率质量的代币。
+ *
+ * 我们通常建议更改此设置,但不要同时更改两者。temperature
+ */
+ @JsonProperty("top_p")
+ @Builder.Default
+ private Double topP = 1d;
+
+ /**
+ * 为每个提示生成的完成次数。
+ */
+ @Builder.Default
+ private Integer n = 1;
+
+ @Builder.Default
+ private boolean stream = false;
+ /**
+ * 最大值:5
+ */
+ private Integer logprobs;
+
+ @Builder.Default
+ private boolean echo = false;
+
+ private List stop;
+
+ @JsonProperty("presence_penalty")
+ @Builder.Default
+ private double presencePenalty = 0;
+
+ /**
+ * -2.0 ~~ 2.0
+ */
+ @JsonProperty("frequency_penalty")
+ @Builder.Default
+ private double frequencyPenalty = 0;
+
+ @JsonProperty("best_of")
+ @Builder.Default
+ private Integer bestOf = 1;
+
+ @JsonProperty("logit_bias")
+ private Map logitBias;
+ /**
+ * 用户唯一值,确保接口不被重复调用
+ */
+ private String user;
+
+ /**
+ * 获取当前参数的tokens数
+ * @return token数量
+ */
+// public long tokens() {
+// if (StrUtil.isBlank(this.prompt) || StrUtil.isBlank(this.model)) {
+// log.warn("参数异常model:{},prompt:{}", this.model, this.prompt);
+// return 0;
+// }
+// return TikTokensUtil.tokens(this.model, this.prompt);
+// }
+
+ @Getter
+ @AllArgsConstructor
+ public enum Model {
+ DAVINCI_003("text-davinci-003"),
+ DAVINCI_002("text-davinci-002"),
+ DAVINCI("davinci"),
+ ;
+ private String name;
+ }
+}
+
+
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/completions/CompletionResponse.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/completions/CompletionResponse.java
new file mode 100644
index 0000000..cb510ef
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/completions/CompletionResponse.java
@@ -0,0 +1,26 @@
+package org.aibidding.common.chat.entity.completions;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+import org.aibidding.common.chat.entity.common.Choice;
+import org.aibidding.common.chat.entity.common.OpenAiResponse;
+import org.aibidding.common.chat.entity.common.Usage;
+
+import java.io.Serializable;
+
+/**
+ * 描述: 答案类
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-11
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class CompletionResponse extends OpenAiResponse implements Serializable {
+ private String id;
+ private String object;
+ private long created;
+ private String model;
+ private Choice[] choices;
+ private Usage usage;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/dto/WebSocketMessageDto.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/dto/WebSocketMessageDto.java
new file mode 100644
index 0000000..22ca9c3
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/dto/WebSocketMessageDto.java
@@ -0,0 +1,29 @@
+package org.aibidding.common.chat.entity.dto;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 消息的dto
+ *
+ * @author zendwang
+ */
+@Data
+public class WebSocketMessageDto implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 需要推送到的session key 列表
+ */
+ private List sessionKeys;
+
+ /**
+ * 需要发送的消息
+ */
+ private String message;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/edits/Edit.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/edits/Edit.java
new file mode 100644
index 0000000..4b54743
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/edits/Edit.java
@@ -0,0 +1,104 @@
+package org.aibidding.common.chat.entity.edits;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.*;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.Serializable;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Getter
+@Builder
+@Slf4j
+@NoArgsConstructor
+@AllArgsConstructor
+public class Edit implements Serializable {
+ /**
+ * 编辑模型,目前支持两种
+ */
+ @NonNull
+ private String model;
+
+ @NonNull
+ private String input;
+ /**
+ * 提示说明。告知模型如何修改。
+ */
+ @NonNull
+ private String instruction;
+
+
+ /**
+ * 使用什么取样温度,0到2之间。较高的值(如0.8)将使输出更加随机,而较低的值(如0.2)将使输出更加集中和确定。
+ *
+ * We generally recommend altering this or but not both.top_p
+ */
+ @Builder.Default
+ private double temperature = 0;
+
+ /**
+ * 使用温度采样的替代方法称为核心采样,其中模型考虑具有top_p概率质量的令牌的结果。因此,0.1 意味着只考虑包含前 10% 概率质量的代币。
+ *
+ * 我们通常建议更改此设置,但不要同时更改两者。temperature
+ */
+ @JsonProperty("top_p")
+ @Builder.Default
+ private Double topP = 1d;
+
+ /**
+ * 为每个提示生成的完成次数。
+ */
+ @Builder.Default
+ private Integer n = 1;
+
+ public void setModel(Model model) {
+ this.model = model.getName();
+ }
+
+ public void setTemperature(double temperature) {
+ if (temperature > 2 || temperature < 0) {
+ log.error("temperature参数异常,temperature属于[0,2]");
+ this.temperature = 2;
+ return;
+ }
+ if (temperature < 0) {
+ log.error("temperature参数异常,temperature属于[0,2]");
+ this.temperature = 0;
+ return;
+ }
+ this.temperature = temperature;
+ }
+
+
+ public void setTopP(Double topP) {
+ this.topP = topP;
+ }
+
+ public void setN(Integer n) {
+ this.n = n;
+ }
+
+ public void setInput(String input) {
+ this.input = input;
+ }
+
+ public void setInstruction(String instruction) {
+ this.instruction = instruction;
+ }
+ @Getter
+ @AllArgsConstructor
+ public enum Model {
+ TEXT_DAVINCI_EDIT_001("text-davinci-edit-001"),
+ CODE_DAVINCI_EDIT_001("code-davinci-edit-001"),
+ ;
+ private String name;
+ }
+}
+
+
+
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/edits/EditResponse.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/edits/EditResponse.java
new file mode 100644
index 0000000..fb5e789
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/edits/EditResponse.java
@@ -0,0 +1,26 @@
+package org.aibidding.common.chat.entity.edits;
+
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+import org.aibidding.common.chat.entity.common.Choice;
+import org.aibidding.common.chat.entity.common.Usage;
+
+import java.io.Serializable;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class EditResponse implements Serializable {
+ private String id;
+ private String object;
+ private long created;
+ private String model;
+ private Choice[] choices;
+ private Usage usage;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/embeddings/Embedding.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/embeddings/Embedding.java
new file mode 100644
index 0000000..d494116
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/embeddings/Embedding.java
@@ -0,0 +1,54 @@
+package org.aibidding.common.chat.entity.embeddings;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.*;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Getter
+@Slf4j
+@Builder
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@NoArgsConstructor
+@AllArgsConstructor
+public class Embedding implements Serializable {
+ @NonNull
+ @Builder.Default
+ private String model = Model.TEXT_EMBEDDING_ADA_002.getName();
+ /**
+ * 必选项:长度不能超过:8192
+ */
+ @NonNull
+ private List input;
+
+ private String user;
+
+ public void setModel(Model model) {
+ if (Objects.isNull(model)) {
+ model = Model.TEXT_EMBEDDING_ADA_002;
+ }
+ this.model = model.getName();
+ }
+
+
+ public void setUser(String user) {
+ this.user = user;
+ }
+
+ @Getter
+ @AllArgsConstructor
+ public enum Model {
+ TEXT_EMBEDDING_ADA_002("text-embedding-ada-002"),
+ ;
+ private String name;
+ }
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/embeddings/EmbeddingResponse.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/embeddings/EmbeddingResponse.java
new file mode 100644
index 0000000..b55fa80
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/embeddings/EmbeddingResponse.java
@@ -0,0 +1,24 @@
+package org.aibidding.common.chat.entity.embeddings;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+import org.aibidding.common.chat.entity.common.Usage;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class EmbeddingResponse implements Serializable {
+
+ private String object;
+ private List- data;
+ private String model;
+ private Usage usage;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/embeddings/Item.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/embeddings/Item.java
new file mode 100644
index 0000000..b1a5d62
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/embeddings/Item.java
@@ -0,0 +1,16 @@
+package org.aibidding.common.chat.entity.embeddings;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Item implements Serializable {
+ private String object;
+ private List embedding;
+ private Integer index;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/engines/Engine.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/engines/Engine.java
new file mode 100644
index 0000000..1cdc54b
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/engines/Engine.java
@@ -0,0 +1,25 @@
+package org.aibidding.common.chat.entity.engines;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Engine implements Serializable {
+
+ private String id;
+ private String object;
+ private String owner;
+ private boolean ready;
+ private Object permissions;
+ private long created;
+
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/files/File.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/files/File.java
new file mode 100644
index 0000000..2f0210b
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/files/File.java
@@ -0,0 +1,34 @@
+package org.aibidding.common.chat.entity.files;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class File implements Serializable {
+
+// private String id;
+// private String object;
+// private long bytes;
+// private long created_at;
+// private String filename;
+// private String purpose;
+// private String status;
+// @JsonProperty("status_details")
+// private String statusDetails;
+
+ private long bytes;
+ private long created_at;
+ private String filename;
+ private String id;
+ private String object;
+ private String url;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/files/UploadFileResponse.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/files/UploadFileResponse.java
new file mode 100644
index 0000000..b000495
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/files/UploadFileResponse.java
@@ -0,0 +1,17 @@
+package org.aibidding.common.chat.entity.files;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class UploadFileResponse extends File implements Serializable {
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/fineTune/Event.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/fineTune/Event.java
new file mode 100644
index 0000000..35d90e1
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/fineTune/Event.java
@@ -0,0 +1,17 @@
+package org.aibidding.common.chat.entity.fineTune;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Event implements Serializable {
+ private String object;
+ @JsonProperty("created_at")
+ private long createdAt;
+ private String level;
+ private String message;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/fineTune/FineTune.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/fineTune/FineTune.java
new file mode 100644
index 0000000..66713f9
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/fineTune/FineTune.java
@@ -0,0 +1,122 @@
+package org.aibidding.common.chat.entity.fineTune;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.*;
+import lombok.extern.slf4j.Slf4j;
+import org.aibidding.common.chat.openai.exception.CommonError;
+import org.aibidding.common.core.exception.base.BaseException;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Objects;
+
+@Getter
+@Slf4j
+@Builder
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@NoArgsConstructor
+@AllArgsConstructor
+public class FineTune implements Serializable {
+
+ /**
+ * 上传的文件ID
+ */
+ @NonNull
+ @JsonProperty("training_file")
+ private String trainingFile;
+
+ @JsonProperty("validation_file")
+ private String validationFile;
+ /**
+ * 参考
+ * @see Model
+ */
+ private String model;
+
+ @JsonProperty("n_epochs")
+ @Builder.Default
+ private Integer n_epochs = 4;
+
+ @JsonProperty("batch_size")
+ private Integer batchSize;
+
+ @JsonProperty("learning_rate_multiplier")
+ private Double learningRateMultiplier;
+
+ @JsonProperty("prompt_loss_weight")
+ @Builder.Default
+ private Double promptLossWeight = 0.01;
+
+ @JsonProperty("compute_classification_metrics")
+ @Builder.Default
+ private boolean computeClassificationMetrics = false;
+
+ @JsonProperty("classification_n_classes")
+ private Integer classificationNClasses;
+
+ @JsonProperty("classification_betas")
+ private List classificationBetas;
+
+ private String suffix;
+
+ public void setTrainingFile(String trainingFile) {
+ this.trainingFile = trainingFile;
+ }
+
+ public void setValidationFile(String validationFile) {
+ this.validationFile = validationFile;
+ }
+
+ public void setModel(String model) {
+ this.model = model;
+ }
+
+ public void setN_epochs(Integer n_epochs) {
+ this.n_epochs = n_epochs;
+ }
+
+ public void setBatchSize(Integer batchSize) {
+ this.batchSize = batchSize;
+ }
+
+ public void setLearningRateMultiplier(Double learningRateMultiplier) {
+ this.learningRateMultiplier = learningRateMultiplier;
+ }
+
+ public void setPromptLossWeight(Double promptLossWeight) {
+ this.promptLossWeight = promptLossWeight;
+ }
+
+ public void setComputeClassificationMetrics(boolean computeClassificationMetrics) {
+ this.computeClassificationMetrics = computeClassificationMetrics;
+ }
+
+ public void setClassificationNClasses(Integer classificationNClasses) {
+ this.classificationNClasses = classificationNClasses;
+ }
+
+ public void setClassificationBetas(List classificationBetas) {
+ this.classificationBetas = classificationBetas;
+ }
+
+ public void setSuffix(String suffix) {
+ if(Objects.nonNull(suffix) && !"".equals(suffix) && suffix.length() > 40){
+ log.error("后缀长度不能大于40");
+ throw new BaseException(CommonError.PARAM_ERROR.msg());
+ }
+ this.suffix = suffix;
+ }
+
+ @Getter
+ @AllArgsConstructor
+ public enum Model {
+ // or a fine-tuned model created after 2022-04-21.
+ ADA("ada"),
+ BABBAGE("babbage"),
+ CURIE("curie"),
+ DAVINCI("davinci"),
+ ;
+ private String name;
+ }
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/fineTune/FineTuneDeleteResponse.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/fineTune/FineTuneDeleteResponse.java
new file mode 100644
index 0000000..c2c43cc
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/fineTune/FineTuneDeleteResponse.java
@@ -0,0 +1,18 @@
+package org.aibidding.common.chat.entity.fineTune;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class FineTuneDeleteResponse implements Serializable {
+
+ private String id;
+
+ private String object;
+
+ private boolean deleted;
+
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/fineTune/FineTuneResponse.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/fineTune/FineTuneResponse.java
new file mode 100644
index 0000000..42f92f1
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/fineTune/FineTuneResponse.java
@@ -0,0 +1,49 @@
+package org.aibidding.common.chat.entity.fineTune;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class FineTuneResponse implements Serializable {
+
+ private String id;
+
+ private String object;
+
+ private String model;
+
+ @JsonProperty("created_at")
+ private long createdAt;
+
+ private List events;
+
+ @JsonProperty("fine_tuned_model")
+ private String fineTunedModel;
+
+ @JsonProperty("hyperparams")
+ private HyperParam hyperParams;
+
+ @JsonProperty("organization_id")
+ private String organizationId;
+
+ @JsonProperty("result_files")
+ private List resultFiles;
+
+ private String status;
+
+ @JsonProperty("validation_files")
+ private List validationFiles;
+
+ @JsonProperty("training_files")
+ private List trainingFiles;
+
+ @JsonProperty("updated_at")
+ private long updatedAt;
+
+
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/fineTune/HyperParam.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/fineTune/HyperParam.java
new file mode 100644
index 0000000..96a0a06
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/fineTune/HyperParam.java
@@ -0,0 +1,21 @@
+package org.aibidding.common.chat.entity.fineTune;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class HyperParam implements Serializable {
+
+ @JsonProperty("batch_size")
+ private Integer batchSize;
+ @JsonProperty("learning_rate_multiplier")
+ private Double learningRateMultiplier;
+ @JsonProperty("n_epochs")
+ private Integer nEpochs;
+ @JsonProperty("prompt_loss_weight")
+ private Double promptLossWeight;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/fineTune/TrainingFile.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/fineTune/TrainingFile.java
new file mode 100644
index 0000000..55b1387
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/fineTune/TrainingFile.java
@@ -0,0 +1,23 @@
+package org.aibidding.common.chat.entity.fineTune;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class TrainingFile implements Serializable {
+
+ private String id;
+ private String object;
+ private long bytes;
+ @JsonProperty("created_at")
+ private long createdAt;
+ private String filename;
+ private String purpose;
+ private String status;
+ @JsonProperty("status_details")
+ private String statusDetails;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/images/Image.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/images/Image.java
new file mode 100644
index 0000000..f615112
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/images/Image.java
@@ -0,0 +1,111 @@
+package org.aibidding.common.chat.entity.images;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.Serializable;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Getter
+@Slf4j
+@Builder
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@NoArgsConstructor
+@AllArgsConstructor
+public class Image implements Serializable {
+
+ /**
+ * 提示词:dall-e-2支持1000字符、dall-e-3支持4000字符
+ */
+ private String prompt;
+ /**
+ * 支持dall-e-2、dall-e-3
+ *
+ * @see Model
+ */
+ @Builder.Default
+ private String model = Model.DALL_E_3.getName();
+
+ /**
+ * 此参数仅仅dall-e-3,默认值:standard
+ *
+ * @see Quality
+ */
+ private String quality;
+
+ /**
+ * 为每个提示生成的个数,dall-e-3只能为1。
+ */
+ private Integer n;
+ /**
+ * 图片尺寸,默认值:1024x1024
+ * dall-e-2支持:256x256, 512x512, or 1024x1024
+ * dall-e-3支持:1024x1024, 1792x1024, or 1024x1792
+ *
+ * @see SizeEnum
+ */
+ private String size;
+ /**
+ * 此参数仅仅dall-e-3,取值范围:vivid、natural
+ * 默认值:vivid
+ *
+ * @see Style
+ */
+ private String style;
+
+ /**
+ * 生成图片格式:url、b64_json
+ *
+ * @see ResponseFormat
+ */
+ @JsonProperty("response_format")
+ private String responseFormat;
+
+ private String user;
+
+ /**
+ * 图片生成模型
+ */
+ @Getter
+ @AllArgsConstructor
+ public enum Model {
+ DALL_E_2("dall-e-2"),
+ DALL_E_3("dall-e-3"),
+ ;
+ private final String name;
+ }
+
+ /**
+ * 生成图片质量
+ */
+ @Getter
+ @AllArgsConstructor
+ public enum Quality {
+ STANDARD("standard"),
+ HD("hd"),
+ ;
+ private final String name;
+ }
+
+ /**
+ * 生成图片风格
+ */
+ @Getter
+ @AllArgsConstructor
+ public enum Style {
+ VIVID("vivid"),
+ NATURAL("natural"),
+ ;
+ private final String name;
+ }
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/images/ImageEdit.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/images/ImageEdit.java
new file mode 100644
index 0000000..216346f
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/images/ImageEdit.java
@@ -0,0 +1,98 @@
+package org.aibidding.common.chat.entity.images;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.*;
+import lombok.extern.slf4j.Slf4j;
+import org.aibidding.common.chat.openai.exception.CommonError;
+import org.aibidding.common.core.exception.base.BaseException;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Getter
+@Slf4j
+@Builder
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@NoArgsConstructor
+@AllArgsConstructor
+public class ImageEdit implements Serializable {
+ /**
+ * 必选项:描述文字,最多1000字符
+ */
+ @NonNull
+ private String prompt;
+ /**
+ * 为每个提示生成的完成次数。
+ */
+ @Builder.Default
+ private Integer n = 1;
+ /**
+ * 256x256
+ * 512x512
+ * 1024x1024
+ */
+ @Builder.Default
+ private String size = SizeEnum.size_512.getName();
+
+ @JsonProperty("response_format")
+ @Builder.Default
+ private String responseFormat = ResponseFormat.URL.getName();
+
+ private String user;
+
+ public ImageEdit setN(Integer n) {
+ if(n < 1){
+ log.warn("n最小值1");
+ n = 1;
+ }
+ if(n > 10){
+ log.warn("n最大值10");
+ n = 10;
+ }
+ this.n = n;
+ return this;
+ }
+
+ public ImageEdit setPrompt(String prompt) {
+ if(Objects.isNull(prompt) || "".equals(prompt)){
+ log.error("参数异常");
+ throw new BaseException(CommonError.PARAM_ERROR.msg());
+ }
+ if(prompt.length() > 1000){
+ log.error("长度超过1000");
+ throw new BaseException(CommonError.PARAM_ERROR.msg());
+ }
+ this.prompt = prompt;
+ return this;
+ }
+
+ public ImageEdit setSize(SizeEnum size) {
+ if(Objects.isNull(size)){
+ size = SizeEnum.size_512;
+ }
+ this.size = size.getName();
+ return this;
+ }
+
+ public ImageEdit setResponseFormat(ResponseFormat responseFormat) {
+ if(Objects.isNull(responseFormat)){
+ responseFormat = ResponseFormat.URL;
+ }
+ this.responseFormat = responseFormat.getName();
+ return this;
+ }
+
+ public ImageEdit setUser(String user) {
+ this.user = user;
+ return this;
+ }
+
+
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/images/ImageResponse.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/images/ImageResponse.java
new file mode 100644
index 0000000..207feae
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/images/ImageResponse.java
@@ -0,0 +1,20 @@
+package org.aibidding.common.chat.entity.images;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ImageResponse implements Serializable {
+ private long created;
+ private List
- data;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/images/ImageVariations.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/images/ImageVariations.java
new file mode 100644
index 0000000..bb9c633
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/images/ImageVariations.java
@@ -0,0 +1,81 @@
+package org.aibidding.common.chat.entity.images;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Getter
+@Slf4j
+@Builder
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@NoArgsConstructor
+@AllArgsConstructor
+public class ImageVariations implements Serializable {
+ /**
+ * 为每个提示生成的完成次数。
+ */
+ @Builder.Default
+ private Integer n = 1;
+ /**
+ * 256x256
+ * 512x512
+ * 1024x1024
+ */
+ @Builder.Default
+ private String size = SizeEnum.size_512.getName();
+
+ @JsonProperty("response_format")
+ @Builder.Default
+ private String responseFormat = ResponseFormat.URL.getName();
+
+ private String user;
+
+
+ public void setN(Integer n) {
+ if (n < 1) {
+ log.warn("n最小值1");
+ this.n = 1;
+ return;
+ }
+ if (n > 10) {
+ log.warn("n最大值10");
+ this.n = 10;
+ return;
+ }
+ this.n = n;
+ }
+
+
+ public void setSize(SizeEnum size) {
+ if (Objects.isNull(size)) {
+ size = SizeEnum.size_512;
+ }
+ this.size = size.getName();
+ }
+
+ public void setResponseFormat(ResponseFormat responseFormat) {
+ if (Objects.isNull(responseFormat)) {
+ responseFormat = ResponseFormat.URL;
+ }
+ this.responseFormat = responseFormat.getName();
+ }
+
+ public void setUser(String user) {
+ this.user = user;
+ }
+
+
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/images/Item.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/images/Item.java
new file mode 100644
index 0000000..7f66b2c
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/images/Item.java
@@ -0,0 +1,21 @@
+package org.aibidding.common.chat.entity.images;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Item implements Serializable {
+ private String url;
+ @JsonProperty("b64_json")
+ private String b64Json;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/images/ResponseFormat.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/images/ResponseFormat.java
new file mode 100644
index 0000000..e772e5f
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/images/ResponseFormat.java
@@ -0,0 +1,22 @@
+package org.aibidding.common.chat.entity.images;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.io.Serializable;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@AllArgsConstructor
+@Getter
+public enum ResponseFormat implements Serializable {
+ URL("url"),
+ B64_JSON("b64_json"),
+ ;
+
+ private String name;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/images/SizeEnum.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/images/SizeEnum.java
new file mode 100644
index 0000000..2c694a6
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/images/SizeEnum.java
@@ -0,0 +1,26 @@
+package org.aibidding.common.chat.entity.images;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.io.Serializable;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Getter
+@AllArgsConstructor
+public enum SizeEnum implements Serializable {
+ size_1024_1792("1024x1792"),
+ size_1792_1024("1792x1024"),
+ size_1024("1024x1024"),
+ size_512("512x512"),
+ size_256("256x256"),
+
+ ;
+ private String name;
+
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/models/LocalModelsSearchRequest.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/models/LocalModelsSearchRequest.java
new file mode 100644
index 0000000..362b85b
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/models/LocalModelsSearchRequest.java
@@ -0,0 +1,38 @@
+package org.aibidding.common.chat.entity.models;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @program: RUOYIAI
+ * @ClassName LocalModelsSearchRequest
+ * @description:
+ * @author: hejh
+ * @create: 2025-03-15 17:22
+ * @Version 1.0
+ **/
+@Data
+public class LocalModelsSearchRequest {
+
+ private List text;
+ private String model_name;
+ private String delimiter;
+ private int k;
+ private int block_size;
+ private int overlap_chars;
+
+ // 构造函数、Getter 和 Setter
+ public LocalModelsSearchRequest(List text, String model_name, String delimiter, int k, int block_size, int overlap_chars) {
+ this.text = text;
+ this.model_name = model_name;
+ this.delimiter = delimiter;
+ this.k = k;
+ this.block_size = block_size;
+ this.overlap_chars = overlap_chars;
+ }
+
+
+}
+
+
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/models/LocalModelsSearchResponse.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/models/LocalModelsSearchResponse.java
new file mode 100644
index 0000000..24cf8a4
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/models/LocalModelsSearchResponse.java
@@ -0,0 +1,20 @@
+package org.aibidding.common.chat.entity.models;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class LocalModelsSearchResponse {
+ @JsonProperty("topKEmbeddings")
+
+ private List
>> topKEmbeddings; // 处理三层嵌套数组
+
+ // 默认构造函数
+ public LocalModelsSearchResponse() {}
+
+
+
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/models/Model.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/models/Model.java
new file mode 100644
index 0000000..3ef411c
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/models/Model.java
@@ -0,0 +1,29 @@
+package org.aibidding.common.chat.entity.models;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Model implements Serializable {
+
+ private String id;
+ private String object;
+ private long created;
+ @JsonProperty("owned_by")
+ private String ownedBy;
+ @JsonProperty("permission")
+ private List permission;
+ private String root;
+ private Object parent;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/models/ModelResponse.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/models/ModelResponse.java
new file mode 100644
index 0000000..cfe309e
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/models/ModelResponse.java
@@ -0,0 +1,20 @@
+package org.aibidding.common.chat.entity.models;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ModelResponse implements Serializable {
+ private String object;
+ private List data;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/models/Permission.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/models/Permission.java
new file mode 100644
index 0000000..5ef80f9
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/models/Permission.java
@@ -0,0 +1,45 @@
+package org.aibidding.common.chat.entity.models;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Permission implements Serializable {
+
+ private String id;
+ @JsonProperty("object")
+ private String object;
+ @JsonProperty("created")
+ private long created;
+ @JsonProperty("allow_create_engine")
+ private boolean allowCreateEngine;
+ @JsonProperty("allow_sampling")
+ private boolean allowSampling;
+ @JsonProperty("allow_logprobs")
+ private boolean allowLogprobs;
+ @JsonProperty("allow_search_indices")
+ private boolean allowSearchIndices;
+ @JsonProperty("allow_view")
+ private boolean allowView;
+ @JsonProperty("allow_fine_tuning")
+ private boolean allowFineTuning;
+ @JsonProperty("organization")
+ private String organization;
+ /**
+ * 不知道是什么类型的数据
+ */
+ @JsonProperty("group")
+ private Object group;
+ @JsonProperty("is_blocking")
+ private boolean isBlocking;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/moderations/Categories.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/moderations/Categories.java
new file mode 100644
index 0000000..47c6302
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/moderations/Categories.java
@@ -0,0 +1,50 @@
+package org.aibidding.common.chat.entity.moderations;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Categories implements Serializable {
+ /**
+ * 表达、煽动或宣扬基于种族、性别、民族、宗教、国籍、性取向、残疾状况或种姓的仇恨的内容。
+ */
+ private boolean hate;
+ /**
+ * 仇恨内容,还包括对目标群体的暴力或严重伤害。
+ */
+ @JsonProperty("hate/threatening")
+ private boolean hateThreatening;
+ /**
+ * 宣扬、鼓励或描绘自残行为(例如自杀、割伤和饮食失调)的内容。
+ */
+ @JsonProperty("self-harm")
+ private boolean selfHarm;
+ /**
+ * 旨在引起性兴奋的内容,例如对性活动的描述,或宣传性服务(不包括性教育和健康)的内容。
+ */
+ private boolean sexual;
+ /**
+ * 包含未满 18 周岁的个人的色情内容。
+ */
+ @JsonProperty("sexual/minors")
+ private boolean sexualMinors;
+ /**
+ * 宣扬或美化暴力或歌颂他人遭受苦难或羞辱的内容。
+ */
+ private boolean violence;
+ /**
+ * 以极端血腥细节描绘死亡、暴力或严重身体伤害的暴力内容。
+ */
+ @JsonProperty("violence/graphic")
+ private boolean violenceGraphic;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/moderations/CategoryScores.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/moderations/CategoryScores.java
new file mode 100644
index 0000000..f4ee50e
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/moderations/CategoryScores.java
@@ -0,0 +1,31 @@
+package org.aibidding.common.chat.entity.moderations;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class CategoryScores implements Serializable {
+ private BigDecimal hate;
+ @JsonProperty("hate/threatening")
+ private BigDecimal hateThreatening;
+ @JsonProperty("self-harm")
+ private BigDecimal selfHarm;
+ private BigDecimal sexual;
+ @JsonProperty("sexual/minors")
+ private BigDecimal sexualMinors;
+ private BigDecimal violence;
+ @JsonProperty("violence/graphic")
+ private BigDecimal violenceGraphic;
+
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/moderations/Moderation.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/moderations/Moderation.java
new file mode 100644
index 0000000..dd95749
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/moderations/Moderation.java
@@ -0,0 +1,54 @@
+package org.aibidding.common.chat.entity.moderations;
+
+import lombok.*;
+import lombok.extern.slf4j.Slf4j;
+import org.aibidding.common.chat.openai.exception.CommonError;
+import org.aibidding.common.core.exception.base.BaseException;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 描述:文本审核,敏感词鉴别
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Getter
+@Builder
+@Slf4j
+@NoArgsConstructor
+@AllArgsConstructor
+public class Moderation implements Serializable {
+
+ @NonNull
+ private List input;
+ @Builder.Default
+ private String model = Model.TEXT_MODERATION_LATEST.getName();
+
+ public void setInput(List input) {
+ if (Objects.isNull(input) || input.size() == 0) {
+ log.error("input不能为空");
+ throw new BaseException(CommonError.PARAM_ERROR.msg());
+ }
+ this.input = input;
+ }
+
+ public void setModel(Model model) {
+ if (Objects.isNull(model)) {
+ model = Model.TEXT_MODERATION_LATEST;
+ }
+ this.model = model.getName();
+ }
+
+ @Getter
+ @AllArgsConstructor
+ public enum Model {
+ TEXT_MODERATION_STABLE("text-moderation-stable"),
+ TEXT_MODERATION_LATEST("text-moderation-latest"),
+ ;
+
+ private String name;
+ }
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/moderations/ModerationResponse.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/moderations/ModerationResponse.java
new file mode 100644
index 0000000..3813fa1
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/moderations/ModerationResponse.java
@@ -0,0 +1,21 @@
+package org.aibidding.common.chat.entity.moderations;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ModerationResponse implements Serializable {
+ private String id;
+ private String model;
+ private List results;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/moderations/Result.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/moderations/Result.java
new file mode 100644
index 0000000..58efcce
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/moderations/Result.java
@@ -0,0 +1,22 @@
+package org.aibidding.common.chat.entity.moderations;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Result implements Serializable {
+ private Categories categories;
+ @JsonProperty("category_scores")
+ private CategoryScores categoryScores;
+ private boolean flagged;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/whisper/Transcriptions.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/whisper/Transcriptions.java
new file mode 100644
index 0000000..4d640f8
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/whisper/Transcriptions.java
@@ -0,0 +1,49 @@
+package org.aibidding.common.chat.entity.whisper;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.FieldNameConstants;
+
+/**
+ * @author Admin
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@FieldNameConstants
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class Transcriptions extends Whisper {
+ /**
+ * 模型目前只支持这一种:WHISPER_1
+ */
+ @Builder.Default
+ private String model = Model.WHISPER_1.getName();
+ /**
+ * 提示语,需要与语音语言匹配
+ */
+ private String prompt;
+ /**
+ * 输出的格式,采用以下选项之一:json、text、srt、verbose_json 或 vtt。
+ * 默认值:json
+ */
+ @JsonProperty("response_format")
+ @Builder.Default
+ private String responseFormat = ResponseFormat.JSON.getName();
+ /**
+ * 温度控制随机效果:0-1,值越大输出更加随机
+ * 默认值:0
+ */
+ @Builder.Default
+ private Double temperature = 0d;
+ /**
+ * 输入音频的语言,以 ISO-639-1 格式提供输入语言将提高准确性和延迟。
+ * 参考:ISO-639-1
+ */
+ private String language;
+
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/whisper/Translations.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/whisper/Translations.java
new file mode 100644
index 0000000..009574a
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/whisper/Translations.java
@@ -0,0 +1,41 @@
+package org.aibidding.common.chat.entity.whisper;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.FieldNameConstants;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@FieldNameConstants
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class Translations {
+ /**
+ * 模型目前只支持这一种:WHISPER_1
+ */
+ @Builder.Default
+ private String model = Whisper.Model.WHISPER_1.getName();
+ /**
+ * 提示语,需要与语音语言匹配
+ */
+ private String prompt;
+ /**
+ * 输出的格式,采用以下选项之一:json、text、srt、verbose_json 或 vtt。
+ * 默认值:json
+ */
+ @JsonProperty("response_format")
+ @Builder.Default
+ private String responseFormat = Whisper.ResponseFormat.JSON.getName();
+ /**
+ * 温度控制随机效果:0-1,值越大输出更加随机
+ * 默认值:0
+ */
+ @Builder.Default
+ private double temperature = 0;
+
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/whisper/Whisper.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/whisper/Whisper.java
new file mode 100644
index 0000000..fa89689
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/whisper/Whisper.java
@@ -0,0 +1,38 @@
+package org.aibidding.common.chat.entity.whisper;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.Getter;
+
+import java.io.Serializable;
+
+/**
+ * 描述:语音转文字
+ *
+ * @author https:www.unfbx.com
+ * @since 2023-03-02
+ */
+@Data
+public class Whisper implements Serializable {
+
+
+ @Getter
+ @AllArgsConstructor
+ public enum Model {
+ WHISPER_1("whisper-1"),
+ ;
+ private String name;
+ }
+
+ @Getter
+ @AllArgsConstructor
+ public enum ResponseFormat {
+ JSON("json"),
+ TEXT("text"),
+ SRT("srt"),
+ VERBOSE_JSON("verbose_json"),
+ VTT("vtt"),
+ ;
+ private String name;
+ }
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/whisper/WhisperResponse.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/whisper/WhisperResponse.java
new file mode 100644
index 0000000..7556932
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/entity/whisper/WhisperResponse.java
@@ -0,0 +1,19 @@
+package org.aibidding.common.chat.entity.whisper;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 描述:
+ *
+ * @author https:www.unfbx.com
+ * @since 2023-03-02
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class WhisperResponse implements Serializable {
+
+ private String text;
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/handler/PlusWebSocketHandler.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/handler/PlusWebSocketHandler.java
new file mode 100644
index 0000000..b454015
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/handler/PlusWebSocketHandler.java
@@ -0,0 +1,122 @@
+package org.aibidding.common.chat.handler;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.aibidding.common.chat.config.LocalCache;
+import org.aibidding.common.chat.entity.chat.ChatCompletion;
+import org.aibidding.common.chat.entity.chat.Message;
+import org.aibidding.common.chat.holder.WebSocketSessionHolder;
+import org.aibidding.common.chat.listener.WebSocketEventListener;
+import org.aibidding.common.chat.openai.OpenAiStreamClient;
+import org.aibidding.common.chat.utils.WebSocketUtils;
+import org.aibidding.common.core.utils.SpringUtils;
+import org.springframework.web.socket.*;
+import org.springframework.web.socket.handler.AbstractWebSocketHandler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * WebSocketHandler 实现类
+ *
+ * @author zendwang
+ */
+@Slf4j
+public class PlusWebSocketHandler extends AbstractWebSocketHandler {
+
+ /**
+ * 连接成功后
+ */
+ @Override
+ public void afterConnectionEstablished(WebSocketSession session) {
+ WebSocketSessionHolder.addSession(session.getId(), session);
+ }
+
+ /**
+ * 处理发送来的文本消息
+ *
+ * @param session
+ * @param message
+ */
+ @Override
+ protected void handleTextMessage(WebSocketSession session, TextMessage message) {
+ WebSocketEventListener eventSourceListener = new WebSocketEventListener(session);
+ String messageContext = (String) LocalCache.CACHE.get(session.getId());
+ List messages = new ArrayList<>();
+ if (StrUtil.isNotBlank(messageContext)) {
+ messages = JSONUtil.toList(messageContext, Message.class);
+ // 上下文长度
+ int contextSize=10;
+ if (messages.size() >= contextSize) {
+ messages = messages.subList(1, contextSize);
+ }
+ Message currentMessage = Message.builder().content(message.getPayload()).role(Message.Role.USER).build();
+ messages.add(currentMessage);
+ } else {
+ Message currentMessage = Message.builder().content(message.getPayload()).role(Message.Role.USER).build();
+ messages.add(currentMessage);
+ }
+ ChatCompletion chatCompletion = ChatCompletion
+ .builder()
+ .model("gpt-4o-mini")
+ .messages(messages)
+ .temperature(0.2)
+ .stream(true)
+ .build();
+ OpenAiStreamClient openAiStreamClient=(OpenAiStreamClient) SpringUtils.context().getBean("openAiStreamClient");
+ openAiStreamClient.streamChatCompletion(chatCompletion, eventSourceListener);
+ LocalCache.CACHE.put(session.getId(), JSONUtil.toJsonStr(messages), LocalCache.TIMEOUT);
+ }
+
+
+ @Override
+ protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
+ super.handleBinaryMessage(session, message);
+ }
+
+ /**
+ * 心跳监测的回复
+ *
+ * @param session
+ * @param message
+ * @throws Exception
+ */
+ @Override
+ protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception {
+ WebSocketUtils.sendPongMessage(session);
+ }
+
+ /**
+ * 连接出错时
+ *
+ * @param session
+ * @param exception
+ * @throws Exception
+ */
+ @Override
+ public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
+ log.error("[transport error] sessionId: {} , exception:{}", session.getId(), exception.getMessage());
+ }
+
+ /**
+ * 连接关闭后
+ *
+ * @param session
+ * @param status
+ */
+ @Override
+ public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
+ WebSocketSessionHolder.removeSession(session.getId());
+ }
+
+ /**
+ * 指示处理程序是否支持接收部分消息
+ *
+ * @return 如果支持接收部分消息,则返回true;否则返回false
+ */
+ @Override
+ public boolean supportsPartialMessages() {
+ return false;
+ }
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/holder/WebSocketSessionHolder.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/holder/WebSocketSessionHolder.java
new file mode 100644
index 0000000..8e1ac2c
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/holder/WebSocketSessionHolder.java
@@ -0,0 +1,37 @@
+package org.aibidding.common.chat.holder;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.springframework.web.socket.WebSocketSession;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * WebSocketSession 用于保存当前所有在线的会话信息
+ *
+ * @author zendwang
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class WebSocketSessionHolder {
+
+ private static final Map USER_SESSION_MAP = new ConcurrentHashMap<>();
+
+ public static void addSession(String sessionKey, WebSocketSession session) {
+ USER_SESSION_MAP.put(sessionKey, session);
+ }
+
+ public static void removeSession(String sessionKey) {
+ if (USER_SESSION_MAP.containsKey(sessionKey)) {
+ USER_SESSION_MAP.remove(sessionKey);
+ }
+ }
+
+ public static WebSocketSession getSessions(Long sessionKey) {
+ return USER_SESSION_MAP.get(sessionKey);
+ }
+
+ public static Boolean existSession(Long sessionKey) {
+ return USER_SESSION_MAP.containsKey(sessionKey);
+ }
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/interceptor/PlusWebSocketInterceptor.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/interceptor/PlusWebSocketInterceptor.java
new file mode 100644
index 0000000..ffbdd9d
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/interceptor/PlusWebSocketInterceptor.java
@@ -0,0 +1,46 @@
+package org.aibidding.common.chat.interceptor;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.web.socket.WebSocketHandler;
+import org.springframework.web.socket.server.HandshakeInterceptor;
+
+import java.util.Map;
+
+/**
+ * WebSocket握手请求的拦截器
+ *
+ * @author zendwang
+ */
+@Slf4j
+public class PlusWebSocketInterceptor implements HandshakeInterceptor {
+
+ /**
+ * 握手前
+ *
+ * @param request request
+ * @param response response
+ * @param wsHandler wsHandler
+ * @param attributes attributes
+ * @return 是否握手成功
+ */
+ @Override
+ public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map attributes) {
+ return true;
+ }
+
+
+ /**
+ * 握手后
+ *
+ * @param request request
+ * @param response response
+ * @param wsHandler wsHandler
+ * @param exception 异常
+ */
+ @Override
+ public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
+
+ }
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/listener/WebSocketEventListener.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/listener/WebSocketEventListener.java
new file mode 100644
index 0000000..9310a31
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/listener/WebSocketEventListener.java
@@ -0,0 +1,90 @@
+package org.aibidding.common.chat.listener;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+import okhttp3.sse.EventSource;
+import okhttp3.sse.EventSourceListener;
+import org.aibidding.common.chat.entity.chat.ChatCompletionResponse;
+import org.springframework.web.socket.TextMessage;
+import org.springframework.web.socket.WebSocketSession;
+
+import java.util.Objects;
+
+/**
+ * 描述:OpenAI流式输出Socket接收
+ *
+ * @author https:www.unfbx.com
+ * @date 2023-03-23
+ */
+@Slf4j
+public class WebSocketEventListener extends EventSourceListener {
+
+ private WebSocketSession session;
+
+ /**
+ * 消息结束标识
+ */
+ private final String msgEnd = "[DONE]";
+
+ public WebSocketEventListener(WebSocketSession session) {
+ this.session = session;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onOpen(EventSource eventSource, Response response) {
+ log.info("OpenAI建立Socket连接...");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SneakyThrows
+ @Override
+ public void onEvent(EventSource eventSource, String id, String type, String data) {
+ log.info("OpenAI返回数据:{}", data);
+ if (data.equals(msgEnd)) {
+ log.info("OpenAI返回数据结束了");
+ session.sendMessage(new TextMessage(msgEnd));
+ return;
+ }
+ ObjectMapper mapper = new ObjectMapper();
+ // 读取Json
+ ChatCompletionResponse completionResponse = mapper.readValue(data, ChatCompletionResponse.class);
+ String delta = "";
+ try {
+ delta = mapper.writeValueAsString(completionResponse.getChoices().get(0).getDelta());
+ }catch (Exception e){
+ log.error("转换失败{}",e.getMessage());
+ }
+ session.sendMessage(new TextMessage(delta));
+ }
+
+
+ @Override
+ public void onClosed(EventSource eventSource) {
+ log.info("OpenAI关闭Socket连接...");
+ }
+
+
+ @SneakyThrows
+ @Override
+ public void onFailure(EventSource eventSource, Throwable t, Response response) {
+ if (Objects.isNull(response)) {
+ return;
+ }
+ ResponseBody body = response.body();
+ if (Objects.nonNull(body)) {
+ // 返回非流式回复内容
+ log.error("Socket连接异常data:{},异常:{}", body.string(), t);
+ } else {
+ log.error("Socket连接异常data:{},异常:{}", response, t);
+ }
+ eventSource.cancel();
+ }
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/listener/WebSocketTopicListener.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/listener/WebSocketTopicListener.java
new file mode 100644
index 0000000..2bc012d
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/listener/WebSocketTopicListener.java
@@ -0,0 +1,38 @@
+package org.aibidding.common.chat.listener;
+
+import cn.hutool.core.collection.CollUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.aibidding.common.chat.holder.WebSocketSessionHolder;
+import org.aibidding.common.chat.utils.WebSocketUtils;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.core.Ordered;
+
+/**
+ * WebSocket 主题订阅监听器
+ *
+ * @author zendwang
+ */
+@Slf4j
+public class WebSocketTopicListener implements ApplicationRunner, Ordered {
+
+ @Override
+ public void run(ApplicationArguments args) throws Exception {
+ WebSocketUtils.subscribeMessage((message) -> {
+ log.info("WebSocket主题订阅收到消息session keys={} message={}!", message.getSessionKeys(), message.getMessage());
+ if (CollUtil.isNotEmpty(message.getSessionKeys())) {
+ message.getSessionKeys().forEach(key -> {
+ if (WebSocketSessionHolder.existSession(key)) {
+ WebSocketUtils.sendMessage(key, message.getMessage());
+ }
+ });
+ }
+ });
+ log.info("初始化WebSocket主题订阅监听器成功");
+ }
+
+ @Override
+ public int getOrder() {
+ return -1;
+ }
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/localModels/LocalModelsofitClient.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/localModels/LocalModelsofitClient.java
new file mode 100644
index 0000000..8e7a43d
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/localModels/LocalModelsofitClient.java
@@ -0,0 +1,198 @@
+package org.aibidding.common.chat.localModels;
+
+import io.micrometer.common.util.StringUtils;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.OkHttpClient;
+import org.aibidding.common.chat.entity.models.LocalModelsSearchRequest;
+import org.aibidding.common.chat.entity.models.LocalModelsSearchResponse;
+import org.springframework.stereotype.Service;
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+import retrofit2.Retrofit;
+import retrofit2.converter.jackson.JacksonConverterFactory;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+@Slf4j
+@Service
+public class LocalModelsofitClient {
+ private static final String BASE_URL = "http://127.0.0.1:5000"; // Flask 服务的 URL
+ private static Retrofit retrofit = null;
+
+ // 获取 Retrofit 实例
+ public static Retrofit getRetrofitInstance() {
+ if (retrofit == null) {
+ OkHttpClient client = new OkHttpClient.Builder()
+ .build();
+
+ retrofit = new Retrofit.Builder()
+ .baseUrl(BASE_URL)
+ .client(client)
+ .addConverterFactory(JacksonConverterFactory.create()) // 使用 Jackson 处理 JSON 转换
+ .build();
+ }
+ return retrofit;
+ }
+
+ /**
+ * 向 Flask 服务发送文本向量化请求
+ *
+ * @param queries 查询文本列表
+ * @param modelName 模型名称
+ * @param delimiter 文本分隔符
+ * @param topK 返回的结果数
+ * @param blockSize 文本块大小
+ * @param overlapChars 重叠字符数
+ * @return 返回计算得到的 Top K 嵌入向量列表
+ */
+
+ public static List> getTopKEmbeddings(
+ List queries,
+ String modelName,
+ String delimiter,
+ int topK,
+ int blockSize,
+ int overlapChars) {
+
+ modelName = (!StringUtils.isEmpty(modelName)) ? modelName : "msmarco-distilbert-base-tas-b"; // 默认模型名称
+ delimiter = (!StringUtils.isEmpty(delimiter) ) ? delimiter : "."; // 默认分隔符
+ topK = (topK > 0) ? topK : 3; // 默认返回 3 个结果
+ blockSize = (blockSize > 0) ? blockSize : 500; // 默认文本块大小为 500
+ overlapChars = (overlapChars > 0) ? overlapChars : 50; // 默认重叠字符数为 50
+
+ // 创建 Retrofit 实例
+ Retrofit retrofit = getRetrofitInstance();
+
+ // 创建 SearchService 接口
+ SearchService service = retrofit.create(SearchService.class);
+
+ // 创建请求对象 LocalModelsSearchRequest
+ LocalModelsSearchRequest request = new LocalModelsSearchRequest(
+ queries, // 查询文本列表
+ modelName, // 模型名称
+ delimiter, // 文本分隔符
+ topK, // 返回的结果数
+ blockSize, // 文本块大小
+ overlapChars // 重叠字符数
+ );
+
+ final CountDownLatch latch = new CountDownLatch(1); // 创建一个 CountDownLatch
+ final List>[] topKEmbeddings = new List[]{null}; // 使用数组来存储结果(因为 Java 不支持直接修改 List)
+
+ // 发起异步请求
+ service.vectorize(request).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ if (response.isSuccessful()) {
+ LocalModelsSearchResponse searchResponse = response.body();
+ if (searchResponse != null) {
+ topKEmbeddings[0] = searchResponse.getTopKEmbeddings().get(0); // 获取结果
+ log.info("Successfully retrieved embeddings");
+ } else {
+ log.error("Response body is null");
+ }
+ } else {
+ log.error("Request failed. HTTP error code: " + response.code());
+ }
+ latch.countDown(); // 请求完成,减少计数
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ t.printStackTrace();
+ log.error("Request failed: ", t);
+ latch.countDown(); // 请求失败,减少计数
+ }
+ });
+
+ try {
+ latch.await(); // 等待请求完成
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ return topKEmbeddings[0]; // 返回结果
+ }
+
+// public static void main(String[] args) {
+// // 示例调用
+// List queries = Arrays.asList("What is artificial intelligence?", "AI is transforming industries.");
+// String modelName = "msmarco-distilbert-base-tas-b";
+// String delimiter = ".";
+// int topK = 3;
+// int blockSize = 500;
+// int overlapChars = 50;
+//
+// List> topKEmbeddings = getTopKEmbeddings(queries, modelName, delimiter, topK, blockSize, overlapChars);
+//
+// // 打印结果
+// if (topKEmbeddings != null) {
+// System.out.println("Top K embeddings: ");
+// for (List embedding : topKEmbeddings) {
+// System.out.println(embedding);
+// }
+// } else {
+// System.out.println("No embeddings returned.");
+// }
+// }
+
+
+// public static void main(String[] args) {
+// // 创建 Retrofit 实例
+// Retrofit retrofit = LocalModelsofitClient.getRetrofitInstance();
+//
+// // 创建 SearchService 接口
+// SearchService service = retrofit.create(SearchService.class);
+//
+// // 创建请求对象 LocalModelsSearchRequest
+// LocalModelsSearchRequest request = new LocalModelsSearchRequest(
+// Arrays.asList("What is artificial intelligence?", "AI is transforming industries."), // 查询文本列表
+// "msmarco-distilbert-base-tas-b", // 模型名称
+// ".", // 分隔符
+// 3, // 返回的结果数
+// 500, // 文本块大小
+// 50 // 重叠字符数
+// );
+//
+// // 发起请求
+// service.vectorize(request).enqueue(new Callback() {
+// @Override
+// public void onResponse(Call call, Response response) {
+// if (response.isSuccessful()) {
+// LocalModelsSearchResponse searchResponse = response.body();
+// System.out.println("Response Body: " + response.body()); // Print the whole response body for debugging
+//
+// if (searchResponse != null) {
+// // If the response is not null, process it.
+// // Example: Extract the embeddings and print them
+// List>> topKEmbeddings = searchResponse.getTopKEmbeddings();
+// if (topKEmbeddings != null) {
+// // Print the Top K embeddings
+//
+// } else {
+// System.err.println("Top K embeddings are null");
+// }
+//
+// // If there is more information you want to process, handle it here
+//
+// } else {
+// System.err.println("Response body is null");
+// }
+// } else {
+// System.err.println("Request failed. HTTP error code: " + response.code());
+// log.error("Failed to retrieve data. HTTP error code: " + response.code());
+// }
+// }
+//
+// @Override
+// public void onFailure(Call call, Throwable t) {
+// // 请求失败,打印错误
+// t.printStackTrace();
+// log.error("Request failed: ", t);
+// }
+// });
+// }
+
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/localModels/SearchService.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/localModels/SearchService.java
new file mode 100644
index 0000000..74169e1
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/localModels/SearchService.java
@@ -0,0 +1,25 @@
+package org.aibidding.common.chat.localModels;
+
+
+
+import org.aibidding.common.chat.entity.models.LocalModelsSearchRequest;
+import org.aibidding.common.chat.entity.models.LocalModelsSearchResponse;
+import retrofit2.Call;
+import retrofit2.http.Body;
+import retrofit2.http.POST;
+/**
+ * @program: RUOYIAI
+ * @ClassName SearchService
+ * @description: 请求模型
+ * @author: hejh
+ * @create: 2025-03-15 17:27
+ * @Version 1.0
+ **/
+
+
+public interface SearchService {
+ @POST("/vectorize") // 与 Flask 服务中的路由匹配
+ Call vectorize(@Body LocalModelsSearchRequest request);
+}
+
+
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/openai/OpenAiApi.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/openai/OpenAiApi.java
new file mode 100644
index 0000000..c9a77d1
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/openai/OpenAiApi.java
@@ -0,0 +1,355 @@
+package org.aibidding.common.chat.openai;
+
+import io.reactivex.Single;
+import okhttp3.MultipartBody;
+import okhttp3.RequestBody;
+import okhttp3.ResponseBody;
+import org.aibidding.common.chat.entity.Tts.TextToSpeech;
+import org.aibidding.common.chat.entity.billing.BillingUsage;
+import org.aibidding.common.chat.entity.billing.CreditGrantsResponse;
+import org.aibidding.common.chat.entity.billing.Subscription;
+import org.aibidding.common.chat.entity.chat.ChatCompletion;
+import org.aibidding.common.chat.entity.chat.ChatCompletionResponse;
+import org.aibidding.common.chat.entity.chat.ChatCompletionWithPicture;
+import org.aibidding.common.chat.entity.common.DeleteResponse;
+import org.aibidding.common.chat.entity.common.OpenAiResponse;
+import org.aibidding.common.chat.entity.completions.Completion;
+import org.aibidding.common.chat.entity.completions.CompletionResponse;
+import org.aibidding.common.chat.entity.edits.Edit;
+import org.aibidding.common.chat.entity.edits.EditResponse;
+import org.aibidding.common.chat.entity.embeddings.Embedding;
+import org.aibidding.common.chat.entity.embeddings.EmbeddingResponse;
+import org.aibidding.common.chat.entity.engines.Engine;
+import org.aibidding.common.chat.entity.files.File;
+import org.aibidding.common.chat.entity.files.UploadFileResponse;
+import org.aibidding.common.chat.entity.fineTune.Event;
+import org.aibidding.common.chat.entity.fineTune.FineTune;
+import org.aibidding.common.chat.entity.fineTune.FineTuneDeleteResponse;
+import org.aibidding.common.chat.entity.fineTune.FineTuneResponse;
+import org.aibidding.common.chat.entity.images.Image;
+import org.aibidding.common.chat.entity.images.ImageResponse;
+import org.aibidding.common.chat.entity.models.Model;
+import org.aibidding.common.chat.entity.models.ModelResponse;
+import org.aibidding.common.chat.entity.moderations.Moderation;
+import org.aibidding.common.chat.entity.moderations.ModerationResponse;
+import org.aibidding.common.chat.entity.whisper.WhisperResponse;
+import retrofit2.Call;
+import retrofit2.http.*;
+
+import java.time.LocalDate;
+import java.util.Map;
+
+/**
+ * 描述: open ai官方api接口
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-15
+ */
+public interface OpenAiApi {
+
+ /**
+ * 模型列表
+ *
+ * @return Single ModelResponse
+ */
+ @GET("v1/models")
+ Single models();
+
+ /**
+ * models 返回的数据id
+ *
+ * @param id 模型主键
+ * @return Single Model
+ */
+ @GET("v1/models/{id}")
+ Single model(@Path("id") String id);
+
+ /**
+ * 文本问答
+ * Given a prompt, the model will return one or more predicted completions, and can also return the probabilities of alternative tokens at each position.
+ *
+ * @param completion 问答参数
+ * @return Single CompletionResponse
+ */
+ @POST("v1/completions")
+ Single completions(@Body Completion completion);
+
+ /**
+ * Creates a new edit for the provided input, instruction, and parameters.
+ * 文本修复
+ *
+ * @param edit 编辑参数
+ * @return Single EditResponse
+ */
+ @POST("v1/edits")
+ Single edits(@Body Edit edit);
+
+ /**
+ * Creates an image given a prompt.
+ * 根据描述生成图片
+ *
+ * @param image 图片对象
+ * @return Single ImageResponse
+ */
+ @POST("v1/images/generations")
+ Single genImages(@Body Image image);
+
+ /**
+ * Creates an edited or extended image given an original image and a prompt.
+ * 根据描述修改图片
+ *
+ * @param image 图片对象
+ * @param mask 图片对象
+ * @param requestBodyMap 请求参数
+ * @return Single ImageResponse
+ */
+ @Multipart
+ @POST("v1/images/edits")
+ Single editImages(@Part() MultipartBody.Part image,
+ @Part() MultipartBody.Part mask,
+ @PartMap() Map requestBodyMap
+ );
+
+ /**
+ * Creates a variation of a given image.
+ *
+ * @param image 图片对象
+ * @param requestBodyMap 请求参数
+ * @return Single ImageResponse
+ */
+ @Multipart
+ @POST("v1/images/variations")
+ Single variationsImages(@Part() MultipartBody.Part image,
+ @PartMap() Map requestBodyMap
+ );
+
+ /**
+ * 文本向量计算
+ *
+ * @param embedding 向量参数
+ * @return Single EmbeddingResponse
+ */
+ @POST("v1/embeddings")
+ Single embeddings(@Body Embedding embedding);
+
+
+ /**
+ * Returns a list of files that belong to the user's organization.
+ *
+ * @return Single OpenAiResponse File
+ */
+ @GET("/v1/files")
+ Single> files();
+
+ /**
+ * 删除文件
+ *
+ * @param fileId 文件id
+ * @return Single DeleteResponse
+ */
+ @DELETE("v1/files/{file_id}")
+ Single deleteFile(@Path("file_id") String fileId);
+
+ /**
+ * 上传文件
+ *
+ * @param purpose purpose
+ * @param file 文件对象
+ * @return Single UploadFileResponse
+ */
+ @Multipart
+ @POST("v1/files")
+ Single uploadFile(@Part MultipartBody.Part file,
+ @Part("purpose") RequestBody purpose);
+
+
+ /**
+ * 检索文件
+ *
+ * @param fileId 文件id
+ * @return Single File
+ */
+ @GET("v1/files/{file_id}")
+ Single retrieveFile(@Path("file_id") String fileId);
+
+ /**
+ * 检索文件内容
+ * ###不对免费用户开放###
+ * ###不对免费用户开放###
+ * ###不对免费用户开放###
+ *
+ * @param fileId 文件id
+ * @return Single ResponseBody
+ */
+ @Streaming
+ @GET("v1/files/{file_id}/content")
+ Single retrieveFileContent(@Path("file_id") String fileId);
+
+
+ /**
+ * 文本审核
+ *
+ * @param moderation 文本审核参数
+ * @return Single ModerationResponse
+ */
+ @POST("v1/moderations")
+ Single moderations(@Body Moderation moderation);
+
+
+ /**
+ * 创建微调作业
+ *
+ * @param fineTune 微调
+ * @return Single FineTuneResponse
+ */
+ @POST("v1/fine-tunes")
+ Single fineTune(@Body FineTune fineTune);
+
+ /**
+ * 微调作业集合
+ *
+ * @return Single OpenAiResponse FineTuneResponse
+ */
+ @GET("v1/fine-tunes")
+ Single> fineTunes();
+
+
+ /**
+ * 检索微调作业
+ *
+ * @return Single FineTuneResponse
+ */
+ @GET("v1/fine-tunes/{fine_tune_id}")
+ Single retrieveFineTune(@Path("fine_tune_id") String fineTuneId);
+
+ /**
+ * 取消微调作业
+ *
+ * @return Single FineTuneResponse
+ */
+ @POST("v1/fine-tunes/{fine_tune_id}/cancel")
+ Single cancelFineTune(@Path("fine_tune_id") String fineTuneId);
+
+ /**
+ * 微调作业事件列表
+ *
+ * @return Single OpenAiResponse Event
+ */
+ @GET("v1/fine-tunes/{fine_tune_id}/events")
+ Single> fineTuneEvents(@Path("fine_tune_id") String fineTuneId);
+
+ /**
+ * 删除微调作业模型
+ * Delete a fine-tuned model. You must have the Owner role in your organization.
+ *
+ * @return Single DeleteResponse
+ */
+ @DELETE("v1/models/{model}")
+ Single deleteFineTuneModel(@Path("model") String model);
+
+
+ /**
+ * 引擎列表
+ * 官方已废弃此接口
+ *
+ * @return Single OpenAiResponse Engine
+ */
+ @Deprecated
+ @GET("v1/engines")
+ Single> engines();
+
+ /**
+ * 检索引擎
+ * 官方已废弃此接口
+ *
+ * @param engineId 引擎id
+ * @return Engine
+ */
+ @Deprecated
+ @GET("v1/engines/{engine_id}")
+ Single engine(@Path("engine_id") String engineId);
+
+
+ /**
+ * 最新版的GPT-3.5 chat completion 更加贴近官方网站的问答模型
+ *
+ * @param chatCompletion chat completion
+ * @return 返回答案
+ */
+ @POST("v1/chat/completions")
+ Single chatCompletion(@Body ChatCompletion chatCompletion);
+
+
+ /**
+ * 语音转文字
+ *
+ * @param file 语音文件
+ * @param requestBodyMap 参数
+ * @return 文本
+ */
+ @Multipart
+ @POST("v1/audio/transcriptions")
+ Single speechToTextTranscriptions(@Part MultipartBody.Part file,
+ @PartMap() Map requestBodyMap);
+
+ /**
+ * 语音翻译:目前仅支持翻译为英文
+ *
+ * @param file 语音文件
+ * @param requestBodyMap 参数
+ * @return 文本
+ */
+ @Multipart
+ @POST("v1/audio/translations")
+ Single speechToTextTranslations(@Part MultipartBody.Part file,
+ @PartMap() Map requestBodyMap);
+
+ /**
+ * 余额查询
+ * 官方禁止访问此接口
+ *
+ * @return 余额结果
+ */
+ @GET("dashboard/billing/credit_grants")
+ @Deprecated
+ Single creditGrants();
+
+ /**
+ * 账户信息查询:里面包含总金额(美元)等信息
+ *
+ * @return 账户信息
+ */
+ @GET("v1/dashboard/billing/subscription")
+ Single subscription();
+
+ /**
+ * 账户调用接口消耗金额信息查询
+ * totalUsage = 账户总使用金额(美分)
+ *
+ * @param starDate 开始时间
+ * @param endDate 结束时间
+ * @return 消耗金额信息
+ */
+ @GET("v1/dashboard/billing/usage")
+ Single billingUsage(@Query("start_date") LocalDate starDate, @Query("end_date") LocalDate endDate);
+
+ /**
+ * 最新版的GPT-4 chat completion 支持图片输入
+ *
+ * @param chatCompletion chat completion
+ * @return 返回答案
+ */
+ @POST("v1/chat/completions")
+ Single chatCompletionWithPicture(@Body ChatCompletionWithPicture chatCompletion);
+
+ /**
+ * 文本转语音
+ *
+ * @param textToSpeech 参数
+ * @return ResponseBody body
+ * @since 1.1.2
+ */
+ @POST("v1/audio/speech")
+ @Streaming
+ Call textToSpeech(@Body TextToSpeech textToSpeech);
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/openai/OpenAiClient.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/openai/OpenAiClient.java
new file mode 100644
index 0000000..67daee7
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/openai/OpenAiClient.java
@@ -0,0 +1,890 @@
+package org.aibidding.common.chat.openai;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import io.reactivex.Single;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.MediaType;
+import okhttp3.MultipartBody;
+import okhttp3.OkHttpClient;
+import okhttp3.RequestBody;
+import org.aibidding.common.chat.constant.OpenAIConst;
+import org.aibidding.common.chat.entity.billing.BillingUsage;
+import org.aibidding.common.chat.entity.billing.Subscription;
+import org.aibidding.common.chat.entity.chat.*;
+import org.aibidding.common.chat.entity.common.DeleteResponse;
+import org.aibidding.common.chat.entity.common.OpenAiResponse;
+import org.aibidding.common.chat.entity.completions.Completion;
+import org.aibidding.common.chat.entity.completions.CompletionResponse;
+import org.aibidding.common.chat.entity.edits.Edit;
+import org.aibidding.common.chat.entity.edits.EditResponse;
+import org.aibidding.common.chat.entity.embeddings.Embedding;
+import org.aibidding.common.chat.entity.embeddings.EmbeddingResponse;
+import org.aibidding.common.chat.entity.engines.Engine;
+import org.aibidding.common.chat.entity.files.File;
+import org.aibidding.common.chat.entity.files.UploadFileResponse;
+import org.aibidding.common.chat.entity.fineTune.Event;
+import org.aibidding.common.chat.entity.fineTune.FineTune;
+import org.aibidding.common.chat.entity.fineTune.FineTuneDeleteResponse;
+import org.aibidding.common.chat.entity.fineTune.FineTuneResponse;
+import org.aibidding.common.chat.entity.images.*;
+import org.aibidding.common.chat.entity.models.Model;
+import org.aibidding.common.chat.entity.models.ModelResponse;
+import org.aibidding.common.chat.entity.moderations.Moderation;
+import org.aibidding.common.chat.entity.moderations.ModerationResponse;
+import org.aibidding.common.chat.entity.whisper.Translations;
+import org.aibidding.common.chat.entity.whisper.WhisperResponse;
+import org.aibidding.common.chat.openai.exception.CommonError;
+import org.aibidding.common.chat.openai.function.KeyRandomStrategy;
+import org.aibidding.common.chat.openai.function.KeyStrategyFunction;
+import org.aibidding.common.chat.openai.interceptor.DefaultOpenAiAuthInterceptor;
+import org.aibidding.common.chat.openai.interceptor.DynamicKeyOpenAiAuthInterceptor;
+import org.aibidding.common.chat.openai.interceptor.OpenAiAuthInterceptor;
+import org.aibidding.common.chat.openai.plugin.PluginAbstract;
+import org.aibidding.common.chat.openai.plugin.PluginParam;
+import org.aibidding.common.core.exception.base.BaseException;
+import org.jetbrains.annotations.NotNull;
+import retrofit2.Retrofit;
+import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
+import retrofit2.converter.jackson.JacksonConverterFactory;
+
+import java.time.LocalDate;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * 描述: open ai 客户端
+ *
+ * @author https:www.unfbx.com
+ * @since 2023-02-11
+ */
+
+@Slf4j
+public class OpenAiClient {
+ /**
+ * keys
+ */
+ @Getter
+ @NotNull
+ private List apiKey;
+ /**
+ * 自定义api host使用builder的方式构造client
+ */
+ @Getter
+ private String apiHost;
+ @Getter
+ private OpenAiApi openAiApi;
+ /**
+ * 自定义的okHttpClient
+ * 如果不自定义 ,就是用sdk默认的OkHttpClient实例
+ */
+ @Getter
+ private OkHttpClient okHttpClient;
+ /**
+ * api key的获取策略
+ */
+ @Getter
+ private KeyStrategyFunction, String> keyStrategy;
+
+ /**
+ * 自定义鉴权处理拦截器
+ * 可以不设置,默认实现:DefaultOpenAiAuthInterceptor
+ * 如需自定义实现参考:DealKeyWithOpenAiAuthInterceptor
+ *
+ * @see DynamicKeyOpenAiAuthInterceptor
+ * @see DefaultOpenAiAuthInterceptor
+ */
+ @Getter
+ private OpenAiAuthInterceptor authInterceptor;
+
+ /**
+ * 构造器
+ *
+ * @return OpenAiClient.Builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * 构造
+ *
+ * @param builder
+ */
+ private OpenAiClient(Builder builder) {
+ if (CollectionUtil.isEmpty(builder.apiKey)) {
+ throw new BaseException(CommonError.API_KEYS_NOT_NUL.msg()
+ );
+ }
+ apiKey = builder.apiKey;
+
+ if (StrUtil.isBlank(builder.apiHost)) {
+ builder.apiHost = OpenAIConst.OPENAI_HOST;
+ }
+ apiHost = builder.apiHost;
+
+ if (Objects.isNull(builder.keyStrategy)) {
+ builder.keyStrategy = new KeyRandomStrategy();
+ }
+ keyStrategy = builder.keyStrategy;
+
+ if (Objects.isNull(builder.authInterceptor)) {
+ builder.authInterceptor = new DefaultOpenAiAuthInterceptor();
+ }
+ authInterceptor = builder.authInterceptor;
+ authInterceptor.setApiKey(this.apiKey);
+ authInterceptor.setKeyStrategy(this.keyStrategy);
+
+ if (Objects.isNull(builder.okHttpClient)) {
+ builder.okHttpClient = this.okHttpClient();
+ } else {
+ //自定义的okhttpClient 需要增加api keys
+ builder.okHttpClient = builder.okHttpClient
+ .newBuilder()
+ .addInterceptor(authInterceptor)
+ .build();
+ }
+ okHttpClient = builder.okHttpClient;
+ this.openAiApi = new Retrofit.Builder()
+ .baseUrl(apiHost)
+ .client(okHttpClient)
+ .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
+ .addConverterFactory(JacksonConverterFactory.create())
+ .build().create(OpenAiApi.class);
+ }
+
+
+ /**
+ * 创建默认OkHttpClient
+ *
+ * @return
+ */
+ private OkHttpClient okHttpClient() {
+ if (Objects.isNull(this.authInterceptor)) {
+ this.authInterceptor = new DefaultOpenAiAuthInterceptor();
+ }
+ this.authInterceptor.setApiKey(this.apiKey);
+ this.authInterceptor.setKeyStrategy(this.keyStrategy);
+ return new OkHttpClient
+ .Builder()
+ .addInterceptor(this.authInterceptor)
+ .connectTimeout(30, TimeUnit.SECONDS)
+ .writeTimeout(30, TimeUnit.SECONDS)
+ .readTimeout(30, TimeUnit.SECONDS).build();
+ }
+
+ /**
+ * openAi模型列表
+ *
+ * @return Model list
+ */
+ public List models() {
+ Single models = this.openAiApi.models();
+ return models.blockingGet().getData();
+ }
+
+ /**
+ * openAi模型详细信息
+ *
+ * @param id 模型主键
+ * @return Model 模型类
+ */
+ public Model model(String id) {
+ if (Objects.isNull(id) || "".equals(id)) {
+ throw new BaseException(CommonError.PARAM_ERROR.msg());
+ }
+ Single model = this.openAiApi.model(id);
+ return model.blockingGet();
+ }
+
+
+ /**
+ * 问答接口
+ *
+ * @param completion 问答参数
+ * @return CompletionResponse
+ */
+ public CompletionResponse completions(Completion completion) {
+ Single completions = this.openAiApi.completions(completion);
+ return completions.blockingGet();
+ }
+
+ /**
+ * 问答接口-简易版
+ *
+ * @param question 问题描述
+ * @return CompletionResponse
+ */
+ public CompletionResponse completions(String question) {
+ Completion q = Completion.builder()
+ .prompt(question)
+ .build();
+ Single completions = this.openAiApi.completions(q);
+ return completions.blockingGet();
+ }
+
+ /**
+ * 文本修改
+ *
+ * @param edit 图片对象
+ * @return EditResponse
+ */
+ public EditResponse edit(Edit edit) {
+ Single edits = this.openAiApi.edits(edit);
+ return edits.blockingGet();
+ }
+
+ /**
+ * 根据描述生成图片
+ *
+ * @param prompt 描述信息
+ * @return ImageResponse
+ */
+ public ImageResponse genImages(String prompt) {
+ Image image = Image.builder().prompt(prompt).build();
+ return this.genImages(image);
+ }
+
+ /**
+ * 根据描述生成图片
+ *
+ * @param image 图片参数
+ * @return ImageResponse
+ */
+ public ImageResponse genImages(Image image) {
+ Single edits = this.openAiApi.genImages(image);
+ return edits.blockingGet();
+ }
+
+ /**
+ * Creates an edited or extended image given an original image and a prompt.
+ * 根据描述修改图片
+ *
+ * @param image 图片对象
+ * @param prompt 描述信息
+ * @return Item list
+ */
+ public List- editImages(java.io.File image, String prompt) {
+ ImageEdit imageEdit = ImageEdit.builder().prompt(prompt).build();
+ return this.editImages(image, null, imageEdit);
+ }
+
+ /**
+ * Creates an edited or extended image given an original image and a prompt.
+ * 根据描述修改图片
+ *
+ * @param image 图片对象
+ * @param imageEdit 图片参数
+ * @return Item list
+ */
+ public List
- editImages(java.io.File image, ImageEdit imageEdit) {
+ return this.editImages(image, null, imageEdit);
+ }
+
+ /**
+ * Creates an edited or extended image given an original image and a prompt.
+ * 根据描述修改图片
+ *
+ * @param image png格式的图片,最大4MB
+ * @param mask png格式的图片,最大4MB
+ * @param imageEdit 图片参数
+ * @return Item list
+ */
+ public List
- editImages(java.io.File image, java.io.File mask, ImageEdit imageEdit) {
+ checkImage(image);
+ checkImageFormat(image);
+ checkImageSize(image);
+ if (Objects.nonNull(mask)) {
+ checkImageFormat(image);
+ checkImageSize(image);
+ }
+ // 创建 RequestBody,用于封装构建RequestBody
+ RequestBody imageBody = RequestBody.create(MediaType.parse("multipart/form-data"), image);
+ MultipartBody.Part imageMultipartBody = MultipartBody.Part.createFormData("image", image.getName(), imageBody);
+ MultipartBody.Part maskMultipartBody = null;
+ if (Objects.nonNull(mask)) {
+ RequestBody maskBody = RequestBody.create(MediaType.parse("multipart/form-data"), mask);
+ maskMultipartBody = MultipartBody.Part.createFormData("mask", image.getName(), maskBody);
+ }
+ Map requestBodyMap = new HashMap<>();
+ requestBodyMap.put("prompt", RequestBody.create(MediaType.parse("multipart/form-data"), imageEdit.getPrompt()));
+ requestBodyMap.put("n", RequestBody.create(MediaType.parse("multipart/form-data"), imageEdit.getN().toString()));
+ requestBodyMap.put("size", RequestBody.create(MediaType.parse("multipart/form-data"), imageEdit.getSize()));
+ requestBodyMap.put("response_format", RequestBody.create(MediaType.parse("multipart/form-data"), imageEdit.getResponseFormat()));
+ if (!(Objects.isNull(imageEdit.getUser()) || "".equals(imageEdit.getUser()))) {
+ requestBodyMap.put("user", RequestBody.create(MediaType.parse("multipart/form-data"), imageEdit.getUser()));
+ }
+ Single imageResponse = this.openAiApi.editImages(
+ imageMultipartBody,
+ maskMultipartBody,
+ requestBodyMap
+ );
+ return imageResponse.blockingGet().getData();
+ }
+
+ /**
+ * Creates a variation of a given image.
+ *
+ * 变化图片,类似ai重做图片
+ *
+ * @param image 图片对象
+ * @param imageVariations 图片参数
+ * @return ImageResponse
+ */
+ public ImageResponse variationsImages(java.io.File image, ImageVariations imageVariations) {
+ checkImage(image);
+ checkImageFormat(image);
+ checkImageSize(image);
+ RequestBody imageBody = RequestBody.create(MediaType.parse("multipart/form-data"), image);
+ MultipartBody.Part multipartBody = MultipartBody.Part.createFormData("image", image.getName(), imageBody);
+ Map requestBodyMap = new HashMap<>();
+ requestBodyMap.put("n", RequestBody.create(MediaType.parse("multipart/form-data"), imageVariations.getN().toString()));
+ requestBodyMap.put("size", RequestBody.create(MediaType.parse("multipart/form-data"), imageVariations.getSize()));
+ requestBodyMap.put("response_format", RequestBody.create(MediaType.parse("multipart/form-data"), imageVariations.getResponseFormat()));
+ if (!(Objects.isNull(imageVariations.getUser()) || "".equals(imageVariations.getUser()))) {
+ requestBodyMap.put("user", RequestBody.create(MediaType.parse("multipart/form-data"), imageVariations.getUser()));
+ }
+ Single variationsImages = this.openAiApi.variationsImages(
+ multipartBody,
+ requestBodyMap
+ );
+ return variationsImages.blockingGet();
+ }
+
+ /**
+ * Creates a variation of a given image.
+ *
+ * @param image 图片对象
+ * @return ImageResponse
+ */
+ public ImageResponse variationsImages(java.io.File image) {
+ checkImage(image);
+ checkImageFormat(image);
+ checkImageSize(image);
+ ImageVariations imageVariations = ImageVariations.builder().build();
+ return this.variationsImages(image, imageVariations);
+ }
+
+ /**
+ * 校验图片不能为空
+ *
+ * @param image
+ */
+ private void checkImage(java.io.File image) {
+ if (Objects.isNull(image)) {
+ log.error("image不能为空");
+ throw new BaseException(CommonError.PARAM_ERROR.msg());
+ }
+ }
+
+ /**
+ * 校验图片格式
+ *
+ * @param image
+ */
+ private void checkImageFormat(java.io.File image) {
+ if (!(image.getName().endsWith("png") || image.getName().endsWith("PNG"))) {
+ log.error("image格式错误");
+ throw new BaseException(CommonError.PARAM_ERROR.msg());
+ }
+ }
+
+ /**
+ * 校验图片大小
+ *
+ * @param image
+ */
+ private void checkImageSize(java.io.File image) {
+ if (image.length() > 4 * 1024 * 1024) {
+ log.error("image最大支持4MB");
+ throw new BaseException(CommonError.PARAM_ERROR.msg());
+ }
+ }
+
+ /**
+ * 向量计算:单文本
+ *
+ * @param input 单文本
+ * @return EmbeddingResponse
+ */
+ public EmbeddingResponse embeddings(String input) {
+ List inputs = new ArrayList<>(1);
+ inputs.add(input);
+ Embedding embedding = Embedding.builder().input(inputs).build();
+ return this.embeddings(embedding);
+ }
+
+ /**
+ * 向量计算:集合文本
+ *
+ * @param input 文本集合
+ * @return EmbeddingResponse
+ */
+ public EmbeddingResponse embeddings(List input) {
+ Embedding embedding = Embedding.builder().input(input).build();
+ return this.embeddings(embedding);
+ }
+
+ /**
+ * 文本转换向量
+ *
+ * @param embedding 入参
+ * @return EmbeddingResponse
+ */
+ public EmbeddingResponse embeddings(Embedding embedding) {
+ Single embeddings = this.openAiApi.embeddings(embedding);
+ return embeddings.blockingGet();
+ }
+
+ /**
+ * 获取文件列表
+ *
+ * @return File list
+ */
+ public List files() {
+ Single> files = this.openAiApi.files();
+ return files.blockingGet().getData();
+ }
+
+ /**
+ * 删除文件
+ *
+ * @param fileId 文件id
+ * @return DeleteResponse
+ */
+ public DeleteResponse deleteFile(String fileId) {
+ Single deleteFile = this.openAiApi.deleteFile(fileId);
+ return deleteFile.blockingGet();
+ }
+
+ /**
+ * 上传文件
+ *
+ * @param purpose purpose
+ * @param file 文件对象
+ * @return UploadFileResponse
+ */
+ public UploadFileResponse uploadFile(String purpose, java.io.File file) {
+ // 创建 RequestBody,用于封装构建RequestBody
+ RequestBody fileBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
+ MultipartBody.Part multipartBody = MultipartBody.Part.createFormData("file", file.getName(), fileBody);
+
+ RequestBody purposeBody = RequestBody.create(MediaType.parse("multipart/form-data"), purpose);
+ Single uploadFileResponse = this.openAiApi.uploadFile(multipartBody, purposeBody);
+ return uploadFileResponse.blockingGet();
+ }
+
+ /**
+ * 上传文件
+ *
+ * @param file 文件
+ * @return UploadFileResponse
+ */
+ public UploadFileResponse uploadFile(java.io.File file) {
+ //purpose 官网示例默认是:fine-tune
+ return this.uploadFile("fine-tune", file);
+ }
+
+ /**
+ * 检索文件
+ *
+ * @param fileId 文件id
+ * @return File
+ */
+ public File retrieveFile(String fileId) {
+ Single fileContent = this.openAiApi.retrieveFile(fileId);
+ return fileContent.blockingGet();
+ }
+
+ /**
+ * 检索文件内容
+ * 免费用户无法使用此接口 #未经过测试
+ *
+ * @param fileId
+ * @return ResponseBody
+ */
+// public ResponseBody retrieveFileContent(String fileId) {
+// Single fileContent = this.openAiApi.retrieveFileContent(fileId);
+// return fileContent.blockingGet();
+// }
+
+ /**
+ * 文本审核
+ *
+ * @param input 待检测数据
+ * @return ModerationResponse
+ */
+ public ModerationResponse moderations(String input) {
+ List content = new ArrayList<>(1);
+ content.add(input);
+ Moderation moderation = Moderation.builder().input(content).build();
+ return this.moderations(moderation);
+ }
+
+ /**
+ * 文本审核
+ *
+ * @param input 待检测数据集合
+ * @return ModerationResponse
+ */
+ public ModerationResponse moderations(List input) {
+ Moderation moderation = Moderation.builder().input(input).build();
+ return this.moderations(moderation);
+ }
+
+ /**
+ * 文本审核
+ *
+ * @param moderation 审核参数
+ * @return ModerationResponse
+ */
+ public ModerationResponse moderations(Moderation moderation) {
+ Single moderations = this.openAiApi.moderations(moderation);
+ return moderations.blockingGet();
+ }
+
+ /**
+ * 创建微调模型
+ *
+ * @param fineTune 微调作业id
+ * @return FineTuneResponse
+ */
+ public FineTuneResponse fineTune(FineTune fineTune) {
+ Single fineTuneResponse = this.openAiApi.fineTune(fineTune);
+ return fineTuneResponse.blockingGet();
+ }
+
+ /**
+ * 创建微调模型
+ *
+ * @param trainingFileId 文件id,文件上传返回的id
+ * @return FineTuneResponse
+ */
+ public FineTuneResponse fineTune(String trainingFileId) {
+ FineTune fineTune = FineTune.builder().trainingFile(trainingFileId).build();
+ return this.fineTune(fineTune);
+ }
+
+ /**
+ * 微调模型列表
+ *
+ * @return FineTuneResponse list
+ */
+ public List fineTunes() {
+ Single> fineTunes = this.openAiApi.fineTunes();
+ return fineTunes.blockingGet().getData();
+ }
+
+ /**
+ * 检索微调作业
+ *
+ * @param fineTuneId 微调作业id
+ * @return FineTuneResponse
+ */
+ public FineTuneResponse retrieveFineTune(String fineTuneId) {
+ Single fineTune = this.openAiApi.retrieveFineTune(fineTuneId);
+ return fineTune.blockingGet();
+ }
+
+ /**
+ * 取消微调作业
+ *
+ * @param fineTuneId 主键
+ * @return FineTuneResponse
+ */
+ public FineTuneResponse cancelFineTune(String fineTuneId) {
+ Single fineTune = this.openAiApi.cancelFineTune(fineTuneId);
+ return fineTune.blockingGet();
+ }
+
+ /**
+ * 微调作业事件列表
+ *
+ * @param fineTuneId 微调作业id
+ * @return Event List
+ */
+ public List fineTuneEvents(String fineTuneId) {
+ Single> events = this.openAiApi.fineTuneEvents(fineTuneId);
+ return events.blockingGet().getData();
+ }
+
+ /**
+ * 删除微调作业模型
+ * Delete a fine-tuned model. You must have the Owner role in your organization.
+ *
+ * @param model 模型名称
+ * @return FineTuneDeleteResponse
+ */
+ public FineTuneDeleteResponse deleteFineTuneModel(String model) {
+ Single delete = this.openAiApi.deleteFineTuneModel(model);
+ return delete.blockingGet();
+ }
+
+
+ /**
+ * 引擎列表
+ *
+ * @return Engine List
+ */
+ @Deprecated
+ public List engines() {
+ Single> engines = this.openAiApi.engines();
+ return engines.blockingGet().getData();
+ }
+
+ /**
+ * 引擎详细信息
+ *
+ * @param engineId 引擎id
+ * @return Engine
+ */
+ @Deprecated
+ public Engine engine(String engineId) {
+ Single engine = this.openAiApi.engine(engineId);
+ return engine.blockingGet();
+ }
+
+ /**
+ * 最新版的GPT-3.5 chat completion 更加贴近官方网站的问答模型
+ *
+ * @param chatCompletion 问答参数
+ * @return 答案
+ */
+ public ChatCompletionResponse chatCompletion(ChatCompletion chatCompletion) {
+ Single chatCompletionResponse = this.openAiApi.chatCompletion(chatCompletion);
+ return chatCompletionResponse.blockingGet();
+ }
+
+ /**
+ * 简易版
+ *
+ * @param messages 问答参数
+ * @return 答案
+ */
+ public ChatCompletionResponse chatCompletion(List messages) {
+ ChatCompletion chatCompletion = ChatCompletion.builder().messages(messages).build();
+ return this.chatCompletion(chatCompletion);
+ }
+
+ /**
+ * 语音翻译:目前仅支持翻译为英文
+ *
+ * @param translations 参数
+ * @param file 语音文件 最大支持25MB mp3, mp4, mpeg, mpga, m4a, wav, webm
+ * @return 翻译后文本
+ */
+ public WhisperResponse speechToTextTranslations(java.io.File file, Translations translations) {
+ //文件
+ RequestBody fileBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
+ MultipartBody.Part multipartBody = MultipartBody.Part.createFormData("file", file.getName(), fileBody);
+ //自定义参数
+ Map requestBodyMap = new HashMap<>(5,1L);
+
+ if (StrUtil.isNotBlank(translations.getModel())) {
+ requestBodyMap.put(Translations.Fields.model, RequestBody.create(MediaType.parse("multipart/form-data"), translations.getModel()));
+ }
+ if (StrUtil.isNotBlank(translations.getPrompt())) {
+ requestBodyMap.put(Translations.Fields.prompt, RequestBody.create(MediaType.parse("multipart/form-data"), translations.getPrompt()));
+ }
+ if (StrUtil.isNotBlank(translations.getResponseFormat())) {
+ requestBodyMap.put(Translations.Fields.responseFormat, RequestBody.create(MediaType.parse("multipart/form-data"), translations.getResponseFormat()));
+ }
+ requestBodyMap.put(Translations.Fields.temperature, RequestBody.create(MediaType.parse("multipart/form-data"), String.valueOf(translations.getTemperature())));
+ Single whisperResponse = this.openAiApi.speechToTextTranslations(multipartBody, requestBodyMap);
+ return whisperResponse.blockingGet();
+ }
+
+ /**
+ * 插件问答简易版
+ * 默认取messages最后一个元素构建插件对话
+ * 默认模型:ChatCompletion.Model.GPT_3_5_TURBO_16K_0613
+ *
+ * @param chatCompletion 参数
+ * @param plugin 插件
+ * @param 插件自定义函数的请求值
+ * @param 插件自定义函数的返回值
+ * @return ChatCompletionResponse
+ */
+ public ChatCompletionResponse chatCompletionWithPlugin(ChatCompletion chatCompletion, PluginAbstract plugin) {
+ if (Objects.isNull(plugin)) {
+ return this.chatCompletion(chatCompletion);
+ }
+ if (CollectionUtil.isEmpty(chatCompletion.getMessages())) {
+ throw new BaseException(CommonError.MESSAGE_NOT_NUL.msg());
+ }
+ List messages = chatCompletion.getMessages();
+ Functions functions = Functions.builder()
+ .name(plugin.getFunction())
+ .description(plugin.getDescription())
+ .parameters(plugin.getParameters())
+ .build();
+ //没有值,设置默认值
+ if (Objects.isNull(chatCompletion.getFunctionCall())) {
+ chatCompletion.setFunctionCall("auto");
+ }
+ //tip: 覆盖自己设置的functions参数,使用plugin构造的functions
+ chatCompletion.setFunctions(Collections.singletonList(functions));
+ //调用OpenAi
+ ChatCompletionResponse functionCallChatCompletionResponse = this.chatCompletion(chatCompletion);
+ ChatChoice chatChoice = functionCallChatCompletionResponse.getChoices().get(0);
+ log.debug("构造的方法值:{}", chatChoice.getMessage().getFunctionCall());
+
+ R realFunctionParam = (R) JSONUtil.toBean(chatChoice.getMessage().getFunctionCall().getArguments(), plugin.getR());
+ T tq = plugin.func(realFunctionParam);
+
+ FunctionCall functionCall = FunctionCall.builder()
+ .arguments(chatChoice.getMessage().getFunctionCall().getArguments())
+ .name(plugin.getFunction())
+ .build();
+ messages.add(Message.builder().role(Message.Role.ASSISTANT).content("function_call").functionCall(functionCall).build());
+ messages.add(Message.builder().role(Message.Role.FUNCTION).name(plugin.getFunction()).content(plugin.content(tq)).build());
+ //设置第二次,请求的参数
+ chatCompletion.setFunctionCall(null);
+ chatCompletion.setFunctions(null);
+
+ ChatCompletionResponse chatCompletionResponse = this.chatCompletion(chatCompletion);
+ log.debug("自定义的方法返回值:{}", chatCompletionResponse.getChoices());
+ return chatCompletionResponse;
+ }
+
+ /**
+ * 插件问答简易版
+ * 默认取messages最后一个元素构建插件对话
+ * 默认模型:ChatCompletion.Model.GPT_3_5_TURBO_16K_0613
+ *
+ * @param messages 问答参数
+ * @param plugin 插件
+ * @param 插件自定义函数的请求值
+ * @param 插件自定义函数的返回值
+ * @return ChatCompletionResponse
+ */
+ public ChatCompletionResponse chatCompletionWithPlugin(List messages, PluginAbstract plugin) {
+ return chatCompletionWithPlugin(messages, ChatCompletion.Model.GPT_3_5_TURBO_16K_0613.getName(), plugin);
+ }
+
+ /**
+ * 插件问答简易版
+ * 默认取messages最后一个元素构建插件对话
+ *
+ * @param messages 问答参数
+ * @param model 模型
+ * @param plugin 插件
+ * @param 插件自定义函数的请求值
+ * @param 插件自定义函数的返回值
+ * @return ChatCompletionResponse
+ */
+ public ChatCompletionResponse chatCompletionWithPlugin(List messages, String model, PluginAbstract plugin) {
+ ChatCompletion chatCompletion = ChatCompletion.builder().messages(messages).model(model).build();
+ return this.chatCompletionWithPlugin(chatCompletion, plugin);
+ }
+
+ /**
+ * 简易版 语音翻译:目前仅支持翻译为英文
+ *
+ * @param file 语音文件 最大支持25MB mp3, mp4, mpeg, mpga, m4a, wav, webm
+ * @return 翻译后文本
+ */
+ public WhisperResponse speechToTextTranslations(java.io.File file) {
+ Translations translations = Translations.builder().build();
+ return this.speechToTextTranslations(file, translations);
+ }
+
+ /**
+ * 校验语音文件大小给出提示,目前官方限制25MB,后续可能会改动所以不报错只做提示
+ *
+ * @param file
+ */
+ private void checkSpeechFileSize(java.io.File file) {
+ if (file.length() > 25 * 1204 * 1024) {
+ log.warn("2023-03-02官方文档提示:文件不能超出25MB");
+ }
+ }
+
+ /**
+ * 账户信息查询:里面包含总金额等信息
+ *
+ * @return 账户信息
+ */
+ public Subscription subscription() {
+ Single subscription = this.openAiApi.subscription();
+ return subscription.blockingGet();
+ }
+ /**
+ * 账户调用接口消耗金额信息查询
+ * 最多查询100天
+ *
+ * @param starDate 开始时间
+ * @param endDate 结束时间
+ * @return 消耗金额信息
+ */
+ public BillingUsage billingUsage(@NotNull LocalDate starDate, @NotNull LocalDate endDate) {
+ Single billingUsage = this.openAiApi.billingUsage(starDate, endDate);
+ return billingUsage.blockingGet();
+ }
+
+
+ public static final class Builder {
+ /**
+ * api keys
+ */
+ private @NotNull List apiKey;
+ /**
+ * api请求地址,结尾处有斜杠
+ *
+ */
+ private String apiHost;
+ /**
+ * 自定义OkhttpClient
+ */
+ private OkHttpClient okHttpClient;
+
+ /**
+ * api key的获取策略
+ */
+ private KeyStrategyFunction keyStrategy;
+
+ /**
+ * 自定义鉴权拦截器
+ */
+ private OpenAiAuthInterceptor authInterceptor;
+
+ public Builder() {
+ }
+
+ /**
+ * @param val api请求地址,结尾处有斜杠
+ * @return Builder对象
+ */
+ public Builder apiHost(String val) {
+ apiHost = val;
+ return this;
+ }
+
+ public Builder apiKey(@NotNull List val) {
+ apiKey = val;
+ return this;
+ }
+
+ public Builder keyStrategy(KeyStrategyFunction val) {
+ keyStrategy = val;
+ return this;
+ }
+
+ public Builder okHttpClient(OkHttpClient val) {
+ okHttpClient = val;
+ return this;
+ }
+
+ public Builder authInterceptor(OpenAiAuthInterceptor val) {
+ authInterceptor = val;
+ return this;
+ }
+
+ public OpenAiClient build() {
+ return new OpenAiClient(this);
+ }
+ }
+}
diff --git a/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/openai/OpenAiStreamClient.java b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/openai/OpenAiStreamClient.java
new file mode 100644
index 0000000..6f68775
--- /dev/null
+++ b/aibidding-common/aibidding-common-chat/src/main/java/org/aibidding/common/chat/openai/OpenAiStreamClient.java
@@ -0,0 +1,671 @@
+package org.aibidding.common.chat.openai;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.ContentType;
+import cn.hutool.json.JSONUtil;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.reactivex.Single;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.*;
+import okhttp3.sse.EventSource;
+import okhttp3.sse.EventSourceListener;
+import okhttp3.sse.EventSources;
+import org.aibidding.common.chat.constant.OpenAIConst;
+import org.aibidding.common.chat.entity.Tts.TextToSpeech;
+import org.aibidding.common.chat.entity.billing.BillingUsage;
+import org.aibidding.common.chat.entity.billing.KeyInfo;
+import org.aibidding.common.chat.entity.billing.Subscription;
+import org.aibidding.common.chat.entity.chat.*;
+import org.aibidding.common.chat.entity.embeddings.Embedding;
+import org.aibidding.common.chat.entity.embeddings.EmbeddingResponse;
+import org.aibidding.common.chat.entity.files.UploadFileResponse;
+import org.aibidding.common.chat.entity.images.Image;
+import org.aibidding.common.chat.entity.images.ImageResponse;
+import org.aibidding.common.chat.entity.models.Model;
+import org.aibidding.common.chat.entity.models.ModelResponse;
+import org.aibidding.common.chat.entity.whisper.Transcriptions;
+import org.aibidding.common.chat.entity.whisper.WhisperResponse;
+import org.aibidding.common.chat.openai.exception.CommonError;
+import org.aibidding.common.chat.openai.function.KeyRandomStrategy;
+import org.aibidding.common.chat.openai.function.KeyStrategyFunction;
+import org.aibidding.common.chat.openai.interceptor.DefaultOpenAiAuthInterceptor;
+import org.aibidding.common.chat.openai.interceptor.DynamicKeyOpenAiAuthInterceptor;
+import org.aibidding.common.chat.openai.interceptor.OpenAiAuthInterceptor;
+import org.aibidding.common.chat.openai.plugin.PluginAbstract;
+import org.aibidding.common.chat.openai.plugin.PluginParam;
+import org.aibidding.common.chat.sse.DefaultPluginListener;
+import org.aibidding.common.chat.sse.PluginListener;
+import org.aibidding.common.core.exception.base.BaseException;
+import org.jetbrains.annotations.NotNull;
+import retrofit2.Call;
+import retrofit2.Retrofit;
+import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
+import retrofit2.converter.jackson.JacksonConverterFactory;
+
+import java.io.IOException;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 描述: open ai 客户端
+ *
+ * @author https:www.unfbx.com
+ * 2023-02-28
+ */
+
+@Getter
+@Slf4j
+@Setter
+public class OpenAiStreamClient {
+
+ @NotNull
+ private List apiKey;
+ /**
+ * 自定义api host使用builder的方式构造client
+ */
+ private String apiHost;
+
+ /**
+ * 自定义的okHttpClient
+ * 如果不自定义 ,就是用sdk默认的OkHttpClient实例
+ */
+ private OkHttpClient okHttpClient;
+
+ /**
+ * api key的获取策略
+ */
+ private KeyStrategyFunction, String> keyStrategy;
+
+ private OpenAiApi openAiApi;
+
+ /**
+ * 自定义鉴权处理拦截器
+ * 可以不设置,默认实现:DefaultOpenAiAuthInterceptor
+ * 如需自定义实现参考:DealKeyWithOpenAiAuthInterceptor
+ *
+ * @see DynamicKeyOpenAiAuthInterceptor
+ * @see DefaultOpenAiAuthInterceptor
+ */
+ private OpenAiAuthInterceptor authInterceptor;
+
+ private static final String DONE_SIGNAL = "[DONE]";
+
+ /**
+ * 构造实例对象
+ *
+ * @param builder
+ */
+ private OpenAiStreamClient(Builder builder) {
+ if (CollectionUtil.isEmpty(builder.apiKey)) {
+ throw new BaseException(CommonError.API_KEYS_NOT_NUL.msg());
+ }
+ apiKey = builder.apiKey;
+
+ if (StrUtil.isBlank(builder.apiHost)) {
+ builder.apiHost = OpenAIConst.OPENAI_HOST;
+ }
+ apiHost = builder.apiHost;
+
+ if (Objects.isNull(builder.keyStrategy)) {
+ builder.keyStrategy = new KeyRandomStrategy();
+ }
+ keyStrategy = builder.keyStrategy;
+
+ if (Objects.isNull(builder.authInterceptor)) {
+ builder.authInterceptor = new DefaultOpenAiAuthInterceptor();
+ }
+ authInterceptor = builder.authInterceptor;
+ //设置apiKeys和key的获取策略
+ authInterceptor.setApiKey(this.apiKey);
+ authInterceptor.setKeyStrategy(this.keyStrategy);
+
+ if (Objects.isNull(builder.okHttpClient)) {
+ builder.okHttpClient = this.okHttpClient();
+ } else {
+ //自定义的okhttpClient 需要增加api keys
+ builder.okHttpClient = builder.okHttpClient
+ .newBuilder()
+ .addInterceptor(authInterceptor)
+ .build();
+ }
+ okHttpClient = builder.okHttpClient;
+
+ this.openAiApi = new Retrofit.Builder()
+ .baseUrl(apiHost)
+ .client(okHttpClient)
+ .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
+ .addConverterFactory(JacksonConverterFactory.create())
+ .build().create(OpenAiApi.class);
+ }
+
+ /**
+ * 创建默认的OkHttpClient
+ */
+ private OkHttpClient okHttpClient() {
+ if (Objects.isNull(this.authInterceptor)) {
+ this.authInterceptor = new DefaultOpenAiAuthInterceptor();
+ }
+ this.authInterceptor.setApiKey(this.apiKey);
+ this.authInterceptor.setKeyStrategy(this.keyStrategy);
+ OkHttpClient okHttpClient = new OkHttpClient
+ .Builder()
+ .addInterceptor(this.authInterceptor)
+ .connectTimeout(10, TimeUnit.SECONDS)
+ .writeTimeout(50, TimeUnit.SECONDS)
+ .readTimeout(50, TimeUnit.SECONDS)
+ .build();
+ return okHttpClient;
+ }
+
+
+ /**
+ * 流式输出,最新版的GPT-3.5 chat completion 更加贴近官方网站的问答模型
+ *
+ * @param chatCompletion 问答参数
+ * @param eventSourceListener 监听器
+ */
+ public void streamChatCompletion(T chatCompletion, EventSourceListener eventSourceListener) {
+ if (Objects.isNull(eventSourceListener)) {
+ log.error("参数异常:EventSourceListener不能为空!");
+ throw new BaseException(CommonError.PARAM_ERROR.msg());
+ }
+ try {
+ EventSource.Factory factory = EventSources.createFactory(this.okHttpClient);
+ ObjectMapper mapper = new ObjectMapper();
+ String requestBody = mapper.writeValueAsString(chatCompletion);
+ Request request = new Request.Builder()
+ .url(this.apiHost + "v1/chat/completions")
+ .post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), requestBody))
+ .build();
+ factory.newEventSource(request, eventSourceListener);
+ } catch (Exception e) {
+ log.error("请求参数解析异常:{}", e.getMessage());
+ }
+ }
+
+ /**
+ * 插件问答简易版
+ * 默认取messages最后一个元素构建插件对话
+ * 默认模型:ChatCompletion.Model.GPT_3_5_TURBO_16K_0613
+ *
+ * @param chatCompletion 参数
+ * @param eventSourceListener sse监听器
+ * @param pluginEventSourceListener 插件sse监听器,收集function call返回信息
+ * @param plugin 插件
+ * @param 插件自定义函数的请求值
+ * @param 插件自定义函数的返回值
+ */
+ public void streamChatCompletionWithPlugin(ChatCompletion chatCompletion, EventSourceListener eventSourceListener, PluginListener pluginEventSourceListener, PluginAbstract plugin) {
+ if (Objects.isNull(plugin)) {
+ this.streamChatCompletion(chatCompletion, eventSourceListener);
+ return;
+ }
+ if (CollectionUtil.isEmpty(chatCompletion.getMessages())) {
+ throw new BaseException(CommonError.MESSAGE_NOT_NUL.msg());
+ }
+ Functions functions = Functions.builder()
+ .name(plugin.getFunction())
+ .description(plugin.getDescription())
+ .parameters(plugin.getParameters())
+ .build();
+ //没有值,设置默认值
+ if (Objects.isNull(chatCompletion.getFunctionCall())) {
+ chatCompletion.setFunctionCall("auto");
+ }
+ //tip: 覆盖自己设置的functions参数,使用plugin构造的functions
+ chatCompletion.setFunctions(Collections.singletonList(functions));
+ //调用OpenAi
+ if (Objects.isNull(pluginEventSourceListener)) {
+ pluginEventSourceListener = new DefaultPluginListener(this, eventSourceListener, plugin, chatCompletion);
+ }
+ this.streamChatCompletion(chatCompletion, pluginEventSourceListener);
+ }
+
+
+ /**
+ * 插件问答简易版
+ * 默认取messages最后一个元素构建插件对话
+ * 默认模型:ChatCompletion.Model.GPT_3_5_TURBO_16K_0613
+ *
+ * @param chatCompletion 参数
+ * @param eventSourceListener sse监听器
+ * @param plugin 插件
+ * @param 插件自定义函数的请求值
+ * @param 插件自定义函数的返回值
+ */
+ public void streamChatCompletionWithPlugin(ChatCompletion chatCompletion, EventSourceListener eventSourceListener, PluginAbstract plugin) {
+ PluginListener pluginEventSourceListener = new DefaultPluginListener(this, eventSourceListener, plugin, chatCompletion);
+ this.streamChatCompletionWithPlugin(chatCompletion, eventSourceListener, pluginEventSourceListener, plugin);
+ }
+
+
+ /**
+ * 插件问答简易版
+ * 默认取messages最后一个元素构建插件对话
+ * 默认模型:ChatCompletion.Model.GPT_3_5_TURBO_16K_0613
+ *
+ * @param messages 问答参数
+ * @param eventSourceListener sse监听器
+ * @param plugin 插件
+ * @param 插件自定义函数的请求值
+ * @param 插件自定义函数的返回值
+ */
+ public void streamChatCompletionWithPlugin(List messages, EventSourceListener eventSourceListener, PluginAbstract plugin) {
+ this.streamChatCompletionWithPlugin(messages, ChatCompletion.Model.GPT_3_5_TURBO_16K_0613.getName(), eventSourceListener, plugin);
+ }
+
+ /**
+ * 插件问答简易版
+ * 默认取messages最后一个元素构建插件对话
+ *
+ * @param messages 问答参数
+ * @param model 模型
+ * @param eventSourceListener eventSourceListener
+ * @param plugin 插件
+ * @param 插件自定义函数的请求值
+ * @param 插件自定义函数的返回值
+ */
+ public void streamChatCompletionWithPlugin(List messages, String model, EventSourceListener eventSourceListener, PluginAbstract plugin) {
+ ChatCompletion chatCompletion = ChatCompletion.builder().messages(messages).model(model).build();
+ this.streamChatCompletionWithPlugin(chatCompletion, eventSourceListener, plugin);
+ }
+
+
+ /**
+ * 根据描述生成图片
+ *
+ * @param image 图片参数
+ * @return ImageResponse
+ */
+ public ImageResponse genImages(Image image) {
+ Single edits = this.openAiApi.genImages(image);
+ return edits.blockingGet();
+ }
+
+ /**
+ * 最新版的GPT-3.5 chat completion 更加贴近官方网站的问答模型
+ *
+ * @param chatCompletion 问答参数
+ * @return 答案
+ */
+ public ChatCompletionResponse chatCompletion(T chatCompletion) {
+ if (chatCompletion instanceof ChatCompletion) {
+ Single chatCompletionResponse = this.openAiApi.chatCompletion((ChatCompletion) chatCompletion);
+ return chatCompletionResponse.blockingGet();
+ }
+ Single chatCompletionResponse = this.openAiApi.chatCompletionWithPicture((ChatCompletionWithPicture) chatCompletion);
+ return chatCompletionResponse.blockingGet();
+ }
+
+ /**
+ * 上传文件
+ *
+ * @param purpose purpose
+ * @param file 文件对象
+ * @return UploadFileResponse
+ */
+ public UploadFileResponse uploadFile(String purpose, java.io.File file) {
+ // 创建 RequestBody,用于封装构建RequestBody
+ RequestBody fileBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
+ MultipartBody.Part multipartBody = MultipartBody.Part.createFormData("file", file.getName(), fileBody);
+
+ RequestBody purposeBody = RequestBody.create(MediaType.parse("multipart/form-data"), purpose);
+ Single uploadFileResponse = this.openAiApi.uploadFile(multipartBody, purposeBody);
+ return uploadFileResponse.blockingGet();
+ }
+
+ /**
+ * 获取openKey账户信息(近90天)
+ *
+ * @param key
+ * @return KeyInfo
+ * @Date 2023/7/6
+ **/
+ public KeyInfo getKeyInfo(String key) {
+ Date now = new Date();
+ Date start = new Date(now.getTime() - (long) 90 * 24 * 60 * 60 * 1000);
+ Date end = new Date(now.getTime() + (long) 24 * 60 * 60 * 1000);
+
+ BillingUsage billingUsage = billingUsage(start.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(), end.toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
+ double totalUsage = billingUsage.getTotalUsage().doubleValue() / 100;
+ System.out.println(totalUsage);
+ Subscription subscription = subscription();
+ KeyInfo keyInfo = new KeyInfo();
+ String start_key = key.substring(0, 6);
+ String end_key = key.substring(key.length() - 6);
+ String mid_key = key.substring(6, key.length() - 6);
+ mid_key = mid_key.replaceAll(".", "*");
+
+ keyInfo.setKeyValue(start_key + mid_key + end_key);
+ keyInfo.setTotalAmount(subscription.getHardLimitUsd());
+ keyInfo.setRemaining(subscription.getHardLimitUsd() - totalUsage);
+ keyInfo.setTotalUsage(totalUsage);
+ keyInfo.setLimitDate(new Date(subscription.getAccessUntil() * 1000).toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
+ keyInfo.setPlanTitle(subscription.getPlan() != null ? subscription.getPlan().getTitle() : "null");
+ keyInfo.setIsHasPaymentMethod(subscription.isHasPaymentMethod());
+ keyInfo.setModel(getModelName());
+ return keyInfo;
+ }
+
+ /**
+ * 获取可用模型
+ *
+ * @param
+ * @return String
+ * @Date 2023/7/6
+ **/
+ public String getModelName() {
+ Single models = this.openAiApi.models();
+ List modelList = models.blockingGet().getData();
+ for (Model model : modelList) {
+ if (Objects.equals(model.getId(), "gpt-4")) {
+ return "GPT-4.0";
+ }
+ }
+ return "GPT-3.5";
+ }
+
+ /**
+ * 账户调用接口消耗金额信息查询
+ * 最多查询100天
+ *
+ * @param starDate 开始时间
+ * @param endDate 结束时间
+ * @return 消耗金额信息
+ */
+ public BillingUsage billingUsage(@NotNull LocalDate starDate, @NotNull LocalDate endDate) {
+ Single billingUsage = this.openAiApi.billingUsage(starDate, endDate);
+ return billingUsage.blockingGet();
+ }
+
+ /**
+ * 文本转换向量
+ *
+ * @param embedding 入参
+ * @return EmbeddingResponse
+ */
+ public EmbeddingResponse embeddings(Embedding embedding) {
+ Single embeddings = this.openAiApi.embeddings(embedding);
+ return embeddings.blockingGet();
+ }
+
+ /**
+ * 账户信息查询:里面包含总金额等信息
+ *
+ * @return 账户信息
+ */
+ public Subscription subscription() {
+ Single subscription = this.openAiApi.subscription();
+ return subscription.blockingGet();
+ }
+
+ /**
+ * 语音转文字
+ *
+ * @param transcriptions 参数
+ * @param file 语音文件 最大支持25MB mp3, mp4, mpeg, mpga, m4a, wav, webm
+ * @return 语音文本
+ */
+ public WhisperResponse speechToTextTranscriptions(java.io.File file, Transcriptions transcriptions) {
+ //文件
+ RequestBody fileBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
+ MultipartBody.Part multipartBody = MultipartBody.Part.createFormData("file", file.getName(), fileBody);
+ //自定义参数
+ Map requestBodyMap = new HashMap<>(10);
+ if (StrUtil.isNotBlank(transcriptions.getLanguage())) {
+ requestBodyMap.put(Transcriptions.Fields.language, RequestBody.create(MediaType.parse("multipart/form-data"), transcriptions.getLanguage()));
+ }
+ if (StrUtil.isNotBlank(transcriptions.getModel())) {
+ requestBodyMap.put(Transcriptions.Fields.model, RequestBody.create(MediaType.parse("multipart/form-data"), transcriptions.getModel()));
+ }
+ if (StrUtil.isNotBlank(transcriptions.getPrompt())) {
+ requestBodyMap.put(Transcriptions.Fields.prompt, RequestBody.create(MediaType.parse("multipart/form-data"), transcriptions.getPrompt()));
+ }
+ if (StrUtil.isNotBlank(transcriptions.getResponseFormat())) {
+ requestBodyMap.put(Transcriptions.Fields.responseFormat, RequestBody.create(MediaType.parse("multipart/form-data"), transcriptions.getResponseFormat()));
+ }
+ if (Objects.nonNull(transcriptions.getTemperature())) {
+ requestBodyMap.put(Transcriptions.Fields.temperature, RequestBody.create(MediaType.parse("multipart/form-data"), String.valueOf(transcriptions.getTemperature())));
+ }
+ Single whisperResponse = this.openAiApi.speechToTextTranscriptions(multipartBody, requestBodyMap);
+ return whisperResponse.blockingGet();
+ }
+
+ /**
+ * 简易版 语音转文字
+ *
+ * @param file 语音文件 最大支持25MB mp3, mp4, mpeg, mpga, m4a, wav, webm
+ * @return 语音文本
+ */
+ public WhisperResponse speechToTextTranscriptions(java.io.File file) {
+ Transcriptions transcriptions = Transcriptions.builder().build();
+ return this.speechToTextTranscriptions(file, transcriptions);
+ }
+ /**
+ * 文本转语音(异步)
+ *
+ * @param textToSpeech 参数
+ * @param callback 返回值接收
+ * @since 1.1.2
+ */
+ public void textToSpeech(TextToSpeech textToSpeech, retrofit2.Callback callback) {
+ Call responseBody = this.openAiApi.textToSpeech(textToSpeech);
+ responseBody.enqueue(callback);
+ }
+
+ /**
+ * 文本转语音(同步)
+ *
+ * @param textToSpeech 参数
+ * @since 1.1.3
+ */
+ public ResponseBody textToSpeech(TextToSpeech textToSpeech){
+ Call responseBody = this.openAiApi.textToSpeech(textToSpeech);
+ try {
+ return responseBody.execute().body();
+ } catch (IOException e) {
+ throw new BaseException("文本转语音(同步)失败: "+e.getMessage());
+ }
+ }
+
+ /**
+ * 文本转语音(克隆)
+ *
+ * @param textToSpeech
+ * @return
+ */
+ public ResponseBody textToSpeechClone(TextToSpeech textToSpeech) {
+ String baseUrl = "http://localhost:8081";
+ String spk = "三月七";
+ String text = textToSpeech.getInput();
+ String lang = "zh";
+
+ // 创建OkHttpClient实例
+ OkHttpClient client = new OkHttpClient();
+
+ // 构建请求URL
+ HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl).newBuilder();
+ urlBuilder.addQueryParameter("spk", spk);
+ urlBuilder.addQueryParameter("text", text);
+ urlBuilder.addQueryParameter("lang", lang);
+ String url = urlBuilder.build().toString();
+
+ // 创建请求对象
+ Request request = new Request.Builder()
+ .url(url)
+ .build();
+ // 发送请求并处理响应
+ try {
+ return client.newCall(request).execute().body();
+ } catch (IOException e) {
+ throw new BaseException("语音克隆失败!{}",e.getMessage());
+ }
+ }
+
+ /**
+ * 插件问答简易版
+ * 默认取messages最后一个元素构建插件对话
+ * 默认模型:ChatCompletion.Model.GPT_3_5_TURBO_16K_0613
+ *
+ * @param chatCompletion 参数
+ * @param plugin 插件
+ * @param 插件自定义函数的请求值
+ * @param 插件自定义函数的返回值
+ * @return ChatCompletionResponse
+ */
+ public ChatCompletionResponse chatCompletionWithPlugin(ChatCompletion chatCompletion, PluginAbstract plugin) {
+ if (Objects.isNull(plugin)) {
+ return this.chatCompletion(chatCompletion);
+ }
+ if (CollectionUtil.isEmpty(chatCompletion.getMessages())) {
+ throw new BaseException(CommonError.MESSAGE_NOT_NUL.msg());
+ }
+ List