Skip to content

[New Plugin] peyeeye PII redaction & rehydration#1621

Open
tim-peyeeye wants to merge 1 commit into
Portkey-AI:mainfrom
peyeeye:add-peyeeye-plugin
Open

[New Plugin] peyeeye PII redaction & rehydration#1621
tim-peyeeye wants to merge 1 commit into
Portkey-AI:mainfrom
peyeeye:add-peyeeye-plugin

Conversation

@tim-peyeeye
Copy link
Copy Markdown

@tim-peyeeye tim-peyeeye commented Apr 26, 2026

Resolves #1620.

Summary

A new guardrail plugin that wraps peyeeye's PII redaction & rehydration API. The plugin runs a round-trip across both hooks:

  1. beforeRequestHookpeyeeye/redact: extracts every text-bearing chunk of the request via getCurrentContentPart, batch-POSTs to /v1/redact, writes the placeholder-token text ([EMAIL_1], [CARD_1], …) back via setCurrentContentPart. Caches the returned session_id (or sealed skey_… blob in stateless mode) keyed on peyeeye:session:${context.metadata.requestID}.
  2. afterRequestHookpeyeeye/rehydrate: pulls the cached session, POSTs the model's response text through /v1/rehydrate so end-users see the real PII values, then best-effort DELETE /v1/sessions/{id} for stateful mode and clears the cache entry. No-ops cleanly if the cache miss (so it's safe to enable on routes where redact wasn't run).

The original PII never reaches the upstream LLM, but the response the gateway hands back to the user contains the original values.

Files

  • plugins/peyeeye/manifest.json — two functions (redact, rehydrate); credentials apiKey (encrypted) + optional apiBase; per-function parameters locale, entities, sessionMode.
  • plugins/peyeeye/globals.tscallRedact, callRehydrate, callDeleteSession, cache-key builder, typed errors. Reuses post() from ../utils; uses native fetch for the DELETE.
  • plugins/peyeeye/redact.ts — pre-call handler.
  • plugins/peyeeye/rehydrate.ts — post-call handler.
  • plugins/peyeeye/peyeeye.test.ts — 11 mock-only Jest tests.
  • plugins/index.ts — adds the two handler imports + peyeeye: { redact, rehydrate } entry.
  • conf.json — adds "peyeeye" to plugins_enabled.

Features

  • Two session modes:
    • stateful (default) — peyeeye stores the token→value mapping under a ses_… id; DELETEd after rehydration.
    • stateless — peyeeye returns a sealed AEAD blob (skey_…); nothing retained server-side; no DELETE.
  • 60+ built-in entity detectors out of the box (email, phone, SSN, credit card, IBAN, addresses, names, …). Per-call narrowing via the entities parameter.
  • BCP-47 locale knob (default auto).
  • Multimodal-aware — text parts in a [{type:"text",text:…},{type:"image_url",…}] content list are redacted in place; non-text parts are left untouched at their original indices.

Behavioral invariants

  • Length-guard: if /v1/redact returns a different count of texts than were sent, raise — never forward partially-redacted data.
  • Shape-guard: an unexpected response shape from /v1/redact raises rather than passing through silently.
  • No silent passthrough: any redact failure surfaces as an error on the plugin response; the gateway decides whether to fail the upstream call.

Tests

11 Jest tests, all mock-only (no real network). The post() helper from ../utils is mocked via jest.mock; the DELETE path uses a per-test global.fetch mock. Coverage:

  • Missing apiKey returns an error.
  • Redact on plain-string user content (caches session_id).
  • Redact on multimodal content (text parts only, indices preserved).
  • Length-mismatch raises (no silent passthrough).
  • Unexpected response shape raises.
  • Stateless mode caches rehydration_key and sends session: "stateless".
  • Rehydrate end-to-end with cache hit + stateful DELETE.
  • Rehydrate no-op without cache.
  • DELETE failure is swallowed.
  • Stateless skey_… sessions skip DELETE.
  • Rehydrate missing apiKey returns an error.
$ npm run test:plugins -- plugins/peyeeye
PASS plugins/peyeeye/peyeeye.test.ts
  peyeeye redact handler
    ✓ returns an error if the api key is missing
    ✓ redacts a plain-string user message and caches the session id
    ✓ redacts multimodal text parts and writes them back at the right indices
    ✓ raises (no silent passthrough) when redacted count != input count
    ✓ raises when /v1/redact returns an unexpected response shape
    ✓ caches the rehydration_key in stateless mode
  peyeeye rehydrate handler
    ✓ rehydrates when a cached session exists and DELETEs the session
    ✓ no-ops cleanly when no cached session exists
    ✓ swallows DELETE failures without surfacing an error
    ✓ does not call DELETE for stateless (skey_) sessions
    ✓ returns an error if the api key is missing

Test Suites: 1 passed, 1 total
Tests:       11 passed, 11 total

Test plan

  • npm run test:plugins -- plugins/peyeeye — 11 / 11 passing
  • Confirmed full npm run test:plugins baseline is unchanged (other failing suites are pre-existing real-network tests against external vendor APIs)
  • npx tsc --noEmit shows no new TS errors in plugins/peyeeye/

Adds a new guardrail plugin that wraps the peyeeye PII API
(https://api.peyeeye.ai). Two functions:

- peyeeye/redact (beforeRequestHook): batches every text-bearing
  chunk of the request through POST /v1/redact, writes the
  placeholder-token text back via setCurrentContentPart, and caches
  the returned session id (or sealed skey_ blob in stateless mode)
  on the request id.
- peyeeye/rehydrate (afterRequestHook): reads the cached session,
  POSTs the model output through /v1/rehydrate, swaps the original
  PII back in, and best-effort DELETEs the session for stateful mode.

Behavioral invariants the implementation preserves (matches the
peyeeye litellm guardrail, BerriAI/litellm#26546):

- Length-guard: if /v1/redact returns a different number of texts
  than were sent, raise; never forward partially-redacted data.
- Unexpected response shape raises rather than passing through.
- Multimodal content list parts are handled (text parts redacted,
  non-text parts left untouched at their original indices).
- Stateless mode (session: "stateless") returns rehydration_key
  (skey_...) and skips the DELETE step.

11 jest tests, all mock-only (mock post from ../utils and a local
fetch mock for DELETE).
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.

[Feature] peyeeye PII redaction & rehydration plugin

1 participant