Skip to content

feat(adk): honour spec.declarative.context.compaction in the Go runtime (backport to release/v0.10.x) - #2791

Open
teemow wants to merge 2 commits into
kagent-dev:release/v0.10.xfrom
teemow:feat/go-runtime-compaction-v0.10.x
Open

feat(adk): honour spec.declarative.context.compaction in the Go runtime (backport to release/v0.10.x)#2791
teemow wants to merge 2 commits into
kagent-dev:release/v0.10.xfrom
teemow:feat/go-runtime-compaction-v0.10.x

Conversation

@teemow

@teemow teemow commented Sep 10, 2026

Copy link
Copy Markdown

Backport of #2790 to release/v0.10.x. This is the branch where the field exists on the Agent CRD and the reconciler flagged it, so it carries two things main cannot: the reconciler no longer reports compaction as unsupported on the Go runtime, and the CRD description is corrected.

Problem

As of 0.10.0 the Agent CRD defaults spec.declarative.runtime to go, and the Go runtime never read spec.declarative.context.compaction. The controller compiled the field into the agent's config.json (context_config.compaction, translator/agent/compiler.go) and the Python runtime consumed it, but a Go agent grew its prompt with every turn regardless of what the operator configured; the only signal was the UnsupportedFeatures condition (#2728). Measured on an internal installation running 0.10.1: an agent session grew from 4k to 190k input tokens per model call over 119 calls, and every Go agent there ran without compaction because the runtime ignored the field.

google.golang.org/adk/v2 v2.3.0 (2026-08-31) ships native compaction (session/compaction: sliding window, tail retention, LLM summarizer) and the runner.Config.Compaction hook, so the runtime consumes that instead of carrying its own port of the algorithm, which is what #1971 and #2025 attempted before the primitives existed.

Change

  • Dependency: google.golang.org/adk/v2 v2.1.0 → v2.3.0. The OpenTelemetry exporters and contrib instrumentation move to the matching v1.45.0 / v0.21.0 / v0.70.0 set, which the new ADK version requires for otel/log. go get google.golang.org/adk/v2@v2.3.0 also raises the direct requirements that ADK v2.3.0 pins higher than this branch had (minimum version selection, nothing bumped by hand): github.com/a2aproject/a2a-go/v2 v2.3.1 → v2.5.0, github.com/modelcontextprotocol/go-sdk v1.6.1 → v1.7.0, github.com/openai/openai-go/v3 v3.46.0 → v3.52.0, google.golang.org/genai v1.65.0 → v1.69.0, google.golang.org/protobuf → v1.36.12, and through openai-go v3.52.0 github.com/aws/aws-sdk-go-v2/config v1.32.31 → v1.32.35 (go mod graph names ADK v2.3.0 as the requirer of each). main is already at these versions, which is why feat(adk): honour context compaction in the Go runtime #2790 shows only the ADK and OpenTelemetry hunks. No kagent code needed changes for the bump; the v2.2.0 and v2.3.0 release notes carry no breaking changes for the packages kagent imports, and the full make -C go test passes on the branch.
  • Runtime: agent.CompactionConfig translates context_config.compaction into a compaction.Config and runner.CreateRunnerConfig hands it to the ADK runner. compactionInterval / overlapSize drive the sliding window, tokenThreshold / eventRetentionSize tail retention; either strategy is off when its settings are unset, so an agent without context.compaction runs exactly as before.
  • Half-configured settings are ignored with a warning rather than rejected: tokenThreshold without eventRetentionSize (and the reverse), overlapSize without an interval. That mirrors the Python runtime, where adk-python runs tail retention only when both settings are present. What remains is validated at startup with the runner's own rules, so a configuration the runner would refuse (negative values, no strategy at all) fails fast with the reason instead of on the first turn.
  • Summarizer: summarizer.modelConfig builds a compaction.LLMSummarizer through the same model factory as the agent (CreateLLM), with the model's generation config and a 60 s bound per call (the same bound the runner applies to its default summarizer). summarizer.promptTemplate gets the tool-name instruction the Python runtime appends (fix(compaction): preserve namespaced tool names in LLM summarizer prompt #1907), so summaries keep namespaced tool names; a template without a summarizer model runs on the agent's own model. With neither, the runner summarizes with the agent's model and ADK's default prompt, matching the Python runtime.
  • Observability: startup logs Context compaction enabled with the resolved values and the summarizer model, or Context compaction not configured, so an operator can see from the agent log what the runtime does with the field.
  • Controller: validateRuntimeFeatures drops the "context compression/compaction (not implemented in Go runtime)" entry; executeCodeBlocks is still reported.
  • CRD: the summarizer description claimed compacted events are dropped without summarization; on both runtimes the agent's own model summarizes by default. tokenThreshold documents that it takes effect together with eventRetentionSize, which both runtimes require. Regenerated with make controller-manifests.

Compaction records travel on EventActions.Compaction; both the controller-backed session service (json.Marshal of the whole event) and the local DurableDir store (ADK's session/database) persist Actions as a whole, so a summary survives a restart and the same range is not summarized again.

Testing

  • go test ./adk/pkg/agent/: TestCompactionStrategies (table: each strategy alone and combined, unset settings, the three half-configured cases, no strategy, negative values), TestCompactionConfig (not configured → nil; default summarizer; dedicated summarizer model; custom prompt on the agent model; prompt without {conversation_history} rejected; prompt without any model rejected; the context_config block as the controller compiles it), TestCompactionSlidingWindowFiresAfterInterval (a scripted model drives the ADK runner: no record after turn 1, one record after turn 2 whose content is the summarizer's reply, and turn 3's request shows the summary and none of the compacted turns), TestCompactionTailRetentionFiresOverTokenThreshold.
  • go test ./core/internal/controller/reconciler/: TestValidateRuntimeFeatures (compaction on go / default / python raises nothing; executeCodeBlocks still does, without mentioning compaction).
  • make -C go test: passes (52 packages, -race).
  • Lint: the full make -C go lint crashes on this branch inside staticcheck's buildir while analysing the standard library package internal/poll with the local Go 1.27 toolchain, independent of this change; with staticcheck and unused disabled the touched packages (adk/pkg/agent, adk/pkg/runner, core/internal/controller/reconciler, api/v1alpha2) report 0 issues. CI runs the full linter.

End-to-end (kind, images from this branch, real model)

kagent 0.10.1 chart in a kind cluster with the controller and golang-adk images built from this branch; default-model-config is Anthropic claude-haiku-4-5. Two identical Go agents with the k8s tools (k8s_get_resources, k8s_get_resource_yaml, k8s_describe_resource, k8s_get_events), one with

context:
  compaction:
    compactionInterval: 2
    overlapSize: 0
    summarizer:
      modelConfig: summarizer-model   # a second Anthropic ModelConfig

and one without. Both were driven through the same five-turn conversation over the controller's A2A endpoint with a fixed contextId (list all pods, the controller deployment YAML, describe the controller pod and its events, the services of the namespace, then a recap "without calling any tool"). prompt_token_count per model call as the runtime reports it in adk_usage_metadata:

turn model calls (first → last) without compaction with compaction
1 list pods 1,273 → 2,589 1,275 → 2,605
2 deployment YAML 3,228 → 4,825 3,191 → 4,802
compaction 1: turns 1–2 → summary of 1,971 chars
3 describe pod + events 5,409 → 46,720 1,815 → 43,142
4 services 48,121 → 48,707 44,303 → 44,889
compaction 2: turns 3–4 → summary of 2,220 chars
5 recap, no tool 50,247 2,524

(The jump in turn 3 is the describe-plus-events tool result, about 41k tokens on both agents.) The two EventActions.Compaction records are stored in the session and come back through GET /api/sessions/{id}, each covering exactly the two invocations of its window. The summaries were written by the dedicated summarizer ModelConfig and carry the "Durable facts" section of ADK's default prompt: controller image and digest, port 8083/TCP, probes, resource limits, the LoadBalancer comparison. The recap in turn 5 answered from the summaries with the controller image, digest, port and service comparison intact; the per-pod list from turn 1 came back as a count per namespace, which is the recall trade-off ADK documents for summaries.

Before the upgrade the 0.10.1 controller reported on the same Agent:

UnsupportedFeatures=True UnsupportedFeatures: The following features are not supported in Go runtime and will be ignored: context compression/compaction (not implemented in Go runtime). Consider using runtime: python or removing these configurations.

After the upgrade the condition is gone (Accepted=True, Ready=True), and the agent log shows at startup

{"msg":"Context compaction enabled","compaction_interval":2,"overlap_size":0,"token_threshold":0,"event_retention_size":0,"summarizer_model":"anthropic","custom_prompt_template":false}

and Context compaction not configured for the agent without the field. The tokenThreshold / eventRetentionSize strategy was not exercised end to end; it is covered by TestCompactionTailRetentionFiresOverTokenThreshold.

Closes #2728.

ADK v2.3.0 ships native context compaction (session/compaction: sliding
window, tail retention, LLM summarizer) and the runner hook that applies
it. The OpenTelemetry exporters and contrib instrumentation move to the
matching v1.45.0 / v0.21.0 / v0.70.0 set, which the new ADK version
already requires for otel/log.

Signed-off-by: Timo Derstappen <teemow@gmail.com>
The controller compiles spec.declarative.context.compaction into the
agent's config.json (context_config.compaction) and the Python runtime
consumes it, but the Go runtime never read it: a Go agent, the CRD
default since 0.10.0, grew its prompt with every turn no matter what the
operator configured, and the only signal was the UnsupportedFeatures
condition.

The runner now receives a compaction.Config derived from the agent
config:

- compactionInterval and overlapSize drive ADK's sliding window,
  tokenThreshold and eventRetentionSize its tail retention. Either
  strategy is off when its settings are unset, so an agent without
  context.compaction runs exactly as before.
- A setting whose partner is missing (tokenThreshold without
  eventRetentionSize, and the reverse; overlapSize without an interval)
  is ignored with a warning, the way the Python runtime treats a
  half-configured tail retention. What remains is validated at startup
  with the runner's own rules, so a configuration the runner would
  refuse fails fast with the reason instead of on the first turn.
- summarizer.modelConfig builds an LLM summarizer through the same model
  factory as the agent, with the model's generation config and a 60s
  bound per call. summarizer.promptTemplate gets the tool-name
  instruction the Python runtime appends, so summaries keep namespaced
  tool names; a template without a summarizer model runs on the agent's
  own model. Without either, the runner summarizes with the agent's
  model and ADK's default prompt.
- Startup logs whether compaction is configured and with which values,
  so an operator can see it from the agent's log.

Compaction records travel on EventActions.Compaction, which both the
controller-backed and the local session store persist as a whole, so a
summary survives a restart.

The reconciler no longer reports context compaction as unsupported on
the Go runtime, and the CRD description no longer claims that compacted
events are dropped without a summarizer: on both runtimes the agent's
own model summarizes by default. tokenThreshold documents that it takes
effect together with eventRetentionSize, which both runtimes require.

Signed-off-by: Timo Derstappen <teemow@gmail.com>

Copilot AI 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.

🟡 Changes recommended

The backport updates additional direct dependencies beyond what the PR description scopes/justifies, which should be confirmed as required (or reduced) to keep release-branch risk controlled.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Backports Go-runtime support for spec.declarative.context.compaction to release/v0.10.x, wiring the Agent CRD’s compaction settings through to the Google ADK v2 runner so Go declarative agents can compact session history (and the controller no longer reports compaction as unsupported).

Changes:

  • Upgrade google.golang.org/adk/v2 to v2.3.0 and align related telemetry dependencies.
  • Add Go runtime translation of context_config.compaction into ADK runner Compaction configuration (incl. optional summarizer model/prompt handling) plus unit tests.
  • Update controller feature validation and CRD text to reflect compaction support and correct summarizer/token-threshold documentation.
File summaries
File Description
helm/kagent-crds/templates/kagent.dev_sandboxagents.yaml CRD helm template doc fixes for compaction summarizer + tokenThreshold semantics.
helm/kagent-crds/templates/kagent.dev_agents.yaml CRD helm template doc fixes for compaction summarizer + tokenThreshold semantics.
go/go.mod Bumps ADK + OTel deps; also bumps several other direct deps.
go/go.sum Updated checksums for the dependency upgrades.
go/core/internal/controller/reconciler/runtime_features_test.go Adds coverage to ensure compaction is no longer flagged unsupported for Go runtime.
go/core/internal/controller/reconciler/reconciler.go Stops reporting context compaction as unsupported for Go declarative agents.
go/api/v1alpha2/agent_types.go Updates CRD field comments for summarizer default + tokenThreshold/eventRetentionSize coupling.
go/api/config/crd/bases/kagent.dev_sandboxagents.yaml Regenerated CRD base with updated compaction docs.
go/api/config/crd/bases/kagent.dev_agents.yaml Regenerated CRD base with updated compaction docs.
go/adk/pkg/runner/adapter.go Passes computed compaction config into ADK runner config.
go/adk/pkg/README.md Updates package overview to mention context compaction wiring.
go/adk/pkg/agent/compaction.go Implements translation from agent config compaction settings to ADK compaction config + summarizer creation/logging.
go/adk/pkg/agent/compaction_test.go Adds unit tests for strategy mapping and runner behavior with compaction enabled.
Review details
  • Files reviewed: 12/13 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread go/go.mod
Comment on lines 10 to +14
github.com/a2aproject/a2a-go v0.3.15
github.com/a2aproject/a2a-go/v2 v2.3.1
github.com/a2aproject/a2a-go/v2 v2.5.0
github.com/abiosoft/ishell/v2 v2.0.2
github.com/anthropics/anthropic-sdk-go v1.61.0
github.com/aws/aws-sdk-go-v2/config v1.32.31
github.com/aws/aws-sdk-go-v2/config v1.32.35

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

These are not hand-picked bumps: go get google.golang.org/adk/v2@v2.3.0 raised them to the minimum versions ADK v2.3.0 requires (MVS), and go mod tidy kept them. From go mod graph on this branch:

  • github.com/a2aproject/a2a-go/v2@v2.5.0 ← required by google.golang.org/adk/v2@v2.3.0 (its go.mod: require github.com/a2aproject/a2a-go/v2 v2.5.0)
  • github.com/aws/aws-sdk-go-v2/config@v1.32.35 ← required by github.com/openai/openai-go/v3@v3.52.0, which ADK v2.3.0 requires

Reverting either would put the module below what ADK v2.3.0 needs and go mod tidy would raise it again. main did not show these hunks only because its go.mod was already at these versions. I have listed the full set with the reason in the PR description under "Dependency".

Comment thread go/go.mod
Comment on lines 29 to +33
github.com/kagent-dev/kmcp v0.3.0
github.com/kagent-dev/mockllm v0.0.5
github.com/modelcontextprotocol/go-sdk v1.6.1
github.com/modelcontextprotocol/go-sdk v1.7.0
github.com/muesli/reflow v0.3.0
github.com/openai/openai-go/v3 v3.46.0
github.com/openai/openai-go/v3 v3.52.0

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Same cause as above: google.golang.org/adk/v2@v2.3.0 requires github.com/modelcontextprotocol/go-sdk v1.7.0 and github.com/openai/openai-go/v3 v3.52.0 directly (both listed in its go.mod; v2.1.0 required v1.4.1 and v3.8.1), plus google.golang.org/genai v1.69.0 and google.golang.org/protobuf v1.36.12. They cannot be reverted while staying on ADK v2.3.0, which is the version that ships the compaction primitives this change consumes. Documented in the PR description now.

@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Sep 10, 2026

@EItanya EItanya 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.

Same comment as the branch on main, can you add an e2e test with mockllm as the summarizer?

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants