mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-18 13:14:56 +03:00
feat(compression): adota omniglyph 1.4.0 e tira o gate de modelo da env do host
O 1.4.0 introduziu escopos de segurança e passou a resolvê-los dentro de isOmniGlyphSupportedModel() lendo process.env.OMNIGLYPH_PROFILE. Somado ao OMNIGLYPH_MODELS que já existia, duas variáveis do ambiente do host decidiam em silêncio o gate de TODO request do OmniRoute: passthrough desligaria a engine inteira e OMNIGLYPH_MODELS admitiria modelos sem recibo medido, enquanto a UI segue prometendo "Claude Fable 5 na rota direta medida". O adapter passa a usar isOmniGlyphSupportedModelForScope() com escopo explícito e fixa o escopo mais restrito como teto: a env só pode ESTREITAR a allowlist, nunca alargar. Os dois wires compartilham a mesma lista no pacote desde o 1.4.0, então uma checagem cobre Anthropic e GPT. - omniglyph ^1.3.1 -> ^1.4.0 (lock em 1.4.0); - testes de regressão para os dois caminhos de sequestro por env; - teste de contrato dos exports novos (escopo, perfis, accounting). O 1.4.0 também traz, sem mudança de código aqui: correção do glyph K que era lido como H, remoção do backtracking polinomial no secret-guard, overrides do pnpm em pnpm-workspace.yaml e as transitivas vulneráveis resolvidas.
This commit is contained in:
@@ -21,15 +21,41 @@ import type { CompressionEngine, CompressionEngineApplyOptions } from "./types.t
|
||||
import type { CompressionResult } from "../types.ts";
|
||||
import { createCompressionStats } from "../stats.ts";
|
||||
import {
|
||||
isOmniGlyphSupportedGptModel,
|
||||
isOmniGlyphSupportedModel,
|
||||
isOmniGlyphSupportedModelForScope,
|
||||
transformAnthropicMessages,
|
||||
transformOpenAIChatCompletions,
|
||||
transformOpenAIResponses,
|
||||
transformRequest,
|
||||
type OmniGlyphSafetyScope,
|
||||
} from "omniglyph";
|
||||
import { isModelImageable } from "omniglyph/applicability";
|
||||
|
||||
/**
|
||||
* Teto de modelos do OmniRoute — sempre o escopo mais restrito do pacote.
|
||||
*
|
||||
* `isOmniGlyphSupportedModel()` resolve o escopo lendo `OMNIGLYPH_PROFILE` do
|
||||
* processo, e a lista base sai de `OMNIGLYPH_MODELS`. Duas variáveis do HOST
|
||||
* decidiriam, em silêncio, o gate de todo request do OmniRoute: `passthrough`
|
||||
* desligaria a engine inteira e `OMNIGLYPH_MODELS` ADMITIRIA modelos sem
|
||||
* recibo medido — enquanto a UI continua prometendo "Claude Fable 5 na rota
|
||||
* direta medida". Fixar o escopo mais restrito faz o gate só poder ESTREITAR
|
||||
* pela env, nunca alargar, e mantém a decisão na configuração do OmniRoute.
|
||||
*/
|
||||
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";
|
||||
|
||||
/**
|
||||
* O modelo precisa passar no teto medido E no escopo em vigor. Os dois wires
|
||||
* (Anthropic e GPT) compartilham a mesma allowlist no pacote desde 1.4.0, então
|
||||
* uma única checagem cobre os dois.
|
||||
*/
|
||||
function isModelWithinScope(model: string, scope: OmniGlyphSafetyScope): boolean {
|
||||
if (!isOmniGlyphSupportedModelForScope(model, MEASURED_MODEL_SCOPE)) return false;
|
||||
return isOmniGlyphSupportedModelForScope(model, scope);
|
||||
}
|
||||
|
||||
function skip(body: Record<string, unknown>, reason: string): CompressionResult {
|
||||
try {
|
||||
return {
|
||||
@@ -133,11 +159,9 @@ async function applyOmniglyph(
|
||||
) {
|
||||
return skip(body, "source_format_not_openai_responses");
|
||||
}
|
||||
const supportedModel =
|
||||
wireFormat === "claude"
|
||||
? isOmniGlyphSupportedModel(model)
|
||||
: isOmniGlyphSupportedGptModel(model);
|
||||
if (!supportedModel) return skip(body, "model_not_approved");
|
||||
if (!isModelWithinScope(model, DEFAULT_TRANSFORM_SCOPE)) {
|
||||
return skip(body, "model_not_approved");
|
||||
}
|
||||
// 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");
|
||||
|
||||
8
package-lock.json
generated
8
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.3.1",
|
||||
"omniglyph": "^1.4.0",
|
||||
"onnxruntime-node": "~1.24.3",
|
||||
"open": "^11.0.0",
|
||||
"ora": "^9.4.1",
|
||||
@@ -28871,9 +28871,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/omniglyph": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/omniglyph/-/omniglyph-1.3.1.tgz",
|
||||
"integrity": "sha512-6QnZCoXYczjsPN2x+XpbimimjO6kCoSZUzsdSvoKjtw28U1U724VgLICBNaLX4FFs5jd7SrYNNs9Aee2iIkcoA==",
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/omniglyph/-/omniglyph-1.4.0.tgz",
|
||||
"integrity": "sha512-4zAqDW9pBb2i+fiGOVLIKbdecZeo55UmKQoLku1apxo3TSA4gfqcMo2MqQpal1VicckUwTbexFLasW7qngXUHA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"gpt-tokenizer": "^3.4.0"
|
||||
|
||||
12
package.json
12
package.json
@@ -263,6 +263,7 @@
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@huggingface/transformers": "^4.2.0",
|
||||
"@lobehub/icons": "^5.8.0",
|
||||
"@modelcontextprotocol/sdk": "^1.29.0",
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
@@ -304,7 +305,8 @@
|
||||
"next-intl": "^4.13.6",
|
||||
"next-themes": "^0.4.6",
|
||||
"node-machine-id": "^1.1.12",
|
||||
"omniglyph": "^1.3.1",
|
||||
"omniglyph": "^1.4.0",
|
||||
"onnxruntime-node": "~1.24.3",
|
||||
"open": "^11.0.0",
|
||||
"ora": "^9.4.1",
|
||||
"parse5": "^8.0.1",
|
||||
@@ -335,9 +337,7 @@
|
||||
"xxhash-wasm": "^1.1.0",
|
||||
"yazl": "^3.3.1",
|
||||
"zod": "^4.4.3",
|
||||
"zustand": "^5.0.13",
|
||||
"@huggingface/transformers": "^4.2.0",
|
||||
"onnxruntime-node": "~1.24.3"
|
||||
"zustand": "^5.0.13"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@atjsh/llmlingua-2": "2.0.3",
|
||||
@@ -345,9 +345,9 @@
|
||||
"better-sqlite3": "^13.0.2",
|
||||
"js-tiktoken": "^1.0.20",
|
||||
"keytar": "^7.9.0",
|
||||
"sqlite-vec": "^0.1.9",
|
||||
"tls-client-node": "^0.2.0",
|
||||
"wreq-js": "^3.0.0",
|
||||
"sqlite-vec": "^0.1.9"
|
||||
"wreq-js": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@axe-core/playwright": "^4.11.3",
|
||||
|
||||
@@ -187,3 +187,45 @@ test("OpenAI não roda no estágio pré-tradução", async () => {
|
||||
assert.equal(r.compressed, false);
|
||||
assert.ok(r.stats?.techniquesUsed.includes("skip:requires_post_translation"));
|
||||
});
|
||||
|
||||
// OmniGlyph 1.4.0 introduziu escopos de segurança (`coding-safe`/`balanced`/
|
||||
// `aggressive`/`passthrough`) e passou a resolvê-los, dentro de
|
||||
// `isOmniGlyphSupportedModel()`, lendo `process.env.OMNIGLYPH_PROFILE`. Isso
|
||||
// transforma uma variável de ambiente do HOST num gate silencioso de TODO
|
||||
// request do OmniRoute: um `OMNIGLYPH_PROFILE=passthrough` exportado no shell
|
||||
// do processo desligaria a engine sem que nenhuma configuração do OmniRoute
|
||||
// tivesse mudado — e sem nenhum sinal na UI. A política é do OmniRoute; o
|
||||
// adapter tem de passar o escopo explicitamente.
|
||||
async function withEnv<T>(key: string, value: string, fn: () => Promise<T>): Promise<T> {
|
||||
const had = Object.prototype.hasOwnProperty.call(process.env, key);
|
||||
const previous = process.env[key];
|
||||
process.env[key] = value;
|
||||
try {
|
||||
return await fn();
|
||||
} finally {
|
||||
if (had) process.env[key] = previous;
|
||||
else delete process.env[key];
|
||||
}
|
||||
}
|
||||
|
||||
test("OMNIGLYPH_PROFILE do host não decide o gate de modelo do OmniRoute", async () => {
|
||||
const r = await withEnv("OMNIGLYPH_PROFILE", "passthrough", () =>
|
||||
omniglyphEngine.applyAsync!(claudeBody(), OK)
|
||||
);
|
||||
assert.equal(
|
||||
r.compressed,
|
||||
true,
|
||||
"env do processo não pode desligar a engine: o escopo vem da config do OmniRoute"
|
||||
);
|
||||
});
|
||||
|
||||
test("OMNIGLYPH_PROFILE do host não amplia a allowlist de modelos do OmniRoute", async () => {
|
||||
const body = { ...claudeBody(), model: "claude-sonnet-5" };
|
||||
const r = await withEnv("OMNIGLYPH_PROFILE", "aggressive", () =>
|
||||
withEnv("OMNIGLYPH_MODELS", "claude-fable-5,claude-sonnet-5", () =>
|
||||
omniglyphEngine.applyAsync!(body, { ...OK, model: "claude-sonnet-5" })
|
||||
)
|
||||
);
|
||||
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"));
|
||||
});
|
||||
|
||||
@@ -15,3 +15,30 @@ test("pacote omniglyph exporta a API que o adapter consome", async () => {
|
||||
const applicability = await import("omniglyph/applicability");
|
||||
assert.equal(typeof applicability.isModelImageable, "function");
|
||||
});
|
||||
|
||||
// Superfícies introduzidas no 1.4.0. Sem esta asserção, uma remoção upstream só
|
||||
// apareceria em runtime — o adapter importa esses símbolos diretamente.
|
||||
test("omniglyph 1.4.0 exporta escopo de segurança, perfis e accounting", async () => {
|
||||
const mod = await import("omniglyph");
|
||||
|
||||
// Gate de modelo por escopo explícito (não pela env do processo).
|
||||
assert.equal(typeof mod.isOmniGlyphSupportedModelForScope, "function");
|
||||
assert.equal(mod.isOmniGlyphSupportedModelForScope("claude-fable-5", "coding-safe"), true);
|
||||
assert.equal(mod.isOmniGlyphSupportedModelForScope("claude-sonnet-5", "coding-safe"), false);
|
||||
assert.equal(
|
||||
mod.isOmniGlyphSupportedModelForScope("claude-fable-5", "passthrough"),
|
||||
false,
|
||||
"passthrough não habilita modelo nenhum"
|
||||
);
|
||||
|
||||
// Perfis semânticos.
|
||||
assert.equal(typeof mod.resolveCompressionProfile, "function");
|
||||
assert.equal(typeof mod.mergeCompressionProfileOptions, "function");
|
||||
assert.equal(typeof mod.shouldKeepToolResultSharp, "function");
|
||||
assert.equal(mod.resolveCompressionProfile("coding-safe").name, "coding-safe");
|
||||
assert.throws(() => mod.resolveCompressionProfile("nao-existe"));
|
||||
|
||||
// Contabilidade física normalizada.
|
||||
assert.equal(typeof mod.normalizeAccounting, "function");
|
||||
assert.equal(typeof mod.providerActualInputTokens, "function");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user