INTEGRITY Документация

Разметка изображений с участием человека через waitForEvent

Этот пример показывает, как использовать waitForEvent() API в Cloudflare Workflows для добавления шага с участием человека. Workflow запускается загрузкой изображения, во время которой метаданные сохраняются в базе данных D1. Затем Workflow ожидает подтверждения от пользователя и после подтверждения использует Workers AI для генерации тегов изображения, которые сохраняются в базе данных. Прилагаемое фронтенд-приложение на Next.js обеспечивает процесс загрузки изображения и подтверждения.

Обзор Workflow

В этом Workflow мы моделируем сценарий, в котором загруженное изображение требует одобрения человеком перед обработкой с помощью ИИ. Изображение загружается в R2, после чего Workflow выполняет следующие шаги:

  1. Сохраняет метаданные изображений в базе данных D1.
  2. Приостанавливает выполнение с помощью waitForEvent() и ожидает внешнего события, отправленного с фронтенда Next.js, которое указывает на одобрение или отклонение.
  3. В случае одобрения Workflow использует Workers AI для генерации тегов изображения и сохраняет их в базе данных D1.
  4. В случае отклонения Workflow завершается без дальнейших действий.

Этот паттерн полезен в сценариях, где определённые операции не должны выполняться без явного согласия человека, что добавляет дополнительный уровень контроля и безопасности.

Интеграция фронтенда

Этот пример включает фронтенд-приложение на Next.js, которое обеспечивает процесс загрузки и подтверждения изображений. Фронтенд предоставляет интерфейс для загрузки изображений, их просмотра, а также одобрения или отклонения. При загрузке изображения приложение запускает Cloudflare Workflow, который затем управляет следующими шагами, включая ожидание подтверждения от пользователя и тегирование изображений на основе ИИ после одобрения.

См. /nextjs-workflow-frontend папка в репозиторий GitHub ↗ с полным описанием реализации клиентской части и развёртывания.

Workflow index.ts

index.ts файл определяет основную логику Cloudflare Workflow, отвечающего за обработку загрузки изображений, ожидание одобрения человеком и выполнение тегирования изображений на основе ИИ после одобрения. Он расширяет WorkflowEntrypoint класс и реализует run() метод.

Полную реализацию index.ts файл, обратитесь к репозиторий GitHub ↗.

export class MyWorkflow extends WorkflowEntrypoint {
	db;

	async run(event, step) {
		this.db = new DatabaseService(this.env.DB);
		const { imageKey } = event.payload;

		await step.do("Insert image name into database", async () => {
			await this.db.insertImage(imageKey, event.instanceId);
		});

		const waitForApproval = await step.waitForEvent(
			"Wait for AI Image tagging approval",
			{
				type: "approval-for-ai-tagging",
				timeout: "5 minute",
			},
		);

		const approvalPayload = waitForApproval.payload;
		if (approvalPayload?.approved) {
			const aiTags = await step.do("Generate AI tags", async () => {
				const image = await this.env.workflow_demo_bucket.get(imageKey);
				if (!image) throw new Error("Image not found");

				const arrayBuffer = await image.arrayBuffer();
				const uint8Array = new Uint8Array(arrayBuffer);

				const input = {
					image: Array.from(uint8Array),
					prompt: AI_CONFIG.PROMPT,
					max_tokens: AI_CONFIG.MAX_TOKENS,
				};

				const response = await this.env.AI.run(AI_CONFIG.MODEL, input);
				return response.description;
			});

			await step.do("Update DB with AI tags", async () => {
				await this.db.updateImageTags(event.instanceId, aiTags);
			});
		}
	}
}
export class MyWorkflow extends WorkflowEntrypoint<Env, WorkflowParams> {
	private db!: DatabaseService;

	async run(event: WorkflowEvent<WorkflowParams>, step: WorkflowStep) {
		this.db = new DatabaseService(this.env.DB);
		const { imageKey } = event.payload;

		await step.do('Insert image name into database', async () => {
			await this.db.insertImage(imageKey, event.instanceId);
		});

		const waitForApproval = await step.waitForEvent('Wait for AI Image tagging approval', {
			type: 'approval-for-ai-tagging',
			timeout: '5 minute',
		});

		const approvalPayload = waitForApproval.payload as ApprovalRequest;
		if (approvalPayload?.approved) {
			const aiTags = await step.do('Generate AI tags', async () => {
				const image = await this.env.workflow_demo_bucket.get(imageKey);
				if (!image) throw new Error('Image not found');

				const arrayBuffer = await image.arrayBuffer();
				const uint8Array = new Uint8Array(arrayBuffer);

				const input = {
					image: Array.from(uint8Array),
					prompt: AI_CONFIG.PROMPT,
					max_tokens: AI_CONFIG.MAX_TOKENS,
				};

				const response = await this.env.AI.run(AI_CONFIG.MODEL, input);
				return response.description;
			});

			await step.do('Update DB with AI tags', async () => {
				await this.db.updateImageTags(event.instanceId, aiTags);
			});
		}
	}
}

Workflow wrangler.jsonc

Конфигурация Workflow задаётся в wrangler.jsonc файл. Этот файл включает привязки для R2-бакета, базы данных D1, Workers AI и самого Workflow. Убедитесь, что все необходимые привязки и переменные окружения настроены правильно и соответствуют вашему аккаунту Cloudflare и используемым сервисам.

{
	"$schema": "node_modules/wrangler/config-schema.json",
	"name": "workflows-waitforevent",
	"main": "src/index.ts",
	// Set this to today's date
	"compatibility_date": "2026-08-28",
	"observability": {
		"enabled": true,
		"head_sampling_rate": 1,
	},
	"ai": {
		"binding": "AI"
	},
	"workflows": [
		{
			"name": "workflows-starter",
			"binding": "MY_WORKFLOW",
			"class_name": "MyWorkflow"
		}
	],
	"r2_buckets": [
		{
			"bucket_name": "workflow-demo",
			"binding": "workflow_demo_bucket"
		}
	],
	"d1_databases": [
		{
			"binding": "DB",
			"database_name": "workflows-demo-d1",
			"database_id": "66e4fbe9-06ac-4548-abba-2dc42088e13a"
		}
	]
}
"$schema" = "node_modules/wrangler/config-schema.json"
name = "workflows-waitforevent"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-08-28"

[observability]
enabled = true
head_sampling_rate = 1

[ai]
binding = "AI"

[[workflows]]
name = "workflows-starter"
binding = "MY_WORKFLOW"
class_name = "MyWorkflow"

[[r2_buckets]]
bucket_name = "workflow-demo"
binding = "workflow_demo_bucket"

[[d1_databases]]
binding = "DB"
database_name = "workflows-demo-d1"
database_id = "66e4fbe9-06ac-4548-abba-2dc42088e13a"

Чтобы получить доступ к исходному коду, инструкциям по развертыванию и эталонной архитектуре, посетите репозиторий GitHub ↗. Этот ресурс содержит все инструменты и сведения, необходимые для эффективной реализации Workflow и клиентского приложения на Next.js.