mirror of
https://github.com/run-llama/llama_cloud_services.git
synced 2026-07-20 19:47:38 -04:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a953225a15 | |||
| bf110ed2cf | |||
| 196ab827f5 | |||
| ba4cb4d5e9 | |||
| 58d883b825 | |||
| 5fc5ebfc6c | |||
| fe3e20fd53 | |||
| e7e59459ab | |||
| f4d7c84e19 | |||
| 9050a346e4 | |||
| 9690ccf4ea | |||
| 97745f0f1c |
@@ -19,8 +19,6 @@ jobs:
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Lint - Python
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -29,7 +29,18 @@ jobs:
|
||||
- name: Set up Python
|
||||
run: uv python install ${{ matrix.python-version }}
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: "ts/llama_cloud_services/.nvmrc"
|
||||
- name: Install dependencies
|
||||
run: pnpm install --no-frozen-lockfile
|
||||
|
||||
- name: Run linter
|
||||
shell: bash
|
||||
working-directory: py
|
||||
run: uv run -- pre-commit run -a
|
||||
# the js checks are run roundaboutly through lint-staged, and -a doesn't run it. Run them directly.
|
||||
- run: pnpm -w --filter llama-cloud-services run lint
|
||||
- run: pnpm -w --filter llama-cloud-services run format:check
|
||||
@@ -1,37 +0,0 @@
|
||||
name: Lint - TypeScript
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "ts/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "ts/**"
|
||||
|
||||
env:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_REMOTE_ONLY: true
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: "ts/llama_cloud_services/.nvmrc"
|
||||
- name: Install dependencies
|
||||
run: pnpm install --no-frozen-lockfile
|
||||
- name: Run lint
|
||||
working-directory: ts/llama_cloud_services/
|
||||
run: pnpm run lint
|
||||
- name: Run Prettier
|
||||
working-directory: ts/llama_cloud_services/
|
||||
run: pnpm run format
|
||||
@@ -13,8 +13,6 @@ jobs:
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
@@ -49,6 +47,6 @@ jobs:
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
artifacts: "ts/llama_cloud_services/llama-cloud-services*.tgz"
|
||||
name: Release ${{ github.ref }} - LlamaCloud Services TS
|
||||
bodyFile: "ts/llama_cloud_services/CHANGELOG.md"
|
||||
name: Release ${{ github.ref_name }} - LlamaCloud Services TS
|
||||
generateReleaseNotes: true
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Lint - TypeScript
|
||||
name: Test - TypeScript
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -23,17 +23,14 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: "ts/llama_cloud_services/.nvmrc"
|
||||
- name: Install dependencies
|
||||
run: pnpm install --no-frozen-lockfile
|
||||
- name: Run Build
|
||||
working-directory: ts/llama_cloud_services/
|
||||
run: pnpm build
|
||||
run: pnpm -r install --no-frozen-lockfile
|
||||
- name: Build package
|
||||
run: pnpm --filter llama-cloud-services build
|
||||
- name: Run Tests
|
||||
working-directory: ts/llama_cloud_services/
|
||||
run: pnpm test
|
||||
|
||||
@@ -60,11 +60,13 @@ repos:
|
||||
additional_dependencies: [black==23.10.1]
|
||||
# Using PEP 8's line length in docs prevents excess left/right scrolling
|
||||
args: [--line-length=79]
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: v3.0.3
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: prettier
|
||||
exclude: ^(uv.lock|ts/llama_cloud_services/pnpm-lock.yaml|ts/e2e-tests)
|
||||
- id: lint-staged
|
||||
name: Run lint-staged for TS files
|
||||
entry: pnpm -w exec lint-staged
|
||||
language: system
|
||||
pass_filenames: false
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: v2.2.6
|
||||
hooks:
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ versions need to be kept consistent to sidecar it with `llama_cloud_services`. B
|
||||
|
||||
You can also do this with `./scripts/version-bump.py set 0.x.x` if you have `uv` installed.
|
||||
|
||||
Once the change is merged, push a tag `git tag -a v0.x.x -m 0.x.x` and `git push origin 0.x.x`.
|
||||
Once the change is merged, push a tag `git tag -a v0.x.x -m 0.x.x` and `git push origin v0.x.x`.
|
||||
|
||||
This tagging step can be done with `./scripts/version-bump tag`.
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ This repository contains the code for hand-written SDKs and clients for interact
|
||||
This includes:
|
||||
|
||||
- [LlamaParse](./parse.md) - A GenAI-native document parser that can parse complex document data for any downstream LLM use case (Agents, RAG, data processing, etc.).
|
||||
- [LlamaReport (beta/invite-only)](./report.md) - A prebuilt agentic report builder that can be used to build reports from a variety of data sources.
|
||||
- [LlamaExtract](./extract.md) - A prebuilt agentic data extractor that can be used to transform data into a structured JSON representation.
|
||||
- [LlamaCloud Index](./index.md) - A widely customizable and fully automated document ingestion pipeline that also serves retrieval purposes.
|
||||
|
||||
@@ -28,13 +27,11 @@ Then, you can use the services in your code:
|
||||
```python
|
||||
from llama_cloud_services import (
|
||||
LlamaParse,
|
||||
LlamaReport,
|
||||
LlamaExtract,
|
||||
LlamaCloudIndex,
|
||||
)
|
||||
|
||||
parser = LlamaParse(api_key="YOUR_API_KEY")
|
||||
report = LlamaReport(api_key="YOUR_API_KEY")
|
||||
extract = LlamaExtract(api_key="YOUR_API_KEY")
|
||||
index = LlamaCloudIndex(
|
||||
"my_first_index", project_name="default", api_key="YOUR_API_KEY"
|
||||
@@ -44,7 +41,6 @@ index = LlamaCloudIndex(
|
||||
See the quickstart guides for each service for more information:
|
||||
|
||||
- [LlamaParse](./parse.md)
|
||||
- [LlamaReport (beta/invite-only)](./report.md)
|
||||
- [LlamaExtract](./extract.md)
|
||||
- [LlamaCloud Index](./index.md)
|
||||
|
||||
@@ -57,13 +53,11 @@ You can also create your API key in the EU region [here](https://cloud.eu.llamai
|
||||
```python
|
||||
from llama_cloud_services import (
|
||||
LlamaParse,
|
||||
LlamaReport,
|
||||
LlamaExtract,
|
||||
EU_BASE_URL,
|
||||
)
|
||||
|
||||
parser = LlamaParse(api_key="YOUR_API_KEY", base_url=EU_BASE_URL)
|
||||
report = LlamaReport(api_key="YOUR_API_KEY", base_url=EU_BASE_URL)
|
||||
extract = LlamaExtract(api_key="YOUR_API_KEY", base_url=EU_BASE_URL)
|
||||
index = LlamaCloudIndex(
|
||||
"my_first_index",
|
||||
|
||||
+1
-1
@@ -4,6 +4,6 @@ In this folder you will find several python notebooks that contain examples rega
|
||||
|
||||
- [LlamaParse](./parse/)
|
||||
- [LlamaExtract](./extract/)
|
||||
- [LlamaReport](./report/)
|
||||
- [LlamaCloudIndex](./index/)
|
||||
|
||||
Follow the instructions in each notebook to get started!
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -43,7 +43,7 @@
|
||||
"source": [
|
||||
"from llama_cloud_services import LlamaParse\n",
|
||||
"\n",
|
||||
"api_key = \"llx-jwAQZL8T38onyL9hKBOXyRtnuCU0Fk3z7tmDhIT3L0GEfohJ\" # get from cloud.llamaindex.ai"
|
||||
"api_key = \"llx-...\" # get from cloud.llamaindex.ai"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -75,7 +75,6 @@
|
||||
" adaptive_long_table=True,\n",
|
||||
" outlined_table_extraction=True,\n",
|
||||
" output_tables_as_HTML=True,\n",
|
||||
" api_key=\"llx-jwAQZL8T38onyL9hKBOXyRtnuCU0Fk3z7tmDhIT3L0GEfohJ\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"result = await parser.aparse(\"./dcf_template.xlsx\")\n",
|
||||
|
||||
@@ -1,762 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Report Generation with LlamaReport\n",
|
||||
"\n",
|
||||
"In this notebook, we'll walk through the basic process of generating a report with LlamaReport, and highlight some of the key features of the library.\n",
|
||||
"\n",
|
||||
"TLDR:\n",
|
||||
"1. Download source data to use as knowledge base for the report\n",
|
||||
"2. Kick off report generation with a template\n",
|
||||
"3. Get the plan and review/accept/reject suggestions\n",
|
||||
"4. Get the final report\n",
|
||||
"5. Review/accept/reject suggestions to edit the final report\n",
|
||||
"6. Print the final report"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install llama-cloud-services"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 1. Download Source Data\n",
|
||||
"\n",
|
||||
"Here, we download the `Attention is All You Need` paper as a PDF.\n",
|
||||
"\n",
|
||||
"LlamaReport currently supports up to 5 files as input, and essentially any file type that can be parsed by LlamaParse.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!wget \"https://arxiv.org/pdf/1706.03762.pdf\" -O \"./attention.pdf\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 2. Kick off Report Generation\n",
|
||||
"\n",
|
||||
"Here, we kick off report generation with a template.\n",
|
||||
"\n",
|
||||
"The template can either be a string or a file path, but here we'll use a string.\n",
|
||||
"\n",
|
||||
"In our experiments, anything works as a template, but some general guidelines:\n",
|
||||
"\n",
|
||||
"- Use markdown formatting + instructions in each section to guide the report generation\n",
|
||||
"- If using an existing file as a template, provide extra instructions to guide the report generation\n",
|
||||
"\n",
|
||||
"**NOTE:** Since we are in a notebook, we will use async functions and `await` throughout. Synchronous methods that work without `await` are available by just removing the `a` from the method name and removing the `await` keyword."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from llama_cloud_services import LlamaReport\n",
|
||||
"\n",
|
||||
"llama_report = LlamaReport(\n",
|
||||
" api_key=\"llx-...\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"report_client = await llama_report.acreate_report(\n",
|
||||
" name=\"my_cool_report_on_attention\",\n",
|
||||
" # can pass in file paths or bytes\n",
|
||||
" input_files=[\"./attention.pdf\"],\n",
|
||||
" template_text=\"\"\"\\\n",
|
||||
"# [Some title]\\n\\n\n",
|
||||
"## TLDR\\n\n",
|
||||
"A quick summary of the paper.\\n\\n\n",
|
||||
"## Details\\n\n",
|
||||
"More details about the paper, possibly more than one section here.\\n\n",
|
||||
"\"\"\",\n",
|
||||
" # optional additional instructions for the report generation\n",
|
||||
" # template_instructions=None,\n",
|
||||
" # optional file path to an existing template instead of template_text\n",
|
||||
" # template_file=None,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The returned `ReportClient` object is used to interact with the report generation process for this specific report."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Report(id=0a394b33-1a3e-463c-b5cb-7ff8ab827d0a, name=my_cool_report_on_attention)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(report_client)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 3. Get the plan\n",
|
||||
"\n",
|
||||
"The first phases of report generation involve ingesting the source data and generating a plan.\n",
|
||||
"\n",
|
||||
"The plan is a list of instructions for the report generation, and can be reviewed/accepted/rejected by the user.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"plan = await report_client.await_for_plan(\n",
|
||||
" timeout=10000,\n",
|
||||
" poll_interval=10,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"# {title}\n",
|
||||
"[ReportQuery(field='title', prompt='Generate a clear and concise title for this paper about the Transformer model and attention mechanisms', context='The paper discusses the Transformer architecture for sequence transduction using attention mechanisms, focusing on machine translation applications')]\n",
|
||||
"==================\n",
|
||||
"## TLDR\n",
|
||||
"\n",
|
||||
"{tldr_content}\n",
|
||||
"[ReportQuery(field='tldr_content', prompt='Write a brief, clear summary of the key points about the Transformer model', context='Focus on the main innovations: attention mechanisms, efficiency improvements, and state-of-the-art results in machine translation')]\n",
|
||||
"==================\n",
|
||||
"## Details\n",
|
||||
"\n",
|
||||
"{details_content}\n",
|
||||
"[ReportQuery(field='details_content', prompt='Provide detailed information about the Transformer model architecture and its applications', context='Include information about:\\n- The attention mechanism implementation\\n- Advantages over recurrent and convolutional models\\n- Performance in machine translation tasks\\n- Training efficiency improvements')]\n",
|
||||
"==================\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for plan_block in plan.blocks:\n",
|
||||
" print(plan_block.block.template)\n",
|
||||
" print(plan_block.queries)\n",
|
||||
" print(\"==================\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"With the plan, we can either use it to kick off generation of the final report, or we can edit the plan and adjust it as needed.\n",
|
||||
"\n",
|
||||
"While we could manually edit the objects here and use `await report_client.aupdate_plan(action=\"edit\", updated_plan=plan)`, we can also use `LlamaReport` to agentically edit the plan."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"suggestions = await report_client.asuggest_edits(\n",
|
||||
" \"Can you split the details section into two sections?\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Justification for change: \n",
|
||||
"I'll help you break down the details section into two distinct parts - one focusing on the architecture and another on the practical applications and performance. This will make the content more organized and easier to follow. The original block at index 2 will be replaced with these two new sections.\n",
|
||||
"\n",
|
||||
"Proposed changes:\n",
|
||||
"\n",
|
||||
"## Architecture Details\n",
|
||||
"\n",
|
||||
"{architecture_content}\n",
|
||||
"\n",
|
||||
"[ReportQuery(field='architecture_content', prompt='Describe the technical details of the Transformer model architecture', context='Focus on:\\n- Core components of the Transformer architecture\\n- Self-attention mechanism implementation\\n- Multi-head attention details\\n- Position encoding approach\\n- Feed-forward network structure')]\n",
|
||||
"==================\n",
|
||||
"\n",
|
||||
"## Performance and Applications\n",
|
||||
"\n",
|
||||
"{applications_content}\n",
|
||||
"\n",
|
||||
"[ReportQuery(field='applications_content', prompt='Explain the practical applications and performance advantages of the Transformer model', context='Cover:\\n- Comparison with RNN and CNN models\\n- Machine translation results and benchmarks\\n- Training efficiency improvements\\n- Real-world applications and use cases\\n- Scalability benefits')]\n",
|
||||
"==================\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for suggestion in suggestions:\n",
|
||||
" print(\"Justification for change:\", suggestion.justification)\n",
|
||||
" print(\"Proposed changes:\")\n",
|
||||
" for plan_block in suggestion.blocks:\n",
|
||||
" print(plan_block.block.template)\n",
|
||||
" print(plan_block.queries)\n",
|
||||
" print(\"==================\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This looks pretty good! We can also use the client to automatically accept and apply, or reject, these suggestions.\n",
|
||||
"\n",
|
||||
"This will (locally) keep track of the history of changes, so that future suggestions can be based on the previous changes."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"for suggestion in suggestions:\n",
|
||||
" await report_client.aaccept_edit(suggestion)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"What effect did that have on the tracked local history? Let's see!"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[EditAction(block_idx=2, old_content='## Details\\n\\n{details_content}\\n\\nField: details_content, Prompt: Provide detailed information about the Transformer model architecture and its applications, Context: Include information about:\\n- The attention mechanism implementation\\n- Advantages over recurrent and convolutional models\\n- Performance in machine translation tasks\\n- Training efficiency improvements\\nDepends on: none', new_content='\\n## Architecture Details\\n\\n{architecture_content}\\n\\n\\nField: architecture_content, Prompt: Describe the technical details of the Transformer model architecture, Context: Focus on:\\n- Core components of the Transformer architecture\\n- Self-attention mechanism implementation\\n- Multi-head attention details\\n- Position encoding approach\\n- Feed-forward network structure\\nDepends on: none', action='approved', timestamp=datetime.datetime(2025, 2, 4, 20, 59, 55, 773558)),\n",
|
||||
" EditAction(block_idx=3, old_content='[No old content]', new_content='\\n## Performance and Applications\\n\\n{applications_content}\\n\\n\\nField: applications_content, Prompt: Explain the practical applications and performance advantages of the Transformer model, Context: Cover:\\n- Comparison with RNN and CNN models\\n- Machine translation results and benchmarks\\n- Training efficiency improvements\\n- Real-world applications and use cases\\n- Scalability benefits\\nDepends on: previous', action='approved', timestamp=datetime.datetime(2025, 2, 4, 20, 59, 55, 773687))]"
|
||||
]
|
||||
},
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"report_client.edit_history"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[Message(role=<MessageRole.USER: 'user'>, content='Can you split the details section into two sections?', timestamp=datetime.datetime(2025, 2, 4, 20, 59, 47, 754848)),\n",
|
||||
" Message(role=<MessageRole.ASSISTANT: 'assistant'>, content=\"\\nI'll help you break down the details section into two distinct parts - one focusing on the architecture and another on the practical applications and performance. This will make the content more organized and easier to follow. The original block at index 2 will be replaced with these two new sections.\\n\", timestamp=datetime.datetime(2025, 2, 4, 20, 59, 55, 482070))]"
|
||||
]
|
||||
},
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"report_client.chat_history"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"These two items are used to provide context for future suggestions! You can always clear this, or provide your own history."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# report_client.suggest_edits(\"....\", chat_history=[{\"role\": \"user\", \"content\": \"...\"}, ...])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 4. Get the final report\n",
|
||||
"\n",
|
||||
"Now that we have a plan, we can kick off generation of the final report."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# kicks off report generation\n",
|
||||
"await report_client.aupdate_plan(action=\"approve\")\n",
|
||||
"\n",
|
||||
"# waits for report generation to complete\n",
|
||||
"report = await report_client.await_completion(\n",
|
||||
" timeout=10000,\n",
|
||||
" poll_interval=10,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"# Attention Is All You Need: A Pure Attention-Based Architecture for Neural Machine Translation\n",
|
||||
"\n",
|
||||
"## TLDR\n",
|
||||
"\n",
|
||||
"The Transformer introduced a revolutionary architecture that relies entirely on attention mechanisms, eliminating the need for recurrence or convolution in sequence processing. Its key innovations include multi-head self-attention for parallel processing of input sequences, scaled dot-product attention for efficient computation, and positional encodings for sequence order awareness. The model achieved breakthrough results in machine translation (28.4 BLEU on English-to-German, 41.8 BLEU on English-to-French) while requiring significantly less training time than previous approaches, training in 3.5 days on 8 GPUs. This architecture demonstrated that attention mechanisms alone are sufficient for state-of-the-art sequence modeling, setting a new direction for natural language processing.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## Architecture Details\n",
|
||||
"\n",
|
||||
"The Transformer architecture represents a groundbreaking approach to sequence processing, built entirely on attention mechanisms without recurrence or convolution. Here are its key technical details:\n",
|
||||
"\n",
|
||||
"Core Components:\n",
|
||||
"- Encoder-decoder architecture with stacked self-attention and point-wise feed-forward layers\n",
|
||||
"- Each layer contains two main sub-layers: multi-head self-attention mechanism and position-wise feed-forward network\n",
|
||||
"- Layer normalization and residual connections between sub-layers\n",
|
||||
"- No recurrent or convolutional elements, enabling parallel processing\n",
|
||||
"\n",
|
||||
"Self-Attention Mechanism:\n",
|
||||
"- Processes relationships between all positions in a sequence simultaneously\n",
|
||||
"- Computes attention weights using queries, keys, and values derived from input representations\n",
|
||||
"- Implements scaled dot-product attention to prevent gradient issues with large input dimensions\n",
|
||||
"- Allows direct modeling of dependencies regardless of positional distance\n",
|
||||
"- Uses masking in decoder to prevent leftward information flow and maintain auto-regressive property\n",
|
||||
"\n",
|
||||
"Multi-Head Attention:\n",
|
||||
"- Employs multiple attention heads operating in parallel\n",
|
||||
"- Each head processes information in different representation subspaces\n",
|
||||
"- Three types of attention applications:\n",
|
||||
" 1. Encoder self-attention (all positions attend to each other)\n",
|
||||
" 2. Decoder self-attention (each position attends to previous positions)\n",
|
||||
" 3. Encoder-decoder attention (decoder queries attend to encoder outputs)\n",
|
||||
"- Counteracts reduced resolution from attention averaging through parallel processing\n",
|
||||
"\n",
|
||||
"Position-wise Feed-Forward Network:\n",
|
||||
"- Applied identically to each position separately\n",
|
||||
"- Consists of two linear transformations with ReLU activation\n",
|
||||
"- Structure: FFN(x) = max(0, xW1 + b1)W2 + b2\n",
|
||||
"- Input and output dimensionality: dmodel = 512\n",
|
||||
"- Inner-layer dimensionality: dff = 2048\n",
|
||||
"- Parameters vary between layers but remain constant across positions\n",
|
||||
"\n",
|
||||
"Position Encoding:\n",
|
||||
"- Adds positional information to input embeddings\n",
|
||||
"- Enables the model to consider sequential order without recurrence\n",
|
||||
"- Implements sinusoidal position encodings to allow model to attend to relative positions\n",
|
||||
"- Maintains constant number of operations between any two positions, unlike convolutional approaches\n",
|
||||
"- Allows effective modeling of both local and long-range dependencies\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## Performance and Applications\n",
|
||||
"\n",
|
||||
"The Transformer model demonstrates significant performance advantages and practical applications across multiple domains:\n",
|
||||
"\n",
|
||||
"Performance Advantages over RNN/CNN Models:\n",
|
||||
"- Eliminates sequential computation constraints present in RNNs, enabling superior parallelization\n",
|
||||
"- Reduces operations needed for relating distant positions to a constant number, compared to linear/logarithmic scaling in CNNs\n",
|
||||
"- Processes all input and output positions simultaneously through self-attention mechanisms\n",
|
||||
"- Achieves state-of-the-art results while requiring significantly less computational resources\n",
|
||||
"\n",
|
||||
"Machine Translation Benchmarks:\n",
|
||||
"- WMT 2014 English-to-German: 28.4 BLEU score, exceeding previous best results by over 2 BLEU points\n",
|
||||
"- WMT 2014 English-to-French: 41.8 BLEU score (single-model state-of-the-art)\n",
|
||||
"- Surpasses performance of existing model ensembles in translation tasks\n",
|
||||
"\n",
|
||||
"Training Efficiency:\n",
|
||||
"- Requires only 3.5 days of training on eight GPUs for state-of-the-art performance\n",
|
||||
"- Achieves superior results at \"a small fraction of the training costs\" compared to previous models\n",
|
||||
"- Enables significantly faster training through parallel processing of input/output sequences\n",
|
||||
"- Can reach production-quality performance in as little as twelve hours on modern GPU hardware\n",
|
||||
"\n",
|
||||
"Real-world Applications:\n",
|
||||
"- Machine translation systems\n",
|
||||
"- Natural language understanding tasks\n",
|
||||
"- Reading comprehension\n",
|
||||
"- Abstractive summarization\n",
|
||||
"- Text entailment analysis\n",
|
||||
"- Constituency parsing (achieving 92.7 F1 score in semi-supervised settings)\n",
|
||||
"- Adaptable to both large and limited training data scenarios\n",
|
||||
"\n",
|
||||
"Scalability Benefits:\n",
|
||||
"- Highly parallelizable architecture enables efficient scaling across multiple GPUs\n",
|
||||
"- Constant computational complexity for relating any input/output positions\n",
|
||||
"- Effective handling of long-range dependencies in sequences\n",
|
||||
"- Maintains performance quality while scaling to larger datasets and model sizes\n",
|
||||
"- Generalizes well across different tasks and domains without architectural changes\n",
|
||||
"- Supports efficient inference and deployment in production environments\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"report_text = \"\\n\\n\".join([block.template for block in report.blocks])\n",
|
||||
"print(report_text)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 5. Edit the final report\n",
|
||||
"\n",
|
||||
"Now that we have a report, we can edit it.\n",
|
||||
"\n",
|
||||
"We can use the `asuggest_edits` method to get suggestions for edits, and then use the `aaccept_edit`/`areject_edit` methods to apply them.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Justification for change: \n",
|
||||
"I'd suggest changing \"TLDR\" to \"Executive Summary\" which is more appropriate for a professional or academic report. This term is widely used in formal documents and better reflects the nature of this concise overview section while maintaining the same function of providing a quick summary of the key points.\n",
|
||||
"\n",
|
||||
"Proposed changes:\n",
|
||||
"## Executive Summary\n",
|
||||
"\n",
|
||||
"The Transformer introduced a revolutionary architecture that relies entirely on attention mechanisms, eliminating the need for recurrence or convolution in sequence processing. Its key innovations include multi-head self-attention for parallel processing of input sequences, scaled dot-product attention for efficient computation, and positional encodings for sequence order awareness. The model achieved breakthrough results in machine translation (28.4 BLEU on English-to-German, 41.8 BLEU on English-to-French) while requiring significantly less training time than previous approaches, training in 3.5 days on 8 GPUs. This architecture demonstrated that attention mechanisms alone are sufficient for state-of-the-art sequence modeling, setting a new direction for natural language processing.\n",
|
||||
"==================\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"suggestions = await report_client.asuggest_edits(\n",
|
||||
" \"Can you change the TLDR header to something more professional?\"\n",
|
||||
")\n",
|
||||
"for suggestion in suggestions:\n",
|
||||
" print(\"Justification for change:\", suggestion.justification)\n",
|
||||
" print(\"Proposed changes:\")\n",
|
||||
" for block in suggestion.blocks:\n",
|
||||
" print(block.template)\n",
|
||||
" print(\"==================\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Changing to \"Executive Summary\" sounds reasonable, lets accept that!\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"for suggestion in suggestions:\n",
|
||||
" await report_client.aaccept_edit(suggestion)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 7. Print the final report\n",
|
||||
"\n",
|
||||
"Now that we have a report, we can print it."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"# Attention Is All You Need: A Pure Attention-Based Architecture for Neural Machine Translation\n",
|
||||
"\n",
|
||||
"## Executive Summary\n",
|
||||
"\n",
|
||||
"The Transformer introduced a revolutionary architecture that relies entirely on attention mechanisms, eliminating the need for recurrence or convolution in sequence processing. Its key innovations include multi-head self-attention for parallel processing of input sequences, scaled dot-product attention for efficient computation, and positional encodings for sequence order awareness. The model achieved breakthrough results in machine translation (28.4 BLEU on English-to-German, 41.8 BLEU on English-to-French) while requiring significantly less training time than previous approaches, training in 3.5 days on 8 GPUs. This architecture demonstrated that attention mechanisms alone are sufficient for state-of-the-art sequence modeling, setting a new direction for natural language processing.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## Architecture Details\n",
|
||||
"\n",
|
||||
"The Transformer architecture represents a groundbreaking approach to sequence processing, built entirely on attention mechanisms without recurrence or convolution. Here are its key technical details:\n",
|
||||
"\n",
|
||||
"Core Components:\n",
|
||||
"- Encoder-decoder architecture with stacked self-attention and point-wise feed-forward layers\n",
|
||||
"- Each layer contains two main sub-layers: multi-head self-attention mechanism and position-wise feed-forward network\n",
|
||||
"- Layer normalization and residual connections between sub-layers\n",
|
||||
"- No recurrent or convolutional elements, enabling parallel processing\n",
|
||||
"\n",
|
||||
"Self-Attention Mechanism:\n",
|
||||
"- Processes relationships between all positions in a sequence simultaneously\n",
|
||||
"- Computes attention weights using queries, keys, and values derived from input representations\n",
|
||||
"- Implements scaled dot-product attention to prevent gradient issues with large input dimensions\n",
|
||||
"- Allows direct modeling of dependencies regardless of positional distance\n",
|
||||
"- Uses masking in decoder to prevent leftward information flow and maintain auto-regressive property\n",
|
||||
"\n",
|
||||
"Multi-Head Attention:\n",
|
||||
"- Employs multiple attention heads operating in parallel\n",
|
||||
"- Each head processes information in different representation subspaces\n",
|
||||
"- Three types of attention applications:\n",
|
||||
" 1. Encoder self-attention (all positions attend to each other)\n",
|
||||
" 2. Decoder self-attention (each position attends to previous positions)\n",
|
||||
" 3. Encoder-decoder attention (decoder queries attend to encoder outputs)\n",
|
||||
"- Counteracts reduced resolution from attention averaging through parallel processing\n",
|
||||
"\n",
|
||||
"Position-wise Feed-Forward Network:\n",
|
||||
"- Applied identically to each position separately\n",
|
||||
"- Consists of two linear transformations with ReLU activation\n",
|
||||
"- Structure: FFN(x) = max(0, xW1 + b1)W2 + b2\n",
|
||||
"- Input and output dimensionality: dmodel = 512\n",
|
||||
"- Inner-layer dimensionality: dff = 2048\n",
|
||||
"- Parameters vary between layers but remain constant across positions\n",
|
||||
"\n",
|
||||
"Position Encoding:\n",
|
||||
"- Adds positional information to input embeddings\n",
|
||||
"- Enables the model to consider sequential order without recurrence\n",
|
||||
"- Implements sinusoidal position encodings to allow model to attend to relative positions\n",
|
||||
"- Maintains constant number of operations between any two positions, unlike convolutional approaches\n",
|
||||
"- Allows effective modeling of both local and long-range dependencies\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## Performance and Applications\n",
|
||||
"\n",
|
||||
"The Transformer model demonstrates significant performance advantages and practical applications across multiple domains:\n",
|
||||
"\n",
|
||||
"Performance Advantages over RNN/CNN Models:\n",
|
||||
"- Eliminates sequential computation constraints present in RNNs, enabling superior parallelization\n",
|
||||
"- Reduces operations needed for relating distant positions to a constant number, compared to linear/logarithmic scaling in CNNs\n",
|
||||
"- Processes all input and output positions simultaneously through self-attention mechanisms\n",
|
||||
"- Achieves state-of-the-art results while requiring significantly less computational resources\n",
|
||||
"\n",
|
||||
"Machine Translation Benchmarks:\n",
|
||||
"- WMT 2014 English-to-German: 28.4 BLEU score, exceeding previous best results by over 2 BLEU points\n",
|
||||
"- WMT 2014 English-to-French: 41.8 BLEU score (single-model state-of-the-art)\n",
|
||||
"- Surpasses performance of existing model ensembles in translation tasks\n",
|
||||
"\n",
|
||||
"Training Efficiency:\n",
|
||||
"- Requires only 3.5 days of training on eight GPUs for state-of-the-art performance\n",
|
||||
"- Achieves superior results at \"a small fraction of the training costs\" compared to previous models\n",
|
||||
"- Enables significantly faster training through parallel processing of input/output sequences\n",
|
||||
"- Can reach production-quality performance in as little as twelve hours on modern GPU hardware\n",
|
||||
"\n",
|
||||
"Real-world Applications:\n",
|
||||
"- Machine translation systems\n",
|
||||
"- Natural language understanding tasks\n",
|
||||
"- Reading comprehension\n",
|
||||
"- Abstractive summarization\n",
|
||||
"- Text entailment analysis\n",
|
||||
"- Constituency parsing (achieving 92.7 F1 score in semi-supervised settings)\n",
|
||||
"- Adaptable to both large and limited training data scenarios\n",
|
||||
"\n",
|
||||
"Scalability Benefits:\n",
|
||||
"- Highly parallelizable architecture enables efficient scaling across multiple GPUs\n",
|
||||
"- Constant computational complexity for relating any input/output positions\n",
|
||||
"- Effective handling of long-range dependencies in sequences\n",
|
||||
"- Maintains performance quality while scaling to larger datasets and model sizes\n",
|
||||
"- Generalizes well across different tasks and domains without architectural changes\n",
|
||||
"- Supports efficient inference and deployment in production environments\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"report_response = await report_client.aget()\n",
|
||||
"report_text = \"\\n\\n\".join([block.template for block in report_response.report.blocks])\n",
|
||||
"print(report_text)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We can also see the sources for each block!"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"0.99687636\n",
|
||||
"# Abstract\n",
|
||||
"\n",
|
||||
"The dominant sequence transduction models are based on complex recurrent or convolutiona\n",
|
||||
"==================\n",
|
||||
"0.99591404\n",
|
||||
"# 2 Background\n",
|
||||
"\n",
|
||||
"The goal of reducing sequential computation also forms the foundation of the Extende\n",
|
||||
"==================\n",
|
||||
"0.9951325\n",
|
||||
"# 1 Introduction\n",
|
||||
"\n",
|
||||
"Recurrent neural networks, long short-term memory [13] and gated recurrent [7] neu\n",
|
||||
"==================\n",
|
||||
"0.99442345\n",
|
||||
"# 7 Conclusion\n",
|
||||
"\n",
|
||||
"In this work, we presented the Transformer, the first sequence transduction model ba\n",
|
||||
"==================\n",
|
||||
"0.9967649\n",
|
||||
"# 3.2.3 Applications of Attention in our Model\n",
|
||||
"\n",
|
||||
"The Transformer uses multi-head attention in three d\n",
|
||||
"==================\n",
|
||||
"0.99533635\n",
|
||||
"# 2 Background\n",
|
||||
"\n",
|
||||
"The goal of reducing sequential computation also forms the foundation of the Extende\n",
|
||||
"==================\n",
|
||||
"0.9935868\n",
|
||||
"# Abstract\n",
|
||||
"\n",
|
||||
"The dominant sequence transduction models are based on complex recurrent or convolutiona\n",
|
||||
"==================\n",
|
||||
"0.98780584\n",
|
||||
"# Outputs\n",
|
||||
"\n",
|
||||
"(shifted right)\n",
|
||||
"\n",
|
||||
"Figure 1: The Transformer - model architecture.\n",
|
||||
"\n",
|
||||
"The Transformer follows\n",
|
||||
"==================\n",
|
||||
"0.9205043\n",
|
||||
"# 3.3 Position-wise Feed-Forward Networks\n",
|
||||
"\n",
|
||||
"In addition to attention sub-layers, each of the layers i\n",
|
||||
"==================\n",
|
||||
"0.79581684\n",
|
||||
"# 1 Introduction\n",
|
||||
"\n",
|
||||
"Recurrent neural networks, long short-term memory [13] and gated recurrent [7] neu\n",
|
||||
"==================\n",
|
||||
"0.9946774\n",
|
||||
"# Abstract\n",
|
||||
"\n",
|
||||
"The dominant sequence transduction models are based on complex recurrent or convolutiona\n",
|
||||
"==================\n",
|
||||
"0.97079873\n",
|
||||
"# 7 Conclusion\n",
|
||||
"\n",
|
||||
"In this work, we presented the Transformer, the first sequence transduction model ba\n",
|
||||
"==================\n",
|
||||
"0.9535353\n",
|
||||
"# 6.3 English Constituency Parsing\n",
|
||||
"\n",
|
||||
"To evaluate if the Transformer can generalize to other tasks we \n",
|
||||
"==================\n",
|
||||
"0.9514138\n",
|
||||
"# 2 Background\n",
|
||||
"\n",
|
||||
"The goal of reducing sequential computation also forms the foundation of the Extende\n",
|
||||
"==================\n",
|
||||
"0.9790758\n",
|
||||
"# 1 Introduction\n",
|
||||
"\n",
|
||||
"Recurrent neural networks, long short-term memory [13] and gated recurrent [7] neu\n",
|
||||
"==================\n",
|
||||
"0.92262185\n",
|
||||
"# Outputs\n",
|
||||
"\n",
|
||||
"(shifted right)\n",
|
||||
"\n",
|
||||
"Figure 1: The Transformer - model architecture.\n",
|
||||
"\n",
|
||||
"The Transformer follows\n",
|
||||
"==================\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for block in report_response.report.blocks:\n",
|
||||
" # Each block has a list of sources, which are the nodes that were used to generate the block\n",
|
||||
" for source in block.sources:\n",
|
||||
" print(source.score)\n",
|
||||
" print(source.node.text[:100])\n",
|
||||
" print(\"==================\")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "llama-parse-aNC435Vv-py3.10",
|
||||
"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,19 @@
|
||||
{
|
||||
"name": "llama-cloud-services-workspace",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"private": true,
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"devDependencies": {
|
||||
"prettier": "^3.6.2",
|
||||
"lint-staged": "^15.4.2"
|
||||
},
|
||||
"lint-staged": {
|
||||
"ts/llama_cloud_services/src/**/*.{ts,tsx,js,jsx}": [
|
||||
"pnpm --filter llama-cloud-services exec eslint --fix",
|
||||
"pnpm --filter llama-cloud-services exec prettier --write"
|
||||
]
|
||||
},
|
||||
"packageManager": "pnpm@10.11.1+sha512.e519b9f7639869dc8d5c3c5dfef73b3f091094b0a006d7317353c72b124e80e1afd429732e28705ad6bfa1ee879c1fce46c128ccebd3192101f43dd67c667912"
|
||||
}
|
||||
Generated
+245
@@ -6,6 +6,15 @@ settings:
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
devDependencies:
|
||||
lint-staged:
|
||||
specifier: ^15.4.2
|
||||
version: 15.5.2
|
||||
prettier:
|
||||
specifier: ^3.6.2
|
||||
version: 3.6.2
|
||||
|
||||
ts/e2e-tests:
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
@@ -829,6 +838,10 @@ packages:
|
||||
ajv@8.17.1:
|
||||
resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
|
||||
|
||||
ansi-escapes@7.0.0:
|
||||
resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
ansi-regex@5.0.1:
|
||||
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -933,6 +946,10 @@ packages:
|
||||
resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
cli-truncate@4.0.0:
|
||||
resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
cliui@8.0.1:
|
||||
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -944,10 +961,17 @@ packages:
|
||||
color-name@1.1.4:
|
||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||
|
||||
colorette@2.0.20:
|
||||
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
|
||||
|
||||
commander@13.0.0:
|
||||
resolution: {integrity: sha512-oPYleIY8wmTVzkvQq10AEok6YcTC4sRUBl8F9gVuwchGVUCTbl/vhLTaQqutuuySYOsu8YTgV+OxKc/8Yvx+mQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
commander@13.1.0:
|
||||
resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
commondir@1.0.1:
|
||||
resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
|
||||
|
||||
@@ -1007,6 +1031,10 @@ packages:
|
||||
emoji-regex@9.2.2:
|
||||
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
|
||||
|
||||
environment@1.1.0:
|
||||
resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
es-module-lexer@1.7.0:
|
||||
resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
|
||||
|
||||
@@ -1071,6 +1099,13 @@ packages:
|
||||
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
eventemitter3@5.0.1:
|
||||
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
|
||||
|
||||
execa@8.0.1:
|
||||
resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
|
||||
engines: {node: '>=16.17'}
|
||||
|
||||
expect-type@1.2.2:
|
||||
resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
@@ -1152,6 +1187,14 @@ packages:
|
||||
resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
get-east-asian-width@1.3.1:
|
||||
resolution: {integrity: sha512-R1QfovbPsKmosqTnPoRFiJ7CF9MLRgb53ChvMZm+r4p76/+8yKDy17qLL2PKInORy2RkZZekuK0efYgmzTkXyQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
get-stream@8.0.1:
|
||||
resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
get-tsconfig@4.10.1:
|
||||
resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==}
|
||||
|
||||
@@ -1198,6 +1241,10 @@ packages:
|
||||
html-escaper@2.0.2:
|
||||
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
|
||||
|
||||
human-signals@5.0.0:
|
||||
resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
|
||||
engines: {node: '>=16.17.0'}
|
||||
|
||||
ieee754@1.2.1:
|
||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||
|
||||
@@ -1229,6 +1276,14 @@ packages:
|
||||
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
is-fullwidth-code-point@4.0.0:
|
||||
resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
is-fullwidth-code-point@5.1.0:
|
||||
resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
is-glob@4.0.3:
|
||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -1251,6 +1306,10 @@ packages:
|
||||
is-reference@1.2.1:
|
||||
resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==}
|
||||
|
||||
is-stream@3.0.0:
|
||||
resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
is-unicode-supported@1.3.0:
|
||||
resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -1314,6 +1373,19 @@ packages:
|
||||
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
lilconfig@3.1.3:
|
||||
resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
lint-staged@15.5.2:
|
||||
resolution: {integrity: sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w==}
|
||||
engines: {node: '>=18.12.0'}
|
||||
hasBin: true
|
||||
|
||||
listr2@8.3.3:
|
||||
resolution: {integrity: sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
locate-path@6.0.0:
|
||||
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -1328,6 +1400,10 @@ packages:
|
||||
resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
log-update@6.1.0:
|
||||
resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
loupe@3.2.0:
|
||||
resolution: {integrity: sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==}
|
||||
|
||||
@@ -1347,6 +1423,9 @@ packages:
|
||||
resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
merge-stream@2.0.0:
|
||||
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
|
||||
|
||||
merge2@1.4.1:
|
||||
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -1355,6 +1434,10 @@ packages:
|
||||
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
||||
engines: {node: '>=8.6'}
|
||||
|
||||
mimic-fn@4.0.0:
|
||||
resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
mimic-function@5.0.1:
|
||||
resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -1414,6 +1497,10 @@ packages:
|
||||
node-fetch-native@1.6.7:
|
||||
resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==}
|
||||
|
||||
npm-run-path@5.3.0:
|
||||
resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
nypm@0.5.4:
|
||||
resolution: {integrity: sha512-X0SNNrZiGU8/e/zAB7sCTtdxWTMSIO73q+xuKgglm2Yvzwlo8UoC5FNySQFCvl84uPaeADkqHUZUkWy4aH4xOA==}
|
||||
engines: {node: ^14.16.0 || >=16.10.0}
|
||||
@@ -1422,6 +1509,10 @@ packages:
|
||||
ohash@1.1.6:
|
||||
resolution: {integrity: sha512-TBu7PtV8YkAZn0tSxobKY2n2aAQva936lhRrj6957aDaCf9IEtqsKbgMzXE/F/sjqYOwmrukeORHNLe5glk7Cg==}
|
||||
|
||||
onetime@6.0.0:
|
||||
resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
onetime@7.0.0:
|
||||
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -1461,6 +1552,10 @@ packages:
|
||||
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
path-key@4.0.0:
|
||||
resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
path-parse@1.0.7:
|
||||
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
|
||||
|
||||
@@ -1492,6 +1587,11 @@ packages:
|
||||
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
pidtree@0.6.0:
|
||||
resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==}
|
||||
engines: {node: '>=0.10'}
|
||||
hasBin: true
|
||||
|
||||
pkg-types@1.3.1:
|
||||
resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
|
||||
|
||||
@@ -1558,6 +1658,9 @@ packages:
|
||||
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
|
||||
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
||||
|
||||
rfdc@1.4.1:
|
||||
resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
|
||||
|
||||
rollup-plugin-dts@6.2.1:
|
||||
resolution: {integrity: sha512-sR3CxYUl7i2CHa0O7bA45mCrgADyAQ0tVtGSqi3yvH28M+eg1+g5d7kQ9hLvEz5dorK3XVsH5L2jwHLQf72DzA==}
|
||||
engines: {node: '>=16'}
|
||||
@@ -1609,6 +1712,14 @@ packages:
|
||||
resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
slice-ansi@5.0.0:
|
||||
resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
slice-ansi@7.1.0:
|
||||
resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
source-map-js@1.2.1:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -1627,6 +1738,10 @@ packages:
|
||||
resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
string-argv@0.3.2:
|
||||
resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
|
||||
engines: {node: '>=0.6.19'}
|
||||
|
||||
string-width@4.2.3:
|
||||
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -1647,6 +1762,10 @@ packages:
|
||||
resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
strip-final-newline@3.0.0:
|
||||
resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
strip-json-comments@3.1.1:
|
||||
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -1871,6 +1990,10 @@ packages:
|
||||
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
wrap-ansi@9.0.0:
|
||||
resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
y18n@5.0.8:
|
||||
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -1878,6 +2001,11 @@ packages:
|
||||
yallist@4.0.0:
|
||||
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
|
||||
|
||||
yaml@2.8.1:
|
||||
resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==}
|
||||
engines: {node: '>= 14.6'}
|
||||
hasBin: true
|
||||
|
||||
yargs-parser@21.1.1:
|
||||
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -2551,6 +2679,10 @@ snapshots:
|
||||
json-schema-traverse: 1.0.0
|
||||
require-from-string: 2.0.2
|
||||
|
||||
ansi-escapes@7.0.0:
|
||||
dependencies:
|
||||
environment: 1.1.0
|
||||
|
||||
ansi-regex@5.0.1: {}
|
||||
|
||||
ansi-regex@6.1.0: {}
|
||||
@@ -2665,6 +2797,11 @@ snapshots:
|
||||
|
||||
cli-spinners@2.9.2: {}
|
||||
|
||||
cli-truncate@4.0.0:
|
||||
dependencies:
|
||||
slice-ansi: 5.0.0
|
||||
string-width: 7.2.0
|
||||
|
||||
cliui@8.0.1:
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
@@ -2677,8 +2814,12 @@ snapshots:
|
||||
|
||||
color-name@1.1.4: {}
|
||||
|
||||
colorette@2.0.20: {}
|
||||
|
||||
commander@13.0.0: {}
|
||||
|
||||
commander@13.1.0: {}
|
||||
|
||||
commondir@1.0.1: {}
|
||||
|
||||
concat-map@0.0.1: {}
|
||||
@@ -2717,6 +2858,8 @@ snapshots:
|
||||
|
||||
emoji-regex@9.2.2: {}
|
||||
|
||||
environment@1.1.0: {}
|
||||
|
||||
es-module-lexer@1.7.0: {}
|
||||
|
||||
esbuild@0.21.5:
|
||||
@@ -2824,6 +2967,20 @@ snapshots:
|
||||
|
||||
esutils@2.0.3: {}
|
||||
|
||||
eventemitter3@5.0.1: {}
|
||||
|
||||
execa@8.0.1:
|
||||
dependencies:
|
||||
cross-spawn: 7.0.6
|
||||
get-stream: 8.0.1
|
||||
human-signals: 5.0.0
|
||||
is-stream: 3.0.0
|
||||
merge-stream: 2.0.0
|
||||
npm-run-path: 5.3.0
|
||||
onetime: 6.0.0
|
||||
signal-exit: 4.1.0
|
||||
strip-final-newline: 3.0.0
|
||||
|
||||
expect-type@1.2.2: {}
|
||||
|
||||
fast-deep-equal@3.1.3: {}
|
||||
@@ -2899,6 +3056,10 @@ snapshots:
|
||||
|
||||
get-east-asian-width@1.3.0: {}
|
||||
|
||||
get-east-asian-width@1.3.1: {}
|
||||
|
||||
get-stream@8.0.1: {}
|
||||
|
||||
get-tsconfig@4.10.1:
|
||||
dependencies:
|
||||
resolve-pkg-maps: 1.0.0
|
||||
@@ -2953,6 +3114,8 @@ snapshots:
|
||||
|
||||
html-escaper@2.0.2: {}
|
||||
|
||||
human-signals@5.0.0: {}
|
||||
|
||||
ieee754@1.2.1: {}
|
||||
|
||||
ignore@5.3.2: {}
|
||||
@@ -2974,6 +3137,12 @@ snapshots:
|
||||
|
||||
is-fullwidth-code-point@3.0.0: {}
|
||||
|
||||
is-fullwidth-code-point@4.0.0: {}
|
||||
|
||||
is-fullwidth-code-point@5.1.0:
|
||||
dependencies:
|
||||
get-east-asian-width: 1.3.1
|
||||
|
||||
is-glob@4.0.3:
|
||||
dependencies:
|
||||
is-extglob: 2.1.1
|
||||
@@ -2990,6 +3159,8 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.8
|
||||
|
||||
is-stream@3.0.0: {}
|
||||
|
||||
is-unicode-supported@1.3.0: {}
|
||||
|
||||
is-unicode-supported@2.1.0: {}
|
||||
@@ -3053,6 +3224,32 @@ snapshots:
|
||||
prelude-ls: 1.2.1
|
||||
type-check: 0.4.0
|
||||
|
||||
lilconfig@3.1.3: {}
|
||||
|
||||
lint-staged@15.5.2:
|
||||
dependencies:
|
||||
chalk: 5.5.0
|
||||
commander: 13.1.0
|
||||
debug: 4.4.1
|
||||
execa: 8.0.1
|
||||
lilconfig: 3.1.3
|
||||
listr2: 8.3.3
|
||||
micromatch: 4.0.8
|
||||
pidtree: 0.6.0
|
||||
string-argv: 0.3.2
|
||||
yaml: 2.8.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
listr2@8.3.3:
|
||||
dependencies:
|
||||
cli-truncate: 4.0.0
|
||||
colorette: 2.0.20
|
||||
eventemitter3: 5.0.1
|
||||
log-update: 6.1.0
|
||||
rfdc: 1.4.1
|
||||
wrap-ansi: 9.0.0
|
||||
|
||||
locate-path@6.0.0:
|
||||
dependencies:
|
||||
p-locate: 5.0.0
|
||||
@@ -3066,6 +3263,14 @@ snapshots:
|
||||
chalk: 5.5.0
|
||||
is-unicode-supported: 1.3.0
|
||||
|
||||
log-update@6.1.0:
|
||||
dependencies:
|
||||
ansi-escapes: 7.0.0
|
||||
cli-cursor: 5.0.0
|
||||
slice-ansi: 7.1.0
|
||||
strip-ansi: 7.1.0
|
||||
wrap-ansi: 9.0.0
|
||||
|
||||
loupe@3.2.0: {}
|
||||
|
||||
lru-cache@10.4.3: {}
|
||||
@@ -3086,6 +3291,8 @@ snapshots:
|
||||
dependencies:
|
||||
semver: 7.7.2
|
||||
|
||||
merge-stream@2.0.0: {}
|
||||
|
||||
merge2@1.4.1: {}
|
||||
|
||||
micromatch@4.0.8:
|
||||
@@ -3093,6 +3300,8 @@ snapshots:
|
||||
braces: 3.0.3
|
||||
picomatch: 2.3.1
|
||||
|
||||
mimic-fn@4.0.0: {}
|
||||
|
||||
mimic-function@5.0.1: {}
|
||||
|
||||
minimatch@3.1.2:
|
||||
@@ -3139,6 +3348,10 @@ snapshots:
|
||||
|
||||
node-fetch-native@1.6.7: {}
|
||||
|
||||
npm-run-path@5.3.0:
|
||||
dependencies:
|
||||
path-key: 4.0.0
|
||||
|
||||
nypm@0.5.4:
|
||||
dependencies:
|
||||
citty: 0.1.6
|
||||
@@ -3150,6 +3363,10 @@ snapshots:
|
||||
|
||||
ohash@1.1.6: {}
|
||||
|
||||
onetime@6.0.0:
|
||||
dependencies:
|
||||
mimic-fn: 4.0.0
|
||||
|
||||
onetime@7.0.0:
|
||||
dependencies:
|
||||
mimic-function: 5.0.1
|
||||
@@ -3199,6 +3416,8 @@ snapshots:
|
||||
|
||||
path-key@3.1.1: {}
|
||||
|
||||
path-key@4.0.0: {}
|
||||
|
||||
path-parse@1.0.7: {}
|
||||
|
||||
path-scurry@1.11.1:
|
||||
@@ -3220,6 +3439,8 @@ snapshots:
|
||||
|
||||
picomatch@4.0.3: {}
|
||||
|
||||
pidtree@0.6.0: {}
|
||||
|
||||
pkg-types@1.3.1:
|
||||
dependencies:
|
||||
confbox: 0.1.8
|
||||
@@ -3272,6 +3493,8 @@ snapshots:
|
||||
|
||||
reusify@1.1.0: {}
|
||||
|
||||
rfdc@1.4.1: {}
|
||||
|
||||
rollup-plugin-dts@6.2.1(rollup@4.46.2)(typescript@5.9.2):
|
||||
dependencies:
|
||||
magic-string: 0.30.17
|
||||
@@ -3342,6 +3565,16 @@ snapshots:
|
||||
mrmime: 2.0.1
|
||||
totalist: 3.0.1
|
||||
|
||||
slice-ansi@5.0.0:
|
||||
dependencies:
|
||||
ansi-styles: 6.2.1
|
||||
is-fullwidth-code-point: 4.0.0
|
||||
|
||||
slice-ansi@7.1.0:
|
||||
dependencies:
|
||||
ansi-styles: 6.2.1
|
||||
is-fullwidth-code-point: 5.1.0
|
||||
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
source-map@0.6.1: {}
|
||||
@@ -3352,6 +3585,8 @@ snapshots:
|
||||
|
||||
stdin-discarder@0.2.2: {}
|
||||
|
||||
string-argv@0.3.2: {}
|
||||
|
||||
string-width@4.2.3:
|
||||
dependencies:
|
||||
emoji-regex: 8.0.0
|
||||
@@ -3378,6 +3613,8 @@ snapshots:
|
||||
dependencies:
|
||||
ansi-regex: 6.1.0
|
||||
|
||||
strip-final-newline@3.0.0: {}
|
||||
|
||||
strip-json-comments@3.1.1: {}
|
||||
|
||||
strtok3@10.3.4:
|
||||
@@ -3584,10 +3821,18 @@ snapshots:
|
||||
string-width: 5.1.2
|
||||
strip-ansi: 7.1.0
|
||||
|
||||
wrap-ansi@9.0.0:
|
||||
dependencies:
|
||||
ansi-styles: 6.2.1
|
||||
string-width: 7.2.0
|
||||
strip-ansi: 7.1.0
|
||||
|
||||
y18n@5.0.8: {}
|
||||
|
||||
yallist@4.0.0: {}
|
||||
|
||||
yaml@2.8.1: {}
|
||||
|
||||
yargs-parser@21.1.1: {}
|
||||
|
||||
yargs@17.7.2:
|
||||
|
||||
+1
-7
@@ -9,7 +9,6 @@ This repository contains the code for hand-written SDKs and clients for interact
|
||||
This includes:
|
||||
|
||||
- [LlamaParse](../parse.md) - A GenAI-native document parser that can parse complex document data for any downstream LLM use case (Agents, RAG, data processing, etc.).
|
||||
- [LlamaReport (beta/invite-only)](../report.md) - A prebuilt agentic report builder that can be used to build reports from a variety of data sources.
|
||||
- [LlamaExtract](../extract.md) - A prebuilt agentic data extractor that can be used to transform data into a structured JSON representation.
|
||||
- [LlamaCloud Index](../index.md) - A widely customizable and fully automated document ingestion pipeline that also serves retrieval purposes.
|
||||
|
||||
@@ -28,14 +27,12 @@ Then, you can use the services in your code:
|
||||
```python
|
||||
from llama_cloud_services import (
|
||||
LlamaParse,
|
||||
LlamaReport,
|
||||
LlamaExtract,
|
||||
LlamaCloudIndex,
|
||||
)
|
||||
from llama_cloud_services import LlamaParse, LlamaReport, LlamaExtract
|
||||
from llama_cloud_services import LlamaParse, LlamaExtract
|
||||
|
||||
parser = LlamaParse(api_key="YOUR_API_KEY")
|
||||
report = LlamaReport(api_key="YOUR_API_KEY")
|
||||
extract = LlamaExtract(api_key="YOUR_API_KEY")
|
||||
index = LlamaCloudIndex(
|
||||
"my_first_index", project_name="default", api_key="YOUR_API_KEY"
|
||||
@@ -45,7 +42,6 @@ index = LlamaCloudIndex(
|
||||
See the quickstart guides for each service for more information:
|
||||
|
||||
- [LlamaParse](../parse.md)
|
||||
- [LlamaReport (beta/invite-only)](../report.md)
|
||||
- [LlamaExtract](../extract.md)
|
||||
- [LlamaCloud Index](../index.md)
|
||||
|
||||
@@ -58,13 +54,11 @@ You can also create your API key in the EU region [here](https://cloud.eu.llamai
|
||||
```python
|
||||
from llama_cloud_services import (
|
||||
LlamaParse,
|
||||
LlamaReport,
|
||||
LlamaExtract,
|
||||
EU_BASE_URL,
|
||||
)
|
||||
|
||||
parser = LlamaParse(api_key="YOUR_API_KEY", base_url=EU_BASE_URL)
|
||||
report = LlamaReport(api_key="YOUR_API_KEY", base_url=EU_BASE_URL)
|
||||
extract = LlamaExtract(api_key="YOUR_API_KEY", base_url=EU_BASE_URL)
|
||||
index = LlamaCloudIndex(
|
||||
"my_first_index",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from llama_cloud_services.parse import LlamaParse
|
||||
from llama_cloud_services.report import ReportClient, LlamaReport
|
||||
from llama_cloud_services.extract import LlamaExtract, ExtractionAgent
|
||||
from llama_cloud_services.extract import LlamaExtract, ExtractionAgent, SourceText
|
||||
from llama_cloud_services.constants import EU_BASE_URL
|
||||
from llama_cloud_services.index import (
|
||||
LlamaCloudCompositeRetriever,
|
||||
@@ -10,10 +9,9 @@ from llama_cloud_services.index import (
|
||||
|
||||
__all__ = [
|
||||
"LlamaParse",
|
||||
"ReportClient",
|
||||
"LlamaReport",
|
||||
"LlamaExtract",
|
||||
"ExtractionAgent",
|
||||
"SourceText",
|
||||
"EU_BASE_URL",
|
||||
"LlamaCloudIndex",
|
||||
"LlamaCloudRetriever",
|
||||
|
||||
@@ -159,6 +159,9 @@ class Page(BaseModel):
|
||||
durationInSeconds: Optional[float] = Field(
|
||||
default=None, description="The duration of the audio transcript in seconds."
|
||||
)
|
||||
slideSpeakerNotes: Optional[str] = Field(
|
||||
default=None, description="The speaker notes for the slide."
|
||||
)
|
||||
|
||||
|
||||
class JobResult(BaseModel):
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
from llama_cloud_services.report.report import ReportClient
|
||||
from llama_cloud_services.report.base import LlamaReport
|
||||
|
||||
__all__ = ["ReportClient", "LlamaReport"]
|
||||
@@ -1,269 +0,0 @@
|
||||
import asyncio
|
||||
import httpx
|
||||
import os
|
||||
import io
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from typing import Optional, List, Union, Any, Coroutine, TypeVar
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from llama_cloud.types import ReportMetadata
|
||||
from llama_cloud_services.report.report import ReportClient
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class LlamaReport:
|
||||
"""Client for managing reports and general report operations."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
api_key: Optional[str] = None,
|
||||
project_id: Optional[str] = None,
|
||||
organization_id: Optional[str] = None,
|
||||
base_url: Optional[str] = None,
|
||||
timeout: Optional[int] = None,
|
||||
async_httpx_client: Optional[httpx.AsyncClient] = None,
|
||||
):
|
||||
self.api_key = api_key or os.getenv("LLAMA_CLOUD_API_KEY", None)
|
||||
if not self.api_key:
|
||||
raise ValueError("No API key provided.")
|
||||
|
||||
self.base_url = base_url or os.getenv(
|
||||
"LLAMA_CLOUD_BASE_URL", "https://api.cloud.llamaindex.ai"
|
||||
)
|
||||
self.timeout = timeout or 60
|
||||
|
||||
# Initialize HTTP clients
|
||||
self._aclient = async_httpx_client or httpx.AsyncClient(timeout=self.timeout)
|
||||
|
||||
# Set auth headers
|
||||
self.headers = {
|
||||
"Authorization": f"Bearer {self.api_key}",
|
||||
}
|
||||
|
||||
self.organization_id = organization_id
|
||||
self.project_id = project_id
|
||||
self._client_params = {
|
||||
"timeout": self._aclient.timeout,
|
||||
"headers": self._aclient.headers,
|
||||
"base_url": self._aclient.base_url,
|
||||
"auth": self._aclient.auth,
|
||||
"event_hooks": self._aclient.event_hooks,
|
||||
"cookies": self._aclient.cookies,
|
||||
"max_redirects": self._aclient.max_redirects,
|
||||
"params": self._aclient.params,
|
||||
"trust_env": self._aclient.trust_env,
|
||||
}
|
||||
self._thread_pool = ThreadPoolExecutor(
|
||||
max_workers=min(10, (os.cpu_count() or 1) + 4)
|
||||
)
|
||||
|
||||
@property
|
||||
def aclient(self) -> httpx.AsyncClient:
|
||||
if self._aclient is None:
|
||||
self._aclient = httpx.AsyncClient(**self._client_params)
|
||||
return self._aclient
|
||||
|
||||
def _run_sync(self, coro: Coroutine[Any, Any, T]) -> T:
|
||||
"""Run coroutine in a separate thread to avoid event loop issues"""
|
||||
|
||||
# force a new client for this thread/event loop
|
||||
original_client = self._aclient
|
||||
self._aclient = None
|
||||
|
||||
def run_coro() -> T:
|
||||
async def wrapped_coro() -> T:
|
||||
return await coro
|
||||
|
||||
return asyncio.run(wrapped_coro())
|
||||
|
||||
result = self._thread_pool.submit(run_coro).result()
|
||||
|
||||
# restore the original client
|
||||
self._aclient = original_client
|
||||
|
||||
return result
|
||||
|
||||
async def _get_default_project(self) -> str:
|
||||
response = await self.aclient.get(
|
||||
urljoin(str(self.base_url), "/api/v1/projects"), headers=self.headers
|
||||
)
|
||||
response.raise_for_status()
|
||||
projects = response.json()
|
||||
default_project = [p for p in projects if p.get("is_default")]
|
||||
return default_project[0]["id"]
|
||||
|
||||
async def _build_url(
|
||||
self, endpoint: str, extra_params: Optional[List[str]] = None
|
||||
) -> str:
|
||||
"""Helper method to build URLs with common query parameters."""
|
||||
url = urljoin(str(self.base_url), endpoint)
|
||||
|
||||
if not self.project_id:
|
||||
self.project_id = await self._get_default_project()
|
||||
|
||||
query_params = []
|
||||
if self.organization_id:
|
||||
query_params.append(f"organization_id={self.organization_id}")
|
||||
if self.project_id:
|
||||
query_params.append(f"project_id={self.project_id}")
|
||||
if extra_params:
|
||||
query_params.extend([p for p in extra_params if p is not None])
|
||||
|
||||
if query_params:
|
||||
url += "?" + "&".join(query_params)
|
||||
|
||||
return url
|
||||
|
||||
async def acreate_report(
|
||||
self,
|
||||
name: str,
|
||||
template_instructions: Optional[str] = None,
|
||||
template_text: Optional[str] = None,
|
||||
template_file: Optional[Union[str, tuple[str, bytes]]] = None,
|
||||
input_files: Optional[List[Union[str, tuple[str, bytes]]]] = None,
|
||||
existing_retriever_id: Optional[str] = None,
|
||||
) -> ReportClient:
|
||||
"""Create a new report asynchronously."""
|
||||
url = await self._build_url("/api/v1/reports/")
|
||||
open_files: List[io.BufferedReader] = []
|
||||
|
||||
data = {"name": name}
|
||||
if template_instructions:
|
||||
data["template_instructions"] = template_instructions
|
||||
if template_text:
|
||||
data["template_text"] = template_text
|
||||
if existing_retriever_id:
|
||||
data["existing_retriever_id"] = str(existing_retriever_id)
|
||||
|
||||
files: List[tuple[str, io.BufferedReader | bytes]] = []
|
||||
if template_file:
|
||||
if isinstance(template_file, str):
|
||||
open_files.append(open(template_file, "rb"))
|
||||
files.append(("template_file", open_files[-1]))
|
||||
else:
|
||||
files.append(("template_file", template_file[1]))
|
||||
|
||||
if input_files:
|
||||
for f in input_files:
|
||||
if isinstance(f, str):
|
||||
open_files.append(open(f, "rb"))
|
||||
files.append(("files", open_files[-1]))
|
||||
else:
|
||||
files.append(("files", f[1]))
|
||||
|
||||
response = await self.aclient.post(
|
||||
url, headers=self.headers, data=data, files=files
|
||||
)
|
||||
try:
|
||||
response.raise_for_status()
|
||||
report_id = response.json()["id"]
|
||||
return ReportClient(report_id, name, self)
|
||||
except httpx.HTTPStatusError as e:
|
||||
raise ValueError(
|
||||
f"Failed to create report: {e.response.text}\nError Code: {e.response.status_code}"
|
||||
)
|
||||
finally:
|
||||
for open_file in open_files:
|
||||
open_file.close()
|
||||
|
||||
def create_report(
|
||||
self,
|
||||
name: str,
|
||||
template_instructions: Optional[str] = None,
|
||||
template_text: Optional[str] = None,
|
||||
template_file: Optional[Union[str, tuple[str, bytes]]] = None,
|
||||
input_files: Optional[List[Union[str, tuple[str, bytes]]]] = None,
|
||||
existing_retriever_id: Optional[str] = None,
|
||||
) -> ReportClient:
|
||||
"""Create a new report."""
|
||||
return self._run_sync(
|
||||
self.acreate_report(
|
||||
name=name,
|
||||
template_instructions=template_instructions,
|
||||
template_text=template_text,
|
||||
template_file=template_file,
|
||||
input_files=input_files,
|
||||
existing_retriever_id=existing_retriever_id,
|
||||
)
|
||||
)
|
||||
|
||||
async def alist_reports(
|
||||
self, state: Optional[str] = None, limit: int = 100, offset: int = 0
|
||||
) -> List[ReportClient]:
|
||||
"""List all reports asynchronously."""
|
||||
params = []
|
||||
if state:
|
||||
params.append(f"state={state}")
|
||||
if limit:
|
||||
params.append(f"limit={limit}")
|
||||
if offset:
|
||||
params.append(f"offset={offset}")
|
||||
|
||||
url = await self._build_url(
|
||||
"/api/v1/reports/list",
|
||||
extra_params=params,
|
||||
)
|
||||
|
||||
response = await self.aclient.get(url, headers=self.headers)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
return [
|
||||
ReportClient(r["report_id"], r["name"], self)
|
||||
for r in data["report_responses"]
|
||||
]
|
||||
|
||||
def list_reports(
|
||||
self, state: Optional[str] = None, limit: int = 100, offset: int = 0
|
||||
) -> List[ReportClient]:
|
||||
"""Synchronous wrapper for listing reports."""
|
||||
return self._run_sync(self.alist_reports(state, limit, offset))
|
||||
|
||||
async def aget_report(self, report_id: str) -> ReportClient:
|
||||
"""Get a Report instance for working with a specific report."""
|
||||
url = await self._build_url(f"/api/v1/reports/{report_id}")
|
||||
|
||||
response = await self.aclient.get(url, headers=self.headers)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
return ReportClient(data["report_id"], data["name"], self)
|
||||
|
||||
def get_report(self, report_id: str) -> ReportClient:
|
||||
"""Synchronous wrapper for getting a report."""
|
||||
return self._run_sync(self.aget_report(report_id))
|
||||
|
||||
async def aget_report_metadata(self, report_id: str) -> ReportMetadata:
|
||||
"""Get metadata for a specific report asynchronously.
|
||||
|
||||
Returns:
|
||||
dict containing:
|
||||
- id: Report ID
|
||||
- name: Report name
|
||||
- state: Current report state
|
||||
- report_metadata: Additional metadata
|
||||
- template_file: Name of template file if used
|
||||
- template_instructions: Template instructions if provided
|
||||
- input_files: List of input file names
|
||||
"""
|
||||
url = await self._build_url(f"/api/v1/reports/{report_id}/metadata")
|
||||
|
||||
response = await self.aclient.get(url, headers=self.headers)
|
||||
response.raise_for_status()
|
||||
return ReportMetadata(**response.json())
|
||||
|
||||
def get_report_metadata(self, report_id: str) -> ReportMetadata:
|
||||
"""Synchronous wrapper for getting report metadata."""
|
||||
return self._run_sync(self.aget_report_metadata(report_id))
|
||||
|
||||
async def adelete_report(self, report_id: str) -> None:
|
||||
"""Delete a specific report asynchronously."""
|
||||
url = await self._build_url(f"/api/v1/reports/{report_id}")
|
||||
|
||||
response = await self.aclient.delete(url, headers=self.headers)
|
||||
response.raise_for_status()
|
||||
|
||||
def delete_report(self, report_id: str) -> None:
|
||||
"""Synchronous wrapper for deleting a report."""
|
||||
return self._run_sync(self.adelete_report(report_id))
|
||||
@@ -1,527 +0,0 @@
|
||||
import asyncio
|
||||
import httpx
|
||||
import time
|
||||
from typing import Optional, List, Literal, Union, TYPE_CHECKING
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
|
||||
from llama_cloud.types import (
|
||||
ReportEventItemEventData_Progress,
|
||||
ReportMetadata,
|
||||
EditSuggestion,
|
||||
ReportResponse,
|
||||
ReportPlan,
|
||||
ReportBlock,
|
||||
ReportPlanBlock,
|
||||
Report,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from llama_cloud_services.report.base import LlamaReport
|
||||
|
||||
|
||||
class MessageRole(str, Enum):
|
||||
USER = "user"
|
||||
ASSISTANT = "assistant"
|
||||
|
||||
|
||||
@dataclass
|
||||
class Message:
|
||||
role: MessageRole
|
||||
content: str
|
||||
timestamp: datetime
|
||||
|
||||
|
||||
@dataclass
|
||||
class EditAction:
|
||||
block_idx: int
|
||||
old_content: str
|
||||
new_content: Optional[str]
|
||||
action: Literal["approved", "rejected"]
|
||||
timestamp: datetime
|
||||
|
||||
|
||||
DEFAULT_POLL_INTERVAL = 5
|
||||
DEFAULT_TIMEOUT = 600
|
||||
|
||||
|
||||
class ReportClient:
|
||||
"""Client for operations on a specific report."""
|
||||
|
||||
def __init__(self, report_id: str, name: str, parent_client: "LlamaReport"):
|
||||
self.report_id = report_id
|
||||
self.name = name
|
||||
self._client = parent_client
|
||||
self._headers = parent_client.headers
|
||||
self._run_sync = parent_client._run_sync
|
||||
self._build_url = parent_client._build_url
|
||||
self.chat_history: List[Message] = []
|
||||
self.edit_history: List[EditAction] = []
|
||||
|
||||
@property
|
||||
def aclient(self) -> httpx.AsyncClient:
|
||||
return self._client.aclient
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Report(id={self.report_id}, name={self.name})"
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Report(id={self.report_id}, name={self.name})"
|
||||
|
||||
def _get_block_content(self, block: Union[ReportBlock, ReportPlanBlock]) -> str:
|
||||
if isinstance(block, ReportBlock):
|
||||
return block.template
|
||||
elif isinstance(block, ReportPlanBlock):
|
||||
return block.block.template
|
||||
else:
|
||||
raise ValueError(f"Invalid block type: {type(block)}")
|
||||
|
||||
def _get_block_idx(self, block: Union[ReportBlock, ReportPlanBlock]) -> int:
|
||||
if isinstance(block, ReportBlock):
|
||||
return block.idx
|
||||
elif isinstance(block, ReportPlanBlock):
|
||||
return block.block.idx
|
||||
else:
|
||||
raise ValueError(f"Invalid block type: {type(block)}")
|
||||
|
||||
async def aget(self, version: Optional[int] = None) -> ReportResponse:
|
||||
"""Get this report's details asynchronously."""
|
||||
extra_params = []
|
||||
if version is not None:
|
||||
extra_params.append(f"version={version}")
|
||||
|
||||
url = await self._build_url(f"/api/v1/reports/{self.report_id}", extra_params)
|
||||
|
||||
response = await self.aclient.get(url, headers=self._headers)
|
||||
response.raise_for_status()
|
||||
return ReportResponse(**response.json())
|
||||
|
||||
def get(self, version: Optional[int] = None) -> ReportResponse:
|
||||
"""Synchronous wrapper for getting this report's details."""
|
||||
return self._run_sync(self.aget(version))
|
||||
|
||||
async def aupdate_report(self, updated_report: Report) -> ReportResponse:
|
||||
"""Update this report's content asynchronously."""
|
||||
url = await self._build_url(f"/api/v1/reports/{self.report_id}")
|
||||
response = await self.aclient.patch(
|
||||
url, headers=self._headers, json={"content": updated_report.dict()}
|
||||
)
|
||||
response.raise_for_status()
|
||||
return ReportResponse(**response.json())
|
||||
|
||||
def update_report(self, updated_report: Report) -> ReportResponse:
|
||||
"""Synchronous wrapper for updating this report's content."""
|
||||
return self._run_sync(self.aupdate_report(updated_report))
|
||||
|
||||
async def aupdate_plan(
|
||||
self,
|
||||
action: Literal["approve", "reject", "edit"],
|
||||
updated_plan: Optional[ReportPlan] = None,
|
||||
) -> ReportResponse:
|
||||
"""Update this report's plan asynchronously."""
|
||||
if action == "edit" and not updated_plan:
|
||||
raise ValueError("updated_plan is required when action is 'edit'")
|
||||
|
||||
url = await self._build_url(
|
||||
f"/api/v1/reports/{self.report_id}/plan", [f"action={action}"]
|
||||
)
|
||||
|
||||
data = None
|
||||
if updated_plan is not None:
|
||||
plan_dict = updated_plan.dict()
|
||||
plan_dict.pop("generated_at", None)
|
||||
data = plan_dict
|
||||
|
||||
if updated_plan is None and action == "edit":
|
||||
raise ValueError("updated_plan is required when action is 'edit'")
|
||||
|
||||
response = await self.aclient.patch(url, headers=self._headers, json=data)
|
||||
response.raise_for_status()
|
||||
return ReportResponse(**response.json())
|
||||
|
||||
def update_plan(
|
||||
self,
|
||||
action: Literal["approve", "reject", "edit"],
|
||||
updated_plan: Optional[ReportPlan] = None,
|
||||
) -> ReportResponse:
|
||||
"""Synchronous wrapper for updating this report's plan."""
|
||||
return self._run_sync(self.aupdate_plan(action, updated_plan))
|
||||
|
||||
async def asuggest_edits(
|
||||
self,
|
||||
user_query: str,
|
||||
auto_history: bool = True,
|
||||
chat_history: Optional[List[dict]] = None,
|
||||
) -> List[EditSuggestion]:
|
||||
"""Get AI suggestions for edits to this report asynchronously.
|
||||
|
||||
Args:
|
||||
user_query: The user's request/question about what to edit
|
||||
auto_history: Whether to automatically add the user's message to the chat history
|
||||
chat_history:
|
||||
A list of chat messages to include in the chat history.
|
||||
The format being a list of dictionaries with "role" and "content" keys.
|
||||
"""
|
||||
# Add user message to history
|
||||
self.chat_history.append(
|
||||
Message(role=MessageRole.USER, content=user_query, timestamp=datetime.now())
|
||||
)
|
||||
|
||||
# Format chat history with edit summaries
|
||||
chat_history_dicts = []
|
||||
for msg in self.chat_history[:-1]: # Exclude current message
|
||||
content = msg.content
|
||||
if msg.role == MessageRole.USER:
|
||||
# Add edit summary for user messages
|
||||
edit_summary = self._get_edit_summary_after_message(msg.timestamp)
|
||||
if edit_summary:
|
||||
content = f"{content}\n\nActions taken:\n{edit_summary}"
|
||||
|
||||
chat_history_dicts.append({"role": msg.role.value, "content": content})
|
||||
|
||||
# decide whether to include chat history or not
|
||||
if chat_history:
|
||||
chat_history_dicts = chat_history
|
||||
elif auto_history:
|
||||
chat_history_dicts = chat_history_dicts
|
||||
else:
|
||||
chat_history_dicts = []
|
||||
|
||||
# Make the API call
|
||||
url = await self._build_url(f"/api/v1/reports/{self.report_id}/suggest_edits")
|
||||
data = {"user_query": user_query, "chat_history": chat_history_dicts}
|
||||
|
||||
response = await self.aclient.post(url, headers=self._headers, json=data)
|
||||
response.raise_for_status()
|
||||
suggestions = response.json()
|
||||
suggestions = [EditSuggestion(**suggestion) for suggestion in suggestions]
|
||||
|
||||
# Add assistant response to history
|
||||
if suggestions:
|
||||
for suggestion in suggestions:
|
||||
self.chat_history.append(
|
||||
Message(
|
||||
role=MessageRole.ASSISTANT,
|
||||
content=suggestion.justification,
|
||||
timestamp=datetime.now(),
|
||||
)
|
||||
)
|
||||
|
||||
return suggestions
|
||||
|
||||
def suggest_edits(
|
||||
self,
|
||||
user_query: str,
|
||||
auto_history: bool = True,
|
||||
chat_history: Optional[List[dict]] = None,
|
||||
) -> List[EditSuggestion]:
|
||||
"""Synchronous wrapper for getting edit suggestions."""
|
||||
return self._run_sync(
|
||||
self.asuggest_edits(user_query, auto_history, chat_history)
|
||||
)
|
||||
|
||||
async def await_completion(
|
||||
self, timeout: int = DEFAULT_TIMEOUT, poll_interval: int = DEFAULT_POLL_INTERVAL
|
||||
) -> Report:
|
||||
"""Wait for this report to complete processing."""
|
||||
start_time = time.time()
|
||||
while True:
|
||||
report_response = await self.aget()
|
||||
status = report_response.status
|
||||
|
||||
if status == "completed":
|
||||
return report_response.report
|
||||
elif status == "error":
|
||||
events = await self.aget_events()
|
||||
raise ValueError(f"Report entered error state: {events[-1].msg}")
|
||||
elif time.time() - start_time > timeout:
|
||||
raise TimeoutError(f"Report did not complete within {timeout} seconds")
|
||||
|
||||
await asyncio.sleep(poll_interval)
|
||||
|
||||
def wait_for_completion(
|
||||
self, timeout: int = DEFAULT_TIMEOUT, poll_interval: int = DEFAULT_POLL_INTERVAL
|
||||
) -> Report:
|
||||
"""Synchronous wrapper for awaiting report completion."""
|
||||
return self._run_sync(self.await_completion(timeout, poll_interval))
|
||||
|
||||
async def await_for_plan(
|
||||
self, timeout: int = DEFAULT_TIMEOUT, poll_interval: int = DEFAULT_POLL_INTERVAL
|
||||
) -> ReportPlan:
|
||||
"""Wait for this report's plan to be ready for review."""
|
||||
start_time = time.time()
|
||||
while True:
|
||||
report_metadata = await self.aget_metadata()
|
||||
state = report_metadata.state
|
||||
|
||||
if state == "waiting_approval":
|
||||
report_response = await self.aget()
|
||||
return report_response.plan
|
||||
elif state == "error":
|
||||
events = await self.aget_events()
|
||||
raise ValueError(f"Report entered error state: {events[-1].msg}")
|
||||
elif time.time() - start_time > timeout:
|
||||
raise TimeoutError(f"Plan was not ready within {timeout} seconds")
|
||||
|
||||
await asyncio.sleep(poll_interval)
|
||||
|
||||
def wait_for_plan(
|
||||
self, timeout: int = DEFAULT_TIMEOUT, poll_interval: int = DEFAULT_POLL_INTERVAL
|
||||
) -> ReportPlan:
|
||||
"""Synchronous wrapper for awaiting plan readiness."""
|
||||
return self._run_sync(self.await_for_plan(timeout, poll_interval))
|
||||
|
||||
async def aget_metadata(self) -> ReportMetadata:
|
||||
"""Get this report's metadata asynchronously."""
|
||||
return await self._client.aget_report_metadata(self.report_id)
|
||||
|
||||
def get_metadata(self) -> ReportMetadata:
|
||||
"""Synchronous wrapper for getting this report's metadata."""
|
||||
return self._run_sync(self.aget_metadata())
|
||||
|
||||
async def adelete(self) -> None:
|
||||
"""Delete this report asynchronously."""
|
||||
return await self._client.adelete_report(self.report_id)
|
||||
|
||||
def delete(self) -> None:
|
||||
"""Synchronous wrapper for deleting this report."""
|
||||
return self._run_sync(self.adelete())
|
||||
|
||||
async def aaccept_edit(self, suggestion: EditSuggestion) -> None:
|
||||
"""Accept a suggested edit.
|
||||
|
||||
Args:
|
||||
suggestion: The EditSuggestion to accept, typically from suggest_edits()
|
||||
"""
|
||||
if len(suggestion.blocks) == 0:
|
||||
return
|
||||
|
||||
# Determine if we're editing a plan or report based on first block type
|
||||
is_plan_edit = isinstance(suggestion.blocks[0], ReportPlanBlock)
|
||||
|
||||
# Get current content
|
||||
report_response = await self.aget()
|
||||
current_blocks = (
|
||||
report_response.plan.blocks
|
||||
if is_plan_edit
|
||||
else report_response.report.blocks
|
||||
)
|
||||
|
||||
# Track the edit
|
||||
new_blocks = []
|
||||
for edit_block in suggestion.blocks:
|
||||
# Find matching block in current content
|
||||
old_block = next(
|
||||
(
|
||||
b
|
||||
for b in current_blocks
|
||||
if self._get_block_idx(b) == self._get_block_idx(edit_block)
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
old_content = (
|
||||
self._get_block_content(old_block) if old_block else "[No old content]"
|
||||
)
|
||||
new_content = self._get_block_content(edit_block)
|
||||
|
||||
if is_plan_edit:
|
||||
new_queries_str = "\n".join(
|
||||
[
|
||||
f"Field: {q.field}, Prompt: {q.prompt}, Context: {q.context}"
|
||||
for q in edit_block.queries
|
||||
]
|
||||
)
|
||||
new_dependency_str = (
|
||||
f"Depends on: {edit_block.dependency}"
|
||||
if edit_block.dependency
|
||||
else ""
|
||||
)
|
||||
new_content += f"\n\n{new_queries_str}\n{new_dependency_str}"
|
||||
|
||||
if old_block:
|
||||
old_queries_str = "\n".join(
|
||||
[
|
||||
f"Field: {q.field}, Prompt: {q.prompt}, Context: {q.context}"
|
||||
for q in old_block.queries
|
||||
]
|
||||
)
|
||||
old_dependency_str = (
|
||||
f"Depends on: {old_block.dependency}"
|
||||
if old_block.dependency
|
||||
else ""
|
||||
)
|
||||
old_content += f"\n\n{old_queries_str}\n{old_dependency_str}"
|
||||
|
||||
self.edit_history.append(
|
||||
EditAction(
|
||||
block_idx=self._get_block_idx(edit_block),
|
||||
old_content=old_content,
|
||||
new_content=new_content,
|
||||
action="approved",
|
||||
timestamp=datetime.now(),
|
||||
)
|
||||
)
|
||||
|
||||
# Create updated block
|
||||
if is_plan_edit:
|
||||
new_blocks.append(
|
||||
ReportPlanBlock(
|
||||
block=ReportBlock(
|
||||
idx=edit_block.block.idx,
|
||||
template=self._get_block_content(edit_block),
|
||||
sources=edit_block.block.sources,
|
||||
),
|
||||
queries=edit_block.queries,
|
||||
dependency=edit_block.dependency,
|
||||
)
|
||||
)
|
||||
else:
|
||||
new_blocks.append(
|
||||
ReportBlock(
|
||||
idx=edit_block.idx,
|
||||
template=self._get_block_content(edit_block),
|
||||
sources=edit_block.sources,
|
||||
)
|
||||
)
|
||||
|
||||
if new_blocks:
|
||||
if is_plan_edit:
|
||||
# Update plan in place
|
||||
plan = report_response.plan
|
||||
|
||||
# Replace edited blocks and add new ones
|
||||
for new_block in new_blocks:
|
||||
block_idx = self._get_block_idx(new_block)
|
||||
existing_block_idx = next(
|
||||
(
|
||||
i
|
||||
for i, b in enumerate(plan.blocks)
|
||||
if b.block.idx == block_idx
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
if existing_block_idx is not None:
|
||||
# Replace existing block
|
||||
plan.blocks[existing_block_idx] = new_block
|
||||
else:
|
||||
# Add new block to end
|
||||
plan.blocks.append(new_block)
|
||||
|
||||
await self.aupdate_plan("edit", plan)
|
||||
else:
|
||||
# Update report in place
|
||||
report = report_response.report
|
||||
|
||||
# Replace edited blocks and add new ones
|
||||
for new_block in new_blocks:
|
||||
block_idx = self._get_block_idx(new_block)
|
||||
existing_block_idx = next(
|
||||
(i for i, b in enumerate(report.blocks) if b.idx == block_idx),
|
||||
None,
|
||||
)
|
||||
|
||||
if existing_block_idx is not None:
|
||||
# Replace existing block
|
||||
report.blocks[existing_block_idx] = new_block
|
||||
else:
|
||||
# Add new block to end
|
||||
report.blocks.append(new_block)
|
||||
|
||||
await self.aupdate_report(report)
|
||||
|
||||
def accept_edit(self, suggestion: EditSuggestion) -> None:
|
||||
"""Synchronous wrapper for accepting an edit."""
|
||||
return self._run_sync(self.aaccept_edit(suggestion))
|
||||
|
||||
async def areject_edit(self, suggestion: EditSuggestion) -> None:
|
||||
"""Reject a suggested edit.
|
||||
|
||||
Args:
|
||||
suggestion: The EditSuggestion to reject, typically from suggest_edits()
|
||||
"""
|
||||
# Track the rejections
|
||||
for edit_block in suggestion.blocks:
|
||||
self.edit_history.append(
|
||||
EditAction(
|
||||
block_idx=self._get_block_idx(edit_block),
|
||||
old_content=self._get_block_content(edit_block),
|
||||
new_content=None,
|
||||
action="rejected",
|
||||
timestamp=datetime.now(),
|
||||
)
|
||||
)
|
||||
|
||||
def reject_edit(self, suggestion: EditSuggestion) -> None:
|
||||
"""Synchronous wrapper for rejecting an edit."""
|
||||
return self._run_sync(self.areject_edit(suggestion))
|
||||
|
||||
def _get_edit_summary_after_message(
|
||||
self, message_timestamp: datetime
|
||||
) -> Optional[str]:
|
||||
"""Get a summary of edits that occurred after a specific message."""
|
||||
relevant_edits = [
|
||||
edit for edit in self.edit_history if edit.timestamp > message_timestamp
|
||||
]
|
||||
|
||||
if not relevant_edits:
|
||||
return None
|
||||
|
||||
approved = [edit for edit in relevant_edits if edit.action == "approved"]
|
||||
rejected = [edit for edit in relevant_edits if edit.action == "rejected"]
|
||||
|
||||
summary = []
|
||||
|
||||
if approved:
|
||||
summary.append("Approved edits:")
|
||||
for edit in approved:
|
||||
summary.append(
|
||||
f'Block {edit.block_idx}: "{edit.old_content}" -> "{edit.new_content}"'
|
||||
)
|
||||
|
||||
if rejected:
|
||||
if approved: # Add spacing if we had approved edits
|
||||
summary.append("")
|
||||
summary.append("Rejected edits:")
|
||||
for edit in rejected:
|
||||
summary.append(f'Block {edit.block_idx}: "{edit.old_content}"')
|
||||
|
||||
return "\n".join(summary)
|
||||
|
||||
async def aget_events(
|
||||
self, last_sequence: Optional[int] = None
|
||||
) -> List[ReportEventItemEventData_Progress]:
|
||||
"""Get all events for this report asynchronously.
|
||||
|
||||
Args:
|
||||
last_sequence: If provided, only get events after this sequence number
|
||||
|
||||
Returns:
|
||||
List of ReportEvent objects
|
||||
"""
|
||||
extra_params = []
|
||||
if last_sequence is not None:
|
||||
extra_params.append(f"last_sequence={last_sequence}")
|
||||
|
||||
url = await self._build_url(
|
||||
f"/api/v1/reports/{self.report_id}/events", extra_params
|
||||
)
|
||||
|
||||
response = await self.aclient.get(url, headers=self._headers)
|
||||
response.raise_for_status()
|
||||
progress_events = []
|
||||
for event in response.json():
|
||||
if event["event_type"] == "progress":
|
||||
progress_events.append(
|
||||
ReportEventItemEventData_Progress(**event["event_data"])
|
||||
)
|
||||
|
||||
return progress_events
|
||||
|
||||
def get_events(
|
||||
self, last_sequence: Optional[int] = None
|
||||
) -> List[ReportEventItemEventData_Progress]:
|
||||
"""Synchronous wrapper for getting report events."""
|
||||
return self._run_sync(self.aget_events(last_sequence))
|
||||
@@ -11,13 +11,13 @@ dev = [
|
||||
|
||||
[project]
|
||||
name = "llama-parse"
|
||||
version = "0.6.62"
|
||||
version = "0.6.63"
|
||||
description = "Parse files into RAG-Optimized formats."
|
||||
authors = [{name = "Logan Markewich", email = "logan@llamaindex.ai"}]
|
||||
requires-python = ">=3.9,<4.0"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
dependencies = ["llama-cloud-services>=0.6.62"]
|
||||
dependencies = ["llama-cloud-services>=0.6.63"]
|
||||
|
||||
[project.scripts]
|
||||
llama-parse = "llama_parse.cli.main:parse"
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ dev = [
|
||||
|
||||
[project]
|
||||
name = "llama-cloud-services"
|
||||
version = "0.6.62"
|
||||
version = "0.6.63"
|
||||
description = "Tailored SDK clients for LlamaCloud services."
|
||||
authors = [{name = "Logan Markewich", email = "logan@runllama.ai"}]
|
||||
requires-python = ">=3.9,<4.0"
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
import os
|
||||
import pytest
|
||||
import uuid
|
||||
from typing import AsyncGenerator
|
||||
from pytest_asyncio import fixture as async_fixture
|
||||
from llama_cloud_services.report import LlamaReport, ReportClient
|
||||
|
||||
# Skip tests if no API key is set
|
||||
pytestmark = pytest.mark.skipif(
|
||||
not os.getenv("LLAMA_CLOUD_API_KEY") or os.getenv("CI") == "true",
|
||||
reason="No API key provided",
|
||||
)
|
||||
|
||||
|
||||
@async_fixture(scope="function")
|
||||
async def client() -> AsyncGenerator[LlamaReport, None]:
|
||||
"""Create a LlamaReport client."""
|
||||
client = LlamaReport()
|
||||
reports_before = await client.alist_reports()
|
||||
reports_before_ids = [r.report_id for r in reports_before]
|
||||
try:
|
||||
yield client
|
||||
finally:
|
||||
# clean up reports
|
||||
try:
|
||||
reports_after = await client.alist_reports()
|
||||
reports_after_ids = [r.report_id for r in reports_after]
|
||||
for report_id in reports_before_ids:
|
||||
if report_id not in reports_after_ids:
|
||||
await client.adelete_report(report_id)
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
await client.aclient.aclose()
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def unique_name() -> str:
|
||||
"""Generate a unique report name."""
|
||||
return f"test-report-{uuid.uuid4()}"
|
||||
|
||||
|
||||
@async_fixture(scope="function")
|
||||
async def report(
|
||||
client: LlamaReport, unique_name: str
|
||||
) -> AsyncGenerator[ReportClient, None]:
|
||||
"""Create a report."""
|
||||
report = await client.acreate_report(
|
||||
name=unique_name,
|
||||
template_text=(
|
||||
"# [Some title]\n\n"
|
||||
" ## TLDR\n"
|
||||
"A quick summary of the paper.\n\n"
|
||||
"## Details\n"
|
||||
"More details about the paper, possible more than one section here.\n"
|
||||
),
|
||||
input_files=["tests/test_files/paper.md"],
|
||||
)
|
||||
try:
|
||||
yield report
|
||||
finally:
|
||||
await report.adelete()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.xfail(
|
||||
condition=lambda: os.getenv("CI"),
|
||||
reason="Backend db issues; needs to be fixed.",
|
||||
)
|
||||
async def test_create_and_delete_report(
|
||||
client: LlamaReport, report: ReportClient
|
||||
) -> None:
|
||||
"""Test basic report creation and deletion."""
|
||||
# Verify the report exists
|
||||
metadata = await report.aget_metadata()
|
||||
assert metadata.name == report.name
|
||||
|
||||
# Test listing reports
|
||||
reports = await client.alist_reports()
|
||||
assert any(r.report_id == report.report_id for r in reports)
|
||||
|
||||
# Test getting report by ID
|
||||
fetched_report = await client.aget_report(report.report_id)
|
||||
assert fetched_report.report_id == report.report_id
|
||||
assert fetched_report.name == report.name
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.xfail(
|
||||
condition=lambda: os.getenv("CI"),
|
||||
reason="Report plan sometimes times out",
|
||||
raises=TimeoutError,
|
||||
)
|
||||
async def test_report_plan_workflow(report: ReportClient) -> None:
|
||||
"""Test the report planning workflow."""
|
||||
# Wait for the plan
|
||||
plan = await report.await_for_plan()
|
||||
assert plan is not None
|
||||
|
||||
# Approve the plan
|
||||
response = await report.aupdate_plan(action="approve")
|
||||
assert response is not None
|
||||
|
||||
# Wait for completion
|
||||
completed_report = await report.await_completion()
|
||||
assert len(completed_report.blocks) > 0
|
||||
|
||||
# Get edit suggestions
|
||||
suggestions = await report.asuggest_edits(
|
||||
"TLDR section header more formal.", auto_history=True
|
||||
)
|
||||
assert len(suggestions) > 0
|
||||
|
||||
# Test accepting an edit
|
||||
await report.aaccept_edit(suggestions[0])
|
||||
|
||||
# Get more suggestions and test rejecting
|
||||
more_suggestions = await report.asuggest_edits(
|
||||
"Add a section about machine learning.", auto_history=True
|
||||
)
|
||||
assert len(more_suggestions) > 0
|
||||
await report.areject_edit(more_suggestions[0])
|
||||
|
||||
# Verify chat history is maintained
|
||||
assert len(report.chat_history) >= 4 # 2 user messages + 2 assistant responses
|
||||
|
||||
# get events
|
||||
events = await report.aget_events()
|
||||
assert len(events) > 0
|
||||
Generated
+2256
-2256
File diff suppressed because it is too large
Load Diff
@@ -1,101 +0,0 @@
|
||||
# LlamaReport (beta/invite-only)
|
||||
|
||||
LlamaReport is a prebuilt agentic report builder that can be used to build reports from a variety of data sources.
|
||||
|
||||
The python SDK for interacting with the LlamaReport API. The SDK provides two main classes:
|
||||
|
||||
- `LlamaReport`: For managing reports (create, list, delete)
|
||||
- `ReportClient`: For working with a specific report (editing, approving, etc.)
|
||||
|
||||
## Quickstart
|
||||
|
||||
```bash
|
||||
pip install llama-cloud-services
|
||||
```
|
||||
|
||||
```python
|
||||
from llama_cloud_services import LlamaReport
|
||||
|
||||
# Initialize the client
|
||||
client = LlamaReport(
|
||||
api_key="your-api-key",
|
||||
# Optional: Specify project_id, organization_id, async_httpx_client
|
||||
)
|
||||
|
||||
# Create a new report
|
||||
report = client.create_report(
|
||||
"My Report",
|
||||
# must have one of template_text or template_instructions
|
||||
template_text="Your template text",
|
||||
template_instructions="Instructions for the template",
|
||||
# must have one of input_files or retriever_id
|
||||
input_files=["data1.pdf", "data2.pdf"],
|
||||
retriever_id="retriever-id",
|
||||
)
|
||||
```
|
||||
|
||||
## Working with Reports
|
||||
|
||||
The typical workflow for a report involves:
|
||||
|
||||
1. Creating the report
|
||||
2. Waiting for and approving the plan
|
||||
3. Waiting for report generation
|
||||
4. Making edits to the report
|
||||
|
||||
Here's a complete example:
|
||||
|
||||
```python
|
||||
# Create a report
|
||||
report = client.create_report(
|
||||
"Quarterly Analysis", input_files=["q1_data.pdf", "q2_data.pdf"]
|
||||
)
|
||||
|
||||
# Wait for the plan to be ready
|
||||
plan = report.wait_for_plan()
|
||||
|
||||
# Option 1: Directly approve the plan
|
||||
report.update_plan(action="approve")
|
||||
|
||||
# Option 2: Suggest and review edits to the plan
|
||||
suggestions = report.suggest_edits(
|
||||
"Can you add a section about market trends?"
|
||||
)
|
||||
for suggestion in suggestions:
|
||||
print(suggestion)
|
||||
|
||||
# Accept or reject the suggestion
|
||||
if input("Accept? (y/n): ").lower() == "y":
|
||||
report.accept_edit(suggestion)
|
||||
else:
|
||||
report.reject_edit(suggestion)
|
||||
|
||||
# Wait for the report to complete
|
||||
report = report.wait_for_completion()
|
||||
|
||||
# Make edits to the final report
|
||||
suggestions = report.suggest_edits("Make the executive summary more concise")
|
||||
|
||||
# Review and accept/reject suggestions as above
|
||||
...
|
||||
```
|
||||
|
||||
### Getting the Final Report
|
||||
|
||||
Once you are satisfied with the report, you can get the final report object and use the content as you see fit.
|
||||
|
||||
Here's an example of printing out the final report:
|
||||
|
||||
```python
|
||||
report = report.get()
|
||||
report_text = "\n\n".join([block.template for block in report.blocks])
|
||||
|
||||
print(report_text)
|
||||
```
|
||||
|
||||
## Additional Features
|
||||
|
||||
- **Async Support**: All methods have async counterparts: `create_report` -> `acreate_report`, `wait_for_plan` -> `await_for_plan`, etc.
|
||||
- **Automatic Chat History**: The SDK automatically keeps track of chat history for each suggestion, unless you specify `auto_history=False` in `suggest_edits`.
|
||||
- **Custom HTTP Client**: You can provide your own `httpx.AsyncClient` to the `LlamaReport` class.
|
||||
- **Project and Organization IDs**: You can specify `project_id` and `organization_id` to use a specific project or organization.
|
||||
+61
-14
@@ -8,10 +8,11 @@ import subprocess
|
||||
import sys
|
||||
import tomlkit
|
||||
from pathlib import Path
|
||||
import json
|
||||
|
||||
|
||||
def get_current_versions() -> tuple[str, str, str]:
|
||||
"""Get current versions from both pyproject.toml files."""
|
||||
def get_current_versions() -> tuple[str, str, str, str | None]:
|
||||
"""Get current versions from both pyproject.toml files and TS package.json."""
|
||||
# Read main pyproject.toml
|
||||
main_content = Path("py/pyproject.toml").read_text()
|
||||
main_doc = tomlkit.parse(main_content)
|
||||
@@ -34,11 +35,21 @@ def get_current_versions() -> tuple[str, str, str]:
|
||||
)
|
||||
break
|
||||
|
||||
return str(main_version), str(llama_parse_version), str(dependency_version)
|
||||
# Read TypeScript package.json version via helper
|
||||
ts_version: str = get_ts_version()
|
||||
|
||||
return (
|
||||
str(main_version),
|
||||
str(llama_parse_version),
|
||||
str(dependency_version),
|
||||
str(ts_version) if ts_version is not None else None,
|
||||
)
|
||||
|
||||
|
||||
def validate_versions(
|
||||
main_version: str, llama_parse_version: str, dependency_version: str
|
||||
main_version: str,
|
||||
llama_parse_version: str,
|
||||
dependency_version: str,
|
||||
) -> list[str]:
|
||||
"""Validate that versions are consistent and return warnings."""
|
||||
warnings = []
|
||||
@@ -60,7 +71,7 @@ def validate_versions(
|
||||
|
||||
|
||||
def set_version(version: str) -> None:
|
||||
"""Set version across all pyproject.toml files using tomlkit to preserve formatting."""
|
||||
"""Set version across Python projects (no TS change)."""
|
||||
# Update main pyproject.toml
|
||||
main_content = Path("py/pyproject.toml").read_text()
|
||||
main_doc = tomlkit.parse(main_content)
|
||||
@@ -79,7 +90,26 @@ def set_version(version: str) -> None:
|
||||
break
|
||||
Path("py/llama_parse/pyproject.toml").write_text(tomlkit.dumps(llama_parse_doc))
|
||||
|
||||
click.echo(f"Updated all versions to {version}")
|
||||
click.echo(f"Updated Python versions to {version}")
|
||||
|
||||
|
||||
def get_ts_version() -> str:
|
||||
"""Read TypeScript package.json version (if present)."""
|
||||
ts_package_path = Path("ts/llama_cloud_services/package.json")
|
||||
package_data = json.loads(ts_package_path.read_text())
|
||||
data = package_data.get("version")
|
||||
if data is None:
|
||||
raise RuntimeError("TypeScript package.json version not found")
|
||||
return data
|
||||
|
||||
|
||||
def set_ts_version(version: str) -> None:
|
||||
"""Set TypeScript package.json version only."""
|
||||
ts_package_path = Path("ts/llama_cloud_services/package.json")
|
||||
package_data = json.loads(ts_package_path.read_text())
|
||||
package_data["version"] = version
|
||||
ts_package_path.write_text(json.dumps(package_data, indent=2) + "\n")
|
||||
click.echo(f"Updated TypeScript package.json version to {version}")
|
||||
|
||||
|
||||
def get_current_branch() -> str:
|
||||
@@ -99,7 +129,7 @@ def create_if_not_exists(version: str) -> None:
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
tag_name = f"v{version}"
|
||||
tag_name = f"v{version}" if version[0].isdigit() else version
|
||||
if not tag_exists(version):
|
||||
# Create tag
|
||||
subprocess.run(["git", "tag", tag_name], check=True)
|
||||
@@ -133,12 +163,18 @@ def cli() -> None:
|
||||
@cli.command()
|
||||
def get() -> None:
|
||||
"""Get current versions and show validation warnings."""
|
||||
main_version, llama_parse_version, dependency_version = get_current_versions()
|
||||
(
|
||||
main_version,
|
||||
llama_parse_version,
|
||||
dependency_version,
|
||||
ts_version,
|
||||
) = get_current_versions()
|
||||
|
||||
click.echo("Current versions:")
|
||||
click.echo(f" llama-cloud-services: {main_version}")
|
||||
click.echo(f" llama-parse: {llama_parse_version}")
|
||||
click.echo(f" dependency reference: {dependency_version}")
|
||||
click.echo(f" typescript package: {ts_version}")
|
||||
|
||||
warnings = validate_versions(main_version, llama_parse_version, dependency_version)
|
||||
if warnings:
|
||||
@@ -151,9 +187,15 @@ def get() -> None:
|
||||
|
||||
@cli.command()
|
||||
@click.argument("version")
|
||||
def set(version: str) -> None:
|
||||
"""Set version across all pyproject.toml files."""
|
||||
set_version(version)
|
||||
@click.option("--js", is_flag=True, help="Update TypeScript package.json only")
|
||||
def set(version: str, js: bool) -> None:
|
||||
"""Set version for Python, TypeScript, or both (default: Python only)."""
|
||||
|
||||
if js:
|
||||
set_ts_version(version)
|
||||
return
|
||||
else:
|
||||
set_version(version)
|
||||
|
||||
|
||||
@cli.command()
|
||||
@@ -165,11 +207,16 @@ def set(version: str) -> None:
|
||||
is_flag=True,
|
||||
help="Push the tag to the remote repository",
|
||||
)
|
||||
def tag(version: str | None = None, push: bool = False) -> None:
|
||||
@click.option(
|
||||
"--js",
|
||||
is_flag=True,
|
||||
help="tag TypeScript package.json only",
|
||||
)
|
||||
def tag(version: str | None = None, push: bool = False, js: bool = False) -> None:
|
||||
"""Create and push a git tag for the current version."""
|
||||
if not version:
|
||||
main_version, _, _ = get_current_versions()
|
||||
version = main_version
|
||||
main_version, _, _, js_version = get_current_versions()
|
||||
version = f"llama-cloud-services@{js_version}" if js else main_version
|
||||
|
||||
create_if_not_exists(version)
|
||||
if push:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "llama-cloud-services",
|
||||
"version": "0.3.3",
|
||||
"version": "0.3.4",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
@@ -9,6 +9,7 @@
|
||||
"dev": "bunchee --watch",
|
||||
"lint": "eslint src/ --ignore-pattern client/*.ts --no-warn-ignored",
|
||||
"format": "prettier --write ./src/",
|
||||
"format:check": "prettier --check ./src/",
|
||||
"test": "vitest run --testTimeout=60000",
|
||||
"test:watch": "vitest --watch",
|
||||
"test:ui": "vitest --ui",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createClient, createConfig } from "@hey-api/client-fetch";
|
||||
import { getEnv } from "@llamaindex/env";
|
||||
import { createClient } from "@hey-api/client-fetch";
|
||||
import { client as defaultClient } from "../../api";
|
||||
import {
|
||||
aggregateAgentDataApiV1BetaAgentDataAggregatePost,
|
||||
createAgentDataApiV1BetaAgentDataPost,
|
||||
@@ -24,36 +24,19 @@ import type {
|
||||
*/
|
||||
export class AgentClient<T = unknown> {
|
||||
private client: ReturnType<typeof createClient>;
|
||||
private baseUrl: string;
|
||||
private headers: Record<string, string>;
|
||||
private collection: string;
|
||||
private agentUrlId: string;
|
||||
|
||||
constructor({
|
||||
apiKey = getEnv("LLAMA_CLOUD_API_KEY"),
|
||||
baseUrl = "https://api.cloud.llamaindex.ai/",
|
||||
client = defaultClient,
|
||||
collection = "default",
|
||||
agentUrlId = "_public",
|
||||
}: {
|
||||
apiKey?: string;
|
||||
baseUrl?: string;
|
||||
client?: ReturnType<typeof createClient>;
|
||||
collection?: string;
|
||||
agentUrlId?: string;
|
||||
}) {
|
||||
this.baseUrl = baseUrl;
|
||||
|
||||
this.headers = {
|
||||
"X-SDK-Name": "llamaindex-ts",
|
||||
...(apiKey && { Authorization: `Bearer ${apiKey}` }),
|
||||
};
|
||||
|
||||
this.client = createClient(
|
||||
createConfig({
|
||||
baseUrl: this.baseUrl,
|
||||
headers: this.headers,
|
||||
}),
|
||||
);
|
||||
|
||||
this.client = client;
|
||||
this.collection = collection;
|
||||
this.agentUrlId = agentUrlId;
|
||||
}
|
||||
@@ -281,15 +264,13 @@ export interface AgentDataClientOptions {
|
||||
* @returns A new AgentClient instance
|
||||
*/
|
||||
export function createAgentDataClient<T = unknown>({
|
||||
apiKey,
|
||||
baseUrl,
|
||||
client = defaultClient,
|
||||
windowUrl,
|
||||
env,
|
||||
agentUrlId,
|
||||
collection = "default",
|
||||
}: {
|
||||
apiKey?: string;
|
||||
baseUrl?: string;
|
||||
client?: ReturnType<typeof createClient>;
|
||||
windowUrl?: string;
|
||||
env?: Record<string, string>;
|
||||
agentUrlId?: string;
|
||||
@@ -321,9 +302,8 @@ export function createAgentDataClient<T = unknown>({
|
||||
}
|
||||
|
||||
return new AgentClient({
|
||||
...(apiKey && { apiKey }),
|
||||
...(baseUrl && { baseUrl }),
|
||||
...(agentUrlId && { agentUrlId }),
|
||||
collection,
|
||||
client,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ export class LlamaParseReader extends FileReader {
|
||||
? this.language
|
||||
: [this.language];
|
||||
this.stdout =
|
||||
params.stdout ?? typeof process !== "undefined"
|
||||
(params.stdout ?? typeof process !== "undefined")
|
||||
? process!.stdout
|
||||
: undefined;
|
||||
const apiKey = params.apiKey ?? getEnv("LLAMA_CLOUD_API_KEY");
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
||||
import { listProjectsApiV1ProjectsGet, client } from "../src/api.js";
|
||||
|
||||
describe("Global client configuration", () => {
|
||||
const originalFetch = globalThis.fetch;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
globalThis.fetch = originalFetch;
|
||||
});
|
||||
|
||||
it("adds X-SDK-Name header from global client config", async () => {
|
||||
const fetchSpy = vi
|
||||
.spyOn(globalThis, "fetch")
|
||||
.mockImplementation(async (input, init) => {
|
||||
// Validate the header is present on the outgoing request
|
||||
let headers: Headers;
|
||||
if (input && typeof input === "object" && "headers" in (input as any)) {
|
||||
headers = (input as Request).headers;
|
||||
} else {
|
||||
headers = new Headers((init && init.headers) || {});
|
||||
}
|
||||
expect(headers.get("X-SDK-Name")).toBe("llamaindex-ts");
|
||||
|
||||
return new Response(JSON.stringify([]), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
});
|
||||
|
||||
// Trigger any request via the generated SDK (imported through src/api.ts)
|
||||
await listProjectsApiV1ProjectsGet({ throwOnError: false });
|
||||
|
||||
expect(fetchSpy).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("respects additional custom headers set via setConfig", async () => {
|
||||
const prevConfig = client.getConfig();
|
||||
try {
|
||||
client.setConfig({
|
||||
...prevConfig,
|
||||
headers: {
|
||||
...(prevConfig.headers || {}),
|
||||
"X-Custom-Header": "custom-value",
|
||||
},
|
||||
});
|
||||
|
||||
const fetchSpy = vi
|
||||
.spyOn(globalThis, "fetch")
|
||||
.mockImplementation(async (input, init) => {
|
||||
let headers: Headers;
|
||||
if (
|
||||
input &&
|
||||
typeof input === "object" &&
|
||||
"headers" in (input as any)
|
||||
) {
|
||||
headers = (input as Request).headers;
|
||||
} else {
|
||||
headers = new Headers((init && init.headers) || {});
|
||||
}
|
||||
expect(headers.get("X-SDK-Name")).toBe("llamaindex-ts");
|
||||
expect(headers.get("X-Custom-Header")).toBe("custom-value");
|
||||
|
||||
return new Response(JSON.stringify([]), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
});
|
||||
|
||||
await listProjectsApiV1ProjectsGet({ throwOnError: false });
|
||||
expect(fetchSpy).toHaveBeenCalledOnce();
|
||||
} finally {
|
||||
// Restore original configuration to avoid test cross-talk
|
||||
client.setConfig(prevConfig);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user