新增: 多 ReAct 执行骨架(PlanExecutor 运行时门控+DAG 进度组件) - PLAN_EXECUTION_ENABLED 改为 AtomicBool 运行时开关 - 新增 set/get_plan_execution IPC 前后端通路 - 新增 PlanProgress.vue 层状执行进度展示组件 - feature flag 默认关, 翻 true 后 process_tool_calls 可并行执行同层工具

This commit is contained in:
2026-07-01 12:59:37 +08:00
parent c89742cb9a
commit 91d80841ea
6 changed files with 160 additions and 7 deletions

View File

@@ -30,4 +30,14 @@ export const settingsApi = {
delete(key: string): Promise<boolean> {
return invoke<boolean>('settings_delete', { key })
},
/** 设置多 ReAct Plan 执行开关 */
setPlanExecution(enabled: boolean): Promise<void> {
return invoke<void>('set_plan_execution', { enabled })
},
/** 读取多 ReAct Plan 执行开关 */
getPlanExecution(): Promise<boolean> {
return invoke<boolean>('get_plan_execution')
},
}

View File

@@ -0,0 +1,98 @@
<template>
<div class="plan-progress" v-if="visible">
<div class="plan-progress-header">
<span class="plan-progress-title">{{ t('aiChat.planProgress') }}</span>
<span class="plan-progress-count">{{ doneCount }}/{{ totalCount }}</span>
</div>
<div class="plan-progress-layers">
<div v-for="(layer, li) in layers" :key="li" class="plan-layer">
<div class="plan-layer-label">{{ t('aiChat.planLayer', { n: li + 1 }) }}</div>
<div class="plan-layer-items">
<div
v-for="(item, ii) in layer"
:key="ii"
class="plan-item"
:class="'plan-item--' + item.status"
>
<span class="plan-item-icon">
<template v-if="item.status === 'done'">&#x2713;</template>
<template v-else-if="item.status === 'running'">&#x25B6;</template>
<template v-else-if="item.status === 'error'">&#x2717;</template>
<template v-else>&#x25CB;</template>
</span>
<span class="plan-item-label">{{ item.label }}</span>
</div>
</div>
<div v-if="li < layers.length - 1" class="plan-layer-arrow">&#x2193;</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
interface PlanItem {
id: string
label: string
status: 'pending' | 'running' | 'done' | 'error'
}
const props = defineProps<{
visible: boolean
layers: PlanItem[][]
}>()
const totalCount = computed(() => props.layers.reduce((s, l) => s + l.length, 0))
const doneCount = computed(() => props.layers.reduce((s, l) => s + l.filter(i => i.status === 'done').length, 0))
</script>
<style scoped>
.plan-progress {
border: 1px solid var(--df-border);
border-radius: 8px;
padding: 8px 12px;
margin: 8px 0;
background: var(--df-bg-secondary);
font-size: 12px;
}
.plan-progress-header {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
font-weight: 600;
}
.plan-progress-title { color: var(--df-text); }
.plan-progress-count { color: var(--df-text-dim); }
.plan-layer {
margin-bottom: 4px;
}
.plan-layer-label {
font-size: 11px;
color: var(--df-text-dim);
margin-bottom: 4px;
}
.plan-layer-items {
display: flex;
flex-wrap: wrap;
gap: 4px;
}
.plan-item {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 8px;
border-radius: 4px;
font-size: 11px;
background: var(--df-bg);
border: 1px solid var(--df-border);
}
.plan-item--done { border-color: var(--df-success); color: var(--df-success); }
.plan-item--running { border-color: var(--df-primary); color: var(--df-primary); }
.plan-item--error { border-color: var(--df-danger); color: var(--df-danger); }
.plan-item-icon { font-size: 10px; }
.plan-layer-arrow { text-align: center; color: var(--df-text-dim); font-size: 10px; }
</style>