From dff3e18afdafd06cb4b203480b8a68d655dab920 Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Mon, 24 Aug 2026 19:07:21 +0800 Subject: [PATCH] fix(python): budget cold profile initialization The Windows x64 installed-wheel job timed out while waiting for initialize even though the same head passed on rerun. Exact packaged-runtime VM evidence showed a 6.47-second first cold handshake and 2.69-2.94-second warm fresh-home handshakes, leaving too little variance below the public 10-second default.\n\nRaise the independent initialize default to 30 seconds in both Python SDK configuration layers. Ordinary turn and shutdown timeouts remain unchanged, callers retain an explicit override, and tests plus paired documentation pin the public behavior. --- .../2026-08-23-python-sdk-windows-x64-runtime.i18n.yaml | 4 ++-- .../architecture/2026-08-23-python-sdk-windows-x64-runtime.md | 2 ++ .../2026-08-23-python-sdk-windows-x64-runtime.zh.md | 2 ++ python/sdk/README.i18n.yaml | 4 ++-- python/sdk/README.md | 2 +- python/sdk/README.zh.md | 2 +- python/sdk/src/deepseek_harness/api.py | 2 +- python/sdk/src/deepseek_harness/client.py | 2 +- python/sdk/tests/test_client.py | 2 ++ 9 files changed, 14 insertions(+), 8 deletions(-) diff --git a/.agents/notes/implemented/architecture/2026-08-23-python-sdk-windows-x64-runtime.i18n.yaml b/.agents/notes/implemented/architecture/2026-08-23-python-sdk-windows-x64-runtime.i18n.yaml index 0da9832af7..3db66f3a7c 100644 --- a/.agents/notes/implemented/architecture/2026-08-23-python-sdk-windows-x64-runtime.i18n.yaml +++ b/.agents/notes/implemented/architecture/2026-08-23-python-sdk-windows-x64-runtime.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-08-23-python-sdk-windows-x64-runtime.md -2026-08-23-python-sdk-windows-x64-runtime.md: b4ba54d9bd8e7a2eaa9277116a7868a1942d0531 -2026-08-23-python-sdk-windows-x64-runtime.zh.md: 6f05817b647a152cec626f09866f415d164064ec +2026-08-23-python-sdk-windows-x64-runtime.md: 59a46d99f9e7ed411aeffbb541bbe3bb0c752078 +2026-08-23-python-sdk-windows-x64-runtime.zh.md: 3ab972aabb8135c8bc6285d129ba7bc9335eb11f diff --git a/.agents/notes/implemented/architecture/2026-08-23-python-sdk-windows-x64-runtime.md b/.agents/notes/implemented/architecture/2026-08-23-python-sdk-windows-x64-runtime.md index b4ba54d9bd..59a46d99f9 100644 --- a/.agents/notes/implemented/architecture/2026-08-23-python-sdk-windows-x64-runtime.md +++ b/.agents/notes/implemented/architecture/2026-08-23-python-sdk-windows-x64-runtime.md @@ -26,6 +26,8 @@ The required GitHub matrix builds `node24-win-x64` on `windows-2025` beside the The Windows lane creates a clean Windows virtual environment, installs the exact SDK and `win_amd64` runtime wheels, changes to a directory outside the checkout, unsets `PYTHONPATH` and `DSH_RUNTIME_MODE`, and runs the same `--scenario all --installed-wheel` blackbox as every other target. Trusted pull requests also run the same two-turn `sdk-live` provider scenario. Fork and Dependabot heads receive no key. +The public Python client gives the initial profile handshake an independent 30-second default through `initialize_timeout_seconds`. The bound accommodates cold Windows x64 executable startup and profile materialization while still failing a stuck runtime; callers may configure it separately from ordinary request timeouts. + After a successful shutdown response, the Python client closes stdin and waits within the configured shutdown timeout for the `dsh` context to exit and flush durable session state before terminating it. A failed shutdown retains immediate bounded termination. `shutdown_timeout_seconds` bounds each of the shutdown request, EOF grace, and termination-confirmation phases, so a pathological close can approach three times that value before the final kill. This distinction preserves the final accepted turn on Windows, where `terminate()` force-kills the process rather than delivering a catchable signal. The minimal blackbox uses persistent `pwsh` plus `str_replace_editor` on Windows and owns `minimal/win-x64/model-visible.json`; Linux and macOS retain persistent Bash and the shared `minimal/model-visible.json`. The advanced process/subagent snapshot and restart/durable-log snapshot remain shared across all targets. The shipped [`sdk-minimal` bundle](../../../../packages/bundle/sdk-minimal/README.md) selects the same platform shell pair for the runnable Python tutorial. diff --git a/.agents/notes/implemented/architecture/2026-08-23-python-sdk-windows-x64-runtime.zh.md b/.agents/notes/implemented/architecture/2026-08-23-python-sdk-windows-x64-runtime.zh.md index 6f05817b64..3ab972aabb 100644 --- a/.agents/notes/implemented/architecture/2026-08-23-python-sdk-windows-x64-runtime.zh.md +++ b/.agents/notes/implemented/architecture/2026-08-23-python-sdk-windows-x64-runtime.zh.md @@ -26,6 +26,8 @@ Python 进程仍按 [Python profile 运行时决策](2026-08-23-python-sdk-dsh-p Windows lane 会创建干净的 Windows 虚拟环境,安装版本精确匹配的 SDK 与 `win_amd64` 运行时 wheel,切换到 checkout 外的目录,清除 `PYTHONPATH` 与 `DSH_RUNTIME_MODE`,再运行与其他目标相同的 `--scenario all --installed-wheel` 黑盒测试。可信拉取请求还会运行相同的双轮 `sdk-live` 真实提供方场景。Fork 与 Dependabot head 不会获得密钥。 +公开 Python 客户端通过 `initialize_timeout_seconds` 为首次 profile 握手提供独立的 30 秒默认上限。该上限可容纳 Windows x64 可执行文件冷启动与 profile 物化,同时仍会使卡死的运行时失败;调用方可将其与普通请求超时分开配置。 + 成功收到 shutdown 响应后,Python 客户端会关闭 stdin,并在已配置的 shutdown 超时内等待 `dsh` 上下文退出及刷写持久 session 状态,然后才回退到终止进程。Shutdown 失败时仍立即执行有界终止。`shutdown_timeout_seconds` 会分别限制 shutdown 请求、EOF 宽限与终止确认阶段,因此异常关闭在最终 kill 前可能接近该值的三倍。该区别会保留 Windows 上最后一个已接受轮次;该平台的 `terminate()` 会强制结束进程,而不是发送可捕获信号。 极简黑盒测试在 Windows 上使用持久 `pwsh` 与 `str_replace_editor`,并由 `minimal/win-x64/model-visible.json` 固定预期;Linux 与 macOS 保留持久 Bash 和共享的 `minimal/model-visible.json`。高级进程/subagent 快照与重启/持久日志快照继续由所有目标共享。随附的 [`sdk-minimal` 组合包](../../../../packages/bundle/sdk-minimal/README.zh.md)为可运行 Python 教程选择同一组平台 shell。 diff --git a/python/sdk/README.i18n.yaml b/python/sdk/README.i18n.yaml index 5295ed3c11..c8ee3ec85f 100644 --- a/python/sdk/README.i18n.yaml +++ b/python/sdk/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write python/sdk/README.md -README.md: cf9bb3e3ccac4908e9212d8f7247545b5a6b5d8e -README.zh.md: 9acb8f26129144a77a834f94854cdd3f1a200086 +README.md: 1b03fe5553f25da3bc62f8a7eec2a274b0afb66a +README.zh.md: c0bfa8bdd9e2ecbaad0a019a274b94516e219ac6 diff --git a/python/sdk/README.md b/python/sdk/README.md index cf9bb3e3cc..1b03fe5553 100644 --- a/python/sdk/README.md +++ b/python/sdk/README.md @@ -26,7 +26,7 @@ with DeepSeekHarness( print(result.final_response) ``` -`DeepSeekHarness` starts lazily and reuses its runtime until `close()` or context-manager exit. The initial profile handshake has an independent 10-second default bound through `initialize_timeout_seconds`; ordinary turns remain unbounded unless `request_timeout_seconds` is set. A timeout names the selected profile and includes retained runtime diagnostics. `cwd` is the agent workspace; `runtime_cwd` independently selects the subprocess working directory. Both become absolute before launch. `provider`, `model`, and optional positive `max_tokens` are sent during JSON-RPC initialization. `base_url` and `api_key` explicitly override `DEEPSEEK_BASE_URL` and `DEEPSEEK_API_KEY` in the child environment. +`DeepSeekHarness` starts lazily and reuses its runtime until `close()` or context-manager exit. The initial profile handshake has an independent 30-second default bound through `initialize_timeout_seconds`; ordinary turns remain unbounded unless `request_timeout_seconds` is set. A timeout names the selected profile and includes retained runtime diagnostics. `cwd` is the agent workspace; `runtime_cwd` independently selects the subprocess working directory. Both become absolute before launch. `provider`, `model`, and optional positive `max_tokens` are sent during JSON-RPC initialization. `base_url` and `api_key` explicitly override `DEEPSEEK_BASE_URL` and `DEEPSEEK_API_KEY` in the child environment. ## Customize plugins diff --git a/python/sdk/README.zh.md b/python/sdk/README.zh.md index 9acb8f2612..c0bfa8bdd9 100644 --- a/python/sdk/README.zh.md +++ b/python/sdk/README.zh.md @@ -26,7 +26,7 @@ with DeepSeekHarness( print(result.final_response) ``` -`DeepSeekHarness` 延迟启动运行时,并在调用 `close()` 或退出上下文管理器前复用该进程。首次 profile 握手通过 `initialize_timeout_seconds` 使用独立的 10 秒默认上限;普通轮次在未设置 `request_timeout_seconds` 时仍不设上限。超时诊断会指明所选 profile,并包含保留的运行时诊断。`cwd` 是 agent workspace;`runtime_cwd` 独立选择子进程工作目录。两者都会在启动前转成绝对路径。`provider`、`model` 和可选的正整数 `max_tokens` 通过 JSON-RPC 初始化发送。`base_url` 与 `api_key` 会显式覆盖子进程环境中的 `DEEPSEEK_BASE_URL` 与 `DEEPSEEK_API_KEY`。 +`DeepSeekHarness` 延迟启动运行时,并在调用 `close()` 或退出上下文管理器前复用该进程。首次 profile 握手通过 `initialize_timeout_seconds` 使用独立的 30 秒默认上限;普通轮次在未设置 `request_timeout_seconds` 时仍不设上限。超时诊断会指明所选 profile,并包含保留的运行时诊断。`cwd` 是 agent workspace;`runtime_cwd` 独立选择子进程工作目录。两者都会在启动前转成绝对路径。`provider`、`model` 和可选的正整数 `max_tokens` 通过 JSON-RPC 初始化发送。`base_url` 与 `api_key` 会显式覆盖子进程环境中的 `DEEPSEEK_BASE_URL` 与 `DEEPSEEK_API_KEY`。 ## 自定义插件 diff --git a/python/sdk/src/deepseek_harness/api.py b/python/sdk/src/deepseek_harness/api.py index 09286a1ad1..a9a10f993c 100644 --- a/python/sdk/src/deepseek_harness/api.py +++ b/python/sdk/src/deepseek_harness/api.py @@ -29,7 +29,7 @@ class DeepSeekHarnessConfig: patches: tuple[str, ...] = () dsh_home: str | None = None env: dict[str, str] = field(default_factory=dict) - initialize_timeout_seconds: float = 10.0 + initialize_timeout_seconds: float = 30.0 request_timeout_seconds: float | None = None shutdown_timeout_seconds: float | None = 1.0 base_url: str | None = None diff --git a/python/sdk/src/deepseek_harness/client.py b/python/sdk/src/deepseek_harness/client.py index f6752a9906..804076636d 100644 --- a/python/sdk/src/deepseek_harness/client.py +++ b/python/sdk/src/deepseek_harness/client.py @@ -31,7 +31,7 @@ class HarnessConfig: dsh_home: str | None = None cwd: str | None = None env: dict[str, str] | None = None - initialize_timeout_seconds: float = 10.0 + initialize_timeout_seconds: float = 30.0 request_timeout_seconds: float | None = None shutdown_timeout_seconds: float | None = 1.0 _launch_args: tuple[str, ...] | None = None diff --git a/python/sdk/tests/test_client.py b/python/sdk/tests/test_client.py index fba315320b..d5ed7dada8 100644 --- a/python/sdk/tests/test_client.py +++ b/python/sdk/tests/test_client.py @@ -873,6 +873,8 @@ def test_public_signatures_omit_unsupported_wire_parameters() -> None: ) assert "initialize_timeout_seconds" in DeepSeekHarnessConfig.__dataclass_fields__ assert "initialize_timeout_seconds" in HarnessConfig.__dataclass_fields__ + assert DeepSeekHarnessConfig().initialize_timeout_seconds == 30.0 + assert HarnessConfig().initialize_timeout_seconds == 30.0 for removed in ("cordis", "session_root", "runtime_bin", "bridge_bin", "launch_args_override"): assert removed not in DeepSeekHarnessConfig.__dataclass_fields__ assert removed not in HarnessConfig.__dataclass_fields__