OpenHands / OpenHands/software-agent-sdk

[Feature]: Derive ConversationInfo from ConversationState + StoredConversation instead of re-declaring fields

Open
#4,849 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

architecture enhancement ready-for-dev typing
Dominant language
Python
Stars
1.1k
Forks
539
Avg merge
1d 19h
Merged PRs (30d)
137

Description

Problem or Use Case

ConversationInfo (the HTTP response shape returned by every conversation endpoint) re-declares ~20 fields that already exist on ConversationState or StoredConversation. The assembly function _compose_conversation_info() merges them with **state.model_dump(mode="json"), but ConversationInfo/_ConversationInfoBase only picks up fields it explicitly declares — any field added to ConversationState that a client needs is silently dropped from the response unless someone also remembers to add it to _ConversationInfoBase.

This happened recently with activated_path_rules: it was added to ConversationState but never added to _ConversationInfoBase, so it is silently absent from every API response. It is harmless today (the field is internal dedup state no client needs), but the pattern means future fields can vanish without error, test failure, or any signal.

Discovered while reviewing #4814 and the surrounding ConversationState / StoredConversation boundary work in #4810 / #4811.

Desired Behavior

Adding a field to ConversationState or StoredConversation should not require a separate, coordinated change to _ConversationInfoBase for the field to appear in API responses. The mapping from source model to wire shape should be explicit and type-checked rather than relying on model_dump + silent Pydantic ignore.

A minimal step would be a typed classmethod constructor on ConversationInfo that takes (state: ConversationState, stored: StoredConversation, ...) and maps fields explicitly — making any missing field a type error rather than a silent drop. A more structural step would be replacing the re-declared fields with typed references to the source models, though that would change the wire shape and require client updates.

Acceptance Criteria
  • Adding a new field to ConversationState that should appear in API responses does not require a separate change to _ConversationInfoBase
  • The mapping from ConversationState / StoredConversation fields to ConversationInfo fields is explicit and surfaced at compile/lint time, not discovered at runtime
Alternatives Considered

Keep the current approach but add a test that asserts every public field on ConversationState either appears in ConversationInfo or is explicitly excluded. This is lower effort but still manual maintenance.

Technical Implementation Ideas (Optional)

The four fields in ConversationInfo that come from neither source model (current_model_id, available_models, supports_runtime_model_switch, sub_conversation_ids) are computed at assembly time from ACP agent runtime state and the server catalog — any solution needs to handle these as explicit extra parameters to the constructor rather than derived from the two source models.

The existing _compose_conversation_info(stored, state, ...) function is already the right shape; the issue is that ConversationInfo is a flat re-declaration rather than being structurally tied to its sources.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at _compose_conversation_info(stored, state, ...) and read ConversationInfo, _ConversationInfoBase, ConversationState, and StoredConversation together. Trace how the four computed fields are supplied from ACP runtime state and the server catalog. Done means source-model fields are mapped explicitly and new response fields cannot be silently omitted without a compile- or lint-time signal.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend-api-design
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.