refactor(cleanup): 移除余额查询功能、清理前端调试日志、规整终端输出
This commit is contained in:
parent
bb420b0d04
commit
cf51ab0555
@ -147,11 +147,6 @@ docker build -f docker/terminal.Dockerfile -t my-agent-shell:latest .
|
|||||||
},
|
},
|
||||||
"models": { "default_response_max_tokens": 32768 },
|
"models": { "default_response_max_tokens": 32768 },
|
||||||
"search": { "tavily_api_key": "", "tavily_api_key_2": "" },
|
"search": { "tavily_api_key": "", "tavily_api_key_2": "" },
|
||||||
"balance": {
|
|
||||||
"aliyun_access_key_id": "",
|
|
||||||
"aliyun_access_key_secret": "",
|
|
||||||
"usd_cny_rate": 7.1
|
|
||||||
},
|
|
||||||
"env_vars": {
|
"env_vars": {
|
||||||
"API_BASE_KIMI": "https://api.moonshot.cn/v1",
|
"API_BASE_KIMI": "https://api.moonshot.cn/v1",
|
||||||
"API_KEY_KIMI": "sk-..."
|
"API_KEY_KIMI": "sk-..."
|
||||||
@ -174,7 +169,6 @@ docker build -f docker/terminal.Dockerfile -t my-agent-shell:latest .
|
|||||||
| `secrets.api_token_secret` | `API_TOKEN_SECRET` | API Token 加密密钥(随机 hex) |
|
| `secrets.api_token_secret` | `API_TOKEN_SECRET` | API Token 加密密钥(随机 hex) |
|
||||||
| `models.default_response_max_tokens` | `AGENT_DEFAULT_RESPONSE_MAX_TOKENS` | 单轮响应 token 全局上限 |
|
| `models.default_response_max_tokens` | `AGENT_DEFAULT_RESPONSE_MAX_TOKENS` | 单轮响应 token 全局上限 |
|
||||||
| `search.tavily_api_key(_2)` | `AGENT_TAVILY_API_KEY(_2)` | Tavily 搜索 Key(可选) |
|
| `search.tavily_api_key(_2)` | `AGENT_TAVILY_API_KEY(_2)` | Tavily 搜索 Key(可选) |
|
||||||
| `balance.aliyun_*` / `balance.usd_cny_rate` | `ALIYUN_ACCESS_KEY_ID` 等 | 可选:阿里云余额查询 |
|
|
||||||
| `env_vars.*` | 原样注入 | 任意环境变量(API Key、沙箱参数等) |
|
| `env_vars.*` | 原样注入 | 任意环境变量(API Key、沙箱参数等) |
|
||||||
|
|
||||||
> ⚠️ **以下字段已弃用/无效**,存在于旧配置中可删除:
|
> ⚠️ **以下字段已弃用/无效**,存在于旧配置中可删除:
|
||||||
|
|||||||
@ -74,9 +74,6 @@ def _load_dotenv():
|
|||||||
"secrets.api_token_secret": "API_TOKEN_SECRET",
|
"secrets.api_token_secret": "API_TOKEN_SECRET",
|
||||||
"search.tavily_api_key": "AGENT_TAVILY_API_KEY",
|
"search.tavily_api_key": "AGENT_TAVILY_API_KEY",
|
||||||
"search.tavily_api_key_2": "AGENT_TAVILY_API_KEY_2",
|
"search.tavily_api_key_2": "AGENT_TAVILY_API_KEY_2",
|
||||||
"balance.aliyun_access_key_id": "ALIYUN_ACCESS_KEY_ID",
|
|
||||||
"balance.aliyun_access_key_secret": "ALIYUN_ACCESS_KEY_SECRET",
|
|
||||||
"balance.usd_cny_rate": "USD_CNY_RATE",
|
|
||||||
"models.default_response_max_tokens": "AGENT_DEFAULT_RESPONSE_MAX_TOKENS",
|
"models.default_response_max_tokens": "AGENT_DEFAULT_RESPONSE_MAX_TOKENS",
|
||||||
}
|
}
|
||||||
for dotted_key, env_name in _LEGACY_MAP.items():
|
for dotted_key, env_name in _LEGACY_MAP.items():
|
||||||
|
|||||||
@ -823,7 +823,7 @@ class MainTerminalCommandMixin:
|
|||||||
if not profile.get("fast_only"):
|
if not profile.get("fast_only"):
|
||||||
self._mode_before_fast_only = None
|
self._mode_before_fast_only = None
|
||||||
try:
|
try:
|
||||||
logger.info(
|
logger.debug(
|
||||||
"[ModelSwitch] model=%s run_mode=%s thinking=%s fast_only=%s thinking_only=%s",
|
"[ModelSwitch] model=%s run_mode=%s thinking=%s fast_only=%s thinking_only=%s",
|
||||||
self.model_key,
|
self.model_key,
|
||||||
self.run_mode,
|
self.run_mode,
|
||||||
|
|||||||
@ -153,24 +153,13 @@ class ToolsDefinitionMainMixin:
|
|||||||
if tool.get("function", {}).get("name") not in self.disabled_tools
|
if tool.get("function", {}).get("name") not in self.disabled_tools
|
||||||
]
|
]
|
||||||
|
|
||||||
# 调试日志:记录工具列表
|
# 调试日志:记录工具列表(DEBUG 级,不上终端)
|
||||||
tool_names = [t.get("function", {}).get("name") for t in tools]
|
tool_names = [t.get("function", {}).get("name") for t in tools]
|
||||||
logger.info("[define_tools] 可用工具列表: %s", tool_names)
|
logger.debug("[define_tools] 可用工具列表: %s", tool_names)
|
||||||
print(f"[DEBUG] define_tools: 工具数量={len(tools)}")
|
|
||||||
|
|
||||||
# 检查personalization分类状态
|
|
||||||
if hasattr(self, 'tool_categories_map') and 'personalization' in self.tool_categories_map:
|
|
||||||
cat = self.tool_categories_map['personalization']
|
|
||||||
state = self.tool_category_states.get('personalization', cat.default_enabled)
|
|
||||||
print(f"[DEBUG] personalization分类: default_enabled={cat.default_enabled}, current_state={state}")
|
|
||||||
else:
|
|
||||||
print(f"[DEBUG] personalization分类不在tool_categories_map中")
|
|
||||||
|
|
||||||
if "manage_personalization" in tool_names:
|
if "manage_personalization" in tool_names:
|
||||||
logger.info("[define_tools] manage_personalization 工具已启用")
|
logger.debug("[define_tools] manage_personalization 工具已启用")
|
||||||
print("[DEBUG] manage_personalization 在可用工具列表中")
|
|
||||||
else:
|
else:
|
||||||
logger.warning("[define_tools] manage_personalization 工具未找到!disabled_tools=%s", self.disabled_tools)
|
logger.debug("[define_tools] manage_personalization 未启用: disabled_tools=%s", self.disabled_tools)
|
||||||
print(f"[DEBUG] manage_personalization 不在可用工具列表中!disabled_tools={self.disabled_tools}")
|
|
||||||
|
|
||||||
return self._apply_intent_to_tools(tools)
|
return self._apply_intent_to_tools(tools)
|
||||||
|
|||||||
@ -873,7 +873,7 @@ class MainTerminalToolsExecutionMixin:
|
|||||||
|
|
||||||
async def handle_tool_call(self, tool_name: str, arguments: Dict) -> str:
|
async def handle_tool_call(self, tool_name: str, arguments: Dict) -> str:
|
||||||
"""处理工具调用(添加参数预检查和改进错误处理)"""
|
"""处理工具调用(添加参数预检查和改进错误处理)"""
|
||||||
logger.info("[handle_tool_call] 工具调用开始: tool_name=%s, arguments=%s", tool_name, arguments)
|
logger.debug("[handle_tool_call] 工具调用开始: tool_name=%s, arguments=%s", tool_name, arguments)
|
||||||
try:
|
try:
|
||||||
if hasattr(self, "_apply_execution_mode_to_runtime"):
|
if hasattr(self, "_apply_execution_mode_to_runtime"):
|
||||||
self._apply_execution_mode_to_runtime()
|
self._apply_execution_mode_to_runtime()
|
||||||
@ -2176,13 +2176,12 @@ class MainTerminalToolsExecutionMixin:
|
|||||||
logger.exception("[handle_tool_call] 工具执行异常详情")
|
logger.exception("[handle_tool_call] 工具执行异常详情")
|
||||||
result = {"success": False, "error": f"工具执行异常: {str(e)}"}
|
result = {"success": False, "error": f"工具执行异常: {str(e)}"}
|
||||||
|
|
||||||
logger.info("[handle_tool_call] 工具调用结束: tool_name=%s, result=%s", tool_name, result)
|
logger.debug("[handle_tool_call] 工具调用结束: tool_name=%s, result=%s", tool_name, result)
|
||||||
return json.dumps(result, ensure_ascii=False)
|
return json.dumps(result, ensure_ascii=False)
|
||||||
|
|
||||||
async def _execute_manage_personalization(self, arguments: Dict) -> Dict:
|
async def _execute_manage_personalization(self, arguments: Dict) -> Dict:
|
||||||
"""执行个性化管理操作"""
|
"""执行个性化管理操作"""
|
||||||
print(f"[DEBUG] _execute_manage_personalization 被调用: {arguments}")
|
logger.debug("[_execute_manage_personalization] 方法被调用: arguments=%s", arguments)
|
||||||
logger.info("[_execute_manage_personalization] 方法被调用: arguments=%s", arguments)
|
|
||||||
action = arguments.get("action")
|
action = arguments.get("action")
|
||||||
logger.info("[_execute_manage_personalization] action=%s", action)
|
logger.info("[_execute_manage_personalization] action=%s", action)
|
||||||
|
|
||||||
@ -2289,8 +2288,7 @@ class MainTerminalToolsExecutionMixin:
|
|||||||
|
|
||||||
# 保存配置
|
# 保存配置
|
||||||
save_personalization_config(self.data_dir, config)
|
save_personalization_config(self.data_dir, config)
|
||||||
print(f"[DEBUG] 配置已保存: field={field}, value={value}")
|
logger.info("[_execute_manage_personalization] 配置已保存到文件: field=%s", field)
|
||||||
logger.info("[_execute_manage_personalization] 配置已保存到文件")
|
|
||||||
|
|
||||||
# 重新加载配置到当前终端
|
# 重新加载配置到当前终端
|
||||||
self.apply_personalization_preferences(config)
|
self.apply_personalization_preferences(config)
|
||||||
|
|||||||
@ -6,6 +6,7 @@ from datetime import datetime
|
|||||||
from typing import Any, Dict, List, Optional, Callable, TYPE_CHECKING
|
from typing import Any, Dict, List, Optional, Callable, TYPE_CHECKING
|
||||||
import os
|
import os
|
||||||
from core.main_terminal import MainTerminal
|
from core.main_terminal import MainTerminal
|
||||||
|
from server.utils_common import debug_log
|
||||||
from utils.logger import setup_logger
|
from utils.logger import setup_logger
|
||||||
from modules.personalization_manager import load_personalization_config
|
from modules.personalization_manager import load_personalization_config
|
||||||
from modules.versioning_manager import ConversationVersioningManager, VersioningError
|
from modules.versioning_manager import ConversationVersioningManager, VersioningError
|
||||||
@ -50,9 +51,9 @@ class WebTerminal(MainTerminal):
|
|||||||
bound_id = f"conv_{bound_id}"
|
bound_id = f"conv_{bound_id}"
|
||||||
result = self.load_conversation(bound_id, restore_model=True)
|
result = self.load_conversation(bound_id, restore_model=True)
|
||||||
if result.get("success"):
|
if result.get("success"):
|
||||||
print(f"[WebTerminal] 已加载绑定对话: {bound_id}")
|
debug_log(f"[WebTerminal] 已加载绑定对话: {bound_id}")
|
||||||
else:
|
else:
|
||||||
print(f"[WebTerminal] 警告:绑定对话 {bound_id} 加载失败: {result.get('message') or result.get('error')}")
|
logger.warning("[WebTerminal] 绑定对话 %s 加载失败: %s", bound_id, result.get('message') or result.get('error'))
|
||||||
return
|
return
|
||||||
|
|
||||||
latest_list = self.context_manager.get_conversation_list(limit=1, offset=0)
|
latest_list = self.context_manager.get_conversation_list(limit=1, offset=0)
|
||||||
@ -64,14 +65,14 @@ class WebTerminal(MainTerminal):
|
|||||||
if conv_id:
|
if conv_id:
|
||||||
result = self.load_conversation(conv_id, restore_model=False)
|
result = self.load_conversation(conv_id, restore_model=False)
|
||||||
if result.get("success"):
|
if result.get("success"):
|
||||||
print(f"[WebTerminal] 已加载最近对话: {conv_id}")
|
debug_log(f"[WebTerminal] 已加载最近对话: {conv_id}")
|
||||||
return
|
return
|
||||||
|
|
||||||
conversation_id = self.context_manager.start_new_conversation(
|
conversation_id = self.context_manager.start_new_conversation(
|
||||||
project_path=self.project_path,
|
project_path=self.project_path,
|
||||||
thinking_mode=self.thinking_mode
|
thinking_mode=self.thinking_mode
|
||||||
)
|
)
|
||||||
print(f"[WebTerminal] 自动创建新对话: {conversation_id}")
|
debug_log(f"[WebTerminal] 自动创建新对话: {conversation_id}")
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -126,16 +127,16 @@ class WebTerminal(MainTerminal):
|
|||||||
# 让 run_command 与实时终端共享同一容器环境
|
# 让 run_command 与实时终端共享同一容器环境
|
||||||
self.terminal_ops.attach_terminal_manager(self.terminal_manager)
|
self.terminal_ops.attach_terminal_manager(self.terminal_manager)
|
||||||
|
|
||||||
print(f"[WebTerminal] 初始化完成,项目路径: {project_path}")
|
debug_log(f"[WebTerminal] 初始化完成,项目路径: {project_path}")
|
||||||
print(f"[WebTerminal] 初始模式: {self.run_mode}")
|
debug_log(f"[WebTerminal] 初始模式: {self.run_mode}")
|
||||||
print(f"[WebTerminal] 对话管理已就绪")
|
debug_log(f"[WebTerminal] 对话管理已就绪")
|
||||||
|
|
||||||
# 设置token更新回调
|
# 设置token更新回调
|
||||||
if message_callback is not None:
|
if message_callback is not None:
|
||||||
self.context_manager._web_terminal_callback = message_callback
|
self.context_manager._web_terminal_callback = message_callback
|
||||||
print(f"[WebTerminal] 实时token统计已启用")
|
debug_log(f"[WebTerminal] 实时token统计已启用")
|
||||||
else:
|
else:
|
||||||
print(f"[WebTerminal] 警告:message_callback为None,无法启用实时token统计")
|
logger.warning("[WebTerminal] message_callback为None,无法启用实时token统计")
|
||||||
# ===========================================
|
# ===========================================
|
||||||
# 新增:对话管理相关方法(Web版本)
|
# 新增:对话管理相关方法(Web版本)
|
||||||
# ===========================================
|
# ===========================================
|
||||||
|
|||||||
15
main.py
15
main.py
@ -27,7 +27,7 @@ class AgentSystem:
|
|||||||
async def initialize(self):
|
async def initialize(self):
|
||||||
"""初始化系统"""
|
"""初始化系统"""
|
||||||
print("\n" + "="*50)
|
print("\n" + "="*50)
|
||||||
print("ð¤ Astrion 启动")
|
print("🤖 Astrion 启动")
|
||||||
print("="*50)
|
print("="*50)
|
||||||
|
|
||||||
# 1. 获取项目路径
|
# 1. 获取项目路径
|
||||||
@ -123,25 +123,23 @@ class AgentSystem:
|
|||||||
project_path = Path(path_input).resolve()
|
project_path = Path(path_input).resolve()
|
||||||
|
|
||||||
if self.is_unsafe_path(str(project_path)):
|
if self.is_unsafe_path(str(project_path)):
|
||||||
raise RuntimeError(f"默认项目路径不安全: {project_path}")
|
raise RuntimeError(f"默认工作区路径不安全: {project_path}")
|
||||||
|
|
||||||
project_path.mkdir(parents=True, exist_ok=True)
|
project_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
if not os.access(project_path, os.R_OK | os.W_OK):
|
if not os.access(project_path, os.R_OK | os.W_OK):
|
||||||
print(f"{OUTPUT_FORMATS['warning']} 对默认项目路径缺少读写权限: {project_path}")
|
print(f"{OUTPUT_FORMATS['warning']} 对默认工作区路径缺少读写权限: {project_path}")
|
||||||
|
|
||||||
self.project_path = str(project_path)
|
self.project_path = str(project_path)
|
||||||
print(f"{OUTPUT_FORMATS['success']} 已选择项目路径: {self.project_path}")
|
print(f"{OUTPUT_FORMATS['info']} 默认工作区路径: {self.project_path}")
|
||||||
|
|
||||||
async def setup_run_mode(self):
|
async def setup_run_mode(self):
|
||||||
"""选择运行模式"""
|
"""设置运行模式(当前统一入口默认 Web)"""
|
||||||
self.web_mode = True
|
self.web_mode = True
|
||||||
print(f"{OUTPUT_FORMATS['info']} 运行模式: Web(默认)")
|
|
||||||
|
|
||||||
async def setup_thinking_mode(self):
|
async def setup_thinking_mode(self):
|
||||||
"""选择思考模式"""
|
"""设置默认思考模式(对话中可随时切换)"""
|
||||||
self.thinking_mode = True
|
self.thinking_mode = True
|
||||||
print(f"{OUTPUT_FORMATS['info']} 思考模式: {'开启' if self.thinking_mode else '关闭'}(默认)")
|
|
||||||
|
|
||||||
async def init_system(self):
|
async def init_system(self):
|
||||||
"""初始化系统文件"""
|
"""初始化系统文件"""
|
||||||
@ -194,7 +192,6 @@ class AgentSystem:
|
|||||||
from web_server import run_server
|
from web_server import run_server
|
||||||
|
|
||||||
print(f"\n{OUTPUT_FORMATS['success']} 正在启动Web服务器...")
|
print(f"\n{OUTPUT_FORMATS['success']} 正在启动Web服务器...")
|
||||||
print(f"{OUTPUT_FORMATS['info']} 项目路径: {self.project_path}")
|
|
||||||
port = WEB_SERVER_PORT
|
port = WEB_SERVER_PORT
|
||||||
|
|
||||||
# 运行服务器(这会阻塞)
|
# 运行服务器(这会阻塞)
|
||||||
|
|||||||
@ -1,175 +0,0 @@
|
|||||||
"""
|
|
||||||
Admin balance fetchers for Kimi (Moonshot), DeepSeek, and Qwen (Aliyun BSS).
|
|
||||||
|
|
||||||
Credentials (优先读取仓库根目录 .env,其次环境变量):
|
|
||||||
- MOONSHOT_API_KEY : Bearer token for Kimi
|
|
||||||
- DEEPSEEK_API_KEY : Bearer token for DeepSeek
|
|
||||||
- ALIYUN_ACCESS_KEY_ID : AccessKey ID for Aliyun (Qwen billing)
|
|
||||||
- ALIYUN_ACCESS_KEY_SECRET : AccessKey Secret for Aliyun (Qwen billing)
|
|
||||||
- USD_CNY_RATE : Override USD->CNY rate (default 7.1)
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import base64
|
|
||||||
import hashlib
|
|
||||||
import hmac
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
import uuid
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import Any, Dict, Tuple
|
|
||||||
from urllib import parse, request
|
|
||||||
|
|
||||||
# -------- .env 读取助手(简化版,已由 config/__init__.py 统一注入) --------
|
|
||||||
|
|
||||||
|
|
||||||
def _env(key: str, default: str | None = None) -> str | None:
|
|
||||||
"""从环境变量获取配置。"""
|
|
||||||
if key in os.environ:
|
|
||||||
return os.environ[key]
|
|
||||||
return default
|
|
||||||
|
|
||||||
|
|
||||||
USD_CNY_RATE = float(_env("USD_CNY_RATE", "7.1") or "7.1")
|
|
||||||
|
|
||||||
|
|
||||||
def _http_get(url: str, headers: Dict[str, str] | None = None, timeout: int = 8) -> Tuple[Dict[str, Any] | None, str | None]:
|
|
||||||
"""Perform a simple GET request, return (json, error_message)."""
|
|
||||||
req = request.Request(url, headers=headers or {})
|
|
||||||
try:
|
|
||||||
with request.urlopen(req, timeout=timeout) as resp:
|
|
||||||
data = resp.read()
|
|
||||||
return json.loads(data.decode("utf-8")), None
|
|
||||||
except Exception as exc: # broad: network/parsing errors
|
|
||||||
return None, str(exc)
|
|
||||||
|
|
||||||
|
|
||||||
# -------- Kimi (Moonshot) --------
|
|
||||||
def fetch_kimi_balance() -> Dict[str, Any]:
|
|
||||||
api_key = _env("MOONSHOT_API_KEY") or _env("API_KEY_KIMI") or _env("AGENT_API_KEY")
|
|
||||||
if not api_key:
|
|
||||||
return {"success": False, "error": "Kimi 密钥未设置(MOONSHOT_API_KEY / API_KEY_KIMI / AGENT_API_KEY)"}
|
|
||||||
|
|
||||||
base = (
|
|
||||||
_env("API_BASE_KIMI")
|
|
||||||
or _env("MOONSHOT_BASE_URL")
|
|
||||||
or _env("AGENT_API_BASE_URL")
|
|
||||||
or "https://api.moonshot.ai/v1"
|
|
||||||
).rstrip("/")
|
|
||||||
url = f"{base}/users/me/balance"
|
|
||||||
payload, err = _http_get(url, headers={"Authorization": f"Bearer {api_key}"})
|
|
||||||
if err:
|
|
||||||
return {"success": False, "error": err}
|
|
||||||
|
|
||||||
try:
|
|
||||||
data = payload.get("data") or {}
|
|
||||||
available = float(data.get("available_balance", 0))
|
|
||||||
voucher = float(data.get("voucher_balance", 0))
|
|
||||||
cash = float(data.get("cash_balance", 0))
|
|
||||||
return {
|
|
||||||
"success": True,
|
|
||||||
"currency": payload.get("currency") or "CNY",
|
|
||||||
"available": available,
|
|
||||||
"voucher": voucher,
|
|
||||||
"cash": cash,
|
|
||||||
"raw": payload,
|
|
||||||
}
|
|
||||||
except Exception as exc: # pragma: no cover
|
|
||||||
return {"success": False, "error": f"解析失败: {exc}", "raw": payload}
|
|
||||||
|
|
||||||
|
|
||||||
# -------- DeepSeek --------
|
|
||||||
def fetch_deepseek_balance() -> Dict[str, Any]:
|
|
||||||
api_key = _env("DEEPSEEK_API_KEY") or _env("API_KEY_DEEPSEEK")
|
|
||||||
if not api_key:
|
|
||||||
return {"success": False, "error": "DeepSeek 密钥未设置(DEEPSEEK_API_KEY / API_KEY_DEEPSEEK)"}
|
|
||||||
|
|
||||||
url = "https://api.deepseek.com/user/balance"
|
|
||||||
payload, err = _http_get(url, headers={"Authorization": f"Bearer {api_key}"})
|
|
||||||
if err:
|
|
||||||
return {"success": False, "error": err}
|
|
||||||
|
|
||||||
try:
|
|
||||||
infos = payload.get("balance_infos") or []
|
|
||||||
primary = infos[0] if infos else {}
|
|
||||||
total = float(primary.get("total_balance") or 0)
|
|
||||||
granted = float(primary.get("granted_balance") or 0)
|
|
||||||
topped_up = float(primary.get("topped_up_balance") or 0)
|
|
||||||
return {
|
|
||||||
"success": True,
|
|
||||||
"currency": primary.get("currency", "CNY"),
|
|
||||||
"available": total,
|
|
||||||
"granted": granted,
|
|
||||||
"topped_up": topped_up,
|
|
||||||
"raw": payload,
|
|
||||||
}
|
|
||||||
except Exception as exc: # pragma: no cover
|
|
||||||
return {"success": False, "error": f"解析失败: {exc}", "raw": payload}
|
|
||||||
|
|
||||||
|
|
||||||
# -------- Qwen (Aliyun BSS QueryAccountBalance) --------
|
|
||||||
def _percent_encode(val: str) -> str:
|
|
||||||
res = parse.quote(str(val), safe="~")
|
|
||||||
return res.replace("+", "%20").replace("*", "%2A").replace("%7E", "~")
|
|
||||||
|
|
||||||
|
|
||||||
def _sign(params: Dict[str, Any], secret: str) -> str:
|
|
||||||
sorted_params = sorted(params.items(), key=lambda x: x[0])
|
|
||||||
canonicalized = "&".join(f"{_percent_encode(k)}={_percent_encode(v)}" for k, v in sorted_params)
|
|
||||||
string_to_sign = f"GET&%2F&{_percent_encode(canonicalized)}"
|
|
||||||
h = hmac.new((secret + "&").encode("utf-8"), string_to_sign.encode("utf-8"), hashlib.sha1)
|
|
||||||
return base64.b64encode(h.digest()).decode("utf-8")
|
|
||||||
|
|
||||||
|
|
||||||
def fetch_qwen_balance() -> Dict[str, Any]:
|
|
||||||
ak = _env("ALIYUN_ACCESS_KEY_ID")
|
|
||||||
sk = _env("ALIYUN_ACCESS_KEY_SECRET")
|
|
||||||
if not ak or not sk:
|
|
||||||
return {"success": False, "error": "缺少 ALIYUN_ACCESS_KEY_ID / ALIYUN_ACCESS_KEY_SECRET"}
|
|
||||||
|
|
||||||
params: Dict[str, Any] = {
|
|
||||||
"Format": "JSON",
|
|
||||||
"Version": "2017-12-14",
|
|
||||||
"AccessKeyId": ak,
|
|
||||||
"SignatureMethod": "HMAC-SHA1",
|
|
||||||
"Timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
|
||||||
"SignatureVersion": "1.0",
|
|
||||||
"SignatureNonce": str(uuid.uuid4()),
|
|
||||||
"Action": "QueryAccountBalance",
|
|
||||||
"RegionId": "cn-hangzhou",
|
|
||||||
}
|
|
||||||
signature = _sign(params, sk)
|
|
||||||
params["Signature"] = signature
|
|
||||||
|
|
||||||
# 按阿里云规范组装最终查询字符串(不可用 urlencode 的 quote_plus)
|
|
||||||
query = "&".join(f"{_percent_encode(k)}={_percent_encode(v)}" for k, v in params.items())
|
|
||||||
url = "https://business.aliyuncs.com/?" + query
|
|
||||||
|
|
||||||
payload, err = _http_get(url)
|
|
||||||
if err:
|
|
||||||
return {"success": False, "error": err}
|
|
||||||
|
|
||||||
try:
|
|
||||||
data = payload.get("Data") or {}
|
|
||||||
amount = float(data.get("AvailableAmount") or 0)
|
|
||||||
currency = data.get("Currency", "CNY")
|
|
||||||
return {
|
|
||||||
"success": True,
|
|
||||||
"currency": currency,
|
|
||||||
"available": amount,
|
|
||||||
"cash": float(data.get("AvailableCashAmount") or 0),
|
|
||||||
"raw": payload,
|
|
||||||
}
|
|
||||||
except Exception as exc: # pragma: no cover
|
|
||||||
return {"success": False, "error": f"解析失败: {exc}", "raw": payload}
|
|
||||||
|
|
||||||
|
|
||||||
def fetch_all_balances() -> Dict[str, Any]:
|
|
||||||
return {
|
|
||||||
"rate_usd_cny": USD_CNY_RATE,
|
|
||||||
"kimi": fetch_kimi_balance(),
|
|
||||||
"deepseek": fetch_deepseek_balance(),
|
|
||||||
"qwen": fetch_qwen_balance(),
|
|
||||||
}
|
|
||||||
@ -1,4 +1,15 @@
|
|||||||
"""Web server package entry."""
|
"""Web server package entry.
|
||||||
from .app import app, socketio, run_server, parse_arguments, initialize_system, resource_busy_page
|
|
||||||
|
使用 PEP 562 惰性导入:避免 `python -m server.app` 时因包级提前 import
|
||||||
|
导致 'server.app' 被加载两次(runpy RuntimeWarning)。
|
||||||
|
"""
|
||||||
|
import importlib
|
||||||
|
|
||||||
__all__ = ["app", "socketio", "run_server", "parse_arguments", "initialize_system", "resource_busy_page"]
|
__all__ = ["app", "socketio", "run_server", "parse_arguments", "initialize_system", "resource_busy_page"]
|
||||||
|
|
||||||
|
|
||||||
|
def __getattr__(name):
|
||||||
|
if name in __all__:
|
||||||
|
module = importlib.import_module(".app", __name__)
|
||||||
|
return getattr(module, name)
|
||||||
|
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||||||
|
|||||||
@ -28,7 +28,7 @@ from .conversation import (
|
|||||||
summarize_upload_events,
|
summarize_upload_events,
|
||||||
)
|
)
|
||||||
from .conversation_stats import summarize_invite_codes
|
from .conversation_stats import summarize_invite_codes
|
||||||
from modules import admin_policy_manager, balance_client
|
from modules import admin_policy_manager
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
from config import ADMIN_SECONDARY_PASSWORD_HASH, ADMIN_SECONDARY_PASSWORD, ADMIN_SECONDARY_TTL_SECONDS
|
from config import ADMIN_SECONDARY_PASSWORD_HASH, ADMIN_SECONDARY_PASSWORD, ADMIN_SECONDARY_TTL_SECONDS
|
||||||
|
|
||||||
@ -126,18 +126,6 @@ def admin_secondary_verify():
|
|||||||
return jsonify({"success": True, "ttl": ADMIN_SECONDARY_TTL_SECONDS})
|
return jsonify({"success": True, "ttl": ADMIN_SECONDARY_TTL_SECONDS})
|
||||||
|
|
||||||
|
|
||||||
@admin_bp.route('/api/admin/balance', methods=['GET'])
|
|
||||||
@login_required
|
|
||||||
@admin_required
|
|
||||||
def admin_balance_api():
|
|
||||||
"""查询第三方账户余额(Kimi/DeepSeek/Qwen)。"""
|
|
||||||
guard = _secondary_required()
|
|
||||||
if guard:
|
|
||||||
return guard
|
|
||||||
data = balance_client.fetch_all_balances()
|
|
||||||
return jsonify({"success": True, "data": data})
|
|
||||||
|
|
||||||
|
|
||||||
@admin_bp.route('/admin/assets/<path:filename>')
|
@admin_bp.route('/admin/assets/<path:filename>')
|
||||||
@login_required
|
@login_required
|
||||||
@admin_required
|
@admin_required
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
# web_server.py - Web服务器(修复版 - 确保text_end事件正确发送 + 停止功能)
|
# web_server.py - Web服务器(修复版 - 确保text_end事件正确发送 + 停止功能)
|
||||||
|
|
||||||
|
import os as _os_early
|
||||||
|
_os_early.environ.setdefault('FLASK_SKIP_DOTENV', '1') # 抑制 Flask 的 python-dotenv 提示(项目自带 .env 解析)
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
@ -21,7 +24,7 @@ import time
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from collections import defaultdict, deque, Counter
|
from collections import defaultdict, deque, Counter
|
||||||
from config.model_profiles import get_default_model_key, get_model_profile, get_registered_model_keys
|
from config.model_profiles import get_default_model_key, get_model_profile, get_registered_model_keys
|
||||||
from modules import admin_policy_manager, balance_client
|
from modules import admin_policy_manager
|
||||||
from modules.custom_tool_registry import CustomToolRegistry
|
from modules.custom_tool_registry import CustomToolRegistry
|
||||||
import server.state as state # 共享单例
|
import server.state as state # 共享单例
|
||||||
from server.auth import auth_bp
|
from server.auth import auth_bp
|
||||||
@ -221,6 +224,7 @@ from config import (
|
|||||||
DEFAULT_CONVERSATIONS_LIMIT,
|
DEFAULT_CONVERSATIONS_LIMIT,
|
||||||
MAX_CONVERSATIONS_LIMIT,
|
MAX_CONVERSATIONS_LIMIT,
|
||||||
CONVERSATIONS_DIR,
|
CONVERSATIONS_DIR,
|
||||||
|
DATA_DIR,
|
||||||
DEFAULT_RESPONSE_MAX_TOKENS,
|
DEFAULT_RESPONSE_MAX_TOKENS,
|
||||||
DEFAULT_PROJECT_PATH,
|
DEFAULT_PROJECT_PATH,
|
||||||
LOGS_DIR,
|
LOGS_DIR,
|
||||||
@ -231,6 +235,7 @@ from config import (
|
|||||||
UPLOAD_SCAN_LOG_SUBDIR,
|
UPLOAD_SCAN_LOG_SUBDIR,
|
||||||
WEB_SERVER_PORT,
|
WEB_SERVER_PORT,
|
||||||
WEB_SERVER_HOST,
|
WEB_SERVER_HOST,
|
||||||
|
TERMINAL_SANDBOX_MODE,
|
||||||
)
|
)
|
||||||
from modules.user_manager import UserManager, UserWorkspace
|
from modules.user_manager import UserManager, UserWorkspace
|
||||||
from modules.gui_file_manager import GuiFileManager
|
from modules.gui_file_manager import GuiFileManager
|
||||||
@ -251,7 +256,7 @@ app.config['MAX_CONTENT_LENGTH'] = MAX_UPLOAD_SIZE
|
|||||||
_secret_key = os.environ.get("WEB_SECRET_KEY") or os.environ.get("SECRET_KEY")
|
_secret_key = os.environ.get("WEB_SECRET_KEY") or os.environ.get("SECRET_KEY")
|
||||||
if not _secret_key:
|
if not _secret_key:
|
||||||
_secret_key = secrets.token_hex(32)
|
_secret_key = secrets.token_hex(32)
|
||||||
print("[security] WEB_SECRET_KEY 未设置,已生成临时密钥(重启后失效)。")
|
print(f"{OUTPUT_FORMATS['warning']} WEB_SECRET_KEY 未设置,已生成临时密钥(重启后所有会话将失效)。")
|
||||||
app.config['SECRET_KEY'] = _secret_key
|
app.config['SECRET_KEY'] = _secret_key
|
||||||
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=12)
|
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=12)
|
||||||
_cookie_secure_env = (os.environ.get("WEB_COOKIE_SECURE") or "").strip().lower()
|
_cookie_secure_env = (os.environ.get("WEB_COOKIE_SECURE") or "").strip().lower()
|
||||||
@ -1229,17 +1234,15 @@ def initialize_system(path: str, thinking_mode: bool = False):
|
|||||||
DEBUG_LOG_FILE.parent.mkdir(parents=True, exist_ok=True)
|
DEBUG_LOG_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||||
with DEBUG_LOG_FILE.open('w', encoding='utf-8') as f:
|
with DEBUG_LOG_FILE.open('w', encoding='utf-8') as f:
|
||||||
f.write(f"调试日志开始 - {datetime.now()}\n")
|
f.write(f"调试日志开始 - {datetime.now()}\n")
|
||||||
f.write(f"项目路径: {path}\n")
|
f.write(f"默认工作区路径: {path}\n")
|
||||||
f.write(f"思考模式: {'思考模式' if thinking_mode else '快速模式'}\n")
|
f.write(f"默认思考模式: {'思考' if thinking_mode else '快速'}(对话中可切换)\n")
|
||||||
f.write(f"自动修复: {'开启' if AUTO_FIX_TOOL_CALL else '关闭'}\n")
|
f.write(f"自动修复: {'开启' if AUTO_FIX_TOOL_CALL else '关闭'}\n")
|
||||||
f.write(f"最大迭代: {MAX_ITERATIONS_PER_TASK}\n")
|
f.write(f"最大迭代: {MAX_ITERATIONS_PER_TASK}\n")
|
||||||
f.write(f"最大工具调用: {MAX_TOTAL_TOOL_CALLS}\n")
|
f.write(f"最大工具调用: {MAX_TOTAL_TOOL_CALLS}\n")
|
||||||
f.write("="*80 + "\n")
|
f.write("="*80 + "\n")
|
||||||
print(f"[Init] 初始化Web系统...")
|
print(f"{OUTPUT_FORMATS['info']} 初始化 Web 系统...")
|
||||||
print(f"[Init] 项目路径: {path}")
|
print(f"{OUTPUT_FORMATS['info']} 数据目录: {DATA_DIR}")
|
||||||
print(f"[Init] 运行模式: {'思考模式(首次思考,后续快速)' if thinking_mode else '快速模式(无思考)'}")
|
print(f"{OUTPUT_FORMATS['info']} 调试日志: {DEBUG_LOG_FILE}")
|
||||||
print(f"[Init] 自动修复: {'开启' if AUTO_FIX_TOOL_CALL else '关闭'}")
|
|
||||||
print(f"[Init] 调试日志: {DEBUG_LOG_FILE}")
|
|
||||||
app.config['DEFAULT_THINKING_MODE'] = thinking_mode
|
app.config['DEFAULT_THINKING_MODE'] = thinking_mode
|
||||||
app.config['DEFAULT_RUN_MODE'] = "thinking" if thinking_mode else "fast"
|
app.config['DEFAULT_RUN_MODE'] = "thinking" if thinking_mode else "fast"
|
||||||
# 同步预设子智能体角色到运行态目录
|
# 同步预设子智能体角色到运行态目录
|
||||||
@ -1249,7 +1252,8 @@ def initialize_system(path: str, thinking_mode: bool = False):
|
|||||||
print(f"{OUTPUT_FORMATS['success']} 预设子智能体角色同步完成")
|
print(f"{OUTPUT_FORMATS['success']} 预设子智能体角色同步完成")
|
||||||
except Exception as _e:
|
except Exception as _e:
|
||||||
print(f"{OUTPUT_FORMATS['warning']} 预设子智能体角色同步失败: {_e}")
|
print(f"{OUTPUT_FORMATS['warning']} 预设子智能体角色同步失败: {_e}")
|
||||||
print(f"{OUTPUT_FORMATS['success']} Web系统初始化完成(多用户模式)")
|
_mode_label = "宿主机模式(单用户)" if TERMINAL_SANDBOX_MODE == "host" else "多用户模式(Web)"
|
||||||
|
print(f"{OUTPUT_FORMATS['success']} Web 系统初始化完成({_mode_label})")
|
||||||
|
|
||||||
|
|
||||||
def run_server(path: str, thinking_mode: bool = False, port: int = DEFAULT_PORT, debug: bool = False):
|
def run_server(path: str, thinking_mode: bool = False, port: int = DEFAULT_PORT, debug: bool = False):
|
||||||
@ -1276,7 +1280,7 @@ def parse_arguments():
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--path",
|
"--path",
|
||||||
default=str(Path(DEFAULT_PROJECT_PATH).resolve()),
|
default=str(Path(DEFAULT_PROJECT_PATH).resolve()),
|
||||||
help="项目工作目录(默认使用 config.DEFAULT_PROJECT_PATH)"
|
help="默认工作区路径(仅作兜底,工作区可在界面中管理)"
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--port",
|
"--port",
|
||||||
@ -1292,7 +1296,7 @@ def parse_arguments():
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--thinking-mode",
|
"--thinking-mode",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="启用思考模式(首次请求使用 reasoning)"
|
help="新对话默认使用思考模式(对话中可随时切换)"
|
||||||
)
|
)
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from server.chat import chat_bp
|
from server.chat import chat_bp
|
||||||
import json, time
|
import json, logging, time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Dict, Any, Optional
|
from typing import Dict, Any, Optional
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@ -8,6 +8,8 @@ from io import BytesIO
|
|||||||
import zipfile
|
import zipfile
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
from flask import Blueprint, jsonify, request, session, send_file
|
from flask import Blueprint, jsonify, request, session, send_file
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
from werkzeug.exceptions import RequestEntityTooLarge
|
from werkzeug.exceptions import RequestEntityTooLarge
|
||||||
@ -111,7 +113,7 @@ def update_thinking_mode(terminal: WebTerminal, workspace: UserWorkspace, userna
|
|||||||
has_videos=getattr(ctx, "has_videos", False)
|
has_videos=getattr(ctx, "has_videos", False)
|
||||||
)
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print(f"[API] 保存思考模式到对话失败: {exc}")
|
logger.error(f"[API] 保存思考模式到对话失败: {exc}")
|
||||||
|
|
||||||
status = terminal.get_status()
|
status = terminal.get_status()
|
||||||
socketio.emit('status_update', status, room=f"user_{username}")
|
socketio.emit('status_update', status, room=f"user_{username}")
|
||||||
@ -124,7 +126,7 @@ def update_thinking_mode(terminal: WebTerminal, workspace: UserWorkspace, userna
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print(f"[API] 切换思考模式失败: {exc}")
|
logger.error(f"[API] 切换思考模式失败: {exc}")
|
||||||
code = 400 if isinstance(exc, ValueError) else 500
|
code = 400 if isinstance(exc, ValueError) else 500
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"success": False,
|
"success": False,
|
||||||
@ -178,7 +180,7 @@ def update_reasoning_effort(terminal: WebTerminal, workspace: UserWorkspace, use
|
|||||||
reasoning_effort=effort,
|
reasoning_effort=effort,
|
||||||
)
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print(f"[API] 保存推理强度到对话失败: {exc}")
|
logger.error(f"[API] 保存推理强度到对话失败: {exc}")
|
||||||
|
|
||||||
status = terminal.get_status()
|
status = terminal.get_status()
|
||||||
socketio.emit('status_update', status, room=f"user_{username}")
|
socketio.emit('status_update', status, room=f"user_{username}")
|
||||||
@ -190,7 +192,7 @@ def update_reasoning_effort(terminal: WebTerminal, workspace: UserWorkspace, use
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print(f"[API] 设置推理强度失败: {exc}")
|
logger.error(f"[API] 设置推理强度失败: {exc}")
|
||||||
code = 400 if isinstance(exc, ValueError) else 500
|
code = 400 if isinstance(exc, ValueError) else 500
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"success": False,
|
"success": False,
|
||||||
@ -253,7 +255,7 @@ def update_model(terminal: WebTerminal, workspace: UserWorkspace, username: str)
|
|||||||
has_videos=getattr(ctx, "has_videos", False)
|
has_videos=getattr(ctx, "has_videos", False)
|
||||||
)
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print(f"[API] 保存模型到对话失败: {exc}")
|
logger.error(f"[API] 保存模型到对话失败: {exc}")
|
||||||
else:
|
else:
|
||||||
print(
|
print(
|
||||||
f"[API] 跳过模型保存:请求对话 {requested_cid} "
|
f"[API] 跳过模型保存:请求对话 {requested_cid} "
|
||||||
@ -272,7 +274,7 @@ def update_model(terminal: WebTerminal, workspace: UserWorkspace, username: str)
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print(f"[API] 切换模型失败: {exc}")
|
logger.error(f"[API] 切换模型失败: {exc}")
|
||||||
code = 400 if isinstance(exc, ValueError) else 500
|
code = 400 if isinstance(exc, ValueError) else 500
|
||||||
return jsonify({"success": False, "error": str(exc), "message": str(exc)}), code
|
return jsonify({"success": False, "error": str(exc), "message": str(exc)}), code
|
||||||
|
|
||||||
|
|||||||
@ -220,7 +220,6 @@ def process_message_task(terminal: WebTerminal, message: str, images, sender, cl
|
|||||||
debug_log(f"错误恢复:保存对话状态失败: {save_error}")
|
debug_log(f"错误恢复:保存对话状态失败: {save_error}")
|
||||||
|
|
||||||
# 原有的错误处理逻辑
|
# 原有的错误处理逻辑
|
||||||
print(f"[Task] 错误: {e}")
|
|
||||||
debug_log(f"任务处理错误: {e}")
|
debug_log(f"任务处理错误: {e}")
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|||||||
@ -9,7 +9,7 @@ from config.model_profiles import get_model_profile
|
|||||||
|
|
||||||
from utils.token_usage import extract_usage_payload
|
from utils.token_usage import extract_usage_payload
|
||||||
|
|
||||||
from .utils_common import debug_log, brief_log, log_backend_chunk
|
from .utils_common import debug_log, log_backend_chunk
|
||||||
from .chat_flow_runner_helpers import extract_intent_from_partial
|
from .chat_flow_runner_helpers import extract_intent_from_partial
|
||||||
from .chat_flow_task_support import wait_retry_delay, cancel_pending_tools
|
from .chat_flow_task_support import wait_retry_delay, cancel_pending_tools
|
||||||
from .state import get_stop_flag, clear_stop_flag
|
from .state import get_stop_flag, clear_stop_flag
|
||||||
@ -158,7 +158,7 @@ async def run_streaming_attempts(*, web_terminal, messages, tools, sender, clien
|
|||||||
and detected_tool_intent.get(tool_id) != intent_value
|
and detected_tool_intent.get(tool_id) != intent_value
|
||||||
):
|
):
|
||||||
detected_tool_intent[tool_id] = intent_value
|
detected_tool_intent[tool_id] = intent_value
|
||||||
brief_log(f"[intent] 增量提取 {tool_name}: {intent_value}")
|
debug_log(f"[intent] 增量提取 {tool_name}: {intent_value}")
|
||||||
sender('tool_intent', {
|
sender('tool_intent', {
|
||||||
'id': tool_id,
|
'id': tool_id,
|
||||||
'name': tool_name,
|
'name': tool_name,
|
||||||
@ -185,10 +185,10 @@ async def run_streaming_attempts(*, web_terminal, messages, tools, sender, clien
|
|||||||
intent_value = extract_intent_from_partial(arguments_str)
|
intent_value = extract_intent_from_partial(arguments_str)
|
||||||
if intent_value:
|
if intent_value:
|
||||||
detected_tool_intent[tool_id] = intent_value
|
detected_tool_intent[tool_id] = intent_value
|
||||||
brief_log(f"[intent] 预提取 {tool_name}: {intent_value}")
|
debug_log(f"[intent] 预提取 {tool_name}: {intent_value}")
|
||||||
|
|
||||||
# 立即发送工具准备中事件
|
# 立即发送工具准备中事件
|
||||||
brief_log(f"[tool] 准备调用 {tool_name} (id={tool_id}) intent={intent_value or '-'}")
|
debug_log(f"[tool] 准备调用 {tool_name} (id={tool_id}) intent={intent_value or '-'}")
|
||||||
sender('tool_preparing', {
|
sender('tool_preparing', {
|
||||||
'id': tool_id,
|
'id': tool_id,
|
||||||
'name': tool_name,
|
'name': tool_name,
|
||||||
@ -249,7 +249,7 @@ async def run_streaming_attempts(*, web_terminal, messages, tools, sender, clien
|
|||||||
text_started = True
|
text_started = True
|
||||||
text_streaming = True
|
text_streaming = True
|
||||||
sender('text_start', {})
|
sender('text_start', {})
|
||||||
brief_log("模型输出了内容")
|
debug_log("模型输出了内容")
|
||||||
await asyncio.sleep(0.05)
|
await asyncio.sleep(0.05)
|
||||||
|
|
||||||
full_response += content
|
full_response += content
|
||||||
|
|||||||
@ -1915,7 +1915,7 @@ async def handle_task_with_sender(
|
|||||||
return
|
return
|
||||||
|
|
||||||
tool_call_limit_label = MAX_TOTAL_TOOL_CALLS if MAX_TOTAL_TOOL_CALLS is not None else "∞"
|
tool_call_limit_label = MAX_TOTAL_TOOL_CALLS if MAX_TOTAL_TOOL_CALLS is not None else "∞"
|
||||||
print(f"[API] 第{current_iteration}次调用 (总工具调用: {total_tool_calls}/{tool_call_limit_label})")
|
debug_log(f"[API] 第{current_iteration}次调用 (总工具调用: {total_tool_calls}/{tool_call_limit_label})")
|
||||||
|
|
||||||
stream_result = await run_streaming_attempts(
|
stream_result = await run_streaming_attempts(
|
||||||
web_terminal=web_terminal,
|
web_terminal=web_terminal,
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import uuid
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, Dict, Any, List
|
from typing import Optional, Dict, Any, List
|
||||||
|
|
||||||
from .utils_common import debug_log, brief_log
|
from .utils_common import debug_log
|
||||||
from .state import MONITOR_FILE_TOOLS, MONITOR_MEMORY_TOOLS, MONITOR_SNAPSHOT_CHAR_LIMIT, MONITOR_MEMORY_ENTRY_LIMIT
|
from .state import MONITOR_FILE_TOOLS, MONITOR_MEMORY_TOOLS, MONITOR_SNAPSHOT_CHAR_LIMIT, MONITOR_MEMORY_ENTRY_LIMIT
|
||||||
from .state import tool_approval_manager, user_question_manager
|
from .state import tool_approval_manager, user_question_manager
|
||||||
from .monitor import cache_monitor_snapshot
|
from .monitor import cache_monitor_snapshot
|
||||||
@ -726,7 +726,7 @@ async def execute_tool_calls(*, web_terminal, tool_calls, sender, messages, clie
|
|||||||
'monitor_snapshot': monitor_snapshot,
|
'monitor_snapshot': monitor_snapshot,
|
||||||
'conversation_id': conversation_id
|
'conversation_id': conversation_id
|
||||||
})
|
})
|
||||||
brief_log(f"调用了工具: {function_name}")
|
debug_log(f"调用了工具: {function_name}")
|
||||||
|
|
||||||
await asyncio.sleep(0.3)
|
await asyncio.sleep(0.3)
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
import logging
|
||||||
import sys, os
|
import sys, os
|
||||||
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
|
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
|
||||||
if PROJECT_ROOT not in sys.path:
|
if PROJECT_ROOT not in sys.path:
|
||||||
sys.path.insert(0, PROJECT_ROOT)
|
sys.path.insert(0, PROJECT_ROOT)
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
import asyncio, json, time, re, os, shutil
|
import asyncio, json, time, re, os, shutil
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@ -579,7 +582,7 @@ def get_conversations(terminal: WebTerminal, workspace: UserWorkspace, username:
|
|||||||
}), 500
|
}), 500
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[API] 获取对话列表错误: {e}")
|
logger.error(f"[API] 获取对话列表错误: {e}")
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"success": False,
|
"success": False,
|
||||||
"error": str(e),
|
"error": str(e),
|
||||||
@ -754,7 +757,7 @@ def create_conversation(terminal: WebTerminal, workspace: UserWorkspace, usernam
|
|||||||
return jsonify(result), 500
|
return jsonify(result), 500
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[API] 创建对话错误: {e}")
|
logger.error(f"[API] 创建对话错误: {e}")
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"success": False,
|
"success": False,
|
||||||
"error": str(e),
|
"error": str(e),
|
||||||
@ -793,7 +796,7 @@ def get_conversation_info(terminal: WebTerminal, workspace: UserWorkspace, usern
|
|||||||
}), 404
|
}), 404
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[API] 获取对话信息错误: {e}")
|
logger.error(f"[API] 获取对话信息错误: {e}")
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"success": False,
|
"success": False,
|
||||||
"error": str(e),
|
"error": str(e),
|
||||||
@ -903,7 +906,7 @@ def load_conversation(conversation_id, terminal: WebTerminal, workspace: UserWor
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
import traceback
|
import traceback
|
||||||
print(f"[API] 加载对话错误: {e}")
|
logger.error(f"[API] 加载对话错误: {e}")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"success": False,
|
"success": False,
|
||||||
@ -957,7 +960,7 @@ def delete_conversation(conversation_id, terminal: WebTerminal, workspace: UserW
|
|||||||
return jsonify(result), 404 if "不存在" in result.get("message", "") else 500
|
return jsonify(result), 404 if "不存在" in result.get("message", "") else 500
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[API] 删除对话错误: {e}")
|
logger.error(f"[API] 删除对话错误: {e}")
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"success": False,
|
"success": False,
|
||||||
"error": str(e),
|
"error": str(e),
|
||||||
@ -1033,12 +1036,12 @@ def search_conversations(terminal: WebTerminal, workspace: UserWorkspace, userna
|
|||||||
username, ws_id, terminal, workspace
|
username, ws_id, terminal, workspace
|
||||||
)
|
)
|
||||||
except (ValueError, RuntimeError) as resolve_err:
|
except (ValueError, RuntimeError) as resolve_err:
|
||||||
print(f"[API] 搜索跳过工作区 {ws_id}: {resolve_err}")
|
logger.error(f"[API] 搜索跳过工作区 {ws_id}: {resolve_err}")
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
result = target_terminal.search_conversations(query, limit, multi_agent_mode=multi_agent_filter)
|
result = target_terminal.search_conversations(query, limit, multi_agent_mode=multi_agent_filter)
|
||||||
except Exception as search_err:
|
except Exception as search_err:
|
||||||
print(f"[API] 搜索工作区对话失败 {ws_id}: {search_err}")
|
logger.error(f"[API] 搜索工作区对话失败 {ws_id}: {search_err}")
|
||||||
continue
|
continue
|
||||||
results = result.get("results") or []
|
results = result.get("results") or []
|
||||||
if not results:
|
if not results:
|
||||||
@ -1061,7 +1064,7 @@ def search_conversations(terminal: WebTerminal, workspace: UserWorkspace, userna
|
|||||||
})
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[API] 搜索对话错误: {e}")
|
logger.error(f"[API] 搜索对话错误: {e}")
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"success": False,
|
"success": False,
|
||||||
"error": str(e),
|
"error": str(e),
|
||||||
@ -1124,7 +1127,7 @@ def get_conversation_messages(conversation_id, terminal: WebTerminal, workspace:
|
|||||||
}), 404
|
}), 404
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[API] 获取对话消息错误: {e}")
|
logger.error(f"[API] 获取对话消息错误: {e}")
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"success": False,
|
"success": False,
|
||||||
"error": str(e),
|
"error": str(e),
|
||||||
@ -1763,7 +1766,7 @@ def compress_conversation(conversation_id, terminal: WebTerminal, workspace: Use
|
|||||||
return jsonify(response_payload)
|
return jsonify(response_payload)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[API] 压缩对话错误: {e}")
|
logger.error(f"[API] 压缩对话错误: {e}")
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"success": False,
|
"success": False,
|
||||||
"error": str(e),
|
"error": str(e),
|
||||||
@ -2208,7 +2211,7 @@ def duplicate_conversation(conversation_id, terminal: WebTerminal, workspace: Us
|
|||||||
return jsonify(response_payload)
|
return jsonify(response_payload)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[API] 复制对话错误: {e}")
|
logger.error(f"[API] 复制对话错误: {e}")
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"success": False,
|
"success": False,
|
||||||
"error": str(e),
|
"error": str(e),
|
||||||
@ -2251,7 +2254,7 @@ def review_conversation_preview(conversation_id, terminal: WebTerminal, workspac
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[API] 对话回顾预览错误: {e}")
|
logger.error(f"[API] 对话回顾预览错误: {e}")
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"success": False,
|
"success": False,
|
||||||
"error": str(e),
|
"error": str(e),
|
||||||
@ -2307,7 +2310,7 @@ def review_conversation(conversation_id, terminal: WebTerminal, workspace: UserW
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[API] 对话回顾生成错误: {e}")
|
logger.error(f"[API] 对话回顾生成错误: {e}")
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"success": False,
|
"success": False,
|
||||||
"error": str(e),
|
"error": str(e),
|
||||||
@ -2328,7 +2331,7 @@ def get_conversations_statistics(terminal: WebTerminal, workspace: UserWorkspace
|
|||||||
})
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[API] 获取对话统计错误: {e}")
|
logger.error(f"[API] 获取对话统计错误: {e}")
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"success": False,
|
"success": False,
|
||||||
"error": str(e),
|
"error": str(e),
|
||||||
@ -2377,7 +2380,7 @@ def get_current_conversation(terminal: WebTerminal, workspace: UserWorkspace, us
|
|||||||
}), 404
|
}), 404
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[API] 获取当前对话错误: {e}")
|
logger.error(f"[API] 获取当前对话错误: {e}")
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"success": False,
|
"success": False,
|
||||||
"error": str(e)
|
"error": str(e)
|
||||||
@ -2490,7 +2493,7 @@ def get_conversation_token_statistics(conversation_id, terminal: WebTerminal, wo
|
|||||||
}), 404
|
}), 404
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[API] 获取token统计错误: {e}")
|
logger.error(f"[API] 获取token统计错误: {e}")
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"success": False,
|
"success": False,
|
||||||
"error": str(e),
|
"error": str(e),
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
from collections import Counter, deque
|
from collections import Counter, deque
|
||||||
@ -8,6 +9,8 @@ from datetime import datetime, timedelta
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
from modules.user_manager import UserWorkspace
|
from modules.user_manager import UserWorkspace
|
||||||
from modules.usage_tracker import QUOTA_DEFAULTS
|
from modules.usage_tracker import QUOTA_DEFAULTS
|
||||||
from utils.conversation_manager import ConversationManager
|
from utils.conversation_manager import ConversationManager
|
||||||
@ -131,7 +134,7 @@ def _read_token_totals_file(workspace: UserWorkspace) -> Dict[str, int]:
|
|||||||
"total_tokens": total_tokens,
|
"total_tokens": total_tokens,
|
||||||
}
|
}
|
||||||
except (OSError, json.JSONDecodeError, ValueError) as exc:
|
except (OSError, json.JSONDecodeError, ValueError) as exc:
|
||||||
print(f"[admin] 解析 token_totals.json 失败 ({workspace.username}): {exc}")
|
logger.error(f"[admin] 解析 token_totals.json 失败 ({workspace.username}): {exc}")
|
||||||
return {"input_tokens": 0, "output_tokens": 0, "total_tokens": 0}
|
return {"input_tokens": 0, "output_tokens": 0, "total_tokens": 0}
|
||||||
|
|
||||||
def _collect_conversation_token_totals(workspace: UserWorkspace) -> Dict[str, int]:
|
def _collect_conversation_token_totals(workspace: UserWorkspace) -> Dict[str, int]:
|
||||||
@ -148,7 +151,7 @@ def _collect_conversation_token_totals(workspace: UserWorkspace) -> Dict[str, in
|
|||||||
"total_tokens": total_tokens,
|
"total_tokens": total_tokens,
|
||||||
}
|
}
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print(f"[admin] 读取 legacy token 统计失败 ({workspace.username}): {exc}")
|
logger.error(f"[admin] 读取 legacy token 统计失败 ({workspace.username}): {exc}")
|
||||||
return {"input_tokens": 0, "output_tokens": 0, "total_tokens": 0}
|
return {"input_tokens": 0, "output_tokens": 0, "total_tokens": 0}
|
||||||
|
|
||||||
def collect_user_token_statistics(workspace: UserWorkspace) -> Dict[str, int]:
|
def collect_user_token_statistics(workspace: UserWorkspace) -> Dict[str, int]:
|
||||||
|
|||||||
@ -23,7 +23,7 @@ from config.model_profiles import model_supports_image, model_supports_video
|
|||||||
@socketio.on('connect')
|
@socketio.on('connect')
|
||||||
def handle_connect(auth):
|
def handle_connect(auth):
|
||||||
"""客户端连接"""
|
"""客户端连接"""
|
||||||
print(f"[WebSocket] 客户端连接: {request.sid}")
|
debug_log(f"[WebSocket] 客户端连接: {request.sid}")
|
||||||
username = get_current_username()
|
username = get_current_username()
|
||||||
token_value = (auth or {}).get('socket_token') if isinstance(auth, dict) else None
|
token_value = (auth or {}).get('socket_token') if isinstance(auth, dict) else None
|
||||||
if not username or not consume_socket_token(token_value, username):
|
if not username or not consume_socket_token(token_value, username):
|
||||||
@ -75,7 +75,7 @@ def handle_connect(auth):
|
|||||||
@socketio.on('disconnect')
|
@socketio.on('disconnect')
|
||||||
def handle_disconnect():
|
def handle_disconnect():
|
||||||
"""客户端断开"""
|
"""客户端断开"""
|
||||||
print(f"[WebSocket] 客户端断开: {request.sid}")
|
debug_log(f"[WebSocket] 客户端断开: {request.sid}")
|
||||||
username = connection_users.pop(request.sid, None)
|
username = connection_users.pop(request.sid, None)
|
||||||
# 若同一用户仍有其他活跃连接,不因断开而停止任务
|
# 若同一用户仍有其他活跃连接,不因断开而停止任务
|
||||||
has_other_connection = False
|
has_other_connection = False
|
||||||
@ -127,7 +127,7 @@ def handle_disconnect():
|
|||||||
@socketio.on('stop_task')
|
@socketio.on('stop_task')
|
||||||
def handle_stop_task():
|
def handle_stop_task():
|
||||||
"""处理停止任务请求"""
|
"""处理停止任务请求"""
|
||||||
print(f"[停止] 收到停止请求: {request.sid}")
|
debug_log(f"[停止] 收到停止请求: {request.sid}")
|
||||||
username = connection_users.get(request.sid)
|
username = connection_users.get(request.sid)
|
||||||
task_info = get_stop_flag(request.sid, username)
|
task_info = get_stop_flag(request.sid, username)
|
||||||
if not isinstance(task_info, dict):
|
if not isinstance(task_info, dict):
|
||||||
@ -172,7 +172,7 @@ def handle_terminal_subscribe(data):
|
|||||||
room_name = f"user_{username}_terminal"
|
room_name = f"user_{username}_terminal"
|
||||||
join_room(room_name)
|
join_room(room_name)
|
||||||
terminal_rooms[request.sid].add(room_name)
|
terminal_rooms[request.sid].add(room_name)
|
||||||
print(f"[Terminal] {request.sid} 订阅所有终端事件")
|
debug_log(f"[Terminal] {request.sid} 订阅所有终端事件")
|
||||||
|
|
||||||
# 发送当前终端状态
|
# 发送当前终端状态
|
||||||
emit('terminal_subscribed', {
|
emit('terminal_subscribed', {
|
||||||
@ -184,7 +184,7 @@ def handle_terminal_subscribe(data):
|
|||||||
room_name = f'user_{username}_terminal_{session_name}'
|
room_name = f'user_{username}_terminal_{session_name}'
|
||||||
join_room(room_name)
|
join_room(room_name)
|
||||||
terminal_rooms[request.sid].add(room_name)
|
terminal_rooms[request.sid].add(room_name)
|
||||||
print(f"[Terminal] {request.sid} 订阅终端: {session_name}")
|
debug_log(f"[Terminal] {request.sid} 订阅终端: {session_name}")
|
||||||
|
|
||||||
# 发送该终端的当前输出
|
# 发送该终端的当前输出
|
||||||
output_result = terminal.terminal_manager.get_terminal_output(session_name, 100)
|
output_result = terminal.terminal_manager.get_terminal_output(session_name, 100)
|
||||||
@ -205,7 +205,7 @@ def handle_terminal_unsubscribe(data):
|
|||||||
leave_room(room_name)
|
leave_room(room_name)
|
||||||
if request.sid in terminal_rooms:
|
if request.sid in terminal_rooms:
|
||||||
terminal_rooms[request.sid].discard(room_name)
|
terminal_rooms[request.sid].discard(room_name)
|
||||||
print(f"[Terminal] {request.sid} 取消订阅终端: {session_name}")
|
debug_log(f"[Terminal] {request.sid} 取消订阅终端: {session_name}")
|
||||||
|
|
||||||
@socketio.on('get_terminal_output')
|
@socketio.on('get_terminal_output')
|
||||||
def handle_get_terminal_output(data):
|
def handle_get_terminal_output(data):
|
||||||
@ -279,7 +279,7 @@ def handle_message(data):
|
|||||||
emit('error', {'message': '图片和视频请分开发送'})
|
emit('error', {'message': '图片和视频请分开发送'})
|
||||||
return
|
return
|
||||||
|
|
||||||
print(f"[WebSocket] 收到消息: {message}")
|
debug_log(f"[WebSocket] 收到消息: {message}")
|
||||||
debug_log(f"\n{'='*80}\n新任务开始: {message}\n{'='*80}")
|
debug_log(f"\n{'='*80}\n新任务开始: {message}\n{'='*80}")
|
||||||
record_user_activity(username)
|
record_user_activity(username)
|
||||||
|
|
||||||
|
|||||||
@ -272,55 +272,6 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
<section v-else-if="activeSection === 'balance'" key="balance" class="panel">
|
|
||||||
<div class="balance-header">
|
|
||||||
<div>
|
|
||||||
<h2>余额查询</h2>
|
|
||||||
<p class="muted">手动刷新,查看 Kimi / DeepSeek / Qwen 余额</p>
|
|
||||||
</div>
|
|
||||||
<div class="balance-actions">
|
|
||||||
<span class="muted" v-if="balanceUpdatedAt"
|
|
||||||
>上次刷新:{{ timeAgo(balanceUpdatedAt) }}</span
|
|
||||||
>
|
|
||||||
<button type="button" :disabled="balanceLoading" @click="fetchBalance">
|
|
||||||
{{ balanceLoading ? '查询中...' : '手动刷新' }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<section v-if="balanceError" class="banner-error">
|
|
||||||
<strong>查询失败:</strong>
|
|
||||||
<span>{{ balanceError }}</span>
|
|
||||||
</section>
|
|
||||||
<div class="balance-grid">
|
|
||||||
<div
|
|
||||||
v-for="card in balanceCards"
|
|
||||||
:key="card.key"
|
|
||||||
class="balance-card"
|
|
||||||
:class="{ error: !card.success }"
|
|
||||||
>
|
|
||||||
<div class="balance-card-head">
|
|
||||||
<h3>{{ card.label }}</h3>
|
|
||||||
<span v-if="card.success" class="status-badge online">正常</span>
|
|
||||||
<span v-else class="status-badge danger">失败</span>
|
|
||||||
</div>
|
|
||||||
<div class="balance-amount">
|
|
||||||
<strong v-if="card.amount !== null">{{ card.amount }} {{ card.currency }}</strong>
|
|
||||||
<strong v-else>—</strong>
|
|
||||||
<span v-if="card.amountCny !== null" class="sub">≈ ¥{{ card.amountCny }}</span>
|
|
||||||
</div>
|
|
||||||
<ul class="balance-meta" v-if="card.success">
|
|
||||||
<li v-if="card.extras.voucher !== undefined">券:{{ card.extras.voucher }}</li>
|
|
||||||
<li v-if="card.extras.cash !== undefined">现金:{{ card.extras.cash }}</li>
|
|
||||||
<li v-if="card.extras.granted !== undefined">赠送:{{ card.extras.granted }}</li>
|
|
||||||
<li v-if="card.extras.topped_up !== undefined">
|
|
||||||
充值:{{ card.extras.topped_up }}
|
|
||||||
</li>
|
|
||||||
<li v-if="card.extras.rate">汇率 USD→CNY:{{ card.extras.rate }}</li>
|
|
||||||
</ul>
|
|
||||||
<p v-else class="muted small">{{ card.error || '未知错误' }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section v-else-if="activeSection === 'invites'" key="invites" class="panel">
|
<section v-else-if="activeSection === 'invites'" key="invites" class="panel">
|
||||||
<h2>邀请码</h2>
|
<h2>邀请码</h2>
|
||||||
<div class="stats-row">
|
<div class="stats-row">
|
||||||
@ -478,7 +429,7 @@ import { useSecondaryPass } from './useSecondaryPass';
|
|||||||
|
|
||||||
type Snapshot = Record<string, any> | null;
|
type Snapshot = Record<string, any> | null;
|
||||||
|
|
||||||
type SectionId = 'overview' | 'usage' | 'users' | 'containers' | 'uploads' | 'balance' | 'invites' | 'passwords';
|
type SectionId = 'overview' | 'usage' | 'users' | 'containers' | 'uploads' | 'invites' | 'passwords';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
verified: secondaryVerified,
|
verified: secondaryVerified,
|
||||||
@ -497,10 +448,6 @@ const bannerError = ref<string | null>(null);
|
|||||||
const autoRefresh = ref(true);
|
const autoRefresh = ref(true);
|
||||||
const activeSection = ref<SectionId>('overview');
|
const activeSection = ref<SectionId>('overview');
|
||||||
let timer: number | undefined;
|
let timer: number | undefined;
|
||||||
const balanceLoading = ref(false);
|
|
||||||
const balanceError = ref<string | null>(null);
|
|
||||||
const balanceData = ref<Record<string, any> | null>(null);
|
|
||||||
const balanceUpdatedAt = ref<number | null>(null);
|
|
||||||
const inviteSubmitting = ref(false);
|
const inviteSubmitting = ref(false);
|
||||||
const inviteError = ref<string | null>(null);
|
const inviteError = ref<string | null>(null);
|
||||||
const newInviteCode = ref('');
|
const newInviteCode = ref('');
|
||||||
@ -522,7 +469,6 @@ const sectionTabs: Array<{ id: SectionId; label: string }> = [
|
|||||||
{ id: 'users', label: '用户' },
|
{ id: 'users', label: '用户' },
|
||||||
{ id: 'containers', label: '容器' },
|
{ id: 'containers', label: '容器' },
|
||||||
{ id: 'uploads', label: '上传审计' },
|
{ id: 'uploads', label: '上传审计' },
|
||||||
{ id: 'balance', label: '余额查询' },
|
|
||||||
{ id: 'invites', label: '邀请码' },
|
{ id: 'invites', label: '邀请码' },
|
||||||
{ id: 'passwords', label: '密码管理' }
|
{ id: 'passwords', label: '密码管理' }
|
||||||
];
|
];
|
||||||
@ -789,23 +735,6 @@ const removeInvite = async (code: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchBalance = async () => {
|
|
||||||
balanceLoading.value = true;
|
|
||||||
balanceError.value = null;
|
|
||||||
try {
|
|
||||||
const resp = await fetch('/api/admin/balance', { credentials: 'same-origin' });
|
|
||||||
if (!resp.ok) throw new Error(`请求失败:${resp.status}`);
|
|
||||||
const payload = await resp.json();
|
|
||||||
if (!payload.success) throw new Error(payload.error || '未知错误');
|
|
||||||
balanceData.value = payload.data || {};
|
|
||||||
balanceUpdatedAt.value = Date.now();
|
|
||||||
} catch (error) {
|
|
||||||
balanceError.value = error instanceof Error ? error.message : String(error);
|
|
||||||
} finally {
|
|
||||||
balanceLoading.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleVerifySecondary = async () => {
|
const handleVerifySecondary = async () => {
|
||||||
await verifySecondary(secondaryPassword.value);
|
await verifySecondary(secondaryPassword.value);
|
||||||
if (secondaryVerified.value) {
|
if (secondaryVerified.value) {
|
||||||
@ -819,15 +748,6 @@ watch(autoRefresh, () => {
|
|||||||
scheduleAutoRefresh();
|
scheduleAutoRefresh();
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
|
||||||
() => activeSection.value,
|
|
||||||
(val) => {
|
|
||||||
if (val === 'balance' && !balanceData.value && !balanceLoading.value) {
|
|
||||||
fetchBalance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
document.addEventListener('click', handleClickOutside);
|
document.addEventListener('click', handleClickOutside);
|
||||||
await checkSecondary();
|
await checkSecondary();
|
||||||
@ -891,36 +811,6 @@ const tokenBreakdown = computed(() => {
|
|||||||
return list.sort((a, b) => b.total - a.total);
|
return list.sort((a, b) => b.total - a.total);
|
||||||
});
|
});
|
||||||
|
|
||||||
const usdToCnyRate = computed(() => balanceData.value?.rate_usd_cny ?? null);
|
|
||||||
|
|
||||||
const balanceCards = computed(() => {
|
|
||||||
const makeCard = (key: string, label: string) => {
|
|
||||||
const data = (balanceData.value as any)?.[key] || {};
|
|
||||||
const success = data.success === true;
|
|
||||||
const currency = data.currency || '—';
|
|
||||||
const amount = data.available ?? data.total ?? null;
|
|
||||||
return {
|
|
||||||
key,
|
|
||||||
label,
|
|
||||||
success,
|
|
||||||
currency,
|
|
||||||
amount,
|
|
||||||
amountCny:
|
|
||||||
data.available_cny ??
|
|
||||||
(currency === 'USD' && usdToCnyRate.value && amount != null
|
|
||||||
? Math.round(amount * usdToCnyRate.value * 100) / 100
|
|
||||||
: null),
|
|
||||||
extras: data,
|
|
||||||
error: success ? null : data.error
|
|
||||||
};
|
|
||||||
};
|
|
||||||
return [
|
|
||||||
makeCard('kimi', 'Kimi'),
|
|
||||||
makeCard('deepseek', 'DeepSeek'),
|
|
||||||
makeCard('qwen', 'Qwen / Aliyun')
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
const metricCards = computed(() => [
|
const metricCards = computed(() => [
|
||||||
{
|
{
|
||||||
title: '注册用户',
|
title: '注册用户',
|
||||||
@ -1593,70 +1483,6 @@ th {
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.balance-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 12px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.balance-actions {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.balance-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
|
||||||
gap: 12px;
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.balance-card {
|
|
||||||
border: 1px solid rgba(118, 103, 84, 0.2);
|
|
||||||
border-radius: 14px;
|
|
||||||
padding: 12px;
|
|
||||||
background: rgba(255, 255, 255, 0.9);
|
|
||||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35);
|
|
||||||
}
|
|
||||||
|
|
||||||
.balance-card.error {
|
|
||||||
border-color: rgba(189, 93, 58, 0.4);
|
|
||||||
background: rgba(189, 93, 58, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.balance-card-head {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 8px;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.balance-amount {
|
|
||||||
display: flex;
|
|
||||||
align-items: baseline;
|
|
||||||
gap: 8px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.balance-amount .sub {
|
|
||||||
color: #6a5d4c;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.balance-meta {
|
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 4px;
|
|
||||||
color: #6a5d4c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.small {
|
.small {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -229,35 +229,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="panel">
|
|
||||||
<div class="balance-header">
|
|
||||||
<div>
|
|
||||||
<h3>余额查询</h3>
|
|
||||||
<p class="muted">Kimi / DeepSeek / Qwen</p>
|
|
||||||
</div>
|
|
||||||
<button type="button" :disabled="balanceLoading" @click="fetchBalance">
|
|
||||||
{{ balanceLoading ? '查询中...' : '刷新余额' }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="balance-grid">
|
|
||||||
<div
|
|
||||||
v-for="card in balanceCards"
|
|
||||||
:key="card.key"
|
|
||||||
class="balance-card"
|
|
||||||
:class="{ error: !card.success }"
|
|
||||||
>
|
|
||||||
<div class="balance-card-head">
|
|
||||||
<h4>{{ card.label }}</h4>
|
|
||||||
<span :class="['status-badge', card.success ? 'online' : 'danger']">
|
|
||||||
{{ card.success ? '正常' : '失败' }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<strong>{{ card.amount ?? '—' }} {{ card.currency }}</strong>
|
|
||||||
<span class="muted small" v-if="card.amountCny">≈ ¥{{ card.amountCny }}</span>
|
|
||||||
<p class="muted small" v-else-if="card.error">{{ card.error }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -287,9 +258,6 @@ const creating = ref(false);
|
|||||||
const createForm = ref({ username: '', note: '' });
|
const createForm = ref({ username: '', note: '' });
|
||||||
const createError = ref('');
|
const createError = ref('');
|
||||||
|
|
||||||
const balanceData = ref<Record<string, any> | null>(null);
|
|
||||||
const balanceLoading = ref(false);
|
|
||||||
|
|
||||||
const overview = computed(() => snapshot.value?.overview || {});
|
const overview = computed(() => snapshot.value?.overview || {});
|
||||||
const uploads = computed(() => snapshot.value?.uploads || {});
|
const uploads = computed(() => snapshot.value?.uploads || {});
|
||||||
const containerItems = computed(() => {
|
const containerItems = computed(() => {
|
||||||
@ -303,22 +271,6 @@ const totalRequests = computed(() =>
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const balanceCards = computed(() => {
|
|
||||||
const keys = ['kimi', 'deepseek', 'qwen'];
|
|
||||||
return keys.map((k) => {
|
|
||||||
const data = (balanceData.value as any)?.[k] || {};
|
|
||||||
return {
|
|
||||||
key: k,
|
|
||||||
label: k.toUpperCase(),
|
|
||||||
success: data.success === true,
|
|
||||||
amount: data.available ?? data.total ?? null,
|
|
||||||
amountCny: data.amount_cny ?? data.amountCny ?? null,
|
|
||||||
currency: data.currency || '',
|
|
||||||
error: data.error
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const formatNumber = (val: any) => {
|
const formatNumber = (val: any) => {
|
||||||
const num = Number(val || 0);
|
const num = Number(val || 0);
|
||||||
return Number.isFinite(num) ? num.toLocaleString('zh-CN') : '0';
|
return Number.isFinite(num) ? num.toLocaleString('zh-CN') : '0';
|
||||||
@ -501,21 +453,6 @@ const copyToken = async (username: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchBalance = async () => {
|
|
||||||
if (!secondaryVerified.value) return;
|
|
||||||
balanceLoading.value = true;
|
|
||||||
try {
|
|
||||||
const resp = await fetch('/api/admin/balance', { credentials: 'same-origin' });
|
|
||||||
const data = await resp.json();
|
|
||||||
if (!resp.ok || !data.success) throw new Error(data.error || '查询失败');
|
|
||||||
balanceData.value = data.data;
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
} finally {
|
|
||||||
balanceLoading.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleVerifySecondary = async () => {
|
const handleVerifySecondary = async () => {
|
||||||
await verifySecondary(secondaryPassword.value);
|
await verifySecondary(secondaryPassword.value);
|
||||||
if (secondaryVerified.value) {
|
if (secondaryVerified.value) {
|
||||||
@ -754,37 +691,6 @@ td {
|
|||||||
gap: 14px;
|
gap: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.balance-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 10px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.balance-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.balance-card {
|
|
||||||
background: rgba(255, 255, 255, 0.92);
|
|
||||||
border: 1px solid rgba(44, 32, 19, 0.12);
|
|
||||||
border-radius: 14px;
|
|
||||||
padding: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.balance-card.error {
|
|
||||||
border-color: rgba(181, 71, 61, 0.35);
|
|
||||||
}
|
|
||||||
|
|
||||||
.balance-card-head {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-text {
|
.error-text {
|
||||||
color: #b5473d;
|
color: #b5473d;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
|
|||||||
@ -62,17 +62,16 @@ function debugShowImageLog(event: string, payload: Record<string, any> = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isShowHtmlDebugEnabled() {
|
function isShowHtmlDebugEnabled() {
|
||||||
if (typeof window === 'undefined') return true;
|
if (typeof window === 'undefined') return false;
|
||||||
try {
|
try {
|
||||||
const explicitFlag = (window as any).__SHOW_HTML_DEBUG__;
|
const explicitFlag = (window as any).__SHOW_HTML_DEBUG__;
|
||||||
if (explicitFlag === false || explicitFlag === '0') return false;
|
|
||||||
if (explicitFlag === true || explicitFlag === '1') return true;
|
if (explicitFlag === true || explicitFlag === '1') return true;
|
||||||
|
if (explicitFlag === false || explicitFlag === '0') return false;
|
||||||
const localFlag = window.localStorage?.getItem('showHtmlDebug');
|
const localFlag = window.localStorage?.getItem('showHtmlDebug');
|
||||||
if (localFlag === '0' || localFlag === 'false') return false;
|
|
||||||
if (localFlag === '1' || localFlag === 'true') return true;
|
if (localFlag === '1' || localFlag === 'true') return true;
|
||||||
return true;
|
return false;
|
||||||
} catch {
|
} catch {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1042,7 +1041,8 @@ function getShowTagContainer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isLayoutDebugEnabled() {
|
function isLayoutDebugEnabled() {
|
||||||
if (typeof window === 'undefined') return true;
|
// 默认关闭:排障时通过 window.__LAYOUT_DEBUG__ = true 或 localStorage.layoutDebug = '1' 显式打开
|
||||||
|
if (typeof window === 'undefined') return false;
|
||||||
try {
|
try {
|
||||||
const explicitFlag = (window as any).__LAYOUT_DEBUG__;
|
const explicitFlag = (window as any).__LAYOUT_DEBUG__;
|
||||||
if (explicitFlag === false || explicitFlag === '0') return false;
|
if (explicitFlag === false || explicitFlag === '0') return false;
|
||||||
@ -1050,9 +1050,9 @@ function isLayoutDebugEnabled() {
|
|||||||
const localFlag = window.localStorage?.getItem('layoutDebug');
|
const localFlag = window.localStorage?.getItem('layoutDebug');
|
||||||
if (localFlag === '0' || localFlag === 'false') return false;
|
if (localFlag === '0' || localFlag === 'false') return false;
|
||||||
if (localFlag === '1' || localFlag === 'true') return true;
|
if (localFlag === '1' || localFlag === 'true') return true;
|
||||||
return true;
|
return false;
|
||||||
} catch {
|
} catch {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,7 +23,6 @@ export function created() {
|
|||||||
|
|
||||||
export async function mounted() {
|
export async function mounted() {
|
||||||
debugLog('Vue应用已挂载');
|
debugLog('Vue应用已挂载');
|
||||||
console.log('[DEBUG_SYSTEM][boot] 前端调试版本已加载 2026-04-05');
|
|
||||||
if (window.ensureCsrfToken) {
|
if (window.ensureCsrfToken) {
|
||||||
window.ensureCsrfToken().catch((err) => {
|
window.ensureCsrfToken().catch((err) => {
|
||||||
console.warn('CSRF token 初始化失败:', err);
|
console.warn('CSRF token 初始化失败:', err);
|
||||||
|
|||||||
@ -25,8 +25,6 @@ export const traceLog = (...args) => {
|
|||||||
function isGoalModeDebugEnabled() {
|
function isGoalModeDebugEnabled() {
|
||||||
if (typeof window === 'undefined') return false;
|
if (typeof window === 'undefined') return false;
|
||||||
try {
|
try {
|
||||||
// 临时强制开启,用于调试多智能体消息轮询问题
|
|
||||||
return true;
|
|
||||||
const explicit = (window as any).__GOAL_MODE_DEBUG__;
|
const explicit = (window as any).__GOAL_MODE_DEBUG__;
|
||||||
if (explicit === true || explicit === '1') return true;
|
if (explicit === true || explicit === '1') return true;
|
||||||
if (explicit === false || explicit === '0') return false;
|
if (explicit === false || explicit === '0') return false;
|
||||||
|
|||||||
@ -17,7 +17,6 @@ export const actionMethods = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async createNewConversation() {
|
async createNewConversation() {
|
||||||
console.log('[DEBUG_AWAITING] ===== createNewConversation =====');
|
|
||||||
if (this.compressionInProgress || this.compressing) {
|
if (this.compressionInProgress || this.compressing) {
|
||||||
const confirmed = await this.confirmAction({
|
const confirmed = await this.confirmAction({
|
||||||
title: '压缩进行中',
|
title: '压缩进行中',
|
||||||
|
|||||||
@ -7,7 +7,6 @@ import {
|
|||||||
|
|
||||||
export const stateMethods = {
|
export const stateMethods = {
|
||||||
resetAllStates(reason = 'unspecified', options: { preserveMonitorWindows?: boolean } = {}) {
|
resetAllStates(reason = 'unspecified', options: { preserveMonitorWindows?: boolean } = {}) {
|
||||||
console.log('[DEBUG_AWAITING] ===== resetAllStates =====', { reason });
|
|
||||||
|
|
||||||
// 如果正在等待子智能体完成,不重置任务状态
|
// 如果正在等待子智能体完成,不重置任务状态
|
||||||
if (this.waitingForSubAgent) {
|
if (this.waitingForSubAgent) {
|
||||||
@ -74,11 +73,6 @@ export const stateMethods = {
|
|||||||
generatingLabel: m.generatingLabel
|
generatingLabel: m.generatingLabel
|
||||||
}));
|
}));
|
||||||
|
|
||||||
console.log('[DEBUG_AWAITING] resetAllStates 清理完成', {
|
|
||||||
before: assistantMsgsBefore,
|
|
||||||
after: assistantMsgsAfter
|
|
||||||
});
|
|
||||||
|
|
||||||
// 清理Markdown缓存
|
// 清理Markdown缓存
|
||||||
if (this.markdownCache) {
|
if (this.markdownCache) {
|
||||||
this.markdownCache.clear();
|
this.markdownCache.clear();
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
import { debugLog } from './common';
|
import { debugLog } from './common';
|
||||||
import { useQuickDockStore } from '../../stores/quickDock';
|
import { useQuickDockStore } from '../../stores/quickDock';
|
||||||
const jsonDebug = (...args: any[]) => {
|
const jsonDebug = (...args: any[]) => {
|
||||||
console.log('[JSONDEBUG]', ...args);
|
|
||||||
};
|
};
|
||||||
const RESTORE_DEBUG_PREFIX = '[RESTORE_DEBUG]';
|
const RESTORE_DEBUG_PREFIX = '[RESTORE_DEBUG]';
|
||||||
const RESTORE_DEBUG_EVENTS = new Set([
|
const RESTORE_DEBUG_EVENTS = new Set([
|
||||||
@ -12,17 +11,16 @@ const RESTORE_DEBUG_EVENTS = new Set([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
function isRestoreDebugEnabled() {
|
function isRestoreDebugEnabled() {
|
||||||
if (typeof window === 'undefined') return true;
|
if (typeof window === 'undefined') return false;
|
||||||
try {
|
try {
|
||||||
const explicit = (window as any).__RESTORE_DEBUG__;
|
const explicit = (window as any).__RESTORE_DEBUG__;
|
||||||
if (explicit === false || explicit === '0') return false;
|
|
||||||
if (explicit === true || explicit === '1') return true;
|
if (explicit === true || explicit === '1') return true;
|
||||||
|
if (explicit === false || explicit === '0') return false;
|
||||||
const localFlag = window.localStorage?.getItem('restoreDebug');
|
const localFlag = window.localStorage?.getItem('restoreDebug');
|
||||||
if (localFlag === '0' || localFlag === 'false') return false;
|
|
||||||
if (localFlag === '1' || localFlag === 'true') return true;
|
if (localFlag === '1' || localFlag === 'true') return true;
|
||||||
return true;
|
return false;
|
||||||
} catch {
|
} catch {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -459,10 +457,6 @@ export const historyMethods = {
|
|||||||
if (message.role === 'system') {
|
if (message.role === 'system') {
|
||||||
const rawContent = message.content || '';
|
const rawContent = message.content || '';
|
||||||
const label = parseSystemNoticeLabel(rawContent);
|
const label = parseSystemNoticeLabel(rawContent);
|
||||||
console.log('[DEBUG_SYSTEM][history] 命中 role=system 历史消息', {
|
|
||||||
rawContent,
|
|
||||||
matchedDoneNotice: !!label
|
|
||||||
});
|
|
||||||
if (label) {
|
if (label) {
|
||||||
// 历史中的 system 通知转换为 assistant system action,避免被 role=system 过滤掉
|
// 历史中的 system 通知转换为 assistant system action,避免被 role=system 过滤掉
|
||||||
this.messages.push({
|
this.messages.push({
|
||||||
@ -485,9 +479,6 @@ export const historyMethods = {
|
|||||||
generatingLabel: ''
|
generatingLabel: ''
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.log('[DEBUG_SYSTEM][history] role=system 被历史加载拦截(非子智能体完成)', {
|
|
||||||
rawContent
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -97,7 +97,6 @@ export const sendMethods = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async sendMessage(options = {}) {
|
async sendMessage(options = {}) {
|
||||||
console.log('[DEBUG_AWAITING] ===== sendMessage =====');
|
|
||||||
const presetText = typeof options?.presetText === 'string' ? options.presetText : null;
|
const presetText = typeof options?.presetText === 'string' ? options.presetText : null;
|
||||||
const usePresetText = presetText !== null;
|
const usePresetText = presetText !== null;
|
||||||
|
|
||||||
@ -417,7 +416,6 @@ export const sendMethods = {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
async stopTask() {
|
async stopTask() {
|
||||||
console.log('[DEBUG_AWAITING] ===== stopTask =====');
|
|
||||||
if (this._stopTaskRunning) {
|
if (this._stopTaskRunning) {
|
||||||
goalModeDebugLog('stopTask:debounce-rejected', { stopRequested: this.stopRequested });
|
goalModeDebugLog('stopTask:debounce-rejected', { stopRequested: this.stopRequested });
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -21,22 +21,8 @@ import {
|
|||||||
export const aiStreamMethods = {
|
export const aiStreamMethods = {
|
||||||
handleAiMessageStart(data: any, eventIdx: number) {
|
handleAiMessageStart(data: any, eventIdx: number) {
|
||||||
const lastMessage = this.messages[this.messages.length - 1];
|
const lastMessage = this.messages[this.messages.length - 1];
|
||||||
console.log('[DEBUG_AWAITING] ===== handleAiMessageStart =====', {
|
|
||||||
eventIdx,
|
|
||||||
lastMessageRole: lastMessage?.role,
|
|
||||||
lastMessageAwaitingFirstContent: lastMessage?.awaitingFirstContent,
|
|
||||||
lastMessageGeneratingLabel: lastMessage?.generatingLabel
|
|
||||||
});
|
|
||||||
|
|
||||||
debugLog('[TaskPolling] AI消息开始, idx:', eventIdx);
|
debugLog('[TaskPolling] AI消息开始, idx:', eventIdx);
|
||||||
console.log('[AiMessageStart] 开始处理', {
|
|
||||||
eventIdx,
|
|
||||||
messagesLength: this.messages.length,
|
|
||||||
lastMessageRole: this.messages[this.messages.length - 1]?.role,
|
|
||||||
streamingMessage: this.streamingMessage,
|
|
||||||
taskInProgress: this.taskInProgress,
|
|
||||||
currentConversationId: this.currentConversationId
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.waitingForSubAgent) {
|
if (this.waitingForSubAgent) {
|
||||||
this.waitingForSubAgent = false;
|
this.waitingForSubAgent = false;
|
||||||
@ -45,13 +31,6 @@ export const aiStreamMethods = {
|
|||||||
// 检查是否已经有 assistant 消息
|
// 检查是否已经有 assistant 消息
|
||||||
const hasAssistantMessage = lastMessage && lastMessage.role === 'assistant';
|
const hasAssistantMessage = lastMessage && lastMessage.role === 'assistant';
|
||||||
|
|
||||||
console.log('[AiMessageStart] 最后一条消息状态', {
|
|
||||||
hasAssistantMessage,
|
|
||||||
lastMessageActionsLength: lastMessage?.actions?.length || 0,
|
|
||||||
lastMessageAwaitingFirstContent: lastMessage?.awaitingFirstContent,
|
|
||||||
lastMessageGeneratingLabel: lastMessage?.generatingLabel
|
|
||||||
});
|
|
||||||
|
|
||||||
// 判断是否是刷新恢复的情况:
|
// 判断是否是刷新恢复的情况:
|
||||||
// 1. 有assistant消息
|
// 1. 有assistant消息
|
||||||
// 2. 且最后一条 assistant 仍处于“未完成流式状态”(而不是普通已完成消息)
|
// 2. 且最后一条 assistant 仍处于“未完成流式状态”(而不是普通已完成消息)
|
||||||
@ -83,16 +62,7 @@ export const aiStreamMethods = {
|
|||||||
isRefreshRestore
|
isRefreshRestore
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('[AiMessageStart] 场景判断', {
|
|
||||||
isRefreshRestore,
|
|
||||||
streamingMessage: this.streamingMessage,
|
|
||||||
hasActions: !!(lastMessage?.actions && lastMessage.actions.length > 0),
|
|
||||||
awaitingFirstContent: lastMessage?.awaitingFirstContent,
|
|
||||||
taskInProgress: this.taskInProgress
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isRefreshRestore) {
|
if (isRefreshRestore) {
|
||||||
console.log('[AiMessageStart] 场景=刷新恢复,复用现有assistant消息');
|
|
||||||
debugLog('[TaskPolling] 刷新恢复场景,复用现有 assistant 消息');
|
debugLog('[TaskPolling] 刷新恢复场景,复用现有 assistant 消息');
|
||||||
// 只更新状态,不创建新消息
|
// 只更新状态,不创建新消息
|
||||||
this.taskInProgress = true;
|
this.taskInProgress = true;
|
||||||
@ -105,25 +75,18 @@ export const aiStreamMethods = {
|
|||||||
// 从头重建时,restoreTaskState 已根据事件流分析(hasAssistantContentEvent)
|
// 从头重建时,restoreTaskState 已根据事件流分析(hasAssistantContentEvent)
|
||||||
// 正确设置了 awaitingFirstContent,此处不覆盖。
|
// 正确设置了 awaitingFirstContent,此处不覆盖。
|
||||||
// actions 被清空不代表没有内容,事件重放会恢复。
|
// actions 被清空不代表没有内容,事件重放会恢复。
|
||||||
console.log('[AiMessageStart] 从头重建,保留 awaitingFirstContent:', lastMessage.awaitingFirstContent);
|
|
||||||
} else if (!hasContent) {
|
} else if (!hasContent) {
|
||||||
lastMessage.awaitingFirstContent = true;
|
lastMessage.awaitingFirstContent = true;
|
||||||
lastMessage.generatingLabel = lastMessage.generatingLabel || '思考中...';
|
lastMessage.generatingLabel = lastMessage.generatingLabel || '思考中...';
|
||||||
console.log('[AiMessageStart] 设置等待动画', {
|
|
||||||
awaitingFirstContent: true,
|
|
||||||
generatingLabel: lastMessage.generatingLabel
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
// 如果已有内容,确保等待动画不显示
|
// 如果已有内容,确保等待动画不显示
|
||||||
lastMessage.awaitingFirstContent = false;
|
lastMessage.awaitingFirstContent = false;
|
||||||
lastMessage.generatingLabel = '';
|
lastMessage.generatingLabel = '';
|
||||||
console.log('[AiMessageStart] 关闭等待动画(已有内容)');
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 其他情况:创建新的 assistant 消息
|
// 其他情况:创建新的 assistant 消息
|
||||||
console.log('[AiMessageStart] 场景=创建新消息');
|
|
||||||
debugLog('[TaskPolling] 创建新的 assistant 消息');
|
debugLog('[TaskPolling] 创建新的 assistant 消息');
|
||||||
this.monitorResetSpeech();
|
this.monitorResetSpeech();
|
||||||
this.cleanupStaleToolActions();
|
this.cleanupStaleToolActions();
|
||||||
@ -133,13 +96,6 @@ export const aiStreamMethods = {
|
|||||||
this.streamingMessage = true;
|
this.streamingMessage = true;
|
||||||
|
|
||||||
const newMessage = this.messages[this.messages.length - 1];
|
const newMessage = this.messages[this.messages.length - 1];
|
||||||
console.log('[AiMessageStart] 新消息创建完成', {
|
|
||||||
role: newMessage?.role,
|
|
||||||
awaitingFirstContent: newMessage?.awaitingFirstContent,
|
|
||||||
generatingLabel: newMessage?.generatingLabel,
|
|
||||||
actionsLength: newMessage?.actions?.length || 0,
|
|
||||||
messagesLength: this.messages.length
|
|
||||||
});
|
|
||||||
|
|
||||||
// 如果是从头重建,标记消息为静默恢复
|
// 如果是从头重建,标记消息为静默恢复
|
||||||
if (this._rebuildingFromScratch) {
|
if (this._rebuildingFromScratch) {
|
||||||
@ -147,33 +103,27 @@ export const aiStreamMethods = {
|
|||||||
debugLog('[TaskPolling] 标记消息为静默恢复(从头重建)');
|
debugLog('[TaskPolling] 标记消息为静默恢复(从头重建)');
|
||||||
newMessage.awaitingFirstContent = false;
|
newMessage.awaitingFirstContent = false;
|
||||||
newMessage.generatingLabel = '';
|
newMessage.generatingLabel = '';
|
||||||
console.log('[AiMessageStart] 从头重建,关闭等待动画');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 强制触发Vue响应式更新
|
// 强制触发Vue响应式更新
|
||||||
this.$forceUpdate();
|
this.$forceUpdate();
|
||||||
console.log('[AiMessageStart] 已调用$forceUpdate');
|
|
||||||
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
console.log('[AiMessageStart] nextTick回调执行');
|
|
||||||
this.conditionalScrollToBottom();
|
this.conditionalScrollToBottom();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleThinkingStart(data: any, eventIdx: number) {
|
handleThinkingStart(data: any, eventIdx: number) {
|
||||||
console.log('[ThinkingStart] 开始处理', { eventIdx });
|
|
||||||
debugLog('[TaskPolling] 思考开始, idx:', eventIdx);
|
debugLog('[TaskPolling] 思考开始, idx:', eventIdx);
|
||||||
const ignoreThinking = this.runMode === 'fast' || this.thinkingMode === false;
|
const ignoreThinking = this.runMode === 'fast' || this.thinkingMode === false;
|
||||||
if (ignoreThinking) {
|
if (ignoreThinking) {
|
||||||
this.monitorEndModelOutput();
|
this.monitorEndModelOutput();
|
||||||
console.log('[ThinkingStart] 忽略思考(fast模式或关闭思考)');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 防御性检查:如果没有assistant消息,先创建一个
|
// 防御性检查:如果没有assistant消息,先创建一个
|
||||||
const lastMessage = this.messages[this.messages.length - 1];
|
const lastMessage = this.messages[this.messages.length - 1];
|
||||||
if (!lastMessage || lastMessage.role !== 'assistant') {
|
if (!lastMessage || lastMessage.role !== 'assistant') {
|
||||||
console.log('[DEBUG_AWAITING] ThinkingStart 检测到缺少assistant消息,自动创建');
|
|
||||||
this.chatStartAssistantMessage();
|
this.chatStartAssistantMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,10 +138,6 @@ export const aiStreamMethods = {
|
|||||||
|
|
||||||
// 有内容了,关闭等待动画
|
// 有内容了,关闭等待动画
|
||||||
if (lastMessage && lastMessage.role === 'assistant') {
|
if (lastMessage && lastMessage.role === 'assistant') {
|
||||||
console.log('[ThinkingStart] 关闭等待动画', {
|
|
||||||
beforeAwaitingFirstContent: lastMessage.awaitingFirstContent,
|
|
||||||
beforeGeneratingLabel: lastMessage.generatingLabel
|
|
||||||
});
|
|
||||||
lastMessage.awaitingFirstContent = false;
|
lastMessage.awaitingFirstContent = false;
|
||||||
lastMessage.generatingLabel = '';
|
lastMessage.generatingLabel = '';
|
||||||
}
|
}
|
||||||
@ -267,13 +213,11 @@ export const aiStreamMethods = {
|
|||||||
this.monitorEndModelOutput();
|
this.monitorEndModelOutput();
|
||||||
},
|
},
|
||||||
handleTextStart() {
|
handleTextStart() {
|
||||||
console.log('[TextStart] 开始处理');
|
|
||||||
debugLog('[TaskPolling] 文本开始');
|
debugLog('[TaskPolling] 文本开始');
|
||||||
|
|
||||||
// 防御性检查:如果没有assistant消息,先创建一个
|
// 防御性检查:如果没有assistant消息,先创建一个
|
||||||
const lastMessage = this.messages[this.messages.length - 1];
|
const lastMessage = this.messages[this.messages.length - 1];
|
||||||
if (!lastMessage || lastMessage.role !== 'assistant') {
|
if (!lastMessage || lastMessage.role !== 'assistant') {
|
||||||
console.log('[DEBUG_AWAITING] TextStart 检测到缺少assistant消息,自动创建');
|
|
||||||
this.chatStartAssistantMessage();
|
this.chatStartAssistantMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,10 +226,6 @@ export const aiStreamMethods = {
|
|||||||
// 有内容了,关闭等待动画
|
// 有内容了,关闭等待动画
|
||||||
const currentMessage = this.messages[this.messages.length - 1];
|
const currentMessage = this.messages[this.messages.length - 1];
|
||||||
if (currentMessage && currentMessage.role === 'assistant') {
|
if (currentMessage && currentMessage.role === 'assistant') {
|
||||||
console.log('[TextStart] 关闭等待动画', {
|
|
||||||
beforeAwaitingFirstContent: currentMessage.awaitingFirstContent,
|
|
||||||
beforeGeneratingLabel: currentMessage.generatingLabel
|
|
||||||
});
|
|
||||||
currentMessage.awaitingFirstContent = false;
|
currentMessage.awaitingFirstContent = false;
|
||||||
currentMessage.generatingLabel = '';
|
currentMessage.generatingLabel = '';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -94,12 +94,6 @@ export const lifecycleMethods = {
|
|||||||
currentConversationId: this.currentConversationId
|
currentConversationId: this.currentConversationId
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
console.log(`[DEBUG_AWAITING] 忽略不匹配的事件`, {
|
|
||||||
eventType,
|
|
||||||
eventIdx,
|
|
||||||
eventConversationId: eventData.conversation_id,
|
|
||||||
currentConversationId: this.currentConversationId
|
|
||||||
});
|
|
||||||
debugLog(
|
debugLog(
|
||||||
`[TaskPolling] 忽略不匹配的事件 #${eventIdx}: ${eventType}, 事件对话=${eventData.conversation_id}, 当前对话=${this.currentConversationId}`
|
`[TaskPolling] 忽略不匹配的事件 #${eventIdx}: ${eventType}, 事件对话=${eventData.conversation_id}, 当前对话=${this.currentConversationId}`
|
||||||
);
|
);
|
||||||
@ -176,13 +170,6 @@ export const lifecycleMethods = {
|
|||||||
|
|
||||||
debugLog(`[TaskPolling] 处理事件 #${eventIdx}: ${eventType}`, eventData);
|
debugLog(`[TaskPolling] 处理事件 #${eventIdx}: ${eventType}`, eventData);
|
||||||
|
|
||||||
console.log(`[DEBUG_AWAITING] 处理事件`, {
|
|
||||||
eventType,
|
|
||||||
eventIdx,
|
|
||||||
eventConversationId: eventData.conversation_id,
|
|
||||||
currentConversationId: this.currentConversationId
|
|
||||||
});
|
|
||||||
|
|
||||||
// 根据事件类型调用对应的处理方法
|
// 根据事件类型调用对应的处理方法
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case 'ai_message_start':
|
case 'ai_message_start':
|
||||||
|
|||||||
@ -8,13 +8,10 @@ export const debugNotifyLog = (...args: any[]) => {
|
|||||||
void args;
|
void args;
|
||||||
};
|
};
|
||||||
export const keyNotifyLog = (...args: any[]) => {
|
export const keyNotifyLog = (...args: any[]) => {
|
||||||
console.log(...args);
|
|
||||||
};
|
};
|
||||||
export const jsonDebug = (...args: any[]) => {
|
export const jsonDebug = (...args: any[]) => {
|
||||||
console.log('[JSONDEBUG]', ...args);
|
|
||||||
};
|
};
|
||||||
export const userMDebug = (...args: any[]) => {
|
export const userMDebug = (...args: any[]) => {
|
||||||
console.log('[USERMDEBUG]', ...args);
|
|
||||||
};
|
};
|
||||||
export const RESTORE_DEBUG_PREFIX = '[RESTORE_DEBUG]';
|
export const RESTORE_DEBUG_PREFIX = '[RESTORE_DEBUG]';
|
||||||
export let restoreDebugCount = 0;
|
export let restoreDebugCount = 0;
|
||||||
@ -35,17 +32,16 @@ export const RESTORE_DEBUG_EVENTS = new Set([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
export function isRestoreDebugEnabled() {
|
export function isRestoreDebugEnabled() {
|
||||||
if (typeof window === 'undefined') return true;
|
if (typeof window === 'undefined') return false;
|
||||||
try {
|
try {
|
||||||
const explicit = (window as any).__RESTORE_DEBUG__;
|
const explicit = (window as any).__RESTORE_DEBUG__;
|
||||||
if (explicit === false || explicit === '0') return false;
|
|
||||||
if (explicit === true || explicit === '1') return true;
|
if (explicit === true || explicit === '1') return true;
|
||||||
|
if (explicit === false || explicit === '0') return false;
|
||||||
const localFlag = window.localStorage?.getItem('restoreDebug');
|
const localFlag = window.localStorage?.getItem('restoreDebug');
|
||||||
if (localFlag === '0' || localFlag === 'false') return false;
|
|
||||||
if (localFlag === '1' || localFlag === 'true') return true;
|
if (localFlag === '1' || localFlag === 'true') return true;
|
||||||
return true;
|
return false;
|
||||||
} catch {
|
} catch {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -21,25 +21,18 @@ import {
|
|||||||
|
|
||||||
export const toolMethods = {
|
export const toolMethods = {
|
||||||
handleToolPreparing(data: any) {
|
handleToolPreparing(data: any) {
|
||||||
console.log('[ToolPreparing] 开始处理', { name: data.name, id: data.id });
|
|
||||||
debugLog('[TaskPolling] 工具准备中:', data.name);
|
debugLog('[TaskPolling] 工具准备中:', data.name);
|
||||||
|
|
||||||
if (this.dropToolEvents) {
|
if (this.dropToolEvents) {
|
||||||
console.log('[ToolPreparing] dropToolEvents=true,跳过');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const msg = this.chatEnsureAssistantMessage();
|
const msg = this.chatEnsureAssistantMessage();
|
||||||
if (!msg) {
|
if (!msg) {
|
||||||
console.log('[ToolPreparing] 无法获取assistant消息');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg.awaitingFirstContent) {
|
if (msg.awaitingFirstContent) {
|
||||||
console.log('[ToolPreparing] 关闭等待动画', {
|
|
||||||
beforeAwaitingFirstContent: msg.awaitingFirstContent,
|
|
||||||
beforeGeneratingLabel: msg.generatingLabel
|
|
||||||
});
|
|
||||||
msg.awaitingFirstContent = false;
|
msg.awaitingFirstContent = false;
|
||||||
msg.generatingLabel = '';
|
msg.generatingLabel = '';
|
||||||
}
|
}
|
||||||
@ -105,10 +98,6 @@ export const toolMethods = {
|
|||||||
// 但从头重建等场景可能跳过 tool_preparing 直接收到 tool_start)
|
// 但从头重建等场景可能跳过 tool_preparing 直接收到 tool_start)
|
||||||
const msgForAwaiting = this.chatEnsureAssistantMessage();
|
const msgForAwaiting = this.chatEnsureAssistantMessage();
|
||||||
if (msgForAwaiting && msgForAwaiting.awaitingFirstContent) {
|
if (msgForAwaiting && msgForAwaiting.awaitingFirstContent) {
|
||||||
console.log('[ToolStart] 关闭等待动画', {
|
|
||||||
beforeAwaitingFirstContent: msgForAwaiting.awaitingFirstContent,
|
|
||||||
toolName: data.name
|
|
||||||
});
|
|
||||||
msgForAwaiting.awaitingFirstContent = false;
|
msgForAwaiting.awaitingFirstContent = false;
|
||||||
msgForAwaiting.generatingLabel = '';
|
msgForAwaiting.generatingLabel = '';
|
||||||
}
|
}
|
||||||
@ -294,7 +283,6 @@ export const toolMethods = {
|
|||||||
await personalizationStore.fetchPersonalization();
|
await personalizationStore.fetchPersonalization();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// 静默处理,不影响主流程
|
// 静默处理,不影响主流程
|
||||||
console.debug('[TaskPolling] 刷新个人空间数据失败:', e);
|
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -336,19 +336,10 @@ export const hostWorkspaceMethods = {
|
|||||||
},
|
},
|
||||||
async handleSelectWorkspaceConversation(payload: { conversationId: string; workspaceId: string }) {
|
async handleSelectWorkspaceConversation(payload: { conversationId: string; workspaceId: string }) {
|
||||||
const { conversationId, workspaceId } = payload || {};
|
const { conversationId, workspaceId } = payload || {};
|
||||||
console.log('[sidebar-debug] handleSelectWorkspaceConversation', {
|
|
||||||
conversationId,
|
|
||||||
workspaceId,
|
|
||||||
currentHostWorkspaceId: this.currentHostWorkspaceId,
|
|
||||||
versioningHostMode: this.versioningHostMode,
|
|
||||||
dockerProjectMode: this.dockerProjectMode
|
|
||||||
});
|
|
||||||
if (!conversationId || !workspaceId) return;
|
if (!conversationId || !workspaceId) return;
|
||||||
if ((this.versioningHostMode || this.dockerProjectMode) && workspaceId !== this.currentHostWorkspaceId) {
|
if ((this.versioningHostMode || this.dockerProjectMode) && workspaceId !== this.currentHostWorkspaceId) {
|
||||||
console.log('[sidebar-debug] switching workspace', { from: this.currentHostWorkspaceId, to: workspaceId });
|
|
||||||
await this.handleHostWorkspaceSwitch(workspaceId);
|
await this.handleHostWorkspaceSwitch(workspaceId);
|
||||||
}
|
}
|
||||||
console.log('[sidebar-debug] loading conversation', { conversationId, currentHostWorkspaceIdAfterSwitch: this.currentHostWorkspaceId });
|
|
||||||
await this.loadConversation(conversationId, { force: true, workspaceId });
|
await this.loadConversation(conversationId, { force: true, workspaceId });
|
||||||
},
|
},
|
||||||
async handleRenameWorkspaceFromSidebar(payload: { workspaceId: string; label: string }) {
|
async handleRenameWorkspaceFromSidebar(payload: { workspaceId: string; label: string }) {
|
||||||
|
|||||||
@ -67,23 +67,13 @@ export const mobileMethods = {
|
|||||||
this.uiToggleMobileOverlayMenu();
|
this.uiToggleMobileOverlayMenu();
|
||||||
},
|
},
|
||||||
openMobileOverlay(target) {
|
openMobileOverlay(target) {
|
||||||
console.log(
|
|
||||||
'[UI_DEBUG] openMobileOverlay called, target:',
|
|
||||||
target,
|
|
||||||
'isMobileViewport:',
|
|
||||||
this.isMobileViewport,
|
|
||||||
'activeMobileOverlay:',
|
|
||||||
this.activeMobileOverlay
|
|
||||||
);
|
|
||||||
if (!this.isMobileViewport) {
|
if (!this.isMobileViewport) {
|
||||||
console.log('[UI_DEBUG] openMobileOverlay: blocked - not mobile viewport');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (target === 'approval') {
|
if (target === 'approval') {
|
||||||
this.fetchPendingToolApprovals();
|
this.fetchPendingToolApprovals();
|
||||||
}
|
}
|
||||||
if (this.activeMobileOverlay === target) {
|
if (this.activeMobileOverlay === target) {
|
||||||
console.log('[UI_DEBUG] openMobileOverlay: target already active, closing');
|
|
||||||
this.closeMobileOverlay('same-target-click');
|
this.closeMobileOverlay('same-target-click');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -93,17 +83,10 @@ export const mobileMethods = {
|
|||||||
if (target === 'conversation') {
|
if (target === 'conversation') {
|
||||||
this.uiSetSidebarCollapsed(false);
|
this.uiSetSidebarCollapsed(false);
|
||||||
}
|
}
|
||||||
console.log('[UI_DEBUG] openMobileOverlay: opening target:', target);
|
|
||||||
this.uiSetActiveMobileOverlay(target);
|
this.uiSetActiveMobileOverlay(target);
|
||||||
this.uiSetMobileOverlayMenuOpen(false);
|
this.uiSetMobileOverlayMenuOpen(false);
|
||||||
},
|
},
|
||||||
closeMobileOverlay(source = 'unknown') {
|
closeMobileOverlay(source = 'unknown') {
|
||||||
console.log(
|
|
||||||
'[UI_DEBUG] closeMobileOverlay called from:',
|
|
||||||
source,
|
|
||||||
'activeMobileOverlay:',
|
|
||||||
this.activeMobileOverlay
|
|
||||||
);
|
|
||||||
if (!this.activeMobileOverlay) {
|
if (!this.activeMobileOverlay) {
|
||||||
this.uiCloseMobileOverlay();
|
this.uiCloseMobileOverlay();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -38,12 +38,6 @@ export const panelMethods = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
toggleApprovalPanel() {
|
toggleApprovalPanel() {
|
||||||
console.log(
|
|
||||||
'[UI_DEBUG] toggleApprovalPanel called, current rightCollapsed:',
|
|
||||||
this.rightCollapsed,
|
|
||||||
'new state:',
|
|
||||||
!this.rightCollapsed
|
|
||||||
);
|
|
||||||
this.rightCollapsed = !this.rightCollapsed;
|
this.rightCollapsed = !this.rightCollapsed;
|
||||||
if (!this.rightCollapsed && this.rightWidth < this.minPanelWidth) {
|
if (!this.rightCollapsed && this.rightWidth < this.minPanelWidth) {
|
||||||
this.rightWidth = this.minPanelWidth;
|
this.rightWidth = this.minPanelWidth;
|
||||||
@ -71,53 +65,28 @@ export const panelMethods = {
|
|||||||
this.toggleFocusPanel();
|
this.toggleFocusPanel();
|
||||||
},
|
},
|
||||||
handleApprovalPanelToggleClick() {
|
handleApprovalPanelToggleClick() {
|
||||||
console.log(
|
|
||||||
'[UI_DEBUG] handleApprovalPanelToggleClick called, isMobileViewport:',
|
|
||||||
this.isMobileViewport,
|
|
||||||
'currentConversationId:',
|
|
||||||
this.currentConversationId,
|
|
||||||
'activeMobileOverlay:',
|
|
||||||
this.activeMobileOverlay
|
|
||||||
);
|
|
||||||
if (!this.currentConversationId) {
|
if (!this.currentConversationId) {
|
||||||
console.log('[UI_DEBUG] handleApprovalPanelToggleClick: blocked - no conversation');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.isMobileViewport) {
|
if (this.isMobileViewport) {
|
||||||
console.log('[UI_DEBUG] handleApprovalPanelToggleClick: mobile path, toggling overlay');
|
|
||||||
// 手机端:获取审批列表(不自动关闭),然后切换遮罩
|
// 手机端:获取审批列表(不自动关闭),然后切换遮罩
|
||||||
this.fetchPendingToolApprovals();
|
this.fetchPendingToolApprovals();
|
||||||
this.openMobileOverlay('approval');
|
this.openMobileOverlay('approval');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log('[UI_DEBUG] handleApprovalPanelToggleClick: desktop path, toggling panel');
|
|
||||||
this.toggleApprovalPanel();
|
this.toggleApprovalPanel();
|
||||||
},
|
},
|
||||||
handleTokenPanelToggleClick(fromSettingsMenu = false) {
|
handleTokenPanelToggleClick(fromSettingsMenu = false) {
|
||||||
console.log(
|
|
||||||
'[UI_DEBUG] handleTokenPanelToggleClick called, fromSettingsMenu:',
|
|
||||||
fromSettingsMenu,
|
|
||||||
'isMobileViewport:',
|
|
||||||
this.isMobileViewport,
|
|
||||||
'tokenPanelCollapsed:',
|
|
||||||
this.tokenPanelCollapsed,
|
|
||||||
'currentConversationId:',
|
|
||||||
this.currentConversationId
|
|
||||||
);
|
|
||||||
if (!this.currentConversationId) {
|
if (!this.currentConversationId) {
|
||||||
console.log('[UI_DEBUG] handleTokenPanelToggleClick: blocked - no conversation');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.isPolicyBlocked('block_token_panel', '用量统计已被管理员禁用')) {
|
if (this.isPolicyBlocked('block_token_panel', '用量统计已被管理员禁用')) {
|
||||||
console.log('[UI_DEBUG] handleTokenPanelToggleClick: blocked - policy');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 移动端禁用“点击展开顶部用量面板”,仅允许在已展开时点击收起
|
// 移动端禁用“点击展开顶部用量面板”,仅允许在已展开时点击收起
|
||||||
if (this.isMobileViewport && this.tokenPanelCollapsed && !fromSettingsMenu) {
|
if (this.isMobileViewport && this.tokenPanelCollapsed && !fromSettingsMenu) {
|
||||||
console.log('[UI_DEBUG] handleTokenPanelToggleClick: blocked - mobile viewport restriction');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log('[UI_DEBUG] handleTokenPanelToggleClick: calling toggleTokenPanel');
|
|
||||||
this.toggleTokenPanel();
|
this.toggleTokenPanel();
|
||||||
},
|
},
|
||||||
getMessagesAreaElement() {
|
getMessagesAreaElement() {
|
||||||
|
|||||||
@ -20,14 +20,14 @@ import { debugLog } from '../common';
|
|||||||
export const SUB_AGENT_DONE_PREFIX_RE = /^(?:✅\s*)?子智能体\s*#?\s*(\d+)\s*任务摘要[::]/;
|
export const SUB_AGENT_DONE_PREFIX_RE = /^(?:✅\s*)?子智能体\s*#?\s*(\d+)\s*任务摘要[::]/;
|
||||||
export const BG_RUN_COMMAND_DONE_PREFIX_RE = /^\[后台\s*run_command\s*完成\]/;
|
export const BG_RUN_COMMAND_DONE_PREFIX_RE = /^\[后台\s*run_command\s*完成\]/;
|
||||||
export const userMDebug = (...args: any[]) => {
|
export const userMDebug = (...args: any[]) => {
|
||||||
console.log('[USERMDEBUG]', ...args);
|
|
||||||
};
|
};
|
||||||
export let uiBounceTraceCount = 0;
|
export let uiBounceTraceCount = 0;
|
||||||
export const UI_BOUNCE_TRACE_MAX = 140;
|
export const UI_BOUNCE_TRACE_MAX = 140;
|
||||||
export const uiBounceTraceLastTsByKey = new Map<string, number>();
|
export const uiBounceTraceLastTsByKey = new Map<string, number>();
|
||||||
|
|
||||||
export function isUiBounceTraceEnabled() {
|
export function isUiBounceTraceEnabled() {
|
||||||
if (typeof window === 'undefined') return true;
|
// 默认关闭:排障时通过 window.__SCROLL_BOUNCE_TRACE__ = true 或 localStorage.scrollBounceTrace = '1' 显式打开
|
||||||
|
if (typeof window === 'undefined') return false;
|
||||||
try {
|
try {
|
||||||
const explicit = (window as any).__SCROLL_BOUNCE_TRACE__;
|
const explicit = (window as any).__SCROLL_BOUNCE_TRACE__;
|
||||||
if (explicit === false || explicit === '0') return false;
|
if (explicit === false || explicit === '0') return false;
|
||||||
@ -35,9 +35,9 @@ export function isUiBounceTraceEnabled() {
|
|||||||
const localFlag = window.localStorage?.getItem('scrollBounceTrace');
|
const localFlag = window.localStorage?.getItem('scrollBounceTrace');
|
||||||
if (localFlag === '0' || localFlag === 'false') return false;
|
if (localFlag === '0' || localFlag === 'false') return false;
|
||||||
if (localFlag === '1' || localFlag === 'true') return true;
|
if (localFlag === '1' || localFlag === 'true') return true;
|
||||||
return true;
|
return false;
|
||||||
} catch {
|
} catch {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +62,8 @@ export function uiBounceTrace(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function isConnectionDiagEnabled() {
|
export function isConnectionDiagEnabled() {
|
||||||
if (typeof window === 'undefined') return true;
|
// 默认关闭:排障时通过 window.__CONN_DIAG__ = true 或 localStorage.connDiag = '1' 显式打开
|
||||||
|
if (typeof window === 'undefined') return false;
|
||||||
try {
|
try {
|
||||||
const explicit = (window as any).__CONN_DIAG__;
|
const explicit = (window as any).__CONN_DIAG__;
|
||||||
if (explicit === false || explicit === '0') return false;
|
if (explicit === false || explicit === '0') return false;
|
||||||
@ -70,10 +71,9 @@ export function isConnectionDiagEnabled() {
|
|||||||
const localFlag = window.localStorage?.getItem('connDiag');
|
const localFlag = window.localStorage?.getItem('connDiag');
|
||||||
if (localFlag === '0' || localFlag === 'false') return false;
|
if (localFlag === '0' || localFlag === 'false') return false;
|
||||||
if (localFlag === '1' || localFlag === 'true') return true;
|
if (localFlag === '1' || localFlag === 'true') return true;
|
||||||
// 默认常开:无需手动启动
|
return false;
|
||||||
return true;
|
|
||||||
} catch {
|
} catch {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,18 +118,10 @@ export function connectionDiag(
|
|||||||
export function parseSubAgentDoneLabel(rawContent: any): string | null {
|
export function parseSubAgentDoneLabel(rawContent: any): string | null {
|
||||||
const content = (rawContent || '').toString().trim();
|
const content = (rawContent || '').toString().trim();
|
||||||
if (!content) {
|
if (!content) {
|
||||||
console.log('[DEBUG_SYSTEM][ui] parseSubAgentDoneLabel: 空内容');
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const match = content.match(SUB_AGENT_DONE_PREFIX_RE);
|
const match = content.match(SUB_AGENT_DONE_PREFIX_RE);
|
||||||
const isCompleted = /已完成/.test(content);
|
const isCompleted = /已完成/.test(content);
|
||||||
console.log('[DEBUG_SYSTEM][ui] parseSubAgentDoneLabel', {
|
|
||||||
content,
|
|
||||||
regex: SUB_AGENT_DONE_PREFIX_RE.toString(),
|
|
||||||
matched: !!match,
|
|
||||||
matchAgentId: match?.[1],
|
|
||||||
isCompleted
|
|
||||||
});
|
|
||||||
if (!match || !isCompleted) {
|
if (!match || !isCompleted) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -91,8 +91,7 @@ export const socketMethods = {
|
|||||||
streamingMessage: !!this.streamingMessage,
|
streamingMessage: !!this.streamingMessage,
|
||||||
visibility: document?.visibilityState || 'unknown',
|
visibility: document?.visibilityState || 'unknown',
|
||||||
online: typeof navigator !== 'undefined' ? navigator.onLine : null
|
online: typeof navigator !== 'undefined' ? navigator.onLine : null
|
||||||
},
|
}
|
||||||
{ force: true }
|
|
||||||
);
|
);
|
||||||
} else if (
|
} else if (
|
||||||
isConnectionDiagEnabled() &&
|
isConnectionDiagEnabled() &&
|
||||||
@ -177,8 +176,7 @@ export const socketMethods = {
|
|||||||
{
|
{
|
||||||
connectedIntervalMs: this.connectionHeartbeatIntervalMs,
|
connectedIntervalMs: this.connectionHeartbeatIntervalMs,
|
||||||
disconnectedIntervalMs: this.connectionHeartbeatDisconnectedIntervalMs
|
disconnectedIntervalMs: this.connectionHeartbeatDisconnectedIntervalMs
|
||||||
},
|
}
|
||||||
{ force: true }
|
|
||||||
);
|
);
|
||||||
const runHeartbeat = async () => {
|
const runHeartbeat = async () => {
|
||||||
if (!this.connectionHeartbeatActive) {
|
if (!this.connectionHeartbeatActive) {
|
||||||
@ -224,7 +222,7 @@ export const socketMethods = {
|
|||||||
clearTimeout(this.connectionHeartbeatTimer);
|
clearTimeout(this.connectionHeartbeatTimer);
|
||||||
this.connectionHeartbeatTimer = null;
|
this.connectionHeartbeatTimer = null;
|
||||||
}
|
}
|
||||||
connectionDiag('log', 'heartbeat-stop', {}, { force: true });
|
connectionDiag('log', 'heartbeat-stop', {});
|
||||||
},
|
},
|
||||||
async loadInitialData() {
|
async loadInitialData() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -30,11 +30,6 @@ export const tutorialMethods = {
|
|||||||
try {
|
try {
|
||||||
const resp = await fetch('/api/tutorial-status', { credentials: 'same-origin' });
|
const resp = await fetch('/api/tutorial-status', { credentials: 'same-origin' });
|
||||||
const data = await resp.json().catch(() => ({}));
|
const data = await resp.json().catch(() => ({}));
|
||||||
console.info('[tutorial-prompt] status response:', {
|
|
||||||
ok: resp.ok,
|
|
||||||
status: resp.status,
|
|
||||||
data
|
|
||||||
});
|
|
||||||
if (!resp.ok || !data?.success) {
|
if (!resp.ok || !data?.success) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -159,17 +159,13 @@ export const versioningMethods = {
|
|||||||
throw new Error(`详情响应解析失败: ${parseErr?.message || '未知错误'}`);
|
throw new Error(`详情响应解析失败: ${parseErr?.message || '未知错误'}`);
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log('[VersioningDetail] raw response:', data);
|
|
||||||
if (!resp.ok || !data?.success) {
|
if (!resp.ok || !data?.success) {
|
||||||
throw new Error(data?.error || '加载详情失败');
|
throw new Error(data?.error || '加载详情失败');
|
||||||
}
|
}
|
||||||
this.versioningSelectedDetail = data.data || null;
|
this.versioningSelectedDetail = data.data || null;
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log('[VersioningDetail] detail set:', this.versioningSelectedDetail);
|
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log('[VersioningDetail] files:', this.versioningSelectedDetail?.files);
|
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log('[VersioningDetail] first file patch_lines:', this.versioningSelectedDetail?.files?.[0]?.patch_lines);
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.error('[VersioningDetail] error:', error);
|
console.error('[VersioningDetail] error:', error);
|
||||||
|
|||||||
@ -147,7 +147,6 @@ onMounted(async () => {
|
|||||||
try {
|
try {
|
||||||
const resp = await fetch('/api/session-status', { credentials: 'same-origin' });
|
const resp = await fetch('/api/session-status', { credentials: 'same-origin' });
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
console.info('[auth-debug] login page session-status:', data);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn('[auth-debug] login page session-status failed:', err);
|
console.warn('[auth-debug] login page session-status failed:', err);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -125,7 +125,6 @@ onMounted(async () => {
|
|||||||
try {
|
try {
|
||||||
const resp = await fetch('/api/session-status', { credentials: 'same-origin' });
|
const resp = await fetch('/api/session-status', { credentials: 'same-origin' });
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
console.info('[auth-debug] register page session-status:', data);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn('[auth-debug] register page session-status failed:', err);
|
console.warn('[auth-debug] register page session-status failed:', err);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -916,7 +916,6 @@ const copyUserMessage = async (msg: any, index: number) => {
|
|||||||
const branchUserMessage = (_msg: any) => {
|
const branchUserMessage = (_msg: any) => {
|
||||||
// TODO: 分支功能占位
|
// TODO: 分支功能占位
|
||||||
void _msg;
|
void _msg;
|
||||||
console.log('[ChatArea] 分支功能待实现');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const observeUserBubbles = () => {
|
const observeUserBubbles = () => {
|
||||||
@ -1083,10 +1082,6 @@ const filteredMessages = computed(() => {
|
|||||||
const source = props.messages || [];
|
const source = props.messages || [];
|
||||||
const droppedRoleSystem = source.filter((m) => m && m.role === 'system');
|
const droppedRoleSystem = source.filter((m) => m && m.role === 'system');
|
||||||
if (droppedRoleSystem.length > 0) {
|
if (droppedRoleSystem.length > 0) {
|
||||||
console.log('[DEBUG_SYSTEM][render] filteredMessages 过滤了 role=system 消息', {
|
|
||||||
count: droppedRoleSystem.length,
|
|
||||||
samples: droppedRoleSystem.slice(0, 3)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
const result = source.filter((m) => {
|
const result = source.filter((m) => {
|
||||||
if (m && m.metadata && m.metadata.system_injected_image) {
|
if (m && m.metadata && m.metadata.system_injected_image) {
|
||||||
@ -1762,7 +1757,6 @@ function getSubAgentSystemNoticeLabel(action: any): string | null {
|
|||||||
const key = action?.id || `no-id-${content.slice(0, 32)}`;
|
const key = action?.id || `no-id-${content.slice(0, 32)}`;
|
||||||
if (!content) {
|
if (!content) {
|
||||||
if (!debugLoggedSystemActionKeys.has(`${key}-empty`)) {
|
if (!debugLoggedSystemActionKeys.has(`${key}-empty`)) {
|
||||||
console.log('[DEBUG_SYSTEM][render] system action 内容为空', { action });
|
|
||||||
debugLoggedSystemActionKeys.add(`${key}-empty`);
|
debugLoggedSystemActionKeys.add(`${key}-empty`);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -1773,7 +1767,6 @@ function getSubAgentSystemNoticeLabel(action: any): string | null {
|
|||||||
BG_RUN_COMMAND_DONE_LABEL_RE.test(content))
|
BG_RUN_COMMAND_DONE_LABEL_RE.test(content))
|
||||||
) {
|
) {
|
||||||
if (!debugLoggedSystemActionKeys.has(`${key}-variant-pass`)) {
|
if (!debugLoggedSystemActionKeys.has(`${key}-variant-pass`)) {
|
||||||
console.log('[DEBUG_SYSTEM][render] 命中 variant 渲染', { action, label: content });
|
|
||||||
debugLoggedSystemActionKeys.add(`${key}-variant-pass`);
|
debugLoggedSystemActionKeys.add(`${key}-variant-pass`);
|
||||||
}
|
}
|
||||||
return content;
|
return content;
|
||||||
@ -1781,20 +1774,11 @@ function getSubAgentSystemNoticeLabel(action: any): string | null {
|
|||||||
const m = content.match(SUB_AGENT_DONE_PREFIX_RE);
|
const m = content.match(SUB_AGENT_DONE_PREFIX_RE);
|
||||||
if (m && /已完成/.test(content)) {
|
if (m && /已完成/.test(content)) {
|
||||||
if (!debugLoggedSystemActionKeys.has(`${key}-regex-pass`)) {
|
if (!debugLoggedSystemActionKeys.has(`${key}-regex-pass`)) {
|
||||||
console.log('[DEBUG_SYSTEM][render] 命中 regex 渲染', {
|
|
||||||
action,
|
|
||||||
normalized: `子智能体${m[1]} 任务完成`
|
|
||||||
});
|
|
||||||
debugLoggedSystemActionKeys.add(`${key}-regex-pass`);
|
debugLoggedSystemActionKeys.add(`${key}-regex-pass`);
|
||||||
}
|
}
|
||||||
return `子智能体${m[1]} 任务完成`;
|
return `子智能体${m[1]} 任务完成`;
|
||||||
}
|
}
|
||||||
if (!debugLoggedSystemActionKeys.has(`${key}-blocked`)) {
|
if (!debugLoggedSystemActionKeys.has(`${key}-blocked`)) {
|
||||||
console.log('[DEBUG_SYSTEM][render] system action 被渲染层拦截', {
|
|
||||||
action,
|
|
||||||
regex: SUB_AGENT_DONE_PREFIX_RE.toString(),
|
|
||||||
completed: /已完成/.test(content)
|
|
||||||
});
|
|
||||||
debugLoggedSystemActionKeys.add(`${key}-blocked`);
|
debugLoggedSystemActionKeys.add(`${key}-blocked`);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -1808,10 +1792,6 @@ function isActionVisible(action: any): boolean {
|
|||||||
if (!RENDERABLE_ACTION_TYPES.has(actionType)) {
|
if (!RENDERABLE_ACTION_TYPES.has(actionType)) {
|
||||||
const key = action?.id || `unknown-${actionType || 'none'}`;
|
const key = action?.id || `unknown-${actionType || 'none'}`;
|
||||||
if (!debugLoggedUnknownActionKeys.has(key)) {
|
if (!debugLoggedUnknownActionKeys.has(key)) {
|
||||||
console.log('[DEBUG_SYSTEM][render] 未知 action.type,已跳过渲染', {
|
|
||||||
actionType,
|
|
||||||
action
|
|
||||||
});
|
|
||||||
debugLoggedUnknownActionKeys.add(key);
|
debugLoggedUnknownActionKeys.add(key);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -2099,11 +2079,6 @@ const splitActionGroups = (actions: any[] = [], messageIndex = 0) => {
|
|||||||
|
|
||||||
// 调试:记录输入
|
// 调试:记录输入
|
||||||
if (CHAT_DEBUG_LOGS && actions.length > 0) {
|
if (CHAT_DEBUG_LOGS && actions.length > 0) {
|
||||||
console.log(`[splitActionGroups] 消息 ${messageIndex}:`, {
|
|
||||||
totalActions: actions.length,
|
|
||||||
actionTypes: actions.map((a) => a.type),
|
|
||||||
stackedBlocksEnabled: props.stackedBlocksEnabled
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const flushBuffer = () => {
|
const flushBuffer = () => {
|
||||||
@ -2140,14 +2115,6 @@ const splitActionGroups = (actions: any[] = [], messageIndex = 0) => {
|
|||||||
|
|
||||||
// 调试:记录输出
|
// 调试:记录输出
|
||||||
if (CHAT_DEBUG_LOGS && actions.length > 0) {
|
if (CHAT_DEBUG_LOGS && actions.length > 0) {
|
||||||
console.log(`[splitActionGroups] 消息 ${messageIndex} 结果:`, {
|
|
||||||
totalGroups: result.length,
|
|
||||||
groups: result.map((g) => ({
|
|
||||||
kind: g.kind,
|
|
||||||
actionsCount: g.kind === 'stack' ? g.actions.length : 1,
|
|
||||||
actionType: g.kind === 'stack' ? g.actions[0]?.type : g.action?.type
|
|
||||||
}))
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@ -98,7 +98,7 @@ const RENAME_TYPE_INTERVAL = 32;
|
|||||||
const MONITOR_EDITOR_DEBUG = false;
|
const MONITOR_EDITOR_DEBUG = false;
|
||||||
|
|
||||||
const MONITOR_READER_DEBUG = false;
|
const MONITOR_READER_DEBUG = false;
|
||||||
const MONITOR_RENAME_DEBUG = true;
|
const MONITOR_RENAME_DEBUG = false;
|
||||||
const readerDebug = (...args: any[]) => {
|
const readerDebug = (...args: any[]) => {
|
||||||
if (!MONITOR_READER_DEBUG) {
|
if (!MONITOR_READER_DEBUG) {
|
||||||
return;
|
return;
|
||||||
@ -117,14 +117,14 @@ const editorDebug = (...args: any[]) => {
|
|||||||
}
|
}
|
||||||
console.log('[MonitorEditor]', ...args);
|
console.log('[MonitorEditor]', ...args);
|
||||||
};
|
};
|
||||||
const MONITOR_PROGRESS_DEBUG = true;
|
const MONITOR_PROGRESS_DEBUG = false;
|
||||||
const progressDebug = (...args: any[]) => {
|
const progressDebug = (...args: any[]) => {
|
||||||
if (!MONITOR_PROGRESS_DEBUG) {
|
if (!MONITOR_PROGRESS_DEBUG) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.debug('[MonitorProgress]', ...args);
|
console.debug('[MonitorProgress]', ...args);
|
||||||
};
|
};
|
||||||
const MONITOR_LIFECYCLE_DEBUG = true;
|
const MONITOR_LIFECYCLE_DEBUG = false;
|
||||||
const monitorLifecycleDebug = (...args: any[]) => {
|
const monitorLifecycleDebug = (...args: any[]) => {
|
||||||
if (!MONITOR_LIFECYCLE_DEBUG) {
|
if (!MONITOR_LIFECYCLE_DEBUG) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -62,7 +62,6 @@ const filePadding = computed(() => ({
|
|||||||
|
|
||||||
function toggle() {
|
function toggle() {
|
||||||
if (props.node.type === 'folder') {
|
if (props.node.type === 'folder') {
|
||||||
console.log('[FileTree] click folder header', props.node.path);
|
|
||||||
fileStore.toggleFolder(props.node.path);
|
fileStore.toggleFolder(props.node.path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1911,7 +1911,6 @@ const voiceButtonTitle = computed(() => {
|
|||||||
const setupVoiceBridgeCallbacks = () => {
|
const setupVoiceBridgeCallbacks = () => {
|
||||||
if (typeof window === 'undefined') return;
|
if (typeof window === 'undefined') return;
|
||||||
(window as any).__onVoiceResult = (text: string) => {
|
(window as any).__onVoiceResult = (text: string) => {
|
||||||
console.log('[VoiceInput] Android 识别结果:', text);
|
|
||||||
const bridge = (window as any).AndroidVoiceBridge;
|
const bridge = (window as any).AndroidVoiceBridge;
|
||||||
bridge?.debugLog('__onVoiceResult 被调用, text=' + JSON.stringify(text));
|
bridge?.debugLog('__onVoiceResult 被调用, text=' + JSON.stringify(text));
|
||||||
if (!text) {
|
if (!text) {
|
||||||
@ -1959,7 +1958,6 @@ const setupVoiceBridgeCallbacks = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
(window as any).__onVoiceStatus = (status: string) => {
|
(window as any).__onVoiceStatus = (status: string) => {
|
||||||
console.log('[VoiceInput] 状态:', status);
|
|
||||||
if (status === 'listening' || status === 'processing' || status === 'initializing') {
|
if (status === 'listening' || status === 'processing' || status === 'initializing') {
|
||||||
// initializing 也视为录音中,防止用户重复点击触发多次 startListening
|
// initializing 也视为录音中,防止用户重复点击触发多次 startListening
|
||||||
isRecording.value = true;
|
isRecording.value = true;
|
||||||
@ -2008,23 +2006,19 @@ const isWebSpeechSupported = computed(() => {
|
|||||||
if (typeof window === 'undefined') return false;
|
if (typeof window === 'undefined') return false;
|
||||||
const hasSR = 'SpeechRecognition' in window;
|
const hasSR = 'SpeechRecognition' in window;
|
||||||
const hasWebkit = 'webkitSpeechRecognition' in window;
|
const hasWebkit = 'webkitSpeechRecognition' in window;
|
||||||
console.log('[VoiceInput] 检测支持:', { hasSR, hasWebkit, supported: hasSR || hasWebkit });
|
|
||||||
return hasSR || hasWebkit;
|
return hasSR || hasWebkit;
|
||||||
});
|
});
|
||||||
|
|
||||||
const getSpeechRecognitionConstructor = (): typeof SpeechRecognition | null => {
|
const getSpeechRecognitionConstructor = (): typeof SpeechRecognition | null => {
|
||||||
if (typeof window === 'undefined') return null;
|
if (typeof window === 'undefined') return null;
|
||||||
const Ctor = (window as any).SpeechRecognition || (window as any).webkitSpeechRecognition || null;
|
const Ctor = (window as any).SpeechRecognition || (window as any).webkitSpeechRecognition || null;
|
||||||
console.log('[VoiceInput] 构造函数:', Ctor ? Ctor.name || 'webkitSpeechRecognition' : 'null');
|
|
||||||
return Ctor;
|
return Ctor;
|
||||||
};
|
};
|
||||||
|
|
||||||
const stopVoiceRecording = () => {
|
const stopVoiceRecording = () => {
|
||||||
console.log('[VoiceInput] stopVoiceRecording called, isRecording:', isRecording.value);
|
|
||||||
if (recognitionInstance.value) {
|
if (recognitionInstance.value) {
|
||||||
try {
|
try {
|
||||||
recognitionInstance.value.stop();
|
recognitionInstance.value.stop();
|
||||||
console.log('[VoiceInput] recognition.stop() called');
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('[VoiceInput] recognition.stop() error:', e);
|
console.warn('[VoiceInput] recognition.stop() error:', e);
|
||||||
}
|
}
|
||||||
@ -2053,27 +2047,15 @@ const initSpeechRecognition = () => {
|
|||||||
voiceAnchorPos = 0;
|
voiceAnchorPos = 0;
|
||||||
}
|
}
|
||||||
lastVoiceTextLength = 0;
|
lastVoiceTextLength = 0;
|
||||||
console.log('[VoiceInput] init: anchorPos=', voiceAnchorPos);
|
|
||||||
|
|
||||||
recognition.onresult = (event: SpeechRecognitionEvent) => {
|
recognition.onresult = (event: SpeechRecognitionEvent) => {
|
||||||
console.log('[VoiceInput] onresult fired:', {
|
|
||||||
resultLength: event.results.length,
|
|
||||||
resultIndex: (event as any).resultIndex
|
|
||||||
});
|
|
||||||
let fullText = '';
|
let fullText = '';
|
||||||
for (let i = 0; i < event.results.length; i++) {
|
for (let i = 0; i < event.results.length; i++) {
|
||||||
const r = event.results[i];
|
const r = event.results[i];
|
||||||
console.log(`[VoiceInput] result[${i}]:`, {
|
|
||||||
transcript: r[0]?.transcript,
|
|
||||||
isFinal: r.isFinal,
|
|
||||||
confidence: r[0]?.confidence
|
|
||||||
});
|
|
||||||
fullText += r[0].transcript;
|
fullText += r[0].transcript;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[VoiceInput] fullText:', JSON.stringify(fullText));
|
|
||||||
if (!fullText) {
|
if (!fullText) {
|
||||||
console.log('[VoiceInput] fullText empty, skip');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2102,7 +2084,6 @@ const initSpeechRecognition = () => {
|
|||||||
view.focus();
|
view.focus();
|
||||||
|
|
||||||
lastVoiceTextLength = fullText.length;
|
lastVoiceTextLength = fullText.length;
|
||||||
console.log('[VoiceInput] inserted ok, lastVoiceTextLength=', lastVoiceTextLength);
|
|
||||||
|
|
||||||
emit('update:input-message', getEditorPlainText());
|
emit('update:input-message', getEditorPlainText());
|
||||||
emit('input-change');
|
emit('input-change');
|
||||||
@ -2114,7 +2095,6 @@ const initSpeechRecognition = () => {
|
|||||||
recognition.onerror = (event: SpeechRecognitionErrorEvent) => {
|
recognition.onerror = (event: SpeechRecognitionErrorEvent) => {
|
||||||
console.warn('[VoiceInput] onerror:', { error: event.error, message: (event as any).message });
|
console.warn('[VoiceInput] onerror:', { error: event.error, message: (event as any).message });
|
||||||
if (event.error === 'aborted') {
|
if (event.error === 'aborted') {
|
||||||
console.log('[VoiceInput] aborted (user stop), ignore');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// no-speech / network / not-allowed / service-not-allowed 等全部打印
|
// no-speech / network / not-allowed / service-not-allowed 等全部打印
|
||||||
@ -2123,35 +2103,27 @@ const initSpeechRecognition = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
recognition.onstart = () => {
|
recognition.onstart = () => {
|
||||||
console.log('[VoiceInput] onstart: 录音已开始');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
recognition.onaudiostart = () => {
|
recognition.onaudiostart = () => {
|
||||||
console.log('[VoiceInput] onaudiostart: 音频捕获开始');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
recognition.onsoundstart = () => {
|
recognition.onsoundstart = () => {
|
||||||
console.log('[VoiceInput] onsoundstart: 检测到声音');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
recognition.onspeechstart = () => {
|
recognition.onspeechstart = () => {
|
||||||
console.log('[VoiceInput] onspeechstart: 检测到语音');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
recognition.onspeechend = () => {
|
recognition.onspeechend = () => {
|
||||||
console.log('[VoiceInput] onspeechend: 语音结束');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
recognition.onsoundend = () => {
|
recognition.onsoundend = () => {
|
||||||
console.log('[VoiceInput] onsoundend: 声音结束');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
recognition.onaudioend = () => {
|
recognition.onaudioend = () => {
|
||||||
console.log('[VoiceInput] onaudioend: 音频捕获结束');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
recognition.onend = () => {
|
recognition.onend = () => {
|
||||||
console.log('[VoiceInput] onend: 录音结束, isRecording=', isRecording.value);
|
|
||||||
isRecording.value = false;
|
isRecording.value = false;
|
||||||
recognitionInstance.value = null;
|
recognitionInstance.value = null;
|
||||||
voiceAnchorPos = 0;
|
voiceAnchorPos = 0;
|
||||||
@ -2162,7 +2134,6 @@ const initSpeechRecognition = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const toggleVoiceRecording = () => {
|
const toggleVoiceRecording = () => {
|
||||||
console.log('[VoiceInput] toggleVoiceRecording, isRecording=', isRecording.value);
|
|
||||||
|
|
||||||
// Android Bridge 模式
|
// Android Bridge 模式
|
||||||
const bridge = (window as any).AndroidVoiceBridge;
|
const bridge = (window as any).AndroidVoiceBridge;
|
||||||
@ -2193,7 +2164,6 @@ const toggleVoiceRecording = () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
recognition.start();
|
recognition.start();
|
||||||
console.log('[VoiceInput] recognition.start() called');
|
|
||||||
recognitionInstance.value = recognition;
|
recognitionInstance.value = recognition;
|
||||||
isRecording.value = true;
|
isRecording.value = true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -208,7 +208,6 @@ function appendToTerm(data: string) {
|
|||||||
function renderSessionLog() {
|
function renderSessionLog() {
|
||||||
if (!term || !activeSession.value) return;
|
if (!term || !activeSession.value) return;
|
||||||
const log = sessionLogs.value[activeSession.value] || '';
|
const log = sessionLogs.value[activeSession.value] || '';
|
||||||
console.log('[TerminalPanel] renderSessionLog:', activeSession.value, 'len:', log.length, 'ready:', _historyReady.value[activeSession.value], 'hydrated:', sessionHydrated.value[activeSession.value]);
|
|
||||||
term.clear();
|
term.clear();
|
||||||
if (log) {
|
if (log) {
|
||||||
term.write(log, () => {
|
term.write(log, () => {
|
||||||
@ -218,7 +217,6 @@ function renderSessionLog() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function switchToSession(name: string) {
|
function switchToSession(name: string) {
|
||||||
console.log('[TerminalPanel] switchToSession:', name);
|
|
||||||
if (!name) return;
|
if (!name) return;
|
||||||
activeSession.value = name;
|
activeSession.value = name;
|
||||||
if (!sessionHydrated.value[name]) {
|
if (!sessionHydrated.value[name]) {
|
||||||
@ -227,7 +225,6 @@ function switchToSession(name: string) {
|
|||||||
delete updatedTimes[name];
|
delete updatedTimes[name];
|
||||||
_historyLastEventTime.value = updatedTimes;
|
_historyLastEventTime.value = updatedTimes;
|
||||||
if (socket?.connected) {
|
if (socket?.connected) {
|
||||||
console.log('[TerminalPanel] requesting history for:', name);
|
|
||||||
socket.emit('get_terminal_output', { session: name, lines: 0, conversation_id: props.conversationId });
|
socket.emit('get_terminal_output', { session: name, lines: 0, conversation_id: props.conversationId });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -236,12 +233,10 @@ function switchToSession(name: string) {
|
|||||||
|
|
||||||
// ---- watchers ----
|
// ---- watchers ----
|
||||||
watch(activeSession, (val) => {
|
watch(activeSession, (val) => {
|
||||||
console.log('[TerminalPanel] activeSession:', val);
|
|
||||||
if (term) renderSessionLog();
|
if (term) renderSessionLog();
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(sessionKeys, (keys) => {
|
watch(sessionKeys, (keys) => {
|
||||||
console.log('[TERMINAL_DEBUG] sessionKeys:', keys);
|
|
||||||
if (keys.length > 0 && term && !activeSession.value) {
|
if (keys.length > 0 && term && !activeSession.value) {
|
||||||
switchToSession(keys[0]);
|
switchToSession(keys[0]);
|
||||||
}
|
}
|
||||||
@ -250,7 +245,6 @@ watch(sessionKeys, (keys) => {
|
|||||||
// 工作区切换时重连
|
// 工作区切换时重连
|
||||||
watch(() => props.workspaceId, (newId, oldId) => {
|
watch(() => props.workspaceId, (newId, oldId) => {
|
||||||
if (newId && oldId && newId !== oldId) {
|
if (newId && oldId && newId !== oldId) {
|
||||||
console.log('[TerminalPanel] workspace changed:', oldId, '->', newId);
|
|
||||||
// 断开旧连接,清空状态,重新连接
|
// 断开旧连接,清空状态,重新连接
|
||||||
if (socket) {
|
if (socket) {
|
||||||
socket.disconnect();
|
socket.disconnect();
|
||||||
@ -270,7 +264,6 @@ watch(() => props.workspaceId, (newId, oldId) => {
|
|||||||
// 对话切换时重置并重新订阅(对话级 terminal:各对话 shell 互相独立)
|
// 对话切换时重置并重新订阅(对话级 terminal:各对话 shell 互相独立)
|
||||||
watch(() => props.conversationId, (newId, oldId) => {
|
watch(() => props.conversationId, (newId, oldId) => {
|
||||||
if (newId !== oldId) {
|
if (newId !== oldId) {
|
||||||
console.log('[TerminalPanel] conversation changed:', oldId, '->', newId);
|
|
||||||
sessions.value = {};
|
sessions.value = {};
|
||||||
activeSession.value = '';
|
activeSession.value = '';
|
||||||
sessionLogs.value = {};
|
sessionLogs.value = {};
|
||||||
@ -286,7 +279,6 @@ watch(() => props.conversationId, (newId, oldId) => {
|
|||||||
|
|
||||||
// ---- socket 初始化 ----
|
// ---- socket 初始化 ----
|
||||||
async function initSocket() {
|
async function initSocket() {
|
||||||
console.log('[TerminalPanel] initSocket start');
|
|
||||||
|
|
||||||
socket = createSocketClient('/', {
|
socket = createSocketClient('/', {
|
||||||
transports: ['websocket', 'polling'],
|
transports: ['websocket', 'polling'],
|
||||||
@ -319,16 +311,13 @@ async function initSocket() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
socket.on('connect', () => {
|
socket.on('connect', () => {
|
||||||
console.log('[TerminalPanel] socket connected');
|
|
||||||
socket!.emit('terminal_subscribe', { all: true, conversation_id: props.conversationId });
|
socket!.emit('terminal_subscribe', { all: true, conversation_id: props.conversationId });
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('disconnect', () => {
|
socket.on('disconnect', () => {
|
||||||
console.log('[TerminalPanel] socket disconnected');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('terminal_subscribed', (data: any) => {
|
socket.on('terminal_subscribed', (data: any) => {
|
||||||
console.log('[TerminalPanel] terminal_subscribed:', data);
|
|
||||||
if (data?.terminals) {
|
if (data?.terminals) {
|
||||||
const map: Record<string, { working_dir?: string; shell?: string }> = {};
|
const map: Record<string, { working_dir?: string; shell?: string }> = {};
|
||||||
for (const t of data.terminals) {
|
for (const t of data.terminals) {
|
||||||
@ -343,7 +332,6 @@ async function initSocket() {
|
|||||||
|
|
||||||
socket.on('terminal_started', (data: any) => {
|
socket.on('terminal_started', (data: any) => {
|
||||||
if (!acceptTerminalBroadcast(data)) return;
|
if (!acceptTerminalBroadcast(data)) return;
|
||||||
console.log('[TerminalPanel] terminal_started:', data);
|
|
||||||
sessions.value = {
|
sessions.value = {
|
||||||
...sessions.value,
|
...sessions.value,
|
||||||
[data.session]: { working_dir: data.working_dir, shell: 'bash' }
|
[data.session]: { working_dir: data.working_dir, shell: 'bash' }
|
||||||
@ -353,7 +341,6 @@ async function initSocket() {
|
|||||||
|
|
||||||
socket.on('terminal_list_update', (data: any) => {
|
socket.on('terminal_list_update', (data: any) => {
|
||||||
if (!acceptTerminalBroadcast(data)) return;
|
if (!acceptTerminalBroadcast(data)) return;
|
||||||
console.log('[TerminalPanel] terminal_list_update:', data);
|
|
||||||
if (data?.terminals) {
|
if (data?.terminals) {
|
||||||
const map: Record<string, { working_dir?: string; shell?: string }> = {};
|
const map: Record<string, { working_dir?: string; shell?: string }> = {};
|
||||||
for (const t of data.terminals) {
|
for (const t of data.terminals) {
|
||||||
@ -376,7 +363,6 @@ async function initSocket() {
|
|||||||
const historyTime = _historyLastEventTime.value[s] || 0;
|
const historyTime = _historyLastEventTime.value[s] || 0;
|
||||||
const eventTime = typeof data.timestamp === 'number' ? data.timestamp : 0;
|
const eventTime = typeof data.timestamp === 'number' ? data.timestamp : 0;
|
||||||
if (historyTime > 0 && eventTime > 0 && eventTime <= historyTime) {
|
if (historyTime > 0 && eventTime > 0 && eventTime <= historyTime) {
|
||||||
console.log('[TerminalPanel] terminal_output DEDUP skip:', s, 'eventTime:', eventTime, '<= historyTime:', historyTime);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,17 +370,14 @@ async function initSocket() {
|
|||||||
// 避免新终端历史为空时 _historyReady 始终为 false 导致永远写不进去。
|
// 避免新终端历史为空时 _historyReady 始终为 false 导致永远写不进去。
|
||||||
sessionLogs.value[s] = (sessionLogs.value[s] || '') + delta;
|
sessionLogs.value[s] = (sessionLogs.value[s] || '') + delta;
|
||||||
if (s === activeSession.value && term && _historyReady.value[s]) {
|
if (s === activeSession.value && term && _historyReady.value[s]) {
|
||||||
console.log('[TerminalPanel] terminal_output WRITE:', s, 'len:', delta.length);
|
|
||||||
term.write(delta);
|
term.write(delta);
|
||||||
} else {
|
} else {
|
||||||
console.log('[TerminalPanel] terminal_output SKIP:', s, 'len:', delta.length, 'isActive:', s === activeSession.value, 'ready:', _historyReady.value[s]);
|
|
||||||
}
|
}
|
||||||
if (!activeSession.value) switchToSession(s);
|
if (!activeSession.value) switchToSession(s);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('terminal_input', (data: any) => {
|
socket.on('terminal_input', (data: any) => {
|
||||||
if (!acceptTerminalBroadcast(data)) return;
|
if (!acceptTerminalBroadcast(data)) return;
|
||||||
console.log('[TerminalPanel] terminal_input:', data.session);
|
|
||||||
// 原样显示后端输出,此处不重复写入
|
// 原样显示后端输出,此处不重复写入
|
||||||
// 仅用于新终端自动切换
|
// 仅用于新终端自动切换
|
||||||
if (!activeSession.value && data.session) switchToSession(data.session);
|
if (!activeSession.value && data.session) switchToSession(data.session);
|
||||||
@ -402,7 +385,6 @@ async function initSocket() {
|
|||||||
|
|
||||||
socket.on('terminal_closed', (data: any) => {
|
socket.on('terminal_closed', (data: any) => {
|
||||||
if (!acceptTerminalBroadcast(data)) return;
|
if (!acceptTerminalBroadcast(data)) return;
|
||||||
console.log('[TerminalPanel] terminal_closed:', data);
|
|
||||||
const updated = { ...sessions.value };
|
const updated = { ...sessions.value };
|
||||||
delete updated[data.session];
|
delete updated[data.session];
|
||||||
sessions.value = updated;
|
sessions.value = updated;
|
||||||
@ -414,7 +396,6 @@ async function initSocket() {
|
|||||||
|
|
||||||
socket.on('terminal_reset', (data: any) => {
|
socket.on('terminal_reset', (data: any) => {
|
||||||
if (!acceptTerminalBroadcast(data)) return;
|
if (!acceptTerminalBroadcast(data)) return;
|
||||||
console.log('[TerminalPanel] terminal_reset:', data);
|
|
||||||
const target = data.session || activeSession.value;
|
const target = data.session || activeSession.value;
|
||||||
if (target) {
|
if (target) {
|
||||||
sessionLogs.value = { ...sessionLogs.value, [target]: '' };
|
sessionLogs.value = { ...sessionLogs.value, [target]: '' };
|
||||||
@ -428,12 +409,10 @@ async function initSocket() {
|
|||||||
|
|
||||||
socket.on('terminal_switched', (data: any) => {
|
socket.on('terminal_switched', (data: any) => {
|
||||||
if (!acceptTerminalBroadcast(data)) return;
|
if (!acceptTerminalBroadcast(data)) return;
|
||||||
console.log('[TerminalPanel] terminal_switched:', data);
|
|
||||||
if (data.current) switchToSession(data.current);
|
if (data.current) switchToSession(data.current);
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleHistory = (data: any) => {
|
const handleHistory = (data: any) => {
|
||||||
console.log('[TerminalPanel] handleHistory FIRED, event session:', data.session, 'data keys:', Object.keys(data).join(','));
|
|
||||||
const s = data.session || data.active;
|
const s = data.session || data.active;
|
||||||
if (!s) return;
|
if (!s) return;
|
||||||
const raw = data.output || data.data || '';
|
const raw = data.output || data.data || '';
|
||||||
@ -443,7 +422,6 @@ async function initSocket() {
|
|||||||
} else if (Array.isArray(raw)) {
|
} else if (Array.isArray(raw)) {
|
||||||
payload = raw.join('\n');
|
payload = raw.join('\n');
|
||||||
}
|
}
|
||||||
console.log('[TerminalPanel] handleHistory:', s, 'rawLen:', raw.length, 'payloadLen:', payload.length, 'isActive:', s === activeSession.value);
|
|
||||||
if (payload) {
|
if (payload) {
|
||||||
sessionLogs.value[s] = payload;
|
sessionLogs.value[s] = payload;
|
||||||
}
|
}
|
||||||
@ -454,7 +432,6 @@ async function initSocket() {
|
|||||||
// 记录历史快照最后一条事件的时间戳,用于后续 terminal_output 去重
|
// 记录历史快照最后一条事件的时间戳,用于后续 terminal_output 去重
|
||||||
const lastEventTime = typeof data.last_event_time === 'number' ? data.last_event_time : 0;
|
const lastEventTime = typeof data.last_event_time === 'number' ? data.last_event_time : 0;
|
||||||
_historyLastEventTime.value = { ..._historyLastEventTime.value, [s]: lastEventTime };
|
_historyLastEventTime.value = { ..._historyLastEventTime.value, [s]: lastEventTime };
|
||||||
console.log('[TerminalPanel] handleHistory SAVED:', s, 'finalLen:', payload.length, 'calling render:', s === activeSession.value);
|
|
||||||
if (s === activeSession.value) {
|
if (s === activeSession.value) {
|
||||||
renderSessionLog();
|
renderSessionLog();
|
||||||
}
|
}
|
||||||
@ -464,7 +441,6 @@ async function initSocket() {
|
|||||||
socket.on('terminal_history', handleHistory);
|
socket.on('terminal_history', handleHistory);
|
||||||
|
|
||||||
socket.connect();
|
socket.connect();
|
||||||
console.log('[TerminalPanel] socket connecting...');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 生命周期 ----
|
// ---- 生命周期 ----
|
||||||
|
|||||||
@ -158,30 +158,22 @@ const emit = defineEmits<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const handleCloseClick = () => {
|
const handleCloseClick = () => {
|
||||||
console.log('[sidebarDEBUG] ToolApprovalPanel close button clicked');
|
|
||||||
emit('close');
|
emit('close');
|
||||||
};
|
};
|
||||||
|
|
||||||
// 检测是否为移动端视口 - 使用计算属性确保响应式
|
// 检测是否为移动端视口 - 使用计算属性确保响应式
|
||||||
const isMobileViewport = computed(() => {
|
const isMobileViewport = computed(() => {
|
||||||
const isMobile = typeof window !== 'undefined' && window.innerWidth <= 768;
|
const isMobile = typeof window !== 'undefined' && window.innerWidth <= 768;
|
||||||
console.log('[sidebarDEBUG] isMobileViewport computed:', isMobile, 'windowWidth:', window?.innerWidth);
|
|
||||||
return isMobile;
|
return isMobile;
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
console.log('[sidebarDEBUG] ToolApprovalPanel mounted');
|
|
||||||
console.log('[sidebarDEBUG] window.innerWidth:', window.innerWidth);
|
|
||||||
console.log('[sidebarDEBUG] isMobileViewport:', isMobileViewport.value);
|
|
||||||
console.log('[sidebarDEBUG] body[data-theme]:', document.body.getAttribute('data-theme'));
|
|
||||||
|
|
||||||
// 检查父元素链
|
// 检查父元素链
|
||||||
const aside = document.querySelector('.tool-approval-panel');
|
const aside = document.querySelector('.tool-approval-panel');
|
||||||
const sidebarHeader = document.querySelector('.tool-approval-panel .sidebar-header');
|
const sidebarHeader = document.querySelector('.tool-approval-panel .sidebar-header');
|
||||||
if (sidebarHeader) {
|
if (sidebarHeader) {
|
||||||
const computedStyle = window.getComputedStyle(sidebarHeader);
|
const computedStyle = window.getComputedStyle(sidebarHeader);
|
||||||
console.log('[sidebarDEBUG] sidebar-header computed background:', computedStyle.backgroundColor);
|
|
||||||
console.log('[sidebarDEBUG] sidebar-header computed padding:', computedStyle.paddingTop, computedStyle.paddingBottom);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -865,10 +865,6 @@ const persistPinnedWorkspaces = () => {
|
|||||||
const next = Array.from(pinnedWorkspaceIds.value);
|
const next = Array.from(pinnedWorkspaceIds.value);
|
||||||
const current = personalizationStore.form.sidebar_pinned_workspaces || [];
|
const current = personalizationStore.form.sidebar_pinned_workspaces || [];
|
||||||
if (JSON.stringify(next) === JSON.stringify(current)) return;
|
if (JSON.stringify(next) === JSON.stringify(current)) return;
|
||||||
console.log('[sidebar-debug] persistPinnedWorkspaces', {
|
|
||||||
next: JSON.parse(JSON.stringify(next)),
|
|
||||||
current: JSON.parse(JSON.stringify(current))
|
|
||||||
});
|
|
||||||
personalizationStore.updateField({
|
personalizationStore.updateField({
|
||||||
key: 'sidebar_pinned_workspaces',
|
key: 'sidebar_pinned_workspaces',
|
||||||
value: next
|
value: next
|
||||||
@ -880,10 +876,6 @@ const persistWorkspaceOrder = () => {
|
|||||||
const next = Array.from(workspaceOrder.value);
|
const next = Array.from(workspaceOrder.value);
|
||||||
const current = personalizationStore.form.sidebar_workspace_order || [];
|
const current = personalizationStore.form.sidebar_workspace_order || [];
|
||||||
if (JSON.stringify(next) === JSON.stringify(current)) return;
|
if (JSON.stringify(next) === JSON.stringify(current)) return;
|
||||||
console.log('[sidebar-debug] persistWorkspaceOrder', {
|
|
||||||
next: JSON.parse(JSON.stringify(next)),
|
|
||||||
current: JSON.parse(JSON.stringify(current))
|
|
||||||
});
|
|
||||||
personalizationStore.updateField({
|
personalizationStore.updateField({
|
||||||
key: 'sidebar_workspace_order',
|
key: 'sidebar_workspace_order',
|
||||||
value: next
|
value: next
|
||||||
@ -893,19 +885,12 @@ const persistWorkspaceOrder = () => {
|
|||||||
const bumpCurrentWorkspaceToTop = () => {
|
const bumpCurrentWorkspaceToTop = () => {
|
||||||
if (!sidebarSortLoaded.value) return;
|
if (!sidebarSortLoaded.value) return;
|
||||||
const currentId = String(props.currentWorkspaceId || '');
|
const currentId = String(props.currentWorkspaceId || '');
|
||||||
console.log('[sidebar-debug] bumpCurrentWorkspaceToTop', {
|
|
||||||
currentId,
|
|
||||||
orderBefore: JSON.parse(JSON.stringify(workspaceOrder.value))
|
|
||||||
});
|
|
||||||
if (!currentId || pinnedWorkspaceIds.value.has(currentId)) return;
|
if (!currentId || pinnedWorkspaceIds.value.has(currentId)) return;
|
||||||
if (workspaceOrder.value[0] === currentId) return;
|
if (workspaceOrder.value[0] === currentId) return;
|
||||||
const order = workspaceOrder.value.filter((item) => item !== currentId);
|
const order = workspaceOrder.value.filter((item) => item !== currentId);
|
||||||
order.unshift(currentId);
|
order.unshift(currentId);
|
||||||
workspaceOrder.value = order;
|
workspaceOrder.value = order;
|
||||||
persistWorkspaceOrder();
|
persistWorkspaceOrder();
|
||||||
console.log('[sidebar-debug] bumpCurrentWorkspaceToTop after', {
|
|
||||||
orderAfter: JSON.parse(JSON.stringify(workspaceOrder.value))
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const syncWorkspaceOrderWithWorkspaces = (workspaces: any[]) => {
|
const syncWorkspaceOrderWithWorkspaces = (workspaces: any[]) => {
|
||||||
@ -921,12 +906,6 @@ const syncWorkspaceOrderWithWorkspaces = (workspaces: any[]) => {
|
|||||||
const removedIds = new Set(
|
const removedIds = new Set(
|
||||||
workspaceOrder.value.filter((id) => !ids.includes(id))
|
workspaceOrder.value.filter((id) => !ids.includes(id))
|
||||||
);
|
);
|
||||||
console.log('[sidebar-debug] syncWorkspaceOrderWithWorkspaces', {
|
|
||||||
ids,
|
|
||||||
newIds,
|
|
||||||
removedIds: Array.from(removedIds),
|
|
||||||
orderBefore: JSON.parse(JSON.stringify(workspaceOrder.value))
|
|
||||||
});
|
|
||||||
if (newIds.length || removedIds.size) {
|
if (newIds.length || removedIds.size) {
|
||||||
workspaceOrder.value = workspaceOrder.value
|
workspaceOrder.value = workspaceOrder.value
|
||||||
.filter((id) => !removedIds.has(id))
|
.filter((id) => !removedIds.has(id))
|
||||||
@ -937,10 +916,6 @@ const syncWorkspaceOrderWithWorkspaces = (workspaces: any[]) => {
|
|||||||
|
|
||||||
const loadSidebarSortFromStore = () => {
|
const loadSidebarSortFromStore = () => {
|
||||||
if (!personalizationStore.loaded || sidebarSortLoaded.value) return;
|
if (!personalizationStore.loaded || sidebarSortLoaded.value) return;
|
||||||
console.log('[sidebar-debug] loadSidebarSortFromStore start', {
|
|
||||||
pinned: JSON.parse(JSON.stringify(personalizationStore.form.sidebar_pinned_workspaces)),
|
|
||||||
order: JSON.parse(JSON.stringify(personalizationStore.form.sidebar_workspace_order))
|
|
||||||
});
|
|
||||||
syncPinnedFromStore();
|
syncPinnedFromStore();
|
||||||
syncOrderFromStore();
|
syncOrderFromStore();
|
||||||
sidebarSortLoaded.value = true;
|
sidebarSortLoaded.value = true;
|
||||||
@ -948,10 +923,6 @@ const loadSidebarSortFromStore = () => {
|
|||||||
syncWorkspaceOrderWithWorkspaces(props.workspaces);
|
syncWorkspaceOrderWithWorkspaces(props.workspaces);
|
||||||
}
|
}
|
||||||
bumpCurrentWorkspaceToTop();
|
bumpCurrentWorkspaceToTop();
|
||||||
console.log('[sidebar-debug] loadSidebarSortFromStore end', {
|
|
||||||
pinned: Array.from(pinnedWorkspaceIds.value),
|
|
||||||
order: JSON.parse(JSON.stringify(workspaceOrder.value))
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@ -1165,7 +1136,6 @@ watch(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleWorkspaceConversationClick = (conversationId: string, workspaceId: string) => {
|
const handleWorkspaceConversationClick = (conversationId: string, workspaceId: string) => {
|
||||||
console.log('[sidebar-debug] handleWorkspaceConversationClick', { conversationId, workspaceId });
|
|
||||||
openWorkspaceMenuId.value = null;
|
openWorkspaceMenuId.value = null;
|
||||||
emit('select-workspace-conversation', { conversationId, workspaceId });
|
emit('select-workspace-conversation', { conversationId, workspaceId });
|
||||||
};
|
};
|
||||||
|
|||||||
@ -120,7 +120,6 @@ export function useEasterEgg() {
|
|||||||
}, durationSeconds * 1000);
|
}, durationSeconds * 1000);
|
||||||
uiStore.setEasterEggState({ cleanupTimer: timer });
|
uiStore.setEasterEggState({ cleanupTimer: timer });
|
||||||
if (payload?.message) {
|
if (payload?.message) {
|
||||||
console.info(`[彩蛋] ${payload.display_name || effectName}: ${payload.message}`);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -24,7 +24,7 @@ import { useQuickDockStore } from '../stores/quickDock';
|
|||||||
|
|
||||||
export async function initializeLegacySocket(ctx: any) {
|
export async function initializeLegacySocket(ctx: any) {
|
||||||
try {
|
try {
|
||||||
const SOCKET_DEBUG_LOGS_ENABLED = true;
|
const SOCKET_DEBUG_LOGS_ENABLED = false;
|
||||||
const socketLog = (...args: any[]) => {
|
const socketLog = (...args: any[]) => {
|
||||||
if (!SOCKET_DEBUG_LOGS_ENABLED) {
|
if (!SOCKET_DEBUG_LOGS_ENABLED) {
|
||||||
return;
|
return;
|
||||||
@ -74,20 +74,10 @@ export async function initializeLegacySocket(ctx: any) {
|
|||||||
}
|
}
|
||||||
if (action.tool.intent_full === text) {
|
if (action.tool.intent_full === text) {
|
||||||
if (console && console.debug) {
|
if (console && console.debug) {
|
||||||
console.debug('[tool_intent] skip (same text)', {
|
|
||||||
id: action?.id || action?.tool?.id,
|
|
||||||
name: action?.tool?.name,
|
|
||||||
text
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (typeof console !== 'undefined' && console.debug) {
|
if (typeof console !== 'undefined' && console.debug) {
|
||||||
console.debug('[tool_intent] typing start', {
|
|
||||||
id: action?.id || action?.tool?.id,
|
|
||||||
name: action?.tool?.name,
|
|
||||||
text
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
if (action.tool.intent_full === text && action.tool.intent_rendered === text) {
|
if (action.tool.intent_full === text && action.tool.intent_rendered === text) {
|
||||||
return;
|
return;
|
||||||
@ -105,11 +95,6 @@ export async function initializeLegacySocket(ctx: any) {
|
|||||||
action.tool.intent_typing_timer = window.setInterval(() => {
|
action.tool.intent_typing_timer = window.setInterval(() => {
|
||||||
action.tool.intent_rendered = text.slice(0, index + 1);
|
action.tool.intent_rendered = text.slice(0, index + 1);
|
||||||
if (index === 0 && console && console.debug) {
|
if (index === 0 && console && console.debug) {
|
||||||
console.debug('[tool_intent] typing tick', {
|
|
||||||
id: action?.id || action?.tool?.id,
|
|
||||||
name: action?.tool?.name,
|
|
||||||
next: action.tool.intent_rendered
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
index += 1;
|
index += 1;
|
||||||
if (index >= text.length) {
|
if (index >= text.length) {
|
||||||
@ -117,11 +102,6 @@ export async function initializeLegacySocket(ctx: any) {
|
|||||||
action.tool.intent_typing_timer = null;
|
action.tool.intent_typing_timer = null;
|
||||||
action.tool.intent_rendered = text;
|
action.tool.intent_rendered = text;
|
||||||
if (console && console.debug) {
|
if (console && console.debug) {
|
||||||
console.debug('[tool_intent] typing done', {
|
|
||||||
id: action?.id || action?.tool?.id,
|
|
||||||
name: action?.tool?.name,
|
|
||||||
full: text
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (typeof ctx?.$forceUpdate === 'function') {
|
if (typeof ctx?.$forceUpdate === 'function') {
|
||||||
@ -789,7 +769,6 @@ export async function initializeLegacySocket(ctx: any) {
|
|||||||
// 监听对话变更事件
|
// 监听对话变更事件
|
||||||
ctx.socket.on('conversation_changed', (data) => {
|
ctx.socket.on('conversation_changed', (data) => {
|
||||||
socketLog('对话已切换:', data);
|
socketLog('对话已切换:', data);
|
||||||
console.log('[conv-trace] socket:conversation_changed', data);
|
|
||||||
|
|
||||||
// 初始化期间不修改 currentConversationId,避免与 bootstrapRoute 冲突
|
// 初始化期间不修改 currentConversationId,避免与 bootstrapRoute 冲突
|
||||||
if (ctx.initialRouteResolved) {
|
if (ctx.initialRouteResolved) {
|
||||||
@ -1177,11 +1156,6 @@ export async function initializeLegacySocket(ctx: any) {
|
|||||||
socketLog('文本开始 (轮询模式,跳过)');
|
socketLog('文本开始 (轮询模式,跳过)');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log('[DEBUG] 收到 text_start 事件:', {
|
|
||||||
currentConversationId: ctx.currentConversationId,
|
|
||||||
messagesCount: ctx.messages.length,
|
|
||||||
currentMessageIndex: ctx.currentMessageIndex
|
|
||||||
});
|
|
||||||
socketLog('文本开始');
|
socketLog('文本开始');
|
||||||
logStreamingDebug('socket:text_start');
|
logStreamingDebug('socket:text_start');
|
||||||
finalizeStreamingText({ force: true });
|
finalizeStreamingText({ force: true });
|
||||||
@ -1192,7 +1166,6 @@ export async function initializeLegacySocket(ctx: any) {
|
|||||||
streamingState.activeTextAction = action || ensureActiveTextAction();
|
streamingState.activeTextAction = action || ensureActiveTextAction();
|
||||||
ensureActiveMessageBinding();
|
ensureActiveMessageBinding();
|
||||||
ctx.$forceUpdate();
|
ctx.$forceUpdate();
|
||||||
console.log('[DEBUG] text_start 处理完成');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 文本内容块
|
// 文本内容块
|
||||||
@ -1201,12 +1174,6 @@ export async function initializeLegacySocket(ctx: any) {
|
|||||||
if (ctx.usePollingMode && !ctx.waitingForSubAgent) {
|
if (ctx.usePollingMode && !ctx.waitingForSubAgent) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log('[DEBUG] 收到 text_chunk 事件:', {
|
|
||||||
conversation_id: data?.conversation_id,
|
|
||||||
currentConversationId: ctx.currentConversationId,
|
|
||||||
chunkLength: (data?.content || '').length,
|
|
||||||
index: data?.index
|
|
||||||
});
|
|
||||||
logStreamingDebug('socket:text_chunk', {
|
logStreamingDebug('socket:text_chunk', {
|
||||||
index: data?.index ?? null,
|
index: data?.index ?? null,
|
||||||
elapsed: data?.elapsed ?? null,
|
elapsed: data?.elapsed ?? null,
|
||||||
@ -1301,30 +1268,15 @@ export async function initializeLegacySocket(ctx: any) {
|
|||||||
if (ctx.dropToolEvents) {
|
if (ctx.dropToolEvents) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log('[DEBUG] 收到 tool_preparing 事件:', {
|
|
||||||
name: data.name,
|
|
||||||
id: data.id,
|
|
||||||
conversation_id: data?.conversation_id,
|
|
||||||
currentConversationId: ctx.currentConversationId,
|
|
||||||
match: data?.conversation_id === ctx.currentConversationId
|
|
||||||
});
|
|
||||||
socketLog('工具准备中:', data.name);
|
socketLog('工具准备中:', data.name);
|
||||||
if (typeof console !== 'undefined' && console.debug) {
|
if (typeof console !== 'undefined' && console.debug) {
|
||||||
console.debug('[tool_intent] preparing', {
|
|
||||||
id: data?.id,
|
|
||||||
name: data?.name,
|
|
||||||
intent: data?.intent,
|
|
||||||
convo: data?.conversation_id
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
if (data?.conversation_id && data.conversation_id !== ctx.currentConversationId) {
|
if (data?.conversation_id && data.conversation_id !== ctx.currentConversationId) {
|
||||||
console.log('[DEBUG] 跳过 tool_preparing (对话不匹配)');
|
|
||||||
socketLog('跳过tool_preparing(对话不匹配)', data.conversation_id);
|
socketLog('跳过tool_preparing(对话不匹配)', data.conversation_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const msg = ctx.chatEnsureAssistantMessage();
|
const msg = ctx.chatEnsureAssistantMessage();
|
||||||
if (!msg) {
|
if (!msg) {
|
||||||
console.log('[DEBUG] tool_preparing: 无法获取assistant消息');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (msg.awaitingFirstContent) {
|
if (msg.awaitingFirstContent) {
|
||||||
@ -1355,7 +1307,6 @@ export async function initializeLegacySocket(ctx: any) {
|
|||||||
if (data.intent) {
|
if (data.intent) {
|
||||||
startIntentTyping(action, data.intent);
|
startIntentTyping(action, data.intent);
|
||||||
}
|
}
|
||||||
console.log('[DEBUG] tool_preparing 处理完成,action已添加');
|
|
||||||
ctx.$forceUpdate();
|
ctx.$forceUpdate();
|
||||||
ctx.conditionalScrollToBottom();
|
ctx.conditionalScrollToBottom();
|
||||||
if (ctx.monitorPreviewTool) {
|
if (ctx.monitorPreviewTool) {
|
||||||
@ -1376,12 +1327,6 @@ export async function initializeLegacySocket(ctx: any) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (typeof console !== 'undefined' && console.debug) {
|
if (typeof console !== 'undefined' && console.debug) {
|
||||||
console.debug('[tool_intent] update', {
|
|
||||||
id: data?.id,
|
|
||||||
name: data?.name,
|
|
||||||
intent: data?.intent,
|
|
||||||
convo: data?.conversation_id
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
const target =
|
const target =
|
||||||
ctx.toolFindAction(data.id, data.preparing_id, data.execution_id) ||
|
ctx.toolFindAction(data.id, data.preparing_id, data.execution_id) ||
|
||||||
@ -1689,35 +1634,21 @@ export async function initializeLegacySocket(ctx: any) {
|
|||||||
|
|
||||||
// 任务完成(重点:更新Token统计)
|
// 任务完成(重点:更新Token统计)
|
||||||
ctx.socket.on('task_complete', (data) => {
|
ctx.socket.on('task_complete', (data) => {
|
||||||
console.log('[DEBUG] 收到 task_complete 事件:', data);
|
|
||||||
console.log('[DEBUG] 当前状态 (before task_complete):', {
|
|
||||||
taskInProgress: ctx.taskInProgress,
|
|
||||||
streamingMessage: ctx.streamingMessage,
|
|
||||||
has_running_sub_agents: data.has_running_sub_agents
|
|
||||||
});
|
|
||||||
socketLog('任务完成', data);
|
socketLog('任务完成', data);
|
||||||
|
|
||||||
const hasRunningMultiAgent = !!data?.has_running_multi_agent;
|
const hasRunningMultiAgent = !!data?.has_running_multi_agent;
|
||||||
// 多智能体模式下,若仍有运行中的多智能体实例,保持对话运行态,继续接收后续消息;
|
// 多智能体模式下,若仍有运行中的多智能体实例,保持对话运行态,继续接收后续消息;
|
||||||
// 但此时主智能体已空闲,不应阻塞输入区(waitingForSubAgent=false)。
|
// 但此时主智能体已空闲,不应阻塞输入区(waitingForSubAgent=false)。
|
||||||
if (hasRunningMultiAgent) {
|
if (hasRunningMultiAgent) {
|
||||||
console.log('[DEBUG] 多智能体实例仍在运行,保持任务状态但不阻塞输入');
|
|
||||||
} else if (ctx.multiAgentMode || !data.has_running_sub_agents) {
|
} else if (ctx.multiAgentMode || !data.has_running_sub_agents) {
|
||||||
console.log('[DEBUG] 没有运行中的子智能体/多智能体,重置任务状态');
|
|
||||||
if (ctx.waitingForSubAgent) {
|
if (ctx.waitingForSubAgent) {
|
||||||
ctx.waitingForSubAgent = false;
|
ctx.waitingForSubAgent = false;
|
||||||
}
|
}
|
||||||
ctx.taskInProgress = false;
|
ctx.taskInProgress = false;
|
||||||
ctx.scheduleResetAfterTask('socket:task_complete', { preserveMonitorWindows: true });
|
ctx.scheduleResetAfterTask('socket:task_complete', { preserveMonitorWindows: true });
|
||||||
} else {
|
} else {
|
||||||
console.log('[DEBUG] 有运行中的子智能体,保持任务状态');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[DEBUG] 当前状态 (after task_complete):', {
|
|
||||||
taskInProgress: ctx.taskInProgress,
|
|
||||||
streamingMessage: ctx.streamingMessage
|
|
||||||
});
|
|
||||||
|
|
||||||
resetPendingToolEvents();
|
resetPendingToolEvents();
|
||||||
|
|
||||||
// 任务完成后立即更新Token统计(关键修复)
|
// 任务完成后立即更新Token统计(关键修复)
|
||||||
@ -1729,12 +1660,6 @@ export async function initializeLegacySocket(ctx: any) {
|
|||||||
|
|
||||||
// 子智能体等待状态
|
// 子智能体等待状态
|
||||||
ctx.socket.on('sub_agent_waiting', (data) => {
|
ctx.socket.on('sub_agent_waiting', (data) => {
|
||||||
console.log('[DEBUG] 收到 sub_agent_waiting 事件:', data);
|
|
||||||
console.log('[DEBUG] 当前状态 (before sub_agent_waiting):', {
|
|
||||||
taskInProgress: ctx.taskInProgress,
|
|
||||||
streamingMessage: ctx.streamingMessage,
|
|
||||||
stopRequested: ctx.stopRequested
|
|
||||||
});
|
|
||||||
socketLog('等待子智能体完成:', data);
|
socketLog('等待子智能体完成:', data);
|
||||||
|
|
||||||
// 多智能体模式下,子智能体 idle/running 是常态,不阻塞主对话输入区
|
// 多智能体模式下,子智能体 idle/running 是常态,不阻塞主对话输入区
|
||||||
@ -1748,13 +1673,6 @@ export async function initializeLegacySocket(ctx: any) {
|
|||||||
ctx.stopRequested = false;
|
ctx.stopRequested = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[DEBUG] 当前状态 (after sub_agent_waiting):', {
|
|
||||||
taskInProgress: ctx.taskInProgress,
|
|
||||||
streamingMessage: ctx.streamingMessage,
|
|
||||||
stopRequested: ctx.stopRequested,
|
|
||||||
waitingForSubAgent: ctx.waitingForSubAgent
|
|
||||||
});
|
|
||||||
|
|
||||||
// 显示等待提示
|
// 显示等待提示
|
||||||
if (typeof ctx.appendSystemAction === 'function') {
|
if (typeof ctx.appendSystemAction === 'function') {
|
||||||
const taskList = data.tasks
|
const taskList = data.tasks
|
||||||
@ -1764,7 +1682,6 @@ export async function initializeLegacySocket(ctx: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx.$forceUpdate();
|
ctx.$forceUpdate();
|
||||||
console.log('[DEBUG] sub_agent_waiting 处理完成');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 聚焦文件更新
|
// 聚焦文件更新
|
||||||
@ -1886,7 +1803,6 @@ export async function initializeLegacySocket(ctx: any) {
|
|||||||
|
|
||||||
// === 实时终端事件 ===
|
// === 实时终端事件 ===
|
||||||
ctx.socket.on('terminal_subscribed', (data) => {
|
ctx.socket.on('terminal_subscribed', (data) => {
|
||||||
console.log('[Socket] terminal_subscribed:', data);
|
|
||||||
if (data?.terminals) {
|
if (data?.terminals) {
|
||||||
const sessions: Record<string, { working_dir?: string; shell?: string }> = {};
|
const sessions: Record<string, { working_dir?: string; shell?: string }> = {};
|
||||||
for (const t of data.terminals) {
|
for (const t of data.terminals) {
|
||||||
@ -1903,7 +1819,6 @@ export async function initializeLegacySocket(ctx: any) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ctx.socket.on('terminal_started', (data) => {
|
ctx.socket.on('terminal_started', (data) => {
|
||||||
console.log('[Socket] terminal_started:', data);
|
|
||||||
if (data?.session) {
|
if (data?.session) {
|
||||||
const sessions = { ...ctx.terminalSessions };
|
const sessions = { ...ctx.terminalSessions };
|
||||||
sessions[data.session] = {
|
sessions[data.session] = {
|
||||||
|
|||||||
@ -17,17 +17,16 @@ let showHtmlDebugCount = 0;
|
|||||||
const SHOW_HTML_DEBUG_MAX = 500;
|
const SHOW_HTML_DEBUG_MAX = 500;
|
||||||
|
|
||||||
function isShowHtmlDebugEnabled() {
|
function isShowHtmlDebugEnabled() {
|
||||||
if (typeof window === 'undefined') return true;
|
if (typeof window === 'undefined') return false;
|
||||||
try {
|
try {
|
||||||
const flag = (window as any).__SHOW_HTML_DEBUG__;
|
const flag = (window as any).__SHOW_HTML_DEBUG__;
|
||||||
if (flag === false || flag === '0') return false;
|
|
||||||
if (flag === true || flag === '1') return true;
|
if (flag === true || flag === '1') return true;
|
||||||
|
if (flag === false || flag === '0') return false;
|
||||||
const localFlag = window.localStorage?.getItem('showHtmlDebug');
|
const localFlag = window.localStorage?.getItem('showHtmlDebug');
|
||||||
if (localFlag === '0' || localFlag === 'false') return false;
|
|
||||||
if (localFlag === '1' || localFlag === 'true') return true;
|
if (localFlag === '1' || localFlag === 'true') return true;
|
||||||
return true;
|
return false;
|
||||||
} catch {
|
} catch {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -77,7 +77,8 @@ function isScrollDebugEnabled() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isShowHtmlScrollTraceEnabled() {
|
function isShowHtmlScrollTraceEnabled() {
|
||||||
if (typeof window === 'undefined') return true;
|
// 默认关闭:排障时通过 window.__SHOW_HTML_SCROLL_TRACE__ = true 或 localStorage.showHtmlScrollTrace = '1' 显式打开
|
||||||
|
if (typeof window === 'undefined') return false;
|
||||||
try {
|
try {
|
||||||
const explicit = (window as any).__SHOW_HTML_SCROLL_TRACE__;
|
const explicit = (window as any).__SHOW_HTML_SCROLL_TRACE__;
|
||||||
if (explicit === false || explicit === '0') return false;
|
if (explicit === false || explicit === '0') return false;
|
||||||
@ -85,9 +86,9 @@ function isShowHtmlScrollTraceEnabled() {
|
|||||||
const localFlag = window.localStorage?.getItem('showHtmlScrollTrace');
|
const localFlag = window.localStorage?.getItem('showHtmlScrollTrace');
|
||||||
if (localFlag === '0' || localFlag === 'false') return false;
|
if (localFlag === '0' || localFlag === 'false') return false;
|
||||||
if (localFlag === '1' || localFlag === 'true') return true;
|
if (localFlag === '1' || localFlag === 'true') return true;
|
||||||
return true;
|
return false;
|
||||||
} catch {
|
} catch {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -84,7 +84,6 @@ function clearAwaitingFirstContent(message: any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const userMDebug = (...args: any[]) => {
|
const userMDebug = (...args: any[]) => {
|
||||||
console.log('[USERMDEBUG]', ...args);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useChatStore = defineStore('chat', {
|
export const useChatStore = defineStore('chat', {
|
||||||
@ -222,11 +221,6 @@ export const useChatStore = defineStore('chat', {
|
|||||||
this.streamingMessage = true;
|
this.streamingMessage = true;
|
||||||
message.awaitingFirstContent = true;
|
message.awaitingFirstContent = true;
|
||||||
|
|
||||||
console.log('[DEBUG_AWAITING] ===== startAssistantMessage =====', {
|
|
||||||
awaitingFirstContent: message.awaitingFirstContent,
|
|
||||||
generatingLabel: message.generatingLabel
|
|
||||||
});
|
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
},
|
},
|
||||||
startThinkingAction() {
|
startThinkingAction() {
|
||||||
|
|||||||
@ -188,11 +188,6 @@ export const useConversationStore = defineStore('conversation', {
|
|||||||
{ reset = false, refresh = false } = {}
|
{ reset = false, refresh = false } = {}
|
||||||
) {
|
) {
|
||||||
if (!workspaceId) return;
|
if (!workspaceId) return;
|
||||||
console.log('[sidebar-debug] loadWorkspaceConversations', {
|
|
||||||
workspaceId,
|
|
||||||
reset,
|
|
||||||
refresh
|
|
||||||
});
|
|
||||||
let index = this.workspaceGroups.findIndex((g) => g.workspaceId === workspaceId);
|
let index = this.workspaceGroups.findIndex((g) => g.workspaceId === workspaceId);
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
this.ensureWorkspaceGroup(workspaceId);
|
this.ensureWorkspaceGroup(workspaceId);
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { useModelStore } from './model';
|
import { useModelStore } from './model';
|
||||||
|
|
||||||
|
|
||||||
export type BlockDisplayMode = 'traditional' | 'stacked' | 'minimal';
|
export type BlockDisplayMode = 'traditional' | 'stacked' | 'minimal';
|
||||||
export type CompactMessageDisplay = 'full' | 'brief';
|
export type CompactMessageDisplay = 'full' | 'brief';
|
||||||
type RunMode = 'fast' | 'thinking';
|
type RunMode = 'fast' | 'thinking';
|
||||||
@ -859,20 +858,17 @@ export const usePersonalizationStore = defineStore('personalization', {
|
|||||||
},
|
},
|
||||||
async logout() {
|
async logout() {
|
||||||
try {
|
try {
|
||||||
console.info('[auth-debug] logout clicked, sending POST /logout');
|
|
||||||
const resp = await fetch('/logout', {
|
const resp = await fetch('/logout', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
cache: 'no-store'
|
cache: 'no-store'
|
||||||
});
|
});
|
||||||
console.info('[auth-debug] logout POST status:', resp.status);
|
|
||||||
let payload: any = null;
|
let payload: any = null;
|
||||||
try {
|
try {
|
||||||
payload = await resp.json();
|
payload = await resp.json();
|
||||||
} catch (_err) {
|
} catch (_err) {
|
||||||
payload = null;
|
payload = null;
|
||||||
}
|
}
|
||||||
console.info('[auth-debug] logout POST payload:', payload);
|
|
||||||
if (resp.ok && (!payload || payload.success !== false)) {
|
if (resp.ok && (!payload || payload.success !== false)) {
|
||||||
window.location.replace(`/login?logged_out=1&ts=${Date.now()}`);
|
window.location.replace(`/login?logged_out=1&ts=${Date.now()}`);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -103,7 +103,6 @@ export const useResourceStore = defineStore('resource', {
|
|||||||
this.currentContextTokens = value || 0;
|
this.currentContextTokens = value || 0;
|
||||||
},
|
},
|
||||||
toggleTokenPanel() {
|
toggleTokenPanel() {
|
||||||
console.log('[UI_DEBUG] toggleTokenPanel called, current state:', this.tokenPanelCollapsed, 'new state:', !this.tokenPanelCollapsed);
|
|
||||||
this.tokenPanelCollapsed = !this.tokenPanelCollapsed;
|
this.tokenPanelCollapsed = !this.tokenPanelCollapsed;
|
||||||
if (this.tokenPanelCollapsed) {
|
if (this.tokenPanelCollapsed) {
|
||||||
this.stopContainerStatsPolling();
|
this.stopContainerStatsPolling();
|
||||||
|
|||||||
@ -6,13 +6,24 @@ const debugNotifyLog = (...args: any[]) => {
|
|||||||
void args;
|
void args;
|
||||||
};
|
};
|
||||||
const keyNotifyLog = (...args: any[]) => {
|
const keyNotifyLog = (...args: any[]) => {
|
||||||
console.log(...args);
|
|
||||||
};
|
};
|
||||||
const jsonDebug = (...args: any[]) => {
|
const jsonDebug = (...args: any[]) => {
|
||||||
console.log('[JSONDEBUG]', ...args);
|
|
||||||
};
|
};
|
||||||
const CONN_DIAG_PREFIX = '[CONN_DIAG]';
|
const CONN_DIAG_PREFIX = '[CONN_DIAG]';
|
||||||
const TASK_POLL_DIAG_MAX = 2000;
|
const TASK_POLL_DIAG_MAX = 2000;
|
||||||
|
// 默认关闭 console 输出(心跳类高频日志);排障时通过 window.__CONN_DIAG__ = true
|
||||||
|
// 或 localStorage.connDiag = '1' 显式打开。内存记录 __CONN_DIAG_LOGS__ 始终收集。
|
||||||
|
const isConnDiagConsoleEnabled = () => {
|
||||||
|
if (typeof window === 'undefined') return false;
|
||||||
|
try {
|
||||||
|
const w = window as any;
|
||||||
|
if (w.__CONN_DIAG__ === true || w.__CONN_DIAG__ === '1') return true;
|
||||||
|
const localFlag = window.localStorage?.getItem('connDiag');
|
||||||
|
return localFlag === '1' || localFlag === 'true';
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
const taskPollDiag = (event: string, payload: Record<string, any> = {}) => {
|
const taskPollDiag = (event: string, payload: Record<string, any> = {}) => {
|
||||||
const ts = new Date().toISOString();
|
const ts = new Date().toISOString();
|
||||||
const record = { ts, event, ...payload };
|
const record = { ts, event, ...payload };
|
||||||
@ -30,7 +41,9 @@ const taskPollDiag = (event: string, payload: Record<string, any> = {}) => {
|
|||||||
} catch {
|
} catch {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
console.log(CONN_DIAG_PREFIX, event, record);
|
if (isConnDiagConsoleEnabled()) {
|
||||||
|
console.log(CONN_DIAG_PREFIX, event, record);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useTaskStore = defineStore('task', {
|
export const useTaskStore = defineStore('task', {
|
||||||
|
|||||||
@ -10,56 +10,43 @@ const applyTheme = (theme: ThemeKey) => {
|
|||||||
document.body.setAttribute('data-theme', theme);
|
document.body.setAttribute('data-theme', theme);
|
||||||
|
|
||||||
// 调试信息
|
// 调试信息
|
||||||
console.log('=== Theme Applied ===');
|
|
||||||
console.log('Theme:', theme);
|
|
||||||
|
|
||||||
// 检查样式是否生效
|
// 检查样式是否生效
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
console.log('=== Elements Check ===');
|
|
||||||
|
|
||||||
// 输入栏底色
|
// 输入栏底色
|
||||||
const stadiumShell = document.querySelector('.stadium-shell');
|
const stadiumShell = document.querySelector('.stadium-shell');
|
||||||
console.log('.stadium-shell exists:', !!stadiumShell);
|
|
||||||
if (stadiumShell) {
|
if (stadiumShell) {
|
||||||
const styles = window.getComputedStyle(stadiumShell);
|
const styles = window.getComputedStyle(stadiumShell);
|
||||||
console.log('.stadium-shell background-color:', styles.backgroundColor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 对话区域顶部的模型选择器
|
// 对话区域顶部的模型选择器
|
||||||
const ribbonSelector = document.querySelector('.conversation-ribbon__selector');
|
const ribbonSelector = document.querySelector('.conversation-ribbon__selector');
|
||||||
console.log('.conversation-ribbon__selector exists:', !!ribbonSelector);
|
|
||||||
if (ribbonSelector) {
|
if (ribbonSelector) {
|
||||||
const styles = window.getComputedStyle(ribbonSelector);
|
const styles = window.getComputedStyle(ribbonSelector);
|
||||||
console.log('.conversation-ribbon__selector color:', styles.color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectorModel = document.querySelector('.selector-model');
|
const selectorModel = document.querySelector('.selector-model');
|
||||||
if (selectorModel) {
|
if (selectorModel) {
|
||||||
const styles = window.getComputedStyle(selectorModel);
|
const styles = window.getComputedStyle(selectorModel);
|
||||||
console.log('.selector-model color:', styles.color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectorMode = document.querySelector('.selector-mode');
|
const selectorMode = document.querySelector('.selector-mode');
|
||||||
if (selectorMode) {
|
if (selectorMode) {
|
||||||
const styles = window.getComputedStyle(selectorMode);
|
const styles = window.getComputedStyle(selectorMode);
|
||||||
console.log('.selector-mode color:', styles.color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 下拉菜单
|
// 下拉菜单
|
||||||
const dropdown = document.querySelector('.model-mode-dropdown');
|
const dropdown = document.querySelector('.model-mode-dropdown');
|
||||||
console.log('.model-mode-dropdown exists:', !!dropdown);
|
|
||||||
if (dropdown) {
|
if (dropdown) {
|
||||||
const styles = window.getComputedStyle(dropdown);
|
const styles = window.getComputedStyle(dropdown);
|
||||||
console.log('.model-mode-dropdown background-color:', styles.backgroundColor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const dropdownItem = document.querySelector('.dropdown-item');
|
const dropdownItem = document.querySelector('.dropdown-item');
|
||||||
if (dropdownItem) {
|
if (dropdownItem) {
|
||||||
const styles = window.getComputedStyle(dropdownItem);
|
const styles = window.getComputedStyle(dropdownItem);
|
||||||
console.log('.dropdown-item color:', styles.color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('=== End Elements Check ===');
|
|
||||||
}, 100);
|
}, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import base64
|
import base64
|
||||||
|
import logging
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import io
|
import io
|
||||||
import uuid
|
import uuid
|
||||||
@ -13,6 +14,9 @@ from copy import deepcopy
|
|||||||
from typing import Dict, List, Optional, Any
|
from typing import Dict, List, Optional, Any
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from config import (
|
from config import (
|
||||||
MAX_CONTEXT_SIZE,
|
MAX_CONTEXT_SIZE,
|
||||||
@ -198,23 +202,16 @@ class TokenMixin:
|
|||||||
def safe_broadcast_token_update(self):
|
def safe_broadcast_token_update(self):
|
||||||
"""安全的token更新广播(只广播累计统计,不重新计算)"""
|
"""安全的token更新广播(只广播累计统计,不重新计算)"""
|
||||||
try:
|
try:
|
||||||
print(f"[Debug] 尝试广播token更新")
|
|
||||||
|
|
||||||
# 检查是否有回调函数
|
# 检查是否有回调函数
|
||||||
if not hasattr(self, '_web_terminal_callback'):
|
if not hasattr(self, '_web_terminal_callback'):
|
||||||
print(f"[Debug] 没有_web_terminal_callback属性")
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if not self._web_terminal_callback:
|
if not self._web_terminal_callback:
|
||||||
print(f"[Debug] _web_terminal_callback为None")
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if not self.current_conversation_id:
|
if not self.current_conversation_id:
|
||||||
print(f"[Debug] 没有当前对话ID")
|
|
||||||
return
|
return
|
||||||
|
|
||||||
print(f"[Debug] 广播token统计,对话ID: {self.current_conversation_id}")
|
|
||||||
|
|
||||||
# 只获取已有的累计token统计,不重新计算
|
# 只获取已有的累计token统计,不重新计算
|
||||||
cumulative_stats = self.get_conversation_token_statistics()
|
cumulative_stats = self.get_conversation_token_statistics()
|
||||||
|
|
||||||
@ -228,14 +225,8 @@ class TokenMixin:
|
|||||||
'updated_at': datetime.now().isoformat()
|
'updated_at': datetime.now().isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
print(f"[Debug] Token统计: 累计输入={broadcast_data['cumulative_input_tokens']}, 累计输出={broadcast_data['cumulative_output_tokens']}")
|
|
||||||
|
|
||||||
# 广播到前端
|
# 广播到前端
|
||||||
self._web_terminal_callback('token_update', broadcast_data)
|
self._web_terminal_callback('token_update', broadcast_data)
|
||||||
|
|
||||||
print(f"[Debug] token更新已广播")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[Debug] 广播token更新失败: {e}")
|
logger.warning(f"[TokenStats] 广播token更新失败: {e}")
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user