mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-13 19:20:04 +00:00
🐛 fix(model-runtime): align tool-calling fallback tests & surface missing tool call as error (#15691)
* ✅ test(model-runtime): align tool-calling fallback tests with new return shape #15680 changed generateObject's tool-calling fallback to return the parsed schema object (same shape as the json_schema path) instead of an array of tool calls, and reworked its error handling, but left the pre-existing "tool calling fallback" block in index.test.ts asserting the old behavior, breaking CI on canary: - result is now the parsed object, not [{ name, arguments }] - the no-tool-call path returns undefined via debug log without console.error - the parse-failure path logs the single matched tool call, not the array Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * 🐛 fix(model-runtime): surface missing tool call in generateObject fallback as error tool_choice forces the structured-output function, so a response without a tool call means the provider misbehaved. #15680 routed this branch to a debug-namespace log that is invisible in production, leaving callers with an unexplained undefined. Log it via console.error with the response message as context, matching the parse-failure branch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3173,9 +3173,9 @@ describe('LobeOpenAICompatibleFactory', () => {
|
||||
{ headers: undefined, signal: undefined },
|
||||
);
|
||||
|
||||
expect(result).toEqual([
|
||||
{ arguments: { age: 28, name: 'Alice' }, name: 'person_extractor' },
|
||||
]);
|
||||
// The fallback returns the parsed schema object, same shape as the
|
||||
// json_schema path
|
||||
expect(result).toEqual({ age: 28, name: 'Alice' });
|
||||
});
|
||||
|
||||
it('should not forward internal thinking to generic OpenAI-compatible generateObject requests', async () => {
|
||||
@@ -3255,7 +3255,10 @@ describe('LobeOpenAICompatibleFactory', () => {
|
||||
|
||||
const result = await instanceWithToolCalling.generateObject(payload);
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalledWith('parse tool call arguments error:', undefined);
|
||||
expect(consoleSpy).toHaveBeenCalledWith(
|
||||
'no tool call found in structured output response:',
|
||||
mockResponse.choices[0].message,
|
||||
);
|
||||
expect(result).toBeUndefined();
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
@@ -3298,7 +3301,7 @@ describe('LobeOpenAICompatibleFactory', () => {
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalledWith(
|
||||
'parse tool call arguments error:',
|
||||
mockResponse.choices[0].message.tool_calls,
|
||||
mockResponse.choices[0].message.tool_calls[0],
|
||||
);
|
||||
expect(result).toBeUndefined();
|
||||
|
||||
@@ -3350,7 +3353,7 @@ describe('LobeOpenAICompatibleFactory', () => {
|
||||
{ headers: options.headers, signal: options.signal },
|
||||
);
|
||||
|
||||
expect(result).toEqual([{ arguments: { data: 'test' }, name: 'data_extractor' }]);
|
||||
expect(result).toEqual({ data: 'test' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -898,7 +898,9 @@ export const createOpenAICompatibleRuntime = <T extends Record<string, any> = an
|
||||
toolCalls?.find((item) => item.function?.name === tool.function.name) ?? toolCalls?.[0];
|
||||
|
||||
if (!toolCall?.function) {
|
||||
log('no tool call found in structured output response');
|
||||
// tool_choice forces this function, so a missing tool call means the
|
||||
// provider misbehaved — surface it instead of silently returning undefined
|
||||
console.error('no tool call found in structured output response:', res.choices[0]?.message);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user