mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 00:21:58 +00:00
数据目录闭环(前端)复查修正:部分完成的宽表也写「已写进目录 N 段」(段进度都结束了,看不出写进了几段),服务端原因里已经写了的不重复;表助手「补上留空的列」超过 20 列时写明一共几列;流式回答每来一段只重画正在回答的那一轮;答完的一轮没有用量、没有查询时不再露出一个 0;check-catalog 补部分完成的那一条
This commit is contained in:
@@ -3005,7 +3005,8 @@ export const CATALOG_ASSISTANT_TEXT = {
|
||||
`上次起草有 ${formatNumber(n)} 列模型拿不准、留空了${cols.length ? `,例如 ${cols.join('、')}` : ''}。可以先从这几列开始。`,
|
||||
suggestLabel: '可以这样开始',
|
||||
suggestBlank: (n: number) => `补上留空的 ${formatNumber(n)} 列`,
|
||||
suggestBlankText: (cols: string[]) => `请帮我补上这几列的中文名和含义:${cols.join('、')}。拿不准的地方先问我。`,
|
||||
suggestBlankText: (cols: string[], total: number) =>
|
||||
`请帮我补上这几列的中文名和含义:${cols.join('、')}${total > cols.length ? ` 等 ${formatNumber(total)} 列` : ''}。拿不准的地方先问我。`,
|
||||
suggestFacts: '核对有效记录条件和去重规则',
|
||||
suggestFactsText: '这张表的有效记录条件和去重规则应该怎么写?拿不准的地方先问我。',
|
||||
suggestPending: '看看哪些推断可以确认',
|
||||
|
||||
@@ -533,13 +533,13 @@ function TaskRow({ task: t, jobError }: { task: CatalogDraftTask; jobError: stri
|
||||
const reason = jobError && t.error === jobError ? (t.model_error ?? '') : taskReason(t)
|
||||
const started = t.status !== 'pending' && t.status !== 'skipped'
|
||||
const parts = t.parts_total != null && t.parts_total > 1 && started
|
||||
// 段被缩小、拆半后实际的块(比段多时才写);已经写进目录的段:起草中写着(停止也不丢),停下来时服务端的原因里
|
||||
// 没说的才补一句(没写全的段为什么没写出来,服务端的原因里有),写完了的不写
|
||||
// 段被缩小、拆半后实际的块(比段多时才写);已经写进目录的段:起草中、部分完成时写着(停止也不丢,部分完成的
|
||||
// 段进度是「都结束了」,看不出写进了几段),停下来时补一句剩下几段没起草。服务端的原因里已经写了的不重复,写完了的不写
|
||||
const chunks = started ? chunksOf(t) : null
|
||||
const written = writtenOf(t)
|
||||
const writtenText = written > 0 && t.status === 'running' ? DT.partsWritten(written)
|
||||
: written > 0 && t.status === 'cancelled' && !reason.includes(DT.partsWritten(written))
|
||||
? DT.partsWrittenStopped(written, Math.max(0, (t.parts_total ?? written) - written)) : ''
|
||||
const writtenText = written <= 0 || reason.includes(DT.partsWritten(written)) ? ''
|
||||
: t.status === 'running' || t.status === 'partial' ? DT.partsWritten(written)
|
||||
: t.status === 'cancelled' ? DT.partsWrittenStopped(written, Math.max(0, (t.parts_total ?? written) - written)) : ''
|
||||
const changes = DT.taskAdded(t.added, t.updated)
|
||||
const blank = blankOf(t)
|
||||
const sample = t.blank_sample?.filter(Boolean) ?? []
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react'
|
||||
import { memo, useCallback, useEffect, useId, useMemo, useRef, useState } from 'react'
|
||||
import type { KeyboardEvent as ReactKeyboardEvent } from 'react'
|
||||
import { ArrowUp, BookMarked, Database, Info, MessagesSquare, Minus, Plus, Square, Trash2, X } from 'lucide-react'
|
||||
import clsx from 'clsx'
|
||||
@@ -149,11 +149,12 @@ export function TableAssistant({ sourceId, table, tableLabel, detail, profileEna
|
||||
if (!streaming) send(text)
|
||||
}
|
||||
|
||||
const drop = (path: string) => setPending((cur) => dropPending(cur, path))
|
||||
const restore = (part: Extract<TurnPart, { kind: 'proposal' }>, path: string) => {
|
||||
// 两个回调保持不变:流式的正文每来一段只重画正在回答的那一轮
|
||||
const drop = useCallback((path: string) => setPending((cur) => dropPending(cur, path)), [])
|
||||
const restore = useCallback((part: Extract<TurnPart, { kind: 'proposal' }>, path: string) => {
|
||||
const change = part.changes.find((c) => c.path === path)
|
||||
if (change) setPending((cur) => mergeProposal(cur, [change], part.id, part.version))
|
||||
}
|
||||
}, [])
|
||||
const clearAll = async () => {
|
||||
if (!pendingRef.current.length) return
|
||||
if (!(await confirmDialog({ title: T.clearTitle(pendingRef.current.length), confirmLabel: T.clearConfirm, danger: true }))) return
|
||||
@@ -259,7 +260,7 @@ export function TableAssistant({ sourceId, table, tableLabel, detail, profileEna
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{lastBlank > 0 && blankCols.length > 0 && (
|
||||
<SuggestButton kind="blank" label={T.suggestBlank(lastBlank)}
|
||||
onClick={() => send(T.suggestBlankText(blankCols.slice(0, 20)))} />
|
||||
onClick={() => send(T.suggestBlankText(blankCols.slice(0, 20), blankCols.length))} />
|
||||
)}
|
||||
<SuggestButton kind="facts" label={T.suggestFacts} onClick={() => send(T.suggestFactsText)} />
|
||||
{hasProposed && <SuggestButton kind="pending" label={T.suggestPending} onClick={() => send(T.suggestPendingText)} />}
|
||||
@@ -333,7 +334,7 @@ function SuggestButton({ kind, label, onClick }: { kind: string; label: string;
|
||||
// 一轮回答:正文、只读查询、修改提案按到来的先后排;末尾写这一轮的结局(出错、停止、用量)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function AssistantTurnView({ turn, pending, saved, onDrop, onRestore }: {
|
||||
const AssistantTurnView = memo(function AssistantTurnView({ turn, pending, saved, onDrop, onRestore }: {
|
||||
turn: AssistantTurn
|
||||
pending: PendingChange[]
|
||||
saved: ReadonlySet<string>
|
||||
@@ -366,14 +367,14 @@ function AssistantTurnView({ turn, pending, saved, onDrop, onRestore }: {
|
||||
{turn.state === 'stopped' && (
|
||||
<p className="text-2xs text-faint" data-assistant-turn-status="stopped">{T.stopped}</p>
|
||||
)}
|
||||
{turn.state === 'done' && (usage || turn.queries) && (
|
||||
{turn.state === 'done' && (!!usage || (turn.queries ?? 0) > 0) && (
|
||||
<p className="tnum text-2xs text-faint" data-assistant-turn-status="done">
|
||||
{[usage ? T.usage(usage.input_tokens, usage.output_tokens) : '', turn.queries ? T.queries(turn.queries) : ''].filter(Boolean).join(' · ')}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
/** 助手跑过的一条只读查询:摘要写行数(失败写原因),展开看 SQL 和前几行 */
|
||||
function QueryBlock({ part }: { part: Extract<TurnPart, { kind: 'query' }> }) {
|
||||
|
||||
@@ -1604,8 +1604,9 @@ await section('数据目录 · 起草:逐段写进目录、块进度与停止
|
||||
check('……可以只重试这 2 张', (await page.locator('[data-draft-retry]').innerText().catch(() => '')).includes('(2 张)'))
|
||||
await page.locator('[data-draft-close]').click()
|
||||
|
||||
// 老服务端停下来时原因里没写:界面自己补一句
|
||||
const legacy = newJob(state, S1, { tables: ['store_sales'], use_model: true })
|
||||
// 老服务端停下来时原因里没写:界面自己补一句。部分完成的表段进度是「都结束了」,看不出写进了几段:也写一句
|
||||
const legacy = newJob(state, S1, { tables: ['store_sales', 'members'], use_model: true })
|
||||
settle(state, legacy, 'members', { status: 'partial', parts_total: 2, parts_done: 2, parts_written: 1, model_error: CALL_ERR })
|
||||
settle(state, legacy, 'store_sales', { status: 'running', parts_done: 1, parts_written: 1 })
|
||||
stopJob(legacy)
|
||||
legacy.tasks[0].model_error = null
|
||||
@@ -1615,6 +1616,8 @@ await section('数据目录 · 起草:逐段写进目录、块进度与停止
|
||||
await draftBox(page).locator('[data-draft-last-open]').click()
|
||||
check('服务端的原因里没写已写进几段:界面补「已写进目录 1 段,剩下 2 段没起草」', await until(async () => (await text('store_sales', '[data-draft-task-written]')) === '已写进目录 1 段,剩下 2 段没起草'),
|
||||
await text('store_sales', '[data-draft-task-written]'))
|
||||
check('……部分完成的表写「已写进目录 1 段」,原因照旧', (await text('members', '[data-draft-task-written]')) === '已写进目录 1 段'
|
||||
&& (await text('members', '[data-draft-task-reason]')).includes(CALL_ERR), await text('members', '[data-draft-task-written]'))
|
||||
await page.locator('[data-draft-close]').click()
|
||||
state.jobs.pop()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user