@temporalio/langsmith 1.20.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,350 @@
1
+ "use strict";
2
+ /**
3
+ * Workflow-isolate interceptors and the deterministic LangSmith context
4
+ * provider. Loaded into the workflow bundle (via the plugin's `workflowModules`)
5
+ * and runs entirely inside the V8 isolate, where `node:async_hooks` is
6
+ * unavailable so a synchronous stack-based context provider stands in for
7
+ * LangSmith's async-context store.
8
+ *
9
+ * Internal: the worker loads this module by specifier and the bundler aliases
10
+ * `node:async_hooks` to it — it is not a hand-import API.
11
+ *
12
+ * @module
13
+ * @internal
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.AsyncLocalStorage = void 0;
17
+ exports.interceptors = interceptors;
18
+ const traceable_1 = require("langsmith/singletons/traceable");
19
+ const workflow_1 = require("@temporalio/workflow");
20
+ const propagation_1 = require("./propagation");
21
+ const run_tree_1 = require("./run-tree");
22
+ function readConfig() {
23
+ try {
24
+ if (typeof __TEMPORAL_LANGSMITH_CONFIG__ === 'string') {
25
+ return JSON.parse(__TEMPORAL_LANGSMITH_CONFIG__);
26
+ }
27
+ }
28
+ catch {
29
+ /* fall through to default */
30
+ }
31
+ return { addTemporalRuns: false };
32
+ }
33
+ /**
34
+ * Synchronous, isolate-safe replacement for LangSmith's async-context store,
35
+ * shared by the installed provider, the {@link AsyncLocalStorage} shim, and
36
+ * every interceptor so they all read and write one store. `workflowAmbient`
37
+ * persists across `await` boundaries; `stack` tracks synchronous `traceable`
38
+ * nesting. Under `Promise.all` fan-out parenting falls back to the ambient —
39
+ * trace-shape-only non-determinism, never workflow history.
40
+ */
41
+ class WorkflowContextManager {
42
+ workflowAmbient;
43
+ stack = [];
44
+ getStore() {
45
+ return this.stack.length > 0 ? this.stack[this.stack.length - 1] : this.workflowAmbient;
46
+ }
47
+ /**
48
+ * Push `context`, run `fn`, pop in `finally`, and return `fn`'s result — the
49
+ * `AsyncLocalStorage.run` contract langsmith relies on.
50
+ */
51
+ run(context, fn) {
52
+ this.stack.push(context);
53
+ try {
54
+ return fn();
55
+ }
56
+ finally {
57
+ this.stack.pop();
58
+ }
59
+ }
60
+ /** The ambient run an outbound interceptor should propagate / parent under. */
61
+ ambient() {
62
+ return this.getStore();
63
+ }
64
+ /** Run an async scope with a dedicated ambient (workflow execute / handler). */
65
+ async withAmbient(run, fn) {
66
+ const prev = this.workflowAmbient;
67
+ this.workflowAmbient = run;
68
+ try {
69
+ return await fn();
70
+ }
71
+ finally {
72
+ this.workflowAmbient = prev;
73
+ }
74
+ }
75
+ }
76
+ let providerInstalled = false;
77
+ function ensureProviderInstalled(manager) {
78
+ if (providerInstalled) {
79
+ return;
80
+ }
81
+ providerInstalled = true;
82
+ // LangSmith's `AsyncLocalStorageInterface` requires exactly two members,
83
+ // `getStore` and `run` (it never calls `enterWith`), so we provide only those.
84
+ // The cast bridges the generic `run<T>` signature to the interface's
85
+ // `run: (ctx, () => void) => void`.
86
+ traceable_1.AsyncLocalStorageProviderSingleton.initializeGlobalInstance({
87
+ getStore: () => manager.getStore(),
88
+ run: (context, fn) => manager.run(context, fn),
89
+ });
90
+ }
91
+ const sharedManager = new WorkflowContextManager();
92
+ ensureProviderInstalled(sharedManager);
93
+ /**
94
+ * Isolate-safe stand-in for Node's `AsyncLocalStorage`. The plugin's bundler
95
+ * aliases `node:async_hooks` (absent in the isolate) to this module so
96
+ * LangSmith's `import { AsyncLocalStorage } from "node:async_hooks"` resolves
97
+ * here. Every instance delegates to {@link sharedManager}.
98
+ *
99
+ * @internal
100
+ */
101
+ class AsyncLocalStorage {
102
+ getStore() {
103
+ return sharedManager.getStore();
104
+ }
105
+ run(store, fn) {
106
+ return sharedManager.run(store, fn);
107
+ }
108
+ enterWith(_store) {
109
+ /* no-op: parenting inside the isolate is stack-scoped via run() */
110
+ }
111
+ /**
112
+ * Best-effort equivalent of `AsyncLocalStorage.snapshot()` (used only by
113
+ * LangSmith's async-generator streaming paths). Runs the callback in the
114
+ * current synchronous scope; the isolate has no async context to capture.
115
+ */
116
+ static snapshot() {
117
+ return (fn, ...args) => fn(...args);
118
+ }
119
+ }
120
+ exports.AsyncLocalStorage = AsyncLocalStorage;
121
+ /** Reconstruct the propagated parent run from a Payload-keyed header map. */
122
+ function reconstructParent(headers) {
123
+ return (0, run_tree_1.runTreeFromContext)((0, propagation_1.readContextHeader)(headers));
124
+ }
125
+ /**
126
+ * Re-wrap a reconstructed (plain) parent so its `createChild` produces
127
+ * replay-safe children. Carries the same id/trace/dotted so descendants parent
128
+ * under the real propagated run; never emitted itself.
129
+ */
130
+ function asReplaySafeParent(parent, generateNewUuid4) {
131
+ if (!parent) {
132
+ return undefined;
133
+ }
134
+ return new run_tree_1.ReplaySafeRunTree({
135
+ name: parent.name,
136
+ run_type: parent.run_type,
137
+ id: parent.id,
138
+ trace_id: parent.trace_id,
139
+ dotted_order: parent.dotted_order,
140
+ parent_run_id: parent.parent_run_id,
141
+ project_name: parent.project_name,
142
+ }, generateNewUuid4);
143
+ }
144
+ /**
145
+ * A placeholder parent for the no-propagated-parent case. Installed as the ambient
146
+ * so a workflow-body `traceable` always nests via `createChild`; never emitted,
147
+ * and its children are independent roots (see {@link _RootReplaySafeRunTreeFactory}).
148
+ */
149
+ function placeholderRoot(generateNewUuid4) {
150
+ return new run_tree_1._RootReplaySafeRunTreeFactory({ name: (0, workflow_1.workflowInfo)().workflowType, run_type: run_tree_1.RUN_TYPE.CHAIN }, generateNewUuid4);
151
+ }
152
+ /** Emit a short-lived marker run as a child of `parent`. */
153
+ async function emitMarker(parent, name, inputs) {
154
+ const marker = parent.createChild({ name, run_type: run_tree_1.RUN_TYPE.CHAIN, inputs });
155
+ await (0, run_tree_1.emitMarkerRun)(marker);
156
+ return marker;
157
+ }
158
+ function buildReplaySafeRunTree(config, params) {
159
+ return new run_tree_1.ReplaySafeRunTree({
160
+ name: params.name,
161
+ run_type: params.runType,
162
+ parent_run: params.parent,
163
+ project_name: config.projectName,
164
+ tags: config.tags,
165
+ extra: { metadata: (0, propagation_1.scrubSensitive)(config.metadata) ?? {} },
166
+ inputs: params.inputs,
167
+ }, params.generateNewUuid4);
168
+ }
169
+ class LangSmithWorkflowInbound {
170
+ ctx;
171
+ config;
172
+ constructor(ctx, config) {
173
+ this.ctx = ctx;
174
+ this.config = config;
175
+ }
176
+ async execute(input, next) {
177
+ const parent = reconstructParent(input.headers);
178
+ return this.runInbound((0, run_tree_1.runWorkflowRunName)((0, workflow_1.workflowInfo)().workflowType), run_tree_1.RUN_TYPE.CHAIN, parent, { args: input.args }, () => next(input));
179
+ }
180
+ async handleSignal(input, next) {
181
+ const parent = reconstructParent(input.headers);
182
+ await this.runInbound((0, run_tree_1.handleSignalRunName)(input.signalName), run_tree_1.RUN_TYPE.CHAIN, parent, { args: input.args }, () => next(input), true);
183
+ }
184
+ async handleQuery(input, next) {
185
+ if ((0, propagation_1.isInternalQuery)(input.queryName)) {
186
+ return next(input);
187
+ }
188
+ const parent = reconstructParent(input.headers);
189
+ return this.runInbound((0, run_tree_1.handleQueryRunName)(input.queryName), run_tree_1.RUN_TYPE.CHAIN, parent, { args: input.args }, () => next(input), true,
190
+ // Read-only: mint run ids from the nondeterministic unsafe random source so the
191
+ // handler never draws from the Workflow main PRNG (which a clean replay would not advance).
192
+ (0, workflow_1.workflowInfo)().unsafe.random.uuid4);
193
+ }
194
+ async handleUpdate(input, next) {
195
+ const parent = reconstructParent(input.headers);
196
+ return this.runInbound((0, run_tree_1.handleUpdateRunName)(input.name), run_tree_1.RUN_TYPE.CHAIN, parent, { args: input.args }, () => next(input), true);
197
+ }
198
+ validateUpdate(input, next) {
199
+ // Read-only: mint run ids from the nondeterministic unsafe random source so the validator
200
+ // never draws from the Workflow main PRNG (which a clean replay would not advance).
201
+ const generateNewUuid4 = (0, workflow_1.workflowInfo)().unsafe.random.uuid4;
202
+ if (!this.config.addTemporalRuns) {
203
+ // Propagation only: install the reconstructed parent (or a placeholder parent
204
+ // when none was propagated, to keep a validator-body `traceable` off the
205
+ // no-parent `crypto` path) so the validator body nests under the update's
206
+ // trace. Validators are synchronous, so install via the stack-based `run`,
207
+ // not the async `withAmbient`.
208
+ const ambient = asReplaySafeParent(reconstructParent(input.headers), generateNewUuid4) ?? placeholderRoot(generateNewUuid4);
209
+ this.ctx.run(ambient, () => next(input));
210
+ return;
211
+ }
212
+ const run = buildReplaySafeRunTree(this.config, {
213
+ name: (0, run_tree_1.validateUpdateRunName)(input.name),
214
+ runType: run_tree_1.RUN_TYPE.CHAIN,
215
+ parent: asReplaySafeParent(reconstructParent(input.headers), generateNewUuid4),
216
+ inputs: { args: input.args },
217
+ generateNewUuid4,
218
+ });
219
+ // Validators are synchronous and must not be made async; emit start/end
220
+ // around the synchronous call, with the run installed on the stack so a
221
+ // `traceable` in the validator body nests under it.
222
+ void run.postRun();
223
+ try {
224
+ this.ctx.run(run, () => next(input));
225
+ void run.end({});
226
+ }
227
+ catch (err) {
228
+ void run.end(undefined, (0, run_tree_1.describeError)(err));
229
+ void run.patchRun();
230
+ throw err;
231
+ }
232
+ void run.patchRun();
233
+ }
234
+ /**
235
+ * Open the inbound run, install it as the active run for `next`, and close it.
236
+ *
237
+ * `scoped` handlers install the run on the synchronous stack so a handler running
238
+ * concurrently with the workflow body cannot leak its run into the body's ambient;
239
+ * `execute` (scoped: false) installs a persistent ambient that survives the body's
240
+ * awaits.
241
+ */
242
+ async runInbound(name, runType, parent, inputs, next, scoped = false, generateNewUuid4) {
243
+ if (!this.config.addTemporalRuns) {
244
+ // Propagation only: install the reconstructed parent as the active run so
245
+ // user `traceable` runs nest under it; never emit a Temporal-operation run.
246
+ // With no propagated parent, install a placeholder parent instead of
247
+ // `undefined` so a workflow-body `traceable` takes LangSmith's
248
+ // `createChild` branch (deterministic id) rather than the no-parent branch
249
+ // that mints a uuid via `crypto`, which the isolate lacks.
250
+ const ambient = asReplaySafeParent(parent, generateNewUuid4) ?? placeholderRoot(generateNewUuid4);
251
+ return scoped ? this.ctx.run(ambient, next) : this.ctx.withAmbient(ambient, next);
252
+ }
253
+ const run = buildReplaySafeRunTree(this.config, {
254
+ name,
255
+ runType,
256
+ parent: asReplaySafeParent(parent, generateNewUuid4),
257
+ inputs,
258
+ generateNewUuid4,
259
+ });
260
+ await run.postRun();
261
+ const body = async () => {
262
+ try {
263
+ const result = await next();
264
+ await run.end((0, run_tree_1.asOutputs)(result));
265
+ await run.patchRun();
266
+ return result;
267
+ }
268
+ catch (err) {
269
+ await run.end(undefined, (0, run_tree_1.describeError)(err));
270
+ await run.patchRun();
271
+ throw err;
272
+ }
273
+ };
274
+ return scoped ? this.ctx.run(run, body) : this.ctx.withAmbient(run, body);
275
+ }
276
+ }
277
+ class LangSmithWorkflowOutbound {
278
+ ctx;
279
+ config;
280
+ constructor(ctx, config) {
281
+ this.ctx = ctx;
282
+ this.config = config;
283
+ }
284
+ async scheduleActivity(input, next) {
285
+ return this.peerMarker((0, run_tree_1.startActivityRunName)(input.activityType), input, (headers) => next({ ...input, headers }));
286
+ }
287
+ async scheduleLocalActivity(input, next) {
288
+ return this.peerMarker((0, run_tree_1.startActivityRunName)(input.activityType), input, (headers) => next({ ...input, headers }));
289
+ }
290
+ async startChildWorkflowExecution(input, next) {
291
+ return this.peerMarker((0, run_tree_1.startChildWorkflowRunName)(input.workflowType), input, (headers) => next({ ...input, headers }));
292
+ }
293
+ continueAsNew(input, next) {
294
+ // Don't propagate a placeholder root: it is never emitted, so the successor's
295
+ // runs would dangle under a nonexistent parent. Let it install its own.
296
+ const ambient = this.ctx.ambient();
297
+ const context = ambient instanceof run_tree_1._RootReplaySafeRunTreeFactory ? undefined : (0, run_tree_1.runHeaders)(ambient);
298
+ const headers = (0, propagation_1.withContextHeader)(input.headers, context);
299
+ return next({ ...input, headers });
300
+ }
301
+ async signalWorkflow(input, next) {
302
+ const isChild = input.target.type === 'child';
303
+ const name = isChild
304
+ ? (0, run_tree_1.signalChildWorkflowRunName)(input.signalName)
305
+ : (0, run_tree_1.signalExternalWorkflowRunName)(input.signalName);
306
+ return this.parentMarker(name, input, (headers) => next({ ...input, headers }));
307
+ }
308
+ async startNexusOperation(input, next) {
309
+ const ambient = this.ctx.ambient();
310
+ if (this.config.addTemporalRuns && ambient instanceof run_tree_1.ReplaySafeRunTree) {
311
+ await emitMarker(ambient, (0, run_tree_1.startNexusOperationRunName)(input.service, input.operation), {});
312
+ }
313
+ const ctx = (0, run_tree_1.runHeaders)(ambient);
314
+ const headers = ctx ? { ...input.headers, [propagation_1.HEADER_KEY]: (0, propagation_1.encodeContextString)(ctx) } : input.headers;
315
+ return next({ ...input, headers });
316
+ }
317
+ /** Sibling-marker operations: marker is a peer of the remote run; propagate ambient. */
318
+ async peerMarker(name, input, next) {
319
+ const ambient = this.ctx.ambient();
320
+ if (this.config.addTemporalRuns && ambient instanceof run_tree_1.ReplaySafeRunTree) {
321
+ await emitMarker(ambient, name, { args: input.args ?? [] });
322
+ }
323
+ const headers = (0, propagation_1.withContextHeader)(input.headers, (0, run_tree_1.runHeaders)(ambient));
324
+ return next(headers);
325
+ }
326
+ /** Parent-marker operations: remote handler nests under the marker; propagate marker. */
327
+ async parentMarker(name, input, next) {
328
+ const ambient = this.ctx.ambient();
329
+ let propagate = ambient;
330
+ if (this.config.addTemporalRuns && ambient instanceof run_tree_1.ReplaySafeRunTree) {
331
+ propagate = await emitMarker(ambient, name, { args: input.args ?? [] });
332
+ }
333
+ const headers = (0, propagation_1.withContextHeader)(input.headers, (0, run_tree_1.runHeaders)(propagate));
334
+ return next(headers);
335
+ }
336
+ }
337
+ /**
338
+ * Workflow interceptors factory. Loaded by the Temporal worker for every
339
+ * workflow in the bundle (registered via the plugin's `workflowModules`).
340
+ *
341
+ * @internal
342
+ */
343
+ function interceptors() {
344
+ const config = readConfig();
345
+ return {
346
+ inbound: [new LangSmithWorkflowInbound(sharedManager, config)],
347
+ outbound: [new LangSmithWorkflowOutbound(sharedManager, config)],
348
+ };
349
+ }
350
+ //# sourceMappingURL=workflow-interceptors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-interceptors.js","sourceRoot":"","sources":["../src/workflow-interceptors.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,88 @@
1
+ {
2
+ "name": "@temporalio/langsmith",
3
+ "version": "1.20.0",
4
+ "description": "Temporal LangSmith observability integration package",
5
+ "main": "lib/index.js",
6
+ "types": "./lib/index.d.ts",
7
+ "typesVersions": {
8
+ "*": {
9
+ "workflow-interceptors": [
10
+ "./lib/workflow-interceptors.d.ts"
11
+ ]
12
+ }
13
+ },
14
+ "ava": {
15
+ "files": [
16
+ "./lib/__tests__/test-*.js"
17
+ ],
18
+ "timeout": "120s",
19
+ "concurrency": 1,
20
+ "workerThreads": false
21
+ },
22
+ "exports": {
23
+ ".": {
24
+ "types": "./lib/index.d.ts",
25
+ "import": "./lib/index.js",
26
+ "require": "./lib/index.js",
27
+ "default": "./lib/index.js"
28
+ },
29
+ "./workflow-interceptors": {
30
+ "types": "./lib/workflow-interceptors.d.ts",
31
+ "import": "./lib/workflow-interceptors.js",
32
+ "require": "./lib/workflow-interceptors.js",
33
+ "default": "./lib/workflow-interceptors.js"
34
+ }
35
+ },
36
+ "keywords": [
37
+ "temporal",
38
+ "workflow",
39
+ "ai",
40
+ "langsmith",
41
+ "observability",
42
+ "tracing"
43
+ ],
44
+ "author": "Temporal Technologies Inc. <sdk@temporal.io>",
45
+ "license": "MIT",
46
+ "dependencies": {
47
+ "@temporalio/activity": "1.20.0",
48
+ "@temporalio/client": "1.20.0",
49
+ "@temporalio/plugin": "1.20.0",
50
+ "@temporalio/workflow": "1.20.0",
51
+ "@temporalio/common": "1.20.0",
52
+ "@temporalio/worker": "1.20.0"
53
+ },
54
+ "peerDependencies": {
55
+ "langsmith": "^0.7.9"
56
+ },
57
+ "devDependencies": {
58
+ "ava": "^5.3.1",
59
+ "langsmith": "^0.7.9",
60
+ "nexus-rpc": "^0.0.2",
61
+ "@temporalio/testing": "1.20.0"
62
+ },
63
+ "engines": {
64
+ "node": ">= 20.0.0"
65
+ },
66
+ "bugs": {
67
+ "url": "https://github.com/temporalio/sdk-typescript/issues"
68
+ },
69
+ "repository": {
70
+ "type": "git",
71
+ "url": "git+https://github.com/temporalio/sdk-typescript.git",
72
+ "directory": "contrib/langsmith"
73
+ },
74
+ "homepage": "https://github.com/temporalio/sdk-typescript/tree/main/contrib/langsmith",
75
+ "publishConfig": {
76
+ "access": "public"
77
+ },
78
+ "files": [
79
+ "src",
80
+ "lib",
81
+ "!src/__tests__",
82
+ "!lib/__tests__"
83
+ ],
84
+ "scripts": {
85
+ "build": "tsc --build",
86
+ "test": "ava ./lib/__tests__/test-*.js"
87
+ }
88
+ }
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Activity-side and Nexus-handler-side LangSmith interceptors. Both run in the
3
+ * real Node worker process and install the reconstructed run via `withRunTree`
4
+ * so a user's unchanged body `traceable` calls nest under it.
5
+ *
6
+ * @module
7
+ */
8
+
9
+ import { isTracingEnabled } from 'langsmith';
10
+ import { RunTree } from 'langsmith/run_trees';
11
+ import { withRunTree } from 'langsmith/traceable';
12
+ import type { Context as ActivityContext } from '@temporalio/activity';
13
+ import type { ActivityInboundCallsInterceptorFactory, NexusInboundCallsInterceptor } from '@temporalio/worker';
14
+
15
+ import { HEADER_KEY, decodeContextString, readContextHeader, type LangSmithTraceContext } from './propagation';
16
+ import {
17
+ RUN_TYPE,
18
+ asOutputs,
19
+ describeError,
20
+ buildRunTree,
21
+ runActivityRunName,
22
+ runCancelNexusHandlerRunName,
23
+ runStartNexusHandlerRunName,
24
+ runTreeFromContext,
25
+ } from './run-tree';
26
+ import type { EmitterConfig } from './sinks';
27
+
28
+ /**
29
+ * Reconstruct the propagated parent run from a LangSmith trace context, wired to
30
+ * the plugin-configured client so descendant runs emit to the right place. The
31
+ * returned run is the *parent* — never posted itself — used only to parent
32
+ * the operation run (or directly as ambient when `addTemporalRuns` is off).
33
+ */
34
+ function reconstructParentRun(config: EmitterConfig, ctx: LangSmithTraceContext | undefined): RunTree | undefined {
35
+ const parsed = runTreeFromContext(ctx);
36
+ if (!parsed) {
37
+ return undefined;
38
+ }
39
+ return new RunTree({
40
+ name: parsed.name || 'parent',
41
+ run_type: parsed.run_type || RUN_TYPE.CHAIN,
42
+ id: parsed.id,
43
+ trace_id: parsed.trace_id,
44
+ dotted_order: parsed.dotted_order,
45
+ parent_run_id: parsed.parent_run_id,
46
+ project_name: config.projectName ?? parsed.project_name,
47
+ client: config.client,
48
+ // Force-enable so nested body `traceable` runs emit; tracing gate is `isTracingEnabled()`.
49
+ tracingEnabled: true,
50
+ });
51
+ }
52
+
53
+ /**
54
+ * Run `fn` with `op` installed as the active LangSmith run, emitting the run
55
+ * around the call. Shared by activity and Nexus handlers.
56
+ */
57
+ async function traceOperation<T>(op: RunTree, fn: () => Promise<T>): Promise<T> {
58
+ await op.postRun();
59
+ try {
60
+ const result = await withRunTree(op, fn);
61
+ await op.end(asOutputs(result));
62
+ await op.patchRun();
63
+ return result;
64
+ } catch (err) {
65
+ await op.end(undefined, describeError(err));
66
+ await op.patchRun();
67
+ throw err;
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Build the activity-inbound interceptor factory. The worker calls the factory
73
+ * once per activity with that activity's {@link ActivityContext}, from which we
74
+ * read the activity type for the run name.
75
+ */
76
+ export function createActivityInboundInterceptor(config: EmitterConfig): ActivityInboundCallsInterceptorFactory {
77
+ return (ctx: ActivityContext) => ({
78
+ async execute(input, next) {
79
+ if (!isTracingEnabled()) {
80
+ return next(input);
81
+ }
82
+ const parent = reconstructParentRun(config, readContextHeader(input.headers));
83
+ if (!config.addTemporalRuns) {
84
+ // Propagation only: nest the user's body `traceable` runs under the
85
+ // reconstructed parent without emitting a Temporal-operation run.
86
+ // No propagated parent: run the body with no ambient run by design (nothing to attach to).
87
+ return parent ? withRunTree(parent, () => next(input)) : next(input);
88
+ }
89
+ const activityType = ctx.info.activityType;
90
+ const run = buildRunTree(config, {
91
+ name: runActivityRunName(activityType),
92
+ runType: RUN_TYPE.TOOL,
93
+ parent,
94
+ inputs: { args: input.args },
95
+ });
96
+ return traceOperation(run, () => next(input));
97
+ },
98
+ });
99
+ }
100
+
101
+ function nexusContext(headers: Record<string, string>): LangSmithTraceContext | undefined {
102
+ return decodeContextString(headers[HEADER_KEY]);
103
+ }
104
+
105
+ /**
106
+ * Build the Nexus-handler interceptor. `startOperation` opens a
107
+ * `RunStartNexusOperationHandler:` run (so workflows the handler starts nest
108
+ * under it); `cancelOperation` opens `RunCancelNexusOperationHandler:`.
109
+ */
110
+ export function createNexusInboundInterceptor(config: EmitterConfig): NexusInboundCallsInterceptor {
111
+ const handle = async <I extends { ctx: { service: string; operation: string; headers: Record<string, string> } }, O>(
112
+ input: I,
113
+ next: (input: I) => Promise<O>,
114
+ nameOf: (s: string, o: string) => string
115
+ ): Promise<O> => {
116
+ if (!isTracingEnabled()) {
117
+ return next(input);
118
+ }
119
+ const parent = reconstructParentRun(config, nexusContext(input.ctx.headers));
120
+ if (!config.addTemporalRuns) {
121
+ return parent ? withRunTree(parent, () => next(input)) : next(input);
122
+ }
123
+ const run = buildRunTree(config, {
124
+ name: nameOf(input.ctx.service, input.ctx.operation),
125
+ runType: RUN_TYPE.CHAIN,
126
+ parent,
127
+ });
128
+ return traceOperation(run, () => next(input));
129
+ };
130
+
131
+ return {
132
+ startOperation(input, next) {
133
+ return handle(input, next, runStartNexusHandlerRunName);
134
+ },
135
+ cancelOperation(input, next) {
136
+ return handle(input, next, runCancelNexusHandlerRunName);
137
+ },
138
+ };
139
+ }