1
+ from abc import ABC , abstractmethod
2
+ from typing import Any , Callable , Dict , Optional , Union
3
+ from playwright .async_api import Page
4
+
5
+ from .config import StagehandConfig
6
+ from .page import StagehandPage
7
+ from .utils import default_log_handler
8
+ import os
9
+ import time
10
+ import logging
11
+
12
+ logger = logging .getLogger (__name__ )
13
+
14
+
15
+ class StagehandBase (ABC ):
16
+ """
17
+ Base class for Stagehand client implementations.
18
+ Defines the common interface and functionality for both sync and async versions.
19
+ """
20
+ def __init__ (
21
+ self ,
22
+ config : Optional [StagehandConfig ] = None ,
23
+ server_url : Optional [str ] = None ,
24
+ session_id : Optional [str ] = None ,
25
+ browserbase_api_key : Optional [str ] = None ,
26
+ browserbase_project_id : Optional [str ] = None ,
27
+ model_api_key : Optional [str ] = None ,
28
+ on_log : Optional [Callable [[Dict [str , Any ]], Any ]] = default_log_handler ,
29
+ verbose : int = 1 ,
30
+ model_name : Optional [str ] = None ,
31
+ dom_settle_timeout_ms : Optional [int ] = None ,
32
+ debug_dom : Optional [bool ] = None ,
33
+ timeout_settings : Optional [float ] = None ,
34
+ stream_response : Optional [bool ] = None ,
35
+ model_client_options : Optional [Dict [str , Any ]] = None ,
36
+ ):
37
+ """
38
+ Initialize the Stagehand client with common configuration.
39
+ """
40
+ self .server_url = server_url or os .getenv ("STAGEHAND_SERVER_URL" )
41
+
42
+ if config :
43
+ self .browserbase_api_key = config .api_key or browserbase_api_key or os .getenv ("BROWSERBASE_API_KEY" )
44
+ self .browserbase_project_id = config .project_id or browserbase_project_id or os .getenv ("BROWSERBASE_PROJECT_ID" )
45
+ self .session_id = config .browserbase_session_id or session_id
46
+ self .model_name = config .model_name or model_name
47
+ self .dom_settle_timeout_ms = config .dom_settle_timeout_ms or dom_settle_timeout_ms
48
+ self .debug_dom = config .debug_dom if config .debug_dom is not None else debug_dom
49
+ else :
50
+ self .browserbase_api_key = browserbase_api_key or os .getenv ("BROWSERBASE_API_KEY" )
51
+ self .browserbase_project_id = browserbase_project_id or os .getenv ("BROWSERBASE_PROJECT_ID" )
52
+ self .session_id = session_id
53
+ self .model_name = model_name
54
+ self .dom_settle_timeout_ms = dom_settle_timeout_ms
55
+ self .debug_dom = debug_dom
56
+
57
+ # Handle model-related settings directly
58
+ self .model_api_key = model_api_key or os .getenv ("MODEL_API_KEY" )
59
+ self .model_client_options = model_client_options or {}
60
+ if self .model_api_key and "apiKey" not in self .model_client_options :
61
+ self .model_client_options ["apiKey" ] = self .model_api_key
62
+
63
+ # Handle streaming response setting directly
64
+ self .streamed_response = stream_response if stream_response is not None else True
65
+
66
+ self .on_log = on_log
67
+ self .verbose = verbose
68
+ self .timeout_settings = timeout_settings or 180.0
69
+
70
+ self ._initialized = False
71
+ self ._closed = False
72
+ self .page : Optional [StagehandPage ] = None
73
+
74
+ # Validate essential fields if session_id was provided
75
+ if self .session_id :
76
+ if not self .browserbase_api_key :
77
+ raise ValueError ("browserbase_api_key is required (or set BROWSERBASE_API_KEY in env)." )
78
+ if not self .browserbase_project_id :
79
+ raise ValueError ("browserbase_project_id is required (or set BROWSERBASE_PROJECT_ID in env)." )
80
+
81
+ @abstractmethod
82
+ def init (self ):
83
+ """
84
+ Initialize the Stagehand client.
85
+ Must be implemented by subclasses.
86
+ """
87
+ pass
88
+
89
+ @abstractmethod
90
+ def close (self ):
91
+ """
92
+ Clean up resources.
93
+ Must be implemented by subclasses.
94
+ """
95
+ pass
96
+
97
+ def _log (self , message : str , level : int = 1 ):
98
+ """
99
+ Internal logging helper that maps verbosity to logging levels.
100
+ """
101
+ if self .verbose >= level :
102
+ timestamp = time .strftime ("%Y-%m-%d %H:%M:%S" , time .localtime ())
103
+ formatted_msg = f"{ timestamp } ::[stagehand] { message } "
104
+ if level == 1 :
105
+ logger .info (formatted_msg )
106
+ elif level == 2 :
107
+ logger .warning (formatted_msg )
108
+ else :
109
+ logger .debug (formatted_msg )
0 commit comments