Files
deepseek-harness/python/sdk/README.md
T

9.0 KiB

DeepSeek Harness Python SDK

English | 中文

Python subprocess SDK for driving DeepSeek Harness over newline-delimited JSON-RPC on stdio. Install deepseek-harness-sdk; it installs the exact same-version deepseek-harness-runtime-bin wheel for the current platform.

python -m pip install deepseek-harness-sdk

Optional Office operations

Download the Office runtime explicitly, then call the local API without an agent, Session, model request, API key, or Harness home:

from deepseek_harness_runtime import download_office
from deepseek_harness.office import convert, recalculate, render_images

download_office()
pdf = convert("report.docx", "report.pdf")
workbook = recalculate("forecast.xlsx", "calculated.xlsx")
preview = render_images("report.docx", "report-preview", pages=[1])

The output file or image directory must not exist. Successful outputs belong to the caller; Kit removes failed outputs. OfficeOptions supplies operation limits and font preferences. Results expose typed paths, missing fonts, and image metadata. OfficeError retains CLI failure codes; malformed results raise SdkProtocolError. Recalculation does not verify business logic or Excel fidelity. Ctrl+C waits for CLI cleanup; a second interrupt forces termination and may leave partial output. Windows requires an absolute SystemRoot and a shared console for Ctrl+C delivery.

The authoring environment is independently optional through deepseek_harness_runtime.download_primary_runtime(). See the runtime reference for cache selection, resource activation, and download behavior. SDK startup and Office operations never acquire missing resources automatically.

Start a runtime

The Python SDK has no separate application entrypoint. It launches the bundled dsh CLI with --profile sdk; the selected profile owns the JSON-RPC server, agent composition, credentials, persistence, tools, and shutdown behavior.

Every launch requires an explicit Harness home. Pass dsh_home or provide a non-empty DSH_HOME in the child environment. The SDK deliberately never discovers ~/.dsh.

from deepseek_harness import DeepSeekHarness

with DeepSeekHarness(
    dsh_home="/absolute/path/to/isolated-dsh-home",
    cwd="/absolute/path/to/workspace",
    provider="deepseek-official",
    model="deepseek-v4-flash",
    reasoning_effort="max",
    max_tokens=49_152,
) as harness:
    result = harness.run("Say hi.", session_id="example-001")

print(result.final_response)

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, optional reasoning_effort, 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

Persistent customization belongs to a dsh profile. Initialize the shipped SDK profile and install an external bundle with the runtime wheel's dsh command:

export DSH_HOME=/absolute/path/to/isolated-dsh-home
dsh --profile sdk --dump-default-config >/dev/null
dsh plugin --profile sdk add file:/absolute/path/to/my-plugin-bundle

The file: form installs the local bundle into the profile package tree, where its peer imports reach the bundled installation fallback. The profile manifest records installed dependencies and ordered bundle layers; its $DSH_HOME/profiles/sdk/cordis.patch.yml is the persistent user patch. dsh plugin needs pnpm only when managing external packages. Running the SDK does not require system Node.js.

For an invocation-specific change, pass one or more patch files. They become absolute and are forwarded in order after the profile and home patch layers:

with DeepSeekHarness(
    dsh_home="/absolute/path/to/isolated-dsh-home",
    profile="sdk",
    patches=("/absolute/path/to/first.patch.yml", "/absolute/path/to/last.patch.yml"),
) as harness:
    result = harness.run("Make the requested code change.")

profile may select another existing profile, but that composition must retain @deepseek-ai/dsh-sdk-app or another @deepseek-ai/dsh-sdk-jsonrpc-server row. Misconfiguration fails during CLI boot or SDK initialization; there is no complete-config fallback. dsh_bin may select another dsh executable while preserving the same profile grammar. Arbitrary argv replacement remains an internal fake-runtime test adapter, not public API.

provider selects a provider route registered by the chosen Cordis composition; model is the model id resolved by that adapter. reasoning_effort is an optional non-empty adapter-owned identifier for that exact route; omission preserves the model's own default. max_tokens is an optional positive per-request output-token cap for the root agent and its in-process descendants; omission leaves the provider default in control. Initialization rejects a missing adapter, unavailable model, or unsupported effort before a prompt runs. Compaction summaries keep the separate limit configured by their compaction plugin. The bundled default composition registers deepseek-official. A custom composition can mount llm-pi-ai, configure provider-specific credentials/endpoints there, and select any provider/model present in pi-ai's installed catalog.

The shipped sdk-minimal profile is a standalone explicit tree rather than an overlay on dsh-base. Select it with profile="sdk-minimal"; the ordinary model argument is the sole runtime model selection, including for model ids outside the adapter's advisory catalog. It provides only a platform-selected persistent shell, local execution, and JSONL sessions; filesystem tools, settings, managed credentials, telemetry, Web tools, and the full default tool roster remain available through the separate full sdk and web profiles.

harness.resume_session(saved_session_id) restores persisted history and returns a Session ready for run(). Save the original session id and reuse its dsh_home and workspace. Restoration preserves the stored directory, repairs an interrupted final turn, and sends no new input. Missing, unreadable, incompatible, or occupied sessions raise without creating a replacement. start_session(id) only opens a lazy handle; it does not request persisted restoration. The low-level client also exposes resume_session(id), returning the restored identity. This API does not restore shell, browser, or other external processes.

with DeepSeekHarness(dsh_home="/absolute/path/to/saved-home") as harness:
    session = harness.resume_session(saved_session_id)
    result = session.run("Continue the task.")

Results and notifications

Session.run() owns an activity interval from its prompt's durable inbox receipt through the next whole-agent idle and returns RunResult(session_id, final_response, finish_reason, events, notifications). final_response is the last committed root-session assistant text in the interval. finish_reason is the kind of the last root-session turn/end, such as completed, max-tokens, or error, and is None when no turn ended. A turn/end without a string data.reason.kind violates the protocol and raises SdkProtocolError.

HarnessClient retains discovered subagent ancestry for the runtime process lifetime. During Session.run(), RunResult.notifications and on_notification receive the root session and known descendants in wire order. RunResult.events contains root-session events only, so descendant output cannot replace the root response. The low-level session_prompt() returns the queued message id immediately; callers that bypass Session.run() own the later activity boundary.

The selected home stores profiles, plugins, and every profile-owned durable resource. The full sdk profile uses its credentials, settings, and session stores; sdk-minimal uses only its JSONL session store. Use a fresh home when those resources must be isolated, and a fresh session id for independent work. Reusing both a harness and session id continues the durable conversation and session-owned resources.

See the Python tutorial, runnable example, and runtime wheel reference.

Session.get_working_directory() reads the effective working directory; set_working_directory(path) changes it and returns the validated absolute path. Both create an unknown Session without running a model turn. Relative paths use that Session's current directory. Origin metadata, permissions, and existing processes remain unchanged. The low-level client provides the same methods with a leading session_id argument.