Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 181 additions & 0 deletions fixtures/pdfs/generate-pdfs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
#!/bin/bash
# Script to generate test PDFs with unique verification codes
# Each PDF contains embedded text that can be used to verify end-to-end AI processing
#
# TODO: Dynamically randomize verification codes at generation time so fixtures can't be
# guessed in advance. This would involve:
# 1. Generating a random code for each PDF at runtime
# 2. Writing both PDF and JSON metadata with the randomized code
# 3. Reading the code from the JSON in tests instead of hard-coding
# This ensures the "needle" in the PDF cannot be known before execution.

set -e

# Verification codes
SMALL_CODE="SMALL-7X9Q2"
MEDIUM_CODE="MEDIUM-K4P8R"
LARGE_CODE="LARGE-M9N3T"
XLARGE_CODE="XLARGE-W6H5V"

echo "Generating test PDFs with verification codes..."
echo

# Small PDF (33KB) - Text only with minimal content
echo "Creating small.pdf with code $SMALL_CODE..."
convert -size 800x600 xc:white \
-pointsize 36 -gravity center \
-annotate +0-150 'SMALL PDF TEST' \
-annotate +0-80 'Verification Code:' \
-fill red -pointsize 42 \
-annotate +0-20 "$SMALL_CODE" \
-fill black -pointsize 18 \
-annotate +0+60 'If you can read this code, PDF processing works.' \
small_text.jpg

convert small_text.jpg small.pdf
rm small_text.jpg

# Medium PDF (813KB) - Text + some image padding
echo "Creating medium.pdf with code $MEDIUM_CODE..."
magick -size 1200x900 xc:white \
-pointsize 48 -gravity center \
-annotate +0-250 'MEDIUM PDF TEST' \
-pointsize 32 \
-annotate +0-150 'Verification Code:' \
-fill blue -pointsize 38 \
-annotate +0-80 "$MEDIUM_CODE" \
-fill black -pointsize 20 \
-annotate +0+20 'This is a medium test document.' \
-annotate +0+60 'Code confirms AI processed the PDF.' \
medium_base.pdf

# Add filler images to increase size
magick -size 1000x1000 plasma:fractal med_fill1.jpg
magick -size 1000x1000 plasma:fractal med_fill2.jpg
magick medium_base.pdf med_fill1.jpg med_fill2.jpg medium.pdf
rm medium_base.pdf med_fill*.jpg

# Large PDF (3.4MB) - Text + more image padding
echo "Creating large.pdf with code $LARGE_CODE..."
magick -size 1600x1200 xc:white \
-pointsize 60 -gravity center \
-annotate +0-350 'LARGE PDF TEST' \
-pointsize 40 \
-annotate +0-250 'Verification Code:' \
-fill green -pointsize 96 \
-annotate +0-170 "$LARGE_CODE" \
-fill black -pointsize 24 \
-annotate +0-80 'Large test document for PDF uploads.' \
-annotate +0-40 'Verification code confirms processing.' \
large_base.pdf

# Add filler images to increase size
magick -size 1500x1500 plasma:fractal lg_fill1.jpg
magick -size 1500x1500 plasma:fractal lg_fill2.jpg
magick -size 1500x1500 plasma:fractal lg_fill3.jpg
magick -size 1500x1500 plasma:fractal lg_fill4.jpg
magick large_base.pdf lg_fill1.jpg lg_fill2.jpg lg_fill3.jpg lg_fill4.jpg large.pdf
rm large_base.pdf lg_fill*.jpg

# XLarge PDF (11MB) - Text + lots of image padding
echo "Creating xlarge.pdf with code $XLARGE_CODE..."
magick -size 2000x1500 xc:white \
-pointsize 72 -gravity center \
-annotate +0-450 'XLARGE PDF TEST' \
-pointsize 48 \
-annotate +0-330 'Verification Code:' \
-fill purple -pointsize 56 \
-annotate +0-240 "$XLARGE_CODE" \
-fill black -pointsize 28 \
-annotate +0-140 'Extra-large test document.' \
-annotate +0-100 'Code confirms AI read the PDF.' \
xlarge_base.pdf

# Add many filler images to increase size
for i in {1..8}; do
magick -size 2000x2000 plasma:fractal xl_fill$i.jpg
done
magick xlarge_base.pdf xl_fill*.jpg xlarge.pdf
rm xlarge_base.pdf xl_fill*.jpg

echo
echo "✓ PDF generation complete!"
echo
echo "Generating JSON metadata files..."
echo

# Create JSON metadata for each PDF with verification code
# Format: { "verificationCode": "CODE", "description": "...", "size": "...", "type": "test_fixture" }

cat > small.json << EOF
{
"verificationCode": "$SMALL_CODE",
"description": "Small test PDF (33KB) with minimal content",
"size": "small",
"type": "test_fixture",
"purpose": "Test basic PDF processing with FileParserPlugin"
}
EOF
echo " ✓ small.json"

cat > medium.json << EOF
{
"verificationCode": "$MEDIUM_CODE",
"description": "Medium test PDF (813KB) with text and image padding",
"size": "medium",
"type": "test_fixture",
"purpose": "Test PDF processing with moderate file size"
}
EOF
echo " ✓ medium.json"

cat > large.json << EOF
{
"verificationCode": "$LARGE_CODE",
"description": "Large test PDF (3.4MB) for FileParserPlugin regression testing",
"size": "large",
"type": "test_fixture",
"purpose": "Test large PDF handling and plugin activation",
"regression": "Validates fix for FileParserPlugin large PDF issue"
}
EOF
echo " ✓ large.json"

cat > xlarge.json << EOF
{
"verificationCode": "$XLARGE_CODE",
"description": "Extra-large test PDF (11MB) with extensive content",
"size": "xlarge",
"type": "test_fixture",
"purpose": "Test maximum file size handling with FileParserPlugin"
}
EOF
echo " ✓ xlarge.json"

echo
echo "Generated files:"
ls -lh *.pdf *.json
echo
echo "Verification codes:"
echo " small.pdf -> $SMALL_CODE (small.json)"
echo " medium.pdf -> $MEDIUM_CODE (medium.json)"
echo " large.pdf -> $LARGE_CODE (large.json)"
echo " xlarge.pdf -> $XLARGE_CODE (xlarge.json)"
echo
echo "Validating PDFs..."
for pdf in small.pdf medium.pdf large.pdf xlarge.pdf; do
if qpdf --check "$pdf" &>/dev/null; then
echo " ✓ $pdf is valid"
else
echo " ✗ $pdf has issues"
fi
done
echo
echo "Validating JSON metadata..."
for json in small.json medium.json large.json xlarge.json; do
if python3 -m json.tool "$json" > /dev/null 2>&1; then
echo " ✓ $json is valid"
else
echo " ✗ $json has syntax errors"
fi
done
8 changes: 8 additions & 0 deletions fixtures/pdfs/large.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"verificationCode": "LARGE-M9N3T",
"description": "Large test PDF (3.4MB) for FileParserPlugin regression testing",
"size": "large",
"type": "test_fixture",
"purpose": "Test large PDF handling and plugin activation",
"regression": "Validates fix for FileParserPlugin large PDF issue"
}
Binary file added fixtures/pdfs/large.pdf
Binary file not shown.
7 changes: 7 additions & 0 deletions fixtures/pdfs/medium.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"verificationCode": "MEDIUM-K4P8R",
"description": "Medium test PDF (813KB) with text and image padding",
"size": "medium",
"type": "test_fixture",
"purpose": "Test PDF processing with moderate file size"
}
Binary file added fixtures/pdfs/medium.pdf
Binary file not shown.
7 changes: 7 additions & 0 deletions fixtures/pdfs/small.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"verificationCode": "SMALL-7X9Q2",
"description": "Small test PDF (33KB) with minimal content",
"size": "small",
"type": "test_fixture",
"purpose": "Test basic PDF processing with FileParserPlugin"
}
Binary file added fixtures/pdfs/small.pdf
Binary file not shown.
7 changes: 7 additions & 0 deletions fixtures/pdfs/xlarge.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"verificationCode": "XLARGE-W6H5V",
"description": "Extra-large test PDF (11MB) with extensive content",
"size": "xlarge",
"type": "test_fixture",
"purpose": "Test maximum file size handling with FileParserPlugin"
}
Binary file added fixtures/pdfs/xlarge.pdf
Binary file not shown.
15 changes: 15 additions & 0 deletions typescript/fetch/src/plugin-file-parser/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# OpenRouter FileParserPlugin Examples (Fetch)

Examples demonstrating OpenRouter's FileParserPlugin with raw fetch API.

## Overview

The FileParserPlugin enables PDF processing for models that don't natively support file inputs. The plugin:

- Accepts PDFs via base64-encoded data URLs
- Extracts text using configurable engines
- Returns parsed content to the model for processing

## Examples

See the TypeScript files in this directory for specific examples.
161 changes: 161 additions & 0 deletions typescript/fetch/src/plugin-file-parser/file-parser-all-sizes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/**
* Example: OpenRouter FileParserPlugin - All PDF Sizes
*
* This example demonstrates using OpenRouter's FileParserPlugin with raw fetch
* to process PDF documents of various sizes. The plugin automatically parses PDFs
* and makes them consumable by LLMs, even for models that don't natively support
* file inputs.
*
* Key Points:
* - FileParserPlugin processes PDFs for models without native file support
* - PDFs are sent via base64-encoded data URLs
* - Plugin must be explicitly configured in the request body
* - Tests multiple PDF sizes: small (33KB), medium (813KB), large (3.4MB), xlarge (10.8MB)
* - Uses shared fixtures module with absolute paths
*
* To run: bun run typescript/fetch/src/plugin-file-parser/file-parser-all-sizes.ts
*/

import {
PDF_SIZES,
type PdfSize,
extractCode,
formatSize,
getPdfSize,
readExpectedCode,
readPdfAsDataUrl,
} from '@openrouter-examples/shared/fixtures';
import type { ChatCompletionResponse } from '@openrouter-examples/shared/types';

const OPENROUTER_API_URL = 'https://openrouter.ai/api/v1/chat/completions';

/**
* Make a request to process a PDF with FileParserPlugin
*/
async function processPdf(
size: PdfSize,
expectedCode: string,
): Promise<{ success: boolean; extracted: string | null; usage?: unknown }> {
const dataUrl = await readPdfAsDataUrl(size);
const fileSize = getPdfSize(size);

console.log(`\n=== ${size.toUpperCase()} PDF ===`);
console.log(`Size: ${formatSize(fileSize)}`);
console.log(`Expected: ${expectedCode}`);

if (!process.env.OPENROUTER_API_KEY) {
throw new Error('OPENROUTER_API_KEY environment variable is not set');
}

const response = await fetch(OPENROUTER_API_URL, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`,
'Content-Type': 'application/json',
'HTTP-Referer': 'https://github.com/openrouter/examples',
'X-Title': `FileParser - ${size} PDF`,
},
body: JSON.stringify({
model: 'openai/gpt-4o-mini',
messages: [
{
role: 'user',
content: [
{
type: 'file',
file: {
filename: `${size}.pdf`,
file_data: dataUrl,
},
},
{
type: 'text',
text: 'Extract the verification code. Reply with ONLY the code.',
},
],
},
],
plugins: [
{
id: 'file-parser',
pdf: {
engine: 'mistral-ocr',
},
},
],
max_tokens: 500,
}),
});

if (!response.ok) {
const errorText = await response.text();
throw new Error(`HTTP error! status: ${response.status}, body: ${errorText}`);
}

const data = (await response.json()) as ChatCompletionResponse;
const responseText = data.choices[0].message.content;
const extracted = extractCode(responseText);
const success = extracted === expectedCode;

console.log(`Extracted: ${extracted || '(none)'}`);
console.log(`Status: ${success ? '✅ PASS' : '❌ FAIL'}`);
console.log(`Tokens: ${data.usage.total_tokens}`);

return { success, extracted, usage: data.usage };
}

/**
* Main example
*/
async function main() {
console.log('╔════════════════════════════════════════════════════════════════════════════╗');
console.log('║ OpenRouter FileParserPlugin - All PDF Sizes ║');
console.log('╚════════════════════════════════════════════════════════════════════════════╝');
console.log();
console.log('Testing PDF processing with verification code extraction');
console.log();

const results: boolean[] = [];

try {
for (const size of PDF_SIZES) {
try {
const expectedCode = await readExpectedCode(size);
const result = await processPdf(size, expectedCode);
results.push(result.success);
} catch (error) {
console.log('Status: ❌ FAIL');
console.log(`Error: ${error instanceof Error ? error.message : String(error)}`);
results.push(false);
}
}

const passed = results.filter(Boolean).length;
const total = results.length;

console.log('\n' + '='.repeat(80));
console.log(`Results: ${passed}/${total} passed`);
console.log('='.repeat(80));

if (passed === total) {
console.log('\n✅ All PDF sizes processed successfully!');
process.exit(0);
}
console.log('\n❌ Some PDF tests failed');
process.exit(1);
} catch (error) {
console.error('\n❌ ERROR during testing:');

if (error instanceof Error) {
console.error('Error message:', error.message);
console.error('Stack trace:', error.stack);
} else {
console.error('Unknown error:', error);
}

process.exit(1);
}
}

// Run the example
main();
Loading