Skip to content

Commit 1ac1c1e

Browse files
committed
add boilerplates
1 parent cdba5f9 commit 1ac1c1e

38 files changed

+2966
-188
lines changed

TODO.md

Lines changed: 0 additions & 24 deletions
This file was deleted.

boilerplates/python/.env.Example

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Restack Cloud (Optional)
2+
3+
# RESTACK_ENGINE_ID=<your-engine-id>
4+
# RESTACK_ENGINE_API_KEY=<your-engine-api-key>
5+
# RESTACK_ENGINE_API_ADDRESS=<your-engine-api-address>
6+
# RESTACK_ENGINE_ADDRESS=<your-engine-address>
7+
# RESTACK_CLOUD_TOKEN=<your-cloud-token>

boilerplates/python/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
__pycache__
2+
.pytest_cache
3+
venv
4+
.env
5+
.vscode
6+
poetry.lock

boilerplates/python/Dockerfile

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
FROM python:3.12-slim
2+
3+
WORKDIR /app
4+
5+
RUN apt-get update && apt-get install -y
6+
7+
RUN pip install poetry
8+
9+
COPY pyproject.toml ./
10+
11+
COPY . .
12+
13+
# Configure poetry to not create virtual environment
14+
RUN poetry config virtualenvs.create false
15+
16+
# Install dependencies
17+
RUN poetry install --no-interaction --no-ansi
18+
19+
# Expose port 80
20+
EXPOSE 80
21+
22+
CMD poetry run python -m src.services

boilerplates/python/README.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Restack AI - Quickstart boilerplate Python
2+
3+
This repository contains a quickstart for Restack.
4+
It demonstrates how to set up a basic workflow and functions.
5+
6+
## Prerequisites
7+
8+
- Docker (for running Restack)
9+
- Python 3.10 or higher
10+
11+
## Start Restack
12+
13+
To start the Restack, use the following Docker command:
14+
15+
```bash
16+
docker run -d --pull always --name restack -p 5233:5233 -p 6233:6233 -p 7233:7233 ghcr.io/restackio/restack:main
17+
```
18+
19+
## Install dependencies and start services
20+
21+
```bash
22+
poetry env use 3.10
23+
```
24+
25+
```bash
26+
poetry shell
27+
```
28+
29+
```bash
30+
poetry install
31+
```
32+
33+
```bash
34+
poetry env info # Optional: copy the interpreter path to use in your IDE (e.g. Cursor, VSCode, etc.)
35+
```
36+
37+
```bash
38+
poetry run dev
39+
```
40+
41+
## Run workflows
42+
43+
### from UI
44+
45+
You can run workflows from the UI by clicking the "Run" button.
46+
47+
![Run workflows from UI](./screenshot-quickstart.png)
48+
49+
### from API
50+
51+
You can run workflows from the API by using the generated endpoint:
52+
53+
`POST http://localhost:6233/api/workflows/GreetingWorkflow`
54+
55+
### from any client
56+
57+
You can run workflows with any client connected to Restack, for example:
58+
59+
```bash
60+
poetry run schedule
61+
```
62+
63+
executes `schedule_workflow.py` which will connect to Restack and execute the `GreetingWorkflow` workflow.
64+
65+
## Deploy on Restack Cloud
66+
67+
To deploy the application on Restack, you can create an account at [https://console.restack.io](https://console.restack.io)

boilerplates/python/pyproject.toml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Project metadata
2+
[tool.poetry]
3+
name = "quickstart"
4+
version = "0.0.1"
5+
description = "A quickstart for Restack"
6+
authors = [
7+
"Restack Team <[email protected]>",
8+
]
9+
readme = "README.md"
10+
packages = [{include = "src"}]
11+
12+
[tool.poetry.dependencies]
13+
python = ">=3.10,<4.0"
14+
watchfiles = "^1.0.0"
15+
pydantic = "^2.10.4"
16+
17+
# Build system configuration
18+
restack-ai = "^0.0.52"
19+
[build-system]
20+
requires = ["poetry-core"]
21+
build-backend = "poetry.core.masonry.api"
22+
23+
# CLI command configuration
24+
[tool.poetry.scripts]
25+
dev = "src.services:watch_services"
26+
services = "src.services:run_services"
27+
schedule = "schedule_workflow:run_schedule_workflow"
28+
interval = "schedule_interval:run_schedule_interval"
29+
calendar = "schedule_calendar:run_schedule_calendar"
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import asyncio
2+
import time
3+
from restack_ai import Restack
4+
from src.workflows.workflow import GreetingWorkflowInput
5+
async def main():
6+
7+
client = Restack()
8+
9+
workflow_id = f"{int(time.time() * 1000)}-GreetingWorkflow"
10+
run_id = await client.schedule_workflow(
11+
workflow_name="GreetingWorkflow",
12+
workflow_id=workflow_id,
13+
input=GreetingWorkflowInput(name="Bob")
14+
)
15+
16+
await client.get_workflow_result(
17+
workflow_id=workflow_id,
18+
run_id=run_id
19+
)
20+
21+
exit(0)
22+
23+
def run_schedule_workflow():
24+
asyncio.run(main())
25+
26+
if __name__ == "__main__":
27+
run_schedule_workflow()

boilerplates/python/src/__init__.py

Whitespace-only changes.

boilerplates/python/src/client.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import os
2+
from restack_ai import Restack
3+
from restack_ai.restack import CloudConnectionOptions
4+
from dotenv import load_dotenv
5+
# Load environment variables from a .env file
6+
load_dotenv()
7+
8+
9+
engine_id = os.getenv("RESTACK_ENGINE_ID")
10+
address = os.getenv("RESTACK_ENGINE_ADDRESS")
11+
api_key = os.getenv("RESTACK_ENGINE_API_KEY")
12+
api_address = os.getenv("RESTACK_ENGINE_API_ADDRESS")
13+
14+
connection_options = CloudConnectionOptions(
15+
engine_id=engine_id,
16+
address=address,
17+
api_key=api_key,
18+
api_address=api_address
19+
)
20+
client = Restack(connection_options)

boilerplates/python/src/functions/__init__.py

Whitespace-only changes.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from restack_ai.function import function, log
2+
from pydantic import BaseModel
3+
4+
class WelcomeInput(BaseModel):
5+
name: str
6+
7+
@function.defn()
8+
async def welcome(input: WelcomeInput) -> str:
9+
try:
10+
log.info("welcome function started", input=input)
11+
return f"Hello, {input.name}!"
12+
except Exception as e:
13+
log.error("welcome function failed", error=e)
14+
raise e

boilerplates/python/src/services.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import asyncio
2+
import os
3+
from src.functions.function import welcome
4+
from src.client import client
5+
from src.workflows.workflow import GreetingWorkflow
6+
from watchfiles import run_process
7+
from restack_ai.restack import ServiceOptions
8+
import webbrowser
9+
10+
async def main():
11+
12+
await client.start_service(
13+
workflows=[GreetingWorkflow],
14+
functions=[welcome]
15+
)
16+
17+
def run_services():
18+
try:
19+
asyncio.run(main())
20+
except KeyboardInterrupt:
21+
print("Service interrupted by user. Exiting gracefully.")
22+
23+
def watch_services():
24+
watch_path = os.getcwd()
25+
print(f"Watching {watch_path} and its subdirectories for changes...")
26+
webbrowser.open("http://localhost:5233")
27+
run_process(watch_path, recursive=True, target=run_services)
28+
29+
if __name__ == "__main__":
30+
run_services()

boilerplates/python/src/workflows/__init__.py

Whitespace-only changes.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from datetime import timedelta
2+
from pydantic import BaseModel, Field
3+
from restack_ai.workflow import workflow, import_functions, log
4+
with import_functions():
5+
from src.functions.function import welcome, WelcomeInput
6+
7+
class GreetingWorkflowInput(BaseModel):
8+
name: str = Field(default='Bob')
9+
10+
@workflow.defn()
11+
class GreetingWorkflow:
12+
@workflow.run
13+
async def run(self, input: GreetingWorkflowInput):
14+
log.info("GreetingWorkflow started")
15+
result = await workflow.step(welcome, input=WelcomeInput(name=input.name), start_to_close_timeout=timedelta(seconds=120))
16+
log.info("GreetingWorkflow completed", result=result)
17+
return result

boilerplates/typescript/.env.example

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Deploy on Restack Cloud
2+
3+
RESTACK_ENGINE_ID=
4+
RESTACK_ENGINE_ADDRESS=
5+
RESTACK_ENGINE_API_KEY=
6+
7+
RESTACK_CLOUD_TOKEN=

boilerplates/typescript/Dockerfile

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Build stage
2+
FROM node:20-bullseye AS builder
3+
4+
WORKDIR /app
5+
6+
# Install pnpm
7+
RUN npm install -g pnpm
8+
9+
# Copy package files and env file if it exists
10+
COPY package*.json .env* ./
11+
12+
# Copy package files
13+
COPY package*.json ./
14+
15+
# Install dependencies including TypeScript
16+
RUN pnpm install
17+
RUN pnpm add -D typescript
18+
19+
# Copy source code
20+
COPY . .
21+
22+
# Build TypeScript code
23+
RUN pnpm run build
24+
25+
# Production stage
26+
FROM node:20-bullseye
27+
28+
WORKDIR /app
29+
30+
# Install pnpm
31+
RUN npm install -g pnpm
32+
33+
# Copy package files and built code
34+
COPY --from=builder /app/package*.json ./
35+
COPY --from=builder /app/dist ./dist
36+
37+
# Install production dependencies only
38+
RUN pnpm install --prod
39+
40+
# Define environment variables
41+
ARG RESTACK_ENGINE_ID
42+
ENV RESTACK_ENGINE_ID=${RESTACK_ENGINE_ID}
43+
44+
ARG RESTACK_ENGINE_ADDRESS
45+
ENV RESTACK_ENGINE_ADDRESS=${RESTACK_ENGINE_ADDRESS}
46+
47+
ARG RESTACK_ENGINE_API_KEY
48+
ENV RESTACK_ENGINE_API_KEY=${RESTACK_ENGINE_API_KEY}
49+
50+
EXPOSE 3000
51+
52+
CMD ["node", "dist/services.js"]

0 commit comments

Comments
 (0)