UNPKG

browser-use-typescript

Version:

A TypeScript-based browser automation framework

478 lines 16.5 kB
import { HistoryTreeProcessor } from "../domHIstory/historyTypes"; import { randomUUID } from "crypto"; import * as fs from 'fs'; import * as path from 'path'; import { MessageManagerState } from "./message_manager/types"; class AgentSettings { use_vision = true; use_vision_for_planner = false; save_conversation_path = undefined; save_conversation_path_encoding = 'utf-8'; max_failures = 3; retry_delay = 10; max_input_tokens = 128000; validate_output = false; message_context = undefined; generate_gif = false; available_file_paths = undefined; override_system_message = undefined; extend_system_message = undefined; include_attributes = [ 'title', 'type', 'name', 'role', 'tabindex', 'aria-label', 'placeholder', 'value', 'alt', 'aria-expanded', ]; max_actions_per_step = 10; tool_calling_method = 'auto'; page_extraction_llm = undefined; planner_llm = undefined; planner_interval = 1; constructor(params) { this.use_vision = params.use_vision; this.use_vision_for_planner = params.use_vision_for_planner; this.save_conversation_path = params.save_conversation_path; this.save_conversation_path_encoding = params.save_conversation_path_encoding; this.max_failures = params.max_failures; this.retry_delay = params.retry_delay; this.max_input_tokens = params.max_input_tokens; this.validate_output = params.validate_output; this.message_context = params.message_context; this.generate_gif = params.generate_gif; this.available_file_paths = params.available_file_paths; this.override_system_message = params.override_system_message; this.extend_system_message = params.extend_system_message; this.include_attributes = params.include_attributes; this.max_actions_per_step = params.max_actions_per_step; this.tool_calling_method = params.tool_calling_method; this.page_extraction_llm = params.page_extraction_llm; this.planner_llm = params.planner_llm; this.planner_interval = params.planner_interval; } } class AgentState { agent_id = randomUUID(); n_steps = 1; consecutive_failures = 0; last_result = null; history = new AgentHistoryList(); last_plan = null; paused = false; stopped = false; message_manager_state = new MessageManagerState(); } class AgentStepInfo { step_number; max_steps; constructor(step_number, max_steps) { this.step_number = step_number; this.max_steps = max_steps; } is_last_step() { return this.step_number >= this.max_steps - 1; } } class ActionResult { isDone = false; success = null; extractedContent = null; error = null; includeInMemory = false; constructor(params) { Object.assign(this, params); } // Helper methods for serialization toJSON() { const result = {}; if (this.isDone !== undefined) result.isDone = this.isDone; if (this.success !== undefined) result.success = this.success; if (this.extractedContent !== undefined) result.extractedContent = this.extractedContent; if (this.error !== undefined) result.error = this.error; result.includeInMemory = this.includeInMemory; return result; } } class StepMetadata { step_start_time; step_end_time; input_tokens; step_number; constructor(step_start_time, step_end_time, input_tokens, step_number) { this.step_start_time = step_start_time; this.step_end_time = step_end_time; this.input_tokens = input_tokens; this.step_number = step_number; } get duration_seconds() { return this.step_end_time - this.step_start_time; } // Helper method for serialization toJSON() { return { step_start_time: this.step_start_time, step_end_time: this.step_end_time, input_tokens: this.input_tokens, step_number: this.step_number }; } } class AgentBrain { evaluation_previous_goal; memory; next_goal; constructor(fields) { this.evaluation_previous_goal = fields.evaluation_previous_goal; this.memory = fields.memory; this.next_goal = fields.next_goal; } } class AgentOutput { action; current_state; constructor(fields) { this.action = fields.action || []; this.current_state = fields.current_state || new AgentBrain({ evaluation_previous_goal: '', memory: '', next_goal: '' }); } /** * Generate a complete JSON schema for the AgentOutput * This matches the structure created by Pydantic's model_json_schema() in Python */ toJson() { try { // Create the $defs section with models const defs = { "AgentBrain": { "properties": { "evaluation_previous_goal": { "title": "Evaluation Previous Goal", "type": "string", "description": "Evaluation of how the previous goal was met" }, "memory": { "title": "Memory", "type": "string", "description": "Memory or context to keep between steps" }, "next_goal": { "title": "Next Goal", "type": "string", "description": "Next goal to achieve" } }, "required": ["evaluation_previous_goal", "memory", "next_goal"], "title": "AgentBrain", "type": "object", "description": "Agent brain state" } }; // Get action properties and definitions if (this.action && this.action.length > 0 && this.action[0]) { // Create ActionModel definition defs["ActionModel"] = { "properties": this.action[0].toJson(), "title": "ActionModel", "type": "object", "description": "Model for actions to be executed" }; // Add action-specific definitions const actionDefs = this.action[0].getActionDefs(); Object.assign(defs, actionDefs); } // Create the complete schema structure return { "$defs": defs, "properties": { "current_state": { "$ref": "#/$defs/AgentBrain" }, "action": { "type": "array", "items": { "$ref": "#/$defs/ActionModel" }, "minItems": 1, "title": "Action", "description": "List of actions to execute" } }, "required": ["current_state", "action"], "title": "AgentOutput", "type": "object", "description": "AgentOutput model with custom actions" }; } catch (error) { console.error("Error generating JSON schema:", error); return {}; } } } class AgentHistory { model_output; result; state; metadata; constructor(model_output = null, result = [], state, metadata = null) { this.model_output = model_output; this.result = result; this.state = state; this.metadata = metadata; } // Helper method to get interacted elements static async getInteractedElement(model_output, selector_map) { const elements = []; for (const action of model_output.action) { const index = "get_index" in action ? action.get_index() : null; if (index !== null && index in selector_map) { const el = await selector_map[index]; elements.push(await HistoryTreeProcessor.convertDomElementToHistoryElement(el)); } else { elements.push(null); } } return elements; } // Helper method for serialization toJSON() { // Handle action serialization let model_output_dump = null; if (this.model_output) { model_output_dump = this.model_output; } return { model_output: model_output_dump, result: this.result.map(r => r.toJSON()), state: this.state.toDict() ? JSON.parse(this.state.toDict().toString()) : this.state, metadata: this.metadata ? this.metadata.toJSON() : null }; } } class AgentHistoryList { history = []; constructor(history = []) { this.history = history; } totalDurationSeconds() { let total = 0; for (const h of this.history) { if (h.metadata) { total += h.metadata.duration_seconds; } } return total; } totalInputTokens() { let total = 0; for (const h of this.history) { if (h.metadata) { total += h.metadata.input_tokens; } } return total; } inputTokenUsage() { return this.history .filter(h => h.metadata) .map(h => h.metadata.input_tokens); } toString() { return `AgentHistoryList(all_results=${JSON.stringify(this.actionResults())}, all_model_outputs=${JSON.stringify(this.modelActions())})`; } saveToFile(filepath) { fs.mkdirSync(path.dirname(filepath), { recursive: true }); const data = JSON.stringify(this.toJSON(), null, 2); fs.writeFileSync(filepath, data, 'utf-8'); } toJSON() { return { history: this.history.map(h => h.toJSON()) }; } static loadFromFile(filepath) { const data = JSON.parse(fs.readFileSync(filepath, 'utf-8')); // Process the data to convert it to our model objects for (const h of data.history) { if (h.model_output) { if (typeof h.model_output === 'object') { // In a real implementation, we would validate and convert this properly // But for simplicity, we're just setting it directly } else { h.model_output = null; } } if (!h.state.interacted_element) { h.state.interacted_element = null; } } // In a real implementation, we would validate the data structure here return new AgentHistoryList(data.history); } lastAction() { if (this.history.length > 0 && this.history[this.history.length - 1].model_output) { const lastOutput = this.history[this.history.length - 1].model_output; if (lastOutput.action.length > 0) { const lastAction = lastOutput.action[lastOutput.action.length - 1]; return lastAction; } } return null; } errors() { const errors = []; for (const h of this.history) { const stepErrors = h.result .filter(r => r.error) .map(r => r.error); // Each step can have only one error errors.push(stepErrors.length > 0 ? stepErrors[0] : null); } return errors; } finalResult() { if (this.history.length > 0 && this.history[this.history.length - 1].result.length > 0) { const lastResult = this.history[this.history.length - 1].result[this.history[this.history.length - 1].result.length - 1]; return lastResult.extractedContent; } return null; } isDone() { if (this.history.length > 0 && this.history[this.history.length - 1].result.length > 0) { const lastResult = this.history[this.history.length - 1].result[this.history[this.history.length - 1].result.length - 1]; return lastResult.isDone === true; } return false; } isSuccessful() { if (this.history.length > 0 && this.history[this.history.length - 1].result.length > 0) { const lastResult = this.history[this.history.length - 1].result[this.history[this.history.length - 1].result.length - 1]; if (lastResult.isDone === true) { return lastResult.success; } } return null; } hasErrors() { return this.errors().some(error => error !== null); } urls() { return this.history.map(h => h.state.url || null); } screenshots() { return this.history.map(h => h.state.screenshot || null); } actionNames() { const actionNames = []; for (const action of this.modelActions()) { const keys = Object.keys(action); if (keys.length > 0) { actionNames.push(keys[0]); } } return actionNames; } modelThoughts() { return this.history .filter(h => h.model_output !== null) .map(h => h.model_output.current_state); } modelOutputs() { return this.history .filter(h => h.model_output !== null) .map(h => h.model_output); } modelActions() { const outputs = []; for (const h of this.history) { if (h.model_output) { for (let i = 0; i < h.model_output.action.length; i++) { const action = h.model_output.action[i]; const output = action; outputs.push(output); } } } return outputs; } actionResults() { const results = []; for (const h of this.history) { results.push(...h.result.filter(r => r)); } return results; } extractedContent() { const content = []; for (const h of this.history) { for (const r of h.result) { if (r.extractedContent) { content.push(r.extractedContent); } } } return content; } modelActionsFiltered(include = []) { const outputs = this.modelActions(); const result = []; for (const output of outputs) { const keys = Object.keys(output); for (const includeItem of include) { if (keys.length > 0 && includeItem === keys[0]) { result.push(output); } } } return result; } numberOfSteps() { return this.history.length; } } class ValidationError extends Error { constructor(message) { super(message); this.name = 'ValidationError'; } } class RateLimitError extends Error { constructor(message) { super(message); this.name = 'RateLimitError'; } } class AgentError { static VALIDATION_ERROR = 'Invalid model output format. Please follow the correct schema.'; static RATE_LIMIT_ERROR = 'Rate limit reached. Waiting before retry.'; static NO_VALID_ACTION = 'No valid action found'; static formatError(error, includeTrace = false) { if (error instanceof ValidationError) { return `${AgentError.VALIDATION_ERROR}\nDetails: ${error.message}`; } if (error instanceof RateLimitError) { return AgentError.RATE_LIMIT_ERROR; } if (includeTrace) { return `${error.message}\nStacktrace:\n${error.stack || ''}`; } return error.message; } } // Export the classes and interfaces for use in other files export { AgentSettings, AgentState, AgentStepInfo, ActionResult, StepMetadata, AgentBrain, AgentOutput, AgentHistory, AgentHistoryList, AgentError, ValidationError, RateLimitError, MessageManagerState, }; //# sourceMappingURL=types.js.map