import { useEffect, useRef, useState } from "react"; 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; descriptions: Record; 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 activeStates = [ "queued", "running", "waiting_auth", "waiting_connection", ]; export function SubmissionPanel({ id, version, onTask, }: { id: string; version: string; onTask: () => void; }) { const [data, setData] = useState(null); const [draft, setDraft] = useState>({}); const [snapshot, setSnapshot] = useState(""); const [busy, setBusy] = useState(""); const [error, setError] = useState(""); const dirty = useRef(false); const mounted = useRef(true); useEffect(() => { mounted.current = true; return () => { mounted.current = false; }; }, []); useEffect(() => { let active = true; api(`/alphas/${id}/submission`) .then((value) => { if (!active) return; setData(value); if (!dirty.current) { setDraft(value.descriptions); setSnapshot(value.snapshot); } }) .catch((e) => { if (active) setError(e.message); }); return () => { active = false; }; }, [id, version]); async function generate() { setBusy("generate"); setError(""); try { const result = await post<{ descriptions: Record }>( `/alphas/${id}/description/generate`, { snapshot }, ); if (!mounted.current) return; dirty.current = true; setDraft(result.descriptions); Toast.success("Description 已生成,可修改后写回"); } catch (e) { if (mounted.current) setError((e as Error).message); } finally { if (mounted.current) setBusy(""); } } async function check() { setBusy("check"); setError(""); try { const job = await post(`/alphas/${id}/submission-check`, { snapshot, descriptions: draft, }); if (!mounted.current) return; // Keep the reviewed draft visible while the durable task runs. dirty.current = true; setData((current) => (current ? { ...current, job } : current)); onTask(); } catch (e) { if (mounted.current) setError((e as Error).message); } finally { if (mounted.current) setBusy(""); } } if (!data) return error ? : ; const running = !!data.job && activeStates.includes(data.job.status); 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, ); return (

Description 与 Check submission

AI 生成后可逐段修改。写回并检查会更新 BRAIN 的 Description,随后获取平台提交检查结果,不会正式提交 Alpha。

{error && } {!data.can_check && ( )} {!data.can_generate && (

请在个人信息 → 大模型服务中配置 Description 模型。也可以手动填写。

)}
{data.model && 模型:{data.model}}
{conflict && ( )} {Object.entries(draft).map(([section, value]) => (

{section === "regular" ? "Regular" : section === "selection" ? "Selection" : "Combo"}

{data.sections[section]?.description && (
已同步的 Description
                {data.sections[section].description}
              
)} {fields.map(([key, title]) => (