Skip to main content

Laminar.startSpan(options)

Create a span without activating it. Use for spans you’ll pass to other functions.
const span = Laminar.startSpan({ name: 'custom_operation' });
try {
  const result = doWork();
  span.setAttributes({ 'result.count': result.length });
} finally {
  span.end();
}
Parameters:
NameTypeDefaultDescription
namestringSpan name (required)
inputanySpan input (JSON-serialized)
spanType'DEFAULT' | 'LLM' | 'TOOL''DEFAULT'Span type
tagsstring[]Span tags
userIdstringUser ID
sessionIdstringSession ID
metadataRecord<string, any>Metadata
parentSpanContextstring | LaminarSpanContextParent context
contextContextOpenTelemetry context
Returns: SpanNote: Not activated—caller must propagate context manually or use withSpan.

Laminar.startActiveSpan(options)

Create and activate a span. Child spans nest automatically.
const span = Laminar.startActiveSpan({ name: 'parent_operation' });
try {
  // Any spans created here are children of parent_operation
  await doWork();
} finally {
  span.end();
}
Parameters:
NameTypeDefaultDescription
namestringSpan name (required)
inputanySpan input (JSON-serialized)
spanType'DEFAULT' | 'LLM' | 'TOOL''DEFAULT'Span type
tagsstring[]Span tags
userIdstringUser ID
sessionIdstringSession ID
metadataRecord<string, any>Metadata
parentSpanContextstring | LaminarSpanContextParent context
contextContextOpenTelemetry context
Returns: LaminarSpan | SpanNote: Must call span.end() (recommend in finally block). Context push/pop handled automatically.

Laminar.withSpan(span, fn, endOnExit?)

Activate an existing span for the duration of a function.
const parentSpan = Laminar.startSpan({ name: 'parent' });

await Laminar.withSpan(parentSpan, async () => {
  // parentSpan is active here
  const child = Laminar.startSpan({ name: 'child' });
  await doWork();
  child.end();
});

parentSpan.end();
Parameters:
NameTypeDefaultDescription
spanSpanSpan to activate
fn() => TFunction to execute
endOnExitbooleanfalseEnd span when function completes
Returns: Return value of fn (or Promise if async)Note: Records exception on error. If endOnExit is true, span ends after fn (awaited if Promise).Example: Pass span objects between functions
import { Laminar, observe, type Span } from '@lmnr-ai/lmnr';

const processData = (span: Span, data: unknown) =>
  Laminar.withSpan(span, async () => {
    return observe({ name: 'dataValidation' }, async () => validateData(data));
  });

export async function handleRequest(userInput: string) {
  const rootSpan = Laminar.startSpan({ name: 'handleUserRequest' });
  try {
    return await processData(rootSpan, userInput);
  } finally {
    rootSpan.end();
  }
}
Example: Custom span hierarchy
import { Laminar } from '@lmnr-ai/lmnr';

export async function processWorkflow(workflowData: { id: string }) {
  const workflowSpan = Laminar.startSpan({
    name: 'workflow-execution',
    metadata: { workflowId: workflowData.id },
  });

  try {
    await Laminar.withSpan(workflowSpan, async () => {
      const validationSpan = Laminar.startSpan({ name: 'validation' });
      await Laminar.withSpan(validationSpan, async () => {
        await validateWorkflow(workflowData);
      });
      validationSpan.end();

      const processingSpan = Laminar.startSpan({ name: 'processing' });
      await Laminar.withSpan(processingSpan, async () => {
        const result = await processWithLLM(workflowData);
        processingSpan.setAttributes({
          'processing.items_count': result.items.length,
        });
      });
      processingSpan.end();
    });
  } catch (error) {
    workflowSpan.recordException(error as Error);
    throw error;
  } finally {
    workflowSpan.end();
  }
}