优化流程
This commit is contained in:
@@ -393,6 +393,15 @@ button:disabled {
|
||||
.theme-button { display: grid; width: 30px; place-items: center; padding: 0; }
|
||||
.config-warning { display: flex; flex: 0 0 auto; align-items: center; gap: 8px; border-bottom: 1px solid var(--ui-error-border); background: var(--ui-error-bg); color: var(--ui-error-text); font-size: 12px; padding: 8px 12px; }
|
||||
.studio-main { display: flex; min-height: 0; flex: 1; }
|
||||
.task-documents { border-top: 1px solid var(--ui-border); max-height: 34vh; overflow: auto; background: var(--ui-panel-bg); }
|
||||
.task-checklist { padding: 8px 12px; border-bottom: 1px solid var(--ui-border); display: grid; gap: 5px; }
|
||||
.task-checklist-item { display: grid; grid-template-columns: 48px minmax(0, 1fr); gap: 8px; font-size: 12px; line-height: 1.35; }
|
||||
.task-checklist-item > span { color: var(--ui-muted); }
|
||||
.task-checklist-item.is-pass > span { color: #16784d; }
|
||||
.task-checklist-item.is-fail > span { color: #b73c32; }
|
||||
.task-document { border-bottom: 1px solid var(--ui-border); padding: 8px 12px; font-size: 13px; }
|
||||
.task-document summary { cursor: pointer; font-weight: 600; }
|
||||
.task-document > :not(summary) { margin-top: 8px; }
|
||||
.agent-pane { display: flex; width: 420px; min-width: 0; min-height: 0; flex: 0 0 auto; flex-direction: column; border-right: 1px solid var(--ui-border); background: var(--ui-panel); }
|
||||
.preview-pane { position: relative; min-width: 0; min-height: 0; flex: 1; background: var(--ui-viewer-bg); }
|
||||
.agent-thread-shell, .thread-root { display: flex; min-height: 0; flex: 1; flex-direction: column; }
|
||||
|
||||
@@ -21,6 +21,7 @@ import type {
|
||||
} from "@/lib/cad-types";
|
||||
import { AgentThread } from "./agent-thread";
|
||||
import { CadViewerPreview } from "./cad-viewer-preview";
|
||||
import { MarkdownDocument } from "./rich-content";
|
||||
import type { AssistantRuntime } from "@assistant-ui/react";
|
||||
|
||||
type LoadState = "loading" | "ready" | "error";
|
||||
@@ -282,6 +283,7 @@ export function AgentStudio() {
|
||||
onCadError={handleError}
|
||||
onSelectionChange={setViewerSelection}
|
||||
taskRunning={taskRunning}
|
||||
taskRecord={taskRecord}
|
||||
/>
|
||||
</AgentRuntime>
|
||||
);
|
||||
@@ -513,6 +515,7 @@ function StudioShell({
|
||||
onCadError,
|
||||
onSelectionChange,
|
||||
taskRunning,
|
||||
taskRecord,
|
||||
}: {
|
||||
config: BackendConfig | null;
|
||||
cadResult: CadResult | null;
|
||||
@@ -532,6 +535,7 @@ function StudioShell({
|
||||
onCadError: (error: CadError) => void;
|
||||
onSelectionChange: (selection: ViewerSelectionContext | null) => void;
|
||||
taskRunning: boolean;
|
||||
taskRecord: TaskRecord | null;
|
||||
}) {
|
||||
const running = useAuiState((state) => state.thread.isRunning) || taskRunning;
|
||||
const provider = config?.providers.find((item) => item.id === providerId);
|
||||
@@ -555,7 +559,10 @@ function StudioShell({
|
||||
{!config?.configured ? <div className="config-warning"><AlertCircle size={16} /><span>未配置模型环境变量,聊天会保留诊断但不会生成虚假模型。</span></div> : null}
|
||||
{config?.autonomous_generation && !config.review_configured ? <div className="config-warning"><AlertCircle size={16} /><span>最终视觉复核未配置,任务在最终发布前会停止:{config.review_error || "请配置独立视觉模型。"}</span></div> : null}
|
||||
<div className="studio-main">
|
||||
<aside className="agent-pane"><AgentThread attachments={attachments} uploading={uploading} uploadError={uploadError} taskRunning={taskRunning} onUpload={onUpload} onCancel={onCancel} /></aside>
|
||||
<aside className="agent-pane">
|
||||
<AgentThread attachments={attachments} uploading={uploading} uploadError={uploadError} taskRunning={taskRunning} onUpload={onUpload} onCancel={onCancel} />
|
||||
<TaskDocuments task={taskRecord} />
|
||||
</aside>
|
||||
<section className="preview-pane">
|
||||
<CadViewerPreview result={cadResult} isGenerating={running} lastError={lastError} theme={theme} onError={handleViewerError} onSelectionChange={onSelectionChange} />
|
||||
</section>
|
||||
@@ -564,6 +571,21 @@ function StudioShell({
|
||||
);
|
||||
}
|
||||
|
||||
function TaskDocuments({ task }: { task: TaskRecord | null }) {
|
||||
const documents = [
|
||||
["需求文档", task?.requirements_markdown],
|
||||
["完成目标", task?.completion_target_markdown],
|
||||
["建模计划", task?.modeling_plan_markdown],
|
||||
] as const;
|
||||
if (!documents.some(([, markdown]) => markdown)) return null;
|
||||
return <section className="task-documents" aria-label="任务文档">
|
||||
{task?.checklist_progress?.length ? <div className="task-checklist" aria-label="验收进度">
|
||||
{task.checklist_progress.map((item) => <div key={item.requirement_id || item.statement} className={`task-checklist-item is-${item.status}`}><span>{item.status === "pass" ? "完成" : item.status === "fail" ? "未通过" : "待验证"}</span>{item.statement}</div>)}
|
||||
</div> : null}
|
||||
{documents.map(([title, markdown]) => markdown ? <details key={title} className="task-document"><summary>{title}</summary><MarkdownDocument>{markdown}</MarkdownDocument></details> : null)}
|
||||
</section>;
|
||||
}
|
||||
|
||||
function StudioLoading() {
|
||||
return (
|
||||
<main className="boot-screen">
|
||||
|
||||
@@ -46,7 +46,7 @@ test("keeps terminal schema field errors visible to the CAD error part", () => {
|
||||
event: "cad_error",
|
||||
data: {
|
||||
stage: "generation",
|
||||
tool: "submit_requirements_spec",
|
||||
tool: "compile_requirements_spec",
|
||||
message: "Author repeatedly failed the schema.",
|
||||
fieldErrors: [{ path: "/patches", message: "Field required" }],
|
||||
},
|
||||
@@ -54,7 +54,7 @@ test("keeps terminal schema field errors visible to the CAD error part", () => {
|
||||
assert.equal(chunk?.type, "data-cad-error");
|
||||
assert.deepEqual("data" in chunk! ? chunk.data : null, {
|
||||
stage: "generation",
|
||||
tool: "submit_requirements_spec",
|
||||
tool: "compile_requirements_spec",
|
||||
message: "Author repeatedly failed the schema.",
|
||||
fieldErrors: [{ path: "/patches", message: "Field required" }],
|
||||
});
|
||||
|
||||
@@ -20,7 +20,7 @@ export function backendEventToUiChunk(
|
||||
data: { ...item.data, sequence },
|
||||
};
|
||||
}
|
||||
if (["image_observation", "requirements_ready", "completion_result_ready", "action_selection", "tool_call", "candidate_result", "candidate_review", "final_review", "task_terminal"].includes(item.event)) {
|
||||
if (["image_observation", "requirements_document_ready", "completion_target_ready", "requirements_compiled", "modeling_plan_ready", "completion_result_ready", "action_selection", "tool_call", "candidate_result", "candidate_review", "final_review", "task_terminal"].includes(item.event)) {
|
||||
const review = item.data.review && typeof item.data.review === "object"
|
||||
? item.data.review as Record<string, unknown>
|
||||
: null;
|
||||
@@ -44,7 +44,7 @@ export function backendEventToUiChunk(
|
||||
type: "data-cad-progress",
|
||||
id: `event_${eventId}`,
|
||||
data: { step: item.event, label: ({
|
||||
image_observation: "参考图片观察", requirements_ready: "需求规格已就绪", completion_result_ready: "完成结果已就绪", action_selection: "动作选择", tool_call: "建模工具", candidate_result: "候选构建", candidate_review: "候选独立复核", final_review: "最终独立复核", task_terminal: "生成任务",
|
||||
image_observation: "参考图片观察", requirements_document_ready: "需求文档已冻结", completion_target_ready: "完成目标已冻结", requirements_compiled: "需求合同已编译", modeling_plan_ready: "建模计划已冻结", completion_result_ready: "完成结果已就绪", action_selection: "动作选择", tool_call: "建模工具", candidate_result: "候选构建", candidate_review: "候选独立复核", final_review: "最终独立复核", task_terminal: "生成任务",
|
||||
} as Record<string, string>)[item.event], status, ...metadata, message: String(
|
||||
item.data.message || item.data.reason
|
||||
|| (Array.isArray(item.data.questions) ? item.data.questions.map(String).filter(Boolean).join(";") : "")
|
||||
|
||||
@@ -121,8 +121,11 @@ export type TaskRecord = {
|
||||
requirements_contract?: Record<string, unknown> | null;
|
||||
requirements_contract_path?: string;
|
||||
requirements_markdown?: string | null;
|
||||
requirements_document_path?: string;
|
||||
completion_target_markdown?: string | null;
|
||||
completion_target_path?: string;
|
||||
modeling_plan_markdown?: string | null;
|
||||
modeling_plan_path?: string;
|
||||
completion_result_markdown?: string | null;
|
||||
completion_result_path?: string;
|
||||
claim_summary?: Array<{
|
||||
@@ -133,6 +136,11 @@ export type TaskRecord = {
|
||||
status: "pass" | "pending" | "fail" | "unavailable" | string;
|
||||
evidence?: Record<string, unknown>;
|
||||
}>;
|
||||
checklist_progress?: Array<{
|
||||
requirement_id: string;
|
||||
statement: string;
|
||||
status: "pass" | "pending" | "fail" | string;
|
||||
}>;
|
||||
pending_action?: { action_id: string; working_head: string; intent: string; requirement_ids: string[]; atomic_id: string; expected_change: string; contract_hash: string } | null;
|
||||
action_ledger_summary?: Array<Record<string, unknown>>;
|
||||
usage?: { calls: number; prompt_tokens: number; completion_tokens: number; context_chars: number };
|
||||
|
||||
Reference in New Issue
Block a user