LLM spans
Record LLM calls automatically or manually.
Anthropic - wrapAnthropic
Wrap your Anthropic client once and every messages.create() call is traced automatically. Import from @refactorlabs/because/anthropic:
import Anthropic from '@anthropic-ai/sdk';
import { wrapAnthropic } from '@refactorlabs/because/anthropic';
const anthropic = wrapAnthropic(new Anthropic());Then use it exactly as before:
await because.startTrace('Assess claim', async () => {
// llm span recorded automatically - messages, model, response, tokens
const message = await anthropic.messages.create({
model: 'claude-sonnet-4-6',
max_tokens: 1024,
messages: [{ role: 'user', content: prompt }]
});
const result = parseResponse(message.content[0].text);
});Each span captures the provider, model, messages (and system prompt if set), response text, token counts, and stop reason.
Streaming
Streaming works transparently. Pass stream: true as normal - the span is recorded when the stream completes:
const stream = await anthropic.messages.create({
model: 'claude-sonnet-4-6',
max_tokens: 1024,
messages,
stream: true
});
for await (const event of stream) {
if (
event.type === 'content_block_delta' &&
event.delta.type === 'text_delta'
) {
process.stdout.write(event.delta.text);
}
}
// span recorded here, after the stream is consumedInstall
@anthropic-ai/sdk is a peer dependency - use the version already in your project:
npm install @refactorlabs/because
# @anthropic-ai/sdk already in your projectOpenAI - wrapOpenAI
Same pattern for OpenAI. Import from @refactorlabs/because/openai:
import OpenAI from 'openai';
import { wrapOpenAI } from '@refactorlabs/because/openai';
const openai = wrapOpenAI(new OpenAI());Every chat.completions.create() call is traced automatically:
await because.startTrace('Generate summary', async () => {
// llm span recorded automatically
const completion = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [{ role: 'user', content: prompt }]
});
});Streaming
Pass stream_options: { include_usage: true } to capture token counts in the stream:
const stream = await openai.chat.completions.create({
model: 'gpt-4o',
messages,
stream: true,
stream_options: { include_usage: true }
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? '');
}
// span recorded hereInstall
openai is a peer dependency - use the version already in your project:
npm install @refactorlabs/because
# openai already in your projectManual spans
For other providers or when you need full control, use addSpan with type: 'llm' directly.
because.addSpan('llm', 'Classify intent', {
input: {
messages: [
{ role: 'system', content: 'You are a claims assessor.' },
{ role: 'user', content: 'Is this claim fraudulent? ...' }
],
provider: 'anthropic',
model: 'claude-sonnet-4-6'
},
output: { response: 'No indicators of fraud detected.' },
durationMs: 340
});Plain prompt
If you're not using a chat-style API, pass prompt instead of messages:
because.addSpan('llm', 'Summarise claim', {
input: {
prompt: 'Summarise the following claim in one sentence: ...',
provider: 'openai',
model: 'gpt-4o'
},
output: { response: 'Rear-end collision claim for $7,590 damage.' },
durationMs: 820
});Callback form
Use the callback form to auto-set output and timing:
const result = await because.addSpan(
'llm',
'Classify intent',
{ input: { messages, provider: 'anthropic', model: 'claude-sonnet-4-6' } },
async () => {
const res = await myLlmClient.call(messages);
return { response: res.text };
}
);Input fields
| Field | Type | Description |
|---|---|---|
messages | LlmMessage[] | Chat messages (role + content pairs) |
prompt | string | Plain string prompt (alternative to messages) |
provider | string | Provider name, e.g. 'anthropic' |
model | string | Model identifier, e.g. 'gpt-4o' |
Output fields
| Field | Type | Description |
|---|---|---|
response | string | JsonObject | The model's response |
Types
import type {
LlmSpanInput,
LlmSpanOutput,
LlmMessage
} from '@refactorlabs/because';type LlmMessage = {
role: 'system' | 'user' | 'assistant' | 'tool';
content: string;
};
type LlmSpanInput = {
prompt?: string;
messages?: LlmMessage[];
provider?: string;
model?: string;
};
type LlmSpanOutput = {
response?: string | JsonObject;
};