Client library for Sidekick Visual Coding Buddy
Project description
Sidekick Python Library (sidekick-py)
1. Overview
This library provides the Python interface ("Hero") for interacting with the Sidekick Visual Coding Buddy frontend UI. It allows Python scripts to easily create, update, interact with, and remove visual modules like grids, consoles, variable visualizers, drawing canvases, and UI controls within the Sidekick UI, typically via a mediating WebSocket Server.
The library abstracts the underlying WebSocket communication and JSON message formatting, offering an intuitive object-oriented API. Key features include:
- Simplified Event Handling: Register specific callbacks (e.g.,
on_click,on_input_text) directly on module instances. - Reliable Communication: Automatic connection management, peer discovery, message buffering, and keep-alive mechanisms.
- Flexible Instantiation: Create new UI modules or attach to existing ones (
spawn=False). - Reactive Visualization: Integration with
ObservableValuefor automatic UI updates in theVizmodule. - Global Message Observation: Optional handler to inspect all incoming messages.
2. Features
- Object-Oriented API: Control visual modules (
Grid,Console,Viz,Canvas,Control) via Python classes and methods (using standard Pythonsnake_case). - Simplified Event Handling:
- Interactive modules (
Grid,Console,Control) offer specific event registration methods (e.g.,module_instance.on_click(...),module_instance.on_input_text(...)). - All modules provide an
on_error(callback)method to handle errors reported by the frontend for that specific instance. - Removes the need for users to manually parse raw
eventorerrormessages in most cases.
- Interactive modules (
- Global Message Handling: Provides
sidekick.register_global_message_handler(handler)for observing all incoming messages for advanced logging, debugging, or handling custom message types. - Peer Discovery & Status: Automatically announces itself (
system/announcewithrole: "hero") upon connection and listens for Sidekick (role: "sidekick") announcements to manage interaction readiness (CONNECTED_READYstate). - Message Buffering: Automatically queues module-specific commands (
spawn,update,remove) and global commands (clearAll) if the connection is not yetCONNECTED_READY(i.e., Sidekick hasn't announced itself online). The buffer is flushed automatically when the connection becomes ready. - Automatic State Clearing: Configurable options (
clear_on_connect,clear_on_disconnect) viasidekick.set_config()to manage the Sidekick UI state automatically upon connection or disconnection. - Re-attachment Support: Module constructors include
spawn: bool(defaultTrue). Settingspawn=Falseallows the Python object to represent and interact with an existing module instance in Sidekick (identified byinstance_id) without sending aspawncommand. ObservableValue: A general-purpose wrapper (sidekick.ObservableValue) to track changes in Python values (primitives or containers like lists, dicts, sets). It automatically notifies subscribers (like theVizmodule) with detailed change information upon mutation or.set()calls.- Automatic WebSocket Management: Handles connection establishment (default
ws://localhost:5163), peer announcements, keep-alive (Ping/Pong), background listener thread, connection state tracking (ConnectionStatus), and attempts graceful cleanup on script exit (atexit). - Structured Data Visualization (
Viz): Provides detailed, expandable views of Python data structures, reacting automatically to granular changes inObservableValueinstances shown viaviz.show(). - Basic 2D Drawing (
Canvas): Allows programmatic drawing of lines, rectangles, and circles with basic configuration options. Uses command IDs for reliable processing. - Dynamic UI Controls (
Control): Dynamically add buttons and text inputs to the UI and receive interaction events via specific callbacks (on_click,on_input_text).
3. Installation
Development:
Install from the project root (Sidekick/) directory:
pip install -e libs/python
This links the installed package to your source code, so changes are immediately reflected.
Standard Installation (from PyPI):
pip install sidekick-py
4. Core Concepts
4.1. Connection Management (connection.py)
- Singleton & State Machine: Manages a single, shared WebSocket connection using a state machine (
ConnectionStatus:DISCONNECTED,CONNECTING,CONNECTED_WAITING_SIDEKICK,CONNECTED_READY). Thread-safety is managed using anRLock. - Lazy Connection: The connection attempt is automatically triggered when the first module is instantiated or when
sidekick.activate_connection()is explicitly called. - Configuration (
set_url,set_config): These global functions must be called before the first connection attempt is made (i.e., before the first module is created oractivate_connectionis called).set_url(url: str): Sets the WebSocket server URL (defaults tows://localhost:5163).set_config(clear_on_connect: bool = True, clear_on_disconnect: bool = False):clear_on_connect: If True, automatically sends aglobal/clearAllmessage after the connection status becomesCONNECTED_READY(first Sidekick peer announces online).clear_on_disconnect: If True, attempts (best-effort) to sendglobal/clearAllfollowed by asystem/announce offlinemessage during theclose_connectionprocess. Not guaranteed if the connection is already lost or the script terminates abruptly.
- Peer Announcement (
system/announce):- Sending: Upon successful WebSocket connection, generates a unique
peerId(hero-<uuid>) and immediately sends asystem/announcemessage withrole: "hero",status: "online", and the libraryversion. Attempts to send anofflineannouncement on graceful disconnect (close_connection). - Receiving: The listener thread parses incoming
system/announcemessages. It maintains a set of online SidekickpeerIds (_sidekick_peers_online). When the first Sidekick announcesonline, the connection status transitions toCONNECTED_READY.
- Sending: Upon successful WebSocket connection, generates a unique
- Message Buffering (
_message_buffer):- A
dequestores outgoing messages (exceptsystem/announce). - When
send_message()is called for non-system messages:- If status is not
CONNECTED_READY, the message is added to the buffer. - If status is
CONNECTED_READY, the message is sent immediately.
- If status is not
- Buffer Flushing: When the status transitions to
CONNECTED_READY,_flush_message_buffer()sends all buffered messages in FIFO order (after potentially sendingclear_on_connect).
- A
- Keep-Alive: Uses WebSocket Ping/Pong frames (configured via internal constants
_PING_INTERVAL,_PING_TIMEOUT) for reliable connection maintenance and failure detection. - Listener Thread (
_listen_for_messages): Runs in the background to receive messages.- Parses incoming JSON messages.
- Handles
system/announceto update Sidekick status and trigger readiness/buffer flushing. - Message Dispatching:
- If a
_global_message_handleris registered (viaregister_global_message_handler), it's called first with the raw incoming message dictionary. - If the message has a
srcfield (identifying the source module instance, typically foreventorerrormessages), it looks up the internal handler function registered for thatinstance_id(via_message_handlers). - It calls the found internal handler (which belongs to the specific
BaseModulesubclass instance).
- If a
- Handler Registration:
register_message_handler(instance_id, handler): Internal use. Called byBaseModule.__init__to register the module instance's_internal_message_handler.unregister_message_handler(instance_id): Internal use. Called byBaseModule.removeand__del__.register_global_message_handler(handler): Public API. Registers or unregisters a single global function to receive all messages.
- Cleanup (
atexit): Registersclose_connectionto run on normal script exit for graceful shutdown attempts.
4.2. Base Module (base_module.py)
- Provides common functionality for all module classes (
Grid,Console,Viz, etc.). - Constructor
__init__(module_type, instance_id=None, spawn=True, payload=None):- Takes
spawn: boolparameter:spawn=True(Default): Creates a new visual instance in Sidekick.instance_idis optional (auto-generated ifNone). Sends aspawncommand message (buffered if needed) with thepayload.spawn=False: Re-attachment mode. Assumes the visual instance already exists in Sidekick.instance_idbecomes mandatory. Nospawncommand is sent.payloadis ignored.
- Activates the connection (
connection.activate_connection()). - Generates or validates
target_id. - Registers its own
self._internal_message_handlerwith the connection manager using itstarget_id. - Does NOT take an
on_messageargument anymore.
- Takes
- Internal Message Handler (
_internal_message_handler(self, message))- This method is called by
connection.pywhen a message for this specific instance (srcmatchestarget_id) is received. - The base implementation in
BaseModulespecifically handles messages withtype: "error". It extracts the error message from the payload and calls the user-registered_error_callback(if any). - Subclasses (like
Grid,Console,Control) MUST override this method. Their overridden method should:- Check if the message
typeis"event". - Parse the
payloadto determine the specific event (e.g.,payload['event'] == 'click'). - Extract relevant data from the payload (e.g.,
payload['x'],payload['y'],payload['value'],payload['controlId']). - Call the corresponding specific user callback (e.g.,
self._click_callback(x, y)). - Crucially, call
super()._internal_message_handler(message)to allow the base class to handle potentialerrormessages or other common types in the future.
- Check if the message
- This method is called by
- Error Callback (
on_error(self, callback))- Public method available on all module instances.
- Allows users to register a function (
Callable[[str], None]) that will be called if anerrormessage is received from the frontend specifically for this module instance. The callback receives the error message string.
- Sending Commands (
_send_command,_send_update)- Internal helper methods to construct messages (
spawn,update,remove) with the correctmodule,type, andtargetID. - They use
connection.send_message, which handles buffering automatically. - Payloads constructed here MUST use
camelCasekeys.
- Internal helper methods to construct messages (
- Removal (
remove())- Sends the
removecommand to Sidekick. - Unregisters the instance's internal message handler from the connection manager.
- Resets any local callbacks (
_error_callback, and calls_reset_specific_callbacksfor subclasses).
- Sends the
4.3. Communication Protocol & Payloads
- Communication uses JSON messages over WebSocket, typically relayed by a Server.
- Messages follow the structure:
{ id: int, module: str, type: str, target?: str, src?: str, payload?: object | null }. (Seeprotocol.mdfor full details). - Payload Keys: All keys within the
payloadobject and any nested objects within it MUST usecamelCase. This is enforced by the protocol and expected by the Sidekick frontend. The Python library is responsible for ensuring outgoing messages adhere to this. - Key Message Types & Modules:
system/announce: Used for peer discovery and status (handled internally byconnection.py).target/srcomitted.global/clearAll: Used to clear all Sidekick modules. Sent viasidekick.clear_all()or automatically viaclear_on_connect.target/src/payloadomitted.- Module interaction types (
spawn,update,remove) sent from Hero to Sidekick. Requiretargetfield identifying the module instance.payloadstructure depends onmoduleandtype. - Module feedback types (
event,error) sent from Sidekick to Hero. Requiresrcfield identifying the module instance.payloadstructure depends onmoduleandtype.
- Refer to
protocol.mdfor detailed payload structures for each module/type combination.
4.4. ObservableValue (observable_value.py)
- A wrapper class for Python values (primitives, lists, dicts, sets).
- Intercepts common mutable operations (
append,__setitem__,add,update,clear, etc.) or explicit.set()calls. - Notifies subscribed callbacks with detailed
change_detailsdictionary upon changes. - Essential for the reactive updates in the
Vizmodule. When anObservableValueinstance is passed toviz.show(), theVizmodule subscribes to it and sends granular updates to the frontend upon notification.
4.5. Viz Module Integration (viz.py)
viz.show(name, value)sends an initialupdate(action: "set") message with the full representation of thevalue.- If
valueis anObservableValue,viz.showsubscribes to it. - When the
ObservableValuenotifiesVizof a change (via internal callback_handle_observable_update),Viztranslates thechange_detailsinto a granularupdatemessage (action: "setitem",action: "append", etc.) for Sidekick. This message includes thevariableName,pathto the change, andcamelCaserepresentations (valueRepresentation,keyRepresentation,length).
5. API Reference
(Note: All methods sending messages construct payloads with camelCase keys. Non-system messages are buffered until Sidekick is online.)
5.1. Top-Level Functions (sidekick namespace)
sidekick.set_url(url: str): Sets the WebSocket Server URL. Call before connecting.sidekick.set_config(clear_on_connect: bool = True, clear_on_disconnect: bool = False): Configures automatic clearing behavior. Call before connecting.sidekick.clear_all(): Sendsglobal/clearAllmessage to Sidekick (buffered if connection not ready).sidekick.close_connection(): Manually closes the WebSocket connection and attempts cleanup.sidekick.activate_connection(): Ensures the connection attempt is initiated. Called automatically by module constructors but can be called manually. Safe to call multiple times.sidekick.register_global_message_handler(handler: Optional[Callable[[Dict[str, Any]], None]]): Registers or unregisters a single handler function that will be called with every message received from Sidekick. The handler receives the raw message dictionary. UseNoneto unregister.
5.2. sidekick.ObservableValue
ObservableValue(initial_value: Any)- Methods:
.get(),.set(new_value),.subscribe(callback),.unsubscribe(callback). - Also intercepts and notifies on standard mutable container methods like
.append(),.__setitem__(),.add(),.update(),.pop(),.remove(),.clear(),.__delitem__(),.insert(),.discard().
5.3. sidekick.Grid
Grid(num_columns: int = 16, num_rows: int = 16, instance_id: Optional[str] = None, spawn: bool = True)spawn=Falserequiresinstance_id.
- Methods:
.set_cell(x: int, y: int, color: Optional[str] = None, text: Optional[str] = None).set_color(x: int, y: int, color: Optional[str]).set_text(x: int, y: int, text: Optional[str]).clear().remove()
- Event Handlers:
.on_click(callback: Optional[Callable[[int, int], None]]): Registers a function called when a cell is clicked. Callback receivesx(column index) andy(row index). PassNoneto unregister..on_error(callback: Optional[Callable[[str], None]]): Registers a function called when an error related to this grid instance is received from Sidekick. Callback receives the error message string. PassNoneto unregister.
5.4. sidekick.Console
Console(instance_id: Optional[str] = None, spawn: bool = True, initial_text: str = "", show_input: bool = False)spawn=Falserequiresinstance_id;initial_textandshow_inputare ignored.
- Methods:
.print(*args: Any, sep: str = ' ', end: str = '').log(message: Any).clear().remove()
- Event Handlers:
.on_input_text(callback: Optional[Callable[[str], None]]): Registers a function called when text is submitted via the input field (requiresshow_input=True). Callback receives the submitted text string. PassNoneto unregister..on_error(callback: Optional[Callable[[str], None]]): Registers a function called when an error related to this console instance is received. Callback receives the error message string. PassNoneto unregister.
5.5. sidekick.Viz
Viz(instance_id: Optional[str] = None, spawn: bool = True)spawn=Falserequiresinstance_id.
- Methods:
.show(name: str, value: Any): Displays a variable. Automatically subscribes ifvalueis anObservableValue..remove_variable(name: str): Removes a variable from the display..remove(): Removes the entire Viz panel instance.
- Event Handlers:
.on_error(callback: Optional[Callable[[str], None]]): Registers a function called when an error related to this Viz instance is received. Callback receives the error message string. PassNoneto unregister. (Note: Viz currently doesn't emit user interaction events like 'click'.)
5.6. sidekick.Canvas
Canvas(width: int, height: int, instance_id: Optional[str] = None, spawn: bool = True, bg_color: Optional[str] = None)spawn=Falserequiresinstance_id;width,height,bg_colorare ignored.
- Methods:
.clear(color: Optional[str] = None).config(stroke_style: Optional[str] = None, fill_style: Optional[str] = None, line_width: Optional[int] = None).draw_line(x1: int, y1: int, x2: int, y2: int).draw_rect(x: int, y: int, width: int, height: int, filled: bool = False).draw_circle(cx: int, cy: int, radius: int, filled: bool = False).remove()
- Event Handlers:
.on_error(callback: Optional[Callable[[str], None]]): Registers a function called when an error related to this Canvas instance is received. Callback receives the error message string. PassNoneto unregister. (Note: Canvas currently doesn't emit user interaction events.)
5.7. sidekick.Control
Control(instance_id: Optional[str] = None, spawn: bool = True)spawn=Falserequiresinstance_id.
- Methods:
.add_button(control_id: str, text: str).add_text_input(control_id: str, placeholder: str = "", initial_value: str = "", button_text: str = "Submit").remove_control(control_id: str).remove()
- Event Handlers:
.on_click(callback: Optional[Callable[[str], None]]): Registers a function called when a button control created by this instance is clicked. Callback receives thecontrolIdof the clicked button. PassNoneto unregister..on_input_text(callback: Optional[Callable[[str, str], None]]): Registers a function called when a text input control created by this instance is submitted. Callback receives thecontrolIdand the submittedvaluestring. PassNoneto unregister..on_error(callback: Optional[Callable[[str], None]]): Registers a function called when an error related to this Control panel instance is received. Callback receives the error message string. PassNoneto unregister.
6. Development Notes
- Structure: The core connection and dispatch logic resides in
connection.pyandbase_module.py. Individual module classes (grid.py,console.py, etc.) inherit fromBaseModuleand implement specific methods and event handling. - Dependencies: Requires the
websocket-clientlibrary (pip install websocket-client). - Payload Keys: Remember that all keys in the
payloaddictionary sent to Sidekick MUST usecamelCase. This is handled internally by the library's methods, but be aware if constructing messages manually. - Buffering: Module/global commands are automatically buffered if Sidekick is not yet online. Check DEBUG logs (
SidekickConnlogger) to observe buffering and flushing. - Re-attachment: Use
spawn=Falsewith the correctinstance_idto control existing Sidekick elements. Ensure Sidekick's state persistence aligns with this usage and consider the effect ofclear_on_connect. - Event Handling: Use the specific
on_click,on_input_text,on_errormethods provided by module instances for clear and simple event handling. Useregister_global_message_handlerprimarily for debugging or advanced scenarios.
7. Troubleshooting
- Connection Errors:
- Check if the Sidekick WebSocket server (often part of the VS Code extension or run via
npm run devinwebapp) is running. - Verify the URL (
ws://localhost:5163by default) is correct usingsidekick.set_url()before creating modules. - Check firewalls.
- Inspect the
SidekickConnDEBUG logs for detailed connection attempts and errors.
- Check if the Sidekick WebSocket server (often part of the VS Code extension or run via
- Messages Not Appearing in Sidekick (Module Commands):
- Check
SidekickConnlogs. Are messages being buffered (Buffering message...)? - Did Sidekick announce itself online (
Sidekick peer online...,System is READY.)? - Was the buffer flushed (
Flushing message buffer...)? - Inspect the WebSocket messages in your browser's Developer Tools (Network -> WS tab) within Sidekick. Verify the message structure (
module,type,target) and ensure thepayloadkeys arecamelCase. - Check the Sidekick browser console for errors when processing the message (e.g., payload validation errors in
*Logic.ts).
- Check
- Callbacks Not Firing (
on_click,on_input_text,on_error):- Registration: Ensure you called the correct registration method (e.g.,
my_grid.on_click(my_handler)) on the specific module instance. - Message Reception: Check
SidekickConnDEBUG logs. Is the correspondingeventorerrormessage being received from Sidekick? Verify thesrcfield matches yourinstance_id. - Event Type: For
eventmessages, check thepayload['event']field in the log matches the expected type (e.g.,"click","inputText"). - Callback Execution: Add logging inside your callback function to confirm it's being entered. Check for exceptions occurring within your callback function (these will be logged by SidekickConn).
- Registration: Ensure you called the correct registration method (e.g.,
clear_on_connect/clear_on_disconnectIssues: Ensureset_configis called before connection. Note thatclear_on_disconnectis best-effort.- Errors Using
spawn=False:- Did you provide the correct, non-None
instance_id? - Does the instance actually exist in Sidekick with that ID? It might have been cleared (manually or via
clear_on_connect).
- Did you provide the correct, non-None
- Viz Module Not Updating Reactively:
- Ensure the value passed to
viz.show()is ansidekick.ObservableValueinstance. - Ensure mutations are happening through the
ObservableValuewrapper methods (e.g.,obs_list.append(item),obs_dict[key] = value,obs_value.set(new_val)), not by modifying internal data directly. - Check logs for subscription messages and update processing in
Vizandconnection.
- Ensure the value passed to
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sidekick_py-0.0.1.tar.gz.
File metadata
- Download URL: sidekick_py-0.0.1.tar.gz
- Upload date:
- Size: 39.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
922e002ba9c3c06e67c396fe5170d2e275f55a5b722f1ceca8fd4a792c979163
|
|
| MD5 |
df5b95d16280db680dddd7ca816c6d8c
|
|
| BLAKE2b-256 |
2aaf3d73366c8cb3fca43df748d7f47d4f89e2bbbbb9de42b386a5d6eef84a9c
|
File details
Details for the file sidekick_py-0.0.1-py3-none-any.whl.
File metadata
- Download URL: sidekick_py-0.0.1-py3-none-any.whl
- Upload date:
- Size: 38.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
97fb196ada482f98dc6bbf559eb5dde610b846306604b35af9e8d4466157176f
|
|
| MD5 |
e471b330731afd286e1d42a359ee6a32
|
|
| BLAKE2b-256 |
4976462a92d409e0ed3aace0ab7385eb151e7fb574602014c309f0894681f43e
|