ai-agent-book 精选快照(<2MB 代码与文档,来自 github.com/bojieli/ai-agent-book)
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
from collections.abc import Callable
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from browser_use.browser import BrowserSession
|
||||
from browser_use.filesystem.file_system import FileSystem
|
||||
from browser_use.llm.base import BaseChatModel
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
|
||||
class RegisteredAction(BaseModel):
|
||||
"""Model for a registered action"""
|
||||
|
||||
name: str
|
||||
description: str
|
||||
function: Callable
|
||||
param_model: type[BaseModel]
|
||||
|
||||
# filters: provide specific domains to determine whether the action should be available on the given URL or not
|
||||
domains: list[str] | None = None # e.g. ['*.google.com', 'www.bing.com', 'yahoo.*]
|
||||
|
||||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||
|
||||
def prompt_description(self) -> str:
|
||||
"""Get a description of the action for the prompt"""
|
||||
skip_keys = ['title']
|
||||
s = f'{self.description}: \n'
|
||||
s += '{' + str(self.name) + ': '
|
||||
s += str(
|
||||
{
|
||||
k: {sub_k: sub_v for sub_k, sub_v in v.items() if sub_k not in skip_keys}
|
||||
for k, v in self.param_model.model_json_schema()['properties'].items()
|
||||
}
|
||||
)
|
||||
s += '}'
|
||||
return s
|
||||
|
||||
|
||||
class ActionModel(BaseModel):
|
||||
"""Base model for dynamically created action models"""
|
||||
|
||||
# this will have all the registered actions, e.g.
|
||||
# click_element = param_model = ClickElementParams
|
||||
# done = param_model = None
|
||||
#
|
||||
model_config = ConfigDict(arbitrary_types_allowed=True, extra='forbid')
|
||||
|
||||
def get_index(self) -> int | None:
|
||||
"""Get the index of the action"""
|
||||
# {'clicked_element': {'index':5}}
|
||||
params = self.model_dump(exclude_unset=True).values()
|
||||
if not params:
|
||||
return None
|
||||
for param in params:
|
||||
if param is not None and 'index' in param:
|
||||
return param['index']
|
||||
return None
|
||||
|
||||
def set_index(self, index: int):
|
||||
"""Overwrite the index of the action"""
|
||||
# Get the action name and params
|
||||
action_data = self.model_dump(exclude_unset=True)
|
||||
action_name = next(iter(action_data.keys()))
|
||||
action_params = getattr(self, action_name)
|
||||
|
||||
# Update the index directly on the model
|
||||
if hasattr(action_params, 'index'):
|
||||
action_params.index = index
|
||||
|
||||
|
||||
class ActionRegistry(BaseModel):
|
||||
"""Model representing the action registry"""
|
||||
|
||||
actions: dict[str, RegisteredAction] = {}
|
||||
|
||||
@staticmethod
|
||||
def _match_domains(domains: list[str] | None, url: str) -> bool:
|
||||
"""
|
||||
Match a list of domain glob patterns against a URL.
|
||||
|
||||
Args:
|
||||
domains: A list of domain patterns that can include glob patterns (* wildcard)
|
||||
url: The URL to match against
|
||||
|
||||
Returns:
|
||||
True if the URL's domain matches the pattern, False otherwise
|
||||
"""
|
||||
|
||||
if domains is None or not url:
|
||||
return True
|
||||
|
||||
# Use the centralized URL matching logic from utils
|
||||
from browser_use.utils import match_url_with_domain_pattern
|
||||
|
||||
for domain_pattern in domains:
|
||||
if match_url_with_domain_pattern(url, domain_pattern):
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_prompt_description(self, page_url: str | None = None) -> str:
|
||||
"""Get a description of all actions for the prompt
|
||||
|
||||
Args:
|
||||
page_url: If provided, filter actions by URL using domain filters.
|
||||
|
||||
Returns:
|
||||
A string description of available actions.
|
||||
- If page is None: return only actions with no page_filter and no domains (for system prompt)
|
||||
- If page is provided: return only filtered actions that match the current page (excluding unfiltered actions)
|
||||
"""
|
||||
if page_url is None:
|
||||
# For system prompt (no URL provided), include only actions with no filters
|
||||
return '\n'.join(action.prompt_description() for action in self.actions.values() if action.domains is None)
|
||||
|
||||
# only include filtered actions for the current page URL
|
||||
filtered_actions = []
|
||||
for action in self.actions.values():
|
||||
if not action.domains:
|
||||
# skip actions with no filters, they are already included in the system prompt
|
||||
continue
|
||||
|
||||
# Check domain filter
|
||||
if self._match_domains(action.domains, page_url):
|
||||
filtered_actions.append(action)
|
||||
|
||||
return '\n'.join(action.prompt_description() for action in filtered_actions)
|
||||
|
||||
|
||||
class SpecialActionParameters(BaseModel):
|
||||
"""Model defining all special parameters that can be injected into actions"""
|
||||
|
||||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||
|
||||
# optional user-provided context object passed down from Agent(context=...)
|
||||
# e.g. can contain anything, external db connections, file handles, queues, runtime config objects, etc.
|
||||
# that you might want to be able to access quickly from within many of your actions
|
||||
# browser-use code doesn't use this at all, we just pass it down to your actions for convenience
|
||||
context: Any | None = None
|
||||
|
||||
# browser-use session object, can be used to create new tabs, navigate, access CDP
|
||||
browser_session: BrowserSession | None = None
|
||||
|
||||
# Current page URL for filtering and context
|
||||
page_url: str | None = None
|
||||
|
||||
# CDP client for direct Chrome DevTools Protocol access
|
||||
cdp_client: Any | None = None # CDPClient type from cdp_use
|
||||
|
||||
# extra injected config if the action asks for these arg names
|
||||
page_extraction_llm: BaseChatModel | None = None
|
||||
file_system: FileSystem | None = None
|
||||
available_file_paths: list[str] | None = None
|
||||
has_sensitive_data: bool = False
|
||||
|
||||
@classmethod
|
||||
def get_browser_requiring_params(cls) -> set[str]:
|
||||
"""Get parameter names that require browser_session"""
|
||||
return {'browser_session', 'cdp_client', 'page_url'}
|
||||
Reference in New Issue
Block a user