Skip to content

最新 - 给 AVA.analysis() 方法加入分步进度报告能力,并在 site 侧新增 AnalysisSteps UI 组件实现可视化展示 - #960

Closed
ninelcc336 wants to merge 6 commits into
aifrom
feat-analysis2
Closed

最新 - 给 AVA.analysis() 方法加入分步进度报告能力,并在 site 侧新增 AnalysisSteps UI 组件实现可视化展示#960
ninelcc336 wants to merge 6 commits into
aifrom
feat-analysis2

Conversation

@ninelcc336

Copy link
Copy Markdown
Contributor

新开分支实现 #958

以下是原PR描述

=============================================
image

点击 Generate 后,只有按钮变成loading,没有其他的提示了,用户只能干等着,不清楚运行情况,加上了分析步骤

变动

分析流程改造 (src/ava.ts)

  • analysis() 方法新增 options?: AnalysisOptions 参数,支持 onProgress 回调
  • 新增 createStepEmitter 工厂函数:内部维护步骤数组,按 phase 去重(同 phase 状态更新而非新增),每次 emit 后回调传出完整的 AnalysisStep[]
  • 每个分析阶段(SQL/JS 代码生成 → 执行 → 摘要 → 图表类型检测 → 可视化生成)都有进度上报,覆盖 running / done / error 三种状态

进度组件 (site/components/AnalysisSteps.tsx)

  • 可折叠的步骤进度面板,显示步骤名称、状态图标、耗时、错误信息
  • 完成态默认收起为摘要行("分析完成 · 共 N 步 · 耗时 Xs"),可展开查看详情
  • 每个步骤的 detail(代码 / SQL / 摘要)可点击查看/收起

Site 集成 (site/components/Visualization.tsx)

  • 分析请求时传递 onProgress 回调,驱动 AnalysisSteps 实时更新

@github-actions

github-actions Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor

🎊 PR Preview ac4a466 has been successfully built and deployed to https://antvis-AVA-preview-pr-960.surge.sh

🕐 Build time: 0.016s

🤖 By surge-preview

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces an analysis progress tracking feature to the AVA framework. It adds a new AnalysisSteps component to visualize the step-by-step progress of the analysis, updates the AVA class to emit progress events via an onProgress callback during SQL generation, execution, JS code generation, summarization, and visualization, and exposes the necessary types (AnalysisStep, StepStatus, etc.). Additionally, it updates GPTVisRenderer to re-render when dimensions change. The reviewer feedback suggests adding a startTime field to AnalysisStep to accurately track individual step durations, avoiding sorting steps by timestamp in the UI to prevent rendering instability, and replacing non-standard Tailwind CSS classes like ml-6.5 with standard ones like ml-6.

Comment thread src/types.ts
Comment on lines +138 to +140
/** Unix millisecond timestamp, updated on step status change */
timestamp: number;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

为了准确计算每个步骤的耗时(特别是第一步,因为调用 LLM 通常耗时最长),我们需要记录步骤的初始启动时间。否则,在状态更新时重写 timestamp 会覆盖掉启动时间,导致无法计算第一步的耗时。

建议在 AnalysisStep 接口中增加 startTime 字段。

  /** Unix millisecond timestamp, updated on step status change */
  timestamp: number;
  /** Unix millisecond timestamp when the step was first created */
  startTime: number;
}

Comment thread src/ava.ts
Comment on lines +73 to +84
} else {
steps.push({
id: String(id++),
agent: 'main',
phase: item.phase,
label,
status: item.params.status,
timestamp: Date.now(),
detail: item.params.detail,
error: item.params.error,
});
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

在首次创建并推送新步骤到 steps 数组时,初始化 startTime 字段。这样可以确保即使后续更新 timestamp,我们也能保留原始的启动时间。

      } else {
        steps.push({
          id: String(id++),
          agent: 'main',
          phase: item.phase,
          label,
          status: item.params.status,
          timestamp: Date.now(),
          startTime: Date.now(),
          detail: item.params.detail,
          error: item.params.error,
        });
      }

Comment on lines +10 to +17
function getStepTime(step: AnalysisStep, index: number, sorted: AnalysisStep[]): string | null {
if (step.status !== 'done') return null;
if (index === 0) return null;
const prevTimestamp = sorted[index - 1].timestamp;
const duration = (step.timestamp - prevTimestamp) / 1000;
if (duration <= 0) return null;
return `${duration.toFixed(1)}s`;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

引入 startTime 后,我们可以独立且准确地计算每个步骤(包括第一步)的耗时,而不需要依赖 index 或前一个步骤的 timestamp

function getStepTime(step: AnalysisStep): string | null {
  if (step.status !== 'done') return null;
  const duration = (step.timestamp - step.startTime) / 1000;
  if (duration <= 0) return null;
  return `${duration.toFixed(1)}s`;
}

Comment on lines +100 to +112
const sorted = useMemo(() => {
return [...steps].sort((a, b) => a.timestamp - b.timestamp);
}, [steps]);

const totalDuration = useMemo(() => {
if (sorted.length <= 1) return null;
const duration = (sorted[sorted.length - 1].timestamp - sorted[0].timestamp) / 1000;
if (duration <= 0) return null;
return duration;
}, [sorted]);

const hasError = sorted.some((s) => s.status === 'error');
const allDone = sorted.every((s) => s.status === 'done');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

timestamp 排序是不稳定且不必要的。由于步骤是按照创建的先后顺序自然追加到 steps 数组中的,我们可以直接使用 steps。这可以避免在同一批次中更新/创建多个步骤时(例如,前一步完成和下一步开始在同一毫秒内发生),由于时间戳相同导致排序不稳定,从而在 UI 中出现步骤顺序错乱或跳动的问题。

  const totalDuration = useMemo(() => {
    if (steps.length <= 1) return null;
    const duration = (steps[steps.length - 1].timestamp - steps[0].timestamp) / 1000;
    if (duration <= 0) return null;
    return duration;
  }, [steps]);

  const hasError = steps.some((s) => s.status === 'error');
  const allDone = steps.every((s) => s.status === 'done');

Comment on lines +117 to +124
const title = (
<span className="flex items-center gap-2 text-sm">
<DoneIcon />
<span>
Analysis complete · {sorted.length} steps{totalDuration !== null && ` · ${totalDuration.toFixed(1)}s`}
</span>
</span>
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

title 中的 sorted.length 更新为 steps.length

Suggested change
const title = (
<span className="flex items-center gap-2 text-sm">
<DoneIcon />
<span>
Analysis complete · {sorted.length} steps{totalDuration !== null && ` · ${totalDuration.toFixed(1)}s`}
</span>
</span>
);
const title = (
<span className="flex items-center gap-2 text-sm">
<DoneIcon />
<span>
Analysis complete · {steps.length} steps{totalDuration !== null && ' · ' + totalDuration.toFixed(1) + 's'}
</span>
</span>
);

Comment on lines +180 to +181
{sorted.map((step, index) => {
const stepTime = getStepTime(step, index, sorted);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

直接遍历 steps 而不是 sorted,并调用不带 index 和数组参数的 getStepTime(step)

Suggested change
{sorted.map((step, index) => {
const stepTime = getStepTime(step, index, sorted);
{steps.map((step) => {
const stepTime = getStepTime(step);

Comment on lines +229 to +231
{step.status === 'error' && step.error && (
<div className="mt-1 ml-6.5 text-xs text-red-500">{step.error}</div>
)}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Tailwind CSS 的默认间距中没有 ml-6.5。除非在 tailwind.config.js 中自定义了 6.5,否则该类将不会生效。建议使用标准类 ml-6 (24px) 或任意值 ml-[26px],以便与图标下方的文本完美对齐。

Suggested change
{step.status === 'error' && step.error && (
<div className="mt-1 ml-6.5 text-xs text-red-500">{step.error}</div>
)}
{step.status === 'error' && step.error && (
<div className="mt-1 ml-6 text-xs text-red-500">{step.error}</div>
)}

Comment on lines +234 to +237
{isExpanded && step.detail && (
<div className="mt-1.5 ml-6.5">
<pre className="p-3 bg-gray-900 text-gray-100 rounded-lg text-xs overflow-x-auto font-mono leading-relaxed">
{step.detail}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

同样,ml-6.5 不是标准的 Tailwind 间距类。建议在此处也使用 ml-6ml-[26px]

Suggested change
{isExpanded && step.detail && (
<div className="mt-1.5 ml-6.5">
<pre className="p-3 bg-gray-900 text-gray-100 rounded-lg text-xs overflow-x-auto font-mono leading-relaxed">
{step.detail}
{/* Expanded detail code block */}
{isExpanded && step.detail && (
<div className="mt-1.5 ml-6">
<pre className="p-3 bg-gray-900 text-gray-100 rounded-lg text-xs overflow-x-auto font-mono leading-relaxed">

@ninelcc336 ninelcc336 closed this May 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants