Skip to content

Commit

Permalink
Merge pull request #384 from quarkiverse/#382
Browse files Browse the repository at this point in the history
Add way to disable chat memory
  • Loading branch information
cescoffier authored Mar 15, 2024
2 parents 0d4ee28 + 9e7592e commit fc62936
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,10 @@ public void findDeclarativeServices(CombinedIndexBuildItem indexBuildItem,
AnnotationValue chatMemoryProviderSupplierValue = instance.value("chatMemoryProviderSupplier");
if (chatMemoryProviderSupplierValue != null) {
chatMemoryProviderSupplierClassDotName = chatMemoryProviderSupplierValue.asClass().name();
if (!chatMemoryProviderSupplierClassDotName
if (chatMemoryProviderSupplierClassDotName.equals(
LangChain4jDotNames.NO_CHAT_MEMORY_PROVIDER_SUPPLIER)) {
chatMemoryProviderSupplierClassDotName = null;
} else if (!chatMemoryProviderSupplierClassDotName
.equals(LangChain4jDotNames.BEAN_CHAT_MEMORY_PROVIDER_SUPPLIER)) {
validateSupplierAndRegisterForReflection(chatMemoryProviderSupplierClassDotName, index,
reflectiveClassProducer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ public class LangChain4jDotNames {
static final DotName BEAN_CHAT_MEMORY_PROVIDER_SUPPLIER = DotName.createSimple(
RegisterAiService.BeanChatMemoryProviderSupplier.class);

static final DotName NO_CHAT_MEMORY_PROVIDER_SUPPLIER = DotName.createSimple(
RegisterAiService.NoChatMemoryProviderSupplier.class);

static final DotName RETRIEVER = DotName.createSimple(Retriever.class);
static final DotName NO_RETRIEVER = DotName.createSimple(
RegisterAiService.NoRetriever.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,17 @@ public ChatMemoryProvider get() {
}
}

/**
* Marker that is used when the user does not want any memory configured for the AiService
*/
final class NoChatMemoryProviderSupplier implements Supplier<ChatMemoryProvider> {

@Override
public ChatMemoryProvider get() {
throw new UnsupportedOperationException("should never be called");
}
}

/**
* Marker class to indicate that no retriever should be used
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ public void close() {
}

private void clearChatMemory() {
chatMemories.forEach(new BiConsumer<>() {
@Override
public void accept(Object memoryId, ChatMemory chatMemory) {
chatMemory.clear();
}
});
chatMemories = null;
if (chatMemories != null) {
chatMemories.forEach(new BiConsumer<>() {
@Override
public void accept(Object memoryId, ChatMemory chatMemory) {
chatMemory.clear();
}
});
chatMemories = null;
}
}

/**
Expand Down
5 changes: 5 additions & 0 deletions docs/modules/ROOT/pages/ai-services.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ However, in cases where more fine-grained control is needed (which is the case w
When using an AiService that is expected to use to chat memory, it is very important to use `@MemoryId` (as mentioned in a later section). Failure to do so, can lead to unexpected and hard to debug results.
====

[Tip]
====
If you use case requires that no memory should be used, then be sure to use `@RegisterAiService(chatMemoryProviderSupplier = RegisterAiService.NoChatMemoryProviderSupplier.class)`
====

=== Advanced usage

Although the extension's default `ChatMemoryProvider` is very configurable making unnecessary in most cases to resort to a custom implementation, such a capability is possible. Here is a possible example:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,44 @@ void should_keep_separate_chat_memory_for_each_user_in_store() throws IOExceptio
tuple(USER, secondsMessageFromSecondUser), tuple(AI, secondAiMessageToSecondUser));
}

@RegisterAiService(chatMemoryProviderSupplier = RegisterAiService.NoChatMemoryProviderSupplier.class)
interface NoMemoryService {

String chat(@UserMessage String userMessage);
}

@Inject
NoMemoryService noMemoryService;

@Test
@ActivateRequestContext
void no_memory_should_be_used() throws IOException {

String firstUserMessage = "Hello, my name is Klaus";
wireMockServer.stubFor(WiremockUtils.chatCompletionsMessageContent(Optional.empty(),
"Nice to meet you Klaus"));
String firstAiResponse = noMemoryService.chat(firstUserMessage);

// assert response
assertThat(firstAiResponse).isEqualTo("Nice to meet you Klaus");

// assert request
assertSingleRequestMessage(getRequestAsMap(), firstUserMessage);

wireMockServer.resetRequests();

String secondUserMessage = "What is my name";
wireMockServer.stubFor(WiremockUtils.chatCompletionsMessageContent(Optional.empty(),
"I don't know"));
String secondAiResponse = noMemoryService.chat(secondUserMessage);

// assert response
assertThat(secondAiResponse).isEqualTo("I don't know");

// assert request only contains the second request, so no memory is used
assertSingleRequestMessage(getRequestAsMap(), secondUserMessage);
}

private Map<String, Object> getRequestAsMap() throws IOException {
return getRequestAsMap(getRequestBody());
}
Expand Down

0 comments on commit fc62936

Please sign in to comment.