656 lines
19 KiB
TypeScript
656 lines
19 KiB
TypeScript
export type CharterModuleKey =
|
||
| "module_1"
|
||
| "module_2"
|
||
| "module_3"
|
||
| "module_4"
|
||
| "module_5"
|
||
| "module_6";
|
||
|
||
export interface CharterModuleFieldSchema {
|
||
key: string;
|
||
label: string;
|
||
required?: boolean;
|
||
editable?: boolean;
|
||
multiline?: boolean;
|
||
rows?: number;
|
||
placeholder?: string;
|
||
}
|
||
|
||
export interface CharterModuleSchema {
|
||
key: CharterModuleKey;
|
||
title: string;
|
||
shortTitle: string;
|
||
fields: CharterModuleFieldSchema[];
|
||
}
|
||
|
||
export interface CharterModuleDraft {
|
||
metadata: {
|
||
status: string;
|
||
confidence_score: string;
|
||
last_validated_at: string;
|
||
evidence_refs: string;
|
||
escalation_reason: string;
|
||
};
|
||
modules: Record<CharterModuleKey, Record<string, string>>;
|
||
}
|
||
|
||
export interface ModuleProgress {
|
||
requiredTotal: number;
|
||
requiredDone: number;
|
||
percent: number;
|
||
done: boolean;
|
||
missing: string[];
|
||
}
|
||
|
||
export interface CharterProgress {
|
||
requiredTotal: number;
|
||
requiredDone: number;
|
||
requiredPercent: number;
|
||
completedModules: number;
|
||
moduleProgress: Record<CharterModuleKey, ModuleProgress>;
|
||
}
|
||
|
||
export const CHARTER_MODULE_SCHEMAS: CharterModuleSchema[] = [
|
||
{
|
||
key: "module_1",
|
||
title: "模块1:触发与目标",
|
||
shortTitle: "模块1 触发目标",
|
||
fields: [
|
||
{
|
||
key: "problem_statement",
|
||
label: "问题陈述(必填)",
|
||
required: true,
|
||
multiline: true,
|
||
rows: 3,
|
||
placeholder: "一句话问题定义,避免模糊表达",
|
||
},
|
||
{
|
||
key: "goal_statement",
|
||
label: "目标陈述(必填)",
|
||
required: true,
|
||
multiline: true,
|
||
rows: 3,
|
||
placeholder: "包含当前基线、目标值、目标日期",
|
||
},
|
||
{
|
||
key: "kpis",
|
||
label: "KPI 定义(必填)",
|
||
required: true,
|
||
multiline: true,
|
||
rows: 4,
|
||
placeholder: "每行一个指标,建议包含口径/负责人/数据源",
|
||
},
|
||
{
|
||
key: "ai_validations",
|
||
label: "AI 校验规则",
|
||
editable: false,
|
||
multiline: true,
|
||
rows: 3,
|
||
},
|
||
{
|
||
key: "escalation_trigger",
|
||
label: "升级触发条件",
|
||
editable: false,
|
||
multiline: true,
|
||
rows: 2,
|
||
},
|
||
],
|
||
},
|
||
{
|
||
key: "module_2",
|
||
title: "模块2:范围与对齐检查",
|
||
shortTitle: "模块2 范围对齐",
|
||
fields: [
|
||
{
|
||
key: "in_scope",
|
||
label: "范围内(必填)",
|
||
required: true,
|
||
multiline: true,
|
||
rows: 4,
|
||
placeholder: "每行一条功能 + 验收条件",
|
||
},
|
||
{
|
||
key: "out_of_scope",
|
||
label: "范围外(必填)",
|
||
required: true,
|
||
multiline: true,
|
||
rows: 4,
|
||
placeholder: "每行一条排除项 + 边界示例",
|
||
},
|
||
{
|
||
key: "alignment_qa",
|
||
label: "5个固定对齐问答(必填)",
|
||
required: true,
|
||
multiline: true,
|
||
rows: 5,
|
||
},
|
||
{
|
||
key: "ai_validations",
|
||
label: "AI 校验规则",
|
||
editable: false,
|
||
multiline: true,
|
||
rows: 3,
|
||
},
|
||
{
|
||
key: "escalation_trigger",
|
||
label: "升级触发条件",
|
||
editable: false,
|
||
multiline: true,
|
||
rows: 2,
|
||
},
|
||
],
|
||
},
|
||
{
|
||
key: "module_3",
|
||
title: "模块3:模型与技术依赖",
|
||
shortTitle: "模块3 模型依赖",
|
||
fields: [
|
||
{
|
||
key: "primary_model",
|
||
label: "主模型(必填)",
|
||
required: true,
|
||
placeholder: "例如:主推理模型",
|
||
},
|
||
{
|
||
key: "fallback_model",
|
||
label: "备用模型(必填)",
|
||
required: true,
|
||
placeholder: "无则写“无”",
|
||
},
|
||
{
|
||
key: "capability_table",
|
||
label: "能力边界/成本表(必填)",
|
||
required: true,
|
||
multiline: true,
|
||
rows: 4,
|
||
},
|
||
{
|
||
key: "dependency_map",
|
||
label: "技术依赖图(必填)",
|
||
required: true,
|
||
multiline: true,
|
||
rows: 4,
|
||
},
|
||
{
|
||
key: "business_view",
|
||
label: "业务视图",
|
||
multiline: true,
|
||
rows: 3,
|
||
},
|
||
{
|
||
key: "engineering_view",
|
||
label: "工程视图",
|
||
multiline: true,
|
||
rows: 3,
|
||
},
|
||
{
|
||
key: "ai_validations",
|
||
label: "AI 校验规则",
|
||
editable: false,
|
||
multiline: true,
|
||
rows: 3,
|
||
},
|
||
{
|
||
key: "escalation_trigger",
|
||
label: "升级触发条件",
|
||
editable: false,
|
||
multiline: true,
|
||
rows: 2,
|
||
},
|
||
],
|
||
},
|
||
{
|
||
key: "module_4",
|
||
title: "模块4:时间线与资源规划",
|
||
shortTitle: "模块4 计划资源",
|
||
fields: [
|
||
{
|
||
key: "milestones",
|
||
label: "里程碑计划(必填)",
|
||
required: true,
|
||
multiline: true,
|
||
rows: 4,
|
||
placeholder: "每行一个里程碑 + 开始/结束/依赖",
|
||
},
|
||
{
|
||
key: "role_effort_estimates",
|
||
label: "角色工时估算(必填)",
|
||
required: true,
|
||
multiline: true,
|
||
rows: 4,
|
||
},
|
||
{
|
||
key: "capability_owner_assignment",
|
||
label: "能力图谱分配(必填)",
|
||
required: true,
|
||
multiline: true,
|
||
rows: 3,
|
||
},
|
||
{
|
||
key: "buffer_policy",
|
||
label: "缓冲策略",
|
||
multiline: true,
|
||
rows: 2,
|
||
},
|
||
{
|
||
key: "ai_validations",
|
||
label: "AI 校验规则",
|
||
editable: false,
|
||
multiline: true,
|
||
rows: 3,
|
||
},
|
||
{
|
||
key: "escalation_trigger",
|
||
label: "升级触发条件",
|
||
editable: false,
|
||
multiline: true,
|
||
rows: 2,
|
||
},
|
||
],
|
||
},
|
||
{
|
||
key: "module_5",
|
||
title: "模块5:风险与应对",
|
||
shortTitle: "模块5 风险应对",
|
||
fields: [
|
||
{
|
||
key: "risk_register",
|
||
label: "风险台账(必填)",
|
||
required: true,
|
||
multiline: true,
|
||
rows: 4,
|
||
placeholder: "每行一个风险:类别/概率/影响/负责人",
|
||
},
|
||
{
|
||
key: "trigger_rules",
|
||
label: "触发规则(必填)",
|
||
required: true,
|
||
multiline: true,
|
||
rows: 3,
|
||
},
|
||
{
|
||
key: "execution_rule",
|
||
label: "触发后执行规则(必填)",
|
||
required: true,
|
||
multiline: true,
|
||
rows: 3,
|
||
},
|
||
{
|
||
key: "ai_validations",
|
||
label: "AI 校验规则",
|
||
editable: false,
|
||
multiline: true,
|
||
rows: 3,
|
||
},
|
||
{
|
||
key: "escalation_trigger",
|
||
label: "升级触发条件",
|
||
editable: false,
|
||
multiline: true,
|
||
rows: 2,
|
||
},
|
||
],
|
||
},
|
||
{
|
||
key: "module_6",
|
||
title: "模块6:附件与治理",
|
||
shortTitle: "模块6 附件治理",
|
||
fields: [
|
||
{
|
||
key: "attachment_metadata",
|
||
label: "附件元数据(必填)",
|
||
required: true,
|
||
multiline: true,
|
||
rows: 4,
|
||
},
|
||
{
|
||
key: "attachment_summary",
|
||
label: "附件摘要/决策提取(必填)",
|
||
required: true,
|
||
multiline: true,
|
||
rows: 3,
|
||
},
|
||
{
|
||
key: "search_tags",
|
||
label: "检索标签(必填)",
|
||
required: true,
|
||
multiline: true,
|
||
rows: 2,
|
||
},
|
||
{
|
||
key: "ai_validations",
|
||
label: "AI 校验规则",
|
||
editable: false,
|
||
multiline: true,
|
||
rows: 3,
|
||
},
|
||
{
|
||
key: "governance_rule",
|
||
label: "治理规则",
|
||
multiline: true,
|
||
rows: 2,
|
||
},
|
||
],
|
||
},
|
||
];
|
||
|
||
function toRecord(value: unknown): Record<string, unknown> {
|
||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||
return {};
|
||
}
|
||
return value as Record<string, unknown>;
|
||
}
|
||
|
||
const CHARTER_ZH_DICTIONARY: Array<[RegExp, string]> = [
|
||
[
|
||
/Develop an AI-powered automated short video generation platform with script-to-video automation and launch an API for programmatic video content generation\./gi,
|
||
"建设 AI 驱动的短视频自动化生成平台,实现脚本转视频并开放程序化视频生成 API。",
|
||
],
|
||
[
|
||
/Develop a script-to-video automation feature for Pixelle-Video\./gi,
|
||
"为 Pixelle-Video 开发脚本转视频自动化能力。",
|
||
],
|
||
[
|
||
/Launch an API for programmatic video content generation\./gi,
|
||
"上线程序化视频内容生成 API。",
|
||
],
|
||
[
|
||
/Scale the AI video infrastructure to support increased demand\./gi,
|
||
"扩容 AI 视频基础设施以支撑增长需求。",
|
||
],
|
||
[
|
||
/Successful deployment of the script-to-video automation feature\./gi,
|
||
"脚本转视频自动化能力成功上线。",
|
||
],
|
||
[
|
||
/API launch with a minimum of 100 active users within the first three months\./gi,
|
||
"API 上线后前三个月活跃用户不少于 100。",
|
||
],
|
||
[
|
||
/50% increase in AI video infrastructure capacity within six months of the funding round\./gi,
|
||
"融资后六个月内 AI 视频基础设施容量提升 50%。",
|
||
],
|
||
[
|
||
/Design and implement the script-to-video automation feature\./gi,
|
||
"设计并实现脚本转视频自动化能力。",
|
||
],
|
||
[
|
||
/Develop and deploy the API for programmatic video content generation\./gi,
|
||
"开发并部署程序化视频内容生成 API。",
|
||
],
|
||
[
|
||
/Expand the AI video infrastructure to handle increased load\./gi,
|
||
"扩展 AI 视频基础设施以承载增长负载。",
|
||
],
|
||
[
|
||
/Develop additional features not directly related to the core script-to-video automation and API functionalities\./gi,
|
||
"不开发与核心脚本转视频和 API 功能无直接关系的附加功能。",
|
||
],
|
||
[
|
||
/Manage marketing and customer acquisition activities\./gi,
|
||
"不纳入市场营销与获客运营活动。",
|
||
],
|
||
[
|
||
/Availability of skilled AI and video development resources\./gi,
|
||
"依赖具备 AI 与视频开发能力的人才资源供给。",
|
||
],
|
||
[
|
||
/Successful integration of the API with third-party platforms\./gi,
|
||
"依赖 API 与第三方平台的顺利集成。",
|
||
],
|
||
[
|
||
/Sufficient funding to support the scaling of the AI video infrastructure\./gi,
|
||
"依赖足够资金支撑 AI 视频基础设施扩容。",
|
||
],
|
||
[
|
||
/Technical challenges in implementing the script-to-video automation feature\./gi,
|
||
"脚本转视频自动化实现存在技术挑战风险。",
|
||
],
|
||
[
|
||
/Limited adoption of the API due to technical or market issues\./gi,
|
||
"受技术或市场因素影响,API 采纳率可能不足。",
|
||
],
|
||
[
|
||
/Inadequate scaling of the AI video infrastructure leading to performance issues\./gi,
|
||
"基础设施扩容不足可能引发性能问题。",
|
||
],
|
||
[/script-to-video/gi, "脚本转视频"],
|
||
[/programmatic video content generation/gi, "程序化视频内容生成"],
|
||
[/AI video infrastructure/gi, "AI 视频基础设施"],
|
||
[/funding round/gi, "融资轮次"],
|
||
[/within the first three months/gi, "前三个月内"],
|
||
[/within six months/gi, "六个月内"],
|
||
[/successful deployment/gi, "成功上线"],
|
||
];
|
||
|
||
function applyCharterZhDictionary(text: string): string {
|
||
let output = String(text || "");
|
||
if (!output.trim()) return "";
|
||
for (const [pattern, replacement] of CHARTER_ZH_DICTIONARY) {
|
||
output = output.replace(pattern, replacement);
|
||
}
|
||
return output;
|
||
}
|
||
|
||
function toText(value: unknown): string {
|
||
if (value === null || value === undefined) return "";
|
||
if (typeof value === "string") return applyCharterZhDictionary(value);
|
||
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
||
if (Array.isArray(value)) {
|
||
return value
|
||
.map((item) => {
|
||
if (item === null || item === undefined) return "";
|
||
if (typeof item === "string") return applyCharterZhDictionary(item);
|
||
if (typeof item === "number" || typeof item === "boolean") return String(item);
|
||
return applyCharterZhDictionary(JSON.stringify(item));
|
||
})
|
||
.filter((line) => line.trim().length > 0)
|
||
.join("\n");
|
||
}
|
||
try {
|
||
return applyCharterZhDictionary(JSON.stringify(value, null, 2));
|
||
} catch {
|
||
return applyCharterZhDictionary(String(value));
|
||
}
|
||
}
|
||
|
||
function splitLines(input: string): string[] {
|
||
return input
|
||
.split(/\r?\n/)
|
||
.map((line) => line.trim())
|
||
.filter((line) => line.length > 0);
|
||
}
|
||
|
||
function ensureModuleMap(): Record<CharterModuleKey, Record<string, string>> {
|
||
return {
|
||
module_1: {},
|
||
module_2: {},
|
||
module_3: {},
|
||
module_4: {},
|
||
module_5: {},
|
||
module_6: {},
|
||
};
|
||
}
|
||
|
||
function ensureModuleProgressMap(): Record<CharterModuleKey, ModuleProgress> {
|
||
return {
|
||
module_1: { requiredTotal: 0, requiredDone: 0, percent: 0, done: false, missing: [] },
|
||
module_2: { requiredTotal: 0, requiredDone: 0, percent: 0, done: false, missing: [] },
|
||
module_3: { requiredTotal: 0, requiredDone: 0, percent: 0, done: false, missing: [] },
|
||
module_4: { requiredTotal: 0, requiredDone: 0, percent: 0, done: false, missing: [] },
|
||
module_5: { requiredTotal: 0, requiredDone: 0, percent: 0, done: false, missing: [] },
|
||
module_6: { requiredTotal: 0, requiredDone: 0, percent: 0, done: false, missing: [] },
|
||
};
|
||
}
|
||
|
||
function setIfEmpty(target: Record<string, string>, key: string, value: unknown): void {
|
||
if ((target[key] || "").trim().length > 0) return;
|
||
const text = toText(value).trim();
|
||
if (text.length > 0) {
|
||
target[key] = text;
|
||
}
|
||
}
|
||
|
||
function applyLegacyMapping(
|
||
root: Record<string, unknown>,
|
||
modules: Record<CharterModuleKey, Record<string, string>>
|
||
): void {
|
||
setIfEmpty(modules.module_1, "problem_statement", root.problem_statement ?? root.rationale);
|
||
setIfEmpty(modules.module_1, "goal_statement", root.goal_statement ?? root.decision);
|
||
setIfEmpty(modules.module_1, "kpis", root.success_metrics ?? root.core_metrics);
|
||
|
||
setIfEmpty(modules.module_2, "in_scope", root.in_scope ?? root.scope_in);
|
||
setIfEmpty(modules.module_2, "out_of_scope", root.out_of_scope ?? root.scope_out);
|
||
setIfEmpty(modules.module_2, "alignment_qa", root.alignment_checklist ?? root.alignment_questions);
|
||
|
||
setIfEmpty(modules.module_3, "primary_model", root.primary_model ?? root.model_name ?? root.model);
|
||
setIfEmpty(modules.module_3, "fallback_model", root.fallback_model);
|
||
setIfEmpty(modules.module_3, "capability_table", root.capability_table ?? root.model_capabilities);
|
||
setIfEmpty(modules.module_3, "dependency_map", root.dependency_map ?? root.dependencies ?? root.tech_dependencies);
|
||
|
||
setIfEmpty(modules.module_4, "milestones", root.milestones);
|
||
setIfEmpty(modules.module_4, "role_effort_estimates", root.role_effort_estimates ?? root.resource_plan);
|
||
setIfEmpty(modules.module_4, "capability_owner_assignment", root.capability_owner_assignment ?? root.assignment);
|
||
|
||
setIfEmpty(modules.module_5, "risk_register", root.risk_register ?? root.risks);
|
||
setIfEmpty(modules.module_5, "trigger_rules", root.trigger_rules);
|
||
setIfEmpty(modules.module_5, "execution_rule", root.execution_rule ?? root.fallback_plan);
|
||
|
||
setIfEmpty(modules.module_6, "attachment_metadata", root.attachment_metadata ?? root.attachments);
|
||
setIfEmpty(modules.module_6, "attachment_summary", root.attachment_summary ?? root.notes ?? root.misc_notes);
|
||
setIfEmpty(modules.module_6, "search_tags", root.search_tags ?? root.tags);
|
||
}
|
||
|
||
export function createEmptyCharterModuleDraft(): CharterModuleDraft {
|
||
const modules = ensureModuleMap();
|
||
for (const schema of CHARTER_MODULE_SCHEMAS) {
|
||
for (const field of schema.fields) {
|
||
modules[schema.key][field.key] = "";
|
||
}
|
||
}
|
||
return {
|
||
metadata: {
|
||
status: "draft",
|
||
confidence_score: "",
|
||
last_validated_at: "",
|
||
evidence_refs: "",
|
||
escalation_reason: "",
|
||
},
|
||
modules,
|
||
};
|
||
}
|
||
|
||
export function parseCharterModuleDraft(content: Record<string, unknown> | null | undefined): CharterModuleDraft {
|
||
const root = toRecord(content);
|
||
const metadata = toRecord(root.metadata);
|
||
const modulesRoot = toRecord(root.modules);
|
||
|
||
const draft = createEmptyCharterModuleDraft();
|
||
draft.metadata.status = toText(metadata.status ?? root.status) || "draft";
|
||
draft.metadata.confidence_score = toText(metadata.confidence_score ?? root.confidence_score);
|
||
draft.metadata.last_validated_at = toText(metadata.last_validated_at ?? root.last_validated_at);
|
||
draft.metadata.evidence_refs = toText(metadata.evidence_refs ?? root.evidence_refs);
|
||
draft.metadata.escalation_reason = toText(metadata.escalation_reason ?? root.escalation_reason);
|
||
|
||
for (const schema of CHARTER_MODULE_SCHEMAS) {
|
||
const moduleRaw = toRecord(modulesRoot[schema.key]);
|
||
const directModuleRaw = toRecord(root[schema.key]);
|
||
for (const field of schema.fields) {
|
||
draft.modules[schema.key][field.key] = toText(moduleRaw[field.key] ?? directModuleRaw[field.key]);
|
||
}
|
||
}
|
||
|
||
applyLegacyMapping(root, draft.modules);
|
||
return draft;
|
||
}
|
||
|
||
export function buildCharterContentPayload(
|
||
draft: CharterModuleDraft,
|
||
originalContent: Record<string, unknown> | null | undefined
|
||
): Record<string, unknown> {
|
||
const root = toRecord(originalContent);
|
||
const payload: Record<string, unknown> = {
|
||
...root,
|
||
schema_version: "charter_ai_first_v1",
|
||
};
|
||
|
||
const confidence = Number(draft.metadata.confidence_score);
|
||
payload.metadata = {
|
||
status: draft.metadata.status.trim() || "draft",
|
||
confidence_score: Number.isFinite(confidence)
|
||
? Math.max(0, Math.min(1, confidence))
|
||
: null,
|
||
last_validated_at: draft.metadata.last_validated_at.trim() || null,
|
||
evidence_refs: splitLines(draft.metadata.evidence_refs),
|
||
escalation_reason: draft.metadata.escalation_reason.trim() || null,
|
||
};
|
||
|
||
const modules: Record<string, Record<string, string>> = {};
|
||
for (const schema of CHARTER_MODULE_SCHEMAS) {
|
||
modules[schema.key] = {};
|
||
for (const field of schema.fields) {
|
||
modules[schema.key][field.key] = draft.modules[schema.key][field.key] || "";
|
||
}
|
||
}
|
||
payload.modules = modules;
|
||
|
||
const rawSignalIds = Array.isArray(root.signal_ids) ? root.signal_ids : [];
|
||
payload.signal_ids = rawSignalIds.filter((sid) => Number.isFinite(Number(sid))).map((sid) => Number(sid));
|
||
|
||
const metrics = splitLines(draft.modules.module_1.kpis);
|
||
payload.success_metrics = metrics;
|
||
payload.core_metrics = metrics;
|
||
payload.rationale = draft.modules.module_1.problem_statement || "";
|
||
payload.decision = draft.modules.module_1.goal_statement || "";
|
||
payload.in_scope = splitLines(draft.modules.module_2.in_scope);
|
||
payload.out_of_scope = splitLines(draft.modules.module_2.out_of_scope);
|
||
payload.risks = splitLines(draft.modules.module_5.risk_register);
|
||
return payload;
|
||
}
|
||
|
||
export function calculateCharterProgress(draft: CharterModuleDraft): CharterProgress {
|
||
const moduleProgress = ensureModuleProgressMap();
|
||
let requiredTotal = 0;
|
||
let requiredDone = 0;
|
||
let completedModules = 0;
|
||
|
||
for (const schema of CHARTER_MODULE_SCHEMAS) {
|
||
const requiredFields = schema.fields.filter((field) => !!field.required);
|
||
const missing: string[] = [];
|
||
let done = 0;
|
||
|
||
for (const field of requiredFields) {
|
||
const value = draft.modules[schema.key][field.key] || "";
|
||
if (value.trim().length > 0) {
|
||
done += 1;
|
||
} else {
|
||
missing.push(field.label.replace("(必填)", ""));
|
||
}
|
||
}
|
||
|
||
const total = requiredFields.length;
|
||
const percent = total > 0 ? Math.round((done / total) * 100) : 100;
|
||
const isDone = total === done;
|
||
|
||
moduleProgress[schema.key] = {
|
||
requiredTotal: total,
|
||
requiredDone: done,
|
||
percent,
|
||
done: isDone,
|
||
missing,
|
||
};
|
||
|
||
requiredTotal += total;
|
||
requiredDone += done;
|
||
if (isDone) completedModules += 1;
|
||
}
|
||
|
||
return {
|
||
requiredTotal,
|
||
requiredDone,
|
||
requiredPercent: requiredTotal > 0 ? Math.round((requiredDone / requiredTotal) * 100) : 100,
|
||
completedModules,
|
||
moduleProgress,
|
||
};
|
||
}
|