Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial version of prompt token replacements #2072

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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));
}
}
}
Loading