mirror of
https://github.com/run-llama/llama_cloud_services.git
synced 2026-07-20 19:47:38 -04:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 083d8109c2 | |||
| 89cfc8b25f | |||
| c46e157f92 | |||
| 05d6026d37 | |||
| 8e98d5c146 | |||
| 3f311c0669 | |||
| b1a2f9d42b | |||
| 142f55c94c | |||
| 230a110e52 | |||
| 83e2b031cd | |||
| 4844e26e5c |
@@ -0,0 +1,19 @@
|
||||
# Installation
|
||||
|
||||
This project uses poetry. Create a virtual environment, and run `poetry install`
|
||||
|
||||
# Versioning (Maintainers only)
|
||||
|
||||
Before merging your changes, make sure to bump the versions.
|
||||
|
||||
Make a version bump to `pyproject.toml`. If the underlying dependency on the llamacloud platform OpenAPI
|
||||
sdk needs bumping, make sure to bring that in as well. If updating dependencies, run `poetry lock`.
|
||||
|
||||
The legacy `llama_parse` package re-exports some of `llama_cloud_services` in the old namespace. The
|
||||
versions need to be kept consistent to sidecar it with `llama_cloud_services`. Bump it's version in `llama_parse/pyproject.toml`, and also bump it's dependency version of `llama-cloud-services` to match.
|
||||
|
||||
You can also do this with `./scripts/version-bump.py set 0.x.x` if you have `uv` installed.
|
||||
|
||||
Once the change is merged, push a tag `git tag -a v0.x.x -m 0.x.x` and `git push origin 0.x.x`.
|
||||
|
||||
This tagging step can be done with `./scripts/version-bump tag`.
|
||||
@@ -0,0 +1,516 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Table Extraction with LlamaParse\n",
|
||||
"\n",
|
||||
"This notebook will show you how to extract tables and save them as CSV files thanks to LlamaParse advanced parsing capabilities."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**1. Install needed dependencies**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! pip install llama-cloud-services pandas"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**2. Set you LLAMA_CLOUD_API_KEY as env variable**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"LLAMA_CLOUD_API_KEY: ··········\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"from getpass import getpass\n",
|
||||
"\n",
|
||||
"os.environ[\"LLAMA_CLOUD_API_KEY\"] = getpass(\"LLAMA_CLOUD_API_KEY: \")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**3. Initialiaze the parser**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from llama_cloud_services import LlamaParse\n",
|
||||
"\n",
|
||||
"parser = LlamaParse(result_type=\"markdown\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**4. Get data**\n",
|
||||
"\n",
|
||||
"This is a PDF with _lots_ of tables!"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"--2025-07-16 16:20:41-- https://assets.accessible-digital-documents.com/uploads/2017/01/sample-tables.pdf\n",
|
||||
"Resolving assets.accessible-digital-documents.com (assets.accessible-digital-documents.com)... 3.166.135.2, 3.166.135.62, 3.166.135.51, ...\n",
|
||||
"Connecting to assets.accessible-digital-documents.com (assets.accessible-digital-documents.com)|3.166.135.2|:443... connected.\n",
|
||||
"HTTP request sent, awaiting response... 200 OK\n",
|
||||
"Length: 145494 (142K) [application/pdf]\n",
|
||||
"Saving to: ‘sample-tables.pdf’\n",
|
||||
"\n",
|
||||
"sample-tables.pdf 100%[===================>] 142.08K --.-KB/s in 0.04s \n",
|
||||
"\n",
|
||||
"2025-07-16 16:20:41 (3.72 MB/s) - ‘sample-tables.pdf’ saved [145494/145494]\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"! wget https://assets.accessible-digital-documents.com/uploads/2017/01/sample-tables.pdf"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**5. Parse document**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Started parsing the file under job_id b53949f7-9017-4b6a-b30c-be6227271ed2\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"json_result = parser.get_json_result(\"sample-tables.pdf\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**6. Get tables!**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"tables = parser.get_tables(json_result, \"tables/\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**7. Load tables**\n",
|
||||
"\n",
|
||||
"Let's show one example table!"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.google.colaboratory.intrinsic+json": {
|
||||
"summary": "{\n \"name\": \"display(df\",\n \"rows\": 8,\n \"fields\": [\n {\n \"column\": \"Rainfall\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 5,\n \"samples\": [\n \"Average\",\n \"\",\n \"24 hour high\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Americas\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 908,\n \"min\": 9,\n \"max\": 2010,\n \"num_unique_values\": 8,\n \"samples\": [\n 104,\n 133,\n 2010\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Asia\",\n \"properties\": {\n \"dtype\": \"object\",\n \"num_unique_values\": 7,\n \"samples\": [\n \"\",\n 201.0,\n 28.0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Europe\",\n \"properties\": {\n \"dtype\": \"object\",\n \"num_unique_values\": 7,\n \"samples\": [\n \"\",\n 193.0,\n 29.0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Africa\",\n \"properties\": {\n \"dtype\": \"object\",\n \"num_unique_values\": 7,\n \"samples\": [\n \"\",\n 144.0,\n 20.0\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}",
|
||||
"type": "dataframe"
|
||||
},
|
||||
"text/html": [
|
||||
"\n",
|
||||
" <div id=\"df-94a74c8f-1062-4a80-8d3f-32f0fbadf7bb\" class=\"colab-df-container\">\n",
|
||||
" <div>\n",
|
||||
"<style scoped>\n",
|
||||
" .dataframe tbody tr th:only-of-type {\n",
|
||||
" vertical-align: middle;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" .dataframe tbody tr th {\n",
|
||||
" vertical-align: top;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" .dataframe thead th {\n",
|
||||
" text-align: right;\n",
|
||||
" }\n",
|
||||
"</style>\n",
|
||||
"<table border=\"1\" class=\"dataframe\">\n",
|
||||
" <thead>\n",
|
||||
" <tr style=\"text-align: right;\">\n",
|
||||
" <th></th>\n",
|
||||
" <th>Rainfall</th>\n",
|
||||
" <th>Americas</th>\n",
|
||||
" <th>Asia</th>\n",
|
||||
" <th>Europe</th>\n",
|
||||
" <th>Africa</th>\n",
|
||||
" </tr>\n",
|
||||
" </thead>\n",
|
||||
" <tbody>\n",
|
||||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>(inches)</td>\n",
|
||||
" <td>2010</td>\n",
|
||||
" <td></td>\n",
|
||||
" <td></td>\n",
|
||||
" <td></td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>1</th>\n",
|
||||
" <td>Average</td>\n",
|
||||
" <td>104</td>\n",
|
||||
" <td>201.0</td>\n",
|
||||
" <td>193.0</td>\n",
|
||||
" <td>144.0</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>2</th>\n",
|
||||
" <td>24 hour high</td>\n",
|
||||
" <td>15</td>\n",
|
||||
" <td>26.0</td>\n",
|
||||
" <td>27.0</td>\n",
|
||||
" <td>18.0</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>3</th>\n",
|
||||
" <td>12 hour high</td>\n",
|
||||
" <td>9</td>\n",
|
||||
" <td>10.0</td>\n",
|
||||
" <td>11.0</td>\n",
|
||||
" <td>12.0</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>4</th>\n",
|
||||
" <td></td>\n",
|
||||
" <td>2009</td>\n",
|
||||
" <td></td>\n",
|
||||
" <td></td>\n",
|
||||
" <td></td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>5</th>\n",
|
||||
" <td>Average</td>\n",
|
||||
" <td>133</td>\n",
|
||||
" <td>244.0</td>\n",
|
||||
" <td>155.0</td>\n",
|
||||
" <td>166.0</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>6</th>\n",
|
||||
" <td>24 hour high</td>\n",
|
||||
" <td>27</td>\n",
|
||||
" <td>28.0</td>\n",
|
||||
" <td>29.0</td>\n",
|
||||
" <td>20.0</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>7</th>\n",
|
||||
" <td>12 hour high</td>\n",
|
||||
" <td>11</td>\n",
|
||||
" <td>12.0</td>\n",
|
||||
" <td>13.0</td>\n",
|
||||
" <td>16.0</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"</div>\n",
|
||||
" <div class=\"colab-df-buttons\">\n",
|
||||
"\n",
|
||||
" <div class=\"colab-df-container\">\n",
|
||||
" <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-94a74c8f-1062-4a80-8d3f-32f0fbadf7bb')\"\n",
|
||||
" title=\"Convert this dataframe to an interactive table.\"\n",
|
||||
" style=\"display:none;\">\n",
|
||||
"\n",
|
||||
" <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\">\n",
|
||||
" <path d=\"M120-120v-720h720v720H120Zm60-500h600v-160H180v160Zm220 220h160v-160H400v160Zm0 220h160v-160H400v160ZM180-400h160v-160H180v160Zm440 0h160v-160H620v160ZM180-180h160v-160H180v160Zm440 0h160v-160H620v160Z\"/>\n",
|
||||
" </svg>\n",
|
||||
" </button>\n",
|
||||
"\n",
|
||||
" <style>\n",
|
||||
" .colab-df-container {\n",
|
||||
" display:flex;\n",
|
||||
" gap: 12px;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" .colab-df-convert {\n",
|
||||
" background-color: #E8F0FE;\n",
|
||||
" border: none;\n",
|
||||
" border-radius: 50%;\n",
|
||||
" cursor: pointer;\n",
|
||||
" display: none;\n",
|
||||
" fill: #1967D2;\n",
|
||||
" height: 32px;\n",
|
||||
" padding: 0 0 0 0;\n",
|
||||
" width: 32px;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" .colab-df-convert:hover {\n",
|
||||
" background-color: #E2EBFA;\n",
|
||||
" box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
|
||||
" fill: #174EA6;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" .colab-df-buttons div {\n",
|
||||
" margin-bottom: 4px;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" [theme=dark] .colab-df-convert {\n",
|
||||
" background-color: #3B4455;\n",
|
||||
" fill: #D2E3FC;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" [theme=dark] .colab-df-convert:hover {\n",
|
||||
" background-color: #434B5C;\n",
|
||||
" box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
|
||||
" filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
|
||||
" fill: #FFFFFF;\n",
|
||||
" }\n",
|
||||
" </style>\n",
|
||||
"\n",
|
||||
" <script>\n",
|
||||
" const buttonEl =\n",
|
||||
" document.querySelector('#df-94a74c8f-1062-4a80-8d3f-32f0fbadf7bb button.colab-df-convert');\n",
|
||||
" buttonEl.style.display =\n",
|
||||
" google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
|
||||
"\n",
|
||||
" async function convertToInteractive(key) {\n",
|
||||
" const element = document.querySelector('#df-94a74c8f-1062-4a80-8d3f-32f0fbadf7bb');\n",
|
||||
" const dataTable =\n",
|
||||
" await google.colab.kernel.invokeFunction('convertToInteractive',\n",
|
||||
" [key], {});\n",
|
||||
" if (!dataTable) return;\n",
|
||||
"\n",
|
||||
" const docLinkHtml = 'Like what you see? Visit the ' +\n",
|
||||
" '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
|
||||
" + ' to learn more about interactive tables.';\n",
|
||||
" element.innerHTML = '';\n",
|
||||
" dataTable['output_type'] = 'display_data';\n",
|
||||
" await google.colab.output.renderOutput(dataTable, element);\n",
|
||||
" const docLink = document.createElement('div');\n",
|
||||
" docLink.innerHTML = docLinkHtml;\n",
|
||||
" element.appendChild(docLink);\n",
|
||||
" }\n",
|
||||
" </script>\n",
|
||||
" </div>\n",
|
||||
"\n",
|
||||
"\n",
|
||||
" <div id=\"df-54b2aa43-838b-47d3-9209-2fb18153cf87\">\n",
|
||||
" <button class=\"colab-df-quickchart\" onclick=\"quickchart('df-54b2aa43-838b-47d3-9209-2fb18153cf87')\"\n",
|
||||
" title=\"Suggest charts\"\n",
|
||||
" style=\"display:none;\">\n",
|
||||
"\n",
|
||||
"<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
|
||||
" width=\"24px\">\n",
|
||||
" <g>\n",
|
||||
" <path d=\"M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z\"/>\n",
|
||||
" </g>\n",
|
||||
"</svg>\n",
|
||||
" </button>\n",
|
||||
"\n",
|
||||
"<style>\n",
|
||||
" .colab-df-quickchart {\n",
|
||||
" --bg-color: #E8F0FE;\n",
|
||||
" --fill-color: #1967D2;\n",
|
||||
" --hover-bg-color: #E2EBFA;\n",
|
||||
" --hover-fill-color: #174EA6;\n",
|
||||
" --disabled-fill-color: #AAA;\n",
|
||||
" --disabled-bg-color: #DDD;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" [theme=dark] .colab-df-quickchart {\n",
|
||||
" --bg-color: #3B4455;\n",
|
||||
" --fill-color: #D2E3FC;\n",
|
||||
" --hover-bg-color: #434B5C;\n",
|
||||
" --hover-fill-color: #FFFFFF;\n",
|
||||
" --disabled-bg-color: #3B4455;\n",
|
||||
" --disabled-fill-color: #666;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" .colab-df-quickchart {\n",
|
||||
" background-color: var(--bg-color);\n",
|
||||
" border: none;\n",
|
||||
" border-radius: 50%;\n",
|
||||
" cursor: pointer;\n",
|
||||
" display: none;\n",
|
||||
" fill: var(--fill-color);\n",
|
||||
" height: 32px;\n",
|
||||
" padding: 0;\n",
|
||||
" width: 32px;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" .colab-df-quickchart:hover {\n",
|
||||
" background-color: var(--hover-bg-color);\n",
|
||||
" box-shadow: 0 1px 2px rgba(60, 64, 67, 0.3), 0 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
|
||||
" fill: var(--button-hover-fill-color);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" .colab-df-quickchart-complete:disabled,\n",
|
||||
" .colab-df-quickchart-complete:disabled:hover {\n",
|
||||
" background-color: var(--disabled-bg-color);\n",
|
||||
" fill: var(--disabled-fill-color);\n",
|
||||
" box-shadow: none;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" .colab-df-spinner {\n",
|
||||
" border: 2px solid var(--fill-color);\n",
|
||||
" border-color: transparent;\n",
|
||||
" border-bottom-color: var(--fill-color);\n",
|
||||
" animation:\n",
|
||||
" spin 1s steps(1) infinite;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" @keyframes spin {\n",
|
||||
" 0% {\n",
|
||||
" border-color: transparent;\n",
|
||||
" border-bottom-color: var(--fill-color);\n",
|
||||
" border-left-color: var(--fill-color);\n",
|
||||
" }\n",
|
||||
" 20% {\n",
|
||||
" border-color: transparent;\n",
|
||||
" border-left-color: var(--fill-color);\n",
|
||||
" border-top-color: var(--fill-color);\n",
|
||||
" }\n",
|
||||
" 30% {\n",
|
||||
" border-color: transparent;\n",
|
||||
" border-left-color: var(--fill-color);\n",
|
||||
" border-top-color: var(--fill-color);\n",
|
||||
" border-right-color: var(--fill-color);\n",
|
||||
" }\n",
|
||||
" 40% {\n",
|
||||
" border-color: transparent;\n",
|
||||
" border-right-color: var(--fill-color);\n",
|
||||
" border-top-color: var(--fill-color);\n",
|
||||
" }\n",
|
||||
" 60% {\n",
|
||||
" border-color: transparent;\n",
|
||||
" border-right-color: var(--fill-color);\n",
|
||||
" }\n",
|
||||
" 80% {\n",
|
||||
" border-color: transparent;\n",
|
||||
" border-right-color: var(--fill-color);\n",
|
||||
" border-bottom-color: var(--fill-color);\n",
|
||||
" }\n",
|
||||
" 90% {\n",
|
||||
" border-color: transparent;\n",
|
||||
" border-bottom-color: var(--fill-color);\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
"</style>\n",
|
||||
"\n",
|
||||
" <script>\n",
|
||||
" async function quickchart(key) {\n",
|
||||
" const quickchartButtonEl =\n",
|
||||
" document.querySelector('#' + key + ' button');\n",
|
||||
" quickchartButtonEl.disabled = true; // To prevent multiple clicks.\n",
|
||||
" quickchartButtonEl.classList.add('colab-df-spinner');\n",
|
||||
" try {\n",
|
||||
" const charts = await google.colab.kernel.invokeFunction(\n",
|
||||
" 'suggestCharts', [key], {});\n",
|
||||
" } catch (error) {\n",
|
||||
" console.error('Error during call to suggestCharts:', error);\n",
|
||||
" }\n",
|
||||
" quickchartButtonEl.classList.remove('colab-df-spinner');\n",
|
||||
" quickchartButtonEl.classList.add('colab-df-quickchart-complete');\n",
|
||||
" }\n",
|
||||
" (() => {\n",
|
||||
" let quickchartButtonEl =\n",
|
||||
" document.querySelector('#df-54b2aa43-838b-47d3-9209-2fb18153cf87 button');\n",
|
||||
" quickchartButtonEl.style.display =\n",
|
||||
" google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
|
||||
" })();\n",
|
||||
" </script>\n",
|
||||
" </div>\n",
|
||||
"\n",
|
||||
" </div>\n",
|
||||
" </div>\n"
|
||||
],
|
||||
"text/plain": [
|
||||
" Rainfall Americas Asia Europe Africa\n",
|
||||
"0 (inches) 2010 \n",
|
||||
"1 Average 104 201.0 193.0 144.0\n",
|
||||
"2 24 hour high 15 26.0 27.0 18.0\n",
|
||||
"3 12 hour high 9 10.0 11.0 12.0\n",
|
||||
"4 2009 \n",
|
||||
"5 Average 133 244.0 155.0 166.0\n",
|
||||
"6 24 hour high 27 28.0 29.0 20.0\n",
|
||||
"7 12 hour high 11 12.0 13.0 16.0"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import pandas as pd\n",
|
||||
"from IPython.display import display\n",
|
||||
"\n",
|
||||
"df = pd.read_csv(\n",
|
||||
" \"/content/tables/table_2025_16_07_16_30_01_569.csv\",\n",
|
||||
")\n",
|
||||
"display(df.fillna(\"\"))"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -5,6 +5,7 @@ from .schema import (
|
||||
StatusType,
|
||||
ExtractedT,
|
||||
AgentDataT,
|
||||
ComparisonOperator,
|
||||
)
|
||||
from .client import AsyncAgentDataClient
|
||||
|
||||
@@ -16,4 +17,5 @@ __all__ = [
|
||||
"StatusType",
|
||||
"ExtractedT",
|
||||
"AgentDataT",
|
||||
"ComparisonOperator",
|
||||
]
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import os
|
||||
from typing import Dict, Generic, List, Optional, Type
|
||||
from typing import Any, Dict, Generic, List, Optional, Type
|
||||
|
||||
from llama_cloud import FilterOperation
|
||||
from llama_cloud.client import AsyncLlamaCloud
|
||||
from tenacity import (
|
||||
WrappedFn,
|
||||
@@ -14,6 +13,7 @@ import httpx
|
||||
|
||||
from .schema import (
|
||||
AgentDataT,
|
||||
ComparisonOperator,
|
||||
TypedAgentData,
|
||||
TypedAgentDataItems,
|
||||
TypedAggregateGroup,
|
||||
@@ -42,7 +42,7 @@ def agent_data_retry(func: WrappedFn) -> WrappedFn:
|
||||
)(func)
|
||||
|
||||
|
||||
def get_default_agent_id() -> Optional[str]:
|
||||
def get_default_agent_id() -> str:
|
||||
"""
|
||||
Retrieve the default agent ID from environment variables.
|
||||
|
||||
@@ -55,7 +55,7 @@ def get_default_agent_id() -> Optional[str]:
|
||||
via environment variables instead of passing it explicitly
|
||||
to each client instance.
|
||||
"""
|
||||
return os.getenv("LLAMA_DEPLOY_DEPLOYMENT_NAME")
|
||||
return os.getenv("LLAMA_DEPLOY_DEPLOYMENT_NAME") or "_public"
|
||||
|
||||
|
||||
class AsyncAgentDataClient(Generic[AgentDataT]):
|
||||
@@ -85,7 +85,7 @@ class AsyncAgentDataClient(Generic[AgentDataT]):
|
||||
agent_client = AsyncAgentDataClient(
|
||||
client=llama_client,
|
||||
type=ExtractedPerson,
|
||||
collection_name="extracted_people",
|
||||
collection="extracted_people",
|
||||
agent_url_id="person-extraction-agent"
|
||||
)
|
||||
|
||||
@@ -94,8 +94,8 @@ class AsyncAgentDataClient(Generic[AgentDataT]):
|
||||
result = await agent_client.create_agent_data(person)
|
||||
|
||||
# Search data
|
||||
results = await agent_client.search_agent_data(
|
||||
filter={"age": FilterOperation(gt=25)},
|
||||
results = await agent_client.search(
|
||||
filter={"age": {"gt": 25}},
|
||||
order_by="data.name",
|
||||
page_size=20
|
||||
)
|
||||
@@ -107,19 +107,20 @@ class AsyncAgentDataClient(Generic[AgentDataT]):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
client: AsyncLlamaCloud,
|
||||
type: Type[AgentDataT],
|
||||
collection_name: str = "default",
|
||||
collection: str = "default",
|
||||
agent_url_id: Optional[str] = None,
|
||||
client: Optional[AsyncLlamaCloud] = None,
|
||||
token: Optional[str] = None,
|
||||
base_url: Optional[str] = None,
|
||||
):
|
||||
"""
|
||||
Initialize the AsyncAgentDataClient.
|
||||
|
||||
Args:
|
||||
client: AsyncLlamaCloud client instance for API communication
|
||||
type: Pydantic BaseModel class that defines the data structure.
|
||||
All agent data will be validated against this type.
|
||||
collection_name: Named collection within the agent for organizing data.
|
||||
collection: Named collection within the agent for organizing data.
|
||||
Defaults to "default". Collections allow logical separation of
|
||||
different data types or workflows within the same agent.
|
||||
agent_url_id: Unique identifier for the agent. This normally appears in the
|
||||
@@ -127,6 +128,11 @@ class AsyncAgentDataClient(Generic[AgentDataT]):
|
||||
will attempt to use the LLAMA_DEPLOY_DEPLOYMENT_NAME environment
|
||||
variable. Data can only be added to an already existing agent in the
|
||||
platform.
|
||||
client: AsyncLlamaCloud client instance for API communication. If not provided, will
|
||||
construct one from the provided api token and base url
|
||||
token: Llama Cloud API token. Reads from LLAMA_CLOUD_API_KEY if not provided
|
||||
base_url: Llama Cloud API token. Reads from LLAMA_CLOUD_BASE_URL if not provided, and
|
||||
defaults to https://api.cloud.llamaindex.ai
|
||||
|
||||
Raises:
|
||||
ValueError: If agent_url_id is not provided and the
|
||||
@@ -138,33 +144,34 @@ class AsyncAgentDataClient(Generic[AgentDataT]):
|
||||
"""
|
||||
|
||||
self.agent_url_id = agent_url_id or get_default_agent_id()
|
||||
if not self.agent_url_id:
|
||||
raise ValueError(
|
||||
"Agent ID is required, or set the LLAMA_DEPLOY_DEPLOYMENT_NAME environment variable"
|
||||
)
|
||||
|
||||
self.collection_name = collection_name
|
||||
self.collection = collection
|
||||
if not client:
|
||||
client = AsyncLlamaCloud(
|
||||
token=token or os.getenv("LLAMA_CLOUD_API_KEY"),
|
||||
base_url=base_url or os.getenv("LLAMA_CLOUD_BASE_URL"),
|
||||
)
|
||||
self.client = client
|
||||
self.type = type
|
||||
|
||||
@agent_data_retry
|
||||
async def get_agent_data(self, item_id: str) -> TypedAgentData[AgentDataT]:
|
||||
async def get_item(self, item_id: str) -> TypedAgentData[AgentDataT]:
|
||||
raw_data = await self.client.beta.get_agent_data(
|
||||
item_id=item_id,
|
||||
)
|
||||
return TypedAgentData.from_raw(raw_data, validator=self.type)
|
||||
|
||||
@agent_data_retry
|
||||
async def create_agent_data(self, data: AgentDataT) -> TypedAgentData[AgentDataT]:
|
||||
async def create_item(self, data: AgentDataT) -> TypedAgentData[AgentDataT]:
|
||||
raw_data = await self.client.beta.create_agent_data(
|
||||
agent_slug=self.agent_url_id,
|
||||
collection=self.collection_name,
|
||||
collection=self.collection,
|
||||
data=data.model_dump(),
|
||||
)
|
||||
return TypedAgentData.from_raw(raw_data, validator=self.type)
|
||||
|
||||
@agent_data_retry
|
||||
async def update_agent_data(
|
||||
async def update_item(
|
||||
self, item_id: str, data: AgentDataT
|
||||
) -> TypedAgentData[AgentDataT]:
|
||||
raw_data = await self.client.beta.update_agent_data(
|
||||
@@ -174,13 +181,13 @@ class AsyncAgentDataClient(Generic[AgentDataT]):
|
||||
return TypedAgentData.from_raw(raw_data, validator=self.type)
|
||||
|
||||
@agent_data_retry
|
||||
async def delete_agent_data(self, item_id: str) -> None:
|
||||
async def delete_item(self, item_id: str) -> None:
|
||||
await self.client.beta.delete_agent_data(item_id=item_id)
|
||||
|
||||
@agent_data_retry
|
||||
async def search_agent_data(
|
||||
async def search(
|
||||
self,
|
||||
filter: Optional[Dict[str, Optional[FilterOperation]]] = None,
|
||||
filter: Optional[Dict[str, Dict[ComparisonOperator, Any]]] = None,
|
||||
order_by: Optional[str] = None,
|
||||
offset: Optional[int] = None,
|
||||
page_size: Optional[int] = None,
|
||||
@@ -191,11 +198,11 @@ class AsyncAgentDataClient(Generic[AgentDataT]):
|
||||
Args:
|
||||
filter: Filter conditions to apply to the search. Dict mapping field names to FilterOperation objects. Filters only by data fields
|
||||
Examples:
|
||||
- {"age": FilterOperation(gt=18)} - age greater than 18
|
||||
- {"status": FilterOperation(eq="active")} - status equals "active"
|
||||
- {"tags": FilterOperation(includes=["python", "ml"])} - tags include "python" or "ml"
|
||||
- {"created_at": FilterOperation(gte="2024-01-01")} - created after date
|
||||
- {"score": FilterOperation(lt=100, gte=50)} - score between 50 and 100
|
||||
- {"age": {"gt": 18}} - age greater than 18
|
||||
- {"status": {"eq": "active"}} - status equals "active"
|
||||
- {"tags": {"includes": ["python", "ml"]}} - tags include "python" or "ml"
|
||||
- {"created_at": {"gte": "2024-01-01"}} - created after date
|
||||
- {"score": {"lt": 100, "gte": 50}} - score between 50 and 100
|
||||
order_by: Comma delimited list of fields to sort results by. Can order by standard agent fields like created_at, or by data fields. Data fields must be prefixed with "data.". If ordering desceding, use a " desc" suffix.
|
||||
Examples:
|
||||
- "data.name desc, created_at" - sort by name in descending order, and then by creation date
|
||||
@@ -205,7 +212,7 @@ class AsyncAgentDataClient(Generic[AgentDataT]):
|
||||
"""
|
||||
raw = await self.client.beta.search_agent_data_api_v_1_beta_agent_data_search_post(
|
||||
agent_slug=self.agent_url_id,
|
||||
collection=self.collection_name,
|
||||
collection=self.collection,
|
||||
filter=filter,
|
||||
order_by=order_by,
|
||||
offset=offset,
|
||||
@@ -221,9 +228,9 @@ class AsyncAgentDataClient(Generic[AgentDataT]):
|
||||
)
|
||||
|
||||
@agent_data_retry
|
||||
async def aggregate_agent_data(
|
||||
async def aggregate(
|
||||
self,
|
||||
filter: Optional[Dict[str, Optional[FilterOperation]]] = None,
|
||||
filter: Optional[Dict[str, Dict[ComparisonOperator, Any]]] = None,
|
||||
group_by: Optional[List[str]] = None,
|
||||
count: Optional[bool] = None,
|
||||
first: Optional[bool] = None,
|
||||
@@ -235,20 +242,20 @@ class AsyncAgentDataClient(Generic[AgentDataT]):
|
||||
Aggregate agent data into groups according to the group_by fields.
|
||||
Args:
|
||||
filter: Filter conditions to apply to the search. Dict mapping field names to FilterOperation objects. Filters only by data fields
|
||||
See search_agent_data for more details on filtering.
|
||||
See search for more details on filtering.
|
||||
group_by: List of fields to group by. Groups strictly by equality. Can only group by data fields.
|
||||
Examples:
|
||||
- ["name"] - group by name
|
||||
- ["name", "age"] - group by name and age
|
||||
count: Whether to include the count of items in each group.
|
||||
first: Whether to include the first item in each group.
|
||||
order_by: Comma delimited list of fields to sort results by. See search_agent_data for more details on ordering.
|
||||
order_by: Comma delimited list of fields to sort results by. See search for more details on ordering.
|
||||
offset: Number of groups to skip from the beginning. Defaults to 0.
|
||||
page_size: Maximum number of groups to return per page.
|
||||
"""
|
||||
raw = await self.client.beta.aggregate_agent_data_api_v_1_beta_agent_data_aggregate_post(
|
||||
agent_slug=self.agent_url_id,
|
||||
collection=self.collection_name,
|
||||
collection=self.collection,
|
||||
page_size=page_size,
|
||||
filter=filter,
|
||||
order_by=order_by,
|
||||
|
||||
@@ -60,7 +60,11 @@ AgentDataT = TypeVar("AgentDataT", bound=BaseModel)
|
||||
ExtractedT = TypeVar("ExtractedT", bound=Union[BaseModel, dict])
|
||||
|
||||
# Status types for extracted data workflow
|
||||
StatusType = Union[Literal["error", "accepted", "rejected", "in_review"], str]
|
||||
StatusType = Union[Literal["error", "accepted", "rejected", "pending_review"], str]
|
||||
|
||||
ComparisonOperator = Dict[
|
||||
str, Dict[Literal["gt", "gte", "lt", "lte", "eq", "includes"], Any]
|
||||
]
|
||||
|
||||
|
||||
class TypedAgentData(BaseModel, Generic[AgentDataT]):
|
||||
@@ -142,7 +146,7 @@ class TypedAgentDataItems(BaseModel, Generic[AgentDataT]):
|
||||
Example:
|
||||
```python
|
||||
# Search with pagination
|
||||
results = await client.search_agent_data(
|
||||
results = await client.search(
|
||||
page_size=10,
|
||||
include_total=True
|
||||
)
|
||||
@@ -152,7 +156,7 @@ class TypedAgentDataItems(BaseModel, Generic[AgentDataT]):
|
||||
|
||||
if results.has_more:
|
||||
# Load next page
|
||||
next_page = await client.search_agent_data(
|
||||
next_page = await client.search(
|
||||
page_size=10,
|
||||
offset=10
|
||||
)
|
||||
@@ -183,9 +187,12 @@ class ExtractedData(BaseModel, Generic[ExtractedT]):
|
||||
data: The current state of the data (may differ from original after edits)
|
||||
status: Current workflow status (in_review, accepted, rejected, error)
|
||||
confidence: Confidence scores for individual fields (if available)
|
||||
file_id: The llamacloud file ID of the file that was used to extract the data
|
||||
file_name: The name of the file that was used to extract the data
|
||||
file_hash: A content hash of the file that was used to extract the data, for de-duplication
|
||||
|
||||
Status Workflow:
|
||||
- "in_review": Initial state, awaiting human review
|
||||
- "pending_review": Initial state, awaiting human review
|
||||
- "accepted": Data approved and ready for use
|
||||
- "rejected": Data rejected, needs re-extraction or manual fix
|
||||
- "error": Processing error occurred
|
||||
@@ -194,8 +201,8 @@ class ExtractedData(BaseModel, Generic[ExtractedT]):
|
||||
```python
|
||||
# Create extracted data for review
|
||||
extracted = ExtractedData.create(
|
||||
extracted_data=person_data,
|
||||
status="in_review",
|
||||
data=person_data,
|
||||
status="pending_review",
|
||||
confidence={"name": 0.95, "age": 0.87}
|
||||
)
|
||||
|
||||
@@ -212,20 +219,35 @@ class ExtractedData(BaseModel, Generic[ExtractedT]):
|
||||
data: ExtractedT = Field(
|
||||
description="The latest state of the data. Will differ if data has been updated"
|
||||
)
|
||||
status: Union[Literal["error", "accepted", "rejected", "in_review"], str] = Field(
|
||||
description="The status of the extracted data"
|
||||
)
|
||||
confidence: Dict[str, Union[float, Dict]] = Field(
|
||||
status: StatusType = Field(description="The status of the extracted data")
|
||||
confidence: Dict[str, Any] = Field(
|
||||
default_factory=dict,
|
||||
description="Confidence scores, if any, for each primitive field in the original_data data",
|
||||
)
|
||||
file_id: Optional[str] = Field(
|
||||
None, description="The ID of the file that was used to extract the data"
|
||||
)
|
||||
file_name: Optional[str] = Field(
|
||||
None, description="The name of the file that was used to extract the data"
|
||||
)
|
||||
file_hash: Optional[str] = Field(
|
||||
None, description="The hash of the file that was used to extract the data"
|
||||
)
|
||||
metadata: Optional[Dict[str, Any]] = Field(
|
||||
default_factory=dict,
|
||||
description="Additional metadata about the extracted data, such as errors, tokens, etc.",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def create(
|
||||
cls,
|
||||
extracted_data: ExtractedT,
|
||||
status: StatusType = "in_review",
|
||||
confidence: Optional[Dict[str, Union[float, Dict]]] = None,
|
||||
data: ExtractedT,
|
||||
status: StatusType = "pending_review",
|
||||
confidence: Optional[Dict[str, Any]] = None,
|
||||
file_id: Optional[str] = None,
|
||||
file_name: Optional[str] = None,
|
||||
file_hash: Optional[str] = None,
|
||||
metadata: Optional[Dict[str, Any]] = None,
|
||||
) -> "ExtractedData[ExtractedT]":
|
||||
"""
|
||||
Create a new ExtractedData instance with sensible defaults.
|
||||
@@ -234,15 +256,22 @@ class ExtractedData(BaseModel, Generic[ExtractedT]):
|
||||
extracted_data: The extracted data payload
|
||||
status: Initial workflow status
|
||||
confidence: Optional confidence scores for fields
|
||||
file_id: The llamacloud file ID of the file that was used to extract the data
|
||||
file_name: The name of the file that was used to extract the data
|
||||
file_hash: A content hash of the file that was used to extract the data, for de-duplication
|
||||
|
||||
Returns:
|
||||
New ExtractedData instance ready for storage
|
||||
"""
|
||||
return cls(
|
||||
original_data=extracted_data,
|
||||
data=extracted_data,
|
||||
original_data=data,
|
||||
data=data,
|
||||
status=status,
|
||||
confidence=confidence or {},
|
||||
file_id=file_id,
|
||||
file_name=file_name,
|
||||
file_hash=file_hash,
|
||||
metadata=metadata or {},
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -137,15 +137,9 @@ def run_in_thread(
|
||||
|
||||
|
||||
def _extraction_config_warning(config: ExtractConfig) -> None:
|
||||
if config.use_reasoning:
|
||||
if config.cite_sources or config.confidence_scores:
|
||||
warnings.warn(
|
||||
"`use_reasoning` is an experimental feature. Results will be available in "
|
||||
"the `extraction_metadata` field for the extraction run.",
|
||||
ExperimentalWarning,
|
||||
)
|
||||
if config.cite_sources:
|
||||
warnings.warn(
|
||||
"`cite_sources` is an experimental feature. This may greatly increase the "
|
||||
"`cite_sources`/`confidence_scores` could greatly increase the "
|
||||
"size of the response, and slow down the extraction. Results will be "
|
||||
"available in the `extraction_metadata` field for the extraction run.",
|
||||
ExperimentalWarning,
|
||||
|
||||
@@ -37,6 +37,7 @@ from llama_cloud_services.parse.utils import (
|
||||
nest_asyncio_msg,
|
||||
make_api_request,
|
||||
partition_pages,
|
||||
extract_tables_from_json_results,
|
||||
)
|
||||
|
||||
# can put in a path to the file or the file bytes itself
|
||||
@@ -380,6 +381,10 @@ class LlamaParse(BasePydanticReader):
|
||||
default=False,
|
||||
description="Preserve grid alignment across page in text mode.",
|
||||
)
|
||||
preserve_very_small_text: Optional[bool] = Field(
|
||||
default=False,
|
||||
description="If set, the parser will try to preserve very small text lines. This can be useful for documents containing vector graphics with very small text lines that may not be recognized by OCR or a vision model (such as in CAD drawings).",
|
||||
)
|
||||
replace_failed_page_mode: Optional[FailedPageMode] = Field(
|
||||
default=None,
|
||||
description="The mode to use to replace the failed page, see FailedPageMode enum for possible value. If set, the parser will replace the failed page with the specified mode. If not set, the default mode (raw_text) will be used.",
|
||||
@@ -911,6 +916,9 @@ class LlamaParse(BasePydanticReader):
|
||||
"preserve_layout_alignment_across_pages"
|
||||
] = self.preserve_layout_alignment_across_pages
|
||||
|
||||
if self.preserve_very_small_text:
|
||||
data["preserve_very_small_text"] = self.preserve_very_small_text
|
||||
|
||||
if self.preset is not None:
|
||||
data["preset"] = self.preset
|
||||
|
||||
@@ -1620,6 +1628,16 @@ class LlamaParse(BasePydanticReader):
|
||||
else:
|
||||
raise e
|
||||
|
||||
def get_tables(self, json_results: List[dict], download_path: str) -> List[str]:
|
||||
if not os.path.exists(download_path):
|
||||
os.makedirs(download_path)
|
||||
return extract_tables_from_json_results(json_results, download_path)
|
||||
|
||||
async def aget_tables(
|
||||
self, json_result: List[dict], download_path: str
|
||||
) -> List[str]:
|
||||
return await asyncio.to_thread(self.get_tables, json_result, download_path)
|
||||
|
||||
async def aget_xlsx(
|
||||
self, json_result: List[dict], download_path: str
|
||||
) -> List[dict]:
|
||||
@@ -1686,3 +1704,79 @@ class LlamaParse(BasePydanticReader):
|
||||
sub_docs.append(sub_doc)
|
||||
|
||||
return sub_docs
|
||||
|
||||
async def aget_result(
|
||||
self, job_id: Union[str, List[str]]
|
||||
) -> Union[JobResult, List[JobResult]]:
|
||||
"""
|
||||
Return JobResult object for previously parsed job(s).
|
||||
|
||||
If the job is still pending, the result will not be returned until it is completed.
|
||||
|
||||
Args:
|
||||
job_id: Job ID or list of multiple Job IDs to be retrieved.
|
||||
|
||||
Returns:
|
||||
JobResult object or list of JobResult objects if multiple job IDs were provided.
|
||||
"""
|
||||
if isinstance(job_id, str):
|
||||
result = await self._get_job_result(
|
||||
job_id, ResultType.JSON.value, verbose=self.verbose
|
||||
)
|
||||
return JobResult(
|
||||
job_id=job_id,
|
||||
file_name="",
|
||||
job_result=result,
|
||||
api_key=self.api_key,
|
||||
base_url=self.base_url,
|
||||
client=self.aclient,
|
||||
page_separator=self.page_separator or _DEFAULT_SEPARATOR,
|
||||
)
|
||||
elif isinstance(job_id, list):
|
||||
results = []
|
||||
jobs = [
|
||||
self._get_job_result(id_, ResultType.JSON.value, verbose=self.verbose)
|
||||
for id_ in job_id
|
||||
]
|
||||
results = await run_jobs(
|
||||
jobs,
|
||||
workers=self.num_workers,
|
||||
desc="Getting job results",
|
||||
show_progress=self.show_progress,
|
||||
)
|
||||
return [
|
||||
JobResult(
|
||||
job_id=job_id[i],
|
||||
file_name="",
|
||||
job_result=result,
|
||||
api_key=self.api_key,
|
||||
base_url=self.base_url,
|
||||
client=self.aclient,
|
||||
page_separator=self.page_separator or _DEFAULT_SEPARATOR,
|
||||
)
|
||||
for i, result in enumerate(results)
|
||||
]
|
||||
else:
|
||||
raise ValueError("The input job_id must be a string or a list of strings.")
|
||||
|
||||
def get_result(
|
||||
self, job_id: Union[str, List[str]]
|
||||
) -> Union[JobResult, List[JobResult]]:
|
||||
"""
|
||||
Return JobResult object for previously parsed job(s).
|
||||
|
||||
If the job is still pending, the result will not be returned until it is completed.
|
||||
|
||||
Args:
|
||||
job_id: Job ID or list of multiple Job IDs to be retrieved.
|
||||
|
||||
Returns:
|
||||
JobResult object or list of JobResult objects if multiple job IDs were provided.
|
||||
"""
|
||||
try:
|
||||
return asyncio_run(self.aget_result(job_id))
|
||||
except RuntimeError as e:
|
||||
if nest_asyncio_err in str(e):
|
||||
raise RuntimeError(nest_asyncio_msg)
|
||||
else:
|
||||
raise e
|
||||
|
||||
@@ -295,13 +295,64 @@ class JobResult(BaseModel):
|
||||
async def aget_markdown_nodes(self, split_by_page: bool = False) -> List[TextNode]:
|
||||
"""
|
||||
Get the markdown nodes from the job.
|
||||
|
||||
Args:
|
||||
split_by_page: Whether to split the pages into separate documents
|
||||
"""
|
||||
documents = await self.aget_markdown_documents(split_by_page)
|
||||
return [TextNode(text=doc.text, metadata=doc.metadata) for doc in documents]
|
||||
|
||||
def get_markdown(self) -> str:
|
||||
"""
|
||||
Get the raw parsed markdown from the job, distinct from the markdown documents.
|
||||
This does not include page separators, e.g. if merge_tables_across_pages_in_markdown is True
|
||||
"""
|
||||
return asyncio_run(self.aget_markdown())
|
||||
|
||||
async def aget_markdown(self) -> str:
|
||||
"""
|
||||
Get the raw parsed markdown from the job, distinct from the markdown documents.
|
||||
This does not include page separators, e.g. if merge_tables_across_pages_in_markdown is True
|
||||
"""
|
||||
url = f"{self._base_url}/api/v1/parsing/job/{self.job_id}/result/raw/markdown"
|
||||
response = await make_api_request(self._client, "GET", url)
|
||||
return response.content.decode("utf-8")
|
||||
|
||||
def get_text(self) -> str:
|
||||
"""
|
||||
Get the raw parsed text from the job.
|
||||
"""
|
||||
return asyncio_run(self.aget_text())
|
||||
|
||||
async def aget_text(self) -> str:
|
||||
"""
|
||||
Get the raw parsed text from the job.
|
||||
"""
|
||||
url = f"{self._base_url}/api/v1/parsing/job/{self.job_id}/result/raw/text"
|
||||
response = await make_api_request(self._client, "GET", url)
|
||||
return response.content.decode("utf-8")
|
||||
|
||||
def get_json(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Get the full parsed JSON result from the job.
|
||||
|
||||
Note:
|
||||
This is not the same as JobResult.json(), which is a
|
||||
JSON serialized version of the JobResult Page Documents.
|
||||
"""
|
||||
return asyncio_run(self.aget_json())
|
||||
|
||||
async def aget_json(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Get the full parsed JSON result from the job.
|
||||
|
||||
Note:
|
||||
This is not the same as JobResult.json(), which is a
|
||||
JSON serialized version of the JobResult Page Documents.
|
||||
"""
|
||||
url = f"{self._base_url}/api/v1/parsing/job/{self.job_id}/result/json"
|
||||
response = await make_api_request(self._client, "GET", url)
|
||||
return response.json()
|
||||
|
||||
async def _get_image_document_with_bytes(
|
||||
self, image: ImageItem, page: Page
|
||||
) -> ImageDocument:
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import httpx
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
from tenacity import (
|
||||
retry,
|
||||
stop_after_attempt,
|
||||
@@ -9,7 +12,7 @@ from tenacity import (
|
||||
retry_if_exception,
|
||||
before_sleep_log,
|
||||
)
|
||||
from typing import Any, Iterable, Iterator, Optional
|
||||
from typing import Any, Iterable, Iterator, Optional, List, cast
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -351,3 +354,30 @@ def partition_pages(
|
||||
yield ",".join(targets)
|
||||
else:
|
||||
return
|
||||
|
||||
|
||||
def extract_tables_from_json_results(
|
||||
json_results: List[dict], download_path: str
|
||||
) -> List[str]:
|
||||
tables = []
|
||||
for json_result in json_results:
|
||||
pages = json_result["pages"]
|
||||
for page in pages:
|
||||
page = cast(dict, page)
|
||||
items = page.get("items", [])
|
||||
if items:
|
||||
for i, item in enumerate(items):
|
||||
item = cast(dict, item)
|
||||
if item.get("type", "") == "table" and item.get("csv", ""):
|
||||
savepath = os.path.join(
|
||||
download_path,
|
||||
f"table_{datetime.now().strftime('%Y_%d_%m_%H_%M_%S_%f')[:-3]}.csv",
|
||||
)
|
||||
if Path(savepath).exists():
|
||||
savepath = (
|
||||
savepath.replace(".csv", "_")[0] + str(i) + ".csv"
|
||||
)
|
||||
with open(savepath, "w") as f:
|
||||
f.write(item["csv"])
|
||||
tables.append(savepath)
|
||||
return tables
|
||||
|
||||
Generated
+20
-19
@@ -1,4 +1,4 @@
|
||||
# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "aiohappyeyeballs"
|
||||
@@ -257,7 +257,7 @@ version = "2025.1.31"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
groups = ["main", "dev"]
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"},
|
||||
{file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"},
|
||||
@@ -350,7 +350,7 @@ version = "3.4.1"
|
||||
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main", "dev"]
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"},
|
||||
{file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"},
|
||||
@@ -593,7 +593,7 @@ description = "Like `typing._eval_type`, but lets older Python versions use newe
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
markers = "python_version < \"3.10\""
|
||||
markers = "python_version == \"3.9\""
|
||||
files = [
|
||||
{file = "eval_type_backport-0.2.2-py3-none-any.whl", hash = "sha256:cb6ad7c393517f476f96d456d0412ea80f0a8cf96f6892834cd9340149111b0a"},
|
||||
{file = "eval_type_backport-0.2.2.tar.gz", hash = "sha256:f0576b4cf01ebb5bd358d02314d31846af5e07678387486e2c798af0e7d849c1"},
|
||||
@@ -896,7 +896,7 @@ version = "0.16.0"
|
||||
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main", "dev"]
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"},
|
||||
{file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"},
|
||||
@@ -955,7 +955,7 @@ version = "3.10"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
groups = ["main", "dev"]
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"},
|
||||
{file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"},
|
||||
@@ -971,7 +971,7 @@ description = "Read metadata from Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["dev"]
|
||||
markers = "python_version < \"3.10\""
|
||||
markers = "python_version == \"3.9\""
|
||||
files = [
|
||||
{file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"},
|
||||
{file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"},
|
||||
@@ -1170,14 +1170,14 @@ test = ["ipykernel", "pre-commit", "pytest (<8)", "pytest-cov", "pytest-timeout"
|
||||
|
||||
[[package]]
|
||||
name = "llama-cloud"
|
||||
version = "0.1.23"
|
||||
version = "0.1.34"
|
||||
description = ""
|
||||
optional = false
|
||||
python-versions = "<4,>=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "llama_cloud-0.1.23-py3-none-any.whl", hash = "sha256:ce95b0705d85c99b3b27b0af0d16a17d9a81b14c96bf13c1063a1bd13d8d0446"},
|
||||
{file = "llama_cloud-0.1.23.tar.gz", hash = "sha256:3d84a24a860f046d39a106c06742ec0ea39a574ac42bbf91706fe025f44e233e"},
|
||||
{file = "llama_cloud-0.1.34-py3-none-any.whl", hash = "sha256:9b06fb109b1d9f652095a11732ae3dbe84e48cc00c580b2eeb19e71e901267be"},
|
||||
{file = "llama_cloud-0.1.34.tar.gz", hash = "sha256:6866e4bab47d2c1840bdf169c13c06176931c1d30697ac1fa71bab7942a041e9"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -1187,24 +1187,25 @@ pydantic = ">=1.10"
|
||||
|
||||
[[package]]
|
||||
name = "llama-cloud-services"
|
||||
version = "0.6.30"
|
||||
version = "0.6.48"
|
||||
description = "Tailored SDK clients for LlamaCloud services."
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "llama_cloud_services-0.6.30-py3-none-any.whl", hash = "sha256:4d5817a9841fc3ba3409865c52d082090f4ef827931f0e5e4a89f5818c0d4e36"},
|
||||
{file = "llama_cloud_services-0.6.30.tar.gz", hash = "sha256:2cb5004d13127aac52888ae9b3d70f899d598633520b2a2542bb62682d08d776"},
|
||||
{file = "llama_cloud_services-0.6.48-py3-none-any.whl", hash = "sha256:5faadcfa270f3be516dd7d3568cbb54b3014b2c8878c5bd7d3252ab1bd635869"},
|
||||
{file = "llama_cloud_services-0.6.48.tar.gz", hash = "sha256:64fe8d8ce7434d6a481646c35be55973ae10765d20b6be0067a262e6be61f2df"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
click = ">=8.1.7,<9.0.0"
|
||||
eval-type-backport = {version = ">=0.2.0,<0.3.0", markers = "python_version < \"3.10\""}
|
||||
llama-cloud = "0.1.23"
|
||||
llama-cloud = "0.1.34"
|
||||
llama-index-core = ">=0.12.0"
|
||||
platformdirs = ">=4.3.7,<5.0.0"
|
||||
pydantic = ">=2.8,<2.10 || >2.10"
|
||||
python-dotenv = ">=1.0.1,<2.0.0"
|
||||
tenacity = ">=8.5.0,<10.0"
|
||||
|
||||
[[package]]
|
||||
name = "llama-index-core"
|
||||
@@ -2473,7 +2474,7 @@ version = "2.32.4"
|
||||
description = "Python HTTP for Humans."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main", "dev"]
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c"},
|
||||
{file = "requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422"},
|
||||
@@ -2807,7 +2808,7 @@ files = [
|
||||
{file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"},
|
||||
{file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"},
|
||||
]
|
||||
markers = {dev = "python_version < \"3.10\""}
|
||||
markers = {dev = "python_version == \"3.9\""}
|
||||
|
||||
[[package]]
|
||||
name = "typing-inspect"
|
||||
@@ -2846,7 +2847,7 @@ version = "2.4.0"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main", "dev"]
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"},
|
||||
{file = "urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466"},
|
||||
@@ -3068,7 +3069,7 @@ description = "Backport of pathlib-compatible object wrapper for zip files"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["dev"]
|
||||
markers = "python_version < \"3.10\""
|
||||
markers = "python_version == \"3.9\""
|
||||
files = [
|
||||
{file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"},
|
||||
{file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"},
|
||||
@@ -3085,4 +3086,4 @@ type = ["pytest-mypy"]
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = ">=3.9,<4.0"
|
||||
content-hash = "1be767f62c8a8e1c9acb76347055a837ea2c44130004703361d0303b049bede7"
|
||||
content-hash = "0b5ab34c44ce7802b02e01b6535b9bb0e08f99f26a43a76be88534dd8a417666"
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.poetry]
|
||||
name = "llama-parse"
|
||||
version = "0.6.45"
|
||||
version = "0.6.51"
|
||||
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 = ">=0.6.45"
|
||||
llama-cloud-services = ">=0.6.51"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pytest = "^8.0.0"
|
||||
|
||||
Generated
+4
-4
@@ -1925,14 +1925,14 @@ rapidfuzz = ">=3.9.0,<4.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "llama-cloud"
|
||||
version = "0.1.33"
|
||||
version = "0.1.34"
|
||||
description = ""
|
||||
optional = false
|
||||
python-versions = "<4,>=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "llama_cloud-0.1.33-py3-none-any.whl", hash = "sha256:35b7d4a30b013f0a343f7e09126b531c697d65bffd4eb4d2d79bf7d65f256178"},
|
||||
{file = "llama_cloud-0.1.33.tar.gz", hash = "sha256:a0bb900d5a6e86f8c767b48686c5253679ad7ca1b57612dc39b0767e57ad3d78"},
|
||||
{file = "llama_cloud-0.1.34-py3-none-any.whl", hash = "sha256:9b06fb109b1d9f652095a11732ae3dbe84e48cc00c580b2eeb19e71e901267be"},
|
||||
{file = "llama_cloud-0.1.34.tar.gz", hash = "sha256:6866e4bab47d2c1840bdf169c13c06176931c1d30697ac1fa71bab7942a041e9"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -4623,4 +4623,4 @@ type = ["pytest-mypy"]
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = ">=3.9,<4.0"
|
||||
content-hash = "487717a0bbe7ff67360e3e9f187e15a33c77e4942a7c909cb37b95425a81544c"
|
||||
content-hash = "62bbed6ef774d11a0e775524ea3fc86e840fc35823c199dbbe1980ce9c81d2a1"
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@ python_version = "3.10"
|
||||
|
||||
[tool.poetry]
|
||||
name = "llama-cloud-services"
|
||||
version = "0.6.45"
|
||||
version = "0.6.51"
|
||||
description = "Tailored SDK clients for LlamaCloud services."
|
||||
authors = ["Logan Markewich <logan@runllama.ai>"]
|
||||
license = "MIT"
|
||||
@@ -18,7 +18,7 @@ packages = [{include = "llama_cloud_services"}]
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9,<4.0"
|
||||
llama-index-core = ">=0.12.0"
|
||||
llama-cloud = "==0.1.33"
|
||||
llama-cloud = "==0.1.34"
|
||||
pydantic = ">=2.8,!=2.10"
|
||||
click = "^8.1.7"
|
||||
python-dotenv = "^1.0.1"
|
||||
|
||||
Executable
+147
@@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env -S uv run --script
|
||||
# /// script
|
||||
# dependencies = ["click", "tomlkit"]
|
||||
# ///
|
||||
|
||||
import click
|
||||
import subprocess
|
||||
import sys
|
||||
import tomlkit
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def get_current_versions() -> tuple[str, str, str]:
|
||||
"""Get current versions from both pyproject.toml files."""
|
||||
# Read main pyproject.toml
|
||||
main_content = Path("pyproject.toml").read_text()
|
||||
main_doc = tomlkit.parse(main_content)
|
||||
main_version = main_doc["tool"]["poetry"]["version"]
|
||||
|
||||
# Read llama_parse/pyproject.toml
|
||||
llama_parse_content = Path("llama_parse/pyproject.toml").read_text()
|
||||
llama_parse_doc = tomlkit.parse(llama_parse_content)
|
||||
llama_parse_version = llama_parse_doc["tool"]["poetry"]["version"]
|
||||
dependency_version = llama_parse_doc["tool"]["poetry"]["dependencies"][
|
||||
"llama-cloud-services"
|
||||
]
|
||||
|
||||
return str(main_version), str(llama_parse_version), str(dependency_version)
|
||||
|
||||
|
||||
def validate_versions(
|
||||
main_version: str, llama_parse_version: str, dependency_version: str
|
||||
) -> list[str]:
|
||||
"""Validate that versions are consistent and return warnings."""
|
||||
warnings = []
|
||||
|
||||
if main_version != llama_parse_version:
|
||||
warnings.append(
|
||||
f"Version mismatch: main={main_version}, llama_parse={llama_parse_version}"
|
||||
)
|
||||
|
||||
# Extract version from dependency string (e.g., ">=0.6.51" -> "0.6.51")
|
||||
if dependency_version and dependency_version.startswith(">="):
|
||||
dep_ver = dependency_version[2:]
|
||||
if dep_ver != main_version:
|
||||
warnings.append(
|
||||
f"Dependency version mismatch: dependency={dep_ver}, main={main_version}"
|
||||
)
|
||||
|
||||
return warnings
|
||||
|
||||
|
||||
def set_version(version: str) -> None:
|
||||
"""Set version across all pyproject.toml files using tomlkit to preserve formatting."""
|
||||
# Update main pyproject.toml
|
||||
main_content = Path("pyproject.toml").read_text()
|
||||
main_doc = tomlkit.parse(main_content)
|
||||
main_doc["tool"]["poetry"]["version"] = version
|
||||
Path("pyproject.toml").write_text(tomlkit.dumps(main_doc))
|
||||
|
||||
# Update llama_parse/pyproject.toml
|
||||
llama_parse_content = Path("llama_parse/pyproject.toml").read_text()
|
||||
llama_parse_doc = tomlkit.parse(llama_parse_content)
|
||||
llama_parse_doc["tool"]["poetry"]["version"] = version
|
||||
llama_parse_doc["tool"]["poetry"]["dependencies"][
|
||||
"llama-cloud-services"
|
||||
] = f">={version}"
|
||||
Path("llama_parse/pyproject.toml").write_text(tomlkit.dumps(llama_parse_doc))
|
||||
|
||||
click.echo(f"Updated all versions to {version}")
|
||||
|
||||
|
||||
def get_current_branch() -> str:
|
||||
"""Get the current git branch."""
|
||||
result = subprocess.run(
|
||||
["git", "branch", "--show-current"], capture_output=True, text=True, check=True
|
||||
)
|
||||
return result.stdout.strip()
|
||||
|
||||
|
||||
def create_and_push_tag(version: str) -> None:
|
||||
"""Create a git tag and push it."""
|
||||
current_branch = get_current_branch()
|
||||
if current_branch != "main":
|
||||
click.echo(
|
||||
f"Error: Not on main branch (currently on {current_branch})", err=True
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
tag_name = f"v{version}"
|
||||
|
||||
# Create tag
|
||||
subprocess.run(["git", "tag", tag_name], check=True)
|
||||
click.echo(f"Created tag {tag_name}")
|
||||
|
||||
# Push tag
|
||||
subprocess.run(["git", "push", "origin", tag_name], check=True)
|
||||
click.echo(f"Pushed tag {tag_name}")
|
||||
|
||||
|
||||
@click.group()
|
||||
def cli() -> None:
|
||||
"""Version management for llama-cloud-services."""
|
||||
pass
|
||||
|
||||
|
||||
@cli.command()
|
||||
def get() -> None:
|
||||
"""Get current versions and show validation warnings."""
|
||||
main_version, llama_parse_version, dependency_version = get_current_versions()
|
||||
|
||||
click.echo("Current versions:")
|
||||
click.echo(f" llama-cloud-services: {main_version}")
|
||||
click.echo(f" llama-parse: {llama_parse_version}")
|
||||
click.echo(f" dependency reference: {dependency_version}")
|
||||
|
||||
warnings = validate_versions(main_version, llama_parse_version, dependency_version)
|
||||
if warnings:
|
||||
click.echo("\nValidation warnings:")
|
||||
for warning in warnings:
|
||||
click.echo(f" ⚠️ {warning}")
|
||||
else:
|
||||
click.echo("\n✅ All versions are consistent")
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.argument("version")
|
||||
def set(version: str) -> None:
|
||||
"""Set version across all pyproject.toml files."""
|
||||
set_version(version)
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option(
|
||||
"--version", help="Version to tag (uses current version if not specified)"
|
||||
)
|
||||
def tag(version: str | None = None) -> None:
|
||||
"""Create and push a git tag for the current version."""
|
||||
if not version:
|
||||
main_version, _, _ = get_current_versions()
|
||||
version = main_version
|
||||
|
||||
create_and_push_tag(version)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
cli()
|
||||
@@ -48,8 +48,8 @@ class TestData(BaseModel):
|
||||
# Skip all tests if API key is not set
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.skipif(
|
||||
not LLAMA_CLOUD_API_KEY or not LLAMA_DEPLOY_DEPLOYMENT_NAME,
|
||||
reason="LLAMA_CLOUD_API_KEY or LLAMA_DEPLOY_DEPLOYMENT_NAME not set",
|
||||
not LLAMA_CLOUD_API_KEY,
|
||||
reason="LLAMA_CLOUD_API_KEY not set",
|
||||
)
|
||||
async def test_agent_data_crud_operations():
|
||||
"""Test basic CRUD operations for agent data with automatic cleanup"""
|
||||
@@ -67,7 +67,7 @@ async def test_agent_data_crud_operations():
|
||||
agent_data_client = AsyncAgentDataClient(
|
||||
client=client,
|
||||
type=TestData,
|
||||
collection_name=f"test-collection-{test_id[:8]}",
|
||||
collection=f"test-collection-{test_id[:8]}",
|
||||
agent_url_id=LLAMA_DEPLOY_DEPLOYMENT_NAME,
|
||||
)
|
||||
|
||||
@@ -77,21 +77,21 @@ async def test_agent_data_crud_operations():
|
||||
created_item = None
|
||||
try:
|
||||
# Test CREATE
|
||||
created_item = await agent_data_client.create_agent_data(test_data)
|
||||
created_item = await agent_data_client.create_item(test_data)
|
||||
assert created_item.data.name == "test-item"
|
||||
assert created_item.data.test_id == test_id
|
||||
assert created_item.data.value == 42
|
||||
assert created_item.id is not None
|
||||
|
||||
# Test READ
|
||||
retrieved_item = await agent_data_client.get_agent_data(created_item.id)
|
||||
retrieved_item = await agent_data_client.get_item(created_item.id)
|
||||
assert retrieved_item.id == created_item.id
|
||||
assert retrieved_item.data.name == "test-item"
|
||||
assert retrieved_item.data.test_id == test_id
|
||||
assert retrieved_item.data.value == 42
|
||||
|
||||
# Test SEARCH
|
||||
search_results = await agent_data_client.search_agent_data(
|
||||
search_results = await agent_data_client.search(
|
||||
filter={"test_id": {"eq": test_id}}, page_size=10, include_total=True
|
||||
)
|
||||
assert len(search_results.items) == 1
|
||||
@@ -99,7 +99,7 @@ async def test_agent_data_crud_operations():
|
||||
assert search_results.total == 1
|
||||
|
||||
# Test AGGREGATE
|
||||
aggregate_results = await agent_data_client.aggregate_agent_data(
|
||||
aggregate_results = await agent_data_client.aggregate(
|
||||
group_by=["test_id"], count=True
|
||||
)
|
||||
assert len(aggregate_results.items) == 1
|
||||
@@ -108,7 +108,7 @@ async def test_agent_data_crud_operations():
|
||||
|
||||
# Test UPDATE
|
||||
updated_data = TestData(name="updated-item", test_id=test_id, value=84)
|
||||
updated_item = await agent_data_client.update_agent_data(
|
||||
updated_item = await agent_data_client.update_item(
|
||||
created_item.id, updated_data
|
||||
)
|
||||
assert updated_item.data.name == "updated-item"
|
||||
@@ -116,7 +116,7 @@ async def test_agent_data_crud_operations():
|
||||
assert updated_item.id == created_item.id
|
||||
|
||||
# Verify update persisted
|
||||
verified_item = await agent_data_client.get_agent_data(created_item.id)
|
||||
verified_item = await agent_data_client.get_item(created_item.id)
|
||||
assert verified_item.data.name == "updated-item"
|
||||
assert verified_item.data.value == 84
|
||||
|
||||
@@ -124,6 +124,6 @@ async def test_agent_data_crud_operations():
|
||||
# Clean up test data
|
||||
if created_item is not None:
|
||||
try:
|
||||
await agent_data_client.delete_agent_data(created_item.id)
|
||||
await agent_data_client.delete_item(created_item.id)
|
||||
except Exception as e:
|
||||
print(f"Warning: Failed to cleanup test data {created_item.id}: {e}")
|
||||
|
||||
@@ -70,7 +70,7 @@ def test_extracted_data_create_method():
|
||||
extracted = ExtractedData.create(person)
|
||||
assert extracted.original_data == person
|
||||
assert extracted.data == person
|
||||
assert extracted.status == "in_review"
|
||||
assert extracted.status == "pending_review"
|
||||
assert extracted.confidence == {}
|
||||
|
||||
# Test with custom values
|
||||
|
||||
@@ -8,6 +8,7 @@ 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
|
||||
from .conftest import register_agent_for_cleanup
|
||||
|
||||
load_test_dotenv()
|
||||
|
||||
@@ -115,13 +116,11 @@ def extraction_agent(test_case: TestCase, extractor: LlamaExtract):
|
||||
|
||||
# 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)}")
|
||||
# Register agent for cleanup at the end of the test session
|
||||
register_agent_for_cleanup(agent.id)
|
||||
|
||||
yield agent
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
@@ -130,7 +129,7 @@ def extraction_agent(test_case: TestCase, extractor: LlamaExtract):
|
||||
)
|
||||
@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
|
||||
result = extraction_agent.extract(test_case.input_file).data # type: ignore
|
||||
with open(test_case.expected_output, "r") as f:
|
||||
expected = json.load(f)
|
||||
# TODO: fix the saas_slide test
|
||||
|
||||
@@ -193,3 +193,12 @@ async def test_multiple_page_markdown(
|
||||
result = await markdown_parser.aload_data(filepath)
|
||||
assert len(result) == expected
|
||||
assert all(len(doc.text) > 0 for doc in result)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_result(markdown_parser: LlamaParse) -> None:
|
||||
filepath = "tests/test_files/attention_is_all_you_need.pdf"
|
||||
expected = await markdown_parser.aparse(filepath)
|
||||
result = await markdown_parser.aget_result(expected.job_id)
|
||||
assert result.job_id == expected.job_id
|
||||
assert len(result.pages) == len(expected.pages)
|
||||
|
||||
@@ -83,6 +83,12 @@ async def test_basic_parse_result(file_path: str, partition_pages: Optional[int]
|
||||
assert image_documents[0].image is not None
|
||||
assert len(image_documents[0].resolve_image().getvalue()) > 0
|
||||
|
||||
assert len(await result.aget_text()) > 0
|
||||
assert len(await result.aget_markdown()) > 0
|
||||
json = await result.aget_json()
|
||||
assert json.get("job_metadata")
|
||||
assert len(json.get("pages", [])) == len(result.pages)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.skip(
|
||||
|
||||
@@ -1,7 +1,30 @@
|
||||
import pytest
|
||||
|
||||
from pathlib import Path
|
||||
from llama_cloud_services.parse.utils import (
|
||||
expand_target_pages,
|
||||
partition_pages,
|
||||
extract_tables_from_json_results,
|
||||
)
|
||||
from typing import List
|
||||
|
||||
from llama_cloud_services.parse.utils import expand_target_pages, partition_pages
|
||||
|
||||
@pytest.fixture()
|
||||
def pseudo_json_results() -> List[dict]:
|
||||
return [
|
||||
{
|
||||
"pages": [
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"type": "table",
|
||||
"csv": "Name,Age,Height (cm)\nAnna,12,140\nBob,22,175\nClaire,33,173\nDenis,44,185\n",
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def test_expand_target_pages() -> None:
|
||||
@@ -28,3 +51,12 @@ def test_partion_pages() -> None:
|
||||
assert result == ["0,2-3", "5,8"]
|
||||
result = list(partition_pages(pages, 3, max_pages=10))
|
||||
assert result == ["0,2-3", "5,8-9", "10"]
|
||||
|
||||
|
||||
def test_table_extraction(pseudo_json_results: List[dict], tmpdir: Path) -> None:
|
||||
tables = extract_tables_from_json_results(pseudo_json_results, tmpdir)
|
||||
assert len(tables) == 1
|
||||
for table in tables:
|
||||
assert Path(table).exists()
|
||||
with open(table) as t:
|
||||
assert t.read() == pseudo_json_results[0]["pages"][0]["items"][0]["csv"]
|
||||
|
||||
Reference in New Issue
Block a user