Skip to content

Commit

Permalink
Updates formatting for guide: creating-interactive-workflows.md (#11991)
Browse files Browse the repository at this point in the history
  • Loading branch information
discdiver committed Feb 16, 2024
1 parent 37d8f96 commit 5bb364b
Showing 1 changed file with 16 additions and 12 deletions.
28 changes: 16 additions & 12 deletions docs/guides/creating-interactive-workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,12 @@ Please enter your details below:

When a user sees the form for this input, the given markdown will appear above the input fields.


### Handling custom validation

Prefect uses the fields and type hints on your `RunInput` or `BaseModel` class to validate the general structure of input your flow receives, but you might require more complex validation. If you do, you can use Pydantic [validators](https://docs.pydantic.dev/1.10/usage/validators/).

!!! warning "Custom validation runs after the flow resumes"
Prefect transforms the type annotations in your `RunInput` or `BaseModel` class to a JSON schema and uses that schema in the UI for client-side validation. However, custom validation requires running *Python* logic defined in your `RunInput` class. Because of this, validation happens _after the flow resumes_, so you'll want to handle it explicitly in your flow. Continue reading for an example best practice.
Prefect transforms the type annotations in your `RunInput` or `BaseModel` class to a JSON schema and uses that schema in the UI for client-side validation. However, custom validation requires running _Python_ logic defined in your `RunInput` class. Because of this, validation happens _after the flow resumes_, so you'll want to handle it explicitly in your flow. Continue reading for an example best practice.

The following is an example `RunInput` class that uses a custom field validator:

Expand Down Expand Up @@ -295,7 +294,7 @@ The most important parameter to the `send_input` and `receive_input` functions i
- A `prefect.input.RunInput` class

!!! type "When to use a `BaseModel` or `RunInput` instead of a built-in type"
Most built-in types and collections of built-in types should work with `send_input` and `receive_input`, but there is a caveat with nested collection types, such as lists of tuples, e.g. `List[Tuple[str, float]])`. In this case, validation may happen after your flow receives the data, so calling `receive_input` may raise a `ValidationError`. You can plan to catch this exception, but also, consider placing the field in an explicit `BaseModel` or `RunInput` so that your flow only receives exact type matches.
Most built-in types and collections of built-in types should work with `send_input` and `receive_input`, but there is a caveat with nested collection types, such as lists of tuples, e.g. `List[Tuple[str, float]])`. In this case, validation may happen after your flow receives the data, so calling `receive_input` may raise a `ValidationError`. You can plan to catch this exception, but also, consider placing the field in an explicit `BaseModel` or `RunInput` so that your flow only receives exact type matches.

Let's look at some examples! We'll check out `receive_input` first, followed by `send_input`, and then we'll see the two functions working together.

Expand Down Expand Up @@ -466,7 +465,7 @@ async def greeter():
await suspend_flow_run(timeout=10000)
```

As this flow processes name input, it adds the *key* of the flow run input to the `seen_keys_block`. When the flow later suspends and then resumes, it reads the keys it has already seen out of the JSON Block and passes them as the `exlude_keys` parameter to `receive_input`.
As this flow processes name input, it adds the _key_ of the flow run input to the `seen_keys_block`. When the flow later suspends and then resumes, it reads the keys it has already seen out of the JSON Block and passes them as the `exlude_keys` parameter to `receive_input`.

### Responding to the input's sender

Expand Down Expand Up @@ -519,7 +518,7 @@ async def greeter():
await name_input.respond(f"Hello, {name_input.value}!")
```

With a `greeter` flow in place, now we're ready to create the flow that sends `greeter` names!
With a `greeter` flow in place, we're ready to create the flow that sends `greeter` names!

### Sending input

Expand Down Expand Up @@ -657,18 +656,20 @@ if __name__ == "__main__":
asyncio.run(greeter.serve(name="send-receive"))
elif sys.argv[1] == "sender":
asyncio.run(sender())
```
```

To run the example, you'll need a Python environment with Prefect installed, pointed at either open-source Prefect or Prefect Cloud.
To run the example, you'll need a Python environment with Prefect installed, pointed at either an open-source Prefect server instance or Prefect Cloud.

With your environment set up, start a flow runner in one terminal with the following command:

<div class="terminal">
```bash
$ python <filename> greeter
python my_file_name greeter
```
</div>
</div>

For example, with Prefect Cloud, you should see output like this:

<div class="terminal">
```bash
╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
Expand All @@ -685,14 +686,16 @@ For example, with Prefect Cloud, you should see output like this:
```
</div>

Then start the greeter in another process in another terminal:
Then start the greeter process in another terminal:

<div class="terminal">
```bash
$ python <filename> sender
python my_file_name sender
```
</div>

You should see:
You should see output like this:

<div class="terminal">
```bash
11:38:41.800 | INFO | prefect.engine - Created flow run 'gregarious-owl' for flow 'sender'
Expand All @@ -702,6 +705,7 @@ What is your name?
</div>

Type a name and press the enter key to see a greeting, and you'll see sending and receiving in action:

<div class="terminal">
```bash
What is your name? andrew
Expand Down

0 comments on commit 5bb364b

Please sign in to comment.