Python Client
A Python port of the TypeScript @vuer-ai/vuer-rtc library. Multiple clients can concurrently edit a shared scene graph and all changes converge automatically.
Install
Quick Start
Two-Client Example
Operations
Every operation is a dict with ot, key, path, and typically value. The Python client supports all the same operations as the TypeScript client.
Number
Vector3 / Quaternion / Euler
String / Boolean / Color
Array / Object
Node (Scene Graph Structure)
Text (Collaborative CRDT)
Important: Use text.replace instead of separate text.delete + text.insert when replacing a selection. The edit buffer deduplicates by key:path, so a delete followed by an insert on the same key and path will lose the delete.
Undo / Redo
Undo/redo is journal-based. Each undo() marks a journal entry as deleted and replays the remaining entries. This works correctly across concurrent edits from multiple clients.
Edit Buffer
Edits are buffered until commit(). Additive operations on the same key:path are merged automatically:
Use store.cancel() to discard uncommitted edits and revert to the pre-edit graph.
Receiving Remote Messages
Duplicate messages are automatically ignored (idempotent).
Retry and Compaction
API Reference
GraphStore
| Method | Description |
|---|---|
edit(op) | Add operation to edit buffer (optimistic apply) |
commit(description?) | Commit edits as a single CRDTMessage |
cancel() | Discard uncommitted edits |
receive(msg) | Process incoming remote CRDTMessage |
ack(msg_id) | Mark a journal entry as server-acknowledged |
undo() | Undo last committed message from this session |
redo() | Redo last undone message from this session |
compact() | Bake acknowledged entries into snapshot |
get_state() | Return current ClientState |
Pure Functions
For advanced use cases, the bare state-transition functions are also exported:
Low-Level Operations
Conflict Resolution
| Merge Strategy | Operation Types |
|---|---|
| Last-Write-Wins (LWW) | *.set — highest Lamport timestamp wins |
| Additive | number.add, vector3.add, quaternion.multiply — values accumulate |
| Commutative | boolean.or/boolean.and, number.min/number.max, array.union |
| Deep merge | object.merge — recursive per-key merge |
| CRDT text | text.insert/text.delete/text.replace — RGA/YATA algorithm |
All strategies are deterministic: given the same set of operations (in any order), every client converges to the same state.
Running Tests
To skip slow benchmarks:
Lossless checkpoints (Python 0.0.2)
Keep the server snapshot's textRopes field when constructing Snapshot. Both createGraph(initial_snapshot=...) and GraphStore.fromServer(...) hydrate this metadata automatically; custom consumers can call hydrateTextSnapshot(snapshot). The Python graph retains its existing _textCrdt.<path> rope plus visible string representation. TypeScript camelCase rope fields and deleted anchors are supported, and fresh local inserts use the local agent ID.
The raw MessagePack codec forwards additive sync-check, sync-status, and checkpoint metadata unchanged. Python does not automatically run a checksum monitor or manage held drafts. Compare note-body hashes only at equal committed clocks with no pending edits; legacy strings cannot recover discarded character identities.