diff --git a/README.md b/README.md index 9ef4c3eb5..61c1ec131 100644 --- a/README.md +++ b/README.md @@ -232,31 +232,6 @@ Though the `DocumentWriter` interface isn't exclusively for Vector Database writ They ascertain which document sections the AI should use for generating responses. Examples of Vector Databases include Chroma, Postgres, Pinecone, Weaviate, Mongo Atlas, and Redis. Spring AI's `VectorStore` abstraction permits effortless transitions between database implementations. -### Chaining together multiple AI model interactions - -**Chains:** Many AI solutions require multiple AI interactions to respond to a single user input. -"Chains" organize these interactions, offering modular AI workflows that promote reusability. -While you can create custom Chains tailored to your specific use case, pre-configured use-case-specific Chains are provided to accelerate your development. -Use cases such as Question-Answering, Text Generation, and Summarization are examples. - -* This is currently a work in progress. - -### Memory - -**Memory:** To support multiple AI model interactions, your application must recall the previous inputs and outputs. -A variety of algorithms are available for different scenarios, often backed by databases like Redis, Cassandra, MongoDB, Postgres, and other database technologies. - -* This is currently a work in progress - -### Agents - -Beyond Chains, Agents represent the next level of sophistication. -Agents use the AI models themselves to determine the techniques and steps to respond to a user's query. -Agents might even dynamically access external data sources to retrieve information necessary for responding to a user. -It's getting a bit funky, isn't it? - -* This is currently a work in progress - ## Building To build with only unit tests diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chain/AbstractChain.java b/spring-ai-core/src/main/java/org/springframework/ai/chain/AbstractChain.java deleted file mode 100644 index 5300062f6..000000000 --- a/spring-ai-core/src/main/java/org/springframework/ai/chain/AbstractChain.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.springframework.ai.chain; - -import org.springframework.ai.memory.Memory; - -import java.util.*; - -public abstract class AbstractChain implements Chain { - - private Optional memory = Optional.empty(); - - public Optional getMemory() { - return this.memory; - } - - public void setMemory(Memory memory) { - Objects.requireNonNull(memory, "Memory can not be null."); - this.memory = Optional.of(memory); - } - - @Override - public abstract List getInputKeys(); - - @Override - public abstract List getOutputKeys(); - - // TODO validation of input/outputs - - @Override - public AiOutput apply(AiInput aiInput) { - AiInput aiInputToUse = preProcess(aiInput); - AiOutput aiOutput = doApply(aiInputToUse); - Map outputMapToUse = postProcess(aiInput, aiOutput); - return new AiOutput(outputMapToUse); - } - - @Override - public AiInput preProcess(AiInput aiInput) { - validateInputs(aiInput.getInputData()); - return aiInput; - } - - protected abstract AiOutput doApply(AiInput aiInput); - - @Override - public Map postProcess(AiInput aiInput, AiOutput aiOutput) { - validateOutputs(aiOutput.getOutputData()); - Map combindedMap = new HashMap<>(); - combindedMap.putAll(aiInput.getInputData()); - combindedMap.putAll(aiOutput.getOutputData()); - return combindedMap; - } - - protected void validateOutputs(Map outputMap) { - Set missingKeys = new HashSet<>(getOutputKeys()); - missingKeys.removeAll(outputMap.keySet()); - if (!missingKeys.isEmpty()) { - throw new IllegalArgumentException("Missing some output keys: " + missingKeys); - } - } - - protected void validateInputs(Map inputMap) { - Set missingKeys = new HashSet<>(getInputKeys()); - missingKeys.removeAll(inputMap.keySet()); - if (!missingKeys.isEmpty()) { - throw new IllegalArgumentException("Missing some input keys: " + missingKeys); - } - } - -} diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chain/AiInput.java b/spring-ai-core/src/main/java/org/springframework/ai/chain/AiInput.java deleted file mode 100644 index cffa88745..000000000 --- a/spring-ai-core/src/main/java/org/springframework/ai/chain/AiInput.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.springframework.ai.chain; - -import java.util.Map; - -public class AiInput { - - private Map inputData; - - public AiInput(Map inputData) { - this.inputData = inputData; - } - - Map getInputData() { - return inputData; - } - -} diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chain/AiOutput.java b/spring-ai-core/src/main/java/org/springframework/ai/chain/AiOutput.java deleted file mode 100644 index 3a7039df1..000000000 --- a/spring-ai-core/src/main/java/org/springframework/ai/chain/AiOutput.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.springframework.ai.chain; - -import java.util.Map; - -public class AiOutput { - - private final Map outputData; - - public AiOutput(Map outputData) { - this.outputData = outputData; - } - - Map getOutputData() { - return this.outputData; - } - -} diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chain/Chain.java b/spring-ai-core/src/main/java/org/springframework/ai/chain/Chain.java deleted file mode 100644 index 68cf34d97..000000000 --- a/spring-ai-core/src/main/java/org/springframework/ai/chain/Chain.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.springframework.ai.chain; - -import java.util.List; -import java.util.Map; -import java.util.function.Function; - -public interface Chain extends Function { - - List getInputKeys(); - - List getOutputKeys(); - - AiInput preProcess(AiInput aiInput); - - Map postProcess(AiInput aiInput, AiOutput aiOutput); - -} diff --git a/spring-ai-core/src/main/java/org/springframework/ai/memory/Memory.java b/spring-ai-core/src/main/java/org/springframework/ai/memory/Memory.java deleted file mode 100644 index ff6cd69ae..000000000 --- a/spring-ai-core/src/main/java/org/springframework/ai/memory/Memory.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.springframework.ai.memory; - -import java.util.List; -import java.util.Map; - -public interface Memory { - - /** - * The keys that the memory will add to Chain inputs - */ - List getKeys(); - - /** - * Return key-value pairs given the text input to the chain - * @param inputs input of the chain - * @return key-value pairs from memory - */ - Map load(Map inputs); - - void save(Map inputs, Map outputs); - -} diff --git a/spring-ai-core/src/test/java/org/springframework/ai/chain/ChainTests.java b/spring-ai-core/src/test/java/org/springframework/ai/chain/ChainTests.java deleted file mode 100644 index 8e4d70b76..000000000 --- a/spring-ai-core/src/test/java/org/springframework/ai/chain/ChainTests.java +++ /dev/null @@ -1,88 +0,0 @@ -package org.springframework.ai.chain; - -import org.junit.jupiter.api.Test; -import org.springframework.ai.memory.Memory; - -import java.util.List; -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; - -class ChainTests { - - @Test - void badInputs() { - Chain chain = new FakeChain(); - AiInput aiInput = new AiInput(Map.of("foobar", "baz")); - assertThatThrownBy(() -> chain.apply(aiInput)).isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("Missing some input keys"); - } - - @Test - void correctInputs() { - Chain chain = new FakeChain(); - AiInput aiInput = new AiInput(Map.of("foo", "bar")); - AiOutput aiOutput = chain.apply(aiInput); - assertThat(aiOutput.getOutputData()).containsEntry("foo", "bar").containsEntry("bar", "baz"); - } - - class FakeChain extends AbstractChain { - - private boolean beCorrect = true; - - private List inputKeys = List.of("foo"); - - public FakeChain() { - } - - public FakeChain(boolean beCorrect) { - this.beCorrect = beCorrect; - } - - public FakeChain(List inputKeys) { - this.inputKeys = inputKeys; - } - - @Override - public List getInputKeys() { - return this.inputKeys; - } - - @Override - public List getOutputKeys() { - return List.of("bar"); - } - - @Override - protected AiOutput doApply(AiInput aiInput) { - if (beCorrect) { - return new AiOutput(Map.of("bar", "baz")); - } - else { - return new AiOutput(Map.of("baz", "bar")); - } - } - - } - - class FakeMemory implements Memory { - - @Override - public List getKeys() { - return List.of("baz"); - } - - @Override - public Map load(Map inputs) { - return Map.of("baz", "foo"); - } - - @Override - public void save(Map inputs, Map outputs) { - - } - - } - -} diff --git a/spring-ai-docs/concepts-staging.adoc b/spring-ai-docs/concepts-staging.adoc index 37693931c..5824533e9 100644 --- a/spring-ai-docs/concepts-staging.adoc +++ b/spring-ai-docs/concepts-staging.adoc @@ -56,13 +56,6 @@ Output parsing employs meticulously crafted prompts, often necessitating multipl This challenge has prompted OpenAI to introduce 'OpenAI Functions' as a means to specify the desired output format from the model precisely. -== Chaining Calls - -A Chain is a concept that represents a series of calls to an AI model. -It uses the output from one call as the input to another. - -By chaining calls together, you can support complex use cases by composing pipelines of multiple chains. - == Customizing Models: Integrating Your Data How can you equip the AI model with information it hasn't been trained on? diff --git a/spring-ai-openai/src/test/java/org/springframework/ai/openai/acme/AcmeIT.java b/spring-ai-openai/src/test/java/org/springframework/ai/openai/acme/AcmeIT.java index 5a56d4458..0cf980b3f 100644 --- a/spring-ai-openai/src/test/java/org/springframework/ai/openai/acme/AcmeIT.java +++ b/spring-ai-openai/src/test/java/org/springframework/ai/openai/acme/AcmeIT.java @@ -73,7 +73,6 @@ public class AcmeIT extends AbstractIT { // Now user query - // This will be wrapped up in a chain VectorStoreRetriever vectorStoreRetriever = new VectorStoreRetriever(vectorStore); logger.info("Retrieving relevant documents");