From 77d19dd752c40758774408ffa046dd81eae6a27e Mon Sep 17 00:00:00 2001 From: Jason Farber Date: Wed, 7 May 2025 10:46:40 +0100 Subject: [PATCH 1/6] first episode --- .../workflows-course/series/index.mdx | 9 + .../workflows-course/series/workflows-1.mdx | 155 ++++++++ .../workflows-course/series/workflows-2.mdx | 347 ++++++++++++++++++ .../workflows-course/series/workflows-3.mdx | 347 ++++++++++++++++++ .../learning-paths/workflows-course.json | 9 + .../workflows-series-navigation.mdx | 14 + 6 files changed, 881 insertions(+) create mode 100644 src/content/docs/learning-paths/workflows-course/series/index.mdx create mode 100644 src/content/docs/learning-paths/workflows-course/series/workflows-1.mdx create mode 100644 src/content/docs/learning-paths/workflows-course/series/workflows-2.mdx create mode 100644 src/content/docs/learning-paths/workflows-course/series/workflows-3.mdx create mode 100644 src/content/learning-paths/workflows-course.json create mode 100644 src/content/partials/learning-paths/workflows-series-navigation.mdx diff --git a/src/content/docs/learning-paths/workflows-course/series/index.mdx b/src/content/docs/learning-paths/workflows-course/series/index.mdx new file mode 100644 index 00000000000000..0df7484bd14d08 --- /dev/null +++ b/src/content/docs/learning-paths/workflows-course/series/index.mdx @@ -0,0 +1,9 @@ +--- +pcx_content_type: navigation +title: Overview +sidebar: + order: 4 + group: + hideIndex: true +tableOfContents: false +--- diff --git a/src/content/docs/learning-paths/workflows-course/series/workflows-1.mdx b/src/content/docs/learning-paths/workflows-course/series/workflows-1.mdx new file mode 100644 index 00000000000000..15e9f1f3787807 --- /dev/null +++ b/src/content/docs/learning-paths/workflows-course/series/workflows-1.mdx @@ -0,0 +1,155 @@ +--- +pcx_content_type: navigation +title: Introduction to Workflows +sidebar: + order: 2 +tableOfContents: false +--- + +import { Render, Tabs, TabItem, Stream, Card } from "~/components"; + + + + + Cloudflare Workflows provides durable execution capabilities, allowing developers to create reliable, repeatable workflows that run in the background. Workflows are designed to resume execution even if the underlying compute fails, ensuring that tasks complete eventually. They are built on top of Cloudflare Workers and handle scaling and provisioning automatically. + + Workflows are triggered by events, such as Event Notifications consumed from a Queue, HTTP requests, another Worker, or even scheduled timers. Individual steps within a Workflow are designed as retriable units of work. The state is persisted between steps, allowing workflows to resume from the last successful step after failures. Workflows automatically emit metrics for each step, aiding in debugging and observability. + + + + + **Related content** + + If you want to dive into detail, refer to the following pages: + + - [Source code for the Punderful repository](https://github.com/craigsdennis/punderful-workflows) + - [Cloudflare Workflows](/workflows/) + - [Cloudflare Workers AI](/workers-ai/) + + + + + + + Punderful is a sample application that showcases the use of various Cloudflare primitives, including Workers, D1, Vectorize, Workers AI, and Workflows. The application displays a list of puns stored in a D1 database. + + The homepage lists the latest puns stored in D1. The application also includes a semantic search feature powered by Vectorize. To perform a search: + + 1. Go to the Punderful search page. + 2. Type a search query in the "Search for a pun..." input box. + 3. Observe the search results appearing instantly below the search box. + + To demonstrate adding a new pun: + + 1. Go to the Punderful creation page. + 2. Enter a new pun in the "Enter your pun here..." textarea. + 3. Observe the preview of the pun updating as you type. + 4. Click the "Submit Pun" button. + + When a new pun is submitted, it needs to be indexed in Vectorize for the semantic search to work. This indexing process involves creating embeddings from the pun text. This is a task suitable for background processing using Cloudflare Workflows, avoiding delays for the user in the request-response loop. + + ### Implementing a Workflow to Process New Puns + + A workflow is implemented to handle the background processing required when a new pun is submitted. + + #### Triggering the Workflow + + When a new pun is submitted via the `/api/puns` endpoint, the data is first inserted into the D1 database. Then, a new Workflow instance is created and triggered to perform the subsequent background tasks. + + [See here](https://github.com/craigsdennis/punderful-workflows/blob/7cec7f4bd7d6b17085cb6d6cb3e56b6a4b5b7c9d/src/index.tsx#L165) + + In this handler, `c.env.PUBLISH.create(crypto.randomUUID(), { punId, pun: payload.pun })` creates a new instance of the workflow bound as `PUBLISH`, assigns it a unique ID, and passes the `punId` and `pun` text as the payload. + + #### Defining the Workflow Class + + The workflow logic is defined in a class that extends `WorkflowEntrypoint`. + + [See here](https://github.com/craigsdennis/punderful-workflows/blob/7cec7f4bd7d6b17085cb6d6cb3e56b6a4b5b7c9d/src/workflows/publish.ts#L12) + + The `run` method is the entrypoint for the workflow execution. It receives the `event` containing the payload and a `step` object to define individual, durable steps. + + #### Workflow Steps + + Each discrete, retriable task in the workflow is defined using `await step.do()`. + + ##### Content Moderation + + Optionally, the workflow can perform content moderation using an external service like OpenAI's moderation API if an API key is available in the environment. + + [See here](https://github.com/craigsdennis/punderful-workflows/blob/7cec7f4bd7d6b17085cb6d6cb3e56b6a4b5b7c9d/src/workflows/publish.ts#L16) + + This step calls the OpenAI moderation API. If the content is flagged as inappropriate, the pun's status is updated in the database, and a `NonRetryableError` is thrown. Throwing a `NonRetryableError` prevents the workflow from retrying this step, as the content is permanently deemed inappropriate. + + ##### Creating Embeddings + + Next, create vector embeddings for the pun text using a Workers AI model. + +[See here](https://github.com/craigsdennis/punderful-workflows/blob/7cec7f4bd7d6b17085cb6d6cb3e56b6a4b5b7c9d/src/workflows/publish.ts#L34) + + This step uses the `@cf/baai/bge-large-en-v1.5` model from Workers AI to generate a vector embedding for the `pun` text. The result (the embedding vector) is returned by the step and can be used in subsequent steps. `step.do()` ensures this step will be retried if it fails, guaranteeing that embeddings are eventually created. + + ##### Categorizing the Pun + + Optionally, use a Workers AI language model to categorize the pun. + + [See here](https://github.com/craigsdennis/punderful-workflows/blob/7cec7f4bd7d6b17085cb6d6cb3e56b6a4b5b7c9d/src/workflows/publish.ts#L41) + + This step uses the `@cf/meta/llama-3.1-8b-instruct` model with a specific system prompt to generate categories for the pun. The generated categories string is returned by the step. This step also benefits from `step.do()`'s reliability. + + ##### Adding Embeddings to Vectorize + + Insert the created pun embedding and potentially categories embedding into the Vectorize database. + + [See here](https://github.com/craigsdennis/punderful-workflows/blob/7cec7f4bd7d6b17085cb6d6cb3e56b6a4b5b7c9d/src/workflows/publish.ts#L78) + + This step uses `this.env.VECTORIZE.upsert()` to add the generated embeddings and associated metadata to the Vectorize database. This makes the pun searchable semantically. `step.do()` ensures this critical indexing step is completed reliably. + + ##### Updating Database Status + + The final step updates the status of the pun in the D1 database to indicate that it has been published and processed by the workflow. + + [See here](https://github.com/craigsdennis/punderful-workflows/blob/7cec7f4bd7d6b17085cb6d6cb3e56b6a4b5b7c9d/src/workflows/publish.ts#L104) + + This step updates the `status` column in the D1 database to 'published' for the corresponding pun ID. Once this step is complete, the pun is considered fully processed and ready to be displayed on the homepage. + + #### Workflow Bindings + + To make the `PublishWorkflow` class available to the main Worker and to provide access to necessary resources (like D1, AI, Vectorize), bindings are configured in the `wrangler.toml` file. + + [See here](https://github.com/craigsdennis/punderful-workflows/blob/main/wrangler.toml) + + This configuration defines a workflow named `publish`, binds it to the environment variable `PUBLISH`, and links it to the `PublishWorkflow` class in `src/index.ts`. It also shows bindings for Workers AI (`AI`) and Vectorize (`VECTORIZE`), which are accessed via `this.env` within the workflow. + + ### Vectorize for Semantic Search + + Vectorize is a vector database used in this application to enable semantic search for puns. It stores the vector embeddings created by Workers AI. The search functionality queries this Vectorize index to find puns similar in meaning to the user's query. + + The homepage displays recently published puns (status 'published'). The detail page for a specific pun displays "Similar Puns", which are found by querying Vectorize with the embedding of the current pun. + + ### Scalability + + Cloudflare Workers and Workflows are designed to scale automatically based on demand, handling concurrent requests and background tasks efficiently without requiring manual provisioning. + + ### Conclusion + + Cloudflare Workflows provides a powerful primitive for building reliable, durable background processes, seamlessly integrating with other Cloudflare services like D1, Workers AI, and Vectorize. This enables developers to build complex applications with robust background task handling, guaranteed execution, and built-in observability. + + + + + + diff --git a/src/content/docs/learning-paths/workflows-course/series/workflows-2.mdx b/src/content/docs/learning-paths/workflows-course/series/workflows-2.mdx new file mode 100644 index 00000000000000..feb2b3f6712d3c --- /dev/null +++ b/src/content/docs/learning-paths/workflows-course/series/workflows-2.mdx @@ -0,0 +1,347 @@ +--- +pcx_content_type: navigation +title: Introduction to Workflows - 2 +sidebar: + order: 2 +tableOfContents: false +--- + +import { Render, Tabs, TabItem, Stream, Card } from "~/components"; + + + + + In this video, we discuss Cloudflare One, our Secure Access Service Edge (SASE) platform and how it has been designed to revolutionize the corporate network and enable companies with their Zero Trust strategy. Legacy network design is struggling to address today's challenges of security, performance and monitoring needs. Many IT teams are trying to evolve their corporate network with point solutions and finding the lack of integration and performance an issue. + + + + + **Related content** + + If you want to dive into detail about modernizing your corporate network with Cloudflare, refer to the following pages: + + - [Evolving to a SASE architecture with Cloudflare](/reference-architecture/architectures/sase/) + - [Network-focused migration from VPN concentrators to Zero Trust Network Access](/reference-architecture/design-guides/network-vpn-migration/) + - [Using a Zero Trust framework to secure SaaS applications](/reference-architecture/design-guides/zero-trust-for-saas/) + - [Building Zero Trust architecture into your startup](/reference-architecture/design-guides/zero-trust-for-startups/) + + + + + # Cloudflare Workflows: Index Your Data With Background Workflows + +## Outline +- Introduction to Cloudflare Workflows +- Punderful Application Overview +- Implementing a Workflow to Process New Puns + - Triggering the Workflow + - Defining the Workflow Class + - Workflow Steps + - Content Moderation + - Creating Embeddings + - Categorizing the Pun + - Adding Embeddings to Vectorize + - Updating Database Status + - Workflow Bindings +- Vectorize for Semantic Search +- Scalability +- Conclusion + +## Introduction to Cloudflare Workflows + +Cloudflare Workflows provides durable execution capabilities, allowing developers to create reliable, repeatable workflows that run in the background. Workflows are designed to resume execution even if the underlying compute fails, ensuring that tasks complete eventually. They are built on top of Cloudflare Workers and handle scaling and provisioning automatically. + +Workflows are triggered by events, such as Event Notifications consumed from a Queue, HTTP requests, another Worker, or even scheduled timers. Individual steps within a Workflow are designed as retriable units of work. The state is persisted between steps, allowing workflows to resume from the last successful step after failures. Workflows automatically emit metrics for each step, aiding in debugging and observability. + +## Punderful Application Overview + +Punderful is a sample application that showcases the use of various Cloudflare primitives, including Workers, D1, Vectorize, Workers AI, and Workflows. The application displays a list of puns stored in a D1 database. + +The homepage lists the latest puns stored in D1. + +The application also includes a semantic search feature powered by Vectorize. To perform a search: + +1. Go to the Punderful search page. +2. Type a search query in the "Search for a pun..." input box. +3. Observe the search results appearing instantly below the search box. + +To demonstrate adding a new pun: + +1. Go to the Punderful creation page. +2. Enter a new pun in the "Enter your pun here..." textarea. +3. Observe the preview of the pun updating as you type. +4. Click the "Submit Pun" button. + +When a new pun is submitted, it needs to be indexed in Vectorize for the semantic search to work. This indexing process involves creating embeddings from the pun text. This is a task suitable for background processing using Cloudflare Workflows, avoiding delays for the user in the request-response loop. + +## Implementing a Workflow to Process New Puns + +A workflow is implemented to handle the background processing required when a new pun is submitted. + +### Triggering the Workflow + +When a new pun is submitted via the `/api/puns` endpoint, the data is first inserted into the D1 database. Then, a new Workflow instance is created and triggered to perform the subsequent background tasks. + +```typescript +// File: src/index.ts +// ... (previous code for API handlers and database insertion) + +app.post('/api/puns', async (c) => { + const payload = await c.req.json(); + console.log({ payload }); + const punId = await insertPun(c, payload.pun); + + // Trigger the workflow + const workflow = await c.env.PUBLISH.create(crypto.randomUUID(), { + punId, + pun: payload.pun, + }); + + return c.json({ workflow }); +}); + +// ... (remaining code) +``` + +In this handler, `c.env.PUBLISH.create(crypto.randomUUID(), { punId, pun: payload.pun })` creates a new instance of the workflow bound as `PUBLISH`, assigns it a unique ID, and passes the `punId` and `pun` text as the payload. + +### Defining the Workflow Class + +The workflow logic is defined in a class that extends `WorkflowEntrypoint`. + +```typescript +// File: src/index.ts + +// ... (previous code for API handlers and type definitions) + +export class PublishWorkflow extends WorkflowEntrypoint { + async run(event: WorkflowEvent, step: WorkflowStep) { + const { pun, punId } = event.payload; + + // ... (Workflow steps defined below) + } +} + +// ... (remaining code) +``` + +The `run` method is the entrypoint for the workflow execution. It receives the `event` containing the payload and a `step` object to define individual, durable steps. + +### Workflow Steps + +Each discrete, retriable task in the workflow is defined using `await step.do()`. + +#### Content Moderation + +Optionally, the workflow can perform content moderation using an external service like OpenAI's moderation API if an API key is available in the environment. + +```typescript +// File: src/index.ts +// ... (inside PublishWorkflow.run method) + + // step: [OPTIONAL] Moderate using OpenAI's free moderation API + if (this.env.OPENAI_API_KEY !== undefined) { + const moderation = await step.do('content-moderation', async () => { + const openai = new OpenAI({ apiKey: this.env.OPENAI_API_KEY }); + const moderation = await openai.moderations.create({ + model: 'omn-moderation-latest', + input: pun, + }); + return moderation; + }); + + if (moderation.results[0].flagged) { + console.warn('Pun flagged:', JSON.stringify(moderation)); + await this.env.DB.prepare(`UPDATE puns SET status=? WHERE id=?`).bind('flagged', punId).run(); + throw new Error(`Pun ${punId} failed moderation`) as NonRetryableError; + } + return true; + } else { + console.warn('OPENAI_API_KEY is not present in your environment, set it to enable moderation'); + } + +// ... (remaining workflow steps) +``` + +This step calls the OpenAI moderation API. If the content is flagged as inappropriate, the pun's status is updated in the database, and a `NonRetryableError` is thrown. Throwing a `NonRetryableError` prevents the workflow from retrying this step, as the content is permanently deemed inappropriate. + +#### Creating Embeddings + +Next, create vector embeddings for the pun text using a Workers AI model. + +```typescript +// File: src/index.ts +// ... (inside PublishWorkflow.run method, after moderation step) + + // step: Create embedding for indexing + const punEmbedding = await step.do('create-pun-embedding', async () => { + const results = await this.env.AI.run('@cf/baai/bge-large-en-v1.5', { + text: pun, + }); + return results.data[0]; + }); + +// ... (remaining workflow steps) +``` + +This step uses the `@cf/baai/bge-large-en-v1.5` model from Workers AI to generate a vector embedding for the `pun` text. The result (the embedding vector) is returned by the step and can be used in subsequent steps. `step.do()` ensures this step will be retried if it fails, guaranteeing that embeddings are eventually created. + +#### Categorizing the Pun + +Optionally, use a Workers AI language model to categorize the pun. + +```typescript +// File: src/index.ts +// ... (inside PublishWorkflow.run method, after embedding step) + + // step: Categorize + const categories = await step.do('categorize-pun', async () => { + const results = await this.env.AI.run('@cf/meta/llama-3.1-8b-instruct', { + messages: [ + { + role: 'system', + content: 'You help categorize puns for people to search for later. The user is going to give you a pun and your job is to list the most relevant categories for the pun, based on the content of the pun. Do not include words about pun in general, focus on what the content is about. For instance don\'t return "word play" or "pun" as a category. Return only the categories, comma separated. Do not include a preamble or introducdtion, just the categories.', + }, + { role: 'user', content: pun }, + ], + gateway: { + id: "punderful" + } + }); + return results.response; + }); + +// ... (remaining workflow steps) +``` + +This step uses the `@cf/meta/llama-3.1-8b-instruct` model with a specific system prompt to generate categories for the pun. The generated categories string is returned by the step. This step also benefits from `step.do()`'s reliability. + +#### Adding Embeddings to Vectorize + +Insert the created pun embedding and potentially categories embedding into the Vectorize database. + +```typescript +// File: src/index.ts +// ... (inside PublishWorkflow.run method, after categorization step) + + // Step: Add embeddings to vector store + await step.do('add-embeddings-to-vector-store', async () => { + await this.env.VECTORIZE.upsert([ + { + id: `content-${punId}`, + values: punEmbedding, + namespace: 'content', + metadata: { + punId, + pun, + categories, + }, + }, + ]); + + // Assuming categoriesEmbedding is also generated or derived + // await this.env.VECTORIZE.upsert([ + // { + // id: `categories-${punId}`, + // values: categoriesEmbedding, + // namespace: 'categories', + // metadata: { + // punId, + // pun, + // categories, + // }, + // } + // ]); + }); + +// ... (remaining workflow steps) +``` + +This step uses `this.env.VECTORIZE.upsert()` to add the generated embeddings and associated metadata to the Vectorize database. This makes the pun searchable semantically. `step.do()` ensures this critical indexing step is completed reliably. + +#### Updating Database Status + +The final step updates the status of the pun in the D1 database to indicate that it has been published and processed by the workflow. + +```typescript +// File: src/index.ts +// ... (inside PublishWorkflow.run method, after Vectorize step) + + // Step: Update the db + await step.do('update-status-to-published', async () => { + const results = await this.env.DB.prepare(`UPDATE puns SET status=? WHERE id=?`).bind('published', punId).run(); + }); + +// ... (remaining workflow logic) +``` + +This step updates the `status` column in the D1 database to 'published' for the corresponding pun ID. Once this step is complete, the pun is considered fully processed and ready to be displayed on the homepage. + +### Workflow Bindings + +To make the `PublishWorkflow` class available to the main Worker and to provide access to necessary resources (like D1, AI, Vectorize), bindings are configured in the `wrangler.toml` file. + +```toml +# File: wrangler.toml +# ... (other configurations) + +[workflows] +# Name of your workflow +name = "publish" +# Binding name env.MYWORKFLOW +binding = "PUBLISH" +# This is class that extends the Workflow class in src/index.ts +class_name = "PublishWorkflow" + +[ai] +binding = "AI" + +[vectorize] +binding = "VECTORIZE" +index_name = "puns5" # Example index name + +# ... (other bindings like d1_databases) +``` + +This configuration defines a workflow named `publish`, binds it to the environment variable `PUBLISH`, and links it to the `PublishWorkflow` class in `src/index.ts`. It also shows bindings for Workers AI (`AI`) and Vectorize (`VECTORIZE`), which are accessed via `this.env` within the workflow. + +## Vectorize for Semantic Search + +Vectorize is a vector database used in this application to enable semantic search for puns. It stores the vector embeddings created by Workers AI. The search functionality queries this Vectorize index to find puns similar in meaning to the user's query. + +The homepage displays recently published puns (status 'published'). The detail page for a specific pun displays "Similar Puns", which are found by querying Vectorize with the embedding of the current pun. + +## Scalability + +Cloudflare Workers and Workflows are designed to scale automatically based on demand, handling concurrent requests and background tasks efficiently without requiring manual provisioning. + +## Conclusion + +Cloudflare Workflows provides a powerful primitive for building reliable, durable background processes, seamlessly integrating with other Cloudflare services like D1, Workers AI, and Vectorize. This enables developers to build complex applications with robust background task handling, guaranteed execution, and built-in observability. + +[Source code for the Punderful repository](https://github.com/craigsdennis/punderful-workflows) + +## References: +- https://developers.cloudflare.com +- https://developers.cloudflare.com/workflows/ +- https://developers.cloudflare.com/workers-ai/ + + + + + + diff --git a/src/content/docs/learning-paths/workflows-course/series/workflows-3.mdx b/src/content/docs/learning-paths/workflows-course/series/workflows-3.mdx new file mode 100644 index 00000000000000..ed6aababc888b6 --- /dev/null +++ b/src/content/docs/learning-paths/workflows-course/series/workflows-3.mdx @@ -0,0 +1,347 @@ +--- +pcx_content_type: navigation +title: Introduction to Workflows - 2 +sidebar: + order: 2 +tableOfContents: false +--- + +import { Render, Tabs, TabItem, Stream, Card } from "~/components"; + + + + + In this video, we discuss Cloudflare One, our Secure Access Service Edge (SASE) platform and how it has been designed to revolutionize the corporate network and enable companies with their Zero Trust strategy. Legacy network design is struggling to address today's challenges of security, performance and monitoring needs. Many IT teams are trying to evolve their corporate network with point solutions and finding the lack of integration and performance an issue. + + + + + **Related content** + + If you want to dive into detail about modernizing your corporate network with Cloudflare, refer to the following pages: + + - [Evolving to a SASE architecture with Cloudflare](/reference-architecture/architectures/sase/) + - [Network-focused migration from VPN concentrators to Zero Trust Network Access](/reference-architecture/design-guides/network-vpn-migration/) + - [Using a Zero Trust framework to secure SaaS applications](/reference-architecture/design-guides/zero-trust-for-saas/) + - [Building Zero Trust architecture into your startup](/reference-architecture/design-guides/zero-trust-for-startups/) + + + + + # Cloudflare Workflows: Index Your Data With Background Workflows + +## Outline +- Introduction to Cloudflare Workflows +- Punderful Application Overview +- Implementing a Workflow to Process New Puns + - Triggering the Workflow + - Defining the Workflow Class + - Workflow Steps + - Content Moderation + - Creating Embeddings + - Categorizing the Pun + - Adding Embeddings to Vectorize + - Updating Database Status + - Workflow Bindings +- Vectorize for Semantic Search +- Scalability +- Conclusion + +## Introduction to Cloudflare Workflows + +Cloudflare Workflows provides durable execution capabilities, allowing developers to create reliable, repeatable workflows that run in the background. Workflows are designed to resume execution even if the underlying compute fails, ensuring that tasks complete eventually. They are built on top of Cloudflare Workers and handle scaling and provisioning automatically. + +Workflows are triggered by events, such as Event Notifications consumed from a Queue, HTTP requests, another Worker, or even scheduled timers. Individual steps within a Workflow are designed as retriable units of work. The state is persisted between steps, allowing workflows to resume from the last successful step after failures. Workflows automatically emit metrics for each step, aiding in debugging and observability. + +## Punderful Application Overview + +Punderful is a sample application that showcases the use of various Cloudflare primitives, including Workers, D1, Vectorize, Workers AI, and Workflows. The application displays a list of puns stored in a D1 database. + +The homepage lists the latest puns stored in D1. + +The application also includes a semantic search feature powered by Vectorize. To perform a search: + +1. Go to the Punderful search page. +2. Type a search query in the "Search for a pun..." input box. +3. Observe the search results appearing instantly below the search box. + +To demonstrate adding a new pun: + +1. Go to the Punderful creation page. +2. Enter a new pun in the "Enter your pun here..." textarea. +3. Observe the preview of the pun updating as you type. +4. Click the "Submit Pun" button. + +When a new pun is submitted, it needs to be indexed in Vectorize for the semantic search to work. This indexing process involves creating embeddings from the pun text. This is a task suitable for background processing using Cloudflare Workflows, avoiding delays for the user in the request-response loop. + +## Implementing a Workflow to Process New Puns + +A workflow is implemented to handle the background processing required when a new pun is submitted. + +### Triggering the Workflow + +When a new pun is submitted via the `/api/puns` endpoint, the data is first inserted into the D1 database. Then, a new Workflow instance is created and triggered to perform the subsequent background tasks. + +```typescript +// File: src/index.ts +// ... (previous code for API handlers and database insertion) + +app.post('/api/puns', async (c) => { + const payload = await c.req.json(); + console.log({ payload }); + const punId = await insertPun(c, payload.pun); + + // Trigger the workflow + const workflow = await c.env.PUBLISH.create(crypto.randomUUID(), { + punId, + pun: payload.pun, + }); + + return c.json({ workflow }); +}); + +// ... (remaining code) +``` + +In this handler, `c.env.PUBLISH.create(crypto.randomUUID(), { punId, pun: payload.pun })` creates a new instance of the workflow bound as `PUBLISH`, assigns it a unique ID, and passes the `punId` and `pun` text as the payload. + +### Defining the Workflow Class + +The workflow logic is defined in a class that extends `WorkflowEntrypoint`. + +```typescript +// File: src/index.ts + +// ... (previous code for API handlers and type definitions) + +export class PublishWorkflow extends WorkflowEntrypoint { + async run(event: WorkflowEvent, step: WorkflowStep) { + const { pun, punId } = event.payload; + + // ... (Workflow steps defined below) + } +} + +// ... (remaining code) +``` + +The `run` method is the entrypoint for the workflow execution. It receives the `event` containing the payload and a `step` object to define individual, durable steps. + +### Workflow Steps + +Each discrete, retriable task in the workflow is defined using `await step.do()`. + +#### Content Moderation + +Optionally, the workflow can perform content moderation using an external service like OpenAI's moderation API if an API key is available in the environment. + +```typescript +// File: src/index.ts +// ... (inside PublishWorkflow.run method) + + // step: [OPTIONAL] Moderate using OpenAI's free moderation API + if (this.env.OPENAI_API_KEY !== undefined) { + const moderation = await step.do('content-moderation', async () => { + const openai = new OpenAI({ apiKey: this.env.OPENAI_API_KEY }); + const moderation = await openai.moderations.create({ + model: 'omn-moderation-latest', + input: pun, + }); + return moderation; + }); + + if (moderation.results[0].flagged) { + console.warn('Pun flagged:', JSON.stringify(moderation)); + await this.env.DB.prepare(`UPDATE puns SET status=? WHERE id=?`).bind('flagged', punId).run(); + throw new Error(`Pun ${punId} failed moderation`) as NonRetryableError; + } + return true; + } else { + console.warn('OPENAI_API_KEY is not present in your environment, set it to enable moderation'); + } + +// ... (remaining workflow steps) +``` + +This step calls the OpenAI moderation API. If the content is flagged as inappropriate, the pun's status is updated in the database, and a `NonRetryableError` is thrown. Throwing a `NonRetryableError` prevents the workflow from retrying this step, as the content is permanently deemed inappropriate. + +#### Creating Embeddings + +Next, create vector embeddings for the pun text using a Workers AI model. + +```typescript +// File: src/index.ts +// ... (inside PublishWorkflow.run method, after moderation step) + + // step: Create embedding for indexing + const punEmbedding = await step.do('create-pun-embedding', async () => { + const results = await this.env.AI.run('@cf/baai/bge-large-en-v1.5', { + text: pun, + }); + return results.data[0]; + }); + +// ... (remaining workflow steps) +``` + +This step uses the `@cf/baai/bge-large-en-v1.5` model from Workers AI to generate a vector embedding for the `pun` text. The result (the embedding vector) is returned by the step and can be used in subsequent steps. `step.do()` ensures this step will be retried if it fails, guaranteeing that embeddings are eventually created. + +#### Categorizing the Pun + +Optionally, use a Workers AI language model to categorize the pun. + +```typescript +// File: src/index.ts +// ... (inside PublishWorkflow.run method, after embedding step) + + // step: Categorize + const categories = await step.do('categorize-pun', async () => { + const results = await this.env.AI.run('@cf/meta/llama-3.1-8b-instruct', { + messages: [ + { + role: 'system', + content: 'You help categorize puns for people to search for later. The user is going to give you a pun and your job is to list the most relevant categories for the pun, based on the content of the pun. Do not include words about pun in general, focus on what the content is about. For instance don\'t return "word play" or "pun" as a category. Return only the categories, comma separated. Do not include a preamble or introducdtion, just the categories.', + }, + { role: 'user', content: pun }, + ], + gateway: { + id: "punderful" + } + }); + return results.response; + }); + +// ... (remaining workflow steps) +``` + +This step uses the `@cf/meta/llama-3.1-8b-instruct` model with a specific system prompt to generate categories for the pun. The generated categories string is returned by the step. This step also benefits from `step.do()`'s reliability. + +#### Adding Embeddings to Vectorize + +Insert the created pun embedding and potentially categories embedding into the Vectorize database. + +```typescript +// File: src/index.ts +// ... (inside PublishWorkflow.run method, after categorization step) + + // Step: Add embeddings to vector store + await step.do('add-embeddings-to-vector-store', async () => { + await this.env.VECTORIZE.upsert([ + { + id: `content-${punId}`, + values: punEmbedding, + namespace: 'content', + metadata: { + punId, + pun, + categories, + }, + }, + ]); + + // Assuming categoriesEmbedding is also generated or derived + // await this.env.VECTORIZE.upsert([ + // { + // id: `categories-${punId}`, + // values: categoriesEmbedding, + // namespace: 'categories', + // metadata: { + // punId, + // pun, + // categories, + // }, + // } + // ]); + }); + +// ... (remaining workflow steps) +``` + +This step uses `this.env.VECTORIZE.upsert()` to add the generated embeddings and associated metadata to the Vectorize database. This makes the pun searchable semantically. `step.do()` ensures this critical indexing step is completed reliably. + +#### Updating Database Status + +The final step updates the status of the pun in the D1 database to indicate that it has been published and processed by the workflow. + +```typescript +// File: src/index.ts +// ... (inside PublishWorkflow.run method, after Vectorize step) + + // Step: Update the db + await step.do('update-status-to-published', async () => { + const results = await this.env.DB.prepare(`UPDATE puns SET status=? WHERE id=?`).bind('published', punId).run(); + }); + +// ... (remaining workflow logic) +``` + +This step updates the `status` column in the D1 database to 'published' for the corresponding pun ID. Once this step is complete, the pun is considered fully processed and ready to be displayed on the homepage. + +### Workflow Bindings + +To make the `PublishWorkflow` class available to the main Worker and to provide access to necessary resources (like D1, AI, Vectorize), bindings are configured in the `wrangler.toml` file. + +```toml +# File: wrangler.toml +# ... (other configurations) + +[workflows] +# Name of your workflow +name = "publish" +# Binding name env.MYWORKFLOW +binding = "PUBLISH" +# This is class that extends the Workflow class in src/index.ts +class_name = "PublishWorkflow" + +[ai] +binding = "AI" + +[vectorize] +binding = "VECTORIZE" +index_name = "puns5" # Example index name + +# ... (other bindings like d1_databases) +``` + +This configuration defines a workflow named `publish`, binds it to the environment variable `PUBLISH`, and links it to the `PublishWorkflow` class in `src/index.ts`. It also shows bindings for Workers AI (`AI`) and Vectorize (`VECTORIZE`), which are accessed via `this.env` within the workflow. + +## Vectorize for Semantic Search + +Vectorize is a vector database used in this application to enable semantic search for puns. It stores the vector embeddings created by Workers AI. The search functionality queries this Vectorize index to find puns similar in meaning to the user's query. + +The homepage displays recently published puns (status 'published'). The detail page for a specific pun displays "Similar Puns", which are found by querying Vectorize with the embedding of the current pun. + +## Scalability + +Cloudflare Workers and Workflows are designed to scale automatically based on demand, handling concurrent requests and background tasks efficiently without requiring manual provisioning. + +## Conclusion + +Cloudflare Workflows provides a powerful primitive for building reliable, durable background processes, seamlessly integrating with other Cloudflare services like D1, Workers AI, and Vectorize. This enables developers to build complex applications with robust background task handling, guaranteed execution, and built-in observability. + +[Source code for the Punderful repository](https://github.com/craigsdennis/punderful-workflows) + +## References: +- https://developers.cloudflare.com +- https://developers.cloudflare.com/workflows/ +- https://developers.cloudflare.com/workers-ai/ + + + + + + diff --git a/src/content/learning-paths/workflows-course.json b/src/content/learning-paths/workflows-course.json new file mode 100644 index 00000000000000..08c206e841fb6c --- /dev/null +++ b/src/content/learning-paths/workflows-course.json @@ -0,0 +1,9 @@ +{ + "title": "Introduction to Cloudflare Workflows", + "path": "/learning-paths/workflows-course/series/workflows-1/", + "priority": 2, + "description": "In this series, we introduce Cloudflare Workflows and the term 'Durable Execution' which comes from the desire to run applications that can resume execution from where they left off, even if the underlying host or compute fails.", + "products": ["Durable Objects", "Workers"], + "product_group": "Developer platform", + "video": true +} diff --git a/src/content/partials/learning-paths/workflows-series-navigation.mdx b/src/content/partials/learning-paths/workflows-series-navigation.mdx new file mode 100644 index 00000000000000..c70af7754aa05f --- /dev/null +++ b/src/content/partials/learning-paths/workflows-series-navigation.mdx @@ -0,0 +1,14 @@ +--- +{} + +--- + +import { CardGrid, LinkCard, Card } from "~/components"; + + + + From f010d49b84dcebeb6f86f861fe5bbb6d88157e13 Mon Sep 17 00:00:00 2001 From: Jason Farber Date: Wed, 7 May 2025 12:02:17 +0100 Subject: [PATCH 2/6] draft 2 --- .../workflows-course/series/workflows-1.mdx | 7 +- .../workflows-course/series/workflows-2.mdx | 367 ++++-------------- .../workflows-course/series/workflows-3.mdx | 352 ++++------------- .../docs/workflows/tutorials/index.mdx | 14 - src/content/docs/workflows/videos.mdx | 16 + .../workflows-series-navigation.mdx | 12 +- 6 files changed, 181 insertions(+), 587 deletions(-) delete mode 100644 src/content/docs/workflows/tutorials/index.mdx create mode 100644 src/content/docs/workflows/videos.mdx diff --git a/src/content/docs/learning-paths/workflows-course/series/workflows-1.mdx b/src/content/docs/learning-paths/workflows-course/series/workflows-1.mdx index 15e9f1f3787807..45b53954f4dc1f 100644 --- a/src/content/docs/learning-paths/workflows-course/series/workflows-1.mdx +++ b/src/content/docs/learning-paths/workflows-course/series/workflows-1.mdx @@ -2,7 +2,7 @@ pcx_content_type: navigation title: Introduction to Workflows sidebar: - order: 2 + order: 1 tableOfContents: false --- @@ -144,11 +144,8 @@ import { Render, Tabs, TabItem, Stream, Card } from "~/components"; Cloudflare Workers and Workflows are designed to scale automatically based on demand, handling concurrent requests and background tasks efficiently without requiring manual provisioning. - ### Conclusion - - Cloudflare Workflows provides a powerful primitive for building reliable, durable background processes, seamlessly integrating with other Cloudflare services like D1, Workers AI, and Vectorize. This enables developers to build complex applications with robust background task handling, guaranteed execution, and built-in observability. - + diff --git a/src/content/docs/learning-paths/workflows-course/series/workflows-2.mdx b/src/content/docs/learning-paths/workflows-course/series/workflows-2.mdx index feb2b3f6712d3c..9c2e8938568302 100644 --- a/src/content/docs/learning-paths/workflows-course/series/workflows-2.mdx +++ b/src/content/docs/learning-paths/workflows-course/series/workflows-2.mdx @@ -1,6 +1,6 @@ --- pcx_content_type: navigation -title: Introduction to Workflows - 2 +title: Monitor and batch your website data sidebar: order: 2 tableOfContents: false @@ -11,7 +11,7 @@ import { Render, Tabs, TabItem, Stream, Card } from "~/components"; - In this video, we discuss Cloudflare One, our Secure Access Service Edge (SASE) platform and how it has been designed to revolutionize the corporate network and enable companies with their Zero Trust strategy. Legacy network design is struggling to address today's challenges of security, performance and monitoring needs. Many IT teams are trying to evolve their corporate network with point solutions and finding the lack of integration and performance an issue. + Workflows can be used to process batches of data, ensuring each item in the batch goes through a defined process with reliable execution. This section demonstrates processing a batch of puns using the Punderful application as an example. **Related content** - If you want to dive into detail about modernizing your corporate network with Cloudflare, refer to the following pages: + If you want to dive into detail, refer to the following pages: - - [Evolving to a SASE architecture with Cloudflare](/reference-architecture/architectures/sase/) - - [Network-focused migration from VPN concentrators to Zero Trust Network Access](/reference-architecture/design-guides/network-vpn-migration/) - - [Using a Zero Trust framework to secure SaaS applications](/reference-architecture/design-guides/zero-trust-for-saas/) - - [Building Zero Trust architecture into your startup](/reference-architecture/design-guides/zero-trust-for-startups/) + - [Source code for the Punderful repository](https://github.com/craigsdennis/punderful-workflows) + - [Cloudflare Workflows](/workflows/) + - [Cloudflare Workers AI](/workers-ai/) - - # Cloudflare Workflows: Index Your Data With Background Workflows -## Outline -- Introduction to Cloudflare Workflows -- Punderful Application Overview -- Implementing a Workflow to Process New Puns - - Triggering the Workflow - - Defining the Workflow Class - - Workflow Steps - - Content Moderation - - Creating Embeddings - - Categorizing the Pun - - Adding Embeddings to Vectorize - - Updating Database Status - - Workflow Bindings -- Vectorize for Semantic Search -- Scalability -- Conclusion + -## Introduction to Cloudflare Workflows + The Punderful application processes user-submitted puns by performing content moderation, creating embeddings, categorizing, and adding them to a vector store. This process is defined as a Workflow. To process a batch of existing puns (from an open-source dataset called OPun), a batch endpoint is created that iterates through the puns and triggers the defined Workflow for each one. -Cloudflare Workflows provides durable execution capabilities, allowing developers to create reliable, repeatable workflows that run in the background. Workflows are designed to resume execution even if the underlying compute fails, ensuring that tasks complete eventually. They are built on top of Cloudflare Workers and handle scaling and provisioning automatically. + #### Batch Processing Code -Workflows are triggered by events, such as Event Notifications consumed from a Queue, HTTP requests, another Worker, or even scheduled timers. Individual steps within a Workflow are designed as retriable units of work. The state is persisted between steps, allowing workflows to resume from the last successful step after failures. Workflows automatically emit metrics for each step, aiding in debugging and observability. + The following code snippet shows the endpoint responsible for batch processing: -## Punderful Application Overview + [See here](https://github.com/craigsdennis/punderful-workflows/tree/main/src/index.tsx#L291) -Punderful is a sample application that showcases the use of various Cloudflare primitives, including Workers, D1, Vectorize, Workers AI, and Workflows. The application displays a list of puns stored in a D1 database. + This code: -The homepage lists the latest puns stored in D1. + 1. Fetches the list of puns from a JSON file (`puns.json`). + 2. Logs the number of puns being processed. + 3. Sets a user ID for tracking. + 4. Loops through each pun. + 5. Performs basic text cleaning on the pun. + 6. Inserts the pun into the database (handled by `insertPun`). + 7. Triggers the `PUBLISH` Workflow for each individual pun using `c.env.PUBLISH.create()`. The Workflow is given a unique ID using `crypto.randomUUID()`. -The application also includes a semantic search feature powered by Vectorize. To perform a search: + ### Monitoring Workflow Instances via CLI -1. Go to the Punderful search page. -2. Type a search query in the "Search for a pun..." input box. -3. Observe the search results appearing instantly below the search box. + The Cloudflare Wrangler CLI provides commands to monitor and manage Workflows and their instances. -To demonstrate adding a new pun: + To list the available workflows associated with your account: -1. Go to the Punderful creation page. -2. Enter a new pun in the "Enter your pun here..." textarea. -3. Observe the preview of the pun updating as you type. -4. Click the "Submit Pun" button. + ```bash + npx wrangler workflows list + ``` -When a new pun is submitted, it needs to be indexed in Vectorize for the semantic search to work. This indexing process involves creating embeddings from the pun text. This is a task suitable for background processing using Cloudflare Workflows, avoiding delays for the user in the request-response loop. + To list the instances of a specific workflow (e.g., the `publish` workflow): -## Implementing a Workflow to Process New Puns + ```bash + npx wrangler workflows instances list publish + ``` -A workflow is implemented to handle the background processing required when a new pun is submitted. + This command will show a list of workflow instances, their status (Queued, Running, Completed, Errored), and timestamps. -### Triggering the Workflow + To view the details of a specific workflow instance, including its steps and their status, duration, and output: -When a new pun is submitted via the `/api/puns` endpoint, the data is first inserted into the D1 database. Then, a new Workflow instance is created and triggered to perform the subsequent background tasks. + ```bash + npx wrangler workflows instances describe publish + ``` -```typescript -// File: src/index.ts -// ... (previous code for API handlers and database insertion) + Replace `` with the actual ID of a running or completed instance from the `list` command output. -app.post('/api/puns', async (c) => { - const payload = await c.req.json(); - console.log({ payload }); - const punId = await insertPun(c, payload.pun); + #### Example CLI Output (Describe Instance) - // Trigger the workflow - const workflow = await c.env.PUBLISH.create(crypto.randomUUID(), { - punId, - pun: payload.pun, - }); + Describing a workflow instance provides a detailed breakdown of its execution: - return c.json({ workflow }); -}); + ``` + Workflow Name: publish + Instance ID: oPun-batch-aea07d75-95fa-448f-9573-6e435388eff7 + Version ID: 75665fce-24a1-4c83-a561-088aabc91e5f + Status: Completed + Trigger: API + Queued: 10/24/2024, 1:43:45 AM + Success: Yes + Start: 10/24/2024, 1:43:45 AM + End: 10/24/2024, 1:43:49 AM + Duration: 4 seconds + Last Successful Step: update-status-to-published-1 + Steps: -// ... (remaining code) -``` + Name: content-moderation-1 + Type: Step + Start: 10/24/2024, 1:43:45 AM + End: 10/24/2024, 1:43:45 AM + Duration: 0 seconds + Success: Yes + Output: "true" + Config: {"retries":{"limit":5,"delay":1000,"backoff":"exponential"},"timeout":"10 minutes"} + Attempts: + Status: Completed + Start Time: Oct 23, 2024 6:44:57 PM + End Time: Oct 23, 2024 6:44:57 PM + Wall Time: 180 ms + ... (additional steps like create-pun-embedding-1, categorize-pun-1, add-embeddings-to-vector-store-1, update-status-to-published-1) + ``` -In this handler, `c.env.PUBLISH.create(crypto.randomUUID(), { punId, pun: payload.pun })` creates a new instance of the workflow bound as `PUBLISH`, assigns it a unique ID, and passes the `punId` and `pun` text as the payload. + This output shows the status, start/end times, duration, success status, and even the output and configuration for each step within the workflow instance. -### Defining the Workflow Class + ### Monitoring Workflow Instances via Cloudflare Dashboard -The workflow logic is defined in a class that extends `WorkflowEntrypoint`. + You can also monitor Workflows and their instances directly in the Cloudflare Dashboard. -```typescript -// File: src/index.ts - -// ... (previous code for API handlers and type definitions) - -export class PublishWorkflow extends WorkflowEntrypoint { - async run(event: WorkflowEvent, step: WorkflowStep) { - const { pun, punId } = event.payload; - - // ... (Workflow steps defined below) - } -} - -// ... (remaining code) -``` - -The `run` method is the entrypoint for the workflow execution. It receives the `event` containing the payload and a `step` object to define individual, durable steps. - -### Workflow Steps - -Each discrete, retriable task in the workflow is defined using `await step.do()`. - -#### Content Moderation - -Optionally, the workflow can perform content moderation using an external service like OpenAI's moderation API if an API key is available in the environment. - -```typescript -// File: src/index.ts -// ... (inside PublishWorkflow.run method) - - // step: [OPTIONAL] Moderate using OpenAI's free moderation API - if (this.env.OPENAI_API_KEY !== undefined) { - const moderation = await step.do('content-moderation', async () => { - const openai = new OpenAI({ apiKey: this.env.OPENAI_API_KEY }); - const moderation = await openai.moderations.create({ - model: 'omn-moderation-latest', - input: pun, - }); - return moderation; - }); - - if (moderation.results[0].flagged) { - console.warn('Pun flagged:', JSON.stringify(moderation)); - await this.env.DB.prepare(`UPDATE puns SET status=? WHERE id=?`).bind('flagged', punId).run(); - throw new Error(`Pun ${punId} failed moderation`) as NonRetryableError; - } - return true; - } else { - console.warn('OPENAI_API_KEY is not present in your environment, set it to enable moderation'); - } - -// ... (remaining workflow steps) -``` - -This step calls the OpenAI moderation API. If the content is flagged as inappropriate, the pun's status is updated in the database, and a `NonRetryableError` is thrown. Throwing a `NonRetryableError` prevents the workflow from retrying this step, as the content is permanently deemed inappropriate. - -#### Creating Embeddings - -Next, create vector embeddings for the pun text using a Workers AI model. - -```typescript -// File: src/index.ts -// ... (inside PublishWorkflow.run method, after moderation step) - - // step: Create embedding for indexing - const punEmbedding = await step.do('create-pun-embedding', async () => { - const results = await this.env.AI.run('@cf/baai/bge-large-en-v1.5', { - text: pun, - }); - return results.data[0]; - }); - -// ... (remaining workflow steps) -``` - -This step uses the `@cf/baai/bge-large-en-v1.5` model from Workers AI to generate a vector embedding for the `pun` text. The result (the embedding vector) is returned by the step and can be used in subsequent steps. `step.do()` ensures this step will be retried if it fails, guaranteeing that embeddings are eventually created. - -#### Categorizing the Pun - -Optionally, use a Workers AI language model to categorize the pun. - -```typescript -// File: src/index.ts -// ... (inside PublishWorkflow.run method, after embedding step) - - // step: Categorize - const categories = await step.do('categorize-pun', async () => { - const results = await this.env.AI.run('@cf/meta/llama-3.1-8b-instruct', { - messages: [ - { - role: 'system', - content: 'You help categorize puns for people to search for later. The user is going to give you a pun and your job is to list the most relevant categories for the pun, based on the content of the pun. Do not include words about pun in general, focus on what the content is about. For instance don\'t return "word play" or "pun" as a category. Return only the categories, comma separated. Do not include a preamble or introducdtion, just the categories.', - }, - { role: 'user', content: pun }, - ], - gateway: { - id: "punderful" - } - }); - return results.response; - }); - -// ... (remaining workflow steps) -``` - -This step uses the `@cf/meta/llama-3.1-8b-instruct` model with a specific system prompt to generate categories for the pun. The generated categories string is returned by the step. This step also benefits from `step.do()`'s reliability. - -#### Adding Embeddings to Vectorize - -Insert the created pun embedding and potentially categories embedding into the Vectorize database. - -```typescript -// File: src/index.ts -// ... (inside PublishWorkflow.run method, after categorization step) - - // Step: Add embeddings to vector store - await step.do('add-embeddings-to-vector-store', async () => { - await this.env.VECTORIZE.upsert([ - { - id: `content-${punId}`, - values: punEmbedding, - namespace: 'content', - metadata: { - punId, - pun, - categories, - }, - }, - ]); - - // Assuming categoriesEmbedding is also generated or derived - // await this.env.VECTORIZE.upsert([ - // { - // id: `categories-${punId}`, - // values: categoriesEmbedding, - // namespace: 'categories', - // metadata: { - // punId, - // pun, - // categories, - // }, - // } - // ]); - }); - -// ... (remaining workflow steps) -``` - -This step uses `this.env.VECTORIZE.upsert()` to add the generated embeddings and associated metadata to the Vectorize database. This makes the pun searchable semantically. `step.do()` ensures this critical indexing step is completed reliably. - -#### Updating Database Status - -The final step updates the status of the pun in the D1 database to indicate that it has been published and processed by the workflow. - -```typescript -// File: src/index.ts -// ... (inside PublishWorkflow.run method, after Vectorize step) - - // Step: Update the db - await step.do('update-status-to-published', async () => { - const results = await this.env.DB.prepare(`UPDATE puns SET status=? WHERE id=?`).bind('published', punId).run(); - }); - -// ... (remaining workflow logic) -``` - -This step updates the `status` column in the D1 database to 'published' for the corresponding pun ID. Once this step is complete, the pun is considered fully processed and ready to be displayed on the homepage. - -### Workflow Bindings - -To make the `PublishWorkflow` class available to the main Worker and to provide access to necessary resources (like D1, AI, Vectorize), bindings are configured in the `wrangler.toml` file. - -```toml -# File: wrangler.toml -# ... (other configurations) - -[workflows] -# Name of your workflow -name = "publish" -# Binding name env.MYWORKFLOW -binding = "PUBLISH" -# This is class that extends the Workflow class in src/index.ts -class_name = "PublishWorkflow" - -[ai] -binding = "AI" - -[vectorize] -binding = "VECTORIZE" -index_name = "puns5" # Example index name - -# ... (other bindings like d1_databases) -``` - -This configuration defines a workflow named `publish`, binds it to the environment variable `PUBLISH`, and links it to the `PublishWorkflow` class in `src/index.ts`. It also shows bindings for Workers AI (`AI`) and Vectorize (`VECTORIZE`), which are accessed via `this.env` within the workflow. - -## Vectorize for Semantic Search - -Vectorize is a vector database used in this application to enable semantic search for puns. It stores the vector embeddings created by Workers AI. The search functionality queries this Vectorize index to find puns similar in meaning to the user's query. - -The homepage displays recently published puns (status 'published'). The detail page for a specific pun displays "Similar Puns", which are found by querying Vectorize with the embedding of the current pun. - -## Scalability - -Cloudflare Workers and Workflows are designed to scale automatically based on demand, handling concurrent requests and background tasks efficiently without requiring manual provisioning. - -## Conclusion - -Cloudflare Workflows provides a powerful primitive for building reliable, durable background processes, seamlessly integrating with other Cloudflare services like D1, Workers AI, and Vectorize. This enables developers to build complex applications with robust background task handling, guaranteed execution, and built-in observability. - -[Source code for the Punderful repository](https://github.com/craigsdennis/punderful-workflows) - -## References: -- https://developers.cloudflare.com -- https://developers.cloudflare.com/workflows/ -- https://developers.cloudflare.com/workers-ai/ + This dashboard view provides a user-friendly way to observe the progress of your batch jobs, identify failed instances, and inspect the execution details of each step. + - + diff --git a/src/content/docs/learning-paths/workflows-course/series/workflows-3.mdx b/src/content/docs/learning-paths/workflows-course/series/workflows-3.mdx index ed6aababc888b6..52d68f7a318ff4 100644 --- a/src/content/docs/learning-paths/workflows-course/series/workflows-3.mdx +++ b/src/content/docs/learning-paths/workflows-course/series/workflows-3.mdx @@ -1,8 +1,8 @@ --- pcx_content_type: navigation -title: Introduction to Workflows - 2 +title: Use cron triggers to develop time-aware applications sidebar: - order: 2 + order: 3 tableOfContents: false --- @@ -11,337 +11,135 @@ import { Render, Tabs, TabItem, Stream, Card } from "~/components"; - In this video, we discuss Cloudflare One, our Secure Access Service Edge (SASE) platform and how it has been designed to revolutionize the corporate network and enable companies with their Zero Trust strategy. Legacy network design is struggling to address today's challenges of security, performance and monitoring needs. Many IT teams are trying to evolve their corporate network with point solutions and finding the lack of integration and performance an issue. + Cloudflare Workflows provide a powerful way to manage asynchronous, durable processes. The ability to explicitly schedule tasks using cron triggers and pause execution with `step.sleep` allows developers to build sophisticated, time-aware applications. **Related content** - If you want to dive into detail about modernizing your corporate network with Cloudflare, refer to the following pages: + If you want to dive into detail, refer to the following pages: - - [Evolving to a SASE architecture with Cloudflare](/reference-architecture/architectures/sase/) - - [Network-focused migration from VPN concentrators to Zero Trust Network Access](/reference-architecture/design-guides/network-vpn-migration/) - - [Using a Zero Trust framework to secure SaaS applications](/reference-architecture/design-guides/zero-trust-for-saas/) - - [Building Zero Trust architecture into your startup](/reference-architecture/design-guides/zero-trust-for-startups/) + - [Source code for the Punderful repository](https://github.com/craigsdennis/punderful-workflows) + - [Cloudflare Workflows](/workflows/) + - [Cloudflare Workers AI](/workers-ai/) - - # Cloudflare Workflows: Index Your Data With Background Workflows - -## Outline -- Introduction to Cloudflare Workflows -- Punderful Application Overview -- Implementing a Workflow to Process New Puns - - Triggering the Workflow - - Defining the Workflow Class - - Workflow Steps - - Content Moderation - - Creating Embeddings - - Categorizing the Pun - - Adding Embeddings to Vectorize - - Updating Database Status - - Workflow Bindings -- Vectorize for Semantic Search -- Scalability -- Conclusion - -## Introduction to Cloudflare Workflows - -Cloudflare Workflows provides durable execution capabilities, allowing developers to create reliable, repeatable workflows that run in the background. Workflows are designed to resume execution even if the underlying compute fails, ensuring that tasks complete eventually. They are built on top of Cloudflare Workers and handle scaling and provisioning automatically. - -Workflows are triggered by events, such as Event Notifications consumed from a Queue, HTTP requests, another Worker, or even scheduled timers. Individual steps within a Workflow are designed as retriable units of work. The state is persisted between steps, allowing workflows to resume from the last successful step after failures. Workflows automatically emit metrics for each step, aiding in debugging and observability. - -## Punderful Application Overview - -Punderful is a sample application that showcases the use of various Cloudflare primitives, including Workers, D1, Vectorize, Workers AI, and Workflows. The application displays a list of puns stored in a D1 database. - -The homepage lists the latest puns stored in D1. - -The application also includes a semantic search feature powered by Vectorize. To perform a search: - -1. Go to the Punderful search page. -2. Type a search query in the "Search for a pun..." input box. -3. Observe the search results appearing instantly below the search box. - -To demonstrate adding a new pun: - -1. Go to the Punderful creation page. -2. Enter a new pun in the "Enter your pun here..." textarea. -3. Observe the preview of the pun updating as you type. -4. Click the "Submit Pun" button. - -When a new pun is submitted, it needs to be indexed in Vectorize for the semantic search to work. This indexing process involves creating embeddings from the pun text. This is a task suitable for background processing using Cloudflare Workflows, avoiding delays for the user in the request-response loop. - -## Implementing a Workflow to Process New Puns - -A workflow is implemented to handle the background processing required when a new pun is submitted. - -### Triggering the Workflow - -When a new pun is submitted via the `/api/puns` endpoint, the data is first inserted into the D1 database. Then, a new Workflow instance is created and triggered to perform the subsequent background tasks. - -```typescript -// File: src/index.ts -// ... (previous code for API handlers and database insertion) - -app.post('/api/puns', async (c) => { - const payload = await c.req.json(); - console.log({ payload }); - const punId = await insertPun(c, payload.pun); - - // Trigger the workflow - const workflow = await c.env.PUBLISH.create(crypto.randomUUID(), { - punId, - pun: payload.pun, - }); - - return c.json({ workflow }); -}); - -// ... (remaining code) -``` - -In this handler, `c.env.PUBLISH.create(crypto.randomUUID(), { punId, pun: payload.pun })` creates a new instance of the workflow bound as `PUBLISH`, assigns it a unique ID, and passes the `punId` and `pun` text as the payload. - -### Defining the Workflow Class - -The workflow logic is defined in a class that extends `WorkflowEntrypoint`. - -```typescript -// File: src/index.ts - -// ... (previous code for API handlers and type definitions) - -export class PublishWorkflow extends WorkflowEntrypoint { - async run(event: WorkflowEvent, step: WorkflowStep) { - const { pun, punId } = event.payload; - - // ... (Workflow steps defined below) - } -} - -// ... (remaining code) -``` - -The `run` method is the entrypoint for the workflow execution. It receives the `event` containing the payload and a `step` object to define individual, durable steps. - -### Workflow Steps - -Each discrete, retriable task in the workflow is defined using `await step.do()`. - -#### Content Moderation - -Optionally, the workflow can perform content moderation using an external service like OpenAI's moderation API if an API key is available in the environment. - -```typescript -// File: src/index.ts -// ... (inside PublishWorkflow.run method) - - // step: [OPTIONAL] Moderate using OpenAI's free moderation API - if (this.env.OPENAI_API_KEY !== undefined) { - const moderation = await step.do('content-moderation', async () => { - const openai = new OpenAI({ apiKey: this.env.OPENAI_API_KEY }); - const moderation = await openai.moderations.create({ - model: 'omn-moderation-latest', - input: pun, - }); - return moderation; - }); - - if (moderation.results[0].flagged) { - console.warn('Pun flagged:', JSON.stringify(moderation)); - await this.env.DB.prepare(`UPDATE puns SET status=? WHERE id=?`).bind('flagged', punId).run(); - throw new Error(`Pun ${punId} failed moderation`) as NonRetryableError; - } - return true; - } else { - console.warn('OPENAI_API_KEY is not present in your environment, set it to enable moderation'); - } - -// ... (remaining workflow steps) -``` - -This step calls the OpenAI moderation API. If the content is flagged as inappropriate, the pun's status is updated in the database, and a `NonRetryableError` is thrown. Throwing a `NonRetryableError` prevents the workflow from retrying this step, as the content is permanently deemed inappropriate. - -#### Creating Embeddings - -Next, create vector embeddings for the pun text using a Workers AI model. - -```typescript -// File: src/index.ts -// ... (inside PublishWorkflow.run method, after moderation step) + + - // step: Create embedding for indexing - const punEmbedding = await step.do('create-pun-embedding', async () => { - const results = await this.env.AI.run('@cf/baai/bge-large-en-v1.5', { - text: pun, - }); - return results.data[0]; - }); + Workflows allow you to kick off asynchronous processes without blocking the user. This is demonstrated in the `addInteraction` function, which creates a new instance of the `INTERACTION` workflow. -// ... (remaining workflow steps) -``` + Locate the `addInteraction` function in `src/index.tsx`: + + [See here](https://github.com/craigsdennis/punderful-workflows/blob/main/src/index.tsx#L237) -This step uses the `@cf/baai/bge-large-en-v1.5` model from Workers AI to generate a vector embedding for the `pun` text. The result (the embedding vector) is returned by the step and can be used in subsequent steps. `step.do()` ensures this step will be retried if it fails, guaranteeing that embeddings are eventually created. + This function is called when a user interacts with a pun (e.g., likes it). Instead of performing the interaction logic directly, it offloads the work to a workflow. -#### Categorizing the Pun + Examine the `InteractionWorkflow` definition in `src/workflows/interaction.ts`. This workflow performs tasks like checking if the user/session exists and recording the interaction in the database. + + [See here](https://github.com/craigsdennis/punderful-workflows/blob/main/src/workflows/interaction.ts) -Optionally, use a Workers AI language model to categorize the pun. + ### Leaderboard Code -```typescript -// File: src/index.ts -// ... (inside PublishWorkflow.run method, after embedding step) + A common use case for background processes is crunching data and caching results, such as building a leaderboard. - // step: Categorize - const categories = await step.do('categorize-pun', async () => { - const results = await this.env.AI.run('@cf/meta/llama-3.1-8b-instruct', { - messages: [ - { - role: 'system', - content: 'You help categorize puns for people to search for later. The user is going to give you a pun and your job is to list the most relevant categories for the pun, based on the content of the pun. Do not include words about pun in general, focus on what the content is about. For instance don\'t return "word play" or "pun" as a category. Return only the categories, comma separated. Do not include a preamble or introducdtion, just the categories.', - }, - { role: 'user', content: pun }, - ], - gateway: { - id: "punderful" - } - }); - return results.response; - }); + Examine the `LeaderboardWorkflow` in `src/workflows/leaderboard.ts`. This workflow performs a database query to find trending puns and then stores the results in Cloudflare KV (Key-Value Store). + + [See here](https://github.com/craigsdennis/punderful-workflows/blob/main/src/workflows/leaderboard.ts) -// ... (remaining workflow steps) -``` + This workflow can be scheduled to run periodically to update the leaderboard data. -This step uses the `@cf/meta/llama-3.1-8b-instruct` model with a specific system prompt to generate categories for the pun. The generated categories string is returned by the step. This step also benefits from `step.do()`'s reliability. + ### Wrangler.toml Configuration -#### Adding Embeddings to Vectorize + The `wrangler.toml` file is used to configure your Worker and Workflows. This includes defining bindings to resources like KV namespaces and setting up triggers for workflows. -Insert the created pun embedding and potentially categories embedding into the Vectorize database. + Open `wrangler.toml` and find the `[triggers]` section. + + [See here](https://github.com/craigsdennis/punderful-workflows/blob/main/wrangler.toml#L67) -```typescript -// File: src/index.ts -// ... (inside PublishWorkflow.run method, after categorization step) + The `crons` array allows you to define scheduled triggers for your main Worker. The example shows a cron job configured to run every 30 minutes. - // Step: Add embeddings to vector store - await step.do('add-embeddings-to-vector-store', async () => { - await this.env.VECTORIZE.upsert([ - { - id: `content-${punId}`, - values: punEmbedding, - namespace: 'content', - metadata: { - punId, - pun, - categories, - }, - }, - ]); + Locate the `scheduled` handler in your main Worker code (`src/index.tsx`). This handler is executed when a cron trigger fires. - // Assuming categoriesEmbedding is also generated or derived - // await this.env.VECTORIZE.upsert([ - // { - // id: `categories-${punId}`, - // values: categoriesEmbedding, - // namespace: 'categories', - // metadata: { - // punId, - // pun, - // categories, - // }, - // } - // ]); - }); + [See here](https://github.com/craigsdennis/punderful-workflows/blob/main/src/index.tsx#L315) -// ... (remaining workflow steps) -``` + This handler creates an instance of the `LEADERBOARD_WORKFLOW`, initiating the leaderboard update process on a schedule. -This step uses `this.env.VECTORIZE.upsert()` to add the generated embeddings and associated metadata to the Vectorize database. This makes the pun searchable semantically. `step.do()` ensures this critical indexing step is completed reliably. + ### Puntificator: Using AI to Develop More Puns Automatically -#### Updating Database Status + Workflows can also be used for more complex, multi-step processes, including interacting with AI models. The `PuntificatorWorkflow` is an example that leverages AI to generate and evaluate new puns. -The final step updates the status of the pun in the D1 database to indicate that it has been published and processed by the workflow. + Examine the `PuntificatorWorkflow` definition in `src/workflows/puntificator.ts`. + [See here](https://github.com/craigsdennis/punderful-workflows/blob/main/src/workflows/puntificator.ts) -```typescript -// File: src/index.ts -// ... (inside PublishWorkflow.run method, after Vectorize step) + This workflow includes steps to: - // Step: Update the db - await step.do('update-status-to-published', async () => { - const results = await this.env.DB.prepare(`UPDATE puns SET status=? WHERE id=?`).bind('published', punId).run(); - }); + 1. Retrieve trending puns. + 2. Create a new pun based on trends using an AI model. + 3. Judge the quality of the created pun using another AI model. + 4. Save the pun if it meets a certain rating threshold. -// ... (remaining workflow logic) -``` + Crucially, this workflow includes a `step.sleep` call: -This step updates the `status` column in the D1 database to 'published' for the corresponding pun ID. Once this step is complete, the pun is considered fully processed and ready to be displayed on the homepage. + [See here](https://github.com/craigsdennis/punderful-workflows/blob/main/src/workflows/puntificator.ts#L135) -### Workflow Bindings + This step pauses the workflow execution for a specified duration (1 day in this case). This is useful for waiting to see user feedback on a published pun before potentially taking further action based on its popularity. -To make the `PublishWorkflow` class available to the main Worker and to provide access to necessary resources (like D1, AI, Vectorize), bindings are configured in the `wrangler.toml` file. + ### Nested Workflows -```toml -# File: wrangler.toml -# ... (other configurations) + Workflows can initiate other workflows, allowing you to compose complex processes from smaller, modular units. -[workflows] -# Name of your workflow -name = "publish" -# Binding name env.MYWORKFLOW -binding = "PUBLISH" -# This is class that extends the Workflow class in src/index.ts -class_name = "PublishWorkflow" + In the `PuntificatorWorkflow`, find where it calls the `PUBLISH` workflow. -[ai] -binding = "AI" + [See here](https://github.com/craigsdennis/punderful-workflows/blob/main/src/workflows/puntificator.ts#L115) -[vectorize] -binding = "VECTORIZE" -index_name = "puns5" # Example index name + This demonstrates how one workflow can trigger another, enabling the separation of concerns and modular design. -# ... (other bindings like d1_databases) -``` + Examine the `PublishWorkflow` in `src/workflows/publish.ts`. -This configuration defines a workflow named `publish`, binds it to the environment variable `PUBLISH`, and links it to the `PublishWorkflow` class in `src/index.ts`. It also shows bindings for Workers AI (`AI`) and Vectorize (`VECTORIZE`), which are accessed via `this.env` within the workflow. + [See here](https://github.com/craigsdennis/punderful-workflows/blob/main/src/workflows/publish.ts) -## Vectorize for Semantic Search + This workflow handles the logic for publishing a pun, likely involving saving it to the database and making it visible on the site. -Vectorize is a vector database used in this application to enable semantic search for puns. It stores the vector embeddings created by Workers AI. The search functionality queries this Vectorize index to find puns similar in meaning to the user's query. + ### Workflow Instances -The homepage displays recently published puns (status 'published'). The detail page for a specific pun displays "Similar Puns", which are found by querying Vectorize with the embedding of the current pun. + You can trigger workflows manually and inspect their execution status and output using the `wrangler` command-line tool. -## Scalability + To trigger the `PuntificatorWorkflow` manually: -Cloudflare Workers and Workflows are designed to scale automatically based on demand, handling concurrent requests and background tasks efficiently without requiring manual provisioning. + ```bash + npx wrangler workflows trigger puntificator + ``` -## Conclusion + This command will queue an instance of the workflow. You will see a success message and the instance ID. -Cloudflare Workflows provides a powerful primitive for building reliable, durable background processes, seamlessly integrating with other Cloudflare services like D1, Workers AI, and Vectorize. This enables developers to build complex applications with robust background task handling, guaranteed execution, and built-in observability. + To describe the latest instance of a workflow: -[Source code for the Punderful repository](https://github.com/craigsdennis/punderful-workflows) + ```bash + npx wrangler workflows instances describe puntificator latest + ``` -## References: -- https://developers.cloudflare.com -- https://developers.cloudflare.com/workflows/ -- https://developers.cloudflare.com/workers-ai/ + This command will show details about the most recent run of the specified workflow, including its start time, end time, duration, state, and the state of each individual step within the workflow. You can observe steps like `create-new-pun-based-on-trends`, `judge-pun`, `save-pun`, `publish`, and `wait-for-publish` (which shows a 'Sleeping' state). - + diff --git a/src/content/docs/workflows/tutorials/index.mdx b/src/content/docs/workflows/tutorials/index.mdx deleted file mode 100644 index cfb7e3e63ae071..00000000000000 --- a/src/content/docs/workflows/tutorials/index.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -type: overview -hideChildren: true -pcx_content_type: navigation -title: Tutorials -sidebar: - order: 4 ---- - -import { GlossaryTooltip, YouTubeVideos } from "~/components"; - -View tutorials to help you get started with Workers. - - diff --git a/src/content/docs/workflows/videos.mdx b/src/content/docs/workflows/videos.mdx new file mode 100644 index 00000000000000..ed867207b957cc --- /dev/null +++ b/src/content/docs/workflows/videos.mdx @@ -0,0 +1,16 @@ +--- +pcx_content_type: navigation +title: Videos +sidebar: + order: 5 +--- + +import { CardGrid, LinkCard } from "~/components"; + + + + diff --git a/src/content/partials/learning-paths/workflows-series-navigation.mdx b/src/content/partials/learning-paths/workflows-series-navigation.mdx index c70af7754aa05f..3aef6a36002ba8 100644 --- a/src/content/partials/learning-paths/workflows-series-navigation.mdx +++ b/src/content/partials/learning-paths/workflows-series-navigation.mdx @@ -10,5 +10,15 @@ import { CardGrid, LinkCard, Card } from "~/components"; title="Watch Episode 1: Understand Cloudflare Workflows" description="In this episode, we introduce Cloudflare Workflows, which provides durable execution capabilities, allowing developers to create reliable, repeatable workflows that run in the background." href="/learning-paths/workflows-course/series/workflows-1/" - /> + /> + + From 471e2fdb5df451144c65fc190864b30617e1ff1d Mon Sep 17 00:00:00 2001 From: Jason Farber Date: Wed, 7 May 2025 12:58:23 +0100 Subject: [PATCH 3/6] few tweaks --- public/__redirects | 2 ++ .../learning-paths/workflows-course/series/workflows-2.mdx | 2 +- .../learning-paths/workflows-course/series/workflows-3.mdx | 6 +++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/public/__redirects b/public/__redirects index 64714d6b0bd4a9..ac9a2a05a28a83 100644 --- a/public/__redirects +++ b/public/__redirects @@ -2088,6 +2088,8 @@ /workers-ai/demos/* /workers-ai/guides/demos-architectures/:splat 301 /workers-ai/tutorials/* /workers-ai/guides/tutorials/:splat 301 +# Workflows +/workflows/tutorials/ /workflows/ 301 # Others /logs/analytics-integrations/* /fundamentals/data-products/analytics-integrations/:splat 301 diff --git a/src/content/docs/learning-paths/workflows-course/series/workflows-2.mdx b/src/content/docs/learning-paths/workflows-course/series/workflows-2.mdx index 9c2e8938568302..0b38886b1d452e 100644 --- a/src/content/docs/learning-paths/workflows-course/series/workflows-2.mdx +++ b/src/content/docs/learning-paths/workflows-course/series/workflows-2.mdx @@ -16,7 +16,7 @@ import { Render, Tabs, TabItem, Stream, Card } from "~/components"; Date: Wed, 7 May 2025 15:48:13 +0100 Subject: [PATCH 4/6] adding meta descriptions --- .../learning-paths/workflows-course/series/workflows-1.mdx | 2 ++ .../learning-paths/workflows-course/series/workflows-2.mdx | 3 +++ .../learning-paths/workflows-course/series/workflows-3.mdx | 2 ++ 3 files changed, 7 insertions(+) diff --git a/src/content/docs/learning-paths/workflows-course/series/workflows-1.mdx b/src/content/docs/learning-paths/workflows-course/series/workflows-1.mdx index 45b53954f4dc1f..eab4c299e8c0f3 100644 --- a/src/content/docs/learning-paths/workflows-course/series/workflows-1.mdx +++ b/src/content/docs/learning-paths/workflows-course/series/workflows-1.mdx @@ -4,6 +4,8 @@ title: Introduction to Workflows sidebar: order: 1 tableOfContents: false +description: | + Cloudflare Workflows provides durable execution capabilities, allowing developers to create reliable, repeatable workflows that run in the background. Workflows are designed to resume execution even if the underlying compute fails, ensuring that tasks complete eventually. They are built on top of Cloudflare Workers and handle scaling and provisioning automatically. --- import { Render, Tabs, TabItem, Stream, Card } from "~/components"; diff --git a/src/content/docs/learning-paths/workflows-course/series/workflows-2.mdx b/src/content/docs/learning-paths/workflows-course/series/workflows-2.mdx index 0b38886b1d452e..1c8ddfc7949a09 100644 --- a/src/content/docs/learning-paths/workflows-course/series/workflows-2.mdx +++ b/src/content/docs/learning-paths/workflows-course/series/workflows-2.mdx @@ -4,6 +4,9 @@ title: Monitor and batch your website data sidebar: order: 2 tableOfContents: false +description: | + Workflows can be used to process batches of data, ensuring each item in the batch goes through a defined process with reliable execution. This section demonstrates processing a batch of puns using the Punderful application as an example. + --- import { Render, Tabs, TabItem, Stream, Card } from "~/components"; diff --git a/src/content/docs/learning-paths/workflows-course/series/workflows-3.mdx b/src/content/docs/learning-paths/workflows-course/series/workflows-3.mdx index bc6673e1d94407..7a880218e5870a 100644 --- a/src/content/docs/learning-paths/workflows-course/series/workflows-3.mdx +++ b/src/content/docs/learning-paths/workflows-course/series/workflows-3.mdx @@ -4,6 +4,8 @@ title: Use cron triggers to develop time-aware applications sidebar: order: 3 tableOfContents: false +description: | + Cloudflare Workflows provide a powerful way to manage asynchronous, durable processes. The ability to explicitly schedule tasks using cron triggers and pause execution with `step.sleep` allows developers to build sophisticated, time-aware applications. --- import { Render, Tabs, TabItem, Stream, Card } from "~/components"; From 6a6b62cde1541901bc14eb7ccc0066eaec8b53af Mon Sep 17 00:00:00 2001 From: Jason Farber Date: Wed, 7 May 2025 17:05:31 +0100 Subject: [PATCH 5/6] changing redirect location --- public/__redirects | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/__redirects b/public/__redirects index ac9a2a05a28a83..fc7893aadeb9cd 100644 --- a/public/__redirects +++ b/public/__redirects @@ -2089,7 +2089,7 @@ /workers-ai/tutorials/* /workers-ai/guides/tutorials/:splat 301 # Workflows -/workflows/tutorials/ /workflows/ 301 +/workflows/tutorials/ /workflows/examples 301 # Others /logs/analytics-integrations/* /fundamentals/data-products/analytics-integrations/:splat 301 From e74630a4cadea6dd7964e3219c462ad75e524fad Mon Sep 17 00:00:00 2001 From: jason-cf <132416570+jason-cf@users.noreply.github.com> Date: Thu, 8 May 2025 09:54:18 +0100 Subject: [PATCH 6/6] Apply suggestions from code review Co-authored-by: Rebecca Tamachiro <62246989+RebeccaTamachiro@users.noreply.github.com> --- .../workflows-course/series/workflows-1.mdx | 8 ++++---- .../workflows-course/series/workflows-2.mdx | 2 +- .../workflows-course/series/workflows-3.mdx | 7 ++++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/content/docs/learning-paths/workflows-course/series/workflows-1.mdx b/src/content/docs/learning-paths/workflows-course/series/workflows-1.mdx index eab4c299e8c0f3..fd8edd0168fe6a 100644 --- a/src/content/docs/learning-paths/workflows-course/series/workflows-1.mdx +++ b/src/content/docs/learning-paths/workflows-course/series/workflows-1.mdx @@ -15,7 +15,7 @@ import { Render, Tabs, TabItem, Stream, Card } from "~/components"; Cloudflare Workflows provides durable execution capabilities, allowing developers to create reliable, repeatable workflows that run in the background. Workflows are designed to resume execution even if the underlying compute fails, ensuring that tasks complete eventually. They are built on top of Cloudflare Workers and handle scaling and provisioning automatically. - Workflows are triggered by events, such as Event Notifications consumed from a Queue, HTTP requests, another Worker, or even scheduled timers. Individual steps within a Workflow are designed as retriable units of work. The state is persisted between steps, allowing workflows to resume from the last successful step after failures. Workflows automatically emit metrics for each step, aiding in debugging and observability. + Workflows are triggered by events, such as Event Notifications consumed from a Queue, HTTP requests, another Worker, or even scheduled timers. Individual steps within a Workflow are designed as retriable units of work. The state is persisted between steps, allowing workflows to resume from the last successful step after failures. Workflows automatically generate metrics for each step, aiding in debugging and observability.