Commit Graph

4645 Commits

Author SHA1 Message Date
Diego Rodrigues de Sa e Souza
af32e0041c fix(dashboard): switch to visible filter after auto-hiding failed models in test-all (#4887) (#4991)
* fix(dashboard): switch to visible filter after auto-hiding failed models in OAuth provider test-all (#4887)

* test(dashboard): move #4887 test into tests/unit/ui so a CI runner collects it

---------

Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
2026-06-25 03:29:09 -03:00
Diego Rodrigues de Sa e Souza
0412b128c0 fix(sse): soft-penalize exhausted providers in auto-combo scoring (#4540) (#4990)
* fix(sse): soft-penalize exhausted providers in auto-combo scoring (#4540)

* chore(quality): document STATUS_SOFT_DEPRIORITIZE_FACTOR + rebaseline combo.ts for #4540

---------

Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
2026-06-25 03:28:43 -03:00
Diego Rodrigues de Sa e Souza
083d4b0044 fix(sse): honor per-account proxies and fingerprint rotation in opencode executor (#4954) (#4989)
* fix(sse): honor per-account proxies and fingerprint rotation in opencode executor (#4954)

* chore(quality): rebaseline auth.ts file-size for #4954 (+39: synthetic no-auth providerSpecificData hydration of fingerprints/accountProxies; irreducible credential-path wiring, covered by opencode-proxy-rotation-4954.test.ts + 159 auth/noauth regression)

---------

Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
2026-06-25 03:27:55 -03:00
Diego Rodrigues de Sa e Souza
bf6c8ca3fd fix(compression): stop RTK over-truncating file-read tool results (#4559) (#4987)
* fix(compression): stop RTK over-truncating file-read tool results (#4559)

* chore(quality): trim #4559 comment to keep rtk/index.ts within size cap

---------

Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
2026-06-25 03:27:36 -03:00
Diego Rodrigues de Sa e Souza
d38bf09551 fix(sse): fail over on 400 responses carrying rate-limit text (#4976) (#4986)
* fix(sse): fail over on 400 responses carrying rate-limit text (#4976)

* chore(quality): rebaseline accountFallback.ts file-size for #4976 fix

---------

Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
2026-06-25 03:26:48 -03:00
Diego Rodrigues de Sa e Souza
568472d09f fix(dashboard): proxy-pool success gating, sync timestamp, opt-in Redis (#4878) (#4988)
Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
2026-06-25 03:11:16 -03:00
Diego Rodrigues de Sa e Souza
d648e215f8 docs(resilience): document Quota-Share Concurrency Control (max_concurrent + serialization + cooldown-wait) (#4980)
Documents the v3.8.36 quota-share concurrency layers in RESILIENCE_GUIDE.md:
per-connection max_concurrent cap, the quota-share request serialization semaphore
(FASE 2.1, qsconn:<connectionId>, fail-open, kill-switch), and the combo
cooldown-aware retry — so operators know how to cap a subscription account's
concurrency and why the routing gate alone cannot contain a single-connection flood.

Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
2026-06-25 00:28:30 -03:00
Diego Rodrigues de Sa e Souza
1f685ebcd7 feat(quota): serializa concorrência por conexão no caminho quota-share (FASE 2.1) (#4970)
O gating de quota-share em selectQuotaShareTarget é fail-open: uma conexão
at-cap só é despriorizada, nunca bloqueada. Com 1 conexão por conta de
assinatura (caso comum), chamadas concorrentes ainda floodam a conta (→ 429 +
cooldown) — provado live na .15: 3 chamadas concorrentes com max_concurrent=1
despacharam todas em 94ms.

Adiciona um semáforo POR CONEXÃO em torno do dispatch quota-share: chamadas
excedentes esperam na fila em vez de floodar (key qsconn:<connectionId>, cap =
max_concurrent da conexão). Fail-open em fila saturada/timeout para nunca
piorar disponibilidade. Gated por strategy===quota-share + kill-switch
resilienceSettings.quotaShareConcurrencyLimit (default on; UI no ResilienceTab).

Lógica extraível isolada no leaf puro combo/quotaShareConcurrency.ts
(unit-testado: estabilidade da key, no-op sem cap, serialização real,
fail-open). Settings + schema + UI espelham comboCooldownWait.

Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
2026-06-24 23:54:43 -03:00
Diego Rodrigues de Sa e Souza
028ed4ea7b fix(quality): resolve base-reds da release — db-rules allowlist + task-aware router precedence (#4973)
Dois base-reds pré-existentes que reprovavam o CI da release v3.8.36 (Fast
Quality Gates + Unit Tests fast-path), independentes de qualquer feature em voo:

1. check:db-rules / allowlist: os módulos db-internal caseMapping (#4947) e
   schemaColumns (#4948), extraídos de db/core.ts e importados só por ele, não
   estavam em INTENTIONALLY_INTERNAL. Registrados na allowlist (correção
   canônica — são internos legítimos, não re-exportados pelo localDb).

2. auto-strategy honra LKGP/cost (combo-routing-engine.test.ts, 2 testes): o
   task-aware reordering (#4945, reorderByTaskWeight) roda para strategy "auto" e
   era aplicado DEPOIS do router explícito (selectWithStrategy: lkgp/cost),
   sobrescrevendo o orderedTargets[0] que o operador escolheu. Instrumentação
   provou: post-filter [0]=claude (LKGP) → post-task [0]=gpt-oss. Correção: quando
   o auto usa router explícito, preserva o [0] dele e deixa o task-aware refinar
   só a cauda de fallback. gpt-oss-120b PERMANECE tool-capable (não é mudança de
   catálogo; o model-capabilities-registry test segue verde).

Validado: 121 testes (combo-routing-engine + combo-task-aware + registry) verdes,
red-check confirmado, db-rules/file-size/typecheck/lint/prettier OK.

Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
2026-06-24 23:51:07 -03:00
Diego Rodrigues de Sa e Souza
a9ddef1302 feat(quota): combo quota-share espera cooldown curto e re-despacha (Variante A) (#4967)
Integrated into release/v3.8.36
2026-06-24 18:14:59 -03:00
Diego Rodrigues de Sa e Souza
195e40ff7a feat(quota): respeita max_concurrent por conexão no roteamento (#4965)
Integrated into release/v3.8.36
2026-06-24 18:13:31 -03:00
Diego Rodrigues de Sa e Souza
a58183ab84 fix(quota): migração 107 ativa estratégia quota-share nos combos qtSd/ existentes [Fase 3 #9] (#4962)
Integrated into release/v3.8.36
2026-06-24 18:12:15 -03:00
Diego Rodrigues de Sa e Souza
d3e02e630f fix(build): drop @omniroute/open-sse from optimizePackageImports (build OOM) (#4968)
Integrated into release/v3.8.36 — fixes build OOM (optimizePackageImports open-sse)
2026-06-24 17:58:33 -03:00
Diego Rodrigues de Sa e Souza
b03134e709 fix(dashboard): restore home provider-topology card hidden by #4596 default (#4963)
Integrated into release/v3.8.36 — restores home topology card (#4596 regression)
2026-06-24 14:31:27 -03:00
Diego Rodrigues de Sa e Souza
678cbcf207 chore(quality): reconcile file-size baseline for #4960 provider-display-name (#4961)
Integrated into release/v3.8.36
2026-06-24 14:09:41 -03:00
Diego Rodrigues de Sa e Souza
072094000d fix(translator): preserve legitimate empty-string tool arguments in openai-to-claude streaming (#4951) (#4959)
Integrated into release/v3.8.36 (fixes #4951)
2026-06-24 14:08:24 -03:00
Diego Rodrigues de Sa e Souza
cd27701c1e fix(api): parse /v1/responses body once instead of 3-4x on the hot path (#4041) (#4958)
Integrated into release/v3.8.36 (fixes #4041)
2026-06-24 14:06:48 -03:00
Diego Rodrigues de Sa e Souza
d4c2e4dd0a fix(api): evict stale in-memory rate-limit windows to stop slow heap leak (#4041) (#4957)
Integrated into release/v3.8.36 (fixes #4041)
2026-06-24 14:03:52 -03:00
Diego Rodrigues de Sa e Souza
3d37242993 fix(dashboard): show custom provider given-name instead of internal id across dashboard pages (#4603) (#4960)
Integrated into release/v3.8.36 (fixes #4603)
2026-06-24 14:03:21 -03:00
Diego Rodrigues de Sa e Souza
d575691d90 refactor(sse): extrai a família Antigravity de services/usage.ts (#4956)
Integrated into release/v3.8.36
2026-06-24 13:13:27 -03:00
Diego Rodrigues de Sa e Souza
cca71a5d22 refactor(sse): extrai a família GLM de services/usage.ts (#4953)
Integrated into release/v3.8.36
2026-06-24 13:12:23 -03:00
Diego Rodrigues de Sa e Souza
8a7cf50144 refactor(sse): extrai a família MiniMax de services/usage.ts (#4952)
Integrated into release/v3.8.36
2026-06-24 13:11:29 -03:00
Diego Rodrigues de Sa e Souza
07b385d4c1 feat(combo): task-aware routing strategy (#4945)
Integrated into release/v3.8.36
2026-06-24 12:43:38 -03:00
Diego Rodrigues de Sa e Souza
6137d4ef57 fix(translator): provider thinking compatibility (DeepSeek/Gemini) (#4946)
Integrated into release/v3.8.36
2026-06-24 12:41:30 -03:00
Diego Rodrigues de Sa e Souza
d65050a1db feat(providers): update volcengine-ark model list with DeepSeek V4 (#4905)
Integrated into release/v3.8.36
2026-06-24 12:40:47 -03:00
Diego Rodrigues de Sa e Souza
7c75b48792 fix(combo): fetch models dynamically from custom provider endpoints (#4860)
Integrated into release/v3.8.36
2026-06-24 12:40:08 -03:00
Diego Rodrigues de Sa e Souza
950fc6e60e fix(compression): eliminate ReDoS in math_inline preservation pattern (#4795) (#4838)
Integrated into release/v3.8.36 (fixes #4795)
2026-06-24 12:39:32 -03:00
Diego Rodrigues de Sa e Souza
4a1ae868ce refactor(open-sse): extract safeParseJSON util, dedup tryParseJSON (#4735)
Integrated into release/v3.8.36
2026-06-24 12:39:03 -03:00
Diego Rodrigues de Sa e Souza
6e28889aad refactor(sse): dedup fallback tool_call id helper (#4736)
Integrated into release/v3.8.36
2026-06-24 12:38:25 -03:00
Diego Rodrigues de Sa e Souza
cc8557cedf fix(qoder): exchange PAT for jt-* job token before Cosy chat (#4683) (#4884)
Integrated into release/v3.8.36 (fixes #4683)
2026-06-24 11:56:33 -03:00
Diego Rodrigues de Sa e Souza
b1b3069cd1 fix(translator): regroup parallel tool results adjacent to their assistant (#4714) (#4882)
Integrated into release/v3.8.36 (fixes #4714)
2026-06-24 11:55:40 -03:00
Diego Rodrigues de Sa e Souza
de19b3b087 refactor(sse): extrai quota-core (UsageQuota + builders) de services/usage.ts (#4950)
Integrated into release/v3.8.36
2026-06-24 11:54:09 -03:00
Diego Rodrigues de Sa e Souza
565b77143c refactor(sse): extrai scalar/format helpers de services/usage.ts (#4949)
Integrated into release/v3.8.36
2026-06-24 11:53:22 -03:00
Diego Rodrigues de Sa e Souza
ca72de7ba4 refactor(db): extrai schema-column reconciliation de db/core.ts (#4948)
Integrated into release/v3.8.36
2026-06-24 11:52:40 -03:00
Diego Rodrigues de Sa e Souza
5073aff2ae refactor(db): extrai column-mapping (snake↔camel) de db/core.ts (#4947)
Integrated into release/v3.8.36
2026-06-24 11:52:04 -03:00
Diego Rodrigues de Sa e Souza
ca0fdddebf refactor(db): extrai row-parsers + tipos compartilhados de db/apiKeys.ts (#4943)
Integrated into release/v3.8.36
2026-06-24 11:51:33 -03:00
Diego Rodrigues de Sa e Souza
f1589801ee refactor(db): extrai model-permission matching de db/apiKeys.ts (#4936)
Integrated into release/v3.8.36
2026-06-24 11:50:52 -03:00
Diego Rodrigues de Sa e Souza
419a90f240 refactor(api): extrai format-validators (OpenAI/Anthropic) de validation.ts (#4933)
Integrated into release/v3.8.36
2026-06-24 11:50:20 -03:00
Diego Rodrigues de Sa e Souza
537e5b5f6e refactor(api): extrai validators search + embedding/rerank de validation.ts (#4932)
Integrated into release/v3.8.36
2026-06-24 11:48:10 -03:00
Diego Rodrigues de Sa e Souza
9708288b57 feat(quota): estratégia dedicada de quota-share (DRR + P2C in-flight + gating per-model) [Fase 3 #9] (#4939)
* feat(quota): estratégia dedicada de quota-share (DRR + P2C in-flight + gating per-model) [Fase 3 #9]

Estratégia interna "quota-share" isolada num módulo dedicado — NÃO toca a seleção/
fair-share genérica (decisão do dono: não mexer no que já funciona). Os combos qtSd/
(quotaCombos.ts) passam de fill-first para essa strategy; combo.ts ganha só 1 branch
de dispatch que delega 100% ao módulo (nenhum case existente alterado).

- quotaShareStrategy.ts: gating per-model (isBucketSaturated do #3) + DRR (quantum
  proporcional ao weight) + P2C sobre carga in-flight.
- quotaShareInflight.ts: contador in-flight com TTL/lease de 120s — fallback do
  decrement-on-abort sem precisar instrumentar o combo genérico.
- "quota-share" registrada como strategy INTERNA (não exposta na UI).
- testes de síntese (quota-combo-balancing, quota-multiprovider) alinhados: a strategy
  esperada dos combos qtSd/ passa de "fill-first" para "quota-share" (alinhamento ao novo
  comportamento intencional, não mascaramento — os 73 testes de qtSd/ seguem verdes).

* test(quota-share): alinha 2 scope-guards ao godfile sweep (base-reds que bloqueavam o CI)

Dois testes de "arquivo contém X" quebraram por decomposições de godfile que outras
sessões mergearam no release DURANTE a validação de #9 — NÃO são regressão de #9
(que não toca validation/oauth). Alinhados ao novo layout, asserts preservados:
- proxy-bypass-scope-guard #3226: bypassProxyPatch foi extraído de validation.ts para
  validation/headers.ts (split #4921–#4930) → o teste lê a camada de validação.
- sse-error-passthrough #3324: a windsurf authHint foi extraída de providers.ts para
  providers/oauth.ts → o teste lê o novo local.

---------

Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
2026-06-24 11:40:17 -03:00
Diego Rodrigues de Sa e Souza
833adce8fb refactor(api): extrai validators áudio/speech + misc apikey de validation.ts (#4930)
Integrado em release/v3.8.36 (validation.ts split fatia 4 — áudio/speech + misc apikey)
2026-06-24 09:53:36 -03:00
Diego Rodrigues de Sa e Souza
6035fd8ba1 refactor(api): extrai validators enterprise-cloud + probe compartilhado de validation.ts (#4923)
Integrado em release/v3.8.36 (validation.ts split fatia 3 — enterprise-cloud + probe)
2026-06-24 09:52:23 -03:00
Diego Rodrigues de Sa e Souza
1cb246215a refactor(api): extrai validators web-cookie + Meta AI de validation.ts (#4922)
Integrado em release/v3.8.36 (validation.ts split fatia 2 — web-cookie + Meta AI)
2026-06-24 09:51:11 -03:00
Diego Rodrigues de Sa e Souza
21ce4872d2 refactor(api): extrai camada-folha pura de validation.ts (URL/headers/transport) (#4921)
Integrado em release/v3.8.36 (validation.ts split fatia 1 — leaf layer)
2026-06-24 09:49:59 -03:00
Diego Rodrigues de Sa e Souza
c872b6710d refactor(pricing): decompõe pricing.ts em shared-tiers + DEFAULT_PRICING particionado (godfile sweep, #3501) (#4918)
Integrado em release/v3.8.36 (godfile sweep pricing.ts, #3501)
2026-06-24 09:48:46 -03:00
Diego Rodrigues de Sa e Souza
b8e3dc950c refactor(providers): decompõe catálogo providers.ts em módulos de dados (godfile sweep, #3501) (#4917)
Integrado em release/v3.8.36 (godfile sweep providers.ts, #3501)
2026-06-24 09:47:45 -03:00
Diego Rodrigues de Sa e Souza
15ee83b44a feat(quota): buckets multi-janela por conexão (5h/7d/per-model) [Fase 3 #3] (#4928)
Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
2026-06-24 09:35:28 -03:00
Diego Rodrigues de Sa e Souza
0811a15a09 feat(quota): session stickiness p/ integridade de prompt-cache [Fase 3 #5] (#4929)
* feat(quota): session stickiness p/ integridade de prompt-cache [Fase 3 #5]

Adiciona stickiness de sessão ao roteamento de combo: uma conversa multi-turno
é roteada para a MESMA conexão enquanto ela permanecer saudável, evitando a
perda do prompt-cache do provider (custo 5-10× sem stickiness, efeito conhecido
no dario/clewdr).

Implementação:
- `open-sse/services/combo/sessionStickiness.ts` (novo, <800 linhas):
  mapa em memória (messageHash → connectionId) com TTL 15 min + cap 500 entradas;
  `applySessionStickiness` promove a conexão sticky ao índice 0 dos targets
  ordenados pelo strategy, guardado por `computeHeadroom > 0.15` (threshold);
  quando saturada (headroom ≤ 0.15), o binding é limpo e a seleção normal reage.
  Hash da sessão = SHA-256 dos primeiros chars da 1ª mensagem user → 16 hex chars.
  Seam de teste: `__setStickinessHeadroomFetcherForTests`.
- `open-sse/services/combo.ts`: import + 2 pontos de integração (pré-eval-scores
  e pós-success), dentro do orçamento congelado de 3180 linhas.
- `tests/unit/combo-session-stickiness.test.ts`: 19 testes node:test + assert/strict,
  todos via injeção de fetcher (zero rede/DB).

Threshold 0.15: conexão a >85% de utilização está a um burst de rate-limit;
o benefício de cache não compensa manter-se numa conexão degradada. Valor
alinhado com a zona de soft-penalty do restante do engine de quota-share.

* test(combo): isola combo-strategies da session stickiness (#5)

selectedConnectionFor reusa o mesmo body, então o sticky map (#5) fixava a
connection após a 1ª chamada e quebrava o round-robin tie-break do teste
reset-aware. Limpa o sticky map no início da helper — a stickiness tem suíte
própria (combo-session-stickiness). Sem enfraquecer asserts.

---------

Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
2026-06-24 09:23:34 -03:00
Diego Rodrigues de Sa e Souza
98296169ed feat(quota): cap per-(key,model) — quota_allocation_model_caps [Fase 3 #7] (#4927)
* feat(quota): cap per-(key,model) com tabela quota_allocation_model_caps [Fase 3 #7]

Fecha o buraco onde uma API key pode drenar o pool inteiro consumindo um único modelo.

Tabela nova: quota_allocation_model_caps(pool_id, api_key_id, model, cap_value, cap_unit)
PK composta (pool_id, api_key_id, model). cap_unit alinhado ao QuotaUnit existente.

Comportamento: keyA acima do cap para modelo M → bloqueada somente em M; ainda
permitida em qualquer outro modelo no mesmo pool. Cap <= EPSILON → ignorado (seed).

Consumo por-(key,model) usa bucket segregado no quota_consumption existente
(poolId mangled ':model:<model>') com window fixa 'hourly'; nenhuma nova tabela
ou método de store necessário.

Módulo novo: src/lib/db/quotaModelCaps.ts (getModelCap/setModelCap/deleteModelCap/listModelCaps)
enforce.ts ganha o pre-check em enforceQuotaShare + recording em recordConsumption.
EnforceInput e RecordConsumptionInput ganham model?: string (backward-compatible).
localDb.ts re-exporta os 4 helpers (Hard Rule #2).

TDD: tests/unit/quota-per-key-model.test.ts — 4 cenários (bloqueia em M, permite em M2,
sem cap → sem bloqueio, EPSILON → ignorado). Todos os gates de qualidade passam.

* feat(quota): plumba model resolvido no hot path para ativar o per-(key,model) cap [Fase 3 #7]

A tabela/enforce do commit anterior estavam INERTES: o hot path não passava `model`
ao enforce nem ao record, então nenhum model-cap disparava em produção.

Plumbagem (model resolvido = mesma var usada no log/roteamento, pós background-redirect/alias):
- chatCore.ts: enforceQuotaShare ganha `model`; scheduleQuotaShareConsumption recebe `model`.
- chatCore/quotaShareConsumption.ts: threade `model` no RecordConsumptionInput (non-streaming).
- spendRecorder.ts: recordStreamingConsumption já recebia `model` — agora o coloca no
  RecordConsumptionInput (streaming accrue por-modelo).
- embeddings.ts: enforce + record ganham `model`.

Namespace do cap = id do modelo RESOLVIDO (o mesmo de modelForScope/pendingScope/getUnsupportedParams),
não o requestedModel cru nem o finalModelToUpstream (sem prefixo de provider). Operador configura
o cap contra esse id. `model || undefined` em todos os pontos: vazio/null → check pulado (fail-safe,
zero latência — só um campo no objeto).

Teste de integração novo (tests/unit/quota-per-key-model-hotpath.test.ts): prova end-to-end que
N consumos via scheduleQuotaShareConsumption({model}) → enforceQuotaShare({model}) bloqueia, e que
outro modelo no mesmo pool ainda passa; + guard de que enforce SEM model nunca dispara model-cap.

---------

Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
2026-06-24 08:07:42 -03:00
Diego Rodrigues de Sa e Souza
118875e3ed feat(quota): estratégia de combo "headroom" — seleção por folga de cota [Fase 3 #4] (#4908)
Nova estratégia de roteamento que escolhe a conexão com MAIS folga de plano:
headroom = 1 − max(util_5h, util_7d) (técnica do dario), via getSaturation
(melhorado p/ Claude no #1). Proativo em vez de só fill-first reativo.

- Helper PURO headroomRanking.ts (computeHeadroom + rankByHeadroom; saturação
  injetada, não-mutante, tie-break estável, fail-open).
- Orderer async em combo/quotaStrategies.ts (reusa a maquinaria reset-aware de
  expansão de conexões + concorrência limitada; seam injetável).
- Registrada como "headroom" em routingStrategies (combo-only); fill-first segue
  default — nenhuma estratégia existente tocada.
- baseline file-size combo.ts 3168->3180 (só +12L de dispatch; lógica fora do god-file).

16 testes novos + combo-strategies 15/15 = 31/31; typecheck:core + eslint +
file-size limpos.

Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
2026-06-24 07:25:40 -03:00