using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using System;
using System.Linq;
using AI.Models.Store;
using AI.Utils;
namespace AI.Models
{
///
/// 表示一个聊天会话,包含会话信息和历史记录。
/// 会话存储(Store)为唯一事实来源;History 仅作兼容,发给 LLM 的历史由 GetHistoryForLlm 从 Store 生成。
///
public class ChatSession
{
///
/// 会话唯一标识符
///
public string Id { get; }
///
/// 会话标题(通常从第一条用户消息生成)
///
public string Title { get; set; }
///
/// 会话创建时间
///
public DateTime CreatedAt { get; }
///
/// 最后更新时间
///
public DateTime UpdatedAt { get; private set; }
///
/// 对话历史记录(兼容保留;发给 LLM 的请求应使用 GetHistoryForLlm 从 Store 生成)
///
public ChatHistory History { get; }
///
/// 会话级结构化存储,唯一事实来源。UI 与 Prompt 视图均由此派生。
///
public ConversationStore Store { get; }
///
/// 消息数量
///
public int MessageCount => History.Count;
///
/// 用户消息数量(用于判断是否是第一条用户消息)
///
private int _userMessageCount = 0;
///
/// 当前会话的工作模式
///
public WorkflowMode WorkflowMode { get; set; } = WorkflowMode.Ask;
///
/// 初始化新的聊天会话
///
/// 会话ID,如果为null则自动生成GUID
/// 会话标题
public ChatSession(string? id = null, string? title = null)
{
Id = id ?? Guid.NewGuid().ToString();
Title = title ?? "新会话";
CreatedAt = DateTime.Now;
UpdatedAt = DateTime.Now;
History = new ChatHistory();
Store = new ConversationStore();
}
///
/// 从持久化数据还原的会话(用于加载已保存的会话文件)
///
public ChatSession(string id, string title, DateTime createdAt, DateTime updatedAt, WorkflowMode workflowMode, ConversationStore store)
{
Id = id ?? Guid.NewGuid().ToString();
Title = title ?? "新会话";
CreatedAt = createdAt;
UpdatedAt = updatedAt;
History = new ChatHistory();
Store = store ?? new ConversationStore();
WorkflowMode = workflowMode;
_userMessageCount = Store.Entries.OfType().Count(t => t.Role == AuthorRole.User);
}
///
/// 更新会话的最后更新时间
///
public void UpdateTimestamp()
{
UpdatedAt = DateTime.Now;
}
///
/// 添加用户消息(同时写入 Store,作为真相源)
///
public void AddUserMessage(string message)
{
History.AddUserMessage(message);
Store.AppendText(AuthorRole.User, message);
_userMessageCount++;
UpdateTimestamp();
if (_userMessageCount == 1 && Title == "新会话")
{
Title = message.Length > 30 ? message.Substring(0, 30) + "..." : message;
}
}
///
/// 添加助手消息(同时写入 Store)
///
public void AddAssistantMessage(string message)
{
History.AddAssistantMessage(message);
Store.AppendText(AuthorRole.Assistant, message);
UpdateTimestamp();
}
///
/// 向会话存储追加一条特殊消息(不写入 History)。供 ViewModel 在展示表单/参数集/表格/列匹配时调用。
///
public void AppendSpecialEntry(string type, string yamlPayload)
{
Store.AppendSpecial(type, yamlPayload ?? string.Empty);
UpdateTimestamp();
}
///
/// 从会话存储生成发给 LLM 的 ChatHistory:仅包含 User/Assistant 文本及可选的 System(guidePrompt)。
/// 特殊消息不追加原始 YAML,可替换为一句短占位(如「[已展示表单:xxx]」)。
///
/// 可选;若提供则在开头插入一条 System 消息
/// 供 KernelService 使用的 ChatHistory,不含原始 YAML
public ChatHistory GetHistoryForLlm(string? guidePrompt = null)
{
var history = new ChatHistory();
if (!string.IsNullOrWhiteSpace(guidePrompt))
{
history.AddSystemMessage(guidePrompt);
}
foreach (var entry in Store.Entries)
{
switch (entry)
{
case Store.TextConversationEntry text:
AddTextEntry(history, text, guidePrompt);
break;
case Store.SpecialConversationEntry special:
// 卡片展示记录仅用于 UI 重建,不注入 LLM 历史,避免模型从占位文本中学到错误的输出模式
break;
}
}
return history;
}
///
/// 将 Store 中的一条文本条目按角色加入 ChatHistory,供 GetHistoryForLlm 使用。
///
private static void AddTextEntry(ChatHistory history, Store.TextConversationEntry text, string? guidePrompt)
{
if (text.Role == AuthorRole.System)
{
if (string.IsNullOrWhiteSpace(guidePrompt))
{
history.AddSystemMessage(text.Content);
}
}
else if (text.Role == AuthorRole.User)
{
history.AddUserMessage(text.Content);
}
else if (text.Role == AuthorRole.Assistant)
{
history.AddAssistantMessage(text.Content);
}
}
///
/// 添加系统消息
///
public void AddSystemMessage(string message)
{
History.AddSystemMessage(message);
Store.AppendText(AuthorRole.System, message);
UpdateTimestamp();
}
///
/// 仅在需要时确保 History 中存在一条 System(guidePrompt)。不再写入 formsYaml 或摘要。
/// 发给 LLM 的 System 由 GetHistoryForLlm(guidePrompt) 在生成时插入,调用方传入 guidePrompt 即可。
///
/// 引导提示词
/// 已忽略,保留参数以兼容现有调用
public void EnsureFormsYamlInSystem(string guidePrompt, string formsYaml)
{
if (string.IsNullOrWhiteSpace(guidePrompt))
{
return;
}
if (History.Count == 0 || History[0].Role != AuthorRole.System)
{
History.Insert(0, new ChatMessageContent(AuthorRole.System, guidePrompt));
}
UpdateTimestamp();
}
}
}