Compare commits
83 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fd14b5a6e5 | |||
| 15cc7c4070 | |||
| fc709438a7 | |||
| 5801375f8c | |||
| 8a45ba81ba | |||
| e16644f9c1 | |||
| 2e85a0c0c0 | |||
| 89348aa8e5 | |||
| 3ab2ce27b5 | |||
| 265261862f | |||
| 66cf052b8c | |||
| 2ca2d81e58 | |||
| 951ba4dfd8 | |||
| 386d210e8b | |||
| 9321602845 | |||
| 26c06353f0 | |||
| 62cf12d6eb | |||
| 253ee61463 | |||
| 2ccd2a9397 | |||
| c139e8e3e6 | |||
| 6e6e96c422 | |||
| b677e5226d | |||
| df723584b6 | |||
| efe06ffff0 | |||
| 6ba052d58f | |||
| 8cf52058b5 | |||
| 1bae09126c | |||
| bbbae9de9d | |||
| 7cb6d06316 | |||
| bca5492829 | |||
| f6a4d8681f | |||
| fd3836ec95 | |||
| f304c2dc08 | |||
| f13a1a2fc3 | |||
| df1453e30c | |||
| bac204f800 | |||
| dbf24a7daa | |||
| 6a29b1ac96 | |||
| 6c700d9e0f | |||
| ab69e87c2a | |||
| d1f97531dc | |||
| 17ebbca6ea | |||
| 08ddaaaa2f | |||
| 7515fe5f3e | |||
| cd49dae7ed | |||
| 2977f56061 | |||
| 8938286862 | |||
| 7b90d03f28 | |||
| 9dfe4d6d79 | |||
| 3a781a453e | |||
| e23487b1d8 | |||
| ccee75721b | |||
| 0a4147116c | |||
| e58b40b34c | |||
| 0db05b9b96 | |||
| a8a191ae87 | |||
| 4d92775aa8 | |||
| 477847111e | |||
| efbcfb1d2e | |||
| a9b01c761c | |||
| dac2f7c84e | |||
| 478142e509 | |||
| e76d5ba679 | |||
| 23dc9c0f68 | |||
| 8b96176d8a | |||
| 1bbf5f4823 | |||
| 2f57682035 | |||
| 4c05160f98 | |||
| 3e9ad64a7f | |||
| 21fa19c73b | |||
| 58257d546b | |||
| 5e99d810fd | |||
| a2dc717d85 | |||
| 87062e6ca8 | |||
| ae3a21c5ff | |||
| ccebb8a2fa | |||
| 2d21d6e688 | |||
| 270d96b7e3 | |||
| ea21daa96f | |||
| 42845f8d07 | |||
| 8b63ae9c46 | |||
| 173060dc50 | |||
| d19b35cd48 |
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
Write a concise description of what the bug is.
|
||||
|
||||
**Files**
|
||||
If possible, please provide the PDF file causing the issue.
|
||||
|
||||
**Job ID**
|
||||
If you have it, please provide the ID of the job you ran.
|
||||
You can find it here: https://cloud.llamaindex.ai/parse in the "History" tab.
|
||||
|
||||
**Client:**
|
||||
Please remove untested options:
|
||||
- Python Library
|
||||
- API
|
||||
- Frontend (cloud.llamaindex.ai)
|
||||
- Typescript Library
|
||||
- Notebook
|
||||
|
||||
**Additional context**
|
||||
Add any additional context about the problem here.
|
||||
What options did you use? Premium mode, multimodal, fast mode, parsing instructions, etc.
|
||||
Screenshots, code snippets, etc.
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Custom issue
|
||||
about: Not a bug nor a feature request
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.DS_Store
|
||||
.idea
|
||||
|
||||
@@ -72,7 +72,7 @@ repos:
|
||||
args:
|
||||
[
|
||||
"--ignore-words-list",
|
||||
"astroid,gallary,momento,narl,ot,rouge,nin,gere,te,inh",
|
||||
"astroid,gallary,momento,narl,ot,rouge,nin,gere,te,inh,vor",
|
||||
]
|
||||
- repo: https://github.com/srstevenson/nb-clean
|
||||
rev: 3.1.0
|
||||
@@ -84,3 +84,5 @@ repos:
|
||||
hooks:
|
||||
- id: toml-sort-fix
|
||||
exclude: ".*poetry.lock"
|
||||
|
||||
exclude: .github/ISSUE_TEMPLATE
|
||||
|
||||
@@ -1,14 +1,29 @@
|
||||
# LlamaParse
|
||||
|
||||
LlamaParse is an API created by LlamaIndex to efficiently parse and represent files for efficient retrieval and context augmentation using LlamaIndex frameworks.
|
||||
[](https://pypi.org/project/llama-parse/)
|
||||
[](https://github.com/run-llama/llama_parse/graphs/contributors)
|
||||
[](https://discord.gg/dGcwcsnxhU)
|
||||
|
||||
LlamaParse is a **GenAI-native document parser** that can parse complex document data for any downstream LLM use case (RAG, agents).
|
||||
|
||||
It is really good at the following:
|
||||
|
||||
- ✅ **Broad file type support**: Parsing a variety of unstructured file types (.pdf, .pptx, .docx, .xlsx, .html) with text, tables, visual elements, weird layouts, and more.
|
||||
- ✅ **Table recognition**: Parsing embedded tables accurately into text and semi-structured representations.
|
||||
- ✅ **Multimodal parsing and chunking**: Extracting visual elements (images/diagrams) into structured formats and return image chunks using the latest multimodal models.
|
||||
- ✅ **Custom parsing**: Input custom prompt instructions to customize the output the way you want it.
|
||||
|
||||
LlamaParse directly integrates with [LlamaIndex](https://github.com/run-llama/llama_index).
|
||||
|
||||
Free plan is up to 1000 pages a day. Paid plan is free 7k pages per week + 0.3c per additional page.
|
||||
The free plan is up to 1000 pages a day. Paid plan is free 7k pages per week + 0.3c per additional page by default. There is a sandbox available to test the API [**https://cloud.llamaindex.ai/parse ↗**](https://cloud.llamaindex.ai/parse).
|
||||
|
||||
Read below for some quickstart information, or see the [full documentation](https://docs.cloud.llamaindex.ai/).
|
||||
|
||||
If you're a company interested in enterprise RAG solutions, and/or high volume/on-prem usage of LlamaParse, come [talk to us](https://www.llamaindex.ai/contact).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, login and get an api-key from [**https://cloud.llamaindex.ai ↗**](https://cloud.llamaindex.ai).
|
||||
First, login and get an api-key from [**https://cloud.llamaindex.ai/api-key ↗**](https://cloud.llamaindex.ai/api-key).
|
||||
|
||||
Then, make sure you have the latest LlamaIndex version installed.
|
||||
|
||||
@@ -23,7 +38,22 @@ Lastly, install the package:
|
||||
|
||||
`pip install llama-parse`
|
||||
|
||||
Now you can run the following to parse your first PDF file:
|
||||
Now you can parse your first PDF file using the command line interface. Use the command `llama-parse [file_paths]`. See the help text with `llama-parse --help`.
|
||||
|
||||
```bash
|
||||
export LLAMA_CLOUD_API_KEY='llx-...'
|
||||
|
||||
# output as text
|
||||
llama-parse my_file.pdf --result-type text --output-file output.txt
|
||||
|
||||
# output as markdown
|
||||
llama-parse my_file.pdf --result-type markdown --output-file output.md
|
||||
|
||||
# output as raw json
|
||||
llama-parse my_file.pdf --output-raw-json --output-file output.json
|
||||
```
|
||||
|
||||
You can also create simple scripts:
|
||||
|
||||
```python
|
||||
import nest_asyncio
|
||||
@@ -53,6 +83,39 @@ documents = await parser.aload_data("./my_file.pdf")
|
||||
documents = await parser.aload_data(["./my_file1.pdf", "./my_file2.pdf"])
|
||||
```
|
||||
|
||||
## Using with file object
|
||||
|
||||
You can parse a file object directly:
|
||||
|
||||
```python
|
||||
import nest_asyncio
|
||||
|
||||
nest_asyncio.apply()
|
||||
|
||||
from llama_parse import LlamaParse
|
||||
|
||||
parser = LlamaParse(
|
||||
api_key="llx-...", # can also be set in your env as LLAMA_CLOUD_API_KEY
|
||||
result_type="markdown", # "markdown" and "text" are available
|
||||
num_workers=4, # if multiple files passed, split in `num_workers` API calls
|
||||
verbose=True,
|
||||
language="en", # Optionally you can define a language, default=en
|
||||
)
|
||||
|
||||
file_name = "my_file1.pdf"
|
||||
extra_info = {"file_name": file_name}
|
||||
|
||||
with open(f"./{file_name}", "rb") as f:
|
||||
# must provide extra_info with file_name key with passing file object
|
||||
documents = parser.load_data(f, extra_info=extra_info)
|
||||
|
||||
# you can also pass file bytes directly
|
||||
with open(f"./{file_name}", "rb") as f:
|
||||
file_bytes = f.read()
|
||||
# must provide extra_info with file_name key with passing file bytes
|
||||
documents = parser.load_data(file_bytes, extra_info=extra_info)
|
||||
```
|
||||
|
||||
## Using with `SimpleDirectoryReader`
|
||||
|
||||
You can also integrate the parser as the default PDF loader in `SimpleDirectoryReader`:
|
||||
@@ -87,6 +150,16 @@ Several end-to-end indexing examples can be found in the examples folder
|
||||
- [Advanced RAG Example](examples/demo_advanced.ipynb)
|
||||
- [Raw API Usage](examples/demo_api.ipynb)
|
||||
|
||||
## Documentation
|
||||
|
||||
[https://docs.cloud.llamaindex.ai/](https://docs.cloud.llamaindex.ai/)
|
||||
|
||||
## Terms of Service
|
||||
|
||||
See the [Terms of Service Here](./TOS.pdf).
|
||||
|
||||
## Get in Touch (LlamaCloud)
|
||||
|
||||
LlamaParse is part of LlamaCloud, our e2e enterprise RAG platform that provides out-of-the-box, production-ready connectors, indexing, and retrieval over your complex data sources. We offer SaaS and VPC options.
|
||||
|
||||
LlamaCloud is currently available via waitlist (join by [creating an account](https://cloud.llamaindex.ai/)). If you're interested in state-of-the-art quality and in centralizing your RAG efforts, come [get in touch with us](https://www.llamaindex.ai/contact).
|
||||
|
||||
|
After Width: | Height: | Size: 6.9 MiB |
@@ -342,7 +342,7 @@
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "llama-parse-aNC435Vv-py3.10",
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,493 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0db58db5-d4ee-4631-af5b-4fc53eb05170",
|
||||
"metadata": {},
|
||||
"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",
|
||||
"\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"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "5f7d99ad-6ebd-47d0-92a7-566630b0c22a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Setup\n",
|
||||
"\n",
|
||||
"We first setup and load the data. If you haven't already, [download the template](https://eqvista.com/app/uploads/2020/09/Eqvista_DCF-Excel-Template.xlsx) and name it `dcf_template.xlxs` locally."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d867d1a6-cfcf-4f53-952a-f4a6ff2fa205",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install llama-index\n",
|
||||
"%pip install llama-parse"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "103c7983-56d3-45be-b763-d1828d07c43e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import nest_asyncio\n",
|
||||
"\n",
|
||||
"nest_asyncio.apply()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "7b694b56-e04b-4d87-aa37-f0725d6b3adb",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from llama_parse import LlamaParse\n",
|
||||
"\n",
|
||||
"# api_key = \"llx-\" # get from cloud.llamaindex.ai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "9c4693c7-c1c8-47b4-8a8c-25d7e9ef9d2c",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Started parsing the file under job_id cac11eca-d5da-4d46-90e6-321f40e11611\n",
|
||||
"Started parsing the file under job_id cac11eca-5450-4847-9da0-fa6879c4cf3a\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"parser = LlamaParse(\n",
|
||||
" # api_key=api_key, # can also be set in your env as LLAMA_CLOUD_API_KEY\n",
|
||||
" result_type=\"markdown\",\n",
|
||||
")\n",
|
||||
"docs = parser.load_data(\"./dcf_template.xlsx\")\n",
|
||||
"# docs_txt = LlamaParse(result_type=\"text\").load_data(\"./dcf_template.xlsx\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "7302f1c8-e405-4cda-8ff7-1d55185816f7",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"# Cover Page\n",
|
||||
"\n",
|
||||
"|Thank you for downloading our DCF Model excel template. This DCF Model excel template helps you to value your business using Discounted Free Cash Flow or DCF Method. | |\n",
|
||||
"|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n",
|
||||
"| | |\n",
|
||||
"| |Eqvista is an equity management software that allows companies, investors and company shareholders to track, manage, and make intelligent decisions about their companies’ equity.|\n",
|
||||
"| | |\n",
|
||||
"| |GET STARTED- IT'S FREE |\n",
|
||||
"| | |\n",
|
||||
"| |Note: This template is not professional advice and not a substitute for professional advice. |\n",
|
||||
"|Accordingly, before taking any actions based upon such information, we encourage you to consult with the appropriate professionals. | |\n",
|
||||
"| | |\n",
|
||||
"| |@Eqvista Inc. All Rights Reserved |\n",
|
||||
"---\n",
|
||||
"# DCF Model\n",
|
||||
"\n",
|
||||
"|Discounted Cash Flow Excel Template | | | | | | | | | | | |\n",
|
||||
"|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------|-----------|-----------|-----------------------|-----------|-----------------------|--------------|-----------|-----------|-----------|--------------|\n",
|
||||
"| | | | | | | | | | | | |\n",
|
||||
"|Here is a simple discounted cash flow excel template for estimating your company value based on this income valuation approach | | | | | | | | | | | |\n",
|
||||
"| | | | | | | | | | | | |\n",
|
||||
"|Instructions: | | | | | | | | | | | |\n",
|
||||
"|1) Fill out the two assumptions in yellow highlight | | | | | | | | | | | |\n",
|
||||
"|2) Fill in either the 5 year or 3 year weighted average figures in yellow highlight | | | | | | | | | | | |\n",
|
||||
"| | | | | | | | | | | | |\n",
|
||||
"|Assumptions | | | | | | | | | | | |\n",
|
||||
"|Tax Rate |20% | | | | | | | | | | |\n",
|
||||
"|Discount Rate |15% | | | | | | | | | | |\n",
|
||||
"| | | | | | | | | | | | |\n",
|
||||
"|5 Year Weighted Moving Average | | | | | | | | | | | |\n",
|
||||
"|Indication of Company Value |$242,995.43 | | | | | | | | | | |\n",
|
||||
"| | | | | | | | | | | | |\n",
|
||||
"|3 Year Weighted Moving Average | | | | | | | | | | | |\n",
|
||||
"|Indication of Company Value |$158,651.07 | | | | | | | | | | |\n",
|
||||
"| | | | | | | | | | | | |\n",
|
||||
"| |5 Year Weighted Moving Average| | | | | | | | | | |\n",
|
||||
"| |Past Years | | | | |Forecasted Future Years| | | | | |\n",
|
||||
"| |Year 1 |Year 2 |Year 3 |Year 4 |Year 5 |Year 6 |Year 7 |Year 8 |Year 9 |Year 10 |Terminal Value|\n",
|
||||
"|Pre-tax income |50,000.00 |55,000.00 |45,000.00 |52,000.00 |60,000.00 | | | | | | |\n",
|
||||
"|Income Taxes |10,000.00 |11,000.00 |9,000.00 |10,400.00 |12,000.00 | | | | | | |\n",
|
||||
"|Net Income |40,000.00 |44,000.00 |36,000.00 |41,600.00 |48,000.00 | | | | | | |\n",
|
||||
"|Depreciation Expense |5,000.00 |4,000.00 |3,000.00 |2,000.00 |1,000.00 | | | | | | |\n",
|
||||
"|Capital Expenditures |10,000.00 |8,000.00 |5,000.00 |5,000.00 |7,000.00 | | | | | | |\n",
|
||||
"|Debt Repayments |5,000.00 |5,000.00 |5,000.00 |5,000.00 |5,000.00 | | | | | | |\n",
|
||||
"|Net Cash Flow |20,000.00 |27,000.00 |23,000.00 |29,600.00 |35,000.00 |29,093.33 |29,817.78 |30,177.48 |30,469.23 |30,379.74 |287,188.00 |\n",
|
||||
"|Discounting Factor | | | | | |0.8696 |0.7561 |0.6575 |0.5718 |0.4972 |0.4972 |\n",
|
||||
"|Present Value of Future Cash Flow | | | | | |25,298.55 |22,546.52 |19,842.18 |17,420.88 |15,104.10 |142,783.19 |\n",
|
||||
"| | | | | | | | | | | | |\n",
|
||||
"| |3 Year Weighted Moving Average| | | | | | | | | | |\n",
|
||||
"| |Past Years | | |Forecasted Future Years| | | | | | | |\n",
|
||||
"| |Year 1 |Year 2 |Year 3 |Year 4 |Year 5 |Year 6 |Terminal Value| | | | |\n",
|
||||
"|Pre-tax income |50,000.00 |55,000.00 |45,000.00 | | | | | | | | |\n",
|
||||
"|Income Taxes |10,000.00 |11,000.00 |9,000.00 | | | | | | | | |\n",
|
||||
"|Net Income |40,000.00 |44,000.00 |36,000.00 | | | | | | | | |\n",
|
||||
"|Depreciation Expense |5,000.00 |4,000.00 |3,000.00 | | | | | | | | |\n",
|
||||
"|Capital Expenditures |10,000.00 |8,000.00 |5,000.00 | | | | | | | | |\n",
|
||||
"|Debt Repayments |5,000.00 |5,000.00 |5,000.00 | | | | | | | | |\n",
|
||||
"|Net Cash Flow |20,000.00 |27,000.00 |23,000.00 |23,833.33 |24,083.33 |23,819.44 |158,253.59 | | | | |\n",
|
||||
"|Discounting Factor | | | |0.8696 |0.7561 |0.6575 |0.6575 | | | | |\n",
|
||||
"|Present Value of Future Cash Flow | | | |20,724.64 |18,210.46 |15,661.67 |104,054.30 | | | | |\n",
|
||||
"| | | | | | | | | | | | |\n",
|
||||
"|Notes: | | | | | | | | | | | |\n",
|
||||
"|-We based this simple discounted cash flow excel model based on the weighted moving averages (5 year or 3 year) for simplicity, in case a constant growth rate cannot be easily determined.| | | | | | | | | | | |\n",
|
||||
"|-The factors such as Depreciation Expense, Capital Expense and Debt Repayments remain constant, so consider this when looking at the forecasted figures. | | | | | | | | | | | |\n",
|
||||
"|-For the terminal value constant growth rate, we make the assumption of the growth from the last forecasted year compared to the first forecasted year. Adjust in the formula as needed. | | | | | | | | | | | |\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(docs[0].get_content())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1aedd4bb-7939-4fbc-8f07-d362e24d9772",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Configure LLM, Setup Basic Summary Engine\n",
|
||||
"\n",
|
||||
"We setup a basic summary engine which retrieves the entire document as context to put into the prompt."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "f7c056a8-d098-4ebe-9341-d9f07081067c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from llama_index.llms.openai import OpenAI\n",
|
||||
"from llama_index.core import Settings\n",
|
||||
"\n",
|
||||
"llm = OpenAI(model=\"gpt-4-turbo-preview\")\n",
|
||||
"Settings.llm = llm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "c0fa2630-ee1b-4ce7-91e9-f9ffff8347f9",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from llama_index.core import SummaryIndex\n",
|
||||
"\n",
|
||||
"index = SummaryIndex.from_documents(docs)\n",
|
||||
"# index = SummaryIndex.from_documents(docs_txt)\n",
|
||||
"\n",
|
||||
"query_engine = index.as_query_engine()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1d39a075-46b8-4dcb-8aee-abd10343bedd",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Define Baseline\n",
|
||||
"\n",
|
||||
"Let's define a baseline query engine over this data, using a naive parser (our PandasExcelReader, available on LlamaHub)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "632f918e-7811-4931-8a5f-4aa4850718db",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Collecting openpyxl\n",
|
||||
" Downloading openpyxl-3.1.3-py2.py3-none-any.whl (251 kB)\n",
|
||||
"\u001b[2K \u001b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m251.3/251.3 kB\u001b[0m \u001b[31m5.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n",
|
||||
"\u001b[?25hCollecting et-xmlfile\n",
|
||||
" Using cached et_xmlfile-1.1.0-py3-none-any.whl (4.7 kB)\n",
|
||||
"Installing collected packages: et-xmlfile, openpyxl\n",
|
||||
"Successfully installed et-xmlfile-1.1.0 openpyxl-3.1.3\n",
|
||||
"\n",
|
||||
"\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip available: \u001b[0m\u001b[31;49m22.2.2\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.0\u001b[0m\n",
|
||||
"\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"!pip install llama-index-readers-file\n",
|
||||
"!pip install openpyxl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "85ff09fd-8a99-4aa4-8182-8d0cf30f7b85",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from llama_index.readers.file import PandasExcelReader\n",
|
||||
"import importlib\n",
|
||||
"from pathlib import Path\n",
|
||||
"\n",
|
||||
"base_reader = PandasExcelReader()\n",
|
||||
"base_docs = base_reader.load_data(Path(\"dcf_template.xlsx\"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ba45f806-58be-4f57-bf42-2721555136cb",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Discounted Cash Flow Excel Template \n",
|
||||
" \n",
|
||||
"Here is a simple discounted cash flow excel template for estimating your company value based on this income valuation approach \n",
|
||||
" \n",
|
||||
"Instructions: \n",
|
||||
"1) Fill out the two assumptions in yellow highlight \n",
|
||||
"2) Fill in either the 5 year or 3 year weighted average figures in yellow highlight \n",
|
||||
" \n",
|
||||
" \n",
|
||||
" \n",
|
||||
" \n",
|
||||
"Assumptions \n",
|
||||
"Tax Rate 0.2 \n",
|
||||
"Discount Rate 0.15 \n",
|
||||
" \n",
|
||||
"5 Year Weighted Moving Average \n",
|
||||
"Indication of Company Value 242995.4347636059 \n",
|
||||
" \n",
|
||||
"3 Year Weighted Moving Average \n",
|
||||
"Indication of Company Value 158651.0723286644 \n",
|
||||
" \n",
|
||||
" 5 Year Weighted Moving Average \n",
|
||||
" Past Years Forecasted Future Years \n",
|
||||
" Year 1 Year 2 Year 3 Year 4 Year 5 Year 6 Year 7 Year 8 Year 9 Year 10 Terminal Value\n",
|
||||
"Pre-tax income 50000 55000 45000 52000 60000 \n",
|
||||
"Income Taxes 10000 11000 9000 10400 12000 \n",
|
||||
"Net Income 40000 44000 36000 41600 48000 \n",
|
||||
"Depreciation Expense 5000 4000 3000 2000 1000 \n",
|
||||
"Capital Expenditures 10000 8000 5000 5000 7000 \n",
|
||||
"Debt Repayments 5000 5000 5000 5000 5000 \n",
|
||||
"Net Cash Flow 20000 27000 23000 29600 35000 29093.333333333332 29817.777777777774 30177.481481481478 30469.234567901232 30379.73991769547 287188.0007003137\n",
|
||||
"Discounting Factor 0.8695652173913044 0.7561436672967865 0.6575162324319883 0.5717532455930334 0.4971767352982899 0.4971767352982899\n",
|
||||
"Present Value of Future Cash Flow 25298.550724637684 22546.523839529513 19842.183927989798 17420.883754932976 15104.099911490972 142783.19260502496\n",
|
||||
" \n",
|
||||
" \n",
|
||||
" 3 Year Weighted Moving Average \n",
|
||||
" Past Years Forecasted Future Years \n",
|
||||
" Year 1 Year 2 Year 3 Year 4 Year 5 Year 6 Terminal Value \n",
|
||||
"Pre-tax income 50000 55000 45000 \n",
|
||||
"Income Taxes 10000 11000 9000 \n",
|
||||
"Net Income 40000 44000 36000 \n",
|
||||
"Depreciation Expense 5000 4000 3000 \n",
|
||||
"Capital Expenditures 10000 8000 5000 \n",
|
||||
"Debt Repayments 5000 5000 5000 \n",
|
||||
"Net Cash Flow 20000 27000 23000 23833.333333333332 24083.333333333332 23819.44444444444 158253.58851674633 \n",
|
||||
"Discounting Factor 0.8695652173913044 0.7561436672967865 0.6575162324319883 0.6575162324319883 \n",
|
||||
"Present Value of Future Cash Flow 20724.63768115942 18210.459987397608 15661.671369734164 104054.30329037321 \n",
|
||||
" \n",
|
||||
" \n",
|
||||
"Notes: \n",
|
||||
"-We based this simple discounted cash flow excel model based on the weighted moving averages (5 year or 3 year) for simplicity, in case a constant growth rate cannot be easily determined. \n",
|
||||
"-The factors such as Depreciation Expense, Capital Expense and Debt Repayments remain constant, so consider this when looking at the forecasted figures. \n",
|
||||
"-For the terminal value constant growth rate, we make the assumption of the growth from the last forecasted year compared to the first forecasted year. Adjust in the formula as needed. \n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(base_docs[1].get_content())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ff6e812f-fa94-4b0f-8907-ee70983e53f1",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from llama_index.core import SummaryIndex\n",
|
||||
"\n",
|
||||
"base_index = SummaryIndex.from_documents([base_docs[1]])\n",
|
||||
"\n",
|
||||
"base_query_engine = base_index.as_query_engine()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "fa75f1bc-6fed-4721-ba5e-dc5408395618",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Ask Questions over this Data\n",
|
||||
"\n",
|
||||
"Let's now ask questions over this data, using both the LlamaParse-powered pipeline and naive pipeline."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "a875a20e-a6b6-46b7-80d4-614546215ffc",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"query_str = \"Tell me about the income taxes in the past years (year 3-5) for the 5 year WMA table\"\n",
|
||||
"response = query_engine.query(query_str)\n",
|
||||
"base_response = base_query_engine.query(query_str)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "06b0b072-f159-47c4-9cad-9f0cc0d56b28",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"******* LlamaParse RAG *******\n",
|
||||
"The income taxes in the past years (year 3 to 5) for the 5-year Weighted Moving Average table were $9,000.00 in Year 3, $10,400.00 in Year 4, and $12,000.00 in Year 5.\n",
|
||||
"******* Naive RAG *******\n",
|
||||
"The income taxes in the past years (year 3-5) for the 5 year WMA table were $9,000, $10,400, and $12,000, respectively.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(\"******* LlamaParse RAG *******\")\n",
|
||||
"print(str(response))\n",
|
||||
"print(\"******* Naive RAG *******\")\n",
|
||||
"print(str(base_response))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "8bd0998f-4f7f-46f9-9b51-cfb510f384ee",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(response.source_nodes[0].get_content())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "7a93af5f-fcea-4f14-80eb-5dfad230cd8a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"query_str = \"Tell me about the discounting factors in year 5 for the 3 year WMA\"\n",
|
||||
"response = query_engine.query(query_str)\n",
|
||||
"base_response = base_query_engine.query(query_str)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "c6d3a5fb-c32c-4dea-8f2e-956af85456a4",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"******* LlamaParse RAG *******\n",
|
||||
"The discounting factor in year 5 for the 3-year Weighted Moving Average (WMA) is 0.7561.\n",
|
||||
"******* Naive RAG *******\n",
|
||||
"The discounting factor in year 5 for the 3-year Weighted Moving Average is 0.6575162324319883.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(\"******* LlamaParse RAG *******\")\n",
|
||||
"print(str(response))\n",
|
||||
"print(\"******* Naive RAG *******\")\n",
|
||||
"print(str(base_response))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "b96f3a9b-6e99-4192-b6d6-447319d3c4fa",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"query_str = \"Tell me about the projected net cash flow in years 7-9 for the 5 year WMA\"\n",
|
||||
"response = query_engine.query(query_str)\n",
|
||||
"base_response = base_query_engine.query(query_str)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "92b419b9-25ee-4d69-98d9-56c0a45b24af",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"******* LlamaParse RAG *******\n",
|
||||
"The projected net cash flow for years 7 to 9 in the 5-year Weighted Moving Average scenario is as follows: Year 7 is $29,817.78, Year 8 is $30,177.48, and Year 9 is $30,469.23.\n",
|
||||
"******* Naive RAG *******\n",
|
||||
"The projected net cash flow for years 7 to 9 in the 5-year weighted moving average scenario is as follows: Year 7 is $29,093.33, Year 8 is $29,817.78, and Year 9 is $30,177.48.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(\"******* LlamaParse RAG *******\")\n",
|
||||
"print(str(response))\n",
|
||||
"print(\"******* Naive RAG *******\")\n",
|
||||
"print(str(base_response))"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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
|
||||
}
|
||||
|
After Width: | Height: | Size: 195 KiB |
|
After Width: | Height: | Size: 363 KiB |
|
After Width: | Height: | Size: 343 KiB |
|
After Width: | Height: | Size: 185 KiB |
|
After Width: | Height: | Size: 254 KiB |
|
After Width: | Height: | Size: 650 KiB |
|
After Width: | Height: | Size: 334 KiB |
@@ -0,0 +1,635 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "97c79c38-38a3-40f3-ba2e-250649347d63",
|
||||
"metadata": {},
|
||||
"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",
|
||||
"\n",
|
||||
"This cookbook shows you how to use LlamaParse to parse any document with the multimodal capabilities of Sonnet 3.5. \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. Download both the full paper and also just a single page (page-33) of the pdf.\n",
|
||||
"\n",
|
||||
"Swap in `data/llama2-p33.pdf` for `data/llama2.pdf` in the code blocks below if you want to save on parsing tokens. \n",
|
||||
"\n",
|
||||
"An image of this page is shown below."
|
||||
]
|
||||
},
|
||||
{
|
||||
"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": [
|
||||
"--2024-07-11 23:44:38-- https://arxiv.org/pdf/2307.09288\n",
|
||||
"Resolving arxiv.org (arxiv.org)... 151.101.195.42, 151.101.131.42, 151.101.3.42, ...\n",
|
||||
"Connecting to arxiv.org (arxiv.org)|151.101.195.42|:443... connected.\n",
|
||||
"HTTP request sent, awaiting response... 200 OK\n",
|
||||
"Length: 13661300 (13M) [application/pdf]\n",
|
||||
"Saving to: ‘data/llama2.pdf’\n",
|
||||
"\n",
|
||||
"data/llama2.pdf 100%[===================>] 13.03M 69.3MB/s in 0.2s \n",
|
||||
"\n",
|
||||
"2024-07-11 23:44:38 (69.3 MB/s) - ‘data/llama2.pdf’ saved [13661300/13661300]\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"!wget \"https://arxiv.org/pdf/2307.09288\" -O data/llama2.pdf\n",
|
||||
"!wget \"https://www.dropbox.com/scl/fi/wpql661uu98vf6e2of2i0/llama2-p33.pdf?rlkey=64weubzkwpmf73y58vbmc8pyi&st=khgx5161&dl=1\" -O data/llama2-p33.pdf"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "b5c214a2-56fd-4b09-93b3-be994a3b5aa4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "4e29a9d7-5bd9-4fb8-8ec1-4c128a748662",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Initialize LlamaParse\n",
|
||||
"\n",
|
||||
"Initialize LlamaParse in multimodal mode, and specify the vendor.\n",
|
||||
"\n",
|
||||
"**NOTE**: optionally you can specify the Anthropic API key. If you do so you will be charged our base LlamaParse price of 0.3c per page. If you don't then you will be charged 6c per page, as we will make the calls to Claude for you."
|
||||
]
|
||||
},
|
||||
{
|
||||
"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 811a29d8-8bcd-4100-bee3-6a83fbde1697\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from llama_parse import LlamaParse\n",
|
||||
"\n",
|
||||
"parser = LlamaParse(\n",
|
||||
" result_type=\"markdown\",\n",
|
||||
" use_vendor_multimodal_model=True,\n",
|
||||
" vendor_multimodal_model_name=\"anthropic-sonnet-3.5\",\n",
|
||||
" # invalidate_cache=True\n",
|
||||
")\n",
|
||||
"json_objs = parser.get_json_result(\"./data/llama2.pdf\")\n",
|
||||
"# json_objs = parser.get_json_result(\"./data/llama2-p33.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.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.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 (3c 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 04c69ecc-e45d-4ad9-ba72-3045af38268b\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",
|
||||
")\n",
|
||||
"json_objs_gpt4o = parser_gpt4o.get_json_result(\"./data/llama2.pdf\")\n",
|
||||
"# json_objs_gpt4o = parser.get_json_result(\"./data/llama2-p33.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 along with the original document page.\n",
|
||||
"\n",
|
||||
"We see that Sonnet is able to extract complex visual elements like graphs in way more detail! \n",
|
||||
"\n",
|
||||
"**NOTE**: If you're using llama2-p33, just use `docs[0]`"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "778698aa-da7e-4081-b3b5-0372f228536f",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"page: 33\n",
|
||||
"\n",
|
||||
"| Temperature | RLHF v3 | RLHF v2 | RLHF v1 | SFT |\n",
|
||||
"|-------------|---------|---------|---------|-----|\n",
|
||||
"| 0.4 | 98 | 98 | 97 | 95 |\n",
|
||||
"| 0.6 | 97 | 97 | 95 | 94 |\n",
|
||||
"| 0.8 | 97 | 96 | 94 | 92 |\n",
|
||||
"| 1.0 | 96 | 94 | 92 | 89 |\n",
|
||||
"| 1.2 | 95 | 92 | 88 | 83 |\n",
|
||||
"| 1.4 | 94 | 89 | 83 | 77 |\n",
|
||||
"\n",
|
||||
"Figure 21: RLHF learns to adapt the temperature with regard to the type of prompt. Lower Self-BLEU corresponds to more diversity: RLHF eliminates diversity in responses to factual prompts but retains more diversity when generating responses to creative prompts. We prompt each model with a diverse set of 10 creative and 10 factual instructions and sample 25 responses. This is repeated for the temperatures T ∈ {k/10 | k ∈ N : 1 ≤ k ≤ 15}. For each of the 25 responses we compute the Self-BLEU metric and report the mean and standard deviation against the temperature.\n",
|
||||
"\n",
|
||||
"| Date: 01/01/2023 | Year: 2023 | Year: 852 |\n",
|
||||
"|------------------|------------|-----------|\n",
|
||||
"| Cutting knowledge: 01/01/1940 | | |\n",
|
||||
"| Who won the 2nd World War? | Is the Earth flat or round? | Is the Earth flat or round? |\n",
|
||||
"| I'm not sure what you're referring to, as my knowledge stopped in 1940. | The Earth is round. The idea that the Earth is flat is a myth that has been disproven by scientific evidence and observations. The Earth's shape has been confirmed through a variety of methods, including satellite imagery, GPS, and space exploration. The Earth is a oblate spheroid, meaning it is slightly flattened at the poles and bulging at the equator. | In 852, the Earth was believed to be flat by many people. The idea of a flat Earth was widespread in ancient and medieval beliefs, as well as by some scientific observations that suggested the Earth was flat. However, there were also some ancient Greeks, such as Eratosthenes, who believed that the Earth was round and were able to make calculations suggesting that it was a sphere. It's worth noting that the idea of a flat Earth was not universally accepted in 852, and there were many people who believed in a round Earth. Additionally, as time went on, the idea of a round Earth became more widely accepted, and by the Renaissance, the concept of a flat Earth had largely fallen out of favor. |\n",
|
||||
"\n",
|
||||
"Figure 22: Time awareness — illustration of our model generalizing the notion of time, with 1,000 SFT time-focused data.\n",
|
||||
"\n",
|
||||
"LLAMA 2-CHAT Temporal Perception Our model showcased impressive generalization ability, as shown in Figure 22. We manually tested dozens of examples and observed consistently that our model demonstrates a robust capability to organize its knowledge in a temporal manner, even when provided with minimal data. To instill a concept of time in LLAMA 2-CHAT, we collected a set of 1,000 SFT examples that were related to specific dates. These examples included questions like \"How long ago did Barack Obama become president?\" Each was associated with two critical pieces of metadata: the date when the query was posed — which influenced the response — and the event date, a point in time prior to which the question would be nonsensical.\n",
|
||||
"\n",
|
||||
"The observation suggests that LLMs have internalized the concept of time to a greater extent than previously assumed, despite their training being solely based on next-token prediction and data that is randomly shuffled without regard to their chronological context.\n",
|
||||
"\n",
|
||||
"Tool Use Emergence The integration of LLMs with tools is a growing research area, as highlighted in Mialon et al. (2023). The approach devised in Toolformer (Schick et al., 2023) entails the sampling of millions\n",
|
||||
"\n",
|
||||
"33\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# using Sonnet-3.5\n",
|
||||
"print(docs[32].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: 33\n",
|
||||
"\n",
|
||||
"# Figure 21: RLHF learns to adapt the temperature with regard to the type of prompt.\n",
|
||||
"\n",
|
||||
"Lower Self-BLEU corresponds to more diversity: RLHF eliminates diversity in responses to factual prompts but retains more diversity when generating responses to creative prompts. We prompt each model with a diverse set of 10 creative and 10 factual instructions and sample 25 responses. This is repeated for the temperatures \\( T \\in \\{k/10 | k \\in \\{1:1:15\\}\\). For each of the 25 responses we compute the Self-BLEU metric and report the mean and standard deviation against the temperature.\n",
|
||||
"\n",
|
||||
"| Temperature | Factual Prompts | Creative Prompts |\n",
|
||||
"|-------------|-----------------|------------------|\n",
|
||||
"| 0.4 | | |\n",
|
||||
"| 0.6 | | |\n",
|
||||
"| 0.8 | | |\n",
|
||||
"| 1.0 | | |\n",
|
||||
"| 1.2 | | |\n",
|
||||
"| 1.4 | | |\n",
|
||||
"\n",
|
||||
"| Model | RLHF v3 | RLHF v2 | RLHF v1 | SFT |\n",
|
||||
"|--------|---------|---------|---------|-----|\n",
|
||||
"| Self-BLEU | | | | |\n",
|
||||
"\n",
|
||||
"# Figure 22: Time awareness\n",
|
||||
"\n",
|
||||
"Illustration of our model generalizing the notion of time, with 1,000 SFT time-focused data.\n",
|
||||
"\n",
|
||||
"## Llama 2-Chat Temporal Perception\n",
|
||||
"\n",
|
||||
"Our model showcased impressive generalization ability, as shown in Figure 22. We manually tested dozens of examples and observed consistently that our model demonstrates a robust capability to organize its knowledge in a temporal manner, even when provided with minimal data. To instill a concept of time in Llama 2-Chat, we collected a set of 1,000 SFT examples that were related to specific dates. These examples included questions like \"How long ago did Barack Obama become president?\" Each was associated with two critical pieces of metadata: the date when the query was posed — which influenced the response — and the event date, a point in time prior to which the question would be nonsensical.\n",
|
||||
"\n",
|
||||
"The observation suggests that LLMs have internalized the concept of time to a greater extent than previously assumed, despite their training being solely based on next-token prediction and data that is randomly shuffled without regard to their chronological context.\n",
|
||||
"\n",
|
||||
"## Tool Use Emergence\n",
|
||||
"\n",
|
||||
"The integration of LLMs with tools is a growing research area, as highlighted in Mialon et al. (2023). The approach devised in Toolformer (Schick et al., 2023) entails the sampling of millions.\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"### Example Prompts and Responses\n",
|
||||
"\n",
|
||||
"| Date: 01/01/2023 | Year: 2023 | Year: 852 |\n",
|
||||
"|------------------|------------|-----------|\n",
|
||||
"| **Who won the 2nd World War?** | **Is the Earth flat or round?** | **Is the Earth flat or round?** |\n",
|
||||
"| I'm not sure what you're referring to, as my knowledge stopped in 1940. | The Earth is round. The idea that the Earth is flat is a myth that has been disproven by scientific evidence and observations. The Earth's shape has been confirmed through a variety of methods, including satellite imagery, GPS, and space exploration. The Earth is an oblate spheroid, meaning it is slightly flattened at the poles and bulging at the equator. | In 852, the Earth was believed to be flat by many people. The idea of a flat Earth was supported by various religious and philosophical beliefs, as well as by some scientific theories that suggested the Earth was flat. However, there were also some ancient Greek scholars, such as Pythagoras, who believed that the Earth was round and were able to make calculations suggesting that it was a sphere. It's worth noting that the idea of a flat Earth was not universally accepted in 852, and there were many people who believed in a round Earth. Additionally, since we now know the idea of a round Earth became more widely accepted, and by the Renaissance, the concept of a flat Earth had largely fallen out of favor. |\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"Page 33\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# using GPT-4o\n",
|
||||
"print(docs_gpt4o[32].get_content(metadata_mode=\"all\"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "705f7729-fa0f-4ca0-8562-c42afeaa8532",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Setup RAG Pipeline\n",
|
||||
"\n",
|
||||
"These parsing capabilities translate to great RAG performance as well. Let's setup a RAG pipeline over this data.\n",
|
||||
"\n",
|
||||
"(we'll use GPT-4o from OpenAI 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=\"gpt-4o\")\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 = \"Tell me more about all the values for each line in the 'RLHF learns to adapt the temperature with regard to the type of prompt' graph \"\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 graph titled \"RLHF learns to adapt the temperature with regard to the type of prompt\" presents values for different temperatures across various versions of RLHF and SFT. The values are as follows:\n",
|
||||
"\n",
|
||||
"- **Temperature 0.4:**\n",
|
||||
" - RLHF v3: 98\n",
|
||||
" - RLHF v2: 98\n",
|
||||
" - RLHF v1: 97\n",
|
||||
" - SFT: 95\n",
|
||||
"\n",
|
||||
"- **Temperature 0.6:**\n",
|
||||
" - RLHF v3: 97\n",
|
||||
" - RLHF v2: 97\n",
|
||||
" - RLHF v1: 95\n",
|
||||
" - SFT: 94\n",
|
||||
"\n",
|
||||
"- **Temperature 0.8:**\n",
|
||||
" - RLHF v3: 97\n",
|
||||
" - RLHF v2: 96\n",
|
||||
" - RLHF v1: 94\n",
|
||||
" - SFT: 92\n",
|
||||
"\n",
|
||||
"- **Temperature 1.0:**\n",
|
||||
" - RLHF v3: 96\n",
|
||||
" - RLHF v2: 94\n",
|
||||
" - RLHF v1: 92\n",
|
||||
" - SFT: 89\n",
|
||||
"\n",
|
||||
"- **Temperature 1.2:**\n",
|
||||
" - RLHF v3: 95\n",
|
||||
" - RLHF v2: 92\n",
|
||||
" - RLHF v1: 88\n",
|
||||
" - SFT: 83\n",
|
||||
"\n",
|
||||
"- **Temperature 1.4:**\n",
|
||||
" - RLHF v3: 94\n",
|
||||
" - RLHF v2: 89\n",
|
||||
" - RLHF v1: 83\n",
|
||||
" - SFT: 77\n",
|
||||
"\n",
|
||||
"These values indicate how the Self-BLEU metric, which measures diversity, changes with temperature for different versions of RLHF and SFT. Lower Self-BLEU corresponds to more diversity in the responses.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(response)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "7bee8167-f021-4c87-8d28-9f40a4f7b69d",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"| Temperature | RLHF v3 | RLHF v2 | RLHF v1 | SFT |\n",
|
||||
"|-------------|---------|---------|---------|-----|\n",
|
||||
"| 0.4 | 98 | 98 | 97 | 95 |\n",
|
||||
"| 0.6 | 97 | 97 | 95 | 94 |\n",
|
||||
"| 0.8 | 97 | 96 | 94 | 92 |\n",
|
||||
"| 1.0 | 96 | 94 | 92 | 89 |\n",
|
||||
"| 1.2 | 95 | 92 | 88 | 83 |\n",
|
||||
"| 1.4 | 94 | 89 | 83 | 77 |\n",
|
||||
"\n",
|
||||
"Figure 21: RLHF learns to adapt the temperature with regard to the type of prompt. Lower Self-BLEU corresponds to more diversity: RLHF eliminates diversity in responses to factual prompts but retains more diversity when generating responses to creative prompts. We prompt each model with a diverse set of 10 creative and 10 factual instructions and sample 25 responses. This is repeated for the temperatures T ∈ {k/10 | k ∈ N : 1 ≤ k ≤ 15}. For each of the 25 responses we compute the Self-BLEU metric and report the mean and standard deviation against the temperature.\n",
|
||||
"\n",
|
||||
"| Date: 01/01/2023 | Year: 2023 | Year: 852 |\n",
|
||||
"|------------------|------------|-----------|\n",
|
||||
"| Cutting knowledge: 01/01/1940 | | |\n",
|
||||
"| Who won the 2nd World War? | Is the Earth flat or round? | Is the Earth flat or round? |\n",
|
||||
"| I'm not sure what you're referring to, as my knowledge stopped in 1940. | The Earth is round. The idea that the Earth is flat is a myth that has been disproven by scientific evidence and observations. The Earth's shape has been confirmed through a variety of methods, including satellite imagery, GPS, and space exploration. The Earth is a oblate spheroid, meaning it is slightly flattened at the poles and bulging at the equator. | In 852, the Earth was believed to be flat by many people. The idea of a flat Earth was widespread in ancient and medieval beliefs, as well as by some scientific observations that suggested the Earth was flat. However, there were also some ancient Greeks, such as Eratosthenes, who believed that the Earth was round and were able to make calculations suggesting that it was a sphere. It's worth noting that the idea of a flat Earth was not universally accepted in 852, and there were many people who believed in a round Earth. Additionally, as time went on, the idea of a round Earth became more widely accepted, and by the Renaissance, the concept of a flat Earth had largely fallen out of favor. |\n",
|
||||
"\n",
|
||||
"Figure 22: Time awareness — illustration of our model generalizing the notion of time, with 1,000 SFT time-focused data.\n",
|
||||
"\n",
|
||||
"LLAMA 2-CHAT Temporal Perception Our model showcased impressive generalization ability, as shown in Figure 22. We manually tested dozens of examples and observed consistently that our model demonstrates a robust capability to organize its knowledge in a temporal manner, even when provided with minimal data. To instill a concept of time in LLAMA 2-CHAT, we collected a set of 1,000 SFT examples that were related to specific dates. These examples included questions like \"How long ago did Barack Obama become president?\" Each was associated with two critical pieces of metadata: the date when the query was posed — which influenced the response — and the event date, a point in time prior to which the question would be nonsensical.\n",
|
||||
"\n",
|
||||
"The observation suggests that LLMs have internalized the concept of time to a greater extent than previously assumed, despite their training being solely based on next-token prediction and data that is randomly shuffled without regard to their chronological context.\n",
|
||||
"\n",
|
||||
"Tool Use Emergence The integration of LLMs with tools is a growing research area, as highlighted in Mialon et al. (2023). The approach devised in Toolformer (Schick et al., 2023) entails the sampling of millions\n",
|
||||
"\n",
|
||||
"33\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(response.source_nodes[4].get_content())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "5f9fef7f-510b-46a5-8716-f5616f542035",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"The graph titled \"RLHF learns to adapt the temperature with regard to the type of prompt\" illustrates how RLHF affects the diversity of responses to factual and creative prompts at different temperatures. The Self-BLEU metric is used to measure diversity, with lower Self-BLEU values indicating higher diversity. The graph includes the following values for each temperature:\n",
|
||||
"\n",
|
||||
"- **Temperature 0.4**: Values for factual and creative prompts are not provided.\n",
|
||||
"- **Temperature 0.6**: Values for factual and creative prompts are not provided.\n",
|
||||
"- **Temperature 0.8**: Values for factual and creative prompts are not provided.\n",
|
||||
"- **Temperature 1.0**: Values for factual and creative prompts are not provided.\n",
|
||||
"- **Temperature 1.2**: Values for factual and creative prompts are not provided.\n",
|
||||
"- **Temperature 1.4**: Values for factual and creative prompts are not provided.\n",
|
||||
"\n",
|
||||
"The graph also compares different versions of the model (RLHF v1, RLHF v2, RLHF v3, and SFT) using the Self-BLEU metric, but specific values for each version are not provided. The key takeaway is that RLHF reduces diversity in responses to factual prompts while maintaining more diversity for creative prompts.\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": [
|
||||
"# Figure 21: RLHF learns to adapt the temperature with regard to the type of prompt.\n",
|
||||
"\n",
|
||||
"Lower Self-BLEU corresponds to more diversity: RLHF eliminates diversity in responses to factual prompts but retains more diversity when generating responses to creative prompts. We prompt each model with a diverse set of 10 creative and 10 factual instructions and sample 25 responses. This is repeated for the temperatures \\( T \\in \\{k/10 | k \\in \\{1:1:15\\}\\). For each of the 25 responses we compute the Self-BLEU metric and report the mean and standard deviation against the temperature.\n",
|
||||
"\n",
|
||||
"| Temperature | Factual Prompts | Creative Prompts |\n",
|
||||
"|-------------|-----------------|------------------|\n",
|
||||
"| 0.4 | | |\n",
|
||||
"| 0.6 | | |\n",
|
||||
"| 0.8 | | |\n",
|
||||
"| 1.0 | | |\n",
|
||||
"| 1.2 | | |\n",
|
||||
"| 1.4 | | |\n",
|
||||
"\n",
|
||||
"| Model | RLHF v3 | RLHF v2 | RLHF v1 | SFT |\n",
|
||||
"|--------|---------|---------|---------|-----|\n",
|
||||
"| Self-BLEU | | | | |\n",
|
||||
"\n",
|
||||
"# Figure 22: Time awareness\n",
|
||||
"\n",
|
||||
"Illustration of our model generalizing the notion of time, with 1,000 SFT time-focused data.\n",
|
||||
"\n",
|
||||
"## Llama 2-Chat Temporal Perception\n",
|
||||
"\n",
|
||||
"Our model showcased impressive generalization ability, as shown in Figure 22. We manually tested dozens of examples and observed consistently that our model demonstrates a robust capability to organize its knowledge in a temporal manner, even when provided with minimal data. To instill a concept of time in Llama 2-Chat, we collected a set of 1,000 SFT examples that were related to specific dates. These examples included questions like \"How long ago did Barack Obama become president?\" Each was associated with two critical pieces of metadata: the date when the query was posed — which influenced the response — and the event date, a point in time prior to which the question would be nonsensical.\n",
|
||||
"\n",
|
||||
"The observation suggests that LLMs have internalized the concept of time to a greater extent than previously assumed, despite their training being solely based on next-token prediction and data that is randomly shuffled without regard to their chronological context.\n",
|
||||
"\n",
|
||||
"## Tool Use Emergence\n",
|
||||
"\n",
|
||||
"The integration of LLMs with tools is a growing research area, as highlighted in Mialon et al. (2023). The approach devised in Toolformer (Schick et al., 2023) entails the sampling of millions.\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"### Example Prompts and Responses\n",
|
||||
"\n",
|
||||
"| Date: 01/01/2023 | Year: 2023 | Year: 852 |\n",
|
||||
"|------------------|------------|-----------|\n",
|
||||
"| **Who won the 2nd World War?** | **Is the Earth flat or round?** | **Is the Earth flat or round?** |\n",
|
||||
"| I'm not sure what you're referring to, as my knowledge stopped in 1940. | The Earth is round. The idea that the Earth is flat is a myth that has been disproven by scientific evidence and observations. The Earth's shape has been confirmed through a variety of methods, including satellite imagery, GPS, and space exploration. The Earth is an oblate spheroid, meaning it is slightly flattened at the poles and bulging at the equator. | In 852, the Earth was believed to be flat by many people. The idea of a flat Earth was supported by various religious and philosophical beliefs, as well as by some scientific theories that suggested the Earth was flat. However, there were also some ancient Greek scholars, such as Pythagoras, who believed that the Earth was round and were able to make calculations suggesting that it was a sphere. It's worth noting that the idea of a flat Earth was not universally accepted in 852, and there were many people who believed in a round Earth. Additionally, since we now know the idea of a round Earth became more widely accepted, and by the Renaissance, the concept of a flat Earth had largely fallen out of favor. |\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"Page 33\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(response_gpt4o.source_nodes[4].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
|
||||
}
|
||||
@@ -0,0 +1,560 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "97c79c38-38a3-40f3-ba2e-250649347d63",
|
||||
"metadata": {},
|
||||
"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",
|
||||
"\n",
|
||||
"This cookbook shows you how to use LlamaParse to parse any document with the multimodal capabilities of GPT4o-mini.\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 - the blog post from Meta on Llama3.1, in PDF form."
|
||||
]
|
||||
},
|
||||
{
|
||||
"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": [],
|
||||
"source": [
|
||||
"!wget \"https://www.dropbox.com/scl/fi/8iu23epvv3473im5rq19g/llama3.1_blog.pdf?rlkey=5u417tbdox4aip33fdubvni56&st=dzozd11e&dl=1\" -O \"data/llama3.1_blog.pdf\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c70d420d-1778-4b0d-81e2-db09276e90cf",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "4e29a9d7-5bd9-4fb8-8ec1-4c128a748662",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Initialize LlamaParse\n",
|
||||
"\n",
|
||||
"Initialize LlamaParse in multimodal mode, and specify the vendor.\n",
|
||||
"\n",
|
||||
"**NOTE**: optionally you can specify the OpenAI API key. If you do so you will be charged our base LlamaParse price of 0.3c per page. If you don't then you will be charged 1.5c per page, as we will make the calls to gpt4o-mini for you and give you price predictability."
|
||||
]
|
||||
},
|
||||
{
|
||||
"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 bf3e7341-bb11-42d4-a5f7-bb5260ad792c\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from llama_parse import LlamaParse\n",
|
||||
"\n",
|
||||
"parser = LlamaParse(\n",
|
||||
" result_type=\"markdown\",\n",
|
||||
" use_vendor_multimodal_model=True,\n",
|
||||
" vendor_multimodal_model_name=\"openai-gpt-4o-mini\",\n",
|
||||
" invalidate_cache=True,\n",
|
||||
")\n",
|
||||
"json_objs = parser.get_json_result(\"./data/llama3.1_blog.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.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.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 (3c 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 391ff280-08e5-4143-85f2-90ada287e26c\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",
|
||||
")\n",
|
||||
"json_objs_gpt4o = parser_gpt4o.get_json_result(\"./data/llama3.1_blog.pdf\")\n",
|
||||
"# json_objs_gpt4o = parser.get_json_result(\"./data/llama2-p33.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-mini and GPT-4o along with the original document page.\n",
|
||||
"\n",
|
||||
"We see that \n",
|
||||
"\n",
|
||||
"**NOTE**: If you're using llama2-p33, just use `docs[0]`"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "778698aa-da7e-4081-b3b5-0372f228536f",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"page: 5\n",
|
||||
"\n",
|
||||
"# Llama 3.1 Model Evaluation\n",
|
||||
"\n",
|
||||
"## Category Benchmark\n",
|
||||
"\n",
|
||||
"| Benchmark | Gemma 2 9B IT | Mistral 7B Instruct | Llama 3.1 70B | Mistral 8x228B Instruct | GPT 3.5 Turbo |\n",
|
||||
"|-------------------------------|----------------|----------------------|----------------|-------------------------|----------------|\n",
|
||||
"| General | | | | | |\n",
|
||||
"| MMLU (0-shot, CoT) | 73.0 | 72.3 | 86.0 | 79.9 | 69.8 |\n",
|
||||
"| MMLU PRO (5-shot, CoT) | 48.3 | 36.9 | 66.4 | 56.3 | 49.2 |\n",
|
||||
"| IFEval | 80.4 | 73.6 | 87.5 | 72.7 | 69.9 |\n",
|
||||
"| Code | | | | | |\n",
|
||||
"| HumanEval (0-shot) | 72.6 | 54.3 | 80.5 | 75.6 | 68.0 |\n",
|
||||
"| MBPP EvalPlus (Human) (0-shot, CoT) | 72.8 | 71.7 | 86.0 | 78.6 | 82.0 |\n",
|
||||
"| Math | | | | | |\n",
|
||||
"| GSM8K | 84.5 | 76.7 | 95.1 | 88.2 | 81.6 |\n",
|
||||
"| MATH (0-shot, CoT) | 51.9 | 44.3 | 70.8 | 54.1 | 43.1 |\n",
|
||||
"| Reasoning | | | | | |\n",
|
||||
"| ARC Challenge | 83.4 | 87.6 | 74.2 | 87.7 | 83.7 |\n",
|
||||
"| GPA (0-shot) | 32.8 | 24.8 | 46.7 | 33.3 | 35.8 |\n",
|
||||
"| Tool use | | | | | |\n",
|
||||
"| BFCL | 76.1 | 64.0 | 94.8 | 81.4 | 78.0 |\n",
|
||||
"| Noxus | 38.5 | 30.0 | 24.7 | 48.5 | 37.5 |\n",
|
||||
"| Long context | | | | | |\n",
|
||||
"| ZeroSCROLLS/QualiTY | 81.0 | - | 90.5 | - | - |\n",
|
||||
"| InfiniteBench/En.MC | 65.1 | - | 78.2 | - | - |\n",
|
||||
"| NHI/Multi-needle | 98.8 | - | 97.5 | - | - |\n",
|
||||
"| Multilingual | | | | | |\n",
|
||||
"| MGSM (0-shot) | 68.9 | 53.2 | 86.9 | 71.1 | 51.4 |\n",
|
||||
"\n",
|
||||
"## Llama 3.1 405B Human Evaluation\n",
|
||||
"\n",
|
||||
"| Comparison | Win Rate | Tie Rate | Loss Rate |\n",
|
||||
"|----------------------------------------------|----------|----------|-----------|\n",
|
||||
"| Llama 3.1 405B vs GPT-4-0125-Preview | 23.3% | 52.2% | 24.5% |\n",
|
||||
"| Llama 3.1 405B vs GPT-4o | 19.1% | 51.7% | 29.2% |\n",
|
||||
"| Llama 3.1 405B vs Claude 3.5 Sonnet | 24.9% | 50.8% | 24.2% |\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# using GPT4o-mini\n",
|
||||
"print(docs[4].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: 5\n",
|
||||
"\n",
|
||||
"# Introducing Llama 3.1: Our most capable models to date\n",
|
||||
"\n",
|
||||
"## Meta\n",
|
||||
"\n",
|
||||
"| Category | Benchmark | Llama 3.1 8B | Gemma 2 9B IT | Mistral 7B Instruct | Llama 3.1 70B | Mixtral 8x22B Instruct | GPT 3.5 Turbo |\n",
|
||||
"|----------|-----------|--------------|---------------|---------------------|---------------|-----------------------|---------------|\n",
|
||||
"| General | MMLU (0-shot, CoT) | 73.0 | 72.3 (0-shot, non-CoT) | 60.5 | 86.0 | 79.9 | 69.8 |\n",
|
||||
"| | MMLU PRO (5-shot, CoT) | 48.3 | 71.7 | 36.9 | 66.4 | 56.3 | 49.2 |\n",
|
||||
"| | ITEval | 80.4 | 73.6 | 57.6 | 87.5 | 72.7 | 69.9 |\n",
|
||||
"| Code | HumanEval (0-shot) | 72.6 | 54.3 | 40.2 | 80.5 | 75.6 | 68.0 |\n",
|
||||
"| | MBPP EvalPlus (5-shot) (0-shot) | 72.8 | 71.7 | 49.5 | 86.0 | 78.6 | 82.0 |\n",
|
||||
"| Math | GSM8K | 84.5 | 76.7 | 53.2 | 95.1 | 88.2 | 81.6 |\n",
|
||||
"| | MATH (0-shot, CoT) | 51.9 | 44.3 | 13.0 | 68.0 | 54.1 | 43.1 |\n",
|
||||
"| Reasoning | ARC Challenge (0-shot) | 83.4 | 87.6 | 74.2 | 94.8 | 88.7 | 83.7 |\n",
|
||||
"| | GOPA (0-shot) | 32.8 | 40.8 | 28.0 | 46.7 | - | - |\n",
|
||||
"| Tool use | BFCL | 76.1 | 60.3 | 60.4 | 94.8 | - | 85.9 |\n",
|
||||
"| | Noxus | 38.5 | 30.0 | 24.7 | 56.7 | 48.5 | 37.2 |\n",
|
||||
"| Long context | ZeroSCROLLS/QuaLITY | 81.0 | - | - | 90.5 | - | - |\n",
|
||||
"| | InfiniteBench/En.MC | 65.1 | - | - | 78.2 | - | - |\n",
|
||||
"| | NIH/Multi-needle | 98.8 | - | - | 97.5 | - | - |\n",
|
||||
"| Multilingual | Multilingual MGSM (0-shot) | 68.9 | 53.2 | 29.9 | 86.9 | 71.1 | 51.4 |\n",
|
||||
"\n",
|
||||
"## Llama 3.1 405B Human Evaluation\n",
|
||||
"\n",
|
||||
"| Model Comparison | Win | Tie | Loss |\n",
|
||||
"|------------------|-----|-----|------|\n",
|
||||
"| Llama 3.1 405B vs GPT-4-0125-Preview | 23.3% | 52.2% | 24.5% |\n",
|
||||
"| Llama 3.1 405B vs GPT-4o | 19.1% | 51.7% | 29.2% |\n",
|
||||
"| Llama 3.1 405B vs Claude 3.5 Sonnet | 24.9% | 50.8% | 24.2% |\n",
|
||||
"\n",
|
||||
"https://ai.meta.com/blog/meta-llama-3-1/\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# using GPT-4o\n",
|
||||
"print(docs_gpt4o[4].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=\"gpt-4o-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 = \"How does Llama3.1 compare against gpt-4o and Claude 3.5 Sonnet in human evals?\"\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": [
|
||||
"In human evaluations, Llama 3.1 405B has a win rate of 19.1% against GPT-4o and 24.9% against Claude 3.5 Sonnet. The tie rates for Llama 3.1 405B are 51.7% against GPT-4o and 50.8% against Claude 3.5 Sonnet, while the loss rates are 29.2% against GPT-4o and 24.2% against Claude 3.5 Sonnet. This indicates that Llama 3.1 performs competitively in comparison to both models, with a notable number of ties.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(response)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "7bee8167-f021-4c87-8d28-9f40a4f7b69d",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"# Llama 3.1 Model Evaluation\n",
|
||||
"\n",
|
||||
"## Category Benchmark\n",
|
||||
"\n",
|
||||
"| Benchmark | Gemma 2 9B IT | Mistral 7B Instruct | Llama 3.1 70B | Mistral 8x228B Instruct | GPT 3.5 Turbo |\n",
|
||||
"|-------------------------------|----------------|----------------------|----------------|-------------------------|----------------|\n",
|
||||
"| General | | | | | |\n",
|
||||
"| MMLU (0-shot, CoT) | 73.0 | 72.3 | 86.0 | 79.9 | 69.8 |\n",
|
||||
"| MMLU PRO (5-shot, CoT) | 48.3 | 36.9 | 66.4 | 56.3 | 49.2 |\n",
|
||||
"| IFEval | 80.4 | 73.6 | 87.5 | 72.7 | 69.9 |\n",
|
||||
"| Code | | | | | |\n",
|
||||
"| HumanEval (0-shot) | 72.6 | 54.3 | 80.5 | 75.6 | 68.0 |\n",
|
||||
"| MBPP EvalPlus (Human) (0-shot, CoT) | 72.8 | 71.7 | 86.0 | 78.6 | 82.0 |\n",
|
||||
"| Math | | | | | |\n",
|
||||
"| GSM8K | 84.5 | 76.7 | 95.1 | 88.2 | 81.6 |\n",
|
||||
"| MATH (0-shot, CoT) | 51.9 | 44.3 | 70.8 | 54.1 | 43.1 |\n",
|
||||
"| Reasoning | | | | | |\n",
|
||||
"| ARC Challenge | 83.4 | 87.6 | 74.2 | 87.7 | 83.7 |\n",
|
||||
"| GPA (0-shot) | 32.8 | 24.8 | 46.7 | 33.3 | 35.8 |\n",
|
||||
"| Tool use | | | | | |\n",
|
||||
"| BFCL | 76.1 | 64.0 | 94.8 | 81.4 | 78.0 |\n",
|
||||
"| Noxus | 38.5 | 30.0 | 24.7 | 48.5 | 37.5 |\n",
|
||||
"| Long context | | | | | |\n",
|
||||
"| ZeroSCROLLS/QualiTY | 81.0 | - | 90.5 | - | - |\n",
|
||||
"| InfiniteBench/En.MC | 65.1 | - | 78.2 | - | - |\n",
|
||||
"| NHI/Multi-needle | 98.8 | - | 97.5 | - | - |\n",
|
||||
"| Multilingual | | | | | |\n",
|
||||
"| MGSM (0-shot) | 68.9 | 53.2 | 86.9 | 71.1 | 51.4 |\n",
|
||||
"\n",
|
||||
"## Llama 3.1 405B Human Evaluation\n",
|
||||
"\n",
|
||||
"| Comparison | Win Rate | Tie Rate | Loss Rate |\n",
|
||||
"|----------------------------------------------|----------|----------|-----------|\n",
|
||||
"| Llama 3.1 405B vs GPT-4-0125-Preview | 23.3% | 52.2% | 24.5% |\n",
|
||||
"| Llama 3.1 405B vs GPT-4o | 19.1% | 51.7% | 29.2% |\n",
|
||||
"| Llama 3.1 405B vs Claude 3.5 Sonnet | 24.9% | 50.8% | 24.2% |\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(response.source_nodes[1].get_content())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "5f9fef7f-510b-46a5-8716-f5616f542035",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"In human evaluations, Llama 3.1 405B shows competitive performance against GPT-4o and Claude 3.5 Sonnet. Specifically, when compared to GPT-4o, Llama 3.1 won 19.1% of the time, tied 51.7%, and lost 29.2%. Against Claude 3.5 Sonnet, it won 24.9% of the time, tied 50.8%, and lost 24.2%. This indicates that Llama 3.1 performs comparably in real-world scenarios against these leading models.\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": [
|
||||
"# Introducing Llama 3.1: Our most capable models to date\n",
|
||||
"\n",
|
||||
"## Meta\n",
|
||||
"\n",
|
||||
"| Category | Benchmark | Llama 3.1 8B | Gemma 2 9B IT | Mistral 7B Instruct | Llama 3.1 70B | Mixtral 8x22B Instruct | GPT 3.5 Turbo |\n",
|
||||
"|----------|-----------|--------------|---------------|---------------------|---------------|-----------------------|---------------|\n",
|
||||
"| General | MMLU (0-shot, CoT) | 73.0 | 72.3 (0-shot, non-CoT) | 60.5 | 86.0 | 79.9 | 69.8 |\n",
|
||||
"| | MMLU PRO (5-shot, CoT) | 48.3 | 71.7 | 36.9 | 66.4 | 56.3 | 49.2 |\n",
|
||||
"| | ITEval | 80.4 | 73.6 | 57.6 | 87.5 | 72.7 | 69.9 |\n",
|
||||
"| Code | HumanEval (0-shot) | 72.6 | 54.3 | 40.2 | 80.5 | 75.6 | 68.0 |\n",
|
||||
"| | MBPP EvalPlus (5-shot) (0-shot) | 72.8 | 71.7 | 49.5 | 86.0 | 78.6 | 82.0 |\n",
|
||||
"| Math | GSM8K | 84.5 | 76.7 | 53.2 | 95.1 | 88.2 | 81.6 |\n",
|
||||
"| | MATH (0-shot, CoT) | 51.9 | 44.3 | 13.0 | 68.0 | 54.1 | 43.1 |\n",
|
||||
"| Reasoning | ARC Challenge (0-shot) | 83.4 | 87.6 | 74.2 | 94.8 | 88.7 | 83.7 |\n",
|
||||
"| | GOPA (0-shot) | 32.8 | 40.8 | 28.0 | 46.7 | - | - |\n",
|
||||
"| Tool use | BFCL | 76.1 | 60.3 | 60.4 | 94.8 | - | 85.9 |\n",
|
||||
"| | Noxus | 38.5 | 30.0 | 24.7 | 56.7 | 48.5 | 37.2 |\n",
|
||||
"| Long context | ZeroSCROLLS/QuaLITY | 81.0 | - | - | 90.5 | - | - |\n",
|
||||
"| | InfiniteBench/En.MC | 65.1 | - | - | 78.2 | - | - |\n",
|
||||
"| | NIH/Multi-needle | 98.8 | - | - | 97.5 | - | - |\n",
|
||||
"| Multilingual | Multilingual MGSM (0-shot) | 68.9 | 53.2 | 29.9 | 86.9 | 71.1 | 51.4 |\n",
|
||||
"\n",
|
||||
"## Llama 3.1 405B Human Evaluation\n",
|
||||
"\n",
|
||||
"| Model Comparison | Win | Tie | Loss |\n",
|
||||
"|------------------|-----|-----|------|\n",
|
||||
"| Llama 3.1 405B vs GPT-4-0125-Preview | 23.3% | 52.2% | 24.5% |\n",
|
||||
"| Llama 3.1 405B vs GPT-4o | 19.1% | 51.7% | 29.2% |\n",
|
||||
"| Llama 3.1 405B vs Claude 3.5 Sonnet | 24.9% | 50.8% | 24.2% |\n",
|
||||
"\n",
|
||||
"https://ai.meta.com/blog/meta-llama-3-1/\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(response_gpt4o.source_nodes[1].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
|
||||
}
|
||||
@@ -0,0 +1,443 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"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>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This cookbook shows how to use LlamaParse and OpenAI's multimodal GPT-4o model to parse auto insurance claim documents that contain complex tabular data. In this example, we will use an auto insurance claim template form, which contains complex tabular inputs regarding information about the location of the accident, accident description, information about vehicles of both parties, and injury information. The template is shown below.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"This example demonstrates how LlamaParse can be used on insurance documents, which often contains complex tabular data. We parse these tabluar PDF files into markdown-formatted tables, which can be indexed and queried over with a `VectorStoreIndex`. This can help insurance companies accelerate the process of gathering information about car accidents from insurance claim documents."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Install and Setup\n",
|
||||
"\n",
|
||||
"Install LlamaIndex, download the data, and apply `nest_asyncio`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install llama-index"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!wget https://github.com/user-attachments/files/16536240/claims.zip -O claims.zip\n",
|
||||
"!unzip -o claims.zip\n",
|
||||
"!rm claims.zip"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import nest_asyncio\n",
|
||||
"\n",
|
||||
"nest_asyncio.apply()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Set up your OpenAI and LlamaCloud keys."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = \"<Your OpenAI API Key>\"\n",
|
||||
"os.environ[\"LLAMA_CLOUD_API_KEY\"] = \"<Your Llamacloud API Key>\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Code Implementation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Set up LlamaParse. We want to parse the PDF files into markdown, translating the tabular data into markdown tables. To ensure accuracy, we will use the GPT-4o multimodal model to parse the PDFs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from llama_parse import LlamaParse\n",
|
||||
"\n",
|
||||
"parser = LlamaParse(\n",
|
||||
" result_type=\"markdown\",\n",
|
||||
" parsing_instruction=\"This is an auto insurance claim document.\",\n",
|
||||
" use_vendor_multimodal_model=True,\n",
|
||||
" vendor_multimodal_model_name=\"openai-gpt4o\",\n",
|
||||
" show_progress=True,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"CLAIMS_DIR = \"claims\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_claims_files(claims_dir=CLAIMS_DIR) -> list[str]:\n",
|
||||
" files = []\n",
|
||||
" for f in os.listdir(claims_dir):\n",
|
||||
" fname = os.path.join(claims_dir, f)\n",
|
||||
" if os.path.isfile(fname):\n",
|
||||
" files.append(fname)\n",
|
||||
" return files\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"files = get_claims_files() # get all files from the claims/ directory\n",
|
||||
"md_json_objs = parser.get_json_result(\n",
|
||||
" files\n",
|
||||
") # extract markdown data for insurance claim document\n",
|
||||
"parser.get_images(\n",
|
||||
" md_json_objs, download_path=\"data_images\"\n",
|
||||
") # extract images from PDFs and save them to ./data_images/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# extract list of pages for insurance claim doc\n",
|
||||
"md_json_list = []\n",
|
||||
"for obj in md_json_objs:\n",
|
||||
" md_json_list.extend(obj[\"pages\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Create helper functions to create a list of `TextNode`s from the markdown tables to feed into the `VectorStoreIndex`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import re\n",
|
||||
"from pathlib import Path\n",
|
||||
"import typing as t\n",
|
||||
"from llama_index.core.schema import TextNode, ImageNode\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_page_number(file_name):\n",
|
||||
" \"\"\"Gets page number of images using regex on file names\"\"\"\n",
|
||||
" match = re.search(r\"-page-(\\d+)\\.jpg$\", str(file_name))\n",
|
||||
" if match:\n",
|
||||
" return int(match.group(1))\n",
|
||||
" return 0\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def _get_sorted_image_files(image_dir):\n",
|
||||
" \"\"\"Get image files sorted by page.\"\"\"\n",
|
||||
" raw_files = [f for f in list(Path(image_dir).iterdir()) if f.is_file()]\n",
|
||||
" sorted_files = sorted(raw_files, key=get_page_number)\n",
|
||||
" return sorted_files\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_text_nodes(json_dicts, image_dir) -> t.List[TextNode]:\n",
|
||||
" \"\"\"Creates nodes from json + images\"\"\"\n",
|
||||
"\n",
|
||||
" nodes = []\n",
|
||||
"\n",
|
||||
" docs = [doc[\"md\"] for doc in json_dicts] # extract text\n",
|
||||
" image_files = _get_sorted_image_files(image_dir) # extract images\n",
|
||||
"\n",
|
||||
" for idx, doc in enumerate(docs):\n",
|
||||
" # adds both a text node and the corresponding image node (jpg of the page) for each page\n",
|
||||
" node = TextNode(\n",
|
||||
" text=doc,\n",
|
||||
" metadata={\"image_path\": str(image_files[idx]), \"page_num\": idx + 1},\n",
|
||||
" )\n",
|
||||
" image_node = ImageNode(\n",
|
||||
" image_path=str(image_files[idx]),\n",
|
||||
" metadata={\"page_num\": idx + 1, \"text_node_id\": node.id_},\n",
|
||||
" )\n",
|
||||
" nodes.extend([node, image_node])\n",
|
||||
"\n",
|
||||
" return nodes\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"text_nodes = get_text_nodes(md_json_list, \"data_images\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Index the documents."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from llama_index.core import (\n",
|
||||
" VectorStoreIndex,\n",
|
||||
" StorageContext,\n",
|
||||
" load_index_from_storage,\n",
|
||||
" Settings,\n",
|
||||
")\n",
|
||||
"from llama_index.embeddings.openai import OpenAIEmbedding\n",
|
||||
"from llama_index.llms.openai import OpenAI\n",
|
||||
"\n",
|
||||
"embed_model = OpenAIEmbedding(model=\"text-embedding-3-large\")\n",
|
||||
"llm = OpenAI(\"gpt-4o\")\n",
|
||||
"\n",
|
||||
"Settings.llm = llm\n",
|
||||
"Settings.embed_model = embed_model\n",
|
||||
"\n",
|
||||
"if not os.path.exists(\"storage_insurance\"):\n",
|
||||
" index = VectorStoreIndex(text_nodes, embed_model=embed_model)\n",
|
||||
" index.storage_context.persist(persist_dir=\"./storage_insurance\")\n",
|
||||
"else:\n",
|
||||
" ctx = StorageContext.from_defaults(persist_dir=\"./storage_insurance\")\n",
|
||||
" index = load_index_from_storage(ctx)\n",
|
||||
"\n",
|
||||
"query_engine = index.as_query_engine()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Example queries are shown below."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"Michael Johnson filed the insurance claim for the accident that happened on Sunset Blvd."
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Markdown object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from IPython.display import display, Markdown\n",
|
||||
"\n",
|
||||
"response = query_engine.query(\n",
|
||||
" \"Who filed the insurance claim for the accident that happened on Sunset Blvd?\"\n",
|
||||
")\n",
|
||||
"display(Markdown(str(response)))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"Ms. Patel's accident occurred on March 10, 2023, at approximately 9:15 AM in the Boise Towne Square Mall parking lot. She was heading west at a parking space and, after checking her mirrors and blind spots, did not see any approaching vehicles. However, Michael Chen, the driver of another vehicle, was driving too fast through the parking lot and failed to stop in time, resulting in a collision with Ms. Patel's vehicle. This caused significant damage to the rear bumper and trunk of her car."
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Markdown object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"response = query_engine.query(\"How did Ms. Patel's accident happen?\")\n",
|
||||
"display(Markdown(str(response)))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"Mr. Johnson's red sedan, a 2020 Honda Accord, was damaged on the front passenger side, including a dented fender and a broken headlight. The estimated repair cost is $3,500."
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Markdown object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"response = query_engine.query(\"How was Mr. Johnson's red sedan damaged?\")\n",
|
||||
"display(Markdown(str(response)))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"Mr. Doe's Honda Accord sustained damage to the front bumper, hood, fenders, head/tail lights, windshield, and doors."
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Markdown object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"response = query_engine.query(\"How was Mr. Doe's Honda Accord damaged?\")\n",
|
||||
"display(Markdown(str(response)))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"The witness for Ms. Patel's accident is Sophia Rodriguez. She can be contacted at 5554567890."
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Markdown object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"response = query_engine.query(\n",
|
||||
" \"Who are some witnesses for the Ms. Patel's accident and how can we contact them?\"\n",
|
||||
")\n",
|
||||
"display(Markdown(str(response)))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"Yes, Ms. Johnson sustained injuries. She experienced minor injuries, including a bruised knee and some whiplash."
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Markdown object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"response = query_engine.query(\n",
|
||||
" \"Did Ms. Johnson sustain any injuries? If so, what were those injuries?\"\n",
|
||||
")\n",
|
||||
"display(Markdown(str(response)))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"Mark Johnson is liable for the damages from the accident on Lombard Street. He was driving a delivery van that collided with the rear of Emily Rodriguez's vehicle. In rear-end collisions, the driver who hits the vehicle in front is typically at fault because they are expected to maintain a safe distance and be able to stop in time to avoid a collision."
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Markdown object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"chat_engine = index.as_chat_engine()\n",
|
||||
"response = chat_engine.chat(\n",
|
||||
" \"Given the accident that happened on Lombard Street, name a party that is liable for the damages and explain why.\"\n",
|
||||
")\n",
|
||||
"display(Markdown(str(response)))"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "llama-parse-5ZmnAQ0r-py3.11",
|
||||
"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": 2
|
||||
}
|
||||
@@ -0,0 +1,371 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"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",
|
||||
"\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",
|
||||
"In this example, we will be using legal documents from the archive of the Library of Congress ([link to dataset](https://www.loc.gov/item/2020445568/)). These documents vary by format, with some containing pure text and others containing headings, sections, and large tables. This shows how LlamaParse can parse a wide variety of documents and still retrieve accurate results.\n",
|
||||
"\n",
|
||||
"The documents in this example include:\n",
|
||||
"- [APA Program Report](https://www.irs.gov/pub/irs-apa/a_2003-19.pdf)\n",
|
||||
"- [2004 Report on the CRA performance of Barre Savings Bank in Barre, MA](https://github.com/user-attachments/files/16536412/barre_savings_bank_evaluation.pdf)\n",
|
||||
"- [2016 Energy Supply/Demand Forecast](https://github.com/user-attachments/files/16536415/energy_supply_demand.pdf)\n",
|
||||
"- [Transcript of Senate Committee Hearing about Foreign Markets](https://github.com/user-attachments/files/16536422/foreign_markets.pdf)\n",
|
||||
"- [A Motion To Stay for an Indiana Court Case](https://github.com/user-attachments/files/16536427/motion_to_stay.pdf)\n",
|
||||
"- [Article About an OC Representative's Bill to Introduce Offshore Drilling to CA](https://github.com/user-attachments/files/16536437/oc_bill_offshore_drilling.pdf)\n",
|
||||
"- [Charter of the Subcommittee on Ocean Science and Technology](https://github.com/user-attachments/files/16536445/ost_subcommittee_charter.pdf)\n",
|
||||
"- [US Immigration Case](https://github.com/user-attachments/files/16536446/us_immigration_case.pdf)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Setup and Installation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Install LlamaIndex, download the data, and apply `nest_asyncio`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install llama-index llama-parse"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!wget https://github.com/user-attachments/files/16447759/data.zip -O data.zip\n",
|
||||
"!unzip -o data.zip\n",
|
||||
"!rm data.zip"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import nest_asyncio\n",
|
||||
"\n",
|
||||
"nest_asyncio.apply()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Set up your OpenAI and LlamaCloud keys."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = \"<Your OpenAI API Key>\"\n",
|
||||
"os.environ[\"LLAMA_CLOUD_API_KEY\"] = \"<Your LlamaCloud API Key>\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Code Implementation\n",
|
||||
"\n",
|
||||
"Set up LlamaParse. We want to parse the PDF files into markdown, translating the tabular data into markdown tables. To ensure accuracy, we will use the GPT-4o multimodal model to parse the PDFs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from llama_parse import LlamaParse\n",
|
||||
"\n",
|
||||
"parser = LlamaParse(\n",
|
||||
" result_type=\"markdown\",\n",
|
||||
" parsing_instruction=\"Provided are a series of US legal documents.\",\n",
|
||||
" use_vendor_multimodal_model=True,\n",
|
||||
" vendor_multimodal_model_name=\"openai-gpt4o\",\n",
|
||||
" show_progress=True,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"DATA_DIR = \"data\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_data_files(data_dir=DATA_DIR) -> list[str]:\n",
|
||||
" files = []\n",
|
||||
" for f in os.listdir(data_dir):\n",
|
||||
" fname = os.path.join(data_dir, f)\n",
|
||||
" if os.path.isfile(fname):\n",
|
||||
" files.append(fname)\n",
|
||||
" return files\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"files = get_data_files()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Load data from parser into documents containing parsed Markdown text from the legal document PDFs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Parsing files: 100%|██████████| 8/8 [01:25<00:00, 10.67s/it]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"documents = parser.load_data(\n",
|
||||
" files,\n",
|
||||
" extra_info={\"name\": \"US legal documents provided by the Library of Congress.\"},\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Setup LlamaIndex. Set the default LLM to GPT-4o (a multi-modal model), and create an index from the documents, and persist these documents to disk. If these documents have already been persisted, then load index from the persisted docs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from llama_index.core import (\n",
|
||||
" VectorStoreIndex,\n",
|
||||
" StorageContext,\n",
|
||||
" load_index_from_storage,\n",
|
||||
" Settings,\n",
|
||||
")\n",
|
||||
"from llama_index.embeddings.openai import OpenAIEmbedding\n",
|
||||
"from llama_index.llms.openai import OpenAI\n",
|
||||
"\n",
|
||||
"embed_model = OpenAIEmbedding(model=\"text-embedding-3-large\")\n",
|
||||
"llm = OpenAI(\"gpt-4o\")\n",
|
||||
"\n",
|
||||
"Settings.llm = llm\n",
|
||||
"Settings.embed_model = embed_model\n",
|
||||
"\n",
|
||||
"if not os.path.exists(\"storage_legal\"):\n",
|
||||
" index = VectorStoreIndex(documents, embed_model=embed_model)\n",
|
||||
" index.storage_context.persist(persist_dir=\"./storage_legal\")\n",
|
||||
"else:\n",
|
||||
" ctx = StorageContext.from_defaults(persist_dir=\"./storage_legal\")\n",
|
||||
" index = load_index_from_storage(ctx)\n",
|
||||
"\n",
|
||||
"query_engine = index.as_query_engine()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Example Queries"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"The majority of Barre Savings Bank's loans went to residential real estate, specifically 1-4 family mortgages, which accounted for 78.7 percent of the total loans."
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Markdown object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from IPython.display import display, Markdown\n",
|
||||
"\n",
|
||||
"response = query_engine.query(\n",
|
||||
" \"Where did the majority of Barre Savings Bank's loans go?\"\n",
|
||||
")\n",
|
||||
"display(Markdown(str(response)))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"Mr. Kubarych believes foreign markets are important because they are attractive to foreign investors for the same reasons they are attractive to Americans. The economic data is strong, and the high tech boom has created a positive perception that overshadows longer-term vulnerabilities. Additionally, foreign investors have high expectations for the U.S. to maintain a firm monetary policy in response to inflation and to act as a superpower rather than pursuing narrow nationalist economic policies."
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Markdown object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"response = query_engine.query(\n",
|
||||
" \"Why does Mr. Kubarych believe foreign markets are so important?\"\n",
|
||||
")\n",
|
||||
"display(Markdown(str(response)))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"House Speaker Nancy Pelosi and the Democratic majority are against the proposal of offshore drilling in California. Pelosi stated that offshore drilling is \"off the table,\" and Democrats have been consistently unwilling to bend environmental rules. They argue that oil companies are not using the 68 million acres of federal lands already leased to them, either because it takes a long time or they lack the necessary equipment."
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Markdown object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"response = query_engine.query(\n",
|
||||
" \"Who is against the proposal of offshore drilling in CA and why?\"\n",
|
||||
")\n",
|
||||
"display(Markdown(str(response)))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"The purpose of the Ocean Science and Technology Subcommittee (SOST) is to advise and assist the Committee on Environment, Natural Resources, and Sustainability on national issues of ocean science and technology. The SOST aims to contribute to the goals for Federal ocean science and technology by developing coordinated interagency strategies. It also retains the functions of the previously-chartered Joint Subcommittee on Ocean Science and Technology and serves as the Ocean Science and Technology Interagency Policy Committee for the National Ocean Council."
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Markdown object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"response = query_engine.query(\n",
|
||||
" \"What is the purpose of the Ocean Science and Technology Subcommittee?\"\n",
|
||||
")\n",
|
||||
"display(Markdown(str(response)))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"The immigration appeal is dismissed because the petitioner is not a U.S. citizen, and therefore, is not eligible to file a Petition for Alien Fiancé(e) (Form I-129F) on behalf of the beneficiary. The relevant law provides nonimmigrant classification only to aliens who are the fiancé(e)s of U.S. citizens."
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Markdown object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"response = query_engine.query(\"Why is the immigration appeal dismissed?\")\n",
|
||||
"display(Markdown(str(response)))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"An advance pricing agreement (APA) is a binding contract between a taxpayer and the IRS that establishes an approved transfer pricing method (TPM) for specific transactions. This agreement aims to prevent disputes over transfer pricing by ensuring that the taxpayer's tax returns for the covered years are consistent with the agreed TPM. APAs can be unilateral, involving only the taxpayer and the IRS, or bilateral/multilateral, involving agreements with one or more foreign tax authorities to avoid double taxation."
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Markdown object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"response = query_engine.query(\"What is an advance pricing agreement?\")\n",
|
||||
"display(Markdown(str(response)))"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "llama-parse-5ZmnAQ0r-py3.11",
|
||||
"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": 2
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 580 KiB |
@@ -0,0 +1,999 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "93ae9bad-b8cc-43de-ba7d-387e0155674c",
|
||||
"metadata": {},
|
||||
"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",
|
||||
"\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",
|
||||
"A gap of text-based RAG is that they struggle with purely text-based representations of complex documents. For instance, if a page contains a lot of images and diagrams, a text parser would need to rely on raw OCR to extract out text. You can also use a multimodal model (e.g. gpt-4o and up) to do text extraction, but this is inherently a lossy conversion.\n",
|
||||
"\n",
|
||||
"Instead a **native multimodal pipeline** stores both a text and image representation of a document chunk. They are indexed via embeddings (text or image), and during synthesis both text and image are directly fed to the multimodal model for synthesis.\n",
|
||||
"\n",
|
||||
"This can have the following advantages:\n",
|
||||
"- **Robustness**: This solution is more robust than a pure text or even a pure image-based approach. In a pure text RAG approach, the parsing piece can be lossy. In a pure image-based approach, multimodal OCR is not perfect and may lose out against text parsing for text-heavy documents.\n",
|
||||
"- **Cost Optimization**: You may choose to dynamically include text-only, or text + image depending on the content of the page.\n",
|
||||
"\n",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "54e8d9a7-5036-4d32-818f-00b2e888521f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Setup"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "70ccdd53-e68a-4199-aacb-cfe71ad1ff0b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import nest_asyncio\n",
|
||||
"\n",
|
||||
"nest_asyncio.apply()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "225c5556-a789-4386-a1ee-cce01dbeb6cf",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Setup Observability\n",
|
||||
"\n",
|
||||
"We setup an integration with LlamaTrace (integration with Arize).\n",
|
||||
"\n",
|
||||
"If you haven't already done so, make sure to create an account here: https://llamatrace.com/login. Then create an API key and put it in the `PHOENIX_API_KEY` variable below."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "0eabee1f-290a-4c85-b362-54f45c8559ae",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip install -U llama-index-callbacks-arize-phoenix"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "aaeb245c-730b-4c34-ad68-708fdde0e6cb",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# setup Arize Phoenix for logging/observability\n",
|
||||
"import llama_index.core\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"PHOENIX_API_KEY = \"<PHOENIX_API_KEY>\"\n",
|
||||
"os.environ[\"OTEL_EXPORTER_OTLP_HEADERS\"] = f\"api_key={PHOENIX_API_KEY}\"\n",
|
||||
"llama_index.core.set_global_handler(\n",
|
||||
" \"arize_phoenix\", endpoint=\"https://llamatrace.com/v1/traces\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "fbb362db-b1b1-4eea-be1a-b1f78b0779d7",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Load Data\n",
|
||||
"\n",
|
||||
"Here we load the [Conoco Phillips 2023 investor meeting slide deck](https://static.conocophillips.com/files/2023-conocophillips-aim-presentation.pdf)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "8bce3407-a7d2-47e8-9eaf-ab297a94750c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!mkdir data\n",
|
||||
"!mkdir data_images\n",
|
||||
"!wget \"https://static.conocophillips.com/files/2023-conocophillips-aim-presentation.pdf\" -O data/conocophillips.pdf"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "246ba6b0-51af-42f9-b1b2-8d3e721ef782",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Model Setup\n",
|
||||
"\n",
|
||||
"Setup models that will be used for downstream orchestration."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "16e2071d-bbc2-4707-8ae7-cb4e1fecafd3",
|
||||
"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",
|
||||
"embed_model = OpenAIEmbedding(model=\"text-embedding-3-large\")\n",
|
||||
"llm = OpenAI(model=\"gpt-4o\")\n",
|
||||
"\n",
|
||||
"Settings.embed_model = embed_model\n",
|
||||
"Settings.llm = llm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "e3f6416f-f580-4722-aaa9-7f3500408547",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Use LlamaParse to Parse Text and Images\n",
|
||||
"\n",
|
||||
"In this example, use LlamaParse to parse both the text and images from the document.\n",
|
||||
"\n",
|
||||
"We parse out the text in two ways: \n",
|
||||
"- in regular `text` mode using our default text layout algorithm\n",
|
||||
"- in `markdown` mode using GPT-4o (`gpt4o_mode=True`). This also allows us to capture page screenshots"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "570089e5-238a-4dcc-af65-96e7393c2b4d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from llama_parse import LlamaParse\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"parser_text = LlamaParse(result_type=\"text\")\n",
|
||||
"parser_gpt4o = LlamaParse(result_type=\"markdown\", gpt4o_mode=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ef82a985-4088-4bb7-9a21-0318e1b9207d",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Parsing text...\n",
|
||||
"Started parsing the file under job_id 62f157a9-9ef9-4e5b-95ac-67093fa25800\n",
|
||||
"..........Parsing PDF file...\n",
|
||||
"Started parsing the file under job_id 1ddd5654-062b-4e19-b488-d66efc9c509d\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(f\"Parsing text...\")\n",
|
||||
"docs_text = parser_text.load_data(\"data/conocophillips.pdf\")\n",
|
||||
"print(f\"Parsing PDF file...\")\n",
|
||||
"md_json_objs = parser_gpt4o.get_json_result(\"data/conocophillips.pdf\")\n",
|
||||
"md_json_list = md_json_objs[0][\"pages\"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "5318fb7b-fe6a-4a8a-b82e-4ed7b4512c37",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"# Commitment to Disciplined Reinvestment Rate\n",
|
||||
"\n",
|
||||
"| Period | Description | Reinvestment Rate | WTI Average |\n",
|
||||
"|--------------|--------------------------------------|-------------------|-------------|\n",
|
||||
"| 2012-2016 | Industry Growth Focus | >100% | ~$75/BBL |\n",
|
||||
"| 2017-2022 | ConocoPhillips Strategy Reset | <60% | ~$63/BBL |\n",
|
||||
"| 2023E | | | at $80/BBL |\n",
|
||||
"| 2024-2028 | Disciplined Reinvestment Rate | ~50% | at $60/BBL |\n",
|
||||
"| 2029-2032 | | ~6% CFO CAGR | at $60/BBL |\n",
|
||||
"\n",
|
||||
"- **Historic Reinvestment Rate**: Gray bars\n",
|
||||
"- **Reinvestment Rate at $60/BBL WTI**: Blue bars\n",
|
||||
"- **Reinvestment Rate at $80/BBL WTI**: Dashed blue lines\n",
|
||||
"\n",
|
||||
"Reinvestment rate and cash from operations (CFO) are non-GAAP measures. Definitions and reconciliations are included in the Appendix.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(md_json_list[10][\"md\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "eeadb16c-97eb-4622-9551-b34d7f90d72f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"image_dicts = parser_gpt4o.get_images(md_json_objs, download_path=\"data_images\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "fd3e098b-0606-4429-b48d-d4fe0140fc0e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Build Multimodal Index\n",
|
||||
"\n",
|
||||
"In this section we build the multimodal index over the parsed deck. \n",
|
||||
"\n",
|
||||
"We do this by creating **text** nodes from the document that contain metadata referencing the original image path.\n",
|
||||
"\n",
|
||||
"In this example we're indexing the text node for retrieval. The text node has a reference to both the parsed text as well as the image screenshot."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "3aae2dee-9d85-4604-8a51-705d4db527f7",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Get Text Nodes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "18c24174-05ce-417f-8dd2-79c3f375db03",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from llama_index.core.schema import TextNode\n",
|
||||
"from typing import Optional"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "8e331dfe-a627-4e23-8c57-70ab1d9342e4",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# get pages loaded through llamaparse\n",
|
||||
"import re\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_page_number(file_name):\n",
|
||||
" match = re.search(r\"-page-(\\d+)\\.jpg$\", str(file_name))\n",
|
||||
" if match:\n",
|
||||
" return int(match.group(1))\n",
|
||||
" return 0\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def _get_sorted_image_files(image_dir):\n",
|
||||
" \"\"\"Get image files sorted by page.\"\"\"\n",
|
||||
" raw_files = [f for f in list(Path(image_dir).iterdir()) if f.is_file()]\n",
|
||||
" sorted_files = sorted(raw_files, key=get_page_number)\n",
|
||||
" return sorted_files"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "346fe5ef-171e-4a54-9084-7a7805103a13",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from copy import deepcopy\n",
|
||||
"from pathlib import Path\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# attach image metadata to the text nodes\n",
|
||||
"def get_text_nodes(docs, image_dir=None, json_dicts=None):\n",
|
||||
" \"\"\"Split docs into nodes, by separator.\"\"\"\n",
|
||||
" nodes = []\n",
|
||||
"\n",
|
||||
" image_files = _get_sorted_image_files(image_dir) if image_dir is not None else None\n",
|
||||
" md_texts = [d[\"md\"] for d in json_dicts] if json_dicts is not None else None\n",
|
||||
"\n",
|
||||
" doc_chunks = [c for d in docs for c in d.text.split(\"---\")]\n",
|
||||
" for idx, doc_chunk in enumerate(doc_chunks):\n",
|
||||
" chunk_metadata = {\"page_num\": idx + 1}\n",
|
||||
" if image_files is not None:\n",
|
||||
" image_file = image_files[idx]\n",
|
||||
" chunk_metadata[\"image_path\"] = str(image_file)\n",
|
||||
" if md_texts is not None:\n",
|
||||
" chunk_metadata[\"parsed_text_markdown\"] = md_texts[idx]\n",
|
||||
" chunk_metadata[\"parsed_text\"] = doc_chunk\n",
|
||||
" node = TextNode(\n",
|
||||
" text=\"\",\n",
|
||||
" metadata=chunk_metadata,\n",
|
||||
" )\n",
|
||||
" nodes.append(node)\n",
|
||||
"\n",
|
||||
" return nodes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "f591669c-5a8e-491d-9cef-0b754abbf26f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# this will split into pages\n",
|
||||
"text_nodes = get_text_nodes(docs_text, image_dir=\"data_images\", json_dicts=md_json_list)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "32c13950-c1db-435f-b5b4-89d62b8b7744",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"page_num: 11\n",
|
||||
"image_path: data_images/1ddd5654-062b-4e19-b488-d66efc9c509d-page_39.jpg\n",
|
||||
"parsed_text_markdown: # Commitment to Disciplined Reinvestment Rate\n",
|
||||
"\n",
|
||||
"| Period | Description | Reinvestment Rate | WTI Average |\n",
|
||||
"|--------------|--------------------------------------|-------------------|-------------|\n",
|
||||
"| 2012-2016 | Industry Growth Focus | >100% | ~$75/BBL |\n",
|
||||
"| 2017-2022 | ConocoPhillips Strategy Reset | <60% | ~$63/BBL |\n",
|
||||
"| 2023E | | | at $80/BBL |\n",
|
||||
"| 2024-2028 | Disciplined Reinvestment Rate | ~50% | at $60/BBL |\n",
|
||||
"| 2029-2032 | | ~6% CFO CAGR | at $60/BBL |\n",
|
||||
"\n",
|
||||
"- **Historic Reinvestment Rate**: Gray bars\n",
|
||||
"- **Reinvestment Rate at $60/BBL WTI**: Blue bars\n",
|
||||
"- **Reinvestment Rate at $80/BBL WTI**: Dashed blue lines\n",
|
||||
"\n",
|
||||
"Reinvestment rate and cash from operations (CFO) are non-GAAP measures. Definitions and reconciliations are included in the Appendix.\n",
|
||||
"parsed_text: Commitment to Disciplined Reinvestment Rate\n",
|
||||
" Industry ConocoPhillips\n",
|
||||
" Strategy Reset Disciplined Reinvestment Rate is the Foundation for Superior\n",
|
||||
" Growth Focus Returns on and of Capital, while Driving Durable CFO Growth\n",
|
||||
" 100% <60% 50% 6% at $60/BBL WTI\n",
|
||||
" Reinvestment Rate Reinvestment Rate Reinvestment Rate10-YearCFO CAGR Planning PriceMid-Cycle\n",
|
||||
" 2024-2032\n",
|
||||
" 2 100%\n",
|
||||
" 1 75%\n",
|
||||
" 1 50%\n",
|
||||
" 1 WTIat $80/BBL at S80/BBL\n",
|
||||
" 25% 'S75/BBL $63/BBL WTI\n",
|
||||
" WTI WTI at S80/BBL at S60/BBL at S60/BBL\n",
|
||||
" Average Average WTI WTI WTI\n",
|
||||
" 0%\n",
|
||||
" 2012-2016 2017-2022 2023E 2024-2028 2029-2032\n",
|
||||
" Historic Reinvestment Rate Reinvestment Rate at $60/BBL WTI Reinvestment Rate at $80/BBL WTI\n",
|
||||
" Reinvestment rate and cash from operations (CFO) are non-GAAP measures: Definitions and reconciliations are included in the Appendix ConocoPhillips\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(text_nodes[10].get_content(metadata_mode=\"all\"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "4f404f56-db1e-4ed7-9ba1-ead763546348",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Build Index\n",
|
||||
"\n",
|
||||
"Once the text nodes are ready, we feed into our vector store index abstraction, which will index these nodes into a simple in-memory vector store (of course, you should definitely check out our 40+ vector store integrations!)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "6ea53c31-0e38-421c-8d9b-0e3adaa1677e",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"/Users/jerryliu/Programming/gpt_index/.venv/lib/python3.10/site-packages/tiktoken/core.py:50: RuntimeWarning: coroutine 'LlamaParse.aload_data' was never awaited\n",
|
||||
" self._core_bpe = _tiktoken.CoreBPE(mergeable_ranks, special_tokens, pat_str)\n",
|
||||
"RuntimeWarning: Enable tracemalloc to get the object allocation traceback\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"from llama_index.core import (\n",
|
||||
" StorageContext,\n",
|
||||
" VectorStoreIndex,\n",
|
||||
" load_index_from_storage,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"if not os.path.exists(\"storage_nodes\"):\n",
|
||||
" index = VectorStoreIndex(text_nodes, embed_model=embed_model)\n",
|
||||
" # save index to disk\n",
|
||||
" index.set_index_id(\"vector_index\")\n",
|
||||
" index.storage_context.persist(\"./storage_nodes\")\n",
|
||||
"else:\n",
|
||||
" # rebuild storage context\n",
|
||||
" storage_context = StorageContext.from_defaults(persist_dir=\"storage_nodes\")\n",
|
||||
" # load index\n",
|
||||
" index = load_index_from_storage(storage_context, index_id=\"vector_index\")\n",
|
||||
"\n",
|
||||
"retriever = index.as_retriever()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "5f0e33a4-9422-498d-87ee-d917bdf74d80",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Build Multimodal Query Engine\n",
|
||||
"\n",
|
||||
"We now use LlamaIndex abstractions to build a **custom query engine**. In contrast to a standard RAG query engine that will retrieve the text node and only put that into the prompt (response synthesis module), this custom query engine will also load the image document, and put both the text and image document into the response synthesis module."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "35a94be2-e289-41a6-92e4-d3cb428fb0c8",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from llama_index.core.query_engine import CustomQueryEngine, SimpleMultiModalQueryEngine\n",
|
||||
"from llama_index.core.retrievers import BaseRetriever\n",
|
||||
"from llama_index.multi_modal_llms.openai import OpenAIMultiModal\n",
|
||||
"from llama_index.core.schema import ImageNode, NodeWithScore, MetadataMode\n",
|
||||
"from llama_index.core.prompts import PromptTemplate\n",
|
||||
"from llama_index.core.base.response.schema import Response\n",
|
||||
"from typing import Optional\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"gpt_4o = OpenAIMultiModal(model=\"gpt-4o\", max_new_tokens=4096)\n",
|
||||
"\n",
|
||||
"QA_PROMPT_TMPL = \"\"\"\\\n",
|
||||
"Below we give parsed text from slides in two different formats, as well as the image.\n",
|
||||
"\n",
|
||||
"We parse the text in both 'markdown' mode as well as 'raw text' mode. Markdown mode attempts \\\n",
|
||||
"to convert relevant diagrams into tables, whereas raw text tries to maintain the rough spatial \\\n",
|
||||
"layout of the text.\n",
|
||||
"\n",
|
||||
"Use the image information first and foremost. ONLY use the text/markdown information \n",
|
||||
"if you can't understand the image.\n",
|
||||
"\n",
|
||||
"---------------------\n",
|
||||
"{context_str}\n",
|
||||
"---------------------\n",
|
||||
"Given the context information and not prior knowledge, answer the query. Explain whether you got the answer\n",
|
||||
"from the parsed markdown or raw text or image, and if there's discrepancies, and your reasoning for the final answer.\n",
|
||||
"\n",
|
||||
"Query: {query_str}\n",
|
||||
"Answer: \"\"\"\n",
|
||||
"\n",
|
||||
"QA_PROMPT = PromptTemplate(QA_PROMPT_TMPL)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class MultimodalQueryEngine(CustomQueryEngine):\n",
|
||||
" \"\"\"Custom multimodal Query Engine.\n",
|
||||
"\n",
|
||||
" Takes in a retriever to retrieve a set of document nodes.\n",
|
||||
" Also takes in a prompt template and multimodal model.\n",
|
||||
"\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" qa_prompt: PromptTemplate\n",
|
||||
" retriever: BaseRetriever\n",
|
||||
" multi_modal_llm: OpenAIMultiModal\n",
|
||||
"\n",
|
||||
" def __init__(self, qa_prompt: Optional[PromptTemplate] = None, **kwargs) -> None:\n",
|
||||
" \"\"\"Initialize.\"\"\"\n",
|
||||
" super().__init__(qa_prompt=qa_prompt or QA_PROMPT, **kwargs)\n",
|
||||
"\n",
|
||||
" def custom_query(self, query_str: str):\n",
|
||||
" # retrieve text nodes\n",
|
||||
" nodes = self.retriever.retrieve(query_str)\n",
|
||||
" # create ImageNode items from text nodes\n",
|
||||
" image_nodes = [\n",
|
||||
" NodeWithScore(node=ImageNode(image_path=n.metadata[\"image_path\"]))\n",
|
||||
" for n in nodes\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" # create context string from text nodes, dump into the prompt\n",
|
||||
" context_str = \"\\n\\n\".join(\n",
|
||||
" [r.get_content(metadata_mode=MetadataMode.LLM) for r in nodes]\n",
|
||||
" )\n",
|
||||
" fmt_prompt = self.qa_prompt.format(context_str=context_str, query_str=query_str)\n",
|
||||
"\n",
|
||||
" # synthesize an answer from formatted text and images\n",
|
||||
" llm_response = self.multi_modal_llm.complete(\n",
|
||||
" prompt=fmt_prompt,\n",
|
||||
" image_documents=[image_node.node for image_node in image_nodes],\n",
|
||||
" )\n",
|
||||
" return Response(\n",
|
||||
" response=str(llm_response),\n",
|
||||
" source_nodes=nodes,\n",
|
||||
" metadata={\"text_nodes\": text_nodes, \"image_nodes\": image_nodes},\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" return response"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "0890be59-fb12-4bb5-959b-b2d9600f7774",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"query_engine = MultimodalQueryEngine(\n",
|
||||
" retriever=index.as_retriever(similarity_top_k=9), multi_modal_llm=gpt_4o\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a92aa4f1-7501-4711-b054-f02338e54e74",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Define Baseline\n",
|
||||
"\n",
|
||||
"In addition, we define a \"baseline\" where we rely only on text-based indexing. Here we define an index using only the nodes that are parsed in text-mode from LlamaParse. \n",
|
||||
"\n",
|
||||
"**NOTE**: We don't currently include the markdown-parsed text because that was parsed with GPT-4o, so already uses a multimodal model during the text extraction phase.\n",
|
||||
"\n",
|
||||
"It is of course a valid experiment to compare RAG where multimodal extraction only happens during indexing, vs. the current multimodal RAG implementation where images are fed during synthesis to the LLM. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "c0b15a48-d177-4666-aec2-98ee90664642",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def get_nodes(docs):\n",
|
||||
" \"\"\"Split docs into nodes, by separator.\"\"\"\n",
|
||||
" nodes = []\n",
|
||||
" for doc in docs:\n",
|
||||
" doc_chunks = doc.text.split(\"\\n---\\n\")\n",
|
||||
" for doc_chunk in doc_chunks:\n",
|
||||
" node = TextNode(\n",
|
||||
" text=doc_chunk,\n",
|
||||
" metadata=deepcopy(doc.metadata),\n",
|
||||
" )\n",
|
||||
" nodes.append(node)\n",
|
||||
"\n",
|
||||
" return nodes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "2065d2c6-d6ba-4ee3-8e9e-dbc83cbcec1b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"base_nodes = get_nodes(docs_text)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "bcaea1a8-26c9-4385-8f62-32855aa898b6",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Our Differentiated Portfolio: Deep; Durable and Diverse\n",
|
||||
" 20 BBOE of Resource Diverse Production Base\n",
|
||||
" Under $40/BBL Cost of Supply 10-Year Plan Cumulative Production (BBOE)\n",
|
||||
" S50 S32/BBL Lower 48 Alaska\n",
|
||||
" Average Cost of Supply\n",
|
||||
" 3 $40 GKA GWA\n",
|
||||
" GPA WNS\n",
|
||||
" $30 EMENA\n",
|
||||
" 3 Norway\n",
|
||||
" 8 $20\n",
|
||||
" E Qatar Libya\n",
|
||||
" Asia Pacific Canada\n",
|
||||
" $10 Permian\n",
|
||||
" APLNG Montney\n",
|
||||
" S0\n",
|
||||
" 10 15 20 Bakken\n",
|
||||
" Resource (BBOE) Eagle Ford Other Malaysia ChinaSurmont\n",
|
||||
" Lower 48 Canada Alaska EMENA Asia Pacific\n",
|
||||
"Costs assumemid-cycle price environment of S60/BBL WTI:\n",
|
||||
" ConocoPhillips\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(base_nodes[13].get_content(metadata_mode=\"all\"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "f6bcfbc6-4e9b-41ad-ad81-1c4245b95cd5",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"base_index = VectorStoreIndex(base_nodes, embed_model=embed_model)\n",
|
||||
"base_query_engine = base_index.as_query_engine(llm=llm, similarity_top_k=9)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1f94ef26-0df5-4468-a156-903d686f02ce",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Build a Multimodal Agent\n",
|
||||
"\n",
|
||||
"Build an agent around the multimodal query engine. This gives you agent capabilities like query planning/decomposition and memory around a central QA interface."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "5b7a8c5f-39fc-4d04-8c56-3642f5718437",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from llama_index.core.tools import QueryEngineTool\n",
|
||||
"from llama_index.core.agent import FunctionCallingAgentWorker\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"vector_tool = QueryEngineTool.from_defaults(\n",
|
||||
" query_engine=query_engine,\n",
|
||||
" name=\"vector_tool\",\n",
|
||||
" description=(\n",
|
||||
" \"Useful for retrieving specific context from the data. Do NOT select if question asks for a summary of the data.\"\n",
|
||||
" ),\n",
|
||||
")\n",
|
||||
"agent = FunctionCallingAgentWorker.from_tools(\n",
|
||||
" [vector_tool], llm=llm, verbose=True\n",
|
||||
").as_agent()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "2b4f7eb1-d247-45fa-bb41-c02fc353a22a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# define a similar agent for the baseline\n",
|
||||
"base_vector_tool = QueryEngineTool.from_defaults(\n",
|
||||
" query_engine=base_query_engine,\n",
|
||||
" name=\"vector_tool\",\n",
|
||||
" description=(\n",
|
||||
" \"Useful for retrieving specific context from the data. Do NOT select if question asks for a summary of the data.\"\n",
|
||||
" ),\n",
|
||||
")\n",
|
||||
"base_agent = FunctionCallingAgentWorker.from_tools(\n",
|
||||
" [base_vector_tool], llm=llm, verbose=True\n",
|
||||
").as_agent()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2336f98b-c0a1-413a-849d-8a89bacb90b5",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Try out Queries\n",
|
||||
"\n",
|
||||
"Let's try out queries against these documents and compare against each other."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d78e53cf-35cb-4ef8-b03e-1b47ba15ae64",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Added user message to memory: Tell me about the diverse geographies where Conoco Phillips has a production base\n",
|
||||
"=== Calling Function ===\n",
|
||||
"Calling function: vector_tool with args: {\"input\": \"Conoco Phillips production base geographies\"}\n",
|
||||
"=== Function Output ===\n",
|
||||
"ConocoPhillips' production base geographies include:\n",
|
||||
"\n",
|
||||
"1. **Lower 48** (Permian, Eagle Ford, Bakken, Other)\n",
|
||||
"2. **Alaska** (GKA, GWA, GPA, WNS)\n",
|
||||
"3. **EMENA** (Norway, Libya, Qatar)\n",
|
||||
"4. **Asia Pacific** (APLNG, Malaysia, China)\n",
|
||||
"5. **Canada** (Montney, Surmont)\n",
|
||||
"\n",
|
||||
"This information was derived from the image on page 14, which provides a detailed breakdown of the diverse production base and the regions involved. The parsed markdown and raw text also support this information, but the image provides the clearest and most comprehensive view. There are no discrepancies between the image and the parsed text in this case.\n",
|
||||
"=== LLM Response ===\n",
|
||||
"ConocoPhillips has a diverse production base spread across various geographies, including:\n",
|
||||
"\n",
|
||||
"1. **Lower 48**:\n",
|
||||
" - Permian Basin\n",
|
||||
" - Eagle Ford\n",
|
||||
" - Bakken\n",
|
||||
" - Other regions within the continental United States\n",
|
||||
"\n",
|
||||
"2. **Alaska**:\n",
|
||||
" - Greater Kuparuk Area (GKA)\n",
|
||||
" - Greater Prudhoe Area (GPA)\n",
|
||||
" - Greater Willow Area (GWA)\n",
|
||||
" - Western North Slope (WNS)\n",
|
||||
"\n",
|
||||
"3. **EMENA (Europe, Middle East, and North Africa)**:\n",
|
||||
" - Norway\n",
|
||||
" - Libya\n",
|
||||
" - Qatar\n",
|
||||
"\n",
|
||||
"4. **Asia Pacific**:\n",
|
||||
" - Australia Pacific LNG (APLNG)\n",
|
||||
" - Malaysia\n",
|
||||
" - China\n",
|
||||
"\n",
|
||||
"5. **Canada**:\n",
|
||||
" - Montney\n",
|
||||
" - Surmont\n",
|
||||
"\n",
|
||||
"These regions highlight the global reach and diverse geographical footprint of ConocoPhillips' production operations.\n",
|
||||
"Added user message to memory: Tell me about the diverse geographies where Conoco Phillips has a production base\n",
|
||||
"=== Calling Function ===\n",
|
||||
"Calling function: vector_tool with args: {\"input\": \"diverse geographies where Conoco Phillips has a production base\"}\n",
|
||||
"=== Function Output ===\n",
|
||||
"ConocoPhillips has a diverse production base that includes the Lower 48 (Permian, Bakken, Eagle Ford), Alaska, Canada (Montney, Surmont), EMENA (Norway, Libya), Asia Pacific (Malaysia, China, APLNG), and Qatar.\n",
|
||||
"=== LLM Response ===\n",
|
||||
"ConocoPhillips has a diverse production base spanning several key geographies:\n",
|
||||
"\n",
|
||||
"1. **Lower 48 (United States)**: This includes major production areas such as the Permian Basin, Bakken Formation, and Eagle Ford Shale.\n",
|
||||
"2. **Alaska**: Significant operations in the North Slope region.\n",
|
||||
"3. **Canada**: Operations in the Montney Formation and the Surmont oil sands project.\n",
|
||||
"4. **EMENA (Europe, Middle East, and North Africa)**: Notable operations in Norway and Libya.\n",
|
||||
"5. **Asia Pacific**: Includes operations in Malaysia, China, and the Australia Pacific LNG (APLNG) project.\n",
|
||||
"6. **Qatar**: Involvement in the country's energy sector.\n",
|
||||
"\n",
|
||||
"These regions highlight the company's extensive and varied geographical footprint in the energy production industry.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"query = (\n",
|
||||
" \"Tell me about the diverse geographies where Conoco Phillips has a production base\"\n",
|
||||
")\n",
|
||||
"response = agent.query(query)\n",
|
||||
"base_response = base_agent.query(query)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "355d2aa4-c26f-480e-b512-4446acbd9227",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"ConocoPhillips has a diverse production base spread across various geographies, including:\n",
|
||||
"\n",
|
||||
"1. **Lower 48**:\n",
|
||||
" - Permian Basin\n",
|
||||
" - Eagle Ford\n",
|
||||
" - Bakken\n",
|
||||
" - Other regions within the continental United States\n",
|
||||
"\n",
|
||||
"2. **Alaska**:\n",
|
||||
" - Greater Kuparuk Area (GKA)\n",
|
||||
" - Greater Prudhoe Area (GPA)\n",
|
||||
" - Greater Willow Area (GWA)\n",
|
||||
" - Western North Slope (WNS)\n",
|
||||
"\n",
|
||||
"3. **EMENA (Europe, Middle East, and North Africa)**:\n",
|
||||
" - Norway\n",
|
||||
" - Libya\n",
|
||||
" - Qatar\n",
|
||||
"\n",
|
||||
"4. **Asia Pacific**:\n",
|
||||
" - Australia Pacific LNG (APLNG)\n",
|
||||
" - Malaysia\n",
|
||||
" - China\n",
|
||||
"\n",
|
||||
"5. **Canada**:\n",
|
||||
" - Montney\n",
|
||||
" - Surmont\n",
|
||||
"\n",
|
||||
"These regions highlight the global reach and diverse geographical footprint of ConocoPhillips' production operations.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(str(response))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d584c560-8f49-4c10-a4db-2e0d3b7085d2",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"page_num: 14\n",
|
||||
"image_path: data_images/1ddd5654-062b-4e19-b488-d66efc9c509d-page_12.jpg\n",
|
||||
"parsed_text_markdown: # Our Differentiated Portfolio: Deep, Durable and Diverse\n",
|
||||
"\n",
|
||||
"## ~20 BBOE of Resource\n",
|
||||
"Under $40/BBL Cost of Supply\n",
|
||||
"\n",
|
||||
"### ~ $32/BBL\n",
|
||||
"Average Cost of Supply\n",
|
||||
"\n",
|
||||
"### WTI Cost of Supply ($/BBL)\n",
|
||||
"\n",
|
||||
"| Cost ($/BBL) | Resource (BBOE) |\n",
|
||||
"|--------------|-----------------|\n",
|
||||
"| $0 | 0 |\n",
|
||||
"| $10 | |\n",
|
||||
"| $20 | |\n",
|
||||
"| $30 | |\n",
|
||||
"| $40 | |\n",
|
||||
"| $50 | |\n",
|
||||
"\n",
|
||||
"- **Legend:**\n",
|
||||
" - Lower 48\n",
|
||||
" - Canada\n",
|
||||
" - Alaska\n",
|
||||
" - EMENA\n",
|
||||
" - Asia Pacific\n",
|
||||
"\n",
|
||||
"*Costs assume a mid-cycle price environment of $60/BBL WTI.*\n",
|
||||
"\n",
|
||||
"## Diverse Production Base\n",
|
||||
"10-Year Plan Cumulative Production (BBOE)\n",
|
||||
"\n",
|
||||
"| Region | Sub-region |\n",
|
||||
"|--------------|-----------------|\n",
|
||||
"| Lower 48 | Permian |\n",
|
||||
"| | Eagle Ford |\n",
|
||||
"| | Bakken |\n",
|
||||
"| | Other |\n",
|
||||
"| Alaska | GKA |\n",
|
||||
"| | GWA |\n",
|
||||
"| | GPA |\n",
|
||||
"| | WNS |\n",
|
||||
"| EMENA | Norway |\n",
|
||||
"| | Libya |\n",
|
||||
"| | Qatar |\n",
|
||||
"| Asia Pacific | APLNG |\n",
|
||||
"| | Malaysia |\n",
|
||||
"| | China |\n",
|
||||
"| Canada | Montney |\n",
|
||||
"| | Surmont |\n",
|
||||
"parsed_text: Our Differentiated Portfolio: Deep; Durable and Diverse\n",
|
||||
" 20 BBOE of Resource Diverse Production Base\n",
|
||||
" Under $40/BBL Cost of Supply 10-Year Plan Cumulative Production (BBOE)\n",
|
||||
" S50 S32/BBL Lower 48 Alaska\n",
|
||||
" Average Cost of Supply\n",
|
||||
" 3 $40 GKA GWA\n",
|
||||
" GPA WNS\n",
|
||||
" $30 EMENA\n",
|
||||
" 3 Norway\n",
|
||||
" 8 $20\n",
|
||||
" E Qatar Libya\n",
|
||||
" Asia Pacific Canada\n",
|
||||
" $10 Permian\n",
|
||||
" APLNG Montney\n",
|
||||
" S0\n",
|
||||
" 10 15 20 Bakken\n",
|
||||
" Resource (BBOE) Eagle Ford Other Malaysia ChinaSurmont\n",
|
||||
" Lower 48 Canada Alaska EMENA Asia Pacific\n",
|
||||
"Costs assumemid-cycle price environment of S60/BBL WTI:\n",
|
||||
" ConocoPhillips\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(response.source_nodes[7].get_content(metadata_mode=\"all\"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d21d694b-6618-4d04-a6f6-8b0c2625f539",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"ConocoPhillips has a diverse production base spanning several key geographies:\n",
|
||||
"\n",
|
||||
"1. **Lower 48 (United States)**: This includes major production areas such as the Permian Basin, Bakken Formation, and Eagle Ford Shale.\n",
|
||||
"2. **Alaska**: Significant operations in the North Slope region.\n",
|
||||
"3. **Canada**: Operations in the Montney Formation and the Surmont oil sands project.\n",
|
||||
"4. **EMENA (Europe, Middle East, and North Africa)**: Notable operations in Norway and Libya.\n",
|
||||
"5. **Asia Pacific**: Includes operations in Malaysia, China, and the Australia Pacific LNG (APLNG) project.\n",
|
||||
"6. **Qatar**: Involvement in the country's energy sector.\n",
|
||||
"\n",
|
||||
"These regions highlight the company's extensive and varied geographical footprint in the energy production industry.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(str(base_response))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d3afccae-ad8d-4c5d-9d93-810dba413a5d",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Our Differentiated Portfolio: Deep; Durable and Diverse\n",
|
||||
" 20 BBOE of Resource Diverse Production Base\n",
|
||||
" Under $40/BBL Cost of Supply 10-Year Plan Cumulative Production (BBOE)\n",
|
||||
" S50 S32/BBL Lower 48 Alaska\n",
|
||||
" Average Cost of Supply\n",
|
||||
" 3 $40 GKA GWA\n",
|
||||
" GPA WNS\n",
|
||||
" $30 EMENA\n",
|
||||
" 3 Norway\n",
|
||||
" 8 $20\n",
|
||||
" E Qatar Libya\n",
|
||||
" Asia Pacific Canada\n",
|
||||
" $10 Permian\n",
|
||||
" APLNG Montney\n",
|
||||
" S0\n",
|
||||
" 10 15 20 Bakken\n",
|
||||
" Resource (BBOE) Eagle Ford Other Malaysia ChinaSurmont\n",
|
||||
" Lower 48 Canada Alaska EMENA Asia Pacific\n",
|
||||
"Costs assumemid-cycle price environment of S60/BBL WTI:\n",
|
||||
" ConocoPhillips\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(base_response.source_nodes[1].get_content(metadata_mode=\"all\"))"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "llama_index_v3",
|
||||
"language": "python",
|
||||
"name": "llama_index_v3"
|
||||
},
|
||||
"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
|
||||
}
|
||||
|
After Width: | Height: | Size: 271 KiB |
@@ -0,0 +1,834 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"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>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This cookbook shows how to use LlamaParse and OpenAI's multimodal models to query over IKEA instruction manual PDFs, which mainly contain images and diagrams to show how one can assemble the product.\n",
|
||||
"\n",
|
||||
"LlamaParse and multimodal LLMs can interpret these diagrams and translate them into textual instructions. With textual assistance, confusing visual instructions within the IKEA product manuals can be made easier to understand and interpret. Additionally, textual instructions can be helpful for those who are visually impaired."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Install and Setup\n",
|
||||
"\n",
|
||||
"Install LlamaIndex, download the data, and apply `nest_asyncio`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install llama-index llama-parse llama-index-multi-modal-llms-openai git+https://github.com/openai/CLIP.git"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!wget https://github.com/user-attachments/files/16461058/data.zip -O data.zip\n",
|
||||
"!unzip -o data.zip\n",
|
||||
"!rm data.zip"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import nest_asyncio\n",
|
||||
"\n",
|
||||
"nest_asyncio.apply()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Set up your OpenAI and LlamaCloud keys."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"OPENAI_API_KEY\"] = \"<Your OpenAI API Key>\"\n",
|
||||
"os.environ[\"LLAMA_CLOUD_API_KEY\"] = \"<Your LlamaCloud API Key>\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Code Implementation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Set up LlamaParse. We will parse the PDF files into markdown and use the GPT-4o multimodal model to parse the PDFs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Load data from the parser."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from llama_parse import LlamaParse\n",
|
||||
"\n",
|
||||
"parser = LlamaParse(\n",
|
||||
" result_type=\"markdown\",\n",
|
||||
" parsing_instruction=\"You are given IKEA assembly instruction manuals\",\n",
|
||||
" use_vendor_multimodal_model=True,\n",
|
||||
" vendor_multimodal_model_name=\"openai-gpt4o\",\n",
|
||||
" show_progress=True,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"DATA_DIR = \"data\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_data_files(data_dir=DATA_DIR) -> list[str]:\n",
|
||||
" files = []\n",
|
||||
" for f in os.listdir(data_dir):\n",
|
||||
" fname = os.path.join(data_dir, f)\n",
|
||||
" if os.path.isfile(fname):\n",
|
||||
" files.append(fname)\n",
|
||||
" return files\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"files = get_data_files()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Load data into docs, and save images from PDFs into `data_images` directory."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"md_json_objs = parser.get_json_result(files)\n",
|
||||
"md_json_list = md_json_objs[0][\"pages\"]\n",
|
||||
"image_dicts = parser.get_images(md_json_objs, download_path=\"data_images\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Create helper functions to create a list of `TextNode`s from the markdown tables to feed into the `VectorStoreIndex`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import re\n",
|
||||
"from pathlib import Path\n",
|
||||
"import typing as t\n",
|
||||
"from llama_index.core.schema import TextNode\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_page_number(file_name):\n",
|
||||
" \"\"\"Gets page number of images using regex on file names\"\"\"\n",
|
||||
" match = re.search(r\"-page-(\\d+)\\.jpg$\", str(file_name))\n",
|
||||
" if match:\n",
|
||||
" return int(match.group(1))\n",
|
||||
" return 0\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def _get_sorted_image_files(image_dir):\n",
|
||||
" \"\"\"Get image files sorted by page.\"\"\"\n",
|
||||
" raw_files = [f for f in list(Path(image_dir).iterdir()) if f.is_file()]\n",
|
||||
" sorted_files = sorted(raw_files, key=get_page_number)\n",
|
||||
" return sorted_files\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_text_nodes(json_dicts, image_dir) -> t.List[TextNode]:\n",
|
||||
" \"\"\"Creates nodes from json + images\"\"\"\n",
|
||||
"\n",
|
||||
" nodes = []\n",
|
||||
"\n",
|
||||
" docs = [doc[\"md\"] for doc in json_dicts] # extract text\n",
|
||||
" image_files = _get_sorted_image_files(image_dir) # extract images\n",
|
||||
"\n",
|
||||
" for idx, doc in enumerate(docs):\n",
|
||||
" # adds both a text node and the corresponding image node (jpg of the page) for each page\n",
|
||||
" node = TextNode(\n",
|
||||
" text=doc,\n",
|
||||
" metadata={\"image_path\": str(image_files[idx]), \"page_num\": idx + 1},\n",
|
||||
" )\n",
|
||||
" nodes.append(node)\n",
|
||||
"\n",
|
||||
" return nodes\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"text_nodes = get_text_nodes(md_json_list, \"data_images\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Index the documents."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from llama_index.core import (\n",
|
||||
" VectorStoreIndex,\n",
|
||||
" StorageContext,\n",
|
||||
" load_index_from_storage,\n",
|
||||
" Settings,\n",
|
||||
")\n",
|
||||
"from llama_index.embeddings.openai import OpenAIEmbedding\n",
|
||||
"from llama_index.llms.openai import OpenAI\n",
|
||||
"\n",
|
||||
"embed_model = OpenAIEmbedding(model=\"text-embedding-3-large\")\n",
|
||||
"llm = OpenAI(\"gpt-4o\")\n",
|
||||
"\n",
|
||||
"Settings.llm = llm\n",
|
||||
"Settings.embed_model = embed_model\n",
|
||||
"\n",
|
||||
"if not os.path.exists(\"storage_ikea\"):\n",
|
||||
" index = VectorStoreIndex(text_nodes, embed_model=embed_model)\n",
|
||||
" index.storage_context.persist(persist_dir=\"./storage_ikea\")\n",
|
||||
"else:\n",
|
||||
" ctx = StorageContext.from_defaults(persist_dir=\"./storage_ikea\")\n",
|
||||
" index = load_index_from_storage(ctx)\n",
|
||||
"\n",
|
||||
"retriever = index.as_retriever()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Create a custom query engine that uses GPT-4o's multimodal model."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from llama_index.core.query_engine import CustomQueryEngine\n",
|
||||
"from llama_index.core.retrievers import BaseRetriever\n",
|
||||
"from llama_index.multi_modal_llms.openai import OpenAIMultiModal\n",
|
||||
"from llama_index.core.schema import NodeWithScore, MetadataMode\n",
|
||||
"from llama_index.core.base.response.schema import Response\n",
|
||||
"from llama_index.core.prompts import PromptTemplate\n",
|
||||
"from llama_index.core.schema import ImageNode\n",
|
||||
"\n",
|
||||
"QA_PROMPT_TMPL = \"\"\"\\\n",
|
||||
"Below we give parsed text from slides in two different formats, as well as the image.\n",
|
||||
"\n",
|
||||
"We parse the text in both 'markdown' mode as well as 'raw text' mode. Markdown mode attempts \\\n",
|
||||
"to convert relevant diagrams into tables, whereas raw text tries to maintain the rough spatial \\\n",
|
||||
"layout of the text.\n",
|
||||
"\n",
|
||||
"Use the image information first and foremost. ONLY use the text/markdown information \n",
|
||||
"if you can't understand the image.\n",
|
||||
"\n",
|
||||
"---------------------\n",
|
||||
"{context_str}\n",
|
||||
"---------------------\n",
|
||||
"Given the context information and not prior knowledge, answer the query. Explain whether you got the answer\n",
|
||||
"from the parsed markdown or raw text or image, and if there's discrepancies, and your reasoning for the final answer.\n",
|
||||
"\n",
|
||||
"Query: {query_str}\n",
|
||||
"Answer: \"\"\"\n",
|
||||
"\n",
|
||||
"QA_PROMPT = PromptTemplate(QA_PROMPT_TMPL)\n",
|
||||
"\n",
|
||||
"gpt_4o_mm = OpenAIMultiModal(model=\"gpt-4o\", max_new_tokens=4096)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class MultimodalQueryEngine(CustomQueryEngine):\n",
|
||||
" qa_prompt: PromptTemplate\n",
|
||||
" retriever: BaseRetriever\n",
|
||||
" multi_modal_llm: OpenAIMultiModal\n",
|
||||
"\n",
|
||||
" def __init__(\n",
|
||||
" self,\n",
|
||||
" qa_prompt: PromptTemplate,\n",
|
||||
" retriever: BaseRetriever,\n",
|
||||
" multi_modal_llm: OpenAIMultiModal,\n",
|
||||
" ):\n",
|
||||
" super().__init__(\n",
|
||||
" qa_prompt=qa_prompt, retriever=retriever, multi_modal_llm=multi_modal_llm\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" def custom_query(self, query_str: str):\n",
|
||||
" # retrieve most relevant nodes\n",
|
||||
" nodes = self.retriever.retrieve(query_str)\n",
|
||||
"\n",
|
||||
" # create image nodes from the image associated with those nodes\n",
|
||||
" image_nodes = [\n",
|
||||
" NodeWithScore(node=ImageNode(image_path=n.node.metadata[\"image_path\"]))\n",
|
||||
" for n in nodes\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" # create context string from parsed markdown text\n",
|
||||
" ctx_str = \"\\n\\n\".join(\n",
|
||||
" [r.node.get_content(metadata_mode=MetadataMode.LLM) for r in nodes]\n",
|
||||
" )\n",
|
||||
" # prompt for the LLM\n",
|
||||
" fmt_prompt = self.qa_prompt.format(context_str=ctx_str, query_str=query_str)\n",
|
||||
"\n",
|
||||
" # use the multimodal LLM to interpret images and generate a response to the prompt\n",
|
||||
" llm_repsonse = self.multi_modal_llm.complete(\n",
|
||||
" prompt=fmt_prompt,\n",
|
||||
" image_documents=[image_node.node for image_node in image_nodes],\n",
|
||||
" )\n",
|
||||
" return Response(\n",
|
||||
" response=str(llm_repsonse),\n",
|
||||
" source_nodes=nodes,\n",
|
||||
" metadata={\"text_nodes\": text_nodes, \"image_nodes\": image_nodes},\n",
|
||||
" )"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Create a query engine instance."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"query_engine = MultimodalQueryEngine(\n",
|
||||
" qa_prompt=QA_PROMPT,\n",
|
||||
" retriever=index.as_retriever(similarity_top_k=9),\n",
|
||||
" multi_modal_llm=gpt_4o_mm,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"## Example Queries"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"The query asks about the parts included in the Uppspel, but the provided images and parsed text do not contain any information about the Uppspel. Instead, they contain information about other IKEA products such as SMÅGÖRA, FREDDE, and TUFFING.\n",
|
||||
"\n",
|
||||
"Therefore, based on the provided images and parsed text, I cannot determine the parts included in the Uppspel. The answer cannot be derived from the given information."
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Markdown object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from IPython.display import display, Markdown\n",
|
||||
"\n",
|
||||
"response = query_engine.query(\"What parts are included in the Uppspel?\")\n",
|
||||
"display(Markdown(str(response)))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"The Tuffing is a bunk bed frame with a minimalist design, featuring a metal frame and safety rails on the top bunk. The image provided shows the Tuffing bunk bed with a ladder for access to the top bunk and a simple, sturdy construction.\n",
|
||||
"\n",
|
||||
"I got the answer from the image provided. The image clearly shows the design and structure of the Tuffing bunk bed. There were no discrepancies between the parsed markdown or raw text and the image. The image was the primary source for understanding what the Tuffing looks like."
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Markdown object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"response = query_engine.query(\"What does the Tuffing look like?\")\n",
|
||||
"display(Markdown(str(response)))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"The query asks for step 4 of assembling the Nordli. Based on the provided information, step 4 is described in the parsed text as follows:\n",
|
||||
"\n",
|
||||
"**Step 4:**\n",
|
||||
"- Insert the provided tool into the hole as shown.\n",
|
||||
"- Ensure the structure is properly aligned and secure.\n",
|
||||
"- Push down firmly to lock the structure in place.\n",
|
||||
"\n",
|
||||
"This information was derived from the parsed text, as the image provided does not contain step-by-step instructions for the Nordli assembly. There are no discrepancies between the parsed markdown and raw text for this step."
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Markdown object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"response = query_engine.query(\"What is step 4 of assembling the Nordli?\")\n",
|
||||
"display(Markdown(str(response)))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"If you're confused with reading the manual, you should contact IKEA customer service for assistance. This information is derived from the image on page 2, which shows a person with a question mark next to an IKEA box and another person making a phone call to IKEA. This visual cue indicates that contacting IKEA customer service is the recommended action if you need help."
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Markdown object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"response = query_engine.query(\n",
|
||||
" \"What should I do if I'm confused with reading the manual?\"\n",
|
||||
")\n",
|
||||
"display(Markdown(str(response)))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"You can also create an agent around the query engine and chat with the agent."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from llama_index.core.agent import FunctionCallingAgentWorker\n",
|
||||
"from llama_index.core.tools import QueryEngineTool\n",
|
||||
"\n",
|
||||
"query_engine_tool = QueryEngineTool.from_defaults(\n",
|
||||
" query_engine=query_engine,\n",
|
||||
" name=\"query_engine_tool\",\n",
|
||||
" description=\"Useful for retrieving specific context from the data. Do NOT select if question asks for a summary of the data.\",\n",
|
||||
")\n",
|
||||
"agent = FunctionCallingAgentWorker.from_tools(\n",
|
||||
" [query_engine_tool], llm=llm, verbose=True\n",
|
||||
").as_agent()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Added user message to memory: Give a step-by-step instruction guide on how to assemble the Smagora\n",
|
||||
"=== Calling Function ===\n",
|
||||
"Calling function: query_engine_tool with args: {\"input\": \"step-by-step instruction guide on how to assemble the Smagora\"}\n",
|
||||
"=== Function Output ===\n",
|
||||
"The step-by-step instruction guide on how to assemble the Smågåra crib is provided in the images. The images show detailed visual instructions for each step of the assembly process, including the tools required, the parts involved, and the specific actions to be taken.\n",
|
||||
"\n",
|
||||
"Here is a summary of the steps based on the images:\n",
|
||||
"\n",
|
||||
"1. **Tools Required**:\n",
|
||||
" - Flathead screwdriver\n",
|
||||
" - Phillips screwdriver\n",
|
||||
" - Hammer\n",
|
||||
"\n",
|
||||
"2. **Preparation**:\n",
|
||||
" - Do not assemble alone; assemble with a partner.\n",
|
||||
" - Do not assemble on a hard surface; use a soft surface to avoid damage.\n",
|
||||
" - If you have questions or need assistance, contact IKEA customer service.\n",
|
||||
"\n",
|
||||
"3. **Step 1**:\n",
|
||||
" - Insert 12 screws into the designated holes on the frame.\n",
|
||||
"\n",
|
||||
"4. **Step 2**:\n",
|
||||
" - Align the side panels with the headboard and footboard.\n",
|
||||
" - Use 4 connectors and secure them with bolts and washers.\n",
|
||||
" - Tighten using the provided tool.\n",
|
||||
" - Carefully flip the structure as shown.\n",
|
||||
"\n",
|
||||
"5. **Step 3**:\n",
|
||||
" - Use the provided Allen key to tighten the screws into the designated holes.\n",
|
||||
" - Ensure the screws are properly aligned and tightened.\n",
|
||||
" - Repeat this process for all four screws.\n",
|
||||
" - Make sure the screws are flush with the surface.\n",
|
||||
"\n",
|
||||
"6. **Step 4**:\n",
|
||||
" - Insert the provided tool into the hole as shown.\n",
|
||||
" - Ensure the structure is properly aligned and secure.\n",
|
||||
" - Push down firmly to lock the structure in place.\n",
|
||||
"\n",
|
||||
"7. **Step 5**:\n",
|
||||
" - Insert 4 dowels into the designated holes on the board.\n",
|
||||
"\n",
|
||||
"8. **Step 6**:\n",
|
||||
" - Align the board with the dowels and insert it into the corresponding slots on the frame.\n",
|
||||
"\n",
|
||||
"9. **Step 7**:\n",
|
||||
" - Insert the top panel into the side panels.\n",
|
||||
" - Use 4 screws to secure the top panel.\n",
|
||||
" - Ensure the screws are properly aligned and tightened using the provided tool.\n",
|
||||
"\n",
|
||||
"10. **Step 8**:\n",
|
||||
" - Carefully flip the assembled structure upright.\n",
|
||||
" - Use 2 screws to secure the bottom panel.\n",
|
||||
" - Tighten the screws with the provided tool.\n",
|
||||
"\n",
|
||||
"These steps are derived from the images provided, which offer a clear and detailed visual guide for assembling the Smågåra crib.\n",
|
||||
"=== LLM Response ===\n",
|
||||
"Here is a step-by-step instruction guide on how to assemble the Smågåra crib:\n",
|
||||
"\n",
|
||||
"### Tools Required:\n",
|
||||
"- Flathead screwdriver\n",
|
||||
"- Phillips screwdriver\n",
|
||||
"- Hammer\n",
|
||||
"- Allen key (provided in the package)\n",
|
||||
"\n",
|
||||
"### Preparation:\n",
|
||||
"- **Safety First**: Assemble with a partner to ensure safety and ease.\n",
|
||||
"- **Surface**: Assemble on a soft surface to avoid damaging the parts.\n",
|
||||
"- **Assistance**: If you have questions or need help, contact IKEA customer service.\n",
|
||||
"\n",
|
||||
"### Step-by-Step Assembly:\n",
|
||||
"\n",
|
||||
"#### Step 1: Insert Screws into the Frame\n",
|
||||
"1. Insert 12 screws into the designated holes on the frame.\n",
|
||||
"2. Ensure the screws are properly aligned.\n",
|
||||
"\n",
|
||||
"#### Step 2: Align and Secure Side Panels\n",
|
||||
"1. Align the side panels with the headboard and footboard.\n",
|
||||
"2. Use 4 connectors and secure them with bolts and washers.\n",
|
||||
"3. Tighten the bolts using the provided tool.\n",
|
||||
"4. Carefully flip the structure as shown in the instructions.\n",
|
||||
"\n",
|
||||
"#### Step 3: Tighten Screws\n",
|
||||
"1. Use the provided Allen key to tighten the screws into the designated holes.\n",
|
||||
"2. Ensure the screws are properly aligned and tightened.\n",
|
||||
"3. Repeat this process for all four screws.\n",
|
||||
"4. Make sure the screws are flush with the surface.\n",
|
||||
"\n",
|
||||
"#### Step 4: Lock the Structure\n",
|
||||
"1. Insert the provided tool into the hole as shown.\n",
|
||||
"2. Ensure the structure is properly aligned and secure.\n",
|
||||
"3. Push down firmly to lock the structure in place.\n",
|
||||
"\n",
|
||||
"#### Step 5: Insert Dowels\n",
|
||||
"1. Insert 4 dowels into the designated holes on the board.\n",
|
||||
"\n",
|
||||
"#### Step 6: Align and Insert the Board\n",
|
||||
"1. Align the board with the dowels.\n",
|
||||
"2. Insert the board into the corresponding slots on the frame.\n",
|
||||
"\n",
|
||||
"#### Step 7: Secure the Top Panel\n",
|
||||
"1. Insert the top panel into the side panels.\n",
|
||||
"2. Use 4 screws to secure the top panel.\n",
|
||||
"3. Ensure the screws are properly aligned and tightened using the provided tool.\n",
|
||||
"\n",
|
||||
"#### Step 8: Secure the Bottom Panel\n",
|
||||
"1. Carefully flip the assembled structure upright.\n",
|
||||
"2. Use 2 screws to secure the bottom panel.\n",
|
||||
"3. Tighten the screws with the provided tool.\n",
|
||||
"\n",
|
||||
"By following these steps, you should be able to assemble the Smågåra crib successfully. If you encounter any issues, refer to the visual instructions provided in the package or contact IKEA customer service for assistance.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"Here is a step-by-step instruction guide on how to assemble the Smågåra crib:\n",
|
||||
"\n",
|
||||
"### Tools Required:\n",
|
||||
"- Flathead screwdriver\n",
|
||||
"- Phillips screwdriver\n",
|
||||
"- Hammer\n",
|
||||
"- Allen key (provided in the package)\n",
|
||||
"\n",
|
||||
"### Preparation:\n",
|
||||
"- **Safety First**: Assemble with a partner to ensure safety and ease.\n",
|
||||
"- **Surface**: Assemble on a soft surface to avoid damaging the parts.\n",
|
||||
"- **Assistance**: If you have questions or need help, contact IKEA customer service.\n",
|
||||
"\n",
|
||||
"### Step-by-Step Assembly:\n",
|
||||
"\n",
|
||||
"#### Step 1: Insert Screws into the Frame\n",
|
||||
"1. Insert 12 screws into the designated holes on the frame.\n",
|
||||
"2. Ensure the screws are properly aligned.\n",
|
||||
"\n",
|
||||
"#### Step 2: Align and Secure Side Panels\n",
|
||||
"1. Align the side panels with the headboard and footboard.\n",
|
||||
"2. Use 4 connectors and secure them with bolts and washers.\n",
|
||||
"3. Tighten the bolts using the provided tool.\n",
|
||||
"4. Carefully flip the structure as shown in the instructions.\n",
|
||||
"\n",
|
||||
"#### Step 3: Tighten Screws\n",
|
||||
"1. Use the provided Allen key to tighten the screws into the designated holes.\n",
|
||||
"2. Ensure the screws are properly aligned and tightened.\n",
|
||||
"3. Repeat this process for all four screws.\n",
|
||||
"4. Make sure the screws are flush with the surface.\n",
|
||||
"\n",
|
||||
"#### Step 4: Lock the Structure\n",
|
||||
"1. Insert the provided tool into the hole as shown.\n",
|
||||
"2. Ensure the structure is properly aligned and secure.\n",
|
||||
"3. Push down firmly to lock the structure in place.\n",
|
||||
"\n",
|
||||
"#### Step 5: Insert Dowels\n",
|
||||
"1. Insert 4 dowels into the designated holes on the board.\n",
|
||||
"\n",
|
||||
"#### Step 6: Align and Insert the Board\n",
|
||||
"1. Align the board with the dowels.\n",
|
||||
"2. Insert the board into the corresponding slots on the frame.\n",
|
||||
"\n",
|
||||
"#### Step 7: Secure the Top Panel\n",
|
||||
"1. Insert the top panel into the side panels.\n",
|
||||
"2. Use 4 screws to secure the top panel.\n",
|
||||
"3. Ensure the screws are properly aligned and tightened using the provided tool.\n",
|
||||
"\n",
|
||||
"#### Step 8: Secure the Bottom Panel\n",
|
||||
"1. Carefully flip the assembled structure upright.\n",
|
||||
"2. Use 2 screws to secure the bottom panel.\n",
|
||||
"3. Tighten the screws with the provided tool.\n",
|
||||
"\n",
|
||||
"By following these steps, you should be able to assemble the Smågåra crib successfully. If you encounter any issues, refer to the visual instructions provided in the package or contact IKEA customer service for assistance."
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Markdown object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"response = agent.chat(\n",
|
||||
" \"Give a step-by-step instruction guide on how to assemble the Smagora\"\n",
|
||||
")\n",
|
||||
"display(Markdown(str(response)))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Added user message to memory: How do I assemble the Fredde?\n",
|
||||
"=== Calling Function ===\n",
|
||||
"Calling function: query_engine_tool with args: {\"input\": \"step-by-step instruction guide on how to assemble the Fredde\"}\n",
|
||||
"=== Function Output ===\n",
|
||||
"The query asks for a step-by-step instruction guide on how to assemble the Fredde. However, based on the provided images and parsed text, there is no specific mention or visual representation of the Fredde assembly instructions. The images and text provided are related to other IKEA products such as Tuffing and Smågöra, but not Fredde.\n",
|
||||
"\n",
|
||||
"Therefore, I cannot provide the step-by-step instructions for assembling the Fredde from the given information. If you have the specific instructions for Fredde, please provide them, and I can assist you further.\n",
|
||||
"=== LLM Response ===\n",
|
||||
"It appears that the specific step-by-step instructions for assembling the Fredde desk are not available in the provided data. However, I can offer a general guide based on typical assembly procedures for IKEA furniture. For the most accurate and detailed instructions, please refer to the assembly manual that comes with the product.\n",
|
||||
"\n",
|
||||
"### General Assembly Guide for Fredde Desk:\n",
|
||||
"\n",
|
||||
"#### Tools Required:\n",
|
||||
"- Phillips screwdriver\n",
|
||||
"- Flathead screwdriver\n",
|
||||
"- Allen key (usually provided in the package)\n",
|
||||
"- Hammer (if needed for dowels)\n",
|
||||
"\n",
|
||||
"### Step-by-Step Assembly:\n",
|
||||
"\n",
|
||||
"#### Step 1: Unpack and Organize\n",
|
||||
"1. **Unpack** all the parts and hardware.\n",
|
||||
"2. **Organize** the parts by type and size to make the assembly process easier.\n",
|
||||
"\n",
|
||||
"#### Step 2: Assemble the Main Frame\n",
|
||||
"1. **Connect the Side Panels**: Attach the side panels to the back panel using screws and dowels as indicated in the manual.\n",
|
||||
"2. **Secure the Bottom Panel**: Attach the bottom panel to the side panels.\n",
|
||||
"\n",
|
||||
"#### Step 3: Attach the Shelves\n",
|
||||
"1. **Install the Lower Shelves**: Insert the lower shelves into the designated slots and secure them with screws.\n",
|
||||
"2. **Install the Upper Shelves**: Repeat the process for the upper shelves.\n",
|
||||
"\n",
|
||||
"#### Step 4: Attach the Desktop\n",
|
||||
"1. **Align the Desktop**: Place the desktop on top of the frame, ensuring it is properly aligned.\n",
|
||||
"2. **Secure the Desktop**: Use screws to secure the desktop to the frame.\n",
|
||||
"\n",
|
||||
"#### Step 5: Install Additional Features\n",
|
||||
"1. **Attach Monitor Shelf**: If the Fredde desk includes a monitor shelf, attach it to the back panel using screws.\n",
|
||||
"2. **Install Side Extensions**: Attach any side extensions or additional shelves as per the instructions.\n",
|
||||
"\n",
|
||||
"#### Step 6: Final Adjustments\n",
|
||||
"1. **Check Stability**: Ensure all screws are tightened and the desk is stable.\n",
|
||||
"2. **Adjust Height**: If the desk has adjustable height features, set it to the desired height.\n",
|
||||
"\n",
|
||||
"#### Step 7: Clean Up\n",
|
||||
"1. **Remove Packaging**: Dispose of any packaging materials.\n",
|
||||
"2. **Organize Tools**: Put away your tools and clean the workspace.\n",
|
||||
"\n",
|
||||
"For the most accurate and detailed instructions, please refer to the assembly manual that comes with the Fredde desk. If you encounter any issues, IKEA customer service can provide additional support.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"It appears that the specific step-by-step instructions for assembling the Fredde desk are not available in the provided data. However, I can offer a general guide based on typical assembly procedures for IKEA furniture. For the most accurate and detailed instructions, please refer to the assembly manual that comes with the product.\n",
|
||||
"\n",
|
||||
"### General Assembly Guide for Fredde Desk:\n",
|
||||
"\n",
|
||||
"#### Tools Required:\n",
|
||||
"- Phillips screwdriver\n",
|
||||
"- Flathead screwdriver\n",
|
||||
"- Allen key (usually provided in the package)\n",
|
||||
"- Hammer (if needed for dowels)\n",
|
||||
"\n",
|
||||
"### Step-by-Step Assembly:\n",
|
||||
"\n",
|
||||
"#### Step 1: Unpack and Organize\n",
|
||||
"1. **Unpack** all the parts and hardware.\n",
|
||||
"2. **Organize** the parts by type and size to make the assembly process easier.\n",
|
||||
"\n",
|
||||
"#### Step 2: Assemble the Main Frame\n",
|
||||
"1. **Connect the Side Panels**: Attach the side panels to the back panel using screws and dowels as indicated in the manual.\n",
|
||||
"2. **Secure the Bottom Panel**: Attach the bottom panel to the side panels.\n",
|
||||
"\n",
|
||||
"#### Step 3: Attach the Shelves\n",
|
||||
"1. **Install the Lower Shelves**: Insert the lower shelves into the designated slots and secure them with screws.\n",
|
||||
"2. **Install the Upper Shelves**: Repeat the process for the upper shelves.\n",
|
||||
"\n",
|
||||
"#### Step 4: Attach the Desktop\n",
|
||||
"1. **Align the Desktop**: Place the desktop on top of the frame, ensuring it is properly aligned.\n",
|
||||
"2. **Secure the Desktop**: Use screws to secure the desktop to the frame.\n",
|
||||
"\n",
|
||||
"#### Step 5: Install Additional Features\n",
|
||||
"1. **Attach Monitor Shelf**: If the Fredde desk includes a monitor shelf, attach it to the back panel using screws.\n",
|
||||
"2. **Install Side Extensions**: Attach any side extensions or additional shelves as per the instructions.\n",
|
||||
"\n",
|
||||
"#### Step 6: Final Adjustments\n",
|
||||
"1. **Check Stability**: Ensure all screws are tightened and the desk is stable.\n",
|
||||
"2. **Adjust Height**: If the desk has adjustable height features, set it to the desired height.\n",
|
||||
"\n",
|
||||
"#### Step 7: Clean Up\n",
|
||||
"1. **Remove Packaging**: Dispose of any packaging materials.\n",
|
||||
"2. **Organize Tools**: Put away your tools and clean the workspace.\n",
|
||||
"\n",
|
||||
"For the most accurate and detailed instructions, please refer to the assembly manual that comes with the Fredde desk. If you encounter any issues, IKEA customer service can provide additional support."
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Markdown object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"response = agent.chat(\"How do I assemble the Fredde?\")\n",
|
||||
"display(Markdown(str(response)))"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "llama-parse-5ZmnAQ0r-py3.11",
|
||||
"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": 2
|
||||
}
|
||||
|
After Width: | Height: | Size: 986 KiB |
@@ -46,7 +46,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"os.environ[\"LLAMA_CLOUD_API_KEY\"] = \"<LLAMA_CLOUD_API_KEY>"
|
||||
"os.environ[\"LLAMA_CLOUD_API_KEY\"] = \"<LLAMA_CLOUD_API_KEY>\""
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -113,6 +113,7 @@
|
||||
" result_type=\"markdown\",\n",
|
||||
" # api_key=api_key,\n",
|
||||
" gpt4o_mode=True,\n",
|
||||
" split_by_page=True,\n",
|
||||
" # gpt4o_api_key=\"<gpt4o_api_key>\"\n",
|
||||
")"
|
||||
]
|
||||
@@ -127,7 +128,8 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Started parsing the file under job_id 1a934a50-59a9-4bb4-bbb7-ecefff228537\n"
|
||||
"Started parsing the file under job_id bf7d4619-3e26-479d-80e9-25702186ef32\n",
|
||||
"."
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -145,66 +147,6 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"# Impact Report\n",
|
||||
"## 2019\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"TESLA\n",
|
||||
"---\n",
|
||||
"# Introduction 03\n",
|
||||
"\n",
|
||||
"# Mission and Tesla Ecosystem 04\n",
|
||||
"\n",
|
||||
"# Environmental Impact 06\n",
|
||||
"- Lifecycle Analysis of Tesla Vehicles versus Average ICE\n",
|
||||
"- Battery Recycling\n",
|
||||
"- NOx, Particulates and Other Pollutants\n",
|
||||
"- Water Used per Vehicle Manufactured\n",
|
||||
"- Emissions Credits\n",
|
||||
"- Net Energy Impact of Our Products\n",
|
||||
"\n",
|
||||
"# Product Impact 20\n",
|
||||
"- Price Equivalency\n",
|
||||
"- Primary Driver\n",
|
||||
"- Long Distance Travel\n",
|
||||
"- Active Safety\n",
|
||||
"- Passive Safety\n",
|
||||
"- Tesla Safety Awards\n",
|
||||
"- Fire Safety\n",
|
||||
"- Cyber Security\n",
|
||||
"- Disaster Relief\n",
|
||||
"- Resilience of the Grid\n",
|
||||
"- Megapack\n",
|
||||
"- Solar Roof\n",
|
||||
"\n",
|
||||
"# Supply Chain 33\n",
|
||||
"- Responsible Material Sourcing\n",
|
||||
"- Cobalt Sourcing\n",
|
||||
"\n",
|
||||
"# People and Culture 37\n",
|
||||
"- Our Environmental, Health, and Safety Strategy\n",
|
||||
"- Safety Improvements\n",
|
||||
"- Case Study: Ergonomics and Model Y Design\n",
|
||||
"- Rewarding the Individual\n",
|
||||
"- Culture of Diversity and Inclusion\n",
|
||||
"- Workforce Development\n",
|
||||
"- Community Engagement\n",
|
||||
"- Employee Mobility and Transportation Programs\n",
|
||||
"- Corporate Governance\n",
|
||||
"\n",
|
||||
"# Appendix 52\n",
|
||||
"---\n",
|
||||
"# Introduction\n",
|
||||
"\n",
|
||||
"The very purpose of Tesla’s existence is to accelerate the world’s transition to sustainable energy. In furtherance of this mission, we are excited to publish our second annual Impact Report. Transparency and disclosure are important for our customers, employees, and shareholders, which is why we have expanded the Impact Report’s content this year.\n",
|
||||
"\n",
|
||||
"While many environmental reports focus on emissions generated by the manufacturing phase of products and future goals for energy consumption, we highlight the totality of the environmental impact of our products today. After all, the vast majority of emissions generated by vehicles today occur in the product-use phase—that is, when consumers are driving their vehicles. We believe that providing information on both sides of the manufacturing and consumer-use equation provides a clearer picture of the environmental impact of Tesla products, and we have done so this year largely through a lifecycle analysis detailed in this report.\n",
|
||||
"\n",
|
||||
"Tesla aims to continue to increase the proportion of renewable energy usage at our factories in an effort to minimize the carbon footprint for every mile traveled by our products and their components in our supply chain. All of the factories that we built from the ground-up, such as Gigafactory Nevada and Gigafactory Shanghai, and our forthcoming Gigafactories in Berlin and North America, are designed from the beginning to use energy from renewable sources.\n",
|
||||
"\n",
|
||||
"Making a significant and lasting impact on environmental sustainability is difficult to achieve without securing financial sustainability for the long term. We generated positive Free Cash Flow (operating cash flow less capex) of more than $1 billion for the first time in 2019. We believe the notion that a sustainable future is not economically feasible is no longer valid.\n",
|
||||
"---\n",
|
||||
"# Mission & Tesla Ecosystem\n",
|
||||
"\n",
|
||||
"Climate change is reaching alarming levels in large part due to emissions from burning fossil fuels for transportation and electricity generation. In 2016, carbon dioxide (CO2) concentration levels in the atmosphere exceeded the 400 parts per million threshold on a sustained basis - a level that climate scientists believe will have a catastrophic impact on the environment. Worse, annual global CO2 emissions continue to increase and have approximately doubled over the past 50 years to over 43 gigatons in 2019. The world’s current path is unwise and unsustainable.\n",
|
||||
@@ -213,7 +155,9 @@
|
||||
"\n",
|
||||
"Since the onset of shelter-in-place orders and travel restrictions due to COVID-19, we have seen dramatic increases in air quality across the planet, as well as projections for CO2 emissions to drop in excess of 4% in 2020 compared to pre-COVID-19 levels, according to researchers. Because these improvements in air quality and reductions in CO2 are a result of a global economic disruption and not due to systemic changes in how we produce and consume energy, they are not expected to be sustained absent intervention. However, these changes have shown us the positive impacts of reduced pollution in a very short period of time. At Tesla, we believe that we all have an unprecedented opportunity to learn from this disruption and accelerate the deployment of clean energy solutions as part of a recovery for all economies throughout the world, and we will actively continue to advocate for the realization of these long-term changes.\n",
|
||||
"\n",
|
||||
"## Global Greenhouse Gas (GHG) Emissions by Economic Sector\n",
|
||||
"| Global Greenhouse Gas (GHG) Emissions by Economic Sector |\n",
|
||||
"|----------------------------------------------------------|\n",
|
||||
"|  |\n",
|
||||
"\n",
|
||||
"| Sector | Percentage |\n",
|
||||
"|---------------------------------------------|------------|\n",
|
||||
@@ -226,281 +170,12 @@
|
||||
"\n",
|
||||
"*Tesla-related sectors. Source: World Resources Institute\n",
|
||||
"\n",
|
||||
"According to the Global Carbon project, when fully tallied, total carbon emissions from 2019 are expected to hit another record high of over 43 gigatons for the year. Energy use through electricity and heat production (31%) and transportation (16%) are significant drivers of these GHG emissions.\n",
|
||||
"---\n",
|
||||
"# Mission & Tesla Ecosystem\n",
|
||||
"\n",
|
||||
"To create an entire sustainable energy ecosystem, Tesla also manufactures a unique set of energy products that enable homeowners, businesses and utilities to produce and manage renewable energy generation, storage and consumption. Homeowners can install solar panels or Solar Roof to power their home using 100% renewable energy and then store that energy in Powerwall, which makes electricity available during peak energy-use periods and at night, while also providing power during grid outages. Meanwhile, depending on their particular requirements and the size of the project, utilities and businesses can purchase Megapack – an infinitely scalable energy storage system that provides greater control, efficiency, and reliability across the electric grid – for their energy storage needs.\n",
|
||||
"\n",
|
||||
"Renewable energy generation and storage are critical components of developing microgrids — an increasingly important means of delivering reliable and sustainable electricity around the world. As the deployment of Tesla’s products continues to accelerate, we can scale the adoption of renewable energy, cost-effectively modernize our aging infrastructure (while becoming less reliant on it), and improve the resilience of the electric grid to benefit everyone.\n",
|
||||
"---\n",
|
||||
"# Environmental Impact\n",
|
||||
"---\n",
|
||||
"# Introduction\n",
|
||||
"\n",
|
||||
"In this section of the Impact Report, we will go through the details and calculations of the lifetime environmental impact of our products.\n",
|
||||
"\n",
|
||||
"We are often asked if electric vehicles (EVs) are more sustainable than internal combustion engine (ICE) vehicles. The environmental impact of zero-emission transport and energy products, like the products that Tesla produces and sells, is undeniably more positive than the GHG-emitting alternatives. However, determining the lifetime impact of EVs versus ICE vehicles requires looking at the entire lifecycle - from raw materials to emissions to disposal and not just at the emissions resulting from vehicle usage.\n",
|
||||
"\n",
|
||||
"This is not a straightforward task and some of the most common omissions that we have seen in similar studies include the following:\n",
|
||||
"\n",
|
||||
"a) Using Worldwide Harmonized Light Vehicle Test Procedure (WLTP) or Environmental Protection Agency (EPA) fuel/energy consumption data (which overestimate fuel-economy and underestimate emissions), rather than real-world data;\n",
|
||||
"\n",
|
||||
"b) Not taking into account the higher energy efficiency of Tesla’s powertrains;\n",
|
||||
"\n",
|
||||
"c) Assuming that the average EV needs a battery replacement at some point in its life;\n",
|
||||
"\n",
|
||||
"d) Not accounting for emissions generated through oil refining and the transportation process; and\n",
|
||||
"\n",
|
||||
"e) Using outdated data for the carbon impact of cell manufacturing.\n",
|
||||
"\n",
|
||||
"We tried to address these considerations and complexities in deriving a more accurate calculation in the following lifecycle analysis.\n",
|
||||
"\n",
|
||||
"It is important to remember that environmental impact goes beyond just carbon footprint. According to the World Health Organization (WHO), more than four million people die of air pollution every year. The reduction of Nitrogen Oxides (NOx) and other particulates in the air makes our communities healthier places to live, work, and visit and is another core benefit of driving an EV.\n",
|
||||
"\n",
|
||||
"In addition, solar panels deployed by Tesla (including SolarCity prior to its 2016 acquisition by Tesla) over the years have generated vastly more electricity than what was required to run our factories and related facilities.\n",
|
||||
"---\n",
|
||||
"# Lifecycle Analysis of Tesla EVs versus Average ICE Vehicles\n",
|
||||
"\n",
|
||||
"## 69 tons\n",
|
||||
"Lifetime CO2 emitted by an average combustion engine vehicle (model year 2019) sold in the U.S. through its use phase, excluding CO2 emitted during the oil refining phase.\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"On the right and in the following pages we have laid out our lifecycle analysis, which includes the emissions per mile from:\n",
|
||||
"\n",
|
||||
"- A current Fremont-made Model 3 charged from a grid with the generation mix that reflects the geographic distribution of Model 3 deliveries in the U.S.\n",
|
||||
"- What emissions per mile could be if the Model 3 were used for ridesharing over one million miles using cell chemistry from our energy products.\n",
|
||||
"- What emissions per mile could be if a Model 3 were principally charged at home using a solar system and energy storage.\n",
|
||||
"- What emissions per mile could be if a Model 3 were used for ridesharing over one million miles using cell chemistry from our energy products and if it were only charged using a solar system and energy storage.\n",
|
||||
"- The reference ICE vehicle is based on the average mid-size premium sedan in the U.S.\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"The most important variable in a life cycle analysis is real-world fuel consumption or electricity consumption, which impacts the use phase of the lifecycle. Various efficiency testing cycles such as the New European Driving Cycle (NEDC), WLTP, or EPA don’t truly represent real-world fuel/energy consumption. This is why, for the purpose of this analysis, we used average energy consumption over the more than 4 billion miles Tesla Model 3s have travelled to date, including energy losses during the charging process. For ICE vehicles, we used real-world fuel consumption data provided by Consumer Reports, according to which mid-size premium sedans for model year 2019 on average achieve 23.6 MPG, and this translates to approximately 420 grams of CO2 per mile, once we account for emissions generated through extraction, refining, and shipment of oil.\n",
|
||||
"\n",
|
||||
"Even if we use the official EPA efficiency rating (instead of real-world data) for a Toyota Prius of 56 MPG, which translates to 177 grams of CO2 per mile (incl. refining & transport of oil), an EV would still emit fewer lifetime emissions than the Prius. Regarding mileage and lifespan, we estimate that an average vehicle in the U.S. is driven slightly less than 12,000 miles per year for about 17 years before it is scrapped. Furthermore, as an ICE vehicle ages, its fuel efficiency only remains stable if serviced properly. On the other hand, electricity generation to charge EVs has become “greener” over time with the addition of cleaner energy sources to the grid. Thus, emissions generated through EV charging should continue to decline over time.\n",
|
||||
"\n",
|
||||
"It is important to highlight that, for the purpose of this analysis, we assumed no additional renewable energy capacity on the grid during the life of the vehicle given the shape of the renewable energy adoption curve in the U.S. is still very much up for debate. That said, in the following slide we show that a Tesla Model 3 charged in locations with “greener” grids like New York state, for example, have much lower lifecycle emissions than the U.S. average. We believe that cities, states, and countries alike will strive to reduce grid emissions in the future. This dynamic highlights how EVs on the road today will become cleaner as they age and how critical “greening” the grid will be to achieving reduced transportation emissions.\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"## Average Lifecycle Emissions in U.S. (gCO2e/mi)\n",
|
||||
"\n",
|
||||
"| | Manufacturing Phase | Use Phase |\n",
|
||||
"|--------------------|---------------------|-----------|\n",
|
||||
"| Model 3 Ridesharing Use (solar charged) | | |\n",
|
||||
"| Model 3 Personal Use (solar charged) | | |\n",
|
||||
"| Model 3 Ridesharing Use (grid charged) | | |\n",
|
||||
"| Model 3 Personal Use (grid charged) | | |\n",
|
||||
"| Avg. Mid-Size Premium ICE | | |\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"# Reducing Carbon Footprint Even Further\n",
|
||||
"## Increasing Proportion of Renewable Energy Sources\n",
|
||||
"\n",
|
||||
"Charging a Model 3 using solar panels and a Powerwall adds emissions to the manufacturing phase while reducing use phase emissions as low as zero when 100% of charging is done using that system. The personal use scenario below assumes 18% of charging is done using the public fast-charging network, based on observed fleet behavior.\n",
|
||||
"\n",
|
||||
"### Average Lifecycle Emissions in U.S. (gCO2e/mi)\n",
|
||||
"\n",
|
||||
"| | Manufacturing Phase | Use Phase |\n",
|
||||
"|--------------------|---------------------|-----------|\n",
|
||||
"| Avg. Mid-Size Premium ICE | | |\n",
|
||||
"| Model 3 Personal Use (grid charged) | | |\n",
|
||||
"| Model 3 Ridesharing Use (grid charged) | | |\n",
|
||||
"| Model 3 Personal Use (solar charged) | | |\n",
|
||||
"| Model 3 Ridesharing Use (solar charged) | | |\n",
|
||||
"\n",
|
||||
"While the electricity grid varies from region to region, charging EVs is becoming less carbon intensive every year. In the U.S., coal has historically been the dominant energy source for generating electricity. But in the last decade, coal power has declined significantly as regions turn to cleaner energy sources. Energy generated by renewable sources has grown rapidly, accounting for an estimated 43% of new electricity generation capacity in 2018. Many U.S. states (such as New York referenced in the chart below) have been making significant investments in renewable energy as these sustainable options become more cost competitive compared to fossil fuel resources.\n",
|
||||
"\n",
|
||||
"To put this in perspective, average GHG emissions from charging one New York-based Tesla vehicle equates to the emissions from an ICE vehicle with a fuel economy of 144 MPG (no such vehicle is on the market). Even when charging a Tesla in Michigan, where approximately 64% of energy comes from coal and natural gas, the emissions from our vehicles still equates to the equivalent emissions of an ICE vehicle with 55 real-world MPG (considerably more in terms of EPA rated MPG). As more regions adopt sustainable energy solutions to generate power, emissions related to charging an EV from the grid will decrease even further.\n",
|
||||
"\n",
|
||||
"EV customers can accelerate the process of increasing their renewable energy mix by installing solar panels or a Solar Roof and an energy storage solution, such as Powerwall, in their homes. Such an effort dramatically reduces the lifetime carbon footprint of an EV, even when accounting for the carbon footprint of both the solar panel/Solar roof and Powerwall manufacturing. Remaining use-phase emissions from solar charged vehicles come from publicly available fast-charging, which too is becoming “greener” every year. Our goal is to strategically pair solar and battery storage at as many Tesla Supercharger stations as is feasible.\n",
|
||||
"\n",
|
||||
"### Average Lifecycle Emissions in New York State (gCO2e/mi)\n",
|
||||
"\n",
|
||||
"| | Manufacturing Phase | Use Phase |\n",
|
||||
"|--------------------|---------------------|-----------|\n",
|
||||
"| Model 3 Ridesharing Use (solar charged) | | |\n",
|
||||
"| Model 3 Personal Use (solar charged) | | |\n",
|
||||
"| Model 3 Ridesharing Use (grid charged) | | |\n",
|
||||
"| Model 3 Personal Use (grid charged) | | |\n",
|
||||
"| Avg. Mid-Size Premium ICE | | |\n",
|
||||
"---\n",
|
||||
"# Reducing Carbon Footprint Even Further\n",
|
||||
"## Improving Powertrain Efficiency\n",
|
||||
"\n",
|
||||
"Tesla vehicles are known to have the highest energy efficiency of any EV built to date. In the early days of Model S production, we were able to achieve energy efficiency of 3.1 EPA miles / kWh. Today, our most efficient Model 3 Standard Range Plus (SR+) achieves an EPA range of 4.8 miles / kWh, more than any EV in production. Model Y all-wheel drive (AWD) achieves 4.1 EPA miles / kWh, which makes it the most efficient electric SUV produced to date.\n",
|
||||
"\n",
|
||||
"The energy efficiency of Tesla vehicles will continue to improve further over time as we continue to improve our technology and powertrain efficiency. It is also reasonable to assume that our high-mileage products, such as our future Tesla Robotaxis, will be designed for maximum energy efficiency as handling, acceleration, and top speed become less relevant. That way, we will minimize cost for our customers as well as reduce the carbon footprint per mile driven.\n",
|
||||
"\n",
|
||||
"### Average Lifecycle Emissions in U.S. (gCO2e/mi)\n",
|
||||
"\n",
|
||||
"| Vehicle Type | Manufacturing Phase | Use Phase | Total Emissions |\n",
|
||||
"|---------------------------------------|---------------------|-----------|-----------------|\n",
|
||||
"| Avg. Mid-Size Premium ICE | | | |\n",
|
||||
"| Model 3 Personal Use (grid charged) | | | |\n",
|
||||
"| Model 3 Ridesharing Use (grid charged)| | | |\n",
|
||||
"| Model 3 Personal Use (solar charged) | | | |\n",
|
||||
"| Model 3 Ridesharing Use (solar charged)| | | |\n",
|
||||
"\n",
|
||||
"*Note: The exact values for the manufacturing and use phases are not provided in the image.*\n",
|
||||
"\n",
|
||||
"### Energy Efficiency EPA range in miles/kWh\n",
|
||||
"\n",
|
||||
"| Vehicle Model | EPA Range (miles/kWh) |\n",
|
||||
"|---------------------|-----------------------|\n",
|
||||
"| Model 3 SR+ | 5 |\n",
|
||||
"| Model 3 AWD | 4.5 |\n",
|
||||
"| Model Y AWD | 4.5 |\n",
|
||||
"| Hyundai Kona | 4 |\n",
|
||||
"| Chevy Bolt | 4 |\n",
|
||||
"| Model S LR+ | 4 |\n",
|
||||
"| Nissan Leaf | 3.5 |\n",
|
||||
"| Model X LR+ | 3.5 |\n",
|
||||
"| Jaguar iPace | 3 |\n",
|
||||
"| Mercedes EQC* | 3 |\n",
|
||||
"| Ford Mach E AWD | 3 |\n",
|
||||
"| Audi e-tron | 3 |\n",
|
||||
"| Porsche Taycan | 3 |\n",
|
||||
"\n",
|
||||
"*Tesla estimate. Source: OEM websites*\n",
|
||||
"---\n",
|
||||
"# Reducing Carbon Footprint Even Further\n",
|
||||
"## Reducing Emissions at our Factories\n",
|
||||
"\n",
|
||||
"While emissions from the manufacturing phase can account for a relatively minor portion of lifetime vehicle emissions when compared to the use-phase, it is still an important part of lifecycle emissions. Thus, we strive to source as much renewable energy where possible for our factories in an effort to reduce our manufacturing-phase emissions.\n",
|
||||
"\n",
|
||||
"As we continue to ramp production of Tesla products, we are committed to making significant progress towards our goal of operating global Tesla manufacturing, vehicle charging, and other operations using 100% renewable energy.\n",
|
||||
"\n",
|
||||
"Predominantly due to lack of reliable data, various third-party studies tend to overstate the actual energy requirement, and therefore the associated emissions, for battery manufacturing. In fact, in 2019, the emissions from producing a full EV were nearly comparable to than the emissions from producing an average ICE vehicle. That said, battery manufacturing technology continues to improve rapidly, and we expect the EV manufacturing energy requirement and associated emissions to drop significantly in the near future.\n",
|
||||
"\n",
|
||||
"In the second half of 2018, Tesla launched an Operations Energy Efficiency Program (OEEP) aimed at reducing energy usage across our factories in Fremont, Nevada, and Buffalo. In 2019, the OEEP helped us to achieve energy consumption reductions while we simultaneously ramped new lines and products across the three facilities. Our goal is to install as many solar panels as is practically feasible on the roofs of all of our manufacturing facilities.\n",
|
||||
"\n",
|
||||
"### Average Lifecycle Emissions in U.S. (gCO2e/mi)\n",
|
||||
"\n",
|
||||
"| Vehicle Type | Manufacturing Phase | Use Phase | Total Emissions |\n",
|
||||
"|---------------------------------------|---------------------|-----------|-----------------|\n",
|
||||
"| Avg. Mid-Size Premium ICE | | | |\n",
|
||||
"| Model 3 Personal Use (grid charged) | | | |\n",
|
||||
"| Model 3 Ridesharing Use (grid charged)| | | |\n",
|
||||
"| Model 3 Personal Use (solar charged) | | | |\n",
|
||||
"| Model 3 Ridesharing Use (solar charged)| | | |\n",
|
||||
"\n",
|
||||
"### gCO2e/mi for Model 3 Battery Pack versus Rest of Vehicle\n",
|
||||
"\n",
|
||||
"| Year | Battery Pack | Rest of Vehicle |\n",
|
||||
"|------|--------------|-----------------|\n",
|
||||
"| 2017 | 60 | |\n",
|
||||
"| 2019 | 30 | |\n",
|
||||
"---\n",
|
||||
"# Reducing Carbon Footprint Even Further\n",
|
||||
"## Reducing Emissions at our Factories - Gigafactory Shanghai\n",
|
||||
"\n",
|
||||
"Underpinning our strategy for regional manufacturing is a reduction of carbon emissions that result from shipping parts and finished products. From a sustainability standpoint, having vertically integrated Tesla factories in each region helps to reduce the carbon footprint for our operations.\n",
|
||||
"\n",
|
||||
"As highlighted in Tesla’s fourth quarter earnings call for 2019, reductions in shipping costs as well as the strain on the environment from avoided trans-oceanic shipping also makes good business sense. A simplified factory design and localized supply chain near the factory saves time and creates efficiencies, and localized delivery saves outbound logistics costs.\n",
|
||||
"\n",
|
||||
"Our newly opened Gigafactory outside of Shanghai, China has provided us the opportunity to set up and implement the most simplified flows based on what we have learned from the operation of our U.S. factories. The design simplification and operational efficiencies result in time and monetary savings for Gigafactory Shanghai and less carbon emissions per vehicle produced.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"### Average Lifecycle Emissions in U.S. (gCO2e/mi)\n",
|
||||
"\n",
|
||||
"| Vehicle Type | Manufacturing Phase | Use Phase | Total Emissions |\n",
|
||||
"|---------------------------------------|---------------------|-----------|-----------------|\n",
|
||||
"| Avg. Mid-Size Premium ICE | | | |\n",
|
||||
"| Model 3 Personal Use (grid charged) | | | |\n",
|
||||
"| Model 3 Ridesharing Use (grid charged)| | | |\n",
|
||||
"| Model 3 Personal Use (solar charged) | | | |\n",
|
||||
"| Model 3 Ridesharing Use (solar charged)| | | |\n",
|
||||
"\n",
|
||||
"- **Avg. Mid-Size Premium ICE**: ~450 gCO2e/mi\n",
|
||||
"- **Model 3 Personal Use (grid charged)**: ~200 gCO2e/mi\n",
|
||||
"- **Model 3 Ridesharing Use (grid charged)**: ~150 gCO2e/mi\n",
|
||||
"- **Model 3 Personal Use (solar charged)**: ~100 gCO2e/mi\n",
|
||||
"- **Model 3 Ridesharing Use (solar charged)**: ~50 gCO2e/mi\n",
|
||||
"\n",
|
||||
"**Reducing Factory Emissions**\n",
|
||||
"\n",
|
||||
"- **Manufacturing Phase**: Blue\n",
|
||||
"- **Use Phase**: Light Blue\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"# Reducing Carbon Footprint Even Further\n",
|
||||
"## Increasing Vehicle Utilization\n",
|
||||
"\n",
|
||||
"Tesla’s battery packs are designed to outlast the car. We estimate that a vehicle gets scrapped after approximately 200,000 miles of usage in the U.S. and roughly 130,000 miles in Europe. Creating a battery that could instead last for a 1,000,000 miles (4,000 to 5,000 charging cycles) would dramatically reduce emissions per vehicle produced.\n",
|
||||
"\n",
|
||||
"All vehicles in the world combined travel trillions of miles every year. A relatively small number of vehicles, such as taxis, delivery vans, trucks, or buses, account for a disproportionate amount of vehicle miles and as a result, a disproportionate amount of emissions.\n",
|
||||
"\n",
|
||||
"A single future Tesla vehicle with a million-mile battery could be utilized over five times more than an average vehicle in the U.S. (almost eight times more than an average vehicle sold in Europe). As a portion of the carbon footprint is emitted during the production phase of each vehicle, utilization of such vehicle over 1,000,000 miles dramatically reduces the lifetime carbon footprint per each mile travelled. Furthermore, battery recycling has the potential to further reduce emissions as components of a battery pack can be captured and reused, displacing much of the need for raw material mining and the associated emissions.\n",
|
||||
"\n",
|
||||
"### Average Lifecycle Emissions in U.S. (gCO2e/mi)\n",
|
||||
"\n",
|
||||
"| Vehicle Type | Manufacturing Phase | Use Phase |\n",
|
||||
"|---------------------------------------|---------------------|-----------|\n",
|
||||
"| Avg. Mid-Size Premium ICE | | |\n",
|
||||
"| Model 3 Personal Use (grid charged) | | |\n",
|
||||
"| Model 3 Ridesharing Use (grid charged)| | |\n",
|
||||
"| Model 3 Personal Use (solar charged) | | |\n",
|
||||
"| Model 3 Ridesharing Use (solar charged)| | |\n",
|
||||
"\n",
|
||||
"### Tesla Model S/X Battery Capacity Retention per Distance Traveled\n",
|
||||
"\n",
|
||||
"| Distance Traveled (thousands of miles) | Retention | Standard Deviation |\n",
|
||||
"|----------------------------------------|-----------|--------------------|\n",
|
||||
"| 0 | 100% | |\n",
|
||||
"| 25 | | |\n",
|
||||
"| 50 | | |\n",
|
||||
"| 75 | | |\n",
|
||||
"| 100 | | |\n",
|
||||
"| 125 | | |\n",
|
||||
"| 150 | | |\n",
|
||||
"| 175 | | |\n",
|
||||
"| 200 | | |\n",
|
||||
"---\n",
|
||||
"# Battery Recycling\n",
|
||||
"\n",
|
||||
"A common question we hear is, “What happens to Tesla vehicle battery packs once they reach their end of life?” An important distinction between fossil fuels and lithium-ion batteries as an energy source is that while fossil fuels are extracted and used once, the materials in a lithium-ion battery are recyclable. When petroleum is pumped out of the ground, chemically refined, and then burned, it releases harmful emissions into the atmosphere that are not recovered for reuse. Battery materials, in contrast, are refined and put into a cell, and will still remain in the cell at the end of their life, when they can be recycled to recover valuable materials for reuse over and over again.\n",
|
||||
"\n",
|
||||
"Extending the life of a battery pack is a superior option to recycling for both environmental and business reasons. For those reasons, before decommissioning a consumer battery pack and sending it for recycling, Tesla does everything it can to extend the useful life of each battery pack. Any battery that is no longer meeting a customer’s needs can be serviced by Tesla at one of our service centers around the world.\n",
|
||||
"\n",
|
||||
"Tesla’s current vehicle batteries are designed to outlast our cars. We estimate an average ICE vehicle in the U.S. is scrapped after 17 years of usage, by which time it will have ~200,000 miles on its odometer. Data from our fleet of over 1 million Tesla vehicles on the road shows that our vehicles that have been driven between 150,000 and 200,000 miles had battery packs that degraded by less than 15% on average.\n",
|
||||
"\n",
|
||||
"# Battery Materials Lifecycle\n",
|
||||
"\n",
|
||||
"| Step | Description |\n",
|
||||
"|-----------------------------|--------------------------------|\n",
|
||||
"| Raw material mining | |\n",
|
||||
"| Battery production | |\n",
|
||||
"| Lifetime usage in a vehicle | |\n",
|
||||
"| Battery recycling | Extracting raw materials |\n",
|
||||
"---\n",
|
||||
"# Battery Recycling at Gigafactory Nevada\n",
|
||||
"\n",
|
||||
"## Global annual amount of li-ion battery metals sent for recycling by Tesla in 2019\n",
|
||||
"\n",
|
||||
"- **1,000** Tons of Nickel\n",
|
||||
"- **320** Tons of Copper\n",
|
||||
"- **110** Tons of Cobalt\n",
|
||||
"\n",
|
||||
"A closed-loop battery recycling process presents a compelling solution to move energy supply away from the fossil-fuel based practice of take, make and burn, to a more circular model of recycling end-of-life batteries for reuse over and over again.\n",
|
||||
"\n",
|
||||
"Tesla battery packs are made to last many years and therefore we have only received a limited number of these batteries back from the field. Most batteries that Tesla recycles today are pre-consumer, coming to us through R&D and quality control. None of our scrapped lithium-ion batteries go to landfilling, and 100% are recycled. The small amount of post-consumer batteries that we receive are generated from our fleet of vehicles on the road, predominantly from taxi-like vehicles. Since we have only been producing Model S for approximately eight years, it will likely be some time before we start receiving back vehicle batteries in larger volumes.\n",
|
||||
"\n",
|
||||
"All materials contained in a battery remain in their original form at end-of-life and the vast majority of these materials are then captured in the recycling process. Presently, only high-value elements are recycled and re-introduced into the supply chain. However, as recycling technology improves, we strive to re-introduce more and more materials back into their original commodity markets. Over half of the materials in a battery cell are metals, which is great for sustainability given they are infinitely recyclable. The remaining materials are plastics, organics, and other difficult to re-use materials. Research is underway by organizations all over the world to improve the ability to recycle these remaining materials.\n",
|
||||
"\n",
|
||||
"Today, we work with third-party recyclers around the world to process all scrap and end-of-life batteries to recover valuable metals. Our recycling partners work with us to ensure that non-valuable or non-recoverable materials from the batteries are disposed of responsibly.\n",
|
||||
"\n",
|
||||
"Tesla is currently developing a unique battery recycling system at Gigafactory Nevada that will process both battery manufacturing scrap and end-of-life batteries. Through this system, the recovery of critical minerals will be maximized along with the recovery of all metals used in Tesla battery cells, such as copper, aluminum and steel. Our ultimate goal is to develop a recycling process that has high recovery rates, low costs, and a low environmental impact. From an economic perspective, we expect to recognize significant savings over the long term, as the costs associated with large-scale battery material recovery and recycling will be far lower than purchasing and transporting new materials to put into cells.\n"
|
||||
"According to the Global Carbon project, when fully tallied, total carbon emissions from 2019 are expected to hit another record high of over 43 gigatons for the year. Energy use through electricity and heat production (31%) and transportation (16%) are significant drivers of these GHG emissions.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(documents_gpt4o[0].get_content())"
|
||||
"print(documents_gpt4o[3].get_content())"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -515,44 +190,6 @@
|
||||
"We ask a question over the parsed markdown table and get back the right answer! We also ask a question over the text."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "bb991e26-f9e5-404f-9d2c-73dbba12554b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from copy import deepcopy\n",
|
||||
"from llama_index.core.schema import TextNode\n",
|
||||
"from llama_index.core import VectorStoreIndex\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_nodes(docs):\n",
|
||||
" \"\"\"Split docs into nodes, by separator.\"\"\"\n",
|
||||
" nodes = []\n",
|
||||
" for doc in docs:\n",
|
||||
" doc_chunks = doc.text.split(\"\\n---\\n\")\n",
|
||||
" for doc_chunk in doc_chunks:\n",
|
||||
" node = TextNode(\n",
|
||||
" text=doc_chunk,\n",
|
||||
" metadata=deepcopy(doc.metadata),\n",
|
||||
" )\n",
|
||||
" nodes.append(node)\n",
|
||||
"\n",
|
||||
" return nodes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "de1b3606-259a-44cf-9892-3c31d6516c2b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# this will split into pages\n",
|
||||
"nodes = get_nodes(documents_gpt4o)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
@@ -560,7 +197,9 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"vector_index = VectorStoreIndex(nodes)"
|
||||
"from llama_index.core import VectorStoreIndex\n",
|
||||
"\n",
|
||||
"vector_index = VectorStoreIndex(documents_gpt4o)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -595,7 +234,7 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"The greenhouse emissions for agriculture and transportation are 20% and 16% respectively.\n"
|
||||
"Agriculture accounts for 20% of global greenhouse gas emissions, while transportation contributes 16% of these emissions.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -633,7 +272,7 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"The EPA range of Tesla vehicles, such as the Model 3 Standard Range Plus achieving 4.8 miles/kWh and the Model Y all-wheel drive achieving 4.1 miles/kWh, surpasses that of other electric vehicles currently in production. For example, the Hyundai Kona, Chevy Bolt, Model S LR+, and Nissan Leaf have EPA ranges ranging from 3.5 to 4 miles/kWh, while the Jaguar iPace, Mercedes EQC, Ford Mach E AWD, Audi e-tron, and Porsche Taycan have EPA ranges of 3 miles/kWh. This indicates that Tesla vehicles generally have higher energy efficiency and longer EPA ranges compared to other electric vehicles available in the market.\n"
|
||||
"The EPA range of Tesla vehicles varies across different models. The Model 3 Standard Range Plus (SR+) achieves an EPA range of 4.8 miles/kWh, making it the most efficient electric vehicle in production. The Model Y all-wheel drive (AWD) achieves 4.1 miles/kWh, which positions it as the most efficient electric SUV produced to date. The energy efficiency of Tesla vehicles is highlighted by these EPA range figures, showcasing their advancements in powertrain efficiency compared to other electric vehicles on the market.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -668,25 +307,25 @@
|
||||
"| Model 3 Personal Use (solar charged) | | | |\n",
|
||||
"| Model 3 Ridesharing Use (solar charged)| | | |\n",
|
||||
"\n",
|
||||
"*Note: The exact values for the manufacturing and use phases are not provided in the image.*\n",
|
||||
"*Note: The chart shows that the emissions depend on powertrain efficiency.*\n",
|
||||
"\n",
|
||||
"### Energy Efficiency EPA range in miles/kWh\n",
|
||||
"\n",
|
||||
"| Vehicle Model | EPA Range (miles/kWh) |\n",
|
||||
"|---------------------|-----------------------|\n",
|
||||
"| Model 3 SR+ | 5 |\n",
|
||||
"| Model 3 AWD | 4.5 |\n",
|
||||
"| Model Y AWD | 4.5 |\n",
|
||||
"| Hyundai Kona | 4 |\n",
|
||||
"| Chevy Bolt | 4 |\n",
|
||||
"| Model S LR+ | 4 |\n",
|
||||
"| Nissan Leaf | 3.5 |\n",
|
||||
"| Model X LR+ | 3.5 |\n",
|
||||
"| Jaguar iPace | 3 |\n",
|
||||
"| Mercedes EQC* | 3 |\n",
|
||||
"| Ford Mach E AWD | 3 |\n",
|
||||
"| Audi e-tron | 3 |\n",
|
||||
"| Porsche Taycan | 3 |\n",
|
||||
"| Model 3 SR+ | 4.8 |\n",
|
||||
"| Model 3 AWD | |\n",
|
||||
"| Model Y AWD | |\n",
|
||||
"| Hyundai Kona | |\n",
|
||||
"| Chevy Bolt | |\n",
|
||||
"| Model S LR+ | |\n",
|
||||
"| Nissan Leaf | |\n",
|
||||
"| Model X LR+ | |\n",
|
||||
"| Jaguar iPace | |\n",
|
||||
"| Mercedes EQC* | |\n",
|
||||
"| Ford Mach E AWD | |\n",
|
||||
"| Audi e-tron | |\n",
|
||||
"| Porsche Taycan | |\n",
|
||||
"\n",
|
||||
"*Tesla estimate. Source: OEM websites*\n"
|
||||
]
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
import os
|
||||
import asyncio
|
||||
from io import TextIOWrapper
|
||||
|
||||
import httpx
|
||||
import mimetypes
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import List, Optional, Union
|
||||
from pathlib import Path, PurePath, PurePosixPath
|
||||
from typing import AsyncGenerator, Any, Dict, List, Optional, Union
|
||||
from contextlib import asynccontextmanager
|
||||
from io import BufferedIOBase
|
||||
|
||||
from llama_index.core.async_utils import run_jobs
|
||||
from llama_index.core.bridge.pydantic import Field, validator
|
||||
from fsspec import AbstractFileSystem
|
||||
from fsspec.spec import AbstractBufferedFile
|
||||
from llama_index.core.async_utils import asyncio_run, run_jobs
|
||||
from llama_index.core.bridge.pydantic import Field, field_validator
|
||||
from llama_index.core.constants import DEFAULT_BASE_URL
|
||||
from llama_index.core.readers.base import BasePydanticReader
|
||||
from llama_index.core.readers.file.base import get_default_fs
|
||||
from llama_index.core.schema import Document
|
||||
from llama_parse.utils import (
|
||||
nest_asyncio_err,
|
||||
@@ -18,12 +25,23 @@ from llama_parse.utils import (
|
||||
Language,
|
||||
SUPPORTED_FILE_TYPES,
|
||||
)
|
||||
from copy import deepcopy
|
||||
|
||||
# can put in a path to the file or the file bytes itself
|
||||
# if passing as bytes or a buffer, must provide the file_name in extra_info
|
||||
FileInput = Union[str, bytes, BufferedIOBase]
|
||||
|
||||
_DEFAULT_SEPARATOR = "\n---\n"
|
||||
|
||||
|
||||
class LlamaParse(BasePydanticReader):
|
||||
"""A smart-parser for files."""
|
||||
|
||||
api_key: str = Field(default="", description="The API key for the LlamaParse API.")
|
||||
api_key: str = Field(
|
||||
default="",
|
||||
description="The API key for the LlamaParse API.",
|
||||
validate_default=True,
|
||||
)
|
||||
base_url: str = Field(
|
||||
default=DEFAULT_BASE_URL,
|
||||
description="The base URL of the Llama Parsing API.",
|
||||
@@ -57,6 +75,46 @@ class LlamaParse(BasePydanticReader):
|
||||
parsing_instruction: Optional[str] = Field(
|
||||
default="", description="The parsing instruction for the parser."
|
||||
)
|
||||
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).",
|
||||
)
|
||||
invalidate_cache: Optional[bool] = Field(
|
||||
default=False,
|
||||
description="If set to true, the cache will be ignored and the document re-processes. All document are kept in cache for 48hours after the job was completed to avoid processing the same document twice.",
|
||||
)
|
||||
do_not_cache: Optional[bool] = Field(
|
||||
default=False,
|
||||
description="If set to true, the document will not be cached. This mean that you will be re-charged it you reprocess them as they will not be cached.",
|
||||
)
|
||||
fast_mode: Optional[bool] = Field(
|
||||
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.",
|
||||
)
|
||||
premium_mode: bool = Field(
|
||||
default=False,
|
||||
description="Use our best parser mode if set to True.",
|
||||
)
|
||||
continuous_mode: bool = Field(
|
||||
default=False,
|
||||
description="Parse documents continuously, leading to better results on documents where tables span across two pages.",
|
||||
)
|
||||
do_not_unroll_columns: Optional[bool] = Field(
|
||||
default=False,
|
||||
description="If set to true, the parser will keep column in the text according to document layout. Reduce reconstruction accuracy, and LLM's/embedings performances in most case.",
|
||||
)
|
||||
page_separator: Optional[str] = Field(
|
||||
default=None,
|
||||
description="A templated page separator to use to split the text. If it contain `{page_number}`,it will be replaced by the next page number. If not set will the default separator '\\n---\\n' will be used.",
|
||||
)
|
||||
page_prefix: Optional[str] = Field(
|
||||
default=None,
|
||||
description="A templated prefix to add to the beginning of each page. If it contain `{page_number}`, it will be replaced by the page number.",
|
||||
)
|
||||
page_suffix: Optional[str] = Field(
|
||||
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.",
|
||||
)
|
||||
gpt4o_mode: bool = Field(
|
||||
default=False,
|
||||
description="Whether to use gpt-4o extract text from documents.",
|
||||
@@ -65,12 +123,76 @@ class LlamaParse(BasePydanticReader):
|
||||
default=None,
|
||||
description="The API key for the GPT-4o API. Lowers the cost of parsing.",
|
||||
)
|
||||
guess_xlsx_sheet_names: Optional[bool] = Field(
|
||||
default=False,
|
||||
description="Whether to guess the sheet names of the xlsx file.",
|
||||
)
|
||||
bounding_box: Optional[str] = Field(
|
||||
default=None,
|
||||
description="The bounding box to use to extract text from documents describe as a string containing the bounding box margins",
|
||||
)
|
||||
target_pages: Optional[str] = Field(
|
||||
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",
|
||||
)
|
||||
ignore_errors: bool = Field(
|
||||
default=True,
|
||||
description="Whether or not to ignore and skip errors raised during parsing.",
|
||||
)
|
||||
split_by_page: bool = Field(
|
||||
default=True,
|
||||
description="Whether to split by page using the page separator",
|
||||
)
|
||||
vendor_multimodal_api_key: Optional[str] = Field(
|
||||
default=None,
|
||||
description="The API key for the multimodal API.",
|
||||
)
|
||||
use_vendor_multimodal_model: bool = Field(
|
||||
default=False,
|
||||
description="Whether to use the vendor multimodal API.",
|
||||
)
|
||||
vendor_multimodal_model_name: Optional[str] = Field(
|
||||
default=None,
|
||||
description="The model name for the vendor multimodal API.",
|
||||
)
|
||||
take_screenshot: bool = Field(
|
||||
default=False,
|
||||
description="Whether to take screenshot of each page of the document.",
|
||||
)
|
||||
custom_client: Optional[httpx.AsyncClient] = Field(
|
||||
default=None, description="A custom HTTPX client to use for sending requests."
|
||||
)
|
||||
disable_ocr: bool = Field(
|
||||
default=False,
|
||||
description="Disable the OCR on the document. LlamaParse will only extract the copyable text from the document.",
|
||||
)
|
||||
is_formatting_instruction: bool = Field(
|
||||
default=True,
|
||||
description="Allow the parsing instruction to also format the output. Disable to have a cleaner markdown output.",
|
||||
)
|
||||
annotate_links: bool = Field(
|
||||
default=False,
|
||||
description="Annotate links found in the document to extract their URL.",
|
||||
)
|
||||
webhook_url: Optional[str] = Field(
|
||||
default=None,
|
||||
description="A URL that needs to be called at the end of the parsing job.",
|
||||
)
|
||||
azure_openai_deployment_name: Optional[str] = Field(
|
||||
default=None, description="Azure Openai Deployment Name"
|
||||
)
|
||||
azure_openai_endpoint: Optional[str] = Field(
|
||||
default=None, description="Azure Openai Endpoint"
|
||||
)
|
||||
azure_openai_api_version: Optional[str] = Field(
|
||||
default=None, description="Azure Openai API Version"
|
||||
)
|
||||
azure_openai_key: Optional[str] = Field(
|
||||
default=None, description="Azure Openai Key"
|
||||
)
|
||||
|
||||
@validator("api_key", pre=True, always=True)
|
||||
@field_validator("api_key", mode="before", check_fields=True)
|
||||
@classmethod
|
||||
def validate_api_key(cls, v: str) -> str:
|
||||
"""Validate the API key."""
|
||||
if not v:
|
||||
@@ -83,58 +205,142 @@ class LlamaParse(BasePydanticReader):
|
||||
|
||||
return v
|
||||
|
||||
@validator("base_url", pre=True, always=True)
|
||||
@field_validator("base_url", mode="before", check_fields=True)
|
||||
@classmethod
|
||||
def validate_base_url(cls, v: str) -> str:
|
||||
"""Validate the base URL."""
|
||||
url = os.getenv("LLAMA_CLOUD_BASE_URL", None)
|
||||
return url or v or DEFAULT_BASE_URL
|
||||
|
||||
@asynccontextmanager
|
||||
async def client_context(self) -> AsyncGenerator[httpx.AsyncClient, None]:
|
||||
"""Create a context for the HTTPX client."""
|
||||
if self.custom_client is not None:
|
||||
yield self.custom_client
|
||||
else:
|
||||
async with httpx.AsyncClient(timeout=self.max_timeout) as client:
|
||||
yield client
|
||||
|
||||
# upload a document and get back a job_id
|
||||
async def _create_job(
|
||||
self, file_path: str, extra_info: Optional[dict] = None
|
||||
self,
|
||||
file_input: FileInput,
|
||||
extra_info: Optional[dict] = None,
|
||||
fs: Optional[AbstractFileSystem] = None,
|
||||
) -> str:
|
||||
file_path = str(file_path)
|
||||
file_ext = os.path.splitext(file_path)[1]
|
||||
if file_ext not in SUPPORTED_FILE_TYPES:
|
||||
raise Exception(
|
||||
f"Currently, only the following file types are supported: {SUPPORTED_FILE_TYPES}\n"
|
||||
f"Current file type: {file_ext}"
|
||||
headers = {"Authorization": f"Bearer {self.api_key}"}
|
||||
url = f"{self.base_url}/api/parsing/upload"
|
||||
files = None
|
||||
file_handle = None
|
||||
|
||||
if isinstance(file_input, (bytes, BufferedIOBase)):
|
||||
if not extra_info or "file_name" not in extra_info:
|
||||
raise ValueError(
|
||||
"file_name must be provided in extra_info when passing bytes"
|
||||
)
|
||||
file_name = extra_info["file_name"]
|
||||
mime_type = mimetypes.guess_type(file_name)[0]
|
||||
files = {"file": (file_name, file_input, mime_type)}
|
||||
elif isinstance(file_input, (str, Path, PurePosixPath, PurePath)):
|
||||
file_path = str(file_input)
|
||||
file_ext = os.path.splitext(file_path)[1].lower()
|
||||
if file_ext not in SUPPORTED_FILE_TYPES:
|
||||
raise Exception(
|
||||
f"Currently, only the following file types are supported: {SUPPORTED_FILE_TYPES}\n"
|
||||
f"Current file type: {file_ext}"
|
||||
)
|
||||
mime_type = mimetypes.guess_type(file_path)[0]
|
||||
# Open the file here for the duration of the async context
|
||||
# load data, set the mime type
|
||||
fs = fs or get_default_fs()
|
||||
file_handle = fs.open(file_input, "rb")
|
||||
files = {"file": (os.path.basename(file_path), file_handle, mime_type)}
|
||||
else:
|
||||
raise ValueError(
|
||||
"file_input must be either a file path string, file bytes, or buffer object"
|
||||
)
|
||||
|
||||
extra_info = extra_info or {}
|
||||
extra_info["file_path"] = file_path
|
||||
data = {
|
||||
"language": self.language.value,
|
||||
"parsing_instruction": self.parsing_instruction,
|
||||
"invalidate_cache": self.invalidate_cache,
|
||||
"skip_diagonal_text": self.skip_diagonal_text,
|
||||
"do_not_cache": self.do_not_cache,
|
||||
"fast_mode": self.fast_mode,
|
||||
"premium_mode": self.premium_mode,
|
||||
"continuous_mode": self.continuous_mode,
|
||||
"do_not_unroll_columns": self.do_not_unroll_columns,
|
||||
"gpt4o_mode": self.gpt4o_mode,
|
||||
"gpt4o_api_key": self.gpt4o_api_key,
|
||||
"vendor_multimodal_api_key": self.vendor_multimodal_api_key,
|
||||
"use_vendor_multimodal_model": self.use_vendor_multimodal_model,
|
||||
"vendor_multimodal_model_name": self.vendor_multimodal_model_name,
|
||||
"take_screenshot": self.take_screenshot,
|
||||
"disable_ocr": self.disable_ocr,
|
||||
"guess_xlsx_sheet_names": self.guess_xlsx_sheet_names,
|
||||
"is_formatting_instruction": self.is_formatting_instruction,
|
||||
"annotate_links": self.annotate_links,
|
||||
"from_python_package": True,
|
||||
}
|
||||
|
||||
headers = {"Authorization": f"Bearer {self.api_key}"}
|
||||
# only send page separator to server if it is not None
|
||||
# as if a null, "" string is sent the server will then ignore the page separator instead of using the default
|
||||
if self.page_separator is not None:
|
||||
data["page_separator"] = self.page_separator
|
||||
|
||||
# load data, set the mime type
|
||||
with open(file_path, "rb") as f:
|
||||
mime_type = mimetypes.guess_type(file_path)[0]
|
||||
files = {"file": (f.name, f, mime_type)}
|
||||
if self.page_prefix is not None:
|
||||
data["page_prefix"] = self.page_prefix
|
||||
|
||||
# send the request, start job
|
||||
url = f"{self.base_url}/api/parsing/upload"
|
||||
async with httpx.AsyncClient(timeout=self.max_timeout) as client:
|
||||
if self.page_suffix is not None:
|
||||
data["page_suffix"] = self.page_suffix
|
||||
|
||||
if self.bounding_box is not None:
|
||||
data["bounding_box"] = self.bounding_box
|
||||
|
||||
if self.target_pages is not None:
|
||||
data["target_pages"] = self.target_pages
|
||||
|
||||
if self.webhook_url is not None:
|
||||
data["webhook_url"] = self.webhook_url
|
||||
|
||||
# Azure OpenAI
|
||||
if self.azure_openai_deployment_name is not None:
|
||||
data["azure_openai_deployment_name"] = self.azure_openai_deployment_name
|
||||
|
||||
if self.azure_openai_endpoint is not None:
|
||||
data["azure_openai_endpoint"] = self.azure_openai_endpoint
|
||||
|
||||
if self.azure_openai_api_version is not None:
|
||||
data["azure_openai_api_version"] = self.azure_openai_api_version
|
||||
|
||||
if self.azure_openai_key is not None:
|
||||
data["azure_openai_key"] = self.azure_openai_key
|
||||
|
||||
try:
|
||||
async with self.client_context() as client:
|
||||
response = await client.post(
|
||||
url,
|
||||
files=files,
|
||||
headers=headers,
|
||||
data={
|
||||
"language": self.language.value,
|
||||
"parsing_instruction": self.parsing_instruction,
|
||||
"gpt4o_mode": self.gpt4o_mode,
|
||||
"gpt4o_api_key": self.gpt4o_api_key,
|
||||
},
|
||||
data=data,
|
||||
)
|
||||
if not response.is_success:
|
||||
raise Exception(f"Failed to parse the file: {response.text}")
|
||||
job_id = response.json()["id"]
|
||||
return job_id
|
||||
finally:
|
||||
if file_handle is not None:
|
||||
file_handle.close()
|
||||
|
||||
# check the status of the job, return when done
|
||||
job_id = response.json()["id"]
|
||||
return job_id
|
||||
@staticmethod
|
||||
def __get_filename(f: Union[TextIOWrapper, AbstractBufferedFile]) -> str:
|
||||
if isinstance(f, TextIOWrapper):
|
||||
return f.name
|
||||
return f.full_name
|
||||
|
||||
async def _get_job_result(
|
||||
self, job_id: str, result_type: str, verbose: bool = False
|
||||
) -> dict:
|
||||
) -> Dict[str, Any]:
|
||||
result_url = f"{self.base_url}/api/parsing/job/{job_id}/result/{result_type}"
|
||||
status_url = f"{self.base_url}/api/parsing/job/{job_id}"
|
||||
headers = {"Authorization": f"Bearer {self.api_key}"}
|
||||
@@ -143,7 +349,7 @@ class LlamaParse(BasePydanticReader):
|
||||
tries = 0
|
||||
while True:
|
||||
await asyncio.sleep(self.check_interval)
|
||||
async with httpx.AsyncClient(timeout=self.max_timeout) as client:
|
||||
async with self.client_context() as client:
|
||||
tries += 1
|
||||
|
||||
result = await client.get(status_url, headers=headers)
|
||||
@@ -160,7 +366,8 @@ class LlamaParse(BasePydanticReader):
|
||||
continue
|
||||
|
||||
# Allowed values "PENDING", "SUCCESS", "ERROR", "CANCELED"
|
||||
status = result.json()["status"]
|
||||
result_json = result.json()
|
||||
status = result_json["status"]
|
||||
if status == "SUCCESS":
|
||||
parsed_result = await client.get(result_url, headers=headers)
|
||||
return parsed_result.json()
|
||||
@@ -172,19 +379,25 @@ class LlamaParse(BasePydanticReader):
|
||||
print(".", end="", flush=True)
|
||||
|
||||
await asyncio.sleep(self.check_interval)
|
||||
|
||||
continue
|
||||
else:
|
||||
raise Exception(
|
||||
f"Failed to parse the file: {job_id}, status: {status}"
|
||||
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)
|
||||
|
||||
async def _aload_data(
|
||||
self, file_path: str, extra_info: Optional[dict] = None, verbose: bool = False
|
||||
self,
|
||||
file_path: FileInput,
|
||||
extra_info: Optional[dict] = None,
|
||||
fs: Optional[AbstractFileSystem] = None,
|
||||
verbose: bool = False,
|
||||
) -> List[Document]:
|
||||
"""Load data from the input path."""
|
||||
try:
|
||||
job_id = await self._create_job(file_path, extra_info=extra_info)
|
||||
job_id = await self._create_job(file_path, extra_info=extra_info, fs=fs)
|
||||
if verbose:
|
||||
print("Started parsing the file under job_id %s" % job_id)
|
||||
|
||||
@@ -192,33 +405,42 @@ class LlamaParse(BasePydanticReader):
|
||||
job_id, self.result_type.value, verbose=verbose
|
||||
)
|
||||
|
||||
return [
|
||||
docs = [
|
||||
Document(
|
||||
text=result[self.result_type.value],
|
||||
metadata=extra_info or {},
|
||||
)
|
||||
]
|
||||
if self.split_by_page:
|
||||
return self._get_sub_docs(docs)
|
||||
else:
|
||||
return docs
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error while parsing the file '{file_path}':", e)
|
||||
file_repr = file_path if isinstance(file_path, str) else "<bytes/buffer>"
|
||||
print(f"Error while parsing the file '{file_repr}':", e)
|
||||
if self.ignore_errors:
|
||||
return []
|
||||
else:
|
||||
raise e
|
||||
|
||||
async def aload_data(
|
||||
self, file_path: Union[List[str], str], extra_info: Optional[dict] = None
|
||||
self,
|
||||
file_path: Union[List[FileInput], FileInput],
|
||||
extra_info: Optional[dict] = None,
|
||||
fs: Optional[AbstractFileSystem] = None,
|
||||
) -> List[Document]:
|
||||
"""Load data from the input path."""
|
||||
if isinstance(file_path, (str, Path)):
|
||||
if isinstance(file_path, (str, PurePosixPath, Path, bytes, BufferedIOBase)):
|
||||
return await self._aload_data(
|
||||
file_path, extra_info=extra_info, verbose=self.verbose
|
||||
file_path, extra_info=extra_info, fs=fs, verbose=self.verbose
|
||||
)
|
||||
elif isinstance(file_path, list):
|
||||
jobs = [
|
||||
self._aload_data(
|
||||
f,
|
||||
extra_info=extra_info,
|
||||
fs=fs,
|
||||
verbose=self.verbose and not self.show_progress,
|
||||
)
|
||||
for f in file_path
|
||||
@@ -244,11 +466,14 @@ class LlamaParse(BasePydanticReader):
|
||||
)
|
||||
|
||||
def load_data(
|
||||
self, file_path: Union[List[str], str], extra_info: Optional[dict] = None
|
||||
self,
|
||||
file_path: Union[List[FileInput], FileInput],
|
||||
extra_info: Optional[dict] = None,
|
||||
fs: Optional[AbstractFileSystem] = None,
|
||||
) -> List[Document]:
|
||||
"""Load data from the input path."""
|
||||
try:
|
||||
return asyncio.run(self.aload_data(file_path, extra_info))
|
||||
return asyncio_run(self.aload_data(file_path, extra_info, fs=fs))
|
||||
except RuntimeError as e:
|
||||
if nest_asyncio_err in str(e):
|
||||
raise RuntimeError(nest_asyncio_msg)
|
||||
@@ -256,28 +481,32 @@ class LlamaParse(BasePydanticReader):
|
||||
raise e
|
||||
|
||||
async def _aget_json(
|
||||
self, file_path: str, extra_info: Optional[dict] = None
|
||||
self, file_path: FileInput, extra_info: Optional[dict] = None
|
||||
) -> List[dict]:
|
||||
"""Load data from the input path."""
|
||||
try:
|
||||
job_id = await self._create_job(file_path, extra_info=extra_info)
|
||||
if self.verbose:
|
||||
print("Started parsing the file under job_id %s" % job_id)
|
||||
|
||||
result = await self._get_job_result(job_id, "json")
|
||||
result["job_id"] = job_id
|
||||
result["file_path"] = file_path
|
||||
return [result]
|
||||
|
||||
if not isinstance(file_path, (bytes, BufferedIOBase)):
|
||||
result["file_path"] = str(file_path)
|
||||
|
||||
return [result]
|
||||
except Exception as e:
|
||||
print(f"Error while parsing the file '{file_path}':", e)
|
||||
file_repr = file_path if isinstance(file_path, str) else "<bytes/buffer>"
|
||||
print(f"Error while parsing the file '{file_repr}':", e)
|
||||
if self.ignore_errors:
|
||||
return []
|
||||
else:
|
||||
raise e
|
||||
|
||||
async def aget_json(
|
||||
self, file_path: Union[List[str], str], extra_info: Optional[dict] = None
|
||||
self,
|
||||
file_path: Union[List[FileInput], FileInput],
|
||||
extra_info: Optional[dict] = None,
|
||||
) -> List[dict]:
|
||||
"""Load data from the input path."""
|
||||
if isinstance(file_path, (str, Path)):
|
||||
@@ -305,18 +534,22 @@ class LlamaParse(BasePydanticReader):
|
||||
)
|
||||
|
||||
def get_json_result(
|
||||
self, file_path: Union[List[str], str], extra_info: Optional[dict] = None
|
||||
self,
|
||||
file_path: Union[List[FileInput], FileInput],
|
||||
extra_info: Optional[dict] = None,
|
||||
) -> List[dict]:
|
||||
"""Parse the input path."""
|
||||
try:
|
||||
return asyncio.run(self.aget_json(file_path, extra_info))
|
||||
return asyncio_run(self.aget_json(file_path, extra_info))
|
||||
except RuntimeError as e:
|
||||
if nest_asyncio_err in str(e):
|
||||
raise RuntimeError(nest_asyncio_msg)
|
||||
else:
|
||||
raise e
|
||||
|
||||
def get_images(self, json_result: List[dict], download_path: str) -> List[dict]:
|
||||
async def aget_images(
|
||||
self, json_result: List[dict], download_path: str
|
||||
) -> List[dict]:
|
||||
"""Download images from the parsed result."""
|
||||
headers = {"Authorization": f"Bearer {self.api_key}"}
|
||||
|
||||
@@ -341,15 +574,23 @@ class LlamaParse(BasePydanticReader):
|
||||
|
||||
# get a valid image path
|
||||
if not image_path.endswith(".png"):
|
||||
image_path += ".png"
|
||||
if not image_path.endswith(".jpg"):
|
||||
image_path += ".png"
|
||||
|
||||
image["path"] = image_path
|
||||
image["job_id"] = job_id
|
||||
image["original_pdf_path"] = result["file_path"]
|
||||
|
||||
image["original_file_path"] = result.get("file_path", None)
|
||||
|
||||
image["page_number"] = page["page"]
|
||||
with open(image_path, "wb") as f:
|
||||
image_url = f"{self.base_url}/api/parsing/job/{job_id}/result/image/{image_name}"
|
||||
f.write(httpx.get(image_url, headers=headers).content)
|
||||
async with self.client_context() as client:
|
||||
res = await client.get(
|
||||
image_url, headers=headers, timeout=self.max_timeout
|
||||
)
|
||||
res.raise_for_status()
|
||||
f.write(res.content)
|
||||
images.append(image)
|
||||
return images
|
||||
except Exception as e:
|
||||
@@ -358,3 +599,82 @@ class LlamaParse(BasePydanticReader):
|
||||
return []
|
||||
else:
|
||||
raise e
|
||||
|
||||
def get_images(self, json_result: List[dict], download_path: str) -> List[dict]:
|
||||
"""Download images from the parsed result."""
|
||||
try:
|
||||
return asyncio_run(self.aget_images(json_result, download_path))
|
||||
except RuntimeError as e:
|
||||
if nest_asyncio_err in str(e):
|
||||
raise RuntimeError(nest_asyncio_msg)
|
||||
else:
|
||||
raise e
|
||||
|
||||
async def aget_xlsx(
|
||||
self, json_result: List[dict], download_path: str
|
||||
) -> List[dict]:
|
||||
"""Download images from the parsed result."""
|
||||
headers = {"Authorization": f"Bearer {self.api_key}"}
|
||||
|
||||
# make the download path
|
||||
if not os.path.exists(download_path):
|
||||
os.makedirs(download_path)
|
||||
try:
|
||||
xlsx_list = []
|
||||
for result in json_result:
|
||||
job_id = result["job_id"]
|
||||
if self.verbose:
|
||||
print("> XLSX")
|
||||
|
||||
xlsx_path = os.path.join(download_path, f"{job_id}.xlsx")
|
||||
|
||||
xlsx = {}
|
||||
|
||||
xlsx["path"] = xlsx_path
|
||||
xlsx["job_id"] = job_id
|
||||
xlsx["original_file_path"] = result.get("file_path", None)
|
||||
|
||||
with open(xlsx_path, "wb") as f:
|
||||
xlsx_url = (
|
||||
f"{self.base_url}/api/parsing/job/{job_id}/result/raw/xlsx"
|
||||
)
|
||||
async with self.client_context() as client:
|
||||
res = await client.get(
|
||||
xlsx_url, headers=headers, timeout=self.max_timeout
|
||||
)
|
||||
res.raise_for_status()
|
||||
f.write(res.content)
|
||||
xlsx_list.append(xlsx)
|
||||
return xlsx_list
|
||||
|
||||
except Exception as e:
|
||||
print("Error while downloading xlsx:", e)
|
||||
if self.ignore_errors:
|
||||
return []
|
||||
else:
|
||||
raise e
|
||||
|
||||
def get_xlsx(self, json_result: List[dict], download_path: str) -> List[dict]:
|
||||
"""Download xlsx from the parsed result."""
|
||||
try:
|
||||
return asyncio_run(self.aget_xlsx(json_result, download_path))
|
||||
except RuntimeError as e:
|
||||
if nest_asyncio_err in str(e):
|
||||
raise RuntimeError(nest_asyncio_msg)
|
||||
else:
|
||||
raise e
|
||||
|
||||
def _get_sub_docs(self, docs: List[Document]) -> List[Document]:
|
||||
"""Split docs into pages, by separator."""
|
||||
sub_docs = []
|
||||
separator = self.page_separator or _DEFAULT_SEPARATOR
|
||||
for doc in docs:
|
||||
doc_chunks = doc.text.split(separator)
|
||||
for doc_chunk in doc_chunks:
|
||||
sub_doc = Document(
|
||||
text=doc_chunk,
|
||||
metadata=deepcopy(doc.metadata),
|
||||
)
|
||||
sub_docs.append(sub_doc)
|
||||
|
||||
return sub_docs
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import click
|
||||
import json
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from pydantic.fields import FieldInfo
|
||||
from typing import Any, Callable, List
|
||||
|
||||
from llama_parse.base import LlamaParse
|
||||
|
||||
|
||||
def pydantic_field_to_click_option(name: str, field: FieldInfo) -> click.Option:
|
||||
"""Convert a Pydantic field to a Click option."""
|
||||
kwargs = {
|
||||
"default": field.default if field.default else None,
|
||||
"help": field.description,
|
||||
}
|
||||
|
||||
if isinstance(kwargs["default"], Enum):
|
||||
kwargs["default"] = kwargs["default"].value
|
||||
|
||||
if field.annotation is bool:
|
||||
kwargs["is_flag"] = True
|
||||
if field.default and field.default is True:
|
||||
name = f"no-{name}"
|
||||
return click.option(f'--{name.replace("_", "-")}', **kwargs)
|
||||
|
||||
|
||||
def add_options(options: List[click.Option]) -> Callable:
|
||||
def _add_options(func: Callable) -> Callable:
|
||||
for option in reversed(options):
|
||||
func = option(func)
|
||||
return func
|
||||
|
||||
return _add_options
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.argument("file_paths", nargs=-1, type=click.Path(exists=True, path_type=Path))
|
||||
@click.option(
|
||||
"--output-file", type=click.Path(path_type=Path), help="Path to save the output"
|
||||
)
|
||||
@click.option("--output-raw-json", is_flag=True, help="Output the raw JSON result")
|
||||
@add_options(
|
||||
[
|
||||
pydantic_field_to_click_option(name, field)
|
||||
for name, field in LlamaParse.model_fields.items()
|
||||
if name not in ["custom_client"]
|
||||
]
|
||||
)
|
||||
def parse(**kwargs: Any) -> None:
|
||||
"""Parse files using LlamaParse and output the results."""
|
||||
file_paths = kwargs.pop("file_paths")
|
||||
output_file = kwargs.pop("output_file")
|
||||
output_raw_json = kwargs.pop("output_raw_json")
|
||||
|
||||
# Remove None values to use LlamaParse defaults
|
||||
kwargs = {k: v for k, v in kwargs.items() if v is not None}
|
||||
|
||||
# Remove no- prefix for boolean flags
|
||||
kwargs = {k.replace("no_", ""): v for k, v in kwargs.items()}
|
||||
|
||||
parser = LlamaParse(**kwargs)
|
||||
if output_raw_json:
|
||||
results = parser.get_json_result(list(file_paths))
|
||||
|
||||
if output_file:
|
||||
with output_file.open("w") as f:
|
||||
json.dump(results, f)
|
||||
click.echo(f"Results saved to {output_file}")
|
||||
else:
|
||||
click.echo(results)
|
||||
else:
|
||||
results = parser.load_data(list(file_paths))
|
||||
|
||||
if output_file:
|
||||
with output_file.open("w") as f:
|
||||
for i, doc in enumerate(results):
|
||||
f.write(f"File: {doc.metadata.get('file_path', 'Unknown')}\n") # type: ignore
|
||||
f.write(doc.text) # type: ignore
|
||||
if i < len(results) - 1:
|
||||
f.write("\n\n---\n\n")
|
||||
click.echo(f"Results saved to {output_file}")
|
||||
else:
|
||||
for i, doc in enumerate(results):
|
||||
click.echo(f"File: {doc.metadata.get('file_path', 'Unknown')}") # type: ignore
|
||||
click.echo(doc.text) # type: ignore
|
||||
if i < len(results) - 1:
|
||||
click.echo("\n---\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parse()
|
||||
@@ -10,7 +10,6 @@ class ResultType(str, Enum):
|
||||
|
||||
TXT = "text"
|
||||
MD = "markdown"
|
||||
JSON = "json"
|
||||
|
||||
|
||||
class Language(str, Enum):
|
||||
@@ -101,52 +100,93 @@ class Language(str, Enum):
|
||||
|
||||
SUPPORTED_FILE_TYPES = [
|
||||
".pdf",
|
||||
# Microsoft word - all versions
|
||||
# document and presentations
|
||||
".602",
|
||||
".abw",
|
||||
".cgm",
|
||||
".cwk",
|
||||
".doc",
|
||||
".docx",
|
||||
".docm",
|
||||
".dot",
|
||||
".dotx",
|
||||
".dotm",
|
||||
# Rich text format
|
||||
".hwp",
|
||||
".key",
|
||||
".lwp",
|
||||
".mw",
|
||||
".mcw",
|
||||
".pages",
|
||||
".pbd",
|
||||
".ppt",
|
||||
".pptm",
|
||||
".pptx",
|
||||
".pot",
|
||||
".potm",
|
||||
".potx",
|
||||
".rtf",
|
||||
# Microsoft Works
|
||||
".wps",
|
||||
# Word Perfect
|
||||
".wpd",
|
||||
# Open Office
|
||||
".sda",
|
||||
".sdd",
|
||||
".sdp",
|
||||
".sdw",
|
||||
".sgl",
|
||||
".sti",
|
||||
".sxi",
|
||||
".sxw",
|
||||
".stw",
|
||||
".sxg",
|
||||
# Apple
|
||||
".pages",
|
||||
# Mac Write
|
||||
".mw",
|
||||
".mcw",
|
||||
# Unified Office Format text
|
||||
".uot",
|
||||
".txt",
|
||||
".uof",
|
||||
".uos",
|
||||
".uop",
|
||||
# Microsoft powerpoints
|
||||
".ppt",
|
||||
".pptx",
|
||||
".pot",
|
||||
".pptm",
|
||||
".potx",
|
||||
".potm",
|
||||
# Apple keynote
|
||||
".key",
|
||||
# Open Office Presentations
|
||||
".odp",
|
||||
".odg",
|
||||
".otp",
|
||||
".fopd",
|
||||
".sxi",
|
||||
".sti",
|
||||
# ebook
|
||||
".uot",
|
||||
".vor",
|
||||
".wpd",
|
||||
".wps",
|
||||
".xml",
|
||||
".zabw",
|
||||
".epub",
|
||||
# html
|
||||
".html",
|
||||
# images
|
||||
".jpg",
|
||||
".jpeg",
|
||||
".png",
|
||||
".gif",
|
||||
".bmp",
|
||||
".svg",
|
||||
".tiff",
|
||||
".webp",
|
||||
# web
|
||||
".htm",
|
||||
".html",
|
||||
# spreadsheets
|
||||
".xlsx",
|
||||
".xls",
|
||||
".xlsm",
|
||||
".xlsb",
|
||||
".xlw",
|
||||
".csv",
|
||||
".dif",
|
||||
".sylk",
|
||||
".slk",
|
||||
".prn",
|
||||
".numbers",
|
||||
".et",
|
||||
".ods",
|
||||
".fods",
|
||||
".uos1",
|
||||
".uos2",
|
||||
".dbf",
|
||||
".wk1",
|
||||
".wk2",
|
||||
".wk3",
|
||||
".wk4",
|
||||
".wks",
|
||||
".123",
|
||||
".wq1",
|
||||
".wq2",
|
||||
".wb1",
|
||||
".wb2",
|
||||
".wb3",
|
||||
".qpw",
|
||||
".xlr",
|
||||
".eth",
|
||||
".tsv",
|
||||
]
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.poetry]
|
||||
name = "llama-parse"
|
||||
version = "0.4.3"
|
||||
version = "0.5.13"
|
||||
description = "Parse files into RAG-Optimized formats."
|
||||
authors = ["Logan Markewich <logan@llamaindex.ai>"]
|
||||
license = "MIT"
|
||||
@@ -13,8 +13,12 @@ packages = [{include = "llama_parse"}]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.8.1,<4.0"
|
||||
llama-index-core = ">=0.10.29"
|
||||
llama-index-core = ">=0.11.0"
|
||||
click = "^8.1.7"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pytest = "^8.0.0"
|
||||
ipykernel = "^6.29.0"
|
||||
|
||||
[tool.poetry.scripts]
|
||||
llama-parse = "llama_parse.cli.main:parse"
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import os
|
||||
import pytest
|
||||
from fsspec.implementations.local import LocalFileSystem
|
||||
from httpx import AsyncClient
|
||||
|
||||
from llama_parse import LlamaParse
|
||||
|
||||
|
||||
@@ -18,21 +21,71 @@ def test_simple_page_text() -> None:
|
||||
assert len(result[0].text) > 0
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
os.environ.get("LLAMA_CLOUD_API_KEY", "") == "",
|
||||
reason="LLAMA_CLOUD_API_KEY not set",
|
||||
)
|
||||
def test_simple_page_markdown() -> None:
|
||||
parser = LlamaParse(result_type="markdown")
|
||||
@pytest.fixture
|
||||
def markdown_parser() -> LlamaParse:
|
||||
if os.environ.get("LLAMA_CLOUD_API_KEY", "") == "":
|
||||
pytest.skip("LLAMA_CLOUD_API_KEY not set")
|
||||
return LlamaParse(result_type="markdown", ignore_errors=False)
|
||||
|
||||
|
||||
def test_simple_page_markdown(markdown_parser: LlamaParse) -> None:
|
||||
filepath = os.path.join(
|
||||
os.path.dirname(__file__), "test_files/attention_is_all_you_need.pdf"
|
||||
)
|
||||
result = markdown_parser.load_data(filepath)
|
||||
assert len(result) == 1
|
||||
assert len(result[0].text) > 0
|
||||
|
||||
|
||||
def test_simple_page_markdown_bytes(markdown_parser: LlamaParse) -> None:
|
||||
markdown_parser = LlamaParse(result_type="markdown", ignore_errors=False)
|
||||
|
||||
filepath = os.path.join(
|
||||
os.path.dirname(__file__), "test_files/attention_is_all_you_need.pdf"
|
||||
)
|
||||
result = parser.load_data(filepath)
|
||||
with open(filepath, "rb") as f:
|
||||
file_bytes = f.read()
|
||||
# client must provide extra_info with file_name
|
||||
with pytest.raises(ValueError):
|
||||
result = markdown_parser.load_data(file_bytes)
|
||||
result = markdown_parser.load_data(
|
||||
file_bytes, extra_info={"file_name": "attention_is_all_you_need.pdf"}
|
||||
)
|
||||
assert len(result) == 1
|
||||
assert len(result[0].text) > 0
|
||||
|
||||
|
||||
def test_simple_page_markdown_buffer(markdown_parser: LlamaParse) -> None:
|
||||
markdown_parser = LlamaParse(result_type="markdown", ignore_errors=False)
|
||||
|
||||
filepath = os.path.join(
|
||||
os.path.dirname(__file__), "test_files/attention_is_all_you_need.pdf"
|
||||
)
|
||||
with open(filepath, "rb") as f:
|
||||
# client must provide extra_info with file_name
|
||||
with pytest.raises(ValueError):
|
||||
result = markdown_parser.load_data(f)
|
||||
result = markdown_parser.load_data(
|
||||
f, extra_info={"file_name": "attention_is_all_you_need.pdf"}
|
||||
)
|
||||
assert len(result) == 1
|
||||
assert len(result[0].text) > 0
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
os.environ.get("LLAMA_CLOUD_API_KEY", "") == "",
|
||||
reason="LLAMA_CLOUD_API_KEY not set",
|
||||
)
|
||||
def test_simple_page_with_custom_fs() -> None:
|
||||
parser = LlamaParse(result_type="markdown")
|
||||
fs = LocalFileSystem()
|
||||
filepath = os.path.join(
|
||||
os.path.dirname(__file__), "test_files/attention_is_all_you_need.pdf"
|
||||
)
|
||||
result = parser.load_data(filepath, fs=fs)
|
||||
assert len(result) == 1
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
os.environ.get("LLAMA_CLOUD_API_KEY", "") == "",
|
||||
reason="LLAMA_CLOUD_API_KEY not set",
|
||||
@@ -57,3 +110,18 @@ def test_simple_page_progress_workers() -> None:
|
||||
result = parser.load_data([filepath, filepath])
|
||||
assert len(result) == 2
|
||||
assert len(result[0].text) > 0
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
os.environ.get("LLAMA_CLOUD_API_KEY", "") == "",
|
||||
reason="LLAMA_CLOUD_API_KEY not set",
|
||||
)
|
||||
def test_custom_client() -> None:
|
||||
custom_client = AsyncClient(verify=False, timeout=10)
|
||||
parser = LlamaParse(result_type="markdown", custom_client=custom_client)
|
||||
filepath = os.path.join(
|
||||
os.path.dirname(__file__), "test_files/attention_is_all_you_need.pdf"
|
||||
)
|
||||
result = parser.load_data(filepath)
|
||||
assert len(result) == 1
|
||||
assert len(result[0].text) > 0
|
||||
|
||||