feat: workspace persistence across conversational exchanges via content-parts#145
feat: workspace persistence across conversational exchanges via content-parts#145radu-mocanu wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds a new runtime wrapper to persist a workspace across conversational exchanges by hydrating from the last assistant message’s file content-parts and (on success) emitting workspace files back as content-part attachments.
Changes:
- Introduces
ConversationalWorkspaceRuntimeto hydrate from conversation history and dehydrate workspace files as file content-parts before the final assistantendMessage. - Adds CAS URI helpers (
build_cas_uri,parse_attachment_id) andWorkspaceHydrator.hydrate_filesto support registry-less hydration from history. - Adds comprehensive tests for hydration/dehydration behavior and bumps package version to
0.12.2.
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| uv.lock | Bumps locked package version to 0.12.2. |
| pyproject.toml | Bumps project version to 0.12.2. |
| src/uipath/runtime/workspace/hydrator.py | Adds hydrate_files() to download attachments and build a registry without a preexisting registry. |
| src/uipath/runtime/workspace/conversational.py | Adds ConversationalWorkspaceRuntime that hydrates from history and emits workspace content-parts on success. |
| src/uipath/runtime/workspace/cas_uri.py | Adds helpers to build/parse CAS attachment URIs used by conversational workspace persistence. |
| src/uipath/runtime/workspace/init.py | Exposes the new conversational runtime and CAS URI helpers from the workspace package. |
| src/uipath/runtime/init.py | Re-exports ConversationalWorkspaceRuntime from the top-level runtime package. |
| tests/workspace/test_conversational_workspace.py | Adds tests covering ordering, hydration rules, relinking vs reupload, and disposal behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
359f08f to
ed1ce54
Compare
ed1ce54 to
13bd8fe
Compare
|
| for message in reversed(messages): | ||
| if isinstance(message, UiPathConversationMessage): | ||
| if message.role == "assistant": | ||
| return message | ||
| elif isinstance(message, Mapping) and message.get("role") == "assistant": | ||
| return UiPathConversationMessage.model_validate(message) | ||
| return None |
There was a problem hiding this comment.
quite surprised we have no existing helper for "get last message of type X". Pretty sure we had similar needs for the tool node
| if conversation_event is None: | ||
| yield event | ||
| return |
There was a problem hiding this comment.
nit: there is a technical edge case here, but I'm not sure how much of a real concern it is yet:
Suppose the original event order would be:
- Message Start
- Message End
- some kind of langgraph event not related to the conversation messages
Under the new buffer, message end will get buffered while the non-conversation events will be yielded, so the non-conversational events will get mixed between:
- Message Start
- Other events
- Message End



Summary