mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-18 13:14:56 +03:00
feat(compression): target-wire OmniGlyph stage and transport fidelity gate
Roda o OmniGlyph depois da tradução para o wire real do provedor, em vez do corpo de origem. Um cliente OpenAI roteado para Claude deixava de comprimir com skip:source_format_not_claude porque o corpo ainda estava em formato OpenAI quando a engine era avaliada. - dispatch nativo por wire: Anthropic Messages, OpenAI Chat Completions e OpenAI Responses (input[] preservado, sem achatar para messages[]); - estágio target-wire pós-translateRequest, com guarda contra dupla compressão no caminho Claude→OpenAI; - preserveSystemPrompt do OmniRoute mapeado para compressSystem: false; - imageTransportPolicy: fidelidade de bytes/dimensões separada de supportsVision; só Anthropic/Claude tem recibo byte-preserving, o resto é fail-closed; - contagem de tokens de data URL PNG no wire OpenAI (marcador ;base64,); - README e i18n en/pt-BR com claims escopados ao caminho medido.
This commit is contained in:
@@ -853,7 +853,7 @@ Engines run in pipeline order; each is independently toggleable and configurable
|
||||
<tr><td align="center" nowrap>9</td><td align="left" nowrap><b>Aggressive</b></td><td align="left">Summarization + progressive aging of old turns</td></tr>
|
||||
<tr><td align="center" nowrap>10</td><td align="left" nowrap><b>LLMLingua-2</b></td><td align="left">ML semantic pruning via MobileBERT ONNX — code-safe, async</td></tr>
|
||||
<tr><td align="center" nowrap>11</td><td align="left" nowrap><b>Ultra</b></td><td align="left">Heuristic token pruning with an optional small-model (SLM) tier</td></tr>
|
||||
<tr><td align="center" nowrap>12</td><td align="left" nowrap><b>OmniGlyph</b></td><td align="left">Experimental context-as-image encoding routed to Claude Fable 5 (most aggressive; opt-in)</td></tr>
|
||||
<tr><td align="center" nowrap>12</td><td align="left" nowrap><b>OmniGlyph</b></td><td align="left">Experimental context-as-image encoding for measured Claude Fable 5 on the direct Anthropic wire; GPT 5.6 transformers remain fail-closed pending provider receipts (most aggressive; opt-in)</td></tr>
|
||||
</table>
|
||||
|
||||
Code blocks, URLs and structured data are **always preserved** byte-perfect. **One-click presets** combine the engines:
|
||||
|
||||
@@ -88,6 +88,7 @@ import { checkResourcePressureGuard } from "../utils/resourcePressure.ts";
|
||||
import { normalizeHeaders } from "../utils/headers.ts";
|
||||
import { resolveChatCoreRequestFormat } from "./chatCore/requestFormat.ts";
|
||||
import { resolveChatCoreTargetFormat } from "./chatCore/targetFormat.ts";
|
||||
import { resolveOmniGlyphTransport } from "../services/compression/imageTransportPolicy.ts";
|
||||
import { stripStore, usesClaudeBridge } from "./chatCore/agentRouterProtocol.ts";
|
||||
import { defaultClaudeToolType } from "./chatCore/claudeToolDefaults.ts";
|
||||
import { injectSystemPrompt, injectCustomSystemPrompt } from "../services/systemPrompt.ts";
|
||||
@@ -1548,16 +1549,9 @@ export async function handleChatCore({
|
||||
// models, which is intentionally NOT `false` so the gate still preserves images.
|
||||
supportsVision: getResolvedModelCapabilities({ provider, model: effectiveModel })
|
||||
.supportsVision,
|
||||
// Rotas diretas oficiais ('anthropic' API key e 'claude' OAuth) vs agregadores:
|
||||
// o engine omniglyph exige 'direct' — agregadores redimensionam imagens
|
||||
// (medido 2026-07-06). OAuth 'claude' é rota direta oficial (#7863).
|
||||
providerTransport:
|
||||
provider === "anthropic" ||
|
||||
provider === "claude" ||
|
||||
provider === "openai" ||
|
||||
provider === "xai"
|
||||
? ("direct" as const)
|
||||
: ("aggregator" as const),
|
||||
// OmniGlyph uses a measured provider/image-fidelity allowlist. Direct HTTP
|
||||
// alone is not proof that a route preserves PNG bytes and dimensions.
|
||||
...resolveOmniGlyphTransport(provider),
|
||||
sourceFormat,
|
||||
targetFormat,
|
||||
compressionStage: "pre-translation" as const,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* - modelo fora da allowlist medida → skip:model_not_approved
|
||||
* - providerTransport !== 'direct' → skip:transport_not_direct
|
||||
* (agregadores redimensionam imagens e destroem a legibilidade — medido)
|
||||
* - imageTransportFidelity !== 'byte-preserving' → skip:transport_fidelity_unknown/resizes
|
||||
* - wire não é Claude/OpenAI suportado → skip:target_format_not_supported
|
||||
* - gate de rentabilidade interno do omniglyph decide o resto (patches 28px
|
||||
* exatos; texto esparso/pequeno passa direto) → skip:not_profitable
|
||||
@@ -25,6 +26,7 @@ import {
|
||||
transformAnthropicMessages,
|
||||
transformOpenAIChatCompletions,
|
||||
transformOpenAIResponses,
|
||||
transformRequest,
|
||||
} from "omniglyph";
|
||||
import { isModelImageable } from "omniglyph/applicability";
|
||||
|
||||
@@ -80,9 +82,36 @@ async function applyOmniglyph(
|
||||
const model = options?.model ?? (body as { model?: string }).model ?? "";
|
||||
if (options?.supportsVision !== true) return skip(body, "no_vision");
|
||||
if (options?.providerTransport !== "direct") return skip(body, "transport_not_direct");
|
||||
// Keep the old direct-call contract usable for standalone callers, but let
|
||||
// production callers override it explicitly. The chat pipeline supplies
|
||||
// `unknown` for every provider without a byte-preservation receipt.
|
||||
if (
|
||||
options?.imageTransportFidelity !== undefined &&
|
||||
options.imageTransportFidelity !== "byte-preserving"
|
||||
) {
|
||||
return skip(
|
||||
body,
|
||||
options.imageTransportFidelity === "resizes"
|
||||
? "transport_resizes_images"
|
||||
: "transport_fidelity_unknown"
|
||||
);
|
||||
}
|
||||
|
||||
const stage = options?.compressionStage ?? "pre-translation";
|
||||
const wireFormat = resolveWireFormat(body, options);
|
||||
// A source/target format mismatch means the body is still on the wrong wire,
|
||||
// even when the source itself is native Claude. Defer the engine until the
|
||||
// translated provider body so Claude→OpenAI cannot be imaged once before
|
||||
// translation and then considered again on the target wire.
|
||||
const sourceWireFormat = options?.sourceFormat ?? wireFormat;
|
||||
if (
|
||||
stage === "pre-translation" &&
|
||||
options?.targetFormat &&
|
||||
sourceWireFormat &&
|
||||
options.targetFormat !== sourceWireFormat
|
||||
) {
|
||||
return skip(body, "requires_post_translation");
|
||||
}
|
||||
// The pre-translation lane is retained for the existing native Claude
|
||||
// passthrough. OpenAI requests must wait until translation has produced the
|
||||
// exact provider wire, otherwise Responses input[] would be flattened by the
|
||||
@@ -112,20 +141,32 @@ async function applyOmniglyph(
|
||||
// OmniGlyph 1.3.x deliberately keeps unverified families (currently Grok)
|
||||
// text-only until the operator acknowledges them via its own env gate.
|
||||
if (!isModelImageable(model)) return skip(body, "model_not_imageable");
|
||||
|
||||
const started = Date.now();
|
||||
let outBody: Record<string, unknown>;
|
||||
try {
|
||||
const encoded = new TextEncoder().encode(JSON.stringify(body));
|
||||
// The upstream OpenAI transformer resolves its billing/render profile from
|
||||
// body.model. Keep the provider body byte-compatible on output, but use the
|
||||
// already-resolved engine model for that internal gate when a translator
|
||||
// omitted the model or left an alias in place.
|
||||
const transformBody =
|
||||
wireFormat !== "claude" && model && body.model !== model ? { ...body, model } : body;
|
||||
const encoded = new TextEncoder().encode(JSON.stringify(transformBody));
|
||||
const preserveSystemPrompt =
|
||||
(typeof options?.stepConfig?.preserveSystemPrompt === "boolean"
|
||||
? options.stepConfig.preserveSystemPrompt
|
||||
: options?.config?.preserveSystemPrompt) === true;
|
||||
const result =
|
||||
wireFormat === "claude"
|
||||
? await transformAnthropicMessages({ body: encoded, model })
|
||||
: wireFormat === "openai"
|
||||
? await transformOpenAIChatCompletions(encoded)
|
||||
: await transformOpenAIResponses(encoded);
|
||||
const applied = wireFormat === "claude" ? result.applied : result.info.compressed;
|
||||
wireFormat === "claude" && preserveSystemPrompt
|
||||
? await transformRequest(encoded, { compressSystem: false })
|
||||
: wireFormat === "claude"
|
||||
? await transformAnthropicMessages({ body: encoded, model })
|
||||
: wireFormat === "openai"
|
||||
? await transformOpenAIChatCompletions(encoded)
|
||||
: await transformOpenAIResponses(encoded);
|
||||
const applied = "applied" in result ? result.applied : result.info.compressed;
|
||||
if (!applied) return skip(body, result.info?.reason ?? "not_profitable");
|
||||
outBody = JSON.parse(new TextDecoder().decode(result.body)) as Record<string, unknown>;
|
||||
if (transformBody !== body && body.model !== undefined) outBody.model = body.model;
|
||||
} catch {
|
||||
// Fail-open: qualquer erro no encode/transform/decode (ex.: corpo não serializável,
|
||||
// render PNG estourando, JSON decodificado malformado) vira skip, nunca propaga.
|
||||
@@ -150,7 +191,7 @@ export const omniglyphEngine: CompressionEngine = {
|
||||
id: "omniglyph",
|
||||
name: "OmniGlyph",
|
||||
description:
|
||||
"Contexto-como-imagem (Anthropic Fable 5, rota direta): system prompt, tool docs e histórico viram páginas PNG densas — ~10× menos tokens no bloco convertido.",
|
||||
"Contexto-como-imagem para Claude Fable 5 na rota direta medida; wires GPT nativos ficam disponíveis apenas após recibo de fidelidade do provedor.",
|
||||
icon: "image",
|
||||
targets: ["messages", "tool_results"],
|
||||
stackable: true,
|
||||
@@ -160,7 +201,7 @@ export const omniglyphEngine: CompressionEngine = {
|
||||
id: "omniglyph",
|
||||
name: "OmniGlyph",
|
||||
description:
|
||||
"Contexto-como-imagem para Claude Fable 5 e GPT 5.6 via wires nativos Anthropic/OpenAI em rota direta.",
|
||||
"Contexto-como-imagem para Claude Fable 5 na rota direta medida; transformadores GPT nativos permanecem fail-closed até validação do provedor.",
|
||||
inputScope: "mixed",
|
||||
targetLatencyMs: 250, // render+encode PNG de páginas grandes
|
||||
supportsPreview: true,
|
||||
|
||||
@@ -7,6 +7,9 @@ export type CompressionWireFormat = "claude" | "openai" | "openai-responses" | s
|
||||
|
||||
export type CompressionStage = "pre-translation" | "post-translation";
|
||||
|
||||
/** Whether an upstream route preserves OmniGlyph PNG bytes and dimensions. */
|
||||
export type ImageTransportFidelity = "byte-preserving" | "resizes" | "unknown";
|
||||
|
||||
export interface EngineConfigField {
|
||||
key: string;
|
||||
type: "boolean" | "number" | "string" | "select" | "multiselect";
|
||||
@@ -42,8 +45,11 @@ export interface CompressionEngineApplyOptions {
|
||||
/** Como o request chega ao provider: rota direta oficial ('direct') vs
|
||||
* agregador que pode reprocessar imagens ('aggregator'). O engine omniglyph
|
||||
* exige 'direct' — medição 2026-07-06: agregadores redimensionam as páginas
|
||||
* e destroem a legibilidade. undefined = desconhecido = skip (fail-closed). */
|
||||
* e destroem a legibilidade. A política de produção também informa
|
||||
* imageTransportFidelity; chamadas legadas sem esse campo mantêm o gate direct. */
|
||||
providerTransport?: "direct" | "aggregator";
|
||||
/** Independent image-fidelity gate; direct HTTP does not imply byte preservation. */
|
||||
imageTransportFidelity?: ImageTransportFidelity;
|
||||
/** Protocol shape before the current compression stage. */
|
||||
sourceFormat?: CompressionWireFormat;
|
||||
/** Protocol shape expected by the upstream provider. */
|
||||
|
||||
33
open-sse/services/compression/imageTransportPolicy.ts
Normal file
33
open-sse/services/compression/imageTransportPolicy.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Provider-level image transport policy for loss-sensitive compression engines.
|
||||
*
|
||||
* `supportsVision` only says that a model can read images. OmniGlyph also needs
|
||||
* the PNG bytes and dimensions to survive the provider route unchanged. The
|
||||
* allowlist below contains only paths with an existing OmniRoute receipt;
|
||||
* everything else is deliberately classified as unknown and skipped.
|
||||
*/
|
||||
|
||||
import type { ImageTransportFidelity } from "./engines/types.ts";
|
||||
|
||||
export type OmniGlyphTransportPolicy = {
|
||||
providerTransport: "direct" | "aggregator";
|
||||
imageTransportFidelity: ImageTransportFidelity;
|
||||
};
|
||||
|
||||
const BYTE_PRESERVING_PROVIDERS = new Set(["anthropic", "claude"]);
|
||||
|
||||
export function resolveOmniGlyphTransport(
|
||||
provider: string | null | undefined
|
||||
): OmniGlyphTransportPolicy {
|
||||
const normalized = typeof provider === "string" ? provider.trim().toLowerCase() : "";
|
||||
if (BYTE_PRESERVING_PROVIDERS.has(normalized)) {
|
||||
return {
|
||||
providerTransport: "direct",
|
||||
imageTransportFidelity: "byte-preserving",
|
||||
};
|
||||
}
|
||||
return {
|
||||
providerTransport: "aggregator",
|
||||
imageTransportFidelity: "unknown",
|
||||
};
|
||||
}
|
||||
@@ -77,7 +77,7 @@ function isOpenAIResponsesPngImagePart(value: unknown): value is OpenAIResponses
|
||||
}
|
||||
|
||||
function pngDimensionsFromDataUrl(value: string): { width: number; height: number } | null {
|
||||
const marker = ",base64,";
|
||||
const marker = ";base64,";
|
||||
const markerIndex = value.indexOf(marker);
|
||||
if (markerIndex < 0) return null;
|
||||
return decodePngDimensions(value.slice(markerIndex + marker.length));
|
||||
|
||||
@@ -11,6 +11,7 @@ import type {
|
||||
CompressionEngineApplyOptions,
|
||||
CompressionStage,
|
||||
CompressionWireFormat,
|
||||
ImageTransportFidelity,
|
||||
} from "./engines/types.ts";
|
||||
import { applyLiteCompression } from "./lite.ts";
|
||||
import { cavemanCompress } from "./caveman.ts";
|
||||
@@ -266,6 +267,7 @@ export function applyCompression(
|
||||
options?: {
|
||||
model?: string;
|
||||
supportsVision?: boolean | null;
|
||||
imageTransportFidelity?: ImageTransportFidelity;
|
||||
sourceFormat?: CompressionWireFormat;
|
||||
targetFormat?: CompressionWireFormat;
|
||||
compressionStage?: CompressionStage;
|
||||
@@ -292,6 +294,7 @@ function runCompression(
|
||||
options?: {
|
||||
model?: string;
|
||||
supportsVision?: boolean | null;
|
||||
imageTransportFidelity?: ImageTransportFidelity;
|
||||
sourceFormat?: CompressionWireFormat;
|
||||
targetFormat?: CompressionWireFormat;
|
||||
compressionStage?: CompressionStage;
|
||||
@@ -479,6 +482,7 @@ export async function applyCompressionAsync(
|
||||
supportsVision?: boolean | null;
|
||||
/** Direct-to-provider vs. aggregator transport (gates transport-sensitive engines like omniglyph). */
|
||||
providerTransport?: "direct" | "aggregator";
|
||||
imageTransportFidelity?: ImageTransportFidelity;
|
||||
sourceFormat?: CompressionWireFormat;
|
||||
targetFormat?: CompressionWireFormat;
|
||||
compressionStage?: CompressionStage;
|
||||
@@ -501,6 +505,7 @@ async function runCompressionAsync(
|
||||
supportsVision?: boolean | null;
|
||||
/** Direct-to-provider vs. aggregator transport (gates transport-sensitive engines like omniglyph). */
|
||||
providerTransport?: "direct" | "aggregator";
|
||||
imageTransportFidelity?: ImageTransportFidelity;
|
||||
sourceFormat?: CompressionWireFormat;
|
||||
targetFormat?: CompressionWireFormat;
|
||||
compressionStage?: CompressionStage;
|
||||
@@ -693,6 +698,7 @@ interface StackOptions {
|
||||
supportsVision?: boolean | null;
|
||||
/** Direct-to-provider vs. aggregator transport (gates transport-sensitive engines like omniglyph). */
|
||||
providerTransport?: "direct" | "aggregator";
|
||||
imageTransportFidelity?: ImageTransportFidelity;
|
||||
sourceFormat?: CompressionWireFormat;
|
||||
targetFormat?: CompressionWireFormat;
|
||||
compressionStage?: CompressionStage;
|
||||
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -60,7 +60,7 @@
|
||||
"next-intl": "^4.13.6",
|
||||
"next-themes": "^0.4.6",
|
||||
"node-machine-id": "^1.1.12",
|
||||
"omniglyph": "^1.0.2",
|
||||
"omniglyph": "^1.3.1",
|
||||
"onnxruntime-node": "~1.24.3",
|
||||
"open": "^11.0.0",
|
||||
"ora": "^9.4.1",
|
||||
|
||||
@@ -304,7 +304,7 @@
|
||||
"next-intl": "^4.13.6",
|
||||
"next-themes": "^0.4.6",
|
||||
"node-machine-id": "^1.1.12",
|
||||
"omniglyph": "^1.0.2",
|
||||
"omniglyph": "^1.3.1",
|
||||
"open": "^11.0.0",
|
||||
"ora": "^9.4.1",
|
||||
"parse5": "^8.0.1",
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
import { logger } from "@omniroute/open-sse/utils/logger.ts";
|
||||
import { estimateTokens } from "@omniroute/open-sse/services/contextManager.ts";
|
||||
import { adaptBodyForCompression } from "@omniroute/open-sse/services/compression/bodyAdapter.ts";
|
||||
import { resolveOmniGlyphTransport } from "@omniroute/open-sse/services/compression/imageTransportPolicy.ts";
|
||||
import type {
|
||||
CompressionConfig,
|
||||
CompressionResult,
|
||||
@@ -55,13 +56,16 @@ export async function applyResponsesWsCompression(
|
||||
if (!enabled || !settings) return responseBody;
|
||||
|
||||
const adapter = adaptBodyForCompression(responseBody);
|
||||
if (!adapter.adapted || !Array.isArray(adapter.body.messages) || adapter.body.messages.length === 0) {
|
||||
if (
|
||||
!adapter.adapted ||
|
||||
!Array.isArray(adapter.body.messages) ||
|
||||
adapter.body.messages.length === 0
|
||||
) {
|
||||
return responseBody;
|
||||
}
|
||||
|
||||
const { selectCompressionStrategy, applyCompressionAsync } = await import(
|
||||
"@omniroute/open-sse/services/compression/strategySelector.ts"
|
||||
);
|
||||
const { selectCompressionStrategy, applyCompressionAsync } =
|
||||
await import("@omniroute/open-sse/services/compression/strategySelector.ts");
|
||||
|
||||
const estimatedTokens = estimateTokens(adapter.body.messages);
|
||||
const cachingContext = {
|
||||
@@ -83,8 +87,7 @@ export async function applyResponsesWsCompression(
|
||||
|
||||
const result = await applyCompressionAsync(adapter.body, mode, {
|
||||
model: ctx.model,
|
||||
providerTransport:
|
||||
ctx.provider === "anthropic" || ctx.provider === "claude" ? "direct" : "aggregator",
|
||||
...resolveOmniGlyphTransport(ctx.provider),
|
||||
config: settings as CompressionConfig,
|
||||
cachingContext,
|
||||
});
|
||||
|
||||
@@ -8534,13 +8534,13 @@
|
||||
},
|
||||
"omniglyph": {
|
||||
"preview": "Preview",
|
||||
"description": "Context-as-image compression. Renders the system prompt, tool documentation, and dense history as compact PNG pages that Claude Fable 5 reads instead of text. Image tokens are billed by dimensions rather than characters, so the converted block costs about 10× less. Direct Anthropic route only.",
|
||||
"description": "Context-as-image compression. Renders the system prompt, tool documentation, and dense history as compact PNG pages for the measured Claude Fable 5 path. The published OmniGlyph package also exposes native GPT 5.6 transformers, but OmniRoute keeps those routes fail-closed until provider fidelity and reading receipts exist.",
|
||||
"economicsTitle": "The economics",
|
||||
"economics": {
|
||||
"fewerTokens": "fewer tokens on the converted block",
|
||||
"savings": "end-to-end savings (measured)",
|
||||
"imageTokens": "image tokens for a 1568×728 page (about 28k characters)",
|
||||
"accuracy": "reading accuracy on Fable 5 (n=30)"
|
||||
"accuracy": "scoped Fable 5 reading receipt"
|
||||
},
|
||||
"beforeAfterTitle": "Before → after",
|
||||
"blockSavings": "−{percent}% tokens on this block",
|
||||
@@ -8553,18 +8553,18 @@
|
||||
"gates": {
|
||||
"model": {
|
||||
"label": "Model",
|
||||
"pass": "claude-fable-5",
|
||||
"why": "Only Fable 5 reads dense pages at 100% accuracy (measured, n=30). GPT-5.5 and Gemini 2.5 Flash are blocked."
|
||||
"pass": "claude-fable-5 (measured); gpt-5.6 (HOLD)",
|
||||
"why": "Only the measured Fable 5 path is enabled in OmniRoute. GPT 5.6 is structurally supported by the package but remains blocked until its provider receipt; unverified families such as Grok stay text-only."
|
||||
},
|
||||
"transport": {
|
||||
"label": "Transport",
|
||||
"pass": "direct Anthropic",
|
||||
"why": "Aggregators resample images and destroy legibility — only the direct route is authoritative."
|
||||
"pass": "direct provider",
|
||||
"why": "Aggregators may resample images and destroy legibility — only a direct provider route is authoritative."
|
||||
},
|
||||
"format": {
|
||||
"label": "Format",
|
||||
"pass": "native Claude",
|
||||
"why": "The body must use Claude format and must never put a system role inside messages."
|
||||
"pass": "native Claude/OpenAI",
|
||||
"why": "The body must match the provider wire: Claude Messages, OpenAI Chat Completions, or native OpenAI Responses input[]."
|
||||
},
|
||||
"profitable": {
|
||||
"label": "Profitable",
|
||||
@@ -8573,7 +8573,7 @@
|
||||
}
|
||||
},
|
||||
"enableTitle": "Enable the engine",
|
||||
"enableDescription": "Runs last in the stack (after RTK/Caveman cleans the text, OmniGlyph converts the remainder to images) and also runs standalone through <code>omniglyph</code> mode. This is a preview and remains off by default until end-to-end validation is complete.",
|
||||
"enableDescription": "Runs last in the stack after text engines clean the request, using the native provider wire; OpenAI Chat/Responses requests are compressed after translation so their shape is preserved. It also runs standalone through <code>omniglyph</code> mode. This is a preview and remains off by default until end-to-end validation is complete.",
|
||||
"saved": "Saved.",
|
||||
"saveFailed": "Could not save.",
|
||||
"enableAria": "Enable the OmniGlyph engine",
|
||||
|
||||
@@ -8534,13 +8534,13 @@
|
||||
},
|
||||
"omniglyph": {
|
||||
"preview": "Visualização",
|
||||
"description": "Compressão de contexto como imagem. Renderiza o prompt de sistema, a documentação de ferramentas e o histórico denso como páginas PNG compactas que o Claude Fable 5 lê em vez de texto. Os tokens de imagem são cobrados por dimensões em vez de caracteres, então o bloco convertido custa cerca de 10× menos. Somente na rota direta da Anthropic.",
|
||||
"description": "Compressão de contexto como imagem. Renderiza o prompt de sistema, a documentação de ferramentas e o histórico denso como páginas PNG compactas para a rota medida do Claude Fable 5. O pacote OmniGlyph também expõe transformadores nativos para GPT 5.6, mas o OmniRoute mantém essas rotas fail-closed até existirem recibos de fidelidade do provedor e de leitura.",
|
||||
"economicsTitle": "A economia",
|
||||
"economics": {
|
||||
"fewerTokens": "menos tokens no bloco convertido",
|
||||
"savings": "economia de ponta a ponta (medida)",
|
||||
"imageTokens": "tokens de imagem para uma página de 1568×728 (cerca de 28 mil caracteres)",
|
||||
"accuracy": "precisão de leitura no Fable 5 (n=30)"
|
||||
"accuracy": "recibo de leitura do Fable 5 (escopo medido)"
|
||||
},
|
||||
"beforeAfterTitle": "Antes → depois",
|
||||
"blockSavings": "−{percent}% de tokens neste bloco",
|
||||
@@ -8553,18 +8553,18 @@
|
||||
"gates": {
|
||||
"model": {
|
||||
"label": "Modelo",
|
||||
"pass": "claude-fable-5",
|
||||
"why": "Somente o Fable 5 lê páginas densas com 100% de precisão (medido, n=30). GPT-5.5 e Gemini 2.5 Flash são bloqueados."
|
||||
"pass": "claude-fable-5 (medido); gpt-5.6 (HOLD)",
|
||||
"why": "Somente a rota medida do Fable 5 está habilitada no OmniRoute. O GPT 5.6 tem suporte estrutural no pacote, mas continua bloqueado até seu recibo do provedor; famílias não verificadas, como Grok, permanecem em texto."
|
||||
},
|
||||
"transport": {
|
||||
"label": "Transporte",
|
||||
"pass": "direto Anthropic",
|
||||
"why": "Agregadores reamostram imagens e destroem a legibilidade — apenas a rota direta é confiável."
|
||||
"pass": "provedor direto",
|
||||
"why": "Agregadores podem reamostrar imagens e destruir a legibilidade — apenas uma rota direta do provedor é confiável."
|
||||
},
|
||||
"format": {
|
||||
"label": "Formato",
|
||||
"pass": "Claude nativo",
|
||||
"why": "O corpo deve usar o formato Claude e nunca colocar um papel system dentro de messages."
|
||||
"pass": "Claude/OpenAI nativo",
|
||||
"why": "O corpo precisa corresponder ao wire do provedor: Claude Messages, OpenAI Chat Completions ou input[] nativo do OpenAI Responses."
|
||||
},
|
||||
"profitable": {
|
||||
"label": "Rentável",
|
||||
@@ -8573,7 +8573,7 @@
|
||||
}
|
||||
},
|
||||
"enableTitle": "Ativar a engine",
|
||||
"enableDescription": "Executa por último na pilha (depois que RTK/Caveman limpa o texto, o OmniGlyph converte o restante em imagens) e também roda de forma independente pelo modo <code>omniglyph</code>. Este é um preview e permanece desativado por padrão até que a validação de ponta a ponta seja concluída.",
|
||||
"enableDescription": "Executa por último na pilha, depois que os engines de texto limpam a solicitação, usando o wire nativo do provedor; solicitações OpenAI Chat/Responses são comprimidas depois da tradução para preservar seu formato. Também roda de forma independente pelo modo <code>omniglyph</code>. Este é um preview e permanece desativado por padrão até que a validação de ponta a ponta seja concluída.",
|
||||
"saved": "Salvo.",
|
||||
"saveFailed": "Não foi possível salvar.",
|
||||
"enableAria": "Ativar a engine OmniGlyph",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert";
|
||||
import { estimateCompressionTokens } from "../../../open-sse/services/compression/stats.ts";
|
||||
import { transformAnthropicMessages } from "omniglyph";
|
||||
import { transformAnthropicMessages, transformOpenAIChatCompletions } from "omniglyph";
|
||||
|
||||
const CHARS_PER_TOKEN = 4;
|
||||
|
||||
@@ -67,3 +67,33 @@ test("regressão: corpo sem imagem estima o MESMO valor de antes (char-count pur
|
||||
const expected = Math.ceil(JSON.stringify(plainBody).length / CHARS_PER_TOKEN);
|
||||
assert.equal(estimateCompressionTokens(plainBody), expected);
|
||||
});
|
||||
|
||||
test("estimateCompressionTokens contabiliza image_url PNG no wire OpenAI", async () => {
|
||||
const originalBody = {
|
||||
model: "gpt-5.6",
|
||||
messages: [
|
||||
{ role: "system", content: DENSE },
|
||||
{ role: "user", content: "oi" },
|
||||
],
|
||||
};
|
||||
const encoded = new TextEncoder().encode(JSON.stringify(originalBody));
|
||||
const result = await transformOpenAIChatCompletions(encoded);
|
||||
assert.equal(result.info.compressed, true);
|
||||
const compressedBody = JSON.parse(new TextDecoder().decode(result.body)) as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
assert.ok(JSON.stringify(compressedBody).includes('"type":"image_url"'));
|
||||
|
||||
const originalEstimate = estimateCompressionTokens(originalBody);
|
||||
const compressedEstimate = estimateCompressionTokens(compressedBody);
|
||||
const naiveEstimate = Math.ceil(JSON.stringify(compressedBody).length / CHARS_PER_TOKEN);
|
||||
assert.ok(
|
||||
compressedEstimate < naiveEstimate,
|
||||
`expected image-aware (${compressedEstimate}) < base64 char estimate (${naiveEstimate})`
|
||||
);
|
||||
assert.ok(
|
||||
compressedEstimate < originalEstimate,
|
||||
`expected OpenAI image compression (${compressedEstimate}) < original (${originalEstimate})`
|
||||
);
|
||||
});
|
||||
|
||||
@@ -18,11 +18,17 @@ function claudeBody(): Record<string, unknown> {
|
||||
messages: [{ role: "user", content: [{ type: "text", text: "oi" }] }],
|
||||
};
|
||||
}
|
||||
const OK = { model: "claude-fable-5", supportsVision: true, providerTransport: "direct" as const };
|
||||
const OK = {
|
||||
model: "claude-fable-5",
|
||||
supportsVision: true,
|
||||
providerTransport: "direct" as const,
|
||||
imageTransportFidelity: "byte-preserving" as const,
|
||||
};
|
||||
const GPT_OK = {
|
||||
model: "gpt-5.6",
|
||||
supportsVision: true,
|
||||
providerTransport: "direct" as const,
|
||||
imageTransportFidelity: "byte-preserving" as const,
|
||||
sourceFormat: "openai" as const,
|
||||
targetFormat: "openai" as const,
|
||||
compressionStage: "post-translation" as const,
|
||||
@@ -82,6 +88,15 @@ test("skip fail-closed: sem supportsVision / transporte agregador / undefined",
|
||||
}
|
||||
});
|
||||
|
||||
test("skip fail-closed: rota direta sem recibo de fidelidade de imagem", async () => {
|
||||
const r = await omniglyphEngine.applyAsync!(claudeBody(), {
|
||||
...OK,
|
||||
imageTransportFidelity: "unknown",
|
||||
});
|
||||
assert.equal(r.compressed, false);
|
||||
assert.ok(r.stats?.techniquesUsed.includes("skip:transport_fidelity_unknown"));
|
||||
});
|
||||
|
||||
test("skip: modelo fora da allowlist medida", async () => {
|
||||
const body = { ...claudeBody(), model: "gpt-5.5" };
|
||||
const r = await omniglyphEngine.applyAsync!(body, { ...OK, model: "gpt-5.5" });
|
||||
@@ -110,6 +125,31 @@ test("cache_control do cliente sobrevive byte a byte", async () => {
|
||||
assert.ok(JSON.stringify(r.body).includes('"cache_control"'));
|
||||
});
|
||||
|
||||
test("preserveSystemPrompt mantém o sistema nativo e ainda pode comprimir tool_result", async () => {
|
||||
const body = {
|
||||
...claudeBody(),
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "call_1",
|
||||
content: DENSE,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
const r = await omniglyphEngine.applyAsync!(body, {
|
||||
...OK,
|
||||
config: { preserveSystemPrompt: true } as never,
|
||||
});
|
||||
assert.equal(r.compressed, true);
|
||||
assert.equal((r.body as { system?: unknown }).system, DENSE);
|
||||
assert.ok(JSON.stringify(r.body).includes('"type":"image"'));
|
||||
});
|
||||
|
||||
test("apply síncrono é pass-through seguro (engine async-only)", () => {
|
||||
const body = claudeBody();
|
||||
const r = omniglyphEngine.apply(body, OK);
|
||||
|
||||
@@ -1,12 +1,27 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "node:test";
|
||||
import { resolveOmniGlyphTransport } from "../../../open-sse/services/compression/imageTransportPolicy.ts";
|
||||
|
||||
test("chatCore treats both Anthropic providers as direct OmniGlyph transports", () => {
|
||||
test("chatCore uses the measured Anthropic byte-preserving transport policy", () => {
|
||||
const chatCore = readFileSync("open-sse/handlers/chatCore.ts", "utf8");
|
||||
|
||||
assert.match(
|
||||
chatCore,
|
||||
/providerTransport:\s*provider === "anthropic" \|\| provider === "claude"[\s\S]{0,80}?"direct"/
|
||||
);
|
||||
assert.match(chatCore, /resolveOmniGlyphTransport\(provider\)/);
|
||||
const translationIndex = chatCore.indexOf("translatedBody = translateRequest(");
|
||||
const postCompressionIndex = chatCore.indexOf("if (runPostTranslationCompression");
|
||||
assert.ok(translationIndex >= 0, "chatCore deve manter a tradução explícita");
|
||||
assert.ok(postCompressionIndex > translationIndex, "OmniGlyph target-wire roda após tradução");
|
||||
assert.match(chatCore, /compressionStage: "post-translation"/);
|
||||
assert.deepEqual(resolveOmniGlyphTransport("anthropic"), {
|
||||
providerTransport: "direct",
|
||||
imageTransportFidelity: "byte-preserving",
|
||||
});
|
||||
assert.deepEqual(resolveOmniGlyphTransport("claude"), {
|
||||
providerTransport: "direct",
|
||||
imageTransportFidelity: "byte-preserving",
|
||||
});
|
||||
assert.deepEqual(resolveOmniGlyphTransport("openai"), {
|
||||
providerTransport: "aggregator",
|
||||
imageTransportFidelity: "unknown",
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ test("pacote omniglyph exporta a API que o adapter consome", async () => {
|
||||
assert.equal(typeof mod.transformAnthropicMessages, "function");
|
||||
assert.equal(typeof mod.transformOpenAIChatCompletions, "function");
|
||||
assert.equal(typeof mod.transformOpenAIResponses, "function");
|
||||
assert.equal(typeof mod.transformRequest, "function");
|
||||
assert.equal(typeof mod.isOmniGlyphSupportedModel, "function");
|
||||
assert.equal(typeof mod.isOmniGlyphSupportedGptModel, "function");
|
||||
assert.equal(mod.isOmniGlyphSupportedModel("claude-fable-5"), true);
|
||||
|
||||
@@ -18,6 +18,20 @@ const body = () => ({
|
||||
messages: [{ role: "user", content: [{ type: "text", text: "oi" }] }],
|
||||
});
|
||||
|
||||
const openaiBody = () => ({
|
||||
model: "gpt-5.6",
|
||||
messages: [
|
||||
{ role: "system", content: DENSE },
|
||||
{ role: "user", content: "oi" },
|
||||
],
|
||||
});
|
||||
|
||||
const responsesBody = () => ({
|
||||
model: "gpt-5.6",
|
||||
instructions: DENSE,
|
||||
input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "oi" }] }],
|
||||
});
|
||||
|
||||
// Agora que `estimateCompressionTokens` (stats.ts) é image-aware (Task 6), o guard
|
||||
// honesto de inflação agregada do stacked (`guardPipelineInflation` em
|
||||
// pipelineGuards.ts) não reverte mais a saída imageada do omniglyph: o estimador
|
||||
@@ -30,6 +44,7 @@ test("stacked com step omniglyph recebe providerTransport (engine roda, não é
|
||||
model: "claude-fable-5",
|
||||
supportsVision: true,
|
||||
providerTransport: "direct",
|
||||
imageTransportFidelity: "byte-preserving",
|
||||
config: { stackedPipeline: [{ engine: "rtk" }, { engine: "omniglyph" }] } as never,
|
||||
});
|
||||
const omniglyphStep = r.stats?.engineBreakdown?.find((e) => e.engine === "omniglyph");
|
||||
@@ -59,3 +74,97 @@ test("stacked sem providerTransport 'direct' pula omniglyph (transport_not_direc
|
||||
assert.ok(omniglyphStep, "omniglyph step deveria aparecer no engineBreakdown mesmo pulado");
|
||||
assert.ok(omniglyphStep!.techniquesUsed.includes("skip:transport_not_direct"));
|
||||
});
|
||||
|
||||
test("stacked pós-tradução preserva o wire OpenAI Chat e executa OmniGlyph nativo", async () => {
|
||||
registerBuiltinCompressionEngines();
|
||||
const r = await applyCompressionAsync(openaiBody(), "stacked", {
|
||||
model: "gpt-5.6",
|
||||
supportsVision: true,
|
||||
providerTransport: "direct",
|
||||
imageTransportFidelity: "byte-preserving",
|
||||
sourceFormat: "openai",
|
||||
targetFormat: "openai",
|
||||
compressionStage: "post-translation",
|
||||
config: { stackedPipeline: [{ engine: "rtk" }, { engine: "omniglyph" }] } as never,
|
||||
});
|
||||
const omniglyphStep = r.stats?.engineBreakdown?.find((e) => e.engine === "omniglyph");
|
||||
assert.ok(omniglyphStep);
|
||||
assert.ok(omniglyphStep!.techniquesUsed.includes("omniglyph:context-as-image"));
|
||||
assert.equal(r.compressed, true);
|
||||
assert.ok(JSON.stringify(r.body).includes('"type":"image_url"'));
|
||||
assert.ok(
|
||||
r.stats?.validationWarnings?.some((warning) => warning.includes("rtk: skipped (stage")),
|
||||
"engines sem suporte ao estágio devem ser explicitamente pulados"
|
||||
);
|
||||
});
|
||||
|
||||
test("stacked pós-tradução não adapta input[] Responses para messages[] antes do OmniGlyph", async () => {
|
||||
registerBuiltinCompressionEngines();
|
||||
const r = await applyCompressionAsync(responsesBody(), "stacked", {
|
||||
model: "gpt-5.6",
|
||||
supportsVision: true,
|
||||
providerTransport: "direct",
|
||||
imageTransportFidelity: "byte-preserving",
|
||||
sourceFormat: "openai-responses",
|
||||
targetFormat: "openai-responses",
|
||||
compressionStage: "post-translation",
|
||||
config: { stackedPipeline: [{ engine: "omniglyph" }] } as never,
|
||||
});
|
||||
assert.equal(r.compressed, true);
|
||||
assert.ok(JSON.stringify(r.body).includes('"type":"input_image"'));
|
||||
assert.ok(Array.isArray((r.body as { input?: unknown }).input));
|
||||
});
|
||||
|
||||
test("OpenAI→Claude usa o wire traduzido sem uma segunda tradução", async () => {
|
||||
registerBuiltinCompressionEngines();
|
||||
const translatedClaudeBody = body();
|
||||
const r = await applyCompressionAsync(translatedClaudeBody, "omniglyph", {
|
||||
model: "claude-fable-5",
|
||||
supportsVision: true,
|
||||
providerTransport: "direct",
|
||||
imageTransportFidelity: "byte-preserving",
|
||||
sourceFormat: "openai",
|
||||
targetFormat: "claude",
|
||||
compressionStage: "post-translation",
|
||||
});
|
||||
assert.equal(r.compressed, true);
|
||||
assert.ok(JSON.stringify(r.body).includes('"type":"image"'));
|
||||
assert.ok(!JSON.stringify(r.body).includes('"type":"image_url"'));
|
||||
});
|
||||
|
||||
test("Claude→OpenAI aguarda o wire alvo e não imageia o corpo fonte", async () => {
|
||||
registerBuiltinCompressionEngines();
|
||||
const pre = await applyCompressionAsync(body(), "omniglyph", {
|
||||
model: "claude-fable-5",
|
||||
supportsVision: true,
|
||||
providerTransport: "direct",
|
||||
imageTransportFidelity: "byte-preserving",
|
||||
sourceFormat: "claude",
|
||||
targetFormat: "openai",
|
||||
compressionStage: "pre-translation",
|
||||
});
|
||||
assert.equal(pre.compressed, false);
|
||||
assert.ok(pre.stats?.techniquesUsed.includes("skip:requires_post_translation"));
|
||||
|
||||
const post = await applyCompressionAsync(
|
||||
{
|
||||
model: "gpt-5.6",
|
||||
messages: [
|
||||
{ role: "system", content: DENSE },
|
||||
{ role: "user", content: "oi" },
|
||||
],
|
||||
},
|
||||
"omniglyph",
|
||||
{
|
||||
model: "gpt-5.6",
|
||||
supportsVision: true,
|
||||
providerTransport: "direct",
|
||||
imageTransportFidelity: "byte-preserving",
|
||||
sourceFormat: "claude",
|
||||
targetFormat: "openai",
|
||||
compressionStage: "post-translation",
|
||||
}
|
||||
);
|
||||
assert.equal(post.compressed, true);
|
||||
assert.ok(JSON.stringify(post.body).includes('"type":"image_url"'));
|
||||
});
|
||||
|
||||
@@ -3,8 +3,13 @@ import assert from "node:assert";
|
||||
import { applyCompressionAsync } from "../../../open-sse/services/compression/strategySelector.ts";
|
||||
import { registerBuiltinCompressionEngines } from "../../../open-sse/services/compression/engines/index.ts";
|
||||
|
||||
const DENSE = "X".repeat(500) + "\n" +
|
||||
Array.from({ length: 400 }, (_, i) => `const row_${i} = compute(${i * 17}, "${"v".repeat(80)}");`).join("\n");
|
||||
const DENSE =
|
||||
"X".repeat(500) +
|
||||
"\n" +
|
||||
Array.from(
|
||||
{ length: 400 },
|
||||
(_, i) => `const row_${i} = compute(${i * 17}, "${"v".repeat(80)}");`
|
||||
).join("\n");
|
||||
const body = () => ({
|
||||
model: "claude-fable-5",
|
||||
max_tokens: 128,
|
||||
@@ -18,6 +23,7 @@ test("modo omniglyph sozinho comprime (selecionar o modo é o enable)", async ()
|
||||
model: "claude-fable-5",
|
||||
supportsVision: true,
|
||||
providerTransport: "direct",
|
||||
imageTransportFidelity: "byte-preserving",
|
||||
});
|
||||
assert.equal(r.compressed, true);
|
||||
assert.ok(JSON.stringify(r.body).includes('"type":"image"'));
|
||||
|
||||
Reference in New Issue
Block a user