feat: add persisted batch text testing

This commit is contained in:
Xin Wang
2026-08-10 13:49:24 +08:00
parent 5b8b9fd097
commit 19e8c8c108
27 changed files with 3855 additions and 1450 deletions
@@ -66,15 +66,10 @@ export type DebugTestCase = {
failAtTurn?: number;
};
/** 测试集:Debug Drawer 只按「当前助手关联」浏览,不做管理 */
/** 测试集:Debug Drawer 只浏览和选择,不做管理。 */
export type DebugTestSuite = {
id: string;
name: string;
/**
* 关联助手 id。MVP 用 `"*"` 表示「当前正在调试的助手都可见」;
* 接上真实 API 后改成具体 assistantId 列表。
*/
assistantIds: string[];
caseIds: string[];
};
@@ -295,15 +290,11 @@ export const MOCK_TEST_CASES: DebugTestCase[] = [
),
];
/**
* 当前助手关联的测试集(MVP mock)。
* `"*"` = 任意正在调试的助手都可见,避免把「系统全部测试集」甩进 Drawer。
*/
/** 测试集(MVP mock);测试集本身不绑定助手。 */
export const MOCK_TEST_SUITES: DebugTestSuite[] = [
{
id: "suite-accident-basic",
name: "事故快处基础流程",
assistantIds: ["*"],
caseIds: [
"tc-dual-car",
"tc-no-injury",
@@ -314,7 +305,6 @@ export const MOCK_TEST_SUITES: DebugTestSuite[] = [
{
id: "suite-clarify",
name: "异常输入与澄清",
assistantIds: ["*"],
caseIds: [
"tc-hello",
"tc-noise",
@@ -326,13 +316,11 @@ export const MOCK_TEST_SUITES: DebugTestSuite[] = [
{
id: "suite-tools",
name: "工具与系统联动",
assistantIds: ["*"],
caseIds: ["tc-injury-transfer", "tc-transfer-tool", "tc-end-call-tool"],
},
{
id: "suite-realtime-voice",
name: "实时语音交互",
assistantIds: ["*"],
caseIds: ["tc-hello", "tc-noise", "tc-barge-in", "tc-long-silence"],
},
];
@@ -347,15 +335,6 @@ function findSuite(id: string | null): DebugTestSuite | null {
return MOCK_TEST_SUITES.find((item) => item.id === id) ?? null;
}
/** 只返回当前助手关联的测试集 */
function suitesForAssistant(assistantId: string | null): DebugTestSuite[] {
return MOCK_TEST_SUITES.filter((suite) => {
if (suite.assistantIds.includes("*")) return true;
if (!assistantId) return false;
return suite.assistantIds.includes(assistantId);
});
}
function casesInSuites(suites: DebugTestSuite[]): DebugTestCase[] {
const seen = new Set<string>();
const cases: DebugTestCase[] = [];
@@ -657,23 +636,18 @@ function TestCasePickerPopover({
onOpenChange,
selectedId,
onSelect,
assistantId,
trigger,
}: {
open: boolean;
onOpenChange: (open: boolean) => void;
selectedId: string | null;
onSelect: (id: string) => void;
assistantId: string | null;
trigger: React.ReactNode;
}) {
const [query, setQuery] = useState("");
const [activeSuiteId, setActiveSuiteId] = useState<string | null>(null);
const suites = useMemo(
() => suitesForAssistant(assistantId),
[assistantId],
);
const suites = MOCK_TEST_SUITES;
const scopedCases = useMemo(() => casesInSuites(suites), [suites]);
const activeSuite = findSuite(activeSuiteId);
@@ -810,7 +784,7 @@ function TestCasePickerPopover({
</div>
{suites.length === 0 ? (
<div className="rounded-xl border border-dashed border-hairline-strong px-3 py-6 text-center text-xs text-muted-foreground">
当前助手还没有关联测试集
暂无测试集
</div>
) : (
suites.map((suite) => (
@@ -851,7 +825,6 @@ function TestCasePickerPopover({
export function AutoTestRunBar({
state,
assistantId,
onSelectCase,
onClearCase,
onStart,
@@ -859,7 +832,6 @@ export function AutoTestRunBar({
onRerun,
}: {
state: AutoTestRunState;
assistantId: string | null;
onSelectCase: (id: string) => void;
onClearCase: () => void;
onStart: () => void;
@@ -877,7 +849,6 @@ export function AutoTestRunBar({
onOpenChange={setPickerOpen}
selectedId={state.selectedId}
onSelect={onSelectCase}
assistantId={assistantId}
trigger={
<button
type="button"
@@ -985,7 +956,6 @@ export function AutoTestRunBar({
onOpenChange={setPickerOpen}
selectedId={state.selectedId}
onSelect={onSelectCase}
assistantId={assistantId}
trigger={
<button
type="button"