Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates formatting for guide: creating-interactive-workflows.md #11991

Merged
merged 3 commits into from
Feb 16, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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