journal/Journal.AI/ServiceCollectionExtensions.cs
Jacob Schmidt 192e6e3891 feat: add AI coaching, conversation persistence, and LLamaSharp integration
- Add Journal.AI project with LLamaSharp-based AI service (Phi-3 model)
- Implement coach sessions (daily check-in, evening review, weekly review)
- Add conversation CRUD with SQLCipher persistence
- AI chat with full conversation history for context-aware replies
- Frontend: CoachPanel, AI stores, conversation stores, side panel UI
- Conversation list with create, rename, and delete support
- Fix Phi-3 output quality (system prompt leaking, token cleanup, JSON filtering)
- Fix CREATEDRAFT kind override in coach sessions

Co-Authored-By: Oz <oz-agent@warp.dev>
2026-03-01 16:07:59 -06:00

74 lines
2.6 KiB
C#

using Journal.Core.Services.Ai;
using Journal.Core.Services.Config;
using Journal.Core.Services.Sidecar;
using Microsoft.Extensions.DependencyInjection;
namespace Journal.AI;
public static class ServiceCollectionExtensions
{
/// <summary>
/// Registers LLamaSharp-based AI and Coach services.
/// Call this AFTER <c>AddFragmentServices()</c> so that <c>IJournalConfigService</c> is available.
/// When the provider is "llamasharp", this replaces the default <c>IAiService</c> registration.
/// </summary>
public static IServiceCollection AddLlamaSharpServices(this IServiceCollection services)
{
// Override IAiService — last registration wins in MS DI
services.AddSingleton<IAiService>(provider =>
{
var config = provider.GetRequiredService<IJournalConfigService>().Current;
if (string.Equals(config.AiProvider, "llamasharp", StringComparison.OrdinalIgnoreCase))
{
try
{
return new LlamaSharpAiService(config);
}
catch (Exception ex)
{
return new DisabledAiService(
provider: "llamasharp",
message: $"LLamaSharp unavailable: {ex.Message}",
healthy: false);
}
}
if (string.Equals(config.AiProvider, "python-sidecar", StringComparison.OrdinalIgnoreCase))
{
try
{
return new PythonSidecarAiService(config);
}
catch (Exception ex)
{
return new DisabledAiService(
provider: "python-sidecar",
message: $"Python AI sidecar unavailable: {ex.Message}",
healthy: false);
}
}
return new DisabledAiService(config.AiProvider);
});
// Register coach service
services.AddSingleton<ICoachService>(provider =>
{
var config = provider.GetRequiredService<IJournalConfigService>().Current;
var ai = provider.GetRequiredService<IAiService>();
if (ai is LlamaSharpAiService llamaAi)
return new LlamaSharpCoachService(llamaAi);
if (string.Equals(config.AiProvider, "none", StringComparison.OrdinalIgnoreCase))
return new DisabledCoachService();
return new DisabledCoachService(
$"Coach requires llamasharp provider (current: {config.AiProvider}).");
});
return services;
}
}