Skip to content

Commit

Permalink
feat: update AI SDK with latest spec changes (#50)
Browse files Browse the repository at this point in the history
* Updates `Model` config to contain `Id`, `Parameters` and `Custom`,
which are both dictionaries from string -> `LdValue`
* Adds `Provider` config which contains an `Id`
* Renames `Prompt` -> `Messages`, update the getters


There are no changes to the tracking methods.
  • Loading branch information
cwaldren-ld authored Nov 22, 2024
1 parent 0396c4a commit b1a3a8c
Show file tree
Hide file tree
Showing 11 changed files with 313 additions and 79 deletions.
146 changes: 104 additions & 42 deletions pkgs/sdk/server-ai/src/Config/LdAiConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,32 +32,81 @@ internal Message(string content, Role role)
}
}


/// <summary>
/// Information about the model provider.
/// </summary>
public record ModelProvider
{
/// <summary>
/// The ID of the model provider.
/// </summary>
public readonly string Id;

internal ModelProvider(string id)
{
Id = id;
}
}

/// <summary>
/// Information about the model.
/// </summary>
public record ModelConfiguration
{
/// <summary>
/// The ID of the model.
/// </summary>
public readonly string Id;

/// <summary>
/// The model's built-in parameters provided by LaunchDarkly.
/// </summary>
public readonly IReadOnlyDictionary<string, LdValue> Parameters;

/// <summary>
/// The model's custom parameters provided by the user.
/// </summary>
public readonly IReadOnlyDictionary<string, LdValue> Custom;

internal ModelConfiguration(string id, IReadOnlyDictionary<string, LdValue> parameters, IReadOnlyDictionary<string, LdValue> custom)
{
Id = id;
Parameters = parameters;
Custom = custom;
}
}

/// <summary>
/// Builder for constructing an LdAiConfig instance, which can be passed as the default
/// value to the AI Client's <see cref="LdAiClient.ModelConfig"/> method.
/// </summary>
public class Builder
{
private bool _enabled;
private readonly List<Message> _prompt;
private readonly Dictionary<string, object> _modelParams;
private readonly List<Message> _messages;
private readonly Dictionary<string, LdValue> _modelParams;
private readonly Dictionary<string, LdValue> _customModelParams;
private string _providerId;

internal Builder()
{
_enabled = false;
_prompt = new List<Message>();
_modelParams = new Dictionary<string, object>();
_messages = new List<Message>();
_modelParams = new Dictionary<string, LdValue>();
_customModelParams = new Dictionary<string, LdValue>();
_providerId = "";
}

/// <summary>
/// Adds a prompt message with the given content and role. The default role is <see cref="Role.User"/>.
/// Adds a message with the given content and role. The default role is <see cref="Role.User"/>.
/// </summary>
/// <param name="content">the content, which may contain Mustache templates</param>
/// <param name="role">the role</param>
/// <returns>a new builder</returns>
public Builder AddPromptMessage(string content, Role role = Role.User)
public Builder AddMessage(string content, Role role = Role.User)
{
_prompt.Add(new Message(content, role));
_messages.Add(new Message(content, role));
return this;
}

Expand Down Expand Up @@ -85,66 +134,74 @@ public Builder SetEnabled(bool enabled)
}

/// <summary>
/// Sets a parameter for the model. The value may be any object.
/// Sets a parameter for the model.
/// </summary>
/// <param name="name">the parameter name</param>
/// <param name="value">the parameter value</param>
/// <returns>the builder</returns>
public Builder SetModelParam(string name, object value)
public Builder SetModelParam(string name, LdValue value)
{
_modelParams[name] = value;
return this;
}

/// <summary>
/// Sets a custom parameter for the model.
/// </summary>
/// <param name="name">the custom parameter name</param>
/// <param name="value">the custom parameter value</param>
/// <returns>the builder</returns>
public Builder SetCustomModelParam(string name, LdValue value)
{
_customModelParams[name] = value;
return this;
}

/// <summary>
/// Sets the model provider's ID. By default, this will be the empty string.
/// </summary>
/// <param name="id">the ID</param>
/// <returns></returns>
public Builder SetModelProviderId(string id)
{
_providerId = id;
return this;
}

/// <summary>
/// Builds the LdAiConfig instance.
/// </summary>
/// <returns>a new LdAiConfig</returns>
public LdAiConfig Build()
{
return new LdAiConfig(_enabled, _prompt, new Meta(), _modelParams);
return new LdAiConfig(_enabled, _messages, new Meta(), new Model {Parameters = _modelParams, Custom = _customModelParams}, new Provider{ Id = _providerId });
}
}

/// <summary>
/// The prompts associated with the config.
/// </summary>
public readonly IReadOnlyList<Message> Prompt;
public readonly IReadOnlyList<Message> Messages;

/// <summary>
/// The model parameters associated with the config.
/// </summary>
public readonly IReadOnlyDictionary<string, object> Model;

public readonly ModelConfiguration Model;

/// <summary>
/// Information about the model provider.
/// </summary>
public readonly ModelProvider Provider;

internal LdAiConfig(bool enabled, IEnumerable<Message> prompt, Meta meta, IReadOnlyDictionary<string, object> model)
internal LdAiConfig(bool enabled, IEnumerable<Message> messages, Meta meta, Model model, Provider provider)
{
Model = model ?? new Dictionary<string, object>();
Prompt = prompt?.ToList() ?? new List<Message>();
Model = new ModelConfiguration(model?.Id ?? "", model?.Parameters ?? new Dictionary<string, LdValue>(),
model?.Custom ?? new Dictionary<string, LdValue>());
Messages = messages?.ToList() ?? new List<Message>();
VersionKey = meta?.VersionKey ?? "";
Enabled = enabled;
Provider = new ModelProvider(provider?.Id ?? "");
}

private static LdValue ObjectToValue(object obj)
{
if (obj == null)
{
return LdValue.Null;
}

return obj switch
{
bool b => LdValue.Of(b),
double d => LdValue.Of(d),
string s => LdValue.Of(s),
IEnumerable<object> list => LdValue.ArrayFrom(list.Select(ObjectToValue)),
IDictionary<string, object> dict => LdValue.ObjectFrom(dict.ToDictionary(kv => kv.Key,
kv => ObjectToValue(kv.Value))),
_ => LdValue.Null
};
}

internal LdValue ToLdValue()
{
return LdValue.ObjectFrom(new Dictionary<string, LdValue>
Expand All @@ -155,12 +212,20 @@ internal LdValue ToLdValue()
{ "versionKey", LdValue.Of(VersionKey) },
{ "enabled", LdValue.Of(Enabled) }
}) },
{ "prompt", LdValue.ArrayFrom(Prompt.Select(m => LdValue.ObjectFrom(new Dictionary<string, LdValue>
{ "messages", LdValue.ArrayFrom(Messages.Select(m => LdValue.ObjectFrom(new Dictionary<string, LdValue>
{
{ "content", LdValue.Of(m.Content) },
{ "role", LdValue.Of(m.Role.ToString()) }
}))) },
{ "model", ObjectToValue(Model) }
{ "model", LdValue.ObjectFrom(new Dictionary<string, LdValue>
{
{ "parameters", LdValue.ObjectFrom(Model.Parameters) },
{ "custom", LdValue.ObjectFrom(Model.Custom) }
}) },
{"provider", LdValue.ObjectFrom(new Dictionary<string, LdValue>
{
{"id", LdValue.Of(Provider.Id)}
})}
});
}

Expand All @@ -176,7 +241,6 @@ internal LdValue ToLdValue()
/// <returns>true if enabled</returns>
public bool Enabled { get; }


/// <summary>
/// This field meant for internal LaunchDarkly usage.
/// </summary>
Expand All @@ -185,7 +249,5 @@ internal LdValue ToLdValue()
/// <summary>
/// Convenient helper that returns a disabled LdAiConfig.
/// </summary>
public static LdAiConfig Disabled = New().Disable().Build();


public static LdAiConfig Disabled => New().Disable().Build();
}
50 changes: 46 additions & 4 deletions pkgs/sdk/server-ai/src/DataModel/DataModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ public class AiConfig
/// <summary>
/// The prompt.
/// </summary>
[JsonPropertyName("prompt")]
public List<Message> Prompt { get; set; }
[JsonPropertyName("messages")]
public List<Message> Messages { get; set; }

/// <summary>
/// LaunchDarkly metadata.
Expand All @@ -79,8 +79,50 @@ public class AiConfig
public Meta Meta { get; set; }

/// <summary>
/// The model params;
/// The model configuration.
/// </summary>
[JsonPropertyName("model")]
public Dictionary<string, object> Model { get; set; }
public Model Model { get; set; }

/// <summary>
/// The model provider.
/// </summary>
[JsonPropertyName("provider")]
public Provider Provider { get; set; }
}

/// <summary>
/// Represents the JSON serialization of a model.
/// </summary>
public class Model
{
/// <summary>
/// The model's ID.
/// </summary>
[JsonPropertyName("id")]
public string Id { get; set; }

/// <summary>
/// The model's parameters. These are provided by LaunchDarkly.
/// </summary>
[JsonPropertyName("parameters")]
public Dictionary<string, LdValue> Parameters { get; set; }

/// <summary>
/// The model's custom parameters. These are arbitrary and provided by the user.
/// </summary>
[JsonPropertyName("custom")]
public Dictionary<string, LdValue> Custom { get; set; }
}

/// <summary>
/// Represents the JSON serialization of a model provider.
/// </summary>
public class Provider
{
/// <summary>
/// The provider's ID.
/// </summary>
[JsonPropertyName("id")]
public string Id { get; set; }
}
2 changes: 1 addition & 1 deletion pkgs/sdk/server-ai/src/Interfaces/ILdAiConfigTracker.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using System.Threading.Tasks;
using LaunchDarkly.Sdk.Server.Ai.Config;
using LaunchDarkly.Sdk.Server.Ai.Provider;
using LaunchDarkly.Sdk.Server.Ai.Tracking;

namespace LaunchDarkly.Sdk.Server.Ai.Interfaces;

Expand Down
11 changes: 5 additions & 6 deletions pkgs/sdk/server-ai/src/LdAiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ public ILdAiConfigTracker ModelConfig(string key, Context context, LdAiConfig de
return new LdAiConfigTracker(_client, key, defaultValue, context);
}


var mergedVariables = new Dictionary<string, object> { { LdContextVariable, GetAllAttributes(context) } };
if (variables != null)
{
Expand All @@ -75,14 +74,14 @@ public ILdAiConfigTracker ModelConfig(string key, Context context, LdAiConfig de

var prompt = new List<LdAiConfig.Message>();

if (parsed.Prompt != null)
if (parsed.Messages != null)
{
for (var i = 0; i < parsed.Prompt.Count; i++)
for (var i = 0; i < parsed.Messages.Count; i++)
{
try
{
var content = InterpolateTemplate(parsed.Prompt[i].Content, mergedVariables);
prompt.Add(new LdAiConfig.Message(content, parsed.Prompt[i].Role));
var content = InterpolateTemplate(parsed.Messages[i].Content, mergedVariables);
prompt.Add(new LdAiConfig.Message(content, parsed.Messages[i].Role));
}
catch (Exception ex)
{
Expand All @@ -93,7 +92,7 @@ public ILdAiConfigTracker ModelConfig(string key, Context context, LdAiConfig de
}
}

return new LdAiConfigTracker(_client, key, new LdAiConfig(parsed.Meta?.Enabled ?? false, prompt, parsed.Meta, parsed.Model), context);
return new LdAiConfigTracker(_client, key, new LdAiConfig(parsed.Meta?.Enabled ?? false, prompt, parsed.Meta, parsed.Model, parsed.Provider), context);

}

Expand Down
2 changes: 1 addition & 1 deletion pkgs/sdk/server-ai/src/LdAiConfigTracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using System.Threading.Tasks;
using LaunchDarkly.Sdk.Server.Ai.Config;
using LaunchDarkly.Sdk.Server.Ai.Interfaces;
using LaunchDarkly.Sdk.Server.Ai.Provider;
using LaunchDarkly.Sdk.Server.Ai.Tracking;

namespace LaunchDarkly.Sdk.Server.Ai;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace LaunchDarkly.Sdk.Server.Ai.Provider;
namespace LaunchDarkly.Sdk.Server.Ai.Tracking;

/// <summary>
/// Feedback about the generated content.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace LaunchDarkly.Sdk.Server.Ai.Provider;
namespace LaunchDarkly.Sdk.Server.Ai.Tracking;

/// <summary>
/// Represents metrics returned by a model provider.
Expand Down
8 changes: 4 additions & 4 deletions pkgs/sdk/server-ai/test/InterpolationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ private string Eval(string prompt, Context context, IReadOnlyDictionary<string,
{
"_ldMeta": {"versionKey": "1", "enabled": true},
"model": {},
"prompt": [
"messages": [
{
"content": "<do-not-use-in-any-tests-prompt-placeholder>",
"role": "System"
"role": "system"
}
]
}
Expand All @@ -41,7 +41,7 @@ private string Eval(string prompt, Context context, IReadOnlyDictionary<string,
var client = new LdAiClient(mockClient.Object);
var tracker = client.ModelConfig("foo", context, LdAiConfig.Disabled, variables);

return tracker.Config.Prompt[0].Content;
return tracker.Config.Messages[0].Content;
}

[Theory]
Expand Down Expand Up @@ -128,7 +128,7 @@ public void TestInterpolationMalformed()
{
"_ldMeta": {"versionKey": "1", "enabled": true},
"model": {},
"prompt": [
"messages": [
{
"content": "This is a {{ malformed }]} prompt",
"role": "System"
Expand Down
Loading

0 comments on commit b1a3a8c

Please sign in to comment.