Skip to content

Commit 13241fb

Browse files
committed
feat: stabilize thinking budget; Gemini 3 Pro levels; selector/logging updates
1 parent 08ad2a2 commit 13241fb

6 files changed

Lines changed: 91 additions & 40 deletions

File tree

browser_utils/page_controller.py

Lines changed: 78 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -138,26 +138,68 @@ async def _handle_thinking_budget(
138138
"""
139139
reasoning_effort = request_params.get("reasoning_effort")
140140

141-
# 使用归一化模块标准化参数
142141
directive = normalize_reasoning_effort(reasoning_effort)
143142
self.logger.info(f"[{self.req_id}] 思考模式指令: {format_directive_log(directive)}")
144143

145-
if self._uses_thinking_level(model_id_to_use) and await self._has_thinking_dropdown():
144+
uses_level = self._uses_thinking_level(model_id_to_use) and await self._has_thinking_dropdown()
145+
146+
def _should_enable_from_raw(rv: Any) -> bool:
147+
try:
148+
if isinstance(rv, str):
149+
rs = rv.strip().lower()
150+
if rs in ["high", "low", "none", "-1"]:
151+
return True
152+
v = int(rs)
153+
return v > 0
154+
if isinstance(rv, int):
155+
return rv > 0 or rv == -1
156+
except Exception:
157+
return False
158+
return False
159+
160+
desired_enabled = directive.thinking_enabled or _should_enable_from_raw(reasoning_effort)
161+
162+
has_main_toggle = self._model_has_main_thinking_toggle(model_id_to_use)
163+
if has_main_toggle:
164+
self.logger.info(f"[{self.req_id}] 开始设置主思考开关到: {'开启' if desired_enabled else '关闭'}")
165+
await self._control_thinking_mode_toggle(
166+
should_be_enabled=desired_enabled,
167+
check_client_disconnected=check_client_disconnected,
168+
)
169+
else:
170+
self.logger.info(f"[{self.req_id}] 该模型无主思考开关,跳过开关设置。")
171+
172+
if not desired_enabled:
173+
# 若关闭思考,则确保预算开关关闭(兼容旧UI)
174+
await self._control_thinking_budget_toggle(
175+
should_be_checked=False,
176+
check_client_disconnected=check_client_disconnected,
177+
)
178+
return
179+
180+
# 2) 已开启思考:根据模型类型设置等级或预算
181+
if uses_level:
182+
rv = reasoning_effort
146183
level_to_set = None
147-
if reasoning_effort is None:
148-
self.logger.info(
149-
f"[{self.req_id}] 该模型使用思考等级下拉,未指定 reasoning_effort,保持当前等级。"
150-
)
151-
return
152-
if not directive.thinking_enabled:
153-
level_to_set = "low"
154-
elif not directive.budget_enabled:
155-
level_to_set = "high"
184+
if isinstance(rv, str):
185+
rs = rv.strip().lower()
186+
if rs == "low":
187+
level_to_set = "low"
188+
elif rs in ["high", "none", "-1"]:
189+
level_to_set = "high"
190+
else:
191+
try:
192+
v = int(rs)
193+
level_to_set = "high" if v >= 8000 else "low"
194+
except Exception:
195+
level_to_set = None
196+
elif isinstance(rv, int):
197+
level_to_set = "high" if rv >= 8000 or rv == -1 else "low"
198+
199+
if level_to_set is None:
200+
self.logger.info(f"[{self.req_id}] 无法解析等级,保持当前等级。")
156201
else:
157-
level_to_set = (
158-
"high" if (directive.budget_value or 0) >= 8000 else "low"
159-
)
160-
await self._set_thinking_level(level_to_set, check_client_disconnected)
202+
await self._set_thinking_level(level_to_set, check_client_disconnected)
161203
return
162204

163205
if not directive.thinking_enabled:
@@ -236,6 +278,13 @@ def _uses_thinking_level(self, model_id_to_use: Optional[str]) -> bool:
236278
except Exception:
237279
return False
238280

281+
def _model_has_main_thinking_toggle(self, model_id_to_use: Optional[str]) -> bool:
282+
try:
283+
mid = (model_id_to_use or "").lower()
284+
return "flash" in mid
285+
except Exception:
286+
return False
287+
239288
async def _set_thinking_level(
240289
self, level: str, check_client_disconnected: Callable
241290
):
@@ -562,31 +611,37 @@ async def _control_thinking_mode_toggle(
562611

563612
try:
564613
toggle_locator = self.page.locator(toggle_selector)
565-
566-
# 等待元素可见(5秒超时)
567614
await expect_async(toggle_locator).to_be_visible(timeout=5000)
615+
try:
616+
await toggle_locator.scroll_into_view_if_needed()
617+
except Exception:
618+
pass
568619
await self._check_disconnect(check_client_disconnected, "主思考开关 - 元素可见后")
569620

570-
# 检查当前状态
571621
is_checked_str = await toggle_locator.get_attribute("aria-checked")
572622
current_state_is_enabled = is_checked_str == "true"
573623
self.logger.info(
574624
f"[{self.req_id}] 主思考开关当前状态: {is_checked_str} (是否开启: {current_state_is_enabled})"
575625
)
576626

577-
# 如果当前状态与期望状态不同,点击切换
578627
if current_state_is_enabled != should_be_enabled:
579628
action = "开启" if should_be_enabled else "关闭"
580629
self.logger.info(f"[{self.req_id}] 主思考开关需要切换,正在点击以{action}思考模式...")
581630

582-
await toggle_locator.click(timeout=CLICK_TIMEOUT_MS)
631+
try:
632+
await toggle_locator.click(timeout=CLICK_TIMEOUT_MS)
633+
except Exception:
634+
try:
635+
root = self.page.locator('mat-slide-toggle[data-test-toggle="enable-thinking"]')
636+
label = root.locator('label.mdc-label')
637+
await expect_async(label).to_be_visible(timeout=2000)
638+
await label.click(timeout=CLICK_TIMEOUT_MS)
639+
except Exception:
640+
raise
583641
await self._check_disconnect(
584642
check_client_disconnected, f"主思考开关 - 点击{action}后"
585643
)
586644

587-
# 等待状态更新
588-
await asyncio.sleep(0.5)
589-
590645
# 验证新状态
591646
new_state_str = await toggle_locator.get_attribute("aria-checked")
592647
new_state_is_enabled = new_state_str == "true"

browser_utils/thinking_normalizer.py

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -123,22 +123,11 @@ def _parse_budget_value(reasoning_effort: Any) -> Optional[int]:
123123
if isinstance(reasoning_effort, int) and reasoning_effort > 0:
124124
return reasoning_effort
125125

126-
# 如果是字符串,尝试匹配预设值或解析为数字
126+
# 如果是字符串,尝试解析为数字
127127
if isinstance(reasoning_effort, str):
128128
effort_str = reasoning_effort.strip().lower()
129129

130-
# 预设值映射
131-
effort_map = {
132-
"low": 1000,
133-
"medium": 8000,
134-
"high": 32768,
135-
}
136-
137-
# 先尝试预设值
138-
if effort_str in effort_map:
139-
return effort_map[effort_str]
140-
141-
# 再尝试解析为数字
130+
# 解析为数字
142131
try:
143132
value = int(effort_str)
144133
if value > 0:

config/selectors.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@
5454

5555
# --- 思考模式相关选择器 ---
5656
# 主思考开关:控制是否启用思考模式(总开关)
57-
ENABLE_THINKING_MODE_TOGGLE_SELECTOR = '[data-test-toggle="enable-thinking"] button'
57+
ENABLE_THINKING_MODE_TOGGLE_SELECTOR = (
58+
'mat-slide-toggle[data-test-toggle="enable-thinking"] button[role="switch"].mdc-switch, '
59+
'[data-test-toggle="enable-thinking"] button[role="switch"].mdc-switch'
60+
)
5861
# 手动预算开关:控制是否手动限制思考预算
5962
SET_THINKING_BUDGET_TOGGLE_SELECTOR = (
6063
'mat-slide-toggle[data-test-toggle="manual-budget"] button[role="switch"].mdc-switch, '

docs/api-usage.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,9 @@ response = requests.post(
177177
使用预设值:
178178
```json
179179
{
180-
"reasoning_effort": "low" // 1000 tokens
181-
// 或 "medium" (8000 tokens)
182-
// 或 "high" (32768 tokens)
180+
"reasoning_effort": 1000 // 使用具体数值限制预算
181+
// 或 8000
182+
// 或 32768
183183
}
184184
```
185185

stream/interceptors.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ def setup_logging():
2222
logging.StreamHandler()
2323
]
2424
)
25+
logging.getLogger('asyncio').setLevel(logging.ERROR)
26+
logging.getLogger('websockets').setLevel(logging.ERROR)
2527

2628
@staticmethod
2729
def should_intercept(host, path):

stream/main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ async def main():
3232
logging.StreamHandler()
3333
]
3434
)
35+
logging.getLogger('asyncio').setLevel(logging.ERROR)
36+
logging.getLogger('websockets').setLevel(logging.ERROR)
3537

3638
logger = logging.getLogger('main')
3739

0 commit comments

Comments
 (0)