Documentation Index
Fetch the complete documentation index at: https://docs.mortemlabs.com/llms.txt
Use this file to discover all available pages before exploring further.
Mortem wraps the OpenAI client structurally, patching chat.completions.create at runtime. It does not import the openai package itself, so the wrapper works with any version of the SDK and adds no extra dependency to your agent.
What gets captured
For each call to chat.completions.create, Mortem records an llm_call event containing:
- Prompts — the full message array passed to the model
- Completion — the assistant message content returned
- Model — the model string from the request parameters
- Token usage —
prompt_tokens, completion_tokens, and total_tokens from the response
- Cost estimate — calculated from token counts and recorded on the event
- Finish reason —
stop, length, tool_calls, or any other finish reason the model returns
- Tool calls — structured tool call arguments when the model uses function calling
Both standard (non-streaming) and streaming responses are captured. For streaming, Mortem accumulates content deltas as they arrive and records the assembled completion when the stream ends.
Prerequisites
Install the SDK and create an agent in the dashboard before continuing. You need MORTEM_API_KEY and MORTEM_AGENT_ID set in your environment.
Integration
Initialize the Mortem client
Create a Mortem instance at module scope. This is typically done once at the start of your agent process.import { Mortem } from "@mortemlabs/sdk"
const mortem = new Mortem({
apiKey: process.env.MORTEM_API_KEY ?? "",
agentId: process.env.MORTEM_AGENT_ID,
verifyToken: process.env.MORTEM_VERIFY_TOKEN, // remove after first verified run
environment: "devnet",
})
verifyToken is only needed during your first deployment. Once the dashboard shows the agent as verified, remove MORTEM_VERIFY_TOKEN from your environment and code.
Wrap the OpenAI client
Pass your OpenAI client instance to mortem.wrapOpenAI. The wrapper patches chat.completions.create in place and returns the same client reference. All subsequent calls on the wrapped client are automatically traced.import OpenAI from "openai"
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })
const tracedOpenAI = mortem.wrapOpenAI(openai)
You can replace the original variable if you want tracing for every call in the module:const openai = mortem.wrapOpenAI(new OpenAI({ apiKey: process.env.OPENAI_API_KEY }))
Start a session and run the agent
Create a session with mortem.startSession, then run your agent logic inside session.run. All chat.completions.create calls made through the wrapped client inside the callback are automatically associated with this trace.const session = await mortem.startSession({
inputSummary: "Analyze market conditions and decide whether to swap",
tags: ["openai", "devnet"],
})
try {
const result = await session.run(async () => {
return tracedOpenAI.chat.completions.create({
model: "gpt-4o",
messages: [
{
role: "system",
content: "You are a Solana trading bot. Analyze the data and respond with a trading decision.",
},
{
role: "user",
content: "SOL is at $145 and JUP is at $0.62. Should I swap 1 SOL for JUP?",
},
],
})
})
const decision = result.choices[0]?.message.content ?? ""
await session.complete(decision)
} catch (error) {
await session.fail(error)
} finally {
await mortem.close()
}
Always call mortem.close() in a finally block. It flushes the trace buffer and ensures all events reach the ingest service before the process exits.
Complete example
import { Mortem } from "@mortemlabs/sdk"
import OpenAI from "openai"
const mortem = new Mortem({
apiKey: process.env.MORTEM_API_KEY ?? "",
agentId: process.env.MORTEM_AGENT_ID,
verifyToken: process.env.MORTEM_VERIFY_TOKEN,
environment: "devnet",
})
const openai = mortem.wrapOpenAI(new OpenAI({ apiKey: process.env.OPENAI_API_KEY }))
const session = await mortem.startSession({
inputSummary: "Analyze market conditions and decide whether to swap",
tags: ["openai", "devnet"],
})
try {
const result = await session.run(async () => {
return openai.chat.completions.create({
model: "gpt-4o",
messages: [
{
role: "system",
content: "You are a Solana trading bot. Analyze the data and respond with a trading decision.",
},
{
role: "user",
content: "SOL is at $145 and JUP is at $0.62. Should I swap 1 SOL for JUP?",
},
],
})
})
const decision = result.choices[0]?.message.content ?? ""
await session.complete(decision)
} catch (error) {
await session.fail(error)
} finally {
await mortem.close()
}
Streaming
When you pass stream: true to chat.completions.create, the wrapper detects the async-iterable response and taps it with a generator that accumulates content deltas. The llm_call event is completed with the assembled text once the stream is exhausted.
const stream = await openai.chat.completions.create({
model: "gpt-4o",
stream: true,
messages: [{ role: "user", content: "Evaluate current market risk." }],
})
let text = ""
for await (const chunk of stream) {
text += chunk.choices[0]?.delta?.content ?? ""
}
await session.complete(text)
No additional configuration is needed — the same wrapped client handles both streaming and non-streaming calls.
Function calling
When the model returns tool calls, Mortem captures each tool call block under the output.toolCalls field of the llm_call event. Each entry includes the tool call ID, function name, and raw arguments string as returned by the API.
If you want to trace the execution of tool functions themselves — not just the model’s decision to call them — add a tool_call event with session.beginEvent("tool_call", payload) around your tool handler, or use the Vercel AI SDK wrapper which instruments tool execution automatically.