Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
63 changes: 63 additions & 0 deletions typescript/tracing/anthropic-openinference/_runtime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import dotenv from "dotenv";
import Anthropic from "@anthropic-ai/sdk";
import { AnthropicInstrumentation } from "@arizeai/openinference-instrumentation-anthropic";
import { Respan } from "@respan/respan";
import { OpenInferenceInstrumentor } from "@respan/instrumentation-openinference";
import { fileURLToPath } from "node:url";

dotenv.config({
path: fileURLToPath(new URL("../../../.env", import.meta.url)),
quiet: true,
});

export const RUN_ID =
process.env.RESPAN_EXAMPLE_RUN_ID || `openinference-anthropic-${Date.now()}`;

function required(name: string): string {
const value = process.env[name]?.trim();
if (!value) throw new Error(`Set ${name} in the repository-root .env file.`);
return value;
}

export function createRuntime(): { respan: Respan } {
return {
respan: new Respan({
apiKey: required("RESPAN_API_KEY"),
baseURL: process.env.RESPAN_BASE_URL,
instrumentations: [
new OpenInferenceInstrumentor(AnthropicInstrumentation, Anthropic),
],
silenceInitializationMessage: true,
}),
};
}

export function createAnthropicClient(): Anthropic {
const gatewayBase = (
process.env.RESPAN_GATEWAY_BASE_URL?.trim() || required("RESPAN_BASE_URL")
).replace(/\/$/, "");
return new Anthropic({
apiKey: process.env.RESPAN_GATEWAY_API_KEY?.trim() || required("RESPAN_API_KEY"),
baseURL: `${gatewayBase}/anthropic`,
});
}

export async function runWithOpenInferenceWorkflow<T>(
respan: Respan,
workflowName: string,
fn: () => Promise<T>,
): Promise<T> {
await respan.initialize();
return await respan.propagateAttributes(
{
custom_identifier: RUN_ID,
trace_group_identifier: workflowName,
metadata: {
example: "typescript-anthropic-openinference",
run_id: RUN_ID,
workflow_name: workflowName,
},
},
async () => await respan.withWorkflow({ name: workflowName }, fn),
);
}
27 changes: 27 additions & 0 deletions typescript/tracing/anthropic-openinference/failure.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { createAnthropicClient, createRuntime, runWithOpenInferenceWorkflow } from "./_runtime.js";

const workflowName = "openinference_anthropic_failure";
const { respan } = createRuntime();
let rejected = false;

try {
await runWithOpenInferenceWorkflow(respan, workflowName, async () => {
await createAnthropicClient().messages.create({
model: process.env.RESPAN_ANTHROPIC_INVALID_MODEL || "respan-intentional-invalid-model",
max_tokens: 20,
messages: [
{ role: "user", content: "Exercise the expected Anthropic failure path." },
],
});
});
} catch (error) {
rejected = true;
const message = error instanceof Error ? error.message : String(error);
console.log(JSON.stringify({ workflowName, expectedFailure: message }, null, 2));
} finally {
await respan.shutdown();
}

if (!rejected) {
throw new Error("Expected the unsupported Anthropic model request to fail.");
}
45 changes: 20 additions & 25 deletions typescript/tracing/anthropic-openinference/hello_world.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,26 @@
/** Anthropic SDK — Chat completion with Respan tracing via OpenInference. */

import "dotenv/config";
import Anthropic from "@anthropic-ai/sdk";
import { AnthropicInstrumentation } from "@arizeai/openinference-instrumentation-anthropic";
import { Respan } from "@respan/respan";
import { OpenInferenceInstrumentor } from "@respan/instrumentation-openinference";
import { createAnthropicClient, createRuntime, runWithOpenInferenceWorkflow } from "./_runtime.js";

const respan = new Respan({
apiKey: process.env.RESPAN_API_KEY,
baseURL: process.env.RESPAN_BASE_URL,
instrumentations: [
new OpenInferenceInstrumentor(AnthropicInstrumentation, Anthropic),
],
});
await respan.initialize();
const workflowName = "openinference_anthropic_supported";
const { respan } = createRuntime();

const client = new Anthropic();
try {
const output = await runWithOpenInferenceWorkflow(respan, workflowName, async () => {
const message = await createAnthropicClient().messages.create({
model: process.env.RESPAN_ANTHROPIC_MODEL || "claude-sonnet-4-5-20250929",
max_tokens: 100,
messages: [
{ role: "user", content: "Write a haiku about recursion in programming." },
],
});

const message = await client.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 100,
messages: [
{ role: "user", content: "Write a haiku about recursion in programming." },
],
});

const text = message.content[0];
if (text.type === "text") {
console.log(text.text);
return message.content
.map((block) => (block.type === "text" ? block.text : ""))
.filter(Boolean)
.join("\n");
});
console.log(JSON.stringify({ workflowName, output }, null, 2));
} finally {
await respan.shutdown();
}
Loading