using Agent.Core.Logging;using Agent.Core.Ollama;namespace Lilith.Agent;public partial class Lilith{ public async Task<string?> 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(); return 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 ContinueRunning, string? AssistantReply)> HandleInputAsync(string input) { if (input.Equals("/history", StringComparison.OrdinalIgnoreCase) || input.StartsWith("/history ", StringComparison.OrdinalIgnoreCase)) { bool ok = TryCatchExtensions.ReturnTrueTryCatch( () => HandleHistoryCommand(input), new CatchLog("History", "History command failed.", Logger)); return (ok, null); } return input.ToLowerInvariant() switch { "exit" => (false, null), "/new" => ( TryCatchExtensions.ReturnTrueTryCatch( NewConversation, new CatchLog("System", "Failed to start new conversation.", Logger)), null), _ => await HandleChatInputAsync(input) }; } private async Task<(bool ContinueRunning, string? AssistantReply)> HandleChatInputAsync(string input) { try { string? reply = await ChatAsync(input); return (true, reply); } catch (Exception ex) { Logger.WriteLine("Lilith", $"Chat failed: [{ex.GetType().Name}] {ex.Message}", LogColors.Red); return (true, null); } }}
Documentation
Chat
User-facing chat loop, conversation management, and slash commands.
Lilith.Chat.cs
ChatAsync— streams a user message (optional images), logs token usage, and returns the assistant reply (for console TTS).NewConversation/SaveConversation/LoadConversation— session persistence via Agent-Core chat history.HandleInputAsync— parses commands (exit,/new,/history, etc.) or forwards text toChatAsync; returns whether to keep running and any assistant reply to speak.