Skip to content

ci: run the Envoy ext_proc path against a real Envoy, in every body mode - #113

Open
CharmingGroot wants to merge 3 commits into
feat/ext-proc-modesfrom
ci/envoy-e2e
Open

CharmingGroot wants to merge 3 commits into
feat/ext-proc-modesfrom
ci/envoy-e2e

Conversation

@CharmingGroot

Copy link
Copy Markdown
Owner

Stacked on #112.

The Envoy behaviour in #110 and #112 was checked by hand. This PR asserts it on every push against a real Envoy, using the production ruleset (deploy/rules.yaml, deliberately not overridable).

tests/e2e/envoy/

  • run.sh <pasu-proxy> <envoy-image> builds nothing.
    • It waits for real readiness: the pasu gRPC port, then Envoy admin /ready = LIVE, then the listener.
    • It traps cleanup.
    • Networking: --network host on Linux, host.docker.internal elsewhere.
  • mock_provider.py is a stdlib OpenAI-compatible provider.
    • It records every request, including one cut off mid-body, so "never reached the provider" counts bytes that did, not only requests that finished.
    • It paces its SSE events like a model generating tokens.
  • slow_post.py sends a prompt as a chunked request in two pieces, with the resident number in the first.
  • envoy.yaml.tmpl renders the request/response body and trailer modes. Its filter block matches integrations/envoy/envoy.yaml.

25 cases

phase cases
BUFFERED passthrough · text and streamed text byte-identical · identity upstream · shell tool call (streamed or not) 403 with no chunk · /v1/embeddings 403, never at the provider
BUFFERED + --block-pii-kr clean prompt passes · resident number 403, never at the provider · --redact delivers [REDACTED:ko-rrn] with a matching content-length
response_body_mode: NONE passthrough still works · completion 500 pasu_misconfigured naming the setting
pasu down 500 (failure_mode_allow: false)
STREAMED / FULL_DUPLEX_STREAMED text intact · streamed text intact · tool call not delivered · streamed tool call: no chunk (full duplex: clean 403) · resident number, sent in pieces, never at the provider · resident number 403

Mutation-checked on Envoy 1.38:

  • With misconfigured() returning None, the NONE case fails: the shell call gets a 200.
  • With STREAMED piece-clearing disabled, three cases fail: duplicated streamed text, a streamed shell tool call delivered with a 200, and the number reaching the provider.

Before the mock paced its events and recorded truncated requests, that second mutant passed: Envoy coalesced every body into one piece.

CI job

Envoy ext_proc E2E (linux) pins envoyproxy/envoy:v1.37.6. A red job should mean pasu changed, not Envoy. AGENTS.md now lists the jobs as they actually are.

Run locally on Envoy 1.38 (macOS, bridge network): 25 passed. Not run locally: the Linux --network host path and the 1.37.6 image. This PR's CI run is the first check of both.

🤖 Generated with Claude Code

The ext_proc front-end was verified against Envoy 1.37 by hand. Those runs
are now a harness (tests/e2e/envoy/run.sh) and a CI job: a real Envoy
between curl and a stdlib mock OpenAI provider, pasu-proxy judging with the
production ruleset (deploy/rules.yaml), 13 cases each asserting PASS/FAIL:
passthrough, text and streamed text byte-equal to what the provider sent,
identity upstream for a gzip client, shell tool calls (streamed or not)
refused 403 with no chunk delivered, /v1/embeddings and a Korean RRN refused
before the provider, redaction with a matching content-length,
response_body_mode NONE refused with 500, and a stopped pasu answered 500.

The mock records every request before replying, so "never reached the
provider" is asserted rather than assumed. Readiness waits for pasu's gRPC
port and for Envoy's admin to report LIVE with the listener bound; a trap
removes the container and processes. On Linux Envoy runs with --network
host; elsewhere it reaches the host through host.docker.internal.

Envoy is pinned to v1.37.6 rather than v1.37-latest, so a red job means
pasu changed, not Envoy. With misconfigured() forced to return None the
NONE case fails with the shell call delivered as a 200.

Signed-off-by: Ho Geun Choi <ohyes9711@gmail.com>
AGENTS.md said CI was four jobs; it is six in ci.yml (MSRV was missing,
and the Envoy E2E is new), with pii.yml alongside. It now says how to run
the Envoy harness locally. integrations/README.md notes that its Envoy
claims are checked by that harness; CHANGELOG records it.

Signed-off-by: Ho Geun Choi <ohyes9711@gmail.com>
The harness only ran BUFFERED, and its mock wrote small streams in one go and
recorded a request only once it had read the whole body. Under those
conditions disabling pasu's piece clearing in STREAMED still passed: Envoy
coalesced every body into one piece, and a prompt reset mid-body left no
record at the provider.

The mock now paces its SSE events and records a request cut off mid-body;
a small client sends a prompt in two pieces with the resident number in the
first. With clearing disabled the run now fails three ways: a streamed text
completion duplicated, a streamed shell tool call delivered with a 200, and
the number reaching the provider. The misconfiguration case checks the new
pasu_misconfigured type. 25 cases, all passing on Envoy 1.38.

Signed-off-by: Ho Geun Choi <ohyes9711@gmail.com>
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