Skip to content

Commit 65418be

Browse files
committed
Add a reference project that showcases the new waitpoint primitive
This reference project shows a possible approach to implement workflows using Trigger.dev and ReactFlow. It makes use of the Trigger.dev Realtime API and the new waitpoint primitive to implement a human-in-the-loop approach for approving the result of an AI workflow.
1 parent 61232ab commit 65418be

28 files changed

+2874
-76
lines changed

pnpm-lock.yaml

Lines changed: 1407 additions & 75 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

references/nextjs-realtime/trigger.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { rscExtension } from "@trigger.dev/rsc";
33
import { AISDKExporter } from "langsmith/vercel";
44

55
export default defineConfig({
6-
project: "proj_bzhdaqhlymtuhlrcgbqy",
6+
project: "proj_jlodmrnldlncmkmscdyu",
77
dirs: ["./src/trigger"],
88
telemetry: {
99
exporters: [new AISDKExporter()],
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
TRIGGER_SECRET_KEY=
2+
TRIGGER_API_URL=
3+
NEXT_PUBLIC_TRIGGER_API_URL=
4+
OPENAI_API_KEY=
5+
ELEVENLABS_API_KEY=
6+
AWS_ACCESS_KEY_ID=
7+
AWS_SECRET_ACCESS_KEY=
8+
AWS_ENDPOINT_URL_S3=
9+
AWS_ENDPOINT_URL_IAM=
10+
AWS_REGION=
11+
SLACK_WEBHOOK_URL=
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.*
7+
.yarn/*
8+
!.yarn/patches
9+
!.yarn/plugins
10+
!.yarn/releases
11+
!.yarn/versions
12+
13+
# testing
14+
/coverage
15+
16+
# next.js
17+
/.next/
18+
/out/
19+
20+
# production
21+
/build
22+
23+
# misc
24+
.DS_Store
25+
*.pem
26+
27+
# debug
28+
npm-debug.log*
29+
yarn-debug.log*
30+
yarn-error.log*
31+
.pnpm-debug.log*
32+
33+
# env files (can opt-in for committing if needed)
34+
.env*
35+
!.env.example
36+
37+
# vercel
38+
.vercel
39+
40+
# typescript
41+
*.tsbuildinfo
42+
next-env.d.ts
43+
44+
.trigger
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# An AI workflow with a human-in-the-loop approval step
2+
3+
This reference project shows a possible approach to implement workflows using Trigger.dev and [ReactFlow](https://reactflow.dev/).
4+
It makes use of the Trigger.dev Realtime API and the new waitpoint token feature to implement a human-in-the-loop workflow.
5+
6+
## Getting Started
7+
8+
This guide assumes that you have followed the [Contributing.md](https://github.com/triggerdotdev/trigger.dev/blob/main/CONTRIBUTING.md#setup) instructions to set up a local Trigger.dev instance. If not, please complete the setup before continuing.
9+
10+
1. Run the main Trigger.dev webapp:
11+
12+
```bash
13+
pnpm run dev --filter webapp
14+
```
15+
16+
2. Optionally, build the CLI and SDK if you are working on them and applying changes:
17+
18+
```bash
19+
pnpm run dev --filter trigger.dev --filter "@trigger.dev/*"
20+
```
21+
22+
3. Login with the CLI:
23+
24+
```bash
25+
cd references/trigger-flow
26+
pnpm exec trigger login -a http://localhost:3030
27+
```
28+
29+
Optionally, you can use the `profile` flag to create a new profile:
30+
31+
```bash
32+
pnpm exec trigger login -a http://localhost:3030 --profile local
33+
```
34+
35+
Note that you'll need to use this profile for the subsequent commands.
36+
37+
4. Run the CLI
38+
39+
```bash
40+
pnpm exec trigger dev
41+
```
42+
43+
You should see now the `dev` command spitting out messages, including that it's started a background worker.
44+
45+
5. Run the Trigger Flow app:
46+
47+
```bash
48+
pnpm run dev
49+
```
50+
51+
Open [http://localhost:3000](http://localhost:3000) on your browser to checkout the workflow.
52+
53+
## Learn More
54+
55+
To learn more about the technologies used in this project, check out the following resources:
56+
57+
- [Trigger.dev Docs](https://trigger.dev/docs) - learn about Trigger.dev and its features
58+
- [Trigger.dev Waitpoint Token Docs](https://trigger.dev/docs/wait-for-token) - learn about waitpoint tokens in Trigger.dev and human-in-the-loop flows
59+
- [Trigger.dev Realtime Docs](https://trigger.dev/docs/realtime) - learn about the Realtime feature of Trigger.dev
60+
- [Trigger.dev Realtime Streams](https://trigger.dev/docs/realtime/streams) - learn about the different types of streams available in Trigger.dev
61+
- [ReactFlow Docs](https://reactflow.dev/learn) - learn about building interactive diagrams using ReactFlow
62+
- [React Hooks for Trigger.dev](https://trigger.dev/docs/frontend/react-hooks) - learn about the React hooks provided by Trigger.dev
63+
- [ElevenLabs SDK](https://elevenlabs.io/docs/overview) - learn about ElevenLabs' AI audio capabilities
64+
- [AI SDK Documentation](https://sdk.vercel.ai/docs/introduction) - learn about the AI SDK for working with LLMs
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import type { NextConfig } from "next";
2+
3+
const nextConfig: NextConfig = {
4+
/* config options here */
5+
};
6+
7+
export default nextConfig;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"name": "waitpoint-tokens",
3+
"version": "0.1.0",
4+
"private": true,
5+
"scripts": {
6+
"dev": "next dev --turbopack",
7+
"trigger:dev": "trigger dev",
8+
"build": "next build",
9+
"start": "next start",
10+
"lint": "next lint"
11+
},
12+
"dependencies": {
13+
"@ai-sdk/openai": "^1.3.4",
14+
"@aws-sdk/client-s3": "^3.777.0",
15+
"@aws-sdk/s3-request-presigner": "^3.777.0",
16+
"@trigger.dev/react-hooks": "workspace:*",
17+
"@trigger.dev/sdk": "workspace:*",
18+
"@xyflow/react": "^12.4.4",
19+
"ai": "^4.2.8",
20+
"clsx": "^2.1.1",
21+
"elevenlabs": "^1.55.0",
22+
"html-to-text": "^9.0.5",
23+
"lucide-react": "^0.484.0",
24+
"next": "15.2.4",
25+
"react": "^19.0.0",
26+
"react-dom": "^19.0.0",
27+
"react-tippy": "^1.4.0",
28+
"tailwind-merge": "^3.2.0"
29+
},
30+
"devDependencies": {
31+
"@tailwindcss/postcss": "^4",
32+
"@trigger.dev/build": "workspace:*",
33+
"@types/html-to-text": "^9.0.4",
34+
"@types/node": "^20",
35+
"@types/react": "^19",
36+
"@types/react-dom": "^19",
37+
"tailwindcss": "^4",
38+
"trigger.dev": "workspace:*",
39+
"typescript": "^5"
40+
}
41+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const config = {
2+
plugins: ["@tailwindcss/postcss"],
3+
};
4+
5+
export default config;
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
"use server";
2+
3+
import type { articleWorkflow } from "@/trigger/articleWorkflow";
4+
import type { ReviewPayload } from "@/trigger/reviewSummary";
5+
import { tasks, wait } from "@trigger.dev/sdk/v3";
6+
7+
// A user identifier that could be fetched from your auth mechanism.
8+
// This is out of scope for this example, so we just hardcode it.
9+
const user = "reactflowtest";
10+
const userTag = `user_${user}`;
11+
12+
const randomStr = (length: number) =>
13+
[...Array(length)]
14+
.map(
15+
() =>
16+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"[
17+
Math.floor(Math.random() * 62)
18+
]
19+
)
20+
.join("");
21+
22+
export async function triggerArticleWorkflow(prevState: any, formData: FormData) {
23+
const articleUrl = formData.get("articleUrl") as string;
24+
const uniqueTag = `reactflow_${randomStr(20)}`;
25+
26+
const reviewWaitpointToken = await wait.createToken({
27+
tags: [uniqueTag, userTag],
28+
timeout: "1h",
29+
idempotencyKey: `review-summary-${uniqueTag}`,
30+
});
31+
32+
const handle = await tasks.trigger<typeof articleWorkflow>(
33+
"article-workflow",
34+
{
35+
articleUrl,
36+
approvalWaitpointTokenId: reviewWaitpointToken.id,
37+
},
38+
{
39+
tags: [uniqueTag, userTag],
40+
}
41+
);
42+
43+
return {
44+
articleUrl,
45+
runId: handle.id,
46+
runTag: uniqueTag,
47+
reviewWaitpointTokenId: reviewWaitpointToken.id,
48+
};
49+
}
50+
51+
export async function approveArticleSummary(tokenId: string) {
52+
await wait.completeToken<ReviewPayload>(
53+
{ id: tokenId },
54+
{
55+
approved: true,
56+
approvedAt: new Date(),
57+
approvedBy: user,
58+
}
59+
);
60+
}
61+
62+
export async function rejectArticleSummary(tokenId: string) {
63+
await wait.completeToken<ReviewPayload>(
64+
{ id: tokenId },
65+
{
66+
approved: false,
67+
rejectedAt: new Date(),
68+
rejectedBy: user,
69+
reason: "It's no good",
70+
}
71+
);
72+
}
25.3 KB
Binary file not shown.

0 commit comments

Comments
 (0)