Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
- Bedrock: Assistant text and reasoning are now preserved alongside tool calls in the same turn instead of being dropped. (#4457)
- Bugfix: With checkpoints stored on S3, a resumed sample can now itself be resumed — previously a crash after resume lost the checkpoint chain and the next retry restarted the sample from scratch.
- Bugfix: Crash recovery now reconstructs `sample.messages` from the first model role's conversation when a solver runs multiple agents concurrently, instead of returning whichever agent's model call happened to fire last. (#4414)
- Web Browser: headful macOS scale-factor detection falls back to 1 instead of crashing when `pyobjc-framework-AppKit` is missing or no display is attached.
- Bugfix: Errored samples that were scored (e.g. via `score_on_error`) now contribute their scores to metrics and `scored_samples`, matching what the sample log shows. (#4412)
- Bugfix: Several raised errors (unexpected Anthropic input block, unsupported image data type, missing sample-buffer database) now interpolate the offending value into the message instead of showing the literal placeholder text.
- Bugfix: Native compaction now truncates an oversized tool output before summarizing instead of crashing or silently summarizing an error when it would exceed the model's context window. (#3600)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
import logging
import subprocess
import sys

logger = logging.getLogger(__name__)


def warn_once(logger: logging.Logger, message: str) -> None:
"""Emit ``message`` as a warning, but only the first time it is seen this process."""
if message not in _warned:
logger.warning(message)
_warned.add(message)


_warned: set[str] = set()

# Playwright launches Chromium with --force-device-scale-factor=1 by default, which
# ensures consistent rendering and measurement behavior using CSS pixels instead of
# native device pixels, regardless of the actual display's DPI. On HiDPI displays,
Expand All @@ -19,12 +32,29 @@
# allows the code to accurately calculate node visibility.


def get_screen_scale_factor() -> float:
if sys.platform == "darwin":
def _darwin_scale_factor() -> float:
try:
# `pip install pyobjc-framework-AppKit` is required to run headfully on macOS
from AppKit import NSScreen # type: ignore # noqa: PLC0415

return NSScreen.mainScreen().backingScaleFactor()
screen = NSScreen.mainScreen()
except ModuleNotFoundError:
warn_once(
logger,
"pyobjc-framework-AppKit is not installed; falling back to a screen "
"scale factor of 1. Install it (`pip install pyobjc-framework-AppKit`) "
"to enable HiDPI scale-factor detection when running headfully on macOS.",
)
return 1.0
except Exception:
return 1.0
# mainScreen() is None when no display is attached (headless / over SSH)
return float(screen.backingScaleFactor()) if screen is not None else 1.0


def get_screen_scale_factor() -> float:
if sys.platform == "darwin":
return _darwin_scale_factor()
elif sys.platform == "win32":
try:
# Using GetDpiForSystem from Windows API
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import sys
import types

from scale_factor import get_screen_scale_factor


def _install_fake_appkit(monkeypatch, screen: object) -> None:
"""Put a fake ``AppKit`` on ``sys.modules`` whose ``mainScreen()`` returns ``screen``."""
module = types.ModuleType("AppKit")
module.NSScreen = types.SimpleNamespace(mainScreen=lambda: screen) # type: ignore[attr-defined]
monkeypatch.setitem(sys.modules, "AppKit", module)


def test_darwin_returns_backing_scale_factor(monkeypatch):
monkeypatch.setattr(sys, "platform", "darwin")
_install_fake_appkit(
monkeypatch, types.SimpleNamespace(backingScaleFactor=lambda: 2.0)
)
assert get_screen_scale_factor() == 2.0


def test_darwin_headless_screen_falls_back_to_one(monkeypatch):
# NSScreen.mainScreen() is None when no display is attached (headless / over SSH);
# dereferencing it used to raise AttributeError instead of defaulting to 1.0.
monkeypatch.setattr(sys, "platform", "darwin")
_install_fake_appkit(monkeypatch, None)
assert get_screen_scale_factor() == 1.0


def test_darwin_missing_appkit_falls_back_to_one(monkeypatch):
# pyobjc-framework-AppKit is an optional dependency; a missing import used to crash
# the probe instead of degrading like the Windows and Linux branches do.
monkeypatch.setattr(sys, "platform", "darwin")
monkeypatch.setitem(
sys.modules, "AppKit", None
) # `from AppKit import ...` -> ImportError
assert get_screen_scale_factor() == 1.0
Loading