Pular para o conteúdo

POST /v1/routing/explain

Faça dry-run da seleção de roteamento para um body de requisição candidato. O gateway executa a cadeia completa de estratégias — incluindo o LLM firewall — e retorna os candidatos, pesos, o modelo que teria sido selecionado e a confiança do roteador. Nenhum provider é chamado. Nenhuma linha decision_trace é emitida. Nenhum orçamento de rate limit é gasto no caminho de chat.

Projetado para checks de CI (“este prompt roteia como esperamos?”), demos pré-venda e experimentos de tuning de constraints.

POST https://api.floopy.ai/v1/routing/explain
Authorization: Bearer <your-floopy-api-key>
Content-Type: application/json
  • Permissão: write_permission. O endpoint aceita um payload JSON (daí write), mas é não-mutativo — sem chamada ao provider, sem linha de log, sem incremento de contador.
  • Apenas plano Pro (requer Feedback-Driven routing).
  • Estamos liberando esse endpoint por organização enquanto validamos a qualidade. Fale com o suporte caso sua organização ainda não esteja habilitada.

Limite do body: 64 KiB. Payloads maiores retornam 400 body_too_large.

{
"request": {
"model": "gpt-5.4",
"messages": [
{ "role": "user", "content": "Summarise this incident report in 3 bullets." }
],
"temperature": 0.3
},
"headers": {
"floopy-session-id": "sess_demo_1"
}
}
FieldTypeRequiredConstraints
requestobjectYesUm body padrão de chat-completion compatível com OpenAI (mesmo formato de POST /v1/chat/completions).
headersobjectNoUm subconjunto de headers para encaminhar ao contexto de roteamento. Chaves reservadas são descartadas no servidor (veja Sanitização de headers).

Chaves desconhecidas no nível superior retornam 400 invalid_body (o schema da requisição é deny_unknown_fields).

Antes de o map de headers ser passado para o engine de dry-run, entradas cuja chave em minúsculas combinar com as seguintes são descartadas:

  • match exato: authorization, cookie, host
  • match por prefixo: x-forwarded-, x-real-ip

Isso impede que o endpoint de explain seja abusado como ferramenta de header-relay ou fingerprinting.

{
"dry_run": true,
"strategy_id": "feedback_driven",
"phase": "auto",
"weights": { "session": 0.5, "auto": 0.3, "manual": 0.1, "benchmark": 0.1 },
"candidates": [
{ "provider": "openai", "model": "gpt-5.4-mini", "score": 0.81 },
{ "provider": "anthropic", "model": "claude-sonnet-4", "score": 0.78 }
],
"filtered": [
{ "provider": "openai", "model": "gpt-5.4", "reason": "constraint_max_cost_increase", "score": 0.86 }
],
"would_select": { "provider": "openai", "model": "gpt-5.4-mini" },
"reason": "dispatched",
"confidence": 0.78,
"confidence_reason": "ok",
"exploration_rate_effective": 0.05,
"used_shared_pool_prior": false,
"evidence": {
"samples": 412,
"top2_score_gap": 0.07,
"outcome_variance": 0.04,
"recent_regressions": { "kind": "exact", "exact": 1 },
"last_regression_at": "2026-05-04T18:25:00Z"
},
"explanation": {
"text": "A Floopy rotearia esta requisicao para openai/gpt-5.4-mini com base em 412 amostras historicas e uma confianca moderada de 0,78. O proximo candidato pontuou dentro de 0,07 pontos e a variancia dos resultados tem se mantido estavel. Uma regressao foi registrada nos ultimos 7 dias.",
"template_id": "feedback_driven_moderate_confidence"
}
}

A resposta tambem carrega um cabecalho Content-Language ecoando a locale usada para renderizar explanation.text. Veja a feature de Explicacao de Decisao.

FieldTypeDescription
dry_runbooleanSempre true.
strategy_idstringEstratégia executada (feedback_driven, smart_cost, etc.).
phasestring | nullFase do Feedback-Driven.
weightsobject | nullPesos dos sinais (apenas Feedback-Driven).
candidatesarrayModelos considerados e seus scores.
filteredarrayCandidatos removidos. reason é um enum fechado (veja GET /v1/decisions/{request_id}).
would_selectobject | nullO par (provider, model) para o qual o gateway despacharia. null quando o firewall bloqueou a requisição.
reasonstringdispatched, exhausted, ou no_enabled_targets.
confidencenumber | nullVeja a metodologia de Confidence.
confidence_reasonstring | nullEnum fechado: ok, cap_day0, cap_shared, no_router_invoked, insufficient_samples, single_candidate.
exploration_rate_effectivenumberTaxa efetiva de exploração.
used_shared_pool_priorbooleanSe priors cross-tenant influenciaram o score.
evidenceobject | nullEntradas que produziram o numero de confianca, expostas para auditoria (apenas Feedback-Driven e Smart-Cost). null quando nenhum router foi invocado. Veja Metodologia de Confianca.
evidence.samplesintegerContagem rolante de amostras.
evidence.top2_score_gapnumberGap de score entre vencedor e segundo colocado.
evidence.outcome_variancenumberVariancia rolante de resultados sobre o vencedor.
evidence.recent_regressionstagged unionContagem bucketizada de regressoes sobre o (provider, model) do vencedor nos ultimos 7 dias. kind: "exact" com campo exact para n < 10; kind: "at_least" com campo at_least para os limiares 10 e 50.
evidence.last_regression_atISO8601 | nullRegressao mais recente na janela de 7 dias, arredondada para o multiplo de 5 minutos mais proximo. null quando nao ha.
explanationobject | nullExplicacao legivel da decisao em dry-run. Veja Explicacao de Decisao.
explanation.textstringParagrafo em prosa simples (≤ 600 caracteres). Nunca referencia o conteudo do prompt.
explanation.template_idstringId de template (enum fechado).

A resposta carrega:

  • Content-Language: en ou Content-Language: pt — a locale usada para renderizar explanation.text. Resolvida do cabecalho Accept-Language da requisicao, caindo para en. Veja Explicacao de Decisao.

O LLM firewall executa no caminho de dry-run. Se ele bloquear, a resposta ainda é 200, com:

{ "filtered": [{ "provider": "...", "model": "...", "reason": "firewall_blocked", "score": 0.0 }],
"would_select": null,
"reason": "no_enabled_targets" }

Isto é intencional — o dry-run reflete fielmente o que aconteceria em produção, incluindo o firewall.

Statuserror codeQuando
400invalid_bodyO body falhou na validação de schema ou não é JSON.
400body_too_largeO body excede 64 KiB.
403write_permission / plan_requiredPermissão ou flag de plano ausente.
404no_routeNenhuma regra de roteamento se aplica à org e ao modelo.
429rate_limitedExcedeu o orçamento por organização ou por key. Carrega Retry-After.
5xxinternalFalha upstream.
Terminal window
curl -s -X POST \
-H "Authorization: Bearer $FLOOPY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"request": {
"model": "gpt-5.4",
"messages": [{"role":"user","content":"Summarise this incident report in 3 bullets."}],
"temperature": 0.3
},
"headers": {"floopy-session-id":"sess_demo_1"}
}' \
"https://api.floopy.ai/v1/routing/explain" | jq .

Ambas as janelas são avaliadas atomicamente — se qualquer uma negar, a requisição é rejeitada com 429.

  • 30 requests / minuto / organização (floopy_explain_rpm_budget, default).
  • 10 requests / minuto / API key.

O cap duplo existe para que uma key vazada não consiga sondar internals do roteamento em alta cadência.