mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-09-09 04:02:35 +00:00
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.
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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<string, unknown>[] = []
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user