perf: 降低 lru_cache 上限并限制流式响应块内存占用

- 缩减 model_permissions / tiktoken / formula_engine 的 lru_cache maxsize
- StreamUsageTracker 响应块增加 4MB 大小限制,超限后只计数不存储
- raw_chunks 改用 deque(maxlen=50) 避免无限增长
- HardwareTooltip 导入路径修正、tooltip 延迟归零、文案中文化
This commit is contained in:
fawney19
2026-02-12 11:41:42 +08:00
parent 483d536e2c
commit be430ebdde
5 changed files with 41 additions and 21 deletions

View File

@@ -1,11 +1,6 @@
<script setup lang="ts">
import type { ProxyNode } from '@/api/proxy-nodes'
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from '@/components/ui'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
import { Cpu } from 'lucide-vue-next'
import { computed } from 'vue'
@@ -77,7 +72,10 @@ function formatNumber(n: number) {
</script>
<template>
<TooltipProvider v-if="showHardwareInfo">
<TooltipProvider
v-if="showHardwareInfo"
:delay-duration="0"
>
<Tooltip>
<TooltipTrigger as-child>
<button
@@ -97,7 +95,7 @@ function formatNumber(n: number) {
v-if="hardwareRows.length === 0"
class="text-muted-foreground"
>
No hardware info reported.
暂无硬件信息上报
</div>
<template v-else>
<div

View File

@@ -236,7 +236,7 @@ def validate_and_extract_model_mappings(
return True, None, mappings
@lru_cache(maxsize=2000)
@lru_cache(maxsize=512)
def _compile_pattern_cached(pattern: str) -> regex.Pattern | None:
"""
编译正则模式(带 LRU 缓存)

View File

@@ -22,7 +22,7 @@ except ImportError: # pragma: no cover
tiktoken = None
@lru_cache(maxsize=256)
@lru_cache(maxsize=32)
def _get_encoder_cached(model: str) -> Any:
"""全局编码器缓存。

View File

@@ -71,7 +71,7 @@ def _iter_ast_nodes(node: ast.AST) -> Iterable[ast.AST]:
yield from _iter_ast_nodes(child)
@lru_cache(maxsize=2048)
@lru_cache(maxsize=256)
def _validate_expression_cached(expression: str) -> ast.Expression:
"""
Parse + validate an expression and cache the resulting AST.

View File

@@ -7,6 +7,7 @@ from __future__ import annotations
import json
import re
from collections import deque
from collections.abc import AsyncIterator
from typing import Any
@@ -108,8 +109,13 @@ class StreamUsageTracker:
"stop_sequence": None,
"usage": {},
}
self.response_chunks = [] # 保存所有原始响应块
self.raw_chunks = [] # 保存所有原始字节流(用于错误诊断
self.response_chunks = [] # 保存解析后的响应块
self.response_chunks_count = 0 # 响应块总计数(含被丢弃的
self.response_chunks_size = 0 # 响应块累计序列化大小(字节)
self._response_chunks_max_size = 4 * 1024 * 1024 # 4MB留余量给 truncate_body 的 5MB 上限
self.raw_chunks: deque[str | bytes] = deque(
maxlen=50
) # 仅保留最后50个原始chunk用于错误诊断
# 时间跟踪
self.start_time = None
@@ -133,6 +139,15 @@ class StreamUsageTracker:
self.error_message = None # 错误消息(如果有)
self.attempt_id = attempt_id
def _append_response_chunk(self, data: dict[str, Any]) -> None:
"""追加响应块,超过大小限制后只计数不存储"""
self.response_chunks_count += 1
if self.response_chunks_size < self._response_chunks_max_size:
chunk_size = len(json.dumps(data, ensure_ascii=False))
self.response_chunks_size += chunk_size
if self.response_chunks_size <= self._response_chunks_max_size:
self.response_chunks.append(data)
def set_error_status(self, status_code: int, error_message: str) -> None:
"""
设置错误状态
@@ -275,7 +290,7 @@ class StreamUsageTracker:
data = json.loads(data_str)
if isinstance(data, dict):
self.response_chunks.append(data)
self._append_response_chunk(data)
try:
self._update_complete_response(data)
except Exception as update_error:
@@ -357,7 +372,7 @@ class StreamUsageTracker:
# 更新完整响应(如果有数据)
if chunk.data:
self.response_chunks.append(chunk.data)
self._append_response_chunk(chunk.data)
try:
self._update_complete_response(chunk.data)
except Exception as update_error:
@@ -635,14 +650,21 @@ class StreamUsageTracker:
# 否则使用原始字节流用于错误诊断如403 HTML响应
if self.response_chunks:
# 正常情况成功解析的SSE JSON响应
stored_chunks = len(self.response_chunks)
total_chunks = self.response_chunks_count
metadata = {
"stream": True,
"total_chunks": total_chunks,
"stored_chunks": stored_chunks,
"content_length": len(self.accumulated_content),
"response_time_ms": response_time_ms,
}
if stored_chunks < total_chunks:
metadata["truncated"] = True
metadata["dropped_chunks"] = total_chunks - stored_chunks
response_body = {
"chunks": self.response_chunks,
"metadata": {
"stream": True,
"total_chunks": len(self.response_chunks),
"content_length": len(self.accumulated_content),
"response_time_ms": response_time_ms,
},
"metadata": metadata,
}
else:
# 错误情况无法解析为JSON如HTML错误页面