diff --git a/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIChatCompletion.java b/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIChatCompletion.java index 0c2e11780a..3fdb1f9b56 100644 --- a/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIChatCompletion.java +++ b/java/aiservices/openai/src/main/java/com/microsoft/semantickernel/aiservices/openai/chatcompletion/OpenAIChatCompletion.java @@ -25,6 +25,7 @@ import com.microsoft.semantickernel.contextvariables.ContextVariableTypes; import com.microsoft.semantickernel.exceptions.AIException; import com.microsoft.semantickernel.exceptions.SKException; import com.microsoft.semantickernel.hooks.KernelHooks; +import com.microsoft.semantickernel.hooks.PostChatCompletionEvent; import com.microsoft.semantickernel.hooks.PreChatCompletionEvent; import com.microsoft.semantickernel.orchestration.FunctionResult; import com.microsoft.semantickernel.orchestration.FunctionResultMetadata; @@ -154,6 +155,9 @@ public class OpenAIChatCompletion implements ChatCompletionService { .filter(Objects::nonNull) .collect(Collectors.toList()); + // execute post chat completion hook + kernelHooks.executeHooks(new PostChatCompletionEvent(completions)); + // Just return the result: // If we don't want to attempt to invoke any functions // Or if we are auto-invoking, but we somehow end up with other than 1 choice even though only 1 was requested diff --git a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example57_KernelHooks.java b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example57_KernelHooks.java index 0313edb38a..026254544c 100644 --- a/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example57_KernelHooks.java +++ b/java/samples/sample-code/src/main/java/com/microsoft/semantickernel/samples/syntaxexamples/Example57_KernelHooks.java @@ -19,6 +19,7 @@ import com.microsoft.semantickernel.hooks.KernelHook.PreChatCompletionHook; import com.microsoft.semantickernel.hooks.KernelHook.PromptRenderedHook; import com.microsoft.semantickernel.hooks.KernelHook.PromptRenderingHook; import com.microsoft.semantickernel.hooks.KernelHooks; +import com.microsoft.semantickernel.hooks.PostChatCompletionEvent; import com.microsoft.semantickernel.hooks.PreChatCompletionEvent; import com.microsoft.semantickernel.hooks.PromptRenderedEvent; import com.microsoft.semantickernel.orchestration.FunctionResult; @@ -347,6 +348,12 @@ public class Example57_KernelHooks { PreChatCompletionHook.cloneOptionsWithMessages(options, messages)); }); + kernel.getGlobalKernelHooks().addPostChatCompletionHook(event -> { + System.out.println("Chat completion"); + System.out.println("Id: " + event.getChatCompletions().getId()); + return event; + }); + try { // Invoke prompt to trigger execution hooks. var result = kernel.invokeAsync(writerFunction) diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/KernelHook.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/KernelHook.java index a1e8850573..77ab211c2e 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/KernelHook.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/KernelHook.java @@ -118,4 +118,17 @@ public interface KernelHook extends Predicate { + + @Override + default boolean test(KernelHookEvent arguments) { + return PostChatCompletionEvent.class.isAssignableFrom(arguments.getClass()); + } + + } + } diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/KernelHooks.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/KernelHooks.java index ea3dc82ee0..dd0669d0c6 100644 --- a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/KernelHooks.java +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/KernelHooks.java @@ -3,6 +3,7 @@ package com.microsoft.semantickernel.hooks; import com.microsoft.semantickernel.hooks.KernelHook.FunctionInvokedHook; import com.microsoft.semantickernel.hooks.KernelHook.FunctionInvokingHook; +import com.microsoft.semantickernel.hooks.KernelHook.PostChatCompletionHook; import com.microsoft.semantickernel.hooks.KernelHook.PreChatCompletionHook; import com.microsoft.semantickernel.hooks.KernelHook.PromptRenderedHook; import com.microsoft.semantickernel.hooks.KernelHook.PromptRenderingHook; @@ -101,6 +102,17 @@ public class KernelHooks { return addHook((PreChatCompletionHook) function::apply); } + /** + * Add a {@link PostChatCompletionEvent} to the collection of hooks. + * + * @param function the function to add + * @return the key of the hook in the collection + */ + public String addPostChatCompletionHook( + Function function) { + return addHook((PostChatCompletionHook) function::apply); + } + /** * Add a {@link PromptRenderedHook} to the collection of hooks. * @@ -227,6 +239,12 @@ public class KernelHooks { throw new UnsupportedOperationException("unmodifiable instance of KernelHooks"); } + @Override + public String addPostChatCompletionHook( + Function function) { + throw new UnsupportedOperationException("unmodifiable instance of KernelHooks"); + } + @Override public String addPromptRenderedHook( Function function) { diff --git a/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/PostChatCompletionEvent.java b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/PostChatCompletionEvent.java new file mode 100644 index 0000000000..d824063c2a --- /dev/null +++ b/java/semantickernel-api/src/main/java/com/microsoft/semantickernel/hooks/PostChatCompletionEvent.java @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft. All rights reserved. +package com.microsoft.semantickernel.hooks; + +import com.azure.ai.openai.models.ChatCompletions; + +/** + * Represents a KernelHookEvent that is raised after a chat completion is invoked. + */ +public class PostChatCompletionEvent implements KernelHookEvent { + + private final ChatCompletions chatCompletions; + + /** + * Creates a new instance of the {@link PostChatCompletionEvent} class. + * + * @param chatCompletions the chat completions + */ + public PostChatCompletionEvent(ChatCompletions chatCompletions) { + this.chatCompletions = chatCompletions; + } + + /** + * Gets the chat completions. + * + * @return the chat completions + */ + public ChatCompletions getChatCompletions() { + return chatCompletions; + } +}