-
Notifications
You must be signed in to change notification settings - Fork 120
feat(ai-sdk-client): add tool support error detection and retry mechanism #259
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
will-lamerton
merged 4 commits into
Nano-Collective:main
from
Githubguy132010:Fix-issue-#252
Jan 15, 2026
+654
−8
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
e2555c2
feat(ai-sdk-client): add tool support error detection and retry mecha…
Githubguy132010 3d26501
test(ai-sdk-client): add comprehensive test suite for tool error dete…
Githubguy132010 aae2039
Document DisableToolModels tool in README
Githubguy132010 3c59ddc
Merge branch 'main' into Fix-issue-#252
will-lamerton File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
225 changes: 225 additions & 0 deletions
225
source/ai-sdk-client/error-handling/tool-error-detector.spec.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,225 @@ | ||
| import test from 'ava'; | ||
| import {isToolSupportError} from './tool-error-detector.js'; | ||
|
|
||
| test('isToolSupportError returns false for non-Error values', t => { | ||
| t.false(isToolSupportError(null)); | ||
| t.false(isToolSupportError(undefined)); | ||
| t.false(isToolSupportError('string error')); | ||
| t.false(isToolSupportError(123)); | ||
| t.false(isToolSupportError({})); | ||
| }); | ||
|
|
||
| test('isToolSupportError returns false for generic errors without tool patterns', t => { | ||
| t.false(isToolSupportError(new Error('Something went wrong'))); | ||
| t.false(isToolSupportError(new Error('Network error'))); | ||
| t.false(isToolSupportError(new Error('Rate limit exceeded'))); | ||
| }); | ||
|
|
||
| test('isToolSupportError detects 400 Bad Request with tool keyword', t => { | ||
| t.true( | ||
| isToolSupportError( | ||
| new Error('400 Bad Request: invalid parameter "tools"'), | ||
| ), | ||
| ); | ||
| t.true( | ||
| isToolSupportError( | ||
| new Error('400 bad request: unexpected field "tools"'), | ||
| ), | ||
| ); | ||
| }); | ||
|
|
||
| test('isToolSupportError detects 400 Bad Request with function keyword', t => { | ||
| t.true( | ||
| isToolSupportError( | ||
| new Error('400 Bad Request: invalid parameter "functions"'), | ||
| ), | ||
| ); | ||
| t.true( | ||
| isToolSupportError( | ||
| new Error('400 bad request: unrecognized field "function_calls"'), | ||
| ), | ||
| ); | ||
| }); | ||
|
|
||
| test('isToolSupportError detects 400 Bad Request with invalid parameter', t => { | ||
| t.true( | ||
| isToolSupportError( | ||
| new Error('400 Bad Request: invalid parameter found'), | ||
| ), | ||
| ); | ||
| }); | ||
|
|
||
| test('isToolSupportError detects 400 Bad Request with unexpected field', t => { | ||
| t.true( | ||
| isToolSupportError( | ||
| new Error('400 bad request: unexpected field in request'), | ||
| ), | ||
| ); | ||
| }); | ||
|
|
||
| test('isToolSupportError detects 400 Bad Request with unrecognized keyword', t => { | ||
| t.true( | ||
| isToolSupportError( | ||
| new Error('400 Bad Request: unrecognized parameter'), | ||
| ), | ||
| ); | ||
| }); | ||
|
|
||
| test('isToolSupportError returns false for 400 without tool-related patterns', t => { | ||
| t.false( | ||
| isToolSupportError( | ||
| new Error('400 Bad Request: invalid input format'), | ||
| ), | ||
| ); | ||
| t.false(isToolSupportError(new Error('400 bad request: malformed JSON'))); | ||
| }); | ||
|
|
||
| test('isToolSupportError detects direct tool not supported messages', t => { | ||
| t.true(isToolSupportError(new Error('tool not supported'))); | ||
| t.true(isToolSupportError(new Error('Tool not supported by model'))); | ||
| t.true(isToolSupportError(new Error('This tool is not supported'))); | ||
| }); | ||
|
|
||
| test('isToolSupportError detects function not supported messages', t => { | ||
| t.true(isToolSupportError(new Error('function not supported'))); | ||
| t.true(isToolSupportError(new Error('Function calling not supported'))); | ||
| t.true(isToolSupportError(new Error('This function is not supported'))); | ||
| }); | ||
|
|
||
| test('isToolSupportError detects tool unsupported messages', t => { | ||
| t.true(isToolSupportError(new Error('tool unsupported'))); | ||
| t.true(isToolSupportError(new Error('Tools are unsupported'))); | ||
| }); | ||
|
|
||
| test('isToolSupportError detects function unsupported messages', t => { | ||
| t.true(isToolSupportError(new Error('function unsupported'))); | ||
| t.true(isToolSupportError(new Error('Function calling unsupported'))); | ||
| }); | ||
|
|
||
| test('isToolSupportError detects invalid tool messages', t => { | ||
| t.true(isToolSupportError(new Error('invalid tool'))); | ||
| t.true(isToolSupportError(new Error('Invalid tool definition'))); | ||
| }); | ||
|
|
||
| test('isToolSupportError detects invalid function messages', t => { | ||
| t.true(isToolSupportError(new Error('invalid function'))); | ||
| t.true(isToolSupportError(new Error('Invalid function definition'))); | ||
| }); | ||
|
|
||
| test('isToolSupportError detects tool parameter invalid messages', t => { | ||
| t.true( | ||
| isToolSupportError(new Error('tool parameter invalid')), | ||
| ); | ||
| t.true( | ||
| isToolSupportError(new Error('Invalid tool parameters provided')), | ||
| ); | ||
| }); | ||
|
|
||
| test('isToolSupportError detects function parameter invalid messages', t => { | ||
| t.true( | ||
| isToolSupportError(new Error('function parameter invalid')), | ||
| ); | ||
| t.true( | ||
| isToolSupportError( | ||
| new Error('Invalid function parameters in request'), | ||
| ), | ||
| ); | ||
| }); | ||
|
|
||
| test('isToolSupportError is case-insensitive for direct patterns', t => { | ||
| t.true(isToolSupportError(new Error('TOOL NOT SUPPORTED'))); | ||
| t.true(isToolSupportError(new Error('Tool Not Supported'))); | ||
| t.true(isToolSupportError(new Error('tool NOT supported'))); | ||
| }); | ||
|
|
||
| test('isToolSupportError detects Ollama-specific error with invalid character', t => { | ||
| t.true( | ||
| isToolSupportError( | ||
| new Error( | ||
| 'invalid character after top-level value', | ||
| ), | ||
| ), | ||
| ); | ||
| t.true( | ||
| isToolSupportError( | ||
| new Error( | ||
| 'invalid character after top-level value: unmarshal error', | ||
| ), | ||
| ), | ||
| ); | ||
| }); | ||
|
|
||
| test('isToolSupportError requires both invalid character and top-level value patterns', t => { | ||
| t.false( | ||
| isToolSupportError(new Error('invalid character in string')), | ||
| ); | ||
| t.false( | ||
| isToolSupportError(new Error('after top-level value')), | ||
| ); | ||
| }); | ||
|
|
||
| test('isToolSupportError detects mixed provider error messages', t => { | ||
| // OpenAI-style | ||
| t.true( | ||
| isToolSupportError( | ||
| new Error('400 Bad Request: invalid parameter tools'), | ||
| ), | ||
| ); | ||
|
|
||
| // Anthropic-style | ||
| t.true( | ||
| isToolSupportError(new Error('function calling not supported')), | ||
| ); | ||
|
|
||
| // Ollama-style | ||
| t.true( | ||
| isToolSupportError( | ||
| new Error( | ||
| 'invalid character after top-level value: unmarshal error', | ||
| ), | ||
| ), | ||
| ); | ||
|
|
||
| // Generic | ||
| t.true( | ||
| isToolSupportError(new Error('tool parameter invalid')), | ||
| ); | ||
| }); | ||
|
|
||
| test('isToolSupportError returns false for other error types', t => { | ||
| t.false( | ||
| isToolSupportError(new Error('404 Not Found: model not available')), | ||
| ); | ||
| t.false( | ||
| isToolSupportError( | ||
| new Error('401 Unauthorized: invalid API key'), | ||
| ), | ||
| ); | ||
| t.false( | ||
| isToolSupportError(new Error('500 Internal Server Error')), | ||
| ); | ||
| t.false( | ||
| isToolSupportError(new Error('Timeout: request took too long')), | ||
| ); | ||
| t.false( | ||
| isToolSupportError( | ||
| new Error('Connection refused: cannot reach server'), | ||
| ), | ||
| ); | ||
| }); | ||
|
|
||
| test('isToolSupportError handles Error subclasses', t => { | ||
| class CustomError extends Error { | ||
| constructor(message: string) { | ||
| super(message); | ||
| this.name = 'CustomError'; | ||
| } | ||
| } | ||
|
|
||
| t.true( | ||
| isToolSupportError(new CustomError('tool not supported')), | ||
| ); | ||
| t.false( | ||
| isToolSupportError(new CustomError('some other error')), | ||
| ); | ||
| }); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.