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 },
|
||||
"search": { "tavily_api_key": "", "tavily_api_key_2": "" },
|
||||
"balance": {
|
||||
"aliyun_access_key_id": "",
|
||||
"aliyun_access_key_secret": "",
|
||||
"usd_cny_rate": 7.1
|
||||
},
|
||||
"env_vars": {
|
||||
"API_BASE_KIMI": "https://api.moonshot.cn/v1",
|
||||
"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) |
|
||||
| `models.default_response_max_tokens` | `AGENT_DEFAULT_RESPONSE_MAX_TOKENS` | 单轮响应 token 全局上限 |
|
||||
| `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、沙箱参数等) |
|
||||
|
||||
> ⚠️ **以下字段已弃用/无效**,存在于旧配置中可删除:
|
||||
|
||||
@ -74,9 +74,6 @@ def _load_dotenv():
|
||||
"secrets.api_token_secret": "API_TOKEN_SECRET",
|
||||
"search.tavily_api_key": "AGENT_TAVILY_API_KEY",
|
||||
"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",
|
||||
}
|
||||
for dotted_key, env_name in _LEGACY_MAP.items():
|
||||
|
||||
@ -823,7 +823,7 @@ class MainTerminalCommandMixin:
|
||||
if not profile.get("fast_only"):
|
||||
self._mode_before_fast_only = None
|
||||
try:
|
||||
logger.info(
|
||||
logger.debug(
|
||||
"[ModelSwitch] model=%s run_mode=%s thinking=%s fast_only=%s thinking_only=%s",
|
||||
self.model_key,
|
||||
self.run_mode,
|
||||
|
||||
@ -153,24 +153,13 @@ class ToolsDefinitionMainMixin:
|
||||
if tool.get("function", {}).get("name") not in self.disabled_tools
|
||||
]
|
||||
|
||||
# 调试日志:记录工具列表
|
||||
# 调试日志:记录工具列表(DEBUG 级,不上终端)
|
||||
tool_names = [t.get("function", {}).get("name") for t in tools]
|
||||
logger.info("[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中")
|
||||
logger.debug("[define_tools] 可用工具列表: %s", tool_names)
|
||||
|
||||
if "manage_personalization" in tool_names:
|
||||
logger.info("[define_tools] manage_personalization 工具已启用")
|
||||
print("[DEBUG] manage_personalization 在可用工具列表中")
|
||||
logger.debug("[define_tools] manage_personalization 工具已启用")
|
||||
else:
|
||||
logger.warning("[define_tools] manage_personalization 工具未找到!disabled_tools=%s", self.disabled_tools)
|
||||
print(f"[DEBUG] manage_personalization 不在可用工具列表中!disabled_tools={self.disabled_tools}")
|
||||
logger.debug("[define_tools] manage_personalization 未启用: disabled_tools=%s", self.disabled_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:
|
||||
"""处理工具调用(添加参数预检查和改进错误处理)"""
|
||||
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:
|
||||
if hasattr(self, "_apply_execution_mode_to_runtime"):
|
||||
self._apply_execution_mode_to_runtime()
|
||||
@ -2176,13 +2176,12 @@ class MainTerminalToolsExecutionMixin:
|
||||
logger.exception("[handle_tool_call] 工具执行异常详情")
|
||||
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)
|
||||
|
||||
async def _execute_manage_personalization(self, arguments: Dict) -> Dict:
|
||||
"""执行个性化管理操作"""
|
||||
print(f"[DEBUG] _execute_manage_personalization 被调用: {arguments}")
|
||||
logger.info("[_execute_manage_personalization] 方法被调用: arguments=%s", arguments)
|
||||
logger.debug("[_execute_manage_personalization] 方法被调用: arguments=%s", arguments)
|
||||
action = arguments.get("action")
|
||||
logger.info("[_execute_manage_personalization] action=%s", action)
|
||||
|
||||
@ -2289,8 +2288,7 @@ class MainTerminalToolsExecutionMixin:
|
||||
|
||||
# 保存配置
|
||||
save_personalization_config(self.data_dir, config)
|
||||
print(f"[DEBUG] 配置已保存: field={field}, value={value}")
|
||||
logger.info("[_execute_manage_personalization] 配置已保存到文件")
|
||||
logger.info("[_execute_manage_personalization] 配置已保存到文件: field=%s", field)
|
||||
|
||||
# 重新加载配置到当前终端
|
||||
self.apply_personalization_preferences(config)
|
||||
|
||||
@ -6,6 +6,7 @@ from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional, Callable, TYPE_CHECKING
|
||||
import os
|
||||
from core.main_terminal import MainTerminal
|
||||
from server.utils_common import debug_log
|
||||
from utils.logger import setup_logger
|
||||
from modules.personalization_manager import load_personalization_config
|
||||
from modules.versioning_manager import ConversationVersioningManager, VersioningError
|
||||
@ -50,9 +51,9 @@ class WebTerminal(MainTerminal):
|
||||
bound_id = f"conv_{bound_id}"
|
||||
result = self.load_conversation(bound_id, restore_model=True)
|
||||
if result.get("success"):
|
||||
print(f"[WebTerminal] 已加载绑定对话: {bound_id}")
|
||||
debug_log(f"[WebTerminal] 已加载绑定对话: {bound_id}")
|
||||
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
|
||||
|
||||
latest_list = self.context_manager.get_conversation_list(limit=1, offset=0)
|
||||
@ -64,14 +65,14 @@ class WebTerminal(MainTerminal):
|
||||
if conv_id:
|
||||
result = self.load_conversation(conv_id, restore_model=False)
|
||||
if result.get("success"):
|
||||
print(f"[WebTerminal] 已加载最近对话: {conv_id}")
|
||||
debug_log(f"[WebTerminal] 已加载最近对话: {conv_id}")
|
||||
return
|
||||
|
||||
conversation_id = self.context_manager.start_new_conversation(
|
||||
project_path=self.project_path,
|
||||
thinking_mode=self.thinking_mode
|
||||
)
|
||||
print(f"[WebTerminal] 自动创建新对话: {conversation_id}")
|
||||
debug_log(f"[WebTerminal] 自动创建新对话: {conversation_id}")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@ -126,16 +127,16 @@ class WebTerminal(MainTerminal):
|
||||
# 让 run_command 与实时终端共享同一容器环境
|
||||
self.terminal_ops.attach_terminal_manager(self.terminal_manager)
|
||||
|
||||
print(f"[WebTerminal] 初始化完成,项目路径: {project_path}")
|
||||
print(f"[WebTerminal] 初始模式: {self.run_mode}")
|
||||
print(f"[WebTerminal] 对话管理已就绪")
|
||||
debug_log(f"[WebTerminal] 初始化完成,项目路径: {project_path}")
|
||||
debug_log(f"[WebTerminal] 初始模式: {self.run_mode}")
|
||||
debug_log(f"[WebTerminal] 对话管理已就绪")
|
||||
|
||||
# 设置token更新回调
|
||||
if message_callback is not None:
|
||||
self.context_manager._web_terminal_callback = message_callback
|
||||
print(f"[WebTerminal] 实时token统计已启用")
|
||||
debug_log(f"[WebTerminal] 实时token统计已启用")
|
||||
else:
|
||||
print(f"[WebTerminal] 警告:message_callback为None,无法启用实时token统计")
|
||||
logger.warning("[WebTerminal] message_callback为None,无法启用实时token统计")
|
||||
# ===========================================
|
||||
# 新增:对话管理相关方法(Web版本)
|
||||
# ===========================================
|
||||
|
||||
15
main.py
15
main.py
@ -27,7 +27,7 @@ class AgentSystem:
|
||||
async def initialize(self):
|
||||
"""初始化系统"""
|
||||
print("\n" + "="*50)
|
||||
print("ð¤ Astrion 启动")
|
||||
print("🤖 Astrion 启动")
|
||||
print("="*50)
|
||||
|
||||
# 1. 获取项目路径
|
||||
@ -123,25 +123,23 @@ class AgentSystem:
|
||||
project_path = Path(path_input).resolve()
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
print(f"{OUTPUT_FORMATS['success']} 已选择项目路径: {self.project_path}")
|
||||
print(f"{OUTPUT_FORMATS['info']} 默认工作区路径: {self.project_path}")
|
||||
|
||||
async def setup_run_mode(self):
|
||||
"""选择运行模式"""
|
||||
"""设置运行模式(当前统一入口默认 Web)"""
|
||||
self.web_mode = True
|
||||
print(f"{OUTPUT_FORMATS['info']} 运行模式: Web(默认)")
|
||||
|
||||
async def setup_thinking_mode(self):
|
||||
"""选择思考模式"""
|
||||
"""设置默认思考模式(对话中可随时切换)"""
|
||||
self.thinking_mode = True
|
||||
print(f"{OUTPUT_FORMATS['info']} 思考模式: {'开启' if self.thinking_mode else '关闭'}(默认)")
|
||||
|
||||
async def init_system(self):
|
||||
"""初始化系统文件"""
|
||||
@ -194,7 +192,6 @@ class AgentSystem:
|
||||
from web_server import run_server
|
||||
|
||||
print(f"\n{OUTPUT_FORMATS['success']} 正在启动Web服务器...")
|
||||
print(f"{OUTPUT_FORMATS['info']} 项目路径: {self.project_path}")
|
||||
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."""
|
||||
from .app import app, socketio, run_server, parse_arguments, initialize_system, resource_busy_page
|
||||
"""Web server package entry.
|
||||
|
||||
使用 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"]
|
||||
|
||||
|
||||
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,
|
||||
)
|
||||
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 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})
|
||||
|
||||
|
||||
@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>')
|
||||
@login_required
|
||||
@admin_required
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
# 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 json
|
||||
import os
|
||||
@ -21,7 +24,7 @@ import time
|
||||
from datetime import datetime
|
||||
from collections import defaultdict, deque, Counter
|
||||
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
|
||||
import server.state as state # 共享单例
|
||||
from server.auth import auth_bp
|
||||
@ -221,6 +224,7 @@ from config import (
|
||||
DEFAULT_CONVERSATIONS_LIMIT,
|
||||
MAX_CONVERSATIONS_LIMIT,
|
||||
CONVERSATIONS_DIR,
|
||||
DATA_DIR,
|
||||
DEFAULT_RESPONSE_MAX_TOKENS,
|
||||
DEFAULT_PROJECT_PATH,
|
||||
LOGS_DIR,
|
||||
@ -231,6 +235,7 @@ from config import (
|
||||
UPLOAD_SCAN_LOG_SUBDIR,
|
||||
WEB_SERVER_PORT,
|
||||
WEB_SERVER_HOST,
|
||||
TERMINAL_SANDBOX_MODE,
|
||||
)
|
||||
from modules.user_manager import UserManager, UserWorkspace
|
||||
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")
|
||||
if not _secret_key:
|
||||
_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['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=12)
|
||||
_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)
|
||||
with DEBUG_LOG_FILE.open('w', encoding='utf-8') as f:
|
||||
f.write(f"调试日志开始 - {datetime.now()}\n")
|
||||
f.write(f"项目路径: {path}\n")
|
||||
f.write(f"思考模式: {'思考模式' if thinking_mode else '快速模式'}\n")
|
||||
f.write(f"默认工作区路径: {path}\n")
|
||||
f.write(f"默认思考模式: {'思考' if thinking_mode else '快速'}(对话中可切换)\n")
|
||||
f.write(f"自动修复: {'开启' if AUTO_FIX_TOOL_CALL else '关闭'}\n")
|
||||
f.write(f"最大迭代: {MAX_ITERATIONS_PER_TASK}\n")
|
||||
f.write(f"最大工具调用: {MAX_TOTAL_TOOL_CALLS}\n")
|
||||
f.write("="*80 + "\n")
|
||||
print(f"[Init] 初始化Web系统...")
|
||||
print(f"[Init] 项目路径: {path}")
|
||||
print(f"[Init] 运行模式: {'思考模式(首次思考,后续快速)' if thinking_mode else '快速模式(无思考)'}")
|
||||
print(f"[Init] 自动修复: {'开启' if AUTO_FIX_TOOL_CALL else '关闭'}")
|
||||
print(f"[Init] 调试日志: {DEBUG_LOG_FILE}")
|
||||
print(f"{OUTPUT_FORMATS['info']} 初始化 Web 系统...")
|
||||
print(f"{OUTPUT_FORMATS['info']} 数据目录: {DATA_DIR}")
|
||||
print(f"{OUTPUT_FORMATS['info']} 调试日志: {DEBUG_LOG_FILE}")
|
||||
app.config['DEFAULT_THINKING_MODE'] = thinking_mode
|
||||
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']} 预设子智能体角色同步完成")
|
||||
except Exception as _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):
|
||||
@ -1276,7 +1280,7 @@ def parse_arguments():
|
||||
parser.add_argument(
|
||||
"--path",
|
||||
default=str(Path(DEFAULT_PROJECT_PATH).resolve()),
|
||||
help="项目工作目录(默认使用 config.DEFAULT_PROJECT_PATH)"
|
||||
help="默认工作区路径(仅作兜底,工作区可在界面中管理)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--port",
|
||||
@ -1292,7 +1296,7 @@ def parse_arguments():
|
||||
parser.add_argument(
|
||||
"--thinking-mode",
|
||||
action="store_true",
|
||||
help="启用思考模式(首次请求使用 reasoning)"
|
||||
help="新对话默认使用思考模式(对话中可随时切换)"
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
from server.chat import chat_bp
|
||||
import json, time
|
||||
import json, logging, time
|
||||
from datetime import datetime
|
||||
from typing import Dict, Any, Optional
|
||||
from pathlib import Path
|
||||
@ -8,6 +8,8 @@ from io import BytesIO
|
||||
import zipfile
|
||||
import os
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
from flask import Blueprint, jsonify, request, session, send_file
|
||||
from werkzeug.utils import secure_filename
|
||||
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)
|
||||
)
|
||||
except Exception as exc:
|
||||
print(f"[API] 保存思考模式到对话失败: {exc}")
|
||||
logger.error(f"[API] 保存思考模式到对话失败: {exc}")
|
||||
|
||||
status = terminal.get_status()
|
||||
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:
|
||||
print(f"[API] 切换思考模式失败: {exc}")
|
||||
logger.error(f"[API] 切换思考模式失败: {exc}")
|
||||
code = 400 if isinstance(exc, ValueError) else 500
|
||||
return jsonify({
|
||||
"success": False,
|
||||
@ -178,7 +180,7 @@ def update_reasoning_effort(terminal: WebTerminal, workspace: UserWorkspace, use
|
||||
reasoning_effort=effort,
|
||||
)
|
||||
except Exception as exc:
|
||||
print(f"[API] 保存推理强度到对话失败: {exc}")
|
||||
logger.error(f"[API] 保存推理强度到对话失败: {exc}")
|
||||
|
||||
status = terminal.get_status()
|
||||
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:
|
||||
print(f"[API] 设置推理强度失败: {exc}")
|
||||
logger.error(f"[API] 设置推理强度失败: {exc}")
|
||||
code = 400 if isinstance(exc, ValueError) else 500
|
||||
return jsonify({
|
||||
"success": False,
|
||||
@ -253,7 +255,7 @@ def update_model(terminal: WebTerminal, workspace: UserWorkspace, username: str)
|
||||
has_videos=getattr(ctx, "has_videos", False)
|
||||
)
|
||||
except Exception as exc:
|
||||
print(f"[API] 保存模型到对话失败: {exc}")
|
||||
logger.error(f"[API] 保存模型到对话失败: {exc}")
|
||||
else:
|
||||
print(
|
||||
f"[API] 跳过模型保存:请求对话 {requested_cid} "
|
||||
@ -272,7 +274,7 @@ def update_model(terminal: WebTerminal, workspace: UserWorkspace, username: str)
|
||||
}
|
||||
})
|
||||
except Exception as exc:
|
||||
print(f"[API] 切换模型失败: {exc}")
|
||||
logger.error(f"[API] 切换模型失败: {exc}")
|
||||
code = 400 if isinstance(exc, ValueError) else 500
|
||||
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}")
|
||||
|
||||
# 原有的错误处理逻辑
|
||||
print(f"[Task] 错误: {e}")
|
||||
debug_log(f"任务处理错误: {e}")
|
||||
import traceback
|
||||
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_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_task_support import wait_retry_delay, cancel_pending_tools
|
||||
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
|
||||
):
|
||||
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', {
|
||||
'id': tool_id,
|
||||
'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)
|
||||
if 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', {
|
||||
'id': tool_id,
|
||||
'name': tool_name,
|
||||
@ -249,7 +249,7 @@ async def run_streaming_attempts(*, web_terminal, messages, tools, sender, clien
|
||||
text_started = True
|
||||
text_streaming = True
|
||||
sender('text_start', {})
|
||||
brief_log("模型输出了内容")
|
||||
debug_log("模型输出了内容")
|
||||
await asyncio.sleep(0.05)
|
||||
|
||||
full_response += content
|
||||
|
||||
@ -1915,7 +1915,7 @@ async def handle_task_with_sender(
|
||||
return
|
||||
|
||||
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(
|
||||
web_terminal=web_terminal,
|
||||
|
||||
@ -7,7 +7,7 @@ import uuid
|
||||
from pathlib import Path
|
||||
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 tool_approval_manager, user_question_manager
|
||||
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,
|
||||
'conversation_id': conversation_id
|
||||
})
|
||||
brief_log(f"调用了工具: {function_name}")
|
||||
debug_log(f"调用了工具: {function_name}")
|
||||
|
||||
await asyncio.sleep(0.3)
|
||||
start_time = time.time()
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
import sys, os
|
||||
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
|
||||
if PROJECT_ROOT not in sys.path:
|
||||
sys.path.insert(0, PROJECT_ROOT)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
import asyncio, json, time, re, os, shutil
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
@ -579,7 +582,7 @@ def get_conversations(terminal: WebTerminal, workspace: UserWorkspace, username:
|
||||
}), 500
|
||||
|
||||
except Exception as e:
|
||||
print(f"[API] 获取对话列表错误: {e}")
|
||||
logger.error(f"[API] 获取对话列表错误: {e}")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
@ -754,7 +757,7 @@ def create_conversation(terminal: WebTerminal, workspace: UserWorkspace, usernam
|
||||
return jsonify(result), 500
|
||||
|
||||
except Exception as e:
|
||||
print(f"[API] 创建对话错误: {e}")
|
||||
logger.error(f"[API] 创建对话错误: {e}")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
@ -793,7 +796,7 @@ def get_conversation_info(terminal: WebTerminal, workspace: UserWorkspace, usern
|
||||
}), 404
|
||||
|
||||
except Exception as e:
|
||||
print(f"[API] 获取对话信息错误: {e}")
|
||||
logger.error(f"[API] 获取对话信息错误: {e}")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
@ -903,7 +906,7 @@ def load_conversation(conversation_id, terminal: WebTerminal, workspace: UserWor
|
||||
|
||||
except Exception as e:
|
||||
import traceback
|
||||
print(f"[API] 加载对话错误: {e}")
|
||||
logger.error(f"[API] 加载对话错误: {e}")
|
||||
traceback.print_exc()
|
||||
return jsonify({
|
||||
"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
|
||||
|
||||
except Exception as e:
|
||||
print(f"[API] 删除对话错误: {e}")
|
||||
logger.error(f"[API] 删除对话错误: {e}")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
@ -1033,12 +1036,12 @@ def search_conversations(terminal: WebTerminal, workspace: UserWorkspace, userna
|
||||
username, ws_id, terminal, workspace
|
||||
)
|
||||
except (ValueError, RuntimeError) as resolve_err:
|
||||
print(f"[API] 搜索跳过工作区 {ws_id}: {resolve_err}")
|
||||
logger.error(f"[API] 搜索跳过工作区 {ws_id}: {resolve_err}")
|
||||
continue
|
||||
try:
|
||||
result = target_terminal.search_conversations(query, limit, multi_agent_mode=multi_agent_filter)
|
||||
except Exception as search_err:
|
||||
print(f"[API] 搜索工作区对话失败 {ws_id}: {search_err}")
|
||||
logger.error(f"[API] 搜索工作区对话失败 {ws_id}: {search_err}")
|
||||
continue
|
||||
results = result.get("results") or []
|
||||
if not results:
|
||||
@ -1061,7 +1064,7 @@ def search_conversations(terminal: WebTerminal, workspace: UserWorkspace, userna
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
print(f"[API] 搜索对话错误: {e}")
|
||||
logger.error(f"[API] 搜索对话错误: {e}")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
@ -1124,7 +1127,7 @@ def get_conversation_messages(conversation_id, terminal: WebTerminal, workspace:
|
||||
}), 404
|
||||
|
||||
except Exception as e:
|
||||
print(f"[API] 获取对话消息错误: {e}")
|
||||
logger.error(f"[API] 获取对话消息错误: {e}")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
@ -1763,7 +1766,7 @@ def compress_conversation(conversation_id, terminal: WebTerminal, workspace: Use
|
||||
return jsonify(response_payload)
|
||||
|
||||
except Exception as e:
|
||||
print(f"[API] 压缩对话错误: {e}")
|
||||
logger.error(f"[API] 压缩对话错误: {e}")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
@ -2208,7 +2211,7 @@ def duplicate_conversation(conversation_id, terminal: WebTerminal, workspace: Us
|
||||
return jsonify(response_payload)
|
||||
|
||||
except Exception as e:
|
||||
print(f"[API] 复制对话错误: {e}")
|
||||
logger.error(f"[API] 复制对话错误: {e}")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
@ -2251,7 +2254,7 @@ def review_conversation_preview(conversation_id, terminal: WebTerminal, workspac
|
||||
}
|
||||
})
|
||||
except Exception as e:
|
||||
print(f"[API] 对话回顾预览错误: {e}")
|
||||
logger.error(f"[API] 对话回顾预览错误: {e}")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
@ -2307,7 +2310,7 @@ def review_conversation(conversation_id, terminal: WebTerminal, workspace: UserW
|
||||
}
|
||||
})
|
||||
except Exception as e:
|
||||
print(f"[API] 对话回顾生成错误: {e}")
|
||||
logger.error(f"[API] 对话回顾生成错误: {e}")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
@ -2328,7 +2331,7 @@ def get_conversations_statistics(terminal: WebTerminal, workspace: UserWorkspace
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
print(f"[API] 获取对话统计错误: {e}")
|
||||
logger.error(f"[API] 获取对话统计错误: {e}")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
@ -2377,7 +2380,7 @@ def get_current_conversation(terminal: WebTerminal, workspace: UserWorkspace, us
|
||||
}), 404
|
||||
|
||||
except Exception as e:
|
||||
print(f"[API] 获取当前对话错误: {e}")
|
||||
logger.error(f"[API] 获取当前对话错误: {e}")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": str(e)
|
||||
@ -2490,7 +2493,7 @@ def get_conversation_token_statistics(conversation_id, terminal: WebTerminal, wo
|
||||
}), 404
|
||||
|
||||
except Exception as e:
|
||||
print(f"[API] 获取token统计错误: {e}")
|
||||
logger.error(f"[API] 获取token统计错误: {e}")
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
from collections import Counter, deque
|
||||
@ -8,6 +9,8 @@ from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
from modules.user_manager import UserWorkspace
|
||||
from modules.usage_tracker import QUOTA_DEFAULTS
|
||||
from utils.conversation_manager import ConversationManager
|
||||
@ -131,7 +134,7 @@ def _read_token_totals_file(workspace: UserWorkspace) -> Dict[str, int]:
|
||||
"total_tokens": total_tokens,
|
||||
}
|
||||
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}
|
||||
|
||||
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,
|
||||
}
|
||||
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}
|
||||
|
||||
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')
|
||||
def handle_connect(auth):
|
||||
"""客户端连接"""
|
||||
print(f"[WebSocket] 客户端连接: {request.sid}")
|
||||
debug_log(f"[WebSocket] 客户端连接: {request.sid}")
|
||||
username = get_current_username()
|
||||
token_value = (auth or {}).get('socket_token') if isinstance(auth, dict) else None
|
||||
if not username or not consume_socket_token(token_value, username):
|
||||
@ -75,7 +75,7 @@ def handle_connect(auth):
|
||||
@socketio.on('disconnect')
|
||||
def handle_disconnect():
|
||||
"""客户端断开"""
|
||||
print(f"[WebSocket] 客户端断开: {request.sid}")
|
||||
debug_log(f"[WebSocket] 客户端断开: {request.sid}")
|
||||
username = connection_users.pop(request.sid, None)
|
||||
# 若同一用户仍有其他活跃连接,不因断开而停止任务
|
||||
has_other_connection = False
|
||||
@ -127,7 +127,7 @@ def handle_disconnect():
|
||||
@socketio.on('stop_task')
|
||||
def handle_stop_task():
|
||||
"""处理停止任务请求"""
|
||||
print(f"[停止] 收到停止请求: {request.sid}")
|
||||
debug_log(f"[停止] 收到停止请求: {request.sid}")
|
||||
username = connection_users.get(request.sid)
|
||||
task_info = get_stop_flag(request.sid, username)
|
||||
if not isinstance(task_info, dict):
|
||||
@ -172,7 +172,7 @@ def handle_terminal_subscribe(data):
|
||||
room_name = f"user_{username}_terminal"
|
||||
join_room(room_name)
|
||||
terminal_rooms[request.sid].add(room_name)
|
||||
print(f"[Terminal] {request.sid} 订阅所有终端事件")
|
||||
debug_log(f"[Terminal] {request.sid} 订阅所有终端事件")
|
||||
|
||||
# 发送当前终端状态
|
||||
emit('terminal_subscribed', {
|
||||
@ -184,7 +184,7 @@ def handle_terminal_subscribe(data):
|
||||
room_name = f'user_{username}_terminal_{session_name}'
|
||||
join_room(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)
|
||||
@ -205,7 +205,7 @@ def handle_terminal_unsubscribe(data):
|
||||
leave_room(room_name)
|
||||
if request.sid in terminal_rooms:
|
||||
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')
|
||||
def handle_get_terminal_output(data):
|
||||
@ -279,7 +279,7 @@ def handle_message(data):
|
||||
emit('error', {'message': '图片和视频请分开发送'})
|
||||
return
|
||||
|
||||
print(f"[WebSocket] 收到消息: {message}")
|
||||
debug_log(f"[WebSocket] 收到消息: {message}")
|
||||
debug_log(f"\n{'='*80}\n新任务开始: {message}\n{'='*80}")
|
||||
record_user_activity(username)
|
||||
|
||||
|
||||
@ -272,55 +272,6 @@
|
||||
</li>
|
||||
</ul>
|
||||
</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">
|
||||
<h2>邀请码</h2>
|
||||
<div class="stats-row">
|
||||
@ -478,7 +429,7 @@ import { useSecondaryPass } from './useSecondaryPass';
|
||||
|
||||
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 {
|
||||
verified: secondaryVerified,
|
||||
@ -497,10 +448,6 @@ const bannerError = ref<string | null>(null);
|
||||
const autoRefresh = ref(true);
|
||||
const activeSection = ref<SectionId>('overview');
|
||||
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 inviteError = ref<string | null>(null);
|
||||
const newInviteCode = ref('');
|
||||
@ -522,7 +469,6 @@ const sectionTabs: Array<{ id: SectionId; label: string }> = [
|
||||
{ id: 'users', label: '用户' },
|
||||
{ id: 'containers', label: '容器' },
|
||||
{ id: 'uploads', label: '上传审计' },
|
||||
{ id: 'balance', label: '余额查询' },
|
||||
{ id: 'invites', 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 () => {
|
||||
await verifySecondary(secondaryPassword.value);
|
||||
if (secondaryVerified.value) {
|
||||
@ -819,15 +748,6 @@ watch(autoRefresh, () => {
|
||||
scheduleAutoRefresh();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => activeSection.value,
|
||||
(val) => {
|
||||
if (val === 'balance' && !balanceData.value && !balanceLoading.value) {
|
||||
fetchBalance();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
document.addEventListener('click', handleClickOutside);
|
||||
await checkSecondary();
|
||||
@ -891,36 +811,6 @@ const tokenBreakdown = computed(() => {
|
||||
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(() => [
|
||||
{
|
||||
title: '注册用户',
|
||||
@ -1593,70 +1483,6 @@ th {
|
||||
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 {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
@ -229,35 +229,6 @@
|
||||
</div>
|
||||
</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>
|
||||
</template>
|
||||
|
||||
@ -287,9 +258,6 @@ const creating = ref(false);
|
||||
const createForm = ref({ username: '', note: '' });
|
||||
const createError = ref('');
|
||||
|
||||
const balanceData = ref<Record<string, any> | null>(null);
|
||||
const balanceLoading = ref(false);
|
||||
|
||||
const overview = computed(() => snapshot.value?.overview || {});
|
||||
const uploads = computed(() => snapshot.value?.uploads || {});
|
||||
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 num = Number(val || 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 () => {
|
||||
await verifySecondary(secondaryPassword.value);
|
||||
if (secondaryVerified.value) {
|
||||
@ -754,37 +691,6 @@ td {
|
||||
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 {
|
||||
color: #b5473d;
|
||||
margin-top: 8px;
|
||||
|
||||
@ -62,17 +62,16 @@ function debugShowImageLog(event: string, payload: Record<string, any> = {}) {
|
||||
}
|
||||
|
||||
function isShowHtmlDebugEnabled() {
|
||||
if (typeof window === 'undefined') return true;
|
||||
if (typeof window === 'undefined') return false;
|
||||
try {
|
||||
const explicitFlag = (window as any).__SHOW_HTML_DEBUG__;
|
||||
if (explicitFlag === false || explicitFlag === '0') return false;
|
||||
if (explicitFlag === true || explicitFlag === '1') return true;
|
||||
if (explicitFlag === false || explicitFlag === '0') return false;
|
||||
const localFlag = window.localStorage?.getItem('showHtmlDebug');
|
||||
if (localFlag === '0' || localFlag === 'false') return false;
|
||||
if (localFlag === '1' || localFlag === 'true') return true;
|
||||
return true;
|
||||
return false;
|
||||
} catch {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1042,7 +1041,8 @@ function getShowTagContainer() {
|
||||
}
|
||||
|
||||
function isLayoutDebugEnabled() {
|
||||
if (typeof window === 'undefined') return true;
|
||||
// 默认关闭:排障时通过 window.__LAYOUT_DEBUG__ = true 或 localStorage.layoutDebug = '1' 显式打开
|
||||
if (typeof window === 'undefined') return false;
|
||||
try {
|
||||
const explicitFlag = (window as any).__LAYOUT_DEBUG__;
|
||||
if (explicitFlag === false || explicitFlag === '0') return false;
|
||||
@ -1050,9 +1050,9 @@ function isLayoutDebugEnabled() {
|
||||
const localFlag = window.localStorage?.getItem('layoutDebug');
|
||||
if (localFlag === '0' || localFlag === 'false') return false;
|
||||
if (localFlag === '1' || localFlag === 'true') return true;
|
||||
return true;
|
||||
return false;
|
||||
} catch {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -23,7 +23,6 @@ export function created() {
|
||||
|
||||
export async function mounted() {
|
||||
debugLog('Vue应用已挂载');
|
||||
console.log('[DEBUG_SYSTEM][boot] 前端调试版本已加载 2026-04-05');
|
||||
if (window.ensureCsrfToken) {
|
||||
window.ensureCsrfToken().catch((err) => {
|
||||
console.warn('CSRF token 初始化失败:', err);
|
||||
|
||||
@ -25,8 +25,6 @@ export const traceLog = (...args) => {
|
||||
function isGoalModeDebugEnabled() {
|
||||
if (typeof window === 'undefined') return false;
|
||||
try {
|
||||
// 临时强制开启,用于调试多智能体消息轮询问题
|
||||
return true;
|
||||
const explicit = (window as any).__GOAL_MODE_DEBUG__;
|
||||
if (explicit === true || explicit === '1') return true;
|
||||
if (explicit === false || explicit === '0') return false;
|
||||
|
||||
@ -17,7 +17,6 @@ export const actionMethods = {
|
||||
}
|
||||
},
|
||||
async createNewConversation() {
|
||||
console.log('[DEBUG_AWAITING] ===== createNewConversation =====');
|
||||
if (this.compressionInProgress || this.compressing) {
|
||||
const confirmed = await this.confirmAction({
|
||||
title: '压缩进行中',
|
||||
|
||||
@ -7,7 +7,6 @@ import {
|
||||
|
||||
export const stateMethods = {
|
||||
resetAllStates(reason = 'unspecified', options: { preserveMonitorWindows?: boolean } = {}) {
|
||||
console.log('[DEBUG_AWAITING] ===== resetAllStates =====', { reason });
|
||||
|
||||
// 如果正在等待子智能体完成,不重置任务状态
|
||||
if (this.waitingForSubAgent) {
|
||||
@ -74,11 +73,6 @@ export const stateMethods = {
|
||||
generatingLabel: m.generatingLabel
|
||||
}));
|
||||
|
||||
console.log('[DEBUG_AWAITING] resetAllStates 清理完成', {
|
||||
before: assistantMsgsBefore,
|
||||
after: assistantMsgsAfter
|
||||
});
|
||||
|
||||
// 清理Markdown缓存
|
||||
if (this.markdownCache) {
|
||||
this.markdownCache.clear();
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
import { debugLog } from './common';
|
||||
import { useQuickDockStore } from '../../stores/quickDock';
|
||||
const jsonDebug = (...args: any[]) => {
|
||||
console.log('[JSONDEBUG]', ...args);
|
||||
};
|
||||
const RESTORE_DEBUG_PREFIX = '[RESTORE_DEBUG]';
|
||||
const RESTORE_DEBUG_EVENTS = new Set([
|
||||
@ -12,17 +11,16 @@ const RESTORE_DEBUG_EVENTS = new Set([
|
||||
]);
|
||||
|
||||
function isRestoreDebugEnabled() {
|
||||
if (typeof window === 'undefined') return true;
|
||||
if (typeof window === 'undefined') return false;
|
||||
try {
|
||||
const explicit = (window as any).__RESTORE_DEBUG__;
|
||||
if (explicit === false || explicit === '0') return false;
|
||||
if (explicit === true || explicit === '1') return true;
|
||||
if (explicit === false || explicit === '0') return false;
|
||||
const localFlag = window.localStorage?.getItem('restoreDebug');
|
||||
if (localFlag === '0' || localFlag === 'false') return false;
|
||||
if (localFlag === '1' || localFlag === 'true') return true;
|
||||
return true;
|
||||
return false;
|
||||
} catch {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -459,10 +457,6 @@ export const historyMethods = {
|
||||
if (message.role === 'system') {
|
||||
const rawContent = message.content || '';
|
||||
const label = parseSystemNoticeLabel(rawContent);
|
||||
console.log('[DEBUG_SYSTEM][history] 命中 role=system 历史消息', {
|
||||
rawContent,
|
||||
matchedDoneNotice: !!label
|
||||
});
|
||||
if (label) {
|
||||
// 历史中的 system 通知转换为 assistant system action,避免被 role=system 过滤掉
|
||||
this.messages.push({
|
||||
@ -485,9 +479,6 @@ export const historyMethods = {
|
||||
generatingLabel: ''
|
||||
});
|
||||
} else {
|
||||
console.log('[DEBUG_SYSTEM][history] role=system 被历史加载拦截(非子智能体完成)', {
|
||||
rawContent
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -97,7 +97,6 @@ export const sendMethods = {
|
||||
}
|
||||
},
|
||||
async sendMessage(options = {}) {
|
||||
console.log('[DEBUG_AWAITING] ===== sendMessage =====');
|
||||
const presetText = typeof options?.presetText === 'string' ? options.presetText : null;
|
||||
const usePresetText = presetText !== null;
|
||||
|
||||
@ -417,7 +416,6 @@ export const sendMethods = {
|
||||
return true;
|
||||
},
|
||||
async stopTask() {
|
||||
console.log('[DEBUG_AWAITING] ===== stopTask =====');
|
||||
if (this._stopTaskRunning) {
|
||||
goalModeDebugLog('stopTask:debounce-rejected', { stopRequested: this.stopRequested });
|
||||
return;
|
||||
|
||||
@ -21,22 +21,8 @@ import {
|
||||
export const aiStreamMethods = {
|
||||
handleAiMessageStart(data: any, eventIdx: number) {
|
||||
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);
|
||||
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) {
|
||||
this.waitingForSubAgent = false;
|
||||
@ -45,13 +31,6 @@ export const aiStreamMethods = {
|
||||
// 检查是否已经有 assistant 消息
|
||||
const hasAssistantMessage = lastMessage && lastMessage.role === 'assistant';
|
||||
|
||||
console.log('[AiMessageStart] 最后一条消息状态', {
|
||||
hasAssistantMessage,
|
||||
lastMessageActionsLength: lastMessage?.actions?.length || 0,
|
||||
lastMessageAwaitingFirstContent: lastMessage?.awaitingFirstContent,
|
||||
lastMessageGeneratingLabel: lastMessage?.generatingLabel
|
||||
});
|
||||
|
||||
// 判断是否是刷新恢复的情况:
|
||||
// 1. 有assistant消息
|
||||
// 2. 且最后一条 assistant 仍处于“未完成流式状态”(而不是普通已完成消息)
|
||||
@ -83,16 +62,7 @@ export const aiStreamMethods = {
|
||||
isRefreshRestore
|
||||
});
|
||||
|
||||
console.log('[AiMessageStart] 场景判断', {
|
||||
isRefreshRestore,
|
||||
streamingMessage: this.streamingMessage,
|
||||
hasActions: !!(lastMessage?.actions && lastMessage.actions.length > 0),
|
||||
awaitingFirstContent: lastMessage?.awaitingFirstContent,
|
||||
taskInProgress: this.taskInProgress
|
||||
});
|
||||
|
||||
if (isRefreshRestore) {
|
||||
console.log('[AiMessageStart] 场景=刷新恢复,复用现有assistant消息');
|
||||
debugLog('[TaskPolling] 刷新恢复场景,复用现有 assistant 消息');
|
||||
// 只更新状态,不创建新消息
|
||||
this.taskInProgress = true;
|
||||
@ -105,25 +75,18 @@ export const aiStreamMethods = {
|
||||
// 从头重建时,restoreTaskState 已根据事件流分析(hasAssistantContentEvent)
|
||||
// 正确设置了 awaitingFirstContent,此处不覆盖。
|
||||
// actions 被清空不代表没有内容,事件重放会恢复。
|
||||
console.log('[AiMessageStart] 从头重建,保留 awaitingFirstContent:', lastMessage.awaitingFirstContent);
|
||||
} else if (!hasContent) {
|
||||
lastMessage.awaitingFirstContent = true;
|
||||
lastMessage.generatingLabel = lastMessage.generatingLabel || '思考中...';
|
||||
console.log('[AiMessageStart] 设置等待动画', {
|
||||
awaitingFirstContent: true,
|
||||
generatingLabel: lastMessage.generatingLabel
|
||||
});
|
||||
} else {
|
||||
// 如果已有内容,确保等待动画不显示
|
||||
lastMessage.awaitingFirstContent = false;
|
||||
lastMessage.generatingLabel = '';
|
||||
console.log('[AiMessageStart] 关闭等待动画(已有内容)');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 其他情况:创建新的 assistant 消息
|
||||
console.log('[AiMessageStart] 场景=创建新消息');
|
||||
debugLog('[TaskPolling] 创建新的 assistant 消息');
|
||||
this.monitorResetSpeech();
|
||||
this.cleanupStaleToolActions();
|
||||
@ -133,13 +96,6 @@ export const aiStreamMethods = {
|
||||
this.streamingMessage = true;
|
||||
|
||||
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) {
|
||||
@ -147,33 +103,27 @@ export const aiStreamMethods = {
|
||||
debugLog('[TaskPolling] 标记消息为静默恢复(从头重建)');
|
||||
newMessage.awaitingFirstContent = false;
|
||||
newMessage.generatingLabel = '';
|
||||
console.log('[AiMessageStart] 从头重建,关闭等待动画');
|
||||
}
|
||||
}
|
||||
|
||||
// 强制触发Vue响应式更新
|
||||
this.$forceUpdate();
|
||||
console.log('[AiMessageStart] 已调用$forceUpdate');
|
||||
|
||||
this.$nextTick(() => {
|
||||
console.log('[AiMessageStart] nextTick回调执行');
|
||||
this.conditionalScrollToBottom();
|
||||
});
|
||||
},
|
||||
handleThinkingStart(data: any, eventIdx: number) {
|
||||
console.log('[ThinkingStart] 开始处理', { eventIdx });
|
||||
debugLog('[TaskPolling] 思考开始, idx:', eventIdx);
|
||||
const ignoreThinking = this.runMode === 'fast' || this.thinkingMode === false;
|
||||
if (ignoreThinking) {
|
||||
this.monitorEndModelOutput();
|
||||
console.log('[ThinkingStart] 忽略思考(fast模式或关闭思考)');
|
||||
return;
|
||||
}
|
||||
|
||||
// 防御性检查:如果没有assistant消息,先创建一个
|
||||
const lastMessage = this.messages[this.messages.length - 1];
|
||||
if (!lastMessage || lastMessage.role !== 'assistant') {
|
||||
console.log('[DEBUG_AWAITING] ThinkingStart 检测到缺少assistant消息,自动创建');
|
||||
this.chatStartAssistantMessage();
|
||||
}
|
||||
|
||||
@ -188,10 +138,6 @@ export const aiStreamMethods = {
|
||||
|
||||
// 有内容了,关闭等待动画
|
||||
if (lastMessage && lastMessage.role === 'assistant') {
|
||||
console.log('[ThinkingStart] 关闭等待动画', {
|
||||
beforeAwaitingFirstContent: lastMessage.awaitingFirstContent,
|
||||
beforeGeneratingLabel: lastMessage.generatingLabel
|
||||
});
|
||||
lastMessage.awaitingFirstContent = false;
|
||||
lastMessage.generatingLabel = '';
|
||||
}
|
||||
@ -267,13 +213,11 @@ export const aiStreamMethods = {
|
||||
this.monitorEndModelOutput();
|
||||
},
|
||||
handleTextStart() {
|
||||
console.log('[TextStart] 开始处理');
|
||||
debugLog('[TaskPolling] 文本开始');
|
||||
|
||||
// 防御性检查:如果没有assistant消息,先创建一个
|
||||
const lastMessage = this.messages[this.messages.length - 1];
|
||||
if (!lastMessage || lastMessage.role !== 'assistant') {
|
||||
console.log('[DEBUG_AWAITING] TextStart 检测到缺少assistant消息,自动创建');
|
||||
this.chatStartAssistantMessage();
|
||||
}
|
||||
|
||||
@ -282,10 +226,6 @@ export const aiStreamMethods = {
|
||||
// 有内容了,关闭等待动画
|
||||
const currentMessage = this.messages[this.messages.length - 1];
|
||||
if (currentMessage && currentMessage.role === 'assistant') {
|
||||
console.log('[TextStart] 关闭等待动画', {
|
||||
beforeAwaitingFirstContent: currentMessage.awaitingFirstContent,
|
||||
beforeGeneratingLabel: currentMessage.generatingLabel
|
||||
});
|
||||
currentMessage.awaitingFirstContent = false;
|
||||
currentMessage.generatingLabel = '';
|
||||
}
|
||||
|
||||
@ -94,12 +94,6 @@ export const lifecycleMethods = {
|
||||
currentConversationId: this.currentConversationId
|
||||
});
|
||||
}
|
||||
console.log(`[DEBUG_AWAITING] 忽略不匹配的事件`, {
|
||||
eventType,
|
||||
eventIdx,
|
||||
eventConversationId: eventData.conversation_id,
|
||||
currentConversationId: this.currentConversationId
|
||||
});
|
||||
debugLog(
|
||||
`[TaskPolling] 忽略不匹配的事件 #${eventIdx}: ${eventType}, 事件对话=${eventData.conversation_id}, 当前对话=${this.currentConversationId}`
|
||||
);
|
||||
@ -176,13 +170,6 @@ export const lifecycleMethods = {
|
||||
|
||||
debugLog(`[TaskPolling] 处理事件 #${eventIdx}: ${eventType}`, eventData);
|
||||
|
||||
console.log(`[DEBUG_AWAITING] 处理事件`, {
|
||||
eventType,
|
||||
eventIdx,
|
||||
eventConversationId: eventData.conversation_id,
|
||||
currentConversationId: this.currentConversationId
|
||||
});
|
||||
|
||||
// 根据事件类型调用对应的处理方法
|
||||
switch (eventType) {
|
||||
case 'ai_message_start':
|
||||
|
||||
@ -8,13 +8,10 @@ export const debugNotifyLog = (...args: any[]) => {
|
||||
void args;
|
||||
};
|
||||
export const keyNotifyLog = (...args: any[]) => {
|
||||
console.log(...args);
|
||||
};
|
||||
export const jsonDebug = (...args: any[]) => {
|
||||
console.log('[JSONDEBUG]', ...args);
|
||||
};
|
||||
export const userMDebug = (...args: any[]) => {
|
||||
console.log('[USERMDEBUG]', ...args);
|
||||
};
|
||||
export const RESTORE_DEBUG_PREFIX = '[RESTORE_DEBUG]';
|
||||
export let restoreDebugCount = 0;
|
||||
@ -35,17 +32,16 @@ export const RESTORE_DEBUG_EVENTS = new Set([
|
||||
]);
|
||||
|
||||
export function isRestoreDebugEnabled() {
|
||||
if (typeof window === 'undefined') return true;
|
||||
if (typeof window === 'undefined') return false;
|
||||
try {
|
||||
const explicit = (window as any).__RESTORE_DEBUG__;
|
||||
if (explicit === false || explicit === '0') return false;
|
||||
if (explicit === true || explicit === '1') return true;
|
||||
if (explicit === false || explicit === '0') return false;
|
||||
const localFlag = window.localStorage?.getItem('restoreDebug');
|
||||
if (localFlag === '0' || localFlag === 'false') return false;
|
||||
if (localFlag === '1' || localFlag === 'true') return true;
|
||||
return true;
|
||||
return false;
|
||||
} catch {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -21,25 +21,18 @@ import {
|
||||
|
||||
export const toolMethods = {
|
||||
handleToolPreparing(data: any) {
|
||||
console.log('[ToolPreparing] 开始处理', { name: data.name, id: data.id });
|
||||
debugLog('[TaskPolling] 工具准备中:', data.name);
|
||||
|
||||
if (this.dropToolEvents) {
|
||||
console.log('[ToolPreparing] dropToolEvents=true,跳过');
|
||||
return;
|
||||
}
|
||||
|
||||
const msg = this.chatEnsureAssistantMessage();
|
||||
if (!msg) {
|
||||
console.log('[ToolPreparing] 无法获取assistant消息');
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.awaitingFirstContent) {
|
||||
console.log('[ToolPreparing] 关闭等待动画', {
|
||||
beforeAwaitingFirstContent: msg.awaitingFirstContent,
|
||||
beforeGeneratingLabel: msg.generatingLabel
|
||||
});
|
||||
msg.awaitingFirstContent = false;
|
||||
msg.generatingLabel = '';
|
||||
}
|
||||
@ -105,10 +98,6 @@ export const toolMethods = {
|
||||
// 但从头重建等场景可能跳过 tool_preparing 直接收到 tool_start)
|
||||
const msgForAwaiting = this.chatEnsureAssistantMessage();
|
||||
if (msgForAwaiting && msgForAwaiting.awaitingFirstContent) {
|
||||
console.log('[ToolStart] 关闭等待动画', {
|
||||
beforeAwaitingFirstContent: msgForAwaiting.awaitingFirstContent,
|
||||
toolName: data.name
|
||||
});
|
||||
msgForAwaiting.awaitingFirstContent = false;
|
||||
msgForAwaiting.generatingLabel = '';
|
||||
}
|
||||
@ -294,7 +283,6 @@ export const toolMethods = {
|
||||
await personalizationStore.fetchPersonalization();
|
||||
} catch (e) {
|
||||
// 静默处理,不影响主流程
|
||||
console.debug('[TaskPolling] 刷新个人空间数据失败:', e);
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
@ -336,19 +336,10 @@ export const hostWorkspaceMethods = {
|
||||
},
|
||||
async handleSelectWorkspaceConversation(payload: { conversationId: string; workspaceId: string }) {
|
||||
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 ((this.versioningHostMode || this.dockerProjectMode) && workspaceId !== this.currentHostWorkspaceId) {
|
||||
console.log('[sidebar-debug] switching workspace', { from: this.currentHostWorkspaceId, to: workspaceId });
|
||||
await this.handleHostWorkspaceSwitch(workspaceId);
|
||||
}
|
||||
console.log('[sidebar-debug] loading conversation', { conversationId, currentHostWorkspaceIdAfterSwitch: this.currentHostWorkspaceId });
|
||||
await this.loadConversation(conversationId, { force: true, workspaceId });
|
||||
},
|
||||
async handleRenameWorkspaceFromSidebar(payload: { workspaceId: string; label: string }) {
|
||||
|
||||
@ -67,23 +67,13 @@ export const mobileMethods = {
|
||||
this.uiToggleMobileOverlayMenu();
|
||||
},
|
||||
openMobileOverlay(target) {
|
||||
console.log(
|
||||
'[UI_DEBUG] openMobileOverlay called, target:',
|
||||
target,
|
||||
'isMobileViewport:',
|
||||
this.isMobileViewport,
|
||||
'activeMobileOverlay:',
|
||||
this.activeMobileOverlay
|
||||
);
|
||||
if (!this.isMobileViewport) {
|
||||
console.log('[UI_DEBUG] openMobileOverlay: blocked - not mobile viewport');
|
||||
return;
|
||||
}
|
||||
if (target === 'approval') {
|
||||
this.fetchPendingToolApprovals();
|
||||
}
|
||||
if (this.activeMobileOverlay === target) {
|
||||
console.log('[UI_DEBUG] openMobileOverlay: target already active, closing');
|
||||
this.closeMobileOverlay('same-target-click');
|
||||
return;
|
||||
}
|
||||
@ -93,17 +83,10 @@ export const mobileMethods = {
|
||||
if (target === 'conversation') {
|
||||
this.uiSetSidebarCollapsed(false);
|
||||
}
|
||||
console.log('[UI_DEBUG] openMobileOverlay: opening target:', target);
|
||||
this.uiSetActiveMobileOverlay(target);
|
||||
this.uiSetMobileOverlayMenuOpen(false);
|
||||
},
|
||||
closeMobileOverlay(source = 'unknown') {
|
||||
console.log(
|
||||
'[UI_DEBUG] closeMobileOverlay called from:',
|
||||
source,
|
||||
'activeMobileOverlay:',
|
||||
this.activeMobileOverlay
|
||||
);
|
||||
if (!this.activeMobileOverlay) {
|
||||
this.uiCloseMobileOverlay();
|
||||
return;
|
||||
|
||||
@ -38,12 +38,6 @@ export const panelMethods = {
|
||||
}
|
||||
},
|
||||
toggleApprovalPanel() {
|
||||
console.log(
|
||||
'[UI_DEBUG] toggleApprovalPanel called, current rightCollapsed:',
|
||||
this.rightCollapsed,
|
||||
'new state:',
|
||||
!this.rightCollapsed
|
||||
);
|
||||
this.rightCollapsed = !this.rightCollapsed;
|
||||
if (!this.rightCollapsed && this.rightWidth < this.minPanelWidth) {
|
||||
this.rightWidth = this.minPanelWidth;
|
||||
@ -71,53 +65,28 @@ export const panelMethods = {
|
||||
this.toggleFocusPanel();
|
||||
},
|
||||
handleApprovalPanelToggleClick() {
|
||||
console.log(
|
||||
'[UI_DEBUG] handleApprovalPanelToggleClick called, isMobileViewport:',
|
||||
this.isMobileViewport,
|
||||
'currentConversationId:',
|
||||
this.currentConversationId,
|
||||
'activeMobileOverlay:',
|
||||
this.activeMobileOverlay
|
||||
);
|
||||
if (!this.currentConversationId) {
|
||||
console.log('[UI_DEBUG] handleApprovalPanelToggleClick: blocked - no conversation');
|
||||
return;
|
||||
}
|
||||
if (this.isMobileViewport) {
|
||||
console.log('[UI_DEBUG] handleApprovalPanelToggleClick: mobile path, toggling overlay');
|
||||
// 手机端:获取审批列表(不自动关闭),然后切换遮罩
|
||||
this.fetchPendingToolApprovals();
|
||||
this.openMobileOverlay('approval');
|
||||
return;
|
||||
}
|
||||
console.log('[UI_DEBUG] handleApprovalPanelToggleClick: desktop path, toggling panel');
|
||||
this.toggleApprovalPanel();
|
||||
},
|
||||
handleTokenPanelToggleClick(fromSettingsMenu = false) {
|
||||
console.log(
|
||||
'[UI_DEBUG] handleTokenPanelToggleClick called, fromSettingsMenu:',
|
||||
fromSettingsMenu,
|
||||
'isMobileViewport:',
|
||||
this.isMobileViewport,
|
||||
'tokenPanelCollapsed:',
|
||||
this.tokenPanelCollapsed,
|
||||
'currentConversationId:',
|
||||
this.currentConversationId
|
||||
);
|
||||
if (!this.currentConversationId) {
|
||||
console.log('[UI_DEBUG] handleTokenPanelToggleClick: blocked - no conversation');
|
||||
return;
|
||||
}
|
||||
if (this.isPolicyBlocked('block_token_panel', '用量统计已被管理员禁用')) {
|
||||
console.log('[UI_DEBUG] handleTokenPanelToggleClick: blocked - policy');
|
||||
return;
|
||||
}
|
||||
// 移动端禁用“点击展开顶部用量面板”,仅允许在已展开时点击收起
|
||||
if (this.isMobileViewport && this.tokenPanelCollapsed && !fromSettingsMenu) {
|
||||
console.log('[UI_DEBUG] handleTokenPanelToggleClick: blocked - mobile viewport restriction');
|
||||
return;
|
||||
}
|
||||
console.log('[UI_DEBUG] handleTokenPanelToggleClick: calling toggleTokenPanel');
|
||||
this.toggleTokenPanel();
|
||||
},
|
||||
getMessagesAreaElement() {
|
||||
|
||||
@ -20,14 +20,14 @@ import { debugLog } from '../common';
|
||||
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 userMDebug = (...args: any[]) => {
|
||||
console.log('[USERMDEBUG]', ...args);
|
||||
};
|
||||
export let uiBounceTraceCount = 0;
|
||||
export const UI_BOUNCE_TRACE_MAX = 140;
|
||||
export const uiBounceTraceLastTsByKey = new Map<string, number>();
|
||||
|
||||
export function isUiBounceTraceEnabled() {
|
||||
if (typeof window === 'undefined') return true;
|
||||
// 默认关闭:排障时通过 window.__SCROLL_BOUNCE_TRACE__ = true 或 localStorage.scrollBounceTrace = '1' 显式打开
|
||||
if (typeof window === 'undefined') return false;
|
||||
try {
|
||||
const explicit = (window as any).__SCROLL_BOUNCE_TRACE__;
|
||||
if (explicit === false || explicit === '0') return false;
|
||||
@ -35,9 +35,9 @@ export function isUiBounceTraceEnabled() {
|
||||
const localFlag = window.localStorage?.getItem('scrollBounceTrace');
|
||||
if (localFlag === '0' || localFlag === 'false') return false;
|
||||
if (localFlag === '1' || localFlag === 'true') return true;
|
||||
return true;
|
||||
return false;
|
||||
} catch {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,7 +62,8 @@ export function uiBounceTrace(
|
||||
}
|
||||
|
||||
export function isConnectionDiagEnabled() {
|
||||
if (typeof window === 'undefined') return true;
|
||||
// 默认关闭:排障时通过 window.__CONN_DIAG__ = true 或 localStorage.connDiag = '1' 显式打开
|
||||
if (typeof window === 'undefined') return false;
|
||||
try {
|
||||
const explicit = (window as any).__CONN_DIAG__;
|
||||
if (explicit === false || explicit === '0') return false;
|
||||
@ -70,10 +71,9 @@ export function isConnectionDiagEnabled() {
|
||||
const localFlag = window.localStorage?.getItem('connDiag');
|
||||
if (localFlag === '0' || localFlag === 'false') return false;
|
||||
if (localFlag === '1' || localFlag === 'true') return true;
|
||||
// 默认常开:无需手动启动
|
||||
return true;
|
||||
return false;
|
||||
} catch {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,18 +118,10 @@ export function connectionDiag(
|
||||
export function parseSubAgentDoneLabel(rawContent: any): string | null {
|
||||
const content = (rawContent || '').toString().trim();
|
||||
if (!content) {
|
||||
console.log('[DEBUG_SYSTEM][ui] parseSubAgentDoneLabel: 空内容');
|
||||
return null;
|
||||
}
|
||||
const match = content.match(SUB_AGENT_DONE_PREFIX_RE);
|
||||
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) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -91,8 +91,7 @@ export const socketMethods = {
|
||||
streamingMessage: !!this.streamingMessage,
|
||||
visibility: document?.visibilityState || 'unknown',
|
||||
online: typeof navigator !== 'undefined' ? navigator.onLine : null
|
||||
},
|
||||
{ force: true }
|
||||
}
|
||||
);
|
||||
} else if (
|
||||
isConnectionDiagEnabled() &&
|
||||
@ -177,8 +176,7 @@ export const socketMethods = {
|
||||
{
|
||||
connectedIntervalMs: this.connectionHeartbeatIntervalMs,
|
||||
disconnectedIntervalMs: this.connectionHeartbeatDisconnectedIntervalMs
|
||||
},
|
||||
{ force: true }
|
||||
}
|
||||
);
|
||||
const runHeartbeat = async () => {
|
||||
if (!this.connectionHeartbeatActive) {
|
||||
@ -224,7 +222,7 @@ export const socketMethods = {
|
||||
clearTimeout(this.connectionHeartbeatTimer);
|
||||
this.connectionHeartbeatTimer = null;
|
||||
}
|
||||
connectionDiag('log', 'heartbeat-stop', {}, { force: true });
|
||||
connectionDiag('log', 'heartbeat-stop', {});
|
||||
},
|
||||
async loadInitialData() {
|
||||
try {
|
||||
|
||||
@ -30,11 +30,6 @@ export const tutorialMethods = {
|
||||
try {
|
||||
const resp = await fetch('/api/tutorial-status', { credentials: 'same-origin' });
|
||||
const data = await resp.json().catch(() => ({}));
|
||||
console.info('[tutorial-prompt] status response:', {
|
||||
ok: resp.ok,
|
||||
status: resp.status,
|
||||
data
|
||||
});
|
||||
if (!resp.ok || !data?.success) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -159,17 +159,13 @@ export const versioningMethods = {
|
||||
throw new Error(`详情响应解析失败: ${parseErr?.message || '未知错误'}`);
|
||||
}
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('[VersioningDetail] raw response:', data);
|
||||
if (!resp.ok || !data?.success) {
|
||||
throw new Error(data?.error || '加载详情失败');
|
||||
}
|
||||
this.versioningSelectedDetail = data.data || null;
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('[VersioningDetail] detail set:', this.versioningSelectedDetail);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('[VersioningDetail] files:', this.versioningSelectedDetail?.files);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('[VersioningDetail] first file patch_lines:', this.versioningSelectedDetail?.files?.[0]?.patch_lines);
|
||||
} catch (error: any) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('[VersioningDetail] error:', error);
|
||||
|
||||
@ -147,7 +147,6 @@ onMounted(async () => {
|
||||
try {
|
||||
const resp = await fetch('/api/session-status', { credentials: 'same-origin' });
|
||||
const data = await resp.json();
|
||||
console.info('[auth-debug] login page session-status:', data);
|
||||
} catch (err) {
|
||||
console.warn('[auth-debug] login page session-status failed:', err);
|
||||
}
|
||||
|
||||
@ -125,7 +125,6 @@ onMounted(async () => {
|
||||
try {
|
||||
const resp = await fetch('/api/session-status', { credentials: 'same-origin' });
|
||||
const data = await resp.json();
|
||||
console.info('[auth-debug] register page session-status:', data);
|
||||
} catch (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) => {
|
||||
// TODO: 分支功能占位
|
||||
void _msg;
|
||||
console.log('[ChatArea] 分支功能待实现');
|
||||
};
|
||||
|
||||
const observeUserBubbles = () => {
|
||||
@ -1083,10 +1082,6 @@ const filteredMessages = computed(() => {
|
||||
const source = props.messages || [];
|
||||
const droppedRoleSystem = source.filter((m) => m && m.role === 'system');
|
||||
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) => {
|
||||
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)}`;
|
||||
if (!content) {
|
||||
if (!debugLoggedSystemActionKeys.has(`${key}-empty`)) {
|
||||
console.log('[DEBUG_SYSTEM][render] system action 内容为空', { action });
|
||||
debugLoggedSystemActionKeys.add(`${key}-empty`);
|
||||
}
|
||||
return null;
|
||||
@ -1773,7 +1767,6 @@ function getSubAgentSystemNoticeLabel(action: any): string | null {
|
||||
BG_RUN_COMMAND_DONE_LABEL_RE.test(content))
|
||||
) {
|
||||
if (!debugLoggedSystemActionKeys.has(`${key}-variant-pass`)) {
|
||||
console.log('[DEBUG_SYSTEM][render] 命中 variant 渲染', { action, label: content });
|
||||
debugLoggedSystemActionKeys.add(`${key}-variant-pass`);
|
||||
}
|
||||
return content;
|
||||
@ -1781,20 +1774,11 @@ function getSubAgentSystemNoticeLabel(action: any): string | null {
|
||||
const m = content.match(SUB_AGENT_DONE_PREFIX_RE);
|
||||
if (m && /已完成/.test(content)) {
|
||||
if (!debugLoggedSystemActionKeys.has(`${key}-regex-pass`)) {
|
||||
console.log('[DEBUG_SYSTEM][render] 命中 regex 渲染', {
|
||||
action,
|
||||
normalized: `子智能体${m[1]} 任务完成`
|
||||
});
|
||||
debugLoggedSystemActionKeys.add(`${key}-regex-pass`);
|
||||
}
|
||||
return `子智能体${m[1]} 任务完成`;
|
||||
}
|
||||
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`);
|
||||
}
|
||||
return null;
|
||||
@ -1808,10 +1792,6 @@ function isActionVisible(action: any): boolean {
|
||||
if (!RENDERABLE_ACTION_TYPES.has(actionType)) {
|
||||
const key = action?.id || `unknown-${actionType || 'none'}`;
|
||||
if (!debugLoggedUnknownActionKeys.has(key)) {
|
||||
console.log('[DEBUG_SYSTEM][render] 未知 action.type,已跳过渲染', {
|
||||
actionType,
|
||||
action
|
||||
});
|
||||
debugLoggedUnknownActionKeys.add(key);
|
||||
}
|
||||
return false;
|
||||
@ -2099,11 +2079,6 @@ const splitActionGroups = (actions: any[] = [], messageIndex = 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 = () => {
|
||||
@ -2140,14 +2115,6 @@ const splitActionGroups = (actions: any[] = [], messageIndex = 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;
|
||||
|
||||
@ -98,7 +98,7 @@ const RENAME_TYPE_INTERVAL = 32;
|
||||
const MONITOR_EDITOR_DEBUG = false;
|
||||
|
||||
const MONITOR_READER_DEBUG = false;
|
||||
const MONITOR_RENAME_DEBUG = true;
|
||||
const MONITOR_RENAME_DEBUG = false;
|
||||
const readerDebug = (...args: any[]) => {
|
||||
if (!MONITOR_READER_DEBUG) {
|
||||
return;
|
||||
@ -117,14 +117,14 @@ const editorDebug = (...args: any[]) => {
|
||||
}
|
||||
console.log('[MonitorEditor]', ...args);
|
||||
};
|
||||
const MONITOR_PROGRESS_DEBUG = true;
|
||||
const MONITOR_PROGRESS_DEBUG = false;
|
||||
const progressDebug = (...args: any[]) => {
|
||||
if (!MONITOR_PROGRESS_DEBUG) {
|
||||
return;
|
||||
}
|
||||
console.debug('[MonitorProgress]', ...args);
|
||||
};
|
||||
const MONITOR_LIFECYCLE_DEBUG = true;
|
||||
const MONITOR_LIFECYCLE_DEBUG = false;
|
||||
const monitorLifecycleDebug = (...args: any[]) => {
|
||||
if (!MONITOR_LIFECYCLE_DEBUG) {
|
||||
return;
|
||||
|
||||
@ -62,7 +62,6 @@ const filePadding = computed(() => ({
|
||||
|
||||
function toggle() {
|
||||
if (props.node.type === 'folder') {
|
||||
console.log('[FileTree] click folder header', props.node.path);
|
||||
fileStore.toggleFolder(props.node.path);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1911,7 +1911,6 @@ const voiceButtonTitle = computed(() => {
|
||||
const setupVoiceBridgeCallbacks = () => {
|
||||
if (typeof window === 'undefined') return;
|
||||
(window as any).__onVoiceResult = (text: string) => {
|
||||
console.log('[VoiceInput] Android 识别结果:', text);
|
||||
const bridge = (window as any).AndroidVoiceBridge;
|
||||
bridge?.debugLog('__onVoiceResult 被调用, text=' + JSON.stringify(text));
|
||||
if (!text) {
|
||||
@ -1959,7 +1958,6 @@ const setupVoiceBridgeCallbacks = () => {
|
||||
}
|
||||
};
|
||||
(window as any).__onVoiceStatus = (status: string) => {
|
||||
console.log('[VoiceInput] 状态:', status);
|
||||
if (status === 'listening' || status === 'processing' || status === 'initializing') {
|
||||
// initializing 也视为录音中,防止用户重复点击触发多次 startListening
|
||||
isRecording.value = true;
|
||||
@ -2008,23 +2006,19 @@ const isWebSpeechSupported = computed(() => {
|
||||
if (typeof window === 'undefined') return false;
|
||||
const hasSR = 'SpeechRecognition' in window;
|
||||
const hasWebkit = 'webkitSpeechRecognition' in window;
|
||||
console.log('[VoiceInput] 检测支持:', { hasSR, hasWebkit, supported: hasSR || hasWebkit });
|
||||
return hasSR || hasWebkit;
|
||||
});
|
||||
|
||||
const getSpeechRecognitionConstructor = (): typeof SpeechRecognition | null => {
|
||||
if (typeof window === 'undefined') return null;
|
||||
const Ctor = (window as any).SpeechRecognition || (window as any).webkitSpeechRecognition || null;
|
||||
console.log('[VoiceInput] 构造函数:', Ctor ? Ctor.name || 'webkitSpeechRecognition' : 'null');
|
||||
return Ctor;
|
||||
};
|
||||
|
||||
const stopVoiceRecording = () => {
|
||||
console.log('[VoiceInput] stopVoiceRecording called, isRecording:', isRecording.value);
|
||||
if (recognitionInstance.value) {
|
||||
try {
|
||||
recognitionInstance.value.stop();
|
||||
console.log('[VoiceInput] recognition.stop() called');
|
||||
} catch (e) {
|
||||
console.warn('[VoiceInput] recognition.stop() error:', e);
|
||||
}
|
||||
@ -2053,27 +2047,15 @@ const initSpeechRecognition = () => {
|
||||
voiceAnchorPos = 0;
|
||||
}
|
||||
lastVoiceTextLength = 0;
|
||||
console.log('[VoiceInput] init: anchorPos=', voiceAnchorPos);
|
||||
|
||||
recognition.onresult = (event: SpeechRecognitionEvent) => {
|
||||
console.log('[VoiceInput] onresult fired:', {
|
||||
resultLength: event.results.length,
|
||||
resultIndex: (event as any).resultIndex
|
||||
});
|
||||
let fullText = '';
|
||||
for (let i = 0; i < event.results.length; 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;
|
||||
}
|
||||
|
||||
console.log('[VoiceInput] fullText:', JSON.stringify(fullText));
|
||||
if (!fullText) {
|
||||
console.log('[VoiceInput] fullText empty, skip');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2102,7 +2084,6 @@ const initSpeechRecognition = () => {
|
||||
view.focus();
|
||||
|
||||
lastVoiceTextLength = fullText.length;
|
||||
console.log('[VoiceInput] inserted ok, lastVoiceTextLength=', lastVoiceTextLength);
|
||||
|
||||
emit('update:input-message', getEditorPlainText());
|
||||
emit('input-change');
|
||||
@ -2114,7 +2095,6 @@ const initSpeechRecognition = () => {
|
||||
recognition.onerror = (event: SpeechRecognitionErrorEvent) => {
|
||||
console.warn('[VoiceInput] onerror:', { error: event.error, message: (event as any).message });
|
||||
if (event.error === 'aborted') {
|
||||
console.log('[VoiceInput] aborted (user stop), ignore');
|
||||
return;
|
||||
}
|
||||
// no-speech / network / not-allowed / service-not-allowed 等全部打印
|
||||
@ -2123,35 +2103,27 @@ const initSpeechRecognition = () => {
|
||||
};
|
||||
|
||||
recognition.onstart = () => {
|
||||
console.log('[VoiceInput] onstart: 录音已开始');
|
||||
};
|
||||
|
||||
recognition.onaudiostart = () => {
|
||||
console.log('[VoiceInput] onaudiostart: 音频捕获开始');
|
||||
};
|
||||
|
||||
recognition.onsoundstart = () => {
|
||||
console.log('[VoiceInput] onsoundstart: 检测到声音');
|
||||
};
|
||||
|
||||
recognition.onspeechstart = () => {
|
||||
console.log('[VoiceInput] onspeechstart: 检测到语音');
|
||||
};
|
||||
|
||||
recognition.onspeechend = () => {
|
||||
console.log('[VoiceInput] onspeechend: 语音结束');
|
||||
};
|
||||
|
||||
recognition.onsoundend = () => {
|
||||
console.log('[VoiceInput] onsoundend: 声音结束');
|
||||
};
|
||||
|
||||
recognition.onaudioend = () => {
|
||||
console.log('[VoiceInput] onaudioend: 音频捕获结束');
|
||||
};
|
||||
|
||||
recognition.onend = () => {
|
||||
console.log('[VoiceInput] onend: 录音结束, isRecording=', isRecording.value);
|
||||
isRecording.value = false;
|
||||
recognitionInstance.value = null;
|
||||
voiceAnchorPos = 0;
|
||||
@ -2162,7 +2134,6 @@ const initSpeechRecognition = () => {
|
||||
};
|
||||
|
||||
const toggleVoiceRecording = () => {
|
||||
console.log('[VoiceInput] toggleVoiceRecording, isRecording=', isRecording.value);
|
||||
|
||||
// Android Bridge 模式
|
||||
const bridge = (window as any).AndroidVoiceBridge;
|
||||
@ -2193,7 +2164,6 @@ const toggleVoiceRecording = () => {
|
||||
|
||||
try {
|
||||
recognition.start();
|
||||
console.log('[VoiceInput] recognition.start() called');
|
||||
recognitionInstance.value = recognition;
|
||||
isRecording.value = true;
|
||||
} catch (error) {
|
||||
|
||||
@ -208,7 +208,6 @@ function appendToTerm(data: string) {
|
||||
function renderSessionLog() {
|
||||
if (!term || !activeSession.value) return;
|
||||
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();
|
||||
if (log) {
|
||||
term.write(log, () => {
|
||||
@ -218,7 +217,6 @@ function renderSessionLog() {
|
||||
}
|
||||
|
||||
function switchToSession(name: string) {
|
||||
console.log('[TerminalPanel] switchToSession:', name);
|
||||
if (!name) return;
|
||||
activeSession.value = name;
|
||||
if (!sessionHydrated.value[name]) {
|
||||
@ -227,7 +225,6 @@ function switchToSession(name: string) {
|
||||
delete updatedTimes[name];
|
||||
_historyLastEventTime.value = updatedTimes;
|
||||
if (socket?.connected) {
|
||||
console.log('[TerminalPanel] requesting history for:', name);
|
||||
socket.emit('get_terminal_output', { session: name, lines: 0, conversation_id: props.conversationId });
|
||||
}
|
||||
}
|
||||
@ -236,12 +233,10 @@ function switchToSession(name: string) {
|
||||
|
||||
// ---- watchers ----
|
||||
watch(activeSession, (val) => {
|
||||
console.log('[TerminalPanel] activeSession:', val);
|
||||
if (term) renderSessionLog();
|
||||
});
|
||||
|
||||
watch(sessionKeys, (keys) => {
|
||||
console.log('[TERMINAL_DEBUG] sessionKeys:', keys);
|
||||
if (keys.length > 0 && term && !activeSession.value) {
|
||||
switchToSession(keys[0]);
|
||||
}
|
||||
@ -250,7 +245,6 @@ watch(sessionKeys, (keys) => {
|
||||
// 工作区切换时重连
|
||||
watch(() => props.workspaceId, (newId, oldId) => {
|
||||
if (newId && oldId && newId !== oldId) {
|
||||
console.log('[TerminalPanel] workspace changed:', oldId, '->', newId);
|
||||
// 断开旧连接,清空状态,重新连接
|
||||
if (socket) {
|
||||
socket.disconnect();
|
||||
@ -270,7 +264,6 @@ watch(() => props.workspaceId, (newId, oldId) => {
|
||||
// 对话切换时重置并重新订阅(对话级 terminal:各对话 shell 互相独立)
|
||||
watch(() => props.conversationId, (newId, oldId) => {
|
||||
if (newId !== oldId) {
|
||||
console.log('[TerminalPanel] conversation changed:', oldId, '->', newId);
|
||||
sessions.value = {};
|
||||
activeSession.value = '';
|
||||
sessionLogs.value = {};
|
||||
@ -286,7 +279,6 @@ watch(() => props.conversationId, (newId, oldId) => {
|
||||
|
||||
// ---- socket 初始化 ----
|
||||
async function initSocket() {
|
||||
console.log('[TerminalPanel] initSocket start');
|
||||
|
||||
socket = createSocketClient('/', {
|
||||
transports: ['websocket', 'polling'],
|
||||
@ -319,16 +311,13 @@ async function initSocket() {
|
||||
}
|
||||
|
||||
socket.on('connect', () => {
|
||||
console.log('[TerminalPanel] socket connected');
|
||||
socket!.emit('terminal_subscribe', { all: true, conversation_id: props.conversationId });
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
console.log('[TerminalPanel] socket disconnected');
|
||||
});
|
||||
|
||||
socket.on('terminal_subscribed', (data: any) => {
|
||||
console.log('[TerminalPanel] terminal_subscribed:', data);
|
||||
if (data?.terminals) {
|
||||
const map: Record<string, { working_dir?: string; shell?: string }> = {};
|
||||
for (const t of data.terminals) {
|
||||
@ -343,7 +332,6 @@ async function initSocket() {
|
||||
|
||||
socket.on('terminal_started', (data: any) => {
|
||||
if (!acceptTerminalBroadcast(data)) return;
|
||||
console.log('[TerminalPanel] terminal_started:', data);
|
||||
sessions.value = {
|
||||
...sessions.value,
|
||||
[data.session]: { working_dir: data.working_dir, shell: 'bash' }
|
||||
@ -353,7 +341,6 @@ async function initSocket() {
|
||||
|
||||
socket.on('terminal_list_update', (data: any) => {
|
||||
if (!acceptTerminalBroadcast(data)) return;
|
||||
console.log('[TerminalPanel] terminal_list_update:', data);
|
||||
if (data?.terminals) {
|
||||
const map: Record<string, { working_dir?: string; shell?: string }> = {};
|
||||
for (const t of data.terminals) {
|
||||
@ -376,7 +363,6 @@ async function initSocket() {
|
||||
const historyTime = _historyLastEventTime.value[s] || 0;
|
||||
const eventTime = typeof data.timestamp === 'number' ? data.timestamp : 0;
|
||||
if (historyTime > 0 && eventTime > 0 && eventTime <= historyTime) {
|
||||
console.log('[TerminalPanel] terminal_output DEDUP skip:', s, 'eventTime:', eventTime, '<= historyTime:', historyTime);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -384,17 +370,14 @@ async function initSocket() {
|
||||
// 避免新终端历史为空时 _historyReady 始终为 false 导致永远写不进去。
|
||||
sessionLogs.value[s] = (sessionLogs.value[s] || '') + delta;
|
||||
if (s === activeSession.value && term && _historyReady.value[s]) {
|
||||
console.log('[TerminalPanel] terminal_output WRITE:', s, 'len:', delta.length);
|
||||
term.write(delta);
|
||||
} else {
|
||||
console.log('[TerminalPanel] terminal_output SKIP:', s, 'len:', delta.length, 'isActive:', s === activeSession.value, 'ready:', _historyReady.value[s]);
|
||||
}
|
||||
if (!activeSession.value) switchToSession(s);
|
||||
});
|
||||
|
||||
socket.on('terminal_input', (data: any) => {
|
||||
if (!acceptTerminalBroadcast(data)) return;
|
||||
console.log('[TerminalPanel] terminal_input:', data.session);
|
||||
// 原样显示后端输出,此处不重复写入
|
||||
// 仅用于新终端自动切换
|
||||
if (!activeSession.value && data.session) switchToSession(data.session);
|
||||
@ -402,7 +385,6 @@ async function initSocket() {
|
||||
|
||||
socket.on('terminal_closed', (data: any) => {
|
||||
if (!acceptTerminalBroadcast(data)) return;
|
||||
console.log('[TerminalPanel] terminal_closed:', data);
|
||||
const updated = { ...sessions.value };
|
||||
delete updated[data.session];
|
||||
sessions.value = updated;
|
||||
@ -414,7 +396,6 @@ async function initSocket() {
|
||||
|
||||
socket.on('terminal_reset', (data: any) => {
|
||||
if (!acceptTerminalBroadcast(data)) return;
|
||||
console.log('[TerminalPanel] terminal_reset:', data);
|
||||
const target = data.session || activeSession.value;
|
||||
if (target) {
|
||||
sessionLogs.value = { ...sessionLogs.value, [target]: '' };
|
||||
@ -428,12 +409,10 @@ async function initSocket() {
|
||||
|
||||
socket.on('terminal_switched', (data: any) => {
|
||||
if (!acceptTerminalBroadcast(data)) return;
|
||||
console.log('[TerminalPanel] terminal_switched:', data);
|
||||
if (data.current) switchToSession(data.current);
|
||||
});
|
||||
|
||||
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;
|
||||
if (!s) return;
|
||||
const raw = data.output || data.data || '';
|
||||
@ -443,7 +422,6 @@ async function initSocket() {
|
||||
} else if (Array.isArray(raw)) {
|
||||
payload = raw.join('\n');
|
||||
}
|
||||
console.log('[TerminalPanel] handleHistory:', s, 'rawLen:', raw.length, 'payloadLen:', payload.length, 'isActive:', s === activeSession.value);
|
||||
if (payload) {
|
||||
sessionLogs.value[s] = payload;
|
||||
}
|
||||
@ -454,7 +432,6 @@ async function initSocket() {
|
||||
// 记录历史快照最后一条事件的时间戳,用于后续 terminal_output 去重
|
||||
const lastEventTime = typeof data.last_event_time === 'number' ? data.last_event_time : 0;
|
||||
_historyLastEventTime.value = { ..._historyLastEventTime.value, [s]: lastEventTime };
|
||||
console.log('[TerminalPanel] handleHistory SAVED:', s, 'finalLen:', payload.length, 'calling render:', s === activeSession.value);
|
||||
if (s === activeSession.value) {
|
||||
renderSessionLog();
|
||||
}
|
||||
@ -464,7 +441,6 @@ async function initSocket() {
|
||||
socket.on('terminal_history', handleHistory);
|
||||
|
||||
socket.connect();
|
||||
console.log('[TerminalPanel] socket connecting...');
|
||||
}
|
||||
|
||||
// ---- 生命周期 ----
|
||||
|
||||
@ -158,30 +158,22 @@ const emit = defineEmits<{
|
||||
}>();
|
||||
|
||||
const handleCloseClick = () => {
|
||||
console.log('[sidebarDEBUG] ToolApprovalPanel close button clicked');
|
||||
emit('close');
|
||||
};
|
||||
|
||||
// 检测是否为移动端视口 - 使用计算属性确保响应式
|
||||
const isMobileViewport = computed(() => {
|
||||
const isMobile = typeof window !== 'undefined' && window.innerWidth <= 768;
|
||||
console.log('[sidebarDEBUG] isMobileViewport computed:', isMobile, 'windowWidth:', window?.innerWidth);
|
||||
return isMobile;
|
||||
});
|
||||
|
||||
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 sidebarHeader = document.querySelector('.tool-approval-panel .sidebar-header');
|
||||
if (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 current = personalizationStore.form.sidebar_pinned_workspaces || [];
|
||||
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({
|
||||
key: 'sidebar_pinned_workspaces',
|
||||
value: next
|
||||
@ -880,10 +876,6 @@ const persistWorkspaceOrder = () => {
|
||||
const next = Array.from(workspaceOrder.value);
|
||||
const current = personalizationStore.form.sidebar_workspace_order || [];
|
||||
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({
|
||||
key: 'sidebar_workspace_order',
|
||||
value: next
|
||||
@ -893,19 +885,12 @@ const persistWorkspaceOrder = () => {
|
||||
const bumpCurrentWorkspaceToTop = () => {
|
||||
if (!sidebarSortLoaded.value) return;
|
||||
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 (workspaceOrder.value[0] === currentId) return;
|
||||
const order = workspaceOrder.value.filter((item) => item !== currentId);
|
||||
order.unshift(currentId);
|
||||
workspaceOrder.value = order;
|
||||
persistWorkspaceOrder();
|
||||
console.log('[sidebar-debug] bumpCurrentWorkspaceToTop after', {
|
||||
orderAfter: JSON.parse(JSON.stringify(workspaceOrder.value))
|
||||
});
|
||||
};
|
||||
|
||||
const syncWorkspaceOrderWithWorkspaces = (workspaces: any[]) => {
|
||||
@ -921,12 +906,6 @@ const syncWorkspaceOrderWithWorkspaces = (workspaces: any[]) => {
|
||||
const removedIds = new Set(
|
||||
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) {
|
||||
workspaceOrder.value = workspaceOrder.value
|
||||
.filter((id) => !removedIds.has(id))
|
||||
@ -937,10 +916,6 @@ const syncWorkspaceOrderWithWorkspaces = (workspaces: any[]) => {
|
||||
|
||||
const loadSidebarSortFromStore = () => {
|
||||
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();
|
||||
syncOrderFromStore();
|
||||
sidebarSortLoaded.value = true;
|
||||
@ -948,10 +923,6 @@ const loadSidebarSortFromStore = () => {
|
||||
syncWorkspaceOrderWithWorkspaces(props.workspaces);
|
||||
}
|
||||
bumpCurrentWorkspaceToTop();
|
||||
console.log('[sidebar-debug] loadSidebarSortFromStore end', {
|
||||
pinned: Array.from(pinnedWorkspaceIds.value),
|
||||
order: JSON.parse(JSON.stringify(workspaceOrder.value))
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
@ -1165,7 +1136,6 @@ watch(
|
||||
);
|
||||
|
||||
const handleWorkspaceConversationClick = (conversationId: string, workspaceId: string) => {
|
||||
console.log('[sidebar-debug] handleWorkspaceConversationClick', { conversationId, workspaceId });
|
||||
openWorkspaceMenuId.value = null;
|
||||
emit('select-workspace-conversation', { conversationId, workspaceId });
|
||||
};
|
||||
|
||||
@ -120,7 +120,6 @@ export function useEasterEgg() {
|
||||
}, durationSeconds * 1000);
|
||||
uiStore.setEasterEggState({ cleanupTimer: timer });
|
||||
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) {
|
||||
try {
|
||||
const SOCKET_DEBUG_LOGS_ENABLED = true;
|
||||
const SOCKET_DEBUG_LOGS_ENABLED = false;
|
||||
const socketLog = (...args: any[]) => {
|
||||
if (!SOCKET_DEBUG_LOGS_ENABLED) {
|
||||
return;
|
||||
@ -74,20 +74,10 @@ export async function initializeLegacySocket(ctx: any) {
|
||||
}
|
||||
if (action.tool.intent_full === text) {
|
||||
if (console && console.debug) {
|
||||
console.debug('[tool_intent] skip (same text)', {
|
||||
id: action?.id || action?.tool?.id,
|
||||
name: action?.tool?.name,
|
||||
text
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
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) {
|
||||
return;
|
||||
@ -105,11 +95,6 @@ export async function initializeLegacySocket(ctx: any) {
|
||||
action.tool.intent_typing_timer = window.setInterval(() => {
|
||||
action.tool.intent_rendered = text.slice(0, index + 1);
|
||||
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;
|
||||
if (index >= text.length) {
|
||||
@ -117,11 +102,6 @@ export async function initializeLegacySocket(ctx: any) {
|
||||
action.tool.intent_typing_timer = null;
|
||||
action.tool.intent_rendered = text;
|
||||
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') {
|
||||
@ -789,7 +769,6 @@ export async function initializeLegacySocket(ctx: any) {
|
||||
// 监听对话变更事件
|
||||
ctx.socket.on('conversation_changed', (data) => {
|
||||
socketLog('对话已切换:', data);
|
||||
console.log('[conv-trace] socket:conversation_changed', data);
|
||||
|
||||
// 初始化期间不修改 currentConversationId,避免与 bootstrapRoute 冲突
|
||||
if (ctx.initialRouteResolved) {
|
||||
@ -1177,11 +1156,6 @@ export async function initializeLegacySocket(ctx: any) {
|
||||
socketLog('文本开始 (轮询模式,跳过)');
|
||||
return;
|
||||
}
|
||||
console.log('[DEBUG] 收到 text_start 事件:', {
|
||||
currentConversationId: ctx.currentConversationId,
|
||||
messagesCount: ctx.messages.length,
|
||||
currentMessageIndex: ctx.currentMessageIndex
|
||||
});
|
||||
socketLog('文本开始');
|
||||
logStreamingDebug('socket:text_start');
|
||||
finalizeStreamingText({ force: true });
|
||||
@ -1192,7 +1166,6 @@ export async function initializeLegacySocket(ctx: any) {
|
||||
streamingState.activeTextAction = action || ensureActiveTextAction();
|
||||
ensureActiveMessageBinding();
|
||||
ctx.$forceUpdate();
|
||||
console.log('[DEBUG] text_start 处理完成');
|
||||
});
|
||||
|
||||
// 文本内容块
|
||||
@ -1201,12 +1174,6 @@ export async function initializeLegacySocket(ctx: any) {
|
||||
if (ctx.usePollingMode && !ctx.waitingForSubAgent) {
|
||||
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', {
|
||||
index: data?.index ?? null,
|
||||
elapsed: data?.elapsed ?? null,
|
||||
@ -1301,30 +1268,15 @@ export async function initializeLegacySocket(ctx: any) {
|
||||
if (ctx.dropToolEvents) {
|
||||
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);
|
||||
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) {
|
||||
console.log('[DEBUG] 跳过 tool_preparing (对话不匹配)');
|
||||
socketLog('跳过tool_preparing(对话不匹配)', data.conversation_id);
|
||||
return;
|
||||
}
|
||||
const msg = ctx.chatEnsureAssistantMessage();
|
||||
if (!msg) {
|
||||
console.log('[DEBUG] tool_preparing: 无法获取assistant消息');
|
||||
return;
|
||||
}
|
||||
if (msg.awaitingFirstContent) {
|
||||
@ -1355,7 +1307,6 @@ export async function initializeLegacySocket(ctx: any) {
|
||||
if (data.intent) {
|
||||
startIntentTyping(action, data.intent);
|
||||
}
|
||||
console.log('[DEBUG] tool_preparing 处理完成,action已添加');
|
||||
ctx.$forceUpdate();
|
||||
ctx.conditionalScrollToBottom();
|
||||
if (ctx.monitorPreviewTool) {
|
||||
@ -1376,12 +1327,6 @@ export async function initializeLegacySocket(ctx: any) {
|
||||
return;
|
||||
}
|
||||
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 =
|
||||
ctx.toolFindAction(data.id, data.preparing_id, data.execution_id) ||
|
||||
@ -1689,35 +1634,21 @@ export async function initializeLegacySocket(ctx: any) {
|
||||
|
||||
// 任务完成(重点:更新Token统计)
|
||||
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);
|
||||
|
||||
const hasRunningMultiAgent = !!data?.has_running_multi_agent;
|
||||
// 多智能体模式下,若仍有运行中的多智能体实例,保持对话运行态,继续接收后续消息;
|
||||
// 但此时主智能体已空闲,不应阻塞输入区(waitingForSubAgent=false)。
|
||||
if (hasRunningMultiAgent) {
|
||||
console.log('[DEBUG] 多智能体实例仍在运行,保持任务状态但不阻塞输入');
|
||||
} else if (ctx.multiAgentMode || !data.has_running_sub_agents) {
|
||||
console.log('[DEBUG] 没有运行中的子智能体/多智能体,重置任务状态');
|
||||
if (ctx.waitingForSubAgent) {
|
||||
ctx.waitingForSubAgent = false;
|
||||
}
|
||||
ctx.taskInProgress = false;
|
||||
ctx.scheduleResetAfterTask('socket:task_complete', { preserveMonitorWindows: true });
|
||||
} else {
|
||||
console.log('[DEBUG] 有运行中的子智能体,保持任务状态');
|
||||
}
|
||||
|
||||
console.log('[DEBUG] 当前状态 (after task_complete):', {
|
||||
taskInProgress: ctx.taskInProgress,
|
||||
streamingMessage: ctx.streamingMessage
|
||||
});
|
||||
|
||||
resetPendingToolEvents();
|
||||
|
||||
// 任务完成后立即更新Token统计(关键修复)
|
||||
@ -1729,12 +1660,6 @@ export async function initializeLegacySocket(ctx: any) {
|
||||
|
||||
// 子智能体等待状态
|
||||
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);
|
||||
|
||||
// 多智能体模式下,子智能体 idle/running 是常态,不阻塞主对话输入区
|
||||
@ -1748,13 +1673,6 @@ export async function initializeLegacySocket(ctx: any) {
|
||||
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') {
|
||||
const taskList = data.tasks
|
||||
@ -1764,7 +1682,6 @@ export async function initializeLegacySocket(ctx: any) {
|
||||
}
|
||||
|
||||
ctx.$forceUpdate();
|
||||
console.log('[DEBUG] sub_agent_waiting 处理完成');
|
||||
});
|
||||
|
||||
// 聚焦文件更新
|
||||
@ -1886,7 +1803,6 @@ export async function initializeLegacySocket(ctx: any) {
|
||||
|
||||
// === 实时终端事件 ===
|
||||
ctx.socket.on('terminal_subscribed', (data) => {
|
||||
console.log('[Socket] terminal_subscribed:', data);
|
||||
if (data?.terminals) {
|
||||
const sessions: Record<string, { working_dir?: string; shell?: string }> = {};
|
||||
for (const t of data.terminals) {
|
||||
@ -1903,7 +1819,6 @@ export async function initializeLegacySocket(ctx: any) {
|
||||
});
|
||||
|
||||
ctx.socket.on('terminal_started', (data) => {
|
||||
console.log('[Socket] terminal_started:', data);
|
||||
if (data?.session) {
|
||||
const sessions = { ...ctx.terminalSessions };
|
||||
sessions[data.session] = {
|
||||
|
||||
@ -17,17 +17,16 @@ let showHtmlDebugCount = 0;
|
||||
const SHOW_HTML_DEBUG_MAX = 500;
|
||||
|
||||
function isShowHtmlDebugEnabled() {
|
||||
if (typeof window === 'undefined') return true;
|
||||
if (typeof window === 'undefined') return false;
|
||||
try {
|
||||
const flag = (window as any).__SHOW_HTML_DEBUG__;
|
||||
if (flag === false || flag === '0') return false;
|
||||
if (flag === true || flag === '1') return true;
|
||||
if (flag === false || flag === '0') return false;
|
||||
const localFlag = window.localStorage?.getItem('showHtmlDebug');
|
||||
if (localFlag === '0' || localFlag === 'false') return false;
|
||||
if (localFlag === '1' || localFlag === 'true') return true;
|
||||
return true;
|
||||
return false;
|
||||
} catch {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -77,7 +77,8 @@ function isScrollDebugEnabled() {
|
||||
}
|
||||
|
||||
function isShowHtmlScrollTraceEnabled() {
|
||||
if (typeof window === 'undefined') return true;
|
||||
// 默认关闭:排障时通过 window.__SHOW_HTML_SCROLL_TRACE__ = true 或 localStorage.showHtmlScrollTrace = '1' 显式打开
|
||||
if (typeof window === 'undefined') return false;
|
||||
try {
|
||||
const explicit = (window as any).__SHOW_HTML_SCROLL_TRACE__;
|
||||
if (explicit === false || explicit === '0') return false;
|
||||
@ -85,9 +86,9 @@ function isShowHtmlScrollTraceEnabled() {
|
||||
const localFlag = window.localStorage?.getItem('showHtmlScrollTrace');
|
||||
if (localFlag === '0' || localFlag === 'false') return false;
|
||||
if (localFlag === '1' || localFlag === 'true') return true;
|
||||
return true;
|
||||
return false;
|
||||
} catch {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -84,7 +84,6 @@ function clearAwaitingFirstContent(message: any) {
|
||||
}
|
||||
}
|
||||
const userMDebug = (...args: any[]) => {
|
||||
console.log('[USERMDEBUG]', ...args);
|
||||
};
|
||||
|
||||
export const useChatStore = defineStore('chat', {
|
||||
@ -222,11 +221,6 @@ export const useChatStore = defineStore('chat', {
|
||||
this.streamingMessage = true;
|
||||
message.awaitingFirstContent = true;
|
||||
|
||||
console.log('[DEBUG_AWAITING] ===== startAssistantMessage =====', {
|
||||
awaitingFirstContent: message.awaitingFirstContent,
|
||||
generatingLabel: message.generatingLabel
|
||||
});
|
||||
|
||||
return message;
|
||||
},
|
||||
startThinkingAction() {
|
||||
|
||||
@ -188,11 +188,6 @@ export const useConversationStore = defineStore('conversation', {
|
||||
{ reset = false, refresh = false } = {}
|
||||
) {
|
||||
if (!workspaceId) return;
|
||||
console.log('[sidebar-debug] loadWorkspaceConversations', {
|
||||
workspaceId,
|
||||
reset,
|
||||
refresh
|
||||
});
|
||||
let index = this.workspaceGroups.findIndex((g) => g.workspaceId === workspaceId);
|
||||
if (index === -1) {
|
||||
this.ensureWorkspaceGroup(workspaceId);
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { useModelStore } from './model';
|
||||
|
||||
|
||||
export type BlockDisplayMode = 'traditional' | 'stacked' | 'minimal';
|
||||
export type CompactMessageDisplay = 'full' | 'brief';
|
||||
type RunMode = 'fast' | 'thinking';
|
||||
@ -859,20 +858,17 @@ export const usePersonalizationStore = defineStore('personalization', {
|
||||
},
|
||||
async logout() {
|
||||
try {
|
||||
console.info('[auth-debug] logout clicked, sending POST /logout');
|
||||
const resp = await fetch('/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
cache: 'no-store'
|
||||
});
|
||||
console.info('[auth-debug] logout POST status:', resp.status);
|
||||
let payload: any = null;
|
||||
try {
|
||||
payload = await resp.json();
|
||||
} catch (_err) {
|
||||
payload = null;
|
||||
}
|
||||
console.info('[auth-debug] logout POST payload:', payload);
|
||||
if (resp.ok && (!payload || payload.success !== false)) {
|
||||
window.location.replace(`/login?logged_out=1&ts=${Date.now()}`);
|
||||
return;
|
||||
|
||||
@ -103,7 +103,6 @@ export const useResourceStore = defineStore('resource', {
|
||||
this.currentContextTokens = value || 0;
|
||||
},
|
||||
toggleTokenPanel() {
|
||||
console.log('[UI_DEBUG] toggleTokenPanel called, current state:', this.tokenPanelCollapsed, 'new state:', !this.tokenPanelCollapsed);
|
||||
this.tokenPanelCollapsed = !this.tokenPanelCollapsed;
|
||||
if (this.tokenPanelCollapsed) {
|
||||
this.stopContainerStatsPolling();
|
||||
|
||||
@ -6,13 +6,24 @@ const debugNotifyLog = (...args: any[]) => {
|
||||
void args;
|
||||
};
|
||||
const keyNotifyLog = (...args: any[]) => {
|
||||
console.log(...args);
|
||||
};
|
||||
const jsonDebug = (...args: any[]) => {
|
||||
console.log('[JSONDEBUG]', ...args);
|
||||
};
|
||||
const CONN_DIAG_PREFIX = '[CONN_DIAG]';
|
||||
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 ts = new Date().toISOString();
|
||||
const record = { ts, event, ...payload };
|
||||
@ -30,7 +41,9 @@ const taskPollDiag = (event: string, payload: Record<string, any> = {}) => {
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
console.log(CONN_DIAG_PREFIX, event, record);
|
||||
if (isConnDiagConsoleEnabled()) {
|
||||
console.log(CONN_DIAG_PREFIX, event, record);
|
||||
}
|
||||
};
|
||||
|
||||
export const useTaskStore = defineStore('task', {
|
||||
|
||||
@ -10,56 +10,43 @@ const applyTheme = (theme: ThemeKey) => {
|
||||
document.body.setAttribute('data-theme', theme);
|
||||
|
||||
// 调试信息
|
||||
console.log('=== Theme Applied ===');
|
||||
console.log('Theme:', theme);
|
||||
|
||||
// 检查样式是否生效
|
||||
setTimeout(() => {
|
||||
console.log('=== Elements Check ===');
|
||||
|
||||
// 输入栏底色
|
||||
const stadiumShell = document.querySelector('.stadium-shell');
|
||||
console.log('.stadium-shell exists:', !!stadiumShell);
|
||||
if (stadiumShell) {
|
||||
const styles = window.getComputedStyle(stadiumShell);
|
||||
console.log('.stadium-shell background-color:', styles.backgroundColor);
|
||||
}
|
||||
|
||||
// 对话区域顶部的模型选择器
|
||||
const ribbonSelector = document.querySelector('.conversation-ribbon__selector');
|
||||
console.log('.conversation-ribbon__selector exists:', !!ribbonSelector);
|
||||
if (ribbonSelector) {
|
||||
const styles = window.getComputedStyle(ribbonSelector);
|
||||
console.log('.conversation-ribbon__selector color:', styles.color);
|
||||
}
|
||||
|
||||
const selectorModel = document.querySelector('.selector-model');
|
||||
if (selectorModel) {
|
||||
const styles = window.getComputedStyle(selectorModel);
|
||||
console.log('.selector-model color:', styles.color);
|
||||
}
|
||||
|
||||
const selectorMode = document.querySelector('.selector-mode');
|
||||
if (selectorMode) {
|
||||
const styles = window.getComputedStyle(selectorMode);
|
||||
console.log('.selector-mode color:', styles.color);
|
||||
}
|
||||
|
||||
// 下拉菜单
|
||||
const dropdown = document.querySelector('.model-mode-dropdown');
|
||||
console.log('.model-mode-dropdown exists:', !!dropdown);
|
||||
if (dropdown) {
|
||||
const styles = window.getComputedStyle(dropdown);
|
||||
console.log('.model-mode-dropdown background-color:', styles.backgroundColor);
|
||||
}
|
||||
|
||||
const dropdownItem = document.querySelector('.dropdown-item');
|
||||
if (dropdownItem) {
|
||||
const styles = window.getComputedStyle(dropdownItem);
|
||||
console.log('.dropdown-item color:', styles.color);
|
||||
}
|
||||
|
||||
console.log('=== End Elements Check ===');
|
||||
}, 100);
|
||||
};
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
import os
|
||||
import json
|
||||
import base64
|
||||
import logging
|
||||
import mimetypes
|
||||
import io
|
||||
import uuid
|
||||
@ -13,6 +14,9 @@ from copy import deepcopy
|
||||
from typing import Dict, List, Optional, Any
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
from config import (
|
||||
MAX_CONTEXT_SIZE,
|
||||
@ -198,23 +202,16 @@ class TokenMixin:
|
||||
def safe_broadcast_token_update(self):
|
||||
"""安全的token更新广播(只广播累计统计,不重新计算)"""
|
||||
try:
|
||||
print(f"[Debug] 尝试广播token更新")
|
||||
|
||||
# 检查是否有回调函数
|
||||
if not hasattr(self, '_web_terminal_callback'):
|
||||
print(f"[Debug] 没有_web_terminal_callback属性")
|
||||
return
|
||||
|
||||
if not self._web_terminal_callback:
|
||||
print(f"[Debug] _web_terminal_callback为None")
|
||||
return
|
||||
|
||||
if not self.current_conversation_id:
|
||||
print(f"[Debug] 没有当前对话ID")
|
||||
return
|
||||
|
||||
print(f"[Debug] 广播token统计,对话ID: {self.current_conversation_id}")
|
||||
|
||||
# 只获取已有的累计token统计,不重新计算
|
||||
cumulative_stats = self.get_conversation_token_statistics()
|
||||
|
||||
@ -228,14 +225,8 @@ class TokenMixin:
|
||||
'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)
|
||||
|
||||
print(f"[Debug] token更新已广播")
|
||||
|
||||
except Exception as e:
|
||||
print(f"[Debug] 广播token更新失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
logger.warning(f"[TokenStats] 广播token更新失败: {e}")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user