Skip to content

Commit

Permalink
Merge pull request #2073 from solliancenet/cp_prompt_tokens_alt
Browse files Browse the repository at this point in the history
Prompt token replacement alternative solution
  • Loading branch information
ciprianjichici authored Dec 15, 2024
2 parents 8d2fcf0 + 2780080 commit 3504a54
Show file tree
Hide file tree
Showing 12 changed files with 218 additions and 26 deletions.
12 changes: 12 additions & 0 deletions docs/release-notes/breaking-changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
> [!NOTE]
> This section is for changes that are not yet released but will affect future releases.
## Starting with 0.9.1

Prompt prefixes and suffixes support FoundationaLLM variables for dynamic replacement at runtime. The variable format is `{{foundationallm:variable_name[:format]}}` where
- `variable_name` is the name of the well-known variable.
- `format` is the optional formatting applied to the value of the variable.

The following variables are supported:

| Name | Value | Example
| --- | --- | --- |
| `current_datetime_utc` | The current UTC date and time. | `The current date is {{foundationallm:current_datetime_utc:dddd, MMMM dd, yyyy}}. This looks great.` -> `The current date is Sunday, December 15, 2024. This looks great.`

## Starting with 0.9.0

### Configuration changes
Expand Down
13 changes: 13 additions & 0 deletions src/dotnet/Common/Constants/Templates/TemplateVariables.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace FoundationaLLM.Common.Constants.Templates
{
/// <summary>
/// Provides template variables that can be used in templates.
/// </summary>
public static class TemplateVariables
{
/// <summary>
/// Token for current date in UTC format.
/// </summary>
public const string CurrentDateTimeUTC = "current_datetime_utc";
}
}
15 changes: 15 additions & 0 deletions src/dotnet/Common/Interfaces/ITemplatingService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace FoundationaLLM.Common.Interfaces
{
/// <summary>
/// Defines the interface for a templating engine.
/// </summary>
public interface ITemplatingService
{
/// <summary>
/// Transforms the input string by replacing tokens with the corresponding values.
/// </summary>
/// <param name="s">The input string to be transformed.</param>
/// <returns>The transformed string where all the valid tokens have been replaced.</returns>
string Transform(string s);
}
}
15 changes: 15 additions & 0 deletions src/dotnet/Common/Services/DependencyInjection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using FoundationaLLM.Common.Services.API;
using FoundationaLLM.Common.Services.Azure;
using FoundationaLLM.Common.Services.Security;
using FoundationaLLM.Common.Services.Templates;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Azure.Cosmos;
using Microsoft.Azure.Cosmos.Fluent;
Expand Down Expand Up @@ -297,5 +298,19 @@ public static void AddAzureCosmosDBService(this IServiceCollection services, ICo

services.AddSingleton<IAzureCosmosDBService, AzureCosmosDBService>();
}

/// <summary>
/// Registers the <see cref="ITemplatingService"/> implementation with the dependency injection container.
/// </summary>
/// <param name="builder">The <see cref="IHostApplicationBuilder"/> application builder managing the dependency injection container.</param>
public static void AddRegexTemplatingEngine(this IHostApplicationBuilder builder) =>
builder.Services.AddSingleton<ITemplatingService, RegexTemplatingService>();

/// <summary>
/// Registers the <see cref="ITemplatingService"/> implementation with the dependency injection container.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> dependency injection container service collection.</param>
public static void AddRegexTemplatingEngine(this IServiceCollection services) =>
services.AddSingleton<ITemplatingService, RegexTemplatingService>();
}
}
78 changes: 78 additions & 0 deletions src/dotnet/Common/Services/Templates/RegexTemplatingService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using FoundationaLLM.Common.Constants.Templates;
using FoundationaLLM.Common.Interfaces;
using Microsoft.Extensions.Logging;
using System.Text.RegularExpressions;

namespace FoundationaLLM.Common.Services.Templates
{
/// <summary>
/// Templating engine that uses regular expressions to replace tokens in strings.
/// </summary>
/// <param name="logger">The logger used for logging.</param>
public partial class RegexTemplatingService(
ILogger<RegexTemplatingService> logger) : ITemplatingService
{
/// <summary>
/// Regular expression pattern for template variables.
/// </summary>
private const string REGEX_VARIABLE_PATTERN = "\\{\\{foundationallm:(.*?)\\}\\}";

private readonly ILogger<RegexTemplatingService> _logger = logger;

[GeneratedRegex(REGEX_VARIABLE_PATTERN, RegexOptions.Compiled)]
private static partial Regex VariableRegex();

/// <inheritdoc/>
public string Transform(string s)
{
if (string.IsNullOrWhiteSpace(s))
{
return string.Empty;
}

try
{
// Expects the format {{foundationallm:variable_name[:format]}}

var matches = VariableRegex().Matches(s);
Dictionary<string, string> replacements = [];

foreach (Match match in matches)
{
var matchedVariable = match.Value;

var variableTokens = match.Groups[1].Value.Split(":", 2);
var variableName = variableTokens[0];
var variableFormat = variableTokens.Length > 1 ? variableTokens[1] : null;

switch (variableName)
{
case TemplateVariables.CurrentDateTimeUTC:
replacements.Add(
matchedVariable,
string.IsNullOrWhiteSpace(variableFormat)
? DateTime.UtcNow.ToString()
: DateTime.UtcNow.ToString(variableFormat));
break;
default:
break;
}
}

var transformedString = s;
foreach (var replacement in replacements)
{
transformedString = transformedString.Replace(replacement.Key, replacement.Value);
}

return transformedString;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occurred while transforming the string.");
}

return s;
}
}
}
15 changes: 15 additions & 0 deletions src/dotnet/Orchestration/Orchestration/OrchestrationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class OrchestrationBuilder
/// <param name="resourceProviderServices">A dictionary of <see cref="IResourceProviderService"/> resource providers hashed by resource provider name.</param>
/// <param name="llmOrchestrationServiceManager">The <see cref="ILLMOrchestrationServiceManager"/> that manages internal and external orchestration services.</param>
/// <param name="cosmosDBService">The <see cref="IAzureCosmosDBService"/> used to interact with the Cosmos DB database.</param>
/// <param name="templatingService">The <see cref="ITemplatingService"/> used to render templates.</param>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> provding dependency injection services for the current scope.</param>
/// <param name="loggerFactory">The logger factory used to create new loggers.</param>
/// <returns></returns>
Expand All @@ -53,6 +54,7 @@ public class OrchestrationBuilder
Dictionary<string, IResourceProviderService> resourceProviderServices,
ILLMOrchestrationServiceManager llmOrchestrationServiceManager,
IAzureCosmosDBService cosmosDBService,
ITemplatingService templatingService,
IServiceProvider serviceProvider,
ILoggerFactory loggerFactory)
{
Expand All @@ -64,6 +66,7 @@ public class OrchestrationBuilder
originalRequest.SessionId,
originalRequest.Settings?.ModelParameters,
resourceProviderServices,
templatingService,
callContext.CurrentUserIdentity!,
logger);

Expand Down Expand Up @@ -176,6 +179,7 @@ await cosmosDBService.PatchOperationsItemPropertiesAsync<LongRunningOperationCon
string? sessionId,
Dictionary<string, object>? modelParameterOverrides,
Dictionary<string, IResourceProviderService> resourceProviderServices,
ITemplatingService templatingService,
UnifiedUserIdentity currentUserIdentity,
ILogger<OrchestrationBuilder> logger)
{
Expand Down Expand Up @@ -256,6 +260,17 @@ 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.Prefix = templatingService.Transform(multipartPrompt.Prefix!);
multipartPrompt.Suffix = templatingService.Transform(multipartPrompt.Suffix!);
}
}
explodedObjectsManager.TryAdd(
retrievedPrompt.ObjectId!,
retrievedPrompt);
Expand Down
7 changes: 7 additions & 0 deletions src/dotnet/Orchestration/Services/OrchestrationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class OrchestrationService : IOrchestrationService
{
private readonly ILLMOrchestrationServiceManager _llmOrchestrationServiceManager;
private readonly IAzureCosmosDBService _cosmosDBService;
private readonly ITemplatingService _templatingService;
private readonly ICallContext _callContext;
private readonly IConfiguration _configuration;
private readonly ILogger<OrchestrationService> _logger;
Expand All @@ -39,6 +40,7 @@ public class OrchestrationService : IOrchestrationService
/// <param name="resourceProviderServices">A list of <see cref="IResourceProviderService"/> resource providers hashed by resource provider name.</param>
/// <param name="llmOrchestrationServiceManager">The <see cref="ILLMOrchestrationServiceManager"/> managing the internal and external LLM orchestration services.</param>
/// <param name="cosmosDBService">The <see cref="IAzureCosmosDBService"/> used to interact with the Cosmos DB database.</param>
/// <param name="templatingService">The <see cref="ITemplatingService"/> used to render templates.</param>
/// <param name="callContext">The call context of the request being handled.</param>
/// <param name="configuration">The <see cref="IConfiguration"/> used to retrieve app settings from configuration.</param>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> provding dependency injection services for the current scope.</param>
Expand All @@ -47,6 +49,7 @@ public OrchestrationService(
IEnumerable<IResourceProviderService> resourceProviderServices,
ILLMOrchestrationServiceManager llmOrchestrationServiceManager,
IAzureCosmosDBService cosmosDBService,
ITemplatingService templatingService,
ICallContext callContext,
IConfiguration configuration,
IServiceProvider serviceProvider,
Expand All @@ -56,6 +59,7 @@ public OrchestrationService(
rps => rps.Name);
_llmOrchestrationServiceManager = llmOrchestrationServiceManager;
_cosmosDBService = cosmosDBService;
_templatingService = templatingService;

_callContext = callContext;
_configuration = configuration;
Expand Down Expand Up @@ -100,6 +104,7 @@ public async Task<CompletionResponse> GetCompletion(string instanceId, Completio
_resourceProviderServices,
_llmOrchestrationServiceManager,
_cosmosDBService,
_templatingService,
_serviceProvider,
_loggerFactory)
?? throw new OrchestrationException($"The orchestration builder was not able to create an orchestration for agent [{completionRequest.AgentName ?? string.Empty}].");
Expand Down Expand Up @@ -139,6 +144,7 @@ public async Task<LongRunningOperation> StartCompletionOperation(string instance
_resourceProviderServices,
_llmOrchestrationServiceManager,
_cosmosDBService,
_templatingService,
_serviceProvider,
_loggerFactory)
?? throw new OrchestrationException($"The orchestration builder was not able to create an orchestration for agent [{completionRequest.AgentName ?? string.Empty}].");
Expand Down Expand Up @@ -225,6 +231,7 @@ private async Task<CompletionResponse> GetCompletionForAgentConversation(
_resourceProviderServices,
_llmOrchestrationServiceManager,
_cosmosDBService,
_templatingService,
_serviceProvider,
_loggerFactory);

Expand Down
3 changes: 3 additions & 0 deletions src/dotnet/OrchestrationAPI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ public static void Main(string[] args)
builder.AddGroupMembership();
builder.AddAuthorizationServiceClient();

// Add the templating engine.
builder.AddRegexTemplatingEngine();

//----------------------------
// Resource providers
//----------------------------
Expand Down
Loading

0 comments on commit 3504a54

Please sign in to comment.