From 4e32ae0f2b1b9c26af62fb9ddcdf11424b8b225c Mon Sep 17 00:00:00 2001
From: Casey Waldren <cwaldren@launchdarkly.com>
Date: Tue, 12 Nov 2024 16:34:45 -0800
Subject: [PATCH] fix: catch exceptions thrown by template interpolation

---
 pkgs/sdk/server-ai/src/LdAiClient.cs          | 21 ++++++++++--
 pkgs/sdk/server-ai/test/InterpolationTests.cs | 32 +++++++++++++++++++
 2 files changed, 51 insertions(+), 2 deletions(-)

diff --git a/pkgs/sdk/server-ai/src/LdAiClient.cs b/pkgs/sdk/server-ai/src/LdAiClient.cs
index 928f2f63..5067af0b 100644
--- a/pkgs/sdk/server-ai/src/LdAiClient.cs
+++ b/pkgs/sdk/server-ai/src/LdAiClient.cs
@@ -73,10 +73,27 @@ public ILdAiConfigTracker ModelConfig(string key, Context context, LdAiConfig de
         }
 
 
-        var prompt =
-            parsed.Prompt?.Select(m => new LdAiConfig.Message(InterpolateTemplate(m.Content, mergedVariables), m.Role));
+        var prompt = new List<LdAiConfig.Message>();
+
+        if (parsed.Prompt != null)
+        {
+            for (var i = 0; i < parsed.Prompt.Count; i++)
+            {
+                try
+                {
+                    var content = InterpolateTemplate(parsed.Prompt[i].Content, mergedVariables);
+                    prompt.Add(new LdAiConfig.Message(content, parsed.Prompt[i].Role));
+                }
+                catch (Exception ex)
+                {
+                    _logger.Error($"AI model config prompt has malformed message at index {i}: {ex.Message} (returning default config)");
+                    return new LdAiConfigTracker(_client, key, defaultValue, context);
+                }
+            }
+        }
 
         return new LdAiConfigTracker(_client, key, new LdAiConfig(parsed.Meta?.Enabled ?? false, prompt, parsed.Meta, parsed.Model), context);
+
     }
 
     /// <summary>
diff --git a/pkgs/sdk/server-ai/test/InterpolationTests.cs b/pkgs/sdk/server-ai/test/InterpolationTests.cs
index d6256e9d..8635a390 100644
--- a/pkgs/sdk/server-ai/test/InterpolationTests.cs
+++ b/pkgs/sdk/server-ai/test/InterpolationTests.cs
@@ -1,3 +1,4 @@
+using System;
 using System.Collections.Generic;
 using System.Text.Json;
 using System.Text.Json.Serialization;
@@ -117,6 +118,37 @@ public void TestInterpolationWithArraySectionWorks()
         Assert.Equal("hello world ! ", result);
     }
 
+    [Fact]
+    public void TestInterpolationMalformed()
+    {
+        var mockClient = new Mock<ILaunchDarklyClient>();
+        var mockLogger = new Mock<ILogger>();
+
+
+        // The replacement is done this way because to use string.Format, we'd need to escape the curly braces.
+        var configJson = """
+                         {
+                             "_ldMeta": {"versionKey": "1", "enabled": true},
+                             "model": {},
+                             "prompt": [
+                                 {
+                                     "content": "This is a {{ malformed }]} prompt",
+                                     "role": "System"
+                                 }
+                             ]
+                         }
+                         """;
+
+
+        mockClient.Setup(x =>
+            x.JsonVariation("foo", It.IsAny<Context>(), It.IsAny<LdValue>())).Returns(LdValue.Parse(configJson));
+
+        mockClient.Setup(x => x.GetLogger()).Returns(mockLogger.Object);
+
+        var client = new LdAiClient(mockClient.Object);
+        var tracker = client.ModelConfig("foo", Context.New("key"), LdAiConfig.Disabled, null);
+        Assert.False(tracker.Config.Enabled);
+    }
 
     [Fact]
     public void TestInterpolationWithBasicContext()