Skip to content

Commit

Permalink
feat: support multi-kind contexts in template interpolation (#48)
Browse files Browse the repository at this point in the history
This PR allows for Mustache interpolation of multi-kind contexts. Developers can refer to single kinds by their name, e.g. ldctx.user or ldctx.device.
  • Loading branch information
cwaldren-ld authored Nov 19, 2024
1 parent 8767510 commit 40ff539
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 7 deletions.
39 changes: 32 additions & 7 deletions pkgs/sdk/server-ai/src/LdAiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,25 @@ public ILdAiConfigTracker ModelConfig(string key, Context context, LdAiConfig de

}


private static IDictionary<string, object> AddSingleKindContextAttributes(Context context)
{
var attributes = new Dictionary<string, object>
{
["kind"] = context.Kind.ToString(),
["key"] = context.Key,
["anonymous"] = context.Anonymous
};

foreach (var key in context.OptionalAttributeNames)
{
attributes[key] = ValueToObject(context.GetValue(AttributeRef.FromLiteral(key)));
}

return attributes;
}


/// <summary>
/// Retrieves all attributes from the given context, including private attributes. The attributes
/// are converted into C# primitives recursively.
Expand All @@ -105,17 +124,23 @@ public ILdAiConfigTracker ModelConfig(string key, Context context, LdAiConfig de
/// <returns>the attributes</returns>
private static IDictionary<string, object> GetAllAttributes(Context context)
{
var attributes = new Dictionary<string, object>();
foreach (var key in context.OptionalAttributeNames)
if (!context.Multiple)
{
attributes[key] = ValueToObject(context.GetValue(AttributeRef.FromLiteral(key)));
return AddSingleKindContextAttributes(context);
}

attributes["kind"] = context.Kind.ToString();
attributes["key"] = context.Key;
attributes["anonymous"] = context.Anonymous;
var attrs = new Dictionary<string, object>
{
["kind"] = context.Kind,
["key"] = context.FullyQualifiedKey
};

return attributes;
foreach (var kind in context.MultiKindContexts)
{
attrs[kind.Kind.ToString()] = AddSingleKindContextAttributes(kind);
}

return attrs;
}

/// <summary>
Expand Down
66 changes: 66 additions & 0 deletions pkgs/sdk/server-ai/test/InterpolationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,4 +170,70 @@ public void TestInterpolationWithNestedContextAttributes()
var result = Eval("I can ingest over {{ ldctx.stats.power }} tokens per second!", context, null);
Assert.Equal("I can ingest over 9000 tokens per second!", result);
}

[Fact]
public void TestInterpolationWithMultiKindContext()
{
var user = Context.Builder(ContextKind.Default, "123")
.Set("cat_ownership", LdValue.ObjectFrom(new Dictionary<string, LdValue>
{
{ "count", LdValue.Of(12) }
})).Build();

var cat = Context.Builder(ContextKind.Of("cat"), "456")
.Set("health", LdValue.ObjectFrom(new Dictionary<string, LdValue>
{
{ "hunger", LdValue.Of("off the charts") }
})).Build();

var context = Context.MultiBuilder().Add(user).Add(cat).Build();

var nestedVars = Eval("As an owner of {{ ldctx.user.cat_ownership.count }} cats, I must report that my cat's hunger level is {{ ldctx.cat.health.hunger }}!", context, null);
Assert.Equal("As an owner of 12 cats, I must report that my cat's hunger level is off the charts!", nestedVars);

var canonicalKeys = Eval("multi={{ ldctx.key }} user={{ ldctx.user.key }} cat={{ ldctx.cat.key }}", context, null);
Assert.Equal("multi=cat:456:user:123 user=123 cat=456", canonicalKeys);
}

[Fact]
public void TestInterpolationMultiKindDoesNotHaveAnonymousAttribute()
{
var user = Context.Builder(ContextKind.Default, "123")
.Set("cat_ownership", LdValue.ObjectFrom(new Dictionary<string, LdValue>
{
{ "count", LdValue.Of(12) }
})).Build();

var cat = Context.Builder(ContextKind.Of("cat"), "456")
.Set("health", LdValue.ObjectFrom(new Dictionary<string, LdValue>
{
{ "hunger", LdValue.Of("off the charts") }
})).Build();

var context = Context.MultiBuilder().Add(user).Add(cat).Build();

var result = Eval("anonymous=<{{ ldctx.anonymous }}>", context, null);
Assert.Equal("anonymous=<>", result);
}

[Fact]
public void TestInterpolationMultiKindContextHasKindMulti()
{
var user = Context.Builder(ContextKind.Default, "123")
.Set("cat_ownership", LdValue.ObjectFrom(new Dictionary<string, LdValue>
{
{ "count", LdValue.Of(12) }
})).Build();

var cat = Context.Builder(ContextKind.Of("cat"), "456")
.Set("health", LdValue.ObjectFrom(new Dictionary<string, LdValue>
{
{ "hunger", LdValue.Of("off the charts") }
})).Build();

var context = Context.MultiBuilder().Add(user).Add(cat).Build();

var result = Eval("kind={{ ldctx.kind }}", context, null);
Assert.Equal("kind=multi", result);
}
}

0 comments on commit 40ff539

Please sign in to comment.