feat: chat + optmz control panel

This commit is contained in:
thanhnv
2026-07-10 16:26:30 +09:00
parent d882a9dc23
commit 7cea023dce
28 changed files with 1702 additions and 402 deletions
@@ -27,6 +27,15 @@ export class ChatController {
return ok(this.svc.verifyAudit());
}
@Get('history')
history(
@Headers() headers: Record<string, string | string[] | undefined>,
@Query('chatId') chatId?: string,
@Query('limit') limit?: string,
) {
return ok(this.svc.history(actorFromHeaders(headers), chatId || '', Number(limit ?? 50)));
}
@Get('replay')
replay(@Query('chatId') chatId?: string, @Query('turnId') turnId?: string, @Query('tenant') tenant?: string) {
return ok(this.svc.replay(chatId || '', turnId || '', tenant || ''));
@@ -95,6 +95,17 @@ export class ChatService {
return { ok: res.status === 0, output: res.stdout || res.stderr };
}
history(actor: SettingsActor, chatId = '', limit = 50) {
this.requireRead(actor);
const safeLimit = Math.max(1, Math.min(Number.isFinite(limit) ? Math.trunc(limit) : 50, 100));
const args = ['history', '--actor', actor.actor, '--tenant', actor.tenant, '--limit', String(safeLimit)];
if (chatId) args.push('--chat-id', chatId);
const res = runPython(CHAT_CLI, args);
const parsed = parseJson<Record<string, unknown>>(res.stdout);
if (res.status === 0 && parsed?.ok === true) return parsed;
throw new InternalServerErrorException(res.stderr || res.stdout || 'CHAT_HISTORY_FAILED');
}
/**
* Item 3: streaming read-only/analysis turns. The harness emits two NDJSON
* phases — an UNCERTIFIED deterministic draft, then the certified final. We