From 37931d2ef5901bd5bfbf057f1da615fbd2530932 Mon Sep 17 00:00:00 2001 From: yumao Date: Fri, 3 Jul 2026 16:38:21 +0800 Subject: [PATCH] fix: raise Cursor SDK bridge timeout for daily LLM calls Co-authored-by: Cursor --- .env.example | 1 + daily/cursor_client.py | 41 ++++++++++++++++++++++++++++++++++--- tests/test_cursor_client.py | 15 ++++++++++++++ 3 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 tests/test_cursor_client.py diff --git a/.env.example b/.env.example index 7ede0ff..8ddb77c 100644 --- a/.env.example +++ b/.env.example @@ -65,6 +65,7 @@ DAILY_CN_AI_NEWS=1 # cursor — 强制 Cursor SDK # CURSOR_API_KEY=cursor_... # CURSOR_MODEL=composer-2.5 +# CURSOR_TIMEOUT=600 # DAILY_CURSOR_CWD=. # 新增榜对比(较昨日 Top15,供 Agent 导语/signals;列表展示 Top N) diff --git a/daily/cursor_client.py b/daily/cursor_client.py index a6a7079..5a34d37 100644 --- a/daily/cursor_client.py +++ b/daily/cursor_client.py @@ -2,18 +2,52 @@ from __future__ import annotations -from cursor_sdk import Agent, AgentOptions, CursorAgentError, LocalAgentOptions +import os -from daily.config import env +from cursor_sdk import Agent, AgentOptions, Client, CursorAgentError, LocalAgentOptions + +from daily.config import env, env_int from daily.cursor_bridge import cursor_cwd, warm_cursor_bridge +_sdk_client: Client | None = None +_sdk_client_key: tuple[str, str, float] | None = None + + +def cursor_timeout_seconds() -> float: + """Cursor bridge unary/stream 超时(秒);与 bot 共用 CURSOR_TIMEOUT。""" + return float(env_int("CURSOR_TIMEOUT", 600)) + + +def _cursor_sdk_client() -> Client: + """带自定义超时的 bridge Client(SDK 默认 unary 仅 60s,复杂 Agent 任务易超时)。""" + global _sdk_client, _sdk_client_key + + warm_cursor_bridge() + url = os.environ.get("CURSOR_SDK_BRIDGE_URL", "") + token = os.environ.get("CURSOR_SDK_BRIDGE_TOKEN", "") + timeout = cursor_timeout_seconds() + key = (url, token, timeout) + if _sdk_client is not None and _sdk_client_key == key: + return _sdk_client + if _sdk_client is not None: + _sdk_client.close() + _sdk_client = Client( + base_url=url, + auth_token=token, + unary_timeout=timeout, + stream_timeout=timeout, + allow_api_key_env_fallback=False, + ) + _sdk_client_key = key + return _sdk_client + def cursor_chat(system: str, user: str) -> str: api_key = (env("CURSOR_API_KEY") or "").strip() if not api_key: return "" - warm_cursor_bridge() + client = _cursor_sdk_client() cwd = cursor_cwd() model = env("CURSOR_MODEL") or "composer-2.5" prompt = f"{system}\n\n{user}" @@ -25,6 +59,7 @@ def cursor_chat(system: str, user: str) -> str: model=model, local=LocalAgentOptions(cwd=cwd), ), + client=client, ) except CursorAgentError as exc: raise RuntimeError(f"LLM 调用失败:{exc.message}") from exc diff --git a/tests/test_cursor_client.py b/tests/test_cursor_client.py new file mode 100644 index 0000000..0c9863d --- /dev/null +++ b/tests/test_cursor_client.py @@ -0,0 +1,15 @@ +from __future__ import annotations + + +def test_cursor_timeout_seconds_default(monkeypatch): + import daily.cursor_client as cc + + monkeypatch.delenv("CURSOR_TIMEOUT", raising=False) + assert cc.cursor_timeout_seconds() == 600.0 + + +def test_cursor_timeout_seconds_from_env(monkeypatch): + import daily.cursor_client as cc + + monkeypatch.setenv("CURSOR_TIMEOUT", "900") + assert cc.cursor_timeout_seconds() == 900.0