agent-Specialization/server/state.py
JOJO d6fb59e1d8 refactor: split web_server into modular architecture
- Refactor 6000+ line web_server.py into server/ module
- Create separate modules: auth, chat, conversation, files, admin, etc.
- Keep web_server.py as backward-compatible entry point
- Add container running status field in user_container_manager
- Improve admin dashboard API with credentials and debug support

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-22 09:21:53 +08:00

143 lines
4.9 KiB
Python

"""共享状态与常量,供各子模块使用。"""
from __future__ import annotations
import os
import threading
from collections import defaultdict, deque
from pathlib import Path
from typing import Dict, Any, Optional
from config import LOGS_DIR, PROJECT_MAX_STORAGE_BYTES, PROJECT_MAX_STORAGE_MB
from core.web_terminal import WebTerminal
from modules.custom_tool_registry import CustomToolRegistry
from modules.usage_tracker import UsageTracker
from modules.user_container_manager import UserContainerManager
from modules.user_manager import UserManager
# 全局实例
user_manager = UserManager()
custom_tool_registry = CustomToolRegistry()
container_manager = UserContainerManager()
user_terminals: Dict[str, WebTerminal] = {}
terminal_rooms: Dict[str, set] = {}
connection_users: Dict[str, str] = {}
RECENT_UPLOAD_EVENT_LIMIT = 150
RECENT_UPLOAD_FEED_LIMIT = 60
stop_flags: Dict[str, Dict[str, Any]] = {}
# 监控/限流/用量
MONITOR_FILE_TOOLS = {'append_to_file', 'modify_file', 'write_file_diff'}
MONITOR_MEMORY_TOOLS = {'update_memory'}
MONITOR_SNAPSHOT_CHAR_LIMIT = 60000
MONITOR_MEMORY_ENTRY_LIMIT = 256
RATE_LIMIT_BUCKETS: Dict[str, deque] = defaultdict(deque)
FAILURE_TRACKERS: Dict[str, Dict[str, float]] = {}
pending_socket_tokens: Dict[str, Dict[str, Any]] = {}
usage_trackers: Dict[str, UsageTracker] = {}
MONITOR_SNAPSHOT_CACHE: Dict[str, Dict[str, Any]] = {}
MONITOR_SNAPSHOT_CACHE_LIMIT = 120
RECENT_UPLOAD_EVENT_LIMIT = 150
RECENT_UPLOAD_FEED_LIMIT = 60
# 路径与缓存设置(依赖项目配置)
PROJECT_STORAGE_CACHE: Dict[str, Dict[str, Any]] = {}
PROJECT_STORAGE_CACHE_TTL_SECONDS = float(os.environ.get("PROJECT_STORAGE_CACHE_TTL", "30"))
# 其他配置
DEFAULT_PORT = 8091
THINKING_FAILURE_KEYWORDS = ["⚠️", "🛑", "失败", "错误", "异常", "终止", "error", "failed", "未完成", "超时", "强制"]
CSRF_HEADER_NAME = "X-CSRF-Token"
CSRF_SESSION_KEY = "_csrf_token"
CSRF_SAFE_METHODS = {"GET", "HEAD", "OPTIONS", "TRACE"}
CSRF_PROTECTED_PATHS = {"/login", "/register", "/logout"}
CSRF_PROTECTED_PREFIXES = ("/api/",)
CSRF_EXEMPT_PATHS = {"/api/csrf-token"}
FAILED_LOGIN_LIMIT = 5
FAILED_LOGIN_LOCK_SECONDS = 300
SOCKET_TOKEN_TTL_SECONDS = 45
USER_IDLE_TIMEOUT_SECONDS = int(os.environ.get("USER_IDLE_TIMEOUT_SECONDS", "900"))
LAST_ACTIVE_FILE = Path(LOGS_DIR).expanduser().resolve() / "last_active.json"
_last_active_lock = threading.Lock()
_last_active_cache: Dict[str, float] = {}
_idle_reaper_started = False
TITLE_PROMPT_PATH = Path(__file__).resolve().parent.parent / "prompts" / "title_generation_prompt.txt"
# 项目存储限制常量也会被使用
PROJECT_MAX_STORAGE_BYTES = PROJECT_MAX_STORAGE_BYTES
PROJECT_MAX_STORAGE_MB = PROJECT_MAX_STORAGE_MB
__all__ = [
"user_manager",
"custom_tool_registry",
"container_manager",
"user_terminals",
"terminal_rooms",
"connection_users",
"stop_flags",
"MONITOR_FILE_TOOLS",
"MONITOR_MEMORY_TOOLS",
"MONITOR_SNAPSHOT_CHAR_LIMIT",
"MONITOR_MEMORY_ENTRY_LIMIT",
"RATE_LIMIT_BUCKETS",
"FAILURE_TRACKERS",
"pending_socket_tokens",
"usage_trackers",
"MONITOR_SNAPSHOT_CACHE",
"MONITOR_SNAPSHOT_CACHE_LIMIT",
"PROJECT_STORAGE_CACHE",
"PROJECT_STORAGE_CACHE_TTL_SECONDS",
"DEFAULT_PORT",
"THINKING_FAILURE_KEYWORDS",
"CSRF_HEADER_NAME",
"CSRF_SESSION_KEY",
"CSRF_SAFE_METHODS",
"CSRF_PROTECTED_PATHS",
"CSRF_PROTECTED_PREFIXES",
"CSRF_EXEMPT_PATHS",
"FAILED_LOGIN_LIMIT",
"FAILED_LOGIN_LOCK_SECONDS",
"SOCKET_TOKEN_TTL_SECONDS",
"USER_IDLE_TIMEOUT_SECONDS",
"LAST_ACTIVE_FILE",
"_last_active_lock",
"_last_active_cache",
"_idle_reaper_started",
"TITLE_PROMPT_PATH",
"PROJECT_MAX_STORAGE_BYTES",
"PROJECT_MAX_STORAGE_MB",
"RECENT_UPLOAD_EVENT_LIMIT",
"RECENT_UPLOAD_FEED_LIMIT",
"get_last_active_ts",
]
def get_last_active_ts(username: str, fallback: Optional[float] = None) -> Optional[float]:
"""
返回最近活跃时间,优先使用缓存;当容器句柄中的时间更新、更晚时,自动刷新缓存。
这样避免“缓存过旧导致刚触碰的容器被立即回收”的问题。
"""
fallback_val: Optional[float]
try:
fallback_val = float(fallback) if fallback is not None else None
except (TypeError, ValueError):
fallback_val = None
with _last_active_lock:
cached = _last_active_cache.get(username)
try:
cached_val = float(cached) if cached is not None else None
except (TypeError, ValueError):
cached_val = None
# 若没有缓存,或句柄时间更新、更晚,则刷新缓存
if cached_val is None:
if fallback_val is not None:
_last_active_cache[username] = fallback_val
return fallback_val
if fallback_val is not None and fallback_val > cached_val:
_last_active_cache[username] = fallback_val
return fallback_val
return cached_val