From 79a8f667f71ef5e09fda54fed5079fab93177ebc Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Mon, 24 Aug 2026 15:04:24 +0800 Subject: [PATCH] refactor(python): launch the minimal example through sdk-minimal Make minimal.py select the shipped sdk-minimal profile directly and pass its selected model into the profile-owned adapter catalog. The Python SDK still starts only the bundled dsh CLI with an explicit Harness home; it no longer supplies an invocation overlay for this mode. Drive both the source keyless process test and installed-wheel smoke through the same named profile. The keyless test pins the generated profile manifest and exact two-tool model request, while the packaged smoke keeps the persistent-shell, editor, session-log, and model-visible snapshot evidence. Delete minimal.patch.yml and the unused complete-config/replay fixtures. Their composition now has one owner in @deepseek-ai/dsh-sdk-minimal, so the example and tests cannot drift into separate launch trees. --- examples/python-sdk-agent/minimal.cordis.yml | 90 ------------------- examples/python-sdk-agent/minimal.patch.yml | 47 ---------- examples/python-sdk-agent/minimal.py | 7 +- .../minimal.snapshot.cordis.yml | 20 ----- .../tests/keyless-smoke.e2e.ts | 17 ++-- scripts/smoke-python-runtime.py | 78 ++-------------- 6 files changed, 18 insertions(+), 241 deletions(-) delete mode 100644 examples/python-sdk-agent/minimal.cordis.yml delete mode 100644 examples/python-sdk-agent/minimal.patch.yml delete mode 100644 examples/python-sdk-agent/minimal.snapshot.cordis.yml diff --git a/examples/python-sdk-agent/minimal.cordis.yml b/examples/python-sdk-agent/minimal.cordis.yml deleted file mode 100644 index f849b798a0..0000000000 --- a/examples/python-sdk-agent/minimal.cordis.yml +++ /dev/null @@ -1,90 +0,0 @@ -# Complete minimal-agent composition fixture for lower-level snapshot tests. -# Python users apply `minimal.patch.yml` to `dsh --profile sdk`. This fixture -# retains the same two-tool behavior without defining a supported launch path. - -- id: sdk-jsonrpc-server - name: '@deepseek-ai/dsh-sdk-jsonrpc-server' - config: - maxTokensAsSuccess: false - -- id: deepseek-llm-api-extensions - name: '@deepseek-ai/dsh-deepseek-llm-api-extensions' - -- id: session-log-deepseek - name: '@deepseek-ai/dsh-session-log-deepseek' - -- id: plugin-package-inventory-deepseek - name: '@deepseek-ai/dsh-plugin-package-inventory-deepseek' - -- id: llm-deepseek - name: '@deepseek-ai/dsh-llm-deepseek' - config: - apiKeyEnv: DEEPSEEK_API_KEY - streamIdleTimeoutMs: 172800000 - models: - - id: !!js process.env.DSH_MODEL ?? 'deepseek-v4-flash' - contextWindow: !!js Number(process.env.DSH_CONTEXT_WINDOW ?? 1000000) - -- id: sandbox - name: '@deepseek-ai/dsh-sandbox-local' - -- id: sandbox-policy - name: '@deepseek-ai/dsh-sandbox-policy' - config: - mode: danger-full-access - workspaceRoot: !!js process.env.DSH_CWD ?? process.cwd() - -- id: subprocess - name: '@deepseek-ai/dsh-subprocess-local' - -- id: pty - name: '@deepseek-ai/dsh-terminal' - -- id: terminal-bash - name: '@deepseek-ai/dsh-terminal-bash' - config: - timeoutMs: 300000 - -# The editor uses the bare local filesystem; persistent Bash still consumes the -# shared danger-full-access sandbox policy above. -- id: fs-local - name: '@deepseek-ai/dsh-fs-local' - config: - cwd: !!js process.env.DSH_CWD ?? process.cwd() - -- id: agent-spine - name: '@deepseek-ai/dsh-agent-spine-demo' - config: - includeHarnessIdentity: false - includeRuntimeContext: false - persona: !!js process.env.DSH_SYSTEM_PROMPT ?? 'You are a helpful software engineer assistant.' - workspaceContext: false - skills: - enabled: false - toolBash: false - toolJobs: false - -- id: persistent-bash - name: '@deepseek-ai/dsh-tool-bash-persistent' - config: - timeoutMs: 300000 - description: |- - Run commands in a bash shell - * When invoking this tool, the contents of the "command" parameter does NOT need to be XML-escaped. - * You don't have access to the internet via this tool. - * You do have access to a mirror of common linux and python packages via apt and pip. - * State is persistent across command calls and discussions with the user. - * To inspect a particular line range of a file, e.g. lines 10-25, try 'sed -n 10,25p /path/to/the/file'. - * Please avoid commands that may produce a very large amount of output. - * Please run long lived commands in the background, e.g. 'sleep 10 &' or start a server in the background. - -- id: str-replace-editor - name: '@deepseek-ai/dsh-tool-str-replace-editor' - config: - maxOutputChars: 16000 - -- id: sessions - name: '@deepseek-ai/dsh-session-persistence-jsonl' - config: - root: !!js process.env.DSH_SESSION_ROOT ?? './.sessions' - compression: none diff --git a/examples/python-sdk-agent/minimal.patch.yml b/examples/python-sdk-agent/minimal.patch.yml deleted file mode 100644 index 2c3ac0f5bc..0000000000 --- a/examples/python-sdk-agent/minimal.patch.yml +++ /dev/null @@ -1,47 +0,0 @@ -# Minimal Python SDK overlay for `dsh --profile sdk`. The SDK server applies -# the model-facing allowlist to every root session, so later base-bundle tools -# cannot appear implicitly. The profile still owns JSON-RPC and persistence. - -- id: system-prompt - config: - includeHarnessIdentity: false - includeRuntimeContext: false - persona: !!js process.env.DSH_SYSTEM_PROMPT ?? 'You are a helpful software engineer assistant.' - personaComplete: true - -- id: sdk-jsonrpc-server - config: - maxTokensAsSuccess: !!js "process.env.DSH_MAX_TOKENS_AS_SUCCESS === undefined ? true : JSON.parse(process.env.DSH_MAX_TOKENS_AS_SUCCESS)" - toolFilter: - allow: - - bash - - str_replace_editor - -# Remove non-tool model inputs and turn rewriting, plus the one-shot Bash row -# that would conflict with the persistent Bash tool inserted below. -- id: agent-instructions - disabled: true -- id: tool-bash - disabled: true -- id: compaction-basic - disabled: true -- id: command-compact - disabled: true -- id: tool-result-pruner - disabled: true - -- id: tool-str-replace-editor - config: - maxOutputChars: 16000 - -- insert: - - id: pty - name: '@deepseek-ai/dsh-terminal' - - id: terminal-bash - name: '@deepseek-ai/dsh-terminal-bash' - config: - timeoutMs: 300000 - - id: persistent-bash - name: '@deepseek-ai/dsh-tool-bash-persistent' - config: - timeoutMs: 300000 diff --git a/examples/python-sdk-agent/minimal.py b/examples/python-sdk-agent/minimal.py index 255a0dd3c0..86c1dc8082 100644 --- a/examples/python-sdk-agent/minimal.py +++ b/examples/python-sdk-agent/minimal.py @@ -10,9 +10,6 @@ from pathlib import Path from deepseek_harness import DeepSeekHarness -PATCH = Path(__file__).with_name("minimal.patch.yml") - - def main() -> None: """Parse one task and print the agent's final response.""" parser = argparse.ArgumentParser() @@ -24,7 +21,7 @@ def main() -> None: type=Path, default=Path(configured_home) if configured_home.strip() else None, ) - parser.add_argument("--profile", default="sdk") + parser.add_argument("--profile", default="sdk-minimal") parser.add_argument("--session-id") parser.add_argument("--provider", default="deepseek-official") parser.add_argument("--model", default=os.environ.get("DSH_MODEL", "deepseek-v4-flash")) @@ -42,7 +39,7 @@ def main() -> None: cwd=str(workspace), dsh_home=str(dsh_home), profile=args.profile, - patches=(str(PATCH.resolve()),), + env={"DSH_MODEL": args.model}, ) as harness: result = harness.run(args.prompt, session_id=args.session_id) print(result.final_response) diff --git a/examples/python-sdk-agent/minimal.snapshot.cordis.yml b/examples/python-sdk-agent/minimal.snapshot.cordis.yml deleted file mode 100644 index 0f26fa6716..0000000000 --- a/examples/python-sdk-agent/minimal.snapshot.cordis.yml +++ /dev/null @@ -1,20 +0,0 @@ -# Keyless replay keeps the complete minimal composition intact and replaces -# only its live DeepSeek adapter with the fixture-backed provider. The replay -# catalog claims the same route initialized by the SDK. -- id: base - name: '@deepseek-ai/cordis-plugin-include' - config: - path: ./minimal.cordis.yml - patches: - - id: llm-deepseek - name: '@deepseek-ai/dsh-llm-deepseek' - disabled: true - - insert: - - id: llm-replay - name: '@deepseek-ai/dsh-llm-replay' - config: - providers: - - id: deepseek-official - name: DeepSeek - models: - - id: deepseek-v4-flash diff --git a/examples/python-sdk-agent/tests/keyless-smoke.e2e.ts b/examples/python-sdk-agent/tests/keyless-smoke.e2e.ts index 4b2519c7f8..e0bd4ed6c3 100644 --- a/examples/python-sdk-agent/tests/keyless-smoke.e2e.ts +++ b/examples/python-sdk-agent/tests/keyless-smoke.e2e.ts @@ -10,7 +10,6 @@ import { describe, expect, it } from 'vitest' const binScript = fileURLToPath(new URL('../../../apps/cli/src/bin.ts', import.meta.url)) const patchPath = fileURLToPath(new URL('./keyless.patch.yml', import.meta.url)) -const minimalPatchPath = fileURLToPath(new URL('../minimal.patch.yml', import.meta.url)) const repoRoot = fileURLToPath(new URL('../../..', import.meta.url)) const decompress = promisify(zstdDecompress) @@ -181,7 +180,7 @@ describe('Python SDK dsh profile keyless smoke', () => { } }, 40_000) - it('keeps the minimal overlay on an explicit model-facing allowlist', async () => { + it('boots the standalone minimal profile with its exact model-facing roster', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-python-sdk-minimal-')) const modelRequests: Record[] = [] const modelServer = createServer((request, response) => { @@ -205,16 +204,13 @@ describe('Python SDK dsh profile keyless smoke', () => { 'tsx/esm', binScript, '--profile', - 'sdk', - '--patch', - minimalPatchPath, + 'sdk-minimal', ], { cwd: repoRoot, env: { DSH_HOME: join(root, '.dsh'), - DSH_PERMISSION_MODE: 'danger-full-access', + DSH_MODEL: 'deepseek-v4-pro', DSH_SYSTEM_PROMPT: 'Minimal allowlist prompt.', - DSH_TELEMETRY_DISABLED: '1', DEEPSEEK_API_KEY: 'keyless-smoke-no-call', DEEPSEEK_BASE_URL: `http://127.0.0.1:${address.port}`, }, @@ -259,6 +255,13 @@ describe('Python SDK dsh profile keyless smoke', () => { } expect(request.messages?.[0]).toMatchObject({ role: 'system', content: 'Minimal allowlist prompt.' }) expect(request.tools?.map(tool => tool.function?.name).sort()).toEqual(['bash', 'str_replace_editor']) + const profile = JSON.parse( + await readFile(join(root, '.dsh', 'profiles', 'sdk-minimal', 'package.json'), 'utf8'), + ) as { dsh?: { profile?: { bundles?: string[]; patchReload?: string } } } + expect(profile.dsh?.profile).toEqual({ + bundles: ['@deepseek-ai/dsh-sdk-minimal'], + patchReload: 'startup', + }) child.stdin.write(`${JSON.stringify({ jsonrpc: '2.0', id: 3, method: 'shutdown' })}\n`) await waitForLine(lines, value => value.id === 3, () => stderr) diff --git a/scripts/smoke-python-runtime.py b/scripts/smoke-python-runtime.py index 3be8a9bcfb..ba5279000d 100644 --- a/scripts/smoke-python-runtime.py +++ b/scripts/smoke-python-runtime.py @@ -46,14 +46,6 @@ MINIMAL_BASH_COMMAND = ( "printf 'COUNT=%s CWD=%s\\n' \"$counter\" \"$PWD\"; " "if [ \"$counter\" -eq 1 ]; then cd /tmp; fi" ) -MINIMAL_BASH_DESCRIPTION = """Run commands in a bash shell -* When invoking this tool, the contents of the "command" parameter does NOT need to be XML-escaped. -* You don't have access to the internet via this tool. -* You do have access to a mirror of common linux and python packages via apt and pip. -* State is persistent across command calls and discussions with the user. -* To inspect a particular line range of a file, e.g. lines 10-25, try 'sed -n 10,25p /path/to/the/file'. -* Please avoid commands that may produce a very large amount of output. -* Please run long lived commands in the background, e.g. 'sleep 10 &' or start a server in the background.""" LEGACY_CUSTOM_DISABLED_ROWS = ( "agent-instructions", "goal", @@ -121,10 +113,6 @@ RESTART_SNAPSHOT_DIRECTORY = ( Path(__file__).resolve().parent / "snapshots" / "python-sdk-single-exe" / "restart" ) RESTART_SNAPSHOT_FILENAMES = ("result.json", "requests.json", "session.1.jsonl", "session.2.jsonl") -# The agent loop's dynamic runtime-context snapshot is the one model-visible message this -# expected output cannot carry: the same composition emits it on macOS and not on Linux -# (deepseek-harness#2488), and the file must replay on both. Everything else is compared. -RUNTIME_CONTEXT_PREFIX = "Current runtime context" MCP_SERVER_SCRIPT = """\ import json import os @@ -972,7 +960,7 @@ def smoke_sdk_custom(base_url: str, executable: Path) -> None: def smoke_sdk_minimal(base_url: str, executable: Path, update_snapshots: bool) -> None: - """Exercise the checked-in minimal composition through the packaged executable.""" + """Exercise the shipped standalone minimal profile through the packaged executable.""" from deepseek_harness import DeepSeekHarness # One mock model serves every scenario of a run, so the snapshot takes this turn's slice. @@ -983,59 +971,15 @@ def smoke_sdk_minimal(base_url: str, executable: Path, update_snapshots: bool) - prompt = f"{MINIMAL_PROMPT}\n{MINIMAL_EDITOR_PATH_PREFIX}{editor_path}" dsh_home = root / "home" sessions = dsh_home / "sessions" - disabled = [ - "agent-instructions", - "tool-bash", - "compaction-basic", - "command-compact", - "tool-result-pruner", - ] - patch = write_profile_patch(root, "minimal.patch.yml", sessions, [ - { - "id": "system-prompt", - "config": { - "includeHarnessIdentity": False, - "includeRuntimeContext": False, - "persona": "You are a helpful software engineer assistant.", - "personaComplete": True, - }, - }, - { - "id": "sdk-jsonrpc-server", - "config": { - "maxTokensAsSuccess": True, - "toolFilter": {"allow": ["bash", "str_replace_editor"]}, - }, - }, - *({"id": row_id, "disabled": True} for row_id in disabled), - {"id": "tool-str-replace-editor", "config": {"maxOutputChars": 16000}}, - {"insert": [ - {"id": "pty", "name": "@deepseek-ai/dsh-terminal"}, - { - "id": "terminal-bash", - "name": "@deepseek-ai/dsh-terminal-bash", - "config": {"timeoutMs": 300000}, - }, - { - "id": "persistent-bash", - "name": "@deepseek-ai/dsh-tool-bash-persistent", - "config": { - "timeoutMs": 300000, - "description": MINIMAL_BASH_DESCRIPTION, - }, - }, - ]}, - ]) with DeepSeekHarness( provider="deepseek-official", model="smoke-model", cwd=str(root), dsh_bin=str(executable), dsh_home=str(dsh_home), - patches=(str(patch),), + profile="sdk-minimal", env={ - "DSH_PERMISSION_MODE": "danger-full-access", - "DSH_TELEMETRY_DISABLED": "1", + "DSH_MODEL": "smoke-model", }, api_key="sk-keyless-smoke", base_url=base_url, @@ -1550,9 +1494,9 @@ def build_minimal_snapshot_files( Every assembled system prompt, advertised tool schema, and system or user message is kept verbatim: they carry what the deployment actually shows the model, so a plugin that contributes an unintended system section or user message cannot pass unnoticed. - Assistant and tool payloads keep only their call identity, and the dynamic - runtime-context snapshot is dropped, because their text differs across the platforms - this expected output must replay on. + Assistant and tool payloads keep only their call identity because their text differs + across the platforms this expected output must replay on. The shipped profile omits + dynamic runtime context, so every message it emits is compared. """ snapshot = [] for body in requests: @@ -1564,21 +1508,11 @@ def build_minimal_snapshot_files( "messages": [ minimal_snapshot_message(message, cwd) for message in messages - if not is_runtime_context_message(message) ], }) return {"model-visible.json": json.dumps(snapshot, indent=2, ensure_ascii=False) + "\n"} -def is_runtime_context_message(message: object) -> bool: - """Identify the agent loop's dynamic runtime-context snapshot, current or cleared.""" - return ( - isinstance(message, dict) - and message.get("role") == "user" - and message_text(message.get("content")).startswith(RUNTIME_CONTEXT_PREFIX) - ) - - def minimal_snapshot_message(message: object, cwd: Path) -> dict[str, object]: """Reduce one model-visible message to its stable, behavior-carrying parts.""" if not isinstance(message, dict):