Extracts patient information, test results, and reference ranges from lab reports.
A lab results report is a clinical document issued by a healthcare laboratory that contains a patient's demographic information, specimen details, individual test results with values and reference ranges, and the report status and timestamps. This template takes in laboratory results, such as blood tests, and outputs markdown (.md) capturing the lab report's full text and layout, and JSON (.json) with structured patient and test data fields per the extraction schema by using Extend's Parse, Extract primitives.
Parse lab results
blockOptions.figures.enabledtruechangedblockOptions.tables.agentic.enabledtruechangedblockOptions.tables.tableHeaderContinuationEnabledtruechangedblockOptions.text.agentic.enabledtruechangedblockOptions.text.signatureDetectionEnabledtruechangedchunkingStrategy.type"page"changedYou can learn more about Parse configuration in Extend's Parse documentation.
Extract lab results extractor fields
schemacustom schema — 11 fieldschangedadditionalPropertiesfalsechangedproperties.age.description"The age of the patient at the time of the test. May be explicitly stated or derived from the date of birth and collection date."changedproperties.age.type["number","null"]changedproperties.date_of_birth.description"The patient's date of birth. This is used for identification and to interpret age-dependent reference ranges. May be labeled as 'DOB', 'Date of Birth', or simi…changedproperties.date_of_birth.extend:type"date"changedproperties.date_of_birth.type["string","null"]changedproperties.fasting_status.description"Indicates whether the patient was fasting at the time of specimen collection. May be labeled as 'Fasting', 'Fasting Status', or similar, with values such as 'Y…changedproperties.fasting_status.type["string","null"]changedproperties.gender.description"The gender or sex of the patient. May be labeled as 'Gender', 'Sex', or indicated by 'M', 'F', or other descriptors."changedproperties.gender.type["string","null"]changedproperties.hospital_address.description"The address of the hospital, clinic, or healthcare facility associated with the test. May include street, city, state, and postal code."changedproperties.hospital_address.type["string","null"]changedproperties.hospital_name.description"The name of the hospital, clinic, or healthcare facility associated with the test. May be labeled as 'Facility', 'Client', or appear in the client information …changedproperties.hospital_name.type["string","null"]changedproperties.hospital_phone.description"The contact phone number for the hospital or healthcare facility, if provided."changedproperties.hospital_phone.type["string","null"]changedproperties.lab_address.description"The address of the laboratory that performed the analysis, if provided."changedproperties.lab_address.type["string","null"]changedproperties.lab_clia_number.description"The CLIA (Clinical Laboratory Improvement Amendments) number or other accreditation identifier for the laboratory, if present."changedproperties.lab_clia_number.type["string","null"]changedproperties.lab_director.description"The name of the laboratory director or responsible scientist, if listed."changedproperties.lab_director.type["string","null"]changedproperties.lab_name.description"The name of the laboratory that performed the analysis. May be labeled as 'Lab', 'Performing Site', or appear in the laboratory information section."changedproperties.lab_name.type["string","null"]changedproperties.lab_results.description"A list of all individual laboratory test results included in this report. Each entry represents a single analyte or measurement, with its value, reference rang…changedproperties.lab_results.items.additionalPropertiesfalsechangedproperties.lab_results.items.properties.flag.description"An indicator of whether the result is outside the reference range or otherwise notable. Common values include 'H' (high), 'L' (low), 'Normal', or may be blank …changedproperties.lab_results.items.properties.flag.type["string","null"]changedproperties.lab_results.items.properties.interpretation.description"Any additional interpretation, comments, or notes provided for this result, such as 'Fasting reference interval', 'calc', or other remarks."changedproperties.lab_results.items.properties.interpretation.type["string","null"]changedproperties.lab_results.items.properties.lab_code.description"A code or abbreviation for the laboratory or testing site associated with this result, if provided."changedproperties.lab_results.items.properties.lab_code.type["string","null"]changedproperties.lab_results.items.properties.panel_name.description"The name of the test panel or group this result belongs to, if applicable (e.g., 'COMPREHENSIVE METABOLIC PANEL', 'CBC')."changedproperties.lab_results.items.properties.panel_name.type["string","null"]changedproperties.lab_results.items.properties.reference_range.description"The normal or expected range for this test result, as provided by the laboratory. May include lower and upper limits, or descriptive text (e.g., '65-99 mg/dL',…changedproperties.lab_results.items.properties.reference_range.type["string","null"]changedproperties.lab_results.items.properties.result_value.description"The reported value or measurement for this test. May be numeric or text (e.g., 'NOT APPLICABLE', 'Pending')."changedproperties.lab_results.items.properties.result_value.type["string","null"]changedproperties.lab_results.items.properties.test_name.description"The name of the laboratory test or analyte measured. May include abbreviations, full names, or panel names (e.g., 'GLUCOSE', 'HEMOGLOBIN', 'CBC')."changedproperties.lab_results.items.properties.test_name.type["string","null"]changedproperties.lab_results.items.properties.units.description"The measurement units for the result value, such as 'mg/dL', 'mmol/L', '%', or other units. May be omitted for qualitative results."changedproperties.lab_results.items.properties.units.type["string","null"]changedproperties.lab_results.items.required["flag","units","lab_code","test_name","panel_name","result_value","interpretation","reference_range"]changedproperties.lab_results.items.type"object"changedproperties.lab_results.type"array"changedproperties.patient_id.description"A unique identifier assigned to the patient by the healthcare provider or laboratory. This may be labeled as 'Patient ID', 'MRN', or similar, and is used to di…changedproperties.patient_id.type["string","null"]changedproperties.patient_name.description"The full name of the patient whose blood test results are reported. This is the individual for whom the laboratory analysis was performed. May appear with labe…changedproperties.patient_name.type["string","null"]changedproperties.patient_phone.description"The contact phone number for the patient, if provided. May appear in the patient information section."changedproperties.patient_phone.type["string","null"]changedproperties.report_date.description"The date when the laboratory report was generated or finalized. May be labeled as 'Report Date', 'Reported', or similar."changedproperties.report_date.extend:type"date"changedproperties.report_date.type["string","null"]changedproperties.specimen_collection_date.description"The date when the specimen was collected from the patient. May be labeled as 'Collected', 'Collection Date', or similar."changedproperties.specimen_collection_date.extend:type"date"changedproperties.specimen_collection_date.type["string","null"]changedproperties.specimen_id.description"A unique identifier for the specimen or sample tested. May be labeled as 'Specimen', 'Sample ID', or similar."changedproperties.specimen_id.type["string","null"]changedproperties.specimen_received_date.description"The date when the laboratory received the specimen. May be labeled as 'Received', 'Specimen Received', or similar."changedproperties.specimen_received_date.extend:type"date"changedproperties.specimen_received_date.type["string","null"]changedrequired["age","gender","lab_name","patient_id","lab_address","lab_results","report_date","specimen_id","lab_director","patient_name","date_of_birth","hospital_name","p…changedtype"object"changedYou can learn more about Extract configuration in Extend's Extract documentation.
{
"name": "Lab Results Extractor Processing Pipeline",
"steps": [
{
"name": "startTrigger1",
"type": "TRIGGER",
"next": [
{
"step": "parse1"
}
]
},
{
"name": "parse1",
"type": "PARSE",
"config": {
"parseConfig": {
"blockOptions": {
"text": {
"agentic": {
"enabled": true
},
"signatureDetectionEnabled": true
},
"tables": {
"agentic": {
"enabled": true
},
"tableHeaderContinuationEnabled": true
},
"figures": {
"enabled": true
}
},
"chunkingStrategy": {
"type": "page",
"options": {}
}
}
},
"next": [
{
"step": "extraction2"
}
]
},
{
"name": "extraction2",
"type": "EXTRACT",
"config": {
"extractorConfig": {
"schema": {
"type": "object",
"properties": {
"age": {
"type": [
"integer",
"null"
],
"description": "Patient's age in years"
},
"gender": {
"type": [
"string",
"null"
],
"description": "Patient gender (M/F)"
},
"patient_id": {
"type": [
"string",
"null"
],
"description": "Unique patient identifier"
},
"lab_results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"test_name": {
"type": [
"string",
"null"
],
"description": "Name of the lab test"
},
"result_value": {
"type": [
"string",
"null"
],
"description": "Numerical or descriptive result value"
},
"reference_range": {
"type": [
"string",
"null"
],
"description": "Normal reference range for the test"
}
}
},
"description": "Array of individual lab test results"
},
"specimen_id": {
"type": [
"string",
"null"
],
"description": "Unique specimen identifier"
},
"patient_name": {
"type": [
"string",
"null"
],
"description": "Full name of the patient"
},
"date_of_birth": {
"type": [
"string",
"null"
],
"description": "Patient's date of birth in MM/DD/YYYY format"
},
"report_status": {
"type": [
"string",
"null"
],
"description": "Status of the report (e.g., Final, Preliminary)"
},
"fasting_status": {
"type": [
"string",
"null"
],
"description": "Whether patient was fasting (Y/N)"
},
"report_datetime": {
"type": [
"string",
"null"
],
"description": "Date and time report was finalized in MM/DD/YYYY / HH:MM format"
},
"collection_datetime": {
"type": [
"string",
"null"
],
"description": "Date and time specimen was collected in MM/DD/YYYY / HH:MM TZ format"
}
}
}
}
}
}
]
}# Lab Results Extractor — Extend AI Skill
## What this pipeline does
This pipeline ingests clinical laboratory result PDFs from diagnostic facilities (Quest Diagnostics, LabCorp, etc.) and extracts structured patient demographics, specimen details, and comprehensive test results with reference ranges into JSON. It parses complex tabular layouts and handwritten annotations using agentic OCR, then extracts 20+ fields including patient name, DOB, age, gender, specimen ID, report status, fasting status, and a fully populated array of individual lab tests (test name, result value, reference range, units, flags, panel grouping, and interpretation notes).
## When to use this
- **EHR integration**: Import lab results directly into electronic health record systems, eliminating manual data entry and transcription errors.
- **Clinical decision support**: Feed structured lab data into diagnostic algorithms that flag abnormal results or suggest follow-up tests.
- **Patient engagement portals**: Automatically populate patient-facing web interfaces with lab results, historical trends, and actionable insights.
- **Compliance & audit**: Extract CLIA-required fields (lab director, CLIA number, facility accreditation) for regulatory reporting and audit trails.
- **Batch processing**: Handle high-volume lab result ingestion from multiple facilities in a single workflow, with consistent schema across all sources.
## Processor pipeline
### Step 1: Parse (agentic OCR with table & signature detection)
- **Processor**: `parseRuns.createAndPoll()`
- **Purpose**: Convert PDF pages into structured markdown, preserving table rows, column alignment, and metadata.
- **Key config**:
- `blockOptions.text.agentic.enabled: true` — enables intelligent text recognition for variable layouts, handwriting, and anomalies common in lab reports.
- `blockOptions.tables.agentic.enabled: true` — instructs the parser to recognize table structure even if columns shift between pages or headers repeat.
- `blockOptions.tables.tableHeaderContinuationEnabled: true` — critical for multi-page reports where lab results tables span pages; ensures column headers are inferred for continuation sections.
- `blockOptions.figures.enabled: true` — captures any charts or graphs embedded in the report.
- `chunkingStrategy.type: "page"` — outputs one markdown chunk per page, making downstream extraction aligned to document structure.
- **Why this config**: Lab reports have highly variable layouts (different labs, different test panels). Agentic OCR handles handwritten notes, non-standard fonts, and shifted table alignments. Table header continuation is essential because CMP (Comprehensive Metabolic Panel) results often span multiple pages with implicit column headers.
### Step 2: Extract (structured JSON with Zod schema)
- **Processor**: `extractRuns.createAndPoll()`
- **Purpose**: Parse the markdown output and extract 20 patient/specimen/facility fields and a dynamic array of test results.
- **Key config**:
- **Schema structure**: Root object with nested properties for demographics (name, DOB, age, gender, patient_id), facility info (lab_name, lab_address, lab_director, lab_clia_number, hospital_name, hospital_address, hospital_phone, patient_phone), specimen metadata (specimen_id, specimen_collection_date, specimen_received_date), report metadata (report_status, report_datetime, fasting_status), and a `lab_results` array.
- **lab_results array**: Each item is an object with test_name, result_value, reference_range, units, panel_name, lab_code, flag, and interpretation — capturing the full context of each analyte.
- **Field descriptions**: Ultra-specific guidance (e.g., "may be labeled as 'CLIA', 'CMS', or appear in footer") helps the model find fields in non-standard positions.
- **Nullability**: All string/number fields are nullable (`type: ["string", "null"]`) to gracefully handle missing fields; arrays are not nullable but may be empty.
- **Why this config**: Lab result PDFs are dense with clinical terminology and variable field positions. Explicit descriptions tie each schema field to all known label variations across labs, ensuring high recall. The nested array structure mirrors the semantic unit of a lab report: one patient, one specimen, multiple tests.
---
## TypeScript implementation
---
## CLI equivalent
```bash
# Set API key
export EXTEND_API_KEY="sk_..."
# Step 1: Parse the lab results PDF with agentic OCR and table detection
extend parse lab_results.pdf \
--mode agentic_ocr \
--block-option text.agentic.enabled=true \
--block-option text.signatureDetectionEnabled=true \
--block-option tables.agentic.enabled=true \
--block-option tables.tableHeaderContinuationEnabled=true \
--block-option figures.enabled=true \
--chunking-strategy page \
> parsed_output.md
# Step 2: Extract structured fields using the schema (save schema to file first)
cat > lab_schema.json << 'EOF'
{
"type": "object",
"properties": {
"patient_name": { "type": ["string", "null"], "description": "Full name of the patient" },
"date_of_birth": { "type": ["string", "null"], "description": "Patient's date of birth in MM/DD/YYYY format" },
"age": { "type": ["integer", "null"], "description": "Patient's age in years" },
"gender": { "type": ["string", "null"], "description": "Patient gender: M/F" },
"patient_id": { "type": ["string", "null"], "description": "Unique patient identifier or MRN" },
"patient_phone": { "type": ["string", "null"], "description": "Patient contact phone" },
"specimen_id": { "type": ["string", "null"], "description": "Unique specimen identifier" },
"specimen_collection_date": { "type": ["string", "null"], "description": "Date specimen was collected" },
"specimenimport { ExtendClient } from "extend-ai";
import { z } from "zod";
import fs from "fs";
const client = new ExtendClient({ token: process.env.EXTEND_API_KEY });
// Zod schema matching the lab results extractor pipeline
const labResultItemSchema = z.object({
test_name: z.string().nullable().describe("Name of the lab test (e.g., 'GLUCOSE', 'HEMOGLOBIN')"),
result_value: z.string().nullable().describe("Numerical or descriptive result value"),
reference_range: z.string().nullable().describe("Normal reference range for the test (e.g., '65-99 mg/dL')"),
units: z.string().nullable().describe("Measurement units (e.g., 'mg/dL', 'mmol/L')"),
panel_name: z.string().nullable().describe("Name of test panel (e.g., 'COMPREHENSIVE METABOLIC PANEL')"),
lab_code: z.string().nullable().describe("Lab code or abbreviation for this test"),
flag: z.string().nullable().describe("Flag if outside range: 'H' (high), 'L' (low), 'Normal', or blank"),
interpretation: z.string().nullable().describe("Additional notes or interpretation (e.g., 'Fasting reference interval')"),
});
const labResultsExtractorSchema = z.object({
patient_name: z.string().nullable().describe("Full name of the patient whose test results are reported"),
date_of_birth: z.string().nullable().describe("Patient's date of birth in MM/DD/YYYY format"),
age: z.number().nullable().describe("Patient's age in years, computed from DOB or explicitly stated"),
gender: z.string().nullable().describe("Patient gender: 'M', 'F', or full descriptor"),
patient_id: z.string().nullable().describe("Unique patient identifier, MRN, or account number"),
patient_phone: z.string().nullable().describe("Contact phone number for the patient"),
specimen_id: z.string().nullable().describe("Unique specimen or sample identifier"),
specimen_collection_date: z.string().nullable().describe("Date specimen was collected (MM/DD/YYYY or similar)"),
specimen_received_date: z.string().nullable().describe("Date lab received the specimen"),
fasting_status: z.string().nullable().describe("Whether patient was fasting: 'Y', 'N', 'Yes', 'No', or descriptive"),
report_status: z.string().nullable().describe("Report status: 'Final', 'Preliminary', 'Amended', or similar"),
report_datetime: z.string().nullable().describe("Date/time report was finalized (MM/DD/YYYY / HH:MM format)"),
collection_datetime: z.string().nullable().describe("Date/time specimen collected (MM/DD/YYYY / HH:MM TZ format)"),
lab_name: z.string().nullable().describe("Name of the laboratory that performed the analysis"),
lab_address: z.string().nullable().describe("Address of the laboratory"),
lab_director: z.string().nullable().describe("Name of laboratory director or responsible scientist"),
lab_clia_number: z.string().nullable().describe("CLIA number or accreditation identifier for the lab"),
hospital_name: z.string().nullable().describe("Name of hospital or healthcare facility associated with the test"),
hospital_address: z.string().nullable().describe("Address of the hospital or healthcare facility"),
hospital_phone: z.string().nullable().describe("Contact phone number for the hospital or facility"),
lab_results: z.array(labResultItemSchema).describe("Array of individual lab test results with values, ranges, and interpretations"),
});
export async function processLabResultsExtractor(filePath: string) {
console.log(`Processing lab results from: ${filePath}`);
// Convert local file to data URL (base64) for SDK compatibility
const fileBuffer = fs.readFileSync(filePath);
const base64Data = fileBuffer.toString("base64");
const dataUrl = `data:application/octet-stream;base64,${base64Data}`;
// Step 1: Parse with agentic OCR, table detection, and signature detection
console.log("Step 1: Parsing lab results document...");
const parseRun = await client.parseRuns.createAndPoll({
file: { url: dataUrl },
config: {
mode: "agentic_ocr",
blockOptions: {
text: {
agentic: { enabled: true },
signatureDetectionEnabled: true,
},
tables: {
agentic: { enabled: true },
tableHeaderContinuationEnabled: true,
},
figures: { enabled: true },
},
chunkingStrategy: {
type: "page",
options: {},
},
},
});
if (parseRun.status !== "PROCESSED") {
throw new Error(`Parse failed with status: ${parseRun.status}`);
}
console.log(`✓ Parsed ${parseRun.output.chunks.length} pages`);
// Log the first chunk as a sanity check
if (parseRun.output.chunks.length > 0) {
console.log(`First chunk preview:\n${parseRun.output.chunks[0].content.substring(0, 300)}...\n`);
}
// Step 2: Extract structured fields using Zod schema
console.log("Step 2: Extracting lab results fields...");
const extractRun = await client.extractRuns.createAndPoll({
file: { url: dataUrl },
config: {
schema: labResultsExtractorSchema,
},
});
if (extractRun.status !== "PROCESSED") {
throw new Error(`Extraction failed with status: ${extractRun.status}`);
}
const result = extractRun.output.value;
console.log("✓ Extraction complete\n");
console.log("=== EXTRACTED LAB RESULTS ===\n");
// Display patient demographics
console.log("PATIENT INFORMATION:");
console.log(` Name: ${result.patient_name || "N/A"}`);
console.log(` DOB: ${result.date_of_birth || "N/A"}`);
console.log(` Age: ${result.age || "N/A"}`);
console.log(` Gender: ${result.gender || "N/A"}`);
console.log(` Patient ID: ${result.patient_id || "N/A"}`);
console.log(` Phone: ${result.patient_phone || "N/A"}\n`);
// Display specimen and report metadata
console.log("SPECIMEN & REPORT:");
console.log(` Specimen ID: ${result.specimen_id || "N/A"}`);
console.log(` Collection Date/Time: ${result.collection_datetime || "N/A"}`);
console.log(` Received Date: ${result.specimen_received_date || "N/A"}`);
console.log(` Fasting Status: ${result.fasting_status || "N/A"}`);
console.log(` Report Status: ${result.report_status || "N/A"}`);
console.log(` Report Date/Time: ${result.report_datetime || "N/A"}\n`);
// Display facility information
console.log("FACILITY INFORMATION:");
console.log(` Lab Name: ${result.lab_name || "N/A"}`);
console.log(` Lab Address: ${result.lab_address || "N/A"}`);
console.log(` Lab Director: ${result.lab_director || "N/A"}`);
console.log(` CLIA Number: ${result.lab_clia_number || "N/A"}`);
console.log(` Hospital: ${result.hospital_name || "N/A"}`);
console.log(` Hospital Address: ${result.hospital_address || "N/A"}`);
console.log(` Hospital Phone: ${result.hospital_phone || "N/A"}\n`);
// Display lab results
console.log("LAB TEST RESULTS:");
if (result.lab_results && result.lab_results.length > 0) {
console.log(`Total Tests: ${result.lab_results.length}\n`);
// Group by panel if available
const byPanel = new Map<string | null, typeof result.lab_results>();
for (const test of result.lab_results) {
const panel = test.panel_name || "OTHER";
if (!byPanel.has(panel)) byPanel.set(panel, []);
byPanel.get(panel)!.push(test);
}
for (const [panel, tests] of byPanel) {
console.log(` ${panel}:`);
for (const test of tests) {
const flag = test.flag ? ` [${test.flag}]` : "";
const units = test.units ? ` ${test.units}` : "";
const refRange = test.reference_range ? ` (ref: ${test.reference_range})` : "";
console.log(` • ${test.test_name}: ${test.result_value}${units}${refRange}${flag}`);
if (test.interpretation) {
console.log(` Note: ${test.interpretation}`);
}
}
console.log();
}
} else {
console.log(" No lab results extracted.\n");
}
// Return the full structured result for programmatic use
return {
status: "success",
data: result,
parseChunks: parseRun.output.chunks.map(c => c.content),
};
}
// For direct execution (e.g., npm run or node script)
const filePath = process.argv[2];
if (!filePath) {
console.error("Usage: node solution.ts <path-to-pdf>");
process.exit(1);
}
processLabResultsExtractor(filePath)
.then(result => {
console.log("\n=== RAW JSON OUTPUT ===");
console.log(JSON.stringify(result.data, null, 2));
})
.catch(err => {
console.error("Error:", err);
process.exit(1);
});from extend_ai import Extend
from pydantic import BaseModel, Field
from typing import Optional, List
import base64
import os
import sys
import json
client = Extend(token=os.environ["EXTEND_API_KEY"])
class LabResultItem(BaseModel):
test_name: Optional[str] = Field(None, description="Name of the lab test (e.g., 'GLUCOSE', 'HEMOGLOBIN')")
result_value: Optional[str] = Field(None, description="Numerical or descriptive result value")
reference_range: Optional[str] = Field(None, description="Normal reference range for the test (e.g., '65-99 mg/dL')")
units: Optional[str] = Field(None, description="Measurement units (e.g., 'mg/dL', 'mmol/L')")
panel_name: Optional[str] = Field(None, description="Name of test panel (e.g., 'COMPREHENSIVE METABOLIC PANEL')")
lab_code: Optional[str] = Field(None, description="Lab code or abbreviation for this test")
flag: Optional[str] = Field(None, description="Flag if outside range: 'H' (high), 'L' (low), 'Normal', or blank")
interpretation: Optional[str] = Field(None, description="Additional notes or interpretation (e.g., 'Fasting reference interval')")
class LabResultsExtractor(BaseModel):
patient_name: Optional[str] = Field(None, description="Full name of the patient whose test results are reported")
date_of_birth: Optional[str] = Field(None, description="Patient's date of birth in MM/DD/YYYY format")
age: Optional[int] = Field(None, description="Patient's age in years, computed from DOB or explicitly stated")
gender: Optional[str] = Field(None, description="Patient gender: 'M', 'F', or full descriptor")
patient_id: Optional[str] = Field(None, description="Unique patient identifier, MRN, or account number")
patient_phone: Optional[str] = Field(None, description="Contact phone number for the patient")
specimen_id: Optional[str] = Field(None, description="Unique specimen or sample identifier")
specimen_collection_date: Optional[str] = Field(None, description="Date specimen was collected (MM/DD/YYYY or similar)")
specimen_received_date: Optional[str] = Field(None, description="Date lab received the specimen")
fasting_status: Optional[str] = Field(None, description="Whether patient was fasting: 'Y', 'N', 'Yes', 'No', or descriptive")
report_status: Optional[str] = Field(None, description="Report status: 'Final', 'Preliminary', 'Amended', or similar")
report_datetime: Optional[str] = Field(None, description="Date/time report was finalized (MM/DD/YYYY / HH:MM format)")
collection_datetime: Optional[str] = Field(None, description="Date/time specimen collected (MM/DD/YYYY / HH:MM TZ format)")
lab_name: Optional[str] = Field(None, description="Name of the laboratory that performed the analysis")
lab_address: Optional[str] = Field(None, description="Address of the laboratory")
lab_director: Optional[str] = Field(None, description="Name of laboratory director or responsible scientist")
lab_clia_number: Optional[str] = Field(None, description="CLIA number or accreditation identifier for the lab")
hospital_name: Optional[str] = Field(None, description="Name of hospital or healthcare facility associated with the test")
hospital_address: Optional[str] = Field(None, description="Address of the hospital or healthcare facility")
hospital_phone: Optional[str] = Field(None, description="Contact phone number for the hospital or facility")
lab_results: List[LabResultItem] = Field(default_factory=list, description="Array of individual lab test results with values, ranges, and interpretations")
def process_lab_results_extractor(file_path: str):
print(f"Processing lab results from: {file_path}")
# Convert local file to data URL (base64) for SDK compatibility
with open(file_path, "rb") as f:
file_buffer = f.read()
base64_data = base64.b64encode(file_buffer).decode("utf-8")
data_url = f"data:application/octet-stream;base64,{base64_data}"
# Step 1: Parse with agentic OCR, table detection, and signature detection
print("Step 1: Parsing lab results document...")
parse_run = client.parse_runs.create_and_poll(
file={"url": data_url},
config={
"mode": "agentic_ocr",
"block_options": {
"text": {
"agentic": {"enabled": True},
"signature_detection_enabled": True,
},
"tables": {
"agentic": {"enabled": True},
"table_header_continuation_enabled": True,
},
"figures": {"enabled": True},
},
"chunking_strategy": {
"type": "page",
"options": {},
},
},
)
if parse_run.status != "PROCESSED":
raise Exception(f"Parse failed with status: {parse_run.status}")
print(f"✓ Parsed {len(parse_run.output.chunks)} pages")
# Log the first chunk as a sanity check
if parse_run.output.chunks:
print(f"First chunk preview:\n{parse_run.output.chunks[0].content[:300]}...\n")
# Step 2: Extract structured fields using Pydantic schema
print("Step 2: Extracting lab results fields...")
extract_run = client.extract_runs.create_and_poll(
file={"url": data_url},
config={
"schema": LabResultsExtractor.model_json_schema(),
},
)
if extract_run.status != "PROCESSED":
raise Exception(f"Extraction failed with status: {extract_run.status}")
result = LabResultsExtractor(**extract_run.output.value)
print("✓ Extraction complete\n")
print("=== EXTRACTED LAB RESULTS ===\n")
# Display patient demographics
print("PATIENT INFORMATION:")
print(f" Name: {result.patient_name or 'N/A'}")
print(f" DOB: {result.date_of_birth or 'N/A'}")
print(f" Age: {result.age or 'N/A'}")
print(f" Gender: {result.gender or 'N/A'}")
print(f" Patient ID: {result.patient_id or 'N/A'}")
print(f" Phone: {result.patient_phone or 'N/A'}\n")
# Display specimen and report metadata
print("SPECIMEN & REPORT:")
print(f" Specimen ID: {result.specimen_id or 'N/A'}")
print(f" Collection Date/Time: {result.collection_datetime or 'N/A'}")
print(f" Received Date: {result.specimen_received_date or 'N/A'}")
print(f" Fasting Status: {result.fasting_status or 'N/A'}")
print(f" Report Status: {result.report_status or 'N/A'}")
print(f" Report Date/Time: {result.report_datetime or 'N/A'}\n")
# Display facility information
print("FACILITY INFORMATION:")
print(f" Lab Name: {result.lab_name or 'N/A'}")
print(f" Lab Address: {result.lab_address or 'N/A'}")
print(f" Lab Director: {result.lab_director or 'N/A'}")
print(f" CLIA Number: {result.lab_clia_number or 'N/A'}")
print(f" Hospital: {result.hospital_name or 'N/A'}")
print(f" Hospital Address: {result.hospital_address or 'N/A'}")
print(f" Hospital Phone: {result.hospital_phone or 'N/A'}\n")
# Display lab results
print("LAB TEST RESULTS:")
if result.lab_results:
print(f"Total Tests: {len(result.lab_results)}\n")
# Group by panel if available
by_panel = {}
for test in result.lab_results:
panel = test.panel_name or "OTHER"
if panel not in by_panel:
by_panel[panel] = []
by_panel[panel].append(test)
for panel, tests in by_panel.items():
print(f" {panel}:")
for test in tests:
flag = f" [{test.flag}]" if test.flag else ""
units = f" {test.units}" if test.units else ""
ref_range = f" (ref: {test.reference_range})" if test.reference_range else ""
print(f" • {test.test_name}: {test.result_value}{units}{ref_range}{flag}")
if test.interpretation:
print(f" Note: {test.interpretation}")
print()
else:
print(" No lab results extracted.\n")
# Return the full structured result for programmatic use
return {
"status": "success",
"data": result.model_dump(),
"parse_chunks": [c.content for c in parse_run.output.chunks],
}
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python solution.py <path-to-pdf>")
sys.exit(1)
file_path = sys.argv[1]
result = process_lab_results_extractor(file_path)
print("\n=== RAW JSON OUTPUT ===")
print(json.dumps(result["data"], indent=2))import com.extend.ai.ExtendClient;
import com.extend.ai.models.ParseRunsCreateAndPollRequest;
import com.extend.ai.models.ExtractRunsCreateAndPollRequest;
import com.extend.ai.models.ParseRun;
import com.extend.ai.models.ExtractRun;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import java.io.File;
import java.nio.file.Files;
import java.util.*;
public class LabResultsExtractor {
private static final ExtendClient client = new ExtendClient(
System.getenv("EXTEND_API_KEY")
);
private static final ObjectMapper objectMapper = new ObjectMapper();
public static void main(String[] args) throws Exception {
if (args.length == 0) {
System.err.println("Usage: java LabResultsExtractor <path-to-pdf>");
System.exit(1);
}
String filePath = args[0];
try {
Map<String, Object> result = processLabResultsExtractor(filePath);
System.out.println("\n=== RAW JSON OUTPUT ===");
System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(result.get("data")));
} catch (Exception err) {
System.err.println("Error: " + err.getMessage());
err.printStackTrace();
System.exit(1);
}
}
public static Map<String, Object> processLabResultsExtractor(String filePath) throws Exception {
System.out.println("Processing lab results from: " + filePath);
// Convert local file to data URL (base64) for SDK compatibility
byte[] fileBuffer = Files.readAllBytes(new File(filePath).toPath());
String base64Data = Base64.getEncoder().encodeToString(fileBuffer);
String dataUrl = "data:application/octet-stream;base64," + base64Data;
// Step 1: Parse with agentic OCR, table detection, and signature detection
System.out.println("Step 1: Parsing lab results document...");
ObjectNode parseConfig = objectMapper.createObjectNode();
parseConfig.put("mode", "agentic_ocr");
ObjectNode blockOptions = parseConfig.putObject("blockOptions");
ObjectNode textBlock = blockOptions.putObject("text");
textBlock.putObject("agentic").put("enabled", true);
textBlock.put("signatureDetectionEnabled", true);
ObjectNode tablesBlock = blockOptions.putObject("tables");
tablesBlock.putObject("agentic").put("enabled", true);
tablesBlock.put("tableHeaderContinuationEnabled", true);
blockOptions.putObject("figures").put("enabled", true);
ObjectNode chunkingStrategy = parseConfig.putObject("chunkingStrategy");
chunkingStrategy.put("type", "page");
chunkingStrategy.putObject("options");
ParseRunsCreateAndPollRequest parseRequest = new ParseRunsCreateAndPollRequest.Builder()
.file(new ParseRunsCreateAndPollRequest.File(dataUrl))
.config(parseConfig)
.build();
ParseRun parseRun = client.parseRuns().createAndPoll(parseRequest);
if (!"PROCESSED".equals(parseRun.getStatus())) {
throw new Exception("Parse failed with status: " + parseRun.getStatus());
}
System.out.println("✓ Parsed " + parseRun.getOutput().getChunks().size() + " pages");
if (!parseRun.getOutput().getChunks().isEmpty()) {
String firstChunk = parseRun.getOutput().getChunks().get(0).getContent();
int previewLength = Math.min(300, firstChunk.length());
System.out.println("First chunk preview:\n" + firstChunk.substring(0, previewLength) + "...\n");
}
// Step 2: Extract structured fields using schema
System.out.println("Step 2: Extracting lab results fields...");
ObjectNode schema = createLabResultsSchema();
ExtractRunsCreateAndPollRequest extractRequest = new ExtractRunsCreateAndPollRequest.Builder()
.file(new ExtractRunsCreateAndPollRequest.File(dataUrl))
.config(new ExtractRunsCreateAndPollRequest.Config(schema))
.build();
ExtractRun extractRun = client.extractRuns().createAndPoll(extractRequest);
if (!"PROCESSED".equals(extractRun.getStatus())) {
throw new Exception("Extraction failed with status: " + extractRun.getStatus());
}
JsonNode result = (JsonNode) extractRun.getOutput().getValue();
System.out.println("✓ Extraction complete\n");
System.out.println("=== EXTRACTED LAB RESULTS ===\n");
// Display patient demographics
System.out.println("PATIENT INFORMATION:");
System.out.println(" Name: " + getStringOrNA(result, "patient_name"));
System.out.println(" DOB: " + getStringOrNA(result, "date_of_birth"));
System.out.println(" Age: " + getStringOrNA(result, "age"));
System.out.println(" Gender: " + getStringOrNA(result, "gender"));
System.out.println(" Patient ID: " + getStringOrNA(result, "patient_id"));
System.out.println(" Phone: " + getStringOrNA(result, "patient_phone") + "\n");
// Display specimen and report metadata
System.out.println("SPECIMEN & REPORT:");
System.out.println(" Specimen ID: " + getStringOrNA(result, "specimen_id"));
System.out.println(" Collection Date/Time: " + getStringOrNA(result, "collection_datetime"));
System.out.println(" Received Date: " + getStringOrNA(result, "specimen_received_date"));
System.out.println(" Fasting Status: " + getStringOrNA(result, "fasting_status"));
System.out.println(" Report Status: " + getStringOrNA(result, "report_status"));
System.out.println(" Report Date/Time: " + getStringOrNA(result, "report_datetime") + "\n");
// Display facility information
System.out.println("FACILITY INFORMATION:");
System.out.println(" Lab Name: " + getStringOrNA(result, "lab_name"));
System.out.println(" Lab Address: " + getStringOrNA(result, "lab_address"));
System.out.println(" Lab Director: " + getStringOrNA(result, "lab_director"));
System.out.println(" CLIA Number: " + getStringOrNA(result, "lab_clia_number"));
System.out.println(" Hospital: " + getStringOrNA(result, "hospital_name"));
System.out.println(" Hospital Address: " + getStringOrNA(result, "hospital_address"));
System.out.println(" Hospital Phone: " + getStringOrNA(result, "hospital_phone") + "\n");
// Display lab results
System.out.println("LAB TEST RESULTS:");
JsonNode labResultsNode = result.get("lab_results");
if (labResultsNode != null && labResultsNode.isArray() && labResultsNode.size() > 0) {
System.out.println("Total Tests: " + labResultsNode.size() + "\n");
Map<String, List<JsonNode>> byPanel = new LinkedHashMap<>();
for (JsonNode test : labResultsNode) {
String panel = test.has("panel_name") && !test.get("panel_name").isNull()
? test.get("panel_name").asText()
: "OTHER";
byPanel.computeIfAbsent(panel, k -> new ArrayList<>()).add(test);
}
for (Map.Entry<String, List<JsonNode>> entry : byPanel.entrySet()) {
System.out.println(" " + entry.getKey() + ":");
for (JsonNode test : entry.getValue()) {
String flag = test.has("flag") && !test.get("flag").isNull()
? " [" + test.get("flag").asText() + "]"
: "";
String units = test.has("units") && !test.get("units").isNull()
? " " + test.get("units").asText()
: "";
String refRange = test.has("reference_range") && !test.get("reference_range").isNull()
? " (ref: " + test.get("reference_range").asText() + ")"
: "";
String testName = test.has("test_name") && !test.get("test_name").isNull()
? test.get("test_name").asText()
: "N/A";
String resultValue = test.has("result_value") && !test.get("result_value").isNull()
? test.get("result_value").asText()
: "N/A";
System.out.println(" • " + testName + ": " + resultValue + units + refRange + flag);
if (test.has("interpretation") && !test.get("interpretation").isNull()) {
System.out.println(" Note: " + test.get("interpretation").asText());
}
}
System.out.println();
}
} else {
System.out.println(" No lab results extracted.\n");
}
// Return the full structured result for programmatic use
Map<String, Object> response = new HashMap<>();
response.put("status", "success");
response.put("data", result);
List<String> chunks = new ArrayList<>();
for (int i = 0; i < parseRun.getOutput().getChunks().size(); i++) {
chunks.add(parseRun.getOutput().getChunks().get(i).getContent());
}
response.put("parseChunks", chunks);
return response;
}
private static String getStringOrNA(JsonNode node, String field) {
if (node.has(field) && !node.get(field).isNull()) {
return node.get(field).asText();
}
return "N/A";
}
private static ObjectNode createLabResultsSchema() {
ObjectNode schema = objectMapper.createObjectNode();
schema.put("type", "object");
ObjectNode properties = schema.putObject("properties");
// Patient name
ObjectNode patientName = properties.putObject("patient_name");
ArrayNode patientNameType = patientName.putArray("type");
patientNameType.add("string");
patientNameType.add("null");
patientName.put("description", "Full name of the patient whose test results are reported");
// Date of birth
ObjectNode dob = properties.putObject("date_of_birth");
ArrayNode dobType = dob.putArray("type");
dobType.add("string");
dobType.add("null");
dob.put("description", "Patient's date of birth in MM/DD/YYYY format");
// Age
ObjectNode age = properties.putObject("age");
ArrayNode ageType = age.putArray("type");
ageType.add("integer");
ageType.add("null");
age.put("description", "Patient's age in years, computed from DOB or explicitly stated");
// Gender
ObjectNode gender = properties.putObject("gender");
ArrayNode genderType = gender.putArray("type");
genderType.add("string");
genderType.add("null");
gender.put("description", "Patient gender: 'M', 'F', or full descriptor");
// Patient ID
ObjectNode patientId = properties.putObject("patient_id");
ArrayNode patientIdType = patientId.putArray("type");
patientIdType.add("string");
patientIdType.add("null");
patientId.put("description", "Unique patient identifier, MRN, or account number");
// Patient phone
ObjectNode patientPhone = properties.putObject("patient_phone");
ArrayNode patientPhoneType = patientPhone.putArray("type");
patientPhoneType.add("string");
patientPhoneType.add("null");
patientPhone.put("description", "Contact phone number for the patient");
// Specimen ID
ObjectNode specimenId = properties.putObject("specimen_id");
ArrayNode specimenIdType = specimenId.putArray("type");
specimenIdType.add("string");
specimenIdType.add("null");
specimenId.put("description", "Unique specimen or sample identifier");
// Specimen collection date
ObjectNode specimenCollectionDate = properties.putObject("specimen_collection_date");
ArrayNode specimenCollectionDateType = specimenCollectionDate.putArray("type");
specimenCollectionDateType.add("string");
specimenCollectionDateType.add("null");
specimenCollectionDate.put("description", "Date specimen was collected (MM/DD/YYYY or similar)");
// Specimen received date
ObjectNode specimenReceivedDate = properties.putObject("specimen_received_date");
ArrayNode specimenReceivedDateType = specimenReceivedDate.putArray("type");
specimenReceivedDateType.add("string");
specimenReceivedDateType.add("null");
specimenReceivedDate.put("description", "Date lab received the specimen");
// Fasting status
ObjectNode fastingStatus = properties.putObject("fasting_status");
ArrayNode fastingStatusType = fastingStatus.putArray("type");
fastingStatusType.add("string");
fastingStatusType.add("null");
fastingStatus.put("description", "Whether patient was fasting: 'Y', 'N', 'Yes', 'No', or descriptive");
// Report status
ObjectNode reportStatus = properties.putObject("report_status");
ArrayNode reportStatusType = reportStatus.putArray("type");
reportStatusType.add("string");
reportStatusType.add("null");
reportStatus.put("description", "Report status: 'Final', 'Preliminary', 'Amended', or similar");
// Report datetime
ObjectNode reportDatetime = properties.putObject("report_datetime");
ArrayNode reportDatetimeType = reportDatetime.putArray("type");
reportDatetimeType.add("string");
reportDatetimeType.add("null");
reportDatetime.put("description", "Date/time report was finalized (MM/DD/YYYY / HH:MM format)");
// Collection datetime
ObjectNode collectionDatetime = properties.putObject("collection_datetime");
ArrayNode collectionDatetimeType = collectionDatetime.putArray("type");
collectionDatetimeType.add("string");
collectionDatetimeType.add("null");
collectionDatetime.put("description", "Date/time specimen collected (MM/DD/YYYY / HH:MM TZ format)");
// Lab name
ObjectNode labName = properties.putObject("lab_name");
ArrayNode labNameType = labName.putArray("type");
labNameType.add("string");
labNameType.add("null");
labName.put("description", "Name of the laboratory that performed the analysis");
// Lab address
ObjectNode labAddress = properties.putObject("lab_address");
ArrayNode labAddressType = labAddress.putArray("type");
labAddressType.add("string");
labAddressType.add("null");
labAddress.put("description", "Address of the laboratory");
// Lab director
ObjectNode labDirector = properties.putObject("lab_director");
ArrayNode labDirectorType = labDirector.putArray("type");
labDirectorType.add("string");
labDirectorType.add("null");
labDirector.put("description", "Name of laboratory director or responsible scientist");
// Lab CLIA number
ObjectNode labCliaNumber = properties.putObject("lab_clia_number");
ArrayNode labCliaNumberType = labCliaNumber.putArray("type");
labCliaNumberType.add("string");
labCliaNumberType.add("null");
labCliaNumber.put("description", "CLIA number or accreditation identifier for the lab");
// Hospital name
ObjectNode hospitalName = properties.putObject("hospital_name");
ArrayNode hospitalNameType = hospitalName.putArray("type");
hospitalNameType.add("string");
hospitalNameType.add("null");
hospitalName.put("description", "Name of hospital or healthcare facility associated with the test");
// Hospital address
ObjectNode hospitalAddress = properties.putObject("hospital_address");
ArrayNode hospitalAddressType = hospitalAddress.putArray("type");
hospitalAddressType.add("string");
hospitalAddressType.add("null");
hospitalAddress.put("description", "Address of the hospital or healthcare facility");
// Hospital phone
ObjectNode hospitalPhone = properties.putObject("hospital_phone");
ArrayNode hospitalPhoneType = hospitalPhone.putArray("type");
hospitalPhoneType.add("string");
hospitalPhoneType.add("null");
hospitalPhone.put("description", "Contact phone number for the hospital or facility");
// Lab results array
ObjectNode labResults = properties.putObject("lab_results");
labResults.put("type", "array");
ObjectNode labResultItem = labResults.putObject("items");
labResultItem.put("type", "object");
ObjectNode labResultItemProperties = labResultItem.putObject("properties");
// Test name
ObjectNode testName = labResultItemProperties.putObject("test_name");
ArrayNode testNameType = testName.putArray("type");
testNameType.add("string");
testNameType.add("null");
testName.put("description", "Name of the lab test (e.g., 'GLUCOSE', 'HEMOGLOBIN')");
// Result value
ObjectNode resultValue = labResultItemProperties.putObject("result_value");
ArrayNode resultValueType = resultValue.putArray("type");
resultValueType.add("string");
resultValueType.add("null");
resultValue.put("description", "Numerical or descriptive result value");
// Reference range
ObjectNode referenceRange = labResultItemProperties.putObject("reference_range");
ArrayNode referenceRangeType = referenceRange.putArray("type");
referenceRangeType.add("string");
referenceRangeType.add("null");
referenceRange.put("description", "Normal reference range for the test (e.g., '65-99 mg/dL')");
// Units
ObjectNode units = labResultItemProperties.putObject("units");
ArrayNode unitsType = units.putArray("type");
unitsType.add("string");
unitsType.add("null");
units.put("description", "Measurement units (e.g., 'mg/dL', 'mmol/L')");
// Panel name
ObjectNode panelName = labResultItemProperties.putObject("panel_name");
ArrayNode panelNameType = panelName.putArray("type");
panelNameType.add("string");
panelNameType.add("null");
panelName.put("description", "Name of test panel (e.g., 'COMPREHENSIVE METABOLIC PANEL')");
// Lab code
ObjectNode labCode = labResultItemProperties.putObject("lab_code");
ArrayNode labCodeType = labCode.putArray("type");
labCodeType.add("string");
labCodeType.add("null");
labCode.put("description", "Lab code or abbreviation for this test");
// Flag
ObjectNode flag = labResultItemProperties.putObject("flag");
ArrayNode flagType = flag.putArray("type");
flagType.add("string");
flagType.add("null");
flag.put("description", "Flag if outside range: 'H' (high), 'L' (low), 'Normal', or blank");
// Interpretation
ObjectNode interpretation = labResultItemProperties.putObject("interpretation");
ArrayNode interpretationType = interpretation.putArray("type");
interpretationType.add("string");
interpretationType.add("null");
interpretation.put("description", "Additional notes or interpretation (e.g., 'Fasting reference interval')");
labResults.put("description", "Array of individual lab test results with values, ranges, and interpretations");
return schema;
}
}package main
import (
"encoding/base64"
"encoding/json"
"flag"
"fmt"
"os"
"github.com/extend-ai/extend-go"
)
type LabResultItem struct {
TestName *string `json:"test_name"`
ResultValue *string `json:"result_value"`
ReferenceRange *string `json:"reference_range"`
Units *string `json:"units"`
PanelName *string `json:"panel_name"`
LabCode *string `json:"lab_code"`
Flag *string `json:"flag"`
Interpretation *string `json:"interpretation"`
}
type LabResultsExtractor struct {
PatientName *string `json:"patient_name"`
DateOfBirth *string `json:"date_of_birth"`
Age *int `json:"age"`
Gender *string `json:"gender"`
PatientID *string `json:"patient_id"`
PatientPhone *string `json:"patient_phone"`
SpecimenID *string `json:"specimen_id"`
SpecimenCollectionDate *string `json:"specimen_collection_date"`
SpecimenReceivedDate *string `json:"specimen_received_date"`
FastingStatus *string `json:"fasting_status"`
ReportStatus *string `json:"report_status"`
ReportDatetime *string `json:"report_datetime"`
CollectionDatetime *string `json:"collection_datetime"`
LabName *string `json:"lab_name"`
LabAddress *string `json:"lab_address"`
LabDirector *string `json:"lab_director"`
LabCliaNumber *string `json:"lab_clia_number"`
HospitalName *string `json:"hospital_name"`
HospitalAddress *string `json:"hospital_address"`
HospitalPhone *string `json:"hospital_phone"`
LabResults []LabResultItem `json:"lab_results"`
}
func processLabResultsExtractor(filePath string) (map[string]interface{}, error) {
fmt.Printf("Processing lab results from: %s\n", filePath)
client := extend.NewClient(os.Getenv("EXTEND_API_KEY"))
// Convert local file to data URL (base64) for SDK compatibility
fileBuffer, err := os.ReadFile(filePath)
if err != nil {
return nil, fmt.Errorf("failed to read file: %w", err)
}
base64Data := base64.StdEncoding.EncodeToString(fileBuffer)
dataURL := fmt.Sprintf("data:application/octet-stream;base64,%s", base64Data)
// Step 1: Parse with agentic OCR, table detection, and signature detection
fmt.Println("Step 1: Parsing lab results document...")
parseConfig := extend.ParseConfig{
Mode: "agentic_ocr",
BlockOptions: extend.BlockOptions{
Text: extend.TextBlockOptions{
Agentic: extend.AgenticOptions{
Enabled: true,
},
SignatureDetectionEnabled: true,
},
Tables: extend.TableBlockOptions{
Agentic: extend.AgenticOptions{
Enabled: true,
},
TableHeaderContinuationEnabled: true,
},
Figures: extend.FigureBlockOptions{
Enabled: true,
},
},
ChunkingStrategy: extend.ChunkingStrategy{
Type: "page",
Options: map[string]interface{}{},
},
}
parseRun, err := client.ParseRuns.CreateAndPoll(&extend.ParseRunRequest{
File: extend.FileReference{
URL: dataURL,
},
Config: parseConfig,
})
if err != nil {
return nil, fmt.Errorf("parse request failed: %w", err)
}
if parseRun.Status != "PROCESSED" {
return nil, fmt.Errorf("parse failed with status: %s", parseRun.Status)
}
fmt.Printf("✓ Parsed %d pages\n", len(parseRun.Output.Chunks))
if len(parseRun.Output.Chunks) > 0 {
chunkPreview := parseRun.Output.Chunks[0].Content
if len(chunkPreview) > 300 {
chunkPreview = chunkPreview[:300]
}
fmt.Printf("First chunk preview:\n%s...\n\n", chunkPreview)
}
// Step 2: Extract structured fields using JSON schema
fmt.Println("Step 2: Extracting lab results fields...")
schema := map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"patient_name": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Full name of the patient whose test results are reported",
},
"date_of_birth": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Patient's date of birth in MM/DD/YYYY format",
},
"age": map[string]interface{}{
"type": []interface{}{"integer", "null"},
"description": "Patient's age in years, computed from DOB or explicitly stated",
},
"gender": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Patient gender: 'M', 'F', or full descriptor",
},
"patient_id": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Unique patient identifier, MRN, or account number",
},
"patient_phone": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Contact phone number for the patient",
},
"specimen_id": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Unique specimen or sample identifier",
},
"specimen_collection_date": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Date specimen was collected (MM/DD/YYYY or similar)",
},
"specimen_received_date": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Date lab received the specimen",
},
"fasting_status": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Whether patient was fasting: 'Y', 'N', 'Yes', 'No', or descriptive",
},
"report_status": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Report status: 'Final', 'Preliminary', 'Amended', or similar",
},
"report_datetime": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Date/time report was finalized (MM/DD/YYYY / HH:MM format)",
},
"collection_datetime": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Date/time specimen collected (MM/DD/YYYY / HH:MM TZ format)",
},
"lab_name": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Name of the laboratory that performed the analysis",
},
"lab_address": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Address of the laboratory",
},
"lab_director": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Name of laboratory director or responsible scientist",
},
"lab_clia_number": map[string]interface{}{
"type": []string{"string", "null"},
"description": "CLIA number or accreditation identifier for the lab",
},
"hospital_name": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Name of hospital or healthcare facility associated with the test",
},
"hospital_address": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Address of the hospital or healthcare facility",
},
"hospital_phone": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Contact phone number for the hospital or facility",
},
"lab_results": map[string]interface{}{
"type": "array",
"items": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"test_name": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Name of the lab test (e.g., 'GLUCOSE', 'HEMOGLOBIN')",
},
"result_value": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Numerical or descriptive result value",
},
"reference_range": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Normal reference range for the test (e.g., '65-99 mg/dL')",
},
"units": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Measurement units (e.g., 'mg/dL', 'mmol/L')",
},
"panel_name": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Name of test panel (e.g., 'COMPREHENSIVE METABOLIC PANEL')",
},
"lab_code": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Lab code or abbreviation for this test",
},
"flag": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Flag if outside range: 'H' (high), 'L' (low), 'Normal', or blank",
},
"interpretation": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Additional notes or interpretation (e.g., 'Fasting reference interval')",
},
},
},
"description": "Array of individual lab test results with values, ranges, and interpretations",
},
},
}
extractRun, err := client.ExtractRuns.CreateAndPoll(&extend.ExtractRunRequest{
File: extend.FileReference{
URL: dataURL,
},
Config: extend.ExtractConfig{
Schema: schema,
},
})
if err != nil {
return nil, fmt.Errorf("extraction request failed: %w", err)
}
if extractRun.Status != "PROCESSED" {
return nil, fmt.Errorf("extraction failed with status: %s", extractRun.Status)
}
var result LabResultsExtractor
resultBytes, err := json.Marshal(extractRun.Output.Value)
if err != nil {
return nil, fmt.Errorf("failed to marshal extraction result: %w", err)
}
if err := json.Unmarshal(resultBytes, &result); err != nil {
return nil, fmt.Errorf("failed to unmarshal extraction result: %w", err)
}
fmt.Println("✓ Extraction complete\n")
fmt.Println("=== EXTRACTED LAB RESULTS ===\n")
// Display patient demographics
fmt.Println("PATIENT INFORMATION:")
fmt.Printf(" Name: %s\n", stringOrNA(result.PatientName))
fmt.Printf(" DOB: %s\n", stringOrNA(result.DateOfBirth))
fmt.Printf(" Age: %s\n", intOrNA(result.Age))
fmt.Printf(" Gender: %s\n", stringOrNA(result.Gender))
fmt.Printf(" Patient ID: %s\n", stringOrNA(result.PatientID))
fmt.Printf(" Phone: %s\n\n", stringOrNA(result.PatientPhone))
// Display specimen and report metadata
fmt.Println("SPECIMEN & REPORT:")
fmt.Printf(" Specimen ID: %s\n", stringOrNA(result.SpecimenID))
fmt.Printf(" Collection Date/Time: %s\n", stringOrNA(result.CollectionDatetime))
fmt.Printf(" Received Date: %s\n", stringOrNA(result.SpecimenReceivedDate))
fmt.Printf(" Fasting Status: %s\n", stringOrNA(result.FastingStatus))
fmt.Printf(" Report Status: %s\n", stringOrNA(result.ReportStatus))
fmt.Printf(" Report Date/Time: %s\n\n", stringOrNA(result.ReportDatetime))
// Display facility information
fmt.Println("FACILITY INFORMATION:")
fmt.Printf(" Lab Name: %s\n", stringOrNA(result.LabName))
fmt.Printf(" Lab Address: %s\n", stringOrNA(result.LabAddress))
fmt.Printf(" Lab Director: %s\n", stringOrNA(result.LabDirector))
fmt.Printf(" CLIA Number: %s\n", stringOrNA(result.LabCliaNumber))
fmt.Printf(" Hospital: %s\n", stringOrNA(result.HospitalName))
fmt.Printf(" Hospital Address: %s\n", stringOrNA(result.HospitalAddress))
fmt.Printf(" Hospital Phone: %s\n\n", stringOrNA(result.HospitalPhone))
// Display lab results
fmt.Println("LAB TEST RESULTS:")
if len(result.LabResults) > 0 {
fmt.Printf("Total Tests: %d\n\n", len(result.LabResults))
// Group by panel if available
byPanel := make(map[string][]LabResultItem)
for _, test := range result.LabResults {
panel := "OTHER"
if test.PanelName != nil {
panel = *test.PanelName
}
byPanel[panel] = append(byPanel[panel], test)
}
for panel, tests := range byPanel {
fmt.Printf(" %s:\n", panel)
for _, test := range tests {
flag := ""
if test.Flag != nil {
flag = fmt.Sprintf(" [%s]", *test.Flag)
}
units := ""
if test.Units != nil {
units = fmt.Sprintf(" %s", *test.Units)
}
refRange := ""
if test.ReferenceRange != nil {
refRange = fmt.Sprintf(" (ref: %s)", *test.ReferenceRange)
}
fmt.Printf(" • %s: %s%s%s%s\n", stringOrNA(test.TestName), stringOrNA(test.ResultValue), units, refRange, flag)
if test.Interpretation != nil {
fmt.Printf(" Note: %s\n", *test.Interpretation)
}
}
fmt.Println()
}
} else {
fmt.Println(" No lab results extracted.\n")
}
// Return the full structured result for programmatic use
parseChunks := make([]string, 0)
for _, chunk := range parseRun.Output.Chunks {
parseChunks = append(parseChunks, chunk.Content)
}
return map[string]interface{}{
"status": "success",
"data": result,
"parseChunks": parseChunks,
}, nil
}
func stringOrNA(s *string) string {
if s == nil {
return "N/A"
}
return *s
}
func intOrNA(i *int) string {
if i == nil {
return "N/A"
}
return fmt.Sprintf("%d", *i)
}
func main() {
flag.Parse()
args := flag.Args()
if len(args) == 0 {
fmt.Fprintln(os.Stderr, "Usage: solution <path-to-pdf>")
os.Exit(1)
}
filePath := args[0]
result, err := processLabResultsExtractor(filePath)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Println("\n=== RAW JSON OUTPUT ===")
jsonBytes, err := json.MarshalIndent(result["data"], "", " ")
if err != nil {
fmt.Fprintf(os.Stderr, "Error marshaling JSON: %v\n", err)
os.Exit(1)
}
fmt.Println(string(jsonBytes))
}// Deploy the "Lab Results Extractor" pipeline to YOUR Extend account.
//
// The workflow below is fully self-contained — every EXTRACT/CLASSIFY/SPLIT
// step carries its extractor/classifier/splitter config INLINE, so this is a
// single API call. No processors to create or wire up beforehand.
// Idempotent: the created workflow id is cached in .extend/lab-results-extractor.json,
// so re-running updates the existing workflow instead of duplicating it.
//
// Usage:
// export EXTEND_API_KEY=sk_... (from https://dashboard.extend.ai → API Keys)
// npx tsx provision.ts
//
// Generated by doc1 (template: lab-results-extractor).
import fs from "node:fs";
import path from "node:path";
const API = "https://api.extend.ai";
const VERSION = "2026-02-09";
const API_KEY = process.env.EXTEND_API_KEY;
if (!API_KEY) { console.error("Set EXTEND_API_KEY first."); process.exit(1); }
const STATE_DIR = path.join(process.cwd(), ".extend");
const STATE_FILE = path.join(STATE_DIR, "lab-results-extractor.json");
type State = { workflowId?: string };
const state: State = fs.existsSync(STATE_FILE)
? JSON.parse(fs.readFileSync(STATE_FILE, "utf8"))
: {};
function saveState() {
fs.mkdirSync(STATE_DIR, { recursive: true });
fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
}
async function api(method: string, pathName: string, body?: unknown) {
const res = await fetch(API + pathName, {
method,
headers: {
Authorization: `Bearer ${API_KEY}`,
"x-extend-api-version": VERSION,
...(body ? { "Content-Type": "application/json" } : {}),
},
body: body ? JSON.stringify(body) : undefined,
});
const data = await res.json().catch(() => ({}));
if (!res.ok) throw new Error(`${method} ${pathName} failed (${res.status}): ${JSON.stringify(data).slice(0, 300)}`);
return data;
}
// ── Workflow definition — extractor/classifier/splitter configs inline ──────
const WORKFLOW = {
"name": "Lab Results Extractor Processing Pipeline",
"steps": [
{
"name": "startTrigger1",
"type": "TRIGGER",
"next": [
{
"step": "parse1"
}
]
},
{
"name": "parse1",
"type": "PARSE",
"config": {
"parseConfig": {
"blockOptions": {
"text": {
"agentic": {
"enabled": true
},
"signatureDetectionEnabled": true
},
"tables": {
"agentic": {
"enabled": true
},
"tableHeaderContinuationEnabled": true
},
"figures": {
"enabled": true
}
},
"chunkingStrategy": {
"type": "page",
"options": {}
}
}
},
"next": [
{
"step": "extraction2"
}
]
},
{
"name": "extraction2",
"type": "EXTRACT",
"config": {
"extractorConfig": {
"schema": {
"type": "object",
"properties": {
"age": {
"type": [
"integer",
"null"
],
"description": "Patient's age in years"
},
"gender": {
"type": [
"string",
"null"
],
"description": "Patient gender (M/F)"
},
"patient_id": {
"type": [
"string",
"null"
],
"description": "Unique patient identifier"
},
"lab_results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"test_name": {
"type": [
"string",
"null"
],
"description": "Name of the lab test"
},
"result_value": {
"type": [
"string",
"null"
],
"description": "Numerical or descriptive result value"
},
"reference_range": {
"type": [
"string",
"null"
],
"description": "Normal reference range for the test"
}
}
},
"description": "Array of individual lab test results"
},
"specimen_id": {
"type": [
"string",
"null"
],
"description": "Unique specimen identifier"
},
"patient_name": {
"type": [
"string",
"null"
],
"description": "Full name of the patient"
},
"date_of_birth": {
"type": [
"string",
"null"
],
"description": "Patient's date of birth in MM/DD/YYYY format"
},
"report_status": {
"type": [
"string",
"null"
],
"description": "Status of the report (e.g., Final, Preliminary)"
},
"fasting_status": {
"type": [
"string",
"null"
],
"description": "Whether patient was fasting (Y/N)"
},
"report_datetime": {
"type": [
"string",
"null"
],
"description": "Date and time report was finalized in MM/DD/YYYY / HH:MM format"
},
"collection_datetime": {
"type": [
"string",
"null"
],
"description": "Date and time specimen was collected in MM/DD/YYYY / HH:MM TZ format"
}
}
}
}
}
}
]
};
async function main() {
console.log(`Deploying "${WORKFLOW.name}"…`);
if (state.workflowId) {
console.log(`✓ workflow already provisioned (${state.workflowId}) — updating steps`);
await api("POST", `/workflows/${state.workflowId}`, { steps: WORKFLOW.steps });
} else {
// Reuse an existing workflow with the same name if one exists (e.g. a
// previous run's state file was lost) instead of creating a duplicate.
try {
const list = await api("GET", `/workflows?name=${encodeURIComponent(WORKFLOW.name)}`);
const items = (list.data ?? list.items ?? []) as Array<{ name?: string; id?: string }>;
const existing = items.find((x) => x.name === WORKFLOW.name);
if (existing?.id) {
state.workflowId = existing.id; saveState();
console.log(`✓ workflow "${WORKFLOW.name}" found in your account (${existing.id}) — updating steps`);
await api("POST", `/workflows/${existing.id}`, { steps: WORKFLOW.steps });
}
} catch { /* lookup is best-effort; fall through to create */ }
if (!state.workflowId) {
const created = await api("POST", "/workflows", WORKFLOW);
const wfId = created.id ?? created.workflow?.id;
if (!wfId) throw new Error("Could not read created workflow id from response");
state.workflowId = wfId; saveState();
console.log(`+ created workflow (${wfId})`);
}
}
// Deploy the current draft as a new version so the workflow is runnable —
// best-effort: some accounts/plans may not require this explicit step.
await api("POST", `/workflows/${state.workflowId}/versions`, {}).catch(() => {});
console.log("\nDone. Run documents through it with:");
console.log(` POST ${API}/workflow_runs { workflow: { id: "${state.workflowId}" }, file: { url: "https://…" } }`);
console.log("Or open the workflow in the Extend dashboard to review and deploy it.");
}
main().catch((e) => { console.error(e.message ?? e); process.exit(1); });
import json
import os
import sys
from pathlib import Path
from typing import Any, Optional, TypedDict
import httpx
API = "https://api.extend.ai"
VERSION = "2026-02-09"
API_KEY = os.environ.get("EXTEND_API_KEY")
if not API_KEY:
print("Set EXTEND_API_KEY first.", file=sys.stderr)
sys.exit(1)
STATE_DIR = Path.cwd() / ".extend"
STATE_FILE = STATE_DIR / "lab-results-extractor.json"
class State(TypedDict, total=False):
workflowId: Optional[str]
state: State = {}
if STATE_FILE.exists():
state = json.loads(STATE_FILE.read_text())
def save_state() -> None:
STATE_DIR.mkdir(parents=True, exist_ok=True)
STATE_FILE.write_text(json.dumps(state, indent=2))
def api(method: str, path_name: str, body: Optional[Any] = None) -> Any:
headers = {
"Authorization": f"Bearer {API_KEY}",
"x-extend-api-version": VERSION,
}
if body:
headers["Content-Type"] = "application/json"
response = httpx.request(
method,
API + path_name,
headers=headers,
json=body if body else None,
)
try:
data = response.json()
except Exception:
data = {}
if not response.is_success:
error_msg = json.dumps(data)[:300]
raise RuntimeError(f"{method} {path_name} failed ({response.status_code}): {error_msg}")
return data
WORKFLOW = {
"name": "Lab Results Extractor Processing Pipeline",
"steps": [
{
"name": "startTrigger1",
"type": "TRIGGER",
"next": [{"step": "parse1"}],
},
{
"name": "parse1",
"type": "PARSE",
"config": {
"parseConfig": {
"blockOptions": {
"text": {
"agentic": {"enabled": True},
"signatureDetectionEnabled": True,
},
"tables": {
"agentic": {"enabled": True},
"tableHeaderContinuationEnabled": True,
},
"figures": {"enabled": True},
},
"chunkingStrategy": {"type": "page", "options": {}},
}
},
"next": [{"step": "extraction2"}],
},
{
"name": "extraction2",
"type": "EXTRACT",
"config": {
"extractorConfig": {
"schema": {
"type": "object",
"properties": {
"age": {
"type": ["integer", "null"],
"description": "Patient's age in years",
},
"gender": {
"type": ["string", "null"],
"description": "Patient gender (M/F)",
},
"patient_id": {
"type": ["string", "null"],
"description": "Unique patient identifier",
},
"lab_results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"test_name": {
"type": ["string", "null"],
"description": "Name of the lab test",
},
"result_value": {
"type": ["string", "null"],
"description": "Numerical or descriptive result value",
},
"reference_range": {
"type": ["string", "null"],
"description": "Normal reference range for the test",
},
},
},
"description": "Array of individual lab test results",
},
"specimen_id": {
"type": ["string", "null"],
"description": "Unique specimen identifier",
},
"patient_name": {
"type": ["string", "null"],
"description": "Full name of the patient",
},
"date_of_birth": {
"type": ["string", "null"],
"description": "Patient's date of birth in MM/DD/YYYY format",
},
"report_status": {
"type": ["string", "null"],
"description": "Status of the report (e.g., Final, Preliminary)",
},
"fasting_status": {
"type": ["string", "null"],
"description": "Whether patient was fasting (Y/N)",
},
"report_datetime": {
"type": ["string", "null"],
"description": "Date and time report was finalized in MM/DD/YYYY / HH:MM format",
},
"collection_datetime": {
"type": ["string", "null"],
"description": "Date and time specimen was collected in MM/DD/YYYY / HH:MM TZ format",
},
},
}
}
},
},
],
}
def main() -> None:
print(f'Deploying "{WORKFLOW["name"]}…')
if state.get("workflowId"):
print(f'✓ workflow already provisioned ({state["workflowId"]}) — updating steps')
api("POST", f'/workflows/{state["workflowId"]}', {"steps": WORKFLOW["steps"]})
else:
try:
list_response = api("GET", f'/workflows?name={WORKFLOW["name"]}')
items = list_response.get("data") or list_response.get("items") or []
existing = next(
(x for x in items if x.get("name") == WORKFLOW["name"]),
None,
)
if existing and existing.get("id"):
state["workflowId"] = existing["id"]
save_state()
print(f'✓ workflow "{WORKFLOW["name"]}" found in your account ({existing["id"]}) — updating steps')
api("POST", f'/workflows/{existing["id"]}', {"steps": WORKFLOW["steps"]})
except Exception:
pass
if not state.get("workflowId"):
created = api("POST", "/workflows", WORKFLOW)
wf_id = created.get("id") or created.get("workflow", {}).get("id")
if not wf_id:
raise RuntimeError("Could not read created workflow id from response")
state["workflowId"] = wf_id
save_state()
print(f"+ created workflow ({wf_id})")
try:
api("POST", f'/workflows/{state["workflowId"]}/versions', {})
except Exception:
pass
print("\nDone. Run documents through it with:")
print(f' POST {API}/workflow_runs {{ "workflow": {{ "id": "{state["workflowId"]}" }}, "file": {{ "url": "https://…" }} }}')
print("Or open the workflow in the Extend dashboard to review and deploy it.")
if __name__ == "__main__":
try:
main()
except Exception as e:
print(str(e), file=sys.stderr)
sys.exit(1)import java.io.*;
import java.net.URI;
import java.net.http.*;
import java.nio.file.*;
import java.util.*;
import com.google.gson.*;
public class ProvisionLabResultsExtractor {
private static final String API = "https://api.extend.ai";
private static final String VERSION = "2026-02-09";
private static final String API_KEY = System.getenv("EXTEND_API_KEY");
private static final Path STATE_DIR = Paths.get(System.getProperty("user.dir"), ".extend");
private static final Path STATE_FILE = STATE_DIR.resolve("lab-results-extractor.json");
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
private static final HttpClient httpClient = HttpClient.newHttpClient();
static class State {
String workflowId;
}
static {
if (API_KEY == null || API_KEY.isEmpty()) {
System.err.println("Set EXTEND_API_KEY first.");
System.exit(1);
}
}
private static State loadState() throws IOException {
if (Files.exists(STATE_FILE)) {
String json = Files.readString(STATE_FILE);
return gson.fromJson(json, State.class);
}
return new State();
}
private static void saveState(State state) throws IOException {
Files.createDirectories(STATE_DIR);
Files.writeString(STATE_FILE, gson.toJson(state));
}
private static JsonElement api(String method, String pathName, JsonElement body) throws Exception {
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
.uri(URI.create(API + pathName))
.header("Authorization", "Bearer " + API_KEY)
.header("x-extend-api-version", VERSION);
if (body != null) {
requestBuilder.method(method, HttpRequest.BodyPublishers.ofString(gson.toJson(body)))
.header("Content-Type", "application/json");
} else {
requestBuilder.method(method, HttpRequest.BodyPublishers.noBody());
}
HttpResponse<String> response = httpClient.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofString());
JsonElement data = JsonParser.parseString(response.body().isEmpty() ? "{}" : response.body());
if (response.statusCode() < 200 || response.statusCode() >= 300) {
String errorMsg = data.toString();
if (errorMsg.length() > 300) errorMsg = errorMsg.substring(0, 300);
throw new Exception(method + " " + pathName + " failed (" + response.statusCode() + "): " + errorMsg);
}
return data;
}
private static JsonElement buildWorkflow() {
JsonObject workflow = new JsonObject();
workflow.addProperty("name", "Lab Results Extractor Processing Pipeline");
JsonArray steps = new JsonArray();
JsonObject trigger = new JsonObject();
trigger.addProperty("name", "startTrigger1");
trigger.addProperty("type", "TRIGGER");
JsonArray triggerNext = new JsonArray();
JsonObject triggerNextItem = new JsonObject();
triggerNextItem.addProperty("step", "parse1");
triggerNext.add(triggerNextItem);
trigger.add("next", triggerNext);
steps.add(trigger);
JsonObject parse = new JsonObject();
parse.addProperty("name", "parse1");
parse.addProperty("type", "PARSE");
JsonObject parseConfig = new JsonObject();
JsonObject parseOptions = new JsonObject();
JsonObject blockOptions = new JsonObject();
JsonObject textBlock = new JsonObject();
JsonObject textAgentic = new JsonObject();
textAgentic.addProperty("enabled", true);
textBlock.add("agentic", textAgentic);
textBlock.addProperty("signatureDetectionEnabled", true);
blockOptions.add("text", textBlock);
JsonObject tablesBlock = new JsonObject();
JsonObject tablesAgentic = new JsonObject();
tablesAgentic.addProperty("enabled", true);
tablesBlock.add("agentic", tablesAgentic);
tablesBlock.addProperty("tableHeaderContinuationEnabled", true);
blockOptions.add("tables", tablesBlock);
JsonObject figuresBlock = new JsonObject();
figuresBlock.addProperty("enabled", true);
blockOptions.add("figures", figuresBlock);
parseOptions.add("blockOptions", blockOptions);
JsonObject chunkingStrategy = new JsonObject();
chunkingStrategy.addProperty("type", "page");
chunkingStrategy.add("options", new JsonObject());
parseOptions.add("chunkingStrategy", chunkingStrategy);
parseConfig.add("parseConfig", parseOptions);
parse.add("config", parseConfig);
JsonArray parseNext = new JsonArray();
JsonObject parseNextItem = new JsonObject();
parseNextItem.addProperty("step", "extraction2");
parseNext.add(parseNextItem);
parse.add("next", parseNext);
steps.add(parse);
JsonObject extraction = new JsonObject();
extraction.addProperty("name", "extraction2");
extraction.addProperty("type", "EXTRACT");
JsonObject extractionConfig = new JsonObject();
JsonObject extractorConfig = new JsonObject();
JsonObject schema = new JsonObject();
schema.addProperty("type", "object");
JsonObject properties = new JsonObject();
properties.add("age", buildProperty("integer", "Patient's age in years"));
properties.add("gender", buildProperty("string", "Patient gender (M/F)"));
properties.add("patient_id", buildProperty("string", "Unique patient identifier"));
JsonObject labResults = new JsonObject();
labResults.add("type", gson.toJsonTree(new String[]{"array"}));
JsonObject labResultsItems = new JsonObject();
labResultsItems.addProperty("type", "object");
JsonObject labResultsProperties = new JsonObject();
labResultsProperties.add("test_name", buildProperty("string", "Name of the lab test"));
labResultsProperties.add("result_value", buildProperty("string", "Numerical or descriptive result value"));
labResultsProperties.add("reference_range", buildProperty("string", "Normal reference range for the test"));
labResultsItems.add("properties", labResultsProperties);
labResults.add("items", labResultsItems);
labResults.addProperty("description", "Array of individual lab test results");
properties.add("lab_results", labResults);
properties.add("specimen_id", buildProperty("string", "Unique specimen identifier"));
properties.add("patient_name", buildProperty("string", "Full name of the patient"));
properties.add("date_of_birth", buildProperty("string", "Patient's date of birth in MM/DD/YYYY format"));
properties.add("report_status", buildProperty("string", "Status of the report (e.g., Final, Preliminary)"));
properties.add("fasting_status", buildProperty("string", "Whether patient was fasting (Y/N)"));
properties.add("report_datetime", buildProperty("string", "Date and time report was finalized in MM/DD/YYYY / HH:MM format"));
properties.add("collection_datetime", buildProperty("string", "Date and time specimen was collected in MM/DD/YYYY / HH:MM TZ format"));
schema.add("properties", properties);
extractorConfig.add("schema", schema);
extractionConfig.add("extractorConfig", extractorConfig);
extraction.add("config", extractionConfig);
steps.add(extraction);
workflow.add("steps", steps);
return workflow;
}
private static JsonObject buildProperty(String type, String description) {
JsonObject prop = new JsonObject();
prop.add("type", gson.toJsonTree(new String[]{type, "null"}));
prop.addProperty("description", description);
return prop;
}
public static void main(String[] args) {
try {
State state = loadState();
JsonElement workflow = buildWorkflow();
String workflowName = workflow.getAsJsonObject().get("name").getAsString();
JsonArray steps = workflow.getAsJsonObject().getAsJsonArray("steps");
System.out.println("Deploying \"" + workflowName + "\"…");
if (state.workflowId != null && !state.workflowId.isEmpty()) {
System.out.println("✓ workflow already provisioned (" + state.workflowId + ") — updating steps");
JsonObject updateBody = new JsonObject();
updateBody.add("steps", steps);
api("POST", "/workflows/" + state.workflowId, updateBody);
} else {
try {
String listUrl = "/workflows?name=" + java.net.URLEncoder.encode(workflowName, java.nio.charset.StandardCharsets.UTF_8);
JsonElement listResponse = api("GET", listUrl, null);
JsonArray items = listResponse.getAsJsonObject().has("data")
? listResponse.getAsJsonObject().getAsJsonArray("data")
: listResponse.getAsJsonObject().getAsJsonArray("items");
for (JsonElement item : items) {
if (item.getAsJsonObject().has("name") &&
item.getAsJsonObject().get("name").getAsString().equals(workflowName) &&
item.getAsJsonObject().has("id")) {
String existingId = item.getAsJsonObject().get("id").getAsString();
state.workflowId = existingId;
saveState(state);
System.out.println("✓ workflow \"" + workflowName + "\" found in your account (" + existingId + ") — updating steps");
JsonObject updateBody = new JsonObject();
updateBody.add("steps", steps);
api("POST", "/workflows/" + existingId, updateBody);
break;
}
}
} catch (Exception e) {
// lookup is best-effort; fall through to create
}
if (state.workflowId == null || state.workflowId.isEmpty()) {
JsonElement created = api("POST", "/workflows", workflow);
String wfId = null;
if (created.getAsJsonObject().has("id")) {
wfId = created.getAsJsonObject().get("id").getAsString();
} else if (created.getAsJsonObject().has("workflow")) {
wfId = created.getAsJsonObject().getAsJsonObject("workflow").get("id").getAsString();
}
if (wfId == null) {
throw new Exception("Could not read created workflow id from response");
}
state.workflowId = wfId;
saveState(state);
System.out.println("+ created workflow (" + wfId + ")");
}
}
try {
api("POST", "/workflows/" + state.workflowId + "/versions", new JsonObject());
} catch (Exception e) {
// best-effort: some accounts/plans may not require this explicit step
}
System.out.println("\nDone. Run documents through it with:");
System.out.println(" POST " + API + "/workflow_runs { workflow: { id: \"" + state.workflowId + "\" }, file: { url: \"https://…\" } }");
System.out.println("Or open the workflow in the Extend dashboard to review and deploy it.");
} catch (Exception e) {
System.err.println(e.getMessage() != null ? e.getMessage() : e);
System.exit(1);
}
}
}package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"path/filepath"
)
const (
API = "https://api.extend.ai"
VERSION = "2026-02-09"
)
type State struct {
WorkflowID string `json:"workflowId,omitempty"`
}
var (
apiKey string
stateDir string
stateFile string
state State
)
func init() {
apiKey = os.Getenv("EXTEND_API_KEY")
if apiKey == "" {
fmt.Fprintf(os.Stderr, "Set EXTEND_API_KEY first.\n")
os.Exit(1)
}
wd, err := os.Getwd()
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to get working directory: %v\n", err)
os.Exit(1)
}
stateDir = filepath.Join(wd, ".extend")
stateFile = filepath.Join(stateDir, "lab-results-extractor.json")
// Load existing state if available
if data, err := os.ReadFile(stateFile); err == nil {
json.Unmarshal(data, &state)
}
}
func saveState() error {
if err := os.MkdirAll(stateDir, 0755); err != nil {
return err
}
data, err := json.MarshalIndent(state, "", " ")
if err != nil {
return err
}
return os.WriteFile(stateFile, data, 0644)
}
func apiCall(method, pathName string, body interface{}) (map[string]interface{}, error) {
url := API + pathName
var bodyReader io.Reader
if body != nil {
bodyBytes, err := json.Marshal(body)
if err != nil {
return nil, err
}
bodyReader = bytes.NewReader(bodyBytes)
}
req, err := http.NewRequest(method, url, bodyReader)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey))
req.Header.Set("x-extend-api-version", VERSION)
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var data map[string]interface{}
json.Unmarshal(respBody, &data)
if !resp.Ok {
errMsg := string(respBody)
if len(errMsg) > 300 {
errMsg = errMsg[:300]
}
return nil, fmt.Errorf("%s %s failed (%d): %s", method, pathName, resp.StatusCode, errMsg)
}
return data, nil
}
var workflow = map[string]interface{}{
"name": "Lab Results Extractor Processing Pipeline",
"steps": []map[string]interface{}{
{
"name": "startTrigger1",
"type": "TRIGGER",
"next": []map[string]interface{}{
{"step": "parse1"},
},
},
{
"name": "parse1",
"type": "PARSE",
"config": map[string]interface{}{
"parseConfig": map[string]interface{}{
"blockOptions": map[string]interface{}{
"text": map[string]interface{}{
"agentic": map[string]interface{}{
"enabled": true,
},
"signatureDetectionEnabled": true,
},
"tables": map[string]interface{}{
"agentic": map[string]interface{}{
"enabled": true,
},
"tableHeaderContinuationEnabled": true,
},
"figures": map[string]interface{}{
"enabled": true,
},
},
"chunkingStrategy": map[string]interface{}{
"type": "page",
"options": map[string]interface{}{},
},
},
},
"next": []map[string]interface{}{
{"step": "extraction2"},
},
},
{
"name": "extraction2",
"type": "EXTRACT",
"config": map[string]interface{}{
"extractorConfig": map[string]interface{}{
"schema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"age": map[string]interface{}{
"type": []string{"integer", "null"},
"description": "Patient's age in years",
},
"gender": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Patient gender (M/F)",
},
"patient_id": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Unique patient identifier",
},
"lab_results": map[string]interface{}{
"type": "array",
"items": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"test_name": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Name of the lab test",
},
"result_value": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Numerical or descriptive result value",
},
"reference_range": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Normal reference range for the test",
},
},
},
"description": "Array of individual lab test results",
},
"specimen_id": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Unique specimen identifier",
},
"patient_name": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Full name of the patient",
},
"date_of_birth": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Patient's date of birth in MM/DD/YYYY format",
},
"report_status": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Status of the report (e.g., Final, Preliminary)",
},
"fasting_status": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Whether patient was fasting (Y/N)",
},
"report_datetime": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Date and time report was finalized in MM/DD/YYYY / HH:MM format",
},
"collection_datetime": map[string]interface{}{
"type": []string{"string", "null"},
"description": "Date and time specimen was collected in MM/DD/YYYY / HH:MM TZ format",
},
},
},
},
},
},
},
}
func main() {
workflowName := workflow["name"].(string)
fmt.Printf("Deploying \"%s\"…\n", workflowName)
if state.WorkflowID != "" {
fmt.Printf("✓ workflow already provisioned (%s) — updating steps\n", state.WorkflowID)
steps := workflow["steps"]
_, err := apiCall("POST", fmt.Sprintf("/workflows/%s", state.WorkflowID), map[string]interface{}{"steps": steps})
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
} else {
// Try to reuse an existing workflow with the same name
listURL := fmt.Sprintf("/workflows?name=%s", url.QueryEscape(workflowName))
list, err := apiCall("GET", listURL, nil)
if err == nil {
var items []map[string]interface{}
if data, ok := list["data"].([]interface{}); ok {
for _, item := range data {
items = append(items, item.(map[string]interface{}))
}
} else if data, ok := list["items"].([]interface{}); ok {
for _, item := range data {
items = append(items, item.(map[string]interface{}))
}
}
for _, item := range items {
if name, ok := item["name"].(string); ok && name == workflowName {
if id, ok := item["id"].(string); ok {
state.WorkflowID = id
saveState()
fmt.Printf("✓ workflow \"%s\" found in your account (%s) — updating steps\n", workflowName, id)
steps := workflow["steps"]
_, err := apiCall("POST", fmt.Sprintf("/workflows/%s", id), map[string]interface{}{"steps": steps})
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
break
}
}
}
}
if state.WorkflowID == "" {
created, err := apiCall("POST", "/workflows", workflow)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
wfID := ""
if id, ok := created["id"].(string); ok {
wfID = id
} else if wf, ok := created["workflow"].(map[string]interface{}); ok {
if id, ok := wf["id"].(string); ok {
wfID = id
}
}
if wfID == "" {
fmt.Fprintf(os.Stderr, "Could not read created workflow id from response\n")
os.Exit(1)
}
state.WorkflowID = wfID
saveState()
fmt.Printf("+ created workflow (%s)\n", wfID)
}
}
// Deploy the current draft as a new version
apiCall("POST", fmt.Sprintf("/workflows/%s/versions", state.WorkflowID), map[string]interface{}{})
fmt.Println("\nDone. Run documents through it with:")
fmt.Printf(" POST %s/workflow_runs { workflow: { id: \"%s\" }, file: { url: \"https://…\" } }\n", API, state.WorkflowID)
fmt.Println("Or open the workflow in the Extend dashboard to review and deploy it.")
}This template processes clinical laboratory results from diagnostic facilities like Quest Diagnostics, Labcorp, BiorReference, etc. It captures patient demographics, specimen details, and comprehensive metabolic panel results with reference ranges. Ideal for engineering teams building tools that require extracting patient test data and medical records.