Parcourir la source

[Feat 0000]AI助手增加右侧的模型思考详情面板

wangkeyi il y a 1 semaine
Parent
commit
3f6a241aa3

+ 55 - 2
src/views/ventAI/manageAssistent/components/AiAssistantModal.vue

@@ -77,6 +77,7 @@
             @open-file-preview="handleOpenFilePreview"
             @ask-user-submit="handleAskUserSubmit"
             @ask-user-cancel="handleAskUserCancel"
+            @agent-detail-click="handleAgentDetailClick"
           />
 
           <!-- 任务进度条 -->
@@ -121,6 +122,16 @@
           />
         </div>
 
+        <!-- Agent 详情面板(与 chat-panel 并列) -->
+        <div v-if="activeAgentDetail" class="right-panel">
+          <AgentDetailPanel
+            :messageIndex="activeAgentDetail.messageIndex"
+            :agentName="activeAgentDetail.agent"
+            :messages="messages"
+            @close="closeAgentDetail"
+          />
+        </div>
+
         <!-- 定时任务详情面板(与 chat-panel 并列) -->
         <div v-if="currentScheduleTask" class="right-panel narrow">
           <ScheduleDetailPanel
@@ -178,6 +189,8 @@
   import TaskListPanel from './chatModal/TaskListPanel.vue';
   import FileListPanel from './chatModal/FileListPanel.vue';
   import ChatMessages from './chatModal/ChatMessages.vue';
+  import type { AgentDetailData } from './chatModal/ChatMessages.vue';
+  import AgentDetailPanel from './chatModal/AgentDetailPanel.vue';
   import ChatInputArea from './chatModal/ChatInputArea.vue';
   import FilePreviewPanel from './chatModal/FilePreviewPanel.vue';
   import TodoListBar from './chatModal/TodoListBar.vue';
@@ -272,6 +285,7 @@
   const previewFile = ref<AttachedFile | null>(null);
   const previewEditable = ref(false);
   const showFileList = ref(false);
+  const activeAgentDetail = ref<{ messageIndex: number; agent: string } | null>(null);
   const activePanel = ref<'chat' | 'skill' | 'sub-agents' | 'schedules'>('chat');
   const scheduleListForDetail = ref<any[]>([]);
   const showScheduleDetail = ref(false);
@@ -545,6 +559,8 @@
     inputMessage.value = '';
     pendingFile.value = null;
     previewFile.value = null;
+    activeAgentDetail.value = null;
+    showScheduleDetail.value = false;
     showFileList.value = false;
 
     if (newTask.sessionId && !historyLoadedTasks.has(taskId)) {
@@ -1011,10 +1027,26 @@
     }
   };
 
+  const handleAgentDetailClick = (data: AgentDetailData) => {
+    // 同一个 agent 再次点击则关闭面板
+    if (activeAgentDetail.value?.messageIndex === data.messageIndex && activeAgentDetail.value?.agent === data.agent) {
+      activeAgentDetail.value = null;
+    } else {
+      activeAgentDetail.value = { messageIndex: data.messageIndex, agent: data.agent };
+      previewFile.value = null;
+      showScheduleDetail.value = false;
+    }
+  };
+
+  const closeAgentDetail = () => {
+    activeAgentDetail.value = null;
+  };
+
   const openFilePreview = (file: AttachedFile) => {
     previewFile.value = file;
     previewEditable.value = false;
     showScheduleDetail.value = false;
+    activeAgentDetail.value = null;
 
     const msgIndex = messages.value.findIndex((m) => m.attachedFile?.id === file.id);
     if (msgIndex !== -1) {
@@ -1074,6 +1106,7 @@
     previewFile.value = file;
     previewEditable.value = true;
     showScheduleDetail.value = false;
+    activeAgentDetail.value = null;
   };
 
   const handleSaveSuccess = (content: string) => {
@@ -1242,14 +1275,32 @@
       // token 流式内容 - 直接拼接到消息内容
       case 'token':
         if (normalizedContent) {
-          aiMsg.content += normalizedContent;
+          if (data.source === 'subagent' && data.agent_id) {
+            // subagent 的输出内容归属到对应的 agent step
+            const targetStep = aiMsg.thinkingSteps!.findLast((s) => s.type === 'agent_start' && s.agentId === data.agent_id);
+            if (targetStep) {
+              targetStep.content = (targetStep.content || '') + normalizedContent;
+            }
+          } else {
+            // main 模型输出,保持原有逻辑
+            aiMsg.content += normalizedContent;
+          }
         }
         break;
 
       // thinking_token - 模型思考过程内容
       case 'thinking_token':
         if (normalizedContent) {
-          aiMsg.thinkingContent = (aiMsg.thinkingContent || '') + normalizedContent;
+          if (data.source === 'subagent' && data.agent_id) {
+            // subagent 的思考内容归属到对应的 agent step
+            const targetStep = aiMsg.thinkingSteps!.findLast((s) => s.type === 'agent_start' && s.agentId === data.agent_id);
+            if (targetStep) {
+              targetStep.thinkingContent = (targetStep.thinkingContent || '') + normalizedContent;
+            }
+          } else {
+            // main 模型思考,保持原有逻辑
+            aiMsg.thinkingContent = (aiMsg.thinkingContent || '') + normalizedContent;
+          }
         }
         break;
 
@@ -1271,6 +1322,8 @@
         upsertStep(aiMsg.thinkingSteps!, {
           type: 'agent_start',
           agent: data.cn_agent || data.agent || '智能助手',
+          agentId: data.agent_id,
+          subagentType: data.subagent_type,
           message: data.message || '开始处理...',
           timestamp: Date.now(),
         });

+ 423 - 0
src/views/ventAI/manageAssistent/components/chatModal/AgentDetailPanel.vue

@@ -0,0 +1,423 @@
+<template>
+  <div v-if="detailData" class="agent-detail-panel">
+    <div class="agent-detail-header">
+      <div class="agent-detail-header-left">
+        <div class="agent-detail-icon"></div>
+        <span class="agent-detail-title">{{ detailData.agent }}</span>
+      </div>
+      <div class="agent-detail-close" @click="emit('close')">
+        <div class="close-icon"></div>
+      </div>
+    </div>
+    <div class="agent-detail-body">
+      <!-- 思考内容 -->
+      <div v-if="detailData.thinkingContent" class="agent-detail-section">
+        <div class="agent-detail-section-header" @click="thinkingCollapsed = !thinkingCollapsed">
+          <span class="agent-detail-section-title">思考内容</span>
+          <div :class="['section-arrow', { collapsed: thinkingCollapsed }]">
+            <div class="arrow-icon"></div>
+          </div>
+        </div>
+        <Transition name="collapse">
+          <div v-show="!thinkingCollapsed" class="agent-detail-section-body thinking" v-html="renderMarkdown(detailData.thinkingContent)"></div>
+        </Transition>
+      </div>
+
+      <!-- 执行步骤 -->
+      <div v-if="detailData.steps.length" class="agent-detail-section">
+        <div class="agent-detail-section-header" @click="stepsCollapsed = !stepsCollapsed">
+          <span class="agent-detail-section-title">执行步骤</span>
+          <span class="agent-detail-section-count">{{ detailData.steps.length }}</span>
+          <div :class="['section-arrow', { collapsed: stepsCollapsed }]">
+            <div class="arrow-icon"></div>
+          </div>
+        </div>
+        <Transition name="collapse">
+          <div v-show="!stepsCollapsed" class="agent-detail-section-body">
+            <div v-for="(step, sIdx) in detailData.steps" :key="sIdx" class="detail-step">
+              <!-- agent_start -->
+              <div v-if="step.type === 'agent_start'" class="detail-step-item">
+                <div class="step-dot agent-start"></div>
+                <div class="step-text">{{ step.message }}</div>
+              </div>
+              <!-- tool_call -->
+              <div v-else-if="step.type === 'tool_call'" class="detail-step-item">
+                <div class="step-dot tool-call"></div>
+                <div class="step-text">
+                  <span class="step-label">调用工具:</span>
+                  <span class="step-tool">{{ step.tool }}</span>
+                  <span v-if="step.message" class="step-msg">{{ step.message }}</span>
+                </div>
+              </div>
+              <!-- executing -->
+              <div v-else-if="step.type === 'executing'" class="detail-step-item">
+                <div class="step-dot executing"></div>
+                <div class="step-text">
+                  {{ step.message || '正在执行...' }}
+                  <span v-if="step.tool" class="step-tool">{{ step.tool }}</span>
+                </div>
+              </div>
+              <!-- tool_result -->
+              <div v-else-if="step.type === 'tool_result'" class="detail-step-item">
+                <div class="step-dot tool-result"></div>
+                <div class="step-text">
+                  <span class="step-label">结果:</span>
+                  <span class="step-tool">{{ step.tool }}</span>
+                  <span class="step-msg">{{ step.message }}</span>
+                </div>
+              </div>
+              <!-- agent_done -->
+              <div v-else-if="step.type === 'agent_done'" class="detail-step-item">
+                <div class="step-dot agent-done"></div>
+                <div class="step-text">{{ step.message }}</div>
+              </div>
+            </div>
+          </div>
+        </Transition>
+      </div>
+
+      <!-- 输出内容 -->
+      <div v-if="detailData.content" class="agent-detail-section">
+        <div class="agent-detail-section-header" @click="outputCollapsed = !outputCollapsed">
+          <span class="agent-detail-section-title">输出内容</span>
+          <div :class="['section-arrow', { collapsed: outputCollapsed }]">
+            <div class="arrow-icon"></div>
+          </div>
+        </div>
+        <Transition name="collapse">
+          <div v-show="!outputCollapsed" class="agent-detail-section-body output" v-html="renderMarkdown(detailData.content)"></div>
+        </Transition>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { ref, computed } from 'vue';
+  import { renderMarkdown } from './utils';
+  import type { Message, ThinkingStep } from './types';
+
+  const props = defineProps<{
+    messageIndex: number;
+    agentName: string;
+    messages: Message[];
+  }>();
+
+  const emit = defineEmits<{
+    (e: 'close'): void;
+  }>();
+
+  const thinkingCollapsed = ref(false);
+  const stepsCollapsed = ref(false);
+  const outputCollapsed = ref(false);
+
+  const detailData = computed(() => {
+    const msg = props.messages[props.messageIndex];
+    if (!msg?.thinkingSteps) return null;
+    const agentName = props.agentName;
+    let thinkingContent = '';
+    let content = '';
+    const steps: ThinkingStep[] = [];
+    for (const step of msg.thinkingSteps) {
+      if ((step.agent || '系统') === agentName) {
+        if (step.thinkingContent) thinkingContent += step.thinkingContent;
+        if (step.content) content += step.content;
+        steps.push(step);
+      }
+    }
+    return { agent: agentName, thinkingContent, content, steps };
+  });
+</script>
+
+<style scoped lang="less">
+  .agent-detail-panel {
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    overflow: hidden;
+
+    .agent-detail-header {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding: 10px 16px;
+      border-bottom: 1px solid rgba(63, 80, 106, 0.3);
+      flex-shrink: 0;
+
+      .agent-detail-header-left {
+        display: flex;
+        align-items: center;
+        gap: 10px;
+        min-width: 0;
+      }
+
+      .agent-detail-icon {
+        width: 20px;
+        height: 20px;
+        background-image: var(--img-chat-agent-icon);
+        background-repeat: no-repeat;
+        background-size: 100% 100%;
+        flex-shrink: 0;
+      }
+
+      .agent-detail-title {
+        font-size: 15px;
+        font-weight: 600;
+        color: #e0e6ed;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+      }
+
+      .agent-detail-close {
+        width: 32px;
+        height: 32px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        border-radius: 4px;
+        cursor: pointer;
+        transition: all 0.2s;
+        flex-shrink: 0;
+
+        &:hover {
+          background: rgba(10, 132, 255, 0.15);
+        }
+
+        .close-icon {
+          width: 16px;
+          height: 16px;
+          background-image: var(--img-chat-close-icon-btn);
+          background-repeat: no-repeat;
+          background-size: 100% 100%;
+        }
+      }
+    }
+
+    .agent-detail-body {
+      flex: 1;
+      overflow-y: auto;
+      padding: 12px 16px;
+
+      .agent-detail-section {
+        margin-bottom: 8px;
+        border: 1px solid rgba(63, 80, 106, 0.25);
+        border-radius: 8px;
+        overflow: hidden;
+        background: rgba(10, 13, 23, 0.3);
+
+        &:last-child {
+          margin-bottom: 0;
+        }
+
+        .agent-detail-section-header {
+          display: flex;
+          align-items: center;
+          gap: 8px;
+          padding: 10px 14px;
+          cursor: pointer;
+          user-select: none;
+          transition: background 0.2s;
+
+          &:hover {
+            background: rgba(10, 132, 255, 0.06);
+          }
+
+          .agent-detail-section-title {
+            font-size: 13px;
+            font-weight: 600;
+            color: #8b949e;
+          }
+
+          .agent-detail-section-count {
+            font-size: 11px;
+            color: #8b949e;
+            background: rgba(10, 132, 255, 0.1);
+            padding: 1px 6px;
+            border-radius: 10px;
+          }
+
+          .section-arrow {
+            margin-left: auto;
+            display: flex;
+            align-items: center;
+            transition: transform 0.25s ease;
+
+            &.collapsed {
+              transform: rotate(-90deg);
+            }
+
+            .arrow-icon {
+              width: 10px;
+              height: 10px;
+              background-image: var(--img-chat-arrow-icon);
+              background-repeat: no-repeat;
+              background-size: 100% 100%;
+            }
+          }
+        }
+
+        .agent-detail-section-body {
+          padding: 0 14px 12px;
+          font-size: 13px;
+          line-height: 1.6;
+          color: #e0e6ed;
+          word-break: break-word;
+
+          &.thinking {
+            white-space: pre-wrap;
+            color: #b0b8c4;
+            max-height: 300px;
+            overflow-y: auto;
+          }
+
+          &.output {
+            max-height: 500px;
+            overflow-y: auto;
+          }
+
+          :deep(h1),
+          :deep(h2),
+          :deep(h3),
+          :deep(h4),
+          :deep(h5),
+          :deep(h6) {
+            color: #fff;
+          }
+          :deep(p) {
+            margin: 0;
+          }
+          :deep(code) {
+            background: rgba(10, 132, 255, 0.1);
+            padding: 1px 4px;
+            border-radius: 3px;
+            font-size: 12px;
+          }
+          :deep(pre) {
+            background: rgba(10, 132, 255, 0.1);
+            padding: 8px;
+            border-radius: 4px;
+            overflow-x: auto;
+            margin: 6px 0;
+            white-space: pre-wrap;
+          }
+          :deep(pre code) {
+            background: transparent;
+            padding: 0;
+          }
+
+          /* 步骤时间线 */
+          .detail-step {
+            position: relative;
+            padding-left: 20px;
+
+            &::before {
+              content: '';
+              position: absolute;
+              left: 5px;
+              top: 16px;
+              bottom: 0;
+              width: 1px;
+              background: rgba(63, 80, 106, 0.3);
+            }
+
+            &:last-child::before {
+              display: none;
+            }
+
+            .detail-step-item {
+              display: flex;
+              align-items: flex-start;
+              gap: 10px;
+              padding: 8px 0;
+
+              .step-dot {
+                width: 10px;
+                height: 10px;
+                border-radius: 50%;
+                flex-shrink: 0;
+                margin-top: 4px;
+                position: relative;
+                z-index: 1;
+
+                &.agent-start {
+                  background: #0a84ff;
+                  box-shadow: 0 0 6px rgba(10, 132, 255, 0.4);
+                }
+                &.tool-call {
+                  background: #79c0ff;
+                }
+                &.executing {
+                  background: #d29922;
+                  animation: pulse-dot 1.5s ease-in-out infinite;
+                }
+                &.tool-result {
+                  background: #3fb950;
+                }
+                &.agent-done {
+                  background: #8b949e;
+                }
+              }
+
+              .step-text {
+                flex: 1;
+                font-size: 12px;
+                line-height: 1.5;
+                color: #b0b8c4;
+
+                .step-label {
+                  color: #8b949e;
+                }
+
+                .step-tool {
+                  color: #79c0ff;
+                  font-family: monospace;
+                  font-size: 11px;
+                  background: rgba(10, 132, 255, 0.1);
+                  padding: 1px 5px;
+                  border-radius: 3px;
+                }
+
+                .step-msg {
+                  color: #b0b8c4;
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  .collapse-enter-active,
+  .collapse-leave-active {
+    transition: all 0.25s ease;
+    overflow: hidden;
+  }
+
+  .collapse-enter-from,
+  .collapse-leave-to {
+    opacity: 0;
+    max-height: 0;
+    padding-top: 0;
+    padding-bottom: 0;
+    margin-top: 0;
+    margin-bottom: 0;
+  }
+
+  .collapse-enter-to,
+  .collapse-leave-from {
+    opacity: 1;
+    max-height: 500px;
+  }
+
+  @keyframes pulse-dot {
+    0%,
+    100% {
+      opacity: 1;
+    }
+    50% {
+      opacity: 0.4;
+    }
+  }
+</style>

+ 95 - 179
src/views/ventAI/manageAssistent/components/chatModal/ChatMessages.vue

@@ -17,88 +17,48 @@
                   <img :src="getCssUrl('--img-chat-arrow-icon')" width="12" height="12" />
                 </div>
               </div>
-              <div v-show="!collapsedThinking[index]">
-                <div class="thinking-header">
-                  <div class="thinking-header-left">
-                    <div class="thinking-icon"></div>
-                    <span class="thinking-title">思考过程 持续了几秒</span>
-                  </div>
-                </div>
-
-                <!-- thinking_token 思考内容 -->
-                <div v-if="message.thinkingContent" class="thinking-content-card">
-                  <div class="thinking-content-header" @click="toggleThinkingContent(index)">
-                    <span class="thinking-content-title">模型思考</span>
-                    <div :class="['thinking-arrow', { expanded: !collapsedThinkingContent[index] }]">
+              <Transition name="collapse">
+                <div v-show="!collapsedThinking[index]" class="thinking-collapse-wrap">
+                  <div class="thinking-header" @click="toggleThinkingContent(index)">
+                    <div class="thinking-header-left">
+                      <div class="thinking-icon"></div>
+                      <span class="thinking-title">思考过程</span>
+                    </div>
+                    <div v-if="message.thinkingContent" :class="['thinking-arrow', { expanded: !collapsedThinkingContent[index] }]">
                       <img :src="getCssUrl('--img-chat-arrow-icon')" width="10" height="10" />
                     </div>
                   </div>
-                  <div v-show="!collapsedThinkingContent[index]" class="thinking-content-body">
-                    {{ message.thinkingContent }}
-                  </div>
-                </div>
 
-                <div class="thinking-body">
-                  <div
-                    v-for="(group, groupIdx) in groupStepsByAgent(message.thinkingSteps)"
-                    :key="groupIdx"
-                    :class="['agent-group', { expanded: expandedAgents[index + '-' + group.agent] }]"
-                  >
-                    <div class="agent-group-header" @click="toggleAgent(index + '-' + group.agent)">
-                      <div class="agent-group-header-left">
-                        <div class="agent-group-icon"></div>
-                        <span class="agent-group-name">{{ group.agent }}</span>
-                      </div>
-                      <!-- 最新一条执行结果,内联显示 -->
-                      <span v-if="getLatestStepMessage(group)" class="agent-group-latest">
-                        {{ getLatestStepMessage(group) }}
-                      </span>
-                      <div :class="['thinking-arrow', 'agent-arrow', { expanded: expandedAgents[index + '-' + group.agent] }]">
-                        <img :src="getCssUrl('--img-chat-arrow-icon')" width="10" height="10" />
-                      </div>
+                  <!-- thinking_token 思考内容 -->
+                  <Transition name="collapse">
+                    <div v-if="message.thinkingContent && !collapsedThinkingContent[index]" class="thinking-content-body">
+                      {{ message.thinkingContent }}
                     </div>
-                    <div v-show="expandedAgents[index + '-' + group.agent]" class="agent-group-body">
-                      <div v-for="(step, stepIdx) in group.steps" :key="stepIdx" class="thinking-step">
-                        <!-- agent_start -->
-                        <div v-if="step.type === 'agent_start'" class="step-item step-agent-start">
-                          <div class="step-content">
-                            <span class="step-desc">{{ step.message }}</span>
-                          </div>
-                        </div>
-                        <!-- tool_call -->
-                        <div v-else-if="step.type === 'tool_call'" class="step-item step-tool-call">
-                          <div class="step-content">
-                            <span class="step-label">调用工具:</span>
-                            <span class="step-tool-name">{{ step.tool }}</span>
-                            <span v-if="step.message" class="step-desc">{{ step.message }}</span>
-                          </div>
-                        </div>
-                        <!-- executing -->
-                        <div v-else-if="step.type === 'executing'" class="step-item step-executing">
-                          <div class="step-content">
-                            <span class="step-desc">{{ step.message || '正在执行...' }}</span>
-                            <span v-if="step.tool" class="step-tool-name">{{ step.tool }}</span>
-                          </div>
-                        </div>
-                        <!-- tool_result -->
-                        <div v-else-if="step.type === 'tool_result'" class="step-item step-tool-result">
-                          <div class="step-content">
-                            <span class="step-label">工具结果:</span>
-                            <span class="step-tool-name">{{ step.tool }}</span>
-                            <span class="step-desc">{{ step.message }}</span>
-                          </div>
+                  </Transition>
+
+                  <div class="thinking-body">
+                    <div
+                      v-for="(group, groupIdx) in groupStepsByAgent(message.thinkingSteps)"
+                      :key="groupIdx"
+                      class="agent-group"
+                      @click="handleAgentGroupClick(index, group)"
+                    >
+                      <div class="agent-group-header">
+                        <div class="agent-group-header-left">
+                          <div class="agent-group-icon"></div>
+                          <span class="agent-group-name">{{ group.agent }}</span>
                         </div>
-                        <!-- agent_done -->
-                        <div v-else-if="step.type === 'agent_done'" class="step-item step-agent-done">
-                          <div class="step-content">
-                            <span class="step-desc">{{ step.message }}</span>
-                          </div>
+                        <span v-if="getLatestStepMessage(group)" class="agent-group-latest">
+                          {{ getLatestStepMessage(group) }}
+                        </span>
+                        <div class="thinking-arrow agent-arrow">
+                          <img :src="getCssUrl('--img-chat-next-icon')" width="10" height="10" />
                         </div>
                       </div>
                     </div>
                   </div>
                 </div>
-              </div>
+              </Transition>
             </div>
 
             <div
@@ -452,12 +412,18 @@
     { immediate: true, deep: true }
   );
 
+  export interface AgentDetailData {
+    messageIndex: number;
+    agent: string;
+  }
+
   const emit = defineEmits<{
     (e: 'file-preview', file: AttachedFile): void;
     (e: 'download-word', url: string): void;
     (e: 'open-file-preview', file: AttachedFile): void;
     (e: 'ask-user-submit', answers: string[]): void;
     (e: 'ask-user-cancel'): void;
+    (e: 'agent-detail-click', data: AgentDetailData): void;
   }>();
 
   const isEditableFile = (filename: string) => isMdFile(filename) || isTextFile(filename);
@@ -578,7 +544,6 @@
   const messagesRef = ref<HTMLElement>();
   const collapsedThinking = reactive<Record<number, boolean>>({});
   const collapsedThinkingContent = reactive<Record<number, boolean>>({});
-  const expandedAgents = reactive<Record<string, boolean>>({});
   const collapsedCitations = reactive<Record<number, boolean>>({});
   const isAtBottom = ref(true);
   const copiedMessageIndex = ref(-1);
@@ -708,8 +673,11 @@
     collapsedThinkingContent[index] = !collapsedThinkingContent[index];
   };
 
-  const toggleAgent = (key: string) => {
-    expandedAgents[key] = !expandedAgents[key];
+  const handleAgentGroupClick = (messageIndex: number, group: AgentGroup) => {
+    emit('agent-detail-click', {
+      messageIndex,
+      agent: group.agent,
+    });
   };
 
   const toggleCitations = (index: number) => {
@@ -858,7 +826,7 @@
               transition: background 0.2s;
 
               &:hover {
-                background: rgba(10, 132, 255, 0.08);
+                background: rgba(10, 132, 255, 0.12);
               }
 
               .thinking-duration {
@@ -881,7 +849,15 @@
             .thinking-header {
               display: flex;
               align-items: center;
+              justify-content: space-between;
               padding: 10px 14px;
+              cursor: pointer;
+              user-select: none;
+              transition: background 0.2s;
+
+              &:hover {
+                background: rgba(10, 132, 255, 0.05);
+              }
 
               .thinking-header-left {
                 display: flex;
@@ -889,6 +865,16 @@
                 gap: 8px;
               }
 
+              .thinking-arrow {
+                display: flex;
+                align-items: center;
+                transition: transform 0.2s;
+
+                &.expanded {
+                  transform: rotate(180deg);
+                }
+              }
+
               .thinking-icon {
                 width: 16px;
                 height: 16px;
@@ -912,52 +898,18 @@
               }
             }
 
-            .thinking-content-card {
-              margin: 4px 14px 8px;
-              border: 1px solid rgba(63, 80, 106, 0.25);
+            .thinking-content-body {
+              margin: 0 14px 8px;
+              padding: 8px 12px;
+              max-height: 200px;
+              overflow-y: auto;
+              font-size: 13px;
+              line-height: 1.6;
+              color: #8b949e;
+              white-space: pre-wrap;
+              word-break: break-word;
+              background: rgba(10, 13, 23, 0.3);
               border-radius: 6px;
-              overflow: hidden;
-
-              .thinking-content-header {
-                display: flex;
-                align-items: center;
-                justify-content: space-between;
-                padding: 8px 12px;
-                cursor: pointer;
-                user-select: none;
-                transition: background 0.2s;
-
-                &:hover {
-                  background: rgba(10, 132, 255, 0.08);
-                }
-
-                .thinking-content-title {
-                  font-size: 12px;
-                  font-weight: 500;
-                  color: #8b949e;
-                }
-
-                .thinking-arrow {
-                  display: flex;
-                  align-items: center;
-                  transition: transform 0.2s;
-
-                  &.expanded {
-                    transform: rotate(180deg);
-                  }
-                }
-              }
-
-              .thinking-content-body {
-                padding: 8px 12px;
-                max-height: 200px;
-                overflow-y: auto;
-                font-size: 13px;
-                line-height: 1.6;
-                color: #8b949e;
-                white-space: pre-wrap;
-                word-break: break-word;
-              }
             }
 
             .thinking-body {
@@ -969,9 +921,11 @@
                 border-radius: 6px;
                 overflow: hidden;
                 transition: border-color 0.2s;
+                cursor: pointer;
 
-                &.expanded {
+                &:hover {
                   border-color: rgba(63, 80, 106, 0.25);
+                  background: rgba(10, 132, 255, 0.05);
                 }
 
                 &:last-child {
@@ -983,13 +937,7 @@
                   align-items: center;
                   justify-content: space-between;
                   padding: 8px 12px;
-                  cursor: pointer;
                   user-select: none;
-                  transition: background 0.2s;
-
-                  &:hover {
-                    background: rgba(10, 132, 255, 0.1);
-                  }
 
                   .agent-group-header-left {
                     display: flex;
@@ -1028,63 +976,32 @@
                     flex-shrink: 0;
                     display: flex;
                     align-items: center;
-                    transition: transform 0.2s;
-
-                    &.expanded {
-                      transform: rotate(180deg);
-                    }
                   }
                 }
-
-                .agent-group-body {
-                  padding: 4px 12px;
-                  max-height: 300px;
-                  overflow-y: auto;
-                }
               }
+            }
+          }
 
-              .thinking-step {
-                .step-item {
-                  display: flex;
-                  align-items: flex-start;
-                  padding: 6px 0;
-                  border-bottom: 1px solid rgba(63, 80, 106, 0.15);
+          .collapse-enter-active,
+          .collapse-leave-active {
+            transition: all 0.25s ease;
+            overflow: hidden;
+          }
 
-                  &:last-child {
-                    border-bottom: none;
-                  }
+          .collapse-enter-from,
+          .collapse-leave-to {
+            opacity: 0;
+            max-height: 0;
+            padding-top: 0;
+            padding-bottom: 0;
+            margin-top: 0;
+            margin-bottom: 0;
+          }
 
-                  .step-content {
-                    flex: 1;
-                    font-size: 13px;
-                    line-height: 1.5;
-                    color: #b0b8c4;
-
-                    .step-agent-name {
-                      color: #0a84ff;
-                      font-weight: 500;
-                    }
-
-                    .step-tool-name {
-                      color: #79c0ff;
-                      font-family: monospace;
-                      font-size: 12px;
-                      background: rgba(10, 132, 255, 0.1);
-                      padding: 1px 5px;
-                      border-radius: 3px;
-                    }
-
-                    .step-label {
-                      color: #8b949e;
-                    }
-
-                    .step-desc {
-                      color: #b0b8c4;
-                    }
-                  }
-                }
-              }
-            }
+          .collapse-enter-to,
+          .collapse-leave-from {
+            opacity: 1;
+            max-height: 500px;
           }
 
           .message-text {
@@ -1586,7 +1503,6 @@
                   text-decoration: none;
                   white-space: nowrap;
                   flex-shrink: 0;
-
                 }
               }
 

+ 3 - 0
src/views/ventAI/manageAssistent/components/chatModal/types.ts

@@ -49,6 +49,9 @@ export interface CitationsData {
 export interface ThinkingStep {
   type: 'agent_start' | 'agent_done' | 'tool_call' | 'tool_result' | 'executing' | 'updated_todo_list' | 'token';
   agent?: string;
+  agentId?: string;
+  subagentType?: string;
+  thinkingContent?: string;
   tool?: string;
   message?: string;
   todos?: Array<{ content: string; status: string }>;