Docs: supported no-trimming mode for app-owned compaction and prompt-cache costs · Issue #7766 · CopilotKit/CopilotKit · GitHub
Skip to content

Docs: supported no-trimming mode for app-owned compaction and prompt-cache costs #7766

Open

Description

@MartinLoeper

Clarification requested

What is the supported way to let an application own conversation compaction completely, without OpenAIAdapter silently trimming earlier messages? In newer releases, is maxInputTokens: Infinity an intentional, supported configuration, or merely an implementation detail? Could the adapter documentation include this use case and its prompt-cache implications?

Our production app is pinned to @copilotkit/runtime, react-core, and react-ui 1.10.6. We initially worked around the unconditional cutoff locally. Before filing, we researched the newer implementation and found that a configurable limit already exists. This is not another request to add maxInputTokens.

Prior research and existing explanation

The code and adapter docs explain trimming as a way to fit the model context window. The historical token-counting change replaced tokenization with text.length / 3 for performance. We understand the default overflow-protection goal; the missing guidance is how an application with its own context budgeting and compaction opts out reliably.

At inspected main commit cf326fab12f1738f95ba23553634b9ce6fb73227, the constructor exposes maxInputTokens and passes it to the limiter. The limiter still uses a 128,000 fallback for unrecognized model IDs. forwardedParameters.maxTokens controls output (max_completion_tokens), not this input budget.

Why we flagged this: long-session input costs

Our app uses OpenRouter through the OpenAI-compatible adapter and already has an explicit compaction flow that replaces history with a handover. Between compactions, preserving the conversation prefix is important for provider prompt caching.

Once the stored conversation grew beyond the adapter's approximate 128k budget, dropping the oldest turns changed the prefix repeatedly. The outgoing request could remain around 125k tokens, but much less of it was cached. Representative recorded requests from one session, on the same DeepSeek model and Together provider:

Request Input tokens Cached input Cache hit rate Reported input cost (USD, excludes output)
Warm history 125,111 122,752 98.1% $0.001444212
Later prefix miss 125,969 39,552 31.4% $0.026162412

That is roughly 18 times the input charge for almost the same input size, making continued long sessions unexpectedly expensive. Across the session, chat had an 87.5% token-weighted cache rate; a separate sandbox conversation bypassing this adapter achieved 97.4%.

These are observations, not a controlled provider benchmark or a claim that every miss comes from trimming. We verified the local cutoff mechanism and its removal separately. We excluded an isolated 0% request after a long idle gap from this comparison. Together's caching documentation explains why truncating or rewriting earlier history invalidates cache reuse from that point onward.

Small local reproduction / configuration check

Exercising the current limiter with an unrecognized model ID and this synthetic history:

const messages = [
  { role: "system", content: "policy" },
  { role: "user", content: "oldest instruction" },
  { role: "assistant", content: "x".repeat(500_000) },
  { role: "user", content: "newest instruction" },
];
  • Default limit: only the system and newest user message remain.
  • 0: same default cutoff, not an opt-out.
  • Infinity: all four messages remain.
  • A sufficiently large finite override also preserves these messages, but eventually starts trimming again.

This suggests the following configuration can replace our patch after an upgrade, but we would appreciate confirmation that it is supported:

new OpenAIAdapter({ openai, model, maxInputTokens: Infinity });

Our temporary workaround on 1.10.6

We remove only the OpenAI adapter's limiting call:

- openaiMessages = limitMessagesToTokenCount(openaiMessages, tools, model);
+ // Preserve history; the application owns compaction.

A version-checked, idempotent pre-dev/pre-build script applies this to the published CommonJS and ESM adapter copies, validates both before writing, and fails if the expected version or code shape changes. It leaves upstream message conversion, tool-result filtering, and streaming intact. This is a compatibility workaround for our old pin, not a recommendation that current users patch dependencies.

We tested the actual runtime with history, system prompts, and schemas beyond the old limit, plus tool pairs and images. An isolated Nix-packaged app smoke test also sent the full oversized synthetic history to a local mock OpenRouter and streamed the reply back. No paid inference was needed for those tests. Provider context-limit errors remain visible; our app's compaction handles reducing the conversation.

Requested documentation / guidance

  1. Is maxInputTokens: Infinity a supported no-trimming mode for applications that own compaction? If so, please document and regression-test it; otherwise, what is the supported alternative?
  2. Is automatic trimming intended strictly as a default overflow guard, and should app-owned compaction bypass it rather than set a larger finite estimate?
  3. Could the docs warn that repeated removal of early turns can substantially increase input costs by invalidating prompt caches, even while the visible transcript still contains those turns?
  4. If v1 deprecation changes the recommended solution, please point to the v2 configuration/adapter path that preserves application-managed history without an implicit cutoff.

Activity

  1. copilotkit-outpost-bot commented on Oct 12, 2026

    @copilotkit-outpost-bot

    ⚠️ This is an AI-generated response. A member of our team will review and follow up if needed.

    maxInputTokens: Infinity passes through the limiter without trimming based on how the code is structured, but whether it is an intentional, supported contract versus an implementation side-effect is a question only the maintainers can answer definitively. Engineering will need to confirm that and decide whether to document it, add a regression test, and add the prompt-cache cost warning you're describing.

    What the code does confirm:

    • maxInputTokens was added in 1.56.1 and is wired directly into limitMessagesToTokenCount via this.maxInputTokens (openai-adapter.ts, utils.ts).
    • Unrecognized model IDs (including OpenRouter slugs) fall back to DEFAULT_MAX_TOKENS = 128000 when no maxInputTokens override is supplied (utils.ts line 170).
    • forwardedParameters.maxTokens maps to max_completion_tokens (output), not the input budget — your reading is correct.
    • The docs page for OpenAIAdapter lists maxInputTokens as a constructor parameter but does not document a no-trimming mode or prompt-cache implications (docs.copilotkit.ai/reference/v1/classes/llm-adapters/OpenAIAdapter).

    Your four documentation questions — supported contract for Infinity, intent of trimming as a default-only guard, prompt-cache cost warning, and the v2 equivalent path — are exactly the right questions and the team is picking this up.


    🤖 Generated by CopilotKit AI Support · Documentation


    Was this helpful? React with 👍 or 👎

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions