fix: separate instructions from API declaration to prevent API error

This commit is contained in:
Aishanee Shah
2026-02-17 14:14:27 +00:00
parent 0b2fcb84f4
commit 7550fc3d93
9 changed files with 1318 additions and 1094 deletions

View File

@@ -12,23 +12,38 @@ import type { ToolDefinition } from './types.js';
*
* @param definition The tool definition containing the base declaration and optional overrides.
* @param modelId Optional model identifier to apply specific overrides.
* @returns The FunctionDeclaration to be sent to the API.
* @returns An object containing the FunctionDeclaration for the API and optional instructions for the system prompt.
*/
export function resolveToolDeclaration(
definition: ToolDefinition,
modelId?: string,
): FunctionDeclaration & { instructions?: string } {
): { declaration: FunctionDeclaration; instructions?: string } {
const { instructions: baseInstructions, ...baseDeclaration } =
definition.base;
if (!modelId || !definition.overrides) {
return definition.base;
return {
declaration: baseDeclaration,
instructions: baseInstructions,
};
}
const override = definition.overrides(modelId);
if (!override) {
return definition.base;
return {
declaration: baseDeclaration,
instructions: baseInstructions,
};
}
const { instructions: overrideInstructions, ...overrideDeclaration } =
override;
return {
...definition.base,
...override,
declaration: {
...baseDeclaration,
...overrideDeclaration,
},
instructions: overrideInstructions ?? baseInstructions,
};
}

View File

@@ -914,19 +914,22 @@ export class EditTool
typeof config.getActiveModel === 'function'
? config.getActiveModel()
: undefined;
const resolved = resolveToolDeclaration(EDIT_DEFINITION, modelId);
const { declaration, instructions } = resolveToolDeclaration(
EDIT_DEFINITION,
modelId,
);
super(
EditTool.Name,
EDIT_DISPLAY_NAME,
resolved.description!,
declaration.description!,
Kind.Edit,
resolved.parametersJsonSchema,
declaration.parametersJsonSchema,
messageBus,
true, // isOutputMarkdown
false, // canUpdateOutput
undefined, // extensionName
undefined, // extensionId
resolved.instructions,
instructions,
);
}
@@ -970,7 +973,7 @@ export class EditTool
}
override getSchema(modelId?: string) {
return resolveToolDeclaration(EDIT_DEFINITION, modelId);
return resolveToolDeclaration(EDIT_DEFINITION, modelId).declaration;
}
getModifyContext(_: AbortSignal): ModifyContext<EditToolParams> {

View File

@@ -243,6 +243,6 @@ export class ReadFileTool extends BaseDeclarativeTool<
}
override getSchema(modelId?: string) {
return resolveToolDeclaration(READ_FILE_DEFINITION, modelId);
return resolveToolDeclaration(READ_FILE_DEFINITION, modelId).declaration;
}
}

View File

@@ -493,6 +493,7 @@ export class ReadManyFilesTool extends BaseDeclarativeTool<
}
override getSchema(modelId?: string) {
return resolveToolDeclaration(READ_MANY_FILES_DEFINITION, modelId);
return resolveToolDeclaration(READ_MANY_FILES_DEFINITION, modelId)
.declaration;
}
}

View File

@@ -474,19 +474,22 @@ export class ShellTool extends BaseDeclarativeTool<
config.getEnableInteractiveShell(),
config.getEnableShellOutputEfficiency(),
);
const resolved = resolveToolDeclaration(definition, modelId);
const { declaration, instructions } = resolveToolDeclaration(
definition,
modelId,
);
super(
ShellTool.Name,
'Shell',
resolved.description!,
declaration.description!,
Kind.Execute,
resolved.parametersJsonSchema,
declaration.parametersJsonSchema,
messageBus,
false, // output is not markdown
true, // output can be updated
undefined, // extensionName
undefined, // extensionId
resolved.instructions,
instructions,
);
}
@@ -527,6 +530,6 @@ export class ShellTool extends BaseDeclarativeTool<
this.config.getEnableInteractiveShell(),
this.config.getEnableShellOutputEfficiency(),
);
return resolveToolDeclaration(definition, modelId);
return resolveToolDeclaration(definition, modelId).declaration;
}
}

View File

@@ -7,9 +7,10 @@
import { describe, expect, it } from 'vitest';
import { WriteTodosTool, type WriteTodosToolParams } from './write-todos.js';
import { createMockMessageBus } from '../test-utils/mock-message-bus.js';
import { makeFakeConfig } from '../test-utils/config.js';
describe('WriteTodosTool', () => {
const tool = new WriteTodosTool(createMockMessageBus());
const tool = new WriteTodosTool(makeFakeConfig(), createMockMessageBus());
const signal = new AbortController().signal;
describe('validation', () => {

View File

@@ -90,24 +90,33 @@ export class WriteTodosTool extends BaseDeclarativeTool<
typeof config.getActiveModel === 'function'
? config.getActiveModel()
: undefined;
const resolved = resolveToolDeclaration(WRITE_TODOS_DEFINITION, modelId);
const { declaration, instructions } = resolveToolDeclaration(
WRITE_TODOS_DEFINITION,
modelId,
);
super(
WriteTodosTool.Name,
'WriteTodos',
resolved.description!,
declaration.description!,
Kind.Other,
resolved.parametersJsonSchema,
declaration.parametersJsonSchema,
messageBus,
true, // isOutputMarkdown
false, // canUpdateOutput
undefined, // extensionName
undefined, // extensionId
resolved.instructions,
instructions,
);
}
override getSchema(modelId?: string) {
return resolveToolDeclaration(WRITE_TODOS_DEFINITION, modelId);
const activeModel =
modelId ??
(typeof this.config.getActiveModel === 'function'
? this.config.getActiveModel()
: undefined);
return resolveToolDeclaration(WRITE_TODOS_DEFINITION, activeModel)
.declaration;
}
protected override validateToolParamValues(