-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Feat/stagehand #589
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Feat/stagehand #589
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -18,6 +18,32 @@ const requestSchema = z.object({ | |||||||||||||||||
| selector: z.string().nullable().optional(), | ||||||||||||||||||
| apiKey: z.string(), | ||||||||||||||||||
| url: z.string().url(), | ||||||||||||||||||
| model: z.enum([ | ||||||||||||||||||
| "gpt-4o", | ||||||||||||||||||
| "gpt-4o-mini", | ||||||||||||||||||
| "gpt-4o-2024-08-06", | ||||||||||||||||||
| "gpt-4.5-preview", | ||||||||||||||||||
| "claude-3-5-sonnet-latest", | ||||||||||||||||||
| "claude-3-5-sonnet-20241022", | ||||||||||||||||||
| "claude-3-5-sonnet-20240620", | ||||||||||||||||||
| "claude-3-7-sonnet-latest", | ||||||||||||||||||
| "claude-3-7-sonnet-20250219", | ||||||||||||||||||
| "o1-mini", | ||||||||||||||||||
| "o1-preview", | ||||||||||||||||||
| "o3-mini", | ||||||||||||||||||
| "gemini-2.0-flash", | ||||||||||||||||||
| "gemini-1.5-flash", | ||||||||||||||||||
| "gemini-1.5-pro", | ||||||||||||||||||
| "gemini-1.5-flash-8b", | ||||||||||||||||||
| "gemini-2.0-flash-lite", | ||||||||||||||||||
| "gemini-2.0-flash", | ||||||||||||||||||
| "gemini-2.5-pro-preview-03-25", | ||||||||||||||||||
| "cerebras-llama-3.3-70b", | ||||||||||||||||||
| "cerebras-llama-3.1-8b", | ||||||||||||||||||
| "groq-llama-3.3-70b-versatile", | ||||||||||||||||||
| "groq-llama-3.3-70b-specdec" | ||||||||||||||||||
| ]), | ||||||||||||||||||
| env: z.enum(['browserbase', 'local']), | ||||||||||||||||||
| }) | ||||||||||||||||||
|
|
||||||||||||||||||
| export async function POST(request: NextRequest) { | ||||||||||||||||||
|
|
@@ -34,6 +60,7 @@ export async function POST(request: NextRequest) { | |||||||||||||||||
| const validationResult = requestSchema.safeParse(body) | ||||||||||||||||||
|
|
||||||||||||||||||
| if (!validationResult.success) { | ||||||||||||||||||
|
|
||||||||||||||||||
| logger.error('Invalid request body', { errors: validationResult.error.errors }) | ||||||||||||||||||
| return NextResponse.json( | ||||||||||||||||||
| { error: 'Invalid request parameters', details: validationResult.error.errors }, | ||||||||||||||||||
|
|
@@ -42,7 +69,7 @@ export async function POST(request: NextRequest) { | |||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| const params = validationResult.data | ||||||||||||||||||
| const { url: rawUrl, instruction, selector, useTextExtract, apiKey, schema } = params | ||||||||||||||||||
| const { url: rawUrl, instruction, selector, useTextExtract, apiKey, schema, model, env } = params | ||||||||||||||||||
| const url = normalizeUrl(rawUrl) | ||||||||||||||||||
|
|
||||||||||||||||||
| logger.info('Starting Stagehand extraction process', { | ||||||||||||||||||
|
|
@@ -61,7 +88,7 @@ export async function POST(request: NextRequest) { | |||||||||||||||||
| ) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| if (!BROWSERBASE_API_KEY || !BROWSERBASE_PROJECT_ID) { | ||||||||||||||||||
| if (env === 'browserbase' && (!BROWSERBASE_API_KEY || !BROWSERBASE_PROJECT_ID)) { | ||||||||||||||||||
| logger.error('Missing required environment variables', { | ||||||||||||||||||
| hasBrowserbaseApiKey: !!BROWSERBASE_API_KEY, | ||||||||||||||||||
| hasBrowserbaseProjectId: !!BROWSERBASE_PROJECT_ID, | ||||||||||||||||||
|
|
@@ -73,24 +100,30 @@ export async function POST(request: NextRequest) { | |||||||||||||||||
| ) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| if (!apiKey || typeof apiKey !== 'string' || !apiKey.startsWith('sk-')) { | ||||||||||||||||||
| logger.error('Invalid OpenAI API key format') | ||||||||||||||||||
| return NextResponse.json({ error: 'Invalid OpenAI API key format' }, { status: 400 }) | ||||||||||||||||||
| if (!apiKey || typeof apiKey !== 'string') { | ||||||||||||||||||
| logger.error('Invalid API key format') | ||||||||||||||||||
| return NextResponse.json({ error: 'Invalid API key format' }, { status: 400 }) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| try { | ||||||||||||||||||
| logger.info('Initializing Stagehand with Browserbase') | ||||||||||||||||||
| stagehand = new Stagehand({ | ||||||||||||||||||
| env: 'BROWSERBASE', | ||||||||||||||||||
| env: env === 'browserbase' ? 'BROWSERBASE' : 'LOCAL', | ||||||||||||||||||
| apiKey: BROWSERBASE_API_KEY, | ||||||||||||||||||
| projectId: BROWSERBASE_PROJECT_ID, | ||||||||||||||||||
|
Comment on lines
+111
to
113
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: apiKey and projectId are always passed to Stagehand even when env is 'local'. Only pass these for browserbase environment
Suggested change
|
||||||||||||||||||
| verbose: 1, | ||||||||||||||||||
| logger: (msg) => logger.info(typeof msg === 'string' ? msg : JSON.stringify(msg)), | ||||||||||||||||||
| disablePino: true, | ||||||||||||||||||
| modelName: 'gpt-4o', | ||||||||||||||||||
| modelName: model, | ||||||||||||||||||
| modelClientOptions: { | ||||||||||||||||||
| apiKey: apiKey, | ||||||||||||||||||
| }, | ||||||||||||||||||
| ...(env === 'local' && { | ||||||||||||||||||
| localBrowserLaunchOptions: { | ||||||||||||||||||
| headless: true, | ||||||||||||||||||
| executablePath: "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe", | ||||||||||||||||||
| } | ||||||||||||||||||
| }) | ||||||||||||||||||
| }) | ||||||||||||||||||
|
|
||||||||||||||||||
| logger.info('Starting stagehand.init()') | ||||||||||||||||||
|
|
||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -33,12 +33,55 @@ export const StagehandBlock: BlockConfig<StagehandExtractResponse> = { | |||||||||||||||||||||||
| layout: 'full', | ||||||||||||||||||||||||
| placeholder: 'Enter detailed instructions for what data to extract from the page...', | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| id: "env", | ||||||||||||||||||||||||
| title: 'Environment', | ||||||||||||||||||||||||
| type: 'combobox', | ||||||||||||||||||||||||
| layout: 'full', | ||||||||||||||||||||||||
| placeholder: 'Select the environment for extraction', | ||||||||||||||||||||||||
| options: [ | ||||||||||||||||||||||||
| { id: 'browserbase', label: 'Browserbase' }, | ||||||||||||||||||||||||
| { id: "local", label: 'Local (Chromium)' }, | ||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| id: "model", | ||||||||||||||||||||||||
| title: 'Model', | ||||||||||||||||||||||||
| type: "combobox", | ||||||||||||||||||||||||
| placeholder: 'Select the model to use for extraction', | ||||||||||||||||||||||||
| layout: 'full', | ||||||||||||||||||||||||
| options: [ | ||||||||||||||||||||||||
| { id: 'gpt-4o', label: 'gpt-4o' }, | ||||||||||||||||||||||||
| { id: 'gpt-4o-mini', label: 'gpt-4o-mini' }, | ||||||||||||||||||||||||
| { id: 'gpt-4o-2024-08-06', label: 'gpt-4o-2024-08-06' }, | ||||||||||||||||||||||||
| { id: 'gpt-4.5-preview', label: 'gpt-4.5-preview' }, | ||||||||||||||||||||||||
| { id: 'claude-3-5-sonnet-latest', label: 'claude-3-5-sonnet-latest' }, | ||||||||||||||||||||||||
| { id: 'claude-3-5-sonnet-20241022', label: 'claude-3-5-sonnet-20241022' }, | ||||||||||||||||||||||||
| { id: 'claude-3-5-sonnet-20240620', label: 'claude-3-5-sonnet-20240620' }, | ||||||||||||||||||||||||
| { id: 'claude-3-7-sonnet-latest', label: 'claude-3-7-sonnet-latest' }, | ||||||||||||||||||||||||
| { id: 'claude-3-7-sonnet-20250219', label: 'claude-3-7-sonnet-20250219' }, | ||||||||||||||||||||||||
| { id: 'o1-mini', label: 'o1-mini' }, | ||||||||||||||||||||||||
| { id: 'o1-preview', label: 'o1-preview' }, | ||||||||||||||||||||||||
| { id: 'o3-mini', label: 'o3-mini' }, | ||||||||||||||||||||||||
| { id: 'gemini-2.0-flash', label: 'gemini-2.0-flash' }, | ||||||||||||||||||||||||
| { id: 'gemini-1.5-flash', label: 'gemini-1.5-flash' }, | ||||||||||||||||||||||||
| { id: 'gemini-1.5-pro', label: 'gemini-1.5-pro' }, | ||||||||||||||||||||||||
| { id: 'gemini-1.5-flash-8b', label: 'gemini-1.5-flash-8b' }, | ||||||||||||||||||||||||
| { id: 'gemini-2.0-flash-lite', label: 'gemini-2.0-flash-lite' }, | ||||||||||||||||||||||||
| { id: 'gemini-2.0-flash', label: 'gemini-2.0-flash' }, | ||||||||||||||||||||||||
|
Comment on lines
+66
to
+71
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: Duplicate entry 'gemini-2.0-flash' in model options. Remove one instance to prevent confusion.
Suggested change
|
||||||||||||||||||||||||
| { id: 'gemini-2.5-pro-preview-03-25', label: 'gemini-2.5-pro-preview-03-25' }, | ||||||||||||||||||||||||
| { id: 'cerebras-llama-3.3-70b', label: 'cerebras-llama-3.3-70b' }, | ||||||||||||||||||||||||
| { id: 'cerebras-llama-3.1-8b', label: 'cerebras-llama-3.1-8b' }, | ||||||||||||||||||||||||
| { id: 'groq-llama-3.3-70b-versatile', label: 'groq-llama-3.3-70b-versatile' }, | ||||||||||||||||||||||||
| { id: 'groq-llama-3.3-70b-specdec', label: 'groq-llama-3.3-70b-specdec' } | ||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
|
Comment on lines
+47
to
+78
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: Consider moving model options to a separate config file to improve maintainability |
||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| id: 'apiKey', | ||||||||||||||||||||||||
| title: 'OpenAI API Key', | ||||||||||||||||||||||||
| title: 'API Key', | ||||||||||||||||||||||||
| type: 'short-input', | ||||||||||||||||||||||||
| layout: 'full', | ||||||||||||||||||||||||
| placeholder: 'Enter your OpenAI API key', | ||||||||||||||||||||||||
| placeholder: 'Enter your API key', | ||||||||||||||||||||||||
| password: true, | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
|
|
@@ -62,6 +105,8 @@ export const StagehandBlock: BlockConfig<StagehandExtractResponse> = { | |||||||||||||||||||||||
| instruction: { type: 'string', required: true }, | ||||||||||||||||||||||||
| schema: { type: 'json', required: true }, | ||||||||||||||||||||||||
| apiKey: { type: 'string', required: true }, | ||||||||||||||||||||||||
| model: { type: 'string', required: true }, | ||||||||||||||||||||||||
| env: { type: 'string', required: true, }, | ||||||||||||||||||||||||
|
Comment on lines
+108
to
+109
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: Extra comma after required: true in env field definition. Remove for consistency.
Suggested change
|
||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| outputs: { | ||||||||||||||||||||||||
| response: { | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,4 +8,5 @@ export default { | |
| dbCredentials: { | ||
| url: env.DATABASE_URL, | ||
| }, | ||
|
|
||
| } satisfies Config | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Extract model list to separate config file to improve maintainability