Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Spring AI Oracle Chat Memory Repository

[Chat Memory Documentation](https://docs.spring.io/spring-ai/reference/api/chat-memory.html#_chat_memory)

## Test Notes

### Use Existing Oracle Database

If `ORACLE_DATABASE_URL` is set, tests use that database instead of Testcontainers.

Optional credentials:

- `ORACLE_DATABASE_USERNAME`
- `ORACLE_DATABASE_PASSWORD`

### Rancher Desktop + Testcontainers

With Rancher Desktop, Ryuk can fail to start unless you set:

- `TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock`

Example:

```bash
TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock \
mvn -pl memory/repository/spring-ai-model-chat-memory-repository-oracle \
-Denforcer.skip=true \
-Dmaven.build.cache.enabled=false \
-Dtest=OracleChatMemoryRepositoryIT test
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-parent</artifactId>
<version>2.0.0-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>

<artifactId>spring-ai-model-chat-memory-repository-oracle</artifactId>
<name>Spring AI Oracle Chat Memory</name>
<description>Spring AI Oracle Chat Memory implementation</description>

<url>https://github.com/spring-projects/spring-ai</url>

<scm>
<url>https://github.com/spring-projects/spring-ai</url>
<connection>scm:git:git://github.com/spring-projects/spring-ai.git</connection>
<developerConnection>scm:git:ssh://git@github.com/spring-projects/spring-ai.git</developerConnection>
</scm>

<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-model-chat-memory-repository-jdbc</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-oracle-free</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc11</artifactId>
<version>23.4.0.24.05</version>
<scope>test</scope>
<optional>true</optional>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
* Copyright 2023-present 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.chat.memory.repository.oracle;

import java.util.List;

import javax.sql.DataSource;

import org.jspecify.annotations.Nullable;

import org.springframework.ai.chat.memory.ChatMemoryRepository;
import org.springframework.ai.chat.memory.repository.jdbc.JdbcChatMemoryRepository;
import org.springframework.ai.chat.messages.Message;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.util.Assert;

/**
* An implementation of {@link ChatMemoryRepository} for Oracle.
*
* @since 2.0.0
*/
public final class OracleChatMemoryRepository implements ChatMemoryRepository {

/** Delegate repository implementation. */
private final JdbcChatMemoryRepository delegate;

private OracleChatMemoryRepository(final JdbcChatMemoryRepository delegateRepository) {
this.delegate = delegateRepository;
}

@Override
public List<String> findConversationIds() {
return this.delegate.findConversationIds();
}

@Override
public List<Message> findByConversationId(final String conversationId) {
return this.delegate.findByConversationId(conversationId);
}

@Override
public void saveAll(final String conversationId, final List<Message> messages) {
this.delegate.saveAll(conversationId, messages);
}

@Override
public void deleteByConversationId(final String conversationId) {
this.delegate.deleteByConversationId(conversationId);
}

/**
* Create a builder for {@link OracleChatMemoryRepository}.
* @return a new builder
*/
public static Builder builder() {
return new Builder();
}

/**
* Builder for {@link OracleChatMemoryRepository}.
*/
public static final class Builder {

private static final String DEFAULT_TABLE_NAME = "SPRING_AI_CHAT_MEMORY";

/** Data source used by the underlying JDBC repository. */
private @Nullable DataSource dataSource;

/** JDBC template used by the underlying JDBC repository. */
private @Nullable JdbcTemplate jdbcTemplate;

/** Optional transaction manager used by the repository. */
private @Nullable PlatformTransactionManager transactionManager;

/** Table name used by the repository. */
private String tableName = DEFAULT_TABLE_NAME;

private Builder() {
}

/**
* Set the data source.
* @param dataSource data source to use
* @return this builder
*/
public Builder dataSource(final DataSource dataSource) {
this.dataSource = dataSource;
return this;
}

/**
* Set the JDBC template.
* @param jdbcTemplate JDBC template to use
* @return this builder
*/
public Builder jdbcTemplate(final JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
return this;
}

/**
* Set the transaction manager.
* @param transactionManager transaction manager to use
* @return this builder
*/
public Builder transactionManager(final PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
return this;
}

/**
* Set the table name used to store chat memory.
* @param tableName table name to use
* @return this builder
*/
public Builder tableName(final String tableName) {
Assert.hasText(tableName, "tableName cannot be null or empty");
this.tableName = tableName;
return this;
}

/**
* Build the Oracle chat memory repository.
* @return a new Oracle chat memory repository
*/
public OracleChatMemoryRepository build() {
var dialect = new OracleRepositoryDialect(this.tableName);
var repository = JdbcChatMemoryRepository.builder().dialect(dialect);

if (this.dataSource != null) {
repository.dataSource(this.dataSource);
}

if (this.jdbcTemplate != null) {
repository.jdbcTemplate(this.jdbcTemplate);
}

if (this.transactionManager != null) {
repository.transactionManager(this.transactionManager);
}

return new OracleChatMemoryRepository(repository.build());
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright 2023-present 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.chat.memory.repository.oracle;

import java.util.regex.Pattern;

import org.springframework.ai.chat.memory.repository.jdbc.JdbcChatMemoryRepositoryDialect;
import org.springframework.util.Assert;

/**
* Oracle SQL dialect for chat memory repository.
*
* @since 2.0.0
*/
final class OracleRepositoryDialect implements JdbcChatMemoryRepositoryDialect {

private static final Pattern TABLE_NAME_PATTERN = Pattern
.compile("^[A-Za-z][A-Za-z0-9_$#]*(\\.[A-Za-z][A-Za-z0-9_$#]*)?$");

private final String tableName;

OracleRepositoryDialect(final String tableName) {
Assert.hasText(tableName, "tableName cannot be null or empty");
Assert.isTrue(TABLE_NAME_PATTERN.matcher(tableName).matches(),
"tableName must be an Oracle identifier or schema-qualified identifier");
this.tableName = tableName;
}

@Override
public String getSelectMessagesSql() {
return "SELECT content, type FROM " + this.tableName + " WHERE CONVERSATION_ID = ? ORDER BY \"TIMESTAMP\"";
}

@Override
public String getInsertMessageSql() {
return "INSERT INTO " + this.tableName + " (CONVERSATION_ID, CONTENT, TYPE, \"TIMESTAMP\") VALUES (?, ?, ?, ?)";
}

@Override
public String getSelectConversationIdsSql() {
return "SELECT DISTINCT conversation_id FROM " + this.tableName;
}

@Override
public String getDeleteMessagesSql() {
return "DELETE FROM " + this.tableName + " WHERE CONVERSATION_ID = ?";
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2023-present 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.
*/

/**
* Oracle chat memory repository support.
*/
@NullMarked
package org.springframework.ai.chat.memory.repository.oracle;

import org.jspecify.annotations.NullMarked;
Loading
Loading