Differences with legacy Juice#

This document describes the main differences between legacy OrangeQS Juice and the new OrangeQS Juice. Users who are familiar with legacy Juice can use this guide as a starting point to get acquainted with the new Juice.

Migrating from old Juice

Note that this guide is not meant as a migration guide! The list below is not meant to be exhaustive, but rather highlights the most important changes for end users.

Changed features#

The following features have undergone significant changes compared to legacy Juice.

Executing code on services#

Legacy. Code is executed using raw strings on a remote service:

>>> from orangeqs.juice import Client
>>> client = Client()
>>> client.execute("device", "5 + 3")
{
    # IPython response
}
>>> client._return_variable
"8"

New. Code is executed by defining a task schema and passing an instance of the task to the execute() method.

>>> from orangeqs.juice import Client
>>> from orangeqs.juice.schemas.tasks import IPythonTask
...
>>> class AddNumbers(IPythonTask):
...     a: int
...     b: int
...     @property
...     def code(self) -> str:
...         return f"{self.a} + {self.b}"
...
>>> client = Client()
>>> await client.execute("device", AddNumbers(a=5, b=3))
TaskResultOk(id='...', result={"status": "ok", "result": 8})

It is also possible to execute raw code, as in legacy Juice, by using the RawIPython task:

>>> from orangeqs.juice.schemas.tasks import RawIPython
>>> await client.execute("device", RawIPython(raw_code="5 + 3"))
TaskResultOk(id='...', result={"status": "ok", "result": 8})

The following APIs should help you get started with executing code on services:

Client.request

Request a task to be executed and return a future for the result.

Client.execute

Execute a task, wait for the result, returning a TaskResult.

Client.execute_blocking

Execute a task, wait for the result, returning a TaskResult.

Task

Base class for tasks that can be executed by a service.

IPythonTask

Base class for executing code in an IPython kernel.

Additionally, see Tasks for an interactive guide. This guide also explains how to define custom handlers for tasks on services. These handlers can interact with other parts of a service besides the IPython kernel.

Dependency management#

Legacy. All dependencies listed in ~/shared/requirements.txt are installed when the service or user container is started. A combination of micromamba and pip is used to install the dependencies.

New. Dependencies are now specified via the orchestration.toml configuration and the Lab repository. See Installing Juice extensions and Python packages for specific instructions on how to add new dependencies.

Additionally, each service has its own container image that is built from the configuration, and can be rebuilt using rebuild_service(). Dependencies are thus no longer automatically installed when the service or user container is started, but require an explicit rebuild of the container image. After rebuilding, don’t forget to restart the service using restart_service(), or restart your user container from the Hub control panel.

Finally, OrangeQS Juice uses uv for dependency management, which means all its commands are available. Note that any changes made using uv commands will not persist across restarts, as the service will use the container image that was created during the last rebuild.

Other#

Other changes include:

  • Repositories of Python packages are now stored in ~/shared/lib.

  • The Quantify data folder is now stored at ~/shared/data/quantify.

New features#

The following features are new in OrangeQS Juice and were not present in legacy Juice.

Extensions#

The bare OrangeQS Juice installation comes with a minimal set of features like logging, configuration management, JupyterLab and dashboard interfaces, and inter-service communication. Additional functionality is provided via extensions that can be installed separately. Extensions can provide everything from additional services and new task types to custom dashboard pages. See Tutorials for more information on how to install and develop extensions. Extensions can provide the following features:

  • New configuration files and options.

  • New client methods.

  • New services.

  • New task types.

  • New dashboard pages.

  • New pub/sub message types.

Pub/sub communication#

OrangeQS Juice now includes a built-in publish/subscribe communication system that allows services to communicate with each other in a decoupled manner. The following APIs are available:

publisher_async

Create an async publisher instance from Juice configuration.

subscriber_async

Create an async subscriber instance from Juice configuration.

publisher_blocking

Create a synchronous publisher instance from Juice configuration.

subscriber_blocking

Create a synchronous subscriber instance from Juice configuration.

See Pub/Sub Communication for an interactive guide.

Lab repository#

By default, OrangeQS Juice now includes an editable Python package that is installed in all services and user containers. It is located at ~/shared/lib/lab. This package is intended as a starting point for users to develop their own code and share it between services.

As this package is installed in each environment, users can add additional packages by listing them as dependencies in pyproject.toml. See Installing Juice extensions and Python packages for specific instructions on how to add new dependencies.

Other#

Other new features include:

  • All user containers and services have access to a shared directory meant for storing temporary runtime data. The path is defined by the SHARED_RUNTIME_PATH constant. Currently, this path is /var/run/juice.

Moved features#

The following features are no longer part of OrangeQS Juice core and have been moved to extensions:

Missing features#

The following features from legacy Juice are not yet available in the new OrangeQS Juice.

  • File browser (planned, coming soon)

  • VS Code server (planned, coming soon)

  • Monitoring (planned, for a later release)