diff --git a/packages/google/src/google-prepare-tools.test.ts b/packages/google/src/google-prepare-tools.test.ts index 0a5094196021..01876d5e2d35 100644 --- a/packages/google/src/google-prepare-tools.test.ts +++ b/packages/google/src/google-prepare-tools.test.ts @@ -472,3 +472,173 @@ it('should add warnings for google maps on unsupported models', () => { ] `); }); + +it('should throw error for invalid tool name starting with number', () => { + expect(() => + prepareTools({ + tools: [ + { + type: 'function', + name: '123invalid', + description: 'Invalid name', + inputSchema: { type: 'object', properties: {} }, + }, + ], + modelId: 'gemini-2.5-flash', + }), + ).toThrowError(/Invalid tool name/); +}); + +it('should accept valid tool name starting with underscore', () => { + const result = prepareTools({ + tools: [ + { + type: 'function', + name: '_validName', + description: 'Valid name starting with underscore', + inputSchema: { type: 'object', properties: {} }, + }, + ], + modelId: 'gemini-2.5-flash', + }); + expect(result.tools).toEqual([ + { + functionDeclarations: [ + { + name: '_validName', + description: 'Valid name starting with underscore', + parameters: undefined, + }, + ], + }, + ]); + expect(result.toolWarnings).toEqual([]); +}); + +it('should accept valid tool names with allowed special characters', () => { + const result = prepareTools({ + tools: [ + { + type: 'function', + name: 'tool.name', + description: 'Name with dot', + inputSchema: { type: 'object', properties: {} }, + }, + { + type: 'function', + name: 'tool:name', + description: 'Name with colon', + inputSchema: { type: 'object', properties: {} }, + }, + { + type: 'function', + name: 'tool-name', + description: 'Name with dash', + inputSchema: { type: 'object', properties: {} }, + }, + ], + modelId: 'gemini-2.5-flash', + }); + expect(result.tools).toEqual([ + { + functionDeclarations: [ + { + name: 'tool.name', + description: 'Name with dot', + parameters: undefined, + }, + { + name: 'tool:name', + description: 'Name with colon', + parameters: undefined, + }, + { + name: 'tool-name', + description: 'Name with dash', + parameters: undefined, + }, + ], + }, + ]); + expect(result.toolWarnings).toEqual([]); +}); + +it('should throw error for tool name starting with dash', () => { + expect(() => + prepareTools({ + tools: [ + { + type: 'function', + name: '-invalid', + description: 'Invalid name', + inputSchema: { type: 'object', properties: {} }, + }, + ], + modelId: 'gemini-2.5-flash', + }), + ).toThrowError(/Invalid tool name/); +}); + +it('should throw error for tool name starting with dot', () => { + expect(() => + prepareTools({ + tools: [ + { + type: 'function', + name: '.invalid', + description: 'Invalid name', + inputSchema: { type: 'object', properties: {} }, + }, + ], + modelId: 'gemini-2.5-flash', + }), + ).toThrowError(/Invalid tool name/); +}); + +it('should throw error for tool name starting with colon', () => { + expect(() => + prepareTools({ + tools: [ + { + type: 'function', + name: ':invalid', + description: 'Invalid name', + inputSchema: { type: 'object', properties: {} }, + }, + ], + modelId: 'gemini-2.5-flash', + }), + ).toThrowError(/Invalid tool name/); +}); + +it('should throw error for tool name with @ symbol', () => { + expect(() => + prepareTools({ + tools: [ + { + type: 'function', + name: 'tool@name', + description: 'Invalid name', + inputSchema: { type: 'object', properties: {} }, + }, + ], + modelId: 'gemini-2.5-flash', + }), + ).toThrowError(/Invalid tool name/); +}); + +it('should throw error for tool name with # symbol', () => { + expect(() => + prepareTools({ + tools: [ + { + type: 'function', + name: 'tool#name', + description: 'Invalid name', + inputSchema: { type: 'object', properties: {} }, + }, + ], + modelId: 'gemini-2.5-flash', + }), + ).toThrowError(/Invalid tool name/); +}); diff --git a/packages/google/src/google-prepare-tools.ts b/packages/google/src/google-prepare-tools.ts index a94f1006821e..1c7ffda02104 100644 --- a/packages/google/src/google-prepare-tools.ts +++ b/packages/google/src/google-prepare-tools.ts @@ -16,25 +16,25 @@ export function prepareTools({ modelId: GoogleGenerativeAIModelId; }): { tools: - | Array< - | { - functionDeclarations: Array<{ - name: string; - description: string; - parameters: unknown; - }>; - } - | Record - > - | undefined; - toolConfig: - | undefined + | Array< | { - functionCallingConfig: { - mode: 'AUTO' | 'NONE' | 'ANY'; - allowedFunctionNames?: string[]; - }; - }; + functionDeclarations: Array<{ + name: string; + description: string; + parameters: unknown; + }>; + } + | Record + > + | undefined; + toolConfig: + | undefined + | { + functionCallingConfig: { + mode: 'AUTO' | 'NONE' | 'ANY'; + allowedFunctionNames?: string[]; + }; + }; toolWarnings: SharedV3Warning[]; } { // when the tools array is empty, change it to undefined to prevent errors: @@ -199,6 +199,12 @@ export function prepareTools({ for (const tool of tools) { switch (tool.type) { case 'function': + if (!/^[a-zA-Z_][a-zA-Z0-9_.:-]*$/.test(tool.name)) { + throw new Error( + `Invalid tool name: ${tool.name}. Tool names must start with a letter or underscore and contain only alphanumeric characters, underscores, dots, colons, or dashes.`, + ); + } + functionDeclarations.push({ name: tool.name, description: tool.description ?? '',