personal memory agent
0

Configure Feed

Select the types of activity you want to include in your feed.

solstone / tests / test_openhands_context_boundary.py
3.3 kB 90 lines
1# SPDX-License-Identifier: AGPL-3.0-only 2# Copyright (c) 2026 sol pbc 3 4"""OpenHands dependency boundary tests for context-window classification.""" 5 6from __future__ import annotations 7 8from types import ModuleType 9from typing import Any 10 11import pytest 12 13from solstone.think.providers.shared import ( 14 _CONTEXT_WINDOW_PATTERNS, 15 classify_provider_error, 16) 17 18# Documents the OpenHands SDK 1.27.1 wrapper split for Solstone's recognised 19# context-window messages. The behavioral invariant is classification below; 20# this mapping makes dependency drift explicit and reviewable. 21_OPENHANDS_SDK_1_27_1_CONTEXT_WRAPPERS = { 22 "exceeds the available context size": "LLMBadRequestError", 23 "context size has been exceeded": "LLMBadRequestError", 24 "exceeds the context window": "LLMBadRequestError", 25 "maximum context length": "LLMBadRequestError", 26 "longer than the model's context length": "LLMBadRequestError", 27 "context length exceeded": "LLMContextWindowExceedError", 28} 29 30 31def _require_attrs(module: ModuleType, *names: str) -> list[Any]: 32 missing = [name for name in names if not hasattr(module, name)] 33 if missing: 34 pytest.skip(f"{module.__name__} does not expose {', '.join(missing)}") 35 return [getattr(module, name) for name in names] 36 37 38def _openhands_sdk_version() -> str: 39 openhands_sdk = pytest.importorskip("openhands.sdk") 40 return str(getattr(openhands_sdk, "__version__", "unknown")) 41 42 43def _mapped_openhands_exception(message: str) -> BaseException: 44 litellm_exceptions = pytest.importorskip("litellm.exceptions") 45 openhands_mapping = pytest.importorskip("openhands.sdk.llm.exceptions.mapping") 46 (bad_request_error,) = _require_attrs(litellm_exceptions, "BadRequestError") 47 (map_provider_exception,) = _require_attrs( 48 openhands_mapping, "map_provider_exception" 49 ) 50 51 exc = bad_request_error( 52 message, 53 model="gemini-test", 54 llm_provider="google", 55 ) 56 return map_provider_exception(exc) 57 58 59def test_expected_wrapper_mapping_covers_every_context_pattern() -> None: 60 assert set(_OPENHANDS_SDK_1_27_1_CONTEXT_WRAPPERS) == set( 61 _CONTEXT_WINDOW_PATTERNS 62 ), ( 63 "OpenHands context-wrapper expectation must cover every Solstone " 64 "context-window pattern before the dependency contract can be reviewed." 65 ) 66 67 68@pytest.mark.parametrize("message", _CONTEXT_WINDOW_PATTERNS) 69def test_context_window_patterns_classify_after_real_openhands_mapping( 70 message: str, 71) -> None: 72 mapped = _mapped_openhands_exception(message) 73 74 assert classify_provider_error(mapped, "google") == "context_window_exceeded" 75 76 77def test_openhands_context_wrapper_split_matches_pinned_contract() -> None: 78 expected = _OPENHANDS_SDK_1_27_1_CONTEXT_WRAPPERS 79 actual = { 80 message: type(_mapped_openhands_exception(message)).__name__ 81 for message in _CONTEXT_WINDOW_PATTERNS 82 } 83 84 assert actual == expected, ( 85 f"OpenHands SDK {_openhands_sdk_version()} changed the context-window " 86 f"wrapper mapping: {actual!r}. This is a Solstone/OpenHands dependency " 87 "contract; review classify_provider_error's rule ordering before " 88 "changing _OPENHANDS_SDK_1_27_1_CONTEXT_WRAPPERS, don't just update the " 89 "constant." 90 )