perf: name the run arguments to the model only when the user changed them

This commit is contained in:
AlexRV12
2026-09-03 16:30:09 +02:00
parent 0717870d9f
commit d8cff1bfb0
2 changed files with 33 additions and 5 deletions
@@ -4868,6 +4868,27 @@ describe('global AI tools', () => {
expect(result).toContain('Ran with arguments: {"name":"Grace"}')
})
// The arguments are already in the call this result answers, and every way the form's
// own differ from the proposed ones has its own clause — so an untouched form has
// nothing to name, and naming it anyway pays for the copy on every later iteration.
it('run_script names the arguments only when the user changed them', async () => {
vi.mocked(ScriptService.getScriptByPath).mockResolvedValueOnce({
path: 'f/scripts/greet',
schema: { properties: { name: { type: 'string' } } }
} as any)
const result = await withCompletedTestJob(() =>
callGlobalTool(
'run_script',
{ path: 'f/scripts/greet', args: { name: 'Ada' } },
{ ...toolCallbacks, requestRunArgs: async (_toolId, form) => form.args }
)
)
expect(result).not.toContain('Ran with arguments')
expect(result).toContain('unedited')
})
it('test_run_step lists nested step ids when a step is not found', async () => {
await callGlobalTool('write_flow', {
path: 'f/flows/nested-step-error',
@@ -20,6 +20,7 @@ import {
WebsocketTriggerService
} from '$lib/gen'
import { createTwoFilesPatch } from 'diff'
import { deepEqual } from 'fast-equals'
import type { ArtifactVersionTarget } from '$lib/components/sessions/previewRouter'
import { $ScriptLang } from '$lib/gen/schemas.gen'
import type {
@@ -3677,7 +3678,8 @@ export const globalTools: Tool<{}>[] = [
return runDeployedScript(parsed, ctx)
},
// No requiresConfirmation: the argument form is the confirmation, and unlike a
// yes/no card it must not be auto-accepted away by YOLO.
// yes/no card it must not be auto-accepted away by YOLO. One thing does run
// before Run — see the note on the form's SchemaForm.
streamingLabel: 'Preparing the run form...',
queuedLabel: (args) => `Run ${args?.path ?? 'a script'}`,
showDetails: true,
@@ -5567,14 +5569,19 @@ async function runDeployedScript(
: ''
// Redacted: a variable path is enough to run a job on a value the model cannot read,
// and one shown a path proposes it back on the next call.
const submittedJson = JSON.stringify(
redactFileArgs(redactSecretArgs(submitted, schema as any), schema as any)
)
const redacted = redactFileArgs(redactSecretArgs(submitted, schema as any), schema as any)
const submittedJson = JSON.stringify(redacted)
const shown =
submittedJson.length > MAX_SUBMITTED_ARGS_LENGTH
? submittedJson.slice(0, MAX_SUBMITTED_ARGS_LENGTH) + '... (truncated)'
: submittedJson
return `Ran with arguments: ${shown}${dropped}${unshowable}${reset}${stripped}\n${outcome}`
// Naming them costs a copy of arguments already in the call above, and the clauses
// below cover every way the form's own were not the proposed ones — so only what the
// user changed is news, and an untouched form is worth saying in a line.
const ran = deepEqual(redacted, proposed)
? 'Ran with the arguments the form opened with, unedited.'
: `Ran with arguments: ${shown}`
return `${ran}${dropped}${unshowable}${reset}${stripped}\n${outcome}`
}
async function testRunFlowByPath(