АРЧ ЮИ - остановка прогона через гуй

This commit is contained in:
2026-04-18 18:14:30 +03:00
parent 9710d81676
commit 12550606bb
39 changed files with 1783 additions and 93 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -4,8 +4,8 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>NDC AI Normalizer Playground</title>
<script type="module" crossorigin src="/assets/index-CLCo6iY2.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DkvyfMZP.css">
<script type="module" crossorigin src="/assets/index-DWgwfAPc.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-sbJz56_k.css">
</head>
<body>
<div id="root"></div>
@@ -270,6 +270,12 @@ export const apiClient = {
return request(`/eval/run-async/${encodeURIComponent(jobId)}`);
},
async cancelEvalRunAsync(jobId: string): Promise<AsyncEvalRunStatusResponse> {
return request(`/eval/run-async/${encodeURIComponent(jobId)}/cancel`, {
method: "POST"
});
},
async startRun(): Promise<{ ok: boolean; run: RuntimeRun; runId: string; sessionId: string; status: string }> {
return request("/accounting-agent/v1/runs/start", {
method: "POST",
@@ -612,6 +612,14 @@ function CardLaunchIcon() {
);
}
function CardStopIcon() {
return (
<svg className="autoruns-card-stop-svg" viewBox="0 0 16 16" aria-hidden="true" focusable="false">
<rect x="4.2" y="4.2" width="7.6" height="7.6" rx="0.8" />
</svg>
);
}
function GroupChevronIcon({ expanded }: { expanded: boolean }) {
return (
<svg className={expanded ? "autoruns-group-chevron-svg expanded" : "autoruns-group-chevron-svg"} viewBox="0 0 16 16" aria-hidden="true" focusable="false">
@@ -702,6 +710,8 @@ export function AutoRunsHistoryPanel({
const [limitInput, setLimitInput] = useState(String(DEFAULT_FILTERS.limit));
const [autogenCountInput, setAutogenCountInput] = useState(String(DEFAULT_AUTOGEN_SETTINGS.count));
const [autogenPersonalityPromptHeight, setAutogenPersonalityPromptHeight] = useState(160);
const [runningAutogenGenerationId, setRunningAutogenGenerationId] = useState("");
const [autogenStopBusy, setAutogenStopBusy] = useState(false);
const [filtersGroupExpanded, setFiltersGroupExpanded] = useState(true);
const [generationContextGroupExpanded, setGenerationContextGroupExpanded] = useState(true);
const [autogenGroupExpanded, setAutogenGroupExpanded] = useState(true);
@@ -1507,6 +1517,8 @@ export function AutoRunsHistoryPanel({
if (payload.job.status === "completed") {
stopAsyncJobPolling();
setAutogenRunBusy(false);
setAutogenStopBusy(false);
setRunningAutogenGenerationId("");
const finalRunId = payload.job.report_summary?.run_id ?? payload.job.run_id;
await loadHistory({
keepSelection: true,
@@ -1521,11 +1533,25 @@ export function AutoRunsHistoryPanel({
if (payload.job.status === "failed") {
stopAsyncJobPolling();
setAutogenRunBusy(false);
setAutogenStopBusy(false);
setRunningAutogenGenerationId("");
setErrorText(`Запуск прогонов: ${payload.job.error ?? "неизвестная ошибка"}`);
log(`Autogen async run failed: ${payload.job.error ?? "unknown error"}`);
return;
}
if (payload.job.status === "canceled") {
stopAsyncJobPolling();
setAutogenRunBusy(false);
setAutogenStopBusy(false);
setRunningAutogenGenerationId("");
setActiveAsyncJob(null);
await loadHistory({ keepSelection: false });
await loadAutoGenHistory();
log(`Autogen async run canceled: job=${payload.job.job_id}`);
return;
}
stopAsyncJobPolling();
asyncJobPollTimerRef.current = window.setTimeout(() => {
void pollAsyncJobStatus(jobId);
@@ -1533,6 +1559,8 @@ export function AutoRunsHistoryPanel({
} catch (error) {
stopAsyncJobPolling();
setAutogenRunBusy(false);
setAutogenStopBusy(false);
setRunningAutogenGenerationId("");
const message = error instanceof Error ? error.message : String(error);
setErrorText(`Запуск прогонов: ${message}`);
log(`Autogen async status error: ${message}`);
@@ -1579,6 +1607,8 @@ export function AutoRunsHistoryPanel({
});
const liveJob = payload.job;
setRunningAutogenGenerationId(generation.generation_id);
setAutogenStopBusy(false);
setActiveAsyncJob(liveJob);
const liveRunId = toLiveRunId(liveJob.job_id);
const live = buildLiveRunDetail(liveJob, ALL_CASES_ID);
@@ -1602,6 +1632,8 @@ export function AutoRunsHistoryPanel({
setErrorText(`Запуск прогонов: ${message}`);
log(`Autogen run error: ${message}`);
setAutogenRunBusy(false);
setAutogenStopBusy(false);
setRunningAutogenGenerationId("");
}
}, [
analysisDate,
@@ -1616,6 +1648,36 @@ export function AutoRunsHistoryPanel({
stopAsyncJobPolling
]);
const stopAutogenCampaign = useCallback(async () => {
const jobId = activeAsyncJob?.job_id ?? "";
if (!jobId) {
setAutogenRunBusy(false);
setAutogenStopBusy(false);
setRunningAutogenGenerationId("");
setActiveAsyncJob(null);
stopAsyncJobPolling();
return;
}
setAutogenStopBusy(true);
setErrorText("");
try {
const payload = await apiClient.cancelEvalRunAsync(jobId);
stopAsyncJobPolling();
setActiveAsyncJob(null);
setAutogenRunBusy(false);
setAutogenStopBusy(false);
setRunningAutogenGenerationId("");
await loadHistory({ keepSelection: false });
await loadAutoGenHistory();
log(`Autogen async run stopped: job=${payload.job.job_id}`);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
setAutogenStopBusy(false);
setErrorText(`Остановка прогона: ${message}`);
log(`Autogen stop error: ${message}`);
}
}, [activeAsyncJob, loadAutoGenHistory, loadHistory, log, stopAsyncJobPolling]);
const openCommentModal = useCallback((message: AutoRunDialogMessage) => {
if (message.role !== "assistant") return;
const resolvedCaseId = message.case_id ?? selectedCaseId;
@@ -2750,10 +2812,13 @@ export function AutoRunsHistoryPanel({
type="button"
className="autoruns-run-launch-btn"
style={isSavedUserSessionsMode ? { display: "none" } : undefined}
disabled={autogenRunBusy || editableGeneratedQuestions.length === 0 || !selectedAutogenGeneration}
onClick={() => void runAutogenCampaign()}
disabled={
autogenStopBusy ||
(!autogenRunBusy && (editableGeneratedQuestions.length === 0 || !selectedAutogenGeneration))
}
onClick={() => void (autogenRunBusy ? stopAutogenCampaign() : runAutogenCampaign())}
>
{autogenRunBusy ? "Запускаю..." : "Запустить прогон"}
{autogenRunBusy ? (autogenStopBusy ? "Останавливаю..." : "Остановить прогон") : "Запустить прогон"}
</button>
</div>
@@ -2955,7 +3020,10 @@ export function AutoRunsHistoryPanel({
{isSavedUserSessionsMode ? "Сохраненные пользовательские сессии пока пусты." : "История автогенераций пока пустая."}
</p>
) : null}
{visibleAutoGenHistory.slice(0, 30).map((item) => (
{visibleAutoGenHistory.slice(0, 30).map((item) => {
const isRunningThisGeneration = autogenRunBusy && runningAutogenGenerationId === item.generation_id;
const isAnotherGenerationRunning = autogenRunBusy && runningAutogenGenerationId !== item.generation_id;
return (
<article
key={item.generation_id}
className={[
@@ -2971,20 +3039,24 @@ export function AutoRunsHistoryPanel({
<button
type="button"
className="autoruns-saved-session-icon-btn"
disabled={autogenRunBusy}
disabled={autogenStopBusy || isAnotherGenerationRunning}
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
if (isRunningThisGeneration) {
void stopAutogenCampaign();
return;
}
setSelectedAutogenGenerationId(item.generation_id);
void runAutogenCampaign(
item,
selectedAutogenGenerationId === item.generation_id ? editableGeneratedQuestions : item.questions
);
}}
title="Запустить прогон"
aria-label={`Запустить прогон для ${formatAutoGenGenerationTitle(item)}`}
title={isRunningThisGeneration ? "Остановить прогон" : "Запустить прогон"}
aria-label={`${isRunningThisGeneration ? "Остановить прогон" : "Запустить прогон"} для ${formatAutoGenGenerationTitle(item)}`}
>
<CardLaunchIcon />
{isRunningThisGeneration ? <CardStopIcon /> : <CardLaunchIcon />}
</button>
<button
type="button"
@@ -3182,7 +3254,8 @@ export function AutoRunsHistoryPanel({
</>
) : null}
</article>
))}
);
})}
</div>
) : null}
+2 -2
View File
@@ -392,13 +392,13 @@ export interface AutoGenPersonalityCatalogResponse {
export interface AsyncEvalRunCase {
case_id: string;
turns_total: number;
status: "queued" | "running" | "completed" | "failed";
status: "queued" | "running" | "completed" | "failed" | "canceled";
messages: AutoRunDialogMessage[];
}
export interface AsyncEvalRunJob {
job_id: string;
status: "queued" | "running" | "completed" | "failed";
status: "queued" | "running" | "completed" | "failed" | "canceled";
created_at: string;
updated_at: string;
eval_target: AutoRunTarget;
+7
View File
@@ -1627,6 +1627,13 @@ button:disabled {
stroke: none;
}
.autoruns-card-stop-svg {
width: 14px;
height: 14px;
fill: currentColor;
stroke: none;
}
.autoruns-card-chevron-svg.expanded {
transform: rotate(180deg);
}