Skip to content

Commit 5f3226b

Browse files
authored
Fix logging (#82)
* separate logging from utils * remove warning level, consolidate with api * centralize logging * update llmclient logging * correct verbose levels from env bb * fix imports * formatting * formatting * missing merge conflict errors * delete empty file * fix CI
1 parent 9df1660 commit 5f3226b

File tree

16 files changed

+804
-713
lines changed

16 files changed

+804
-713
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,7 @@ jobs:
3737
pip install -e ".[dev]"
3838
# Install jsonschema for schema validation tests
3939
pip install jsonschema
40-
# Install temporary Google GenAI wheel
41-
pip install temp/google_genai-1.14.0-py3-none-any.whl
42-
40+
4341
- name: Run unit tests
4442
run: |
4543
pytest tests/unit/ -v --junit-xml=junit-unit-${{ matrix.python-version }}.xml
@@ -74,8 +72,6 @@ jobs:
7472
python -m pip install --upgrade pip
7573
pip install -e ".[dev]"
7674
pip install jsonschema
77-
# Install temporary Google GenAI wheel
78-
pip install temp/google_genai-1.14.0-py3-none-any.whl
7975
# Install Playwright browsers for integration tests
8076
playwright install chromium
8177
playwright install-deps chromium
@@ -129,8 +125,6 @@ jobs:
129125
python -m pip install --upgrade pip
130126
pip install -e ".[dev]"
131127
pip install jsonschema
132-
# Install temporary Google GenAI wheel
133-
pip install temp/google_genai-1.14.0-py3-none-any.whl
134128
135129
- name: Run API integration tests
136130
run: |
@@ -170,7 +164,6 @@ jobs:
170164
python -m pip install --upgrade pip
171165
pip install -e ".[dev]"
172166
pip install jsonschema
173-
pip install temp/google_genai-1.14.0-py3-none-any.whl
174167
175168
- name: Run smoke tests
176169
run: |
@@ -213,8 +206,6 @@ jobs:
213206
python -m pip install --upgrade pip
214207
pip install -e ".[dev]"
215208
pip install jsonschema
216-
# Install temporary Google GenAI wheel
217-
pip install temp/google_genai-1.14.0-py3-none-any.whl
218209
playwright install chromium
219210
playwright install-deps chromium
220211
@@ -261,8 +252,6 @@ jobs:
261252
python -m pip install --upgrade pip
262253
pip install -e ".[dev]"
263254
pip install jsonschema
264-
# Install temporary Google GenAI wheel
265-
pip install temp/google_genai-1.14.0-py3-none-any.whl
266255
playwright install chromium
267256
268257
- name: Run complete test suite

stagehand/__init__.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
"""Stagehand - The AI Browser Automation Framework"""
2+
13
from .agent import Agent
24
from .config import StagehandConfig, default_config
35
from .handlers.observe_handler import ObserveHandler
6+
from .llm import LLMClient
7+
from .logging import LogConfig, configure_logging
48
from .main import Stagehand
59
from .metrics import StagehandFunctionName, StagehandMetrics
610
from .page import StagehandPage
@@ -11,36 +15,33 @@
1115
AgentExecuteOptions,
1216
AgentExecuteResult,
1317
AgentProvider,
14-
AvailableModel,
1518
ExtractOptions,
1619
ExtractResult,
1720
ObserveOptions,
1821
ObserveResult,
1922
)
20-
from .utils import configure_logging
2123

2224
__version__ = "0.0.1"
2325

2426
__all__ = [
2527
"Stagehand",
2628
"StagehandConfig",
27-
"default_config",
2829
"StagehandPage",
2930
"Agent",
30-
"configure_logging",
31+
"AgentConfig",
32+
"AgentExecuteOptions",
33+
"AgentExecuteResult",
34+
"AgentProvider",
3135
"ActOptions",
3236
"ActResult",
33-
"AvailableModel",
3437
"ExtractOptions",
3538
"ExtractResult",
3639
"ObserveOptions",
3740
"ObserveResult",
38-
"AgentConfig",
39-
"AgentExecuteOptions",
40-
"AgentExecuteResult",
41-
"AgentProvider",
4241
"ObserveHandler",
43-
"observe",
42+
"LLMClient",
43+
"configure_logging",
4444
"StagehandFunctionName",
4545
"StagehandMetrics",
46+
"LogConfig",
4647
]

stagehand/a11y/utils.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@
77
if TYPE_CHECKING:
88
from stagehand.page import StagehandPage
99

10+
from ..logging import StagehandLogger
1011
from ..types.a11y import (
1112
AccessibilityNode,
1213
AXNode,
1314
CDPSession,
1415
TreeResult,
1516
)
16-
from ..utils import StagehandLogger, format_simplified_tree
17+
from ..utils import format_simplified_tree
1718

1819

1920
async def _clean_structural_nodes(
@@ -80,16 +81,16 @@ async def _clean_structural_nodes(
8081
node["role"] = result_value
8182
node_role = result_value
8283
except Exception as tag_name_error:
83-
# Use logger.warning (level 2)
84-
logger.warning(
84+
# Use logger.debug (level 2)
85+
logger.debug(
8586
message=f"Could not fetch tagName for node {backend_node_id}",
8687
auxiliary={
8788
"error": {"value": str(tag_name_error), "type": "string"}
8889
},
8990
)
9091
except Exception as resolve_error:
91-
# Use logger.warning (level 2)
92-
logger.warning(
92+
# Use logger.debug (level 2)
93+
logger.debug(
9394
message=f"Could not resolve DOM node ID {backend_node_id}",
9495
auxiliary={"error": {"value": str(resolve_error), "type": "string"}},
9596
)
@@ -277,9 +278,8 @@ async def get_accessibility_tree(
277278
try:
278279
await page.disable_cdp_domain("Accessibility")
279280
except Exception:
280-
# Log if disabling fails, but don't raise further
281-
if logger:
282-
logger.warning("Failed to disable Accessibility domain on cleanup.")
281+
# Use logger.debug (level 2)
282+
logger.debug("Failed to disable Accessibility domain on cleanup.")
283283

284284

285285
# JavaScript function to get XPath (remains JavaScript)

stagehand/agent.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,20 @@ def __init__(self, stagehand_client, agent_config: AgentConfig):
3131
self._stagehand = stagehand_client
3232
self._config = agent_config # Store the required config
3333

34+
if not self._stagehand._initialized:
35+
self._stagehand.logger.error(
36+
"Stagehand must be initialized before creating an agent. Call await stagehand.init() first."
37+
)
38+
raise RuntimeError(
39+
"Stagehand must be initialized before creating an agent. Call await stagehand.init() first."
40+
)
41+
3442
# Perform provider inference and validation
3543
if self._config.model and not self._config.provider:
3644
if self._config.model in MODEL_TO_PROVIDER_MAP:
3745
self._config.provider = MODEL_TO_PROVIDER_MAP[self._config.model]
3846
else:
39-
self._stagehand.logger.warning(
47+
self._stagehand.logger.error(
4048
f"Could not infer provider for model: {self._config.model}"
4149
)
4250

stagehand/agent/anthropic_cua.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ async def run_task(
243243
break
244244

245245
if not agent_action and not task_completed:
246-
self.logger.warning(
246+
self.logger.info(
247247
"Model did not request an action and task not marked complete. Ending task to prevent loop.",
248248
category=StagehandFunctionName.AGENT,
249249
)
@@ -290,7 +290,7 @@ def _process_provider_response(
290290
block.model_dump() for block in response.content
291291
]
292292
except Exception as e:
293-
self.logger.warning(
293+
self.logger.error(
294294
f"Could not model_dump response.content blocks: {e}",
295295
category=StagehandFunctionName.AGENT,
296296
)
@@ -337,7 +337,7 @@ def _convert_tool_use_to_agent_action(
337337
and tool_name != "goto"
338338
and tool_name != "navigate_back"
339339
):
340-
self.logger.warning(
340+
self.logger.error(
341341
f"Unsupported tool name from Anthropic: {tool_name}",
342342
category=StagehandFunctionName.AGENT,
343343
)
@@ -501,7 +501,7 @@ def _convert_tool_use_to_agent_action(
501501
)
502502
action_type_str = "drag" # Normalize
503503
else:
504-
self.logger.warning(
504+
self.logger.error(
505505
"Drag action missing valid start or end coordinates.",
506506
category=StagehandFunctionName.AGENT,
507507
)
@@ -559,7 +559,7 @@ def _convert_tool_use_to_agent_action(
559559
)
560560
action_type_str = "function"
561561
else:
562-
self.logger.warning(
562+
self.logger.error(
563563
"Goto action from Anthropic missing URL",
564564
category=StagehandFunctionName.AGENT,
565565
)
@@ -572,7 +572,7 @@ def _convert_tool_use_to_agent_action(
572572
)
573573
action_type_str = "function"
574574
else:
575-
self.logger.warning(
575+
self.logger.error(
576576
f"Unsupported action type '{action_type_str}' from Anthropic computer tool.",
577577
category=StagehandFunctionName.AGENT,
578578
)
@@ -613,7 +613,7 @@ def _format_action_feedback(
613613
self.format_screenshot(new_screenshot_base64)
614614
)
615615
else:
616-
self.logger.warning(
616+
self.logger.error(
617617
"Missing screenshot for computer tool feedback (empty string passed).",
618618
category=StagehandFunctionName.AGENT,
619619
)

stagehand/agent/openai_cua.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ def _process_provider_response(
214214
)
215215
# Ensure arguments is a dict, even if empty
216216
if not isinstance(arguments, dict):
217-
self.logger.warning(
217+
self.logger.debug(
218218
f"Function call arguments are not a dict: {arguments}. Using empty dict.",
219219
category="agent",
220220
)
@@ -464,7 +464,7 @@ async def run_task(
464464
)
465465

466466
if not agent_action and not task_completed:
467-
self.logger.warning(
467+
self.logger.info(
468468
"Model did not request an action and task not marked complete. Ending task to prevent loop.",
469469
category="agent",
470470
)
@@ -480,7 +480,7 @@ async def run_task(
480480
usage=usage_obj,
481481
)
482482

483-
self.logger.warning("Max steps reached for OpenAI CUA task.", category="agent")
483+
self.logger.info("Max steps reached for OpenAI CUA task.", category="agent")
484484
usage_obj = {
485485
"input_tokens": total_input_tokens,
486486
"output_tokens": total_output_tokens,

stagehand/browser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
)
1414

1515
from .context import StagehandContext
16+
from .logging import StagehandLogger
1617
from .page import StagehandPage
17-
from .utils import StagehandLogger
1818

1919

2020
async def connect_browserbase_browser(

stagehand/handlers/act_handler.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ async def _act_from_observe_result(
156156
)
157157

158158
if observe_result.method == "not-supported":
159-
self.logger.warning(
159+
self.logger.error(
160160
message="Cannot execute ObserveResult with unsupported method",
161161
category="act",
162162
auxiliary={
@@ -229,7 +229,7 @@ async def _act_from_observe_result(
229229
if (
230230
not act_command
231231
): # If both method and description were empty or resulted in an empty command
232-
self.logger.warning(
232+
self.logger.error(
233233
"Self-heal attempt aborted: could not construct a valid command from ObserveResult.",
234234
category="act",
235235
auxiliary={
@@ -307,7 +307,7 @@ async def _perform_playwright_method(
307307
elif hasattr(locator, method) and callable(getattr(locator, method)):
308308
await fallback_locator_method(context)
309309
else:
310-
self.logger.warning(
310+
self.logger.error(
311311
message="chosen method is invalid",
312312
category="act",
313313
auxiliary={"method": {"value": method, "type": "string"}},

stagehand/handlers/act_handler_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ async def handle_possible_page_navigation(
458458
try:
459459
await stagehand_page._wait_for_settled_dom(dom_settle_timeout_ms)
460460
except Exception as e:
461-
logger.warning(
461+
logger.debug(
462462
message="wait for settled DOM timeout hit",
463463
category="action",
464464
auxiliary={

stagehand/handlers/cua_handler.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ async def perform_action(self, action: AgentAction) -> ActionExecutionResult:
149149
await self.page.go_back()
150150
return {"success": True}
151151
# Add other function calls like back, forward, reload if needed, similar to TS version
152-
self.logger.warning(
152+
self.logger.error(
153153
f"Unsupported function call: {name}",
154154
category=StagehandFunctionName.AGENT,
155155
)
@@ -195,7 +195,7 @@ async def perform_action(self, action: AgentAction) -> ActionExecutionResult:
195195
return {"success": True}
196196

197197
else:
198-
self.logger.warning(
198+
self.logger.error(
199199
f"Unsupported action type: {action_type}",
200200
category=StagehandFunctionName.AGENT,
201201
)
@@ -236,7 +236,7 @@ async def _update_cursor_position(self, x: int, y: int) -> None:
236236
f"window.__stagehandUpdateCursorPosition({x}, {y})"
237237
)
238238
except Exception as e:
239-
self.logger.warning(
239+
self.logger.debug(
240240
f"Failed to call window.__stagehandUpdateCursorPosition: {e}",
241241
category=StagehandFunctionName.AGENT,
242242
)
@@ -246,7 +246,7 @@ async def _animate_click(self, x: int, y: int) -> None:
246246
try:
247247
await self.page.evaluate(f"window.__stagehandAnimateClick({x}, {y})")
248248
except Exception as e:
249-
self.logger.warning(
249+
self.logger.debug(
250250
f"Failed to call window.__stagehandAnimateClick: {e}",
251251
category=StagehandFunctionName.AGENT,
252252
)

0 commit comments

Comments
 (0)