Skip to content

Commit

Permalink
Initial version of prompt token replacements
Browse files Browse the repository at this point in the history
  • Loading branch information
codingbandit committed Dec 14, 2024
1 parent 31f3b6a commit be48a79
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ public class MultipartPrompt : PromptBase
[JsonPropertyName("suffix")]
public string? Suffix { get; set; }

/// <summary>
/// Optional string token replacements for the prompt.
/// </summary>
[JsonPropertyName("token_replacements")]
public TokenReplacementDefinition[] TokenReplacements { get; set; } = [];

/// <summary>
/// Set default property values.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Text.Json.Serialization;

namespace FoundationaLLM.Common.Models.ResourceProviders.Prompt
{
/// <summary>
/// String text token replacement definition.
/// </summary>
public class TokenReplacementDefinition
{
/// <summary>
/// The token to be replaced.
/// </summary>
[JsonPropertyName("token")]
public required string Token { get; set; }

/// <summary>
/// The code to compute the replacement value.
/// </summary>
[JsonPropertyName("compute_code")]
public required string ComputeCode { get; set; }
}
}
1 change: 1 addition & 0 deletions src/dotnet/Orchestration/Orchestration.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.12.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>

Expand Down
15 changes: 14 additions & 1 deletion src/dotnet/Orchestration/Orchestration/OrchestrationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using FoundationaLLM.Common.Models.ResourceProviders.Prompt;
using FoundationaLLM.Common.Models.ResourceProviders.Vectorization;
using FoundationaLLM.Orchestration.Core.Interfaces;
using FoundationaLLM.Orchestration.Core.Services.TokenReplacement;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
Expand Down Expand Up @@ -204,7 +205,7 @@ await cosmosDBService.PatchOperationsItemPropertiesAsync<LongRunningOperationCon
var agentWorkflow = agentBase.Workflow;
AIModelBase? mainAIModel = null;
APIEndpointConfiguration? mainAIModelAPIEndpointConfiguration = null;

if (agentWorkflow is not null)
{
foreach (var resourceObjectId in agentWorkflow.ResourceObjectIds.Values)
Expand Down Expand Up @@ -256,6 +257,18 @@ await cosmosDBService.PatchOperationsItemPropertiesAsync<LongRunningOperationCon
var retrievedPrompt = await promptResourceProvider.GetResourceAsync<PromptBase>(
resourceObjectId.ObjectId,
currentUserIdentity);

if(retrievedPrompt is MultipartPrompt multipartPrompt)
{
//check for token replacements, multipartPrompt variable has the same reference as retrievedPrompt therefore this edits the prefix/suffix in place
if (multipartPrompt is not null && multipartPrompt.TokenReplacements != null && multipartPrompt.TokenReplacements.Length > 0)
{
var tokenReplacementEngine = new TokenReplacementEngine(multipartPrompt.TokenReplacements.ToList());
multipartPrompt.Prefix = await tokenReplacementEngine.ReplaceTokensAsync(multipartPrompt.Prefix!);
multipartPrompt.Suffix = await tokenReplacementEngine.ReplaceTokensAsync(multipartPrompt.Suffix!);
}
}

explodedObjectsManager.TryAdd(
retrievedPrompt.ObjectId!,
retrievedPrompt);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System.Text.RegularExpressions;
using FoundationaLLM.Common.Models.ResourceProviders.Prompt;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;

namespace FoundationaLLM.Orchestration.Core.Services.TokenReplacement
{
/// <summary>
/// Token replacement engine, responsible for replacing tokens in a string with their computed values.
/// </summary>
public class TokenReplacementEngine
{
private readonly List<TokenReplacementDefinition> _tokenReplacements;
private readonly ScriptOptions _scriptOptions;

/// <summary>
/// Creates an instance of the <see cref="TokenReplacementEngine"/> class.
/// </summary>
/// <param name="tokenReplacements"></param>
public TokenReplacementEngine(List<TokenReplacementDefinition> tokenReplacements)
{
_tokenReplacements = tokenReplacements;

// Define script options, such as referencing necessary assemblies and namespaces
_scriptOptions = ScriptOptions.Default
.AddImports("System");
}

/// <summary>
/// Replaces tokens in the input string with their corresponding computed values.
/// </summary>
/// <param name="input">The input string containing tokens to be replaced.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the string with tokens replaced.</returns>
public async Task<string> ReplaceTokensAsync(string input)
{
if (string.IsNullOrWhiteSpace(input))
{
return input;
}
string pattern = @"{{\s*(\w+)\s*}}";
return await Task.Run(() => Regex.Replace(input, pattern, match =>
{
string tokenName = match.Groups[1].Value;
var tokenReplacement = _tokenReplacements.Find(tr => tr.Token == $"{{{{{tokenName}}}}}");

if (tokenReplacement != null)
{
try
{
// Evaluate the compute code
var result = CSharpScript.EvaluateAsync<string>(
tokenReplacement.ComputeCode,
_scriptOptions).Result;
return result;
}
catch (Exception ex)
{
// Handle errors in compute code
return $"[Error: {ex.Message}]";
}
}

// If token not found, return it unchanged or handle as needed
return match.Value;
}, RegexOptions.IgnoreCase));
}
}
}

0 comments on commit be48a79

Please sign in to comment.