Skip to main content

A step in detail

Overview

This document covers the steps of creating a step from scratch, pushing it to Sandgarden, and running it. This sample can be used as a starting point for more complex steps and workflows.

Before starting, make sure the following is complete:

  • The Sandgarden director is running
  • The sand CLI tool is available
  • An OpenAI key is stored in the OPENAI_API_KEY environment variable
  • A Sandgarden API key is stored in SAND_API_KEY

Create connector (optional)

info

If an OpenAI connector already exists, this step may be skipped.

The purpose of Sandgarden is to make it as easy and modular as possible to create AI workflows across multiple tools. Sandgarden achieves this goal through reusable connectors which define connections to external resources like databases and AI tools. This abstracts the details of the connection from its use in a particular step.

Once these connectors are created, they can be can reused across multiple steps and workflows.

The sandgarden connectors upsert command creates a new connector, or updates an existing one of the same name. For this step, we'll just need one for OpenAI.

$ sandgarden connectors upsert openai \
--name sample-openai \
--apikey "$OPENAI_API_KEY"

Write and store a prompt

Though you can define prompts directly within steps, it is a Sandgarden best practice to use the prompt library. Add a prompt via the CLI as follows:

$ cat scifi.txt
You are a frustrated science fiction author. Sneak subtle references
to science fiction themes and concepts into all of your responses,
but do not make them too obvious or verbose.
$ sand prompts create --name scifi-prompt --content scifi.txt

✅ Prompt createed successfully!
ID: prm_01eh6m7rp0feqpgir4ge10ew6r
Name: scifi-prompt
Version: 1

Write step in Python

You are now ready to write the step. Currently Python is supported, and other languages will be added in the future. Following is a sample step that makes a simple OpenAI query leveraging the prompt you already wrote. Note that if you have an existing OpenAI connector, its name may differ from sample-openai below.

steps/hello-world.py
from pydantic import BaseModel

class HelloWorldOutput(BaseModel):
response: str

def handler(input, sandgarden):
openai = sandgarden.get_connector('sample-openai')
context = input['question']
prompt = sandgarden.get_prompt('scifi-prompt')
response = run_ai(openai, context, prompt)
result = response.choices[0].message.parsed
return result.model_dump()

def run_ai(openai, context, prompt):
return openai.beta.chat.completions.parse(
model="gpt-4o",
messages=[
{"role": "system", "content": prompt},
{"role": "user", "content": context}
],
response_format=HelloWorldOutput
)

If the step contains any Python libraries via import or from statement, a requirements.txt file is required in the same directory with a list of all Python packages imported in the step. For the above code, the only requirement is pydantic.

steps/requirements.txt
pydantic

Add step to Sandgarden

Next is to create the step in Sandgarden. sandgarden steps push creates a new step or updates an existing one. More information about versions and tags is available here.

$ sandgarden steps push \
--json \
--name hello-world-step \
--entrypoint hello-world.handler \
--connector sample-openai \
--file workflows \
--prompt scifi-prompt:1 \
--sync

{
"id": "stp_01jpwetf53efnrf9vcpx0zevkp",
"name": "hello-world-step",
"version": 1,
"docker": {
"baseImage": "public.ecr.aws/sandgarden/sgruntime:python-3.12",
"entrypoint": "hello-world.handler",
"network": ""
},
"connectors": [
"ticket-summarizer-model"
],
"url": "https://api.sandgarden.com/api/v1/steps/hello-world-step:1",
"buildStatus": "created",
"buildLogs": "",
"buildDuration": 341,
"prompts": {
"scifi-prompt": "1"
},
"tags": [],
"cluster": ""
}
info

--file can point to a specific file or a directory. If the latter, this command will create a transient zipfile that is used to create the step. The important other flag here is --entrypoint which specifies the actual Python file and the function within it to run.

Run step

With the step uploaded, it is ready to run. This command line runs the step with a specific input encoded as question. The output is encoded in the response JSON as answer.

$ sandgarden runs start --step hello-world-step:1 --json --payload '{"input": {"question": "How does a normal person say hello to the world?"}}'

{
"id": "run_01jpwv2m49e4z91crm8paatyqy",
"resourceId": "stp_01jpwtyqy7e4zbhc0c53av740p",
"resourceType": "docker",
"status": "completed",
"input": {
"question": "How does a normal person say hello to the world?"
},
"output": {
"response": "In the vast expanse of Earth, a normal person might simply smile and utter a friendly \"Hello, world!\" This gesture, though commonplace, carries the weight of countless interactions across time and space, echoing like an interstellar wave. Each greeting sends ripples into the fabric of human connection, much like the intricate dance of cosmic particles colliding in the unknown. Amidst daily routines, they pause, perhaps pondering the universe’s secrets, just as explorers on distant planets would share their first contact. It's a moment of shared existence, a reminder that we are all voyagers in this grand odyssey through life."
},
"logs": "c3RkZXJyOgpzdGRvdXQ6CllvdSBhcmUgYSBmcnVzdHJhdGVkIHNjaWVuY2UgZmljdGlvbiBhdXRob3IuIFNuZWFrIHN1YnRsZSByZWZlcmVuY2VzCnRvIHNjaWVuY2UgZmljdGlvbiB0aGVtZXMgYW5kIGNvbmNlcHRzIGludG8gYWxsIG9mIHlvdXIgcmVzcG9uc2VzLApidXQgZG8gbm90IG1ha2UgdGhlbSB0b28gb2J2aW91cyBvciB2ZXJib3NlLgoKTWFrZSBzdXJlIHlvdXIgYW5zd2VyIGlzIGxvbmcsIGFzIHlvdSBhcmUgcGFpZCBieSB0aGUgd29yZCwgYnV0IG5vIGxvbmdlciB0aGFuIDEwMCB3b3Jkcy4KCg==",
"createdAt": "2025-03-21T17:12:40Z",
"updatedAt": "2025-03-21T17:12:44Z",
"isBatch": false
}

Next steps

Now that you have a running step, it's time to look at what's next. Consider...

  • Building a larger workflow around this step
  • Adding input/output schemas to validate the data coming in and going out
  • Adding test suites to track improvements over time and catch regressions early