Compare commits

...

36 Commits

Author SHA1 Message Date
Neeraj Pradhan ad4f8b6e22 remove stringio 2025-04-09 17:10:32 -07:00
Neeraj Pradhan cb1b0b1822 Support text as input directly in the SDK 2025-04-09 17:08:22 -07:00
Neeraj Pradhan 233d715a14 Better connection management on llamaextract client (#674) 2025-04-09 14:26:52 -07:00
Neeraj Pradhan 77ac385dfe Fix bytes input for LlamaExtract (#673)
* Fix bytes input for LlamaExtract

* backwards compatibility

* compat python 3.9
2025-04-09 10:37:22 -07:00
Neeraj Pradhan 53b78fcd7d Rename test endpoint to match functionality (#668) 2025-04-08 17:42:20 -07:00
Jerry Liu 16f81bd7ee add due diligence notebook (#670) 2025-04-08 09:13:11 -07:00
Marplex 0ee049fd11 Add layout agent mode visual citation demo notebook (#672) 2025-04-07 09:54:06 -06:00
Neeraj Pradhan 7dba17e5bc Update extract.md (#671) 2025-04-06 22:18:03 -07:00
Jerry Liu eeb678b937 solar panel extraction workflow (#667)
* cr

* cr

* cr
2025-04-02 17:28:13 -07:00
Emanuel Ferreira fe4eb664fd chore: add base url documentation (#666)
* wip

* newline

* wip

* docs
2025-04-01 18:43:17 -03:00
Jerry Liu 257720e443 fix notebook (#665)
cr
2025-04-01 08:05:34 -07:00
Jerry Liu e7afaedf3e create llamaextract demo with lm317 datasheet (#664) 2025-03-31 17:38:24 -07:00
Neeraj Pradhan b66b47a708 Bump to version 0.6.9 (#663)
* Bump to version 0.6.8

* add banks as dep

* Add platformdirs to poetry

* Fix version number
2025-03-28 17:07:46 -07:00
George He fe485ff62e fix:Add retry handling to parse and backoff patterns - catching 5XX errors and HTTP errors (#648)
* Add parse retry logic

* Update code cleanliness

* Update errors

* Fix lint

* Fix backoff strategies

* Update docs

* Fix errors

* Add base
2025-03-26 12:09:56 +01:00
Pierre-Loic Doulcet 1ebe1cee67 Add new parameter, fix parse_mode (#660)
* update with new parameters

* lint
2025-03-25 11:14:37 +01:00
Neeraj Pradhan e9252eb48a Update notebook for extract (#658) 2025-03-22 09:34:40 -07:00
Neeraj Pradhan dad7728135 Bump to version 0.6.7 (#656) 2025-03-21 21:26:54 -07:00
Neeraj Pradhan c5111e3335 Revert httpx_client as argument (#657) 2025-03-21 21:16:56 -07:00
Neeraj Pradhan bbbdb98362 Add provision for custom httpx client for LlamaExtract (#654) 2025-03-21 11:37:40 -07:00
Neeraj Pradhan 60cdc2af84 Add xfail for timeout errors in report gen (#655) 2025-03-21 11:06:49 -07:00
Neeraj Pradhan 344c20f331 Bump up version for release (#652) 2025-03-18 15:54:32 -07:00
Neeraj Pradhan 2b0496e947 Update llama cloud for extract endpoints (#651) 2025-03-18 15:43:43 -07:00
Laurie Voss 6c63dba6fb Typos and removing staging URL (#647) 2025-03-13 08:11:09 -07:00
Neeraj Pradhan 734c021a2e Add notebook for extraction from SEC 10-K/Q filings (#646)
* Add notebook for extraction from SEC 10-K/Q filings

* Add notebook for 10 k/q extraction

* Remove unnecessary cell

* fix file link

* fix code rendering

* Add notes for clarity

* fix notes
2025-03-12 20:42:17 -07:00
Neeraj Pradhan eeb034896f Bump to version 0.6.5 (updating llama-cloud dependency) (#645)
* Bump to version 0.6.5 (updating llama-cloud dependency)

* fix other endpoints
2025-03-06 18:22:42 -08:00
Sacha Bron 4c977e8384 Bump version 2025-03-06 17:04:56 +01:00
Sacha Bron c6137713c7 Add adaptive_long_table option (#638) 2025-03-04 22:42:05 +01:00
Neeraj Pradhan fd4b1893f1 Bump version to v0.6.3 (#636) 2025-02-26 15:09:39 -08:00
Neeraj Pradhan e542e6136b Update README.md (#635) 2025-02-26 15:41:19 -06:00
Neeraj Pradhan 393451e304 Add LlamaExtract to llama-cloud-services (#628) 2025-02-25 18:17:29 -08:00
Logan 5084ba27ab v0.6.2 (#632) 2025-02-25 18:35:44 -06:00
Pierre-Loic Doulcet c82771f841 add new parsing mode and prompt parameters (#622) 2025-02-25 18:24:04 -06:00
Logan dc6860535a fix publish flow (#617) 2025-02-24 22:59:02 -10:00
Logan c872617b4e add organization id and project id as args (#616) 2025-02-11 17:46:50 -06:00
Jen Person 47c8682761 fixing colab links (#611) 2025-02-10 11:29:02 -06:00
Jerry Liu 683400788b add gemini2 flash notebook (#606) 2025-02-07 14:48:40 -06:00
81 changed files with 8263 additions and 1180 deletions
+1 -5
View File
@@ -14,7 +14,7 @@ env:
jobs:
build-n-publish:
name: Build and publish to PyPI
if: github.repository == 'run-llama/llama_parse'
if: github.repository == 'run-llama/llama_cloud_services'
runs-on: ubuntu-latest
steps:
@@ -36,16 +36,12 @@ jobs:
- name: Build and publish llama-cloud-services
uses: JRubics/poetry-publish@v2.1
with:
poetry_version: ${{ env.POETRY_VERSION }}
python_version: ${{ env.PYTHON_VERSION }}
pypi_token: ${{ secrets.LLAMA_PARSE_PYPI_TOKEN }}
poetry_install_options: "--without dev"
- name: Build and publish llama-parse
uses: JRubics/poetry-publish@v2.1
with:
poetry_version: ${{ env.POETRY_VERSION }}
python_version: ${{ env.PYTHON_VERSION }}
working_directory: "llama_parse"
pypi_token: ${{ secrets.LLAMA_PARSE_PYPI_TOKEN }}
poetry_install_options: "--without dev"
+2
View File
@@ -3,3 +3,5 @@ __pycache__/
*.pyc
.DS_Store
.idea
.env*
.ipynb_checkpoints*
+23 -3
View File
@@ -10,7 +10,7 @@ This includes:
- [LlamaParse](./parse.md) - A GenAI-native document parser that can parse complex document data for any downstream LLM use case (Agents, RAG, data processing, etc.).
- [LlamaReport (beta/invite-only)](./report.md) - A prebuilt agentic report builder that can be used to build reports from a variety of data sources.
- [LlamaExtract (coming soon!)]() - A prebuilt agentic data extractor that can be used to transform data into a structured JSON representation.
- [LlamaExtract (beta/invite-only)](./extract.md) - A prebuilt agentic data extractor that can be used to transform data into a structured JSON representation.
## Getting Started
@@ -25,17 +25,37 @@ Then, get your API key from [LlamaCloud](https://cloud.llamaindex.ai/).
Then, you can use the services in your code:
```python
from llama_cloud_services import LlamaParse, LlamaReport
from llama_cloud_services import LlamaParse, LlamaReport, LlamaExtract
parser = LlamaParse(api_key="YOUR_API_KEY")
report = LlamaReport(api_key="YOUR_API_KEY")
extract = LlamaExtract(api_key="YOUR_API_KEY")
```
See the quickstart guides for each service for more information:
- [LlamaParse](./parse.md)
- [LlamaReport (beta/invite-only)](./report.md)
- [LlamaExtract (coming soon!)]()
- [LlamaExtract (beta/invite-only)](./extract.md)
## Switch to EU SaaS 🇪🇺
If you are interested in using LlamaCloud services in the EU, you can adjust your base URL to `https://api.cloud.eu.llamaindex.ai`.
You can also create your API key in the EU region [here](https://cloud.eu.llamaindex.ai).
```python
from llama_cloud_services import (
LlamaParse,
LlamaReport,
LlamaExtract,
EU_BASE_URL,
)
parser = LlamaParse(api_key="YOUR_API_KEY", base_url=EU_BASE_URL)
report = LlamaReport(api_key="YOUR_API_KEY", base_url=EU_BASE_URL)
extract = LlamaExtract(api_key="YOUR_API_KEY", base_url=EU_BASE_URL)
```
## Documentation
File diff suppressed because one or more lines are too long
@@ -0,0 +1,10 @@
# Financial Modeling Assumptions
Discount Rate: 8%
Terminal Growth Rate: 2%
Tax Rate: 25%
Revenue Growth (Years 1-5): 10% per annum
Revenue Growth (Years 6-10): 5% per annum
Capital Expenditures as % of Revenue: 7%
Working Capital Assumption: 3% of Revenue
Depreciation Rate: 10% per annum
Cost of Capital Assumption: 8%
Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 893 KiB

@@ -0,0 +1,318 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "1f6bd03d-1b8b-45a0-bc2c-5a13f1a5d8d3",
"metadata": {},
"source": [
"# LM317 Voltage Regulator Datasheet Structured Extraction\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/extract/lm317_structured_extraction.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"This notebook demonstrates an agentic document workflow using LlamaExtract to process an LM317 voltage regulator datasheet. In this example, we define a structured extraction schema that converts key technical fields into standardized subfields. For instance, the output voltage is split into a minimum and maximum value with a defined unit, and we capture page citations for each extracted field.\n",
"\n",
"The target user is an electronics engineer at a component manufacturing company who needs to consolidate datasheet information into a standardized specification sheet for design and quality control.\n",
"\n",
"This approach reduces manual data entry, improves extraction accuracy and standardization, and provides traceability for each technical detail."
]
},
{
"cell_type": "markdown",
"id": "a3b8c8d5-ff3e-48ce-b0b8-29b6b1f517f8",
"metadata": {},
"source": [
"## Use Case Overview\n",
"\n",
"### Problem\n",
"Datasheets like that for the LM317 regulator are often distributed as PDFs containing multiple tables, charts, and complex textual descriptions. Engineers must manually extract technical details such as voltage ranges, dropout voltage, maximum current, input voltage range, and pin configurations. This process is error-prone and time-consuming.\n",
"\n",
"### Agent Workflow (Combination of Automation and Chat)\n",
"1. **Upload Datasheet:** The engineer uploads the LM317 datasheet PDF. \n",
"2. **Structured Extraction:** An automated agent processes the PDF and extracts key technical details into structured fields (e.g., output voltage as a range with separate min/max values).\n",
"3. **Interactive Verification:** The engineer can query the agent (via chat) for further details or clarification (e.g., \"Show me the detailed pin configuration extraction\") and review the cited pages.\n",
"\n",
"**Value Delivered:**\n",
"- Up to 70% reduction in manual data extraction time.\n",
"- Increased accuracy and standardization with structured fields."
]
},
{
"cell_type": "markdown",
"id": "a704e843-54be-4969-842b-713584cb3c35",
"metadata": {},
"source": [
"## Setup and Download Data\n",
"\n",
"Download the [LM317 Datasheet](https://www.ti.com/lit/ds/symlink/lm317.pdf) and setup LlamaExtract."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6e5b1f91-8785-44d4-a710-8be1b48b76de",
"metadata": {},
"outputs": [],
"source": [
"!mkdir -p data/lm317_structured_extraction\n",
"!wget https://www.ti.com/lit/ds/symlink/lm317.pdf -O data/lm317_structured_extraction/lm317.pdf"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f17b914a-00ed-4b63-8198-69fd7c4a7c62",
"metadata": {},
"outputs": [],
"source": [
"from dotenv import load_dotenv\n",
"from llama_cloud_services import LlamaExtract\n",
"from llama_cloud.core.api_error import ApiError\n",
"\n",
"# Load environment variables (ensure LLAMA_CLOUD_API_KEY is set in your .env file)\n",
"load_dotenv(override=True)\n",
"\n",
"# Initialize the LlamaExtract client\n",
"llama_extract = LlamaExtract(\n",
" project_id=\"<project_id>\",\n",
" organization_id=\"<organization_id>\",\n",
")"
]
},
{
"cell_type": "markdown",
"id": "ed9f6e9a-96c8-4ee1-8b45-0b6a4f7dbbf1",
"metadata": {},
"source": [
"## Defining a Structured Extraction Schema\n",
"\n",
"We now define a rich Pydantic schema to extract technical specifications from the LM317 datasheet. In this schema:\n",
"\n",
"- The **output_voltage** and **input_voltage** fields are structured as ranges with separate minimum and maximum values and a unit.\n",
"- The **pin_configuration** field is structured to include a pin count and a descriptive layout.\n",
"- Additional technical fields (e.g., dropout voltage, max current) are captured as numbers.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4f7e9b44-5e69-4b30-9864-cd98f1e2a7d4",
"metadata": {},
"outputs": [],
"source": [
"from pydantic import BaseModel, Field\n",
"from typing import List\n",
"\n",
"\n",
"class VoltageRange(BaseModel):\n",
" min_voltage: float = Field(..., description=\"Minimum voltage in volts\")\n",
" max_voltage: float = Field(..., description=\"Maximum voltage in volts\")\n",
" unit: str = Field(\"V\", description=\"Voltage unit\")\n",
"\n",
"\n",
"class PinConfiguration(BaseModel):\n",
" pin_count: int = Field(..., description=\"Number of pins\")\n",
" layout: str = Field(..., description=\"Detailed pin layout description\")\n",
"\n",
"\n",
"class LM317Spec(BaseModel):\n",
" component_name: str = Field(..., description=\"Name of the component\")\n",
" output_voltage: VoltageRange = Field(\n",
" ..., description=\"Output voltage range specification\"\n",
" )\n",
" dropout_voltage: float = Field(..., description=\"Dropout voltage in volts\")\n",
" max_current: float = Field(..., description=\"Maximum current rating in amperes\")\n",
" input_voltage: VoltageRange = Field(\n",
" ..., description=\"Input voltage range specification\"\n",
" )\n",
" pin_configuration: PinConfiguration = Field(\n",
" ..., description=\"Pin configuration details\"\n",
" )\n",
" features: List[str] = Field([], description=\"List of additional technical features\")\n",
"\n",
"\n",
"class LM317Schema(BaseModel):\n",
" specs: List[LM317Spec] = Field(\n",
" ..., description=\"List of extracted LM317 technical specifications\"\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e0508e38-35be-446c-afe7-129e39553281",
"metadata": {},
"outputs": [],
"source": [
"try:\n",
" existing_agent = llama_extract.get_agent(name=\"lm317-datasheet\")\n",
" if existing_agent:\n",
" llama_extract.delete_agent(existing_agent.id)\n",
"except ApiError as e:\n",
" if e.status_code == 404:\n",
" pass\n",
" else:\n",
" raise"
]
},
{
"cell_type": "markdown",
"id": "bb197dfd-dd37-459e-8953-cc1b12f25bdd",
"metadata": {},
"source": [
"Here we use our balanced extraction mode."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e3defc0a-c685-4fbd-bbb1-1270f1442e72",
"metadata": {},
"outputs": [],
"source": [
"from llama_cloud import ExtractConfig\n",
"\n",
"extract_config = ExtractConfig(\n",
" extraction_mode=\"BALANCED\",\n",
")\n",
"\n",
"agent = llama_extract.create_agent(\n",
" name=\"lm317-datasheet\", data_schema=LM317Schema, config=extract_config\n",
")"
]
},
{
"cell_type": "markdown",
"id": "c0a0f9f9-2ef3-4a38-bd74-68d2c2e9e2d8",
"metadata": {},
"source": [
"## Extracting Information from the LM317 Datasheet\n",
"\n",
"For this demonstration, please download a publicly available LM317 voltage regulator datasheet (for example, from Texas Instruments) and save it as `lm317.pdf` in the `./data` directory. Then run the cell below to extract the structured technical specifications."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c58e8b7a-8f9b-46f3-8f72-3c2f96b49e8f",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Uploading files: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:01<00:00, 1.08s/it]\n",
"Creating extraction jobs: 100%|████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 1.96it/s]\n",
"Extracting files: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [01:27<00:00, 87.38s/it]\n"
]
}
],
"source": [
"# Path to the LM317 datasheet PDF\n",
"lm317_pdf = \"./data/lm317_structured_extraction/lm317.pdf\"\n",
"\n",
"# Extract structured technical specifications from the datasheet\n",
"lm317_extract = agent.extract(lm317_pdf)"
]
},
{
"cell_type": "markdown",
"id": "1a2e2e44-6c48-4a38-a6de-5f2f3c7d4d8b",
"metadata": {},
"source": [
"## Assessing the Extraction Results\n",
"\n",
"The output will be a consolidated list of LM317 technical specifications. For each entry, you should see structured fields including:\n",
"\n",
"- **component_name**\n",
"- **output_voltage** as a range (with separate `min_voltage` and `max_voltage` plus `unit`)\n",
"- **dropout_voltage** and **max_current** as numbers\n",
"- **input_voltage** as a structured range\n",
"- **pin_configuration** with a `pin_count` and `layout`\n",
"- **features** (if available)\n",
"\n",
"This structured approach makes it easier to standardize the information for downstream integration and verification. Engineers can click on the cited page numbers (in a UI that supports it) to validate the extraction."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fb2abc44-7c9b-4b19-958e-d0d7b390ae57",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'specs': [{'component_name': 'LM317',\n",
" 'output_voltage': {'min_voltage': 1.25, 'max_voltage': 37.0, 'unit': 'V'},\n",
" 'dropout_voltage': 0.0,\n",
" 'max_current': 1.5,\n",
" 'input_voltage': {'min_voltage': 4.25, 'max_voltage': 40.0, 'unit': 'V'},\n",
" 'pin_configuration': {'pin_count': 3,\n",
" 'layout': '1: ADJUST, 2: OUTPUT, 3: INPUT'},\n",
" 'features': ['Output voltage range adjustable from 1.25 V to 37 V',\n",
" 'Output current greater than 1.5 A',\n",
" 'Internal short-circuit current limiting',\n",
" 'Thermal overload protection',\n",
" 'Output safe-area compensation']}]}"
]
},
"execution_count": null,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Display the extraction results\n",
"lm317_extract.data"
]
},
{
"cell_type": "markdown",
"id": "c7a2a523-095e-40bf-b713-f509c13a7747",
"metadata": {},
"source": [
"You can also see the output result in the UI."
]
},
{
"cell_type": "markdown",
"id": "dc22dfa5-b667-4fb0-8dbe-24e401b12389",
"metadata": {},
"source": [
"![](data/lm317_structured_extraction/lm317_extraction.png)"
]
},
{
"cell_type": "markdown",
"id": "e0e0c12a-9f89-4bb3-b40d-3e9f7c6d2fef",
"metadata": {},
"source": [
"## Conclusion\n",
"\n",
"This notebook demonstrated how to use LlamaExtract with a structured extraction schema for the LM317 voltage regulator datasheet. By defining detailed subfields (such as splitting voltage ranges into minimum and maximum values, and structuring the pin configuration), we ensure that the extracted data is standardized and traceable through page citations. This approach minimizes manual effort and improves accuracy, providing a robust example of an agentic document workflow for technical documentation processing.\n",
"\n",
"Feel free to modify or extend the schema to capture additional technical details or to suit your own use cases."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "llama_parse",
"language": "python",
"name": "llama_parse"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+834
View File
@@ -0,0 +1,834 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Extracting data from Resumes\n",
"\n",
"Let us assume that we are running a hiring process for a company and we have received a list of resumes from candidates. We want to extract structured data from the resumes so that we can run a screening process and shortlist candidates. \n",
"\n",
"Take a look at one of the resumes in the `data/resumes` directory. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" <iframe\n",
" width=\"600\"\n",
" height=\"400\"\n",
" src=\"./data/resumes/ai_researcher.pdf\"\n",
" frameborder=\"0\"\n",
" allowfullscreen\n",
" \n",
" ></iframe>\n",
" "
],
"text/plain": [
"<IPython.lib.display.IFrame at 0x109a7dcd0>"
]
},
"execution_count": null,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from IPython.display import IFrame\n",
"\n",
"IFrame(src=\"./data/resumes/ai_researcher.pdf\", width=600, height=400)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You will notice that all the resumes have different layouts but contain common information like name, email, experience, education, etc. \n",
"\n",
"With LlamaExtract, we will show you how to:\n",
"- *Define* a data schema to extract the information of interest. \n",
"- *Iterate* over the data schema to generalize the schema for multiple resumes.\n",
"- *Finalize* the schema and schedule extractions for multiple resumes.\n",
"\n",
"We will start by defining a `LlamaExtract` client which provides a Python interface to the LlamaExtract API. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from dotenv import load_dotenv\n",
"from llama_cloud_services import LlamaExtract\n",
"\n",
"\n",
"# Load environment variables (put LLAMA_CLOUD_API_KEY in your .env file)\n",
"load_dotenv(override=True)\n",
"\n",
"# Optionally, add your project id/organization id\n",
"llama_extract = LlamaExtract()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Defining the data schema\n",
"\n",
"Next, let us try to extract two fields from the resume: `name` and `email`. We can either use a Python dictionary structure to define the `data_schema` as a JSON or use a Pydantic model instead, for brevity and convenience. In either case, our output is guaranteed to validate against this schema."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from pydantic import BaseModel, Field\n",
"\n",
"\n",
"class Resume(BaseModel):\n",
" name: str = Field(description=\"The name of the candidate\")\n",
" email: str = Field(description=\"The email address of the candidate\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Uploading files: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:02<00:00, 2.20s/it]\n",
"Creating extraction jobs: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:02<00:00, 2.93s/it]\n",
"Extracting files: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:02<00:00, 2.94s/it]\n",
"Uploading files: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 1.13it/s]\n",
"Creating extraction jobs: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 1.80it/s]\n",
"Extracting files: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:15<00:00, 15.18s/it]\n",
"Uploading files: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 1.16it/s]\n",
"Creating extraction jobs: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 2.33it/s]\n",
"Extracting files: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:32<00:00, 32.86s/it]\n"
]
}
],
"source": [
"from llama_cloud.core.api_error import ApiError\n",
"\n",
"try:\n",
" existing_agent = llama_extract.get_agent(name=\"resume-screening\")\n",
" if existing_agent:\n",
" llama_extract.delete_agent(existing_agent.id)\n",
"except ApiError as e:\n",
" if e.status_code == 404:\n",
" pass\n",
" else:\n",
" raise\n",
"\n",
"agent = llama_extract.create_agent(name=\"resume-screening\", data_schema=Resume)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[ExtractionAgent(id=1fef43b5-8230-43b4-9e80-c1cddf53889c, name=resume-screening),\n",
" ExtractionAgent(id=93f8508b-3570-46f0-ae62-6315b40043bd, name=receipt/noisebridge_receipt.pdf_56db3d92),\n",
" ExtractionAgent(id=08315f0e-7146-430b-99b8-9701cb3ace6a, name=receipt/noisebridge_receipt.pdf_5c4730a7),\n",
" ExtractionAgent(id=cfcd7756-015d-4dbd-b142-a3eefcb16cd3, name=resume/software_architect_resume.html_4a11cf15),\n",
" ExtractionAgent(id=17cb83d9-601e-4f5c-a7aa-286e3045bcb4, name=resume/software_architect_resume.html_0b7d84a8),\n",
" ExtractionAgent(id=adc8e88c-44d3-4613-a5aa-d666ef007494, name=slide/saas_slide.pdf_bcc627a5),\n",
" ExtractionAgent(id=189f14cd-6370-4476-a6ad-36eafbc62618, name=slide/saas_slide.pdf_065aa22b),\n",
" ExtractionAgent(id=b9938ca5-6225-43cb-89ea-b0065237792f, name=test2),\n",
" ExtractionAgent(id=574d37b8-59dc-41e9-bde0-5c506a8eb670, name=test)]"
]
},
"execution_count": null,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"llama_extract.list_agents()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'name': 'Dr. Rachel Zhang', 'email': 'rachel.zhang@email.com'}"
]
},
"execution_count": null,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"resume = agent.extract(\"./data/resumes/ai_researcher.pdf\")\n",
"resume.data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Iterating over the data schema\n",
"\n",
"Now that we have created a data schema, let us add more fields to the schema. We will add `experience` and `education` fields to the schema. \n",
"- We can create a new Pydantic model for each of these fields and represent `experience` and `education` as lists of these models. Doing this will allow us to extract multiple entities from the resume without having to pre-define how many experiences or education the candidate has. \n",
"- We have added a `description` parameter to provide more context for extraction. We can use `description` to provide example inputs/outputs for the extraction. \n",
"- Note that we have annotated the `start_date` and `end_date` fields with `Optional[str]` to indicate that these fields are optional. This is *important* because the schema will be used to extract data from multiple resumes and not all resumes will have the same format. A field must only be required if it is guaranteed to be present in all the resumes. \n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from typing import List, Optional\n",
"\n",
"\n",
"class Education(BaseModel):\n",
" institution: str = Field(description=\"The institution of the candidate\")\n",
" degree: str = Field(description=\"The degree of the candidate\")\n",
" start_date: Optional[str] = Field(\n",
" default=None, description=\"The start date of the candidate's education\"\n",
" )\n",
" end_date: Optional[str] = Field(\n",
" default=None, description=\"The end date of the candidate's education\"\n",
" )\n",
"\n",
"\n",
"class Experience(BaseModel):\n",
" company: str = Field(description=\"The name of the company\")\n",
" title: str = Field(description=\"The title of the candidate\")\n",
" description: Optional[str] = Field(\n",
" default=None, description=\"The description of the candidate's experience\"\n",
" )\n",
" start_date: Optional[str] = Field(\n",
" default=None, description=\"The start date of the candidate's experience\"\n",
" )\n",
" end_date: Optional[str] = Field(\n",
" default=None, description=\"The end date of the candidate's experience\"\n",
" )\n",
"\n",
"\n",
"class Resume(BaseModel):\n",
" name: str = Field(description=\"The name of the candidate\")\n",
" email: str = Field(description=\"The email address of the candidate\")\n",
" links: List[str] = Field(\n",
" description=\"The links to the candidate's social media profiles\"\n",
" )\n",
" experience: List[Experience] = Field(description=\"The candidate's experience\")\n",
" education: List[Education] = Field(description=\"The candidate's education\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next, we will update the `data_schema` for the `resume-screening` agent to use the new `Resume` model. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'name': 'Dr. Rachel Zhang',\n",
" 'email': 'rachel.zhang@email.com',\n",
" 'links': ['linkedin.com/in/rachelzhang',\n",
" 'github.com/rzhang-ai',\n",
" 'scholar.google.com/rachelzhang'],\n",
" 'experience': [{'company': 'DeepMind',\n",
" 'title': 'Senior Research Scientist',\n",
" 'description': '- Lead researcher on large-scale multi-task learning systems, developing novel architectures that improve cross-task generalization by 40%\\n- Pioneered new approach to zero-shot learning using contrastive training, published in NeurIPS 2023\\n- Built and led team of 6 researchers working on foundational ML models\\n- Developed novel regularization techniques for large language models, reducing catastrophic forgetting by 35%',\n",
" 'start_date': '2019',\n",
" 'end_date': 'Present'},\n",
" {'company': 'Google Research',\n",
" 'title': 'Research Scientist',\n",
" 'description': '- Developed probabilistic frameworks for robust ML, published in ICML 2018\\n- Created novel attention mechanisms for computer vision models, improving accuracy by 25%\\n- Led collaboration with Google Brain team on efficient training methods for transformer models\\n- Mentored 4 PhD interns and collaborated with academic institutions',\n",
" 'start_date': '2015',\n",
" 'end_date': '2019'},\n",
" {'company': 'Columbia University',\n",
" 'title': 'Research Assistant Professor',\n",
" 'description': '- Published seminal work on Bayesian optimization methods (cited 1000+ times)\\n- Taught graduate-level courses in Machine Learning and Statistical Learning Theory\\n- Supervised 5 PhD students and 3 MSc students\\n- Secured $500K in research grants for probabilistic ML research',\n",
" 'start_date': '2011',\n",
" 'end_date': '2015'}],\n",
" 'education': [{'institution': 'Columbia University',\n",
" 'degree': 'Ph.D. in Computer Science',\n",
" 'start_date': '2007',\n",
" 'end_date': '2011'},\n",
" {'institution': 'Stanford University',\n",
" 'degree': 'M.S. in Computer Science',\n",
" 'start_date': '2005',\n",
" 'end_date': '2007'}]}"
]
},
"execution_count": null,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"agent.data_schema = Resume\n",
"resume = agent.extract(\"./data/resumes/ai_researcher.pdf\")\n",
"resume.data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This is a good start. Let us add a few more fields to the schema and re-run the extraction. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class TechnicalSkills(BaseModel):\n",
" programming_languages: List[str] = Field(\n",
" description=\"The programming languages the candidate is proficient in.\"\n",
" )\n",
" frameworks: List[str] = Field(\n",
" description=\"The tools/frameworks the candidate is proficient in, e.g. React, Django, PyTorch, etc.\"\n",
" )\n",
" skills: List[str] = Field(\n",
" description=\"Other general skills the candidate is proficient in, e.g. Data Engineering, Machine Learning, etc.\"\n",
" )\n",
"\n",
"\n",
"class Resume(BaseModel):\n",
" name: str = Field(description=\"The name of the candidate\")\n",
" email: str = Field(description=\"The email address of the candidate\")\n",
" links: List[str] = Field(\n",
" description=\"The links to the candidate's social media profiles\"\n",
" )\n",
" experience: List[Experience] = Field(description=\"The candidate's experience\")\n",
" education: List[Education] = Field(description=\"The candidate's education\")\n",
" technical_skills: TechnicalSkills = Field(\n",
" description=\"The candidate's technical skills\"\n",
" )\n",
" key_accomplishments: str = Field(\n",
" description=\"Summarize the candidates highest achievements.\"\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'name': 'Dr. Rachel Zhang, Ph.D.',\n",
" 'email': 'rachel.zhang@email.com',\n",
" 'links': ['linkedin.com/in/rachelzhang',\n",
" 'github.com/rzhang-ai',\n",
" 'scholar.google.com/rachelzhang'],\n",
" 'experience': [{'company': 'DeepMind',\n",
" 'title': 'Senior Research Scientist',\n",
" 'description': 'Lead researcher on large-scale multi-task learning systems, developing novel architectures that improve cross-task generalization by 40%\\nPioneered new approach to zero-shot learning using contrastive training, published in NeurIPS 2023\\nBuilt and led team of 6 researchers working on foundational ML models\\nDeveloped novel regularization techniques for large language models, reducing catastrophic forgetting by 35%',\n",
" 'start_date': '2019',\n",
" 'end_date': 'Present'},\n",
" {'company': 'Google Research',\n",
" 'title': 'Research Scientist',\n",
" 'description': 'Developed probabilistic frameworks for robust ML, published in ICML 2018\\nCreated novel attention mechanisms for computer vision models, improving accuracy by 25%\\nLed collaboration with Google Brain team on efficient training methods for transformer models\\nMentored 4 PhD interns and collaborated with academic institutions',\n",
" 'start_date': '2015',\n",
" 'end_date': '2019'},\n",
" {'company': 'Columbia University',\n",
" 'title': 'Research Assistant Professor',\n",
" 'description': 'Published seminal work on Bayesian optimization methods (cited 1000+ times)\\nTaught graduate-level courses in Machine Learning and Statistical Learning Theory\\nSupervised 5 PhD students and 3 MSc students\\nSecured $500K in research grants for probabilistic ML research',\n",
" 'start_date': '2011',\n",
" 'end_date': '2015'}],\n",
" 'education': [{'institution': 'Columbia University',\n",
" 'degree': 'Ph.D. in Computer Science',\n",
" 'start_date': '2007',\n",
" 'end_date': '2011'},\n",
" {'institution': 'Stanford University',\n",
" 'degree': 'M.S. in Computer Science',\n",
" 'start_date': '2005',\n",
" 'end_date': '2007'}],\n",
" 'technical_skills': {'programming_languages': ['Python',\n",
" 'C++',\n",
" 'Julia',\n",
" 'CUDA'],\n",
" 'frameworks': ['PyTorch', 'TensorFlow', 'JAX', 'Ray'],\n",
" 'skills': ['Deep Learning',\n",
" 'Reinforcement Learning',\n",
" 'Probabilistic Models',\n",
" 'Multi-Task Learning',\n",
" 'Zero-Shot Learning',\n",
" 'Neural Architecture Search']},\n",
" 'key_accomplishments': 'AI researcher with 12+ years of experience spanning classical machine learning, deep learning, and probabilistic modeling. Led groundbreaking research in reinforcement learning, generative models, and multi-task learning. Published 25+ papers in top-tier conferences (NeurIPS, ICML, ICLR). Strong track record of transitioning theoretical advances into practical applications in both academic and industrial settings.'}"
]
},
"execution_count": null,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"agent.data_schema = Resume\n",
"resume = agent.extract(\"./data/resumes/ai_researcher.pdf\")\n",
"resume.data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Finalizing the schema\n",
"\n",
"This is great! We have extracted a lot of key information from the resume that is well-typed and can be used downstream for further processing. Until now, this data is ephemeral and will be lost if we close the session. Let us save the state of our extraction and use it to extract data from multiple resumes. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"agent.save()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'type': 'object',\n",
" 'required': ['name',\n",
" 'email',\n",
" 'links',\n",
" 'experience',\n",
" 'education',\n",
" 'technical_skills',\n",
" 'key_accomplishments'],\n",
" 'properties': {'name': {'type': 'string',\n",
" 'description': 'The name of the candidate'},\n",
" 'email': {'type': 'string',\n",
" 'description': 'The email address of the candidate'},\n",
" 'links': {'type': 'array',\n",
" 'items': {'type': 'string'},\n",
" 'description': \"The links to the candidate's social media profiles\"},\n",
" 'education': {'type': 'array',\n",
" 'items': {'type': 'object',\n",
" 'required': ['institution', 'degree', 'start_date', 'end_date'],\n",
" 'properties': {'degree': {'type': 'string',\n",
" 'description': 'The degree of the candidate'},\n",
" 'end_date': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
" 'description': \"The end date of the candidate's education\"},\n",
" 'start_date': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
" 'description': \"The start date of the candidate's education\"},\n",
" 'institution': {'type': 'string',\n",
" 'description': 'The institution of the candidate'}},\n",
" 'additionalProperties': False},\n",
" 'description': \"The candidate's education\"},\n",
" 'experience': {'type': 'array',\n",
" 'items': {'type': 'object',\n",
" 'required': ['company', 'title', 'description', 'start_date', 'end_date'],\n",
" 'properties': {'title': {'type': 'string',\n",
" 'description': 'The title of the candidate'},\n",
" 'company': {'type': 'string', 'description': 'The name of the company'},\n",
" 'end_date': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
" 'description': \"The end date of the candidate's experience\"},\n",
" 'start_date': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
" 'description': \"The start date of the candidate's experience\"},\n",
" 'description': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
" 'description': \"The description of the candidate's experience\"}},\n",
" 'additionalProperties': False},\n",
" 'description': \"The candidate's experience\"},\n",
" 'technical_skills': {'type': 'object',\n",
" 'required': ['programming_languages', 'frameworks', 'skills'],\n",
" 'properties': {'skills': {'type': 'array',\n",
" 'items': {'type': 'string'},\n",
" 'description': 'Other general skills the candidate is proficient in, e.g. Data Engineering, Machine Learning, etc.'},\n",
" 'frameworks': {'type': 'array',\n",
" 'items': {'type': 'string'},\n",
" 'description': 'The tools/frameworks the candidate is proficient in, e.g. React, Django, PyTorch, etc.'},\n",
" 'programming_languages': {'type': 'array',\n",
" 'items': {'type': 'string'},\n",
" 'description': 'The programming languages the candidate is proficient in.'}},\n",
" 'description': \"The candidate's technical skills\",\n",
" 'additionalProperties': False},\n",
" 'key_accomplishments': {'type': 'string',\n",
" 'description': 'Summarize the candidates highest achievements.'}},\n",
" 'additionalProperties': False}"
]
},
"execution_count": null,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"agent = llama_extract.get_agent(\"resume-screening\")\n",
"agent.data_schema # Latest schema should be returned"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Queueing extractions"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For multiple resumes, we can use the `queue_extraction` method to run extractions asynchronously. This is ideal for processing batch extraction jobs."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Uploading files: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 3/3 [00:01<00:00, 2.13it/s]\n",
"Creating extraction jobs: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 3/3 [00:00<00:00, 5.83it/s]\n"
]
}
],
"source": [
"import os\n",
"\n",
"# All resumes in the data/resumes directory\n",
"resumes = []\n",
"\n",
"with os.scandir(\"./data/resumes\") as entries:\n",
" for entry in entries:\n",
" if entry.is_file():\n",
" resumes.append(entry.path)\n",
"\n",
"jobs = await agent.queue_extraction(resumes)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To get the latest status of the extractions for any `job_id`, we can use the `get_extraction_job` method. \n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[<StatusEnum.PENDING: 'PENDING'>,\n",
" <StatusEnum.PENDING: 'PENDING'>,\n",
" <StatusEnum.PENDING: 'PENDING'>]"
]
},
"execution_count": null,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"[agent.get_extraction_job(job_id=job.id).status for job in jobs]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We notice that all extraction runs are in a PENDING state. We can check back again to see if the extractions have completed. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[<StatusEnum.SUCCESS: 'SUCCESS'>,\n",
" <StatusEnum.SUCCESS: 'SUCCESS'>,\n",
" <StatusEnum.SUCCESS: 'SUCCESS'>]"
]
},
"execution_count": null,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"[agent.get_extraction_job(job_id=job.id).status for job in jobs]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Retrieving results\n",
"\n",
"Let us now retrieve the results of the extractions. If the status of the extraction is `SUCCESS`, we can retrieve the data from the `data` field. In case there are errors (status = `ERROR`), we can retrieve the error message from the `error` field. \n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"results = []\n",
"for job in jobs:\n",
" extract_run = agent.get_extraction_run_for_job(job.id)\n",
" if extract_run.status == \"SUCCESS\":\n",
" results.append(extract_run.data)\n",
" else:\n",
" print(f\"Extraction status for job {job.id}: {extract_run.status}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'name': 'Dr. Rachel Zhang, Ph.D.',\n",
" 'email': 'rachel.zhang@email.com',\n",
" 'links': ['linkedin.com/in/rachelzhang',\n",
" 'github.com/rzhang-ai',\n",
" 'scholar.google.com/rachelzhang'],\n",
" 'education': [{'degree': 'Ph.D. in Computer Science',\n",
" 'end_date': '2011',\n",
" 'start_date': '2007',\n",
" 'institution': 'Columbia University'},\n",
" {'degree': 'M.S. in Computer Science',\n",
" 'end_date': '2007',\n",
" 'start_date': '2005',\n",
" 'institution': 'Stanford University'}],\n",
" 'experience': [{'title': 'Senior Research Scientist',\n",
" 'company': 'DeepMind',\n",
" 'end_date': None,\n",
" 'start_date': '2019',\n",
" 'description': '- Lead researcher on large-scale multi-task learning systems, developing novel architectures that improve cross-task generalization by 40%\\n- Pioneered new approach to zero-shot learning using contrastive training, published in NeurIPS 2023\\n- Built and led team of 6 researchers working on foundational ML models\\n- Developed novel regularization techniques for large language models, reducing catastrophic forgetting by 35%'},\n",
" {'title': 'Research Scientist',\n",
" 'company': 'Google Research',\n",
" 'end_date': '2019',\n",
" 'start_date': '2015',\n",
" 'description': '- Developed probabilistic frameworks for robust ML, published in ICML 2018\\n- Created novel attention mechanisms for computer vision models, improving accuracy by 25%\\n- Led collaboration with Google Brain team on efficient training methods for transformer models\\n- Mentored 4 PhD interns and collaborated with academic institutions'},\n",
" {'title': 'Research Assistant Professor',\n",
" 'company': 'Columbia University',\n",
" 'end_date': '2015',\n",
" 'start_date': '2011',\n",
" 'description': '- Published seminal work on Bayesian optimization methods (cited 1000+ times)\\n- Taught graduate-level courses in Machine Learning and Statistical Learning Theory\\n- Supervised 5 PhD students and 3 MSc students\\n- Secured $500K in research grants for probabilistic ML research'}],\n",
" 'technical_skills': {'skills': ['Deep Learning',\n",
" 'Reinforcement Learning',\n",
" 'Probabilistic Models',\n",
" 'Multi-Task Learning',\n",
" 'Zero-Shot Learning',\n",
" 'Neural Architecture Search'],\n",
" 'frameworks': ['PyTorch', 'TensorFlow', 'JAX', 'Ray'],\n",
" 'programming_languages': ['Python', 'C++', 'Julia', 'CUDA']},\n",
" 'key_accomplishments': 'AI researcher with 12+ years of experience spanning classical machine learning, deep learning, and probabilistic modeling. Led groundbreaking research in reinforcement learning, generative models, and multi-task learning. Published 25+ papers in top-tier conferences (NeurIPS, ICML, ICLR). Strong track record of transitioning theoretical advances into practical applications in both academic and industrial settings.'}"
]
},
"execution_count": null,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"results[0]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'name': 'Alex Park',\n",
" 'email': 'alex park@email.com',\n",
" 'links': ['linkedin.com/in/alexpark'],\n",
" 'education': [{'degree': 'M.S. Computer Science',\n",
" 'end_date': None,\n",
" 'start_date': None,\n",
" 'institution': 'University of California, Berkeley'},\n",
" {'degree': 'B.S. Computer Science',\n",
" 'end_date': None,\n",
" 'start_date': None,\n",
" 'institution': 'University of California, Berkeley'}],\n",
" 'experience': [{'title': 'Senior Machine Learning Engineer',\n",
" 'company': 'SearchTech AI',\n",
" 'end_date': None,\n",
" 'start_date': None,\n",
" 'description': 'Led development of next-generation learning-to-rank system using BER\\nArchitected and deployed real-time personalization system processing 10\\nIncreasing CTR by 15%\\nImproving search relevance by 24% (NDCG@10)'},\n",
" {'title': '',\n",
" 'company': 'Commerce Corp',\n",
" 'end_date': None,\n",
" 'start_date': None,\n",
" 'description': 'Developed semantic search system using transformer models and approximate nearest neighbors, reducing null search results by 35%'},\n",
" {'title': 'Machine Learning Engineer',\n",
" 'company': 'Tech Solutions Inc',\n",
" 'end_date': None,\n",
" 'start_date': None,\n",
" 'description': 'Implemented query understanding pipeline'},\n",
" {'title': 'Software Engineer',\n",
" 'company': '',\n",
" 'end_date': None,\n",
" 'start_date': None,\n",
" 'description': 'Built data pipelines and Flasticsearch'}],\n",
" 'technical_skills': {'skills': ['Elasticsearch',\n",
" 'Solr',\n",
" 'Lucene',\n",
" 'Python',\n",
" 'SQL',\n",
" 'Java',\n",
" 'Scala',\n",
" 'Shell Scripting'],\n",
" 'frameworks': ['PyTorch',\n",
" 'TensorFlow',\n",
" 'Scikit-learn',\n",
" 'BERT',\n",
" 'Word2Vec',\n",
" 'FastAI',\n",
" 'BM25',\n",
" 'FAISS',\n",
" 'Docker',\n",
" 'Kubernetes'],\n",
" 'programming_languages': []},\n",
" 'key_accomplishments': 'Machine Learning Engineer with 5 years of experience building and deploying large-scale search and relevance systems: Specialized in developing personalized search algorithms, learning-to-rank models; and recommendation systems. Strong track record of improving search relevance metrics and user engagement through ML-driven solutions:'}"
]
},
"execution_count": null,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"results[1]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'name': 'Sarah Chen',\n",
" 'email': 'sarah.chen@email.com',\n",
" 'links': [],\n",
" 'education': [{'degree': 'Master of Science in Computer Science',\n",
" 'end_date': '2013',\n",
" 'start_date': None,\n",
" 'institution': 'Stanford University'},\n",
" {'degree': 'Bachelor of Science in Computer Engineering',\n",
" 'end_date': '2011',\n",
" 'start_date': None,\n",
" 'institution': 'University of California, Berkeley'}],\n",
" 'experience': [{'title': 'Senior Software Architect',\n",
" 'company': 'TechCorp Solutions',\n",
" 'end_date': None,\n",
" 'start_date': '2020',\n",
" 'description': '- Led architectural design and implementation of a cloud-native platform serving 2M+ users\\n- Established architectural guidelines and best practices adopted across 12 development teams\\n- Reduced system latency by 40% through implementation of event-driven architecture\\n- Mentored 15+ senior developers in cloud-native development practices'},\n",
" {'title': 'Lead Software Engineer',\n",
" 'company': 'DataFlow Systems',\n",
" 'end_date': '2020',\n",
" 'start_date': '2016',\n",
" 'description': '- Architected and led development of distributed data processing platform handling 5TB daily\\n- Designed microservices architecture reducing deployment time by 65%\\n- Led migration of legacy monolith to cloud-native architecture\\n- Managed team of 8 engineers across 3 international locations'},\n",
" {'title': 'Senior Software Engineer',\n",
" 'company': 'InnovateTech',\n",
" 'end_date': '2016',\n",
" 'start_date': '2013',\n",
" 'description': '- Developed high-performance trading platform processing 100K transactions per second\\n- Implemented real-time analytics engine reducing processing latency by 75%\\n- Led adoption of container orchestration reducing deployment costs by 35%'}],\n",
" 'technical_skills': {'skills': ['Architecture & Design',\n",
" 'Microservices',\n",
" 'Event-Driven Architecture',\n",
" 'Domain-Driven Design',\n",
" 'REST APIs',\n",
" 'Cloud Platforms'],\n",
" 'frameworks': ['AWS (Advanced)', 'Azure', 'Google Cloud Platform'],\n",
" 'programming_languages': ['Java', 'Python', 'Go', 'JavaScript/TypeScript']},\n",
" 'key_accomplishments': '- Co-inventor on three patents for distributed systems architecture\\n- Published paper on \"Scalable Microservices Architecture\" at IEEE Cloud Computing Conference 2022\\n- Keynote Speaker, CloudCon 2023: \"Future of Cloud-Native Architecture\"\\n- Regular presenter at local tech meetups and conferences'}"
]
},
"execution_count": null,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"results[2]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Congratulations! You now have an agent that can extract structured data from resumes. \n",
"- You can now use this agent to extract data from more resumes and use the extracted data for further processing. \n",
"- To update the schema, you can simply update the `data_schema` attribute of the agent and re-run the extraction. \n",
"- You can also use the `save` method to save the state of the agent and persist changes to the schema for future use. \n",
"\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,450 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "00f6713b-2a32-4f8f-80e5-9a7d9b6e3b90",
"metadata": {},
"source": [
"# Solar Panel Datasheet Comparison Workflow\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/extract/solar_panel_e2e_comparison.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"\n",
"This notebook demonstrates an endtoend agentic workflow using LlamaExtract and the LlamaIndex eventdriven workflow framework. In this workflow, we:\n",
"\n",
"1. **Extract** structured technical specifications from a solar panel datasheet (e.g. a PDF downloaded from a vendor).\n",
"2. **Load** design requirements (provided as a text blob) for a labgrade solar panel.\n",
"3. **Generate** a detailed comparison report by triggering an event that injects both the extracted data and the requirements into an LLM prompt.\n",
"\n",
"The workflow is designed for renewable energy engineers who need to quickly validate that a solar panel meets specific design criteria.\n",
"\n",
"The following notebook uses the eventdriven syntax (with custom events, steps, and a workflow class) adapted from the technical datasheet and contract review examples."
]
},
{
"cell_type": "markdown",
"id": "36d8e34e-ed98-46ac-b744-1642f6e253d5",
"metadata": {},
"source": [
"## Setup and Load Data\n",
"\n",
"We download the [Honey M TSM-DE08M.08(II) datasheet](https://static.trinasolar.com/sites/default/files/EU_Datasheet_HoneyM_DE08M.08%28II%29_2021_A.pdf) as a PDF.\n",
"\n",
"**NOTE**: The design requirements are already stored in `data/solar_panel_e2e_comparison/design_reqs.txt`."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1de7b1b3-c285-492c-8b2e-b37974b4fc63",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"--2025-04-01 14:47:56-- https://static.trinasolar.com/sites/default/files/EU_Datasheet_HoneyM_DE08M.08%28II%29_2021_A.pdf\n",
"Resolving static.trinasolar.com (static.trinasolar.com)... 47.246.23.232, 47.246.23.234, 47.246.23.227, ...\n",
"Connecting to static.trinasolar.com (static.trinasolar.com)|47.246.23.232|:443... connected.\n",
"WARNING: cannot verify static.trinasolar.com's certificate, issued by CN=DigiCert Global G2 TLS RSA SHA256 2020 CA1,O=DigiCert Inc,C=US:\n",
" Unable to locally verify the issuer's authority.\n",
"HTTP request sent, awaiting response... 200 OK\n",
"Length: 1888183 (1.8M) [application/pdf]\n",
"Saving to: data/solar_panel_e2e_comparison/datasheet.pdf\n",
"\n",
"data/solar_panel_e2 100%[===================>] 1.80M 7.47MB/s in 0.2s \n",
"\n",
"2025-04-01 14:47:56 (7.47 MB/s) - data/solar_panel_e2e_comparison/datasheet.pdf saved [1888183/1888183]\n",
"\n"
]
}
],
"source": [
"!wget https://static.trinasolar.com/sites/default/files/EU_Datasheet_HoneyM_DE08M.08%28II%29_2021_A.pdf -O data/solar_panel_e2e_comparison/datasheet.pdf --no-check-certificate"
]
},
{
"cell_type": "markdown",
"id": "89d2f4c9-f785-424d-a409-3381796c457c",
"metadata": {},
"source": [
"## Define the Structured Extraction Schema\n",
"\n",
"We define a new, rich schema called `SolarPanelSchema` to capture key technical details from the datasheet. This schema includes:\n",
"\n",
"- **PowerRange:** Structured as minimum and maximum power output (in Watts).\n",
"- **SolarPanelSpec:** Includes module name, power output range, maximum efficiency, certifications, and a mapping of page citations.\n",
"\n",
"This schema replaces the earlier LM317 schema and will be used when creating our extraction agent."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bfb40d48-36e0-4b1c-97a1-32a1704c582b",
"metadata": {},
"outputs": [],
"source": [
"from pydantic import BaseModel, Field\n",
"from typing import List\n",
"\n",
"\n",
"class PowerRange(BaseModel):\n",
" min_power: float = Field(..., description=\"Minimum power output in Watts\")\n",
" max_power: float = Field(..., description=\"Maximum power output in Watts\")\n",
" unit: str = Field(\"W\", description=\"Power unit\")\n",
"\n",
"\n",
"class SolarPanelSpec(BaseModel):\n",
" module_name: str = Field(..., description=\"Name or model of the solar panel module\")\n",
" power_output: PowerRange = Field(..., description=\"Power output range\")\n",
" maximum_efficiency: float = Field(\n",
" ..., description=\"Maximum module efficiency in percentage\"\n",
" )\n",
" temperature_coefficient: float = Field(\n",
" ..., description=\"Temperature coefficient in %/°C\"\n",
" )\n",
" certifications: List[str] = Field([], description=\"List of certifications\")\n",
" page_citations: dict = Field(\n",
" ..., description=\"Mapping of each extracted field to its page numbers\"\n",
" )\n",
"\n",
"\n",
"class SolarPanelSchema(BaseModel):\n",
" specs: List[SolarPanelSpec] = Field(\n",
" ..., description=\"List of extracted solar panel specifications\"\n",
" )"
]
},
{
"cell_type": "markdown",
"id": "19dc309e-7cec-43c1-8f6c-72e14df58f8f",
"metadata": {},
"source": [
"## Initialize Extraction Agent\n",
"\n",
"Here we initialize our extraction agent that will be responsible for extracting the schema from the solar panel datasheet."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c9d9f4a2-2e14-493d-8a7e-d01159d38b8f",
"metadata": {},
"outputs": [],
"source": [
"from dotenv import load_dotenv\n",
"from llama_cloud_services import LlamaExtract\n",
"from llama_cloud.core.api_error import ApiError\n",
"from llama_cloud import ExtractConfig\n",
"\n",
"# Initialize the LlamaExtract client\n",
"llama_extract = LlamaExtract(\n",
" project_id=\"2fef999e-1073-40e6-aeb3-1f3c0e64d99b\",\n",
" organization_id=\"43b88c8f-e488-46f6-9013-698e3d2e374a\",\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ec0eb2a7-6e02-45da-a6af-227e2f7c81f2",
"metadata": {},
"outputs": [],
"source": [
"try:\n",
" existing_agent = llama_extract.get_agent(name=\"solar-panel-datasheet\")\n",
" if existing_agent:\n",
" llama_extract.delete_agent(existing_agent.id)\n",
"except ApiError as e:\n",
" if e.status_code == 404:\n",
" pass\n",
" else:\n",
" raise\n",
"\n",
"extract_config = ExtractConfig(\n",
" extraction_mode=\"BALANCED\",\n",
")\n",
"\n",
"agent = llama_extract.create_agent(\n",
" name=\"solar-panel-datasheet\", data_schema=SolarPanelSchema, config=extract_config\n",
")"
]
},
{
"cell_type": "markdown",
"id": "b4d7bb60-0456-4a2d-8d48-14f9bb3e71d2",
"metadata": {},
"source": [
"## Workflow Overview\n",
"\n",
"The workflow consists of four main steps:\n",
"\n",
"1. **parse_datasheet:** Reads the solar panel datasheet (PDF) and converts its content into text (with page citations).\n",
"2. **load_requirements:** Loads the design requirements (as a text blob) that will be injected into the prompt.\n",
"3. **generate_comparison_report:** Constructs a prompt using the extracted datasheet content and design requirements and triggers the LLM to generate a comparison report.\n",
"4. **output_result:** Logs and returns the final report as the workflows result.\n",
"\n",
"Each step is implemented as an asynchronous function decorated with `@step`, and the workflow is built by subclassing `Workflow`."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7c482e3a-66b4-4e1b-8d2d-9a9c6b3967f3",
"metadata": {},
"outputs": [],
"source": [
"from llama_index.core.workflow import (\n",
" Event,\n",
" StartEvent,\n",
" StopEvent,\n",
" Context,\n",
" Workflow,\n",
" step,\n",
")\n",
"from llama_index.llms.openai import OpenAI\n",
"from llama_index.core.prompts import ChatPromptTemplate\n",
"from llama_cloud_services import LlamaExtract\n",
"from llama_cloud.core.api_error import ApiError\n",
"from pydantic import BaseModel, Field\n",
"from typing import List\n",
"\n",
"\n",
"# Define output schema for the comparison report (for reference)\n",
"class ComparisonReportOutput(BaseModel):\n",
" component_name: str = Field(\n",
" ..., description=\"The name of the component being evaluated.\"\n",
" )\n",
" meets_requirements: bool = Field(\n",
" ...,\n",
" description=\"Overall indicator of whether the component meets the design criteria.\",\n",
" )\n",
" summary: str = Field(..., description=\"A brief summary of the evaluation results.\")\n",
" details: dict = Field(\n",
" ..., description=\"Detailed comparisons for each key parameter.\"\n",
" )\n",
"\n",
"\n",
"# Define custom events\n",
"\n",
"\n",
"class DatasheetParseEvent(Event):\n",
" datasheet_content: dict\n",
"\n",
"\n",
"class RequirementsLoadEvent(Event):\n",
" requirements_text: str\n",
"\n",
"\n",
"class ComparisonReportEvent(Event):\n",
" report: ComparisonReportOutput\n",
"\n",
"\n",
"class LogEvent(Event):\n",
" msg: str\n",
" delta: bool = False\n",
"\n",
"\n",
"# For our demonstration, we assume that LlamaExtract is used to parse the datasheet into text.\n",
"# We'll also use OpenAI (via LlamaIndex) as our LLM for generating the report.\n",
"\n",
"llm = OpenAI(model=\"gpt-4o\") # or your preferred model"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "67a0c391-c7f5-4b93-8d6b-9e31b2d7a817",
"metadata": {},
"outputs": [],
"source": [
"class SolarPanelComparisonWorkflow(Workflow):\n",
" \"\"\"\n",
" Workflow to extract data from a solar panel datasheet and generate a comparison report\n",
" against provided design requirements.\n",
" \"\"\"\n",
"\n",
" def __init__(self, agent: LlamaExtract, requirements_path: str, **kwargs):\n",
" super().__init__(**kwargs)\n",
" self.agent = agent\n",
" # Load design requirements from file as a text blob\n",
" with open(requirements_path, \"r\") as f:\n",
" self.requirements_text = f.read()\n",
"\n",
" @step\n",
" async def parse_datasheet(\n",
" self, ctx: Context, ev: StartEvent\n",
" ) -> DatasheetParseEvent:\n",
" # datasheet_path is provided in the StartEvent\n",
" datasheet_path = (\n",
" ev.datasheet_path\n",
" ) # e.g., \"./data/solar_panel_comparison/datasheet.pdf\"\n",
" extraction_result = await self.agent.aextract(datasheet_path)\n",
" datasheet_dict = (\n",
" extraction_result.data\n",
" ) # assumed to be a string with page citations\n",
" await ctx.set(\"datasheet_content\", datasheet_dict)\n",
" ctx.write_event_to_stream(LogEvent(msg=\"Datasheet parsed successfully.\"))\n",
" return DatasheetParseEvent(datasheet_content=datasheet_dict)\n",
"\n",
" @step\n",
" async def load_requirements(\n",
" self, ctx: Context, ev: DatasheetParseEvent\n",
" ) -> RequirementsLoadEvent:\n",
" # Use the pre-loaded requirements text from __init__\n",
" req_text = self.requirements_text\n",
" ctx.write_event_to_stream(LogEvent(msg=\"Design requirements loaded.\"))\n",
" return RequirementsLoadEvent(requirements_text=req_text)\n",
"\n",
" @step\n",
" async def generate_comparison_report(\n",
" self, ctx: Context, ev: RequirementsLoadEvent\n",
" ) -> StopEvent:\n",
" # Build a prompt that injects both the extracted datasheet content and the design requirements\n",
" datasheet_content = await ctx.get(\"datasheet_content\")\n",
" prompt_str = \"\"\"\n",
"You are an expert renewable energy engineer.\n",
"\n",
"Compare the following solar panel datasheet information with the design requirements.\n",
"\n",
"Design Requirements:\n",
"{requirements_text}\n",
"\n",
"Extracted Datasheet Information:\n",
"{datasheet_content}\n",
"\n",
"Generate a detailed comparison report in JSON format with the following schema:\n",
" - component_name: string\n",
" - meets_requirements: boolean\n",
" - summary: string\n",
" - details: dictionary of comparisons for each parameter\n",
"\n",
"For each parameter (Maximum Power, Open-Circuit Voltage, Short-Circuit Current, Efficiency, Temperature Coefficient),\n",
"indicate PASS or FAIL and provide brief explanations and recommendations.\n",
"\"\"\"\n",
"\n",
" # extract from contract\n",
" prompt = ChatPromptTemplate.from_messages([(\"user\", prompt_str)])\n",
"\n",
" # Call the LLM to generate the report using the prompt\n",
" report_output = await llm.astructured_predict(\n",
" ComparisonReportOutput,\n",
" prompt,\n",
" requirements_text=ev.requirements_text,\n",
" datasheet_content=str(datasheet_content),\n",
" )\n",
" ctx.write_event_to_stream(LogEvent(msg=\"Comparison report generated.\"))\n",
" return StopEvent(\n",
" result={\"report\": report_output, \"datasheet_content\": datasheet_content}\n",
" )"
]
},
{
"cell_type": "markdown",
"id": "d205f532-1a11-4a48-b5a8-87a7f85e9ce7",
"metadata": {},
"source": [
"## Running the Workflow\n",
"\n",
"Below, we instantiate and run the workflow. We inject the design requirements as a text blob (no custom code to load) and pass the path to the solar panel datasheet (the HoneyM datasheet from Trina).\n",
"\n",
"The design requirements are:\n",
"\n",
"```\n",
"Solar Panel Design Requirements:\n",
"- Power Output Range: ≥ 350 W\n",
"- Maximum Efficiency: ≥ 18%\n",
"- Certifications: Must include IEC61215 and UL1703\n",
"```\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6b24fa61-a2f5-4ebb-84eb-1c9b48683b1b",
"metadata": {},
"outputs": [],
"source": [
"import nest_asyncio\n",
"\n",
"nest_asyncio.apply()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "be3ebad5-1f70-4671-a2ec-17bf9e4d788f",
"metadata": {},
"outputs": [],
"source": [
"# Path to design requirements file (e.g., a text file with design criteria for solar panels)\n",
"requirements_path = \"./data/solar_panel_e2e_comparison/design_reqs.txt\"\n",
"\n",
"# Instantiate the workflow\n",
"workflow = SolarPanelComparisonWorkflow(\n",
" agent=agent, requirements_path=requirements_path, verbose=True, timeout=120\n",
")\n",
"\n",
"# Run the workflow; pass the datasheet path in the StartEvent\n",
"result = await workflow.run(\n",
" datasheet_path=\"./data/solar_panel_e2e_comparison/datasheet.pdf\"\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e1e61f1e-8701-4acc-8f99-cc89d8aae535",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"********Final Comparison Report:********\n",
"\n",
"{\n",
" \"component_name\": \"TSM-DE08M.08(II)\",\n",
" \"meets_requirements\": true,\n",
" \"summary\": \"The solar panel TSM-DE08M.08(II) meets all the design requirements, making it a suitable choice for the intended application.\",\n",
" \"details\": {\n",
" \"Maximum Power Output\": \"PASS - The panel's power output ranges from 360 W to 385 W, exceeding the minimum requirement of 350 W.\",\n",
" \"Open-Circuit Voltage\": \"PASS - The datasheet does not specify Voc, but the panel meets other critical requirements. Verification of Voc is recommended.\",\n",
" \"Short-Circuit Current\": \"PASS - The datasheet does not specify Isc, but the panel meets other critical requirements. Verification of Isc is recommended.\",\n",
" \"Efficiency\": \"PASS - The panel's efficiency is 21.0%, which is above the required 18%.\",\n",
" \"Temperature Coefficient\": \"PASS - The temperature coefficient is -0.34%/°C, which is better than the maximum allowable -0.5%/°C.\"\n",
" }\n",
"}\n"
]
}
],
"source": [
"print(\"\\n********Final Comparison Report:********\\n\")\n",
"print(result[\"report\"].model_dump_json(indent=4))\n",
"# print(\"\\n********Datasheet Content:********\\n\", result[\"datasheet_content\"])"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "llama_parse",
"language": "python",
"name": "llama_parse"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
@@ -7,7 +7,7 @@
"source": [
"# Dynamic Section Retrieval with LlamaParse\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llamacloud-demo/blob/main/examples/advanced_rag/dynamic_section_retrieval.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services-demo/blob/main/examples/parse/advanced_rag/dynamic_section_retrieval.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"This notebook showcases a concept called \"dynamic section retrieval\".\n",
"\n",
@@ -7,7 +7,7 @@
"source": [
"# RAG over the Caltrain Weekend Schedule \n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/caltrain/caltrain_text_mode.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/caltrain/caltrain_text_mode.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"This example shows off LlamaParse parsing capabilities to build a functioning query pipeline over the Caltrain weekend schedule, a big timetable containing all trains northbound and southbound and their stops in various cities.\n",
"\n",
+1 -1
View File
@@ -6,7 +6,7 @@
"source": [
"# Advanced RAG with LlamaParse + Weaviate\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/demo_advanced_weaviate.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\\\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/parse/demo_advanced_weaviate.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\\\" alt=\"Open In Colab\"/></a>\n",
"\n",
"This notebook shows you how to use `LlamaParse` for advancd RAG applications with `LlamaIndex` and [Weaviate](https://weaviate.io/).\n",
"\n",
+1 -1
View File
@@ -6,7 +6,7 @@
"source": [
"# RAG with Excel Spreadsheet using LlamaPrase\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/demo_excel.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/demo_excel.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"This notebook shows you using LlamaParse with Excel Spreadsheet.\n",
"\n",
+1 -1
View File
@@ -7,7 +7,7 @@
"source": [
"# Download Charts\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/demo_get_charts.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/demo_get_charts.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"This notebook demonstrates how to download charts from a document using the JSON mode in LlamaParse.\n",
"\n",
+1 -1
View File
@@ -6,7 +6,7 @@
"source": [
"# LlamaParse - Fast checking Insurance Contract for Coverage\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/demo_insurance.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/demo_insurance.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"In this notebook we will look at how LlamaParse can be used to extract structured coverage information from an insurance policy."
]
+1 -1
View File
@@ -7,7 +7,7 @@
"source": [
"# LlamaParse JSON Mode + Multimodal RAG\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/demo_json.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/demo_json.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"This notebook shows you how to use LlamaParse JSON mode with LlamaIndex to build a simple multimodal RAG pipeline.\n",
"\n",
+1 -1
View File
@@ -7,7 +7,7 @@
"source": [
"# LlamaParse JSON Mode + Advanced RAG with `LlamaParseJsonNodeParser`\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/demo_json_parsing.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/demo_json_parsing.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"This notebook shows you how to use LlamaParse JSON mode with LlamaIndex to build a simple recursive retrieval RAG pipeline using `LlamaParseJsonNodeParser`.\n",
"\n",
+1 -1
View File
@@ -7,7 +7,7 @@
"source": [
"# LlamaParse JSON Mode Tour\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/demo_json.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/demo_json.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"This notebook shows you how to use LlamaParse JSON mode with LlamaIndex and all the features it supports.\n",
"\n",
+1 -1
View File
@@ -9,7 +9,7 @@
"\n",
"LlamaParse supports users to specify a `language` parameter before uploading documents, giving users better OCR capabilities over non-English PDFs, parsing images into more accurate representations.\n",
"\n",
"You can specify 80+ different languages: see this file for a full list of supported languages: https://github.com/run-llama/llama_parse/blob/main/llama_parse/base.py.\n",
"You can specify 80+ different languages: see this file for a full list of supported languages: https://github.com/run-llama/llama_cloud_services/blob/main/llama_parse/base.py.\n",
"\n",
"This notebook shows a demo of this in action. "
]
+1 -1
View File
@@ -7,7 +7,7 @@
"source": [
"# LlamaParse With MongoDB\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/demo_mongodb.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/demo_mongodb.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"In this notebook, we provide a straightforward example of using LlamaParse with MongoDB Atlas VectorSearch.\n",
"\n",
+1 -1
View File
@@ -5,7 +5,7 @@
"id": "97c79c38-38a3-40f3-ba2e-250649347d63",
"metadata": {},
"source": [
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/demo_starter_multimodal.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/demo_starter_multimodal.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
@@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/demo_starter_parse_selected_pages.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/demo_starter_parse_selected_pages.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
+1 -1
View File
@@ -6,7 +6,7 @@
"source": [
"# RAG for Table Comparisons with LlamaParse + LlamaIndex\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/demo_table_comparisons.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/demo_table_comparisons.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"This notebook shows you how to do comparisons across both tabular and text data across multiple PDF documents.\n",
"\n",
+1 -1
View File
@@ -7,7 +7,7 @@
"source": [
"# RAG with Excel Spreadsheet using LlamaPrase\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/demo_excel.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/excel/dcf_rag.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"This notebook constructs a RAG pipeline over a simple DCF template [here](https://eqvista.com/app/uploads/2020/09/Eqvista_DCF-Excel-Template.xlsx).\n",
"\n"
+1 -1
View File
@@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/excel/o1_excel_rag.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/excel/o1_excel_rag.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
@@ -7,7 +7,7 @@
"source": [
"# Knowledge Graph Agent with LlamaParse\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/knowledge_graphs/kg_agent.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/knowledge_graphs/kg_agent.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"Here we build a knowledge graph agent over the SF 2023 Budget Proposal. We use LlamaIndex abstractions to construct a knowledge graph, and we store the property graph in neo4j. We then build an agent that can interact with the knowledge graph as a tool."
]
Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

+1 -1
View File
@@ -7,7 +7,7 @@
"source": [
"# Multimodal Parsing using Anthropic Claude (Sonnet 3.5)\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/multimodal/claude_parse.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/multimodal/claude_parse.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"This cookbook shows you how to use LlamaParse to parse any document with the multimodal capabilities of Sonnet 3.5. \n",
"\n",
@@ -0,0 +1,633 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "97c79c38-38a3-40f3-ba2e-250649347d63",
"metadata": {},
"source": [
"# Multimodal Parsing with Gemini 2.0 Flash\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/multimodal/gemini2_flash.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"This cookbook shows you how to use LlamaParse to parse any document with the multimodal capabilities of Gemini 2.0 Flash.\n",
"\n",
"LlamaParse allows you to plug in external, multimodal model vendors for parsing - we handle the error correction, validation, and scalability/reliability for you.\n"
]
},
{
"cell_type": "markdown",
"id": "15e60ecf-519c-41fc-911b-765adaf8bad4",
"metadata": {},
"source": [
"## Setup\n",
"\n",
"Download the data - we'll use a technical datasheet for a programmable logic device (Xilinx's XC9500 In-System Programmable CPLD)."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "91a9e532-1454-40e0-bbf0-fd442c350121",
"metadata": {},
"outputs": [],
"source": [
"import nest_asyncio\n",
"\n",
"nest_asyncio.apply()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0d9fb0aa-74cd-476f-8161-efd9e04248bf",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"--2025-02-06 20:24:19-- https://media.digikey.com/pdf/Data%20Sheets/AMD/XC9500_CPLD_Family.pdf\n",
"Resolving media.digikey.com (media.digikey.com)... 23.37.18.160\n",
"Connecting to media.digikey.com (media.digikey.com)|23.37.18.160|:443... connected.\n",
"HTTP request sent, awaiting response... 200 OK\n",
"Length: 201899 (197K) [application/pdf]\n",
"Saving to: data/XC9500_CPLD_Family.pdf\n",
"\n",
"data/XC9500_CPLD_Fa 100%[===================>] 197.17K --.-KB/s in 0.03s \n",
"\n",
"2025-02-06 20:24:19 (7.67 MB/s) - data/XC9500_CPLD_Family.pdf saved [201899/201899]\n",
"\n"
]
}
],
"source": [
"!wget \"https://media.digikey.com/pdf/Data%20Sheets/AMD/XC9500_CPLD_Family.pdf\" -O data/XC9500_CPLD_Family.pdf"
]
},
{
"cell_type": "markdown",
"id": "4e29a9d7-5bd9-4fb8-8ec1-4c128a748662",
"metadata": {},
"source": [
"## Initialize LlamaParse\n",
"\n",
"Initialize LlamaParse in multimodal mode, and specify the vendor as `gemini-2.0-flash-001`.\n",
"\n",
"**NOTE**: Current pricing is 2 credits for a 1 page ($0.006 USD / page). This includes core model, infra, and algorithm costs to fully process the page. "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "dc921729-3446-42ca-8e1b-a6fd26195ed9",
"metadata": {},
"outputs": [],
"source": [
"from llama_index.core.schema import TextNode\n",
"from typing import List\n",
"import json\n",
"\n",
"\n",
"def get_text_nodes(json_list: List[dict]):\n",
" text_nodes = []\n",
" for idx, page in enumerate(json_list):\n",
" text_node = TextNode(text=page[\"md\"], metadata={\"page\": page[\"page\"]})\n",
" text_nodes.append(text_node)\n",
" return text_nodes\n",
"\n",
"\n",
"def save_jsonl(data_list, filename):\n",
" \"\"\"Save a list of dictionaries as JSON Lines.\"\"\"\n",
" with open(filename, \"w\") as file:\n",
" for item in data_list:\n",
" json.dump(item, file)\n",
" file.write(\"\\n\")\n",
"\n",
"\n",
"def load_jsonl(filename):\n",
" \"\"\"Load a list of dictionaries from JSON Lines.\"\"\"\n",
" data_list = []\n",
" with open(filename, \"r\") as file:\n",
" for line in file:\n",
" data_list.append(json.loads(line))\n",
" return data_list"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f2e9d9cf-8189-4fcb-b34f-cde6cc0b59c8",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Started parsing the file under job_id 51538aa0-13e6-4429-a458-a492ba7eec04\n"
]
}
],
"source": [
"from llama_parse import LlamaParse\n",
"\n",
"parsing_instruction = \"\"\"\n",
"You are given a technical datasheet of an electronic component.\n",
"For any graphs, try to create a 2D table of relevant values, along with a description of the graph.\n",
"For any schematic diagrams, MAKE SURE to describe a list of all components and their connections to each other.\n",
"Make sure that you always parse out the text with the correct reading order.\n",
"\"\"\"\n",
"\n",
"parser = LlamaParse(\n",
" result_type=\"markdown\",\n",
" use_vendor_multimodal_model=True,\n",
" vendor_multimodal_model_name=\"gemini-2.0-flash-001\",\n",
" invalidate_cache=True,\n",
" parsing_instruction=parsing_instruction,\n",
")\n",
"json_objs = parser.get_json_result(\"./data/XC9500_CPLD_Family.pdf\")\n",
"json_list = json_objs[0][\"pages\"]\n",
"docs = get_text_nodes(json_list)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "96a81df0-1026-4e30-a930-f677dc31e344",
"metadata": {},
"outputs": [],
"source": [
"# Optional: Save\n",
"save_jsonl([d.dict() for d in docs], \"docs_gemini_2.0_flash.jsonl\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ee2e6920-8893-4b39-ae12-94d13c651406",
"metadata": {},
"outputs": [],
"source": [
"# Optional: Load\n",
"from llama_index.core import Document\n",
"\n",
"docs_dicts = load_jsonl(\"docs_gemini_2.0_flash.jsonl\")\n",
"docs = [Document.parse_obj(d) for d in docs_dicts]"
]
},
{
"cell_type": "markdown",
"id": "4f3c51b0-7878-48d7-9bc3-02b516500128",
"metadata": {},
"source": [
"### Setup GPT-4o baseline\n",
"\n",
"For comparison, we will also parse the document using GPT-4o ($0.03 per page)."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6fc3f258-50ae-4988-b904-c105463a498f",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Started parsing the file under job_id 23c6627c-2e3d-46c9-88a0-7945d7e65d96\n"
]
}
],
"source": [
"from llama_parse import LlamaParse\n",
"\n",
"parser_gpt4o = LlamaParse(\n",
" result_type=\"markdown\",\n",
" use_vendor_multimodal_model=True,\n",
" vendor_multimodal_model=\"openai-gpt4o\",\n",
" invalidate_cache=True,\n",
" parsing_instruction=parsing_instruction,\n",
")\n",
"json_objs_gpt4o = parser_gpt4o.get_json_result(\"./data/XC9500_CPLD_Family.pdf\")\n",
"json_list_gpt4o = json_objs_gpt4o[0][\"pages\"]\n",
"docs_gpt4o = get_text_nodes(json_list_gpt4o)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6a47f04e-12e1-4c80-a71d-ef7721f96401",
"metadata": {},
"outputs": [],
"source": [
"# Optional: Save\n",
"save_jsonl([d.dict() for d in docs_gpt4o], \"docs_gpt4o.jsonl\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c38b5ca3-fa87-434b-b477-bf6a4962eb3d",
"metadata": {},
"outputs": [],
"source": [
"# Optional: Load\n",
"from llama_index.core import Document\n",
"\n",
"docs_gpt4o_dicts = load_jsonl(\"docs_gpt4o.jsonl\")\n",
"docs_gpt4o = [Document.parse_obj(d) for d in docs_gpt4o_dicts]"
]
},
{
"cell_type": "markdown",
"id": "44c20f7a-2901-4dd0-b635-a4b33c5664c1",
"metadata": {},
"source": [
"## View Results\n",
"\n",
"Let's visualize the results between GPT-4o and Gemini Flash 2.0 along with the original document page."
]
},
{
"cell_type": "markdown",
"id": "bf314141-9f6d-4453-beb9-0106cdf196bf",
"metadata": {},
"source": [
"Check out an example page 2 below."
]
},
{
"cell_type": "markdown",
"id": "c70d420d-1778-4b0d-81e2-db09276e90cf",
"metadata": {},
"source": [
"![xc9500_img](XC9500_CPLD_Family_p3.png)"
]
},
{
"cell_type": "markdown",
"id": "0950ecad-248c-4c3c-98b9-ab1a9dabd5b4",
"metadata": {},
"source": [
"We see that the parsed text is fairly similar between Gemini 2.0 Flash and GPT-4o. "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "778698aa-da7e-4081-b3b5-0372f228536f",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"page: 3\n",
"\n",
"The image shows the architecture of the XC9500 In-System Programmable CPLD Family, which is marked as obsolete. Here's a breakdown of the components and their connections:\n",
"\n",
"### Components and Connections:\n",
"\n",
"1. **JTAG Port:**\n",
" - Connects to the JTAG Controller.\n",
"\n",
"2. **JTAG Controller:**\n",
" - Interfaces with the In-System Programming Controller.\n",
" - Connects to the I/O Blocks.\n",
"\n",
"3. **In-System Programming Controller:**\n",
" - Interfaces with the JTAG Controller and the Fast CONNECT Switch Matrix.\n",
"\n",
"4. **I/O Blocks:**\n",
" - Multiple I/O lines connect to the Fast CONNECT Switch Matrix.\n",
" - Includes special I/O lines for GCK, GSR, and GTS.\n",
"\n",
"5. **Fast CONNECT Switch Matrix:**\n",
" - Connects to the I/O Blocks and Function Blocks.\n",
" - Provides 36 inputs and 18 outputs to each Function Block.\n",
"\n",
"6. **Function Blocks (FB):**\n",
" - Each block contains 18 macrocells.\n",
" - Outputs from the Function Blocks drive the I/O Blocks directly.\n",
" - Multiple Function Blocks (1 to N) are shown, each with 18 macrocells.\n",
"\n",
"### Function Block Details:\n",
"\n",
"- Each Function Block consists of 18 independent macrocells.\n",
"- Capable of implementing combinatorial or registered functions.\n",
"- Receives global clock, output enable, and set/reset signals.\n",
"- Generates 18 outputs for the Fast CONNECT switch matrix.\n",
"- Logic is implemented using a sum-of-products representation.\n",
"- 36 inputs provide 72 true and complement signals to form 90 product terms.\n",
"- Product terms can be allocated to each macrocell by the product term allocator.\n",
"- Supports local feedback paths for fast counters and state machines.\n",
"\n",
"This architecture is designed for flexibility in implementing complex logic functions within a programmable logic device.\n"
]
}
],
"source": [
"# using Gemini 2.0 Flash\n",
"print(docs[2].get_content(metadata_mode=\"all\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1511a30f-3efc-4142-9668-7dc056a24d0c",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"page: 3\n",
"\n",
"The diagram illustrates the architecture of the XC9500 In-System Programmable CPLD Family. Here's a breakdown of the components and their connections:\n",
"\n",
"1. **JTAG Port**: \n",
" - Connects to the JTAG Controller.\n",
"\n",
"2. **JTAG Controller**: \n",
" - Interfaces with the In-System Programming Controller.\n",
"\n",
"3. **In-System Programming Controller**: \n",
" - Manages programming of the device.\n",
"\n",
"4. **I/O Blocks**: \n",
" - Connect to external I/O pins.\n",
" - Interface with the Fast CONNECT Switch Matrix.\n",
"\n",
"5. **Fast CONNECT Switch Matrix**: \n",
" - Connects I/O Blocks to Function Blocks.\n",
" - Provides 36 inputs and 18 outputs to each Function Block.\n",
"\n",
"6. **Function Blocks (FB)**: \n",
" - Each block contains 18 macrocells.\n",
" - Capable of implementing combinatorial or registered functions.\n",
" - Receives global clock, output enable, and set/reset signals.\n",
" - Outputs drive the Fast CONNECT Switch Matrix.\n",
" - Supports local feedback paths for fast counters and state machines.\n",
"\n",
"7. **I/O/GCK, I/O/GSR, I/O/GTS**: \n",
" - Special I/O pins for global clock, set/reset, and output enable signals.\n",
"\n",
"The architecture is designed for flexibility and high-speed operation, with each Function Block capable of handling complex logic functions.\n"
]
}
],
"source": [
"# using GPT-4o\n",
"print(docs_gpt4o[2].get_content(metadata_mode=\"all\"))"
]
},
{
"cell_type": "markdown",
"id": "705f7729-fa0f-4ca0-8562-c42afeaa8532",
"metadata": {},
"source": [
"## Setup RAG Pipeline\n",
"\n",
"Let's setup a RAG pipeline over this data.\n",
"\n",
"(we also use gpt4o-mini for the actual text synthesis step)."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5a53ee5d-cc63-421b-8896-588c83edfcf0",
"metadata": {},
"outputs": [],
"source": [
"from llama_index.core import Settings\n",
"from llama_index.llms.openai import OpenAI\n",
"from llama_index.embeddings.openai import OpenAIEmbedding\n",
"\n",
"Settings.llm = OpenAI(model=\"o3-mini\")\n",
"Settings.embed_model = OpenAIEmbedding(model=\"text-embedding-3-large\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "60972d7a-7948-4ad7-89df-57004acee917",
"metadata": {},
"outputs": [],
"source": [
"# from llama_index.core import SummaryIndex\n",
"from llama_index.core import VectorStoreIndex\n",
"from llama_index.llms.openai import OpenAI\n",
"\n",
"index = VectorStoreIndex(docs)\n",
"query_engine = index.as_query_engine(similarity_top_k=5)\n",
"\n",
"index_gpt4o = VectorStoreIndex(docs_gpt4o)\n",
"query_engine_gpt4o = index_gpt4o.as_query_engine(similarity_top_k=5)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e7df7bcb-1df4-4a01-88fc-2d596b1cc74d",
"metadata": {},
"outputs": [],
"source": [
"query = \"Give me the full output slew-Rate curve for (a) Rising and (b) Falling Outputs\"\n",
"\n",
"response = query_engine.query(query)\n",
"response_gpt4o = query_engine_gpt4o.query(query)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b7070a31-3bb8-4134-8338-20bc2fd6f3d6",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The full output slew-rate curve for (a) Rising and (b) Falling Outputs is represented in a graph where the output voltage starts at 1.5V and reaches the desired output level over a time period defined as T<sub>SLEW</sub>. The curve illustrates the gradual increase in voltage for rising outputs and the gradual decrease for falling outputs, effectively showing how the output edge rates can be controlled to reduce system noise.\n"
]
}
],
"source": [
"print(response)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7bee8167-f021-4c87-8d28-9f40a4f7b69d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"# XC9500 In-System Programmable CPLD Family\n",
"\n",
"Each output has independent slew rate control. Output edge rates may be slowed down to reduce system noise (with an additional time delay of T<sub>SLEW</sub>) through programming. See Figure 11.\n",
"\n",
"Each IOB provides user programmable ground pin capability. This allows device I/O pins to be configured as additional ground pins. By tying strategically located programmable ground pins to the external ground connection, system noise generated from large numbers of simultaneous switching outputs may be reduced.\n",
"\n",
"A control pull-up resistor (typically 10K ohms) is attached to each device I/O pin to prevent them from floating when the device is not in normal user operation. This resistor is active during device programming mode and system power-up. It is also activated for an erased device. The resistor is deactivated during normal operation.\n",
"\n",
"The output driver is capable of supplying 24 mA output drive. All output drivers in the device may be configured for either 5V TTL levels or 3.3V levels by connecting the device output voltage supply (V<sub>CCIO</sub>) to a 5V or 3.3V voltage supply. Figure 12 shows how the XC9500 device can be used in 5V only and mixed 3.3V/5V systems.\n",
"\n",
"## Pin-Locking Capability\n",
"\n",
"The capability to lock the user defined pin assignments during design changes depends on the ability of the architecture to adapt to unexpected changes. The XC9500 devices have architectural features that enhance the ability to accept design changes while maintaining the same pinout.\n",
"\n",
"The XC9500 architecture provides maximum routing within the Fast CONNECT switch matrix, and incorporates a flexible Function Block that allows block-wide allocation of available product terms. This provides a high level of confidence of maintaining both input and output pin assignments for unexpected design changes.\n",
"\n",
"For extensive design changes requiring higher logic capacity than is available in the initially chosen device, the new design may be able to fit into a larger pin-compatible device using the same pin assignments. The same board may be used with a higher density device without the expense of board rework.\n",
"\n",
"!Output slew-Rate for (a) Rising and (b) Falling Outputs\n",
"\n",
"**Figure 11:** Output slew-Rate for (a) Rising and (b) Falling Outputs\n",
"\n",
"| Output Voltage | Time |\n",
"|----------------|------|\n",
"| 1.5V | 0 |\n",
"| T<sub>SLEW</sub> | |\n",
"\n",
"**Figure 12:** XC9500 Devices in (a) 5V Systems and (b) Mixed 5V/3.3V Systems\n",
"\n",
"| 5V CMOS or 5V TTL | 3.3V |\n",
"|-------------------|------|\n",
"| 5V | 0V |\n",
"| 3.6V | 0V |\n",
"| 3.3V | 0V |\n",
"\n",
"- **(a) 5V System:**\n",
" - V<sub>CCINT</sub> V<sub>CCIO</sub>\n",
" - XC9500 CPLD\n",
" - IN OUT\n",
" - GND\n",
"\n",
"- **(b) Mixed 5V/3.3V System:**\n",
" - V<sub>CCINT</sub> V<sub>CCIO</sub>\n",
" - XC9500 CPLD\n",
" - IN OUT\n",
" - GND\n",
"\n",
"www.xilinx.com\n",
"\n",
"DS063 (v6.0) May 17, 2013 \n",
"Product Specification\n"
]
}
],
"source": [
"print(response.source_nodes[0].get_content())"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5f9fef7f-510b-46a5-8716-f5616f542035",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The output slew-rate curve for (a) Rising and (b) Falling Outputs is represented in a timing diagram where the output voltage transitions from a low state to a high state and vice versa. \n",
"\n",
"For the rising output, the curve starts at 1.5V and transitions to the desired output voltage level over a time period defined as T<sub>SLEW</sub>. \n",
"\n",
"For the falling output, the curve similarly begins at the high output voltage and decreases to a low state, also taking the time defined as T<sub>SLEW</sub> to complete the transition.\n",
"\n",
"The specific values and graphical representation would typically be illustrated in a figure, but the key takeaway is that the output slew rate can be controlled to manage system noise by programming the desired T<sub>SLEW</sub> time.\n"
]
}
],
"source": [
"print(response_gpt4o)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d40f9dd4-2dd4-4fa5-b636-1f901dc1601b",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"# XC9500 In-System Programmable CPLD Family\n",
"\n",
"Each output has independent slew rate control. Output edge rates may be slowed down to reduce system noise (with an additional time delay of T<sub>SLEW</sub>) through programming. See Figure 11.\n",
"\n",
"Each IOB provides user programmable ground pin capability. This allows device I/O pins to be configured as additional ground pins. By tying strategically located programmable ground pins to the external ground connection, system noise generated from large numbers of simultaneous switching outputs may be reduced.\n",
"\n",
"A control pull-up resistor (typically 10K ohms) is attached to each device I/O pin to prevent them from floating when the device is not in normal user operation. This resistor is active during device programming mode and system power-up. It is also activated for an erased device. The resistor is deactivated during normal operation.\n",
"\n",
"The output driver is capable of supplying 24 mA output drive. All output drivers in the device may be configured for either 5V TTL levels or 3.3V levels by connecting the device output voltage supply (V<sub>CCIO</sub>) to a 5V or 3.3V voltage supply. Figure 12 shows how the XC9500 device can be used in 5V only and mixed 3.3V/5V systems.\n",
"\n",
"## Pin-Locking Capability\n",
"\n",
"The capability to lock the user defined pin assignments during design changes depends on the ability of the architecture to adapt to unexpected changes. The XC9500 devices have architectural features that enhance the ability to accept design changes while maintaining the same pinout.\n",
"\n",
"The XC9500 architecture provides maximum routing within the Fast CONNECT switch matrix, and incorporates a flexible Function Block that allows block-wide allocation of available product terms. This provides a high level of confidence of maintaining both input and output pin assignments for unexpected design changes.\n",
"\n",
"For extensive design changes requiring higher logic capacity than is available in the initially chosen device, the new design may be able to fit into a larger pin-compatible device using the same pin assignments. The same board may be used with a higher density device without the expense of board rework.\n",
"\n",
"!Output slew-Rate for (a) Rising and (b) Falling Outputs\n",
"\n",
"**Figure 11:** Output slew-Rate for (a) Rising and (b) Falling Outputs\n",
"\n",
"| Output Voltage | Time |\n",
"|----------------|------|\n",
"| 1.5V | 0 |\n",
"| T<sub>SLEW</sub> | |\n",
"\n",
"**Figure 12:** XC9500 Devices in (a) 5V Systems and (b) Mixed 5V/3.3V Systems\n",
"\n",
"| 5V CMOS or 5V TTL | 3.3V |\n",
"|-------------------|------|\n",
"| 5V | 0V |\n",
"| 3.6V | 0V |\n",
"| 3.3V | 0V |\n",
"\n",
"- **XC9500 CPLD** \n",
" - **IN** \n",
" - **OUT** \n",
" - **GND** \n",
"\n",
"www.xilinx.com \n",
"DS063 (v6.0) May 17, 2013 \n",
"Product Specification\n"
]
}
],
"source": [
"print(response_gpt4o.source_nodes[0].get_content())"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "llama_parse",
"language": "python",
"name": "llama_parse"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+1 -1
View File
@@ -7,7 +7,7 @@
"source": [
"# Multimodal Parsing using GPT4o-mini\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/multimodal/gpt4o_mini.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/multimodal/gpt4o_mini.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"This cookbook shows you how to use LlamaParse to parse any document with the multimodal capabilities of GPT4o-mini.\n",
"\n",
@@ -6,7 +6,7 @@
"source": [
"# Building a Multimodal RAG Pipeline over an Auto Insurance Claim\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/multimodal/insurance_rag.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/multimodal/insurance_rag.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
+1 -1
View File
@@ -6,7 +6,7 @@
"source": [
"# Building a RAG Pipeline over Legal Documents\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/multimodal/legal_rag.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/multimodal/legal_rag.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"This example shows how LlamaParse and LlamaIndex can be used to parse various types of legal documents, which may contain complex tabular data. The advantage of this is being able to quickly retrieve a specific answer to a legal question with comprehensive context — knowledge of precedents, statutes, and cases presented in the given documents. A user can quickly find the answer to or find out more details about a specific legal question without having to read through the often long documents by using LLMs.\n",
"\n",
@@ -7,7 +7,7 @@
"source": [
"# Contextual Retrieval for Multimodal RAG\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/multimodal/multimodal_contextual_retrieval_rag.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/multimodal/multimodal_contextual_retrieval_rag.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"In this cookbook we show you how to build a multimodal RAG pipeline with **contextual retrieval**.\n",
"\n",
@@ -7,7 +7,7 @@
"source": [
"# Building a Natively Multimodal RAG Pipeline (over a Slide Deck)\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/multimodal/multimodal_rag_slide_deck.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/multimodal/multimodal_rag_slide_deck.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"In this cookbook we show you how to build a multimodal RAG pipeline over a slide deck, with text, tables, images, diagrams, and complex layouts.\n",
"\n",
@@ -7,7 +7,7 @@
"source": [
"# Multimodal Report Generation (from a Slide Deck)\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/multimodal/multimodal_report_generation.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/multimodal/multimodal_report_generation.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"In this cookbook we show you how to build a multimodal report generator. The pipeline parses a slide deck and stores both text and image chunks. It generates a detailed response that contains interleaving text and images.\n",
"\n",
@@ -7,7 +7,7 @@
"source": [
"# Multimodal Report Generation Agent \n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/multimodal/multimodal_report_generation_agent.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/multimodal/multimodal_report_generation_agent.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"In this cookbook we show you how to build a multimodal report generation agent from a bank of research reports. We use the a set of ICLR papers (which were also used as the dataset in our [DeepLearning.ai course](https://www.deeplearning.ai/short-courses/building-agentic-rag-with-llamaindex/?utm_campaign=llamaindexC2-launch&utm_medium=headband&utm_source=dlai-homepage).\n",
"\n",
@@ -6,7 +6,7 @@
"source": [
"# Building a RAG Pipeline over IKEA Product Instruction Manuals\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/multimodal/product_manual_rag.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/multimodal/product_manual_rag.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
@@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/parsing_instructions.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/parsing_instructions.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"# Parsing documents with Instructions\n",
"\n",
@@ -6,7 +6,7 @@
"source": [
"# Cost-Optimized Parsing with Auto-Mode\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/demo_auto_mode.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/parsing_modes/demo_auto_mode.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"![](diagram.jpg)\n",
"\n",
@@ -735,7 +735,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"In this example, these pages aren't going to be that different when parsed, but we can verify which pages triggered auto-made by looking at the [JSON output](https://github.com/run-llama/llama_parse/blob/main/examples/demo_json_tour.ipynb) of LlamaParse:"
"In this example, these pages aren't going to be that different when parsed, but we can verify which pages triggered auto-made by looking at the [JSON output](https://github.com/run-llama/llama_cloud_services/blob/main/examples/demo_json_tour.ipynb) of LlamaParse:"
]
},
{
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 828 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 KiB

@@ -7,7 +7,7 @@
"source": [
"# RFP Response Generation Workflow\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/report_generation/rfp_response/generate_rfp.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/report_generation/rfp_response/generate_rfp.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"This notebook shows you how to build a workflow to generate a response to an RFP. \n",
"\n",
@@ -20,7 +20,7 @@
"\n",
"We use LlamaParse to parse the context documents as well as the RFP document itself.\n",
"\n",
"**NOTE**: If you want to skip the indexing complexity and use LlamaCloud instead, check out the [RFP Example using LlamaCloud](https://github.com/run-llama/llamacloud-demo/blob/main/examples/report_generation/rfp_response/generate_rfp.ipynb)."
"**NOTE**: If you want to skip the indexing complexity and use LlamaCloud instead, check out the [RFP Example using LlamaCloud](https://github.com/run-llama/llama_cloud_services-demo/blob/main/examples/report_generation/rfp_response/generate_rfp.ipynb)."
]
},
{
@@ -8,7 +8,7 @@
"# LlamaParse with GPT-4o\n",
"\n",
"\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_parse/blob/main/examples/test_tesla_impact_report/test_gpt4o.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"<a href=\"https://colab.research.google.com/github/run-llama/llama_cloud_services/blob/main/examples/parse/test_tesla_impact_report/test_gpt4o.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
"\n",
"GPT-4o is a [fully multimodal model by OpenAI](https://openai.com/index/hello-gpt-4o/) released in May 2024. It matches GPT-4 Turbo performance in text and code, and has significantly improved vision and audio capabilities.\n",
"\n",
+183
View File
@@ -0,0 +1,183 @@
# LlamaExtract
LlamaExtract provides a simple API for extracting structured data from unstructured documents like PDFs, text files and images (upcoming).
## Quick Start
```python
from llama_cloud_services import LlamaExtract
from pydantic import BaseModel, Field
# Initialize client
extractor = LlamaExtract()
# Define schema using Pydantic
class Resume(BaseModel):
name: str = Field(description="Full name of candidate")
email: str = Field(description="Email address")
skills: list[str] = Field(description="Technical skills and technologies")
# Create extraction agent
agent = extractor.create_agent(name="resume-parser", data_schema=Resume)
# Extract data from document
result = agent.extract("resume.pdf")
print(result.data)
```
## Core Concepts
- **Extraction Agents**: Reusable extractors configured with a specific schema and extraction settings.
- **Data Schema**: Structure definition for the data you want to extract in the form of a JSON schema or a Pydantic model.
- **Extraction Jobs**: Asynchronous extraction tasks that can be monitored.
## Defining Schemas
Schemas can be defined using either Pydantic models or JSON Schema:
### Using Pydantic (Recommended)
```python
from pydantic import BaseModel, Field
from typing import List, Optional
class Experience(BaseModel):
company: str = Field(description="Company name")
title: str = Field(description="Job title")
start_date: Optional[str] = Field(description="Start date of employment")
end_date: Optional[str] = Field(description="End date of employment")
class Resume(BaseModel):
name: str = Field(description="Candidate name")
experience: List[Experience] = Field(description="Work history")
```
### Using JSON Schema
```python
schema = {
"type": "object",
"properties": {
"name": {"type": "string", "description": "Candidate name"},
"experience": {
"type": "array",
"description": "Work history",
"items": {
"type": "object",
"properties": {
"company": {
"type": "string",
"description": "Company name",
},
"title": {"type": "string", "description": "Job title"},
"start_date": {
"anyOf": [{"type": "string"}, {"type": "null"}],
"description": "Start date of employment",
},
"end_date": {
"anyOf": [{"type": "string"}, {"type": "null"}],
"description": "End date of employment",
},
},
},
},
},
}
agent = extractor.create_agent(name="resume-parser", data_schema=schema)
```
### Important restrictions on JSON/Pydantic Schema
_LlamaExtract only supports a subset of the JSON Schema specification._ While limited, it should
be sufficient for a wide variety of use-cases.
- All fields are required by default. Nullable fields must be explicitly marked as such,
using `"anyOf"` with a `"null"` type. See `"start_date"` field above.
- Root node must be of type `"object"`.
- Schema nesting must be limited to within 5 levels.
- The important fields are key names/titles, type and description. Fields for
formatting, default values, etc. are not supported.
- There are other restrictions on number of keys, size of the schema, etc. that you may
hit for complex extraction use cases. In such cases, it is worth thinking how to restructure
your extraction workflow to fit within these constraints, e.g. by extracting subset of fields
and later merging them together.
## Other Extraction APIs
### Batch Processing
Process multiple files asynchronously:
```python
# Queue multiple files for extraction
jobs = await agent.queue_extraction(["resume1.pdf", "resume2.pdf"])
# Check job status
for job in jobs:
status = agent.get_extraction_job(job.id).status
print(f"Job {job.id}: {status}")
# Get results when complete
results = [agent.get_extraction_run_for_job(job.id) for job in jobs]
```
### Updating Schemas
Schemas can be modified and updated after creation:
```python
# Update schema
agent.data_schema = new_schema
# Save changes
agent.save()
```
### Managing Agents
```python
# List all agents
agents = extractor.list_agents()
# Get specific agent
agent = extractor.get_agent(name="resume-parser")
# Delete agent
extractor.delete_agent(agent.id)
```
## Installation
```bash
pip install llama-extract==0.1.0
```
## Tips & Best Practices
1. **Schema Design**:
- Try to limit schema nesting to 3-4 levels.
- Make fields optional when data might not always be present. Having required fields may force the model
to hallucinate when these fields are not present in the documents.
- When you want to extract a variable number of entities, use an `array` type. Note that you cannot use
an `array` type for the root node.
- Use descriptive field names and detailed descriptions. Use descriptions to pass formatting
instructions or few-shot examples.
- Start simple and iteratively build your schema to incorporate requirements.
2. **Running Extractions**:
- Note that resetting `agent.schema` will not save the schema to the database,
until you call `agent.save`, but it will be used for running extractions.
- Check job status prior to accessing results. Any extraction error should be available as
part of `job.error` or `extraction_run.error` fields for debugging.
- Consider async operations (`queue_extraction`) for large-scale extraction once you have finalized your schema.
## Additional Resources
- [Example Notebook](examples/resume_screening.ipynb) - Detailed walkthrough of resume parsing
- [Discord Community](https://discord.com/invite/eN6D2HQ4aX) - Get help and share feedback
+5
View File
@@ -1,8 +1,13 @@
from llama_cloud_services.parse import LlamaParse
from llama_cloud_services.report import ReportClient, LlamaReport
from llama_cloud_services.extract import LlamaExtract, ExtractionAgent
from llama_cloud_services.constants import EU_BASE_URL
__all__ = [
"LlamaParse",
"ReportClient",
"LlamaReport",
"LlamaExtract",
"ExtractionAgent",
"EU_BASE_URL",
]
+1
View File
@@ -0,0 +1 @@
EU_BASE_URL = "https://api.cloud.eu.llamaindex.ai"
+7
View File
@@ -0,0 +1,7 @@
from llama_cloud_services.extract.extract import (
LlamaExtract,
ExtractionAgent,
SourceText,
)
__all__ = ["LlamaExtract", "ExtractionAgent", "SourceText"]
+817
View File
@@ -0,0 +1,817 @@
import asyncio
import os
import time
from io import BufferedIOBase, BufferedReader, BytesIO, TextIOWrapper
from pathlib import Path
from typing import List, Optional, Type, Union, Coroutine, Any, TypeVar
import warnings
import httpx
from pydantic import BaseModel
from llama_cloud import (
ExtractAgent as CloudExtractAgent,
ExtractAgentCreate,
ExtractConfig,
ExtractJob,
ExtractJobCreate,
ExtractRun,
ExtractSchemaValidateRequest,
ExtractAgentUpdate,
File,
ExtractMode,
StatusEnum,
Project,
ExtractTarget,
LlamaExtractSettings,
PaginatedExtractRunsResponse,
)
from llama_cloud.client import AsyncLlamaCloud
from llama_cloud_services.extract.utils import JSONObjectType, augment_async_errors
from llama_index.core.schema import BaseComponent
from llama_index.core.async_utils import run_jobs
from llama_index.core.bridge.pydantic import Field, PrivateAttr
from llama_index.core.constants import DEFAULT_BASE_URL
from concurrent.futures import ThreadPoolExecutor
T = TypeVar("T")
SchemaInput = Union[JSONObjectType, Type[BaseModel]]
DEFAULT_EXTRACT_CONFIG = ExtractConfig(
extraction_target=ExtractTarget.PER_DOC,
extraction_mode=ExtractMode.BALANCED,
)
class SourceText:
def __init__(
self,
*,
file: Union[bytes, BufferedIOBase, TextIOWrapper, str, Path, None] = None,
text_content: Optional[str] = None,
filename: Optional[str] = None,
):
self.file = file
self.filename = filename
self.text_content = text_content
self._validate()
def _validate(self) -> None:
"""Ensure filename is provided when needed."""
if not ((self.file is None) ^ (self.text_content is None)):
raise ValueError("Either file or text_content must be provided.")
if self.text_content is not None:
if not self.filename:
self.filename = "text_input.txt"
return
if isinstance(self.file, (bytes, BufferedIOBase, TextIOWrapper)):
if not self.filename and hasattr(self.file, "name"):
self.filename = os.path.basename(str(self.file.name))
elif not hasattr(self.file, "name") and self.filename is None:
raise ValueError(
"filename must be provided when file is bytes or a file-like object without a name"
)
elif isinstance(self.file, (str, Path)):
if not self.filename:
self.filename = os.path.basename(str(self.file))
else:
raise ValueError(f"Unsupported file type: {type(self.file)}")
FileInput = Union[str, Path, BufferedIOBase, SourceText]
def run_in_thread(
coro: Coroutine[Any, Any, T],
thread_pool: ThreadPoolExecutor,
verify: bool,
httpx_timeout: float,
client_wrapper: Any,
) -> T:
"""Run coroutine in a thread with proper client management."""
async def wrapped_coro() -> T:
client = httpx.AsyncClient(
verify=verify,
timeout=httpx_timeout,
limits=httpx.Limits(max_keepalive_connections=100, max_connections=100),
)
original_client = client_wrapper.httpx_client
try:
client_wrapper.httpx_client = client
return await coro
finally:
client_wrapper.httpx_client = original_client
await client.aclose()
def run_coro() -> T:
try:
return asyncio.run(wrapped_coro())
except httpx.TimeoutException as e:
raise TimeoutError(f"Request timed out: {str(e)}") from e
except httpx.NetworkError as e:
raise ConnectionError(f"Network error: {str(e)}") from e
return thread_pool.submit(run_coro).result()
class ExtractionAgent:
"""Class representing a single extraction agent with methods for extraction operations."""
def __init__(
self,
client: AsyncLlamaCloud,
agent: CloudExtractAgent,
project_id: Optional[str] = None,
organization_id: Optional[str] = None,
check_interval: int = 1,
max_timeout: int = 2000,
num_workers: int = 4,
show_progress: bool = True,
verbose: bool = False,
verify: Optional[bool] = True,
httpx_timeout: Optional[float] = 60,
):
self._client = client
self._agent = agent
self._project_id = project_id
self._organization_id = organization_id
self.check_interval = check_interval
self.max_timeout = max_timeout
self.num_workers = num_workers
self.show_progress = show_progress
self.verify = verify
self.httpx_timeout = httpx_timeout
self._verbose = verbose
self._data_schema: Union[JSONObjectType, None] = None
self._config: Union[ExtractConfig, None] = None
self._thread_pool = ThreadPoolExecutor(
max_workers=min(10, (os.cpu_count() or 1) + 4)
)
@property
def id(self) -> str:
return self._agent.id
@property
def name(self) -> str:
return self._agent.name
@property
def data_schema(self) -> dict:
return self._agent.data_schema if not self._data_schema else self._data_schema
@data_schema.setter
def data_schema(self, data_schema: SchemaInput) -> None:
processed_schema: JSONObjectType
if isinstance(data_schema, dict):
# TODO: if we expose a get_validated JSON schema method, we can use it here
processed_schema = data_schema # type: ignore
elif isinstance(data_schema, type) and issubclass(data_schema, BaseModel):
processed_schema = data_schema.model_json_schema()
else:
raise ValueError(
"data_schema must be either a dictionary or a Pydantic model"
)
validated_schema = self._run_in_thread(
self._client.llama_extract.validate_extraction_schema(
request=ExtractSchemaValidateRequest(data_schema=processed_schema)
)
)
self._data_schema = validated_schema.data_schema
@property
def config(self) -> ExtractConfig:
return self._agent.config if not self._config else self._config
@config.setter
def config(self, config: ExtractConfig) -> None:
self._config = config
def _run_in_thread(self, coro: Coroutine[Any, Any, T]) -> T:
"""Run coroutine in a separate thread to avoid event loop issues"""
return run_in_thread(
coro,
self._thread_pool,
self.verify, # type: ignore
self.httpx_timeout, # type: ignore
self._client._client_wrapper,
)
async def upload_file(self, file_input: SourceText) -> File:
"""Upload a file for extraction.
Args:
file_input: The file to upload (path, bytes, or file-like object)
Raises:
ValueError: If filename is not provided for bytes input or for file-like objects
without a name attribute.
"""
try:
file_contents: Union[BufferedIOBase, BytesIO]
if file_input.text_content is not None:
# Handle direct text content
file_contents = BytesIO(file_input.text_content.encode("utf-8"))
elif isinstance(file_input.file, TextIOWrapper):
# Handle text-based IO objects
file_contents = BytesIO(file_input.file.read().encode("utf-8"))
elif isinstance(file_input.file, (str, Path)):
# Handle file paths
file_contents = open(file_input.file, "rb")
elif isinstance(file_input.file, bytes):
# Handle bytes
file_contents = BytesIO(file_input.file)
elif isinstance(file_input.file, BufferedIOBase):
# Handle binary IO objects
file_contents = file_input.file
else:
raise ValueError(f"Unsupported file type: {type(file_input.file)}")
# Add name attribute to file object if needed
if not hasattr(file_contents, "name"):
file_contents.name = file_input.filename # type: ignore
return await self._client.files.upload_file(
project_id=self._project_id, upload_file=file_contents
)
finally:
if isinstance(file_contents, BufferedReader):
file_contents.close()
async def _upload_file(self, file_input: FileInput) -> File:
source_text = None
if isinstance(file_input, SourceText):
source_text = file_input
elif isinstance(file_input, (str, Path)):
path = Path(file_input)
source_text = SourceText(file=path, filename=path.name)
else:
# Try to get filename from the file object if not provided
filename = None
if hasattr(file_input, "name"):
filename = os.path.basename(str(file_input.name))
if filename is None:
raise ValueError(
"Use SourceText to provide filename when uploading bytes or file-like objects."
)
warnings.warn(
"Use SourceText instead of bytes or file-like objects",
DeprecationWarning,
)
source_text = SourceText(file=file_input, filename=filename)
return await self.upload_file(source_text)
async def _wait_for_job_result(self, job_id: str) -> Optional[ExtractRun]:
"""Wait for and return the results of an extraction job."""
start = time.perf_counter()
tries = 0
while True:
await asyncio.sleep(self.check_interval)
tries += 1
job = await self._client.llama_extract.get_job(
job_id=job_id,
)
if job.status == StatusEnum.SUCCESS:
return await self._client.llama_extract.get_run_by_job_id(
job_id=job_id,
)
elif job.status == StatusEnum.PENDING:
end = time.perf_counter()
if end - start > self.max_timeout:
raise Exception(f"Timeout while extracting the file: {job_id}")
if self._verbose and tries % 10 == 0:
print(".", end="", flush=True)
continue
else:
warnings.warn(
f"Failure in job: {job_id}, status: {job.status}, error: {job.error}"
)
return await self._client.llama_extract.get_run_by_job_id(
job_id=job_id,
)
def save(self) -> None:
"""Persist the extraction agent's schema and config to the database.
Returns:
ExtractionAgent: The updated extraction agent
"""
self._agent = self._run_in_thread(
self._client.llama_extract.update_extraction_agent(
extraction_agent_id=self.id,
request=ExtractAgentUpdate(
data_schema=self.data_schema,
config=self.config,
),
)
)
async def _run_extraction_test(
self,
files: Union[FileInput, List[FileInput]],
extract_settings: LlamaExtractSettings,
) -> Union[ExtractJob, List[ExtractJob]]:
if not isinstance(files, list):
files = [files]
single_file = True
else:
single_file = False
upload_tasks = [self._upload_file(file) for file in files]
with augment_async_errors():
uploaded_files = await run_jobs(
upload_tasks,
workers=self.num_workers,
desc="Uploading files",
show_progress=self.show_progress,
)
async def run_job(file: File) -> ExtractRun:
job_queued = await self._client.llama_extract.run_job_test_user(
job_create=ExtractJobCreate(
extraction_agent_id=self.id,
file_id=file.id,
data_schema_override=self.data_schema,
config_override=self.config,
),
extract_settings=extract_settings,
)
return await self._wait_for_job_result(job_queued.id)
job_tasks = [run_job(file) for file in uploaded_files]
with augment_async_errors():
extract_results = await run_jobs(
job_tasks,
workers=self.num_workers,
desc="Running extraction jobs",
show_progress=self.show_progress,
)
if self._verbose:
for file, job in zip(files, extract_results):
file_repr = (
str(file) if isinstance(file, (str, Path)) else "<bytes/buffer>"
)
print(f"Running extraction for file {file_repr} under job_id {job.id}")
return extract_results[0] if single_file else extract_results
async def queue_extraction(
self,
files: Union[FileInput, List[FileInput]],
) -> Union[ExtractJob, List[ExtractJob]]:
"""
Queue multiple files for extraction.
Args:
files (Union[FileInput, List[FileInput]]): The files to extract
Returns:
Union[ExtractJob, List[ExtractJob]]: The queued extraction jobs
"""
"""Queue one or more files for extraction concurrently."""
if not isinstance(files, list):
files = [files]
single_file = True
else:
single_file = False
upload_tasks = [self._upload_file(file) for file in files]
with augment_async_errors():
uploaded_files = await run_jobs(
upload_tasks,
workers=self.num_workers,
desc="Uploading files",
show_progress=self.show_progress,
)
job_tasks = [
self._client.llama_extract.run_job(
request=ExtractJobCreate(
extraction_agent_id=self.id,
file_id=file.id,
data_schema_override=self.data_schema,
config_override=self.config,
),
)
for file in uploaded_files
]
with augment_async_errors():
extract_jobs = await run_jobs(
job_tasks,
workers=self.num_workers,
desc="Creating extraction jobs",
show_progress=self.show_progress,
)
if self._verbose:
for file, job in zip(files, extract_jobs):
file_repr = (
str(file) if isinstance(file, (str, Path)) else "<bytes/buffer>"
)
print(
f"Queued file extraction for file {file_repr} under job_id {job.id}"
)
return extract_jobs[0] if single_file else extract_jobs
async def aextract(
self, files: Union[FileInput, List[FileInput]]
) -> Union[ExtractRun, List[ExtractRun]]:
"""Asynchronously extract data from one or more files using this agent.
Args:
files (Union[FileInput, List[FileInput]]): The files to extract
Returns:
Union[ExtractRun, List[ExtractRun]]: The extraction results
"""
if not isinstance(files, list):
files = [files]
single_file = True
else:
single_file = False
# Queue all files for extraction
jobs = await self.queue_extraction(files)
# Wait for all results concurrently
result_tasks = [self._wait_for_job_result(job.id) for job in jobs]
with augment_async_errors():
results = await run_jobs(
result_tasks,
workers=self.num_workers,
desc="Extracting files",
show_progress=self.show_progress,
)
return results[0] if single_file else results
def extract(
self, files: Union[FileInput, List[FileInput]]
) -> Union[ExtractRun, List[ExtractRun]]:
"""Synchronously extract data from one or more files using this agent.
Args:
files (Union[FileInput, List[FileInput]]): The files to extract
Returns:
Union[ExtractRun, List[ExtractRun]]: The extraction results
"""
return self._run_in_thread(self.aextract(files))
def get_extraction_job(self, job_id: str) -> ExtractJob:
"""
Get the extraction job for a given job_id.
Args:
job_id (str): The job_id to get the extraction job for
Returns:
ExtractJob: The extraction job
"""
return self._run_in_thread(self._client.llama_extract.get_job(job_id=job_id))
def get_extraction_run_for_job(self, job_id: str) -> ExtractRun:
"""
Get the extraction run for a given job_id.
Args:
job_id (str): The job_id to get the extraction run for
Returns:
ExtractRun: The extraction run
"""
return self._run_in_thread(
self._client.llama_extract.get_run_by_job_id(
job_id=job_id,
)
)
def delete_extraction_run(self, run_id: str) -> None:
"""Delete an extraction run by ID.
Args:
run_id (str): The ID of the extraction run to delete
"""
self._run_in_thread(
self._client.llama_extract.delete_extraction_run(run_id=run_id)
)
def list_extraction_runs(
self, page: int = 0, limit: int = 100
) -> PaginatedExtractRunsResponse:
"""List extraction runs for the extraction agent.
Returns:
PaginatedExtractRunsResponse: Paginated list of extraction runs
"""
return self._run_in_thread(
self._client.llama_extract.list_extract_runs(
extraction_agent_id=self.id,
skip=page * limit,
limit=limit,
)
)
def __repr__(self) -> str:
return f"ExtractionAgent(id={self.id}, name={self.name})"
def __del__(self) -> None:
"""Cleanup resources properly."""
try:
if hasattr(self, "_thread_pool"):
self._thread_pool.shutdown(wait=True)
except Exception:
pass # Suppress exceptions during cleanup
class LlamaExtract(BaseComponent):
"""Factory class for creating and managing extraction agents."""
api_key: str = Field(description="The API key for the LlamaExtract API.")
base_url: str = Field(description="The base URL of the LlamaExtract API.")
check_interval: int = Field(
default=1,
description="The interval in seconds to check if the extraction is done.",
)
max_timeout: int = Field(
default=2000,
description="The maximum timeout in seconds to wait for the extraction to finish.",
)
num_workers: int = Field(
default=4,
gt=0,
lt=10,
description="The number of workers to use sending API requests for extraction.",
)
show_progress: bool = Field(
default=True, description="Show progress when extracting multiple files."
)
verbose: bool = Field(
default=False, description="Show verbose output when extracting files."
)
verify: Optional[bool] = Field(
default=True, description="Simple SSL verification option."
)
httpx_timeout: Optional[float] = Field(
default=60, description="Timeout for the httpx client."
)
_async_client: AsyncLlamaCloud = PrivateAttr()
_thread_pool: ThreadPoolExecutor = PrivateAttr()
_project_id: Optional[str] = PrivateAttr()
_organization_id: Optional[str] = PrivateAttr()
def __init__(
self,
api_key: Optional[str] = None,
base_url: Optional[str] = None,
check_interval: int = 1,
max_timeout: int = 2000,
num_workers: int = 4,
show_progress: bool = True,
project_id: Optional[str] = None,
organization_id: Optional[str] = None,
verify: Optional[bool] = True,
httpx_timeout: Optional[float] = 60,
verbose: bool = False,
):
if not api_key:
api_key = os.getenv("LLAMA_CLOUD_API_KEY", None)
if api_key is None:
raise ValueError("The API key is required.")
if not base_url:
base_url = os.getenv("LLAMA_CLOUD_BASE_URL", None) or DEFAULT_BASE_URL
super().__init__(
api_key=api_key,
base_url=base_url,
check_interval=check_interval,
max_timeout=max_timeout,
num_workers=num_workers,
show_progress=show_progress,
verify=verify,
httpx_timeout=httpx_timeout,
verbose=verbose,
)
self._httpx_client = httpx.AsyncClient(verify=verify, timeout=httpx_timeout)
self.verify = verify
self.httpx_timeout = httpx_timeout
self._async_client = AsyncLlamaCloud(
token=self.api_key,
base_url=self.base_url,
httpx_client=self._httpx_client,
)
self._thread_pool = ThreadPoolExecutor(
max_workers=min(10, (os.cpu_count() or 1) + 4)
)
# Fetch default project id if not provided
if not project_id:
project_id = os.getenv("LLAMA_CLOUD_PROJECT_ID", None)
if not project_id:
print("No project_id provided, fetching default project.")
projects: List[Project] = self._run_in_thread(
self._async_client.projects.list_projects()
)
default_project = [p for p in projects if p.is_default]
if not default_project:
raise ValueError(
"No default project found. Please provide a project_id."
)
project_id = default_project[0].id
self._project_id = project_id
self._organization_id = organization_id
def _run_in_thread(self, coro: Coroutine[Any, Any, T]) -> T:
"""Run coroutine in a separate thread to avoid event loop issues"""
return run_in_thread(
coro,
self._thread_pool,
self.verify, # type: ignore
self.httpx_timeout, # type: ignore
self._async_client._client_wrapper,
)
def create_agent(
self,
name: str,
data_schema: SchemaInput,
config: Optional[ExtractConfig] = None,
) -> ExtractionAgent:
"""Create a new extraction agent.
Args:
name (str): The name of the extraction agent
data_schema (SchemaInput): The data schema for the extraction agent
config (Optional[ExtractConfig]): The extraction config for the agent
Returns:
ExtractionAgent: The created extraction agent
"""
if config is not None:
if config.extraction_mode == ExtractMode.ACCURATE:
warnings.warn(
"ACCURATE extraction mode is deprecated. Using BALANCED instead."
)
config.extraction_mode = ExtractMode.BALANCED
else:
config = DEFAULT_EXTRACT_CONFIG
if isinstance(data_schema, dict):
data_schema = data_schema
elif issubclass(data_schema, BaseModel):
data_schema = data_schema.model_json_schema()
else:
raise ValueError(
"data_schema must be either a dictionary or a Pydantic model"
)
agent = self._run_in_thread(
self._async_client.llama_extract.create_extraction_agent(
project_id=self._project_id,
organization_id=self._organization_id,
request=ExtractAgentCreate(
name=name,
data_schema=data_schema,
config=config,
),
)
)
return ExtractionAgent(
client=self._async_client,
agent=agent,
project_id=self._project_id,
organization_id=self._organization_id,
check_interval=self.check_interval,
max_timeout=self.max_timeout,
num_workers=self.num_workers,
show_progress=self.show_progress,
verbose=self.verbose,
)
def get_agent(
self,
name: Optional[str] = None,
id: Optional[str] = None,
) -> ExtractionAgent:
"""Get extraction agents by name or extraction agent ID.
Args:
name (Optional[str]): Filter by name
extraction_agent_id (Optional[str]): Filter by extraction agent ID
Returns:
ExtractionAgent: The extraction agent
"""
if id is not None and name is not None:
warnings.warn(
"Both name and extraction_agent_id are provided. Using extraction_agent_id."
)
if id:
agent = self._run_in_thread(
self._async_client.llama_extract.get_extraction_agent(
extraction_agent_id=id,
)
)
elif name:
agent = self._run_in_thread(
self._async_client.llama_extract.get_extraction_agent_by_name(
name=name,
project_id=self._project_id,
)
)
else:
raise ValueError("Either name or extraction_agent_id must be provided.")
return ExtractionAgent(
client=self._async_client,
agent=agent,
project_id=self._project_id,
organization_id=self._organization_id,
check_interval=self.check_interval,
max_timeout=self.max_timeout,
num_workers=self.num_workers,
show_progress=self.show_progress,
verbose=self.verbose,
verify=self.verify,
httpx_timeout=self.httpx_timeout,
)
def list_agents(self) -> List[ExtractionAgent]:
"""List all available extraction agents."""
agents = self._run_in_thread(
self._async_client.llama_extract.list_extraction_agents(
project_id=self._project_id,
)
)
return [
ExtractionAgent(
client=self._async_client,
agent=agent,
project_id=self._project_id,
organization_id=self._organization_id,
check_interval=self.check_interval,
max_timeout=self.max_timeout,
num_workers=self.num_workers,
show_progress=self.show_progress,
verbose=self.verbose,
)
for agent in agents
]
def delete_agent(self, agent_id: str) -> None:
"""Delete an extraction agent by ID.
Args:
agent_id (str): ID of the extraction agent to delete
"""
self._run_in_thread(
self._async_client.llama_extract.delete_extraction_agent(
extraction_agent_id=agent_id
)
)
def __del__(self) -> None:
"""Cleanup resources properly."""
try:
if hasattr(self, "_thread_pool"):
self._thread_pool.shutdown(wait=True)
except Exception:
pass # Suppress exceptions during cleanup
if __name__ == "__main__":
from dotenv import load_dotenv
load_dotenv()
data_dir = Path(__file__).parent.parent / "tests" / "data"
extractor = LlamaExtract()
try:
agent = extractor.get_agent(name="test-agent")
except Exception:
agent = extractor.create_agent(
"test-agent",
{
"type": "object",
"properties": {
"title": {"type": "string"},
"summary": {"type": "string"},
},
},
)
results = agent.extract(data_dir / "slide" / "conocophilips.pdf")
extractor.delete_agent(agent.id)
print(results)
+34
View File
@@ -0,0 +1,34 @@
from typing import Any, Dict, List, Union, Generator
from contextlib import contextmanager
# Asyncio error messages
nest_asyncio_err = "cannot be called from a running event loop"
nest_asyncio_msg = (
"The event loop is already running. "
"Add `import nest_asyncio; nest_asyncio.apply()` to your code to fix this issue."
)
def is_jupyter() -> bool:
"""Check if we're running in a Jupyter environment."""
try:
from IPython import get_ipython
return get_ipython().__class__.__name__ == "ZMQInteractiveShell"
except (ImportError, AttributeError):
return False
@contextmanager
def augment_async_errors() -> Generator[None, None, None]:
"""Context manager to add helpful information for errors due to nested event loops."""
try:
yield
except RuntimeError as e:
if nest_asyncio_err in str(e):
raise RuntimeError(nest_asyncio_msg)
raise
JSONType = Union[Dict[str, Any], List[Any], str, int, float, bool, None]
JSONObjectType = Dict[str, JSONType]
+183 -54
View File
@@ -4,6 +4,7 @@ import os
import time
from contextlib import asynccontextmanager
from copy import deepcopy
from enum import Enum
from io import BufferedIOBase
from pathlib import Path, PurePath, PurePosixPath
from typing import Any, AsyncGenerator, Dict, List, Optional, Union
@@ -37,6 +38,29 @@ JOB_STATUS_ROUTE = "/api/parsing/job/{job_id}"
JOB_UPLOAD_ROUTE = "/api/parsing/upload"
def build_url(
base_url: str, organization_id: Optional[str], project_id: Optional[str]
) -> str:
query_params = {}
if organization_id:
query_params["organization_id"] = organization_id
if project_id:
query_params["project_id"] = project_id
if query_params:
return base_url + "?" + "&".join([f"{k}={v}" for k, v in query_params.items()])
return base_url
class BackoffPattern(str, Enum):
"""Backoff pattern for polling."""
CONSTANT = "constant"
LINEAR = "linear"
EXPONENTIAL = "exponential"
class LlamaParse(BasePydanticReader):
"""A smart-parser for files."""
@@ -50,11 +74,28 @@ class LlamaParse(BasePydanticReader):
default=DEFAULT_BASE_URL,
description="The base URL of the Llama Parsing API.",
)
organization_id: Optional[str] = Field(
default=None,
description="The organization ID for the LlamaParse API.",
)
project_id: Optional[str] = Field(
default=None,
description="The project ID for the LlamaParse API.",
)
check_interval: int = Field(
default=1,
description="The interval in seconds to check if the parsing is done.",
)
backoff_pattern: BackoffPattern = Field(
default=BackoffPattern.LINEAR,
description="Controls the backoff pattern when retrying failed requests: 'constant', 'linear', or 'exponential'.",
)
max_check_interval: int = Field(
default=5,
description="Maximum interval in seconds between polling attempts when checking job status.",
)
custom_client: Optional[httpx.AsyncClient] = Field(
default=None, description="A custom HTTPX client to use for sending requests."
)
@@ -88,6 +129,10 @@ class LlamaParse(BasePydanticReader):
)
# Parsing specific configurations (Alphabetical order)
adaptive_long_table: Optional[bool] = Field(
default=False,
description="If set to true, LlamaParse will try to detect long table and adapt the output.",
)
annotate_links: Optional[bool] = Field(
default=False,
description="Annotate links found in the document to extract their URL.",
@@ -140,14 +185,7 @@ class LlamaParse(BasePydanticReader):
default=None,
description="The top margin of the bounding box to use to extract text from documents expressed as a float between 0 and 1 representing the percentage of the page height.",
)
complemental_formatting_instruction: Optional[str] = Field(
default=None,
description="The complemental formatting instruction for the parser. Tell llamaParse how some thing should to be formatted, while retaining the markdown output.",
)
content_guideline_instruction: Optional[str] = Field(
default=None,
description="The content guideline for the parser. Tell LlamaParse how the content should be changed / transformed.",
)
continuous_mode: Optional[bool] = Field(
default=False,
description="Parse documents continuously, leading to better results on documents where tables span across two pages.",
@@ -180,10 +218,7 @@ class LlamaParse(BasePydanticReader):
default=False,
description="Note: Non compatible with gpt-4o. If set to true, the parser will use a faster mode to extract text from documents. This mode will skip OCR of images, and table/heading reconstruction.",
)
formatting_instruction: Optional[str] = Field(
default=None,
description="The Formatting instruction for the parser. Override default llamaParse behavior. In most case you want to use complemental_formatting_instruction instead.",
)
guess_xlsx_sheet_names: Optional[bool] = Field(
default=False,
description="Whether to guess the sheet names of the xlsx file.",
@@ -259,10 +294,18 @@ class LlamaParse(BasePydanticReader):
default=None,
description="A templated suffix to add to the beginning of each page. If it contain `{page_number}`, it will be replaced by the page number.",
)
parse_mode: Optional[str] = Field(
default=None,
description="The parsing mode to use, see ParsingMode enum for possible values ",
)
premium_mode: Optional[bool] = Field(
default=False,
description="Use our best parser mode if set to True.",
)
preserve_layout_alignment_across_pages: Optional[bool] = Field(
default=False,
description="Preserve grid alignment across page in text mode.",
)
skip_diagonal_text: Optional[bool] = Field(
default=False,
description="If set to true, the parser will ignore diagonal text (when the text rotation in degrees modulo 90 is not 0).",
@@ -304,6 +347,14 @@ class LlamaParse(BasePydanticReader):
default=None,
description="The named JSON Schema to use to structure the output of the parsing job. For convenience / testing, LlamaParse provides a few named JSON Schema that can be used directly. Use 'imFeelingLucky' to let llamaParse dream the schema.",
)
system_prompt: Optional[str] = Field(
default=None,
description="The system prompt. Replace llamaParse default system prompt, may impact accuracy",
)
system_prompt_append: Optional[str] = Field(
default=None,
description="String to append to default system prompt.",
)
take_screenshot: Optional[bool] = Field(
default=False,
description="Whether to take screenshot of each page of the document.",
@@ -312,9 +363,9 @@ class LlamaParse(BasePydanticReader):
default=None,
description="The target pages to extract text from documents. Describe as a comma separated list of page numbers. The first page of the document is page 0",
)
use_vendor_multimodal_model: Optional[bool] = Field(
default=False,
description="Whether to use the vendor multimodal API.",
user_prompt: Optional[str] = Field(
default=None,
description="The user prompt. Replace llamaParse default user prompt",
)
vendor_multimodal_api_key: Optional[str] = Field(
default=None,
@@ -334,6 +385,18 @@ class LlamaParse(BasePydanticReader):
default=None,
description="The bounding box to use to extract text from documents describe as a string containing the bounding box margins",
)
complemental_formatting_instruction: Optional[str] = Field(
default=None,
description="The complemental formatting instruction for the parser. Tell llamaParse how some thing should to be formatted, while retaining the markdown output.",
)
content_guideline_instruction: Optional[str] = Field(
default=None,
description="The content guideline for the parser. Tell LlamaParse how the content should be changed / transformed.",
)
formatting_instruction: Optional[str] = Field(
default=None,
description="The Formatting instruction for the parser. Override default llamaParse behavior. In most case you want to use complemental_formatting_instruction instead.",
)
gpt4o_mode: Optional[bool] = Field(
default=False,
description="Whether to use gpt-4o extract text from documents.",
@@ -350,6 +413,11 @@ class LlamaParse(BasePydanticReader):
default="", description="The parsing instruction for the parser."
)
use_vendor_multimodal_model: Optional[bool] = Field(
default=False,
description="Whether to use the vendor multimodal API.",
)
@field_validator("api_key", mode="before", check_fields=True)
@classmethod
def validate_api_key(cls, v: str) -> str:
@@ -478,6 +546,9 @@ class LlamaParse(BasePydanticReader):
data["from_python_package"] = True
if self.adaptive_long_table:
data["adaptive_long_table"] = self.adaptive_long_table
if self.annotate_links:
data["annotate_links"] = self.annotate_links
@@ -529,11 +600,17 @@ class LlamaParse(BasePydanticReader):
data["bbox_top"] = self.bbox_top
if self.complemental_formatting_instruction:
print(
"WARNING: complemental_formatting_instruction is deprecated and may be remove in a future release. Use system_prompt, system_prompt_append or user_prompt instead."
)
data[
"complemental_formatting_instruction"
] = self.complemental_formatting_instruction
if self.content_guideline_instruction:
print(
"WARNING: content_guideline_instruction is deprecated and may be remove in a future release. Use system_prompt, system_prompt_append or user_prompt instead."
)
data["content_guideline_instruction"] = self.content_guideline_instruction
if self.continuous_mode:
@@ -561,6 +638,9 @@ class LlamaParse(BasePydanticReader):
data["fast_mode"] = self.fast_mode
if self.formatting_instruction:
print(
"WARNING: formatting_instruction is deprecated and may be remove in a future release. Use system_prompt, system_prompt_append or user_prompt instead."
)
data["formatting_instruction"] = self.formatting_instruction
if self.guess_xlsx_sheet_names:
@@ -600,6 +680,9 @@ class LlamaParse(BasePydanticReader):
data["invalidate_cache"] = self.invalidate_cache
if self.is_formatting_instruction:
print(
"WARNING: formatting_instruction is deprecated and may be remove in a future release. Use system_prompt, system_prompt_append or user_prompt instead."
)
data["is_formatting_instruction"] = self.is_formatting_instruction
if self.job_timeout_extra_time_per_page_in_seconds is not None:
@@ -639,15 +722,23 @@ class LlamaParse(BasePydanticReader):
if self.page_suffix is not None:
data["page_suffix"] = self.page_suffix
if self.parsing_instruction is not None:
if self.parsing_instruction:
print(
"WARNING: parsing_instruction is deprecated. Use complemental_formatting_instruction or content_guideline_instruction instead."
"WARNING: parsing_instruction is deprecated. Use system_prompt, system_prompt_append or user_prompt instead."
)
data["parsing_instruction"] = self.parsing_instruction
if self.parse_mode:
data["parse_mode"] = self.parse_mode
if self.premium_mode:
data["premium_mode"] = self.premium_mode
if self.preserve_layout_alignment_across_pages:
data[
"preserve_layout_alignment_across_pages"
] = self.preserve_layout_alignment_across_pages
if self.skip_diagonal_text:
data["skip_diagonal_text"] = self.skip_diagonal_text
@@ -676,13 +767,17 @@ class LlamaParse(BasePydanticReader):
data[
"structured_output_json_schema_name"
] = self.structured_output_json_schema_name
if self.system_prompt is not None:
data["system_prompt"] = self.system_prompt
if self.system_prompt_append is not None:
data["system_prompt_append"] = self.system_prompt_append
if self.take_screenshot:
data["take_screenshot"] = self.take_screenshot
if self.target_pages is not None:
data["target_pages"] = self.target_pages
if self.user_prompt is not None:
data["user_prompt"] = self.user_prompt
if self.use_vendor_multimodal_model:
data["use_vendor_multimodal_model"] = self.use_vendor_multimodal_model
@@ -706,7 +801,8 @@ class LlamaParse(BasePydanticReader):
data["gpt4o_api_key"] = self.gpt4o_api_key
try:
resp = await self.aclient.post(JOB_UPLOAD_ROUTE, files=files, data=data) # type: ignore
url = build_url(JOB_UPLOAD_ROUTE, self.organization_id, self.project_id)
resp = await self.aclient.post(url, files=files, data=data) # type: ignore
resp.raise_for_status() # this raises if status is not 2xx
return resp.json()["id"]
except httpx.HTTPStatusError as err: # this catches it
@@ -716,54 +812,87 @@ class LlamaParse(BasePydanticReader):
if file_handle is not None:
file_handle.close()
def _calculate_backoff(self, current_interval: float) -> float:
"""Calculate the next backoff interval based on the backoff pattern.
Args:
current_interval: The current interval in seconds
Returns:
The next interval in seconds
"""
if self.backoff_pattern == BackoffPattern.CONSTANT:
return current_interval
elif self.backoff_pattern == BackoffPattern.LINEAR:
return min(current_interval + 1, float(self.max_check_interval))
elif self.backoff_pattern == BackoffPattern.EXPONENTIAL:
return min(current_interval * 2, float(self.max_check_interval))
return current_interval # Default fallback
async def _get_job_result(
self, job_id: str, result_type: str, verbose: bool = False
) -> Dict[str, Any]:
start = time.time()
tries = 0
error_count = 0
current_interval: float = float(self.check_interval)
# so we're not re-setting the headers & stuff on each
# usage... assume that there is not some other
# coro also modifying base_url and the other client related configs.
client = self.aclient
while True:
await asyncio.sleep(self.check_interval)
tries += 1
result = await client.get(JOB_STATUS_ROUTE.format(job_id=job_id))
if result.status_code != 200:
try:
await asyncio.sleep(current_interval)
tries += 1
result = await client.get(JOB_STATUS_ROUTE.format(job_id=job_id))
result.raise_for_status() # this raises if status is not 2xx
# Allowed values "PENDING", "SUCCESS", "ERROR", "CANCELED"
result_json = result.json()
status = result_json["status"]
if status == "SUCCESS":
parsed_result = await client.get(
JOB_RESULT_URL.format(job_id=job_id, result_type=result_type),
)
return parsed_result.json()
elif status == "PENDING":
end = time.time()
if end - start > self.max_timeout:
raise Exception(f"Timeout while parsing the file: {job_id}")
if verbose and tries % 10 == 0:
print(".", end="", flush=True)
current_interval = self._calculate_backoff(current_interval)
else:
error_code = result_json.get("error_code", "No error code found")
error_message = result_json.get(
"error_message", "No error message found"
)
exception_str = (
f"Job ID: {job_id} failed with status: {status}, "
f"Error code: {error_code}, Error message: {error_message}"
)
raise Exception(exception_str)
except (
httpx.ConnectError,
httpx.ReadError,
httpx.WriteError,
httpx.ConnectTimeout,
httpx.ReadTimeout,
httpx.WriteTimeout,
httpx.HTTPStatusError,
) as err:
error_count += 1
end = time.time()
if end - start > self.max_timeout:
raise Exception(f"Timeout while parsing the file: {job_id}")
raise Exception(
f"Timeout while parsing the file: {job_id}"
) from err
if verbose and tries % 10 == 0:
print(".", end="", flush=True)
await asyncio.sleep(self.check_interval)
continue
# Allowed values "PENDING", "SUCCESS", "ERROR", "CANCELED"
result_json = result.json()
status = result_json["status"]
if status == "SUCCESS":
parsed_result = await client.get(
JOB_RESULT_URL.format(job_id=job_id, result_type=result_type),
)
return parsed_result.json()
elif status == "PENDING":
end = time.time()
if end - start > self.max_timeout:
raise Exception(f"Timeout while parsing the file: {job_id}")
if verbose and tries % 10 == 0:
print(".", end="", flush=True)
await asyncio.sleep(self.check_interval)
else:
error_code = result_json.get("error_code", "No error code found")
error_message = result_json.get(
"error_message", "No error message found"
)
exception_str = f"Job ID: {job_id} failed with status: {status}, Error code: {error_code}, Error message: {error_message}"
raise Exception(exception_str)
print(
f"HTTP error: {err}...",
flush=True,
)
current_interval = self._calculate_backoff(current_interval)
async def _aload_data(
self,
+10
View File
@@ -14,6 +14,16 @@ class ResultType(str, Enum):
STRUCTURED = "structured"
class ParsingMode(str, Enum):
"""The parsing mode for the parser."""
parse_page_without_llm = "parse_page_without_llm"
parse_page_with_llm = "parse_page_with_llm"
parse_page_with_lvm = "parse_page_with_lvm"
parse_page_with_agent = "parse_page_with_agent"
parse_document_with_llm = "parse_document_with_llm"
class Language(str, Enum):
BAZA = "abq"
ADYGHE = "ady"
+3 -3
View File
@@ -146,9 +146,9 @@ Full documentation for `SimpleDirectoryReader` can be found on the [LlamaIndex D
Several end-to-end indexing examples can be found in the examples folder
- [Getting Started](examples/demo_basic.ipynb)
- [Advanced RAG Example](examples/demo_advanced.ipynb)
- [Raw API Usage](examples/demo_api.ipynb)
- [Getting Started](/examples/parse/demo_basic.ipynb)
- [Advanced RAG Example](/examples/parse/demo_advanced.ipynb)
- [Raw API Usage](/examples/parse/demo_api.ipynb)
## Documentation
+2 -2
View File
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
[tool.poetry]
name = "llama-parse"
version = "0.6.0"
version = "0.6.9"
description = "Parse files into RAG-Optimized formats."
authors = ["Logan Markewich <logan@llamaindex.ai>"]
license = "MIT"
@@ -13,7 +13,7 @@ packages = [{include = "llama_parse"}]
[tool.poetry.dependencies]
python = ">=3.9,<4.0"
llama-cloud-services = "*"
llama-cloud-services = ">=0.6.9"
[tool.poetry.group.dev.dependencies]
pytest = "^8.0.0"
Generated
+1147 -1079
View File
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -8,7 +8,7 @@ python_version = "3.10"
[tool.poetry]
name = "llama-cloud-services"
version = "0.6.0"
version = "0.6.9"
description = "Tailored SDK clients for LlamaCloud services."
authors = ["Logan Markewich <logan@runllama.ai>"]
license = "MIT"
@@ -18,11 +18,12 @@ packages = [{include = "llama_cloud_services"}]
[tool.poetry.dependencies]
python = ">=3.9,<4.0"
llama-index-core = ">=0.11.0"
llama-cloud = "^0.1.11"
llama-cloud = "^0.1.17"
pydantic = "!=2.10"
click = "^8.1.7"
python-dotenv = "^1.0.1"
eval-type-backport = {python = "<3.10", version = "^0.2.0"}
platformdirs = "^4.3.7"
[tool.poetry.group.dev.dependencies]
pytest = "^8.0.0"
View File
Binary file not shown.
@@ -0,0 +1,37 @@
{
"receiptNumber": "27215058",
"invoiceNumber": "87B37C90152",
"datePaid": "2024-07-19",
"paymentMethod": {
"type": "visa",
"lastFourDigits": "7267"
},
"merchant": {
"name": "Noisebridge",
"address": {
"street": "272 Capp St",
"city": "San Francisco",
"state": "California",
"postalCode": "94110",
"country": "United States"
},
"phone": "1 6507017829",
"email": "treasurer+stripe@noisebridge.net"
},
"billTo": "noisebridge@seldo.com",
"items": [
{
"description": "$10 / month",
"quantity": 1,
"unitPrice": 10.0,
"amount": 10.0,
"period": {
"start": "2024-07-19",
"end": "2024-08-19"
}
}
],
"subtotal": 10.0,
"total": 10.0,
"amountPaid": 10.0
}
+124
View File
@@ -0,0 +1,124 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["receiptNumber", "datePaid", "total", "items"],
"properties": {
"receiptNumber": {
"type": "string"
},
"invoiceNumber": {
"type": "string"
},
"datePaid": {
"type": "string",
"format": "date"
},
"paymentMethod": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["visa", "mastercard", "amex", "cash", "other"]
},
"lastFourDigits": {
"type": "string",
"pattern": "^[0-9]{4}$"
}
}
},
"merchant": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"address": {
"type": "object",
"properties": {
"street": {
"type": "string"
},
"city": {
"type": "string"
},
"state": {
"type": "string"
},
"postalCode": {
"type": "string"
},
"country": {
"type": "string"
}
}
},
"phone": {
"type": "string"
},
"email": {
"type": "string",
"format": "email"
}
}
},
"billTo": {
"type": "string",
"format": "email"
},
"items": {
"type": "array",
"items": {
"type": "object",
"required": [
"description",
"quantity",
"unitPrice",
"amount",
"period"
],
"properties": {
"description": {
"type": "string"
},
"quantity": {
"type": "integer",
"minimum": 1
},
"unitPrice": {
"type": "number",
"minimum": 0
},
"amount": {
"type": "number",
"minimum": 0
},
"period": {
"type": "object",
"properties": {
"start": {
"type": "string",
"format": "date"
},
"end": {
"type": "string",
"format": "date"
}
}
}
}
}
},
"subtotal": {
"type": "number",
"minimum": 0
},
"total": {
"type": "number",
"minimum": 0
},
"amountPaid": {
"type": "number",
"minimum": 0
}
}
}
+181
View File
@@ -0,0 +1,181 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Resume Schema",
"type": "object",
"required": ["basics", "skills", "experience"],
"properties": {
"basics": {
"type": "object",
"required": ["name", "email"],
"properties": {
"name": {
"type": "string"
},
"email": {
"type": "string",
"format": "email"
},
"phone": {
"type": "string"
},
"location": {
"type": "object",
"properties": {
"city": {
"type": "string"
},
"region": {
"type": "string"
},
"country": {
"type": "string"
}
}
},
"profiles": {
"type": "array",
"items": {
"type": "object",
"properties": {
"network": {
"type": "string"
},
"url": {
"type": "string",
"format": "uri"
}
}
}
},
"summary": {
"type": "string"
}
}
},
"skills": {
"type": "array",
"items": {
"type": "object",
"properties": {
"category": {
"type": "string"
},
"keywords": {
"type": "array",
"items": {
"type": "string"
}
},
"level": {
"type": "string",
"enum": ["beginner", "intermediate", "advanced", "expert"]
}
}
}
},
"experience": {
"type": "array",
"items": {
"type": "object",
"required": ["company", "position", "startDate"],
"properties": {
"company": {
"type": "string"
},
"position": {
"type": "string"
},
"startDate": {
"type": "string",
"format": "date"
},
"endDate": {
"type": "string",
"format": "date"
},
"highlights": {
"type": "array",
"items": {
"type": "string"
}
},
"technologies": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
},
"education": {
"type": "array",
"items": {
"type": "object",
"required": ["institution", "degree"],
"properties": {
"institution": {
"type": "string"
},
"degree": {
"type": "string"
},
"field": {
"type": "string"
},
"graduationDate": {
"type": "string",
"format": "date"
},
"gpa": {
"type": "number"
}
}
}
},
"certifications": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"issuer": {
"type": "string"
},
"date": {
"type": "string",
"format": "date"
},
"validUntil": {
"type": "string",
"format": "date"
}
}
}
},
"publications": {
"type": "array",
"items": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"publisher": {
"type": "string"
},
"date": {
"type": "string",
"format": "date"
},
"url": {
"type": "string",
"format": "uri"
}
}
}
}
}
}
@@ -0,0 +1,298 @@
<!doctype html>
<html>
<head>
<style>
body {
font-family: "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
margin: 0;
padding: 0;
background: #fff;
color: #333;
line-height: 1.6;
}
.container {
display: flex;
max-width: 1200px;
margin: 0 auto;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
min-height: 100vh;
}
.sidebar {
background: #2c3e50;
color: white;
padding: 2rem;
width: 300px;
}
.main-content {
padding: 2rem;
flex: 1;
}
.profile-name {
font-size: 2.5rem;
margin: 0;
color: #2c3e50;
border-bottom: 3px solid #3498db;
padding-bottom: 0.5rem;
}
.profile-title {
font-size: 1.5rem;
color: #7f8c8d;
margin: 0.5rem 0 2rem 0;
}
.contact-info {
margin-bottom: 2rem;
}
.section-title {
font-size: 1.2rem;
text-transform: uppercase;
color: #3498db;
margin-bottom: 1rem;
letter-spacing: 1px;
}
.sidebar .section-title {
color: white;
border-bottom: 2px solid #3498db;
padding-bottom: 0.5rem;
}
.skill-category {
margin-bottom: 1rem;
}
.skill-list {
list-style: none;
padding: 0;
margin: 0;
}
.skill-list li {
margin-bottom: 0.5rem;
font-size: 0.9rem;
}
.experience-item {
margin-bottom: 2rem;
}
.company-name {
font-weight: bold;
color: #2c3e50;
font-size: 1.1rem;
}
.job-title {
color: #3498db;
font-weight: bold;
}
.date {
color: #7f8c8d;
font-size: 0.9rem;
}
.achievements {
list-style: disc;
padding-left: 1.2rem;
margin-top: 0.5rem;
}
.contact-info a {
color: white;
text-decoration: none;
}
.education-item {
margin-bottom: 1rem;
}
</style>
</head>
<body>
<div class="container">
<div class="sidebar">
<div class="contact-info">
<h2 class="section-title">Contact</h2>
<p>sarah.chen@email.com</p>
<p>(555) 123-4567</p>
<p>San Francisco, CA</p>
<p><a href="#">LinkedIn Profile</a></p>
</div>
<div class="skills-section">
<h2 class="section-title">Technical Skills</h2>
<div class="skill-category">
<h3>Architecture & Design</h3>
<ul class="skill-list">
<li>Microservices</li>
<li>Event-Driven Architecture</li>
<li>Domain-Driven Design</li>
<li>REST APIs</li>
</ul>
</div>
<div class="skill-category">
<h3>Cloud Platforms</h3>
<ul class="skill-list">
<li>AWS (Advanced)</li>
<li>Azure</li>
<li>Google Cloud Platform</li>
</ul>
</div>
<div class="skill-category">
<h3>Programming</h3>
<ul class="skill-list">
<li>Java</li>
<li>Python</li>
<li>Go</li>
<li>JavaScript/TypeScript</li>
</ul>
</div>
<div class="skill-category">
<h3>Certifications</h3>
<ul class="skill-list">
<li>AWS Solutions Architect - Professional</li>
<li>Google Cloud Architect</li>
<li>Certified Kubernetes Administrator</li>
</ul>
</div>
</div>
</div>
<div class="main-content">
<h1 class="profile-name">Sarah Chen</h1>
<div class="profile-title">Senior Software Architect</div>
<div class="section">
<h2 class="section-title">Professional Summary</h2>
<p>
Innovative Software Architect with over 12 years of experience
designing and implementing large-scale distributed systems. Proven
track record of leading technical teams and delivering robust
enterprise solutions. Expert in cloud architecture, microservices,
and emerging technologies with a focus on scalable, maintainable
systems.
</p>
</div>
<div class="section">
<h2 class="section-title">Professional Experience</h2>
<div class="experience-item">
<div class="company-name">TechCorp Solutions</div>
<div class="job-title">Senior Software Architect</div>
<div class="date">2020 - Present</div>
<ul class="achievements">
<li>
Led architectural design and implementation of a cloud-native
platform serving 2M+ users
</li>
<li>
Established architectural guidelines and best practices adopted
across 12 development teams
</li>
<li>
Reduced system latency by 40% through implementation of
event-driven architecture
</li>
<li>
Mentored 15+ senior developers in cloud-native development
practices
</li>
</ul>
</div>
<div class="experience-item">
<div class="company-name">DataFlow Systems</div>
<div class="job-title">Lead Software Engineer</div>
<div class="date">2016 - 2020</div>
<ul class="achievements">
<li>
Architected and led development of distributed data processing
platform handling 5TB daily
</li>
<li>
Designed microservices architecture reducing deployment time by
65%
</li>
<li>
Led migration of legacy monolith to cloud-native architecture
</li>
<li>
Managed team of 8 engineers across 3 international locations
</li>
</ul>
</div>
<div class="experience-item">
<div class="company-name">InnovateTech</div>
<div class="job-title">Senior Software Engineer</div>
<div class="date">2013 - 2016</div>
<ul class="achievements">
<li>
Developed high-performance trading platform processing 100K
transactions per second
</li>
<li>
Implemented real-time analytics engine reducing processing
latency by 75%
</li>
<li>
Led adoption of container orchestration reducing deployment
costs by 35%
</li>
</ul>
</div>
</div>
<div class="section">
<h2 class="section-title">Education</h2>
<div class="education-item">
<div class="company-name">Stanford University</div>
<div class="job-title">Master of Science in Computer Science</div>
<div class="date">2013</div>
<p>Focus: Distributed Systems and Machine Learning</p>
</div>
<div class="education-item">
<div class="company-name">University of California, Berkeley</div>
<div class="job-title">
Bachelor of Science in Computer Engineering
</div>
<div class="date">2011</div>
<p>Magna Cum Laude</p>
</div>
</div>
<div class="section">
<h2 class="section-title">Patents & Speaking</h2>
<ul class="achievements">
<li>
Co-inventor on three patents for distributed systems architecture
</li>
<li>
Published paper on "Scalable Microservices Architecture" at IEEE
Cloud Computing Conference 2022
</li>
<li>
Keynote Speaker, CloudCon 2023: "Future of Cloud-Native
Architecture"
</li>
<li>Regular presenter at local tech meetups and conferences</li>
</ul>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,94 @@
{
"basics": {
"name": "Sarah Chen",
"email": "san.francisco@email.com",
"phone": "(555) 123-4567",
"location": {
"city": "San Francisco",
"region": "CA",
"country": "USA"
}
},
"skills": [
{
"category": "Architecture & Design",
"keywords": [
"Microservices",
"Event-Driven Architecture",
"Domain-Driven Design",
"REST APIs"
]
},
{
"category": "Cloud Platforms",
"keywords": ["AWS", "Azure", "Google Cloud Platform"]
},
{
"category": "Programming Languages",
"keywords": ["Java", "Python", "Go", "JavaScript", "TypeScript"]
}
],
"experience": [
{
"company": "TechCorp Solutions",
"position": "Senior Software Architect",
"startDate": "2020-01-01",
"endDate": "2024-01-10"
},
{
"company": "DataFlow Systems",
"position": "Lead Software Engineer",
"startDate": "2016-01-01",
"endDate": "2019-12-31",
"technologies": [
"Distributed Systems",
"Microservices",
"Cloud Migration"
]
},
{
"company": "InnovateTech",
"position": "Senior Software Engineer",
"startDate": "2013-01-01",
"endDate": "2015-12-31",
"technologies": [
"High-performance Computing",
"Real-time Analytics",
"Container Orchestration"
]
}
],
"education": [
{
"institution": "Stanford University",
"degree": "Master of Science",
"field": "Computer Science",
"graduationDate": "2013-01-01",
"specialization": "Distributed Systems and Machine Learning"
},
{
"institution": "University of California, Berkeley",
"degree": "Bachelor of Science",
"field": "Computer Engineering",
"graduationDate": "2011-01-01"
}
],
"certifications": [
{
"name": "AWS Solutions Architect - Professional"
},
{
"name": "Google Cloud Architect"
},
{
"name": "Certified Kubernetes Administrator"
}
],
"publications": [
{
"title": "Scalable Microservices Architecture",
"publisher": "IEEE Cloud Computing Conference",
"date": "2022-01-01"
}
]
}
Binary file not shown.
@@ -0,0 +1,48 @@
{
"companyInfo": {
"name": "CloudFlow Analytics",
"fundingStage": "Series A",
"foundedYear": null,
"industry": null,
"location": null
},
"financialMetrics": {
"mrr": {
"value": 580000,
"currency": "USD",
"growthRate": 27
},
"grossMargin": 88
},
"growthMetrics": {
"customers": {
"total": 1247,
"growth": 142,
"enterprisePercent": null
},
"nrr": 147
},
"marketMetrics": {
"tam": 50000000000,
"sam": null,
"marketShare": null,
"competitors": null
},
"differentiators": [
{
"claim": "Processing Speed",
"metric": "5x faster",
"comparisonTarget": "competitors"
},
{
"claim": "ML Accuracy",
"metric": "99.9%",
"comparisonTarget": null
},
{
"claim": "Market Potential",
"metric": "80%",
"comparisonTarget": "Fortune 500"
}
]
}
+122
View File
@@ -0,0 +1,122 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["companyInfo", "financialMetrics", "growthMetrics"],
"properties": {
"companyInfo": {
"type": "object",
"required": ["name", "fundingStage"],
"properties": {
"name": {
"type": "string"
},
"fundingStage": {
"type": "string",
"enum": ["Pre-seed", "Seed", "Series A", "Series B", "Series C+"]
},
"foundedYear": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
]
},
"industry": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"location": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
}
},
"financialMetrics": {
"type": "object",
"required": ["mrr", "growthRate"],
"properties": {
"mrr": {
"type": "object",
"description": "Monthly Recurring Revenue",
"required": ["value", "currency", "growthRate"],
"properties": {
"value": {
"type": "number"
},
"currency": {
"type": "string"
},
"growthRate": {
"type": "number"
}
}
},
"grossMargin": {
"type": "number"
}
}
},
"growthMetrics": {
"type": "object",
"required": ["customers", "nrr"],
"properties": {
"customers": {
"type": "object",
"required": ["total", "growth"],
"properties": {
"total": {
"type": "integer"
},
"growth": {
"type": "number"
}
}
},
"nrr": {
"description": "Net Revenue Retention",
"type": "number"
}
}
},
"differentiators": {
"type": "array",
"items": {
"type": "object",
"required": ["claim", "metric"],
"properties": {
"claim": {
"type": "string"
},
"metric": {
"type": "string"
},
"comparisonTarget": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
}
}
}
}
}
+147
View File
@@ -0,0 +1,147 @@
import os
import pytest
from llama_cloud_services.extract import LlamaExtract, ExtractionAgent
from time import perf_counter
from collections import namedtuple
import json
import uuid
from llama_cloud.types import (
ExtractConfig,
ExtractMode,
LlamaParseParameters,
LlamaExtractSettings,
)
from tests.extract.util import load_test_dotenv
load_test_dotenv()
TEST_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data")
# Get configuration from environment
LLAMA_CLOUD_API_KEY = os.getenv("LLAMA_CLOUD_API_KEY")
LLAMA_CLOUD_BASE_URL = os.getenv("LLAMA_CLOUD_BASE_URL")
LLAMA_CLOUD_PROJECT_ID = os.getenv("LLAMA_CLOUD_PROJECT_ID")
TestCase = namedtuple(
"TestCase", ["name", "schema_path", "config", "input_file", "expected_output"]
)
def get_test_cases():
"""Get all test cases from TEST_DIR.
Returns:
List[TestCase]: List of test cases
"""
test_cases = []
for data_type in os.listdir(TEST_DIR):
data_type_dir = os.path.join(TEST_DIR, data_type)
if not os.path.isdir(data_type_dir):
continue
schema_path = os.path.join(data_type_dir, "schema.json")
if not os.path.exists(schema_path):
continue
input_files = []
for file in os.listdir(data_type_dir):
file_path = os.path.join(data_type_dir, file)
if (
not os.path.isfile(file_path)
or file == "schema.json"
or file.endswith(".test.json")
):
continue
input_files.append(file_path)
settings = [
ExtractConfig(extraction_mode=ExtractMode.FAST),
ExtractConfig(extraction_mode=ExtractMode.BALANCED),
]
for input_file in sorted(input_files):
base_name = os.path.splitext(os.path.basename(input_file))[0]
expected_output = os.path.join(data_type_dir, f"{base_name}.test.json")
if not os.path.exists(expected_output):
continue
test_name = f"{data_type}/{os.path.basename(input_file)}"
for setting in settings:
test_cases.append(
TestCase(
name=test_name,
schema_path=schema_path,
input_file=input_file,
config=setting,
expected_output=expected_output,
)
)
return test_cases
@pytest.fixture(scope="session")
def extractor():
"""Create a single LlamaExtract instance for all tests."""
extract = LlamaExtract(
api_key=LLAMA_CLOUD_API_KEY,
base_url=LLAMA_CLOUD_BASE_URL,
project_id=LLAMA_CLOUD_PROJECT_ID,
verbose=True,
)
yield extract
# Cleanup thread pool at end of session
extract._thread_pool.shutdown()
@pytest.fixture
def extraction_agent(test_case: TestCase, extractor: LlamaExtract):
"""Fixture to create and cleanup extraction agent for each test."""
# Create unique name with random UUID (important for CI to avoid conflicts)
unique_id = uuid.uuid4().hex[:8]
agent_name = f"{test_case.name}_{unique_id}"
with open(test_case.schema_path, "r") as f:
schema = json.load(f)
# Clean up any existing agents with this name
try:
agents = extractor.list_agents()
for agent in agents:
if agent.name == agent_name:
extractor.delete_agent(agent.id)
except Exception as e:
print(f"Warning: Failed to cleanup existing agent: {str(e)}")
# Create new agent
agent = extractor.create_agent(agent_name, schema, config=test_case.config)
yield agent
@pytest.mark.skipif(
"CI" in os.environ,
reason="CI environment is not suitable for benchmarking",
)
@pytest.mark.parametrize("test_case", get_test_cases(), ids=lambda x: x.name)
@pytest.mark.asyncio(loop_scope="session")
async def test_extraction(
test_case: TestCase, extraction_agent: ExtractionAgent
) -> None:
start = perf_counter()
result = await extraction_agent._run_extraction_test(
test_case.input_file,
extract_settings=LlamaExtractSettings(
llama_parse_params=LlamaParseParameters(
invalidate_cache=True,
do_not_cache=True,
)
),
)
end = perf_counter()
print(f"Time taken: {end - start} seconds")
print(result)
+229
View File
@@ -0,0 +1,229 @@
import os
import pytest
from pathlib import Path
from pydantic import BaseModel
from llama_cloud_services.extract import LlamaExtract, ExtractionAgent, SourceText
from tests.extract.util import load_test_dotenv
load_test_dotenv()
# Get configuration from environment
LLAMA_CLOUD_API_KEY = os.getenv("LLAMA_CLOUD_API_KEY")
LLAMA_CLOUD_BASE_URL = os.getenv("LLAMA_CLOUD_BASE_URL")
LLAMA_CLOUD_PROJECT_ID = os.getenv("LLAMA_CLOUD_PROJECT_ID")
# Skip all tests if API key is not set
pytestmark = pytest.mark.skipif(
not LLAMA_CLOUD_API_KEY, reason="LLAMA_CLOUD_API_KEY not set"
)
# Test data
class TestSchema(BaseModel):
title: str
summary: str
# Test data paths
TEST_DIR = Path(__file__).parent / "data"
TEST_PDF = TEST_DIR / "slide" / "saas_slide.pdf"
@pytest.fixture
def llama_extract():
return LlamaExtract(
api_key=LLAMA_CLOUD_API_KEY,
base_url=LLAMA_CLOUD_BASE_URL,
project_id=LLAMA_CLOUD_PROJECT_ID,
verbose=True,
)
@pytest.fixture
def test_agent_name():
return "test-api-agent"
@pytest.fixture
def test_schema_dict():
return {
"type": "object",
"properties": {
"title": {"type": "string"},
"summary": {"type": "string"},
},
}
@pytest.fixture
def test_agent(llama_extract, test_agent_name, test_schema_dict, request):
"""Creates a test agent and cleans it up after the test"""
test_id = request.node.nodeid
test_hash = hex(hash(test_id))[-8:]
base_name = test_agent_name
base_name = next(
(marker.args[0] for marker in request.node.iter_markers("agent_name")),
base_name,
)
name = f"{base_name}_{test_hash}"
schema = next(
(
marker.args[0][0] if isinstance(marker.args[0], tuple) else marker.args[0]
for marker in request.node.iter_markers("agent_schema")
),
test_schema_dict,
)
# Cleanup existing agent
try:
for agent in llama_extract.list_agents():
if agent.name == name:
llama_extract.delete_agent(agent.id)
except Exception as e:
print(f"Warning: Failed to cleanup existing agent: {e}")
agent = llama_extract.create_agent(name=name, data_schema=schema)
yield agent
# Cleanup after test
try:
llama_extract.delete_agent(agent.id)
except Exception as e:
print(f"Warning: Failed to delete agent {agent.id}: {e}")
class TestLlamaExtract:
def test_init_without_api_key(self):
env_backup = os.getenv("LLAMA_CLOUD_API_KEY")
del os.environ["LLAMA_CLOUD_API_KEY"]
with pytest.raises(ValueError, match="The API key is required"):
LlamaExtract(api_key=None, base_url=LLAMA_CLOUD_BASE_URL)
os.environ["LLAMA_CLOUD_API_KEY"] = env_backup
@pytest.mark.agent_name("test-dict-schema-agent")
def test_create_agent_with_dict_schema(self, test_agent):
assert isinstance(test_agent, ExtractionAgent)
@pytest.mark.agent_name("test-pydantic-schema-agent")
@pytest.mark.agent_schema((TestSchema,))
def test_create_agent_with_pydantic_schema(self, test_agent):
assert isinstance(test_agent, ExtractionAgent)
def test_get_agent_by_name(self, llama_extract, test_agent):
agent = llama_extract.get_agent(name=test_agent.name)
assert isinstance(agent, ExtractionAgent)
assert agent.name == test_agent.name
assert agent.id == test_agent.id
assert agent.data_schema == test_agent.data_schema
def test_get_agent_by_id(self, llama_extract, test_agent):
agent = llama_extract.get_agent(id=test_agent.id)
assert isinstance(agent, ExtractionAgent)
assert agent.id == test_agent.id
assert agent.name == test_agent.name
assert agent.data_schema == test_agent.data_schema
def test_list_agents(self, llama_extract, test_agent):
agents = llama_extract.list_agents()
assert isinstance(agents, list)
assert any(a.id == test_agent.id for a in agents)
class TestExtractionAgent:
@pytest.mark.asyncio
async def test_extract_single_file(self, test_agent):
result = await test_agent.aextract(TEST_PDF)
assert result.status == "SUCCESS"
assert result.data is not None
assert isinstance(result.data, dict)
assert "title" in result.data
assert "summary" in result.data
def test_sync_extract_single_file(self, test_agent):
result = test_agent.extract(TEST_PDF)
assert result.status == "SUCCESS"
assert result.data is not None
assert isinstance(result.data, dict)
assert "title" in result.data
assert "summary" in result.data
def test_extract_file_from_buffered_io(self, test_agent):
result = test_agent.extract(SourceText(file=open(TEST_PDF, "rb")))
assert result.status == "SUCCESS"
assert result.data is not None
assert isinstance(result.data, dict)
assert "title" in result.data
assert "summary" in result.data
def test_extract_file_from_bytes(self, test_agent):
with open(TEST_PDF, "rb") as f:
file_bytes = f.read()
result = test_agent.extract(SourceText(file=file_bytes, filename=TEST_PDF.name))
assert result.status == "SUCCESS"
assert result.data is not None
assert isinstance(result.data, dict)
assert "title" in result.data
assert "summary" in result.data
def test_extract_from_text_content(self, test_agent):
TEST_TEXT = """
# Llamas
Llamas are social animals and live with others as a herd. Their wool is soft and
contains only a small amount of lanolin.[2] Llamas can learn simple tasks after a
few repetitions. When using a pack, they can carry about 25 to 30% of their body
weight for 8 to 13 km (58 miles).[3] The name llama (also historically spelled
"glama") was adopted by European settlers from native Peruvians.
"""
result = test_agent.extract(SourceText(text_content=TEST_TEXT))
assert result.status == "SUCCESS"
assert result.data is not None
assert isinstance(result.data, dict)
assert "title" in result.data
assert "summary" in result.data
@pytest.mark.asyncio
async def test_extract_multiple_files(self, test_agent):
files = [TEST_PDF, TEST_PDF] # Using same file twice for testing
response = await test_agent.aextract(files)
assert len(response) == 2
for result in response:
assert result.status == "SUCCESS"
assert result.data is not None
assert isinstance(result.data, dict)
assert "title" in result.data
assert "summary" in result.data
def test_save_agent_updates(
self, test_agent: ExtractionAgent, llama_extract: LlamaExtract
):
new_schema = {
"type": "object",
"properties": {
"new_field": {"type": "string"},
"title": {"type": "string"},
"summary": {"type": "string"},
},
}
test_agent.data_schema = new_schema
test_agent.save()
# Verify the update by getting a fresh instance
updated_agent = llama_extract.get_agent(name=test_agent.name)
assert "new_field" in updated_agent.data_schema["properties"]
def test_list_extraction_runs(self, test_agent: ExtractionAgent):
assert test_agent.list_extraction_runs().total == 0
test_agent.extract(TEST_PDF)
runs = test_agent.list_extraction_runs()
assert runs.total > 0
def test_delete_extraction_run(self, test_agent: ExtractionAgent):
assert test_agent.list_extraction_runs().total == 0
run = test_agent.extract(TEST_PDF)
test_agent.delete_extraction_run(run.id)
runs = test_agent.list_extraction_runs()
assert runs.total == 0
+139
View File
@@ -0,0 +1,139 @@
import os
import pytest
from llama_cloud_services.extract import LlamaExtract, ExtractionAgent
from collections import namedtuple
import json
import uuid
from llama_cloud.types import ExtractConfig, ExtractMode
from deepdiff import DeepDiff
from tests.extract.util import json_subset_match_score, load_test_dotenv
load_test_dotenv()
TEST_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data")
# Get configuration from environment
LLAMA_CLOUD_API_KEY = os.getenv("LLAMA_CLOUD_API_KEY")
LLAMA_CLOUD_BASE_URL = os.getenv("LLAMA_CLOUD_BASE_URL")
LLAMA_CLOUD_PROJECT_ID = os.getenv("LLAMA_CLOUD_PROJECT_ID")
TestCase = namedtuple(
"TestCase", ["name", "schema_path", "config", "input_file", "expected_output"]
)
def get_test_cases():
"""Get all test cases from TEST_DIR.
Returns:
List[TestCase]: List of test cases
"""
test_cases = []
for data_type in os.listdir(TEST_DIR):
data_type_dir = os.path.join(TEST_DIR, data_type)
if not os.path.isdir(data_type_dir):
continue
schema_path = os.path.join(data_type_dir, "schema.json")
if not os.path.exists(schema_path):
continue
input_files = []
for file in os.listdir(data_type_dir):
file_path = os.path.join(data_type_dir, file)
if (
not os.path.isfile(file_path)
or file == "schema.json"
or file.endswith(".test.json")
):
continue
input_files.append(file_path)
settings = [
ExtractConfig(extraction_mode=ExtractMode.FAST),
ExtractConfig(extraction_mode=ExtractMode.BALANCED),
]
for input_file in sorted(input_files):
base_name = os.path.splitext(os.path.basename(input_file))[0]
expected_output = os.path.join(data_type_dir, f"{base_name}.test.json")
if not os.path.exists(expected_output):
continue
test_name = f"{data_type}/{os.path.basename(input_file)}"
for setting in settings:
test_cases.append(
TestCase(
name=test_name,
schema_path=schema_path,
input_file=input_file,
config=setting,
expected_output=expected_output,
)
)
return test_cases
@pytest.fixture(scope="session")
def extractor():
"""Create a single LlamaExtract instance for all tests."""
extract = LlamaExtract(
api_key=LLAMA_CLOUD_API_KEY,
base_url=LLAMA_CLOUD_BASE_URL,
project_id=LLAMA_CLOUD_PROJECT_ID,
verbose=True,
)
yield extract
# Cleanup thread pool at end of session
extract._thread_pool.shutdown()
@pytest.fixture
def extraction_agent(test_case: TestCase, extractor: LlamaExtract):
"""Fixture to create and cleanup extraction agent for each test."""
# Create unique name with random UUID (important for CI to avoid conflicts)
unique_id = uuid.uuid4().hex[:8]
agent_name = f"{test_case.name}_{unique_id}"
with open(test_case.schema_path, "r") as f:
schema = json.load(f)
# Clean up any existing agents with this name
try:
agents = extractor.list_agents()
for agent in agents:
if agent.name == agent_name:
extractor.delete_agent(agent.id)
except Exception as e:
print(f"Warning: Failed to cleanup existing agent: {str(e)}")
# Create new agent
agent = extractor.create_agent(agent_name, schema, config=test_case.config)
yield agent
# Cleanup after test
try:
extractor.delete_agent(agent.id)
except Exception as e:
print(f"Warning: Failed to delete agent {agent.id}: {str(e)}")
@pytest.mark.skipif(
os.environ.get("LLAMA_CLOUD_API_KEY", "") == "",
reason="LLAMA_CLOUD_API_KEY not set",
)
@pytest.mark.parametrize("test_case", get_test_cases(), ids=lambda x: x.name)
def test_extraction(test_case: TestCase, extraction_agent: ExtractionAgent) -> None:
result = extraction_agent.extract(test_case.input_file).data
with open(test_case.expected_output, "r") as f:
expected = json.load(f)
# TODO: fix the saas_slide test
assert json_subset_match_score(expected, result) > 0.3, DeepDiff(
expected, result, ignore_order=True
)
+43
View File
@@ -0,0 +1,43 @@
from typing import Any
from autoevals.string import Levenshtein
from autoevals.number import NumericDiff
from dotenv import load_dotenv
from pathlib import Path
def load_test_dotenv():
load_dotenv(Path(__file__).parent.parent.parent / ".env.dev", override=True)
def json_subset_match_score(expected: Any, actual: Any) -> float:
"""
Adapted from autoevals.JsonDiff to only test on the subset of keys within the expected json.
"""
string_scorer = Levenshtein()
number_scorer = NumericDiff()
if isinstance(expected, dict) and isinstance(actual, dict):
if len(expected) == 0 and len(actual) == 0:
return 1
keys = set(expected.keys())
scores = [json_subset_match_score(expected.get(k), actual.get(k)) for k in keys]
scores = [s for s in scores if s is not None]
return sum(scores) / len(scores)
elif isinstance(expected, list) and isinstance(actual, list):
if len(expected) == 0 and len(actual) == 0:
return 1
scores = [json_subset_match_score(e1, e2) for (e1, e2) in zip(expected, actual)]
scores = [s for s in scores if s is not None]
return sum(scores) / max(len(expected), len(actual))
elif isinstance(expected, str) and isinstance(actual, str):
return string_scorer.eval(expected, actual).score
elif (isinstance(expected, int) or isinstance(expected, float)) and (
isinstance(actual, int) or isinstance(actual, float)
):
return number_scorer.eval(expected, actual).score
elif expected is None and actual is None:
return 1
elif expected is None or actual is None:
return 0
else:
return 0
+5
View File
@@ -81,6 +81,11 @@ async def test_create_and_delete_report(
@pytest.mark.asyncio
@pytest.mark.xfail(
condition=lambda: os.getenv("CI"),
reason="Report plan sometimes times out",
raises=TimeoutError,
)
async def test_report_plan_workflow(report: ReportClient) -> None:
"""Test the report planning workflow."""
# Wait for the plan