refactor: split anthropic-transforms.js into focused sub-modules#3478
Conversation
Move the four independent transform concerns out of the monolithic
anthropic-transforms.js into dedicated files under transforms/:
- transforms/ansi-strip.js — stripAnsi, applyAnsiStrip (~70 lines)
- transforms/cache-control.js — withCacheControl, injectCacheBreakpoints,
upgradeEphemeralTtl, MAX_CACHE_BREAKPOINTS,
EXTENDED_CACHE_BETA (~195 lines)
- transforms/tool-drop.js — buildToolScrubPattern, applyToolDrop (~73 lines)
anthropic-transforms.js is now a thin composition layer (~183 lines) that
imports from the sub-modules and retains loadCustomTransform +
makeAnthropicTransform. All existing exports are preserved for backward
compatibility with providers/anthropic.js and the test suite.
✅ Coverage Check PassedOverall Coverage
📁 Per-file Coverage Changes (2 files)
✨ New Files (4 files)
Coverage comparison generated by |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Pull request overview
This PR refactors the Anthropic request-body transform layer in containers/api-proxy by extracting previously bundled transform concerns (ANSI stripping, cache-control breakpoint/TTL logic, and tool dropping) into focused sub-modules, while keeping anthropic-transforms.js as a composition/re-export layer to preserve the existing public API.
Changes:
- Added dedicated transform modules under
containers/api-proxy/transforms/for ANSI stripping, cache-control logic, and tool dropping. - Updated
containers/api-proxy/anthropic-transforms.jsto import from the new modules and re-export the same symbols as before.
Show a summary per file
| File | Description |
|---|---|
| containers/api-proxy/transforms/tool-drop.js | New module for tool removal and system-prompt scrubbing utilities. |
| containers/api-proxy/transforms/cache-control.js | New module containing cache breakpoint injection + ephemeral TTL upgrade logic and constants. |
| containers/api-proxy/transforms/ansi-strip.js | New generic ANSI SGR stripping helper + body transform for tool_result blocks. |
| containers/api-proxy/anthropic-transforms.js | Refactored into a composition/re-export layer that delegates to the new transform modules. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 4/4 changed files
- Comments generated: 5
| const { stripAnsi, applyAnsiStrip } = require('./transforms/ansi-strip'); | ||
| const { | ||
| withCacheControl, | ||
| injectCacheBreakpoints, | ||
| upgradeEphemeralTtl, | ||
| MAX_CACHE_BREAKPOINTS, | ||
| EXTENDED_CACHE_BETA, | ||
| } = require('./transforms/cache-control'); | ||
| const { buildToolScrubPattern, applyToolDrop } = require('./transforms/tool-drop'); | ||
|
|
| // Slot 3: last block of messages[0] | ||
| const msgs = result.messages; | ||
| if (slotsUsed < MAX_CACHE_BREAKPOINTS && | ||
| Array.isArray(msgs) && msgs.length > 0 && | ||
| Array.isArray(msgs[0].content) && msgs[0].content.length > 0) { | ||
| const content = [...msgs[0].content]; | ||
| content[content.length - 1] = withCacheControl(content[content.length - 1], { type: 'ephemeral', ttl: '1h' }); | ||
| const messages = [...msgs]; | ||
| messages[0] = { ...msgs[0], content }; | ||
| result.messages = messages; | ||
| slotsUsed++; |
| if (Array.isArray(body.messages)) { | ||
| outer: for (let i = body.messages.length - 1; i >= 0; i--) { | ||
| const msg = body.messages[i]; | ||
| if (!Array.isArray(msg.content)) continue; | ||
| for (let j = msg.content.length - 1; j >= 0; j--) { | ||
| const b = msg.content[j]; | ||
| if (b && b.cache_control && b.cache_control.type === 'ephemeral') { | ||
| tailMsgIdx = i; |
| const messages = body.messages.map(msg => { | ||
| if (!Array.isArray(msg.content)) return msg; | ||
|
|
||
| const content = msg.content.map(block => { | ||
| if (block.type !== 'tool_result') return block; | ||
|
|
| // Remove matching entries from the tools array | ||
| if (Array.isArray(result.tools)) { | ||
| const filtered = result.tools.filter(tool => !dropSet.has(tool.name)); | ||
| if (filtered.length < result.tools.length) { | ||
| if (filtered.length === 0) { | ||
| result = { ...result }; | ||
| delete result.tools; | ||
| } else { | ||
| result.tools = filtered; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Scrub tool-name references from system-prompt text blocks. | ||
| // We remove bare occurrences; surrounding punctuation/whitespace is left intact | ||
| // to avoid corrupting sentence structure. | ||
| if (Array.isArray(result.system)) { | ||
| const pattern = scrubPattern || buildToolScrubPattern([...dropSet]); | ||
| result.system = result.system.map(block => { | ||
| if (block.type !== 'text' || typeof block.text !== 'string') return block; | ||
| const scrubbed = block.text.replace(pattern, ''); | ||
| return scrubbed === block.text ? block : { ...block, text: scrubbed }; | ||
| }); |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
…ort, function or class' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
|
@copilot address review feedback |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Addressed in
|
|
✅ Smoke Test Results
Result: PASS
|
Smoke Test: Copilot BYOK (Offline) Mode
Running in BYOK offline mode ( Author: Overall: PARTIAL — BYOK inference path ✅, pre-step outputs not injected into prompt.
|
🔍 Smoke Test Results
Overall: PARTIAL — MCP confirmed working; pre-step outputs were not expanded. cc
|
Smoke TestMerged PRs: fix: flatten OTEL cache attribute names for Sentry compatibility; fix: align OTEL attributes with gen_ai semconv spec Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "registry.npmjs.org"See Network Configuration for more information.
|
🔬 Smoke Test: API Proxy OpenTelemetry Tracing
Overall: ✅ All scenarios pass or are expected-pending during development.
|
Smoke Test Results: Gemini
Overall Status: FAIL Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "localhost"See Network Configuration for more information.
|
Chroot Version Comparison Results
Overall: ❌ Not all tests passed — Python and Node.js versions differ between host and chroot environments.
|
🏗️ Build Test Suite Results
Overall: 8/8 ecosystems passed — ✅ PASS
|
Smoke Test Results — FAIL
Overall: FAIL —
|
containers/api-proxy/anthropic-transforms.js(474 lines) bundled four unrelated concerns — ANSI stripping, cache breakpoint injection, ephemeral TTL upgrading, and tool dropping — making each harder to review and reuse in isolation.Split
transforms/ansi-strip.jsstripAnsi,applyAnsiStriptransforms/cache-control.jswithCacheControl,injectCacheBreakpoints,upgradeEphemeralTtl,MAX_CACHE_BREAKPOINTS,EXTENDED_CACHE_BETAtransforms/tool-drop.jsbuildToolScrubPattern,applyToolDropanthropic-transforms.jsis now a ~183-line composition-only layer:Compatibility
All existing exports from
anthropic-transforms.jsare preserved via re-exports — no changes required toproviders/anthropic.jsor the test suite.ansi-strip.jsis now also available for reuse by other provider adapters.