You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kev/Drawer/AI/Utils/FormSchemaYamlGenerator.cs

147 lines
5.7 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System.Collections.Generic;
using System;
using AI.Models.Form;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace AI.Utils
{
/// <summary>
/// 将表单定义序列化为 YAML供 LLM 上下文、追踪、调试等使用。
/// </summary>
public static class FormSchemaYamlGenerator
{
/// <summary>
/// System 消息中「表单 Schema YAML」块的开始标记存在则整块替换避免重复追加
/// </summary>
public const string FormsYamlBlockStart = "\n\n# --- 当前可用表单 Schema (YAML) ---\n";
/// <summary>
/// System 消息中「表单 Schema YAML」块的结束标记。
/// </summary>
public const string FormsYamlBlockEnd = "\n# --- 结束 ---";
/// <summary>
/// 将现有 System 内容与新的表单 YAML 合并:若已包含标记块则替换该块,否则在末尾追加。
/// </summary>
public static string MergeFormsYamlIntoSystemContent(string currentSystemContent, string formsYaml)
{
var block = FormsYamlBlockStart + formsYaml + FormsYamlBlockEnd;
if (string.IsNullOrEmpty(currentSystemContent))
return block.TrimStart('\n');
if (currentSystemContent.Contains(FormsYamlBlockStart))
{
int start = currentSystemContent.IndexOf(FormsYamlBlockStart, StringComparison.Ordinal);
int end = currentSystemContent.IndexOf(FormsYamlBlockEnd, start, StringComparison.Ordinal);
if (end >= 0)
{
end += FormsYamlBlockEnd.Length;
}
else
{
end = currentSystemContent.Length;
}
return currentSystemContent.Remove(start, end - start).Insert(start, block);
}
return currentSystemContent + block;
}
/// <summary>
/// 将当前可用表单定义生成为 YAML 字符串。
/// </summary>
public static string ToYaml(IEnumerable<FormDefinition> forms)
{
var schema = new FormsSchemaYaml
{
Forms = BuildForms(forms)
};
var serializer = new SerializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull | DefaultValuesHandling.OmitDefaults)
.Build();
// 保留顶部说明注释(内容不参与结构序列化)。
return "# 当前可用表单 Schema供助手了解可展示的表单与字段\n" + serializer.Serialize(schema);
}
private static List<FormYaml> BuildForms(IEnumerable<FormDefinition>? forms)
{
var list = new List<FormYaml>();
foreach (var form in forms ?? [])
{
var formYaml = new FormYaml
{
Id = form.Id,
Title = form.Title,
SubmitTarget = form.SubmitTarget,
SubmitLabel = form.SubmitLabel,
Fields = BuildFields(form.Fields)
};
list.Add(formYaml);
}
return list;
}
private static List<FieldYaml> BuildFields(IReadOnlyList<FormField>? fields)
{
var list = new List<FieldYaml>();
foreach (var f in fields ?? [])
{
list.Add(new FieldYaml
{
Id = f.Id,
Label = f.Label,
Description = string.IsNullOrEmpty(f.Description) ? null : f.Description,
Type = f.Type.ToString(),
Required = f.Required ? true : null,
Default = f.DefaultValue == null ? null : f.DefaultValue.ToString(),
Min = f.Min,
Max = f.Max,
Step = f.Step,
MaxLength = f.MaxLength,
Placeholder = string.IsNullOrEmpty(f.Placeholder) ? null : f.Placeholder,
Options = (f.Options != null && f.Options.Count > 0) ? new List<string>(f.Options) : null,
DefaultValues = (f.DefaultValues != null && f.DefaultValues.Count > 0) ? new List<string>(f.DefaultValues) : null
});
}
return list;
}
private sealed class FormsSchemaYaml
{
public List<FormYaml> Forms { get; init; } = [];
}
private sealed class FormYaml
{
public string? Id { get; init; }
public string? Title { get; init; }
public string? SubmitTarget { get; init; }
public string? SubmitLabel { get; init; }
public List<FieldYaml> Fields { get; init; } = [];
}
private sealed class FieldYaml
{
public string? Id { get; init; }
public string? Label { get; init; }
public string? Description { get; init; }
public string? Type { get; init; }
// 仅在 true 时输出
public bool? Required { get; init; }
[YamlMember(Alias = "default")]
public string? Default { get; init; }
public double? Min { get; init; }
public double? Max { get; init; }
public double? Step { get; init; }
public int? MaxLength { get; init; }
public string? Placeholder { get; init; }
public List<string>? Options { get; init; }
public List<string>? DefaultValues { get; init; }
}
}
}