fix(account,models): reconcile provider lifecycle with master
This commit is contained in:
commit
6ef32577a3
3271 files changed
+111666
-48764
No files matched your search
@@ -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-runtime/README.md
|
||||
README.md: b2fb0b29c44db0e9e251ce0227e334e08a14e0f1
|
||||
README.zh.md: 0f13c0a60c7378cb897ccfa7b7082bb86d722a07
|
||||
README.md: 3a27ef586cce3290b13b9cd4517ff5a491f312c2
|
||||
README.zh.md: 85bf35f62aa60bdb84e612518e2af740f1e8c708
|
||||
@@ -12,6 +12,19 @@ Production executables are named `deepseek-harness-sdk-runtime-<platform>-<arch>
|
||||
|
||||
Each target also requires `<executable-stem>-office/`, where the stem excludes `.exe`. This directory contains the complete installed Office packages and their dependencies, preserving engine resources, manifests, licenses, source inventories, and helper permissions. Copy this directory together with the executable. A missing target engine fails the sidecar build with its npm package name and target platform/architecture.
|
||||
|
||||
Each wheel also includes `<platform>-<arch>/primary-runtime/` (CPython and the locked Office Python libraries) and sibling `office-skills/` (three default workflows and their shared checker). These are ordinary relocatable files, not bytes embedded in the executable. The shared builder selects target-native archives for all five wheel targets and executes its smoke on the native build host. Packaging and installed-runtime lookup reject missing resources, wrong-platform metadata, and lost Python executable permissions. The short platform directory avoids repeating the executable name in Python DLL paths on Windows.
|
||||
|
||||
The packaged bootstrap supplies `DSH_BUNDLED_PRIMARY_RUNTIME` as a carrier default. The `sdk` profile uses it when `DSH_PRIMARY_RUNTIME` is unset; an explicit path overrides it, and an empty string disables the query and Office provider. External payloads retain the `primary-runtime/` plus sibling `office-skills/` layout. The SDK reads Python in place without copying it into `DSH_HOME`. Source and dev-only Node carriers have no bundled default; they use an explicit `DSH_PRIMARY_RUNTIME`.
|
||||
|
||||
Skill selection is independent of delivery: project, custom-directory, and user filesystem skills override same-name bundled skills. An SDK patch can disable only the Office provider while retaining the Python query:
|
||||
|
||||
```yaml
|
||||
- id: skill-office
|
||||
disabled: true
|
||||
```
|
||||
|
||||
To replace the three Office workflows and shared checker as a set, patch `skill-office.config.assetRoot` to another absolute resource directory. Use the filesystem skill provider for arbitrary skill collections. Configuration patches apply at process startup; changing skills does not require rebuilding the runtime wheel or Python environment.
|
||||
|
||||
Repository builds also materialize a dev-only `runtime/node/` carrier. It runs `node runtime/node/node_modules/@deepseek-ai/dsh/lib/bin.js` on system Node 22.19 or newer. It is never selected automatically and is excluded from wheels and sdists.
|
||||
|
||||
Both carriers execute the same `dsh` grammar and shipped profiles, including the standalone `sdk-minimal` tree and the full `web` profile with its frontend assets. The private `dsh-python-runtime-closure` manifest defines the packaged dependency closure; there is no Python-specific Node application or checked-in default `cordis.yml`.
|
||||
|
||||
@@ -12,6 +12,19 @@ wheel 包会安装 `dsh` 控制台命令和 `deepseek_harness_runtime` Python
|
||||
|
||||
每个目标还要求 `<executable-stem>-office/`,其中 stem 不含 `.exe`。该目录包含完整的已安装 Office 包及其依赖,保留引擎资源、清单、许可证、源码清单与辅助程序权限。复制可执行文件时必须一并复制此目录。缺少目标引擎会使 sidecar 构建失败,错误会指出其 npm 包名与目标平台/架构。
|
||||
|
||||
每个 wheel 还包含 `<platform>-<arch>/primary-runtime/`(CPython 与锁定版本的 Office Python 库)及同级 `office-skills/`(三个默认工作流与共用检查脚本)。它们是可重定位的普通文件,不嵌入可执行文件。共享构建器为全部五个 wheel 目标选择原生归档,并在对应构建主机上执行冒烟检查。打包与已安装运行时定位会拒绝缺失资源、平台不符的元数据及 Python 执行权限丢失。 较短的平台目录避免在 Windows Python DLL 路径中重复可执行文件名称。
|
||||
|
||||
打包启动器通过 `DSH_BUNDLED_PRIMARY_RUNTIME` 提供载体默认路径。`sdk` profile 在未设置 `DSH_PRIMARY_RUNTIME` 时使用该路径;显式路径覆盖它,空字符串禁用查询与 Office 提供方。外部 payload 保持 `primary-runtime/` 与同级 `office-skills/` 布局。SDK 原位读取 Python,不复制到 `DSH_HOME`。源码与仅供开发的 Node 载体没有随包默认路径,需显式设置 `DSH_PRIMARY_RUNTIME`。
|
||||
|
||||
选择 skills 与交付资源相互独立:项目、自定义目录和用户文件系统 skills 优先于同名随包 skills。SDK patch 可以仅禁用 Office 提供方,同时保留 Python 查询:
|
||||
|
||||
```yaml
|
||||
- id: skill-office
|
||||
disabled: true
|
||||
```
|
||||
|
||||
要成套替换三个 Office 工作流与共用检查脚本,可将 `skill-office.config.assetRoot` 配置为另一个绝对资源目录。任意 skill 集合通过文件系统 skill 提供方加载。配置 patch 在进程启动时生效;切换 skills 无需重建 runtime wheel 或 Python 环境。
|
||||
|
||||
仓库构建还会物化仅限开发的 `runtime/node/` 载体。它在系统 Node 22.19 或更高版本上运行 `node runtime/node/node_modules/@deepseek-ai/dsh/lib/bin.js`。系统不会自动选择它,而且 wheel 包与 sdist 均不包含它。
|
||||
|
||||
两种载体执行相同的 `dsh` 语法与随附 profile,包括独立的 `sdk-minimal` 配置树,以及包含前端产物的完整 `web` profile。私有 `dsh-python-runtime-closure` manifest(元数据清单)定义打包依赖闭包;不存在 Python 专用 Node 应用或检入的默认 `cordis.yml`。
|
||||
|
||||
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
import runpy
|
||||
import stat
|
||||
from pathlib import Path
|
||||
|
||||
@@ -76,7 +77,7 @@ class RuntimeBuildHook(BuildHookInterface):
|
||||
target = next(name for name, value in _PLATFORMS.items() if value[0] == platform_tag)
|
||||
runtime_dir = Path(self.root) / "src" / "deepseek_harness_runtime" / "runtime"
|
||||
runtime_files = sorted(
|
||||
runtime_dir.glob("deepseek-harness-sdk-runtime-*") if runtime_dir.is_dir() else []
|
||||
(path for path in runtime_dir.iterdir() if path.name != "node") if runtime_dir.is_dir() else []
|
||||
)
|
||||
expected_files = (
|
||||
[expected_executable, f"{expected_executable.removesuffix('.exe')}-rg.exe"]
|
||||
@@ -87,6 +88,8 @@ class RuntimeBuildHook(BuildHookInterface):
|
||||
expected_files.append(f"{expected_executable}-spawn-helper")
|
||||
office = runtime_dir / f"{expected_executable.removesuffix('.exe')}-office"
|
||||
expected_files.append(office.name)
|
||||
resources = runtime_dir / target
|
||||
expected_files.append(resources.name)
|
||||
expected_files.sort()
|
||||
found_files = [path.name for path in runtime_files]
|
||||
if found_files != expected_files:
|
||||
@@ -94,6 +97,10 @@ class RuntimeBuildHook(BuildHookInterface):
|
||||
f"runtime wheel {platform_tag} payload must be {expected_files}; found {found_files}"
|
||||
)
|
||||
for executable in runtime_files:
|
||||
if executable == resources:
|
||||
validate = runpy.run_path(str(runtime_dir.parent / "_resources.py"))["validate_resources"]
|
||||
validate(resources, target)
|
||||
continue
|
||||
if executable == office:
|
||||
adapter = office / "node_modules/@deepseek-ai/libreoffice-kit/package.json"
|
||||
if not adapter.is_file():
|
||||
|
||||
@@ -16,14 +16,20 @@
|
||||
"@deepseek-ai/dsh-agent": "workspace:^",
|
||||
"@deepseek-ai/dsh-agent-instructions": "workspace:^",
|
||||
"@deepseek-ai/dsh-agent-loop": "workspace:^",
|
||||
"@deepseek-ai/dsh-agent-preset-registry": "workspace:^",
|
||||
"@deepseek-ai/dsh-agent-tool-presentation": "workspace:^",
|
||||
"@deepseek-ai/dsh-anonymous-user-id": "workspace:^",
|
||||
"@deepseek-ai/dsh-app-boot": "workspace:^",
|
||||
"@deepseek-ai/dsh-atomic-write": "workspace:^",
|
||||
"@deepseek-ai/dsh-attachment": "workspace:^",
|
||||
"@deepseek-ai/dsh-authorization": "workspace:^",
|
||||
"@deepseek-ai/dsh-base": "workspace:^",
|
||||
"@deepseek-ai/dsh-bash-local": "workspace:^",
|
||||
"@deepseek-ai/dsh-brand": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-connection": "workspace:^",
|
||||
"@deepseek-ai/dsh-cmdline": "workspace:^",
|
||||
"@deepseek-ai/dsh-command-feedback": "workspace:^",
|
||||
"@deepseek-ai/dsh-message-feedback": "workspace:^",
|
||||
"@deepseek-ai/dsh-ptc-runtime": "workspace:^",
|
||||
"@deepseek-ai/dsh-ptc-runtime-node": "workspace:^",
|
||||
"@deepseek-ai/dsh-command-compact": "workspace:^",
|
||||
@@ -33,8 +39,10 @@
|
||||
"@deepseek-ai/dsh-compaction-basic": "workspace:^",
|
||||
"@deepseek-ai/dsh-compaction-image-offload": "workspace:^",
|
||||
"@deepseek-ai/dsh-compaction-tool-result-pruner": "workspace:^",
|
||||
"@deepseek-ai/dsh-config-editor": "workspace:^",
|
||||
"@deepseek-ai/dsh-cordis-host-runner": "workspace:^",
|
||||
"@deepseek-ai/dsh-credentials": "workspace:^",
|
||||
"@deepseek-ai/dsh-deepseek-account": "workspace:^",
|
||||
"@deepseek-ai/dsh-deepseek-llm-api-extensions": "workspace:^",
|
||||
"@deepseek-ai/dsh-fs": "workspace:^",
|
||||
"@deepseek-ai/dsh-fs-local": "workspace:^",
|
||||
@@ -46,6 +54,8 @@
|
||||
"@deepseek-ai/dsh-hook-protocol": "workspace:^",
|
||||
"@deepseek-ai/dsh-hooks-claude-code": "workspace:^",
|
||||
"@deepseek-ai/dsh-hooks-codex": "workspace:^",
|
||||
"@deepseek-ai/dsh-host-plugin-inventory": "workspace:^",
|
||||
"@deepseek-ai/dsh-host-webserver": "workspace:^",
|
||||
"@deepseek-ai/dsh-http-proxy": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/dsh-jobs": "workspace:^",
|
||||
@@ -60,7 +70,10 @@
|
||||
"@deepseek-ai/dsh-permission-presets": "workspace:^",
|
||||
"@deepseek-ai/dsh-persona": "workspace:^",
|
||||
"@deepseek-ai/dsh-plan-mode": "workspace:^",
|
||||
"@deepseek-ai/dsh-plugin-manager": "workspace:^",
|
||||
"@deepseek-ai/dsh-plugin-package-inventory-deepseek": "workspace:^",
|
||||
"@deepseek-ai/dsh-ptc-runtime": "workspace:^",
|
||||
"@deepseek-ai/dsh-ptc-runtime-node": "workspace:^",
|
||||
"@deepseek-ai/dsh-pwsh-local": "workspace:^",
|
||||
"@deepseek-ai/dsh-repeat-tool-reminder": "workspace:^",
|
||||
"@deepseek-ai/dsh-sandbox": "workspace:^",
|
||||
@@ -86,6 +99,10 @@
|
||||
"@deepseek-ai/dsh-shell-env": "workspace:^",
|
||||
"@deepseek-ai/dsh-skill": "workspace:^",
|
||||
"@deepseek-ai/dsh-skill-filesystem": "workspace:^",
|
||||
"@deepseek-ai/dsh-skill-office": "workspace:^",
|
||||
"@deepseek-ai/dsh-storage": "workspace:^",
|
||||
"@deepseek-ai/dsh-storage-domain": "workspace:^",
|
||||
"@deepseek-ai/dsh-tool-workspace-dependencies": "workspace:^",
|
||||
"@deepseek-ai/dsh-spill": "workspace:^",
|
||||
"@deepseek-ai/dsh-subagent": "workspace:^",
|
||||
"@deepseek-ai/dsh-subagent-acp": "workspace:^",
|
||||
@@ -104,11 +121,11 @@
|
||||
"@deepseek-ai/dsh-tool-bash-persistent": "workspace:^",
|
||||
"@deepseek-ai/dsh-tool-call-timeout-policy": "workspace:^",
|
||||
"@deepseek-ai/dsh-tool-cordis": "workspace:^",
|
||||
"@deepseek-ai/dsh-tool-present": "workspace:^",
|
||||
"@deepseek-ai/dsh-tool-fs": "workspace:^",
|
||||
"@deepseek-ai/dsh-tool-fs-search": "workspace:^",
|
||||
"@deepseek-ai/dsh-tool-goal": "workspace:^",
|
||||
"@deepseek-ai/dsh-tool-jobs": "workspace:^",
|
||||
"@deepseek-ai/dsh-tool-present": "workspace:^",
|
||||
"@deepseek-ai/dsh-tool-pwsh": "workspace:^",
|
||||
"@deepseek-ai/dsh-tool-pwsh-persistent": "workspace:^",
|
||||
"@deepseek-ai/dsh-tool-ralph": "workspace:^",
|
||||
@@ -121,6 +138,7 @@
|
||||
"@deepseek-ai/dsh-tool-workflow": "workspace:^",
|
||||
"@deepseek-ai/dsh-tools": "workspace:^",
|
||||
"@deepseek-ai/dsh-typert-protocol": "workspace:^",
|
||||
"@deepseek-ai/dsh-typert-registry": "workspace:^",
|
||||
"@deepseek-ai/dsh-user-approval": "workspace:^",
|
||||
"@deepseek-ai/dsh-user-questions": "workspace:^",
|
||||
"@deepseek-ai/dsh-util-time": "workspace:^",
|
||||
@@ -132,8 +150,6 @@
|
||||
"@deepseek-ai/dsh-web-search-perplexity": "workspace:^",
|
||||
"@deepseek-ai/dsh-workflow": "workspace:^",
|
||||
"@deepseek-ai/dsh-workflow-ptc": "workspace:^",
|
||||
"@deepseek-ai/schemastery": "workspace:^",
|
||||
"@deepseek-ai/dsh-plugin-manager": "workspace:^",
|
||||
"@deepseek-ai/dsh-host-plugin-inventory": "workspace:^"
|
||||
"@deepseek-ai/schemastery": "workspace:^"
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,13 @@ dsh = "deepseek_harness_runtime:main"
|
||||
|
||||
# Include the injected dsh executable and sidecars; exclude the dev-only node closure.
|
||||
[tool.hatch.build]
|
||||
artifacts = ["src/deepseek_harness_runtime/runtime/deepseek-harness-sdk-runtime-*", "src/deepseek_harness_runtime/runtime/deepseek-harness-sdk-runtime-*/**"]
|
||||
artifacts = [
|
||||
"src/deepseek_harness_runtime/runtime/deepseek-harness-sdk-runtime-*",
|
||||
"src/deepseek_harness_runtime/runtime/deepseek-harness-sdk-runtime-*/**",
|
||||
"src/deepseek_harness_runtime/runtime/linux-*/**",
|
||||
"src/deepseek_harness_runtime/runtime/macos-*/**",
|
||||
"src/deepseek_harness_runtime/runtime/win-*/**",
|
||||
]
|
||||
exclude = ["src/deepseek_harness_runtime/runtime/node"]
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
/** Private entry owned by the Python single-file runtime packaging. */
|
||||
import { registerHooks } from 'node:module'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { isSea } from 'node:sea'
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url'
|
||||
|
||||
@@ -28,6 +29,11 @@ if (aclRunner !== undefined && process.argv[2] === aclRunner) {
|
||||
Reflect.deleteProperty(process.env, 'DSH_PTC_RUNTIME_NODE')
|
||||
await import('@deepseek-ai/dsh-ptc-runtime-node/process')
|
||||
} else if (selection === undefined) {
|
||||
if (isSea()) {
|
||||
// Carrier default stays separate so process/home environment and profile patches can override it.
|
||||
const platform = process.platform === 'darwin' ? 'macos' : process.platform === 'win32' ? 'win' : process.platform
|
||||
process.env.DSH_BUNDLED_PRIMARY_RUNTIME = join(dirname(process.execPath), `${platform}-${process.arch}`, 'primary-runtime')
|
||||
}
|
||||
const { runCli } = await import('@deepseek-ai/dsh/lib/bin.js')
|
||||
await runCli()
|
||||
} else {
|
||||
|
||||
@@ -28,6 +28,7 @@ import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from ._resources import validate_resources
|
||||
|
||||
PACKAGE_METADATA_FILENAME = "deepseek-harness-runtime.json"
|
||||
|
||||
@@ -59,7 +60,9 @@ def bundled_runtime_path() -> Path:
|
||||
|
||||
Raises FileNotFoundError when the platform is unsupported, the executable
|
||||
has not been placed into this package, the ripgrep or Office sidecar is
|
||||
missing, or the required macOS spawn helper is missing; the message names
|
||||
missing, the authoring resources are incomplete, or the required macOS
|
||||
spawn helper is missing. Invalid authoring metadata or Python permissions
|
||||
raise ValueError. Missing executable/Office messages name
|
||||
the acquisition routes (acquisition strategy is deliberately separate from
|
||||
this lookup interface, so an on-demand download can replace it without
|
||||
touching callers).
|
||||
@@ -104,6 +107,7 @@ def bundled_runtime_path() -> Path:
|
||||
f"deepseek-harness-runtime-bin is missing the Office sidecar engine {engine} at {office}. "
|
||||
+ _EXE_ACQUISITION_HINT
|
||||
)
|
||||
validate_resources(path.with_name(tag), tag)
|
||||
return path
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
"""Validate the authoring resources in staged, installed, or archived runtime wheels."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import stat
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def validate_resources(root: Path | zipfile.Path, target: str) -> None:
|
||||
"""Reject a missing or wrong-target Python environment or bundled Office skill tree."""
|
||||
manifest_path = root / "primary-runtime/runtime.json"
|
||||
if not manifest_path.is_file():
|
||||
raise FileNotFoundError(f"runtime authoring resources are missing: {manifest_path}")
|
||||
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
|
||||
platform, arch = target.rsplit("-", 1)
|
||||
platform = {"macos": "darwin", "win": "win32"}.get(platform, platform)
|
||||
if not isinstance(manifest, dict) or (manifest.get("platform"), manifest.get("arch")) != (platform, arch):
|
||||
raise ValueError(f"runtime authoring resources do not match target {target}")
|
||||
version = manifest.get("python")
|
||||
if not isinstance(version, str) or not re.fullmatch(r"\d+\.\d+\.\d+", version):
|
||||
raise ValueError("runtime authoring resources have invalid Python version metadata")
|
||||
if not isinstance(manifest.get("pythonPackages"), dict) or not manifest["pythonPackages"]:
|
||||
raise ValueError("runtime authoring resources have no Python distributions")
|
||||
python_root = root / "primary-runtime/dependencies/python"
|
||||
executable = python_root / ("python.exe" if platform == "win32" else "bin/python3")
|
||||
packages = python_root / ("Lib/site-packages" if platform == "win32" else f"lib/python{version.rsplit('.', 1)[0]}/site-packages")
|
||||
required = [executable, root / "office-skills/scripts/check_office.py"]
|
||||
required.extend(root / f"office-skills/office-{kind}/SKILL.md" for kind in ("docx", "pptx", "xlsx"))
|
||||
for path in required:
|
||||
if not path.is_file():
|
||||
raise FileNotFoundError(f"runtime authoring resource is missing: {path}")
|
||||
if not packages.is_dir():
|
||||
raise FileNotFoundError(f"runtime Python site-packages is missing: {packages}")
|
||||
if platform != "win32":
|
||||
mode = (executable.root.getinfo(executable.at).external_attr >> 16
|
||||
if isinstance(executable, zipfile.Path) else executable.stat().st_mode)
|
||||
if mode & stat.S_IXUSR == 0:
|
||||
raise ValueError(f"runtime Python lost its executable bit: {executable}")
|
||||
@@ -26,16 +26,22 @@ class MockCompletionHandler(BaseHTTPRequestHandler):
|
||||
body = self.rfile.read(length).decode("utf-8")
|
||||
self.requests.append({
|
||||
"path": self.path,
|
||||
"authorization": self.headers.get("authorization"),
|
||||
"api_key": self.headers.get("x-api-key"),
|
||||
"body": json.loads(body),
|
||||
})
|
||||
self.send_response(200)
|
||||
self.send_header("content-type", "text/event-stream")
|
||||
self.end_headers()
|
||||
self.wfile.write(b'data: {"choices":[{"delta":{"role":"assistant","content":null,"reasoning_content":""}}]}\n\n')
|
||||
self.wfile.write(b'data: {"choices":[{"delta":{"content":"SDK runtime reached the configured HTTP model endpoint."}}]}\n\n')
|
||||
self.wfile.write(b'data: {"choices":[{"delta":{"content":""},"finish_reason":"stop"}],"usage":{"prompt_tokens":7,"completion_tokens":9}}\n\n')
|
||||
self.wfile.write(b"data: [DONE]\n\n")
|
||||
events = [
|
||||
{"type": "message_start", "message": {"id": "sdk-smoke", "model": "sdk-smoke-model", "usage": {"input_tokens": 7, "output_tokens": 0}}},
|
||||
{"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}},
|
||||
{"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "SDK runtime reached the configured HTTP model endpoint."}},
|
||||
{"type": "content_block_stop", "index": 0},
|
||||
{"type": "message_delta", "delta": {"stop_reason": "end_turn"}, "usage": {"output_tokens": 9}},
|
||||
{"type": "message_stop"},
|
||||
]
|
||||
for event in events:
|
||||
self.wfile.write(f"event: {event['type']}\ndata: {json.dumps(event)}\n\n".encode())
|
||||
|
||||
def log_message(self, _format: str, *_args: object) -> None:
|
||||
return
|
||||
@@ -46,7 +52,7 @@ def run_smoke(repo_root: Path, keep_sessions: bool) -> None:
|
||||
session_root = dsh_home / "sessions"
|
||||
runtime_entry = repo_root / "apps/cli/src/bin.ts"
|
||||
server = ThreadingHTTPServer(("127.0.0.1", 0), MockCompletionHandler)
|
||||
thread = threading.Thread(target=server.serve_forever, name="mock-openai-compatible-server", daemon=True)
|
||||
thread = threading.Thread(target=server.serve_forever, name="mock-messages-server", daemon=True)
|
||||
thread.start()
|
||||
base_url = f"http://127.0.0.1:{server.server_address[1]}"
|
||||
|
||||
@@ -86,7 +92,8 @@ def run_smoke(repo_root: Path, keep_sessions: bool) -> None:
|
||||
assert len(MockCompletionHandler.requests) == 1
|
||||
request = MockCompletionHandler.requests[0]
|
||||
print(json.dumps(request, ensure_ascii=False, indent=2)[:4000])
|
||||
assert request["authorization"] == "Bearer sdk-smoke-key"
|
||||
assert request["path"] == "/v1/messages"
|
||||
assert request["api_key"] == "sdk-smoke-key"
|
||||
assert request["body"]["model"] == "sdk-smoke-model"
|
||||
|
||||
jsonl_files = sorted(session_root.rglob("*.jsonl.zstd"))
|
||||
|
||||
@@ -595,6 +595,15 @@ def test_client_keeps_unmatched_notifications_available_globally_while_subscribe
|
||||
def test_session_subscription_keeps_descendant_relationships_across_subscriptions() -> None:
|
||||
client = HarnessClient()
|
||||
with client.subscribe_session_notifications("main") as first:
|
||||
unknown_child = {
|
||||
"type": "subagent/catalog", "seq": 0, "time": 1,
|
||||
"data": {"version": 1, "childId": "unreadable-child", "childCreatedAt": 1, "mode": "unknown"},
|
||||
}
|
||||
client._handle_message({
|
||||
"jsonrpc": "2.0", "method": "session.event",
|
||||
"params": {"sessionId": "main", "event": unknown_child},
|
||||
})
|
||||
assert first.next().payload == {"sessionId": "main", "event": unknown_child}
|
||||
client._handle_message({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "subagent.started",
|
||||
|
||||
@@ -26,6 +26,18 @@ def test_release_tag_is_optional_for_non_release_builds() -> None:
|
||||
build_python_release.validate_release_tag(None, "1.2.3")
|
||||
|
||||
|
||||
def test_wheel_verification_uses_distribution_metadata_not_nested_libraries(tmp_path: Path) -> None:
|
||||
wheel = tmp_path / "sdk.whl"
|
||||
with zipfile.ZipFile(wheel, "w") as archive:
|
||||
archive.writestr("nested/library.dist-info/WHEEL", "Tag: cp312-cp312-linux_x86_64\n")
|
||||
archive.writestr("nested/library.dist-info/METADATA", "Name: library\nVersion: 0.0.1\n")
|
||||
archive.writestr("deepseek_harness_sdk-1.2.3.dist-info/WHEEL", "Tag: py3-none-any\n")
|
||||
archive.writestr("deepseek_harness_sdk-1.2.3.dist-info/METADATA",
|
||||
"Name: deepseek-harness-sdk\nVersion: 1.2.3\nLicense-Expression: MIT\n"
|
||||
"License-File: LICENSE\nRequires-Dist: deepseek-harness-runtime-bin==1.2.3\n")
|
||||
build_python_release.verify_wheel(wheel, "sdk", "1.2.3", None)
|
||||
|
||||
|
||||
def test_release_tag_must_match_repository_version() -> None:
|
||||
build_python_release.validate_release_tag("python-v1.2.3", "1.2.3")
|
||||
|
||||
@@ -96,6 +108,19 @@ def test_stage_sdk_keeps_distribution_module_and_runtime_pin_distinct(tmp_path:
|
||||
assert (destination / "src" / "deepseek_harness" / "__init__.py").is_file()
|
||||
|
||||
|
||||
def test_copy_package_omits_generated_carriers_before_staging_one_target(tmp_path: Path) -> None:
|
||||
source = tmp_path / "source"
|
||||
module = source / "src/deepseek_harness_runtime"
|
||||
for path in ("runtime/macos-arm64/primary-runtime/runtime.json", "runtime/node/package.json",
|
||||
"runtime/deepseek-harness-sdk-runtime-win-x64.exe", "__init__.py", "_resources.py"):
|
||||
file = module / path
|
||||
file.parent.mkdir(parents=True, exist_ok=True)
|
||||
file.touch()
|
||||
destination = tmp_path / "staged"
|
||||
build_python_release.copy_package(source, destination)
|
||||
assert sorted(path.name for path in (destination / "src/deepseek_harness_runtime").iterdir()) == ["__init__.py", "_resources.py"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("target", "with_helper"),
|
||||
[("linux-x64", False), ("macos-arm64", True), ("macos-x64", True), ("win-x64.exe", False)],
|
||||
@@ -124,6 +149,10 @@ def test_stage_runtime_copies_platform_payload(
|
||||
office_asset = office / "node_modules" / "@deepseek-ai" / "libreoffice-kit-wasm" / "assets" / "soffice.data"
|
||||
office_asset.parent.mkdir(parents=True)
|
||||
office_asset.write_bytes(b"office data")
|
||||
resources = executable.with_name(executable.name.removeprefix("deepseek-harness-sdk-runtime-").removesuffix(".exe"))
|
||||
resource = resources / "office-skills/scripts/check_office.py"
|
||||
resource.parent.mkdir(parents=True)
|
||||
resource.write_text("checker")
|
||||
destination = tmp_path / "staging"
|
||||
|
||||
build_python_release.stage_runtime(destination, "1.2.3", executable, executable.name)
|
||||
@@ -135,6 +164,7 @@ def test_stage_runtime_copies_platform_payload(
|
||||
if path.is_file()
|
||||
} == expected
|
||||
assert (runtime_dir / office.name / office_asset.relative_to(office)).read_bytes() == b"office data"
|
||||
assert (runtime_dir / resources.name / resource.relative_to(resources)).read_text() == "checker"
|
||||
pyproject = (destination / "pyproject.toml").read_text()
|
||||
assert 'license = "MIT"' in pyproject
|
||||
assert 'license-files = ["LICENSE", "THIRD_PARTY_NOTICES.md"]' in pyproject
|
||||
|
||||
@@ -6,11 +6,13 @@ import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
|
||||
import deepseek_harness_runtime as runtime
|
||||
import pytest
|
||||
from deepseek_harness_runtime._resources import validate_resources
|
||||
|
||||
from deepseek_harness_runtime import (
|
||||
RUNTIME_MODE_ENV_VAR,
|
||||
@@ -20,7 +22,7 @@ from deepseek_harness_runtime import (
|
||||
)
|
||||
|
||||
|
||||
def _office_sidecar(executable: Path, native_targets: tuple[str, ...] = ("darwin-arm64", "darwin-x64", "win32-x64")) -> Path:
|
||||
def _resource_sidecars(executable: Path, native_targets: tuple[str, ...] = ("darwin-arm64", "darwin-x64", "win32-x64")) -> Path:
|
||||
office = executable.with_name(f"{executable.name.removesuffix('.exe')}-office")
|
||||
tag = executable.name.removeprefix("deepseek-harness-sdk-runtime-").removesuffix(".exe")
|
||||
native = tag.replace("win-", "win32-").replace("macos-", "darwin-")
|
||||
@@ -33,6 +35,21 @@ def _office_sidecar(executable: Path, native_targets: tuple[str, ...] = ("darwin
|
||||
adapter.write_text(json.dumps({"optionalDependencies": {
|
||||
f"@deepseek-ai/libreoffice-kit-{target}": "0.0.1" for target in (*native_targets, "wasm")
|
||||
}}), encoding="utf-8")
|
||||
resources = executable.with_name(tag)
|
||||
manifest = resources / "primary-runtime/runtime.json"
|
||||
manifest.parent.mkdir(parents=True)
|
||||
manifest.write_text(json.dumps({"platform": native.rsplit("-", 1)[0], "arch": tag.rsplit("-", 1)[1],
|
||||
"python": "3.12.14", "pythonPackages": {"numpy": "2.3.5"}}))
|
||||
python = resources / "primary-runtime/dependencies/python" / ("python.exe" if tag.startswith("win-") else "bin/python3")
|
||||
python.parent.mkdir(parents=True)
|
||||
python.touch()
|
||||
python.chmod(0o755)
|
||||
packages = python.parent if tag.startswith("win-") else python.parent.parent
|
||||
(packages / ("Lib/site-packages" if tag.startswith("win-") else "lib/python3.12/site-packages")).mkdir(parents=True)
|
||||
for file in ["scripts/check_office.py", *(f"office-{kind}/SKILL.md" for kind in ("docx", "pptx", "xlsx"))]:
|
||||
path = resources / "office-skills" / file
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.touch()
|
||||
return office
|
||||
|
||||
|
||||
@@ -41,6 +58,43 @@ def test_unknown_explicit_mode_fails_loud() -> None:
|
||||
resolve_bundled_launch_args("bogus")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("target", ["linux-x64", "linux-arm64", "macos-arm64", "macos-x64", "win-x64"])
|
||||
@pytest.mark.parametrize("invalid", [None, "manifest", "platform", "python", "skills", "packages", "mode"])
|
||||
def test_authoring_resources_validate_installed_and_wheel_payloads(tmp_path: Path, target: str, invalid: str | None) -> None:
|
||||
executable = tmp_path / f"deepseek-harness-sdk-runtime-{target}"
|
||||
_resource_sidecars(executable)
|
||||
root = tmp_path / target
|
||||
python = root / "primary-runtime/dependencies/python" / ("python.exe" if target == "win-x64" else "bin/python3")
|
||||
if invalid == "mode" and target == "win-x64":
|
||||
pytest.skip("Windows executables do not require a POSIX executable bit")
|
||||
if invalid == "manifest":
|
||||
(root / "primary-runtime/runtime.json").unlink()
|
||||
elif invalid == "platform":
|
||||
manifest_path = root / "primary-runtime/runtime.json"
|
||||
manifest = json.loads(manifest_path.read_text())
|
||||
manifest["arch"] = "wrong"
|
||||
manifest_path.write_text(json.dumps(manifest))
|
||||
elif invalid == "python":
|
||||
python.unlink()
|
||||
elif invalid == "skills":
|
||||
(root / "office-skills/office-docx/SKILL.md").unlink()
|
||||
elif invalid == "packages":
|
||||
next(root.rglob("site-packages")).rmdir()
|
||||
elif invalid == "mode":
|
||||
python.chmod(0o644)
|
||||
archive_path = tmp_path / "payload.zip"
|
||||
with zipfile.ZipFile(archive_path, "w") as archive:
|
||||
for path in root.rglob("*"):
|
||||
archive.write(path, str(path.relative_to(root)).replace("\\", "/"))
|
||||
with zipfile.ZipFile(archive_path) as archive:
|
||||
for candidate in (root, zipfile.Path(archive)):
|
||||
if invalid is None:
|
||||
validate_resources(candidate, target)
|
||||
else:
|
||||
with pytest.raises((ValueError, FileNotFoundError)):
|
||||
validate_resources(candidate, target)
|
||||
|
||||
|
||||
def test_unknown_env_mode_fails_loud(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setenv(RUNTIME_MODE_ENV_VAR, "bogus")
|
||||
with pytest.raises(ValueError, match="expected 'exe' or 'node'"):
|
||||
@@ -64,7 +118,7 @@ def test_runtime_requires_spawn_helper_only_on_macos(
|
||||
linux = runtime_dir / "deepseek-harness-sdk-runtime-linux-x64"
|
||||
linux.touch()
|
||||
Path(f"{linux}-rg").touch()
|
||||
_office_sidecar(linux)
|
||||
_resource_sidecars(linux)
|
||||
macos = runtime_dir / "deepseek-harness-sdk-runtime-macos-arm64"
|
||||
macos.touch()
|
||||
Path(f"{macos}-rg").touch()
|
||||
@@ -85,7 +139,7 @@ def test_windows_runtime_uses_exe_payload_and_exe_sidecar(
|
||||
executable = runtime_dir / "deepseek-harness-sdk-runtime-win-x64.exe"
|
||||
executable.touch()
|
||||
(runtime_dir / "deepseek-harness-sdk-runtime-win-x64-rg.exe").touch()
|
||||
_office_sidecar(executable)
|
||||
_resource_sidecars(executable)
|
||||
monkeypatch.setattr(runtime, "bundled_package_dir", lambda: tmp_path)
|
||||
monkeypatch.setattr(runtime, "_current_platform_tag", lambda: "win-x64")
|
||||
|
||||
@@ -122,7 +176,7 @@ def test_runtime_requires_ripgrep_sidecar(
|
||||
runtime.bundled_runtime_path()
|
||||
|
||||
|
||||
def test_runtime_requires_complete_office_sidecar(
|
||||
def test_runtime_requires_complete_resource_sidecars(
|
||||
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
executable = tmp_path / "runtime" / "deepseek-harness-sdk-runtime-linux-x64"
|
||||
@@ -134,7 +188,7 @@ def test_runtime_requires_complete_office_sidecar(
|
||||
|
||||
with pytest.raises(FileNotFoundError, match="Office sidecar"):
|
||||
runtime.bundled_runtime_path()
|
||||
office = _office_sidecar(executable)
|
||||
office = _resource_sidecars(executable)
|
||||
assert runtime.bundled_runtime_path() == executable
|
||||
(office / "node_modules/@deepseek-ai/libreoffice-kit-wasm/prebuilds.json").unlink()
|
||||
with pytest.raises(FileNotFoundError, match="Office sidecar"):
|
||||
@@ -250,7 +304,7 @@ def test_runtime_requires_its_platform_office_engine(
|
||||
executable.with_name(f"{executable.stem}-rg{extension}").touch()
|
||||
if target.startswith("macos-"):
|
||||
Path(f"{executable}-spawn-helper").touch()
|
||||
office = _office_sidecar(executable, native_targets)
|
||||
office = _resource_sidecars(executable, native_targets)
|
||||
monkeypatch.setattr(runtime, "bundled_package_dir", lambda: tmp_path)
|
||||
monkeypatch.setattr(runtime, "_current_platform_tag", lambda: target)
|
||||
assert runtime.bundled_runtime_path() == executable
|
||||
|
||||
Reference in new issue
Block a user