Files
kjh2064 c41e5063b7 chore: remove kbx-foundation-v36 reference (superseded by v4 implementation)
Removed entire kbx-foundation-v36 directory as it's been replaced by
the new KBX Foundation v4 patterns implemented in this session:
- Registry-driven screen definitions
- Density-aware UI adapter components
- Feature module templates (ShadowRun, Models)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-12 01:39:58 +09:00

56 lines
2.0 KiB
C#

using Dapper;
using FastEndpoints;
using KBX.Shared.Experience;
using Npgsql;
using System.Diagnostics;
namespace KBX.Modules.Common.Ai.Ask;
public sealed record Request(string Question, AiScreenContext Context);
public sealed class Endpoint(IKbxAiAssistantProvider assistant, NpgsqlDataSource dataSource) : Endpoint<Request, AiAnswer>
{
public override void Configure()
{
Post("/api/common/ai/ask");
Permissions("common.ai.use");
}
public override async Task HandleAsync(Request req, CancellationToken ct)
{
if (string.IsNullOrWhiteSpace(req.Question) || req.Question.Length > 2000)
{
AddError(r => r.Question, "질문을 1~2000자로 입력하세요.");
await Send.ErrorsAsync(cancellation: ct);
return;
}
// Host application replaces this with authenticated claims resolution.
var tenantId = Guid.Empty;
var userId = Guid.Empty;
var sw = Stopwatch.StartNew();
var answer = await assistant.AskAsync(tenantId, userId, new AiAskCommand(req.Question.Trim(), req.Context), ct);
sw.Stop();
await using var connection = await dataSource.OpenConnectionAsync(ct);
await connection.ExecuteAsync(new CommandDefinition("""
insert into kbx.ai_interactions
(id, tenant_id, user_id, screen_id, screen_version, capability, result_kind, provider, duration_ms)
values
(@Id, @TenantId, @UserId, @ScreenId, @ScreenVersion, 'explain', @ResultKind, @Provider, @DurationMs)
""", new
{
Id = Guid.NewGuid(),
TenantId = tenantId,
UserId = userId,
req.Context.ScreenId,
req.Context.ScreenVersion,
ResultKind = answer.Proposal is null ? "answer" : "proposal",
Provider = assistant.GetType().Name,
DurationMs = (int)sw.ElapsedMilliseconds,
}, cancellationToken: ct));
await Send.OkAsync(answer, ct);
}
}