ЮИ - Упростить карточки автопрогонов до названия даты id режима и типа
This commit is contained in:
parent
9f0f7f3e79
commit
6223062b37
|
|
@ -3,11 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.TRACES_DIR = exports.DATA_DIR = exports.VAT_PAYABLE_19_PREFIXES = exports.VAT_PAYABLE_68_PREFIXES = exports.ASSISTANT_MCP_LIVE_LIMIT = exports.ASSISTANT_MCP_TIMEOUT_MS = exports.ASSISTANT_MCP_CHANNEL = exports.ASSISTANT_MCP_PROXY_URL = exports.FEATURE_ASSISTANT_LIVING_CHAT_ROUTER_V1 = exports.FEATURE_ASSISTANT_ROUTE_EXPECTATION_HARD_GUARD_V1 = exports.FEATURE_ASSISTANT_ROUTE_EXPECTATION_AUDIT_V1 = exports.FEATURE_ASSISTANT_ROUTE_SHADOW_PAYABLES_EXACT_V1 = exports.FEATURE_ASSISTANT_ROUTE_RECEIVABLES_HEURISTIC_V1 = exports.FEATURE_ASSISTANT_ROUTE_PAYABLES_HEURISTIC_V1 = exports.FEATURE_ASSISTANT_ROUTE_RECEIVABLES_CONFIRMED_V1 = exports.FEATURE_ASSISTANT_ROUTE_PAYABLES_CONFIRMED_V1 = exports.FEATURE_ASSISTANT_ROUTE_BALANCE_EXACT_V1 = exports.FEATURE_ASSISTANT_ROUTE_DRILLDOWN_V1 = exports.FEATURE_ASSISTANT_ROUTE_ADDRESS_GENERIC_V1 = exports.FEATURE_ASSISTANT_CAPABILITY_ROUTE_GUARD_V1 = exports.FEATURE_ASSISTANT_ADDRESS_NAVIGATION_STATE_V1 = exports.FEATURE_ASSISTANT_ADDRESS_QUERY_LIVE_V1 = exports.FEATURE_ASSISTANT_ADDRESS_QUERY_LLM_PREDECOMPOSE_V1 = exports.FEATURE_ASSISTANT_ADDRESS_QUERY_V1 = exports.FEATURE_ASSISTANT_MCP_RUNTIME_V1 = exports.FEATURE_ASSISTANT_GRAPH_RUNTIME_V1 = exports.FEATURE_ASSISTANT_LIFECYCLE_ANSWER_V1 = exports.FEATURE_ASSISTANT_LIFECYCLE_RUNTIME_V1 = exports.FEATURE_ASSISTANT_STAGE2_EVAL_V1 = exports.FEATURE_ASSISTANT_PROBLEM_UNIT_CONTINUITY_V1 = exports.FEATURE_ASSISTANT_PROBLEM_CENTRIC_ANSWER_V1 = exports.FEATURE_ASSISTANT_PROBLEM_UNITS_V1 = exports.FEATURE_ASSISTANT_ACCOUNTANT_EVAL_V1 = exports.FEATURE_ASSISTANT_ANSWER_POLICY_V11 = exports.FEATURE_ASSISTANT_ANTI_GENERIC_RANKING_GUARD_V1 = exports.FEATURE_ASSISTANT_MIN_EVIDENCE_GATE_V1 = exports.FEATURE_ASSISTANT_BROAD_GUARD_V1 = exports.FEATURE_ASSISTANT_EVIDENCE_ENRICHMENT_V1 = exports.FEATURE_ASSISTANT_STATE_FOLLOWUP_BINDING_V1 = exports.FEATURE_ASSISTANT_CONTRACTS_V11 = exports.FEATURE_ASSISTANT_INVESTIGATION_STATE_V1 = exports.DEFAULT_PROMPT_VERSION = exports.DEFAULT_MAX_OUTPUT_TOKENS = exports.DEFAULT_TEMPERATURE = exports.DEFAULT_MODEL = exports.DEFAULT_OPENAI_BASE_URL = exports.TIMEZONE = exports.PORT = exports.MODULE_ROOT = exports.BACKEND_ROOT = void 0;
|
exports.DATA_DIR = exports.VAT_PAYABLE_19_PREFIXES = exports.VAT_PAYABLE_68_PREFIXES = exports.ASSISTANT_MCP_LIVE_LIMIT = exports.ASSISTANT_MCP_TIMEOUT_MS = exports.ASSISTANT_MCP_CHANNEL = exports.ASSISTANT_MCP_PROXY_URL = exports.FEATURE_ASSISTANT_LIVING_CHAT_ROUTER_V1 = exports.FEATURE_ASSISTANT_ROUTE_EXPECTATION_HARD_GUARD_V1 = exports.FEATURE_ASSISTANT_ROUTE_EXPECTATION_AUDIT_V1 = exports.FEATURE_ASSISTANT_ROUTE_SHADOW_PAYABLES_EXACT_V1 = exports.FEATURE_ASSISTANT_ROUTE_RECEIVABLES_HEURISTIC_V1 = exports.FEATURE_ASSISTANT_ROUTE_PAYABLES_HEURISTIC_V1 = exports.FEATURE_ASSISTANT_ROUTE_RECEIVABLES_CONFIRMED_V1 = exports.FEATURE_ASSISTANT_ROUTE_PAYABLES_CONFIRMED_V1 = exports.FEATURE_ASSISTANT_ROUTE_BALANCE_EXACT_V1 = exports.FEATURE_ASSISTANT_ROUTE_DRILLDOWN_V1 = exports.FEATURE_ASSISTANT_ROUTE_ADDRESS_GENERIC_V1 = exports.FEATURE_ASSISTANT_CAPABILITY_ROUTE_GUARD_V1 = exports.FEATURE_ASSISTANT_ADDRESS_NAVIGATION_STATE_V1 = exports.FEATURE_ASSISTANT_ADDRESS_QUERY_LIVE_V1 = exports.FEATURE_ASSISTANT_ADDRESS_QUERY_LLM_PREDECOMPOSE_V1 = exports.FEATURE_ASSISTANT_ADDRESS_QUERY_V1 = exports.FEATURE_ASSISTANT_MCP_RUNTIME_V1 = exports.FEATURE_ASSISTANT_GRAPH_RUNTIME_V1 = exports.FEATURE_ASSISTANT_LIFECYCLE_ANSWER_V1 = exports.FEATURE_ASSISTANT_LIFECYCLE_RUNTIME_V1 = exports.FEATURE_ASSISTANT_STAGE2_EVAL_V1 = exports.FEATURE_ASSISTANT_PROBLEM_UNIT_CONTINUITY_V1 = exports.FEATURE_ASSISTANT_PROBLEM_CENTRIC_ANSWER_V1 = exports.FEATURE_ASSISTANT_PROBLEM_UNITS_V1 = exports.FEATURE_ASSISTANT_ACCOUNTANT_EVAL_V1 = exports.FEATURE_ASSISTANT_ANSWER_POLICY_V11 = exports.FEATURE_ASSISTANT_ANTI_GENERIC_RANKING_GUARD_V1 = exports.FEATURE_ASSISTANT_MIN_EVIDENCE_GATE_V1 = exports.FEATURE_ASSISTANT_BROAD_GUARD_V1 = exports.FEATURE_ASSISTANT_EVIDENCE_ENRICHMENT_V1 = exports.FEATURE_ASSISTANT_STATE_FOLLOWUP_BINDING_V1 = exports.FEATURE_ASSISTANT_CONTRACTS_V11 = exports.FEATURE_ASSISTANT_INVESTIGATION_STATE_V1 = exports.DEFAULT_PROMPT_VERSION = exports.DEFAULT_MAX_OUTPUT_TOKENS = exports.DEFAULT_TEMPERATURE = exports.DEFAULT_MODEL = exports.DEFAULT_OPENAI_BASE_URL = exports.TIMEZONE = exports.PORT = exports.PROJECT_ROOT = exports.MODULE_ROOT = exports.BACKEND_ROOT = void 0;
|
||||||
exports.MANUAL_CASE_DECISION_SCHEMA_FILE = exports.ASSISTANT_CAPABILITIES_REGISTRY_FILE = exports.ASSISTANT_CANON_FILE = exports.ARCH_EXPORT_2020_DIR = exports.SCHEMAS_DIR = exports.EVAL_DATASETS_DIR = exports.REPORTS_DIR = exports.PROMPTS_DIR = exports.SHARED_LLM_CONNECTION_FILE = exports.AUTORUN_GENERATOR_HISTORY_FILE = exports.AUTORUN_GENERATOR_DIR = exports.AUTORUN_ANNOTATIONS_FILE = exports.AUTORUN_ANNOTATIONS_DIR = exports.ASSISTANT_ANNOTATIONS_FILE = exports.ASSISTANT_ANNOTATIONS_DIR = exports.ASSISTANT_SESSIONS_DIR = exports.EVAL_CASES_DIR = exports.PRESETS_DIR = void 0;
|
exports.MANUAL_CASE_DECISION_SCHEMA_FILE = exports.ASSISTANT_CAPABILITIES_REGISTRY_FILE = exports.ASSISTANT_CANON_FILE = exports.ARCH_EXPORT_2020_DIR = exports.SCHEMAS_DIR = exports.EVAL_DATASETS_DIR = exports.ARTIFACTS_DIR = exports.REPORTS_DIR = exports.PROMPTS_DIR = exports.SHARED_LLM_CONNECTION_FILE = exports.AUTORUN_GENERATOR_HISTORY_FILE = exports.AUTORUN_GENERATOR_DIR = exports.AUTORUN_ANNOTATIONS_FILE = exports.AUTORUN_ANNOTATIONS_DIR = exports.ASSISTANT_ANNOTATIONS_FILE = exports.ASSISTANT_ANNOTATIONS_DIR = exports.ASSISTANT_SESSIONS_DIR = exports.EVAL_CASES_DIR = exports.PRESETS_DIR = exports.TRACES_DIR = void 0;
|
||||||
const path_1 = __importDefault(require("path"));
|
const path_1 = __importDefault(require("path"));
|
||||||
exports.BACKEND_ROOT = path_1.default.resolve(__dirname, "..");
|
exports.BACKEND_ROOT = path_1.default.resolve(__dirname, "..");
|
||||||
exports.MODULE_ROOT = path_1.default.resolve(exports.BACKEND_ROOT, "..");
|
exports.MODULE_ROOT = path_1.default.resolve(exports.BACKEND_ROOT, "..");
|
||||||
|
exports.PROJECT_ROOT = path_1.default.resolve(exports.MODULE_ROOT, "..");
|
||||||
function toBooleanFlag(value, defaultValue) {
|
function toBooleanFlag(value, defaultValue) {
|
||||||
if (!value || value.trim() === "") {
|
if (!value || value.trim() === "") {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
|
|
@ -93,6 +94,7 @@ exports.AUTORUN_GENERATOR_HISTORY_FILE = path_1.default.resolve(exports.AUTORUN_
|
||||||
exports.SHARED_LLM_CONNECTION_FILE = path_1.default.resolve(exports.DATA_DIR, "shared_llm_connection.json");
|
exports.SHARED_LLM_CONNECTION_FILE = path_1.default.resolve(exports.DATA_DIR, "shared_llm_connection.json");
|
||||||
exports.PROMPTS_DIR = path_1.default.resolve(exports.MODULE_ROOT, "prompts");
|
exports.PROMPTS_DIR = path_1.default.resolve(exports.MODULE_ROOT, "prompts");
|
||||||
exports.REPORTS_DIR = path_1.default.resolve(exports.MODULE_ROOT, "reports");
|
exports.REPORTS_DIR = path_1.default.resolve(exports.MODULE_ROOT, "reports");
|
||||||
|
exports.ARTIFACTS_DIR = path_1.default.resolve(exports.PROJECT_ROOT, "artifacts");
|
||||||
exports.EVAL_DATASETS_DIR = path_1.default.resolve(exports.MODULE_ROOT, "eval_cases");
|
exports.EVAL_DATASETS_DIR = path_1.default.resolve(exports.MODULE_ROOT, "eval_cases");
|
||||||
exports.SCHEMAS_DIR = path_1.default.resolve(exports.BACKEND_ROOT, "src", "schemas");
|
exports.SCHEMAS_DIR = path_1.default.resolve(exports.BACKEND_ROOT, "src", "schemas");
|
||||||
exports.ARCH_EXPORT_2020_DIR = path_1.default.resolve(exports.MODULE_ROOT, "..", "docs", "ARCH", "2020экспорт");
|
exports.ARCH_EXPORT_2020_DIR = path_1.default.resolve(exports.MODULE_ROOT, "..", "docs", "ARCH", "2020экспорт");
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ const path_1 = __importDefault(require("path"));
|
||||||
const express_1 = require("express");
|
const express_1 = require("express");
|
||||||
const iconv_lite_1 = __importDefault(require("iconv-lite"));
|
const iconv_lite_1 = __importDefault(require("iconv-lite"));
|
||||||
const config_1 = require("../config");
|
const config_1 = require("../config");
|
||||||
|
const agentSemanticRunRegistry_1 = require("../services/agentSemanticRunRegistry");
|
||||||
const http_1 = require("../utils/http");
|
const http_1 = require("../utils/http");
|
||||||
const capabilitiesRegistry_1 = require("../services/capabilitiesRegistry");
|
const capabilitiesRegistry_1 = require("../services/capabilitiesRegistry");
|
||||||
const openaiResponsesClient_1 = require("../services/openaiResponsesClient");
|
const openaiResponsesClient_1 = require("../services/openaiResponsesClient");
|
||||||
|
|
@ -189,7 +190,14 @@ function readAutoGenHistory() {
|
||||||
? repairAutogenMojibake(String(toRecord(item.context)?.agent_focus))
|
? repairAutogenMojibake(String(toRecord(item.context)?.agent_focus))
|
||||||
: null,
|
: null,
|
||||||
architecture_phase: toStringSafe(toRecord(item.context)?.architecture_phase),
|
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)
|
||||||
|
.map((tag) => toStringSafe(tag))
|
||||||
|
.filter((tag) => Boolean(tag))))
|
||||||
|
: null,
|
||||||
|
latest_acceptance: null
|
||||||
}
|
}
|
||||||
: null
|
: null
|
||||||
}))
|
}))
|
||||||
|
|
@ -207,6 +215,75 @@ function writeAutoGenHistory(records) {
|
||||||
}
|
}
|
||||||
fs_1.default.writeFileSync(config_1.AUTORUN_GENERATOR_HISTORY_FILE, JSON.stringify(records, null, 2), "utf-8");
|
fs_1.default.writeFileSync(config_1.AUTORUN_GENERATOR_HISTORY_FILE, JSON.stringify(records, null, 2), "utf-8");
|
||||||
}
|
}
|
||||||
|
function isAgentSemanticHistoryRecord(record) {
|
||||||
|
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) {
|
||||||
|
const specSummaryCache = new Map();
|
||||||
|
const acceptanceCache = new Map();
|
||||||
|
const readSpecSummary = (sourceSpecFile) => {
|
||||||
|
const normalizedPath = toStringSafe(sourceSpecFile);
|
||||||
|
if (!normalizedPath) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (specSummaryCache.has(normalizedPath)) {
|
||||||
|
return specSummaryCache.get(normalizedPath) ?? null;
|
||||||
|
}
|
||||||
|
const summary = (0, agentSemanticRunRegistry_1.readAgentSemanticSpecSummaryFromFile)(normalizedPath);
|
||||||
|
specSummaryCache.set(normalizedPath, summary);
|
||||||
|
return summary;
|
||||||
|
};
|
||||||
|
const readAcceptanceSummary = (scenarioId) => {
|
||||||
|
const normalizedScenarioId = toStringSafe(scenarioId);
|
||||||
|
if (!normalizedScenarioId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (acceptanceCache.has(normalizedScenarioId)) {
|
||||||
|
return acceptanceCache.get(normalizedScenarioId) ?? null;
|
||||||
|
}
|
||||||
|
const summary = (0, agentSemanticRunRegistry_1.findLatestAgentSemanticAcceptanceSummary)({
|
||||||
|
artifactsRootDir: path_1.default.resolve(config_1.ARTIFACTS_DIR, "domain_runs"),
|
||||||
|
repoRootDir: config_1.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) {
|
function readEvalDatasetCases(filePath) {
|
||||||
try {
|
try {
|
||||||
const parsed = JSON.parse(fs_1.default.readFileSync(filePath, "utf-8"));
|
const parsed = JSON.parse(fs_1.default.readFileSync(filePath, "utf-8"));
|
||||||
|
|
@ -2015,9 +2092,9 @@ function buildAutoRunsRouter(services, openaiClient = new openaiResponsesClient_
|
||||||
const rawMode = toStringSafe(req.query.mode);
|
const rawMode = toStringSafe(req.query.mode);
|
||||||
const includeAllModes = !rawMode || !isAutoGenMode(rawMode);
|
const includeAllModes = !rawMode || !isAutoGenMode(rawMode);
|
||||||
const modeFilter = rawMode ?? "codex_creative";
|
const modeFilter = rawMode ?? "codex_creative";
|
||||||
const items = readAutoGenHistory()
|
const items = hydrateAutoGenHistoryForApi(readAutoGenHistory()
|
||||||
.filter((item) => (includeAllModes ? true : item.mode === modeFilter))
|
.filter((item) => (includeAllModes ? true : item.mode === modeFilter))
|
||||||
.slice(0, limit);
|
.slice(0, limit));
|
||||||
(0, http_1.ok)(res, {
|
(0, http_1.ok)(res, {
|
||||||
ok: true,
|
ok: true,
|
||||||
generated_at: new Date().toISOString(),
|
generated_at: new Date().toISOString(),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,166 @@
|
||||||
|
"use strict";
|
||||||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.readAgentSemanticSpecSummaryFromFile = readAgentSemanticSpecSummaryFromFile;
|
||||||
|
exports.findLatestAgentSemanticAcceptanceSummary = findLatestAgentSemanticAcceptanceSummary;
|
||||||
|
const fs_1 = __importDefault(require("fs"));
|
||||||
|
const path_1 = __importDefault(require("path"));
|
||||||
|
function toRecord(value) {
|
||||||
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
function toStringSafe(value) {
|
||||||
|
if (typeof value !== "string") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const trimmed = value.trim();
|
||||||
|
return trimmed.length > 0 ? trimmed : null;
|
||||||
|
}
|
||||||
|
function toBooleanSafe(value) {
|
||||||
|
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) {
|
||||||
|
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) {
|
||||||
|
return JSON.parse(fs_1.default.readFileSync(filePath, "utf-8"));
|
||||||
|
}
|
||||||
|
function normalizeSemanticTags(rawSteps) {
|
||||||
|
if (!Array.isArray(rawSteps)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const tags = new Set();
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
function readAgentSemanticSpecSummaryFromFile(sourceSpecFile) {
|
||||||
|
const normalizedPath = toStringSafe(sourceSpecFile);
|
||||||
|
if (!normalizedPath || !fs_1.default.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) {
|
||||||
|
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)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function findLatestAgentSemanticAcceptanceSummary(input) {
|
||||||
|
const scenarioId = toStringSafe(input.scenarioId);
|
||||||
|
if (!scenarioId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!fs_1.default.existsSync(input.artifactsRootDir)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const candidates = fs_1.default
|
||||||
|
.readdirSync(input.artifactsRootDir, { withFileTypes: true })
|
||||||
|
.filter((entry) => entry.isDirectory() && entry.name.startsWith(`${scenarioId}_`))
|
||||||
|
.map((entry) => {
|
||||||
|
const outputDir = path_1.default.resolve(input.artifactsRootDir, entry.name);
|
||||||
|
const packStatePath = path_1.default.resolve(outputDir, "pack_state.json");
|
||||||
|
if (!fs_1.default.existsSync(packStatePath)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const stats = fs_1.default.statSync(packStatePath);
|
||||||
|
return {
|
||||||
|
outputDir,
|
||||||
|
packStatePath,
|
||||||
|
mtimeMs: stats.mtimeMs
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter((item) => 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_1.default.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ import path from "path";
|
||||||
|
|
||||||
export const BACKEND_ROOT = path.resolve(__dirname, "..");
|
export const BACKEND_ROOT = path.resolve(__dirname, "..");
|
||||||
export const MODULE_ROOT = path.resolve(BACKEND_ROOT, "..");
|
export const MODULE_ROOT = path.resolve(BACKEND_ROOT, "..");
|
||||||
|
export const PROJECT_ROOT = path.resolve(MODULE_ROOT, "..");
|
||||||
|
|
||||||
function toBooleanFlag(value: string | undefined, defaultValue: boolean): boolean {
|
function toBooleanFlag(value: string | undefined, defaultValue: boolean): boolean {
|
||||||
if (!value || value.trim() === "") {
|
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 PROMPTS_DIR = path.resolve(MODULE_ROOT, "prompts");
|
||||||
export const REPORTS_DIR = path.resolve(MODULE_ROOT, "reports");
|
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 EVAL_DATASETS_DIR = path.resolve(MODULE_ROOT, "eval_cases");
|
||||||
export const SCHEMAS_DIR = path.resolve(BACKEND_ROOT, "src", "schemas");
|
export const SCHEMAS_DIR = path.resolve(BACKEND_ROOT, "src", "schemas");
|
||||||
export const ARCH_EXPORT_2020_DIR = path.resolve(MODULE_ROOT, "..", "docs", "ARCH", "2020экспорт");
|
export const ARCH_EXPORT_2020_DIR = path.resolve(MODULE_ROOT, "..", "docs", "ARCH", "2020экспорт");
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,21 @@ import path from "path";
|
||||||
import { Router } from "express";
|
import { Router } from "express";
|
||||||
import iconv from "iconv-lite";
|
import iconv from "iconv-lite";
|
||||||
import {
|
import {
|
||||||
|
ARTIFACTS_DIR,
|
||||||
ASSISTANT_SESSIONS_DIR,
|
ASSISTANT_SESSIONS_DIR,
|
||||||
AUTORUN_ANNOTATIONS_FILE,
|
AUTORUN_ANNOTATIONS_FILE,
|
||||||
AUTORUN_GENERATOR_HISTORY_FILE,
|
AUTORUN_GENERATOR_HISTORY_FILE,
|
||||||
EVAL_CASES_DIR,
|
EVAL_CASES_DIR,
|
||||||
EVAL_DATASETS_DIR,
|
EVAL_DATASETS_DIR,
|
||||||
MANUAL_CASE_DECISION_SCHEMA_FILE,
|
MANUAL_CASE_DECISION_SCHEMA_FILE,
|
||||||
|
PROJECT_ROOT,
|
||||||
REPORTS_DIR
|
REPORTS_DIR
|
||||||
} from "../config";
|
} from "../config";
|
||||||
import type { AppServices } from "../serverContext";
|
import type { AppServices } from "../serverContext";
|
||||||
|
import {
|
||||||
|
findLatestAgentSemanticAcceptanceSummary,
|
||||||
|
readAgentSemanticSpecSummaryFromFile
|
||||||
|
} from "../services/agentSemanticRunRegistry";
|
||||||
import { ApiError, ok } from "../utils/http";
|
import { ApiError, ok } from "../utils/http";
|
||||||
import { loadCapabilitiesRegistry, resolveNearestCapabilityGroup, type CapabilityGroup } from "../services/capabilitiesRegistry";
|
import { loadCapabilitiesRegistry, resolveNearestCapabilityGroup, type CapabilityGroup } from "../services/capabilitiesRegistry";
|
||||||
import { OpenAIResponsesClient } from "../services/openaiResponsesClient";
|
import { OpenAIResponsesClient } from "../services/openaiResponsesClient";
|
||||||
|
|
@ -197,6 +203,34 @@ interface AutoGenHistoryRecord {
|
||||||
agent_focus?: string | null;
|
agent_focus?: string | null;
|
||||||
architecture_phase?: string | null;
|
architecture_phase?: string | null;
|
||||||
source_spec_file?: 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;
|
} | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -375,7 +409,18 @@ function readAutoGenHistory(): AutoGenHistoryRecord[] {
|
||||||
? repairAutogenMojibake(String(toRecord(item.context)?.agent_focus))
|
? repairAutogenMojibake(String(toRecord(item.context)?.agent_focus))
|
||||||
: null,
|
: null,
|
||||||
architecture_phase: toStringSafe(toRecord(item.context)?.architecture_phase),
|
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
|
: null
|
||||||
}))
|
}))
|
||||||
|
|
@ -394,6 +439,81 @@ function writeAutoGenHistory(records: AutoGenHistoryRecord[]): void {
|
||||||
fs.writeFileSync(AUTORUN_GENERATOR_HISTORY_FILE, JSON.stringify(records, null, 2), "utf-8");
|
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>> {
|
function readEvalDatasetCases(filePath: string): Array<Record<string, unknown>> {
|
||||||
try {
|
try {
|
||||||
const parsed = JSON.parse(fs.readFileSync(filePath, "utf-8")) as unknown;
|
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 rawMode = toStringSafe((req.query as Record<string, unknown>).mode);
|
||||||
const includeAllModes = !rawMode || !isAutoGenMode(rawMode);
|
const includeAllModes = !rawMode || !isAutoGenMode(rawMode);
|
||||||
const modeFilter = (rawMode as AutoGenMode | null) ?? "codex_creative";
|
const modeFilter = (rawMode as AutoGenMode | null) ?? "codex_creative";
|
||||||
const items = readAutoGenHistory()
|
const items = hydrateAutoGenHistoryForApi(
|
||||||
|
readAutoGenHistory()
|
||||||
.filter((item) => (includeAllModes ? true : item.mode === modeFilter))
|
.filter((item) => (includeAllModes ? true : item.mode === modeFilter))
|
||||||
.slice(0, limit);
|
.slice(0, limit)
|
||||||
|
);
|
||||||
|
|
||||||
ok(res, {
|
ok(res, {
|
||||||
ok: true,
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
import fs from "fs";
|
||||||
|
import os from "os";
|
||||||
|
import path from "path";
|
||||||
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
|
import {
|
||||||
|
findLatestAgentSemanticAcceptanceSummary,
|
||||||
|
readAgentSemanticSpecSummaryFromFile
|
||||||
|
} from "../src/services/agentSemanticRunRegistry";
|
||||||
|
|
||||||
|
const tempRoots: string[] = [];
|
||||||
|
|
||||||
|
function makeTempRoot(): string {
|
||||||
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "agent-semantic-registry-"));
|
||||||
|
tempRoots.push(root);
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeJson(filePath: string, payload: unknown): void {
|
||||||
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
||||||
|
fs.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
while (tempRoots.length > 0) {
|
||||||
|
const root = tempRoots.pop();
|
||||||
|
if (root && fs.existsSync(root)) {
|
||||||
|
fs.rmSync(root, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("agentSemanticRunRegistry", () => {
|
||||||
|
it("reads scenario id and deduplicated semantic tags from the source spec", () => {
|
||||||
|
const root = makeTempRoot();
|
||||||
|
const specPath = path.resolve(root, "docs", "orchestration", "sample_agent_spec.json");
|
||||||
|
writeJson(specPath, {
|
||||||
|
scenario_id: "address_truth_harness_demo",
|
||||||
|
domain: "address_demo",
|
||||||
|
title: "Demo AGENT spec",
|
||||||
|
steps: [
|
||||||
|
{ step_id: "step_01", question: "привет", semantic_tags: ["meta_smalltalk", "meta_scope"] },
|
||||||
|
{ step_id: "step_02", question: "остатки", semantic_tags: ["inventory_root", "meta_scope"] }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
const summary = readAgentSemanticSpecSummaryFromFile(specPath);
|
||||||
|
|
||||||
|
expect(summary).toEqual({
|
||||||
|
scenario_id: "address_truth_harness_demo",
|
||||||
|
domain: "address_demo",
|
||||||
|
title: "Demo AGENT spec",
|
||||||
|
semantic_tags: ["inventory_root", "meta_scope", "meta_smalltalk"],
|
||||||
|
steps_total: 2
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("picks the newest pack_state for the scenario and exposes acceptance summary", () => {
|
||||||
|
const root = makeTempRoot();
|
||||||
|
const artifactsRoot = path.resolve(root, "artifacts", "domain_runs");
|
||||||
|
|
||||||
|
const olderDir = path.resolve(artifactsRoot, "address_truth_harness_demo_live_20260417");
|
||||||
|
const olderPackStatePath = path.resolve(olderDir, "pack_state.json");
|
||||||
|
writeJson(olderPackStatePath, {
|
||||||
|
final_status: "partial",
|
||||||
|
final_status_reason: "review_failures_remaining",
|
||||||
|
review_overall_status: "fail",
|
||||||
|
acceptance_gate_passed: false,
|
||||||
|
critical_path_green: false,
|
||||||
|
unresolved_p0_count: 1,
|
||||||
|
unresolved_p1_count: 0,
|
||||||
|
unresolved_p2_count: 0,
|
||||||
|
steps_total: 4,
|
||||||
|
steps_passed: 2,
|
||||||
|
steps_with_warning: 1,
|
||||||
|
steps_failed: 1,
|
||||||
|
updated_at: "2026-04-17T09:00:00+00:00",
|
||||||
|
invariants: {
|
||||||
|
direct_answer_ok: false,
|
||||||
|
temporal_honesty_ok: true,
|
||||||
|
selected_object_continuity_ok: false,
|
||||||
|
truth_gate_ok: true,
|
||||||
|
human_answer_quality_ok: true,
|
||||||
|
meta_context_integrity_ok: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
fs.utimesSync(olderPackStatePath, new Date("2026-04-17T09:00:00.000Z"), new Date("2026-04-17T09:00:00.000Z"));
|
||||||
|
|
||||||
|
const newerDir = path.resolve(artifactsRoot, "address_truth_harness_demo_live_20260417_rerun2");
|
||||||
|
const newerPackStatePath = path.resolve(newerDir, "pack_state.json");
|
||||||
|
writeJson(newerPackStatePath, {
|
||||||
|
final_status: "accepted",
|
||||||
|
final_status_reason: "scenario_acceptance_gate_passed",
|
||||||
|
review_overall_status: "pass",
|
||||||
|
acceptance_gate_passed: true,
|
||||||
|
critical_path_green: true,
|
||||||
|
unresolved_p0_count: 0,
|
||||||
|
unresolved_p1_count: 0,
|
||||||
|
unresolved_p2_count: 0,
|
||||||
|
steps_total: 4,
|
||||||
|
steps_passed: 4,
|
||||||
|
steps_with_warning: 0,
|
||||||
|
steps_failed: 0,
|
||||||
|
updated_at: "2026-04-17T09:10:00+00:00",
|
||||||
|
invariants: {
|
||||||
|
direct_answer_ok: true,
|
||||||
|
temporal_honesty_ok: true,
|
||||||
|
selected_object_continuity_ok: true,
|
||||||
|
truth_gate_ok: true,
|
||||||
|
human_answer_quality_ok: true,
|
||||||
|
meta_context_integrity_ok: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
fs.utimesSync(newerPackStatePath, new Date("2026-04-17T09:10:00.000Z"), new Date("2026-04-17T09:10:00.000Z"));
|
||||||
|
|
||||||
|
const summary = findLatestAgentSemanticAcceptanceSummary({
|
||||||
|
artifactsRootDir: artifactsRoot,
|
||||||
|
repoRootDir: root,
|
||||||
|
scenarioId: "address_truth_harness_demo"
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(summary).not.toBeNull();
|
||||||
|
expect(summary?.final_status).toBe("accepted");
|
||||||
|
expect(summary?.relative_output_dir).toBe("artifacts/domain_runs/address_truth_harness_demo_live_20260417_rerun2");
|
||||||
|
expect(summary?.steps_passed).toBe(4);
|
||||||
|
expect(summary?.unresolved_p0_count).toBe(0);
|
||||||
|
expect(summary?.invariants.meta_context_integrity_ok).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
{
|
||||||
|
"suite_id": "assistant_saved_session_runtime_job-WKk8Q3UqdX",
|
||||||
|
"suite_version": "0.1.0",
|
||||||
|
"schema_version": "assistant_saved_session_runtime_v0_1",
|
||||||
|
"title": "AGENT | Phase 7 mixed replay for documents, selected-object continuity, meta context, and cross-domain pivots",
|
||||||
|
"scenario_count": 1,
|
||||||
|
"case_ids": [
|
||||||
|
"SAVED-001"
|
||||||
|
],
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"case_id": "SAVED-001",
|
||||||
|
"scenario_tag": "saved_user_sessions_runtime",
|
||||||
|
"title": "AGENT | Phase 7 mixed replay for documents, selected-object continuity, meta context, and cross-domain pivots",
|
||||||
|
"question_type": "followup",
|
||||||
|
"broadness_level": "medium",
|
||||||
|
"turns": [
|
||||||
|
{
|
||||||
|
"user_message": "привет, как дела?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "по какой компании мы сейчас работаем?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "покажи все документы по чепурнову"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "что нам отгружал чепурнов, какой товар или услугу?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "какие остатки на складе на март 2021"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "По выбранному объекту \"Столешница 600*3050*26 альмандин\": кто нам это поставил?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "что ты умеешь?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "По выбранному объекту \"Столешница 600*3050*26 альмандин\": покажи документы по этой позиции"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "а ты помнишь, что мы по этой позиции уже выяснили?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "покажи еще раз остатки на эту же дату"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "кто нам должен на март 2020"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "остатки по складу на эту же дату"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "а исторические остатки тоже можешь?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "хвосты покажи по счету 60 на август 2022"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"schema_version": "shared_llm_connection_v1",
|
"schema_version": "shared_llm_connection_v1",
|
||||||
"updated_at": "2026-04-16T17:54:57.636Z",
|
"updated_at": "2026-04-17T10:55:35.860Z",
|
||||||
"connection": {
|
"connection": {
|
||||||
"llmProvider": "local",
|
"llmProvider": "local",
|
||||||
"model": "unsloth/qwen3-30b-a3b-instruct-2507",
|
"model": "unsloth/qwen3-30b-a3b-instruct-2507",
|
||||||
|
|
|
||||||
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
|
|
@ -4,8 +4,8 @@
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>NDC AI Normalizer Playground</title>
|
<title>NDC AI Normalizer Playground</title>
|
||||||
<script type="module" crossorigin src="/assets/index-BIzNO_Mb.js"></script>
|
<script type="module" crossorigin src="/assets/index-C8U6PD78.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-DkWsdP2H.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-CfrZGsZo.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|
|
||||||
|
|
@ -1534,6 +1534,7 @@ export function AutoRunsHistoryPanel({
|
||||||
selectedAutogenGeneration,
|
selectedAutogenGeneration,
|
||||||
stopAsyncJobPolling
|
stopAsyncJobPolling
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const openCommentModal = useCallback((message: AutoRunDialogMessage) => {
|
const openCommentModal = useCallback((message: AutoRunDialogMessage) => {
|
||||||
if (message.role !== "assistant") return;
|
if (message.role !== "assistant") return;
|
||||||
const resolvedCaseId = message.case_id ?? selectedCaseId;
|
const resolvedCaseId = message.case_id ?? selectedCaseId;
|
||||||
|
|
@ -2507,23 +2508,11 @@ export function AutoRunsHistoryPanel({
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="autoruns-run-meta">
|
<div className="autoruns-run-meta">
|
||||||
режим={formatAutoGenModeLabel(item.mode)} | count={item.count}
|
режим={formatAutoGenModeLabel(item.mode)}
|
||||||
</div>
|
</div>
|
||||||
{isAgentSemanticGeneration(item) ? (
|
|
||||||
<div className="autoruns-run-meta">
|
<div className="autoruns-run-meta">
|
||||||
тип=АГЕНТНЫЙ ПРОГОН
|
тип={isAgentSemanticGeneration(item) ? "АГЕНТНЫЙ ПРОГОН" : "АВТОПРОГОН"}
|
||||||
{item.context?.architecture_phase ? ` | этап=${item.context.architecture_phase}` : ""}
|
|
||||||
{item.context?.agent_focus ? ` | фокус=${item.context.agent_focus}` : ""}
|
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
|
||||||
{item.domain || item.generated_by ? (
|
|
||||||
<div className="autoruns-run-meta">
|
|
||||||
{item.domain ? `домен=${item.domain}` : "домен=общий"}
|
|
||||||
{item.generated_by ? ` | автор=${item.generated_by}` : ""}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{item.saved_case_set_file ? <div className="autoruns-run-meta">кейс-сет={item.saved_case_set_file}</div> : null}
|
|
||||||
{(item.questions ?? []).length > 0 ? <p>{item.questions[0]}</p> : null}
|
|
||||||
</article>
|
</article>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -338,6 +338,34 @@ export interface AutoGenHistoryRecord {
|
||||||
agent_focus?: string | null;
|
agent_focus?: string | null;
|
||||||
architecture_phase?: string | null;
|
architecture_phase?: string | null;
|
||||||
source_spec_file?: 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;
|
} | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@
|
||||||
--rgb-active-text: 18, 18, 18;
|
--rgb-active-text: 18, 18, 18;
|
||||||
--rgb-text-main: 240, 240, 240;
|
--rgb-text-main: 240, 240, 240;
|
||||||
--rgb-text-muted: 166, 166, 170;
|
--rgb-text-muted: 166, 166, 170;
|
||||||
|
--rgb-emerald: 126, 211, 154;
|
||||||
|
--rgb-warning: 255, 196, 116;
|
||||||
--rgb-danger: 255, 126, 126;
|
--rgb-danger: 255, 126, 126;
|
||||||
--rgb-scrollbar-track: 31, 31, 36;
|
--rgb-scrollbar-track: 31, 31, 36;
|
||||||
--rgb-scrollbar-thumb: 74, 74, 82;
|
--rgb-scrollbar-thumb: 74, 74, 82;
|
||||||
|
|
@ -1802,6 +1804,55 @@ button:disabled {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.autoruns-agent-acceptance {
|
||||||
|
display: grid;
|
||||||
|
gap: 6px;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoruns-agent-pill-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoruns-agent-pill {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 24px;
|
||||||
|
padding: 0 8px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgb(var(--rgb-surface-focus));
|
||||||
|
color: rgb(var(--rgb-text-main));
|
||||||
|
font-size: 0.73rem;
|
||||||
|
line-height: 1;
|
||||||
|
border: 1px solid rgba(var(--rgb-text-main), 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoruns-agent-pill.neutral {
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoruns-agent-pill.status-accepted {
|
||||||
|
color: rgb(var(--rgb-emerald));
|
||||||
|
border-color: rgba(var(--rgb-emerald), 0.32);
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoruns-agent-pill.status-partial {
|
||||||
|
color: rgb(var(--rgb-warning));
|
||||||
|
border-color: rgba(var(--rgb-warning), 0.32);
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoruns-agent-pill.status-blocked {
|
||||||
|
color: rgb(var(--rgb-danger));
|
||||||
|
border-color: rgba(var(--rgb-danger), 0.32);
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoruns-agent-pill.status-unknown {
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
:root {
|
:root {
|
||||||
--mode-column-width: 400px;
|
--mode-column-width: 400px;
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,24 @@ def normalize_questions(raw_questions: list[Any]) -> list[str]:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def extract_semantic_tags(spec: dict[str, Any]) -> list[str]:
|
||||||
|
steps = spec.get("steps")
|
||||||
|
if not isinstance(steps, list):
|
||||||
|
return []
|
||||||
|
tags: set[str] = set()
|
||||||
|
for step in steps:
|
||||||
|
if not isinstance(step, dict):
|
||||||
|
continue
|
||||||
|
raw_tags = step.get("semantic_tags")
|
||||||
|
if not isinstance(raw_tags, list):
|
||||||
|
continue
|
||||||
|
for raw_tag in raw_tags:
|
||||||
|
tag = str(raw_tag or "").strip()
|
||||||
|
if tag:
|
||||||
|
tags.add(tag)
|
||||||
|
return sorted(tags)
|
||||||
|
|
||||||
|
|
||||||
def extract_questions_from_spec(spec: dict[str, Any]) -> list[str]:
|
def extract_questions_from_spec(spec: dict[str, Any]) -> list[str]:
|
||||||
if isinstance(spec.get("questions"), list):
|
if isinstance(spec.get("questions"), list):
|
||||||
return normalize_questions(list(spec["questions"]))
|
return normalize_questions(list(spec["questions"]))
|
||||||
|
|
@ -183,6 +201,8 @@ def build_history_record(
|
||||||
"agent_focus": metadata.get("agent_focus"),
|
"agent_focus": metadata.get("agent_focus"),
|
||||||
"architecture_phase": metadata.get("architecture_phase"),
|
"architecture_phase": metadata.get("architecture_phase"),
|
||||||
"source_spec_file": metadata.get("source_spec_file"),
|
"source_spec_file": metadata.get("source_spec_file"),
|
||||||
|
"scenario_id": metadata.get("scenario_id"),
|
||||||
|
"semantic_tags": metadata.get("semantic_tags"),
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
"generation_id": generation_id,
|
"generation_id": generation_id,
|
||||||
|
|
@ -199,6 +219,7 @@ def build_history_record(
|
||||||
|
|
||||||
|
|
||||||
def build_metadata(args: argparse.Namespace, spec: dict[str, Any], spec_path: Path | None) -> dict[str, Any]:
|
def build_metadata(args: argparse.Namespace, spec: dict[str, Any], spec_path: Path | None) -> dict[str, Any]:
|
||||||
|
semantic_tags = extract_semantic_tags(spec)
|
||||||
return {
|
return {
|
||||||
"assistant_prompt_version": args.assistant_prompt_version,
|
"assistant_prompt_version": args.assistant_prompt_version,
|
||||||
"decomposition_prompt_version": args.decomposition_prompt_version,
|
"decomposition_prompt_version": args.decomposition_prompt_version,
|
||||||
|
|
@ -206,6 +227,8 @@ def build_metadata(args: argparse.Namespace, spec: dict[str, Any], spec_path: Pa
|
||||||
"agent_focus": args.agent_focus or spec.get("description") or spec.get("title"),
|
"agent_focus": args.agent_focus or spec.get("description") or spec.get("title"),
|
||||||
"architecture_phase": args.architecture_phase,
|
"architecture_phase": args.architecture_phase,
|
||||||
"source_spec_file": str(spec_path.resolve()) if spec_path else None,
|
"source_spec_file": str(spec_path.resolve()) if spec_path else None,
|
||||||
|
"scenario_id": str(spec.get("scenario_id") or "").strip() or None,
|
||||||
|
"semantic_tags": semantic_tags,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue