feat(llm-deepseek): configure max token defaults

This commit is contained in:
Yichen Jiang
2026-07-30 21:04:00 +08:00
parent 2fb90a744e
commit daf70f3660
50 changed files with 430 additions and 95 deletions

View File

@@ -5,7 +5,7 @@
# carries ACP JSON-RPC.
# The DeepSeek adapter. Shipped default: full thinking at max effort on every
# request (wire-only defaults; they never enter the request header).
# request; exact-model resolution materializes request defaults before logging.
- id: llm-deepseek
name: '@deepseek-ai/dsh-llm-deepseek'
config:
@@ -13,7 +13,6 @@
baseURL: !!js process.env.DEEPSEEK_BASE_URL
thinking: enabled
reasoningEffort: max
defaultContextWindow: 256000
models:
- id: deepseek-v4-flash
- id: deepseek-v4-pro

View File

@@ -17,7 +17,6 @@
baseURL: !!js process.env.DEEPSEEK_BASE_URL
thinking: enabled
reasoningEffort: max
defaultContextWindow: 256000
retryPolicy:
mode: normal
maxRetries: 2

View File

@@ -4,8 +4,8 @@
# The DeepSeek adapter. Swap to '@deepseek-ai/dsh-llm-pi-ai' for the pi-ai-backed
# twin (same config shape; `reasoning: high` replaces thinking/reasoningEffort).
# Shipped default: full thinking at max effort on every request (wire-only
# defaults; they never enter the request header).
# Shipped default: full thinking at max effort on every request. Exact-model
# resolution materializes the effort before the request header is logged.
- id: llm-deepseek
name: '@deepseek-ai/dsh-llm-deepseek'
config:

View File

@@ -0,0 +1,17 @@
- id: base
name: '@cordisjs/plugin-include'
config:
path: ../../cordis.yml
patches:
- id: llm-deepseek
config:
apiKey: snapshot-key
baseURL: !!js process.env.DSH_SNAPSHOT_BASE_URL
thinking: disabled
- id: cli-agent
config:
provider: deepseek
model: deepseek-v4-flash
persistenceRoot: './.sessions'
workspaceContext: false
persona: 'Keyless DeepSeek adapter defaults snapshot.'

View File

@@ -1,4 +1,6 @@
import { readFile, readdir, writeFile } from 'node:fs/promises'
import { createServer } from 'node:http'
import type { IncomingMessage, ServerResponse } from 'node:http'
import { delimiter, dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import {
@@ -32,6 +34,7 @@ const ralphConfigPath = fileURLToPath(new URL('../ralph.cordis.snapshot.yml', im
const binScript = fileURLToPath(new URL('../../../packages/examples/cli-demo/src/bin.ts', import.meta.url))
const tsconfigPath = fileURLToPath(new URL('../../../tsconfig.json', import.meta.url))
const reasoningConfigPath = fileURLToPath(new URL('./fixtures/cli.cordis.yml', import.meta.url))
const deepseekDefaultsConfigPath = fileURLToPath(new URL('./fixtures/deepseek-defaults.cordis.yml', import.meta.url))
const refreshing = process.env.DSH_SNAPSHOT === 'refresh'
interface JsonObject {
@@ -43,6 +46,40 @@ interface PersistedLog {
readonly header: JsonObject
}
interface DeepSeekDefaultsServer {
readonly url: string
readonly requests: JsonObject[]
close(): Promise<void>
}
/** Serve one deterministic DeepSeek-compatible response while retaining its request body. */
async function deepseekDefaultsServer(): Promise<DeepSeekDefaultsServer> {
const requests: JsonObject[] = []
const server = createServer((request: IncomingMessage, response: ServerResponse) => {
let body = ''
request.setEncoding('utf8')
request.on('data', (chunk: string) => { body += chunk })
request.on('end', () => {
requests.push(JSON.parse(body) as JsonObject)
response.writeHead(200, { 'content-type': 'text/event-stream' })
response.end([
'data: {"choices":[{"delta":{"content":"DEFAULTS_OK"}}]}',
'data: {"choices":[{"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":3,"completion_tokens":1}}',
'data: [DONE]',
'',
].join('\n\n'))
})
})
await new Promise<void>(resolve => server.listen(0, '127.0.0.1', resolve))
const address = server.address()
if (address === null || typeof address === 'string') throw new Error('DeepSeek defaults snapshot server has no port')
return {
url: `http://127.0.0.1:${address.port}`,
requests,
close: () => new Promise(resolve => server.close(() => { resolve() })),
}
}
function parseJsonl(content: string): JsonObject[] {
return content.split('\n')
.filter(line => line.trim().length > 0)
@@ -208,6 +245,53 @@ describe('headless stream-json snapshots', () => {
`)
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('logs and sends the DeepSeek adapter maxTokens default through the one-shot app', async () => {
const server = await deepseekDefaultsServer()
try {
const result = await runLoaderSmoke({
label: 'DeepSeek adapter defaults headless stream-json snapshot',
tempDirPrefix: 'headless-snapshot-deepseek-defaults-',
binScript,
configPath: deepseekDefaultsConfigPath,
binArgs: [
'--config',
deepseekDefaultsConfigPath,
'--output-format',
'stream-json',
'return the deterministic response',
],
tsconfigPath,
env: {
DSH_SNAPSHOT_BASE_URL: server.url,
NODE_OPTIONS: [process.env.NODE_OPTIONS, '--disable-warning=ExperimentalWarning'].filter(Boolean).join(' '),
},
})
expect(result.stderr).toBe('')
expect(server.requests).toHaveLength(1)
expect(server.requests[0]?.max_tokens).toBe(256_000)
const config = parseJsonl(result.stdout)
.map(record => record.event)
.find((event): event is JsonObject => (
event !== null
&& typeof event === 'object'
&& !Array.isArray(event)
&& 'type' in event
&& event.type === 'request/header'
))?.data as JsonObject | undefined
expect((config?.header as JsonObject | undefined)?.config).toMatchInlineSnapshot(`
{
"maxTokens": 256000,
"model": "deepseek-v4-flash",
"provider": "deepseek",
"reasoningEffort": "off",
}
`)
} finally {
await server.close()
}
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('replays the advanced toolchain through the one-shot app', async () => {
const prompt = await scenarioPrompt(advancedScenarioDir, 'advanced-toolchain')
const fixtureFiles = [

View File

@@ -7,8 +7,8 @@
maxTokensAsSuccess: !!js "process.env.DSH_MAX_TOKENS_AS_SUCCESS === undefined ? true : JSON.parse(process.env.DSH_MAX_TOKENS_AS_SUCCESS)"
# The DeepSeek adapter. Shipped default: full thinking at max effort on every
# request (wire-only defaults; they never enter the request header). The model
# arrives per session over JSON-RPC, so it is not pinned here.
# request; exact-model resolution materializes request defaults before logging.
# The model arrives per session over JSON-RPC, so it is not pinned here.
- id: llm-deepseek
name: '@deepseek-ai/dsh-llm-deepseek'
config: