From 6ef70205bae97d3c36b6c0611f0e5e027719d9e6 Mon Sep 17 00:00:00 2001 From: Christopher Schraer <32145632+chschrae@users.noreply.github.com> Date: Tue, 6 Feb 2024 17:46:08 -0800 Subject: [PATCH] java stream with own data (#158) * updated imports on chat completions template * Added java streaming with your own data sample * updated test --------- Co-authored-by: Chris Schraer --- .../src/OpenAIChatCompletionsClass.java | 7 +- .../_.json | 15 +++ .../pom.xml | 39 +++++++ .../scripts/1-copydependencies.bat | 1 + .../scripts/2-compile.bat | 1 + .../scripts/3-run.bat | 1 + .../src/Main.java | 49 ++++++++ ...ChatCompletionsWithDataStreamingClass.java | 109 ++++++++++++++++++ tests/test.yaml | 2 +- 9 files changed, 222 insertions(+), 2 deletions(-) create mode 100644 src/ai/.x/templates/openai-chat-streaming-with-data-java/_.json create mode 100644 src/ai/.x/templates/openai-chat-streaming-with-data-java/pom.xml create mode 100644 src/ai/.x/templates/openai-chat-streaming-with-data-java/scripts/1-copydependencies.bat create mode 100644 src/ai/.x/templates/openai-chat-streaming-with-data-java/scripts/2-compile.bat create mode 100644 src/ai/.x/templates/openai-chat-streaming-with-data-java/scripts/3-run.bat create mode 100644 src/ai/.x/templates/openai-chat-streaming-with-data-java/src/Main.java create mode 100644 src/ai/.x/templates/openai-chat-streaming-with-data-java/src/OpenAIChatCompletionsWithDataStreamingClass.java diff --git a/src/ai/.x/templates/openai-chat-java/src/OpenAIChatCompletionsClass.java b/src/ai/.x/templates/openai-chat-java/src/OpenAIChatCompletionsClass.java index deccae33..792e85f0 100644 --- a/src/ai/.x/templates/openai-chat-java/src/OpenAIChatCompletionsClass.java +++ b/src/ai/.x/templates/openai-chat-java/src/OpenAIChatCompletionsClass.java @@ -3,7 +3,12 @@ <#@ parameter type="System.String" name="ClassName" #> import com.azure.ai.openai.OpenAIClient; import com.azure.ai.openai.OpenAIClientBuilder; -import com.azure.ai.openai.models.*; +import com.azure.ai.openai.models.ChatRequestAssistantMessage; +import com.azure.ai.openai.models.ChatRequestMessage; +import com.azure.ai.openai.models.ChatRequestSystemMessage; +import com.azure.ai.openai.models.ChatRequestUserMessage; +import com.azure.ai.openai.models.ChatCompletions; +import com.azure.ai.openai.models.ChatCompletionsOptions; import com.azure.core.credential.AzureKeyCredential; import java.util.ArrayList; diff --git a/src/ai/.x/templates/openai-chat-streaming-with-data-java/_.json b/src/ai/.x/templates/openai-chat-streaming-with-data-java/_.json new file mode 100644 index 00000000..a4123b59 --- /dev/null +++ b/src/ai/.x/templates/openai-chat-streaming-with-data-java/_.json @@ -0,0 +1,15 @@ +{ + "_LongName": "OpenAI Chat Completions (w/ Data + AI Search)", + "_ShortName": "openai-chat-streaming-with-data", + "_Language": "Java", + "ClassName": "OpenAIChatCompletionsWithDataStreamingClass", + "AZURE_OPENAI_API_VERSION": "", + "AZURE_OPENAI_ENDPOINT": "", + "AZURE_OPENAI_KEY": "", + "AZURE_OPENAI_CHAT_DEPLOYMENT": "", + "AZURE_OPENAI_EMBEDDING_DEPLOYMENT": "", + "AZURE_OPENAI_SYSTEM_PROMPT": "You are a helpful AI assistant.", + "AZURE_AI_SEARCH_ENDPOINT": "", + "AZURE_AI_SEARCH_KEY": "", + "AZURE_AI_SEARCH_INDEX_NAME": "" +} \ No newline at end of file diff --git a/src/ai/.x/templates/openai-chat-streaming-with-data-java/pom.xml b/src/ai/.x/templates/openai-chat-streaming-with-data-java/pom.xml new file mode 100644 index 00000000..59337755 --- /dev/null +++ b/src/ai/.x/templates/openai-chat-streaming-with-data-java/pom.xml @@ -0,0 +1,39 @@ + + 4.0.0 + + com.azure.ai.openai.samples + openai-chat-java-streaming + 1.0-SNAPSHOT + + + + + com.azure + azure-ai-openai + 1.0.0-beta.6 + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.1.2 + + + copy-dependencies + prepare-package + + copy-dependencies + + + ${project.build.directory}/lib + + + + + + + + \ No newline at end of file diff --git a/src/ai/.x/templates/openai-chat-streaming-with-data-java/scripts/1-copydependencies.bat b/src/ai/.x/templates/openai-chat-streaming-with-data-java/scripts/1-copydependencies.bat new file mode 100644 index 00000000..f0b4c1c7 --- /dev/null +++ b/src/ai/.x/templates/openai-chat-streaming-with-data-java/scripts/1-copydependencies.bat @@ -0,0 +1 @@ +mvn clean package \ No newline at end of file diff --git a/src/ai/.x/templates/openai-chat-streaming-with-data-java/scripts/2-compile.bat b/src/ai/.x/templates/openai-chat-streaming-with-data-java/scripts/2-compile.bat new file mode 100644 index 00000000..f0249b59 --- /dev/null +++ b/src/ai/.x/templates/openai-chat-streaming-with-data-java/scripts/2-compile.bat @@ -0,0 +1 @@ +javac -cp target/lib/* src/OpenAIChatCompletionsWithDataStreamingClass.java src/Main.java -d out diff --git a/src/ai/.x/templates/openai-chat-streaming-with-data-java/scripts/3-run.bat b/src/ai/.x/templates/openai-chat-streaming-with-data-java/scripts/3-run.bat new file mode 100644 index 00000000..6d301cb6 --- /dev/null +++ b/src/ai/.x/templates/openai-chat-streaming-with-data-java/scripts/3-run.bat @@ -0,0 +1 @@ +java -cp out;target/lib/* Main diff --git a/src/ai/.x/templates/openai-chat-streaming-with-data-java/src/Main.java b/src/ai/.x/templates/openai-chat-streaming-with-data-java/src/Main.java new file mode 100644 index 00000000..f4efebc5 --- /dev/null +++ b/src/ai/.x/templates/openai-chat-streaming-with-data-java/src/Main.java @@ -0,0 +1,49 @@ +<#@ template hostspecific="true" #> +<#@ output extension=".java" encoding="utf-8" #> +<#@ parameter type="System.String" name="ClassName" #> +<#@ parameter type="System.String" name="AZURE_OPENAI_API_VERSION" #> +<#@ parameter type="System.String" name="AZURE_OPENAI_ENDPOINT" #> +<#@ parameter type="System.String" name="AZURE_OPENAI_KEY" #> +<#@ parameter type="System.String" name="AZURE_OPENAI_CHAT_DEPLOYMENT" #> +<#@ parameter type="System.String" name="AZURE_OPENAI_EMBEDDING_DEPLOYMENT" #> +<#@ parameter type="System.String" name="AZURE_OPENAI_SYSTEM_PROMPT" #> +<#@ parameter type="System.String" name="AZURE_AI_SEARCH_ENDPOINT" #> +<#@ parameter type="System.String" name="AZURE_AI_SEARCH_KEY" #> +<#@ parameter type="System.String" name="AZURE_AI_SEARCH_INDEX_NAME" #> +import java.util.Scanner; +import reactor.core.publisher.Flux; +import com.azure.ai.openai.models.ChatCompletions; + +public class Main { + + public static void main(String[] args) { + String openAIKey = (System.getenv("AZURE_OPENAI_KEY") != null) ? System.getenv("AZURE_OPENAI_KEY") : ""; + String openAIEndpoint = (System.getenv("AZURE_OPENAI_ENDPOINT") != null) ? System.getenv("AZURE_OPENAI_ENDPOINT") : ""; + String openAIChatDeployment = (System.getenv("AZURE_OPENAI_CHAT_DEPLOYMENT") != null) ? System.getenv("AZURE_OPENAI_CHAT_DEPLOYMENT") : ""; + String openAISystemPrompt = (System.getenv("AZURE_OPENAI_SYSTEM_PROMPT") != null) ? System.getenv("AZURE_OPENAI_SYSTEM_PROMPT") : "You are a helpful AI assistant."; + + String openAIApiVersion = System.getenv("AZURE_OPENAI_API_VERSION") != null ? System.getenv("AZURE_OPENAI_API_VERSION") : "<#= AZURE_OPENAI_API_VERSION #>"; + String azureSearchEmbeddingsDeploymentName = System.getenv("AZURE_OPENAI_EMBEDDING_DEPLOYMENT") != null ? System.getenv("AZURE_OPENAI_EMBEDDING_DEPLOYMENT") : "<#= AZURE_OPENAI_EMBEDDING_DEPLOYMENT #>"; + String azureSearchEndpoint = System.getenv("AZURE_AI_SEARCH_ENDPOINT") != null ? System.getenv("AZURE_AI_SEARCH_ENDPOINT") : "<#= AZURE_AI_SEARCH_ENDPOINT #>"; + String azureSearchAPIKey = System.getenv("AZURE_AI_SEARCH_KEY") != null ? System.getenv("AZURE_AI_SEARCH_KEY") : "<#= AZURE_AI_SEARCH_KEY #>"; + String azureSearchIndexName = System.getenv("AZURE_AI_SEARCH_INDEX_NAME") != null ? System.getenv("AZURE_AI_SEARCH_INDEX_NAME") : "<#= AZURE_AI_SEARCH_INDEX_NAME #>"; + + <#= ClassName #> chat = new <#= ClassName #>(openAIKey, openAIEndpoint, openAIChatDeployment, openAISystemPrompt, azureSearchEndpoint, azureSearchIndexName, azureSearchAPIKey, azureSearchEmbeddingsDeploymentName); + + Scanner scanner = new Scanner(System.in); + while (true) { + System.out.print("User: "); + String userPrompt = scanner.nextLine(); + if (userPrompt.isEmpty() || "exit".equals(userPrompt)) + break; + + System.out.print("\nAssistant: "); + Flux responseFlux = chat.getChatCompletionsStreamingAsync(userPrompt, update -> { + System.out.print(update.getContent()); + }); + responseFlux.blockLast(); + System.out.println("\n"); + } + scanner.close(); + } +} \ No newline at end of file diff --git a/src/ai/.x/templates/openai-chat-streaming-with-data-java/src/OpenAIChatCompletionsWithDataStreamingClass.java b/src/ai/.x/templates/openai-chat-streaming-with-data-java/src/OpenAIChatCompletionsWithDataStreamingClass.java new file mode 100644 index 00000000..ec7469bd --- /dev/null +++ b/src/ai/.x/templates/openai-chat-streaming-with-data-java/src/OpenAIChatCompletionsWithDataStreamingClass.java @@ -0,0 +1,109 @@ +<#@ template hostspecific="true" #> +<#@ output extension=".java" encoding="utf-8" #> +<#@ parameter type="System.String" name="ClassName" #> +import com.azure.ai.openai.OpenAIAsyncClient; +import com.azure.ai.openai.OpenAIClientBuilder; +import com.azure.ai.openai.models.AzureCognitiveSearchChatExtensionConfiguration; +import com.azure.ai.openai.models.AzureCognitiveSearchChatExtensionParameters; +import com.azure.ai.openai.models.AzureCognitiveSearchIndexFieldMappingOptions; +import com.azure.ai.openai.models.AzureCognitiveSearchQueryType; +import com.azure.ai.openai.models.ChatChoice; +import com.azure.ai.openai.models.ChatCompletions; +import com.azure.ai.openai.models.ChatCompletionsOptions; +import com.azure.ai.openai.models.ChatRequestAssistantMessage; +import com.azure.ai.openai.models.ChatRequestMessage; +import com.azure.ai.openai.models.ChatRequestSystemMessage; +import com.azure.ai.openai.models.ChatRequestUserMessage; +import com.azure.ai.openai.models.ChatResponseMessage; +import com.azure.ai.openai.models.CompletionsFinishReason; +import com.azure.ai.openai.models.OnYourDataApiKeyAuthenticationOptions; +import com.azure.ai.openai.models.OnYourDataDeploymentNameVectorizationSource; +import com.azure.core.credential.AzureKeyCredential; +import reactor.core.publisher.Flux; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.function.Consumer; +import java.util.List; + +public class <#= ClassName #> { + + private OpenAIAsyncClient client; + private ChatCompletionsOptions options; + private String openAIChatDeployment; + private String openAISystemPrompt; + + public <#= ClassName #> ( + String openAIKey, + String openAIEndpoint, + String openAIChatDeployment, + String openAISystemPrompt, + String azureSearchEndpoint, + String azureSearchIndexName, + String azureSearchAPIKey, + String azureSearchEmbeddingsDeploymentName) { + + this.openAIChatDeployment = openAIChatDeployment; + this.openAISystemPrompt = openAISystemPrompt; + client = new OpenAIClientBuilder() + .endpoint(openAIEndpoint) + .credential(new AzureKeyCredential(openAIKey)) + .buildAsyncClient(); + + AzureCognitiveSearchChatExtensionConfiguration searchConfiguration = + new AzureCognitiveSearchChatExtensionConfiguration( + new AzureCognitiveSearchChatExtensionParameters(azureSearchEndpoint, azureSearchIndexName) + .setAuthentication(new OnYourDataApiKeyAuthenticationOptions(azureSearchAPIKey)) + .setQueryType(AzureCognitiveSearchQueryType.VECTOR_SIMPLE_HYBRID) + .setEmbeddingDependency(new OnYourDataDeploymentNameVectorizationSource(azureSearchEmbeddingsDeploymentName)) + ); + + List chatMessages = new ArrayList<>(); + options = new ChatCompletionsOptions(chatMessages) + .setDataSources(Arrays.asList(searchConfiguration)); + ClearConversation(); + options.setStream(true); + } + + public void ClearConversation(){ + List chatMessages = options.getMessages(); + chatMessages.clear(); + chatMessages.add(new ChatRequestSystemMessage(this.openAISystemPrompt)); + } + + public Flux getChatCompletionsStreamingAsync(String userPrompt, + Consumer callback) { + options.getMessages().add(new ChatRequestUserMessage(userPrompt)); + + StringBuilder responseContent = new StringBuilder(); + Flux response = client.getChatCompletionsStream(this.openAIChatDeployment, options); + + response.subscribe(chatResponse -> { + if (chatResponse.getChoices() != null) { + for (ChatChoice update : chatResponse.getChoices()) { + if (update.getDelta() == null || update.getDelta().getContent() == null) + continue; + String content = update.getDelta().getContent(); + + if (update.getFinishReason() == CompletionsFinishReason.CONTENT_FILTERED) { + content = content + "\nWARNING: Content filtered!"; + } else if (update.getFinishReason() == CompletionsFinishReason.TOKEN_LIMIT_REACHED) { + content = content + "\nERROR: Exceeded token limit!"; + } + + if (content.isEmpty()) + continue; + + if(callback != null) { + callback.accept(update.getDelta()); + } + responseContent.append(content); + } + + options.getMessages().add(new ChatRequestAssistantMessage(responseContent.toString())); + } + }); + + return response; + } +} \ No newline at end of file diff --git a/tests/test.yaml b/tests/test.yaml index 92a3747e..d7b1bac3 100644 --- a/tests/test.yaml +++ b/tests/test.yaml @@ -89,5 +89,5 @@ ^Helper +Function +Class +Library +helper-functions +C# *\r?$\n ^OpenAI +Chat +Completions +openai-chat +C#, +Go, +Java, +JavaScript, +Python *\r?$\n ^OpenAI +Chat +Completions +\(Streaming\) +openai-chat-streaming +C#, +Go, +Java, +JavaScript, +Python *\r?$\n - ^OpenAI +Chat +Completions +\(w/ +Data +\+ +AI +Search\) +openai-chat-streaming-with-data +C#, +Go, +JavaScript, +Python *\r?$\n + ^OpenAI +Chat +Completions +\(w/ +Data +\+ +AI +Search\) +openai-chat-streaming-with-data +C#, +Go, +Java, +JavaScript, +Python *\r?$\n ^OpenAI +Chat +Completions +\(w/ +Functions\) +openai-chat-streaming-with-functions +C#, +Go, +JavaScript, +Python *\r?$\n