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

Comment From: ogbozoyan

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

Comment From: leijendary

@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.

Comment From: ogbozoyan

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

Comment From: leijendary

@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()
}

Comment From: 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:

```kotlin @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()
}

Comment From: leijendary

@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: ```kotlin @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 ?

```kotlin 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

Comment From: eddumelendez

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.

Comment From: leijendary

@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?

Comment From: eddumelendez

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

Comment From: ogbozoyan

sql 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()
);