\
documents for structured metadata.Key moving parts:
pdf-parse, tesseract.js <h1 className="text-3xl font-bold leading-tight text-gray-900"> Medical Document Analysis </h1> </div> </header> <main> <div className="max-w-7xl mx-auto sm:px-6 lg:px-8"> <MedicalDocUploader />
documents. const handleFileUpload = useCallback(async (event: React.ChangeEvent<HTMLInputElement>) => { try { setIsUploading(true); setError(null); const file = event.target.files?.[0]; if (!file) return; // Extract text based on file type const text = file.type === 'application/pdf' ? await extractTextFromPDF(file) : await extractTextFromImage(file); // Analyze the text with Llama const analysis = await analyzeWithLlama(text, file.name); // Upload to Supabase Storage const { data: uploadData, error: uploadError } = await supabase.storage .from('medical-documents') .upload(analysis.renamed_file, file); if (uploadError) throw uploadError; // Store metadata in Supabase const { data: metaData, error: metaError } = await supabase .from('documents') .insert({ filename: file.name, renamed_file: analysis.renamed_file, file_url: uploadData.path, summary: analysis.summary, keywords: analysis.keywords, categories: analysis.categories, word_count: countWords(text), report_type: detectReportType(text), threshold_flags: analysis.threshold_flags, pubmed_refs: analysis.pubmed_refs, ai_notes: analysis.ai_notes, status: 'processed', version: 1 }) .select() .single(); if (metaError) throw metaError; setDocuments(prev => [...prev, metaData]); } catch (err) { setError(err instanceof Error ? err.message : 'An error occurred'); } finally { setIsUploading(false); } }, []);
const analyzeWithLlama = async (text: string, originalFilename: string) => { const prompt = `Analyze this medical document and provide a detailed analysis in the following format: 1. Summary: Provide a clear, plain-English summary 2. Keywords: Extract key medical terms and their values (if any) 3. Categories: Classify into these categories: ${VALID_CATEGORIES.join(", ")} 4. Filename: Suggest a clear, descriptive filename 5. Threshold Flags: Identify any abnormal values and mark as "high", "low", or "normal" 6. PubMed References: Suggest relevant PubMed articles (just article titles) 7. Additional Notes: Any important medical guidance or observations Document text: ${text} Please format your response exactly as follows: Summary: [summary] Keywords: [key:value pairs] Categories: [categories] Filename: [filename] Flags: [abnormal values] References: [article titles] Notes: [additional guidance]`;
documents row. // Prepare LLaMA prompt const prompt = `Analyze this medical document and provide a detailed analysis in the following format: 1. Summary: Provide a clear, plain-English summary 2. Keywords: Extract key medical terms and their values (if any) 3. Categories: Classify into these categories: ${VALID_CATEGORIES.join(", ")} 4. Filename: Suggest a clear, descriptive filename 5. Threshold Flags: Identify any abnormal values and mark as "high", "low", or "normal" 6. PubMed References: Suggest relevant PubMed articles (just article titles) 7. Additional Notes: Any important medical guidance or observations Document text: ${text} Please format your response exactly as follows: Summary: [summary] Keywords: [key:value pairs] Categories: [categories] Filename: [filename] Flags: [abnormal values] References: [article titles] Notes: [additional guidance]`
// Insert into Supabase const { data: insertData, error: insertError } = await supabase .from('documents') .insert(documentData) .select() .single()
Use JSONB where the structure can vary or expand over time.
-- documents table create table if not exists public.documents ( id bigint generated always as identity primary key, created_at timestamp with time zone default now() not null, user_id uuid null, filename text not null, renamed_file text not null, file_url text not null, summary text not null, keywords jsonb not null default '{}'::jsonb, categories text[] not null default '{}', word_count integer not null, report_type text not null, threshold_flags jsonb not null default '{}'::jsonb, pubmed_refs jsonb not null default '{}'::jsonb, ai_notes text not null default '', status text not null check (status in ('uploaded','processed','failed')), user_notes text null, version integer not null default 1 ); -- Optional: RLS policies for multi-tenant setups alter table public.documents enable row level security; -- Example policies (tune for your auth model) create policy "Allow read to authenticated users" on public.documents for select to authenticated using (true); create policy "Insert own documents" on public.documents for insert to authenticated with check (auth.uid() = user_id); create policy "Update own documents" on public.documents for update to authenticated using (auth.uid() = user_id);
medical-documents bucket.storage.objects table..env.local and do not commit it.Example variables to configure:
For the Edge Function, set:
SUPABASE_URLSUPABASE_ANON_KEY (or service role if needed)LLAMA_API_KEYsupabase functions deploy process-medical-pdf supabase functions list supabase functions serve process-medical-pdf --no-verify-jwt
Wire the function behind an HTTP trigger or call it from your app to process files already stored in the bucket.
version and status to support re-processing and migrations.documents and signed URLs for downloads.tesseract.js) can be CPU-heavy; pre-processing images helps (deskew, denoise, contrast).documents row.\ \ \



