Skip to content

refactor(sse): split chatCore.ts pure helpers into chatCore/ modules (−561 LOC) - #4159

Merged
diegosouzapw merged 1 commit into
release/v3.8.29from
refactor/chatcore-split-pure-helpers
Jun 18, 2026
Merged

diegosouzapw merged 1 commit into
release/v3.8.29from
refactor/chatcore-split-pure-helpers

Conversation

@diegosouzapw

Copy link
Copy Markdown
Owner

Objetivo

Encolhimento estrutural do maior god-file de hot-path do open-sse/: chatCore.ts 5980 → 5419 LOC (−561), extraindo 6 grupos coesos de helpers internos puros para módulos focados em open-sse/handlers/chatCore/, continuando o padrão de modularização #3598/#3821 (que já criou idempotency.ts/sanitization.ts/semanticCache.ts/memorySkillsInjection.ts).

Zero mudança de comportamento — todos os corpos foram movidos verbatim; as únicas edições são export, ajuste de path de import, remoção das defs + import-back, e 1 comentário.

Módulos novos (cada um com teste de seam dedicado)

Módulo Funções Teste de seam
chatCore/logTruncation.ts capMemoryExtractionText, truncateChatLogText, cloneBoundedChatLogPayload, truncateForLog chatcore-log-truncation.test.ts
chatCore/memoryExtraction.ts extractMemoryTextFromResponse/RequestBody, resolveMemoryOwnerId chatcore-memory-extraction.test.ts
chatCore/passthroughToolNames.ts build/restore/merge ClaudePassthroughToolNames chatcore-passthrough-tool-names.test.ts
chatCore/headers.ts getHeaderValueCaseInsensitive (compartilhado — evita ciclo) chatcore-headers.test.ts
chatCore/nonStreamingSse.ts parse/convertNDJSON/normalize/terminal-detection SSE (8 fns) chatcore-non-streaming-sse.test.ts
chatCore/upstreamTimeouts.ts create*Error, readStreamChunkWithTimeout, computeBillableTokens, getExecutorTimeoutMs, normalizeExecutorResult, executeWithUpstreamStartTimeout chatcore-upstream-timeouts.test.ts

A lição #3821 (a extração #3598 shipou um ReferenceError que nenhum teste pegou) motivou o teste de seam por módulo — incluindo cobertura das 2 funções mais complexas do nonStreamingSse (appendNonStreamingSseTerminalSignal stateful + parseNonStreamingSSEPayload).

Protocolo lossless (por commit)

Move-not-copy + git diff = só relocação + grep de símbolos + typecheck + lint + check:cycles + file-size encolhe (ratcheado p/ baixo). Nenhum módulo importa de volta de chatCore.ts (sem ciclo).

Verificação

  • typecheck:core limpo · lint 0 errors · check:cycles OK · file-size sem --update PASSA (chatCore não cresceu, baseline ratcheado 5980→5419)
  • test:unit → ℹ fail 0 (1 cancel de quota-spend-recorder = race do --test-force-exit sob carga, arquivo não-relacionado, passa isolado 5/5)
  • test:vitest → 187/187
  • 19 testes de seam (6 arquivos novos) verdes

Nota sobre #3501

Vários comentários do file-size-baseline.json adiavam "structural shrink of chatCore.ts tracked in #3501" — mas #3501 era o épico do providers/[id]/page.tsx (ProviderDetailPageClient.tsx), concluído (META ≤800, fase 1t) e fechado; nunca cobriu o chatCore. Este PR é esse shrink adiado; um comentário novo no baseline registra isso (sem reescrever os históricos).

Escopo / próximos

Fora deste PR (sub-projetos futuros): decompor a orquestração do handleChatCore; split do combo.ts; curadoria da lista mutate do Stryker.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors chatCore.ts by extracting several helper functions into dedicated modules under open-sse/handlers/chatCore/ and adding corresponding unit tests under tests/unit/. The review feedback identifies a potential memory leak in upstreamTimeouts.ts where an anonymous event listener registered on signal inside abortPromise is not cleaned up after the promise race completes, and provides a suggestion to remove it in the finally block.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +145 to +157
const abortPromise = new Promise<never>((_, reject) => {
signal.addEventListener("abort", () => reject(createAbortError(signal)), { once: true });
});

try {
return await Promise.race([execute(combinedController.signal), timeoutPromise, abortPromise]);
} finally {
if (timeoutId) clearTimeout(timeoutId);
if (abortListener) signal.removeEventListener("abort", abortListener);
if (timeoutAbortListener) {
timeoutController.signal.removeEventListener("abort", timeoutAbortListener);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The anonymous event listener registered on signal inside abortPromise is never removed when the promise race completes. If signal is a long-lived object or if many requests share/re-use signals, this will cause a memory leak.

To prevent this, store a reference to the listener function and remove it in the finally block, similar to how abortListener and timeoutAbortListener are cleaned up.

  let abortPromiseListener: (() => void) | null = null;
  const abortPromise = new Promise<never>((_, reject) => {
    abortPromiseListener = () => reject(createAbortError(signal));
    signal.addEventListener("abort", abortPromiseListener, { once: true });
  });

  try {
    return await Promise.race([execute(combinedController.signal), timeoutPromise, abortPromise]);
  } finally {
    if (timeoutId) clearTimeout(timeoutId);
    if (abortListener) signal.removeEventListener("abort", abortListener);
    if (abortPromiseListener) signal.removeEventListener("abort", abortPromiseListener);
    if (timeoutAbortListener) {
      timeoutController.signal.removeEventListener("abort", timeoutAbortListener);
    }
  }

…(−561 LOC)

Extracts the pure, side-effect-free helpers out of the chatCore.ts god-file
into focused open-sse/handlers/chatCore/ modules (headers, logTruncation,
memoryExtraction, nonStreamingSse, passthroughToolNames, upstreamTimeouts),
each with its own unit test. Behavior-preserving — chatCore.ts re-imports them.

Re-synced cleanly onto release/v3.8.29: the prior branch tip carried a
stale-merge regression that re-bloated open-sse/config/freeModelCatalog.data.ts
(462 -> 4530 lines) and reverted the free-tier refresh + provider/docs changes.
This commit keeps ONLY the chatCore split (zero contamination).
@diegosouzapw
diegosouzapw force-pushed the refactor/chatcore-split-pure-helpers branch from 15f591f to c2e6b61 Compare June 18, 2026 13:19
@diegosouzapw
diegosouzapw merged commit 305474d into release/v3.8.29 Jun 18, 2026
4 checks passed
@diegosouzapw
diegosouzapw deleted the refactor/chatcore-split-pure-helpers branch June 19, 2026 12:07
tkgo11 pushed a commit to tkgo11/OmniRoute that referenced this pull request Sep 23, 2026
…(−561 LOC) (diegosouzapw#4159)

Integrated into release/v3.8.29 (re-synced: kept only the chatCore split, dropped the stale-merge contamination)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant