Skip to content

refactor(adapter): normalize adapter request state - #61

Draft
isaacbmiller wants to merge 1 commit into
mainfrom
reviewable/react-v2-downscope-01-adapter-request-state
Draft

refactor(adapter): normalize adapter request state#61
isaacbmiller wants to merge 1 commit into
mainfrom
reviewable/react-v2-downscope-01-adapter-request-state

Conversation

@isaacbmiller

Copy link
Copy Markdown

Summary

This PR introduces a private normalized request state for adapters and routes the core adapter call path through a normalized LM request/response boundary. The public adapter surface stays the same: callers still use Adapter.__call__, Adapter.acall, and Adapter.format as before.

Why state

Adapter preprocessing can temporarily change the shape of a request. Native tool calling is the clearest example: the user-facing source signature may include tools and tool_calls, but the prompt sent to the LM should hide those fields while the LM request carries native tool specs separately.

Before this change, that request-scoped information was spread across local variables and legacy kwargs. _AdapterRequestState makes the split explicit:

  • source_signature: the original DSPy signature that final parsed outputs must conform to.
  • render_signature: the signature actually used to format text prompts after preprocessing hides native-only fields.
  • inputs and lm_kwargs: copied request data, so preprocessing does not mutate caller-owned dictionaries.
  • tools: normalized LMToolSpec values extracted from legacy LM kwargs and passed through the normalized request object.
  • hidden_output_fields: output fields present on the source signature but intentionally absent from the render signature, so parsing can restore consistent final output keys.

That state is private, but it gives the adapter pipeline one durable handoff object from preprocessing through rendering, LM execution, and parsing. It is deliberately not a public extension point; this PR is only trying to normalize the internal handoff before later commits add more capable rendering behavior.

Private interface flow

The new private flow is:

  1. _prepare_request_state(...) copies inputs/kwargs, runs existing preprocessing, extracts native tool specs, and returns _AdapterRequestState.
  2. _render_request(...) formats messages through _format_request_with_callbacks(...), prepares request kwargs, and builds an LMRequest.
  3. _call_lm(...) / _acall_lm(...) convert the normalized request back to the current BaseLM-compatible call shape, then normalize legacy LM outputs into LMResponse.
  4. _parse_response(...) parses text with the render signature, restores native tool/native response fields against the source signature, fills absent source outputs with None, and preserves logprobs.

_format_request_with_callbacks(...) is intentionally private. It exists so the new state-aware render path still emits the existing adapter format callbacks without turning the state object into public API. TwoStepAdapter gets the same private wrapper while keeping its custom first-stage prompt rendering.

Tests

Added focused base adapter coverage for:

  • normal request state preserving source/render signatures and copying inputs/kwargs
  • request rendering preserving demos, messages, kwargs, and empty tools on the non-native path
  • history expansion without mutating inputs or rendering raw history into the current user turn
  • normal text response parsing with logprobs
  • native tool response parsing that combines visible text fields and tool calls

Validation

  • uv run --frozen ruff check --fix-only --diff --exit-non-zero-on-fix
  • uv run --frozen pytest -q --tb=short tests/adapters/test_adapter_base.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant