Skip to content

Commit

Permalink
semantic caching working
Browse files Browse the repository at this point in the history
  • Loading branch information
stanleysmall-microsoft committed Apr 26, 2024
1 parent b71af90 commit 5fd9b3a
Show file tree
Hide file tree
Showing 10 changed files with 89 additions and 13 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,6 @@ To use Azure Cache for Redis with Vector Search, you need to enable the `USE_RED
```bash
azd env set USE_REDIS true
azd env set EMBEDDING_TYPE 4
azd env set AZURE_USE_APPLICATION_INSIGHTS true
azd up
```

Expand Down
2 changes: 2 additions & 0 deletions app/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
<PackageVersion Include="MudBlazor" Version="6.11.1" />
<PackageVersion Include="PdfSharpCore" Version="1.3.62" />
<PackageVersion Include="Pinecone.NET" Version="1.3.2" />
<PackageVersion Include="Redis.OM" Version="0.7.0" />
<PackageVersion Include="Redis.OM.Vectorizers" Version="0.7.0" />
<PackageVersion Include="StackExchange.Redis" Version="2.7.27" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
Expand Down
2 changes: 2 additions & 0 deletions app/backend/MinimalApi.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" />
<PackageReference Include="PdfSharpCore" />
<PackageReference Include="Swashbuckle.AspNetCore" />
<PackageReference Include="Redis.OM" />
<PackageReference Include="Redis.OM.Vectorizers"/>
</ItemGroup>

<ItemGroup>
Expand Down
74 changes: 66 additions & 8 deletions app/backend/Services/ReadRetrieveReadChatService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.Embeddings;
using Redis.OM;
using Redis.OM.Vectorizers;

namespace MinimalApi.Services;
#pragma warning disable SKEXP0011 // Mark members as static
Expand All @@ -15,6 +17,7 @@ public class ReadRetrieveReadChatService
private readonly IConfiguration _configuration;
private readonly IComputerVisionService? _visionService;
private readonly TokenCredential? _tokenCredential;
private readonly Redis.OM.Contracts.ISemanticCache? _semanticCache;

public ReadRetrieveReadChatService(
ISearchService searchClient,
Expand Down Expand Up @@ -54,6 +57,42 @@ public ReadRetrieveReadChatService(
_configuration = configuration;
_visionService = visionService;
_tokenCredential = tokenCredential;
if (configuration["UseRedis"] == "true")
{
var SEredisConnectionString = configuration["AzureCacheServiceEndpoint"] ?? throw new ArgumentNullException("AzureCacheServiceEndpoint");
var redisConnectionString = ConvertStackExchangeRedisConnectionStringToRedisCloudConnectionURL(SEredisConnectionString);
var provider = new RedisConnectionProvider(redisConnectionString);

var deploymentId = configuration["AzureOpenAiEmbeddingDeployment"] ?? throw new ArgumentNullException("AzureOpenAiEmbeddingDeployment");
var url = configuration["AzureOpenAiServiceEndpoint"] ?? throw new ArgumentNullException("AzureOpenAiServiceEndpoint");
var apiKey = configuration["AzureOpenAiServiceKey"] ?? throw new ArgumentNullException("AzureOpenAiServiceKey");
int startIndex = url.IndexOf("https://") + "https://".Length;
int endIndex = url.IndexOf(".openai.azure.com/");

var resourceName = string.Empty;
if (startIndex >= 0 && endIndex >= 0)
{
// Extract the desired string
resourceName = url.Substring(startIndex, endIndex - startIndex);

}
else
{
throw new ArgumentNullException("AzureOpenAiServiceEndpoint is not in the correct format");
}

var dim = 1536;
_semanticCache = provider.AzureOpenAISemanticCache(apiKey, resourceName, deploymentId, dim);
}
}

private string ConvertStackExchangeRedisConnectionStringToRedisCloudConnectionURL(string stackExchangeRedisConnectionString)
{
var split = stackExchangeRedisConnectionString.Split(',');
var host = split[0].Split(':')[0];
var port = split[0].Split(':')[1];
var password = split[1].Split('=')[1];
return $"redis://:{password}=@{host}:{port}";
}

public async Task<ChatAppResponse> ReplyAsync(
Expand Down Expand Up @@ -110,7 +149,7 @@ standard plan AND dental AND employee benefit.
}
else
{
documentContents = string.Join("\r", documentContentList.Select(x =>$"{x.Title}:{x.Content}"));
documentContents = string.Join("\r", documentContentList.Select(x => $"{x.Title}:{x.Content}"));
}

// step 2.5
Expand Down Expand Up @@ -140,7 +179,7 @@ standard plan AND dental AND employee benefit.
}
}


if (images != null)
{
var prompt = @$"## Source ##
Expand Down Expand Up @@ -185,12 +224,31 @@ You answer needs to be a json object with the following format.
StopSequences = [],
};

// get answer
var answer = await chat.GetChatMessageContentAsync(
answerChat,
promptExecutingSetting,
cancellationToken: cancellationToken);
var answerJson = answer.Content ?? throw new InvalidOperationException("Failed to get search query");
string answerJson = string.Empty;
if (_semanticCache is not null)
{
var res = _semanticCache.GetSimilar(question);
if (res.Length > 0)
{
answerJson = res[0].Response;
}
}

if (string.IsNullOrEmpty(answerJson))
{
// get answer
var answer = await chat.GetChatMessageContentAsync(
answerChat,
promptExecutingSetting,
cancellationToken: cancellationToken);
answerJson = answer.Content ?? throw new InvalidOperationException("Failed to get search query");

if (_semanticCache is not null)
{
_semanticCache.Store(question, answerJson);
}
}

var answerObject = JsonSerializer.Deserialize<JsonElement>(answerJson);
var ans = answerObject.GetProperty("answer").GetString() ?? throw new InvalidOperationException("Failed to get answer");
var thoughts = answerObject.GetProperty("thoughts").GetString() ?? throw new InvalidOperationException("Failed to get thoughts");
Expand Down
1 change: 1 addition & 0 deletions app/prepdocs/PrepareDocs/AppOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ internal record class AppOptions(
string? TenantId,
string? SearchServiceEndpoint,
string? AzureOpenAIServiceEndpoint,
string? AzureOpenAiServiceKey,
string? SearchIndexName,
string? EmbeddingModelName,
bool Remove,
Expand Down
6 changes: 6 additions & 0 deletions app/prepdocs/PrepareDocs/Program.Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ internal static partial class Program
private static readonly Option<string> s_azureOpenAIService =
new(name: "--openaiendpoint", description: "Optional. The Azure OpenAI service endpoint which will be used to extract text, tables and layout from the documents (must exist already)");

private static readonly Option<string> s_azureOpenAIServiceKey =
new(name: "--openaiendpointkey", description: "Optional. The Azure OpenAI service endpoint key");


private static readonly Option<string> s_embeddingModelName =
new(name: "--embeddingmodel", description: "Optional. Name of the Azure Cognitive Search embedding model to use for embedding content in the search index (will be created if it doesn't exist)");

Expand Down Expand Up @@ -68,6 +72,7 @@ internal static partial class Program
s_searchService,
s_searchIndexName,
s_azureOpenAIService,
s_azureOpenAIServiceKey,
s_embeddingModelName,
s_remove,
s_removeAll,
Expand All @@ -88,6 +93,7 @@ internal static partial class Program
SearchServiceEndpoint: context.ParseResult.GetValueForOption(s_searchService),
SearchIndexName: context.ParseResult.GetValueForOption(s_searchIndexName),
AzureOpenAIServiceEndpoint: context.ParseResult.GetValueForOption(s_azureOpenAIService),
AzureOpenAiServiceKey: context.ParseResult.GetValueForOption(s_azureOpenAIServiceKey),
EmbeddingModelName: context.ParseResult.GetValueForOption(s_embeddingModelName),
Remove: context.ParseResult.GetValueForOption(s_remove),
RemoveAll: context.ParseResult.GetValueForOption(s_removeAll),
Expand Down
1 change: 1 addition & 0 deletions infra/core/ai/cognitiveservices.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,4 @@ resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01
output endpoint string = account.properties.endpoint
output id string = account.id
output name string = account.name
output apiKey string = account.listKeys().key1
13 changes: 9 additions & 4 deletions infra/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,10 @@ module keyVaultSecrets 'core/security/keyvault-secrets.bicep' = {
name: 'AzureOpenAiEmbeddingDeployment'
value: azureEmbeddingDeploymentName
}
{
name: 'AzureOpenAiServiceKey'
value: azureOpenAi.outputs.apiKey
}
] : [
{
name: 'OpenAIAPIKey'
Expand Down Expand Up @@ -797,6 +801,7 @@ output AZURE_LOCATION string = location
output AZURE_OPENAI_RESOURCE_LOCATION string = openAiResourceGroupLocation
output AZURE_OPENAI_CHATGPT_DEPLOYMENT string = azureChatGptDeploymentName
output AZURE_OPENAI_EMBEDDING_DEPLOYMENT string = azureEmbeddingDeploymentName
output AZURE_OPENAI_EMBEDDING_API_KEY string = useAOAI? azureOpenAi.outputs.apiKey : ''
output AZURE_OPENAI_ENDPOINT string = useAOAI? azureOpenAi.outputs.endpoint : ''
output AZURE_OPENAI_RESOURCE_GROUP string = useAOAI ? azureOpenAiResourceGroup.name : ''
output AZURE_OPENAI_SERVICE string = useAOAI ? azureOpenAi.outputs.name : ''
Expand All @@ -805,10 +810,10 @@ output AZURE_SEARCH_INDEX string = searchIndexName
output AZURE_SEARCH_SERVICE string = searchService.outputs.name
output AZURE_SEARCH_SERVICE_ENDPOINT string = searchService.outputs.endpoint
output AZURE_SEARCH_SERVICE_RESOURCE_GROUP string = searchServiceResourceGroup.name
output AZURE_CACHE_INDEX string = azureCacheIndexName
output AZURE_CACHE_SERVICE string = azureCache.outputs.name
output AZURE_CACHE_SERVICE_ENDPOINT string = azureCache.outputs.endpoint
output AZURE_CACHE_SERVICE_RESOURCE_GROUP string = azureCacheResourceGroup.name
output AZURE_CACHE_INDEX string = useRedis ? azureCacheIndexName : ''
output AZURE_CACHE_SERVICE string = useRedis ? azureCache.outputs.name : ''
output AZURE_CACHE_SERVICE_ENDPOINT string = useRedis ? azureCache.outputs.endpoint : ''
output AZURE_CACHE_SERVICE_RESOURCE_GROUP string = useRedis ? azureCacheResourceGroup.name : ''
output USE_REDIS bool = useRedis
output AZURE_STORAGE_ACCOUNT string = storage.outputs.name
output AZURE_STORAGE_BLOB_ENDPOINT string = storage.outputs.primaryEndpoints.blob
Expand Down
1 change: 1 addition & 0 deletions scripts/prepdocs.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ if ([string]::IsNullOrEmpty($env:AZD_PREPDOCS_RAN) -or $env:AZD_PREPDOCS_RAN -eq
if ($env:USE_AOAI -eq "true") {
Write-Host "Using Azure OpenAI"
$dotnetArguments += " --openaiendpoint $($env:AZURE_OPENAI_ENDPOINT) "
$dotnetArguments += " --openaiendpointkey $($env:AZURE_OPENAI_EMBEDDING_API_KEY) "
$dotnetArguments += " --embeddingmodel $($env:AZURE_OPENAI_EMBEDDING_DEPLOYMENT) "
}
else{
Expand Down
1 change: 1 addition & 0 deletions scripts/prepdocs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ if [ -z "$AZD_PREPDOCS_RAN" ] || [ "$AZD_PREPDOCS_RAN" = "false" ]; then
echo "use azure openai"
args="$args --openaiendpoint $AZURE_OPENAI_ENDPOINT"
args="$args --embeddingmodel $AZURE_OPENAI_EMBEDDING_DEPLOYMENT"
args="$args --openaiendpointkey $AZURE_OPENAI_EMBEDDING_API_KEY"
else
echo "use openai"
args="$args --embeddingmodel $OPENAI_EMBEDDING_DEPLOYMENT"
Expand Down

0 comments on commit 5fd9b3a

Please sign in to comment.