SuperAPI AI Directory

speckle-impl-automate-functions

Deployment & operations

View repository

Updated 10-15 22:18

About this skill

speckle-impl-automate-functions

Quick Reference

Functions vs Automations

Concept What It Is Created By Scope
Function Reusable code template defining execution logic Developer Global — listed in Function Library
Automation Configured instance of a Function bound to a model Project owner / admin Project-specific — triggers on version creation

Trigger Flow

1. User publishes new version to a model
2. Speckle Server detects version creation event
3. Server finds all automations bound to that model
4. For each automation, Speckle spins up a Docker container
5. Function code executes with access to the new version data
6. Results (pass/fail, annotations, files) attach to the version
7. Results appear in the Speckle web UI and 3D viewer

SDK Packages

Language Package Target Input Class
Python specklepy (3.1.0+, includes automate) Python 3.10+ AutomateBase (Pydantic)
C# Speckle.Automate.Sdk (NuGet, 3.4.0-alpha.20) .NET 8.0 readonly struct with DataAnnotations

AutomationContext Methods

Method (Python) Method (C#) Purpose
receive_version() ReceiveVersion() Retrieve the root Base object for the triggering version
attach_error_to_objects(category, object_ids, message) AttachErrorToObjects(category, objectIds, message) Annotate specific objects with errors in the 3D viewer
mark_run_success(message) MarkRunSuccess(message) Explicitly mark the run as successful
mark_run_failed(message) MarkRunFailed(message) Explicitly mark the run as failed (intentional validation failure)
store_file_result(file_path) StoreFileResult(filePath) Attach a file artifact to the run results
set_context_view() SetContextView() Configure the 3D viewer URL for the run result

Critical Warnings

ALWAYS call either mark_run_success() or mark_run_failed() before the function exits. If neither is called and no exception occurs, the run status is ambiguous and unreliable.

NEVER pass an empty list to attach_error_to_objects(). Empty or invalid object IDs produce silent failures with no visible annotations in the viewer.

NEVER use plain str for sensitive inputs (API keys, tokens). ALWAYS use SecretStr (Python) or [Secret] attribute (C#) to prevent exposure in logs and the Speckle UI.

NEVER modify or delete the auto-generated .github/workflows/main.yml environment variables (SPECKLE_FUNCTION_ID, SPECKLE_FUNCTION_TOKEN). Removing these breaks the deployment pipeline completely.

NEVER expect a function to appear in the Function Library without creating a GitHub release. Pushing code to the repository alone is NOT sufficient for publishing.

ALWAYS provide title and description on every input field. Without metadata, the Speckle UI shows raw field names that are meaningless to end users.


Python Function Template

Project Structure

my-automate-function/
├── main.py                   # Function entry point
├── flatten.py                # Object traversal utility
├── pyproject.toml            # Dependencies (specklepy)
├── Dockerfile                # Container image definition
├── tests/                    # Local test suite
│   └── test_function.py
├── .env.example              # Environment variable template
└── .github/
    └── workflows/
        └── main.yml          # CI/CD — auto-generated by wizard

Entry Point Pattern (Python)

from speckle_automate import (
    AutomateBase,
    AutomationContext,
    execute_automate_function,
)
from pydantic import Field, SecretStr

class FunctionInputs(AutomateBase):
    param_name: str = Field(title="Parameter", description="Describe this input")

def automate_function(
    automate_context: AutomationContext,
    function_inputs: FunctionInputs,
) -> None:
    base = automate_context.receive_version()
    # ... validation logic ...
    automate_context.mark_run_success("Validation passed.")

if __name__ == "__main__":
    execute_automate_function(automate_function, FunctionInputs)

CRITICAL: Pass automate_function WITHOUT parentheses and FunctionInputs as a CLASS (not an instance). The Automate runtime handles instantiation and injection.

flatten_base Traversal

from specklepy.objects import Base
from typing import Iterable

def flatten_base(base: Base) -> Iterable[Base]:
    """Recursively flatten a Base object hierarchy (depth-first, bottom-up)."""
    elements = getattr(base, "elements", getattr(base, "@elements", None))
    if elements is not None:
        for element in elements:
            yield from flatten_base(element)
    yield base

This function:

  • Uses getattr with fallback to handle both elements and @elements naming conventions
  • Yields descendants before the parent (depth-first, bottom-up order)
  • Returns Iterable[Base] for lazy evaluation — memory-efficient for large models
  • Is included in the official Python template as flatten.py

C# Function Template

Project Structure

MyAutomateFunction/
├── MyAutomateFunction/
│   ├── AutomateFunction.cs       # Function logic
│   ├── FunctionInputs.cs         # Input schema definition
│   ├── Program.cs                # DI entry point
│   └── MyAutomateFunction.csproj # .NET 8.0, NuGet refs
├── TestAutomateFunction/         # Integration tests
│   └── TestAutomateFunction.cs
├── Dockerfile                    # Multi-stage build
└── .github/
    └── workflows/
        └── main.yml              # CI/CD — auto-generated by wizard

Entry Point Pattern (C#)

// Program.cs
using Microsoft.Extensions.DependencyInjection;
using Speckle.Automate.Sdk;

var serviceCollection = new ServiceCollection();
serviceCollection.AddAutomateSdk();
serviceCollection.AddSingleton<AutomateFunction>();
var serviceProvider = serviceCollection.BuildServiceProvider();

var runner = serviceProvider.GetRequiredService<IAutomationRunner>();
var function = serviceProvider.GetRequiredService<AutomateFunction>();

return await runner.Main<FunctionInputs>(args, function.Run);

Function Class (C#)

using Speckle.Automate.Sdk;
using Speckle.Sdk.Models.Extensions;

public class AutomateFunction
{
    public async Task Run(
        IAutomationContext automationContext,
        FunctionInputs functionInputs)
    {
        var rootObject = await automationContext.ReceiveVersion();
        var allObjects = rootObject.Flatten().ToList();
        // ... validation logic ...
        automationContext.MarkRunSuccess("Validation passed.");
    }
}

CRITICAL: The C# SDK uses IAutomationContext (interface), NOT a concrete class. The Flatten() extension method from Speckle.Sdk.Models.Extensions replaces the manual flatten_base() pattern used in Python.


Input Schema Definition

Python (Pydantic)

class FunctionInputs(AutomateBase):
    category_filter: str = Field(
        title="Category Filter",
        description="Only check objects in this category"
    )
    max_count: int = Field(
        default=100, title="Maximum Count", ge=1, le=10000
    )
    api_key: SecretStr = Field(title="External API Key")
    strict_mode: bool = Field(
        default=False, title="Strict Mode",
        description="Fail on warnings too"
    )

C# (DataAnnotations)

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

public readonly struct FunctionInputs
{
    [Required]
    public string CategoryFilter { get; init; }

    [DefaultValue(100)]
    [Range(1, 10000)]
    public int MaxCount { get; init; }

    [Required]
    [Secret]
    public string ApiKey { get; init; }

    [DefaultValue(false)]
    public bool StrictMode { get; init; }
}

ALWAYS keep inputs flat and simple. Deeply nested objects, lists of objects, or union types may NOT render correctly in the Speckle UI form.


Error Reporting

Three Outcome States

State Cause Meaning
Success mark_run_success() called Model meets all criteria
Failed mark_run_failed() called Intentional validation failure — model does NOT meet criteria
Exception Unhandled exception / crash Code error or platform failure

Result Visibility

  • 3D Viewer: Doughnut icon on the model with green/red pass/fail indicators
  • Project Dashboard: Automation status indicators
  • Run Cards: Detailed messages, annotated objects, file artifacts
  • Object Annotations: attach_error_to_objects() highlights specific objects in the viewer, grouped by category

File Artifacts

Call store_file_result("report.pdf") to attach files to run results. Common artifact types:

  • PDF compliance reports
  • CSV data exports
  • JSON analysis results
  • Log files

Files are stored alongside the run and downloadable from the run card in the Speckle UI.


GitHub Actions CI/CD

Auto-Generated Workflow

The deployment wizard creates .github/workflows/main.yml with:

  • Build step (pip install / dotnet build)
  • Docker container image creation
  • Container push to Speckle registry
  • Function version registration with Speckle Automate

Environment Variables (Injected by Wizard)

Variable Purpose
SPECKLE_FUNCTION_ID Identifies the function in the Speckle registry
SPECKLE_FUNCTION_TOKEN Authentication token for publishing

Deployment Flow

1. Create function via wizard (creates GitHub repo from template)
2. Modify function code locally or in Codespaces
3. Push changes to GitHub
4. Create a GitHub Release (REQUIRED for publishing)
5. GitHub Actions workflow triggers automatically:
   a. Builds Docker image
   b. Pushes image to Speckle registry
   c. Registers function version
6. Function appears in Function Library
7. Users create Automations using the function

ALWAYS create a GitHub release to publish. The workflow triggers on release creation, NOT on push.


Function Creation Wizard

Steps

  1. Navigate to the Automations tab in a Speckle Enterprise project
  2. Click "View Functions" then "New Function"
  3. Authorize GitHub OAuth (first time only)
  4. Select template: Python or C#
  5. Configure metadata:
    • Name (required) — descriptive identifier
    • Description (required) — supports Markdown
    • Avatar/Logo (optional)
    • Source Application (optional) — target app compatibility
    • Tags (optional) — categorical identifiers
    • GitHub Organization — personal or organizational account
  6. Wizard automatically clones the template and injects CI/CD configuration

Requirements

  • Speckle Enterprise plan on app.speckle.systems (Automate is NOT available on free plans)
  • GitHub account with OAuth authorization for Speckle
  • Repository access for the Speckle OAuth app

Local Testing

Python

# Install dependencies
pip install -e ".[dev]"

# Run tests
pytest tests/

# Local execution (with environment variables)
cp .env.example .env
# Edit .env with your Speckle credentials
uv run python main.py

C#

# Build
dotnet build

# Run tests
dotnet test TestAutomateFunction/

ALWAYS test locally before creating a GitHub release. Use the template's test infrastructure to verify function logic.


Key Differences: Python vs C#

Aspect Python C#
Input class AutomateBase (Pydantic) readonly struct with DataAnnotations
Secret fields SecretStr [Secret] attribute
Context type AutomationContext (concrete) IAutomationContext (interface)
Entry point execute_automate_function() DI container + IAutomationRunner.Main()
Object flattening Manual flatten_base() function Flatten() extension method
Async pattern Synchronous (Python GIL) Full async/await
Package specklepy (includes automate) Speckle.Automate.Sdk (separate NuGet)

Reference Links

Official Sources