@browserbasehq/stagehand 2.1.0 → 2.2.0-alpha-8814af9ece99fddc3dd9fb32671d0513a3a00c67
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/dist/evals/tasks/extract_jfk_links.d.ts +2 -0
- package/dist/evals/tasks/extract_single_link.d.ts +2 -0
- package/dist/index.d.ts +18 -1
- package/dist/index.js +189 -28
- package/dist/lib/handlers/extractHandler.d.ts +34 -1
- package/dist/types/stagehand.d.ts +17 -0
- package/lib/handlers/extractHandler.ts +256 -4
- package/lib/index.ts +0 -13
- package/lib/prompt.ts +3 -3
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -524,6 +524,23 @@ interface HistoryEntry {
|
|
|
524
524
|
result: unknown;
|
|
525
525
|
timestamp: string;
|
|
526
526
|
}
|
|
527
|
+
/**
|
|
528
|
+
* Represents a path through a Zod schema from the root object down to a
|
|
529
|
+
* particular field. The `segments` array describes the chain of keys/indices.
|
|
530
|
+
*
|
|
531
|
+
* - **String** segments indicate object property names.
|
|
532
|
+
* - **Number** segments indicate array indices.
|
|
533
|
+
*
|
|
534
|
+
* For example, `["users", 0, "homepage"]` might describe reaching
|
|
535
|
+
* the `homepage` field in `schema.users[0].homepage`.
|
|
536
|
+
*/
|
|
537
|
+
interface ZodPathSegments {
|
|
538
|
+
/**
|
|
539
|
+
* The ordered list of keys/indices leading from the schema root
|
|
540
|
+
* to the targeted field.
|
|
541
|
+
*/
|
|
542
|
+
segments: Array<string | number>;
|
|
543
|
+
}
|
|
527
544
|
|
|
528
545
|
declare const defaultExtractSchema: z.ZodObject<{
|
|
529
546
|
extraction: z.ZodString;
|
|
@@ -849,4 +866,4 @@ declare class Stagehand {
|
|
|
849
866
|
get history(): ReadonlyArray<HistoryEntry>;
|
|
850
867
|
}
|
|
851
868
|
|
|
852
|
-
export { type ActOptions, type ActResult, type ActionExecutionResult, type AgentAction, type AgentClientOptions, type AgentConfig, type AgentExecuteOptions, type AgentExecuteParams, type AgentExecutionOptions, type AgentHandlerOptions, type AgentOptions, type AgentProviderType, type AgentResult, AgentScreenshotProviderError, type AgentType, AnnotatedScreenshotText, type AnthropicContentBlock, type AnthropicJsonSchemaObject, type AnthropicMessage, type AnthropicTextBlock, type AnthropicToolResult, type AvailableModel, AvailableModelSchema, type Browser, type BrowserContext, type BrowserResult, BrowserbaseSessionNotFoundError, CaptchaTimeoutError, type ChatCompletionOptions, type ChatMessage, type ChatMessageContent, type ChatMessageImageContent, type ChatMessageTextContent, type ClientOptions, type ComputerCallItem, type ConstructorParams, type CreateChatCompletionOptions, CreateChatCompletionResponseError, type ExtractOptions, type ExtractResult, type FunctionCallItem, type GotoOptions, HandlerNotInitializedError, type HistoryEntry, type InitResult, LLMClient, type LLMResponse, LOG_LEVEL_NAMES, type LocalBrowserLaunchOptions, type LogLevel, type LogLine, type Logger, MissingEnvironmentVariableError, MissingLLMConfigurationError, type ModelProvider, type ObserveOptions, type ObserveResult, type OperatorResponse, type OperatorSummary, type Page, PlaywrightCommandException, PlaywrightCommandMethodNotSupportedException, type ResponseInputItem, type ResponseItem, Stagehand, StagehandAPIError, StagehandAPIUnauthorizedError, StagehandDefaultError, StagehandDeprecationError, StagehandDomProcessError, StagehandElementNotFoundError, StagehandEnvironmentError, StagehandError, StagehandEvalError, StagehandFunctionName, StagehandHttpError, StagehandInvalidArgumentError, type StagehandMetrics, StagehandMissingArgumentError, StagehandNotImplementedError, StagehandNotInitializedError, StagehandResponseBodyError, StagehandResponseParseError, StagehandServerError, type ToolUseItem, UnsupportedModelError, UnsupportedModelProviderError, defaultExtractSchema, operatorResponseSchema, operatorSummarySchema, pageTextSchema };
|
|
869
|
+
export { type ActOptions, type ActResult, type ActionExecutionResult, type AgentAction, type AgentClientOptions, type AgentConfig, type AgentExecuteOptions, type AgentExecuteParams, type AgentExecutionOptions, type AgentHandlerOptions, type AgentOptions, type AgentProviderType, type AgentResult, AgentScreenshotProviderError, type AgentType, AnnotatedScreenshotText, type AnthropicContentBlock, type AnthropicJsonSchemaObject, type AnthropicMessage, type AnthropicTextBlock, type AnthropicToolResult, type AvailableModel, AvailableModelSchema, type Browser, type BrowserContext, type BrowserResult, BrowserbaseSessionNotFoundError, CaptchaTimeoutError, type ChatCompletionOptions, type ChatMessage, type ChatMessageContent, type ChatMessageImageContent, type ChatMessageTextContent, type ClientOptions, type ComputerCallItem, type ConstructorParams, type CreateChatCompletionOptions, CreateChatCompletionResponseError, type ExtractOptions, type ExtractResult, type FunctionCallItem, type GotoOptions, HandlerNotInitializedError, type HistoryEntry, type InitResult, LLMClient, type LLMResponse, LOG_LEVEL_NAMES, type LocalBrowserLaunchOptions, type LogLevel, type LogLine, type Logger, MissingEnvironmentVariableError, MissingLLMConfigurationError, type ModelProvider, type ObserveOptions, type ObserveResult, type OperatorResponse, type OperatorSummary, type Page, PlaywrightCommandException, PlaywrightCommandMethodNotSupportedException, type ResponseInputItem, type ResponseItem, Stagehand, StagehandAPIError, StagehandAPIUnauthorizedError, StagehandDefaultError, StagehandDeprecationError, StagehandDomProcessError, StagehandElementNotFoundError, StagehandEnvironmentError, StagehandError, StagehandEvalError, StagehandFunctionName, StagehandHttpError, StagehandInvalidArgumentError, type StagehandMetrics, StagehandMissingArgumentError, StagehandNotImplementedError, StagehandNotInitializedError, StagehandResponseBodyError, StagehandResponseParseError, StagehandServerError, type ToolUseItem, UnsupportedModelError, UnsupportedModelProviderError, type ZodPathSegments, defaultExtractSchema, operatorResponseSchema, operatorSummarySchema, pageTextSchema };
|
package/dist/index.js
CHANGED
|
@@ -188,7 +188,7 @@ Please keep the user's instructions in mind when performing actions. If the user
|
|
|
188
188
|
User Instructions:
|
|
189
189
|
${userProvidedInstructions}`;
|
|
190
190
|
}
|
|
191
|
-
function buildExtractSystemPrompt(isUsingPrintExtractedDataTool = false, useTextExtract =
|
|
191
|
+
function buildExtractSystemPrompt(isUsingPrintExtractedDataTool = false, useTextExtract = false, userProvidedInstructions) {
|
|
192
192
|
const baseContent = `You are extracting content on behalf of a user.
|
|
193
193
|
If a user asks you to extract a 'list' of information, or 'all' information,
|
|
194
194
|
YOU MUST EXTRACT ALL OF THE INFORMATION THAT THE USER REQUESTS.
|
|
@@ -207,7 +207,7 @@ ONLY print the content using the print_extracted_data tool provided.
|
|
|
207
207
|
`.trim() : "";
|
|
208
208
|
const additionalInstructions = useTextExtract ? `Once you are given the text-rendered webpage,
|
|
209
209
|
you must thoroughly and meticulously analyze it. Be very careful to ensure that you
|
|
210
|
-
do not miss any important information.` : "";
|
|
210
|
+
do not miss any important information.` : "If a user is attempting to extract links or URLs, you MUST respond with ONLY the IDs of the link elements. \nDo not attempt to extract links directly from the text unless absolutely necessary. ";
|
|
211
211
|
const userInstructions = buildUserInstructionsString(
|
|
212
212
|
userProvidedInstructions
|
|
213
213
|
);
|
|
@@ -261,7 +261,6 @@ Refined content:`
|
|
|
261
261
|
}
|
|
262
262
|
var metadataSystemPrompt = `You are an AI assistant tasked with evaluating the progress and completion status of an extraction task.
|
|
263
263
|
Analyze the extraction response and determine if the task is completed or if more information is needed.
|
|
264
|
-
|
|
265
264
|
Strictly abide by the following criteria:
|
|
266
265
|
1. Once the instruction has been satisfied by the current extraction response, ALWAYS set completion status to true and stop processing, regardless of remaining chunks.
|
|
267
266
|
2. Only set completion status to false if BOTH of these conditions are true:
|
|
@@ -1233,6 +1232,9 @@ var StagehandActHandler = class {
|
|
|
1233
1232
|
}
|
|
1234
1233
|
};
|
|
1235
1234
|
|
|
1235
|
+
// lib/handlers/extractHandler.ts
|
|
1236
|
+
var import_zod3 = require("zod");
|
|
1237
|
+
|
|
1236
1238
|
// lib/inference.ts
|
|
1237
1239
|
var import_zod2 = require("zod");
|
|
1238
1240
|
|
|
@@ -2500,11 +2502,13 @@ var StagehandExtractHandler = class {
|
|
|
2500
2502
|
level: 1
|
|
2501
2503
|
});
|
|
2502
2504
|
const outputString = tree.simplified;
|
|
2505
|
+
const idToUrlMapping = tree.idToUrl;
|
|
2506
|
+
const [transformedSchema, urlFieldPaths] = transformUrlStringsToNumericIds(schema);
|
|
2503
2507
|
const extractionResponse = yield extract({
|
|
2504
2508
|
instruction,
|
|
2505
2509
|
previouslyExtractedContent: content,
|
|
2506
2510
|
domElements: outputString,
|
|
2507
|
-
schema,
|
|
2511
|
+
schema: transformedSchema,
|
|
2508
2512
|
chunksSeen: 1,
|
|
2509
2513
|
chunksTotal: 1,
|
|
2510
2514
|
llmClient,
|
|
@@ -2565,6 +2569,9 @@ var StagehandExtractHandler = class {
|
|
|
2565
2569
|
}
|
|
2566
2570
|
});
|
|
2567
2571
|
}
|
|
2572
|
+
for (const { segments } of urlFieldPaths) {
|
|
2573
|
+
injectUrls(output, segments, idToUrlMapping);
|
|
2574
|
+
}
|
|
2568
2575
|
return output;
|
|
2569
2576
|
});
|
|
2570
2577
|
}
|
|
@@ -2679,6 +2686,172 @@ var StagehandExtractHandler = class {
|
|
|
2679
2686
|
return deduplicated;
|
|
2680
2687
|
}
|
|
2681
2688
|
};
|
|
2689
|
+
function transformUrlStringsToNumericIds(schema) {
|
|
2690
|
+
const shape = schema._def.shape();
|
|
2691
|
+
const newShape = {};
|
|
2692
|
+
const urlPaths = [];
|
|
2693
|
+
let changed = false;
|
|
2694
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
2695
|
+
const [childTransformed, childPaths] = transformSchema(value, [key]);
|
|
2696
|
+
newShape[key] = childTransformed;
|
|
2697
|
+
if (childTransformed !== value) {
|
|
2698
|
+
changed = true;
|
|
2699
|
+
}
|
|
2700
|
+
if (childPaths.length > 0) {
|
|
2701
|
+
childPaths.forEach((cp) => {
|
|
2702
|
+
urlPaths.push({ segments: [key, ...cp.segments] });
|
|
2703
|
+
});
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
const finalSchema = changed ? import_zod3.z.object(newShape) : schema;
|
|
2707
|
+
return [finalSchema, urlPaths];
|
|
2708
|
+
}
|
|
2709
|
+
function transformSchema(schema, currentPath) {
|
|
2710
|
+
var _a, _b;
|
|
2711
|
+
if (schema instanceof import_zod3.z.ZodString) {
|
|
2712
|
+
const hasUrlCheck = (_b = (_a = schema._def.checks) == null ? void 0 : _a.some((check) => check.kind === "url")) != null ? _b : false;
|
|
2713
|
+
if (hasUrlCheck) {
|
|
2714
|
+
return [
|
|
2715
|
+
import_zod3.z.number().describe("ID of element that points to a URL"),
|
|
2716
|
+
[{ segments: [] }]
|
|
2717
|
+
];
|
|
2718
|
+
}
|
|
2719
|
+
return [schema, []];
|
|
2720
|
+
}
|
|
2721
|
+
if (schema instanceof import_zod3.z.ZodObject) {
|
|
2722
|
+
const shape = schema._def.shape();
|
|
2723
|
+
const newShape = {};
|
|
2724
|
+
const urlPaths = [];
|
|
2725
|
+
let changed = false;
|
|
2726
|
+
const shapeKeys = Object.keys(shape);
|
|
2727
|
+
for (const key of shapeKeys) {
|
|
2728
|
+
const child = shape[key];
|
|
2729
|
+
const [transformedChild, childPaths] = transformSchema(child, [
|
|
2730
|
+
...currentPath,
|
|
2731
|
+
key
|
|
2732
|
+
]);
|
|
2733
|
+
if (transformedChild !== child) {
|
|
2734
|
+
changed = true;
|
|
2735
|
+
}
|
|
2736
|
+
newShape[key] = transformedChild;
|
|
2737
|
+
if (childPaths.length > 0) {
|
|
2738
|
+
for (const cp of childPaths) {
|
|
2739
|
+
urlPaths.push({ segments: [key, ...cp.segments] });
|
|
2740
|
+
}
|
|
2741
|
+
}
|
|
2742
|
+
}
|
|
2743
|
+
if (changed) {
|
|
2744
|
+
return [import_zod3.z.object(newShape), urlPaths];
|
|
2745
|
+
}
|
|
2746
|
+
return [schema, urlPaths];
|
|
2747
|
+
}
|
|
2748
|
+
if (schema instanceof import_zod3.z.ZodArray) {
|
|
2749
|
+
const itemType = schema._def.type;
|
|
2750
|
+
const [transformedItem, childPaths] = transformSchema(itemType, [
|
|
2751
|
+
...currentPath,
|
|
2752
|
+
"*"
|
|
2753
|
+
]);
|
|
2754
|
+
const changed = transformedItem !== itemType;
|
|
2755
|
+
const arrayPaths = childPaths.map((cp) => ({
|
|
2756
|
+
segments: ["*", ...cp.segments]
|
|
2757
|
+
}));
|
|
2758
|
+
if (changed) {
|
|
2759
|
+
return [import_zod3.z.array(transformedItem), arrayPaths];
|
|
2760
|
+
}
|
|
2761
|
+
return [schema, arrayPaths];
|
|
2762
|
+
}
|
|
2763
|
+
if (schema instanceof import_zod3.z.ZodUnion) {
|
|
2764
|
+
const unionOptions = schema._def.options;
|
|
2765
|
+
const newOptions = [];
|
|
2766
|
+
let changed = false;
|
|
2767
|
+
let allPaths = [];
|
|
2768
|
+
unionOptions.forEach((option, idx) => {
|
|
2769
|
+
const [newOption, childPaths] = transformSchema(option, [
|
|
2770
|
+
...currentPath,
|
|
2771
|
+
`union_${idx}`
|
|
2772
|
+
]);
|
|
2773
|
+
if (newOption !== option) {
|
|
2774
|
+
changed = true;
|
|
2775
|
+
}
|
|
2776
|
+
newOptions.push(newOption);
|
|
2777
|
+
allPaths = [...allPaths, ...childPaths];
|
|
2778
|
+
});
|
|
2779
|
+
if (changed) {
|
|
2780
|
+
return [
|
|
2781
|
+
import_zod3.z.union(newOptions),
|
|
2782
|
+
allPaths
|
|
2783
|
+
];
|
|
2784
|
+
}
|
|
2785
|
+
return [schema, allPaths];
|
|
2786
|
+
}
|
|
2787
|
+
if (schema instanceof import_zod3.z.ZodIntersection) {
|
|
2788
|
+
const leftType = schema._def.left;
|
|
2789
|
+
const rightType = schema._def.right;
|
|
2790
|
+
const [left, leftPaths] = transformSchema(leftType, [
|
|
2791
|
+
...currentPath,
|
|
2792
|
+
"intersection_left"
|
|
2793
|
+
]);
|
|
2794
|
+
const [right, rightPaths] = transformSchema(rightType, [
|
|
2795
|
+
...currentPath,
|
|
2796
|
+
"intersection_right"
|
|
2797
|
+
]);
|
|
2798
|
+
const changed = left !== leftType || right !== rightType;
|
|
2799
|
+
const allPaths = [...leftPaths, ...rightPaths];
|
|
2800
|
+
if (changed) {
|
|
2801
|
+
return [import_zod3.z.intersection(left, right), allPaths];
|
|
2802
|
+
}
|
|
2803
|
+
return [schema, allPaths];
|
|
2804
|
+
}
|
|
2805
|
+
if (schema instanceof import_zod3.z.ZodOptional) {
|
|
2806
|
+
const innerType = schema._def.innerType;
|
|
2807
|
+
const [inner, innerPaths] = transformSchema(innerType, currentPath);
|
|
2808
|
+
if (inner !== innerType) {
|
|
2809
|
+
return [import_zod3.z.optional(inner), innerPaths];
|
|
2810
|
+
}
|
|
2811
|
+
return [schema, innerPaths];
|
|
2812
|
+
}
|
|
2813
|
+
if (schema instanceof import_zod3.z.ZodNullable) {
|
|
2814
|
+
const innerType = schema._def.innerType;
|
|
2815
|
+
const [inner, innerPaths] = transformSchema(innerType, currentPath);
|
|
2816
|
+
if (inner !== innerType) {
|
|
2817
|
+
return [import_zod3.z.nullable(inner), innerPaths];
|
|
2818
|
+
}
|
|
2819
|
+
return [schema, innerPaths];
|
|
2820
|
+
}
|
|
2821
|
+
if (schema instanceof import_zod3.z.ZodEffects) {
|
|
2822
|
+
const baseSchema = schema._def.schema;
|
|
2823
|
+
const [newBaseSchema, basePaths] = transformSchema(baseSchema, currentPath);
|
|
2824
|
+
if (newBaseSchema !== baseSchema) {
|
|
2825
|
+
return [import_zod3.z.effect(newBaseSchema, schema._def.effect), basePaths];
|
|
2826
|
+
}
|
|
2827
|
+
return [schema, basePaths];
|
|
2828
|
+
}
|
|
2829
|
+
return [schema, []];
|
|
2830
|
+
}
|
|
2831
|
+
function injectUrls(obj, path4, idToUrlMapping) {
|
|
2832
|
+
if (path4.length === 0) return;
|
|
2833
|
+
const [key, ...rest] = path4;
|
|
2834
|
+
if (key === "*") {
|
|
2835
|
+
if (Array.isArray(obj)) {
|
|
2836
|
+
for (const item of obj) {
|
|
2837
|
+
injectUrls(item, rest, idToUrlMapping);
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
return;
|
|
2841
|
+
}
|
|
2842
|
+
if (obj && typeof obj === "object") {
|
|
2843
|
+
const record = obj;
|
|
2844
|
+
if (path4.length === 1) {
|
|
2845
|
+
const fieldValue = record[key];
|
|
2846
|
+
if (typeof fieldValue === "number") {
|
|
2847
|
+
const mappedUrl = idToUrlMapping[String(fieldValue)];
|
|
2848
|
+
record[key] = mappedUrl != null ? mappedUrl : ``;
|
|
2849
|
+
}
|
|
2850
|
+
} else {
|
|
2851
|
+
injectUrls(record[key], rest, idToUrlMapping);
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2682
2855
|
|
|
2683
2856
|
// lib/handlers/observeHandler.ts
|
|
2684
2857
|
var StagehandObserveHandler = class {
|
|
@@ -5647,7 +5820,7 @@ var GroqClient = class extends LLMClient {
|
|
|
5647
5820
|
|
|
5648
5821
|
// lib/llm/OpenAIClient.ts
|
|
5649
5822
|
var import_openai3 = __toESM(require("openai"));
|
|
5650
|
-
var
|
|
5823
|
+
var import_zod4 = require("openai/helpers/zod");
|
|
5651
5824
|
var import_zod_to_json_schema6 = __toESM(require("zod-to-json-schema"));
|
|
5652
5825
|
var OpenAIClient = class extends LLMClient {
|
|
5653
5826
|
constructor({
|
|
@@ -5835,7 +6008,7 @@ ${parsedSchema}
|
|
|
5835
6008
|
throw error;
|
|
5836
6009
|
}
|
|
5837
6010
|
} else {
|
|
5838
|
-
responseFormat = (0,
|
|
6011
|
+
responseFormat = (0, import_zod4.zodResponseFormat)(
|
|
5839
6012
|
options.response_model.schema,
|
|
5840
6013
|
options.response_model.name
|
|
5841
6014
|
);
|
|
@@ -7697,12 +7870,12 @@ var StagehandAgentHandler = class {
|
|
|
7697
7870
|
};
|
|
7698
7871
|
|
|
7699
7872
|
// types/operator.ts
|
|
7700
|
-
var
|
|
7701
|
-
var operatorResponseSchema =
|
|
7702
|
-
reasoning:
|
|
7873
|
+
var import_zod5 = require("zod");
|
|
7874
|
+
var operatorResponseSchema = import_zod5.z.object({
|
|
7875
|
+
reasoning: import_zod5.z.string().describe(
|
|
7703
7876
|
"The reasoning for the step taken. If this step's method is `close`, the goal was to extract data, and the task was successful, state the data that was extracted."
|
|
7704
7877
|
),
|
|
7705
|
-
method:
|
|
7878
|
+
method: import_zod5.z.enum([
|
|
7706
7879
|
"act",
|
|
7707
7880
|
"extract",
|
|
7708
7881
|
"goto",
|
|
@@ -7718,7 +7891,7 @@ var operatorResponseSchema = import_zod4.z.object({
|
|
|
7718
7891
|
wait: Wait for a period of time.
|
|
7719
7892
|
navback: Navigate back to the previous page. Do not navigate back if you are already on the first page.
|
|
7720
7893
|
refresh: Refresh the page.`),
|
|
7721
|
-
parameters:
|
|
7894
|
+
parameters: import_zod5.z.string().describe(
|
|
7722
7895
|
`The parameter for the action. Only pass in a parameter for the following methods:
|
|
7723
7896
|
- act: The action to perform. e.g. "click on the submit button" or "type [email] into the email input field and press enter"
|
|
7724
7897
|
- extract: The data to extract. e.g. "the title of the article". If you want to extract all of the text on the page, leave this undefined.
|
|
@@ -7726,12 +7899,12 @@ var operatorResponseSchema = import_zod4.z.object({
|
|
|
7726
7899
|
- goto: The URL to navigate to. e.g. "https://www.google.com"
|
|
7727
7900
|
The other methods do not require a parameter.`
|
|
7728
7901
|
).optional(),
|
|
7729
|
-
taskComplete:
|
|
7902
|
+
taskComplete: import_zod5.z.boolean().describe(
|
|
7730
7903
|
"Whether the task is complete. If true, the task is complete and no more steps are needed. If you chose to close the task because the goal is not achievable, set this to false."
|
|
7731
7904
|
)
|
|
7732
7905
|
});
|
|
7733
|
-
var operatorSummarySchema =
|
|
7734
|
-
answer:
|
|
7906
|
+
var operatorSummarySchema = import_zod5.z.object({
|
|
7907
|
+
answer: import_zod5.z.string().describe("The final answer to the original instruction.")
|
|
7735
7908
|
});
|
|
7736
7909
|
|
|
7737
7910
|
// lib/handlers/operatorHandler.ts
|
|
@@ -8133,8 +8306,8 @@ var LOG_LEVEL_NAMES = {
|
|
|
8133
8306
|
};
|
|
8134
8307
|
|
|
8135
8308
|
// types/model.ts
|
|
8136
|
-
var
|
|
8137
|
-
var AvailableModelSchema =
|
|
8309
|
+
var import_zod6 = require("zod");
|
|
8310
|
+
var AvailableModelSchema = import_zod6.z.enum([
|
|
8138
8311
|
"gpt-4o",
|
|
8139
8312
|
"gpt-4o-mini",
|
|
8140
8313
|
"gpt-4o-2024-08-06",
|
|
@@ -8317,18 +8490,6 @@ function getBrowser(apiKey, projectId, env = "LOCAL", headless = false, logger,
|
|
|
8317
8490
|
}
|
|
8318
8491
|
}
|
|
8319
8492
|
});
|
|
8320
|
-
if (localBrowserLaunchOptions) {
|
|
8321
|
-
logger({
|
|
8322
|
-
category: "init",
|
|
8323
|
-
message: "local browser launch options",
|
|
8324
|
-
auxiliary: {
|
|
8325
|
-
localLaunchOptions: {
|
|
8326
|
-
value: JSON.stringify(localBrowserLaunchOptions),
|
|
8327
|
-
type: "string"
|
|
8328
|
-
}
|
|
8329
|
-
}
|
|
8330
|
-
});
|
|
8331
|
-
}
|
|
8332
8493
|
if (localBrowserLaunchOptions == null ? void 0 : localBrowserLaunchOptions.cdpUrl) {
|
|
8333
8494
|
if (!localBrowserLaunchOptions.cdpUrl.includes("connect.connect")) {
|
|
8334
8495
|
logger({
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
1
|
+
import { z, ZodTypeAny } from "zod";
|
|
2
|
+
import { ZodPathSegments } from "../../types/stagehand";
|
|
2
3
|
import { LLMClient } from "../llm/LLMClient";
|
|
3
4
|
import { StagehandPage } from "../StagehandPage";
|
|
4
5
|
import { Stagehand } from "../index";
|
|
@@ -97,3 +98,35 @@ export declare class StagehandExtractHandler {
|
|
|
97
98
|
*/
|
|
98
99
|
private deduplicateAnnotations;
|
|
99
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Scans the provided Zod schema for any `z.string().url()` fields and
|
|
103
|
+
* replaces them with `z.number()`.
|
|
104
|
+
*
|
|
105
|
+
* @param schema - The Zod object schema to transform.
|
|
106
|
+
* @returns A tuple containing:
|
|
107
|
+
* 1. The transformed schema (or the original schema if no changes were needed).
|
|
108
|
+
* 2. An array of {@link ZodPathSegments} objects representing all the replaced URL fields,
|
|
109
|
+
* with each path segment showing where in the schema the replacement occurred.
|
|
110
|
+
*/
|
|
111
|
+
export declare function transformUrlStringsToNumericIds<T extends z.ZodObject<z.ZodRawShape>>(schema: T): [T, ZodPathSegments[]];
|
|
112
|
+
/**
|
|
113
|
+
* Recursively traverses a given Zod schema, scanning for any fields of type `z.string().url()`.
|
|
114
|
+
* For each such field, it replaces the `z.string().url()` with `z.number()`.
|
|
115
|
+
*
|
|
116
|
+
* This function is used internally by higher-level utilities (e.g., transforming entire object schemas)
|
|
117
|
+
* and handles nested objects, arrays, unions, intersections, optionals.
|
|
118
|
+
*
|
|
119
|
+
* @param schema - The Zod schema to transform.
|
|
120
|
+
* @param currentPath - An array of string/number keys representing the current schema path (used internally for recursion).
|
|
121
|
+
* @returns A two-element tuple:
|
|
122
|
+
* 1. The updated Zod schema, with any `.url()` fields replaced by `z.number()`.
|
|
123
|
+
* 2. An array of {@link ZodPathSegments} objects representing each replaced field, including the path segments.
|
|
124
|
+
*/
|
|
125
|
+
export declare function transformSchema(schema: ZodTypeAny, currentPath: Array<string | number>): [ZodTypeAny, ZodPathSegments[]];
|
|
126
|
+
/**
|
|
127
|
+
* Once we get the final extracted object that has numeric IDs in place of URLs,
|
|
128
|
+
* use `injectUrls` to walk the object and replace numeric IDs
|
|
129
|
+
* with the real URL strings from idToUrlMapping. The `path` may include `*`
|
|
130
|
+
* for array indices (indicating "all items in the array").
|
|
131
|
+
*/
|
|
132
|
+
export declare function injectUrls(obj: unknown, path: Array<string | number>, idToUrlMapping: Record<string, string>): void;
|
|
@@ -269,3 +269,20 @@ export interface HistoryEntry {
|
|
|
269
269
|
result: unknown;
|
|
270
270
|
timestamp: string;
|
|
271
271
|
}
|
|
272
|
+
/**
|
|
273
|
+
* Represents a path through a Zod schema from the root object down to a
|
|
274
|
+
* particular field. The `segments` array describes the chain of keys/indices.
|
|
275
|
+
*
|
|
276
|
+
* - **String** segments indicate object property names.
|
|
277
|
+
* - **Number** segments indicate array indices.
|
|
278
|
+
*
|
|
279
|
+
* For example, `["users", 0, "homepage"]` might describe reaching
|
|
280
|
+
* the `homepage` field in `schema.users[0].homepage`.
|
|
281
|
+
*/
|
|
282
|
+
export interface ZodPathSegments {
|
|
283
|
+
/**
|
|
284
|
+
* The ordered list of keys/indices leading from the schema root
|
|
285
|
+
* to the targeted field.
|
|
286
|
+
*/
|
|
287
|
+
segments: Array<string | number>;
|
|
288
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
1
|
+
import { z, ZodTypeAny } from "zod";
|
|
2
2
|
import { LogLine } from "../../types/log";
|
|
3
|
+
import { ZodPathSegments } from "../../types/stagehand";
|
|
3
4
|
import { TextAnnotation } from "../../types/textannotation";
|
|
4
5
|
import { extract } from "../inference";
|
|
5
6
|
import { LLMClient } from "../llm/LLMClient";
|
|
@@ -420,12 +421,18 @@ export class StagehandExtractHandler {
|
|
|
420
421
|
level: 1,
|
|
421
422
|
});
|
|
422
423
|
const outputString = tree.simplified;
|
|
424
|
+
const idToUrlMapping = tree.idToUrl;
|
|
423
425
|
|
|
426
|
+
// Transform user defined schema to replace string().url() with .number()
|
|
427
|
+
const [transformedSchema, urlFieldPaths] =
|
|
428
|
+
transformUrlStringsToNumericIds(schema);
|
|
429
|
+
|
|
430
|
+
// call extract inference with transformed schema
|
|
424
431
|
const extractionResponse = await extract({
|
|
425
432
|
instruction,
|
|
426
433
|
previouslyExtractedContent: content,
|
|
427
434
|
domElements: outputString,
|
|
428
|
-
schema,
|
|
435
|
+
schema: transformedSchema,
|
|
429
436
|
chunksSeen: 1,
|
|
430
437
|
chunksTotal: 1,
|
|
431
438
|
llmClient,
|
|
@@ -486,9 +493,14 @@ export class StagehandExtractHandler {
|
|
|
486
493
|
},
|
|
487
494
|
});
|
|
488
495
|
}
|
|
489
|
-
return output;
|
|
490
|
-
}
|
|
491
496
|
|
|
497
|
+
// revert to original schema and populate with URLs
|
|
498
|
+
for (const { segments } of urlFieldPaths) {
|
|
499
|
+
injectUrls(output, segments, idToUrlMapping);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return output as z.infer<T>;
|
|
503
|
+
}
|
|
492
504
|
/**
|
|
493
505
|
* Get the width, height, and offsets of either the entire page or a specific element.
|
|
494
506
|
* (Matches your existing getTargetDimensions logic, just adapted to accept a string | undefined.)
|
|
@@ -642,3 +654,243 @@ export class StagehandExtractHandler {
|
|
|
642
654
|
return deduplicated;
|
|
643
655
|
}
|
|
644
656
|
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Scans the provided Zod schema for any `z.string().url()` fields and
|
|
660
|
+
* replaces them with `z.number()`.
|
|
661
|
+
*
|
|
662
|
+
* @param schema - The Zod object schema to transform.
|
|
663
|
+
* @returns A tuple containing:
|
|
664
|
+
* 1. The transformed schema (or the original schema if no changes were needed).
|
|
665
|
+
* 2. An array of {@link ZodPathSegments} objects representing all the replaced URL fields,
|
|
666
|
+
* with each path segment showing where in the schema the replacement occurred.
|
|
667
|
+
*/
|
|
668
|
+
export function transformUrlStringsToNumericIds<
|
|
669
|
+
T extends z.ZodObject<z.ZodRawShape>,
|
|
670
|
+
>(schema: T): [T, ZodPathSegments[]] {
|
|
671
|
+
const shape = schema._def.shape();
|
|
672
|
+
const newShape: Record<string, ZodTypeAny> = {};
|
|
673
|
+
const urlPaths: ZodPathSegments[] = [];
|
|
674
|
+
let changed = false;
|
|
675
|
+
|
|
676
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
677
|
+
const [childTransformed, childPaths] = transformSchema(value, [key]);
|
|
678
|
+
newShape[key] = childTransformed;
|
|
679
|
+
if (childTransformed !== value) {
|
|
680
|
+
changed = true;
|
|
681
|
+
}
|
|
682
|
+
if (childPaths.length > 0) {
|
|
683
|
+
childPaths.forEach((cp) => {
|
|
684
|
+
urlPaths.push({ segments: [key, ...cp.segments] });
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
const finalSchema = changed ? z.object(newShape) : schema;
|
|
690
|
+
return [finalSchema as T, urlPaths];
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Recursively traverses a given Zod schema, scanning for any fields of type `z.string().url()`.
|
|
695
|
+
* For each such field, it replaces the `z.string().url()` with `z.number()`.
|
|
696
|
+
*
|
|
697
|
+
* This function is used internally by higher-level utilities (e.g., transforming entire object schemas)
|
|
698
|
+
* and handles nested objects, arrays, unions, intersections, optionals.
|
|
699
|
+
*
|
|
700
|
+
* @param schema - The Zod schema to transform.
|
|
701
|
+
* @param currentPath - An array of string/number keys representing the current schema path (used internally for recursion).
|
|
702
|
+
* @returns A two-element tuple:
|
|
703
|
+
* 1. The updated Zod schema, with any `.url()` fields replaced by `z.number()`.
|
|
704
|
+
* 2. An array of {@link ZodPathSegments} objects representing each replaced field, including the path segments.
|
|
705
|
+
*/
|
|
706
|
+
export function transformSchema(
|
|
707
|
+
schema: ZodTypeAny,
|
|
708
|
+
currentPath: Array<string | number>,
|
|
709
|
+
): [ZodTypeAny, ZodPathSegments[]] {
|
|
710
|
+
// 1) If it's a string with .url(), convert to z.number()
|
|
711
|
+
if (schema instanceof z.ZodString) {
|
|
712
|
+
const hasUrlCheck =
|
|
713
|
+
schema._def.checks?.some((check) => check.kind === "url") ?? false;
|
|
714
|
+
if (hasUrlCheck) {
|
|
715
|
+
return [
|
|
716
|
+
z.number().describe("ID of element that points to a URL"),
|
|
717
|
+
[{ segments: [] }],
|
|
718
|
+
];
|
|
719
|
+
}
|
|
720
|
+
return [schema, []];
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// 2) If it's an object, transform each field
|
|
724
|
+
if (schema instanceof z.ZodObject) {
|
|
725
|
+
// The shape is a raw object containing fields keyed by string (no symbols):
|
|
726
|
+
const shape = schema._def.shape() as Record<string, ZodTypeAny>;
|
|
727
|
+
const newShape: Record<string, ZodTypeAny> = {};
|
|
728
|
+
const urlPaths: ZodPathSegments[] = [];
|
|
729
|
+
let changed = false;
|
|
730
|
+
|
|
731
|
+
const shapeKeys = Object.keys(shape);
|
|
732
|
+
|
|
733
|
+
for (const key of shapeKeys) {
|
|
734
|
+
const child = shape[key];
|
|
735
|
+
const [transformedChild, childPaths] = transformSchema(child, [
|
|
736
|
+
...currentPath,
|
|
737
|
+
key,
|
|
738
|
+
]);
|
|
739
|
+
|
|
740
|
+
if (transformedChild !== child) {
|
|
741
|
+
changed = true;
|
|
742
|
+
}
|
|
743
|
+
newShape[key] = transformedChild;
|
|
744
|
+
|
|
745
|
+
if (childPaths.length > 0) {
|
|
746
|
+
for (const cp of childPaths) {
|
|
747
|
+
urlPaths.push({ segments: [key, ...cp.segments] });
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
if (changed) {
|
|
753
|
+
return [z.object(newShape), urlPaths];
|
|
754
|
+
}
|
|
755
|
+
return [schema, urlPaths];
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// 3) If it's an array, transform its item type
|
|
759
|
+
if (schema instanceof z.ZodArray) {
|
|
760
|
+
const itemType = schema._def.type as ZodTypeAny;
|
|
761
|
+
const [transformedItem, childPaths] = transformSchema(itemType, [
|
|
762
|
+
...currentPath,
|
|
763
|
+
"*",
|
|
764
|
+
]);
|
|
765
|
+
const changed = transformedItem !== itemType;
|
|
766
|
+
const arrayPaths: ZodPathSegments[] = childPaths.map((cp) => ({
|
|
767
|
+
segments: ["*", ...cp.segments],
|
|
768
|
+
}));
|
|
769
|
+
|
|
770
|
+
if (changed) {
|
|
771
|
+
return [z.array(transformedItem), arrayPaths];
|
|
772
|
+
}
|
|
773
|
+
return [schema, arrayPaths];
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// 4) If it's a union, transform each option
|
|
777
|
+
if (schema instanceof z.ZodUnion) {
|
|
778
|
+
// Cast the union’s options to an array of ZodTypeAny
|
|
779
|
+
const unionOptions = schema._def.options as ZodTypeAny[];
|
|
780
|
+
const newOptions: ZodTypeAny[] = [];
|
|
781
|
+
let changed = false;
|
|
782
|
+
let allPaths: ZodPathSegments[] = [];
|
|
783
|
+
|
|
784
|
+
unionOptions.forEach((option: ZodTypeAny, idx: number) => {
|
|
785
|
+
const [newOption, childPaths] = transformSchema(option, [
|
|
786
|
+
...currentPath,
|
|
787
|
+
`union_${idx}`,
|
|
788
|
+
]);
|
|
789
|
+
if (newOption !== option) {
|
|
790
|
+
changed = true;
|
|
791
|
+
}
|
|
792
|
+
newOptions.push(newOption);
|
|
793
|
+
allPaths = [...allPaths, ...childPaths];
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
if (changed) {
|
|
797
|
+
// We assume at least two options remain:
|
|
798
|
+
return [
|
|
799
|
+
z.union(newOptions as [ZodTypeAny, ZodTypeAny, ...ZodTypeAny[]]),
|
|
800
|
+
allPaths,
|
|
801
|
+
];
|
|
802
|
+
}
|
|
803
|
+
return [schema, allPaths];
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
// 5) If it's an intersection, transform left and right
|
|
807
|
+
if (schema instanceof z.ZodIntersection) {
|
|
808
|
+
const leftType = schema._def.left as ZodTypeAny;
|
|
809
|
+
const rightType = schema._def.right as ZodTypeAny;
|
|
810
|
+
|
|
811
|
+
const [left, leftPaths] = transformSchema(leftType, [
|
|
812
|
+
...currentPath,
|
|
813
|
+
"intersection_left",
|
|
814
|
+
]);
|
|
815
|
+
const [right, rightPaths] = transformSchema(rightType, [
|
|
816
|
+
...currentPath,
|
|
817
|
+
"intersection_right",
|
|
818
|
+
]);
|
|
819
|
+
const changed = left !== leftType || right !== rightType;
|
|
820
|
+
const allPaths = [...leftPaths, ...rightPaths];
|
|
821
|
+
if (changed) {
|
|
822
|
+
return [z.intersection(left, right), allPaths];
|
|
823
|
+
}
|
|
824
|
+
return [schema, allPaths];
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// 6) If it's optional, transform inner
|
|
828
|
+
if (schema instanceof z.ZodOptional) {
|
|
829
|
+
const innerType = schema._def.innerType as ZodTypeAny;
|
|
830
|
+
const [inner, innerPaths] = transformSchema(innerType, currentPath);
|
|
831
|
+
if (inner !== innerType) {
|
|
832
|
+
return [z.optional(inner), innerPaths];
|
|
833
|
+
}
|
|
834
|
+
return [schema, innerPaths];
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// 7) If it's nullable, transform inner
|
|
838
|
+
if (schema instanceof z.ZodNullable) {
|
|
839
|
+
const innerType = schema._def.innerType as ZodTypeAny;
|
|
840
|
+
const [inner, innerPaths] = transformSchema(innerType, currentPath);
|
|
841
|
+
if (inner !== innerType) {
|
|
842
|
+
return [z.nullable(inner), innerPaths];
|
|
843
|
+
}
|
|
844
|
+
return [schema, innerPaths];
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// 8) If it's an effect, transform base schema
|
|
848
|
+
if (schema instanceof z.ZodEffects) {
|
|
849
|
+
const baseSchema = schema._def.schema as ZodTypeAny;
|
|
850
|
+
const [newBaseSchema, basePaths] = transformSchema(baseSchema, currentPath);
|
|
851
|
+
if (newBaseSchema !== baseSchema) {
|
|
852
|
+
return [z.effect(newBaseSchema, schema._def.effect), basePaths];
|
|
853
|
+
}
|
|
854
|
+
return [schema, basePaths];
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// 9) If none of the above, return as-is
|
|
858
|
+
return [schema, []];
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* Once we get the final extracted object that has numeric IDs in place of URLs,
|
|
863
|
+
* use `injectUrls` to walk the object and replace numeric IDs
|
|
864
|
+
* with the real URL strings from idToUrlMapping. The `path` may include `*`
|
|
865
|
+
* for array indices (indicating "all items in the array").
|
|
866
|
+
*/
|
|
867
|
+
export function injectUrls(
|
|
868
|
+
obj: unknown,
|
|
869
|
+
path: Array<string | number>,
|
|
870
|
+
idToUrlMapping: Record<string, string>,
|
|
871
|
+
): void {
|
|
872
|
+
if (path.length === 0) return;
|
|
873
|
+
const [key, ...rest] = path;
|
|
874
|
+
|
|
875
|
+
if (key === "*") {
|
|
876
|
+
if (Array.isArray(obj)) {
|
|
877
|
+
for (const item of obj) {
|
|
878
|
+
injectUrls(item, rest, idToUrlMapping);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
if (obj && typeof obj === "object") {
|
|
885
|
+
const record = obj as Record<string | number, unknown>;
|
|
886
|
+
if (path.length === 1) {
|
|
887
|
+
const fieldValue = record[key];
|
|
888
|
+
if (typeof fieldValue === "number") {
|
|
889
|
+
const mappedUrl = idToUrlMapping[String(fieldValue)];
|
|
890
|
+
record[key] = mappedUrl ?? ``;
|
|
891
|
+
}
|
|
892
|
+
} else {
|
|
893
|
+
injectUrls(record[key], rest, idToUrlMapping);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
package/lib/index.ts
CHANGED
|
@@ -236,19 +236,6 @@ async function getBrowser(
|
|
|
236
236
|
},
|
|
237
237
|
});
|
|
238
238
|
|
|
239
|
-
if (localBrowserLaunchOptions) {
|
|
240
|
-
logger({
|
|
241
|
-
category: "init",
|
|
242
|
-
message: "local browser launch options",
|
|
243
|
-
auxiliary: {
|
|
244
|
-
localLaunchOptions: {
|
|
245
|
-
value: JSON.stringify(localBrowserLaunchOptions),
|
|
246
|
-
type: "string",
|
|
247
|
-
},
|
|
248
|
-
},
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
|
|
252
239
|
if (localBrowserLaunchOptions?.cdpUrl) {
|
|
253
240
|
if (!localBrowserLaunchOptions.cdpUrl.includes("connect.connect")) {
|
|
254
241
|
logger({
|
package/lib/prompt.ts
CHANGED
|
@@ -18,7 +18,7 @@ ${userProvidedInstructions}`;
|
|
|
18
18
|
// extract
|
|
19
19
|
export function buildExtractSystemPrompt(
|
|
20
20
|
isUsingPrintExtractedDataTool: boolean = false,
|
|
21
|
-
useTextExtract: boolean =
|
|
21
|
+
useTextExtract: boolean = false,
|
|
22
22
|
userProvidedInstructions?: string,
|
|
23
23
|
): ChatMessage {
|
|
24
24
|
const baseContent = `You are extracting content on behalf of a user.
|
|
@@ -51,7 +51,8 @@ ONLY print the content using the print_extracted_data tool provided.
|
|
|
51
51
|
? `Once you are given the text-rendered webpage,
|
|
52
52
|
you must thoroughly and meticulously analyze it. Be very careful to ensure that you
|
|
53
53
|
do not miss any important information.`
|
|
54
|
-
: ""
|
|
54
|
+
: "If a user is attempting to extract links or URLs, you MUST respond with ONLY the IDs of the link elements. \n" +
|
|
55
|
+
"Do not attempt to extract links directly from the text unless absolutely necessary. ";
|
|
55
56
|
|
|
56
57
|
const userInstructions = buildUserInstructionsString(
|
|
57
58
|
userProvidedInstructions,
|
|
@@ -119,7 +120,6 @@ Refined content:`,
|
|
|
119
120
|
|
|
120
121
|
const metadataSystemPrompt = `You are an AI assistant tasked with evaluating the progress and completion status of an extraction task.
|
|
121
122
|
Analyze the extraction response and determine if the task is completed or if more information is needed.
|
|
122
|
-
|
|
123
123
|
Strictly abide by the following criteria:
|
|
124
124
|
1. Once the instruction has been satisfied by the current extraction response, ALWAYS set completion status to true and stop processing, regardless of remaining chunks.
|
|
125
125
|
2. Only set completion status to false if BOTH of these conditions are true:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@browserbasehq/stagehand",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0-alpha-8814af9ece99fddc3dd9fb32671d0513a3a00c67",
|
|
4
4
|
"description": "An AI web browsing framework focused on simplicity and extensibility.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|