Why
The LocalConversation.fork() method (introduced in #2841) contains inline logic for deep-copying conversation state fields (events, agent_state, activated_knowledge_skills, stats, tags). Each field requires different copy semantics:
- events: deep-copied via
event.model_copy(deep=True) (mutable Pydantic models)
- agent_state: deep-copied via
copy.deepcopy() (arbitrary mutable values)
- activated_knowledge_skills: shallow list copy (immutable
str elements)
- stats:
model_copy(deep=True) (Pydantic model)
- tags: shallow dict copy (immutable
str keys/values)
This knowledge is currently embedded inside fork(). As more operations need to clone or snapshot state (e.g., checkpointing, branching, replay), this logic would be duplicated.
Proposal
Add a copy() (or snapshot()) method on ConversationState that encapsulates the field-by-field copy semantics and returns a new ConversationState with its own identity and independent mutable state. The method should:
- Accept an optional new
ConversationID
- Deep-copy events,
agent_state, and stats
- Shallow-copy immutable-element collections (
activated_knowledge_skills, tags)
- Handle private attributes (
_fs, _events, _lock) appropriately for the new instance
- Optionally accept
reset_metrics=True to start fresh stats
This would let fork() delegate to state.copy(...) instead of managing the details inline.
Context
Raised during review of #2841 by @xingyaoww: #2841 (comment)
This issue was created by an AI assistant (OpenHands) on behalf of the user.
Why
The
LocalConversation.fork()method (introduced in #2841) contains inline logic for deep-copying conversation state fields (events,agent_state,activated_knowledge_skills,stats,tags). Each field requires different copy semantics:event.model_copy(deep=True)(mutable Pydantic models)copy.deepcopy()(arbitrary mutable values)strelements)model_copy(deep=True)(Pydantic model)strkeys/values)This knowledge is currently embedded inside
fork(). As more operations need to clone or snapshot state (e.g., checkpointing, branching, replay), this logic would be duplicated.Proposal
Add a
copy()(orsnapshot()) method onConversationStatethat encapsulates the field-by-field copy semantics and returns a newConversationStatewith its own identity and independent mutable state. The method should:ConversationIDagent_state, andstatsactivated_knowledge_skills,tags)_fs,_events,_lock) appropriately for the new instancereset_metrics=Trueto start fresh statsThis would let
fork()delegate tostate.copy(...)instead of managing the details inline.Context
Raised during review of #2841 by @xingyaoww: #2841 (comment)
This issue was created by an AI assistant (OpenHands) on behalf of the user.