@braintrust/temporal 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,40 @@
1
+ # Mise will automatically read and use .tool-versions files as well as this file.
2
+ [settings]
3
+ experimental=true
4
+
5
+ [env]
6
+ # See env.example to configure API keys.
7
+ _.file = ".env"
8
+
9
+ [tools]
10
+ node = "20"
11
+ temporal = "latest"
12
+ overmind = "latest"
13
+
14
+ [hooks]
15
+ postinstall = "mise run install"
16
+
17
+ [tasks.install]
18
+ description = "Install dependencies"
19
+ run = "pnpm install --ignore-workspace"
20
+
21
+ [tasks.server]
22
+ description = "Run temporal server and workers"
23
+ run = "overmind s"
24
+
25
+ [tasks.workflow]
26
+ description = "Run workflow client"
27
+ run = "pnpm exec ts-node src/client.ts"
28
+
29
+ [tasks.stop]
30
+ description = "Stop temporal server and workers"
31
+ run = "overmind quit || true"
32
+
33
+ [tasks.kill]
34
+ description = "Force kill temporal server and workers"
35
+ run = """
36
+ pkill -f 'examples/temporal.*ts-node' 2>/dev/null || true
37
+ pkill -f 'overmind.*temporal' 2>/dev/null || true
38
+ rm -f .overmind.sock
39
+ echo 'Server killed'
40
+ """
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "temporal-example",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "build": "tsc",
7
+ "worker": "pnpm exec ts-node src/worker.ts",
8
+ "client": "pnpm exec ts-node src/client.ts"
9
+ },
10
+ "dependencies": {
11
+ "@braintrust/temporal": "^0.1.0",
12
+ "@temporalio/activity": "^1.11.0",
13
+ "@temporalio/client": "^1.11.0",
14
+ "@temporalio/common": "^1.11.0",
15
+ "@temporalio/worker": "^1.11.0",
16
+ "@temporalio/workflow": "^1.11.0",
17
+ "braintrust": "^2.0.0",
18
+ "uuid": "^9.0.0"
19
+ },
20
+ "devDependencies": {
21
+ "@types/node": "^20.0.0",
22
+ "@types/uuid": "^9.0.0",
23
+ "ts-node": "^10.9.0",
24
+ "typescript": "^5.0.0"
25
+ }
26
+ }
@@ -0,0 +1,42 @@
1
+ import * as braintrust from "braintrust";
2
+
3
+ export interface TaskInput {
4
+ value: number;
5
+ }
6
+
7
+ export async function addTen(input: TaskInput): Promise<number> {
8
+ console.log(`Adding 10 to ${input.value}`);
9
+
10
+ // Test child span within activity
11
+ const result = await braintrust.traced(
12
+ async (span) => {
13
+ span.log({
14
+ input: { value: input.value, operation: "add", operand: 10 },
15
+ });
16
+ await new Promise((resolve) => setTimeout(resolve, 500));
17
+ const sum = input.value + 10;
18
+ span.log({ output: sum });
19
+ return sum;
20
+ },
21
+ { name: "compute.addition" },
22
+ );
23
+
24
+ console.log(`Result: ${input.value} + 10 = ${result}`);
25
+ return result;
26
+ }
27
+
28
+ export async function multiplyByTwo(input: TaskInput): Promise<number> {
29
+ console.log(`Multiplying ${input.value} by 2`);
30
+ await new Promise((resolve) => setTimeout(resolve, 300));
31
+ const result = input.value * 2;
32
+ console.log(`Result: ${input.value} * 2 = ${result}`);
33
+ return result;
34
+ }
35
+
36
+ export async function subtractFive(input: TaskInput): Promise<number> {
37
+ console.log(`Subtracting 5 from ${input.value}`);
38
+ await new Promise((resolve) => setTimeout(resolve, 300));
39
+ const result = input.value - 5;
40
+ console.log(`Result: ${input.value} - 5 = ${result}`);
41
+ return result;
42
+ }
@@ -0,0 +1,53 @@
1
+ import { Client, Connection } from "@temporalio/client";
2
+ import { v4 as uuid } from "uuid";
3
+ import * as braintrust from "braintrust";
4
+ import { BraintrustTemporalPlugin } from "@braintrust/temporal";
5
+ import { simpleWorkflow } from "./workflows";
6
+ import type { TaskInput } from "./activities";
7
+
8
+ const TASK_QUEUE = "braintrust-example-task-queue";
9
+
10
+ async function main() {
11
+ braintrust.initLogger({ projectName: "temporal-example" });
12
+
13
+ const connection = await Connection.connect({
14
+ address: "localhost:7233",
15
+ });
16
+
17
+ const client = new Client({
18
+ connection,
19
+ namespace: "default",
20
+ plugins: [new BraintrustTemporalPlugin()],
21
+ });
22
+
23
+ const inputData: TaskInput = { value: 5 };
24
+ const workflowId = `simple-workflow-${uuid().slice(0, 8)}`;
25
+
26
+ console.log(`Starting workflow with value: ${inputData.value}`);
27
+ console.log(`Workflow ID: ${workflowId}`);
28
+
29
+ // Wrap in a Braintrust span
30
+ await braintrust.traced(
31
+ async (span) => {
32
+ const handle = await client.workflow.start(simpleWorkflow, {
33
+ args: [inputData],
34
+ taskQueue: TASK_QUEUE,
35
+ workflowId,
36
+ });
37
+
38
+ const result = await handle.result();
39
+ span.log({ output: result });
40
+ console.log(`\nResult: ${result}`);
41
+ console.log(`\nView trace: ${span.link()}`);
42
+ return result;
43
+ },
44
+ { name: "temporal.client.simpleWorkflow" },
45
+ );
46
+
47
+ await braintrust.flush();
48
+ }
49
+
50
+ main().catch((err) => {
51
+ console.error(err);
52
+ process.exit(1);
53
+ });
@@ -0,0 +1,31 @@
1
+ import { Worker, NativeConnection } from "@temporalio/worker";
2
+ import * as braintrust from "braintrust";
3
+ import { BraintrustTemporalPlugin } from "@braintrust/temporal";
4
+ import * as activities from "./activities";
5
+
6
+ const TASK_QUEUE = "braintrust-example-task-queue";
7
+
8
+ async function main() {
9
+ braintrust.initLogger({ projectName: "temporal-example" });
10
+
11
+ const connection = await NativeConnection.connect({
12
+ address: "localhost:7233",
13
+ });
14
+
15
+ const worker = await Worker.create({
16
+ connection,
17
+ namespace: "default",
18
+ taskQueue: TASK_QUEUE,
19
+ workflowsPath: require.resolve("./workflows"),
20
+ activities,
21
+ plugins: [new BraintrustTemporalPlugin()],
22
+ });
23
+
24
+ console.log(`Worker started on task queue: ${TASK_QUEUE}`);
25
+ await worker.run();
26
+ }
27
+
28
+ main().catch((err) => {
29
+ console.error(err);
30
+ process.exit(1);
31
+ });
@@ -0,0 +1,47 @@
1
+ import {
2
+ proxyActivities,
3
+ sleep,
4
+ workflowInfo,
5
+ defineSignal,
6
+ setHandler,
7
+ log,
8
+ } from "@temporalio/workflow";
9
+ import type * as activities from "./activities";
10
+
11
+ const { addTen, multiplyByTwo, subtractFive } = proxyActivities<
12
+ typeof activities
13
+ >({
14
+ startToCloseTimeout: "10 seconds",
15
+ });
16
+
17
+ export const addSignalValue = defineSignal<[number]>("addSignalValue");
18
+
19
+ export interface TaskInput {
20
+ value: number;
21
+ }
22
+
23
+ export async function simpleWorkflow(input: TaskInput): Promise<string> {
24
+ log.info(`Starting workflow with value: ${input.value}`);
25
+
26
+ let signalValue = 0;
27
+ setHandler(addSignalValue, (value: number) => {
28
+ log.info(`Received signal with value: ${value}`);
29
+ signalValue += value;
30
+ });
31
+
32
+ // Step 1: Add 10
33
+ const step1 = await addTen({ value: input.value });
34
+ log.info(`After step 1: ${step1}`);
35
+
36
+ // Step 2: Multiply by 2
37
+ const step2 = await multiplyByTwo({ value: step1 });
38
+ log.info(`After step 2: ${step2}`);
39
+
40
+ // Step 3: Subtract 5
41
+ const step3 = await subtractFive({ value: step2 });
42
+ log.info(`After step 3: ${step3}`);
43
+
44
+ const finalResult = `Complete: ${input.value} -> +10=${step1} -> *2=${step2} -> -5=${step3} + signal(${signalValue}) = ${step3 + signalValue}`;
45
+ log.info(finalResult);
46
+ return finalResult;
47
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2021",
4
+ "module": "commonjs",
5
+ "lib": ["ES2021"],
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "outDir": "./dist",
11
+ "rootDir": "./src",
12
+ "declaration": true
13
+ },
14
+ "include": ["src/**/*"]
15
+ }
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@braintrust/temporal",
3
+ "version": "0.1.0",
4
+ "description": "SDK for integrating Braintrust with Temporal",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ "./package.json": "./package.json",
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "module": "./dist/index.mjs",
14
+ "require": "./dist/index.js"
15
+ },
16
+ "./workflow-interceptors": {
17
+ "types": "./dist/workflow-interceptors.d.ts",
18
+ "import": "./dist/workflow-interceptors.mjs",
19
+ "module": "./dist/workflow-interceptors.mjs",
20
+ "require": "./dist/workflow-interceptors.js"
21
+ }
22
+ },
23
+ "scripts": {
24
+ "build": "tsup",
25
+ "watch": "tsup --watch",
26
+ "clean": "rm -r dist/*",
27
+ "test": "vitest run"
28
+ },
29
+ "author": "Braintrust Data Inc.",
30
+ "license": "MIT",
31
+ "devDependencies": {
32
+ "@temporalio/activity": "^1.14.1",
33
+ "@temporalio/client": "^1.14.1",
34
+ "@temporalio/common": "^1.14.1",
35
+ "@temporalio/worker": "^1.14.1",
36
+ "@temporalio/workflow": "^1.14.1",
37
+ "@types/node": "^22.15.21",
38
+ "braintrust": "workspace:*",
39
+ "tsup": "^8.5.0",
40
+ "typescript": "5.5.4",
41
+ "vitest": "^2.1.9"
42
+ },
43
+ "peerDependencies": {
44
+ "@temporalio/activity": ">=1.11.0",
45
+ "@temporalio/client": ">=1.11.0",
46
+ "@temporalio/common": ">=1.11.0",
47
+ "@temporalio/worker": ">=1.11.0",
48
+ "@temporalio/workflow": ">=1.11.0",
49
+ "braintrust": ">=2.0.0"
50
+ },
51
+ "peerDependenciesMeta": {
52
+ "braintrust": {
53
+ "optional": false
54
+ },
55
+ "@temporalio/activity": {
56
+ "optional": false
57
+ },
58
+ "@temporalio/client": {
59
+ "optional": false
60
+ },
61
+ "@temporalio/common": {
62
+ "optional": false
63
+ },
64
+ "@temporalio/worker": {
65
+ "optional": false
66
+ },
67
+ "@temporalio/workflow": {
68
+ "optional": false
69
+ }
70
+ }
71
+ }
package/src/index.ts ADDED
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Braintrust integration for Temporal workflows and activities.
3
+ *
4
+ * This module provides a plugin that automatically creates Braintrust spans
5
+ * for Temporal workflows and activities, with proper parent-child relationships
6
+ * across distributed workers.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { Client, Connection } from "@temporalio/client";
11
+ * import { Worker } from "@temporalio/worker";
12
+ * import * as braintrust from "braintrust";
13
+ * import { createBraintrustTemporalPlugin } from "@braintrust/temporal";
14
+ *
15
+ * // Initialize Braintrust logger
16
+ * braintrust.initLogger({ projectName: "my-project" });
17
+ *
18
+ * // Create the plugin
19
+ * const braintrustPlugin = createBraintrustTemporalPlugin();
20
+ *
21
+ * // Create client with the plugin
22
+ * const client = new Client({
23
+ * connection: await Connection.connect(),
24
+ * plugins: [braintrustPlugin],
25
+ * });
26
+ *
27
+ * // Create worker with the plugin
28
+ * const worker = await Worker.create({
29
+ * taskQueue: "my-queue",
30
+ * workflowsPath: require.resolve("./workflows"),
31
+ * activities,
32
+ * plugins: [braintrustPlugin],
33
+ * });
34
+ * ```
35
+ *
36
+ * The resulting trace will show:
37
+ * ```
38
+ * trigger-workflow (client span)
39
+ * └── temporal.workflow.myWorkflow
40
+ * ├── temporal.activity.activityOne
41
+ * └── temporal.activity.activityTwo
42
+ * ```
43
+ */
44
+
45
+ export {
46
+ BraintrustTemporalPlugin,
47
+ createBraintrustTemporalPlugin,
48
+ } from "./plugin";
49
+
50
+ export type { BraintrustSinks } from "./sinks";
@@ -0,0 +1,184 @@
1
+ import type { Context } from "@temporalio/activity";
2
+ import type {
3
+ ActivityInboundCallsInterceptor,
4
+ ActivityExecuteInput,
5
+ Next,
6
+ ActivityInterceptors,
7
+ } from "@temporalio/worker";
8
+ import type { WorkflowClientInterceptor } from "@temporalio/client";
9
+ import { defaultPayloadConverter } from "@temporalio/common";
10
+ import * as braintrust from "braintrust";
11
+ import { SpanComponentsV3 } from "braintrust/util";
12
+ import { getWorkflowSpanExport } from "./sinks";
13
+ import {
14
+ BRAINTRUST_SPAN_HEADER,
15
+ BRAINTRUST_WORKFLOW_SPAN_ID_HEADER,
16
+ deserializeHeaderValue,
17
+ } from "./utils";
18
+
19
+ /**
20
+ * Create a client interceptor that propagates Braintrust span context to workflows.
21
+ * Use this when creating a Temporal Client to enable trace context propagation.
22
+ */
23
+ export function createBraintrustClientInterceptor(): WorkflowClientInterceptor {
24
+ return {
25
+ async start(input, next) {
26
+ const span = braintrust.currentSpan();
27
+ if (span) {
28
+ const exported = await span.export();
29
+ if (exported) {
30
+ const payload = defaultPayloadConverter.toPayload(exported);
31
+ if (payload) {
32
+ return next({
33
+ ...input,
34
+ headers: {
35
+ ...input.headers,
36
+ [BRAINTRUST_SPAN_HEADER]: payload,
37
+ },
38
+ });
39
+ }
40
+ }
41
+ }
42
+ return next(input);
43
+ },
44
+ async signal(input, next) {
45
+ return next(input);
46
+ },
47
+ async signalWithStart(input, next) {
48
+ const span = braintrust.currentSpan();
49
+ if (span) {
50
+ const exported = await span.export();
51
+ if (exported) {
52
+ const payload = defaultPayloadConverter.toPayload(exported);
53
+ if (payload) {
54
+ return next({
55
+ ...input,
56
+ headers: {
57
+ ...input.headers,
58
+ [BRAINTRUST_SPAN_HEADER]: payload,
59
+ },
60
+ });
61
+ }
62
+ }
63
+ }
64
+ return next(input);
65
+ },
66
+ };
67
+ }
68
+
69
+ /**
70
+ * Activity interceptor that creates Braintrust spans for activity executions.
71
+ */
72
+ class BraintrustActivityInterceptor implements ActivityInboundCallsInterceptor {
73
+ constructor(private ctx: Context) {}
74
+
75
+ async execute(
76
+ input: ActivityExecuteInput,
77
+ next: Next<ActivityInboundCallsInterceptor, "execute">,
78
+ ): Promise<unknown> {
79
+ const info = this.ctx.info;
80
+ const runId = info.workflowExecution.runId;
81
+
82
+ // Try to get workflow span export - first check local Map, then headers
83
+ let parent: string | undefined;
84
+
85
+ // Check if we have the workflow span export locally (same worker as workflow)
86
+ const spanExportPromise = getWorkflowSpanExport(runId);
87
+ if (spanExportPromise) {
88
+ try {
89
+ parent = await spanExportPromise;
90
+ } catch {
91
+ // Ignore errors, fall through to header check
92
+ }
93
+ }
94
+
95
+ // For cross-worker activities: construct parent from workflow span ID + client context
96
+ if (!parent && input.headers) {
97
+ const workflowSpanId = deserializeHeaderValue(
98
+ input.headers[BRAINTRUST_WORKFLOW_SPAN_ID_HEADER],
99
+ );
100
+ const clientContext = deserializeHeaderValue(
101
+ input.headers[BRAINTRUST_SPAN_HEADER],
102
+ );
103
+
104
+ if (workflowSpanId && clientContext) {
105
+ try {
106
+ const clientComponents = SpanComponentsV3.fromStr(clientContext);
107
+ const clientData = clientComponents.data;
108
+
109
+ // We can only construct a workflow parent if we have:
110
+ // 1. Tracing context (root_span_id)
111
+ // 2. Object metadata (object_id or compute_object_metadata_args)
112
+ const hasTracingContext = !!clientData.root_span_id;
113
+ const hasObjectMetadata =
114
+ !!clientData.object_id || !!clientData.compute_object_metadata_args;
115
+
116
+ if (hasTracingContext && hasObjectMetadata) {
117
+ // Construct workflow parent with the workflow's span ID
118
+ // IMPORTANT: row_id must match span_id for the parent span
119
+ // Must provide EITHER object_id OR compute_object_metadata_args, not both
120
+ const workflowComponents = new SpanComponentsV3({
121
+ object_type: clientData.object_type,
122
+ object_id: clientData.object_id || undefined,
123
+ compute_object_metadata_args: clientData.object_id
124
+ ? undefined
125
+ : clientData.compute_object_metadata_args || undefined,
126
+ propagated_event: clientData.propagated_event,
127
+ row_id: workflowSpanId, // Use workflow's row_id, not client's
128
+ span_id: workflowSpanId, // Use workflow's span_id, not client's
129
+ root_span_id: clientData.root_span_id, // Keep same trace
130
+ });
131
+
132
+ parent = workflowComponents.toStr();
133
+ } else {
134
+ // Client context doesn't have root_span_id, use it directly
135
+ parent = clientContext;
136
+ }
137
+ } catch {
138
+ // Fall back to client context if parsing fails
139
+ parent = clientContext;
140
+ }
141
+ } else if (clientContext) {
142
+ // No workflow span ID, use client context directly
143
+ parent = clientContext;
144
+ }
145
+ }
146
+
147
+ const span = braintrust.startSpan({
148
+ name: `temporal.activity.${info.activityType}`,
149
+ spanAttributes: { type: "task" },
150
+ parent,
151
+ event: {
152
+ metadata: {
153
+ "temporal.activity_type": info.activityType,
154
+ "temporal.activity_id": info.activityId,
155
+ "temporal.workflow_id": info.workflowExecution.workflowId,
156
+ "temporal.workflow_run_id": runId,
157
+ },
158
+ },
159
+ });
160
+
161
+ try {
162
+ const result = await braintrust.withCurrent(span, () => next(input));
163
+ span.log({ output: result });
164
+ span.end();
165
+ return result;
166
+ } catch (e) {
167
+ span.log({ error: String(e) });
168
+ span.end();
169
+ throw e;
170
+ }
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Create an activity interceptor factory for use with Worker.create().
176
+ * This factory creates BraintrustActivityInterceptor instances for each activity.
177
+ */
178
+ export function createBraintrustActivityInterceptor(
179
+ ctx: Context,
180
+ ): ActivityInterceptors {
181
+ return {
182
+ inbound: new BraintrustActivityInterceptor(ctx),
183
+ };
184
+ }
package/src/plugin.ts ADDED
@@ -0,0 +1,128 @@
1
+ import type { ClientPlugin, ClientOptions } from "@temporalio/client";
2
+ import type { WorkerPlugin, WorkerOptions } from "@temporalio/worker";
3
+ import {
4
+ createBraintrustClientInterceptor,
5
+ createBraintrustActivityInterceptor,
6
+ } from "./interceptors";
7
+ import { createBraintrustSinks } from "./sinks";
8
+
9
+ /**
10
+ * A Braintrust plugin for Temporal that automatically instruments
11
+ * workflows and activities with tracing spans.
12
+ *
13
+ * This plugin implements both ClientPlugin and WorkerPlugin interfaces,
14
+ * so it can be used with both Temporal Client and Worker.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * import { Client, Connection } from "@temporalio/client";
19
+ * import { Worker } from "@temporalio/worker";
20
+ * import * as braintrust from "braintrust";
21
+ * import { BraintrustTemporalPlugin } from "@braintrust/temporal";
22
+ *
23
+ * // Initialize Braintrust logger
24
+ * braintrust.initLogger({ projectName: "my-project" });
25
+ *
26
+ * // Create client with the plugin
27
+ * const client = new Client({
28
+ * connection: await Connection.connect(),
29
+ * plugins: [new BraintrustTemporalPlugin()],
30
+ * });
31
+ *
32
+ * // Create worker with the plugin
33
+ * const worker = await Worker.create({
34
+ * taskQueue: "my-queue",
35
+ * workflowsPath: require.resolve("./workflows"),
36
+ * activities,
37
+ * plugins: [new BraintrustTemporalPlugin()],
38
+ * });
39
+ * ```
40
+ */
41
+ export class BraintrustTemporalPlugin implements ClientPlugin, WorkerPlugin {
42
+ get name(): string {
43
+ return "braintrust";
44
+ }
45
+
46
+ /**
47
+ * Configure the Temporal Client with Braintrust interceptors.
48
+ * Adds the client interceptor for propagating span context to workflows.
49
+ */
50
+ configureClient(
51
+ options: Omit<ClientOptions, "plugins">,
52
+ ): Omit<ClientOptions, "plugins"> {
53
+ const existing = options.interceptors?.workflow;
54
+ const braintrustInterceptor = createBraintrustClientInterceptor();
55
+
56
+ // workflow can be an array or an object with named interceptors
57
+ let workflow: typeof existing;
58
+ if (Array.isArray(existing)) {
59
+ workflow = [...existing, braintrustInterceptor];
60
+ } else if (existing) {
61
+ // It's a WorkflowClientInterceptors object, merge our interceptor
62
+ workflow = {
63
+ ...existing,
64
+ ...braintrustInterceptor,
65
+ };
66
+ } else {
67
+ workflow = [braintrustInterceptor];
68
+ }
69
+
70
+ return {
71
+ ...options,
72
+ interceptors: {
73
+ ...options.interceptors,
74
+ workflow,
75
+ },
76
+ };
77
+ }
78
+
79
+ /**
80
+ * Configure the Temporal Worker with Braintrust interceptors and sinks.
81
+ * Adds the activity interceptor for creating spans, the sinks for workflow spans,
82
+ * and the workflow interceptor modules for bundling.
83
+ */
84
+ configureWorker(options: WorkerOptions): WorkerOptions {
85
+ const existingActivityInterceptors = options.interceptors?.activity ?? [];
86
+ const existingWorkflowModules = options.interceptors?.workflowModules ?? [];
87
+ const existingSinks = options.sinks ?? {};
88
+
89
+ const braintrustSinks = createBraintrustSinks();
90
+
91
+ // Resolve the workflow interceptors module path
92
+ // This needs to be resolved at runtime to get the actual file path
93
+ const workflowInterceptorsPath = require.resolve(
94
+ "@braintrust/temporal/workflow-interceptors",
95
+ );
96
+
97
+ return {
98
+ ...options,
99
+ interceptors: {
100
+ ...options.interceptors,
101
+ activity: [
102
+ ...existingActivityInterceptors,
103
+ createBraintrustActivityInterceptor,
104
+ ],
105
+ workflowModules: [...existingWorkflowModules, workflowInterceptorsPath],
106
+ },
107
+ sinks: {
108
+ ...existingSinks,
109
+ ...braintrustSinks,
110
+ },
111
+ };
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Create a Braintrust plugin for Temporal.
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * const plugin = createBraintrustTemporalPlugin();
121
+ *
122
+ * const client = new Client({ plugins: [plugin] });
123
+ * const worker = await Worker.create({ plugins: [plugin], ... });
124
+ * ```
125
+ */
126
+ export function createBraintrustTemporalPlugin(): BraintrustTemporalPlugin {
127
+ return new BraintrustTemporalPlugin();
128
+ }