### Setup Backend Environment and Install Dependencies Source: https://github.com/jamietso/tabular_review/blob/main/README.md Navigate to the server directory, create a Python virtual environment, activate it, and install backend dependencies. ```bash cd server python3 -m venv venv source venv/bin/activate pip install -r requirements.txt ``` -------------------------------- ### Docker Deployment Setup Source: https://github.com/jamietso/tabular_review/blob/main/README.md Copy the example environment file and edit it to include your Google Gemini API key for Docker deployment. ```bash cp .env.example .env # Edit .env and add your Google Gemini API key ``` -------------------------------- ### Run Backend Server Source: https://github.com/jamietso/tabular_review/blob/main/README.md Start the backend server (Docling) in one terminal after activating the virtual environment. ```bash cd server source venv/bin/activate python main.py ``` -------------------------------- ### Install Frontend Dependencies Source: https://github.com/jamietso/tabular_review/blob/main/README.md Install Node dependencies for the React frontend using pnpm. ```bash pnpm install ``` -------------------------------- ### Build and Run with Docker Compose Source: https://github.com/jamietso/tabular_review/blob/main/README.md Build the Docker images and start the application services using Docker Compose. ```bash docker-compose up --build ``` -------------------------------- ### Run Frontend Development Server Source: https://github.com/jamietso/tabular_review/blob/main/README.md Start the frontend development server in a separate terminal. ```bash pnpm dev ``` -------------------------------- ### Start Backend and Convert PDF using cURL Source: https://context7.com/jamietso/tabular_review/llms.txt Instructions to start the FastAPI backend server and a cURL command to convert a PDF file to Markdown. Ensure the backend is running before executing the cURL command. ```bash # Start the backend cd server python3 -m venv venv && source venv/bin/activate pip install -r requirements.txt python main.py # listens on http://0.0.0.0:8000 # Convert a PDF curl -X POST http://localhost:8000/convert \ -F "file=@contract.pdf" \ | jq . # Expected output: # { # "markdown": "# Service Agreement\n\nThis Agreement is entered into..." # } # Error response (HTTP 500): # { # "detail": "Unable to process file: ..." # } ``` -------------------------------- ### Clone Repository and Navigate Source: https://github.com/jamietso/tabular_review/blob/main/README.md Use these bash commands to clone the project repository and navigate into the project directory. ```bash git clone https://github.com/yourusername/tabular-review.git cd tabular-review ``` -------------------------------- ### Configure Frontend Environment Variables Source: https://github.com/jamietso/tabular_review/blob/main/README.md Create a .env.local file in the root directory and add your Google API Key. ```env VITE_GEMINI_API_KEY=your_google_api_key_here ``` -------------------------------- ### Generate Sample Files Source: https://context7.com/jamietso/tabular_review/llms.txt Generates a predefined set of `DocumentFile` objects representing fictional side letters for various institutional investors. ```APIDOC ## `generateSampleFiles()` ### Description Returns an array of eight `DocumentFile` objects, each representing a fictional side letter. These files are pre-configured with base64-encoded plain text content and appropriate mime types. ### Method `generateSampleFiles()` ### Response - **docs** (DocumentFile[]) - An array of `DocumentFile` objects. ``` -------------------------------- ### loadProject Source: https://context7.com/jamietso/tabular_review/llms.txt Opens a file picker to load a project from a JSON file. It reads and validates the file against the `SavedProject` schema and returns the parsed project object or null if the user cancels. ```APIDOC ## loadProject(): Promise ### Description Opens a file picker filtered to `.json` files, reads and validates the chosen file against the `SavedProject` schema (`version`, `name`, `columns`, `documents`, `results`), and returns the parsed object or `null` if the user cancelled. ### Returns - **Promise** - A promise that resolves to the parsed `SavedProject` object, or `null` if the user cancelled the file picker. ``` -------------------------------- ### Docker Compose Configuration Source: https://context7.com/jamietso/tabular_review/llms.txt Docker Compose file for setting up the full stack of the Tabular Review application. Requires setting up the `.env` file with API keys. ```yaml # docker-compose.yml — full stack # 1. cp .env.example .env → add VITE_GEMINI_API_KEY # 2. docker-compose up --build # Frontend: http://localhost:3000 # Backend: http://localhost:8000/docs (FastAPI Swagger UI) ``` -------------------------------- ### `generatePromptHelper(name, type, currentPrompt, modelId)` Source: https://context7.com/jamietso/tabular_review/llms.txt AI prompt generation service that creates an optimal extraction prompt based on column name and type, or optimizes an existing prompt. ```APIDOC ## `generatePromptHelper(name, type, currentPrompt, modelId)` ### Description Given a column name and type, asks Gemini to write an optimal extraction prompt. Used by the AddColumnMenu "AI Generate / Optimize" button. Falls back to a simple default string on error. ### Parameters - **name** (`string`) - The name of the column for which to generate or optimize a prompt. - **type** (`string`) - The data type of the column (e.g., 'text', 'number', 'date'). - **currentPrompt** (`string` | `undefined`) - An existing prompt draft to optimize. If `undefined`, a fresh prompt is generated. - **modelId** (`string`) - The identifier of the AI model to use (e.g., 'gemini-2.5-flash'). ### Returns - `Promise` - A promise that resolves to the generated or optimized prompt string. ### Example ```typescript import { generatePromptHelper } from './services/geminiService'; // Generate a fresh prompt const prompt = await generatePromptHelper( 'Governing Law', 'text', undefined, // no existing draft 'gemini-2.5-flash' ); console.log(prompt); // Example output: // "Identify the governing law clause of the agreement. State the jurisdiction // (e.g., 'New York', 'Cayman Islands'). If not found, return 'Not specified'." // Optimize an existing draft const improved = await generatePromptHelper( 'Governing Law', 'text', 'What is the governing law?', // existing draft 'gemini-2.5-pro-preview' ); // Returns a more precise, edge-case-aware version of the draft. ``` ``` -------------------------------- ### Generate Sample Document Files Source: https://context7.com/jamietso/tabular_review/llms.txt The `generateSampleFiles` function returns an array of `DocumentFile` objects representing pre-configured side letters. `SAMPLE_COLUMNS` provides corresponding extraction column configurations. ```typescript import { generateSampleFiles } from './utils/sampleData'; import { SAMPLE_COLUMNS } from './utils/sampleData'; const docs = generateSampleFiles(); console.log(docs.length); // 8 console.log(docs[0].name); // "Horizon_Growth_Fund_I_LP_Side_Letter_Metropolitan_College.pdf" console.log(docs[0].mimeType); // "text/plain" console.log(docs[6].name); // "Horizon_Growth_Fund_I_LP_Side_Letter_Nordic_Pension_Alliance.pdf" // Pair with pre-configured extraction columns console.log(SAMPLE_COLUMNS.map(c => c.name)); // ["Investor Entity", "Date", "Commitment Amount", "MFN", "Co-Investment Rights", "Power of Attorney"] // Load into app state directly // setDocuments(docs); // setColumns(SAMPLE_COLUMNS); ``` -------------------------------- ### Load Project from JSON Source: https://context7.com/jamietso/tabular_review/llms.txt Opens a file picker for .json files, reads and validates the chosen file against SavedProject schema. Returns parsed object or null if cancelled. ```typescript import { loadProject } from './utils/fileStorage'; try { const project = await loadProject(); if (project) { console.log(`Loaded "${project.name}" saved at ${project.savedAt}`); console.log(`${project.documents.length} documents, ${project.columns.length} columns`); // Restore state: // setProjectName(project.name); // setColumns(project.columns); // setDocuments(project.documents); // setResults(project.results); // setSelectedModel(project.selectedModel); } else { console.log('User cancelled file picker'); } } catch (err) { console.error('Invalid project file:', err); // throws: "Failed to load: Invalid project file structure" } ``` -------------------------------- ### Save, Load, and Import Column Libraries Source: https://context7.com/jamietso/tabular_review/llms.txt Use these functions to manage column template libraries. `loadColumnLibrary` reads from `localStorage`. `saveColumnLibrary` writes to `localStorage` and can trigger a file download. `importColumnLibrary` opens a file picker to merge templates from a JSON file. ```typescript import { loadColumnLibrary, saveColumnLibrary, importColumnLibrary } from './utils/fileStorage'; // Read existing library const library = loadColumnLibrary(); console.log(library.templates.length); // 0 on first run // Persist changes to localStorage (no file download) await saveColumnLibrary({ version: 1, templates: library.templates }, false); // Download as file using File System Access API (or fallback download) await saveColumnLibrary({ version: 1, templates: library.templates }, true); // Import and merge from a JSON file the user picks const merged = await importColumnLibrary(); if (merged) { console.log(`Library now has ${merged.templates.length} templates`); } ``` -------------------------------- ### Environment Variables for Docker Source: https://context7.com/jamietso/tabular_review/llms.txt Required environment variables for the Docker deployment, including Gemini API key and backend URL. Ensure `VITE_GEMINI_API_KEY` is set from Google MakerSuite. ```bash # .env (required values) VITE_GEMINI_API_KEY=AIza... # from https://makersuite.google.com/app/apikey VITE_API_URL=http://backend:8000 # Docker service name NODE_ENV=production PYTHONUNBUFFERED=1 ``` -------------------------------- ### saveProject Source: https://context7.com/jamietso/tabular_review/llms.txt Downloads the current project state as a JSON file. This includes the project name, columns, documents (with base64 content), extraction results, and the selected model. ```APIDOC ## saveProject(project: SavedProject): Promise ### Description Serializes the entire workspace state (project name, columns, documents with base64 content, extraction results, and selected model) to a `.tabular-project.json` file and triggers a browser download. ### Parameters - **project** (SavedProject) - Required - The project object to save. ### Returns - **Promise** - A promise that resolves to `true` if the save operation was successful. ``` -------------------------------- ### POST /convert — Convert PDF or DOCX to Markdown Source: https://context7.com/jamietso/tabular_review/llms.txt Accepts a multipart file upload, processes it using the Docling library, and returns the document content as a Markdown string. This endpoint is used by the frontend to convert uploaded files before storing them. ```APIDOC ## POST /convert — Convert PDF or DOCX to Markdown ### Description Accepts a multipart file upload, writes it to a temporary path, runs the Docling `DocumentConverter` pipeline (with MPS GPU acceleration on macOS), and returns the document as a Markdown string. The frontend sends every newly uploaded file here before storing it. ### Method POST ### Endpoint /convert ### Parameters #### Request Body - **file** (multipart/form-data) - Required - The PDF or DOCX file to convert. ### Request Example ```bash curl -X POST http://localhost:8000/convert \ -F "file=@contract.pdf" ``` ### Response #### Success Response (200) - **markdown** (string) - The converted document content in Markdown format. #### Response Example ```json { "markdown": "# Service Agreement\n\nThis Agreement is entered into..." } ``` #### Error Response (500) ```json { "detail": "Unable to process file: ..." } ``` ``` -------------------------------- ### Column Library Persistence Source: https://context7.com/jamietso/tabular_review/llms.txt Functions for loading, saving, and importing column libraries. `loadColumnLibrary` reads from localStorage synchronously. `saveColumnLibrary` writes to localStorage and can optionally trigger a file download. `importColumnLibrary` opens a file picker to import and merge libraries. ```APIDOC ## `loadColumnLibrary()` ### Description Reads the column library from `localStorage` synchronously. ### Method `loadColumnLibrary()` ### Response - **library** (object) - The loaded column library object. ## `saveColumnLibrary(library, toFile)` ### Description Saves the provided column library to `localStorage`. Optionally triggers a file download using the File System Access API or a fallback download mechanism. ### Method `saveColumnLibrary(library, toFile)` ### Parameters - **library** (object) - The column library object to save. - **toFile** (boolean) - If true, triggers a file download; otherwise, only saves to `localStorage`. ## `importColumnLibrary()` ### Description Opens a file picker for the user to select a JSON file. Parses the file, merges its contents with the existing library (deduplicating by template `id`), and persists the result. ### Method `importColumnLibrary()` ### Response - **mergedLibrary** (object | null) - The merged library object if import was successful, otherwise null. ``` -------------------------------- ### Save Project to JSON Source: https://context7.com/jamietso/tabular_review/llms.txt Serializes workspace state to a .tabular-project.json file and triggers browser download. Requires SavedProject type. ```typescript import { saveProject } from './utils/fileStorage'; import { SavedProject } from './types'; const project: SavedProject = { version: 1, name: 'PE Side Letters Review', savedAt: new Date().toISOString(), selectedModel: 'gemini-2.5-flash', columns: [ { id: 'col_1', name: 'Investor Entity', type: 'text', prompt: 'Identify the full legal name of the Investor entity.', status: 'completed' } ], documents: [ { id: 'doc_1', name: 'side_letter_metropolitan.pdf', type: 'application/pdf', size: 28000, content: 'base64...', mimeType: 'text/markdown' } ], results: { doc_1: { col_1: { value: 'Metropolitan College Foundation', confidence: 'High', quote: 'Metropolitan College Foundation (the "Investor")', page: 1, reasoning: 'Explicitly named as the Investor.' } } } }; const ok = await saveProject(project); // → browser downloads "pe_side_letters_review.tabular-project.json" console.log(ok); // true ``` -------------------------------- ### Generate AI Extraction Prompt Source: https://context7.com/jamietso/tabular_review/llms.txt Generates or optimizes extraction prompts for AI models based on column name and type. Falls back to a default prompt on error. ```typescript import { generatePromptHelper } from './services/geminiService'; // Generate a fresh prompt const prompt = await generatePromptHelper( 'Governing Law', 'text', undefined, // no existing draft 'gemini-2.5-flash' ); console.log(prompt); // "Identify the governing law clause of the agreement. State the jurisdiction // (e.g., 'New York', 'Cayman Islands'). If not found, return 'Not specified'." // Optimize an existing draft const improved = await generatePromptHelper( 'Governing Law', 'text', 'What is the governing law?', // existing draft 'gemini-2.5-pro-preview' ); // Returns a more precise, edge-case-aware version of the draft. ``` -------------------------------- ### Analyze Data with Chat Source: https://context7.com/jamietso/tabular_review/llms.txt Serializes extraction grid as CSV and uses it as system instruction for AI chat. Supports multi-turn conversations via history. Returns the assistant's text response. ```typescript import { analyzeDataWithChat } from './services/geminiService'; import { DocumentFile, Column, ExtractionResult } from './types'; const context = { documents: [ { id: 'd1', name: 'Side_Letter_Nordic.pdf', /* ... */ }, { id: 'd2', name: 'Side_Letter_Sovereign.pdf', /* ... */ }, ] as DocumentFile[], columns: [ { id: 'c1', name: 'Commitment Amount', /* ... */ }, { id: 'c2', name: 'MFN', /* ... */ }, ] as Column[], results: { d1: { c1: { value: '120000000', confidence: 'High', quote: '...', page: 1, reasoning: '...' }, c2: { value: 'true', /* ... */ } }, d2: { c1: { value: '150000000', confidence: 'High', quote: '...', page: 1, reasoning: '...' }, c2: { value: 'false', /* ... */ } }, } as ExtractionResult, }; // First turn (empty history) const answer = await analyzeDataWithChat( 'Which investor has the largest commitment?', context, [], // empty history for first message 'gemini-2.5-flash' ); console.log(answer); // "Based on the data, Sovereign Wealth Partners has the largest commitment at $150,000,000, // followed by Nordic Pension Alliance at $120,000,000." // Second turn (pass previous messages as history) const history = [ { role: 'user', parts: [{ text: 'Which investor has the largest commitment?' }] }, { role: 'model', parts: [{ text: answer }] }, ]; const followUp = await analyzeDataWithChat( 'Does that investor have an MFN clause?', context, history, 'gemini-2.5-flash' ); // "No. Sovereign Wealth Partners does not have an MFN clause — the MFN field is 'false'." ``` -------------------------------- ### processDocumentToMarkdown(file: File): Promise Source: https://context7.com/jamietso/tabular_review/llms.txt Sends a browser `File` object to the `/convert` endpoint and returns the resulting Markdown string. This function is called for every file the user uploads. ```APIDOC ## processDocumentToMarkdown(file: File): Promise — Send file to Docling backend ### Description Sends a browser `File` object to the `/convert` endpoint and returns the resulting Markdown string. Called for every file the user drops or selects before adding it to the document list. ### Usage ```typescript import { processDocumentToMarkdown } from './services/documentProcessor'; // Usage inside a file-input change handler async function handleUpload(fileList: FileList) { for (const file of Array.from(fileList)) { try { const markdown = await processDocumentToMarkdown(file); console.log(`Converted ${file.name}:\n`, markdown.slice(0, 200)); // Store as base64 for the rest of the app const contentBase64 = btoa(unescape(encodeURIComponent(markdown))); // → use contentBase64 as DocumentFile.content } catch (err) { console.error('Backend not running?', err); // throws: "Failed to convert contract.pdf. Is the backend server running?" } } } // Environment variable (set in .env.local or .env) // VITE_API_URL=http://localhost:8000 (default) // VITE_API_URL=http://backend:8000 (Docker) ``` ``` -------------------------------- ### Render VerificationSidebar for Document Preview Source: https://context7.com/jamietso/tabular_review/llms.txt Use VerificationSidebar in document preview mode when a document name is clicked. It displays the full document content with the relevant quote highlighted. ```tsx import { VerificationSidebar } from './components/VerificationSidebar'; // Document preview mode (click on a document name row) d.id === previewDocId) ?? null} column={null} isExpanded={true} // open document viewer immediately onExpand={setIsSidebarExpanded} onClose={() => { setSidebarMode('none'); setPreviewDocId(null); }}/> ``` -------------------------------- ### Open AddColumnMenu for Editing Column Source: https://context7.com/jamietso/tabular_review/llms.txt Render the AddColumnMenu for editing an existing column when both addColumnAnchor and editingColumnId are set. Includes save and delete callbacks. ```tsx import { AddColumnMenu } from './components/AddColumnMenu'; // Open for editing an existing column {addColumnAnchor && editingColumnId && ( c.id === editingColumnId)} onClose={() => { setAddColumnAnchor(null); setEditingColumnId(null); }} onSave={(col) => setColumns(prev => prev.map(c => c.id === editingColumnId ? { ...c, ...col } : c))} onDelete={() => { setColumns(prev => prev.filter(c => c.id !== editingColumnId)); setAddColumnAnchor(null); }}/> )} ``` -------------------------------- ### Open AddColumnMenu for New Column Source: https://context7.com/jamietso/tabular_review/llms.txt Conditionally render the AddColumnMenu when addColumnAnchor is set to create a new column. The 'onSave' callback adds the new column to the state. ```tsx import { AddColumnMenu } from './components/AddColumnMenu'; // Open for a new column {addColumnAnchor && ( setAddColumnAnchor(null)} onSave={(col) => { // col: { name: string; type: ColumnType; prompt: string } setColumns(prev => [...prev, { id: `col_${Date.now()}`, ...col, status: 'idle', width: 250 }]); setAddColumnAnchor(null); }} onOpenLibrary={() => { setAddColumnAnchor(null); setIsLibraryOpen(true); }}/> )} ``` -------------------------------- ### Template CRUD Operations Source: https://context7.com/jamietso/tabular_review/llms.txt These helper functions facilitate Create, Read, Update, and Delete operations on templates stored in `localStorage`. `addTemplateToLibrary` automatically generates a unique ID and creation timestamp. ```typescript import { addTemplateToLibrary, removeTemplateFromLibrary, updateTemplateInLibrary, getTemplateCategories } from './utils/fileStorage'; // Add a new template const tpl = addTemplateToLibrary({ name: 'Governing Law', type: 'text', prompt: 'Identify the governing law clause and return the jurisdiction name.', category: 'Legal' }); console.log(tpl.id); // e.g. "tpl_1720000000000_a3f7k" console.log(tpl.createdAt); // ISO timestamp // Update it updateTemplateInLibrary(tpl.id, { prompt: 'Return only the jurisdiction, e.g. "Cayman Islands".' }); // List categories console.log(getTemplateCategories()); // ["Legal"] // Delete it removeTemplateFromLibrary(tpl.id); ``` -------------------------------- ### Process Document to Markdown in TypeScript Source: https://context7.com/jamietso/tabular_review/llms.txt A TypeScript function to send a browser File object to the backend's /convert endpoint and retrieve the Markdown string. Handles potential errors if the backend is not running. Environment variables configure the API URL. ```typescript import { processDocumentToMarkdown } from './services/documentProcessor'; // Usage inside a file-input change handler async function handleUpload(fileList: FileList) { for (const file of Array.from(fileList)) { try { const markdown = await processDocumentToMarkdown(file); console.log(`Converted ${file.name}:\n`, markdown.slice(0, 200)); // Store as base64 for the rest of the app const contentBase64 = btoa(unescape(encodeURIComponent(markdown))); // → use contentBase64 as DocumentFile.content } catch (err) { console.error('Backend not running?', err); // throws: "Failed to convert contract.pdf. Is the backend server running?" } } } // Environment variable (set in .env.local or .env) // VITE_API_URL=http://localhost:8000 (default) // VITE_API_URL=http://backend:8000 (Docker) ``` -------------------------------- ### Render DataGrid Component Source: https://context7.com/jamietso/tabular_review/llms.txt Use the DataGrid component to display documents and extraction results. Configure column resizing, cell selection, and file uploads. ```tsx import { DataGrid } from './components/DataGrid'; setAddColumnAnchor(rect)} onEditColumn={(colId, rect) => { setEditingColumnId(colId); setAddColumnAnchor(rect); }} onColumnResize={(colId, w) => setColumns(prev => prev.map(c => c.id === colId ? { ...c, width: w } : c))} onCellClick={(docId, colId) => { setSelectedCell({ docId, colId }); setSidebarMode('verify'); }} onDocClick={(docId) => { setPreviewDocId(docId); setSidebarMode('verify'); }} onRemoveDoc={(docId) => setDocuments(prev => prev.filter(d => d.id !== docId))} onDropFiles={(files) => processUploadedFiles(files)} onToggleDocSelection={(docId) => setSelectedDocIds(prev => { const s = new Set(prev); s.has(docId) ? s.delete(docId) : s.add(docId); return s; })} onToggleAllDocSelection={() => setSelectedDocIds(prev => prev.size === documents.length ? new Set() : new Set(documents.map(d => d.id)))}/> ``` -------------------------------- ### Template CRUD Operations Source: https://context7.com/jamietso/tabular_review/llms.txt Functions for adding, removing, and updating templates in the column library. These functions load the library, mutate the templates array, and save it back. ```APIDOC ## `addTemplateToLibrary(template)` ### Description Adds a new template to the column library. Automatically generates a unique `id` and `createdAt` timestamp for the new template. ### Method `addTemplateToLibrary(template)` ### Parameters - **template** (object) - The template object to add. Should contain properties like `name`, `type`, `prompt`, and `category`. ### Response - **newTemplate** (object) - The newly added template object, including its generated `id` and `createdAt`. ## `updateTemplateInLibrary(templateId, updates)` ### Description Updates an existing template in the column library identified by its `templateId` with the provided `updates`. ### Method `updateTemplateInLibrary(templateId, updates)` ### Parameters - **templateId** (string) - The ID of the template to update. - **updates** (object) - An object containing the fields to update for the template. ## `removeTemplateFromLibrary(templateId)` ### Description Removes a template from the column library based on its `templateId`. ### Method `removeTemplateFromLibrary(templateId)` ### Parameters - **templateId** (string) - The ID of the template to remove. ## `getTemplateCategories()` ### Description Retrieves a list of all unique categories present in the current template library. ### Method `getTemplateCategories()` ### Response - **categories** (string[]) - An array of strings, where each string is a template category. ``` -------------------------------- ### analyzeDataWithChat Source: https://context7.com/jamietso/tabular_review/llms.txt Performs a chat-based analysis over extracted data. It serializes the extraction grid into a CSV string and uses it as system instructions for an AI chat session. Supports multi-turn conversations and returns the assistant's text response. ```APIDOC ## analyzeDataWithChat(message, context, history, modelId): Promise ### Description Serializes the full extraction grid (all documents × all columns) as an in-memory CSV string and injects it as the system instruction for a `ai.chats` session. Supports multi-turn conversation via the `history` parameter (formatted as Gemini content parts). Returns the assistant's text response. ### Parameters - **message** (string) - Required - The user's message or query. - **context** (object) - Required - The extracted data context, including documents, columns, and results. - **history** (Array) - Optional - An array of previous messages for multi-turn conversation. - **modelId** (string) - Required - The ID of the AI model to use for analysis. ### Returns - **Promise** - A promise that resolves to the assistant's text response. ``` -------------------------------- ### `extractColumnData(doc, column, modelId)` Source: https://context7.com/jamietso/tabular_review/llms.txt AI-powered field extraction that decodes document content, builds a structured prompt, and calls an AI model to generate content with a JSON response schema. It includes retry logic for rate-limit errors. ```APIDOC ## `extractColumnData(doc, column, modelId)` ### Description AI-powered field extraction that decodes the base64 document content, builds a structured prompt from the column definition and type-specific format instructions, calls `ai.models.generateContent` with a JSON response schema enforcing `value`, `confidence`, `quote`, `page`, and `reasoning`, and returns a typed `ExtractionCell`. Wraps every call in `withRetry` (5 attempts, exponential backoff with jitter) to handle Gemini rate-limit errors (HTTP 429 / `RESOURCE_EXHAUSTED`). ### Parameters - **doc** (`DocumentFile`) - The document object containing content to extract from. - **column** (`Column`) - The column definition specifying the data to extract. - **modelId** (`string`) - The identifier of the AI model to use (e.g., 'gemini-2.5-flash'). ### Returns - `Promise` - A promise that resolves to an `ExtractionCell` object containing the extracted value, confidence, quote, page number, and reasoning. ### Example ```typescript import { extractColumnData } from './services/geminiService'; import { DocumentFile, Column } from './types'; const doc: DocumentFile = { id: 'doc_001', name: 'Side_Letter_Metropolitan.pdf', type: 'application/pdf', size: 28000, content: btoa('...markdown text of the document...'), mimeType: 'text/markdown', }; const column: Column = { id: 'col_commitment', name: 'Commitment Amount', type: 'number', // forces "Return a clean number string" format instruction prompt: 'Return as a number the capital commitment of the investor as shown in the side letter.', status: 'idle', }; try { const cell = await extractColumnData(doc, column, 'gemini-2.5-flash'); console.log(cell); // Expected output structure: // { // value: "25000000", // confidence: "High", // quote: "will become a Limited Partner with a capital commitment of $25,000,000", // page: 1, // reasoning: "The commitment amount is explicitly stated in the opening paragraph.", // status: "needs_review" // } } catch (err) { // Thrown after 5 retries, or for non-rate-limit errors console.error('Extraction failed:', err); } ``` ### Column Types and Format Instructions - `'date'` → "Format the date as YYYY-MM-DD." - `'boolean'` → "Return 'true' or 'false' as the value string." - `'number'` → "Return a clean number string, removing currency symbols if needed." - `'list'` → "Return the items as a comma-separated string." - `'text'` → "Keep the text concise." ``` -------------------------------- ### Chat Interface Component Source: https://context7.com/jamietso/tabular_review/llms.txt Renders a chat UI for interacting with the data analysis. It calls `analyzeDataWithChat` with the full extraction context and message history. Supports Enter key to submit. ```tsx import { ChatInterface } from './components/ChatInterface'; // Render in the right sidebar when sidebarMode === 'chat' {sidebarMode === 'chat' && ( setSidebarMode('none')} /> )} // Example questions the analyst handles well: // "Which contract has the most favourable MFN clause?" // "List all investors whose commitment exceeds $100M." // "Summarize the co-investment rights for the Nordic Pension Alliance." // "Which side letters do NOT have a power-of-attorney limitation clause?" ``` -------------------------------- ### Render VerificationSidebar for Cell Inspection Source: https://context7.com/jamietso/tabular_review/llms.txt Use VerificationSidebar in cell inspection mode when a cell is selected. It displays cell details, document context, and allows verification. The 'onVerify' callback updates the status. ```tsx import { VerificationSidebar } from './components/VerificationSidebar'; d.id === selectedCell.docId) ?? null} column={columns.find(c => c.id === selectedCell.colId) ?? null} isExpanded={isSidebarExpanded} onExpand={setIsSidebarExpanded} // set true to show the document viewer panel onClose={() => { setSidebarMode('none'); setSelectedCell(null); }} onVerify={() => { // Mark cell as verified setResults(prev => ({ ...prev, [selectedCell.docId]: { ...prev[selectedCell.docId], [selectedCell.colId]: { ...prev[selectedCell.docId][selectedCell.colId]!, status: 'verified' } } })); }}/> ``` -------------------------------- ### Extract Column Data with Gemini Source: https://context7.com/jamietso/tabular_review/llms.txt Extracts data from a document column using AI, with built-in retry logic for rate limits. Ensure document content is base64 encoded and column definitions are accurate. ```typescript import { extractColumnData } from './services/geminiService'; import { DocumentFile, Column } from './types'; const doc: DocumentFile = { id: 'doc_001', name: 'Side_Letter_Metropolitan.pdf', type: 'application/pdf', size: 28000, content: btoa('...markdown text of the document...'), mimeType: 'text/markdown', }; const column: Column = { id: 'col_commitment', name: 'Commitment Amount', type: 'number', // forces "Return a clean number string" format instruction prompt: 'Return as a number the capital commitment of the investor as shown in the side letter.', status: 'idle', }; try { const cell = await extractColumnData(doc, column, 'gemini-2.5-flash'); console.log(cell); // { // value: "25000000", // confidence: "High", // quote: "will become a Limited Partner with a capital commitment of $25,000,000", // page: 1, // reasoning: "The commitment amount is explicitly stated in the opening paragraph.", // status: "needs_review" // } } catch (err) { // Thrown after 5 retries, or for non-rate-limit errors console.error('Extraction failed:', err); } // Column types and their format instructions: // 'date' → "Format the date as YYYY-MM-DD." // 'boolean' → "Return 'true' or 'false' as the value string." // 'number' → "Return a clean number string, removing currency symbols if needed." // 'list' → "Return the items as a comma-separated string." // 'text' → "Keep the text concise." ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.