@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.
- package/README.md +54 -0
- package/dist/chunk-KT66NY2E.mjs +39 -0
- package/dist/index.d.mts +71 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.js +301 -0
- package/dist/index.mjs +257 -0
- package/dist/workflow-interceptors.d.mts +13 -0
- package/dist/workflow-interceptors.d.ts +13 -0
- package/dist/workflow-interceptors.js +134 -0
- package/dist/workflow-interceptors.mjs +95 -0
- package/examples/temporal/.env.example +2 -0
- package/examples/temporal/Procfile +4 -0
- package/examples/temporal/README.md +183 -0
- package/examples/temporal/mise.toml +40 -0
- package/examples/temporal/package.json +26 -0
- package/examples/temporal/src/activities.ts +42 -0
- package/examples/temporal/src/client.ts +53 -0
- package/examples/temporal/src/worker.ts +31 -0
- package/examples/temporal/src/workflows.ts +47 -0
- package/examples/temporal/tsconfig.json +15 -0
- package/package.json +71 -0
- package/src/index.ts +50 -0
- package/src/interceptors.ts +184 -0
- package/src/plugin.ts +128 -0
- package/src/sinks.ts +76 -0
- package/src/temporal.test.ts +243 -0
- package/src/utils.ts +29 -0
- package/src/workflow-interceptors.ts +159 -0
- package/tsconfig.json +14 -0
- package/tsup.config.ts +20 -0
- package/turbo.json +8 -0
- package/vitest.config.ts +5 -0
package/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# @braintrust/temporal
|
|
2
|
+
|
|
3
|
+
SDK for integrating [Braintrust](https://braintrust.dev) tracing with [Temporal](https://temporal.io/) workflows and activities.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
This package has peer dependencies that you must install alongside it:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @braintrust/temporal braintrust @temporalio/client @temporalio/worker @temporalio/workflow @temporalio/activity @temporalio/common
|
|
11
|
+
# or
|
|
12
|
+
yarn add @braintrust/temporal braintrust @temporalio/client @temporalio/worker @temporalio/workflow @temporalio/activity @temporalio/common
|
|
13
|
+
# or
|
|
14
|
+
pnpm add @braintrust/temporal braintrust @temporalio/client @temporalio/worker @temporalio/workflow @temporalio/activity @temporalio/common
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
Initialize Braintrust, then install the plugin on both the Temporal client and worker.
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { Client, Connection } from "@temporalio/client";
|
|
23
|
+
import { Worker } from "@temporalio/worker";
|
|
24
|
+
import * as braintrust from "braintrust";
|
|
25
|
+
import { BraintrustTemporalPlugin } from "@braintrust/temporal";
|
|
26
|
+
|
|
27
|
+
braintrust.initLogger({ projectName: "my-project" });
|
|
28
|
+
|
|
29
|
+
const plugin = new BraintrustTemporalPlugin();
|
|
30
|
+
|
|
31
|
+
const client = new Client({
|
|
32
|
+
connection: await Connection.connect(),
|
|
33
|
+
plugins: [plugin],
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const worker = await Worker.create({
|
|
37
|
+
taskQueue: "my-queue",
|
|
38
|
+
workflowsPath: require.resolve("./workflows"),
|
|
39
|
+
activities,
|
|
40
|
+
plugins: [plugin],
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Workflow interceptors
|
|
45
|
+
|
|
46
|
+
This package also exports workflow interceptors that are loaded into the Temporal workflow isolate:
|
|
47
|
+
|
|
48
|
+
- `@braintrust/temporal/workflow-interceptors`
|
|
49
|
+
|
|
50
|
+
The `BraintrustTemporalPlugin` automatically configures `workflowModules` to include these interceptors when used on a worker.
|
|
51
|
+
|
|
52
|
+
## Example
|
|
53
|
+
|
|
54
|
+
See the example app in `examples/temporal`.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// src/utils.ts
|
|
9
|
+
var BRAINTRUST_SPAN_HEADER = "_braintrust-span";
|
|
10
|
+
var BRAINTRUST_WORKFLOW_SPAN_HEADER = "_braintrust-workflow-span";
|
|
11
|
+
var BRAINTRUST_WORKFLOW_SPAN_ID_HEADER = "_braintrust-workflow-span-id";
|
|
12
|
+
function serializeHeaderValue(value) {
|
|
13
|
+
return {
|
|
14
|
+
metadata: {
|
|
15
|
+
encoding: new TextEncoder().encode("json/plain")
|
|
16
|
+
},
|
|
17
|
+
data: new TextEncoder().encode(JSON.stringify(value))
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function deserializeHeaderValue(payload) {
|
|
21
|
+
if (!payload?.data) {
|
|
22
|
+
return void 0;
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const decoded = new TextDecoder().decode(payload.data);
|
|
26
|
+
return JSON.parse(decoded);
|
|
27
|
+
} catch {
|
|
28
|
+
return void 0;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export {
|
|
33
|
+
__require,
|
|
34
|
+
BRAINTRUST_SPAN_HEADER,
|
|
35
|
+
BRAINTRUST_WORKFLOW_SPAN_HEADER,
|
|
36
|
+
BRAINTRUST_WORKFLOW_SPAN_ID_HEADER,
|
|
37
|
+
serializeHeaderValue,
|
|
38
|
+
deserializeHeaderValue
|
|
39
|
+
};
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { ClientPlugin, ClientOptions } from '@temporalio/client';
|
|
2
|
+
import { WorkerPlugin, WorkerOptions } from '@temporalio/worker';
|
|
3
|
+
import { Sinks } from '@temporalio/workflow';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A Braintrust plugin for Temporal that automatically instruments
|
|
7
|
+
* workflows and activities with tracing spans.
|
|
8
|
+
*
|
|
9
|
+
* This plugin implements both ClientPlugin and WorkerPlugin interfaces,
|
|
10
|
+
* so it can be used with both Temporal Client and Worker.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { Client, Connection } from "@temporalio/client";
|
|
15
|
+
* import { Worker } from "@temporalio/worker";
|
|
16
|
+
* import * as braintrust from "braintrust";
|
|
17
|
+
* import { BraintrustTemporalPlugin } from "@braintrust/temporal";
|
|
18
|
+
*
|
|
19
|
+
* // Initialize Braintrust logger
|
|
20
|
+
* braintrust.initLogger({ projectName: "my-project" });
|
|
21
|
+
*
|
|
22
|
+
* // Create client with the plugin
|
|
23
|
+
* const client = new Client({
|
|
24
|
+
* connection: await Connection.connect(),
|
|
25
|
+
* plugins: [new BraintrustTemporalPlugin()],
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* // Create worker with the plugin
|
|
29
|
+
* const worker = await Worker.create({
|
|
30
|
+
* taskQueue: "my-queue",
|
|
31
|
+
* workflowsPath: require.resolve("./workflows"),
|
|
32
|
+
* activities,
|
|
33
|
+
* plugins: [new BraintrustTemporalPlugin()],
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
declare class BraintrustTemporalPlugin implements ClientPlugin, WorkerPlugin {
|
|
38
|
+
get name(): string;
|
|
39
|
+
/**
|
|
40
|
+
* Configure the Temporal Client with Braintrust interceptors.
|
|
41
|
+
* Adds the client interceptor for propagating span context to workflows.
|
|
42
|
+
*/
|
|
43
|
+
configureClient(options: Omit<ClientOptions, "plugins">): Omit<ClientOptions, "plugins">;
|
|
44
|
+
/**
|
|
45
|
+
* Configure the Temporal Worker with Braintrust interceptors and sinks.
|
|
46
|
+
* Adds the activity interceptor for creating spans, the sinks for workflow spans,
|
|
47
|
+
* and the workflow interceptor modules for bundling.
|
|
48
|
+
*/
|
|
49
|
+
configureWorker(options: WorkerOptions): WorkerOptions;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Create a Braintrust plugin for Temporal.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* const plugin = createBraintrustTemporalPlugin();
|
|
57
|
+
*
|
|
58
|
+
* const client = new Client({ plugins: [plugin] });
|
|
59
|
+
* const worker = await Worker.create({ plugins: [plugin], ... });
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
declare function createBraintrustTemporalPlugin(): BraintrustTemporalPlugin;
|
|
63
|
+
|
|
64
|
+
interface BraintrustSinks extends Sinks {
|
|
65
|
+
braintrust: {
|
|
66
|
+
workflowStarted(parentContext?: string, workflowSpanId?: string): void;
|
|
67
|
+
workflowCompleted(error?: string): void;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export { type BraintrustSinks, BraintrustTemporalPlugin, createBraintrustTemporalPlugin };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { ClientPlugin, ClientOptions } from '@temporalio/client';
|
|
2
|
+
import { WorkerPlugin, WorkerOptions } from '@temporalio/worker';
|
|
3
|
+
import { Sinks } from '@temporalio/workflow';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A Braintrust plugin for Temporal that automatically instruments
|
|
7
|
+
* workflows and activities with tracing spans.
|
|
8
|
+
*
|
|
9
|
+
* This plugin implements both ClientPlugin and WorkerPlugin interfaces,
|
|
10
|
+
* so it can be used with both Temporal Client and Worker.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { Client, Connection } from "@temporalio/client";
|
|
15
|
+
* import { Worker } from "@temporalio/worker";
|
|
16
|
+
* import * as braintrust from "braintrust";
|
|
17
|
+
* import { BraintrustTemporalPlugin } from "@braintrust/temporal";
|
|
18
|
+
*
|
|
19
|
+
* // Initialize Braintrust logger
|
|
20
|
+
* braintrust.initLogger({ projectName: "my-project" });
|
|
21
|
+
*
|
|
22
|
+
* // Create client with the plugin
|
|
23
|
+
* const client = new Client({
|
|
24
|
+
* connection: await Connection.connect(),
|
|
25
|
+
* plugins: [new BraintrustTemporalPlugin()],
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* // Create worker with the plugin
|
|
29
|
+
* const worker = await Worker.create({
|
|
30
|
+
* taskQueue: "my-queue",
|
|
31
|
+
* workflowsPath: require.resolve("./workflows"),
|
|
32
|
+
* activities,
|
|
33
|
+
* plugins: [new BraintrustTemporalPlugin()],
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
declare class BraintrustTemporalPlugin implements ClientPlugin, WorkerPlugin {
|
|
38
|
+
get name(): string;
|
|
39
|
+
/**
|
|
40
|
+
* Configure the Temporal Client with Braintrust interceptors.
|
|
41
|
+
* Adds the client interceptor for propagating span context to workflows.
|
|
42
|
+
*/
|
|
43
|
+
configureClient(options: Omit<ClientOptions, "plugins">): Omit<ClientOptions, "plugins">;
|
|
44
|
+
/**
|
|
45
|
+
* Configure the Temporal Worker with Braintrust interceptors and sinks.
|
|
46
|
+
* Adds the activity interceptor for creating spans, the sinks for workflow spans,
|
|
47
|
+
* and the workflow interceptor modules for bundling.
|
|
48
|
+
*/
|
|
49
|
+
configureWorker(options: WorkerOptions): WorkerOptions;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Create a Braintrust plugin for Temporal.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* const plugin = createBraintrustTemporalPlugin();
|
|
57
|
+
*
|
|
58
|
+
* const client = new Client({ plugins: [plugin] });
|
|
59
|
+
* const worker = await Worker.create({ plugins: [plugin], ... });
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
declare function createBraintrustTemporalPlugin(): BraintrustTemporalPlugin;
|
|
63
|
+
|
|
64
|
+
interface BraintrustSinks extends Sinks {
|
|
65
|
+
braintrust: {
|
|
66
|
+
workflowStarted(parentContext?: string, workflowSpanId?: string): void;
|
|
67
|
+
workflowCompleted(error?: string): void;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export { type BraintrustSinks, BraintrustTemporalPlugin, createBraintrustTemporalPlugin };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
BraintrustTemporalPlugin: () => BraintrustTemporalPlugin,
|
|
34
|
+
createBraintrustTemporalPlugin: () => createBraintrustTemporalPlugin
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
37
|
+
|
|
38
|
+
// src/interceptors.ts
|
|
39
|
+
var import_common = require("@temporalio/common");
|
|
40
|
+
var braintrust2 = __toESM(require("braintrust"));
|
|
41
|
+
var import_util = require("braintrust/util");
|
|
42
|
+
|
|
43
|
+
// src/sinks.ts
|
|
44
|
+
var braintrust = __toESM(require("braintrust"));
|
|
45
|
+
var workflowSpans = /* @__PURE__ */ new Map();
|
|
46
|
+
var workflowSpanExports = /* @__PURE__ */ new Map();
|
|
47
|
+
function getWorkflowSpanExport(runId) {
|
|
48
|
+
return workflowSpanExports.get(runId);
|
|
49
|
+
}
|
|
50
|
+
function createBraintrustSinks() {
|
|
51
|
+
return {
|
|
52
|
+
braintrust: {
|
|
53
|
+
workflowStarted: {
|
|
54
|
+
fn: (info, parentContext, workflowSpanId) => {
|
|
55
|
+
const span = braintrust.startSpan({
|
|
56
|
+
name: `temporal.workflow.${info.workflowType}`,
|
|
57
|
+
spanAttributes: { type: "task" },
|
|
58
|
+
parent: parentContext,
|
|
59
|
+
spanId: workflowSpanId,
|
|
60
|
+
event: {
|
|
61
|
+
metadata: {
|
|
62
|
+
"temporal.workflow_type": info.workflowType,
|
|
63
|
+
"temporal.workflow_id": info.workflowId,
|
|
64
|
+
"temporal.run_id": info.runId
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
workflowSpans.set(info.runId, span);
|
|
69
|
+
workflowSpanExports.set(info.runId, span.export());
|
|
70
|
+
},
|
|
71
|
+
callDuringReplay: false
|
|
72
|
+
},
|
|
73
|
+
workflowCompleted: {
|
|
74
|
+
fn: (info, error) => {
|
|
75
|
+
const span = workflowSpans.get(info.runId);
|
|
76
|
+
if (span) {
|
|
77
|
+
if (error) {
|
|
78
|
+
span.log({ error });
|
|
79
|
+
}
|
|
80
|
+
span.end();
|
|
81
|
+
workflowSpans.delete(info.runId);
|
|
82
|
+
workflowSpanExports.delete(info.runId);
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
callDuringReplay: false
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/utils.ts
|
|
92
|
+
var BRAINTRUST_SPAN_HEADER = "_braintrust-span";
|
|
93
|
+
var BRAINTRUST_WORKFLOW_SPAN_ID_HEADER = "_braintrust-workflow-span-id";
|
|
94
|
+
function deserializeHeaderValue(payload) {
|
|
95
|
+
if (!payload?.data) {
|
|
96
|
+
return void 0;
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
const decoded = new TextDecoder().decode(payload.data);
|
|
100
|
+
return JSON.parse(decoded);
|
|
101
|
+
} catch {
|
|
102
|
+
return void 0;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/interceptors.ts
|
|
107
|
+
function createBraintrustClientInterceptor() {
|
|
108
|
+
return {
|
|
109
|
+
async start(input, next) {
|
|
110
|
+
const span = braintrust2.currentSpan();
|
|
111
|
+
if (span) {
|
|
112
|
+
const exported = await span.export();
|
|
113
|
+
if (exported) {
|
|
114
|
+
const payload = import_common.defaultPayloadConverter.toPayload(exported);
|
|
115
|
+
if (payload) {
|
|
116
|
+
return next({
|
|
117
|
+
...input,
|
|
118
|
+
headers: {
|
|
119
|
+
...input.headers,
|
|
120
|
+
[BRAINTRUST_SPAN_HEADER]: payload
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return next(input);
|
|
127
|
+
},
|
|
128
|
+
async signal(input, next) {
|
|
129
|
+
return next(input);
|
|
130
|
+
},
|
|
131
|
+
async signalWithStart(input, next) {
|
|
132
|
+
const span = braintrust2.currentSpan();
|
|
133
|
+
if (span) {
|
|
134
|
+
const exported = await span.export();
|
|
135
|
+
if (exported) {
|
|
136
|
+
const payload = import_common.defaultPayloadConverter.toPayload(exported);
|
|
137
|
+
if (payload) {
|
|
138
|
+
return next({
|
|
139
|
+
...input,
|
|
140
|
+
headers: {
|
|
141
|
+
...input.headers,
|
|
142
|
+
[BRAINTRUST_SPAN_HEADER]: payload
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return next(input);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
var BraintrustActivityInterceptor = class {
|
|
153
|
+
constructor(ctx) {
|
|
154
|
+
this.ctx = ctx;
|
|
155
|
+
}
|
|
156
|
+
async execute(input, next) {
|
|
157
|
+
const info = this.ctx.info;
|
|
158
|
+
const runId = info.workflowExecution.runId;
|
|
159
|
+
let parent;
|
|
160
|
+
const spanExportPromise = getWorkflowSpanExport(runId);
|
|
161
|
+
if (spanExportPromise) {
|
|
162
|
+
try {
|
|
163
|
+
parent = await spanExportPromise;
|
|
164
|
+
} catch {
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (!parent && input.headers) {
|
|
168
|
+
const workflowSpanId = deserializeHeaderValue(
|
|
169
|
+
input.headers[BRAINTRUST_WORKFLOW_SPAN_ID_HEADER]
|
|
170
|
+
);
|
|
171
|
+
const clientContext = deserializeHeaderValue(
|
|
172
|
+
input.headers[BRAINTRUST_SPAN_HEADER]
|
|
173
|
+
);
|
|
174
|
+
if (workflowSpanId && clientContext) {
|
|
175
|
+
try {
|
|
176
|
+
const clientComponents = import_util.SpanComponentsV3.fromStr(clientContext);
|
|
177
|
+
const clientData = clientComponents.data;
|
|
178
|
+
const hasTracingContext = !!clientData.root_span_id;
|
|
179
|
+
const hasObjectMetadata = !!clientData.object_id || !!clientData.compute_object_metadata_args;
|
|
180
|
+
if (hasTracingContext && hasObjectMetadata) {
|
|
181
|
+
const workflowComponents = new import_util.SpanComponentsV3({
|
|
182
|
+
object_type: clientData.object_type,
|
|
183
|
+
object_id: clientData.object_id || void 0,
|
|
184
|
+
compute_object_metadata_args: clientData.object_id ? void 0 : clientData.compute_object_metadata_args || void 0,
|
|
185
|
+
propagated_event: clientData.propagated_event,
|
|
186
|
+
row_id: workflowSpanId,
|
|
187
|
+
// Use workflow's row_id, not client's
|
|
188
|
+
span_id: workflowSpanId,
|
|
189
|
+
// Use workflow's span_id, not client's
|
|
190
|
+
root_span_id: clientData.root_span_id
|
|
191
|
+
// Keep same trace
|
|
192
|
+
});
|
|
193
|
+
parent = workflowComponents.toStr();
|
|
194
|
+
} else {
|
|
195
|
+
parent = clientContext;
|
|
196
|
+
}
|
|
197
|
+
} catch {
|
|
198
|
+
parent = clientContext;
|
|
199
|
+
}
|
|
200
|
+
} else if (clientContext) {
|
|
201
|
+
parent = clientContext;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const span = braintrust2.startSpan({
|
|
205
|
+
name: `temporal.activity.${info.activityType}`,
|
|
206
|
+
spanAttributes: { type: "task" },
|
|
207
|
+
parent,
|
|
208
|
+
event: {
|
|
209
|
+
metadata: {
|
|
210
|
+
"temporal.activity_type": info.activityType,
|
|
211
|
+
"temporal.activity_id": info.activityId,
|
|
212
|
+
"temporal.workflow_id": info.workflowExecution.workflowId,
|
|
213
|
+
"temporal.workflow_run_id": runId
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
try {
|
|
218
|
+
const result = await braintrust2.withCurrent(span, () => next(input));
|
|
219
|
+
span.log({ output: result });
|
|
220
|
+
span.end();
|
|
221
|
+
return result;
|
|
222
|
+
} catch (e) {
|
|
223
|
+
span.log({ error: String(e) });
|
|
224
|
+
span.end();
|
|
225
|
+
throw e;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
function createBraintrustActivityInterceptor(ctx) {
|
|
230
|
+
return {
|
|
231
|
+
inbound: new BraintrustActivityInterceptor(ctx)
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// src/plugin.ts
|
|
236
|
+
var BraintrustTemporalPlugin = class {
|
|
237
|
+
get name() {
|
|
238
|
+
return "braintrust";
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Configure the Temporal Client with Braintrust interceptors.
|
|
242
|
+
* Adds the client interceptor for propagating span context to workflows.
|
|
243
|
+
*/
|
|
244
|
+
configureClient(options) {
|
|
245
|
+
const existing = options.interceptors?.workflow;
|
|
246
|
+
const braintrustInterceptor = createBraintrustClientInterceptor();
|
|
247
|
+
let workflow;
|
|
248
|
+
if (Array.isArray(existing)) {
|
|
249
|
+
workflow = [...existing, braintrustInterceptor];
|
|
250
|
+
} else if (existing) {
|
|
251
|
+
workflow = {
|
|
252
|
+
...existing,
|
|
253
|
+
...braintrustInterceptor
|
|
254
|
+
};
|
|
255
|
+
} else {
|
|
256
|
+
workflow = [braintrustInterceptor];
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
...options,
|
|
260
|
+
interceptors: {
|
|
261
|
+
...options.interceptors,
|
|
262
|
+
workflow
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Configure the Temporal Worker with Braintrust interceptors and sinks.
|
|
268
|
+
* Adds the activity interceptor for creating spans, the sinks for workflow spans,
|
|
269
|
+
* and the workflow interceptor modules for bundling.
|
|
270
|
+
*/
|
|
271
|
+
configureWorker(options) {
|
|
272
|
+
const existingActivityInterceptors = options.interceptors?.activity ?? [];
|
|
273
|
+
const existingWorkflowModules = options.interceptors?.workflowModules ?? [];
|
|
274
|
+
const existingSinks = options.sinks ?? {};
|
|
275
|
+
const braintrustSinks = createBraintrustSinks();
|
|
276
|
+
const workflowInterceptorsPath = require.resolve("@braintrust/temporal/workflow-interceptors");
|
|
277
|
+
return {
|
|
278
|
+
...options,
|
|
279
|
+
interceptors: {
|
|
280
|
+
...options.interceptors,
|
|
281
|
+
activity: [
|
|
282
|
+
...existingActivityInterceptors,
|
|
283
|
+
createBraintrustActivityInterceptor
|
|
284
|
+
],
|
|
285
|
+
workflowModules: [...existingWorkflowModules, workflowInterceptorsPath]
|
|
286
|
+
},
|
|
287
|
+
sinks: {
|
|
288
|
+
...existingSinks,
|
|
289
|
+
...braintrustSinks
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
function createBraintrustTemporalPlugin() {
|
|
295
|
+
return new BraintrustTemporalPlugin();
|
|
296
|
+
}
|
|
297
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
298
|
+
0 && (module.exports = {
|
|
299
|
+
BraintrustTemporalPlugin,
|
|
300
|
+
createBraintrustTemporalPlugin
|
|
301
|
+
});
|