in src/WebJobs.Extensions.OpenAI/Search/SemanticSearchConverter.cs [49:109]
async Task<SemanticSearchContext> ConvertHelperAsync(
SemanticSearchAttribute attribute,
CancellationToken cancellationToken)
{
if (this.searchProvider == null)
{
throw new InvalidOperationException(
"No search provider is configured. Search providers are configured in the host.json file. For .NET apps, the appropriate nuget package must also be added to the app's project file.");
}
if (string.IsNullOrEmpty(attribute.Query))
{
throw new InvalidOperationException("The query must be specified.");
}
// Get the embeddings for the query, which will be used for doing a semantic search
this.logger.LogInformation("Sending OpenAI embeddings request: {request}", attribute.Query);
ClientResult<OpenAIEmbedding> embedding = await this.openAIClientFactory.GetEmbeddingClient(
attribute.AIConnectionName,
attribute.EmbeddingsModel).GenerateEmbeddingAsync(attribute.Query, cancellationToken: cancellationToken);
this.logger.LogInformation("Received OpenAI embeddings");
ConnectionInfo connectionInfo = new(attribute.SearchConnectionName, attribute.Collection);
if (string.IsNullOrEmpty(connectionInfo.ConnectionName))
{
throw new InvalidOperationException("No connection string information was provided.");
}
else if (string.IsNullOrEmpty(connectionInfo.CollectionName))
{
throw new InvalidOperationException("No collection name information was provided.");
}
// Search for relevant document snippets using the original query and the embeddings
SearchRequest searchRequest = new(
attribute.Query,
embedding.Value.ToFloats(),
attribute.MaxKnowledgeCount,
connectionInfo);
SearchResponse searchResponse = await this.searchProvider.SearchAsync(searchRequest);
// Append the fetched knowledge from the system prompt
StringBuilder promptBuilder = new(capacity: 8 * 1024);
promptBuilder.AppendLine(attribute.SystemPrompt);
foreach (SearchResult result in searchResponse.OrderedResults)
{
promptBuilder.AppendLine(result.ToString());
}
// Call the chat API with the new combined prompt to get a response back
IList<ChatMessage> messages = new List<ChatMessage>()
{
new SystemChatMessage(promptBuilder.ToString()),
new UserChatMessage(attribute.Query),
};
ChatCompletionOptions completionOptions = attribute.BuildRequest();
ClientResult<ChatCompletion> chatResponse = await this.openAIClientFactory.GetChatClient(attribute.AIConnectionName, attribute.ChatModel).CompleteChatAsync(messages, completionOptions);
// Give the user the full context, including the embeddings information as well as the chat info
return new SemanticSearchContext(new EmbeddingsContext(new List<string> { attribute.Query }, null), chatResponse);
}