diff --git a/pom.xml b/pom.xml
index 311f68d95..b97fc03f9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -54,7 +54,11 @@
UTF-8
UTF-8
17
+
+
+
3.1.2
+ 4.0.2
1.6.2
@@ -239,7 +243,7 @@
-
+
test-coverage
@@ -292,7 +296,6 @@
-
diff --git a/spring-ai-core/pom.xml b/spring-ai-core/pom.xml
index 0d5034768..78462cf22 100644
--- a/spring-ai-core/pom.xml
+++ b/spring-ai-core/pom.xml
@@ -19,8 +19,20 @@
+
-
+
+ org.antlr
+ stringtemplate
+ ${stringtemplate.version}
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/AbstractPromptTemplate.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/AbstractPromptTemplate.java
new file mode 100644
index 000000000..c18e7dda5
--- /dev/null
+++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/AbstractPromptTemplate.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 org.springframework.ai.core.prompts;
+
+import java.util.Map;
+import java.util.Optional;
+
+public abstract class AbstractPromptTemplate implements PromptInput {
+
+ private Optional outputParser = Optional.empty();
+
+ public AbstractPromptTemplate(Optional outputParser) {
+ this.outputParser = outputParser;
+ }
+
+ @Override
+ public Optional getOutputParser() {
+ return this.outputParser;
+ }
+
+ public abstract String formatAsString(Map inputVariables);
+
+ public abstract PromptValue formatAsPrompt(Map inputVariables);
+
+}
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/AbstractStringPromptTemplate.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/AbstractStringPromptTemplate.java
new file mode 100644
index 000000000..cb6013121
--- /dev/null
+++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/AbstractStringPromptTemplate.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 org.springframework.ai.core.prompts;
+
+import java.util.Map;
+import java.util.Optional;
+
+public abstract class AbstractStringPromptTemplate extends AbstractPromptTemplate implements PromptTemplateInput {
+
+ public AbstractStringPromptTemplate(Optional outputParser) {
+ super(outputParser);
+ }
+
+ @Override
+ public PromptValue formatAsPrompt(Map inputVariables) {
+ String formattedPrompt = formatAsString(inputVariables);
+ return new StringPromptValue(formattedPrompt);
+ }
+
+}
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/Generation.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/Generation.java
new file mode 100644
index 000000000..76be53095
--- /dev/null
+++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/Generation.java
@@ -0,0 +1,31 @@
+/*
+ * 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 org.springframework.ai.core.prompts;
+
+import java.util.Map;
+
+public class Generation {
+
+ private final String text;
+
+ private Map info;
+
+ public Generation(String text) {
+ this.text = text;
+ }
+
+}
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/OutputParser.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/OutputParser.java
new file mode 100644
index 000000000..e880f6490
--- /dev/null
+++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/OutputParser.java
@@ -0,0 +1,25 @@
+/*
+ * 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 org.springframework.ai.core.prompts;
+
+import java.util.List;
+
+public interface OutputParser {
+
+ Object parseResut(List generations);
+
+}
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/PromptInput.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/PromptInput.java
new file mode 100644
index 000000000..3bcae2b56
--- /dev/null
+++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/PromptInput.java
@@ -0,0 +1,30 @@
+/*
+ * 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 org.springframework.ai.core.prompts;
+
+import java.util.Optional;
+import java.util.Set;
+
+public interface PromptInput {
+
+ Set getInputVariables();
+
+ Optional getOutputParser();
+
+ // Leave out Partial Input Variables for now
+
+}
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/PromptTemplate.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/PromptTemplate.java
new file mode 100644
index 000000000..7e023a64c
--- /dev/null
+++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/PromptTemplate.java
@@ -0,0 +1,134 @@
+/*
+ * 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 org.springframework.ai.core.prompts;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import org.antlr.runtime.Token;
+import org.antlr.runtime.TokenStream;
+import org.stringtemplate.v4.ST;
+import org.stringtemplate.v4.compiler.STLexer;
+
+public class PromptTemplate extends AbstractStringPromptTemplate implements PromptTemplateInput {
+
+ private String template;
+
+ private TemplateFormat templateFormat = TemplateFormat.FSTRING;
+
+ public PromptTemplate(String template) {
+ super(Optional.empty());
+ this.template = template;
+ }
+
+ public PromptTemplate(String template, boolean validate) {
+ super(Optional.empty());
+ validateTemplate(validate);
+ this.template = template;
+ }
+
+ public PromptTemplate(String template, TemplateFormat templateFormat) {
+ super(Optional.empty());
+ this.template = template;
+ this.templateFormat = templateFormat;
+ }
+
+ public PromptTemplate(String template, TemplateFormat templateFormat, boolean validate) {
+ super(Optional.empty());
+ validateTemplate(validate);
+ this.template = template;
+ this.templateFormat = templateFormat;
+ }
+
+ public PromptTemplate(String template, Optional outputParser) {
+ super(outputParser);
+ this.template = template;
+ }
+
+ public PromptTemplate(String template, Optional outputParser, boolean validate) {
+ super(outputParser);
+ validateTemplate(validate);
+ this.template = template;
+ }
+
+ public PromptTemplate(String template, Optional outputParser, TemplateFormat templateFormat) {
+ super(outputParser);
+ this.template = template;
+ this.templateFormat = templateFormat;
+ }
+
+ public PromptTemplate(String template, Optional outputParser, TemplateFormat templateFormat,
+ boolean validate) {
+ super(outputParser);
+ validateTemplate(validate);
+ this.template = template;
+ this.templateFormat = templateFormat;
+ }
+
+ @Override
+ public String formatAsString(Map inputVariables) {
+ // Only "F-String" for now
+ ST st = new ST(this.template, '{', '}');
+ for (Entry stringObjectEntry : inputVariables.entrySet()) {
+ st.add(stringObjectEntry.getKey(), stringObjectEntry.getValue());
+ }
+ return st.render();
+ }
+
+ @Override
+ public String getTemplate() {
+ return this.template;
+ }
+
+ @Override
+ public TemplateFormat getTemplateFormat() {
+ return this.templateFormat;
+ }
+
+ @Override
+ public Set getInputVariables() {
+ ST st = new ST(this.template, '{', '}');
+ TokenStream tokens = st.impl.tokens;
+ return IntStream.range(0, tokens.range())
+ .mapToObj(tokens::get)
+ .filter(token -> token.getType() == STLexer.ID)
+ .map(Token::getText)
+ .collect(Collectors.toSet());
+ }
+
+ private void validateTemplate(boolean validate) {
+ if (!validate) {
+ return;
+ }
+ try {
+ ST st = new ST(this.template, '{', '}');
+ Set inputVariables = getInputVariables();
+ for (String inputVariable : inputVariables) {
+ st.add(inputVariable, "foo");
+ }
+ st.render();
+ }
+ catch (Exception ex) {
+ throw new IllegalArgumentException("The template string is not valid.", ex);
+ }
+ }
+
+}
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/PromptTemplateInput.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/PromptTemplateInput.java
new file mode 100644
index 000000000..30990cff4
--- /dev/null
+++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/PromptTemplateInput.java
@@ -0,0 +1,25 @@
+/*
+ * 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 org.springframework.ai.core.prompts;
+
+public interface PromptTemplateInput extends PromptInput {
+
+ String getTemplate();
+
+ TemplateFormat getTemplateFormat();
+
+}
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/PromptValue.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/PromptValue.java
new file mode 100644
index 000000000..8743818f7
--- /dev/null
+++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/PromptValue.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 org.springframework.ai.core.prompts;
+
+import java.util.List;
+
+import org.springframework.ai.core.prompts.messages.Message;
+
+public interface PromptValue {
+
+ String toStringValue();
+
+ List toMessages();
+
+}
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/StringPromptValue.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/StringPromptValue.java
new file mode 100644
index 000000000..56007804f
--- /dev/null
+++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/StringPromptValue.java
@@ -0,0 +1,43 @@
+/*
+ * 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 org.springframework.ai.core.prompts;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.springframework.ai.core.prompts.messages.HumanMessage;
+import org.springframework.ai.core.prompts.messages.Message;
+
+public class StringPromptValue implements PromptValue {
+
+ private String value;
+
+ public StringPromptValue(String formattedPrompt) {
+ this.value = formattedPrompt;
+ }
+
+ @Override
+ public String toStringValue() {
+ return this.value;
+ }
+
+ @Override
+ public List toMessages() {
+ return Collections.singletonList(new HumanMessage(this.value));
+ }
+
+}
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/TemplateFormat.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/TemplateFormat.java
new file mode 100644
index 000000000..daa8a240d
--- /dev/null
+++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/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 org.springframework.ai.core.prompts;
+
+public enum TemplateFormat {
+
+ FSTRING("f-string");
+
+ 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/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/messages/AbstractMessage.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/messages/AbstractMessage.java
new file mode 100644
index 000000000..78caec75c
--- /dev/null
+++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/messages/AbstractMessage.java
@@ -0,0 +1,56 @@
+/*
+ * 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 org.springframework.ai.core.prompts.messages;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public abstract class AbstractMessage implements Message {
+
+ private String content;
+
+ private Map properties = new HashMap<>();
+
+ private MessageType messageType;
+
+ protected AbstractMessage(MessageType messageType, String content) {
+ this.messageType = messageType;
+ this.content = content;
+ }
+
+ protected AbstractMessage(MessageType messageType, String content, Map properties) {
+ this.messageType = messageType;
+ this.content = content;
+ this.properties = properties;
+ }
+
+ @Override
+ public String getContent() {
+ return this.content;
+ }
+
+ @Override
+ public Map getProperties() {
+ return this.properties;
+ }
+
+ @Override
+ public MessageType getMessageType() {
+ return this.messageType;
+ }
+
+}
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/messages/AiMessage.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/messages/AiMessage.java
new file mode 100644
index 000000000..f9328071e
--- /dev/null
+++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/messages/AiMessage.java
@@ -0,0 +1,31 @@
+/*
+ * 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 org.springframework.ai.core.prompts.messages;
+
+import java.util.Map;
+
+public class AiMessage extends AbstractMessage {
+
+ public AiMessage(String content) {
+ super(MessageType.AI, content);
+ }
+
+ public AiMessage(String content, Map properties) {
+ super(MessageType.AI, content, properties);
+ }
+
+}
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/messages/ChatMessage.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/messages/ChatMessage.java
new file mode 100644
index 000000000..7e4818bc3
--- /dev/null
+++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/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 org.springframework.ai.core.prompts.messages;
+
+import java.util.Map;
+
+public class ChatMessage extends AbstractMessage {
+
+ private String role;
+
+ public ChatMessage(String content, String role) {
+ super(MessageType.SYSTEM, content);
+ this.role = role;
+ }
+
+ public ChatMessage(String content, Map properties, String role) {
+ super(MessageType.SYSTEM, content, properties);
+ this.role = role;
+ }
+
+ public String getRole() {
+ return role;
+ }
+
+}
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/messages/FunctionMessage.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/messages/FunctionMessage.java
new file mode 100644
index 000000000..0ca950204
--- /dev/null
+++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/messages/FunctionMessage.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 org.springframework.ai.core.prompts.messages;
+
+import java.util.Map;
+
+public class FunctionMessage extends AbstractMessage {
+
+ private String functionName;
+
+ public FunctionMessage(String content, String functionName) {
+ super(MessageType.SYSTEM, content);
+ this.functionName = functionName;
+ }
+
+ public FunctionMessage(String content, Map properties, String functionName) {
+ super(MessageType.SYSTEM, content, properties);
+ this.functionName = functionName;
+ }
+
+ public String getFunctionName() {
+ return functionName;
+ }
+
+}
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/messages/HumanMessage.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/messages/HumanMessage.java
new file mode 100644
index 000000000..eaaa3cf46
--- /dev/null
+++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/messages/HumanMessage.java
@@ -0,0 +1,31 @@
+/*
+ * 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 org.springframework.ai.core.prompts.messages;
+
+import java.util.Map;
+
+public class HumanMessage extends AbstractMessage {
+
+ public HumanMessage(String content) {
+ super(MessageType.HUMAN, content);
+ }
+
+ public HumanMessage(String content, Map properties) {
+ super(MessageType.HUMAN, content, properties);
+ }
+
+}
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/messages/Message.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/messages/Message.java
new file mode 100644
index 000000000..77f4df346
--- /dev/null
+++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/messages/Message.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 org.springframework.ai.core.prompts.messages;
+
+import java.util.Map;
+
+public interface Message {
+
+ String getContent();
+
+ Map getProperties();
+
+ MessageType getMessageType();
+
+}
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/messages/MessageType.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/messages/MessageType.java
new file mode 100644
index 000000000..5a8d22457
--- /dev/null
+++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/messages/MessageType.java
@@ -0,0 +1,49 @@
+/*
+ * 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 org.springframework.ai.core.prompts.messages;
+
+public enum MessageType {
+
+ HUMAN("human"),
+
+ AI("ai"),
+
+ GENERIC("generic"),
+
+ SYSTEM("system"),
+
+ 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/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/messages/SystemMessage.java b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/messages/SystemMessage.java
new file mode 100644
index 000000000..71f90f6d4
--- /dev/null
+++ b/spring-ai-core/src/main/java/org/springframework/ai/core/prompts/messages/SystemMessage.java
@@ -0,0 +1,31 @@
+/*
+ * 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 org.springframework.ai.core.prompts.messages;
+
+import java.util.Map;
+
+public class SystemMessage extends AbstractMessage {
+
+ public SystemMessage(String content) {
+ super(MessageType.SYSTEM, content);
+ }
+
+ public SystemMessage(String content, Map properties) {
+ super(MessageType.SYSTEM, content, properties);
+ }
+
+}
diff --git a/spring-ai-core/src/test/java/org/springframework/ai/core/prompts/PromptTests.java b/spring-ai-core/src/test/java/org/springframework/ai/core/prompts/PromptTests.java
new file mode 100644
index 000000000..fd4e5fb26
--- /dev/null
+++ b/spring-ai-core/src/test/java/org/springframework/ai/core/prompts/PromptTests.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 org.springframework.ai.core.prompts;
+
+import java.util.Set;
+
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class PromptTests {
+
+ @Test
+ void testSingleInputVariable() {
+ String template = "This is a {foo} test";
+ PromptTemplate promptTemplate = new PromptTemplate(template);
+ Set inputVariables = promptTemplate.getInputVariables();
+ assertThat(inputVariables).isNotEmpty();
+ assertThat(inputVariables).hasSize(1);
+ assertThat(inputVariables).contains("foo");
+ }
+
+ @Test
+ void testMultipleInputVariables() {
+ String template = "This {bar} is a {foo} test";
+ PromptTemplate promptTemplate = new PromptTemplate(template);
+ Set inputVariables = promptTemplate.getInputVariables();
+ assertThat(inputVariables).isNotEmpty();
+ assertThat(inputVariables).hasSize(2);
+ assertThat(inputVariables).contains("foo", "bar");
+ }
+
+ @Test
+ void testMultipleInputVariablesWithRepeats() {
+ String template = "This {bar} is a {foo} test {foo}.";
+ PromptTemplate promptTemplate = new PromptTemplate(template);
+ Set inputVariables = promptTemplate.getInputVariables();
+ assertThat(inputVariables).isNotEmpty();
+ assertThat(inputVariables).hasSize(2);
+ assertThat(inputVariables).contains("foo", "bar");
+ }
+
+ @Test
+ void testBadTemplateString() {
+ String template = "This is a {foo test";
+ Assertions.assertThatThrownBy(() -> {
+ PromptTemplate promptTemplate = new PromptTemplate(template, true);
+ }).isInstanceOf(IllegalArgumentException.class).hasMessage("The template string is not valid.");
+ }
+
+}