fix(gui): 修复对话无skill时无法聊天、空对话列表、输入框清空、监控数据

- portal.py: 移除 all_skills 空检查,无 skill 时 fallback 到直接 LLM 对话
- portal.py: general 路径 agent 为 None 时也 fallback 到直接对话
- ChatView.vue: onMounted 自动创建默认对话
- ChatInput.vue: nextTick 清空输入框
- evolution_dashboard.py: usage API 从 LLMGateway.UsageTracker 读取真实数据
- DashboardOverview.vue: 活跃 agent 从 capabilities API 获取
This commit is contained in:
chiguyong 2026-06-13 11:32:45 +08:00
parent d02a6d5200
commit 905c1f6b18
5 changed files with 41 additions and 26 deletions

View File

@ -33,7 +33,7 @@
</template>
<script setup lang="ts">
import { ref, computed, nextTick, type Component } from 'vue'
import { ref, computed, type Component } from 'vue'
import { Input as AInput, Button as AButton } from 'ant-design-vue'
import { SendOutlined } from '@ant-design/icons-vue'
import ContextPill from './ContextPill.vue'
@ -71,10 +71,7 @@ function handleSend(): void {
const message = inputText.value.trim()
if (!message) return
emit('send', message)
// Use nextTick to ensure Ant Design Vue's v-model syncs properly
nextTick(() => {
inputText.value = ''
})
inputText.value = ''
}
function handlePressEnter(event: KeyboardEvent): void {

View File

@ -134,11 +134,14 @@ onMounted(async () => {
activeAgentCount.value = taskTypes.size
}
// Also fetch active agents from capabilities API
// Use capabilities API as primary source when available, fall back to task-type count
try {
const caps = await apiClient.getCapabilities()
if (caps?.capabilities?.length) {
activeAgentCount.value = caps.capabilities.length
// Use capabilities count only if no experience-based count yet
if (activeAgentCount.value === 0) {
activeAgentCount.value = caps.capabilities.length
}
}
} catch {
// capabilities may not be available

View File

@ -66,11 +66,13 @@ const ATypographyText = ATypography.Text
const chatStore = useChatStore()
const messagesContainer = ref<HTMLElement | null>(null)
onMounted(() => {
chatStore.loadConversations()
onMounted(async () => {
await chatStore.loadConversations()
chatStore.connectWebSocket()
// Auto-create a default conversation so user can start chatting immediately
if (!chatStore.currentConversationId) {
// Auto-select or create a conversation so user can start chatting immediately
if (chatStore.conversations.length > 0 && !chatStore.currentConversationId) {
chatStore.selectConversation(chatStore.conversations[0].id)
} else if (chatStore.conversations.length === 0) {
chatStore.createConversation()
}
})

View File

@ -456,15 +456,17 @@ async def get_usage(
"""获取 LLM 用量统计"""
now = datetime.now(timezone.utc)
if period == "today":
days = 1
start_time = now.replace(hour=0, minute=0, second=0, microsecond=0)
elif period == "30d":
days = 30
start_time = now - timedelta(days=30)
else:
days = 7
start_time = now - timedelta(days=7)
total_tokens = 0
total_requests = 0
total_errors = 0
total_latency = 0.0
usage_records: list[dict] = []
@ -482,17 +484,18 @@ async def get_usage(
for r in summary.records:
date_key = r.timestamp.strftime("%Y-%m-%d")
if date_key not in daily:
daily[date_key] = {"tokens": 0, "requests": 0, "latency": 0.0, "model": r.model}
daily[date_key] = {"tokens": 0, "requests": 0, "latency": 0.0, "models": set()}
daily[date_key]["tokens"] += r.total_tokens
daily[date_key]["requests"] += 1
daily[date_key]["latency"] += r.latency_ms
daily[date_key]["models"].add(r.model)
for date_key in sorted(daily):
d = daily[date_key]
usage_records.append({
"date": date_key,
"provider": "llm",
"model": d["model"],
"model": ", ".join(sorted(d["models"])),
"tokens": d["tokens"],
"requests": d["requests"],
"errors": 0,
@ -502,12 +505,6 @@ async def get_usage(
logger.error(f"Failed to get usage from LLMGateway: {e}")
# Fill in missing dates with zero
if period == "today":
days = 1
elif period == "30d":
days = 30
else:
days = 7
existing_dates = {r["date"] for r in usage_records}
for i in range(days - 1, -1, -1):
date = (now - timedelta(days=i)).strftime("%Y-%m-%d")

View File

@ -529,11 +529,6 @@ async def portal_websocket(websocket: WebSocket):
cost_aware_router = websocket.app.state.cost_aware_router
all_skills = skill_registry.list_skills()
if not all_skills:
await websocket.send_json(
{"type": "error", "data": {"message": "No skills available"}}
)
continue
# Get default tools for CostAwareRouter routing (only if default skill exists)
default_tools = []
@ -599,6 +594,27 @@ async def portal_websocket(websocket: WebSocket):
agent_name = routing_result.agent_name or "default"
agent = pool.get_agent(agent_name)
if agent is None:
if not all_skills:
# No skills registered — fallback to direct chat
chat_messages = [{"role": "user", "content": message_text}]
try:
history = _conversation_store.get_history(conv.id, limit=20)
for hist_msg in history[:-1]:
if hist_msg.role in ("user", "assistant"):
chat_messages.insert(0, {"role": hist_msg.role, "content": hist_msg.content})
except Exception:
pass
response = await llm_gateway.chat(
messages=chat_messages,
model="default",
agent_name="default",
task_type="chat",
)
await websocket.send_json({
"type": "result",
"data": {"status": "completed", "content": response.content},
})
continue
agent = await pool.create_agent_from_skill(agent_name)
# Execute via ReAct stream
@ -664,7 +680,7 @@ async def portal_websocket(websocket: WebSocket):
)
except WebSocketDisconnect:
logger.debug(f"Portal WebSocket disconnected for conversation {conv.id}")
logger.debug(f"Portal WebSocket disconnected for conversation {conv.id if conv else 'N/A'}")
except Exception as e:
logger.error(f"Portal WebSocket error: {e}")
try: