АДРЕСНЫЙ РЕЖИМ - авторан история - юи + адресный рендер прогонов в реалтайме

This commit is contained in:
2026-04-09 23:48:32 +03:00
parent cf5ba1afc2
commit fd6764d412
38 changed files with 6179 additions and 777 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-Cbn_mHUl.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BT0bMOoF.css">
<script type="module" crossorigin src="/assets/index-DNcr9aV9.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-qw2gR5-7.css">
</head>
<body>
<div id="root"></div>
+79 -1
View File
@@ -1,4 +1,7 @@
import type {
AsyncEvalRunStartResponse,
AsyncEvalRunStatusResponse,
AutoGenPersonalityCatalogResponse,
AutoGenHistoryResponse,
AutoGenMode,
AutoRunAnnotationsResponse,
@@ -155,6 +158,8 @@ export const apiClient = {
mode?: "standard" | "single-pass-strict";
caseSetFile?: string;
rawQuestions?: string;
evalTarget?: "normalizer" | "assistant_stage1" | "assistant_stage2" | "assistant_p0";
compareWithReportFile?: string;
}): Promise<{ ok: boolean; report: unknown }> {
return request("/eval/run", {
method: "POST",
@@ -176,11 +181,58 @@ export const apiClient = {
useMock: Boolean(input.useMock),
mode: input.mode ?? "standard",
caseSetFile: input.caseSetFile,
rawQuestions: input.rawQuestions
rawQuestions: input.rawQuestions,
eval_target: input.evalTarget,
compare_with_report_file: input.compareWithReportFile
})
});
},
async startEvalRunAsync(input: {
connection: ConnectionState;
prompts: PromptState;
promptVersion?: string;
caseIds?: string[];
useMock?: boolean;
mode?: "standard" | "single-pass-strict";
caseSetFile?: string;
rawQuestions?: string;
evalTarget?: "normalizer" | "assistant_stage1" | "assistant_stage2" | "assistant_p0";
compareWithReportFile?: string;
questions?: string[];
}): Promise<AsyncEvalRunStartResponse> {
return request("/eval/run-async/start", {
method: "POST",
body: JSON.stringify({
normalizeConfig: {
llmProvider: input.connection.llmProvider,
apiKey: input.connection.apiKey,
model: input.connection.model,
baseUrl: input.connection.baseUrl,
temperature: input.connection.temperature,
maxOutputTokens: input.connection.maxOutputTokens,
promptVersion: input.promptVersion,
systemPrompt: input.prompts.systemPrompt,
developerPrompt: input.prompts.developerPrompt,
domainPrompt: input.prompts.domainPrompt,
fewShotExamples: input.prompts.fewShotExamples
},
caseIds: input.caseIds,
useMock: Boolean(input.useMock),
mode: input.mode ?? "standard",
caseSetFile: input.caseSetFile,
rawQuestions: input.rawQuestions,
eval_target: input.evalTarget,
compare_with_report_file: input.compareWithReportFile,
questions: input.questions
})
});
},
async loadEvalRunAsyncStatus(jobId: string): Promise<AsyncEvalRunStatusResponse> {
return request(`/eval/run-async/${encodeURIComponent(jobId)}`);
},
async startRun(): Promise<{ ok: boolean; run: RuntimeRun; runId: string; sessionId: string; status: string }> {
return request("/accounting-agent/v1/runs/start", {
method: "POST",
@@ -321,6 +373,20 @@ export const apiClient = {
});
},
async updateAutoRunAnnotation(input: {
annotation_id: string;
resolved: boolean;
resolved_by?: string;
}): Promise<{ ok: boolean; annotation: AutoRunAnnotationRecord; case_annotation_stats: { count: number; latest_at: string | null; avg_rating: number | null } | null }> {
return request(`/autoruns/annotations/${encodeURIComponent(input.annotation_id)}`, {
method: "PATCH",
body: JSON.stringify({
resolved: input.resolved,
resolved_by: input.resolved_by
})
});
},
async loadAutoRunPostAnalysis(input?: {
run_id?: string;
limit_per_queue?: number;
@@ -359,12 +425,24 @@ export const apiClient = {
return request(`/autoruns/autogen/history${query ? `?${query}` : ""}`);
},
async loadAutoRunAutogenPersonalityCatalog(): Promise<AutoGenPersonalityCatalogResponse> {
return request("/autoruns/autogen/personality-catalog");
},
async generateAutoRunQuestions(input: {
mode: AutoGenMode;
count: number;
domain?: string;
persist_to_eval_cases?: boolean;
generated_by?: string;
llm?: {
llm_provider?: "openai" | "local";
api_key?: string;
model?: string;
base_url?: string;
temperature?: number;
max_output_tokens?: number;
};
context?: {
llm_provider?: string;
model?: string;
File diff suppressed because it is too large Load Diff
@@ -1,3 +1,4 @@
import { useEffect, useState } from "react";
import { PanelFrame } from "./PanelFrame";
import type { ConnectionState } from "../state/types";
@@ -26,6 +27,46 @@ export function ConnectionPanel({
}: ConnectionPanelProps) {
const isLocal = value.llmProvider === "local";
const modelInCatalog = modelOptions.includes(value.model);
const [temperatureInput, setTemperatureInput] = useState(String(value.temperature));
const [maxOutputTokensInput, setMaxOutputTokensInput] = useState(String(value.maxOutputTokens));
useEffect(() => {
setTemperatureInput(String(value.temperature));
}, [value.temperature]);
useEffect(() => {
setMaxOutputTokensInput(String(value.maxOutputTokens));
}, [value.maxOutputTokens]);
const commitTemperatureInput = (raw: string) => {
const normalized = raw.replace(",", ".").trim();
if (!normalized) {
setTemperatureInput(String(value.temperature));
return;
}
const parsed = Number(normalized);
if (!Number.isFinite(parsed)) {
setTemperatureInput(String(value.temperature));
return;
}
onChange({ ...value, temperature: parsed });
setTemperatureInput(String(parsed));
};
const commitMaxOutputTokensInput = (raw: string) => {
const normalized = raw.trim();
if (!normalized) {
setMaxOutputTokensInput(String(value.maxOutputTokens));
return;
}
const parsed = Number.parseInt(normalized, 10);
if (!Number.isFinite(parsed) || parsed <= 0) {
setMaxOutputTokensInput(String(value.maxOutputTokens));
return;
}
onChange({ ...value, maxOutputTokens: parsed });
setMaxOutputTokensInput(String(parsed));
};
return (
<PanelFrame
@@ -108,8 +149,14 @@ export function ConnectionPanel({
<input
type="number"
step="0.1"
value={value.temperature}
onChange={(event) => onChange({ ...value, temperature: Number(event.target.value) })}
value={temperatureInput}
onChange={(event) => setTemperatureInput(event.target.value)}
onBlur={(event) => commitTemperatureInput(event.target.value)}
onKeyDown={(event) => {
if (event.key === "Enter") {
commitTemperatureInput((event.target as HTMLInputElement).value);
}
}}
/>
</label>
@@ -117,8 +164,14 @@ export function ConnectionPanel({
Max output tokens
<input
type="number"
value={value.maxOutputTokens}
onChange={(event) => onChange({ ...value, maxOutputTokens: Number(event.target.value) })}
value={maxOutputTokensInput}
onChange={(event) => setMaxOutputTokensInput(event.target.value)}
onBlur={(event) => commitMaxOutputTokensInput(event.target.value)}
onKeyDown={(event) => {
if (event.key === "Enter") {
commitMaxOutputTokensInput((event.target as HTMLInputElement).value);
}
}}
/>
</label>
</div>
+61 -1
View File
@@ -188,6 +188,9 @@ export interface AutoRunAnnotationRecord {
comment: string;
manual_case_decision: ManualCaseDecision;
annotation_author: string | null;
resolved: boolean;
resolved_at: string | null;
resolved_by: string | null;
created_at: string;
updated_at: string;
context: {
@@ -198,6 +201,8 @@ export interface AutoRunAnnotationRecord {
prompt_version: string | null;
domain: string | null;
query_class: string | null;
question_text: string | null;
answer_text: string | null;
};
}
@@ -243,6 +248,8 @@ export interface AutoRunDialogMessage {
trace_id: string | null;
reply_type: string | null;
message_index: number;
case_id?: string | null;
case_message_index?: number | null;
commented: boolean;
annotation: AutoRunAnnotationRecord | null;
}
@@ -251,7 +258,7 @@ export interface AutoRunDialogResponse {
ok: boolean;
run_id: string;
case_id: string;
source: "assistant_session" | "report_fallback" | "none";
source: "assistant_session" | "report_fallback" | "run_aggregate" | "none";
session_id: string;
messages: AutoRunDialogMessage[];
decomposition: string[];
@@ -320,6 +327,8 @@ export interface AutoGenHistoryRecord {
assistant_prompt_version: string | null;
decomposition_prompt_version: string | null;
prompt_fingerprint: string | null;
autogen_personality_id: string | null;
autogen_personality_prompt: string | null;
} | null;
}
@@ -329,6 +338,57 @@ export interface AutoGenHistoryResponse {
items: AutoGenHistoryRecord[];
}
export interface AutoGenPersonalityDefinition {
id: string;
label: string;
domain: string | null;
default_prompt: string;
source: "built_in" | "capabilities_registry";
}
export interface AutoGenPersonalityCatalogResponse {
ok: boolean;
generated_at: string;
items: AutoGenPersonalityDefinition[];
}
export interface AsyncEvalRunCase {
case_id: string;
turns_total: number;
status: "queued" | "running" | "completed" | "failed";
messages: AutoRunDialogMessage[];
}
export interface AsyncEvalRunJob {
job_id: string;
status: "queued" | "running" | "completed" | "failed";
created_at: string;
updated_at: string;
eval_target: AutoRunTarget;
run_id: string;
case_set_file: string | null;
total_cases: number;
completed_cases: number;
error: string | null;
cases: AsyncEvalRunCase[];
report_summary: {
run_id: string | null;
run_timestamp: string | null;
score_index: number | null;
cases_total: number | null;
} | null;
}
export interface AsyncEvalRunStartResponse {
ok: boolean;
job: AsyncEvalRunJob;
}
export interface AsyncEvalRunStatusResponse {
ok: boolean;
job: AsyncEvalRunJob;
}
export type AssistantFallbackType = "none" | "out_of_scope" | "clarification" | "partial" | "unknown";
export type AssistantReplyType =
| "factual"
+207 -13
View File
@@ -741,10 +741,17 @@ button:disabled {
padding: 10px;
display: grid;
gap: 5px;
transition: background-color 0.2s ease, box-shadow 0.2s ease;
}
.autoruns-run-item.selected {
border-color: var(--line-strong);
background: rgb(var(--rgb-active));
color: rgb(var(--rgb-active-text));
box-shadow: none;
}
.autoruns-run-item.selected .autoruns-run-meta {
color: rgba(var(--rgb-active-text), 0.95);
}
.autoruns-run-head,
@@ -790,7 +797,9 @@ button:disabled {
}
.autoruns-case-item.selected {
border-color: var(--line-strong);
background: rgb(var(--rgb-active));
color: rgb(var(--rgb-active-text));
box-shadow: none;
}
.autoruns-dialog-view {
@@ -831,6 +840,17 @@ button:disabled {
gap: 8px;
}
.autoruns-msg-case-tag {
display: inline-flex;
align-items: center;
border-radius: 999px;
padding: 2px 8px;
font-size: 0.7rem;
line-height: 1;
color: rgb(var(--rgb-active-text));
background: rgba(var(--rgb-active), 0.24);
}
.autoruns-msg p {
margin: 0;
white-space: pre-wrap;
@@ -840,30 +860,106 @@ button:disabled {
.autoruns-comment-icon {
border: none;
background: rgb(var(--rgb-surface-horizontal));
color: var(--text-main);
border-radius: 999px;
min-width: 28px;
min-height: 28px;
padding: 0 8px;
background: transparent;
color: rgb(var(--rgb-text-main));
border-radius: 0;
min-width: 20px;
min-height: 20px;
width: 20px;
height: 20px;
padding: 0;
line-height: 1;
box-shadow: none;
transform: none;
display: inline-flex;
align-items: center;
justify-content: center;
}
.autoruns-comment-icon:hover {
border-color: var(--line-strong);
background: transparent;
color: rgb(var(--rgb-active));
box-shadow: none;
transform: none;
}
.autoruns-comment-icon.commented {
border-color: var(--line-strong);
color: var(--lime-main);
color: rgb(var(--rgb-active-text));
background: transparent;
box-shadow: none;
}
.comment-icon-svg {
width: 20px;
height: 20px;
stroke: currentColor;
stroke-width: 1.75;
stroke-linecap: round;
stroke-linejoin: round;
fill: none;
}
.comment-icon-svg .comment-icon-dot {
fill: currentColor;
}
.comment-icon-svg.commented {
fill: rgb(var(--rgb-active));
stroke: rgb(var(--rgb-active));
}
.autoruns-comment-icon.commented .comment-icon-dot {
fill: rgb(var(--rgb-active-text));
}
.autoruns-resolve-toggle {
border: none;
background: rgb(var(--rgb-surface-focus));
color: rgb(var(--rgb-text-main));
border-radius: 999px;
width: 22px;
height: 22px;
min-width: 22px;
min-height: 22px;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
box-shadow: none;
transform: none;
}
.autoruns-resolve-toggle:hover {
background: rgb(var(--rgb-surface-focus));
color: rgb(var(--rgb-active));
box-shadow: none;
transform: none;
}
.autoruns-resolve-toggle:disabled {
opacity: 0.55;
cursor: wait;
}
.autoruns-resolve-toggle.resolved {
background: rgb(var(--rgb-active));
color: rgb(var(--rgb-active-text));
}
.resolve-icon-svg {
width: 14px;
height: 14px;
fill: none;
stroke: currentColor;
stroke-width: 1.8;
stroke-linecap: round;
stroke-linejoin: round;
}
.resolve-icon-svg.resolved {
fill: currentColor;
}
.autoruns-msg-annotation {
display: grid;
gap: 4px;
@@ -882,6 +978,7 @@ button:disabled {
flex: 1 1 auto;
overflow: auto;
padding-right: 2px;
margin-top: 6px;
}
.autoruns-autogen-list {
@@ -900,6 +997,17 @@ button:disabled {
padding: 8px;
display: grid;
gap: 5px;
cursor: pointer;
}
.autoruns-autogen-item.selected {
background: rgb(var(--rgb-active));
color: rgb(var(--rgb-active-text));
}
.autoruns-autogen-item.selected .autoruns-run-meta,
.autoruns-autogen-item.selected p {
color: rgba(var(--rgb-active-text), 0.95);
}
.autoruns-autogen-item header {
@@ -916,6 +1024,58 @@ button:disabled {
font-size: 0.8rem;
}
.autoruns-generated-questions {
border: none;
border-radius: 10px;
background: rgb(var(--rgb-surface-horizontal));
padding: 8px;
display: grid;
gap: 8px;
}
.autoruns-generated-questions-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.autoruns-generated-questions-list {
display: grid;
gap: 6px;
max-height: 220px;
overflow: auto;
padding-right: 2px;
}
.autoruns-generated-question-item {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 8px;
border: none;
border-radius: 9px;
background: rgb(var(--rgb-surface-focus));
padding: 6px 8px;
font-size: 0.78rem;
}
.autoruns-generated-question-item span {
white-space: pre-wrap;
}
.autoruns-remove-question-btn {
flex: 0 0 auto;
border: none;
border-radius: 7px;
background: rgb(var(--rgb-surface-horizontal));
color: var(--text-main);
min-width: 24px;
height: 24px;
font-size: 0.75rem;
line-height: 1;
}
.autoruns-comment-item {
width: 100%;
text-align: left;
@@ -926,6 +1086,7 @@ button:disabled {
padding: 9px;
display: grid;
gap: 6px;
cursor: pointer;
}
.autoruns-comment-item p {
@@ -936,16 +1097,48 @@ button:disabled {
}
.autoruns-comment-item.selected {
border-color: var(--line-strong);
background: rgb(var(--rgb-active));
color: rgb(var(--rgb-active-text));
}
.autoruns-comment-item.selected p,
.autoruns-comment-item.selected .autoruns-run-meta,
.autoruns-comment-item.selected .muted {
color: rgba(var(--rgb-active-text), 0.94);
}
.autoruns-comment-item.selected .autoruns-resolve-toggle {
background: rgba(var(--rgb-active-text), 0.18);
color: rgb(var(--rgb-active-text));
}
.autoruns-comment-head {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
font-size: 0.75rem;
}
.autoruns-comment-head-actions {
display: inline-flex;
align-items: center;
gap: 8px;
}
.autoruns-comment-filter-row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: end;
gap: 10px;
margin-bottom: 8px;
}
.autoruns-resolved-filter-toggle {
min-height: 38px;
white-space: nowrap;
}
.autoruns-msg.assistant {
margin-right: 12%;
}
@@ -1096,7 +1289,8 @@ button:disabled {
.autoruns-form-grid,
.autoruns-dialog-toolbar,
.autoruns-stats-grid {
.autoruns-stats-grid,
.autoruns-comment-filter-row {
grid-template-columns: 1fr;
}
}