feat: 归档 XL10 异步工具业务编译层
- 将 AgentDefinitionCompiler 升级为 AgentRuntimeCompiler - 接入 Workflow 和 Plugin 的同步/异步工具编译与 Redis 任务态 - 增加异步执行配置开关、聊天时间线聚合和后端测试
This commit is contained in:
@@ -5,6 +5,7 @@ import type {
|
||||
ChatTimelineKnowledgeHit,
|
||||
ChatTimelineMessageItem,
|
||||
ChatTimelineToolApprovalPayload,
|
||||
ChatTimelineToolStatus,
|
||||
} from '@easyflow/common-ui';
|
||||
import {ChatTimelineBuilder} from '@easyflow/common-ui';
|
||||
|
||||
@@ -30,6 +31,17 @@ function asArray(value: unknown): any[] {
|
||||
return Array.isArray(value) ? value : [];
|
||||
}
|
||||
|
||||
function asyncToolTimelineStatus(
|
||||
payload: Record<string, any>,
|
||||
): ChatTimelineToolStatus {
|
||||
const status = asText(payload.status).toUpperCase();
|
||||
if (status === 'SUCCEEDED') return 'success';
|
||||
if (status === 'FAILED' || status === 'TIMEOUT' || status === 'CANCELLED') {
|
||||
return 'error';
|
||||
}
|
||||
return 'running';
|
||||
}
|
||||
|
||||
function asTimestamp(value: unknown) {
|
||||
if (!value) {
|
||||
return Date.now();
|
||||
@@ -427,19 +439,29 @@ export function applyAgentSseEnvelope(
|
||||
return;
|
||||
}
|
||||
if (domain === 'TOOL' && (type === 'TOOL_CALL' || type === 'TOOL_RESULT')) {
|
||||
const asyncTool = payload.asyncTool === true;
|
||||
const toolName = normalizeToolName(
|
||||
payload.toolDisplayName ?? payload.toolName ?? payload.name,
|
||||
);
|
||||
ChatTimelineBuilder.upsertToolCall(items, {
|
||||
input: payload.input ?? payload.toolInput,
|
||||
output: payload.output ?? payload.result ?? payload.text,
|
||||
status: type === 'TOOL_RESULT' ? 'success' : 'running',
|
||||
output: asyncTool
|
||||
? payload.summary ?? payload.label ?? payload.output ?? payload.result ?? payload.text
|
||||
: payload.output ?? payload.result ?? payload.text,
|
||||
status: asyncTool
|
||||
? asyncToolTimelineStatus(payload)
|
||||
: type === 'TOOL_RESULT'
|
||||
? 'success'
|
||||
: 'running',
|
||||
statusKey: statusKeyForProjection(
|
||||
payload,
|
||||
metadata,
|
||||
'knowledge-retrieval',
|
||||
),
|
||||
toolCallId: normalizeToolCallId(payload),
|
||||
toolName: normalizeToolName(
|
||||
payload.toolDisplayName ?? payload.toolName ?? payload.name,
|
||||
),
|
||||
toolCallId: asyncTool
|
||||
? asText(payload.toolCallId ?? payload.taskId ?? payload.id)
|
||||
: normalizeToolCallId(payload),
|
||||
toolName,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -79,6 +79,19 @@ const selectedMcpTools = computed(() => {
|
||||
|
||||
const selectedMcpToolCount = computed(() => selectedMcpTools.value.length);
|
||||
|
||||
const asyncExecutionEnabled = computed({
|
||||
get() {
|
||||
return String(props.binding.optionsJson?.executionMode || '').toUpperCase() === 'ASYNC';
|
||||
},
|
||||
set(value: boolean) {
|
||||
props.binding.optionsJson = {
|
||||
...(props.binding.optionsJson || {}),
|
||||
executionMode: value ? 'ASYNC' : 'SYNC',
|
||||
};
|
||||
emit('change');
|
||||
},
|
||||
});
|
||||
|
||||
function handleTargetChange(value: string) {
|
||||
const option = props.options.find(
|
||||
(item) => String(item.value) === String(value),
|
||||
@@ -147,6 +160,9 @@ function handleTargetChange(value: string) {
|
||||
<ElFormItem label="执行前确认">
|
||||
<ElSwitch v-model="binding.hitlEnabled" @change="emit('change')" />
|
||||
</ElFormItem>
|
||||
<ElFormItem v-if="kind !== 'mcp'" label="异步执行">
|
||||
<ElSwitch v-model="asyncExecutionEnabled" />
|
||||
</ElFormItem>
|
||||
<ElButton
|
||||
class="agent-form__danger"
|
||||
type="danger"
|
||||
|
||||
@@ -145,6 +145,12 @@ function normalizeToolBinding(
|
||||
binding: AgentToolBinding,
|
||||
index: number,
|
||||
): AgentToolBinding {
|
||||
const optionsJson = {
|
||||
...(binding.optionsJson || {}),
|
||||
};
|
||||
if (String(optionsJson.executionMode || '').toUpperCase() !== 'ASYNC') {
|
||||
optionsJson.executionMode = 'SYNC';
|
||||
}
|
||||
return {
|
||||
...binding,
|
||||
enabled: binding.enabled !== false,
|
||||
@@ -154,9 +160,9 @@ function normalizeToolBinding(
|
||||
String(
|
||||
binding.id ||
|
||||
createLocalId(String(binding.toolType || 'tool').toLowerCase()),
|
||||
),
|
||||
),
|
||||
toolName: normalizeBindingToolName(binding),
|
||||
optionsJson: binding.optionsJson || {},
|
||||
optionsJson,
|
||||
sortNo: binding.sortNo ?? index + 1,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import type {
|
||||
ChatTimelineItem,
|
||||
ChatTimelineKnowledgeHit,
|
||||
ChatTimelineMessageItem,
|
||||
ChatTimelineToolStatus,
|
||||
} from '@easyflow/common-ui';
|
||||
import {ChatTimelineBuilder} from '@easyflow/common-ui';
|
||||
|
||||
@@ -503,18 +504,25 @@ function projectEventToTimeline(
|
||||
const displayToolName = asText(
|
||||
payload.toolDisplayName ?? rawToolName ?? '工具',
|
||||
);
|
||||
const asyncTool = payload.asyncTool === true;
|
||||
ChatTimelineBuilder.upsertToolCall(items, {
|
||||
input: payload.input ?? payload.toolInput,
|
||||
output: payload.output ?? payload.result ?? payload.text,
|
||||
status: type === 'TOOL_RESULT' ? 'success' : 'running',
|
||||
output: asyncTool
|
||||
? payload.summary ?? payload.label ?? payload.output ?? payload.result ?? payload.text
|
||||
: payload.output ?? payload.result ?? payload.text,
|
||||
status: asyncTool
|
||||
? asyncToolTimelineStatus(payload)
|
||||
: type === 'TOOL_RESULT'
|
||||
? 'success'
|
||||
: 'running',
|
||||
statusKey: statusKeyForProjection(
|
||||
payload,
|
||||
roundId,
|
||||
variantIndex,
|
||||
'knowledge-retrieval',
|
||||
),
|
||||
toolCallId: asText(payload.toolCallId ?? payload.tool_call_id ?? payload.id),
|
||||
toolName: isHiddenToolName(rawToolName) ? rawToolName : displayToolName,
|
||||
toolCallId: asText(payload.toolCallId ?? payload.taskId ?? payload.tool_call_id ?? payload.id),
|
||||
toolName: asyncTool ? displayToolName : isHiddenToolName(rawToolName) ? rawToolName : displayToolName,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -560,6 +568,15 @@ function projectEventToTimeline(
|
||||
}
|
||||
}
|
||||
|
||||
function asyncToolTimelineStatus(payload: Record<string, unknown>): ChatTimelineToolStatus {
|
||||
const status = asText(payload.status).toUpperCase();
|
||||
if (status === 'SUCCEEDED') return 'success';
|
||||
if (status === 'FAILED' || status === 'TIMEOUT' || status === 'CANCELLED') {
|
||||
return 'error';
|
||||
}
|
||||
return 'running';
|
||||
}
|
||||
|
||||
function sortedRounds(rounds: Map<string, AgentTryoutRawRound>) {
|
||||
return [...rounds.values()].sort(
|
||||
(first, second) =>
|
||||
|
||||
Reference in New Issue
Block a user