Skip to content

Commit

Permalink
change act api output
Browse files Browse the repository at this point in the history
  • Loading branch information
Filip Michalsky committed Sep 26, 2024
1 parent 407178a commit fe6569f
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 21 deletions.
25 changes: 20 additions & 5 deletions examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,20 @@ async function example() {
debugDom: true,
});

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)
await stagehand.init({ modelName: "claude-3-5-sonnet-20240620" });
await stagehand.page.goto("https://www.nytimes.com/games/wordle/index.html");
await stagehand.act({ action: "start the game" });
await stagehand.act({ action: "close tutorial popup" });

const startGameResult = await stagehand.act({ action: "start the game" });
if (!startGameResult.success) {
console.error("Failed to start the game:", startGameResult.error);
return;
}

const closeTutorialResult = await stagehand.act({ action: "close tutorial popup" });
if (!closeTutorialResult.success) {
console.error("Failed to close tutorial:", closeTutorialResult.error);
// Decide whether to continue or return based on the importance of this action
}

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

await stagehand.page.locator("body").pressSequentially(response);
await stagehand.page.keyboard.press("Enter");
try {
await stagehand.page.locator("body").pressSequentially(response);
await stagehand.page.keyboard.press("Enter");
} catch (error) {
console.error("Failed to input guess:", error.message);
continue;
}

const guess = await stagehand.extract({
instruction: "extract the five letter guess at the bottom",
Expand Down
41 changes: 25 additions & 16 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ export class Stagehand {
}
}

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

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

await expect(firstLocator).toBeAttached();
try {
await expect(firstLocator).toBeAttached();
} catch (error) {
return { success: false, error: `Element found but not attached: ${error.message}` };
}

const observationId = await this.recordObservation(
observation,
locatorString
);

return observationId;
return { success: true, result: observationId };
}
async ask(question: string, modelName?: string): Promise<string | null> {
return ask({
Expand Down Expand Up @@ -344,7 +349,7 @@ export class Stagehand {
steps?: string;
chunksSeen?: Array<number>;
modelName?: string;
}): Promise<void> {
}): Promise<{ success: boolean; error?: string }> {
this.log({
category: "action",
message: `taking action: ${action}`,
Expand Down Expand Up @@ -390,7 +395,7 @@ export class Stagehand {
level: 1
});
this.recordAction(action, null);
return;
return { success: false, error: "Action could not be performed after checking all chunks" };
}
}

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

if (method === 'scrollIntoView') { // this is not a native playwright function
if (method === 'scrollIntoView') {
await locator.evaluate((element) => {
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
});
} else if (typeof locator[method as keyof typeof locator] === "function") {

const isLink = await locator.evaluate((element) => {
return element.tagName.toLowerCase() === 'a' && element.hasAttribute('href');
});

// Perform the action
//@ts-ignore playwright's TS does not think this is valid, but we proved it with the check above
await locator[method](...args);
try {
//@ts-ignore playwright's TS does not think this is valid, but we proved it with the check above
await locator[method](...args);
} catch (error) {
return { success: false, error: `Failed to perform ${method} on element: ${error.message}` };
}

// Check if a new page was created, but only if the method is 'click'
if (method === 'click') {
const isLink = await locator.evaluate((element) => {
return element.tagName.toLowerCase() === 'a' && element.hasAttribute('href');
});

if (isLink) {
// Create a promise that resolves when a new page is created
console.log("clicking link");
Expand All @@ -452,7 +459,7 @@ export class Stagehand {
}
}
} else {
throw new Error(`stagehand: chosen method ${method} is invalid`);
return { success: false, error: `Invalid method: ${method}` };
}

if (!response.completed) {
Expand All @@ -468,6 +475,8 @@ export class Stagehand {
modelName,
});
}

return { success: true };
}
setPage(page: Page) {
this.page = page;
Expand Down

0 comments on commit fe6569f

Please sign in to comment.