using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using AI.Interface; using AI.Models; using AI.Models.Form; using AI.Models.SpecialMessages; using YamlDotNet.Core; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; namespace AI.Utils { public static class SpecialMessageTypes { public const string Form = "Form"; public const string ParameterSet = "ParameterSet"; public const string Table = "Table"; public const string ColumnMatch = "ColumnMatch"; public const string WorkflowStatus = "WorkflowStatus"; public const string KnowledgeBase = "KnowledgeBase"; public const string XyzLoadCard = "XyzLoadCard"; public const string GriddingParamCard = "GriddingParamCard"; } /// /// 根据 type + YAML 载荷还原 ISpecialMessage。Form 需 IFormRegistry 根据 formId 取 FormDefinition。 /// formId 缺失或 Definition 不存在时降级为占位或只读文本。 /// public static class SpecialMessageDeserializer { public static ISpecialMessage? Deserialize(string type, string yamlPayload, IFormRegistry? formRegistry = null) { if (string.IsNullOrWhiteSpace(type) || string.IsNullOrWhiteSpace(yamlPayload)) { return null; } try { return type.Trim() switch { SpecialMessageTypes.Form => DeserializeForm(yamlPayload, formRegistry), SpecialMessageTypes.ParameterSet => DeserializeParameterSet(yamlPayload), SpecialMessageTypes.Table => DeserializeTable(yamlPayload), SpecialMessageTypes.ColumnMatch => DeserializeColumnMatch(yamlPayload), SpecialMessageTypes.WorkflowStatus => DeserializeWorkflowStatus(yamlPayload), SpecialMessageTypes.KnowledgeBase => DeserializeKnowledgeBase(yamlPayload), SpecialMessageTypes.XyzLoadCard => DeserializeXyzLoadCard(yamlPayload), SpecialMessageTypes.GriddingParamCard => DeserializeGriddingParamCard(yamlPayload), _ => null }; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"[SpecialMessageDeserializer] 反序列化失败: type={type}, error={ex}"); return null; } } private static readonly IReadOnlyDictionary ShortDescriptionTable = new Dictionary(StringComparer.OrdinalIgnoreCase) { [SpecialMessageTypes.Form] = ("表单", new[] { "title", "formId" }), [SpecialMessageTypes.ParameterSet] = ("参数集", new[] { "title" }), [SpecialMessageTypes.Table] = ("表格", new[] { "title" }), [SpecialMessageTypes.ColumnMatch] = ("列匹配", new[] { "title" }), [SpecialMessageTypes.WorkflowStatus] = ("工作流状态", new[] { "title" }), [SpecialMessageTypes.XyzLoadCard] = ("散点文件加载卡片", new[] { "filePath" }), [SpecialMessageTypes.GriddingParamCard] = ("网格化参数卡片", Array.Empty()), }; /// /// 从特殊消息的 type + YAML 载荷生成发给 LLM 的短描述(如「[已展示:表单「加载散点」]」),便于模型理解上下文。 /// 对于 KnowledgeBase 类型,返回完整 rawContent,以便后续轮次 LLM 能看到查到的知识。 /// public static string GetShortDescriptionForLlm(string type, string payload) { if (string.IsNullOrWhiteSpace(type)) { return "[已展示:未知]"; } if (string.IsNullOrWhiteSpace(payload)) { return $"[已展示:{type}]"; } try { var dict = FromYaml(payload); if (dict == null) { return $"[已展示:{type}]"; } // 知识库条目:把完整内容带给 LLM,作为后续对话的参考 if (string.Equals(type.Trim(), SpecialMessageTypes.KnowledgeBase, StringComparison.OrdinalIgnoreCase)) { var rawContent = GetString(dict, "rawContent"); return string.IsNullOrWhiteSpace(rawContent) ? "[已查询知识库,无匹配内容]" : $"[来自知识库的参考]{System.Environment.NewLine}{rawContent}"; } string typeName = type; string? detail = null; if (ShortDescriptionTable.TryGetValue(type.Trim(), out var row)) { typeName = row.TypeName; foreach (var key in row.DetailKeys) { var value = GetString(dict, key); if (!string.IsNullOrWhiteSpace(value)) { detail = value; break; } } } if (!string.IsNullOrWhiteSpace(detail)) { return $"[已展示:{typeName}「{detail}」]"; } return $"[已展示:{typeName}]"; } catch { return $"[已展示:{type}]"; } } private static ISpecialMessage? DeserializeForm(string yaml, IFormRegistry? formRegistry) { var dict = FromYaml(yaml); if (dict == null) { return null; } string formId = GetString(dict, "formId") ?? string.Empty; FormDefinition? definition = null; if (formRegistry != null && !string.IsNullOrEmpty(formId)) { definition = formRegistry.GetForm(formId); } if (definition == null) { return null; // 降级:无 FormRegistry 或 formId 不存在时不还原表单卡片 } var formMsg = new FormRequestMessage(definition); if (TryGet(dict, "id", out var idObj)) { formMsg.Id = idObj?.ToString() ?? formMsg.Id; } if (TryGet(dict, "submitLabel", out var submitObj)) { formMsg.SubmitLabel = submitObj?.ToString() ?? formMsg.SubmitLabel; } if (TryGet(dict, "fields", out var fieldsObj) && fieldsObj is IEnumerable fieldsList) { foreach (var fe in fieldsList) { if (fe is not Dictionary fieldDict) { continue; } string fid = GetString(fieldDict, "id") ?? string.Empty; var entry = formMsg.FieldsWithValues.FirstOrDefault(f => string.Equals(f.Id, fid, StringComparison.OrdinalIgnoreCase)); if (entry == null) { continue; } entry.CurrentValue = GetString(fieldDict, "currentValue") ?? string.Empty; if (TryGet(fieldDict, "selectedValues", out var svObj) && svObj is IEnumerable svList) { entry.SelectedValues.Clear(); foreach (var v in svList) { if (v?.ToString() is string s) { entry.SelectedValues.Add(s); } } } } } return formMsg; } private static ISpecialMessage? DeserializeParameterSet(string yaml) { var dict = FromYaml(yaml); if (dict == null) { return null; } var param = new ParameterSetMessage(); if (TryGet(dict, "id", out var idObj)) { param.Id = idObj?.ToString() ?? param.Id; } param.Title = GetString(dict, "title") ?? param.Title; if (TryGet(dict, "items", out var itemsObj) && itemsObj is IEnumerable itemsList) { foreach (var item in itemsList) { if (item is not Dictionary id) { continue; } var name = GetString(id, "name") ?? string.Empty; var valueText = GetString(id, "valueText") ?? string.Empty; var description = GetString(id, "description") ?? string.Empty; var fieldTypeStr = GetString(id, "fieldType"); var fieldType = ParameterSetFieldType.Text; if (!string.IsNullOrEmpty(fieldTypeStr) && Enum.TryParse(fieldTypeStr, true, out var ft)) { fieldType = ft; } var options = new List(); if (TryGet(id, "options", out var optObj) && optObj is IEnumerable optList) { foreach (var o in optList) { if (o?.ToString() is string s) { options.Add(s); } } } param.Items.Add(new ParameterSetItem { Name = name, ValueText = valueText, Description = description, FieldType = fieldType, Options = options }); } } return param; } private static ISpecialMessage? DeserializeTable(string yaml) { var dict = FromYaml(yaml); if (dict == null) { return null; } var table = new TableDataMessage(); if (TryGet(dict, "id", out var idObj)) { table.Id = idObj?.ToString() ?? table.Id; } table.Title = GetString(dict, "title") ?? table.Title; table.TotalRowCount = GetInt(dict, "totalRowCount") ?? 0; table.MaxPreviewRows = GetInt(dict, "maxPreviewRows") ?? 50; var columnNames = new List(); if (TryGet(dict, "columnNames", out var cnObj) && cnObj is IEnumerable cnList) { foreach (var c in cnList) { if (c?.ToString() is string s) { columnNames.Add(s); } } } var rows = new List>(); if (TryGet(dict, "rows", out var rowsObj) && rowsObj is IEnumerable rowsList) { foreach (var rowObj in rowsList) { if (rowObj is IEnumerable cells) { rows.Add(cells.Select(c => c?.ToString() ?? string.Empty).ToList()); } } } table.SetData(columnNames, rows, table.TotalRowCount > 0 ? table.TotalRowCount : rows.Count); return table; } private static ISpecialMessage? DeserializeColumnMatch(string yaml) { var dict = FromYaml(yaml); if (dict == null) { return null; } var col = new ColumnMatchMessage(); if (TryGet(dict, "id", out var idObj)) { col.Id = idObj?.ToString() ?? col.Id; } col.Title = GetString(dict, "title") ?? col.Title; var required = new List(); if (TryGet(dict, "requiredColumns", out var rcObj) && rcObj is IEnumerable rcList) { foreach (var c in rcList) { if (c?.ToString() is string s) { required.Add(s); } } } var preview = new List(); if (TryGet(dict, "previewColumns", out var pcObj) && pcObj is IEnumerable pcList) { foreach (var c in pcList) { if (c?.ToString() is string s) { preview.Add(s); } } } col.SetColumns(required, preview); if (TryGet(dict, "mappings", out var mapObj) && mapObj is IEnumerable mapList) { var mappings = new List>(); foreach (var m in mapList) { if (m is not Dictionary md) { continue; } string req = GetString(md, "requiredColumn") ?? string.Empty; string mat = GetString(md, "matchedColumn") ?? string.Empty; mappings.Add(new KeyValuePair(req, mat)); } col.SetMappings(mappings); } return col; } private static ISpecialMessage? DeserializeKnowledgeBase(string yaml) { var dict = FromYaml(yaml); if (dict == null) { return null; } var kb = new KnowledgeBaseMessage(); if (TryGet(dict, "id", out var idObj)) { kb.Id = idObj?.ToString() ?? kb.Id; } kb.KnowledgeId = GetString(dict, "knowledgeId") ?? string.Empty; kb.Version = GetString(dict, "version") ?? "1"; kb.TopicKey = GetString(dict, "topicKey") ?? string.Empty; kb.Query = GetString(dict, "query") ?? string.Empty; kb.RawContent = GetString(dict, "rawContent") ?? string.Empty; return kb; } private static ISpecialMessage? DeserializeWorkflowStatus(string yaml) { var dict = FromYaml(yaml); if (dict == null) { return null; } var wf = new WorkflowStatusMessage(); if (TryGet(dict, "id", out var idObj)) { wf.Id = idObj?.ToString() ?? wf.Id; } wf.Title = GetString(dict, "title") ?? wf.Title; if (TryGet(dict, "steps", out var stepsObj) && stepsObj is IEnumerable stepsList) { var steps = new ObservableCollection(); foreach (var s in stepsList) { if (s is not Dictionary sd) { continue; } var step = new WorkflowStepModel { Id = GetString(sd, "id") ?? string.Empty, DisplayName = GetString(sd, "displayName") ?? string.Empty, Order = GetInt(sd, "order") ?? 0, OutputResult = GetString(sd, "outputResult"), Thought = GetString(sd, "thought") }; var statusStr = GetString(sd, "status"); if (!string.IsNullOrEmpty(statusStr) && Enum.TryParse(statusStr, true, out var st)) { step.Status = st; } steps.Add(step); } wf.Steps = steps; } return wf; } private static ISpecialMessage? DeserializeXyzLoadCard(string yaml) { var dict = FromYaml(yaml); if (dict == null) return null; var card = new AI.Models.SpecialMessages.XyzLoadCardMessage(); if (TryGet(dict, "id", out var idObj)) card.Id = idObj?.ToString() ?? card.Id; card.FilePath = GetString(dict, "filePath") ?? string.Empty; card.MatchButtonLabel = GetString(dict, "matchButtonLabel") ?? "确认匹配"; var phaseInt = GetInt(dict, "phase") ?? 0; card.Phase = (AI.Models.SpecialMessages.XyzLoadPhase)phaseInt; // 还原表格预览 if (TryGet(dict, "tablePreview", out var tableObj) && tableObj is Dictionary tableDict) { var table = new AI.Models.SpecialMessages.TableDataMessage(); table.Title = GetString(tableDict, "title") ?? "数据预览"; table.TotalRowCount = GetInt(tableDict, "totalRowCount") ?? 0; table.MaxPreviewRows = GetInt(tableDict, "maxPreviewRows") ?? 50; var colNames = new List(); if (TryGet(tableDict, "columnNames", out var cnObj) && cnObj is IEnumerable cnList) foreach (var c in cnList) if (c?.ToString() is string s) colNames.Add(s); var rows = new List>(); if (TryGet(tableDict, "rows", out var rowsObj) && rowsObj is IEnumerable rowsList) foreach (var rowObj in rowsList) if (rowObj is IEnumerable cells) rows.Add(cells.Select(c => c?.ToString() ?? string.Empty).ToList()); table.SetData(colNames, rows, table.TotalRowCount > 0 ? table.TotalRowCount : rows.Count); card.TablePreview = table; } // 还原列头匹配字段 if (TryGet(dict, "columnMatchFields", out var fieldsObj) && fieldsObj is IEnumerable fieldsList) { var definitionTitle = GetString(dict, "columnMatchDefinitionTitle") ?? "列头匹配"; var definitionTarget = GetString(dict, "columnMatchDefinitionSubmitTarget") ?? "GriddingModuleMatchColumns"; var formFields = new List(); foreach (var fe in fieldsList) { if (fe is not Dictionary fd) continue; var fid = GetString(fd, "id") ?? string.Empty; var label = GetString(fd, "label") ?? fid; var currentValue = GetString(fd, "currentValue") ?? string.Empty; var options = new List(); if (TryGet(fd, "options", out var optObj) && optObj is IEnumerable optList) foreach (var o in optList) if (o?.ToString() is string s) options.Add(s); formFields.Add(new AI.Models.Form.FormField { Id = fid, Label = label, Type = AI.Models.Form.FormFieldType.Choice, Options = options, Required = true, }); card.ColumnMatchFields.Add(new AI.Models.Form.FormFieldEntry { Id = fid, Label = label, Type = AI.Models.Form.FormFieldType.Choice, Options = options, Required = true, CurrentValue = currentValue, }); } if (formFields.Count > 0) { card.ColumnMatchDefinition = new AI.Models.Form.FormDefinition { Id = "gridding-match-columns", Title = definitionTitle, SubmitTarget = definitionTarget, SubmitLabel = "确认匹配", Fields = formFields, }; } } return card; } private static ISpecialMessage? DeserializeGriddingParamCard(string yaml) { var dict = FromYaml(yaml); if (dict == null) return null; var card = new AI.Models.SpecialMessages.GriddingParamCardMessage(); if (TryGet(dict, "id", out var idObj)) card.Id = idObj?.ToString() ?? card.Id; var phaseInt = GetInt(dict, "phase") ?? 0; card.Phase = (AI.Models.SpecialMessages.GriddingParamCardPhase)phaseInt; card.StatusMessage = GetString(dict, "statusMessage") ?? string.Empty; card.GenerateButtonLabel = GetString(dict, "generateButtonLabel") ?? "生成"; if (TryGet(dict, "items", out var itemsObj) && itemsObj is IEnumerable itemsList) { foreach (var item in itemsList) { if (item is not Dictionary id) continue; var name = GetString(id, "name") ?? string.Empty; var valueText = GetString(id, "valueText") ?? string.Empty; var description = GetString(id, "description") ?? string.Empty; var fieldTypeStr = GetString(id, "fieldType"); var fieldType = AI.Models.SpecialMessages.ParameterSetFieldType.Text; if (!string.IsNullOrEmpty(fieldTypeStr) && Enum.TryParse(fieldTypeStr, true, out var ft)) fieldType = ft; var options = new List(); if (TryGet(id, "options", out var optObj) && optObj is IEnumerable optList) foreach (var o in optList) if (o?.ToString() is string s) options.Add(s); card.Items.Add(new AI.Models.SpecialMessages.ParameterSetItem { Name = name, ValueText = valueText, Description = description, FieldType = fieldType, Options = options }); } } return card; } private static readonly IDeserializer YamlDeserializer = new DeserializerBuilder() .WithNamingConvention(CamelCaseNamingConvention.Instance) .IgnoreUnmatchedProperties() .Build(); private static Dictionary? FromYaml(string yaml) { try { return YamlDeserializer.Deserialize>(yaml); } catch (YamlException ex) { System.Diagnostics.Debug.WriteLine($"[SpecialMessageDeserializer] YAML 解析失败: {ex.Message}"); return null; } } private static string? GetString(Dictionary dict, string key) { if (!TryGet(dict, key, out var v)) { return null; } return v?.ToString(); } private static int? GetInt(Dictionary dict, string key) { if (!TryGet(dict, key, out var v)) { return null; } if (v is int i) { return i; } if (v is long l) { return (int)l; } if (v != null && int.TryParse(v.ToString(), out var n)) { return n; } return null; } private static bool TryGet(Dictionary dict, string key, out object? value) { value = null; var keyEq = StringComparer.OrdinalIgnoreCase; foreach (var kv in dict) { if (keyEq.Equals(kv.Key?.ToString(), key)) { value = kv.Value; return true; } } return false; } } }