Skip to content

Commit c9aa3de

Browse files
committed
refactor: modular architecture v2 with grid logging system and comprehensive test suite
1 parent c54fbe1 commit c9aa3de

129 files changed

Lines changed: 13641 additions & 9480 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -561,14 +561,12 @@ python gui_launcher.py
561561
#### 响应获取优先级
562562

563563
1. **第一层: 集成流式代理服务** (默认启用)
564-
565564
-**性能最优**: 直接转发流式响应,延迟最低
566565
- 📍 **端口**: 3120 (可通过 `STREAM_PORT` 配置)
567566
-**适用场景**: 流式请求、实时对话
568567
- ⚠️ **限制**: 参数支持有限,主要支持基础参数
569568

570569
2. **第二层: 外部 Helper 服务** (可选配置)
571-
572570
- 🔧 **需要**: 有效的认证文件 (`auth_profiles/active/*.json`)
573571
- 📡 **配置**: 通过 `--helper <endpoint>``.env` 配置
574572
-**适用场景**: 需要额外功能的场景

api_utils/app.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717

1818
import stream
1919

20+
# --- 导入集中状态模块 ---
21+
from api_utils.server_state import state
22+
2023
# --- browser_utils模块导入 ---
2124
from browser_utils import (
2225
_close_page_logic, # pyright: ignore[reportPrivateUsage]
@@ -35,9 +38,6 @@
3538
# --- models模块导入 ---
3639
from models import WebSocketConnectionManager
3740

38-
# --- 导入集中状态模块 ---
39-
from api_utils.server_state import state
40-
4141
from . import auth_utils
4242

4343

@@ -167,17 +167,23 @@ async def _shutdown_resources() -> None:
167167
logger = state.logger
168168
logger.info("Shutting down resources...")
169169

170+
# Signal all streaming generators to exit immediately
171+
state.should_exit = True
172+
170173
if state.STREAM_PROCESS:
171174
state.STREAM_PROCESS.terminate()
172175
logger.info("STREAM proxy terminated.")
173176

174177
if state.worker_task and not state.worker_task.done():
178+
logger.info("Cancelling worker task...")
175179
state.worker_task.cancel()
176180
try:
177-
await asyncio.wait_for(state.worker_task, timeout=5.0)
178-
except (asyncio.TimeoutError, asyncio.CancelledError):
179-
pass
180-
logger.info("Worker task stopped.")
181+
await asyncio.wait_for(state.worker_task, timeout=2.0)
182+
logger.info("Worker task cancelled.")
183+
except asyncio.TimeoutError:
184+
logger.warning("Worker task did not respond to cancellation within 2s.")
185+
except asyncio.CancelledError:
186+
logger.info("Worker task cancelled.")
181187

182188
if state.page_instance:
183189
await _close_page_logic()

api_utils/client_connection.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from fastapi import HTTPException, Request
77

8+
from logging_utils import set_request_id
89
from models import ClientDisconnectedError
910

1011

@@ -61,14 +62,15 @@ async def setup_disconnect_monitoring(
6162

6263
logger = logging.getLogger("AIStudioProxyServer")
6364
client_disconnected_event = Event()
65+
set_request_id(req_id)
6466

6567
async def check_disconnect_periodically() -> None:
6668
while not client_disconnected_event.is_set():
6769
try:
6870
is_connected = await check_client_connection(req_id, http_request)
6971
if not is_connected:
7072
logger.info(
71-
f"[{req_id}] Active disconnect check detected client disconnection."
73+
"Active disconnect check detected client disconnection."
7274
)
7375
client_disconnected_event.set()
7476
if not result_future.done():
@@ -85,7 +87,7 @@ async def check_disconnect_periodically() -> None:
8587
# Task cancelled, exit gracefully
8688
break
8789
except Exception as e:
88-
logger.error(f"[{req_id}] (Disco Check Task) Error: {e}")
90+
logger.error(f"(Disco Check Task) Error: {e}")
8991
client_disconnected_event.set()
9092
if not result_future.done():
9193
result_future.set_exception(
@@ -100,7 +102,7 @@ async def check_disconnect_periodically() -> None:
100102

101103
def check_client_disconnected(stage: str = "") -> bool:
102104
if client_disconnected_event.is_set():
103-
logger.info(f"[{req_id}] Client disconnected detected at stage: '{stage}'")
105+
logger.info(f"Client disconnected detected at stage: '{stage}'")
104106
raise ClientDisconnectedError(
105107
f"[{req_id}] Client disconnected at stage: {stage}"
106108
)
@@ -116,13 +118,14 @@ async def enhanced_disconnect_monitor(
116118
Enhanced disconnect monitor for streaming responses.
117119
Returns True if client disconnected early.
118120
"""
121+
set_request_id(req_id)
119122
client_disconnected_early = False
120123
while not completion_event.is_set():
121124
try:
122125
is_connected = await check_client_connection(req_id, http_request)
123126
if not is_connected:
124127
logger.info(
125-
f"[{req_id}] (Monitor) Client disconnected during streaming, triggering completion event."
128+
"(Monitor) Client disconnected during streaming, triggering completion event."
126129
)
127130
client_disconnected_early = True
128131
if not completion_event.is_set():
@@ -132,7 +135,7 @@ async def enhanced_disconnect_monitor(
132135
except asyncio.CancelledError:
133136
break
134137
except Exception as e:
135-
logger.error(f"[{req_id}] (Monitor) Enhanced disconnect checker error: {e}")
138+
logger.error(f"(Monitor) Enhanced disconnect checker error: {e}")
136139
break
137140
return client_disconnected_early
138141

@@ -147,13 +150,14 @@ async def non_streaming_disconnect_monitor(
147150
Disconnect monitor for non-streaming responses.
148151
Returns True if client disconnected early.
149152
"""
153+
set_request_id(req_id)
150154
client_disconnected_early = False
151155
while not result_future.done():
152156
try:
153157
is_connected = await check_client_connection(req_id, http_request)
154158
if not is_connected:
155159
logger.info(
156-
f"[{req_id}] (Monitor) Client disconnected during non-streaming processing."
160+
"(Monitor) Client disconnected during non-streaming processing."
157161
)
158162
client_disconnected_early = True
159163
if not result_future.done():
@@ -168,8 +172,6 @@ async def non_streaming_disconnect_monitor(
168172
except asyncio.CancelledError:
169173
break
170174
except Exception as e:
171-
logger.error(
172-
f"[{req_id}] (Monitor) Non-streaming disconnect checker error: {e}"
173-
)
175+
logger.error(f"(Monitor) Non-streaming disconnect checker error: {e}")
174176
break
175177
return client_disconnected_early

api_utils/context_init.py

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from logging_utils import set_request_id
12
from models import ChatCompletionRequest
23

34
from .context_types import RequestContext
@@ -6,31 +7,21 @@
67
async def initialize_request_context(
78
req_id: str, request: ChatCompletionRequest
89
) -> RequestContext:
9-
from server import (
10-
current_ai_studio_model_id,
11-
is_page_ready,
12-
logger,
13-
model_switching_lock,
14-
page_instance,
15-
page_params_cache,
16-
params_cache_lock,
17-
parsed_model_list,
18-
)
10+
from api_utils.server_state import state
1911

20-
logger.info(f"[{req_id}] 开始处理请求...")
21-
logger.info(
22-
f"[{req_id}] 请求参数 - Model: {request.model}, Stream: {request.stream}"
23-
)
12+
set_request_id(req_id)
13+
state.logger.info("开始处理请求...")
14+
state.logger.info(f" 请求参数 - Model: {request.model}, Stream: {request.stream}")
2415

2516
context: RequestContext = {
26-
"logger": logger,
27-
"page": page_instance,
28-
"is_page_ready": is_page_ready,
29-
"parsed_model_list": parsed_model_list,
30-
"current_ai_studio_model_id": current_ai_studio_model_id,
31-
"model_switching_lock": model_switching_lock,
32-
"page_params_cache": page_params_cache,
33-
"params_cache_lock": params_cache_lock,
17+
"logger": state.logger,
18+
"page": state.page_instance,
19+
"is_page_ready": state.is_page_ready,
20+
"parsed_model_list": state.parsed_model_list,
21+
"current_ai_studio_model_id": state.current_ai_studio_model_id,
22+
"model_switching_lock": state.model_switching_lock,
23+
"page_params_cache": state.page_params_cache,
24+
"params_cache_lock": state.params_cache_lock,
3425
"is_streaming": request.stream,
3526
"model_actually_switched": False,
3627
"requested_model": request.model,

api_utils/model_switching.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
from playwright.async_api import Page as AsyncPage
22

33
from api_utils.server_state import state
4+
from logging_utils import set_request_id
45

56
from .context_types import RequestContext
67

78

89
async def analyze_model_requirements(
910
req_id: str, context: RequestContext, requested_model: str, proxy_model_name: str
1011
) -> RequestContext:
12+
set_request_id(req_id)
1113
logger = context["logger"]
1214
current_ai_studio_model_id = context["current_ai_studio_model_id"]
1315
parsed_model_list = context["parsed_model_list"]
1416

1517
if requested_model and requested_model != proxy_model_name:
1618
requested_model_id = requested_model.split("/")[-1]
17-
logger.info(f"[{req_id}] 请求使用模型: {requested_model_id}")
19+
logger.info(f"请求使用模型: {requested_model_id}")
1820

1921
if parsed_model_list:
2022
valid_model_ids = [m.get("id") for m in parsed_model_list]
@@ -30,7 +32,7 @@ async def analyze_model_requirements(
3032
if current_ai_studio_model_id != requested_model_id:
3133
context["needs_model_switching"] = True
3234
logger.info(
33-
f"[{req_id}] 需要切换模型: 当前={current_ai_studio_model_id} -> 目标={requested_model_id}"
35+
f"需要切换模型: 当前={current_ai_studio_model_id} -> 目标={requested_model_id}"
3436
)
3537

3638
return context
@@ -39,6 +41,7 @@ async def analyze_model_requirements(
3941
async def handle_model_switching(
4042
req_id: str, context: RequestContext
4143
) -> RequestContext:
44+
set_request_id(req_id)
4245
if not context["needs_model_switching"]:
4346
return context
4447

@@ -50,7 +53,7 @@ async def handle_model_switching(
5053
async with model_switching_lock:
5154
if state.current_ai_studio_model_id != model_id_to_use:
5255
logger.info(
53-
f"[{req_id}] 准备切换模型: {state.current_ai_studio_model_id} -> {model_id_to_use}"
56+
f"准备切换模型: {state.current_ai_studio_model_id} -> {model_id_to_use}"
5457
)
5558
from browser_utils import switch_ai_studio_model
5659

@@ -59,9 +62,7 @@ async def handle_model_switching(
5962
state.current_ai_studio_model_id = model_id_to_use
6063
context["model_actually_switched"] = True
6164
context["current_ai_studio_model_id"] = model_id_to_use
62-
logger.info(
63-
f"[{req_id}] 模型切换成功: {state.current_ai_studio_model_id}"
64-
)
65+
logger.info(f"模型切换成功: {state.current_ai_studio_model_id}")
6566
else:
6667
await _handle_model_switch_failure(
6768
req_id,
@@ -77,7 +78,8 @@ async def handle_model_switching(
7778
async def _handle_model_switch_failure(
7879
req_id: str, page: AsyncPage, model_id_to_use: str, model_before_switch: str, logger
7980
) -> None:
80-
logger.warning(f"[{req_id}] 模型切换至 {model_id_to_use} 失败。")
81+
set_request_id(req_id)
82+
logger.warning(f"模型切换至 {model_id_to_use} 失败。")
8183
state.current_ai_studio_model_id = model_before_switch
8284
from .error_utils import http_error
8385

@@ -87,6 +89,7 @@ async def _handle_model_switch_failure(
8789

8890

8991
async def handle_parameter_cache(req_id: str, context: RequestContext) -> None:
92+
set_request_id(req_id)
9093
logger = context["logger"]
9194
params_cache_lock = context["params_cache_lock"]
9295
page_params_cache = context["page_params_cache"]
@@ -100,7 +103,7 @@ async def handle_parameter_cache(req_id: str, context: RequestContext) -> None:
100103
if model_actually_switched or (
101104
current_ai_studio_model_id != cached_model_for_params
102105
):
103-
logger.info(f"[{req_id}] 模型已更改,参数缓存失效。")
106+
logger.info("模型已更改,参数缓存失效。")
104107
page_params_cache.clear()
105108
page_params_cache["last_known_model_id_for_params"] = (
106109
current_ai_studio_model_id

api_utils/page_response.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,23 @@
55
from playwright.async_api import expect as expect_async
66

77
from config import RESPONSE_CONTAINER_SELECTOR, RESPONSE_TEXT_SELECTOR
8+
from logging_utils import set_request_id
89

910

1011
async def locate_response_elements(
1112
page: AsyncPage, req_id: str, logger, check_client_disconnected
1213
) -> None:
1314
"""定位响应容器与文本元素,包含超时与错误处理。"""
14-
logger.info(f"[{req_id}] 定位响应元素...")
15+
set_request_id(req_id)
16+
logger.info("定位响应元素...")
1517
response_container = page.locator(RESPONSE_CONTAINER_SELECTOR).last
1618
response_element = response_container.locator(RESPONSE_TEXT_SELECTOR)
1719

1820
try:
1921
await expect_async(response_container).to_be_attached(timeout=20000)
2022
check_client_disconnected("After Response Container Attached: ")
2123
await expect_async(response_element).to_be_attached(timeout=90000)
22-
logger.info(f"[{req_id}] 响应元素已定位。")
24+
logger.info("响应元素已定位。")
2325
except (PlaywrightAsyncError, asyncio.TimeoutError) as locate_err:
2426
from .error_utils import upstream_error
2527

0 commit comments

Comments
 (0)