Fixed AI Studio discarding answers of the AI written in an unexpected JSON form (#1109)

This commit is contained in:
Thorsten Sommer authored and GitHub committed 2026-10-25 20:13:02 +01:00
1 parent d26139936a
commit c7be04b727
11 files changed
+443 -256

No files matched your search

@@ -45,8 +45,8 @@ internal sealed class LauncherTextsResponse
error = LuaResponseParseError.NONE;
technicalDetails = string.Empty;
var json = LuaResponse.ExtractJson(modelResponse);
if (string.IsNullOrWhiteSpace(json))
var json = ModelAnswerJson.ExtractObject(modelResponse);
if (json.IsWhiteSpace())
{
error = LuaResponseParseError.MISSING_JSON_OBJECT;
return false;
@@ -18,8 +18,8 @@ internal sealed partial class LuaResponse
error = LuaResponseParseError.NONE;
technicalDetails = string.Empty;
var json = ExtractJson(modelResponse);
if (string.IsNullOrWhiteSpace(json))
var json = ModelAnswerJson.ExtractObject(modelResponse);
if (json.IsWhiteSpace())
{
error = LuaResponseParseError.MISSING_JSON_OBJECT;
return false;
@@ -158,53 +158,4 @@ internal sealed partial class LuaResponse
private static bool IsOptionalGuid(string? value, bool allowEmpty) => value is null ||
Guid.TryParse(value, out var parsed) && (allowEmpty || parsed != Guid.Empty);
/// <summary>
/// Reads the first complete JSON object out of a model answer that may carry text around it.
/// </summary>
/// <remarks>
/// Shared with the launcher texts response and the slide deck of the Slide Assistant, which are
/// different shapes but arrive the same way, wrapped in whatever prose the model felt like adding.
/// </remarks>
internal static string ExtractJson(string input)
{
var start = input.IndexOf('{');
if (start < 0)
return string.Empty;
var depth = 0;
var insideString = false;
for (var index = start; index < input.Length; index++)
{
if (input[index] == '"' && !IsEscaped(input, index))
insideString = !insideString;
if (insideString)
continue;
switch (input[index])
{
case '{':
depth++;
break;
case '}':
depth--;
break;
}
if (depth == 0)
return input[start..(index + 1)];
}
return string.Empty;
}
private static bool IsEscaped(string input, int index)
{
var backslashCount = 0;
for (var i = index - 1; i >= 0 && input[i] == '\\'; i--)
backslashCount++;
return backslashCount % 2 == 1;
}
}
@@ -1,5 +1,4 @@
using System.Text.Json;
using System.Text.RegularExpressions;
using AIStudio.Chat;
using AIStudio.Dialogs;
@@ -17,10 +16,6 @@ namespace AIStudio.Assistants.PromptOptimizer;
public partial class AssistantPromptOptimizer : AssistantBaseCore<SettingsDialogPromptOptimizer>
{
private static readonly Regex JSON_CODE_FENCE_REGEX = new(
pattern: """```(?:json)?\s*(?<json>\{[\s\S]*\})\s*```""",
options: RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly JsonSerializerOptions JSON_OPTIONS = new()
{
PropertyNameCaseInsensitive = true,
@@ -384,34 +379,8 @@ public partial class AssistantPromptOptimizer : AssistantBaseCore<SettingsDialog
{
parsedResult = new();
if (TryDeserialize(rawResponse, out parsedResult))
return true;
var codeFenceMatch = JSON_CODE_FENCE_REGEX.Match(rawResponse);
if (codeFenceMatch.Success)
{
var codeFenceJson = codeFenceMatch.Groups["json"].Value;
if (TryDeserialize(codeFenceJson, out parsedResult))
return true;
}
var firstBrace = rawResponse.IndexOf('{');
var lastBrace = rawResponse.LastIndexOf('}');
if (firstBrace >= 0 && lastBrace > firstBrace)
{
var objectText = rawResponse[firstBrace..(lastBrace + 1)];
if (TryDeserialize(objectText, out parsedResult))
return true;
}
return false;
}
private static bool TryDeserialize(string json, out PromptOptimizationResult parsedResult)
{
parsedResult = new();
if (string.IsNullOrWhiteSpace(json))
var json = ModelAnswerJson.ExtractObject(rawResponse);
if (json.IsWhiteSpace())
return false;
try