Open
Description
Description
Hi, I have a custom tool I wrote and when I'm trying to use it with generateText it return an empty text result.
my tool definition (assume I see the code is called and return result)
tool({
description: ' "Use this tool to fetch real-time web data for queries that require the latest information, niche topics, local context, or when the answer may have changed since the model’s last update. This includes news, recent events, evolving technologies, up-to-date documentation, or anything likely not captured in training data. The tool supports issuing queries to a search engine and browsing the returned links to extract relevant content. ALWAYS PASS engine parameter with \'google\'',
parameters: z.object({
query: z.string()
}),
// Function to execute the search
execute: async ({ query }) => {
try {
console.log(`[SERPAPI] Searching for: ${query}`);
// Format our query for SerpAPI directly
const params = new URLSearchParams({
engine: 'google',
api_key: apiKey,
q: query,
output: 'json'
});
// Make the request to SERP API
const response = await fetch(`https://serpapi.com/search?${params.toString()}`);
if (!response.ok) {
throw new Error(`SERP API error: ${response.status} ${response.statusText}`);
}
const data = await response.json();
console.log(`[SERPAPI] Received response with ${data.organic_results?.length || 0} results`);
// Format the results to match our expected schema defined in constants.js
const results = data.organic_results?.map(result => ({
url: result.link,
snippet: result.snippet || result.title // Fallback to title if snippet isn't available
})) || [];
// Return in the format our app expects
return JSON.stringify({ results });
} catch (error) {
// Enhanced error logging with full stack trace
console.error('[SERPAPI] Search error:');
console.error(error);
console.error(error.stack);
return JSON.stringify({ results: [] }); // Return empty results on error to match schema
}
}
})
And this is my call:
import { openai } from '@ai-sdk/openai';
import { generateText, Output } from 'ai';
import { z } from 'zod';
const searchResultsSchema = z.object({
results: z.array(z.object({
url: z.string(),
snippet: z.string()
}))
});
const response = await generateText({
model: openai.responses('gpt-4o-mini'),
prompt: `${prompt}
IMPORTANT: You must ALWAYS return this exact structure no matter what the tool returns.
NEVER return raw markdown or the original tool response or empty strings.`,
tools: {
serpapi_search: /* the tool definition above */
},
tool_choice: { type: 'tool', toolName: 'serpapi_search' },
experimental_output: Output.object({ schema: searchResultsSchema }),
debug: true,
maxToolRoundTrip: 2,
onStepFinish({ text, toolCalls, toolResults, finishReason, usage }) {
// Log the tool results
}
});
// Safely access results, handling null/undefined
let results = response.experimental_output?.results || [];
Adding the following parts work:
maxToolRoundTrip: 1,
maxSteps: 2,
...
experimental_prepareStep: async ({ model, stepNumber, maxSteps, steps }) => {
if (stepNumber > 0) {
return {
experimental_activeTools: [],
};
}
},
Is there a better way to control how many times a tool is called? what am I doing wrong?
AI SDK Version
- ai: 4.3.16
- @ai-sdk/openai: 1.3.22