@browserbasehq/stagehand 1.0.3 → 1.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -5
- package/dist/evals/index.eval.js +1075 -0
- package/dist/evals/index.eval.js.map +1 -0
- package/dist/evals/playground.js +112 -0
- package/dist/evals/playground.js.map +1 -0
- package/dist/evals/utils.js +52 -0
- package/dist/evals/utils.js.map +1 -0
- package/dist/examples/2048.js +108 -0
- package/dist/examples/2048.js.map +1 -0
- package/dist/examples/debugUrl.js +35 -0
- package/dist/examples/debugUrl.js.map +1 -0
- package/dist/examples/example.js +37 -0
- package/dist/examples/example.js.map +1 -0
- package/dist/index.d.ts +22 -6
- package/dist/index.js +629 -152
- package/dist/lib/browserbase.js +56 -0
- package/dist/lib/browserbase.js.map +1 -0
- package/dist/lib/cache.js +78 -0
- package/dist/lib/cache.js.map +1 -0
- package/dist/lib/dom/debug.js +119 -0
- package/dist/lib/dom/debug.js.map +1 -0
- package/dist/lib/dom/index.js +20 -0
- package/dist/lib/dom/index.js.map +1 -0
- package/dist/lib/dom/process.js +396 -0
- package/dist/lib/dom/process.js.map +1 -0
- package/dist/lib/dom/utils.js +28 -0
- package/dist/lib/dom/utils.js.map +1 -0
- package/dist/lib/index.js +978 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/inference.js +226 -0
- package/dist/lib/inference.js.map +1 -0
- package/dist/lib/llm/AnthropicClient.js +150 -0
- package/dist/lib/llm/AnthropicClient.js.map +1 -0
- package/dist/lib/llm/LLMClient.js +12 -0
- package/dist/lib/llm/LLMClient.js.map +1 -0
- package/dist/lib/llm/LLMProvider.js +34 -0
- package/dist/lib/llm/LLMProvider.js.map +1 -0
- package/dist/lib/llm/OpenAIClient.js +69 -0
- package/dist/lib/llm/OpenAIClient.js.map +1 -0
- package/dist/lib/prompt.js +288 -0
- package/dist/lib/prompt.js.map +1 -0
- package/dist/lib/vision.js +194 -0
- package/dist/lib/vision.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,978 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
12
|
+
var t = {};
|
|
13
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
14
|
+
t[p] = s[p];
|
|
15
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
16
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
17
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
18
|
+
t[p[i]] = s[p[i]];
|
|
19
|
+
}
|
|
20
|
+
return t;
|
|
21
|
+
};
|
|
22
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
23
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.Stagehand = void 0;
|
|
27
|
+
const test_1 = require("@playwright/test");
|
|
28
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
29
|
+
const fs_1 = __importDefault(require("fs"));
|
|
30
|
+
const inference_1 = require("./inference");
|
|
31
|
+
const LLMProvider_1 = require("./llm/LLMProvider");
|
|
32
|
+
const path_1 = __importDefault(require("path"));
|
|
33
|
+
const browserbase_1 = __importDefault(require("./browserbase"));
|
|
34
|
+
const vision_1 = require("./vision");
|
|
35
|
+
const LLMClient_1 = require("./llm/LLMClient");
|
|
36
|
+
require("dotenv").config({ path: ".env" });
|
|
37
|
+
function getBrowser() {
|
|
38
|
+
return __awaiter(this, arguments, void 0, function* (env = "LOCAL", headless = false, logger) {
|
|
39
|
+
if (env === "BROWSERBASE" && !process.env.BROWSERBASE_API_KEY) {
|
|
40
|
+
logger({
|
|
41
|
+
category: "Init",
|
|
42
|
+
message: "BROWSERBASE_API_KEY is required to use BROWSERBASE env. Defaulting to LOCAL.",
|
|
43
|
+
level: 0,
|
|
44
|
+
});
|
|
45
|
+
env = "LOCAL";
|
|
46
|
+
}
|
|
47
|
+
if (env === "BROWSERBASE" && !process.env.BROWSERBASE_PROJECT_ID) {
|
|
48
|
+
logger({
|
|
49
|
+
category: "Init",
|
|
50
|
+
message: "BROWSERBASE_PROJECT_ID is required to use BROWSERBASE env. Defaulting to LOCAL.",
|
|
51
|
+
level: 0,
|
|
52
|
+
});
|
|
53
|
+
env = "LOCAL";
|
|
54
|
+
}
|
|
55
|
+
if (env === "BROWSERBASE") {
|
|
56
|
+
let debugUrl = undefined;
|
|
57
|
+
let sessionUrl = undefined;
|
|
58
|
+
logger({
|
|
59
|
+
category: "Init",
|
|
60
|
+
message: "Connecting you to Browserbase...",
|
|
61
|
+
level: 0,
|
|
62
|
+
});
|
|
63
|
+
const browserbase = new browserbase_1.default();
|
|
64
|
+
const { sessionId, connectUrl } = yield browserbase.createSession();
|
|
65
|
+
const browser = yield test_1.chromium.connectOverCDP(connectUrl);
|
|
66
|
+
debugUrl = yield browserbase.retrieveDebugConnectionURL(sessionId);
|
|
67
|
+
sessionUrl = `https://www.browserbase.com/sessions/${sessionId}`;
|
|
68
|
+
logger({
|
|
69
|
+
category: "Init",
|
|
70
|
+
message: `Browserbase session started.\n\nSession Url: ${sessionUrl}\n\nLive debug accessible here: ${debugUrl}.`,
|
|
71
|
+
level: 0,
|
|
72
|
+
});
|
|
73
|
+
const context = browser.contexts()[0];
|
|
74
|
+
return { browser, context, debugUrl, sessionUrl };
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
logger({
|
|
78
|
+
category: "Init",
|
|
79
|
+
message: `Launching local browser in ${headless ? "headless" : "headed"} mode`,
|
|
80
|
+
level: 0,
|
|
81
|
+
});
|
|
82
|
+
const tmpDir = fs_1.default.mkdtempSync(`/tmp/pwtest`);
|
|
83
|
+
fs_1.default.mkdirSync(`${tmpDir}/userdir/Default`, { recursive: true });
|
|
84
|
+
const defaultPreferences = {
|
|
85
|
+
plugins: {
|
|
86
|
+
always_open_pdf_externally: true,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
fs_1.default.writeFileSync(`${tmpDir}/userdir/Default/Preferences`, JSON.stringify(defaultPreferences));
|
|
90
|
+
const downloadsPath = `${process.cwd()}/downloads`;
|
|
91
|
+
fs_1.default.mkdirSync(downloadsPath, { recursive: true });
|
|
92
|
+
const context = yield test_1.chromium.launchPersistentContext(`${tmpDir}/userdir`, {
|
|
93
|
+
acceptDownloads: true,
|
|
94
|
+
headless: headless,
|
|
95
|
+
viewport: {
|
|
96
|
+
width: 1250,
|
|
97
|
+
height: 800,
|
|
98
|
+
},
|
|
99
|
+
locale: "en-US",
|
|
100
|
+
timezoneId: "America/New_York",
|
|
101
|
+
deviceScaleFactor: 1,
|
|
102
|
+
args: [
|
|
103
|
+
"--enable-webgl",
|
|
104
|
+
"--use-gl=swiftshader",
|
|
105
|
+
"--enable-accelerated-2d-canvas",
|
|
106
|
+
"--disable-blink-features=AutomationControlled",
|
|
107
|
+
"--disable-web-security",
|
|
108
|
+
],
|
|
109
|
+
bypassCSP: true,
|
|
110
|
+
});
|
|
111
|
+
logger({
|
|
112
|
+
category: "Init",
|
|
113
|
+
message: "Local browser started successfully.",
|
|
114
|
+
});
|
|
115
|
+
yield applyStealthScripts(context);
|
|
116
|
+
return { context };
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
function applyStealthScripts(context) {
|
|
121
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
122
|
+
yield context.addInitScript(() => {
|
|
123
|
+
// Override the navigator.webdriver property
|
|
124
|
+
Object.defineProperty(navigator, "webdriver", {
|
|
125
|
+
get: () => undefined,
|
|
126
|
+
});
|
|
127
|
+
// Mock languages and plugins to mimic a real browser
|
|
128
|
+
Object.defineProperty(navigator, "languages", {
|
|
129
|
+
get: () => ["en-US", "en"],
|
|
130
|
+
});
|
|
131
|
+
Object.defineProperty(navigator, "plugins", {
|
|
132
|
+
get: () => [1, 2, 3, 4, 5],
|
|
133
|
+
});
|
|
134
|
+
// Remove Playwright-specific properties
|
|
135
|
+
delete window.__playwright;
|
|
136
|
+
delete window.__pw_manual;
|
|
137
|
+
delete window.__PW_inspect;
|
|
138
|
+
// Redefine the headless property
|
|
139
|
+
Object.defineProperty(navigator, "headless", {
|
|
140
|
+
get: () => false,
|
|
141
|
+
});
|
|
142
|
+
// Override the permissions API
|
|
143
|
+
const originalQuery = window.navigator.permissions.query;
|
|
144
|
+
window.navigator.permissions.query = (parameters) => parameters.name === "notifications"
|
|
145
|
+
? Promise.resolve({
|
|
146
|
+
state: Notification.permission,
|
|
147
|
+
})
|
|
148
|
+
: originalQuery(parameters);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
class Stagehand {
|
|
153
|
+
constructor({ env, verbose = 0, debugDom = false, llmProvider, headless = false, logger, domSettleTimeoutMs = 60000, } = {
|
|
154
|
+
env: "BROWSERBASE",
|
|
155
|
+
}) {
|
|
156
|
+
// Logging
|
|
157
|
+
this.pending_logs_to_send_to_browserbase = [];
|
|
158
|
+
this.is_processing_browserbase_logs = false;
|
|
159
|
+
this.externalLogger = logger;
|
|
160
|
+
this.logger = this.log.bind(this);
|
|
161
|
+
this.llmProvider = llmProvider || new LLMProvider_1.LLMProvider(this.logger);
|
|
162
|
+
this.env = env;
|
|
163
|
+
this.observations = {};
|
|
164
|
+
this.actions = {};
|
|
165
|
+
this.verbose = verbose;
|
|
166
|
+
this.debugDom = debugDom;
|
|
167
|
+
this.defaultModelName = "gpt-4o";
|
|
168
|
+
this.headless = headless;
|
|
169
|
+
this.domSettleTimeoutMs = domSettleTimeoutMs;
|
|
170
|
+
}
|
|
171
|
+
init() {
|
|
172
|
+
return __awaiter(this, arguments, void 0, function* ({ modelName = "gpt-4o", } = {}) {
|
|
173
|
+
const { context, debugUrl, sessionUrl } = yield getBrowser(this.env, this.headless, this.logger).catch((e) => {
|
|
174
|
+
console.error("Error in init:", e);
|
|
175
|
+
return { context: undefined, debugUrl: undefined, sessionUrl: undefined };
|
|
176
|
+
});
|
|
177
|
+
this.context = context;
|
|
178
|
+
this.page = context.pages()[0];
|
|
179
|
+
this.defaultModelName = modelName;
|
|
180
|
+
// Overload the page.goto method
|
|
181
|
+
const originalGoto = this.page.goto.bind(this.page);
|
|
182
|
+
this.page.goto = (url, options) => __awaiter(this, void 0, void 0, function* () {
|
|
183
|
+
const result = yield originalGoto(url, options);
|
|
184
|
+
yield this.page.waitForLoadState("domcontentloaded");
|
|
185
|
+
yield this._waitForSettledDom();
|
|
186
|
+
return result;
|
|
187
|
+
});
|
|
188
|
+
// Set the browser to headless mode if specified
|
|
189
|
+
if (this.headless) {
|
|
190
|
+
yield this.page.setViewportSize({ width: 1280, height: 720 });
|
|
191
|
+
}
|
|
192
|
+
// This can be greatly improved, but the tldr is we put our built web scripts in dist, which should always
|
|
193
|
+
// be one level above our running directly across evals, example, and as a package
|
|
194
|
+
yield this.page.addInitScript({
|
|
195
|
+
path: path_1.default.join(__dirname, "..", "dist", "dom", "build", "process.js"),
|
|
196
|
+
});
|
|
197
|
+
yield this.page.addInitScript({
|
|
198
|
+
path: path_1.default.join(__dirname, "..", "dist", "dom", "build", "utils.js"),
|
|
199
|
+
});
|
|
200
|
+
yield this.page.addInitScript({
|
|
201
|
+
path: path_1.default.join(__dirname, "..", "dist", "dom", "build", "debug.js"),
|
|
202
|
+
});
|
|
203
|
+
return { debugUrl, sessionUrl };
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
initFromPage(page, modelName) {
|
|
207
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
208
|
+
this.page = page;
|
|
209
|
+
this.context = page.context();
|
|
210
|
+
this.defaultModelName = modelName || this.defaultModelName;
|
|
211
|
+
const originalGoto = this.page.goto.bind(this.page);
|
|
212
|
+
this.page.goto = (url, options) => __awaiter(this, void 0, void 0, function* () {
|
|
213
|
+
const result = yield originalGoto(url, options);
|
|
214
|
+
yield this.page.waitForLoadState("domcontentloaded");
|
|
215
|
+
yield this._waitForSettledDom();
|
|
216
|
+
return result;
|
|
217
|
+
});
|
|
218
|
+
// Set the browser to headless mode if specified
|
|
219
|
+
if (this.headless) {
|
|
220
|
+
yield this.page.setViewportSize({ width: 1280, height: 720 });
|
|
221
|
+
}
|
|
222
|
+
// Add initialization scripts
|
|
223
|
+
yield this.page.addInitScript({
|
|
224
|
+
path: path_1.default.join(__dirname, "..", "dist", "dom", "build", "process.js"),
|
|
225
|
+
});
|
|
226
|
+
yield this.page.addInitScript({
|
|
227
|
+
path: path_1.default.join(__dirname, "..", "dist", "dom", "build", "utils.js"),
|
|
228
|
+
});
|
|
229
|
+
yield this.page.addInitScript({
|
|
230
|
+
path: path_1.default.join(__dirname, "..", "dist", "dom", "build", "debug.js"),
|
|
231
|
+
});
|
|
232
|
+
return { context: this.context };
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
log({ message, category, level, }) {
|
|
236
|
+
const logObj = { category, message, level };
|
|
237
|
+
logObj.level = logObj.level || 1;
|
|
238
|
+
// Normal Logging
|
|
239
|
+
if (this.externalLogger) {
|
|
240
|
+
this.externalLogger(logObj);
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
const categoryString = logObj.category ? `:${logObj.category}` : "";
|
|
244
|
+
const logMessage = `[stagehand${categoryString}] ${logObj.message}`;
|
|
245
|
+
console.log(logMessage);
|
|
246
|
+
}
|
|
247
|
+
// Add the logs to the browserbase session
|
|
248
|
+
this.pending_logs_to_send_to_browserbase.push(Object.assign(Object.assign({}, logObj), { id: Math.random().toString(36).substring(2, 15) }));
|
|
249
|
+
this._run_browserbase_log_processing_cycle();
|
|
250
|
+
}
|
|
251
|
+
_run_browserbase_log_processing_cycle() {
|
|
252
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
253
|
+
if (this.is_processing_browserbase_logs) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
this.is_processing_browserbase_logs = true;
|
|
257
|
+
const pending_logs = [...this.pending_logs_to_send_to_browserbase];
|
|
258
|
+
for (const logObj of pending_logs) {
|
|
259
|
+
yield this._log_to_browserbase(logObj);
|
|
260
|
+
}
|
|
261
|
+
this.is_processing_browserbase_logs = false;
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
_log_to_browserbase(logObj) {
|
|
265
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
266
|
+
logObj.level = logObj.level || 1;
|
|
267
|
+
if (!this.page) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
if (this.verbose >= logObj.level) {
|
|
271
|
+
yield this.page
|
|
272
|
+
.evaluate((logObj) => {
|
|
273
|
+
const logMessage = `[stagehand${logObj.category ? `:${logObj.category}` : ""}] ${logObj.message}`;
|
|
274
|
+
if (logObj.message.toLowerCase().includes("trace") ||
|
|
275
|
+
logObj.message.toLowerCase().includes("error:")) {
|
|
276
|
+
console.error(logMessage);
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
console.log(logMessage);
|
|
280
|
+
}
|
|
281
|
+
}, logObj)
|
|
282
|
+
.then(() => {
|
|
283
|
+
this.pending_logs_to_send_to_browserbase =
|
|
284
|
+
this.pending_logs_to_send_to_browserbase.filter((log) => log.id !== logObj.id);
|
|
285
|
+
})
|
|
286
|
+
.catch((e) => {
|
|
287
|
+
// NAVIDTODO: Rerun the log call on the new page
|
|
288
|
+
// This is expected to happen when the user is changing pages
|
|
289
|
+
// console.error("Logging Error:", e);
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
_waitForSettledDom(timeoutMs) {
|
|
295
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
296
|
+
try {
|
|
297
|
+
const timeout = timeoutMs !== null && timeoutMs !== void 0 ? timeoutMs : this.domSettleTimeoutMs;
|
|
298
|
+
let timeoutHandle;
|
|
299
|
+
const timeoutPromise = new Promise((resolve, reject) => {
|
|
300
|
+
timeoutHandle = setTimeout(() => {
|
|
301
|
+
this.log({
|
|
302
|
+
category: "dom",
|
|
303
|
+
message: `DOM settle timeout of ${timeout}ms exceeded, continuing anyway`,
|
|
304
|
+
level: 1,
|
|
305
|
+
});
|
|
306
|
+
resolve();
|
|
307
|
+
}, timeout);
|
|
308
|
+
});
|
|
309
|
+
try {
|
|
310
|
+
yield Promise.race([
|
|
311
|
+
this.page.evaluate(() => {
|
|
312
|
+
return new Promise((resolve) => {
|
|
313
|
+
if (typeof window.waitForDomSettle === "function") {
|
|
314
|
+
window.waitForDomSettle().then(resolve);
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
console.warn("waitForDomSettle is not defined, considering DOM as settled");
|
|
318
|
+
resolve();
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
}),
|
|
322
|
+
this.page.waitForLoadState("domcontentloaded"),
|
|
323
|
+
this.page.waitForSelector("body"),
|
|
324
|
+
timeoutPromise,
|
|
325
|
+
]);
|
|
326
|
+
}
|
|
327
|
+
finally {
|
|
328
|
+
clearTimeout(timeoutHandle);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
catch (e) {
|
|
332
|
+
this.log({
|
|
333
|
+
category: "dom",
|
|
334
|
+
message: `Error in waitForSettledDom: ${e.message}\nTrace: ${e.stack}`,
|
|
335
|
+
level: 1,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
startDomDebug() {
|
|
341
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
342
|
+
try {
|
|
343
|
+
yield this.page
|
|
344
|
+
.evaluate(() => {
|
|
345
|
+
if (typeof window.debugDom === "function") {
|
|
346
|
+
window.debugDom();
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
this.log({
|
|
350
|
+
category: "dom",
|
|
351
|
+
message: "debugDom is not defined",
|
|
352
|
+
level: 1,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
})
|
|
356
|
+
.catch(() => { });
|
|
357
|
+
}
|
|
358
|
+
catch (e) {
|
|
359
|
+
this.log({
|
|
360
|
+
category: "dom",
|
|
361
|
+
message: `Error in startDomDebug: ${e.message}\nTrace: ${e.stack}`,
|
|
362
|
+
level: 1,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
cleanupDomDebug() {
|
|
368
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
369
|
+
if (this.debugDom) {
|
|
370
|
+
yield this.page.evaluate(() => window.cleanupDebug()).catch(() => { });
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
// Recording
|
|
375
|
+
_generateId(operation) {
|
|
376
|
+
return crypto_1.default.createHash("sha256").update(operation).digest("hex");
|
|
377
|
+
}
|
|
378
|
+
_recordObservation(instruction, result) {
|
|
379
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
380
|
+
const id = this._generateId(instruction);
|
|
381
|
+
this.observations[id] = { result, instruction };
|
|
382
|
+
return id;
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
_recordAction(action, result) {
|
|
386
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
387
|
+
const id = this._generateId(action);
|
|
388
|
+
this.actions[id] = { result, action };
|
|
389
|
+
return id;
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
// Main methods
|
|
393
|
+
_extract(_a) {
|
|
394
|
+
return __awaiter(this, arguments, void 0, function* ({ instruction, schema, progress = "", content = {}, chunksSeen = [], modelName, }) {
|
|
395
|
+
this.log({
|
|
396
|
+
category: "extraction",
|
|
397
|
+
message: `starting extraction '${instruction}'`,
|
|
398
|
+
level: 1,
|
|
399
|
+
});
|
|
400
|
+
yield this._waitForSettledDom();
|
|
401
|
+
yield this.startDomDebug();
|
|
402
|
+
const { outputString, chunk, chunks } = yield this.page.evaluate((chunksSeen) => window.processDom(chunksSeen !== null && chunksSeen !== void 0 ? chunksSeen : []), chunksSeen);
|
|
403
|
+
this.log({
|
|
404
|
+
category: "extraction",
|
|
405
|
+
message: `received output from processDom. Current chunk index: ${chunk}, Number of chunks left: ${chunks.length - chunksSeen.length}`,
|
|
406
|
+
level: 1,
|
|
407
|
+
});
|
|
408
|
+
const extractionResponse = yield (0, inference_1.extract)({
|
|
409
|
+
instruction,
|
|
410
|
+
progress,
|
|
411
|
+
previouslyExtractedContent: content,
|
|
412
|
+
domElements: outputString,
|
|
413
|
+
llmProvider: this.llmProvider,
|
|
414
|
+
schema,
|
|
415
|
+
modelName: modelName || this.defaultModelName,
|
|
416
|
+
chunksSeen: chunksSeen.length,
|
|
417
|
+
chunksTotal: chunks.length,
|
|
418
|
+
});
|
|
419
|
+
const { metadata: { progress: newProgress, completed } } = extractionResponse, output = __rest(extractionResponse, ["metadata"]);
|
|
420
|
+
yield this.cleanupDomDebug();
|
|
421
|
+
this.log({
|
|
422
|
+
category: "extraction",
|
|
423
|
+
message: `received extraction response: ${JSON.stringify(extractionResponse)}`,
|
|
424
|
+
level: 1,
|
|
425
|
+
});
|
|
426
|
+
chunksSeen.push(chunk);
|
|
427
|
+
if (completed || chunksSeen.length === chunks.length) {
|
|
428
|
+
this.log({
|
|
429
|
+
category: "extraction",
|
|
430
|
+
message: `response: ${JSON.stringify(extractionResponse)}`,
|
|
431
|
+
level: 1,
|
|
432
|
+
});
|
|
433
|
+
return output;
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
this.log({
|
|
437
|
+
category: "extraction",
|
|
438
|
+
message: `continuing extraction, progress: '${newProgress}'`,
|
|
439
|
+
level: 1,
|
|
440
|
+
});
|
|
441
|
+
yield this._waitForSettledDom();
|
|
442
|
+
return this._extract({
|
|
443
|
+
instruction,
|
|
444
|
+
schema,
|
|
445
|
+
progress: newProgress,
|
|
446
|
+
content: output,
|
|
447
|
+
chunksSeen,
|
|
448
|
+
modelName,
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
_observe(_a) {
|
|
454
|
+
return __awaiter(this, arguments, void 0, function* ({ instruction, useVision, fullPage, modelName, }) {
|
|
455
|
+
if (!instruction) {
|
|
456
|
+
instruction = `Find elements that can be used for any future actions in the page. These may be navigation links, related pages, section/subsection links, buttons, or other interactive elements. Be comprehensive: if there are multiple elements that may be relevant for future actions, return all of them.`;
|
|
457
|
+
}
|
|
458
|
+
const model = modelName !== null && modelName !== void 0 ? modelName : this.defaultModelName;
|
|
459
|
+
this.log({
|
|
460
|
+
category: "observation",
|
|
461
|
+
message: `starting observation: ${instruction}`,
|
|
462
|
+
level: 1,
|
|
463
|
+
});
|
|
464
|
+
yield this._waitForSettledDom();
|
|
465
|
+
yield this.startDomDebug();
|
|
466
|
+
let { outputString, selectorMap } = yield this.page.evaluate((fullPage) => fullPage ? window.processAllOfDom() : window.processDom([]), fullPage);
|
|
467
|
+
let annotatedScreenshot;
|
|
468
|
+
if (useVision === true) {
|
|
469
|
+
if (!LLMClient_1.modelsWithVision.includes(model)) {
|
|
470
|
+
this.log({
|
|
471
|
+
category: "observation",
|
|
472
|
+
message: `${model} does not support vision. Skipping vision processing.`,
|
|
473
|
+
level: 1,
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
else {
|
|
477
|
+
const screenshotService = new vision_1.ScreenshotService(this.page, selectorMap, this.verbose);
|
|
478
|
+
annotatedScreenshot =
|
|
479
|
+
yield screenshotService.getAnnotatedScreenshot(fullPage);
|
|
480
|
+
outputString = "n/a. use the image to find the elements.";
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
const observationResponse = yield (0, inference_1.observe)({
|
|
484
|
+
instruction,
|
|
485
|
+
domElements: outputString,
|
|
486
|
+
llmProvider: this.llmProvider,
|
|
487
|
+
modelName: modelName || this.defaultModelName,
|
|
488
|
+
image: annotatedScreenshot,
|
|
489
|
+
});
|
|
490
|
+
const elementsWithSelectors = observationResponse.elements.map((element) => {
|
|
491
|
+
const { elementId } = element, rest = __rest(element, ["elementId"]);
|
|
492
|
+
return Object.assign(Object.assign({}, rest), { selector: `xpath=${selectorMap[elementId]}` });
|
|
493
|
+
});
|
|
494
|
+
yield this.cleanupDomDebug();
|
|
495
|
+
this._recordObservation(instruction, elementsWithSelectors);
|
|
496
|
+
this.log({
|
|
497
|
+
category: "observation",
|
|
498
|
+
message: `found element ${JSON.stringify(elementsWithSelectors)}`,
|
|
499
|
+
level: 1,
|
|
500
|
+
});
|
|
501
|
+
yield this._recordObservation(instruction, elementsWithSelectors);
|
|
502
|
+
return elementsWithSelectors;
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
_act(_a) {
|
|
506
|
+
return __awaiter(this, arguments, void 0, function* ({ action, steps = "", chunksSeen, modelName, useVision, verifierUseVision, retries = 0, }) {
|
|
507
|
+
var _b;
|
|
508
|
+
const model = modelName !== null && modelName !== void 0 ? modelName : this.defaultModelName;
|
|
509
|
+
if (!LLMClient_1.modelsWithVision.includes(model) &&
|
|
510
|
+
(useVision !== false || verifierUseVision)) {
|
|
511
|
+
this.log({
|
|
512
|
+
category: "action",
|
|
513
|
+
message: `${model} does not support vision, but useVision was set to ${useVision}. Defaulting to false.`,
|
|
514
|
+
level: 1,
|
|
515
|
+
});
|
|
516
|
+
useVision = false;
|
|
517
|
+
verifierUseVision = false;
|
|
518
|
+
}
|
|
519
|
+
this.log({
|
|
520
|
+
category: "action",
|
|
521
|
+
message: `Running / Continuing action: ${action} on page: ${this.page.url()}`,
|
|
522
|
+
level: 2,
|
|
523
|
+
});
|
|
524
|
+
yield this._waitForSettledDom();
|
|
525
|
+
yield this.startDomDebug();
|
|
526
|
+
this.log({
|
|
527
|
+
category: "action",
|
|
528
|
+
message: `Processing DOM...`,
|
|
529
|
+
level: 2,
|
|
530
|
+
});
|
|
531
|
+
const { outputString, selectorMap, chunk, chunks } = yield this.page.evaluate(({ chunksSeen }) => {
|
|
532
|
+
// @ts-ignore
|
|
533
|
+
return window.processDom(chunksSeen);
|
|
534
|
+
}, { chunksSeen });
|
|
535
|
+
this.log({
|
|
536
|
+
category: "action",
|
|
537
|
+
message: `Looking at chunk ${chunk}. Chunks left: ${chunks.length - chunksSeen.length}`,
|
|
538
|
+
level: 1,
|
|
539
|
+
});
|
|
540
|
+
// Prepare annotated screenshot if vision is enabled
|
|
541
|
+
let annotatedScreenshot;
|
|
542
|
+
if (useVision === true) {
|
|
543
|
+
if (!LLMClient_1.modelsWithVision.includes(model)) {
|
|
544
|
+
this.log({
|
|
545
|
+
category: "action",
|
|
546
|
+
message: `${model} does not support vision. Skipping vision processing.`,
|
|
547
|
+
level: 1,
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
else {
|
|
551
|
+
const screenshotService = new vision_1.ScreenshotService(this.page, selectorMap, this.verbose);
|
|
552
|
+
annotatedScreenshot =
|
|
553
|
+
yield screenshotService.getAnnotatedScreenshot(false);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
const response = yield (0, inference_1.act)({
|
|
557
|
+
action,
|
|
558
|
+
domElements: outputString,
|
|
559
|
+
steps,
|
|
560
|
+
llmProvider: this.llmProvider,
|
|
561
|
+
modelName: model,
|
|
562
|
+
screenshot: annotatedScreenshot,
|
|
563
|
+
logger: this.logger,
|
|
564
|
+
});
|
|
565
|
+
this.log({
|
|
566
|
+
category: "action",
|
|
567
|
+
message: `Received response from LLM: ${JSON.stringify(response)}`,
|
|
568
|
+
level: 1,
|
|
569
|
+
});
|
|
570
|
+
yield this.cleanupDomDebug();
|
|
571
|
+
if (!response) {
|
|
572
|
+
if (chunksSeen.length + 1 < chunks.length) {
|
|
573
|
+
chunksSeen.push(chunk);
|
|
574
|
+
this.log({
|
|
575
|
+
category: "action",
|
|
576
|
+
message: `No action found in current chunk. Chunks seen: ${chunksSeen.length}.`,
|
|
577
|
+
level: 1,
|
|
578
|
+
});
|
|
579
|
+
return this._act({
|
|
580
|
+
action,
|
|
581
|
+
steps: steps +
|
|
582
|
+
(!steps.endsWith("\n") ? "\n" : "") +
|
|
583
|
+
"## Step: Scrolled to another section\n",
|
|
584
|
+
chunksSeen,
|
|
585
|
+
modelName,
|
|
586
|
+
useVision,
|
|
587
|
+
verifierUseVision,
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
else if (useVision === "fallback") {
|
|
591
|
+
this.log({
|
|
592
|
+
category: "action",
|
|
593
|
+
message: `Switching to vision-based processing`,
|
|
594
|
+
level: 1,
|
|
595
|
+
});
|
|
596
|
+
yield this.page.evaluate(() => window.scrollToHeight(0));
|
|
597
|
+
return yield this._act({
|
|
598
|
+
action,
|
|
599
|
+
steps,
|
|
600
|
+
chunksSeen,
|
|
601
|
+
modelName,
|
|
602
|
+
useVision: true,
|
|
603
|
+
verifierUseVision,
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
else {
|
|
607
|
+
return {
|
|
608
|
+
success: false,
|
|
609
|
+
message: `Action was not able to be completed.`,
|
|
610
|
+
action: action,
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
// Action found, proceed to execute
|
|
615
|
+
const elementId = response["element"];
|
|
616
|
+
const xpath = selectorMap[elementId];
|
|
617
|
+
const method = response["method"];
|
|
618
|
+
const args = response["args"];
|
|
619
|
+
// Get the element text from the outputString
|
|
620
|
+
const elementLines = outputString.split("\n");
|
|
621
|
+
const elementText = ((_b = elementLines
|
|
622
|
+
.find((line) => line.startsWith(`${elementId}:`))) === null || _b === void 0 ? void 0 : _b.split(":")[1]) || "Element not found";
|
|
623
|
+
this.log({
|
|
624
|
+
category: "action",
|
|
625
|
+
message: `Executing method: ${method} on element: ${elementId} (xpath: ${xpath}) with args: ${JSON.stringify(args)}`,
|
|
626
|
+
level: 1,
|
|
627
|
+
});
|
|
628
|
+
let urlChangeString = "";
|
|
629
|
+
const locator = this.page.locator(`xpath=${xpath}`).first();
|
|
630
|
+
try {
|
|
631
|
+
const initialUrl = this.page.url();
|
|
632
|
+
if (method === "scrollIntoView") {
|
|
633
|
+
this.log({
|
|
634
|
+
category: "action",
|
|
635
|
+
message: `Scrolling element into view`,
|
|
636
|
+
level: 2,
|
|
637
|
+
});
|
|
638
|
+
try {
|
|
639
|
+
yield locator
|
|
640
|
+
.evaluate((element) => {
|
|
641
|
+
element.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
642
|
+
})
|
|
643
|
+
.catch((e) => {
|
|
644
|
+
this.log({
|
|
645
|
+
category: "action",
|
|
646
|
+
message: `Error scrolling element into view: ${e.message}\nTrace: ${e.stack}`,
|
|
647
|
+
level: 1,
|
|
648
|
+
});
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
catch (e) {
|
|
652
|
+
this.log({
|
|
653
|
+
category: "action",
|
|
654
|
+
message: `Error scrolling element into view (Retries ${retries}): ${e.message}\nTrace: ${e.stack}`,
|
|
655
|
+
level: 1,
|
|
656
|
+
});
|
|
657
|
+
if (retries < 2) {
|
|
658
|
+
return this._act({
|
|
659
|
+
action,
|
|
660
|
+
steps,
|
|
661
|
+
modelName,
|
|
662
|
+
useVision,
|
|
663
|
+
verifierUseVision,
|
|
664
|
+
retries: retries + 1,
|
|
665
|
+
chunksSeen,
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
else if (method === "fill" || method === "type") {
|
|
671
|
+
try {
|
|
672
|
+
yield locator.fill("");
|
|
673
|
+
yield locator.click();
|
|
674
|
+
const text = args[0];
|
|
675
|
+
for (const char of text) {
|
|
676
|
+
yield this.page.keyboard.type(char, {
|
|
677
|
+
delay: Math.random() * 50 + 25,
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
catch (e) {
|
|
682
|
+
this.log({
|
|
683
|
+
category: "action",
|
|
684
|
+
message: `Error filling element (Retries ${retries}): ${e.message}\nTrace: ${e.stack}`,
|
|
685
|
+
level: 1,
|
|
686
|
+
});
|
|
687
|
+
if (retries < 2) {
|
|
688
|
+
return this._act({
|
|
689
|
+
action,
|
|
690
|
+
steps,
|
|
691
|
+
modelName,
|
|
692
|
+
useVision,
|
|
693
|
+
verifierUseVision,
|
|
694
|
+
retries: retries + 1,
|
|
695
|
+
chunksSeen,
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
else if (method === "press") {
|
|
701
|
+
try {
|
|
702
|
+
const key = args[0];
|
|
703
|
+
yield this.page.keyboard.press(key);
|
|
704
|
+
}
|
|
705
|
+
catch (e) {
|
|
706
|
+
this.log({
|
|
707
|
+
category: "action",
|
|
708
|
+
message: `Error pressing key (Retries ${retries}): ${e.message}\nTrace: ${e.stack}`,
|
|
709
|
+
level: 1,
|
|
710
|
+
});
|
|
711
|
+
if (retries < 2) {
|
|
712
|
+
return this._act({
|
|
713
|
+
action,
|
|
714
|
+
steps,
|
|
715
|
+
modelName,
|
|
716
|
+
useVision,
|
|
717
|
+
verifierUseVision,
|
|
718
|
+
retries: retries + 1,
|
|
719
|
+
chunksSeen,
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
else if (typeof locator[method] === "function") {
|
|
725
|
+
// Log current URL before action
|
|
726
|
+
this.log({
|
|
727
|
+
category: "action",
|
|
728
|
+
message: `Page URL before action: ${this.page.url()}`,
|
|
729
|
+
level: 2,
|
|
730
|
+
});
|
|
731
|
+
// Perform the action
|
|
732
|
+
try {
|
|
733
|
+
// @ts-ignore
|
|
734
|
+
yield locator[method](...args);
|
|
735
|
+
}
|
|
736
|
+
catch (e) {
|
|
737
|
+
this.log({
|
|
738
|
+
category: "action",
|
|
739
|
+
message: `Error performing method ${method} with args ${JSON.stringify(args)} (Retries: ${retries}): ${e.message}\nTrace: ${e.stack}`,
|
|
740
|
+
level: 1,
|
|
741
|
+
});
|
|
742
|
+
if (retries < 2) {
|
|
743
|
+
return this._act({
|
|
744
|
+
action,
|
|
745
|
+
steps,
|
|
746
|
+
modelName,
|
|
747
|
+
useVision,
|
|
748
|
+
verifierUseVision,
|
|
749
|
+
retries: retries + 1,
|
|
750
|
+
chunksSeen,
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
// Handle navigation if a new page is opened
|
|
755
|
+
if (method === "click") {
|
|
756
|
+
this.log({
|
|
757
|
+
category: "action",
|
|
758
|
+
message: `Clicking element, checking for page navigation`,
|
|
759
|
+
level: 1,
|
|
760
|
+
});
|
|
761
|
+
// NAVIDNOTE: Should this happen before we wait for locator[method]?
|
|
762
|
+
const newOpenedTab = yield Promise.race([
|
|
763
|
+
new Promise((resolve) => {
|
|
764
|
+
this.context.once("page", (page) => resolve(page));
|
|
765
|
+
setTimeout(() => resolve(null), 1500);
|
|
766
|
+
}),
|
|
767
|
+
]);
|
|
768
|
+
this.log({
|
|
769
|
+
category: "action",
|
|
770
|
+
message: `Clicked element, ${newOpenedTab ? "opened a new tab" : "no new tabs opened"}`,
|
|
771
|
+
level: 1,
|
|
772
|
+
});
|
|
773
|
+
if (newOpenedTab) {
|
|
774
|
+
this.log({
|
|
775
|
+
category: "action",
|
|
776
|
+
message: `New page detected (new tab) with URL: ${newOpenedTab.url()}`,
|
|
777
|
+
level: 1,
|
|
778
|
+
});
|
|
779
|
+
yield newOpenedTab.close();
|
|
780
|
+
yield this.page.goto(newOpenedTab.url());
|
|
781
|
+
yield this.page.waitForLoadState("domcontentloaded");
|
|
782
|
+
yield this._waitForSettledDom();
|
|
783
|
+
}
|
|
784
|
+
// Wait for the network to be idle with timeout of 5s (will only wait if loading a new page)
|
|
785
|
+
// await this.waitForSettledDom();
|
|
786
|
+
yield Promise.race([
|
|
787
|
+
this.page.waitForLoadState("networkidle"),
|
|
788
|
+
new Promise((resolve) => setTimeout(resolve, 5000)),
|
|
789
|
+
]).catch((e) => {
|
|
790
|
+
this.log({
|
|
791
|
+
category: "action",
|
|
792
|
+
message: `Network idle timeout hit`,
|
|
793
|
+
level: 1,
|
|
794
|
+
});
|
|
795
|
+
});
|
|
796
|
+
this.log({
|
|
797
|
+
category: "action",
|
|
798
|
+
message: `Finished waiting for (possible) page navigation`,
|
|
799
|
+
level: 1,
|
|
800
|
+
});
|
|
801
|
+
if (this.page.url() !== initialUrl) {
|
|
802
|
+
this.log({
|
|
803
|
+
category: "action",
|
|
804
|
+
message: `New page detected with URL: ${this.page.url()}`,
|
|
805
|
+
level: 1,
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
else {
|
|
811
|
+
this.log({
|
|
812
|
+
category: "action",
|
|
813
|
+
message: `Chosen method ${method} is invalid`,
|
|
814
|
+
level: 1,
|
|
815
|
+
});
|
|
816
|
+
if (retries < 2) {
|
|
817
|
+
return this._act({
|
|
818
|
+
action,
|
|
819
|
+
steps,
|
|
820
|
+
modelName: model,
|
|
821
|
+
useVision,
|
|
822
|
+
verifierUseVision,
|
|
823
|
+
retries: retries + 1,
|
|
824
|
+
chunksSeen,
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
else {
|
|
828
|
+
return {
|
|
829
|
+
success: false,
|
|
830
|
+
message: `Internal error: Chosen method ${method} is invalid`,
|
|
831
|
+
action: action,
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
let newSteps = steps +
|
|
836
|
+
(!steps.endsWith("\n") ? "\n" : "") +
|
|
837
|
+
`## Step: ${response.step}\n` +
|
|
838
|
+
` Element: ${elementText}\n` +
|
|
839
|
+
` Action: ${response.method}\n` +
|
|
840
|
+
` Reasoning: ${response.why}\n`;
|
|
841
|
+
if (urlChangeString) {
|
|
842
|
+
newSteps += ` Result (Important): ${urlChangeString}\n\n`;
|
|
843
|
+
}
|
|
844
|
+
let actionComplete = false;
|
|
845
|
+
if (response.completed) {
|
|
846
|
+
// Run action completion verifier
|
|
847
|
+
this.log({
|
|
848
|
+
category: "action",
|
|
849
|
+
message: `Action marked as completed, Verifying if this is true...`,
|
|
850
|
+
level: 1,
|
|
851
|
+
});
|
|
852
|
+
let domElements = undefined;
|
|
853
|
+
let fullpageScreenshot = undefined;
|
|
854
|
+
if (verifierUseVision) {
|
|
855
|
+
try {
|
|
856
|
+
const screenshotService = new vision_1.ScreenshotService(this.page, selectorMap, this.verbose);
|
|
857
|
+
fullpageScreenshot = yield screenshotService.getScreenshot(true, 15);
|
|
858
|
+
}
|
|
859
|
+
catch (e) {
|
|
860
|
+
this.log({
|
|
861
|
+
category: "action",
|
|
862
|
+
message: `Error getting full page screenshot: ${e.message}\n. Trying again...`,
|
|
863
|
+
level: 1,
|
|
864
|
+
});
|
|
865
|
+
const screenshotService = new vision_1.ScreenshotService(this.page, selectorMap, this.verbose);
|
|
866
|
+
fullpageScreenshot = yield screenshotService.getScreenshot(true, 15);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
else {
|
|
870
|
+
({ outputString: domElements } = yield this.page.evaluate(() => {
|
|
871
|
+
return window.processAllOfDom();
|
|
872
|
+
}));
|
|
873
|
+
}
|
|
874
|
+
actionComplete = yield (0, inference_1.verifyActCompletion)({
|
|
875
|
+
goal: action,
|
|
876
|
+
steps: newSteps,
|
|
877
|
+
llmProvider: this.llmProvider,
|
|
878
|
+
modelName: model,
|
|
879
|
+
screenshot: fullpageScreenshot,
|
|
880
|
+
domElements: domElements,
|
|
881
|
+
logger: this.logger,
|
|
882
|
+
});
|
|
883
|
+
this.log({
|
|
884
|
+
category: "action",
|
|
885
|
+
message: `Action completion verification result: ${actionComplete}`,
|
|
886
|
+
level: 1,
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
if (!actionComplete) {
|
|
890
|
+
this.log({
|
|
891
|
+
category: "action",
|
|
892
|
+
message: `Continuing to next action step`,
|
|
893
|
+
level: 1,
|
|
894
|
+
});
|
|
895
|
+
return this._act({
|
|
896
|
+
action,
|
|
897
|
+
steps: newSteps,
|
|
898
|
+
modelName,
|
|
899
|
+
chunksSeen,
|
|
900
|
+
useVision,
|
|
901
|
+
verifierUseVision,
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
else {
|
|
905
|
+
this.log({
|
|
906
|
+
category: "action",
|
|
907
|
+
message: `Action completed successfully`,
|
|
908
|
+
level: 1,
|
|
909
|
+
});
|
|
910
|
+
yield this._recordAction(action, response.step);
|
|
911
|
+
return {
|
|
912
|
+
success: true,
|
|
913
|
+
message: `Action completed successfully: ${steps}${response.step}`,
|
|
914
|
+
action: action,
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
catch (error) {
|
|
919
|
+
this.log({
|
|
920
|
+
category: "action",
|
|
921
|
+
message: `Error performing action (Retries: ${retries}): ${error.message}\nTrace: ${error.stack}`,
|
|
922
|
+
level: 1,
|
|
923
|
+
});
|
|
924
|
+
if (retries < 2) {
|
|
925
|
+
return this._act({
|
|
926
|
+
action,
|
|
927
|
+
steps,
|
|
928
|
+
modelName,
|
|
929
|
+
useVision,
|
|
930
|
+
verifierUseVision,
|
|
931
|
+
retries: retries + 1,
|
|
932
|
+
chunksSeen,
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
yield this._recordAction(action, "");
|
|
936
|
+
return {
|
|
937
|
+
success: false,
|
|
938
|
+
message: `Error performing action: ${error.message}`,
|
|
939
|
+
action: action,
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
act(_a) {
|
|
945
|
+
return __awaiter(this, arguments, void 0, function* ({ action, modelName, useVision = "fallback", }) {
|
|
946
|
+
useVision = useVision !== null && useVision !== void 0 ? useVision : "fallback";
|
|
947
|
+
return this._act({
|
|
948
|
+
action,
|
|
949
|
+
modelName,
|
|
950
|
+
chunksSeen: [],
|
|
951
|
+
useVision,
|
|
952
|
+
verifierUseVision: useVision !== false,
|
|
953
|
+
});
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
extract(_a) {
|
|
957
|
+
return __awaiter(this, arguments, void 0, function* ({ instruction, schema, modelName, }) {
|
|
958
|
+
return this._extract({
|
|
959
|
+
instruction,
|
|
960
|
+
schema,
|
|
961
|
+
modelName,
|
|
962
|
+
});
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
observe(options) {
|
|
966
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
967
|
+
var _a, _b;
|
|
968
|
+
return this._observe({
|
|
969
|
+
instruction: (_a = options === null || options === void 0 ? void 0 : options.instruction) !== null && _a !== void 0 ? _a : "Find actions that can be performed on this page.",
|
|
970
|
+
modelName: options === null || options === void 0 ? void 0 : options.modelName,
|
|
971
|
+
useVision: (_b = options === null || options === void 0 ? void 0 : options.useVision) !== null && _b !== void 0 ? _b : false,
|
|
972
|
+
fullPage: false,
|
|
973
|
+
});
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
exports.Stagehand = Stagehand;
|
|
978
|
+
//# sourceMappingURL=index.js.map
|