feat(filter): add identity header guard filter - #709
Open
yossiovadia wants to merge 644 commits into
Open
Conversation
…raxis-proxy#274) Signed-off-by: Sébastien Han <seb@redhat.com>
Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> Co-authored-by: Sébastien Han <seb@redhat.com>
…04) (praxis-proxy#290) Signed-off-by: Sébastien Han <seb@redhat.com>
…-proxy#270) Signed-off-by: Sébastien Han <seb@redhat.com>
…roxy#293) Wire the openai_stream_events filter into the Responses API full-flow pipeline between openai_response_store and openai_responses_rehydrate. Signed-off-by: Sébastien Han <seb@redhat.com>
…is-proxy#299) docs(filters): generate missing openai_stream_events doc and lint in CI Run `cargo xtask generate-filter-docs` to produce the missing `openai_stream_events.md` and refresh stale docs (a2a, mcp, openai_response_store, reference index). Add `cargo xtask lint-filter-docs` to the `make lint` target so CI catches stale or missing filter documentation going forward. Signed-off-by: Sébastien Han <seb@redhat.com>
This also removes CI shims to build and test with the main branch while we were waiting for this release. Signed-off-by: Shane Utt <shaneutt@linux.com>
…axis-proxy#308) The lint-filter-docs check fails in CI because parse_shared_config_items() hardcodes ../praxis to find shared config types. Since praxis moved from a path dependency to a crates.io dependency (v0.4.0), ../praxis does not exist in CI. Fall back to resolving praxis-proxy-filter source via cargo metadata when ../praxis is not available. Also trim unused core/tls source parsing since only filter/payload_processing types (OnInvalidBehavior) are referenced by AI filter configs. Signed-off-by: Sébastien Han <seb@redhat.com>
…dencies group (praxis-proxy#307) chore(deps): bump jsonwebtoken in the rust-dependencies group Bumps the rust-dependencies group with 1 update: [jsonwebtoken](https://github.com/Keats/jsonwebtoken). Updates `jsonwebtoken` from 9.3.1 to 10.4.0 - [Changelog](https://github.com/Keats/jsonwebtoken/blob/master/CHANGELOG.md) - [Commits](Keats/jsonwebtoken@v9.3.1...v10.4.0) --- updated-dependencies: - dependency-name: jsonwebtoken dependency-version: 10.4.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: rust-dependencies ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sébastien Han <seb@redhat.com>
…roxy#306) Bumps [actions/github-script](https://github.com/actions/github-script) from 7.0.1 to 9.0.0. - [Release notes](https://github.com/actions/github-script/releases) - [Commits](actions/github-script@60a0d83...3a2844b) --- updated-dependencies: - dependency-name: actions/github-script dependency-version: 9.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sébastien Han <seb@redhat.com>
…axis-proxy#305) Bumps [github/codeql-action/init](https://github.com/github/codeql-action) from 4.36.2 to 4.36.3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@8aad20d...54f647b) --- updated-dependencies: - dependency-name: github/codeql-action/init dependency-version: 4.36.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sébastien Han <seb@redhat.com>
…raxis-proxy#303) Bumps [github/codeql-action/analyze](https://github.com/github/codeql-action) from 4.36.2 to 4.36.3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@8aad20d...54f647b) --- updated-dependencies: - dependency-name: github/codeql-action/analyze dependency-version: 4.36.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sébastien Han <seb@redhat.com>
…proxy#304) Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 6.1.0 to 6.2.0. - [Release notes](https://github.com/docker/metadata-action/releases) - [Commits](docker/metadata-action@80c7e94...dc80280) --- updated-dependencies: - dependency-name: docker/metadata-action dependency-version: 6.2.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sébastien Han <seb@redhat.com>
…xy#302) Bumps [docker/login-action](https://github.com/docker/login-action) from 4.2.0 to 4.4.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](docker/login-action@650006c...af1e73f) --- updated-dependencies: - dependency-name: docker/login-action dependency-version: 4.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sébastien Han <seb@redhat.com>
Signed-off-by: Francisco Javier Arceo <farceo@redhat.com>
…-proxy#248) * feat(conversations): rehydrate and append via conversation.id When a POST /v1/responses request includes a `conversation` field (string ID or `{"id": "..."}` object), load the stored conversation and prepend its messages to the input. `previous_response_id` takes precedence when both are present. After a successful non-streaming response with status "completed", input and output items are appended back to the conversation so subsequent requests see the full accumulated history. - Rehydrate filter: add conversation-based rehydration path alongside existing previous_response_id support - Proxy filter: strip `conversation` field from outbound body - Conversations filter: add response-phase append-back with guards for streaming, background, and non-completed responses - Store filter: trigger store init when has_conversation metadata set - Full-flow config: add openai_conversations filter with on_invalid: continue for format classifier compatibility - Add unit and integration tests for all new paths Signed-off-by: Sébastien Han <seb@redhat.com> * fix(tests): remove stale ai-inference feature gate from conversations_rehydrate The ai-inference feature no longer exists in the integration test crate. Signed-off-by: Sébastien Han <seb@redhat.com> * test(conversations): add unit tests for append-back response hooks Cover on_response arming logic (metadata guards, content-type, status code) and on_response_body paths (skip non-completed, invalid JSON, empty items, full persist cycle) to restore coverage above 90%. Signed-off-by: Sébastien Han <seb@redhat.com> * fix(lint): resolve clippy and compile errors after rebase Suppress large_stack_frames on on_request dispatcher and fix BackendGuard import path after test-utils restructuring on main. Signed-off-by: Sébastien Han <seb@redhat.com> * fix(config): restore original backend endpoint port in full-flow example The full-flow example config endpoint was changed from 3001 to 8000, breaking the integration tests that use port mapping keyed on 127.0.0.1:3001. Signed-off-by: Sébastien Han <seb@redhat.com> * fix: address review feedback on conversation rehydration - Reject malformed conversation values (e.g. {}, 42) with 400 instead of silently forwarding them to the backend - Defensively strip conversation from proxy passthrough body as a safety net against unresolved fields leaking upstream - Propagate append-back store errors with ? instead of swallowing them, consistent with the response store filter - Replace fixed sleep in integration test with retry/poll loop - Add tenant metadata mismatch test and make mock store tenant-aware Signed-off-by: Sébastien Han <seb@redhat.com> * fix: revert append-back to warn+swallow and add conversation store failure test Append-back is a best-effort side-effect — propagating store errors would abort the client's already-successful inference response. Revert to warn and continue. Add missing test for store failure during conversation rehydration (the existing test only covered the previous_response_id path). Signed-off-by: Sébastien Han <seb@redhat.com> --------- Signed-off-by: Sébastien Han <seb@redhat.com>
) * test(mcp): add unit tests for broker config validation (praxis-proxy#278) Add 63 inline unit tests to config.rs covering CacheScope, InvalidToolPolicy, serde deserialization, validate_schema_object edge cases, validate_path, server/tool/exposed name uniqueness, cache field profile validation, version validation, build_config integration, and catalog entry construction. Also fix pre-existing unfulfilled #[expect(dead_code)] on CatalogTool by using cfg_attr(not(test), expect(...)) since test builds use all fields. Signed-off-by: Sébastien Han <seb@redhat.com> * test(mcp): add coverage for broker config validation gaps Add tests for uncovered code paths in MCP broker config validation: - build_config_minimal: assert default_version and supported_versions are correctly derived from the current protocol profile - validate_server_paths: direct unit tests with invalid path and multi-server iteration proving the second server is checked - validate_server_clusters: positive test with valid non-empty cluster - build_config_with_tools_and_prefix: assert input_schema preservation, cluster, backend_path, and server_name on catalog entries Widen validate_server_paths and validate_server_clusters to pub(super) so the broker test module can call them directly. Signed-off-by: Sébastien Han <seb@redhat.com> * test(mcp): address review feedback on broker config tests Remove short-form separator comments from the test module — test function names provide sufficient organization. Replace fragile Debug-format assertions with direct field access for cluster and backend_path on catalog entries, and remove the associated inline comment. Signed-off-by: Sébastien Han <seb@redhat.com> --------- Signed-off-by: Sébastien Han <seb@redhat.com>
* test: replay sanitized agent sessions Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * chore: update crossbeam-epoch advisory fix Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * docs: explain replay fixture examples Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * test: share temp sqlite helper Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * test: address replay review comments Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * test: add session replay importer tooling Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * test: assert replayed response body is stored Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * test: harden replay fixture paths Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * test: avoid replay fixture source path leaks Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> --------- Signed-off-by: Francisco Javier Arceo <farceo@redhat.com>
Signed-off-by: Brent Salisbury <bsalisbu@redhat.com>
test(a2a): add unit tests for envelope parsing (praxis-proxy#280) Signed-off-by: Sébastien Han <seb@redhat.com>
…tion (praxis-proxy#309) * feat(filter): add tool_parse filter for Responses API tool classification Add a new tool_parse filter that parses the tools array and tool_choice from Responses API POST /v1/responses requests. Classifies each tool by type (function, web_search, file_search, code_interpreter, computer_use, image_generation, tool_search, MCP) and promotes summary facts to metadata and filter results for branch conditions. The filter does not mutate the request body and is gated to only process Responses API create requests. Malformed function tools (missing name) are still counted by discriminator for has_tools routing. Unsafe values (oversized, control characters) in tool_choice are silently dropped rather than failing the filter. Closes praxis-proxy#42 Signed-off-by: Sébastien Han <seb@redhat.com> * fix(tool_parse): promote function_count to filter results function_count was written to metadata but never promoted to filter_results or restored, making it invisible to branch_chains conditions. Add it to promote_filter_results() and restore via restore_function_count(). Signed-off-by: Sébastien Han <seb@redhat.com> * fix(tool_parse): recognize web_search_2025_08_26 as WebSearch The OpenAI API lists web_search_2025_08_26 as a valid web search tool type. Without this match arm, requests using that literal fall through to Unknown, setting has_tools=true but has_web_search=false, causing branch_chains on has_web_search to silently miss them. Signed-off-by: Sébastien Han <seb@redhat.com> * docs(tool_parse): note why accumulate_tool extracts full details The parser clones names, configs, and MCP entries that tool_parse does not consume today. Document that tool_dispatch (praxis-proxy#26) will need these fields. Signed-off-by: Sébastien Han <seb@redhat.com> * test(tool_parse): assert FilterAction and add restore unit tests Assert Release on has-tools paths and Continue on no-tools/skip paths in every run_filter caller. Add unit tests for restore_presence_flags, restore_function_count, and restore_tool_choice (present and absent). Signed-off-by: Sébastien Han <seb@redhat.com> --------- Signed-off-by: Sébastien Han <seb@redhat.com>
praxis-proxy#261) feat(apis): add Deserialize to TokenUsageProvider and streaming token extraction Add serde Deserialize to TokenUsageProvider for YAML config parsing. Add extract_streaming_tokens for providers that spread token counts across multiple SSE events (Anthropic message_start/message_delta, Bedrock ConverseStream metadata). Signed-off-by: szedan <szedan@redhat.com>
test(config): add unit tests for remaining config modules (praxis-proxy#283) Add inline #[cfg(test)] modules to the four config files that had zero test coverage: - filters/src/prompt_enrich/config.rs (17 tests) - apis/src/openai/responses/model_rewrite/config.rs (24 tests) - apis/src/openai/responses/config.rs (9 tests) - apis/src/anthropic/messages_format/config.rs (10 tests) Tests cover serde defaults, deny_unknown_fields enforcement, validation functions, null-header-disables-promotion, and role/enum round-trips. Signed-off-by: Sébastien Han <seb@redhat.com>
…s-proxy#317) * docs: use language-neutral type names in generated filter docs Replace Rust-specific type names (u8, u16, u32, u64, usize, i32, f64, etc.) with language-neutral equivalents (integer, number) in the xtask filter-doc generator and regenerate all filter documentation. Signed-off-by: Sébastien Han <seb@redhat.com> * test(xtask): table-driven coverage for all numeric primitives Signed-off-by: Sébastien Han <seb@redhat.com> --------- Signed-off-by: Sébastien Han <seb@redhat.com>
…is-proxy#262) feat(filter): add token_count filter for streaming and non-streaming responses Implements issue 211. Extracts token usage from AI inference responses across all five providers via content-type detection. Reuses the SSE scanner from A2A and the token extraction APIs. Writes unified counts to filter_metadata for downstream consumers like token_usage_headers. Signed-off-by: szedan <szedan@redhat.com>
test(a2a): add unit tests for config validation (praxis-proxy#279) Signed-off-by: Sébastien Han <seb@redhat.com>
Signed-off-by: Shane Utt <shaneutt@linux.com>
Signed-off-by: Sébastien Han <seb@redhat.com>
) test(token_usage): add unit tests for provider parsers (praxis-proxy#281) Add 26 inline tests to providers.rs covering edge cases not in the existing tests.rs: missing/null fields, zero values, cache token permutations, saturating_add overflow, Bedrock Converse fallback paths, and malformed inputs across all four parsers. Signed-off-by: Sébastien Han <seb@redhat.com>
* Preserve source records in session replay fixtures Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * Fix session replay importer lint Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * Cover image replay chat completions translation Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * Address session replay review feedback Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * docs: add anthropic messages replay test plan Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * test: guard replay fixtures against local paths Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * test: normalize replay fixture source label Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * test: normalize replay fixture source metadata Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * test: preserve redacted replay id relationships Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * test: keep replay fixture hygiene helper focused Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * test: ignore redacted thinking in replay import Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * test: accept string assistant replay content Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * test: move claude assistant source records Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> --------- Signed-off-by: Francisco Javier Arceo <farceo@redhat.com>
…y replac…" (praxis-proxy#821) This reverts commit 7d1aef9. Signed-off-by: szedan <szedan@redhat.com>
…s-proxy#799) * fix(openai): preserve OGX HTTP responses Signed-off-by: Sumanth Kamenani <skamenan@redhat.com> (cherry picked from commit 8738f3bd19717c74d463e4c9ab05c08d9fe7e76c) * docs(openai): clarify redirect response handling Signed-off-by: Sumanth Kamenani <skamenan@redhat.com> (cherry picked from commit a5bab9cf11a5b6f315b410d9253e11446829c486) * test(openai): cover non-success file responses Signed-off-by: Sumanth Kamenani <skamenan@redhat.com> (cherry picked from commit 7c4bab199b795907be5dfcf3e2d589eb27c85375) * test(openai): cover file search callout statuses Signed-off-by: Sumanth Kamenani <skamenan@redhat.com> (cherry picked from commit fa96e6c7248807117ae8fe04d0df14171de58012) * chore(openai): document response test complexity Signed-off-by: Sumanth Kamenani <skamenan@redhat.com> (cherry picked from commit 57426f8b33b037f0f04092ee732502731042fb5e) * test(openai): cover example non-2xx file responses Prove resolver 502 safety, transparent /v1/files 429, and closed file-search failure. Signed-off-by: Sumanth Kamenani <skamenan@redhat.com> (cherry picked from commit 2b0878d5284deb1616aee9285020cdd9600532ea) * chore(openai): format file resolve test constants Signed-off-by: Sumanth Kamenani <skamenan@redhat.com> (cherry picked from commit 646cf3f6e5bd29e37738109601e1c7ac6b507622) * test(openai): restore file-search failure-mode asserts Closed reject should keep searching because it returns before apply_batch. Open continue still needs the reinference function_call_output. Drop the stale JSON-decode rustdoc on ApiClientError. Signed-off-by: Sumanth Kamenani <skamenan@redhat.com> --------- Signed-off-by: Sumanth Kamenani <skamenan@redhat.com> Co-authored-by: Aslak Knutsen <aslak@4fs.no>
…xis-proxy#818) Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 4.2.0 to 4.3.0. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](docker/setup-buildx-action@bb05f3f...37fe631) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-version: 4.3.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…raxis-proxy#819) Bumps [github/codeql-action/analyze](https://github.com/github/codeql-action) from 4.37.7 to 4.37.8. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@ff2f1c6...db488dd) --- updated-dependencies: - dependency-name: github/codeql-action/analyze dependency-version: 4.37.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…axis-proxy#820) Bumps [github/codeql-action/init](https://github.com/github/codeql-action) from 4.37.7 to 4.37.8. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@ff2f1c6...db488dd) --- updated-dependencies: - dependency-name: github/codeql-action/init dependency-version: 4.37.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Shane Utt <shaneutt@linux.com>
Signed-off-by: Shane Utt <shaneutt@linux.com>
Signed-off-by: Shane Utt <shaneutt@linux.com>
Signed-off-by: Shane Utt <shaneutt@linux.com>
Signed-off-by: Shane Utt <shaneutt@linux.com>
Signed-off-by: Shane Utt <shaneutt@linux.com>
) Bumps rust from 1.97-alpine to 1.98-alpine. --- updated-dependencies: - dependency-name: rust dependency-version: 1.98-alpine dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…praxis-proxy#792) Signed-off-by: Liav Weiss <lweiss@redhat.com>
) * feat(token-count): report prompt cache token breakdown Providers that support prompt caching return how much of the input was served from cache and how much was written to it, but token_count collapsed that detail into a single input total. Callers that price cached input differently from fresh input had no way to recover the split. Record the breakdown alongside the existing counts as two new metadata keys, token.cache_read and token.cache_write, for both JSON and SSE responses: - OpenAI reports cached reads via prompt_tokens_details.cached_tokens and has no cache-write count, so writes are reported as zero. - Anthropic reports both directions via cache_read_input_tokens and cache_creation_input_tokens, in JSON bodies and in the streaming message_start event. - Google reports cached reads via cachedContentTokenCount and has no cache-write count. - Bedrock Converse and the InvokeModel header path carry no cache signal, so neither key is recorded there. Claude served through InvokeModel still reports the full breakdown via the Anthropic response shape. Both keys are a breakdown of token.input, not an addition to it: every provider above already includes the cached portion in the input total, so summing them would double-count. The example config states this so downstream filters do not have to rediscover it. Signed-off-by: Noy Itzikowitz <nitzikow@redhat.com> * fix(token-count): distinguish an unreported cache count from zero Cache counts were stored as plain integers defaulting to zero, so a provider that says nothing about prompt caching produced the same metadata as one that reports no cache hit. A downstream biller cannot tell "this provider never told us" from "this request cached nothing", and the two carry very different meaning when reconciling spend. Model both cache fields as `Option<u64>` so absence stays absent: - `TokenUsage::cache_read` and `cache_write` become `Option<u64>`, and `with_cache` takes options rather than integers. - `set_cache_token_usage` emits each metadata key only when the corresponding count is present, and records a provider-reported `0` as "0" rather than dropping it. - Streaming accumulators only insert a cache entry when the event carried one, so a later event without cache data no longer overwrites an earlier reported count with zero. - `parse_anthropic_event` no longer fabricates `Some(0)` for a `message_delta`, which never carries cache fields at all. The OpenAI and Google shapes stay inclusive (cached tokens are a subset of the input count) while Anthropic stays exclusive (cache read and write add to the input count); only the absent-versus-zero distinction changes. Part of praxis-proxy#577. Signed-off-by: Noy Itzikowitz <nitzikow@redhat.com> * fix(deps): update h2 to 0.4.16 for RUSTSEC-2026-0258 h2 0.4.15 is flagged by RUSTSEC-2026-0258 (unbounded empty DATA frames), failing the dependency-check and security-audit jobs on every branch. Bump the locked version to the patched 0.4.16 release. Lockfile-only change: 0.4.16 is a drop-in patch with an unchanged dependency list, so only the h2 version and checksum move. Signed-off-by: Noy Itzikowitz <nitzikow@redhat.com> * refactor(token_usage): extract record_json_usage from handle_json_body Merging main into the prompt-cache branch recombined the overflow-status guard (praxis-proxy#782) with the cache breakdown recording, pushing handle_json_body one line over the too_many_lines threshold. Extract the end-of-stream JSON usage recording into a dedicated helper to keep both concerns and stay under the limit. Signed-off-by: Noy Itzikowitz <nitzikow@redhat.com> --------- Signed-off-by: Noy Itzikowitz <nitzikow@redhat.com> Signed-off-by: Aslak Knutsen <aslak@4fs.no> Co-authored-by: crstrn13 <acristur@redhat.com> Co-authored-by: Aslak Knutsen <aslak@4fs.no>
…xis-proxy#824) Signed-off-by: mkoushni <mkoushni@redhat.com>
…raxis-proxy#823) Signed-off-by: mkoushni <mkoushni@redhat.com>
praxis-proxy#822) * fix(responses): support the include query parameter on store retrieval Extract the include functionality from conversations/ into a shared openai::include module and wire GET /v1/responses/{id} and its input_items through it, replacing the hardcoded rejection. Fixes praxis-proxy#685 Signed-off-by: Rastislav Papso <rpapso@redhat.com> * style(responses): formatting, linting Apply nigthly rustfmt to fix the linting / formatting. No behavior change. Signed-off-by: Rastislav Papso rpapso@redhat.com Signed-off-by: Rastislav Papso <rpapso@redhat.com> * test(responses): cover include projection on input_items retrieval Add unit tests for GET /v1/responses/{id}/input_items include handling: default omission, both SDK encodings, combination with pagination, rejection of unknown and value-less values, and per-item projection across pages. Signed-off-by: Rastislav Papso <rpapso@redhat.com> * fix(responses): decode query keys before matching on input_items The array-valued include parameter is also sent percent-encoded as include%5B%5D. parse_include decodes keys, but parse_query_params matched the raw key and rejected that spelling as an unknown parameter, returning 400. Percent-decode each key once in parse_query_params so both helpers match the same decoded name. Signed-off-by: Rastislav Papso <rpapso@redhat.com> * style(responses): fix malformed doc string Fixed malformed docstring in include.rs Signed-off-by: Rastislav Papso <rpapso@redhat.com> --------- Signed-off-by: Rastislav Papso <rpapso@redhat.com> Signed-off-by: Rastislav Papso rpapso@redhat.com
…raxis-proxy#829) * fix(azure_ad): schedule token refresh within skewed validity window The refresh was scheduled at refresh_ratio * ttl while the cached token expires at ttl - skew. Whenever ratio * ttl exceeds ttl - skew (any TTL under 120s at the defaults, or a high refresh_ratio), the token went invalid before the refresh fired, producing a deterministic window of 503s every cycle against a healthy token endpoint. Schedule from the skew-adjusted usable lifetime instead, and extend the short-TTL regression test to assert the token is still valid at the scheduled refresh instant. Also race the token fetch against cancellation so dropping a pipeline is not blocked behind an in-flight token request, log the missing-token condition on state transitions instead of once per rejected request so a token outage under load cannot flood the logs, and read the token cache with load() instead of load_full() on the request path. Signed-off-by: szedan <szedan@redhat.com> * docs(azure_ad): describe refresh_ratio against the usable lifetime Review follow-up: the refresh now fires at refresh_ratio of the skew-adjusted usable lifetime (TTL minus the expiry safety margin), not of the raw TTL. Update the module doc YAML example, the refresh_ratio field docs, and the refresh_delay helper doc to match, and regenerate docs/filters/azure_ad.md. Signed-off-by: szedan <szedan@redhat.com> --------- Signed-off-by: szedan <szedan@redhat.com>
…-proxy#828) * feat: add experimental gcp_adc upstream authentication filter Add a gcp_adc filter skeleton behind the off-by-default gcp-adc-filter cargo feature. This establishes the configuration surface (source, scope, service_account, credentials_file, refresh_ratio, with strict cross-field validation and an allowlist for service_account), Application Default Credentials source resolution at construct time, and the fail-closed request path: without a valid cached token, requests are rejected with 503 rather than forwarded unauthenticated. The Authorization value is formatted and marked sensitive by the CachedToken constructor so a token can never reach the cache unredacted. Token acquisition is intentionally not implemented here: filters must not spawn their own refresher threads (praxis-proxy/praxis#1042), so the fetch lands on top of the shared background-task primitive tracked in praxis-proxy/praxis#555, praxis-proxy/praxis#1042, and praxis-proxy/praxis#1043. The filter docs and example config state the fail-closed status explicitly. Includes unit tests, an integration test for the example config, and generated filter documentation (with a GCP acronym mapping for the doc generator). Signed-off-by: szedan <szedan@redhat.com> * ci: lint and test experimental filter features in make targets The azure-ad-filter, gcp-adc-filter, and http-callout-filter cargo features are off by default, so the default-features clippy and test runs never compiled the gated filter code and dead_code/clippy regressions in it went unseen. Add a second workspace clippy pass with the experimental features enabled, run the filters unit tests with them, and run the feature-gated example integration tests, so the CI jobs that call these make targets cover the experimental filters. Signed-off-by: szedan <szedan@redhat.com> --------- Signed-off-by: szedan <szedan@redhat.com>
Captures request headers matching a configurable prefix into filter_metadata and strips them before upstream forwarding. Prevents identity headers (e.g. x-tenant-username, x-tenant-group) from leaking to LLM providers while making them available to downstream filters like external_metering. Fixes praxis-proxy#698 Signed-off-by: Yossi Ovadia <yovadia@redhat.com>
- Add registry assertion for identity_header_guard in build_ai_registry_includes_ai_and_builtin_filters test - Add test for non-UTF-8 header values (stripped but not captured) - Add test for duplicate headers (last-value-wins behavior) - Clarify default namespace test with comment explaining the indirect verification via captures_matching_headers_to_metadata Signed-off-by: Yossi Ovadia <yovadia@redhat.com>
Signed-off-by: Yossi Ovadia <yovadia@redhat.com>
Signed-off-by: Yossi Ovadia <yovadia@redhat.com>
…filters Signed-off-by: Yossi Ovadia <yovadia@redhat.com>
yossiovadia
force-pushed
the
feat/identity-header-guard
branch
from
August 27, 2026 18:49
8428c38 to
0389131
Compare
4 tasks
Contributor
|
@yossiovadia please rebase |
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
identity_header_guardfilter that captures headers matching a configurable prefix intofilter_metadataand strips them before upstream forwardingx-tenant-username,x-tenant-group) from leaking to LLM providersexternal_metering(feat(filter): add external metering filter for usage reporting and balance checks #577)Fixes #698
Motivation
The
external_meteringfilter reads tenant identity from request headers for per-user usage attribution. These headers are set by an upstream auth layer and must not reach the upstream provider.reserved_headersin core only handles hardcodedx-praxis-*prefixes with no metadata capture and no configurable prefixes (core TODO #186).Design
x-tenant-)filter_metadataunder a configurable namespace (prevents collision with verified auth metadata)request_headers_to_removeWhat's included
filters/src/identity_guard/— filter, config, 11 unit tests + 1 doctesttests/integration/tests/suite/examples/identity_header_guard.rs— 3 integration tests (config parse + header capture + strip)examples/configs/identity-header-guard.yaml— example configTest plan
cargo xtask lint-example-tests— passes (example config has test coverage)cargo xtask lint-filter-docs— passes (generated docs up to date)cargo clippy -p praxis-ai-filters -- -D warnings— zero warningsexternal_meteringconsuming captured identity