From b17605ce073962125111b510d4965af19eb5cabb Mon Sep 17 00:00:00 2001 From: flippedcoder Date: Wed, 12 Nov 2025 14:28:09 -0600 Subject: [PATCH 01/17] added first draft of the plugin guide --- docs/develop/plugins-guide.mdx | 351 +++++++++++++++++++++++++++++++++ sidebars.js | 1 + 2 files changed, 352 insertions(+) create mode 100644 docs/develop/plugins-guide.mdx diff --git a/docs/develop/plugins-guide.mdx b/docs/develop/plugins-guide.mdx new file mode 100644 index 0000000000..fe19edbaa9 --- /dev/null +++ b/docs/develop/plugins-guide.mdx @@ -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 + +## 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. + +- [Built-in Activities](#built-in-activity) +- [Workflow libraries](#workflow-libraries) +- [Built-in Child Workflows](#built-in-child-workflows) +- [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 + +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. + +#### 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. + +Follow the example for your SDK below: + + + +```python +@activity.defn +async def some_activity() -> None: + return None + +plugin = SimplePlugin( + activities = [some_activity] +) +``` + + + +### Workflow librarires + +You can provide a library with functionality for use within a Workflow. Your library will call elements you include in your Plugin Activities, Child Workflows, Signals, Updates, Queries, Nexus Operations, Interceptors, Data Converters, and any other code as long as it follows these requirements: + +- It should be [deterministic](https://docs.temporal.io/workflows#deterministic-constraints), running the same way every time it’s executed. Non-deterministic code should go in Activities or Nexus Operations. +- It should be used in the Python [sandbox](https://docs.temporal.io/develop/python/python-sdk-sandbox). +- 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 run quickly since it may run many times during a long Workflow execution. + +A Plugin should allow a user to decompose their Workflows into Activities, as well as Child Workflows and Nexus Calls when needed. This gives users granular control through retries and timeouts, debuggability through the Temporal UI, operability with resets, pauses, and cancels, memoization for efficiency and resumability, and scalability using task queues and Workers. + +Users use Workflows for: + +- Orchestration and decision-making +- Interactivity via [message-passing](https://docs.temporal.io/evaluate/development-production-features/workflow-message-passing) +- Tracing and observability. + +#### Making changes to your Workflow Library + +Your users may want to keep their Workflows running across deployments of their code. If their deployment includes a new version of your Plugin, changes to your Plugin could break Workflow code that started before the new version was deployed. This can be due to [non-deterministic behavior from code changes](https://docs.temporal.io/workflow-definition#non-deterministic-change) in your Plugin. + +Therefore, as you make changes, you need to use [patching](https://docs.temporal.io/patching) and [replay testing](https://docs.temporal.io/develop/python/testing-suite#replay) to make sure that you’re not causing non-determinism errors for your users. + +#### Example of a Workflow library that uses a Plugin in Python + +- [Implementation of the `OpenAIAgentsPlugin`](https://github.com/temporalio/sdk-python/tree/main/temporalio/contrib/openai_agents) +- [Example of replay testing](https://github.com/temporalio/sdk-python/blob/main/tests/contrib/openai_agents/test_openai_replay.py) + +### Child Workflow + +You can provide a built-in Workflow in a `SimplePlugin`. It’s callable as a Child Workflow or standalone. When you want to provide a piece of functionality that's more complex than an Activity, you can: + +- Use a [Workflow Library](#workflow-libraries) that runs directly in the end user’s Workflow +- Add a Child Workflow + +Consider adding a Child Workflow when one or more of these conditions applies: + +- That child should outlive the parent. +- The Workflow Event History would otherwise [not scale](https://docs.temporal.io/workflow-execution/event#event-history-limits) in parent Workflows. +- When you want a separate Workflow ID for the child so that it can be operated independently of the parent's state (canceled, terminated, paused). + +Any Workflow can be run as a standalone Workflow or as a Child Workflow, so registering a Child Workflow in a `SimplePlugin` is the same as registering any Workflow. + +Follow the example for your SDK below: + + + +```python +@workflow.defn +class HelloWorkflow: + @workflow.run + async def run(self, name: str) -> str: + return f"Hello, {name}!" + +plugin = SimplePlugin( + workflows = [HelloWorkflow] +) + +... + +client = await Client.connect( + "localhost:7233", + plugins=[ + plugin, + ], +) +async with Worker( + client, + task_queue="task-queue", +): + client.execute_workflow( + HelloWorkflow.run, + "Tim", + task_queue=worker.task_queue, + ) +``` + + + +### Nexus Operation + +Nexus calls are used from Workflows similar to Activities and you can check out some common [Nexus Use Cases](https://docs.temporal.io/nexus/use-cases). Like Activities, Nexus Call arguments and return values must be serializable. + +To register Nexus handlers in Workflows, follow the example for your SDK below: + + + +```python +@nexusrpc.service +class WeatherService: + get_weather_nexus_operation: nexusrpc.Operation[WeatherInput, Weather] + +@nexusrpc.handler.service_handler(service=WeatherService) +class WeatherServiceHandler: + @nexusrpc.handler.sync_operation + async def get_weather_nexus_operation( + self, ctx: nexusrpc.handler.StartOperationContext, input: WeatherInput + ) -> Weather: + return Weather( + city=input.city, temperature_range="14-20C", conditions="Sunny with wind." + ) + +plugin = SimplePlugin( + nexus_service_handlers = [WeatherServiceHandler()] +) +``` + + + +### Data Converter + +A [Custom Data Converter](https://docs.temporal.io/default-custom-data-converters#custom-data-converter) can alter data formats or provide compression or encryption. + +Note that you can use an existing Data Converter such as `PydanticPayloadConverter` in your Plugin. + +To add a Custom Data Converter to a Plugin, follow the example for your SDK below: + + + +```python +def add_converter(converter: Optional[DataConverter]) -> DataConverte + if converter is None or converter == temporalio.converter.DataConverter.default + return pydantic_data_converter + # Should consider interactions with other plugins, + # as this will override the data converter. + # This may mean failing, warning, or something else + return converter + +plugin = SimplePlugin( + data_converter = add_converter +) +``` + + + +### Interceptors + +Interceptors are middleware that can run before and after various calls such as Activities, Workflows, and Signals. You can [learn more about interceptors](https://docs.temporal.io/develop/python/interceptors) for the details of implementing them. They're used to: + +- Create side effects such as logging and tracing. +- Modify arguments, such as adding headers for authorization or tracing propagation. + +To add one to a Plugin, follow the example for your SDK below:: + + + +```python +class SomeWorkerInterceptor( + temporalio.worker.Interceptor +): + +# ... + +plugin = SimplePlugin( + worker_interceptors = [SomeWorkerInterceptor()] +) +``` + + + +## Advanced Topics for Plugins + +If you go deeper into `SimplePlugin`, you can note that it aggregates a pair of raw Plugin classes that you can use for a higher level of flexibility: a Worker Plugin and a client Plugin. + +- Worker Plugins contain functionality that runs inside your users’ Workflows. +- Client Plugins contain functionality that runs when Workflows are created and return results. + +If your Plugin implements both of them, registering it in the client will also register it in Workers created with that client. + +### Client Plugin + +Client Plugins are provided to the Temporal client on creation. They can change client configurations and service client configurations. `ClientConfig` contains settings like client Interceptors and DataConverters. `ConnectConfig` configures the actual network connections to the local or cloud Temporal server with values like an API key. This is the basic implementation of a client Plugin: + + + +```python +class MyAdvancedClientPlugin(temporalio.client.Plugin): + + def configure_client(self, config: ClientConfig) -> ClientConfig: + return config + + async def connect_service_client( + self, + config: ConnectConfig, + next: Callable[[ConnectConfig], Awaitable[ServiceClient]], + ) -> temporalio.service.ServiceClient: + return await next(config) +``` + + + +The primary use case for integrations so far is setting a `DataConverter`, like in the [Data Converter example](#data-converter). + +### Worker Plugin + +Worker Plugins are provided at Worker creation and have more capabilities and corresponding implementation than client Plugins. They can change Worker configurations, run code during the Worker lifetime, and manage the Replayer in a similar way. You can learn more about the [Replayer](#replayer) in a later section. + +Similar to `configure_client` above, you implement `configure_worker` and `configure_replayer` to change any necessary configurations. In addition, `run_worker` allows you to execute code before and after the Worker runs. This can be used to set up resources or globals for use during the Worker execution. `run_replayer` does the same for the Replayer, but keep in mind that the Replayer has a more complex return type. This is a basic implementation of a Worker plugin: + + + +```python +class MyAdvancedWorkerPlugin(temporalio.worker.Plugin): + def configure_worker(self, config: WorkerConfig) -> WorkerConfig: + return config + + async def run_worker( + self, worker: Worker, next: Callable[[Worker], Awaitable[None]] + ) -> None: + next(worker) + + def configure_replayer(self, config: ReplayerConfig) -> ReplayerConfig: + return config + + def run_replayer( + self, + replayer: Replayer, + histories: AsyncIterator[temporalio.client.WorkflowHistory], + next: Callable[ + [Replayer, AsyncIterator[WorkflowHistory]], + AbstractAsyncContextManager[AsyncIterator[WorkflowReplayResult]], + ], + ) -> AbstractAsyncContextManager[AsyncIterator[WorkflowReplayResult]]: + return next(replayer, histories) +``` + + + +### Replayer + +The Replayer allows Workflow authors to validate that their Workflows will work after changes to either the Workflow or a library they depend on. It’s normally used in test runs or when testing Workers before they roll out in production. + +The Replayer runs on a Workflow History created by a previous Workflow run. Suppose something in the Workflow or underlying code has changed in a way which could potentially cause a non-determinism error. In that case, the Replayer will notice the change in the way it runs compared to the history provided. + +The Replayer is typically configured identically to the Worker and client. Ff you’re using `SimplePlugin`, this is already handled for you. + +If you need to do something custom for the Replayer, you can configure it directly: + + + +```python +class MyAdvancedWorkerPlugin(temporalio.worker.Plugin): + def configure_replayer(self, config: ReplayerConfig) -> ReplayerConfig: + return config + + def run_replayer( + self, + replayer: Replayer, + histories: AsyncIterator[temporalio.client.WorkflowHistory], + next: Callable[ + [Replayer, AsyncIterator[WorkflowHistory]], + AbstractAsyncContextManager[AsyncIterator[WorkflowReplayResult]], + ], + ) -> AbstractAsyncContextManager[AsyncIterator[WorkflowReplayResult]]: + return next(replayer, histories) +``` + + \ No newline at end of file diff --git a/sidebars.js b/sidebars.js index ed1a0b14d0..b887b39187 100644 --- a/sidebars.js +++ b/sidebars.js @@ -309,6 +309,7 @@ module.exports = { 'develop/activity-retry-simulator', 'develop/worker-performance', 'develop/safe-deployments', + 'develop/plugins-guide', ], }, { From 694b01d60fd48f5ea4bce79f29d4b4ca6451ddd6 Mon Sep 17 00:00:00 2001 From: flippedcoder Date: Wed, 12 Nov 2025 14:45:51 -0600 Subject: [PATCH 02/17] fixed link --- docs/develop/plugins-guide.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/develop/plugins-guide.mdx b/docs/develop/plugins-guide.mdx index fe19edbaa9..ebd2cad762 100644 --- a/docs/develop/plugins-guide.mdx +++ b/docs/develop/plugins-guide.mdx @@ -66,7 +66,7 @@ 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. +- 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. #### Timeouts and retry policies From df3ee7c4ffa6d40c2381bab344990903b0c841b4 Mon Sep 17 00:00:00 2001 From: flippedcoder Date: Wed, 12 Nov 2025 15:01:52 -0600 Subject: [PATCH 03/17] fixed more links --- docs/develop/plugins-guide.mdx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/develop/plugins-guide.mdx b/docs/develop/plugins-guide.mdx index ebd2cad762..52bdf875b6 100644 --- a/docs/develop/plugins-guide.mdx +++ b/docs/develop/plugins-guide.mdx @@ -30,7 +30,7 @@ Here are some common use cases for plugins: 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. +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 @@ -44,7 +44,7 @@ If you prefer to learn by getting hands-on with code, check out some existing pl 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. - [Built-in Activities](#built-in-activity) -- [Workflow libraries](#workflow-libraries) +- [Workflow libraries](#workflow-librarires) - [Built-in Child Workflows](#built-in-child-workflows) - [Built-in Nexus Operations](#built-in-nexus-operations) - [Custom Data Converters](#custom-data-converters) @@ -119,7 +119,7 @@ Therefore, as you make changes, you need to use [patching](https://docs.temporal - [Implementation of the `OpenAIAgentsPlugin`](https://github.com/temporalio/sdk-python/tree/main/temporalio/contrib/openai_agents) - [Example of replay testing](https://github.com/temporalio/sdk-python/blob/main/tests/contrib/openai_agents/test_openai_replay.py) -### Child Workflow +### Built-in Child Workflows You can provide a built-in Workflow in a `SimplePlugin`. It’s callable as a Child Workflow or standalone. When you want to provide a piece of functionality that's more complex than an Activity, you can: @@ -170,7 +170,7 @@ async with Worker( -### Nexus Operation +### Built-in Nexus Operations Nexus calls are used from Workflows similar to Activities and you can check out some common [Nexus Use Cases](https://docs.temporal.io/nexus/use-cases). Like Activities, Nexus Call arguments and return values must be serializable. @@ -200,7 +200,7 @@ plugin = SimplePlugin( -### Data Converter +### Custom Data Converters A [Custom Data Converter](https://docs.temporal.io/default-custom-data-converters#custom-data-converter) can alter data formats or provide compression or encryption. From 31072a53b05a73405ff23f8e769527fd5cf15aa7 Mon Sep 17 00:00:00 2001 From: flippedcoder Date: Wed, 12 Nov 2025 15:06:34 -0600 Subject: [PATCH 04/17] fixed typos and links --- docs/develop/plugins-guide.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/develop/plugins-guide.mdx b/docs/develop/plugins-guide.mdx index 52bdf875b6..74ee42ff61 100644 --- a/docs/develop/plugins-guide.mdx +++ b/docs/develop/plugins-guide.mdx @@ -44,7 +44,7 @@ If you prefer to learn by getting hands-on with code, check out some existing pl 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. - [Built-in Activities](#built-in-activity) -- [Workflow libraries](#workflow-librarires) +- [Workflow libraries](#workflow-libraries) - [Built-in Child Workflows](#built-in-child-workflows) - [Built-in Nexus Operations](#built-in-nexus-operations) - [Custom Data Converters](#custom-data-converters) @@ -91,7 +91,7 @@ plugin = SimplePlugin( -### Workflow librarires +### Workflow libraries You can provide a library with functionality for use within a Workflow. Your library will call elements you include in your Plugin Activities, Child Workflows, Signals, Updates, Queries, Nexus Operations, Interceptors, Data Converters, and any other code as long as it follows these requirements: @@ -282,7 +282,7 @@ class MyAdvancedClientPlugin(temporalio.client.Plugin): -The primary use case for integrations so far is setting a `DataConverter`, like in the [Data Converter example](#data-converter). +The primary use case for integrations so far is setting a `DataConverter`, like in the [Data Converter example](#custom-data-converters). ### Worker Plugin From a1889cd6de68fcd6a17c1600d655e1a69759f0dd Mon Sep 17 00:00:00 2001 From: Milecia McG <47196133+flippedcoder@users.noreply.github.com> Date: Fri, 14 Nov 2025 09:45:42 -0600 Subject: [PATCH 05/17] Update docs/develop/plugins-guide.mdx Co-authored-by: Spencer Judge --- docs/develop/plugins-guide.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/develop/plugins-guide.mdx b/docs/develop/plugins-guide.mdx index 74ee42ff61..1993cda911 100644 --- a/docs/develop/plugins-guide.mdx +++ b/docs/develop/plugins-guide.mdx @@ -253,7 +253,7 @@ plugin = SimplePlugin( ## Advanced Topics for Plugins -If you go deeper into `SimplePlugin`, you can note that it aggregates a pair of raw Plugin classes that you can use for a higher level of flexibility: a Worker Plugin and a client Plugin. +If you go deeper into `SimplePlugin`, you'll see it aggregates a pair of raw Plugin classes that you can use for a higher level of flexibility: a Worker Plugin and a client Plugin. - Worker Plugins contain functionality that runs inside your users’ Workflows. - Client Plugins contain functionality that runs when Workflows are created and return results. From d046ef2d28a1b4c81685a391fc6f0999b42727fe Mon Sep 17 00:00:00 2001 From: Milecia McG <47196133+flippedcoder@users.noreply.github.com> Date: Fri, 14 Nov 2025 09:45:52 -0600 Subject: [PATCH 06/17] Update docs/develop/plugins-guide.mdx Co-authored-by: Spencer Judge --- docs/develop/plugins-guide.mdx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/develop/plugins-guide.mdx b/docs/develop/plugins-guide.mdx index 1993cda911..62f0dbbff6 100644 --- a/docs/develop/plugins-guide.mdx +++ b/docs/develop/plugins-guide.mdx @@ -241,8 +241,7 @@ To add one to a Plugin, follow the example for your SDK below:: class SomeWorkerInterceptor( temporalio.worker.Interceptor ): - -# ... + pass # Your implementation plugin = SimplePlugin( worker_interceptors = [SomeWorkerInterceptor()] From 1b8937e11cda0f1c81af776cf1506596dbac3e24 Mon Sep 17 00:00:00 2001 From: Milecia McG <47196133+flippedcoder@users.noreply.github.com> Date: Fri, 14 Nov 2025 09:46:23 -0600 Subject: [PATCH 07/17] Update docs/develop/plugins-guide.mdx Co-authored-by: Spencer Judge --- docs/develop/plugins-guide.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/develop/plugins-guide.mdx b/docs/develop/plugins-guide.mdx index 62f0dbbff6..2c3236b467 100644 --- a/docs/develop/plugins-guide.mdx +++ b/docs/develop/plugins-guide.mdx @@ -119,7 +119,7 @@ Therefore, as you make changes, you need to use [patching](https://docs.temporal - [Implementation of the `OpenAIAgentsPlugin`](https://github.com/temporalio/sdk-python/tree/main/temporalio/contrib/openai_agents) - [Example of replay testing](https://github.com/temporalio/sdk-python/blob/main/tests/contrib/openai_agents/test_openai_replay.py) -### Built-in Child Workflows +### Built-in Workflows You can provide a built-in Workflow in a `SimplePlugin`. It’s callable as a Child Workflow or standalone. When you want to provide a piece of functionality that's more complex than an Activity, you can: From 4f372534ad5032ef9b3d88a122faeef6e144bd26 Mon Sep 17 00:00:00 2001 From: Milecia McG <47196133+flippedcoder@users.noreply.github.com> Date: Fri, 14 Nov 2025 09:46:39 -0600 Subject: [PATCH 08/17] Update docs/develop/plugins-guide.mdx Co-authored-by: Spencer Judge --- docs/develop/plugins-guide.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/develop/plugins-guide.mdx b/docs/develop/plugins-guide.mdx index 2c3236b467..07ef92a9c3 100644 --- a/docs/develop/plugins-guide.mdx +++ b/docs/develop/plugins-guide.mdx @@ -110,7 +110,7 @@ Users use Workflows for: #### Making changes to your Workflow Library -Your users may want to keep their Workflows running across deployments of their code. If their deployment includes a new version of your Plugin, changes to your Plugin could break Workflow code that started before the new version was deployed. This can be due to [non-deterministic behavior from code changes](https://docs.temporal.io/workflow-definition#non-deterministic-change) in your Plugin. +Your users may want to keep their Workflows running across deployments of their worker code. If their deployment includes a new version of your Plugin, changes to your Plugin could break Workflow code that started before the new version was deployed. This can be due to [non-deterministic behavior from code changes](https://docs.temporal.io/workflow-definition#non-deterministic-change) in your Plugin. Therefore, as you make changes, you need to use [patching](https://docs.temporal.io/patching) and [replay testing](https://docs.temporal.io/develop/python/testing-suite#replay) to make sure that you’re not causing non-determinism errors for your users. From 2bde07cb1fcd30f4c6d001e27719c1915ece088b Mon Sep 17 00:00:00 2001 From: Milecia McG <47196133+flippedcoder@users.noreply.github.com> Date: Fri, 14 Nov 2025 09:48:50 -0600 Subject: [PATCH 09/17] Update docs/develop/plugins-guide.mdx Co-authored-by: Drew Hoskins <166441821+drewhoskins-temporal@users.noreply.github.com> --- docs/develop/plugins-guide.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/develop/plugins-guide.mdx b/docs/develop/plugins-guide.mdx index 07ef92a9c3..b3d09c9710 100644 --- a/docs/develop/plugins-guide.mdx +++ b/docs/develop/plugins-guide.mdx @@ -211,7 +211,7 @@ To add a Custom Data Converter to a Plugin, follow the example for your SDK belo ```python -def add_converter(converter: Optional[DataConverter]) -> DataConverte +def add_converter(converter: Optional[DataConverter]) -> DataConverter if converter is None or converter == temporalio.converter.DataConverter.default return pydantic_data_converter # Should consider interactions with other plugins, From e1d905ac70b2a8a24cbef888421402015ee96e12 Mon Sep 17 00:00:00 2001 From: Milecia McG <47196133+flippedcoder@users.noreply.github.com> Date: Fri, 14 Nov 2025 09:49:00 -0600 Subject: [PATCH 10/17] Update docs/develop/plugins-guide.mdx Co-authored-by: Drew Hoskins <166441821+drewhoskins-temporal@users.noreply.github.com> --- docs/develop/plugins-guide.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/develop/plugins-guide.mdx b/docs/develop/plugins-guide.mdx index b3d09c9710..65cc24cba2 100644 --- a/docs/develop/plugins-guide.mdx +++ b/docs/develop/plugins-guide.mdx @@ -204,7 +204,7 @@ plugin = SimplePlugin( A [Custom Data Converter](https://docs.temporal.io/default-custom-data-converters#custom-data-converter) can alter data formats or provide compression or encryption. -Note that you can use an existing Data Converter such as `PydanticPayloadConverter` in your Plugin. +Note that you can use an existing Data Converter such as, in python, `PydanticPayloadConverter` in your Plugin. To add a Custom Data Converter to a Plugin, follow the example for your SDK below: From 5968e4978bfa7017d6e17381a7bfdd40331eb5e3 Mon Sep 17 00:00:00 2001 From: Milecia McG <47196133+flippedcoder@users.noreply.github.com> Date: Fri, 14 Nov 2025 09:50:44 -0600 Subject: [PATCH 11/17] Update docs/develop/plugins-guide.mdx Co-authored-by: Drew Hoskins <166441821+drewhoskins-temporal@users.noreply.github.com> --- docs/develop/plugins-guide.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/develop/plugins-guide.mdx b/docs/develop/plugins-guide.mdx index 65cc24cba2..b7ae904634 100644 --- a/docs/develop/plugins-guide.mdx +++ b/docs/develop/plugins-guide.mdx @@ -73,7 +73,7 @@ Here are some best practices you can use when you are making Activity plugins: 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 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. Follow the example for your SDK below: From 582c7d69e47a6f3d035ab2873499a13bd83ab101 Mon Sep 17 00:00:00 2001 From: Milecia McG <47196133+flippedcoder@users.noreply.github.com> Date: Fri, 14 Nov 2025 09:51:03 -0600 Subject: [PATCH 12/17] Update docs/develop/plugins-guide.mdx Co-authored-by: Drew Hoskins <166441821+drewhoskins-temporal@users.noreply.github.com> --- docs/develop/plugins-guide.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/develop/plugins-guide.mdx b/docs/develop/plugins-guide.mdx index b7ae904634..27f3f5ba5e 100644 --- a/docs/develop/plugins-guide.mdx +++ b/docs/develop/plugins-guide.mdx @@ -98,7 +98,7 @@ You can provide a library with functionality for use within a Workflow. Your lib - It should be [deterministic](https://docs.temporal.io/workflows#deterministic-constraints), running the same way every time it’s executed. Non-deterministic code should go in Activities or Nexus Operations. - It should be used in the Python [sandbox](https://docs.temporal.io/develop/python/python-sdk-sandbox). - 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 run quickly since it may run many times during a long Workflow execution. +- It should run quickly since it may be replayed many times during a long Workflow execution. More expensive code should go in Activities or Nexus Operations. A Plugin should allow a user to decompose their Workflows into Activities, as well as Child Workflows and Nexus Calls when needed. This gives users granular control through retries and timeouts, debuggability through the Temporal UI, operability with resets, pauses, and cancels, memoization for efficiency and resumability, and scalability using task queues and Workers. From 06122043224105244e9c457a86f7a782929a33ff Mon Sep 17 00:00:00 2001 From: Milecia McG <47196133+flippedcoder@users.noreply.github.com> Date: Fri, 14 Nov 2025 09:51:15 -0600 Subject: [PATCH 13/17] Update docs/develop/plugins-guide.mdx Co-authored-by: Drew Hoskins <166441821+drewhoskins-temporal@users.noreply.github.com> --- docs/develop/plugins-guide.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/develop/plugins-guide.mdx b/docs/develop/plugins-guide.mdx index 27f3f5ba5e..6e131e5afa 100644 --- a/docs/develop/plugins-guide.mdx +++ b/docs/develop/plugins-guide.mdx @@ -93,7 +93,7 @@ plugin = SimplePlugin( ### Workflow libraries -You can provide a library with functionality for use within a Workflow. Your library will call elements you include in your Plugin Activities, Child Workflows, Signals, Updates, Queries, Nexus Operations, Interceptors, Data Converters, and any other code as long as it follows these requirements: +You can provide a library with functionality for use within a Workflow. Your library will call elements you include in your Plugin: Activities, Child Workflows, Signals, Updates, Queries, Nexus Operations, Interceptors, Data Converters, and any other code as long as it follows these requirements: - It should be [deterministic](https://docs.temporal.io/workflows#deterministic-constraints), running the same way every time it’s executed. Non-deterministic code should go in Activities or Nexus Operations. - It should be used in the Python [sandbox](https://docs.temporal.io/develop/python/python-sdk-sandbox). From 5a037d7451ac71744e0589ced88eef8a5baafe5e Mon Sep 17 00:00:00 2001 From: Milecia McG <47196133+flippedcoder@users.noreply.github.com> Date: Fri, 14 Nov 2025 09:51:33 -0600 Subject: [PATCH 14/17] Update docs/develop/plugins-guide.mdx Co-authored-by: Drew Hoskins <166441821+drewhoskins-temporal@users.noreply.github.com> --- docs/develop/plugins-guide.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/develop/plugins-guide.mdx b/docs/develop/plugins-guide.mdx index 6e131e5afa..d88b28a112 100644 --- a/docs/develop/plugins-guide.mdx +++ b/docs/develop/plugins-guide.mdx @@ -112,7 +112,7 @@ Users use Workflows for: Your users may want to keep their Workflows running across deployments of their worker code. If their deployment includes a new version of your Plugin, changes to your Plugin could break Workflow code that started before the new version was deployed. This can be due to [non-deterministic behavior from code changes](https://docs.temporal.io/workflow-definition#non-deterministic-change) in your Plugin. -Therefore, as you make changes, you need to use [patching](https://docs.temporal.io/patching) and [replay testing](https://docs.temporal.io/develop/python/testing-suite#replay) to make sure that you’re not causing non-determinism errors for your users. +Therefore, it's recommended that you set up [replay testing](https://docs.temporal.io/develop/python/testing-suite#replay) to make sure that you’re not causing non-determinism errors for your users. If you make substantive changes, you need to use [patching](https://docs.temporal.io/patching). #### Example of a Workflow library that uses a Plugin in Python From 8b2bf1c9912940556f661bbdb33b60bed81d653f Mon Sep 17 00:00:00 2001 From: Milecia McG <47196133+flippedcoder@users.noreply.github.com> Date: Fri, 14 Nov 2025 09:52:50 -0600 Subject: [PATCH 15/17] Update docs/develop/plugins-guide.mdx Co-authored-by: Drew Hoskins <166441821+drewhoskins-temporal@users.noreply.github.com> --- docs/develop/plugins-guide.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/develop/plugins-guide.mdx b/docs/develop/plugins-guide.mdx index d88b28a112..cb46524af0 100644 --- a/docs/develop/plugins-guide.mdx +++ b/docs/develop/plugins-guide.mdx @@ -97,7 +97,7 @@ You can provide a library with functionality for use within a Workflow. Your lib - It should be [deterministic](https://docs.temporal.io/workflows#deterministic-constraints), running the same way every time it’s executed. Non-deterministic code should go in Activities or Nexus Operations. - It should be used in the Python [sandbox](https://docs.temporal.io/develop/python/python-sdk-sandbox). -- 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. - It should run quickly since it may be replayed many times during a long Workflow execution. More expensive code should go in Activities or Nexus Operations. A Plugin should allow a user to decompose their Workflows into Activities, as well as Child Workflows and Nexus Calls when needed. This gives users granular control through retries and timeouts, debuggability through the Temporal UI, operability with resets, pauses, and cancels, memoization for efficiency and resumability, and scalability using task queues and Workers. From dcb500dfdb0edf066fa741104f05b2166bb3145b Mon Sep 17 00:00:00 2001 From: flippedcoder Date: Fri, 14 Nov 2025 09:54:06 -0600 Subject: [PATCH 16/17] minor updates --- docs/develop/plugins-guide.mdx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/develop/plugins-guide.mdx b/docs/develop/plugins-guide.mdx index cb46524af0..8daa471909 100644 --- a/docs/develop/plugins-guide.mdx +++ b/docs/develop/plugins-guide.mdx @@ -37,15 +37,14 @@ For advanced use cases, you can extend the methods in lower-level classes that S 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. +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. - [Built-in Activities](#built-in-activity) - [Workflow libraries](#workflow-libraries) -- [Built-in Child Workflows](#built-in-child-workflows) +- [Built-in Workflows](#built-in-workflows) - [Built-in Nexus Operations](#built-in-nexus-operations) - [Custom Data Converters](#custom-data-converters) - [Interceptors](#interceptors) @@ -110,7 +109,7 @@ Users use Workflows for: #### Making changes to your Workflow Library -Your users may want to keep their Workflows running across deployments of their worker code. If their deployment includes a new version of your Plugin, changes to your Plugin could break Workflow code that started before the new version was deployed. This can be due to [non-deterministic behavior from code changes](https://docs.temporal.io/workflow-definition#non-deterministic-change) in your Plugin. +Your users may want to keep their Workflows running across deployments of their Worker code. If their deployment includes a new version of your Plugin, changes to your Plugin could break Workflow code that started before the new version was deployed. This can be due to [non-deterministic behavior from code changes](https://docs.temporal.io/workflow-definition#non-deterministic-change) in your Plugin. Therefore, it's recommended that you set up [replay testing](https://docs.temporal.io/develop/python/testing-suite#replay) to make sure that you’re not causing non-determinism errors for your users. If you make substantive changes, you need to use [patching](https://docs.temporal.io/patching). From 2e7a1cc9cb59a44863f9a222bda7abbd0179c16e Mon Sep 17 00:00:00 2001 From: flippedcoder Date: Fri, 14 Nov 2025 11:45:51 -0600 Subject: [PATCH 17/17] removed extra space --- docs/develop/plugins-guide.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/develop/plugins-guide.mdx b/docs/develop/plugins-guide.mdx index 8daa471909..c8bdbac204 100644 --- a/docs/develop/plugins-guide.mdx +++ b/docs/develop/plugins-guide.mdx @@ -97,7 +97,7 @@ You can provide a library with functionality for use within a Workflow. Your lib - It should be [deterministic](https://docs.temporal.io/workflows#deterministic-constraints), running the same way every time it’s executed. Non-deterministic code should go in Activities or Nexus Operations. - It should be used in the Python [sandbox](https://docs.temporal.io/develop/python/python-sdk-sandbox). - 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. -- It should run quickly since it may be replayed many times during a long Workflow execution. More expensive code should go in Activities or Nexus Operations. +- It should run quickly since it may be replayed many times during a long Workflow execution. More expensive code should go in Activities or Nexus Operations. A Plugin should allow a user to decompose their Workflows into Activities, as well as Child Workflows and Nexus Calls when needed. This gives users granular control through retries and timeouts, debuggability through the Temporal UI, operability with resets, pauses, and cancels, memoization for efficiency and resumability, and scalability using task queues and Workers.