Speedofmastery's picture
Merge Landrun + Browser-Use + Chromium with AI agent support (without binary files)
d7b3d84
"""Export code-use session to Jupyter notebook format."""
import json
import re
from pathlib import Path
from browser_use.code_use.service import CodeAgent
from .views import CellType, NotebookExport
def export_to_ipynb(agent: CodeAgent, output_path: str | Path) -> Path:
"""
Export a NotebookSession to a Jupyter notebook (.ipynb) file.
Now includes JavaScript code blocks that were stored in the namespace.
Args:
session: The NotebookSession to export
output_path: Path where to save the notebook file
agent: Optional CodeAgent instance to access namespace for JavaScript blocks
Returns:
Path to the saved notebook file
Example:
```python
session = await agent.run()
notebook_path = export_to_ipynb(agent, 'my_automation.ipynb')
print(f'Notebook saved to {notebook_path}')
```
"""
output_path = Path(output_path)
# Create notebook structure
notebook = NotebookExport(
metadata={
'kernelspec': {'display_name': 'Python 3', 'language': 'python', 'name': 'python3'},
'language_info': {
'name': 'python',
'version': '3.11.0',
'mimetype': 'text/x-python',
'codemirror_mode': {'name': 'ipython', 'version': 3},
'pygments_lexer': 'ipython3',
'nbconvert_exporter': 'python',
'file_extension': '.py',
},
}
)
# Add setup cell at the beginning with proper type hints
setup_code = """import asyncio
import json
from typing import Any
from browser_use import BrowserSession
from browser_use.code_use import create_namespace
# Initialize browser and namespace
browser = BrowserSession()
await browser.start()
# Create namespace with all browser control functions
namespace: dict[str, Any] = create_namespace(browser)
# Import all functions into the current namespace
globals().update(namespace)
# Type hints for better IDE support (these are now available globally)
# navigate, click, input, evaluate, search, extract, scroll, done, etc.
print("Browser-use environment initialized!")
print("Available functions: navigate, click, input, evaluate, search, extract, done, etc.")"""
setup_cell = {
'cell_type': 'code',
'metadata': {},
'source': setup_code.split('\n'),
'execution_count': None,
'outputs': [],
}
notebook.cells.append(setup_cell)
# Add JavaScript code blocks as variables FIRST
if hasattr(agent, 'namespace') and agent.namespace:
# Look for JavaScript variables in the namespace
code_block_vars = agent.namespace.get('_code_block_vars', set())
for var_name in sorted(code_block_vars):
var_value = agent.namespace.get(var_name)
if isinstance(var_value, str) and var_value.strip():
# Check if this looks like JavaScript code
# Look for common JS patterns
js_patterns = [
r'function\s+\w+\s*\(',
r'\(\s*function\s*\(\)',
r'=>\s*{',
r'document\.',
r'Array\.from\(',
r'\.querySelector',
r'\.textContent',
r'\.innerHTML',
r'return\s+',
r'console\.log',
r'window\.',
r'\.map\(',
r'\.filter\(',
r'\.forEach\(',
]
is_js = any(re.search(pattern, var_value, re.IGNORECASE) for pattern in js_patterns)
if is_js:
# Create a code cell with the JavaScript variable
js_cell = {
'cell_type': 'code',
'metadata': {},
'source': [f'# JavaScript Code Block: {var_name}\n', f'{var_name} = """{var_value}"""'],
'execution_count': None,
'outputs': [],
}
notebook.cells.append(js_cell)
# Convert cells
python_cell_count = 0
for cell in agent.session.cells:
notebook_cell: dict = {
'cell_type': cell.cell_type.value,
'metadata': {},
'source': cell.source.splitlines(keepends=True),
}
if cell.cell_type == CellType.CODE:
python_cell_count += 1
notebook_cell['execution_count'] = cell.execution_count
notebook_cell['outputs'] = []
# Add output if available
if cell.output:
notebook_cell['outputs'].append(
{
'output_type': 'stream',
'name': 'stdout',
'text': cell.output.split('\n'),
}
)
# Add error if available
if cell.error:
notebook_cell['outputs'].append(
{
'output_type': 'error',
'ename': 'Error',
'evalue': cell.error.split('\n')[0] if cell.error else '',
'traceback': cell.error.split('\n') if cell.error else [],
}
)
# Add browser state as a separate output
if cell.browser_state:
notebook_cell['outputs'].append(
{
'output_type': 'stream',
'name': 'stdout',
'text': [f'Browser State:\n{cell.browser_state}'],
}
)
notebook.cells.append(notebook_cell)
# Write to file
output_path.parent.mkdir(parents=True, exist_ok=True)
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(notebook.model_dump(), f, indent=2, ensure_ascii=False)
return output_path
def session_to_python_script(agent: CodeAgent) -> str:
"""
Convert a CodeAgent session to a Python script.
Now includes JavaScript code blocks that were stored in the namespace.
Args:
agent: The CodeAgent instance to convert
Returns:
Python script as a string
Example:
```python
await agent.run()
script = session_to_python_script(agent)
print(script)
```
"""
lines = []
lines.append('# Generated from browser-use code-use session\n')
lines.append('import asyncio\n')
lines.append('import json\n')
lines.append('from browser_use import BrowserSession\n')
lines.append('from browser_use.code_use import create_namespace\n\n')
lines.append('async def main():\n')
lines.append('\t# Initialize browser and namespace\n')
lines.append('\tbrowser = BrowserSession()\n')
lines.append('\tawait browser.start()\n\n')
lines.append('\t# Create namespace with all browser control functions\n')
lines.append('\tnamespace = create_namespace(browser)\n\n')
lines.append('\t# Extract functions from namespace for direct access\n')
lines.append('\tnavigate = namespace["navigate"]\n')
lines.append('\tclick = namespace["click"]\n')
lines.append('\tinput_text = namespace["input"]\n')
lines.append('\tevaluate = namespace["evaluate"]\n')
lines.append('\tsearch = namespace["search"]\n')
lines.append('\textract = namespace["extract"]\n')
lines.append('\tscroll = namespace["scroll"]\n')
lines.append('\tdone = namespace["done"]\n')
lines.append('\tgo_back = namespace["go_back"]\n')
lines.append('\twait = namespace["wait"]\n')
lines.append('\tscreenshot = namespace["screenshot"]\n')
lines.append('\tfind_text = namespace["find_text"]\n')
lines.append('\tswitch_tab = namespace["switch"]\n')
lines.append('\tclose_tab = namespace["close"]\n')
lines.append('\tdropdown_options = namespace["dropdown_options"]\n')
lines.append('\tselect_dropdown = namespace["select_dropdown"]\n')
lines.append('\tupload_file = namespace["upload_file"]\n')
lines.append('\tsend_keys = namespace["send_keys"]\n\n')
# Add JavaScript code blocks as variables FIRST
if hasattr(agent, 'namespace') and agent.namespace:
code_block_vars = agent.namespace.get('_code_block_vars', set())
for var_name in sorted(code_block_vars):
var_value = agent.namespace.get(var_name)
if isinstance(var_value, str) and var_value.strip():
# Check if this looks like JavaScript code
js_patterns = [
r'function\s+\w+\s*\(',
r'\(\s*function\s*\(\)',
r'=>\s*{',
r'document\.',
r'Array\.from\(',
r'\.querySelector',
r'\.textContent',
r'\.innerHTML',
r'return\s+',
r'console\.log',
r'window\.',
r'\.map\(',
r'\.filter\(',
r'\.forEach\(',
]
is_js = any(re.search(pattern, var_value, re.IGNORECASE) for pattern in js_patterns)
if is_js:
lines.append(f'\t# JavaScript Code Block: {var_name}\n')
lines.append(f'\t{var_name} = """{var_value}"""\n\n')
for i, cell in enumerate(agent.session.cells):
if cell.cell_type == CellType.CODE:
lines.append(f'\t# Cell {i + 1}\n')
# Indent each line of source
source_lines = cell.source.split('\n')
for line in source_lines:
if line.strip(): # Only add non-empty lines
lines.append(f'\t{line}\n')
lines.append('\n')
lines.append('\tawait browser.stop()\n\n')
lines.append("if __name__ == '__main__':\n")
lines.append('\tasyncio.run(main())\n')
return ''.join(lines)