diff --git a/README.md b/README.md
index 6913f0b0c..de570956d 100644
--- a/README.md
+++ b/README.md
@@ -37,7 +37,7 @@ And the Spring Boot Starter depending on if you are using Azure Open AI or Open
org.springframework.experimental.ai
spring-ai-azure-openai-spring-boot-starter
- 0.2.0-SNAPSHOT
+ 0.7.0-SNAPSHOT
```
@@ -47,7 +47,7 @@ And the Spring Boot Starter depending on if you are using Azure Open AI or Open
org.springframework.experimental.ai
spring-ai-openai-spring-boot-starter
- 0.2.0-SNAPSHOT
+ 0.7.0-SNAPSHOT
```
@@ -59,7 +59,7 @@ And the Spring Boot Starter depending on if you are using Azure Open AI or Open
org.springframework.experimental.ai
spring-ai-openai-spring-boot-starter
- 0.2.0-SNAPSHOT
+ 0.7.0-SNAPSHOT
```
diff --git a/pom.xml b/pom.xml
index 76eaa8555..28548238c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
org.springframework.experimental.ai
spring-ai
- 0.2.0-SNAPSHOT
+ 0.7.0-SNAPSHOT
pom
https://github.com/spring-projects-experimental/spring-ai
diff --git a/spring-ai-azure-openai/pom.xml b/spring-ai-azure-openai/pom.xml
index 87d7def9b..ffe0b62d3 100644
--- a/spring-ai-azure-openai/pom.xml
+++ b/spring-ai-azure-openai/pom.xml
@@ -4,7 +4,7 @@
org.springframework.experimental.ai
spring-ai
- 0.2.0-SNAPSHOT
+ 0.7.0-SNAPSHOT
spring-ai-azure-openai
jar
diff --git a/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/embedding/AzureOpenAiEmbeddingClient.java b/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/embedding/AzureOpenAiEmbeddingClient.java
index f84f133d6..f2126365f 100644
--- a/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/embedding/AzureOpenAiEmbeddingClient.java
+++ b/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/embedding/AzureOpenAiEmbeddingClient.java
@@ -15,6 +15,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.document.Document;
+import org.springframework.ai.document.MetadataMode;
import org.springframework.ai.embedding.Embedding;
import org.springframework.ai.embedding.EmbeddingClient;
import org.springframework.ai.embedding.EmbeddingResponse;
@@ -31,15 +32,23 @@ public class AzureOpenAiEmbeddingClient implements EmbeddingClient {
private final AtomicInteger embeddingDimensions = new AtomicInteger(-1);
+ private final MetadataMode metadataMode;
+
public AzureOpenAiEmbeddingClient(OpenAIClient azureOpenAiClient) {
this(azureOpenAiClient, "text-embedding-ada-002");
}
public AzureOpenAiEmbeddingClient(OpenAIClient azureOpenAiClient, String model) {
+ this(azureOpenAiClient, model, MetadataMode.EMBED);
+ }
+
+ public AzureOpenAiEmbeddingClient(OpenAIClient azureOpenAiClient, String model, MetadataMode metadataMode) {
Assert.notNull(azureOpenAiClient, "com.azure.ai.openai.OpenAIClient must not be null");
Assert.notNull(model, "Model must not be null");
+ Assert.notNull(metadataMode, "Metadata mode must not be null");
this.azureOpenAiClient = azureOpenAiClient;
this.model = model;
+ this.metadataMode = metadataMode;
}
@Override
@@ -54,7 +63,7 @@ public class AzureOpenAiEmbeddingClient implements EmbeddingClient {
public List embed(Document document) {
logger.debug("Retrieving embeddings");
Embeddings embeddings = this.azureOpenAiClient.getEmbeddings(this.model,
- new EmbeddingsOptions(List.of(document.getContent())));
+ new EmbeddingsOptions(List.of(document.getFormattedContent(this.metadataMode))));
logger.debug("Embeddings retrieved");
return extractEmbeddingsList(embeddings);
}
diff --git a/spring-ai-core/pom.xml b/spring-ai-core/pom.xml
index b2c74e939..1cc19e60e 100644
--- a/spring-ai-core/pom.xml
+++ b/spring-ai-core/pom.xml
@@ -5,7 +5,7 @@
org.springframework.experimental.ai
spring-ai
- 0.2.0-SNAPSHOT
+ 0.7.0-SNAPSHOT
spring-ai-core
jar
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/client/AiResponse.java b/spring-ai-core/src/main/java/org/springframework/ai/client/AiResponse.java
index c2ab03d09..3e65c6fd6 100644
--- a/spring-ai-core/src/main/java/org/springframework/ai/client/AiResponse.java
+++ b/spring-ai-core/src/main/java/org/springframework/ai/client/AiResponse.java
@@ -55,7 +55,7 @@ public class AiResponse {
}
/**
- * Arbitrary LLM-provider specific output
+ * Arbitrary model provider specific output
*/
public Map getProviderOutput() {
return Collections.unmodifiableMap(providerOutput);
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/document/ContentFormatter.java b/spring-ai-core/src/main/java/org/springframework/ai/document/ContentFormatter.java
new file mode 100644
index 000000000..cf285dc20
--- /dev/null
+++ b/spring-ai-core/src/main/java/org/springframework/ai/document/ContentFormatter.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2023-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.document;
+
+/**
+ * Converts the Document text and metadata into a AI, prompt-friendly text representation.
+ *
+ * @author Christian Tzolov
+ */
+public interface ContentFormatter {
+
+ String format(Document document, MetadataMode mode);
+
+}
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/document/DefaultContentFormatter.java b/spring-ai-core/src/main/java/org/springframework/ai/document/DefaultContentFormatter.java
new file mode 100644
index 000000000..fcc7a5703
--- /dev/null
+++ b/spring-ai-core/src/main/java/org/springframework/ai/document/DefaultContentFormatter.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2023-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.document;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.springframework.util.Assert;
+
+/**
+ * @author Christian Tzolov
+ */
+public class DefaultContentFormatter implements ContentFormatter {
+
+ private static final String TEMPLATE_CONTENT_PLACEHOLDER = "{content}";
+
+ private static final String TEMPLATE_METADATA_STRING_PLACEHOLDER = "{metadata_string}";
+
+ private static final String TEMPLATE_VALUE_PLACEHOLDER = "{value}";
+
+ private static final String TEMPLATE_KEY_PLACEHOLDER = "{key}";
+
+ private static final String DEFAULT_METADATA_TEMPLATE = String.format("%s: %s", TEMPLATE_KEY_PLACEHOLDER,
+ TEMPLATE_VALUE_PLACEHOLDER);
+
+ private static final String DEFAULT_METADATA_SEPARATOR = "\n";
+
+ private static final String DEFAULT_TEXT_TEMPLATE = String.format("%s\n\n%s", TEMPLATE_METADATA_STRING_PLACEHOLDER,
+ TEMPLATE_CONTENT_PLACEHOLDER);
+
+ /**
+ * Template for how metadata is formatted, with {key} and {value} placeholders.
+ */
+ private final String metadataTemplate;
+
+ /**
+ * Separator between metadata fields when converting to string.
+ */
+ private final String metadataSeparator;
+
+ /**
+ * Template for how Document text is formatted, with {content} and {metadata_string}
+ * placeholders.
+ */
+ private final String textTemplate;
+
+ /**
+ * Metadata keys that are excluded from text for the inference.
+ */
+ private final List excludedInferenceMetadataKeys;
+
+ /**
+ * Metadata keys that are excluded from text for the embed model.
+ */
+ private final List excludedEmbedMetadataKeys;
+
+ /**
+ * Start building a new configuration.
+ * @return The entry point for creating a new configuration.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * {@return the default config}
+ */
+ public static DefaultContentFormatter defaultConfig() {
+
+ return builder().build();
+ }
+
+ private DefaultContentFormatter(Builder builder) {
+ this.metadataTemplate = builder.metadataTemplate;
+ this.metadataSeparator = builder.metadataSeparator;
+ this.textTemplate = builder.textTemplate;
+ this.excludedInferenceMetadataKeys = builder.excludedInferenceMetadataKeys;
+ this.excludedEmbedMetadataKeys = builder.excludedEmbedMetadataKeys;
+ }
+
+ public static class Builder {
+
+ private String metadataTemplate = DEFAULT_METADATA_TEMPLATE;
+
+ private String metadataSeparator = DEFAULT_METADATA_SEPARATOR;
+
+ private String textTemplate = DEFAULT_TEXT_TEMPLATE;
+
+ private List excludedInferenceMetadataKeys = new ArrayList<>();
+
+ private List excludedEmbedMetadataKeys = new ArrayList<>();
+
+ private Builder() {
+ }
+
+ public Builder from(DefaultContentFormatter fromFormatter) {
+ this.withExcludedEmbedMetadataKeys(fromFormatter.getExcludedEmbedMetadataKeys())
+ .withExcludedInferenceMetadataKeys(fromFormatter.getExcludedInferenceMetadataKeys())
+ .withMetadataSeparator(fromFormatter.getMetadataSeparator())
+ .withMetadataTemplate(fromFormatter.getMetadataTemplate())
+ .withTextTemplate(fromFormatter.getTextTemplate());
+ return this;
+ }
+
+ /**
+ * Configures the Document metadata template.
+ * @param metadataTemplate Metadata template to use.
+ * @return this builder
+ */
+ public Builder withMetadataTemplate(String metadataTemplate) {
+ Assert.hasText(metadataTemplate, "Metadata Template must not be empty");
+ this.metadataTemplate = metadataTemplate;
+ return this;
+ }
+
+ /**
+ * Configures the Document metadata separator.
+ * @param metadataSeparator Metadata separator to use.
+ * @return this builder
+ */
+ public Builder withMetadataSeparator(String metadataSeparator) {
+ Assert.notNull(metadataSeparator, "Metadata separator must not be empty");
+ this.metadataSeparator = metadataSeparator;
+ return this;
+ }
+
+ /**
+ * Configures the Document text template.
+ * @param textTemplate Document's content template.
+ * @return this builder
+ */
+ public Builder withTextTemplate(String textTemplate) {
+ Assert.hasText(textTemplate, "Document's text template must not be empty");
+ this.textTemplate = textTemplate;
+ return this;
+ }
+
+ /**
+ * Configures the excluded Inference metadata keys to filter out from the model.
+ * @param excludedInferenceMetadataKeys Excluded inference metadata keys to use.
+ * @return this builder
+ */
+ public Builder withExcludedInferenceMetadataKeys(List excludedInferenceMetadataKeys) {
+ Assert.notNull(excludedInferenceMetadataKeys, "Excluded inference metadata keys must not be null");
+ this.excludedInferenceMetadataKeys = excludedInferenceMetadataKeys;
+ return this;
+ }
+
+ public Builder withExcludedInferenceMetadataKeys(String... keys) {
+ Assert.notNull(keys, "Excluded inference metadata keys must not be null");
+ this.excludedInferenceMetadataKeys.addAll(Arrays.asList(keys));
+ return this;
+ }
+
+ /**
+ * Configures the excluded Embed metadata keys to filter out from the model.
+ * @param excludedEmbedMetadataKeys Excluded Embed metadata keys to use.
+ * @return this builder
+ */
+ public Builder withExcludedEmbedMetadataKeys(List excludedEmbedMetadataKeys) {
+ Assert.notNull(excludedEmbedMetadataKeys, "Excluded Embed metadata keys must not be null");
+ this.excludedEmbedMetadataKeys = excludedEmbedMetadataKeys;
+ return this;
+ }
+
+ public Builder withExcludedEmbedMetadataKeys(String... keys) {
+ Assert.notNull(keys, "Excluded Embed metadata keys must not be null");
+ this.excludedEmbedMetadataKeys.addAll(Arrays.asList(keys));
+ return this;
+ }
+
+ /**
+ * {@return the immutable configuration}
+ */
+ public DefaultContentFormatter build() {
+ return new DefaultContentFormatter(this);
+ }
+
+ }
+
+ @Override
+ public String format(Document document, MetadataMode metadataMode) {
+
+ var metadata = metadataFilter(document.getMetadata(), metadataMode);
+
+ var metadataText = metadata.entrySet()
+ .stream()
+ .map(metadataEntry -> this.metadataTemplate.replace(TEMPLATE_KEY_PLACEHOLDER, metadataEntry.getKey())
+ .replace(TEMPLATE_VALUE_PLACEHOLDER, metadataEntry.getValue().toString()))
+ .collect(Collectors.joining(this.metadataSeparator));
+
+ return this.textTemplate.replace(TEMPLATE_METADATA_STRING_PLACEHOLDER, metadataText)
+ .replace(TEMPLATE_CONTENT_PLACEHOLDER, document.getContent());
+ }
+
+ /**
+ * Filters the metadata by the configured MetadataMode.
+ * @param metadata Document metadata.
+ * @return Returns the filtered by configured mode metadata.
+ */
+ protected Map metadataFilter(Map metadata, MetadataMode metadataMode) {
+
+ if (metadataMode == MetadataMode.ALL) {
+ return new HashMap(metadata);
+ }
+ if (metadataMode == MetadataMode.NONE) {
+ return new HashMap(Collections.emptyMap());
+ }
+
+ Set usableMetadataKeys = new HashSet<>(metadata.keySet());
+
+ if (metadataMode == MetadataMode.INFERENCE) {
+ usableMetadataKeys.removeAll(this.excludedInferenceMetadataKeys);
+ }
+ else if (metadataMode == MetadataMode.EMBED) {
+ usableMetadataKeys.removeAll(this.excludedEmbedMetadataKeys);
+ }
+
+ return new HashMap(metadata.entrySet()
+ .stream()
+ .filter(e -> usableMetadataKeys.contains(e.getKey()))
+ .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue())));
+ }
+
+ public String getMetadataTemplate() {
+ return this.metadataTemplate;
+ }
+
+ public String getMetadataSeparator() {
+ return this.metadataSeparator;
+ }
+
+ public String getTextTemplate() {
+ return this.textTemplate;
+ }
+
+ public List getExcludedInferenceMetadataKeys() {
+ return Collections.unmodifiableList(this.excludedInferenceMetadataKeys);
+ }
+
+ public List getExcludedEmbedMetadataKeys() {
+ return Collections.unmodifiableList(this.excludedEmbedMetadataKeys);
+ }
+
+}
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/document/Document.java b/spring-ai-core/src/main/java/org/springframework/ai/document/Document.java
index 896b9ab69..8554a1081 100644
--- a/spring-ai-core/src/main/java/org/springframework/ai/document/Document.java
+++ b/spring-ai-core/src/main/java/org/springframework/ai/document/Document.java
@@ -1,44 +1,83 @@
+/*
+ * Copyright 2023-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.document;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
-import org.springframework.util.StringUtils;
-import java.util.*;
+import org.springframework.util.Assert;
+@JsonIgnoreProperties({ "contentFormatter" })
public class Document {
+ public final static ContentFormatter DEFAULT_CONTENT_FORMATTER = DefaultContentFormatter.defaultConfig();
+
/**
* Unique ID
*/
private final String id;
- @JsonProperty(index = 100)
- private List embedding = new ArrayList<>();
-
/**
* Metadata for the document. It should not be nested and values should be restricted
* to string, int, float, boolean for simple use with Vector Dbs.
*/
private Map metadata;
- // Type; introduce when support images, now only text.
+ /**
+ * Document content.
+ */
+ private String content;
- private String text;
+ /**
+ * Embedding of the document. Note: ephemeral field.
+ */
+ @JsonProperty(index = 100)
+ private List embedding = new ArrayList<>();
+
+ /**
+ * Mutable, ephemeral, content to text formatter. Defaults to Document text.
+ */
+ @JsonIgnore
+ private ContentFormatter contentFormatter = DEFAULT_CONTENT_FORMATTER;
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
- public Document(@JsonProperty("text") String text) {
- this(text, new HashMap<>());
+ public Document(@JsonProperty("content") String content) {
+ this(content, new HashMap<>());
}
- public Document(String text, Map metadata) {
- this(UUID.randomUUID().toString(), text, metadata);
+ public Document(String content, Map metadata) {
+ this(UUID.randomUUID().toString(), content, metadata);
}
- public Document(String id, String text, Map metadata) {
+ public Document(String id, String content, Map metadata) {
+ Assert.hasText(id, "id must not be null");
+ Assert.hasText(content, "content must not be null");
+ Assert.notNull(metadata, "metadata must not be null");
+
this.id = id;
- this.text = text;
+ this.content = content;
this.metadata = metadata;
}
@@ -46,109 +85,98 @@ public class Document {
return id;
}
- public String getText() {
- return this.text;
+ public String getContent() {
+ return this.content;
}
- public Map getMetadata() {
- return metadata;
+ @JsonIgnore
+ public String getFormattedContent() {
+ return this.getFormattedContent(MetadataMode.ALL);
}
- public List getEmbedding() {
- return embedding;
+ public String getFormattedContent(MetadataMode metadataMode) {
+ Assert.notNull(metadataMode, "Metadata mode must not be null");
+ return this.contentFormatter.format(this, metadataMode);
+ }
+
+ /**
+ * Helper content extractor that uses and external {@link ContentFormatter}.
+ */
+ public String getFormattedContent(ContentFormatter formatter, MetadataMode metadataMode) {
+ Assert.notNull(formatter, "formatter must not be null");
+ Assert.notNull(metadataMode, "Metadata mode must not be null");
+ return formatter.format(this, metadataMode);
}
public void setEmbedding(List embedding) {
+ Assert.notNull(embedding, "embedding must not be null");
this.embedding = embedding;
}
+ /**
+ * Replace the document's {@link ContentFormatter}.
+ * @param contentFormatter new formatter to use.
+ */
+ public void setContentFormatter(ContentFormatter contentFormatter) {
+ this.contentFormatter = contentFormatter;
+ }
+
+ public Map getMetadata() {
+ return this.metadata;
+ }
+
+ public List getEmbedding() {
+ return this.embedding;
+ }
+
+ public ContentFormatter getContentFormatter() {
+ return contentFormatter;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((id == null) ? 0 : id.hashCode());
+ result = prime * result + ((metadata == null) ? 0 : metadata.hashCode());
+ result = prime * result + ((content == null) ? 0 : content.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;
+ Document other = (Document) obj;
+ if (id == null) {
+ if (other.id != null)
+ return false;
+ }
+ else if (!id.equals(other.id))
+ return false;
+ if (metadata == null) {
+ if (other.metadata != null)
+ return false;
+ }
+ else if (!metadata.equals(other.metadata))
+ return false;
+ if (content == null) {
+ if (other.content != null)
+ return false;
+ }
+ else if (!content.equals(other.content))
+ return false;
+ return true;
+ }
+
@Override
public String toString() {
- return "Document{" + "id='" + id + '\'' + ", metadata=" + metadata + ", text='" + text + '\'' + '}';
- }
-
- private static String DEFAULT_TEXT_TEMPLATE = "{metadata_string}\n\n{text}";
-
- private static String DEFAULT_METADATA_TEMPLATE = "{key}: {value}";
-
- private String textTemplate = DEFAULT_TEXT_TEMPLATE;
-
- private String metadataTemplate = DEFAULT_METADATA_TEMPLATE;
-
- private String metadataSeparator = "\n";
-
- private MetadataMode metadataMode = MetadataMode.NONE;
-
- private List excludedMetadataKeysForLlm;
-
- @JsonIgnore
- public String getContent() {
- return getContent(MetadataMode.ALL);
- }
-
- public String getContent(MetadataMode metadataMode) {
- if (metadataMode == MetadataMode.NONE) {
- return this.text;
- }
- String metadataString = getMetadataString(metadataMode);
- if (!StringUtils.hasText(metadataString)) {
- return this.text;
- }
- return getTextTemplate().replace("{metadata_string}", metadataString).replace("{text}", text);
- }
-
- @JsonIgnore
- public String getMetadataString() {
- return getMetadataString(metadataMode);
- }
-
- public String getMetadataString(MetadataMode metadataMode) {
- if (metadataMode == MetadataMode.NONE) {
- return "";
- }
- Set usableMetadataKeys = new HashSet<>(metadata.keySet());
- if (metadataMode == MetadataMode.LLM) {
- usableMetadataKeys.removeAll(this.excludedMetadataKeysForLlm);
- }
- else if (metadataMode == MetadataMode.EMBED) {
- usableMetadataKeys.removeAll(this.excludedMetadataKeysForLlm);
- }
-
- List metadataStringList = new ArrayList<>();
-
- for (Map.Entry entry : metadata.entrySet()) {
- String key = entry.getKey();
- Object value = entry.getValue();
- if (usableMetadataKeys.contains(key)) {
- metadataStringList
- .add(getMetadataTemplate().replace("{key}", key).replace("{value}", value.toString()));
- }
- }
- return String.join(getMetadataSeparator(), metadataStringList);
- }
-
- private String getTextTemplate() {
- return textTemplate;
- }
-
- private String getMetadataTemplate() {
- return metadataTemplate;
- }
-
- private String getMetadataSeparator() {
- return metadataSeparator;
- }
-
- public void setTextTemplate(String textTemplate) {
- this.textTemplate = textTemplate;
- }
-
- public void setMetadataTemplate(String metadataTemplate) {
- this.metadataTemplate = metadataTemplate;
- }
-
- public void setMetadataSeparator(String metadataSeparator) {
- this.metadataSeparator = metadataSeparator;
+ return "Document{" + "id='" + id + '\'' + ", metadata=" + metadata + ", content='" + new String(content) + '\''
+ + '}';
}
}
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/document/DocumentTransformer.java b/spring-ai-core/src/main/java/org/springframework/ai/document/DocumentTransformer.java
index 518b5ac96..1a8243b3d 100644
--- a/spring-ai-core/src/main/java/org/springframework/ai/document/DocumentTransformer.java
+++ b/spring-ai-core/src/main/java/org/springframework/ai/document/DocumentTransformer.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2023-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.document;
import java.util.List;
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/document/MetadataMode.java b/spring-ai-core/src/main/java/org/springframework/ai/document/MetadataMode.java
index 795ca16d7..13ef775cb 100644
--- a/spring-ai-core/src/main/java/org/springframework/ai/document/MetadataMode.java
+++ b/spring-ai-core/src/main/java/org/springframework/ai/document/MetadataMode.java
@@ -1,7 +1,23 @@
+/*
+ * Copyright 2023-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.document;
public enum MetadataMode {
- ALL, EMBED, LLM, NONE;
+ ALL, EMBED, INFERENCE, NONE;
-}
+}
\ No newline at end of file
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/loader/extractor/AbstractMetadataFeatureExtractor.java b/spring-ai-core/src/main/java/org/springframework/ai/loader/extractor/AbstractMetadataFeatureExtractor.java
new file mode 100644
index 000000000..6646560fd
--- /dev/null
+++ b/spring-ai-core/src/main/java/org/springframework/ai/loader/extractor/AbstractMetadataFeatureExtractor.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2023-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.loader.extractor;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.ai.document.Document;
+import org.springframework.ai.document.DocumentTransformer;
+
+/**
+ * @author Christian Tzolov
+ */
+public abstract class AbstractMetadataFeatureExtractor implements DocumentTransformer {
+
+ @Override
+ public List apply(List documents) {
+ List