fix: 修复参数传递bug
This commit is contained in:
parent
feabfc8537
commit
a31d751137
|
|
@ -40,10 +40,6 @@ const navItems = [
|
||||||
path: '/tools',
|
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>`
|
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',
|
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>`
|
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>`
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,108 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="room-view">
|
<div class="room-view">
|
||||||
<!-- 左侧:房间列表 -->
|
<!-- 左侧:侧栏 -->
|
||||||
<aside class="sidebar">
|
<aside class="sidebar">
|
||||||
<div class="sidebar-header">
|
<div class="sidebar-tabs">
|
||||||
<h2>聊天室</h2>
|
<button :class="{ active: sidebarTab === 'rooms' }" @click="sidebarTab = 'rooms'">
|
||||||
<button class="btn-icon" @click="showCreateRoom = true" title="创建房间">+</button>
|
聊天室
|
||||||
|
</button>
|
||||||
|
<button :class="{ active: sidebarTab === 'agents' }" @click="sidebarTab = 'agents'">
|
||||||
|
Agent
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="room-list">
|
<!-- 聊天室列表 -->
|
||||||
<div v-for="room in rooms" :key="room.id"
|
<template v-if="sidebarTab === 'rooms'">
|
||||||
class="room-item" :class="{ active: currentRoom?.id === room.id }"
|
<div class="sidebar-header">
|
||||||
@click="joinRoom(room)">
|
<button class="btn-icon" @click="showCreateRoom = true" title="创建房间">+</button>
|
||||||
<div class="room-icon">💬</div>
|
|
||||||
<div class="room-info">
|
|
||||||
<span class="room-name">{{ room.name }}</span>
|
|
||||||
<span class="room-desc">{{ room.description || '无描述' }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="room-list">
|
||||||
|
<div v-for="room in rooms" :key="room.id"
|
||||||
|
class="room-item" :class="{ active: currentRoom?.id === room.id }"
|
||||||
|
@click="joinRoom(room)">
|
||||||
|
<div class="room-icon">💬</div>
|
||||||
|
<div class="room-info">
|
||||||
|
<span class="room-name">{{ room.name }}</span>
|
||||||
|
<span class="room-desc">{{ room.description || '无描述' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="!rooms.length" class="empty-sidebar">暂无房间</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<div class="sidebar-footer">
|
<!-- Agent 列表 -->
|
||||||
<router-link to="/agents" class="nav-link">
|
<template v-if="sidebarTab === 'agents'">
|
||||||
<span>⚙️</span> 管理 Agent
|
<div class="sidebar-header">
|
||||||
</router-link>
|
<span class="sidebar-title">Agent 管理</span>
|
||||||
|
<button class="btn-icon" @click="openCreateAgent" title="创建 Agent">+</button>
|
||||||
|
</div>
|
||||||
|
<div class="agent-sidebar-list">
|
||||||
|
<div v-for="agent in allAgents" :key="agent.id" class="agent-sidebar-item"
|
||||||
|
:class="{ 'in-room': isAgentInRoom(agent.id) }">
|
||||||
|
<div class="agent-sidebar-avatar">{{ agent.name?.charAt(0).toUpperCase() }}</div>
|
||||||
|
<div class="agent-sidebar-info">
|
||||||
|
<span class="agent-sidebar-name">{{ agent.name }}</span>
|
||||||
|
<span class="agent-sidebar-meta">
|
||||||
|
{{ agent.auto_response ? '自动' : '@触发' }} · {{ agent.role }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="agent-sidebar-actions">
|
||||||
|
<button class="btn-icon-sm" @click="editAgent(agent)" title="编辑">✎</button>
|
||||||
|
<button class="btn-icon-sm btn-danger" @click="deleteAgent(agent.id)" title="删除">×</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="!allAgents.length" class="empty-sidebar">暂无 Agent</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Agent 创建/编辑表单 -->
|
||||||
|
<div v-if="showAgentForm" class="agent-sidebar-form">
|
||||||
|
<div class="form-header">
|
||||||
|
<h3>{{ editingAgent ? '编辑 Agent' : '创建 Agent' }}</h3>
|
||||||
|
<button class="btn-close" @click="closeAgentForm">×</button>
|
||||||
|
</div>
|
||||||
|
<form @submit.prevent="saveAgent">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>名称 *</label>
|
||||||
|
<input v-model="agentForm.name" type="text" required />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>角色 *</label>
|
||||||
|
<input v-model="agentForm.role" type="text" required />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>系统提示词 *</label>
|
||||||
|
<textarea v-model="agentForm.system_prompt" rows="3" required></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Provider</label>
|
||||||
|
<select v-model="agentForm.provider_id">
|
||||||
|
<option :value="null">默认</option>
|
||||||
|
<option v-for="p in providers" :key="p.id" :value="p.id">{{ p.name }}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>模型</label>
|
||||||
|
<input v-model="agentForm.model" type="text" />
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>优先级</label>
|
||||||
|
<input v-model.number="agentForm.priority" type="number" min="1" max="10" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>响应方式</label>
|
||||||
|
<select v-model="agentForm.auto_response">
|
||||||
|
<option :value="true">自动</option>
|
||||||
|
<option :value="false">@触发</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-actions">
|
||||||
|
<button type="button" class="btn-secondary" @click="closeAgentForm">取消</button>
|
||||||
|
<button type="submit" class="btn-primary">保存</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
|
|
@ -36,7 +116,8 @@
|
||||||
<span class="agents-count">{{ roomAgents.length }} 个 Agent</span>
|
<span class="agents-count">{{ roomAgents.length }} 个 Agent</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-actions">
|
<div class="header-actions">
|
||||||
<button class="btn-small" @click="showManageAgents = true">管理成员</button>
|
<button v-if="currentRoom" class="btn-small" @click="openAddMembers">添加成员</button>
|
||||||
|
<button class="btn-small" @click="openRoomManage">管理房间</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
@ -128,35 +209,93 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 管理 Agent Modal -->
|
<!-- 管理房间 Modal -->
|
||||||
<div v-if="showManageAgents" class="modal-overlay" @click.self="showManageAgents = false">
|
<div v-if="showRoomManage" class="modal-overlay" @click.self="showRoomManage = false">
|
||||||
<div class="modal">
|
<div class="modal modal-wide">
|
||||||
<h2>管理聊天室成员</h2>
|
<div class="modal-tabs">
|
||||||
<div class="agents-management">
|
<button :class="{ active: roomTab === 'list' }" @click="roomTab = 'list'">
|
||||||
<div class="section">
|
房间列表
|
||||||
<h3>当前成员</h3>
|
</button>
|
||||||
<div class="agent-tags">
|
<button :class="{ active: roomTab === 'edit' }" @click="openRoomForm">
|
||||||
<span v-for="agent in roomAgents" :key="agent.id" class="agent-tag">
|
{{ editingRoom ? '编辑房间' : '创建房间' }}
|
||||||
{{ agent.name }}
|
</button>
|
||||||
<button @click="removeAgentFromRoom(agent.id)">×</button>
|
</div>
|
||||||
</span>
|
|
||||||
<div v-if="!roomAgents.length" class="empty-hint">暂无成员</div>
|
<!-- 房间列表 -->
|
||||||
</div>
|
<div v-if="roomTab === 'list'" class="rooms-management">
|
||||||
</div>
|
<div class="rooms-grid">
|
||||||
<div class="section">
|
<div v-for="room in rooms" :key="room.id"
|
||||||
<h3>添加成员</h3>
|
class="room-card" :class="{ active: currentRoom?.id === room.id }">
|
||||||
<div class="agent-list-select">
|
<div class="room-card-header">
|
||||||
<div v-for="agent in availableAgents" :key="agent.id"
|
<div class="room-icon">💬</div>
|
||||||
class="agent-option" @click="addAgentToRoom(agent.id)">
|
<div class="room-card-info">
|
||||||
<span class="agent-avatar-sm">{{ agent.name?.charAt(0) }}</span>
|
<span class="room-card-name">{{ room.name }}</span>
|
||||||
<span>{{ agent.name }}</span>
|
<span class="room-card-desc">{{ room.description || '无描述' }}</span>
|
||||||
<span class="role">{{ agent.role }}</span>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="room-card-actions">
|
||||||
|
<button class="btn-tiny" @click="joinRoom(room); showRoomManage = false">进入</button>
|
||||||
|
<button class="btn-tiny" @click="editRoom(room)">编辑</button>
|
||||||
|
<button class="btn-tiny btn-danger" @click="deleteRoom(room.id)">删除</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="!rooms.length" class="empty-hint">暂无房间</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 房间表单 -->
|
||||||
|
<div v-if="roomTab === 'edit'" class="room-form-section">
|
||||||
|
<h3>{{ editingRoom ? '编辑房间' : '创建房间' }}</h3>
|
||||||
|
<form @submit.prevent="saveRoom">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>房间名称 *</label>
|
||||||
|
<input v-model="roomForm.name" type="text" required />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>描述</label>
|
||||||
|
<input v-model="roomForm.description" type="text" />
|
||||||
|
</div>
|
||||||
|
<div class="form-actions">
|
||||||
|
<button type="button" class="btn-secondary" @click="roomTab = 'list'">取消</button>
|
||||||
|
<button type="submit" class="btn-primary">保存</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-actions">
|
||||||
|
<button class="btn-secondary" @click="showRoomManage = false">关闭</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 添加成员 Modal -->
|
||||||
|
<div v-if="showAddMembers" class="modal-overlay" @click.self="showAddMembers = false">
|
||||||
|
<div class="modal">
|
||||||
|
<h2>房间成员管理</h2>
|
||||||
|
<div class="section">
|
||||||
|
<h3>当前成员</h3>
|
||||||
|
<div class="agent-tags">
|
||||||
|
<span v-for="agent in roomAgents" :key="agent.id" class="agent-tag">
|
||||||
|
{{ agent.name }}
|
||||||
|
<button @click="removeAgentFromRoom(agent.id)">×</button>
|
||||||
|
</span>
|
||||||
|
<div v-if="!roomAgents.length" class="empty-hint">暂无成员</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="section">
|
||||||
|
<h3>添加成员</h3>
|
||||||
|
<div class="agent-list-select">
|
||||||
|
<div v-for="agent in availableAgents" :key="agent.id"
|
||||||
|
class="agent-option" @click="addAgentToRoom(agent.id)">
|
||||||
|
<span class="agent-avatar-sm">{{ agent.name?.charAt(0) }}</span>
|
||||||
|
<span>{{ agent.name }}</span>
|
||||||
|
<span class="role">{{ agent.role }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="!availableAgents.length" class="empty-hint">没有可添加的 Agent</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<button class="btn-secondary" @click="showManageAgents = false">关闭</button>
|
<button class="btn-secondary" @click="showAddMembers = false">关闭</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -165,7 +304,7 @@
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, computed, onMounted, onUnmounted, nextTick } from 'vue'
|
import { ref, reactive, computed, onMounted, onUnmounted, nextTick } from 'vue'
|
||||||
import { roomsAPI, createRoomWS, agentsAPI } from '@/api'
|
import { roomsAPI, createRoomWS, agentsAPI, providersAPI } from '@/api'
|
||||||
import { marked } from 'marked'
|
import { marked } from 'marked'
|
||||||
|
|
||||||
const rooms = ref([])
|
const rooms = ref([])
|
||||||
|
|
@ -173,16 +312,35 @@ const currentRoom = ref(null)
|
||||||
const messages = ref([])
|
const messages = ref([])
|
||||||
const roomAgents = ref([])
|
const roomAgents = ref([])
|
||||||
const allAgents = ref([])
|
const allAgents = ref([])
|
||||||
|
const providers = ref([])
|
||||||
const typingAgents = ref(new Set())
|
const typingAgents = ref(new Set())
|
||||||
const streamingMessages = ref({})
|
const streamingMessages = ref({})
|
||||||
|
const sidebarTab = ref('rooms') // 'rooms' | 'agents'
|
||||||
|
|
||||||
const showCreateRoom = ref(false)
|
const showCreateRoom = ref(false)
|
||||||
const showManageAgents = ref(false)
|
const showAddMembers = ref(false)
|
||||||
|
const showRoomManage = ref(false)
|
||||||
|
const roomTab = ref('list') // 'list' | 'edit'
|
||||||
|
const editingRoom = ref(null)
|
||||||
|
const agentTab = ref('members') // 'members' | 'agents'
|
||||||
|
const showAgentForm = ref(false)
|
||||||
|
const editingAgent = ref(null)
|
||||||
const inputMessage = ref('')
|
const inputMessage = ref('')
|
||||||
const messagesContainer = ref(null)
|
const messagesContainer = ref(null)
|
||||||
let ws = null
|
let ws = null
|
||||||
|
|
||||||
const newRoom = reactive({ name: '', description: '' })
|
const newRoom = reactive({ name: '', description: '' })
|
||||||
|
const roomForm = reactive({ name: '', description: '' })
|
||||||
|
|
||||||
|
const agentForm = reactive({
|
||||||
|
name: '',
|
||||||
|
role: '',
|
||||||
|
system_prompt: '',
|
||||||
|
provider_id: null,
|
||||||
|
model: '',
|
||||||
|
priority: 5,
|
||||||
|
auto_response: true
|
||||||
|
})
|
||||||
|
|
||||||
const availableAgents = computed(() => {
|
const availableAgents = computed(() => {
|
||||||
const roomAgentIds = new Set(roomAgents.value.map(a => a.id))
|
const roomAgentIds = new Set(roomAgents.value.map(a => a.id))
|
||||||
|
|
@ -224,6 +382,14 @@ async function loadAllAgents() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isAgentInRoom(agentId) {
|
||||||
|
return roomAgents.value.some(a => a.id === agentId)
|
||||||
|
}
|
||||||
|
|
||||||
|
function openAddMembers() {
|
||||||
|
showAddMembers.value = true
|
||||||
|
}
|
||||||
|
|
||||||
async function createRoom() {
|
async function createRoom() {
|
||||||
try {
|
try {
|
||||||
const res = await roomsAPI.create(newRoom)
|
const res = await roomsAPI.create(newRoom)
|
||||||
|
|
@ -237,6 +403,60 @@ async function createRoom() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openRoomManage() {
|
||||||
|
roomTab.value = 'list'
|
||||||
|
showRoomManage.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function openRoomForm() {
|
||||||
|
editingRoom.value = null
|
||||||
|
Object.assign(roomForm, { name: '', description: '' })
|
||||||
|
roomTab.value = 'edit'
|
||||||
|
}
|
||||||
|
|
||||||
|
function editRoom(room) {
|
||||||
|
editingRoom.value = room
|
||||||
|
Object.assign(roomForm, { name: room.name, description: room.description || '' })
|
||||||
|
roomTab.value = 'edit'
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveRoom() {
|
||||||
|
try {
|
||||||
|
if (editingRoom.value) {
|
||||||
|
await roomsAPI.update(editingRoom.value.id, roomForm)
|
||||||
|
const idx = rooms.value.findIndex(r => r.id === editingRoom.value.id)
|
||||||
|
if (idx !== -1) {
|
||||||
|
rooms.value[idx] = { ...rooms.value[idx], ...roomForm }
|
||||||
|
}
|
||||||
|
if (currentRoom.value?.id === editingRoom.value.id) {
|
||||||
|
currentRoom.value = { ...currentRoom.value, ...roomForm }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const res = await roomsAPI.create(roomForm)
|
||||||
|
rooms.value.push(res.room)
|
||||||
|
}
|
||||||
|
roomTab.value = 'list'
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to save room:', e)
|
||||||
|
alert('保存失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteRoom(roomId) {
|
||||||
|
if (!confirm('确定删除此聊天室?')) return
|
||||||
|
try {
|
||||||
|
await roomsAPI.delete(roomId)
|
||||||
|
rooms.value = rooms.value.filter(r => r.id !== roomId)
|
||||||
|
if (currentRoom.value?.id === roomId) {
|
||||||
|
currentRoom.value = null
|
||||||
|
messages.value = []
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to delete room:', e)
|
||||||
|
alert('删除失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function joinRoom(room) {
|
async function joinRoom(room) {
|
||||||
if (ws) {
|
if (ws) {
|
||||||
ws.close()
|
ws.close()
|
||||||
|
|
@ -280,14 +500,27 @@ async function joinRoom(room) {
|
||||||
},
|
},
|
||||||
onStream: (event, data, agentId, agentName) => {
|
onStream: (event, data, agentId, agentName) => {
|
||||||
if (event === 'process_step') {
|
if (event === 'process_step') {
|
||||||
|
// data.step contains {id, index, type, content}
|
||||||
|
const step = data.step || data
|
||||||
if (!streamingMessages.value[agentId]) {
|
if (!streamingMessages.value[agentId]) {
|
||||||
streamingMessages.value[agentId] = { agentName, content: '' }
|
streamingMessages.value[agentId] = { agentName, content: '' }
|
||||||
}
|
}
|
||||||
if (data.type === 'text') {
|
if (step.type === 'text') {
|
||||||
streamingMessages.value[agentId].content = data.content
|
streamingMessages.value[agentId].content = step.content
|
||||||
}
|
}
|
||||||
} else if (event === 'done') {
|
} else if (event === 'done') {
|
||||||
delete streamingMessages.value[agentId]
|
// Save streaming message to messages list before removing
|
||||||
|
if (streamingMessages.value[agentId]) {
|
||||||
|
messages.value.push({
|
||||||
|
id: `msg-${Date.now()}`,
|
||||||
|
sender_type: 'agent',
|
||||||
|
sender_id: agentId,
|
||||||
|
sender_name: agentName,
|
||||||
|
content: streamingMessages.value[agentId].content,
|
||||||
|
created_at: new Date().toISOString()
|
||||||
|
})
|
||||||
|
delete streamingMessages.value[agentId]
|
||||||
|
}
|
||||||
typingAgents.value.delete(agentId)
|
typingAgents.value.delete(agentId)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -323,6 +556,69 @@ async function removeAgentFromRoom(agentId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadProviders() {
|
||||||
|
try {
|
||||||
|
const res = await providersAPI.list()
|
||||||
|
if (res.success) {
|
||||||
|
providers.value = res.data?.providers || []
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to load providers:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openCreateAgent() {
|
||||||
|
editingAgent.value = null
|
||||||
|
// 默认选中用户设置的默认 Provider
|
||||||
|
const defaultProvider = providers.value.find(p => p.is_default)
|
||||||
|
Object.assign(agentForm, { name: '', role: '', system_prompt: '', provider_id: defaultProvider?.id || null, model: '', priority: 5, auto_response: true })
|
||||||
|
showAgentForm.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function editAgent(agent) {
|
||||||
|
editingAgent.value = agent
|
||||||
|
Object.assign(agentForm, {
|
||||||
|
name: agent.name,
|
||||||
|
role: agent.role,
|
||||||
|
system_prompt: agent.system_prompt,
|
||||||
|
provider_id: agent.provider_id,
|
||||||
|
model: agent.model || '',
|
||||||
|
priority: agent.priority || 5,
|
||||||
|
auto_response: agent.auto_response
|
||||||
|
})
|
||||||
|
showAgentForm.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveAgent() {
|
||||||
|
try {
|
||||||
|
if (editingAgent.value) {
|
||||||
|
await agentsAPI.update(editingAgent.value.id, agentForm)
|
||||||
|
} else {
|
||||||
|
await agentsAPI.create(agentForm)
|
||||||
|
}
|
||||||
|
await loadAllAgents()
|
||||||
|
closeAgentForm()
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to save agent:', e)
|
||||||
|
alert('保存失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteAgent(agentId) {
|
||||||
|
if (!confirm('确定删除此 Agent?')) return
|
||||||
|
try {
|
||||||
|
await agentsAPI.delete(agentId)
|
||||||
|
await loadAllAgents()
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to delete agent:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeAgentForm() {
|
||||||
|
showAgentForm.value = false
|
||||||
|
editingAgent.value = null
|
||||||
|
}
|
||||||
|
|
||||||
function scrollToBottom() {
|
function scrollToBottom() {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (messagesContainer.value) {
|
if (messagesContainer.value) {
|
||||||
|
|
@ -334,6 +630,7 @@ function scrollToBottom() {
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadRooms()
|
loadRooms()
|
||||||
loadAllAgents()
|
loadAllAgents()
|
||||||
|
loadProviders()
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
|
@ -350,6 +647,7 @@ onUnmounted(() => {
|
||||||
|
|
||||||
/* Sidebar */
|
/* Sidebar */
|
||||||
.sidebar {
|
.sidebar {
|
||||||
|
position: relative;
|
||||||
width: 280px;
|
width: 280px;
|
||||||
background: var(--card-bg, #fff);
|
background: var(--card-bg, #fff);
|
||||||
border-right: 1px solid var(--border-color);
|
border-right: 1px solid var(--border-color);
|
||||||
|
|
@ -358,27 +656,52 @@ onUnmounted(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-header {
|
.sidebar-header {
|
||||||
padding: 20px;
|
padding: 12px 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-bottom: 1px solid var(--border-color);
|
border-bottom: 1px solid var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-header h2 {
|
.sidebar-title {
|
||||||
margin: 0;
|
font-size: 14px;
|
||||||
font-size: 18px;
|
font-weight: 500;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-tabs {
|
||||||
|
display: flex;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-tabs button {
|
||||||
|
flex: 1;
|
||||||
|
padding: 12px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #666;
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-tabs button.active {
|
||||||
|
color: #667eea;
|
||||||
|
border-bottom-color: #667eea;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-icon {
|
.btn-icon {
|
||||||
width: 32px;
|
width: 28px;
|
||||||
height: 32px;
|
height: 28px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: none;
|
border: none;
|
||||||
background: #667eea;
|
background: #667eea;
|
||||||
color: white;
|
color: white;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 18px;
|
font-size: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.room-list {
|
.room-list {
|
||||||
|
|
@ -424,6 +747,218 @@ onUnmounted(() => {
|
||||||
color: #888;
|
color: #888;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Agent sidebar */
|
||||||
|
.agent-sidebar-list {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-sidebar-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-sidebar-item:hover {
|
||||||
|
background: var(--hover-bg, #f0f0f0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-sidebar-item.in-room {
|
||||||
|
background: #667eea10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-sidebar-avatar {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: linear-gradient(135deg, #667eea, #764ba2);
|
||||||
|
color: white;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 14px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-sidebar-info {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-sidebar-name {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 13px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-sidebar-meta {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-sidebar-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-sidebar-item:hover .agent-sidebar-actions {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-icon-sm {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-icon-sm:hover {
|
||||||
|
background: var(--hover-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-icon-sm.btn-danger {
|
||||||
|
color: #dc2626;
|
||||||
|
border-color: #dc2626;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-icon-sm.btn-danger:hover {
|
||||||
|
background: #fef2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Agent sidebar form */
|
||||||
|
.agent-sidebar-form {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: var(--card-bg, #fff);
|
||||||
|
padding: 16px;
|
||||||
|
overflow-y: auto;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-sidebar-form .form-group {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-sidebar-form .form-group label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-sidebar-form .form-group input,
|
||||||
|
.agent-sidebar-form .form-group textarea,
|
||||||
|
.agent-sidebar-form .form-group select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-sidebar-form .form-group textarea {
|
||||||
|
resize: vertical;
|
||||||
|
min-height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-sidebar-form .form-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-sidebar-form .form-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-sidebar-form .form-actions .btn-secondary,
|
||||||
|
.agent-sidebar-form .form-actions .btn-primary {
|
||||||
|
flex: 1 !important;
|
||||||
|
padding: 10px 16px !important;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px !important;
|
||||||
|
border: none !important;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: 0 !important;
|
||||||
|
min-width: 80px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-sidebar-form .btn-secondary {
|
||||||
|
background: #e5e7eb;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-sidebar-form .btn-primary {
|
||||||
|
background: #667eea;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-sidebar-form .btn-secondary:hover {
|
||||||
|
background: #d1d5db;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-sidebar-form .btn-primary:hover {
|
||||||
|
background: #5a67d8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-close {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-close:hover {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-sidebar {
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
color: #888;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-footer {
|
.sidebar-footer {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
border-top: 1px solid var(--border-color);
|
border-top: 1px solid var(--border-color);
|
||||||
|
|
@ -782,4 +1317,224 @@ onUnmounted(() => {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Tabs */
|
||||||
|
.modal-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
padding-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-tabs button {
|
||||||
|
padding: 8px 16px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 6px;
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-tabs button.active {
|
||||||
|
background: #667eea;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wide modal */
|
||||||
|
.modal-wide {
|
||||||
|
max-width: 800px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Section header */
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Agent cards in grid */
|
||||||
|
.agents-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
max-height: 250px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-card-small {
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-card-small .agent-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-card-small .agent-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-card-small .name {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-card-small .role-badge {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-meta-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-actions-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-tiny {
|
||||||
|
padding: 4px 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-tiny.btn-danger {
|
||||||
|
color: #dc2626;
|
||||||
|
border-color: #dc2626;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-small.btn-primary {
|
||||||
|
background: #667eea;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Agent form section */
|
||||||
|
.agent-form-section {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding-top: 20px;
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-form-section h3 {
|
||||||
|
margin: 0 0 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: inherit;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header actions */
|
||||||
|
.header-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Rooms management */
|
||||||
|
.rooms-management {
|
||||||
|
max-height: 400px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rooms-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-card {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-card.active {
|
||||||
|
border-color: #667eea;
|
||||||
|
background: #667eea10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-card-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-icon {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-card-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-card-name {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-card-desc {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-card-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-form-section {
|
||||||
|
margin-top: 16px;
|
||||||
|
padding-top: 16px;
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-form-section h3 {
|
||||||
|
margin: 0 0 16px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -123,27 +123,48 @@ async def websocket_handler(websocket: WebSocket, room_id: str):
|
||||||
})
|
})
|
||||||
|
|
||||||
context = {"user_id": user_id, "username": user_name}
|
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(
|
async for event in chat_room_service.process_message(
|
||||||
room_id=room_id, user_message=content, user_id=user_id,
|
room_id=room_id, user_message=content, user_id=user_id,
|
||||||
user_name=user_name, context=context
|
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"]:
|
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, {
|
await connection_manager.broadcast_to_room(room_id, {
|
||||||
"event": event.get("event"),
|
"event": event.get("event"),
|
||||||
"data": event.get("data", {}),
|
"data": event.get("data", {}),
|
||||||
"agent_id": event.get("agent_id"),
|
"agent_id": event.get("agent_id"),
|
||||||
"agent_name": event.get("agent_name")
|
"agent_name": agent_name
|
||||||
})
|
})
|
||||||
|
|
||||||
if event.get("event") == "done":
|
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(
|
chat_room_service.save_message(
|
||||||
room_id=room_id, sender_type="agent",
|
room_id=room_id, sender_type="agent",
|
||||||
sender_id=event.get("agent_id"),
|
sender_id=event.get("agent_id"),
|
||||||
sender_name=event.get("agent_name"),
|
sender_name=agent_name,
|
||||||
content=event.get("content", ""),
|
content=event.get("data", {}).get("content", "") if isinstance(event.get("data"), dict) else "",
|
||||||
token_count=event.get("token_count", 0)
|
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:
|
for agent in agents:
|
||||||
await connection_manager.broadcast_to_room(room_id, {
|
await connection_manager.broadcast_to_room(room_id, {
|
||||||
|
|
|
||||||
|
|
@ -369,6 +369,7 @@ class StreamService:
|
||||||
|
|
||||||
yield _sse_event("done", {
|
yield _sse_event("done", {
|
||||||
"message_id": msg_id,
|
"message_id": msg_id,
|
||||||
|
"content": ctx.full_content,
|
||||||
"token_count": actual_token_count,
|
"token_count": actual_token_count,
|
||||||
"usage": total_usage
|
"usage": total_usage
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue