Skip to content

Conversation

leijendary
Copy link
Contributor

@leijendary leijendary commented Oct 11, 2024

An option to use a generic JdbcChatMemory with a starter for users that want to use SQL as chat memory.

@leijendary leijendary changed the title Added ChatMemory implementation for PgVector Add ChatMemory implementation for PgVector Oct 11, 2024
@leijendary leijendary changed the title Add ChatMemory implementation for PgVector Add PgVectorChatMemory Oct 11, 2024
@ogbozoyan
Copy link

Is it will be usable with VectorStoreChayMemoryAdvisor? Or what is the point ?

@leijendary
Copy link
Contributor Author

@ogbozoyan VectorStoreChatMemoryAdvisor is optional if we have this change. PgVectorChatMemory is an implementation of ChatMemory and the postgres version of CassandraChatMemory which is specialized just for the chat memory/history.

@ogbozoyan
Copy link

@leijendary which advisor you should use with that ChatMemory to process saving ?

@leijendary
Copy link
Contributor Author

@ogbozoyan You can use MessageChatMemoryAdvisor and pass in the instance of the PgVectorChatMemory. Then for RAG, you can use RetrievalAugmentationAdvisor. This is what I did for my chatClient bean:

@Bean
fun chatClient(builder: ChatClient.Builder, vectorStore: VectorStore, chatMemory: ChatMemory): ChatClient {
    val memoryAdvisor = MessageChatMemoryAdvisor(chatMemory)
    val documentRetriever = VectorStoreDocumentRetriever.builder()
        .vectorStore(vectorStore)
        .build()
    val ragAdvisor = RetrievalAugmentationAdvisor.builder()
        .documentRetriever(documentRetriever)
        .build()

    return builder
        .defaultAdvisors(memoryAdvisor, ragAdvisor)
        .build()
}

@ogbozoyan
Copy link

@ogbozoyan You can use MessageChatMemoryAdvisor and pass in the instance of the PgVectorChatMemory. Then for RAG, you can use RetrievalAugmentationAdvisor. This is what I did for my chatClient bean:

@Bean
fun chatClient(builder: ChatClient.Builder, vectorStore: VectorStore, chatMemory: ChatMemory): ChatClient {
    val memoryAdvisor = MessageChatMemoryAdvisor(chatMemory)
    val documentRetriever = VectorStoreDocumentRetriever.builder()
        .vectorStore(vectorStore)
        .build()
    val ragAdvisor = RetrievalAugmentationAdvisor.builder()
        .documentRetriever(documentRetriever)
        .build()

    return builder
        .defaultAdvisors(memoryAdvisor, ragAdvisor)
        .build()
}

in this example you passing VectorStore, may be you meant ?

fun chatClient(builder: ChatClient.Builder, **pgVectorChatMemory: PgVectorChatMemory**, chatMemory: ChatMemory): ChatClient {
     val memoryAdvisor = MessageChatMemoryAdvisor(**pgVectorChatMemory**)
     val documentRetriever = VectorStoreDocumentRetriever.builder()
         .vectorStore(vectorStore)
         .build()
     val ragAdvisor = RetrievalAugmentationAdvisor.builder()
         .documentRetriever(documentRetriever)
         .build()
 
     return builder
         .defaultAdvisors(memoryAdvisor, ragAdvisor)
         .build()
}

@leijendary
Copy link
Contributor Author

@ogbozoyan

@ogbozoyan You can use MessageChatMemoryAdvisor and pass in the instance of the PgVectorChatMemory. Then for RAG, you can use RetrievalAugmentationAdvisor. This is what I did for my chatClient bean:

@Bean
fun chatClient(builder: ChatClient.Builder, vectorStore: VectorStore, chatMemory: ChatMemory): ChatClient {
    val memoryAdvisor = MessageChatMemoryAdvisor(chatMemory)
    val documentRetriever = VectorStoreDocumentRetriever.builder()
        .vectorStore(vectorStore)
        .build()
    val ragAdvisor = RetrievalAugmentationAdvisor.builder()
        .documentRetriever(documentRetriever)
        .build()

    return builder
        .defaultAdvisors(memoryAdvisor, ragAdvisor)
        .build()
}

in this example you passing VectorStore, may be you meant ?

fun chatClient(builder: ChatClient.Builder, **pgVectorChatMemory: PgVectorChatMemory**, chatMemory: ChatMemory): ChatClient {
     val memoryAdvisor = MessageChatMemoryAdvisor(**pgVectorChatMemory**)
     val documentRetriever = VectorStoreDocumentRetriever.builder()
         .vectorStore(vectorStore)
         .build()
     val ragAdvisor = RetrievalAugmentationAdvisor.builder()
         .documentRetriever(documentRetriever)
         .build()
 
     return builder
         .defaultAdvisors(memoryAdvisor, ragAdvisor)
         .build()
}

The code I provided is correct. I am using it right now. The ChatMemory instance is automatically picked up by spring as PgVectorChatMemory because of PgVectorChatMemoryAutoConfiguration

@eddumelendez
Copy link
Contributor

I wonder if this should be more generic and instead providing a JdbcChatMemory instead. I would be also nice to provide the proper scripts to create the tables, similar to what spring security and spring session does.

@leijendary
Copy link
Contributor Author

@eddumelendez I agree. I also originally designed it to be the same as the existing CassandraChatMemory and just used the same columns. I thought of changing the columns to the following:

CREATE TABLE ai_chat_memory (
    session_id character varying(36) NOT NULL,
    content text NOT NULL,
    type character varying(10) NOT NULL, -- USER/ASSISTANT
    "timestamp" timestamp without time zone NOT NULL DEFAULT NOW()
);

Do you also mean that the table should not be automatically created and instead be created manually by the developer?

@eddumelendez
Copy link
Contributor

Do you also mean that the table should not be automatically created and instead be created manually by the developer?

it should be created automatically by detecting the database. I meant having something like https://github.com/spring-projects/spring-session/blob/main/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-postgresql.sql that spring ai can execute, so, if new database should be supported then adding a script should be enough

@ogbozoyan
Copy link

type character varying(10) NOT NULL, -- USER/ASSISTANT

maybe add check ?

CREATE TABLE ai_chat_memory (
    session_id character varying(36) NOT NULL,
    content text NOT NULL,
    type character varying(10) NOT NULL CHECK (type IN ('USER', 'ASSISTANT')), -- USER/ASSISTANT
    "timestamp" timestamp without time zone NOT NULL DEFAULT NOW()
);

@leijendary leijendary changed the title Add PgVectorChatMemory Add JdbcChatMemory Dec 13, 2024
@leijendary leijendary force-pushed the main branch 7 times, most recently from ac555b9 to af61678 Compare January 25, 2025 10:27
@markpollack markpollack added this to the 1.0.0-M7 milestone Mar 25, 2025
@sobychacko
Copy link
Contributor

@leijendary @ogbozoyan We are reviewing this PR currently. Could you rebase the changes against the latest main branch? Please keep in mind that we refactored auto-configuration and starters in a significant manner recently. See this blog for more details: https://spring.io/blog/2025/03/26/spring-ai-update-to-snapshots. The previous monolithic auto-configuration module has been removed in favor of dedicated auto-config modules. Can you adjust your PR for that? Also, I couldn't run your tests in the chat-memory module. I didn't try the autoconfig tests yet. Thanks!

@leijendary
Copy link
Contributor Author

@sobychacko I pushed the changes.

@@ -0,0 +1 @@
DROP TABLE IF EXISTS ai_chat_memory;
Copy link
Contributor

@sobychacko sobychacko Apr 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these SQL scripts necessary for the JDBC chat memory? I see they are used in the integration test, but it's unclear how a user would use it. The docs below don't mention them either. Could you clarify? At the very least, we need to inform the end user of the existence of these scripts. I see some of these scripts are used from the auto-configuration, but how would an end user that is not using auto configuration makes use of these scripts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the docs and specified that the chat memory table will be automatically created using autoconfiguration and how to disable it. I also removed the sql files that are not used.

);

CREATE INDEX IF NOT EXISTS ai_chat_memory_conversation_id_timestamp_idx
ON ai_chat_memory(conversation_id, `timestamp` DESC);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the docs and specified that the chat memory table will be automatically created using autoconfiguration and how to disable it. I also removed the sql files that are not used.

);

CREATE INDEX IF NOT EXISTS ai_chat_memory_conversation_id_timestamp_idx
ON ai_chat_memory(conversation_id, "timestamp" DESC);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the docs and specified that the chat memory table will be automatically created using autoconfiguration and how to disable it. I also removed the sql files that are not used.

@@ -0,0 +1 @@
DROP TABLE IF EXISTS ai_chat_memory;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is now removed. See comment above.

@sobychacko
Copy link
Contributor

sobychacko commented Apr 5, 2025

@leijendary I see that you are making some updates to the PR. I was in the process of making your PR conformant with the new structure we introduced in the latest main. Since you are already making changes, could you restructure your PR to be consistent with the latest main? For e.g. move the jdbc chat memory module under the top-level memory folder and rename the module to reflect the new structure etc. Please ping here if you can do that. Otherwise, we can take care of it on the merge. (If you end up making the changes, I would suggest squashing all your commits into a single one after rebasing to the latest main). Thanks!

Signed-off-by: leijendary <jonathanleijendekker@gmail.com>
@leijendary
Copy link
Contributor Author

@sobychacko I took care of it and migrated the folder structure based on the latest structure in main. Feel free to update it if I missed anything.

@sobychacko
Copy link
Contributor

@leijendary Thank you for the PR. It is now merged upstream via 4be1002.

@sobychacko sobychacko closed this Apr 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants