fix: 修复参数传递bug

This commit is contained in:
ViperEkura 2026-04-21 18:15:32 +08:00
parent feabfc8537
commit 29fe4b6e6f
7 changed files with 1197 additions and 234 deletions

View File

@ -40,10 +40,6 @@ const navItems = [
path: '/tools',
icon: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"></path></svg>`
},
{
path: '/agents',
icon: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a10 10 0 1 0 10 10 4 4 0 0 1-5-5 4 4 0 0 1-5-5"></path><path d="M8.5 8.5v.01"></path><path d="M16 15.5v.01"></path><path d="M12 12v.01"></path><path d="M11 17v.01"></path><path d="M7 14v.01"></path></svg>`
},
{
path: '/settings',
icon: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>`

View File

@ -0,0 +1,142 @@
<template>
<div class="inline-form">
<div class="form-header">
<h3>{{ title }}</h3>
<button class="btn-close" @click="$emit('close')">×</button>
</div>
<form @submit.prevent="$emit('submit')">
<slot></slot>
<div class="form-actions">
<button type="button" class="btn-secondary" @click="$emit('close')">取消</button>
<button type="submit" class="btn-primary">{{ submitText }}</button>
</div>
</form>
</div>
</template>
<script setup>
defineProps({
title: { type: String, required: true },
submitText: { type: String, default: '保存' },
})
defineEmits(['submit', 'close'])
</script>
<style scoped>
.inline-form {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--bg-primary);
padding: 16px;
overflow-y: auto;
z-index: 10;
}
.form-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
padding-bottom: 0.75rem;
border-bottom: 1px solid var(--border-light);
}
.form-header h3 {
margin: 0;
font-size: 0.9rem;
font-weight: 600;
}
.btn-close {
width: 28px;
height: 28px;
border: none;
background: transparent;
cursor: pointer;
font-size: 1.25rem;
color: var(--text-tertiary);
border-radius: 6px;
transition: all 0.15s;
}
.btn-close:hover {
background: var(--bg-hover);
color: var(--text-primary);
}
.form-group {
margin-bottom: 1rem;
}
.form-group label {
display: block;
margin-bottom: 0.4rem;
font-weight: 500;
color: var(--text-primary);
font-size: 0.8rem;
}
.form-group input,
.form-group textarea,
.form-group select {
width: 100%;
padding: 0.5rem;
border: 1px solid var(--border-input);
border-radius: 8px;
background: var(--bg-input);
box-sizing: border-box;
color: var(--text-primary);
font-size: 0.85rem;
}
.form-group textarea {
resize: vertical;
min-height: 60px;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.75rem;
}
.form-actions {
display: flex;
gap: 0.75rem;
margin-top: 1.25rem;
}
.btn-secondary,
.btn-primary {
flex: 1;
padding: 0.5rem 1rem;
border-radius: 6px;
font-size: 0.85rem;
cursor: pointer;
transition: all 0.2s;
}
.btn-secondary {
background: var(--bg-secondary);
color: var(--text-primary);
border: 1px solid var(--border-light);
}
.btn-secondary:hover {
background: var(--bg-hover);
}
.btn-primary {
background: var(--accent-primary);
color: white;
border: none;
}
.btn-primary:hover {
background: var(--accent-primary-hover);
}
</style>

View File

@ -0,0 +1,165 @@
<template>
<div class="list-item" :class="{ active, 'in-room': inRoom }" @click="$emit('click')">
<!-- 左侧图标/头像 -->
<div v-if="avatar" class="item-avatar" :style="avatarStyle">
{{ avatar }}
</div>
<div v-else-if="icon" class="item-icon">
{{ icon }}
</div>
<!-- 信息 -->
<div class="item-info">
<span class="item-name">{{ title }}</span>
<span v-if="subtitle" class="item-subtitle">{{ subtitle }}</span>
</div>
<!-- 操作按钮 -->
<div class="item-actions">
<slot name="actions">
<button v-if="onEdit" @click.stop="$emit('edit')" class="btn-icon" title="编辑">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
</svg>
</button>
<button v-if="onDelete" @click.stop="$emit('delete')" class="btn-icon btn-delete-icon" title="删除">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="3 6 5 6 21 6"></polyline>
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
</svg>
</button>
</slot>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
title: { type: String, required: true },
subtitle: { type: String, default: '' },
icon: { type: String, default: '' },
avatar: { type: String, default: '' },
avatarColor: { type: String, default: '#667eea' },
active: { type: Boolean, default: false },
inRoom: { type: Boolean, default: false },
onEdit: { type: Boolean, default: true },
onDelete: { type: Boolean, default: true },
})
defineEmits(['click', 'edit', 'delete'])
const avatarStyle = computed(() => ({
background: `linear-gradient(135deg, ${props.avatarColor}, #764ba2)`
}))
</script>
<style scoped>
.list-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.85rem 1rem;
border-bottom: 1px solid var(--border-light);
cursor: pointer;
transition: all 0.15s ease;
}
.list-item:hover {
background: var(--bg-hover);
}
.list-item.active {
background: var(--accent-primary-light);
border-left: 3px solid var(--accent-primary);
}
.list-item.in-room {
background: var(--accent-primary-light);
}
.item-icon {
font-size: 1.25rem;
flex-shrink: 0;
}
.item-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.8rem;
flex-shrink: 0;
}
.item-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
}
.item-name {
font-size: 0.8rem;
font-weight: 500;
color: var(--text-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.item-subtitle {
font-size: 0.65rem;
color: var(--text-tertiary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.item-actions {
display: flex;
gap: 0.25rem;
opacity: 0;
transition: opacity 0.15s;
}
.list-item:hover .item-actions {
opacity: 1;
}
.btn-icon {
background: transparent;
border: none;
cursor: pointer;
padding: 4px;
border-radius: 4px;
color: var(--text-secondary);
transition: all 0.15s;
display: flex;
align-items: center;
justify-content: center;
}
.btn-icon:hover {
background: var(--bg-hover);
color: var(--accent-primary);
}
.btn-icon svg {
width: 14px;
height: 14px;
}
.btn-delete-icon {
color: var(--danger-color);
}
.btn-delete-icon:hover {
background: var(--danger-bg);
}
</style>

View File

@ -0,0 +1,146 @@
<template>
<div class="message-bubble" :class="message.sender_type">
<div class="message-avatar">
{{ message.sender_name?.charAt(0).toUpperCase() }}
</div>
<div class="message-container">
<div class="message-body">
<ProcessBlock
v-if="message.process_steps && message.process_steps.length > 0"
:process-steps="message.process_steps"
/>
<div v-else class="md-content message-text" v-html="renderedContent"></div>
</div>
<div class="message-footer">
<span class="sender-name">{{ message.sender_name }}</span>
<span class="message-time">{{ formatTime(message.created_at) }}</span>
<button class="ghost-btn accent" @click="copyContent" title="复制">
<span v-html="copyIcon"></span>
</button>
</div>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { renderMarkdown } from '@/utils/markdown.js'
import ProcessBlock from './ProcessBlock.vue'
const props = defineProps({
message: { type: Object, required: true },
})
const renderedContent = computed(() => {
const text = props.message.content || props.message.text || ''
if (!text) return ''
return renderMarkdown(text)
})
function formatTime(time) {
if (!time) return ''
return new Date(time).toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
}
function copyContent() {
let text = props.message.content || props.message.text || ''
if (props.message.process_steps && props.message.process_steps.length > 0) {
const parts = props.message.process_steps
.filter(s => s && s.type === 'text')
.map(s => s.content)
if (parts.length > 0) text = parts.join('\n\n')
}
navigator.clipboard.writeText(text).catch(() => {})
}
const copyIcon = `<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>`
</script>
<style scoped>
.message-bubble {
display: flex;
gap: 0.75rem;
margin-bottom: 1.25rem;
}
.message-bubble.user {
flex-direction: row-reverse;
}
.message-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.9rem;
flex-shrink: 0;
}
.message-bubble.user .message-avatar {
background: linear-gradient(135deg, #11998e, #38ef7d);
}
.message-container {
max-width: 70%;
}
.message-body {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.message-text {
padding: 0.75rem 1rem;
background: var(--bg-primary);
border-radius: 12px;
border: 1px solid var(--border-light);
line-height: 1.6;
}
.message-bubble.user .message-text {
background: var(--accent-primary);
color: white;
border: none;
}
.message-footer {
display: flex;
align-items: center;
gap: 0.5rem;
margin-top: 0.25rem;
font-size: 0.75rem;
color: var(--text-tertiary);
}
.message-bubble.user .message-footer {
flex-direction: row-reverse;
}
.sender-name {
font-weight: 500;
color: var(--text-secondary);
}
.ghost-btn {
background: transparent;
border: none;
cursor: pointer;
padding: 4px;
border-radius: 4px;
color: var(--text-tertiary);
transition: all 0.15s;
display: flex;
align-items: center;
justify-content: center;
}
.ghost-btn:hover {
background: var(--bg-hover);
color: var(--accent-primary);
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -123,27 +123,48 @@ async def websocket_handler(websocket: WebSocket, room_id: str):
})
context = {"user_id": user_id, "username": user_name}
logger.info(f"[ROOM_WS] Starting process_message, agents count: {len(agents)}")
event_count = 0
async for event in chat_room_service.process_message(
room_id=room_id, user_message=content, user_id=user_id,
user_name=user_name, context=context
):
event_count += 1
logger.info(f"[ROOM_WS] Received event {event_count}: {event.get('event')}")
if event.get("event") in ["process_step", "done", "error"]:
# Find agent_name from agents list
agent_name = None
for agent in agents:
if agent.agent_id == event.get("agent_id"):
agent_name = agent.name
break
await connection_manager.broadcast_to_room(room_id, {
"event": event.get("event"),
"data": event.get("data", {}),
"agent_id": event.get("agent_id"),
"agent_name": event.get("agent_name")
"agent_name": agent_name
})
if event.get("event") == "done":
# Find agent_name from agents list
agent_name = None
for agent in agents:
if agent.agent_id == event.get("agent_id"):
agent_name = agent.name
break
chat_room_service.save_message(
room_id=room_id, sender_type="agent",
sender_id=event.get("agent_id"),
sender_name=event.get("agent_name"),
content=event.get("content", ""),
token_count=event.get("token_count", 0)
sender_name=agent_name,
content=event.get("data", {}).get("content", "") if isinstance(event.get("data"), dict) else "",
token_count=event.get("data", {}).get("token_count", 0) if isinstance(event.get("data"), dict) else 0
)
else:
logger.info(f"[ROOM_WS] Skipping event: {event.get('event')}")
logger.info(f"[ROOM_WS] process_message completed, total events: {event_count}")
for agent in agents:
await connection_manager.broadcast_to_room(room_id, {

View File

@ -369,6 +369,7 @@ class StreamService:
yield _sse_event("done", {
"message_id": msg_id,
"content": ctx.full_content,
"token_count": actual_token_count,
"usage": total_usage
})