// TEMPLATE: PromptRepository.cs // PURPOSE: Loads agent system prompts from the agent_prompts table, with in-memory caching. // READS STANDARD: ai-agents-prompt-sources // PLACEHOLDERS: // {{PROJECT_NAMESPACE}} — root namespace of the project // LAST-VERIFIED: 2026-05-19 using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; namespace {{PROJECT_NAMESPACE}}.AI; // --------------------------------------------------------------------------- // Entity — map this to the agent_prompts table (EF Core convention mapping). // Alternatively, configure via Fluent API in your DbContext.OnModelCreating. // --------------------------------------------------------------------------- /// /// EF Core entity for the agent_prompts table. /// Content is immutable once written — use INSERT + flip is_active for versioning. /// public sealed class AgentPrompt { public Guid Id { get; init; } = Guid.CreateVersion7(); public required string AgentKey { get; init; } public int Version { get; init; } public required string Content { get; init; } public bool IsActive { get; set; } public DateTimeOffset CreatedAt { get; init; } = DateTimeOffset.UtcNow; } // --------------------------------------------------------------------------- // Interface // --------------------------------------------------------------------------- /// /// Provides the active system-prompt content for a named agent. /// public interface IPromptRepository { /// /// Returns the content of the currently active prompt for . /// /// /// Thrown when no row with is_active = true exists for . /// Task GetActivePromptAsync(string agentKey, CancellationToken ct = default); } // --------------------------------------------------------------------------- // Implementation // --------------------------------------------------------------------------- // NOTE: Register in Program.cs (or a bootstrap extension): // builder.Services.AddMemoryCache(); // builder.Services.AddScoped(); // // The DbContext must expose: public DbSet AgentPrompts { get; set; } // Replace YourDbContext below with the actual DbContext type of the project. /// /// Reads the active agent prompt from the database with an in-memory cache /// (default TTL: 5 minutes sliding). Cache invalidation is time-based; /// for immediate invalidation after a prompt update call /// IMemoryCache.Remove($"agent_prompt:{agentKey}"). /// public sealed class PromptRepository(YourDbContext db, IMemoryCache cache) : IPromptRepository { private static readonly TimeSpan CacheTtl = TimeSpan.FromMinutes(5); public async Task GetActivePromptAsync(string agentKey, CancellationToken ct = default) { var cacheKey = $"agent_prompt:{agentKey}"; if (cache.TryGetValue(cacheKey, out string? cached) && cached is not null) return cached; var content = await db.AgentPrompts .Where(p => p.AgentKey == agentKey && p.IsActive) .Select(p => p.Content) .FirstOrDefaultAsync(ct); if (content is null) throw new InvalidOperationException( $"No active prompt found for agent '{agentKey}'. " + "Insert a row in agent_prompts with is_active = true for this agent_key."); cache.Set(cacheKey, content, new MemoryCacheEntryOptions { SlidingExpiration = CacheTtl }); return content; } }