ЮИ - Упростить карточки автопрогонов до названия даты id режима и типа

This commit is contained in:
2026-04-17 14:09:38 +03:00
parent 9f0f7f3e79
commit 6223062b37
17 changed files with 896 additions and 37 deletions
+2
View File
@@ -2,6 +2,7 @@ import path from "path";
export const BACKEND_ROOT = path.resolve(__dirname, "..");
export const MODULE_ROOT = path.resolve(BACKEND_ROOT, "..");
export const PROJECT_ROOT = path.resolve(MODULE_ROOT, "..");
function toBooleanFlag(value: string | undefined, defaultValue: boolean): boolean {
if (!value || value.trim() === "") {
@@ -192,6 +193,7 @@ export const SHARED_LLM_CONNECTION_FILE = path.resolve(DATA_DIR, "shared_llm_con
export const PROMPTS_DIR = path.resolve(MODULE_ROOT, "prompts");
export const REPORTS_DIR = path.resolve(MODULE_ROOT, "reports");
export const ARTIFACTS_DIR = path.resolve(PROJECT_ROOT, "artifacts");
export const EVAL_DATASETS_DIR = path.resolve(MODULE_ROOT, "eval_cases");
export const SCHEMAS_DIR = path.resolve(BACKEND_ROOT, "src", "schemas");
export const ARCH_EXPORT_2020_DIR = path.resolve(MODULE_ROOT, "..", "docs", "ARCH", "2020экспорт");
+125 -3
View File
@@ -3,15 +3,21 @@ import path from "path";
import { Router } from "express";
import iconv from "iconv-lite";
import {
ARTIFACTS_DIR,
ASSISTANT_SESSIONS_DIR,
AUTORUN_ANNOTATIONS_FILE,
AUTORUN_GENERATOR_HISTORY_FILE,
EVAL_CASES_DIR,
EVAL_DATASETS_DIR,
MANUAL_CASE_DECISION_SCHEMA_FILE,
PROJECT_ROOT,
REPORTS_DIR
} from "../config";
import type { AppServices } from "../serverContext";
import {
findLatestAgentSemanticAcceptanceSummary,
readAgentSemanticSpecSummaryFromFile
} from "../services/agentSemanticRunRegistry";
import { ApiError, ok } from "../utils/http";
import { loadCapabilitiesRegistry, resolveNearestCapabilityGroup, type CapabilityGroup } from "../services/capabilitiesRegistry";
import { OpenAIResponsesClient } from "../services/openaiResponsesClient";
@@ -197,6 +203,34 @@ interface AutoGenHistoryRecord {
agent_focus?: string | null;
architecture_phase?: string | null;
source_spec_file?: string | null;
scenario_id?: string | null;
semantic_tags?: string[] | null;
latest_acceptance?: {
scenario_id: string;
output_dir: string;
relative_output_dir: string;
final_status: string | null;
final_status_reason: string | null;
review_overall_status: string | null;
acceptance_gate_passed: boolean | null;
critical_path_green: boolean | null;
unresolved_p0_count: number | null;
unresolved_p1_count: number | null;
unresolved_p2_count: number | null;
steps_total: number | null;
steps_passed: number | null;
steps_with_warning: number | null;
steps_failed: number | null;
updated_at: string | null;
invariants: {
direct_answer_ok: boolean | null;
temporal_honesty_ok: boolean | null;
selected_object_continuity_ok: boolean | null;
truth_gate_ok: boolean | null;
human_answer_quality_ok: boolean | null;
meta_context_integrity_ok: boolean | null;
};
} | null;
} | null;
}
@@ -375,7 +409,18 @@ function readAutoGenHistory(): AutoGenHistoryRecord[] {
? repairAutogenMojibake(String(toRecord(item.context)?.agent_focus))
: null,
architecture_phase: toStringSafe(toRecord(item.context)?.architecture_phase),
source_spec_file: toStringSafe(toRecord(item.context)?.source_spec_file)
source_spec_file: toStringSafe(toRecord(item.context)?.source_spec_file),
scenario_id: toStringSafe(toRecord(item.context)?.scenario_id),
semantic_tags: Array.isArray(toRecord(item.context)?.semantic_tags)
? Array.from(
new Set(
(toRecord(item.context)?.semantic_tags as unknown[])
.map((tag) => toStringSafe(tag))
.filter((tag): tag is string => Boolean(tag))
)
)
: null,
latest_acceptance: null
}
: null
}))
@@ -394,6 +439,81 @@ function writeAutoGenHistory(records: AutoGenHistoryRecord[]): void {
fs.writeFileSync(AUTORUN_GENERATOR_HISTORY_FILE, JSON.stringify(records, null, 2), "utf-8");
}
function isAgentSemanticHistoryRecord(record: AutoGenHistoryRecord): boolean {
if (record.context?.agent_run === true) {
return true;
}
if (record.context?.saved_case_set_kind === "agent_semantic_scenario") {
return true;
}
return typeof record.title === "string" && record.title.trim().toUpperCase().startsWith("AGENT");
}
function hydrateAutoGenHistoryForApi(records: AutoGenHistoryRecord[]): AutoGenHistoryRecord[] {
const specSummaryCache = new Map<string, ReturnType<typeof readAgentSemanticSpecSummaryFromFile>>();
const acceptanceCache = new Map<string, ReturnType<typeof findLatestAgentSemanticAcceptanceSummary>>();
const readSpecSummary = (sourceSpecFile: string | null | undefined) => {
const normalizedPath = toStringSafe(sourceSpecFile);
if (!normalizedPath) {
return null;
}
if (specSummaryCache.has(normalizedPath)) {
return specSummaryCache.get(normalizedPath) ?? null;
}
const summary = readAgentSemanticSpecSummaryFromFile(normalizedPath);
specSummaryCache.set(normalizedPath, summary);
return summary;
};
const readAcceptanceSummary = (scenarioId: string | null | undefined) => {
const normalizedScenarioId = toStringSafe(scenarioId);
if (!normalizedScenarioId) {
return null;
}
if (acceptanceCache.has(normalizedScenarioId)) {
return acceptanceCache.get(normalizedScenarioId) ?? null;
}
const summary = findLatestAgentSemanticAcceptanceSummary({
artifactsRootDir: path.resolve(ARTIFACTS_DIR, "domain_runs"),
repoRootDir: PROJECT_ROOT,
scenarioId: normalizedScenarioId
});
acceptanceCache.set(normalizedScenarioId, summary);
return summary;
};
return records.map((record) => {
if (!isAgentSemanticHistoryRecord(record)) {
return record;
}
const specSummary = readSpecSummary(record.context?.source_spec_file);
const scenarioId = toStringSafe(record.context?.scenario_id) ?? specSummary?.scenario_id ?? null;
const semanticTags =
Array.isArray(record.context?.semantic_tags) && record.context?.semantic_tags.length > 0
? Array.from(new Set(record.context?.semantic_tags.map((item) => String(item).trim()).filter((item) => item.length > 0)))
: specSummary?.semantic_tags ?? [];
const latestAcceptance = readAcceptanceSummary(scenarioId);
return {
...record,
context: {
...(record.context ?? {
llm_provider: null,
model: null,
assistant_prompt_version: null,
decomposition_prompt_version: null,
prompt_fingerprint: null,
autogen_personality_id: null,
autogen_personality_prompt: null
}),
scenario_id: scenarioId,
semantic_tags: semanticTags,
latest_acceptance: latestAcceptance
}
};
});
}
function readEvalDatasetCases(filePath: string): Array<Record<string, unknown>> {
try {
const parsed = JSON.parse(fs.readFileSync(filePath, "utf-8")) as unknown;
@@ -2416,9 +2536,11 @@ export function buildAutoRunsRouter(services: AppServices, openaiClient = new Op
const rawMode = toStringSafe((req.query as Record<string, unknown>).mode);
const includeAllModes = !rawMode || !isAutoGenMode(rawMode);
const modeFilter = (rawMode as AutoGenMode | null) ?? "codex_creative";
const items = readAutoGenHistory()
const items = hydrateAutoGenHistoryForApi(
readAutoGenHistory()
.filter((item) => (includeAllModes ? true : item.mode === modeFilter))
.slice(0, limit);
.slice(0, limit)
);
ok(res, {
ok: true,
@@ -0,0 +1,208 @@
import fs from "fs";
import path from "path";
interface AgentSemanticSpecSummary {
scenario_id: string;
domain: string | null;
title: string | null;
semantic_tags: string[];
steps_total: number;
}
interface AgentSemanticInvariantSummary {
direct_answer_ok: boolean | null;
temporal_honesty_ok: boolean | null;
selected_object_continuity_ok: boolean | null;
truth_gate_ok: boolean | null;
human_answer_quality_ok: boolean | null;
meta_context_integrity_ok: boolean | null;
}
export interface AgentSemanticAcceptanceSummary {
scenario_id: string;
output_dir: string;
relative_output_dir: string;
final_status: string | null;
final_status_reason: string | null;
review_overall_status: string | null;
acceptance_gate_passed: boolean | null;
critical_path_green: boolean | null;
unresolved_p0_count: number | null;
unresolved_p1_count: number | null;
unresolved_p2_count: number | null;
steps_total: number | null;
steps_passed: number | null;
steps_with_warning: number | null;
steps_failed: number | null;
updated_at: string | null;
invariants: AgentSemanticInvariantSummary;
}
function toRecord(value: unknown): Record<string, unknown> | null {
if (!value || typeof value !== "object" || Array.isArray(value)) {
return null;
}
return value as Record<string, unknown>;
}
function toStringSafe(value: unknown): string | null {
if (typeof value !== "string") {
return null;
}
const trimmed = value.trim();
return trimmed.length > 0 ? trimmed : null;
}
function toBooleanSafe(value: unknown): boolean | null {
if (typeof value === "boolean") {
return value;
}
if (typeof value === "string") {
const lowered = value.trim().toLowerCase();
if (["1", "true", "yes", "on"].includes(lowered)) return true;
if (["0", "false", "no", "off"].includes(lowered)) return false;
}
return null;
}
function toNumberSafe(value: unknown): number | null {
if (typeof value === "number" && Number.isFinite(value)) {
return value;
}
if (typeof value === "string" && value.trim().length > 0) {
const parsed = Number(value);
return Number.isFinite(parsed) ? parsed : null;
}
return null;
}
function readJsonFile(filePath: string): unknown {
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
}
function normalizeSemanticTags(rawSteps: unknown): string[] {
if (!Array.isArray(rawSteps)) {
return [];
}
const tags = new Set<string>();
for (const rawStep of rawSteps) {
const step = toRecord(rawStep);
if (!step || !Array.isArray(step.semantic_tags)) {
continue;
}
for (const rawTag of step.semantic_tags) {
const tag = toStringSafe(rawTag);
if (tag) {
tags.add(tag);
}
}
}
return Array.from(tags).sort((left, right) => left.localeCompare(right));
}
export function readAgentSemanticSpecSummaryFromFile(sourceSpecFile: string | null | undefined): AgentSemanticSpecSummary | null {
const normalizedPath = toStringSafe(sourceSpecFile);
if (!normalizedPath || !fs.existsSync(normalizedPath)) {
return null;
}
try {
const parsed = readJsonFile(normalizedPath);
const record = toRecord(parsed);
if (!record) {
return null;
}
const scenarioId = toStringSafe(record.scenario_id);
if (!scenarioId) {
return null;
}
const semanticTags = normalizeSemanticTags(record.steps);
return {
scenario_id: scenarioId,
domain: toStringSafe(record.domain),
title: toStringSafe(record.title),
semantic_tags: semanticTags,
steps_total: Array.isArray(record.steps) ? record.steps.length : 0
};
} catch {
return null;
}
}
function buildInvariantSummary(rawValue: unknown): AgentSemanticInvariantSummary {
const record = toRecord(rawValue);
return {
direct_answer_ok: toBooleanSafe(record?.direct_answer_ok),
temporal_honesty_ok: toBooleanSafe(record?.temporal_honesty_ok),
selected_object_continuity_ok: toBooleanSafe(record?.selected_object_continuity_ok),
truth_gate_ok: toBooleanSafe(record?.truth_gate_ok),
human_answer_quality_ok: toBooleanSafe(record?.human_answer_quality_ok),
meta_context_integrity_ok: toBooleanSafe(record?.meta_context_integrity_ok)
};
}
export function findLatestAgentSemanticAcceptanceSummary(input: {
artifactsRootDir: string;
repoRootDir: string;
scenarioId: string | null | undefined;
}): AgentSemanticAcceptanceSummary | null {
const scenarioId = toStringSafe(input.scenarioId);
if (!scenarioId) {
return null;
}
if (!fs.existsSync(input.artifactsRootDir)) {
return null;
}
const candidates = fs
.readdirSync(input.artifactsRootDir, { withFileTypes: true })
.filter((entry) => entry.isDirectory() && entry.name.startsWith(`${scenarioId}_`))
.map((entry) => {
const outputDir = path.resolve(input.artifactsRootDir, entry.name);
const packStatePath = path.resolve(outputDir, "pack_state.json");
if (!fs.existsSync(packStatePath)) {
return null;
}
const stats = fs.statSync(packStatePath);
return {
outputDir,
packStatePath,
mtimeMs: stats.mtimeMs
};
})
.filter((item): item is { outputDir: string; packStatePath: string; mtimeMs: number } => item !== null)
.sort((left, right) => right.mtimeMs - left.mtimeMs);
const latest = candidates[0];
if (!latest) {
return null;
}
try {
const parsed = readJsonFile(latest.packStatePath);
const packState = toRecord(parsed);
if (!packState) {
return null;
}
return {
scenario_id: scenarioId,
output_dir: latest.outputDir,
relative_output_dir: path.relative(input.repoRootDir, latest.outputDir).replace(/\\/g, "/"),
final_status: toStringSafe(packState.final_status),
final_status_reason: toStringSafe(packState.final_status_reason),
review_overall_status: toStringSafe(packState.review_overall_status),
acceptance_gate_passed: toBooleanSafe(packState.acceptance_gate_passed),
critical_path_green: toBooleanSafe(packState.critical_path_green),
unresolved_p0_count: toNumberSafe(packState.unresolved_p0_count),
unresolved_p1_count: toNumberSafe(packState.unresolved_p1_count),
unresolved_p2_count: toNumberSafe(packState.unresolved_p2_count),
steps_total: toNumberSafe(packState.steps_total),
steps_passed: toNumberSafe(packState.steps_passed),
steps_with_warning: toNumberSafe(packState.steps_with_warning),
steps_failed: toNumberSafe(packState.steps_failed),
updated_at: toStringSafe(packState.updated_at),
invariants: buildInvariantSummary(packState.invariants)
};
} catch {
return null;
}
}