V15 / 313404a015e8
Lilith.cs
Lilith/Agent/Lilith.cs
Changed in this versionCS
using Lilith.Core.Logging;
using Lilith.Core.Ollama;
using Lilith.Core.Providers;
using Lilith.Core.Kokoro;
namespace Lilith.Agent;
public partial class Lilith(Action<LogEntry> onLog, LilithCredentials? credentials = null) : IAsyncDisposable
{
private KokoroTts? _tts;
private readonly SemaphoreSlim _operation = new(1, 1);
private bool _disposed;
private readonly CancellationTokenSource _scheduleStop = new();
private Task? _schedulePump;
private ProviderClient _llmclient = null!;
private bool _workspaceLocked;
private global::Lilith.Core.Workspace.WorkspaceOptions _workspaceOptions = global::Lilith.Core.Workspace.WorkspaceOptions.Default;
public global::Lilith.Core.Workspace.WorkspaceOptions WorkspaceOptions
{
get => _workspaceOptions;
set { if (_workspaceLocked) throw new InvalidOperationException("Workspace configuration can only be chosen before boot."); _workspaceOptions = value ?? throw new ArgumentNullException(nameof(value)); }
}
public global::Lilith.Core.Workspace.WorkspaceSession Workspace => Client.Workspace ?? throw new InvalidOperationException("Workspace unavailable.");
public global::Lilith.Core.Scheduling.ScheduleSession Schedules => Client.Schedules ?? throw new InvalidOperationException("Schedules unavailable.");
private readonly LilithCredentials _credentials = credentials ?? new();
public LilithCredentials Credentials => _credentials;
public Logger Logger { get; } = new(onLog);
internal Func<bool, Task<ProviderClient>>? ProviderFactory { get; set; }
private ProviderClient Client
{
get
{
ObjectDisposedException.ThrowIf(_disposed, this);
return _llmclient ?? throw new InvalidOperationException("Boot the agent before using its conversation.");
}
}
public ChatHistory History => Client.History;
public int ContextMax => Client.ContextMax;
public int CurrentContextTokens => Client.CurrentContextTokens;
private async Task InitializeProviderAsync(bool startServerIfNotRunning)
{
_workspaceLocked = true;
_llmclient = ProviderFactory is { } factory ? await factory(startServerIfNotRunning) : await this.CreateClient(startServerIfNotRunning);
_llmclient.ShowSubagentOutput = _showSubagentOutput;
_llmclient.ConfigureWorkspace(_workspaceOptions);
_llmclient.ConfigurePersonality(PersonalityFile ?? Path.Combine(_workspaceOptions.RootDirectory, ".lilith", "personality.json"),
new(_credentials.AgentName, Voice: _credentials.Tts?.VoiceId ?? "af_heart"));
if (EnableScenarios) _llmclient.ConfigureSchedules(new(Path.Combine(_workspaceOptions.RootDirectory, ".lilith", "schedules"), _workspaceOptions.Mode), ScenarioRoot);
if (EnableScenarios) _llmclient.ConfigureCalendar(new(Path.Combine(_workspaceOptions.RootDirectory, ".lilith", "calendar"), _workspaceOptions.Mode));
if (EnableScenarios) _llmclient.ConfigureHome(global::Lilith.Core.HomeAutomation.HomeOptions.Load(Path.Combine(_workspaceOptions.RootDirectory,".lilith","home"),_workspaceOptions.Mode));
foreach (var tool in Tools) _llmclient.RegisterTool(tool);
if (EnableScenarios) global::Lilith.Core.Scenarios.ScenarioTools.Register(_llmclient, ScenarioRoot);
}
public bool EnableScenarios { get; set; } = true;
public string? ScenarioRoot { get; set; }
/// <summary>Configure before boot. The template is deliberately not enabled.</summary>
public List<global::Lilith.Core.Tools.ILilithTool> Tools { get; } = [new global::Lilith.Core.Tools.CurrentTimeTool(), new global::Lilith.Core.Tools.CurrentWeatherTool()];
private IDisposable EnterOperation(string? bootRequiredMessage = null)
{
var operation = _operation.EnterOrThrow("An agent operation is already in progress.");
try
{
ObjectDisposedException.ThrowIf(_disposed, this);
if (bootRequiredMessage is not null && !IsBooted) throw new InvalidOperationException(bootRequiredMessage);
return operation;
}
catch { operation.Dispose(); throw; }
}
public async ValueTask DisposeAsync()
{
await StopSchedulingAsync();
using var operation = await _operation.EnterAsync();
if (_disposed) return;
_disposed = true;
try { if (_tts is not null) await _tts.DisposeAsync(); }
finally { _llmclient?.Dispose(); IsBooted = false; }
}
}