Skip to content

feat(proxy): make the wire format a trait, not a closed enum - #106

Merged
CharmingGroot merged 1 commit into
mainfrom
feat/open-wire-format
Sep 1, 2026
Merged

CharmingGroot merged 1 commit into
mainfrom
feat/open-wire-format

Conversation

@CharmingGroot

Copy link
Copy Markdown
Owner

Provider was an enum with three variants, so a company running an in-house gateway with its own request and response shape had to fork pasu-proxy to be guarded at all.

Everything else in this repository is replaceable behind a trait. The one thing that decides whether pasu can sit in front of your traffic was not.

WireFormat

pub trait WireFormat: Send + Sync {
    fn name(&self) -> &str;
    fn tool_calls(&self, body: &[u8]) -> Option<Vec<ToolCall>>;
    fn tool_calls_streaming(&self, body: &[u8]) -> Option<Vec<ToolCall>>;
    fn visit_prompt(&self, value: &mut Value, f: &mut dyn FnMut(&str) -> Option<String>) -> Option<()>;
}

Provider still exists and still ships the three formats — it is now one implementation rather than the definition of what a format can be. ProxyState holds an Arc<dyn WireFormat>: the proxy runs a format instead of knowing which ones exist.

visit_prompt covers reading and rewriting in one method deliberately. Two that decided separately which fields hold prose would drift, and the day they did, a scanner would be reading a field the redactor no longer edits.

The seam is tested, not just declared

tests/custom_wire_format.rs drives a format defined outside parse.rs all the way through the proxy — a bare input string for the prompt, {"invoke": …} for a tool call, resembling no provider on purpose.

It is written the way an outside adapter has to be written: nothing from parse.rs reused, no variant added, no edit inside the proxy. So if the extension point regresses to something only the built-ins can satisfy, this stops compiling.

Both halves are covered — a tool-call-only trait would have silently left request inspection unable to find the prompt:

  • a denied tool call in the custom shape is blocked by the same ruleset
  • an allowed one passes
  • request inspection reads the custom prompt field, refuses, and does not quote what it caught

No behaviour change for the three built-in formats.

🤖 Generated with Claude Code

Provider was an enum with three variants, so a company running an in-house
gateway with its own request and response shape had to fork pasu-proxy to be
guarded at all. Everything else in this repository is replaceable behind a
trait; the one thing that decides whether pasu can sit in front of your traffic
was not.

WireFormat is that trait. Provider still exists and still ships the three
formats, but it is now one implementation rather than the definition of what a
format can be, and the proxy holds an Arc<dyn WireFormat> — it runs a format
instead of knowing which ones exist.

visit_prompt covers reading and rewriting in one method deliberately. Two that
decided separately which fields hold prose would drift, and the day they did, a
scanner would be reading a field the redactor no longer edits.

The trait existing is not the same as the seam working, so there is a test that
drives a format defined outside parse.rs all the way through — a bare `input`
string for the prompt, `{"invoke": …}` for a tool call, resembling no provider
on purpose. It is written the way an outside adapter has to be written: nothing
from parse.rs reused, no variant added, no edit inside the proxy. Both halves are
covered, because a tool-call-only trait would have silently left request
inspection unable to find the prompt.

No behaviour change for the three built-in formats.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@CharmingGroot
CharmingGroot merged commit 431b8e5 into main Sep 1, 2026
5 checks passed
@CharmingGroot
CharmingGroot deleted the feat/open-wire-format branch September 1, 2026 04:30
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