Skip to content

Commit fe6569f

Browse files
author
Filip Michalsky
committed
change act api output
1 parent 407178a commit fe6569f

File tree

2 files changed

+45
-21
lines changed

2 files changed

+45
-21
lines changed

examples/index.ts

+20-5
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,20 @@ async function example() {
99
debugDom: true,
1010
});
1111

12-
await stagehand.init({ modelName: "claude-3-5-sonnet-20240620" }); // optionally specify model_name, defaults to "gpt-4o" (as of sept 18, 2024, we need to specify the model name with date, changing on 10/2/2024)
12+
await stagehand.init({ modelName: "claude-3-5-sonnet-20240620" });
1313
await stagehand.page.goto("https://www.nytimes.com/games/wordle/index.html");
14-
await stagehand.act({ action: "start the game" });
15-
await stagehand.act({ action: "close tutorial popup" });
14+
15+
const startGameResult = await stagehand.act({ action: "start the game" });
16+
if (!startGameResult.success) {
17+
console.error("Failed to start the game:", startGameResult.error);
18+
return;
19+
}
20+
21+
const closeTutorialResult = await stagehand.act({ action: "close tutorial popup" });
22+
if (!closeTutorialResult.success) {
23+
console.error("Failed to close tutorial:", closeTutorialResult.error);
24+
// Decide whether to continue or return based on the importance of this action
25+
}
1626

1727
let guesses: { guess: string | null; description: string | null }[] = [];
1828
for (let i = 0; i < 6; i++) {
@@ -22,8 +32,13 @@ async function example() {
2232
throw new Error("no response when asking for a guess");
2333
}
2434

25-
await stagehand.page.locator("body").pressSequentially(response);
26-
await stagehand.page.keyboard.press("Enter");
35+
try {
36+
await stagehand.page.locator("body").pressSequentially(response);
37+
await stagehand.page.keyboard.press("Enter");
38+
} catch (error) {
39+
console.error("Failed to input guess:", error.message);
40+
continue;
41+
}
2742

2843
const guess = await stagehand.extract({
2944
instruction: "extract the five letter guess at the bottom",

lib/index.ts

+25-16
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ export class Stagehand {
251251
}
252252
}
253253

254-
async observe(observation: string, modelName?: string): Promise<string | null> {
254+
async observe(observation: string, modelName?: string): Promise<{ success: boolean; result?: string; error?: string }> {
255255
this.log({
256256
category: "observation",
257257
message: `starting observation: ${observation}`,
@@ -278,7 +278,7 @@ export class Stagehand {
278278
message: `no element found for ${observation}`,
279279
level: 1
280280
});
281-
return null;
281+
return { success: false, error: `No element found for observation: ${observation}` };
282282
}
283283

284284
this.log({
@@ -299,13 +299,18 @@ export class Stagehand {
299299
// the locator string found by the LLM might resolve to multiple places in the DOM
300300
const firstLocator = this.page.locator(locatorString).first();
301301

302-
await expect(firstLocator).toBeAttached();
302+
try {
303+
await expect(firstLocator).toBeAttached();
304+
} catch (error) {
305+
return { success: false, error: `Element found but not attached: ${error.message}` };
306+
}
307+
303308
const observationId = await this.recordObservation(
304309
observation,
305310
locatorString
306311
);
307312

308-
return observationId;
313+
return { success: true, result: observationId };
309314
}
310315
async ask(question: string, modelName?: string): Promise<string | null> {
311316
return ask({
@@ -344,7 +349,7 @@ export class Stagehand {
344349
steps?: string;
345350
chunksSeen?: Array<number>;
346351
modelName?: string;
347-
}): Promise<void> {
352+
}): Promise<{ success: boolean; error?: string }> {
348353
this.log({
349354
category: "action",
350355
message: `taking action: ${action}`,
@@ -390,7 +395,7 @@ export class Stagehand {
390395
level: 1
391396
});
392397
this.recordAction(action, null);
393-
return;
398+
return { success: false, error: "Action could not be performed after checking all chunks" };
394399
}
395400
}
396401

@@ -416,22 +421,24 @@ export class Stagehand {
416421
});
417422
const locator = await this.page.locator(`xpath=${path}`).first();
418423

419-
if (method === 'scrollIntoView') { // this is not a native playwright function
424+
if (method === 'scrollIntoView') {
420425
await locator.evaluate((element) => {
421426
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
422427
});
423428
} else if (typeof locator[method as keyof typeof locator] === "function") {
424-
425-
const isLink = await locator.evaluate((element) => {
426-
return element.tagName.toLowerCase() === 'a' && element.hasAttribute('href');
427-
});
428-
429-
// Perform the action
430-
//@ts-ignore playwright's TS does not think this is valid, but we proved it with the check above
431-
await locator[method](...args);
429+
try {
430+
//@ts-ignore playwright's TS does not think this is valid, but we proved it with the check above
431+
await locator[method](...args);
432+
} catch (error) {
433+
return { success: false, error: `Failed to perform ${method} on element: ${error.message}` };
434+
}
432435

433436
// Check if a new page was created, but only if the method is 'click'
434437
if (method === 'click') {
438+
const isLink = await locator.evaluate((element) => {
439+
return element.tagName.toLowerCase() === 'a' && element.hasAttribute('href');
440+
});
441+
435442
if (isLink) {
436443
// Create a promise that resolves when a new page is created
437444
console.log("clicking link");
@@ -452,7 +459,7 @@ export class Stagehand {
452459
}
453460
}
454461
} else {
455-
throw new Error(`stagehand: chosen method ${method} is invalid`);
462+
return { success: false, error: `Invalid method: ${method}` };
456463
}
457464

458465
if (!response.completed) {
@@ -468,6 +475,8 @@ export class Stagehand {
468475
modelName,
469476
});
470477
}
478+
479+
return { success: true };
471480
}
472481
setPage(page: Page) {
473482
this.page = page;

0 commit comments

Comments
 (0)