mirror of
https://github.com/EstrellaXD/Auto_Bangumi.git
synced 2026-07-11 22:47:18 +08:00
perf(startup): defer LLM SDK imports and gate the volume chown
Two things dominated container start: - The LLM SDKs (anthropic + openai + google-genai) imported at module load via parser.analyser/__init__ → llm.py, costing ~0.7s on every boot even though the LLM parser is off by default. Move those three heavy imports into the provider branch of __init__ and each _parse_* method, so they load only when an LLMParser is actually built. Measured: importing parser.analyser drops 782ms → 203ms; none of the SDKs are in sys.modules after the startup import chain. httpx/httpx_socks stay module-level (cheap, and the proxy tests patch them by module path). - entrypoint.sh ran `chown -R /app/data /app/config` unconditionally every start — O(files), which a large poster cache on a NAS turns into the dominant startup cost. Walk the tree only when ownership needs fixing (first run, changed PUID/PGID, or a wrong-owned volume root), tracked by a marker; the legacy bangumi.json migration chowns its one file explicitly so the gate can't miss it. /home/ab (image-internal, tiny) is still chowned every time. Also regenerates the auto-imports dts for onResponseError (exported for the 401 test in the previous commit). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FAxVyRwrY7z5NotTtgnwVs
This commit is contained in:
@@ -14,18 +14,17 @@ import json
|
||||
import logging
|
||||
from typing import Any, Optional
|
||||
|
||||
import anthropic
|
||||
import httpx
|
||||
import openai as openai_sdk
|
||||
from google import genai
|
||||
from google.genai import errors as genai_errors
|
||||
from google.genai import types as genai_types
|
||||
from httpx_socks import AsyncProxyTransport
|
||||
from openai import AsyncOpenAI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from module.conf import settings
|
||||
|
||||
# anthropic / openai / google-genai 合计导入约 0.7s,而 LLM 默认关闭。
|
||||
# 这些重型 SDK 改为在真正构造/调用某提供商时才导入(见 __init__ 与各
|
||||
# _parse_* 方法),把这段耗时移出容器启动路径。httpx / httpx_socks 保持
|
||||
# 顶层导入:它们本就在网络层被加载,且测试按模块级名字 patch 它们。
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -123,6 +122,8 @@ class LLMParser:
|
||||
self.model = model
|
||||
self._http_client: httpx.AsyncClient | None = None
|
||||
if provider == "openai":
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
self._http_client = _build_http_client(timeout)
|
||||
self._openai_client = AsyncOpenAI(
|
||||
api_key=api_key,
|
||||
@@ -131,6 +132,8 @@ class LLMParser:
|
||||
http_client=self._http_client,
|
||||
)
|
||||
elif provider == "anthropic":
|
||||
import anthropic
|
||||
|
||||
self._http_client = _build_http_client(timeout)
|
||||
self._anthropic_client = anthropic.AsyncAnthropic(
|
||||
api_key=api_key,
|
||||
@@ -138,6 +141,8 @@ class LLMParser:
|
||||
http_client=self._http_client,
|
||||
)
|
||||
elif provider == "gemini":
|
||||
from google import genai
|
||||
|
||||
self._gemini_client = genai.Client(api_key=api_key)
|
||||
else:
|
||||
raise ValueError(f"Unsupported LLM provider: {provider}")
|
||||
@@ -187,6 +192,8 @@ class LLMParser:
|
||||
return result
|
||||
|
||||
async def _parse_openai(self, raw: str) -> dict | None:
|
||||
import openai as openai_sdk
|
||||
|
||||
try:
|
||||
resp = await self._openai_client.beta.chat.completions.parse(
|
||||
model=self.model,
|
||||
@@ -208,6 +215,8 @@ class LLMParser:
|
||||
return parsed.model_dump()
|
||||
|
||||
async def _parse_anthropic(self, raw: str) -> dict | None:
|
||||
import anthropic
|
||||
|
||||
try:
|
||||
resp = await self._anthropic_client.messages.create(
|
||||
model=self.model,
|
||||
@@ -236,6 +245,9 @@ class LLMParser:
|
||||
return None
|
||||
|
||||
async def _parse_gemini(self, raw: str) -> dict | None:
|
||||
from google.genai import errors as genai_errors
|
||||
from google.genai import types as genai_types
|
||||
|
||||
try:
|
||||
resp = await self._gemini_client.aio.models.generate_content(
|
||||
model=self.model,
|
||||
|
||||
@@ -5,6 +5,9 @@ umask ${UMASK}
|
||||
|
||||
if [ -f /config/bangumi.json ]; then
|
||||
mv /config/bangumi.json /app/data/bangumi.json
|
||||
# Created as root by the mv; the gated chown below may skip the tree walk,
|
||||
# so fix this one legacy file explicitly.
|
||||
chown ab:ab /app/data/bangumi.json
|
||||
fi
|
||||
|
||||
groupmod -o -g "${PGID}" ab
|
||||
@@ -29,7 +32,20 @@ while true; do
|
||||
# 兜底 try/except,这里再加 `|| true`:覆盖层失败绝不能阻断容器启动。
|
||||
python /app/boot_overlay.py || true
|
||||
|
||||
chown ab:ab -R /app/data /app/config /home/ab
|
||||
# Recursive chown of the mounted volumes is O(files): a large poster cache
|
||||
# on a NAS makes it the dominant cost of every container start. Only walk
|
||||
# the tree when ownership actually needs fixing — first run or a changed
|
||||
# PUID/PGID — recorded by a marker. /home/ab is image-internal and tiny, so
|
||||
# chown it every time.
|
||||
chown ab:ab -R /home/ab
|
||||
own_marker="/app/config/.ab_owner"
|
||||
want="${PUID}:${PGID}"
|
||||
if [ "$(cat "${own_marker}" 2>/dev/null)" != "${want}" ] ||
|
||||
[ "$(stat -c '%u:%g' /app/data 2>/dev/null)" != "${want}" ] ||
|
||||
[ "$(stat -c '%u:%g' /app/config 2>/dev/null)" != "${want}" ]; then
|
||||
echo "${want}" >"${own_marker}"
|
||||
chown ab:ab -R /app/data /app/config
|
||||
fi
|
||||
|
||||
su-exec "${PUID}:${PGID}" python main.py &
|
||||
child=$!
|
||||
|
||||
1
webui/types/dts/auto-imports.d.ts
vendored
1
webui/types/dts/auto-imports.d.ts
vendored
@@ -68,6 +68,7 @@ declare global {
|
||||
const onMounted: typeof import('vue')['onMounted']
|
||||
const onRenderTracked: typeof import('vue')['onRenderTracked']
|
||||
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
|
||||
const onResponseError: typeof import('../../src/utils/axios')['onResponseError']
|
||||
const onScopeDispose: typeof import('vue')['onScopeDispose']
|
||||
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
|
||||
const onUnmounted: typeof import('vue')['onUnmounted']
|
||||
|
||||
Reference in New Issue
Block a user