feat(filter): add rehydrate filter for previous_response_id (#494)#604
Draft
leseb wants to merge 9 commits into
Draft
feat(filter): add rehydrate filter for previous_response_id (#494)#604leseb wants to merge 9 commits into
leseb wants to merge 9 commits into
Conversation
8bfa482 to
3974848
Compare
Collaborator
Author
|
converting to a draft as i just realized that the filter should not mutate the body but just pass a state - reworking this |
Collaborator
|
This PR has been in |
Add ResponsesState, stored in RequestExtensions, to carry parsed request data across the validate → rehydrate → tool_parse → responses_proxy → stream_events → tool_dispatch pipeline. Extracts input messages (string or array), tools, and tool_choice from the request body; tracks tool calls, output items, usage, and agentic loop iteration across inference rounds. Signed-off-by: Sébastien Han <seb@redhat.com>
The openai_response_store filter now registers its initialized store backend as 'default' in the ResponseStoreRegistry on ctx, making it accessible to downstream filters (rehydrate, compact) that need to read stored responses. Signed-off-by: Sébastien Han <seb@redhat.com>
Implement the rehydrate filter that loads conversation context from `previous_response_id` by fetching stored responses and prepending their message history to the current request's `input` field. The filter runs in on_request_body at end-of-stream with ReadWrite body access. It reads from the ResponseStoreRegistry populated by the upstream openai_response_store filter, validates the stored response status is 'completed', assembles previous messages with current input, and writes the enriched body back. Falls back to reconstructing from input + output when messages is null/empty. Signed-off-by: Sébastien Han <seb@redhat.com>
The response store filter now always initializes its backend during on_request, regardless of format metadata. This is necessary because format metadata is set by the classifier in on_request_body (a later phase), and downstream filters like rehydrate need the store available during on_request_body. The skip_persist guard still gates actual persistence in later phases. Adds the rehydrate example config, integration test, test-utils ai-inference feature passthrough, and README entry. Signed-off-by: Sébastien Han <seb@redhat.com>
With StreamBuffer mode, the Pingora handler runs pre_read_body (calling all filters' on_request_body) BEFORE on_request. The store filter only registered its backend in on_request, so the rehydrate filter couldn't find it during pre-read. Add on_request_body to ResponseStoreFilter that eagerly initializes and registers at end-of-stream. Also renames filter to openai_responses_rehydrate, fixes user_message_item to take &str, and deduplicates shared constants into the parent responses module. Signed-off-by: Sébastien Han <seb@redhat.com>
…ing body The rehydrate filter no longer rewrites the request body with assembled conversation history. It validates the previous_response_id, confirms the stored response status is completed, and promotes the ID to filter metadata. Body construction is deferred to the downstream responses_proxy filter which will consume ResponsesState. Also fixes post-rebase build errors: defines store constants locally, updates SqliteResponseStore::new call signature, and resolves clippy lints. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Sébastien Han <seb@redhat.com>
…hydrate After validating the previous response, the rehydrate filter now creates ResponsesState from the parsed request body and prepends the stored conversation history to the current input messages. Downstream filters read from state.messages instead of the raw body. Signed-off-by: Sébastien Han <seb@redhat.com>
- Gate store initialization on classifier metadata: only open the backend for Responses API traffic that needs persistence or rehydration, not for unrelated POSTs. - Capture the original request input during request-body processing and persist it alongside the response, since a spec-conformant ResponseResource does not include the create-request input field. - Scope response store registrations per pipeline/listener to prevent cross-listener data leakage when multiple listeners or hot-reloaded configs use different store backends. Signed-off-by: Sébastien Han <seb@redhat.com>
3974848 to
884fa92
Compare
Collaborator
Author
|
this now lives on top of #635 |
Fix three CI failures: - Clippy: use map_or_else instead of map().unwrap_or_else() in ResponseCapture::from_response_json - Rustdoc: remove intra-doc links to private ResponsesState from public RehydrateFilter docs - Test: update postgres store test to expect both normalized input and output in persisted messages (matching sqlite test) Signed-off-by: Sébastien Han <seb@redhat.com>
3 tasks
Collaborator
Author
|
ok i extracted some portions into #646 so this one will be smaller to review |
leseb
added a commit
to leseb/praxis
that referenced
this pull request
Jun 22, 2026
The pipeline_persists_rehydrated_messages_when_response_omits_input test depends on the openai_responses_rehydrate filter which lives in the rehydrate PR (praxis-proxy#604). Move it there. Signed-off-by: Sébastien Han <seb@redhat.com>
leseb
added a commit
to leseb/praxis
that referenced
this pull request
Jun 22, 2026
The pipeline_persists_rehydrated_messages_when_response_omits_input test depends on the openai_responses_rehydrate filter which lives in the rehydrate PR (praxis-proxy#604). Move it there. Signed-off-by: Sébastien Han <seb@redhat.com>
leseb
added a commit
to leseb/praxis
that referenced
this pull request
Jun 22, 2026
The pipeline_persists_rehydrated_messages_when_response_omits_input test depends on the openai_responses_rehydrate filter which lives in the rehydrate PR (praxis-proxy#604). Move it there. Signed-off-by: Sébastien Han <seb@redhat.com>
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
openai_responses_rehydratefilter that loads conversation context fromprevious_response_idby fetching stored responses and prepending their message history to the current request'sinputfieldResponseStoreFilterto register its store backend inctx.response_storesduring bothon_requestandon_request_body, ensuring availability duringStreamBufferpre-read when body filters run before request filtersMockStore), 2 integration tests, example config (rehydrate.yaml), and updates tofull-flow.yamlDEFAULT_STORE_NAME,DEFAULT_TENANT_ID,TENANT_METADATA_KEY) into the parent responses moduleTest plan
cargo test -p praxis-proxy-filter -- rehydrate— 23 unit tests passcargo test -p praxis-tests-integration --test suite -- rehydrate— 2 integration tests pass (store+rehydrate end-to-end, passthrough)make lint— cleanmake build— cleanmake doc— cleanCloses #494