spring-projects / spring-ai Goto Github PK
View Code? Open in Web Editor NEWAn Application Framework for AI Engineering
Home Page: https://docs.spring.io/spring-ai/reference/1.0-SNAPSHOT/index.html
License: Apache License 2.0
An Application Framework for AI Engineering
Home Page: https://docs.spring.io/spring-ai/reference/1.0-SNAPSHOT/index.html
License: Apache License 2.0
Hi,
I have an improvement for OllamaAutoConfigurationIT which can help when working locally. The current issue as described in the test is the model is around 3GB which can take quite a while depending on internet speed.
My approach using the Testcontainers lifecycle:
First test execution
Second test execution
If there is interest on this, I can provide the PR.
It would bring a lot of convenience to have Boot autoconfigurations for the various Vector Store implementations.
I would make it easier to bootstrap with just property configurations .
There is an analogy to make between GenerationTemplate and JdbcTemplate that will help simplify the use of the lower level classes such as AiClient, Prompts, and OutputParsers. Note: AiClient should likely be renamed GenerationClient as it is focused on generation, specifcally text, so perhaps even TextGenerationClient as we haven't yet experimented with multi-modal apis.
DataSource <-> AiClient
String Query with placeholdres <-> Prompt with placeholders
RowMapper <-> OutputParser
The simplest method (which is a default method in AiClient) is string input and string output, that could be removed from the AiClient interface and used in GenerateTemplate instead. Here are a few sample signatures for consideration.
public interface GenerateOperations {
String generate(String message);
String generate(String message, Map<String, Object> model)
String generate(PromptTemplate promptTemplate, Map<String, Object> model);
<T> T generate(String message, Class<T> elementType, Map<String, Object> model);
<T> T generate(String message, OutputParser<T> parser, Map<String, Object> model);
<T> List<T> generateList(String message, Class<T> elementType, Map<String, Object> model);
<T> List<T> generateList(String message, OutputParser<T> parser, Map<String, Object> model);
AiResponse generateResponse(PromptTemplate promptTemplate, Map<String, Object> model);
}
Example usage
ActorsFilms actorsFilms = generateTemplate.generate("Generate the filmography for the actor {actor}",
ActorsFilms.class, Map.of("actor", "Tom Hanks"));
Simple "chains" for flows can be done with standard Java functional programming, and we can see how our needs for a "chain" or a "flow" evolve over time. e.g. the example of a chain from langchain using functional programming
@Test
void functionalChains() {
Function<String, String> combinedFunction = generateSynopsis.andThen(generateReview);
System.out.println(combinedFunction.apply("Tragedy at sunset on the beach"));
}
private Function<String, String> generateSynopsis = title -> {
String synopsisInput = """
You are a playwright. Given the title of play, it is your job to write a synopsis for that title.
Title: {title}
Playwright: This is a synopsis for the above play:""";
return generateTemplate.generate(synopsisInput, Map.of("title", title));
};
private Function<String, String> generateReview = synopsis -> {
String synopsisInput = """
You are a play critic from the New York Times. Given the synopsis of play, it is your job to write a review for that play.
Play Synopsis:
{synopsis}
Review from a New York Times play critic of the above play:""";
return generateTemplate.generate(synopsisInput, Map.of("synopsis", synopsis));
};
Most of the vector store implementations provide, usually proprietary, capabilities for embedding search with metadata filters.
PineconeVectorStore
implementation maps the document metadata to the native Pinecone json metadata format.@@
). The PgVectorSore implementation uses jsonb column to store the document's metadata and can leverage this functionality.Ref:
[1] https://www.pinecone.io/learn/vector-search-filtering/
The AiClient, EmbeddingClient or VectorStore clients interaction with their remote service endpoints could suffer from transient errors such as a momentary network glitch or rate limitation errors.
Often, those communication issues are resolvable by repetitive service invocation or altering the invocation rate.
We should provide a retry-decorators that automatically re-invoke the failed operations according to pre-configured retry policies.
Currently the VectorStore
interface offers at least 5 variations of the similaritySearch()
method, each with different set of arguments.
We should collapse all methods into a single similaritySearch(SearchRequest req)
using a convenient SearchRequest
builder like:
vectorStore.similaritySearch(
SearchRequest.query("Hello World")
.withTopK(5)
.withSimilarityThreshold(0.6)
.withFilterExpression("country == 'UK"));
Allow using Postgres+PGVector as VectorStore
PGVector is Open-source vector similarity search for Postgres.
It comes with pgvector-java support.
Please do a quick search on Github issues first, the feature you are about to request might have already been requested.
Expected Behavior
It would be nice to add support for AWS Bedrock.
Current Behavior
No support yet
Context
In general AWS Bedrock will probably be relevant since a lot of people are on AWS and will probably not migrate everything over.
I could pitch in time to make it happen.
As a first pitch I would probably start with inference
The spring-ai-test is meant to help evaluating generative responses using the the LLM itself.
But is it stands at the moment it is not obvious how to make use of it.
There is a method String getMessageTypeValue();
which is not needed. This was used in the implementation of the Microsoft ACME fitness store and is no longer needed.
Consider making the interface generic, interface Message<T> { T getContent(); ... }
A quick spike using Whisper or DallE etc would show if this class is usable in the context of other types of content.
Also review if there is a need for Map<String, Object> getProperties();
It doesn't seem necessary and is likely just a hold over from the initial work to create the org.springframework.ai.prompt.messages
when modifying the langhchain design.
Also review the naming of MessageType
- perhaps Role
is more succinct.
Current Version of the Spring AI allows InMemoryVectorStore but RedisVectorStore is expected along with.
Expected Behavior
Redis should be able to use as VectorStore
Current Behavior
InMemoryVectorStore is supported.
Context
I am working on document querying use-case with azure openai embedding stored in Redis. So i request the team to implement Redis as Vector Store
The AiResponse
and Generation
types are fairly common across different AI providers, AFAICT. Although, where we refer to what the AI returns as a Generation
, others refer to this as a Completion
, or based on use, a Choice
.
Regardless, like Prompt
, I think that AiResponse
and Generation
warrant their own top-level package, such as org.springframework.ai.generation
, or org.springframework.ai.response
.
Food for thought.
The use of Supplier/Consumer was found to be limiting in a few scenarios. In the case of loading documents from a Resource, the use of a Supplier made the class stateful, meaning a new class instance was needed per Resource. Instead using a Functional interface so that the Resource is the input makes it easier to process many files.
So move from
interface DocumentReader extends Supplier<List<Document>>
to
interface DocumentReader<I> extends Function<I, List<Document>>
and have an implementation that takes a Spring Resource as input:
public class ResourceNewDocumentReader implements NewDocumentReader<Resource> {
@Override
public List<Document> apply(Resource resource) {
...
}
}
The case of consumer is a bit less clear, perhaps the current DocumentWriter can still exist, but we could have our current implementations that write to the vector store implement DocumentTransformer
so that if we wanted to chain together writin to multiple vector stores, the output from one could be changed to another. Maybe DocumentProcessor
would be better then instead of DocumentTransformer
Using Void
in a function instead of consumer is also an option, though it looks a little odd now that I write it out.
public interface DocumentWriter extends Consumer<List<Document>>
to
public interface DocumentCreator<O> extends Function<List<Document>, O> {
}
and
public class DocumentWriter implements DocumentCreator<Void>{
@Override
public Void apply(List<Document> documents) {
return null;
}
}
Please do a quick search on Github issues first, there might be already a duplicate issue for the one you are about to create.
If the bug is trivial, just go ahead and create the issue. Otherwise, please take a few moments and fill in the following sections:
Environment
Please provide as many details as possible: Spring AI version, Java version, which vector store you use if any, etc
Steps to reproduce
Steps to reproduce the issue.
Expected behavior
A clear and concise description of what you expected to happen.
Minimal Complete Reproducible example
Please provide a failing test or a minimal complete verifiable example that reproduces the issue.
Bug reports that are reproducible will take priority in resolution over reports that are not reproducible.
Azure Cognitive Search provides value add capabilities to users of Azure. In addition to supporting easy document upload and
indexing, indexes that are configured with Vector storage could be integrated and consumed via a VectorStore implementation in Spring AI.
Expected Behavior
Azure Cognitive Search supports a Java search API for uploading, deleting, and searching documents using vector based queries and storage. This API could be wrapped in a Spring AI Vector Store implementation giving Spring AI and Azure users the ability to integrate their existing Cognitive Search indexes with Spring AI.
Conceptually, a user would create a SearchClient instance using existing methods and inject the instance along with an EmbeddingClient into a VectorStore implementation. Using bean creation methods and a PropertiesSource class, the VectorStore creation could look like the following
@Bean
public SearchClient searchClient(AzureSearchClientProperties props) {
return new SearchClientBuilder().endpoint(props.getEndpoint())
.credential(new AzureKeyCredential(props.getApiKey()))
.indexName(props.getIndex())
.buildClient();
}
@Bean
public VectorStore vectorStore(SearchClient searchClient, EmbeddingClient embeddingClient) {
return new AzureCognitiveSearchVectorStore(searchClient, embeddingClient);
}
Current Behavior
Spring AI currently does not support Azure Cognitive Search as a VectorStore implementation.
Context
An Azure Spring sample project currently exists that
implements a Cognitive Search based VectorStore. In addition, the Microsoft AI learning pages also demonstrate sample code
that utilizes existing documents already uploaded and indexed into CognitiveSearch. It would be desirable for Spring AI to integrate with Azure Cognitive Search as another VectorStore implementation.
Current Version of the Spring AI allows InMemoryVectorStore but Neo4jVectorStore is expected along with it.
Expected Behavior
Neo4j should be able to use as VectorStore
Current Behavior
InMemoryVectorStore is supported.
Context
I am working on content writer suggestions use-case with azure openai embedding stored in Neo4j. So i request the team to implement Neo4j as Vector Store
Expected Behavior
I want to be able to provide Spring AI with one or more functions that could be used to provide additional information or functionality. (See https://platform.openai.com/docs/guides/gpt/function-calling). Moreover, when the response to a prompt indicates that a function should be called, I'd like it if that decision, the call to the function, and the reissuing of a new prompt were all handled internally by Spring AI. This behavior is what I believe is commonly referred to as an "agent".
In short, I'd like to be able to configure Spring AI with one or more functions (perhaps as lambdas or method references) that could be used later. Then, if I ask a question that the LLM needs more information to answer, Spring AI would call those functions for me and resubmit the prompt. I don't want to have to do what is shown as "step 2" in the example at https://platform.openai.com/docs/guides/gpt/function-calling. That decision making should be handled internally by Spring AI.
My primary desire is to have function support, but almost as importantly, I want to not have to deal with the back and forth interactions in my own code. I want Spring AI to handle that internally.
Current Behavior
Spring AI does not yet support functions, so at this point there's no need for agent behavior. Currently Spring AI only handles one-turn Q&A type interactions.
Context
As an example (drawn from the aforementioned documentation), if I were to ask what the weather is in Boston, most/all LLMs wouldn't know that answer because they're not trained on real-time or even relatively current data. But if I provide a function that can lookup the weather in a city, then instead of the LLM responding that it doesn't know what the weather is, it could respond with an instruction to call the function. The application (or the agent on behalf of the application) would call that function as instructed, then submit a new prompt with weather data from which the LLM would be able to generate the desired answer.
Please add support Weaviate as a vector store for existing vector dbs list.
It would be nice to support PostgresML embeddings (https://postgresml.org/docs/guides/transformers/embeddings) as an EmbeddingClient
that doesn't require OpenAI/Azure OpenAI account
Bug description
ParagraphPdfDocumentReader causing NullPointerException when reading sample1.pdf
https://github.com/spring-projects-experimental/spring-ai/blob/main/document-readers/pdf-reader/src/test/resources/sample1.pdf
Environment
Spring Boot version: 3.1.4
Spring AI version: 0.7.0-SNAPSHOT
Java version: openjdk version "17.0.2" 2022-01-18
Steps to reproduce
Add dependency spring-ai-pdf-document-reader: 0.7.0-SNAPSHOT version to pom.xml
`
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-pdf-document-reader</artifactId>
<version>0.7.0-SNAPSHOT</version>
</dependency>
`
Code to read paragraphs:
`
var documents = pdfReader.get();
ParagraphPdfDocumentReader pdfReader = new ParagraphPdfDocumentReader(
"file:\\C:\\Users\\test\\sample1.pdf",
PdfDocumentReaderConfig.builder()
.build());
var documents = pdfReader.get();
for (Document document : documents) {
System.out.println(document.getContent());
}
`
Expected behavior
It should read each paragraph from the sample1.pdf file
Exception
`
java.lang.NullPointerException: Cannot invoke "org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineNode.getFirstChild()" because "bookmark" is null
at org.springframework.ai.reader.pdf.config.ParagraphManager.generateParagraphs(ParagraphManager.java:131) ~[spring-ai-pdf-document-reader-0.7.0-20231019.142632-5.jar:0.7.0-SNAPSHOT]
at org.springframework.ai.reader.pdf.config.ParagraphManager.<init>(ParagraphManager.java:82) ~[spring-ai-pdf-document-reader-0.7.0-20231019.142632-5.jar:0.7.0-SNAPSHOT]
at org.springframework.ai.reader.pdf.ParagraphPdfDocumentReader.<init>(ParagraphPdfDocumentReader.java:109) ~[spring-ai-pdf-document-reader-0.7.0-20231019.142632-5.jar:0.7.0-SNAPSHOT]
at org.springframework.ai.reader.pdf.ParagraphPdfDocumentReader.<init>(ParagraphPdfDocumentReader.java:92) ~[spring-ai-pdf-document-reader-0.7.0-20231019.142632-5.jar:0.7.0-SNAPSHOT]
`
It would be helpful to add additional descriptions to the JSON schema for better prompt results. The jsonschema-generator used by the BeanOutputParser
could also add the jsonschema-generator/jsonschema-module-jackson
module to enable @JsonPropertyDescription
annotations on fields.
The generative AI client implementations are modules sitting at the top of the project, e.g. spring-ai-ollama
. Create a directory, generative-clients
and put the various client modules in that directory
Chroma is a popular open-source embedding database. It gives you the tools to store document embeddings, content and metadata and to search through those embeddings including metadata filtering.
The project will move to 0.8.0-SNAPSHOT and have breaking changes. For people who want to continue to use 0.7.1-SNAPSHOT until that 0.8.0 release, these docs will still be available, but in another location since our use of antora can't yet support having two snapshot versions listed simultaneously.
The chain idea from langchain involves 'workflow' and often type unsafe access to input and output. Instead of following down that path, first extract the key helper classes that are needed for use cases and later revisit how they can be composed into a chain/flow. There is some example usage of 'chaining' based on langchain's example using java.util.Function in #108
Helper classes envisioned are GenerationTemplate (similar to LLMChain) and ChatEngine (similar to conversationalchain)
The current implementation is focused on text/code based GenerativeAI use cases as input and output. The name AiClient
, is likely too broad. There isn't yet a ton of experience with multi-modal language models and such a generic name might be better suited in that use case.
Consider to rename AiClient
. Here are some things to consider.
AiClient
to GenerativeClient
and perhaps the package name to org.sf.ai.client.generative
Generation
an interface with a generic type.Expected Behavior
Lanchain's Parent Document Retriever feature is expected in Spring AI
Current Behavior
Files are chunked and cretead embeddings for them. We would like to have child chunks for parent chunks of the document.
Apologies for creating an issue to only ask a question, but I don't see the discussions enabled for this repository.
Why don't you use Azure client library instead of theokanning client? Did you consider it?
It provides sync and async clients. And it's very easy to switch from Azure API to Open AI API
I just noticed when writing the README for the Neo4j store that the sentence in the PgVector store is a little bit vague.
Yes, it's true that the document will be there but it is either to expect that it is the first in the collection or we should set the "top-k amount" to 1 in the example snippet, or?
A little bit tweaked in the Neo4j store README:
https://github.com/spring-projects-experimental/spring-ai/blob/0a584f0dc2483ef9ad0741c8598998e489b64726/vector-stores/spring-ai-neo4j-store/README.md?plain=1#L114-L118
为什么不用milvus作为向量数据库呢?
Most Spring-ai vector store implementations already have a proper boot auto-configurations in place.
Create the corresponding Spring Boot Starters under the spring-ai-spring-boot-starters and update the VectorStore's README.md
with both starter usage instructions.
Bug description
spring-ai-azure-openai-spring-boot-starter reports issue about DeploymentNotFound ( it requires Azure model deployment name)
with properties included
spring.ai.azure.openai.api-key= spring.ai.azure.openai.endpoint=https://abc.openai.azure.com/ spring.ai.azure.openai.embedding-model=text-embedding-ada-002
com.azure.core.exception.ResourceNotFoundException: Status code 404, "{"error":{"code":"DeploymentNotFound", "message":"The API deployment for this resource does not exist. If you created the deployment within the last 5 minutes, please wait a moment and try again."}}" at com.azure.core.implementation.http.rest.RestProxyBase.instantiateUnexpectedException(RestProxyBase.java:347) ~[azure-core-1.41.0.jar:1.41.0] at com.azure.core.implementation.http.rest.SyncRestProxy.ensureExpectedStatus(SyncRestProxy.java:130) ~[azure-core-1.41.0.jar:1.41.0] at com.azure.core.implementation.http.rest.SyncRestProxy.handleRestReturnType(SyncRestProxy.java:213) ~[azure-core-1.41.0.jar:1.41.0] at com.azure.core.implementation.http.rest.SyncRestProxy.invoke(SyncRestProxy.java:81) ~[azure-core-1.41.0.jar:1.41.0] at com.azure.core.implementation.http.rest.RestProxyBase.invoke(RestProxyBase.java:109) ~[azure-core-1.41.0.jar:1.41.0] at com.azure.core.http.rest.RestProxy.invoke(RestProxy.java:91) ~[azure-core-1.41.0.jar:1.41.0] at jdk.proxy2/jdk.proxy2.$Proxy75.getChatCompletionsSync(Unknown Source) ~[na:na] at com.azure.ai.openai.implementation.OpenAIClientImpl.getChatCompletionsWithResponse(OpenAIClientImpl.java:897) ~[azure-ai-openai-1.0.0-beta.3.jar:1.0.0-beta.3] at com.azure.ai.openai.OpenAIClient.getChatCompletionsWithResponse(OpenAIClient.java:294) ~[azure-ai-openai-1.0.0-beta.3.jar:1.0.0-beta.3] at com.azure.ai.openai.OpenAIClient.getChatCompletions(OpenAIClient.java:430) ~[azure-ai-openai-1.0.0-beta.3.jar:1.0.0-beta.3] at org.springframework.ai.azure.openai.client.AzureOpenAiClient.generate(AzureOpenAiClient.java:60) ~[spring-ai-azure-openai-0.2.0-20230908.193706-31.jar:0.2.0-SNAPSHOT] at org.springframework.ai.openai.samples.helloworld.simple.SimpleAiController.completion(SimpleAiController.java:21) ~[classes/:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na] at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-6.0.11.jar:6.0.11] at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150) ~[spring-web-6.0.11.jar:6.0.11] at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) ~[spring-webmvc-6.0.11.jar:6.0.11] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:884) ~[spring-webmvc-6.0.11.jar:6.0.11] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797) ~[spring-webmvc-6.0.11.jar:6.0.11] at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-6.0.11.jar:6.0.11] at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1081) ~[spring-webmvc-6.0.11.jar:6.0.11] at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:974) ~[spring-webmvc-6.0.11.jar:6.0.11] at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1011) ~[spring-webmvc-6.0.11.jar:6.0.11] at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) ~[spring-webmvc-6.0.11.jar:6.0.11] at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) ~[tomcat-embed-core-10.1.11.jar:6.0] at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[spring-webmvc-6.0.11.jar:6.0.11] at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[tomcat-embed-core-10.1.11.jar:6.0] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-10.1.11.jar:10.1.11] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.0.11.jar:6.0.11] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.11.jar:6.0.11] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-6.0.11.jar:6.0.11] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.11.jar:6.0.11] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:109) ~[spring-web-6.0.11.jar:6.0.11] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.11.jar:6.0.11] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-6.0.11.jar:6.0.11] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.11.jar:6.0.11] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:166) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:341) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:894) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1740) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-10.1.11.jar:10.1.11] at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
Environment
spring-ai-azure-openai-spring-boot-starter : 0.2.0-SNAPSHOT
spring boot: 3.1.2
Steps to reproduce
Just run the application and access the endpoint /ai/sample
Expected behavior
It has to connect to Azure OpenAI and get the response.
Minimal Complete Reproducible example
https://github.com/rd-1-2022/ai-azure-openai-helloworld.git
Context
Applications using AI often use a UI that generates text little by little in a stream instead of outputting texts all at once. The current spring-ai AiClient
does not support stream output.
Expected Behavior
I hope that generateStream
method will be added to AiClient
. As for the return value, Flux<AiResponse>
is promising, but if you want to avoid dependence on reactor, you can also consider the JDK standard Flow.Publisher<AiResponse>
.
For example, openai-java used in OpenAiClient
uses Flowable
. This can be converted to Flux
or Flow.Publisher
.
This is a little off topic, but openai-java uses outdated RxJava2 and has an unnecessary number of dependencies (e.g. kotlin sdk). Since spring-ai does not use all OpenAI APIs, I think it is better to have an in-house OpenAI client that implements only the minimum API with WebClient
that supports the streaming.
Neo4j added vector support in August 2023 based on HNSW, we'd like to add support for that to spring-ai
Just wanted to open an issue first before sending in a PR, to see if there are any thoughts.
Currently the Document
if not provided with an explicit ID
, generates a random UUID for every document.
Even if the document content/metadata haven't changed a new ID is generated every time.
This will lead to document content duplications in the Vector store.
To prevent this type of unnecessary duplications we can allow generation of Document ID based on the hashed document content+metadata.
Following snippet is inspired by a langchain4j vector store implementations.
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
....
public static String generateIdFrom(String contentWithMetadata) {
try {
byte[] hashBytes = MessageDigest.getInstance("SHA-256").digest(contentWithMetadata.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
for (byte b : hashBytes) {
sb.append(String.format("%02x", b));
}
return UUID.nameUUIDFromBytes(sb.toString().getBytes(StandardCharsets.UTF_8)).toString();
}
catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException(e);
}
}
This would be akin to llamindex chatengine as well as what was done in canopy from pinecone.
This is the alternative to following the design in langchain which adds on workflow/chain elements that at the moment is too premature (in my mind) to delve into and can be added later. Functional programming chain in Java can also be useful in this context, will wait and see how it evolved.
Our local OpenAI endpoint has a very small default max_tokens against the chat completion.
We like to make it adjustable
Expected Behavior
Hoping this code.
Looks something like this
private ChatCompletionRequest getChatCompletionRequest(String text) {
List<ChatMessage> chatMessages = List.of(new ChatMessage("user", text));
logger.trace("ChatMessages: ", chatMessages);
ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
.model(this.model)
.temperature(this.temperature)
.messages(List.of(new ChatMessage("user", text)))
.maxTokens(4000) /// ADD HERE
.build();
logger.trace("ChatCompletionRequest: ", chatCompletionRequest);
return chatCompletionRequest;
}
Current Behavior
max tokens not adjustable
Context
As far as I understood the JavaDoc of VectorStore
threshold is a lower bound.
The Postgres store uses the threshold as an upper limit to the distance, though.
In addition, the cosine distance must be inverted, see https://github.com/pgvector/pgvector#distances
I suggest fixing the comparison operator and also change the sql statement when using cosine.
This was tested with Spring AI 0.7.0-SNAPSHOT.
Once a PromptTemplate
has been created without model data and then used once via the render()
method that takes model data, it cannot be reused with different model data. Attempting to do so will result in a prompt that contains the original model data. For example, I would expect the following test to pass. But it does not as described in the comments.
@Test
public void testPromptTemplate() {
PromptTemplate pt = new PromptTemplate("Here is the input: {input}");
String output1 = pt.render(Map.of("input", "Hello, world!"));
Assertions.assertThat(output1).isEqualTo("Here is the input: Hello, world!");
String output2 = pt.render(Map.of("input", "Goodbye, world!"));
// This next assertion fails!!!
// The rendered output is the "Hello world" from the first time!!!
Assertions.assertThat(output2).isEqualTo("Here is the input: Goodbye, world!");
}
This is appears to be because of the if
statement at https://github.com/spring-projects-experimental/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/prompt/PromptTemplate.java#L141 that prevents new model data from being given to the ST
instance unless that key is null.
Note that I've tested the same thing with both the Python and Node.js implementations of PromptTemplate
from Langchain and they both work as I'd expect. That is, the PromptTemplate
can be reused with new model data. For the sake of clarity, here's what I did in Python:
from langchain.prompts import PromptTemplate
prompt_template = PromptTemplate.from_template(
"Here is the input: {input}"
)
output1 = prompt_template.format(input="Hello, world!")
print(output1)
# Here is the input: Hello, world!
output2 = prompt_template.format(input="Goodbye, world!")
print(output2)
# Here is the input: Goodbye, world!
And here is what I did in Node.js:
import { PromptTemplate } from "langchain/prompts";
const pt = PromptTemplate.fromTemplate('Here is the input: {input}');
const output1 = await pt.format({input: "Hello, world!"});
console.log(output1);
// Here is the input: Hello, world!
const output2 = await pt.format({input: "Goodbye, world!"});
console.log(output2);
// Here is the input: Goodbye, world!
In both cases, the result was (as expected):
Here is the input: Hello, world!
Here is the input: Goodbye, world!
I'd expect Spring AI's PromptTemplate
to be reusable in the same way.
Expected Behavior
The EmbeddingClient
interface should expose a method to report the dimension size of the configured embedding model.
Current Behavior
Currently the users must retrieve this information manually from the documentations of the used models.
Context
The dimensions are need to configure VectorStores and at the moment has to be specified explicitly.
Instead they could obtain this information directly from the provided EmbeddingClient instance.
Please do a quick search on Github issues first, the feature you are about to request might have already been requested.
Expected Behavior
I now want to use vpn to access gpt. However, it seems that we do not have this entrance yet. We hope that we can support a method similar to openAI-java sdk in the future.
Current Behavior
Github Markdown is a popular format that provides ready structural hints as to document content, e.g paragraph, table, etc and many document formats can be converted to markdown, for example pandoc can convert asciidoc to markdown which makes it easier to work with.
I've used https://github.com/commonmark/commonmark-java and the GH extension with success in spring-cli.
Current Version of the Spring AI allows JsonReader and TextReader.
Expected Behavior
More DocumentReader implementations should allow reading all kinds documents (PDF, word, html, ...)
Current Behavior
DocumentReader is implemented only for Json and Text content types.
Context
I am working on document querying use-case with azure openai to load all the documents and create embeddings to store them in Redis. So i request the team to implement DocumentLoader.
For example, the module spring-ai-postgresml-embedding-client
has the class PostgresMEmbeddingClient
in the package org.springframework.ai.embedding
and transformers-embedding
module has TransformersEmbeddingClient
in the same package org.springframework.ai.embedding
When making this change, create a more specific name for the transformers-embedding
perhaps onnx-embedding-client
?
Also, there is a convention of having the artifact contain the prefix spring-ai
. The Postgres embedding clients doesn't follow this convention.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.