1
1
from enum import Enum
2
2
from typing import Any , Dict , List , Optional , Type , Union
3
3
4
- from pydantic import BaseModel , Field
4
+ from pydantic import BaseModel , Field , field_serializer
5
5
6
6
# Default extraction schema that matches the TypeScript version
7
7
DEFAULT_EXTRACT_SCHEMA = {
@@ -18,7 +18,18 @@ class AvailableModel(str, Enum):
18
18
CLAUDE_3_7_SONNET_LATEST = "claude-3-7-sonnet-latest"
19
19
20
20
21
- class ActOptions (BaseModel ):
21
+ class StagehandBaseModel (BaseModel ):
22
+ """Base model for all Stagehand models with camelCase conversion support"""
23
+
24
+ class Config :
25
+ populate_by_name = True # Allow accessing fields by their Python name
26
+ alias_generator = lambda field_name : '' .join (
27
+ [field_name .split ('_' )[0 ]] +
28
+ [word .capitalize () for word in field_name .split ('_' )[1 :]]
29
+ ) # snake_case to camelCase
30
+
31
+
32
+ class ActOptions (StagehandBaseModel ):
22
33
"""
23
34
Options for the 'act' command.
24
35
@@ -31,11 +42,11 @@ class ActOptions(BaseModel):
31
42
32
43
action : str = Field (..., description = "The action command to be executed by the AI." )
33
44
variables : Optional [Dict [str , str ]] = None
34
- model_name : Optional [AvailableModel ] = Field ( None , alias = "modelName" )
35
- slow_dom_based_act : Optional [bool ] = Field ( None , alias = "slowDomBasedAct" )
45
+ model_name : Optional [AvailableModel ] = None
46
+ slow_dom_based_act : Optional [bool ] = None
36
47
37
48
38
- class ActResult (BaseModel ):
49
+ class ActResult (StagehandBaseModel ):
39
50
"""
40
51
Result of the 'act' command.
41
52
@@ -50,7 +61,7 @@ class ActResult(BaseModel):
50
61
action : str = Field (..., description = "The action command that was executed." )
51
62
52
63
53
- class ExtractOptions (BaseModel ):
64
+ class ExtractOptions (StagehandBaseModel ):
54
65
"""
55
66
Options for the 'extract' command.
56
67
@@ -66,22 +77,28 @@ class ExtractOptions(BaseModel):
66
77
instruction : str = Field (
67
78
..., description = "Instruction specifying what data to extract using AI."
68
79
)
69
- model_name : Optional [AvailableModel ] = Field ( None , alias = "modelName" )
80
+ model_name : Optional [AvailableModel ] = None
70
81
selector : Optional [str ] = None
71
82
# IMPORTANT: If using a Pydantic model for schema_definition, please call its .model_json_schema() method
72
83
# to convert it to a JSON serializable dictionary before sending it with the extract command.
73
84
schema_definition : Union [Dict [str , Any ], Type [BaseModel ]] = Field (
74
85
default = DEFAULT_EXTRACT_SCHEMA ,
75
86
description = "A JSON schema or Pydantic model that defines the structure of the expected data." ,
76
- alias = "schemaDefinition" ,
77
87
)
78
- use_text_extract : Optional [bool ] = Field (True , alias = "useTextExtract" )
88
+ use_text_extract : Optional [bool ] = True
89
+
90
+ @field_serializer ('schema_definition' )
91
+ def serialize_schema_definition (self , schema_definition : Union [Dict [str , Any ], Type [BaseModel ]]) -> Dict [str , Any ]:
92
+ """Serialize schema_definition to a JSON schema if it's a Pydantic model"""
93
+ if isinstance (schema_definition , type ) and issubclass (schema_definition , BaseModel ):
94
+ return schema_definition .model_json_schema ()
95
+ return schema_definition
79
96
80
97
class Config :
81
98
arbitrary_types_allowed = True
82
99
83
100
84
- class ExtractResult (BaseModel ):
101
+ class ExtractResult (StagehandBaseModel ):
85
102
"""
86
103
Result of the 'extract' command.
87
104
@@ -103,7 +120,7 @@ def __getitem__(self, key):
103
120
return getattr (self , key )
104
121
105
122
106
- class ObserveOptions (BaseModel ):
123
+ class ObserveOptions (StagehandBaseModel ):
107
124
"""
108
125
Options for the 'observe' command.
109
126
@@ -118,13 +135,13 @@ class ObserveOptions(BaseModel):
118
135
instruction : str = Field (
119
136
..., description = "Instruction detailing what the AI should observe."
120
137
)
121
- only_visible : Optional [bool ] = Field ( False , alias = "onlyVisible" )
122
- model_name : Optional [AvailableModel ] = Field ( None , alias = "modelName" )
123
- return_action : Optional [bool ] = Field ( None , alias = "returnAction" )
124
- draw_overlay : Optional [bool ] = Field ( None , alias = "drawOverlay" )
138
+ only_visible : Optional [bool ] = False
139
+ model_name : Optional [AvailableModel ] = None
140
+ return_action : Optional [bool ] = None
141
+ draw_overlay : Optional [bool ] = None
125
142
126
143
127
- class ObserveResult (BaseModel ):
144
+ class ObserveResult (StagehandBaseModel ):
128
145
"""
129
146
Result of the 'observe' command.
130
147
"""
@@ -133,7 +150,7 @@ class ObserveResult(BaseModel):
133
150
description : str = Field (
134
151
..., description = "The description of the observed element."
135
152
)
136
- backend_node_id : Optional [int ] = Field ( None , alias = "backendNodeId" )
153
+ backend_node_id : Optional [int ] = None
137
154
method : Optional [str ] = None
138
155
arguments : Optional [List [str ]] = None
139
156
0 commit comments