Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
{
"permissions": {
"allow": [
"Bash(sleep:*)",
"Bash(make:*)",
"Bash(make)",
"Bash(mise:*)",
"Bash(mise)",
"Bash(nox:*)",
"Bash(nox)",
"Bash(pytest:*)",
Expand Down
46 changes: 46 additions & 0 deletions .github/workflows/temporal-js-build-test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: temporal-js

on:
pull_request:
paths:
- "integrations/temporal-js/**"
- "js/**"
- ".github/workflows/temporal-js-test.yaml"
push:
branches: [main]
paths:
- "integrations/temporal-js/**"
- "js/**"

jobs:
build-and-test:
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
node-version: [20, 22]

steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}

- uses: pnpm/action-setup@v4

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build braintrust
working-directory: js
run: pnpm run build

- name: Build @braintrust/temporal
working-directory: integrations/temporal-js
run: pnpm run build

- name: Run @braintrust/temporal tests
working-directory: integrations/temporal-js
run: pnpm run test
54 changes: 54 additions & 0 deletions integrations/temporal-js/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# @braintrust/temporal

SDK for integrating [Braintrust](https://braintrust.dev) tracing with [Temporal](https://temporal.io/) workflows and activities.

## Installation

This package has peer dependencies that you must install alongside it:

```bash
npm install @braintrust/temporal braintrust @temporalio/client @temporalio/worker @temporalio/workflow @temporalio/activity @temporalio/common
# or
yarn add @braintrust/temporal braintrust @temporalio/client @temporalio/worker @temporalio/workflow @temporalio/activity @temporalio/common
# or
pnpm add @braintrust/temporal braintrust @temporalio/client @temporalio/worker @temporalio/workflow @temporalio/activity @temporalio/common
```

## Usage

Initialize Braintrust, then install the plugin on both the Temporal client and worker.

```typescript
import { Client, Connection } from "@temporalio/client";
import { Worker } from "@temporalio/worker";
import * as braintrust from "braintrust";
import { BraintrustTemporalPlugin } from "@braintrust/temporal";

braintrust.initLogger({ projectName: "my-project" });

const plugin = new BraintrustTemporalPlugin();

const client = new Client({
connection: await Connection.connect(),
plugins: [plugin],
});

const worker = await Worker.create({
taskQueue: "my-queue",
workflowsPath: require.resolve("./workflows"),
activities,
plugins: [plugin],
});
```

## Workflow interceptors

This package also exports workflow interceptors that are loaded into the Temporal workflow isolate:

- `@braintrust/temporal/workflow-interceptors`

The `BraintrustTemporalPlugin` automatically configures `workflowModules` to include these interceptors when used on a worker.

## Example

See the example app in `examples/temporal`.
2 changes: 2 additions & 0 deletions integrations/temporal-js/examples/temporal/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Braintrust API key for tracing
BRAINTRUST_API_KEY=your-api-key-here
3 changes: 3 additions & 0 deletions integrations/temporal-js/examples/temporal/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
dist
.env
4 changes: 4 additions & 0 deletions integrations/temporal-js/examples/temporal/Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
server: temporal server start-dev
worker1: pnpm run worker
worker2: pnpm run worker
worker3: pnpm run worker
183 changes: 183 additions & 0 deletions integrations/temporal-js/examples/temporal/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
# Temporal + Braintrust Tracing Example

This example demonstrates how to integrate Braintrust tracing with Temporal workflows and activities.

## Prerequisites

- [mise](https://mise.jdx.dev/) (recommended) - automatically installs all dependencies
- OR manually install:
- Node.js 20+
- `pnpm`
- Temporal CLI (`temporal`)
- Optional: [`overmind`](https://github.com/DarthSim/overmind) (only if you want to use the included `Procfile`)

### Option 1: Using mise (recommended)

[mise](https://mise.jdx.dev/) will automatically install and manage all required tools (Node.js, Temporal CLI, overmind, and dependencies):

**Install mise:**

```bash
# macOS/Linux
curl https://mise.run | sh

# Or using Homebrew
brew install mise
```

**Setup and run:**

```bash
# Copy and configure environment
cp .env.example .env
# Edit .env with your BRAINTRUST_API_KEY

# mise will automatically install tools and dependencies
mise run server # Start temporal server and workers

# In another terminal:
mise run workflow # Run the workflow client
```

**Available mise tasks:**

```bash
mise run install # Install dependencies
mise run server # Run temporal server and workers
mise run workflow # Run workflow client
mise run stop # Stop temporal server and workers
mise run kill # Force kill all processes
```

### Option 2: Manual installation

#### Installing Temporal CLI

The Temporal CLI is required to run the local Temporal server:

**macOS:**

```bash
brew install temporal
```

**Linux:**

```bash
# Using Homebrew
brew install temporal

# Or using curl
curl -sSf https://temporal.download/cli.sh | sh
```

**Windows:**

```powershell
# Using Scoop
scoop install temporal

# Or download from releases
# https://github.com/temporalio/cli/releases
```

Verify the installation:

```bash
temporal --version
```

#### Installing overmind (optional)

Overmind is a process manager that makes it easy to run multiple services together. If you want to use `overmind start` to run everything at once, install it for your platform:

**macOS:**

```bash
brew install overmind
```

**Linux:**

```bash
brew install overmind

# Or download from releases
# https://github.com/DarthSim/overmind/releases
```

**Windows:**
Overmind is not officially supported on Windows. Use the manual approach below instead.

## Setup

```bash
# Copy and configure environment
cp .env.example .env
# Edit .env with your BRAINTRUST_API_KEY

# Install dependencies
pnpm install
```

## Running the Example

### Option 1: Using overmind

Start the temporal server and workers together:

```bash
overmind start
```

Then in another terminal, run the workflow:

```bash
pnpm run client
```

### Option 2: Manual

1. Start the Temporal server:

```bash
temporal server start-dev
```

2. Start the worker:

```bash
pnpm run worker
```

3. Run the client:

```bash
pnpm run client

# Or with a signal:
pnpm run client -- --signal
```

## What Gets Traced

- **Client span**: Wraps the workflow execution call
- **Workflow span**: Created via sinks when the workflow starts
- **Activity spans**: Created for each activity execution with parent linking

The trace hierarchy looks like:

```
Client span ("example.temporal.workflow")
└── Workflow span ("temporal.workflow.simpleWorkflow")
└── Activity span ("temporal.activity.addTen")
└── Activity span ("temporal.activity.multiplyByTwo")
└── Activity span ("temporal.activity.subtractFive")
```

## How It Works

1. **Client interceptor**: Captures the current Braintrust span context and adds it to workflow headers
2. **Workflow interceptor**: Extracts parent context from headers and creates a workflow span via sinks
3. **Sinks**: Allow the workflow isolate to call into Node.js to create spans (with `callDuringReplay: false`)
4. **Activity interceptor**: Creates spans for each activity, using the workflow span as parent
40 changes: 40 additions & 0 deletions integrations/temporal-js/examples/temporal/mise.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Mise will automatically read and use .tool-versions files as well as this file.
[settings]
experimental=true

[env]
# See env.example to configure API keys.
_.file = ".env"

[tools]
node = "20"
temporal = "latest"
overmind = "latest"

[hooks]
postinstall = "mise run install"

[tasks.install]
description = "Install dependencies"
run = "pnpm install --ignore-workspace"

[tasks.server]
description = "Run temporal server and workers"
run = "overmind s"

[tasks.workflow]
description = "Run workflow client"
run = "pnpm exec ts-node src/client.ts"

[tasks.stop]
description = "Stop temporal server and workers"
run = "overmind quit || true"

[tasks.kill]
description = "Force kill temporal server and workers"
run = """
pkill -f 'examples/temporal.*ts-node' 2>/dev/null || true
pkill -f 'overmind.*temporal' 2>/dev/null || true
rm -f .overmind.sock
echo 'Server killed'
"""
26 changes: 26 additions & 0 deletions integrations/temporal-js/examples/temporal/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "temporal-example",
"version": "1.0.0",
"private": true,
"scripts": {
"build": "tsc",
"worker": "pnpm exec ts-node src/worker.ts",
"client": "pnpm exec ts-node src/client.ts"
},
"dependencies": {
"@braintrust/temporal": "^0.1.0",
"@temporalio/activity": "^1.11.0",
"@temporalio/client": "^1.11.0",
"@temporalio/common": "^1.11.0",
"@temporalio/worker": "^1.11.0",
"@temporalio/workflow": "^1.11.0",
"braintrust": "^2.0.0",
"uuid": "^9.0.0"
},
"devDependencies": {
"@types/node": "^20.0.0",
"@types/uuid": "^9.0.0",
"ts-node": "^10.9.0",
"typescript": "^5.0.0"
}
}
Loading
Loading