Repository navigation
Conversation
Every generation serialized the full session history (earlier turns plus earlier steps of the current turn) onto its span, so memory grew quadratically with session length and long sessions crashed the Stop hook with a V8 heap OOM or an oversized export request. Keep only the most recent messages that fit in max_history_chars (default 200000, 0 disables) and replace the rest with a single omitted-messages marker. Messages are never cut, and tool spans keep their full output. Co-authored-by: Hassieb Pakzad <hassiebp@users.noreply.github.com>
The Stop hook converted every pending turn, flushed once, and only then marked turns in the sidecar. A backlog therefore sat in memory all at once and was delivered all-or-nothing: any crash, timeout or failed export left every turn unmarked, so the next hook retried an even larger backlog. Flush after each turn and mark it as soon as its export succeeds, so a backlog drains incrementally and only one turn's spans are held at a time. Co-authored-by: Hassieb Pakzad <hassiebp@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes LFE-17701. Closes #119.
Problem
On long Codex sessions the Stop hook aborts with a V8 heap OOM, and from then on none of that session's turns reach Langfuse.
Every generation's input is the full conversation so far: earlier turns plus earlier steps of the current turn, tool outputs included. Each input is
JSON.stringify'd onto its span, so memory grows roughly quadratically with session length. All pending turns are converted before a single flush at shutdown, and only then are turns marked in the sidecar.That gives three ways to fail, each of which leaves every turn unmarked and the next hook with an even bigger backlog:
abort()that the hook's try/catch can't catch.RangeError: Invalid string length: the batched OTLP request body passes V8's ~512 MB string limit. This happens even without an OOM.forceFlushwaits out the 30 s export timeout, which is over the 30 s Codex hook timeout.Changes
1. Bound the conversation attached to each generation (
max_history_chars, default200000,0disables).[N earlier message(s) omitted …]system marker.langfuse.jsonorLANGFUSE_CODEX_MAX_HISTORY_CHARS. Invalid values fall back to the default without discarding the rest of the config file.2. Flush and mark each turn before converting the next.
convertRollouttakes anonTurnExportedcallback. The hook uses it toflush()and thenmarkTurnUploadedper turn.Measurements
Built hook, synthetic rollouts, local sink that accepts uploads,
--max-old-space-size=1536:Invalid string length, 33 s, nothing uploadedInvalid string length, 36 s, nothing uploadedTests
config.test.ts: the default value, reading from the file and from env, and invalid values being ignored without dropping the file.trace.test.ts: the budget covering earlier turns and current-turn steps, the exact marker text, and0meaning no cap.hook-command.test.ts: the end-to-end hook delivers turn 1, the sink rejects turn 2, and the sidecar contains onlyturn-1.mainand pass here.pnpm lintandpnpm test(125 tests) pass.Linear Issue: LFE-17701