wrapAgent wraps your agent function with OpenTelemetry tracing, creating a top-level span that captures the full execution — timing, errors, and any nested operations that happen inside it.
Basic Usage
import { wrapAgent } from "@uselemma/tracing";
export const callAgent = async (userMessage: string) => {
const wrappedFn = wrapAgent(
"my-agent",
async ({ onComplete }, input) => {
const result = await doWork(input.userMessage);
onComplete(result);
return result;
}
);
const { result, runId } = await wrappedFn({ userMessage });
return { result, runId };
};
from uselemma_tracing import TraceContext, wrap_agent
async def run_agent(ctx: TraceContext, input: dict):
result = await do_work(input["user_message"])
ctx.on_complete(result)
return result
async def call_agent(user_message: str):
wrapped = wrap_agent("my-agent", run_agent)
result, run_id, span = await wrapped({"user_message": user_message})
return result, run_id
Streaming
For streaming responses, set autoEndRoot: true so the RunBatchSpanProcessor automatically ends the root span when all direct child spans have finished. Call onComplete to signal the run is done, and recordError if something goes wrong.
import { wrapAgent } from "@uselemma/tracing";
export const callAgent = async (userMessage: string) => {
const wrappedFn = wrapAgent(
"my-agent",
async ({ onComplete, recordError }, input) => {
try {
const stream = await streamResponse(input.userMessage);
let fullResponse = "";
for await (const chunk of stream) {
fullResponse += chunk;
// stream to your client here
}
onComplete({ text: fullResponse });
return { text: fullResponse };
} catch (error) {
recordError(error);
throw error;
}
},
{ autoEndRoot: true }
);
const { result, runId } = await wrappedFn({ userMessage });
return { result, runId };
};
from uselemma_tracing import TraceContext, wrap_agent
async def run_agent(ctx: TraceContext, input: dict):
try:
stream = await stream_response(input["user_message"])
full_response = ""
async for chunk in stream:
full_response += chunk
# stream to your client here
ctx.on_complete(full_response)
return full_response
except Exception as err:
ctx.record_error(err)
raise
async def call_agent(user_message: str):
wrapped = wrap_agent("my-agent", run_agent, auto_end_root=True)
result, run_id, span = await wrapped({"user_message": user_message})
return result, run_id
API Reference
Parameters
wrapAgent(name, agentFn, options?)
| Parameter | Type | Description |
|---|
name | string | Agent identifier for filtering and grouping in the dashboard |
agentFn | (ctx: TraceContext, input: Input) => any | Your agent logic. Receives the call-time input as its second argument |
options | object | Optional configuration (see below) |
Options| Field | Type | Default | Description |
|---|
isExperiment | boolean | false | Tag this run as an experiment |
autoEndRoot | boolean | true | If true (the default), the top-level span is automatically ended when the wrapped function returns or throws. Set to false to manage the span lifetime manually via onComplete. |
Return Value| Field | Type | Description |
|---|
result | any | Your agent function’s return value |
runId | string | Unique identifier for this trace |
span | Span | The underlying OpenTelemetry span |
wrap_agent(name, fn, *, is_experiment, auto_end_root)
| Parameter | Type | Default | Description |
|---|
name | str | — | Agent identifier for filtering and grouping in the dashboard |
fn | Callable | — | Your agent logic. Receives the call-time input as its second argument |
is_experiment | bool | False | Tag this run as an experiment |
auto_end_root | bool | True | If True (the default), the top-level span is automatically ended when the wrapped function returns or throws. Set to False to manage the span lifetime manually via on_complete. |
Return ValueReturns a tuple of (result, run_id, span).| Field | Type | Description |
|---|
result | Any | Your agent function’s return value |
run_id | str | Unique identifier for this trace |
span | Span | The underlying OpenTelemetry span |
TraceContext
Your agent function always receives a TraceContext object as its first argument:
| Field | Description |
|---|
onComplete(result) / on_complete(result) | Signals the run is complete. When autoEndRoot is true (the default), this is a no-op — the span ends automatically when the function returns. When autoEndRoot is false, this ends the root span and returns true. |
recordError(error) / record_error(error) | Records the exception and marks the span as errored |
span | The underlying OpenTelemetry span for custom attributes or child spans |
runId / run_id | The unique run identifier |
By default (autoEndRoot: true), the root span ends automatically when the wrapped function returns or throws — no manual onComplete call needed. Set autoEndRoot: false to manage span lifetime yourself via onComplete.
Next Steps