refactor(adapter): normalize adapter request state - #61
Draft
isaacbmiller wants to merge 1 commit into
Draft
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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, andAdapter.formatas 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
toolsandtool_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.
_AdapterRequestStatemakes 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.inputsandlm_kwargs: copied request data, so preprocessing does not mutate caller-owned dictionaries.tools: normalizedLMToolSpecvalues 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:
_prepare_request_state(...)copies inputs/kwargs, runs existing preprocessing, extracts native tool specs, and returns_AdapterRequestState._render_request(...)formats messages through_format_request_with_callbacks(...), prepares request kwargs, and builds anLMRequest._call_lm(...)/_acall_lm(...)convert the normalized request back to the currentBaseLM-compatible call shape, then normalize legacy LM outputs intoLMResponse._parse_response(...)parses text with the render signature, restores native tool/native response fields against the source signature, fills absent source outputs withNone, and preserveslogprobs._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.TwoStepAdaptergets the same private wrapper while keeping its custom first-stage prompt rendering.Tests
Added focused base adapter coverage for:
historyinto the current user turnlogprobsValidation
uv run --frozen ruff check --fix-only --diff --exit-non-zero-on-fixuv run --frozen pytest -q --tb=short tests/adapters/test_adapter_base.py