using Agent.Core.Logging;using Agent.Core.Ollama;namespace Lilith.Agent;public partial class Lilith{ public async Task ChatAsync(string input, List<string>? images = null) { Logger.Write("Lilith", "Lilith: ", LogColors.Yellow); string reply = await _client.StreamReply(input, images); Logger.WriteLine("Lilith", color: LogColors.Yellow); LogTokenCounts(); await SpeakReplyAsync(reply); } public void NewConversation() { _client.SaveCurrentConversation(); _client.StartNewSession(); _client.ClearHistory(); Logger.WriteLine("System", "New conversation started.", LogColors.Cyan); } public void SaveConversation() => _client.SaveCurrentConversation(); public IReadOnlyList<ChatSessionSummary> ListConversations() => _client.ListSavedConversations(); public bool LoadConversation(int displayIndex) => _client.LoadSavedConversation(displayIndex); public bool LoadConversation(string id) => _client.LoadSavedConversation(id); public void HandleHistoryCommand(string input) { var parts = input.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); if (parts.Length == 1) { var sessions = ListConversations(); if (sessions.Count == 0) { Logger.WriteLine("History", "No saved chats found.", LogColors.Gray); return; } Logger.WriteLine("History", "Saved chats:", LogColors.Cyan); for (int i = 0; i < sessions.Count; i++) { var session = sessions[i]; Logger.WriteLine( "History", $"{i + 1}. {session.Title} ({session.UpdatedAt.LocalDateTime:g}, {session.MessageCount} messages)", LogColors.Gray); } Logger.WriteLine("History", "Use /history <number> to load a chat.", LogColors.Gray); return; } if (!int.TryParse(parts[1], out int index)) { Logger.WriteLine("History", "Usage: /history or /history <number>", LogColors.Orange); return; } if (LoadConversation(index)) { Logger.WriteLine("History", $"Loaded saved chat #{index}.", LogColors.Cyan); } else { Logger.WriteLine("History", $"Saved chat #{index} was not found.", LogColors.Orange); } } public void LogTokenCounts() { if (History.Count < 2) { return; } int? promptTokens = History[^2].Tokens; int? completionTokens = History[^1].Tokens; if (promptTokens.HasValue || completionTokens.HasValue) { Logger.WriteLine( "Tokens", $"prompt: {promptTokens ?? 0} completion: {completionTokens ?? 0} total: {(promptTokens ?? 0) + (completionTokens ?? 0)}", LogColors.Gray); } } public async Task<bool> HandleInputAsync(string input) { if (input.Equals("/history", StringComparison.OrdinalIgnoreCase) || input.StartsWith("/history ", StringComparison.OrdinalIgnoreCase)) { return TryCatchExtensions.ReturnTrueTryCatch( () => HandleHistoryCommand(input), new CatchLog("History", "History command failed.", Logger)); } return input.ToLowerInvariant() switch { "exit" => false, "/new" => TryCatchExtensions.ReturnTrueTryCatch( NewConversation, new CatchLog("System", "Failed to start new conversation.", Logger)), _ => await TryCatchExtensions.ReturnTrueTryCatchAsync( () => ChatAsync(input), new CatchLog("Lilith", "Chat failed.", Logger)) }; }}
Documentation
Chat
User-facing chat loop, conversation management, and slash commands.
Lilith.Chat.cs
ChatAsync— streams a user message (optional images) and logs token usage.NewConversation/SaveConversation/LoadConversation— session persistence via Agent-Core chat history.HandleInputAsync— parses commands (exit,/new,/history, etc.) or forwards text toChatAsync.