Skip to content

Distiller API

Distiller is AI Refinery’s underlying multi-agent collaboration framework. It provides convenient abstractions that let developers quickly create autonomous, collaborative agents capable of advanced reasoning and decision-making.

Before you begin, you must create an authenticated AsyncAIRefinery client, as shown below. All Distiller-related APIs are accessed via client.distiller.

import os
from air import AsyncAIRefinery
from dotenv import load_dotenv


load_dotenv() # loads your API_KEY from your local '.env' file
api_key=str(os.getenv("API_KEY"))


client = AsyncAIRefinery(api_key=api_key)

Preliminaries

Validating Your Configuration File

client.distiller.validate_config() (synchronous)

Validates a distiller configuration file to ensure it works with AI Refinery. This method helps catch configuration errors early in the development workflow by sending your configuration to the server for validation without actually creating a project.

Parameters:

  • config_path (Optional[str]): Path to a YAML configuration file. If provided, the file will be loaded and validated. Note: You must provide either config_path OR config, but not both. An error will be raised if both parameters are provided.
  • config (Optional[dict | str]): Either a configuration dictionary (JSON format) or a YAML string. Used when you want to validate a configuration without saving it to a file first.

    Note: You must provide either config_path OR config, but not both. An error will be raised if both parameters are provided.

  • send_yaml_string (bool, optional): If True and config_path is provided, sends the raw YAML text to the server. If False (default), converts the YAML to JSON before sending. This parameter is useful when you need the server to validate the exact YAML syntax.
  • timeout (float, optional): Request timeout in seconds. Defaults to 15.0.

Returns:

  • bool: True if the configuration is valid and passes all server-side validation checks. False if validation fails due to configuration errors, network issues, or server problems.

Usage Examples:

# Basic validation with a YAML configuration file
is_valid = client.distiller.validate_config(config_path="example.yaml")
if is_valid:
    print("Configuration is valid!")
else:
    print("Configuration validation failed.")

# Validate a configuration dictionary
config_dict = {
    "orchestrator": {...},
    "utility_agents": [...],
    "super_agents": [...],
    "base_config": {...},
    "memory_config": {...}
}
is_valid = client.distiller.validate_config(config=config_dict)

# Send raw YAML string for validation (preserves exact YAML syntax)
is_valid = client.distiller.validate_config(
    config_path="example.yaml", 
    send_yaml_string=True
)

# Usage with custom timeout for large configurations
is_valid = client.distiller.validate_config(
    config_path="large_config.yaml", 
    timeout=30.0
)

Error Handling and Best Practices:

  • Validation Failures: When validate_config() returns False, check your configuration file for common issues such as:

    • Invalid YAML syntax
    • Missing required fields in agent configurations
    • Incorrect agent class names
    • Invalid model names or parameters
    • Malformed nested configurations
  • Error Logging Example: When validation fails, detailed error information is logged. For example, a typo in an agent class name will produce an error log like this:

2025-10-13 11:59:43,999 ERROR air.distiller.client: 
Config validation failed: status=422 
body={'error': {'code': 'distiller.schema.validation_error', 'message': 'Distiller Configuration Validation Error', 
'detail': {'pydantic_errors': [{'type': 'value_error', 'loc': ['utility_agents', 0, 'agent_class'], 'msg': 
"Agent class 'AnalyticsAgnt' is not registered.", 'input': 'AnalyticsAgnt', 'ctx': {'error': "Agent class 
'AnalyticsAgnt' is not registered."}}]}}}

In this example, the error shows that 'AnalyticsAgnt' should be 'AnalyticsAgent' (missing 'e'). The error details include:

- **Location:** `['utility_agents', 0, 'agent_class']` - the exact path in your configuration
- **Issue:** The agent class name has a typo and is not registered
- **Input:** The incorrect value that caused the error
  • Network Issues: If validation fails due to network problems, the method will return False. Consider increasing the timeout parameter for slow connections.

  • Recommended Workflow: Always validate your configuration before calling create_project() to catch errors early and avoid failed project creation attempts.

  • Configuration Formats: You can validate configurations in multiple ways:

    • YAML files via config_path
    • Python dictionaries via config
    • Raw YAML strings via config parameter
    • Choose send_yaml_string=True when YAML-specific validation is needed

Creating Your Project

client.distiller.create_project() (synchronous)

Creates a new project based on the specified YAML configuration file.

Parameters:

  • config_path (str): The path to the YAML configuration file.
  • project (str): A name for your project (letters, digits, hyphens, underscores only).

Returns:

  • bool: True if the project is successfully created.

Project Versioning:

  • Distiller automatically handles project versioning, starting at version 0.
  • The first time you create a project with a given name, it is assigned version 0. If you create another project with the same name, Distiller increments the version to 1, and so on.
  • By default, connections are made to the latest project version unless a specific version is specified. For more details, refer to the distiller connection section below.

Example:

# This command registers the project "example" using the "example.yaml" configuration file.
client.distiller.create_project(config_path="example.yaml", project="example")

Downloading Your Project Configuration

client.distiller.download_project() (synchronous)

Retrieves the configuration of a specified project from the server.

Parameters:

  • project (str): The name of the project whose configuration you want to download.
  • project_version (str, optional): The version of the project configuration to download. Defaults to the latest version if not provided.

Returns:

  • dict: A Python dictionary containing the downloaded configuration.

Example:

# This command downloads version "1" of the "example" project.
project_config = client.distiller.download_project(project="example", project_version="1")

Connecting to Distiller

client.distiller.__call__() (asynchronous)

Establishes an asynchronous connection (via a WebSocket) to the Distiller endpoint for a specific project. Usage of this function within an async context manager allows easy management of all Distiller-related operations.

Parameters:

  • project (str): The project name (letters, digits, hyphens, underscores only).
  • uuid (str): A unique user identifier (letters, digits, hyphens, underscores only).
  • executor_dict (dict[str, Callable], optional): A dictionary mapping custom agent names to callable functions. These callables are invoked when their corresponding agents are triggered by the super agent or orchestrator. Defaults to {}.
  • project_version (str, optional): The project version to connect to. If not provided, Distiller uses the latest version.

Returns:

  • _DistillerContextManager: An asynchronous context manager that handles operations within the given project.

Example:

async with client.distiller(
    project="example",
    uuid="test"
) as dc:
    # Your asynchronous operations here
    pass

client.distiller.query() (asynchronous)

Sends a query message to the WebSocket asynchronously.

Parameters:

  • query (str): The text of your query.
  • image (Optional[str], optional): An image to include in the query. Defaults to None.
  • **kwargs: Additional keyword arguments.

Returns:

  • Coroutine: A coroutine that, when awaited, sends the query request.

Example:

async with client.distiller(
    project="example",
    uuid="test"
) as dc:
    responses = await dc.query(query="hi")
    async for response in responses:
        print(response)

client.distiller.add_memory() (asynchronous)

Adds memory to the WebSocket asynchronously.

Parameters:

  • **kwargs: Any keyword arguments you want to store as memory.

Returns:

  • Coroutine: A coroutine that, when awaited, adds the specified memory.

Example:

async with client.distiller(
    project="example",
    uuid="test"
) as dc:
    # Adding environment variables to memory
    await dc.add_memory(
        source="env_variable",
        variables_dict={"travel_destinations": "Hidden gems and cultural hotspots"},
    )

client.distiller.retrieve_memory() (asynchronous)

Retrieves memory from the WebSocket asynchronously.

Parameters:

  • **kwargs: Keyword arguments for memory retrieval.

Returns:

  • Coroutine: A coroutine that, when awaited, retrieves the requested memory.

Example:

async with client.distiller(
    project="example",
    uuid="test"
) as dc:
    # Retrieve environment variables
    retrieved_env_variables = await dc.retrieve_memory(
        source="env_variable"
    )

AsyncAIRefinery.distiller.reset_memory() (asynchronous)

Resets memory in the WebSocket asynchronously.

Parameters:

  • **kwargs: Keyword arguments indicating which memory to reset (if applied).

Returns:

  • Coroutine: A coroutine that, when awaited, resets the specified memory.

Example:

async with client.distiller(
    project="example",
    uuid="test"
) as dc:
    # Reset Memory
    await dc.reset_memory()

To learn more about Distiller, visit the Distiller section in the AI Refinery documentation. For detailed examples of building complex multi-agent projects, check out the Tutorial pages.