feat(python-sdk): launch dsh profiles from explicit homes

Replace complete-config, session_root, runtime-bin, bridge-bin, and public argv override options with dsh_bin, profile, ordered patches, and dsh_home. Resolve executable/home/patch/cwd paths before spawn, select the sdk profile by default, and fail before launch unless dsh_home or non-empty DSH_HOME is explicit; Python never inherits ~/.dsh silently.

Remove Python-owned DSH_CORDIS_CONFIG, DSH_SESSION_ROOT, and DSH_CWD injection and drop session_root from RunResult. Keep arbitrary argv only as an underscore-prefixed fake-runtime adapter, retain provider/model/token and process controls, and append subprocess stderr to initialization JSON-RPC errors so profile boot failures name their actual plugin cause. Unit and carrier tests cover both exe and Node modes.
This commit is contained in:
Tianyi Cui
2026-08-24 17:28:26 +08:00
parent be7b064504
commit 56e038b2e3
5 changed files with 225 additions and 215 deletions
+98 -68
View File
@@ -9,7 +9,8 @@ from pathlib import Path
import pytest
from deepseek_harness import DeepSeekHarness, HarnessClient, HarnessConfig, Notification, SdkProtocolError
from deepseek_harness import DeepSeekHarness, HarnessClient, HarnessConfig, Notification, RunResult, SdkProtocolError
from deepseek_harness.errors import JsonRpcError
def test_high_level_sdk_runs_turn_and_collects_final_response(tmp_path: Path) -> None:
@@ -95,9 +96,7 @@ for line in sys.stdin:
model="deepseek-v4-flash",
max_tokens=4096,
cwd=str(tmp_path),
cordis=str(tmp_path / "cordis.yml"),
session_root=str(tmp_path / "sessions"),
launch_args_override=(sys.executable, str(script)),
_launch_args=(sys.executable, str(script)),
env={
"ENV_DUMP": str(env_dump),
"INIT_DUMP": str(init_dump),
@@ -113,9 +112,9 @@ for line in sys.stdin:
dumped_env = json.loads(env_dump.read_text())
assert dumped_env["DEEPSEEK_API_KEY"] == "env-key"
assert dumped_env["DEEPSEEK_BASE_URL"] == "http://127.0.0.1:4321"
assert dumped_env["DSH_CWD"] == str(tmp_path)
assert dumped_env["DSH_SESSION_ROOT"] == str(tmp_path / "sessions")
assert dumped_env["DSH_CORDIS_CONFIG"] == str(tmp_path / "cordis.yml")
assert dumped_env["DSH_CWD"] is None
assert dumped_env["DSH_SESSION_ROOT"] is None
assert dumped_env["DSH_CORDIS_CONFIG"] is None
assert json.loads(init_dump.read_text()) == {
"cwd": str(tmp_path),
"provider": "deepseek-official",
@@ -150,7 +149,7 @@ for line in sys.stdin:
seen: list[str] = []
with DeepSeekHarness(
launch_args_override=(sys.executable, str(script)),
_launch_args=(sys.executable, str(script)),
cwd=str(tmp_path),
) as harness:
session = harness.start_session("main")
@@ -188,7 +187,7 @@ for line in sys.stdin:
)
with DeepSeekHarness(
launch_args_override=(sys.executable, str(script)),
_launch_args=(sys.executable, str(script)),
cwd=str(tmp_path),
) as harness:
with pytest.raises(
@@ -224,7 +223,7 @@ for line in sys.stdin:
with DeepSeekHarness(
cwd=".",
runtime_cwd=".",
launch_args_override=(sys.executable, str(script)),
_launch_args=(sys.executable, str(script)),
env={"CAPTURE": str(capture)},
):
pass
@@ -232,7 +231,7 @@ for line in sys.stdin:
expected = str(tmp_path.resolve())
assert json.loads(capture.read_text()) == {
"process": expected,
"environment": expected,
"environment": None,
"wire": expected,
}
@@ -263,7 +262,7 @@ for line in sys.stdin:
)
with DeepSeekHarness(
launch_args_override=(sys.executable, str(script)),
_launch_args=(sys.executable, str(script)),
cwd=str(tmp_path),
) as harness:
result = harness.run("spawn a helper", session_id="main")
@@ -312,7 +311,7 @@ for line in sys.stdin:
seen: list[str] = []
with DeepSeekHarness(
launch_args_override=(sys.executable, str(script)),
_launch_args=(sys.executable, str(script)),
cwd=str(tmp_path),
) as harness:
result = harness.run(
@@ -367,7 +366,7 @@ for line in sys.stdin:
)
with DeepSeekHarness(
launch_args_override=(sys.executable, str(script)),
_launch_args=(sys.executable, str(script)),
cwd=str(tmp_path),
) as harness:
result = harness.run("stay in your lane", session_id="main")
@@ -401,7 +400,7 @@ for line in sys.stdin:
""".strip()
)
with DeepSeekHarness(launch_args_override=(sys.executable, str(script)), cwd=str(tmp_path)) as harness:
with DeepSeekHarness(_launch_args=(sys.executable, str(script)), cwd=str(tmp_path)) as harness:
result = harness.run("one turn", session_id="main")
assert harness.client._notifications.qsize() == 0
@@ -441,7 +440,7 @@ for line in sys.stdin:
""".strip()
)
with DeepSeekHarness(launch_args_override=(sys.executable, str(script)), cwd=str(tmp_path)) as harness:
with DeepSeekHarness(_launch_args=(sys.executable, str(script)), cwd=str(tmp_path)) as harness:
first = harness.run("first turn", session_id="main")
second = harness.run("second turn", session_id="main")
@@ -473,7 +472,7 @@ for line in sys.stdin:
)
with HarnessClient(
HarnessConfig(launch_args_override=(sys.executable, str(script)))
HarnessConfig(_launch_args=(sys.executable, str(script)))
) as client:
init = client.initialize(provider="deepseek-official", cwd="/workspace", model="dsagent")
assert init.serverInfo.name == "fake-dsh"
@@ -612,7 +611,7 @@ for line in sys.stdin:
def broken_filter(_notification: object) -> bool:
raise RuntimeError("bad notification filter")
with HarnessClient(HarnessConfig(launch_args_override=(sys.executable, str(script)))) as client:
with HarnessClient(HarnessConfig(_launch_args=(sys.executable, str(script)))) as client:
client.initialize(provider="deepseek-official", cwd="/workspace", model="dsagent")
with (
client.subscribe_notifications(broken_filter) as broken,
@@ -649,7 +648,7 @@ for line in sys.stdin:
""".strip()
)
with HarnessClient(HarnessConfig(launch_args_override=(sys.executable, str(script)))) as client:
with HarnessClient(HarnessConfig(_launch_args=(sys.executable, str(script)))) as client:
client.initialize(provider="deepseek-official", cwd="/workspace", model="dsagent")
with pytest.raises(ValueError):
client.session_prompt("main", [{"type": "text", "text": "fix it"}])
@@ -677,7 +676,7 @@ for line in sys.stdin:
)
with HarnessClient(
HarnessConfig(launch_args_override=(sys.executable, str(script)))
HarnessConfig(_launch_args=(sys.executable, str(script)))
) as client:
client.initialize(provider="deepseek-official", cwd="/workspace", model="dsagent")
@@ -711,7 +710,7 @@ for line in sys.stdin:
)
with HarnessClient(
HarnessConfig(launch_args_override=(sys.executable, str(script)))
HarnessConfig(_launch_args=(sys.executable, str(script)))
) as client:
init = client.initialize(provider="deepseek-official", cwd="/workspace", model="dsagent")
assert init.serverInfo.name == "fake-dsh"
@@ -731,7 +730,7 @@ time.sleep(60)
with HarnessClient(
HarnessConfig(
launch_args_override=(sys.executable, str(script)),
_launch_args=(sys.executable, str(script)),
request_timeout_seconds=0.1,
)
) as client:
@@ -767,7 +766,7 @@ for line in sys.stdin:
client = HarnessClient(
HarnessConfig(
launch_args_override=(sys.executable, str(script)),
_launch_args=(sys.executable, str(script)),
shutdown_timeout_seconds=0.1,
)
)
@@ -792,6 +791,7 @@ import sys
for line in sys.stdin:
msg = json.loads(line)
if msg.get("method") == "initialize":
print("initialize diagnostic", file=sys.stderr, flush=True)
print(json.dumps({"jsonrpc": "2.0", "id": msg["id"], "error": {"code": -32000, "message": "bad initialize"}}), flush=True)
elif msg.get("method") == "shutdown":
print(json.dumps({"jsonrpc": "2.0", "id": msg["id"], "result": {}}), flush=True)
@@ -799,14 +799,16 @@ for line in sys.stdin:
""".strip()
)
client = HarnessClient(HarnessConfig(launch_args_override=(sys.executable, str(script))))
client = HarnessClient(HarnessConfig(_launch_args=(sys.executable, str(script))))
client.start()
proc = client._proc
assert proc is not None
with pytest.raises(Exception, match="bad initialize"):
with pytest.raises(JsonRpcError, match="bad initialize") as excinfo:
client.initialize(provider="deepseek-official", cwd=".", model="dsagent")
assert excinfo.value.code == -32000
assert "initialize diagnostic" in str(excinfo.value)
assert proc.wait(timeout=1) is not None
assert client._proc is None
@@ -824,6 +826,16 @@ def test_public_signatures_omit_unsupported_wire_parameters() -> None:
assert "max_tokens" in inspect.signature(HarnessClient.initialize).parameters
assert "client_name" not in HarnessConfig.__dataclass_fields__
assert "client_version" not in HarnessConfig.__dataclass_fields__
assert {"dsh_bin", "profile", "patches", "dsh_home"} <= set(
DeepSeekHarnessConfig.__dataclass_fields__
)
assert {"dsh_bin", "profile", "patches", "dsh_home"} <= set(
HarnessConfig.__dataclass_fields__
)
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__
assert "session_root" not in RunResult.__dataclass_fields__
def test_client_close_is_idempotent_before_and_after_start(tmp_path: Path) -> None:
@@ -845,7 +857,7 @@ for line in sys.stdin:
""".strip()
)
client = HarnessClient(HarnessConfig(launch_args_override=(sys.executable, str(script))))
client = HarnessClient(HarnessConfig(_launch_args=(sys.executable, str(script))))
client.start()
client.initialize(provider="deepseek-official", cwd="/workspace", model="dsagent")
client.close()
@@ -865,7 +877,7 @@ sys.exit(42)
with HarnessClient(
HarnessConfig(
launch_args_override=(sys.executable, str(script)),
_launch_args=(sys.executable, str(script)),
request_timeout_seconds=2,
)
) as client:
@@ -897,7 +909,7 @@ with open(os.environ["SEEN"], "w") as seen:
with HarnessClient(
HarnessConfig(
launch_args_override=(sys.executable, str(script)),
_launch_args=(sys.executable, str(script)),
env={"SEEN": str(output)},
)
) as client:
@@ -915,21 +927,22 @@ with open(os.environ["SEEN"], "w") as seen:
json.loads(line)
def _install_fake_bundled_runtime(
def _install_fake_bundled_dsh(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> Path:
"""Install a fake runtime package that records config and serves lifecycle calls.
Returns the fake bundled default config path.
"""
runtime = tmp_path / "dsh-jsonrpc-agent"
) -> None:
"""Install a fake runtime package that records dsh argv and serves lifecycle calls."""
runtime = tmp_path / "dsh.py"
runtime.write_text(
"""#!/usr/bin/env python3
"""
import json
import os
import sys
json.dump({"DSH_CORDIS_CONFIG": os.environ.get("DSH_CORDIS_CONFIG")}, open(os.environ["ENV_DUMP"], "w"))
json.dump({
"argv": sys.argv[1:],
"DSH_HOME": os.environ.get("DSH_HOME"),
"DSH_CORDIS_CONFIG": os.environ.get("DSH_CORDIS_CONFIG"),
}, open(os.environ["ENV_DUMP"], "w"))
for line in sys.stdin:
msg = json.loads(line)
if msg.get("method") == "initialize":
@@ -939,58 +952,75 @@ for line in sys.stdin:
break
""".strip()
)
runtime.chmod(0o755)
default_config = tmp_path / "default-cordis.yml"
module_dir = tmp_path / "deepseek_harness_runtime"
module_dir.mkdir()
(module_dir / "__init__.py").write_text(
f"""
def resolve_bundled_launch_args(mode=None):
return ({str(runtime)!r},)
def bundled_default_config_path():
return {str(default_config)!r}
return ({sys.executable!r}, {str(runtime)!r})
""".strip()
)
monkeypatch.syspath_prepend(str(tmp_path))
monkeypatch.delitem(sys.modules, "deepseek_harness_runtime", raising=False)
return default_config
@pytest.mark.parametrize("ambient_config", [None, ""], ids=["unset", "empty-counts-as-absent"])
def test_client_default_launch_uses_bundled_runtime_and_injects_default_config(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ambient_config: str | None
) -> None:
env_dump = tmp_path / "env.json"
default_config = _install_fake_bundled_runtime(tmp_path, monkeypatch)
if ambient_config is None:
monkeypatch.delenv("DSH_CORDIS_CONFIG", raising=False)
else:
monkeypatch.setenv("DSH_CORDIS_CONFIG", ambient_config)
with HarnessClient(HarnessConfig(env={"ENV_DUMP": str(env_dump)})) as client:
init = client.initialize(provider="deepseek-official", cwd="/workspace", model="deepseek-v4-pro")
assert init.serverInfo.name == "bundled-runtime"
assert json.loads(env_dump.read_text())["DSH_CORDIS_CONFIG"] == str(default_config)
def test_client_respects_explicit_config_over_bundled_default(
def test_client_default_launch_uses_bundled_dsh_sdk_profile_and_explicit_home(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
env_dump = tmp_path / "env.json"
_install_fake_bundled_runtime(tmp_path, monkeypatch)
home = tmp_path / "home"
patch = tmp_path / "sdk.patch.yml"
patch.write_text("[]\n")
_install_fake_bundled_dsh(tmp_path, monkeypatch)
monkeypatch.chdir(tmp_path)
monkeypatch.setenv("DSH_HOME", str(tmp_path / "ambient-home"))
monkeypatch.delenv("DSH_CORDIS_CONFIG", raising=False)
with HarnessClient(HarnessConfig(
profile="sdk",
patches=("sdk.patch.yml",),
dsh_home=str(home),
env={"ENV_DUMP": str(env_dump), "DSH_HOME": str(tmp_path / "env-home")},
)) as client:
init = client.initialize(provider="deepseek-official", cwd="/workspace", model="deepseek-v4-pro")
assert init.serverInfo.name == "bundled-runtime"
assert json.loads(env_dump.read_text()) == {
"argv": ["--profile", "sdk", "--patch", str(patch)],
"DSH_HOME": str(home),
"DSH_CORDIS_CONFIG": None,
}
def test_client_accepts_explicit_environment_dsh_home(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
env_dump = tmp_path / "env.json"
home = tmp_path / "environment-home"
_install_fake_bundled_dsh(tmp_path, monkeypatch)
with HarnessClient(
HarnessConfig(env={"ENV_DUMP": str(env_dump), "DSH_CORDIS_CONFIG": "./explicit.yml"})
HarnessConfig(profile="custom", env={"ENV_DUMP": str(env_dump), "DSH_HOME": str(home)})
) as client:
client.initialize(provider="deepseek-official", cwd="/workspace", model="deepseek-v4-pro")
assert json.loads(env_dump.read_text())["DSH_CORDIS_CONFIG"] == "./explicit.yml"
assert json.loads(env_dump.read_text()) == {
"argv": ["--profile", "custom"],
"DSH_HOME": str(home),
"DSH_CORDIS_CONFIG": None,
}
def test_client_rejects_an_implicit_default_dsh_home(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
_install_fake_bundled_dsh(tmp_path, monkeypatch)
monkeypatch.delenv("DSH_HOME", raising=False)
with pytest.raises(ValueError, match="explicit dsh_home or non-empty DSH_HOME"):
HarnessClient(HarnessConfig(env={})).start()
def test_client_reports_missing_bundled_runtime_dependency(monkeypatch: pytest.MonkeyPatch) -> None:
@@ -998,4 +1028,4 @@ def test_client_reports_missing_bundled_runtime_dependency(monkeypatch: pytest.M
monkeypatch.setattr(sys, "path", [])
with pytest.raises(FileNotFoundError, match="Install deepseek-harness-runtime-bin"):
HarnessClient().start()
HarnessClient(HarnessConfig(dsh_home="/explicit/home")).start()