"""Lock per-step varying metadata (step counter, wall-clock date) at the tail of the user message. The agent's user message looks roughly: ... ... ... ... [...] [...] [unavailable skills info] ... <-- new home `` carries a step counter and `datetime.now()`, both of which change between calls. Keeping it at the very end means everything above it can in principle be cached; moving it back into `` would silently shrink the cacheable prefix. """ from browser_use.agent.prompts import AgentMessagePrompt from browser_use.agent.views import AgentStepInfo from browser_use.browser.views import BrowserStateSummary, PageInfo, TabInfo from browser_use.dom.views import SerializedDOMState from browser_use.filesystem.file_system import FileSystem def _make_prompt(tmp_path, step_number: int = 3) -> AgentMessagePrompt: dom_state = SerializedDOMState(_root=None, selector_map={}) bs = BrowserStateSummary( url='https://example.test/foo', title='Test', tabs=[TabInfo(target_id='abcd1234', url='https://example.test/foo', title='Test')], page_info=PageInfo( viewport_width=1280, viewport_height=720, page_width=1280, page_height=1440, scroll_x=0, scroll_y=0, pixels_above=0, pixels_below=720, pixels_left=0, pixels_right=0, ), dom_state=dom_state, is_pdf_viewer=False, recent_events=None, closed_popup_messages=[], screenshot=None, ) fs = FileSystem(base_dir=str(tmp_path), create_default_files=False) return AgentMessagePrompt( browser_state_summary=bs, file_system=fs, agent_history_description='existing history', task='Do the test thing', step_info=AgentStepInfo(step_number=step_number, max_steps=50), ) def test_step_info_lives_at_suffix(tmp_path): content = _make_prompt(tmp_path).get_user_message(use_vision=False).content assert isinstance(content, str) assert '' in content assert content.index('') < content.index('') assert content.index('') < content.index('') # Suffix: step_info must come after agent_state and browser_state. assert content.index('') < content.index('') assert content.index('') < content.index('') # And it must NOT live inside any more. state_start = content.index('') state_end = content.index('') assert '' not in content[state_start:state_end], 'per-step metadata leaked into prefix region' assert '' not in content[state_start:state_end], 'user request leaked back into ' def test_agent_history_end_marker_is_present_once(tmp_path): content = _make_prompt(tmp_path).get_user_message(use_vision=False).content assert isinstance(content, str) assert content.count('') == 1, ( 'super important: LLM gateway cache splitting expects exactly one marker' ) def test_prefix_up_to_step_info_is_stable_across_steps(tmp_path): """Step number and date can change; the bytes before must not.""" a = _make_prompt(tmp_path, step_number=3).get_user_message(use_vision=False).content b = _make_prompt(tmp_path, step_number=4).get_user_message(use_vision=False).content assert isinstance(a, str) and isinstance(b, str) prefix_end = a.index('') assert a[:prefix_end] == b[:prefix_end], 'message bytes diverged before — step counter is leaking into the prefix' # Sanity: the tails do differ (step counter advanced). assert a[prefix_end:] != b[prefix_end:] def test_agent_state_block_unaffected_by_step_change(tmp_path): """ should not include per-step-varying metadata.""" a = _make_prompt(tmp_path, step_number=3).get_user_message(use_vision=False).content b = _make_prompt(tmp_path, step_number=4).get_user_message(use_vision=False).content assert isinstance(a, str) and isinstance(b, str) def agent_state_block(s: str) -> str: return s[s.index('') : s.index('')] assert agent_state_block(a) == agent_state_block(b)