|
|
@@ -9,8 +9,7 @@ import {
|
|
|
} from "@/lib/api/client";
|
|
|
import type { ConfigFileResponse } from "@/lib/api/types";
|
|
|
import { AppShell } from "@/components/layout/AppShell";
|
|
|
-import { FactGrid } from "@/components/cards/FactGrid";
|
|
|
-import { fieldAnnotation } from "@/lib/config/fieldLabels";
|
|
|
+import { RulePacksTab, WalkStrategyTab, WalkPolicyTab, QueryPromptsTab } from "@/features/config/configTables";
|
|
|
|
|
|
const TABS = [
|
|
|
{ id: "rule-packs", label: "规则包" },
|
|
|
@@ -21,326 +20,6 @@ const TABS = [
|
|
|
|
|
|
type TabId = (typeof TABS)[number]["id"];
|
|
|
|
|
|
-// walk_policy 的拍板值可能裸值,也可能带 {value, provenance, tbd} 留痕包裹。
|
|
|
-function unwrap(value: unknown): unknown {
|
|
|
- if (value && typeof value === "object" && "value" in (value as Record<string, unknown>)) {
|
|
|
- return (value as Record<string, unknown>).value;
|
|
|
- }
|
|
|
- return value;
|
|
|
-}
|
|
|
-
|
|
|
-function provenanceOf(value: unknown): string {
|
|
|
- if (value && typeof value === "object" && "provenance" in (value as Record<string, unknown>)) {
|
|
|
- return String((value as Record<string, unknown>).provenance || "");
|
|
|
- }
|
|
|
- return "";
|
|
|
-}
|
|
|
-
|
|
|
-function cell(value: unknown): string {
|
|
|
- const v = unwrap(value);
|
|
|
- if (v == null || v === "") return "—";
|
|
|
- if (typeof v === "boolean") return v ? "是" : "否";
|
|
|
- if (typeof v === "object") return JSON.stringify(v, null, 0);
|
|
|
- return String(v);
|
|
|
-}
|
|
|
-
|
|
|
-function ConfigTable({
|
|
|
- title,
|
|
|
- note,
|
|
|
- rows,
|
|
|
- fields
|
|
|
-}: {
|
|
|
- title: string;
|
|
|
- note?: string;
|
|
|
- rows: Array<Record<string, unknown>>;
|
|
|
- fields: string[];
|
|
|
-}) {
|
|
|
- return (
|
|
|
- <section className="section">
|
|
|
- <h3 className="section-title">
|
|
|
- {title} <span className="muted">{rows.length} 条</span>
|
|
|
- </h3>
|
|
|
- {note ? <p className="muted">{note}</p> : null}
|
|
|
- {rows.length === 0 ? (
|
|
|
- <div className="empty-state">无条目。</div>
|
|
|
- ) : (
|
|
|
- <div className="config-table-wrap">
|
|
|
- <table className="config-table">
|
|
|
- <thead>
|
|
|
- <tr>
|
|
|
- {fields.map((field) => {
|
|
|
- const annotation = fieldAnnotation(field);
|
|
|
- return (
|
|
|
- <th key={field} title={annotation.desc}>
|
|
|
- {annotation.label}
|
|
|
- {annotation.label !== field ? <small>{field}</small> : null}
|
|
|
- </th>
|
|
|
- );
|
|
|
- })}
|
|
|
- </tr>
|
|
|
- </thead>
|
|
|
- <tbody>
|
|
|
- {rows.map((row, index) => (
|
|
|
- <tr key={index}>
|
|
|
- {fields.map((field) => (
|
|
|
- <td key={field}>{cell(row[field])}</td>
|
|
|
- ))}
|
|
|
- </tr>
|
|
|
- ))}
|
|
|
- </tbody>
|
|
|
- </table>
|
|
|
- <details className="field-annotations">
|
|
|
- <summary className="muted">字段业务含义说明</summary>
|
|
|
- <dl>
|
|
|
- {fields.map((field) => {
|
|
|
- const annotation = fieldAnnotation(field);
|
|
|
- return (
|
|
|
- <div key={field}>
|
|
|
- <dt>
|
|
|
- {annotation.label}
|
|
|
- <small>{field}</small>
|
|
|
- </dt>
|
|
|
- <dd>{annotation.desc || "—"}</dd>
|
|
|
- </div>
|
|
|
- );
|
|
|
- })}
|
|
|
- </dl>
|
|
|
- </details>
|
|
|
- </div>
|
|
|
- )}
|
|
|
- </section>
|
|
|
- );
|
|
|
-}
|
|
|
-
|
|
|
-function asRows(value: unknown): Array<Record<string, unknown>> {
|
|
|
- return Array.isArray(value) ? (value as Array<Record<string, unknown>>) : [];
|
|
|
-}
|
|
|
-
|
|
|
-function asRecord(value: unknown): Record<string, unknown> {
|
|
|
- return value && typeof value === "object" ? (value as Record<string, unknown>) : {};
|
|
|
-}
|
|
|
-
|
|
|
-function RulePacksTab({ data }: { data: Record<string, unknown> }) {
|
|
|
- const dispatch = asRows(data.rule_pack_dispatch);
|
|
|
- const packs = asRows(data.rule_packs);
|
|
|
- return (
|
|
|
- <>
|
|
|
- <ConfigTable
|
|
|
- title="Dispatch(按对象选择规则包)"
|
|
|
- note="dispatch_enabled=否 即 future 包:已归属未运行(M3 拍板,启用需打开 dispatch)。"
|
|
|
- rows={dispatch}
|
|
|
- fields={["dispatch_id", "target_entity", "content_format", "runtime_stage", "dispatch_enabled", "rule_pack_id", "rule_pack_version", "notes"]}
|
|
|
- />
|
|
|
- {/* 遍历全部规则包(天然支持多平台);每包独立展示门槛 / 评分 / 阈值。 */}
|
|
|
- {packs.map((pack, i) => {
|
|
|
- const p = asRecord(pack);
|
|
|
- const scorecard = asRecord(p.scorecard);
|
|
|
- const packId = String(p.rule_pack_id || `包 ${i + 1}`);
|
|
|
- return (
|
|
|
- <div key={packId}>
|
|
|
- <ConfigTable
|
|
|
- title={`Hard Gates(${packId})`}
|
|
|
- note="动作 / 严重度全部配置驱动;V3 新增 not_fit_senior / low_confidence / judge_failed(Gemini 判定门槛)。"
|
|
|
- rows={asRows(p.hard_gates)}
|
|
|
- fields={["gate_id", "label", "when", "decision_action", "severity", "stop_scoring", "priority"]}
|
|
|
- />
|
|
|
- <ConfigTable
|
|
|
- title="Scorecard 维度"
|
|
|
- note="V3 实际打分 = relevance(60)+ platform_heat(40);runtime_status=deprecated 的旧 5 维已弃用,仅留档。"
|
|
|
- rows={asRows(scorecard.dimensions)}
|
|
|
- fields={["key", "label", "max_score", "weight_percent", "runtime_status"]}
|
|
|
- />
|
|
|
- <ConfigTable
|
|
|
- title="Scorecard 评分规则"
|
|
|
- rows={asRows(scorecard.scoring_rules)}
|
|
|
- fields={["scoring_rule_id", "dimension_key", "field_path", "operator", "expected_value", "score_value", "enabled"]}
|
|
|
- />
|
|
|
- <ConfigTable
|
|
|
- title="阈值(Thresholds)"
|
|
|
- rows={asRows(p.thresholds)}
|
|
|
- fields={["min_score", "max_score", "decision_action", "decision_reason_code", "effect_status", "enabled"]}
|
|
|
- />
|
|
|
- </div>
|
|
|
- );
|
|
|
- })}
|
|
|
- <ConfigTable
|
|
|
- title="效果状态映射(effect_status_mapping)"
|
|
|
- rows={asRows(data.effect_status_mapping)}
|
|
|
- fields={["mapping_id", "decision_action", "reason_category", "is_hard_gate", "content_effect_status", "query_effect_status", "priority", "enabled", "notes"]}
|
|
|
- />
|
|
|
- <ConfigTable
|
|
|
- title="决策原因码"
|
|
|
- rows={asRows(data.decision_reason_codes)}
|
|
|
- fields={["decision_reason_code", "reason_label", "reason_category", "is_hard_gate", "business_explanation"]}
|
|
|
- />
|
|
|
- </>
|
|
|
- );
|
|
|
-}
|
|
|
-
|
|
|
-const GATE_ACTION_ROWS: Array<{ key: string; cn: string }> = [
|
|
|
- { key: "ADD_TO_CONTENT_POOL", cn: "入池" },
|
|
|
- { key: "KEEP_CONTENT_FOR_REVIEW", cn: "复看" },
|
|
|
- { key: "REJECT_CONTENT", cn: "淘汰" }
|
|
|
-];
|
|
|
-
|
|
|
-function WalkPolicyTab({ data }: { data: Record<string, unknown> }) {
|
|
|
- const global = asRecord(data.global);
|
|
|
- const perm = asRecord(data.edge_permissions);
|
|
|
- const budgets = asRows(data.edge_budgets);
|
|
|
- const tiers = asRecord(data.budget_tiers);
|
|
|
- const dedup = asRecord(data.dedup);
|
|
|
-
|
|
|
- // 受控边集合(edge_permissions 三态行里出现的边,排除 _meta)
|
|
|
- const controlledEdges = new Set<string>();
|
|
|
- GATE_ACTION_ROWS.forEach(({ key }) => {
|
|
|
- const row = asRecord(perm[key]);
|
|
|
- Object.keys(row).forEach((edge) => {
|
|
|
- if (!edge.startsWith("_") && edge !== "note") controlledEdges.add(edge);
|
|
|
- });
|
|
|
- });
|
|
|
- const edgeCols = [...controlledEdges];
|
|
|
-
|
|
|
- return (
|
|
|
- <>
|
|
|
- <section className="section">
|
|
|
- <h3 className="section-title">全局护栏(global)</h3>
|
|
|
- <FactGrid
|
|
|
- rows={Object.entries(global).map(([k, v]) => {
|
|
|
- const prov = provenanceOf(v);
|
|
|
- return [
|
|
|
- `${fieldAnnotation(k).label}(${k})`,
|
|
|
- prov ? `${cell(v)} — ${prov}` : cell(v)
|
|
|
- ];
|
|
|
- })}
|
|
|
- />
|
|
|
- </section>
|
|
|
-
|
|
|
- <section className="section">
|
|
|
- <h3 className="section-title">判定门控矩阵(edge_permissions)</h3>
|
|
|
- <p className="muted">
|
|
|
- 判定动作 → 各受控出边的放行结果。hashtag_to_query 随 video_to_hashtag 链放行;其余 6 条边为导航 / 系统边,无判定门控。
|
|
|
- </p>
|
|
|
- <div className="config-table-wrap">
|
|
|
- <table className="config-table">
|
|
|
- <thead>
|
|
|
- <tr>
|
|
|
- <th>判定动作</th>
|
|
|
- {edgeCols.map((edge) => (
|
|
|
- <th key={edge}>{edge}</th>
|
|
|
- ))}
|
|
|
- </tr>
|
|
|
- </thead>
|
|
|
- <tbody>
|
|
|
- {GATE_ACTION_ROWS.map(({ key, cn }) => {
|
|
|
- const row = asRecord(perm[key]);
|
|
|
- return (
|
|
|
- <tr key={key}>
|
|
|
- <td>
|
|
|
- {cn}
|
|
|
- <small>{key}</small>
|
|
|
- </td>
|
|
|
- {edgeCols.map((edge) => {
|
|
|
- const v = String(unwrap(row[edge]) || "—");
|
|
|
- const cls =
|
|
|
- v === "allow" ? "gate-allow" : v === "allow_low_budget" ? "gate-low" : v === "deny" ? "gate-deny" : "";
|
|
|
- const vCn =
|
|
|
- v === "allow" ? "放行" : v === "allow_low_budget" ? "低预算" : v === "deny" ? "拦截" : v;
|
|
|
- return (
|
|
|
- <td key={edge} className={cls}>
|
|
|
- {vCn}
|
|
|
- </td>
|
|
|
- );
|
|
|
- })}
|
|
|
- </tr>
|
|
|
- );
|
|
|
- })}
|
|
|
- </tbody>
|
|
|
- </table>
|
|
|
- </div>
|
|
|
- </section>
|
|
|
-
|
|
|
- <ConfigTable
|
|
|
- title="边预算(edge_budgets)"
|
|
|
- note="对齐 v1 实际硬限;provenance 留拍板来源,理想值待 M7 标定。"
|
|
|
- rows={budgets}
|
|
|
- fields={["edge_id", "max_total_actions", "max_per_query", "max_works_per_author", "max_per_content", "max_pages", "max_tag_hops", "provenance"]}
|
|
|
- />
|
|
|
-
|
|
|
- <section className="section">
|
|
|
- <h3 className="section-title">预算档位(budget_tiers)与去重(dedup)</h3>
|
|
|
- <FactGrid
|
|
|
- rows={[
|
|
|
- ...Object.entries(tiers).map(([k, v]) => [`tier · ${k}`, cell(v)] as [string, string]),
|
|
|
- ...Object.entries(dedup).map(([k, v]) => [`dedup · ${k}`, cell(v)] as [string, string])
|
|
|
- ]}
|
|
|
- />
|
|
|
- </section>
|
|
|
- </>
|
|
|
- );
|
|
|
-}
|
|
|
-
|
|
|
-function WalkStrategyTab({ data }: { data: Record<string, unknown> }) {
|
|
|
- return (
|
|
|
- <>
|
|
|
- <ConfigTable
|
|
|
- title="边→规则包显式绑定(walk_rule_pack_binding)"
|
|
|
- note="仅 decision_to_asset → 内容包(advisory);其余边的判定门控见「游走护栏」tab 的 edge_permissions。"
|
|
|
- rows={asRows(data.walk_rule_pack_binding)}
|
|
|
- fields={["binding_id", "edge_id", "target_entity", "rule_pack_id", "dispatch_policy", "required", "notes"]}
|
|
|
- />
|
|
|
- <ConfigTable
|
|
|
- title="游走边目录(walk_edge_catalog)"
|
|
|
- note="共 10 条边;edge_type=navigate/reseed/stop/budget;门控与预算见「游走护栏」tab。"
|
|
|
- rows={asRows(data.walk_edge_catalog)}
|
|
|
- fields={["edge_id", "edge_type", "from_node_type", "to_node_type", "priority", "can_loop", "notes"]}
|
|
|
- />
|
|
|
- <ConfigTable
|
|
|
- title="预算策略(walk_budget_policy)"
|
|
|
- rows={asRows(data.walk_budget_policy)}
|
|
|
- fields={["budget_id", "edge_id", "max_total_actions", "max_per_query", "max_pages", "max_depth", "max_tag_hops"]}
|
|
|
- />
|
|
|
- <ConfigTable
|
|
|
- title="停止策略(walk_stop_policy)"
|
|
|
- rows={asRows(data.walk_stop_policy)}
|
|
|
- fields={["stop_policy_id", "edge_id", "condition_label", "field_path", "expected_value", "stop_action", "priority"]}
|
|
|
- />
|
|
|
- </>
|
|
|
- );
|
|
|
-}
|
|
|
-
|
|
|
-function QueryPromptsTab({ data }: { data: Record<string, unknown> }) {
|
|
|
- const profiles = asRecord(data.profiles);
|
|
|
- return (
|
|
|
- <>
|
|
|
- {Object.entries(profiles).map(([name, raw]) => {
|
|
|
- const profile = asRecord(raw);
|
|
|
- const genericFilter = asRecord(profile.generic_filter);
|
|
|
- return (
|
|
|
- <section className="section" key={name}>
|
|
|
- <h3 className="section-title">Profile · {name}</h3>
|
|
|
- <FactGrid
|
|
|
- rows={[
|
|
|
- [`${fieldAnnotation("prompt_version").label}(prompt_version)`, profile.prompt_version],
|
|
|
- [`${fieldAnnotation("temperature").label}(temperature)`, `${cell(profile.temperature)} — ${fieldAnnotation("temperature").desc}`],
|
|
|
- [`${fieldAnnotation("max_tokens").label}(max_tokens)`, profile.max_tokens],
|
|
|
- [`${fieldAnnotation("variants_per_seed").label}(variants_per_seed)`, `${cell(profile.variants_per_seed)} — ${fieldAnnotation("variants_per_seed").desc}`],
|
|
|
- [`${fieldAnnotation("evidence_fields").label}(evidence_fields)`, `${(profile.evidence_fields as unknown[] | undefined)?.length || 0} 个字段 — ${fieldAnnotation("evidence_fields").desc}`],
|
|
|
- [`${fieldAnnotation("generic_filter").label} queries`, ((genericFilter.queries as unknown[]) || []).join("、")],
|
|
|
- [`${fieldAnnotation("generic_filter").label} tokens`, ((genericFilter.tokens as unknown[]) || []).join("、")]
|
|
|
- ]}
|
|
|
- />
|
|
|
- <details>
|
|
|
- <summary className="muted">system / user prompt 原文(逐字复刻,运行真相源)</summary>
|
|
|
- <pre className="event-payload">{cell(profile.system)}</pre>
|
|
|
- <pre className="event-payload">{cell(profile.user)}</pre>
|
|
|
- </details>
|
|
|
- </section>
|
|
|
- );
|
|
|
- })}
|
|
|
- </>
|
|
|
- );
|
|
|
-}
|
|
|
-
|
|
|
export function ConfigPage() {
|
|
|
const [tab, setTab] = useState<TabId>("rule-packs");
|
|
|
const [responses, setResponses] = useState<Partial<Record<TabId, ConfigFileResponse>>>({});
|