fix(tools): forward nested image results in code mode

This commit is contained in:
Tianyi Cui
2026-08-11 15:34:13 +08:00
parent 219d2a1fb9
commit e00146be73
19 changed files with 117 additions and 42 deletions

View File

@@ -6,7 +6,7 @@
* @module @deepseek-ai/dsh-tools/src/code-mode
*/
import { CallId, HarnessError } from '@deepseek-ai/dsh-llm'
import { CallId, createUserMessage, HarnessError } from '@deepseek-ai/dsh-llm'
import type { ContentBlock } from '@deepseek-ai/dsh-llm'
import type { CodeBindingFunction, CodeRunResult, CodeRuntime } from '@deepseek-ai/dsh-code-runtime'
import { snapshotJsonValue } from '@deepseek-ai/dsh-session'
@@ -48,7 +48,7 @@ const TYPESCRIPT_FLAVOR: RunCodeFlavor = {
'Execute a TypeScript program against the available tools. Write the BODY of an '
+ 'async function (erasable syntax only; top-level `await` and `return` work) and '
+ 'call tools as `await tools.name(args)` per the declarations in the system prompt. '
+ 'Only what you print or return comes back — curate it.',
+ 'Only what you print or return is program output; image-bearing subtool results are attached after the run.',
codeDescription: 'The program: the body of an async TypeScript function.',
}
@@ -61,8 +61,9 @@ const PYTHON_FLAVOR: RunCodeFlavor = {
description:
'Execute a Python program against the available tools. Write the BODY of an '
+ 'async function (top-level `await` and `return` work) and call tools as '
+ '`await tools.name(args)` per the declarations in the system prompt. Answer '
+ 'with `print(...)` and/or `return <value>` — only that comes back, so curate it.',
+ '`await tools.name(args)` per the declarations in the system prompt. Use '
+ '`print(...)` and/or `return <value>` for program output; image-bearing '
+ 'subtool results attach after the run.',
codeDescription: 'The program: the body of an async Python function.',
}
@@ -557,6 +558,12 @@ export function createRunCodeTool(registry: ToolRegistry, options: RunCodeBridge
const result = parked.kind === 'post-result'
? await scheduler.finalize(parked.exec, parked.result)
: scheduler.finish(parked.exec, parked.result)
if (!result.isError && result.content.some(block => block.type === 'image')) {
exec.deferContext(createUserMessage({
content: result.content,
source: { kind: 'plugin', plugin: 'tools-code-mode' },
}))
}
for (const context of result.additionalContexts ?? []) {
exec.deferContext(context)
}

View File

@@ -738,7 +738,7 @@ Pass \`run_code\` the body of an async Python function (top-level \`await\` and
- Call tools as \`await tools.name(args)\` — subscript access for exotic, reserved, or underscore-leading names: \`await tools["my-tool"](args)\`. Every call resolves to the tool's typed canonical JSON value (each method's return type below). Tool arguments must be lossless JSON.
- A FAILED tool call raises \`ToolCallError\`, whose \`toolName\` identifies the failed tool and whose message is human-readable — wrap in \`try/except\` to handle and continue.
- Independent read-only calls MAY overlap under \`asyncio.gather\` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with \`await\`.
- Emit the run's answer with \`print(...)\` and/or a top-level \`return <value>\`; the returned value must be lossless JSON. ONLY what you print and the returned value come back — intermediate tool results never enter the conversation, so extract just what you need.
- Emit the run's answer with \`print(...)\` and/or a top-level \`return <value>\`; the returned value must be lossless JSON. Only what you print and return is program output. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need.
The available tools:`

View File

@@ -254,7 +254,7 @@ Pass \`run_code\` the body of an async TypeScript function (erasable syntax only
- Call tools as \`await tools.name(args)\` — quoted access for exotic names: \`tools["my-tool"](args)\`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON.
- A FAILED tool call rejects with \`ToolCallError\`, whose \`toolName\` identifies the failed tool and whose \`message\` is human-readable — \`try/catch\` it to handle and continue.
- Independent read-only calls MAY overlap under \`Promise.all\` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with \`await\`.
- Emit results with \`return\` and/or \`console.log(...)\`. ONLY what you print or return comes back to you — intermediate tool results never enter the conversation, so extract just what you need.
- Emit results with \`return\` and/or \`console.log(...)\`. Only what you print or return is program output. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need.
The available tools:`