Skip to content

Commit

Permalink
Add suggestions
Browse files Browse the repository at this point in the history
  • Loading branch information
milderhc committed Jan 21, 2025
1 parent 6d19745 commit 8c46067
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 78 deletions.
2 changes: 1 addition & 1 deletion app/backend/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

<spring-cloud-azure.version>5.14.0</spring-cloud-azure.version>
<azure-search.version>11.7.2</azure-search.version>
<semantic-kernel.version>1.4.1</semantic-kernel.version>
<semantic-kernel.version>1.4.2</semantic-kernel.version>
<mockito-inline.version>4.5.1</mockito-inline.version>
<maven.compiler-plugin.version>3.11.0</maven.compiler-plugin.version>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@
package com.microsoft.openai.samples.rag.ask.approaches.semantickernel;

import com.azure.ai.openai.OpenAIAsyncClient;
import com.azure.core.credential.TokenCredential;
import com.azure.search.documents.SearchAsyncClient;
import com.azure.search.documents.indexes.SearchIndexAsyncClient;
import com.azure.search.documents.indexes.SearchIndexClientBuilder;
import com.microsoft.openai.samples.rag.approaches.ContentSource;
import com.microsoft.openai.samples.rag.approaches.RAGApproach;
import com.microsoft.openai.samples.rag.approaches.RAGOptions;
import com.microsoft.openai.samples.rag.approaches.RAGResponse;
import com.microsoft.openai.samples.rag.chat.approaches.semantickernel.JavaSemanticKernelWithVectorStoreChatApproach;
import com.microsoft.openai.samples.rag.retrieval.semantickernel.AzureAISearchVectorStoreApproach;
import com.microsoft.openai.samples.rag.retrieval.semantickernel.AzureAISearchVectorStoreUtils;
import com.microsoft.semantickernel.Kernel;

import com.microsoft.semantickernel.aiservices.openai.chatcompletion.OpenAIChatCompletion;
Expand All @@ -38,7 +35,7 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.microsoft.openai.samples.rag.retrieval.semantickernel.AzureAISearchVectorStoreApproach.MemoryRecord;
import com.microsoft.openai.samples.rag.retrieval.semantickernel.AzureAISearchVectorStoreUtils.DocumentRecord;

/**
* Use Java Semantic Kernel framework with built-in VectorStores for embeddings similarity search. A
Expand All @@ -51,15 +48,9 @@
public class JavaSemanticKernelWithVectorStoreApproach implements RAGApproach<String, RAGResponse> {
private static final Logger LOGGER =
LoggerFactory.getLogger(JavaSemanticKernelWithVectorStoreApproach.class);
private final TokenCredential tokenCredential;
private final OpenAIAsyncClient openAIAsyncClient;
private final SearchIndexAsyncClient searchAsyncClient;

private final SearchAsyncClient searchAsyncClient;

private final String EMBEDDING_FIELD_NAME = "embedding";

@Value("${cognitive.search.service}")
String searchServiceName;

@Value("${cognitive.search.index}")
String indexName;
Expand All @@ -71,10 +62,8 @@ public class JavaSemanticKernelWithVectorStoreApproach implements RAGApproach<St
private String embeddingDeploymentModelId;

public JavaSemanticKernelWithVectorStoreApproach(
TokenCredential tokenCredential,
OpenAIAsyncClient openAIAsyncClient,
SearchAsyncClient searchAsyncClient) {
this.tokenCredential = tokenCredential;
SearchIndexAsyncClient searchAsyncClient) {
this.openAIAsyncClient = openAIAsyncClient;
this.searchAsyncClient = searchAsyncClient;
}
Expand All @@ -90,29 +79,23 @@ public RAGResponse run(String question, RAGOptions options) {
// skill is imported from src/main/resources/semantickernel/Plugins.
Kernel semanticKernel = buildSemanticKernel(options);

// STEP 1: Create Azure AI Search client
SearchIndexAsyncClient client = new SearchIndexClientBuilder()
.endpoint("https://%s.search.windows.net".formatted(searchServiceName))
.credential(tokenCredential)
.buildAsyncClient();

// STEP 2: Build Vector Record Collection
AzureAISearchVectorStoreRecordCollection<MemoryRecord> recordCollection = new AzureAISearchVectorStoreRecordCollection<>(
client,
// STEP 1: Build Vector Record Collection
AzureAISearchVectorStoreRecordCollection<DocumentRecord> recordCollection = new AzureAISearchVectorStoreRecordCollection<>(
searchAsyncClient,
indexName,
AzureAISearchVectorStoreRecordCollectionOptions.<MemoryRecord>builder()
.withRecordClass(MemoryRecord.class)
AzureAISearchVectorStoreRecordCollectionOptions.<DocumentRecord>builder()
.withRecordClass(DocumentRecord.class)
.build()
);

// STEP 3: Retrieve relevant documents using user question.
List<MemoryRecord> memoryResult = AzureAISearchVectorStoreApproach.searchAsync(
// STEP 2: Retrieve relevant documents using user question.
List<DocumentRecord> memoryResult = AzureAISearchVectorStoreUtils.searchAsync(
question, semanticKernel, recordCollection, options);

String sources = AzureAISearchVectorStoreApproach.buildSourcesText(memoryResult);
List<ContentSource> sourcesList = AzureAISearchVectorStoreApproach.buildSources(memoryResult);
String sources = AzureAISearchVectorStoreUtils.buildSourcesText(memoryResult);
List<ContentSource> sourcesList = AzureAISearchVectorStoreUtils.buildSources(memoryResult);

// STEP 4: Generate a contextual and content specific answer using the search results and question
// STEP 3: Generate a contextual and content specific answer using the search results and question
KernelFunction<String> answerQuestion = semanticKernel.getFunction("RAG", "AnswerQuestion");
KernelFunctionArguments arguments = KernelFunctionArguments.builder()
.withVariable("sources", sourcesList)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
package com.microsoft.openai.samples.rag.chat.approaches.semantickernel;

import com.azure.ai.openai.OpenAIAsyncClient;
import com.azure.core.credential.TokenCredential;
import com.azure.search.documents.indexes.SearchIndexAsyncClient;
import com.azure.search.documents.indexes.SearchIndexClientBuilder;
import com.microsoft.openai.samples.rag.approaches.ContentSource;
import com.microsoft.openai.samples.rag.approaches.RAGApproach;
import com.microsoft.openai.samples.rag.approaches.RAGOptions;
import com.microsoft.openai.samples.rag.approaches.RAGResponse;
import com.microsoft.openai.samples.rag.common.ChatGPTConversation;
import com.microsoft.openai.samples.rag.common.ChatGPTUtils;
import com.microsoft.openai.samples.rag.retrieval.semantickernel.AzureAISearchVectorStoreApproach;
import com.microsoft.openai.samples.rag.retrieval.semantickernel.AzureAISearchVectorStoreUtils;
import com.microsoft.semantickernel.Kernel;
import com.microsoft.semantickernel.aiservices.openai.chatcompletion.OpenAIChatCompletion;
import com.microsoft.semantickernel.aiservices.openai.textembedding.OpenAITextEmbeddingGenerationService;
Expand Down Expand Up @@ -38,7 +36,7 @@
import java.util.ArrayList;
import java.util.List;

import com.microsoft.openai.samples.rag.retrieval.semantickernel.AzureAISearchVectorStoreApproach.MemoryRecord;
import com.microsoft.openai.samples.rag.retrieval.semantickernel.AzureAISearchVectorStoreUtils.DocumentRecord;

/**
* Use Java Semantic Kernel framework with built-in VectorStores for embeddings similarity search. A
Expand All @@ -50,22 +48,20 @@
@Component
public class JavaSemanticKernelWithVectorStoreChatApproach implements RAGApproach<ChatGPTConversation, RAGResponse> {
private static final Logger LOGGER = LoggerFactory.getLogger(JavaSemanticKernelWithVectorStoreChatApproach.class);
private final TokenCredential tokenCredential;
private final OpenAIAsyncClient openAIAsyncClient;
private final SearchIndexAsyncClient searchAsyncClient;
private String renderedConversation;

@Value("${cognitive.search.service}")
String searchServiceName;
@Value("${cognitive.search.index}")
String indexName;
@Value("${openai.chatgpt.deployment}")
private String gptChatDeploymentModelId;
@Value("${openai.embedding.deployment}")
private String embeddingDeploymentModelId;

public JavaSemanticKernelWithVectorStoreChatApproach(TokenCredential tokenCredential, OpenAIAsyncClient openAIAsyncClient) {
this.tokenCredential = tokenCredential;
public JavaSemanticKernelWithVectorStoreChatApproach(OpenAIAsyncClient openAIAsyncClient, SearchIndexAsyncClient searchAsyncClient) {
this.openAIAsyncClient = openAIAsyncClient;
this.searchAsyncClient = searchAsyncClient;
}

@Override
Expand All @@ -76,29 +72,23 @@ public RAGResponse run(ChatGPTConversation questionOrConversation, RAGOptions op
// Build semantic kernel context with AnswerConversation and ExtractKeywords plugins, EmbeddingGenerationService and ChatCompletionService.
Kernel semanticKernel = buildSemanticKernel();

// STEP 1: Create Azure AI Search client
SearchIndexAsyncClient client = new SearchIndexClientBuilder()
.endpoint("https://%s.search.windows.net".formatted(searchServiceName))
.credential(tokenCredential)
.buildAsyncClient();

// STEP 2: Build Vector Record Collection
AzureAISearchVectorStoreRecordCollection<AzureAISearchVectorStoreApproach.MemoryRecord> recordCollection = new AzureAISearchVectorStoreRecordCollection<>(
client,
// STEP 1: Build Vector Record Collection
AzureAISearchVectorStoreRecordCollection<DocumentRecord> recordCollection = new AzureAISearchVectorStoreRecordCollection<>(
searchAsyncClient,
indexName,
AzureAISearchVectorStoreRecordCollectionOptions.<MemoryRecord>builder()
.withRecordClass(AzureAISearchVectorStoreApproach.MemoryRecord.class)
AzureAISearchVectorStoreRecordCollectionOptions.<DocumentRecord>builder()
.withRecordClass(DocumentRecord.class)
.build()
);

// STEP 3: Retrieve relevant documents using keywords extracted from the chat history
// STEP 2: Retrieve relevant documents using keywords extracted from the chat history
String conversationString = ChatGPTUtils.formatAsChatML(questionOrConversation.toOpenAIChatMessages());
List<AzureAISearchVectorStoreApproach.MemoryRecord> sourcesResult = getSourcesFromConversation(conversationString, semanticKernel, recordCollection, options);
List<DocumentRecord> sourcesResult = getSourcesFromConversation(conversationString, semanticKernel, recordCollection, options);

LOGGER.info("Total {} sources found in cognitive vector store for search query[{}]", sourcesResult.size(), question);

String sources = AzureAISearchVectorStoreApproach.buildSourcesText(sourcesResult);
List<ContentSource> sourcesList = AzureAISearchVectorStoreApproach.buildSources(sourcesResult);
String sources = AzureAISearchVectorStoreUtils.buildSourcesText(sourcesResult);
List<ContentSource> sourcesList = AzureAISearchVectorStoreUtils.buildSources(sourcesResult);

// STEP 3: Generate a contextual and content specific answer using the search results and chat history
KernelFunction<String> answerConversation = semanticKernel.getFunction("RAG", "AnswerConversation");
Expand Down Expand Up @@ -133,10 +123,10 @@ private ChatHistory removeLastMessage(ChatHistory conversation) {
return new ChatHistory(messages);
}

private List<MemoryRecord> getSourcesFromConversation(String conversation,
Kernel kernel,
AzureAISearchVectorStoreRecordCollection<MemoryRecord> recordCollection,
RAGOptions ragOptions) {
private List<DocumentRecord> getSourcesFromConversation(String conversation,
Kernel kernel,
AzureAISearchVectorStoreRecordCollection<DocumentRecord> recordCollection,
RAGOptions ragOptions) {
KernelFunction<String> extractKeywords = kernel
.getPlugin("RAG")
.get("ExtractKeywords");
Expand All @@ -151,7 +141,7 @@ private List<MemoryRecord> getSourcesFromConversation(String conversation,
.block();
String searchQuery = result.getResult();

return AzureAISearchVectorStoreApproach.searchAsync(
return AzureAISearchVectorStoreUtils.searchAsync(
searchQuery,
kernel,
recordCollection,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import com.azure.search.documents.SearchAsyncClient;
import com.azure.search.documents.SearchClient;
import com.azure.search.documents.SearchClientBuilder;
import com.azure.search.documents.indexes.SearchIndexAsyncClient;
import com.azure.search.documents.indexes.SearchIndexClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
Expand Down Expand Up @@ -82,4 +84,15 @@ public SearchAsyncClient asyncSearchDefaultClient() {
.indexName(indexName)
.buildAsyncClient();
}

@Bean
@ConditionalOnProperty(name = "cognitive.tracing.enabled", havingValue = "true")
public SearchIndexAsyncClient asyncSearchIndexDefaultClient() {
String endpoint = "https://%s.search.windows.net".formatted(searchServiceName);

return new SearchIndexClientBuilder()
.endpoint(endpoint)
.credential(tokenCredential)
.buildAsyncClient();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@
import java.util.Optional;
import java.util.stream.Collectors;

public class AzureAISearchVectorStoreApproach {
public class AzureAISearchVectorStoreUtils {

private static final Logger LOGGER = LoggerFactory.getLogger(AzureAISearchVectorStoreApproach.class);
private static final Logger LOGGER = LoggerFactory.getLogger(AzureAISearchVectorStoreUtils.class);

private static final String EMBEDDING_FIELD_NAME = "embedding";

public static class MemoryRecord {
public static class DocumentRecord {
@VectorStoreRecordKey
private final String id;
@VectorStoreRecordData
Expand All @@ -49,7 +49,7 @@ public static class MemoryRecord {
@VectorStoreRecordData
private final String sourceFile;

public MemoryRecord(
public DocumentRecord(
@JsonProperty("id") String id,
@JsonProperty("content") String content,
@JsonProperty("embedding") List<Float> embedding,
Expand Down Expand Up @@ -90,10 +90,10 @@ public String getSourceFile() {
}


public static List<MemoryRecord> searchAsync(String searchQuery,
Kernel kernel,
AzureAISearchVectorStoreRecordCollection<MemoryRecord> recordCollection,
RAGOptions ragOptions) {
public static List<DocumentRecord> searchAsync(String searchQuery,
Kernel kernel,
AzureAISearchVectorStoreRecordCollection<DocumentRecord> recordCollection,
RAGOptions ragOptions) {
// Create VectorSearch options
VectorSearchOptions vectorSearchOptions = VectorSearchOptions.builder()
.withTop(ragOptions.getTop())
Expand Down Expand Up @@ -126,15 +126,15 @@ public static List<MemoryRecord> searchAsync(String searchQuery,
}

// Search the vector store for the relevant documents with the generated embeddings
VectorSearchResults<MemoryRecord> memoryResult = recordCollection.hybridSearchAsync(searchQuery, questionVector, vectorSearchOptions, searchOptions)
VectorSearchResults<DocumentRecord> memoryResult = recordCollection.hybridSearchAsync(searchQuery, questionVector, vectorSearchOptions, searchOptions)
.block();

// Remove the score from the result
return memoryResult.getResults().stream().map(VectorSearchResult::getRecord).collect(Collectors.toList());
}


public static List<ContentSource> buildSources(List<MemoryRecord> memoryResult) {
public static List<ContentSource> buildSources(List<DocumentRecord> memoryResult) {
return memoryResult
.stream()
.map(result -> {
Expand All @@ -147,7 +147,7 @@ public static List<ContentSource> buildSources(List<MemoryRecord> memoryResult)
}


public static String buildSourcesText(List<MemoryRecord> memoryResult) {
public static String buildSourcesText(List<DocumentRecord> memoryResult) {
StringBuilder sourcesContentBuffer = new StringBuilder();
memoryResult.stream().forEach(memory -> {
sourcesContentBuffer.append(memory.getSourceFile())
Expand Down
6 changes: 3 additions & 3 deletions app/frontend/src/pages/chat/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -252,12 +252,12 @@ const Chat = () => {
text: "Java Azure Open AI SDK"
},
{
key: Approaches.JAVA_SEMANTIC_KERNEL_PLANNER,
key: Approaches.JAVA_SEMANTIC_KERNEL,
text: "Java Semantic Kernel"
},
{
key: Approaches.JAVA_SEMANTIC_KERNEL,
text: "Java Semantic Kernel - Vector Store"
key: Approaches.JAVA_SEMANTIC_KERNEL_PLANNER,
text: "Java Semantic Kernel - Chains"
},
];

Expand Down
6 changes: 3 additions & 3 deletions app/frontend/src/pages/oneshot/OneShot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,12 @@ export function Component(): JSX.Element {
text: "Java Azure Open AI SDK"
},
{
key: Approaches.JAVA_SEMANTIC_KERNEL_PLANNER,
key: Approaches.JAVA_SEMANTIC_KERNEL,
text: "Java Semantic Kernel"
},
{
key: Approaches.JAVA_SEMANTIC_KERNEL,
text: "Java Semantic Kernel - Vector Store"
key: Approaches.JAVA_SEMANTIC_KERNEL_PLANNER,
text: "Java Semantic Kernel - Chains"
},
];

Expand Down

0 comments on commit 8c46067

Please sign in to comment.