Streaming Dynamic Files & PDF Generation in watsonx Orchestrate: Delivering On-Demand Reports

WXO Labs Series Icon By Markus van Kempen | Series: WXO Labs & Tutorial Guide (Part 6 of 15)
Interactive Lab Portal: https://markusvankempen.github.io/wxo-labs/
GitHub Repository: https://github.com/markusvankempen/wxo-labs

Streaming Dynamic Files & PDF Generation in watsonx Orchestrate: Delivering On-Demand Reports

Why This Pattern Matters

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.


📋 Prerequisites & Setup

Ensure your environment has the required dependencies:

pip install ibm-watsonx-orchestrate

Verify active environment:

orchestrate env list

🛠️ Step 1: Implement the Logic (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
"""

📦 Step 2: Declare Dependencies (requirements.txt)

ibm-watsonx-orchestrate>=2.9.0
reportlab>=4.0.0

🤖 Step 3: Configure the AI Agent (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

🚀 Step 4: Import & Test Live in Terminal

Import tool and test conversationally:

orchestrate chat ask -n report_publisher_agent "Generate a PDF report on Q3 AI Adoption"

Live Response:

╭─ 🤖 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                                          │
╰────────────────────────────────────────────────────────────────────────╯

💡 Pro Tips & Gotchas


🏷️ Recommended Community Tags

When publishing this blog post, select or enter the following tags in the community editor:

watsonx Orchestrate, PDF Generation, File Streaming, ReportLab, AI Documents, Python


🗺️ Series Roadmap (15 Hands-On Labs)

This tutorial is Part 6 of 15 in the complete enterprise hands-on series:

Explore the interactive curriculum and code repository: