* test(python): exercise installed wheels as black boxes Add an installed-wheel mode that refuses source/editable imports, repository working directories, mismatched SDK/runtime versions, unpinned runtime dependencies, and executables outside the installed runtime distribution. The mode resolves the wheel-owned executable itself, so callers cannot accidentally prove an explicit checkout artifact. Add a real-API scenario that drives two tool-using turns through the public synchronous SDK, verifies the file bytes outside the agent, checks completed turn/tool events and persistence, and projects provider failures without retaining credential-bearing error text. The existing deterministic scenario set remains the keyless behavior oracle. Refs #2952. * ci(python): require installed-wheel checks on every release target Move the complete deterministic runtime scenarios behind construction and clean installation of the SDK and matching runtime wheels. Each native leg runs outside the checkout with source-resolution environment variables removed; Linux manylinux smokes assert the same installed provenance. Expand the required pull-request call from Linux x64 to Linux x64, Linux arm64, and macOS arm64. Trusted heads receive only DEEPSEEK_API_KEY_EXTERNAL for a fail-loud live two-turn smoke on each carrier, while fork and Dependabot heads retain the full keyless path without exposing secrets. Pin the reusable secret declaration, matrix call, aggregate dependency, untrusted-head condition, and live/keyless commands in the workflow contract test. Refs #2952. * docs(testing): make installed wheels the Python CI authority Record the clean-wheel provenance boundary, complete keyless scenario set, trusted real-API contract, secret handling, and three-target required topology in a new implemented testing decision. Update the SEA distribution and portable-CI authorities plus the Python contributor reference to describe the same current state. Archive the fully superseded Linux-x64-only decision after consolidating its rationale and alternatives into the new owner. Preserve its bilingual triplet as a sealed historical snapshot and redirect every active current-state reference. Refs #2952.
6.6 KiB
Python contributor workflows
English | 中文
Follow the workflow for the contributor outcome you need: build runtime artifacts, validate the SDK, run against source, or build distributions. Package behavior belongs in the SDK reference and runtime carrier reference.
Build runtime artifacts
Platform executables are build artifacts and are not checked into git. Run the build from the repository root:
pnpm install
pnpm exec tsx scripts/build-exe-for-python-sdk.ts
Use --skip-build when the required lib/ artifacts already exist, or --targets=node24-linux-x64,node24-linux-arm64,node24-macos-arm64 to select platforms. Products land in dist-exe/ and the script syncs the selected carriers into python/sdk-runtime/. macOS builds also sync the matching spawn helper required by node-pty.
Validate the SDK
Keep the virtual environment outside python/, install the test group, and run the Python suite:
export UV_PROJECT_ENVIRONMENT="$PWD/tmp/py-sdk-venv"
uv sync --project python/sdk --group test
uv run --project python/sdk pytest
python/sdk/tests/test_bundled_runtime.py exercises available bundled carriers and skips a carrier when its artifact has not been built. For repository-wide test policy, see Testing.
That suite drives fake runtime peers. scripts/smoke-python-runtime.py drives the packaged runtime instead. The required python-runtime CI job builds every published native target, installs the matching SDK and runtime wheels into a new Python 3.10 virtual environment, runs outside the checkout with PYTHONPATH and DSH_RUNTIME_MODE unset, proves that both modules and the executable came from those distributions, and then runs every keyless scenario. A focused local source-SDK run can select one built executable and scenario:
uv run --project python/sdk python scripts/smoke-python-runtime.py \
--scenario sdk-minimal --exe dist-exe/dsh-jsonrpc-agent-pkg-macos-arm64
Three scenarios compare committed expected output under scripts/snapshots/python-sdk-single-exe/. minimal/model-visible.json pins the checked-in minimal composition's assembled system prompts, advertised tool schemas, and model-visible messages, so a plugin that contributes an unintended system section or user message fails the job; it drops the dynamic runtime-context snapshot, which the same composition emits on macOS and not on Linux (#2488). advanced/ pins one complex process's SDK result and parent/child session logs. restart/ launches two complete SDK runtime processes against one persistence root and snapshots their isolated model histories, high-level results, and separate durable logs. Rerun the owning scenario with --update-snapshots and review that diff before committing it.
Trusted pull requests also run --scenario sdk-live --installed-wheel on every native target. That scenario performs two tool-using turns against https://api.deepseek.com, verifies the created file externally, and fails when the repository secret is absent instead of self-skipping. Fork and Dependabot pull requests run the complete keyless installed-wheel path but receive no key.
An interactive smoke test needs DEEPSEEK_API_KEY in the environment or repository-root .env:
from deepseek_harness import DeepSeekHarness
with DeepSeekHarness() as harness:
print(harness.run("say hi").final_response)
Run against Node source
Repository contributors can select either development carrier:
- Set
DSH_RUNTIME_MODE=nodeto use the built Node carrier on system Node>=22.19. The build script refreshes this carrier, but distributions never include or auto-select it. - Set
launch_args_override=("./node_modules/.bin/tsx", "packages/sdk/python-runtime/src/packaged-bin.ts")with the repository root ascwdto run the private carrier's unbuilt TypeScript source. Supplycordis=...when the default configuration is not suitable.
See python/sdk/tests/manual_sdk_agent_smoke.py for a complete source-mode invocation.
Build distributions
The root package.json version is authoritative for both Python distributions. The staging script injects that version into both wheels and pins the SDK to the same deepseek-harness-runtime-bin version.
Build the pure SDK wheel once and one runtime wheel on each native platform:
version="$(python - <<'PY'
import runpy
release = runpy.run_path("scripts/build-python-release.py")
print(release["pep440_version"](release["repository_version"]()))
PY
)"
python scripts/build-python-release.py --package sdk --output-dir dist-python
python scripts/build-python-release.py --package runtime --platform macos-arm64 --runtime-exe dist-exe/dsh-jsonrpc-agent-pkg-macos-arm64 --output-dir dist-python
pip install \
"dist-python/deepseek_harness_sdk-$version-py3-none-any.whl" \
"dist-python/deepseek_harness_runtime_bin-$version-py3-none-macosx_14_0_arm64.whl"
The runtime distribution is wheel-only. The release pipeline publishes three platform wheels with the pure SDK wheel: Linux x64, Linux arm64, and macOS 14 or newer on arm64. A python-v<repository-version> tag is accepted only when it matches the repository version; prerelease repository versions such as 0.0.1-rc.1 use their normalized PEP 440 spelling, such as 0.0.1rc1, inside wheel filenames and metadata.
Validate a release candidate
Manually run the GitHub Release (Python) workflow with publish=false to build all four wheels, install the Linux release set on Python 3.10 and 3.14, check exact filenames and metadata, enforce PyPI's default per-file size limit, and retain one aggregate artifact with SHA-256 hashes. The run has no registry credentials; a dry run cannot enter either publication job.
Public publication runs from the private automation repository; package metadata points to the separate read-only public source mirror, which does not run release Actions. The private repository defines the repository variable PYPI_PUBLISHER_REPOSITORY as its own owner/name and keeps PUBLIC_PYPI_RELEASE_ENABLED=false except during an intentional release.
Separate runtime and SDK jobs let an SDK upload failure resume without resending immutable runtime files. They accept publish=true only when the workflow runs from the configured publisher repository at the matching python-v* tag and the protected pypi-runtime and pypi environments approve the runtime and SDK jobs, respectively. PyPI Trusted Publishing still supplies short-lived OIDC credentials, but public attestations are disabled because they would disclose the private publisher identity.