mirror of
https://github.com/browser-use/browser-use.git
synced 2026-09-14 19:59:47 +08:00
180 lines
6.6 KiB
Python
180 lines
6.6 KiB
Python
from typing import Generic, TypeVar
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field
|
|
from pydantic.json_schema import SkipJsonSchema
|
|
|
|
|
|
# Action Input Models
|
|
class ExtractAction(BaseModel):
|
|
query: str
|
|
extract_links: bool = Field(
|
|
default=False, description='Set True to true if the query requires links, else false to safe tokens'
|
|
)
|
|
start_from_char: int = Field(
|
|
default=0, description='Use this for long markdowns to start from a specific character (not index in browser_state)'
|
|
)
|
|
output_schema: SkipJsonSchema[dict | None] = Field(
|
|
default=None,
|
|
description='Optional JSON Schema dict. When provided, extraction returns validated JSON matching this schema instead of free-text.',
|
|
)
|
|
already_collected: list[str] = Field(
|
|
default_factory=list,
|
|
description='Item identifiers (name, URL, or ID) already collected in prior extract calls on other pages. The extractor will skip items matching these to prevent duplicates. Use when paginating across multiple pages.',
|
|
)
|
|
|
|
|
|
class SearchPageAction(BaseModel):
|
|
pattern: str = Field(description='Text or regex pattern to search for in page content')
|
|
regex: bool = Field(default=False, description='Treat pattern as regex (default: literal text match)')
|
|
case_sensitive: bool = Field(default=False, description='Case-sensitive search (default: case-insensitive)')
|
|
context_chars: int = Field(default=150, description='Characters of surrounding context per match')
|
|
css_scope: str | None = Field(default=None, description='CSS selector to limit search scope (e.g. "div#main")')
|
|
max_results: int = Field(default=25, description='Maximum matches to return')
|
|
|
|
|
|
class FindElementsAction(BaseModel):
|
|
selector: str = Field(description='CSS selector to query elements (e.g. "table tr", "a.link", "div.product")')
|
|
attributes: list[str] | None = Field(
|
|
default=None,
|
|
description='Specific attributes to extract (e.g. ["href", "src", "class"]). If not set, returns tag and text only.',
|
|
)
|
|
max_results: int = Field(default=50, description='Maximum elements to return')
|
|
include_text: bool = Field(default=True, description='Include text content of each element')
|
|
|
|
|
|
class SearchAction(BaseModel):
|
|
query: str
|
|
engine: str = Field(
|
|
default='duckduckgo', description='duckduckgo, google, bing (use duckduckgo by default because less captchas)'
|
|
)
|
|
|
|
|
|
# Backward compatibility alias
|
|
SearchAction = SearchAction
|
|
|
|
|
|
class NavigateAction(BaseModel):
|
|
url: str
|
|
new_tab: bool = Field(default=False)
|
|
|
|
|
|
# Backward compatibility alias
|
|
GoToUrlAction = NavigateAction
|
|
|
|
|
|
class ClickElementAction(BaseModel):
|
|
index: int | None = Field(default=None, ge=1, description='Element index from browser_state')
|
|
coordinate_x: int | None = Field(default=None, description='Horizontal coordinate relative to viewport left edge')
|
|
coordinate_y: int | None = Field(default=None, description='Vertical coordinate relative to viewport top edge')
|
|
# expect_download: bool = Field(default=False, description='set True if expecting a download, False otherwise') # moved to downloads_watchdog.py
|
|
# click_count: int = 1 # TODO
|
|
|
|
|
|
class ClickElementActionIndexOnly(BaseModel):
|
|
model_config = ConfigDict(title='ClickElementAction')
|
|
|
|
index: int = Field(ge=1, description='Element index from browser_state')
|
|
|
|
|
|
class InputTextAction(BaseModel):
|
|
index: int = Field(ge=0, description='from browser_state')
|
|
text: str
|
|
clear: bool = Field(default=True, description='1=clear, 0=append')
|
|
|
|
|
|
class DoneAction(BaseModel):
|
|
text: str = Field(description='Final user message in the format the user requested')
|
|
success: bool = Field(default=True, description='True if user_request completed successfully')
|
|
files_to_display: list[str] | None = Field(default=[])
|
|
|
|
|
|
T = TypeVar('T', bound=BaseModel)
|
|
|
|
|
|
def _hide_internal_fields_from_schema(schema: dict) -> None:
|
|
"""Remove internal fields from the JSON schema to avoid collisions with user models."""
|
|
props = schema.get('properties', {})
|
|
props.pop('success', None)
|
|
props.pop('files_to_display', None)
|
|
|
|
|
|
class StructuredOutputAction(BaseModel, Generic[T]):
|
|
model_config = ConfigDict(json_schema_extra=_hide_internal_fields_from_schema)
|
|
|
|
success: bool = Field(default=True, description='True if user_request completed successfully')
|
|
data: T = Field(description='The actual output data matching the requested schema')
|
|
files_to_display: list[str] | None = Field(default=[])
|
|
|
|
|
|
class SwitchTabAction(BaseModel):
|
|
tab_id: str = Field(min_length=4, max_length=4, description='4-char id')
|
|
|
|
|
|
class CloseTabAction(BaseModel):
|
|
tab_id: str = Field(min_length=4, max_length=4, description='4-char id')
|
|
|
|
|
|
class ScrollAction(BaseModel):
|
|
down: bool = Field(default=True, description='down=True=scroll down, down=False scroll up')
|
|
pages: float = Field(default=1.0, description='0.5=half page, 1=full page, 10=to bottom/top')
|
|
index: int | None = Field(default=None, description='Optional element index to scroll within specific element')
|
|
|
|
|
|
class SendKeysAction(BaseModel):
|
|
keys: str = Field(description='keys (Escape, Enter, PageDown) or shortcuts (Control+o)')
|
|
|
|
|
|
class UploadFileAction(BaseModel):
|
|
index: int
|
|
path: str
|
|
|
|
|
|
class NoParamsAction(BaseModel):
|
|
model_config = ConfigDict(extra='ignore')
|
|
|
|
# Optional field required by Gemini API which errors on empty objects in response_schema
|
|
description: str | None = Field(None, description='Optional description for the action')
|
|
|
|
|
|
class ScreenshotAction(BaseModel):
|
|
model_config = ConfigDict(extra='ignore')
|
|
|
|
file_name: str | None = Field(
|
|
default=None,
|
|
description='If provided, saves screenshot to this file and returns path. Otherwise screenshot is included in next observation.',
|
|
)
|
|
|
|
|
|
class SaveAsPdfAction(BaseModel):
|
|
file_name: str | None = Field(
|
|
default=None,
|
|
description='Output PDF filename (without path). Defaults to page title. Extension .pdf is added automatically if missing.',
|
|
)
|
|
print_background: bool = Field(default=True, description='Include background graphics and colors')
|
|
landscape: bool = Field(default=False, description='Use landscape orientation')
|
|
scale: float = Field(default=1.0, ge=0.1, le=2.0, description='Scale of the webpage rendering (0.1 to 2.0)')
|
|
paper_format: str = Field(
|
|
default='Letter',
|
|
description='Paper size: Letter, Legal, A4, A3, or Tabloid',
|
|
)
|
|
|
|
|
|
class ReadContentAction(BaseModel):
|
|
"""Action for intelligent reading of long content."""
|
|
|
|
goal: str = Field(description='What to look for or extract from the content')
|
|
source: str = Field(
|
|
default='page',
|
|
description='What to read: "page" for current webpage, or a file path',
|
|
)
|
|
context: str = Field(default='', description='Additional context about the task')
|
|
|
|
|
|
class GetDropdownOptionsAction(BaseModel):
|
|
index: int
|
|
|
|
|
|
class SelectDropdownOptionAction(BaseModel):
|
|
index: int
|
|
text: str = Field(description='exact text/value')
|