JavaScript Examples
Complete JavaScript examples for Extract integration.
Basic Invoice Processing
const fs = require('fs');
const FormData = require('form-data');
const BASE_URL = 'http://localhost/api/v1';
const API_KEY = 'your_api_key';
async function processInvoice(filepath, companyName = null) {
const form = new FormData();
form.append('file', fs.createReadStream(filepath));
form.append('template_id', 'detailed_invoice');
if (companyName) {
form.append('context', JSON.stringify({ company_name: companyName }));
}
const response = await fetch(`${BASE_URL}/extract/process`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
...form.getHeaders()
},
body: form
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Processing failed');
}
return response.json();
}
// Usage
const result = await processInvoice('invoice.pdf', 'Acme Corp');
console.log(`Invoice Number: ${result.result.parsed_data.invoice_number}`);
console.log(`Total: $${result.result.parsed_data.total}`);
console.log(`Cost: $${result.usage.cost.toFixed(4)}`);
Browser-Based Upload
async function processDocument(file, templateId, context = {}) {
const formData = new FormData();
formData.append('file', file);
formData.append('template_id', templateId);
if (Object.keys(context).length > 0) {
formData.append('context', JSON.stringify(context));
}
const response = await fetch(`${BASE_URL}/extract/process`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`
},
body: formData
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Processing failed');
}
return response.json();
}
// Usage with file input
document.getElementById('fileInput').addEventListener('change', async (e) => {
const file = e.target.files[0];
if (!file) return;
try {
const result = await processDocument(file, 'simple_receipt');
console.log('Extracted data:', result.result.parsed_data);
// Display warnings if any
if (result.result.validation_warnings.length > 0) {
console.warn('Warnings:', result.result.validation_warnings);
}
} catch (error) {
console.error('Processing failed:', error.message);
}
});
Extract Client Class
class ExtractClient {
constructor(baseUrl, apiKey) {
this.baseUrl = baseUrl.replace(/\/$/, '');
this.apiKey = apiKey;
}
async #request(method, endpoint, options = {}) {
const url = `${this.baseUrl}${endpoint}`;
const headers = {
'Authorization': `Bearer ${this.apiKey}`,
...options.headers
};
const response = await fetch(url, {
method,
headers,
body: options.body
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || `Request failed: ${response.status}`);
}
return response.json();
}
async processDocument(file, templateId, context = {}) {
const formData = new FormData();
formData.append('file', file);
formData.append('template_id', templateId);
if (Object.keys(context).length > 0) {
formData.append('context', JSON.stringify(context));
}
return this.#request('POST', '/extract/process', { body: formData });
}
async listJobs(options = {}) {
const params = new URLSearchParams();
if (options.status) params.append('status', options.status);
if (options.limit) params.append('limit', options.limit);
if (options.offset) params.append('offset', options.offset);
const query = params.toString() ? `?${params}` : '';
return this.#request('GET', `/extract/jobs${query}`);
}
async getJob(jobId) {
return this.#request('GET', `/extract/jobs/${jobId}`);
}
async listTemplates() {
return this.#request('GET', '/extract/templates');
}
async getTemplate(templateId) {
return this.#request('GET', `/extract/templates/${templateId}`);
}
async createTemplate(template) {
return this.#request('POST', '/extract/templates', {
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(template)
});
}
async updateTemplate(templateId, updates) {
return this.#request('PUT', `/extract/templates/${templateId}`, {
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(updates)
});
}
async deleteTemplate(templateId) {
return this.#request('DELETE', `/extract/templates/${templateId}`);
}
async resetDefaults() {
return this.#request('POST', '/extract/templates/reset-defaults');
}
async getSettings() {
return this.#request('GET', '/extract/settings');
}
async updateSettings(settings) {
return this.#request('PUT', '/extract/settings', {
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(settings)
});
}
async listModels() {
return this.#request('GET', '/extract/models');
}
}
// Usage
const client = new ExtractClient('http://localhost/api/v1', 'your_api_key');
// Process a document
const result = await client.processDocument(file, 'detailed_invoice', {
company_name: 'Acme Corp'
});
// List recent jobs
const jobs = await client.listJobs({ status: 'completed', limit: 10 });
// Get available templates
const templates = await client.listTemplates();
Custom Template Creation
async function createPurchaseOrderTemplate(client) {
const template = {
id: 'purchase_order',
system_prompt: `You are a document extraction specialist.
Extract data accurately from purchase orders.
Return valid JSON matching the requested structure.
Use null for fields that are not visible in the document.`,
user_prompt: `Extract the following from this purchase order for {company_name}:
- PO Number
- Vendor name and address
- Order date and delivery date
- Line items (part number, description, quantity, unit price)
- Shipping terms
- Total amount`,
context_schema: {
company_name: {
type: 'string',
description: 'Name of the company placing the order'
}
},
output_schema: {
type: 'object',
required: ['po_number', 'vendor', 'line_items', 'total_amount'],
properties: {
po_number: { type: 'string' },
vendor: {
type: 'object',
properties: {
name: { type: 'string' },
address: { type: 'string' }
}
},
order_date: { type: 'string', format: 'date' },
delivery_date: { type: 'string', format: 'date' },
line_items: {
type: 'array',
items: {
type: 'object',
properties: {
part_number: { type: 'string' },
description: { type: 'string' },
quantity: { type: 'number' },
unit_price: { type: 'number' },
line_total: { type: 'number' }
}
}
},
shipping_terms: { type: 'string' },
total_amount: { type: 'number' }
}
}
};
return client.createTemplate(template);
}
// Usage
const template = await createPurchaseOrderTemplate(client);
console.log(`Created template: ${template.id}`);
Batch Processing with Concurrency Control
async function batchProcessInvoices(client, files, concurrency = 3) {
const results = [];
const queue = [...files];
const inProgress = new Set();
async function processNext() {
if (queue.length === 0) return;
const file = queue.shift();
inProgress.add(file.name);
try {
const result = await client.processDocument(file, 'detailed_invoice');
results.push({
filename: file.name,
success: true,
data: result.result.parsed_data,
cost: result.usage.cost
});
console.log(`✓ ${file.name}`);
} catch (error) {
results.push({
filename: file.name,
success: false,
error: error.message
});
console.log(`✗ ${file.name}: ${error.message}`);
} finally {
inProgress.delete(file.name);
}
await processNext();
}
// Start concurrent workers
const workers = Array(Math.min(concurrency, files.length))
.fill()
.map(() => processNext());
await Promise.all(workers);
return results;
}
// Usage
const fileInput = document.getElementById('multiFileInput');
const files = Array.from(fileInput.files).filter(f =>
f.type === 'application/pdf' || f.type.startsWith('image/')
);
const results = await batchProcessInvoices(client, files, 3);
// Summary
const successful = results.filter(r => r.success);
const failed = results.filter(r => !r.success);
const totalCost = successful.reduce((sum, r) => sum + r.cost, 0);
console.log(`Processed: ${results.length} files`);
console.log(`Successful: ${successful.length}`);
console.log(`Failed: ${failed.length}`);
console.log(`Total cost: $${totalCost.toFixed(4)}`);
React Hook
import { useState, useCallback } from 'react';
function useExtract(baseUrl, apiKey) {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [result, setResult] = useState(null);
const processDocument = useCallback(async (file, templateId, context = {}) => {
setLoading(true);
setError(null);
setResult(null);
try {
const formData = new FormData();
formData.append('file', file);
formData.append('template_id', templateId);
if (Object.keys(context).length > 0) {
formData.append('context', JSON.stringify(context));
}
const response = await fetch(`${baseUrl}/extract/process`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`
},
body: formData
});
if (!response.ok) {
const err = await response.json();
throw new Error(err.detail || 'Processing failed');
}
const data = await response.json();
setResult(data);
return data;
} catch (err) {
setError(err.message);
throw err;
} finally {
setLoading(false);
}
}, [baseUrl, apiKey]);
return { processDocument, loading, error, result };
}
// Usage in component
function InvoiceUploader() {
const { processDocument, loading, error, result } = useExtract(
'http://localhost/api/v1',
'your_api_key'
);
const handleUpload = async (e) => {
const file = e.target.files[0];
if (!file) return;
try {
await processDocument(file, 'detailed_invoice', {
company_name: 'Acme Corp'
});
} catch (err) {
console.error('Upload failed:', err);
}
};
return (
<div>
<input type="file" onChange={handleUpload} disabled={loading} />
{loading && <p>Processing...</p>}
{error && <p className="error">{error}</p>}
{result && (
<div>
<h3>Extracted Data</h3>
<pre>{JSON.stringify(result.result.parsed_data, null, 2)}</pre>
<p>Cost: ${result.usage.cost.toFixed(4)}</p>
</div>
)}
</div>
);
}
TypeScript Types
interface ExtractResult {
job_id: string;
status: 'pending' | 'processing' | 'completed' | 'failed';
file_metadata: {
original_name: string;
file_type: string;
file_size: number;
page_count: number;
};
template_id: string;
model_used: string;
result: {
parsed_data: Record<string, any>;
validation_errors: string[];
validation_warnings: string[];
};
usage: {
prompt_tokens: number;
completion_tokens: number;
total_tokens: number;
cost: number;
};
created_at: string;
completed_at: string;
}
interface ExtractTemplate {
id: string;
system_prompt: string;
user_prompt: string;
context_schema?: Record<string, any>;
output_schema?: Record<string, any>;
vision_model?: string;
is_default?: boolean;
is_active?: boolean;
}
interface ExtractJob {
job_id: string;
status: 'pending' | 'processing' | 'completed' | 'failed';
file_metadata: {
original_name: string;
file_type: string;
file_size: number;
};
template_id: string;
created_at: string;
}