|
|
@@ -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;
|
|
|
-
|
|
|
}
|
|
|
}
|
|
|
|