async ask(options: EvaluateOptions): Promise<EvaluationResult> {
const {
question,
answer,
screenshot = true,
systemPrompt,
screenshotDelayMs = 250,
agentReasoning,
} = options;
// Validation
if (!question) throw new StagehandInvalidArgumentError("Question required");
if (!answer && !screenshot) throw new StagehandInvalidArgumentError("Need answer or screenshot");
// Handle multiple screenshots
if (Array.isArray(screenshot)) {
return this._evaluateWithMultipleScreenshots({
question,
screenshots: screenshot,
systemPrompt,
agentReasoning,
});
}
const defaultSystemPrompt = `You are an expert evaluator that returns YES or NO.
You have access to ${screenshot ? "a screenshot" : "the agent's reasoning"}.
Today's date is ${new Date().toLocaleDateString()}`;
// Capture screenshot if needed
await new Promise((r) => setTimeout(r, screenshotDelayMs));
let imageBuffer: Buffer | undefined;
if (screenshot) {
const page = await this.v3.context.awaitActivePage();
imageBuffer = await page.screenshot({ fullPage: false });
}
// Get LLM client and make request
const llmClient = this.getClient();
const response = await llmClient.createChatCompletion({
logger: this.silentLogger,
options: {
messages: [
{ role: "system", content: systemPrompt || defaultSystemPrompt },
{
role: "user",
content: [
{
type: "text",
text: agentReasoning
? `Question: ${question}\n\nAgent's reasoning:\n${agentReasoning}`
: question,
},
...(screenshot && imageBuffer ? [{
type: "image_url" as const,
image_url: {
url: `data:image/jpeg;base64,${imageBuffer.toString("base64")}`,
},
}] : []),
...(answer ? [{ type: "text" as const, text: `the answer is ${answer}` }] : []),
],
},
],
response_model: { name: "EvaluationResult", schema: EvaluationSchema },
},
});
const result = response.data as EvaluationResult;
return { evaluation: result.evaluation, reasoning: result.reasoning };
}