Skip to content

Temporal: Braintrust Temporal for ESM projects and fix merging of WorkflowClientInterceptors#1348

Draft
cpinn wants to merge 6 commits intomainfrom
caitlin/fix-temporal-integration
Draft

Temporal: Braintrust Temporal for ESM projects and fix merging of WorkflowClientInterceptors#1348
cpinn wants to merge 6 commits intomainfrom
caitlin/fix-temporal-integration

Conversation

@cpinn
Copy link
Contributor

@cpinn cpinn commented Feb 5, 2026

This PR adds an improvement to our temporal integration to work in esm environments and fixes an issue with the use of the older WorkflowClientInterceptors.

Additional examples have been added:

  • ESM project
  • Example with BraintrustTemporalPlugin, AISdkPlugin and wrapAISDK

@cpinn cpinn changed the title fix merging of temporal interceptors Temporal: Let temporal work with ESM fix merging of old interceptors Feb 6, 2026
@cpinn cpinn changed the title Temporal: Let temporal work with ESM fix merging of old interceptors Temporal: Braintrust Temporal for ESM projects and fix merging of WorkflowClientInterceptors Feb 6, 2026
// 2. wrapAISDKProvider adds LLM tracing (prompts, tokens, completions)
// 3. BraintrustTemporalPlugin traces Temporal workflows and activities
plugins: [
new AiSdkPlugin({
Copy link
Contributor Author

@cpinn cpinn Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ibolmo (and @clutchski since you wrote the python implementation and my know more about temporal in general)

I plan to split this example into two different examples to illustrate the issues I have been BraintrustTemporalPlugin and AISDKPlugin working together.

The two different workflows I have found are:

  1. The BraintrustTemporalPlugin works with just wrapAISDK (no AISDKPlugin) if you create activities in temporal for llm calls.
  2. I found one way of making the AISDKPlugin work with the BraintrustTemporalPlugin (make sure our plugin is registered first) and only wrap the underlying model provider.
// this will wrap just the doGenerate call and map it with out temporal flows
const tracedProvider = wrapAISDKProvider(openai as any);

  const worker = await Worker.create({
    connection,
    namespace: "default",
    taskQueue: TASK_QUEUE,
    // Load the workflows from this example's `src` so the exported
    // workflow functions (`haikuAgent`, `haikuAgentTraced`, etc.) are available.
    workflowsPath: new URL("./workflows.ts", import.meta.url).pathname,
    activities: { getWeather },
    plugins: [
      new AiSdkPlugin({ modelProvider: tracedProvider }),
      new BraintrustTemporalPlugin(),
    ],

The advantage of the temporal AISDKPlugin is that the plugin can work without creating explicit activities for the llm calls but it does that by a low level wrapping which means the trace looks less detailed if I just wrap the model.

What you get with creating activities and using wrapAISDK (which does not support the temporal aisdk plugin type)
Image

What you get with wrapping just the Model Provider
Image

What the user was getting when trying to currently use our workflow integration with just the AISDKPlugin
Image

I am still looking into what might work to wrap the entire plugin but haven't figured it out yet and wondering if I should push out something that sort of works with the model provider wrapping or if that is not that useful.

import { createBraintrustSinks } from "./sinks";

// Add the workflow interceptor package specifier so the Temporal bundler can include it
const WORKFLOW_INTERCEPTORS_SPEC = "@braintrust/temporal/workflow-interceptors";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the important change for ESM to work, Temporal is able to bundle the package by name.

workflow = {
...existing,
...braintrustInterceptor,
calls: [...(existing.calls ?? []), () => braintrustInterceptor],
Copy link
Contributor Author

@cpinn cpinn Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was leading to our plugin being dropped when registered with another plugin (ie. AISDKPlugin).

* Uses a generic type constraint instead of specific LanguageModel version
* to ensure compatibility with both current and future AI SDK versions.
*/
function wrapModel<
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is temp until I decide what I wish to do for the temporal integration

  1. Drop attempting to wrap the temporal plugin and just merge the fix for making our plugin work
  2. Add this simple wrapping on the model
  3. Continue the quest to figure out how to integrate with the plugin

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant