Skip to main content

Multi-Dimensional Extraction

The mde resource submits and manages multi-dimensional extraction jobs. It is available via client.mde.

Multi-dimensional extraction is for table-like or entity-centric outputs: you define a schema with columns, then the AI returns rows and cells that match that schema.

import asyncio
from biolevate import BiolevateClient, EntityColumnInput, EntitySchemaInput

async def main():
async with BiolevateClient(
base_url="https://<api-domain>",
token="<your-pat>",
) as client:
schema = EntitySchemaInput(
name="compounds",
description="Compounds studied in the source documents and their administered doses",
columns=[
EntityColumnInput(
key="compound",
label="Compound",
type="ENTITY_COLUMN_TYPE_STRING",
role="ENTITY_COLUMN_ROLE_IDENTIFIER",
description="Compound or treatment name",
is_row_key=True,
),
EntityColumnInput(
key="dose",
label="Dose",
type="ENTITY_COLUMN_TYPE_FLOAT",
role="ENTITY_COLUMN_ROLE_VALUE",
),
],
)

job = await client.mde.create_job(
schema=schema,
file_ids=["a1b2c3d4-e5f6-7890-abcd-ef1234567890"],
)
print(f"Job created: {job.job_id} (status: {job.status})")

asyncio.run(main())

Schema Fields

EntitySchemaInput describes the table to extract. All fields are optional.

FieldTypeDescription
namestr | NoneShort, machine-friendly name of the entity schema (for example, compounds)
descriptionstr | NoneNatural-language description of what the table represents, giving the AI extra context
columnslist[EntityColumnInput] | NoneThe columns to extract for each row
tip

A concise description helps the backend understand the intent of the whole schema, complementing the per-column label, description, and role. Leave it empty if the name and columns are already self-explanatory.

Methods

create_job()

Submit a new multi-dimensional extraction job. Define the entity schema and target documents by file_ids, collection_ids, or both.

from biolevate import EntityColumnInput, EntitySchemaInput

schema = EntitySchemaInput(
name="study_interventions",
description="Interventions evaluated per study population and their effect sizes",
columns=[
EntityColumnInput(
key="intervention",
label="Intervention",
type="ENTITY_COLUMN_TYPE_STRING",
role="ENTITY_COLUMN_ROLE_IDENTIFIER",
is_row_key=True,
),
EntityColumnInput(
key="population",
label="Population",
type="ENTITY_COLUMN_TYPE_STRING",
role="ENTITY_COLUMN_ROLE_GROUP",
),
EntityColumnInput(
key="effect_size",
label="Effect Size",
type="ENTITY_COLUMN_TYPE_FLOAT",
role="ENTITY_COLUMN_ROLE_VALUE",
),
],
)

job = await client.mde.create_job(
schema=schema,
file_ids=["a1b2c3d4-e5f6-7890-abcd-ef1234567890"],
)
ParameterTypeDefaultDescription
schemaEntitySchemaInputEntity schema describing columns to extract
file_idslist[str] | NoneNoneUUIDs of individual EliseFiles to process
collection_idslist[str] | NoneNoneUUIDs of Collections to process
configJobLaunchConfig | NoneNoneJob launch behaviour, e.g. JobLaunchConfig(skip_unindexed_files=True)

See Common Patterns: Job Launch Config for details on config.

Returns Job

Raises AuthenticationError, APIError

Column Roles

EntityColumnInput.role is optional. It describes the semantic function of a column, beyond type and is_row_key, and helps the backend interpret how columns relate to each other.

If you are unsure, omit role or set it to ENTITY_COLUMN_ROLE_UNSPECIFIED. The backend can infer intent from key, label, description, type, and is_row_key.

EntityColumnInput(
key="custom_field",
label="Custom field",
description="A custom field where the semantic role is not known",
# role omitted intentionally
)

Available Role Values

RoleMeaningExamples
ENTITY_COLUMN_ROLE_UNSPECIFIEDUnknown or intentionally unspecified role. The backend infers from the rest of the schema.custom_field, misc_value
ENTITY_COLUMN_ROLE_IDENTIFIERStable ID, code, acronym, accession number, DOI, NCT ID, PMID, or registry ID. Helps recognize entities.study_id, nct_id, doi, pmid, accession_number
ENTITY_COLUMN_ROLE_LABELHuman-readable name or display label. Not necessarily stable or unique.study_name, intervention_name, endpoint_name, biomarker_name
ENTITY_COLUMN_ROLE_GROUPGroup, category, arm, cohort, population, subgroup, sex, age group, or disease severity. Scopes values.treatment_arm, dose_group, cohort, population, sex, age_group
ENTITY_COLUMN_ROLE_TIMEPOINTVisit, timepoint, cycle, follow-up window, day, month, or week. Aligns repeated measurements over time.week, visit, timepoint, cycle, month, assessment_window
ENTITY_COLUMN_ROLE_VALUEMeasured value: score, percentage, count, ratio, p-value, number, or textual result. Usually not a row key.response_rate, mean_change, event_count, hazard_ratio, p_value, score
ENTITY_COLUMN_ROLE_UNITUnit, scale, or symbol separated from the value. Helps interpret adjacent values.unit, dose_unit, measurement_unit, rate_unit, scale
ENTITY_COLUMN_ROLE_DEFINITIONDefinition of a concept, endpoint, criterion, threshold, or inclusion rule.endpoint_definition, response_definition, inclusion_criteria, event_definition
ENTITY_COLUMN_ROLE_COMMENTNote, caveat, footnote, extraction comment, or additional context.comment, note, footnote, caveat, additional_context

Role Examples

from biolevate import EntityColumnInput, EntitySchemaInput

schema = EntitySchemaInput(
name="ClinicalOutcome",
columns=[
EntityColumnInput(
key="trial_id",
label="Trial ID",
type="ENTITY_COLUMN_TYPE_STRING",
role="ENTITY_COLUMN_ROLE_IDENTIFIER",
is_row_key=True,
),
EntityColumnInput(
key="treatment_arm",
label="Treatment arm",
type="ENTITY_COLUMN_TYPE_STRING",
role="ENTITY_COLUMN_ROLE_GROUP",
is_row_key=True,
),
EntityColumnInput(
key="week",
label="Week",
type="ENTITY_COLUMN_TYPE_STRING",
role="ENTITY_COLUMN_ROLE_TIMEPOINT",
is_row_key=True,
),
EntityColumnInput(
key="response_rate",
label="Response rate (%)",
type="ENTITY_COLUMN_TYPE_FLOAT",
role="ENTITY_COLUMN_ROLE_VALUE",
is_row_key=False,
),
EntityColumnInput(
key="unit",
label="Unit",
type="ENTITY_COLUMN_TYPE_STRING",
role="ENTITY_COLUMN_ROLE_UNIT",
is_row_key=False,
),
EntityColumnInput(
key="response_definition",
label="Response definition",
type="ENTITY_COLUMN_TYPE_STRING",
role="ENTITY_COLUMN_ROLE_DEFINITION",
description="How response was defined in the source document",
),
EntityColumnInput(
key="note",
label="Note",
type="ENTITY_COLUMN_TYPE_STRING",
role="ENTITY_COLUMN_ROLE_COMMENT",
),
],
)

Practical Rule of Thumb

  • Measured result -> ENTITY_COLUMN_ROLE_VALUE
  • "For which group?" -> ENTITY_COLUMN_ROLE_GROUP
  • "When?" -> ENTITY_COLUMN_ROLE_TIMEPOINT
  • Stable ID or code -> ENTITY_COLUMN_ROLE_IDENTIFIER
  • Human-readable name -> ENTITY_COLUMN_ROLE_LABEL
  • Unit or scale -> ENTITY_COLUMN_ROLE_UNIT
  • Concept or criterion definition -> ENTITY_COLUMN_ROLE_DEFINITION
  • Note or caveat -> ENTITY_COLUMN_ROLE_COMMENT
  • Not sure -> omit role or use ENTITY_COLUMN_ROLE_UNSPECIFIED

get_job()

Get the current status of a multi-dimensional extraction job. Poll until status is SUCCESS, FAILED, or ABORTED.

job = await client.mde.get_job(job_id)
print(f"Status: {job.status}")
ParameterTypeDescription
job_idstrUUID of the multi-dimensional extraction job

Returns Job

Raises NotFoundError, AuthenticationError, APIError


list_jobs()

List all multi-dimensional extraction jobs for the current user.

page = await client.mde.list_jobs(page=0, page_size=20)
for job in page.data:
print(f"{job.job_id}: {job.status}")
ParameterTypeDefaultDescription
pageint0Page number (0-based)
page_sizeint20Number of jobs per page
sort_bystr | NoneNoneField to sort by
sort_orderstr"asc"Sort direction

Returns JobPage

Raises AuthenticationError, APIError


get_job_outputs()

Retrieve extracted rows and cells from a completed job.

outputs = await client.mde.get_job_outputs(job_id)
entity_extraction = outputs.entity_extraction

for row in entity_extraction.rows or []:
for cell in row.cells or []:
print(f"{cell.column_key}: {cell.value}")
ParameterTypeDescription
job_idstrUUID of the multi-dimensional extraction job

Returns MDEJobOutputs

Raises NotFoundError, AuthenticationError, APIError


get_job_inputs()

Retrieve the original file targets and entity schema submitted to the job.

inputs = await client.mde.get_job_inputs(job_id)
print(inputs.var_schema.name)

The generated Python model exposes the JSON field schema as var_schema to avoid conflicting with Pydantic's schema() method.

ParameterTypeDescription
job_idstrUUID of the multi-dimensional extraction job

Returns MDEJobInputs

Raises NotFoundError, AuthenticationError, APIError


get_job_annotations()

Retrieve source annotations for all extracted rows and cells.

annotations = await client.mde.get_job_annotations(job_id)
annotation_map = {a.id.id: a for a in annotations}
ParameterTypeDescription
job_idstrUUID of the multi-dimensional extraction job

Returns list[Annotation]

Raises NotFoundError, AuthenticationError, APIError

Full Example — Create, Poll, and Retrieve Rows

import asyncio
from biolevate import BiolevateClient, EntityColumnInput, EntitySchemaInput

TERMINAL_STATUSES = {"SUCCESS", "FAILED", "ABORTED"}

async def main():
async with BiolevateClient(
base_url="https://<api-domain>",
token="<your-pat>",
) as client:
schema = EntitySchemaInput(
name="compounds",
description="Compounds studied in the source documents and their administered doses",
columns=[
EntityColumnInput(
key="compound",
label="Compound",
type="ENTITY_COLUMN_TYPE_STRING",
role="ENTITY_COLUMN_ROLE_IDENTIFIER",
is_row_key=True,
),
EntityColumnInput(
key="dose",
label="Dose",
type="ENTITY_COLUMN_TYPE_FLOAT",
role="ENTITY_COLUMN_ROLE_VALUE",
),
],
)

job = await client.mde.create_job(schema=schema, file_ids=["a1b2c3d4-e5f6-7890-abcd-ef1234567890"])
print(f"Job created: {job.job_id}")

while True:
job = await client.mde.get_job(job.job_id)
if job.status in TERMINAL_STATUSES:
break
await asyncio.sleep(3)

if job.status == "SUCCESS":
outputs = await client.mde.get_job_outputs(job.job_id)
for row in outputs.entity_extraction.rows or []:
print({cell.column_key: cell.value for cell in row.cells or []})

asyncio.run(main())

Next Steps