Skip to content

feat: add requestTransform for deterministic matching and recording#79

Merged
jpr5 merged 1 commit intomainfrom
feat/request-transform
Apr 6, 2026
Merged

feat: add requestTransform for deterministic matching and recording#79
jpr5 merged 1 commit intomainfrom
feat/request-transform

Conversation

@jpr5
Copy link
Copy Markdown
Contributor

@jpr5 jpr5 commented Apr 6, 2026

Summary

From #63. Full credit to @iskhakovt, this is just me taking on the rebase/re-do work from the large 1.7.0 PR that just merged.

Adds requestTransform to MockServerOptions — a function that normalizes requests before both matching and recording, solving the problem of dynamic data (timestamps, UUIDs, session IDs) causing fixture mismatches on replay.

const mock = new LLMock({
  requestTransform: (req) => ({
    ...req,
    messages: req.messages.map(m => ({
      ...m,
      content: typeof m.content === "string"
        ? m.content.replace(/\d{4}-\d{2}-\d{2}T[\d:.+Z]+/g, "")
        : m.content,
    })),
  }),
});

Recording: saves the transformed match key — no timestamps in fixture.
Matching: transforms incoming request before comparison — same clean key.

When requestTransform is set, string matching switches from includes (substring) to === (exact equality). Without a transform, existing includes behavior is preserved (backward compatible).

Changes

  • types.ts: Add requestTransform to MockServerOptions and HandlerDefaults
  • router.ts: Optional 4th param on matchFixture, exact match when transform set
  • server.ts: Thread transform into defaults
  • recorder.ts: Apply transform before match key extraction
  • All 15 provider handlers: Pass defaults.requestTransform to matchFixture
  • docs/record-replay.html: Document the feature with use case and example
  • 16 new tests covering basic transform, exact vs includes, backward compat, embeddings, predicates, streaming, tool calls

Attribution

Based on the design by @iskhakovt in #63, rebuilt cleanly on the 1.7.0 codebase. The original PR had 100 commits and was conflicting after the v1.7.0 rebrand — this is a fresh implementation of the same concept.

Test plan

  • npx vitest run — 2064 tests pass (58 files)
  • npx tsc --noEmit — clean
  • Backward compatible — no transform = existing includes behavior unchanged

Normalizes requests before matching and recording to handle dynamic
data (timestamps, UUIDs, session IDs) in prompts. When set, string
matching switches from includes to exact equality to prevent false
positives from shortened keys.

Applied across all 15 provider handlers + recorder. 16 new tests.

Based on the design by @iskhakovt in #63, rebuilt on the 1.7.0 codebase.
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 6, 2026

Open in StackBlitz

npm i https://pkg.pr.new/CopilotKit/aimock/@copilotkit/aimock@79

commit: aafe82a

@jpr5
Copy link
Copy Markdown
Contributor Author

jpr5 commented Apr 6, 2026

Full credit to @iskhakovt, this is just his work rebuilt on top of the new release of aimock.

@jpr5 jpr5 merged commit a989617 into main Apr 6, 2026
18 checks passed
@jpr5 jpr5 deleted the feat/request-transform branch April 6, 2026 15:30
jpr5 added a commit that referenced this pull request Apr 6, 2026
## v1.8.0

### Features
- `requestTransform` — deterministic matching and recording (#79, based
on @iskhakovt's #63)
- Reasoning/thinking for OpenAI Chat Completions (#62 by @erezcor)
- Reasoning for Gemini, Bedrock, Bedrock Converse, Ollama (#81)
- Web search events for OpenAI Responses API (#62)

### Fixes
- Migration page health checks (#80)
- Stream collapse reasoning event handling
- GitHub URLs, OG image, drift docs reframe

### Versions bumped
- `package.json` → 1.8.0
- `plugin.json` → 1.8.0
- `marketplace.json` → ^1.8.0
- `Chart.yaml` appVersion → 1.8.0
- `aimock-pytest/_version.py` AIMOCK_VERSION → 1.8.0
- `CHANGELOG.md` — full v1.8.0 + retroactive v1.7.0 entries

2,090 tests across 59 files. Build clean.
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