Skip to content

capability_driver_quick_start

Anton Kremenetsky edited this page Jul 1, 2025 · 3 revisions

Capability driver: Quick start quide

This page provides a quick start guide for capability drivers. Please refer to main terms of the Universal Agent to be in the right context.

Driver interface

You can find the driver interface here every method has a docstring with short description and they won't be duplicated here.

Quick start

If you are reading this section, it means you would like to write your capability driver and that's great! As you already read a driver needs to work with data plane part and translate results to the Universal Agent via such abstraction as Resource. Let's imagine a simple problem we would like to solve for this quick start guide and write a driver for it. For instance, our driver should keep files in a particular directory. For simplicity we assume all files are empty, no nested directory and so on. We only need presence of these files in the particular directory. Let's start.

The full implementation can be found in dummy.py.

Register capability

The first step is to specify capabilities or kinds the driver works with. The capabilities are defined as a list of strings. For example, if we want to work with files we can register the file capability.

def get_capabilities(self) -> list[str]:
    """Returns a list of capabilities supported by the driver."""
    return ["file"]

Work with data plane

The data plane is a particular directory in our case. Let's say it's stored in the self.work_dir variable. Don't worry about initialization we will cover it later.

The first method we need to implement is create. The signature is as follows:

def create(self, resource: models.Resource) -> models.Resource:
    """Creates a resource."""

The agent sends resource to the driver to create something, in our case it's a file. The actual data is stored in the resource.value field. It's an ordinary dict and we can assume if somebody wants to create a file, he will send a file name. In other words the resource.value may look like this:

{
    "uuid": "a1b2c3d4-e5f6-7890-a1b2-c3d4e5f67890",
    "name": "test.txt"
}

Where uuid is a mandatory field.

The implementation of create can look like this:

def create(self, resource: models.Resource) -> models.Resource:
    """Creates a new file in the work directory."""
    name = f"{resource.value['uuid']}-{resource.value['name']}"
    path = os.path.join(self.work_dir, name)
    with open(path, "w") as f:
        f.write("")
    return resource

Create a file with the name <uuid>_<name> in the work_dir. uuid is mandatory to detect a resource the file belongs to.

The next step is to implement get:

def get(self, resource: models.Resource) -> models.Resource:
    """Find and return the file resource."""
    for f in os.listdir(self.work_dir):
        uuid, name = f.split("_", 1)
        if uuid == str(resource.uuid):
            value = {"uuid": uuid, "name": name}
            new_resource = models.Resource.from_value(value, resource.kind)
            return new_resource

    raise driver_exc.ResourceNotFound(resource=resource)

The first step is to find the file with the corresponding uuid. Then we need to create a new Resource from raw data(value). There are a couple of useful methods in the Resource class. The from_value static method will create a new resource from raw data. The resource is ready to be returned. If the resource is not found, raise ResourceNotFound exception.

list all available files in the work_dir:

def list(self, capability: str) -> list[models.Resource]:
    """Lists all files"""
    resources = []

    for f in os.listdir(self.work_dir):
        uuid, name = f.split("_", 1)
        value = {"uuid": uuid, "name": name}
        resource = models.Resource.from_value(value, capability)
        resources.append(resource)

    return resources

As described above, the first step is collecting raw data, then create a resource and append it to the list.

The next step is to implement delete:

def delete(self, resource: models.Resource) -> None:
    """Delete the file in the work directory."""
    try:
        res = self.get(resource)
    except driver_exc.ResourceNotFound:
        # Nothing to do, the resource does not exist
        return
    
    name = f"{res.value['uuid']}-{res.value['name']}"
    path = os.path.join(self.work_dir, name)
    os.remove(path)

Use get method to find the file and delete it. If the file is not found, then nothing to do.

Now we have a working driver for the file capability.

Register the driver

When the implementation is ready, you should register the driver in entry points. For example, if you use setup.cfg in your project you can do it like this:

[entry_points]
gcl_sdk_universal_agent =
    RenderAgentDriver = gcl_sdk.agents.universal.drivers.dummy:DummyFilesDriver

Specify your driver class name in the gcl_sdk_universal_agent section.

Usage

The driver and package are ready, it's time to use it in the universal agent. Firstly install your package with the driver in the machine where you run the agent.

pip install your-package-with-driver

Then add the driver to the configuration file /etc/genesis_universal_agent/genesis_universal_agent.conf:

[universal_agent]
caps_drivers = ...,YouDriverClassName

Restart the agent:

systemctl restart genesis-universal-agent

Now the agent will use your driver to work with files!

Clone this wiki locally