fix: 修复工具调用和样式渲染的问题
This commit is contained in:
parent
c1788f1ba3
commit
805f8c86da
|
|
@ -1,11 +1,9 @@
|
|||
<template>
|
||||
<div ref="processRef" class="process-block" :class="{ 'is-streaming': streaming }">
|
||||
<!-- Thinking Steps -->
|
||||
<div
|
||||
v-for="item in thinkingItems"
|
||||
:key="item.key"
|
||||
class="step-item thinking"
|
||||
>
|
||||
<!-- Single loop: render all steps in index order for proper alternation -->
|
||||
<template v-for="item in orderedItems">
|
||||
<!-- Thinking Step -->
|
||||
<div v-if="item.type === 'thinking'" :key="`thinking-${item.key}`" class="step-item thinking">
|
||||
<div class="step-header" @click="toggleExpand(item.key)">
|
||||
<span v-html="brainIcon"></span>
|
||||
<span class="step-label">思考中</span>
|
||||
|
|
@ -18,13 +16,8 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tool Call Steps -->
|
||||
<div
|
||||
v-for="item in toolCallItems"
|
||||
:key="item.key"
|
||||
class="step-item tool_call"
|
||||
:class="{ loading: item.loading }"
|
||||
>
|
||||
<!-- Tool Call Step -->
|
||||
<div v-else-if="item.type === 'tool_call'" :key="`tool-${item.key}`" class="step-item tool_call" :class="{ loading: item.loading }">
|
||||
<div class="step-header" @click="toggleExpand(item.key)">
|
||||
<span v-html="toolIcon"></span>
|
||||
<span class="step-label">{{ item.name || '工具调用' }}</span>
|
||||
|
|
@ -46,13 +39,9 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Text Steps -->
|
||||
<div
|
||||
v-for="item in textItems"
|
||||
:key="item.key"
|
||||
class="text-content"
|
||||
v-html="renderMarkdown(item.content)"
|
||||
></div>
|
||||
<!-- Text Step -->
|
||||
<div v-else-if="item.type === 'text'" :key="`text-${item.key}`" class="text-content md-content" v-html="renderMarkdown(item.content)"></div>
|
||||
</template>
|
||||
|
||||
<!-- Streaming indicator -->
|
||||
<div v-if="streaming && !hasContent" class="streaming-indicator">
|
||||
|
|
@ -85,6 +74,7 @@ const allItems = computed(() => {
|
|||
items.push({
|
||||
key: step.id || `thinking-${step.index}`,
|
||||
type: 'thinking',
|
||||
index: step.index,
|
||||
content: step.content || '',
|
||||
brief: step.content ? step.content.slice(0, 50) + (step.content.length > 50 ? '...' : '') : '',
|
||||
})
|
||||
|
|
@ -92,9 +82,10 @@ const allItems = computed(() => {
|
|||
items.push({
|
||||
key: step.id || `tool-${step.index}`,
|
||||
type: 'tool_call',
|
||||
index: step.index,
|
||||
id: step.id,
|
||||
name: step.name,
|
||||
args: step.args,
|
||||
args: step.arguments || step.args || '{}', // 后端发送 arguments 字段
|
||||
brief: step.name || '',
|
||||
loading: step.loading,
|
||||
isSuccess: step.isSuccess,
|
||||
|
|
@ -115,9 +106,10 @@ const allItems = computed(() => {
|
|||
items.push({
|
||||
key: `result-${step.id || step.index}`,
|
||||
type: 'tool_call',
|
||||
index: step.index,
|
||||
id: step.id_ref || step.id,
|
||||
name: step.name || '工具结果',
|
||||
args: '{}',
|
||||
args: '{}', // 占位符默认空参数
|
||||
brief: step.name || '工具结果',
|
||||
loading: false,
|
||||
isSuccess: true,
|
||||
|
|
@ -129,6 +121,7 @@ const allItems = computed(() => {
|
|||
items.push({
|
||||
key: step.id || `text-${step.index}`,
|
||||
type: 'text',
|
||||
index: step.index,
|
||||
content: step.content || '',
|
||||
})
|
||||
}
|
||||
|
|
@ -150,14 +143,33 @@ const allItems = computed(() => {
|
|||
return items
|
||||
})
|
||||
|
||||
const thinkingItems = computed(() => allItems.value.filter(i => i.type === 'thinking'))
|
||||
const toolCallItems = computed(() => allItems.value.filter(i => i.type === 'tool_call'))
|
||||
const textItems = computed(() => allItems.value.filter(i => i.type === 'text'))
|
||||
// Ordered by index for proper step alternation
|
||||
const orderedItems = computed(() =>
|
||||
[...allItems.value].sort((a, b) => (a.index || 0) - (b.index || 0))
|
||||
)
|
||||
|
||||
const orderedThinkingItems = computed(() =>
|
||||
allItems.value
|
||||
.filter(i => i.type === 'thinking')
|
||||
.sort((a, b) => (a.index || 0) - (b.index || 0))
|
||||
)
|
||||
|
||||
const orderedToolCallItems = computed(() =>
|
||||
allItems.value
|
||||
.filter(i => i.type === 'tool_call')
|
||||
.sort((a, b) => (a.index || 0) - (b.index || 0))
|
||||
)
|
||||
|
||||
const orderedTextItems = computed(() =>
|
||||
allItems.value
|
||||
.filter(i => i.type === 'text')
|
||||
.sort((a, b) => (a.index || 0) - (b.index || 0))
|
||||
)
|
||||
|
||||
const hasContent = computed(() => allItems.value.length > 0)
|
||||
const lastThinkingKey = computed(() => {
|
||||
const thinkingItems = allItems.value.filter(i => i.type === 'thinking')
|
||||
return thinkingItems.length > 0 ? thinkingItems[thinkingItems.length - 1].key : null
|
||||
const items = allItems.value.filter(i => i.type === 'thinking')
|
||||
return items.length > 0 ? items[items.length - 1].key : null
|
||||
})
|
||||
|
||||
function toggleExpand(key) {
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ export function createSSEStream(url, body, { onProcessStep, onDone, onError }) {
|
|||
} else if (line.startsWith('data: ')) {
|
||||
const data = JSON.parse(line.slice(6))
|
||||
if (currentEvent === 'process_step' && onProcessStep) {
|
||||
onProcessStep(data)
|
||||
onProcessStep(data.step)
|
||||
} else if (currentEvent === 'done' && onDone) {
|
||||
completed = true
|
||||
onDone(data)
|
||||
|
|
|
|||
|
|
@ -71,7 +71,15 @@ onMounted(fetchData)
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tools { padding: 0; }
|
||||
.tools {
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.tools > * { flex-shrink: 0; }
|
||||
.grid { flex: 1; min-height: 0; }
|
||||
.tools h1 { font-size: 2rem; margin: 0 0 1.5rem; color: var(--text-h); }
|
||||
.stats { display: flex; justify-content: space-between; align-items: center; margin-bottom: 2rem; }
|
||||
.stat { font-size: 1.1rem; color: var(--text); }
|
||||
|
|
|
|||
|
|
@ -135,9 +135,11 @@ class ChatService:
|
|||
full_thinking = ""
|
||||
tool_calls_list = []
|
||||
|
||||
# Generate new step IDs for each iteration to track multiple thoughts/tools
|
||||
iteration_thinking_step_id = f"thinking-{iteration}"
|
||||
iteration_text_step_id = f"text-{iteration}"
|
||||
# Step tracking - use unified step-{index} format
|
||||
thinking_step_id = None
|
||||
thinking_step_idx = None
|
||||
text_step_id = None
|
||||
text_step_idx = None
|
||||
|
||||
async for sse_line in llm.stream_call(
|
||||
model=model,
|
||||
|
|
@ -185,31 +187,37 @@ class ChatService:
|
|||
# Handle reasoning (thinking)
|
||||
reasoning = delta.get("reasoning_content", "")
|
||||
if reasoning:
|
||||
prev_thinking_len = len(full_thinking)
|
||||
full_thinking += reasoning
|
||||
if thinking_step_id is None:
|
||||
thinking_step_id = iteration_thinking_step_id
|
||||
if prev_thinking_len == 0: # New thinking stream started
|
||||
thinking_step_idx = step_index
|
||||
thinking_step_id = f"step-{step_index}"
|
||||
step_index += 1
|
||||
yield _sse_event("process_step", {
|
||||
"step": {
|
||||
"id": thinking_step_id,
|
||||
"index": thinking_step_idx,
|
||||
"type": "thinking",
|
||||
"content": full_thinking
|
||||
}
|
||||
})
|
||||
|
||||
# Handle content
|
||||
content = delta.get("content", "")
|
||||
if content:
|
||||
prev_content_len = len(full_content)
|
||||
full_content += content
|
||||
if text_step_id is None:
|
||||
if prev_content_len == 0: # New text stream started
|
||||
text_step_idx = step_index
|
||||
text_step_id = iteration_text_step_id
|
||||
text_step_id = f"step-{step_index}"
|
||||
step_index += 1
|
||||
yield _sse_event("process_step", {
|
||||
"step": {
|
||||
"id": text_step_id,
|
||||
"index": text_step_idx,
|
||||
"type": "text",
|
||||
"content": full_content
|
||||
}
|
||||
})
|
||||
|
||||
# Accumulate tool calls
|
||||
|
|
@ -250,42 +258,45 @@ class ChatService:
|
|||
if tool_calls_list:
|
||||
all_tool_calls.extend(tool_calls_list)
|
||||
|
||||
# Yield tool_call steps
|
||||
# Yield tool_call steps - use unified step-{index} format
|
||||
tool_call_step_ids = [] # Track step IDs for tool calls
|
||||
for tc in tool_calls_list:
|
||||
call_step_id = f"tool-{iteration}-{tc.get('function', {}).get('name', 'unknown')}"
|
||||
call_step_idx = step_index
|
||||
call_step_id = f"step-{step_index}"
|
||||
tool_call_step_ids.append(call_step_id)
|
||||
step_index += 1
|
||||
call_step = {
|
||||
"id": call_step_id,
|
||||
"index": step_index,
|
||||
"index": call_step_idx,
|
||||
"type": "tool_call",
|
||||
"id_ref": tc.get("id", ""),
|
||||
"name": tc["function"]["name"],
|
||||
"arguments": tc["function"]["arguments"]
|
||||
}
|
||||
all_steps.append(call_step)
|
||||
yield _sse_event("process_step", call_step)
|
||||
step_index += 1
|
||||
yield _sse_event("process_step", {"step": call_step})
|
||||
|
||||
# Execute tools
|
||||
tool_results = self.tool_executor.process_tool_calls_parallel(
|
||||
tool_calls_list, {}
|
||||
)
|
||||
|
||||
# Yield tool_result steps
|
||||
# Yield tool_result steps - use unified step-{index} format
|
||||
for i, tr in enumerate(tool_results):
|
||||
tool_call_step_id = tool_call_step_ids[i] if i < len(tool_call_step_ids) else f"tool-{i}"
|
||||
tool_call_step_id = tool_call_step_ids[i] if i < len(tool_call_step_ids) else f"step-{i}"
|
||||
result_step_idx = step_index
|
||||
result_step_id = f"step-{step_index}"
|
||||
step_index += 1
|
||||
result_step = {
|
||||
"id": f"result-{iteration}-{tr.get('name', 'unknown')}",
|
||||
"index": step_index,
|
||||
"id": result_step_id,
|
||||
"index": result_step_idx,
|
||||
"type": "tool_result",
|
||||
"id_ref": tool_call_step_id, # Reference to the tool_call step
|
||||
"name": tr.get("name", ""),
|
||||
"content": tr.get("content", "")
|
||||
}
|
||||
all_steps.append(result_step)
|
||||
yield _sse_event("process_step", result_step)
|
||||
step_index += 1
|
||||
yield _sse_event("process_step", {"step": result_step})
|
||||
|
||||
all_tool_results.append({
|
||||
"role": "tool",
|
||||
|
|
|
|||
Loading…
Reference in New Issue