feat: 完善 Alpha 详情页布局与提交检查,增加描述生成与验证功能
Deploy production / deploy (push) Successful in 54s

This commit is contained in:
yuxuanhui
2026-09-10 11:45:05 +08:00
parent f67b4d99b7
commit 9334b6f651
12 changed files with 500 additions and 122 deletions
+41 -46
View File
@@ -3,27 +3,27 @@ import { Banner, Button, Spin, TextArea, Toast } from "@douyinfe/semi-ui-19";
import { api, formatTime, jobStateLabels, post } from "../api";
import type { Job } from "../types";
type Description = {
idea: string;
data_rationale: string;
operator_rationale: string;
};
type Submission = {
snapshot: string;
sections: Record<string, { code: string | null; description: string }>;
descriptions: Record<string, Description>;
descriptions: Record<string, string>;
model: string;
can_generate: boolean;
can_check: boolean;
job: Job | null;
};
const fields = [
["idea", "Idea"],
["data_rationale", "Rationale for data used"],
["operator_rationale", "Rationale for operators used"],
] as const;
const render = (value: Description) =>
fields.map(([key, title]) => `${title}: ${value[key].trim()}`).join("\n");
const validDescription = (text: string) => {
const paragraphs = text.match(
/^\s*Idea:\s*(.*?)\s*Rationale for data used:\s*(.*?)\s*Rationale for operators used:\s*(.*?)\s*$/s,
);
const length = Array.from(text).length;
return (
length >= 100 &&
length <= 500 &&
!!paragraphs &&
paragraphs.slice(1).every((part) => part.trim())
);
};
const activeStates = [
"queued",
"running",
@@ -41,7 +41,7 @@ export function SubmissionPanel({
onTask: () => void;
}) {
const [data, setData] = useState<Submission | null>(null);
const [draft, setDraft] = useState<Record<string, Description>>({});
const [draft, setDraft] = useState<Record<string, string>>({});
const [snapshot, setSnapshot] = useState("");
const [busy, setBusy] = useState("");
const [error, setError] = useState("");
@@ -76,7 +76,7 @@ export function SubmissionPanel({
setBusy("generate");
setError("");
try {
const result = await post<{ descriptions: Record<string, Description> }>(
const result = await post<{ descriptions: Record<string, string> }>(
`/alphas/${id}/description/generate`,
{ snapshot },
);
@@ -115,23 +115,19 @@ export function SubmissionPanel({
const conflict = snapshot !== data.snapshot;
const valid =
Object.keys(draft).length > 0 &&
Object.values(draft).every(
(value) =>
fields.every(([key]) => value[key].trim()) &&
Array.from(render(value)).length >= 100,
);
Object.values(draft).every(validDescription);
return (
<div className="detail-section">
<h3>Description 与 Check submission</h3>
<h3>Description 与提交检查</h3>
<p className="muted">
AI 生成后可逐段修改。写回并检查会更新 BRAIN 的
AI 一次生成完整的三段内容,可在下方统一修改。写回并检查会更新 BRAIN 的
Description,随后获取平台提交检查结果,不会正式提交 Alpha。
</p>
{error && <Banner type="danger" description={error} />}
{!data.can_check && (
<Banner
type="info"
description="写回并检查需要待提交 Alpha,并先取得有效、样本完整且低于阈值的本地自相关结果。"
description="写回并检查需要待提交 Alpha。有本地比较基准时,需取得有效、完整且低于阈值的自相关结果;没有本地基准时可直接继续平台检查。"
/>
)}
{!data.can_generate && (
@@ -145,7 +141,7 @@ export function SubmissionPanel({
loading={busy === "generate"}
disabled={!!busy || running || conflict || !data.can_generate}
>
AI 生成三段 Description
AI 生成 Description
</Button>
{data.model && <span className="muted">模型:{data.model}</span>}
</div>
@@ -172,28 +168,27 @@ export function SubmissionPanel({
</pre>
</details>
)}
{fields.map(([key, title]) => (
<label key={key}>
{title}:
<TextArea
aria-label={`${section} ${title}`}
value={value[key]}
rows={3}
maxCount={6000}
disabled={!!busy || running}
onChange={(text) => {
dirty.current = true;
setDraft((current) => ({
...current,
[section]: { ...current[section], [key]: text },
}));
}}
/>
</label>
))}
<label>
完整 Description
<TextArea
aria-label={`${section} Description`}
value={value}
rows={7}
maxCount={500}
placeholder={
"Idea: 策略假设\n\nRationale for data used: 数据选择理由\n\nRationale for operators used: 算子使用理由"
}
disabled={!!busy || running}
onChange={(text) => {
dirty.current = true;
setDraft((current) => ({ ...current, [section]: text }));
}}
/>
</label>
<p className="muted">
总长度:{Array.from(render(value)).length} 字符,至少 100
字符(含标题和换行)。三段内容均需填写。
总长度:{Array.from(value).length} / 500 字符;至少 100
字符,包含标题与换行。 三个段落分别以 Idea:、Rationale for data
used:、Rationale for operators used: 开头。
</p>
</section>
))}
@@ -222,7 +217,7 @@ export function SubmissionPanel({
<div role="status">
<p>
最近任务:{jobStateLabels[data.job.status] ?? data.job.status}
{data.job.status === "completed" && ",请在“指标与检查”查看结果"}
{data.job.status === "completed" && ",请在“指标”查看结果"}
{data.job.next_retry_at &&
` · 下次轮询:${formatTime(data.job.next_retry_at)}`}
</p>