diff --git a/typescript/ai-sdk-v5/src/plugin-file-parser/README.md b/typescript/ai-sdk-v5/src/plugin-file-parser/README.md new file mode 100644 index 0000000..c1eb217 --- /dev/null +++ b/typescript/ai-sdk-v5/src/plugin-file-parser/README.md @@ -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. diff --git a/typescript/ai-sdk-v5/src/plugin-file-parser/file-parser-all-sizes.ts b/typescript/ai-sdk-v5/src/plugin-file-parser/file-parser-all-sizes.ts new file mode 100644 index 0000000..a2c93ec --- /dev/null +++ b/typescript/ai-sdk-v5/src/plugin-file-parser/file-parser-all-sizes.ts @@ -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 { + 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; + 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); +}); diff --git a/typescript/ai-sdk-v5/src/plugin-file-parser/file-parser-pdf-url.ts b/typescript/ai-sdk-v5/src/plugin-file-parser/file-parser-pdf-url.ts new file mode 100644 index 0000000..12fd17d --- /dev/null +++ b/typescript/ai-sdk-v5/src/plugin-file-parser/file-parser-pdf-url.ts @@ -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 { + 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();