Skip to content
7 changes: 7 additions & 0 deletions typescript/ai-sdk-v5/src/plugin-file-parser/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# OpenRouter FileParserPlugin Examples (AI SDK)

Examples demonstrating OpenRouter's FileParserPlugin with AI SDK v5.

## Examples

See the TypeScript files in this directory for specific examples.
136 changes: 136 additions & 0 deletions typescript/ai-sdk-v5/src/plugin-file-parser/file-parser-all-sizes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/**
* Example: OpenRouter FileParserPlugin with AI SDK Provider
*
* Demonstrates PDF processing using the AI SDK with OpenRouter's file parser plugin.
* PDFs are sent as file attachments and automatically parsed server-side.
*
* Key Points:
* - FileParserPlugin explicitly configured for models without native PDF support
* - PDFs sent via data URI format
* - Tests multiple PDF sizes with verification code extraction
* - Uses shared fixtures module with absolute paths
*
* To run: bun run typescript/ai-sdk-v5/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 { createOpenRouter } from '@openrouter/ai-sdk-provider';
import { generateText } from 'ai';

const openrouter = createOpenRouter({
apiKey: process.env.OPENROUTER_API_KEY,
});

// Use a model that doesn't have native PDF support to demonstrate FileParserPlugin
const MODEL = 'openai/gpt-4o-mini';

/**
* Process a single PDF with FileParserPlugin
*/
async function testPdf(size: PdfSize, expectedCode: string): Promise<boolean> {
const dataUrl = await readPdfAsDataUrl(size);
const fileSize = getPdfSize(size);

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

const model = openrouter(MODEL, {
plugins: [
{
id: 'file-parser',
pdf: {
engine: 'mistral-ocr',
},
},
],
usage: { include: true },
});

const result = await generateText({
model,
messages: [
{
role: 'user',
content: [
{
type: 'text',
text: 'Extract the verification code. Reply with ONLY the code.',
},
{
type: 'file',
data: dataUrl,
mediaType: 'application/pdf',
},
],
},
],
});

const extracted = extractCode(result.text);
const success = extracted === expectedCode;

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

const usage = result.providerMetadata?.openrouter?.usage;
if (usage && typeof usage === 'object' && 'cost' in usage) {
const cost = usage.cost as number;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should assert the type

assert(typeof usage.cost === 'number')

Or something like that, I think

console.log(`Cost: $${cost.toFixed(6)}`);
}

return success;
}

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

const results: boolean[] = [];

for (const size of PDF_SIZES) {
try {
const expectedCode = await readExpectedCode(size);
results.push(await testPdf(size, expectedCode));
} 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);
}

main().catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});
86 changes: 86 additions & 0 deletions typescript/ai-sdk-v5/src/plugin-file-parser/file-parser-pdf-url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/**
* Example: OpenRouter FileParserPlugin - PDF URL (AI SDK)
*
* This example demonstrates sending PDFs via publicly accessible URLs using AI SDK.
* This is more efficient than base64 encoding as you don't need to download
* and encode the file.
*
* Key Points:
* - Send PDFs directly via URL without downloading
* - Works with AI SDK's file attachment format
* - Reduces payload size compared to base64
* - Ideal for publicly accessible documents
*
* To run: bun run typescript/ai-sdk-v5/src/plugin-file-parser/file-parser-pdf-url.ts
*/

import { createOpenRouter } from '@openrouter/ai-sdk-provider';
import { generateText } from 'ai';

const openrouter = createOpenRouter({
apiKey: process.env.OPENROUTER_API_KEY,
});

const MODEL = 'anthropic/claude-3.5-sonnet';

/**
* Example using the Bitcoin whitepaper (publicly accessible PDF)
*/
async function main() {
console.log('╔════════════════════════════════════════════════════════════════════════════╗');
console.log('║ OpenRouter FileParserPlugin - PDF URL Example (AI SDK) ║');
console.log('╚════════════════════════════════════════════════════════════════════════════╝');
console.log();
console.log('Sending PDF via public URL (Bitcoin whitepaper)');
console.log('URL: https://bitcoin.org/bitcoin.pdf');
console.log();

try {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need try/catch here btw if the entry crash it will simply crash right?

const model = openrouter(MODEL, { usage: { include: true } });

const result = await generateText({
model,
messages: [
{
role: 'user',
content: [
{
type: 'text',
text: 'What are the main points of this document? Provide a brief 2-3 sentence summary.',
},
{
type: 'file',
// Send PDF via public URL - AI SDK supports this directly
data: 'https://bitcoin.org/bitcoin.pdf',
mediaType: 'application/pdf',
},
],
},
],
});

console.log('✅ Request successful!');
console.log('\nSummary:');
console.log(result.text);
console.log('\nToken usage:');
// FIXME: result.usage should have proper type with promptTokens, completionTokens
// @ts-expect-error - usage is typed as LanguageModelV2Usage but should have token properties
console.log(`- Prompt tokens: ${result.usage.promptTokens}`);
// @ts-expect-error - usage is typed as LanguageModelV2Usage but should have token properties
console.log(`- Completion tokens: ${result.usage.completionTokens}`);
console.log(`- Total tokens: ${result.usage.totalTokens}`);

const usage = result.providerMetadata?.openrouter?.usage;
if (usage && typeof usage === 'object' && 'cost' in usage) {
const cost = usage.cost as number;
console.log(`\nCost: $${cost.toFixed(6)}`);
}

process.exit(0);
} catch (error) {
console.error('\n❌ Error:', error instanceof Error ? error.message : String(error));
process.exit(1);
}
}

main();