Skip to content

Commit a3e8d0b

Browse files
committed
update llmclient logging
1 parent 76bfb90 commit a3e8d0b

File tree

4 files changed

+38
-47
lines changed

4 files changed

+38
-47
lines changed

stagehand/__init__.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
"""Stagehand - Browser automation with AI"""
1+
"""Stagehand - The AI Browser Automation Framework"""
22

33
from .agent import Agent
44
from .client import Stagehand
55
from .config import StagehandConfig
66
from .handlers.observe_handler import ObserveHandler
77
from .llm import LLMClient
8-
from .logging import LogConfig, configure_logging, get_logger
8+
from .logging import LogConfig, configure_logging
99
from .metrics import StagehandFunctionName, StagehandMetrics
1010
from .page import StagehandPage
1111
from .schemas import (
@@ -21,7 +21,7 @@
2121
ObserveResult,
2222
)
2323

24-
__version__ = "0.1.0"
24+
__version__ = "0.0.1"
2525

2626
__all__ = [
2727
"Stagehand",
@@ -44,5 +44,4 @@
4444
"StagehandFunctionName",
4545
"StagehandMetrics",
4646
"LogConfig",
47-
"get_logger",
4847
]

stagehand/client.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ def __init__(
222222
self.llm = None
223223
if self.env == "LOCAL":
224224
self.llm = LLMClient(
225+
stagehand_logger=self.logger,
225226
api_key=self.model_api_key,
226227
default_model=self.model_name,
227228
metrics_callback=self._handle_llm_metrics,

stagehand/llm/client.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
"""LLM client for model interactions."""
22

3-
import logging
4-
from typing import Any, Callable, Optional
3+
from typing import TYPE_CHECKING, Any, Callable, Optional
54

65
import litellm
76

87
from stagehand.metrics import get_inference_time_ms, start_inference_timer
98

10-
# Configure logger for the module
11-
logger = logging.getLogger(__name__)
9+
if TYPE_CHECKING:
10+
from ..logging import StagehandLogger
1211

1312

1413
class LLMClient:
@@ -19,6 +18,7 @@ class LLMClient:
1918

2019
def __init__(
2120
self,
21+
stagehand_logger: "StagehandLogger",
2222
api_key: Optional[str] = None,
2323
default_model: Optional[str] = None,
2424
metrics_callback: Optional[Callable[[Any, int, Optional[str]], None]] = None,
@@ -28,6 +28,7 @@ def __init__(
2828
Initializes the LiteLLMClient.
2929
3030
Args:
31+
stagehand_logger: StagehandLogger instance for centralized logging
3132
api_key: An API key for the default provider, if required.
3233
It's often better to set provider-specific environment variables
3334
(e.g., OPENAI_API_KEY, ANTHROPIC_API_KEY) which litellm reads automatically.
@@ -39,6 +40,7 @@ def __init__(
3940
**kwargs: Additional global settings for litellm (e.g., api_base).
4041
See litellm documentation for available settings.
4142
"""
43+
self.logger = stagehand_logger
4244
self.default_model = default_model
4345
self.metrics_callback = metrics_callback
4446

@@ -50,11 +52,13 @@ def __init__(
5052
for key, value in kwargs.items():
5153
if hasattr(litellm, key):
5254
setattr(litellm, key, value)
53-
logger.debug(f"Set global litellm.{key}")
55+
self.logger.debug(f"Set global litellm.{key}", category="llm")
5456
# Handle common aliases or expected config names if necessary
5557
elif key == "api_base": # Example: map api_base if needed
5658
litellm.api_base = value
57-
logger.debug(f"Set global litellm.api_base to {value}")
59+
self.logger.debug(
60+
f"Set global litellm.api_base to {value}", category="llm"
61+
)
5862

5963
def create_response(
6064
self,
@@ -107,9 +111,11 @@ def create_response(
107111
k: v for k, v in params.items() if v is not None or k in kwargs
108112
}
109113

110-
logger.debug(
111-
f"Calling litellm.completion with model={completion_model} and params: {filtered_params}"
114+
self.logger.debug(
115+
f"Calling litellm.completion with model={completion_model} and params: {filtered_params}",
116+
category="llm",
112117
)
118+
113119
try:
114120
# Start tracking inference time
115121
start_time = start_inference_timer()
@@ -127,6 +133,6 @@ def create_response(
127133
return response
128134

129135
except Exception as e:
130-
logger.error(f"Error calling litellm.completion: {e}")
136+
self.logger.error(f"Error calling litellm.completion: {e}", category="llm")
131137
# Consider more specific exception handling based on litellm errors
132138
raise

stagehand/logging.py

Lines changed: 19 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,6 @@ def get_remote_verbose(self) -> int:
4949
"""
5050
return self.verbose
5151

52-
def get_python_log_level(self) -> int:
53-
"""Get the Python logging level based on verbose setting."""
54-
level_map = {
55-
0: logging.ERROR,
56-
1: logging.INFO,
57-
2: logging.DEBUG,
58-
}
59-
return level_map.get(self.verbose, logging.INFO)
60-
6152
def should_log(self, level: int) -> bool:
6253
"""Check if a message at the given level should be logged."""
6354
# Always log errors (level 0)
@@ -214,24 +205,17 @@ def __init__(
214205
config: Optional LogConfig instance. If provided, overrides other parameters.
215206
"""
216207
if config:
217-
self.verbose = config.verbose
218-
self.external_logger = config.external_logger
219-
self.use_rich = config.use_rich
220208
self.config = config
221209
else:
222-
self.verbose = verbose
223-
self.external_logger = external_logger
224-
self.use_rich = use_rich
225210
self.config = LogConfig(
226211
verbose=verbose,
227212
use_rich=use_rich,
228213
external_logger=external_logger,
229214
)
230215

231-
self.console = get_console(self.use_rich)
216+
self.console = get_console(self.config.use_rich)
232217

233218
# Map our verbosity levels to Python's logging levels
234-
# Now using only 3 levels to match the remote Fastify server
235219
self.level_map = {
236220
0: logging.ERROR, # Critical errors only
237221
1: logging.INFO, # Standard information
@@ -242,11 +226,26 @@ def __init__(
242226
self.level_style = {0: "error", 1: "info", 2: "debug"}
243227

244228
# Update logger level based on verbosity
245-
self._set_verbosity(self.verbose)
229+
self._set_verbosity(self.config.verbose)
230+
231+
@property
232+
def verbose(self):
233+
"""Get verbose level from config"""
234+
return self.config.verbose
235+
236+
@property
237+
def use_rich(self):
238+
"""Get use_rich setting from config"""
239+
return self.config.use_rich
240+
241+
@property
242+
def external_logger(self):
243+
"""Get external logger from config"""
244+
return self.config.external_logger
246245

247246
def _set_verbosity(self, level: int):
248247
"""Set the logger verbosity level"""
249-
self.verbose = level
248+
self.config.verbose = level
250249
logger.setLevel(self.level_map.get(level, logging.INFO))
251250

252251
def _format_json(self, data: dict) -> str:
@@ -445,7 +444,7 @@ def log(
445444
auxiliary: Optional dictionary of auxiliary data
446445
"""
447446
# Skip logging if below current verbosity level
448-
if level > self.verbose and level != 0: # Always log errors (level 0)
447+
if not self.config.should_log(level):
449448
return
450449

451450
# Call external logger if provided (handle async function)
@@ -650,20 +649,6 @@ def debug(
650649
self.log(message, level=2, category=category, auxiliary=auxiliary)
651650

652651

653-
def get_logger(name: str, config: LogConfig) -> StagehandLogger:
654-
"""
655-
Factory function to get a configured logger instance for a module.
656-
657-
Args:
658-
name: The name of the module requesting the logger
659-
config: LogConfig instance with logging configuration
660-
661-
Returns:
662-
StagehandLogger: Configured logger instance
663-
"""
664-
return StagehandLogger(config=config)
665-
666-
667652
# Create a synchronous wrapper for the async default_log_handler
668653
def sync_log_handler(log_data: dict[str, Any]) -> None:
669654
"""

0 commit comments

Comments
 (0)