PDFs in Traces
Capture the PDFs sent to your models, and add documents to any trace or span
Overview
Confident AI captures the PDFs in your traces and displays each one as an embedded preview in a span's input or output, with a link to open the full document in a new tab. This is useful for document-heavy use cases, such as contract review, invoice extraction, or report summarization, where you need to see the exact document a model was given to judge its answer.
PDFs get onto a trace in two ways:
- Automatically, from the model calls recorded by a supported integration
- Manually, by adding
Mediaat the span or trace level
Capture PDFs from Model Calls
If you're using a supported integration, you don't need to do anything extra. Send PDFs to your model the way your provider SDK expects them, and as long as init() has run, they're captured with the rest of the messages:
import base64
from pathlib import Path
from confident_trace import init, shutdown
from openai import OpenAI
init()
client = OpenAI()
document = base64.b64encode(Path("report.pdf").read_bytes()).decode()
try:
response = client.chat.completions.create(
model="gpt-4.1",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "Summarize this report."},
{
"type": "file",
"file": {
"filename": "report.pdf",
"file_data": f"data:application/pdf;base64,{document}",
},
},
],
}],
)
print(response.choices[0].message.content)
finally:
shutdown()import { readFileSync } from "node:fs";
import OpenAI from "openai";
import { init } from "confident-trace";
const runtime = init();
const client = new OpenAI();
const document = readFileSync("report.pdf").toString("base64");
try {
const response = await client.chat.completions.create({
model: "gpt-4.1",
messages: [{
role: "user",
content: [
{ type: "text", text: "Summarize this report." },
{
type: "file",
file: {
filename: "report.pdf",
file_data: `data:application/pdf;base64,${document}`,
},
},
],
}],
});
console.log(response.choices[0]?.message.content);
} finally {
await runtime.shutdown();
}Remember to launch your entry point with the Node preload so the OpenAI call is instrumented.
Here, the OpenAI integration records the file part on the LLM span, and the report is displayed as a preview in the span's input. Every other supported integration works the same way, so an Anthropic document block, a Gemini inline file, or a LangChain file content block is captured just like this. If you pass the PDF as a URL instead of inline data, the span records the URL as a reference.
Add PDFs to Traces & Spans
For PDFs that aren't part of a model call, such as a document your app loaded or a file a user uploaded, wrap the file in Media and add it at the span or trace level. A PDF can be a value on its own, or part of a string:
from confident_trace import Media, init, span, update_span, shutdown
init()
@span(type="tool")
def read_policy(path: str):
policy = Media.from_file(path)
update_span(input=policy)
return "Refunds are accepted within 30 days of purchase."
try:
read_policy("refund-policy.pdf")
finally:
shutdown()Here, the PDF becomes the span's input on its own, and the text the tool returns is captured as the span's output, so you can check the extracted text against the source document. To place a PDF inside a string instead, use it in an f-string, such as update_span(input=f"Summarize {policy}").
update_trace() takes PDFs the same way, if you'd rather put the document on the trace's input or output. To create a PDF from a URL, bytes, or base64 instead of a local file, see Create Media.
import { Media, init, span, updateSpan } from "confident-trace";
const runtime = init();
const readPolicy = span({ name: "read_policy", type: "tool" }, (path: string) => {
const policy = Media.fromFile(path);
updateSpan({ input: policy });
return "Refunds are accepted within 30 days of purchase.";
});
try {
readPolicy("refund-policy.pdf");
} finally {
await runtime.shutdown();
}Here, the PDF becomes the span's input on its own, and the text the tool returns is captured as the span's output, so you can check the extracted text against the source document. To place a PDF inside a string instead, use it in a template literal, such as updateSpan({ input: `Summarize ${policy}` }).
updateTrace() takes PDFs the same way, if you'd rather put the document on the trace's input or output. To create a PDF from a URL, bytes, or base64 instead of a local file, see Create Media.
Next Steps
With PDFs in your traces, see how images and audio are captured.
Images
Capture the images sent to your models, and add images to any trace or span.
Audio
Add audio to your messages and play it back on each message in a trace.
Last updated on