Skip to content
Cavemandocs
MIT

Tracing

Correlate provider calls and tools, buffer spans, and flush OTLP explicitly.

Local middleware does not require this connected tracing service. See measurement for a provider-independent evaluation recipe.

A trace groups related provider requests, tool calls, and exported spans under one trace ID. The SDK supplies lightweight OTLP/JSON export without an OpenTelemetry runtime dependency. Examples assume your quickstart's cave client.

Trace a workflow#

typescript
await cave.trace({ workflow: "support", tags: { environment: "development" } }, async trace => {
const status = await trace.tool("lookup_status", { readOnly: true, idempotent: true },
() => ({ status: "ok" }));
const response = await trace.model.openai.responses.create({
model: process.env.CAVE_MODEL!,
input: `Explain this status: ${JSON.stringify(status)}`,
});
console.log(trace.traceId, response);

const exporter = trace.exporter({ serviceName: "support-service" });
exporter.recordSpan("support.complete", { operation: "chat", status: "ok" });
try {
await exporter.flush();
} catch (error) {
console.error("Trace export failed; pending spans:", exporter.pending);
}
});
python
with cave.trace(workflow="support", tags={"environment": "development"}) as trace:
status = trace.tool("lookup_status", {"readOnly": True, "idempotent": True},
lambda: {"status": "ok"})
response = trace.model["openai"].responses.create({
"model": os.environ["CAVE_MODEL"],
"input": f"Explain this status: {status}",
})
print(trace.trace_id, response)

exporter = trace.exporter(service_name="support-service")
exporter.record_span("support.complete", operation="chat", status="ok")
try:
exporter.flush()
except Exception:
print("Trace export failed; pending spans:", exporter.pending)

These trace-bound provider calls assume the service supplies provider access. The built-in trace model namespace exposes OpenAI routes; the direct provider factories cover other routes. Calling cave.openai() inside a trace callback does not automatically bind that client to the trace.

Trace continuity#

TypeScript exposes traceId and spanId; Python exposes trace_id and span_id. Trace IDs use 32 lowercase hexadecimal characters, span IDs 16. Valid nonzero inbound IDs can be passed through the trace options. Invalid IDs are replaced with fresh ones.

Calls made through the trace carry x-cave-trace-id and x-cave-parent-span-id. trace.exporter() uses the same trace ID for spans unless explicitly overridden. A standalone cave.exporter() is independent of a trace.

Tool events#

trace.tool() runs your callback and sends a metadata event with the tool name, outcome, sequence, duration, options, and trace tags. It preserves the tool's return value or original exception even when event delivery fails. The event does not contain the callback's result body.

Options include readOnly, idempotent, and artifactEligible. Python passes these through as dictionary keys using the same spelling. They describe the tool; they do not enforce sandboxing, permissions, or idempotency. Your application still owns those controls.

Buffer and export spans#

TypeScriptPythonEffect
recordSpan(name, options)record_span(name, **options)Buffer one span locally.
buildPayload()build_payload()Inspect the OTLP payload without sending it.
pendingpendingNumber of buffered/in-flight spans.
export() or flush()export() or flush()Send buffered spans to /v1/traces.
newTraceId() / newSpanId()new_trace_id() / new_span_id()Generate IDs for explicit correlation.

Specify provider, model, operation, and actual observed inputTokens/outputTokens/cachedTokens in TypeScript, or their snake_case equivalents in Python. Do not populate usage fields from compression estimates. Missing provider usage should stay missing.

An export failure preserves the failed batch for an explicit later retry. There is no automatic periodic exporter, trace-exit flush, or shutdown hook. Repeated trace.exporter() calls for the same service share a buffer; calling cave.exporter() again creates a new one. Flush the exporter you recorded into.

Limits#

A trace context does not itself emit a completed root span or intercept every HTTP call. Tool-event delivery is best effort, while explicit exporter failures are visible to the caller. Metadata tags and custom span attributes are supplied by your application; do not include secrets or raw personal data.

The SDK records fields you supply. A trace is not independent proof of provider usage or savings. Keep measurement vocabulary intact when reporting results.