-
Notifications
You must be signed in to change notification settings - Fork 279
Added the plugin guide #3990
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
base: main
Are you sure you want to change the base?
Added the plugin guide #3990
Changes from 4 commits
b17605c
694b01d
df3ee7c
31072a5
a1889cd
d046ef2
1b8937e
4f37253
2bde07c
e1d905a
5968e49
582c7d6
0612204
5a037d7
8b2bf1c
dcb500d
2e7a1cc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,351 @@ | ||||||
| --- | ||||||
| id: plugins-guide | ||||||
| title: Plugins Guide | ||||||
| sidebar_label: Plugins guide | ||||||
| description: Best practices for creating plugins for AI Agents | ||||||
| toc_max_heading_level: 4 | ||||||
| hide_table_of_contents: true | ||||||
| keywords: | ||||||
| - ai agents | ||||||
| - best practices | ||||||
| tags: | ||||||
| - Best Practices | ||||||
| - AI Agents | ||||||
| --- | ||||||
|
|
||||||
| import SdkTabs from '@site/src/components'; | ||||||
|
|
||||||
| # Plugins | ||||||
|
|
||||||
| A **Plugin** is an abstraction that will let your users add your feature to their Workflows in one line. It makes it seamless for platform developers to add their own custom functionality to many Workflows. Using plugins, you can build reusable open source libraries or build add-ons for engineers at your company. | ||||||
|
|
||||||
| Here are some common use cases for plugins: | ||||||
|
|
||||||
| - AI Agent SDKs | ||||||
| - Observability, tracing, or logging middleware | ||||||
| - Adding reliable built-in functionality such as LLM calls, corporate messaging, and payments infrastructure | ||||||
| - Encryption or compliance middleware | ||||||
|
|
||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's frame the guide: This guide will
|
||||||
| ## How to build a Plugin | ||||||
|
|
||||||
| The recommended way to start building plugins is with a `SimplePlugin`. This abstraction will tackle the vast majority of plugins people want to write. | ||||||
|
|
||||||
| For advanced use cases, you can extend the methods in lower-level classes that Simple Plugin is based on without re-implementing what you’ve done. See the [Advanced Topics section](#advanced-topics-for-plugins) for more information. | ||||||
|
|
||||||
| ### Example Plugins | ||||||
|
|
||||||
| If you prefer to learn by getting hands-on with code, check out some existing plugins. | ||||||
|
|
||||||
| - Temporal's Python SDK ships with an [OpenAI Agents SDK](https://github.com/temporalio/sdk-python/tree/main/temporalio/contrib/openai_agents) plugin | ||||||
| - Pydantic plugin when it’s fixed | ||||||
|
||||||
|
|
||||||
| ## What you can provide to users in a plugin | ||||||
|
|
||||||
| There are a number of features you can give your users with a plugin. Here's a short list of some of the things you can do and more are being added. | ||||||
flippedcoder marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| - [Built-in Activities](#built-in-activity) | ||||||
| - [Workflow libraries](#workflow-libraries) | ||||||
| - [Built-in Child Workflows](#built-in-child-workflows) | ||||||
flippedcoder marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| - [Built-in Nexus Operations](#built-in-nexus-operations) | ||||||
| - [Custom Data Converters](#custom-data-converters) | ||||||
| - [Interceptors](#interceptors) | ||||||
|
|
||||||
| ### Built-in Activity | ||||||
|
|
||||||
| You can provide built-in Activities in a Plugin for users to call from their Workflows. Activities are the most common Temporal primitive and should be scoped to: | ||||||
|
|
||||||
| - Single write operations | ||||||
| - Batches of similar writes | ||||||
| - One or more read operations followed by a write operation | ||||||
| - A read that should be memoized, like an LLM call, a large download, or a slow-polling read | ||||||
|
Comment on lines
+56
to
+59
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This reads awkwardly to me. Each line sort of contradicts the last. Presumably we have advice elsewhere in the docs we can link to about what an activity should be scoped to? This could be just "Activities should be scoped to a small, ideally idempotent, unit of work" or something
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We actually don't AFAICT! I'd love to link out to the general activity docs, but I figured, let's get the content in here for starters and then refactor next. Let me suggest a rewording. re: idempotent, we have to be careful there since LLMs are not idempotent by some definitions. |
||||||
|
|
||||||
| Larger pieces of functionality should be broken it up into multiple activities. This makes it easier to do failure recovery, have short timeouts, and be idempotent. | ||||||
|
|
||||||
| Here are some best practices you can use when you are making Activity plugins: | ||||||
|
|
||||||
| - Activity arguments and return values must be serializable. | ||||||
| - Activities that perform writes should be idempotent. | ||||||
| - Activities have [timeouts](https://docs.temporal.io/develop/python/failure-detection#heartbeat-timeout) and [retry policies](https://docs.temporal.io/encyclopedia/retry-policies). To be Activity-friendly, your operation should either complete within a few minutes or it should support the ability to heartbeat or poll for a result. This way it will be clear to the Workflow when the Activity is still making progress. | ||||||
| - You need to specify at least one timeout, typically the [start_to_close_timeout](https://docs.temporal.io/encyclopedia/detecting-activity-failures#start-to-close-timeout). Keep in mind that the shorter the timeout, the faster Temporal will retry upon failure. See the [timeouts and retry policies](#timeouts-and-retry-policies) to learn more. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Definitely seems like we should link to general advice on activities somewhere in this section at least
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed, but we don't actually have almost any of this advice anywhere that I could find. I've opened a task as a follow-up to figure out where to put it. |
||||||
|
|
||||||
| #### Timeouts and retry policies | ||||||
|
|
||||||
| Temporal's Activity retry mechanism gives applications the benefits of durable execution. | ||||||
| For example, Temporal will keep track of the exponential backoff delay even if the Worker crashes. Since Temporal can’t tell when a Worker crashes, Workflows rely on the [start_to_close_timeout](https://docs.temporal.io/encyclopedia/detecting-activity-failures#start-to-close-timeout) to know how long to wait before assuming that an Activity is inactive. | ||||||
|
|
||||||
| Be cautious when doing retries within your Activity because it lengthens this overall timeout to be longer than the longest possible retry sequence. That means it takes too long to recover from other causes of failure. Such internal retries also prevent users from counting failure metrics and make it harder for users to debug in Temporal UI when something is wrong. | ||||||
|
||||||
| Be cautious when doing retries within your Activity because it lengthens this overall timeout to be longer than the longest possible retry sequence. That means it takes too long to recover from other causes of failure. Such internal retries also prevent users from counting failure metrics and make it harder for users to debug in Temporal UI when something is wrong. | |
| Be cautious when doing retries within your Activity because it lengthens the needed Activity timeout. Such internal retries also prevent users from counting failure metrics and make it harder for users to debug in Temporal UI when something is wrong. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same deal as my above comments, not sure this whole section even needs to be here at all. We're just repeating advice we give elsewhere in the docs - none of this is specific to plugins
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not clear what workflow "library" means here. Simply stating that it's some code you define on the plugin that users can call from their workflows would help. Or maybe a small code example too.
flippedcoder marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is python-specific. Could we maybe do this in a tab? Or otherwise reformat into an SDK-specific section or something?
s/used/usable/
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| - It should be designed to handle being restarted, resumed, or executed independently of where or how it originally began without losing correctness or state consistency. | |
| - It should be designed to handle being restarted, resumed, or executed in a different process from where it originally began without losing correctness or state consistency. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tconley1428, can you provide details about how to test this by disabling the workflow cache, starting with python? My thought is we can add a "Testing your plugin" section at the bottom and then link to that here.
flippedcoder marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
flippedcoder marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
flippedcoder marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
flippedcoder marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tconley1428 my initial reaction was that this is unrealistic, since plugins are generally designed to be reused. I'd expect people to create a plugin class that declares the Workfows rather than make a global variable for people to use. If you agree, can you help update the code, here and in the other examples?
flippedcoder marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
flippedcoder marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- This doesn't quite match what we did for OpenAI -- please double check that this is our best up-to-date advice.
- I can't find it, but I remember seeing a snippet where we pull the user's existing codecs into the payload. Should we do something like that here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe re-word this to be more generic than adding stuff to workflows, since you can do more than that?