generations, ChatResponseMetadata chatResponseMetadata) {
+ this.chatResponseMetadata = chatResponseMetadata;
+ this.generations = List.copyOf(generations);
+ }
+
+ /**
+ * The {@link List} of {@link Generation generated outputs}.
+ *
+ * It is a {@link List} of {@link List lists} because the Prompt could request
+ * multiple output {@link Generation generations}.
+ * @return the {@link List} of {@link Generation generated outputs}.
+ */
+
+ @Override
+ public List getResults() {
+ return this.generations;
+ }
+
+ /**
+ * @return Returns the first {@link Generation} in the generations list.
+ */
+ public Generation getResult() {
+ if (CollectionUtils.isEmpty(this.generations)) {
+ return null;
+ }
+ return this.generations.get(0);
+ }
+
+ /**
+ * @return Returns {@link ChatResponseMetadata} containing information about the use
+ * of the AI provider's API.
+ */
+ @Override
+ public ChatResponseMetadata getMetadata() {
+ return this.chatResponseMetadata;
+ }
+
+ @Override
+ public String toString() {
+ return "ChatResponse [metadata=" + chatResponseMetadata + ", generations=" + generations + "]";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof ChatResponse that))
+ return false;
+ return Objects.equals(chatResponseMetadata, that.chatResponseMetadata)
+ && Objects.equals(generations, that.generations);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(chatResponseMetadata, generations);
+ }
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/Generation.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/Generation.java
new file mode 100644
index 000000000..e84ffd409
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/Generation.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat;
+
+import cn.iocoder.yudao.framework.ai.chat.messages.AssistantMessage;
+import cn.iocoder.yudao.framework.ai.chat.metadata.ChatGenerationMetadata;
+import cn.iocoder.yudao.framework.ai.model.ModelResult;
+import org.springframework.lang.Nullable;
+
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * 表示AI返回的响应。
+ *
+ * Represents a response returned by the AI.
+ */
+public class Generation implements ModelResult {
+
+ private AssistantMessage assistantMessage;
+
+ private ChatGenerationMetadata chatGenerationMetadata;
+
+ public Generation(String text) {
+ this.assistantMessage = new AssistantMessage(text);
+ }
+
+ public Generation(String text, Map properties) {
+ this.assistantMessage = new AssistantMessage(text, properties);
+ }
+
+ @Override
+ public AssistantMessage getOutput() {
+ return this.assistantMessage;
+ }
+
+ @Override
+ public ChatGenerationMetadata getMetadata() {
+ ChatGenerationMetadata chatGenerationMetadata = this.chatGenerationMetadata;
+ return chatGenerationMetadata != null ? chatGenerationMetadata : ChatGenerationMetadata.NULL;
+ }
+
+ public Generation withGenerationMetadata(@Nullable ChatGenerationMetadata chatGenerationMetadata) {
+ this.chatGenerationMetadata = chatGenerationMetadata;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof Generation that))
+ return false;
+ return Objects.equals(assistantMessage, that.assistantMessage)
+ && Objects.equals(chatGenerationMetadata, that.chatGenerationMetadata);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(assistantMessage, chatGenerationMetadata);
+ }
+
+ @Override
+ public String toString() {
+ return "Generation{" + "assistantMessage=" + assistantMessage + ", chatGenerationMetadata="
+ + chatGenerationMetadata + '}';
+ }
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/StreamingChatClient.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/StreamingChatClient.java
new file mode 100644
index 000000000..fdd604f4d
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/StreamingChatClient.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat;
+
+import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt;
+import cn.iocoder.yudao.framework.ai.model.StreamingModelClient;
+import reactor.core.publisher.Flux;
+
+@FunctionalInterface
+public interface StreamingChatClient extends StreamingModelClient {
+
+ @Override
+ Flux stream(Prompt prompt);
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/AbstractMessage.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/AbstractMessage.java
new file mode 100644
index 000000000..d622cf9f8
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/AbstractMessage.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.messages;
+
+import org.springframework.core.io.Resource;
+import org.springframework.util.Assert;
+import org.springframework.util.StreamUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public abstract class AbstractMessage implements Message {
+
+ protected final MessageType messageType;
+
+ protected final String textContent;
+
+ protected final List mediaData;
+
+ /**
+ * Additional options for the message to influence the response, not a generative map.
+ */
+ protected final Map properties;
+
+ protected AbstractMessage(MessageType messageType, String content) {
+ this(messageType, content, Map.of());
+ }
+
+ protected AbstractMessage(MessageType messageType, String content, Map messageProperties) {
+ Assert.notNull(messageType, "Message type must not be null");
+ // Assert.notNull(content, "Content must not be null");
+ this.messageType = messageType;
+ this.textContent = content;
+ this.mediaData = new ArrayList<>();
+ this.properties = messageProperties;
+ }
+
+ protected AbstractMessage(MessageType messageType, String textContent, List mediaData) {
+ this(messageType, textContent, mediaData, Map.of());
+ }
+
+ protected AbstractMessage(MessageType messageType, String textContent, List mediaData,
+ Map messageProperties) {
+
+ Assert.notNull(messageType, "Message type must not be null");
+ Assert.notNull(textContent, "Content must not be null");
+ Assert.notNull(mediaData, "media data must not be null");
+
+ this.messageType = messageType;
+ this.textContent = textContent;
+ this.mediaData = new ArrayList<>(mediaData);
+ this.properties = messageProperties;
+ }
+
+ protected AbstractMessage(MessageType messageType, Resource resource) {
+ this(messageType, resource, Collections.emptyMap());
+ }
+
+ @SuppressWarnings("null")
+ protected AbstractMessage(MessageType messageType, Resource resource, Map messageProperties) {
+ Assert.notNull(messageType, "Message type must not be null");
+ Assert.notNull(resource, "Resource must not be null");
+
+ this.messageType = messageType;
+ this.properties = messageProperties;
+ this.mediaData = new ArrayList<>();
+
+ try (InputStream inputStream = resource.getInputStream()) {
+ this.textContent = StreamUtils.copyToString(inputStream, Charset.defaultCharset());
+ }
+ catch (IOException ex) {
+ throw new RuntimeException("Failed to read resource", ex);
+ }
+ }
+
+ @Override
+ public String getContent() {
+ return this.textContent;
+ }
+
+ @Override
+ public List getMediaData() {
+ return this.mediaData;
+ }
+
+ @Override
+ public Map getProperties() {
+ return this.properties;
+ }
+
+ @Override
+ public MessageType getMessageType() {
+ return this.messageType;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((mediaData == null) ? 0 : mediaData.hashCode());
+ result = prime * result + ((properties == null) ? 0 : properties.hashCode());
+ result = prime * result + ((messageType == null) ? 0 : messageType.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ AbstractMessage other = (AbstractMessage) obj;
+ if (mediaData == null) {
+ if (other.mediaData != null)
+ return false;
+ }
+ else if (!mediaData.equals(other.mediaData))
+ return false;
+ if (properties == null) {
+ if (other.properties != null)
+ return false;
+ }
+ else if (!properties.equals(other.properties))
+ return false;
+ if (messageType != other.messageType)
+ return false;
+ return true;
+ }
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/AssistantMessage.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/AssistantMessage.java
new file mode 100644
index 000000000..6a18eeba8
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/AssistantMessage.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.messages;
+
+import java.util.Map;
+
+/**
+ * 让生成人员知道内容是作为对用户的响应生成的。
+ * 此角色指示生成者先前在会话中生成的消息。
+ * 通过包括该系列中的辅助消息,您可以为生成的关于提供上下文之前在谈话中的交流。
+ *
+ * Lets the generative know the content was generated as a response to the user. This role
+ * indicates messages that the generative has previously generated in the conversation. By
+ * including assistant messages in the series, you provide context to the generative about
+ * prior exchanges in the conversation.
+ */
+public class AssistantMessage extends AbstractMessage {
+
+ public AssistantMessage(String content) {
+ super(MessageType.ASSISTANT, content);
+ }
+
+ public AssistantMessage(String content, Map properties) {
+ super(MessageType.ASSISTANT, content, properties);
+ }
+
+ @Override
+ public String toString() {
+ return "AssistantMessage{" + "content='" + getContent() + '\'' + ", properties=" + properties + ", messageType="
+ + messageType + '}';
+ }
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/ChatMessage.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/ChatMessage.java
new file mode 100644
index 000000000..16470135b
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/ChatMessage.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.messages;
+
+import java.util.Map;
+
+public class ChatMessage extends AbstractMessage {
+
+ public ChatMessage(String role, String content) {
+ super(MessageType.valueOf(role), content);
+ }
+
+ public ChatMessage(String role, String content, Map properties) {
+ super(MessageType.valueOf(role), content, properties);
+ }
+
+ public ChatMessage(MessageType messageType, String content) {
+ super(messageType, content);
+ }
+
+ public ChatMessage(MessageType messageType, String content, Map properties) {
+ super(messageType, content, properties);
+ }
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/FunctionMessage.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/FunctionMessage.java
new file mode 100644
index 000000000..d8b6fccbd
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/FunctionMessage.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023-2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.messages;
+
+import java.util.Map;
+
+public class FunctionMessage extends AbstractMessage {
+
+ public FunctionMessage(String content) {
+ super(MessageType.SYSTEM, content);
+ }
+
+ public FunctionMessage(String content, Map properties) {
+ super(MessageType.SYSTEM, content, properties);
+ }
+
+ @Override
+ public String toString() {
+ return "FunctionMessage{" + "content='" + getContent() + '\'' + ", properties=" + properties + ", messageType="
+ + messageType + '}';
+ }
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/MediaData.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/MediaData.java
new file mode 100644
index 000000000..fed60221f
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/MediaData.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2024-2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.messages;
+
+import org.springframework.util.Assert;
+import org.springframework.util.MimeType;
+
+/**
+ * @author Christian Tzolov
+ */
+public class MediaData {
+
+ private final MimeType mimeType;
+
+ private final Object data;
+
+ public MediaData(MimeType mimeType, Object data) {
+ Assert.notNull(mimeType, "MimeType must not be null");
+ // Assert.notNull(data, "Data must not be null");
+ this.mimeType = mimeType;
+ this.data = data;
+ }
+
+ public MimeType getMimeType() {
+ return this.mimeType;
+ }
+
+ public Object getData() {
+ return this.data;
+ }
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/Message.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/Message.java
new file mode 100644
index 000000000..89c7b4fb4
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/Message.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.messages;
+
+import java.util.List;
+import java.util.Map;
+
+public interface Message {
+
+ String getContent();
+
+ List getMediaData();
+
+ Map getProperties();
+
+ MessageType getMessageType();
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/MessageType.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/MessageType.java
new file mode 100644
index 000000000..2c803a060
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/MessageType.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.iocoder.yudao.framework.ai.chat.messages;
+
+public enum MessageType {
+
+ // 用户消息
+ USER("user"),
+
+ // 之前会话的消息
+ ASSISTANT("assistant"),
+
+ // 根据注释说明:您可以使用系统消息来指示具有生成性,表现得像某个角色或以特定的方式提供答案总体安排
+ // 简单理解:在对话前,发送一条具有角色的信息让模型理解(如:你现在是一个10年拍摄经验的导演,拥有丰富的经验。 这样你就可以去问他,怎么拍一个短视频可以在抖音上火)
+ SYSTEM("system"),
+
+ // 函数?根据引用现在不支持,会抛出一个异常 ---> throw new IllegalArgumentException("Tool execution results are not supported for Bedrock models");
+ FUNCTION("function");
+
+ private final String value;
+
+ MessageType(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public static MessageType fromValue(String value) {
+ for (MessageType messageType : MessageType.values()) {
+ if (messageType.getValue().equals(value)) {
+ return messageType;
+ }
+ }
+ throw new IllegalArgumentException("Invalid MessageType value: " + value);
+ }
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/SystemMessage.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/SystemMessage.java
new file mode 100644
index 000000000..c474f24bd
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/SystemMessage.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.messages;
+
+import org.springframework.core.io.Resource;
+
+/**
+ * 作为输入传递的“system”类型的消息。系统消息给出高级别对话说明。
+ * 此角色通常提供高级说明对话。
+ * 例如,您可以使用系统消息来指示具有生成性,表现得像某个角色或以特定的方式提供答案总体安排
+ *
+ * A message of the type 'system' passed as input. The system message gives high level
+ * instructions for the conversation. This role typically provides high-level instructions
+ * for the conversation. For example, you might use a system message to instruct the
+ * generative to behave like a certain character or to provide answers in a specific
+ * format.
+ */
+public class SystemMessage extends AbstractMessage {
+
+ public SystemMessage(String content) {
+ super(MessageType.SYSTEM, content);
+ }
+
+ public SystemMessage(Resource resource) {
+ super(MessageType.SYSTEM, resource);
+ }
+
+ @Override
+ public String toString() {
+ return "SystemMessage{" + "content='" + getContent() + '\'' + ", properties=" + properties + ", messageType="
+ + messageType + '}';
+ }
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/UserMessage.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/UserMessage.java
new file mode 100644
index 000000000..2b8f547f7
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/messages/UserMessage.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.messages;
+
+import org.springframework.core.io.Resource;
+
+import java.util.List;
+
+/**
+ * 作为输入传递的“user”类型的消息具有用户角色的消息来自最终用户或开发者。
+ * 它们表示问题、提示或您想要的任何输入产生反应的。
+ *
+ * A message of the type 'user' passed as input Messages with the user role are from the
+ * end-user or developer. They represent questions, prompts, or any input that you want
+ * the generative to respond to.
+ */
+public class UserMessage extends AbstractMessage {
+
+ public UserMessage(String message) {
+ super(MessageType.USER, message);
+ }
+
+ public UserMessage(Resource resource) {
+ super(MessageType.USER, resource);
+ }
+
+ public UserMessage(String textContent, List mediaDataList) {
+ super(MessageType.USER, textContent, mediaDataList);
+ }
+
+ @Override
+ public String toString() {
+ return "UserMessage{" + "content='" + getContent() + '\'' + ", properties=" + properties + ", messageType="
+ + messageType + '}';
+ }
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/metadata/ChatGenerationMetadata.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/metadata/ChatGenerationMetadata.java
new file mode 100644
index 000000000..a90dda5bf
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/metadata/ChatGenerationMetadata.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.metadata;
+
+import cn.iocoder.yudao.framework.ai.model.ResultMetadata;
+import org.springframework.lang.Nullable;
+
+/**
+ * Abstract Data Type (ADT) encapsulating information on the completion choices in the AI
+ * response.
+ *
+ * @author John Blum
+ * @since 0.7.0
+ */
+public interface ChatGenerationMetadata extends ResultMetadata {
+
+ ChatGenerationMetadata NULL = ChatGenerationMetadata.from(null, null);
+
+ /**
+ * Factory method used to construct a new {@link ChatGenerationMetadata} from the
+ * given {@link String finish reason} and content filter metadata.
+ * @param finishReason {@link String} contain the reason for the choice completion.
+ * @param contentFilterMetadata underlying AI provider metadata for filtering applied
+ * to generation content.
+ * @return a new {@link ChatGenerationMetadata} from the given {@link String finish
+ * reason} and content filter metadata.
+ */
+ static ChatGenerationMetadata from(String finishReason, Object contentFilterMetadata) {
+ return new ChatGenerationMetadata() {
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public T getContentFilterMetadata() {
+ return (T) contentFilterMetadata;
+ }
+
+ @Override
+ public String getFinishReason() {
+ return finishReason;
+ }
+ };
+ }
+
+ /**
+ * Returns the underlying AI provider metadata for filtering applied to generation
+ * content.
+ * @param {@link Class Type} used to cast the filtered content metadata into the
+ * AI provider-specific type.
+ * @return the underlying AI provider metadata for filtering applied to generation
+ * content.
+ */
+ @Nullable
+ T getContentFilterMetadata();
+
+ /**
+ * Get the {@link String reason} this choice completed for the generation.
+ * @return the {@link String reason} this choice completed for the generation.
+ */
+ String getFinishReason();
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/metadata/ChatResponseMetadata.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/metadata/ChatResponseMetadata.java
new file mode 100644
index 000000000..ebcb4ab06
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/metadata/ChatResponseMetadata.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.metadata;
+
+
+import cn.iocoder.yudao.framework.ai.model.ResponseMetadata;
+
+/**
+ * Abstract Data Type (ADT) modeling common AI provider metadata returned in an AI
+ * response.
+ *
+ * 抽象数据类型(ADT)建模AI响应中返回的常见AI提供者元数据。
+ *
+ * @author John Blum
+ * @since 0.7.0
+ */
+public interface ChatResponseMetadata extends ResponseMetadata {
+
+ ChatResponseMetadata NULL = new ChatResponseMetadata() {
+ };
+
+ /**
+ * Returns AI provider specific metadata on rate limits.
+ * @return AI provider specific metadata on rate limits.
+ * @see RateLimit
+ */
+ default RateLimit getRateLimit() {
+ return new EmptyRateLimit();
+ }
+
+ /**
+ * Returns AI provider specific metadata on API usage.
+ * @return AI provider specific metadata on API usage.
+ * @see Usage
+ */
+ default Usage getUsage() {
+ return new EmptyUsage();
+ }
+
+ default PromptMetadata getPromptMetadata() {
+ return PromptMetadata.empty();
+ }
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/metadata/EmptyRateLimit.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/metadata/EmptyRateLimit.java
new file mode 100644
index 000000000..62aeeb531
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/metadata/EmptyRateLimit.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.metadata;
+
+import java.time.Duration;
+
+/**
+ * A RateLimit implementation that returns zero for all property getters
+ *
+ * @author John Blum
+ * @since 0.7.0
+ */
+public class EmptyRateLimit implements RateLimit {
+
+ @Override
+ public Long getRequestsLimit() {
+ return 0L;
+ }
+
+ @Override
+ public Long getRequestsRemaining() {
+ return 0L;
+ }
+
+ @Override
+ public Duration getRequestsReset() {
+ return Duration.ZERO;
+ }
+
+ @Override
+ public Long getTokensLimit() {
+ return 0L;
+ }
+
+ @Override
+ public Long getTokensRemaining() {
+ return 0L;
+ }
+
+ @Override
+ public Duration getTokensReset() {
+ return Duration.ZERO;
+ }
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/metadata/EmptyUsage.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/metadata/EmptyUsage.java
new file mode 100644
index 000000000..11f0255c1
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/metadata/EmptyUsage.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.metadata;
+
+/**
+ * A EmpytUsage implementation that returns zero for all property getters
+ *
+ * @author John Blum
+ * @since 0.7.0
+ */
+public class EmptyUsage implements Usage {
+
+ @Override
+ public Long getPromptTokens() {
+ return 0L;
+ }
+
+ @Override
+ public Long getGenerationTokens() {
+ return 0L;
+ }
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/metadata/PromptMetadata.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/metadata/PromptMetadata.java
new file mode 100644
index 000000000..94bad3aa5
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/metadata/PromptMetadata.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.iocoder.yudao.framework.ai.chat.metadata;
+
+import org.springframework.util.Assert;
+
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.stream.StreamSupport;
+
+/**
+ * Abstract Data Type (ADT) modeling metadata gathered by the AI during request
+ * processing.
+ *
+ * @author John Blum
+ * @since 0.7.0
+ */
+@FunctionalInterface
+public interface PromptMetadata extends Iterable {
+
+ /**
+ * Factory method used to create empty {@link PromptMetadata} when the information is
+ * not supplied by the AI provider.
+ * @return empty {@link PromptMetadata}.
+ */
+ static PromptMetadata empty() {
+ return of();
+ }
+
+ /**
+ * Factory method used to create a new {@link PromptMetadata} composed of an array of
+ * {@link PromptFilterMetadata}.
+ * @param array array of {@link PromptFilterMetadata} used to compose the
+ * {@link PromptMetadata}.
+ * @return a new {@link PromptMetadata} composed of an array of
+ * {@link PromptFilterMetadata}.
+ */
+ static PromptMetadata of(PromptFilterMetadata... array) {
+ return of(Arrays.asList(array));
+ }
+
+ /**
+ * Factory method used to create a new {@link PromptMetadata} composed of an
+ * {@link Iterable} of {@link PromptFilterMetadata}.
+ * @param iterable {@link Iterable} of {@link PromptFilterMetadata} used to compose
+ * the {@link PromptMetadata}.
+ * @return a new {@link PromptMetadata} composed of an {@link Iterable} of
+ * {@link PromptFilterMetadata}.
+ */
+ static PromptMetadata of(Iterable iterable) {
+ Assert.notNull(iterable, "An Iterable of PromptFilterMetadata must not be null");
+ return iterable::iterator;
+ }
+
+ /**
+ * Returns an {@link Optional} {@link PromptFilterMetadata} at the given index.
+ * @param promptIndex index of the {@link PromptFilterMetadata} contained in this
+ * {@link PromptMetadata}.
+ * @return {@link Optional} {@link PromptFilterMetadata} at the given index.
+ * @throws IllegalArgumentException if the prompt index is less than 0.
+ */
+ default Optional findByPromptIndex(int promptIndex) {
+
+ Assert.isTrue(promptIndex > -1, "Prompt index [%d] must be greater than equal to 0".formatted(promptIndex));
+
+ return StreamSupport.stream(this.spliterator(), false)
+ .filter(promptFilterMetadata -> promptFilterMetadata.getPromptIndex() == promptIndex)
+ .findFirst();
+ }
+
+ /**
+ * Abstract Data Type (ADT) modeling filter metadata for all prompts sent during an AI
+ * request.
+ */
+ interface PromptFilterMetadata {
+
+ /**
+ * Factory method used to construct a new {@link PromptFilterMetadata} with the
+ * given prompt index and content filter metadata.
+ * @param promptIndex index of the prompt filter metadata contained in the AI
+ * response.
+ * @param contentFilterMetadata underlying AI provider metadata for filtering
+ * applied to prompt content.
+ * @return a new instance of {@link PromptFilterMetadata} with the given prompt
+ * index and content filter metadata.
+ */
+ static PromptFilterMetadata from(int promptIndex, Object contentFilterMetadata) {
+
+ return new PromptFilterMetadata() {
+
+ @Override
+ public int getPromptIndex() {
+ return promptIndex;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public T getContentFilterMetadata() {
+ return (T) contentFilterMetadata;
+ }
+ };
+ }
+
+ /**
+ * Index of the prompt filter metadata contained in the AI response.
+ * @return an {@link Integer index} fo the prompt filter metadata contained in the
+ * AI response.
+ */
+ int getPromptIndex();
+
+ /**
+ * Returns the underlying AI provider metadata for filtering applied to prompt
+ * content.
+ * @param {@link Class Type} used to cast the filtered content metadata into
+ * the AI provider-specific type.
+ * @return the underlying AI provider metadata for filtering applied to prompt
+ * content.
+ */
+ T getContentFilterMetadata();
+
+ }
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/metadata/RateLimit.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/metadata/RateLimit.java
new file mode 100644
index 000000000..6842783ba
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/metadata/RateLimit.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.metadata;
+
+import java.time.Duration;
+
+/**
+ * Abstract Data Type (ADT) encapsulating metadata from an AI provider's API rate limits
+ * granted to the API key in use and the API key's current balance.
+ *
+ * @author John Blum
+ * @since 0.7.0
+ */
+public interface RateLimit {
+
+ /**
+ * Returns the maximum number of requests that are permitted before exhausting the
+ * rate limit.
+ * @return an {@link Long} with the maximum number of requests that are permitted
+ * before exhausting the rate limit.
+ * @see #getRequestsRemaining()
+ */
+ Long getRequestsLimit();
+
+ /**
+ * Returns the remaining number of requests that are permitted before exhausting the
+ * {@link #getRequestsLimit() rate limit}.
+ * @return an {@link Long} with the remaining number of requests that are permitted
+ * before exhausting the {@link #getRequestsLimit() rate limit}.
+ * @see #getRequestsLimit()
+ */
+ Long getRequestsRemaining();
+
+ /**
+ * Returns the {@link Duration time} until the rate limit (based on requests) resets
+ * to its {@link #getRequestsLimit() initial state}.
+ * @return a {@link Duration} representing the time until the rate limit (based on
+ * requests) resets to its {@link #getRequestsLimit() initial state}.
+ * @see #getRequestsLimit()
+ */
+ Duration getRequestsReset();
+
+ /**
+ * Returns the maximum number of tokens that are permitted before exhausting the rate
+ * limit.
+ * @return an {@link Long} with the maximum number of tokens that are permitted before
+ * exhausting the rate limit.
+ * @see #getTokensRemaining()
+ */
+ Long getTokensLimit();
+
+ /**
+ * Returns the remaining number of tokens that are permitted before exhausting the
+ * {@link #getTokensLimit() rate limit}.
+ * @return an {@link Long} with the remaining number of tokens that are permitted
+ * before exhausting the {@link #getTokensLimit() rate limit}.
+ * @see #getTokensLimit()
+ */
+ Long getTokensRemaining();
+
+ /**
+ * Returns the {@link Duration time} until the rate limit (based on tokens) resets to
+ * its {@link #getTokensLimit() initial state}.
+ * @return a {@link Duration} with the time until the rate limit (based on tokens)
+ * resets to its {@link #getTokensLimit() initial state}.
+ * @see #getTokensLimit()
+ */
+ Duration getTokensReset();
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/metadata/Usage.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/metadata/Usage.java
new file mode 100644
index 000000000..cecbc828e
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/metadata/Usage.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.metadata;
+
+/**
+ * 抽象数据类型(ADT)封装关于人工智能提供商API使用的元数据根据AI请求。
+ *
+ * Abstract Data Type (ADT) encapsulating metadata on the usage of an AI provider's API
+ * per AI request.
+ *
+ * @author John Blum
+ * @since 0.7.0
+ */
+public interface Usage {
+
+ /**
+ * 返回AI请求的{@literal prompt}中使用的令牌数。
+ * @返回一个{@link Long},其中包含在的{@literal提示符}中使用的令牌数AI请求。
+ *
+ * Returns the number of tokens used in the {@literal prompt} of the AI request.
+ * @return an {@link Long} with the number of tokens used in the {@literal prompt} of
+ * the AI request.
+ * @see #getGenerationTokens()
+ */
+ Long getPromptTokens();
+
+ /**
+ * Returns the number of tokens returned in the {@literal generation (aka completion)}
+ * of the AI's response.
+ * @return an {@link Long} with the number of tokens returned in the
+ * {@literal generation (aka completion)} of the AI's response.
+ * @see #getPromptTokens()
+ */
+ Long getGenerationTokens();
+
+ /**
+ * Return the total number of tokens from both the {@literal prompt} of an AI request
+ * and {@literal generation} of the AI's response.
+ * @return the total number of tokens from both the {@literal prompt} of an AI request
+ * and {@literal generation} of the AI's response.
+ * @see #getPromptTokens()
+ * @see #getGenerationTokens()
+ */
+ default Long getTotalTokens() {
+ Long promptTokens = getPromptTokens();
+ promptTokens = promptTokens != null ? promptTokens : 0;
+ Long completionTokens = getGenerationTokens();
+ completionTokens = completionTokens != null ? completionTokens : 0;
+ return promptTokens + completionTokens;
+ }
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/package-info.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/package-info.java
new file mode 100644
index 000000000..17c341f00
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/package-info.java
@@ -0,0 +1,14 @@
+/**
+ * The org.sf.ai.chat package represents the bounded context for the Chat Model within the
+ * AI generative model domain. This package extends the core domain defined in
+ * org.sf.ai.generative, providing implementations specific to chat-based generative AI
+ * interactions.
+ *
+ * In line with Domain-Driven Design principles, this package includes implementations of
+ * entities and value objects specific to the chat context, such as ChatPrompt and
+ * ChatResponse, adhering to the ubiquitous language of chat interactions in AI models.
+ *
+ * This bounded context is designed to encapsulate all aspects of chat-based AI
+ * functionalities, maintaining a clear boundary from other contexts within the AI domain.
+ */
+package cn.iocoder.yudao.framework.ai.chat;
\ No newline at end of file
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/AssistantPromptTemplate.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/AssistantPromptTemplate.java
new file mode 100644
index 000000000..cfe91527e
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/AssistantPromptTemplate.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.prompt;
+
+import cn.iocoder.yudao.framework.ai.chat.messages.AssistantMessage;
+import cn.iocoder.yudao.framework.ai.chat.messages.Message;
+import org.springframework.core.io.Resource;
+
+import java.util.Map;
+
+public class AssistantPromptTemplate extends PromptTemplate {
+
+ public AssistantPromptTemplate(String template) {
+ super(template);
+ }
+
+ public AssistantPromptTemplate(Resource resource) {
+ super(resource);
+ }
+
+ @Override
+ public Prompt create() {
+ return new Prompt(new AssistantMessage(render()));
+ }
+
+ @Override
+ public Prompt create(Map model) {
+ return new Prompt(new AssistantMessage(render(model)));
+ }
+
+ @Override
+ public Message createMessage() {
+ return new AssistantMessage(render());
+ }
+
+ @Override
+ public Message createMessage(Map model) {
+ return new AssistantMessage(render(model));
+ }
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/ChatOptions.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/ChatOptions.java
new file mode 100644
index 000000000..979ca6b20
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/ChatOptions.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2024-2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.prompt;
+
+import cn.iocoder.yudao.framework.ai.model.ModelOptions;
+
+/**
+ * 聊天选项代表了常见的选项,可在不同的聊天模式中移植。
+ *
+ * The ChatOptions represent the common options, portable across different chat models.
+ */
+public interface ChatOptions extends ModelOptions {
+
+ Float getTemperature();
+
+ void setTemperature(Float temperature);
+
+ Float getTopP();
+
+ void setTopP(Float topP);
+
+ Integer getTopK();
+
+ void setTopK(Integer topK);
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/ChatOptionsBuilder.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/ChatOptionsBuilder.java
new file mode 100644
index 000000000..c3e236ea0
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/ChatOptionsBuilder.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2024-2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.prompt;
+
+public class ChatOptionsBuilder {
+
+ private class ChatOptionsImpl implements ChatOptions {
+
+ private Float temperature;
+
+ private Float topP;
+
+ private Integer topK;
+
+ @Override
+ public Float getTemperature() {
+ return temperature;
+ }
+
+ @Override
+ public void setTemperature(Float temperature) {
+ this.temperature = temperature;
+ }
+
+ @Override
+ public Float getTopP() {
+ return topP;
+ }
+
+ @Override
+ public void setTopP(Float topP) {
+ this.topP = topP;
+ }
+
+ @Override
+ public Integer getTopK() {
+ return topK;
+ }
+
+ @Override
+ public void setTopK(Integer topK) {
+ this.topK = topK;
+ }
+
+ }
+
+ private final ChatOptionsImpl options = new ChatOptionsImpl();
+
+ private ChatOptionsBuilder() {
+ }
+
+ public static ChatOptionsBuilder builder() {
+ return new ChatOptionsBuilder();
+ }
+
+ public ChatOptionsBuilder withTemperature(Float temperature) {
+ options.setTemperature(temperature);
+ return this;
+ }
+
+ public ChatOptionsBuilder withTopP(Float topP) {
+ options.setTopP(topP);
+ return this;
+ }
+
+ public ChatOptionsBuilder withTopK(Integer topK) {
+ options.setTopK(topK);
+ return this;
+ }
+
+ public ChatOptions build() {
+ return options;
+ }
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/ChatPromptTemplate.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/ChatPromptTemplate.java
new file mode 100644
index 000000000..a06c981c7
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/ChatPromptTemplate.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.prompt;
+
+import cn.iocoder.yudao.framework.ai.chat.messages.Message;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * PromptTemplate,用于将角色指定为字符串实现及其角色不足以满足您的需求。
+ *
+ * A PromptTemplate that lets you specify the role as a string should the current
+ * implementations and their roles not suffice for your needs.
+ */
+public class ChatPromptTemplate implements PromptTemplateActions, PromptTemplateChatActions {
+
+ private final List promptTemplates;
+
+ public ChatPromptTemplate(List promptTemplates) {
+ this.promptTemplates = promptTemplates;
+ }
+
+ @Override
+ public String render() {
+ StringBuilder sb = new StringBuilder();
+ for (PromptTemplate promptTemplate : promptTemplates) {
+ sb.append(promptTemplate.render());
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public String render(Map model) {
+ StringBuilder sb = new StringBuilder();
+ for (PromptTemplate promptTemplate : promptTemplates) {
+ sb.append(promptTemplate.render(model));
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public List createMessages() {
+ List messages = new ArrayList<>();
+ for (PromptTemplate promptTemplate : promptTemplates) {
+ messages.add(promptTemplate.createMessage());
+ }
+ return messages;
+ }
+
+ @Override
+ public List createMessages(Map model) {
+ List messages = new ArrayList<>();
+ for (PromptTemplate promptTemplate : promptTemplates) {
+ messages.add(promptTemplate.createMessage(model));
+ }
+ return messages;
+ }
+
+ @Override
+ public Prompt create() {
+ List messages = createMessages();
+ return new Prompt(messages);
+ }
+
+ @Override
+ public Prompt create(Map model) {
+ List messages = createMessages(model);
+ return new Prompt(messages);
+ }
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/FunctionPromptTemplate.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/FunctionPromptTemplate.java
new file mode 100644
index 000000000..e94545c23
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/FunctionPromptTemplate.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.prompt;
+
+public class FunctionPromptTemplate extends PromptTemplate {
+
+ private String name;
+
+ public FunctionPromptTemplate(String template) {
+ super(template);
+ }
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/Prompt.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/Prompt.java
new file mode 100644
index 000000000..f9eb37409
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/Prompt.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.prompt;
+
+import cn.iocoder.yudao.framework.ai.chat.messages.Message;
+import cn.iocoder.yudao.framework.ai.chat.messages.UserMessage;
+import cn.iocoder.yudao.framework.ai.model.ModelOptions;
+import cn.iocoder.yudao.framework.ai.model.ModelRequest;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 文字内容
+ */
+public class Prompt implements ModelRequest> {
+
+ private final List messages;
+
+ private ChatOptions modelOptions;
+
+ public Prompt(String contents) {
+ this(new UserMessage(contents));
+ }
+
+ public Prompt(Message message) {
+ this(Collections.singletonList(message));
+ }
+
+ public Prompt(List messages) {
+ this.messages = messages;
+ }
+
+ public Prompt(String contents, ChatOptions modelOptions) {
+ this(new UserMessage(contents), modelOptions);
+ }
+
+ public Prompt(Message message, ChatOptions modelOptions) {
+ this(Collections.singletonList(message), modelOptions);
+ }
+
+ public Prompt(List messages, ChatOptions modelOptions) {
+ this.messages = messages;
+ this.modelOptions = modelOptions;
+ }
+
+ public String getContents() {
+ StringBuilder sb = new StringBuilder();
+ for (Message message : getInstructions()) {
+ sb.append(message.getContent());
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public ModelOptions getOptions() {
+ return this.modelOptions;
+ }
+
+ @Override
+ public List getInstructions() {
+ return this.messages;
+ }
+
+ @Override
+ public String toString() {
+ return "Prompt{" + "messages=" + this.messages + ", modelOptions=" + this.modelOptions + '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof Prompt prompt))
+ return false;
+ return Objects.equals(this.messages, prompt.messages) && Objects.equals(this.modelOptions, prompt.modelOptions);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.messages, this.modelOptions);
+ }
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/PromptTemplate.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/PromptTemplate.java
new file mode 100644
index 000000000..7c0c0f7f3
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/PromptTemplate.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.prompt;
+
+import cn.iocoder.yudao.framework.ai.chat.messages.Message;
+import cn.iocoder.yudao.framework.ai.chat.messages.UserMessage;
+import cn.iocoder.yudao.framework.ai.parser.OutputParser;
+import org.antlr.runtime.Token;
+import org.antlr.runtime.TokenStream;
+import org.springframework.core.io.Resource;
+import org.springframework.util.StreamUtils;
+import org.stringtemplate.v4.ST;
+import org.stringtemplate.v4.compiler.STLexer;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+/**
+ * 用户输入的提示内容模板
+ *
+ * 实现:提示词模板操作 提示词模板message相关操作
+ */
+public class PromptTemplate implements PromptTemplateActions, PromptTemplateMessageActions {
+
+ private ST st;
+
+ private Map dynamicModel = new HashMap<>();
+
+ protected String template;
+
+ protected TemplateFormat templateFormat = TemplateFormat.ST;
+
+ private OutputParser outputParser;
+
+ public PromptTemplate(Resource resource) {
+ try (InputStream inputStream = resource.getInputStream()) {
+ this.template = StreamUtils.copyToString(inputStream, Charset.defaultCharset());
+ }
+ catch (IOException ex) {
+ throw new RuntimeException("Failed to read resource", ex);
+ }
+ try {
+ this.st = new ST(this.template, '{', '}');
+ }
+ catch (Exception ex) {
+ throw new IllegalArgumentException("The template string is not valid.", ex);
+ }
+ }
+
+ public PromptTemplate(String template) {
+ this.template = template;
+ // If the template string is not valid, an exception will be thrown
+ try {
+ this.st = new ST(this.template, '{', '}');
+ }
+ catch (Exception ex) {
+ throw new IllegalArgumentException("The template string is not valid.", ex);
+ }
+ }
+
+ public PromptTemplate(String template, Map model) {
+ this.template = template;
+ // If the template string is not valid, an exception will be thrown
+ try {
+ this.st = new ST(this.template, '{', '}');
+ for (Entry entry : model.entrySet()) {
+ add(entry.getKey(), entry.getValue());
+ dynamicModel.put(entry.getKey(), entry.getValue());
+ }
+ }
+ catch (Exception ex) {
+ throw new IllegalArgumentException("The template string is not valid.", ex);
+ }
+ }
+
+ public PromptTemplate(Resource resource, Map model) {
+ try (InputStream inputStream = resource.getInputStream()) {
+ this.template = StreamUtils.copyToString(inputStream, Charset.defaultCharset());
+ }
+ catch (IOException ex) {
+ throw new RuntimeException("Failed to read resource", ex);
+ }
+ // If the template string is not valid, an exception will be thrown
+ try {
+ this.st = new ST(this.template, '{', '}');
+ for (Entry entry : model.entrySet()) {
+ add(entry.getKey(), entry.getValue());
+ dynamicModel.put(entry.getKey(), entry.getValue());
+ }
+ }
+ catch (Exception ex) {
+ throw new IllegalArgumentException("The template string is not valid.", ex);
+ }
+ }
+
+ public OutputParser getOutputParser() {
+ return outputParser;
+ }
+
+ public void setOutputParser(OutputParser outputParser) {
+ Objects.requireNonNull(outputParser, "Output Parser can not be null");
+ this.outputParser = outputParser;
+ }
+
+ public void add(String name, Object value) {
+ this.st.add(name, value);
+ this.dynamicModel.put(name, value);
+ }
+
+ public String getTemplate() {
+ return this.template;
+ }
+
+ public TemplateFormat getTemplateFormat() {
+ return this.templateFormat;
+ }
+
+ // Render Methods
+ @Override
+ public String render() {
+ validate(this.dynamicModel);
+ return st.render();
+ }
+
+ @Override
+ public String render(Map model) {
+ validate(model);
+ for (Entry entry : model.entrySet()) {
+ if (st.getAttribute(entry.getKey()) != null) {
+ st.remove(entry.getKey());
+ }
+ if (entry.getValue() instanceof Resource) {
+ st.add(entry.getKey(), renderResource((Resource) entry.getValue()));
+ }
+ else {
+ st.add(entry.getKey(), entry.getValue());
+ }
+
+ }
+ return st.render();
+ }
+
+ private String renderResource(Resource resource) {
+ try {
+ return resource.getContentAsString(Charset.defaultCharset());
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ // try (InputStream inputStream = resource.getInputStream()) {
+ // return StreamUtils.copyToString(inputStream, Charset.defaultCharset());
+ // }
+ // catch (IOException ex) {
+ // throw new RuntimeException(ex);
+ // }
+ }
+
+ @Override
+ public Message createMessage() {
+ return new UserMessage(render());
+ }
+
+ @Override
+ public Message createMessage(Map model) {
+ return new UserMessage(render(model));
+ }
+
+ @Override
+ public Prompt create() {
+ return new Prompt(render(new HashMap<>()));
+ }
+
+ @Override
+ public Prompt create(Map model) {
+ return new Prompt(render(model));
+ }
+
+ public Set getInputVariables() {
+ TokenStream tokens = this.st.impl.tokens;
+ return IntStream.range(0, tokens.range())
+ .mapToObj(tokens::get)
+ .filter(token -> token.getType() == STLexer.ID)
+ .map(Token::getText)
+ .collect(Collectors.toSet());
+ }
+
+ protected void validate(Map model) {
+ Set dynamicVariableNames = new HashSet<>(this.dynamicModel.keySet());
+ Set modelVariables = new HashSet<>(model.keySet());
+ modelVariables.addAll(dynamicVariableNames);
+ Set missingEntries = new HashSet<>(getInputVariables());
+ missingEntries.removeAll(modelVariables);
+ if (!missingEntries.isEmpty()) {
+ throw new IllegalStateException(
+ "All template variables were not replaced. Missing variable names are " + missingEntries);
+ }
+ }
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/PromptTemplateActions.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/PromptTemplateActions.java
new file mode 100644
index 000000000..bdee00acb
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/PromptTemplateActions.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.prompt;
+
+import java.util.Map;
+
+/**
+ * 提示词模板操作
+ */
+public interface PromptTemplateActions extends PromptTemplateStringActions {
+
+ /**
+ * 创建 Prompt
+ * @return
+ */
+ Prompt create();
+
+ Prompt create(Map model);
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/PromptTemplateChatActions.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/PromptTemplateChatActions.java
new file mode 100644
index 000000000..922d57c8d
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/PromptTemplateChatActions.java
@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.framework.ai.chat.prompt;
+
+import cn.iocoder.yudao.framework.ai.chat.messages.Message;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 聊天操作
+ *
+ */
+public interface PromptTemplateChatActions {
+
+ List createMessages();
+
+ List createMessages(Map model);
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/PromptTemplateMessageActions.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/PromptTemplateMessageActions.java
new file mode 100644
index 000000000..8de851a89
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/PromptTemplateMessageActions.java
@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.framework.ai.chat.prompt;
+
+import cn.iocoder.yudao.framework.ai.chat.messages.Message;
+
+import java.util.Map;
+
+/**
+ * 用户输入的提示内容 模板信息操作
+ */
+public interface PromptTemplateMessageActions {
+
+ /**
+ * 创建一个 message
+ * @return
+ */
+ Message createMessage();
+
+ /**
+ * 创建一个 message
+ * @return
+ */
+ Message createMessage(Map model);
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/PromptTemplateStringActions.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/PromptTemplateStringActions.java
new file mode 100644
index 000000000..58015b47d
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/PromptTemplateStringActions.java
@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.framework.ai.chat.prompt;
+
+import java.util.Map;
+
+/**
+ * 提示次模板字符串操作
+ */
+public interface PromptTemplateStringActions {
+
+ String render();
+
+ String render(Map model);
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/SystemPromptTemplate.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/SystemPromptTemplate.java
new file mode 100644
index 000000000..cafade4bb
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/SystemPromptTemplate.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.prompt;
+
+import cn.iocoder.yudao.framework.ai.chat.messages.Message;
+import cn.iocoder.yudao.framework.ai.chat.messages.SystemMessage;
+import org.springframework.core.io.Resource;
+
+import java.util.Map;
+
+public class SystemPromptTemplate extends PromptTemplate {
+
+ public SystemPromptTemplate(String template) {
+ super(template);
+ }
+
+ public SystemPromptTemplate(Resource resource) {
+ super(resource);
+ }
+
+ @Override
+ public Message createMessage() {
+ return new SystemMessage(render());
+ }
+
+ @Override
+ public Message createMessage(Map model) {
+ return new SystemMessage(render(model));
+ }
+
+ @Override
+ public Prompt create() {
+ return new Prompt(new SystemMessage(render()));
+ }
+
+ @Override
+ public Prompt create(Map model) {
+ return new Prompt(new SystemMessage(render(model)));
+ }
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/TemplateFormat.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/TemplateFormat.java
new file mode 100644
index 000000000..c22a78db3
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chat/prompt/TemplateFormat.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.chat.prompt;
+
+public enum TemplateFormat {
+
+ ST("ST");
+
+ private final String value;
+
+ TemplateFormat(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public static TemplateFormat fromValue(String value) {
+ for (TemplateFormat templateFormat : TemplateFormat.values()) {
+ if (templateFormat.getValue().equals(value)) {
+ return templateFormat;
+ }
+ }
+ throw new IllegalArgumentException("Invalid TemplateFormat value: " + value);
+ }
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/model/ModelClient.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/model/ModelClient.java
new file mode 100644
index 000000000..705a7a976
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/model/ModelClient.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2024-2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.model;
+
+/**
+ * The ModelClient interface provides a generic API for invoking AI models. It is designed
+ * to handle the interaction with various types of AI models by abstracting the process of
+ * sending requests and receiving responses. The interface uses Java generics to
+ * accommodate different types of requests and responses, enhancing flexibility and
+ * adaptability across different AI model implementations.
+ *
+ * @param the generic type of the request to the AI model
+ * @param the generic type of the response from the AI model
+ * @author Mark Pollack
+ * @since 0.8.0
+ */
+public interface ModelClient, TRes extends ModelResponse>> {
+
+ /**
+ * Executes a method call to the AI model.
+ * @param request the request object to be sent to the AI model
+ * @return the response from the AI model
+ */
+ TRes call(TReq request) throws Throwable;
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/model/ModelOptions.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/model/ModelOptions.java
new file mode 100644
index 000000000..6a5a738fe
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/model/ModelOptions.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2024-2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.model;
+
+/**
+ * Interface representing the customizable options for AI model interactions. This marker
+ * interface allows for the specification of various settings and parameters that can
+ * influence the behavior and output of AI models. It is designed to provide flexibility
+ * and adaptability in different AI scenarios, ensuring that the AI models can be
+ * fine-tuned according to specific requirements.
+ *
+ * @author Mark Pollack
+ * @since 0.8.0
+ */
+public interface ModelOptions {
+
+}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/model/ModelOptionsUtils.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/model/ModelOptionsUtils.java
new file mode 100644
index 000000000..0d6f74007
--- /dev/null
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/model/ModelOptionsUtils.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright 2024-2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.iocoder.yudao.framework.ai.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.github.victools.jsonschema.generator.*;
+import com.github.victools.jsonschema.module.jackson.JacksonModule;
+import com.github.victools.jsonschema.module.jackson.JacksonOption;
+import com.github.victools.jsonschema.module.swagger2.Swagger2Module;
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.BeanWrapperImpl;
+import org.springframework.util.Assert;
+import org.springframework.util.CollectionUtils;
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Field;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+
+/**
+ * Utility class for manipulating {@link ModelOptions} objects.
+ *
+ * @author Christian Tzolov
+ * @since 0.8.0
+ */
+public final class ModelOptionsUtils {
+
+ private final static ObjectMapper OBJECT_MAPPER = new ObjectMapper()
+ .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
+ .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
+
+ private final static List BEAN_MERGE_FIELD_EXCISIONS = List.of("class");
+
+ private static ConcurrentHashMap, List> REQUEST_FIELD_NAMES_PER_CLASS = new ConcurrentHashMap, List>();
+
+ private static AtomicReference SCHEMA_GENERATOR_CACHE = new AtomicReference<>();
+
+ private ModelOptionsUtils() {
+
+ }
+
+ /**
+ * Converts the given JSON string to a Map of String and Object.
+ * @param json the JSON string to convert to a Map.
+ * @return the converted Map.
+ */
+ public static Map jsonToMap(String json) {
+ try {
+ return OBJECT_MAPPER.readValue(json, MAP_TYPE_REF);
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static TypeReference> MAP_TYPE_REF = new TypeReference>() {
+ };
+
+ /**
+ * Converts the given JSON string to an Object of the given type.
+ * @param the type of the object to return.
+ * @param json the JSON string to convert to an object.
+ * @param type the type of the object to return.
+ * @return Object instance of the given type.
+ */
+ public static T jsonToObject(String json, Class type) {
+ try {
+ return OBJECT_MAPPER.readValue(json, type);
+ }
+ catch (Exception e) {
+ throw new RuntimeException("Failed to json: " + json, e);
+ }
+ }
+
+ /**
+ * Converts the given object to a JSON string.
+ * @param object the object to convert to a JSON string.
+ * @return the JSON string.
+ */
+ public static String toJsonString(Object object) {
+ try {
+ return OBJECT_MAPPER.writeValueAsString(object);
+ }
+ catch (JsonProcessingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Merges the source object into the target object and returns an object represented
+ * by the given class. The JSON property names are used to match the fields to merge.
+ * The source non-null values override the target values with the same field name. The
+ * source null values are ignored. If the acceptedFieldNames is not empty, only the
+ * fields with the given names are merged and returned. If the acceptedFieldNames is
+ * empty, use the {@code @JsonProperty} names, inferred from the provided clazz.
+ * @param they type of the class to return.
+ * @param source the source object to merge.
+ * @param target the target object to merge into.
+ * @param clazz the class to return.
+ * @param acceptedFieldNames the list of field names accepted for the target object.
+ * @return the merged object represented by the given class.
+ */
+ public static T merge(Object source, Object target, Class clazz, List acceptedFieldNames) {
+
+ if (source == null) {
+ source = Map.of();
+ }
+
+ List requestFieldNames = CollectionUtils.isEmpty(acceptedFieldNames)
+ ? REQUEST_FIELD_NAMES_PER_CLASS.computeIfAbsent(clazz, ModelOptionsUtils::getJsonPropertyValues)
+ : acceptedFieldNames;
+
+ if (CollectionUtils.isEmpty(requestFieldNames)) {
+ throw new IllegalArgumentException("No @JsonProperty fields found in the " + clazz.getName());
+ }
+
+ Map sourceMap = ModelOptionsUtils.objectToMap(source);
+ Map targetMap = ModelOptionsUtils.objectToMap(target);
+
+ targetMap.putAll(sourceMap.entrySet()
+ .stream()
+ .filter(e -> e.getValue() != null)
+ .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue())));
+
+ targetMap = targetMap.entrySet()
+ .stream()
+ .filter(e -> requestFieldNames.contains(e.getKey()))
+ .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
+
+ return ModelOptionsUtils.mapToClass(targetMap, clazz);
+ }
+
+ /**
+ * Merges the source object into the target object and returns an object represented
+ * by the given class. The JSON property names are used to match the fields to merge.
+ * The source non-null values override the target values with the same field name. The
+ * source null values are ignored. Returns the only field names that match the
+ * {@code @JsonProperty} names, inferred from the provided clazz.
+ * @param they type of the class to return.
+ * @param source the source object to merge.
+ * @param target the target object to merge into.
+ * @param clazz the class to return.
+ * @return the merged object represented by the given class.
+ */
+ public static T merge(Object source, Object target, Class clazz) {
+ return ModelOptionsUtils.merge(source, target, clazz, null);
+ }
+
+ /**
+ * Converts the given object to a Map.
+ * @param source the object to convert to a Map.
+ * @return the converted Map.
+ */
+ public static Map objectToMap(Object source) {
+ if (source == null) {
+ return new HashMap<>();
+ }
+ try {
+ String json = OBJECT_MAPPER.writeValueAsString(source);
+ return OBJECT_MAPPER.readValue(json, new TypeReference