From d088a462bb46a1ee986603eac87e4118fbc9601d Mon Sep 17 00:00:00 2001 From: Miguel <36487034+miguelg719@users.noreply.github.com> Date: Fri, 31 Jan 2025 17:35:40 -0800 Subject: [PATCH] example to showcase how observe can be combined with sensitive data (#447) * example to showcase how observe can be combined with sensitive data * chalk and comments * chalk dev dependency --- examples/form_filling_sensible.ts | 91 +++++++++++++++++++++++++++++++ package-lock.json | 65 +++++++++++++--------- package.json | 1 + 3 files changed, 131 insertions(+), 26 deletions(-) create mode 100644 examples/form_filling_sensible.ts diff --git a/examples/form_filling_sensible.ts b/examples/form_filling_sensible.ts new file mode 100644 index 00000000..e2de10dd --- /dev/null +++ b/examples/form_filling_sensible.ts @@ -0,0 +1,91 @@ +import { Stagehand } from "@/dist"; +import StagehandConfig from "@/stagehand.config"; +import chalk from "chalk"; + +async function formFillingSensible() { + const stagehand = new Stagehand({ + ...StagehandConfig, + // Uncomment the following lines to run locally or use a different model + env: "LOCAL", + modelName: "gpt-4o-mini", + }); + await stagehand.init(); + + // Block manifest worker to prevent PWA installation popup. + // This is necessary because the website prompts the user to install the PWA and prevents form filling. + await stagehand.page.route("**/manifest.json", (route) => route.abort()); + + // Go to the website and wait for it to load + await stagehand.page.goto("https://file.1040.com/estimate/", { + waitUntil: "networkidle", + timeout: 30000, + }); + + // Observe the form fields with suggested actions + const observed = await stagehand.page.observe({ + instruction: + "fill all the form fields in the page with mock data. In the description inlcude the field name", + returnAction: true, + }); + + // Uncomment the following snippet to see the stagehand candidate suggestions (initial) + console.log( + `${chalk.green("Observe:")} Form fields found:\n${observed + .map((r) => `${chalk.yellow(r.description)} -> ${chalk.gray(r.selector)}`) + .join("\n")}`, + ); + + // Create a mapping of 1+ keywords in the form fields to standardize field names + const mapping = (description: string): string | null => { + const keywords: { [key: string]: string[] } = { + age: ["old"], + dependentsUnder17: ["under age 17", "child", "minor"], + dependents17to23: ["17-23", "school", "student"], + wages: ["wages", "W-2 Box 1"], + federalTax: ["federal tax", "Box 2"], + stateTax: ["state tax", "Box 17"], + }; + + for (const [key, terms] of Object.entries(keywords)) { + if (terms.some((term) => description.toLowerCase().includes(term))) { + return key; + } + } + return null; + }; + + // Fill the form fields with sensible data. This data will only be used in your session and not be shared with LLM providers/external APIs. + const userInputs: { [key: string]: string } = { + age: "26", + dependentsUnder17: "1", + wages: "54321", + federalTax: "8345", + stateTax: "2222", + }; + + const updatedFields = observed.map((candidate) => { + const key = mapping(candidate.description); + if (key && userInputs[key]) { + candidate.arguments = [userInputs[key]]; + } + return candidate; + }); + // List of sensible-data candidates + console.log( + `\n${chalk.green("Sensible Data form inputs:")} Form fields to be filled:\n${updatedFields + .map( + (r) => + `${chalk.yellow(r.description)} -> ${chalk.blue(r.arguments?.[0] || "no value")}`, + ) + .join("\n")}`, + ); + + // Fill all the form fields with the sensible candidates + for (const candidate of updatedFields) { + await stagehand.page.act(candidate); + } +} + +(async () => { + await formFillingSensible(); +})(); diff --git a/package-lock.json b/package-lock.json index 015c1d5e..73b17c9a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@browserbasehq/stagehand", - "version": "1.10.1", + "version": "1.11.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@browserbasehq/stagehand", - "version": "1.10.1", + "version": "1.11.0", "license": "MIT", "dependencies": { "@anthropic-ai/sdk": "^0.27.3", @@ -29,6 +29,7 @@ "ai": "^4.0.26", "autoevals": "^0.0.64", "braintrust": "^0.0.171", + "chalk": "^5.4.1", "cheerio": "^1.0.0", "chromium-bidi": "^0.10.0", "esbuild": "^0.21.4", @@ -2782,6 +2783,22 @@ "node": ">=18" } }, + "node_modules/braintrust/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/braintrust/node_modules/esbuild": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", @@ -2919,17 +2936,12 @@ } }, "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" @@ -3785,6 +3797,22 @@ "concat-map": "0.0.1" } }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/eslint/node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -4506,7 +4534,6 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -4901,19 +4928,6 @@ "node": "^18.0.0 || >=20.0.0" } }, - "node_modules/jsondiffpatch/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -6638,7 +6652,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, diff --git a/package.json b/package.json index 856950b9..5691305a 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "ai": "^4.0.26", "autoevals": "^0.0.64", "braintrust": "^0.0.171", + "chalk": "^5.4.1", "cheerio": "^1.0.0", "chromium-bidi": "^0.10.0", "esbuild": "^0.21.4",