Most large language models (LLMs) output text strings. Some advanced multimodal models — which go beyond text-only LLMs — can generate images, audio, and video. However, they typically do not produce output in specific file formats such as DOCX, PDF, or technical drawings.
In some scenarios, the final output must be in one of these formats. To close this gap, we can rely on the LLM's strong text-generation capability to produce content in text form, then transform that text into the desired format. The transformation can be done via function calling or MCP (Model Context Protocol).
For example: HTML text → PDF, or a Mermaid diagram written in Markdown → a rendered graph.
This article shows the steps to produce PDF files via function calling.
Create an executable function (Ruby)
1) Define what the function can do and its inputs.
{
"type": "function",
"function": {
"name": "html_to_pdf",
"description": "Convert HTML code to a PDF file and attach it to the conversation.",
"parameters": {
"type": "object",
"properties": {
"html": {
"type": "string",
"description": "HTML code to convert to PDF (maximum 100000 characters)"
},
"filename": {
"type": "string",
"description": "Optional filename for the PDF (e.g., 'report.pdf'). If not provided, a UUID-based filename will be generated."
}
},
"required": [ "html" ]
}
}
}
2) Implement the actual HTML-to-PDF conversion. This example uses WickedPdf.
html = ""
pdf_generator = WickedPdf.new
pdf_binary = pdf_generator.pdf_from_string(
html,
page_size: "A4",
margin: {
top: 15,
bottom: 20,
left: 15,
right: 15
},
encoding: "UTF-8",
# Footer with page numbers
footer: {
center: "Page [page] of [topage]",
font_size: 9
},
# Security options
disable_javascript: true,
no_stop_slow_scripts: true,
disable_external_links: true,
enable_local_file_access: false,
load_error_handling: "ignore"
)
3) Then attach the file to your conversation or message.
message.files.attach(
io: StringIO.new(pdf_binary),
filename: filename,
content_type: "application/pdf"
)
4) And inform the model that the function calling is completed.
{
success: true,
message: "PDF generated successfully and attached to the conversation"
}
5) This sample code does not perform HTML sanitization. In real use cases, you should validate and sanitize input HTML before conversion.
Use the Function with AI Models
Next, choose a model that outputs text and supports function calling. OpenAI GPT-5 Mini matches these criteria.
1) Enable function calling via tool calls. Add these fields in the request payload:
{
max_tool_calls: 5,
tools: [
{
type: "function",
name: func_definition[:function][:name],
description: func_definition[:function][:description],
parameters: func_definition[:function][:parameters]
}
]
}
2) When the model requests a function, execute it by monitoring the series of events returned by the model:
- response.output_item.added
event with type set to function_call
- response.output_item.done
event
- response.function_call_arguments.delta
event
- response.function_call_arguments.done
event
The first two events request execution, and the next two provide the input data. Store the call_id
from the first event.
3) Call the function to produce the PDF file with the provided input data, and return the function's result.
function_call_results << {
call_id: "#{call_id}",
type: "function_call_output",
output: {
"success": true,
"message": "PDF generated successfully and attached to the conversation"
}
}
4) Send a new request to the AI model, with the function_call_results added.
The result
1) We test the feature by requesting a recipe in PDF format:
2) The PDF is generated correctly.