feat: support image inputs in assistant tests

This commit is contained in:
Xin Wang
2026-08-23 23:46:18 +08:00
parent 0966f382b6
commit 9e4dfbe596
26 changed files with 1010 additions and 146 deletions
+7
View File
@@ -54,6 +54,13 @@ export type BatchTurnResult = {
id: string;
index: number;
userInput: string;
image: {
assetId: string;
filename: string;
width: number;
height: number;
sizeBytes: number;
} | null;
assistantReply: string;
toolCalls: BatchToolCallRecord[];
runtimeRecords?: Array<Record<string, unknown>>;
+17 -6
View File
@@ -1,7 +1,7 @@
/**
* 测试集 / 测试用例的前后端共享契约与编辑器辅助函数。
* 两层:Test Suite → Test Case。
* MVP 仅运行固定文字脚本;其它输入模式保留为产品路线提示。
* MVP 运行固定文字/图片脚本;其它输入模式保留为产品路线提示。
*/
/** 顶部模式选择器的唯一来源;不再单独维护「测试类型」字段 */
@@ -80,6 +80,14 @@ export type ToolCallExpectedBehavior = {
export type ExpectedBehavior = ReplyExpectedBehavior | ToolCallExpectedBehavior;
export type TestImageRef = {
assetId: string;
filename: string;
width: number;
height: number;
sizeBytes: number;
};
/**
* 固定输入的一轮:只编辑 User 输入;Agent 回复 / Tool Call 由运行时生成。
* behaviors 为 0~N 个预期行为(AND);空数组表示仅推动对话、不做断言。
@@ -87,6 +95,7 @@ export type ExpectedBehavior = ReplyExpectedBehavior | ToolCallExpectedBehavior;
export type FixedInputTurn = {
id: string;
userInput: string;
image: TestImageRef | null;
behaviors: ExpectedBehavior[];
};
@@ -134,7 +143,7 @@ export const DEFAULT_INPUT_MODE: TestCaseInputMode = "fixed_script_text";
/**
* MVP 可运行性规则。
* 目前执行器只支持固定文字;每一轮都需要用户输入,并至少配置一项断言。
* 目前执行器支持固定文字和单张图片;每轮至少有一种输入,并配置断言。
*/
export function getTestCaseValidationMessage(
item: Pick<
@@ -219,10 +228,10 @@ export function getTestCaseValidationMessage(
.join("、")}`;
}
const emptyTurnIndex = item.turns.findIndex(
(turn) => !turn.userInput.trim(),
(turn) => !turn.userInput.trim() && !turn.image,
);
if (emptyTurnIndex >= 0) {
return `第 ${emptyTurnIndex + 1} 轮用户输入不能为空`;
return `第 ${emptyTurnIndex + 1} 轮需要填写用户文字或添加图片`;
}
for (let turnIndex = 0; turnIndex < item.turns.length; turnIndex += 1) {
const turn = item.turns[turnIndex];
@@ -267,7 +276,7 @@ export function isTestCaseRunnable(
}
export const TEST_CASE_INPUT_MODE_LABEL: Record<TestCaseInputMode, string> = {
fixed_script_text: "固定脚本 · 文字",
fixed_script_text: "固定脚本 · 多模态",
fixed_script_turn_voice: "固定脚本 · 逐轮语音",
fixed_script_continuous_voice: "固定脚本 · 连续语音",
user_sim_text: "用户模拟 · 文字",
@@ -279,7 +288,7 @@ export const TEST_CASE_INPUT_MODE_SHORT_LABEL: Record<
TestCaseInputMode,
string
> = {
fixed_script_text: "固定文字",
fixed_script_text: "固定输入",
fixed_script_turn_voice: "逐轮语音",
fixed_script_continuous_voice: "连续语音",
user_sim_text: "模拟文字",
@@ -412,6 +421,7 @@ export function createEmptyFixedInputTurn(
return {
id: newEditorId("turn"),
userInput,
image: null,
behaviors: [],
};
}
@@ -484,6 +494,7 @@ export function cloneTurns(turns: FixedInputTurn[]): FixedInputTurn[] {
return turns.map((turn) => ({
id: turn.id,
userInput: turn.userInput,
image: turn.image ? { ...turn.image } : null,
behaviors: turn.behaviors.map(cloneBehavior),
}));
}