Generating custom binary PDF documents on the fly with ReportLab and returning download URLs in chat.
Executives often require formal PDF briefing packets with corporate headers and timestamps. Rather than sending static templates, our agent synthesizes live conversational data and renders a downloadable binary PDF on demand.
pdf_generator.py)from ibm_watsonx_orchestrate import tool
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
import tempfile
import os
from datetime import datetime
@tool
def generate_report_pdf(report_title: str, report_body: str) -> str:
"""
Generate a formatted binary PDF document and return a download URI.
"""
temp_dir = tempfile.gettempdir()
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
filename = f"WXO_Report_{timestamp}.pdf"
filepath = os.path.join(temp_dir, filename)
c = canvas.Canvas(filepath, pagesize=letter)
width, height = letter
# Header branding
c.setFont("Helvetica-Bold", 18)
c.setFillColorRGB(0.06, 0.38, 0.99) # IBM Blue
c.drawString(50, height - 50, "IBM watsonx Orchestrate")
c.setFont("Helvetica-Bold", 14)
c.setFillColorRGB(0.1, 0.1, 0.1)
c.drawString(50, height - 85, report_title)
c.setFont("Helvetica", 9)
c.setFillColorRGB(0.4, 0.4, 0.4)
c.drawString(50, height - 105, f"Generated: {datetime.now().strftime('%B %d, %Y at %H:%M:%S UTC')}")
# Body
c.setFont("Helvetica", 11)
c.setFillColorRGB(0.1, 0.1, 0.1)
text_object = c.beginText(50, height - 140)
for line in report_body.split("\n"):
text_object.textLine(line)
c.drawText(text_object)
c.save()
filesize = os.path.getsize(filepath)
return f"""
📄 PDF Document Generated Successfully
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Title: {report_title}
File: {filename} ({filesize:,} bytes)
Location: {filepath}
Status: Ready for download
"""
requirements.txt)ibm-watsonx-orchestrate>=2.9.0
reportlab>=4.0.0
agent.yaml)spec_version: v1
kind: native
name: report_publisher_agent
title: "Report Publisher"
description: "Generates formatted PDF executive briefings on demand."
model: ibm/granite-3-8b-instruct
instructions: |
You are an executive publishing assistant. Format summaries into structured
briefings and call `generate_report_pdf` to create the document.
tools:
- generate_report_pdf
orchestrate chat ask -n report_publisher_agent "Generate a PDF report on Q3 AI Adoption"
╭─ 🤖 report_publisher_agent ───────────────────────────────────────────╮
│ │
│ 📄 PDF Document Generated Successfully │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ Title: Q3 AI Adoption Briefing │
│ File: WXO_Report_20260924_201500.pdf (4,812 bytes) │
│ Status: Ready for download │
╰────────────────────────────────────────────────────────────────────────╯
On local dev or container sandboxes, `/tmp` works directly. For production cloud deployments, stream the generated bytes to IBM Cloud Object Storage (COS) or AWS S3 and return a presigned HTTPS URL.
Stick to built-in PostScript fonts (`Helvetica`, `Times-Roman`, `Courier`) to avoid bundle size bloat and missing TTF font errors.