Build your first feature
Almost every feature on PEACH — trending lists, recommendations, related content, search — is the same shape:
[Raw data] → [Task: process + store] → [Storage] → [Endpoint: read + serve]
- Tasks run on a schedule. They do the heavy lifting: query the raw events, run the algorithm, and write the result somewhere fast to read.
- Storage — usually Redis, sometimes Milvus for vectors — holds the pre-computed result.
- Endpoints serve HTTP requests by reading what the task already computed. They stay lightweight, because they run on the critical path of somebody's page load.
The important part is the split. Anything expensive happens on the task's schedule, not in the request. An endpoint that queries raw data itself will be too slow, and will get slower as your traffic grows.
A worked example
The peach-templates repository has
this as a complete, runnable feature — Trending Content:
- A task runs hourly, queries the last 24 hours of media events, ranks content by how many distinct users engaged with it, and writes the top 10 to Redis.
- An endpoint reads that list back and returns it as JSON.
That is the whole pattern, in about a hundred lines. Start there:
| Folder | What it shows |
|---|---|
demo/ |
the feature built with peach.conf + notebooks |
demo_decorators/ |
the same feature built with @peach.task / @peach.endpoint |
The two ways to write it
Both routes are supported, and they produce the same running system.
With peach.conf and notebooks
The logic lives in a library package; the notebooks are thin wrappers that
define a task() and an endpoint() function calling into it; YAML files say
when the task runs and where the endpoint is served.
peach.conf/
tasks.yaml # when the task runs
endpoints.yaml # the endpoint's url and components
demo_libs/
trending.py # the actual logic
notebooks/
task_compute_trending.ipynb # thin wrapper -> demo_libs
endpoint_get_trending.ipynb # thin wrapper -> demo_libs
Keeping the notebooks thin is worth the discipline: the logic stays testable, and a change to it produces a readable git diff rather than a re-serialised notebook.
# peach.conf/tasks.yaml
codops: your_codops
py_environments:
analytics:
dependencies:
- duckdb
- is-bot
tasks:
compute-trending:
notebook: notebooks/task_compute_trending.ipynb
method: task
py_env: analytics
cron: "0 * * * *"
# peach.conf/endpoints.yaml
codops: your_codops
endpoints:
get-trending:
url: /trending
py_env: analytics
components:
main:
notebook: notebooks/endpoint_get_trending.ipynb
method: endpoint
See Tasks and scheduling and
Recommendation API for everything these
files can express, and Python environments for
py_environments.
dependencies: is gone
Older examples — including the ones currently in peach-templates — declare
a top-level dependencies: list. That form has been removed and is now
rejected by the config schema. Declare dependencies under py_environments
and select one with py_env, as above.
With the decorators
The newer route collapses all of that into one plain Python file under a
peach/ folder — no YAML, no notebook:
peach/
codops.txt # a single line naming your codops
trending.py # logic + @peach.task + @peach.endpoint
from pipe_algorithms_lib.compute import peach
def compute_trending(days: int = 1):
... # the logic, same as the library version
@peach.task(py_env="analytics", cron="0 * * * *")
def compute_trending_task():
compute_trending(days=1)
@peach.endpoint(name="get-trending", route_prefix="/trending", py_env="analytics")
def get_trending_endpoint(amount: int = 10):
...
Three things to know before choosing this route:
- Helpers have to live in the same file. Generation copies the whole source
file, and only
peach/*.pyis scanned — so a function shared by the task and the endpoint sits alongside them rather than in a package they import. - A
py_envstill has to be declared in apeach.conf. The decorators can select an environment but not create one, so anything beyondbaseneeds apeach.conffile somewhere in the same codops declaring it. - A scheduled task is called with no arguments, so its inputs come from inside the function or its defaults.
Full detail on both: Decorator tasks and Decorator endpoints.
Getting it running
- Write and try the logic in PEACH Lab, where your codops' data and credentials are already available.
- Push to your repository's default branch. The CI validates the configuration, builds an image, and deploys the tag to your dev environment — see Going to Production.
- Check the task ran in Prefect, and the endpoint answers.
- Promote the tag to production from the ops dashboard or
/opsin Slack.
Where to go next
- Querying event data — the raw material most tasks start from.
- Redis & Redis Insight — where the computed result usually goes.
- Vector Embeddings and Introduction to algorithms — for features that need more than a ranking.
- GPU & other resources — when the task outgrows its defaults.