| |
| |
| import { RAW_CAP, MIN_COMPRESS_SIZE } from "./constants.js"; |
| import { autoDetectFilter } from "./autodetect.js"; |
| import { safeApply } from "./applyFilter.js"; |
|
|
| |
| export function compressMessages(body, enabled) { |
| if (!enabled) return null; |
| if (!body) return null; |
|
|
| |
| if (body.conversationState) { |
| return compressKiroFormat(body, enabled); |
| } |
|
|
| |
| const items = Array.isArray(body.messages) ? body.messages |
| : Array.isArray(body.input) ? body.input |
| : null; |
| if (!items) return null; |
|
|
| const stats = { bytesBefore: 0, bytesAfter: 0, hits: [] }; |
| try { |
| for (let i = 0; i < items.length; i++) { |
| const msg = items[i]; |
| if (!msg) continue; |
|
|
| |
| if (msg.type === "function_call_output") { |
| if (typeof msg.output === "string") { |
| msg.output = compressText(msg.output, stats, "openai-responses-string"); |
| } else if (Array.isArray(msg.output)) { |
| for (let k = 0; k < msg.output.length; k++) { |
| const part = msg.output[k]; |
| if (part && part.type === "input_text" && typeof part.text === "string") { |
| part.text = compressText(part.text, stats, "openai-responses-array"); |
| } |
| } |
| } |
| continue; |
| } |
|
|
| |
| if (msg.role === "tool" && typeof msg.content === "string") { |
| msg.content = compressText(msg.content, stats, "openai-tool"); |
| continue; |
| } |
|
|
| if (!Array.isArray(msg.content)) continue; |
|
|
| |
| if (msg.role === "tool") { |
| for (let k = 0; k < msg.content.length; k++) { |
| const part = msg.content[k]; |
| if (part && part.type === "text" && typeof part.text === "string") { |
| part.text = compressText(part.text, stats, "openai-tool-array"); |
| } |
| } |
| continue; |
| } |
|
|
| |
| for (let j = 0; j < msg.content.length; j++) { |
| const block = msg.content[j]; |
| if (!block || block.type !== "tool_result") continue; |
| if (block.is_error === true) continue; |
|
|
| if (typeof block.content === "string") { |
| |
| block.content = compressText(block.content, stats, "claude-string"); |
| } else if (Array.isArray(block.content)) { |
| |
| for (let k = 0; k < block.content.length; k++) { |
| const part = block.content[k]; |
| if (part && part.type === "text" && typeof part.text === "string") { |
| part.text = compressText(part.text, stats, "claude-array"); |
| } |
| } |
| } |
| } |
| } |
| } catch (e) { |
| console.warn("[RTK] compressMessages error:", e.message); |
| return null; |
| } |
| return stats; |
| } |
|
|
| |
| function compressKiroFormat(body, enabled) { |
| const stats = { bytesBefore: 0, bytesAfter: 0, hits: [] }; |
| try { |
| const state = body.conversationState; |
| const allMessages = [...(Array.isArray(state?.history) ? state.history : [])]; |
| if (state?.currentMessage) allMessages.push(state.currentMessage); |
|
|
| for (const msg of allMessages) { |
| const toolResults = msg?.userInputMessage?.userInputMessageContext?.toolResults; |
| if (!Array.isArray(toolResults)) continue; |
|
|
| for (const tr of toolResults) { |
| if (tr.status === "error") continue; |
| if (!Array.isArray(tr.content)) continue; |
|
|
| for (const part of tr.content) { |
| if (part && typeof part.text === "string") { |
| part.text = compressText(part.text, stats, "kiro-tool-result"); |
| } |
| } |
| } |
| } |
| } catch (e) { |
| console.warn("[RTK] compressKiroFormat error:", e.message); |
| return null; |
| } |
| return stats; |
| } |
|
|
| function compressText(text, stats, shape) { |
| const bytesIn = text.length; |
| stats.bytesBefore += bytesIn; |
|
|
| if (bytesIn < MIN_COMPRESS_SIZE || bytesIn > RAW_CAP) { |
| stats.bytesAfter += bytesIn; |
| return text; |
| } |
|
|
| const fn = autoDetectFilter(text); |
| if (!fn) { |
| stats.bytesAfter += bytesIn; |
| return text; |
| } |
|
|
| const out = safeApply(fn, text); |
|
|
| |
| if (!out || out.length === 0 || out.length >= bytesIn) { |
| stats.bytesAfter += bytesIn; |
| return text; |
| } |
|
|
| stats.bytesAfter += out.length; |
| stats.hits.push({ shape, filter: fn.filterName || fn.name, saved: bytesIn - out.length }); |
| return out; |
| } |
|
|
| |
| export function formatRtkLog(stats) { |
| if (!stats || !stats.hits || stats.hits.length === 0) return null; |
| const saved = stats.bytesBefore - stats.bytesAfter; |
| const pct = stats.bytesBefore > 0 ? ((saved / stats.bytesBefore) * 100).toFixed(1) : "0"; |
| const filters = Array.from(new Set(stats.hits.map(h => h.filter))).join(","); |
| return `[RTK] saved ${saved}B / ${stats.bytesBefore}B (${pct}%) via [${filters}] hits=${stats.hits.length}`; |
| } |
|
|