fix: gracefully truncate conversation history when context window is …#545
Open
HasanAlsaafen wants to merge 1 commit into
Open
fix: gracefully truncate conversation history when context window is …#545HasanAlsaafen wants to merge 1 commit into
HasanAlsaafen wants to merge 1 commit into
Conversation
e25e0c2 to
6e0db9b
Compare
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.
fix: gracefully truncate conversation history when context window is exceeded
Closes #514
Problem
When a conversation grows long, Rowboat passes the full message history to the
model API unchanged. If the history exceeds the model's context window limit the
API returns a hard
400 bad_request_error(context window exceeds limit) andthe error is surfaced directly to the user with no recovery.
Solution
Add a lightweight
truncateMessagesToFit()utility(
apps/x/packages/core/src/agents/context-utils.ts) that is called insidestreamLlm()— the single callsite that builds the payload sent to the AI SDK —before
convertFromMessages()runs.How it works
are never dropped regardless of length.
the most valuable, so we walk the history newest → oldest and stop when the
token budget is exhausted.
leaves a
tool(tool-result) message at the head of the kept list, it isalso dropped, because the AI SDK requires every tool-result to have a
preceding tool-call message.
ceil(charCount / 4)which is awell-known approximation that intentionally over-estimates to remain
conservative. A tokenizer library would be more precise but adds a
dependency; the heuristic is sufficient for truncation purposes.
Default budget
80,000tokens. This comfortably fits within the context windows of everymodel currently supported by Rowboat (Claude 3.x, GPT-4o, Gemini 1.5, Llama 3,
etc.) while leaving headroom for the system prompt and the model's output.
Files changed
apps/x/packages/core/src/agents/context-utils.tstruncateMessagesToFit()utilityapps/x/packages/core/src/agents/runtime.tsstreamLlm()before sending to AI SDKTesting
The utility has no external dependencies and can be exercised with a simple
Node script: