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

feat: update AI SDK with latest spec changes #50

Merged
merged 5 commits into from
Nov 22, 2024
Merged
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
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
Loading