-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This implements the guts of the AI client, including fetching Model configuration, interpolation, and tests.
- Loading branch information
1 parent
0ba54e4
commit acfa1a5
Showing
19 changed files
with
1,349 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
using LaunchDarkly.Logging; | ||
using LaunchDarkly.Sdk.Server.Ai.Interfaces; | ||
|
||
namespace LaunchDarkly.Sdk.Server.Ai.Adapters; | ||
|
||
/// <summary> | ||
/// Adapts an <see cref="LdClient"/> to the requirements of <see cref="LdAiClient"/>. | ||
/// </summary> | ||
public class LdClientAdapter : ILaunchDarklyClient | ||
{ | ||
private readonly LdClient _client; | ||
|
||
/// <summary> | ||
/// Constructs the adapter from an existing client. | ||
/// </summary> | ||
/// <param name="client">the adapter</param> | ||
public LdClientAdapter(LdClient client) | ||
{ | ||
_client = client; | ||
} | ||
|
||
/// <inheritdoc/> | ||
public LdValue JsonVariation(string key, Context context, LdValue defaultValue) | ||
=> _client.JsonVariation(key, context, defaultValue); | ||
|
||
/// <inheritdoc/> | ||
public void Track(string name, Context context, LdValue data, double metricValue) | ||
=> _client.Track(name, context, data, metricValue); | ||
|
||
/// <inheritdoc/> | ||
public ILogger GetLogger() => new LoggerAdapter(_client.GetLogger()); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
using LaunchDarkly.Logging; | ||
using LaunchDarkly.Sdk.Server.Ai.Interfaces; | ||
|
||
namespace LaunchDarkly.Sdk.Server.Ai.Adapters; | ||
|
||
/// <summary> | ||
/// Adapts a <see cref="Logger"/> to the requirements of the <see cref="LdAiClient"/>'s | ||
/// logger. | ||
/// </summary> | ||
internal class LoggerAdapter : ILogger | ||
{ | ||
private readonly Logger _logger; | ||
|
||
/// <summary> | ||
/// Creates a new adapter. | ||
/// </summary> | ||
/// <param name="logger">the existing logger</param> | ||
public LoggerAdapter(Logger logger) | ||
{ | ||
_logger = logger; | ||
} | ||
|
||
/// <inheritdoc/> | ||
public void Error(string format, params object[] allParams) => _logger.Error(format, allParams); | ||
|
||
/// <inheritdoc/> | ||
public void Warn(string format, params object[] allParams) => _logger.Warn(format, allParams); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using LaunchDarkly.Sdk.Server.Ai.DataModel; | ||
|
||
namespace LaunchDarkly.Sdk.Server.Ai.Config; | ||
|
||
/// <summary> | ||
/// Represents an AI configuration, which contains model parameters and prompt messages. | ||
/// </summary> | ||
public record LdAiConfig | ||
{ | ||
|
||
/// <summary> | ||
/// Represents a single message, which is part of a prompt. | ||
/// </summary> | ||
public record Message | ||
{ | ||
/// <summary> | ||
/// The content of the message, which may contain Mustache templates. | ||
/// </summary> | ||
public readonly string Content; | ||
|
||
/// <summary> | ||
/// The role of the message. | ||
/// </summary> | ||
public readonly Role Role; | ||
|
||
internal Message(string content, Role role) | ||
{ | ||
Content = content; | ||
Role = role; | ||
} | ||
} | ||
|
||
/// <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; | ||
|
||
internal Builder() | ||
{ | ||
_enabled = false; | ||
_prompt = new List<Message>(); | ||
_modelParams = new Dictionary<string, object>(); | ||
} | ||
|
||
/// <summary> | ||
/// Adds a prompt 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) | ||
{ | ||
_prompt.Add(new Message(content, role)); | ||
return this; | ||
} | ||
|
||
/// <summary> | ||
/// Disables the config. | ||
/// </summary> | ||
/// <returns>the builder</returns> | ||
public Builder Disable() => SetEnabled(false); | ||
|
||
/// <summary> | ||
/// Enables the config. | ||
/// </summary> | ||
/// <returns>the builder</returns> | ||
public Builder Enable() => SetEnabled(true); | ||
|
||
/// <summary> | ||
/// Sets the enabled state of the config based on a boolean. | ||
/// </summary> | ||
/// <param name="enabled">whether the config is enabled</param> | ||
/// <returns>the builder</returns> | ||
public Builder SetEnabled(bool enabled) | ||
{ | ||
_enabled = enabled; | ||
return this; | ||
} | ||
|
||
/// <summary> | ||
/// Sets a parameter for the model. The value may be any object. | ||
/// </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) | ||
{ | ||
_modelParams[name] = value; | ||
return this; | ||
} | ||
|
||
/// <summary> | ||
/// Builds the LdAiConfig instance. | ||
/// </summary> | ||
/// <returns>a new LdAiConfig</returns> | ||
public LdAiConfig Build() | ||
{ | ||
return new LdAiConfig(_enabled, _prompt, new Meta(), _modelParams); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// The prompts associated with the config. | ||
/// </summary> | ||
public readonly IReadOnlyList<Message> Prompt; | ||
|
||
/// <summary> | ||
/// The model parameters associated with the config. | ||
/// </summary> | ||
public readonly IReadOnlyDictionary<string, object> Model; | ||
|
||
|
||
|
||
internal LdAiConfig(bool enabled, IEnumerable<Message> prompt, Meta meta, IReadOnlyDictionary<string, object> model) | ||
{ | ||
Model = model ?? new Dictionary<string, object>(); | ||
Prompt = prompt?.ToList() ?? new List<Message>(); | ||
VersionKey = meta?.VersionKey ?? ""; | ||
Enabled = enabled; | ||
} | ||
|
||
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> | ||
{ | ||
{ "_ldMeta", LdValue.ObjectFrom( | ||
new Dictionary<string, LdValue> | ||
{ | ||
{ "versionKey", LdValue.Of(VersionKey) }, | ||
{ "enabled", LdValue.Of(Enabled) } | ||
}) }, | ||
{ "prompt", LdValue.ArrayFrom(Prompt.Select(m => LdValue.ObjectFrom(new Dictionary<string, LdValue> | ||
{ | ||
{ "content", LdValue.Of(m.Content) }, | ||
{ "role", LdValue.Of(m.Role.ToString()) } | ||
}))) }, | ||
{ "model", ObjectToValue(Model) } | ||
}); | ||
} | ||
|
||
/// <summary> | ||
/// Creates a new LdAiConfig builder. | ||
/// </summary> | ||
/// <returns>a new builder</returns> | ||
public static Builder New() => new(); | ||
|
||
/// <summary> | ||
/// Returns true if the config is enabled. | ||
/// </summary> | ||
/// <returns>true if enabled</returns> | ||
public bool Enabled { get; } | ||
|
||
|
||
/// <summary> | ||
/// This field meant for internal LaunchDarkly usage. | ||
/// </summary> | ||
public string VersionKey { get; } | ||
|
||
/// <summary> | ||
/// Convenient helper that returns a disabled LdAiConfig. | ||
/// </summary> | ||
public static LdAiConfig Disabled = New().Disable().Build(); | ||
|
||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
using System.Collections.Generic; | ||
using System.Text.Json.Serialization; | ||
|
||
namespace LaunchDarkly.Sdk.Server.Ai.DataModel; | ||
|
||
/// <summary> | ||
/// Represents the role of the prompt message. | ||
/// </summary> | ||
public enum Role | ||
{ | ||
/// <summary> | ||
/// User role. | ||
/// </summary> | ||
User, | ||
/// <summary> | ||
/// System role. | ||
/// </summary> | ||
System, | ||
/// <summary> | ||
/// Assistant role. | ||
/// </summary> | ||
Assistant | ||
} | ||
|
||
/// <summary> | ||
/// Represents the JSON serialization of the Meta field. | ||
/// </summary> | ||
public class Meta | ||
{ | ||
/// <summary> | ||
/// The version key. | ||
/// </summary> | ||
[JsonPropertyName("versionKey")] | ||
public string VersionKey { get; set; } | ||
|
||
/// <summary> | ||
/// If the config is enabled. | ||
/// </summary> | ||
[JsonPropertyName("enabled")] | ||
public bool Enabled { get; set; } | ||
} | ||
|
||
/// <summary> | ||
/// Represents the JSON serialization of a Message. | ||
/// </summary> | ||
public class Message | ||
{ | ||
/// <summary> | ||
/// The content. | ||
/// </summary> | ||
[JsonPropertyName("content")] | ||
public string Content { get; set; } | ||
|
||
/// <summary> | ||
/// The role. | ||
/// </summary> | ||
[JsonPropertyName("role")] | ||
[JsonConverter(typeof(JsonStringEnumConverter))] | ||
public Role Role { get; set; } | ||
} | ||
|
||
|
||
/// <summary> | ||
/// Represents the JSON serialization of an AiConfig. | ||
/// </summary> | ||
|
||
public class AiConfig | ||
{ | ||
/// <summary> | ||
/// The prompt. | ||
/// </summary> | ||
[JsonPropertyName("prompt")] | ||
public List<Message> Prompt { get; set; } | ||
|
||
/// <summary> | ||
/// LaunchDarkly metadata. | ||
/// </summary> | ||
[JsonPropertyName("_ldMeta")] | ||
public Meta Meta { get; set; } | ||
|
||
/// <summary> | ||
/// The model params; | ||
/// </summary> | ||
[JsonPropertyName("model")] | ||
public Dictionary<string, object> Model { get; set; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
namespace LaunchDarkly.Sdk.Server.Ai.Interfaces; | ||
|
||
/// <summary> | ||
/// Interface representing capabilities needed by the AI Client. These are usually provided | ||
/// by the LaunchDarkly Server SDK. | ||
/// </summary> | ||
public interface ILaunchDarklyClient | ||
{ | ||
/// <summary> | ||
/// Returns a JSON variation. | ||
/// </summary> | ||
/// <param name="key">the flag key</param> | ||
/// <param name="context">the context</param> | ||
/// <param name="defaultValue">the default value</param> | ||
/// <returns>the evaluation result</returns> | ||
LdValue JsonVariation(string key, Context context, LdValue defaultValue); | ||
|
||
/// <summary> | ||
/// Tracks a metric. | ||
/// </summary> | ||
/// <param name="name">metric name</param> | ||
/// <param name="context">context</param> | ||
/// <param name="data">associated data</param> | ||
/// <param name="metricValue">metric value</param> | ||
void Track(string name, Context context, LdValue data, double metricValue); | ||
|
||
/// <summary> | ||
/// Returns a logger. | ||
/// </summary> | ||
/// <returns>a logger</returns> | ||
ILogger GetLogger(); | ||
} |
Oops, something went wrong.