Skip to content

fix: Incremental view construction#2586

Merged
csmith49 merged 13 commits intomainfrom
fix/incremental-view-construction
Apr 6, 2026
Merged

fix: Incremental view construction#2586
csmith49 merged 13 commits intomainfrom
fix/incremental-view-construction

Conversation

@csmith49
Copy link
Copy Markdown
Collaborator

@csmith49 csmith49 commented Mar 26, 2026

Summary

View objects can be extended by adding new events. View construction simplified to iteratively apply this process.

Other changes:

  • Removing dead code (condensations attribute, unhandled_condensation_request_exists method)
  • Updating tests to account for the new interface
  • Updating enforce_properties to be a method instead of a static method
  • Updating manipulation_indices to no longer be cached (to avoid complicating other functions with cache invalidation logic)

These changes pave the way for more efficient view construction in the conversation state -- by tracking the view explicitly in the state, we can avoid re-computing at every agent step and just focus on updating it with the current events.

This PR is a simpler implementation of the incremental view construction from PR #2141 (now closed).

Checklist

  • If the PR is changing/adding functionality, are there tests to reflect this?
  • If there is an example, have you run the example to make sure that it works?
  • If there are instructions on how to run the code, have you followed the instructions and made sure that it works?
  • If the feature is significant enough to require documentation, is there a PR open on the OpenHands/docs repository with the same branch name?
  • Is the github CI passing?

Agent Server images for this PR

GHCR package: https://github.com/OpenHands/agent-sdk/pkgs/container/agent-server

Variants & Base Images

Variant Architectures Base Image Docs / Tags
java amd64, arm64 eclipse-temurin:17-jdk Link
python amd64, arm64 nikolaik/python-nodejs:python3.13-nodejs22-slim Link
golang amd64, arm64 golang:1.21-bookworm Link

Pull (multi-arch manifest)

# Each variant is a multi-arch manifest supporting both amd64 and arm64
docker pull ghcr.io/openhands/agent-server:19cdf71-python

Run

docker run -it --rm \
  -p 8000:8000 \
  --name agent-server-19cdf71-python \
  ghcr.io/openhands/agent-server:19cdf71-python

All tags pushed for this build

ghcr.io/openhands/agent-server:19cdf71-golang-amd64
ghcr.io/openhands/agent-server:19cdf71-golang_tag_1.21-bookworm-amd64
ghcr.io/openhands/agent-server:19cdf71-golang-arm64
ghcr.io/openhands/agent-server:19cdf71-golang_tag_1.21-bookworm-arm64
ghcr.io/openhands/agent-server:19cdf71-java-amd64
ghcr.io/openhands/agent-server:19cdf71-eclipse-temurin_tag_17-jdk-amd64
ghcr.io/openhands/agent-server:19cdf71-java-arm64
ghcr.io/openhands/agent-server:19cdf71-eclipse-temurin_tag_17-jdk-arm64
ghcr.io/openhands/agent-server:19cdf71-python-amd64
ghcr.io/openhands/agent-server:19cdf71-nikolaik_s_python-nodejs_tag_python3.13-nodejs22-slim-amd64
ghcr.io/openhands/agent-server:19cdf71-python-arm64
ghcr.io/openhands/agent-server:19cdf71-nikolaik_s_python-nodejs_tag_python3.13-nodejs22-slim-arm64
ghcr.io/openhands/agent-server:19cdf71-golang
ghcr.io/openhands/agent-server:19cdf71-java
ghcr.io/openhands/agent-server:19cdf71-python

About Multi-Architecture Support

  • Each variant tag (e.g., 19cdf71-python) is a multi-arch manifest supporting both amd64 and arm64
  • Docker automatically pulls the correct architecture for your platform
  • Individual architecture tags (e.g., 19cdf71-python-amd64) are also available if needed

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 26, 2026

Python API breakage checks — ✅ PASSED

Result:PASSED

Action log

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 26, 2026

REST API breakage checks (OpenAPI) — ✅ PASSED

Result:PASSED

Action log

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 26, 2026

Coverage

Coverage Report •
FileStmtsMissCoverMissing
openhands-sdk/openhands/sdk/context/view
   view.py56198%72
TOTAL21941634371% 

Copy link
Copy Markdown
Collaborator

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

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

🟢 Good taste - Elegant simplification of view construction.

Important: This changes view construction logic which affects what events agents see. Per repo guidelines, flagging for human maintainer review and eval verification before merge. Consider adding the integration-test label.

Summary: The incremental construction approach is cleaner than all-at-once. Dead code removal verified (.condensations and unhandled_condensation_request_exists have zero usage outside tests). Cache removal is necessary for correctness with the new mutable design.

Comment thread openhands-sdk/openhands/sdk/context/view/view.py
Comment thread openhands-sdk/openhands/sdk/context/view/view.py
Comment thread openhands-sdk/openhands/sdk/context/view/view.py
Comment thread openhands-sdk/openhands/sdk/context/view/view.py
Copy link
Copy Markdown

@JiwaniZakir JiwaniZakir left a comment

Choose a reason for hiding this comment

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

The shift from cached_property to property for manipulation_indices is a necessary consequence of making events mutable, but it means the indices are recomputed on every access. If manipulation_indices is called in any hot path (e.g., repeatedly during a context-building loop), this could become a meaningful performance regression depending on how expensive the computation is — worth profiling or at minimum adding a comment explaining why caching was dropped.

More importantly, append_event doesn't call enforce_properties after mutating self.events. This is fine when going through from_events, but the method is public and the PR title implies incremental construction is a first-class use case. Any caller using append_event directly in an incremental fashion would need to remember to call enforce_properties separately to avoid a malformed view — there's no enforcement or documentation of that contract in append_event's docstring. Consider either calling enforce_properties inside append_event (with the full event sequence passed in), or making the method private (_append_event) to signal it's an internal building block.

Removing the condensations field also drops the ability to inspect which condensations were applied to produce a given view, which was useful for debugging. If that observability was intentional to drop, it may be worth a brief comment in the PR noting the trade-off.

@xingyaoww xingyaoww requested a review from all-hands-bot March 30, 2026 15:54
Copy link
Copy Markdown
Collaborator

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

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

🟢 Good taste - Elegant refactoring that simplifies view construction. The incremental append_event approach is more intuitive than all-at-once, dead code removal is clean, and test coverage is comprehensive.

Since this modifies condenser/view construction behavior, recommend adding the integration-test label to verify no unintended behavioral changes before merge (per repo guidelines). No blocking issues found.

@enyst enyst added the integration-test Runs the integration tests and comments the results label Mar 31, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Hi! I started running the integration tests on your PR. You will receive a comment with the results shortly.

@github-actions
Copy link
Copy Markdown
Contributor

🧪 Integration Tests Results

Overall Success Rate: 96.7%
Total Cost: $0.93
Models Tested: 4
Timestamp: 2026-03-31 01:56:57 UTC

📁 Detailed Logs & Artifacts

Click the links below to access detailed agent/LLM logs showing the complete reasoning process for each model. On the GitHub Actions page, scroll down to the 'Artifacts' section to download the logs.

📊 Summary

Model Overall Tests Passed Skipped Total Cost Tokens
litellm_proxy_gemini_3.1_pro_preview 100.0% 8/8 0 8 $0.32 221,742
litellm_proxy_anthropic_claude_sonnet_4_6 87.5% 7/8 0 8 $0.44 259,072
litellm_proxy_moonshot_kimi_k2_thinking 100.0% 7/7 1 8 $0.13 520,960
litellm_proxy_deepseek_deepseek_reasoner 100.0% 7/7 1 8 $0.05 835,677

📋 Detailed Results

litellm_proxy_gemini_3.1_pro_preview

  • Success Rate: 100.0% (8/8)
  • Total Cost: $0.32
  • Token Usage: prompt: 217,947, completion: 3,795, cache_read: 90,041, reasoning: 2,317
  • Run Suffix: litellm_proxy_gemini_3.1_pro_preview_3dc1b51_gemini_3_1_pro_run_N8_20260331_013033

litellm_proxy_anthropic_claude_sonnet_4_6

  • Success Rate: 87.5% (7/8)
  • Total Cost: $0.44
  • Token Usage: prompt: 253,432, completion: 5,640, cache_read: 171,984, cache_write: 81,200, reasoning: 897
  • Run Suffix: litellm_proxy_anthropic_claude_sonnet_4_6_3dc1b51_claude_sonnet_4_6_run_N8_20260331_013032

Failed Tests:

  • t02_add_bash_hello: Shell script is not executable (Cost: $0.05)

litellm_proxy_moonshot_kimi_k2_thinking

  • Success Rate: 100.0% (7/7)
  • Total Cost: $0.13
  • Token Usage: prompt: 512,440, completion: 8,520, cache_read: 451,840
  • Run Suffix: litellm_proxy_moonshot_kimi_k2_thinking_3dc1b51_kimi_k2_thinking_run_N8_20260331_013035
  • Skipped Tests: 1

Skipped Tests:

  • t08_image_file_viewing: This test requires a vision-capable LLM model. Please use a model that supports image input.

litellm_proxy_deepseek_deepseek_reasoner

  • Success Rate: 100.0% (7/7)
  • Total Cost: $0.05
  • Token Usage: prompt: 819,836, completion: 15,841, cache_read: 748,800, reasoning: 5,860
  • Run Suffix: litellm_proxy_deepseek_deepseek_reasoner_3dc1b51_deepseek_v3_2_reasoner_run_N8_20260331_013033
  • Skipped Tests: 1

Skipped Tests:

  • t08_image_file_viewing: This test requires a vision-capable LLM model. Please use a model that supports image input.

@csmith49
Copy link
Copy Markdown
Collaborator Author

csmith49 commented Apr 1, 2026

The shift from cached_property to property for manipulation_indices is a necessary consequence of making events mutable, but it means the indices are recomputed on every access. If manipulation_indices is called in any hot path (e.g., repeatedly during a context-building loop), this could become a meaningful performance regression depending on how expensive the computation is — worth profiling or at minimum adding a comment explaining why caching was dropped.

It's not called repeatedly, it's called once per condenser. And the PR notes why it was dropped, a comment in code referencing old functionality doesn't really make sense.

More importantly, append_event doesn't call enforce_properties after mutating self.events. This is fine when going through from_events, but the method is public and the PR title implies incremental construction is a first-class use case. Any caller using append_event directly in an incremental fashion would need to remember to call enforce_properties separately to avoid a malformed view — there's no enforcement or documentation of that contract in append_event's docstring. Consider either calling enforce_properties inside append_event (with the full event sequence passed in), or making the method private (_append_event) to signal it's an internal building block.

This is a deliberate change. enforce_properties is expensive because it has to iterate through the entire event log, while the manipulation indices preserve the properties inductively. This PR is setting the stage for incrementally maintaining the View object in ConversationState by calling append_event when a new event comes in. See the referenced PR for the full pattern.

Removing the condensations field also drops the ability to inspect which condensations were applied to produce a given view, which was useful for debugging. If that observability was intentional to drop, it may be worth a brief comment in the PR noting the trade-off.

We're removing b/c it was dead code. View objects are ephemeral and not serialized, so I think that loss of observability is purely hypothetical.

Copy link
Copy Markdown
Collaborator

@enyst enyst left a comment

Choose a reason for hiding this comment

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

Thank you!

@csmith49 csmith49 merged commit 8bcb3a3 into main Apr 6, 2026
28 checks passed
@csmith49 csmith49 deleted the fix/incremental-view-construction branch April 6, 2026 16:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

integration-test Runs the integration tests and comments the results

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants