feat(compression): expõe os perfis semânticos do omniglyph nos três wires

O 1.4.0 trouxe perfis nomeados (coding-safe, balanced, aggressive,
passthrough), mas só transformAnthropicMessages() os resolve sozinho: os
transformadores OpenAI recebem TransformOptions cru e ignorariam o campo. Um
perfil escolhido pelo operador valeria no wire Claude e sumiria no OpenAI. O
adapter passa a mesclar o perfil com mergeCompressionProfileOptions() antes de
chamar Chat Completions e Responses.

O default segue aggressive — a política que os recibos publicados mediram.
Medido nesta base: com coding-safe/balanced, uma sessão sem histórico acumulado
para em below_min_chars e a engine não faz nada, porque os dois fixam
minCompressChars no máximo e desligam system/tools/tool-results. Como a engine é
opt-in, um default assim entregaria "ligado, 0% de ganho".

O perfil é TETO, não piso: mergeCompressionProfileOptions não deixa um override
do chamador reabrir uma lane lossy que o perfil fechou. Coberto por teste, por
ser contra-intuitivo.

Também fecha um caminho em que o OmniRoute violaria a própria política: o wire
OpenAI do pacote não tem compressSystem — honra apenas compressTools,
gptHistory, minCompressChars e reflow, e sempre troca a instrução por um
ponteiro para a imagem. Com preserveSystemPrompt ligado, imagear assim queimaria
o prefixo quente que a decisão cache-aware está protegendo, sem nada no corpo
devolvido denunciando. A engine agora pula com
skip:system_preservation_unsupported_on_wire.
This commit is contained in:
Xiangzhe
2026-08-18 00:13:09 -03:00
parent ce89143232
commit b5e5838a59
2 changed files with 153 additions and 15 deletions

View File

@@ -22,10 +22,12 @@ import type { CompressionResult } from "../types.ts";
import { createCompressionStats } from "../stats.ts";
import {
isOmniGlyphSupportedModelForScope,
mergeCompressionProfileOptions,
resolveCompressionProfile,
transformAnthropicMessages,
transformOpenAIChatCompletions,
transformOpenAIResponses,
transformRequest,
type CompressionProfile,
type OmniGlyphSafetyScope,
} from "omniglyph";
import { isModelImageable } from "omniglyph/applicability";
@@ -43,8 +45,26 @@ import { isModelImageable } from "omniglyph/applicability";
*/
const MEASURED_MODEL_SCOPE: OmniGlyphSafetyScope = "coding-safe";
/** Escopo semântico do transform. `aggressive` mantém a política medida atual. */
const DEFAULT_TRANSFORM_SCOPE: OmniGlyphSafetyScope = "aggressive";
/**
* Perfil padrão do OmniRoute.
*
* `aggressive` é a política que os recibos publicados mediram. `coding-safe` e
* `balanced` fixam `minCompressChars` no máximo e só colapsam histórico antigo:
* medido nesta base, uma sessão sem histórico acumulado fica em
* `below_min_chars` e a engine não faz nada — o operador veria "ligado, 0% de
* ganho". Ficam disponíveis como escolha explícita, não como default.
*/
const DEFAULT_PROFILE: OmniGlyphSafetyScope = "aggressive";
/** Perfil do passo (mais específico) > perfil global > default do OmniRoute. */
function resolveProfileName(options?: CompressionEngineApplyOptions): string {
const step = options?.stepConfig?.profile;
if (typeof step === "string" && step.trim()) return step;
const global = (options?.config as { omniglyph?: { profile?: unknown } } | undefined)?.omniglyph
?.profile;
if (typeof global === "string" && global.trim()) return global;
return DEFAULT_PROFILE;
}
/**
* O modelo precisa passar no teto medido E no escopo em vigor. Os dois wires
@@ -159,9 +179,31 @@ async function applyOmniglyph(
) {
return skip(body, "source_format_not_openai_responses");
}
if (!isModelWithinScope(model, DEFAULT_TRANSFORM_SCOPE)) {
let profile: CompressionProfile;
try {
profile = resolveCompressionProfile(resolveProfileName(options));
} catch {
// `resolveCompressionProfile` lança em nome desconhecido. Um perfil que o
// pacote não entende não pode virar "roda com a política padrão".
return skip(body, "invalid_profile");
}
if (profile.name === "passthrough") return skip(body, "profile_passthrough");
if (!isModelWithinScope(model, profile.name)) {
return skip(body, "model_not_approved");
}
const preserveSystemPrompt =
(typeof options?.stepConfig?.preserveSystemPrompt === "boolean"
? options.stepConfig.preserveSystemPrompt
: options?.config?.preserveSystemPrompt) === true;
// `compressSystem` só existe no transform Anthropic. Os wires OpenAI honram
// apenas compressTools/gptHistory/minCompressChars/reflow e sempre trocam a
// instrução por um ponteiro para a imagem. Imagear o system quando o OmniRoute
// decidiu preservá-lo queimaria o prefixo quente que a política cache-aware
// está protegendo — e nada no corpo devolvido denunciaria isso. Sem como
// honrar a política nesse wire, a engine pula.
if (preserveSystemPrompt && wireFormat !== "claude") {
return skip(body, "system_preservation_unsupported_on_wire");
}
// 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");
@@ -175,18 +217,20 @@ async function applyOmniglyph(
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 overrides = preserveSystemPrompt ? { compressSystem: false } : {};
// Só `transformAnthropicMessages` resolve o perfil por conta própria; os
// transformadores OpenAI recebem TransformOptions cru e ignorariam o campo.
const openAIOptions = mergeCompressionProfileOptions(profile, overrides);
const result =
wireFormat === "claude" && preserveSystemPrompt
? await transformRequest(encoded, { compressSystem: false })
: wireFormat === "claude"
? await transformAnthropicMessages({ body: encoded, model })
: wireFormat === "openai"
? await transformOpenAIChatCompletions(encoded)
: await transformOpenAIResponses(encoded);
wireFormat === "claude"
? await transformAnthropicMessages({
body: encoded,
model,
options: { ...overrides, profile: profile.name },
})
: wireFormat === "openai"
? await transformOpenAIChatCompletions(encoded, openAIOptions)
: await transformOpenAIResponses(encoded, openAIOptions);
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>;

View File

@@ -229,3 +229,97 @@ test("OMNIGLYPH_PROFILE do host não amplia a allowlist de modelos do OmniRoute"
assert.equal(r.compressed, false, "modelo sem recibo medido não pode entrar via env do host");
assert.ok(r.stats?.techniquesUsed.includes("skip:model_not_approved"));
});
// ---------------------------------------------------------------------------
// Perfis semânticos (OmniGlyph 1.4.0)
//
// `transformAnthropicMessages()` resolve e mescla o perfil sozinho, mas os
// transformadores OpenAI recebem `TransformOptions` cru e NÃO conhecem
// `profile`. Sem o merge explícito do host, um perfil escolhido pelo operador
// valeria no wire Claude e seria silenciosamente ignorado no wire OpenAI.
// ---------------------------------------------------------------------------
/** Histórico longo o bastante para o colapso valer mesmo com o system nativo. */
function claudeBodyWithHistory(): Record<string, unknown> {
const messages: Array<Record<string, unknown>> = [];
for (let i = 0; i < 40; i++) {
messages.push({ role: "user", content: [{ type: "text", text: `pergunta ${i}: ${DENSE}` }] });
messages.push({
role: "assistant",
content: [{ type: "text", text: `resposta ${i}: ${DENSE}` }],
});
}
messages.push({ role: "user", content: [{ type: "text", text: "oi" }] });
return { model: "claude-fable-5", max_tokens: 128, system: DENSE, messages };
}
test("perfil coding-safe chega ao wire OpenAI (não só ao wrapper Anthropic)", async () => {
const aggressive = await omniglyphEngine.applyAsync!(openaiChatBody(), {
...GPT_OK,
stepConfig: { profile: "aggressive" } as never,
});
assert.equal(aggressive.compressed, true, "aggressive é a política medida atual");
const codingSafe = await omniglyphEngine.applyAsync!(openaiChatBody(), {
...GPT_OK,
stepConfig: { profile: "coding-safe" } as never,
});
assert.equal(
codingSafe.compressed,
false,
"coding-safe mantém system e schemas de tools nativos no wire OpenAI"
);
assert.ok(
codingSafe.stats?.techniquesUsed.some((t) => t.startsWith("skip:below_min_chars")),
`esperado skip por minCompressChars, veio ${JSON.stringify(codingSafe.stats?.techniquesUsed)}`
);
});
test("perfil é TETO, não piso: coding-safe mantém o system nativo mesmo sem preserveSystemPrompt", async () => {
const body = claudeBodyWithHistory();
const r = await omniglyphEngine.applyAsync!(body, {
...OK,
stepConfig: { profile: "coding-safe", preserveSystemPrompt: false } as never,
});
assert.equal(r.compressed, true, "o histórico antigo ainda colapsa");
assert.equal(
(r.body as { system?: unknown }).system,
DENSE,
"coding-safe não deixa um override do chamador reabrir a compressão do system"
);
});
test("perfil passthrough desliga a engine sem tocar no corpo", async () => {
const body = claudeBodyWithHistory();
const r = await omniglyphEngine.applyAsync!(body, {
...OK,
stepConfig: { profile: "passthrough" } as never,
});
assert.equal(r.compressed, false);
assert.ok(r.stats?.techniquesUsed.includes("skip:profile_passthrough"));
assert.deepEqual(r.body, body);
});
test("perfil inválido falha fechado, não propaga exceção", async () => {
const r = await omniglyphEngine.applyAsync!(claudeBody(), {
...OK,
stepConfig: { profile: "turbo-max" } as never,
});
assert.equal(r.compressed, false);
assert.ok(r.stats?.techniquesUsed.includes("skip:invalid_profile"));
});
test("preserveSystemPrompt: wire OpenAI pula, porque o pacote não sabe preservar system lá", async () => {
// O transform OpenAI do OmniGlyph 1.4.0 honra apenas compressTools, gptHistory,
// minCompressChars e reflow — não existe compressSystem nesse wire, e a
// instrução vira sempre um ponteiro para a imagem. Imagear assim queimaria o
// prefixo quente que a decisão cache-aware do OmniRoute mandou preservar, e o
// OmniRoute não teria como saber. Sem opção de honrar a política, pula.
const r = await omniglyphEngine.applyAsync!(openaiChatBody(), {
...GPT_OK,
config: { preserveSystemPrompt: true } as never,
});
assert.equal(r.compressed, false);
assert.ok(r.stats?.techniquesUsed.includes("skip:system_preservation_unsupported_on_wire"));
assert.deepEqual(r.body, openaiChatBody());
});