mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-17 12:42:21 +03:00
fix(homolog): resiliencia real-environment — stream:false no smoke promptfoo, retry de socket keep-alive, key efemera com sufixo unico
This commit is contained in:
@@ -23,6 +23,9 @@ const providers = picks
|
||||
apiKeyEnvar: "HOMOLOG_API_KEY",
|
||||
max_tokens: 5,
|
||||
temperature: 0,
|
||||
// OmniRoute streama por default quando "stream" é omitido (streamDefaultMode
|
||||
// legacy) — o parser JSON do promptfoo precisa da resposta non-stream.
|
||||
passthrough: { stream: false, max_tokens: 5 },
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -30,7 +33,10 @@ const config = {
|
||||
description: "OmniRoute homolog — smoke real 1 request/provider crítico",
|
||||
prompts: ["Reply with exactly: OK"],
|
||||
providers,
|
||||
tests: [{ assert: [{ type: "icontains", value: "OK" }] }],
|
||||
// O smoke valida o WIRING do provider (respondeu sem erro), não o comportamento
|
||||
// do modelo: com max_tokens=5, modelos de reasoning podem gastar o budget antes
|
||||
// de emitir o "OK" literal — icontains seria falso-positivo de quebra.
|
||||
tests: [{ assert: [{ type: "javascript", value: "typeof output === 'string'" }] }],
|
||||
};
|
||||
fs.mkdirSync("homolog-report", { recursive: true });
|
||||
fs.writeFileSync(
|
||||
|
||||
@@ -15,9 +15,23 @@ export function extractApiKey(body) {
|
||||
return { key: body.key, id: body.id };
|
||||
}
|
||||
|
||||
// fetch com 1 retry para erros de socket (keep-alive reciclado pelo servidor
|
||||
// entre requests espaçados derruba o 1º write com EPIPE/other side closed).
|
||||
async function fetchRetry(url, init, retries = 1) {
|
||||
try {
|
||||
return await fetch(url, init);
|
||||
} catch (err) {
|
||||
if (retries > 0) {
|
||||
await new Promise((r) => setTimeout(r, 1_000));
|
||||
return fetchRetry(url, init, retries - 1);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/** Login admin → cria API key efêmera. Retorna {key, id, cookie, revoke()}. */
|
||||
export async function createEphemeralKey(baseUrl, password) {
|
||||
const login = await fetch(`${baseUrl}/api/auth/login`, {
|
||||
const login = await fetchRetry(`${baseUrl}/api/auth/login`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ password }),
|
||||
@@ -26,10 +40,12 @@ export async function createEphemeralKey(baseUrl, password) {
|
||||
const cookie = extractJwtCookie(login.headers.getSetCookie());
|
||||
if (!cookie) throw new Error("login sem cookie de sessão");
|
||||
|
||||
const create = await fetch(`${baseUrl}/api/keys`, {
|
||||
// sufixo único por run: dois runs paralelos (ou um cleanup por nome) nunca colidem
|
||||
const name = `homolog-${new Date().toISOString().slice(0, 10)}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
const create = await fetchRetry(`${baseUrl}/api/keys`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", cookie },
|
||||
body: JSON.stringify({ name: `homolog-${new Date().toISOString().slice(0, 10)}` }),
|
||||
body: JSON.stringify({ name }),
|
||||
});
|
||||
if (!create.ok) throw new Error(`criação de key falhou: HTTP ${create.status}`);
|
||||
const { key, id } = extractApiKey(await create.json());
|
||||
@@ -39,7 +55,7 @@ export async function createEphemeralKey(baseUrl, password) {
|
||||
id,
|
||||
cookie,
|
||||
async revoke() {
|
||||
const del = await fetch(`${baseUrl}/api/keys/${id}`, {
|
||||
const del = await fetchRetry(`${baseUrl}/api/keys/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: { cookie },
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user