commit 3a7c47b2a6c4ebeb3b68e53bf55fd1604d0ea109 Author: wehub-resource-sync Date: Mon Jul 13 13:38:00 2026 +0800 chore: import upstream snapshot with attribution diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..0cb653f --- /dev/null +++ b/.coveragerc @@ -0,0 +1,8 @@ +[run] +source = src/python +concurrency = multiprocessing,thread +disable_warnings = no-data-collected +omit = **/__main__.py + +[combine] +disable_warnings = no-data-collected diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..a3d86ff --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,71 @@ +# GitHub Actions build workflow +name: build + +on: ["push", "pull_request"] + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + + timeout-minutes: 60 + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Install Python + uses: actions/setup-python@v6 + with: + python-version: "3.10" + + - name: Install Java + uses: actions/setup-java@v5 + with: + distribution: "zulu" + java-version: 21 + + - name: Install dependencies - Linux + run: | + sudo apt-get update + sudo apt-get install libportaudio2 libsndfile1 libegl1 libgles2 + sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc + if: matrix.os == 'ubuntu-latest' + + - name: Install dependencies - macOS + run: | + echo "PYTORCH_MPS_DISABLE=1" >> $GITHUB_ENV + echo "LLAMA_NO_METAL=1" >> $GITHUB_ENV + echo "TIKA_STARTUP_SLEEP=30" >> $GITHUB_ENV + echo "TIKA_STARTUP_MAX_RETRY=10" >> $GITHUB_ENV + brew install portaudio + if: matrix.os == 'macos-latest' + + - name: Install dependencies - Windows + run: | + "PYTHONIOENCODING=utf-8" >> $env:GITHUB_ENV + choco install wget + if: matrix.os == 'windows-latest' + + - name: Build + run: | + pip install -U wheel + pip install .[all,dev] + pip cache purge + python -c "import nltk; nltk.download(['punkt', 'punkt_tab', 'averaged_perceptron_tagger_eng'])" + python --version + make data coverage + env: + HF_HUB_ETAG_TIMEOUT: 100 + HF_HUB_DOWNLOAD_TIMEOUT: 100 + HF_XET_CHUNK_CACHE_SIZE_BYTES: 0 + + - uses: pre-commit/action@v3.0.1 + if: matrix.os == 'ubuntu-latest' + + - name: Test Coverage + run: coveralls --service=github + if: matrix.os == 'ubuntu-latest' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..02bdf1e --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,25 @@ +name: docs +on: + push: + branches: + - master +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Install Python + uses: actions/setup-python@v6 + with: + python-version: "3.10" + + - name: Install txtai + run: | + pip install -U pip wheel + pip install .[all,dev] + + - name: Deploy documentation + run: mkdocs gh-deploy --force + \ No newline at end of file diff --git a/.github/workflows/minimal.yml b/.github/workflows/minimal.yml new file mode 100644 index 0000000..444ebff --- /dev/null +++ b/.github/workflows/minimal.yml @@ -0,0 +1,22 @@ +# GitHub Actions minimal build +name: minimal + +on: ["push", "pull_request"] + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Install Python + uses: actions/setup-python@v6 + with: + python-version: "3.10" + + - name: Minimal install + run: | + pip install -U pip wheel + MINIMAL=1 pip install . + python -c "import txtai" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..461bc74 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +build/ +dist/ +docker/**/*.yml +htmlcov/ +*egg-info/ +__pycache__/ +.coverage +.coverage.* +*.pyc +.vscode/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..79f065d --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,14 @@ +repos: + - repo: https://github.com/pycqa/pylint + rev: v3.3.1 + hooks: + - id: pylint + args: + - -d import-error + - -d duplicate-code + - -d too-many-positional-arguments + - repo: https://github.com/ambv/black + rev: 24.10.0 + hooks: + - id: black + language_version: python3 diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..3bb9f57 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,17 @@ +[BASIC] +module-rgx=[a-z_][a-zA-Z0-9_]{2,30}$ +method-rgx=[a-z_][a-zA-Z0-9_]{2,30}$ +function-rgx=[a-z_][a-zA-Z0-9_]{2,30}$ +argument-rgx=[a-z_][a-zA-Z0-9_]{0,30}$ +variable-rgx=[a-z_][a-zA-Z0-9_]{0,30}$ +attr-rgx=[a-z_][a-zA-Z0-9_]{0,30}$ + +[DESIGN] +max-args=10 +max-locals=40 +max-returns=10 +max-attributes=20 +min-public-methods=0 + +[FORMAT] +max-line-length=150 diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..a0a732e --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,11 @@ +cff-version: 1.2.0 +date-released: 2020-08-11 +message: "If you use this software, please cite it as below." +title: "txtai: the all-in-one AI framework" +abstract: "txtai is an all-in-one open-source AI framework for semantic search, LLM orchestration and language model workflows" +url: "https://github.com/neuml/txtai" +authors: +- family-names: "Mezzetti" + given-names: "David" + affiliation: NeuML +license: Apache-2.0 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ca0a1be --- /dev/null +++ b/LICENSE @@ -0,0 +1,190 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2020- NeuML LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ada65da --- /dev/null +++ b/Makefile @@ -0,0 +1,58 @@ +# Project utility scripts +.PHONY: test + +# Setup environment +export SRC_DIR := ./src/python +export TEST_DIR := ./test/python +export PYTHONPATH := ${SRC_DIR}:${TEST_DIR}:${PYTHONPATH} +export PATH := ${TEST_DIR}:${PATH} +export PYTHONWARNINGS := ignore + +# Disable tokenizer parallelism for tests +export TOKENIZERS_PARALLELISM := false + +# Default python executable if not provided +PYTHON ?= python + +# Check for wget +WGET := $(shell wget --version 2> /dev/null) +ifndef WGET + $(error "Required binary `wget` not found, please install wget OS package") +endif + +# Download test data +data: + mkdir -p /tmp/txtai + wget -N https://github.com/neuml/txtai/releases/download/v6.2.0/tests.tar.gz -P /tmp + tar -xvzf /tmp/tests.tar.gz -C /tmp + +# Unit tests +test: + ${PYTHON} -m unittest discover -v -s ${TEST_DIR} + +# Run tests while calculating code coverage +coverage: + coverage run -m unittest discover -v -k testagent -s ${TEST_DIR} + coverage run -m unittest discover -v -k testann -s ${TEST_DIR} + coverage run -m unittest discover -v -k testapi -s ${TEST_DIR} + coverage run -m unittest discover -v -k testapp -s ${TEST_DIR} + coverage run -m unittest discover -v -k testarchive -s ${TEST_DIR} + coverage run -m unittest discover -v -k testcloud -s ${TEST_DIR} + coverage run -m unittest discover -v -k testconsole -s ${TEST_DIR} + coverage run -m unittest discover -v -k testdatabase -s ${TEST_DIR} + coverage run -m unittest discover -v -k testembeddings -s ${TEST_DIR} + coverage run -m unittest discover -v -k testgraph -s ${TEST_DIR} + coverage run -m unittest discover -v -k testlibrary -s ${TEST_DIR} + coverage run -m unittest discover -v -k testmodels -s ${TEST_DIR} + coverage run -m unittest discover -v -k testoptional -s ${TEST_DIR} + coverage run -m unittest discover -v -k testpipeline.testaudio -s ${TEST_DIR} + coverage run -m unittest discover -v -k testpipeline.testdata -s ${TEST_DIR} + coverage run -m unittest discover -v -k testpipeline.testimage -s ${TEST_DIR} + coverage run -m unittest discover -v -k testpipeline.testllm -s ${TEST_DIR} + coverage run -m unittest discover -v -k testpipeline.testtext -s ${TEST_DIR} + coverage run -m unittest discover -v -k testpipeline.testtrain -s ${TEST_DIR} + coverage run -m unittest discover -v -k testscoring -s ${TEST_DIR} + coverage run -m unittest discover -v -k testserialize -s ${TEST_DIR} + coverage run -m unittest discover -v -k testvectors -s ${TEST_DIR} + coverage run -m unittest discover -v -k testworkflow -s ${TEST_DIR} + coverage combine diff --git a/README.md b/README.md new file mode 100644 index 0000000..5597932 --- /dev/null +++ b/README.md @@ -0,0 +1,258 @@ +

+ +

+ +

+ All-in-one AI framework +

+ +

+ + Version + + + GitHub last commit + + + GitHub issues + + + Join Slack + + + Build Status + + + Coverage Status + +

+ +txtai is an all-in-one AI framework for semantic search, LLM orchestration and language model workflows. + +![architecture](https://raw.githubusercontent.com/neuml/txtai/master/docs/images/architecture.png#gh-light-mode-only) +![architecture](https://raw.githubusercontent.com/neuml/txtai/master/docs/images/architecture-dark.png#gh-dark-mode-only) + +The key component of txtai is an embeddings database, which is a union of vector indexes (sparse and dense), graph networks and relational databases. + +This foundation enables vector search and/or serves as a powerful knowledge source for large language model (LLM) applications. + +Build autonomous agents, retrieval augmented generation (RAG) processes, multi-model workflows and more. + +Summary of txtai features: + +- 🔎 Vector search with SQL, object storage, topic modeling, graph analysis and multimodal indexing +- 📄 Create embeddings for text, documents, audio, images and video +- 💡 Pipelines powered by language models that run LLM prompts, question-answering, labeling, transcription, translation, summarization and more +- ↪️️ Workflows to join pipelines together and aggregate business logic. txtai processes can be simple microservices or multi-model workflows. +- 🤖 Agents that intelligently connect embeddings, pipelines, workflows and other agents together to autonomously solve complex problems +- ⚙️ Web and Model Context Protocol (MCP) APIs. Bindings available for [JavaScript](https://github.com/neuml/txtai.js), [Java](https://github.com/neuml/txtai.java), [Rust](https://github.com/neuml/txtai.rs) and [Go](https://github.com/neuml/txtai.go). +- 🔋 Batteries included with defaults to get up and running fast +- ☁️ Run local or scale out with container orchestration + +txtai is built with Python 3.10+, [Hugging Face Transformers](https://github.com/huggingface/transformers), [Sentence Transformers](https://github.com/UKPLab/sentence-transformers) and [FastAPI](https://github.com/tiangolo/fastapi). txtai is open-source under an Apache 2.0 license. + +> [!NOTE] +> +> [NeuML](https://neuml.com) is the company behind txtai and we provide AI consulting services around our stack. [Schedule a meeting](https://cal.com/neuml/intro) or [send a message](mailto:info@neuml.com) to learn more. +> +> We're also building an easy and secure way to run hosted txtai applications with [txtai.cloud](https://txtai.cloud). + +## Why txtai? + +![why](https://raw.githubusercontent.com/neuml/txtai/master/docs/images/why.png#gh-light-mode-only) +![why](https://raw.githubusercontent.com/neuml/txtai/master/docs/images/why-dark.png#gh-dark-mode-only) + +New vector databases, LLM frameworks and everything in between are sprouting up daily. Why build with txtai? + +- Up and running in minutes with [pip](https://neuml.github.io/txtai/install/) or [Docker](https://neuml.github.io/txtai/cloud/) +```python +# Get started in a couple lines +import txtai + +embeddings = txtai.Embeddings() +embeddings.index(["Correct", "Not what we hoped"]) +embeddings.search("positive", 1) +#[(0, 0.29862046241760254)] +``` +- Built-in API makes it easy to develop applications using your programming language of choice +```yaml +# app.yml +embeddings: + path: sentence-transformers/all-MiniLM-L6-v2 +``` +```bash +CONFIG=app.yml uvicorn "txtai.api:app" +curl -X GET "http://localhost:8000/search?query=positive" +``` +- Run local - no need to ship data off to disparate remote services +- Work with micromodels all the way up to large language models (LLMs) +- Low footprint - install additional dependencies and scale up when needed +- [Learn by example](https://neuml.github.io/txtai/examples) - notebooks cover all available functionality + +## Use Cases + +The following sections introduce common txtai use cases. A comprehensive set of over 70 [example notebooks and applications](https://neuml.github.io/txtai/examples) are also available. + +### Semantic Search + +Build semantic/similarity/vector/neural search applications. + +![demo](https://raw.githubusercontent.com/neuml/txtai/master/demo.gif) + +Traditional search systems use keywords to find data. Semantic search has an understanding of natural language and identifies results that have the same meaning, not necessarily the same keywords. + +![search](https://raw.githubusercontent.com/neuml/txtai/master/docs/images/search.png#gh-light-mode-only) +![search](https://raw.githubusercontent.com/neuml/txtai/master/docs/images/search-dark.png#gh-dark-mode-only) + +Get started with the following examples. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Introducing txtai](https://github.com/neuml/txtai/blob/master/examples/01_Introducing_txtai.ipynb) [▶️](https://www.youtube.com/watch?v=SIezMnVdmMs) | Overview of the functionality provided by txtai | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/01_Introducing_txtai.ipynb) | +| [Similarity search with images](https://github.com/neuml/txtai/blob/master/examples/13_Similarity_search_with_images.ipynb) | Embed images and text into the same space for search | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/13_Similarity_search_with_images.ipynb) | +| [Build a QA database](https://github.com/neuml/txtai/blob/master/examples/34_Build_a_QA_database.ipynb) | Question matching with semantic search | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/34_Build_a_QA_database.ipynb) | +| [Semantic Graphs](https://github.com/neuml/txtai/blob/master/examples/38_Introducing_the_Semantic_Graph.ipynb) | Explore topics, data connectivity and run network analysis| [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/38_Introducing_the_Semantic_Graph.ipynb) | + +### LLM Orchestration + +Autonomous agents, retrieval augmented generation (RAG), chat with your data, pipelines and workflows that interface with large language models (LLMs). + +![llm](https://raw.githubusercontent.com/neuml/txtai/master/docs/images/llm.png) + +See below to learn more. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Prompt templates and task chains](https://github.com/neuml/txtai/blob/master/examples/44_Prompt_templates_and_task_chains.ipynb) | Build model prompts and connect tasks together with workflows | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/44_Prompt_templates_and_task_chains.ipynb) | +| [Integrate LLM frameworks](https://github.com/neuml/txtai/blob/master/examples/53_Integrate_LLM_Frameworks.ipynb) | Integrate llama.cpp, LiteLLM and custom generation frameworks | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/53_Integrate_LLM_Frameworks.ipynb) | +| [Build knowledge graphs with LLMs](https://github.com/neuml/txtai/blob/master/examples/57_Build_knowledge_graphs_with_LLM_driven_entity_extraction.ipynb) | Build knowledge graphs with LLM-driven entity extraction | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/57_Build_knowledge_graphs_with_LLM_driven_entity_extraction.ipynb) | +| [Parsing the stars with txtai](https://github.com/neuml/txtai/blob/master/examples/72_Parsing_the_stars_with_txtai.ipynb) | Explore an astronomical knowledge graph of known stars, planets, galaxies | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/72_Parsing_the_stars_with_txtai.ipynb) | + +#### Agents + +Agents connect embeddings, pipelines, workflows and other agents together to autonomously solve complex problems. + +![agent](https://raw.githubusercontent.com/neuml/txtai/master/docs/images/agent.png) + +txtai agents are built on top of the [smolagents](https://github.com/huggingface/smolagents) framework. This supports all LLMs txtai supports (Hugging Face, llama.cpp, OpenAI / Claude / AWS Bedrock via LiteLLM). Agent prompting with [`agents.md`](https://github.com/agentsmd/agents.md) and [`skill.md`](https://agentskills.io/specification) are also supported. + +Check out this [Agent Quickstart Example](https://github.com/neuml/txtai/blob/master/examples/agent_quickstart.py). Additional examples are listed below. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Granting autonomy to agents](https://github.com/neuml/txtai/blob/master/examples/69_Granting_autonomy_to_agents.ipynb) | Agents that iteratively solve problems as they see fit | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/69_Granting_autonomy_to_agents.ipynb) | +| [TxtAI got skills](https://github.com/neuml/txtai/blob/master/examples/83_TxtAI_got_skills.ipynb) | Integrate skill.md files with your agent | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/83_TxtAI_got_skills.ipynb) | +| [Agent Tools](https://github.com/neuml/txtai/blob/master/examples/84_Agent_Tools.ipynb) [▶️](https://www.youtube.com/watch?v=RDNaFXQy3GQ) | Learn about the txtai agent toolkit | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/84_Agent_Tools.ipynb) | +| [Analyzing LinkedIn Company Posts with Graphs and Agents](https://github.com/neuml/txtai/blob/master/examples/71_Analyzing_LinkedIn_Company_Posts_with_Graphs_and_Agents.ipynb) | Exploring how to improve social media engagement with AI | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/71_Analyzing_LinkedIn_Company_Posts_with_Graphs_and_Agents.ipynb) | + +#### Retrieval augmented generation + +Retrieval augmented generation (RAG) reduces the risk of LLM hallucinations by constraining the output with a knowledge base as context. RAG is commonly used to "chat with your data". + +![rag](https://raw.githubusercontent.com/neuml/txtai/master/docs/images/rag.png#gh-light-mode-only) +![rag](https://raw.githubusercontent.com/neuml/txtai/master/docs/images/rag-dark.png#gh-dark-mode-only) + +Check out this [RAG Quickstart Example](https://github.com/neuml/txtai/blob/master/examples/rag_quickstart.py). Additional examples are listed below. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Build RAG pipelines with txtai](https://github.com/neuml/txtai/blob/master/examples/52_Build_RAG_pipelines_with_txtai.ipynb) [▶️](https://www.youtube.com/watch?v=t_OeAc8NVfQ) | Guide on retrieval augmented generation including how to create citations | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/52_Build_RAG_pipelines_with_txtai.ipynb) | +| [RAG is more than Vector Search](https://github.com/neuml/txtai/blob/master/examples/79_RAG_is_more_than_Vector_Search.ipynb) | Context retrieval via Web, SQL and other sources | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/79_RAG_is_more_than_Vector_Search.ipynb) | +| [GraphRAG with Wikipedia and GPT OSS](https://github.com/neuml/txtai/blob/master/examples/77_GraphRAG_with_Wikipedia_and_GPT_OSS.ipynb) | Deep graph search powered RAG | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/77_GraphRAG_with_Wikipedia_and_GPT_OSS.ipynb) | +| [Speech to Speech RAG](https://github.com/neuml/txtai/blob/master/examples/65_Speech_to_Speech_RAG.ipynb) [▶️](https://www.youtube.com/watch?v=tH8QWwkVMKA) | Full cycle speech to speech workflow with RAG | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/65_Speech_to_Speech_RAG.ipynb) | + +### Language Model Workflows + +Language model workflows, also known as semantic workflows, connect language models together to build intelligent applications. + +![flows](https://raw.githubusercontent.com/neuml/txtai/master/docs/images/flows.png#gh-light-mode-only) +![flows](https://raw.githubusercontent.com/neuml/txtai/master/docs/images/flows-dark.png#gh-dark-mode-only) + +While LLMs are powerful, there are plenty of smaller, more specialized models that work better and faster for specific tasks. This includes models for extractive question-answering, automatic summarization, text-to-speech, transcription and translation. + +Check out this [Workflow Quickstart Example](https://github.com/neuml/txtai/blob/master/examples/workflow_quickstart.py). Additional examples are listed below. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Run pipeline workflows](https://github.com/neuml/txtai/blob/master/examples/14_Run_pipeline_workflows.ipynb) [▶️](https://www.youtube.com/watch?v=UBMPDCn1gEU) | Simple yet powerful constructs to efficiently process data | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/14_Run_pipeline_workflows.ipynb) | +| [Building abstractive text summaries](https://github.com/neuml/txtai/blob/master/examples/09_Building_abstractive_text_summaries.ipynb) | Run abstractive text summarization | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/09_Building_abstractive_text_summaries.ipynb) | +| [Transcribe audio to text](https://github.com/neuml/txtai/blob/master/examples/11_Transcribe_audio_to_text.ipynb) | Convert audio files to text | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/11_Transcribe_audio_to_text.ipynb) | +| [Translate text between languages](https://github.com/neuml/txtai/blob/master/examples/12_Translate_text_between_languages.ipynb) | Streamline machine translation and language detection | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/12_Translate_text_between_languages.ipynb) | + +## Installation + +![install](https://raw.githubusercontent.com/neuml/txtai/master/docs/images/install.png#gh-light-mode-only) +![install](https://raw.githubusercontent.com/neuml/txtai/master/docs/images/install-dark.png#gh-dark-mode-only) + +The easiest way to install is via pip and PyPI + +``` +pip install txtai +``` + +Python 3.10+ is supported. Using a Python [virtual environment](https://docs.python.org/3/library/venv.html) is recommended. + +See the detailed [install instructions](https://neuml.github.io/txtai/install) for more information covering [optional dependencies](https://neuml.github.io/txtai/install/#optional-dependencies), [environment specific prerequisites](https://neuml.github.io/txtai/install/#environment-specific-prerequisites), [installing from source](https://neuml.github.io/txtai/install/#install-from-source), [conda support](https://neuml.github.io/txtai/install/#conda), [lightweight minimal installation](https://neuml.github.io/txtai/install/#minimal-install) and how to [run with containers](https://neuml.github.io/txtai/cloud). + +## Model guide + +![models](https://raw.githubusercontent.com/neuml/txtai/master/docs/images/models.png) + +See the table below for the current recommended models. These models all allow commercial use and offer a blend of speed and performance. + +| Component | Model(s) | +| ----------------------------------------------------------------------------- | ------------------------------------------------------------------------ | +| [Embeddings](https://neuml.github.io/txtai/embeddings) | [all-MiniLM-L6-v2](https://hf.co/sentence-transformers/all-MiniLM-L6-v2) | +| [Image Captions](https://neuml.github.io/txtai/pipeline/image/caption) | [BLIP](https://hf.co/Salesforce/blip-image-captioning-base) | +| [Labels - Zero Shot](https://neuml.github.io/txtai/pipeline/text/labels) | [DeBERTa v3 Zeroshot](https://hf.co/MoritzLaurer/deberta-v3-base-zeroshot-v2.0-c) | +| [Labels - Fixed](https://neuml.github.io/txtai/pipeline/text/labels) | Fine-tune with [training pipeline](https://neuml.github.io/txtai/pipeline/train/trainer) | +| [Large Language Model (LLM)](https://neuml.github.io/txtai/pipeline/text/llm) | [Gemma 4 31B](https://hf.co/google/gemma-4-31B) | +| [Summarization](https://neuml.github.io/txtai/pipeline/text/summary) | [DistilBART](https://hf.co/sshleifer/distilbart-cnn-12-6) | +| [Text-to-Speech](https://neuml.github.io/txtai/pipeline/audio/texttospeech) | [ESPnet JETS](https://hf.co/NeuML/ljspeech-jets-onnx) | +| [Transcription](https://neuml.github.io/txtai/pipeline/audio/transcription) | [Whisper](https://hf.co/openai/whisper-base) | +| [Translation](https://neuml.github.io/txtai/pipeline/text/translation) | [OPUS Model Series](https://hf.co/Helsinki-NLP) | + +Models can be loaded as either a path from the Hugging Face Hub or a local directory. Model paths are optional, defaults are loaded when not specified. For tasks with no recommended model, txtai uses the default models as shown in the Hugging Face Tasks guide. + +See the following links to learn more. + +- [Hugging Face Tasks](https://hf.co/tasks) +- [Hugging Face Model Hub](https://hf.co/models) +- [Embeddings Leaderboard](https://hf.co/spaces/mteb/leaderboard) +- [LLM Leaderboard](https://hf.co/spaces/lmarena-ai/arena-leaderboard) + +## Powered by txtai + +The following applications are powered by txtai. + +![apps](https://raw.githubusercontent.com/neuml/txtai/master/apps.jpg) + +| Application | Description | +|:------------ |:-------------| +| [rag](https://github.com/neuml/rag) | Retrieval Augmented Generation (RAG) application | +| [ncoder](https://github.com/neuml/ncoder) | Open-Source AI coding agent | +| [paperai](https://github.com/neuml/paperai) | AI for medical and scientific papers | +| [annotateai](https://github.com/neuml/annotateai) | Automatically annotate papers with LLMs | + +In addition to this list, there are also many other [open-source projects](https://github.com/neuml/txtai/network/dependents), [published research](https://scholar.google.com/scholar?q=txtai&hl=en&as_ylo=2022) and closed proprietary/commercial projects that have built on txtai in production. + +## Further Reading + +![further](https://raw.githubusercontent.com/neuml/txtai/master/docs/images/further.png#gh-light-mode-only) +![further](https://raw.githubusercontent.com/neuml/txtai/master/docs/images/further-ghdark.png#gh-dark-mode-only) + +- [Introducing txtai, the all-in-one AI framework](https://medium.com/neuml/introducing-txtai-the-all-in-one-ai-framework-0660ecfc39d7) +- [txtai: An All-in-One AI Framework for Semantic Search and LLM Workflows](https://github.com/neuml/papers/blob/master/txtai/txtai.pdf) +- [Tutorial series on Hashnode](https://neuml.hashnode.dev/series/txtai-tutorial) | [dev.to](https://dev.to/neuml/tutorial-series-on-txtai-ibg) +- [What's new in txtai 9.0](https://medium.com/neuml/whats-new-in-txtai-9-0-d522bb150afa) | [8.0](https://medium.com/neuml/whats-new-in-txtai-8-0-2d7d0ab4506b) | [7.0](https://medium.com/neuml/whats-new-in-txtai-7-0-855ad6a55440) | [6.0](https://medium.com/neuml/whats-new-in-txtai-6-0-7d93eeedf804) | [5.0](https://medium.com/neuml/whats-new-in-txtai-5-0-e5c75a13b101) | [4.0](https://medium.com/neuml/whats-new-in-txtai-4-0-bbc3a65c3d1c) +- [Getting started with semantic search](https://medium.com/neuml/getting-started-with-semantic-search-a9fd9d8a48cf) | [workflows](https://medium.com/neuml/getting-started-with-semantic-workflows-2fefda6165d9) | [rag](https://medium.com/neuml/getting-started-with-rag-9a0cca75f748) +- [Running txtai at scale](https://medium.com/neuml/running-at-scale-with-txtai-71196cdd99f9) +- [Vector search & RAG Landscape: A review with txtai](https://medium.com/neuml/vector-search-rag-landscape-a-review-with-txtai-a7f37ad0e187) + +## Documentation + +[Full documentation on txtai](https://neuml.github.io/txtai) including configuration settings for embeddings, pipelines, workflows, API and a FAQ with common questions/issues is available. + +## Contributing + +For those who would like to contribute to txtai, please see [this guide](https://github.com/neuml/.github/blob/master/CONTRIBUTING.md). diff --git a/README.wehub.md b/README.wehub.md new file mode 100644 index 0000000..81d7170 --- /dev/null +++ b/README.wehub.md @@ -0,0 +1,7 @@ +# WeHub 来源说明 + +- 原始项目:`neuml/txtai` +- 原始仓库:https://github.com/neuml/txtai +- 导入方式:上游默认分支的最新快照 +- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准 +- 本文件仅用于记录来源,不代表 WeHub 是原项目作者 diff --git a/apps.jpg b/apps.jpg new file mode 100644 index 0000000..eec22d6 Binary files /dev/null and b/apps.jpg differ diff --git a/demo.gif b/demo.gif new file mode 100644 index 0000000..f6d5b7b Binary files /dev/null and b/demo.gif differ diff --git a/docker/api/Dockerfile b/docker/api/Dockerfile new file mode 100644 index 0000000..2899cbb --- /dev/null +++ b/docker/api/Dockerfile @@ -0,0 +1,13 @@ +# Set base image +ARG BASE_IMAGE=neuml/txtai-cpu +FROM $BASE_IMAGE + +# Copy configuration +COPY config.yml . + +# Run local API instance to cache models in container +RUN python -c "from txtai.api import API; API('config.yml', False)" + +# Start server and listen on all interfaces +ENV CONFIG "config.yml" +ENTRYPOINT ["uvicorn", "--host", "0.0.0.0", "txtai.api:app"] diff --git a/docker/aws/Dockerfile b/docker/aws/Dockerfile new file mode 100644 index 0000000..3b4c579 --- /dev/null +++ b/docker/aws/Dockerfile @@ -0,0 +1,23 @@ +# Set base image +ARG BASE_IMAGE=neuml/txtai-cpu +FROM $BASE_IMAGE + +# Application script to copy into image +ARG APP=api.py + +# Install Lambda Runtime Interface Client and Mangum ASGI bindings +RUN pip install awslambdaric mangum + +# Copy configuration +COPY config.yml . + +# Run local API instance to cache models in container +RUN python -c "from txtai.api import API; API('config.yml', False)" + +# Copy application +COPY $APP ./app.py + +# Start runtime client using default application handler +ENV CONFIG "config.yml" +ENTRYPOINT ["python", "-m", "awslambdaric"] +CMD ["app.handler"] diff --git a/docker/aws/api.py b/docker/aws/api.py new file mode 100644 index 0000000..94c49a6 --- /dev/null +++ b/docker/aws/api.py @@ -0,0 +1,17 @@ +""" +Lambda handler for a txtai API instance +""" + +from mangum import Mangum + +from txtai.api import app, start + +# pylint: disable=C0103 +# Create FastAPI application instance wrapped by Mangum +handler = None +if not handler: + # Start application + start() + + # Create handler + handler = Mangum(app, lifespan="off") diff --git a/docker/aws/workflow.py b/docker/aws/workflow.py new file mode 100644 index 0000000..f896ea3 --- /dev/null +++ b/docker/aws/workflow.py @@ -0,0 +1,33 @@ +""" +Lambda handler for txtai workflows +""" + +import json + +from txtai.api import API + +APP = None + + +# pylint: disable=W0603,W0613 +def handler(event, context): + """ + Runs a workflow using input event parameters. + + Args: + event: input event + context: input context + + Returns: + Workflow results + """ + + # Create (or get) global app instance + global APP + APP = APP if APP else API("config.yml") + + # Get parameters from event body + event = json.loads(event["body"]) + + # Run workflow and return results + return {"statusCode": 200, "headers": {"Content-Type": "application/json"}, "body": list(APP.workflow(event["name"], event["elements"]))} diff --git a/docker/base/Dockerfile b/docker/base/Dockerfile new file mode 100644 index 0000000..e365d21 --- /dev/null +++ b/docker/base/Dockerfile @@ -0,0 +1,38 @@ +# Set base image +ARG BASE_IMAGE=python:3.10-slim +FROM $BASE_IMAGE + +# Install GPU-enabled version of PyTorch if set +ARG GPU + +# Target CPU architecture +ARG TARGETARCH + +# Set Python version (i.e. 3, 3.10) +ARG PYTHON_VERSION=3 + +# List of txtai components to install +ARG COMPONENTS=[all] + +# Locale environment variables +ENV LC_ALL=C.UTF-8 +ENV LANG=C.UTF-8 + +RUN \ + # Install required packages + apt-get update && \ + apt-get -y --no-install-recommends install libgomp1 libportaudio2 libsndfile1 libegl1 libgles2 libvulkan1 git gcc g++ python${PYTHON_VERSION} python${PYTHON_VERSION}-dev python3-pip && \ + rm -rf /var/lib/apt/lists && \ + \ + # Install txtai project and dependencies + ln -s /usr/bin/python${PYTHON_VERSION} /usr/bin/python && \ + python -m pip install --no-cache-dir -U pip wheel setuptools && \ + if [ -z ${GPU} ]; then pip install --no-cache-dir torch==2.12.1+cpu torchvision==0.27.1+cpu -f https://download.pytorch.org/whl/torch -f https://download.pytorch.org/whl/torchvision; fi && \ + python -m pip install --no-cache-dir txtai${COMPONENTS} && \ + python -c "import sys, importlib.util as util; 1 if util.find_spec('nltk') else sys.exit(); import nltk; nltk.download(['punkt', 'punkt_tab', 'averaged_perceptron_tagger_eng'])" && \ + \ + # Cleanup build packages + apt-get -y purge git gcc g++ python${PYTHON_VERSION}-dev && apt-get -y autoremove + +# Set default working directory +WORKDIR /app diff --git a/docker/minimal/Dockerfile b/docker/minimal/Dockerfile new file mode 100644 index 0000000..36c7109 --- /dev/null +++ b/docker/minimal/Dockerfile @@ -0,0 +1,20 @@ +# Set base image +ARG BASE_IMAGE=python:3.10-slim +FROM $BASE_IMAGE + +# Target CPU architecture +ARG TARGETARCH + +# Set Python version (i.e. 3, 3.10) +ARG PYTHON_VERSION=3 + +# Locale environment variables +ENV LC_ALL=C.UTF-8 +ENV LANG=C.UTF-8 + +RUN \ + # Install minimal txtai project and dependencies + MINIMAL=1 python -m pip install --no-cache-dir txtai_minimal + +# Set default working directory +WORKDIR /app diff --git a/docker/schedule/Dockerfile b/docker/schedule/Dockerfile new file mode 100644 index 0000000..03fd578 --- /dev/null +++ b/docker/schedule/Dockerfile @@ -0,0 +1,12 @@ +# Set base image +ARG BASE_IMAGE=neuml/txtai-cpu +FROM $BASE_IMAGE + +# Copy configuration +COPY config.yml . + +# Run local API instance to cache models in container +RUN python -c "from txtai.api import API; API('config.yml', False)" + +# Start application and wait for completion. Scheduled workflows can run indefinitely. +ENTRYPOINT ["python", "-c", "from txtai.api import API; API('config.yml').wait()"] diff --git a/docker/workflow/Dockerfile b/docker/workflow/Dockerfile new file mode 100644 index 0000000..a906db5 --- /dev/null +++ b/docker/workflow/Dockerfile @@ -0,0 +1,13 @@ +# Set base image +ARG BASE_IMAGE=neuml/txtai-cpu +FROM $BASE_IMAGE + +# Copy configuration +COPY config.yml . + +# Run local API instance to cache models in container +RUN python -c "from txtai.api import API; API('config.yml', False)" + +# Run workflow. Requires two command line arguments: name of workflow and input elements +ENTRYPOINT ["python", "-c", "import sys; from txtai.api import API\nfor _ in API('config.yml').workflow(sys.argv[1], sys.argv[2:]): pass"] +CMD ["workflow"] diff --git a/docs/agent/configuration.md b/docs/agent/configuration.md new file mode 100644 index 0000000..d53f511 --- /dev/null +++ b/docs/agent/configuration.md @@ -0,0 +1,141 @@ +# Configuration + +An agent takes two main arguments, an LLM and a list of tools. + +The txtai agent framework is built with [smolagents](https://github.com/huggingface/smolagents). Additional options can be passed in the `Agent` constructor. + +```python +from datetime import datetime + +from txtai import Agent + +wikipedia = { + "name": "wikipedia", + "description": "Searches a Wikipedia database", + "provider": "huggingface-hub", + "container": "neuml/txtai-wikipedia" +} + +arxiv = { + "name": "arxiv", + "description": "Searches a database of scientific papers", + "provider": "huggingface-hub", + "container": "neuml/txtai-arxiv" +} + +def today() -> str: + """ + Gets the current date and time + + Returns: + current date and time + """ + + return datetime.today().isoformat() + +agent = Agent( + model="Qwen/Qwen3-4B-Instruct-2507", + tools=[today, wikipedia, arxiv, "websearch"], +) +``` + +## model + +```yaml +model: string|llm instance +``` + +LLM model path or LLM pipeline instance. The `llm` parameter is also supported for backwards compatibility. + +See the [LLM pipeline](../../pipeline/text/llm) for more information. + +## tools + +```yaml +tools: list +``` + +List of tools to supply to the agent. Supports the following configurations. + +### function + +A function tool takes the following dictionary fields. + +| Field | Description | +|:------------|:-------------------------| +| name | name of the tool | +| description | tool description | +| target | target method / callable | + +A function or callable method can also be directly supplied in the `tools` list. In this case, the fields are inferred from the method documentation. + +### embeddings + +Embeddings indexes have built-in support. Provide the following dictionary configuration to add an embeddings index as a tool. + +| Field | Description | +|:------------|:-------------------------------------------| +| name | embeddings index name | +| description | embeddings index description | +| **kwargs | Parameters to pass to [embeddings.load](../../embeddings/methods/#txtai.embeddings.Embeddings.load) | + +### tool + +The following shortcut strings load tools directly. Passing a Tool instance is also supported. + +| Tool | Description | +|:------------|:----------------------------------------------------------| +| bash | Runs a shell command through subprocess | +| defaults | Loads all of these tools as the default toolkit | +| edit | Edits a file in place and returns a diff | +| glob | Finds matching file patterns in a directory | +| grep | Finds matching file content in a directory | +| http.* | HTTP Path to a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/docs/getting-started/intro) server | +| python | Runs a Python action | +| read | Reads file or url content, supports text extraction | +| todowrite | Generates a task list to organize complex tasks | +| websearch | Runs a websearch using the built-in websearch tool | +| webview | Extracts content from a web page. Alias for `read` tool | +| write | Writes content to file | +| *.md | Loads a [`skill.md`](https://agentskills.io/specification) file | + +## instructions + +```yaml +instructions: string|path +``` + +Supports loading an `agents.md` file. Can be provided directly as a string or as a path to a file. + +[Read more about agents.md here](https://github.com/agentsmd/agents.md) + +## template + +```yaml +template: string +``` + +Customize the prompt template used by this agent. Supports Jinja templates. Uses a default template when this parameter is not provided. +Must include `{{ text }}` and `{{ memory }}` placeholders. + +## memory + +```yaml +memory: int +``` + +Keeps a rolling window of `memory` inputs and outputs. These are added to future prompts and serve as "agent memory". + +Supports storing memory by `session` to enable multiple conversation threads. Defaults to shared memory when not set. See the [method documentation](../methods#txtai.agent.base.Agent.__call__) for more information. + +## method + +```yaml +method: code|tool +``` + +Sets the agent method. Supports either a `code` or `tool` (default) calling agent. A code agent generates Python code and executes that. A tool calling agent generates JSON blocks and calls the agents within those blocks. + +Additional options can be directly passed. See [CodeAgent](https://huggingface.co/docs/smolagents/main/en/reference/agents#smolagents.CodeAgent) or [ToolCallingAgent](https://huggingface.co/docs/smolagents/main/en/reference/agents#smolagents.ToolCallingAgent) for a list of parameters. + +[Read more here](https://huggingface.co/docs/smolagents/main/en/guided_tour). diff --git a/docs/agent/index.md b/docs/agent/index.md new file mode 100644 index 0000000..761328b --- /dev/null +++ b/docs/agent/index.md @@ -0,0 +1,156 @@ +# Agent + +![agent](../images/agent.png) + +An agent automatically creates workflows to answer multi-faceted user requests. Agents iteratively prompt and/or interface with tools to +step through a process and ultimately come to an answer for a request. + +Agent prompting with [`agents.md`](https://github.com/agentsmd/agents.md) and [`skill.md`](https://agentskills.io/specification) are also supported. [Read the configuration](./configuration/#tool) for more on how to setup those up. + +Agents excel at complex tasks where multiple tools and/or methods are required. They incorporate a level of randomness similar to different +people working on the same task. When the request is simple and/or there is a rule-based process, other methods such as RAG and Workflows +should be explored. + +The following code snippet defines a basic agent. + +```python +from datetime import datetime + +from txtai import Agent + +wikipedia = { + "name": "wikipedia", + "description": "Searches a Wikipedia database", + "provider": "huggingface-hub", + "container": "neuml/txtai-wikipedia" +} + +arxiv = { + "name": "arxiv", + "description": "Searches a database of scientific papers", + "provider": "huggingface-hub", + "container": "neuml/txtai-arxiv" +} + +def today() -> str: + """ + Gets the current date and time + + Returns: + current date and time + """ + + return datetime.today().isoformat() + +agent = Agent( + model="Qwen/Qwen3-4B-Instruct-2507", + tools=[today, wikipedia, arxiv, "websearch"], + max_steps=10, +) +``` + +The agent above has access to two embeddings databases (Wikipedia and ArXiv) and the web. Given the user's input request, the agent decides the best tool to solve the task. + +## Example + +The first example will solve a problem with multiple data points. See below. + +```python +agent("Which city has the highest population, Boston or New York?") +``` + +This requires looking up the population of each city before knowing how to answer the question. Multiple search requests are run to generate a final answer. + +## Agentic RAG + +Standard retrieval augmented generation (RAG) runs a single vector search to obtain a context and builds a prompt with the context + input question. Agentic RAG is a more complex process that goes through multiple iterations. It can also utilize multiple databases to come to a final conclusion. + +The example below aggregates information from multiple sources and builds a report on a topic. + +```python +researcher = """ +You're an expert researcher looking to write a paper on {topic}. +Search for websites, scientific papers and Wikipedia related to the topic. +Write a report with summaries and references (with hyperlinks). +Write the text as Markdown. +""" + +agent(researcher.format(topic="alien life")) +``` + +## Agent Teams + +Agents can also be tools. This enables the concept of building "Agent Teams" to solve problems. The previous example can be rewritten as a list of agents. + +```python +from txtai import Agent, LLM + +llm = LLM("Qwen/Qwen3-4B-Instruct-2507") + +websearcher = Agent( + model=llm, + tools=["websearch"], +) + +wikiman = Agent( + model=llm, + tools=[{ + "name": "wikipedia", + "description": "Searches a Wikipedia database", + "provider": "huggingface-hub", + "container": "neuml/txtai-wikipedia" + }], +) + +researcher = Agent( + model=llm, + tools=[{ + "name": "arxiv", + "description": "Searches a database of scientific papers", + "provider": "huggingface-hub", + "container": "neuml/txtai-arxiv" + }], +) + +agent = Agent( + model=llm, + tools=[{ + "name": "websearcher", + "description": "I run web searches, there is no answer a web search can't solve!", + "target": websearcher + }, { + "name": "wikiman", + "description": "Wikipedia has all the answers, I search Wikipedia and answer questions", + "target": wikiman + }, { + "name": "researcher", + "description": "I'm a science guy. I search arXiv to get all my answers.", + "target": researcher + }], + max_steps=10 +) +``` + +This provides another level of intelligence to the process. Instead of just a single tool execution, each agent-tool combination has it's own reasoning engine. + +```python +agent(""" +Research fundamental concepts about Signal Processing and build a comprehensive report. +Write the output in Markdown. +""") +``` + +# More examples + +Check out this [Agent Quickstart Example](https://github.com/neuml/txtai/blob/master/examples/agent_quickstart.py). Additional examples are listed below. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [What's new in txtai 8.0](https://github.com/neuml/txtai/blob/master/examples/67_Whats_new_in_txtai_8_0.ipynb) | Agents with txtai | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/67_Whats_new_in_txtai_8_0.ipynb) | +| [Analyzing Hugging Face Posts with Graphs and Agents](https://github.com/neuml/txtai/blob/master/examples/68_Analyzing_Hugging_Face_Posts_with_Graphs_and_Agents.ipynb) | Explore a rich dataset with Graph Analysis and Agents | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/68_Analyzing_Hugging_Face_Posts_with_Graphs_and_Agents.ipynb) | +| [Granting autonomy to agents](https://github.com/neuml/txtai/blob/master/examples/69_Granting_autonomy_to_agents.ipynb) | Agents that iteratively solve problems as they see fit | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/69_Granting_autonomy_to_agents.ipynb) | +| [Analyzing LinkedIn Company Posts with Graphs and Agents](https://github.com/neuml/txtai/blob/master/examples/71_Analyzing_LinkedIn_Company_Posts_with_Graphs_and_Agents.ipynb) | Exploring how to improve social media engagement with AI | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/71_Analyzing_LinkedIn_Company_Posts_with_Graphs_and_Agents.ipynb) | +| [Parsing the stars with txtai](https://github.com/neuml/txtai/blob/master/examples/72_Parsing_the_stars_with_txtai.ipynb) | Explore an astronomical knowledge graph of known stars, planets, galaxies | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/72_Parsing_the_stars_with_txtai.ipynb) | +| [Agentic College Search](https://github.com/neuml/txtai/blob/master/examples/82_Agentic_College_Search.ipynb) | Identify list of strong engineering colleges | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/82_Agentic_College_Search.ipynb) | +| [TxtAI got skills](https://github.com/neuml/txtai/blob/master/examples/83_TxtAI_got_skills.ipynb) | Integrate skill.md files with your agent | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/83_TxtAI_got_skills.ipynb) | +| [Agent Tools](https://github.com/neuml/txtai/blob/master/examples/84_Agent_Tools.ipynb) [▶️](https://www.youtube.com/watch?v=RDNaFXQy3GQ) | Learn about the txtai agent toolkit | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/84_Agent_Tools.ipynb) | diff --git a/docs/agent/methods.md b/docs/agent/methods.md new file mode 100644 index 0000000..fcbc12c --- /dev/null +++ b/docs/agent/methods.md @@ -0,0 +1,4 @@ +# Methods + +## ::: txtai.agent.base.Agent.__init__ +## ::: txtai.agent.base.Agent.__call__ diff --git a/docs/api/cluster.md b/docs/api/cluster.md new file mode 100644 index 0000000..4090b98 --- /dev/null +++ b/docs/api/cluster.md @@ -0,0 +1,20 @@ +# Distributed embeddings clusters + +The API supports combining multiple API instances into a single logical embeddings index. An example configuration is shown below. + +```yaml +cluster: + shards: + - http://127.0.0.1:8002 + - http://127.0.0.1:8003 +``` + +This configuration aggregates the API instances above as index shards. Data is evenly split among each of the shards at index time. Queries are run in parallel against each shard and the results are joined together. This method allows horizontal scaling and supports very large index clusters. + +This method is only recommended for data sets in the 1 billion+ records. The ANN libraries can easily support smaller data sizes and this method is not worth the additional complexity. At this time, new shards can not be added after building the initial index. + +See the link below for a detailed example covering distributed embeddings clusters. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Distributed embeddings cluster](https://github.com/neuml/txtai/blob/master/examples/15_Distributed_embeddings_cluster.ipynb) | Distribute an embeddings index across multiple data nodes | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/15_Distributed_embeddings_cluster.ipynb) | diff --git a/docs/api/configuration.md b/docs/api/configuration.md new file mode 100644 index 0000000..fb0d9ec --- /dev/null +++ b/docs/api/configuration.md @@ -0,0 +1,165 @@ +# Configuration + +Configuration is set through YAML. In most cases, YAML keys map to fields names in Python. The [example in the previous section](../) gave a full-featured example covering a wide array of configuration options. + +Each section below describes the available configuration settings. + +## Embeddings + +The configuration parser expects a top level `embeddings` key to be present in the YAML. All [embeddings configuration](../../embeddings/configuration) is supported. + +The following example defines an embeddings index. + +```yaml +path: index path +writable: true + +embeddings: + path: vector model + content: true +``` + +Three top level settings are available to control where indexes are saved and if an index is a read-only index. + +### path +```yaml +path: string +``` + +Path to save and load the embeddings index. Each API instance can only access a single index at a time. + +### writable +```yaml +writable: boolean +``` + +Sets this embeddings index as writable (true) or read-only (false). This allows serving a read-only index. Defaults to read-only. + +### reindex +```yaml +reindex: boolean +``` + +Enables re-indexing for this embeddings index (defaults to disabled). + +Re-indexing accepts new configuration and should only be enabled when the configuration comes from trusted sources. + +### cloud +[Cloud storage settings](../../embeddings/configuration/cloud) can be set under a `cloud` top level configuration group. + +## Agent + +Agents are defined under a top level `agent` key. Each key under the `agent` key is the name of the agent. Constructor parameters can be passed under this key. + +The following example defines an agent. + +```yaml +agent: + researcher: + tools: + - websearch + +llm: + path: Qwen/Qwen3-4B-Instruct-2507 +``` + +## Pipeline + +Pipelines are loaded as top level configuration parameters. Pipeline names are automatically detected in the YAML configuration and created upon startup. All [pipelines](../../pipeline) are supported. + +The following example defines a series of pipelines. Note that entries below are the lower-case names of the pipeline class. + +```yaml +caption: + +extractor: + path: model path + +labels: + +summary: + +tabular: + +translation: +``` + +Under each pipeline name, configuration settings for the pipeline can be set. + +## Workflow + +Workflows are defined under a top level `workflow` key. Each key under the `workflow` key is the name of the workflow. Under that is a `tasks` key with each task definition. + +The following example defines a workflow. + +```yaml +workflow: + sumtranslate: + tasks: + - action: summary + - action: translation +``` + +### schedule + +Schedules a workflow using a [cron expression](../../workflow/schedule). + +```yaml +workflow: + index: + schedule: + cron: 0/10 * * * * * + elements: ["api params"] + tasks: + - task: service + url: api url + - action: index +``` + +### tasks +```yaml +tasks: list +``` + +Expects a list of workflow tasks. Each element defines a single workflow task. All [task configuration](../../workflow/task) is supported. + +A shorthand syntax for creating tasks is supported. This syntax will automatically map task strings to an `action:value` pair. + +Example below. + +```yaml +workflow: + index: + tasks: + - action1 + - action2 +``` + +Each task element supports the following additional arguments. + +#### action +```yaml +action: string|list +``` + +Both single and multi-action tasks are supported. + +The action parameter works slightly different when passed via configuration. The parameter(s) needs to be converted into callable method(s). If action is a pipeline that has been defined in the current configuration, it will use that pipeline as the action. + +There are three special action names `index`, `upsert` and `search`. If `index` or `upsert` are used as the action, the task will collect workflow data elements and load them into defined the embeddings index. If `search` is used, the task will execute embeddings queries for each input data element. + +Otherwise, the action must be a path to a callable object or function. The configuration parser will resolve the function name and use that as the task action. + +#### task +```yaml +task: string +``` + +Optionally sets the type of task to create. For example, this could be a `file` task or a `retrieve` task. If this is not specified, a generic task is created. [The list of workflow tasks can be found here](../../workflow). + +#### args +```yaml +args: list +``` + +Optional list of static arguments to pass to the workflow task. These are combined with workflow data to pass to each `__call__`. diff --git a/docs/api/customization.md b/docs/api/customization.md new file mode 100644 index 0000000..3bcb402 --- /dev/null +++ b/docs/api/customization.md @@ -0,0 +1,25 @@ +# Customization + +The txtai API has a number of features out of the box that are designed to help get started quickly. API services can also be augmented with custom code and functionality. The two main ways to do this are with extensions and dependencies. + +Extensions add a custom endpoint. Dependencies add middleware that executes with each request. See the sections below for more. + +## Extensions + +While the API is extremely flexible and complex logic can be executed through YAML-driven workflows, some may prefer to create an endpoint in Python. API extensions define custom Python endpoints that interact with txtai applications. + +See the link below for a detailed example. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Custom API Endpoints](https://github.com/neuml/txtai/blob/master/examples/51_Custom_API_Endpoints.ipynb) | Extend the API with custom endpoints | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/51_Custom_API_Endpoints.ipynb) | + +## Dependencies + +txtai has a default API token authorization method that works well in many cases. Dependencies can also add custom logic with each request. This could be an additional authorization step and/or an authentication method. + +See the link below for a detailed example. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [API Authorization and Authentication](https://github.com/neuml/txtai/blob/master/examples/54_API_Authorization_and_Authentication.ipynb) | Add authorization, authentication and middleware dependencies to the API | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/54_API_Authorization_and_Authentication.ipynb) | diff --git a/docs/api/index.md b/docs/api/index.md new file mode 100644 index 0000000..feb1ba7 --- /dev/null +++ b/docs/api/index.md @@ -0,0 +1,134 @@ +# API + +![api](../images/api.png#only-light) +![api](../images/api-dark.png#only-dark) + +txtai has a full-featured API, backed by [FastAPI](https://github.com/tiangolo/fastapi), that can optionally be enabled for any txtai process. All functionality found in txtai can be accessed via the API. + +The following is an example configuration and startup script for the API. + +Note: This configuration file enables all functionality. For memory-bound systems, splitting pipelines into multiple instances is a best practice. + +```yaml +# Index file path +path: /tmp/index + +# Allow indexing of documents +writable: True + +# Enbeddings index +embeddings: + path: sentence-transformers/nli-mpnet-base-v2 + +# Extractive QA +extractor: + path: distilbert-base-cased-distilled-squad + +# Zero-shot labeling +labels: + +# Similarity +similarity: + +# Text segmentation +segmentation: + sentences: true + +# Text summarization +summary: + +# Text extraction +textractor: + paragraphs: true + minlength: 100 + join: true + +# Transcribe audio to text +transcription: + +# Translate text between languages +translation: + +# Workflow definitions +workflow: + sumfrench: + tasks: + - action: textractor + task: url + - action: summary + - action: translation + args: ["fr"] + sumspanish: + tasks: + - action: textractor + task: url + - action: summary + - action: translation + args: ["es"] +``` + +Assuming this YAML content is stored in a file named config.yml, the following command starts the API process. + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" +``` + +Uvicorn is a full-featured production-ready server. See the [Uvicorn deployment guide](https://www.uvicorn.org/deployment/) for more on configuration options. + +## Connect to API + +The default port for the API is 8000. See the uvicorn link above to change this. + +txtai has a number of language bindings which abstract the API (see links below). Alternatively, code can be written to connect directly to the API. Documentation for a live running instance can be found at the `/docs` url (i.e. http://localhost:8000/docs). The following example runs a workflow using cURL. + +```bash +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name":"sumfrench", "elements": ["https://github.com/neuml/txtai"]}' +``` + +## Local instance + +A local instance can be instantiated. In this case, a txtai application runs internally, without any network connections, providing the same consolidated functionality. This enables running txtai in Python with configuration. + +The configuration above can be run in Python with: + +```python +from txtai import Application + +# Load and run workflow +app = Application(config.yml) +app.workflow("sumfrench", ["https://github.com/neuml/txtai"]) +``` + +See this [link for a full list of methods](./methods). + +## Run with containers + +The API can be containerized and run. This will bring up an API instance without having to install Python, txtai or any dependencies on your machine! + +[See this section for more information](../cloud/#api). + +## Supported language bindings + +The following programming languages have bindings with the txtai API: + +- [Python](https://github.com/neuml/txtai.py) +- [JavaScript](https://github.com/neuml/txtai.js) +- [Java](https://github.com/neuml/txtai.java) +- [Rust](https://github.com/neuml/txtai.rs) +- [Go](https://github.com/neuml/txtai.go) + +The API also supports hosting [OpenAI-compatible](./openai) and [Model Context Protocol (MCP)](./mcp) endpoints. + +See the links below for detailed examples covering the API. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [API Gallery](https://github.com/neuml/txtai/blob/master/examples/08_API_Gallery.ipynb) | Using txtai in JavaScript, Java, Rust and Go | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/08_API_Gallery.ipynb) | +| [Distributed embeddings cluster](https://github.com/neuml/txtai/blob/master/examples/15_Distributed_embeddings_cluster.ipynb) | Distribute an embeddings index across multiple data nodes | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/15_Distributed_embeddings_cluster.ipynb) | +| [Embeddings in the Cloud](https://github.com/neuml/txtai/blob/master/examples/43_Embeddings_in_the_Cloud.ipynb) | Load and use an embeddings index from the Hugging Face Hub | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/43_Embeddings_in_the_Cloud.ipynb) | +| [Custom API Endpoints](https://github.com/neuml/txtai/blob/master/examples/51_Custom_API_Endpoints.ipynb) | Extend the API with custom endpoints | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/51_Custom_API_Endpoints.ipynb) | +| [API Authorization and Authentication](https://github.com/neuml/txtai/blob/master/examples/54_API_Authorization_and_Authentication.ipynb) | Add authorization, authentication and middleware dependencies to the API | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/54_API_Authorization_and_Authentication.ipynb) | +| [OpenAI Compatible API](https://github.com/neuml/txtai/blob/master/examples/74_OpenAI_Compatible_API.ipynb) | Connect to txtai with a standard OpenAI client library | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/74_OpenAI_Compatible_API.ipynb) | diff --git a/docs/api/mcp.md b/docs/api/mcp.md new file mode 100644 index 0000000..0c23581 --- /dev/null +++ b/docs/api/mcp.md @@ -0,0 +1,34 @@ +# Model Context Protocol + +The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is an open standard that enables developers to build secure, two-way connections between their data sources and AI-powered tools. + +The API can be configured to handle MCP requests. All enabled endpoints set in the API configuration are automatically added as MCP tools. + +```yaml +mcp: boolean|dict +``` + +When `mcp` is a boolean, default arguments are used. When `mcp` is a dictionary it supports the following options. + +```yaml +mcp: + clientargs: http client options (dict) + mcpargs: FastApiMCP options (dict) +``` + +See the following links for details on the options. + +- [HTTP Client Arguments](https://www.python-httpx.org/api/#asyncclient) +- [FastApiMCP Arguments](https://github.com/tadata-org/fastapi_mcp/blob/main/fastapi_mcp/server.py#L22) + +Once this configuration option is added, a new route is added to the application `/mcp`. + +The [Model Context Protocol Inspector tool](https://www.npmjs.com/package/@modelcontextprotocol/inspector) is a quick way to explore how the MCP tools are exported through this interface. + +Run the following and go to the local URL specified. + +``` +npx @modelcontextprotocol/inspector node build/index.js +``` + +Enter `http://localhost:8000/mcp` to see the full list of tools available. diff --git a/docs/api/methods.md b/docs/api/methods.md new file mode 100644 index 0000000..b81aaa8 --- /dev/null +++ b/docs/api/methods.md @@ -0,0 +1,15 @@ +# Methods + +::: txtai.api.API + options: + inherited_members: true + filters: + - "!__del__" + - "!flows" + - "!function" + - "!indexes" + - "!limit" + - "!pipes" + - "!read" + - "!resolve" + - "!weights" diff --git a/docs/api/openai.md b/docs/api/openai.md new file mode 100644 index 0000000..976e3f9 --- /dev/null +++ b/docs/api/openai.md @@ -0,0 +1,13 @@ +# OpenAI-compatible API + +The API can be configured to serve an OpenAI-compatible API as shown below. + +```yaml +openai: True +``` + +See the link below for a detailed example. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [OpenAI Compatible API](https://github.com/neuml/txtai/blob/master/examples/74_OpenAI_Compatible_API.ipynb) | Connect to txtai with a standard OpenAI client library | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/74_OpenAI_Compatible_API.ipynb) | diff --git a/docs/api/security.md b/docs/api/security.md new file mode 100644 index 0000000..cbe4a79 --- /dev/null +++ b/docs/api/security.md @@ -0,0 +1,33 @@ +# Security + +The default implementation of an API service runs via HTTP and is fully open. If the service is being run as a prototype on an internal network, that may be fine. In most scenarios, the connection should at least be encrypted. Authorization is another built-in feature that requires a valid API token with each request. See below for more. + +## HTTPS + +The default API service command starts a Uvicorn server as a HTTP service on port 8000. To run a HTTPS service, consider the following options. + +- [TLS Proxy Server](https://fastapi.tiangolo.com/deployment/https/). *Recommended choice*. With this configuration, the txtai API service runs as a HTTP service only accessible on the localhost/local network. The proxy server handles all encryption and redirects requests to local services. See this [example configuration](https://www.uvicorn.org/deployment/#running-behind-nginx) for more. + +- [Uvicorn SSL Certificate](https://www.uvicorn.org/deployment/). Another option is setting the SSL certificate on the Uvicorn service. This works in simple situations but gets complex when hosting multiple txtai or other related services. + +## Authorization + +Authorization requires a valid API token with each API request. This token is sent as a HTTP `Authorization` header. + +*Server* +```bash +CONFIG=config.yml TOKEN= uvicorn "txtai.api:app" +``` + +*Client* +```bash +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer " \ + -d '{"name":"sumfrench", "elements": ["https://github.com/neuml/txtai"]}' +``` + +It's important to note that HTTPS **must** be enabled using one of the methods mentioned above. Otherwise, tokens will be exchanged as clear text. + +Authentication and Authorization can be fully customized. See the [dependencies](../customization#dependencies) section for more. diff --git a/docs/cloud.md b/docs/cloud.md new file mode 100644 index 0000000..7bb735d --- /dev/null +++ b/docs/cloud.md @@ -0,0 +1,182 @@ +# Cloud + +![cloud](images/cloud.png#only-light) +![cloud](images/cloud-dark.png#only-dark) + +Scalable cloud-native applications can be built with txtai. The following cloud runtimes are supported. + +- Container Orchestration Systems (i.e. Kubernetes) +- Docker Engine +- Serverless Compute +- txtai.cloud (planned for future) + +Images for txtai are available on Docker Hub for [CPU](https://hub.docker.com/r/neuml/txtai-cpu) and [GPU](https://hub.docker.com/r/neuml/txtai-gpu) installs. The CPU install is recommended when GPUs aren't available given the image is significantly smaller. + +The base txtai images have no models installed and models will be downloaded each time the container starts. Caching the models is recommended as that will significantly reduce container start times. This can be done a couple different ways. + +- Create a container with the [models cached](#container-image-model-caching) +- Set the transformers cache environment variable and mount that volume when starting the image + ```bash + docker run -v :/models -e TRANSFORMERS_CACHE=/models --rm -it + ``` + +## Build txtai images + +The txtai images found on Docker Hub are configured to support most situations. This image can be locally built with different options as desired. + +Examples build commands below. + +```bash +# Get Dockerfile +wget https://raw.githubusercontent.com/neuml/txtai/master/docker/base/Dockerfile + +# Build Ubuntu 22.04 image running Python 3.10 +docker build -t txtai --build-arg BASE_IMAGE=ubuntu:22.04 --build-arg PYTHON_VERSION=3.10 . + +# Build image with GPU support +docker build -t txtai --build-arg GPU=1 . + +# Build minimal image with the base txtai components +docker build -t txtai --build-arg COMPONENTS= . +``` + +## Container image model caching + +As mentioned previously, model caching is recommended to reduce container start times. The following commands demonstrate this. In all cases, it is assumed a config.yml file is present in the local directory with the desired configuration set. + +### API +This section builds an image that caches models and starts an API service. The config.yml file should be configured with the desired components to expose via the API. + +The following is a sample config.yml file that creates an Embeddings API service. + +```yaml +# config.yml +writable: true + +embeddings: + path: sentence-transformers/nli-mpnet-base-v2 + content: true +``` + +The next section builds the image and starts an instance. + +```bash +# Get Dockerfile +wget https://raw.githubusercontent.com/neuml/txtai/master/docker/api/Dockerfile + +# CPU build +docker build -t txtai-api . + +# GPU build +docker build -t txtai-api --build-arg BASE_IMAGE=neuml/txtai-gpu . + +# Run +docker run -p 8000:8000 --rm -it txtai-api +``` + +### Service +This section builds a scheduled workflow service. [More on scheduled workflows can be found here.](../workflow/schedule) + +```bash +# Get Dockerfile +wget https://raw.githubusercontent.com/neuml/txtai/master/docker/service/Dockerfile + +# CPU build +docker build -t txtai-service . + +# GPU build +docker build -t txtai-service --build-arg BASE_IMAGE=neuml/txtai-gpu . + +# Run +docker run --rm -it txtai-service +``` + +### Workflow +This section builds a single run workflow. [Example workflows can be found here.](../examples/#workflows) + +```bash +# Get Dockerfile +wget https://raw.githubusercontent.com/neuml/txtai/master/docker/workflow/Dockerfile + +# CPU build +docker build -t txtai-workflow . + +# GPU build +docker build -t txtai-workflow --build-arg BASE_IMAGE=neuml/txtai-gpu . + +# Run +docker run --rm -it txtai-workflow +``` + +## Serverless Compute + +One of the most powerful features of txtai is building YAML-configured applications with the "build once, run anywhere" approach. API instances and workflows can run locally, on a server, on a cluster or serverless. + +Serverless instances of txtai are supported on frameworks such as [AWS Lambda](https://aws.amazon.com/lambda/), [Google Cloud Functions](https://cloud.google.com/functions), [Azure Cloud Functions](https://azure.microsoft.com/en-us/services/functions/) and [Kubernetes](https://kubernetes.io/) with [Knative](https://knative.dev/docs/). + +### AWS Lambda + +The following steps show a basic example of how to build a serverless API instance with [AWS SAM](https://github.com/aws/serverless-application-model). + +- Create config.yml and template.yml + +```yaml +# config.yml +writable: true + +embeddings: + path: sentence-transformers/nli-mpnet-base-v2 + content: true +``` + +```yaml +# template.yml +Resources: + txtai: + Type: AWS::Serverless::Function + Properties: + PackageType: Image + MemorySize: 3000 + Timeout: 20 + Events: + Api: + Type: Api + Properties: + Path: "/{proxy+}" + Method: ANY + Metadata: + Dockerfile: Dockerfile + DockerContext: ./ + DockerTag: api +``` + +- Install [AWS SAM](https://pypi.org/project/aws-sam-cli/) + +- Run following + +```bash +# Get Dockerfile and application +wget https://raw.githubusercontent.com/neuml/txtai/master/docker/aws/api.py +wget https://raw.githubusercontent.com/neuml/txtai/master/docker/aws/Dockerfile + +# Build the docker image +sam build + +# Start API gateway and Lambda instance locally +sam local start-api -p 8000 --warm-containers LAZY + +# Verify instance running (should return 0) +curl http://localhost:8080/count +``` + +If successful, a local API instance is now running in a "serverless" fashion. This configuration can be deployed to AWS using SAM. [See this link for more information.](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-deploy.html) + +### Kubernetes with Knative + +txtai scales with container orchestration systems. This can be self-hosted or with a cloud provider such as [Amazon Elastic Kubernetes Service](https://aws.amazon.com/eks/), [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine) and [Azure Kubernetes Service](https://azure.microsoft.com/en-us/services/kubernetes-service/). There are also other smaller providers with a managed Kubernetes offering. + +A full example covering how to build a serverless txtai application on Kubernetes with Knative [can be found here](https://medium.com/neuml/serverless-vector-search-with-txtai-96f6163ab972). + +## txtai.cloud + +[txtai.cloud](https://txtai.cloud) is a planned effort that will offer an easy and secure way to run hosted txtai applications. diff --git a/docs/embeddings/configuration/ann.md b/docs/embeddings/configuration/ann.md new file mode 100644 index 0000000..b39b8a3 --- /dev/null +++ b/docs/embeddings/configuration/ann.md @@ -0,0 +1,136 @@ +# ANN + +Approximate Nearest Neighbor (ANN) index configuration for storing vector embeddings. + +## backend +```yaml +backend: faiss|hnsw|annoy|ggml|numpy|torch|turbovec|pgvector|sqlite|custom +``` + +Sets the ANN backend. Defaults to `faiss`. Additional backends are available via the [ann](../../../install/#ann) extras package. Set custom backends via setting this parameter to the fully resolvable class string. + +Backend-specific settings are set with a corresponding configuration object having the same name as the backend (i.e. annoy, faiss, or hnsw). These are optional and set to defaults if omitted. + +### faiss +```yaml +faiss: + components: comma separated list of components - defaults to "IDMap,Flat" for small + indices and "IVFx,Flat" for larger indexes where + x = min(4 * sqrt(embeddings count), embeddings count / 39) + automatically calculates number of IVF cells when omitted (supports "IVF,Flat") + nprobe: search probe setting (int) - defaults to x/16 (as defined above) + for larger indexes + nflip: same as nprobe - only used with binary hash indexes + quantize: store vectors with x-bit precision vs 32-bit (boolean|int) + true sets 8-bit precision, false disables, int sets specified + precision + mmap: load as on-disk index (boolean) - trade query response time for a + smaller RAM footprint, defaults to false + sample: percent of data to use for model training (0.0 - 1.0) + reduces indexing time for larger (>1M+ row) indexes, defaults to 1.0 +``` + +Faiss supports both floating point and binary indexes. Floating point indexes are the default. Binary indexes are used when indexing scalar-quantized datasets. + +See the following Faiss documentation links for more information. + +- [Guidelines for choosing an index](https://github.com/facebookresearch/faiss/wiki/Guidelines-to-choose-an-index) +- [Index configuration summary](https://github.com/facebookresearch/faiss/wiki/Faiss-indexes) +- [Index Factory](https://github.com/facebookresearch/faiss/wiki/The-index-factory) +- [Binary Indexes](https://github.com/facebookresearch/faiss/wiki/Binary-indexes) +- [Search Tuning](https://github.com/facebookresearch/faiss/wiki/Faster-search) + +Note: For macOS users, an existing bug in an upstream package restricts the number of processing threads to 1. This limitation is managed internally to prevent system crashes. + +### hnsw +```yaml +hnsw: + efconstruction: ef_construction param for init_index (int) - defaults to 200 + m: M param for init_index (int) - defaults to 16 + randomseed: random-seed param for init_index (int) - defaults to 100 + efsearch: ef search param (int) - defaults to None and not set +``` + +See [Hnswlib documentation](https://github.com/nmslib/hnswlib/blob/master/ALGO_PARAMS.md) for more information on these parameters. + +### annoy +```yaml +annoy: + ntrees: number of trees (int) - defaults to 10 + searchk: search_k search setting (int) - defaults to -1 +``` + +See [Annoy documentation](https://github.com/spotify/annoy#full-python-api) for more information on these parameters. Note that annoy indexes can not be modified after creation, upserts/deletes and other modifications are not supported. + +### ggml +```yaml +ggml: + gpu: enable GPU - defaults to True + quantize: sets the tensor quantization - defaults to F32 + querysize: query buffer size - defaults to 64 +``` + +The [GGML](https://github.com/ggml-org/ggml) backend is a k-nearest neighbors backend. It stores tensors using GGML and [GGUF](https://huggingface.co/docs/hub/en/gguf). It supports GPU-enabled operations and supports quantization. GGML is the framework used by [llama.cpp](https://github.com/ggml-org/llama.cpp). + +[See this](https://github.com/ggml-org/ggml/blob/master/include/ggml.h#L379) for a list of quantization types. + +### numpy + +The NumPy backend is a k-nearest neighbors backend. It's designed for simplicity and works well with smaller datasets that fit into memory. + +```yaml +numpy: + safetensors: stores vectors using the safetensors format + defaults to NumPy array storage +``` + +### torch + +The Torch backend is a k-nearest neighbors backend like NumPy. It supports GPU-enabled operations. It also has support for quantization which enables fitting larger arrays into GPU memory. + +When quantization is enabled, vectors are _always_ stored in safetensors. _Note that macOS support for quantization is limited._ + +```yaml +torch: + safetensors: stores vectors using the safetensors format - defaults + to NumPy array storage if quantization is disabled + quantize: + type: quantization type (fp4, nf4, int8) + blocksize: quantization block size parameter +``` + +### turbovec + +The [turbovec](https://github.com/RyanCodrai/turbovec) backend is a k-nearest neighbors backend powered by the [TurboQuant algorithm](https://arxiv.org/abs/2504.19874). + +```yaml +turbovec: + bitwidth: number of bits to store each vector dimension as. Supports 2, 3 or 4. + +### pgvector +```yaml +pgvector: + url: database url connection string, alternatively can be set via + ANN_URL environment variable + schema: database schema to store vectors - defaults to being + determined by the database + table: database table to store vectors - defaults to `vectors` + precision: vector float precision (half or full) - defaults to `full` + efconstruction: ef_construction param (int) - defaults to 200 + m: M param for init_index (int) - defaults to 16 +``` + +The pgvector backend stores embeddings in a Postgres database. See the [pgvector documentation](https://github.com/pgvector/pgvector-python?tab=readme-ov-file#sqlalchemy) for more information on these parameters. See the [SQLAlchemy](https://docs.sqlalchemy.org/en/20/core/engines.html#database-urls) documentation for more information on how to construct url connection strings. + +### sqlite +```yaml +sqlite: + quantize: store vectors with x-bit precision vs 32-bit (boolean|int) + true sets 8-bit precision, false disables, int sets specified + precision + table: database table to store vectors - defaults to `vectors` +``` + +The SQLite backend stores embeddings in a SQLite database using [sqlite-vec](https://github.com/asg017/sqlite-vec). This backend supports 1-bit and 8-bit quantization at the storage level. + +See [this note](https://alexgarcia.xyz/sqlite-vec/python.html#macos-blocks-sqlite-extensions-by-default) on how to run this ANN on MacOS. diff --git a/docs/embeddings/configuration/cloud.md b/docs/embeddings/configuration/cloud.md new file mode 100644 index 0000000..3e4c6a5 --- /dev/null +++ b/docs/embeddings/configuration/cloud.md @@ -0,0 +1,110 @@ +# Cloud + +The following describes parameters used to sync indexes with cloud storage. Cloud object storage, the [Hugging Face Hub](https://huggingface.co/models) and custom providers are all supported. + +Parameters are set via the [embeddings.load](../../methods/#txtai.embeddings.base.Embeddings.load) and [embeddings.save](../../methods/#txtai.embeddings.base.Embeddings.save) methods. + +## provider +```yaml +provider: string +``` + +Cloud provider. Can be one of the following: + +- Cloud object storage. Set to one of these [providers](https://libcloud.readthedocs.io/en/stable/storage/supported_providers.html). Use the text shown in the `Provider Constant` column as lower case. + +- Hugging Face Hub. Set to `huggingface-hub`. + +- Custom providers. Set to the full class path of the custom provider. + +## container +```yaml +container: string +``` + +Container/bucket/directory/repository name. Embeddings will be stored in the container with the filename specified by the `path` configuration. + +## Cloud object storage configuration + +In addition to the above common configuration, the cloud object storage provider has the following additional configuration parameters. Note that some cloud providers do not need any of these parameters and can use implicit authentication with service accounts. + +See the [libcloud documentation](https://libcloud.readthedocs.io/en/stable/apidocs/libcloud.common.html#module-libcloud.common.base) for more information on these parameters. + +### key +```yaml +key: string +``` + +Provider-specific access key. Can also be set via `ACCESS_KEY` environment variable. Ensure the configuration file is secured if added to the file. When using implicit authentication, set this to a value such as 'using-implicit-auth'. + +### secret +```yaml +secret: string +``` + +Provider-specific access secret. Can also be set via `ACCESS_SECRET` environment variable. Ensure the configuration file is secured if added to the file. When using implicit authentication, this option is not required. + +### prefix +```yaml +prefix: string +``` + +Optional object prefix. Object storage doesn't have the concept of a directory but a prefix is similar. For example, a prefix could be `base/dir`. This helps with organizing data in an object storage bucket. + +More can be found at the following links. + +- [Organizing objects using prefixes](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-prefixes.html) +- [libcloud container method documentation](https://libcloud.readthedocs.io/en/stable/storage/api.html#libcloud.storage.base.StorageDriver.iterate_container_objects) + +### host +```yaml +host: string +``` + +Optional server host name. Set when using a local cloud storage server. + +### port +```yaml +port: int +``` + +Optional server port. Set when using a local cloud storage server. + +### token +```yaml +token: string +``` + +Optional temporary session token + +### region +```yaml +region: string +``` + +Optional parameter to specify the storage region, provider-specific. + +## Hugging Face Hub configuration + +The huggingface-hub provider supports the following additional configuration parameters. More on these parameters can be found in the [Hugging Face Hub's documentation](https://huggingface.co/docs/huggingface_hub/main/en/package_reference/overview). + +### revision +```yaml +revision: string +``` + +Optional Git revision id which can be a branch name, a tag, or a commit hash + +### cache +```yaml +cache: string +``` + +Path to the folder where cached files are stored + +### token +```yaml +token: string|boolean +``` + +Token to be used for the download. If set to True, the token will be read from the Hugging Face config folder. diff --git a/docs/embeddings/configuration/database.md b/docs/embeddings/configuration/database.md new file mode 100644 index 0000000..5b8e967 --- /dev/null +++ b/docs/embeddings/configuration/database.md @@ -0,0 +1,83 @@ +# Database + +Databases store metadata, text and binary content. + +## content +```yaml +content: boolean|sqlite|duckdb|client|url|custom +``` + +Enables content storage. When true, the default storage engine, `sqlite` will be used to save metadata. + +Client-server connections are supported with either `client` or a full connection URL. When set to `client`, the CLIENT_URL environment variable must be set to the full connection URL. See the [SQLAlchemy](https://docs.sqlalchemy.org/en/20/core/engines.html#database-urls) documentation for more information on how to construct connection strings for client-server databases. + +Add custom storage engines via setting this parameter to the fully resolvable class string. + +Content storage specific settings are set with a corresponding configuration object having the same name as the content storage engine (i.e. duckdb or sqlite). These are optional and set to defaults if omitted. + +### client +```yaml +schema: default database schema for the session - defaults to being + determined by the database +``` + +Additional settings for client-server databases. Also supported when the `content=url`. + +### sqlite +```yaml +sqlite: + wal: enable write-ahead logging - allows concurrent read/write operations, + defaults to false +``` + +Additional settings for SQLite. + +## objects +```yaml +objects: boolean|image|pickle +``` + +Enables object storage. Supports storing binary content. Requires content storage to also be enabled. + +Object encoding options are: + +- `standard`: Default encoder when boolean set. Encodes and decodes objects as byte arrays. +- `image`: Image encoder. Encodes and decodes objects as image objects. +- `pickle`: Pickle encoder. Encodes and decodes objects with the pickle module. Supports arbitrary objects. + +## functions +```yaml +functions: list +``` + +List of functions with user-defined SQL functions. Each list element must be one of the following: + +- function +- callable object +- dict with fields for name, argcount, function and deterministic + +[An example can be found here](../../query#custom-sql-functions). + +## expressions +```yaml +expressions: list +``` + +List of expression shortcuts. Each list element must be a dict with the following fields. + +- `name`: name of the expression +- `expression`: SQL expression, defaults to `name` when empty +- `index`: if this expression should have a database index, defaults to False when not provided + +The expression can be a json data column, sql function or anything that can be run as a SQL snippet. + +## query +```yaml +query: + path: sets the path for the query model - this can be any model on the + Hugging Face Model Hub or a local file path. + prefix: text prefix to prepend to all inputs + maxlength: maximum generated sequence length +``` + +Query translation model. Translates natural language queries to txtai compatible SQL statements. diff --git a/docs/embeddings/configuration/general.md b/docs/embeddings/configuration/general.md new file mode 100644 index 0000000..7fe0e3a --- /dev/null +++ b/docs/embeddings/configuration/general.md @@ -0,0 +1,83 @@ +# General + +General configuration options. + +## keyword +```yaml +keyword: boolean|string +``` + +Enables sparse keyword indexing for this embeddings. + +When set to a boolean, this parameter creates a BM25 index for full text search. When set to a string, it expects a [keyword method](../scoring#method). + +It also implicitly disables the [defaults](#defaults) setting for vector search. + +## sparse +```yaml +sparse: boolean|path +``` + +Enables sparse vector indexing for this embeddings. + +When set to `True`, this parameter creates a sparse vector index using the [default sparse index model](https://huggingface.co/prithivida/Splade_PP_en_v2). When set to a string, it expects a local or Hugging Face model path. + +It also implicitly disables the [defaults](#defaults) setting for vector search. + +## dense +```yaml +dense: boolean|string +``` + +Alias for the [vector model path](../vectors/#path). When set to `True`, the [default transformers vector model](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2) is used. + +## hybrid +```yaml +hybrid: boolean +``` + +Enables hybrid (sparse + dense) indexing for this embeddings. + +When enabled, this parameter creates a BM25 index for full text search. It has no effect on the [defaults](#defaults) or [path](../vectors/#path) settings. + +## defaults +```yaml +defaults: boolean +``` + +Uses default vector model path when enabled (default setting is True) and `path` is not provided. See [this link](../) for an example. + +## indexes +```yaml +indexes: dict +``` + +Key value pairs defining subindexes for this embeddings. Each key is the index name and the value is the full configuration. This configuration can use any of the available configurations in a standard embeddings instance. + +## autoid +```yaml +format: int|uuid function +``` + +Sets the auto id generation method. When this is not set, an autogenerated numeric sequence is used. This also supports [UUID generation functions](https://docs.python.org/3/library/uuid.html#uuid.uuid1). For example, setting this value to `uuid4` will generate random UUIDs. Setting this to `uuid5` will generate deterministic UUIDs for each input data row. + +## columns +```yaml +columns: + text: name of the text column + object: name of the object column + store: limit json data fields to this list of columns +``` + +Sets the `text` and `object` column names. Defaults to `text` and `object` if not provided. + +`store` sets a list of columns to store in the JSON data field. When this isn't provided, all columns are stored (default). When `store` is set to `None`, no JSON columns are stored. This is useful is a field is only needed at indexing time but not search time. + +## format +```yaml +format: json|pickle +``` + +Sets the configuration storage format. Defaults to `json`. + +Note that `pickle` configuration is deprecated and will raise an error with the default settings. Its only kept for reading legacy indexes and requires setting the `ALLOW_PICKLE` environment variable. Only enable for local and/or trusted sources. diff --git a/docs/embeddings/configuration/graph.md b/docs/embeddings/configuration/graph.md new file mode 100644 index 0000000..63fc9b0 --- /dev/null +++ b/docs/embeddings/configuration/graph.md @@ -0,0 +1,80 @@ +# Graph + +Enable graph storage via the `graph` parameter. This component requires the [graph](../../../install/#graph) extras package. + +When enabled, a graph network is built using the embeddings index. Graph nodes are synced with each embeddings index operation (index/upsert/delete). Graph edges are created using the embeddings index upon completion of each index/upsert/delete embeddings index call. + +## backend +```yaml +backend: networkx|rdbms|custom +``` + +Sets the graph backend. Defaults to `networkx`. + +Add custom graph storage engines via setting this parameter to the fully resolvable class string. + +The `rdbms` backend has the following additional settings. + +### rdbms +```yaml +url: database url connection string, alternatively can be set via the + GRAPH_URL environment variable +schema: database schema to store graph - defaults to being + determined by the database +nodes: table to store node data, defaults to `nodes` +edges: table to store edge data, defaults to `edges` +``` + +## batchsize +```yaml +batchsize: int +``` + +Batch query size, used to query embeddings index - defaults to 256. + +## limit +```yaml +limit: int +``` + +Maximum number of results to return per embeddings query - defaults to 15. + +## minscore +```yaml +minscore: float +``` + +Minimum score required to consider embeddings query matches - defaults to 0.1. + +## approximate +```yaml +approximate: boolean +``` + +When true, queries only run for nodes without edges - defaults to true. + +## topics +```yaml +topics: + algorithm: community detection algorithm (string), options are + louvain (default), greedy, lpa + level: controls number of topics (string), options are best (default) or first + resolution: controls number of topics (int), larger values create more + topics (int), defaults to 100 + labels: scoring index method used to build topic labels (string) + options are bm25 (default), tfidf, sif + terms: number of frequent terms to use for topic labels (int), defaults to 4 + stopwords: optional list of stop words to exclude from topic labels + categories: optional list of categories used to group topics, allows + granular topics with broad categories grouping topics +``` + +Enables topic modeling. Defaults are tuned so that in most cases these values don't need to be changed (except for categories). These parameters are available for advanced use cases where one wants full control over the community detection process. + +## copyattributes +```yaml +copyattributes: boolean|list +``` + +Copy these attributes from input dictionaries in the `insert` method. If this is set to `True`, all attributes are copied. Otherwise, only the +attributes specified in this list are copied to the graph as attributes. diff --git a/docs/embeddings/configuration/index.md b/docs/embeddings/configuration/index.md new file mode 100644 index 0000000..be21a7c --- /dev/null +++ b/docs/embeddings/configuration/index.md @@ -0,0 +1,51 @@ +# Configuration + +The following describes available embeddings configuration. These parameters are set in the [Embeddings constructor](../methods#txtai.embeddings.base.Embeddings.__init__) via either the `config` parameter or as keyword arguments. + +Configuration is designed to be optional and set only when needed. Out of the box, sensible defaults are picked to get up and running fast. For example: + +```python +from txtai import Embeddings + +embeddings = Embeddings() +``` + +Creates a new embeddings instance, using [all-MiniLM-L6-v2](https://hf.co/sentence-transformers/all-MiniLM-L6-v2) as the vector model, [Faiss](https://faiss.ai/) as the ANN index backend and content disabled. + +```python +from txtai import Embeddings + +embeddings = Embeddings(content=True) +``` + +Is the same as above except it adds in [SQLite](https://www.sqlite.org/index.html) for content storage. + +The following sections link to all the available configuration options. + +## [ANN](./ann) + +The default vector index backend is Faiss. + +## [Cloud](./cloud) + +Embeddings databases can optionally be synced with cloud storage. + +## [Database](./database) + +Content storage is disabled by default. When enabled, SQLite is the default storage engine. + +## [General](./general) + +General configuration that doesn't fit elsewhere. + +## [Graph](./graph) + +An accomplying graph index can be created with an embeddings database. This enables topic modeling, path traversal and more. [NetworkX](https://github.com/networkx/networkx) is the default graph index. + +## [Scoring](./scoring) + +Sparse keyword indexing and word vectors term weighting. + +## [Vectors](./vectors) + +Vector search is enabled by converting text and other binary data into embeddings vectors. These vectors are then stored in an ANN index. The vector model is optional and a default model is used when not provided. diff --git a/docs/embeddings/configuration/scoring.md b/docs/embeddings/configuration/scoring.md new file mode 100644 index 0000000..03dbae7 --- /dev/null +++ b/docs/embeddings/configuration/scoring.md @@ -0,0 +1,109 @@ +# Scoring + +Enable scoring support via the `scoring` parameter. + +This scoring instance can serve two purposes, depending on the settings. + +One use case is building sparse/keyword indexes. This occurs when the `terms` parameter is set to `True`. + +The other use case is with word vector term weighting. This feature has been available since the initial version but isn't quite as common anymore. + +The following covers the available options. + +## method +```yaml +method: bm25|tfidf|sif|pgtext|sparse|custom +``` + +Sets the scoring method. Add custom scoring via setting this parameter to the fully resolvable class string. + +### pgtext +```yaml +schema: database schema to store keyword index - defaults to being + determined by the database +``` + +Additional settings for Postgres full-text keyword indexes. + +### sparse +```yaml +path: sparse vector model path +vectormethod: vector embeddings method +vectornormalize: enable vector embeddings normalization (boolean) +gpu: boolean|int|string|device +normalize: enable score normalization (boolean|float|string|dict) +batch: Sets the transform batch size +encodebatch: Sets the encode batch size +vectors: additional model init args +encodeargs: additional encode() args +backend: ivfsparse|pgsparse +``` + +Sparse vector scoring options. The sparse scoring instance combines a sparse vector model with a sparse approximate nearest neighbor index (ANN). This method supports both vector normalization and score normalization. + +Vector normalization normalizes all vectors to have a magnitude of 1. By extension, all generated scores will be 0 to 1. + +Score normalization scales the output between 0 and 1. This setting supports: + +- `True` for default scale normalization +- `float` normalize using this as the scale factor +- `"bayes"` for Bayesian normalization using dynamic candidate score statistics +- `{method: "bayes", alpha: 1.0, beta: null}` for Bayesian normalization with optional custom parameters + +#### ivfsparse +```yaml +ivfsparse: + sample: percent of data to use for model training (0.0 - 1.0) + nfeatures: top n features to use for model training (int) + nlist: desired number of clusters (int) + nprobe: search probe setting (int) + minpoints: minimum number of points for a cluster (int) +``` + +Inverted file (IVF) index with flat vector file storage and sparse array support. + +#### pgsparse + +Sparse ANN backed by Postgres. Supports same options as the [pgvector](../ann/#pgvector) ANN. + +## terms +```yaml +terms: boolean|dict +``` + +Enables term frequency sparse arrays for a scoring instance. This is the backend for sparse keyword indexes. + +Supports a `dict` with the parameters `cachelimit` and `cutoff`. + +`cachelimit` is the maximum amount of resident memory in bytes to use during indexing before flushing to disk. This parameter is an `int`. + +`cutoff` is used during search to determine what constitutes a common term. This parameter is a `float`, i.e. 0.1 for a cutoff of 10%. + +When `terms` is set to `True`, default parameters are used for the `cachelimit` and `cutoff`. Normally, these defaults are sufficient. + +## normalize +```yaml +normalize: boolean|str|dict +``` + +Enables normalized scoring (ranging from 0 to 1). This setting supports: + +- `True` for standard score normalization +- `"bayes"` | `"bb25"` for Bayesian normalization using dynamic candidate score statistics +- `{method: "bayes", alpha: 1.0, beta: null}` for Bayesian normalization with optional custom parameters + +When standard normalization is enabled, statistics from the index are used to calculate normalized scores. +When Bayesian/BB25 normalization is enabled, it uses positive-score candidates, dynamic `beta=median(scores)`, adaptive +`alpha_eff=alpha/std(scores)` and a sigmoid transform (likelihood-only variant with flat prior) to map scores to `[0, 1]`. + +Bayesian normalization references: + +- [https://github.com/instructkr/bb25](https://github.com/instructkr/bb25) +- [https://github.com/cognica-io/bayesian-bm25](https://github.com/cognica-io/bayesian-bm25) + +## tokenizer +```yaml +tokenizer: dict +``` + +Set tokenization rules. Passes these arguments to the underlying [Tokenization pipeline](../../../pipeline/data/tokenizer#txtai.pipeline.Tokenizer.__init__). diff --git a/docs/embeddings/configuration/vectors.md b/docs/embeddings/configuration/vectors.md new file mode 100644 index 0000000..da13dd8 --- /dev/null +++ b/docs/embeddings/configuration/vectors.md @@ -0,0 +1,161 @@ +# Vectors + +The following covers available vector model configuration options. + +## path +```yaml +path: string +``` + +Sets the path for a vectors model. When using a transformers/sentence-transformers model, this can be any model on the +[Hugging Face Hub](https://huggingface.co/models) or a local file path. Otherwise, it must be a local file path to a word embeddings model. + +## method +```yaml +method: transformers|sentence-transformers|llama.cpp|litellm|model2vec|external| + words +``` + +Embeddings method to use. If the method is not provided, it is inferred using the `path`. + +`sentence-transformers`, `llama.cpp`, `litellm`, `model2vec` and `words` require the [vectors](../../../install/#vectors) extras package to be installed. + +### transformers + +Builds embeddings using a transformers model. While this can be any transformers model, it works best with +[models trained](https://huggingface.co/models?pipeline_tag=sentence-similarity) to build embeddings. + +`mean`, `cls`, `last` and `late` pooling are supported and automatically inferred from the model. The pooling method can be overwritten by changing the method +from `transformers` to `meanpooling`, `clspooling`, `lastpooling` or `latepooling` respectively. + +Setting `maxlength` to `True` enables truncating inputs to the `max_seq_length`. Setting `maxlength` to an integer will truncate inputs to that value. When omitted (default), the `maxlength` will be set to either the model or tokenizer maxlength. + +Supports loading ONNX models directly. [See this section for more](../../../examples/#model-training). + +### sentence-transformers + +Same as transformers but loads models with the [sentence-transformers](https://github.com/UKPLab/sentence-transformers) library. + +### llama.cpp + +Builds embeddings using a [llama.cpp](https://github.com/abetlen/llama-cpp-python) model. Supports both local and remote GGUF paths on the HF Hub. + +### litellm + +Builds embeddings using a LiteLLM model. See the [LiteLLM documentation](https://litellm.vercel.app/docs/providers) for the options available with LiteLLM models. + +### model2vec + +Builds embeddings using a [Model2Vec](https://github.com/MinishLab/model2vec) model. Model2Vec is a knowledge-distilled version of a transformers model with static vectors. + +### words + +Builds embeddings using a word embeddings model and static vectors. While Transformers models are preferred in most cases, this method can be useful for low resource and historical languages where there isn't much linguistic data available. + +#### pca +```yaml +pca: int +``` + +Removes _n_ principal components from generated embeddings. When enabled, a TruncatedSVD model is built to help with dimensionality reduction. After pooling of vectors creates a single embedding, this method is applied. + +### external + +Embeddings are created via an external model or API. Requires setting the [transform](#transform) parameter to a function that translates data into embeddings. + +#### transform +```yaml +transform: function +``` + +When method is `external`, this function transforms input content into embeddings. The input to this function is a list of data. This method must return either a numpy array or list of numpy arrays. + +## gpu +```yaml +gpu: boolean|int|string|device +``` + +Set the target device. Supports true/false, device id, device string and torch device instance. This is automatically derived if omitted. + +The `sentence-transformers` method supports encoding with multiple GPUs. This can be enabled by setting the gpu parameter to `all`. + +## batch +```yaml +batch: int +``` + +Sets the transform batch size. This parameter controls how input streams are chunked and vectorized. + +## encodebatch +```yaml +encodebatch: int +``` + +Sets the encode batch size. This parameter controls the underlying vector model batch size. This often corresponds to a GPU batch size, which controls GPU memory usage. + +## dimensionality +```yaml +dimensionality: int +``` + +Enables truncation of vectors to this dimensionality. This is only useful for models trained to store more important information in earlier dimensions such as [Matryoshka Representation Learning (MRL)](https://huggingface.co/blog/matryoshka). + +## quantize +```yaml +quantize: int|boolean +``` + +Enables scalar vector quantization at the specified precision. Supports 1-bit through 8-bit quantization. Scalar quantization transforms continuous floating point values to discrete unsigned integers. The `faiss`, `pgvector`, `numpy` and `torch` ANN backends support storing these vectors. + +This parameter supports booleans for backwards compatability. When set to true/false, this flag sets [faiss.quantize](../ann/#faiss). + +In addition to vector-level quantization, some ANN backends have the ability to quantize vectors at the storage layer. See the [ANN](../ann) configuration options for more. + +## instructions +```yaml +instructions: + query: prefix for queries + data: prefix for indexing +``` + +Instruction-based models use prefixes to modify how embeddings are computed. This is especially useful with asymmetric search, which is when the query and indexed data are of vastly different lengths. In other words, short queries with long documents. + +`txtai` automatically loads prompts stored in `config_sentence_transformers.json` except if this parameter is set. For some older models such as [E5-base](https://huggingface.co/intfloat/e5-base), instructions still need to be provided via this parameter. + +## models +```yaml +models: dict +``` + +Loads and stores vector models in this cache. This is primarily used with subindexes but can be set on any embeddings instance. This prevents the same model from being loaded multiple times when working with multiple embeddings instances. + +## tokenize +```yaml +tokenize: boolean +``` + +Enables string tokenization (defaults to false). This method applies tokenization rules that only work with English language text. It's not recommended for use with recent vector models. + +## vectors +```yaml +vectors: dict +``` + +Passes these additional parameters to the underlying vector model. + +### muvera +```yaml +muvera: + repetitions: defaults 20 + hashes: defaults to 5 + projection: defaults 16 +``` + +Settings to control the size of MUVERA fixed dimensional outputs. Default is 20 * 2^5 * 16 = 10,240 dimensions. + +### trust_remote_code +```yaml +trust_remote_code: boolean +``` + +Parameter for trusting the code from Hugging Face models with custom implementations. diff --git a/docs/embeddings/format.md b/docs/embeddings/format.md new file mode 100644 index 0000000..fc1ade2 --- /dev/null +++ b/docs/embeddings/format.md @@ -0,0 +1,59 @@ +# Index format + +![format](../images/format.png#only-light) +![format](../images/format-dark.png#only-dark) + +This section documents the txtai index format. Each component is designed to ensure open access to the underlying data in a programmatic and platform independent way + +If an underlying library has an index format, that is used. Otherwise, txtai persists content with [MessagePack](https://msgpack.org/index.html) serialization. + +To learn more about how these components work together, read the [Index Guide](../indexing) and [Query Guide](../query). + +## ANN + +Approximate Nearest Neighbor (ANN) index configuration for storing vector embeddings. + +| Component | Storage Format | +| ------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| [Faiss](https://github.com/facebookresearch/faiss) | Local file format provided by library | +| [Hnswlib](https://github.com/nmslib/hnswlib) | Local file format provided by library | +| [Annoy](https://github.com/spotify/annoy) | Local file format provided by library | +| [NumPy](https://github.com/numpy/numpy) | Local NumPy array files via np.save / np.load | +| [Postgres via pgvector](https://github.com/pgvector/pgvector) | Vector tables in a Postgres database | + +## Core + +Core embeddings index files. + +| Component | Storage Format | +| ------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| [Configuration](https://www.json.org/) | Embeddings index configuration stored as JSON | +| [Index Ids](https://msgpack.org/index.html) | Embeddings index ids serialized with MessagePack. Only enabled when when content storage (database) is disabled. | + +## Database + +Databases store metadata, text and binary content. + +| Component | Storage Format | +| ------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| [SQLite](https://www.sqlite.org/) | Local database files with SQLite | +| [DuckDB](https://github.com/duckdb/duckdb) | Local database files with DuckDB | +| [Postgres](https://www.postgresql.org/) | Postgres relational database via [SQLAlchemy](https://github.com/sqlalchemy/sqlalchemy). Supports additional databases via this library. | + +## Graph + +Graph nodes and edges for an embeddings index + +| Component | Storage Format | +| ------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| [NetworkX](https://github.com/networkx/networkx) | Nodes and edges exported to local file serialized with MessagePack | +| [Postgres](https://github.com/aplbrain/grand) | Nodes and edges stored in a Postgres database. Supports additional databases. | + +## Scoring + +Sparse/keyword indexing + +| Component | Storage Format | +| ------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| [Local index](https://www.sqlite.org/) | Metadata serialized with MessagePack. Terms stored in SQLite. | +| [Postgres](https://www.postgresql.org/docs/current/textsearch.html) | Text indexed with Postgres Full Text Search (FTS) | diff --git a/docs/embeddings/index.md b/docs/embeddings/index.md new file mode 100644 index 0000000..40145e9 --- /dev/null +++ b/docs/embeddings/index.md @@ -0,0 +1,122 @@ +# Embeddings + +![embeddings](../images/embeddings.png#only-light) +![embeddings](../images/embeddings-dark.png#only-dark) + +Embeddings databases are the engine that delivers semantic search. Data is transformed into embeddings vectors where similar concepts will produce similar vectors. Indexes both large and small are built with these vectors. The indexes are used to find results that have the same meaning, not necessarily the same keywords. + +The following code snippet shows how to build and search an embeddings index. + +```python +from txtai import Embeddings + +# Create embeddings model, backed by sentence-transformers & transformers +embeddings = Embeddings(path="sentence-transformers/nli-mpnet-base-v2") + +data = [ + "US tops 5 million confirmed virus cases", + "Canada's last fully intact ice shelf has suddenly collapsed, " + + "forming a Manhattan-sized iceberg", + "Beijing mobilises invasion craft along coast as Taiwan tensions escalate", + "The National Park Service warns against sacrificing slower friends " + + "in a bear attack", + "Maine man wins $1M from $25 lottery ticket", + "Make huge profits without work, earn up to $100,000 a day" +] + +# Index the list of text +embeddings.index(data) + +print(f"{'Query':20} Best Match") +print("-" * 50) + +# Run an embeddings search for each query +for query in ("feel good story", "climate change", "public health story", "war", + "wildlife", "asia", "lucky", "dishonest junk"): + # Extract uid of first result + # search result format: (uid, score) + uid = embeddings.search(query, 1)[0][0] + + # Print text + print(f"{query:20} {data[uid]}") +``` + +## Build + +An embeddings instance is [configuration-driven](configuration) based on what is passed in the constructor. Vectors are stored with the option to also [store content](configuration/database#content). Content storage enables additional filtering and data retrieval options. + +The example above sets a specific embeddings vector model via the [path](configuration/vectors/#path) parameter. An embeddings instance with no configuration can also be created. + +```python +embeddings = Embeddings() +``` + +In this case, when loading and searching for data, the [default transformers vector model](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2) is used to vectorize data. See the [model guide](../models) for current model recommentations. + +## Index + +After creating a new embeddings instance, the next step is adding data to it. + +```python +embeddings.index(rows) +``` + +The index method takes an iterable and supports the following formats for each element. + +- `(id, data, tags)` - default processing format + +| Element | Description | +| ----------- | ------------------------------------------------------------- | +| id | unique record id | +| data | input data to index, can be text, a dictionary or object | +| tags | optional tags string, used to mark/label data as it's indexed | + +- `(id, data)` + + Same as above but without tags. + +- `data` + +Single element to index. In this case, unique id's will automatically be generated. Note that for generated id's, [upsert](methods/#txtai.embeddings.base.Embeddings.upsert) and [delete](methods/#txtai.embeddings.base.Embeddings.delete) calls require a separate search to get the target ids. + +When the data field is a dictionary, text is passed via the `text` key, binary objects via the `object` key. Note that [content](configuration/database#content) must be enabled to store metadata and [objects](configuration/database#objects) to store binary object data. The `id` and `tags` keys will be extracted, if provided. + +The input iterable can be a list or generator. [Generators](https://wiki.python.org/moin/Generators) help with indexing very large datasets as only portions of the data is in memory at any given time. + +More information on indexing can be found in the [index guide](indexing). + +## Search + +Once data is indexed, it is ready for search. + +```python +embeddings.search(query, limit) +``` + +The search method takes two parameters, the query and query limit. The results format is different based on whether [content](configuration/database#content) is stored or not. + +- List of `(id, score)` when content is _not_ stored +- List of `{**query columns}` when content is stored + +Both natural language and SQL queries are supported. More information can be found in the [query guide](query). + +## Resource management + +Embeddings databases are context managers. The following blocks automatically [close](methods/#txtai.embeddings.base.Embeddings.close) and free resources upon completion. + +```python +# Create a new Embeddings database, index data and save +with Embeddings() as embeddings: + embeddings.index(rows) + embeddings.save(path) + +# Search a saved Embeddings database +with Embeddings().load(path) as embeddings: + embeddings.search(query) +``` + +While calling `close` isn't always necessary (resources will be garbage collected), it's best to free shared resources like database connections as soon as they aren't needed. + +## More examples + +See [this link](../examples/#semantic-search) for a full list of embeddings examples. \ No newline at end of file diff --git a/docs/embeddings/indexing.md b/docs/embeddings/indexing.md new file mode 100644 index 0000000..6e9232e --- /dev/null +++ b/docs/embeddings/indexing.md @@ -0,0 +1,136 @@ +# Index guide + +![indexing](../images/indexing.png#only-light) +![indexing](../images/indexing-dark.png#only-dark) + +This section gives an in-depth overview on how to index data with txtai. We'll cover vectorization, indexing/updating/deleting data and the various components of an embeddings database. + +## Vectorization + +The most compute intensive step in building an index is vectorization. The [path](../configuration/vectors#path) parameter sets the path to the vector model. There is logic to automatically detect the vector model [method](../configuration/vectors#method) but it can also be set directly. + +The [batch](../configuration/vectors#batch) and [encodebatch](../configuration/vectors#encodebatch) parameters control the vectorization process. Larger values for `batch` will pass larger batches to the vectorization method. Larger values for `encodebatch` will pass larger batches for each vector encode call. In the case of GPU vector models, larger values will consume more GPU memory. + +Data is buffered to temporary storage during indexing as embeddings vectors can be quite large (for example 768 dimensions of float32 is 768 * 4 = 3072 bytes per vector). Once vectorization is complete, a mmapped array is created with all vectors for [Approximate Nearest Neighbor (ANN)](../configuration/vectors#backend) indexing. + +The terms `ANN` and `dense vector index` are used interchangeably throughout txtai's documentation. + +## Setting a backend + +As mentioned above, computed vectors are stored in an ANN. There are various index [backends](../configuration/ann#backend) that can be configured. Faiss is the default backend. + +## Content storage + +Embeddings indexes can optionally [store content](../configuration/database#content). When this is enabled, the input content is saved in a database alongside the computed vectors. This enables filtering on additional fields and content retrieval. + +The columns used for text, object and JSON data storage are set via [column configuration](../configuration/general#columns). + +## Index vs Upsert + +Data is loaded into an index with either an [index](../methods#txtai.embeddings.base.Embeddings.index) or [upsert](../methods#txtai.embeddings.base.Embeddings.upsert) call. + +```python +embeddings.index([(uid, text, None) for uid, text in enumerate(data)]) +embeddings.upsert([(uid, text, None) for uid, text in enumerate(data)]) +``` + +The `index` call will build a brand new index replacing an existing one. `upsert` will insert or update records. `upsert` ops do _not_ require a full index rebuild. + +## Save + +Indexes can be stored in a directory using the [save](../methods/#txtai.embeddings.base.Embeddings.save) method. + +```python +embeddings.save("/path/to/save") +``` + +Compressed indexes are also supported. + +```python +embeddings.save("/path/to/save/index.tar.gz") +``` + +In addition to saving indexes locally, they can also be persisted to [cloud storage](../configuration/cloud). + +```python +embeddings.save("/path/to/save/index.tar.gz", cloud={...}) +``` + +This is especially useful when running in a serverless context or otherwise running on temporary compute. Cloud storage is only supported with compressed indexes. + +Embeddings indexes can be restored using the [load](../methods/#txtai.embeddings.base.Embeddings.load) method. + +```python +embeddings.load("/path/to/load") +``` + +## Delete + +Content can be removed from the index with the [delete](../methods#txtai.embeddings.base.Embeddings.delete) method. This method takes a list of ids to delete. + +```python +embeddings.delete(ids) +``` + +## Reindex + +When [content storage](../configuration/database#content) is enabled, [reindex](../methods#txtai.embeddings.base.Embeddings.reindex) can be called to rebuild the index with new settings. For example, the backend can be switched from faiss to hnsw or the vector model can be updated. This prevents having to go back to the original raw data. + +```python +embeddings.reindex(path="sentence-transformers/all-MiniLM-L6-v2", backend="hnsw") +``` + +## Graph + +Enabling a [graph network](../configuration/graph) adds a semantic graph at index time as data is being vectorized. Vector embeddings are used to automatically create relationships in the graph. Relationships can also be manually specified at index time. + +```python +# Manual relationships by id +embeddings.index([{"id": "0", "text": "...", "relationships": ["2"]}]) + +# Manual relationships with additional edge attributes +embeddings.index(["id": "0", "text": "...", "relationships": [ + {"id": "2", "type": "MEMBER_OF"} +]]) +``` + +Additionally, graphs can be used for topic modeling. Dimensionality reduction with UMAP combined with HDBSCAN is a popular topic modeling method found in a number of libraries. txtai takes a different approach using community detection algorithms to build topic clusters. + +This approach has the advantage of only having to vectorize data once. It also has the advantage of better topic precision given there isn't a dimensionality reduction operation (UMAP). Semantic graph examples are shown below. + +Get a mapping of discovered topics to associated ids. + +```python +embeddings.graph.topics +``` + +Show the most central nodes in the index. + +```python +embeddings.graph.centrality() +``` + +Graphs are persisted alongside an embeddings index. Each save and load will also save and load the graph. + +## Sparse vectors + +Scoring instances can create a standalone [sparse keyword indexes](../configuration/general#keyword) (BM25, TF-IDF) and [sparse vector indexes](../configuration/general#sparse) (SPLADE). This enables [hybrid](../configuration/general/#hybrid) search when there is an available dense vector index. + +The terms `sparse vector index`, `keyword index`, `terms index` and `scoring index` are used interchangeably throughout txtai's documentation. + +See [this link](../../examples/#semantic-search) to learn more. + +## Subindexes + +An embeddings instance can optionally have associated [subindexes](../configuration/general/#indexes), which are also embeddings databases. This enables indexing additional fields, vector models and much more. + +## Word vectors + +When using [word vector backed models](../configuration/vectors#words) with scoring set, a separate call is required before calling `index` as follows: + +```python +embeddings.score(rows) +embeddings.index(rows) +``` + +Both calls are required to support generator-backed iteration of data with word vectors models. diff --git a/docs/embeddings/methods.md b/docs/embeddings/methods.md new file mode 100644 index 0000000..4fd6a1b --- /dev/null +++ b/docs/embeddings/methods.md @@ -0,0 +1,20 @@ +# Methods + +::: txtai.embeddings.Embeddings + options: + filters: + - "!columns" + - "!createann" + - "!createcloud" + - "!createdatabase" + - "!creategraph" + - "!createids" + - "!createindexes" + - "!createscoring" + - "!checkarchive" + - "!configure" + - "!defaultallowed" + - "!defaults" + - "!initindex" + - "!loadquery" + - "!loadvectors" diff --git a/docs/embeddings/query.md b/docs/embeddings/query.md new file mode 100644 index 0000000..6b5e2ca --- /dev/null +++ b/docs/embeddings/query.md @@ -0,0 +1,285 @@ +# Query guide + +![query](../images/query.png#only-light) +![query](../images/query-dark.png#only-dark) + +This section covers how to query data with txtai. The simplest way to search for data is building a natural language string with the desired content to find. txtai also supports querying with SQL. We'll cover both methods here. + +## Natural language queries + +In the simplest case, the query is text and the results are index text that is most similar to the query text. + +```python +embeddings.search("feel good story") +embeddings.search("wildlife") +``` + +The queries above [search](../methods#txtai.embeddings.base.Embeddings.search) the index for similarity matches on `feel good story` and `wildlife`. If content storage is enabled, a list of `{**query columns}` is returned. Otherwise, a list of `(id, score)` tuples are returned. + +## SQL + +txtai supports more complex queries with SQL. This is only supported if [content storage](../configuration/database#content) is enabled. txtai has a translation layer that analyzes input SQL statements and combines similarity results with content stored in a relational database. + +SQL queries are run through `embeddings.search` like natural language queries but the examples below only show the SQL query for conciseness. + +```python +embeddings.search("SQL query") +``` + +### Similar clause + +The similar clause is a txtai function that enables similarity searches with SQL. + +```sql +SELECT id, text, score FROM txtai WHERE similar('feel good story') +``` + +The similar clause takes the following arguments: + +```sql +similar("query", "number of candidates", "index", "weights") +``` + +| Argument | Description | +| --------------------- | ---------------------------------------| +| query | natural language query to run | +| number of candidates | number of candidate results to return | +| index | target index name | +| weights | hybrid score weights | + +The txtai query layer joins results from two separate components, a relational store and a similarity index. With a similar clause, a similarity search is run and those ids are fed to the underlying database query. + +The number of candidates should be larger than the desired number of results when applying additional filter clauses. This ensures that `limit` results are still returned after applying additional filters. If the number of candidates is not specified, it is defaulted as follows: + +- For a single query filter clause, the default is the query limit +- With multiple filtering clauses, the default is 10x the query limit + +The index name is only applicable when [subindexes](../configuration/general/#indexes) are enabled. This specifies the index to use for the query. + +Weights sets the hybrid score weights when an index has both a sparse and dense index. + +### Dynamic columns + +Content can be indexed in multiple ways when content storage is enabled. [Remember that input documents](../#index) take the form of `(id, data, tags)` tuples. If data is a string or binary content, it's indexed and searchable with `similar()` clauses. + +If data is a dictionary, then all fields in the dictionary are stored and available via SQL. The `text` field or [field specified in the index configuration](../configuration/general/#columns) is indexed and searchable with `similar()` clauses. + +For example: + +```python +embeddings.index([{"text": "text to index", "flag": True, + "actiondate": "2022-01-01"}]) +``` + +With the above input data, queries can now have more complex filters. + +```sql +SELECT text, flag, actiondate FROM txtai WHERE similar('query') AND flag = 1 +AND actiondate >= '2022-01-01' +``` + +txtai's query layer automatically detects columns and translates queries into a format that can be understood by the underlying database. + +Nested dictionaries/JSON is supported and can be escaped with bracket statements. + +```python +embeddings.index([{"text": "text to index", + "parent": {"child element": "abc"}}]) +``` + +```sql +SELECT text FROM txtai WHERE [parent.child element] = 'abc' +``` + +Note the bracket statement escaping the nested column with spaces in the name. + +### Bind parameters + +txtai has support for SQL bind parameters. + +```python +# Query with a bind parameter for similar clause +query = "SELECT id, text, score FROM txtai WHERE similar(:x)" +results = embeddings.search(query, parameters={"x": "feel good story"}) + +# Query with a bind parameter for column filter +query = "SELECT text, flag, actiondate FROM txtai WHERE flag = :x" +results = embeddings.search(query, parameters={"x": 1}) +``` + +### Aggregation queries + +The goal of txtai's query language is to closely support all functions in the underlying database engine. The main challenge is ensuring dynamic columns are properly escaped into the engines native query function. + +Aggregation query examples. + +```sql +SELECT count(*) FROM txtai WHERE similar('feel good story') AND score >= 0.15 +SELECT max(length(text)) FROM txtai WHERE similar('feel good story') +AND score >= 0.15 +SELECT count(*), flag FROM txtai GROUP BY flag ORDER BY count(*) DESC +``` + +## Binary objects + +txtai has support for storing and retrieving binary objects. Binary objects can be retrieved as shown in the example below. + +```python +# Create embeddings index with content and object storage enabled +embeddings = Embeddings(content=True, objects=True) + +# Get an image +request = open("demo.gif", "rb") + +# Insert record +embeddings.index([( + "txtai", + {"text": "txtai executes machine-learning workflows.", + "object": request.read()} +)]) + +# Query txtai and get associated object +query = "SELECT object FROM txtai WHERE similar('machine learning') LIMIT 1" +result = embeddings.search(query)[0]["object"] + +# Query binary content with a bind parameter +query = "SELECT object FROM txtai WHERE similar(:x) LIMIT 1" +results = embeddings.search(query, parameters={"x": request.read()}) +``` + +## Custom SQL functions + +Custom, user-defined SQL functions extend selection, filtering and ordering clauses with additional logic. For example, the following snippet defines a function that translates text using a translation pipeline. + +```python +# Translation pipeline +translate = Translation() + +# Create embeddings index +embeddings = Embeddings(path="sentence-transformers/nli-mpnet-base-v2", + content=True, + functions=[translate]}) + +# Run a search using a custom SQL function +embeddings.search(""" +SELECT + text, + translation(text, 'de', null) 'text (DE)', + translation(text, 'es', null) 'text (ES)', + translation(text, 'fr', null) 'text (FR)' +FROM txtai WHERE similar('feel good story') +LIMIT 1 +""") +``` + +## Expressions + +Expression shortcuts expand into more complex SQL snippets. This is useful for making SQL queries more concise. Indexing is also available on expressions as a performance improvement. + +The following example indexes a json extraction field (`filepath`) and the length of each field. + +```python +# Create embeddings index +embeddings = Embeddings( + path="sentence-transformers/nli-mpnet-base-v2", + content=True, + expressions=[ + {"name": "filepath", "index": True}, + {"name": "textlength", "expression": "length(text)", "index": True} + ] +) + +embeddings.search("SELECT textlength, filepath FROM txtai LIMIT 1") +``` + +## Query translation + +Natural language queries with filters can be converted to txtai-compatible SQL statements with query translation. For example: + +```python +embeddings.search("feel good story since yesterday") +``` + +can be converted to a SQL statement with a similar clause and date filter. + +```sql +select id, text, score from txtai where similar('feel good story') and +entry >= date('now', '-1 day') +``` + +This requires setting a [query translation model](../configuration/database#query). The default query translation model is [t5-small-txtsql](https://huggingface.co/NeuML/t5-small-txtsql) but this can easily be finetuned to handle different use cases. + +## Hybrid search + +When an embeddings database has both a sparse and dense index, both indexes will be queried and the results will be equally weighted unless otherwise specified. + +```python +embeddings.search("query", weights=0.5) +embeddings.search( + "SELECT id, text, score FROM txtai WHERE similar('query', 0.5)" +) +``` + +## Graph search + +If an embeddings database has an associated graph network, graph searches can be run. The search syntax below uses [openCypher](https://github.com/opencypher/openCypher). Follow the preceding link to learn more about this syntax. + +Additionally, standard embeddings searches can be returned as graphs. + +```python +# Find all paths between id: 0 and id: 5 between 1 and 3 hops away +embeddings.search(""" +MATCH P=({id: 0})-[*1..3]->({id: 5}) +RETURN P +""") + +# Find related nodes for query matches +embeddings.search(""" + MATCH P=(A)-[]->(B) + WHERE SIMILAR(A, "query") + RETURN B + ORDER BY A.score DESC + LIMIT 10 +""") + +# Standard embeddings search as graph +embeddings.search("query", graph=True) +``` + +## Subindexes + +Subindexes can be queried as follows: + +```python +# Build index with subindexes +embeddings = Embeddings( + content=True, + defaults=False, + indexes={ + "keyword": { + "keyword": True + }, + "dense":{ + "dense": True + } + } +) +embeddings.index(stream()) + +# Query with index parameter +embeddings.search("query", index="keyword") + +# Specify with SQL +embeddings.search(""" +SELECT id, text, score FROM txtai +WHERE similar('query', 'keyword') +""") +``` + +## Combined index architecture + +txtai has multiple storage and indexing components. Content is stored in an underlying database along with an approximate nearest neighbor (ANN) index, keyword index and graph network. These components combine to deliver similarity search alongside traditional structured search. + +The ANN index stores ids and vectors for each input element. When a natural language query is run, the query is translated into a vector and a similarity query finds the best matching ids. When a database is added into the mix, an additional step is executed. This step takes those ids and effectively inserts them as part of the underlying database query. The same steps apply with keyword indexes except a term frequency index is used to find the best matching ids. + +Dynamic columns are supported via the underlying engine. For SQLite, data is stored as JSON and dynamic columns are converted into `json_extract` clauses. Client-server databases are supported via [SQLAlchemy](https://docs.sqlalchemy.org/en/20/dialects/) and dynamic columns are supported provided the underlying engine has [JSON](https://docs.sqlalchemy.org/en/20/core/type_basics.html#sqlalchemy.types.JSON) support. diff --git a/docs/examples.md b/docs/examples.md new file mode 100644 index 0000000..c83ab97 --- /dev/null +++ b/docs/examples.md @@ -0,0 +1,163 @@ +# Examples + +![examples](images/examples.png#only-light) +![examples](images/examples-dark.png#only-dark) + +See below for a comprehensive series of example notebooks and applications covering txtai. + +## Semantic Search + +Build semantic/similarity/vector/neural search applications. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Introducing txtai](https://github.com/neuml/txtai/blob/master/examples/01_Introducing_txtai.ipynb) [▶️](https://www.youtube.com/watch?v=SIezMnVdmMs) | Overview of the functionality provided by txtai | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/01_Introducing_txtai.ipynb) | +| [Build an Embeddings index with Hugging Face Datasets](https://github.com/neuml/txtai/blob/master/examples/02_Build_an_Embeddings_index_with_Hugging_Face_Datasets.ipynb) | Index and search Hugging Face Datasets | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/02_Build_an_Embeddings_index_with_Hugging_Face_Datasets.ipynb) | +| [Build an Embeddings index from a data source](https://github.com/neuml/txtai/blob/master/examples/03_Build_an_Embeddings_index_from_a_data_source.ipynb) | Index and search a data source with word embeddings | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/03_Build_an_Embeddings_index_from_a_data_source.ipynb) | +| [Add semantic search to Elasticsearch](https://github.com/neuml/txtai/blob/master/examples/04_Add_semantic_search_to_Elasticsearch.ipynb) | Add semantic search to existing search systems | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/04_Add_semantic_search_to_Elasticsearch.ipynb) | +| [Similarity search with images](https://github.com/neuml/txtai/blob/master/examples/13_Similarity_search_with_images.ipynb) | Embed images and text into the same space for search | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/13_Similarity_search_with_images.ipynb) | +| [Custom Embeddings SQL functions](https://github.com/neuml/txtai/blob/master/examples/30_Embeddings_SQL_custom_functions.ipynb) | Add user-defined functions to Embeddings SQL | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/30_Embeddings_SQL_custom_functions.ipynb) | +| [Model explainability](https://github.com/neuml/txtai/blob/master/examples/32_Model_explainability.ipynb) | Explainability for semantic search | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/32_Model_explainability.ipynb) | +| [Query translation](https://github.com/neuml/txtai/blob/master/examples/33_Query_translation.ipynb) | Domain-specific natural language queries with query translation | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/33_Query_translation.ipynb) | +| [Build a QA database](https://github.com/neuml/txtai/blob/master/examples/34_Build_a_QA_database.ipynb) | Question matching with semantic search | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/34_Build_a_QA_database.ipynb) | +| [Semantic Graphs](https://github.com/neuml/txtai/blob/master/examples/38_Introducing_the_Semantic_Graph.ipynb) | Explore topics, data connectivity and run network analysis| [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/38_Introducing_the_Semantic_Graph.ipynb) | +| [Topic Modeling with BM25](https://github.com/neuml/txtai/blob/master/examples/39_Classic_Topic_Modeling_with_BM25.ipynb) | Topic modeling backed by a BM25 index | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/39_Classic_Topic_Modeling_with_BM25.ipynb) | + +## LLM + +Autonomous agents, retrieval augmented generation (RAG), chat with your data, pipelines and workflows that interface with large language models (LLMs). + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Prompt-driven search with LLMs](https://github.com/neuml/txtai/blob/master/examples/42_Prompt_driven_search_with_LLMs.ipynb) | Embeddings-guided and Prompt-driven search with Large Language Models (LLMs) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/42_Prompt_driven_search_with_LLMs.ipynb) | +| [Prompt templates and task chains](https://github.com/neuml/txtai/blob/master/examples/44_Prompt_templates_and_task_chains.ipynb) | Build model prompts and connect tasks together with workflows | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/44_Prompt_templates_and_task_chains.ipynb) | +| [Build RAG pipelines with txtai](https://github.com/neuml/txtai/blob/master/examples/52_Build_RAG_pipelines_with_txtai.ipynb) [▶️](https://www.youtube.com/watch?v=t_OeAc8NVfQ) | Guide on retrieval augmented generation including how to create citations | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/52_Build_RAG_pipelines_with_txtai.ipynb) | +| [Integrate LLM frameworks](https://github.com/neuml/txtai/blob/master/examples/53_Integrate_LLM_Frameworks.ipynb) | Integrate llama.cpp, LiteLLM and custom generation frameworks | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/53_Integrate_LLM_Frameworks.ipynb) | +| [Generate knowledge with Semantic Graphs and RAG](https://github.com/neuml/txtai/blob/master/examples/55_Generate_knowledge_with_Semantic_Graphs_and_RAG.ipynb) | Knowledge exploration and discovery with Semantic Graphs and RAG | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/55_Generate_knowledge_with_Semantic_Graphs_and_RAG.ipynb) | +| [Build knowledge graphs with LLMs](https://github.com/neuml/txtai/blob/master/examples/57_Build_knowledge_graphs_with_LLM_driven_entity_extraction.ipynb) | Build knowledge graphs with LLM-driven entity extraction | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/57_Build_knowledge_graphs_with_LLM_driven_entity_extraction.ipynb) | +| [Advanced RAG with graph path traversal](https://github.com/neuml/txtai/blob/master/examples/58_Advanced_RAG_with_graph_path_traversal.ipynb) | Graph path traversal to collect complex sets of data for advanced RAG | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/58_Advanced_RAG_with_graph_path_traversal.ipynb) | +| [Advanced RAG with guided generation](https://github.com/neuml/txtai/blob/master/examples/60_Advanced_RAG_with_guided_generation.ipynb) | Retrieval Augmented and Guided Generation | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/60_Advanced_RAG_with_guided_generation.ipynb) | +| [RAG with llama.cpp and external API services](https://github.com/neuml/txtai/blob/master/examples/62_RAG_with_llama_cpp_and_external_API_services.ipynb) | RAG with additional vector and LLM frameworks | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/62_RAG_with_llama_cpp_and_external_API_services.ipynb) | +| [How RAG with txtai works](https://github.com/neuml/txtai/blob/master/examples/63_How_RAG_with_txtai_works.ipynb) | Create RAG processes, API services and Docker instances | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/63_How_RAG_with_txtai_works.ipynb) | +| [Speech to Speech RAG](https://github.com/neuml/txtai/blob/master/examples/65_Speech_to_Speech_RAG.ipynb) [▶️](https://www.youtube.com/watch?v=tH8QWwkVMKA) | Full cycle speech to speech workflow with RAG | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/65_Speech_to_Speech_RAG.ipynb) | +| [Analyzing Hugging Face Posts with Graphs and Agents](https://github.com/neuml/txtai/blob/master/examples/68_Analyzing_Hugging_Face_Posts_with_Graphs_and_Agents.ipynb) | Explore a rich dataset with Graph Analysis and Agents | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/68_Analyzing_Hugging_Face_Posts_with_Graphs_and_Agents.ipynb) | +| [Granting autonomy to agents](https://github.com/neuml/txtai/blob/master/examples/69_Granting_autonomy_to_agents.ipynb) | Agents that iteratively solve problems as they see fit | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/69_Granting_autonomy_to_agents.ipynb) | +| [Getting started with LLM APIs](https://github.com/neuml/txtai/blob/master/examples/70_Getting_started_with_LLM_APIs.ipynb) | Generate embeddings and run LLMs with OpenAI, Claude, Gemini, Bedrock and more | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/70_Getting_started_with_LLM_APIs.ipynb) | +| [Analyzing LinkedIn Company Posts with Graphs and Agents](https://github.com/neuml/txtai/blob/master/examples/71_Analyzing_LinkedIn_Company_Posts_with_Graphs_and_Agents.ipynb) | Exploring how to improve social media engagement with AI | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/71_Analyzing_LinkedIn_Company_Posts_with_Graphs_and_Agents.ipynb) | +| [Parsing the stars with txtai](https://github.com/neuml/txtai/blob/master/examples/72_Parsing_the_stars_with_txtai.ipynb) | Explore an astronomical knowledge graph of known stars, planets, galaxies | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/72_Parsing_the_stars_with_txtai.ipynb) | +| [Chunking your data for RAG](https://github.com/neuml/txtai/blob/master/examples/73_Chunking_your_data_for_RAG.ipynb) | Extract, chunk and index content for effective retrieval | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/73_Chunking_your_data_for_RAG.ipynb) | +| [Medical RAG Research with txtai](https://github.com/neuml/txtai/blob/master/examples/75_Medical_RAG_Research_with_txtai.ipynb) | Analyze PubMed article metadata with RAG | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/75_Medical_RAG_Research_with_txtai.ipynb) | +| [GraphRAG with Wikipedia and GPT OSS](https://github.com/neuml/txtai/blob/master/examples/77_GraphRAG_with_Wikipedia_and_GPT_OSS.ipynb) | Deep graph search powered RAG | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/77_GraphRAG_with_Wikipedia_and_GPT_OSS.ipynb) | +| [RAG is more than Vector Search](https://github.com/neuml/txtai/blob/master/examples/79_RAG_is_more_than_Vector_Search.ipynb) | Context retrieval via Web, SQL and other sources | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/79_RAG_is_more_than_Vector_Search.ipynb) | +| [OpenCode as a txtai LLM](https://github.com/neuml/txtai/blob/master/examples/81_OpenCode_as_a_txtai_LLM.ipynb) | Integrate OpenCode with the txtai ecosystem | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/81_OpenCode_as_a_txtai_LLM.ipynb) | +| [Agentic College Search](https://github.com/neuml/txtai/blob/master/examples/82_Agentic_College_Search.ipynb) | Identify a list of strong engineering colleges | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/82_Agentic_College_Search.ipynb) | +| [TxtAI got skills](https://github.com/neuml/txtai/blob/master/examples/83_TxtAI_got_skills.ipynb) | Integrate skill.md files with your agent | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/83_TxtAI_got_skills.ipynb) | +| [Agent Tools](https://github.com/neuml/txtai/blob/master/examples/84_Agent_Tools.ipynb) [▶️](https://www.youtube.com/watch?v=RDNaFXQy3GQ) | Learn about the txtai agent toolkit | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/84_Agent_Tools.ipynb) | + +## Pipelines + +Transform data with language model backed pipelines. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Extractive QA with txtai](https://github.com/neuml/txtai/blob/master/examples/05_Extractive_QA_with_txtai.ipynb) | Introduction to extractive question-answering with txtai | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/05_Extractive_QA_with_txtai.ipynb) | +| [Extractive QA with Elasticsearch](https://github.com/neuml/txtai/blob/master/examples/06_Extractive_QA_with_Elasticsearch.ipynb) | Run extractive question-answering queries with Elasticsearch | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/06_Extractive_QA_with_Elasticsearch.ipynb) | +| [Extractive QA to build structured data](https://github.com/neuml/txtai/blob/master/examples/20_Extractive_QA_to_build_structured_data.ipynb) | Build structured datasets using extractive question-answering | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/20_Extractive_QA_to_build_structured_data.ipynb) | +| [Apply labels with zero shot classification](https://github.com/neuml/txtai/blob/master/examples/07_Apply_labels_with_zero_shot_classification.ipynb) | Use zero shot learning for labeling, classification and topic modeling | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/07_Apply_labels_with_zero_shot_classification.ipynb) | +| [Building abstractive text summaries](https://github.com/neuml/txtai/blob/master/examples/09_Building_abstractive_text_summaries.ipynb) | Run abstractive text summarization | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/09_Building_abstractive_text_summaries.ipynb) | +| [Extract text from documents](https://github.com/neuml/txtai/blob/master/examples/10_Extract_text_from_documents.ipynb) | Extract text from PDF, Office, HTML and more | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/10_Extract_text_from_documents.ipynb) | +| [Text to speech generation](https://github.com/neuml/txtai/blob/master/examples/40_Text_to_Speech_Generation.ipynb) | Generate speech from text | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/40_Text_to_Speech_Generation.ipynb) | +| [Transcribe audio to text](https://github.com/neuml/txtai/blob/master/examples/11_Transcribe_audio_to_text.ipynb) | Convert audio files to text | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/11_Transcribe_audio_to_text.ipynb) | +| [Translate text between languages](https://github.com/neuml/txtai/blob/master/examples/12_Translate_text_between_languages.ipynb) | Streamline machine translation and language detection | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/12_Translate_text_between_languages.ipynb) | +| [Generate image captions and detect objects](https://github.com/neuml/txtai/blob/master/examples/25_Generate_image_captions_and_detect_objects.ipynb) | Captions and object detection for images | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/25_Generate_image_captions_and_detect_objects.ipynb) | +| [Near duplicate image detection](https://github.com/neuml/txtai/blob/master/examples/31_Near_duplicate_image_detection.ipynb) | Identify duplicate and near-duplicate images | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/31_Near_duplicate_image_detection.ipynb) | + +## Workflows + +Efficiently process data at scale. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Run pipeline workflows](https://github.com/neuml/txtai/blob/master/examples/14_Run_pipeline_workflows.ipynb) [▶️](https://www.youtube.com/watch?v=UBMPDCn1gEU) | Simple yet powerful constructs to efficiently process data | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/14_Run_pipeline_workflows.ipynb) | +| [Transform tabular data with composable workflows](https://github.com/neuml/txtai/blob/master/examples/22_Transform_tabular_data_with_composable_workflows.ipynb) | Transform, index and search tabular data | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/22_Transform_tabular_data_with_composable_workflows.ipynb) | +| [Tensor workflows](https://github.com/neuml/txtai/blob/master/examples/23_Tensor_workflows.ipynb) | Performant processing of large tensor arrays | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/23_Tensor_workflows.ipynb) | +| [Entity extraction workflows](https://github.com/neuml/txtai/blob/master/examples/26_Entity_extraction_workflows.ipynb) | Identify entity/label combinations | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/26_Entity_extraction_workflows.ipynb) | +| [Workflow Scheduling](https://github.com/neuml/txtai/blob/master/examples/27_Workflow_scheduling.ipynb) | Schedule workflows with cron expressions | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/27_Workflow_scheduling.ipynb) | +| [Push notifications with workflows](https://github.com/neuml/txtai/blob/master/examples/28_Push_notifications_with_workflows.ipynb) | Generate and push notifications with workflows | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/28_Push_notifications_with_workflows.ipynb) | +| [Pictures are a worth a thousand words](https://github.com/neuml/txtai/blob/master/examples/35_Pictures_are_worth_a_thousand_words.ipynb) | Generate webpage summary images with DALL-E mini | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/35_Pictures_are_worth_a_thousand_words.ipynb) | +| [Run txtai with native code](https://github.com/neuml/txtai/blob/master/examples/36_Run_txtai_in_native_code.ipynb) | Execute workflows in native code with the Python C API | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/36_Run_txtai_in_native_code.ipynb) | +| [Generative Audio](https://github.com/neuml/txtai/blob/master/examples/66_Generative_Audio.ipynb) | Storytelling with generative audio workflows | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/66_Generative_Audio.ipynb) | + +## Model Training + +Train, distill, fine-tune and export models. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Train a text labeler](https://github.com/neuml/txtai/blob/master/examples/16_Train_a_text_labeler.ipynb) | Build text sequence classification models | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/16_Train_a_text_labeler.ipynb) | +| [Train without labels](https://github.com/neuml/txtai/blob/master/examples/17_Train_without_labels.ipynb) | Use zero-shot classifiers to train new models | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/17_Train_without_labels.ipynb) | +| [Train a QA model](https://github.com/neuml/txtai/blob/master/examples/19_Train_a_QA_model.ipynb) | Build and fine-tune question-answering models | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/19_Train_a_QA_model.ipynb) | +| [Train a language model from scratch](https://github.com/neuml/txtai/blob/master/examples/41_Train_a_language_model_from_scratch.ipynb) | Build new language models | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/41_Train_a_language_model_from_scratch.ipynb) | +| [Distilling Knowledge into Tiny LLMs](https://github.com/neuml/txtai/blob/master/examples/80_Distilling_Knowledge_into_Tiny_LLMs.ipynb) [▶️](https://www.youtube.com/watch?v=Ol560ktgkf0) | Finetune tiny LLMs to enable inference using less resources | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/80_Distilling_Knowledge_into_Tiny_LLMs.ipynb) | +| [Progressive Distillation](https://github.com/neuml/txtai/blob/master/examples/85_Progressive_Distillation.ipynb) | Incrementally compress knowledge from a series of teachers | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/85_Progressive_Distillation.ipynb) | +| [Export and run models with ONNX](https://github.com/neuml/txtai/blob/master/examples/18_Export_and_run_models_with_ONNX.ipynb) | Export models with ONNX, run natively in JavaScript, Java and Rust | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/18_Export_and_run_models_with_ONNX.ipynb) | +| [Export and run other machine learning models](https://github.com/neuml/txtai/blob/master/examples/21_Export_and_run_other_machine_learning_models.ipynb) | Export and run models from scikit-learn, PyTorch and more | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/21_Export_and_run_other_machine_learning_models.ipynb) | + +## API + +Run distributed txtai, integrate with the API and cloud endpoints. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [API Gallery](https://github.com/neuml/txtai/blob/master/examples/08_API_Gallery.ipynb) | Using txtai in JavaScript, Java, Rust and Go | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/08_API_Gallery.ipynb) | +| [Distributed embeddings cluster](https://github.com/neuml/txtai/blob/master/examples/15_Distributed_embeddings_cluster.ipynb) | Distribute an embeddings index across multiple data nodes | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/15_Distributed_embeddings_cluster.ipynb) | +| [Embeddings in the Cloud](https://github.com/neuml/txtai/blob/master/examples/43_Embeddings_in_the_Cloud.ipynb) | Load and use an embeddings index from the Hugging Face Hub | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/43_Embeddings_in_the_Cloud.ipynb) | +| [Custom API Endpoints](https://github.com/neuml/txtai/blob/master/examples/51_Custom_API_Endpoints.ipynb) | Extend the API with custom endpoints | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/51_Custom_API_Endpoints.ipynb) | +| [API Authorization and Authentication](https://github.com/neuml/txtai/blob/master/examples/54_API_Authorization_and_Authentication.ipynb) | Add authorization, authentication and middleware dependencies to the API | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/54_API_Authorization_and_Authentication.ipynb) | +| [OpenAI Compatible API](https://github.com/neuml/txtai/blob/master/examples/74_OpenAI_Compatible_API.ipynb) | Connect to txtai with a standard OpenAI client library | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/74_OpenAI_Compatible_API.ipynb) | + +## Architecture + +Project architecture, data formats, external integrations, scale to production, benchmarks, and performance. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Anatomy of a txtai index](https://github.com/neuml/txtai/blob/master/examples/29_Anatomy_of_a_txtai_index.ipynb) | Deep dive into the file formats behind a txtai embeddings index | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/29_Anatomy_of_a_txtai_index.ipynb) | +| [Embeddings components](https://github.com/neuml/txtai/blob/master/examples/37_Embeddings_index_components.ipynb) | Composable search with vector, SQL and scoring components | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/37_Embeddings_index_components.ipynb) | +| [Customize your own embeddings database](https://github.com/neuml/txtai/blob/master/examples/45_Customize_your_own_embeddings_database.ipynb) | Ways to combine vector indexes with relational databases | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/45_Customize_your_own_embeddings_database.ipynb) | +| [Building an efficient sparse keyword index in Python](https://github.com/neuml/txtai/blob/master/examples/47_Building_an_efficient_sparse_keyword_index_in_Python.ipynb) | Fast and accurate sparse keyword indexing | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/47_Building_an_efficient_sparse_keyword_index_in_Python.ipynb) | +| [Benefits of hybrid search](https://github.com/neuml/txtai/blob/master/examples/48_Benefits_of_hybrid_search.ipynb) | Improve accuracy with a combination of semantic and keyword search | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/48_Benefits_of_hybrid_search.ipynb) | +| [External database integration](https://github.com/neuml/txtai/blob/master/examples/49_External_database_integration.ipynb) | Store metadata in PostgreSQL, MariaDB, MySQL and more | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/49_External_database_integration.ipynb) | +| [All about vector quantization](https://github.com/neuml/txtai/blob/master/examples/50_All_about_vector_quantization.ipynb) | Benchmarking scalar and product quantization methods | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/50_All_about_vector_quantization.ipynb) | +| [External vectorization](https://github.com/neuml/txtai/blob/master/examples/56_External_vectorization.ipynb) | Vectorization with precomputed embeddings datasets and APIs | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/56_External_vectorization.ipynb) | +| [Integrate txtai with Postgres](https://github.com/neuml/txtai/blob/master/examples/61_Integrate_txtai_with_Postgres.ipynb) | Persist content, vectors and graph data in Postgres | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/61_Integrate_txtai_with_Postgres.ipynb) | +| [Embeddings index format for open data access](https://github.com/neuml/txtai/blob/master/examples/64_Embeddings_index_format_for_open_data_access.ipynb) | Platform and programming language independent data storage with txtai | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/64_Embeddings_index_format_for_open_data_access.ipynb) | +| [Accessing Low Level Vector APIs](https://github.com/neuml/txtai/blob/master/examples/78_Accessing_Low_Level_Vector_APIs.ipynb) | Build a vector database using txtai's low-level APIs | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/78_Accessing_Low_Level_Vector_APIs.ipynb) | + +## Releases + +New functionality added in major releases. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [What's new in txtai 4.0](https://github.com/neuml/txtai/blob/master/examples/24_Whats_new_in_txtai_4_0.ipynb) | Content storage, SQL, object storage, reindex and compressed indexes | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/24_Whats_new_in_txtai_4_0.ipynb) | +| [What's new in txtai 6.0](https://github.com/neuml/txtai/blob/master/examples/46_Whats_new_in_txtai_6_0.ipynb) | Sparse, hybrid and subindexes for embeddings, LLM improvements | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/46_Whats_new_in_txtai_6_0.ipynb) | +| [What's new in txtai 7.0](https://github.com/neuml/txtai/blob/master/examples/59_Whats_new_in_txtai_7_0.ipynb) | Semantic graph 2.0, LoRA/QLoRA training and binary API support | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/59_Whats_new_in_txtai_7_0.ipynb) | +| [What's new in txtai 8.0](https://github.com/neuml/txtai/blob/master/examples/67_Whats_new_in_txtai_8_0.ipynb) | Agents with txtai | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/67_Whats_new_in_txtai_8_0.ipynb) | +| [What's new in txtai 9.0](https://github.com/neuml/txtai/blob/master/examples/76_Whats_new_in_txtai_9_0.ipynb) | Learned sparse vectors, late interaction models and rerankers | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/76_Whats_new_in_txtai_9_0.ipynb) | + +## Applications + +Series of example applications with txtai. Links to hosted versions on [Hugging Face Spaces](https://hf.co/spaces) are also provided, when available. + +| Application | Description | | +|:-------------|:-------------|------:| +| [Basic similarity search](https://github.com/neuml/txtai/blob/master/examples/similarity.py) | Basic similarity search example. Data from the original txtai demo. |[🤗](https://hf.co/spaces/NeuML/similarity)| +| [Baseball stats](https://github.com/neuml/txtai/blob/master/examples/baseball.py) | Match historical baseball player stats using vector search. |[🤗](https://hf.co/spaces/NeuML/baseball)| +| [Benchmarks](https://github.com/neuml/txtai/blob/master/examples/benchmarks.py) | Calculate performance metrics for the BEIR datasets. |*Local run only*| +| [Book search](https://github.com/neuml/txtai/blob/master/examples/books.py) | Book similarity search application. Index book descriptions and query using natural language statements. |*Local run only*| +| [Image search](https://github.com/neuml/txtai/blob/master/examples/images.py) | Image similarity search application. Index a directory of images and run searches to identify images similar to the input query. |[🤗](https://hf.co/spaces/NeuML/imagesearch)| +| [Retrieval Augmented Generation](https://github.com/neuml/rag/) | RAG with txtai embeddings databases. Ask questions and get answers from LLMs bound by a context. |*Local run only*| +| [Summarize an article](https://github.com/neuml/txtai/blob/master/examples/article.py) | Summarize an article. Workflow that extracts text from a webpage and builds a summary. |[🤗](https://hf.co/spaces/NeuML/articlesummary)| +| [Wiki search](https://github.com/neuml/txtai/blob/master/examples/wiki.py) | Wikipedia search application. Queries Wikipedia API and summarizes the top result. |[🤗](https://hf.co/spaces/NeuML/wikisummary)| +| [Workflow builder](https://github.com/neuml/txtai/blob/master/examples/workflows.py) | Build and execute txtai workflows. Connect summarization, text extraction, transcription, translation and similarity search pipelines together to run unified workflows. |[🤗](https://hf.co/spaces/NeuML/txtai)| diff --git a/docs/faq.md b/docs/faq.md new file mode 100644 index 0000000..24b3ef2 --- /dev/null +++ b/docs/faq.md @@ -0,0 +1,151 @@ +# FAQ + +![faq](images/faq.png) + +Below is a list of frequently asked questions and common issues encountered. + +## Questions + +---------- + +__Question__ + +What models are recommended? + +__Answer__ + +See the [model guide](../models). + +---------- + +__Question__ + +What is the best way to track the progress of an `embeddings.index` call? + +__Answer__ + +Wrap the list or generator passed to the index call with tqdm. See [#478](https://github.com/neuml/txtai/issues/478) for more. + +---------- + +__Question__ + +What is the best way to analyze and debug a txtai process? + +__Answer__ + +See the [observability](../observability) section for more on how this can be enabled in txtai processes. + +txtai also has a console application. [This article](https://medium.com/neuml/insights-from-the-txtai-console-d307c28e149e) has more details. + +---------- + +__Question__ + +How can models be externally loaded and passed to embeddings and pipelines? + +__Answer__ + +Embeddings example. + +```python +from transformers import AutoModel, AutoTokenizer +from txtai import Embeddings + +# Load model externally +model = AutoModel.from_pretrained("sentence-transformers/all-MiniLM-L6-v2") +tokenizer = AutoTokenizer.from_pretrained("sentence-transformers/all-MiniLM-L6-v2") + +# Pass to embeddings instance +embeddings = Embeddings(path=model, tokenizer=tokenizer) +``` + +LLM pipeline example. + +```python +import torch + +from transformers import AutoModelForCausalLM, AutoTokenizer +from txtai import LLM + +# Load Qwen3 0.6B +path = "Qwen/Qwen3-0.6B" +model = AutoModelForCausalLM.from_pretrained( + path, + dtype=torch.bfloat16, +) +tokenizer = AutoTokenizer.from_pretrained(path) + +llm = LLM((model, tokenizer)) +``` + +## Common issues + +---------- + +__Issue__ + +Embeddings query errors like this: + +``` +SQLError: no such function: json_extract +``` + +__Solution__ + +Upgrade Python version as it doesn't have SQLite support for `json_extract` + +---------- + +__Issue__ + +Segmentation faults and similar errors on macOS + +__Solution__ + +Set the following environment parameters. + +- Disable OpenMP multithreading via `export OMP_NUM_THREADS=1` +- Workaround `OMP: Error #15` errors via `export KMP_DUPLICATE_LIB_OK=TRUE` +- Disable PyTorch MPS device via `export PYTORCH_MPS_DISABLE=1` +- Disable llama.cpp metal via `export LLAMA_NO_METAL=1` + +For more details, refer to [this issue on GitHub](https://github.com/kyamagu/faiss-wheels/issues/73). + +---------- + +__Issue__ + +Error running SQLite ANN on macOS + +``` +AttributeError: 'sqlite3.Connection' object has no attribute 'enable_load_extension' +``` + +__Solution__ + +See [this note](https://alexgarcia.xyz/sqlite-vec/python.html#macos-blocks-sqlite-extensions-by-default) for options on how to fix this. + +---------- + +__Issue__ + +`ContextualVersionConflict` and/or package METADATA exception while running one of the [examples](../examples) notebooks on Google Colab + +__Solution__ + +Restart the kernel. See issue [#409](https://github.com/neuml/txtai/issues/409) for more on this issue. + +---------- + +__Issue__ + +Error installing optional/extra dependencies such as `pipeline` + +__Solution__ + +The default MacOS shell (zsh) and Windows PowerShell require escaping square brackets + +``` +pip install 'txtai[pipeline]' +``` diff --git a/docs/further.md b/docs/further.md new file mode 100644 index 0000000..6d344c3 --- /dev/null +++ b/docs/further.md @@ -0,0 +1,12 @@ +# Further reading + +![further](images/further.png#only-light) +![further](images/further-dark.png#only-dark) + +- [Introducing txtai, the all-in-one AI framework](https://medium.com/neuml/introducing-txtai-the-all-in-one-ai-framework-0660ecfc39d7) +- [txtai: An All-in-One AI Framework for Semantic Search and LLM Workflows](https://github.com/neuml/papers/blob/master/txtai/txtai.pdf) +- [Tutorial series on Hashnode](https://neuml.hashnode.dev/series/txtai-tutorial) | [dev.to](https://dev.to/neuml/tutorial-series-on-txtai-ibg) +- [What's new in txtai 9.0](https://medium.com/neuml/whats-new-in-txtai-9-0-d522bb150afa) | [8.0](https://medium.com/neuml/whats-new-in-txtai-8-0-2d7d0ab4506b) | [7.0](https://medium.com/neuml/whats-new-in-txtai-7-0-855ad6a55440) | [6.0](https://medium.com/neuml/whats-new-in-txtai-6-0-7d93eeedf804) | [5.0](https://medium.com/neuml/whats-new-in-txtai-5-0-e5c75a13b101) | [4.0](https://medium.com/neuml/whats-new-in-txtai-4-0-bbc3a65c3d1c) +- [Getting started with semantic search](https://medium.com/neuml/getting-started-with-semantic-search-a9fd9d8a48cf) | [workflows](https://medium.com/neuml/getting-started-with-semantic-workflows-2fefda6165d9) | [rag](https://medium.com/neuml/getting-started-with-rag-9a0cca75f748) +- [Running txtai at scale](https://medium.com/neuml/running-at-scale-with-txtai-71196cdd99f9) +- [Vector search & RAG Landscape: A review with txtai](https://medium.com/neuml/vector-search-rag-landscape-a-review-with-txtai-a7f37ad0e187) diff --git a/docs/images/agent.excalidraw b/docs/images/agent.excalidraw new file mode 100644 index 0000000..3e06dd2 --- /dev/null +++ b/docs/images/agent.excalidraw @@ -0,0 +1,1149 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "text", + "version": 1781, + "versionNonce": 1504167660, + "isDeleted": false, + "id": "yF7ftUwr3mnAwOlC59RMi", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1746.949005220418, + "y": 32.708022189384565, + "strokeColor": "#7950f2", + "backgroundColor": "transparent", + "width": 212.06666666666666, + "height": 25.420547029684837, + "seed": 376471794, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1731090696250, + "link": null, + "locked": false, + "fontSize": 21.18378919140403, + "fontFamily": 1, + "text": "Tools and Functions", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Tools and Functions", + "index": "a1", + "frameId": null, + "autoResize": true, + "lineHeight": 1.2 + }, + { + "type": "line", + "version": 5394, + "versionNonce": 1005349228, + "isDeleted": false, + "id": "aEfZn91vFxUsNoiS_231h", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1818.1429109642538, + "y": -209.6883063766287, + "strokeColor": "#ff7043", + "backgroundColor": "#fa5252", + "width": 62.46534181999921, + "height": 80.62144046844392, + "seed": 283082348, + "groupIds": [ + "YlFNaYy8uxiVBIOqH078l" + ], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1731090626713, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0.20597861858336483, + 60.93324773178684 + ], + [ + 0.009639315807164919, + 67.87023338578067 + ], + [ + 3.2171032549927925, + 70.86761910642136 + ], + [ + 14.386936381131346, + 73.4043779800946 + ], + [ + 33.26711841453209, + 74.19428679833626 + ], + [ + 51.30588383530208, + 72.93308166143258 + ], + [ + 60.89001271988386, + 69.91693238712155 + ], + [ + 62.241724409786244, + 67.37428690837503 + ], + [ + 62.431567423392536, + 61.78937034530568 + ], + [ + 62.2825627324806, + 5.111959380832905 + ], + [ + 61.946658330498884, + -0.24301167156979483 + ], + [ + 57.93575321694209, + -3.2359402189020954 + ], + [ + 49.489643010028544, + -4.969286064540119 + ], + [ + 30.242099023560478, + -6.427153670107652 + ], + [ + 14.810450273983571, + -5.557823934240758 + ], + [ + 2.6735633343171465, + -2.609163541657122 + ], + [ + -0.033774396606671454, + -0.03661258119549431 + ], + [ + 0, + 0 + ] + ], + "index": "a3", + "frameId": null + }, + { + "type": "line", + "version": 3136, + "versionNonce": 1274305516, + "isDeleted": false, + "id": "JSkCiTP6nfh3_0WXfktLt", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1821.286624574959, + "y": -163.7331019656284, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 57.79381935156673, + "height": 6.618955975035558, + "seed": 465359084, + "groupIds": [ + "YlFNaYy8uxiVBIOqH078l" + ], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1731090626713, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 1.6474005758537682, + 2.765528506120729 + ], + [ + 8.751958185556267, + 5.085781507915724 + ], + [ + 18.205684820748022, + 6.490912696060695 + ], + [ + 33.01612829072934, + 6.618955975035558 + ], + [ + 50.29940758591302, + 5.724372353349996 + ], + [ + 57.79381935156673, + 3.769506661359109 + ], + [ + 56.89849492771129, + 4.013173323127235 + ] + ], + "index": "a4", + "frameId": null + }, + { + "type": "line", + "version": 3222, + "versionNonce": 727318124, + "isDeleted": false, + "id": "XbZa4_v84aJkwUbHNYtWo", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1817.0033279434713, + "y": -183.2009370605118, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 62.53013535500473, + "height": 6.9378137498265104, + "seed": 877384556, + "groupIds": [ + "YlFNaYy8uxiVBIOqH078l" + ], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1731090626713, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 1.647400575853768, + 2.765528506120729 + ], + [ + 8.751958185556267, + 5.085781507915719 + ], + [ + 18.20568482074802, + 6.490912696060696 + ], + [ + 33.016128290729334, + 6.618955975035559 + ], + [ + 50.29940758591301, + 5.7243723533499935 + ], + [ + 60.393038010317625, + 2.4698973319836566 + ], + [ + 62.53013535500473, + -0.3188577747909514 + ] + ], + "index": "a5", + "frameId": null + }, + { + "type": "ellipse", + "version": 6241, + "versionNonce": 48713964, + "isDeleted": false, + "id": "WgZ_UNGw6chJCNh_YqBCg", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1818.1477872422097, + "y": -216.22048622222513, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 62.06467888763865, + "height": 12.55211437327341, + "seed": 416712172, + "groupIds": [ + "YlFNaYy8uxiVBIOqH078l" + ], + "roundness": null, + "boundElements": [], + "updated": 1731090626713, + "link": null, + "locked": false, + "index": "a6", + "frameId": null + }, + { + "type": "ellipse", + "version": 1583, + "versionNonce": 1378243436, + "isDeleted": false, + "id": "5zDzUgYAzqCuHz5y0fEbO", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1867.2486508066536, + "y": -195.2355116320403, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 9.096173632176203, + "height": 9.872132933859838, + "seed": 778496108, + "groupIds": [ + "YlFNaYy8uxiVBIOqH078l" + ], + "roundness": null, + "boundElements": [], + "updated": 1731090626713, + "link": null, + "locked": false, + "index": "a7", + "frameId": null + }, + { + "type": "ellipse", + "version": 1632, + "versionNonce": 392234476, + "isDeleted": false, + "id": "VtzlusDzgSLcDDBt5hG2x", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1867.2486508066536, + "y": -173.56619840583764, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 9.096173632176203, + "height": 9.872132933859838, + "seed": 684129004, + "groupIds": [ + "YlFNaYy8uxiVBIOqH078l" + ], + "roundness": null, + "boundElements": [], + "updated": 1731090626713, + "link": null, + "locked": false, + "index": "a8", + "frameId": null + }, + { + "type": "ellipse", + "version": 1685, + "versionNonce": 2007066732, + "isDeleted": false, + "id": "B_0DhVcOs-USQL4XV0QRo", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1867.2486508066536, + "y": -150.01453414636433, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 9.096173632176203, + "height": 9.872132933859838, + "seed": 847297900, + "groupIds": [ + "YlFNaYy8uxiVBIOqH078l" + ], + "roundness": null, + "boundElements": [], + "updated": 1731090626713, + "link": null, + "locked": false, + "index": "a9", + "frameId": null + }, + { + "type": "text", + "version": 1457, + "versionNonce": 1722907372, + "isDeleted": false, + "id": "4zMdwTCbTrrgg-ITOMXC0", + "fillStyle": "hachure", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1779.9259357205947, + "y": -120.42699264611412, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 136.73556714551438, + "height": 25.535793939087647, + "seed": 1848588206, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "leDc6Y5qdU0OH3j90C072", + "type": "arrow" + } + ], + "updated": 1731090626713, + "link": null, + "locked": false, + "fontSize": 21.27982828257304, + "fontFamily": 1, + "text": "Data Stores", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Data Stores", + "index": "aA", + "frameId": null, + "autoResize": true, + "lineHeight": 1.2 + }, + { + "type": "arrow", + "version": 3697, + "versionNonce": 368624620, + "isDeleted": false, + "id": "iPTphyfJkvqyMptaZS0ge", + "fillStyle": "hachure", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1814.6382080826127, + "y": -173.3228158450134, + "strokeColor": "#ff7043", + "backgroundColor": "#7950f2", + "width": 182.4871709755887, + "height": 81.80578637477413, + "seed": 1965679388, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1731090676915, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "V54vdkZmQeI13AEahvNPV", + "focus": 0.1525458549344351, + "gap": 4.034063550289034, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -182.4871709755887, + 81.80578637477413 + ] + ], + "index": "aB", + "frameId": null + }, + { + "type": "text", + "version": 1522, + "versionNonce": 2052237012, + "isDeleted": false, + "id": "YCEwpQiKAjRS3zIG3_E8s", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1555.5109174458237, + "y": -43.50932007711572, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 38.06666666666666, + "height": 23.223174527172613, + "seed": 676249636, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1731090662600, + "link": null, + "locked": false, + "fontSize": 19.35264543931051, + "fontFamily": 1, + "text": "LLM", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "LLM", + "index": "aC", + "frameId": null, + "autoResize": true, + "lineHeight": 1.2 + }, + { + "type": "arrow", + "version": 5294, + "versionNonce": 1355936620, + "isDeleted": false, + "id": "leDc6Y5qdU0OH3j90C072", + "fillStyle": "hachure", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1816.227500619918, + "y": -9.184012279360502, + "strokeColor": "#7950f2", + "backgroundColor": "#7950f2", + "width": 182.7150865033991, + "height": 52.05945128870996, + "seed": 241264412, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1731090696850, + "link": null, + "locked": false, + "startBinding": { + "elementId": "qzVAVN6sXRxG16958WgBM", + "focus": -0.358596672811187, + "gap": 2.5501158014194516, + "fixedPoint": null + }, + "endBinding": { + "elementId": "V54vdkZmQeI13AEahvNPV", + "focus": 0.704166265794774, + "gap": 5.395440559783879, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -182.7150865033991, + -52.05945128870996 + ] + ], + "index": "aD", + "frameId": null + }, + { + "type": "line", + "version": 2515, + "versionNonce": 679390292, + "isDeleted": false, + "id": "aFBJSnHUVo8ZDTb0uAkQ2", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1310.644313207792, + "y": -121.5611895708457, + "strokeColor": "#ffeb3b", + "backgroundColor": "#ffeb3b", + "width": 99.33023905537627, + "height": 88.43543879744573, + "seed": 1963890941, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1731090662600, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -53.27565065744737, + 16.38089846974506 + ], + [ + -53.90255959959227, + 67.77711223685318 + ], + [ + -6.4999879824429385, + 88.43543879744573 + ], + [ + 44.3351035833632, + 67.53847405928806 + ], + [ + 45.42767945578399, + 18.161010280772913 + ], + [ + 0, + 0 + ] + ], + "index": "aE", + "frameId": null + }, + { + "type": "text", + "version": 1046, + "versionNonce": 235798996, + "isDeleted": false, + "id": "eqOUg003X-50uDJ_XGUnB", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1234.634080487935, + "y": -28.17382387311136, + "strokeColor": "#ffb13b", + "backgroundColor": "#00e676", + "width": 145.03034615947246, + "height": 28.667631224488975, + "seed": 1122464339, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1731090662600, + "link": null, + "locked": false, + "fontSize": 23.889692687074145, + "fontFamily": 1, + "text": "Agent Start", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Agent Start", + "index": "aF", + "frameId": null, + "autoResize": true, + "lineHeight": 1.2 + }, + { + "type": "rectangle", + "version": 2424, + "versionNonce": 35039060, + "isDeleted": false, + "id": "ZTKEryJuyeKfeG4nAI4_e", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1519.3715280015788, + "y": -97.22713220408534, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 16.103557439457987, + "height": 40.2588935986449, + "seed": 1188509340, + "groupIds": [ + "SFux9iuPQVutxOMe0wlW3" + ], + "roundness": null, + "boundElements": [ + { + "id": "Inol-LWi8GThocPMvSZGX", + "type": "arrow" + } + ], + "updated": 1731090662600, + "link": null, + "locked": false, + "index": "aG", + "frameId": null + }, + { + "type": "rectangle", + "version": 2566, + "versionNonce": 741073492, + "isDeleted": false, + "id": "CckqOoWddQBHuvidbpk4P", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1552.7682727230922, + "y": -135.31805500658663, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 16.103557439457962, + "height": 80.51778719728983, + "seed": 331107492, + "groupIds": [ + "SFux9iuPQVutxOMe0wlW3" + ], + "roundness": null, + "boundElements": [], + "updated": 1731090662600, + "link": null, + "locked": false, + "index": "aH", + "frameId": null + }, + { + "type": "rectangle", + "version": 2446, + "versionNonce": 1386510292, + "isDeleted": false, + "id": "J8ox4BBdzEg-04DGRL-jh", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1583.2097012862876, + "y": -111.1627188473997, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 16.103557439457973, + "height": 56.36245103810287, + "seed": 1316437916, + "groupIds": [ + "SFux9iuPQVutxOMe0wlW3" + ], + "roundness": null, + "boundElements": [], + "updated": 1731090662600, + "link": null, + "locked": false, + "index": "aI", + "frameId": null + }, + { + "type": "rectangle", + "version": 2640, + "versionNonce": 739318484, + "isDeleted": false, + "id": "V54vdkZmQeI13AEahvNPV", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1612.013416117277, + "y": -128.4099677663797, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 16.103557439457962, + "height": 72.46600847756086, + "seed": 1025829916, + "groupIds": [ + "SFux9iuPQVutxOMe0wlW3" + ], + "roundness": null, + "boundElements": [ + { + "id": "leDc6Y5qdU0OH3j90C072", + "type": "arrow" + }, + { + "id": "5pptt-wBgcgbxd5LUka3S", + "type": "arrow" + }, + { + "id": "iPTphyfJkvqyMptaZS0ge", + "type": "arrow" + } + ], + "updated": 1731090668783, + "link": null, + "locked": false, + "index": "aJ", + "frameId": null + }, + { + "type": "arrow", + "version": 508, + "versionNonce": 441289068, + "isDeleted": false, + "id": "Inol-LWi8GThocPMvSZGX", + "fillStyle": "hachure", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1355.5263615107983, + "y": -75.4835786217636, + "strokeColor": "#ffeb3b", + "backgroundColor": "transparent", + "width": 160.1111111111113, + "height": 0.7777777777777999, + "seed": 1433457683, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1731090662602, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "ZTKEryJuyeKfeG4nAI4_e", + "focus": -0.12143341039143216, + "gap": 3.73405537966903, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 160.1111111111113, + 0.7777777777777999 + ] + ], + "index": "aK", + "frameId": null + }, + { + "type": "line", + "version": 2786, + "versionNonce": 635507308, + "isDeleted": false, + "id": "8H60JYjdZgpvCjCSLf54L", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2069.873935895482, + "y": -127.67713376387258, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 99.4022378882407, + "height": 88.49954061012453, + "seed": 1027783997, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1731090655648, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -53.31426714225508, + 16.39277205005967 + ], + [ + -53.9416304949861, + 67.82624000521841 + ], + [ + -6.504699453519805, + 88.49954061012453 + ], + [ + 44.36723957481235, + 67.58742885243016 + ], + [ + 45.4606073932546, + 18.174174162751758 + ], + [ + 0, + 0 + ] + ], + "index": "aL", + "frameId": null + }, + { + "type": "text", + "version": 1287, + "versionNonce": 1131144428, + "isDeleted": false, + "id": "E_wil3E0D2OO7QeMf_xiu", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 2006.4263604935459, + "y": -33.70532271267456, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 118.31123365004963, + "height": 28.68841075757759, + "seed": 754233971, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1731090655648, + "link": null, + "locked": false, + "fontSize": 23.90700896464799, + "fontFamily": 1, + "text": "Agent End", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Agent End", + "index": "aM", + "frameId": null, + "autoResize": true, + "lineHeight": 1.2 + }, + { + "type": "arrow", + "version": 845, + "versionNonce": 1896722900, + "isDeleted": false, + "id": "5pptt-wBgcgbxd5LUka3S", + "fillStyle": "hachure", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1632.4152503996868, + "y": -79.37246751065243, + "strokeColor": "#00e676", + "backgroundColor": "#34bbde", + "width": 380.1111111111111, + "height": 0.8888888888888005, + "seed": 603518365, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1731090666260, + "link": null, + "locked": false, + "startBinding": { + "elementId": "V54vdkZmQeI13AEahvNPV", + "focus": 0.35400627835344733, + "gap": 4.29827684295185, + "fixedPoint": null + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 380.1111111111111, + -0.8888888888888005 + ] + ], + "index": "aN", + "frameId": null + }, + { + "id": "qzVAVN6sXRxG16958WgBM", + "type": "rectangle", + "x": 1818.7776164213374, + "y": -52.91854707012696, + "width": 72.81644178885557, + "height": 75.24632678271404, + "angle": 0, + "strokeColor": "#7950f2", + "backgroundColor": "#7950f2", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "strokeSharpness": "sharp", + "seed": 1985394028, + "version": 1013, + "versionNonce": 627575148, + "isDeleted": false, + "boundElementIds": null, + "index": "aW", + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "leDc6Y5qdU0OH3j90C072", + "type": "arrow" + } + ], + "updated": 1731090696250, + "link": null, + "locked": false + }, + { + "id": "z6DGOpk0Ie3KZbNy-UpRO", + "type": "rectangle", + "x": 1835.6278241812026, + "y": -37.7505489614438, + "width": 40.35005029973769, + "height": 16.04584197060145, + "angle": 0, + "strokeColor": "#7950f2", + "backgroundColor": "#7950f2", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "strokeSharpness": "sharp", + "seed": 2036669676, + "version": 850, + "versionNonce": 1651351148, + "isDeleted": false, + "boundElementIds": null, + "index": "aZ", + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1731090696250, + "link": null, + "locked": false + }, + { + "id": "3SSoVnN124G8vYffp5E2K", + "type": "rectangle", + "x": 1835.6278241812026, + "y": -9.064290762712005, + "width": 40.35005029973769, + "height": 16.04584197060145, + "angle": 0, + "strokeColor": "#7950f2", + "backgroundColor": "#7950f2", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "strokeSharpness": "sharp", + "seed": 1326096236, + "version": 892, + "versionNonce": 1451684076, + "isDeleted": false, + "boundElementIds": null, + "index": "aa", + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1731090696250, + "link": null, + "locked": false + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/images/agent.png b/docs/images/agent.png new file mode 100644 index 0000000..f5e1eb8 Binary files /dev/null and b/docs/images/agent.png differ diff --git a/docs/images/api-dark.png b/docs/images/api-dark.png new file mode 100644 index 0000000..6977b6b Binary files /dev/null and b/docs/images/api-dark.png differ diff --git a/docs/images/api.excalidraw b/docs/images/api.excalidraw new file mode 100644 index 0000000..bde82f7 --- /dev/null +++ b/docs/images/api.excalidraw @@ -0,0 +1,1415 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 3067, + "versionNonce": 2074130101, + "isDeleted": false, + "id": "Ufh5VUA3qmvJowuFyEWz4", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 686.7515859513394, + "y": 139.76834100985968, + "strokeColor": "#d0d9dd", + "backgroundColor": "transparent", + "width": 647.3302177689409, + "height": 598.4947884819552, + "seed": 1218477931, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312763581, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 834, + "versionNonce": 351468315, + "isDeleted": false, + "id": "np05zgjgJ1cvT_UcHRO4Q", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 430.44757097692855, + "y": 299.40554486158544, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 110.66704644097234, + "height": 115.17917209201391, + "seed": 112784715, + "groupIds": [ + "MUxhY1x2GsxZKOmYcaGSB" + ], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "_UxQXW5nkuKOXtaavoJ3q", + "type": "arrow" + } + ], + "updated": 1688312763581, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 3122, + "versionNonce": 606214165, + "isDeleted": false, + "id": "GtS3MjQ4UtMxFXJO57RTt", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 436.4176538657067, + "y": 346.76486653900156, + "strokeColor": "#000000", + "backgroundColor": "#4c6ef5", + "width": 100.73332977294922, + "height": 26, + "seed": 1042420715, + "groupIds": [ + "MUxhY1x2GsxZKOmYcaGSB" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312763581, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "txtai.java", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "txtai.java", + "lineHeight": 1.3, + "baseline": 18 + }, + { + "type": "ellipse", + "version": 1432, + "versionNonce": 951771067, + "isDeleted": false, + "id": "OwUTZKSXPIR8EHoAXUrAc", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 748.5117575144184, + "y": 353.37666266846827, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 27.50940800286237, + "height": 23.432583857740315, + "seed": 616058027, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "wtQWWSoRs-w6Dtmax-Hb5", + "type": "arrow" + }, + { + "id": "_UxQXW5nkuKOXtaavoJ3q", + "type": "arrow" + } + ], + "updated": 1688312763581, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 1403, + "versionNonce": 1823493493, + "isDeleted": false, + "id": "qfK-ulSZh44Bl6KC9Ii6F", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 746.9882589503762, + "y": 513.0271342544165, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 27.50940800286237, + "height": 23.432583857740315, + "seed": 605129061, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "Iju4jXbzOeX44JCepZN3j", + "type": "arrow" + } + ], + "updated": 1688312763582, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 1805, + "versionNonce": 794636379, + "isDeleted": false, + "id": "xcu0cSh50RePPTdayuKwa", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 744.9160350012032, + "y": 664.9580577695606, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 27.50940800286237, + "height": 23.432583857740315, + "seed": 726590347, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "QyCYDQ1akZks7nob5gQcb", + "type": "arrow" + } + ], + "updated": 1688312763582, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 1891, + "versionNonce": 1687213781, + "isDeleted": false, + "id": "M3a040F3DuK1icxv47TKI", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 888.8977161326809, + "y": 311.8359502397928, + "strokeColor": "#495057", + "backgroundColor": "#ced4da", + "width": 291.907455568772, + "height": 237.38924779486172, + "seed": 2113350923, + "groupIds": [ + "OA8MrA83v3EseguJzvKpu" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312763582, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 94.12373118273203, + -0.35823119069767984 + ], + [ + 119.85817357723676, + 26.483994997226382 + ], + [ + 290.1826277821376, + 26.874356201934916 + ], + [ + 291.23331617134846, + 236.70336432335256 + ], + [ + -0.6741393974235308, + 237.03101660416405 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "ellipse", + "version": 1544, + "versionNonce": 179072251, + "isDeleted": false, + "id": "09DIdeixkMWbCga6IxuMh", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 896.3667273815456, + "y": 321.85704123732336, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 11.242087394797462, + "height": 9.502075937111973, + "seed": 1749242795, + "groupIds": [ + "o22U873S7MmF-aAZOMXrC", + "OA8MrA83v3EseguJzvKpu" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312763582, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 1579, + "versionNonce": 374944821, + "isDeleted": false, + "id": "HvI6J1Tn27hWbdbb3G2PZ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 911.3550504277948, + "y": 321.3688660254814, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 11.242087394797462, + "height": 9.502075937111973, + "seed": 261203531, + "groupIds": [ + "o22U873S7MmF-aAZOMXrC", + "OA8MrA83v3EseguJzvKpu" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312763582, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 1632, + "versionNonce": 2116752795, + "isDeleted": false, + "id": "2zH7sdihf_U-DKWGnQEP-", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "angle": 0, + "x": 927.1911747446115, + "y": 320.8462868771415, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 11.242087394797462, + "height": 9.502075937111973, + "seed": 1790747883, + "groupIds": [ + "o22U873S7MmF-aAZOMXrC", + "OA8MrA83v3EseguJzvKpu" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312763582, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 1672, + "versionNonce": 502195605, + "isDeleted": false, + "id": "XUyFnqAl7kQAnMAoB7Cgx", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 935.2057099043777, + "y": 359.9947885349467, + "strokeColor": "#343a40", + "backgroundColor": "#fefefe", + "width": 189.85440623862308, + "height": 166.25236270503373, + "seed": 827248523, + "groupIds": [ + "OA8MrA83v3EseguJzvKpu" + ], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "wtQWWSoRs-w6Dtmax-Hb5", + "type": "arrow" + }, + { + "id": "D904AMBJU1YvbvQMXyluo", + "type": "arrow" + } + ], + "updated": 1688312763582, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1200, + "versionNonce": 688630331, + "isDeleted": false, + "id": "oYWPL1hYAJT2RGX0H8kwp", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 988.3296817129515, + "y": 416.9111931412608, + "strokeColor": "#343a40", + "backgroundColor": "#fefefe", + "width": 92.69999694824219, + "height": 72, + "seed": 857102411, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312763582, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "API \nservice", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "API \nservice", + "lineHeight": 1.2857142857142858, + "baseline": 61 + }, + { + "type": "arrow", + "version": 3749, + "versionNonce": 2122803957, + "isDeleted": false, + "id": "wtQWWSoRs-w6Dtmax-Hb5", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 948.4692485198284, + "y": 398.09023585204204, + "strokeColor": "#03a9f4", + "backgroundColor": "#fefefe", + "width": 169.2246874884429, + "height": 34.21193675692433, + "seed": 1918002661, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312763582, + "link": null, + "locked": false, + "startBinding": { + "elementId": "XUyFnqAl7kQAnMAoB7Cgx", + "focus": 0.5294260456922872, + "gap": 1.5145847198102445 + }, + "endBinding": { + "elementId": "OwUTZKSXPIR8EHoAXUrAc", + "focus": -0.11436214558425631, + "gap": 3.2793683958148367 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -41.176703496995515, + -0.5128540090383922 + ], + [ + -41.24791785008779, + -33.26749312785438 + ], + [ + -169.2246874884429, + -34.21193675692433 + ] + ] + }, + { + "type": "line", + "version": 5941, + "versionNonce": 2003095061, + "isDeleted": false, + "id": "_btUlJm1SELpFH74lhe5u", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1242.776436154969, + "y": 396.44332526359824, + "strokeColor": "#5f3dc4", + "backgroundColor": "#7950f2", + "width": 76.99810389727404, + "height": 99.37827711605759, + "seed": 576008901, + "groupIds": [ + "nC_wnGn6V9Cg51lj82An8", + "xEHH4p1jeHBH4PUaRrIni", + "JG_8M8qPMZ6QPLNYCIjBV" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312766628, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0.25390020469271946, + 75.10956320658954 + ], + [ + 0.011881933539363843, + 83.66046081728857 + ], + [ + 3.9655726433067358, + 87.35519793732486 + ], + [ + 17.734103263694266, + 90.48214189738948 + ], + [ + 41.00682018880677, + 91.45582553513545 + ], + [ + 63.24236222825351, + 89.90119697892055 + ], + [ + 75.05626943052894, + 86.1833309042851 + ], + [ + 76.72246117951802, + 83.04913080160064 + ], + [ + 76.95647177899504, + 76.16486549140681 + ], + [ + 76.77280066894052, + 6.301273122914296 + ], + [ + 76.35874703071867, + -0.29954911620753677 + ], + [ + 71.41469198102897, + -3.9887920872726426 + ], + [ + 61.00356715097897, + -6.125406402086433 + ], + [ + 37.27802033642641, + -7.922451580922152 + ], + [ + 18.256149021768273, + -6.850869494392456 + ], + [ + 3.2955764171578545, + -3.2161938062295943 + ], + [ + -0.04163211827899763, + -0.0451306156134037 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "ellipse", + "version": 6700, + "versionNonce": 950438331, + "isDeleted": false, + "id": "afFg1c-VqjZ6VB_2cnCgV", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1242.2827684130289, + "y": 387.6723986399642, + "strokeColor": "#5f3dc4", + "backgroundColor": "#7950f2", + "width": 76.50422544892463, + "height": 15.472404032124233, + "seed": 1054869515, + "groupIds": [ + "nC_wnGn6V9Cg51lj82An8", + "xEHH4p1jeHBH4PUaRrIni", + "JG_8M8qPMZ6QPLNYCIjBV" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312766628, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1907, + "versionNonce": 1915503477, + "isDeleted": false, + "id": "ymMTsrfERD1IWhRqaTh_I", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1261.2853467746677, + "y": 422.35935481481545, + "strokeColor": "#5f3dc4", + "backgroundColor": "#7950f2", + "width": 17.116666793823242, + "height": 36, + "seed": 621580837, + "groupIds": [ + "xEHH4p1jeHBH4PUaRrIni", + "JG_8M8qPMZ6QPLNYCIjBV" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "id": "D904AMBJU1YvbvQMXyluo", + "type": "arrow" + } + ], + "updated": 1688312766628, + "link": null, + "locked": false, + "fontSize": 29.219434366479078, + "fontFamily": 3, + "text": "{", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "{", + "lineHeight": 1.2320567040578883, + "baseline": 29 + }, + { + "type": "text", + "version": 1877, + "versionNonce": 1212389979, + "isDeleted": false, + "id": "j26zNzxtmoRXOdiX0ecde", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1283.2962201204132, + "y": 422.8360647867345, + "strokeColor": "#5f3dc4", + "backgroundColor": "#7950f2", + "width": 17.21666717529297, + "height": 35.94436319458793, + "seed": 1834536619, + "groupIds": [ + "xEHH4p1jeHBH4PUaRrIni", + "JG_8M8qPMZ6QPLNYCIjBV" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312766628, + "link": null, + "locked": false, + "fontSize": 29.384515916572173, + "fontFamily": 3, + "text": "}", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "}", + "lineHeight": 1.2232416316348487, + "baseline": 29 + }, + { + "type": "text", + "version": 1091, + "versionNonce": 52301013, + "isDeleted": false, + "id": "RVGdpt8fu7DFpzBrOmhVl", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1257.5750007629395, + "y": 489.46141030254137, + "strokeColor": "#5f3dc4", + "backgroundColor": "#7950f2", + "width": 46.849998474121094, + "height": 24, + "seed": 1281426821, + "groupIds": [ + "JG_8M8qPMZ6QPLNYCIjBV" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312766628, + "link": null, + "locked": false, + "fontSize": 18.16360832146819, + "fontFamily": 1, + "text": "txtai", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "txtai", + "lineHeight": 1.3213233612637187, + "baseline": 17 + }, + { + "type": "arrow", + "version": 835, + "versionNonce": 1915286293, + "isDeleted": false, + "id": "D904AMBJU1YvbvQMXyluo", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1126.546873152304, + "y": 442.58712580573655, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "width": 118.78773358450212, + "height": 0.33064816023818366, + "seed": 1187727819, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312763582, + "link": null, + "locked": false, + "startBinding": { + "elementId": "XUyFnqAl7kQAnMAoB7Cgx", + "focus": -0.009650513305614256, + "gap": 1.4886753513908815 + }, + "endBinding": { + "elementId": "ymMTsrfERD1IWhRqaTh_I", + "focus": -0.14572391577235705, + "gap": 15.950740037861578 + }, + "lastCommittedPoint": null, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 118.78773358450212, + 0.33064816023818366 + ] + ] + }, + { + "type": "rectangle", + "version": 906, + "versionNonce": 1466119355, + "isDeleted": false, + "id": "y5QaZJpFTfZABVw2mHLpE", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 429.6664767795138, + "y": 461.41041395399304, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 114.10871875077035, + "height": 118.76116853994468, + "seed": 1524581995, + "groupIds": [ + "nGNW7tJf4LAc_8aa1gg6P" + ], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "Iju4jXbzOeX44JCepZN3j", + "type": "arrow" + } + ], + "updated": 1688312763582, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 3201, + "versionNonce": 723765365, + "isDeleted": false, + "id": "v_UF3QajlZu6wJg9yMLih", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 449.4691219935195, + "y": 511.23981160653864, + "strokeColor": "#000000", + "backgroundColor": "#00e676", + "width": 71.91666412353516, + "height": 25.999999999999996, + "seed": 396443915, + "groupIds": [ + "nGNW7tJf4LAc_8aa1gg6P" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312763582, + "link": null, + "locked": false, + "fontSize": 19.322657964237056, + "fontFamily": 1, + "text": "txtai.js", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "txtai.js", + "lineHeight": 1.3455705756486278, + "baseline": 18 + }, + { + "type": "rectangle", + "version": 1005, + "versionNonce": 1613948251, + "isDeleted": false, + "id": "bOQD-uSjLkWjJgytmZ4oa", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 431.6664767795138, + "y": 619.410413953993, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 110.66704644097234, + "height": 115.17917209201391, + "seed": 347667589, + "groupIds": [ + "efl0w5KGoAkFRTYqTi7-b" + ], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "QyCYDQ1akZks7nob5gQcb", + "type": "arrow" + } + ], + "updated": 1688312763582, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 3264, + "versionNonce": 484241877, + "isDeleted": false, + "id": "XLzbZJT2I8ZRyPvDGlp6q", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 448.63655966829197, + "y": 667.7697356314092, + "strokeColor": "#000000", + "backgroundColor": "#ff7043", + "width": 76.5, + "height": 26, + "seed": 869344229, + "groupIds": [ + "efl0w5KGoAkFRTYqTi7-b" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312763582, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "txtai.rs", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "txtai.rs", + "lineHeight": 1.3, + "baseline": 18 + }, + { + "type": "rectangle", + "version": 1804, + "versionNonce": 1627619835, + "isDeleted": false, + "id": "NDWbJgM53uVck-LSQzD-0", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 428.6664767795138, + "y": 141.41041395399304, + "strokeColor": "#ffeb3b", + "backgroundColor": "#ffeb3b", + "width": 110.66704644097234, + "height": 115.17917209201391, + "seed": 1934943141, + "groupIds": [ + "6yaFhpagshturM5EK_Hay" + ], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "095ylTvsupuqCzyoolf44", + "type": "arrow" + } + ], + "updated": 1688312763582, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 3123, + "versionNonce": 1147793205, + "isDeleted": false, + "id": "DnwBnStWE5iZA-9cemLzh", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 445.63655966829197, + "y": 189.76973563140916, + "strokeColor": "#000000", + "backgroundColor": "#ffeb3b", + "width": 78.13333129882812, + "height": 52, + "seed": 547563269, + "groupIds": [ + "6yaFhpagshturM5EK_Hay" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312763583, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "txtai.go\n", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "txtai.go\n", + "lineHeight": 1.3, + "baseline": 44 + }, + { + "type": "ellipse", + "version": 1434, + "versionNonce": 47680155, + "isDeleted": false, + "id": "zt72wyfizv17KOzo8EnhU", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 748.2452959985687, + "y": 186.2837080711298, + "strokeColor": "#ffeb3b", + "backgroundColor": "#ffeb3b", + "width": 27.50940800286237, + "height": 23.432583857740315, + "seed": 1191665131, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "wtQWWSoRs-w6Dtmax-Hb5", + "type": "arrow" + }, + { + "id": "095ylTvsupuqCzyoolf44", + "type": "arrow" + } + ], + "updated": 1688312763583, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 198, + "versionNonce": 1613774997, + "isDeleted": false, + "id": "l2-_RHbfroK1-yNNP9pWJ", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 968.0163994686301, + "y": 373.9539804419734, + "strokeColor": "#ffeb3b", + "backgroundColor": "#4c6ef5", + "width": 186.00110275930913, + "height": 179.95398042998312, + "seed": 767528715, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312763583, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -1.0163994686301048, + -179.9539804299831 + ], + [ + -186.0011027593091, + -179.21342907018897 + ] + ] + }, + { + "type": "line", + "version": 251, + "versionNonce": 711032635, + "isDeleted": false, + "id": "7sDk3itWmD2GE1P-EYplQ", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 973.9999999999992, + "y": 513.9999999998802, + "strokeColor": "#ff7043", + "backgroundColor": "#4c6ef5", + "width": 197.9999999999992, + "height": 166.00000000011983, + "seed": 965983243, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312763583, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -1.010204081632649, + 166.00000000011983 + ], + [ + -197.9999999999992, + 166.00000000011983 + ] + ] + }, + { + "type": "arrow", + "version": 121, + "versionNonce": 1172661749, + "isDeleted": false, + "id": "095ylTvsupuqCzyoolf44", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 545.2882783422247, + "y": 201.14030329253615, + "strokeColor": "#ffeb3b", + "backgroundColor": "#ff7043", + "width": 198.68164012474733, + "height": 0.2787977820074161, + "seed": 4899205, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312763583, + "link": null, + "locked": false, + "startBinding": { + "elementId": "NDWbJgM53uVck-LSQzD-0", + "focus": 0.038606073098519975, + "gap": 5.954755121738572 + }, + "endBinding": { + "elementId": "zt72wyfizv17KOzo8EnhU", + "focus": -0.242073274518529, + "gap": 4.559216593972247 + }, + "lastCommittedPoint": null, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 198.68164012474733, + -0.2787977820074161 + ] + ] + }, + { + "type": "arrow", + "version": 67, + "versionNonce": 33808347, + "isDeleted": false, + "id": "_UxQXW5nkuKOXtaavoJ3q", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 545.2448106156662, + "y": 362.4655547515135, + "strokeColor": "#03a9f4", + "backgroundColor": "#ff7043", + "width": 195.75519089085594, + "height": 1.5344470050185919, + "seed": 180453803, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312763583, + "link": null, + "locked": false, + "startBinding": { + "elementId": "np05zgjgJ1cvT_UcHRO4Q", + "focus": 0.08624654954364566, + "gap": 4.130193197765266 + }, + "endBinding": { + "elementId": "OwUTZKSXPIR8EHoAXUrAc", + "focus": 0.07905353643606809, + "gap": 7.545863589033182 + }, + "lastCommittedPoint": null, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 195.75519089085594, + 1.5344470050185919 + ] + ] + }, + { + "type": "arrow", + "version": 146, + "versionNonce": 2022629205, + "isDeleted": false, + "id": "Iju4jXbzOeX44JCepZN3j", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 547.8284140902045, + "y": 520.9148281821533, + "strokeColor": "#00e676", + "backgroundColor": "#ff7043", + "width": 195.16671561089288, + "height": 3.1847497390683657, + "seed": 1277347109, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312763583, + "link": null, + "locked": false, + "startBinding": { + "elementId": "y5QaZJpFTfZABVw2mHLpE", + "focus": -0.01448029053565506, + "gap": 4.053218559920424 + }, + "endBinding": { + "elementId": "qfK-ulSZh44Bl6KC9Ii6F", + "focus": 0.03022905067490028, + "gap": 4.0079533720564715 + }, + "lastCommittedPoint": null, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 195.16671561089288, + 3.1847497390683657 + ] + ] + }, + { + "type": "arrow", + "version": 551, + "versionNonce": 1137781883, + "isDeleted": false, + "id": "QyCYDQ1akZks7nob5gQcb", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 548.0000000000001, + "y": 679.9183352218894, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 190.09617369448893, + "height": 1.4298124428980827, + "seed": 1602091877, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312763583, + "link": null, + "locked": false, + "startBinding": { + "elementId": "bOQD-uSjLkWjJgytmZ4oa", + "focus": 0.05822088981723922, + "gap": 5.666476779514028 + }, + "endBinding": { + "elementId": "xcu0cSh50RePPTdayuKwa", + "focus": -0.14162811640911155, + "gap": 6.9174857452546 + }, + "lastCommittedPoint": null, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 190.09617369448893, + -1.4298124428980827 + ] + ] + }, + { + "type": "line", + "version": 139, + "versionNonce": 2122460341, + "isDeleted": false, + "id": "HKLfE2F8l78a_hZEKhmnY", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 956, + "y": 497, + "strokeColor": "#00e676", + "backgroundColor": "#ced4da", + "width": 180, + "height": 29, + "seed": 597910187, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312763583, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -45, + 0 + ], + [ + -44, + 29 + ], + [ + -180, + 29 + ] + ] + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/images/api.png b/docs/images/api.png new file mode 100644 index 0000000..2836cd5 Binary files /dev/null and b/docs/images/api.png differ diff --git a/docs/images/architecture-dark.png b/docs/images/architecture-dark.png new file mode 100644 index 0000000..cf3b638 Binary files /dev/null and b/docs/images/architecture-dark.png differ diff --git a/docs/images/architecture.excalidraw b/docs/images/architecture.excalidraw new file mode 100644 index 0000000..89ccae8 --- /dev/null +++ b/docs/images/architecture.excalidraw @@ -0,0 +1,996 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 4858, + "versionNonce": 83494474, + "isDeleted": false, + "id": "1iE4X4trkcbtJpCDC5ajY", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 27.525089187647836, + "y": -558.1200877100962, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 227.65938859429855, + "height": 57.830603028426594, + "seed": 1321467630, + "groupIds": [ + "MnVd9k06uFuFRzqXmnEvR", + "NFKrTdLl_zy4zOvDGWVBD" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "qROQdhT3FWGzgS0JOQXlg", + "type": "arrow" + } + ], + "updated": 1691411991785, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 5224, + "versionNonce": 1682430806, + "isDeleted": false, + "id": "ZiUda1q4lvcNM9G2Or_d7", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 39.103253497881326, + "y": -548.6024245205953, + "strokeColor": "#1e1e1e", + "backgroundColor": "#343a40", + "width": 210.71665954589844, + "height": 40, + "seed": 2050033394, + "groupIds": [ + "NFKrTdLl_zy4zOvDGWVBD" + ], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "qROQdhT3FWGzgS0JOQXlg", + "type": "arrow" + } + ], + "updated": 1691411991785, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "- Semantic, Keyword, Hybrid\n- Search with SQL", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- Semantic, Keyword, Hybrid\n- Search with SQL", + "lineHeight": 1.25, + "baseline": 35 + }, + { + "type": "rectangle", + "version": 2527, + "versionNonce": 2136048394, + "isDeleted": false, + "id": "i9TRMGNTF_ckjPiI7BrdL", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 358.6031681551546, + "y": -598.4332496538902, + "strokeColor": "#868e96", + "backgroundColor": "#e9ecef", + "width": 348.08489527005264, + "height": 200.0037909410919, + "seed": 1867912238, + "groupIds": [ + "cNL6-_r-I9wctWvuw8DqS" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "viSfAjT-8KRTYvsrunjfn", + "type": "arrow" + }, + { + "id": "CJaOip-NXs_WGJ3YnzrzX", + "type": "arrow" + }, + { + "id": "sf5i6bxPYE2ykeh2wWm8m", + "type": "arrow" + } + ], + "updated": 1691411996548, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 1915, + "versionNonce": 211681430, + "isDeleted": false, + "id": "F1r0Plm7hDaeNndIwZAuf", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 368.61999967371725, + "y": -538.0801566026939, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 160.26930429700263, + "height": 62.605196991016655, + "seed": 436869746, + "groupIds": [ + "cNL6-_r-I9wctWvuw8DqS" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "gw7zjxlDiAjodT1x1_fv_" + }, + { + "id": "qROQdhT3FWGzgS0JOQXlg", + "type": "arrow" + }, + { + "id": "viSfAjT-8KRTYvsrunjfn", + "type": "arrow" + } + ], + "updated": 1691411991785, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1897, + "versionNonce": 347110346, + "isDeleted": false, + "id": "gw7zjxlDiAjodT1x1_fv_", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 406.8879869357439, + "y": -522.695916613224, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 83.73332977294922, + "height": 31.836717012076832, + "seed": 1023300530, + "groupIds": [ + "cNL6-_r-I9wctWvuw8DqS" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1691411991785, + "link": null, + "locked": false, + "fontSize": 25.469373609661467, + "fontFamily": 1, + "text": "Sparse", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "F1r0Plm7hDaeNndIwZAuf", + "originalText": "Sparse", + "lineHeight": 1.25, + "baseline": 22 + }, + { + "type": "rectangle", + "version": 2272, + "versionNonce": 696090070, + "isDeleted": false, + "id": "55xWndBPWTiyUuLIS_J8b", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 369.8721036135376, + "y": -469.46654385239594, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 157.76509641736197, + "height": 57.59678123173532, + "seed": 1317384558, + "groupIds": [ + "cNL6-_r-I9wctWvuw8DqS" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "Qaeh-leNa7YgcPsQbfBcq" + }, + { + "id": "qROQdhT3FWGzgS0JOQXlg", + "type": "arrow" + }, + { + "id": "viSfAjT-8KRTYvsrunjfn", + "type": "arrow" + }, + { + "id": "CJaOip-NXs_WGJ3YnzrzX", + "type": "arrow" + } + ], + "updated": 1691411991785, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2210, + "versionNonce": 1434612362, + "isDeleted": false, + "id": "Qaeh-leNa7YgcPsQbfBcq", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 412.012986935744, + "y": -456.59111621253714, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 73.48332977294922, + "height": 31.84592595201767, + "seed": 1661259762, + "groupIds": [ + "cNL6-_r-I9wctWvuw8DqS" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1691411991785, + "link": null, + "locked": false, + "fontSize": 25.476740761614135, + "fontFamily": 1, + "text": "Dense", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "55xWndBPWTiyUuLIS_J8b", + "originalText": "Dense", + "lineHeight": 1.25, + "baseline": 22 + }, + { + "type": "rectangle", + "version": 2356, + "versionNonce": 268129046, + "isDeleted": false, + "id": "KSf88Asx9gne1WsRdsx6-", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 535.1498236698217, + "y": -469.46654385239594, + "strokeColor": "#ffeb3b", + "backgroundColor": "#ffeb3b", + "width": 156.51299247754164, + "height": 57.59678123173532, + "seed": 1016194418, + "groupIds": [ + "cNL6-_r-I9wctWvuw8DqS" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "7RaoYDeBRuz16CJo7jaVO" + }, + { + "id": "CJaOip-NXs_WGJ3YnzrzX", + "type": "arrow" + } + ], + "updated": 1691411991785, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2343, + "versionNonce": 2120716618, + "isDeleted": false, + "id": "7RaoYDeBRuz16CJo7jaVO", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 576.8063214344714, + "y": -456.5934785058263, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 73.19999694824219, + "height": 31.850650538596007, + "seed": 1307924782, + "groupIds": [ + "cNL6-_r-I9wctWvuw8DqS" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1691411991785, + "link": null, + "locked": false, + "fontSize": 25.480520430876805, + "fontFamily": 1, + "text": "Graph", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "KSf88Asx9gne1WsRdsx6-", + "originalText": "Graph", + "lineHeight": 1.25, + "baseline": 22 + }, + { + "type": "text", + "version": 1738, + "versionNonce": 713850966, + "isDeleted": false, + "id": "gAcYCRjD6VZUsDZce-atm", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 439.98992424347625, + "y": -595.9290417742495, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffeb3b", + "width": 183.39999389648438, + "height": 43.823637893711656, + "seed": 1369566386, + "groupIds": [ + "cNL6-_r-I9wctWvuw8DqS" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1691411991786, + "link": null, + "locked": false, + "fontSize": 35.058910314969324, + "fontFamily": 1, + "text": "Embeddings", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Embeddings", + "lineHeight": 1.25, + "baseline": 31 + }, + { + "type": "rectangle", + "version": 2094, + "versionNonce": 704590410, + "isDeleted": false, + "id": "UEE-Z2YT9Y5YvVCJefbd0", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 533.8977197300013, + "y": -535.8280526628736, + "strokeColor": "#7950f2", + "backgroundColor": "#7950f2", + "width": 160.26930429700263, + "height": 60.10098911137599, + "seed": 1015954930, + "groupIds": [ + "cNL6-_r-I9wctWvuw8DqS" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "Y1uf_NH7fI6arCSGiICiX" + }, + { + "id": "CJaOip-NXs_WGJ3YnzrzX", + "type": "arrow" + } + ], + "updated": 1691412007529, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2084, + "versionNonce": 1292240278, + "isDeleted": false, + "id": "Y1uf_NH7fI6arCSGiICiX", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 551.0823711155632, + "y": -521.695916613224, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 125.9000015258789, + "height": 31.836717012076832, + "seed": 867545010, + "groupIds": [ + "cNL6-_r-I9wctWvuw8DqS" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1691411991786, + "link": null, + "locked": false, + "fontSize": 25.469373609661467, + "fontFamily": 1, + "text": "Database", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "UEE-Z2YT9Y5YvVCJefbd0", + "originalText": "Database", + "lineHeight": 1.25, + "baseline": 22 + }, + { + "type": "rectangle", + "version": 4488, + "versionNonce": 1920419530, + "isDeleted": false, + "id": "uV8iktEBp-zvkGceO0YNj", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 28.400291679657585, + "y": -482.37846781152234, + "strokeColor": "#ffeb3b", + "backgroundColor": "#ffeb3b", + "width": 226.65938859429855, + "height": 53.830603028426594, + "seed": 511161586, + "groupIds": [ + "lsnRF8t7e-8bA4k7ZMtQs", + "XOEGmXNmM0vVhFi_aLf0h" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "viSfAjT-8KRTYvsrunjfn", + "type": "arrow" + } + ], + "updated": 1691411991786, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 4865, + "versionNonce": 144499414, + "isDeleted": false, + "id": "VU0ZFGV4tB5pR_8AMPehi", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 38.17389738827433, + "y": -477.32443222094344, + "strokeColor": "#1e1e1e", + "backgroundColor": "#343a40", + "width": 190.39999389648438, + "height": 40, + "seed": 660096690, + "groupIds": [ + "XOEGmXNmM0vVhFi_aLf0h" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1691411991786, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "- Topics + Relationships\n- Multimodal Indexes", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- Topics + Relationships\n- Multimodal Indexes", + "lineHeight": 1.25, + "baseline": 35 + }, + { + "type": "arrow", + "version": 3140, + "versionNonce": 1465895050, + "isDeleted": false, + "id": "qROQdhT3FWGzgS0JOQXlg", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 357.5459819116054, + "y": -474.380420640035, + "strokeColor": "#868e96", + "backgroundColor": "#e9ecef", + "width": 101.91733646013165, + "height": 51.182713481535075, + "seed": 1412373166, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1691412001500, + "link": null, + "locked": false, + "startBinding": { + "elementId": "55xWndBPWTiyUuLIS_J8b", + "focus": -0.17593583244727756, + "gap": 13.269493633705139 + }, + "endBinding": { + "elementId": "ZiUda1q4lvcNM9G2Or_d7", + "focus": -0.7240167179756973, + "gap": 5.808732407693981 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -101.91733646013165, + -51.182713481535075 + ] + ] + }, + { + "type": "arrow", + "version": 2221, + "versionNonce": 297446922, + "isDeleted": false, + "id": "viSfAjT-8KRTYvsrunjfn", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 355.69072075221885, + "y": -473.6934438187176, + "strokeColor": "#868e96", + "backgroundColor": "#e9ecef", + "width": 99.11528692741001, + "height": 28.941684185339795, + "seed": 769791342, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1691412005709, + "link": null, + "locked": false, + "startBinding": { + "elementId": "55xWndBPWTiyUuLIS_J8b", + "focus": 1.1614221413579826, + "gap": 14.797915501333051 + }, + "endBinding": { + "elementId": "uV8iktEBp-zvkGceO0YNj", + "focus": 0.737345008582056, + "gap": 1.5157535508527076 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -99.11528692741001, + 28.941684185339795 + ] + ] + }, + { + "type": "arrow", + "version": 5939, + "versionNonce": 1648005846, + "isDeleted": false, + "id": "CJaOip-NXs_WGJ3YnzrzX", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 780.2644916835155, + "y": -444.3627069906215, + "strokeColor": "#868e96", + "backgroundColor": "#e9ecef", + "width": 71.1891690063859, + "height": 28.06038115974593, + "seed": 1923467222, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1691411901454, + "link": null, + "locked": false, + "startBinding": { + "elementId": "SKqPLoIsGXgDL5mhOTqLv", + "focus": -0.634641820715333, + "gap": 5.063962132776453 + }, + "endBinding": { + "elementId": "UEE-Z2YT9Y5YvVCJefbd0", + "focus": -0.06665280707924877, + "gap": 15.270023644140622 + }, + "lastCommittedPoint": null, + "startArrowhead": "arrow", + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -71.1891690063859, + -28.06038115974593 + ] + ] + }, + { + "type": "rectangle", + "version": 3429, + "versionNonce": 348832458, + "isDeleted": false, + "id": "7UwPwHCm_ZiMJb1o0x5A8", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 783.2131987136702, + "y": -574.1983152922421, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 272.0848952700526, + "height": 73.0037909410919, + "seed": 155582550, + "groupIds": [ + "kgkwY28q5i7W008rJksMR", + "TqYTD07S3yHeAVJOG3yyi" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [], + "updated": 1691411901455, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1591, + "versionNonce": 72296150, + "isDeleted": false, + "id": "fFWZLl9F8IwK5QhXlAssA", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 792.6342330724652, + "y": -569.5895346298003, + "strokeColor": "#1e1e1e", + "backgroundColor": "#343a40", + "width": 77.1500015258789, + "height": 20, + "seed": 543556438, + "groupIds": [ + "TqYTD07S3yHeAVJOG3yyi" + ], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "CJaOip-NXs_WGJ3YnzrzX", + "type": "arrow" + } + ], + "updated": 1691411901455, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": " Prompt >", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": " Prompt >", + "lineHeight": 1.25, + "baseline": 15 + }, + { + "type": "text", + "version": 1341, + "versionNonce": 450634262, + "isDeleted": false, + "id": "FgFcgIY3s9iRdPKwN4t_r", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 785.1996772966362, + "y": -548.1590294764883, + "strokeColor": "#1e1e1e", + "backgroundColor": "#343a40", + "width": 261.5, + "height": 41.41205162166277, + "seed": 423650058, + "groupIds": [ + "TqYTD07S3yHeAVJOG3yyi" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1691412007529, + "link": null, + "locked": false, + "fontSize": 16.56482064866511, + "fontFamily": 1, + "text": " Answer the following question\n using the context below", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": " Answer the following question\n using the context below", + "lineHeight": 1.25, + "baseline": 35 + }, + { + "type": "rectangle", + "version": 3467, + "versionNonce": 1420811286, + "isDeleted": false, + "id": "_8pPaWBfdkvpd8B9PQ7wI", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 784.2131987136702, + "y": -487.1983152922421, + "strokeColor": "#6741d9", + "backgroundColor": "#6741d9", + "width": 272.0848952700526, + "height": 73.0037909410919, + "seed": 2066566410, + "groupIds": [ + "FtTm_MWbxe67f2n84saes", + "3PAnqhix8z46z6JRJjAO7" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "CJaOip-NXs_WGJ3YnzrzX", + "type": "arrow" + } + ], + "updated": 1691411901455, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1602, + "versionNonce": 1943803978, + "isDeleted": false, + "id": "apAxY8HSw8w2lfLe1pCXP", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 792.7130504007473, + "y": -482.33339890257247, + "strokeColor": "#1e1e1e", + "backgroundColor": "#6741d9", + "width": 75.73332977294922, + "height": 20, + "seed": 1870973578, + "groupIds": [ + "3PAnqhix8z46z6JRJjAO7" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1691411901455, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": " Search >", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": " Search >", + "lineHeight": 1.25, + "baseline": 15 + }, + { + "type": "text", + "version": 1069, + "versionNonce": 1506273622, + "isDeleted": false, + "id": "SKqPLoIsGXgDL5mhOTqLv", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 785.3284538162922, + "y": -458.46265045437633, + "strokeColor": "#1e1e1e", + "backgroundColor": "#6741d9", + "width": 251.21665954589844, + "height": 41.82578128069407, + "seed": 1533237462, + "groupIds": [ + "3PAnqhix8z46z6JRJjAO7" + ], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "CJaOip-NXs_WGJ3YnzrzX", + "type": "arrow" + } + ], + "updated": 1691411901455, + "link": null, + "locked": false, + "fontSize": 16.730312512277628, + "fontFamily": 1, + "text": " SELECT ... FROM txtai\n WHERE SIMILAR('question')", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": " SELECT ... FROM txtai\n WHERE SIMILAR('question')", + "lineHeight": 1.25, + "baseline": 35 + }, + { + "type": "arrow", + "version": 5999, + "versionNonce": 1002058634, + "isDeleted": false, + "id": "sf5i6bxPYE2ykeh2wWm8m", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 780.8522503409429, + "y": -537.8502934180265, + "strokeColor": "#868e96", + "backgroundColor": "#e9ecef", + "width": 72.32385186561339, + "height": 64.0647548382936, + "seed": 1598499018, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1691412007529, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": "arrow", + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -72.32385186561339, + 64.0647548382936 + ] + ] + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/images/architecture.png b/docs/images/architecture.png new file mode 100644 index 0000000..4067593 Binary files /dev/null and b/docs/images/architecture.png differ diff --git a/docs/images/cloud-dark.png b/docs/images/cloud-dark.png new file mode 100644 index 0000000..f2f06d2 Binary files /dev/null and b/docs/images/cloud-dark.png differ diff --git a/docs/images/cloud.excalidraw b/docs/images/cloud.excalidraw new file mode 100644 index 0000000..48f2f6d --- /dev/null +++ b/docs/images/cloud.excalidraw @@ -0,0 +1,379 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 3062, + "versionNonce": 456539957, + "isDeleted": false, + "id": "Ufh5VUA3qmvJowuFyEWz4", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 570.7515859513394, + "y": 59.768341009859626, + "strokeColor": "#d0d9dd", + "backgroundColor": "transparent", + "width": 719.330217768941, + "height": 100.49478848195537, + "seed": 2109972213, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312603126, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 1798, + "versionNonce": 1560857173, + "isDeleted": false, + "id": "NDWbJgM53uVck-LSQzD-0", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 740.6664767795138, + "y": 67.92614920483035, + "strokeColor": "#ffeb3b", + "backgroundColor": "#ffeb3b", + "width": 85, + "height": 84.17917209201391, + "seed": 1782446165, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "0VztU7lb7T9I5zoL2LjLt" + } + ], + "updated": 1688312605190, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 385, + "versionNonce": 1280754907, + "isDeleted": false, + "id": "0VztU7lb7T9I5zoL2LjLt", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 745.6664767795138, + "y": 99.51573525083731, + "strokeColor": "#000000", + "backgroundColor": "#7950f2", + "width": 75, + "height": 21, + "seed": 2095457717, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312605190, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Container", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "NDWbJgM53uVck-LSQzD-0", + "originalText": "Container", + "lineHeight": 1.3125, + "baseline": 15 + }, + { + "type": "rectangle", + "version": 1874, + "versionNonce": 847689653, + "isDeleted": false, + "id": "rr3YJsAyrrPqZbZ4qXh5x", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1175.874140249897, + "y": 67.92614920483035, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 85, + "height": 84.17917209201391, + "seed": 1491183381, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "9NBdG6TyrBqjuDKvzAZXN", + "type": "text" + } + ], + "updated": 1688312605190, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 456, + "versionNonce": 1820291451, + "isDeleted": false, + "id": "9NBdG6TyrBqjuDKvzAZXN", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1180.874140249897, + "y": 99.51573525083731, + "strokeColor": "#000000", + "backgroundColor": "#7950f2", + "width": 75, + "height": 21, + "seed": 547748981, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312605190, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Container", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "rr3YJsAyrrPqZbZ4qXh5x", + "originalText": "Container", + "lineHeight": 1.3125, + "baseline": 15 + }, + { + "type": "rectangle", + "version": 1864, + "versionNonce": 1889532181, + "isDeleted": false, + "id": "ji9UBE590YJ5e8DKUKbPG", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 598.874140249897, + "y": 67.92614920483035, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 85, + "height": 84.17917209201391, + "seed": 200844757, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "2fdy7dRwF7ZVaYPrb1Q7p", + "type": "text" + } + ], + "updated": 1688312605190, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 446, + "versionNonce": 673810971, + "isDeleted": false, + "id": "2fdy7dRwF7ZVaYPrb1Q7p", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 603.874140249897, + "y": 99.51573525083731, + "strokeColor": "#000000", + "backgroundColor": "#7950f2", + "width": 75, + "height": 21, + "seed": 1039082293, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312605190, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Container", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ji9UBE590YJ5e8DKUKbPG", + "originalText": "Container", + "lineHeight": 1.3125, + "baseline": 15 + }, + { + "type": "rectangle", + "version": 1805, + "versionNonce": 281677429, + "isDeleted": false, + "id": "Kod3erHdgkFtJcsgqHeQQ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 889.874140249897, + "y": 67.92614920483035, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 85, + "height": 84.17917209201391, + "seed": 1525609621, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "vA5QfvPySVZE9WnP6d7dp", + "type": "text" + } + ], + "updated": 1688312605190, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 390, + "versionNonce": 1682411195, + "isDeleted": false, + "id": "vA5QfvPySVZE9WnP6d7dp", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 894.874140249897, + "y": 99.51573525083731, + "strokeColor": "#000000", + "backgroundColor": "#7950f2", + "width": 75, + "height": 21, + "seed": 1588435445, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312605190, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Container", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Kod3erHdgkFtJcsgqHeQQ", + "originalText": "Container", + "lineHeight": 1.3125, + "baseline": 15 + }, + { + "type": "rectangle", + "version": 1916, + "versionNonce": 1103787989, + "isDeleted": false, + "id": "8p_AIsxC6UbaWESkrwl4g", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1035.874140249897, + "y": 67.92614920483035, + "strokeColor": "#7950f2", + "backgroundColor": "#7950f2", + "width": 85, + "height": 84.17917209201391, + "seed": 842044245, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "AceuLTPujneYjRp_njhIl", + "type": "text" + } + ], + "updated": 1688312605190, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 499, + "versionNonce": 1132199771, + "isDeleted": false, + "id": "AceuLTPujneYjRp_njhIl", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1041.907473074604, + "y": 99.51573525083731, + "strokeColor": "#000000", + "backgroundColor": "#7950f2", + "width": 72.93333435058594, + "height": 21, + "seed": 30236853, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312605190, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Container", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "8p_AIsxC6UbaWESkrwl4g", + "originalText": "Container", + "lineHeight": 1.3125, + "baseline": 15 + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/images/cloud.png b/docs/images/cloud.png new file mode 100644 index 0000000..f253033 Binary files /dev/null and b/docs/images/cloud.png differ diff --git a/docs/images/embeddings-dark.png b/docs/images/embeddings-dark.png new file mode 100644 index 0000000..5a39337 Binary files /dev/null and b/docs/images/embeddings-dark.png differ diff --git a/docs/images/embeddings.excalidraw b/docs/images/embeddings.excalidraw new file mode 100644 index 0000000..f4ac5ec --- /dev/null +++ b/docs/images/embeddings.excalidraw @@ -0,0 +1,513 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 824, + "versionNonce": 1352316119, + "isDeleted": false, + "id": "U2NgEIEiFpAlwmv5Xnyzr", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 50, + "angle": 0, + "x": 557.5, + "y": 267.30499999999995, + "strokeColor": "#7950f2", + "backgroundColor": "#7950f2", + "width": 995.3286713286714, + "height": 339.0000000000001, + "seed": 1946478225, + "groupIds": [ + "C_65R9XVeMQED2nMfIW2D" + ], + "roundness": null, + "boundElements": [], + "updated": 1673788613149, + "link": "", + "locked": false + }, + { + "type": "text", + "version": 607, + "versionNonce": 590931993, + "isDeleted": false, + "id": "fbSO8bnZmAdvIXZxBAtHY", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 572.7237762237763, + "y": 281.50080419580416, + "strokeColor": "#000", + "backgroundColor": "#03a9f4", + "width": 967, + "height": 312, + "seed": 1586673137, + "groupIds": [ + "C_65R9XVeMQED2nMfIW2D" + ], + "roundness": null, + "boundElements": [], + "updated": 1673788613149, + "link": null, + "locked": false, + "fontSize": 20.27972027972028, + "fontFamily": 1, + "text": "Query Best Match\n----------------------------------------------------\nfeel good story Maine man wins $1M from $25 lottery ticket\nclimate change Canada's last fully intact ice shelf has suddenly collapsed, forming a \n Manhattan-sized iceberg\npublic health story US tops 5 million confirmed virus cases\nwar Beijing mobilises invasion craft along coast as Taiwan tensions escalate\nwildlife The National Park Service warns against sacrificing slower friends in a\n bear attack\nasia Beijing mobilises invasion craft along coast as Taiwan tensions escalate\nlucky Maine man wins $1M from $25 lottery ticket\ndishonest junk Make huge profits without work, earn up to $100,000 a day", + "baseline": 304, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Query Best Match\n----------------------------------------------------\nfeel good story Maine man wins $1M from $25 lottery ticket\nclimate change Canada's last fully intact ice shelf has suddenly collapsed, forming a \n Manhattan-sized iceberg\npublic health story US tops 5 million confirmed virus cases\nwar Beijing mobilises invasion craft along coast as Taiwan tensions escalate\nwildlife The National Park Service warns against sacrificing slower friends in a\n bear attack\nasia Beijing mobilises invasion craft along coast as Taiwan tensions escalate\nlucky Maine man wins $1M from $25 lottery ticket\ndishonest junk Make huge profits without work, earn up to $100,000 a day" + }, + { + "type": "rectangle", + "version": 990, + "versionNonce": 2000421367, + "isDeleted": false, + "id": "UO6MS3wSDu7yg2421__LI", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 935, + "y": 109, + "strokeColor": "#ffeb3b", + "backgroundColor": "#ffeb3b", + "width": 214, + "height": 49, + "seed": 1629565989, + "groupIds": [ + "3sURMvhuRfR0M-Q3VRPbg" + ], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "8sp7H8ijWBlh6aMgZ0XTP" + }, + { + "id": "Qzp41i_jzQIBlAB_qFKFH", + "type": "arrow" + }, + { + "id": "SJ0F0Y81z9hir5qQWAJjk", + "type": "arrow" + } + ], + "updated": 1673788613149, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 1649, + "versionNonce": 1978562009, + "isDeleted": false, + "id": "qYd3q0Vjks7VOHUC9RR51", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 551, + "y": 109.5, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 219, + "height": 52, + "seed": 1441952427, + "groupIds": [ + "3sURMvhuRfR0M-Q3VRPbg" + ], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "WPeWn6N4rCHf0jY16N9Ge" + }, + { + "id": "Qzp41i_jzQIBlAB_qFKFH", + "type": "arrow" + } + ], + "updated": 1673788613149, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1336, + "versionNonce": 1274169655, + "isDeleted": false, + "id": "WPeWn6N4rCHf0jY16N9Ge", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 597.5, + "y": 117.5, + "strokeColor": "#000", + "backgroundColor": "#fa5252", + "width": 126, + "height": 36, + "seed": 870516459, + "groupIds": [ + "3sURMvhuRfR0M-Q3VRPbg" + ], + "roundness": null, + "boundElements": [], + "updated": 1673788618421, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Vectorize", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "qYd3q0Vjks7VOHUC9RR51", + "originalText": "Vectorize" + }, + { + "type": "rectangle", + "version": 1236, + "versionNonce": 368709975, + "isDeleted": false, + "id": "5VuUdI_BsJ5pyE1nTqJUI", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1334, + "y": 110, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 218, + "height": 49, + "seed": 1044404613, + "groupIds": [ + "3sURMvhuRfR0M-Q3VRPbg" + ], + "roundness": null, + "boundElements": [ + { + "id": "bJJ9SGsJsvT071qBBH0w5", + "type": "text" + }, + { + "id": "SJ0F0Y81z9hir5qQWAJjk", + "type": "arrow" + } + ], + "updated": 1673788613149, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1420, + "versionNonce": 1511737785, + "isDeleted": false, + "id": "bJJ9SGsJsvT071qBBH0w5", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1397, + "y": 116.5, + "strokeColor": "#000", + "backgroundColor": "#fa5252", + "width": 92, + "height": 36, + "seed": 128953675, + "groupIds": [ + "3sURMvhuRfR0M-Q3VRPbg" + ], + "roundness": null, + "boundElements": [], + "updated": 1673788618421, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Search", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "5VuUdI_BsJ5pyE1nTqJUI", + "originalText": "Search" + }, + { + "type": "text", + "version": 981, + "versionNonce": 1600260695, + "isDeleted": false, + "id": "8sp7H8ijWBlh6aMgZ0XTP", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1004, + "y": 115.5, + "strokeColor": "#000", + "backgroundColor": "transparent", + "width": 76, + "height": 36, + "seed": 1854823263, + "groupIds": [ + "3sURMvhuRfR0M-Q3VRPbg" + ], + "roundness": null, + "boundElements": [], + "updated": 1673788618422, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Index", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "UO6MS3wSDu7yg2421__LI", + "originalText": "Index" + }, + { + "type": "text", + "version": 420, + "versionNonce": 1275651991, + "isDeleted": false, + "id": "jWJpSXHkTCzRTCA4tbAgv", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 550.5, + "y": 189.30499999999995, + "strokeColor": "#000", + "backgroundColor": "#03a9f4", + "width": 296, + "height": 42, + "seed": 1241563487, + "groupIds": [ + "3sURMvhuRfR0M-Q3VRPbg" + ], + "roundness": null, + "boundElements": [], + "updated": 1673788613150, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "- Transform input into numbers\n- Similar concepts have similar values", + "baseline": 36, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- Transform input into numbers\n- Similar concepts have similar values" + }, + { + "type": "text", + "version": 419, + "versionNonce": 51134809, + "isDeleted": false, + "id": "qEnmXs0P_MQE8r4c4OWGh", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 933.5, + "y": 188.20749999999992, + "strokeColor": "#000", + "backgroundColor": "#f44336", + "width": 230, + "height": 42, + "seed": 1038536465, + "groupIds": [ + "3sURMvhuRfR0M-Q3VRPbg" + ], + "roundness": null, + "boundElements": [], + "updated": 1673788613150, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "- Save vectors\n- Store content with vectors", + "baseline": 36, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- Save vectors\n- Store content with vectors" + }, + { + "type": "text", + "version": 529, + "versionNonce": 869653687, + "isDeleted": false, + "id": "1q8bzjK8lnKUZj8_A9v7D", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1245, + "y": 191.20749999999992, + "strokeColor": "#000", + "backgroundColor": "#f44336", + "width": 333, + "height": 42, + "seed": 304472945, + "groupIds": [ + "3sURMvhuRfR0M-Q3VRPbg" + ], + "roundness": null, + "boundElements": [], + "updated": 1673788613150, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "- Find similar vectors with cosine similarity\n- Add rule-based filters using content", + "baseline": 36, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- Find similar vectors with cosine similarity\n- Add rule-based filters using content" + }, + { + "type": "arrow", + "version": 1935, + "versionNonce": 20011767, + "isDeleted": false, + "id": "Qzp41i_jzQIBlAB_qFKFH", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 772.5, + "y": 131.8470411964629, + "strokeColor": "#000", + "backgroundColor": "#f44336", + "width": 158.1310513485223, + "height": 0.5692601572380909, + "seed": 660786897, + "groupIds": [ + "3sURMvhuRfR0M-Q3VRPbg" + ], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1673788613309, + "link": null, + "locked": false, + "startBinding": { + "elementId": "qYd3q0Vjks7VOHUC9RR51", + "focus": -0.15367587596362536, + "gap": 2.5 + }, + "endBinding": { + "elementId": "UO6MS3wSDu7yg2421__LI", + "focus": 0.027437144815141, + "gap": 4.3689486514776945 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 158.1310513485223, + 0.5692601572380909 + ] + ] + }, + { + "type": "arrow", + "version": 2084, + "versionNonce": 1180230679, + "isDeleted": false, + "id": "SJ0F0Y81z9hir5qQWAJjk", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1151.5, + "y": 134.67907617019108, + "strokeColor": "#000", + "backgroundColor": "#f44336", + "width": 181.5, + "height": 1.5898915058209013, + "seed": 899541905, + "groupIds": [ + "3sURMvhuRfR0M-Q3VRPbg" + ], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1673788613309, + "link": null, + "locked": false, + "startBinding": { + "elementId": "UO6MS3wSDu7yg2421__LI", + "focus": 0.08406032225724415, + "gap": 2.5 + }, + "endBinding": { + "elementId": "5VuUdI_BsJ5pyE1nTqJUI", + "focus": 0.09327847520504394, + "gap": 1 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 181.5, + -1.5898915058209013 + ] + ] + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/images/embeddings.png b/docs/images/embeddings.png new file mode 100644 index 0000000..69fa1f8 Binary files /dev/null and b/docs/images/embeddings.png differ diff --git a/docs/images/examples-dark.png b/docs/images/examples-dark.png new file mode 100644 index 0000000..b4c8f6a Binary files /dev/null and b/docs/images/examples-dark.png differ diff --git a/docs/images/examples.excalidraw b/docs/images/examples.excalidraw new file mode 100644 index 0000000..cd072ab --- /dev/null +++ b/docs/images/examples.excalidraw @@ -0,0 +1,1187 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 658, + "versionNonce": 56883131, + "isDeleted": false, + "id": "28knmXv7UWxhQgfO1Ap_e", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 843.2544325440515, + "y": 565.0365494150617, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "width": 131.4290956134916, + "height": 19.163298353951955, + "seed": 333697800, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176184, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 759, + "versionNonce": 1321514357, + "isDeleted": false, + "id": "NWB9SsDdfRnKFM7WSMiDD", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 843.094785904982, + "y": 596.6192561492705, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "width": 236.33086957943124, + "height": 8.353548393013705, + "seed": 1799143800, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176184, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 873, + "versionNonce": 1965246555, + "isDeleted": false, + "id": "o8mRXU1Pv0Lht3Kvw3LjM", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 843.9010630546302, + "y": 628.3064112463869, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "width": 172.46335772731075, + "height": 10.760364257767161, + "seed": 84161032, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176184, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 852, + "versionNonce": 1473789653, + "isDeleted": false, + "id": "7XXBg9rngZPBd8BY3k-Dk", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 50, + "angle": 0, + "x": 820.9965701219508, + "y": 409.40769144764795, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "width": 772.0068597560986, + "height": 326.7582126998559, + "seed": 1645597304, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176185, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 820, + "versionNonce": 1609532667, + "isDeleted": false, + "id": "1djoflJbPq1iZYxeG6vvs", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 843.094785904982, + "y": 612.826386710884, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "width": 236.33086957943124, + "height": 8.353548393013705, + "seed": 1354805512, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176185, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 1047, + "versionNonce": 2087951413, + "isDeleted": false, + "id": "61SvR173kGBlNiM2hYx-i", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 843.933979602177, + "y": 666.8116705347015, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "width": 141.1603070419808, + "height": 35.19095464139164, + "seed": 497337208, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 1 + }, + "boundElements": [], + "updated": 1688314176185, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1179, + "versionNonce": 1800088987, + "isDeleted": false, + "id": "KweZDYL4pV266i29UQo2O", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 873.1942926679951, + "y": 677.4126832647471, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "width": 63, + "height": 17, + "seed": 244666376, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176185, + "link": null, + "locked": false, + "fontSize": 12.613804582094819, + "fontFamily": 1, + "text": "Show more", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Show more", + "lineHeight": 1.3477297741025214, + "baseline": 12 + }, + { + "type": "line", + "version": 1055, + "versionNonce": 612126101, + "isDeleted": false, + "id": "6UHr1F9sbQjfc3Pn3AUC9", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 4.71238898038469, + "x": 955.8234234136398, + "y": 681.4576006557272, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "width": 14.84733876817561, + "height": 7.91335492900009, + "seed": 1278055544, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688314176185, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 7.737815540443359, + 7.91335492900009 + ], + [ + 14.84733876817561, + 0.6726390980766681 + ] + ] + }, + { + "type": "rectangle", + "version": 1593, + "versionNonce": 711508539, + "isDeleted": false, + "id": "pQ7Helj-RkhLonwDn4uxq", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 878.2965111534407, + "y": 493.103306996402, + "strokeColor": "#000000", + "backgroundColor": "#ff7043", + "width": 12.482770172023958, + "height": 31.20692543005986, + "seed": 1223270520, + "groupIds": [ + "0ClD8yewsWOTVzkWq2mkc" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176185, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 806, + "versionNonce": 522703605, + "isDeleted": false, + "id": "bI-A-gEMD-yvyhVvuajAH", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 865.549721104171, + "y": 442.4040920739574, + "strokeColor": "#000000", + "backgroundColor": "#12b886", + "width": 0, + "height": 87.3793912041677, + "seed": 1280885512, + "groupIds": [ + "0ClD8yewsWOTVzkWq2mkc" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688314176185, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 87.3793912041677 + ] + ] + }, + { + "type": "line", + "version": 816, + "versionNonce": 640336603, + "isDeleted": false, + "id": "X3L53b-aDRBM9J_i7xfIb", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 865.3142359887239, + "y": 530.6645290499368, + "strokeColor": "#000000", + "backgroundColor": "#12b886", + "width": 181.0001674943474, + "height": 0, + "seed": 1422840184, + "groupIds": [ + "0ClD8yewsWOTVzkWq2mkc" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688314176185, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 181.0001674943474, + 0 + ] + ] + }, + { + "type": "rectangle", + "version": 1768, + "versionNonce": 956017749, + "isDeleted": false, + "id": "Fk7rL2dsob9lBYBWtqIaE", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 909.5034365835013, + "y": 461.80381850437016, + "strokeColor": "#000000", + "backgroundColor": "#ffeb3b", + "width": 12.48277017202394, + "height": 62.41385086011974, + "seed": 1096491528, + "groupIds": [ + "0ClD8yewsWOTVzkWq2mkc" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176185, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 1638, + "versionNonce": 656650107, + "isDeleted": false, + "id": "V3UV1tCb1upEwCUx2NF_U", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 940.7103620135604, + "y": 480.5279737624063, + "strokeColor": "#000000", + "backgroundColor": "#03a9f4", + "width": 12.482770172023953, + "height": 43.68969560208382, + "seed": 1860559480, + "groupIds": [ + "0ClD8yewsWOTVzkWq2mkc" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176185, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 1650, + "versionNonce": 793292539, + "isDeleted": false, + "id": "H9TFct1zEuli3oFZ0J6Pi", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 971.9172874436208, + "y": 474.28658867639376, + "strokeColor": "#000000", + "backgroundColor": "#7950f2", + "width": 12.482770172023953, + "height": 49.9310806880958, + "seed": 1153016072, + "groupIds": [ + "0ClD8yewsWOTVzkWq2mkc" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314179109, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 1806, + "versionNonce": 1066797083, + "isDeleted": false, + "id": "Yvl5ZFMXGqZoKBjooHuuL", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1003.1242128736812, + "y": 468.04520359038156, + "strokeColor": "#000000", + "backgroundColor": "#00e676", + "width": 12.48277017202394, + "height": 56.172465774107785, + "seed": 905106296, + "groupIds": [ + "0ClD8yewsWOTVzkWq2mkc" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176185, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 820, + "versionNonce": 851598101, + "isDeleted": false, + "id": "k2RjGkR4842FWeQqwZNqP", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1140.5405571864028, + "y": 441.0898168120742, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 134, + "height": 46, + "seed": 351902216, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176185, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "Results", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Results", + "lineHeight": 1.2777777777777777, + "baseline": 32 + }, + { + "type": "line", + "version": 387, + "versionNonce": 359943355, + "isDeleted": false, + "id": "9BG_CdPIU12PIVg57JFjb", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1142.5011920133527, + "y": 524.9312390735229, + "strokeColor": "#000000", + "backgroundColor": "#12b886", + "width": 387.9702360583586, + "height": 0.8663462515423723, + "seed": 656392968, + "groupIds": [ + "RaIXn6GkWfnJt6NeXQ4tC" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688314176185, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 387.9702360583586, + -0.8663462515423723 + ] + ] + }, + { + "type": "text", + "version": 959, + "versionNonce": 1682976885, + "isDeleted": false, + "id": "ooEpA700FaTXoHICTApB5", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1147.668463887201, + "y": 537.9228771043508, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 10.29019548584314, + "height": 17.493332325933352, + "seed": 771215736, + "groupIds": [ + "RaIXn6GkWfnJt6NeXQ4tC" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176185, + "link": null, + "locked": false, + "fontSize": 12.903833622972927, + "fontFamily": 1, + "text": "0", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "0", + "lineHeight": 1.3556693953949979, + "baseline": 12 + }, + { + "type": "text", + "version": 1031, + "versionNonce": 1818295643, + "isDeleted": false, + "id": "n9FAYaIZeMXggjqKvdYxs", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1147.927904475898, + "y": 577.8396648224748, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 4.116078194337256, + "height": 17.493332325933352, + "seed": 725754376, + "groupIds": [ + "RaIXn6GkWfnJt6NeXQ4tC" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176185, + "link": null, + "locked": false, + "fontSize": 12.903833622972932, + "fontFamily": 1, + "text": "1", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "1", + "lineHeight": 1.3556693953949972, + "baseline": 12 + }, + { + "type": "text", + "version": 1107, + "versionNonce": 1144805845, + "isDeleted": false, + "id": "fQfYp0ljtX1ESRr5wPM9b", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1147.927904475895, + "y": 625.2941282157817, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 10.29019548584314, + "height": 17.493332325933352, + "seed": 988734072, + "groupIds": [ + "RaIXn6GkWfnJt6NeXQ4tC" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176185, + "link": null, + "locked": false, + "fontSize": 12.903833622972925, + "fontFamily": 1, + "text": "2", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "2", + "lineHeight": 1.3556693953949979, + "baseline": 12 + }, + { + "type": "text", + "version": 1015, + "versionNonce": 1873713659, + "isDeleted": false, + "id": "6-kghWfO7GdeQUDWs_zBe", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1146.639444338615, + "y": 501.6706877015755, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 18.522351874517668, + "height": 17.493332325933352, + "seed": 1505796360, + "groupIds": [ + "RaIXn6GkWfnJt6NeXQ4tC" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176185, + "link": null, + "locked": false, + "fontSize": 12.903833622972927, + "fontFamily": 1, + "text": "ID", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "ID", + "lineHeight": 1.3556693953949979, + "baseline": 12 + }, + { + "type": "text", + "version": 1024, + "versionNonce": 463230773, + "isDeleted": false, + "id": "UMowu2HWCm_mgorPKfHhX", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1246.2049260479205, + "y": 537.9228771043508, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 54.53803607496866, + "height": 17.493332325933352, + "seed": 417059704, + "groupIds": [ + "RaIXn6GkWfnJt6NeXQ4tC" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176185, + "link": null, + "locked": false, + "fontSize": 12.903833622972925, + "fontFamily": 1, + "text": "_____", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "_____", + "lineHeight": 1.3556693953949979, + "baseline": 12 + }, + { + "type": "text", + "version": 1093, + "versionNonce": 1012900507, + "isDeleted": false, + "id": "1_YJ1fjNPFhQdtKIp-DSI", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1246.464366636618, + "y": 577.8396648224748, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 54.53803607496866, + "height": 17.493332325933352, + "seed": 1377255432, + "groupIds": [ + "RaIXn6GkWfnJt6NeXQ4tC" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176185, + "link": null, + "locked": false, + "fontSize": 12.90383362297293, + "fontFamily": 1, + "text": "_____", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "_____", + "lineHeight": 1.3556693953949974, + "baseline": 12 + }, + { + "type": "text", + "version": 1168, + "versionNonce": 1960159381, + "isDeleted": false, + "id": "O5wbweMweVsQWwdNABtm7", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1246.4643666366173, + "y": 625.2941282157817, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 43.2188210405412, + "height": 17.493332325933352, + "seed": 1845666936, + "groupIds": [ + "RaIXn6GkWfnJt6NeXQ4tC" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176186, + "link": null, + "locked": false, + "fontSize": 12.903833622972925, + "fontFamily": 1, + "text": "____", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "____", + "lineHeight": 1.3556693953949979, + "baseline": 12 + }, + { + "type": "text", + "version": 1068, + "versionNonce": 723949371, + "isDeleted": false, + "id": "v3I-eUd4WMJ8VzvC_7rB_", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1246.204926047921, + "y": 501.6706877015755, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 32.92862555469805, + "height": 17.493332325933352, + "seed": 1682046728, + "groupIds": [ + "RaIXn6GkWfnJt6NeXQ4tC" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176186, + "link": null, + "locked": false, + "fontSize": 12.903833622972927, + "fontFamily": 1, + "text": "Text", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Text", + "lineHeight": 1.3556693953949979, + "baseline": 12 + }, + { + "type": "text", + "version": 1119, + "versionNonce": 1906167285, + "isDeleted": false, + "id": "sdGrFMITxSJsejpV0zIFY", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1354.7619764110675, + "y": 500.94934123472103, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 34.986664651866704, + "height": 17.493332325933352, + "seed": 161608312, + "groupIds": [ + "RaIXn6GkWfnJt6NeXQ4tC" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176186, + "link": null, + "locked": false, + "fontSize": 12.903833622972929, + "fontFamily": 1, + "text": "Score", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Score", + "lineHeight": 1.3556693953949976, + "baseline": 12 + }, + { + "type": "text", + "version": 1042, + "versionNonce": 1516632027, + "isDeleted": false, + "id": "5tPTHdBAi9oW2Cme01uST", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1356.7782004146586, + "y": 539.0230645323406, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 34.986664651866704, + "height": 17.493332325933352, + "seed": 1702057736, + "groupIds": [ + "RaIXn6GkWfnJt6NeXQ4tC" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176186, + "link": null, + "locked": false, + "fontSize": 12.903833622972929, + "fontFamily": 1, + "text": "0.851", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "0.851", + "lineHeight": 1.3556693953949976, + "baseline": 12 + }, + { + "type": "text", + "version": 1117, + "versionNonce": 1627014997, + "isDeleted": false, + "id": "VkvcQQqVWAFSBPXeb-631", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1355.2070363053651, + "y": 576.0677682813757, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 40.131762394788254, + "height": 17.493332325933352, + "seed": 1116261384, + "groupIds": [ + "RaIXn6GkWfnJt6NeXQ4tC" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176186, + "link": null, + "locked": false, + "fontSize": 12.903833622972929, + "fontFamily": 1, + "text": "0.834", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "0.834", + "lineHeight": 1.3556693953949976, + "baseline": 12 + }, + { + "type": "text", + "version": 1189, + "versionNonce": 2016429179, + "isDeleted": false, + "id": "r5V4P6WY1YTnZqynbGcPW", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1354.5812615574996, + "y": 623.4026675162545, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 38.07372329761963, + "height": 17.493332325933352, + "seed": 754228488, + "groupIds": [ + "RaIXn6GkWfnJt6NeXQ4tC" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176186, + "link": null, + "locked": false, + "fontSize": 12.903833622972924, + "fontFamily": 1, + "text": "0.653", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "0.653", + "lineHeight": 1.355669395394998, + "baseline": 12 + }, + { + "type": "text", + "version": 1164, + "versionNonce": 356553909, + "isDeleted": false, + "id": "tplkVB19dZGWPH04eoN3-", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1452.2813974443563, + "y": 501.2965906346964, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 36.01568420045099, + "height": 17.493332325933352, + "seed": 633774600, + "groupIds": [ + "RaIXn6GkWfnJt6NeXQ4tC" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176187, + "link": null, + "locked": false, + "fontSize": 12.903833622972922, + "fontFamily": 1, + "text": "Data", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Data", + "lineHeight": 1.3556693953949983, + "baseline": 12 + }, + { + "type": "text", + "version": 1091, + "versionNonce": 1279786267, + "isDeleted": false, + "id": "-O_818hE0e37gLO8X85ED", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1456.3974756386945, + "y": 536.2832552865634, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 57.6250947207216, + "height": 17.493332325933352, + "seed": 1316501624, + "groupIds": [ + "RaIXn6GkWfnJt6NeXQ4tC" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176187, + "link": null, + "locked": false, + "fontSize": 12.903833622972925, + "fontFamily": 1, + "text": "{____}", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "{____}", + "lineHeight": 1.3556693953949979, + "baseline": 12 + }, + { + "type": "text", + "version": 1118, + "versionNonce": 2048275989, + "isDeleted": false, + "id": "q_5ogcScUemL3g6XJyETb", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1458.4555147358635, + "y": 569.2118808412615, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 57.6250947207216, + "height": 17.493332325933352, + "seed": 649694840, + "groupIds": [ + "RaIXn6GkWfnJt6NeXQ4tC" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176187, + "link": null, + "locked": false, + "fontSize": 12.903833622972925, + "fontFamily": 1, + "text": "{____}", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "{____}", + "lineHeight": 1.3556693953949979, + "baseline": 12 + }, + { + "type": "text", + "version": 1111, + "versionNonce": 35627451, + "isDeleted": false, + "id": "hePUy_nbNvIpf6_6PBvbq", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1458.4555147358635, + "y": 618.6048191733089, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 57.6250947207216, + "height": 17.493332325933352, + "seed": 1966475272, + "groupIds": [ + "RaIXn6GkWfnJt6NeXQ4tC" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688314176187, + "link": null, + "locked": false, + "fontSize": 12.903833622972925, + "fontFamily": 1, + "text": "{____}", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "{____}", + "lineHeight": 1.3556693953949979, + "baseline": 12 + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/images/examples.png b/docs/images/examples.png new file mode 100644 index 0000000..706bc73 Binary files /dev/null and b/docs/images/examples.png differ diff --git a/docs/images/faq.excalidraw b/docs/images/faq.excalidraw new file mode 100644 index 0000000..d098635 --- /dev/null +++ b/docs/images/faq.excalidraw @@ -0,0 +1,367 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "ellipse", + "version": 236, + "versionNonce": 266746677, + "isDeleted": false, + "id": "pLSV--mc1HnQu2oyARr4o", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 593.4166948358098, + "y": -409.48426474916266, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 100, + "height": 92, + "seed": 513066133, + "groupIds": [ + "agHSoiBea3xOkqCmOPa91", + "dKeNjYUtPsZHi90BFF7EY" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688313196175, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 281, + "versionNonce": 1964375707, + "isDeleted": false, + "id": "n2qByon4fJyq2-FZxQqM0", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 629.5250284234563, + "y": -397.41226924952065, + "strokeColor": "#ff7043", + "backgroundColor": "#2e303e", + "width": 29.78333282470704, + "height": 79.856009000716, + "seed": 852571451, + "groupIds": [ + "agHSoiBea3xOkqCmOPa91", + "dKeNjYUtPsZHi90BFF7EY" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688313196175, + "link": null, + "locked": false, + "fontSize": 63.88480720057278, + "fontFamily": 1, + "text": "?", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "?", + "lineHeight": 1.25, + "baseline": 56 + }, + { + "type": "ellipse", + "version": 288, + "versionNonce": 239275157, + "isDeleted": false, + "id": "QIRNCrcWupjspegQn6VDV", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 744.4166948358098, + "y": -409.48426474916266, + "strokeColor": "#ffeb3b", + "backgroundColor": "#ffeb3b", + "width": 100, + "height": 92, + "seed": 1498649243, + "groupIds": [ + "yonc0SR8jSwQLwuVvb9hG", + "dKeNjYUtPsZHi90BFF7EY" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688313196175, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 372, + "versionNonce": 1982180597, + "isDeleted": false, + "id": "a-CPMQS9ApW4f7p9FjS8m", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 780.5250284234563, + "y": -397.41226924952065, + "strokeColor": "#ffd43b", + "backgroundColor": "#ffeb3b", + "width": 29.78333282470704, + "height": 79.856009000716, + "seed": 1679591227, + "groupIds": [ + "yonc0SR8jSwQLwuVvb9hG", + "dKeNjYUtPsZHi90BFF7EY" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688313209123, + "link": null, + "locked": false, + "fontSize": 63.88480720057278, + "fontFamily": 1, + "text": "?", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "?", + "lineHeight": 1.25, + "baseline": 56 + }, + { + "type": "ellipse", + "version": 326, + "versionNonce": 1069462005, + "isDeleted": false, + "id": "nsYgl7js3PjMKKTKcLYf1", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 895.4166948358098, + "y": -409.48426474916266, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 100, + "height": 92, + "seed": 384682581, + "groupIds": [ + "M7MHx-aSMrw_q1mhaAuG6", + "dKeNjYUtPsZHi90BFF7EY" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688313196175, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 371, + "versionNonce": 641029083, + "isDeleted": false, + "id": "a_CBcLegs1ZMstjO9f4bx", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 931.5250284234563, + "y": -397.41226924952065, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 29.78333282470704, + "height": 79.856009000716, + "seed": 1741404085, + "groupIds": [ + "M7MHx-aSMrw_q1mhaAuG6", + "dKeNjYUtPsZHi90BFF7EY" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688313196175, + "link": null, + "locked": false, + "fontSize": 63.88480720057278, + "fontFamily": 1, + "text": "?", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "?", + "lineHeight": 1.25, + "baseline": 56 + }, + { + "type": "ellipse", + "version": 328, + "versionNonce": 22981461, + "isDeleted": false, + "id": "l88Tj2DPIgTUA2Pij6hPA", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1046.4166948358097, + "y": -409.48426474916266, + "strokeColor": "#7950f2", + "backgroundColor": "#7950f2", + "width": 100, + "height": 92, + "seed": 701954677, + "groupIds": [ + "vFCiKG5iTbg2K5S2WQSsn", + "dKeNjYUtPsZHi90BFF7EY" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688313196175, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 373, + "versionNonce": 1236541563, + "isDeleted": false, + "id": "oqmBYU6SpmJhOl9d1JLJy", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1082.5250284234562, + "y": -397.41226924952065, + "strokeColor": "#7950f2", + "backgroundColor": "#7950f2", + "width": 29.78333282470704, + "height": 79.856009000716, + "seed": 39445461, + "groupIds": [ + "vFCiKG5iTbg2K5S2WQSsn", + "dKeNjYUtPsZHi90BFF7EY" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688313196175, + "link": null, + "locked": false, + "fontSize": 63.88480720057278, + "fontFamily": 1, + "text": "?", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "?", + "lineHeight": 1.25, + "baseline": 56 + }, + { + "type": "ellipse", + "version": 329, + "versionNonce": 1937275061, + "isDeleted": false, + "id": "IFZTHrjZaEk7Avy2zkma-", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1197.4166948358097, + "y": -409.48426474916266, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 100, + "height": 92, + "seed": 894921429, + "groupIds": [ + "70qQx7edpvpMfsvEta9mo", + "dKeNjYUtPsZHi90BFF7EY" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688313196175, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 374, + "versionNonce": 2029017371, + "isDeleted": false, + "id": "p0xoCeTYO1ZVy-xm2oET3", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1233.5250284234562, + "y": -397.41226924952065, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 29.78333282470704, + "height": 79.856009000716, + "seed": 959268917, + "groupIds": [ + "70qQx7edpvpMfsvEta9mo", + "dKeNjYUtPsZHi90BFF7EY" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688313196175, + "link": null, + "locked": false, + "fontSize": 63.88480720057278, + "fontFamily": 1, + "text": "?", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "?", + "lineHeight": 1.25, + "baseline": 56 + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/images/faq.png b/docs/images/faq.png new file mode 100644 index 0000000..a83f078 Binary files /dev/null and b/docs/images/faq.png differ diff --git a/docs/images/flows-dark.png b/docs/images/flows-dark.png new file mode 100644 index 0000000..6d6f634 Binary files /dev/null and b/docs/images/flows-dark.png differ diff --git a/docs/images/flows.excalidraw b/docs/images/flows.excalidraw new file mode 100644 index 0000000..9b4e497 --- /dev/null +++ b/docs/images/flows.excalidraw @@ -0,0 +1,793 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "text", + "version": 603, + "versionNonce": 1883892902, + "isDeleted": false, + "id": "Buic2Lx427wuSIW8P_Rw5", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 714, + "y": 141, + "strokeColor": "#000000", + "backgroundColor": "#228be6", + "width": 532, + "height": 46, + "seed": 373648901, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1673791247706, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "What are semantic workflows?", + "baseline": 32, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "What are semantic workflows?" + }, + { + "type": "rectangle", + "version": 1671, + "versionNonce": 51714234, + "isDeleted": false, + "id": "qYd3q0Vjks7VOHUC9RR51", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 425, + "y": 339.5, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 290.0000000000001, + "height": 46, + "seed": 1441952427, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "WPeWn6N4rCHf0jY16N9Ge" + } + ], + "updated": 1673791247706, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 323, + "versionNonce": 921790438, + "isDeleted": false, + "id": "tgnQzXC9s8RY4oImBOXuB", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 562.5285999651802, + "y": 272.4578674923631, + "strokeColor": "#000", + "backgroundColor": "#228be6", + "width": 0.4714000348197942, + "height": 72.5421325076369, + "seed": 650463755, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1673791247706, + "link": null, + "locked": false, + "startBinding": { + "elementId": "B435ajoI5vAQBDvzkd8aY", + "focus": 0.05963733900881362, + "gap": 5.457867492363107 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0.4714000348197942, + 72.5421325076369 + ] + ] + }, + { + "type": "text", + "version": 1359, + "versionNonce": 2032529786, + "isDeleted": false, + "id": "WPeWn6N4rCHf0jY16N9Ge", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 430, + "y": 344.5, + "strokeColor": "#000000", + "backgroundColor": "#fa5252", + "width": 280, + "height": 36, + "seed": 870516459, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1673791247706, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Translate", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "qYd3q0Vjks7VOHUC9RR51", + "originalText": "Translate" + }, + { + "type": "rectangle", + "version": 180, + "versionNonce": 159873830, + "isDeleted": false, + "id": "IxMxusRKX2PpnJT1uY0cC", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 819, + "y": 217.5, + "strokeColor": "#ffeb3b", + "backgroundColor": "#ffeb3b", + "width": 290.0000000000001, + "height": 49, + "seed": 1364021803, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "SSXjX_URKvcVC8h-Qgz4j" + }, + { + "id": "_YNRYTAnVzdhhKxFtkVyg", + "type": "arrow" + } + ], + "updated": 1673791247706, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 85, + "versionNonce": 1360900666, + "isDeleted": false, + "id": "SSXjX_URKvcVC8h-Qgz4j", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 824, + "y": 224, + "strokeColor": "#000000", + "backgroundColor": "#82c91e", + "width": 280, + "height": 36, + "seed": 948915563, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1673791247706, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Extract Text", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "IxMxusRKX2PpnJT1uY0cC", + "originalText": "Extract Text" + }, + { + "type": "rectangle", + "version": 217, + "versionNonce": 511256166, + "isDeleted": false, + "id": "lATIKISgJPOGnUHleuFRH", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 821, + "y": 338, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 290.0000000000001, + "height": 46, + "seed": 1256311595, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "21WiUuyDtpQ9FnJ74REpJ", + "type": "text" + }, + { + "id": "Q51e0Hav-kYfjX3b8tt-r", + "type": "arrow" + } + ], + "updated": 1673791247706, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 96, + "versionNonce": 2111789818, + "isDeleted": false, + "id": "21WiUuyDtpQ9FnJ74REpJ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 826, + "y": 343, + "strokeColor": "#000000", + "backgroundColor": "#fa5252", + "width": 280, + "height": 36, + "seed": 626266373, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1673791247706, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Summarize", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "lATIKISgJPOGnUHleuFRH", + "originalText": "Summarize" + }, + { + "type": "arrow", + "version": 162, + "versionNonce": 1452501414, + "isDeleted": false, + "id": "_YNRYTAnVzdhhKxFtkVyg", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 962.2340210300499, + "y": 270, + "strokeColor": "#000", + "backgroundColor": "#228be6", + "width": 0.2659789699500834, + "height": 75, + "seed": 101249451, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1673791247706, + "link": null, + "locked": false, + "startBinding": { + "elementId": "IxMxusRKX2PpnJT1uY0cC", + "focus": 0.012856281024868153, + "gap": 3.5 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0.2659789699500834, + 75 + ] + ] + }, + { + "type": "rectangle", + "version": 225, + "versionNonce": 502131642, + "isDeleted": false, + "id": "6B3J0wJfi561c9gMsgtXG", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 824, + "y": 455, + "strokeColor": "#7950f2", + "backgroundColor": "#7950f2", + "width": 290.0000000000001, + "height": 46, + "seed": 1164190923, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "qJDqmDEZF9Ewh2UrjXq5Z", + "type": "text" + }, + { + "id": "Q51e0Hav-kYfjX3b8tt-r", + "type": "arrow" + } + ], + "updated": 1673791247706, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 147, + "versionNonce": 1896039654, + "isDeleted": false, + "id": "qJDqmDEZF9Ewh2UrjXq5Z", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 829, + "y": 460, + "strokeColor": "#000000", + "backgroundColor": "#fa5252", + "width": 280, + "height": 36, + "seed": 1307943269, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1673791247706, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Build Vector Index", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "6B3J0wJfi561c9gMsgtXG", + "originalText": "Build Vector Index" + }, + { + "type": "arrow", + "version": 215, + "versionNonce": 124544122, + "isDeleted": false, + "id": "Q51e0Hav-kYfjX3b8tt-r", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 963.600775377322, + "y": 387.5, + "strokeColor": "#000", + "backgroundColor": "#228be6", + "width": 0.29392358842710564, + "height": 66.5, + "seed": 59173285, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1673791247706, + "link": null, + "locked": false, + "startBinding": { + "elementId": "lATIKISgJPOGnUHleuFRH", + "focus": 0.015253485349599789, + "gap": 3.5 + }, + "endBinding": { + "elementId": "6B3J0wJfi561c9gMsgtXG", + "focus": -0.039966641220930875, + "gap": 1 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -0.29392358842710564, + 66.5 + ] + ] + }, + { + "type": "rectangle", + "version": 1258, + "versionNonce": 1062582310, + "isDeleted": false, + "id": "5VuUdI_BsJ5pyE1nTqJUI", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1195, + "y": 221, + "strokeColor": "#7950f2", + "backgroundColor": "#7950f2", + "width": 290.0000000000001, + "height": 46, + "seed": 1044404613, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "bJJ9SGsJsvT071qBBH0w5", + "type": "text" + }, + { + "id": "Q51e0Hav-kYfjX3b8tt-r", + "type": "arrow" + } + ], + "updated": 1673791247706, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1443, + "versionNonce": 1411237178, + "isDeleted": false, + "id": "bJJ9SGsJsvT071qBBH0w5", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1200, + "y": 226, + "strokeColor": "#000000", + "backgroundColor": "#fa5252", + "width": 280, + "height": 36, + "seed": 128953675, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1673791247706, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Run similarity query", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "5VuUdI_BsJ5pyE1nTqJUI", + "originalText": "Run similarity query" + }, + { + "type": "rectangle", + "version": 359, + "versionNonce": 1029930726, + "isDeleted": false, + "id": "pSbgtf1qAB7tWl-pTld7e", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1197, + "y": 343, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 290.0000000000001, + "height": 46, + "seed": 210689733, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "pQzKSM3audka1kiQm_Sku", + "type": "text" + }, + { + "id": "Q51e0Hav-kYfjX3b8tt-r", + "type": "arrow" + }, + { + "id": "k67YzXNtt1GLh4i8Es5zJ", + "type": "arrow" + } + ], + "updated": 1673791251799, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 320, + "versionNonce": 660337658, + "isDeleted": false, + "id": "pQzKSM3audka1kiQm_Sku", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1219.5, + "y": 348, + "strokeColor": "#000", + "backgroundColor": "#fa5252", + "width": 245, + "height": 36, + "seed": 1869028363, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1673791256722, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Send notifications", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "pSbgtf1qAB7tWl-pTld7e", + "originalText": "Send notifications" + }, + { + "type": "arrow", + "version": 329, + "versionNonce": 251216122, + "isDeleted": false, + "id": "k67YzXNtt1GLh4i8Es5zJ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1339.1171359117898, + "y": 265.5, + "strokeColor": "#000", + "backgroundColor": "#228be6", + "width": 1.0119492860442278, + "height": 76.5, + "seed": 1656816747, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1673791249246, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "pSbgtf1qAB7tWl-pTld7e", + "focus": -0.010690950588675632, + "gap": 1 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 1.0119492860442278, + 76.5 + ] + ] + }, + { + "type": "text", + "version": 320, + "versionNonce": 407191226, + "isDeleted": false, + "id": "kKPPLMCj8QQIJLbW-B5hm", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1180, + "y": 451, + "strokeColor": "#000", + "backgroundColor": "#fab005", + "width": 296, + "height": 52, + "seed": 1079990731, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1673791247706, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "- API bindings for JavaScript,\n Rust, Go and Java", + "baseline": 44, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- API bindings for JavaScript,\n Rust, Go and Java" + }, + { + "type": "text", + "version": 572, + "versionNonce": 1757323750, + "isDeleted": false, + "id": "1AQ3rj-V4weRtPA8a5-z-", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 437.5, + "y": 449.5, + "strokeColor": "#000", + "backgroundColor": "#fab005", + "width": 278, + "height": 52, + "seed": 836512075, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1673791247706, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "- Build with Python or YAML\n- Run local or via API", + "baseline": 44, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- Build with Python or YAML\n- Run local or via API" + }, + { + "type": "rectangle", + "version": 304, + "versionNonce": 23156602, + "isDeleted": false, + "id": "B435ajoI5vAQBDvzkd8aY", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 426, + "y": 221, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 290.0000000000001, + "height": 46, + "seed": 942981672, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "AoGnxEHn4x-zq2-0C0VrT", + "type": "text" + }, + { + "id": "Q51e0Hav-kYfjX3b8tt-r", + "type": "arrow" + }, + { + "id": "tgnQzXC9s8RY4oImBOXuB", + "type": "arrow" + } + ], + "updated": 1673791247706, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 183, + "versionNonce": 1100838182, + "isDeleted": false, + "id": "AoGnxEHn4x-zq2-0C0VrT", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 431, + "y": 226, + "strokeColor": "#000000", + "backgroundColor": "#fa5252", + "width": 280, + "height": 36, + "seed": 604886104, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1673791247706, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Summarize", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "B435ajoI5vAQBDvzkd8aY", + "originalText": "Summarize" + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#3030" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/images/flows.png b/docs/images/flows.png new file mode 100644 index 0000000..2262c64 Binary files /dev/null and b/docs/images/flows.png differ diff --git a/docs/images/format-dark.png b/docs/images/format-dark.png new file mode 100644 index 0000000..6153418 Binary files /dev/null and b/docs/images/format-dark.png differ diff --git a/docs/images/format.excalidraw b/docs/images/format.excalidraw new file mode 100644 index 0000000..18e9fd2 --- /dev/null +++ b/docs/images/format.excalidraw @@ -0,0 +1,446 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 564, + "versionNonce": 555415043, + "index": "aZV", + "isDeleted": false, + "id": "c8q6be5K81xmCJ-ukSRnZ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 518, + "y": -130.82625000000002, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 261, + "height": 183, + "seed": 443859885, + "groupIds": [ + "2ZFdM1ONmx5lh5aHlDXqH" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1724584360105, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 518, + "versionNonce": 1253679021, + "index": "aa", + "isDeleted": false, + "id": "uKvu2zcnLI9S3D-aFErJQ", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 616.6083333333333, + "y": -129.82625000000002, + "strokeColor": "#1e1e1e", + "backgroundColor": "#03a9f4", + "width": 53.78333333333333, + "height": 35, + "seed": 848196621, + "groupIds": [ + "2ZFdM1ONmx5lh5aHlDXqH" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1724584360105, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 5, + "text": "ANN", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "ANN", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 480, + "versionNonce": 944506682, + "index": "ab", + "isDeleted": false, + "id": "DCqImwTRCBh0nmPIv-3zR", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 536.8249969482422, + "y": -85.17374999999998, + "strokeColor": "#1e1e1e", + "backgroundColor": "#03a9f4", + "width": 213.35000610351562, + "height": 125, + "seed": 1618350307, + "groupIds": [ + "2ZFdM1ONmx5lh5aHlDXqH" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1724587151711, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 5, + "text": "- Faiss\n- HNSWLib\n- Annoy\n- NumPy\n- Postgres (pgvector)", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- Faiss\n- HNSWLib\n- Annoy\n- NumPy\n- Postgres (pgvector)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 780, + "versionNonce": 1085123203, + "index": "abV", + "isDeleted": false, + "id": "Jo5LO2bSebtz54pWb3XHT", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 810.5, + "y": -131.82625000000002, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 282.9999999999999, + "height": 182, + "seed": 848620899, + "groupIds": [ + "hCyHVfABhzmE_8xEhUbGD" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1724584372798, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 717, + "versionNonce": 708473645, + "index": "ac", + "isDeleted": false, + "id": "wX4udz8nceRrNC3uUDLYf", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 886.2583312988281, + "y": -121.82625000000002, + "strokeColor": "#1e1e1e", + "backgroundColor": "#03a9f4", + "width": 131.48333740234375, + "height": 35, + "seed": 16206083, + "groupIds": [ + "hCyHVfABhzmE_8xEhUbGD" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1724584372798, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 5, + "text": "Database", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Database", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 722, + "versionNonce": 1181998310, + "index": "ad", + "isDeleted": false, + "id": "-5HdWsMbtE_7EnREtXBfc", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 831.1999969482422, + "y": -77.17374999999998, + "strokeColor": "#1e1e1e", + "backgroundColor": "#03a9f4", + "width": 241.60000610351562, + "height": 75, + "seed": 1976381603, + "groupIds": [ + "hCyHVfABhzmE_8xEhUbGD" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1724587158353, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 5, + "text": "- SQLite\n- DuckDB\n- Postgres (SQLAlchemy)", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- SQLite\n- DuckDB\n- Postgres (SQLAlchemy)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 900, + "versionNonce": 1747610221, + "index": "adV", + "isDeleted": false, + "id": "AK1nt4FrnmjinS2uZVZ2g", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1125, + "y": -132.32625000000002, + "strokeColor": "#ffeb3b", + "backgroundColor": "#ffeb3b", + "width": 266.9999999999999, + "height": 180, + "seed": 961514755, + "groupIds": [ + "KOc5NUqUBL03A8qlCEUgw" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1724584372798, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 828, + "versionNonce": 1440317155, + "index": "ae", + "isDeleted": false, + "id": "yeFldcgggtNS8S44yWdtq", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1218.7916666666667, + "y": -129.32625000000002, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffeb3b", + "width": 79.41666666666667, + "height": 35, + "seed": 41743523, + "groupIds": [ + "KOc5NUqUBL03A8qlCEUgw" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1724584372798, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 5, + "text": "Graph", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Graph", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 937, + "versionNonce": 766285005, + "index": "af", + "isDeleted": false, + "id": "FSHrZ4DMc-ubEPZmgY7M3", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1128.8999938964844, + "y": -78.67374999999998, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffeb3b", + "width": 259.20001220703125, + "height": 75, + "seed": 2094819395, + "groupIds": [ + "KOc5NUqUBL03A8qlCEUgw" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1724584372798, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 5, + "text": "- NetworkX (MessagePack)\n- Postgres (grand-graph)\n", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- NetworkX (MessagePack)\n- Postgres (grand-graph)\n", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 784, + "versionNonce": 868973283, + "index": "afV", + "isDeleted": false, + "id": "C_7EOPOUjQk0kE44hF9WC", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1423.5, + "y": -130.82625000000002, + "strokeColor": "#9775fa", + "backgroundColor": "#9775fa", + "width": 290.9999999999999, + "height": 174, + "seed": 606086243, + "groupIds": [ + "-E2Q3MkgggNSCRYPrSvhr" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1724584154381, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 712, + "versionNonce": 1558730957, + "index": "ag", + "isDeleted": false, + "id": "nbfo2fj-5l1xNK7r9SnT8", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1520.2333335876465, + "y": -127.82625000000002, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffeb3b", + "width": 97.53333282470703, + "height": 35, + "seed": 704880643, + "groupIds": [ + "-E2Q3MkgggNSCRYPrSvhr" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1724584154381, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 5, + "text": "Scoring", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Scoring", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 832, + "versionNonce": 2104267693, + "index": "ah", + "isDeleted": false, + "id": "6a1UVKWBLqgnaAvPSuSfw", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1431.6750030517578, + "y": -76.17374999999998, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffeb3b", + "width": 274.6499938964844, + "height": 75, + "seed": 154576803, + "groupIds": [ + "-E2Q3MkgggNSCRYPrSvhr" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1724584347937, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 5, + "text": "- Local index (MessagePack)\n- Postgres\n", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- Local index (MessagePack)\n- Postgres\n", + "autoResize": true, + "lineHeight": 1.25 + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/images/format.png b/docs/images/format.png new file mode 100644 index 0000000..b107c70 Binary files /dev/null and b/docs/images/format.png differ diff --git a/docs/images/further-dark.png b/docs/images/further-dark.png new file mode 100644 index 0000000..0a9ce83 Binary files /dev/null and b/docs/images/further-dark.png differ diff --git a/docs/images/further-ghdark.png b/docs/images/further-ghdark.png new file mode 100644 index 0000000..d703172 Binary files /dev/null and b/docs/images/further-ghdark.png differ diff --git a/docs/images/further.excalidraw b/docs/images/further.excalidraw new file mode 100644 index 0000000..c10fe78 --- /dev/null +++ b/docs/images/further.excalidraw @@ -0,0 +1,1972 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 4646, + "versionNonce": 1614521653, + "isDeleted": false, + "id": "8q8ph1hOgu4FaaN7bUDtj", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 570.5486656509088, + "y": 207.02732362163974, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 971860443, + "groupIds": [ + "EGaO0Qgusro1bOGa76Zij" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312379126, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 4695, + "versionNonce": 245642395, + "isDeleted": false, + "id": "NLF6LLOv1mkjsqrOC4cKB", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 563.0012326151946, + "y": 200.00723433592552, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 435779195, + "groupIds": [ + "zukIlM5pM--6gudqqavs2", + "EGaO0Qgusro1bOGa76Zij" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312379126, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 4793, + "versionNonce": 1146828437, + "isDeleted": false, + "id": "b0FmtXHqO4i5gHXFgOxF6", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 553.6061433294792, + "y": 191.75332808592574, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 35319579, + "groupIds": [ + "EGaO0Qgusro1bOGa76Zij" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312379126, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 3917, + "versionNonce": 722004283, + "isDeleted": false, + "id": "XIBG5MqpMzO91s-zg9RV6", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 565.5925699163902, + "y": 240.5005651332784, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 46.57983585730082, + "height": 3.249953844290203, + "seed": 616846267, + "groupIds": [ + "EGaO0Qgusro1bOGa76Zij" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312379126, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 40.42449133807562, + 0.1573930526684746 + ], + [ + 46.57983585730082, + -3.0925607916217284 + ] + ] + }, + { + "type": "line", + "version": 3943, + "versionNonce": 356621301, + "isDeleted": false, + "id": "Ak-qKgUN0py0hmrGe0aKl", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 567.4478221544045, + "y": 209.03527783443982, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 45.567415680676426, + "height": 2.8032978840147194, + "seed": 1108878427, + "groupIds": [ + "EGaO0Qgusro1bOGa76Zij" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312379126, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 16.832548902953302, + -2.8032978840147194 + ], + [ + 45.567415680676426, + -0.3275477042019195 + ] + ] + }, + { + "type": "line", + "version": 3967, + "versionNonce": 5917339, + "isDeleted": false, + "id": "1CwtdEiRMjK88DqZoUvFn", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 567.3712226380351, + "y": 276.4296521047969, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 48.33668263438425, + "height": 4.280657518731036, + "seed": 1911087355, + "groupIds": [ + "EGaO0Qgusro1bOGa76Zij" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312375231, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 26.41225578429045, + -0.2552319773002338 + ], + [ + 37.62000339651456, + 2.3153712935189787 + ], + [ + 48.33668263438425, + -1.9652862252120569 + ] + ] + }, + { + "type": "line", + "version": 4004, + "versionNonce": 20545685, + "isDeleted": false, + "id": "EtF5vn14aeWzQBbD8KpKB", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 564.1871754565047, + "y": 287.6728005130757, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 54.40694982784246, + "height": 2.9096445412231735, + "seed": 767101339, + "groupIds": [ + "EGaO0Qgusro1bOGa76Zij" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312375231, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 10.166093050596771, + -1.166642430373031 + ], + [ + 16.130660965377448, + -0.8422655250909383 + ], + [ + 46.26079588567538, + 0.6125567455206506 + ], + [ + 54.40694982784246, + -2.297087795702523 + ] + ] + }, + { + "type": "line", + "version": 3970, + "versionNonce": 237898203, + "isDeleted": false, + "id": "ETXFUxT0kF7UCijUSi0-u", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 564.4720183520706, + "y": 224.09587938770105, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 46.92865289294453, + "height": 2.4757501798128, + "seed": 448470587, + "groupIds": [ + "EGaO0Qgusro1bOGa76Zij" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312379126, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 18.193786115221407, + -0.5912874140789839 + ], + [ + 46.92865289294453, + 1.884462765733816 + ] + ] + }, + { + "type": "line", + "version": 3984, + "versionNonce": 1942398453, + "isDeleted": false, + "id": "lLMO9OdzPiwVIPsiVJjmS", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 564.5795065482209, + "y": 258.57920842354054, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 46.92865289294453, + "height": 2.4757501798128, + "seed": 332504795, + "groupIds": [ + "EGaO0Qgusro1bOGa76Zij" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312375231, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 8.093938105125233, + 1.4279702913643746 + ], + [ + 18.193786115221407, + -0.5912874140789839 + ], + [ + 46.92865289294453, + 1.884462765733816 + ] + ] + }, + { + "type": "rectangle", + "version": 4612, + "versionNonce": 232169435, + "isDeleted": false, + "id": "SfdOgKSw2Op0gU02ZRj6h", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 724.2986656509086, + "y": 207.02732362163974, + "strokeColor": "#ffeb3b", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 1938754427, + "groupIds": [ + "qd_DFmKp5LE3bMw3Mtfo2" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312375231, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 4661, + "versionNonce": 1481121621, + "isDeleted": false, + "id": "WZq_8J-B599FxSuSWhMOY", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 716.7512326151943, + "y": 200.00723433592552, + "strokeColor": "#ffeb3b", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 506848283, + "groupIds": [ + "12jDSGpPx8NMUTpKIVTRd", + "qd_DFmKp5LE3bMw3Mtfo2" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312375231, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 4759, + "versionNonce": 1196158075, + "isDeleted": false, + "id": "6UAiKf3P2B8oHIqxMXP9B", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 707.3561433294792, + "y": 191.75332808592574, + "strokeColor": "#ffeb3b", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 1270393019, + "groupIds": [ + "qd_DFmKp5LE3bMw3Mtfo2" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312375231, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 3883, + "versionNonce": 353594549, + "isDeleted": false, + "id": "PtmQWP_l-8IxINtiwRUhu", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 719.3425699163902, + "y": 240.5005651332784, + "strokeColor": "#ffeb3b", + "backgroundColor": "#fff", + "width": 46.57983585730082, + "height": 3.249953844290203, + "seed": 297392475, + "groupIds": [ + "qd_DFmKp5LE3bMw3Mtfo2" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312375231, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 40.42449133807562, + 0.1573930526684746 + ], + [ + 46.57983585730082, + -3.0925607916217284 + ] + ] + }, + { + "type": "line", + "version": 3909, + "versionNonce": 162440475, + "isDeleted": false, + "id": "Q3WDbscMJKM8bJF79W-YD", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 721.1978221544043, + "y": 209.03527783443982, + "strokeColor": "#ffeb3b", + "backgroundColor": "#fff", + "width": 45.567415680676426, + "height": 2.8032978840147194, + "seed": 764147195, + "groupIds": [ + "qd_DFmKp5LE3bMw3Mtfo2" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312375231, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 16.832548902953302, + -2.8032978840147194 + ], + [ + 45.567415680676426, + -0.3275477042019195 + ] + ] + }, + { + "type": "line", + "version": 3934, + "versionNonce": 701369877, + "isDeleted": false, + "id": "BS2q9beMoZf_-aYf5c8vt", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 721.1212226380351, + "y": 276.42965210479707, + "strokeColor": "#ffeb3b", + "backgroundColor": "#fff", + "width": 48.33668263438425, + "height": 4.280657518731036, + "seed": 238124699, + "groupIds": [ + "qd_DFmKp5LE3bMw3Mtfo2" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312375231, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 26.41225578429045, + -0.2552319773002338 + ], + [ + 37.62000339651456, + 2.3153712935189787 + ], + [ + 48.33668263438425, + -1.9652862252120569 + ] + ] + }, + { + "type": "line", + "version": 3971, + "versionNonce": 745621947, + "isDeleted": false, + "id": "oMYU6FaZy7M2x90y9S3Pt", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 717.9371754565047, + "y": 287.67280051307563, + "strokeColor": "#ffeb3b", + "backgroundColor": "#fff", + "width": 54.40694982784246, + "height": 2.9096445412231735, + "seed": 509408059, + "groupIds": [ + "qd_DFmKp5LE3bMw3Mtfo2" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312375231, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 10.166093050596771, + -1.166642430373031 + ], + [ + 16.130660965377448, + -0.8422655250909383 + ], + [ + 46.26079588567538, + 0.6125567455206506 + ], + [ + 54.40694982784246, + -2.297087795702523 + ] + ] + }, + { + "type": "line", + "version": 3936, + "versionNonce": 91063157, + "isDeleted": false, + "id": "PHkrZ4_UHjutk9XZElk_e", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 718.2220183520706, + "y": 224.09587938770105, + "strokeColor": "#ffeb3b", + "backgroundColor": "#fff", + "width": 46.92865289294453, + "height": 2.4757501798128, + "seed": 1316017115, + "groupIds": [ + "qd_DFmKp5LE3bMw3Mtfo2" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312375231, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 18.193786115221407, + -0.5912874140789839 + ], + [ + 46.92865289294453, + 1.884462765733816 + ] + ] + }, + { + "type": "line", + "version": 3951, + "versionNonce": 1958107739, + "isDeleted": false, + "id": "Dorr3dO30jUkk79PopIf_", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 718.3295065482209, + "y": 258.57920842354076, + "strokeColor": "#ffeb3b", + "backgroundColor": "#fff", + "width": 46.92865289294453, + "height": 2.4757501798128, + "seed": 1585422459, + "groupIds": [ + "qd_DFmKp5LE3bMw3Mtfo2" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312375232, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 8.093938105125233, + 1.4279702913643746 + ], + [ + 18.193786115221407, + -0.5912874140789839 + ], + [ + 46.92865289294453, + 1.884462765733816 + ] + ] + }, + { + "type": "rectangle", + "version": 4610, + "versionNonce": 989091157, + "isDeleted": false, + "id": "Jt4NvPMeLg6mhAlXKpPrq", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 878.0486656509083, + "y": 207.02732362163974, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 1749801243, + "groupIds": [ + "oifR3K1EkgRm0woSpwQEe" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312379126, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 4659, + "versionNonce": 1562777211, + "isDeleted": false, + "id": "WgPx2gWYtu6RU7xJArs4i", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 870.5012326151941, + "y": 200.00723433592552, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 692583867, + "groupIds": [ + "Tzrs5VR8o2cJk5khXPvuR", + "oifR3K1EkgRm0woSpwQEe" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312379126, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 4757, + "versionNonce": 13165237, + "isDeleted": false, + "id": "O11wLzeZnprhHXZl10o-v", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 861.106143329479, + "y": 191.75332808592574, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 211193435, + "groupIds": [ + "oifR3K1EkgRm0woSpwQEe" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312379126, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 3881, + "versionNonce": 1991479067, + "isDeleted": false, + "id": "HZv1jZzJjUlbXWXKpVgPq", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 873.09256991639, + "y": 240.5005651332784, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 46.57983585730082, + "height": 3.249953844290203, + "seed": 1963592443, + "groupIds": [ + "oifR3K1EkgRm0woSpwQEe" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312379126, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 40.42449133807562, + 0.1573930526684746 + ], + [ + 46.57983585730082, + -3.0925607916217284 + ] + ] + }, + { + "type": "line", + "version": 3907, + "versionNonce": 728024085, + "isDeleted": false, + "id": "z9PwounZ94sfL3PfhSiQ4", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 874.9478221544041, + "y": 209.03527783443982, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 45.567415680676426, + "height": 2.8032978840147194, + "seed": 1193603995, + "groupIds": [ + "oifR3K1EkgRm0woSpwQEe" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312379126, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 16.832548902953302, + -2.8032978840147194 + ], + [ + 45.567415680676426, + -0.3275477042019195 + ] + ] + }, + { + "type": "line", + "version": 3931, + "versionNonce": 557651003, + "isDeleted": false, + "id": "Imd8yI2Lf6IQozPsseCce", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 874.8712226380349, + "y": 276.4296521047971, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 48.33668263438425, + "height": 4.280657518731036, + "seed": 857327675, + "groupIds": [ + "oifR3K1EkgRm0woSpwQEe" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312375232, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 26.41225578429045, + -0.2552319773002338 + ], + [ + 37.62000339651456, + 2.3153712935189787 + ], + [ + 48.33668263438425, + -1.9652862252120569 + ] + ] + }, + { + "type": "line", + "version": 3968, + "versionNonce": 1811521781, + "isDeleted": false, + "id": "ZH0iHUr1LSzlOrGkrE_Bg", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 871.6871754565045, + "y": 287.6728005130757, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 54.40694982784246, + "height": 2.9096445412231735, + "seed": 1053493467, + "groupIds": [ + "oifR3K1EkgRm0woSpwQEe" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312375232, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 10.166093050596771, + -1.166642430373031 + ], + [ + 16.130660965377448, + -0.8422655250909383 + ], + [ + 46.26079588567538, + 0.6125567455206506 + ], + [ + 54.40694982784246, + -2.297087795702523 + ] + ] + }, + { + "type": "line", + "version": 3934, + "versionNonce": 1028191163, + "isDeleted": false, + "id": "uboh1NQ170zSPaXGoSESZ", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 871.9720183520703, + "y": 224.09587938770105, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 46.92865289294453, + "height": 2.4757501798128, + "seed": 264975739, + "groupIds": [ + "oifR3K1EkgRm0woSpwQEe" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312379126, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 18.193786115221407, + -0.5912874140789839 + ], + [ + 46.92865289294453, + 1.884462765733816 + ] + ] + }, + { + "type": "line", + "version": 3948, + "versionNonce": 1717811797, + "isDeleted": false, + "id": "XLAGZPKHS8Xwt91UpHZ_0", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 872.0795065482207, + "y": 258.57920842354076, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 46.92865289294453, + "height": 2.4757501798128, + "seed": 1969016347, + "groupIds": [ + "oifR3K1EkgRm0woSpwQEe" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312375232, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 8.093938105125233, + 1.4279702913643746 + ], + [ + 18.193786115221407, + -0.5912874140789839 + ], + [ + 46.92865289294453, + 1.884462765733816 + ] + ] + }, + { + "type": "rectangle", + "version": 4634, + "versionNonce": 1854636411, + "isDeleted": false, + "id": "nZq35Q3L6H0QkeG3Vht31", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1031.7986656509083, + "y": 207.02732362163974, + "strokeColor": "#7950f2", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 846353083, + "groupIds": [ + "0RC_0jv9T44Fq-XFPghb3" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312375232, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 4683, + "versionNonce": 855877557, + "isDeleted": false, + "id": "rrNUqmwxf5WDNs8Qq_9z0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1024.251232615194, + "y": 200.00723433592552, + "strokeColor": "#7950f2", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 128861019, + "groupIds": [ + "v87qT3LfamP0fU18OmjI3", + "0RC_0jv9T44Fq-XFPghb3" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312375232, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 4781, + "versionNonce": 775133723, + "isDeleted": false, + "id": "hNZNlkh5OnGJBY8xlIc5t", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1014.8561433294788, + "y": 191.75332808592574, + "strokeColor": "#7950f2", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 2103648251, + "groupIds": [ + "0RC_0jv9T44Fq-XFPghb3" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312375232, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 3905, + "versionNonce": 872343829, + "isDeleted": false, + "id": "KBAv7GqAYMFEu-wMY1mh0", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1026.8425699163897, + "y": 240.5005651332784, + "strokeColor": "#7950f2", + "backgroundColor": "#fff", + "width": 46.57983585730082, + "height": 3.249953844290203, + "seed": 1838377115, + "groupIds": [ + "0RC_0jv9T44Fq-XFPghb3" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312375232, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 40.42449133807562, + 0.1573930526684746 + ], + [ + 46.57983585730082, + -3.0925607916217284 + ] + ] + }, + { + "type": "line", + "version": 3931, + "versionNonce": 1049599675, + "isDeleted": false, + "id": "vBzGi3sNPrEnLnXkuTDui", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1028.697822154404, + "y": 209.03527783443982, + "strokeColor": "#7950f2", + "backgroundColor": "#fff", + "width": 45.567415680676426, + "height": 2.8032978840147194, + "seed": 1307680059, + "groupIds": [ + "0RC_0jv9T44Fq-XFPghb3" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312375232, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 16.832548902953302, + -2.8032978840147194 + ], + [ + 45.567415680676426, + -0.3275477042019195 + ] + ] + }, + { + "type": "line", + "version": 3956, + "versionNonce": 1791688309, + "isDeleted": false, + "id": "riv_87Cqm5BHH4qwhzob4", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1028.6212226380346, + "y": 276.4296521047971, + "strokeColor": "#7950f2", + "backgroundColor": "#fff", + "width": 48.33668263438425, + "height": 4.280657518731036, + "seed": 970997211, + "groupIds": [ + "0RC_0jv9T44Fq-XFPghb3" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312375232, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 26.41225578429045, + -0.2552319773002338 + ], + [ + 37.62000339651456, + 2.3153712935189787 + ], + [ + 48.33668263438425, + -1.9652862252120569 + ] + ] + }, + { + "type": "line", + "version": 3993, + "versionNonce": 977407835, + "isDeleted": false, + "id": "1xxX3pYb1rx-QZk3jub6u", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1025.4371754565043, + "y": 287.6728005130757, + "strokeColor": "#7950f2", + "backgroundColor": "#fff", + "width": 54.40694982784246, + "height": 2.9096445412231735, + "seed": 1014090363, + "groupIds": [ + "0RC_0jv9T44Fq-XFPghb3" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312375232, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 10.166093050596771, + -1.166642430373031 + ], + [ + 16.130660965377448, + -0.8422655250909383 + ], + [ + 46.26079588567538, + 0.6125567455206506 + ], + [ + 54.40694982784246, + -2.297087795702523 + ] + ] + }, + { + "type": "line", + "version": 3958, + "versionNonce": 237856725, + "isDeleted": false, + "id": "bU8iYByqD3dp76KNxsOad", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1025.72201835207, + "y": 224.09587938770105, + "strokeColor": "#7950f2", + "backgroundColor": "#fff", + "width": 46.92865289294453, + "height": 2.4757501798128, + "seed": 1516815131, + "groupIds": [ + "0RC_0jv9T44Fq-XFPghb3" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312375232, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 18.193786115221407, + -0.5912874140789839 + ], + [ + 46.92865289294453, + 1.884462765733816 + ] + ] + }, + { + "type": "line", + "version": 3973, + "versionNonce": 1134390267, + "isDeleted": false, + "id": "VY-TfARiaY6LsV7-7rNWC", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1025.8295065482205, + "y": 258.57920842354076, + "strokeColor": "#7950f2", + "backgroundColor": "#fff", + "width": 46.92865289294453, + "height": 2.4757501798128, + "seed": 1547151291, + "groupIds": [ + "0RC_0jv9T44Fq-XFPghb3" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312375232, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 8.093938105125233, + 1.4279702913643746 + ], + [ + 18.193786115221407, + -0.5912874140789839 + ], + [ + 46.92865289294453, + 1.884462765733816 + ] + ] + }, + { + "type": "rectangle", + "version": 4712, + "versionNonce": 628244187, + "isDeleted": false, + "id": "C72Ce5baIVqALdI-P3OM9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1185.548665650909, + "y": 207.0273236216395, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 1476458587, + "groupIds": [ + "siLS3_RDwEMKtYUwaaxN-" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312381572, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 4761, + "versionNonce": 1358557269, + "isDeleted": false, + "id": "zBGI80VXFpYU7OXFGK6J6", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1178.0012326151943, + "y": 200.0072343359253, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 2073507067, + "groupIds": [ + "oYL8Phei164CeEqvr9yAQ", + "siLS3_RDwEMKtYUwaaxN-" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312381572, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 4859, + "versionNonce": 1209772923, + "isDeleted": false, + "id": "cfu4VQtjnsxUMcZwQ5sVW", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1168.6061433294788, + "y": 191.75332808592574, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 377281947, + "groupIds": [ + "siLS3_RDwEMKtYUwaaxN-" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688312381572, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 3983, + "versionNonce": 425926069, + "isDeleted": false, + "id": "LmvD8cAN6D1U7VGelLV01", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1180.5925699163897, + "y": 240.50056513327817, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 46.57983585730082, + "height": 3.249953844290203, + "seed": 1675915835, + "groupIds": [ + "siLS3_RDwEMKtYUwaaxN-" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312381572, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 40.42449133807562, + 0.1573930526684746 + ], + [ + 46.57983585730082, + -3.0925607916217284 + ] + ] + }, + { + "type": "line", + "version": 4009, + "versionNonce": 2016314395, + "isDeleted": false, + "id": "uIr1YsstwF_3qU4vEy2_0", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1182.4478221544048, + "y": 209.0352778344396, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 45.567415680676426, + "height": 2.8032978840147194, + "seed": 347108059, + "groupIds": [ + "siLS3_RDwEMKtYUwaaxN-" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312381572, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 16.832548902953302, + -2.8032978840147194 + ], + [ + 45.567415680676426, + -0.3275477042019195 + ] + ] + }, + { + "type": "line", + "version": 4034, + "versionNonce": 1258266389, + "isDeleted": false, + "id": "soobVx_Prpyrf1BH-snOZ", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1182.3712226380346, + "y": 276.4296521047969, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 48.33668263438425, + "height": 4.280657518731036, + "seed": 426991483, + "groupIds": [ + "siLS3_RDwEMKtYUwaaxN-" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312381572, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 26.41225578429045, + -0.2552319773002338 + ], + [ + 37.62000339651456, + 2.3153712935189787 + ], + [ + 48.33668263438425, + -1.9652862252120569 + ] + ] + }, + { + "type": "line", + "version": 4071, + "versionNonce": 496847035, + "isDeleted": false, + "id": "wrQ8ziTQK2vkQdM0O4J3M", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1179.1871754565043, + "y": 287.67280051307546, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 54.40694982784246, + "height": 2.9096445412231735, + "seed": 745519131, + "groupIds": [ + "siLS3_RDwEMKtYUwaaxN-" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312381572, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 10.166093050596771, + -1.166642430373031 + ], + [ + 16.130660965377448, + -0.8422655250909383 + ], + [ + 46.26079588567538, + 0.6125567455206506 + ], + [ + 54.40694982784246, + -2.297087795702523 + ] + ] + }, + { + "type": "line", + "version": 4036, + "versionNonce": 205845621, + "isDeleted": false, + "id": "yZFGBgkE8nXDy8E9XYuQO", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1179.47201835207, + "y": 224.09587938770082, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 46.92865289294453, + "height": 2.4757501798128, + "seed": 462945467, + "groupIds": [ + "siLS3_RDwEMKtYUwaaxN-" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312381572, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 18.193786115221407, + -0.5912874140789839 + ], + [ + 46.92865289294453, + 1.884462765733816 + ] + ] + }, + { + "type": "line", + "version": 4051, + "versionNonce": 2114615643, + "isDeleted": false, + "id": "eReA_XFu0cSYQhyG-92nc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1179.5795065482205, + "y": 258.57920842354054, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 46.92865289294453, + "height": 2.4757501798128, + "seed": 1385082203, + "groupIds": [ + "siLS3_RDwEMKtYUwaaxN-" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688312381572, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 8.093938105125233, + 1.4279702913643746 + ], + [ + 18.193786115221407, + -0.5912874140789839 + ], + [ + 46.92865289294453, + 1.884462765733816 + ] + ] + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/images/further.png b/docs/images/further.png new file mode 100644 index 0000000..ced2991 Binary files /dev/null and b/docs/images/further.png differ diff --git a/docs/images/indexing-dark.png b/docs/images/indexing-dark.png new file mode 100644 index 0000000..a6266c0 Binary files /dev/null and b/docs/images/indexing-dark.png differ diff --git a/docs/images/indexing.excalidraw b/docs/images/indexing.excalidraw new file mode 100644 index 0000000..4b46ff3 --- /dev/null +++ b/docs/images/indexing.excalidraw @@ -0,0 +1,1802 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "id": "gu5_xiA8n2ahi-4mERgcR", + "type": "rectangle", + "x": 1278.0454545454545, + "y": 234.17792970816078, + "width": 319.99999999999983, + "height": 286.9999999999999, + "angle": 0, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 60, + "groupIds": [], + "strokeSharpness": "sharp", + "seed": 260819005, + "version": 681, + "versionNonce": 1445045779, + "isDeleted": false, + "boundElements": [ + { + "id": "ZyDOc33tL-6s4vwoFYHAq", + "type": "arrow" + }, + { + "id": "5m7yE6eTRKFfSCbA82DrS", + "type": "arrow" + }, + { + "id": "-D1yDTCnqxjTFsQU0jBjN", + "type": "arrow" + } + ], + "updated": 1641735984230 + }, + { + "type": "line", + "version": 5110, + "versionNonce": 785100115, + "isDeleted": false, + "id": "BcQI3V6na893s7vMmTK-q", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1000.4754963515834, + "y": 217.20853592127958, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 77.09201683999922, + "height": 99.49948667804088, + "seed": 597539171, + "groupIds": [ + "ByeZbnpBUniBnZxL1XiMR", + "YDUnsOO4AJOXnzopVjTfR" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908376, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0.2542098813493443, + 75.20117273657175 + ], + [ + 0.011896425679918422, + 83.76249969444815 + ], + [ + 3.970409367559332, + 87.46174320643391 + ], + [ + 17.75573317066317, + 90.59250103325854 + ], + [ + 41.05683533152865, + 91.56737225214069 + ], + [ + 63.319497586673116, + 90.01084754868091 + ], + [ + 75.14781395923075, + 86.28844687220405 + ], + [ + 76.81603792670788, + 83.15042405259751 + ], + [ + 77.05033394391478, + 76.25776215104557 + ], + [ + 76.86643881413028, + 6.3089586511537865 + ], + [ + 76.45188016352971, + -0.2999144698665015 + ], + [ + 71.50179495549581, + -3.9936571317850627 + ], + [ + 61.077971898861186, + -6.132877429442784 + ], + [ + 37.32348754161154, + -7.932114425900202 + ], + [ + 18.278415656797975, + -6.859225353587373 + ], + [ + 3.2995959613238286, + -3.2201165291205287 + ], + [ + -0.04168289608444441, + -0.045185660461322996 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "line", + "version": 2808, + "versionNonce": 923714909, + "isDeleted": false, + "id": "UFrJE_4GqAUPwzMDMeN7A", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 998.9994257898584, + "y": 272.3348195790094, + "strokeColor": "#03a9f4", + "backgroundColor": "transparent", + "width": 77.17198221193564, + "height": 8.562348957853038, + "seed": 960447053, + "groupIds": [ + "ByeZbnpBUniBnZxL1XiMR", + "YDUnsOO4AJOXnzopVjTfR" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908377, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 2.033150371639873, + 3.413095389435587 + ], + [ + 10.801287372573954, + 6.276651055277943 + ], + [ + 22.468666942209353, + 8.010803051612635 + ], + [ + 40.747074201802775, + 8.168828515515864 + ], + [ + 62.077348233027564, + 7.0647721921469495 + ], + [ + 74.53446931782398, + 3.04824021069218 + ], + [ + 77.17198221193564, + -0.3935204423371723 + ] + ] + }, + { + "type": "line", + "version": 2894, + "versionNonce": 439388915, + "isDeleted": false, + "id": "fqsQ_Qaf-7qXlNZOtktUp", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 997.9675684673548, + "y": 243.3707642437863, + "strokeColor": "#03a9f4", + "backgroundColor": "transparent", + "width": 77.17198221193564, + "height": 8.562348957853038, + "seed": 1862702339, + "groupIds": [ + "ByeZbnpBUniBnZxL1XiMR", + "YDUnsOO4AJOXnzopVjTfR" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908377, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 2.033150371639873, + 3.413095389435587 + ], + [ + 10.801287372573954, + 6.276651055277943 + ], + [ + 22.468666942209353, + 8.010803051612635 + ], + [ + 40.747074201802775, + 8.168828515515864 + ], + [ + 62.077348233027564, + 7.0647721921469495 + ], + [ + 74.53446931782398, + 3.04824021069218 + ], + [ + 77.17198221193564, + -0.3935204423371723 + ] + ] + }, + { + "type": "ellipse", + "version": 5897, + "versionNonce": 1193808317, + "isDeleted": false, + "id": "4l6IrZqyWS_r_TywqVTEW", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 996.9096194852727, + "y": 208.07626729559803, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 76.59753601865496, + "height": 15.49127539284798, + "seed": 1511747757, + "groupIds": [ + "ByeZbnpBUniBnZxL1XiMR", + "YDUnsOO4AJOXnzopVjTfR" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "bxuMGTzXLn7H-uBCptINx" + } + ], + "updated": 1641735908377 + }, + { + "type": "ellipse", + "version": 1298, + "versionNonce": 745059475, + "isDeleted": false, + "id": "vrSWcPDbYZk3CuUPgODtk", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1033.4016065904952, + "y": 232.55293497550144, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 11.226103154161754, + "height": 12.183758484455605, + "seed": 2081619107, + "groupIds": [ + "ByeZbnpBUniBnZxL1XiMR", + "YDUnsOO4AJOXnzopVjTfR" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1641735908377 + }, + { + "type": "ellipse", + "version": 1586, + "versionNonce": 857264669, + "isDeleted": false, + "id": "ptDnFZABr-xnrQYrkdvCj", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1033.7527483744288, + "y": 258.76954926256656, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 11.226103154161754, + "height": 12.183758484455605, + "seed": 1076024077, + "groupIds": [ + "ByeZbnpBUniBnZxL1XiMR", + "YDUnsOO4AJOXnzopVjTfR" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1641735908377 + }, + { + "type": "ellipse", + "version": 1470, + "versionNonce": 1160423987, + "isDeleted": false, + "id": "2v5HdhgHuudJY2EmM0hV_", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 6.239590202363168, + "x": 1034.4016065904952, + "y": 287.53827583387294, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 11.226103154161754, + "height": 12.183758484455605, + "seed": 1718710339, + "groupIds": [ + "ByeZbnpBUniBnZxL1XiMR", + "YDUnsOO4AJOXnzopVjTfR" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1641735908377 + }, + { + "type": "text", + "version": 975, + "versionNonce": 2010285693, + "isDeleted": false, + "id": "YJIBcqHtzJtKcCgFzxnQV", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 989.625, + "y": 310.0375083418067, + "strokeColor": "#03a9f4", + "backgroundColor": "transparent", + "width": 94, + "height": 46, + "seed": 131000685, + "groupIds": [ + "YDUnsOO4AJOXnzopVjTfR" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1641735908377, + "fontSize": 17.4778970902999, + "fontFamily": 1, + "text": "Structured\nDatabase", + "baseline": 39, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Structured\nDatabase" + }, + { + "type": "rectangle", + "version": 1600, + "versionNonce": 157084627, + "isDeleted": false, + "id": "YI607mwBteKrjS78gtTXD", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 50, + "angle": 0, + "x": 1281.9090909090908, + "y": 473.0000000000002, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 315.00000000000006, + "height": 40, + "seed": 928276467, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [ + { + "id": "-D1yDTCnqxjTFsQU0jBjN", + "type": "arrow" + } + ], + "updated": 1641735908377 + }, + { + "type": "rectangle", + "version": 1299, + "versionNonce": 2014752477, + "isDeleted": false, + "id": "UsRZ6Y682zf4FIZyNDOtq", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 50, + "angle": 0, + "x": 1280.9090909090908, + "y": 238.6363636363636, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 316.1818181818184, + "height": 47.00000000000002, + "seed": 1663520371, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [ + { + "id": "5m7yE6eTRKFfSCbA82DrS", + "type": "arrow" + }, + { + "id": "ZyDOc33tL-6s4vwoFYHAq", + "type": "arrow" + } + ], + "updated": 1641735908377 + }, + { + "type": "line", + "version": 6222, + "versionNonce": 1024850291, + "isDeleted": false, + "id": "qtt-6VisYFKR2M8Cu45p-", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1002.5946179731507, + "y": 439.26150708178005, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 76.99810389727404, + "height": 99.37827711605759, + "seed": 89125379, + "groupIds": [ + "XwpXytlIickMBY-WleyOp", + "fZX3gzhfhBW0U-bYngONa", + "q5UWDdBSxh3UYgBDbSFBQ" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908378, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0.25390020469272123, + 75.10956320658954 + ], + [ + 0.011881933539366223, + 83.66046081728857 + ], + [ + 3.9655726433067375, + 87.35519793732486 + ], + [ + 17.73410326369428, + 90.48214189738947 + ], + [ + 41.00682018880676, + 91.45582553513545 + ], + [ + 63.24236222825349, + 89.90119697892054 + ], + [ + 75.05626943052894, + 86.18333090428511 + ], + [ + 76.72246117951802, + 83.04913080160064 + ], + [ + 76.95647177899504, + 76.16486549140681 + ], + [ + 76.77280066894052, + 6.301273122914302 + ], + [ + 76.35874703071867, + -0.299549116207539 + ], + [ + 71.41469198102895, + -3.9887920872726523 + ], + [ + 61.003567150978974, + -6.125406402086429 + ], + [ + 37.27802033642641, + -7.922451580922154 + ], + [ + 18.256149021768273, + -6.850869494392455 + ], + [ + 3.2955764171578545, + -3.2161938062295934 + ], + [ + -0.04163211827899763, + -0.0451306156134037 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "ellipse", + "version": 6957, + "versionNonce": 1115081533, + "isDeleted": false, + "id": "xmFIoJeyaqzUfiLrt7yo1", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1002.1009502312106, + "y": 430.490580458146, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 76.50422544892463, + "height": 15.472404032124233, + "seed": 761422765, + "groupIds": [ + "XwpXytlIickMBY-WleyOp", + "fZX3gzhfhBW0U-bYngONa", + "q5UWDdBSxh3UYgBDbSFBQ" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "bxuMGTzXLn7H-uBCptINx" + } + ], + "updated": 1641735908378 + }, + { + "type": "text", + "version": 2153, + "versionNonce": 1570525971, + "isDeleted": false, + "id": "drtrHswdiUqZPuNgKc90d", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1020.1035285928494, + "y": 465.17753663299726, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 18, + "height": 36, + "seed": 455653795, + "groupIds": [ + "fZX3gzhfhBW0U-bYngONa", + "q5UWDdBSxh3UYgBDbSFBQ" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908378, + "fontSize": 29.219434366479078, + "fontFamily": 3, + "text": "<", + "baseline": 29, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "<" + }, + { + "type": "text", + "version": 2133, + "versionNonce": 391404445, + "isDeleted": false, + "id": "Tof7f8O_qqAbmeVWbwNel", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1042.114401938595, + "y": 465.6542466049163, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 18, + "height": 36, + "seed": 336815629, + "groupIds": [ + "fZX3gzhfhBW0U-bYngONa", + "q5UWDdBSxh3UYgBDbSFBQ" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908378, + "fontSize": 29.384515916572173, + "fontFamily": 3, + "text": ">", + "baseline": 29, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": ">" + }, + { + "type": "text", + "version": 1428, + "versionNonce": 2070313139, + "isDeleted": false, + "id": "VzdNhewfEay7ILpboy3kh", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 877.8181818181818, + "y": 531.2795921207232, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 324, + "height": 48, + "seed": 1976012099, + "groupIds": [ + "q5UWDdBSxh3UYgBDbSFBQ" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1641735908378, + "fontSize": 18.16360832146819, + "fontFamily": 1, + "text": "Approximate Nearest Neighbor (ANN)\nIndex", + "baseline": 41, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Approximate Nearest Neighbor (ANN)\nIndex" + }, + { + "type": "rectangle", + "version": 4844, + "versionNonce": 786489341, + "isDeleted": false, + "id": "J8c0oXxyYGwX5-DrgmaWs", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 594.6310358331666, + "y": 328.3910028343104, + "strokeColor": "#343a40", + "backgroundColor": "#fff", + "width": 63.17951567316358, + "height": 95.87140433060104, + "seed": 982009293, + "groupIds": [ + "BldQyeSIaH-oKkL4FMf2Q" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "CFu0B4Mw_1wC1Hbgx8Fs0" + }, + { + "type": "arrow", + "id": "XIl_NhaFtRO00pX5Pq6VU" + }, + { + "type": "arrow", + "id": "EndiSTFlx1AT7vcBVjgve" + }, + { + "id": "eDQoIxWK3cF_GuzA8jGlK", + "type": "arrow" + }, + { + "id": "UldaTf_SD8SLGi5U0s3az", + "type": "arrow" + } + ], + "updated": 1641735908378 + }, + { + "type": "rectangle", + "version": 4895, + "versionNonce": 1411050067, + "isDeleted": false, + "id": "m9UEIWpFo5o-W_PZIjS5i", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 587.8843926619461, + "y": 322.1157517367904, + "strokeColor": "#343a40", + "backgroundColor": "#fff", + "width": 63.17951567316358, + "height": 95.87140433060104, + "seed": 1164020099, + "groupIds": [ + "OQ3HaFukpFi0eOGW5R5UG", + "BldQyeSIaH-oKkL4FMf2Q" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "CFu0B4Mw_1wC1Hbgx8Fs0" + }, + { + "type": "arrow", + "id": "XIl_NhaFtRO00pX5Pq6VU" + }, + { + "type": "arrow", + "id": "EndiSTFlx1AT7vcBVjgve" + }, + { + "id": "eDQoIxWK3cF_GuzA8jGlK", + "type": "arrow" + }, + { + "id": "UldaTf_SD8SLGi5U0s3az", + "type": "arrow" + } + ], + "updated": 1641735908378 + }, + { + "type": "rectangle", + "version": 4989, + "versionNonce": 1058920541, + "isDeleted": false, + "id": "7d9_4d4sJrQ9DvGC8ybba", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 579.4861313362021, + "y": 314.7375928350884, + "strokeColor": "#343a40", + "backgroundColor": "#fff", + "width": 63.17951567316358, + "height": 95.87140433060104, + "seed": 1652032557, + "groupIds": [ + "BldQyeSIaH-oKkL4FMf2Q" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "CFu0B4Mw_1wC1Hbgx8Fs0" + }, + { + "type": "arrow", + "id": "XIl_NhaFtRO00pX5Pq6VU" + }, + { + "type": "arrow", + "id": "EndiSTFlx1AT7vcBVjgve" + } + ], + "updated": 1641735908378 + }, + { + "type": "line", + "version": 4113, + "versionNonce": 747644915, + "isDeleted": false, + "id": "sbNllVyO-9z2sQtdPITdl", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 590.173416729491, + "y": 358.1987100051989, + "strokeColor": "#343a40", + "backgroundColor": "#fff", + "width": 41.63767071747142, + "height": 2.905130632707587, + "seed": 605774115, + "groupIds": [ + "BldQyeSIaH-oKkL4FMf2Q" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908379, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 36.135414139555195, + 0.14069349922795835 + ], + [ + 41.63767071747142, + -2.7644371334796283 + ] + ] + }, + { + "type": "line", + "version": 4136, + "versionNonce": 1104987325, + "isDeleted": false, + "id": "eUkNzCc4WrlKLnap75eGL", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 591.7725612633524, + "y": 330.1315411841826, + "strokeColor": "#343a40", + "backgroundColor": "#fff", + "width": 40.73266929000497, + "height": 2.5058652970606046, + "seed": 1184953997, + "groupIds": [ + "BldQyeSIaH-oKkL4FMf2Q" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908379, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 15.046599363382079, + -2.5058652970606046 + ], + [ + 40.73266929000497, + -0.2927945794743634 + ] + ] + }, + { + "type": "line", + "version": 4162, + "versionNonce": 1791799699, + "isDeleted": false, + "id": "RfDT1TO01chDwfw5Cbtzd", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 591.7870401126618, + "y": 390.3595353798012, + "strokeColor": "#343a40", + "backgroundColor": "#fff", + "width": 43.20811437101611, + "height": 3.8264756613833097, + "seed": 687424707, + "groupIds": [ + "BldQyeSIaH-oKkL4FMf2Q" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908379, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 23.609890181258436, + -0.22815162036966638 + ], + [ + 33.628485051192264, + 2.0697081845366485 + ], + [ + 43.20811437101611, + -1.7567674768466617 + ] + ] + }, + { + "type": "line", + "version": 4198, + "versionNonce": 2059347229, + "isDeleted": false, + "id": "eWFd9AM2v-w_xidHMXRZY", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 588.8193171598485, + "y": 400.3650618336177, + "strokeColor": "#343a40", + "backgroundColor": "#fff", + "width": 48.6343204088089, + "height": 2.600928472214653, + "seed": 1908186349, + "groupIds": [ + "BldQyeSIaH-oKkL4FMf2Q" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908379, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 9.087460853677031, + -1.042860552572967 + ], + [ + 14.419182407365316, + -0.7529003472200484 + ], + [ + 41.35248119200923, + 0.5475638888872795 + ], + [ + 48.6343204088089, + -2.053364583327373 + ] + ] + }, + { + "type": "line", + "version": 4165, + "versionNonce": 1879508787, + "isDeleted": false, + "id": "x-HvHTslIzJHS5qwLLpGG", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 589.2138897559879, + "y": 343.5358653753724, + "strokeColor": "#343a40", + "backgroundColor": "#fff", + "width": 41.94947793197669, + "height": 2.2130707175862407, + "seed": 194622563, + "groupIds": [ + "BldQyeSIaH-oKkL4FMf2Q" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908379, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 16.263408005353806, + -0.5285512538564915 + ], + [ + 41.94947793197669, + 1.6845194637297491 + ] + ] + }, + { + "type": "line", + "version": 4180, + "versionNonce": 1026980221, + "isDeleted": false, + "id": "0lRPNpWYP4Azt2euXF2xI", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 589.1700215474542, + "y": 374.3250106316369, + "strokeColor": "#343a40", + "backgroundColor": "#fff", + "width": 41.94947793197669, + "height": 2.2130707175862407, + "seed": 1532262221, + "groupIds": [ + "BldQyeSIaH-oKkL4FMf2Q" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908379, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 7.2351635300143835, + 1.2764612775431747 + ], + [ + 16.263408005353806, + -0.5285512538564915 + ], + [ + 41.94947793197669, + 1.6845194637297491 + ] + ] + }, + { + "type": "text", + "version": 470, + "versionNonce": 849717459, + "isDeleted": false, + "id": "RZJSTAnmzzhRhyGEUMHmo", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 557, + "y": 432, + "strokeColor": "#343a40", + "backgroundColor": "transparent", + "width": 117, + "height": 26, + "seed": 705534605, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908379, + "fontSize": 20, + "fontFamily": 1, + "text": "Input Data", + "baseline": 18, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Input Data" + }, + { + "type": "diamond", + "version": 1413, + "versionNonce": 1921270237, + "isDeleted": false, + "id": "TVvWBG3ghORc3yVIw7lIh", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 783.6763173734828, + "y": 422.8472597163642, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 73.64736525303452, + "height": 24.04199580676619, + "seed": 1762684669, + "groupIds": [ + "sgYs6pusUBDCv2FmxIFXR" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908379 + }, + { + "type": "diamond", + "version": 1455, + "versionNonce": 406787699, + "isDeleted": false, + "id": "Z9ZwzURs4p74Wb1ozju71", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 783.6763173734828, + "y": 417.04700672905824, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 73.64736525303452, + "height": 24.04199580676619, + "seed": 1250133853, + "groupIds": [ + "sgYs6pusUBDCv2FmxIFXR" + ], + "strokeSharpness": "round", + "boundElements": [ + { + "id": "CjwxjqXOKYE1zbZ-NBw5A", + "type": "arrow" + } + ], + "updated": 1641735908380 + }, + { + "type": "diamond", + "version": 1536, + "versionNonce": 1606024765, + "isDeleted": false, + "id": "1eOQN_-IrMENzxR2quYRb", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 783.6763173734828, + "y": 408.7893130516918, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 73.64736525303452, + "height": 24.04199580676619, + "seed": 1387566013, + "groupIds": [ + "sgYs6pusUBDCv2FmxIFXR" + ], + "strokeSharpness": "round", + "boundElements": [ + { + "id": "UldaTf_SD8SLGi5U0s3az", + "type": "arrow" + }, + { + "id": "CjwxjqXOKYE1zbZ-NBw5A", + "type": "arrow" + } + ], + "updated": 1641735908380 + }, + { + "type": "diamond", + "version": 1584, + "versionNonce": 261738515, + "isDeleted": false, + "id": "UO_8s06e5WPXOXcLW1Gji", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 783.6763173734828, + "y": 400.5962705882732, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 73.64736525303452, + "height": 24.04199580676619, + "seed": 1573196829, + "groupIds": [ + "sgYs6pusUBDCv2FmxIFXR" + ], + "strokeSharpness": "round", + "boundElements": [ + { + "id": "UldaTf_SD8SLGi5U0s3az", + "type": "arrow" + }, + { + "id": "CjwxjqXOKYE1zbZ-NBw5A", + "type": "arrow" + } + ], + "updated": 1641735908380 + }, + { + "type": "text", + "version": 595, + "versionNonce": 733556381, + "isDeleted": false, + "id": "Rc3sIwmWEVTP-seViLAF-", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 776, + "y": 456, + "strokeColor": "#ff7043", + "backgroundColor": "#ffeb3b", + "width": 90, + "height": 26, + "seed": 1267515533, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [ + { + "id": "CjwxjqXOKYE1zbZ-NBw5A", + "type": "arrow" + } + ], + "updated": 1641735908380, + "fontSize": 20, + "fontFamily": 1, + "text": "Vectorize", + "baseline": 18, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Vectorize" + }, + { + "type": "arrow", + "version": 2357, + "versionNonce": 1789679027, + "isDeleted": false, + "id": "UldaTf_SD8SLGi5U0s3az", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 662.8742386138015, + "y": 381.70953615760675, + "strokeColor": "#ff7043", + "backgroundColor": "#ffeb3b", + "width": 115.84862824570905, + "height": 39.38975678911993, + "seed": 1103614243, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908380, + "startBinding": { + "elementId": "m9UEIWpFo5o-W_PZIjS5i", + "focus": -0.05280483881151566, + "gap": 11.810330278691765 + }, + "endBinding": { + "elementId": "UO_8s06e5WPXOXcLW1Gji", + "focus": -1.8872552019606, + "gap": 9.60046258817383 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 115.84862824570905, + 39.38975678911993 + ] + ] + }, + { + "type": "arrow", + "version": 852, + "versionNonce": 624120573, + "isDeleted": false, + "id": "eDQoIxWK3cF_GuzA8jGlK", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 664.8519551997895, + "y": 376.6331008210118, + "strokeColor": "#03a9f4", + "backgroundColor": "#ffeb3b", + "width": 330.68241691348294, + "height": 116.55682104186076, + "seed": 1088931821, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908380, + "startBinding": { + "elementId": "m9UEIWpFo5o-W_PZIjS5i", + "focus": 0.3821910694005074, + "gap": 13.788046864679757 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 330.68241691348294, + -116.55682104186076 + ] + ] + }, + { + "type": "arrow", + "version": 963, + "versionNonce": 2023565139, + "isDeleted": false, + "id": "CjwxjqXOKYE1zbZ-NBw5A", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 863.6765930965225, + "y": 445.6574693739797, + "strokeColor": "#ff7043", + "backgroundColor": "#ffeb3b", + "width": 137.0993469797395, + "height": 39.74595185737945, + "seed": 1654741539, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908380, + "startBinding": { + "elementId": "Rc3sIwmWEVTP-seViLAF-", + "focus": -1.3712294571679748, + "gap": 10.342530626020277 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 137.0993469797395, + 39.74595185737945 + ] + ] + }, + { + "type": "text", + "version": 879, + "versionNonce": 1219095389, + "isDeleted": false, + "id": "jTu74ufZGB6NCYWR8F6cX", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1409.561741267423, + "y": 407.263410502541, + "strokeColor": "#343a40", + "backgroundColor": "#868e96", + "width": 65, + "height": 25, + "seed": 1925987693, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908381, + "fontSize": 20, + "fontFamily": 1, + "text": "Search", + "baseline": 18, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "" + }, + { + "type": "rectangle", + "version": 1230, + "versionNonce": 1606599923, + "isDeleted": false, + "id": "94RaVBybGnyM0x9Gqgwk5", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0.7882111358327366, + "x": 1433.0842846752632, + "y": 375.5724519986199, + "strokeColor": "#000000", + "backgroundColor": "#868e96", + "width": 70.38608542855415, + "height": 6.900596610642562, + "seed": 489839587, + "groupIds": [ + "_mSpNqaGUToycQhKOY82U" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908381 + }, + { + "type": "ellipse", + "version": 1066, + "versionNonce": 107902909, + "isDeleted": false, + "id": "x9CPx10TrNm9T_lgZgUAM", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1401.9154962081336, + "y": 314.9184076792772, + "strokeColor": "#000000", + "backgroundColor": "#868e96", + "width": 66.0077798878363, + "height": 66.0077798878363, + "seed": 1251739597, + "groupIds": [ + "_mSpNqaGUToycQhKOY82U" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908381 + }, + { + "type": "ellipse", + "version": 1153, + "versionNonce": 933473939, + "isDeleted": false, + "id": "EcCeRt1xfAokj2Az9b8OW", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1411.809595977187, + "y": 325.8539916345475, + "strokeColor": "#000000", + "backgroundColor": "white", + "width": 47.35405419542672, + "height": 47.35405419542672, + "seed": 1122166659, + "groupIds": [ + "_mSpNqaGUToycQhKOY82U" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908381 + }, + { + "type": "text", + "version": 1087, + "versionNonce": 1218607133, + "isDeleted": false, + "id": "haJCsmkSJ7-AvLSKoAVa0", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1293.0909090909092, + "y": 248.72727272727272, + "strokeColor": "#343a40", + "backgroundColor": "#fd7e14", + "width": 297, + "height": 26, + "seed": 1064787981, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [ + { + "id": "5m7yE6eTRKFfSCbA82DrS", + "type": "arrow" + }, + { + "id": "ZyDOc33tL-6s4vwoFYHAq", + "type": "arrow" + } + ], + "updated": 1641735908381, + "fontSize": 20, + "fontFamily": 1, + "text": "SQL> SELECT {} FROM txtai", + "baseline": 18, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "SQL> SELECT {} FROM txtai" + }, + { + "type": "text", + "version": 1452, + "versionNonce": 1660314675, + "isDeleted": false, + "id": "M5hw4wHqIGw3PXnaLCp6p", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1307.1818181818182, + "y": 478.18181818181836, + "strokeColor": "#343a40", + "backgroundColor": "#fd7e14", + "width": 261, + "height": 26, + "seed": 1708000109, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [ + { + "id": "-D1yDTCnqxjTFsQU0jBjN", + "type": "arrow" + } + ], + "updated": 1641735908381, + "fontSize": 20, + "fontFamily": 1, + "text": "> Natural Language Query", + "baseline": 18, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "> Natural Language Query" + }, + { + "type": "arrow", + "version": 1819, + "versionNonce": 959933565, + "isDeleted": false, + "id": "5m7yE6eTRKFfSCbA82DrS", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1272.868226475393, + "y": 268.6451583650937, + "strokeColor": "#ff7043", + "backgroundColor": "#7950f2", + "width": 188.8189376264454, + "height": 219.36950558832018, + "seed": 1269992291, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908382, + "startBinding": { + "elementId": "UsRZ6Y682zf4FIZyNDOtq", + "focus": 0.900241545557177, + "gap": 8.040864433697834 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -188.8189376264454, + 219.36950558832018 + ] + ] + }, + { + "type": "arrow", + "version": 1276, + "versionNonce": 698443219, + "isDeleted": false, + "id": "ZyDOc33tL-6s4vwoFYHAq", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1077.7543216412146, + "y": 263.29298489476895, + "strokeColor": "#03a9f4", + "backgroundColor": "#7950f2", + "width": 199.76731534744795, + "height": 0.40309918148193447, + "seed": 343568205, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908382, + "startBinding": null, + "endBinding": { + "elementId": "haJCsmkSJ7-AvLSKoAVa0", + "focus": -0.17292767152914681, + "gap": 15.569272102246714 + }, + "lastCommittedPoint": null, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 199.76731534744795, + 0.40309918148193447 + ] + ] + }, + { + "type": "arrow", + "version": 1786, + "versionNonce": 1206834397, + "isDeleted": false, + "id": "-D1yDTCnqxjTFsQU0jBjN", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1275.2935337071021, + "y": 493.40248820420277, + "strokeColor": "#ff7043", + "backgroundColor": "#7950f2", + "width": 193.34252933148332, + "height": 3.1975454283124236, + "seed": 855220461, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641735908382, + "startBinding": { + "elementId": "YI607mwBteKrjS78gtTXD", + "focus": -0.13787667355971328, + "gap": 6.615557201988622 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -193.34252933148332, + -3.1975454283124236 + ] + ] + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/images/indexing.png b/docs/images/indexing.png new file mode 100644 index 0000000..1eec3f0 Binary files /dev/null and b/docs/images/indexing.png differ diff --git a/docs/images/install-dark.png b/docs/images/install-dark.png new file mode 100644 index 0000000..b85e0db Binary files /dev/null and b/docs/images/install-dark.png differ diff --git a/docs/images/install.excalidraw b/docs/images/install.excalidraw new file mode 100644 index 0000000..2224e19 --- /dev/null +++ b/docs/images/install.excalidraw @@ -0,0 +1,106 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 1435, + "versionNonce": 1860045045, + "isDeleted": false, + "id": "LgVOdCQnXRH2GYHIGLwOx", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 1.5707963267948957, + "x": 764, + "y": 123.99999999999991, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 20.000000000000092, + "height": 199.99999999999991, + "seed": 782310408, + "groupIds": [ + "s5mCNZzJxsETmWhZoD3qR" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688311151676, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 1534, + "versionNonce": 2090527963, + "isDeleted": false, + "id": "GG_mFtlk6nEiYCQ8vhzjZ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 1.5707963267948957, + "x": 953, + "y": 136.9999999999999, + "strokeColor": "#ced4da", + "backgroundColor": "#ced4da", + "width": 17.99999999999998, + "height": 173.99999999999994, + "seed": 810177544, + "groupIds": [ + "Rezl6DUXkEY8GT9kuDOQX" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688311151677, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 293, + "versionNonce": 1618682453, + "isDeleted": false, + "id": "NLfZWFLgvJj3ccuF5yxD8", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 793, + "y": 216.86805555555557, + "strokeColor": "#343a40", + "backgroundColor": "transparent", + "width": 80, + "height": 16, + "seed": 297813880, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688311151677, + "link": null, + "locked": false, + "fontSize": 12.242424242424246, + "fontFamily": 1, + "text": "Installing.......", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Installing.......", + "lineHeight": 1.3069306930693065, + "baseline": 11 + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/images/install.png b/docs/images/install.png new file mode 100644 index 0000000..b95d97e Binary files /dev/null and b/docs/images/install.png differ diff --git a/docs/images/llm.excalidraw b/docs/images/llm.excalidraw new file mode 100644 index 0000000..abd17bf --- /dev/null +++ b/docs/images/llm.excalidraw @@ -0,0 +1,1350 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "text", + "version": 1159, + "versionNonce": 947191573, + "isDeleted": false, + "id": "yF7ftUwr3mnAwOlC59RMi", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1475.7768832949857, + "y": 232.08688631142672, + "strokeColor": "#03a9f4", + "backgroundColor": "transparent", + "width": 268.6666564941406, + "height": 33.6, + "seed": 376471794, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1691335913281, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Workflow Processing", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Workflow Processing", + "lineHeight": 1.2, + "baseline": 24 + }, + { + "type": "line", + "version": 1539, + "versionNonce": 1192947061, + "isDeleted": false, + "id": "yVN5CxzfpwAZ0J0GEsEim", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1595.7169087648438, + "y": 44.55863019278951, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 170.54791263920666, + "height": 165.88781924917737, + "seed": 1276926237, + "groupIds": [ + "ZD4Gm6iTkUS0-nxoYPoXN" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1691335877136, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -4.396962054402082, + 1.1646874339146507 + ], + [ + -63.9402715195358, + 29.56596636773484 + ], + [ + -63.9402715195358, + 29.56596636773484 + ], + [ + -67.88778686931518, + 32.68804950762777 + ], + [ + -70.0842642471441, + 37.216572224302446 + ], + [ + -84.65888566945969, + 100.95916519983551 + ], + [ + -84.65888566945969, + 100.95916519983551 + ], + [ + -84.76907687141536, + 105.46565159474407 + ], + [ + -83.05610475455529, + 109.6515846607256 + ], + [ + -82.41499236471047, + 110.54913805780339 + ], + [ + -41.27695016258076, + 161.6992265392742 + ], + [ + -41.27695016258076, + 161.6992265392742 + ], + [ + -37.31206815320823, + 164.8012680903417 + ], + [ + -32.397540410633006, + 165.88781924917737 + ], + [ + 33.58360912709668, + 165.88781924917697 + ], + [ + 33.58360912709668, + 165.88781924917697 + ], + [ + 38.509483664717834, + 164.7592029175085 + ], + [ + 42.47369821555802, + 161.6511389543416 + ], + [ + 83.590366618572, + 110.49037113635723 + ], + [ + 83.590366618572, + 110.49037113635723 + ], + [ + 85.77883576779129, + 105.97187048787097 + ], + [ + 85.7274057950955, + 100.94848586332202 + ], + [ + 71.05662649840329, + 37.1524605394965 + ], + [ + 71.05662649840329, + 37.1524605394965 + ], + [ + 68.85613290539939, + 32.62393782282196 + ], + [ + 64.91262485436425, + 29.50185468292866 + ], + [ + 5.465486737767179, + 1.1700299681670856 + ], + [ + 5.465486737767179, + 1.1700299681670856 + ], + [ + -0.053422158085595584, + -3.469446951953614e-18 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "line", + "version": 4960, + "versionNonce": 2121044059, + "isDeleted": false, + "id": "aEfZn91vFxUsNoiS_231h", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1881.7866460270468, + "y": -65.38351814097682, + "strokeColor": "#ff7043", + "backgroundColor": "#fa5252", + "width": 84.50536343081912, + "height": 109.06758737884206, + "seed": 1361500846, + "groupIds": [ + "YlFNaYy8uxiVBIOqH078l" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1691335877136, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0.2786552913217619, + 82.43269138641281 + ], + [ + 0.013040413480105428, + 91.8172953399838 + ], + [ + 4.352213112689576, + 95.87226666144052 + ], + [ + 19.46316552700477, + 99.30408539980334 + ], + [ + 45.00495554826318, + 100.37270248918188 + ], + [ + 69.40844688139491, + 98.66649877125488 + ], + [ + 82.37420150566703, + 94.58614343882556 + ], + [ + 84.20284574710968, + 91.14636108917688 + ], + [ + 84.45967221106837, + 83.5908848820192 + ], + [ + 84.25809330040309, + 6.915642702567624 + ], + [ + 83.80366973142185, + -0.3287549387484745 + ], + [ + 78.37757288423978, + -4.377696435676519 + ], + [ + 66.95137090060692, + -6.722629103443413 + ], + [ + 40.91260040265366, + -8.694884889660191 + ], + [ + 20.036110369548158, + -7.518824323429054 + ], + [ + 3.616892738261588, + -3.529770380085963 + ], + [ + -0.04569121975075156, + -0.049530818049170484 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "line", + "version": 2704, + "versionNonce": 1487369941, + "isDeleted": false, + "id": "JSkCiTP6nfh3_0WXfktLt", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1887.3924109908396, + "y": -3.2136630318863695, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 78.18555964094003, + "height": 8.95436195346155, + "seed": 1565257522, + "groupIds": [ + "YlFNaYy8uxiVBIOqH078l" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1691335877136, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 2.228662812409231, + 3.741306533812953 + ], + [ + 11.83996414096264, + 6.880228333571024 + ], + [ + 24.62930590722697, + 8.7811403955643 + ], + [ + 44.66540707207591, + 8.95436195346155 + ], + [ + 68.04684957381593, + 7.744137021248128 + ], + [ + 78.18555964094003, + 5.099524329682907 + ], + [ + 76.97433252487917, + 5.429165362754105 + ] + ] + }, + { + "type": "line", + "version": 2790, + "versionNonce": 13618427, + "isDeleted": false, + "id": "XbZa4_v84aJkwUbHNYtWo", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1881.5978130909527, + "y": -29.55045127271626, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 84.59301845781613, + "height": 9.385724231428382, + "seed": 1628656878, + "groupIds": [ + "YlFNaYy8uxiVBIOqH078l" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1691335877136, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 2.228662812409231, + 3.7413065338129523 + ], + [ + 11.83996414096264, + 6.880228333571017 + ], + [ + 24.62930590722697, + 8.7811403955643 + ], + [ + 44.66540707207591, + 8.95436195346155 + ], + [ + 68.04684957381593, + 7.744137021248123 + ], + [ + 81.70187622537257, + 3.341366037466631 + ], + [ + 84.59301845781613, + -0.43136227796683113 + ] + ] + }, + { + "type": "ellipse", + "version": 5809, + "versionNonce": 170997243, + "isDeleted": false, + "id": "WgZ_UNGw6chJCNh_YqBCg", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1883.146078835665, + "y": -74.22048622222516, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 83.96333219036016, + "height": 16.980952253415385, + "seed": 1814771954, + "groupIds": [ + "YlFNaYy8uxiVBIOqH078l" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1691336141044, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 1151, + "versionNonce": 1596584437, + "isDeleted": false, + "id": "5zDzUgYAzqCuHz5y0fEbO", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1949.5714949045796, + "y": -45.83125704870746, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 12.305631190363249, + "height": 13.3553768715004, + "seed": 704022318, + "groupIds": [ + "YlFNaYy8uxiVBIOqH078l" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1691336146522, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 1200, + "versionNonce": 1743051285, + "isDeleted": false, + "id": "VtzlusDzgSLcDDBt5hG2x", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1949.5714949045796, + "y": -16.516229929297197, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 12.305631190363249, + "height": 13.3553768715004, + "seed": 2080066226, + "groupIds": [ + "YlFNaYy8uxiVBIOqH078l" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1691336152276, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 1253, + "versionNonce": 1752124155, + "isDeleted": false, + "id": "B_0DhVcOs-USQL4XV0QRo", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1949.5714949045796, + "y": 15.345309440526933, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 12.305631190363249, + "height": 13.3553768715004, + "seed": 1278833006, + "groupIds": [ + "YlFNaYy8uxiVBIOqH078l" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1691336157480, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1070, + "versionNonce": 652075765, + "isDeleted": false, + "id": "4zMdwTCbTrrgg-ITOMXC0", + "fillStyle": "hachure", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1779.4509372464736, + "y": 51.50880129297349, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 298.8666687011719, + "height": 33.6, + "seed": 1848588206, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "leDc6Y5qdU0OH3j90C072", + "type": "arrow" + } + ], + "updated": 1691336802938, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Embeddings Database", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Embeddings Database", + "lineHeight": 1.2, + "baseline": 24 + }, + { + "type": "arrow", + "version": 3186, + "versionNonce": 1016072923, + "isDeleted": false, + "id": "iPTphyfJkvqyMptaZS0ge", + "fillStyle": "hachure", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1878.6382080826127, + "y": -7.322815845013395, + "strokeColor": "#ff7043", + "backgroundColor": "#7950f2", + "width": 202.4871709755887, + "height": 104.80578637477413, + "seed": 1965679388, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1691335877136, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -202.4871709755887, + 104.80578637477413 + ] + ] + }, + { + "type": "text", + "version": 906, + "versionNonce": 1278878741, + "isDeleted": false, + "id": "YCEwpQiKAjRS3zIG3_E8s", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1847.5109174458237, + "y": 281.40729511478503, + "strokeColor": "#7950f2", + "backgroundColor": "transparent", + "width": 159.76666259765625, + "height": 33.6, + "seed": 676249636, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1691335908714, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "LLM Models", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "LLM Models", + "lineHeight": 1.2, + "baseline": 24 + }, + { + "type": "arrow", + "version": 3832, + "versionNonce": 1540672693, + "isDeleted": false, + "id": "leDc6Y5qdU0OH3j90C072", + "fillStyle": "hachure", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1828.6470213908196, + "y": 222.85602296508534, + "strokeColor": "#7950f2", + "backgroundColor": "#7950f2", + "width": 145.94742162238094, + "height": 61.13637008509096, + "seed": 241264412, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1691335877141, + "link": null, + "locked": false, + "startBinding": { + "elementId": "ZTKEryJuyeKfeG4nAI4_e", + "focus": -0.0827769297585387, + "gap": 11.926065811794956 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -145.94742162238094, + -61.13637008509096 + ] + ] + }, + { + "type": "line", + "version": 2313, + "versionNonce": 303665589, + "isDeleted": false, + "id": "aFBJSnHUVo8ZDTb0uAkQ2", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1294.8750417574363, + "y": -49.56118957084571, + "strokeColor": "#ffeb3b", + "backgroundColor": "#ffeb3b", + "width": 116.42036295658872, + "height": 103.65107323746608, + "seed": 1963890941, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1691335877136, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -62.44191743896485, + 19.19929080548739 + ], + [ + -63.17668831316513, + 79.43840749607878 + ], + [ + -7.618334228588694, + 103.65107323746608 + ], + [ + 51.963117173367294, + 79.15871076413049 + ], + [ + 53.24367464342358, + 21.28567723840068 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "text", + "version": 876, + "versionNonce": 1665749019, + "isDeleted": false, + "id": "eqOUg003X-50uDJ_XGUnB", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1186.6810892450637, + "y": 59.89380735137763, + "strokeColor": "#ffb13b", + "backgroundColor": "#00e676", + "width": 208.96665954589844, + "height": 33.6, + "seed": 1122464339, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1691335877136, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Workflow Start", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Workflow Start", + "lineHeight": 1.2, + "baseline": 24 + }, + { + "type": "rectangle", + "version": 1789, + "versionNonce": 1288147733, + "isDeleted": false, + "id": "ZTKEryJuyeKfeG4nAI4_e", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1840.5730872026145, + "y": 200.79305844892008, + "strokeColor": "#7950f2", + "backgroundColor": "#7950f2", + "width": 23.299119994671287, + "height": 58.247799986678125, + "seed": 1188509340, + "groupIds": [ + "SFux9iuPQVutxOMe0wlW3" + ], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "leDc6Y5qdU0OH3j90C072", + "type": "arrow" + } + ], + "updated": 1691335877136, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 1934, + "versionNonce": 2028890229, + "isDeleted": false, + "id": "CckqOoWddQBHuvidbpk4P", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1888.8925200747174, + "y": 145.68194499341337, + "strokeColor": "#7950f2", + "backgroundColor": "#7950f2", + "width": 23.29911999467125, + "height": 116.4955999733563, + "seed": 331107492, + "groupIds": [ + "SFux9iuPQVutxOMe0wlW3" + ], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "leDc6Y5qdU0OH3j90C072", + "type": "arrow" + } + ], + "updated": 1691335877136, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 1815, + "versionNonce": 568477019, + "isDeleted": false, + "id": "J8ox4BBdzEg-04DGRL-jh", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1932.9361110371237, + "y": 180.63062498542024, + "strokeColor": "#7950f2", + "backgroundColor": "#7950f2", + "width": 23.29911999467127, + "height": 81.54691998134939, + "seed": 1316437916, + "groupIds": [ + "SFux9iuPQVutxOMe0wlW3" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1691335877136, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 2003, + "versionNonce": 408130005, + "isDeleted": false, + "id": "V54vdkZmQeI13AEahvNPV", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1974.6102076249463, + "y": 155.6767771383196, + "strokeColor": "#7950f2", + "backgroundColor": "#7950f2", + "width": 23.29911999467125, + "height": 104.84603997602068, + "seed": 1025829916, + "groupIds": [ + "SFux9iuPQVutxOMe0wlW3" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1691335877136, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 279, + "versionNonce": 1975968251, + "isDeleted": false, + "id": "Inol-LWi8GThocPMvSZGX", + "fillStyle": "hachure", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1347.5263615107983, + "y": 20.516421378236373, + "strokeColor": "#ffeb3b", + "backgroundColor": "transparent", + "width": 168.1111111111113, + "height": 78.7777777777778, + "seed": 1433457683, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1691335877136, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 168.1111111111113, + 78.7777777777778 + ] + ] + }, + { + "type": "line", + "version": 2357, + "versionNonce": 1477218101, + "isDeleted": false, + "id": "8H60JYjdZgpvCjCSLf54L", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1292.9959980885017, + "y": 174.32286623612742, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 116.42036295658872, + "height": 103.65107323746608, + "seed": 1027783997, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1691335877136, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -62.44191743896485, + 19.19929080548739 + ], + [ + -63.17668831316513, + 79.43840749607878 + ], + [ + -7.618334228588694, + 103.65107323746608 + ], + [ + 51.963117173367294, + 79.15871076413049 + ], + [ + 53.24367464342358, + 21.28567723840068 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "text", + "version": 823, + "versionNonce": 1747905179, + "isDeleted": false, + "id": "E_wil3E0D2OO7QeMf_xiu", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1194.426360493546, + "y": 283.38308804490305, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 177.48333740234375, + "height": 33.6, + "seed": 754233971, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1691335877136, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Workflow End", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Workflow End", + "lineHeight": 1.2, + "baseline": 24 + }, + { + "type": "arrow", + "version": 385, + "versionNonce": 645849237, + "isDeleted": false, + "id": "5pptt-wBgcgbxd5LUka3S", + "fillStyle": "hachure", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1514.4152503996868, + "y": 167.62753248934757, + "strokeColor": "#00e676", + "backgroundColor": "#34bbde", + "width": 171.8888888888889, + "height": 57.1111111111112, + "seed": 603518365, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1691335877136, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -171.8888888888889, + 57.1111111111112 + ] + ] + }, + { + "type": "ellipse", + "version": 1775, + "versionNonce": 768401211, + "isDeleted": false, + "id": "GPI87MG84w7ACdrAaKDkG", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 40, + "angle": 0, + "x": 1553.9985837330207, + "y": 157.35401003071968, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 28.340425531915425, + "height": 27.553191489362284, + "seed": 1372805341, + "groupIds": [ + "Q6IEbA3c43OUEmWwOqoB-" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1691335877136, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 1683, + "versionNonce": 157287925, + "isDeleted": false, + "id": "x4O7WOdzCU_nAV1UPLb10", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 40, + "angle": 0, + "x": 1554.392200754299, + "y": 90.61996747752698, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 28.340425531915475, + "height": 25.978723404255863, + "seed": 1393440061, + "groupIds": [ + "Q6IEbA3c43OUEmWwOqoB-" + ], + "frameId": null, + "roundness": { + "type": 1 + }, + "boundElements": [], + "updated": 1691335877136, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 1831, + "versionNonce": 1090731995, + "isDeleted": false, + "id": "OAHRxshgLCmGZDCtG_FGl", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 40, + "angle": 0, + "x": 1618.1581582011052, + "y": 90.22635045624997, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 28.340425531915425, + "height": 27.553191489362284, + "seed": 896435613, + "groupIds": [ + "Q6IEbA3c43OUEmWwOqoB-" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1691335877136, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 1777, + "versionNonce": 867532629, + "isDeleted": false, + "id": "uB-LQPQxLXAYzpMkkFNId", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 40, + "angle": 0, + "x": 1583.4087576843299, + "y": 102.97914403772018, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 35.87707425165013, + "height": 0, + "seed": 472238589, + "groupIds": [ + "Q6IEbA3c43OUEmWwOqoB-" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1691335877136, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 35.87707425165013, + 0 + ] + ] + }, + { + "type": "line", + "version": 1658, + "versionNonce": 736398459, + "isDeleted": false, + "id": "AP8DlX5fhd-adRn_4ictA", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 40, + "angle": 0, + "x": 1568.1322242882986, + "y": 117.53634476915352, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 0, + "height": 37.78723404255394, + "seed": 1211466333, + "groupIds": [ + "Q6IEbA3c43OUEmWwOqoB-" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1691335877136, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 37.78723404255394 + ] + ] + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/images/llm.png b/docs/images/llm.png new file mode 100644 index 0000000..97019fa Binary files /dev/null and b/docs/images/llm.png differ diff --git a/docs/images/logo.png b/docs/images/logo.png new file mode 100644 index 0000000..bbd5055 Binary files /dev/null and b/docs/images/logo.png differ diff --git a/docs/images/models.excalidraw b/docs/images/models.excalidraw new file mode 100644 index 0000000..1b707ea --- /dev/null +++ b/docs/images/models.excalidraw @@ -0,0 +1,531 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "line", + "version": 1608, + "versionNonce": 602661205, + "isDeleted": false, + "id": "yVN5CxzfpwAZ0J0GEsEim", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1702.2261496777674, + "y": -5.340583377694145, + "strokeColor": "#ffeb3b", + "backgroundColor": "#ffeb3b", + "width": 191.85486538200286, + "height": 186.6125755399512, + "seed": 1276926237, + "groupIds": [ + "ZD4Gm6iTkUS0-nxoYPoXN" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688309214322, + "link": null, + "locked": false, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -4.946284888409469, + 1.310194580443298 + ], + [ + -71.92848036094448, + 33.259712239166916 + ], + [ + -71.92848036094448, + 33.259712239166916 + ], + [ + -76.36916810848369, + 36.77184458512375 + ], + [ + -78.84005658267724, + 41.86612631945928 + ], + [ + -95.23551981468633, + 113.57223168992945 + ], + [ + -95.23551981468633, + 113.57223168992945 + ], + [ + -95.35947746324585, + 118.64172405287644 + ], + [ + -93.4324997019961, + 123.35061560390292 + ], + [ + -92.71129163005317, + 124.36030246259705 + ], + [ + -46.433776844719105, + 181.9006920694186 + ], + [ + -46.433776844719105, + 181.9006920694186 + ], + [ + -41.973552779866964, + 185.39027898361584 + ], + [ + -36.44504150185128, + 186.6125755399512 + ], + [ + 37.77928857887258, + 186.61257553995074 + ], + [ + 37.77928857887258, + 186.61257553995074 + ], + [ + 43.320564233786016, + 185.34295850958426 + ], + [ + 47.78003745293803, + 181.84659678914795 + ], + [ + 94.03350816005033, + 124.29419365112881 + ], + [ + 94.03350816005033, + 124.29419365112881 + ], + [ + 96.49538791875702, + 119.21118606558457 + ], + [ + 96.43753267834124, + 113.56021815873193 + ], + [ + 79.9338983420456, + 41.794005010746 + ], + [ + 79.9338983420456, + 41.794005010746 + ], + [ + 77.45849189744823, + 36.69972327641062 + ], + [ + 73.02231208993142, + 33.18759093045337 + ], + [ + 6.148302879201523, + 1.3162045700933498 + ], + [ + 6.148302879201523, + 1.3162045700933498 + ], + [ + -0.060096314222328626, + -3.469446951953614e-18 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "rectangle", + "version": 2051, + "versionNonce": 216616981, + "isDeleted": false, + "id": "ZTKEryJuyeKfeG4nAI4_e", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2003.5691665051115, + "y": 81.12265056081542, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 30.25969046240687, + "height": 75.64922615601705, + "seed": 1188509340, + "groupIds": [ + "SFux9iuPQVutxOMe0wlW3" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688309215087, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 2192, + "versionNonce": 1390810587, + "isDeleted": false, + "id": "CckqOoWddQBHuvidbpk4P", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2066.323943610997, + "y": 9.547190432552021, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 30.25969046240682, + "height": 151.29845231203416, + "seed": 331107492, + "groupIds": [ + "SFux9iuPQVutxOMe0wlW3" + ], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "leDc6Y5qdU0OH3j90C072", + "type": "arrow" + } + ], + "updated": 1688308769366, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 2072, + "versionNonce": 1184041301, + "isDeleted": false, + "id": "J8ox4BBdzEg-04DGRL-jh", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2123.5254786767036, + "y": 54.936726126162256, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 30.259690462406844, + "height": 105.90891661842389, + "seed": 1316437916, + "groupIds": [ + "SFux9iuPQVutxOMe0wlW3" + ], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "5pptt-wBgcgbxd5LUka3S", + "type": "arrow" + } + ], + "updated": 1688308769366, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 2259, + "versionNonce": 1727832699, + "isDeleted": false, + "id": "V54vdkZmQeI13AEahvNPV", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2177.649637157211, + "y": 22.527960822066646, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 30.25969046240682, + "height": 136.16860708083075, + "seed": 1025829916, + "groupIds": [ + "SFux9iuPQVutxOMe0wlW3" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688308769366, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 442, + "versionNonce": 1922817787, + "isDeleted": false, + "id": "Inol-LWi8GThocPMvSZGX", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1374.656480958798, + "y": 99.23064378133208, + "strokeColor": "#03a9f4", + "backgroundColor": "transparent", + "width": 216.36132864000353, + "height": 0.12505261106101684, + "seed": 1653816315, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688309230624, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 216.36132864000353, + -0.12505261106101684 + ] + ] + }, + { + "type": "arrow", + "version": 917, + "versionNonce": 151223483, + "isDeleted": false, + "id": "5pptt-wBgcgbxd5LUka3S", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1802.88085185622, + "y": 95.10577155105176, + "strokeColor": "#00e676", + "backgroundColor": "#34bbde", + "width": 184.23585514045453, + "height": 3.170483019063738, + "seed": 1717004827, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688309218767, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 184.23585514045453, + 3.170483019063738 + ] + ] + }, + { + "type": "line", + "version": 4226, + "versionNonce": 789213499, + "isDeleted": false, + "id": "pikmP5MLN0MZlpnSik5KN", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1235.1617590461694, + "y": 25.06522848370164, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 127.43688004487302, + "height": 164.47752527518452, + "seed": 844586005, + "groupIds": [ + "xkmNAKuTv_iYXPLj0sxbd" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1688309232141, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0.4202213858663673, + 124.31122212244284 + ], + [ + 0.019665374373081804, + 138.46351494441197 + ], + [ + 6.563281167657464, + 144.5785456702537 + ], + [ + 29.351096662508862, + 149.75384171231534 + ], + [ + 67.86895989535736, + 151.36535158937204 + ], + [ + 104.67023109801114, + 148.79234001109992 + ], + [ + 124.22301744981843, + 142.63902935804376 + ], + [ + 126.98067338289118, + 137.45172392705157 + ], + [ + 127.36797617588668, + 126.05781617962573 + ], + [ + 127.06398851859365, + 10.429017682904831 + ], + [ + 126.37870276277025, + -0.4957733094390701 + ], + [ + 118.19596944321839, + -6.601710860670866 + ], + [ + 100.96487933911183, + -10.137946798406993 + ], + [ + 61.697553127560816, + -13.11217368581247 + ], + [ + 30.215116414714352, + -11.338635495813458 + ], + [ + 5.454393748609269, + -5.323010354025768 + ], + [ + -0.06890386898634299, + -0.074694110077688 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "ellipse", + "version": 5067, + "versionNonce": 1912378357, + "isDeleted": false, + "id": "sPZv-4m6hv0RYSW1Kzrrb", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1235.9555759014816, + "y": 13.472824531333586, + "strokeColor": "#03a9f4", + "backgroundColor": "transparent", + "width": 126.61947902597214, + "height": 25.607837035548464, + "seed": 194842997, + "groupIds": [ + "xkmNAKuTv_iYXPLj0sxbd" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1688309232141, + "link": null, + "locked": false + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/images/models.png b/docs/images/models.png new file mode 100644 index 0000000..bd092da Binary files /dev/null and b/docs/images/models.png differ diff --git a/docs/images/pipeline-dark.png b/docs/images/pipeline-dark.png new file mode 100644 index 0000000..95267a4 Binary files /dev/null and b/docs/images/pipeline-dark.png differ diff --git a/docs/images/pipeline.excalidraw b/docs/images/pipeline.excalidraw new file mode 100644 index 0000000..a8e3137 --- /dev/null +++ b/docs/images/pipeline.excalidraw @@ -0,0 +1,1439 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 4788, + "versionNonce": 1348021112, + "isDeleted": false, + "id": "9itz4bqXi6IMeZhGhc42J", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 793.1319708150991, + "y": 84.01158837080237, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 2026142776, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "CFu0B4Mw_1wC1Hbgx8Fs0" + }, + { + "type": "arrow", + "id": "XIl_NhaFtRO00pX5Pq6VU" + }, + { + "type": "arrow", + "id": "EndiSTFlx1AT7vcBVjgve" + }, + { + "id": "sbyEF_hTthhrIaA-47du2", + "type": "arrow" + }, + { + "id": "3DJ_wB8Ty8UNX4KAZfwjT", + "type": "arrow" + } + ], + "updated": 1641741755942 + }, + { + "type": "rectangle", + "version": 4836, + "versionNonce": 1572562952, + "isDeleted": false, + "id": "7HUYNqBGn8JbkXw6qt_jr", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 785.5845377793848, + "y": 76.99149908508815, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 1090159432, + "groupIds": [ + "6R5o6OnVZ7fFC_87zCo7G", + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "CFu0B4Mw_1wC1Hbgx8Fs0" + }, + { + "type": "arrow", + "id": "XIl_NhaFtRO00pX5Pq6VU" + }, + { + "type": "arrow", + "id": "EndiSTFlx1AT7vcBVjgve" + } + ], + "updated": 1641741755942 + }, + { + "type": "rectangle", + "version": 4932, + "versionNonce": 2081750136, + "isDeleted": false, + "id": "mI_Mfw3cNZf0AuQhK5KUO", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 776.1894484936695, + "y": 68.73759283508838, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 1146631480, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "CFu0B4Mw_1wC1Hbgx8Fs0" + }, + { + "type": "arrow", + "id": "XIl_NhaFtRO00pX5Pq6VU" + }, + { + "type": "arrow", + "id": "EndiSTFlx1AT7vcBVjgve" + } + ], + "updated": 1641741755942 + }, + { + "type": "line", + "version": 4055, + "versionNonce": 958473992, + "isDeleted": false, + "id": "y3oKPiP36axXzEF0c7OiC", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 788.1758750805805, + "y": 117.48482988244103, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 46.57983585730082, + "height": 3.249953844290203, + "seed": 1952111176, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641741755942, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 40.42449133807562, + 0.1573930526684746 + ], + [ + 46.57983585730082, + -3.0925607916217284 + ] + ] + }, + { + "type": "line", + "version": 4081, + "versionNonce": 1110355320, + "isDeleted": false, + "id": "-52Udo1sfovtw-gxgUT91", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 790.0311273185948, + "y": 86.01954258360246, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 45.567415680676426, + "height": 2.8032978840147194, + "seed": 923586104, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641741755943, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 16.832548902953302, + -2.8032978840147194 + ], + [ + 45.567415680676426, + -0.3275477042019195 + ] + ] + }, + { + "type": "line", + "version": 4106, + "versionNonce": 1064764936, + "isDeleted": false, + "id": "7Tgba-Lgh4JYVwHG-MlYn", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 789.9545278022254, + "y": 153.41391685395956, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 48.33668263438425, + "height": 4.280657518731036, + "seed": 664293704, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641741755943, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 26.41225578429045, + -0.2552319773002338 + ], + [ + 37.62000339651456, + 2.3153712935189787 + ], + [ + 48.33668263438425, + -1.9652862252120569 + ] + ] + }, + { + "type": "line", + "version": 4143, + "versionNonce": 1542078072, + "isDeleted": false, + "id": "KsHbDZNNeZoS7zbhCYNon", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 786.770480620695, + "y": 164.65706526223835, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 54.40694982784246, + "height": 2.9096445412231735, + "seed": 2043037496, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641741755943, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 10.166093050596771, + -1.166642430373031 + ], + [ + 16.130660965377448, + -0.8422655250909383 + ], + [ + 46.26079588567538, + 0.6125567455206506 + ], + [ + 54.40694982784246, + -2.297087795702523 + ] + ] + }, + { + "type": "line", + "version": 4108, + "versionNonce": 1572406536, + "isDeleted": false, + "id": "R5nE2rgdgOpe93Q8VvYhl", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 787.0553235162608, + "y": 101.08014413686368, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 46.92865289294453, + "height": 2.4757501798128, + "seed": 571402312, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641741755943, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 18.193786115221407, + -0.5912874140789839 + ], + [ + 46.92865289294453, + 1.884462765733816 + ] + ] + }, + { + "type": "line", + "version": 4123, + "versionNonce": 973844344, + "isDeleted": false, + "id": "lfWLpL8lTqrf2GMoqJY8y", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 787.1628117124112, + "y": 135.5634731727032, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 46.92865289294453, + "height": 2.4757501798128, + "seed": 2028511288, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641741755943, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 8.093938105125233, + 1.4279702913643746 + ], + [ + 18.193786115221407, + -0.5912874140789839 + ], + [ + 46.92865289294453, + 1.884462765733816 + ] + ] + }, + { + "type": "line", + "version": 3006, + "versionNonce": 23274504, + "isDeleted": false, + "id": "8X7QMyL8Lv5BuDjYinx6w", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 1.5707963267948957, + "x": 1013.7787705852069, + "y": 125.15514857126914, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 50.7174766392476, + "height": 12.698053371678215, + "seed": 1906238776, + "groupIds": [ + "3orQz7tgci8hTh4ECgIAF" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641741755943, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 1.3361877396713384, + 5.061656285093649 + ], + [ + 7.098613049589299, + 9.308339392337079 + ], + [ + 14.766422451441104, + 11.880105003906111 + ], + [ + 26.779003528407447, + 12.114458425450186 + ], + [ + 40.79727342221974, + 10.477131310135727 + ], + [ + 48.98410145879092, + 4.5205722256349645 + ], + [ + 50.7174766392476, + -0.5835949462280285 + ] + ] + }, + { + "type": "line", + "version": 5373, + "versionNonce": 349634680, + "isDeleted": false, + "id": "A4PSBtsGIGM5bIdhtvs22", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 1.5707963267948957, + "x": 1038.306180989564, + "y": 66.05019201370396, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 52.317507746132115, + "height": 154.56722543646003, + "seed": 1241568072, + "groupIds": [ + "3orQz7tgci8hTh4ECgIAF" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641741755943, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0.1725162731731403, + 116.82107121890462 + ], + [ + 0.008073356596080744, + 130.12064288619618 + ], + [ + 2.694467356765587, + 135.86722334556717 + ], + [ + 12.049700419984944, + 140.7306911579349 + ], + [ + 27.86269432990675, + 142.2451023824676 + ], + [ + 42.97096432627199, + 139.82712302629713 + ], + [ + 50.998099415101294, + 134.0445691284302 + ], + [ + 52.13021819883883, + 129.16981553143583 + ], + [ + 52.28922018370778, + 118.46242736729553 + ], + [ + 52.16442211418855, + 9.800635828982735 + ], + [ + 51.88308720685254, + -0.46590137319512337 + ], + [ + 48.52377541516831, + -6.203936550968926 + ], + [ + 41.449781688403746, + -9.527102901326515 + ], + [ + 25.329105770103325, + -12.322123053992442 + ], + [ + 12.404412180527922, + -10.655446243436785 + ], + [ + 2.239228448568725, + -5.00228186200372 + ], + [ + -0.028287562424331975, + -0.07019354973772352 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "line", + "version": 3140, + "versionNonce": 141648648, + "isDeleted": false, + "id": "w17jKx2sTAqGcj5vlmOMy", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 1.5707963267948957, + "x": 1059.615995470046, + "y": 125.12063304384947, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 50.57247907260371, + "height": 11.002162473203482, + "seed": 672606792, + "groupIds": [ + "3orQz7tgci8hTh4ECgIAF" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641741755944, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 1.332367676378171, + 4.385645831062678 + ], + [ + 7.078318632616268, + 8.065162379820556 + ], + [ + 14.724206326638113, + 10.293455353024548 + ], + [ + 26.70244431044034, + 10.496509659421628 + ], + [ + 40.68063699304561, + 9.07785607393191 + ], + [ + 48.84405948536458, + 3.9168263545984514 + ], + [ + 50.57247907260371, + -0.5056528137818528 + ] + ] + }, + { + "type": "ellipse", + "version": 6107, + "versionNonce": 2081436024, + "isDeleted": false, + "id": "sxpRnxFiIOgK8uOIc81pk", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 1.5707963267948957, + "x": 1105.8093248657638, + "y": 119.64103777538094, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 51.27812853552538, + "height": 22.797152568995934, + "seed": 1744666168, + "groupIds": [ + "3orQz7tgci8hTh4ECgIAF" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "bxuMGTzXLn7H-uBCptINx" + }, + { + "id": "ppSKhzeVi1x47qPOxgohP", + "type": "arrow" + }, + { + "id": "IEnqZv0JAf73xE5G5LY1s", + "type": "arrow" + } + ], + "updated": 1641741755944 + }, + { + "type": "text", + "version": 903, + "versionNonce": 828359176, + "isDeleted": false, + "id": "DAMG-SGC-r93uwzYgaFaM", + "fillStyle": "hachure", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 961, + "y": 165.3106382978724, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 213, + "height": 80, + "seed": 905916728, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "ppSKhzeVi1x47qPOxgohP", + "type": "arrow" + } + ], + "updated": 1641741755944, + "fontSize": 16.51063829787236, + "fontFamily": 3, + "text": "[0.114, 0.344, 0.589],\n[0.894, 0.448, 0.385],\n[0.639, 0.785, 0.546],\n[0.789, 0.248, 0.187]", + "baseline": 76, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "[0.114, 0.344, 0.589],\n[0.894, 0.448, 0.385],\n[0.639, 0.785, 0.546],\n[0.789, 0.248, 0.187]" + }, + { + "type": "rectangle", + "version": 3994, + "versionNonce": 1727742584, + "isDeleted": false, + "id": "0kwQuYpKaDvZuuKsvL7r5", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1392.3414205264805, + "y": 66.34139380028421, + "strokeColor": "#00e676", + "backgroundColor": "#d0d9dd", + "width": 74.26505724978897, + "height": 53.22792745407822, + "seed": 1723506296, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "XV1prKL4Cu6Tu_cyztWSc" + } + ], + "updated": 1641741755944 + }, + { + "type": "text", + "version": 3911, + "versionNonce": 187318536, + "isDeleted": false, + "id": "4eS6gRMMGH5z1mWIMwlWa", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1406.4841911219582, + "y": 64.65315479829201, + "strokeColor": "#00e676", + "backgroundColor": "#d0d9dd", + "width": 47.44228316559144, + "height": 24.299706011644403, + "seed": 1855890696, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1641741755945, + "fontSize": 18.51406172315763, + "fontFamily": 1, + "text": "Text", + "baseline": 17.299706011644403, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Text" + }, + { + "type": "rectangle", + "version": 3610, + "versionNonce": 1146893176, + "isDeleted": false, + "id": "PVROaX-PjeUDmh4MQbTl8", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1250.6356656002554, + "y": 86.0327898598672, + "strokeColor": "#00e676", + "backgroundColor": "#ffffff", + "width": 224.03197101896893, + "height": 53.22792745407822, + "seed": 1271926648, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1641741755945 + }, + { + "type": "rectangle", + "version": 3670, + "versionNonce": 1699536904, + "isDeleted": false, + "id": "ugOritv0_fPYbcZUm6Uu4", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1250.8971808651277, + "y": 111.8916716422263, + "strokeColor": "#00e676", + "backgroundColor": "#ffffff", + "width": 224.03197101896893, + "height": 26.01198231582164, + "seed": 613729288, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1641741755945 + }, + { + "type": "rectangle", + "version": 3765, + "versionNonce": 175572088, + "isDeleted": false, + "id": "eio4Ow124ISNuI0VAUUjS", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1250.7639028142198, + "y": 137.72914909405728, + "strokeColor": "#00e676", + "backgroundColor": "#ffffff", + "width": 224.03197101896893, + "height": 26.01198231582164, + "seed": 789536888, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1641741755945 + }, + { + "type": "rectangle", + "version": 3856, + "versionNonce": 1069784840, + "isDeleted": false, + "id": "_6fRtYyVCjDNRCEwdM5Cy", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1250.5409710428607, + "y": 163.83262915775464, + "strokeColor": "#00e676", + "backgroundColor": "#ffffff", + "width": 224.03197101896893, + "height": 26.01198231582164, + "seed": 342819592, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1641741755945 + }, + { + "type": "rectangle", + "version": 3800, + "versionNonce": 1192211832, + "isDeleted": false, + "id": "ZqV86lTip0q28AN63sD4l", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1251.72784114917, + "y": 86.97311738448298, + "strokeColor": "#00e676", + "backgroundColor": "#e6e6e7", + "width": 63.93385920866094, + "height": 24.3311416053828, + "seed": 1883017592, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1641741755945 + }, + { + "type": "rectangle", + "version": 3844, + "versionNonce": 990543368, + "isDeleted": false, + "id": "YChnGbrEsJ7DoNKmra0ci", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1251.9893564140477, + "y": 112.83199916684202, + "strokeColor": "#00e676", + "backgroundColor": "#e6e6e7", + "width": 63.93385920866094, + "height": 24.3311416053828, + "seed": 749298184, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1641741755945 + }, + { + "type": "rectangle", + "version": 3953, + "versionNonce": 795494008, + "isDeleted": false, + "id": "2izY3o3TkA3Cx67YfzF9f", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1251.8560783631362, + "y": 138.66947661867334, + "strokeColor": "#00e676", + "backgroundColor": "#e6e6e7", + "width": 63.93385920866094, + "height": 24.3311416053828, + "seed": 1453040248, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1641741755945 + }, + { + "type": "rectangle", + "version": 4044, + "versionNonce": 136150280, + "isDeleted": false, + "id": "Va8ZaCNlht0FpfumBHJKw", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1251.633146591779, + "y": 164.77295668237002, + "strokeColor": "#00e676", + "backgroundColor": "#e6e6e7", + "width": 63.93385920866094, + "height": 24.3311416053828, + "seed": 742383880, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1641741755945 + }, + { + "type": "text", + "version": 3621, + "versionNonce": 810203000, + "isDeleted": false, + "id": "sVvye4L-cb_iVCic8Mm8d", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1254.7093435648121, + "y": 98.52504819457317, + "strokeColor": "#00e676", + "backgroundColor": "#ffffff", + "width": 32.130700199808395, + "height": 12.657548563560873, + "seed": 552637304, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1641741755946, + "fontSize": 9.73657581812376, + "fontFamily": 1, + "text": "____", + "baseline": 8.657548563560873, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "" + }, + { + "type": "text", + "version": 3688, + "versionNonce": 1484299272, + "isDeleted": false, + "id": "Kmc9TdtkzFXb1_8VdBftg", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1254.9708588296896, + "y": 124.38392997693245, + "strokeColor": "#00e676", + "backgroundColor": "#ffffff", + "width": 32.130700199808395, + "height": 12.657548563560873, + "seed": 313425928, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "IEnqZv0JAf73xE5G5LY1s", + "type": "arrow" + } + ], + "updated": 1641741755946, + "fontSize": 9.73657581812376, + "fontFamily": 1, + "text": "____", + "baseline": 8.657548563560873, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "" + }, + { + "type": "text", + "version": 3782, + "versionNonce": 2113210488, + "isDeleted": false, + "id": "4eUPUHqH2EOhDN59TKy-H", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1254.837580778779, + "y": 150.22140742876354, + "strokeColor": "#00e676", + "backgroundColor": "#ffffff", + "width": 32.130700199808395, + "height": 12.657548563560873, + "seed": 360670328, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "IEnqZv0JAf73xE5G5LY1s", + "type": "arrow" + } + ], + "updated": 1641741755946, + "fontSize": 9.73657581812376, + "fontFamily": 1, + "text": "____", + "baseline": 8.657548563560873, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "" + }, + { + "type": "text", + "version": 3872, + "versionNonce": 1310186248, + "isDeleted": false, + "id": "b-QV7ztzPBTYvkYvn2n8j", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1254.614649007421, + "y": 176.32488749246102, + "strokeColor": "#00e676", + "backgroundColor": "#ffffff", + "width": 32.130700199808395, + "height": 12.657548563560873, + "seed": 267468552, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1641741755946, + "fontSize": 9.73657581812376, + "fontFamily": 1, + "text": "____", + "baseline": 8.657548563560873, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "" + }, + { + "type": "text", + "version": 3901, + "versionNonce": 606943608, + "isDeleted": false, + "id": "4KBkw2hbsRceHQmpPy7CU", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1320.8795641602012, + "y": 98.59324391505857, + "strokeColor": "#00e676", + "backgroundColor": "#ffffff", + "width": 39.91996085430739, + "height": 12.657548563560873, + "seed": 302781816, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1641741755946, + "fontSize": 9.736575818123757, + "fontFamily": 1, + "text": "_____", + "baseline": 8.657548563560873, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "" + }, + { + "type": "text", + "version": 3967, + "versionNonce": 1802975752, + "isDeleted": false, + "id": "ypoyWHhBrPKGhPAUDIGbn", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1321.141079425078, + "y": 124.4521256974175, + "strokeColor": "#00e676", + "backgroundColor": "#ffffff", + "width": 39.91996085430739, + "height": 12.657548563560873, + "seed": 1954815496, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1641741755946, + "fontSize": 9.736575818123757, + "fontFamily": 1, + "text": "_____", + "baseline": 8.657548563560873, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "" + }, + { + "type": "text", + "version": 4061, + "versionNonce": 372722296, + "isDeleted": false, + "id": "1jfyF2ZuAzbvS4YBl80za", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1321.0078013741684, + "y": 150.28960314924905, + "strokeColor": "#00e676", + "backgroundColor": "#ffffff", + "width": 39.91996085430739, + "height": 12.657548563560873, + "seed": 467314296, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1641741755946, + "fontSize": 9.736575818123757, + "fontFamily": 1, + "text": "_____", + "baseline": 8.657548563560873, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "" + }, + { + "type": "text", + "version": 4152, + "versionNonce": 59947272, + "isDeleted": false, + "id": "7coLrbQfexZARdWa0iB51", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1320.7848696028097, + "y": 176.39308321294527, + "strokeColor": "#00e676", + "backgroundColor": "#ffffff", + "width": 39.91996085430739, + "height": 12.657548563560873, + "seed": 560143624, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1641741755946, + "fontSize": 9.736575818123757, + "fontFamily": 1, + "text": "_____", + "baseline": 8.657548563560873, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "" + }, + { + "type": "text", + "version": 282, + "versionNonce": 1054292856, + "isDeleted": false, + "id": "uG1gxQCUX_W4xoK8WjzqS", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 762, + "y": 202, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 117, + "height": 26, + "seed": 657172488, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641741755946, + "fontSize": 20, + "fontFamily": 1, + "text": "Input Data", + "baseline": 18, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Input Data" + }, + { + "type": "arrow", + "version": 312, + "versionNonce": 702961672, + "isDeleted": false, + "id": "3DJ_wB8Ty8UNX4KAZfwjT", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 878.680938765565, + "y": 132.68136619602433, + "strokeColor": "#03a9f4", + "backgroundColor": "#ff7043", + "width": 104.88363252862348, + "height": 0.6395755770326446, + "seed": 1663624312, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641741755946, + "startBinding": { + "elementId": "9itz4bqXi6IMeZhGhc42J", + "focus": -0.09772881248040827, + "gap": 14.870387259234633 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 104.88363252862348, + 0.6395755770326446 + ] + ] + }, + { + "type": "arrow", + "version": 444, + "versionNonce": 273747064, + "isDeleted": false, + "id": "IEnqZv0JAf73xE5G5LY1s", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1148.3291790735977, + "y": 134.9159571720989, + "strokeColor": "#00e676", + "backgroundColor": "#ff7043", + "width": 100.9588566647617, + "height": 1.6503135268827123, + "seed": 2055241224, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641741755946, + "startBinding": { + "elementId": "sxpRnxFiIOgK8uOIc81pk", + "focus": 0.16194715672100898, + "gap": 5.601483341858389 + }, + "endBinding": { + "elementId": "Kmc9TdtkzFXb1_8VdBftg", + "focus": -0.3333798833901454, + "gap": 5.682823091330192 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 100.9588566647617, + -1.6503135268827123 + ] + ] + }, + { + "type": "text", + "version": 247, + "versionNonce": 577640200, + "isDeleted": false, + "id": "g9t3YfCk9S7S38pod_Itm", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1007.5, + "y": 71, + "strokeColor": "#ff7043", + "backgroundColor": "#7950f2", + "width": 107, + "height": 36, + "seed": 1032706680, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641741755946, + "fontSize": 28, + "fontFamily": 1, + "text": "Process", + "baseline": 25, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Process" + }, + { + "type": "line", + "version": 67, + "versionNonce": 342408568, + "isDeleted": false, + "id": "agZFvlVOC2laS2bFwE9Rc", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1079.5, + "y": 90, + "strokeColor": "#ff7043", + "backgroundColor": "#7950f2", + "width": 0, + "height": 0, + "seed": 1617963896, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1641741755947, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ] + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/images/pipeline.png b/docs/images/pipeline.png new file mode 100644 index 0000000..73728e2 Binary files /dev/null and b/docs/images/pipeline.png differ diff --git a/docs/images/query-dark.png b/docs/images/query-dark.png new file mode 100644 index 0000000..0358eba Binary files /dev/null and b/docs/images/query-dark.png differ diff --git a/docs/images/query.excalidraw b/docs/images/query.excalidraw new file mode 100644 index 0000000..d9f4930 --- /dev/null +++ b/docs/images/query.excalidraw @@ -0,0 +1,243 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 3038, + "versionNonce": 1386987419, + "isDeleted": false, + "id": "pjs0X-Y0kuuXB-vpf_dvP", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 934.5, + "y": -240.29250000000002, + "strokeColor": "#ffeb3b", + "backgroundColor": "#ffeb3b", + "width": 312, + "height": 146, + "seed": 1534167490, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "of5l7HpIH0kPvZ95rOMwM", + "type": "text" + }, + { + "id": "Qzp41i_jzQIBlAB_qFKFH", + "type": "arrow" + }, + { + "id": "of5l7HpIH0kPvZ95rOMwM", + "type": "text" + }, + { + "type": "text", + "id": "of5l7HpIH0kPvZ95rOMwM" + } + ], + "updated": 1669033845173, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2554, + "versionNonce": 1319492501, + "isDeleted": false, + "id": "of5l7HpIH0kPvZ95rOMwM", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 939.5, + "y": -235.29250000000002, + "strokeColor": "#000", + "backgroundColor": "#fa5252", + "width": 260, + "height": 130, + "seed": 1972792158, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1669033845173, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "SELECT\n count(*), flag\nFROM txtai\nGROUP BY flag\nORDER BY count(*) DESC", + "baseline": 122, + "textAlign": "left", + "verticalAlign": "top", + "containerId": "pjs0X-Y0kuuXB-vpf_dvP", + "originalText": "SELECT\n count(*), flag\nFROM txtai\nGROUP BY flag\nORDER BY count(*) DESC" + }, + { + "type": "rectangle", + "version": 3354, + "versionNonce": 1004999739, + "isDeleted": false, + "id": "xMKXZw8aL27lv21eVGy_C", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 581, + "y": -238.29250000000002, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 302, + "height": 140, + "seed": 1704428866, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "tlu8eCXVp5ApnEG74CF38", + "type": "text" + }, + { + "id": "Qzp41i_jzQIBlAB_qFKFH", + "type": "arrow" + }, + { + "type": "text", + "id": "tlu8eCXVp5ApnEG74CF38" + } + ], + "updated": 1669033845173, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2750, + "versionNonce": 686792949, + "isDeleted": false, + "id": "tlu8eCXVp5ApnEG74CF38", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 586, + "y": -233.29250000000002, + "strokeColor": "#000", + "backgroundColor": "#fa5252", + "width": 290, + "height": 130, + "seed": 92268510, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1669033845173, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "SELECT\n text, flag, entry\nFROM txtai\nWHERE\n similar('text') AND flag = 1", + "baseline": 122, + "textAlign": "left", + "verticalAlign": "top", + "containerId": "xMKXZw8aL27lv21eVGy_C", + "originalText": "SELECT\n text, flag, entry\nFROM txtai\nWHERE\n similar('text') AND flag = 1" + }, + { + "type": "rectangle", + "version": 3169, + "versionNonce": 1477367003, + "isDeleted": false, + "id": "Tsn_ukSxtC7CIwEMUMHxI", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1301.5, + "y": -236.79250000000002, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 304, + "height": 140, + "seed": 919682718, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "t3OC1akz9Oe2w6N8OCavg", + "type": "text" + }, + { + "id": "Qzp41i_jzQIBlAB_qFKFH", + "type": "arrow" + }, + { + "id": "t3OC1akz9Oe2w6N8OCavg", + "type": "text" + }, + { + "id": "t3OC1akz9Oe2w6N8OCavg", + "type": "text" + }, + { + "type": "text", + "id": "t3OC1akz9Oe2w6N8OCavg" + } + ], + "updated": 1669033845173, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2769, + "versionNonce": 345619029, + "isDeleted": false, + "id": "t3OC1akz9Oe2w6N8OCavg", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1306.5, + "y": -231.79250000000002, + "strokeColor": "#000", + "backgroundColor": "#fa5252", + "width": 259, + "height": 130, + "seed": 1222509122, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1669033845173, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "SELECT\n translation(text, 'fr')\nFROM txtai\nWHERE\n similar('feel good story')", + "baseline": 122, + "textAlign": "left", + "verticalAlign": "top", + "containerId": "Tsn_ukSxtC7CIwEMUMHxI", + "originalText": "SELECT\n translation(text, 'fr')\nFROM txtai\nWHERE\n similar('feel good story')" + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/images/query.png b/docs/images/query.png new file mode 100644 index 0000000..68ed782 Binary files /dev/null and b/docs/images/query.png differ diff --git a/docs/images/rag-dark.png b/docs/images/rag-dark.png new file mode 100644 index 0000000..4c70421 Binary files /dev/null and b/docs/images/rag-dark.png differ diff --git a/docs/images/rag.excalidraw b/docs/images/rag.excalidraw new file mode 100644 index 0000000..a165ab5 --- /dev/null +++ b/docs/images/rag.excalidraw @@ -0,0 +1,1653 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "text", + "version": 1836, + "versionNonce": 917552614, + "isDeleted": false, + "id": "YCEwpQiKAjRS3zIG3_E8s", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2014.213462044321, + "y": -342.9945000407677, + "strokeColor": "#00e676", + "backgroundColor": "transparent", + "width": 54.983333587646484, + "height": 36, + "seed": 676249636, + "groupIds": [ + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "LLM", + "textAlign": "left", + "verticalAlign": "middle", + "containerId": null, + "originalText": "LLM", + "lineHeight": 1.2857142857142858, + "baseline": 25 + }, + { + "type": "line", + "version": 3253, + "versionNonce": 1479474042, + "isDeleted": false, + "id": "aFBJSnHUVo8ZDTb0uAkQ2", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1746.3803586395986, + "y": -463.34177128578517, + "strokeColor": "#ffeb3b", + "backgroundColor": "#ffeb3b", + "width": 116.42036295658872, + "height": 103.65107323746608, + "seed": 1963890941, + "groupIds": [ + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -62.44191743896485, + 19.19929080548739 + ], + [ + -63.17668831316513, + 79.43840749607878 + ], + [ + -7.618334228588694, + 103.65107323746608 + ], + [ + 51.963117173367294, + 79.15871076413049 + ], + [ + 53.24367464342358, + 21.28567723840068 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "rectangle", + "version": 2707, + "versionNonce": 1122767142, + "isDeleted": false, + "id": "ZTKEryJuyeKfeG4nAI4_e", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1980.1453046789213, + "y": -432.63793463623756, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 23.299119994671287, + "height": 58.247799986678125, + "seed": 1188509340, + "groupIds": [ + "SFux9iuPQVutxOMe0wlW3", + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "5xuE_JZVT_PTcKy4YrPE8", + "type": "arrow" + } + ], + "updated": 1701521662211, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 2850, + "versionNonce": 1047886906, + "isDeleted": false, + "id": "CckqOoWddQBHuvidbpk4P", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2028.4647375510242, + "y": -487.74904809174427, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 23.29911999467125, + "height": 116.4955999733563, + "seed": 331107492, + "groupIds": [ + "SFux9iuPQVutxOMe0wlW3", + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 2732, + "versionNonce": 718994534, + "isDeleted": false, + "id": "J8ox4BBdzEg-04DGRL-jh", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2072.5083285134306, + "y": -452.8003680997374, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 23.29911999467127, + "height": 81.54691998134939, + "seed": 1316437916, + "groupIds": [ + "SFux9iuPQVutxOMe0wlW3", + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 2936, + "versionNonce": 1101915386, + "isDeleted": false, + "id": "V54vdkZmQeI13AEahvNPV", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2114.182425101253, + "y": -477.75421594683803, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 23.29911999467125, + "height": 104.84603997602068, + "seed": 1025829916, + "groupIds": [ + "SFux9iuPQVutxOMe0wlW3", + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "tEBpXPxm5iq2pVW3uI-zN", + "type": "arrow" + }, + { + "id": "QwMjSCRfpekAf9are5X7i", + "type": "arrow" + } + ], + "updated": 1701521662211, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1533, + "versionNonce": 1682603942, + "isDeleted": false, + "id": "iRbBQnNoV4xKl3KqmeMQk", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1354.935979235434, + "y": -344.1286480222933, + "strokeColor": "#03a9f4", + "backgroundColor": "transparent", + "width": 172.6999969482422, + "height": 70, + "seed": 437964304, + "groupIds": [ + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Extract and\nIndex text", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": null, + "originalText": "Extract and\nIndex text", + "lineHeight": 1.25, + "baseline": 60 + }, + { + "type": "text", + "version": 1816, + "versionNonce": 995833274, + "isDeleted": false, + "id": "6MGKGOL-iHR1ALgkWdtgq", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1644.3134452350068, + "y": -339.59711992736334, + "strokeColor": "#fab005", + "backgroundColor": "#00e676", + "width": 192.14999389648438, + "height": 36, + "seed": 737085168, + "groupIds": [ + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Vector Search", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": null, + "originalText": "Vector Search", + "lineHeight": 1.2857142857142858, + "baseline": 25 + }, + { + "type": "arrow", + "version": 1734, + "versionNonce": 593992422, + "isDeleted": false, + "id": "TekVO-JfHFlOug8RyHvF6", + "fillStyle": "hachure", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1495.4312181086514, + "y": -410.21715689579304, + "strokeColor": "#03a9f4", + "backgroundColor": "#f8f9fa", + "width": 183.6089623410503, + "height": 1.4014353332532892, + "seed": 1668067473, + "groupIds": [ + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "startBinding": { + "elementId": "ZX3LSm-VHgqQG71nXy8Jp", + "focus": 0.09017987719185833, + "gap": 1.2826405001093732 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 183.6089623410503, + 1.4014353332532892 + ] + ] + }, + { + "type": "arrow", + "version": 1424, + "versionNonce": 1554017914, + "isDeleted": false, + "id": "5xuE_JZVT_PTcKy4YrPE8", + "fillStyle": "hachure", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1802.8350046840696, + "y": -410.21116509773026, + "strokeColor": "#00e676", + "backgroundColor": "#f8f9fa", + "width": 170.5772610888498, + "height": 0.7934282297927382, + "seed": 2139598001, + "groupIds": [ + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "ZTKEryJuyeKfeG4nAI4_e", + "focus": 0.19940295698482952, + "gap": 6.73303890600198 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 170.5772610888498, + 0.7934282297927382 + ] + ] + }, + { + "type": "rectangle", + "version": 829, + "versionNonce": 247418406, + "isDeleted": false, + "id": "ceYyi4FMiqSJFJi9xVvwk", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2246.94133763324, + "y": -461.9464695959758, + "strokeColor": "#7950f2", + "backgroundColor": "#7950f2", + "width": 151.86184901372147, + "height": 45, + "seed": 1675993499, + "groupIds": [ + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "TKBvCE2CYBCmPq45Bjqws" + }, + { + "id": "tEBpXPxm5iq2pVW3uI-zN", + "type": "arrow" + } + ], + "updated": 1701521662211, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 805, + "versionNonce": 977309498, + "isDeleted": false, + "id": "TKBvCE2CYBCmPq45Bjqws", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2277.347260614222, + "y": -456.9464695959758, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffeb3b", + "width": 91.05000305175781, + "height": 35, + "seed": 1955981979, + "groupIds": [ + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Answer", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ceYyi4FMiqSJFJi9xVvwk", + "originalText": "Answer", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "rectangle", + "version": 925, + "versionNonce": 1933482342, + "isDeleted": false, + "id": "8kLHMUMRHF0HnBTmfmRbB", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2248.212626691299, + "y": -408.2942759824447, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 150.58992984312886, + "height": 45, + "seed": 1950178933, + "groupIds": [ + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "Z9_eH0JKgQC5deMjnP4Nl" + }, + { + "id": "QwMjSCRfpekAf9are5X7i", + "type": "arrow" + } + ], + "updated": 1701521662211, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 909, + "versionNonce": 1486788602, + "isDeleted": false, + "id": "Z9_eH0JKgQC5deMjnP4Nl", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2268.8992580252175, + "y": -403.2942759824447, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffeb3b", + "width": 109.21666717529297, + "height": 35, + "seed": 2052310997, + "groupIds": [ + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Citation", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "8kLHMUMRHF0HnBTmfmRbB", + "originalText": "Citation", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "arrow", + "version": 2914, + "versionNonce": 2016574630, + "isDeleted": false, + "id": "tEBpXPxm5iq2pVW3uI-zN", + "fillStyle": "hachure", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 2145.854477497225, + "y": -412.39564497927813, + "strokeColor": "#7950f2", + "backgroundColor": "#f8f9fa", + "width": 98.40776781852423, + "height": 26.51909525787792, + "seed": 67586843, + "groupIds": [ + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "startBinding": { + "elementId": "V54vdkZmQeI13AEahvNPV", + "focus": 0.3299220128455593, + "gap": 8.372932401300432 + }, + "endBinding": { + "elementId": "ceYyi4FMiqSJFJi9xVvwk", + "focus": 0.48070935597063635, + "gap": 2.679092317490813 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 98.40776781852423, + -26.51909525787792 + ] + ] + }, + { + "type": "arrow", + "version": 3333, + "versionNonce": 935168186, + "isDeleted": false, + "id": "QwMjSCRfpekAf9are5X7i", + "fillStyle": "hachure", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 2145.973109719745, + "y": -412.4720176121603, + "strokeColor": "#ff7043", + "backgroundColor": "#f8f9fa", + "width": 99.14068321372724, + "height": 28.4745045092954, + "seed": 1135076219, + "groupIds": [ + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "startBinding": { + "elementId": "V54vdkZmQeI13AEahvNPV", + "focus": 0.12685170125624176, + "gap": 8.491564623820295 + }, + "endBinding": { + "elementId": "8kLHMUMRHF0HnBTmfmRbB", + "focus": -0.5509825166646869, + "gap": 3.098833757826924 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 99.14068321372724, + 28.4745045092954 + ] + ] + }, + { + "type": "rectangle", + "version": 5987, + "versionNonce": 1397225446, + "isDeleted": false, + "id": "Wm6KQXptnXme2EhsZEkeR", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1131.175427860874, + "y": -462.1767369812002, + "strokeColor": "#868e96", + "backgroundColor": "#fff", + "width": 55.277464194934204, + "height": 83.88048030659263, + "seed": 2083904347, + "groupIds": [ + "Qj7F6yX0WhR-X_WVj6a3Q", + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 6037, + "versionNonce": 1598054778, + "isDeleted": false, + "id": "9qC6dzMnsEUYrUkMWcOVp", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1125.2726075818707, + "y": -467.6671236066981, + "strokeColor": "#868e96", + "backgroundColor": "#fff", + "width": 55.277464194934204, + "height": 83.88048030659263, + "seed": 758626299, + "groupIds": [ + "LrzsLb2hfnEcKX352d6a4", + "Qj7F6yX0WhR-X_WVj6a3Q", + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 6141, + "versionNonce": 2089552678, + "isDeleted": false, + "id": "NSEdoJ3xQn_cWFxLphwMQ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1117.9247419835488, + "y": -474.1224740500794, + "strokeColor": "#868e96", + "backgroundColor": "#fff", + "width": 55.277464194934204, + "height": 83.88048030659263, + "seed": 1729429659, + "groupIds": [ + "Qj7F6yX0WhR-X_WVj6a3Q", + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 5257, + "versionNonce": 1785340474, + "isDeleted": false, + "id": "YEmnwZhbbK23DEmyJvwCv", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1127.2992831812242, + "y": -435.9974349769433, + "strokeColor": "#868e96", + "backgroundColor": "#fff", + "width": 36.42992238421251, + "height": 2.541777233017194, + "seed": 1400911163, + "groupIds": [ + "Qj7F6yX0WhR-X_WVj6a3Q", + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 31.61584953581463, + 0.12309654138955267 + ], + [ + 36.42992238421251, + -2.4186806916276415 + ] + ] + }, + { + "type": "line", + "version": 5281, + "versionNonce": 1651226214, + "isDeleted": false, + "id": "MEri2dzaVIq9n6tGJD7WN", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1128.7502692954959, + "y": -460.6063232007082, + "strokeColor": "#868e96", + "backgroundColor": "#fff", + "width": 35.638112199057964, + "height": 2.1924492101548854, + "seed": 1104926171, + "groupIds": [ + "Qj7F6yX0WhR-X_WVj6a3Q", + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 13.164676061582496, + -2.1924492101548854 + ], + [ + 35.638112199057964, + -0.25617388343228015 + ] + ] + }, + { + "type": "line", + "version": 5306, + "versionNonce": 337496826, + "isDeleted": false, + "id": "vgzKUuIqRDM9pNKM15LRR", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1128.6903610887653, + "y": -407.89742505595746, + "strokeColor": "#868e96", + "backgroundColor": "#fff", + "width": 37.803945940804155, + "height": 3.34788687616908, + "seed": 1899651707, + "groupIds": [ + "Qj7F6yX0WhR-X_WVj6a3Q", + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 20.65693041859134, + -0.19961601306414317 + ], + [ + 29.422469585925523, + 1.8108435755749763 + ], + [ + 37.803945940804155, + -1.5370433005941033 + ] + ] + }, + { + "type": "line", + "version": 5343, + "versionNonce": 498334118, + "isDeleted": false, + "id": "Cp8KP5gHGupNwbMjKHtU1", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1126.200129220062, + "y": -399.10419905313154, + "strokeColor": "#868e96", + "backgroundColor": "#fff", + "width": 42.55148011818046, + "height": 2.27562254893163, + "seed": 442391323, + "groupIds": [ + "Qj7F6yX0WhR-X_WVj6a3Q", + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 7.9508648746316855, + -0.9124268560932918 + ], + [ + 12.61573202565594, + -0.658732843111803 + ], + [ + 36.180402367882216, + 0.47907843135401373 + ], + [ + 42.55148011818046, + -1.7965441175776165 + ] + ] + }, + { + "type": "line", + "version": 5309, + "versionNonce": 1121868730, + "isDeleted": false, + "id": "Z_ssChLTRG5S_-bNEkKSc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1126.4229038259869, + "y": -448.82748079933765, + "strokeColor": "#868e96", + "backgroundColor": "#fff", + "width": 36.70273093540022, + "height": 1.9362753267226067, + "seed": 968507323, + "groupIds": [ + "Qj7F6yX0WhR-X_WVj6a3Q", + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 14.229294797924695, + -0.4624437635986851 + ], + [ + 36.70273093540022, + 1.4738315631239216 + ] + ] + }, + { + "type": "line", + "version": 5323, + "versionNonce": 630314214, + "isDeleted": false, + "id": "s_UIbn_04wQnRoID1xAww", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1126.5069699582718, + "y": -421.8581929690114, + "strokeColor": "#868e96", + "backgroundColor": "#fff", + "width": 36.70273093540022, + "height": 1.9362753267226052, + "seed": 426807387, + "groupIds": [ + "Qj7F6yX0WhR-X_WVj6a3Q", + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 6.330239931623038, + 1.1168104379056514 + ], + [ + 14.229294797924746, + -0.4624437635986836 + ], + [ + 36.70273093540022, + 1.4738315631239216 + ] + ] + }, + { + "type": "text", + "version": 1665, + "versionNonce": 349099130, + "isDeleted": false, + "id": "KQzTCckYJwmcri2ZUXolF", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1094.845592872532, + "y": -343.81672500592333, + "strokeColor": "#868e96", + "backgroundColor": "transparent", + "width": 144.43333435058594, + "height": 35, + "seed": 748785339, + "groupIds": [ + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Documents", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": null, + "originalText": "Documents", + "lineHeight": 1.25, + "baseline": 25 + }, + { + "type": "ellipse", + "version": 407, + "versionNonce": 59918374, + "isDeleted": false, + "id": "ZX3LSm-VHgqQG71nXy8Jp", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1372.5849155794615, + "y": -469.38832974419654, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 121.85485206598776, + "height": 107.68568322110525, + "seed": 54693813, + "groupIds": [ + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "id": "TekVO-JfHFlOug8RyHvF6", + "type": "arrow" + }, + { + "id": "ocOrefpIzXSYwlrYeB5zZ", + "type": "arrow" + } + ], + "updated": 1701521662211, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 6067, + "versionNonce": 1806841146, + "isDeleted": false, + "id": "896_9tzrmBEIv0wPMOAA2", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1159.008440355131, + "y": -433.48589612410836, + "strokeColor": "#868e96", + "backgroundColor": "#fff", + "width": 41.38251178619768, + "height": 62.79566212875594, + "seed": 866216059, + "groupIds": [ + "jC1bSinvdhon1lqLCwioC", + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 6117, + "versionNonce": 128078694, + "isDeleted": false, + "id": "FyfaSNb5FLCEHf0iOIi1n", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1154.5893966575723, + "y": -437.5961785429837, + "strokeColor": "#868e96", + "backgroundColor": "#fff", + "width": 41.38251178619768, + "height": 62.79566212875594, + "seed": 1173659931, + "groupIds": [ + "ZcBCWYu7yQph5fcMtQ5UB", + "jC1bSinvdhon1lqLCwioC", + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 6222, + "versionNonce": 2028699130, + "isDeleted": false, + "id": "uhf9NUQGgUhU8COKxhwFD", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1149.0885449243297, + "y": -442.4288643345245, + "strokeColor": "#868e96", + "backgroundColor": "#fff", + "width": 41.38251178619768, + "height": 62.79566212875594, + "seed": 1721222587, + "groupIds": [ + "jC1bSinvdhon1lqLCwioC", + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "ocOrefpIzXSYwlrYeB5zZ", + "type": "arrow" + } + ], + "updated": 1701521662211, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 5337, + "versionNonce": 2081355430, + "isDeleted": false, + "id": "-9TDrRU-TUCFATZg1NUYi", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1156.1066320825057, + "y": -413.8872171773836, + "strokeColor": "#868e96", + "backgroundColor": "#fff", + "width": 27.272627541642883, + "height": 1.902857300622384, + "seed": 481121883, + "groupIds": [ + "jC1bSinvdhon1lqLCwioC", + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 23.668655664678663, + 0.09215408393064761 + ], + [ + 27.272627541642883, + -1.8107032166917363 + ] + ] + }, + { + "type": "line", + "version": 5361, + "versionNonce": 1232973498, + "isDeleted": false, + "id": "XvJ-iIG8KhdoW9fCxaP1l", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1157.1928875909657, + "y": -432.31023317127756, + "strokeColor": "#868e96", + "backgroundColor": "#fff", + "width": 26.679852623386196, + "height": 1.6413389543326597, + "seed": 1728566011, + "groupIds": [ + "jC1bSinvdhon1lqLCwioC", + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 9.855505678747075, + -1.6413389543326597 + ], + [ + 26.679852623386196, + -0.19178012061240474 + ] + ] + }, + { + "type": "line", + "version": 5386, + "versionNonce": 1501901286, + "isDeleted": false, + "id": "VxXBrorj_TWBXByFNwrzO", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1157.1480383543515, + "y": -392.8506336675699, + "strokeColor": "#868e96", + "backgroundColor": "#fff", + "width": 28.30126637038239, + "height": 2.5063372593097366, + "seed": 1177140123, + "groupIds": [ + "jC1bSinvdhon1lqLCwioC", + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 15.464451543932462, + -0.14943905502267457 + ], + [ + 22.0266199282381, + 1.355656535634992 + ], + [ + 28.30126637038239, + -1.1506807236747445 + ] + ] + }, + { + "type": "line", + "version": 5423, + "versionNonce": 1376503674, + "isDeleted": false, + "id": "Y5UL5WFItbSvBNwQhfIs9", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1155.283769593875, + "y": -386.26773801560205, + "strokeColor": "#868e96", + "backgroundColor": "#fff", + "width": 31.855425229005572, + "height": 1.7036052272587878, + "seed": 105222203, + "groupIds": [ + "jC1bSinvdhon1lqLCwioC", + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 5.952276649750224, + -0.6830724903220912 + ], + [ + 9.444548277434599, + -0.49314888157492376 + ], + [ + 27.08582872286446, + 0.3586537320544714 + ], + [ + 31.855425229005572, + -1.3449514952043164 + ] + ] + }, + { + "type": "line", + "version": 5394, + "versionNonce": 2064778534, + "isDeleted": false, + "id": "48QS3eGwek6u2meHCxeow", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1155.450545926502, + "y": -423.49220774985906, + "strokeColor": "#868e96", + "backgroundColor": "#fff", + "width": 30.39283293375309, + "height": 1.5921546638326731, + "seed": 1228787931, + "groupIds": [ + "jC1bSinvdhon1lqLCwioC", + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -2.9159720169125194, + 1.5921546638326731 + ], + [ + 27.47686091684057, + 1.103358356250995 + ] + ] + }, + { + "type": "line", + "version": 5403, + "versionNonce": 2061027386, + "isDeleted": false, + "id": "KCwYMxaGjkAMea5v0qmQT", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1155.513480573754, + "y": -403.30211965956437, + "strokeColor": "#868e96", + "backgroundColor": "#fff", + "width": 27.47686091684057, + "height": 1.449558833720255, + "seed": 1352990075, + "groupIds": [ + "jC1bSinvdhon1lqLCwioC", + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 4.739023983735066, + 0.8360807027362631 + ], + [ + 10.652513972201453, + -0.3462004774692599 + ], + [ + 27.47686091684057, + 1.103358356250995 + ] + ] + }, + { + "type": "arrow", + "version": 2523, + "versionNonce": 1377988710, + "isDeleted": false, + "id": "ocOrefpIzXSYwlrYeB5zZ", + "fillStyle": "hachure", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1205.9627879525642, + "y": -406.5621788129673, + "strokeColor": "#868e96", + "backgroundColor": "#f8f9fa", + "width": 164.6348419304046, + "height": 1.709635034590832, + "seed": 1351597109, + "groupIds": [ + "j11_AWYuFwJUdzbrHyyU4" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1701521662211, + "link": null, + "locked": false, + "startBinding": { + "elementId": "uhf9NUQGgUhU8COKxhwFD", + "focus": 0.1532483360492926, + "gap": 15.491731242036735 + }, + "endBinding": { + "elementId": "ZX3LSm-VHgqQG71nXy8Jp", + "focus": -0.12294829093000738, + "gap": 2.5175197483459826 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 164.6348419304046, + -1.709635034590832 + ] + ] + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/images/rag.png b/docs/images/rag.png new file mode 100644 index 0000000..b2c1bc0 Binary files /dev/null and b/docs/images/rag.png differ diff --git a/docs/images/schedule-dark.png b/docs/images/schedule-dark.png new file mode 100644 index 0000000..3d45d27 Binary files /dev/null and b/docs/images/schedule-dark.png differ diff --git a/docs/images/schedule.excalidraw b/docs/images/schedule.excalidraw new file mode 100644 index 0000000..6d10279 --- /dev/null +++ b/docs/images/schedule.excalidraw @@ -0,0 +1,3788 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 5213, + "versionNonce": 290163336, + "isDeleted": false, + "id": "9itz4bqXi6IMeZhGhc42J", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 793.1319708150991, + "y": 84.01158837080237, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 2026142776, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "CFu0B4Mw_1wC1Hbgx8Fs0" + }, + { + "type": "arrow", + "id": "XIl_NhaFtRO00pX5Pq6VU" + }, + { + "type": "arrow", + "id": "EndiSTFlx1AT7vcBVjgve" + }, + { + "id": "sbyEF_hTthhrIaA-47du2", + "type": "arrow" + }, + { + "id": "3DJ_wB8Ty8UNX4KAZfwjT", + "type": "arrow" + } + ], + "updated": 1643333366010 + }, + { + "type": "rectangle", + "version": 5260, + "versionNonce": 586039800, + "isDeleted": false, + "id": "7HUYNqBGn8JbkXw6qt_jr", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 785.5845377793848, + "y": 76.99149908508815, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 1090159432, + "groupIds": [ + "6R5o6OnVZ7fFC_87zCo7G", + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "CFu0B4Mw_1wC1Hbgx8Fs0" + }, + { + "type": "arrow", + "id": "XIl_NhaFtRO00pX5Pq6VU" + }, + { + "type": "arrow", + "id": "EndiSTFlx1AT7vcBVjgve" + } + ], + "updated": 1643333366010 + }, + { + "type": "rectangle", + "version": 5356, + "versionNonce": 2072574344, + "isDeleted": false, + "id": "mI_Mfw3cNZf0AuQhK5KUO", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 776.1894484936695, + "y": 68.73759283508838, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 1146631480, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "CFu0B4Mw_1wC1Hbgx8Fs0" + }, + { + "type": "arrow", + "id": "XIl_NhaFtRO00pX5Pq6VU" + }, + { + "type": "arrow", + "id": "EndiSTFlx1AT7vcBVjgve" + } + ], + "updated": 1643333366010 + }, + { + "type": "line", + "version": 4486, + "versionNonce": 415449848, + "isDeleted": false, + "id": "y3oKPiP36axXzEF0c7OiC", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 788.1758750805805, + "y": 117.48482988244103, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 46.57983585730082, + "height": 3.249953844290203, + "seed": 1952111176, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1643333366010, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 40.42449133807562, + 0.1573930526684746 + ], + [ + 46.57983585730082, + -3.0925607916217284 + ] + ] + }, + { + "type": "line", + "version": 4512, + "versionNonce": 977307784, + "isDeleted": false, + "id": "-52Udo1sfovtw-gxgUT91", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 790.0311273185948, + "y": 86.01954258360246, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 45.567415680676426, + "height": 2.8032978840147194, + "seed": 923586104, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1643333366010, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 16.832548902953302, + -2.8032978840147194 + ], + [ + 45.567415680676426, + -0.3275477042019195 + ] + ] + }, + { + "type": "line", + "version": 4537, + "versionNonce": 1850745848, + "isDeleted": false, + "id": "7Tgba-Lgh4JYVwHG-MlYn", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 789.9545278022254, + "y": 153.41391685395956, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 48.33668263438425, + "height": 4.280657518731036, + "seed": 664293704, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1643333366010, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 26.41225578429045, + -0.2552319773002338 + ], + [ + 37.62000339651456, + 2.3153712935189787 + ], + [ + 48.33668263438425, + -1.9652862252120569 + ] + ] + }, + { + "type": "line", + "version": 4574, + "versionNonce": 2033072008, + "isDeleted": false, + "id": "KsHbDZNNeZoS7zbhCYNon", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 786.770480620695, + "y": 164.65706526223835, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 54.40694982784246, + "height": 2.9096445412231735, + "seed": 2043037496, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1643333366011, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 10.166093050596771, + -1.166642430373031 + ], + [ + 16.130660965377448, + -0.8422655250909383 + ], + [ + 46.26079588567538, + 0.6125567455206506 + ], + [ + 54.40694982784246, + -2.297087795702523 + ] + ] + }, + { + "type": "line", + "version": 4536, + "versionNonce": 563668216, + "isDeleted": false, + "id": "R5nE2rgdgOpe93Q8VvYhl", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 787.0553235162608, + "y": 101.08014413686368, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 46.92865289294453, + "height": 2.4757501798128, + "seed": 571402312, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1643333366011, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 18.193786115221407, + -0.5912874140789839 + ], + [ + 46.92865289294453, + 1.884462765733816 + ] + ] + }, + { + "type": "line", + "version": 4550, + "versionNonce": 210634376, + "isDeleted": false, + "id": "lfWLpL8lTqrf2GMoqJY8y", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 787.1628117124112, + "y": 135.5634731727032, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 46.92865289294453, + "height": 2.4757501798128, + "seed": 2028511288, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1643333366011, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 8.093938105125233, + 1.4279702913643746 + ], + [ + 18.193786115221407, + -0.5912874140789839 + ], + [ + 46.92865289294453, + 1.884462765733816 + ] + ] + }, + { + "type": "text", + "version": 600, + "versionNonce": 1344374264, + "isDeleted": false, + "id": "uG1gxQCUX_W4xoK8WjzqS", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 762, + "y": 202, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 117, + "height": 26, + "seed": 657172488, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1643333366011, + "fontSize": 20, + "fontFamily": 1, + "text": "Input Data", + "baseline": 18, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Input Data" + }, + { + "type": "arrow", + "version": 2896, + "versionNonce": 1629222280, + "isDeleted": false, + "id": "3DJ_wB8Ty8UNX4KAZfwjT", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 878.680938765565, + "y": 131.68136619602433, + "strokeColor": "#03a9f4", + "backgroundColor": "#ff7043", + "width": 104.88363252862348, + "height": 0.6395755770326446, + "seed": 1663624312, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1643333366011, + "startBinding": { + "elementId": "9itz4bqXi6IMeZhGhc42J", + "focus": -0.11630205093491493, + "gap": 14.870387259234633 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 104.88363252862348, + 0.6395755770326446 + ] + ] + }, + { + "type": "arrow", + "version": 2644, + "versionNonce": 1190004472, + "isDeleted": false, + "id": "IEnqZv0JAf73xE5G5LY1s", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1132.8223412617328, + "y": 135.16943809537315, + "strokeColor": "#00e676", + "backgroundColor": "#ff7043", + "width": 116.4656944766266, + "height": 1.9037944501569655, + "seed": 2055241224, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1643333366011, + "startBinding": null, + "endBinding": { + "elementId": "lIucERyh1MMe7Vzo8_q7d", + "focus": -0.22308504197782658, + "gap": 15.365988697358375 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 116.4656944766266, + -1.9037944501569655 + ] + ] + }, + { + "type": "text", + "version": 859, + "versionNonce": 1813251208, + "isDeleted": false, + "id": "g9t3YfCk9S7S38pod_Itm", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1023.5, + "y": 66, + "strokeColor": "#ff7043", + "backgroundColor": "#7950f2", + "width": 75, + "height": 36, + "seed": 1032706680, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1643333366011, + "fontSize": 28, + "fontFamily": 1, + "text": "Timer", + "baseline": 25, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Timer" + }, + { + "type": "rectangle", + "version": 5052, + "versionNonce": 310536184, + "isDeleted": false, + "id": "qfqvZwfY8g2maDsv8tzi-", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1281.5965467571473, + "y": 81.99348491888458, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 320002220, + "groupIds": [ + "33LizGdDAL7lJNoiSiJZy" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "CFu0B4Mw_1wC1Hbgx8Fs0" + }, + { + "type": "arrow", + "id": "XIl_NhaFtRO00pX5Pq6VU" + }, + { + "type": "arrow", + "id": "EndiSTFlx1AT7vcBVjgve" + }, + { + "id": "sbyEF_hTthhrIaA-47du2", + "type": "arrow" + }, + { + "id": "3DJ_wB8Ty8UNX4KAZfwjT", + "type": "arrow" + } + ], + "updated": 1643333366011 + }, + { + "type": "rectangle", + "version": 5100, + "versionNonce": 456274824, + "isDeleted": false, + "id": "Cydk-vrvGXEEAXU4XHWk7", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1274.049113721433, + "y": 74.97339563317036, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 2124765204, + "groupIds": [ + "m2CJZBWcxZ3sXLBcib5Jt", + "33LizGdDAL7lJNoiSiJZy" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "CFu0B4Mw_1wC1Hbgx8Fs0" + }, + { + "type": "arrow", + "id": "XIl_NhaFtRO00pX5Pq6VU" + }, + { + "type": "arrow", + "id": "EndiSTFlx1AT7vcBVjgve" + } + ], + "updated": 1643333366011 + }, + { + "type": "rectangle", + "version": 5197, + "versionNonce": 249735416, + "isDeleted": false, + "id": "lIucERyh1MMe7Vzo8_q7d", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1264.6540244357177, + "y": 66.71948938317058, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 722133804, + "groupIds": [ + "33LizGdDAL7lJNoiSiJZy" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "CFu0B4Mw_1wC1Hbgx8Fs0" + }, + { + "type": "arrow", + "id": "XIl_NhaFtRO00pX5Pq6VU" + }, + { + "type": "arrow", + "id": "EndiSTFlx1AT7vcBVjgve" + }, + { + "id": "IEnqZv0JAf73xE5G5LY1s", + "type": "arrow" + } + ], + "updated": 1643333366011 + }, + { + "type": "line", + "version": 4318, + "versionNonce": 1152549512, + "isDeleted": false, + "id": "3UaWROnZ6NNaCTSa51ntD", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1276.6404510226287, + "y": 115.46672643052324, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 46.57983585730082, + "height": 3.249953844290203, + "seed": 166278548, + "groupIds": [ + "33LizGdDAL7lJNoiSiJZy" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1643333366011, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 40.42449133807562, + 0.1573930526684746 + ], + [ + 46.57983585730082, + -3.0925607916217284 + ] + ] + }, + { + "type": "line", + "version": 4344, + "versionNonce": 2006108664, + "isDeleted": false, + "id": "H3c0943V86dq4MeskNY-E", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1278.495703260643, + "y": 84.00143913168466, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 45.567415680676426, + "height": 2.8032978840147194, + "seed": 1285397932, + "groupIds": [ + "33LizGdDAL7lJNoiSiJZy" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1643333366011, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 16.832548902953302, + -2.8032978840147194 + ], + [ + 45.567415680676426, + -0.3275477042019195 + ] + ] + }, + { + "type": "line", + "version": 4369, + "versionNonce": 391180680, + "isDeleted": false, + "id": "g62fLo1BCU_TuN8OEk_ie", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1278.4191037442736, + "y": 151.39581340204182, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 48.33668263438425, + "height": 4.280657518731036, + "seed": 198533908, + "groupIds": [ + "33LizGdDAL7lJNoiSiJZy" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1643333366011, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 26.41225578429045, + -0.2552319773002338 + ], + [ + 37.62000339651456, + 2.3153712935189787 + ], + [ + 48.33668263438425, + -1.9652862252120569 + ] + ] + }, + { + "type": "line", + "version": 4406, + "versionNonce": 2006371064, + "isDeleted": false, + "id": "jMbD-eUPMlCihQh7GK--c", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1275.2350565627432, + "y": 162.6389618103206, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 54.40694982784246, + "height": 2.9096445412231735, + "seed": 1358235692, + "groupIds": [ + "33LizGdDAL7lJNoiSiJZy" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1643333366011, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 10.166093050596771, + -1.166642430373031 + ], + [ + 16.130660965377448, + -0.8422655250909383 + ], + [ + 46.26079588567538, + 0.6125567455206506 + ], + [ + 54.40694982784246, + -2.297087795702523 + ] + ] + }, + { + "type": "line", + "version": 4371, + "versionNonce": 172589192, + "isDeleted": false, + "id": "lmzq_v_7Zzb2T4WxQPEqG", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1275.519899458309, + "y": 99.06204068494588, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 46.92865289294453, + "height": 2.4757501798128, + "seed": 719315092, + "groupIds": [ + "33LizGdDAL7lJNoiSiJZy" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1643333366011, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 18.193786115221407, + -0.5912874140789839 + ], + [ + 46.92865289294453, + 1.884462765733816 + ] + ] + }, + { + "type": "line", + "version": 4386, + "versionNonce": 963816440, + "isDeleted": false, + "id": "YoZdONxrJotpnXjxFe748", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1275.6273876544594, + "y": 133.54536972078546, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 46.92865289294453, + "height": 2.4757501798128, + "seed": 1543185068, + "groupIds": [ + "33LizGdDAL7lJNoiSiJZy" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1643333366011, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 8.093938105125233, + 1.4279702913643746 + ], + [ + 18.193786115221407, + -0.5912874140789839 + ], + [ + 46.92865289294453, + 1.884462765733816 + ] + ] + }, + { + "type": "text", + "version": 596, + "versionNonce": 1297624968, + "isDeleted": false, + "id": "fAIYPmdTgnjEqc8PGu-SI", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1255.9645759420482, + "y": 203.9818965480822, + "strokeColor": "#00e676", + "backgroundColor": "#03a9f4", + "width": 134, + "height": 26, + "seed": 1891202964, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1643333366011, + "fontSize": 20, + "fontFamily": 1, + "text": "Output Data", + "baseline": 18, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Output Data" + }, + { + "type": "ellipse", + "version": 901, + "versionNonce": 1487149704, + "isDeleted": false, + "id": "j-wM9an_6t3rpXVK34zli", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1022.0933939958717, + "y": 100.46179899047428, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 81.77778795030434, + "height": 81.77778795030434, + "seed": 1020498168, + "groupIds": [ + "bSaKYXsjdi0TEAthptqF8", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "3DJ_wB8Ty8UNX4KAZfwjT", + "type": "arrow" + }, + { + "id": "IEnqZv0JAf73xE5G5LY1s", + "type": "arrow" + } + ], + "updated": 1643333383394 + }, + { + "type": "ellipse", + "version": 1081, + "versionNonce": 1766820232, + "isDeleted": false, + "id": "9vh3f3W1oSyVisk-dvz1f", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1025.1015457438984, + "y": 102.95003035938365, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 76.42992266951718, + "height": 76.42992266951718, + "seed": 119976440, + "groupIds": [ + "bSaKYXsjdi0TEAthptqF8", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "IEnqZv0JAf73xE5G5LY1s", + "type": "arrow" + }, + { + "id": "3DJ_wB8Ty8UNX4KAZfwjT", + "type": "arrow" + } + ], + "updated": 1643333383394 + }, + { + "type": "line", + "version": 892, + "versionNonce": 1354045576, + "isDeleted": false, + "id": "iqDqmrD3b6Ygz-GFOrXwa", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1061.8311628994297, + "y": 120.0482909120675, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 0.0883491007133227, + "height": 25.95185095385813, + "seed": 1013822200, + "groupIds": [ + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383394, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0.0883491007133227, + 25.95185095385813 + ] + ] + }, + { + "type": "line", + "version": 970, + "versionNonce": 1922090888, + "isDeleted": false, + "id": "ZwJWJndwUCnxKlrLJvMPl", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 1.9734847452380198, + "x": 1070.7802432970152, + "y": 142.30181631205494, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 0.8395797827843811, + "height": 15.147722701630906, + "seed": 321688568, + "groupIds": [ + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383394, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0.8395797827843811, + 15.147722701630906 + ] + ] + }, + { + "type": "line", + "version": 845, + "versionNonce": 1528731272, + "isDeleted": false, + "id": "t0x1SEUOcZRg50TGtYP-w", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 6.280886784478146, + "x": 1064.6162574402501, + "y": 173.36067968426403, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 0.002850147906090324, + "height": 5.074594937944382, + "seed": 1365726456, + "groupIds": [ + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383394, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.002850147906090324, + 5.074594937944382 + ] + ] + }, + { + "type": "line", + "version": 1529, + "versionNonce": 790267272, + "isDeleted": false, + "id": "Yw89MpxBojMQgM6zgnIuk", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 3.1241889472809277, + "x": 1062.1357257347163, + "y": 102.4866803252352, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 0.002850147906090324, + "height": 5.074594937944382, + "seed": 1554118136, + "groupIds": [ + "YELVY3p7Zx7E_9iDQmVra", + "NVEUOovoAoQURmU2RDepy", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383394, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.002850147906090324, + 5.074594937944382 + ] + ] + }, + { + "type": "line", + "version": 1166, + "versionNonce": 465605768, + "isDeleted": false, + "id": "cvYavS_2xzzp8a7_MTILV", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 5.81125135713188, + "x": 1082.8189605518937, + "y": 166.68716660884547, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 0.002850147906090324, + "height": 5.074594937944382, + "seed": 1239447288, + "groupIds": [ + "p-wnEbrxq-_9Ag8WjVI7N", + "SHbRHlotuJGYNAc1emjPb", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383394, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.002850147906090324, + 5.074594937944382 + ] + ] + }, + { + "type": "line", + "version": 682, + "versionNonce": 1187824520, + "isDeleted": false, + "id": "yixQP0fZUpIySDIZ4MLuw", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1068.9460036459864, + "y": 178.0991106962765, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.13385788646434224, + "height": 1.824909818276623, + "seed": 1839650808, + "groupIds": [ + "Mnb3dkXLcGQCoV-PGx3Al", + "SHbRHlotuJGYNAc1emjPb", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383394, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.13385788646434224, + -1.824909818276623 + ] + ] + }, + { + "type": "line", + "version": 754, + "versionNonce": 1048253064, + "isDeleted": false, + "id": "MAdztZi4TKHJd1ANMjVVe", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 6.108887538323692, + "x": 1073.4119741916074, + "y": 177.75916345378153, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.10961722295540602, + "height": 1.9312918250918398, + "seed": 1811486968, + "groupIds": [ + "Mnb3dkXLcGQCoV-PGx3Al", + "SHbRHlotuJGYNAc1emjPb", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383395, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.10961722295540602, + -1.9312918250918398 + ] + ] + }, + { + "type": "line", + "version": 759, + "versionNonce": 1770488200, + "isDeleted": false, + "id": "4KXJlSh8ncnoULQOmGcEa", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 6.108887538323692, + "x": 1076.38361498242, + "y": 176.04686252433794, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15393862916480724, + "height": 1.4493087289890376, + "seed": 1213938168, + "groupIds": [ + "Mnb3dkXLcGQCoV-PGx3Al", + "SHbRHlotuJGYNAc1emjPb", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383395, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15393862916480724, + -1.4493087289890376 + ] + ] + }, + { + "type": "line", + "version": 809, + "versionNonce": 1999507592, + "isDeleted": false, + "id": "wdrgkWEU57wvnk67HRB3Z", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 6.031023708299764, + "x": 1079.4047722054033, + "y": 174.21268269656684, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15495851926454807, + "height": 1.1604068145567383, + "seed": 1976646392, + "groupIds": [ + "Mnb3dkXLcGQCoV-PGx3Al", + "SHbRHlotuJGYNAc1emjPb", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383395, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15495851926454807, + -1.1604068145567383 + ] + ] + }, + { + "type": "line", + "version": 1268, + "versionNonce": 1328201608, + "isDeleted": false, + "id": "LFYbBqV93gdPJr7Cums4g", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 5.181114071523705, + "x": 1095.014529821029, + "y": 152.77748807307205, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 0.002850147906090324, + "height": 5.074594937944382, + "seed": 1725897720, + "groupIds": [ + "P0muzq2rC_ILu5MzTfeLX", + "995t9r7bEj_eb_vHEY1Ks", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383395, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.002850147906090324, + 5.074594937944382 + ] + ] + }, + { + "type": "line", + "version": 775, + "versionNonce": 28937864, + "isDeleted": false, + "id": "Rx0uNkINQcMII9UMJRJ4k", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 5.806701628990837, + "x": 1086.0116981168749, + "y": 169.7448604067505, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.13385788646434224, + "height": 1.824909818276623, + "seed": 495508728, + "groupIds": [ + "HS-2qloHB3pdPonai7TyU", + "995t9r7bEj_eb_vHEY1Ks", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383395, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.13385788646434224, + -1.824909818276623 + ] + ] + }, + { + "type": "line", + "version": 838, + "versionNonce": 932609416, + "isDeleted": false, + "id": "VLCl-pe-WQcf1BfPXBLt0", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 5.632403860134943, + "x": 1088.9868069295608, + "y": 166.505670147792, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.10961722295540602, + "height": 1.9312918250918398, + "seed": 995261944, + "groupIds": [ + "HS-2qloHB3pdPonai7TyU", + "995t9r7bEj_eb_vHEY1Ks", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383395, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.10961722295540602, + -1.9312918250918398 + ] + ] + }, + { + "type": "line", + "version": 834, + "versionNonce": 570264712, + "isDeleted": false, + "id": "mUOYjWx-N-OWdJoscwtE-", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 5.632403860134943, + "x": 1092.1465529032048, + "y": 163.23329625583096, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15393862916480724, + "height": 1.4493087289890376, + "seed": 289406712, + "groupIds": [ + "HS-2qloHB3pdPonai7TyU", + "995t9r7bEj_eb_vHEY1Ks", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383395, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15393862916480724, + -1.4493087289890376 + ] + ] + }, + { + "type": "line", + "version": 886, + "versionNonce": 1789574024, + "isDeleted": false, + "id": "qVO9k0FHbZuyI5LKP9GmF", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 5.5545400301110135, + "x": 1094.2769300914624, + "y": 160.64155267647118, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15495851926454807, + "height": 1.1604068145567383, + "seed": 802512888, + "groupIds": [ + "HS-2qloHB3pdPonai7TyU", + "995t9r7bEj_eb_vHEY1Ks", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383395, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15495851926454807, + -1.1604068145567383 + ] + ] + }, + { + "type": "line", + "version": 1308, + "versionNonce": 601492104, + "isDeleted": false, + "id": "NadcFz4xk5R9NqW8X-MU3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 4.703634473789069, + "x": 1098.3775913645477, + "y": 133.59478791519757, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 0.002850147906090324, + "height": 5.074594937944382, + "seed": 1997971704, + "groupIds": [ + "zA8Ync2bFFM2m53cjUMBg", + "B0S01CmiwscoHuDIbl3Gl", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383395, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.002850147906090324, + 5.074594937944382 + ] + ] + }, + { + "type": "line", + "version": 809, + "versionNonce": 944208264, + "isDeleted": false, + "id": "diAnwk9JCDVD9lfXnCqgW", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 5.236849790887245, + "x": 1097.7186508669784, + "y": 153.30649098297056, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.13385788646434224, + "height": 1.824909818276623, + "seed": 646431224, + "groupIds": [ + "DWLrPYo50Rd-VJ5VCi5f7", + "B0S01CmiwscoHuDIbl3Gl", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383395, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.13385788646434224, + -1.824909818276623 + ] + ] + }, + { + "type": "line", + "version": 883, + "versionNonce": 1529809032, + "isDeleted": false, + "id": "mtlzm_62GJjdDi5RAw4o1", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 5.062552022031351, + "x": 1098.749454343585, + "y": 149.1780894880784, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.10961722295540602, + "height": 1.9312918250918398, + "seed": 1831650040, + "groupIds": [ + "DWLrPYo50Rd-VJ5VCi5f7", + "B0S01CmiwscoHuDIbl3Gl", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383396, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.10961722295540602, + -1.9312918250918398 + ] + ] + }, + { + "type": "line", + "version": 894, + "versionNonce": 2018293640, + "isDeleted": false, + "id": "w7cJevDtNweeN4giVd7kF", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 5.062552022031351, + "x": 1099.6846444314046, + "y": 145.2886034713148, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15393862916480724, + "height": 1.4493087289890376, + "seed": 2039063544, + "groupIds": [ + "DWLrPYo50Rd-VJ5VCi5f7", + "B0S01CmiwscoHuDIbl3Gl", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383396, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15393862916480724, + -1.4493087289890376 + ] + ] + }, + { + "type": "line", + "version": 942, + "versionNonce": 601654920, + "isDeleted": false, + "id": "f56a_kgrxmRNa3Z53Q60z", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 4.984688192007422, + "x": 1099.6193083343842, + "y": 141.54632901313482, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15495851926454807, + "height": 1.1604068145567383, + "seed": 161637624, + "groupIds": [ + "DWLrPYo50Rd-VJ5VCi5f7", + "B0S01CmiwscoHuDIbl3Gl", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383396, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15495851926454807, + -1.1604068145567383 + ] + ] + }, + { + "type": "line", + "version": 1389, + "versionNonce": 177894792, + "isDeleted": false, + "id": "vR3QmWptomkwPkB3sxLie", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 4.175269496869788, + "x": 1091.7346739152727, + "y": 117.50269095596911, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 0.002850147906090324, + "height": 5.074594937944382, + "seed": 2063271416, + "groupIds": [ + "J0ilB_WV5wNeHV0loD0PU", + "picpNQI8seSh60PT9pkMm", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383396, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.002850147906090324, + 5.074594937944382 + ] + ] + }, + { + "type": "line", + "version": 894, + "versionNonce": 1640137864, + "isDeleted": false, + "id": "OotDa77gvcGT8eSVzCVHe", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 4.652271214951025, + "x": 1099.0076812735788, + "y": 133.53986124540666, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.13385788646434224, + "height": 1.824909818276623, + "seed": 1519879928, + "groupIds": [ + "hyo7dIZ-atzeosWf4KGV-", + "picpNQI8seSh60PT9pkMm", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383396, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.13385788646434224, + -1.824909818276623 + ] + ] + }, + { + "type": "line", + "version": 970, + "versionNonce": 1460712328, + "isDeleted": false, + "id": "KYXdIOqSNI3wL2_91JoSI", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 4.477973446095131, + "x": 1097.9247650219345, + "y": 129.89327965571465, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.10961722295540602, + "height": 1.9312918250918398, + "seed": 1962665976, + "groupIds": [ + "hyo7dIZ-atzeosWf4KGV-", + "picpNQI8seSh60PT9pkMm", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383396, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.10961722295540602, + -1.9312918250918398 + ] + ] + }, + { + "type": "line", + "version": 984, + "versionNonce": 12649096, + "isDeleted": false, + "id": "1amz5KsYAaVgqHJnfpUZX", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 4.477973446095131, + "x": 1096.4518739202772, + "y": 125.37009602466333, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15393862916480724, + "height": 1.4493087289890376, + "seed": 1714170104, + "groupIds": [ + "hyo7dIZ-atzeosWf4KGV-", + "picpNQI8seSh60PT9pkMm", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383396, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15393862916480724, + -1.4493087289890376 + ] + ] + }, + { + "type": "line", + "version": 1030, + "versionNonce": 1301246344, + "isDeleted": false, + "id": "M8DZXa5ygyRUlbzzJWUwL", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 4.400109616071202, + "x": 1094.1448873155746, + "y": 122.38879744583448, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15495851926454807, + "height": 1.1604068145567383, + "seed": 699156984, + "groupIds": [ + "hyo7dIZ-atzeosWf4KGV-", + "picpNQI8seSh60PT9pkMm", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383396, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15495851926454807, + -1.1604068145567383 + ] + ] + }, + { + "type": "line", + "version": 1445, + "versionNonce": 1958215816, + "isDeleted": false, + "id": "JH6nmvMEUuLCuyxh7Y-Yl", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 3.866572733087537, + "x": 1078.0076399536329, + "y": 106.44508741487394, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 0.002850147906090324, + "height": 5.074594937944382, + "seed": 713050872, + "groupIds": [ + "Xj8brNstIhDD7KOQvspnH", + "B8ckCsKKOGs9N9f2IAKBw", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383396, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.002850147906090324, + 5.074594937944382 + ] + ] + }, + { + "type": "line", + "version": 965, + "versionNonce": 1710665608, + "isDeleted": false, + "id": "GDNvbfgT_WZFh6Gi2xvwP", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 4.210232827779823, + "x": 1090.4068310713433, + "y": 117.91497341317267, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.13385788646434224, + "height": 1.824909818276623, + "seed": 1395403768, + "groupIds": [ + "hogKkVF1m8maBYwOG6eeL", + "B8ckCsKKOGs9N9f2IAKBw", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383396, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.13385788646434224, + -1.824909818276623 + ] + ] + }, + { + "type": "line", + "version": 1030, + "versionNonce": 965181064, + "isDeleted": false, + "id": "PvojRAHs1S-Tx4T69Sj14", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 4.035935058923929, + "x": 1088.4824681935847, + "y": 115.14139125873646, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.10961722295540602, + "height": 1.9312918250918398, + "seed": 498330872, + "groupIds": [ + "hogKkVF1m8maBYwOG6eeL", + "B8ckCsKKOGs9N9f2IAKBw", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383396, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.10961722295540602, + -1.9312918250918398 + ] + ] + }, + { + "type": "line", + "version": 1042, + "versionNonce": 539389320, + "isDeleted": false, + "id": "kLpgd0Yu3CP-rHZvMfTmB", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 4.035935058923929, + "x": 1085.3441396608778, + "y": 112.34041881492135, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15393862916480724, + "height": 1.4493087289890376, + "seed": 597396984, + "groupIds": [ + "hogKkVF1m8maBYwOG6eeL", + "B8ckCsKKOGs9N9f2IAKBw", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383396, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15393862916480724, + -1.4493087289890376 + ] + ] + }, + { + "type": "line", + "version": 1093, + "versionNonce": 1393523848, + "isDeleted": false, + "id": "XW0QvYMk_SlAi17ooDA2B", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 3.9580712288999997, + "x": 1082.318504219098, + "y": 110.56683796369228, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15495851926454807, + "height": 1.1604068145567383, + "seed": 770117368, + "groupIds": [ + "hogKkVF1m8maBYwOG6eeL", + "B8ckCsKKOGs9N9f2IAKBw", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383396, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15495851926454807, + -1.1604068145567383 + ] + ] + }, + { + "type": "line", + "version": 1023, + "versionNonce": 67021704, + "isDeleted": false, + "id": "F30qFOuZ0dhNLow90Cpan", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 3.7190910390046312, + "x": 1076.2565286147315, + "y": 107.99017766168507, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.13385788646434224, + "height": 1.824909818276623, + "seed": 1159360504, + "groupIds": [ + "oCizKDxwJR0_xadyu1U-s", + "XOmrDYeKZbtBad03NUVlw", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383396, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.13385788646434224, + -1.824909818276623 + ] + ] + }, + { + "type": "line", + "version": 1095, + "versionNonce": 1294989960, + "isDeleted": false, + "id": "LHYAA9-Xbe2aHOuAq8GMZ", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 3.5447932701487375, + "x": 1073.786092965824, + "y": 106.12279110057776, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.10961722295540602, + "height": 1.9312918250918398, + "seed": 1094299896, + "groupIds": [ + "oCizKDxwJR0_xadyu1U-s", + "XOmrDYeKZbtBad03NUVlw", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383396, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.10961722295540602, + -1.9312918250918398 + ] + ] + }, + { + "type": "line", + "version": 1106, + "versionNonce": 559901064, + "isDeleted": false, + "id": "bYj77Rzmjzf8NSjoCwzxt", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 3.5447932701487375, + "x": 1069.089741413119, + "y": 105.47928063454734, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15393862916480724, + "height": 1.4493087289890376, + "seed": 1506333176, + "groupIds": [ + "oCizKDxwJR0_xadyu1U-s", + "XOmrDYeKZbtBad03NUVlw", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383396, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15393862916480724, + -1.4493087289890376 + ] + ] + }, + { + "type": "line", + "version": 1147, + "versionNonce": 1003026568, + "isDeleted": false, + "id": "EHmP4t_Ln-k9nf0WDuFxp", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 3.466929440124808, + "x": 1065.1678246220067, + "y": 104.30035945599846, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15495851926454807, + "height": 1.1604068145567383, + "seed": 902550264, + "groupIds": [ + "oCizKDxwJR0_xadyu1U-s", + "XOmrDYeKZbtBad03NUVlw", + "gOQmYoFEh-8jFI92IdwYc", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383396, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15495851926454807, + -1.1604068145567383 + ] + ] + }, + { + "type": "line", + "version": 1476, + "versionNonce": 271099784, + "isDeleted": false, + "id": "mG1ykrgdbCjwTTLG076UZ", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 8.970693917422981, + "x": 1043.6956781873973, + "y": 109.05152880383471, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 0.002850147906090324, + "height": 5.074594937944382, + "seed": 2076619768, + "groupIds": [ + "aWz-oPOUVw7blecn5ozPM", + "htHHyoSrCWjL5m6ei2Yqp", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383396, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.002850147906090324, + 5.074594937944382 + ] + ] + }, + { + "type": "line", + "version": 980, + "versionNonce": 985298568, + "isDeleted": false, + "id": "Gg1-a5DZ1iqKankEc7NZZ", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 3.159442560291101, + "x": 1058.2415775748786, + "y": 105.58456047024029, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.13385788646434224, + "height": 1.824909818276623, + "seed": 1639806200, + "groupIds": [ + "aWJ4c9SQuu3MMphEuQ-ho", + "htHHyoSrCWjL5m6ei2Yqp", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383396, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.13385788646434224, + -1.824909818276623 + ] + ] + }, + { + "type": "line", + "version": 1047, + "versionNonce": 1882875272, + "isDeleted": false, + "id": "WwKJCewx6wFvIxRehBPm7", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 9.268330098614793, + "x": 1054.6420144085232, + "y": 105.72185538275599, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.10961722295540602, + "height": 1.9312918250918398, + "seed": 1026823672, + "groupIds": [ + "aWJ4c9SQuu3MMphEuQ-ho", + "htHHyoSrCWjL5m6ei2Yqp", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383397, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.10961722295540602, + -1.9312918250918398 + ] + ] + }, + { + "type": "line", + "version": 1066, + "versionNonce": 1843688584, + "isDeleted": false, + "id": "LHWQbDIvQxDN7glIRnmaX", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 9.268330098614793, + "x": 1049.9323826134132, + "y": 107.30966487248091, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15393862916480724, + "height": 1.4493087289890376, + "seed": 321820408, + "groupIds": [ + "aWJ4c9SQuu3MMphEuQ-ho", + "htHHyoSrCWjL5m6ei2Yqp", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383397, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15393862916480724, + -1.4493087289890376 + ] + ] + }, + { + "type": "line", + "version": 1109, + "versionNonce": 1769251720, + "isDeleted": false, + "id": "WrjcrvJkD_rOZMXa3vD3o", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 9.190466268590864, + "x": 1046.5890933046576, + "y": 108.06753249633519, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15495851926454807, + "height": 1.1604068145567383, + "seed": 674297848, + "groupIds": [ + "aWJ4c9SQuu3MMphEuQ-ho", + "htHHyoSrCWjL5m6ei2Yqp", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383397, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15495851926454807, + -1.1604068145567383 + ] + ] + }, + { + "type": "line", + "version": 1562, + "versionNonce": 19329672, + "isDeleted": false, + "id": "0xUXIFFhdKATefNgdnCTU", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 8.340556631814806, + "x": 1030.9425520228833, + "y": 123.07231991978585, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 0.002850147906090324, + "height": 5.074594937944382, + "seed": 272578808, + "groupIds": [ + "qYk4rrM4sZlnENZKO3UnW", + "sntyvqr_NecJhARNGx2SL", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383397, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.002850147906090324, + 5.074594937944382 + ] + ] + }, + { + "type": "line", + "version": 1055, + "versionNonce": 733438344, + "isDeleted": false, + "id": "ZfGjqQgXwQywcm8hhtTbN", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 8.966144189281938, + "x": 1040.3818202212965, + "y": 113.2999529930529, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.13385788646434224, + "height": 1.824909818276623, + "seed": 1054693880, + "groupIds": [ + "TjeeBqtMlR-uheSJHDiPh", + "sntyvqr_NecJhARNGx2SL", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383397, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.13385788646434224, + -1.824909818276623 + ] + ] + }, + { + "type": "line", + "version": 1127, + "versionNonce": 677507208, + "isDeleted": false, + "id": "aZlViJDSlHn7tGAruxEJU", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 8.791846420426044, + "x": 1037.2111805862442, + "y": 115.53583148972575, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.10961722295540602, + "height": 1.9312918250918398, + "seed": 386882296, + "groupIds": [ + "TjeeBqtMlR-uheSJHDiPh", + "sntyvqr_NecJhARNGx2SL", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383397, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.10961722295540602, + -1.9312918250918398 + ] + ] + }, + { + "type": "line", + "version": 1136, + "versionNonce": 1860345736, + "isDeleted": false, + "id": "5d6J2AYoIsJCqodlKVvFF", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 8.791846420426044, + "x": 1034.5956147314077, + "y": 118.39218538985745, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15393862916480724, + "height": 1.4493087289890376, + "seed": 2069445624, + "groupIds": [ + "TjeeBqtMlR-uheSJHDiPh", + "sntyvqr_NecJhARNGx2SL", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383397, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15393862916480724, + -1.4493087289890376 + ] + ] + }, + { + "type": "line", + "version": 1187, + "versionNonce": 527789704, + "isDeleted": false, + "id": "Id2K7BQkmsbEgwiaNHvnl", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 8.713982590402114, + "x": 1031.5298366450852, + "y": 121.68478825218517, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15495851926454807, + "height": 1.1604068145567383, + "seed": 1572797688, + "groupIds": [ + "TjeeBqtMlR-uheSJHDiPh", + "sntyvqr_NecJhARNGx2SL", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383397, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15495851926454807, + -1.1604068145567383 + ] + ] + }, + { + "type": "line", + "version": 1640, + "versionNonce": 1926651272, + "isDeleted": false, + "id": "WQpwsmcJYAvTLHlb5qqMO", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 7.850435269712758, + "x": 1027.322055413258, + "y": 141.29685506859505, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 0.002850147906090324, + "height": 5.074594937944382, + "seed": 1338285560, + "groupIds": [ + "mdak74oZmgKI-RKor4lH3", + "Lcydq1Ofxyd1uRFHNQht1", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383397, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.002850147906090324, + 5.074594937944382 + ] + ] + }, + { + "type": "line", + "version": 1132, + "versionNonce": 708930696, + "isDeleted": false, + "id": "kY4qf7bBSoSDFsDAonEva", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 8.383650586810933, + "x": 1027.767632811453, + "y": 129.6196997470763, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.13385788646434224, + "height": 1.824909818276623, + "seed": 309206776, + "groupIds": [ + "rXzQvZA_jRDvfnnUX32Os", + "Lcydq1Ofxyd1uRFHNQht1", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383397, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.13385788646434224, + -1.824909818276623 + ] + ] + }, + { + "type": "line", + "version": 1206, + "versionNonce": 2138217352, + "isDeleted": false, + "id": "iYo2CtOtKYQEDM57HD40c", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 8.20935281795504, + "x": 1025.9097311088917, + "y": 132.79754112674334, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.10961722295540602, + "height": 1.9312918250918398, + "seed": 641695736, + "groupIds": [ + "rXzQvZA_jRDvfnnUX32Os", + "Lcydq1Ofxyd1uRFHNQht1", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383397, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.10961722295540602, + -1.9312918250918398 + ] + ] + }, + { + "type": "line", + "version": 1221, + "versionNonce": 346417800, + "isDeleted": false, + "id": "BDJwLwzCNzpBXbwJLDZ2G", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 8.20935281795504, + "x": 1025.7465834795503, + "y": 136.198675323957, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15393862916480724, + "height": 1.4493087289890376, + "seed": 1530708216, + "groupIds": [ + "rXzQvZA_jRDvfnnUX32Os", + "Lcydq1Ofxyd1uRFHNQht1", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383397, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15393862916480724, + -1.4493087289890376 + ] + ] + }, + { + "type": "line", + "version": 1268, + "versionNonce": 1687640456, + "isDeleted": false, + "id": "B1tuUNRwrNKyWw6chx8J7", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 8.13148898793111, + "x": 1025.7189251669538, + "y": 140.34816595989207, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15495851926454807, + "height": 1.1604068145567383, + "seed": 1625940472, + "groupIds": [ + "rXzQvZA_jRDvfnnUX32Os", + "Lcydq1Ofxyd1uRFHNQht1", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383397, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15495851926454807, + -1.1604068145567383 + ] + ] + }, + { + "type": "line", + "version": 1689, + "versionNonce": 847440008, + "isDeleted": false, + "id": "1X5bM69TBInx5Cc39a4MP", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 7.334712057160889, + "x": 1034.5138068116148, + "y": 158.13530394464817, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 0.002850147906090324, + "height": 5.074594937944382, + "seed": 1447748344, + "groupIds": [ + "zN689KS9Kxbl5l-WJ8L2b", + "9XDR2v2yWGCJb07_IfL46", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383397, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.002850147906090324, + 5.074594937944382 + ] + ] + }, + { + "type": "line", + "version": 1190, + "versionNonce": 2049218440, + "isDeleted": false, + "id": "smte7vi0Hb6SsmIRNQvAU", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 7.811713775242126, + "x": 1027.0233729407373, + "y": 149.3365526837065, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.13385788646434224, + "height": 1.824909818276623, + "seed": 1508099064, + "groupIds": [ + "Jioetyts4aSni0Wc5m4to", + "9XDR2v2yWGCJb07_IfL46", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383397, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.13385788646434224, + -1.824909818276623 + ] + ] + }, + { + "type": "line", + "version": 1269, + "versionNonce": 1605920392, + "isDeleted": false, + "id": "7A1rcZxIgibmrNb7OsBBA", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 7.637416006386232, + "x": 1027.450721482312, + "y": 153.30036268261665, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.10961722295540602, + "height": 1.9312918250918398, + "seed": 431523064, + "groupIds": [ + "Jioetyts4aSni0Wc5m4to", + "9XDR2v2yWGCJb07_IfL46", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383397, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.10961722295540602, + -1.9312918250918398 + ] + ] + }, + { + "type": "line", + "version": 1281, + "versionNonce": 536499592, + "isDeleted": false, + "id": "U7GuWg-FQFdG1o39ef9rx", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 7.637416006386232, + "x": 1029.1754665346089, + "y": 155.6877986065798, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15393862916480724, + "height": 1.4493087289890376, + "seed": 1666000376, + "groupIds": [ + "Jioetyts4aSni0Wc5m4to", + "9XDR2v2yWGCJb07_IfL46", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383397, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15393862916480724, + -1.4493087289890376 + ] + ] + }, + { + "type": "line", + "version": 1327, + "versionNonce": 2516104, + "isDeleted": false, + "id": "h8jL7BgZwCiKbJiccyKI1", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 7.559552176362303, + "x": 1031.2590192142006, + "y": 158.803031253158, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15495851926454807, + "height": 1.1604068145567383, + "seed": 866494200, + "groupIds": [ + "Jioetyts4aSni0Wc5m4to", + "9XDR2v2yWGCJb07_IfL46", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383398, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15495851926454807, + -1.1604068145567383 + ] + ] + }, + { + "type": "line", + "version": 1734, + "versionNonce": 1039700872, + "isDeleted": false, + "id": "HfpI3EBqLOZIUw8CopY49", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 7.026015293378638, + "x": 1047.1706930973965, + "y": 169.5461324783206, + "strokeColor": "#ff7043", + "backgroundColor": "#fff", + "width": 0.002850147906090324, + "height": 5.074594937944382, + "seed": 1195771896, + "groupIds": [ + "gCe_KP01zh55vsj8TEB6c", + "7iwovNHTIOdyQSVLGokMy", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383398, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.002850147906090324, + 5.074594937944382 + ] + ] + }, + { + "type": "line", + "version": 1257, + "versionNonce": 2142036616, + "isDeleted": false, + "id": "xsKRboS9ZYcxtioyXFXXx", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 7.369675388070924, + "x": 1035.0746865041747, + "y": 164.3384099264308, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.13385788646434224, + "height": 1.824909818276623, + "seed": 707938552, + "groupIds": [ + "z9Y9AYl6CQNhQWcmpyr7H", + "7iwovNHTIOdyQSVLGokMy", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383398, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.13385788646434224, + -1.824909818276623 + ] + ] + }, + { + "type": "line", + "version": 1337, + "versionNonce": 1140806024, + "isDeleted": false, + "id": "5s0mIhhlw3ALQwNvHssE3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 7.19537761921503, + "x": 1037.6241316546934, + "y": 167.32634419511794, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.10961722295540602, + "height": 1.9312918250918398, + "seed": 1206807032, + "groupIds": [ + "z9Y9AYl6CQNhQWcmpyr7H", + "7iwovNHTIOdyQSVLGokMy", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383398, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.10961722295540602, + -1.9312918250918398 + ] + ] + }, + { + "type": "line", + "version": 1360, + "versionNonce": 1378607240, + "isDeleted": false, + "id": "iQY4LZHj3mjwoiMUfCtHA", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 7.19537761921503, + "x": 1040.6724429007581, + "y": 170.40154204034366, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15393862916480724, + "height": 1.4493087289890376, + "seed": 2071882488, + "groupIds": [ + "z9Y9AYl6CQNhQWcmpyr7H", + "7iwovNHTIOdyQSVLGokMy", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383398, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15393862916480724, + -1.4493087289890376 + ] + ] + }, + { + "type": "line", + "version": 1394, + "versionNonce": 703500168, + "isDeleted": false, + "id": "vsDVSWZjnA9E_gvwthaHk", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 7.117513789191101, + "x": 1043.7775848433466, + "y": 171.6359211404589, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15495851926454807, + "height": 1.1604068145567383, + "seed": 526797816, + "groupIds": [ + "z9Y9AYl6CQNhQWcmpyr7H", + "7iwovNHTIOdyQSVLGokMy", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383398, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15495851926454807, + -1.1604068145567383 + ] + ] + }, + { + "type": "line", + "version": 1319, + "versionNonce": 1937989256, + "isDeleted": false, + "id": "olJn68aW_Z7IJUSsLvQkM", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 6.878533599295732, + "x": 1048.7652459830783, + "y": 175.01029570474202, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.13385788646434224, + "height": 1.824909818276623, + "seed": 497683704, + "groupIds": [ + "yPIdiHVxLiy_ePyCR-30f", + "S_idRPhbaHho_kpbqXhbB", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383398, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.13385788646434224, + -1.824909818276623 + ] + ] + }, + { + "type": "line", + "version": 1387, + "versionNonce": 1873169800, + "isDeleted": false, + "id": "Edcd597v0NtE3ZGvSieOB", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 6.704235830439838, + "x": 1052.236174741296, + "y": 176.6542187430698, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.10961722295540602, + "height": 1.9312918250918398, + "seed": 2144185848, + "groupIds": [ + "yPIdiHVxLiy_ePyCR-30f", + "S_idRPhbaHho_kpbqXhbB", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383398, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.10961722295540602, + -1.9312918250918398 + ] + ] + }, + { + "type": "line", + "version": 1401, + "versionNonce": 1737710728, + "isDeleted": false, + "id": "U24QSlSEfvlCb4V6bZh8e", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 6.704235830439838, + "x": 1056.1418818003908, + "y": 177.28050978386295, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15393862916480724, + "height": 1.4493087289890376, + "seed": 2127900408, + "groupIds": [ + "yPIdiHVxLiy_ePyCR-30f", + "S_idRPhbaHho_kpbqXhbB", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383398, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15393862916480724, + -1.4493087289890376 + ] + ] + }, + { + "type": "line", + "version": 1445, + "versionNonce": 486968200, + "isDeleted": false, + "id": "dLghPO0y4N7KB8GxBCKSw", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 6.626372000415909, + "x": 1059.7637637793173, + "y": 177.55113577418354, + "strokeColor": "#ff7043", + "backgroundColor": "transparent", + "width": 0.15495851926454807, + "height": 1.1604068145567383, + "seed": 323260408, + "groupIds": [ + "yPIdiHVxLiy_ePyCR-30f", + "S_idRPhbaHho_kpbqXhbB", + "cSYzw6LXU8ej7pfT_uMdR", + "-IHBW5QNCpMiroffvx8Q_" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1643333383398, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.15495851926454807, + -1.1604068145567383 + ] + ] + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/images/schedule.png b/docs/images/schedule.png new file mode 100644 index 0000000..61f8328 Binary files /dev/null and b/docs/images/schedule.png differ diff --git a/docs/images/search-dark.png b/docs/images/search-dark.png new file mode 100644 index 0000000..7b5f40e Binary files /dev/null and b/docs/images/search-dark.png differ diff --git a/docs/images/search.excalidraw b/docs/images/search.excalidraw new file mode 100644 index 0000000..227f04b --- /dev/null +++ b/docs/images/search.excalidraw @@ -0,0 +1,555 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "text", + "version": 354, + "versionNonce": 1260634512, + "isDeleted": false, + "id": "Buic2Lx427wuSIW8P_Rw5", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 829, + "y": 184, + "strokeColor": "#000000", + "backgroundColor": "#228be6", + "width": 452, + "height": 46, + "seed": 373648901, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1658676789638, + "link": null, + "locked": false, + "fontSize": 36, + "fontFamily": 1, + "text": "What is semantic search?", + "baseline": 32, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "What is semantic search?" + }, + { + "type": "rectangle", + "version": 786, + "versionNonce": 1756557200, + "isDeleted": false, + "id": "U2NgEIEiFpAlwmv5Xnyzr", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 50, + "angle": 0, + "x": 555.5, + "y": 425.30499999999995, + "strokeColor": "#7950f2", + "backgroundColor": "#7950f2", + "width": 995.3286713286714, + "height": 339.0000000000001, + "seed": 1946478225, + "groupIds": [ + "C_65R9XVeMQED2nMfIW2D" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1658676804492, + "link": "", + "locked": false + }, + { + "type": "text", + "version": 569, + "versionNonce": 1675727216, + "isDeleted": false, + "id": "fbSO8bnZmAdvIXZxBAtHY", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 570.7237762237763, + "y": 439.50080419580416, + "strokeColor": "#000", + "backgroundColor": "#03a9f4", + "width": 967, + "height": 312, + "seed": 1586673137, + "groupIds": [ + "C_65R9XVeMQED2nMfIW2D" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1658676804492, + "link": null, + "locked": false, + "fontSize": 20.27972027972028, + "fontFamily": 1, + "text": "Query Best Match\n----------------------------------------------------\nfeel good story Maine man wins $1M from $25 lottery ticket\nclimate change Canada's last fully intact ice shelf has suddenly collapsed, forming a \n Manhattan-sized iceberg\npublic health story US tops 5 million confirmed virus cases\nwar Beijing mobilises invasion craft along coast as Taiwan tensions escalate\nwildlife The National Park Service warns against sacrificing slower friends in a\n bear attack\nasia Beijing mobilises invasion craft along coast as Taiwan tensions escalate\nlucky Maine man wins $1M from $25 lottery ticket\ndishonest junk Make huge profits without work, earn up to $100,000 a day", + "baseline": 304, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Query Best Match\n----------------------------------------------------\nfeel good story Maine man wins $1M from $25 lottery ticket\nclimate change Canada's last fully intact ice shelf has suddenly collapsed, forming a \n Manhattan-sized iceberg\npublic health story US tops 5 million confirmed virus cases\nwar Beijing mobilises invasion craft along coast as Taiwan tensions escalate\nwildlife The National Park Service warns against sacrificing slower friends in a\n bear attack\nasia Beijing mobilises invasion craft along coast as Taiwan tensions escalate\nlucky Maine man wins $1M from $25 lottery ticket\ndishonest junk Make huge profits without work, earn up to $100,000 a day" + }, + { + "type": "rectangle", + "version": 952, + "versionNonce": 581259632, + "isDeleted": false, + "id": "UO6MS3wSDu7yg2421__LI", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 933, + "y": 267, + "strokeColor": "#ffeb3b", + "backgroundColor": "#ffeb3b", + "width": 214, + "height": 49, + "seed": 1629565989, + "groupIds": [ + "3sURMvhuRfR0M-Q3VRPbg" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "text", + "id": "8sp7H8ijWBlh6aMgZ0XTP" + }, + { + "id": "Qzp41i_jzQIBlAB_qFKFH", + "type": "arrow" + }, + { + "id": "SJ0F0Y81z9hir5qQWAJjk", + "type": "arrow" + } + ], + "updated": 1658676831306, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 1611, + "versionNonce": 1109835152, + "isDeleted": false, + "id": "qYd3q0Vjks7VOHUC9RR51", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 549, + "y": 267.5, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 219, + "height": 52, + "seed": 1441952427, + "groupIds": [ + "3sURMvhuRfR0M-Q3VRPbg" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "text", + "id": "WPeWn6N4rCHf0jY16N9Ge" + }, + { + "id": "Qzp41i_jzQIBlAB_qFKFH", + "type": "arrow" + } + ], + "updated": 1658676831306, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1297, + "versionNonce": 1725539184, + "isDeleted": false, + "id": "WPeWn6N4rCHf0jY16N9Ge", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 554, + "y": 275.5, + "strokeColor": "#000", + "backgroundColor": "#fa5252", + "width": 209, + "height": 36, + "seed": 870516459, + "groupIds": [ + "3sURMvhuRfR0M-Q3VRPbg" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1658676831306, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Vectorize", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "qYd3q0Vjks7VOHUC9RR51", + "originalText": "Vectorize" + }, + { + "type": "rectangle", + "version": 1198, + "versionNonce": 1201179536, + "isDeleted": false, + "id": "5VuUdI_BsJ5pyE1nTqJUI", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1332, + "y": 268, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 218, + "height": 49, + "seed": 1044404613, + "groupIds": [ + "3sURMvhuRfR0M-Q3VRPbg" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "bJJ9SGsJsvT071qBBH0w5", + "type": "text" + }, + { + "id": "bJJ9SGsJsvT071qBBH0w5", + "type": "text" + }, + { + "id": "Q51e0Hav-kYfjX3b8tt-r", + "type": "arrow" + }, + { + "type": "text", + "id": "bJJ9SGsJsvT071qBBH0w5" + }, + { + "id": "SJ0F0Y81z9hir5qQWAJjk", + "type": "arrow" + } + ], + "updated": 1658676831306, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1381, + "versionNonce": 78886256, + "isDeleted": false, + "id": "bJJ9SGsJsvT071qBBH0w5", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1337, + "y": 274.5, + "strokeColor": "#000", + "backgroundColor": "#fa5252", + "width": 208, + "height": 36, + "seed": 128953675, + "groupIds": [ + "3sURMvhuRfR0M-Q3VRPbg" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1658676831306, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Search", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "5VuUdI_BsJ5pyE1nTqJUI", + "originalText": "Search" + }, + { + "type": "text", + "version": 942, + "versionNonce": 429955472, + "isDeleted": false, + "id": "8sp7H8ijWBlh6aMgZ0XTP", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 938, + "y": 273.5, + "strokeColor": "#000", + "backgroundColor": "transparent", + "width": 204, + "height": 36, + "seed": 1854823263, + "groupIds": [ + "3sURMvhuRfR0M-Q3VRPbg" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1658676831306, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Index", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "UO6MS3wSDu7yg2421__LI", + "originalText": "Index" + }, + { + "type": "text", + "version": 382, + "versionNonce": 1098614640, + "isDeleted": false, + "id": "jWJpSXHkTCzRTCA4tbAgv", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 548.5, + "y": 347.30499999999995, + "strokeColor": "#000", + "backgroundColor": "#03a9f4", + "width": 296, + "height": 42, + "seed": 1241563487, + "groupIds": [ + "3sURMvhuRfR0M-Q3VRPbg" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1658676831307, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "- Transform input into numbers\n- Similar concepts have similar values", + "baseline": 36, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- Transform input into numbers\n- Similar concepts have similar values" + }, + { + "type": "text", + "version": 381, + "versionNonce": 1266083728, + "isDeleted": false, + "id": "qEnmXs0P_MQE8r4c4OWGh", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 931.5, + "y": 346.2074999999999, + "strokeColor": "#000", + "backgroundColor": "#f44336", + "width": 230, + "height": 42, + "seed": 1038536465, + "groupIds": [ + "3sURMvhuRfR0M-Q3VRPbg" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1658676831307, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "- Save vectors\n- Store content with vectors", + "baseline": 36, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- Save vectors\n- Store content with vectors" + }, + { + "type": "text", + "version": 491, + "versionNonce": 2037979504, + "isDeleted": false, + "id": "1q8bzjK8lnKUZj8_A9v7D", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1243, + "y": 349.2074999999999, + "strokeColor": "#000", + "backgroundColor": "#f44336", + "width": 333, + "height": 42, + "seed": 304472945, + "groupIds": [ + "3sURMvhuRfR0M-Q3VRPbg" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1658676831307, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "- Find similar vectors with cosine similarity\n- Add rule-based filters using content", + "baseline": 36, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- Find similar vectors with cosine similarity\n- Add rule-based filters using content" + }, + { + "type": "arrow", + "version": 1813, + "versionNonce": 1230206352, + "isDeleted": false, + "id": "Qzp41i_jzQIBlAB_qFKFH", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 770.5, + "y": 289.8470411964629, + "strokeColor": "#000", + "backgroundColor": "#f44336", + "width": 158.1310513485223, + "height": 0.5692601572380909, + "seed": 660786897, + "groupIds": [ + "3sURMvhuRfR0M-Q3VRPbg" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1658676831307, + "link": null, + "locked": false, + "startBinding": { + "elementId": "qYd3q0Vjks7VOHUC9RR51", + "focus": -0.15367587596362536, + "gap": 2.5 + }, + "endBinding": { + "elementId": "UO6MS3wSDu7yg2421__LI", + "focus": 0.027437144815141, + "gap": 4.3689486514776945 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 158.1310513485223, + 0.5692601572380909 + ] + ] + }, + { + "type": "arrow", + "version": 1962, + "versionNonce": 1072982896, + "isDeleted": false, + "id": "SJ0F0Y81z9hir5qQWAJjk", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1149.5, + "y": 292.6790761701911, + "strokeColor": "#000", + "backgroundColor": "#f44336", + "width": 181.5, + "height": 1.5898915058209013, + "seed": 899541905, + "groupIds": [ + "3sURMvhuRfR0M-Q3VRPbg" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1658676831307, + "link": null, + "locked": false, + "startBinding": { + "elementId": "UO6MS3wSDu7yg2421__LI", + "focus": 0.08406032225724415, + "gap": 2.5 + }, + "endBinding": { + "elementId": "5VuUdI_BsJ5pyE1nTqJUI", + "focus": 0.09327847520504394, + "gap": 1 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 181.5, + -1.5898915058209013 + ] + ] + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/images/search.png b/docs/images/search.png new file mode 100644 index 0000000..2bb471d Binary files /dev/null and b/docs/images/search.png differ diff --git a/docs/images/task-dark.png b/docs/images/task-dark.png new file mode 100644 index 0000000..59cce71 Binary files /dev/null and b/docs/images/task-dark.png differ diff --git a/docs/images/task.excalidraw b/docs/images/task.excalidraw new file mode 100644 index 0000000..4286939 --- /dev/null +++ b/docs/images/task.excalidraw @@ -0,0 +1,1124 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 4906, + "versionNonce": 976633260, + "isDeleted": false, + "id": "9itz4bqXi6IMeZhGhc42J", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 793.1319708150991, + "y": 84.01158837080237, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 2026142776, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "CFu0B4Mw_1wC1Hbgx8Fs0" + }, + { + "type": "arrow", + "id": "XIl_NhaFtRO00pX5Pq6VU" + }, + { + "type": "arrow", + "id": "EndiSTFlx1AT7vcBVjgve" + }, + { + "id": "sbyEF_hTthhrIaA-47du2", + "type": "arrow" + }, + { + "id": "3DJ_wB8Ty8UNX4KAZfwjT", + "type": "arrow" + } + ], + "updated": 1642714818536 + }, + { + "type": "rectangle", + "version": 4954, + "versionNonce": 1503052564, + "isDeleted": false, + "id": "7HUYNqBGn8JbkXw6qt_jr", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 785.5845377793848, + "y": 76.99149908508815, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 1090159432, + "groupIds": [ + "6R5o6OnVZ7fFC_87zCo7G", + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "CFu0B4Mw_1wC1Hbgx8Fs0" + }, + { + "type": "arrow", + "id": "XIl_NhaFtRO00pX5Pq6VU" + }, + { + "type": "arrow", + "id": "EndiSTFlx1AT7vcBVjgve" + } + ], + "updated": 1642714818536 + }, + { + "type": "rectangle", + "version": 5050, + "versionNonce": 1115269164, + "isDeleted": false, + "id": "mI_Mfw3cNZf0AuQhK5KUO", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 776.1894484936695, + "y": 68.73759283508838, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 1146631480, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "CFu0B4Mw_1wC1Hbgx8Fs0" + }, + { + "type": "arrow", + "id": "XIl_NhaFtRO00pX5Pq6VU" + }, + { + "type": "arrow", + "id": "EndiSTFlx1AT7vcBVjgve" + } + ], + "updated": 1642714818536 + }, + { + "type": "line", + "version": 4173, + "versionNonce": 2037727380, + "isDeleted": false, + "id": "y3oKPiP36axXzEF0c7OiC", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 788.1758750805805, + "y": 117.48482988244103, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 46.57983585730082, + "height": 3.249953844290203, + "seed": 1952111176, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1642714818536, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 40.42449133807562, + 0.1573930526684746 + ], + [ + 46.57983585730082, + -3.0925607916217284 + ] + ] + }, + { + "type": "line", + "version": 4199, + "versionNonce": 1944830636, + "isDeleted": false, + "id": "-52Udo1sfovtw-gxgUT91", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 790.0311273185948, + "y": 86.01954258360246, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 45.567415680676426, + "height": 2.8032978840147194, + "seed": 923586104, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1642714818536, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 16.832548902953302, + -2.8032978840147194 + ], + [ + 45.567415680676426, + -0.3275477042019195 + ] + ] + }, + { + "type": "line", + "version": 4224, + "versionNonce": 1671958036, + "isDeleted": false, + "id": "7Tgba-Lgh4JYVwHG-MlYn", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 789.9545278022254, + "y": 153.41391685395956, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 48.33668263438425, + "height": 4.280657518731036, + "seed": 664293704, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1642714818536, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 26.41225578429045, + -0.2552319773002338 + ], + [ + 37.62000339651456, + 2.3153712935189787 + ], + [ + 48.33668263438425, + -1.9652862252120569 + ] + ] + }, + { + "type": "line", + "version": 4261, + "versionNonce": 355896620, + "isDeleted": false, + "id": "KsHbDZNNeZoS7zbhCYNon", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 786.770480620695, + "y": 164.65706526223835, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 54.40694982784246, + "height": 2.9096445412231735, + "seed": 2043037496, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1642714818536, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 10.166093050596771, + -1.166642430373031 + ], + [ + 16.130660965377448, + -0.8422655250909383 + ], + [ + 46.26079588567538, + 0.6125567455206506 + ], + [ + 54.40694982784246, + -2.297087795702523 + ] + ] + }, + { + "type": "line", + "version": 4226, + "versionNonce": 1764043668, + "isDeleted": false, + "id": "R5nE2rgdgOpe93Q8VvYhl", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 787.0553235162608, + "y": 101.08014413686368, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 46.92865289294453, + "height": 2.4757501798128, + "seed": 571402312, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1642714818536, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 18.193786115221407, + -0.5912874140789839 + ], + [ + 46.92865289294453, + 1.884462765733816 + ] + ] + }, + { + "type": "line", + "version": 4241, + "versionNonce": 130287532, + "isDeleted": false, + "id": "lfWLpL8lTqrf2GMoqJY8y", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 787.1628117124112, + "y": 135.5634731727032, + "strokeColor": "#03a9f4", + "backgroundColor": "#fff", + "width": 46.92865289294453, + "height": 2.4757501798128, + "seed": 2028511288, + "groupIds": [ + "zRT55AcPrMo-vI2M6Q7JX" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1642714818536, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 8.093938105125233, + 1.4279702913643746 + ], + [ + 18.193786115221407, + -0.5912874140789839 + ], + [ + 46.92865289294453, + 1.884462765733816 + ] + ] + }, + { + "type": "text", + "version": 378, + "versionNonce": 1294951852, + "isDeleted": false, + "id": "uG1gxQCUX_W4xoK8WjzqS", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 762, + "y": 202, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 117, + "height": 26, + "seed": 657172488, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1642714818537, + "fontSize": 20, + "fontFamily": 1, + "text": "Input Data", + "baseline": 18, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Input Data" + }, + { + "type": "arrow", + "version": 657, + "versionNonce": 1814305556, + "isDeleted": false, + "id": "3DJ_wB8Ty8UNX4KAZfwjT", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 878.680938765565, + "y": 132.68136619602433, + "strokeColor": "#03a9f4", + "backgroundColor": "#ff7043", + "width": 104.88363252862348, + "height": 0.6395755770326446, + "seed": 1663624312, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1642714818537, + "startBinding": { + "elementId": "9itz4bqXi6IMeZhGhc42J", + "focus": -0.09772881248040827, + "gap": 14.870387259234633 + }, + "endBinding": { + "elementId": "FEMcxzUOmwHX8lGW-KR6K", + "focus": 0.36409382861238426, + "gap": 13.827470007461386 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 104.88363252862348, + 0.6395755770326446 + ] + ] + }, + { + "type": "arrow", + "version": 778, + "versionNonce": 1812844588, + "isDeleted": false, + "id": "IEnqZv0JAf73xE5G5LY1s", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1132.8223412617328, + "y": 135.16943809537315, + "strokeColor": "#00e676", + "backgroundColor": "#ff7043", + "width": 116.4656944766266, + "height": 1.9037944501569655, + "seed": 2055241224, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1642714818537, + "startBinding": { + "elementId": "SitnQfRbjMmvvsNhTxQRZ", + "focus": 0.4543845673944436, + "gap": 15.775328240798956 + }, + "endBinding": { + "elementId": "lIucERyh1MMe7Vzo8_q7d", + "focus": -0.22308504197782658, + "gap": 15.365988697358375 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 116.4656944766266, + -1.9037944501569655 + ] + ] + }, + { + "type": "text", + "version": 354, + "versionNonce": 9934996, + "isDeleted": false, + "id": "g9t3YfCk9S7S38pod_Itm", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1019.5, + "y": 70, + "strokeColor": "#ff7043", + "backgroundColor": "#7950f2", + "width": 84, + "height": 36, + "seed": 1032706680, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1642714818537, + "fontSize": 28, + "fontFamily": 1, + "text": "Action", + "baseline": 25, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Action" + }, + { + "type": "diamond", + "version": 775, + "versionNonce": 79652372, + "isDeleted": false, + "id": "jfnfQYlAh24YsuMMDHNx7", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1006.140893315531, + "y": 143.61217895662338, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 112.64736525303451, + "height": 36.77344700318558, + "seed": 2110406188, + "groupIds": [ + "HaVHECspFkRC2Qpa0lGum" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1642714818537 + }, + { + "type": "diamond", + "version": 816, + "versionNonce": 903918892, + "isDeleted": false, + "id": "6xQj2yQQKSeIivJj9W1ju", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1006.140893315531, + "y": 134.74039902836483, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 112.64736525303451, + "height": 36.77344700318558, + "seed": 1235290772, + "groupIds": [ + "HaVHECspFkRC2Qpa0lGum" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1642714818537 + }, + { + "type": "diamond", + "version": 898, + "versionNonce": 496075668, + "isDeleted": false, + "id": "FEMcxzUOmwHX8lGW-KR6K", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1006.140893315531, + "y": 122.10983943365278, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 112.64736525303451, + "height": 36.77344700318558, + "seed": 1689884844, + "groupIds": [ + "HaVHECspFkRC2Qpa0lGum" + ], + "strokeSharpness": "round", + "boundElements": [ + { + "id": "3DJ_wB8Ty8UNX4KAZfwjT", + "type": "arrow" + }, + { + "id": "IEnqZv0JAf73xE5G5LY1s", + "type": "arrow" + } + ], + "updated": 1642714818537 + }, + { + "type": "diamond", + "version": 940, + "versionNonce": 1625894828, + "isDeleted": false, + "id": "SitnQfRbjMmvvsNhTxQRZ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1006.140893315531, + "y": 109.57816713635543, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 112.64736525303451, + "height": 36.77344700318558, + "seed": 264935444, + "groupIds": [ + "HaVHECspFkRC2Qpa0lGum" + ], + "strokeSharpness": "round", + "boundElements": [ + { + "id": "IEnqZv0JAf73xE5G5LY1s", + "type": "arrow" + } + ], + "updated": 1642714818537 + }, + { + "type": "rectangle", + "version": 4955, + "versionNonce": 1631842580, + "isDeleted": false, + "id": "qfqvZwfY8g2maDsv8tzi-", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1281.5965467571473, + "y": 81.99348491888458, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 320002220, + "groupIds": [ + "33LizGdDAL7lJNoiSiJZy" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "CFu0B4Mw_1wC1Hbgx8Fs0" + }, + { + "type": "arrow", + "id": "XIl_NhaFtRO00pX5Pq6VU" + }, + { + "type": "arrow", + "id": "EndiSTFlx1AT7vcBVjgve" + }, + { + "id": "sbyEF_hTthhrIaA-47du2", + "type": "arrow" + }, + { + "id": "3DJ_wB8Ty8UNX4KAZfwjT", + "type": "arrow" + } + ], + "updated": 1642714818537 + }, + { + "type": "rectangle", + "version": 5003, + "versionNonce": 972970540, + "isDeleted": false, + "id": "Cydk-vrvGXEEAXU4XHWk7", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1274.049113721433, + "y": 74.97339563317036, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 2124765204, + "groupIds": [ + "m2CJZBWcxZ3sXLBcib5Jt", + "33LizGdDAL7lJNoiSiJZy" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "CFu0B4Mw_1wC1Hbgx8Fs0" + }, + { + "type": "arrow", + "id": "XIl_NhaFtRO00pX5Pq6VU" + }, + { + "type": "arrow", + "id": "EndiSTFlx1AT7vcBVjgve" + } + ], + "updated": 1642714818537 + }, + { + "type": "rectangle", + "version": 5100, + "versionNonce": 793554580, + "isDeleted": false, + "id": "lIucERyh1MMe7Vzo8_q7d", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1264.6540244357177, + "y": 66.71948938317058, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 70.67858069123133, + "height": 107.25081879410921, + "seed": 722133804, + "groupIds": [ + "33LizGdDAL7lJNoiSiJZy" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "arrow", + "id": "CFu0B4Mw_1wC1Hbgx8Fs0" + }, + { + "type": "arrow", + "id": "XIl_NhaFtRO00pX5Pq6VU" + }, + { + "type": "arrow", + "id": "EndiSTFlx1AT7vcBVjgve" + }, + { + "id": "IEnqZv0JAf73xE5G5LY1s", + "type": "arrow" + } + ], + "updated": 1642714818537 + }, + { + "type": "line", + "version": 4221, + "versionNonce": 1007301804, + "isDeleted": false, + "id": "3UaWROnZ6NNaCTSa51ntD", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1276.6404510226287, + "y": 115.46672643052324, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 46.57983585730082, + "height": 3.249953844290203, + "seed": 166278548, + "groupIds": [ + "33LizGdDAL7lJNoiSiJZy" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1642714818537, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 40.42449133807562, + 0.1573930526684746 + ], + [ + 46.57983585730082, + -3.0925607916217284 + ] + ] + }, + { + "type": "line", + "version": 4247, + "versionNonce": 140622868, + "isDeleted": false, + "id": "H3c0943V86dq4MeskNY-E", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1278.495703260643, + "y": 84.00143913168466, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 45.567415680676426, + "height": 2.8032978840147194, + "seed": 1285397932, + "groupIds": [ + "33LizGdDAL7lJNoiSiJZy" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1642714818537, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 16.832548902953302, + -2.8032978840147194 + ], + [ + 45.567415680676426, + -0.3275477042019195 + ] + ] + }, + { + "type": "line", + "version": 4272, + "versionNonce": 1958133548, + "isDeleted": false, + "id": "g62fLo1BCU_TuN8OEk_ie", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1278.4191037442736, + "y": 151.39581340204182, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 48.33668263438425, + "height": 4.280657518731036, + "seed": 198533908, + "groupIds": [ + "33LizGdDAL7lJNoiSiJZy" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1642714818537, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 26.41225578429045, + -0.2552319773002338 + ], + [ + 37.62000339651456, + 2.3153712935189787 + ], + [ + 48.33668263438425, + -1.9652862252120569 + ] + ] + }, + { + "type": "line", + "version": 4309, + "versionNonce": 1719212436, + "isDeleted": false, + "id": "jMbD-eUPMlCihQh7GK--c", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1275.2350565627432, + "y": 162.6389618103206, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 54.40694982784246, + "height": 2.9096445412231735, + "seed": 1358235692, + "groupIds": [ + "33LizGdDAL7lJNoiSiJZy" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1642714818537, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 10.166093050596771, + -1.166642430373031 + ], + [ + 16.130660965377448, + -0.8422655250909383 + ], + [ + 46.26079588567538, + 0.6125567455206506 + ], + [ + 54.40694982784246, + -2.297087795702523 + ] + ] + }, + { + "type": "line", + "version": 4274, + "versionNonce": 745404844, + "isDeleted": false, + "id": "lmzq_v_7Zzb2T4WxQPEqG", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1275.519899458309, + "y": 99.06204068494588, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 46.92865289294453, + "height": 2.4757501798128, + "seed": 719315092, + "groupIds": [ + "33LizGdDAL7lJNoiSiJZy" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1642714818537, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 18.193786115221407, + -0.5912874140789839 + ], + [ + 46.92865289294453, + 1.884462765733816 + ] + ] + }, + { + "type": "line", + "version": 4289, + "versionNonce": 348702484, + "isDeleted": false, + "id": "YoZdONxrJotpnXjxFe748", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1275.6273876544594, + "y": 133.54536972078546, + "strokeColor": "#00e676", + "backgroundColor": "#fff", + "width": 46.92865289294453, + "height": 2.4757501798128, + "seed": 1543185068, + "groupIds": [ + "33LizGdDAL7lJNoiSiJZy" + ], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1642714818537, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 8.093938105125233, + 1.4279702913643746 + ], + [ + 18.193786115221407, + -0.5912874140789839 + ], + [ + 46.92865289294453, + 1.884462765733816 + ] + ] + }, + { + "type": "text", + "version": 450, + "versionNonce": 240772140, + "isDeleted": false, + "id": "fAIYPmdTgnjEqc8PGu-SI", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1255.9645759420482, + "y": 203.9818965480822, + "strokeColor": "#00e676", + "backgroundColor": "#03a9f4", + "width": 134, + "height": 26, + "seed": 1891202964, + "groupIds": [], + "strokeSharpness": "round", + "boundElements": [], + "updated": 1642714818537, + "fontSize": 20, + "fontFamily": 1, + "text": "Output Data", + "baseline": 18, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Output Data" + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/images/task.png b/docs/images/task.png new file mode 100644 index 0000000..f8fbf8e Binary files /dev/null and b/docs/images/task.png differ diff --git a/docs/images/why-dark.png b/docs/images/why-dark.png new file mode 100644 index 0000000..b1aaa3f Binary files /dev/null and b/docs/images/why-dark.png differ diff --git a/docs/images/why.excalidraw b/docs/images/why.excalidraw new file mode 100644 index 0000000..4e26a21 --- /dev/null +++ b/docs/images/why.excalidraw @@ -0,0 +1,390 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 639, + "versionNonce": 1876686474, + "isDeleted": false, + "id": "mG5J3YEl3JUm8tWD2gLax", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 840.0920889678455, + "y": 323.8650942251963, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 412.0000000000001, + "height": 54.999999999999964, + "seed": 322487816, + "groupIds": [ + "VYua_NGvuS1eX_KPHCgOc" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1731084295790, + "index": "a4", + "frameId": null, + "roundness": null, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 210, + "versionNonce": 82579222, + "isDeleted": false, + "id": "N9ytCRthOA395FeHYtiWS", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1019.5920889678455, + "y": 335.8362480713501, + "strokeColor": "#343a40", + "backgroundColor": "#03a9f4", + "width": 53, + "height": 36, + "seed": 1396884744, + "groupIds": [ + "VYua_NGvuS1eX_KPHCgOc" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1731084295790, + "fontSize": 28, + "fontFamily": 1, + "text": "API", + "baseline": 25, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "API", + "index": "a5", + "frameId": null, + "roundness": null, + "link": null, + "locked": false, + "autoResize": true, + "lineHeight": 1.2857142857142858 + }, + { + "type": "rectangle", + "version": 460, + "versionNonce": 1551291798, + "isDeleted": false, + "id": "QKBksUpSSix31GSKFO1uV", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 837.5920889678456, + "y": 456.8747096098117, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 417, + "height": 60.846153846153825, + "seed": 910262792, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "4o-d8ZzuqEpB7ROBAyReL", + "type": "text" + } + ], + "updated": 1731084298697, + "index": "a6", + "frameId": null, + "roundness": null, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 263, + "versionNonce": 1011157002, + "isDeleted": false, + "id": "4o-d8ZzuqEpB7ROBAyReL", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 842.4670889678456, + "y": 469.2977865328886, + "strokeColor": "#343a40", + "backgroundColor": "#03a9f4", + "width": 407, + "height": 36, + "seed": 1820749832, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1731084298697, + "fontSize": 28, + "fontFamily": 1, + "text": "Embeddings", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "QKBksUpSSix31GSKFO1uV", + "originalText": "Embeddings", + "index": "a7", + "frameId": null, + "roundness": null, + "link": null, + "locked": false, + "autoResize": true, + "lineHeight": 1.2857142857142858 + }, + { + "type": "rectangle", + "version": 847, + "versionNonce": 266827407, + "isDeleted": false, + "id": "uFbzRXgEesWgll3llORrW", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 969.4670889678455, + "y": 387.5069211482732, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 135, + "height": 60.846153846153825, + "seed": 531948552, + "groupIds": [ + "DlS9eiqyrIHp45FmS35GN" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "text", + "id": "Ctud-Ap642NinH0-64fKT" + } + ], + "updated": 1731149308913, + "index": "a8", + "frameId": null, + "roundness": null, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 617, + "versionNonce": 1566326241, + "isDeleted": false, + "id": "Ctud-Ap642NinH0-64fKT", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 988.8004223011789, + "y": 399.9299980713501, + "strokeColor": "#000000", + "backgroundColor": "#03a9f4", + "width": 96.33333333333333, + "height": 36, + "seed": 720254072, + "groupIds": [ + "DlS9eiqyrIHp45FmS35GN" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1731149308913, + "fontSize": 28, + "fontFamily": 1, + "text": "Pipeline", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "uFbzRXgEesWgll3llORrW", + "originalText": "Pipeline", + "index": "a9", + "frameId": null, + "roundness": null, + "link": null, + "locked": false, + "autoResize": true, + "lineHeight": 1.2857142857142858 + }, + { + "type": "rectangle", + "version": 827, + "versionNonce": 1640467681, + "isDeleted": false, + "id": "_zQO_kyS5Pvq1zDhEn9hv", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1111.4863197370764, + "y": 388.1766044061918, + "strokeColor": "#ffeb3b", + "backgroundColor": "#ffeb3b", + "width": 141, + "height": 59.76470588235287, + "seed": 1988829960, + "groupIds": [ + "DlS9eiqyrIHp45FmS35GN" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "id": "tL_gw7MpliV1bDFJp8Cql", + "type": "text" + } + ], + "updated": 1731149323000, + "index": "aA", + "frameId": null, + "roundness": null, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 617, + "versionNonce": 1776634049, + "isDeleted": false, + "id": "tL_gw7MpliV1bDFJp8Cql", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1122.7113197370763, + "y": 400.0589573473683, + "strokeColor": "#000000", + "backgroundColor": "#03a9f4", + "width": 118.55, + "height": 36, + "seed": 274132856, + "groupIds": [ + "DlS9eiqyrIHp45FmS35GN" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1731149323001, + "fontSize": 28, + "fontFamily": 1, + "text": "Workflow", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "_zQO_kyS5Pvq1zDhEn9hv", + "originalText": "Workflow", + "index": "aB", + "frameId": null, + "roundness": null, + "link": null, + "locked": false, + "autoResize": true, + "lineHeight": 1.2857142857142858 + }, + { + "type": "rectangle", + "version": 910, + "versionNonce": 391148577, + "isDeleted": false, + "id": "STAkXjIBdAZgXMIKZfGnx", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 839.6978581986145, + "y": 387.38672884058093, + "strokeColor": "#9775fa", + "backgroundColor": "#9775fa", + "width": 121, + "height": 60.846153846153825, + "seed": 362222090, + "groupIds": [ + "DlS9eiqyrIHp45FmS35GN" + ], + "strokeSharpness": "sharp", + "boundElements": [ + { + "type": "text", + "id": "1iyFs-1PG5afe6QKq1s34" + } + ], + "updated": 1731149289560, + "index": "aC", + "frameId": null, + "roundness": null, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 690, + "versionNonce": 456727151, + "isDeleted": false, + "id": "1iyFs-1PG5afe6QKq1s34", + "fillStyle": "cross-hatch", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 861.8895238480286, + "y": 399.80980576365783, + "strokeColor": "#1e1e1e", + "backgroundColor": "#03a9f4", + "width": 76.61666870117188, + "height": 36, + "seed": 565985482, + "groupIds": [ + "DlS9eiqyrIHp45FmS35GN" + ], + "strokeSharpness": "sharp", + "boundElements": [], + "updated": 1731149289560, + "fontSize": 28, + "fontFamily": 1, + "text": "Agent", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "STAkXjIBdAZgXMIKZfGnx", + "originalText": "Agent", + "index": "aD", + "frameId": null, + "roundness": null, + "link": null, + "locked": false, + "autoResize": true, + "lineHeight": 1.2857142857142858 + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/images/why.png b/docs/images/why.png new file mode 100644 index 0000000..a0c1210 Binary files /dev/null and b/docs/images/why.png differ diff --git a/docs/images/workflow-dark.png b/docs/images/workflow-dark.png new file mode 100644 index 0000000..bae6c1a Binary files /dev/null and b/docs/images/workflow-dark.png differ diff --git a/docs/images/workflow.excalidraw b/docs/images/workflow.excalidraw new file mode 100644 index 0000000..9a01406 --- /dev/null +++ b/docs/images/workflow.excalidraw @@ -0,0 +1,759 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 1659, + "versionNonce": 1307774681, + "isDeleted": false, + "id": "qYd3q0Vjks7VOHUC9RR51", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 425, + "y": 339.5, + "strokeColor": "#00e676", + "backgroundColor": "#00e676", + "width": 290.0000000000001, + "height": 46, + "seed": 1441952427, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "WPeWn6N4rCHf0jY16N9Ge" + } + ], + "updated": 1673789051585, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 311, + "versionNonce": 924248664, + "isDeleted": false, + "id": "tgnQzXC9s8RY4oImBOXuB", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 562.5285999651802, + "y": 272.4578674923631, + "strokeColor": "#000", + "backgroundColor": "#228be6", + "width": 0.4714000348197942, + "height": 72.5421325076369, + "seed": 650463755, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1658678202111, + "link": null, + "locked": false, + "startBinding": { + "elementId": "B435ajoI5vAQBDvzkd8aY", + "focus": 0.05963733900881362, + "gap": 5.457867492363107 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0.4714000348197942, + 72.5421325076369 + ] + ] + }, + { + "type": "text", + "version": 1347, + "versionNonce": 162309943, + "isDeleted": false, + "id": "WPeWn6N4rCHf0jY16N9Ge", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 430, + "y": 344.5, + "strokeColor": "#000000", + "backgroundColor": "#fa5252", + "width": 280, + "height": 36, + "seed": 870516459, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1673789051585, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Translate", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "qYd3q0Vjks7VOHUC9RR51", + "originalText": "Translate" + }, + { + "type": "rectangle", + "version": 168, + "versionNonce": 2070244952, + "isDeleted": false, + "id": "IxMxusRKX2PpnJT1uY0cC", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 819, + "y": 217.5, + "strokeColor": "#ffeb3b", + "backgroundColor": "#ffeb3b", + "width": 290.0000000000001, + "height": 49, + "seed": 1364021803, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "SSXjX_URKvcVC8h-Qgz4j" + }, + { + "id": "_YNRYTAnVzdhhKxFtkVyg", + "type": "arrow" + } + ], + "updated": 1658678121633, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 73, + "versionNonce": 1790001960, + "isDeleted": false, + "id": "SSXjX_URKvcVC8h-Qgz4j", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 824, + "y": 224, + "strokeColor": "#000000", + "backgroundColor": "#82c91e", + "width": 280, + "height": 36, + "seed": 948915563, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1658678121633, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Extract Text", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "IxMxusRKX2PpnJT1uY0cC", + "originalText": "Extract Text" + }, + { + "type": "rectangle", + "version": 205, + "versionNonce": 1180033880, + "isDeleted": false, + "id": "lATIKISgJPOGnUHleuFRH", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 821, + "y": 338, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 290.0000000000001, + "height": 46, + "seed": 1256311595, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "21WiUuyDtpQ9FnJ74REpJ", + "type": "text" + }, + { + "id": "Q51e0Hav-kYfjX3b8tt-r", + "type": "arrow" + } + ], + "updated": 1658678121633, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 84, + "versionNonce": 1559141928, + "isDeleted": false, + "id": "21WiUuyDtpQ9FnJ74REpJ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 826, + "y": 343, + "strokeColor": "#000000", + "backgroundColor": "#fa5252", + "width": 280, + "height": 36, + "seed": 626266373, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1658678121633, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Summarize", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "lATIKISgJPOGnUHleuFRH", + "originalText": "Summarize" + }, + { + "type": "arrow", + "version": 150, + "versionNonce": 108519256, + "isDeleted": false, + "id": "_YNRYTAnVzdhhKxFtkVyg", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 962.2340210300499, + "y": 270, + "strokeColor": "#000", + "backgroundColor": "#228be6", + "width": 0.2659789699500834, + "height": 75, + "seed": 101249451, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1658678205029, + "link": null, + "locked": false, + "startBinding": { + "elementId": "IxMxusRKX2PpnJT1uY0cC", + "focus": 0.012856281024868153, + "gap": 3.5 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0.2659789699500834, + 75 + ] + ] + }, + { + "type": "rectangle", + "version": 213, + "versionNonce": 1959303464, + "isDeleted": false, + "id": "6B3J0wJfi561c9gMsgtXG", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 824, + "y": 455, + "strokeColor": "#7950f2", + "backgroundColor": "#7950f2", + "width": 290.0000000000001, + "height": 46, + "seed": 1164190923, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "qJDqmDEZF9Ewh2UrjXq5Z", + "type": "text" + }, + { + "id": "Q51e0Hav-kYfjX3b8tt-r", + "type": "arrow" + } + ], + "updated": 1658678121634, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 135, + "versionNonce": 210169176, + "isDeleted": false, + "id": "qJDqmDEZF9Ewh2UrjXq5Z", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 829, + "y": 460, + "strokeColor": "#000000", + "backgroundColor": "#fa5252", + "width": 280, + "height": 36, + "seed": 1307943269, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1658678121634, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Build Vector Index", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "6B3J0wJfi561c9gMsgtXG", + "originalText": "Build Vector Index" + }, + { + "type": "arrow", + "version": 203, + "versionNonce": 133313624, + "isDeleted": false, + "id": "Q51e0Hav-kYfjX3b8tt-r", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 963.600775377322, + "y": 387.5, + "strokeColor": "#000", + "backgroundColor": "#228be6", + "width": 0.29392358842710564, + "height": 66.5, + "seed": 59173285, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1658678208037, + "link": null, + "locked": false, + "startBinding": { + "elementId": "lATIKISgJPOGnUHleuFRH", + "focus": 0.015253485349599789, + "gap": 3.5 + }, + "endBinding": { + "elementId": "6B3J0wJfi561c9gMsgtXG", + "focus": -0.039966641220930875, + "gap": 1 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -0.29392358842710564, + 66.5 + ] + ] + }, + { + "type": "rectangle", + "version": 1246, + "versionNonce": 842910649, + "isDeleted": false, + "id": "5VuUdI_BsJ5pyE1nTqJUI", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1195, + "y": 221, + "strokeColor": "#7950f2", + "backgroundColor": "#7950f2", + "width": 290.0000000000001, + "height": 46, + "seed": 1044404613, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "bJJ9SGsJsvT071qBBH0w5", + "type": "text" + }, + { + "id": "Q51e0Hav-kYfjX3b8tt-r", + "type": "arrow" + } + ], + "updated": 1673789051585, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1431, + "versionNonce": 1914742871, + "isDeleted": false, + "id": "bJJ9SGsJsvT071qBBH0w5", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1200, + "y": 226, + "strokeColor": "#000000", + "backgroundColor": "#fa5252", + "width": 280, + "height": 36, + "seed": 128953675, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1673789051585, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Run similarity query", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "5VuUdI_BsJ5pyE1nTqJUI", + "originalText": "Run similarity query" + }, + { + "type": "rectangle", + "version": 334, + "versionNonce": 7828646, + "isDeleted": false, + "id": "pSbgtf1qAB7tWl-pTld7e", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1197, + "y": 343, + "strokeColor": "#ff7043", + "backgroundColor": "#ff7043", + "width": 290.0000000000001, + "height": 46, + "seed": 210689733, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "pQzKSM3audka1kiQm_Sku", + "type": "text" + }, + { + "id": "Q51e0Hav-kYfjX3b8tt-r", + "type": "arrow" + }, + { + "id": "k67YzXNtt1GLh4i8Es5zJ", + "type": "arrow" + } + ], + "updated": 1673791639562, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 293, + "versionNonce": 271822438, + "isDeleted": false, + "id": "pQzKSM3audka1kiQm_Sku", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1219.5, + "y": 348, + "strokeColor": "#000", + "backgroundColor": "#fa5252", + "width": 245, + "height": 36, + "seed": 1869028363, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1673791644235, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Send notifications", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "pSbgtf1qAB7tWl-pTld7e", + "originalText": "Send notifications" + }, + { + "type": "arrow", + "version": 310, + "versionNonce": 1574300026, + "isDeleted": false, + "id": "k67YzXNtt1GLh4i8Es5zJ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1339.1171359117898, + "y": 265.5, + "strokeColor": "#000", + "backgroundColor": "#228be6", + "width": 1.0119492860442278, + "height": 76.5, + "seed": 1656816747, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1673791641098, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "pSbgtf1qAB7tWl-pTld7e", + "focus": -0.010690950588675632, + "gap": 1 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 1.0119492860442278, + 76.5 + ] + ] + }, + { + "type": "text", + "version": 308, + "versionNonce": 641647912, + "isDeleted": false, + "id": "kKPPLMCj8QQIJLbW-B5hm", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1180, + "y": 451, + "strokeColor": "#000", + "backgroundColor": "#fab005", + "width": 296, + "height": 52, + "seed": 1079990731, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1658678121635, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "- API bindings for JavaScript,\n Rust, Go and Java", + "baseline": 44, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- API bindings for JavaScript,\n Rust, Go and Java" + }, + { + "type": "text", + "version": 560, + "versionNonce": 1977188696, + "isDeleted": false, + "id": "1AQ3rj-V4weRtPA8a5-z-", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 437.5, + "y": 449.5, + "strokeColor": "#000", + "backgroundColor": "#fab005", + "width": 278, + "height": 52, + "seed": 836512075, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1658678121635, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "- Build with Python or YAML\n- Run local or via API", + "baseline": 44, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "- Build with Python or YAML\n- Run local or via API" + }, + { + "type": "rectangle", + "version": 292, + "versionNonce": 259776552, + "isDeleted": false, + "id": "B435ajoI5vAQBDvzkd8aY", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 426, + "y": 221, + "strokeColor": "#03a9f4", + "backgroundColor": "#03a9f4", + "width": 290.0000000000001, + "height": 46, + "seed": 942981672, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "AoGnxEHn4x-zq2-0C0VrT", + "type": "text" + }, + { + "id": "Q51e0Hav-kYfjX3b8tt-r", + "type": "arrow" + }, + { + "id": "tgnQzXC9s8RY4oImBOXuB", + "type": "arrow" + } + ], + "updated": 1658678121635, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 171, + "versionNonce": 2093287976, + "isDeleted": false, + "id": "AoGnxEHn4x-zq2-0C0VrT", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 431, + "y": 226, + "strokeColor": "#000000", + "backgroundColor": "#fa5252", + "width": 280, + "height": 36, + "seed": 604886104, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1658678188284, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "Summarize", + "baseline": 25, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "B435ajoI5vAQBDvzkd8aY", + "originalText": "Summarize" + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#3030" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/images/workflow.png b/docs/images/workflow.png new file mode 100644 index 0000000..37741fb Binary files /dev/null and b/docs/images/workflow.png differ diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..ef2804e --- /dev/null +++ b/docs/index.md @@ -0,0 +1,60 @@ +# + +

+ +

+ +

+ All-in-one AI framework +

+ +

+ + Version + + + GitHub last commit + + + GitHub issues + + + Join Slack + + + Build Status + + + Coverage Status + +

+ +txtai is an all-in-one AI framework for semantic search, LLM orchestration and language model workflows. + +![architecture](images/architecture.png#gh-light-mode-only) +![architecture](images/architecture-dark.png#gh-dark-mode-only) + +The key component of txtai is an embeddings database, which is a union of vector indexes (sparse and dense), graph networks and relational databases. + +This foundation enables vector search and/or serves as a powerful knowledge source for large language model (LLM) applications. + +Build autonomous agents, retrieval augmented generation (RAG) processes, multi-model workflows and more. + +Summary of txtai features: + +- 🔎 Vector search with SQL, object storage, topic modeling, graph analysis and multimodal indexing +- 📄 Create embeddings for text, documents, audio, images and video +- 💡 Pipelines powered by language models that run LLM prompts, question-answering, labeling, transcription, translation, summarization and more +- ↪️️ Workflows to join pipelines together and aggregate business logic. txtai processes can be simple microservices or multi-model workflows. +- 🤖 Agents that intelligently connect embeddings, pipelines, workflows and other agents together to autonomously solve complex problems +- ⚙️ Web and Model Context Protocol (MCP) APIs. Bindings available for [JavaScript](https://github.com/neuml/txtai.js), [Java](https://github.com/neuml/txtai.java), [Rust](https://github.com/neuml/txtai.rs) and [Go](https://github.com/neuml/txtai.go). +- 🔋 Batteries included with defaults to get up and running fast +- ☁️ Run local or scale out with container orchestration + +txtai is built with Python 3.10+, [Hugging Face Transformers](https://github.com/huggingface/transformers), [Sentence Transformers](https://github.com/UKPLab/sentence-transformers) and [FastAPI](https://github.com/tiangolo/fastapi). txtai is open-source under an Apache 2.0 license. + +!!! note + + [NeuML](https://neuml.com) is the company behind txtai and we provide AI consulting services around our stack. [Schedule a meeting](https://cal.com/neuml/intro) or [send a message](mailto:info@neuml.com) to learn more. + + We're also building an easy and secure way to run hosted txtai applications with [txtai.cloud](https://txtai.cloud). diff --git a/docs/install.md b/docs/install.md new file mode 100644 index 0000000..01e703c --- /dev/null +++ b/docs/install.md @@ -0,0 +1,198 @@ +# Installation + +![install](images/install.png#only-light) +![install](images/install-dark.png#only-dark) + +The easiest way to install is via pip and PyPI + +``` +pip install txtai +``` + +Python 3.10+ is supported. Using a Python [virtual environment](https://docs.python.org/3/library/venv.html) is recommended. + +## Optional dependencies + +txtai has the following optional dependencies that can be installed as extras. The patterns below are supported +in setup.py install_requires sections. + +_Note: Extras are provided for convenience. Alternatively, individual packages can be installed to limit dependencies._ + +### All + +Install all dependencies. + +``` +pip install txtai[all] +``` + +### ANN + +Additional ANN backends. + +``` +pip install txtai[ann] +``` + +### API + +Serve txtai via a web API. + +``` +pip install txtai[api] +``` + +### Cloud + +Interface with cloud compute. + +``` +pip install txtai[cloud] +``` + +### Console + +Command line index query console. + +``` +pip install txtai[console] +``` + +### Database + +Additional content storage options. + +``` +pip install txtai[database] +``` + +### Graph + +Topic modeling, data connectivity and network analysis. + +``` +pip install txtai[graph] +``` + +### Model + +Additional non-standard models. + +``` +pip install txtai[model] +``` + +### Pipeline + +All pipelines - default install comes with most common pipelines. + +``` +pip install txtai[pipeline] +``` + +More granular extras are available for pipeline categories: `pipeline-audio`, `pipeline-data`, `pipeline-image`, `pipeline-llm`, `pipeline-text`, and `pipeline-train`. + +### Scoring + +Additional scoring methods. + +``` +pip install txtai[scoring] +``` + +### Vectors + +Additional vector methods. + +``` +pip install txtai[vectors] +``` + +### Workflow + +All workflow tasks - default install comes with most common workflow tasks. + +``` +pip install txtai[workflow] +``` + +### Combining dependencies + +Multiple dependencies can be specified at the same time. + +``` +pip install txtai[pipeline,workflow] +``` + +## Environment specific prerequisites + +Additional environment specific prerequisites are below. + +### Linux + +The AudioStream and Microphone pipelines require the [PortAudio](https://python-sounddevice.readthedocs.io/en/0.5.0/installation.html) system library. + +The Transcription pipeline requires the [SoundFile](https://github.com/bastibe/python-soundfile#installation) system library. + +The LiteRT LLM pipeline requires libegl1, libgles2 and libvulkan1. + +### macOS + +Older versions of Faiss have a runtime dependency on `libomp` for macOS. Run `brew install libomp` in this case. + +The AudioStream and Microphone pipelines require the [PortAudio](https://python-sounddevice.readthedocs.io/en/0.5.0/installation.html) system library. Run `brew install portaudio`. + +### Windows + +Optional dependencies require [C++ Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/) + +The [txtai build workflow](https://github.com/neuml/txtai/blob/master/.github/workflows/build.yml) occasionally has work arounds for other known but temporary dependency issues. The [FAQ](../faq) also has a list of common problems, including common installation issues. + +## CPU-only + +The default install adds PyTorch with GPU support. There are a number of dependencies that come with that. When running in a CPU-only environment, the CPU-only PyTorch package can be installed with txtai as follows. + +``` +pip install txtai torch==[version]+cpu \ +-f https://download.pytorch.org/whl/torch +``` + +Where `[version]` is the version of PyTorch (such as 2.4.1). The [txtai-cpu](https://hub.docker.com/r/neuml/txtai-cpu) image on Docker Hub uses this method to reduce the image size. + +## Install from source + +txtai can also be installed directly from GitHub to access the latest, unreleased features. + +``` +pip install git+https://github.com/neuml/txtai +``` + +Extras can be installed from GitHub by adding `#egg=txtai[]` to the end of the above URL. + +## Conda + +A [community-supported txtai package](https://anaconda.org/conda-forge/txtai) is available via conda-forge. + +``` +conda install -c conda-forge txtai +``` + +## Minimal install + +A lightweight zero dependency minimal install package is available. This is helpful if the default libraries are too heavy or there are no plans to use them. + +All the same extras mentioned above are available in this version of the package. + +``` +# Lightweight minimal install +pip install txtai_minimal + +# Same as standard `txtai` install +pip install txtai_minimal[default] +``` + +Note that in order to use the `Embeddings` or `LLM` interface without `torch` requires either `llama.cpp`, `litellm` or `litert`. + +## Run with containers + +Docker images are available for txtai. [See this section](../cloud) for more information on container-based installs. diff --git a/docs/models.md b/docs/models.md new file mode 100644 index 0000000..66eeeca --- /dev/null +++ b/docs/models.md @@ -0,0 +1,26 @@ +# Model guide + +![models](images/models.png) + +See the table below for the current recommended models. These models all allow commercial use and offer a blend of speed and performance. + +| Component | Model(s) | +| ---------------------------------------------------- | ------------------------------------------------------------------------ | +| [Embeddings](../embeddings) | [all-MiniLM-L6-v2](https://hf.co/sentence-transformers/all-MiniLM-L6-v2) | +| [Image Captions](./pipeline/image/caption.md) | [BLIP](https://hf.co/Salesforce/blip-image-captioning-base) | +| [Labels - Zero Shot](./pipeline/text/labels.md) | [DeBERTa v3 Zeroshot](https://hf.co/MoritzLaurer/deberta-v3-base-zeroshot-v2.0-c) | +| [Labels - Fixed](./pipeline/text/labels.md) | Fine-tune with [training pipeline](./pipeline/train/trainer.md) | +| [Large Language Model (LLM)](./pipeline/text/llm.md) | [Gemma 4 31B](https://hf.co/google/gemma-4-31B) | +| [Summarization](./pipeline/text/summary.md) | [DistilBART](https://hf.co/sshleifer/distilbart-cnn-12-6) | +| [Text-to-Speech](./pipeline/audio/texttospeech.md) | [ESPnet JETS](https://hf.co/NeuML/ljspeech-jets-onnx) | +| [Transcription](./pipeline/audio/transcription.md) | [Whisper](https://hf.co/openai/whisper-base) | +| [Translation](./pipeline/text/translation.md) | [OPUS Model Series](https://hf.co/Helsinki-NLP) | + +Models can be loaded as either a path from the Hugging Face Hub or a local directory. Model paths are optional, defaults are loaded when not specified. For tasks with no recommended model, txtai uses the default models as shown in the Hugging Face Tasks guide. + +See the following links to learn more. + +- [Hugging Face Tasks](https://hf.co/tasks) +- [Hugging Face Model Hub](https://hf.co/models) +- [Embeddings Leaderboard](https://hf.co/spaces/mteb/leaderboard) +- [LLM Leaderboard](https://hf.co/spaces/lmarena-ai/arena-leaderboard) diff --git a/docs/observability.md b/docs/observability.md new file mode 100644 index 0000000..29e1322 --- /dev/null +++ b/docs/observability.md @@ -0,0 +1,182 @@ +# Observability + +![agent](https://raw.githubusercontent.com/neuml/mlflow-txtai/master/images/agent.png) + +Observability enables tracking the inner workings of a system without having to change the system. This makes it much easier to debug and evaluate overall performance. + +`txtai` has an integration with [MLflow](https://mlflow.org) and it's [tracing module](https://mlflow.org/docs/latest/llms/tracing/index.html) to provide insights into each of the components in `txtai`. + +## Examples + +The following shows a number of examples on how to introduce observability into a `txtai` process. + +### Initialization + +Run the following sections first to initialize tracing. + +``` +# Install MLflow plugin for txtai +pip install mlflow-txtai + +# Start a local MLflow service +mlflow server --host 127.0.0.1 --port 8000 +``` + +```python +import mlflow + +mlflow.set_tracking_uri(uri="http://localhost:8000") +mlflow.set_experiment("txtai") + +# Enable txtai automatic tracing +mlflow.txtai.autolog() +``` + +### Textractor + +The first example traces a [Textractor pipeline](../pipeline/data/textractor). + +```python +from txtai.pipeline import Textractor + +with mlflow.start_run(): + textractor = Textractor() + textractor("https://github.com/neuml/txtai") +``` + +![textractor](https://raw.githubusercontent.com/neuml/mlflow-txtai/master/images/textractor.png) + +### Embeddings + +Next, we'll trace an [Embeddings](../embeddings) query. + +```python +from txtai import Embeddings + +with mlflow.start_run(): + wiki = Embeddings() + wiki.load(provider="huggingface-hub", container="neuml/txtai-wikipedia-slim") + + embeddings = Embeddings(content=True, graph=True) + embeddings.index(wiki.search("SELECT id, text FROM txtai LIMIT 25")) + + embeddings.search("MATCH (A)-[]->(B) RETURN A") +``` + +![embeddings-load](https://raw.githubusercontent.com/neuml/mlflow-txtai/master/images/embeddings-load.png) +![embeddings-index](https://raw.githubusercontent.com/neuml/mlflow-txtai/master/images/embeddings-index.png) + +### Retrieval Augmented Generation (RAG) + +The next example traces a [RAG pipeline](../pipeline/text/rag). + +```python +from txtai import Embeddings, RAG + +with mlflow.start_run(): + wiki = Embeddings() + wiki.load(provider="huggingface-hub", container="neuml/txtai-wikipedia-slim") + + # Define prompt template + template = """ + Answer the following question using only the context below. Only include information + specifically discussed. + + question: {question} + context: {context} """ + + # Create RAG pipeline + rag = RAG( + wiki, + "openai/gpt-oss-20b", + system="You are a friendly assistant. You answer questions from users.", + template=template, + context=10 + ) + + rag("Tell me about the Roman Empire", maxlength=2048) +``` + +![rag](https://raw.githubusercontent.com/neuml/mlflow-txtai/master/images/rag.png) + +### Workflow + +This example runs a [workflow](../workflow). This workflow runs an embeddings query and then translates each result to French. + +```python +from txtai import Embeddings, Workflow +from txtai.pipeline import Translation +from txtai.workflow import Task + +with mlflow.start_run(): + wiki = Embeddings() + wiki.load(provider="huggingface-hub", container="neuml/txtai-wikipedia-slim") + + # Translation instance + translate = Translation() + + workflow = Workflow([ + Task(lambda x: [y[0]["text"] for y in wiki.batchsearch(x, 1)]), + Task(lambda x: translate(x, "fr")) + ]) + + print(list(workflow(["Roman Empire", "Greek Empire", "Industrial Revolution"]))) +``` + +![workflow](https://raw.githubusercontent.com/neuml/mlflow-txtai/master/images/workflow.png) + +### Agent + +The last example runs a [txtai agent](../agent) designed to research questions on astronomy. + +```python +from txtai import Agent, Embeddings + +def search(query): + """ + Searches a database of astronomy data. + + Make sure to call this tool only with a string input, never use JSON. + + Args: + query: concepts to search for using similarity search + + Returns: + list of search results with for each match + """ + + return embeddings.search( + "SELECT id, text, distance FROM txtai WHERE similar(:query)", + 10, parameters={"query": query} + ) + +embeddings = Embeddings() +embeddings.load(provider="huggingface-hub", container="neuml/txtai-astronomy") + +agent = Agent( + tools=[search], + llm="Qwen/Qwen3-4B-Instruct-2507", + max_steps=10, +) + +researcher = """ +{command} + +Do the following. + - Search for results related to the topic. + - Analyze the results + - Continue querying until conclusive answers are found + - Write a Markdown report +""" + +with mlflow.start_run(): + agent(researcher.format(command=""" + Write a detailed list with explanations of 10 candidate stars that could potentially be habitable to life. + """), maxlength=16000) +``` + +![agent](https://raw.githubusercontent.com/neuml/mlflow-txtai/master/images/agent.png) + +## Read more + +Check out the [mlflow-txtai](https://github.com/neuml/mlflow-txtai) project to see more examples. diff --git a/docs/overrides/main.html b/docs/overrides/main.html new file mode 100644 index 0000000..fa5a2f4 --- /dev/null +++ b/docs/overrides/main.html @@ -0,0 +1,24 @@ +{% extends "base.html" %} + +{% block extrahead %} + {% set title = config.site_name %} + {% if page and page.meta and page.meta.title %} + {% set title = title ~ " - " ~ page.meta.title %} + {% elif page and page.title and not page.is_homepage %} + {% set title = title ~ " - " ~ page.title %} + {% endif %} + + + + + + + + + + + + + + +{% endblock %} diff --git a/docs/pipeline/audio/audiomixer.md b/docs/pipeline/audio/audiomixer.md new file mode 100644 index 0000000..5cbaf30 --- /dev/null +++ b/docs/pipeline/audio/audiomixer.md @@ -0,0 +1,68 @@ +# Audio Mixer + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The Audio Mixer pipeline mixes multiple audio streams into a single stream. + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai.pipeline import AudioMixer + +# Create and run pipeline +mixer = AudioMixer() +mixer(((audio1, rate1), (audio2, rate2))) +``` + +See the link below for a more detailed example. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Generative Audio](https://github.com/neuml/txtai/blob/master/examples/66_Generative_Audio.ipynb) | Storytelling with generative audio workflows | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/66_Generative_Audio.ipynb) | + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +# Create pipeline using lower case class name +audiomixer: + +# Run pipeline with workflow +workflow: + audiomixer: + tasks: + - action: audiomixer +``` + +### Run with Workflows + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +list(app.workflow("audiomixer", [[[audio1, rate1], [audio2, rate2]]])) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name":"audiomixer", "elements":[[[audio1, rate1], [audio2, rate2]]]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.AudioMixer.__init__ +### ::: txtai.pipeline.AudioMixer.__call__ diff --git a/docs/pipeline/audio/audiostream.md b/docs/pipeline/audio/audiostream.md new file mode 100644 index 0000000..5eadb04 --- /dev/null +++ b/docs/pipeline/audio/audiostream.md @@ -0,0 +1,70 @@ +# Audio Stream + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The Audio Stream pipeline is a threaded pipeline that plays audio segments. This pipeline is designed to run on local machines given that it requires access to write to an output device. + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai.pipeline import AudioStream + +# Create and run pipeline +audio = AudioStream() +audio(data) +``` + +This pipeline may require additional system dependencies. See [this section](../../../install#environment-specific-prerequisites) for more. + +See the link below for a more detailed example. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Speech to Speech RAG](https://github.com/neuml/txtai/blob/master/examples/65_Speech_to_Speech_RAG.ipynb) [▶️](https://www.youtube.com/watch?v=tH8QWwkVMKA) | Full cycle speech to speech workflow with RAG | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/65_Speech_to_Speech_RAG.ipynb) | + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +# Create pipeline using lower case class name +audiostream: + +# Run pipeline with workflow +workflow: + audiostream: + tasks: + - action: audiostream +``` + +### Run with Workflows + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +list(app.workflow("audiostream", [["numpy data", "sample rate"]])) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name":"audiostream", "elements":[["numpy data", "sample rate"]]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.AudioStream.__init__ +### ::: txtai.pipeline.AudioStream.__call__ diff --git a/docs/pipeline/audio/microphone.md b/docs/pipeline/audio/microphone.md new file mode 100644 index 0000000..8a2cf16 --- /dev/null +++ b/docs/pipeline/audio/microphone.md @@ -0,0 +1,70 @@ +# Microphone + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The Microphone pipeline reads input speech from a microphone device. This pipeline is designed to run on local machines given that it requires access to read from an input device. + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai.pipeline import Microphone + +# Create and run pipeline +microphone = Microphone() +microphone() +``` + +This pipeline may require additional system dependencies. See [this section](../../../install#environment-specific-prerequisites) for more. + +See the link below for a more detailed example. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Speech to Speech RAG](https://github.com/neuml/txtai/blob/master/examples/65_Speech_to_Speech_RAG.ipynb) [▶️](https://www.youtube.com/watch?v=tH8QWwkVMKA) | Full cycle speech to speech workflow with RAG | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/65_Speech_to_Speech_RAG.ipynb) | + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +# Create pipeline using lower case class name +microphone: + +# Run pipeline with workflow +workflow: + microphone: + tasks: + - action: microphone +``` + +### Run with Workflows + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +list(app.workflow("microphone", ["1"])) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name":"microphone", "elements":["1"]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.Microphone.__init__ +### ::: txtai.pipeline.Microphone.__call__ diff --git a/docs/pipeline/audio/texttoaudio.md b/docs/pipeline/audio/texttoaudio.md new file mode 100644 index 0000000..6d76b21 --- /dev/null +++ b/docs/pipeline/audio/texttoaudio.md @@ -0,0 +1,68 @@ +# Text To Audio + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The Text To Audio pipeline generates audio from text. + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai.pipeline import TextToAudio + +# Create and run pipeline +tta = TextToAudio() +tta("Describe the audio to generate here") +``` + +See the link below for a more detailed example. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Generative Audio](https://github.com/neuml/txtai/blob/master/examples/66_Generative_Audio.ipynb) | Storytelling with generative audio workflows | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/66_Generative_Audio.ipynb) | + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +# Create pipeline using lower case class name +texttoaudio: + +# Run pipeline with workflow +workflow: + tta: + tasks: + - action: texttoaudio +``` + +### Run with Workflows + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +list(app.workflow("tta", ["Describe the audio to generate here"])) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name":"tta", "elements":["Describe the audio to generate here"]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.TextToAudio.__init__ +### ::: txtai.pipeline.TextToAudio.__call__ diff --git a/docs/pipeline/audio/texttospeech.md b/docs/pipeline/audio/texttospeech.md new file mode 100644 index 0000000..39e8a13 --- /dev/null +++ b/docs/pipeline/audio/texttospeech.md @@ -0,0 +1,92 @@ +# Text To Speech + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The Text To Speech pipeline generates speech from text. + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai.pipeline import TextToSpeech + +# Create and run pipeline with default model +tts = TextToSpeech() +tts("Say something here") + +# Stream audio - incrementally generates snippets of audio +yield from tts( + "Say something here. And say something else.".split(), + stream=True +) + +# Generate audio using a speaker id +tts = TextToSpeech("neuml/vctk-vits-onnx") +tts("Say something here", speaker=15) + +# Generate audio using speaker embeddings +tts = TextToSpeech("neuml/txtai-speecht5-onnx") +tts("Say something here", speaker=np.array(...)) +``` + +See the links below for a more detailed example. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Text to speech generation](https://github.com/neuml/txtai/blob/master/examples/40_Text_to_Speech_Generation.ipynb) | Generate speech from text | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/40_Text_to_Speech_Generation.ipynb) | +| [Speech to Speech RAG](https://github.com/neuml/txtai/blob/master/examples/65_Speech_to_Speech_RAG.ipynb) [▶️](https://www.youtube.com/watch?v=tH8QWwkVMKA) | Full cycle speech to speech workflow with RAG | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/65_Speech_to_Speech_RAG.ipynb) | +| [Generative Audio](https://github.com/neuml/txtai/blob/master/examples/66_Generative_Audio.ipynb) | Storytelling with generative audio workflows | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/66_Generative_Audio.ipynb) | + +This pipeline is backed by ONNX models from the Hugging Face Hub. The following models are currently available. + +- [kokoro-base-onnx](https://huggingface.co/NeuML/kokoro-base-onnx) | [fp16](https://huggingface.co/NeuML/kokoro-fp16-onnx) | [int8](https://huggingface.co/NeuML/kokoro-int8-onnx) +- [ljspeech-jets-onnx](https://huggingface.co/NeuML/ljspeech-jets-onnx) +- [ljspeech-vits-onnx](https://huggingface.co/NeuML/ljspeech-vits-onnx) +- [vctk-vits-onnx](https://huggingface.co/NeuML/vctk-vits-onnx) +- [txtai-speecht5-onnx](https://huggingface.co/NeuML/txtai-speecht5-onnx) + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +# Create pipeline using lower case class name +texttospeech: + +# Run pipeline with workflow +workflow: + tts: + tasks: + - action: texttospeech +``` + +### Run with Workflows + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +list(app.workflow("tts", ["Say something here"])) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name":"tts", "elements":["Say something here"]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.TextToSpeech.__init__ +### ::: txtai.pipeline.TextToSpeech.__call__ diff --git a/docs/pipeline/audio/transcription.md b/docs/pipeline/audio/transcription.md new file mode 100644 index 0000000..ebc8888 --- /dev/null +++ b/docs/pipeline/audio/transcription.md @@ -0,0 +1,71 @@ +# Transcription + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The Transcription pipeline converts speech in audio files to text. + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai.pipeline import Transcription + +# Create and run pipeline +transcribe = Transcription() +transcribe("path to wav file") +``` + +This pipeline may require additional system dependencies. See [this section](../../../install#environment-specific-prerequisites) for more. + +See the links below for a more detailed example. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Transcribe audio to text](https://github.com/neuml/txtai/blob/master/examples/11_Transcribe_audio_to_text.ipynb) | Convert audio files to text | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/11_Transcribe_audio_to_text.ipynb) | +| [Speech to Speech RAG](https://github.com/neuml/txtai/blob/master/examples/65_Speech_to_Speech_RAG.ipynb) [▶️](https://www.youtube.com/watch?v=tH8QWwkVMKA) | Full cycle speech to speech workflow with RAG | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/65_Speech_to_Speech_RAG.ipynb) | + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +# Create pipeline using lower case class name +transcription: + +# Run pipeline with workflow +workflow: + transcribe: + tasks: + - action: transcription +``` + +### Run with Workflows + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +list(app.workflow("transcribe", ["path to wav file"])) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name":"transcribe", "elements":["path to wav file"]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.Transcription.__init__ +### ::: txtai.pipeline.Transcription.__call__ diff --git a/docs/pipeline/data/filetohtml.md b/docs/pipeline/data/filetohtml.md new file mode 100644 index 0000000..600389b --- /dev/null +++ b/docs/pipeline/data/filetohtml.md @@ -0,0 +1,80 @@ +# File To HTML + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The File To HTML pipeline transforms files to HTML. It supports the following text extraction backends. + +## Apache Tika + +[Apache Tika](https://tika.apache.org/) detects and extracts metadata and text from over a thousand different file types. See [this link](https://tika.apache.org/2.9.2/formats.html) for a list of supported document formats. + +Apache Tika requires [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) to be installed. An alternative to that is starting a separate Apache Tika service via [this Docker Image](https://hub.docker.com/r/apache/tika) and setting these [environment variables](https://github.com/chrismattmann/tika-python?tab=readme-ov-file#environment-variables). + +## Docling + +[Docling](https://github.com/DS4SD/docling) parses documents and exports them to the desired format with ease and speed. This is a library that has rapidly gained popularity starting in late 2024. Docling excels in parsing formatting elements from PDFs (tables, sections etc). + +See [this link](https://github.com/DS4SD/docling?tab=readme-ov-file#features) for a list of supported document formats. + +## LiteParse + +[LiteParse](https://github.com/run-llama/liteparse) is a fast, helpful, and open-source document parser. It supports PDF out of the box with additional format support via third-party packages. + +See [this link](https://github.com/run-llama/liteparse#multi-format-input-support) for more. + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai.pipeline import FileToHTML + +# Create and run pipeline +html = FileToHTML() +html("/path/to/file") +``` + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +# Create pipeline using lower case class name +filetohtml: + +# Run pipeline with workflow +workflow: + html: + tasks: + - action: filetohtml +``` + +### Run with Workflows + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +list(app.workflow("html", ["/path/to/file"])) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name":"html", "elements":["/path/to/file"]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.FileToHTML.__init__ +### ::: txtai.pipeline.FileToHTML.__call__ diff --git a/docs/pipeline/data/htmltomd.md b/docs/pipeline/data/htmltomd.md new file mode 100644 index 0000000..b4e06a3 --- /dev/null +++ b/docs/pipeline/data/htmltomd.md @@ -0,0 +1,68 @@ +# HTML To Markdown + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The HTML To Markdown pipeline transforms HTML to Markdown. + +Markdown formatting is applied for headings, blockquotes, lists, code, tables and text. Visual formatting is also included (bold, italic etc). + +This pipeline searches for the best node that has relevant text, often found with an `article`, `main` or `body` tag. + +The HTML to Markdown pipeline requires the [BeautifulSoup4](https://pypi.org/project/beautifulsoup4/) library to be installed. + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai.pipeline import HTMLToMarkdown + +# Create and run pipeline +md = HTMLToMarkdown() +md("This is a test") +``` + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +# Create pipeline using lower case class name +htmltomarkdown: + +# Run pipeline with workflow +workflow: + markdown: + tasks: + - action: htmltomarkdown +``` + +### Run with Workflows + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +list(app.workflow("markdown", ["This is a test"])) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name":"markdown", "elements":["This is a test"]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.HTMLToMarkdown.__init__ +### ::: txtai.pipeline.HTMLToMarkdown.__call__ diff --git a/docs/pipeline/data/segmentation.md b/docs/pipeline/data/segmentation.md new file mode 100644 index 0000000..7e1bae6 --- /dev/null +++ b/docs/pipeline/data/segmentation.md @@ -0,0 +1,69 @@ +# Segmentation + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The Segmentation pipeline segments text into semantic units. + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai.pipeline import Segmentation + +# Create and run pipeline +segment = Segmentation(sentences=True) +segment("This is a test. And another test.") + +# Load third-party chunkers +segment = Segmentation(chunker="semantic") +segment("This is a test. And another test.") +``` + +The Segmentation pipeline supports segmenting `sentences`, `lines`, `paragraphs` and `sections` using a rules-based approach. Each of these modes can be set when creating the pipeline. Third-party chunkers are also supported via the `chunker` parameter. + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +# Create pipeline using lower case class name +segmentation: + sentences: true + +# Run pipeline with workflow +workflow: + segment: + tasks: + - action: segmentation +``` + +### Run with Workflows + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +list(app.workflow("segment", ["This is a test. And another test."])) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name":"segment", "elements":["This is a test. And another test."]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.Segmentation.__init__ +### ::: txtai.pipeline.Segmentation.__call__ diff --git a/docs/pipeline/data/tabular.md b/docs/pipeline/data/tabular.md new file mode 100644 index 0000000..f0d8aaf --- /dev/null +++ b/docs/pipeline/data/tabular.md @@ -0,0 +1,71 @@ +# Tabular + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The Tabular pipeline splits tabular data into rows and columns. The tabular pipeline is most useful in creating (id, text, tag) tuples to load into Embedding indexes. + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai.pipeline import Tabular + +# Create and run pipeline +tabular = Tabular("id", ["text"]) +tabular("path to csv file") +``` + +See the link below for a more detailed example. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Transform tabular data with composable workflows](https://github.com/neuml/txtai/blob/master/examples/22_Transform_tabular_data_with_composable_workflows.ipynb) | Transform, index and search tabular data | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/22_Transform_tabular_data_with_composable_workflows.ipynb) | + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +# Create pipeline using lower case class name +tabular: + idcolumn: id + textcolumns: + - text + +# Run pipeline with workflow +workflow: + tabular: + tasks: + - action: tabular +``` + +### Run with Workflows + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +list(app.workflow("tabular", ["path to csv file"])) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name":"tabular", "elements":["path to csv file"]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.Tabular.__init__ +### ::: txtai.pipeline.Tabular.__call__ diff --git a/docs/pipeline/data/textractor.md b/docs/pipeline/data/textractor.md new file mode 100644 index 0000000..5364584 --- /dev/null +++ b/docs/pipeline/data/textractor.md @@ -0,0 +1,82 @@ +# Textractor + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The Textractor pipeline extracts and splits text from documents. This pipeline extends the [Segmentation](../segmentation) pipeline. + +Each document goes through the following process. + +- Content is retrieved if it's not local +- If the document `mime-type` isn't plain text or HTML, it's converted to HTML via the [FiletoHTML](../filetohtml) pipeline +- HTML is converted to Markdown via the [HTMLToMarkdown](../htmltomd) pipeline +- Content is split/chunked based on the [segmentation parameters](../segmentation/#txtai.pipeline.Segmentation.__init__) and returned + +The [backend](../filetohtml/#txtai.pipeline.FileToHTML.__init__) parameter sets the FileToHTML pipeline backend. If a backend isn't available, this pipeline assumes input is HTML content and only converts it to Markdown. + +See the [FiletoHTML](../filetohtml) and [HTMLToMarkdown](../htmltomd) pipelines to learn more on the dependencies necessary for each of those pipelines. + +Note that the default parameters enable access to all local files and all URLs. The `safeopen` parameter limits this to only files in a specified directory (defaults to temp directory) and public URLs. When running the textractor pipeline through an Application or via the API, `safeopen` defaults to True. It can be disabled if desired via configuration. + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai.pipeline import Textractor + +# Create and run pipeline +textract = Textractor() +textract("https://github.com/neuml/txtai") +``` + +See the link below for a more detailed example. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Extract text from documents](https://github.com/neuml/txtai/blob/master/examples/10_Extract_text_from_documents.ipynb) | Extract text from PDF, Office, HTML and more | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/10_Extract_text_from_documents.ipynb) | +| [Chunking your data for RAG](https://github.com/neuml/txtai/blob/master/examples/73_Chunking_your_data_for_RAG.ipynb) | Extract, chunk and index content for effective retrieval | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/73_Chunking_your_data_for_RAG.ipynb) | + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +# Create pipeline using lower case class name +textractor: + +# Run pipeline with workflow +workflow: + textract: + tasks: + - action: textractor +``` + +### Run with Workflows + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +list(app.workflow("textract", ["https://github.com/neuml/txtai"])) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name":"textract", "elements":["https://github.com/neuml/txtai"]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.Textractor.__init__ +### ::: txtai.pipeline.Textractor.__call__ diff --git a/docs/pipeline/data/tokenizer.md b/docs/pipeline/data/tokenizer.md new file mode 100644 index 0000000..819489e --- /dev/null +++ b/docs/pipeline/data/tokenizer.md @@ -0,0 +1,82 @@ +# Tokenizer + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The Tokenizer pipeline splits text into tokens. This is primarily used for keyword / term indexing. + +_Note: Transformers-based models have their own tokenizers and this pipeline isn't designed for working with Transformers models._ + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai.pipeline import Tokenizer + +# Create and run pipeline +tokenizer = Tokenizer() +tokenizer("text to tokenize") + +# Whitespace tokenization +tokenizer = Tokenizer(whitespace=True) +tokenizer("text to tokenize") + +# Tokenize using a regular expression +tokenizer = Tokenizer(regexp=r"\w{5,}") +tokenizer("text to tokenize") + +# Tokenize into trigrams like pg_trgm +tokenizer = Tokenizer(ngrams={ + "ngrams": 3, "lpad": " ", "rpad": " ", "unique": True +}) +tokenize("text to tokenize") + +# Tokenize into edge ngrams +tokenizer = Tokenizer(ngrams={"nmin": 2, "nmax": 5, "edge": True}) +tokenizer("text to tokenize") +``` + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +# Create pipeline using lower case class name +tokenizer: + +# Run pipeline with workflow +workflow: + tokenizer: + tasks: + - action: tokenizer +``` + +### Run with Workflows + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +list(app.workflow("tokenizer", ["text to tokenize"])) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name":"tokenizer", "elements":["text"]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.Tokenizer.__init__ +### ::: txtai.pipeline.Tokenizer.__call__ diff --git a/docs/pipeline/data/urlretrieve.md b/docs/pipeline/data/urlretrieve.md new file mode 100644 index 0000000..27b6db2 --- /dev/null +++ b/docs/pipeline/data/urlretrieve.md @@ -0,0 +1,62 @@ +# URL Retrieve + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The URL Retrieve pipeline retrieves content from a HTTP(s) URL. + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai.pipeline import URLRetrieve + +# Create and run pipeline +urlretrieve = URLRetrieve() +urlretrieve("https://github.com/neuml/txtai") +``` + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +# Create pipeline using lower case class name +urlretrieve: + +# Run pipeline with workflow +workflow: + retrieve: + tasks: + - action: urlretrieve +``` + +### Run with Workflows + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +list(app.workflow("urlretrieve", ["https://github.com/neuml/txtai"])) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name":"retrieve", "elements":["http://github.com/neuml/txtai"]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.URLRetrieve.__init__ +### ::: txtai.pipeline.URLRetrieve.__call__ diff --git a/docs/pipeline/image/caption.md b/docs/pipeline/image/caption.md new file mode 100644 index 0000000..0d89aad --- /dev/null +++ b/docs/pipeline/image/caption.md @@ -0,0 +1,68 @@ +# Caption + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The caption pipeline reads a list of images and returns a list of captions for those images. + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai.pipeline import Caption + +# Create and run pipeline +caption = Caption() +caption("path to image file") +``` + +See the link below for a more detailed example. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Generate image captions and detect objects](https://github.com/neuml/txtai/blob/master/examples/25_Generate_image_captions_and_detect_objects.ipynb) | Captions and object detection for images | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/25_Generate_image_captions_and_detect_objects.ipynb) | + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +# Create pipeline using lower case class name +caption: + +# Run pipeline with workflow +workflow: + caption: + tasks: + - action: caption +``` + +### Run with Workflows + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +list(app.workflow("caption", ["path to image file"])) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name":"caption", "elements":["path to image file"]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.Caption.__init__ +### ::: txtai.pipeline.Caption.__call__ diff --git a/docs/pipeline/image/imagehash.md b/docs/pipeline/image/imagehash.md new file mode 100644 index 0000000..e56524f --- /dev/null +++ b/docs/pipeline/image/imagehash.md @@ -0,0 +1,68 @@ +# ImageHash + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The image hash pipeline generates perceptual image hashes. These hashes can be used to detect near-duplicate images. This method is not backed by machine learning models and not intended to find conceptually similar images. + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai.pipeline import ImageHash + +# Create and run pipeline +ihash = ImageHash() +ihash("path to image file") +``` + +See the link below for a more detailed example. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Near duplicate image detection](https://github.com/neuml/txtai/blob/master/examples/31_Near_duplicate_image_detection.ipynb) | Identify duplicate and near-duplicate images | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/31_Near_duplicate_image_detection.ipynb) | + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +# Create pipeline using lower case class name +imagehash: + +# Run pipeline with workflow +workflow: + imagehash: + tasks: + - action: imagehash +``` + +### Run with Workflows + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +list(app.workflow("imagehash", ["path to image file"])) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name":"imagehash", "elements":["path to image file"]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.ImageHash.__init__ +### ::: txtai.pipeline.ImageHash.__call__ diff --git a/docs/pipeline/image/objects.md b/docs/pipeline/image/objects.md new file mode 100644 index 0000000..83ddbe6 --- /dev/null +++ b/docs/pipeline/image/objects.md @@ -0,0 +1,68 @@ +# Objects + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The Objects pipeline reads a list of images and returns a list of detected objects. + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai.pipeline import Objects + +# Create and run pipeline +objects = Objects() +objects("path to image file") +``` + +See the link below for a more detailed example. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Generate image captions and detect objects](https://github.com/neuml/txtai/blob/master/examples/25_Generate_image_captions_and_detect_objects.ipynb) | Captions and object detection for images | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/25_Generate_image_captions_and_detect_objects.ipynb) | + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +# Create pipeline using lower case class name +objects: + +# Run pipeline with workflow +workflow: + objects: + tasks: + - action: objects +``` + +### Run with Workflows + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +list(app.workflow("objects", ["path to image file"])) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name":"objects", "elements":["path to image file"]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.Objects.__init__ +### ::: txtai.pipeline.Objects.__call__ diff --git a/docs/pipeline/index.md b/docs/pipeline/index.md new file mode 100644 index 0000000..779effa --- /dev/null +++ b/docs/pipeline/index.md @@ -0,0 +1,47 @@ +# Pipeline + +![pipeline](../images/pipeline.png#only-light) +![pipeline](../images/pipeline-dark.png#only-dark) + +txtai provides a generic pipeline processing framework with the only interface requirement being a `__call__` method. Pipelines are flexible and process various types of data. Pipelines can wrap machine learning models as well as other processes. + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../workflow/#configuration-driven-example) or the [API](../api#local-instance). + +## List of pipelines + +The following is a list of the current pipelines available in txtai. All pipelines use default models when otherwise not specified. See the [model guide](../models) for the current model recommendations. All pipelines are designed to work with local models via the [Transformers library](https://github.com/huggingface/transformers). + +The `LLM` and `RAG` pipelines also have integrations for [llama.cpp](https://github.com/abetlen/llama-cpp-python) and [hosted API models via LiteLLM](https://github.com/BerriAI/litellm). The `LLM` pipeline can be prompted to accomplish many of the same tasks (i.e. summarization, translation, classification). + +- Audio + - [AudioMixer](audio/audiomixer) + - [AudioStream](audio/audiostream) + - [Microphone](audio/microphone) + - [TextToAudio](audio/texttoaudio) + - [TextToSpeech](audio/texttospeech) + - [Transcription](audio/transcription) +- Data Processing + - [FileToHTML](data/filetohtml) + - [HTMLToMarkdown](data/htmltomd) + - [Segmentation](data/segmentation) + - [Tabular](data/tabular) + - [Text extraction](data/textractor) + - [Tokenizer](data/tokenizer) + - [URLRetrieve](data/urlretrieve) +- Image + - [Caption](image/caption) + - [Image Hash](image/imagehash) + - [Objects](image/objects) +- Text + - [Entity](text/entity) + - [Labeling](text/labels) + - [LLM](text/llm) + - [RAG](text/rag) + - [Reranker](text/reranker) + - [Similarity](text/similarity) + - [Summary](text/summary) + - [Translation](text/translation) +- Training + - [HF ONNX](train/hfonnx) + - [ML ONNX](train/mlonnx) + - [Trainer](train/trainer) diff --git a/docs/pipeline/llm/llm.md b/docs/pipeline/llm/llm.md new file mode 100644 index 0000000..72b6044 --- /dev/null +++ b/docs/pipeline/llm/llm.md @@ -0,0 +1,225 @@ +# LLM + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The LLM pipeline runs prompts through a large language model (LLM). This pipeline autodetects the LLM framework based on the model path. + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai import LLM + +# Create LLM pipeline +llm = LLM() + +# Run prompt +llm( + """ + Answer the following question using the provided context. + + Question: + What are the applications of txtai? + + Context: + txtai is an open-source platform for semantic search and + workflows powered by language models. + """ +) + +# Prompts with chat templating can be directly passed +# The template format varies by model +llm( + """ + <|im_start|>system + You are a friendly assistant.<|im_end|> + <|im_start|>user + Answer the following question...<|im_end|> + <|im_start|>assistant + """ +) + +# Chat messages automatically handle templating +llm([ + {"role": "system", "content": "You are a friendly assistant."}, + {"role": "user", "content": "Answer the following question..."} +]) + +# When there is no system prompt passed to instruction tuned models +# the default role is inferred `defaultrole="auto"` +llm("Answer the following question...") + +# To always generate chat messages for string inputs +llm("Answer the following question...", defaultrole="user") + +# To never generate chat messages for string inputs +llm("Answer the following question...", defaultrole="prompt") +``` + +The LLM pipeline automatically detects the underlying LLM framework. This can also be manually set. The following methods are supported. + +- [Hugging Face Transformers](https://github.com/huggingface/transformers) +- [llama.cpp](https://github.com/abetlen/llama-cpp-python) +- [LLM APIs via LiteLLM](https://github.com/BerriAI/litellm) +- [LiteRT-LM](https://ai.google.dev/edge/litert-lm) +- [OpenCode server](https://github.com/anomalyco/opencode) + +`llama.cpp` and `LiteRT-LM` support both local and remote file paths on the HF Hub. + +See the [LiteLLM documentation](https://litellm.vercel.app/docs/providers) for the options available with LiteLLM models. + +See the [OpenCode documentation](https://opencode.ai/docs/server/) for more on how to integrate the LLM pipeline with a running OpenCode instance. + +```python +from txtai import LLM + +# Transformers +llm = LLM("openai/gpt-oss-20b") +llm = LLM("openai/gpt-oss-20b", method="transformers") + +# llama.cpp +llm = LLM("unsloth/gpt-oss-20b-GGUF/gpt-oss-20b-Q4_K_M.gguf") +llm = LLM("unsloth/gpt-oss-20b-GGUF/gpt-oss-20b-Q4_K_M.gguf", + method="llama.cpp") + +# LiteLLM +llm = LLM("ollama/gpt-oss") +llm = LLM("ollama/gpt-oss", method="litellm") + +# LiteRT-LM +llm = LLM( + "litert-community/gemma-4-E2B-it-litert-lm/gemma-4-E2B-it.litertlm" +) +llm = LLM( + "litert-community/gemma-4-E2B-it-litert-lm/gemma-4-E2B-it.litertlm", + method="litert" +) + +# Custom Ollama endpoint +llm = LLM("ollama/gpt-oss", api_base="http://localhost:11434") + +# Custom OpenAI-compatible endpoint +llm = LLM("openai/gpt-oss", api_base="http://localhost:4000") + +# LLM APIs - must also set API key via environment variable +llm = LLM("gpt-5.2") +llm = LLM("claude-opus-4-5-20251101") +llm = LLM("gemini/gemini-3-pro-preview") + +# Local OpenCode server started via `opencode serve` +llm = LLM("opencode") +llm = LLM("opencode/big-pickle", url="http://localhost:4000") +``` + +Models can be externally loaded and passed to pipelines. This is useful for models that are not yet supported by Transformers and/or need special initialization. + +```python +import torch + +from transformers import AutoModelForCausalLM, AutoTokenizer +from txtai import LLM + +# Load Qwen3 0.6B +path = "Qwen/Qwen3-0.6B" +model = AutoModelForCausalLM.from_pretrained( + path, + dtype=torch.bfloat16, +) +tokenizer = AutoTokenizer.from_pretrained(path) + +llm = LLM((model, tokenizer)) +``` + +See the links below for more detailed examples. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Prompt-driven search with LLMs](https://github.com/neuml/txtai/blob/master/examples/42_Prompt_driven_search_with_LLMs.ipynb) | Embeddings-guided and Prompt-driven search with Large Language Models (LLMs) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/42_Prompt_driven_search_with_LLMs.ipynb) | +| [Prompt templates and task chains](https://github.com/neuml/txtai/blob/master/examples/44_Prompt_templates_and_task_chains.ipynb) | Build model prompts and connect tasks together with workflows | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/44_Prompt_templates_and_task_chains.ipynb) | +| [Build RAG pipelines with txtai](https://github.com/neuml/txtai/blob/master/examples/52_Build_RAG_pipelines_with_txtai.ipynb) [▶️](https://www.youtube.com/watch?v=t_OeAc8NVfQ) | Guide on retrieval augmented generation including how to create citations | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/52_Build_RAG_pipelines_with_txtai.ipynb) | +| [Integrate LLM frameworks](https://github.com/neuml/txtai/blob/master/examples/53_Integrate_LLM_Frameworks.ipynb) | Integrate llama.cpp, LiteLLM and custom generation frameworks | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/53_Integrate_LLM_Frameworks.ipynb) | +| [Generate knowledge with Semantic Graphs and RAG](https://github.com/neuml/txtai/blob/master/examples/55_Generate_knowledge_with_Semantic_Graphs_and_RAG.ipynb) | Knowledge exploration and discovery with Semantic Graphs and RAG | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/55_Generate_knowledge_with_Semantic_Graphs_and_RAG.ipynb) | +| [Build knowledge graphs with LLMs](https://github.com/neuml/txtai/blob/master/examples/57_Build_knowledge_graphs_with_LLM_driven_entity_extraction.ipynb) | Build knowledge graphs with LLM-driven entity extraction | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/57_Build_knowledge_graphs_with_LLM_driven_entity_extraction.ipynb) | +| [Advanced RAG with graph path traversal](https://github.com/neuml/txtai/blob/master/examples/58_Advanced_RAG_with_graph_path_traversal.ipynb) | Graph path traversal to collect complex sets of data for advanced RAG | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/58_Advanced_RAG_with_graph_path_traversal.ipynb) | +| [Advanced RAG with guided generation](https://github.com/neuml/txtai/blob/master/examples/60_Advanced_RAG_with_guided_generation.ipynb) | Retrieval Augmented and Guided Generation | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/60_Advanced_RAG_with_guided_generation.ipynb) | +| [RAG with llama.cpp and external API services](https://github.com/neuml/txtai/blob/master/examples/62_RAG_with_llama_cpp_and_external_API_services.ipynb) | RAG with additional vector and LLM frameworks | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/62_RAG_with_llama_cpp_and_external_API_services.ipynb) | +| [How RAG with txtai works](https://github.com/neuml/txtai/blob/master/examples/63_How_RAG_with_txtai_works.ipynb) | Create RAG processes, API services and Docker instances | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/63_How_RAG_with_txtai_works.ipynb) | +| [Speech to Speech RAG](https://github.com/neuml/txtai/blob/master/examples/65_Speech_to_Speech_RAG.ipynb) [▶️](https://www.youtube.com/watch?v=tH8QWwkVMKA) | Full cycle speech to speech workflow with RAG | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/65_Speech_to_Speech_RAG.ipynb) | +| [Analyzing Hugging Face Posts with Graphs and Agents](https://github.com/neuml/txtai/blob/master/examples/68_Analyzing_Hugging_Face_Posts_with_Graphs_and_Agents.ipynb) | Explore a rich dataset with Graph Analysis and Agents | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/68_Analyzing_Hugging_Face_Posts_with_Graphs_and_Agents.ipynb) | +| [Granting autonomy to agents](https://github.com/neuml/txtai/blob/master/examples/69_Granting_autonomy_to_agents.ipynb) | Agents that iteratively solve problems as they see fit | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/69_Granting_autonomy_to_agents.ipynb) | +| [Getting started with LLM APIs](https://github.com/neuml/txtai/blob/master/examples/70_Getting_started_with_LLM_APIs.ipynb) | Generate embeddings and run LLMs with OpenAI, Claude, Gemini, Bedrock and more | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/70_Getting_started_with_LLM_APIs.ipynb) | +| [Analyzing LinkedIn Company Posts with Graphs and Agents](https://github.com/neuml/txtai/blob/master/examples/71_Analyzing_LinkedIn_Company_Posts_with_Graphs_and_Agents.ipynb) | Exploring how to improve social media engagement with AI | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/71_Analyzing_LinkedIn_Company_Posts_with_Graphs_and_Agents.ipynb) | +| [Parsing the stars with txtai](https://github.com/neuml/txtai/blob/master/examples/72_Parsing_the_stars_with_txtai.ipynb) | Explore an astronomical knowledge graph of known stars, planets, galaxies | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/72_Parsing_the_stars_with_txtai.ipynb) | +| [Chunking your data for RAG](https://github.com/neuml/txtai/blob/master/examples/73_Chunking_your_data_for_RAG.ipynb) | Extract, chunk and index content for effective retrieval | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/73_Chunking_your_data_for_RAG.ipynb) | +| [Medical RAG Research with txtai](https://github.com/neuml/txtai/blob/master/examples/75_Medical_RAG_Research_with_txtai.ipynb) | Analyze PubMed article metadata with RAG | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/75_Medical_RAG_Research_with_txtai.ipynb) | +| [GraphRAG with Wikipedia and GPT OSS](https://github.com/neuml/txtai/blob/master/examples/77_GraphRAG_with_Wikipedia_and_GPT_OSS.ipynb) | Deep graph search powered RAG | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/77_GraphRAG_with_Wikipedia_and_GPT_OSS.ipynb) | +| [RAG is more than Vector Search](https://github.com/neuml/txtai/blob/master/examples/79_RAG_is_more_than_Vector_Search.ipynb) | Context retrieval via Web, SQL and other sources | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/79_RAG_is_more_than_Vector_Search.ipynb) | +| [OpenCode as a txtai LLM](https://github.com/neuml/txtai/blob/master/examples/81_OpenCode_as_a_txtai_LLM.ipynb) | Integrate OpenCode with the txtai ecosystem | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/81_OpenCode_as_a_txtai_LLM.ipynb) | +| [Agentic College Search](https://github.com/neuml/txtai/blob/master/examples/82_Agentic_College_Search.ipynb) | Identify a list of strong engineering colleges | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/82_Agentic_College_Search.ipynb) | +| [TxtAI got skills](https://github.com/neuml/txtai/blob/master/examples/83_TxtAI_got_skills.ipynb) | Integrate skill.md files with your agent | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/83_TxtAI_got_skills.ipynb) | +| [Agent Tools](https://github.com/neuml/txtai/blob/master/examples/84_Agent_Tools.ipynb) [▶️](https://www.youtube.com/watch?v=RDNaFXQy3GQ) | Learn about the txtai agent toolkit | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/84_Agent_Tools.ipynb) | + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +# Create pipeline using lower case class name +llm: + +# Run pipeline with workflow +workflow: + llm: + tasks: + - action: llm +``` + +Similar to the Python example above, the underlying [Hugging Face pipeline parameters](https://huggingface.co/docs/transformers/main/main_classes/pipelines#transformers.pipeline.model) and [model parameters](https://huggingface.co/docs/transformers/model_doc/auto#transformers.AutoModel.from_pretrained) can be set in pipeline configuration. + +```yaml +llm: + path: Qwen/Qwen3-0.6B + dtype: torch.bfloat16 +``` + +### Run with Workflows + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +list(app.workflow("llm", [ + """ + Answer the following question using the provided context. + + Question: + What are the applications of txtai? + + Context: + txtai is an open-source platform for semantic search and + workflows powered by language models. + """ +])) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name":"llm", "elements": ["Answer the following question..."]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.LLM.__init__ +### ::: txtai.pipeline.LLM.__call__ diff --git a/docs/pipeline/llm/rag.md b/docs/pipeline/llm/rag.md new file mode 100644 index 0000000..c4cac43 --- /dev/null +++ b/docs/pipeline/llm/rag.md @@ -0,0 +1,180 @@ +# RAG + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The Retrieval Augmented Generation (RAG) pipeline joins a prompt, context data store and generative model together to extract knowledge. + +The data store can be an embeddings database or a similarity instance with associated input text. The generative model can be a prompt-driven large language model (LLM), an extractive question-answering model or a custom pipeline. + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai import Embeddings, RAG + +# Input data +data = [ + "US tops 5 million confirmed virus cases", + "Canada's last fully intact ice shelf has suddenly collapsed, " + + "forming a Manhattan-sized iceberg", + "Beijing mobilises invasion craft along coast as Taiwan tensions escalate", + "The National Park Service warns against sacrificing slower friends " + + "in a bear attack", + "Maine man wins $1M from $25 lottery ticket", + "Make huge profits without work, earn up to $100,000 a day" +] + +# Build embeddings index +embeddings = Embeddings(content=True) +embeddings.index(data) + +# Create the RAG pipeline +rag = RAG(embeddings, "Qwen/Qwen3-0.6B", template=""" + Answer the following question using the provided context. + + Question: + {question} + + Context: + {context} +""") + +# Run RAG pipeline +rag("What was won?") + +# Prompts with chat templating can be directly passed +# The template format varies by model +rag = RAG(embeddings, "Qwen/Qwen3-0.6B", template=""" + <|im_start|>system + You are a friendly assistant.<|im_end|> + <|im_start|>user + Answer the following question using the provided context. + + Question: + {question} + + Context: + {context} + <|im_start|>assistant + """ +) +rag("What was won?") + +# Inputs are automatically converted to chat messages when a +# system prompt is provided +rag = RAG( + embeddings, + "openai/gpt-oss-20b", + system="You are a friendly assistant", + template=""" + Answer the following question using the provided context. + + Question: + {question} + + Context: + {context} +""") +rag("What was won?") + +# LLM options can be passed as additional arguments +# - Streaming RAG response with `stream=True` +# - String inputs are always converted to user messages with `defaultrole="user"` +# - Thinking text is removed with `stripthink=True` +rag("What was won?", stream=True, defaultrole="user", stripThink=True) +``` + +See the [Embeddings](../../../embeddings) and [LLM](../llm) pages for additional configuration options. + +Check out this [RAG Quickstart Example](https://github.com/neuml/txtai/blob/master/examples/rag_quickstart.py). Additional examples are listed below. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Prompt-driven search with LLMs](https://github.com/neuml/txtai/blob/master/examples/42_Prompt_driven_search_with_LLMs.ipynb) | Embeddings-guided and Prompt-driven search with Large Language Models (LLMs) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/42_Prompt_driven_search_with_LLMs.ipynb) | +| [Build RAG pipelines with txtai](https://github.com/neuml/txtai/blob/master/examples/52_Build_RAG_pipelines_with_txtai.ipynb) [▶️](https://www.youtube.com/watch?v=t_OeAc8NVfQ) | Guide on retrieval augmented generation including how to create citations | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/52_Build_RAG_pipelines_with_txtai.ipynb) | +| [Integrate LLM frameworks](https://github.com/neuml/txtai/blob/master/examples/53_Integrate_LLM_Frameworks.ipynb) | Integrate llama.cpp, LiteLLM and custom generation frameworks | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/53_Integrate_LLM_Frameworks.ipynb) | +| [Generate knowledge with Semantic Graphs and RAG](https://github.com/neuml/txtai/blob/master/examples/55_Generate_knowledge_with_Semantic_Graphs_and_RAG.ipynb) | Knowledge exploration and discovery with Semantic Graphs and RAG | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/55_Generate_knowledge_with_Semantic_Graphs_and_RAG.ipynb) | +| [Advanced RAG with graph path traversal](https://github.com/neuml/txtai/blob/master/examples/58_Advanced_RAG_with_graph_path_traversal.ipynb) | Graph path traversal to collect complex sets of data for advanced RAG | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/58_Advanced_RAG_with_graph_path_traversal.ipynb) | +| [Advanced RAG with guided generation](https://github.com/neuml/txtai/blob/master/examples/60_Advanced_RAG_with_guided_generation.ipynb) | Retrieval Augmented and Guided Generation | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/60_Advanced_RAG_with_guided_generation.ipynb) | +| [RAG with llama.cpp and external API services](https://github.com/neuml/txtai/blob/master/examples/62_RAG_with_llama_cpp_and_external_API_services.ipynb) | RAG with additional vector and LLM frameworks | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/62_RAG_with_llama_cpp_and_external_API_services.ipynb) | +| [How RAG with txtai works](https://github.com/neuml/txtai/blob/master/examples/63_How_RAG_with_txtai_works.ipynb) | Create RAG processes, API services and Docker instances | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/63_How_RAG_with_txtai_works.ipynb) | +| [Speech to Speech RAG](https://github.com/neuml/txtai/blob/master/examples/65_Speech_to_Speech_RAG.ipynb) [▶️](https://www.youtube.com/watch?v=tH8QWwkVMKA) | Full cycle speech to speech workflow with RAG | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/65_Speech_to_Speech_RAG.ipynb) | +| [Parsing the stars with txtai](https://github.com/neuml/txtai/blob/master/examples/72_Parsing_the_stars_with_txtai.ipynb) | Explore an astronomical knowledge graph of known stars, planets, galaxies | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/72_Parsing_the_stars_with_txtai.ipynb) | +| [Chunking your data for RAG](https://github.com/neuml/txtai/blob/master/examples/73_Chunking_your_data_for_RAG.ipynb) | Extract, chunk and index content for effective retrieval | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/73_Chunking_your_data_for_RAG.ipynb) | +| [Medical RAG Research with txtai](https://github.com/neuml/txtai/blob/master/examples/75_Medical_RAG_Research_with_txtai.ipynb) | Analyze PubMed article metadata with RAG | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/75_Medical_RAG_Research_with_txtai.ipynb) | +| [GraphRAG with Wikipedia and GPT OSS](https://github.com/neuml/txtai/blob/master/examples/77_GraphRAG_with_Wikipedia_and_GPT_OSS.ipynb) | Deep graph search powered RAG | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/77_GraphRAG_with_Wikipedia_and_GPT_OSS.ipynb) | +| [RAG is more than Vector Search](https://github.com/neuml/txtai/blob/master/examples/79_RAG_is_more_than_Vector_Search.ipynb) | Context retrieval via Web, SQL and other sources | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/79_RAG_is_more_than_Vector_Search.ipynb) | + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +# Allow documents to be indexed +writable: True + +# Content is required for extractor pipeline +embeddings: + content: True + +rag: + path: Qwen/Qwen3-0.6B + template: | + Answer the following question using the provided context. + + Question: + {question} + + Context: + {context} + +workflow: + search: + tasks: + - action: rag +``` + +### Run with Workflows + +Built in tasks make using the extractor pipeline easier. + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +app.add([ + "US tops 5 million confirmed virus cases", + "Canada's last fully intact ice shelf has suddenly collapsed, " + + "forming a Manhattan-sized iceberg", + "Beijing mobilises invasion craft along coast as Taiwan tensions escalate", + "The National Park Service warns against sacrificing slower friends " + + "in a bear attack", + "Maine man wins $1M from $25 lottery ticket", + "Make huge profits without work, earn up to $100,000 a day" +]) +app.index() + +list(app.workflow("search", ["What was won?"])) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name": "search", "elements": ["What was won"]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.RAG.__init__ +### ::: txtai.pipeline.RAG.__call__ diff --git a/docs/pipeline/text/entity.md b/docs/pipeline/text/entity.md new file mode 100644 index 0000000..d45b028 --- /dev/null +++ b/docs/pipeline/text/entity.md @@ -0,0 +1,75 @@ +# Entity + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The Entity pipeline applies a token classifier to text and extracts entity/label combinations. + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai.pipeline import Entity + +# Create and run pipeline +entity = Entity() +entity("Canada's last fully intact ice shelf has suddenly collapsed, " \ + "forming a Manhattan-sized iceberg") + +# Extract entities using a GLiNER model which supports dynamic labels +entity = Entity("gliner-community/gliner_medium-v2.5") +entity("Canada's last fully intact ice shelf has suddenly collapsed, " \ + "forming a Manhattan-sized iceberg", labels=["country", "city"]) +``` + +See the link below for a more detailed example. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Entity extraction workflows](https://github.com/neuml/txtai/blob/master/examples/26_Entity_extraction_workflows.ipynb) | Identify entity/label combinations | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/26_Entity_extraction_workflows.ipynb) | +| [Parsing the stars with txtai](https://github.com/neuml/txtai/blob/master/examples/72_Parsing_the_stars_with_txtai.ipynb) | Explore an astronomical knowledge graph of known stars, planets, galaxies | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/72_Parsing_the_stars_with_txtai.ipynb) | + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +# Create pipeline using lower case class name +entity: + +# Run pipeline with workflow +workflow: + entity: + tasks: + - action: entity +``` + +### Run with Workflows + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +list(app.workflow("entity", ["Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg"])) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name":"entity", "elements": ["Canadas last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg"]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.Entity.__init__ +### ::: txtai.pipeline.Entity.__call__ diff --git a/docs/pipeline/text/labels.md b/docs/pipeline/text/labels.md new file mode 100644 index 0000000..49923d4 --- /dev/null +++ b/docs/pipeline/text/labels.md @@ -0,0 +1,72 @@ +# Labels + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The Labels pipeline uses a text classification model to apply labels to input text. This pipeline can classify text using either a zero shot model (dynamic labeling) or a standard text classification model (fixed labeling). + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai.pipeline import Labels + +# Create and run pipeline +labels = Labels() +labels( + ["Great news", "That's rough"], + ["positive", "negative"] +) +``` + +See the link below for a more detailed example. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Apply labels with zero shot classification](https://github.com/neuml/txtai/blob/master/examples/07_Apply_labels_with_zero_shot_classification.ipynb) | Use zero shot learning for labeling, classification and topic modeling | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/07_Apply_labels_with_zero_shot_classification.ipynb) | + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +# Create pipeline using lower case class name +labels: + +# Run pipeline with workflow +workflow: + labels: + tasks: + - action: labels + args: [["positive", "negative"]] +``` + +### Run with Workflows + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +list(app.workflow("labels", ["Great news", "That's rough"])) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name":"labels", "elements": ["Great news", "Thats rough"]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.Labels.__init__ +### ::: txtai.pipeline.Labels.__call__ diff --git a/docs/pipeline/text/reranker.md b/docs/pipeline/text/reranker.md new file mode 100644 index 0000000..fb61893 --- /dev/null +++ b/docs/pipeline/text/reranker.md @@ -0,0 +1,82 @@ +# Reranker + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The Reranker pipeline runs embeddings queries and re-ranks them using a similarity pipeline. + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai import Embeddings +from txtai.pipeline import Reranker, Similarity + +# Embeddings instance +embeddings = Embeddings() +embeddings.load(provider="huggingface-hub", container="neuml/txtai-wikipedia") + +# Similarity instance +similarity = Similarity(path="colbert-ir/colbertv2.0", lateencode=True) + +# Reranking pipeline +reranker = Reranker(embeddings, similarity) +reranker("Tell me about AI") +``` + +_Note: Content must be enabled with the embeddings instance for this to work properly._ + +See the link below for a more detailed example. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [What's new in txtai 9.0](https://github.com/neuml/txtai/blob/master/examples/76_Whats_new_in_txtai_9_0.ipynb) | Learned sparse vectors, late interaction models and rerankers | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/76_Whats_new_in_txtai_9_0.ipynb) | + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +embeddings: + +similarity: + +# Create pipeline using lower case class name +reranker: + +# Run pipeline with workflow +workflow: + translate: + tasks: + - reranker +``` + +### Run with Workflows + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +list(app.workflow("reranker", ["Tell me about AI"])) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name":"rerank", "elements":["Tell me about AI"]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.Reranker.__init__ +### ::: txtai.pipeline.Reranker.__call__ diff --git a/docs/pipeline/text/similarity.md b/docs/pipeline/text/similarity.md new file mode 100644 index 0000000..79b49b8 --- /dev/null +++ b/docs/pipeline/text/similarity.md @@ -0,0 +1,72 @@ +# Similarity + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The Similarity pipeline computes similarity between queries and list of text using a text classifier. + +This pipeline supports both standard text classification models and zero-shot classification models. The pipeline uses the queries as labels for the input text. The results are transposed to get scores per query/label vs scores per input text. + +Cross-encoder models are supported via the `crossencode=True` constructor parameter. Late interaction (i.e. ColBERT) models are also supported via the `lateencode=True` constructor parameter. CrossEncoder and LateEncoder pipelines back each of these models and can be instantiated directly as well. + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai.pipeline import Similarity + +# Create and run pipeline +similarity = Similarity() +similarity("feel good story", [ + "Maine man wins $1M from $25 lottery ticket", + "Don't sacrifice slower friends in a bear attack" +]) +``` + +See the link below for a more detailed example. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Add semantic search to Elasticsearch](https://github.com/neuml/txtai/blob/master/examples/04_Add_semantic_search_to_Elasticsearch.ipynb) | Add semantic search to existing search systems | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/04_Add_semantic_search_to_Elasticsearch.ipynb) | + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +# Create pipeline using lower case class name +similarity: +``` + +### Run with Workflows + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +app.similarity("feel good story", [ + "Maine man wins $1M from $25 lottery ticket", + "Don't sacrifice slower friends in a bear attack" +]) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/similarity" \ + -H "Content-Type: application/json" \ + -d '{"query": "feel good story", "texts": ["Maine man wins $1M from $25 lottery ticket", "Dont sacrifice slower friends in a bear attack"]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.Similarity.__init__ +### ::: txtai.pipeline.Similarity.__call__ diff --git a/docs/pipeline/text/summary.md b/docs/pipeline/text/summary.md new file mode 100644 index 0000000..2784240 --- /dev/null +++ b/docs/pipeline/text/summary.md @@ -0,0 +1,68 @@ +# Summary + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The Summary pipeline summarizes text. This pipeline runs a text2text model that abstractively creates a summary of the input text. + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai.pipeline import Summary + +# Create and run pipeline +summary = Summary() +summary("Enter long, detailed text to summarize here") +``` + +See the link below for a more detailed example. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Building abstractive text summaries](https://github.com/neuml/txtai/blob/master/examples/09_Building_abstractive_text_summaries.ipynb) | Run abstractive text summarization | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/09_Building_abstractive_text_summaries.ipynb) | + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +# Create pipeline using lower case class name +summary: + +# Run pipeline with workflow +workflow: + summary: + tasks: + - action: summary +``` + +### Run with Workflows + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +list(app.workflow("summary", ["Enter long, detailed text to summarize here"])) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name":"summary", "elements":["Enter long, detailed text to summarize here"]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.Summary.__init__ +### ::: txtai.pipeline.Summary.__call__ diff --git a/docs/pipeline/text/translation.md b/docs/pipeline/text/translation.md new file mode 100644 index 0000000..8067345 --- /dev/null +++ b/docs/pipeline/text/translation.md @@ -0,0 +1,69 @@ +# Translation + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +The Translation pipeline translates text between languages. It supports over 100+ languages. Automatic source language detection is built-in. This pipeline detects the language of each input text row, loads a model for the source-target combination and translates text to the target language. + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai.pipeline import Translation + +# Create and run pipeline +translate = Translation() +translate("This is a test translation into Spanish", "es") +``` + +See the link below for a more detailed example. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Translate text between languages](https://github.com/neuml/txtai/blob/master/examples/12_Translate_text_between_languages.ipynb) | Streamline machine translation and language detection | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/12_Translate_text_between_languages.ipynb) | + +## Configuration-driven example + +Pipelines are run with Python or configuration. Pipelines can be instantiated in [configuration](../../../api/configuration/#pipeline) using the lower case name of the pipeline. Configuration-driven pipelines are run with [workflows](../../../workflow/#configuration-driven-example) or the [API](../../../api#local-instance). + +### config.yml +```yaml +# Create pipeline using lower case class name +translation: + +# Run pipeline with workflow +workflow: + translate: + tasks: + - action: translation + args: ["es"] +``` + +### Run with Workflows + +```python +from txtai import Application + +# Create and run pipeline with workflow +app = Application("config.yml") +list(app.workflow("translate", ["This is a test translation into Spanish"])) +``` + +### Run with API + +```bash +CONFIG=config.yml uvicorn "txtai.api:app" & + +curl \ + -X POST "http://localhost:8000/workflow" \ + -H "Content-Type: application/json" \ + -d '{"name":"translate", "elements":["This is a test translation into Spanish"]}' +``` + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.Translation.__init__ +### ::: txtai.pipeline.Translation.__call__ diff --git a/docs/pipeline/train/hfonnx.md b/docs/pipeline/train/hfonnx.md new file mode 100644 index 0000000..18378c6 --- /dev/null +++ b/docs/pipeline/train/hfonnx.md @@ -0,0 +1,38 @@ +# HFOnnx + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +Exports a Hugging Face Transformer model to ONNX. Currently, this works best with classification/pooling/qa models. Work is ongoing for sequence to +sequence models (summarization, transcription, translation). + +## Example + +The following shows a simple example using this pipeline. + +```python +from txtai.pipeline import HFOnnx, Labels + +# Model path +path = "distilbert-base-uncased-finetuned-sst-2-english" + +# Export model to ONNX +onnx = HFOnnx() +model = onnx(path, "text-classification", "model.onnx", True) + +# Run inference and validate +labels = Labels((model, path), dynamic=False) +labels("I am happy") +``` + +See the link below for a more detailed example. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Export and run models with ONNX](https://github.com/neuml/txtai/blob/master/examples/18_Export_and_run_models_with_ONNX.ipynb) | Export models with ONNX, run natively in JavaScript, Java and Rust | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/18_Export_and_run_models_with_ONNX.ipynb) | + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.HFOnnx.__call__ diff --git a/docs/pipeline/train/mlonnx.md b/docs/pipeline/train/mlonnx.md new file mode 100644 index 0000000..b2de1f5 --- /dev/null +++ b/docs/pipeline/train/mlonnx.md @@ -0,0 +1,20 @@ +# MLOnnx + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +Exports a traditional machine learning model (i.e. scikit-learn) to ONNX. + +## Example + +See the link below for a detailed example. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Export and run other machine learning models](https://github.com/neuml/txtai/blob/master/examples/21_Export_and_run_other_machine_learning_models.ipynb) | Export and run models from scikit-learn, PyTorch and more | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/21_Export_and_run_other_machine_learning_models.ipynb) | + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.MLOnnx.__call__ diff --git a/docs/pipeline/train/trainer.md b/docs/pipeline/train/trainer.md new file mode 100644 index 0000000..6aaa6ff --- /dev/null +++ b/docs/pipeline/train/trainer.md @@ -0,0 +1,119 @@ +# HFTrainer + +![pipeline](../../images/pipeline.png#only-light) +![pipeline](../../images/pipeline-dark.png#only-dark) + +Trains a new Hugging Face Transformer model using the Trainer framework. + +## Example + +The following shows a simple example using this pipeline. + +```python +import pandas as pd + +from datasets import load_dataset + +from txtai.pipeline import HFTrainer + +trainer = HFTrainer() + +# Pandas DataFrame +df = pd.read_csv("training.csv") +model, tokenizer = trainer("bert-base-uncased", df) + +# Hugging Face dataset +ds = load_dataset("glue", "sst2") +model, tokenizer = trainer("bert-base-uncased", ds["train"], columns=("sentence", "label")) + +# List of dicts +dt = [{"text": "sentence 1", "label": 0}, {"text": "sentence 2", "label": 1}]] +model, tokenizer = trainer("bert-base-uncased", dt) + +# Support additional TrainingArguments +model, tokenizer = trainer("bert-base-uncased", dt, + learning_rate=3e-5, num_train_epochs=5) +``` + +All [TrainingArguments](https://huggingface.co/transformers/main_classes/trainer.html#transformers.TrainingArguments) are supported as function arguments to the trainer call. + +See the links below for more detailed examples. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Train a text labeler](https://github.com/neuml/txtai/blob/master/examples/16_Train_a_text_labeler.ipynb) | Build text sequence classification models | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/16_Train_a_text_labeler.ipynb) | +| [Train without labels](https://github.com/neuml/txtai/blob/master/examples/17_Train_without_labels.ipynb) | Use zero-shot classifiers to train new models | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/17_Train_without_labels.ipynb) | +| [Train a QA model](https://github.com/neuml/txtai/blob/master/examples/19_Train_a_QA_model.ipynb) | Build and fine-tune question-answering models | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/19_Train_a_QA_model.ipynb) | +| [Train a language model from scratch](https://github.com/neuml/txtai/blob/master/examples/41_Train_a_language_model_from_scratch.ipynb) | Build new language models | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/41_Train_a_language_model_from_scratch.ipynb) | + +## Training tasks + +The HFTrainer pipeline builds and/or fine-tunes models for following training tasks. + +| Task | Description | +|:-----|:------------| +| language-generation | Causal language model for text generation (e.g. GPT) | +| language-modeling | Masked language model for general tasks (e.g. BERT) | +| question-answering | Extractive question-answering model, typically with the SQuAD dataset | +| sequence-sequence | Sequence-Sequence model (e.g. T5) | +| text-classification | Classify text with a set of labels | +| token-detection | ELECTRA-style pre-training with replaced token detection | + +## PEFT + +Parameter-Efficient Fine-Tuning (PEFT) is supported through [Hugging Face's PEFT library](https://github.com/huggingface/peft). Quantization is provided through [bitsandbytes](https://github.com/TimDettmers/bitsandbytes). See the examples below. + +```python +from txtai.pipeline import HFTrainer + +trainer = HFTrainer() +trainer(..., quantize=True, lora=True) +``` + +When these parameters are set to True, they use default configuration. This can also be customized. + +```python +quantize = { + "load_in_4bit": True, + "bnb_4bit_use_double_quant": True, + "bnb_4bit_quant_type": "nf4", + "bnb_4bit_compute_dtype": "bfloat16" +} + +lora = { + "r": 16, + "lora_alpha": 8, + "target_modules": "all-linear", + "lora_dropout": 0.05, + "bias": "none" +} + +trainer(..., quantize=quantize, lora=lora) +``` + +The parameters also accept `transformers.BitsAndBytesConfig` and `peft.LoraConfig` instances. + +See the following PEFT documentation links for more information. + +- [Quantization](https://huggingface.co/docs/peft/developer_guides/quantization) +- [LoRA](https://huggingface.co/docs/peft/developer_guides/lora) + +## Merge + +An important parameter for `language-generation` and `language-modeling` tasks is `merge` or the packing of data into chunks. + +It supports the following options. + +- `concat` (default) - text is split into chunks up to maxlength, data can be split across multiple chunks +- `pack` - text is split into chunks up to maxlength, data guaranteed to be in same chunk, chunks can be smaller than maxlength +- `None` - disables merging + +Merging helps reduce training time as data can be processed efficiently without padding. `concat` maximizes this as it guarantees each chunk will be up to maxlength size. `pack` is a middle ground where data is combined but records are preserved. + +For general language modeling tasks like masked language modeling, `concat` is the best choice. For instruction/prompt fine-tuning, `pack` or None are the better choices as it guarantees complex logic is not split across chunks. + +## Methods + +Python documentation for the pipeline. + +### ::: txtai.pipeline.HFTrainer.__call__ diff --git a/docs/poweredby.md b/docs/poweredby.md new file mode 100644 index 0000000..a5c14ad --- /dev/null +++ b/docs/poweredby.md @@ -0,0 +1,14 @@ +# Powered by txtai + +The following applications are powered by txtai. + +![apps](https://raw.githubusercontent.com/neuml/txtai/master/apps.jpg) + +| Application | Description | +|:------------ |:-------------| +| [rag](https://github.com/neuml/rag) | Retrieval Augmented Generation (RAG) application | +| [ncoder](https://github.com/neuml/ncoder) | Open-Source AI coding agent | +| [paperai](https://github.com/neuml/paperai) | AI for medical and scientific papers | +| [annotateai](https://github.com/neuml/annotateai) | Automatically annotate papers with LLMs | + +In addition to this list, there are also many other [open-source projects](https://github.com/neuml/txtai/network/dependents), [published research](https://scholar.google.com/scholar?q=txtai&hl=en&as_ylo=2022) and closed proprietary/commercial projects that have built on txtai in production. diff --git a/docs/usecases.md b/docs/usecases.md new file mode 100644 index 0000000..96c2997 --- /dev/null +++ b/docs/usecases.md @@ -0,0 +1,88 @@ +# Use Cases + +The following sections introduce common txtai use cases. A comprehensive set of over 70 [example notebooks and applications](../examples) are also available. + +## Semantic Search + +Build semantic/similarity/vector/neural search applications. + +![demo](https://raw.githubusercontent.com/neuml/txtai/master/demo.gif) + +Traditional search systems use keywords to find data. Semantic search has an understanding of natural language and identifies results that have the same meaning, not necessarily the same keywords. + +![search](https://raw.githubusercontent.com/neuml/txtai/master/docs/images/search.png#gh-light-mode-only) +![search](https://raw.githubusercontent.com/neuml/txtai/master/docs/images/search-dark.png#gh-dark-mode-only) + +Get started with the following examples. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Introducing txtai](https://github.com/neuml/txtai/blob/master/examples/01_Introducing_txtai.ipynb) [▶️](https://www.youtube.com/watch?v=SIezMnVdmMs) | Overview of the functionality provided by txtai | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/01_Introducing_txtai.ipynb) | +| [Similarity search with images](https://github.com/neuml/txtai/blob/master/examples/13_Similarity_search_with_images.ipynb) | Embed images and text into the same space for search | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/13_Similarity_search_with_images.ipynb) | +| [Build a QA database](https://github.com/neuml/txtai/blob/master/examples/34_Build_a_QA_database.ipynb) | Question matching with semantic search | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/34_Build_a_QA_database.ipynb) | +| [Semantic Graphs](https://github.com/neuml/txtai/blob/master/examples/38_Introducing_the_Semantic_Graph.ipynb) | Explore topics, data connectivity and run network analysis| [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/38_Introducing_the_Semantic_Graph.ipynb) | + +## LLM Orchestration + +Autonomous agents, retrieval augmented generation (RAG), chat with your data, pipelines and workflows that interface with large language models (LLMs). + +![llm](images/llm.png) + +See below to learn more. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Prompt templates and task chains](https://github.com/neuml/txtai/blob/master/examples/44_Prompt_templates_and_task_chains.ipynb) | Build model prompts and connect tasks together with workflows | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/44_Prompt_templates_and_task_chains.ipynb) | +| [Integrate LLM frameworks](https://github.com/neuml/txtai/blob/master/examples/53_Integrate_LLM_Frameworks.ipynb) | Integrate llama.cpp, LiteLLM and custom generation frameworks | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/53_Integrate_LLM_Frameworks.ipynb) | +| [Build knowledge graphs with LLMs](https://github.com/neuml/txtai/blob/master/examples/57_Build_knowledge_graphs_with_LLM_driven_entity_extraction.ipynb) | Build knowledge graphs with LLM-driven entity extraction | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/57_Build_knowledge_graphs_with_LLM_driven_entity_extraction.ipynb) | + +### Agents + +Agents connect embeddings, pipelines, workflows and other agents together to autonomously solve complex problems. + +![agent](images/agent.png) + +txtai agents are built on top of the [smolagents](https://github.com/huggingface/smolagents) framework. This supports all LLMs txtai supports (Hugging Face, llama.cpp, OpenAI / Claude / AWS Bedrock via LiteLLM). Agent prompting with [`agents.md`](https://github.com/agentsmd/agents.md) and [`skill.md`](https://agentskills.io/specification) are also supported. + +Check out this [Agent Quickstart Example](https://github.com/neuml/txtai/blob/master/examples/agent_quickstart.py). Additional examples are listed below. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Granting autonomy to agents](https://github.com/neuml/txtai/blob/master/examples/69_Granting_autonomy_to_agents.ipynb) | Agents that iteratively solve problems as they see fit | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/69_Granting_autonomy_to_agents.ipynb) | +| [TxtAI got skills](https://github.com/neuml/txtai/blob/master/examples/83_TxtAI_got_skills.ipynb) | Integrate skill.md files with your agent | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/83_TxtAI_got_skills.ipynb) | +| [Agent Tools](https://github.com/neuml/txtai/blob/master/examples/84_Agent_Tools.ipynb) [▶️](https://www.youtube.com/watch?v=RDNaFXQy3GQ) | Learn about the txtai agent toolkit | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/84_Agent_Tools.ipynb) | +| [Analyzing LinkedIn Company Posts with Graphs and Agents](https://github.com/neuml/txtai/blob/master/examples/71_Analyzing_LinkedIn_Company_Posts_with_Graphs_and_Agents.ipynb) | Exploring how to improve social media engagement with AI | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/71_Analyzing_LinkedIn_Company_Posts_with_Graphs_and_Agents.ipynb) | + +### Retrieval augmented generation + +Retrieval augmented generation (RAG) reduces the risk of LLM hallucinations by constraining the output with a knowledge base as context. RAG is commonly used to "chat with your data". + +![rag](images/rag.png#gh-light-mode-only) +![rag](images/rag-dark.png#gh-dark-mode-only) + +Check out this [RAG Quickstart Example](https://github.com/neuml/txtai/blob/master/examples/rag_quickstart.py). Additional examples are listed below. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Build RAG pipelines with txtai](https://github.com/neuml/txtai/blob/master/examples/52_Build_RAG_pipelines_with_txtai.ipynb) [▶️](https://www.youtube.com/watch?v=t_OeAc8NVfQ) | Guide on retrieval augmented generation including how to create citations | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/52_Build_RAG_pipelines_with_txtai.ipynb) | +| [RAG is more than Vector Search](https://github.com/neuml/txtai/blob/master/examples/79_RAG_is_more_than_Vector_Search.ipynb) | Context retrieval via Web, SQL and other sources | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/79_RAG_is_more_than_Vector_Search.ipynb) | +| [GraphRAG with Wikipedia and GPT OSS](https://github.com/neuml/txtai/blob/master/examples/77_GraphRAG_with_Wikipedia_and_GPT_OSS.ipynb) | Deep graph search powered RAG | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/77_GraphRAG_with_Wikipedia_and_GPT_OSS.ipynb) | +| [Speech to Speech RAG](https://github.com/neuml/txtai/blob/master/examples/65_Speech_to_Speech_RAG.ipynb) [▶️](https://www.youtube.com/watch?v=tH8QWwkVMKA) | Full cycle speech to speech workflow with RAG | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/65_Speech_to_Speech_RAG.ipynb) | + +## Language Model Workflows + +Language model workflows, also known as semantic workflows, connect language models together to build intelligent applications. + +![flows](images/flows.png#gh-light-mode-only) +![flows](images/flows-dark.png#gh-dark-mode-only) + +While LLMs are powerful, there are plenty of smaller, more specialized models that work better and faster for specific tasks. This includes models for extractive question-answering, automatic summarization, text-to-speech, transcription and translation. + +Check out this [Workflow Quickstart Example](https://github.com/neuml/txtai/blob/master/examples/workflow_quickstart.py). Additional examples are listed below. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Run pipeline workflows](https://github.com/neuml/txtai/blob/master/examples/14_Run_pipeline_workflows.ipynb) [▶️](https://www.youtube.com/watch?v=UBMPDCn1gEU) | Simple yet powerful constructs to efficiently process data | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/14_Run_pipeline_workflows.ipynb) | +| [Building abstractive text summaries](https://github.com/neuml/txtai/blob/master/examples/09_Building_abstractive_text_summaries.ipynb) | Run abstractive text summarization | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/09_Building_abstractive_text_summaries.ipynb) | +| [Transcribe audio to text](https://github.com/neuml/txtai/blob/master/examples/11_Transcribe_audio_to_text.ipynb) | Convert audio files to text | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/11_Transcribe_audio_to_text.ipynb) | +| [Translate text between languages](https://github.com/neuml/txtai/blob/master/examples/12_Translate_text_between_languages.ipynb) | Streamline machine translation and language detection | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/12_Translate_text_between_languages.ipynb) | diff --git a/docs/why.md b/docs/why.md new file mode 100644 index 0000000..df8b185 --- /dev/null +++ b/docs/why.md @@ -0,0 +1,31 @@ +# Why txtai? + +![why](images/why.png#only-light) +![why](images/why-dark.png#only-dark) + +New vector databases, LLM frameworks and everything in between are sprouting up daily. Why build with txtai? + +- Up and running in minutes with [pip](../install/) or [Docker](../cloud/) +```python +# Get started in a couple lines +import txtai + +embeddings = txtai.Embeddings() +embeddings.index(["Correct", "Not what we hoped"]) +embeddings.search("positive", 1) +#[(0, 0.29862046241760254)] +``` +- Built-in API makes it easy to develop applications using your programming language of choice +```yaml +# app.yml +embeddings: + path: sentence-transformers/all-MiniLM-L6-v2 +``` +```bash +CONFIG=app.yml uvicorn "txtai.api:app" +curl -X GET "http://localhost:8000/search?query=positive" +``` +- Run local - no need to ship data off to disparate remote services +- Work with micromodels all the way up to large language models (LLMs) +- Low footprint - install additional dependencies and scale up when needed +- [Learn by example](../examples) - notebooks cover all available functionality diff --git a/docs/workflow/index.md b/docs/workflow/index.md new file mode 100644 index 0000000..f67c939 --- /dev/null +++ b/docs/workflow/index.md @@ -0,0 +1,205 @@ +# Workflow + +![workflow](../images/workflow.png#only-light) +![workflow](../images/workflow-dark.png#only-dark) + +Workflows are a simple yet powerful construct that takes a callable and returns elements. Workflows operate well with pipelines but can work with any callable object. Workflows are streaming and work on data in batches, allowing large volumes of data to be processed efficiently. + +Given that pipelines are callable objects, workflows enable efficient processing of pipeline data. Large language models typically work with smaller batches of data, workflows are well suited to feed a series of transformers pipelines. + +An example of the most basic workflow: + +```python +workflow = Workflow([Task(lambda x: [y * 2 for y in x])]) +list(workflow([1, 2, 3])) +``` + +This example multiplies each input value by 2 and returns transformed elements via a generator. + +Since workflows run as generators, output must be consumed for execution to occur. The following snippets show how output can be consumed. + +```python +# Small dataset where output fits in memory +list(workflow(elements)) + +# Large dataset +for output in workflow(elements): + function(output) + +# Large dataset where output is discarded +for _ in workflow(elements): + pass +``` + +Workflows are run with Python or configuration. Examples of both methods are shown below. + +## Example + +A full-featured example is shown below in Python. This workflow transcribes a set of audio files, translates the text into French and indexes the data. + +```python +from txtai import Embeddings +from txtai.pipeline import Transcription, Translation +from txtai.workflow import FileTask, Task, Workflow + +# Embeddings instance +embeddings = Embeddings({ + "path": "sentence-transformers/paraphrase-MiniLM-L3-v2", + "content": True +}) + +# Transcription instance +transcribe = Transcription() + +# Translation instance +translate = Translation() + +tasks = [ + FileTask(transcribe, r"\.wav$"), + Task(lambda x: translate(x, "fr")) +] + +# List of files to process +data = [ + "US_tops_5_million.wav", + "Canadas_last_fully.wav", + "Beijing_mobilises.wav", + "The_National_Park.wav", + "Maine_man_wins_1_mil.wav", + "Make_huge_profits.wav" +] + +# Workflow that translate text to French +workflow = Workflow(tasks) + +# Index data +embeddings.index((uid, text, None) for uid, text in enumerate(workflow(data))) + +# Search +embeddings.search("wildlife", 1) +``` + +## Configuration-driven example + +Workflows can also be defined with YAML configuration. + +```yaml +writable: true +embeddings: + path: sentence-transformers/paraphrase-MiniLM-L3-v2 + content: true + +# Transcribe audio to text +transcription: + +# Translate text between languages +translation: + +workflow: + index: + tasks: + - action: transcription + select: "\\.wav$" + task: file + - action: translation + args: ["fr"] + - action: index +``` + +```python +# Create and run the workflow +from txtai import Application + +# Create and run the workflow +app = Application("workflow.yml") +list(app.workflow("index", [ + "US_tops_5_million.wav", + "Canadas_last_fully.wav", + "Beijing_mobilises.wav", + "The_National_Park.wav", + "Maine_man_wins_1_mil.wav", + "Make_huge_profits.wav" +])) + +# Search +app.search("wildlife") +``` + +The code above executes a workflow defined in the file `workflow.yml. + +## LLM workflow example + +Workflows can connect multiple LLM prompting tasks together. + +```yaml +llm: + path: openai/gpt-oss-20b + +workflow: + llm: + tasks: + - task: template + template: | + Extract keywords for the following text. + + {text} + action: llm + - task: template + template: | + Translate the following text into French. + + {text} + action: llm +``` + +```python +from txtai import Application + +app = Application("workflow.yml") +list(app.workflow("llm", [ + """ + txtai is an open-source platform for semantic search + and workflows powered by language models. + """ +])) +``` + +Any txtai pipeline/workflow task can be connected in workflows with LLMs. + +```yaml +llm: + path: openai/gpt-oss-20b + +translation: + +workflow: + llm: + tasks: + - task: template + template: | + Extract keywords for the following text. + + {text} + action: llm + - action: translation + args: + - fr +``` + +See the following links for more information. + +- [Workflow Demo](https://huggingface.co/spaces/NeuML/txtai) +- [Workflow YAML Examples](https://huggingface.co/spaces/NeuML/txtai/tree/main/workflows) +- [Workflow YAML Guide](../api/configuration/#workflow) + +## Methods + +Workflows are callable objects. Workflows take an input of iterable data elements and output iterable data elements. + +### ::: txtai.workflow.Workflow.__init__ +### ::: txtai.workflow.Workflow.__call__ +### ::: txtai.workflow.Workflow.schedule + +## More examples + +Check out this [Workflow Quickstart Example](https://github.com/neuml/txtai/blob/master/examples/workflow_quickstart.py). See [this link](../examples/#workflows) for a full list of workflow examples. diff --git a/docs/workflow/schedule.md b/docs/workflow/schedule.md new file mode 100644 index 0000000..c09ed1f --- /dev/null +++ b/docs/workflow/schedule.md @@ -0,0 +1,67 @@ +# Schedule + +![schedule](../images/schedule.png#only-light) +![schedule](../images/schedule-dark.png#only-dark) + +Workflows can run on a repeating basis with schedules. This is suitable in cases where a workflow is run against a dynamically expanding input, like an API service or directory of files. + +The schedule method takes a cron expression, list of static elements (which dynamically expand i.e. API service, directory listing) and an optional maximum number of iterations. + +Below are a couple example cron expressions. + +```bash +# ┌─────────────── minute (0 - 59) +# | ┌───────────── hour (0 - 23) +# | | ┌─────────── day of the month (1 - 31) +# | | | ┌───────── month (1 - 12) +# | | | | ┌─────── day of the week (0 - 6) +# | | | | | ┌───── second (0 - 59) +# | | | | | | + * * * * * * # Run every second +0/5 * * * * # Run every 5 minutes + 0 0 1 * * # Run monthly on 1st + 0 0 1 1 * # Run on Jan 1 at 12am + 0 0 * * mon,wed # Run Monday and Wednesday +``` + +## Python +Simple workflow [scheduled](../#txtai.workflow.base.Workflow.schedule) with Python. + +```python +workflow = Workflow(tasks) +workflow.schedule("0/5 * * * *", elements) +``` + +See the link below for a more detailed example. + +| Notebook | Description | | +|:----------|:-------------|------:| +| [Workflow Scheduling](https://github.com/neuml/txtai/blob/master/examples/27_Workflow_scheduling.ipynb) | Schedule workflows with cron expressions | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/27_Workflow_scheduling.ipynb) | + +## Configuration +Simple workflow scheduled with configuration. + +```yaml +workflow: + index: + schedule: + cron: 0/5 * * * * + elements: [...] + tasks: [...] +``` + +```python +# Create and run the workflow +from txtai import Application + +# Create and run the workflow +app = Application("workflow.yml") + +# Wait for scheduled workflows +app.wait() +``` + +See the links below for more information on cron expressions. + +- [cron overview](https://en.wikipedia.org/wiki/Cron) +- [croniter - library used by txtai](https://github.com/kiorky/croniter) diff --git a/docs/workflow/task/console.md b/docs/workflow/task/console.md new file mode 100644 index 0000000..cbb7bf4 --- /dev/null +++ b/docs/workflow/task/console.md @@ -0,0 +1,33 @@ +# Console Task + +![task](../../images/task.png#only-light) +![task](../../images/task-dark.png#only-dark) + +The Console Task prints task inputs and outputs to standard output. This task is mainly used for debugging and can be added at any point in a workflow. + +## Example + +The following shows a simple example using this task as part of a workflow. + +```python +from txtai.workflow import FileTask, Workflow + +workflow = Workflow([ConsoleTask()]) +workflow(["Input 1", "Input2"]) +``` + +## Configuration-driven example + +This task can also be created with workflow configuration. + +```yaml +workflow: + tasks: + - task: console +``` + +## Methods + +Python documentation for the task. + +### ::: txtai.workflow.ConsoleTask.__init__ diff --git a/docs/workflow/task/export.md b/docs/workflow/task/export.md new file mode 100644 index 0000000..35348d8 --- /dev/null +++ b/docs/workflow/task/export.md @@ -0,0 +1,34 @@ +# Export Task + +![task](../../images/task.png#only-light) +![task](../../images/task-dark.png#only-dark) + +The Export Task exports task outputs to CSV or Excel. + +## Example + +The following shows a simple example using this task as part of a workflow. + +```python +from txtai.workflow import FileTask, Workflow + +workflow = Workflow([ExportTask()]) +workflow(["Input 1", "Input2"]) +``` + +## Configuration-driven example + +This task can also be created with workflow configuration. + +```yaml +workflow: + tasks: + - task: export +``` + +## Methods + +Python documentation for the task. + +### ::: txtai.workflow.ExportTask.__init__ +### ::: txtai.workflow.ExportTask.register diff --git a/docs/workflow/task/file.md b/docs/workflow/task/file.md new file mode 100644 index 0000000..c41e5b3 --- /dev/null +++ b/docs/workflow/task/file.md @@ -0,0 +1,33 @@ +# File Task + +![task](../../images/task.png#only-light) +![task](../../images/task-dark.png#only-dark) + +The File Task validates a file exists. It handles both file paths and local file urls. Note that this task _only_ works with local files. + +## Example + +The following shows a simple example using this task as part of a workflow. + +```python +from txtai.workflow import FileTask, Workflow + +workflow = Workflow([FileTask()]) +workflow(["/path/to/file", "file:///path/to/file"]) +``` + +## Configuration-driven example + +This task can also be created with workflow configuration. + +```yaml +workflow: + tasks: + - task: file +``` + +## Methods + +Python documentation for the task. + +### ::: txtai.workflow.FileTask.__init__ diff --git a/docs/workflow/task/image.md b/docs/workflow/task/image.md new file mode 100644 index 0000000..e96507e --- /dev/null +++ b/docs/workflow/task/image.md @@ -0,0 +1,33 @@ +# Image Task + +![task](../../images/task.png#only-light) +![task](../../images/task-dark.png#only-dark) + +The Image Task reads file paths, check the file is an image and opens it as an Image object. Note that this task _only_ works with local files. + +## Example + +The following shows a simple example using this task as part of a workflow. + +```python +from txtai.workflow import ImageTask, Workflow + +workflow = Workflow([ImageTask()]) +workflow(["image.jpg", "image.gif"]) +``` + +## Configuration-driven example + +This task can also be created with workflow configuration. + +```yaml +workflow: + tasks: + - task: image +``` + +## Methods + +Python documentation for the task. + +### ::: txtai.workflow.ImageTask.__init__ diff --git a/docs/workflow/task/index.md b/docs/workflow/task/index.md new file mode 100644 index 0000000..1d0a155 --- /dev/null +++ b/docs/workflow/task/index.md @@ -0,0 +1,84 @@ +# Tasks + +![task](../../images/task.png#only-light) +![task](../../images/task-dark.png#only-dark) + +Workflows execute tasks. Tasks are callable objects with a number of parameters to control the processing of data at a given step. While similar to pipelines, tasks encapsulate processing and don't perform signficant transformations on their own. Tasks perform logic to prepare content for the underlying action(s). + +A simple task is shown below. + +```python +Task(lambda x: [y * 2 for y in x]) +``` + +The task above executes the function above for all input elements. + +Tasks work well with pipelines, since pipelines are callable objects. The example below will summarize each input element. + +```python +summary = Summary() +Task(summary) +``` + +Tasks can operate independently but work best with workflows, as workflows add large-scale stream processing. + +```python +summary = Summary() +task = Task(summary) +task(["Very long text here"]) + +workflow = Workflow([task]) +list(workflow(["Very long text here"])) +``` + +Tasks can also be created with configuration as part of a workflow. + +```yaml +workflow: + tasks: + - action: summary +``` + +::: txtai.workflow.Task.__init__ + +## Multi-action task concurrency + +The default processing mode is to run actions sequentially. Multiprocessing support is already built in at a number of levels. Any of the GPU models will maximize GPU utilization for example and even in CPU mode, concurrency is utilized. But there are still use cases for task action concurrency. For example, if the system has multiple GPUs, the task runs external sequential code, or the task has a large number of I/O tasks. + +In addition to sequential processing, multi-action tasks can run either multithreaded or with multiple processes. The advantages of each approach are discussed below. + +- *multithreading* - no overhead of creating separate processes or pickling data. But Python can only execute a single thread due the GIL, so this approach won't help with CPU bound actions. This method works well with I/O bound actions and GPU actions. + +- *multiprocessing* - separate subprocesses are created and data is exchanged via pickling. This method can fully utilize all CPU cores since each process runs independently. This method works well with CPU bound actions. + +More information on multiprocessing can be found in the [Python documentation](https://docs.python.org/3/library/multiprocessing.html). + +## Multi-action task merges + +Multi-action tasks will generate parallel outputs for the input data. The task output can be merged together in a couple different ways. + +### ::: txtai.workflow.Task.hstack +### ::: txtai.workflow.Task.vstack +### ::: txtai.workflow.Task.concat + +## Extract task output columns + +With column-wise merging, each output row will be a tuple of output values for each task action. This can be fed as input to a downstream task and that task can have separate tasks work with each element. + +A simple example: + +```python +workflow = Workflow([Task(lambda x: [y * 3 for y in x], unpack=False, column=0)]) +list(workflow([(2, 8)])) +``` + +For the example input tuple of (2, 2), the workflow will only select the first element (2) and run the task against that element. + +```python +workflow = Workflow([Task([lambda x: [y * 3 for y in x], + lambda x: [y - 1 for y in x]], + unpack=False, column={0:0, 1:1})]) +list(workflow([(2, 8)])) +``` + +The example above applies a separate action to each input column. This simple construct can help build extremely powerful workflow graphs! diff --git a/docs/workflow/task/retrieve.md b/docs/workflow/task/retrieve.md new file mode 100644 index 0000000..31fddbc --- /dev/null +++ b/docs/workflow/task/retrieve.md @@ -0,0 +1,35 @@ +# Retrieve Task + +![task](../../images/task.png#only-light) +![task](../../images/task-dark.png#only-dark) + +The Retrieve Task connects to a url and downloads the content locally. This task is helpful when working with actions that require data to be available locally. + +## Example + +The following shows a simple example using this task as part of a workflow. + +```python +from txtai.workflow import RetrieveTask, Workflow + +workflow = Workflow([RetrieveTask(directory="/tmp")]) +workflow(["https://file.to.download", "/local/file/to/copy"]) +``` + +## Configuration-driven example + +This task can also be created with workflow configuration. + +```yaml +workflow: + tasks: + - task: retrieve + directory: /tmp +``` + +## Methods + +Python documentation for the task. + +### ::: txtai.workflow.RetrieveTask.__init__ +### ::: txtai.workflow.RetrieveTask.register diff --git a/docs/workflow/task/service.md b/docs/workflow/task/service.md new file mode 100644 index 0000000..ad6ccef --- /dev/null +++ b/docs/workflow/task/service.md @@ -0,0 +1,35 @@ +# Service Task + +![task](../../images/task.png#only-light) +![task](../../images/task-dark.png#only-dark) + +The Service Task extracts content from a http service. + +## Example + +The following shows a simple example using this task as part of a workflow. + +```python +from txtai.workflow import ServiceTask, Workflow + +workflow = Workflow([ServiceTask(url="https://service.url/action)]) +workflow(["parameter"]) +``` + +## Configuration-driven example + +This task can also be created with workflow configuration. + +```yaml +workflow: + tasks: + - task: service + url: https://service.url/action +``` + +## Methods + +Python documentation for the task. + +### ::: txtai.workflow.ServiceTask.__init__ +### ::: txtai.workflow.ServiceTask.register diff --git a/docs/workflow/task/storage.md b/docs/workflow/task/storage.md new file mode 100644 index 0000000..0045e15 --- /dev/null +++ b/docs/workflow/task/storage.md @@ -0,0 +1,33 @@ +# Storage Task + +![task](../../images/task.png#only-light) +![task](../../images/task-dark.png#only-dark) + +The Storage Task expands a local directory or cloud storage bucket into a list of URLs to process. + +## Example + +The following shows a simple example using this task as part of a workflow. + +```python +from txtai.workflow import StorageTask, Workflow + +workflow = Workflow([StorageTask()]) +workflow(["s3://path/to/bucket", "local://local/directory"]) +``` + +## Configuration-driven example + +This task can also be created with workflow configuration. + +```yaml +workflow: + tasks: + - task: storage +``` + +## Methods + +Python documentation for the task. + +### ::: txtai.workflow.StorageTask.__init__ diff --git a/docs/workflow/task/template.md b/docs/workflow/task/template.md new file mode 100644 index 0000000..4462e90 --- /dev/null +++ b/docs/workflow/task/template.md @@ -0,0 +1,35 @@ +# Template Task + +![task](../../images/task.png#only-light) +![task](../../images/task-dark.png#only-dark) + +The Template Task generates text from a template and task inputs. Templates can be used to prepare data for a number of tasks including generating large +language model (LLM) prompts. + +## Example + +The following shows a simple example using this task as part of a workflow. + +```python +from txtai.workflow import TemplateTask, Workflow + +workflow = Workflow([TemplateTask(template="This is a {text} task")]) +workflow([{"text": "template"}]) +``` + +## Configuration-driven example + +This task can also be created with workflow configuration. + +```yaml +workflow: + tasks: + - task: template + template: This is a {text} task +``` + +## Methods + +Python documentation for the task. + +### ::: txtai.workflow.TemplateTask.__init__ diff --git a/docs/workflow/task/url.md b/docs/workflow/task/url.md new file mode 100644 index 0000000..af0909f --- /dev/null +++ b/docs/workflow/task/url.md @@ -0,0 +1,33 @@ +# Url Task + +![task](../../images/task.png#only-light) +![task](../../images/task-dark.png#only-dark) + +The Url Task validates that inputs start with a url prefix. + +## Example + +The following shows a simple example using this task as part of a workflow. + +```python +from txtai.workflow import UrlTask, Workflow + +workflow = Workflow([UrlTask()]) +workflow(["https://file.to.download", "file:////local/file/to/copy"]) +``` + +## Configuration-driven example + +This task can also be created with workflow configuration. + +```yaml +workflow: + tasks: + - task: url +``` + +## Methods + +Python documentation for the task. + +### ::: txtai.workflow.UrlTask.__init__ diff --git a/docs/workflow/task/workflow.md b/docs/workflow/task/workflow.md new file mode 100644 index 0000000..e4c68d0 --- /dev/null +++ b/docs/workflow/task/workflow.md @@ -0,0 +1,23 @@ +# Workflow Task + +![task](../../images/task.png#only-light) +![task](../../images/task-dark.png#only-dark) + +The Workflow Task runs a workflow. Allows creating workflows of workflows. + +## Example + +The following shows a simple example using this task as part of a workflow. + +```python +from txtai.workflow import WorkflowTask, Workflow + +workflow = Workflow([WorkflowTask(otherworkflow)]) +workflow(["input data"]) +``` + +## Methods + +Python documentation for the task. + +### ::: txtai.workflow.WorkflowTask.__init__ diff --git a/examples/01_Introducing_txtai.ipynb b/examples/01_Introducing_txtai.ipynb new file mode 100644 index 0000000..60ca29e --- /dev/null +++ b/examples/01_Introducing_txtai.ipynb @@ -0,0 +1,1112 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "POWZoSJR6XzK" + }, + "source": [ + "# Introducing txtai\n", + "\n", + "[txtai](https://github.com/neuml/txtai) is an all-in-one AI framework for semantic search, LLM orchestration and language model workflows.\n", + "\n", + "The key component of txtai is an embeddings database, which is a union of vector indexes (sparse and dense), graph networks and relational databases.\n", + "\n", + "This foundation enables vector search and/or serves as a powerful knowledge source for large language model (LLM) applications.\n", + "\n", + "Build autonomous agents, retrieval augmented generation (RAG) processes, multi-model workflows and more.\n", + "\n", + "The following is a summary of key features:\n", + "\n", + "- 🔎 Vector search with SQL, object storage, topic modeling, graph analysis and multimodal indexing\n", + "- 📄 Create embeddings for text, documents, audio, images and video\n", + "- 💡 Pipelines powered by language models that run LLM prompts, question-answering, labeling, transcription, translation, summarization and more\n", + "- ↪️️ Workflows to join pipelines together and aggregate business logic. txtai processes can be simple microservices or multi-model workflows.\n", + "- 🤖 Agents that intelligently connect embeddings, pipelines, workflows and other agents together to autonomously solve complex problems\n", + "- ⚙️ Web and Model Context Protocol (MCP) APIs. Bindings available for [JavaScript](https://github.com/neuml/txtai.js), [Java](https://github.com/neuml/txtai.java), [Rust](https://github.com/neuml/txtai.rs) and [Go](https://github.com/neuml/txtai.go).\n", + "- 🔋 Batteries included with defaults to get up and running fast\n", + "- ☁️ Run local or scale out with container orchestration\n", + "\n", + "txtai is built with Python 3.10+, [Hugging Face Transformers](https://github.com/huggingface/transformers), [Sentence Transformers](https://github.com/UKPLab/sentence-transformers) and [FastAPI](https://github.com/tiangolo/fastapi). txtai is open-source under an Apache 2.0 license.\n", + "\n", + "> [NeuML](https://neuml.com) is the company behind txtai and we provide AI consulting services around our stack. [Schedule a meeting](https://cal.com/neuml/intro) or [send a message](mailto:info@neuml.com) to learn more.\n", + ">\n", + "> We're also building an easy and secure way to run hosted txtai applications with [txtai.cloud](https://txtai.cloud).\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qa_PPKVX6XzN" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "_cell_guid": "b1076dfc-b9ad-4769-8c92-a6c4dae69d19", + "_kg_hide-output": true, + "_uuid": "8f2839f25d086af736a60e9eeb907d3b93b6e0e5", + "id": "24q-1n5i6XzQ", + "trusted": true + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[graph]\n", + "\n", + "# Install translation pipeline dependencies for later examples\n", + "!pip install sentencepiece sacremoses staticvectors" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DLIjSzbq6Xzx" + }, + "source": [ + "# Semantic search\n", + "\n", + "Embeddings databases are the engine that delivers semantic search. Data is transformed into embeddings vectors where similar concepts will produce similar vectors. Indexes both large and small are built with these vectors. The indexes are used to find results that have the same meaning, not necessarily the same keywords.\n", + "\n", + "The basic use case for an embeddings database is building an approximate nearest neighbor (ANN) index for semantic search. The following example indexes a small number of text entries to demonstrate the value of semantic search.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "id": "QxX9EtIc6Xzg", + "trusted": true + }, + "outputs": [], + "source": [ + "%%capture\n", + "\n", + "from txtai import Embeddings\n", + "\n", + "# Works with a list, dataset or generator\n", + "data = [\n", + " \"US tops 5 million confirmed virus cases\",\n", + " \"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\",\n", + " \"Beijing mobilises invasion craft along coast as Taiwan tensions escalate\",\n", + " \"The National Park Service warns against sacrificing slower friends in a bear attack\",\n", + " \"Maine man wins $1M from $25 lottery ticket\",\n", + " \"Make huge profits without work, earn up to $100,000 a day\"\n", + "]\n", + "\n", + "# Create an embeddings\n", + "embeddings = Embeddings(path=\"sentence-transformers/nli-mpnet-base-v2\")" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cXfZtdHD6Xzy", + "outputId": "369b637e-1e1c-4229-f68e-92917be5fbd0", + "trusted": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Query Best Match\n", + "--------------------------------------------------\n", + "feel good story Maine man wins $1M from $25 lottery ticket\n", + "climate change Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\n", + "public health story US tops 5 million confirmed virus cases\n", + "war Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "wildlife The National Park Service warns against sacrificing slower friends in a bear attack\n", + "asia Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "lucky Maine man wins $1M from $25 lottery ticket\n", + "dishonest junk Make huge profits without work, earn up to $100,000 a day\n" + ] + } + ], + "source": [ + "# Create an index for the list of text\n", + "embeddings.index(data)\n", + "\n", + "print(\"%-20s %s\" % (\"Query\", \"Best Match\"))\n", + "print(\"-\" * 50)\n", + "\n", + "# Run an embeddings search for each query\n", + "for query in (\"feel good story\", \"climate change\", \"public health story\", \"war\", \"wildlife\", \"asia\", \"lucky\", \"dishonest junk\"):\n", + " # Extract uid of first result\n", + " # search result format: (uid, score)\n", + " uid = embeddings.search(query, 1)[0][0]\n", + "\n", + " # Print text\n", + " print(\"%-20s %s\" % (query, data[uid]))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kIMbLW0t6Xzw" + }, + "source": [ + "The example above shows that for all of the queries, the query text isn’t in the data. This is the true power of transformers models over token based search. What you get out of the box is 🔥🔥🔥!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6m7sYUj_AdOL" + }, + "source": [ + "# Updates and deletes\n", + "\n", + "Updates and deletes are supported for embeddings. The upsert operation will insert new data and update existing data\n", + "\n", + "The following section runs a query, then updates a value changing the top result and finally deletes the updated value to revert back to the original query results." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "2CERR0U2Ac8C", + "outputId": "0c1f4dd2-1319-410b-91a4-7753adba2c26" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Initial: Maine man wins $1M from $25 lottery ticket\n", + "After update: See it: baby panda born\n", + "After delete: Maine man wins $1M from $25 lottery ticket\n" + ] + } + ], + "source": [ + "# Run initial query\n", + "uid = embeddings.search(\"feel good story\", 1)[0][0]\n", + "print(\"Initial: \", data[uid])\n", + "\n", + "# Create a copy of data to modify\n", + "udata = data.copy()\n", + "\n", + "# Update data\n", + "udata[0] = \"See it: baby panda born\"\n", + "embeddings.upsert([(0, udata[0], None)])\n", + "\n", + "uid = embeddings.search(\"feel good story\", 1)[0][0]\n", + "print(\"After update: \", udata[uid])\n", + "\n", + "# Remove record just added from index\n", + "embeddings.delete([0])\n", + "\n", + "# Ensure value matches previous value\n", + "uid = embeddings.search(\"feel good story\", 1)[0][0]\n", + "print(\"After delete: \", udata[uid])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6TCVl6QA6Xz5" + }, + "source": [ + "# Persistence\n", + "\n", + "Embeddings can be saved to storage and reloaded." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "5gyO90Hc6Xz7", + "outputId": "5460fcd8-5b9f-4064-9ac3-f72e5db9ecf4", + "trusted": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\n" + ] + } + ], + "source": [ + "embeddings.save(\"index\")\n", + "\n", + "embeddings = Embeddings()\n", + "embeddings.load(\"index\")\n", + "\n", + "uid = embeddings.search(\"climate change\", 1)[0][0]\n", + "print(data[uid])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "giNZ_fHmqT8u" + }, + "source": [ + "# Hybrid search\n", + "\n", + "While dense vector indexes are by far the best option for semantic search systems, sparse keyword indexes can still add value. There may be cases where finding an exact match is important.\n", + "\n", + "Hybrid search combines the results from sparse and dense vector indexes for the best of both worlds." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "lclxiRFRqsFv", + "outputId": "3bd15b63-3bf4-4132-a819-0f560fce3f92" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Query Best Match\n", + "--------------------------------------------------\n", + "feel good story Maine man wins $1M from $25 lottery ticket\n", + "climate change Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\n", + "public health story US tops 5 million confirmed virus cases\n", + "war Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "wildlife The National Park Service warns against sacrificing slower friends in a bear attack\n", + "asia Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "lucky Maine man wins $1M from $25 lottery ticket\n", + "dishonest junk Make huge profits without work, earn up to $100,000 a day\n" + ] + } + ], + "source": [ + "# Create an embeddings\n", + "embeddings = Embeddings(hybrid=True, path=\"sentence-transformers/nli-mpnet-base-v2\")\n", + "\n", + "# Create an index for the list of text\n", + "embeddings.index(data)\n", + "\n", + "print(\"%-20s %s\" % (\"Query\", \"Best Match\"))\n", + "print(\"-\" * 50)\n", + "\n", + "# Run an embeddings search for each query\n", + "for query in (\"feel good story\", \"climate change\", \"public health story\", \"war\", \"wildlife\", \"asia\", \"lucky\", \"dishonest junk\"):\n", + " # Extract uid of first result\n", + " # search result format: (uid, score)\n", + " uid = embeddings.search(query, 1)[0][0]\n", + "\n", + " # Print text\n", + " print(\"%-20s %s\" % (query, data[uid]))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "d9beQSw-vhz8" + }, + "source": [ + "Same results as with semantic search. Let's run the same example with just a keyword index to view those results." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "WykNb8y3vohL", + "outputId": "5617e912-1014-495c-9dc9-e5729988d77f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[]\n", + "[(4, 0.5234998733628726)]\n" + ] + } + ], + "source": [ + "# Create an embeddings\n", + "embeddings = Embeddings(keyword=True)\n", + "\n", + "# Create an index for the list of text\n", + "embeddings.index(data)\n", + "\n", + "print(embeddings.search(\"feel good story\"))\n", + "print(embeddings.search(\"lottery\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "P0FLRsrmv2hB" + }, + "source": [ + "See that when the embeddings instance only uses a keyword index, it can't find semantic matches, only keyword matches." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0p3WCDniUths" + }, + "source": [ + "# Content storage\n", + "\n", + "Up to this point, all the examples are referencing the original data array to retrieve the input text. This works fine for a demo but what if you have millions of documents? In this case, the text needs to be retrieved from an external datastore using the id.\n", + "\n", + "Content storage adds an associated database (i.e. SQLite, DuckDB) that stores associated metadata with the vector index. The document text, additional metadata and additional objects can be stored and retrieved right alongside the indexed vectors." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "MOntBQIdVv-J", + "outputId": "c9d0d3e7-d7b4-4421-f63d-402db6918cca" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Maine man wins $1M from $25 lottery ticket\n" + ] + } + ], + "source": [ + "# Create embeddings with content enabled. The default behavior is to only store indexed vectors.\n", + "embeddings = Embeddings(path=\"sentence-transformers/nli-mpnet-base-v2\", content=True, objects=True)\n", + "\n", + "# Create an index for the list of text\n", + "embeddings.index(data)\n", + "\n", + "print(embeddings.search(\"feel good story\", 1)[0][\"text\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hHGvhZm-ZTzL" + }, + "source": [ + "The only change above is setting the *content* flag to True. This enables storing text and metadata content (if provided) alongside the index. Note how the text is pulled right from the query result!\n", + "\n", + "Let's add some metadata." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BYWUFBUGyKyY" + }, + "source": [ + "# Query with SQL\n", + "\n", + "When content is enabled, the entire dictionary is stored and can be queried. In addition to vector queries, txtai accepts SQL queries. This enables combined queries using both a vector index and content stored in a database backend." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "aPH-dnV2ZuL1", + "outputId": "c563060c-d292-4b19-aa64-f4c629008cdb" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[{'text': 'The National Park Service warns against sacrificing slower friends in a bear attack', 'score': 0.3151372969150543}]\n", + "[{'text': 'Maine man wins $1M from $25 lottery ticket', 'length': 42, 'score': 0.08329025655984879}]\n", + "[{'count(*)': 6, 'min(length)': 39, 'max(length)': 94, 'sum(length)': 387}]\n" + ] + } + ], + "source": [ + "# Create an index for the list of text\n", + "embeddings.index([{\"text\": text, \"length\": len(text)} for text in data])\n", + "\n", + "# Filter by score\n", + "print(embeddings.search(\"select text, score from txtai where similar('hiking danger') and score >= 0.15\"))\n", + "\n", + "# Filter by metadata field 'length'\n", + "print(embeddings.search(\"select text, length, score from txtai where similar('feel good story') and score >= 0.05 and length >= 40\"))\n", + "\n", + "# Run aggregate queries\n", + "print(embeddings.search(\"select count(*), min(length), max(length), sum(length) from txtai\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "oH4Yd9BOlo5u" + }, + "source": [ + "This example above adds a simple additional field, text length.\n", + "\n", + "Note the second query is filtering on the metadata field length along with a `similar` query clause. This gives a great blend of vector search with traditional filtering to help identify the best results." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lGmiYXyqyjtQ" + }, + "source": [ + "# Object storage\n", + "\n", + "In addition to metadata, binary content can also be associated with documents. The example below downloads an image, upserts it along with associated text into the embeddings index." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 307 + }, + "id": "Ef4-Gd8ZtzUF", + "outputId": "aaa811e8-ee3a-43ed-dab3-994ca6014a64" + }, + "outputs": [ + { + "data": { + "image/gif": "R0lGODlhlwQ4AvUAABITFMzMzMTExKurrGtsbCYnKJOTlIeIiHN0dLOzs0pLTJeYmHt7fFdXWLy8vJubm6SkpBobHBUWGBcZGBAqHyUdKPRDNmglIcw7MOxBNYcsJqczKts+Mrg2LeqA/Mxx3HREfaVdsth36Y1RmLFjvuN89bxpyw80IxIbGgOp9AaQzgtdgwSg5wlwnwd+tADmdgl8RQS2YAHccgeVUAPEZgWsWwLLaf/rO2pjItzLNe3bOIV8J7CjLsi4MryuMAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQEFAD/ACwAAAAAlwQ4AgAF/yAgjmRpnmiqrmzrvnAsz3Rt33iu73zv/8CgcEgsGo/IpHLJbDqf0Kh0Sq1ar9isdsvter/gsHhMLpvP6LR6zW673/C4fE6v2+/4vH7P7/v/gIGCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp+goaKjpKWmp6ipqqusra6vsLGys7S1tre4ubq7vL2+v8DBwsPExcbHyFwBy8zNzcnQ0dLT1KTO18zV2tvc3d6A2Njf4+Tl5ufK4c7o7O3u7/A46uvx9fb3+Obzz/n9/v8Age3LFrCgwYMI3wwI0KDKwGUJI0qceK6AAwEF2ixsyMJiggg2GuwbAOBhAIooU/+qhGaAoZuNLggEOBDyok1mNh+UfLiyp8+fuRQEIPnSpYsERnGIhGDCJNCnUKOmepCUDcyYM3UsbcpTqtevYC9ZxJhCwQGkAgYcUHCiAdW0CDKWKHBWwLIDckccaGYAQIOFafMCKIAAcIK1JGDWPSyYxNjGNLaWcBq2suXLh2T2RSFy2QC0mxN7hmBT8Ni0pJexHWF2cWcDpEl09nxXtEzUnimgoMrxhmQSlDELH07cTksCKZAiH1Gg8V4IeREMnUCCgODbJ07TNDEWL2vBgB/IFRpguQnp231Pn9y1uPv38M1chDzCLn3mEEuQ7o0CKeTTEpywV2j95adXADq1tZ7/ekyxN1B8EEYoIRbL3AcAVQtYiN55WamwQFUijMUfcCCasFB6Ivw2l4E1NUjigxPGKOOMRjyWQgHh0deSOgQSBlgzqzlWYXZDqnAVawuueJJSSYoQHI1QRiklDTaq8JdnQYpAmloLdOnlAgggOdQBCJSJVJYhFqkkdUaCKNRHRAZg4QsqOtnelHjmqaeQS7KgQGrmXVgeCxcFKiiag6nJ55wAHJlik/j12SJXMO5p6aVQKroCdiNsqIJHJpZYJVeI6ucmpGkKkEOdO1WK6auwwmffC6PWasKbSiJq64EonuCoX6gCSxSDlO4T67HICkeVocxZVx2LgiaQJWFyiSim/6iaihkXc/z9iusJnhLroLHJlmtuVOEqmQAEqZW4owPsXiTngUOldkBLQRJwb2pqMTAiALfRhqBoI34b6r8zsPrkuQw3PNGoJRDwAFqMcVZXWojJxm9D0gWpDpxzXXxYlt6iumvCkC7s8MosA7RfIJoNoXLLNNcMD6t7+CfznTb37PM5+P4R886u/mz00dtAnAeoRMyM9NNQRw2D01JXbbXVVF+t9dY+Z83112CHLfbYZJdt9tlop6322my37fbbcMct99x012333XjnrffefPft99+ABy744IQXbvjhiCeu+OKMN+7445BHLvnklFdu+eWYZ6755px37vnnoIcu+v/opJdu+umop6766qy37vrrsMcu++y012777bjnrvvuvPfu++/ABy/88MQXb/zxyCev/PLMN+/889BHL/301Fdv/fXYZ6/99tx37/334Icv/vjkl2/++einr/767Lfv/vvwxy///PTXb//9+Oev//789+///wAMoAAHSMACGvCACEygAhfIwAY68IEQjKAEJ0jBClrwghjMoAY3yMEOevCDIAyhCEdIwhKa8IQoTKEKV8jCFrrwhTCMoQxnSMMa2vCGOMyhDnfIwx768IdADKIQh0jEIhrxiEhMohKXyMQmOvGJUIyiFKdIxSpa8YpYzKIWt8jFLnrxi2AMoxjHSMb/MprxjGhMoxrXyMY2uvGNcIyjHOdIxzra8Y54zKMe98jHPvrxj4AMpCAHSchCGvKQiEykIhfJyEY68pGQjKQkJ0nJSlrykpjMpCY3yclOevKToAylKEdJylKa8pSoTKUqV8nKVrrylbCMpSxnScta2vKWuMylLnfJy1768pfADKYwh0nMYhrzmMhMpjKXycxmOvOZ0IymNKdJzWpa85rYzKY2t8nNbnrzm+AMpzjHSc5ymvOc6EynOtfJzna6853wjKc850nPetrznvjMpz73yc9++vOfAA2oQAdK0IIa9KAITahCF8rQhjr0oRCNqEQnStGKWvSiGM2oRjfK0Y56//SjIA2pSEdK0pKa9KQoTalKV8rS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", + "text/plain": [ + "" + ] + }, + "execution_count": 9, + "metadata": { + "image/gif": { + "width": 600 + } + }, + "output_type": "execute_result" + } + ], + "source": [ + "import urllib\n", + "\n", + "from IPython.display import Image\n", + "\n", + "# Get an image\n", + "request = urllib.request.urlopen(\"https://raw.githubusercontent.com/neuml/txtai/master/demo.gif\")\n", + "\n", + "# Upsert new record having both text and an object\n", + "embeddings.upsert([(\"txtai\", {\"text\": \"txtai executes machine-learning workflows to transform data and build AI-powered semantic search applications.\", \"object\": request.read()}, None)])\n", + "\n", + "# Query txtai for the most similar result to \"machine learning\" and get associated object\n", + "result = embeddings.search(\"select object from txtai where similar('machine learning') limit 1\")[0][\"object\"]\n", + "\n", + "# Display image\n", + "Image(result.getvalue(), width=600)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "boEY-GSUsi_L" + }, + "source": [ + "# Topic modeling\n", + "\n", + "Topic modeling is enabled via semantic graphs. Semantic graphs, also known as knowledge graphs or semantic networks, build a graph network with semantic relationships connecting the nodes. In txtai, they can take advantage of the relationships inherently learned within an embeddings index." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "k7eRzturtCwr", + "outputId": "794d10d6-8463-4e8c-c1d1-0af59e97e59f" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'topic': 'virus_confirmed_us_million',\n", + " 'category': 'health',\n", + " 'text': 'US tops 5 million confirmed virus cases'},\n", + " {'topic': 'collapsed_has_forming_ice',\n", + " 'category': 'climate',\n", + " 'text': \"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\"},\n", + " {'topic': 'along_escalate_mobilises_tensions',\n", + " 'category': 'world politics',\n", + " 'text': 'Beijing mobilises invasion craft along coast as Taiwan tensions escalate'}]" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Create embeddings with a graph index\n", + "embeddings = Embeddings(\n", + " path=\"sentence-transformers/nli-mpnet-base-v2\",\n", + " content=True,\n", + " functions=[\n", + " {\"name\": \"graph\", \"function\": \"graph.attribute\"},\n", + " ],\n", + " expressions=[\n", + " {\"name\": \"category\", \"expression\": \"graph(indexid, 'category')\"},\n", + " {\"name\": \"topic\", \"expression\": \"graph(indexid, 'topic')\"},\n", + " ],\n", + " graph={\n", + " \"topics\": {\n", + " \"level\": \"first\",\n", + " \"categories\": [\"health\", \"climate\", \"finance\", \"world politics\"]\n", + " }\n", + " }\n", + ")\n", + "\n", + "embeddings.index(data)\n", + "embeddings.search(\"select topic, category, text from txtai\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0VTB-LjExpfv" + }, + "source": [ + "When a graph index is enabled, topics are assigned to each of the entries in the embeddings instance. Topics are dynamically created using a sparse index over graph nodes grouped by [community detection algorithms](https://en.wikipedia.org/wiki/Community_structure).\n", + "\n", + "Topic categories are also be derived as shown above." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0aOJOxE3y4vD" + }, + "source": [ + "# Subindexes\n", + "\n", + "Subindexes can be configured for an embeddings. A single embeddings instance can have multiple subindexes each with different configurations.\n", + "\n", + "We'll build an embeddings index having both a keyword and dense index to demonstrate." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "id": "TOwwKw3w_eJG" + }, + "outputs": [], + "source": [ + "# Create embeddings with subindexes\n", + "embeddings = Embeddings(\n", + " content=True,\n", + " defaults=False,\n", + " indexes={\n", + " \"keyword\": {\n", + " \"keyword\": True\n", + " },\n", + " \"dense\": {\n", + " \"path\": \"sentence-transformers/nli-mpnet-base-v2\"\n", + " }\n", + " }\n", + ")\n", + "embeddings.index(data)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "M0HKb9mzxkL-", + "outputId": "16200bfc-715a-4dfe-89c4-cd6476c0425a" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "embeddings.search(\"feel good story\", limit=1, index=\"keyword\")" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-SnA1s0kxw9x", + "outputId": "9f6d7cc6-7325-4ac4-ded0-aa0502d088e0" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'id': '4',\n", + " 'text': 'Maine man wins $1M from $25 lottery ticket',\n", + " 'score': 0.08329025655984879}]" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "embeddings.search(\"feel good story\", limit=1, index=\"dense\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "7vFe31Gax-0r" + }, + "source": [ + "Once again, this example demonstrates the difference between keyword and semantic search. The first search call uses the defined keyword index, the second uses the dense vector index." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1M_OMEndzgnG" + }, + "source": [ + "# LLM orchestration\n", + "\n", + "txtai is an all-in-one AI framework. txtai supports building autonomous agents, retrieval augmented generation (RAG), chat with your data, pipelines and workflows that interface with large language models (LLMs).\n", + "\n", + "The [RAG pipeline](https://neuml.github.io/txtai/pipeline/text/rag/) is txtai's spin on retrieval augmented generation (RAG). This pipeline extracts knowledge from content by joining a prompt, context data store and generative model together.\n", + "\n", + "The following example shows how a large language model (LLM) can use an embeddings database for context." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "vWX9Q6Iy0X3Z", + "outputId": "82f9f9cc-b7fb-4ee9-cd35-9b00c022f83a" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'answer': 'Canada is having issues with climate change.',\n", + " 'reference': 'da633124-33ff-58d6-8ecb-14f7a44c042a'}" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from txtai import RAG\n", + "\n", + "# Create embeddings\n", + "embeddings = Embeddings(path=\"sentence-transformers/nli-mpnet-base-v2\", content=True, autoid=\"uuid5\")\n", + "\n", + "# Create an index for the list of text\n", + "embeddings.index(data)\n", + "\n", + "# RAG Prompt Template\n", + "template = \"\"\"\n", + " Answer the following question using the provided context.\n", + "\n", + " Question:\n", + " {question}\n", + "\n", + " Context:\n", + " {context}\n", + "\"\"\"\n", + "\n", + "# Create and run RAG instance\n", + "rag = RAG(embeddings, \"Qwen/Qwen3-0.6B\", template=template, output=\"reference\")\n", + "rag(\"What country is having issues with climate change?\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lqsZreJQuSfO" + }, + "source": [ + "The logic above first builds an embeddings index. It then loads a LLM and uses the embeddings index to drive a LLM prompt.\n", + "\n", + "The RAG pipeline can optionally return a reference to the id of the best matching record with the answer. That id can be used to resolve the full answer reference. Note that the embeddings above used an [uuid autosequence](https://neuml.github.io/txtai/embeddings/configuration/general/#autoid)." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ioC-gY4wwWVQ", + "outputId": "d6eab14a-83cd-434c-faa8-2afe285e842b" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'id': 'da633124-33ff-58d6-8ecb-14f7a44c042a',\n", + " 'text': \"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\"}]" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "uid = rag(\"What country is having issues with climate change?\")[\"reference\"]\n", + "embeddings.search(f\"select id, text from txtai where id = '{uid}'\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "fwVMGqV2nHcP" + }, + "source": [ + "LLM inference can also be run standalone." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 36 + }, + "id": "NAFMSJO-k8qW", + "outputId": "c2b07b49-f50d-4f74-a2fe-17bc699a91f1" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'One of the most popular and iconic places to visit in Washington, DC is the National Mall.'" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from txtai import LLM\n", + "\n", + "# Default LLM is granite-4.0-350m\n", + "# Supports any LLM (Hugging Face, llama.cpp, Ollama, vLLM, OpenAI, Claude etc)\n", + "# See https://neuml.github.io/txtai/pipeline/llm/llm\n", + "llm = LLM()\n", + "llm(\"Say the name of 1 place to visit in Washington, DC\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ekRIFk4uuoLN" + }, + "source": [ + "# Language model workflows\n", + "\n", + "Language model workflows, also known as semantic workflows, connect language models together to build intelligent applications.\n", + "\n", + "Workflows can run right alongside an embeddings instance, similar to a stored procedure in a relational database. Workflows can be written in either Python or YAML. We'll demonstrate how to write a workflow with YAML." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "JFzSs_Wa012D", + "outputId": "055e4f6d-a324-47ce-e3be-5aae06b28651" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Writing embeddings.yml\n" + ] + } + ], + "source": [ + "%%writefile embeddings.yml\n", + "\n", + "# Embeddings instance\n", + "writable: true\n", + "embeddings:\n", + " path: sentence-transformers/nli-mpnet-base-v2\n", + " content: true\n", + " functions:\n", + " - {name: translation, argcount: 2, function: translation}\n", + "\n", + "# Translation pipeline\n", + "translation:\n", + "\n", + "# Workflow definitions\n", + "workflow:\n", + " search:\n", + " tasks:\n", + " - search\n", + " - action: translation\n", + " args:\n", + " target: fr\n", + " task: template\n", + " template: \"{text}\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2WU0fCZasVNf" + }, + "source": [ + "The workflow above loads an embeddings index and defines a search workflow. The search workflow runs a search and then passes the results to a translation pipeline. The translation pipeline translates results to French." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ySOK3HDK1nOZ", + "outputId": "551f425d-8c99-4705-8dc8-7e23458a3ed5" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Maine homme gagne $1M à partir de $25 billet de loterie']" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from txtai import Application\n", + "\n", + "# Build index\n", + "app = Application(\"embeddings.yml\")\n", + "app.add(data)\n", + "app.index()\n", + "\n", + "# Run workflow\n", + "list(app.workflow(\"search\", [\"select text from txtai where similar('feel good story') limit 1\"]))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rhxBBaUO4znH" + }, + "source": [ + "SQL functions, in some cases, can accomplish the same thing as a workflow. The function below runs the translation pipeline as a function." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "hJAC430s4yIV", + "outputId": "8e0c4d6a-42d4-45e8-e79a-7872639c5512" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'text': 'Maine homme gagne $1M à partir de $25 billet de loterie'}]" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "app.search(\"select translation(text, 'fr') text from txtai where similar('feel good story') limit 1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "u5nHeC8MpX7k" + }, + "source": [ + "LLM chains with templates are also possible with workflows. Workflows are self-contained, they operate both with and without an associated embeddings instance. The following workflow uses a LLM to conditionally translate text to French and then detect the language of the text." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "zfwpSmTLnVU8", + "outputId": "154a34ff-2919-415b-a5b7-fb7c9f5b9cf1" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Writing workflow.yml\n" + ] + } + ], + "source": [ + "%%writefile workflow.yml\n", + "\n", + "llm:\n", + " path: Qwen/Qwen3-4B-Instruct-2507\n", + "\n", + "workflow:\n", + " chain:\n", + " tasks:\n", + " - task: template\n", + " template: Translate text '{statement}' to {language} if the text is English, otherwise keep the original text\n", + " action: llm\n", + " - task: template\n", + " template: What language is the following text. Only print the answer? {text}\n", + " action: llm" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "8mo2XSr9nXJH", + "outputId": "b9775570-4dd1-486e-f0c8-62f94fdb85b1" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['French', 'German']" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "inputs = [\n", + " {\"statement\": \"Hello, how are you\", \"language\": \"French\"},\n", + " {\"statement\": \"Hallo, wie geht's dir\", \"language\": \"French\"}\n", + "]\n", + "\n", + "app = Application(\"workflow.yml\")\n", + "list(app.workflow(\"chain\", inputs))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "aDIF3tYt6X0O" + }, + "source": [ + "# Wrapping up\n", + "\n", + "AI is advancing at a rapid pace. Things not possible even a year ago are now possible. This notebook introduced txtai, an all-in-one AI framework. The possibilities are limitless and we’re excited to see what can be built on top of txtai!\n", + "\n", + "Visit the links below for more.\n", + "\n", + "[GitHub](https://github.com/neuml/txtai) | [Documentation](https://neuml.github.io/txtai) | [Examples](https://neuml.github.io/txtai/examples/)" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "gpuType": "T4", + "provenance": [] + }, + "kernelspec": { + "display_name": "local", + "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", + "version": "3.10.19" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/examples/02_Build_an_Embeddings_index_with_Hugging_Face_Datasets.ipynb b/examples/02_Build_an_Embeddings_index_with_Hugging_Face_Datasets.ipynb new file mode 100644 index 0000000..d94bf0b --- /dev/null +++ b/examples/02_Build_an_Embeddings_index_with_Hugging_Face_Datasets.ipynb @@ -0,0 +1,292 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "LjmhJ4ad9kBL" + }, + "source": [ + "# Build an Embeddings index with Hugging Face Datasets\n", + "\n", + "This notebook shows how txtai can index and search with Hugging Face's [Datasets](https://github.com/huggingface/datasets) library. Datasets opens access to a large and growing list of publicly available datasets. Datasets has functionality to select, transform and filter data stored in each dataset.\n", + "\n", + "In this example, txtai will be used to index and query a dataset.\n", + "\n", + "**Make sure to select a GPU runtime when running this notebook**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8tLWvo9v-Q0u" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies. Also install `datasets`." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "Fa5BCjMFqVKE" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai\n", + "!pip install datasets" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hOdEv8MH-e5h" + }, + "source": [ + "# Load dataset and build a txtai index\n", + "\n", + "In this example, we'll load the `ag_news` dataset, which is a collection of news article headlines. This only takes a single line of code!\n", + "\n", + "Next, txtai will index the first 10,000 rows of the dataset. A sentence similarity model is used to compute sentence embeddings. sentence-transformers has a number of [pre-trained models](https://huggingface.co/models?pipeline_tag=sentence-similarity) that can be swapped in.\n", + "\n", + "In addition to the embeddings index, we'll also create a Similarity instance to re-rank search hits for relevancy. " + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "3hYRk9JnsM0J" + }, + "source": [ + "%%capture\n", + "from datasets import load_dataset\n", + "\n", + "from txtai.embeddings import Embeddings\n", + "from txtai.pipeline import Similarity\n", + "\n", + "def stream(dataset, field, limit):\n", + " index = 0\n", + " for row in dataset:\n", + " yield (index, row[field], None)\n", + " index += 1\n", + "\n", + " if index >= limit:\n", + " break\n", + "\n", + "def search(query):\n", + " return [(result[\"score\"], result[\"text\"]) for result in embeddings.search(query, limit=50)]\n", + "\n", + "def ranksearch(query):\n", + " results = [text for _, text in search(query)]\n", + " return [(score, results[x]) for x, score in similarity(query, results)]\n", + "\n", + "# Load HF dataset\n", + "dataset = load_dataset(\"ag_news\", split=\"train\")\n", + "\n", + "# Create embeddings model, backed by sentence-transformers & transformers, enable content storage\n", + "embeddings = Embeddings({\"path\": \"sentence-transformers/paraphrase-MiniLM-L3-v2\", \"content\": True})\n", + "embeddings.index(stream(dataset, \"text\", 10000))\n", + "\n", + "# Create similarity instance for re-ranking\n", + "similarity = Similarity(\"valhalla/distilbart-mnli-12-3\")" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "LBhHcX6eFmGI" + }, + "source": [ + "# Search the dataset\n", + "\n", + "Now that an index is ready, let's search the data! The following section runs a series of queries and show the results. Like basic search engines, txtai finds token matches. But the real power of txtai is finding semantically similar results.\n", + "\n", + "sentence-transformers has a great overview on [information retrieval](https://www.sbert.net/examples/applications/information-retrieval/README.html) that is well worth a read. " + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "YVmbiY92vxEO", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "outputId": "85f5e0ad-14ba-4642-aed6-13c14a710d68" + }, + "source": [ + "from IPython.core.display import display, HTML\n", + "\n", + "def table(query, rows):\n", + " html = \"\"\"\n", + " \n", + " \"\"\"\n", + "\n", + " html += \"

%s

\" % (query)\n", + " for score, text in rows:\n", + " html += \"\" % (score, text)\n", + " html += \"
ScoreText
%.4f%s
\"\n", + "\n", + " display(HTML(html))\n", + "\n", + "for query in [\"Positive Apple reports\", \"Negative Apple reports\", \"Best planets to explore for life\", \"LA Dodgers good news\", \"LA Dodgers bad news\"]:\n", + " table(query, ranksearch(query)[:2])\n" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + " \n", + "

Positive Apple reports

ScoreText
0.9941Apple's iPod a Huge Hit in Japan The iPod is proving a colossal hit on the Japanese electronics and entertainment giant's home ground. The tiny white machine is catching on as a fashion statement and turning into a cultural icon here, much the same way it won a fanatical following in the United States.
0.9886Apple tops US consumer satisfaction Recent data published by the American Customer Satisfaction Index (ACSI) shows Apple leading the consumer computer industry with the the highest customer satisfaction.
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + " \n", + "

Negative Apple reports

ScoreText
0.9847Apple Recalls 28,000 Faulty Batteries Sold with 15-inch PowerBook Apple has had to recall up to 28,000 notebook batteries that were sold for use with their 15-inch PowerBook. Apple reports that faulty batteries sold between January 2004 and August 2004 can overheat and pose a fire hazard.
0.9795Apple Announces Voluntary Recall of Powerbook Batteries Apple, in cooperation with the US Consumer Product Safety Commission (CPSC), announced Thursday a voluntary recall of 15 quot; Aluminum PowerBook batteries. The batteries being recalled could potentially overheat, though no injuries relating ...
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + " \n", + "

Best planets to explore for life

ScoreText
0.9110Tiny 'David' Telescope Finds 'Goliath' Planet A newfound planet detected by a small, 4-inch-diameter telescope demonstrates that we are at the cusp of a new age of planet discovery. Soon, new worlds may be located at an accelerating pace, bringing the detection of the first Earth-sized world one step closer.
0.8838Venus: Inhabited World? by Harry Bortman In part 1 of this interview with Astrobiology Magazine editor Henry Bortman, planetary scientist David Grinspoon explained how Venus evolved from a wet planet similar to Earth to the scorching hot, dried-out furnace of today. In part 2, Grinspoon discusses the possibility of life on Venus...
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + " \n", + "

LA Dodgers good news

ScoreText
0.9961Dodgers 7, Braves 4 Los Angeles, Ca. -- Shawn Green belted a grand slam and a solo homer as Los Angeles beat Mike Hampton and the Atlanta Braves 7-to-4 Saturday afternoon.
0.9928MLB: Los Angeles 7, Atlanta 4 Shawn Green hit two home runs Saturday, including a grand slam, to lead the Los Angeles Dodgers to a 7-4 victory over the Atlanta Braves.
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + " \n", + "

LA Dodgers bad news

ScoreText
0.9880Expos Keep Dodgers at Bay With 8-7 Win (AP) AP - Giovanni Carrara walked Juan Rivera with the bases loaded and two outs in the ninth inning Monday night, spoiling Los Angeles' six-run comeback and handing the Montreal Expos an 8-7 victory over the Dodgers.
0.9671Gagne blows his 2d save Pinch-hitter Lenny Harris delivered a three-run double off Eric Gagne with two outs in the ninth, rallying the Florida Marlins past the Dodgers, 6-4, last night in Los Angeles.
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + } + ] + } + ] +} \ No newline at end of file diff --git a/examples/03_Build_an_Embeddings_index_from_a_data_source.ipynb b/examples/03_Build_an_Embeddings_index_from_a_data_source.ipynb new file mode 100644 index 0000000..155fc99 --- /dev/null +++ b/examples/03_Build_an_Embeddings_index_from_a_data_source.ipynb @@ -0,0 +1,422 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "WDbhGHtG8jFE" + }, + "source": [ + "# Build an Embeddings index from a data source\n", + "\n", + "In Part 1, we gave a general overview of txtai, the backing technology and examples of how to use it for similarity searches. Part 2 covered an embedding index with a larger dataset.\n", + "\n", + "For real world large-scale use cases, data is often stored in a database (Elasticsearch, SQL, MongoDB, files, etc). Here we'll show how to read from SQLite, build an Embedding index and run queries against the generated Embeddings index.\n", + "\n", + "This example covers functionality found in the [paperai](https://github.com/neuml/paperai) library. See that library for a full solution that can be used with the dataset discussed below." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "UQ0fCwXn9bcH" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "czPYSA2Q9ZHO" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SN9SCZKQ9fJF" + }, + "source": [ + "# Download data\n", + "\n", + "This example is going to work off a subset of the [CORD-19](https://www.semanticscholar.org/cord19) dataset. COVID-19 Open Research Dataset (CORD-19) is a free resource of scholarly articles, aggregated by a coalition of leading research groups, covering COVID-19 and the coronavirus family of viruses.\n", + "\n", + "The following download is a SQLite database generated from a [Kaggle notebook](https://www.kaggle.com/davidmezzetti/cord-19-slim/output). More information on this data format, can be found in the [CORD-19 Analysis](https://www.kaggle.com/davidmezzetti/cord-19-analysis-with-sentence-embeddings) notebook." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "TONQ4_Kv9dtd" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!wget https://github.com/neuml/txtai/releases/download/v1.1.0/tests.gz\n", + "!gunzip tests.gz\n", + "!mv tests articles.sqlite" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_UxcC1-JGH-d" + }, + "source": [ + "# Build an embeddings index\n", + "\n", + "The following steps build an embeddings index using a vector model designed for medical papers, [PubMedBERT Embeddings](https://huggingface.co/NeuML/pubmedbert-base-embeddings)." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "5PrrxGRPGHqX", + "outputId": "61bf7211-6757-4147-8f2f-e4d1ebe58e11" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Iterated over 21499 total rows\n" + ] + } + ], + "source": [ + "import sqlite3\n", + "\n", + "import regex as re\n", + "\n", + "from txtai import Embeddings\n", + "\n", + "def stream():\n", + " # Connection to database file\n", + " db = sqlite3.connect(\"articles.sqlite\")\n", + " cur = db.cursor()\n", + "\n", + " # Select tagged sentences without a NLP label. NLP labels are set for non-informative sentences.\n", + " cur.execute(\"SELECT Id, Name, Text FROM sections WHERE (labels is null or labels NOT IN ('FRAGMENT', 'QUESTION')) AND tags is not null\")\n", + "\n", + " count = 0\n", + " for row in cur:\n", + " # Unpack row\n", + " uid, name, text = row\n", + "\n", + " # Only process certain document sections\n", + " if not name or not re.search(r\"background|(?\n", + " \n", + " \n", + " Title\n", + " Published\n", + " Reference\n", + " Match\n", + " \n", + " \n", + " \n", + " \n", + " Management of osteoarthritis during COVID‐19 pandemic\n", + " 2020-05-21 00:00:00\n", + " https://doi.org/10.1002/cpt.1910\n", + " Indeed, risk factors are sex, obesity, genetic factors and mechanical factors (3) .\n", + " \n", + " \n", + " Does apolipoprotein E genotype predict COVID-19 severity?\n", + " 2020-04-27 00:00:00\n", + " https://doi.org/10.1093/qjmed/hcaa142\n", + " Risk factors associated with subsequent death include older age, hypertension, diabetes, ischemic heart disease, obesity and chronic lung disease; however, sometimes there are no obvious risk factors .\n", + " \n", + " \n", + " Prevalence and Impact of Myocardial Injury in Patients Hospitalized with COVID-19 Infection\n", + " 2020-04-24 00:00:00\n", + " http://medrxiv.org/cgi/content/short/2020.04.20.20072702v1?rss=1\n", + " This risk was consistent across patients stratified by history of CVD, risk factors but no CVD, and neither CVD nor risk factors.\n", + " \n", + " \n", + " COVID-19 and associations with frailty and multimorbidity: a prospective analysis of UK Biobank participants\n", + " 2020-07-23 00:00:00\n", + " https://www.ncbi.nlm.nih.gov/pubmed/32705587/\n", + " BACKGROUND: Frailty and multimorbidity have been suggested as risk factors for severe COVID-19 disease.\n", + " \n", + " \n", + " Risk Stratification for Healthcare workers during the CoViD-19 Pandemic; using demographics, co-morbid disease and clinical domain in order to assign clinical duties\n", + " 2020-05-09 00:00:00\n", + " http://medrxiv.org/cgi/content/short/2020.05.05.20091967v1?rss=1\n", + " Vascular disease, diabetes and chronic pulmonary disease further increased risk.\n", + " \n", + " \n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import pandas as pd\n", + "\n", + "from IPython.display import display, HTML\n", + "\n", + "pd.set_option(\"display.max_colwidth\", None)\n", + "\n", + "db = sqlite3.connect(\"articles.sqlite\")\n", + "cur = db.cursor()\n", + "\n", + "results = []\n", + "for uid, score in embeddings.search(\"risk factors\", 5):\n", + " cur.execute(\"SELECT article, text FROM sections WHERE id = ?\", [uid])\n", + " uid, text = cur.fetchone()\n", + "\n", + " cur.execute(\"SELECT Title, Published, Reference from articles where id = ?\", [uid])\n", + " results.append(cur.fetchone() + (text,))\n", + "\n", + "# Free database resources\n", + "db.close()\n", + "\n", + "df = pd.DataFrame(results, columns=[\"Title\", \"Published\", \"Reference\", \"Match\"])\n", + "\n", + "# It has been reported that displaying HTML within VSCode doesn't work.\n", + "# When using VSCode, the data can be exported to an external HTML file to view.\n", + "# See example below.\n", + "\n", + "# htmlData = df.to_html(index=False)\n", + "# with open(\"data.html\", \"w\") as file:\n", + "# file.write(htmlData)\n", + "\n", + "display(HTML(df.to_html(index=False)))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XSf68I-ZfXOG" + }, + "source": [ + "# Extracting additional columns from query results\n", + "\n", + "The example above uses the Embeddings index to find the top 5 best matches. In addition to this, an Extractor instance (this will be explained further in part 5) is used to ask additional questions over the search results, creating a richer query response." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "TLVOTQJchvTi" + }, + "outputs": [], + "source": [ + "%%capture\n", + "from txtai.pipeline import Extractor\n", + "\n", + "# Create extractor instance using qa model designed for the CORD-19 dataset\n", + "# Note: That extractive QA was a predecessor to Large Language Models (LLMs). LLMs likely will get better results.\n", + "extractor = Extractor(embeddings, \"NeuML/bert-small-cord19qa\")" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 293 + }, + "id": "19fmKawThs6d", + "outputId": "b7cd40e3-a87c-419d-f520-b7795607cebc" + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
TitlePublishedReferenceMatchRisk FactorsLocations
Management of osteoarthritis during COVID‐19 pandemic2020-05-21 00:00:00https://doi.org/10.1002/cpt.1910Indeed, risk factors are sex, obesity, genetic factors and mechanical factors (3) .sex, obesity, genetic factors and mechanical factorshospitals and clinics
Does apolipoprotein E genotype predict COVID-19 severity?2020-04-27 00:00:00https://doi.org/10.1093/qjmed/hcaa142Risk factors associated with subsequent death include older age, hypertension, diabetes, ischemic heart disease, obesity and chronic lung disease; however, sometimes there are no obvious risk factors .NoneNone
Prevalence and Impact of Myocardial Injury in Patients Hospitalized with COVID-19 Infection2020-04-24 00:00:00http://medrxiv.org/cgi/content/short/2020.04.20.20072702v1?rss=1This risk was consistent across patients stratified by history of CVD, risk factors but no CVD, and neither CVD nor risk factors.neither CVD nor risk factorsMount Sinai Health System
COVID-19 and associations with frailty and multimorbidity: a prospective analysis of UK Biobank participants2020-07-23 00:00:00https://www.ncbi.nlm.nih.gov/pubmed/32705587/BACKGROUND: Frailty and multimorbidity have been suggested as risk factors for severe COVID-19 disease.Frailty and multimorbidity213 countries and territories
Risk Stratification for Healthcare workers during the CoViD-19 Pandemic; using demographics, co-morbid disease and clinical domain in order to assign clinical duties2020-05-09 00:00:00http://medrxiv.org/cgi/content/short/2020.05.05.20091967v1?rss=1Vascular disease, diabetes and chronic pulmonary disease further increased risk.Vascular disease, diabetes and chronic pulmonary diseaseNone
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "db = sqlite3.connect(\"articles.sqlite\")\n", + "cur = db.cursor()\n", + "\n", + "results = []\n", + "for uid, score in embeddings.search(\"risk factors\", 5):\n", + " cur.execute(\"SELECT article, text FROM sections WHERE id = ?\", [uid])\n", + " uid, text = cur.fetchone()\n", + "\n", + " # Get list of document text sections to use for the context\n", + " cur.execute(\"SELECT Name, Text FROM sections WHERE (labels is null or labels NOT IN ('FRAGMENT', 'QUESTION')) AND article = ? ORDER BY Id\", [uid])\n", + " texts = []\n", + " for name, txt in cur.fetchall():\n", + " if not name or not re.search(r\"background|(?\n", + " @import url('https://fonts.googleapis.com/css?family=Oswald&display=swap');\n", + " table {\n", + " border-collapse: collapse;\n", + " width: 900px;\n", + " }\n", + " th, td {\n", + " border: 1px solid #9e9e9e;\n", + " padding: 10px;\n", + " font: 15px Oswald;\n", + " }\n", + " \n", + " \"\"\"\n", + "\n", + " html += \"

[%s] %s

\" % (category, query)\n", + " for score, text in rows:\n", + " html += \"\" % (score, text)\n", + " html += \"
ScoreText
%.4f%s
\"\n", + "\n", + " display(HTML(html))\n", + "\n", + "def search(query, limit):\n", + " query = {\n", + " \"size\": limit,\n", + " \"query\": {\n", + " \"query_string\": {\"query\": query}\n", + " }\n", + " }\n", + "\n", + " results = []\n", + " for result in es.search(index=\"articles\", body=query)[\"hits\"][\"hits\"]:\n", + " source = result[\"_source\"]\n", + " results.append((min(result[\"_score\"], 18) / 18, source[\"title\"]))\n", + "\n", + " return results\n", + "\n", + "limit = 3\n", + "query= \"+yankees lose\"\n", + "table(\"Elasticsearch\", query, search(query, limit))" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + " \n", + "

[Elasticsearch] +yankees lose

ScoreText
0.5808El Duque adds to gloomy NY forecast The Yankees #39; staff infection has spread to the one man the team can #39;t afford to lose. Orlando Hernandez was scratched from last night #39;s scheduled start because
0.5688Rangers Derail Red Sox The Red Sox lose for the first time in 11 games, falling to the Rangers 8-6 Saturday and missing a chance to pull within 1 1/2 games of the Yankees in the AL East.
0.5061Rout leaves Yanks #39; lead at 3 Royals gain control with 10-run 5th Against a nothing-to-lose team such as the Kansas City Royals, the Yankees #39; manager wanted his team to put down the hammer early and not let baseball #39;s second worst team believe it had a chance.
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "W1DkpcIob5kt" + }, + "source": [ + "The table above shows the results for the query `+yankees lose`. This query requires the token `yankees`. The search doesn't understand the semantic meaning of the query. It returns the most relevant results with those two tokens.\n", + "\n", + "We can see in this case, the results aren't capturing the meaning of the search. Let's try adding semantic similarity to the search!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hMro47KedzJq" + }, + "source": [ + "# Ranking search results with txtai\n", + "\n", + "txtai has a similarity module that computes the similarity between a query and a list of strings. Of course, txtai can also build a full index as shown in the previous notebooks but in this case we'll just use the ad-hoc similarity function.\n", + "\n", + "The code below creates a Similarity instance and defines a ranking function to order search results based on the computed similarity.\n", + "\n", + "`ranksearch` queries Elasticsearch for a larger set of results, ranks the results using the similarity instance and returns the top n results. " + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "RUOj5zhFFK8N" + }, + "source": [ + "%%capture\n", + "from txtai.pipeline import Similarity\n", + "\n", + "def ranksearch(query, limit):\n", + " results = [text for _, text in search(query, limit * 10)]\n", + " return [(score, results[x]) for x, score in similarity(query, results)][:limit]\n", + "\n", + "# Create similarity instance for re-ranking\n", + "similarity = Similarity(\"valhalla/distilbart-mnli-12-3\")" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "UMFuv5-Hedfc" + }, + "source": [ + "Now let's re-run the previous search." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 334 + }, + "id": "3jJI9OxU0dZk", + "outputId": "3233d128-2bf4-4d53-f851-f9dd8f51629e" + }, + "source": [ + "# Run the search\n", + "table(\"Elasticsearch + txtai\", query, ranksearch(query, limit))" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + " \n", + "

[Elasticsearch + txtai] +yankees lose

ScoreText
0.9929Ouch! Yankees hit new low INDIANS 22, YANKEES 0---At New York, Omar Vizquel went 6-for-7 to tie the American League record for hits as Cleveland handed the Yankees the largest loss in their history last night.
0.9874Vazquez and Yankees Buckle Early Because Javier Vazquez fizzled while Brad Radke flourished, the Yankees sustained their first regular-season defeat by the Minnesota Twins since 2001.
0.9542Slide of the Yankees: Pinstripes Punished George Steinbrenner watched from his box as his Yankees suffered the most one-sided loss in the franchise's long history.
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "RXB2PaDZfd8o" + }, + "source": [ + "The results above do a much better job of finding results semantically similar in meaning to the query. Instead of just finding matches with `yankees` and `lose`, it finds matches where the `yankees lose`. \n", + "\n", + "This combination is effective and powerful. It takes advantage of the high performance of Elasticsearch while adding a semantic search capability. We may already have a large Elasticsearch cluster with TBs (or PBs)+ of data and years of engineering investment that solves most use cases. Semantically ranking search results is a practical approach." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XBVL56fsRI86" + }, + "source": [ + "# More examples\n", + "\n", + "Now for some more examples comparing the results from Elasticsearch vs Elasticsearch + txtai." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "7IHS38SERRpQ", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "outputId": "25325d94-ddc9-44f2-f602-d792ffeb0a8c" + }, + "source": [ + "for query in [\"good news +economy\", \"bad news +economy\"]:\n", + " table(\"Elasticsearch\", query, search(query, limit))\n", + " table(\"Elasticsearch + txtai\", query, ranksearch(query, limit))" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + " \n", + "

[Elasticsearch] good news +economy

ScoreText
0.8756Surprise drop US wholesale prices is mixed news for economy (AFP) AFP - A surprise drop in US wholesale prices in August showed inflation apparently in check, but analysts said this was good and bad news for the US economy.
0.7379China investment slows Good news for officials who are trying to cool an overheated economy; austerity measures to remain. BEIJING (Reuters) - China reported a marked slowdown in investment and money supply growth Monday, but stubbornly
0.7145Spending Rebounds, Good News for Growth WASHINGTON (Reuters) - U.S. consumer spending rebounded sharply July, government data showed on Monday, erasing the disappointment of June and bolstering hopes that the U.S. economy has recovered from its recent soft spot.
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + " \n", + "

[Elasticsearch + txtai] good news +economy

ScoreText
0.9996Spending Rebounds, Good News for Growth WASHINGTON (Reuters) - U.S. consumer spending rebounded sharply in July, the government said on Monday, erasing the disappointment of June and bolstering hopes that the U.S. economy has recovered from its recent soft spot.
0.9996Spending Rebounds, Good News for Growth WASHINGTON (Reuters) - U.S. consumer spending rebounded sharply July, government data showed on Monday, erasing the disappointment of June and bolstering hopes that the U.S. economy has recovered from its recent soft spot.
0.9993Home building surges Housing construction in August jumped to its highest level in five months, a dose of encouraging news for the economy #39;s expansion.
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + " \n", + "

[Elasticsearch] bad news +economy

ScoreText
0.9228Surprise drop US wholesale prices is mixed news for economy (AFP) AFP - A surprise drop in US wholesale prices in August showed inflation apparently in check, but analysts said this was good and bad news for the US economy.
0.6405Field Poll: Californians liking economy Bee Staff Writer. Californians are slowly growing more optimistic about the health of the economy, but a majority still feels the state is in bad economic times, according to a new Field Poll.
0.6188ADB says China should raise rates to cool economy China should raise interest rates to cool the economy and prevent a future buildup of bad loans in the banking system, the Asian Development Bank #39;s (ADB) Bei-jing representative Bruce Murray said.
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + " \n", + "

[Elasticsearch + txtai] bad news +economy

ScoreText
0.9977Aging society hits Japan #39;s economy Japan #39;s economy will be the most severely affected among industrialized nations by population aging, Kyodo News said Thursday.
0.9963Funds: Fund Mergers Can Hurt Investors (Reuters) Reuters - Mergers and acquisitions have\\played an enormous role in the U.S. economy during the past\\several decades, but sometimes the results have been bad for\\consumers. Similarly, consolidation in the mutual fund\\business has sometimes hurt fund investors.
0.9958Signs of listless economy persist In a sign of persistent weakness in the US economy, a widely watched measure of business activity declined in August for the third consecutive month.
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "h-Bk3KLjZMpF" + }, + "source": [ + "Once again while Elasticsearch usually returns quality results, occasionally it will match results that aren't semantically relevant. The power of semantic search is that not only will it find direct matches but matches with the same meaning. " + ] + } + ] +} diff --git a/examples/05_Extractive_QA_with_txtai.ipynb b/examples/05_Extractive_QA_with_txtai.ipynb new file mode 100644 index 0000000..1e39b37 --- /dev/null +++ b/examples/05_Extractive_QA_with_txtai.ipynb @@ -0,0 +1,151 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "vwELCooy4ljr" + }, + "source": [ + "# Extractive QA with txtai\n", + "\n", + "In Parts 1 through 4, we gave a general overview of txtai, the backing technology and examples of how to use it for similarity searches. This notebook builds on that and extends to building extractive question-answering systems." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ew7orE2O441o" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "LPQTb25tASIG" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_YnqorRKAbLu" + }, + "source": [ + "# Create an Embeddings and Extractor instances\n", + "\n", + "The Embeddings instance is the main entrypoint for txtai. An Embeddings instance defines the method used to tokenize and convert a segment of text into an embeddings vector.\n", + "\n", + "The Extractor instance is the entrypoint for extractive question-answering.\n", + "\n", + "Both the Embeddings and Extractor instances take a path to a transformer model. Any model on the [Hugging Face model hub](https://huggingface.co/models) can be used in place of the models below." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "OUc9gqTyAYnm" + }, + "source": [ + "%%capture\n", + "\n", + "from txtai.embeddings import Embeddings\n", + "from txtai.pipeline import Extractor\n", + "\n", + "# Create embeddings model, backed by sentence-transformers & transformers\n", + "embeddings = Embeddings({\"path\": \"sentence-transformers/nli-mpnet-base-v2\"})\n", + "\n", + "# Create extractor instance\n", + "extractor = Extractor(embeddings, \"distilbert-base-cased-distilled-squad\")" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "4X5z3UjnAGe7", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "546d4fdd-9468-4130-ee93-fafafd966e8b" + }, + "source": [ + "data = [\"Giants hit 3 HRs to down Dodgers\",\n", + " \"Giants 5 Dodgers 4 final\",\n", + " \"Dodgers drop Game 2 against the Giants, 5-4\",\n", + " \"Blue Jays beat Red Sox final score 2-1\",\n", + " \"Red Sox lost to the Blue Jays, 2-1\",\n", + " \"Blue Jays at Red Sox is over. Score: 2-1\",\n", + " \"Phillies win over the Braves, 5-0\",\n", + " \"Phillies 5 Braves 0 final\",\n", + " \"Final: Braves lose to the Phillies in the series opener, 5-0\",\n", + " \"Lightning goaltender pulled, lose to Flyers 4-1\",\n", + " \"Flyers 4 Lightning 1 final\",\n", + " \"Flyers win 4-1\"]\n", + "\n", + "questions = [\"What team won the game?\", \"What was score?\"]\n", + "\n", + "execute = lambda query: extractor([(question, query, question, False) for question in questions], data)\n", + "\n", + "for query in [\"Red Sox - Blue Jays\", \"Phillies - Braves\", \"Dodgers - Giants\", \"Flyers - Lightning\"]:\n", + " print(\"----\", query, \"----\")\n", + " for answer in execute(query):\n", + " print(answer)\n", + " print()\n", + "\n", + "# Ad-hoc questions\n", + "question = \"What hockey team won?\"\n", + "\n", + "print(\"----\", question, \"----\")\n", + "print(extractor([(question, question, question, False)], data))" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "---- Red Sox - Blue Jays ----\n", + "('What team won the game?', 'Blue Jays')\n", + "('What was score?', '2-1')\n", + "\n", + "---- Phillies - Braves ----\n", + "('What team won the game?', 'Phillies')\n", + "('What was score?', '5-0')\n", + "\n", + "---- Dodgers - Giants ----\n", + "('What team won the game?', 'Giants')\n", + "('What was score?', '5-4')\n", + "\n", + "---- Flyers - Lightning ----\n", + "('What team won the game?', 'Flyers')\n", + "('What was score?', '4-1')\n", + "\n", + "---- What hockey team won? ----\n", + "[('What hockey team won?', 'Flyers')]\n" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/examples/06_Extractive_QA_with_Elasticsearch.ipynb b/examples/06_Extractive_QA_with_Elasticsearch.ipynb new file mode 100644 index 0000000..a48b6d7 --- /dev/null +++ b/examples/06_Extractive_QA_with_Elasticsearch.ipynb @@ -0,0 +1,442 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "zzZbP0LM6m5z" + }, + "source": [ + "# Extractive QA with Elasticsearch\n", + "\n", + "txtai is datastore agnostic, the library analyzes sets of text. The following example shows how extractive question-answering can be added on top of an Elasticsearch system." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xk7t5Jcd6reO" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and `Elasticsearch`." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "0y1UA4-q-YdA" + }, + "source": [ + "%%capture\n", + "\n", + "# Install txtai and elasticsearch python client\n", + "!pip install git+https://github.com/neuml/txtai elasticsearch\n", + "\n", + "# Download and extract elasticsearch\n", + "!wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.10.1-linux-x86_64.tar.gz\n", + "!tar -xzf elasticsearch-7.10.1-linux-x86_64.tar.gz\n", + "!chown -R daemon:daemon elasticsearch-7.10.1" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nKWz-C5gCJy8" + }, + "source": [ + "Start an instance of Elasticsearch directly within this notebook. " + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "3ZfJeWbM6wmj" + }, + "source": [ + "import os\n", + "from subprocess import Popen, PIPE, STDOUT\n", + "\n", + "# If issues are encountered with this section, ES can be manually started as follows:\n", + "# ./elasticsearch-7.10.1/bin/elasticsearch\n", + "\n", + "# Start and wait for server\n", + "server = Popen(['elasticsearch-7.10.1/bin/elasticsearch'], stdout=PIPE, stderr=STDOUT, preexec_fn=lambda: os.setuid(1))\n", + "!sleep 30" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TWEn4w68-D1y" + }, + "source": [ + "# Download data\n", + "\n", + "This example is going to work off a subset of the [CORD-19](https://www.semanticscholar.org/cord19) dataset. COVID-19 Open Research Dataset (CORD-19) is a free resource of scholarly articles, aggregated by a coalition of leading research groups, covering COVID-19 and the coronavirus family of viruses.\n", + "\n", + "The following download is a SQLite database generated from a [Kaggle notebook](https://www.kaggle.com/davidmezzetti/cord-19-slim/output). More information on this data format, can be found in the [CORD-19 Analysis](https://www.kaggle.com/davidmezzetti/cord-19-analysis-with-sentence-embeddings) notebook." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "8tVrIqSq-KBa" + }, + "source": [ + "%%capture\n", + "!wget https://github.com/neuml/txtai/releases/download/v1.1.0/tests.gz\n", + "!gunzip tests.gz\n", + "!mv tests articles.sqlite" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hSWFzkCn61tM" + }, + "source": [ + "# Load data into Elasticsearch\n", + "\n", + "The following block copies rows from SQLite to Elasticsearch." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "So-OBvUT61QD", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "9647b8f8-8471-41bf-ccfa-a75306665638" + }, + "source": [ + "import sqlite3\n", + "\n", + "import regex as re\n", + "\n", + "from elasticsearch import Elasticsearch, helpers\n", + "\n", + "# Connect to ES instance\n", + "es = Elasticsearch(hosts=[\"http://localhost:9200\"], timeout=60, retry_on_timeout=True)\n", + "\n", + "# Connection to database file\n", + "db = sqlite3.connect(\"articles.sqlite\")\n", + "cur = db.cursor()\n", + "\n", + "# Elasticsearch bulk buffer\n", + "buffer = []\n", + "rows = 0\n", + "\n", + "# Select tagged sentences without a NLP label. NLP labels are set for non-informative sentences.\n", + "cur.execute(\"SELECT s.Id, Article, Title, Published, Reference, Name, Text FROM sections s JOIN articles a on s.article=a.id WHERE (s.labels is null or s.labels NOT IN ('FRAGMENT', 'QUESTION')) AND s.tags is not null\")\n", + "for row in cur:\n", + " # Build dict of name-value pairs for fields\n", + " article = dict(zip((\"id\", \"article\", \"title\", \"published\", \"reference\", \"name\", \"text\"), row))\n", + " name = article[\"name\"]\n", + "\n", + " # Only process certain document sections\n", + " if not name or not re.search(r\"background|(?\n", + " \n", + " \n", + " Title\n", + " Published\n", + " Reference\n", + " Match\n", + " \n", + " \n", + " \n", + " \n", + " Prevalence and Impact of Myocardial Injury in Patients Hospitalized with COVID-19 Infection\n", + " 2020-04-24 00:00:00\n", + " http://medrxiv.org/cgi/content/short/2020.04.20.20072702v1?rss=1\n", + " This risk was consistent across patients stratified by history of CVD, risk factors but no CVD, and neither CVD nor risk factors.\n", + " \n", + " \n", + " Does apolipoprotein E genotype predict COVID-19 severity?\n", + " 2020-04-27 00:00:00\n", + " https://doi.org/10.1093/qjmed/hcaa142\n", + " Risk factors associated with subsequent death include older age, hypertension, diabetes, ischemic heart disease, obesity and chronic lung disease; however, sometimes there are no obvious risk factors .\n", + " \n", + " \n", + " COVID-19 and associations with frailty and multimorbidity: a prospective analysis of UK Biobank participants\n", + " 2020-07-23 00:00:00\n", + " https://www.ncbi.nlm.nih.gov/pubmed/32705587/\n", + " BACKGROUND: Frailty and multimorbidity have been suggested as risk factors for severe COVID-19 disease.\n", + " \n", + " \n", + " COVID-19: what has been learned and to be learned about the novel coronavirus disease\n", + " 2020-03-15 00:00:00\n", + " https://doi.org/10.7150/ijbs.45134\n", + " • Three major risk factors for COVID-19 were sex (male), age (≥60), and severe pneumonia.\n", + " \n", + " \n", + " Associations with covid-19 hospitalisation amongst 406,793 adults: the UK Biobank prospective cohort study\n", + " 2020-05-11 00:00:00\n", + " http://medrxiv.org/cgi/content/short/2020.05.06.20092957v1?rss=1\n", + " In addition, many risk factors for covid-19 documented in the literature are highly correlated and it is not clear which may be independently related to risk.\n", + " \n", + " \n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ylxOKji1-9_K" + }, + "source": [ + "# Derive columns with Extractive QA\n", + "\n", + "The next section uses Extractive QA to derive additional columns. For each article, the full text is retrieved and a series of questions are asked of the document. The answers are added as a derived column per article." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "mwBTrCkcOM_H" + }, + "source": [ + "%%capture\n", + "from txtai.embeddings import Embeddings\n", + "from txtai.pipeline import Extractor\n", + "\n", + "# Create embeddings model, backed by sentence-transformers & transformers\n", + "embeddings = Embeddings({\"path\": \"sentence-transformers/nli-mpnet-base-v2\"})\n", + "\n", + "# Create extractor instance using qa model designed for the CORD-19 dataset\n", + "extractor = Extractor(embeddings, \"NeuML/bert-small-cord19qa\")" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "Yv75Lh-cOpL9", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 400 + }, + "outputId": "adee88e1-02bf-4a20-febb-6d2c170a63f9" + }, + "source": [ + "document = {\n", + " \"_source\": [\"id\", \"name\", \"text\"],\n", + " \"size\": 1000,\n", + " \"query\": {\n", + " \"term\": {\"article\": None}\n", + " },\n", + " \"sort\" : [\"id\"]\n", + "}\n", + "\n", + "def sections(article):\n", + " rows = []\n", + "\n", + " search = document.copy()\n", + " search[\"query\"][\"term\"][\"article\"] = article\n", + "\n", + " for result in es.search(index=\"articles\", body=search)[\"hits\"][\"hits\"]:\n", + " source = result[\"_source\"]\n", + " name, text = source[\"name\"], source[\"text\"]\n", + "\n", + " if not name or not re.search(r\"background|(?\n", + " \n", + " \n", + " Title\n", + " Published\n", + " Reference\n", + " Match\n", + " Risk Factors\n", + " Locations\n", + " \n", + " \n", + " \n", + " \n", + " Management of osteoarthritis during COVID‐19 pandemic\n", + " 2020-05-21 00:00:00\n", + " https://doi.org/10.1002/cpt.1910\n", + " Indeed, risk factors are sex, obesity, genetic factors and mechanical factors (3) .\n", + " sex, obesity, genetic factors and mechanical factors\n", + " None\n", + " \n", + " \n", + " Prevalence and Impact of Myocardial Injury in Patients Hospitalized with COVID-19 Infection\n", + " 2020-04-24 00:00:00\n", + " http://medrxiv.org/cgi/content/short/2020.04.20.20072702v1?rss=1\n", + " This risk was consistent across patients stratified by history of CVD, risk factors but no CVD, and neither CVD nor risk factors.\n", + " None\n", + " Abbott, Abbott Park, Illinois\n", + " \n", + " \n", + " Does apolipoprotein E genotype predict COVID-19 severity?\n", + " 2020-04-27 00:00:00\n", + " https://doi.org/10.1093/qjmed/hcaa142\n", + " Risk factors associated with subsequent death include older age, hypertension, diabetes, ischemic heart disease, obesity and chronic lung disease; however, sometimes there are no obvious risk factors .\n", + " None\n", + " None\n", + " \n", + " \n", + " COVID-19 and associations with frailty and multimorbidity: a prospective analysis of UK Biobank participants\n", + " 2020-07-23 00:00:00\n", + " https://www.ncbi.nlm.nih.gov/pubmed/32705587/\n", + " BACKGROUND: Frailty and multimorbidity have been suggested as risk factors for severe COVID-19 disease.\n", + " Frailty and multimorbidity\n", + " comorbidity groupings and the corresponding health conditions\n", + " \n", + " \n", + " COVID-19: what has been learned and to be learned about the novel coronavirus disease\n", + " 2020-03-15 00:00:00\n", + " https://doi.org/10.7150/ijbs.45134\n", + " • Three major risk factors for COVID-19 were sex (male), age (≥60), and severe pneumonia.\n", + " Mandatory contact tracing and quarantine\n", + " cities, provinces, and countries\n", + " \n", + " \n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + } + ] + } + ] +} diff --git a/examples/07_Apply_labels_with_zero_shot_classification.ipynb b/examples/07_Apply_labels_with_zero_shot_classification.ipynb new file mode 100644 index 0000000..b339abb --- /dev/null +++ b/examples/07_Apply_labels_with_zero_shot_classification.ipynb @@ -0,0 +1,207 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "4Pjmz-RORV8E" + }, + "source": [ + "# Apply labels with zero-shot classification\n", + "\n", + "This notebook shows how zero-shot classification can be used to perform text classification, labeling and topic modeling. txtai provides a light-weight wrapper around the zero-shot-classification pipeline in Hugging Face Transformers. This method works impressively well out of the box. Kudos to the Hugging Face team for the phenomenal work on zero-shot classification!\n", + "\n", + "The examples in this notebook pick the best matching label using a list of labels for a snippet of text.\n", + "\n", + "[tldrstory](https://github.com/neuml/tldrstory) has full-stack implementation of a zero-shot classification system using Streamlit, FastAPI and Hugging Face Transformers. There is also a [Medium article describing tldrstory](https://towardsdatascience.com/tldrstory-ai-powered-understanding-of-headlines-and-story-text-fc86abd702fc) and zero-shot classification. \n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dk31rbYjSTYm" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "XMQuuun2R06J" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PNPJ95cdTKSS" + }, + "source": [ + "# Create a Labels instance\n", + "\n", + "The Labels instance is the main entrypoint for zero-shot classification. This is a light-weight wrapper around the zero-shot-classification pipeline in Hugging Face Transformers.\n", + "\n", + "In addition to the default model, additional models can be found on the [Hugging Face model hub](https://huggingface.co/models?search=mnli).\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "nTDwXOUeTH2-" + }, + "source": [ + "%%capture\n", + "\n", + "from txtai.pipeline import Labels\n", + "\n", + "# Create labels model\n", + "labels = Labels()\n", + "\n", + "# Alternate models can be used via passing the model path as shown below\n", + "# labels = Labels(\"roberta-large-mnli\")" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-vGR_piwZZO6" + }, + "source": [ + "# Applying labels to text\n", + "\n", + "The example below shows how a zero-shot classifier can be applied to arbitary text. The default model for the zero-shot classification pipeline is *bart-large-mnli*. \n", + "\n", + "Look at the results below. It's nothing short of amazing✨ how well it performs. These aren't all simple even for a human. For example, intercepted was purposely picked as that is more common in football than basketball. The amount of knowledge stored in larger Transformer models continues to impress me. " + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-K2YJJzsVtfq", + "outputId": "7a1edf58-15e0-46c8-958e-3a8e6045f802" + }, + "source": [ + "data = [\"Dodgers lose again, give up 3 HRs in a loss to the Giants\",\n", + " \"Giants 5 Cardinals 4 final in extra innings\",\n", + " \"Dodgers drop Game 2 against the Giants, 5-4\",\n", + " \"Flyers 4 Lightning 1 final. 45 saves for the Lightning.\",\n", + " \"Slashing, penalty, 2 minute power play coming up\",\n", + " \"What a stick save!\",\n", + " \"Leads the NFL in sacks with 9.5\",\n", + " \"UCF 38 Temple 13\",\n", + " \"With the 30 yard completion, down to the 10 yard line\",\n", + " \"Drains the 3pt shot!!, 0:15 remaining in the game\",\n", + " \"Intercepted! Drives down the court and shoots for the win\",\n", + " \"Massive dunk!!! they are now up by 15 with 2 minutes to go\"]\n", + "\n", + "# List of labels\n", + "tags = [\"Baseball\", \"Football\", \"Hockey\", \"Basketball\"]\n", + "\n", + "print(\"%-75s %s\" % (\"Text\", \"Label\"))\n", + "print(\"-\" * 100)\n", + "\n", + "for text in data:\n", + " print(\"%-75s %s\" % (text, tags[labels(text, tags)[0][0]]))" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Text Label\n", + "----------------------------------------------------------------------------------------------------\n", + "Dodgers lose again, give up 3 HRs in a loss to the Giants Baseball\n", + "Giants 5 Cardinals 4 final in extra innings Baseball\n", + "Dodgers drop Game 2 against the Giants, 5-4 Baseball\n", + "Flyers 4 Lightning 1 final. 45 saves for the Lightning. Hockey\n", + "Slashing, penalty, 2 minute power play coming up Hockey\n", + "What a stick save! Hockey\n", + "Leads the NFL in sacks with 9.5 Football\n", + "UCF 38 Temple 13 Football\n", + "With the 30 yard completion, down to the 10 yard line Football\n", + "Drains the 3pt shot!!, 0:15 remaining in the game Basketball\n", + "Intercepted! Drives down the court and shoots for the win Basketball\n", + "Massive dunk!!! they are now up by 15 with 2 minutes to go Basketball\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "t-tGAzCxsHLy" + }, + "source": [ + "# Let's try emoji 😀\n", + "\n", + "Does the model have knowledge of emoji? Check out the run below, sure looks like it does! Notice the labels are applied based on the perspective from which the information is presented. " + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "uIf064M9pbjn", + "outputId": "1d104014-e9ca-4c89-d259-2b5b231840ad" + }, + "source": [ + "tags = [\"😀\", \"😡\"]\n", + "\n", + "print(\"%-75s %s\" % (\"Text\", \"Label\"))\n", + "print(\"-\" * 100)\n", + "\n", + "for text in data:\n", + " print(\"%-75s %s\" % (text, tags[labels(text, tags)[0][0]]))" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Text Label\n", + "----------------------------------------------------------------------------------------------------\n", + "Dodgers lose again, give up 3 HRs in a loss to the Giants 😡\n", + "Giants 5 Cardinals 4 final in extra innings 😀\n", + "Dodgers drop Game 2 against the Giants, 5-4 😡\n", + "Flyers 4 Lightning 1 final. 45 saves for the Lightning. 😀\n", + "Slashing, penalty, 2 minute power play coming up 😡\n", + "What a stick save! 😀\n", + "Leads the NFL in sacks with 9.5 😀\n", + "UCF 38 Temple 13 😀\n", + "With the 30 yard completion, down to the 10 yard line 😀\n", + "Drains the 3pt shot!!, 0:15 remaining in the game 😀\n", + "Intercepted! Drives down the court and shoots for the win 😀\n", + "Massive dunk!!! they are now up by 15 with 2 minutes to go 😀\n" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/examples/08_API_Gallery.ipynb b/examples/08_API_Gallery.ipynb new file mode 100644 index 0000000..52f8520 --- /dev/null +++ b/examples/08_API_Gallery.ipynb @@ -0,0 +1,836 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "4Pjmz-RORV8E" + }, + "source": [ + "# API Gallery\n", + "\n", + "The txtai API is a web-based service backed by [FastAPI](https://fastapi.tiangolo.com/). All txtai functionality including similarity search, extractive QA and zero-shot labeling is available via the API.\n", + "\n", + "This notebook installs the txtai API and shows an example using each of the supported language bindings for txtai." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dk31rbYjSTYm" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies. Since this notebook uses the API, we need to install the api extras package." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "XMQuuun2R06J" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[api]" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-vGR_piwZZO6" + }, + "source": [ + "# Python\n", + "\n", + "The first method we'll try is direct access via Python. We'll use zero-shot labeling for all the examples here. See [this notebook](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/07_Apply_labels_with_zero_shot_classification.ipynb) for more details on zero-shot classification. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "P4q72tkRMMkR" + }, + "source": [ + "## Configure Labels instance" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "8Dy_TJ0iM38Q" + }, + "source": [ + "%%capture\n", + "import os\n", + "from IPython.core.display import display, HTML\n", + "from txtai.pipeline import Labels\n", + "\n", + "def table(rows):\n", + " html = \"\"\"\n", + " \n", + " \"\"\"\n", + "\n", + " html += \"\"\n", + " for text, label in rows:\n", + " html += \"\" % (text, label)\n", + " html += \"
TextLabel
%s%s
\"\n", + "\n", + " display(HTML(html))\n", + "\n", + "# Create labels model\n", + "labels = Labels()" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "L4B73tGkMT6Q" + }, + "source": [ + "## Apply labels to text" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 324 + }, + "id": "-K2YJJzsVtfq", + "outputId": "65782fd8-51fb-4531-8e8b-f28bca678fa0" + }, + "source": [ + "data = [\"Wears a red suit and says ho ho\",\n", + " \"Pulls a flying sleigh\",\n", + " \"This is cut down and decorated\",\n", + " \"Santa puts these under the tree\",\n", + " \"Best way to spend the holidays\"]\n", + "\n", + "# List of labels\n", + "tags = [\"🎅 Santa Clause\", \"🦌 Reindeer\", \"🍪 Cookies\", \"🎄 Christmas Tree\", \"🎁 Gifts\", \"👪 Family\"]\n", + "\n", + "# Render output to table\n", + "table([(text, tags[labels(text, tags)[0][0]]) for text in data])" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + " \n", + "
TextLabel
Wears a red suit and says ho ho🎅 Santa Clause
Pulls a flying sleigh🦌 Reindeer
This is cut down and decorated🎄 Christmas Tree
Santa puts these under the tree🎁 Gifts
Best way to spend the holidays👪 Family
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "UF_bImkLHTMs" + }, + "source": [ + "Once again we see the power of zero-shot labeling. The model wasn't trained on any data specific to this example. Still amazed with how much knowledge is stored in large NLP models." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PNPJ95cdTKSS" + }, + "source": [ + "# Start an API instance\n", + "\n", + "Now we'll start an API instance to run the remaining examples. The API needs a configuration file to run. The example below is simplified to only include labeling. See [this link](https://github.com/neuml/txtai#api) for a more detailed configuration example.\n", + "\n", + "The API instance is started in the background.\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "nTDwXOUeTH2-", + "outputId": "2220a3c9-1cff-4c2f-b21e-13dd2d7cb816" + }, + "source": [ + "%%writefile index.yml\n", + "\n", + "# Labels settings\n", + "labels:" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Writing index.yml\n" + ] + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "nGITHxUyRzyp" + }, + "source": [ + "!CONFIG=index.yml nohup uvicorn \"txtai.api:app\" &> api.log &\n", + "!sleep 90" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "NHvBFZeSd9AG" + }, + "source": [ + "# JavaScript\n", + "\n", + "txtai.js is available via NPM and can be installed as follows.\n", + "\n", + "```bash\n", + "npm install txtai\n", + "```\n", + "\n", + "For this example, we'll clone the txtai.js project to import the example build configuration." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "b52knObEdcCr" + }, + "source": [ + "%%capture\n", + "!git clone https://github.com/neuml/txtai.js" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rUGS0t-JMsS9" + }, + "source": [ + "## Create labels.js\n", + "\n", + "The following file is a JavaScript version of the labels example." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "zJbKRTSJV-kd", + "outputId": "6c111b5d-6e55-4dac-c6c2-0988c2a834da" + }, + "source": [ + "%%writefile txtai.js/examples/node/src/labels.js\n", + "import {Labels} from \"txtai\";\n", + "import {sprintf} from \"sprintf-js\";\n", + "\n", + "const run = async () => {\n", + " try {\n", + " let labels = new Labels(\"http://localhost:8000\");\n", + "\n", + " let data = [\"Wears a red suit and says ho ho\",\n", + " \"Pulls a flying sleigh\",\n", + " \"This is cut down and decorated\",\n", + " \"Santa puts these under the tree\",\n", + " \"Best way to spend the holidays\"];\n", + "\n", + " // List of labels\n", + " let tags = [\"🎅 Santa Clause\", \"🦌 Reindeer\", \"🍪 Cookies\", \"🎄 Christmas Tree\", \"🎁 Gifts\", \"👪 Family\"];\n", + "\n", + " console.log(sprintf(\"%-40s %s\", \"Text\", \"Label\"));\n", + " console.log(\"-\".repeat(75))\n", + "\n", + " for (let text of data) {\n", + " let label = await labels.label(text, tags);\n", + " label = tags[label[0].id];\n", + "\n", + " console.log(sprintf(\"%-40s %s\", text, label));\n", + " }\n", + " }\n", + " catch (e) {\n", + " console.trace(e);\n", + " }\n", + "};\n", + "\n", + "run();\n" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Overwriting txtai.js/examples/node/src/labels.js\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nTBs11j-GtD-" + }, + "source": [ + "## Build and run labels example\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "kC5Oub6wa1nK" + }, + "source": [ + "%%capture\n", + "os.chdir(\"txtai.js/examples/node\")\n", + "!npm install\n", + "!npm run build" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ckOHNqyaeL-B", + "outputId": "6d8e745c-52d1-4456-fc46-2ff8fda2e675" + }, + "source": [ + "!node dist/labels.js" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Text Label\n", + "---------------------------------------------------------------------------\n", + "Wears a red suit and says ho ho 🎅 Santa Clause\n", + "Pulls a flying sleigh 🦌 Reindeer\n", + "This is cut down and decorated 🎄 Christmas Tree\n", + "Santa puts these under the tree 🎁 Gifts\n", + "Best way to spend the holidays 👪 Family\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1yukBIMYG5OE" + }, + "source": [ + "The JavaScript program is showing the same results as when natively running through Python!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nNiMgvg0p2BG" + }, + "source": [ + "# Java\n", + "\n", + "txtai.java integrates with standard Java build tools (Gradle, Maven, SBT). The following shows how to add txtai as a dependency to Gradle.\n", + "\n", + "```gradle\n", + "implementation 'com.github.neuml:txtai.java:v4.0.0'\n", + "```\n", + "\n", + "For this example, we'll clone the txtai.java project to import the example build configuration." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "qs2ai8lhqmga" + }, + "source": [ + "%%capture\n", + "os.chdir(\"/content\")\n", + "!git clone https://github.com/neuml/txtai.java" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "o8QFvzXkNFgq" + }, + "source": [ + "## Create LabelsDemo.java\n", + "\n", + "The following file is a Java version of the labels example." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "v73L8Gw0p6fh", + "outputId": "a7f797f2-a91f-4033-89c7-4baf76204d93" + }, + "source": [ + "%%writefile txtai.java/examples/src/main/java/LabelsDemo.java\n", + "import java.util.Arrays;\n", + "import java.util.ArrayList;\n", + "import java.util.List;\n", + "\n", + "import txtai.API.IndexResult;\n", + "import txtai.Labels;\n", + "\n", + "public class LabelsDemo {\n", + " public static void main(String[] args) {\n", + " try {\n", + " Labels labels = new Labels(\"http://localhost:8000\");\n", + "\n", + " List data = \n", + " Arrays.asList(\"Wears a red suit and says ho ho\",\n", + " \"Pulls a flying sleigh\",\n", + " \"This is cut down and decorated\",\n", + " \"Santa puts these under the tree\",\n", + " \"Best way to spend the holidays\");\n", + "\n", + " // List of labels\n", + " List tags = Arrays.asList(\"🎅 Santa Clause\", \"🦌 Reindeer\", \"🍪 Cookies\", \"🎄 Christmas Tree\", \"🎁 Gifts\", \"👪 Family\");\n", + "\n", + " System.out.printf(\"%-40s %s%n\", \"Text\", \"Label\");\n", + " System.out.println(new String(new char[75]).replace(\"\\0\", \"-\"));\n", + "\n", + " for (String text: data) {\n", + " List label = labels.label(text, tags);\n", + " System.out.printf(\"%-40s %s%n\", text, tags.get(label.get(0).id));\n", + " }\n", + " }\n", + " catch (Exception ex) {\n", + " ex.printStackTrace();\n", + " }\n", + " }\n", + "}\n" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Overwriting txtai.java/examples/src/main/java/LabelsDemo.java\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "wZv7eMIOLnRC" + }, + "source": [ + "## Build and run labels example" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "N2Mm3Gl5sH1z", + "outputId": "b5249daf-e5a1-4b71-b64c-2b3c6748e846" + }, + "source": [ + "os.chdir(\"txtai.java/examples\")\n", + "!../gradlew -q --console=plain labels 2> /dev/null" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Text Label\n", + "---------------------------------------------------------------------------\n", + "Wears a red suit and says ho ho 🎅 Santa Clause\n", + "Pulls a flying sleigh 🦌 Reindeer\n", + "This is cut down and decorated 🎄 Christmas Tree\n", + "Santa puts these under the tree 🎁 Gifts\n", + "Best way to spend the holidays 👪 Family\n", + "\u001b[m" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "iHpQvUAgNp7j" + }, + "source": [ + "The Java program is showing the same results as when natively running through Python!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zU6jK2UL7D5H" + }, + "source": [ + "# Rust\n", + "\n", + "txtai.rs is available via crates.io and can be installed by adding the following to your cargo.toml file.\n", + "\n", + "```toml\n", + "[dependencies]\n", + "txtai = { version = \"4.0\" }\n", + "tokio = { version = \"0.2\", features = [\"full\"] }\n", + "```\n", + "\n", + "For this example, we'll clone the txtai.rs project to import the example build configuration. First we need to install Rust." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "Ob4aswkx7jRh" + }, + "source": [ + "%%capture\n", + "os.chdir(\"/content\")\n", + "!apt-get install rustc\n", + "!git clone https://github.com/neuml/txtai.rs" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "evEQQXBuObZn" + }, + "source": [ + "## Create labels.rs\n", + "\n", + "The following file is a Rust version of the labels example." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "jjggKnKQ7jQO", + "outputId": "76a2b1d9-2889-47b0-a3af-5d71a763bb0b" + }, + "source": [ + "%%writefile txtai.rs/examples/demo/src/labels.rs\n", + "use std::error::Error;\n", + "\n", + "use txtai::labels::Labels;\n", + "\n", + "pub async fn labels() -> Result<(), Box> {\n", + " let labels = Labels::new(\"http://localhost:8000\");\n", + "\n", + " let data = [\"Wears a red suit and says ho ho\",\n", + " \"Pulls a flying sleigh\",\n", + " \"This is cut down and decorated\",\n", + " \"Santa puts these under the tree\",\n", + " \"Best way to spend the holidays\"];\n", + "\n", + " println!(\"{:<40} {}\", \"Text\", \"Label\");\n", + " println!(\"{}\", \"-\".repeat(75));\n", + "\n", + " for text in data.iter() {\n", + " let tags = vec![\"🎅 Santa Clause\", \"🦌 Reindeer\", \"🍪 Cookies\", \"🎄 Christmas Tree\", \"🎁 Gifts\", \"👪 Family\"];\n", + " let label = labels.label(text, &tags).await?[0].id;\n", + "\n", + " println!(\"{:<40} {}\", text, tags[label]);\n", + " }\n", + "\n", + " Ok(())\n", + "}" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Overwriting txtai.rs/examples/demo/src/labels.rs\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gFFPZO8sQZC4" + }, + "source": [ + "## Build and run labels example\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "wuoAidGz9T4g" + }, + "source": [ + "%%capture\n", + "os.chdir(\"txtai.rs/examples/demo\")\n", + "!cargo build" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-_v_FbL0-yPk", + "outputId": "821333f5-5f90-4f89-c2eb-673c2e14e4fe" + }, + "source": [ + "!cargo run labels" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\u001b[0m\u001b[0m\u001b[1m\u001b[32m Finished\u001b[0m dev [unoptimized + debuginfo] target(s) in 0.07s\n", + "\u001b[0m\u001b[0m\u001b[1m\u001b[32m Running\u001b[0m `target/debug/demo labels`\n", + "Text Label\n", + "---------------------------------------------------------------------------\n", + "Wears a red suit and says ho ho 🎅 Santa Clause\n", + "Pulls a flying sleigh 🦌 Reindeer\n", + "This is cut down and decorated 🎄 Christmas Tree\n", + "Santa puts these under the tree 🎁 Gifts\n", + "Best way to spend the holidays 👪 Family\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kDmS89TPS3kb" + }, + "source": [ + "The Rust program is showing the same results as when natively running through Python!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ezznN4I8_CCQ" + }, + "source": [ + "# Go\n", + "\n", + "txtai.go can be installed by adding the following import statement. When using modules, txtai.go will automatically be installed. Otherwise use `go get`.\n", + "\n", + "```golang\n", + "import \"github.com/neuml/txtai.go\"\n", + "```\n", + "\n", + "For this example, we'll create a standalone process for labeling. First we need to install Go." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "b-b6fhLQ_DpQ" + }, + "source": [ + "%%capture\n", + "os.chdir(\"/content\")\n", + "!apt install golang-go\n", + "!go get \"github.com/neuml/txtai.go\"" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dw-I6jOGR6vA" + }, + "source": [ + "## Create labels.go\n", + "\n", + "The following file is a Go version of the labels example." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "bLBJwkN4ANpi", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "883ea7b2-2fbc-471c-e0bb-59ef5172a6a4" + }, + "source": [ + "%%writefile labels.go\n", + "package main\n", + "\n", + "import (\n", + "\t\"fmt\"\n", + "\t\"strings\"\n", + "\t\"github.com/neuml/txtai.go\"\n", + ")\n", + "\n", + "func main() {\n", + "\tlabels := txtai.Labels(\"http://localhost:8000\")\n", + "\n", + "\tdata := []string{\"Wears a red suit and says ho ho\",\n", + " \"Pulls a flying sleigh\",\n", + " \"This is cut down and decorated\",\n", + " \"Santa puts these under the tree\",\n", + " \"Best way to spend the holidays\"}\n", + "\n", + "\t// List of labels\n", + "\ttags := []string{\"🎅 Santa Clause\", \"🦌 Reindeer\", \"🍪 Cookies\", \"🎄 Christmas Tree\", \"🎁 Gifts\", \"👪 Family\"}\n", + "\n", + "\tfmt.Printf(\"%-40s %s\\n\", \"Text\", \"Label\")\n", + "\tfmt.Println(strings.Repeat(\"-\", 75))\n", + "\n", + "\tfor _, text := range data {\n", + "\t\tlabel := labels.Label(text, tags)\n", + "\t\tfmt.Printf(\"%-40s %s\\n\", text, tags[label[0].Id])\n", + "\t}\n", + "}" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Writing labels.go\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PJ2XzzDbSeZh" + }, + "source": [ + "## Build and run labels example\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "l1xnUbtdAy0p", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "5bc6015c-5c9c-4d8a-daf7-6897ec6cbd80" + }, + "source": [ + "!go run labels.go" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Text Label\n", + "---------------------------------------------------------------------------\n", + "Wears a red suit and says ho ho 🎅 Santa Clause\n", + "Pulls a flying sleigh 🦌 Reindeer\n", + "This is cut down and decorated 🎄 Christmas Tree\n", + "Santa puts these under the tree 🎁 Gifts\n", + "Best way to spend the holidays 👪 Family\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "oml43X5eS6YB" + }, + "source": [ + "The Go program is showing the same results as when natively running through Python!" + ] + } + ] +} \ No newline at end of file diff --git a/examples/09_Building_abstractive_text_summaries.ipynb b/examples/09_Building_abstractive_text_summaries.ipynb new file mode 100644 index 0000000..947d569 --- /dev/null +++ b/examples/09_Building_abstractive_text_summaries.ipynb @@ -0,0 +1,197 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "4Pjmz-RORV8E" + }, + "source": [ + "# Building abstractive text summaries\n", + "\n", + "In the field of text summarization, there are two primary categories of summarization, extractive and abstractive summarization.\n", + "\n", + "Extractive summarization takes subsections of the text and joins them together to form a summary. This is commonly backed by graph algorithms like TextRank to find the sections/sentences with the most commonality. These summaries can be highly effective but they are unable to transform text and don't have a contextual understanding.\n", + "\n", + "Abstractive summarization uses Natural Language Processing (NLP) models to build transformative summaries of text. This is similar to having a human read an article and asking what was it about. A human wouldn't just give a verbose reading of the text. This notebook shows how blocks of text can be summarized using an abstractive summarization pipeline. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dk31rbYjSTYm" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies. Since this notebook is using optional pipelines, we need to install the pipeline extras package." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "XMQuuun2R06J" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline]" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PNPJ95cdTKSS" + }, + "source": [ + "# Create a Summary instance\n", + "\n", + "The Summary instance is the main entrypoint for text summarization. This is a light-weight wrapper around the summarization pipeline in Hugging Face Transformers.\n", + "\n", + "In addition to the default model, additional models can be found on the [Hugging Face model hub](https://huggingface.co/models?pipeline_tag=summarization).\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "nTDwXOUeTH2-" + }, + "source": [ + "%%capture\n", + "\n", + "from txtai.pipeline import Summary\n", + "\n", + "# Create summary model\n", + "summary = Summary()" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-vGR_piwZZO6" + }, + "source": [ + "# Summarize text\n", + "\n", + "The example below shows how a large block of text can be distilled down into a smaller summary." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 36 + }, + "id": "-K2YJJzsVtfq", + "outputId": "cdf54f20-72ad-4f65-bc17-100e32e6cc71" + }, + "source": [ + "text = (\"Search is the base of many applications. Once data starts to pile up, users want to be able to find it. It’s the foundation \"\n", + " \"of the internet and an ever-growing challenge that is never solved or done. The field of Natural Language Processing (NLP) is \"\n", + " \"rapidly evolving with a number of new developments. Large-scale general language models are an exciting new capability \"\n", + " \"allowing us to add amazing functionality quickly with limited compute and people. Innovation continues with new models \"\n", + " \"and advancements coming in at what seems a weekly basis. This article introduces txtai, an AI-powered search engine \"\n", + " \"that enables Natural Language Understanding (NLU) based search in any application.\"\n", + ")\n", + "\n", + "summary(text, maxlength=10)" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'Search is the foundation of the internet'" + ] + }, + "metadata": {}, + "execution_count": 3 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "n2jndgE-JyWX" + }, + "source": [ + "Notice how the summarizer built a sentence using parts of the document above. It takes a basic understanding of language in order to understand the first two sentences and how to combine them into a single transformative sentence." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "27PneZxQx7NR" + }, + "source": [ + "# Summarize a document\n", + "\n", + "The next section retrieves an article, extracts text from it (more to come on this topic) and summarizes that text." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 53 + }, + "id": "idPThgJGvIju", + "outputId": "7d0580e6-2531-48c9-a32a-481ccf32900d" + }, + "source": [ + "!wget -q \"https://medium.com/neuml/time-lapse-video-for-the-web-a7d8874ff397\"\n", + "\n", + "from txtai.pipeline import Textractor\n", + "\n", + "textractor = Textractor()\n", + "text = textractor(\"time-lapse-video-for-the-web-a7d8874ff397\")\n", + "\n", + "summary(text)" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'Time-lapse video is a popular way to show an area or event over a long period of time. The same concept can be applied to a dynamic real-time website with frequently updated data. webelapse is an open source project developed to provide this functionality. It can be used as is or modified for different use cases.'" + ] + }, + "metadata": {}, + "execution_count": 4 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "a63k89aDyKTW" + }, + "source": [ + "Click through the link to see the full article. This summary does a pretty good job of covering what the article is about!" + ] + } + ] +} \ No newline at end of file diff --git a/examples/10_Extract_text_from_documents.ipynb b/examples/10_Extract_text_from_documents.ipynb new file mode 100644 index 0000000..b44f8a7 --- /dev/null +++ b/examples/10_Extract_text_from_documents.ipynb @@ -0,0 +1,362 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "4Pjmz-RORV8E" + }, + "source": [ + "# Extract text from documents\n", + "\n", + "Up to this point, all the examples have been working with sections of text, which have already been split through some other means. What happens if we're working with documents? First we need to get the text out of these documents, then figure out how to index to best support vector search.\n", + "\n", + "This notebook shows how documents can have text extracted and split to support vector search and retrieval augmented generation (RAG)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dk31rbYjSTYm" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies. Since this notebook is using optional pipelines, we need to install the pipeline extras package." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "XMQuuun2R06J" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline]\n", + "\n", + "# Get test data\n", + "!wget -N https://github.com/neuml/txtai/releases/download/v6.2.0/tests.tar.gz\n", + "!tar -xvzf tests.tar.gz\n", + "\n", + "# Install NLTK\n", + "import nltk\n", + "nltk.download(['punkt', 'punkt_tab'])" + ], + "execution_count": 19, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PNPJ95cdTKSS" + }, + "source": [ + "# Create a Textractor instance\n", + "\n", + "The Textractor instance is the main entrypoint for extracting text. This method is backed by Apache Tika, a robust text extraction library written in Java. [Apache Tika](https://tika.apache.org/0.9/formats.html) has support for a large number of file formats: PDF, Word, Excel, HTML and others. The [Python Tika package](https://github.com/chrismattmann/tika-python) automatically installs Tika and starts a local REST API instance used to read extracted data.\n", + "\n", + "*Note: This requires Java to be installed locally.*" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "nTDwXOUeTH2-" + }, + "source": [ + "%%capture\n", + "\n", + "from txtai.pipeline import Textractor\n", + "\n", + "# Create textractor model\n", + "textractor = Textractor()" + ], + "execution_count": 20, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-vGR_piwZZO6" + }, + "source": [ + "# Extract text\n", + "\n", + "The example below shows how to extract text from a file." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 118 + }, + "id": "-K2YJJzsVtfq", + "outputId": "7754c508-264a-41fa-9843-83460719820f" + }, + "source": [ + "textractor(\"txtai/article.pdf\")" + ], + "execution_count": 21, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "'Introducing txtai, an AI-powered search engine \\nbuilt on Transformers\\n\\nAdd Natural Language Understanding to any application\\n\\nSearch is the base of many applications. Once data starts to pile up, users want to be able to find it. It’s \\nthe foundation of the internet and an ever-growing challenge that is never solved or done.\\n\\nThe field of Natural Language Processing (NLP) is rapidly evolving with a number of new \\ndevelopments. Large-scale general language models are an exciting new capability allowing us to add \\namazing functionality quickly with limited compute and people. Innovation continues with new models\\nand advancements coming in at what seems a weekly basis.\\n\\nThis article introduces txtai, an AI-powered search engine that enables Natural Language \\nUnderstanding (NLU) based search in any application.\\n\\nIntroducing txtai\\ntxtai builds an AI-powered index over sections of text. txtai supports building text indices to perform \\nsimilarity searches and create extractive question-answering based systems. txtai also has functionality \\nfor zero-shot classification. txtai is open source and available on GitHub.\\n\\ntxtai and/or the concepts behind it has already been used to power the Natural Language Processing \\n(NLP) applications listed below:\\n\\n• paperai — AI-powered literature discovery and review engine for medical/scientific papers\\n• tldrstory — AI-powered understanding of headlines and story text\\n• neuspo — Fact-driven, real-time sports event and news site\\n• codequestion — Ask coding questions directly from the terminal\\n\\nBuild an Embeddings index\\nFor small lists of texts, the method above works. But for larger repositories of documents, it doesn’t \\nmake sense to tokenize and convert all embeddings for each query. txtai supports building pre-\\ncomputed indices which significantly improves performance.\\n\\nBuilding on the previous example, the following example runs an index method to build and store the \\ntext embeddings. In this case, only the query is converted to an embeddings vector each search.\\n\\nhttps://github.com/neuml/codequestion\\nhttps://neuspo.com/\\nhttps://github.com/neuml/tldrstory\\nhttps://github.com/neuml/paperai\\n - Introducing txtai, an AI-powered search engine built on Transformers\\n - Add Natural Language Understanding to any application\\n - Introducing txtai\\n - Build an Embeddings index'" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + } + }, + "metadata": {}, + "execution_count": 21 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "n2jndgE-JyWX" + }, + "source": [ + "Note that the text from the article was extracted into a single string. Depending on the articles, this may be acceptable. For long articles, often you'll want to split the content into logical sections to build better downstream vectors." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1w2bhBCPOUdu" + }, + "source": [ + "# Extract sentences\n", + "\n", + "Sentence extraction uses a model that specializes in sentence detection. This call returns a list of sentences." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "PKZVK5vuOTqB", + "outputId": "a31e182e-037b-4e29-c1b0-0f6815e3b2c9" + }, + "source": [ + "textractor = Textractor(sentences=True)\n", + "textractor(\"txtai/article.pdf\")" + ], + "execution_count": 22, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "['Introducing txtai, an AI-powered search engine \\nbuilt on Transformers\\n\\nAdd Natural Language Understanding to any application\\n\\nSearch is the base of many applications.',\n", + " 'Once data starts to pile up, users want to be able to find it.',\n", + " 'It’s \\nthe foundation of the internet and an ever-growing challenge that is never solved or done.',\n", + " 'The field of Natural Language Processing (NLP) is rapidly evolving with a number of new \\ndevelopments.',\n", + " 'Large-scale general language models are an exciting new capability allowing us to add \\namazing functionality quickly with limited compute and people.',\n", + " 'Innovation continues with new models\\nand advancements coming in at what seems a weekly basis.',\n", + " 'This article introduces txtai, an AI-powered search engine that enables Natural Language \\nUnderstanding (NLU) based search in any application.',\n", + " 'Introducing txtai\\ntxtai builds an AI-powered index over sections of text.',\n", + " 'txtai supports building text indices to perform \\nsimilarity searches and create extractive question-answering based systems.',\n", + " 'txtai also has functionality \\nfor zero-shot classification.',\n", + " 'txtai is open source and available on GitHub.',\n", + " 'txtai and/or the concepts behind it has already been used to power the Natural Language Processing \\n(NLP) applications listed below:\\n\\n• paperai — AI-powered literature discovery and review engine for medical/scientific papers\\n• tldrstory — AI-powered understanding of headlines and story text\\n• neuspo — Fact-driven, real-time sports event and news site\\n• codequestion — Ask coding questions directly from the terminal\\n\\nBuild an Embeddings index\\nFor small lists of texts, the method above works.',\n", + " 'But for larger repositories of documents, it doesn’t \\nmake sense to tokenize and convert all embeddings for each query.',\n", + " 'txtai supports building pre-\\ncomputed indices which significantly improves performance.',\n", + " 'Building on the previous example, the following example runs an index method to build and store the \\ntext embeddings.',\n", + " 'In this case, only the query is converted to an embeddings vector each search.',\n", + " 'https://github.com/neuml/codequestion\\nhttps://neuspo.com/\\nhttps://github.com/neuml/tldrstory\\nhttps://github.com/neuml/paperai\\n - Introducing txtai, an AI-powered search engine built on Transformers\\n - Add Natural Language Understanding to any application\\n - Introducing txtai\\n - Build an Embeddings index']" + ] + }, + "metadata": {}, + "execution_count": 22 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vdVCCc9UOv5S" + }, + "source": [ + "Now the document is split up at the sentence level. These sentences can be feed to a workflow that adds each sentence to an embeddings index. Depending on the task, this may work well. Alternatively, it may be even better to split at the paragraph level." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "z1H8XYkaSoP4" + }, + "source": [ + "# Extract paragraphs\n", + "\n", + "Paragraph detection looks for consecutive newlines. This call returns a list of paragraphs." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "9VUito4ISoAe", + "outputId": "08079380-a7c8-4886-ecc3-de9f02be4584" + }, + "source": [ + "textractor = Textractor(paragraphs=True)\n", + "for paragraph in textractor(\"txtai/article.pdf\"):\n", + " print(paragraph, \"\\n----\")" + ], + "execution_count": 23, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Introducing txtai, an AI-powered search engine \n", + "built on Transformers \n", + "----\n", + "Add Natural Language Understanding to any application \n", + "----\n", + "Search is the base of many applications. Once data starts to pile up, users want to be able to find it. It’s \n", + "the foundation of the internet and an ever-growing challenge that is never solved or done. \n", + "----\n", + "The field of Natural Language Processing (NLP) is rapidly evolving with a number of new \n", + "developments. Large-scale general language models are an exciting new capability allowing us to add \n", + "amazing functionality quickly with limited compute and people. Innovation continues with new models\n", + "and advancements coming in at what seems a weekly basis. \n", + "----\n", + "This article introduces txtai, an AI-powered search engine that enables Natural Language \n", + "Understanding (NLU) based search in any application. \n", + "----\n", + "Introducing txtai\n", + "txtai builds an AI-powered index over sections of text. txtai supports building text indices to perform \n", + "similarity searches and create extractive question-answering based systems. txtai also has functionality \n", + "for zero-shot classification. txtai is open source and available on GitHub. \n", + "----\n", + "txtai and/or the concepts behind it has already been used to power the Natural Language Processing \n", + "(NLP) applications listed below: \n", + "----\n", + "• paperai — AI-powered literature discovery and review engine for medical/scientific papers\n", + "• tldrstory — AI-powered understanding of headlines and story text\n", + "• neuspo — Fact-driven, real-time sports event and news site\n", + "• codequestion — Ask coding questions directly from the terminal \n", + "----\n", + "Build an Embeddings index\n", + "For small lists of texts, the method above works. But for larger repositories of documents, it doesn’t \n", + "make sense to tokenize and convert all embeddings for each query. txtai supports building pre-\n", + "computed indices which significantly improves performance. \n", + "----\n", + "Building on the previous example, the following example runs an index method to build and store the \n", + "text embeddings. In this case, only the query is converted to an embeddings vector each search. \n", + "----\n", + "https://github.com/neuml/codequestion\n", + "https://neuspo.com/\n", + "https://github.com/neuml/tldrstory\n", + "https://github.com/neuml/paperai\n", + " - Introducing txtai, an AI-powered search engine built on Transformers\n", + " - Add Natural Language Understanding to any application\n", + " - Introducing txtai\n", + " - Build an Embeddings index \n", + "----\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Extract sections\n", + "\n", + "Section extraction is format dependent. If page breaks are available, each section is a page. Otherwise, this call returns logical sections such by headings." + ], + "metadata": { + "id": "Ae6dRQ2LvN-w" + } + }, + { + "cell_type": "code", + "source": [ + "textractor = Textractor(sections=True)\n", + "print(\"\\n[PAGE BREAK]\\n\".join(section for section in textractor(\"txtai/article.pdf\")))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "nQ6ev2UMwqnh", + "outputId": "3d45491d-3547-4218-d30c-ba0c4d161256" + }, + "execution_count": 24, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Introducing txtai, an AI-powered search engine \n", + "built on Transformers\n", + "\n", + "Add Natural Language Understanding to any application\n", + "\n", + "Search is the base of many applications. Once data starts to pile up, users want to be able to find it. It’s \n", + "the foundation of the internet and an ever-growing challenge that is never solved or done.\n", + "\n", + "The field of Natural Language Processing (NLP) is rapidly evolving with a number of new \n", + "developments. Large-scale general language models are an exciting new capability allowing us to add \n", + "amazing functionality quickly with limited compute and people. Innovation continues with new models\n", + "and advancements coming in at what seems a weekly basis.\n", + "\n", + "This article introduces txtai, an AI-powered search engine that enables Natural Language \n", + "Understanding (NLU) based search in any application.\n", + "\n", + "Introducing txtai\n", + "txtai builds an AI-powered index over sections of text. txtai supports building text indices to perform \n", + "similarity searches and create extractive question-answering based systems. txtai also has functionality \n", + "for zero-shot classification. txtai is open source and available on GitHub.\n", + "\n", + "txtai and/or the concepts behind it has already been used to power the Natural Language Processing \n", + "(NLP) applications listed below:\n", + "\n", + "• paperai — AI-powered literature discovery and review engine for medical/scientific papers\n", + "• tldrstory — AI-powered understanding of headlines and story text\n", + "• neuspo — Fact-driven, real-time sports event and news site\n", + "• codequestion — Ask coding questions directly from the terminal\n", + "\n", + "Build an Embeddings index\n", + "For small lists of texts, the method above works. But for larger repositories of documents, it doesn’t \n", + "make sense to tokenize and convert all embeddings for each query. txtai supports building pre-\n", + "computed indices which significantly improves performance.\n", + "\n", + "Building on the previous example, the following example runs an index method to build and store the \n", + "text embeddings. In this case, only the query is converted to an embeddings vector each search.\n", + "\n", + "https://github.com/neuml/codequestion\n", + "https://neuspo.com/\n", + "https://github.com/neuml/tldrstory\n", + "https://github.com/neuml/paperai\n", + "[PAGE BREAK]\n", + "- Introducing txtai, an AI-powered search engine built on Transformers\n", + " - Add Natural Language Understanding to any application\n", + " - Introducing txtai\n", + " - Build an Embeddings index\n" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/examples/11_Transcribe_audio_to_text.ipynb b/examples/11_Transcribe_audio_to_text.ipynb new file mode 100644 index 0000000..32ba150 --- /dev/null +++ b/examples/11_Transcribe_audio_to_text.ipynb @@ -0,0 +1,463 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "4Pjmz-RORV8E" + }, + "source": [ + "# Transcribe audio to text\n", + "\n", + "This notebook covers the transcription of audio files to text using models provided by Hugging Face." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dk31rbYjSTYm" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies. Since this notebook is using optional pipelines, we need to install the pipeline extras package. We'll also demonstrate running this pipeline through the API." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "XMQuuun2R06J" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[api,pipeline]\n", + "\n", + "# Get test data\n", + "!wget -N https://github.com/neuml/txtai/releases/download/v3.5.0/tests.tar.gz\n", + "!tar -xvzf tests.tar.gz" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PNPJ95cdTKSS" + }, + "source": [ + "# Create a Transcription instance\n", + "\n", + "The Transcription instance is the main entrypoint for transcribing audio to text. The pipeline abstracts transcribing audio into a one line call! \n", + "\n", + "The pipeline executes logic to read audio files into memory, run the data through a machine learning model and output the results to text.\n", + "\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "nTDwXOUeTH2-" + }, + "source": [ + "%%capture\n", + "\n", + "from txtai.pipeline import Transcription\n", + "\n", + "# Create transcription model\n", + "transcribe = Transcription()" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-vGR_piwZZO6" + }, + "source": [ + "# Transcribe audio to text\n", + "\n", + "The example below shows how to transcribe a list of audio files to text. Let's transcribe audio to text and look at each result." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 497 + }, + "id": "-K2YJJzsVtfq", + "outputId": "7f2fe352-de55-428e-f15a-28f389498961" + }, + "source": [ + "from IPython.display import Audio, display\n", + "\n", + "files = [\"Beijing_mobilises.wav\", \"Canadas_last_fully.wav\", \"Maine_man_wins_1_mil.wav\", \"Make_huge_profits.wav\", \"The_National_Park.wav\", \"US_tops_5_million.wav\"]\n", + "files = [\"txtai/%s\" % x for x in files]\n", + "\n", + "for x, text in enumerate(transcribe(files)):\n", + " display(Audio(files[x]))\n", + " print(text)\n", + " print()\n" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Baging mobilizes invasion kraft along coast as tie one tensions escalates\n", + "\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Canodas last fully intact ice shelf has suddenly collapsed forming a manhattan sized iceberge\n", + "\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Main man wins from lottery ticket\n", + "\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Make huge profits without working make up to one hundred thousand dollars a day\n", + "\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "National park service warns against sacrificing slower friends in a bare attack\n", + "\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Ues virus cases top a million\n", + "\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Xn3SlVE1LYvm" + }, + "source": [ + "Overall, the results are solid. Each result sounds phonetically like the audio." + ] + }, + { + "cell_type": "markdown", + "source": [ + "# OpenAI Whisper\n", + "\n", + "In September 2022, [OpenAI Whisper](https://github.com/openai/whisper) was released. This model brings a dramatic improvement in transcription quality. Whisper support was added to Hugging Face Transformers in v4.23.0. Let's give it a try." + ], + "metadata": { + "id": "bDxW-tsCELob" + } + }, + { + "cell_type": "code", + "source": [ + "# Transcribe files\n", + "transcribe = Transcription(\"openai/whisper-base\")\n", + "for text in transcribe(files):\n", + " print(text)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-KgYwAQzFVll", + "outputId": "7b1e9541-1a6f-4814-ae14-21f4bd9794e7" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Beijing mobilizes invasion craft along coast as Taiwan tensions escalate.\n", + "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan sized iceberg.\n", + "Maine Man wins from lottery ticket.\n", + "make huge profits without working. Make up to $100,000 a day.\n", + "National Park Service warns against sacrificing slower friends in a bear attack.\n", + "U.S. virus cases top of million.\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Results were transcribed with near perfect accuracy, amazing!\n", + "\n", + "This can also be run as a txtai application or API instance. Let's try a full indexing workflow with a txtai application." + ], + "metadata": { + "id": "fgMr3B2_IoeN" + } + }, + { + "cell_type": "code", + "source": [ + "%%writefile workflow.yml\n", + "writable: true\n", + "\n", + "embeddings:\n", + " path: sentence-transformers/nli-mpnet-base-v2\n", + " content: true\n", + "\n", + "transcription:\n", + " path: openai/whisper-base\n", + "\n", + "workflow:\n", + " index:\n", + " tasks:\n", + " - transcription\n", + " - index" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "k3Rb2OU9Mq3h", + "outputId": "5e40ecd5-3df1-4864-c7a3-3e26bd087415" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Overwriting workflow.yml\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "from txtai.app import Application\n", + "\n", + "app = Application(\"workflow.yml\")\n", + "\n", + "list(app.workflow(\"index\", files))\n", + "app.search(\"feel good story\", 1)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cj49U095IxTc", + "outputId": "a55b8717-f5fc-4e8f-ef1b-5fecc3f8200a" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '2',\n", + " 'text': 'Maine Man wins from lottery ticket.',\n", + " 'score': 0.1285860687494278}]" + ] + }, + "metadata": {}, + "execution_count": 26 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "This workflow transcribed the input files, loaded the transcriptions into an embeddings index and finally ran a search. Last thing we'll do is run the workflow as an API instance." + ], + "metadata": { + "id": "k6ptCiEyR8p2" + } + }, + { + "cell_type": "code", + "source": [ + "!CONFIG=workflow.yml uvicorn \"txtai.api:app\" &> api.log &\n", + "!sleep 30\n", + "\n", + "# Run indexing workflow\n", + "!curl -s -o /dev/null \\\n", + " -X POST \"http://localhost:8000/workflow\" \\\n", + " -H \"Content-Type: application/json\" \\\n", + " -d '{\"name\":\"index\", \"elements\":[\"txtai/Beijing_mobilises.wav\", \"txtai/Canadas_last_fully.wav\", \"txtai/Maine_man_wins_1_mil.wav\", \"txtai/Make_huge_profits.wav\", \"txtai/The_National_Park.wav\", \"txtai/US_tops_5_million.wav\"]}'\n", + "\n", + "# Test API search\n", + "!curl \"http://localhost:8000/search?query=feel+good+story&limit=1\"" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "XDpqwf8PNHNo", + "outputId": "7ec28122-69a3-47cc-d2ed-acaa186e4aa1" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[{\"id\":\"2\",\"text\":\"Maine Man wins from lottery ticket.\",\"score\":0.1285860687494278}]" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Once again, the same results as in Python and with an application." + ], + "metadata": { + "id": "jfdVtwbBIN4Q" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "There is a lot of development in the audio transcription space. In only a couple of lines of code, high-quality transcription models are now readily available!" + ], + "metadata": { + "id": "VCU8zGGDXQ0Y" + } + } + ] +} \ No newline at end of file diff --git a/examples/12_Translate_text_between_languages.ipynb b/examples/12_Translate_text_between_languages.ipynb new file mode 100644 index 0000000..260fe2e --- /dev/null +++ b/examples/12_Translate_text_between_languages.ipynb @@ -0,0 +1,374 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "4Pjmz-RORV8E" + }, + "source": [ + "# Translate text between languages\n", + "\n", + "This notebook covers machine translation backed by Hugging Face models. The quality of machine translation via cloud services has come a very long way and produces high quality results. This notebook shows how the models from Hugging Face give developers a reasonable alternative for local machine translation." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dk31rbYjSTYm" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies. Since this notebook is using optional pipelines, we need to install the pipeline extras package." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "XMQuuun2R06J" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline]" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PNPJ95cdTKSS" + }, + "source": [ + "# Create a Translation instance\n", + "\n", + "The Translation instance is the main entrypoint for translating text between languages. The pipeline abstracts translating text into a one line call! \n", + "\n", + "The pipeline has logic to detect the input language, load the relevant model that handles translating from source to target language and return results. The translation pipeline also has built-in logic to handle splitting large text blocks into smaller sections the models can handle.\n", + "\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "nTDwXOUeTH2-" + }, + "source": [ + "%%capture\n", + "\n", + "from txtai.pipeline import Translation\n", + "\n", + "# Create translation model\n", + "translate = Translation()" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-vGR_piwZZO6" + }, + "source": [ + "# Translate text\n", + "\n", + "The example below shows how to translate text from English to Spanish. This text is then translated back to English." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 36 + }, + "id": "-K2YJJzsVtfq", + "outputId": "44df5404-ea14-4746-fc8b-a2e205bd9466" + }, + "source": [ + "translation = translate(\"This is a test translation into Spanish\", \"es\")\n", + "translation" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "'Esta es una traducción de prueba al español'" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + } + }, + "metadata": {}, + "execution_count": 16 + } + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 36 + }, + "id": "K_UnAZQpetM8", + "outputId": "46c9c68c-ddcf-4f55-bac3-89f25931e91b" + }, + "source": [ + "translate(translation, \"en\")" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "'This is a test translation into Spanish'" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + } + }, + "metadata": {}, + "execution_count": 17 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4cSI8GdtjhEM" + }, + "source": [ + "# Translating multiple languages in a single call\n", + "\n", + "The section below translates a single English sentence into 5 different languages. The results are then passed to a single translation call to translate back into English. The pipeline detects each input language and is able to load the relevant translation models." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "8jLxGtwNf0Aj", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "47040b2f-f6e5-482a-df81-7b758f47a7d5" + }, + "source": [ + "def run():\n", + " languages = [\"fr\", \"es\", \"de\", \"hi\", \"ja\"]\n", + " translations = [translate(\"The sky is blue, the stars are far\", language) for language in languages]\n", + " english = translate(translations, \"en\")\n", + "\n", + " for x, text in enumerate(translations):\n", + " print(\"Original Language: %s\" % languages[x])\n", + " print(\"Translation: %s\" % text)\n", + " print(\"Back to English: %s\" % english[x])\n", + " print()\n", + "\n", + "# Run multiple translations\n", + "run()" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Original Language: fr\n", + "Translation: Le ciel est bleu, les étoiles sont loin\n", + "Back to English: The sky is blue, the stars are far away\n", + "\n", + "Original Language: es\n", + "Translation: El cielo es azul, las estrellas están lejos.\n", + "Back to English: The sky is blue, the stars are far away.\n", + "\n", + "Original Language: de\n", + "Translation: Der Himmel ist blau, die Sterne sind weit\n", + "Back to English: The sky is blue, the stars are wide\n", + "\n", + "Original Language: hi\n", + "Translation: आकाश नीला है, तारे दूर हैं\n", + "Back to English: Sky is blue, stars are away\n", + "\n", + "Original Language: ja\n", + "Translation: 天は青い、星は遠い。\n", + "Back to English: The heavens are blue and the stars are far away.\n", + "\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Xn3SlVE1LYvm" + }, + "source": [ + "The translation quality overall is very high!" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Additional model types\n", + "\n", + "The translation pipeline is flexible and supports multiple model types. The default mode for the pipeline is to scan the Hugging Face Hub for models that best match the source-target translation pair. This often produces the best quality and is usually a smaller model than a large multi-language mode.\n", + "\n", + "There is a parameter that can override this and always use the base model." + ], + "metadata": { + "id": "3FdS5slz60eA" + } + }, + { + "cell_type": "code", + "source": [ + "translate = Translation(\"t5-small\", findmodels=False)\n", + "translate(\"translate English to French: The sky is blue, the stars are far\", None)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 36 + }, + "id": "REb0X2Kz60Ew", + "outputId": "ae13d4d0-318f-4740-f725-0029ceeabeac" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "'Le ciel est bleu, les étoiles sont loin'" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + } + }, + "metadata": {}, + "execution_count": 11 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Translation isn't limited to spoken languages. txtai provides a text-to-sql model that converts English text into a txtai-compatible SQL statement. " + ], + "metadata": { + "id": "5vSJbW7zVJeH" + } + }, + { + "cell_type": "code", + "source": [ + "translate = Translation(\"NeuML/t5-small-txtsql\", findmodels=False)\n", + "translate(\"translate English to SQL: feel good story since yesterday\", None)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 36 + }, + "id": "d5usam_jhKaz", + "outputId": "c12368a0-ea26-4191-be5a-f2de70711003" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "\"select id, text, score from txtai where similar('feel good story') and entry >= date('now', '-1 day')\"" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + } + }, + "metadata": {}, + "execution_count": 12 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Last thing we'll do is run the multiple language example only using a single large language model." + ], + "metadata": { + "id": "cVb7uk7TXr_v" + } + }, + { + "cell_type": "code", + "source": [ + "translate = Translation(\"facebook/mbart-large-50-many-to-many-mmt\", findmodels=False)\n", + "run()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "JkOLnvKZWI95", + "outputId": "fef6402c-e20e-43e1-a3eb-e4580913fa7e" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Original Language: fr\n", + "Translation: Le ciel est bleu, les étoiles sont loin\n", + "Back to English: The sky is blue, the stars are far away\n", + "\n", + "Original Language: es\n", + "Translation: El cielo es azul, las estrellas están lejos.\n", + "Back to English: The sky is blue, the stars are far away.\n", + "\n", + "Original Language: de\n", + "Translation: Der Himmel ist blau, die Sterne sind weit\n", + "Back to English: The sky is blue, the stars are far.\n", + "\n", + "Original Language: hi\n", + "Translation: आकाश नीली है, तारे दूर हैं।\n", + "Back to English: The sky is blue, and the stars are far away.\n", + "\n", + "Original Language: ja\n", + "Translation: 空は青い、星は遠い\n", + "Back to English: the sky is blue, the stars are far away.\n", + "\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "Machine translation has made giant leaps and strides the last couple of years. These models give developers a solid, locally-hosted alternative to cloud translation services. Additionally, there are models built for low resource languages that cloud translation services don't support.\n", + "\n", + "A number of different models and configurations are supported, give it a try!" + ], + "metadata": { + "id": "TvWx9PS-X32c" + } + } + ] +} \ No newline at end of file diff --git a/examples/13_Similarity_search_with_images.ipynb b/examples/13_Similarity_search_with_images.ipynb new file mode 100644 index 0000000..3bf1ce6 --- /dev/null +++ b/examples/13_Similarity_search_with_images.ipynb @@ -0,0 +1,1368 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "4Pjmz-RORV8E" + }, + "source": [ + "# Similarity search with images\n", + "\n", + "txtai as the name implies works with text and ai, pretty straightforward. But that doesn't mean it can't work with different types of content. For example, an image can be described with words. We can use that description to compare an image to a query or other documents. This notebook shows how images and text can be embedded into the same space to support similarity search." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dk31rbYjSTYm" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies. Since this notebook uses sentence-transformers directly, we need to install the similarity extras package." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "id": "XMQuuun2R06J" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install torchvision ipyplot git+https://github.com/neuml/txtai#egg=txtai[similarity]\n", + "\n", + "# Get test data\n", + "!wget -N https://github.com/neuml/txtai/releases/download/v3.5.0/tests.tar.gz\n", + "!tar -xvzf tests.tar.gz" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PNPJ95cdTKSS" + }, + "source": [ + "# Create an Embeddings model\n", + "\n", + "[sentence-transformers](https://github.com/UKPLab/sentence-transformers/tree/master/examples/applications/image-search) has support for the [OpenAI CLIP model](https://github.com/openai/CLIP). This model embeds text and images into the same space, enabling image similarity search. txtai can directly utilize these models through sentence-transformers. Check out the sentence-transformers link above for additional examples on how to use this model.\n", + "\n", + "This section builds an embeddings index over a series of images.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "id": "nTDwXOUeTH2-" + }, + "outputs": [], + "source": [ + "%%capture\n", + "\n", + "import glob\n", + "\n", + "from PIL import Image\n", + "\n", + "from txtai.embeddings import Embeddings\n", + "from txtai.pipeline import Caption\n", + "\n", + "def images():\n", + " # Create image caption pipeline\n", + " caption = Caption()\n", + "\n", + " for path in glob.glob('txtai/*jpg'):\n", + " # Add image object along with image metadata\n", + " image = Image.open(path)\n", + "\n", + " yield (path, {\"object\": image, \"format\": image.format, \"width\": image.width, \"height\": image.height, \"caption\": caption(image)}, None)\n", + "\n", + "# Index with content and objects\n", + "embeddings = Embeddings({\"method\": \"sentence-transformers\", \"path\": \"sentence-transformers/clip-ViT-B-32\", \"content\": True, \"objects\": \"image\"})\n", + "embeddings.index(images())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PTZbRHiE5_l3" + }, + "source": [ + "Next let's query and see what's available in the index." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "040r95YG1w3J", + "outputId": "65a2b2b2-6153-4f3d-e32c-5e4e661956ab" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': 'txtai/books.jpg',\n", + " 'object': ,\n", + " 'format': 'JPEG',\n", + " 'width': 1024,\n", + " 'height': 682,\n", + " 'caption': 'a book shelf filled with books and a stack of books'},\n", + " {'id': 'txtai/buildings.jpg',\n", + " 'object': ,\n", + " 'format': 'JPEG',\n", + " 'width': 700,\n", + " 'height': 466,\n", + " 'caption': 'a city skyline with buildings and a sky background'},\n", + " {'id': 'txtai/chop.jpg',\n", + " 'object': ,\n", + " 'format': 'JPEG',\n", + " 'width': 700,\n", + " 'height': 466,\n", + " 'caption': 'a tree branch with a person holding a stick'}]" + ] + }, + "metadata": {}, + "execution_count": 9 + } + ], + "source": [ + "embeddings.search(\"select id, object, format, width, height, caption from txtai\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "r5GjmdCA6IPJ" + }, + "source": [ + "The query above shows the metadata that was added in addition to the image object. These fields can be retrieved on search and/or used to filter results." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HLf0KrXwLH5-" + }, + "source": [ + "# Search the index\n", + "\n", + "Now that we have an index, let's search it! This section runs a list of queries against the index and shows the top result for each query. Have to say this is pretty 🔥🔥🔥" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 600 + }, + "id": "WHTq86MG9UBF", + "outputId": "b679e3d3-a82a-43bb-c59e-c7e9a3ce0577" + }, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + "
\n", + "
\n", + "
\n", + "

Walking into the office

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Saturday cleaning the yard

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Working on the latest analysis

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Working on my homework

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Watching an exciting race

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

The universe is massive

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Time lapse video of traffic

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Relaxing Thanksgiving day

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + "
" + ] + }, + "metadata": {} + } + ], + "source": [ + "import ipyplot\n", + "from PIL import Image\n", + "\n", + "def resize(images):\n", + " results = []\n", + " for image in images:\n", + " results.append(image.resize((350, int(image.height * (350 / image.width))), Image.Resampling.LANCZOS))\n", + "\n", + " return results\n", + "\n", + "images, labels = [], []\n", + "for query in [\"Walking into the office\", \"Saturday cleaning the yard\", \"Working on the latest analysis\", \"Working on my homework\", \"Watching an exciting race\",\n", + " \"The universe is massive\", \"Time lapse video of traffic\", \"Relaxing Thanksgiving day\"]:\n", + " result = embeddings.search(f\"select object from txtai where similar(\\\"{query}\\\")\", 1)[0]\n", + " images.append(result[\"object\"])\n", + " labels.append(query)\n", + "\n", + "ipyplot.plot_images(resize(images), labels, img_width=350, force_b64=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8BYDpAeoiOvt" + }, + "source": [ + "# Search with SQL\n", + "\n", + "txtai has support for SQL bind parameters, which enables similarity search with binary content." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 317 + }, + "id": "a2V0wE84iWkh", + "outputId": "f98abb71-1679-40fb-d3b4-98726b10dfe6" + }, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + "
\n", + "
\n", + "
\n", + "

Result

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + "
" + ] + }, + "metadata": {} + } + ], + "source": [ + "result = embeddings.search(f\"select object from txtai where similar(:x)\", 1, parameters={\"x\": Image.open(\"txtai/books.jpg\")})[0]\n", + "\n", + "ipyplot.plot_images(resize([result[\"object\"]]), [\"Result\"], img_width=350, force_b64=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0d1WVJyQkZ8A" + }, + "source": [ + "# Multilingual Support\n", + "\n", + "sentence-transformers also has a [model](https://huggingface.co/sentence-transformers/clip-ViT-B-32-multilingual-v1) that supports over 50+ languages. This enables running queries using those languages with an image index.\n", + "\n", + "Note this model only supports text, so images must first be indexed with the model used above." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 630 + }, + "id": "e8BxURU6gZV3", + "outputId": "e58335f5-25ee-4c9a-bc90-b0027f21cdef" + }, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + "
\n", + "
\n", + "
\n", + "

Zu Fuß ins Büro
(Walking into the office)

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Samstag Reinigung des Hofes
(Saturday cleaning the yard)

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Arbeiten an der neuesten Analyse
(Working on the latest analysis)

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Arbeiten an meinen Hausaufgaben
(Working on my homework)

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Ein spannendes Rennen beobachten
(Watching an exciting race)

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Das Universum ist riesig
(The universe is massive)

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Zeitraffer Video des Verkehrs
(Time lapse video of traffic)

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Entspannender Thanksgiving-Tag
(Relaxing Thanksgiving day)

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + "
" + ] + }, + "metadata": {} + } + ], + "source": [ + "import ipyplot\n", + "\n", + "from txtai.pipeline import Translation\n", + "\n", + "# Update model at query time to support multilingual queries\n", + "embeddings.config[\"path\"] = \"sentence-transformers/clip-ViT-B-32-multilingual-v1\"\n", + "embeddings.model = embeddings.loadvectors()\n", + "\n", + "# Translate queries to German\n", + "queries = [\"Walking into the office\", \"Saturday cleaning the yard\", \"Working on the latest analysis\", \"Working on my homework\", \"Watching an exciting race\",\n", + " \"The universe is massive\", \"Time lapse video of traffic\", \"Relaxing Thanksgiving day\"]\n", + "translate = Translation()\n", + "translated = translate(queries, \"de\")\n", + "\n", + "images, labels = [], []\n", + "for x, query in enumerate(translated):\n", + " result = embeddings.search(f\"select object from txtai where similar(:x)\", 1, parameters={\"x\": query})[0]\n", + "\n", + " images.append(result[\"object\"])\n", + " labels.append(\"%s
(%s)\" % (query, queries[x]))\n", + "\n", + "ipyplot.plot_images(resize(images), labels, img_width=350, force_b64=True)" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/examples/14_Run_pipeline_workflows.ipynb b/examples/14_Run_pipeline_workflows.ipynb new file mode 100644 index 0000000..57c4c94 --- /dev/null +++ b/examples/14_Run_pipeline_workflows.ipynb @@ -0,0 +1,526 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "4Pjmz-RORV8E" + }, + "source": [ + "# Run pipeline workflows\n", + "\n", + "txtai has a growing list of models available through it's pipeline framework. Pipelines wrap a machine learning model and transform data. Currently, pipelines can wrap Hugging Face models, Hugging Face pipelines or PyTorch models (support for TensorFlow is in the backlog).\n", + "\n", + "The following is a list of the currently implemented pipelines.\n", + "\n", + "* **Questions** - Answer questions using a text context\n", + "* **Labels** - Apply labels to text using a zero-shot classification model. Also supports similarity comparisions.\n", + "* **Summary** - Abstractive text summarization\n", + "* **Textractor** - Extract text from documents\n", + "* **Transcription** - Transcribe audio to text\n", + "* **Translation** - Machine translation\n", + "\n", + "Pipelines are great and make using a variety of machine learning models easier. But what if we want to glue the results of different pipelines together? For example, extract text, summarize it, translate it to English and load it into an Embedding index. That would require code to join those operations together in an efficient manner.\n", + "\n", + "Enter workflows. Workflows are a simple yet powerful construct that takes a callable and returns elements. Workflows don't know they are working with pipelines but enable efficient processing of pipeline data. Workflows are streaming by nature and work on data in batches, allowing large volumes of data to be processed efficiently." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dk31rbYjSTYm" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies. Since this notebook is using optional pipelines/workflows, we need to install the pipeline and workflow extras package." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "XMQuuun2R06J" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline,workflow] sacremoses\n", + "\n", + "# Get test data\n", + "!wget -N https://github.com/neuml/txtai/releases/download/v2.0.0/tests.tar.gz\n", + "!tar -xvzf tests.tar.gz" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "I1dNQE7WT4kE" + }, + "source": [ + "# Create a series of pipelines to use in this notebook" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "w4YqwBJaT4QD" + }, + "source": [ + "%%capture\n", + "from txtai.pipeline import Summary, Textractor, Transcription, Translation\n", + "\n", + "# Summary instance\n", + "summary = Summary()\n", + "\n", + "# Text extraction\n", + "textractor = Textractor()\n", + "\n", + "# Transcription instance\n", + "transcribe = Transcription(\"facebook/wav2vec2-large-960h\")\n", + "\n", + "# Create a translation instance\n", + "translate = Translation()" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PNPJ95cdTKSS" + }, + "source": [ + "# Basic workflow\n", + "\n", + "The following shows a basic workflow in action!" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "nTDwXOUeTH2-", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "906d4354-cf29-4593-a790-8c175d981dee" + }, + "source": [ + "from txtai.workflow import Workflow, Task\n", + "\n", + "# Workflow that translate text to French\n", + "workflow = Workflow([Task(lambda x: translate(x, \"fr\"))])\n", + "\n", + "# Data to run through the pipeline\n", + "data = [\"The sky is blue\", \"Forest through the trees\"]\n", + "\n", + "# Workflows are generators for efficiency, read results to list for display\n", + "list(workflow(data))" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "['Le ciel est bleu', 'Forêt à travers les arbres']" + ] + }, + "metadata": {}, + "execution_count": 13 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "wicr0CAYRWZ0" + }, + "source": [ + "This isn't too different from previous pipeline examples. The only difference is data is feed through the workflow. In this example, the workflow calls the translation pipeline and translates text to French. Let's look at a more complex example." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0EeD8m6FR5cH" + }, + "source": [ + "# Multistep workflow\n", + "\n", + "The following workflow reads a series of audio files, transcribes them to text and translates the text to French. This is based on the classic txtai example from [Introducing txtai](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/01_Introducing_txtai.ipynb).\n", + "\n", + "Workflows take two main parameters. The action to execute which is a callable and a pattern to filter data with. Data that is accepted by the filter will be processed, otherwise it will be passed through to the next task." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "OF2G5-OiSBzy", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "e5c74089-1916-4bbd-93d3-9e25b1fe4ee5" + }, + "source": [ + "from txtai.workflow import FileTask\n", + "\n", + "tasks = [\n", + " FileTask(transcribe, r\"\\.wav$\"),\n", + " Task(lambda x: translate(x, \"fr\"))\n", + "]\n", + "\n", + "# List of files to process\n", + "data = [\n", + " \"txtai/US_tops_5_million.wav\",\n", + " \"txtai/Canadas_last_fully.wav\",\n", + " \"txtai/Beijing_mobilises.wav\",\n", + " \"txtai/The_National_Park.wav\",\n", + " \"txtai/Maine_man_wins_1_mil.wav\",\n", + " \"txtai/Make_huge_profits.wav\"\n", + "]\n", + "\n", + "# Workflow that translate text to French\n", + "workflow = Workflow(tasks)\n", + "\n", + "# Run workflow\n", + "list(workflow(data))" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[\"Les cas de virus U sont en tête d'un million\",\n", + " \"La dernière plate-forme de glace entièrement intacte du Canada s'est soudainement effondrée en formant un berge de glace de taille manhatten\",\n", + " \"Bagage mobilise les embarcations d'invasion le long des côtes à mesure que les tensions tiwaniennes s'intensifient\",\n", + " \"Le service des parcs nationaux met en garde contre le sacrifice d'amis plus lents dans une attaque nue\",\n", + " \"L'homme principal gagne du billet de loterie\",\n", + " \"Faire d'énormes profits sans travailler faire jusqu'à cent mille dollars par jour\"]" + ] + }, + "metadata": {}, + "execution_count": 14 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PN08rnrQU1hx" + }, + "source": [ + "# Complex workflow\n", + "\n", + "Let's put this all together into a full-fledged workflow to build an embeddings index. This workflow will work with both documents and audio files. Documents will have text extracted and summarized. Audio files will be transcribed. Both results will be joined, translated into French and loaded into an Embeddings index." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "coZJw_1yU1Sq", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "213b34d5-157f-4548-8788-ac29cb4039dd" + }, + "source": [ + "from txtai.embeddings import Embeddings, Documents\n", + "from txtai.workflow import FileTask, WorkflowTask\n", + "\n", + "# Embeddings index\n", + "embeddings = Embeddings({\"path\": \"sentence-transformers/paraphrase-multilingual-mpnet-base-v2\", \"content\": True})\n", + "documents = Documents()\n", + "\n", + "# List of files to process\n", + "files = [\n", + " \"txtai/article.pdf\",\n", + " \"txtai/US_tops_5_million.wav\",\n", + " \"txtai/Canadas_last_fully.wav\",\n", + " \"txtai/Beijing_mobilises.wav\",\n", + " \"txtai/The_National_Park.wav\",\n", + " \"txtai/Maine_man_wins_1_mil.wav\",\n", + " \"txtai/Make_huge_profits.wav\"\n", + "]\n", + "\n", + "data = [(x, element, None) for x, element in enumerate(files)]\n", + "\n", + "# Workflow that extracts text and builds a summary\n", + "articles = Workflow([\n", + " FileTask(textractor),\n", + " Task(summary)\n", + "])\n", + "\n", + "# Define workflow tasks. Workflows can also be tasks!\n", + "tasks = [\n", + " WorkflowTask(articles, r\".\\.pdf$\"),\n", + " FileTask(transcribe, r\"\\.wav$\"),\n", + " Task(lambda x: translate(x, \"fr\")),\n", + " Task(documents.add, unpack=False)\n", + "]\n", + "\n", + "# Workflow that translate text to French\n", + "workflow = Workflow(tasks)\n", + "\n", + "# Run workflow and show results to be indexed\n", + "for x in workflow(data):\n", + " print(x)\n", + "\n", + "# Build the embeddings index\n", + "embeddings.index(documents)\n", + "\n", + "# Cleanup temporary storage\n", + "documents.close()" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(0, \"Txtai, un moteur de recherche alimenté par l'IA construit sur Transformers, permet la recherche basée sur la compréhension du langage naturel (NLU) dans n'importe quelle application. Le champ de traitement du langage naturel (NLP) évolue rapidement avec un certain nombre de nouveaux développements. Le moteur de recherche open-source est open source et disponible sur GitHub.\", None)\n", + "(1, \"Les cas de virus U sont en tête d'un million\", None)\n", + "(2, \"La dernière plate-forme de glace entièrement intacte du Canada s'est soudainement effondrée en formant un berge de glace de taille manhatten\", None)\n", + "(3, \"Bagage mobilise les embarcations d'invasion le long des côtes à mesure que les tensions tiwaniennes s'intensifient\", None)\n", + "(4, \"Le service des parcs nationaux met en garde contre le sacrifice d'amis plus lents dans une attaque nue\", None)\n", + "(5, \"L'homme principal gagne du billet de loterie\", None)\n", + "(6, \"Faire d'énormes profits sans travailler faire jusqu'à cent mille dollars par jour\", None)\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "n6i-xhJya8o4" + }, + "source": [ + "# Query for results in French" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "cHbjivUOaUGu", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "0da8d8cb-dac6-4cad-ef00-a096b44533cf" + }, + "source": [ + "# Run a search query and show the result.\n", + "embeddings.search(\"changement climatique\", 1)[0]" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "{'id': '2',\n", + " 'score': 0.2982647716999054,\n", + " 'text': \"La dernière plate-forme de glace entièrement intacte du Canada s'est soudainement effondrée en formant un berge de glace de taille manhatten\"}" + ] + }, + "metadata": {}, + "execution_count": 16 + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "aNerHvNpaxD4", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "f3792220-4518-4388-c7e7-c38f38f19b20" + }, + "source": [ + "# Run a search query and show the result.\n", + "embeddings.search(\"traitement du langage naturel\", 1)[0]" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "{'id': '0',\n", + " 'score': 0.47031939029693604,\n", + " 'text': \"Txtai, un moteur de recherche alimenté par l'IA construit sur Transformers, permet la recherche basée sur la compréhension du langage naturel (NLU) dans n'importe quelle application. Le champ de traitement du langage naturel (NLP) évolue rapidement avec un certain nombre de nouveaux développements. Le moteur de recherche open-source est open source et disponible sur GitHub.\"}" + ] + }, + "metadata": {}, + "execution_count": 17 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Configuration-driven workflow\n", + "\n", + "Workflows can also be defined with YAML and run as an application. Applications can run standalone or as a FastAPI instance. More information can be [found here](https://neuml.github.io/txtai/api/). " + ], + "metadata": { + "id": "Sz_f9qoOMC_m" + } + }, + { + "cell_type": "code", + "source": [ + "workflow = \"\"\"\n", + "writable: true\n", + "embeddings:\n", + " path: sentence-transformers/paraphrase-multilingual-mpnet-base-v2\n", + " content: True\n", + "\n", + "# Summarize text\n", + "summary:\n", + "\n", + "# Extract text from documents\n", + "textractor:\n", + "\n", + "# Transcribe audio to text\n", + "transcription:\n", + " path: facebook/wav2vec2-large-960h\n", + "\n", + "# Translate text between languages\n", + "translation:\n", + "\n", + "workflow:\n", + " summarize:\n", + " tasks:\n", + " - action: textractor\n", + " task: file\n", + " - summary\n", + " index:\n", + " tasks:\n", + " - action: summarize\n", + " select: '\\\\.pdf$'\n", + " - action: transcription\n", + " select: '\\\\.wav$'\n", + " task: file\n", + " - action: translation\n", + " args: ['fr']\n", + " - action: index\n", + "\"\"\"\n", + "\n", + "# Create and run the workflow\n", + "from txtai.app import Application\n", + "\n", + "# Create and run the workflow\n", + "app = Application(workflow)\n", + "list(app.workflow(\"index\", files))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "HoVlk_vNJKHY", + "outputId": "34b68bcb-a6d5-4029-9bf2-f33e4381d1bc" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[\"Txtai, un moteur de recherche alimenté par l'IA construit sur Transformers, permet la recherche basée sur la compréhension du langage naturel (NLU) dans n'importe quelle application. Le champ de traitement du langage naturel (NLP) évolue rapidement avec un certain nombre de nouveaux développements. Le moteur de recherche open-source est open source et disponible sur GitHub.\",\n", + " \"Les cas de virus U sont en tête d'un million\",\n", + " \"La dernière plate-forme de glace entièrement intacte du Canada s'est soudainement effondrée en formant un berge de glace de taille manhatten\",\n", + " \"Bagage mobilise les embarcations d'invasion le long des côtes à mesure que les tensions tiwaniennes s'intensifient\",\n", + " \"Le service des parcs nationaux met en garde contre le sacrifice d'amis plus lents dans une attaque nue\",\n", + " \"L'homme principal gagne du billet de loterie\",\n", + " \"Faire d'énormes profits sans travailler faire jusqu'à cent mille dollars par jour\"]" + ] + }, + "metadata": {}, + "execution_count": 18 + } + ] + }, + { + "cell_type": "code", + "source": [ + "# Run a search query and show the result.\n", + "app.search(\"changement climatique\", 1)[0]" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "a_klVZAXHJcw", + "outputId": "33229268-0f98-4ca1-af7d-212bcbde6482" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "{'id': '2',\n", + " 'score': 0.2982647716999054,\n", + " 'text': \"La dernière plate-forme de glace entièrement intacte du Canada s'est soudainement effondrée en formant un berge de glace de taille manhatten\"}" + ] + }, + "metadata": {}, + "execution_count": 19 + } + ] + }, + { + "cell_type": "code", + "source": [ + "# Run a search query and show the result.\n", + "app.search(\"traitement du langage naturel\", 1)[0]" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "I5xin0VNHJOu", + "outputId": "2fbb9a93-b860-437d-c361-ee21eed75b6b" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "{'id': '0',\n", + " 'score': 0.47031939029693604,\n", + " 'text': \"Txtai, un moteur de recherche alimenté par l'IA construit sur Transformers, permet la recherche basée sur la compréhension du langage naturel (NLU) dans n'importe quelle application. Le champ de traitement du langage naturel (NLP) évolue rapidement avec un certain nombre de nouveaux développements. Le moteur de recherche open-source est open source et disponible sur GitHub.\"}" + ] + }, + "metadata": {}, + "execution_count": 20 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "7zG4AimucFJs" + }, + "source": [ + "# Wrapping up\n", + "\n", + "Results are good! We can see the power of workflows and how they can join a series of pipelines together in an efficient manner. Workflows can work with any callable, not just pipelines, workflows transform data from one format to another. Workflows are an exciting and promising development for txtai." + ] + } + ] +} \ No newline at end of file diff --git a/examples/15_Distributed_embeddings_cluster.ipynb b/examples/15_Distributed_embeddings_cluster.ipynb new file mode 100644 index 0000000..6a5f4aa --- /dev/null +++ b/examples/15_Distributed_embeddings_cluster.ipynb @@ -0,0 +1,522 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "4Pjmz-RORV8E" + }, + "source": [ + "# Distributed embeddings cluster\n", + "\n", + "The txtai API is a web-based service backed by [FastAPI](https://fastapi.tiangolo.com/). All txtai functionality is available via the API. The API can also cluster multiple embeddings indices into a single logical index to horizontally scale over multiple nodes. \n", + "\n", + "This notebook installs the txtai API and shows an example of building an embeddings cluster." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dk31rbYjSTYm" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies. Since this notebook uses the API, we need to install the api extras package." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "XMQuuun2R06J" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[api]" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PNPJ95cdTKSS" + }, + "source": [ + "# Start distributed embeddings cluster\n", + "\n", + "First we'll start multiple API instances that will serve as embeddings index shards. Each shard stores a subset of the indexed data and these shards work in tandem to form a single logical index.\n", + "\n", + "Then we'll start the main API instance that clusters the shards together into a logical instance.\n", + "\n", + "The API instances are all started in the background.\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "USb4JXZHxqTA" + }, + "source": [ + "import os\n", + "os.chdir(\"/content\")" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "nTDwXOUeTH2-", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "dee26849-39ae-4390-8bba-76bf9025fa61" + }, + "source": [ + "%%writefile index.yml\n", + "writable: true\n", + "\n", + "# Embeddings settings\n", + "embeddings:\n", + " path: sentence-transformers/nli-mpnet-base-v2\n", + " content: true" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Writing index.yml\n" + ] + } + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "iCdBh-JgfyBl", + "outputId": "0066e314-7461-47c7-ca3b-15204911783e" + }, + "source": [ + "%%writefile cluster.yml\n", + "# Embeddings cluster\n", + "cluster:\n", + " shards:\n", + " - http://127.0.0.1:8001\n", + " - http://127.0.0.1:8002" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Writing cluster.yml\n" + ] + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "nGITHxUyRzyp" + }, + "source": [ + "# Start embeddings shards\n", + "!CONFIG=index.yml nohup uvicorn --port 8001 \"txtai.api:app\" &> shard-1.log &\n", + "!CONFIG=index.yml nohup uvicorn --port 8002 \"txtai.api:app\" &> shard-2.log &\n", + "\n", + "# Start main instance\n", + "!CONFIG=cluster.yml nohup uvicorn --port 8000 \"txtai.api:app\" &> main.log &\n", + "\n", + "# Wait for startup\n", + "!sleep 90" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lxkbVng3giWP" + }, + "source": [ + "# Python\n", + "\n", + "Let's first try the cluster out directly in Python. The code below aggregates the two shards into a single cluster and executes actions against the cluster." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "36HGAokoglfg", + "outputId": "368ae013-2afc-4a1b-d7df-c429183637d7" + }, + "source": [ + "%%writefile run.py\n", + "from txtai.api import Cluster\n", + "\n", + "cluster = Cluster({\"shards\": [\"http://127.0.0.1:8001\", \"http://127.0.0.1:8002\"]})\n", + "\n", + "data = [\n", + " \"US tops 5 million confirmed virus cases\",\n", + " \"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\",\n", + " \"Beijing mobilises invasion craft along coast as Taiwan tensions escalate\",\n", + " \"The National Park Service warns against sacrificing slower friends in a bear attack\",\n", + " \"Maine man wins $1M from $25 lottery ticket\",\n", + " \"Make huge profits without work, earn up to $100,000 a day\",\n", + "]\n", + "\n", + "# Index data\n", + "cluster.add([{\"id\": x, \"text\": row} for x, row in enumerate(data)])\n", + "cluster.index()\n", + "\n", + "# Test search\n", + "result = cluster.search(\"feel good story\", 1)[0]\n", + "print(\"Query: feel good story\\nResult:\", result[\"text\"])" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Writing run.py\n" + ] + } + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "6dQOzcfEs2Pk", + "outputId": "a667594a-b778-4e4e-a75c-72e7982b7fbe" + }, + "source": [ + "!python run.py" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Query: feel good story\n", + "Result: Maine man wins $1M from $25 lottery ticket\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "NHvBFZeSd9AG" + }, + "source": [ + "# JavaScript\n", + "\n", + "Next let's try to run the same code above via the API using JavaScript.\n", + "\n", + "```bash\n", + "npm install txtai\n", + "```\n", + "\n", + "For this example, we'll clone the txtai.js project to import the example build configuration." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "b52knObEdcCr" + }, + "source": [ + "%%capture\n", + "!git clone https://github.com/neuml/txtai.js" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rUGS0t-JMsS9" + }, + "source": [ + "## Run cluster.js\n", + "\n", + "The following script is a JavaScript version of the logic above" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "bPQ40_xRyFmA", + "outputId": "b86a12c4-f2c7-427b-bd28-edba354c6713" + }, + "source": [ + "%%writefile txtai.js/examples/node/src/cluster.js\n", + "import {Embeddings} from \"txtai\";\n", + "import {sprintf} from \"sprintf-js\";\n", + "\n", + "const run = async () => {\n", + " try {\n", + " let embeddings = new Embeddings(process.argv[2]);\n", + "\n", + " let data = [\"US tops 5 million confirmed virus cases\",\n", + " \"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\",\n", + " \"Beijing mobilises invasion craft along coast as Taiwan tensions escalate\",\n", + " \"The National Park Service warns against sacrificing slower friends in a bear attack\",\n", + " \"Maine man wins $1M from $25 lottery ticket\",\n", + " \"Make huge profits without work, earn up to $100,000 a day\"];\n", + "\n", + " console.log();\n", + " console.log(\"Querying an Embeddings cluster\");\n", + " console.log(sprintf(\"%-20s %s\", \"Query\", \"Best Match\"));\n", + " console.log(\"-\".repeat(50));\n", + "\n", + " for (let query of [\"feel good story\", \"climate change\", \"public health story\", \"war\", \"wildlife\", \"asia\", \"lucky\", \"dishonest junk\"]) {\n", + " let results = await embeddings.search(query, 1);\n", + " if (results && results.length > 0) {\n", + " let result = results[0].text;\n", + " console.log(sprintf(\"%-20s %s\", query, result));\n", + " }\n", + " }\n", + " }\n", + " catch (e) {\n", + " console.trace(e);\n", + " }\n", + "};\n", + "\n", + "run();" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Writing txtai.js/examples/node/src/cluster.js\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nTBs11j-GtD-" + }, + "source": [ + "## Build and run cluster.js\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "kC5Oub6wa1nK" + }, + "source": [ + "%%capture\n", + "os.chdir(\"txtai.js/examples/node\")\n", + "!npm install\n", + "!npm run build" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Xr5IlvqH8W77" + }, + "source": [ + "Next lets run the code against the main cluster URL" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ckOHNqyaeL-B", + "outputId": "9c243fac-2316-4b8e-b044-6de529a8f3e8" + }, + "source": [ + "!node dist/cluster.js http://127.0.0.1:8000" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "Querying an Embeddings cluster\n", + "Query Best Match\n", + "--------------------------------------------------\n", + "feel good story Maine man wins $1M from $25 lottery ticket\n", + "climate change Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\n", + "public health story US tops 5 million confirmed virus cases\n", + "war Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "wildlife The National Park Service warns against sacrificing slower friends in a bear attack\n", + "asia Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "lucky Maine man wins $1M from $25 lottery ticket\n", + "dishonest junk Make huge profits without work, earn up to $100,000 a day\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1yukBIMYG5OE" + }, + "source": [ + "The JavaScript program is showing the same results as the Python code above. This is running a clustered query against both nodes in the cluster and aggregating the results together.\n", + "\n", + "Queries can be run against each individual shard to see what the queries independently return." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "73rZCo4O4IQR", + "outputId": "9f2cb119-7a21-41d9-fdbf-4410af246934" + }, + "source": [ + "!node dist/cluster.js http://127.0.0.1:8001" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "Querying an Embeddings cluster\n", + "Query Best Match\n", + "--------------------------------------------------\n", + "feel good story Maine man wins $1M from $25 lottery ticket\n", + "climate change Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "public health story US tops 5 million confirmed virus cases\n", + "war Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "wildlife Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "asia Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "lucky Maine man wins $1M from $25 lottery ticket\n" + ] + } + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ZeVBLJyr4Knr", + "outputId": "b75691a4-25bf-43dc-8878-f9792a4430b8" + }, + "source": [ + "!node dist/cluster.js http://127.0.0.1:8002" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "Querying an Embeddings cluster\n", + "Query Best Match\n", + "--------------------------------------------------\n", + "feel good story Make huge profits without work, earn up to $100,000 a day\n", + "climate change Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\n", + "public health story The National Park Service warns against sacrificing slower friends in a bear attack\n", + "war The National Park Service warns against sacrificing slower friends in a bear attack\n", + "wildlife The National Park Service warns against sacrificing slower friends in a bear attack\n", + "asia The National Park Service warns against sacrificing slower friends in a bear attack\n", + "lucky The National Park Service warns against sacrificing slower friends in a bear attack\n", + "dishonest junk Make huge profits without work, earn up to $100,000 a day\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "J2I_4hmZ8uXs" + }, + "source": [ + "Note the differences. The section below runs a count against the full cluster and each shard to show the count of records in each." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "BKm27yna4MWr", + "outputId": "bfc60af7-1b2b-451f-b10e-e2f8cf6f14fa" + }, + "source": [ + "!curl http://127.0.0.1:8000/count\n", + "!printf \"\\n\"\n", + "!curl http://127.0.0.1:8001/count\n", + "!printf \"\\n\"\n", + "!curl http://127.0.0.1:8002/count" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "6\n", + "3\n", + "3" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6rKj-I0djRQj" + }, + "source": [ + "This notebook showed how a distributed embeddings cluster can be created with txtai. This example can be further scaled out on Kubernetes with StatefulSets, which will be covered in a future tutorial." + ] + } + ] +} \ No newline at end of file diff --git a/examples/16_Train_a_text_labeler.ipynb b/examples/16_Train_a_text_labeler.ipynb new file mode 100644 index 0000000..ccdcc8b --- /dev/null +++ b/examples/16_Train_a_text_labeler.ipynb @@ -0,0 +1,335 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "4Pjmz-RORV8E" + }, + "source": [ + "# Train a text labeler\n", + "\n", + "The [Hugging Face Model Hub](https://huggingface.co/models) has a wide range of models that can handle many tasks. While these models perform well, the best performance often is found when fine-tuning a model with task-specific data. \n", + "\n", + "Hugging Face provides a [number of full-featured examples](https://github.com/huggingface/transformers/tree/master/examples) available to assist with training task-specific models. When building models from the command line, these scripts are a great way to get started.\n", + "\n", + "txtai provides a training pipeline that can be used to train new models programatically using the Transformers Trainer framework. The training pipeline supports the following:\n", + "\n", + "- Building transient models without requiring an output directory\n", + "- Load training data from Hugging Face datasets, pandas DataFrames and list of dicts\n", + "- Text sequence classification tasks (single/multi label classification and regression) including all GLUE tasks\n", + "- All training arguments\n", + "\n", + "This notebook shows examples of how to use txtai to train/fine-tune new models." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dk31rbYjSTYm" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "XMQuuun2R06J" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline-train] datasets pandas" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PNPJ95cdTKSS" + }, + "source": [ + "# Train a model\n", + "\n", + "Let's get right to it! The following example fine-tunes a tiny Bert model with the sst2 dataset.\n", + "\n", + "The trainer pipeline is basically a one-liner that fine-tunes any text classification/regression model available (locally and/or from the HF Hub). \n" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "USb4JXZHxqTA" + }, + "source": [ + "from datasets import load_dataset\n", + "\n", + "from txtai.pipeline import HFTrainer\n", + "\n", + "trainer = HFTrainer()\n", + "\n", + "# Hugging Face dataset\n", + "ds = load_dataset(\"glue\", \"sst2\")\n", + "model, tokenizer = trainer(\"google/bert_uncased_L-2_H-128_A-2\", ds[\"train\"], columns=(\"sentence\", \"label\"))" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "CubsNAbpEWQg" + }, + "source": [ + "The default trainer pipeline functionality will not store any logs, checkpoints or models to disk. The trainer can take any of the standard TrainingArguments to enable persistent models.\n", + "\n", + "The next section creates a Labels pipeline using the newly built model and runs the model against the sst2 validation set. " + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "xw2y2C5Mg11_", + "outputId": "78400e45-ea5c-4cd9-d205-b55ee7a9f005" + }, + "source": [ + "from txtai.pipeline import Labels\n", + "\n", + "labels = Labels((model, tokenizer), dynamic=False)\n", + "\n", + "# Determine accuracy on validation set\n", + "results = [row[\"label\"] == labels(row[\"sentence\"])[0][0] for row in ds[\"validation\"]]\n", + "sum(results) / len(ds[\"validation\"])" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "0.8268348623853211" + ] + }, + "metadata": {}, + "execution_count": 10 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZAHSwaB3Ex49" + }, + "source": [ + "82.68% accuracy - not bad for a tiny Bert model. \n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "f3GkY4JNEhhE" + }, + "source": [ + "# Train a model with Lists\n", + "\n", + "As mentioned earlier, the trainer pipeline supports Hugging Face datasets, pandas DataFrames and lists of dicts. The example below trains a model using lists." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "QkApw1b2hfZq", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 182 + }, + "outputId": "8c3dceae-49fb-4b63-837d-5944e63c768e" + }, + "source": [ + "data = [{\"text\": \"This is a test sentence\", \"label\": 0}, {\"text\": \"This is not a test\", \"label\": 1}]\n", + "\n", + "model, tokenizer = trainer(\"google/bert_uncased_L-2_H-128_A-2\", data)" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "Some weights of the model checkpoint at google/bert_uncased_L-2_H-128_A-2 were not used when initializing BertForSequenceClassification: ['cls.seq_relationship.bias', 'cls.predictions.transform.LayerNorm.bias', 'cls.predictions.decoder.weight', 'cls.seq_relationship.weight', 'cls.predictions.transform.dense.weight', 'cls.predictions.decoder.bias', 'cls.predictions.transform.LayerNorm.weight', 'cls.predictions.transform.dense.bias', 'cls.predictions.bias']\n", + "- This IS expected if you are initializing BertForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n", + "- This IS NOT expected if you are initializing BertForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n", + "Some weights of BertForSequenceClassification were not initialized from the model checkpoint at google/bert_uncased_L-2_H-128_A-2 and are newly initialized: ['classifier.weight', 'classifier.bias']\n", + "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + "
\n", + " \n", + " \n", + " [3/3 00:00, Epoch 3/3]\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
StepTraining Loss

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cjYTxm7sFKyZ" + }, + "source": [ + "# Train a model with DataFrames\n", + "\n", + "The next section builds a new model using data stored in a pandas DataFrame." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "0XaKKQ32wqbs", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 182 + }, + "outputId": "edb82a45-6c2a-4718-ce0b-56030f95ffbf" + }, + "source": [ + "import pandas as pd\n", + "\n", + "df = pd.DataFrame(data)\n", + "\n", + "model, tokenizer = trainer(\"google/bert_uncased_L-2_H-128_A-2\", data)" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "Some weights of the model checkpoint at google/bert_uncased_L-2_H-128_A-2 were not used when initializing BertForSequenceClassification: ['cls.seq_relationship.bias', 'cls.predictions.transform.LayerNorm.bias', 'cls.predictions.decoder.weight', 'cls.seq_relationship.weight', 'cls.predictions.transform.dense.weight', 'cls.predictions.decoder.bias', 'cls.predictions.transform.LayerNorm.weight', 'cls.predictions.transform.dense.bias', 'cls.predictions.bias']\n", + "- This IS expected if you are initializing BertForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n", + "- This IS NOT expected if you are initializing BertForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n", + "Some weights of BertForSequenceClassification were not initialized from the model checkpoint at google/bert_uncased_L-2_H-128_A-2 and are newly initialized: ['classifier.weight', 'classifier.bias']\n", + "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + "

\n", + " \n", + " \n", + " [3/3 00:00, Epoch 3/3]\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
StepTraining Loss

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "QH3D8PQSFvQO" + }, + "source": [ + "# Train a regression model\n", + "\n", + "The previous models were classification tasks. The following model trains a sentence similarity model with a regression output per sentence pair between 0 (dissimilar) and 1 (similar)." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "1rXuz4ncw9G-" + }, + "source": [ + "ds = load_dataset(\"glue\", \"stsb\")\n", + "model, tokenizer = trainer(\"google/bert_uncased_L-2_H-128_A-2\", ds[\"train\"], columns=(\"sentence1\", \"sentence2\", \"label\"))" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "fyvAslSP6j0F", + "outputId": "ec46a6aa-25a7-4777-e226-d53aeb37899b" + }, + "source": [ + "labels = Labels((model, tokenizer), dynamic=False)\n", + "labels([[(\"Sailing to the arctic\", \"Dogs and cats don't get along\")], \n", + " [(\"Walking down the road\", \"Walking down the street\")]])" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[[(0, 0.5648878216743469)], [(0, 0.97544926404953)]]" + ] + }, + "metadata": {}, + "execution_count": 14 + } + ] + } + ] +} diff --git a/examples/17_Train_without_labels.ipynb b/examples/17_Train_without_labels.ipynb new file mode 100644 index 0000000..f1318da --- /dev/null +++ b/examples/17_Train_without_labels.ipynb @@ -0,0 +1,297 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "4Pjmz-RORV8E" + }, + "source": [ + "# Train without labels\n", + "\n", + "Almost all data available is unlabeled. Labeled data takes effort to manually review and/or takes time to collect. Zero-shot classification takes existing large language models and runs a similarity comparison between candidate text and a list of labels. This has been shown to perform surprisingly well.\n", + "\n", + "The problem with zero-shot classifiers is that they need to have a large number of parameters (400M+) to perform well against general tasks, which comes with sizable hardware requirements.\n", + "\n", + "This notebook explores using zero-shot classifiers to build training data for smaller models. A simple form of [knowledge distillation](https://en.wikipedia.org/wiki/Knowledge_distillation). " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dk31rbYjSTYm" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "XMQuuun2R06J" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline-train] datasets pandas" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3PUe1OW8IZR5" + }, + "source": [ + "# Apply zero-shot classifier to unlabeled text\n", + "\n", + "The following section takes a small 1000 record random sample of the sst2 dataset and applies a zero-shot classifer to the text. The labels are ignored. This dataset was chosen only to be able to evaluate the accuracy at then end. " + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "GlrOnS4cmkih" + }, + "source": [ + "import random\n", + "\n", + "from datasets import load_dataset\n", + "\n", + "from txtai.pipeline import Labels\n", + "\n", + "def batch(texts, size):\n", + " return [texts[x : x + size] for x in range(0, len(texts), size)]\n", + "\n", + "# Set random seed for repeatable sampling\n", + "random.seed(42)\n", + "\n", + "ds = load_dataset(\"glue\", \"sst2\")\n", + "\n", + "sentences = random.sample(ds[\"train\"][\"sentence\"], 1000)\n", + "\n", + "# Load a zero shot classifier - txtai provides this through the Labels pipeline\n", + "labels = Labels(\"microsoft/deberta-large-mnli\")\n", + "\n", + "train = []\n", + "\n", + "# Zero-shot prediction using [\"negative\", \"positive\"] labels\n", + "for chunk in batch(sentences, 32):\n", + " train.extend([{\"text\": chunk[x], \"label\": label[0][0]} for x, label in enumerate(labels(chunk, [\"negative\", \"positive\"]))])" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TLsZmRpHJGav" + }, + "source": [ + "Next, we'll use the training set we just built to train a smaller Electra model." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 214 + }, + "id": "nAt42TIHnfTN", + "outputId": "7080b21d-ecf4-459a-c818-11c748e28bb7" + }, + "source": [ + "from txtai.pipeline import HFTrainer\n", + "\n", + "trainer = HFTrainer()\n", + "model, tokenizer = trainer(\"google/electra-base-discriminator\", train, num_train_epochs=5)" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "Some weights of the model checkpoint at google/electra-base-discriminator were not used when initializing ElectraForSequenceClassification: ['discriminator_predictions.dense.bias', 'discriminator_predictions.dense.weight', 'discriminator_predictions.dense_prediction.weight', 'discriminator_predictions.dense_prediction.bias']\n", + "- This IS expected if you are initializing ElectraForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n", + "- This IS NOT expected if you are initializing ElectraForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n", + "Some weights of ElectraForSequenceClassification were not initialized from the model checkpoint at google/electra-base-discriminator and are newly initialized: ['classifier.dense.bias', 'classifier.out_proj.weight', 'classifier.out_proj.bias', 'classifier.dense.weight']\n", + "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + "

\n", + " \n", + " \n", + " [625/625 02:51, Epoch 5/5]\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
StepTraining Loss
5000.282800

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "J9pugqJSJRn6" + }, + "source": [ + "# Evaluating accuracy\n", + "\n", + "Recall the training set is only 1000 records. To be clear, training an Electra model against the full sst2 dataset would perform better than below. But for this exercise, we're are not using the training labels and simulating labeled data not being available.\n", + "\n", + "First, lets see what the baseline accuracy for the zero-shot model would be against the sst2 evaluation set. Reminder that this has not seen any of the sst2 training data. \n" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "RbgIrkgMvJS4", + "outputId": "69287790-e01c-4c17-dfd5-0dc6afd73c98" + }, + "source": [ + "labels = Labels(\"microsoft/deberta-large-mnli\")" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "Some weights of the model checkpoint at microsoft/deberta-large-mnli were not used when initializing DebertaForSequenceClassification: ['config']\n", + "- This IS expected if you are initializing DebertaForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n", + "- This IS NOT expected if you are initializing DebertaForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n" + ] + } + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-36UBMILpKYh", + "outputId": "3a340b9f-57c5-4c4c-d975-0fcc47df4930" + }, + "source": [ + "results = [row[\"label\"] == labels(row[\"sentence\"], [\"negative\", \"positive\"])[0][0] for row in ds[\"validation\"]]\n", + "sum(results) / len(ds[\"validation\"])" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "0.8818807339449541" + ] + }, + "metadata": {}, + "execution_count": 21 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "uJVnWHZZKFIN" + }, + "source": [ + "88.19% accuracy, not bad for a model that has not been trained on the dataset at all! Shows the power of zero-shot classification.\n", + "\n", + "Next, let's test our model trained on the 1000 zero-shot labeled records." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Kr5IZqZtvXlP", + "outputId": "1faeb0d6-349b-4982-e9e8-cdbbde9e9a09" + }, + "source": [ + "labels = Labels((model, tokenizer), dynamic=False)\n", + "\n", + "results = [row[\"label\"] == labels(row[\"sentence\"])[0][0] for row in ds[\"validation\"]]\n", + "sum(results) / len(ds[\"validation\"])" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "0.8738532110091743" + ] + }, + "metadata": {}, + "execution_count": 22 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "sDw-Zh43KVdX" + }, + "source": [ + "87.39% accuracy! Wouldn't get too carried away with the percentages but this at least nearly meets the accuracy of the zero-shot classifier.\n", + "\n", + "Now this model will be highly tuned for a specific task but it had the opportunity to learn from the combined 1000 records whereas the zero-shot classifier views each record independently. It's also much more performant. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "QEAwki2lLM2A" + }, + "source": [ + "# Conclusion\n", + "\n", + "This notebook explored a method of building trained text classifiers without training data being available. Given the amount of resources needed to run large-scale zero-shot classifiers, this method is a simple way to build smaller models tuned for specific tasks. In this example, the zero-shot classifier has 400M parameters and the trained text classifier has 110M. " + ] + } + ] +} diff --git a/examples/18_Export_and_run_models_with_ONNX.ipynb b/examples/18_Export_and_run_models_with_ONNX.ipynb new file mode 100644 index 0000000..6214d1d --- /dev/null +++ b/examples/18_Export_and_run_models_with_ONNX.ipynb @@ -0,0 +1,1352 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "collapsed_sections": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "4Pjmz-RORV8E" + }, + "source": [ + "# Export and run models with ONNX\n", + "\n", + "The [ONNX runtime](https://onnx.ai/) provides a common serialization format for machine learning models. ONNX supports a number of [different platforms/languages](https://onnxruntime.ai/docs/how-to/install.html#requirements) and has features built in to help reduce inference time. \n", + "\n", + "PyTorch has robust support for exporting Torch models to ONNX. This enables exporting Hugging Face Transformer and/or other downstream models directly to ONNX. \n", + "\n", + "ONNX opens an avenue for direct inference using a number of languages and platforms. For example, a model could be run directly on Android to limit data sent to a third party service. ONNX is an exciting development with a lot of promise. Microsoft has also released [Hummingbird](https://github.com/microsoft/hummingbird) which enables exporting traditional models (sklearn, decision trees, logistical regression..) to ONNX. \n", + "\n", + "This notebook will cover how to export models to ONNX using txtai. These models will then be directly run in Python, JavaScript, Java and Rust. Currently, txtai supports all these languages through it's API and that is still the recommended approach. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dk31rbYjSTYm" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies. Since this notebook uses ONNX quantization, we need to install the pipeline extras package." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "XMQuuun2R06J" + }, + "source": [ + "%%capture\n", + "!pip install datasets git+https://github.com/neuml/txtai#egg=txtai[pipeline]" + ], + "execution_count": 25, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PNPJ95cdTKSS" + }, + "source": [ + "# Run a model with ONNX\n", + "\n", + "Let's get right to it! The following example exports a sentiment analysis model to ONNX and runs an inference session.\n", + "\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "USb4JXZHxqTA", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "28d3e70e-efa9-4b07-a602-6ffd89d1279f" + }, + "source": [ + "import numpy as np\n", + "\n", + "from onnxruntime import InferenceSession, SessionOptions\n", + "from transformers import AutoTokenizer\n", + "from txtai.pipeline import HFOnnx\n", + "\n", + "# Normalize logits using sigmoid function\n", + "sigmoid = lambda x: 1.0 / (1.0 + np.exp(-x))\n", + "\n", + "# Export to ONNX\n", + "onnx = HFOnnx()\n", + "model = onnx(\"distilbert-base-uncased-finetuned-sst-2-english\", \"text-classification\")\n", + "\n", + "# Start inference session\n", + "options = SessionOptions()\n", + "session = InferenceSession(model, options)\n", + "\n", + "# Tokenize\n", + "tokenizer = AutoTokenizer.from_pretrained(\"distilbert-base-uncased-finetuned-sst-2-english\")\n", + "tokens = tokenizer([\"I am happy\", \"I am mad\"], return_tensors=\"np\")\n", + "\n", + "# Print results\n", + "outputs = session.run(None, dict(tokens))\n", + "print(sigmoid(outputs[0]))" + ], + "execution_count": 26, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[[0.01295124 0.9909526 ]\n", + " [0.9874723 0.0297817 ]]\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jkmQoQvlmHfQ" + }, + "source": [ + "And just like that, there are results! The text classification model is judging sentiment using two labels, 0 for negative to 1 for positive. The results above shows the probability of each label per text snippet.\n", + "\n", + "The ONNX pipeline loads the model, converts the graph to ONNX and returns. Note that no output file was provided, in this case the ONNX model is returned as a byte array. If an output file is provided, this method returns the output path." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yFAOHVmXml8o" + }, + "source": [ + "# Train and Export a model for Text Classification\n", + "\n", + "Next we'll combine the ONNX pipeline with a Trainer pipeline to create a \"train and export to ONNX\" workflow." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 579 + }, + "id": "Wh8TkszumlIe", + "outputId": "864f2074-ae50-40d6-bc34-2b1d86a71488" + }, + "source": [ + "from datasets import load_dataset\n", + "from txtai.pipeline import HFTrainer\n", + "\n", + "trainer = HFTrainer()\n", + "\n", + "# Hugging Face dataset\n", + "ds = load_dataset(\"glue\", \"sst2\")\n", + "data = ds[\"train\"].select(range(10000)).flatten_indices()\n", + "\n", + "# Train new model using 10,000 SST2 records (in-memory)\n", + "model, tokenizer = trainer(\"google/electra-base-discriminator\", data, columns=(\"sentence\", \"label\"))\n", + "\n", + "# Export model trained in-memory to ONNX (still in-memory)\n", + "output = onnx((model, tokenizer), \"text-classification\", quantize=True)\n", + "\n", + "# Start inference session\n", + "options = SessionOptions()\n", + "session = InferenceSession(output, options)\n", + "\n", + "# Tokenize\n", + "tokens = tokenizer([\"I am happy\", \"I am mad\"], return_tensors=\"np\")\n", + "\n", + "# Print results\n", + "outputs = session.run(None, dict(tokens))\n", + "print(sigmoid(outputs[0]))" + ], + "execution_count": 27, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "WARNING:datasets.arrow_dataset:Loading cached processed dataset at /root/.cache/huggingface/datasets/glue/sst2/1.0.0/dacbe3125aa31d7f70367a07a8a9e72a5a0bfeb5fc42e75c9db75b96da6053ad/cache-e28d0e20a676bad0.arrow\n", + "WARNING:datasets.arrow_dataset:Loading cached processed dataset at /root/.cache/huggingface/datasets/glue/sst2/1.0.0/dacbe3125aa31d7f70367a07a8a9e72a5a0bfeb5fc42e75c9db75b96da6053ad/cache-d7b5d80ca22204f9.arrow\n", + "Some weights of the model checkpoint at google/electra-base-discriminator were not used when initializing ElectraForSequenceClassification: ['discriminator_predictions.dense_prediction.weight', 'discriminator_predictions.dense_prediction.bias', 'discriminator_predictions.dense.weight', 'discriminator_predictions.dense.bias']\n", + "- This IS expected if you are initializing ElectraForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n", + "- This IS NOT expected if you are initializing ElectraForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n", + "Some weights of ElectraForSequenceClassification were not initialized from the model checkpoint at google/electra-base-discriminator and are newly initialized: ['classifier.out_proj.bias', 'classifier.dense.weight', 'classifier.out_proj.weight', 'classifier.dense.bias']\n", + "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n", + "/usr/local/lib/python3.7/dist-packages/transformers/optimization.py:310: FutureWarning: This implementation of AdamW is deprecated and will be removed in a future version. Use the PyTorch implementation torch.optim.AdamW instead, or set `no_deprecation_warning=True` to disable this warning\n", + " FutureWarning,\n", + "You're using a ElectraTokenizerFast tokenizer. Please note that with a fast tokenizer, using the `__call__` method is faster than using a method to encode the text followed by a call to the `pad` method to get a padded encoding.\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + "

\n", + " \n", + " \n", + " [3750/3750 07:56, Epoch 3/3]\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
StepTraining Loss
5000.396800
10000.330900
15000.232400
20000.188200
25000.173600
30000.068600
35000.069800

" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[[0.01525715 0.975399 ]\n", + " [0.97395283 0.04432926]]\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lE7dPj3tsn5S" + }, + "source": [ + "The results are similar to the previous step, although this model is only trained on a fraction of the sst2 dataset. Lets save this model for later." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "Q_kAFYd_s_Bi" + }, + "source": [ + "onnx = HFOnnx()\n", + "text = onnx((model, tokenizer), \"text-classification\", \"text-classify.onnx\", quantize=True)" + ], + "execution_count": 29, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ugNZO4c-uAS-" + }, + "source": [ + "# Export a Sentence Embeddings model\n", + "\n", + "The ONNX pipeline also supports exporting sentence embeddings models trained with the [sentence-transformers](https://github.com/UKPLab/sentence-transformers) package. " + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "x9B7qOk_uQRN" + }, + "source": [ + "embeddings = onnx(\"sentence-transformers/paraphrase-MiniLM-L6-v2\", \"pooling\", \"embeddings.onnx\", quantize=True)" + ], + "execution_count": 30, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rirMSM2kvgJF" + }, + "source": [ + "Now let's run the model with ONNX." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "6MBraENcu8Oz", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "ba4528d2-6d6a-4181-e9c4-3d2a98d6663a" + }, + "source": [ + "from sklearn.metrics.pairwise import cosine_similarity\n", + "\n", + "options = SessionOptions()\n", + "session = InferenceSession(embeddings, options)\n", + "\n", + "tokens = tokenizer([\"I am happy\", \"I am glad\"], return_tensors=\"np\")\n", + "\n", + "outputs = session.run(None, dict(tokens))[0]\n", + "\n", + "print(cosine_similarity(outputs))" + ], + "execution_count": 31, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[[0.99999994 0.8474637 ]\n", + " [0.8474637 0.9999997 ]]\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pwgU4vu8vk0T" + }, + "source": [ + "The code above tokenizes two separate text snippets (\"I am happy\" and \"I am glad\") and runs it through the ONNX model. \n", + "\n", + "This outputs two embeddings arrays and those arrays are compared using cosine similarity. As we can see, the two text snippets have close semantic meaning." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "t_OQaQeIb7UB" + }, + "source": [ + "# Load an ONNX model with txtai\n", + "\n", + "txtai has built-in support for ONNX models. Loading an ONNX model is seamless and Embeddings and Pipelines support it. The following section shows how to load a classification pipeline and embeddings model backed by ONNX." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "vhsFzCRBby-h", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "745d69b0-035b-4e44-a881-57f9ece171ab" + }, + "source": [ + "from txtai.embeddings import Embeddings\n", + "from txtai.pipeline import Labels\n", + "\n", + "labels = Labels((\"text-classify.onnx\", \"google/electra-base-discriminator\"), dynamic=False)\n", + "print(labels([\"I am happy\", \"I am mad\"]))\n", + "\n", + "embeddings = Embeddings({\"path\": \"embeddings.onnx\", \"tokenizer\": \"sentence-transformers/paraphrase-MiniLM-L6-v2\"})\n", + "print(embeddings.similarity(\"I am happy\", [\"I am glad\"]))" + ], + "execution_count": 32, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[[(1, 0.999687910079956), (0, 0.0003121310146525502)], [(0, 0.9991233944892883), (1, 0.0008765518432483077)]]\n", + "[(0, 0.8298245072364807)]\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Xx8G29hkwdNY" + }, + "source": [ + "# JavaScript\n", + "\n", + "So far, we've exported models to ONNX and run them through Python. This already has a lot of advantages, which include fast inference times, quantization and less software dependencies. But ONNX really shines when we run a model trained in Python in other languages/platforms.\n", + "\n", + "Let's try running the models trained above in JavaScript. First step is getting the Node.js environment and dependencies setup.\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "_RK79O9c4Z_y" + }, + "source": [ + "%%capture\n", + "import os\n", + "\n", + "os.chdir(\"/content\")\n", + "!mkdir js\n", + "os.chdir(\"/content/js\")\n", + "\n", + "# Copy ONNX models\n", + "!cp ../text-classify.onnx .\n", + "!cp ../embeddings.onnx .\n", + "\n", + "# Get tokenizers project\n", + "!git clone https://github.com/huggingface/tokenizers.git\n", + "\n", + "os.chdir(\"/content/js/tokenizers/bindings/node\")\n", + "\n", + "# Install Rust to compile tokenizer bindings\n", + "!apt-get install rustc cargo\n", + "\n", + "# Build tokenizers package locally as binary version on npm doesn't work for latest version of Node.js\n", + "!npm install --also=dev\n", + "!npm run dev\n", + "\n", + "os.chdir(\"/content/js\")" + ], + "execution_count": 33, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "0HtVEl74xrZ7", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "7ffea868-6dd7-4603-e04c-ce8d4c557ff6" + }, + "source": [ + "%%writefile package.json\n", + "{\n", + " \"name\": \"onnx-test\",\n", + " \"private\": true,\n", + " \"version\": \"1.0.0\",\n", + " \"description\": \"ONNX Runtime Node.js test\",\n", + " \"main\": \"index.js\",\n", + " \"dependencies\": {\n", + " \"onnxruntime-node\": \">=1.12.1\",\n", + " \"tokenizers\": \"file:tokenizers/bindings/node\"\n", + " }\n", + "}" + ], + "execution_count": 34, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Writing package.json\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "\n", + "# Install all dependencies\n", + "!npm install" + ], + "metadata": { + "id": "4naPtk-iBI-g" + }, + "execution_count": 35, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "At85iA8U63iV" + }, + "source": [ + "Next we'll write the inference code in JavaScript to an index.js file." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "RImohEnFyFg0", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "094ef937-0650-4d5b-f4e3-79f114bd9807", + "cellView": "form" + }, + "source": [ + "#@title\n", + "%%writefile index.js\n", + "const ort = require('onnxruntime-node');\n", + "const { promisify } = require('util');\n", + "const { Tokenizer } = require(\"tokenizers/dist/bindings/tokenizer\");\n", + "\n", + "function sigmoid(data) {\n", + " return data.map(x => 1 / (1 + Math.exp(-x)))\n", + "}\n", + "\n", + "function softmax(data) { \n", + " return data.map(x => Math.exp(x) / (data.map(y => Math.exp(y))).reduce((a,b) => a+b)) \n", + "}\n", + "\n", + "function similarity(v1, v2) {\n", + " let dot = 0.0;\n", + " let norm1 = 0.0;\n", + " let norm2 = 0.0;\n", + "\n", + " for (let x = 0; x < v1.length; x++) {\n", + " dot += v1[x] * v2[x];\n", + " norm1 += Math.pow(v1[x], 2);\n", + " norm2 += Math.pow(v2[x], 2);\n", + " }\n", + "\n", + " return dot / (Math.sqrt(norm1) * Math.sqrt(norm2));\n", + "}\n", + "\n", + "function tokenizer() {\n", + " let tokenizer = Tokenizer.fromPretrained(\"bert-base-uncased\");\n", + " return promisify(tokenizer.encode.bind(tokenizer));\n", + "}\n", + "\n", + "async function predict(session, text) {\n", + " try {\n", + " // Tokenize input\n", + " let encode = tokenizer();\n", + " let output = await encode(text);\n", + "\n", + " let ids = output.getIds().map(x => BigInt(x))\n", + " let mask = output.getAttentionMask().map(x => BigInt(x))\n", + " let tids = output.getTypeIds().map(x => BigInt(x))\n", + "\n", + " // Convert inputs to tensors \n", + " let tensorIds = new ort.Tensor('int64', BigInt64Array.from(ids), [1, ids.length]);\n", + " let tensorMask = new ort.Tensor('int64', BigInt64Array.from(mask), [1, mask.length]);\n", + " let tensorTids = new ort.Tensor('int64', BigInt64Array.from(tids), [1, tids.length]);\n", + "\n", + " let inputs = null;\n", + " if (session.inputNames.length > 2) {\n", + " inputs = { input_ids: tensorIds, attention_mask: tensorMask, token_type_ids: tensorTids};\n", + " }\n", + " else {\n", + " inputs = { input_ids: tensorIds, attention_mask: tensorMask};\n", + " }\n", + "\n", + " return await session.run(inputs);\n", + " } catch (e) {\n", + " console.error(`failed to inference ONNX model: ${e}.`);\n", + " }\n", + "}\n", + "\n", + "async function main() {\n", + " let args = process.argv.slice(2);\n", + " if (args.length > 1) {\n", + " // Run sentence embeddings\n", + " const session = await ort.InferenceSession.create('./embeddings.onnx');\n", + "\n", + " let v1 = await predict(session, args[0]);\n", + " let v2 = await predict(session, args[1]);\n", + "\n", + " // Unpack results\n", + " v1 = v1.embeddings.data;\n", + " v2 = v2.embeddings.data;\n", + "\n", + " // Print similarity\n", + " console.log(similarity(Array.from(v1), Array.from(v2)));\n", + " }\n", + " else {\n", + " // Run text classifier\n", + " const session = await ort.InferenceSession.create('./text-classify.onnx');\n", + " let results = await predict(session, args[0]);\n", + "\n", + " // Normalize results using softmax and print\n", + " console.log(softmax(results.logits.data));\n", + " }\n", + "}\n", + "\n", + "main();" + ], + "execution_count": 36, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Writing index.js\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rZI9PJzi6_bO" + }, + "source": [ + "## Run Text Classification in JavaScript with ONNX" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "bdz68KZT1Jfm", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "48c4a427-3108-436c-eac9-564835ea061c" + }, + "source": [ + "!node . \"I am happy\"\n", + "!node . \"I am mad\"" + ], + "execution_count": 37, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Float32Array(2) [ \u001b[33m0.0003121308400295675\u001b[39m, \u001b[33m0.9996878504753113\u001b[39m ]\n", + "Float32Array(2) [ \u001b[33m0.9991234540939331\u001b[39m, \u001b[33m0.0008765519596636295\u001b[39m ]\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "swSEmqto33VP" + }, + "source": [ + "First off, have to say this is 🔥🔥🔥! Just amazing that this model can be fully run in JavaScript. It's a great time to be in NLP!\n", + "\n", + "The steps above installed a JavaScript environment with dependencies to run ONNX and tokenize data in JavaScript. The text classification model previously created is loaded into the JavaScript ONNX runtime and inference is run.\n", + "\n", + "As a reminder, the text classification model is judging sentiment using two labels, 0 for negative to 1 for positive. The results above shows the probability of each label per text snippet." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5Az9YaDc6u9P" + }, + "source": [ + "## Build sentence embeddings and compare similarity in JavaScript with ONNX" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "10jcUbUx6MAI", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "ac751cee-5f44-4dad-c164-5d124be75ec3" + }, + "source": [ + "!node . \"I am happy\", \"I am glad\"" + ], + "execution_count": 38, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\u001b[33m0.8285076844387538\u001b[39m\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8Jyk-9Ko78Ma" + }, + "source": [ + "Once again....wow!! The sentence embeddings model produces vectors that can be used to compare semantic similarity, -1 being most dissimilar and 1 being most similar.\n", + "\n", + "While the results don't match the exported model exactly, it's very close. Worth mentioning again that this is 100% JavaScript, no API or remote calls, all within node." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BQeMBNWO9Hpr" + }, + "source": [ + "# Java\n", + "\n", + "Let's try the same thing with Java. The following sections initialize a Java build environment and writes out the code necessary to run the ONNX inference." + ] + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "import os\n", + "\n", + "os.chdir(\"/content\")\n", + "!mkdir java\n", + "os.chdir(\"/content/java\")\n", + "\n", + "# Copy ONNX models\n", + "!cp ../text-classify.onnx .\n", + "!cp ../embeddings.onnx .\n", + "\n", + "# Save copy of Bert Tokenizer\n", + "tokenizer.save_pretrained(\"bert\")\n", + "\n", + "!mkdir -p src/main/java\n", + "\n", + "# Install gradle\n", + "!wget https://services.gradle.org/distributions/gradle-7.5.1-bin.zip\n", + "!unzip -o gradle-7.5.1-bin.zip" + ], + "metadata": { + "id": "L1YMoO7WwkEk" + }, + "execution_count": 39, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "gjZ2p7Jf9mOV", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "0cc3f4ef-bac6-4c13-dc6b-8482353bb741" + }, + "source": [ + "%%writefile build.gradle\n", + "apply plugin: \"java\"\n", + "\n", + "repositories {\n", + " mavenCentral()\n", + "}\n", + "\n", + "dependencies {\n", + " implementation \"com.robrua.nlp:easy-bert:1.0.3\"\n", + " implementation \"com.microsoft.onnxruntime:onnxruntime:1.12.1\"\n", + "}\n", + "\n", + "java {\n", + " toolchain {\n", + " languageVersion = JavaLanguageVersion.of(8)\n", + " }\n", + "}\n", + "\n", + "jar {\n", + " archiveBaseName = \"onnxjava\"\n", + "}\n", + "\n", + "task onnx(type: JavaExec) {\n", + " description = \"Runs ONNX demo\"\n", + " classpath = sourceSets.main.runtimeClasspath\n", + " main = \"OnnxDemo\"\n", + "}" + ], + "execution_count": 40, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Writing build.gradle\n" + ] + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "9wlVWVky9NZ3" + }, + "source": [ + "%%capture\n", + "\n", + "# Create environment\n", + "!gradle-7.5.1/bin/gradle wrapper" + ], + "execution_count": 41, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "vnxKGSuz_fnj", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "c1b73bf8-c5f4-46fa-df90-29bd60507287", + "cellView": "form" + }, + "source": [ + "#@title\n", + "%%writefile src/main/java/OnnxDemo.java\n", + "import java.io.File;\n", + "\n", + "import java.nio.LongBuffer;\n", + "\n", + "import java.util.Arrays;\n", + "import java.util.ArrayList;\n", + "import java.util.HashMap;\n", + "import java.util.List;\n", + "import java.util.Map;\n", + "\n", + "import ai.onnxruntime.OnnxTensor;\n", + "import ai.onnxruntime.OrtEnvironment;\n", + "import ai.onnxruntime.OrtSession;\n", + "import ai.onnxruntime.OrtSession.Result;\n", + "\n", + "import com.robrua.nlp.bert.FullTokenizer;\n", + "\n", + "class Tokens {\n", + " public long[] ids;\n", + " public long[] mask;\n", + " public long[] types;\n", + "}\n", + "\n", + "class Tokenizer {\n", + " private FullTokenizer tokenizer;\n", + "\n", + " public Tokenizer(String path) {\n", + " File vocab = new File(path);\n", + " this.tokenizer = new FullTokenizer(vocab, true);\n", + " }\n", + "\n", + " public Tokens tokenize(String text) {\n", + " // Build list of tokens\n", + " List tokensList = new ArrayList();\n", + " tokensList.add(\"[CLS]\"); \n", + " tokensList.addAll(Arrays.asList(tokenizer.tokenize(text)));\n", + " tokensList.add(\"[SEP]\");\n", + "\n", + " int[] ids = tokenizer.convert(tokensList.toArray(new String[0]));\n", + "\n", + " Tokens tokens = new Tokens();\n", + "\n", + " // input ids \n", + " tokens.ids = Arrays.stream(ids).mapToLong(i -> i).toArray();\n", + "\n", + " // attention mask\n", + " tokens.mask = new long[ids.length];\n", + " Arrays.fill(tokens.mask, 1);\n", + "\n", + " // token type ids\n", + " tokens.types = new long[ids.length];\n", + " Arrays.fill(tokens.types, 0);\n", + "\n", + " return tokens;\n", + " }\n", + "}\n", + "\n", + "class Inference {\n", + " private Tokenizer tokenizer;\n", + " private OrtEnvironment env;\n", + " private OrtSession session;\n", + "\n", + " public Inference(String model) throws Exception {\n", + " this.tokenizer = new Tokenizer(\"bert/vocab.txt\");\n", + " this.env = OrtEnvironment.getEnvironment();\n", + " this.session = env.createSession(model, new OrtSession.SessionOptions());\n", + " }\n", + "\n", + " public float[][] predict(String text) throws Exception {\n", + " Tokens tokens = this.tokenizer.tokenize(text);\n", + "\n", + " Map inputs = new HashMap();\n", + " inputs.put(\"input_ids\", OnnxTensor.createTensor(env, LongBuffer.wrap(tokens.ids), new long[]{1, tokens.ids.length}));\n", + " inputs.put(\"attention_mask\", OnnxTensor.createTensor(env, LongBuffer.wrap(tokens.mask), new long[]{1, tokens.mask.length}));\n", + " inputs.put(\"token_type_ids\", OnnxTensor.createTensor(env, LongBuffer.wrap(tokens.types), new long[]{1, tokens.types.length}));\n", + "\n", + " return (float[][])session.run(inputs).get(0).getValue();\n", + " }\n", + "}\n", + "\n", + "class Vectors {\n", + " public static double similarity(float[] v1, float[] v2) {\n", + " double dot = 0.0;\n", + " double norm1 = 0.0;\n", + " double norm2 = 0.0;\n", + "\n", + " for (int x = 0; x < v1.length; x++) {\n", + " dot += v1[x] * v2[x];\n", + " norm1 += Math.pow(v1[x], 2);\n", + " norm2 += Math.pow(v2[x], 2);\n", + " }\n", + "\n", + " return dot / (Math.sqrt(norm1) * Math.sqrt(norm2));\n", + " }\n", + "\n", + " public static float[] softmax(float[] input) {\n", + " double[] t = new double[input.length];\n", + " double sum = 0.0;\n", + "\n", + " for (int x = 0; x < input.length; x++) {\n", + " double val = Math.exp(input[x]);\n", + " sum += val;\n", + " t[x] = val;\n", + " }\n", + "\n", + " float[] output = new float[input.length];\n", + " for (int x = 0; x < output.length; x++) {\n", + " output[x] = (float) (t[x] / sum);\n", + " }\n", + "\n", + " return output;\n", + " }\n", + "}\n", + "\n", + "public class OnnxDemo {\n", + " public static void main(String[] args) {\n", + " try {\n", + " if (args.length < 2) {\n", + " Inference inference = new Inference(\"text-classify.onnx\");\n", + "\n", + " float[][] v1 = inference.predict(args[0]);\n", + "\n", + " System.out.println(Arrays.toString(Vectors.softmax(v1[0])));\n", + " }\n", + " else {\n", + " Inference inference = new Inference(\"embeddings.onnx\");\n", + " float[][] v1 = inference.predict(args[0]);\n", + " float[][] v2 = inference.predict(args[1]);\n", + "\n", + " System.out.println(Vectors.similarity(v1[0], v2[0]));\n", + " }\n", + " }\n", + " catch (Exception ex) {\n", + " ex.printStackTrace();\n", + " }\n", + " }\n", + "}" + ], + "execution_count": 42, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Writing src/main/java/OnnxDemo.java\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qQuuXw97Z_I7" + }, + "source": [ + "## Run Text Classification in Java with ONNX" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "hFXyH96gAZpu", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "efd5b783-b23a-407c-8577-c18e3a6cb984" + }, + "source": [ + "!./gradlew -q --console=plain onnx --args='\"I am happy\"' 2> /dev/null\n", + "!./gradlew -q --console=plain onnx --args='\"I am mad\"' 2> /dev/null" + ], + "execution_count": 43, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[3.1213084E-4, 0.99968785]\n", + "\u001b[m[0.99912345, 8.7655196E-4]\n", + "\u001b[m" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pE3FSsAAaJHe" + }, + "source": [ + "The command above tokenizes the input and runs inference with a text classification model previously created using a Java ONNX inference session. \n", + "\n", + "As a reminder, the text classification model is judging sentiment using two labels, 0 for negative to 1 for positive. The results above shows the probability of each label per text snippet." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Bux8v0C4aDyP" + }, + "source": [ + "## Build sentence embeddings and compare similarity in Java with ONNX" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "f6zE9VrwCcUa", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "988e59d0-943f-45b6-d37e-5fc1ebbbcefe" + }, + "source": [ + "!./gradlew -q --console=plain onnx --args='\"I am happy\" \"I am glad\"' 2> /dev/null" + ], + "execution_count": 44, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "0.8298244656285757\n", + "\u001b[m" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0uepOZvJDOCB" + }, + "source": [ + "The sentence embeddings model produces vectors that can be used to compare semantic similarity, -1 being most dissimilar and 1 being most similar. \n", + "\n", + "This is 100% Java, no API or remote calls, all within the JVM. Still think it's amazing!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "faRu9EAJDUXw" + }, + "source": [ + "# Rust\n", + "\n", + "Last but not least, let's try Rust. The following sections initialize a Rust build environment and writes out the code necessary to run the ONNX inference." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "X3Xp1KLhelqw" + }, + "source": [ + "%%capture\n", + "import os\n", + "\n", + "os.chdir(\"/content\")\n", + "!mkdir rust\n", + "os.chdir(\"/content/rust\")\n", + "\n", + "# Copy ONNX models\n", + "!cp ../text-classify.onnx .\n", + "!cp ../embeddings.onnx .\n", + "\n", + "# Install Rust\n", + "!apt-get install rustc cargo\n", + "\n", + "!mkdir -p src" + ], + "execution_count": 45, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "c7hz--Gne6Oa", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "d98ad709-5675-4193-e598-e6cbe12edda3" + }, + "source": [ + "%%writefile Cargo.toml\n", + "[package]\n", + "name = \"onnx-test\"\n", + "version = \"1.0.0\"\n", + "description = \"\"\"\n", + "ONNX Runtime Rust test\n", + "\"\"\"\n", + "edition = \"2018\"\n", + "\n", + "[dependencies]\n", + "onnxruntime = { version = \"0.0.14\"}\n", + "tokenizers = { version = \"0.13.1\"}" + ], + "execution_count": 46, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Writing Cargo.toml\n" + ] + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "_8fdRvO1fFBm", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "53168684-12c2-46fc-e0e6-54c4c6d03cb1", + "cellView": "form" + }, + "source": [ + "#@title\n", + "%%writefile src/main.rs\n", + "use onnxruntime::environment::Environment;\n", + "use onnxruntime::GraphOptimizationLevel;\n", + "use onnxruntime::ndarray::{Array2, Axis};\n", + "use onnxruntime::tensor::OrtOwnedTensor;\n", + "\n", + "use std::env;\n", + "\n", + "use tokenizers::tokenizer::{Result, Tokenizer};\n", + "\n", + "fn tokenize(text: String, inputs: usize) -> Vec> {\n", + " // Load tokenizer from HF Hub\n", + " let tokenizer = Tokenizer::from_pretrained(\"bert-base-uncased\", None).unwrap();\n", + "\n", + " // Encode input text\n", + " let encoding = tokenizer.encode(text, true).unwrap();\n", + "\n", + " let v1: Vec = encoding.get_ids().to_vec().into_iter().map(|x| x as i64).collect();\n", + " let v2: Vec = encoding.get_attention_mask().to_vec().into_iter().map(|x| x as i64).collect();\n", + " let v3: Vec = encoding.get_type_ids().to_vec().into_iter().map(|x| x as i64).collect();\n", + "\n", + " let ids = Array2::from_shape_vec((1, v1.len()), v1).unwrap();\n", + " let mask = Array2::from_shape_vec((1, v2.len()), v2).unwrap();\n", + " let tids = Array2::from_shape_vec((1, v3.len()), v3).unwrap();\n", + "\n", + " return if inputs > 2 { vec![ids, mask, tids] } else { vec![ids, mask] };\n", + "}\n", + "\n", + "fn predict(text: String, softmax: bool) -> Vec {\n", + " // Start onnx session\n", + " let environment = Environment::builder()\n", + " .with_name(\"test\")\n", + " .build().unwrap();\n", + "\n", + " // Derive model path\n", + " let model = if softmax { \"text-classify.onnx\" } else { \"embeddings.onnx\" };\n", + "\n", + " let mut session = environment\n", + " .new_session_builder().unwrap()\n", + " .with_optimization_level(GraphOptimizationLevel::Basic).unwrap()\n", + " .with_number_threads(1).unwrap()\n", + " .with_model_from_file(model).unwrap();\n", + "\n", + " let inputs = tokenize(text, session.inputs.len());\n", + "\n", + " // Run inference and print result\n", + " let outputs: Vec> = session.run(inputs).unwrap();\n", + " let output: &OrtOwnedTensor = &outputs[0];\n", + "\n", + " let probabilities: Vec;\n", + " if softmax {\n", + " probabilities = output\n", + " .softmax(Axis(1))\n", + " .iter()\n", + " .copied()\n", + " .collect::>();\n", + " }\n", + " else {\n", + " probabilities= output\n", + " .iter()\n", + " .copied()\n", + " .collect::>();\n", + " }\n", + "\n", + " return probabilities;\n", + "}\n", + "\n", + "fn similarity(v1: &Vec, v2: &Vec) -> f64 {\n", + " let mut dot = 0.0;\n", + " let mut norm1 = 0.0;\n", + " let mut norm2 = 0.0;\n", + "\n", + " for x in 0..v1.len() {\n", + " dot += v1[x] * v2[x];\n", + " norm1 += v1[x].powf(2.0);\n", + " norm2 += v2[x].powf(2.0);\n", + " }\n", + "\n", + " return dot as f64 / (norm1.sqrt() * norm2.sqrt()) as f64\n", + "}\n", + "\n", + "fn main() -> Result<()> {\n", + " // Tokenize input string\n", + " let args: Vec = env::args().collect();\n", + "\n", + " if args.len() <= 2 {\n", + " let v1 = predict(args[1].to_string(), true);\n", + " println!(\"{:?}\", v1);\n", + " }\n", + " else {\n", + " let v1 = predict(args[1].to_string(), false);\n", + " let v2 = predict(args[2].to_string(), false);\n", + " println!(\"{:?}\", similarity(&v1, &v2));\n", + " }\n", + "\n", + " Ok(())\n", + "}" + ], + "execution_count": 47, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Writing src/main.rs\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "OdfQFY-MiA-n" + }, + "source": [ + "## Run Text Classification in Rust with ONNX" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "b0ymX4ftgWcT", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "84b42d3c-82d4-46fc-fb84-94967bd5330f" + }, + "source": [ + "!cargo run \"I am happy\" 2> /dev/null\n", + "!cargo run \"I am mad\" 2> /dev/null" + ], + "execution_count": 48, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[0.00030939875, 0.9996906]\n", + "[0.99912345, 0.0008765513]\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "NKccz6bBiIgW" + }, + "source": [ + "The command above tokenizes the input and runs inference with a text classification model previously created using a Rust ONNX inference session. \n", + "\n", + "As a reminder, the text classification model is judging sentiment using two labels, 0 for negative to 1 for positive. The results above shows the probability of each label per text snippet." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1D1kN0yNiEg7" + }, + "source": [ + "## Build sentence embeddings and compare similarity in Rust with ONNX" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "A9p6F_ODhenH", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "b43ad47e-e1f3-4748-d854-a0dc2024b780" + }, + "source": [ + "!cargo run \"I am happy\" \"I am glad\" 2> /dev/null" + ], + "execution_count": 49, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "0.8298246060854143\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TQ7Wvn0OiRr4" + }, + "source": [ + "The sentence embeddings model produces vectors that can be used to compare semantic similarity, -1 being most dissimilar and 1 being most similar. \n", + "\n", + "Once again, this is 100% Rust, no API or remote calls. And yes, still think it's amazing!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-_FNKUWtjLsO" + }, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook covered how to export models to ONNX using txtai. These models were then run in Python, JavaScript, Java and Rust. Golang was also evaluated but there doesn't currently appear to be a stable enough ONNX runtime available. \n", + "\n", + "This method provides a way to train and run machine learning models using a number of programming languages on a number of platforms.\n", + "\n", + "The following is a non-exhaustive list of use cases. \n", + "\n", + "* Build locally executed models for mobile/edge devices\n", + "* Run models with Java/JavaScript/Rust development stacks when teams prefer not to add Python to the mix\n", + "* Export models to ONNX for Python inference to improve CPU performance and/or reduce number of software dependencies" + ] + } + ] +} diff --git a/examples/19_Train_a_QA_model.ipynb b/examples/19_Train_a_QA_model.ipynb new file mode 100644 index 0000000..23f0d89 --- /dev/null +++ b/examples/19_Train_a_QA_model.ipynb @@ -0,0 +1,295 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "4Pjmz-RORV8E" + }, + "source": [ + "# Train a QA model\n", + "\n", + "The [Hugging Face Model Hub](https://huggingface.co/models) has a wide range of models that can handle many tasks. While these models perform well, the best performance is often found when fine-tuning a model with task-specific data. \n", + "\n", + "Hugging Face provides a [number of full-featured examples](https://github.com/huggingface/transformers/tree/master/examples) to assist with training task-specific models. When building models from the command line, these scripts are a great way to get started.\n", + "\n", + "txtai provides a training pipeline that can be used to train new models programatically using the Transformers Trainer framework.\n", + "\n", + "This example trains a small QA model and then further fine-tunes it with a couple new examples (few-shot learning)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dk31rbYjSTYm" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "XMQuuun2R06J" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline-train] datasets pandas" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "r6nmtieHdMfr" + }, + "source": [ + "# Train a SQuAD 2.0 Model\n", + "\n", + "The first step is training a SQuAD 2.0 model. SQuAD is a question-answer dataset that poses a question with a context along with the identified answer. It's also possible to not have an answer. See the [SQuAD dataset website](https://rajpurkar.github.io/SQuAD-explorer/) for more information.\n", + "\n", + "We'll use a tiny Bert model with a portion of SQuAD 2.0 for efficiency purposes." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 297 + }, + "id": "pg9-tUxEdRfk", + "outputId": "06195c45-4b39-46e5-a462-af566f437ade" + }, + "source": [ + "from datasets import load_dataset\n", + "from txtai.pipeline import HFTrainer\n", + "\n", + "ds = load_dataset(\"squad_v2\")\n", + "\n", + "trainer = HFTrainer()\n", + "trainer(\"google/bert_uncased_L-2_H-128_A-2\", ds[\"train\"].select(range(3000)), task=\"question-answering\", output_dir=\"bert-tiny-squadv2\")\n", + "print(\"Training complete\")" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "Reusing dataset squad_v2 (/root/.cache/huggingface/datasets/squad_v2/squad_v2/2.0.0/09187c73c1b837c95d9a249cd97c2c3f1cebada06efe667b4427714b27639b1d)\n", + "Loading cached processed dataset at /root/.cache/huggingface/datasets/squad_v2/squad_v2/2.0.0/09187c73c1b837c95d9a249cd97c2c3f1cebada06efe667b4427714b27639b1d/cache-73bbe029cf3366fc.arrow\n", + "Some weights of the model checkpoint at google/bert_uncased_L-2_H-128_A-2 were not used when initializing BertForQuestionAnswering: ['cls.predictions.decoder.bias', 'cls.predictions.transform.dense.bias', 'cls.predictions.transform.LayerNorm.bias', 'cls.predictions.transform.LayerNorm.weight', 'cls.predictions.decoder.weight', 'cls.predictions.bias', 'cls.seq_relationship.bias', 'cls.predictions.transform.dense.weight', 'cls.seq_relationship.weight']\n", + "- This IS expected if you are initializing BertForQuestionAnswering from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n", + "- This IS NOT expected if you are initializing BertForQuestionAnswering from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n", + "Some weights of BertForQuestionAnswering were not initialized from the model checkpoint at google/bert_uncased_L-2_H-128_A-2 and are newly initialized: ['qa_outputs.bias', 'qa_outputs.weight']\n", + "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + "

\n", + " \n", + " \n", + " [1131/1131 00:50, Epoch 3/3]\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
StepTraining Loss
5004.501800
10003.875900

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Training complete\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zZtHxNSwFNGC" + }, + "source": [ + "# Fine-tune with new data\n", + "\n", + "Next we'll add a few additional examples. Fine-tuning a QA model will help with framing a certain type of question or improve performance for a specific use-case. \n", + "\n", + "For smaller models with a narrow use case, this helps the model zero in on the types of questions that are to be asked. In this case, we want to tell the model exactly the types of information we're looking for when asking for ingredients. This will help improve confidence in the answers the model is generating.\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 75 + }, + "id": "JBeScS5dFNeW", + "outputId": "13596524-b476-4f55-f430-d25eeda9301f" + }, + "source": [ + "# Training data\n", + "data = [\n", + " {\"question\": \"What ingredient?\", \"context\": \"Pour 1 can whole tomatoes\", \"answers\": \"tomatoes\"},\n", + " {\"question\": \"What ingredient?\", \"context\": \"Dice 1 yellow onion\", \"answers\": \"onion\"},\n", + " {\"question\": \"What ingredient?\", \"context\": \"Cut 1 red pepper\", \"answers\": \"pepper\"},\n", + " {\"question\": \"What ingredient?\", \"context\": \"Peel and dice 1 clove garlic\", \"answers\": \"garlic\"},\n", + " {\"question\": \"What ingredient?\", \"context\": \"Put 1/2 lb beef\", \"answers\": \"beef\"},\n", + "]\n", + "\n", + "model, tokenizer = trainer(\"bert-tiny-squadv2\", data, task=\"question-answering\", num_train_epochs=10)" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + "

\n", + " \n", + " \n", + " [10/10 00:00, Epoch 10/10]\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
StepTraining Loss

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "V7nAl3WtkBNK" + }, + "source": [ + "# Test the model\n", + "\n", + "Now we're ready to test the results! The following sections run a question against the original model only trained with SQuAD 2.0 and the further fine-tuned model." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "46fMiJrAIBu4", + "outputId": "fb92ca0a-d433-486f-b61c-054f4e4a9b36" + }, + "source": [ + "from transformers import pipeline\n", + "\n", + "questions = pipeline(\"question-answering\", model=\"bert-tiny-squadv2\")\n", + "questions(\"What ingredient?\", \"Peel and dice 1 shallot\")" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "{'answer': 'dice 1 shallot',\n", + " 'end': 23,\n", + " 'score': 0.05128436163067818,\n", + " 'start': 9}" + ] + }, + "metadata": {}, + "execution_count": 57 + } + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "nWQMRQm0NwdN", + "outputId": "a1f15b5c-1cf8-4fe5-daa8-e19adc1700e1" + }, + "source": [ + "from transformers import pipeline\n", + "\n", + "questions = pipeline(\"question-answering\", model=model.to(\"cpu\"), tokenizer=tokenizer)\n", + "questions(\"What ingredient?\", \"Peel and dice 1 shallot\")" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "{'answer': 'shallot', 'end': 23, 'score': 0.13187439739704132, 'start': 16}" + ] + }, + "metadata": {}, + "execution_count": 58 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "wJoYksLbkZTJ" + }, + "source": [ + "See how the results are more confident and have a better answer. This method allows using a smaller model with a narrow set of functionality with the upside of increased speed. Give it a try with your own data!" + ] + } + ] +} diff --git a/examples/20_Extractive_QA_to_build_structured_data.ipynb b/examples/20_Extractive_QA_to_build_structured_data.ipynb new file mode 100644 index 0000000..e530813 --- /dev/null +++ b/examples/20_Extractive_QA_to_build_structured_data.ipynb @@ -0,0 +1,529 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "vwELCooy4ljr" + }, + "source": [ + "# Extractive QA to build structured data\n", + "\n", + "Traditional ETL/data parsing systems establish rules to extract information of interest. Regular expressions, string parsing and similar methods define fixed rules. This works in many cases but what if you are working with unstructured data containing numerous variations? The rules can be cumbersome and hard to maintain over time.\n", + "\n", + "This notebook uses machine learning and extractive question-answering (QA) to utilize the vast knowledge built into large language models. These models have been trained on extremely large datasets, learning the many variations of natural language. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ew7orE2O441o" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "LPQTb25tASIG" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline-train]" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_YnqorRKAbLu" + }, + "source": [ + "# Train a QA model with few-shot learning\n", + "\n", + "The code below trains a new QA model using a few examples. These examples gives the model hints on the type of questions that will be asked and the type of answers to look for. It doesn't take a lot of examples to do this as shown below." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "OUc9gqTyAYnm", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 75 + }, + "outputId": "7e7f93c7-5ad5-46a6-d04d-c7450d246f6c" + }, + "source": [ + "import pandas as pd\n", + "from txtai.pipeline import HFTrainer, Questions, Labels\n", + "\n", + "# Training data for few-shot learning\n", + "data = [\n", + " {\"question\": \"What is the url?\",\n", + " \"context\": \"Faiss (https://github.com/facebookresearch/faiss) is a library for efficient similarity search.\",\n", + " \"answers\": \"https://github.com/facebookresearch/faiss\"},\n", + " {\"question\": \"What is the url\", \"context\": \"The last release was Wed Sept 25 2021\", \"answers\": None},\n", + " {\"question\": \"What is the date?\", \"context\": \"The last release was Wed Sept 25 2021\", \"answers\": \"Wed Sept 25 2021\"},\n", + " {\"question\": \"What is the date?\", \"context\": \"The order total comes to $44.33\", \"answers\": None},\n", + " {\"question\": \"What is the amount?\", \"context\": \"The order total comes to $44.33\", \"answers\": \"$44.33\"},\n", + " {\"question\": \"What is the amount?\", \"context\": \"The last release was Wed Sept 25 2021\", \"answers\": None},\n", + "]\n", + "\n", + "# Fine-tune QA model\n", + "trainer = HFTrainer()\n", + "model, tokenizer = trainer(\"distilbert-base-cased-distilled-squad\", data, task=\"question-answering\")" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + "

\n", + " \n", + " \n", + " [3/3 00:03, Epoch 3/3]\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
StepTraining Loss

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1hzPnmrTaUjH" + }, + "source": [ + "# Parse data into a structured table\n", + "\n", + "The next section takes a series of rows of text and runs a set of questions against each row. The answers are then used to build a pandas DataFrame." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "4X5z3UjnAGe7", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 206 + }, + "outputId": "41015023-c7b7-4515-ad30-f1b2d8143ea2" + }, + "source": [ + "# Input data\n", + "context = [\"Released on 6/03/2021\",\n", + " \"Release delayed until the 11th of August\",\n", + " \"Documentation can be found here: neuml.github.io/txtai\",\n", + " \"The stock price fell to three dollars\",\n", + " \"Great day: closing price for March 23rd is $33.11, for details - https://finance.google.com\"]\n", + "\n", + "# Define column queries\n", + "queries = [\"What is the url?\", \"What is the date?\", \"What is the amount?\"]\n", + "\n", + "# Extract fields\n", + "questions = Questions(path=(model, tokenizer), gpu=True)\n", + "results = [questions([question] * len(context), context) for question in queries]\n", + "results.append(context)\n", + "\n", + "# Load into DataFrame\n", + "pd.DataFrame(list(zip(*results)), columns=[\"URL\", \"Date\", \"Amount\", \"Text\"])" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/html": [ + "\n", + "

\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
URLDateAmountText
0None6/03/2021NoneReleased on 6/03/2021
1None11th of AugustNoneRelease delayed until the 11th of August
2neuml.github.io/txtaiNoneNoneDocumentation can be found here: neuml.github....
3NoneNonethree dollarsThe stock price fell to three dollars
4https://finance.google.comMarch 23rd$33.11Great day: closing price for March 23rd is $33...
\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + " \n", + "
\n", + "
\n", + " " + ], + "text/plain": [ + " URL ... Text\n", + "0 None ... Released on 6/03/2021\n", + "1 None ... Release delayed until the 11th of August\n", + "2 neuml.github.io/txtai ... Documentation can be found here: neuml.github....\n", + "3 None ... The stock price fell to three dollars\n", + "4 https://finance.google.com ... Great day: closing price for March 23rd is $33...\n", + "\n", + "[5 rows x 4 columns]" + ] + }, + "metadata": {}, + "execution_count": 7 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mY1Le-pve5yi" + }, + "source": [ + "# Add additional columns\n", + "\n", + "This method can be combined with other models to categorize, group or otherwise derive additional columns. The code below derives an additional sentiment column." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 479 + }, + "id": "0kyJUcrKe43a", + "outputId": "cea0d481-3315-4ec8-a7f9-8a9c4942db10" + }, + "source": [ + "# Add sentiment\n", + "labels = Labels(path=\"distilbert-base-uncased-finetuned-sst-2-english\", dynamic=False)\n", + "labels = [\"POSITIVE\" if x[0][0] == 1 else \"NEGATIVE\" for x in labels(context)]\n", + "results.insert(len(results) - 1, labels)\n", + "\n", + "# Load into DataFrame\n", + "pd.DataFrame(list(zip(*results)), columns=[\"URL\", \"Date\", \"Amount\", \"Sentiment\", \"Text\"])" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/html": [ + "\n", + "
\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
URLDateAmountSentimentText
0None6/03/2021NonePOSITIVEReleased on 6/03/2021
1None11th of AugustNoneNEGATIVERelease delayed until the 11th of August
2neuml.github.io/txtaiNoneNoneNEGATIVEDocumentation can be found here: neuml.github....
3NoneNonethree dollarsNEGATIVEThe stock price fell to three dollars
4https://finance.google.comMarch 23rd$33.11POSITIVEGreat day: closing price for March 23rd is $33...
\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + " \n", + "
\n", + "
\n", + " " + ], + "text/plain": [ + " URL ... Text\n", + "0 None ... Released on 6/03/2021\n", + "1 None ... Release delayed until the 11th of August\n", + "2 neuml.github.io/txtai ... Documentation can be found here: neuml.github....\n", + "3 None ... The stock price fell to three dollars\n", + "4 https://finance.google.com ... Great day: closing price for March 23rd is $33...\n", + "\n", + "[5 rows x 5 columns]" + ] + }, + "metadata": {}, + "execution_count": 8 + } + ] + } + ] +} diff --git a/examples/21_Export_and_run_other_machine_learning_models.ipynb b/examples/21_Export_and_run_other_machine_learning_models.ipynb new file mode 100644 index 0000000..46f1855 --- /dev/null +++ b/examples/21_Export_and_run_other_machine_learning_models.ipynb @@ -0,0 +1,664 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "accelerator": "GPU", + "colab": { + "name": "21 - Export and run other machine learning models", + "provenance": [], + "collapsed_sections": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "4Pjmz-RORV8E" + }, + "source": [ + "# Export and run other machine learning models\n", + "\n", + "txtai primarily has support for [Hugging Face Transformers](https://github.com/huggingface/transformers) and [ONNX](https://github.com/microsoft/onnxruntime) models. This enables txtai to hook into the rich model framework available in Python, export this functionality via the API to other languages (JavaScript, Java, Go, Rust) and even export and natively load models with ONNX.\n", + "\n", + "What about other machine learning frameworks? Say we have an existing TF-IDF + Logistic Regression model that has been well tuned. Can this model be exported to ONNX and used in txtai for labeling and similarity queries? Or what about a simple PyTorch text classifier? Yes, both of these can be done!\n", + "\n", + "With the [onnxmltools](https://github.com/onnx/onnxmltools) library, traditional models from [scikit-learn](https://scikit-learn.org/stable/), [XGBoost](https://xgboost.readthedocs.io/en/latest/) and others can be exported to ONNX and loaded with txtai. Additionally, Hugging Face's trainer module can train generic PyTorch modules. This notebook will walk through all these examples.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dk31rbYjSTYm" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "XMQuuun2R06J" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline,similarity] datasets" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "r6nmtieHdMfr" + }, + "source": [ + "# Train a TF-IDF + Logistic Regression model\n", + "\n", + "For this example, we'll load the emotion dataset from Hugging Face datasets and build a TF-IDF + Logistic Regression model with scikit-learn.\n", + "\n", + "The emotion dataset has the following labels:\n", + "\n", + "- sadness (0)\n", + "- joy (1)\n", + "- love (2)\n", + "- anger (3)\n", + "- fear (4)\n", + "- surprise (5)\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "pg9-tUxEdRfk" + }, + "source": [ + "from datasets import load_dataset\n", + "\n", + "from sklearn.feature_extraction.text import TfidfVectorizer\n", + "from sklearn.linear_model import LogisticRegression\n", + "from sklearn.pipeline import Pipeline\n", + "\n", + "ds = load_dataset(\"emotion\")\n", + "\n", + "# Train the model\n", + "pipeline = Pipeline([\n", + " ('tfidf', TfidfVectorizer()),\n", + " ('lr', LogisticRegression(max_iter=250))\n", + "])\n", + "\n", + "pipeline.fit(ds[\"train\"][\"text\"], ds[\"train\"][\"label\"])\n", + "\n", + "# Determine accuracy on validation set\n", + "results = pipeline.predict(ds[\"validation\"][\"text\"])\n", + "labels = ds[\"validation\"][\"label\"]\n", + "\n", + "results = [results[x] == label for x, label in enumerate(labels)]\n", + "print(\"Accuracy =\", sum(results) / len(ds[\"validation\"]))" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "Using custom data configuration default\n", + "Reusing dataset emotion (/root/.cache/huggingface/datasets/emotion/default/0.0.0/348f63ca8e27b3713b6c04d723efe6d824a56fb3d1449794716c0f0296072705)\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Accuracy = 0.8595\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "49jZD4jQgdBg" + }, + "source": [ + "86% accuracy - not too bad! While we all get caught up in deep learning and advanced methods, good ole TF-IDF + Logistic Regression is still a solid performer and runs much faster. If that level of accuracy works, no reason to overcomplicate things." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zZtHxNSwFNGC" + }, + "source": [ + "# Export and load with txtai\n", + "\n", + "The next section exports this model to ONNX and shows how the model can be used for similarity queries. " + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "JBeScS5dFNeW", + "outputId": "e1b5cbf4-87dd-4598-e7ee-14e36cf31a7c" + }, + "source": [ + "from txtai.pipeline import Labels, MLOnnx, Similarity\n", + "\n", + "def tokenize(inputs, **kwargs):\n", + " if isinstance(inputs, str):\n", + " inputs = [inputs]\n", + "\n", + " return {\"input_ids\": [[x] for x in inputs]}\n", + "\n", + "def query(model, tokenizer, multilabel=False):\n", + " # Load models into similarity pipeline\n", + " similarity = Similarity((model, tokenizer), dynamic=False)\n", + "\n", + " # Add labels to model\n", + " similarity.pipeline.model.config.id2label = {0: \"sadness\", 1: \"joy\", 2: \"love\", 3: \"anger\", 4: \"fear\", 5: \"surprise\"}\n", + " similarity.pipeline.model.config.label2id = dict((v, k) for k, v in similarity.pipeline.model.config.id2label.items())\n", + "\n", + " inputs = [\"that caught me off guard\", \"I didn t see that coming\", \"i feel bad\", \"What a wonderful goal!\"]\n", + " scores = similarity(\"joy\", inputs, multilabel)\n", + " for uid, score in scores[:5]:\n", + " print(inputs[uid], score)\n", + "\n", + "# Export to ONNX\n", + "onnx = MLOnnx()\n", + "model = onnx(pipeline)\n", + "\n", + "# Create labels pipeline using scikit-learn ONNX model\n", + "sklabels = Labels((model, tokenize), dynamic=False)\n", + "\n", + "# Add labels to model\n", + "sklabels.pipeline.model.config.id2label = {0: \"sadness\", 1: \"joy\", 2: \"love\", 3: \"anger\", 4: \"fear\", 5: \"surprise\"}\n", + "sklabels.pipeline.model.config.label2id = dict((v, k) for k, v in sklabels.pipeline.model.config.id2label.items())\n", + "\n", + "# Run test query using model\n", + "query(model, tokenize, None)" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "What a wonderful goal! 0.909473717212677\n", + "I didn t see that coming 0.47113093733787537\n", + "that caught me off guard 0.42067453265190125\n", + "i feel bad 0.019547615200281143\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "d-y8gFJwCwKN" + }, + "source": [ + "txtai can use a standard text classification model for similarity queries, where the label(s) are a list of fixed queries. The output above shows the best results for the query \"joy\"." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cbqwX7GgKBkf" + }, + "source": [ + "# Train a PyTorch model\n", + "\n", + "The next section defines a simple PyTorch text classifier. The transformers library has a trainer package that supports training PyTorch models, assuming some standard conventions/naming is used. " + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 239 + }, + "id": "k8PkTlBLKBTy", + "outputId": "4f48bfb2-2f16-45e3-d3e6-f1a2a747fd09" + }, + "source": [ + "# Set predictable seeds\n", + "import os\n", + "import random\n", + "import torch\n", + "\n", + "import numpy as np\n", + "\n", + "from torch import nn\n", + "from torch.nn import CrossEntropyLoss\n", + "from transformers import AutoConfig, AutoTokenizer\n", + "\n", + "from txtai.models import Registry\n", + "from txtai.pipeline import HFTrainer\n", + "\n", + "from transformers.modeling_outputs import SequenceClassifierOutput\n", + "\n", + "def seed(seed=42):\n", + " random.seed(seed)\n", + " os.environ['PYTHONHASHSEED'] = str(seed)\n", + " np.random.seed(seed)\n", + " torch.manual_seed(seed)\n", + " torch.cuda.manual_seed(seed)\n", + " torch.backends.cudnn.deterministic = True\n", + "\n", + "class Simple(nn.Module):\n", + " def __init__(self, vocab, dimensions, labels):\n", + " super().__init__()\n", + "\n", + " self.config = AutoConfig.from_pretrained(\"bert-base-uncased\")\n", + " self.labels = labels\n", + "\n", + " self.embedding = nn.EmbeddingBag(vocab, dimensions)\n", + " self.classifier = nn.Linear(dimensions, labels)\n", + " self.init_weights()\n", + "\n", + " def init_weights(self):\n", + " initrange = 0.5\n", + " self.embedding.weight.data.uniform_(-initrange, initrange)\n", + " self.classifier.weight.data.uniform_(-initrange, initrange)\n", + " self.classifier.bias.data.zero_()\n", + "\n", + " def forward(self, input_ids=None, labels=None, **kwargs):\n", + " embeddings = self.embedding(input_ids)\n", + " logits = self.classifier(embeddings)\n", + "\n", + " loss = None\n", + " if labels is not None:\n", + " loss_fct = CrossEntropyLoss()\n", + " loss = loss_fct(logits.view(-1, self.labels), labels.view(-1))\n", + "\n", + " return SequenceClassifierOutput(\n", + " loss=loss,\n", + " logits=logits,\n", + " )\n", + "\n", + "# Set seed for reproducibility\n", + "seed()\n", + "\n", + "# Define model\n", + "tokenizer = AutoTokenizer.from_pretrained(\"bert-base-uncased\")\n", + "model = Simple(tokenizer.vocab_size, 128, len(ds[\"train\"].unique(\"label\")))\n", + "\n", + "# Train model\n", + "train = HFTrainer()\n", + "model, tokenizer = train((model, tokenizer), ds[\"train\"], per_device_train_batch_size=8, learning_rate=1e-3, num_train_epochs=15, logging_steps=10000)\n", + "\n", + "# Register custom model to fully support pipelines\n", + "Registry.register(model)\n", + "\n", + "# Create labels pipeline using PyTorch model\n", + "thlabels = Labels((model, tokenizer), dynamic=False)\n", + "\n", + "# Determine accuracy on validation set\n", + "results = [row[\"label\"] == thlabels(row[\"text\"])[0][0] for row in ds[\"validation\"]]\n", + "print(\"Accuracy = \", sum(results) / len(ds[\"validation\"]))" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "Loading cached processed dataset at /root/.cache/huggingface/datasets/emotion/default/0.0.0/348f63ca8e27b3713b6c04d723efe6d824a56fb3d1449794716c0f0296072705/cache-a983327c4471f5aa.arrow\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + "
\n", + " \n", + " \n", + " [30000/30000 02:28, Epoch 15/15]\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
StepTraining Loss
100001.017600
200000.286200
300000.152500

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Accuracy = 0.883\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nHQoJnrj60Pz" + }, + "source": [ + "88% accuracy this time. Pretty good for such a simple network and something that could definitely be improved upon. \n", + "\n", + "Once again let's run similarity queries using this model." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "W5_NDInF5lFN", + "outputId": "38a2c126-63e9-40dc-f309-a29826b5b937" + }, + "source": [ + "query(model, tokenizer)" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "What a wonderful goal! 1.0\n", + "that caught me off guard 0.9998751878738403\n", + "I didn t see that coming 0.7328283190727234\n", + "i feel bad 5.2972134609891875e-19\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "KmcsdIltDTwj" + }, + "source": [ + "Same result order as with the scikit-learn model with scoring variations which is expected given this is a completely different model." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-fNTi2jb68rv" + }, + "source": [ + "# Pooled embeddings\n", + "\n", + "The PyTorch model above consists of an embeddings layer with a linear classifier on top of it. What if we take that embeddings layer and use it for similarity queries? Let's give it a try." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "J1yhfHKC7N7L", + "outputId": "11567948-769a-44df-9057-9fe9837a73dd" + }, + "source": [ + "from txtai.embeddings import Embeddings\n", + "\n", + "class SimpleEmbeddings(nn.Module):\n", + " def __init__(self, embeddings):\n", + " super().__init__()\n", + "\n", + " self.embeddings = embeddings\n", + "\n", + " def forward(self, input_ids=None, **kwargs):\n", + " return (self.embeddings(input_ids),)\n", + "\n", + "embeddings = Embeddings({\"method\": \"pooling\", \"path\": SimpleEmbeddings(model.embedding), \"tokenizer\": \"bert-base-uncased\"})\n", + "print(embeddings.similarity(\"mad\", [\"Glad you found it\", \"Happy to see you\", \"I'm angry\"]))" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[(2, 0.8323876857757568), (1, -0.11010512709617615), (0, -0.16152513027191162)]\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0kTUEIcmBNuV" + }, + "source": [ + "Definitely looks like the embeddings have stored knowledge. Could these embeddings be good enough to build a semantic search index, especially for sentiment based data, given the training dataset? Possibly. It certainly would run faster than a standard transformer model (see below). " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "V7nAl3WtkBNK" + }, + "source": [ + "# Train a transformer model and compare accuracy/speed\n", + "\n", + "Let's train a standard transformer sequence classifier and compare the accuracy/speed between the two. " + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 274 + }, + "id": "46fMiJrAIBu4", + "outputId": "f0512cf8-3bc2-41ed-caff-e1541403f2a5" + }, + "source": [ + "train = HFTrainer()\n", + "model, tokenizer = train(\"microsoft/xtremedistil-l6-h384-uncased\", ds[\"train\"], logging_steps=2000)\n", + "\n", + "tflabels = Labels((model, tokenizer), dynamic=False)\n", + "\n", + "# Determine accuracy on validation set\n", + "results = [row[\"label\"] == tflabels(row[\"text\"])[0][0] for row in ds[\"validation\"]]\n", + "print(\"Accuracy = \", sum(results) / len(ds[\"validation\"]))" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "Loading cached processed dataset at /root/.cache/huggingface/datasets/emotion/default/0.0.0/348f63ca8e27b3713b6c04d723efe6d824a56fb3d1449794716c0f0296072705/cache-98b7ef31bf6ca944.arrow\n", + "Some weights of BertForSequenceClassification were not initialized from the model checkpoint at microsoft/xtremedistil-l6-h384-uncased and are newly initialized: ['classifier.bias', 'classifier.weight']\n", + "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + "

\n", + " \n", + " \n", + " [6000/6000 07:13, Epoch 3/3]\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
StepTraining Loss
20000.635500
40000.281700
60000.192600

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Accuracy = 0.926\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ycvozGzPmlbS" + }, + "source": [ + "As expected, the accuracy is better. The model above is a distilled model and even better accuracy can be obtained with a model like \"roberta-base\" with the tradeoff being increased training/inference time. \n", + "\n", + "Speaking of speed, let's compare the speed of these models." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "nWQMRQm0NwdN", + "outputId": "4a49406c-b4eb-46b1-edab-de01c15fdccb" + }, + "source": [ + "import time\n", + "\n", + "# Test inputs\n", + "inputs = ds[\"test\"][\"text\"]\n", + "print(\"Testing speed of %d items\" % len(inputs))\n", + "\n", + "start = time.time()\n", + "r1 = sklabels(inputs, multilabel=None)\n", + "print(\"TF-IDF + Logistic Regression time =\", time.time() - start)\n", + "\n", + "start = time.time()\n", + "r2 = thlabels(inputs)\n", + "print(\"PyTorch time =\", time.time() - start)\n", + "\n", + "start = time.time()\n", + "r3 = tflabels(inputs)\n", + "print(\"Transformers time =\", time.time() - start, \"\\n\")\n", + "\n", + "# Compare model results\n", + "for x in range(5):\n", + " print(\"index: %d\" % x)\n", + " print(r1[x][0])\n", + " print(r2[x][0])\n", + " print(r3[x][0], \"\\n\")" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Testing speed of 2000 items\n", + "TF-IDF + Logistic Regression time = 1.0483319759368896\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "PyTorch time = 2.0001697540283203\n", + "Transformers time = 13.71584439277649 \n", + "\n", + "index: 0\n", + "(0, 0.7258279323577881)\n", + "(0, 1.0)\n", + "(0, 0.998375654220581) \n", + "\n", + "index: 1\n", + "(0, 0.854256272315979)\n", + "(0, 1.0)\n", + "(0, 0.9983494281768799) \n", + "\n", + "index: 2\n", + "(0, 0.6306578516960144)\n", + "(0, 0.9999700784683228)\n", + "(0, 0.9982945322990417) \n", + "\n", + "index: 3\n", + "(1, 0.554378092288971)\n", + "(1, 0.9998960494995117)\n", + "(1, 0.99846351146698) \n", + "\n", + "index: 4\n", + "(0, 0.8961835503578186)\n", + "(0, 1.0)\n", + "(0, 0.9984095692634583) \n", + "\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1YMTyqIWDiOB" + }, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook showed how frameworks outside of Transformers and ONNX can be used as models in txtai.\n", + "\n", + "As seen in the section above, TF-IDF + Logistic Regression is 16 times faster than a distilled Transformers model. A simple PyTorch network is 8 times faster. Depending on your accuracy requirements, it may make sense to use a simpler model to get better runtime performance." + ] + } + ] +} diff --git a/examples/22_Transform_tabular_data_with_composable_workflows.ipynb b/examples/22_Transform_tabular_data_with_composable_workflows.ipynb new file mode 100644 index 0000000..26c7f15 --- /dev/null +++ b/examples/22_Transform_tabular_data_with_composable_workflows.ipynb @@ -0,0 +1,951 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "accelerator": "GPU", + "colab": { + "name": "22 - Transform tabular data with composable workflows", + "provenance": [], + "collapsed_sections": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "4Pjmz-RORV8E" + }, + "source": [ + "# Transform tabular data with composable workflows\n", + "\n", + "txtai has support for processing both unstructured and structured data. Structured or tabular data is grouped into rows and columns. This can be a spreadsheet, an API call that returns JSON or XML or even list of key-value pairs.\n", + "\n", + "This notebook will walk through examples on how to use workflows with the tabular pipeline to transform and index structured data.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dk31rbYjSTYm" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies. We will install the api, pipeline and workflow optional extras packages. " + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "XMQuuun2R06J" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[api,pipeline,workflow] sacremoses" + ], + "execution_count": 66, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "NSYrP0hjtR_E" + }, + "source": [ + "# CSV Workflow\n", + "\n", + "The first example will transform and index a CSV file. The [COVID-19 Open Research Dataset](https://allenai.org/data/cord-19) (CORD-19) is a repository of medical articles covering COVID-19. This workflow reads the input CSV and builds a semantic search index.\n", + "\n", + "The first step is downloading the dataset locally." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "BoPJIKWoTibk" + }, + "source": [ + "%%capture\n", + "# Get CORD-19 metadata file\n", + "!wget https://ai2-semanticscholar-cord-19.s3-us-west-2.amazonaws.com/2021-11-01/metadata.csv\n", + "!head -1 metadata.csv > input.csv\n", + "!tail -10000 metadata.csv >> input.csv" + ], + "execution_count": 67, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Q1ivX4eBuU8T" + }, + "source": [ + "The next section creates a simple workflow consisting of a tabular pipeline. The tabular pipeline builds a list of (id, text, tag) tuples that can be easily loaded into an Embeddings index. For this example, we'll use the `url` column as the id and the `title` column as the text column. The textcolumns parameter takes a list of columns to support indexing text content from multiple columns. \n", + "\n", + "The file input.csv is processed and the first 5 rows are shown." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "-pi2QU3TSlM_", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "a30ffeb2-1d85-467b-a0a3-134c8cbac19f" + }, + "source": [ + "from txtai.pipeline import Tabular\n", + "from txtai.workflow import Task, Workflow\n", + "\n", + "# Create tabular instance mapping input.csv fields\n", + "tabular = Tabular(\"url\", [\"title\"])\n", + "\n", + "# Create workflow\n", + "workflow = Workflow([Task(tabular)])\n", + "\n", + "# Print 5 rows of input.csv via workflow\n", + "list(workflow([\"input.csv\"]))[:5]" + ], + "execution_count": 68, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[('https://doi.org/10.1016/j.cmpb.2021.106469; https://www.ncbi.nlm.nih.gov/pubmed/34715516/',\n", + " 'Computer simulation of the dynamics of a spatial susceptible-infected-recovered epidemic model with time delays in transmission and treatment.',\n", + " None),\n", + " ('https://www.ncbi.nlm.nih.gov/pubmed/34232002/; https://doi.org/10.36849/jdd.5544',\n", + " 'Understanding the Potential Role of Abrocitinib in the Time of SARS-CoV-2',\n", + " None),\n", + " ('https://doi.org/10.1186/1471-2458-8-42; https://www.ncbi.nlm.nih.gov/pubmed/18234083/',\n", + " \"Can the concept of Health Promoting Schools help to improve students' health knowledge and practices to combat the challenge of communicable diseases: Case study in Hong Kong?\",\n", + " None),\n", + " ('https://www.ncbi.nlm.nih.gov/pubmed/32983582/; https://www.sciencedirect.com/science/article/pii/S2095809920302514?v=s5; https://api.elsevier.com/content/article/pii/S2095809920302514; https://doi.org/10.1016/j.eng.2020.07.018',\n", + " 'Buying time for an effective epidemic response: The impact of a public holiday for outbreak control on COVID-19 epidemic spread',\n", + " None),\n", + " ('https://doi.org/10.1093/pcmedi/pbab016',\n", + " 'The SARS-CoV-2 spike L452R-E484Q variant in the Indian B.1.617 strain showed significant reduction in the neutralization activity of immune sera',\n", + " None)]" + ] + }, + "metadata": {}, + "execution_count": 68 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "UYYKnwNhu0hv" + }, + "source": [ + "Next, we take the workflow output, build an Embeddings index and run a search query." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "G7M34puLWeZm", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "0e9e771b-d53d-4c4e-8ac8-257c33992f66" + }, + "source": [ + "from txtai.embeddings import Embeddings\n", + "\n", + "# Embeddings with sentence-transformers backend\n", + "embeddings = Embeddings({\"method\": \"transformers\", \"path\": \"sentence-transformers/paraphrase-mpnet-base-v2\"})\n", + "\n", + "# Index subset of CORD-19 data\n", + "data = list(workflow([\"input.csv\"]))\n", + "embeddings.index(data)\n", + "\n", + "for uid, _ in embeddings.search(\"insulin\"):\n", + " title = [text for url, text, _ in data if url == uid][0]\n", + " print(title, uid)" + ], + "execution_count": 69, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Importance of diabetes management during the COVID-19 pandemic. https://doi.org/10.1080/00325481.2021.1978704; https://www.ncbi.nlm.nih.gov/pubmed/34602003/\n", + "Position Statement on How to Manage Patients with Diabetes and COVID-19 https://www.ncbi.nlm.nih.gov/pubmed/33442169/; https://doi.org/10.15605/jafes.035.01.03\n", + "Successful blood glucose management of a severe COVID-19 patient with diabetes: A case report https://www.ncbi.nlm.nih.gov/pubmed/32590779/; https://doi.org/10.1097/md.0000000000020844\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vPQv7GJSu9Uq" + }, + "source": [ + "The example searched for the term `insulin`. The top results mention diabetes and blood glucose which are a closely associated terms for diabetes." + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Workflow with stored content\n", + "\n", + "Next we'll re-run the same example adding in full content storage. Full content storage enables SQL queries." + ], + "metadata": { + "id": "BQeLRC1md6GR" + } + }, + { + "cell_type": "code", + "source": [ + "import json\n", + "\n", + "# Create tabular instance mapping input.csv fields\n", + "tabular = Tabular(\"url\", [\"title\"], True)\n", + "\n", + "# Create workflow\n", + "workflow = Workflow([Task(tabular)])\n", + "\n", + "# Embeddings with sentence-transformers backend\n", + "embeddings = Embeddings({\"method\": \"transformers\", \"path\": \"sentence-transformers/paraphrase-mpnet-base-v2\", \"content\": True})\n", + "\n", + "# Index subset of CORD-19 data\n", + "data = list(workflow([\"input.csv\"]))\n", + "embeddings.index(data)\n", + "\n", + "for result in embeddings.search(\"select title, abstract, authors, doi from txtai where similar('insulin')\"):\n", + " print(json.dumps(result, default=str, indent=2))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Ln61lB7QeDEq", + "outputId": "ae364e17-36a5-480e-c614-c7e58d8b8462" + }, + "execution_count": 70, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "{\n", + " \"title\": \"Importance of diabetes management during the COVID-19 pandemic.\",\n", + " \"abstract\": \"Uncontrolled diabetes and/or hyperglycemia is associated with severe COVID-19 disease and increased mortality. It is now known that poor glucose control before hospital admission can be associated with a high risk of in-hospital death. By achieving and maintaining glycemic control, primary care physicians (PCPs) play a critical role in limiting this potentially devastating outcome. Further, despite the hope that mass vaccination will help control the pandemic, genetic variants of the virus are causing surges in some countries. As such, PCPs will treat an increasing number of patients with diabetes who have symptoms of post-COVID-19 infection, or even have new-onset type 2 diabetes as a result of COVID-19 infection. However, much of the literature published focuses on the effects of COVID-19 in hospitalized patients, with few publications providing information and advice to those caring for people with diabetes in the primary care setting. This manuscript reviews the current knowledge of the risk and outcomes of individuals with diabetes who are infected with COVID-19 and provides information for PCPs on the importance of glucose control, appropriate treatment, and use of telemedicine and online prescription delivery systems to limit the potentially devastating effects of COVID-19 in people with hyperglycemia.\",\n", + " \"authors\": \"Pettus, Jeremy; Skolnik, Neil\",\n", + " \"doi\": \"10.1080/00325481.2021.1978704\"\n", + "}\n", + "{\n", + " \"title\": \"Position Statement on How to Manage Patients with Diabetes and COVID-19\",\n", + " \"abstract\": null,\n", + " \"authors\": null,\n", + " \"doi\": \"10.15605/jafes.035.01.03\"\n", + "}\n", + "{\n", + " \"title\": \"Successful blood glucose management of a severe COVID-19 patient with diabetes: A case report\",\n", + " \"abstract\": \"RATIONALE: Coronavirus disease 2019 (COVID-19) has emerged as a rapidly spreading communicable disease affecting individuals worldwide. Patients with diabetes are more vulnerable to the disease, and the mortality is higher than in those without diabetes. We reported a severe COVID-19 patient with diabetes and shared our experience with blood glucose management. PATIENT CONCERNS: A 64-year-old female diabetes patient was admitted to the intensive care unit due to productive coughing for 8 days without any obvious cause. The results of blood gas analysis indicated that the partial pressure of oxygen was 84 mm Hg with oxygen 8 L/min, and the oxygenation index was less than 200 mm Hg. In addition, postprandial blood glucose levels were abnormal (29.9 mmol/L). DIAGNOSES: The patient was diagnosed with COVID-19 (severe type) and type 2 diabetes. INTERVENTIONS: Comprehensive interventions including establishing a multidisciplinary team, closely monitoring her blood glucose level, an individualized diabetes diet, early activities, psychological care, etc, were performed to control blood glucose while actively treating COVID-19 infection. OUTCOMES: After the comprehensive measures, the patient's blood glucose level gradually became stable, and the patient was discharged after 20 days of hospitalization. LESSONS: This case indicated that the comprehensive measures performed by a multidisciplinary team achieved good treatment effects on a COVID-19 patient with diabetes. Targeted treatment and nursing methods should be performed based on patients\\u2019 actual situations in clinical practice.\",\n", + " \"authors\": \"Hu, Rujun; Gao, Huiming; Huang, Di; Jiang, Deyu; Chen, Fang; Fu, Bao; Yuan, Xiaoli; Li, Jin; Jiang, Zhixia\",\n", + " \"doi\": \"10.1097/md.0000000000020844\"\n", + "}\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Note how the same results are returned with additional content fields." + ], + "metadata": { + "id": "yTvn0O1v_eHT" + } + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gzFmQDXfvniJ" + }, + "source": [ + "# JSON Service Workflow\n", + "\n", + "The next example builds a workflow that runs a query against a remote URL, retrieves the results, then transforms and indexes the tabular data. This example gets the top results from the [Hacker News front page](https://news.ycombinator.com/). \n", + "\n", + "Below shows how to build the ServiceTask and prints the first JSON result. Details on how to configure the ServiceTask can be found in [txtai's documentation](https://neuml.github.io/txtai/workflows/)." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "bA8SihkeZqbJ", + "outputId": "05962fa0-7e79-49a8-b6ac-3d535ace3fad" + }, + "source": [ + "from txtai.workflow import ServiceTask\n", + "\n", + "service = ServiceTask(url=\"https://hn.algolia.com/api/v1/search\", method=\"get\", params={\"tags\": None}, batch=False, extract=\"hits\")\n", + "workflow = Workflow([service])\n", + "\n", + "list(workflow([\"front_page\"]))[0][2]" + ], + "execution_count": 71, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "{'_highlightResult': {'author': {'matchLevel': 'none',\n", + " 'matchedWords': [],\n", + " 'value': 'cheesestain'},\n", + " 'title': {'matchLevel': 'none',\n", + " 'matchedWords': [],\n", + " 'value': 'Ante: A low-level functional language'},\n", + " 'url': {'matchLevel': 'none',\n", + " 'matchedWords': [],\n", + " 'value': 'https://antelang.org/'}},\n", + " '_tags': ['story', 'author_cheesestain', 'story_31775216', 'front_page'],\n", + " 'author': 'cheesestain',\n", + " 'comment_text': None,\n", + " 'created_at': '2022-06-17T07:39:40.000Z',\n", + " 'created_at_i': 1655451580,\n", + " 'num_comments': 109,\n", + " 'objectID': '31775216',\n", + " 'parent_id': None,\n", + " 'points': 207,\n", + " 'story_id': None,\n", + " 'story_text': None,\n", + " 'story_title': None,\n", + " 'story_url': None,\n", + " 'title': 'Ante: A low-level functional language',\n", + " 'url': 'https://antelang.org/'}" + ] + }, + "metadata": {}, + "execution_count": 71 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Lv_ybw1VwK1N" + }, + "source": [ + "Next we'll map the JSON data using the tabular pipeline. `url` will be used as the id column and `title` as the text to index." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "YAbwhsaveKo1", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "15c1b28a-3c16-4d38-c586-6b8eae244dbb" + }, + "source": [ + "from txtai.workflow import Task\n", + "\n", + "# Create tabular instance mapping input.csv fields\n", + "tabular = Tabular(\"url\", [\"title\"])\n", + "\n", + "# Recreate service applying the tabular pipeline to each result\n", + "service = ServiceTask(action=tabular, url=\"https://hn.algolia.com/api/v1/search\", method=\"get\", params={\"tags\": None}, batch=False, extract=\"hits\")\n", + "workflow = Workflow([service])\n", + "\n", + "list(workflow([\"front_page\"]))[2]" + ], + "execution_count": 72, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "('https://antelang.org/', 'Ante: A low-level functional language', None)" + ] + }, + "metadata": {}, + "execution_count": 72 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SbjMuN5lw63c" + }, + "source": [ + "As we did previously, let's build an Embeddings index and run a search query." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "5lx9pa65e23E", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "99e05da7-b013-4d17-d82b-c7e13e79a563" + }, + "source": [ + "# Embeddings with sentence-transformers backend\n", + "embeddings = Embeddings({\"method\": \"transformers\", \"path\": \"sentence-transformers/paraphrase-mpnet-base-v2\"})\n", + "\n", + "# Index Hacker News front page\n", + "data = list(workflow([\"front_page\"]))\n", + "embeddings.index(data)\n", + "\n", + "for uid, _ in embeddings.search(\"programming\"):\n", + " title = [text for url, text, _ in data if url == uid][0]\n", + " print(title, uid)" + ], + "execution_count": 73, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Bundling binary tools in Python wheels https://simonwillison.net/2022/May/23/bundling-binary-tools-in-python-wheels/\n", + "Ante: A low-level functional language https://antelang.org/\n", + "Adding a Rust compiler front end to GCC [video] https://www.youtube.com/watch?v=R8Pr21nlhig\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yq26KqfgxJ6Q" + }, + "source": [ + "# XML Service workflow\n", + "\n", + "txtai's ServiceTask can consume both JSON and XML. This example runs a query against the [arXiv API](https://arxiv.org/), transforms the results and indexes them for search.\n", + "\n", + "Below shows how to build the ServiceTask and prints the first XML result." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "K6CbS2QwltGi", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "2d9a0336-9680-4ca1-e2cd-68f6c5b4d1d3" + }, + "source": [ + "service = ServiceTask(url=\"http://export.arxiv.org/api/query\", method=\"get\", params={\"search_query\": None, \"max_results\": 25}, batch=False, extract=[\"feed\", \"entry\"])\n", + "workflow = Workflow([service])\n", + "\n", + "list(workflow([\"all:aliens\"]))[0][:1]" + ], + "execution_count": 74, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'arxiv:comment': {'#text': 'To appear in Astrophysical Journal',\n", + " '@xmlns:arxiv': 'http://arxiv.org/schemas/atom'},\n", + " 'arxiv:doi': {'#text': '10.3847/1538-4357/ac2369',\n", + " '@xmlns:arxiv': 'http://arxiv.org/schemas/atom'},\n", + " 'arxiv:primary_category': {'@scheme': 'http://arxiv.org/schemas/atom',\n", + " '@term': 'q-bio.OT',\n", + " '@xmlns:arxiv': 'http://arxiv.org/schemas/atom'},\n", + " 'author': [{'name': 'Robin Hanson'},\n", + " {'name': 'Daniel Martin'},\n", + " {'name': 'Calvin McCarter'},\n", + " {'name': 'Jonathan Paulson'}],\n", + " 'category': [{'@scheme': 'http://arxiv.org/schemas/atom',\n", + " '@term': 'q-bio.OT'},\n", + " {'@scheme': 'http://arxiv.org/schemas/atom', '@term': 'physics.pop-ph'}],\n", + " 'id': 'http://arxiv.org/abs/2102.01522v3',\n", + " 'link': [{'@href': 'http://dx.doi.org/10.3847/1538-4357/ac2369',\n", + " '@rel': 'related',\n", + " '@title': 'doi'},\n", + " {'@href': 'http://arxiv.org/abs/2102.01522v3',\n", + " '@rel': 'alternate',\n", + " '@type': 'text/html'},\n", + " {'@href': 'http://arxiv.org/pdf/2102.01522v3',\n", + " '@rel': 'related',\n", + " '@title': 'pdf',\n", + " '@type': 'application/pdf'}],\n", + " 'published': '2021-02-01T18:27:12Z',\n", + " 'summary': \"If life on Earth had to achieve n 'hard steps' to reach humanity's level,\\nthen the chance of this event rose as time to the n-th power. Integrating this\\nover habitable star formation and planet lifetime distributions predicts >99%\\nof advanced life appears after today, unless n<3 and max planet duration\\n<50Gyr. That is, we seem early. We offer this explanation: a deadline is set by\\n'loud' aliens who are born according to a hard steps power law, expand at a\\ncommon rate, change their volumes' appearances, and prevent advanced life like\\nus from appearing in their volumes. 'Quiet' aliens, in contrast, are much\\nharder to see. We fit this three-parameter model of loud aliens to data: 1)\\nbirth power from the number of hard steps seen in Earth history, 2) birth\\nconstant by assuming a inform distribution over our rank among loud alien birth\\ndates, and 3) expansion speed from our not seeing alien volumes in our sky. We\\nestimate that loud alien civilizations now control 40-50% of universe volume,\\neach will later control ~10^5 - 3x10^7 galaxies, and we could meet them in\\n~200Myr - 2Gyr. If loud aliens arise from quiet ones, a depressingly low\\ntransition chance (~10^-4) is required to expect that even one other quiet\\nalien civilization has ever been active in our galaxy. Which seems bad news for\\nSETI. But perhaps alien volume appearances are subtle, and their expansion\\nspeed lower, in which case we predict many long circular arcs to find in our\\nsky.\",\n", + " 'title': 'If Loud Aliens Explain Human Earliness, Quiet Aliens Are Also Rare',\n", + " 'updated': '2021-09-06T14:18:23Z'}]" + ] + }, + "metadata": {}, + "execution_count": 74 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "aWFwkxjQyscc" + }, + "source": [ + "Next we'll map the XML data using the tabular pipeline. `id` will be used as the id column and `title` as the text to index." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "DyIetJ7OmJjP", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "7e730b22-256d-4b1b-9064-2479e26d50c9" + }, + "source": [ + "from txtai.workflow import Task\n", + "\n", + "# Create tablular pipeline with new mapping\n", + "tabular = Tabular(\"id\", [\"title\"])\n", + "\n", + "# Recreate service applying the tabular pipeline to each result\n", + "service = ServiceTask(action=tabular, url=\"http://export.arxiv.org/api/query\", method=\"get\", params={\"search_query\": None, \"max_results\": 25}, batch=False, extract=[\"feed\", \"entry\"])\n", + "workflow = Workflow([service])\n", + "\n", + "list(workflow([\"all:aliens\"]))[:1]" + ], + "execution_count": 75, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[('http://arxiv.org/abs/2102.01522v3',\n", + " 'If Loud Aliens Explain Human Earliness, Quiet Aliens Are Also Rare',\n", + " None)]" + ] + }, + "metadata": {}, + "execution_count": 75 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "7pFnW7mCyycy" + }, + "source": [ + "As we did previously, let's build an Embeddings index and run a search query." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "NX2oR5dhm_99", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "399c4316-bce6-443b-c383-aedd7ff9a1ec" + }, + "source": [ + "# Embeddings with sentence-transformers backend\n", + "embeddings = Embeddings({\"method\": \"transformers\", \"path\": \"sentence-transformers/paraphrase-mpnet-base-v2\"})\n", + "\n", + "# Index Hacker News front page\n", + "data = list(workflow([\"all:aliens\"]))\n", + "embeddings.index(data)\n", + "\n", + "for uid, _ in embeddings.search(\"alien radio signals\"):\n", + " title = [text for url, text, _ in data if url == uid][0]\n", + " print(title, uid)" + ], + "execution_count": 76, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Calculating the probability of detecting radio signals from alien\n", + " civilizations http://arxiv.org/abs/0707.0011v2\n", + "Field Trial of Alien Wavelengths on GARR Optical Network http://arxiv.org/abs/1805.04278v1\n", + "Aliens on Earth. Are reports of close encounters correct? http://arxiv.org/abs/1203.6805v2\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8xknLo2ey0vZ" + }, + "source": [ + "# Build a workflow with no code!\n", + "\n", + "The next example shows how one of the same workflows above can be constructed via API configuration. This is a no-code way to build a txtai indexing workflow!" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "1eF5IJlzpNbw", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "4ff0bfdc-fb01-46a6-b61a-142a370dc273" + }, + "source": [ + "%%writefile workflow.yml\n", + "# Index settings\n", + "writable: true\n", + "embeddings:\n", + " path: sentence-transformers/nli-mpnet-base-v2\n", + "\n", + "# Tabular pipeline\n", + "tabular:\n", + " idcolumn: id\n", + " textcolumns: \n", + " - title\n", + "\n", + "# Workflow definitions\n", + "workflow:\n", + " index:\n", + " tasks:\n", + " - task: service\n", + " action: tabular\n", + " url: http://export.arxiv.org/api/query?max_results=25\n", + " method: get\n", + " params:\n", + " search_query: null\n", + " batch: false\n", + " extract: [feed, entry]\n", + " - action: upsert" + ], + "execution_count": 77, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Writing workflow.yml\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dxi5w3IezR7Q" + }, + "source": [ + "This workflow once again runs an arXiv query and indexes article titles. The workflow configures the same actions that were configured in Python previously. \n", + "\n", + "Let's start an API instance " + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "B1DQyB5ErIzr", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "7e170445-bc93-4660-b944-8b10b8b02d98" + }, + "source": [ + "!killall -9 uvicorn\n", + "!CONFIG=workflow.yml nohup uvicorn \"txtai.api:app\" &> api.log &\n", + "!sleep 30\n", + "!cat api.log" + ], + "execution_count": 78, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "INFO: Started server process [754]\n", + "2022-06-17 15:05:58,554 [INFO] serve: Started server process [754]\n", + "INFO: Waiting for application startup.\n", + "2022-06-17 15:05:58,554 [INFO] startup: Waiting for application startup.\n", + "INFO: Application startup complete.\n", + "2022-06-17 15:06:07,707 [INFO] startup: Application startup complete.\n", + "INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)\n", + "2022-06-17 15:06:07,707 [INFO] _log_started_message: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "45JaR7Nr0Zmg" + }, + "source": [ + "Next we'll execute the workflow. txtai has API bindings for [JavaScript](https://github.com/neuml/txtai.js), [Java](https://github.com/neuml/txtai.java), [Rust](https://github.com/neuml/txtai.rs) and [Golang](https://github.com/neuml/txtai.go). But to keep things simple, we'll just run the commands via cURL. " + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "xt_qL6eA0SrS", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "3ca70a31-cf48-42b4-949f-f01aac14505c" + }, + "source": [ + "# Execute workflow via API call\n", + "!curl -X POST \"http://localhost:8000/workflow\" -H \"accept: application/json\" -H \"Content-Type: application/json\" -d \"{\\\"name\\\":\\\"index\\\",\\\"elements\\\":[\\\"all:aliens\\\"]}\"" + ], + "execution_count": 79, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[[\"http://arxiv.org/abs/2102.01522v3\",\"If Loud Aliens Explain Human Earliness, Quiet Aliens Are Also Rare\",null],[\"http://arxiv.org/abs/cs/0306071v1\",\"AliEnFS - a Linux File System for the AliEn Grid Services\",null],[\"http://arxiv.org/abs/physics/0306103v1\",\"AliEn - EDG Interoperability in ALICE\",null],[\"http://arxiv.org/abs/2103.05559v1\",\"Oumuamua Is Not a Probe Sent to our Solar System by an Alien\\n Civilization\",null],[\"http://arxiv.org/abs/1403.3979v1\",\"Robust transitivity and density of periodic points of partially\\n hyperbolic diffeomorphisms\",null],[\"http://arxiv.org/abs/1712.09210v1\",\"Sampling alien species inside and outside protected areas: does it\\n matter?\",null],[\"http://arxiv.org/abs/cs/0306067v1\",\"The AliEn system, status and perspectives\",null],[\"http://arxiv.org/abs/0707.0011v2\",\"Calculating the probability of detecting radio signals from alien\\n civilizations\",null],[\"http://arxiv.org/abs/1805.04278v1\",\"Field Trial of Alien Wavelengths on GARR Optical Network\",null],[\"http://arxiv.org/abs/1808.00529v1\",\"Open Category Detection with PAC Guarantees\",null],[\"http://arxiv.org/abs/1206.3640v1\",\"The Study of Climate on Alien Worlds\",null],[\"http://arxiv.org/abs/1203.6805v2\",\"Aliens on Earth. Are reports of close encounters correct?\",null],[\"http://arxiv.org/abs/1604.05078v1\",\"The Imprecise Search for Habitability\",null],[\"http://arxiv.org/abs/1006.2613v1\",\"Resurgence, Stokes phenomenon and alien derivatives for level-one linear\\n differential systems\",null],[\"http://arxiv.org/abs/1307.0653v1\",\"General and alien solutions of a functional equation and of a functional\\n inequality\",null],[\"http://arxiv.org/abs/1705.03394v1\",\"That is not dead which can eternal lie: the aestivation hypothesis for\\n resolving Fermi's paradox\",null],[\"http://arxiv.org/abs/1701.02294v1\",\"Alien Calculus and non perturbative effects in Quantum Field Theory\",null],[\"http://arxiv.org/abs/1801.06180v1\",\"Are Alien Civilizations Technologically Advanced?\",null],[\"http://arxiv.org/abs/1902.05387v1\",\"Simultaneous x, y Pixel Estimation and Feature Extraction for Multiple\\n Small Objects in a Scene: A Description of the ALIEN Network\",null],[\"http://arxiv.org/abs/0711.4034v1\",\"The q-analogue of the wild fundamental group (II)\",null],[\"http://arxiv.org/abs/2111.07895v1\",\"Research Programs Arising from 'Oumuamua Considered as an Alien Craft\",null],[\"http://arxiv.org/abs/2112.15226v1\",\"Variations on the Resurgence of the Gamma Function\",null],[\"http://arxiv.org/abs/astro-ph/0501119v1\",\"Expanding advanced civilizations in the universe\",null],[\"http://arxiv.org/abs/cs/0306068v1\",\"AliEn Resource Brokers\",null],[\"http://arxiv.org/abs/hep-ph/9403231v2\",\"The Renormalization of Composite Operators in Yang-Mills Theories Using\\n General Covariant Gauge\",null]]" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_bwn4KBt1Cos" + }, + "source": [ + "The data is now indexed. Note that the index configuration has an `upsert` action. Each workflow call will insert new rows or update existing rows. This call could be scheduled with a system cron to execute periodically and build an index of arXiv article titles. \n", + "\n", + "Now that the index is ready, let's run a search." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "qbteIueJ1Fds", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "ad8ab84b-36a3-4346-d387-64321b933d25" + }, + "source": [ + "# Run a search\n", + "!curl -X GET \"http://localhost:8000/search?query=radio&limit=3\" -H \"accept: application/json\"" + ], + "execution_count": 80, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[{\"id\":\"http://arxiv.org/abs/0707.0011v2\",\"score\":0.40350058674812317},{\"id\":\"http://arxiv.org/abs/1805.04278v1\",\"score\":0.3406212031841278},{\"id\":\"http://arxiv.org/abs/1902.05387v1\",\"score\":0.22262491285800934}]" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "C5_Tt6EA3Cxb" + }, + "source": [ + "# Add a translation step to workflow\n", + "\n", + "Next we'll recreate the workflow, adding one additional step, translating the text into French before indexing. This workflow runs an arXiv query, translates the results and builds an semantic index of titles in French. " + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "j8rBVl17293q", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "215dc2cf-3a57-4deb-820d-e8ac03e3e041" + }, + "source": [ + "%%writefile workflow.yml\n", + "# Index settings\n", + "writable: true\n", + "embeddings:\n", + " path: sentence-transformers/nli-mpnet-base-v2\n", + "\n", + "# Tabular pipeline\n", + "tabular:\n", + " idcolumn: id\n", + " textcolumns: \n", + " - title\n", + "\n", + "# Translation pipeline\n", + "translation:\n", + "\n", + "# Workflow definitions\n", + "workflow:\n", + " index:\n", + " tasks:\n", + " - task: service\n", + " action: tabular\n", + " url: http://export.arxiv.org/api/query?max_results=25\n", + " method: get\n", + " params:\n", + " search_query: null\n", + " batch: false\n", + " extract: [feed, entry]\n", + " - action: translation\n", + " args: [fr]\n", + " - action: upsert" + ], + "execution_count": 81, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Overwriting workflow.yml\n" + ] + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "UQWvvgb2CwgG", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "3b0c99d7-b5f6-4c81-eb6e-ac9055eaaae4" + }, + "source": [ + "!killall -9 uvicorn\n", + "!CONFIG=workflow.yml nohup uvicorn \"txtai.api:app\" &> api.log &\n", + "!sleep 30\n", + "!cat api.log" + ], + "execution_count": 82, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "INFO: Started server process [775]\n", + "2022-06-17 15:06:29,397 [INFO] serve: Started server process [775]\n", + "INFO: Waiting for application startup.\n", + "2022-06-17 15:06:29,397 [INFO] startup: Waiting for application startup.\n", + "INFO: Application startup complete.\n", + "2022-06-17 15:06:40,198 [INFO] startup: Application startup complete.\n", + "INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)\n", + "2022-06-17 15:06:40,199 [INFO] _log_started_message: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "v1y4heYx679i" + }, + "source": [ + "Same as before, we'll run the index workflow and a search" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "npW3rjCw6_nx", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "fba0bd30-2340-4197-d68c-4fc925ce80b8" + }, + "source": [ + "# Execute workflow via API call\n", + "!curl -s -X POST \"http://localhost:8000/workflow\" -H \"accept: application/json\" -H \"Content-Type: application/json\" -d \"{\\\"name\\\":\\\"index\\\",\\\"elements\\\":[\\\"all:aliens\\\"]}\" > /dev/null\n", + "\n", + "# Run a search\n", + "!curl -X GET \"http://localhost:8000/search?query=radio&limit=3\" -H \"accept: application/json\"" + ], + "execution_count": 83, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[{\"id\":\"http://arxiv.org/abs/0707.0011v2\",\"score\":0.532800555229187},{\"id\":\"http://arxiv.org/abs/0711.4034v1\",\"score\":0.24413327872753143},{\"id\":\"http://arxiv.org/abs/2102.01522v3\",\"score\":0.22881504893302917}]" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "OsQO5DG9zBQF" + }, + "source": [ + "# Run YAML workflow in Python\n", + "\n", + "Workflow YAML files can also be directly executed in Python. In this case, all input data is passed locally in Python and not through network interfaces. The following section shows how to do this!" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "iXmkLDTlzPT3", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "6b795963-2954-42e2-c42d-39c3f88c9ac3" + }, + "source": [ + "import yaml\n", + "\n", + "from txtai.app import Application\n", + "\n", + "with open(\"workflow.yml\") as config:\n", + " workflow = yaml.safe_load(config)\n", + "\n", + "app = Application(workflow)\n", + "\n", + "# Run the workflow\n", + "data = list(app.workflow(\"index\", [\"all:aliens\"]))\n", + "\n", + "# Run a search\n", + "for result in app.search(\"radio\", None):\n", + " text = [row[1] for row in data if row[0] == result[\"id\"]][0]\n", + " print(result[\"id\"], result[\"score\"], text)" + ], + "execution_count": 84, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "http://arxiv.org/abs/0707.0011v2 0.532800555229187 Calcul de la probabilité de détection des signaux radio de l'étrangercivilisations\n", + "http://arxiv.org/abs/0711.4034v1 0.24413327872753143 Le q-analogue du groupe fondamental sauvage (II)\n", + "http://arxiv.org/abs/2102.01522v3 0.22881504893302917 Si les étrangers louds expliquent le début de l'humanité, les étrangers tranquilles sont aussi rares\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EoQFEi_61P9O" + }, + "source": [ + "# Wrapping up\n", + "\n", + "This notework demonstrated how to transform, index and search tabular data from a variety of sources. txtai offers maximum flexibility in building composable workflows to maximize the number of ways data can be indexed for semantic search. " + ] + } + ] +} diff --git a/examples/23_Tensor_workflows.ipynb b/examples/23_Tensor_workflows.ipynb new file mode 100644 index 0000000..6714c6b --- /dev/null +++ b/examples/23_Tensor_workflows.ipynb @@ -0,0 +1,503 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "accelerator": "GPU", + "colab": { + "name": "23 - Tensor workflows", + "provenance": [], + "collapsed_sections": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "4Pjmz-RORV8E" + }, + "source": [ + "# Tensor workflows\n", + "\n", + "Many of the examples and use cases for txtai focus on transforming text. Makes sense as txt is even in the name! But that doesn't mean txtai only works with text.\n", + "\n", + "This notebook will cover examples of how to efficiently process tensors using txtai workflows." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dk31rbYjSTYm" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies. We will install the api, pipeline and workflow optional extras packages, along with the datasets package. " + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "XMQuuun2R06J" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[api,pipeline,workflow] datasets" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "NSYrP0hjtR_E" + }, + "source": [ + "# Transform large tensor arrays\n", + "\n", + "The first section attempts to apply a simple transform to a very large memory-mapped array (2,000,000 x 1024)." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "BoPJIKWoTibk", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 220 + }, + "outputId": "143a6e4d-fe56-4353-e8ee-595ddfc12249" + }, + "source": [ + "import numpy as np\n", + "import torch\n", + "\n", + "# Generate large memory-mapped array\n", + "rows, cols = 2000000, 1024\n", + "data = np.memmap(\"data.npy\", dtype=np.float32, mode=\"w+\", shape=(rows, cols))\n", + "del data\n", + "\n", + "# Open memory-mapped array\n", + "data = np.memmap(\"data.npy\", dtype=np.float32, shape=(rows, cols))\n", + "\n", + "# Create tensor\n", + "tensor = torch.from_numpy(data).to(\"cuda:0\")\n", + "\n", + "# Apply tanh transform to tensor\n", + "torch.tanh(tensor).shape" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "error", + "ename": "RuntimeError", + "evalue": "ignored", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mRuntimeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0;31m# Apply tanh transform to tensor\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 16\u001b[0;31m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtanh\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtensor\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshape\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mRuntimeError\u001b[0m: CUDA out of memory. Tried to allocate 7.63 GiB (GPU 0; 11.17 GiB total capacity; 7.63 GiB already allocated; 3.04 GiB free; 7.63 GiB reserved in total by PyTorch) If reserved memory is >> allocated memory try setting max_split_size_mb to avoid fragmentation. See documentation for Memory Management and PYTORCH_CUDA_ALLOC_CONF" + ] + } + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "O8mKzPP01d_m", + "outputId": "929226a8-6948-4d17-ab70-025da2081abd" + }, + "source": [ + "!ls -l --block-size=MB data.npy" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "-rw-r--r-- 1 root root 8192MB Dec 6 23:24 data.npy\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vuObmAJ9FaJe" + }, + "source": [ + "Not surprisingly this runs out of CUDA memory. The array needs `2,000,000 * 1024 * 4 = 8GB` which exceeds the amount of GPU memory available.\n", + "\n", + "One of the great things about NumPy and PyTorch arrays is that they can be sliced without having to copy data. Additionally, PyTorch has methods to work directly on NumPy arrays without copying data, in other words both NumPy arrays and PyTorch arrays can share the same memory. This opens the door to efficient processing of tensor data in place. \n", + "\n", + "Let's try applying a simple tanh transform in batches over the array." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ciD6unQYD-bJ", + "outputId": "d3b6a0c5-aea5-451d-d3e3-60d04ef33a9e" + }, + "source": [ + "def process(x):\n", + " print(x.shape)\n", + " return torch.tanh(torch.from_numpy(x).to(\"cuda:0\")).cpu().numpy()\n", + "\n", + "# Split into 250,000 rows per call\n", + "batch = 250000\n", + "count = 0\n", + "for x in range(0, len(data), batch):\n", + " for row in process(data[x : x + batch]):\n", + " count += 1\n", + "\n", + "print(count)" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(250000, 1024)\n", + "(250000, 1024)\n", + "(250000, 1024)\n", + "(250000, 1024)\n", + "(250000, 1024)\n", + "(250000, 1024)\n", + "(250000, 1024)\n", + "(250000, 1024)\n", + "2000000\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "uZBzEMsRHpsi" + }, + "source": [ + "Iterating over the data array and selecting slices to operate on allows the transform to complete successfully! Each `torch.from_numpy` call is building a view of a portion the existing large NumPy data array. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Oe7X17vbHJRV" + }, + "source": [ + "# Enter workflows\n", + "\n", + "The next section takes the same array and shows how workflows can apply transformations to tensors. " + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ymqr92kW9hxd", + "outputId": "e4a4c7d1-be54-46c2-bc7c-dd849adfeb7e" + }, + "source": [ + "from txtai.workflow import Task, Workflow\n", + "\n", + "# Create workflow with a single task calling process for each batch\n", + "task = Task(process)\n", + "workflow = Workflow([task], batch)\n", + "\n", + "# Run workflow\n", + "count = 0\n", + "for row in workflow(data):\n", + " count += 1\n", + "\n", + "print(count)" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(250000, 1024)\n", + "(250000, 1024)\n", + "(250000, 1024)\n", + "(250000, 1024)\n", + "(250000, 1024)\n", + "(250000, 1024)\n", + "(250000, 1024)\n", + "(250000, 1024)\n", + "2000000\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "B9qC8qUbHjfk" + }, + "source": [ + "Workflows process the data in the same fashion as the code in the previous section. On top of that, workflows can handle text, images, video, audio, document, tensors and more. Workflow graphs can also be connected together to handle complex use cases." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "wCRD9ERoJvsG" + }, + "source": [ + "# Workflows with PyTorch models\n", + "\n", + "The next example applies a PyTorch model to the same data. The model applies a series of transforms and outputs a single float per row." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "N9UTfSTTIDaO", + "outputId": "000c7e13-a249-43d0-f419-28ddd62e8ba1" + }, + "source": [ + "from torch import nn\n", + "\n", + "class Model(nn.Module):\n", + " def __init__(self):\n", + " super().__init__()\n", + "\n", + " self.gelu = nn.ReLU()\n", + " self.linear1 = nn.Linear(1024, 512)\n", + " self.dropout = nn.Dropout(0.5)\n", + " self.norm = nn.LayerNorm(512)\n", + " self.linear2 = nn.Linear(512, 1)\n", + "\n", + " def forward(self, inputs):\n", + " outputs = self.gelu(inputs)\n", + " outputs = self.linear1(outputs)\n", + " outputs = self.dropout(outputs)\n", + " outputs = self.norm(outputs)\n", + " outputs = self.linear2(outputs)\n", + "\n", + " return outputs\n", + "\n", + "model = Model().to(\"cuda:0\")\n", + "\n", + "def process(x):\n", + " with torch.no_grad():\n", + " outputs = model(torch.from_numpy(x).to(\"cuda:0\")).cpu().numpy()\n", + " print(outputs.shape)\n", + " return outputs\n", + "\n", + "# Create workflow with a single task calling model for each batch\n", + "task = Task(process)\n", + "workflow = Workflow([task], batch)\n", + "\n", + "# Run workflow\n", + "count = 0\n", + "for row in workflow(data):\n", + " count += 1\n", + "\n", + "print(count)" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(250000, 1)\n", + "(250000, 1)\n", + "(250000, 1)\n", + "(250000, 1)\n", + "(250000, 1)\n", + "(250000, 1)\n", + "(250000, 1)\n", + "(250000, 1)\n", + "2000000\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Q1ivX4eBuU8T" + }, + "source": [ + "Once again the data can be processed in batches using workflows, even with a more complex model. Let's try a more interesting example." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "KoSB1mKzUnb0" + }, + "source": [ + "# Workflows in parallel\n", + "\n", + "Workflows consist of a series of tasks. Each task can output one to many outputs per input element. Multi-output tasks have options available to [merge the data](https://neuml.github.io/txtai/workflow/task/#multi-action-task-merges) for downstream tasks.\n", + "\n", + "The following example builds a workflow with a task having three separate actions. Each action takes text as an input an applies a sentiment classifier. This is followed by a task that merges the three outputs for each row using a mean transform. Essentially, this workflow builds a weighted sentiment classifier using the outputs of three models. " + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "JlCdVgo_LXOl" + }, + "source": [ + "import time\n", + "\n", + "from datasets import load_dataset\n", + "from transformers import AutoTokenizer, AutoModelForSequenceClassification\n", + "\n", + "class Tokens:\n", + " def __init__(self, texts):\n", + " tokenizer = AutoTokenizer.from_pretrained(\"distilbert-base-uncased-finetuned-sst-2-english\")\n", + " tokens = tokenizer(texts, padding=True, return_tensors=\"pt\").to(\"cuda:0\")\n", + "\n", + " self.inputs, self.attention = tokens[\"input_ids\"], tokens[\"attention_mask\"]\n", + "\n", + " def __len__(self):\n", + " return len(self.inputs)\n", + "\n", + " def __getitem__(self, value):\n", + " return (self.inputs[value], self.attention[value])\n", + "\n", + "class Classify:\n", + " def __init__(self, model):\n", + " self.model = model\n", + "\n", + " def __call__(self, tokens):\n", + " with torch.no_grad():\n", + " inputs, attention = tokens\n", + " outputs = self.model(input_ids=inputs, attention_mask=attention)\n", + " outputs = outputs[\"logits\"]\n", + "\n", + " return outputs\n", + "\n", + "# Load reviews from the rotten tomatoes dataset\n", + "ds = load_dataset(\"rotten_tomatoes\")\n", + "texts = ds[\"train\"][\"text\"]\n", + "\n", + "tokens = Tokens(texts)\n", + "\n", + "model1 = AutoModelForSequenceClassification.from_pretrained(\"M-FAC/bert-tiny-finetuned-sst2\")\n", + "model1 = model1.to(\"cuda:0\")\n", + "\n", + "model2 = AutoModelForSequenceClassification.from_pretrained(\"howey/electra-base-sst2\")\n", + "model2 = model2.to(\"cuda:0\")\n", + "\n", + "model3 = AutoModelForSequenceClassification.from_pretrained(\"philschmid/MiniLM-L6-H384-uncased-sst2\")\n", + "model3 = model3.to(\"cuda:0\")\n", + "\n", + "task1 = Task([Classify(model1), Classify(model2), Classify(model3)])\n", + "task2 = Task([lambda x: torch.sigmoid(x).mean(axis=1).cpu().numpy()])\n", + "\n", + "workflow = Workflow([task1, task2], 250)\n", + "\n", + "start = time.time()\n", + "for x in workflow(tokens):\n", + " pass\n", + "\n", + "print(f\"Took {time.time() - start} seconds\")" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "Using custom data configuration default\n", + "Reusing dataset rotten_tomatoes_movie_review (/root/.cache/huggingface/datasets/rotten_tomatoes_movie_review/default/1.0.0/40d411e45a6ce3484deed7cc15b82a53dad9a72aafd9f86f8f227134bec5ca46)\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Took 84.73194456100464 seconds\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "eBQzLrtAVUtB" + }, + "source": [ + "Note that while the task actions are parallel, that doesn't necessarily mean the operations are concurrent. In the case above, the actions are are executed sequentially.\n", + "\n", + "Workflows have an additional option to run task actions concurrently. The two supported modes are \"thread\" and \"process\". I/O bound actions will do better with multithreading and CPU bound actions will do better with multiprocessing. More can be read in the [txtai documentation](https://neuml.github.io/txtai/workflow/task/#multi-action-task-concurrency). " + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "AB0onoOlVT-e", + "outputId": "a072d8a6-3b3b-4066-8881-8af9a8b96608" + }, + "source": [ + "task1 = Task([Classify(model1), Classify(model2), Classify(model3)], concurrency=\"thread\")\n", + "task2 = Task([lambda x: torch.sigmoid(x).mean(axis=1).cpu().numpy()])\n", + "\n", + "workflow = Workflow([task1, task2], 250)\n", + "\n", + "start = time.time()\n", + "for x in workflow(tokens):\n", + " pass\n", + "\n", + "print(f\"Took {time.time() - start} seconds\")" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Took 85.21102929115295 seconds\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5s2KexhG_udx" + }, + "source": [ + "In this case, concurrency doesn't improve performance. While the [GIL](https://wiki.python.org/moin/GlobalInterpreterLock) is a factor, a bigger factor is that the GPU is already fully loaded. This method would be more beneficial if the system had a second GPU or the primary GPU had idle cycles. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EoQFEi_61P9O" + }, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook introduced a number of different ways to work with large-scale tensor data and process it efficiently. This notebook purposely didn't cover embeddings and pipelines to demonstrate how workflows can stand on their own. In addition to workflows, this notebook covered efficient methods to work with large tensor arrays in PyTorch and NumPy." + ] + } + ] +} diff --git a/examples/24_Whats_new_in_txtai_4_0.ipynb b/examples/24_Whats_new_in_txtai_4_0.ipynb new file mode 100644 index 0000000..c9e39bc --- /dev/null +++ b/examples/24_Whats_new_in_txtai_4_0.ipynb @@ -0,0 +1,572 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "kernelspec": { + "name": "python3", + "display_name": "Python 3", + "language": "python" + }, + "language_info": { + "name": "python", + "version": "3.7.6", + "mimetype": "text/x-python", + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "pygments_lexer": "ipython3", + "nbconvert_exporter": "python", + "file_extension": ".py" + }, + "colab": { + "name": "24 - Whats new in txtai 4.0", + "provenance": [], + "collapsed_sections": [] + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "POWZoSJR6XzK" + }, + "source": [ + "# 💡 What's new in txtai 4.0\n", + "\n", + "txtai 4.0 brings a number of major feature enhancements, most importantly the capability to store full document content and text right in txtai. This notebook will cover all the changes with examples." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qa_PPKVX6XzN" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "metadata": { + "_uuid": "8f2839f25d086af736a60e9eeb907d3b93b6e0e5", + "_cell_guid": "b1076dfc-b9ad-4769-8c92-a6c4dae69d19", + "trusted": true, + "_kg_hide-output": true, + "id": "24q-1n5i6XzQ" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai" + ], + "execution_count": 23, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Content storage\n", + "Up to now with txtai, once text was vectorized, it was no longer possible to trace back to the input text. Only document ids and vectors were stored. Results consisted of ids and scores. It was the responsibility of the developer to resolve matches with an external data store. \n", + "\n", + "txtai 4.0 brings a major paradigm shift. Content can now be stored alongside embeddings vectors. This opens up a number of exciting possibilities with txtai!\n", + "\n", + "Let's see with the classic txtai example below." + ], + "metadata": { + "id": "0p3WCDniUths" + } + }, + { + "cell_type": "code", + "metadata": { + "_uuid": "d629ff2d2480ee46fbb7e2d37f6b5fab8052498a", + "_cell_guid": "79c7e3d0-c299-4dcb-8224-4455121ee9b0", + "trusted": true, + "id": "2j_CFGDR6Xzp", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "641a6b9e-dfe4-4c77-94d2-00f87c84b8f5" + }, + "source": [ + "from txtai.embeddings import Embeddings\n", + "\n", + "data = [\"US tops 5 million confirmed virus cases\",\n", + " \"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\",\n", + " \"Beijing mobilises invasion craft along coast as Taiwan tensions escalate\",\n", + " \"The National Park Service warns against sacrificing slower friends in a bear attack\",\n", + " \"Maine man wins $1M from $25 lottery ticket\",\n", + " \"Make huge profits without work, earn up to $100,000 a day\"]\n", + "\n", + "# Create embeddings index with content enabled. The default behavior is to only store indexed vectors.\n", + "embeddings = Embeddings({\"path\": \"sentence-transformers/nli-mpnet-base-v2\", \"content\": True, \"objects\": True})\n", + "\n", + "# Create an index for the list of text\n", + "embeddings.index([(uid, text, None) for uid, text in enumerate(data)])\n", + "\n", + "print(\"%-20s %s\" % (\"Query\", \"Best Match\"))\n", + "print(\"-\" * 50)\n", + "\n", + "# Run an embeddings search for each query\n", + "for query in (\"feel good story\", \"climate change\", \"public health story\", \"war\", \"wildlife\", \"asia\", \"lucky\", \"dishonest junk\"):\n", + " # Extract text field from result\n", + " text = embeddings.search(query, 1)[0][\"text\"]\n", + "\n", + " # Print text\n", + " print(\"%-20s %s\" % (query, text))" + ], + "execution_count": 24, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Query Best Match\n", + "--------------------------------------------------\n", + "feel good story Maine man wins $1M from $25 lottery ticket\n", + "climate change Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\n", + "public health story US tops 5 million confirmed virus cases\n", + "war Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "wildlife The National Park Service warns against sacrificing slower friends in a bear attack\n", + "asia Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "lucky Maine man wins $1M from $25 lottery ticket\n", + "dishonest junk Make huge profits without work, earn up to $100,000 a day\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The only change above is setting the *content* flag to True. This enables storing text and metadata content (if provided) alongside the index. Note how the text is pulled right from the query result!" + ], + "metadata": { + "id": "hHGvhZm-ZTzL" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Query with SQL\n", + "\n", + "When content is enabled, the entire dictionary will be stored and can be queried. In addition to similarity queries, txtai accepts SQL queries. This enables combined queries using both a similarity index and content stored in a database backend." + ], + "metadata": { + "id": "BYWUFBUGyKyY" + } + }, + { + "cell_type": "code", + "source": [ + "# Create an index for the list of text\n", + "embeddings.index([(uid, {\"text\": text, \"length\": len(text)}, None) for uid, text in enumerate(data)])\n", + "\n", + "# Filter by score\n", + "print(embeddings.search(\"select text, score from txtai where similar('hiking danger') and score >= 0.15\"))\n", + "\n", + "# Filter by metadata field 'length'\n", + "print(embeddings.search(\"select text, length, score from txtai where similar('feel good story') and score >= 0.05 and length >= 40\"))\n", + "\n", + "# Run aggregate queries\n", + "print(embeddings.search(\"select count(*), min(length), max(length), sum(length) from txtai\"))\n", + "print()\n", + "for x in embeddings.search(\"select count(*), min(length), max(length), sum(length), text, score from txtai group by text limit 10\"):\n", + " print(x)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "aPH-dnV2ZuL1", + "outputId": "f5e45e94-15c1-4635-8050-66c17c572dbb" + }, + "execution_count": 25, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[{'text': 'The National Park Service warns against sacrificing slower friends in a bear attack', 'score': 0.3151373267173767}]\n", + "[{'text': 'Maine man wins $1M from $25 lottery ticket', 'length': 42, 'score': 0.08329004049301147}]\n", + "[{'count(*)': 6, 'min(length)': 39, 'max(length)': 94, 'sum(length)': 387}]\n", + "\n", + "{'count(*)': 1, 'min(length)': 72, 'max(length)': 72, 'sum(length)': 72, 'text': 'Beijing mobilises invasion craft along coast as Taiwan tensions escalate', 'score': None}\n", + "{'count(*)': 1, 'min(length)': 94, 'max(length)': 94, 'sum(length)': 94, 'text': \"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\", 'score': None}\n", + "{'count(*)': 1, 'min(length)': 42, 'max(length)': 42, 'sum(length)': 42, 'text': 'Maine man wins $1M from $25 lottery ticket', 'score': None}\n", + "{'count(*)': 1, 'min(length)': 57, 'max(length)': 57, 'sum(length)': 57, 'text': 'Make huge profits without work, earn up to $100,000 a day', 'score': None}\n", + "{'count(*)': 1, 'min(length)': 83, 'max(length)': 83, 'sum(length)': 83, 'text': 'The National Park Service warns against sacrificing slower friends in a bear attack', 'score': None}\n", + "{'count(*)': 1, 'min(length)': 39, 'max(length)': 39, 'sum(length)': 39, 'text': 'US tops 5 million confirmed virus cases', 'score': None}\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "This example above adds a simple additional field, text length. Starting with txtai 4.0, the index method accepts dictionaries in the data field. \n", + "\n", + "Note the second query is filtering on the metadata field length along with a similarity query clause. This gives a great blend of similarity search with traditional filtering to help identify the best results." + ], + "metadata": { + "id": "oH4Yd9BOlo5u" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Object storage\n", + "\n", + "In addition to metadata, binary content can also be associated with documents. The example below downloads an image, upserts it along with associated text into the embeddings index." + ], + "metadata": { + "id": "lGmiYXyqyjtQ" + } + }, + { + "cell_type": "code", + "source": [ + "import urllib\n", + "\n", + "from IPython.display import Image\n", + "\n", + "# Get an image\n", + "request = urllib.request.urlopen(\"https://raw.githubusercontent.com/neuml/txtai/master/demo.gif\")\n", + "\n", + "# Upsert new record having both text and an object\n", + "embeddings.upsert([(\"txtai\", {\"text\": \"txtai executes machine-learning workflows to transform data and build AI-powered semantic search applications.\", \"object\": request.read()}, None)])\n", + "\n", + "# Query txtai for the most similar result to \"machine learning\" and get associated object\n", + "result = embeddings.search(\"select object from txtai where similar('machine learning') limit 1\")[0][\"object\"]\n", + "\n", + "# Display image\n", + "Image(result.getvalue(), width=600)" + ], + "metadata": { + "id": "Ef4-Gd8ZtzUF", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 420 + }, + "outputId": "a633fb4d-ff88-4a8f-ca15-673e9f8dec30" + }, + "execution_count": 26, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "image/png": "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9KdADapQh0rUohr1qEhNqlKXytSmOvWpUI2qVKdK1apa9apYzapWt8rVrnr1q2ANq1jHStaymvWsaE2rWtfK1ra69a1wjatc50rXutr1rnjNq173yte++vWvgA2sYAdL2MIa9rCITaxiF8vYxjr2sZCNrGQnS9nKWvaymM2sZjfL2c569rOgDa1oR0va0pr2tKhNrWpXy9rWuva1sI2tbGdL29ra9ra4za1ud8vb3vr2t8ANrnCHS9ziGve4yE2ucpfL3OY697nQja50p0vd6lr3utjNrna3y93ueve74A2veMdL3vKa97zoTf+vetfL3va6973wja9850vf+tr3vvjNr373y9/++ve/AA6wgAdM4AIb+MAITrCCF8zgBjv4wRCOsIQnTOEKW/jCGM6whjfM4Q57+MMgDrGIR0ziEpv4xChOsYpXzOIWu/jFMI6xjGdM4xrb+MY4zrGOd8zjHvv4x0AOspCHTOQiG/nISE6ykpfM5CY7+clQjrKUp0zlKlv5yljOspa3zOUue/nLYA6zmMdM5jKb+cxoTrOa18zmNrv5zXCOs5znTOc62/nOeM6znvfM5z77+c+ADrSgB03oQhv60IhOtKIXzehGO/rRkI60pCdN6Upb+tKYzrSmN83pTnv606AOtaj/R03qUpv61KhOtapXzepWu/rVsI61rGdN61rb+ta4zrWud83rXvv618AOtrCHTexiG/vYyE62spfN7GY7+9nQjra0p03talv72tjOtra3ze1ue/vb4A63uMdN7nKb+9zoTre6183udrv73fCOt7znTe962/ve+M63vvfN7377+98AD7jAB07wghv84AhPuMIXzvCGO/zhEI+4xCdO8Ypb/OIYz7jGN87xjnv84yAPuchHTvKSm/zkKE+5ylfO8pa7/OUwj7nMZ07zmtv85jjPuc53zvOe+/znQA+60IdO9KIb/ehIT7rSl870pjv96VCPutSnTvWqW/3qWM+61rfO+/Wue/3rYA+72MdO9rKb/exoT7va1872trv97XCPu9znTve62/3ueM+73vfO9777/e+AD7zgB0/4whv+8IhPvOIXz/jGO/7xkI+85CdP+cpb/vKYz7zmN8/5znv+86APvehHT/rSm/70qE+96lfP+ta7/vWwj73sZ0/72tv+9rjPve53z/ve+/73wA++8IdP/OIb//jIT77y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\n" + }, + "metadata": { + "image/png": { + "width": 600 + } + }, + "execution_count": 26 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Reindex\n", + "\n", + "Now that content is stored, embedding indexes can be rebuilt with different configuration settings." + ], + "metadata": { + "id": "PR3TzkzrjJ6x" + } + }, + { + "cell_type": "code", + "source": [ + "# Print index info before (info() is also new!)\n", + "embeddings.info()\n", + "\n", + "# Reindex\n", + "embeddings.reindex({\"path\": \"sentence-transformers/paraphrase-MiniLM-L3-v2\"})\n", + "\n", + "print(\"------\")\n", + "\n", + "# Print index info after\n", + "embeddings.info()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "fAz7Jv6SjR6a", + "outputId": "d9ffddea-bb78-4664-b0a4-ea8eccdba8f9" + }, + "execution_count": 27, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "{\n", + " \"backend\": \"faiss\",\n", + " \"build\": {\n", + " \"create\": \"2022-05-10T20:32:24Z\",\n", + " \"python\": \"3.7.13\",\n", + " \"settings\": {\n", + " \"components\": \"IDMap,Flat\"\n", + " },\n", + " \"system\": \"Linux (x86_64)\",\n", + " \"txtai\": \"4.5.0\"\n", + " },\n", + " \"content\": \"sqlite\",\n", + " \"dimensions\": 768,\n", + " \"objects\": true,\n", + " \"offset\": 7,\n", + " \"path\": \"sentence-transformers/nli-mpnet-base-v2\",\n", + " \"update\": \"2022-05-10T20:32:25Z\"\n", + "}\n", + "------\n", + "{\n", + " \"backend\": \"faiss\",\n", + " \"build\": {\n", + " \"create\": \"2022-05-10T20:32:26Z\",\n", + " \"python\": \"3.7.13\",\n", + " \"settings\": {\n", + " \"components\": \"IDMap,Flat\"\n", + " },\n", + " \"system\": \"Linux (x86_64)\",\n", + " \"txtai\": \"4.5.0\"\n", + " },\n", + " \"content\": \"sqlite\",\n", + " \"dimensions\": 384,\n", + " \"objects\": true,\n", + " \"offset\": 7,\n", + " \"path\": \"sentence-transformers/paraphrase-MiniLM-L3-v2\",\n", + " \"update\": \"2022-05-10T20:32:26Z\"\n", + "}\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Index compression\n", + "\n", + "txtai normally saves index files to a directory. With 4.0, it is now possible to save compressed indexes. Indexes can be compressed to tar.gz, tar.bz2, tar.xz and zip. txtai can load compressed files and treats them as directories.\n", + "\n", + "Compressed indexes can be used as a backup strategy and/or as the primary storage mechanism." + ], + "metadata": { + "id": "s9aLt2zF2ZW2" + } + }, + { + "cell_type": "code", + "source": [ + "# Save index as tar.xz\n", + "embeddings.save(\"index.tar.xz\")\n", + "!tar -tvJf index.tar.xz\n", + "!echo\n", + "!xz -l index.tar.xz\n", + "!echo\n", + "\n", + "# Reload index\n", + "embeddings.load(\"index.tar.xz\")\n", + "\n", + "# Test search\n", + "embeddings.search(\"lucky guy\", 1)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "0oOC8ToG1pyn", + "outputId": "7e6dd30f-a1cd-47f1-d298-abacf7b69835" + }, + "execution_count": 28, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "drwx------ root/root 0 2022-05-10 20:32 ./\n", + "-rw-r--r-- root/root 301 2022-05-10 20:32 ./config\n", + "-rw-r--r-- root/root 77824 2022-05-10 20:32 ./documents\n", + "-rw-r--r-- root/root 10898 2022-05-10 20:32 ./embeddings\n", + "\n", + "Strms Blocks Compressed Uncompressed Ratio Check Filename\n", + " 1 1 45.8 KiB 100.0 KiB 0.458 CRC64 index.tar.xz\n", + "\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '4',\n", + " 'score': 0.3691234290599823,\n", + " 'text': 'Maine man wins $1M from $25 lottery ticket'}]" + ] + }, + "metadata": {}, + "execution_count": 28 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Note the compression ratio. Depending on the type of data stored, this could be quite substantial (text will compress much better than objects). " + ], + "metadata": { + "id": "x5iSRKZVYfok" + } + }, + { + "cell_type": "markdown", + "source": [ + "# External vector models\n", + "\n", + "txtai supports generating vectors with [Hugging Face Transformers](https://github.com/huggingface/transformers), [PyTorch](https://github.com/pytorch/pytorch), [ONNX](https://github.com/microsoft/onnxruntime) and [Word Vector](https://github.com/neuml/staticvectors) models.\n", + "\n", + "This release adds support for pre-computed vectors using external models. External models may be an API, custom library and/or another way to vectorize data. This adds flexibility given the high computation cost in building embeddings vectors. Embeddings generation could be outsourced or consolidated to a group of servers with GPUs, leaving index servers to run on lower resourced machines. \n", + "\n", + "The example below uses the [Hugging Face Inference API](https://huggingface.co/inference-api) to build embeddings vectors. We'll load the [exact model as in the first example](#scrollTo=0p3WCDniUths) and produce the same results." + ], + "metadata": { + "id": "oAISaBYVg60i" + } + }, + { + "cell_type": "code", + "source": [ + "import numpy as np\n", + "import requests\n", + "\n", + "def transform(inputs):\n", + " response = requests.post(\"https://api-inference.huggingface.co/pipeline/feature-extraction/sentence-transformers/nli-mpnet-base-v2\",\n", + " json={\"inputs\": inputs})\n", + "\n", + " return np.array(response.json(), dtype=np.float32)\n", + "\n", + "# Index data using vectors from Inference API\n", + "embeddings = Embeddings({\"method\": \"external\", \"transform\": transform, \"content\": True})\n", + "embeddings.index([(uid, text, None) for uid, text in enumerate(data)])\n", + "\n", + "print(\"%-20s %s\" % (\"Query\", \"Best Match\"))\n", + "print(\"-\" * 50)\n", + "\n", + "# Run an embeddings search for each query\n", + "for query in (\"feel good story\", \"climate change\", \"public health story\", \"war\", \"wildlife\", \"asia\", \"lucky\", \"dishonest junk\"):\n", + " # Extract text field from result\n", + " text = embeddings.search(f\"select id, text, score from txtai where similar('{query}')\", 1)[0][\"text\"]\n", + "\n", + " # Print text\n", + " print(\"%-20s %s\" % (query, text))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "d6dbB3nMk29O", + "outputId": "fab5be50-4714-4935-8d1f-0955960d1cec" + }, + "execution_count": 29, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Query Best Match\n", + "--------------------------------------------------\n", + "feel good story Maine man wins $1M from $25 lottery ticket\n", + "climate change Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\n", + "public health story US tops 5 million confirmed virus cases\n", + "war Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "wildlife The National Park Service warns against sacrificing slower friends in a bear attack\n", + "asia Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "lucky Maine man wins $1M from $25 lottery ticket\n", + "dishonest junk Make huge profits without work, earn up to $100,000 a day\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The next example uses [spaCy](https://github.com/explosion/spaCy) to build vectors and then loads them into txtai. The vectors with this model are much faster to generate at the expense of accuracy." + ], + "metadata": { + "id": "_kq_fbjd4g74" + } + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "!pip install spacy --upgrade\n", + "!python -m spacy download en_core_web_md" + ], + "metadata": { + "id": "IIGlel3ShyGP" + }, + "execution_count": 30, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "import spacy\n", + "\n", + "# Load spacy\n", + "nlp = spacy.load(\"en_core_web_md\")\n", + "\n", + "def transform(inputs):\n", + " return [result.vector for result in nlp.pipe(inputs)]\n", + "\n", + "# Index data with spacy pipeline\n", + "embeddings = Embeddings({\"method\": \"external\", \"transform\": transform, \"content\": True})\n", + "embeddings.index([(uid, text, None) for uid, text in enumerate(data)])\n", + "\n", + "# Run search\n", + "print(embeddings.search(\"select id, text, score from txtai where similar('nature')\", 1))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "nikKybxEkhjC", + "outputId": "15dcd1cf-7068-4365-e6fe-8af29ff24b9d" + }, + "execution_count": 31, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[{'id': '3', 'text': 'The National Park Service warns against sacrificing slower friends in a bear attack', 'score': 0.44850602746009827}]\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "aDIF3tYt6X0O" + }, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook gave a quick overview of txtai. txtai 4.0 is now out!\n", + "\n", + "See the following links for more information.\n", + "\n", + "- [4.0 Release on GitHub](https://github.com/neuml/txtai/releases/tag/v4.0.0)\n", + "- [Documentation site](https://neuml.github.io/txtai)\n", + "- [Full list of examples](https://neuml.github.io/txtai/examples/)" + ] + } + ] +} diff --git a/examples/25_Generate_image_captions_and_detect_objects.ipynb b/examples/25_Generate_image_captions_and_detect_objects.ipynb new file mode 100644 index 0000000..4e17cde --- /dev/null +++ b/examples/25_Generate_image_captions_and_detect_objects.ipynb @@ -0,0 +1,1167 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "4Pjmz-RORV8E" + }, + "source": [ + "# Generate image captions and detect objects\n", + "\n", + "txtai as the name implies works with text and ai, pretty straightforward. But that doesn't mean it can't work with different types of content. For example, an image can be described with words. We can use that description to compare an image to a query or other documents. This notebook shows how images and text can be embedded into the same space to generate image captions and detect objects." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dk31rbYjSTYm" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies. Since this notebook is using optional pipelines, we need to install the pipeline extras package." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "id": "XMQuuun2R06J" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install ipyplot git+https://github.com/neuml/txtai#egg=txtai[pipeline]\n", + "\n", + "# Get test data\n", + "!wget -N https://github.com/neuml/txtai/releases/download/v3.5.0/tests.tar.gz\n", + "!tar -xvzf tests.tar.gz" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PNPJ95cdTKSS" + }, + "source": [ + "# Create a captions instance\n", + "\n", + "The captions pipeline takes an image or list of images and generates captions. This pipelines works using a combination of an image encoder model and a text model. " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "id": "nTDwXOUeTH2-" + }, + "outputs": [], + "source": [ + "%%capture\n", + "\n", + "from txtai.pipeline import Caption\n", + "\n", + "# Create caption pipeline\n", + "caption = Caption()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-vGR_piwZZO6" + }, + "source": [ + "# Generate captions\n", + "\n", + "The example below shows how to generate captions. A list of images are read from a directory, passed to a caption model and text descriptions are returned." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "-K2YJJzsVtfq", + "outputId": "7cfd549a-1db6-47b9-c4ae-623e94ed48d1" + }, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + " \n", + "

\n", + " \n", + " \n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + " \n", + "
\n", + "
\n", + "
\n", + "

a clock on the side of a wall

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

a painting of flowers on top of a table

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

a computer screen with a picture of a person on it

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

a blurry photo of a sunset with a sky background

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

a blurry photo of a bunch of stuffed animals

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

motorcycles are parked on the side of the road

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

a large tree branch with a person in it

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

a city street at night with traffic lights

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

a book shelf filled with many books

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

a large building with many windows in a city

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + } + ], + "source": [ + "import glob\n", + "import ipyplot\n", + "\n", + "from PIL import Image\n", + "\n", + "# Get list of images\n", + "images = glob.glob('txtai/*jpg')\n", + "\n", + "# Generate captions\n", + "captions = caption(images)\n", + "\n", + "# Show image/caption pairs\n", + "ipyplot.plot_images([Image.open(image) for image in images], captions, img_width=425, force_b64=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dQmxNGkXw-YN" + }, + "source": [ + "Reviewing the captions, they are all generally in the right ballpark but far from perfect. The default model does a decent job but more robust models are necessary to fully deploy an image captioning model. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GxjHgXnz1MCD" + }, + "source": [ + "# Create an objects instance\n", + "\n", + "The objects pipeline takes an image or list of images and generates a list of detected objects. This pipeline works using an object detection model." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "id": "unoteHQM1l6V" + }, + "outputs": [], + "source": [ + "%%capture\n", + "\n", + "from txtai.pipeline import Objects\n", + "\n", + "# Create objects pipeline\n", + "objects = Objects()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HvZE-bww1v1k" + }, + "source": [ + "# Detect objects\n", + "\n", + "The example below shows how to detect objects. A list of images are read from a directory, passed to an object detection model and detected objects are returned." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "id": "F_qrDbdv2IVu", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "outputId": "18642f80-faeb-4fc7-f907-6acc6bdf32fc" + }, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + " \n", + "
\n", + "
\n", + "
\n", + "

[('clock', 0.9837772846221924)]

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

[('vase', 0.9913519620895386)]

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

[('cell phone', 0.9672072529792786)]

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

[]

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

[]

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

[('motorcycle', 0.9990019202232361), ('person', 0.9853999018669128)]

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

[('bird', 0.9167556762695312)]

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

[]

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

[('book', 0.9250583648681641)]

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

[('umbrella', 0.9032363295555115)]

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + } + ], + "source": [ + "import glob\n", + "import ipyplot\n", + "\n", + "from PIL import Image\n", + "\n", + "# Get list of images\n", + "images = glob.glob('txtai/*jpg')\n", + "\n", + "# Detect objects\n", + "detected = objects(images)\n", + "\n", + "# Show image/objects pairs\n", + "ipyplot.plot_images([Image.open(image) for image in images], detected, img_width=425, force_b64=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dEX-dbXE3U7W" + }, + "source": [ + "Reviewing the detected objects, once again they are all generally in the right ballpark but far from perfect.\n", + "\n", + "This model or larger models may do well for a specific use cases in which the model has a high accuracy. For example, the results could be filtered on only accept certain types of objects, which have shown to have high accuracy." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HeN8e1uy-icp" + }, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook introduced image captions and object detection. While the default models for both tasks aren't where we'd like them to be, they provide a good baseline to build on. For certain, targeted use cases where the models excel, they can be used now. This is a fast-evolving area and it is fully expected these models will improve!" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "collapsed_sections": [], + "name": "25 - Generate image captions and detect objects", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/examples/26_Entity_extraction_workflows.ipynb b/examples/26_Entity_extraction_workflows.ipynb new file mode 100644 index 0000000..aac70a8 --- /dev/null +++ b/examples/26_Entity_extraction_workflows.ipynb @@ -0,0 +1,270 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "kernelspec": { + "name": "python3", + "display_name": "Python 3", + "language": "python" + }, + "language_info": { + "name": "python", + "version": "3.7.6", + "mimetype": "text/x-python", + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "pygments_lexer": "ipython3", + "nbconvert_exporter": "python", + "file_extension": ".py" + }, + "colab": { + "name": "26 - Entity extraction workflows", + "provenance": [], + "collapsed_sections": [] + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "POWZoSJR6XzK" + }, + "source": [ + "# Entity extraction workflows\n", + "\n", + "Entity extraction is the process of identifying names, locations, organizations and other entity-like tokens in unstructured text. Entity extraction can organize data into topics and/or feed downstream machine learning pipelines.\n", + "\n", + "This notebook will show how to use the entity extraction pipeline in txtai with workflows." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qa_PPKVX6XzN" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "metadata": { + "_uuid": "8f2839f25d086af736a60e9eeb907d3b93b6e0e5", + "_cell_guid": "b1076dfc-b9ad-4769-8c92-a6c4dae69d19", + "trusted": true, + "_kg_hide-output": true, + "id": "24q-1n5i6XzQ" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Extract entities\n", + "\n", + "Let's get right to it! The following example creates an entity pipeline and extracts entities from text. \n" + ], + "metadata": { + "id": "0p3WCDniUths" + } + }, + { + "cell_type": "code", + "metadata": { + "_uuid": "d629ff2d2480ee46fbb7e2d37f6b5fab8052498a", + "_cell_guid": "79c7e3d0-c299-4dcb-8224-4455121ee9b0", + "trusted": true, + "id": "2j_CFGDR6Xzp", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "6276538e-5b06-4038-ae0f-0aa642e86814" + }, + "source": [ + "from txtai.pipeline import Entity\n", + "\n", + "data = [\"US tops 5 million confirmed virus cases\",\n", + " \"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\",\n", + " \"Beijing mobilises invasion craft along coast as Taiwan tensions escalate\",\n", + " \"The National Park Service warns against sacrificing slower friends in a bear attack\",\n", + " \"Maine man wins $1M from $25 lottery ticket\",\n", + " \"Make huge profits without work, earn up to $100,000 a day\"]\n", + "\n", + "entity = Entity()\n", + "\n", + "for x, e in enumerate(entity(data)):\n", + " print(data[x])\n", + " print(f\" {e}\", \"\\n\")" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "US tops 5 million confirmed virus cases\n", + " [('US', 'LOC', 0.999273955821991)] \n", + "\n", + "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\n", + " [('Canada', 'LOC', 0.999609649181366), ('Manhattan', 'MISC', 0.651396632194519)] \n", + "\n", + "Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + " [('Beijing', 'LOC', 0.9996659755706787), ('Taiwan', 'LOC', 0.9996755123138428)] \n", + "\n", + "The National Park Service warns against sacrificing slower friends in a bear attack\n", + " [('National Park Service', 'ORG', 0.9993489384651184)] \n", + "\n", + "Maine man wins $1M from $25 lottery ticket\n", + " [('Maine', 'LOC', 0.9987521171569824)] \n", + "\n", + "Make huge profits without work, earn up to $100,000 a day\n", + " [] \n", + "\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The section above is running an entity extraction pipeline for each row in data. The outputs are the token(s) identified as part of an entity, the type of entity and score or confidence in the prediction." + ], + "metadata": { + "id": "bVd_qHh97IlJ" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Feed entities to a workflow\n", + "\n", + "The next section demonstrates how the entity extraction pipeline can be used as part of a workflow. This workflow uses the output entities and builds an embeddings index for each row. This effectively computes entity embeddings to compare the row similarity with a focus on mentioned entities." + ], + "metadata": { + "id": "fY71Dyyt8Arv" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai.embeddings import Embeddings, Documents\n", + "from txtai.workflow import Workflow, Task\n", + "\n", + "# Create workflow with an entity pipeline output into a documents collection\n", + "documents = Documents()\n", + "workflow = Workflow([Task(lambda x: entity(x, flatten=True, join=True)), Task(documents.add, unpack=False)])\n", + "\n", + "# Run workflow\n", + "for _ in workflow([(x, row, None) for x, row in enumerate(data)]):\n", + " pass\n", + "\n", + "embeddings = Embeddings({\"path\": \"sentence-transformers/nli-mpnet-base-v2\"})\n", + "embeddings.index(documents)\n", + "\n", + "for query in [\"North America\", \"Asia Pacific\"]:\n", + " index = embeddings.search(query, 1)[0][0]\n", + " print(query, \"\\t\", data[index])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "yB3e6HVM8dRJ", + "outputId": "8424ce5d-9652-421d-d0bd-261b911ea7d6" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "North America \t Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\n", + "Asia Pacific \t Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Run workflow YAML\n", + "\n", + "Below is the same example using workflow YAML." + ], + "metadata": { + "id": "u_0WiV1NvbCK" + } + }, + { + "cell_type": "code", + "source": [ + "workflow = \"\"\"\n", + "writable: true\n", + "embeddings:\n", + " path: sentence-transformers/nli-mpnet-base-v2\n", + "\n", + "entity:\n", + "\n", + "workflow:\n", + " index:\n", + " tasks:\n", + " - action: entity\n", + " args: [null, \"simple\", true, true]\n", + " - action: index\n", + "\"\"\"\n", + "\n", + "from txtai.app import Application\n", + "\n", + "# Create and run workflow\n", + "app = Application(workflow)\n", + "for _ in app.workflow(\"index\", [(x, row, None) for x, row in enumerate(data)]):\n", + " pass\n", + "\n", + "# Run queries\n", + "for query in [\"North America\", \"Asia Pacific\"]:\n", + " index = app.search(query)[0][\"id\"]\n", + " print(query, \"\\t\", data[index])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "3qmRdU7LvhQ3", + "outputId": "e01453fc-5bbf-41a6-e557-64fa453f80ba" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "North America \t Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\n", + "Asia Pacific \t Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "This notebook introduced entity extraction pipelines with txtai. This pipeline supports a number of different configurations to help feed downstream systems and/or directly use the entities.\n", + "\n", + "As with other pipelines, the entity extraction pipeline can be used standalone in Python, as an API service or as part of a workflow!" + ], + "metadata": { + "id": "i3kkN5Vpx8ks" + } + } + ] +} diff --git a/examples/27_Workflow_scheduling.ipynb b/examples/27_Workflow_scheduling.ipynb new file mode 100644 index 0000000..6fc1f48 --- /dev/null +++ b/examples/27_Workflow_scheduling.ipynb @@ -0,0 +1,442 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "27 - Workflow scheduling", + "provenance": [], + "collapsed_sections": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "LjmhJ4ad9kBL" + }, + "source": [ + "# Workflow scheduling\n", + "\n", + "Workflows are a simple yet powerful construct that takes a callable and returns elements. They are streaming and work on data in batches, allowing large volumes of data to be processed efficiently. When working with streaming data, workflows continually run until the data stream is exhausted. \n", + "\n", + "Workflows can also be scheduled to run. In this case, a static set of elements, dynamically expands. For example, an API service endpoint that returns items, or polling a directory with files coming in and out. \n", + "\n", + "This notebook will show how to use workflow scheduling in txtai." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8tLWvo9v-Q0u" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "Fa5BCjMFqVKE" + }, + "source": [ + "%%capture\n", + "!pip install datasets git+https://github.com/neuml/txtai#egg=txtai[workflow]" + ], + "execution_count": 6, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Create workflow action\n", + "\n", + "Workflows run a series of tasks to transform and process data. This section creates a callable object that can be used as a workflow action. The object iterates over a dataset, returning a batch of data." + ], + "metadata": { + "id": "EJ_hHmQtRgQM" + } + }, + { + "cell_type": "code", + "metadata": { + "id": "3hYRk9JnsM0J" + }, + "source": [ + "from datasets import load_dataset\n", + "\n", + "class Stream:\n", + " def __init__(self):\n", + " self.dataset = load_dataset(\"ag_news\", split=\"train\")\n", + " self.index, self.size = 0, 2500\n", + "\n", + " def __call__(self, fields):\n", + " outputs = []\n", + " for field in fields:\n", + " output = []\n", + " for row in self.dataset.select(range(self.index, self.index+self.size)):\n", + " output.append((self.index, row[field], None))\n", + " self.index += 1\n", + "\n", + " outputs.append(output)\n", + "\n", + " return outputs" + ], + "execution_count": 7, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Build workflow\n", + "\n", + "Next we'll create the workflow. The workflow reads batches of data from a stream and loads it into an Embeddings index. We'll run this workflow four times on a scheduled interval to demonstrate a scheduled workflow." + ], + "metadata": { + "id": "_B4YFu-1R2QC" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai.app import Application\n", + "\n", + "# Run up to every 5 seconds 4 times\n", + "workflow = \"\"\"\n", + "writable: true\n", + "embeddings:\n", + " path: sentence-transformers/nli-mpnet-base-v2\n", + " content: true\n", + "\n", + "workflow:\n", + " index:\n", + " schedule:\n", + " cron: '* * * * * 0/5'\n", + " elements:\n", + " - text\n", + " iterations: 4\n", + " tasks:\n", + " - __main__.Stream\n", + " - upsert\n", + "\"\"\"\n", + "\n", + "app = Application(workflow)\n", + "app.wait()" + ], + "metadata": { + "id": "1oZag3tKWkfe", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "54d6ce91-d782-421a-e092-7e78bb6ee1d0" + }, + "execution_count": 8, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "2022-02-03 02:12:06,720 [WARNING] _create_builder_config: Using custom data configuration default\n", + "2022-02-03 02:12:06,727 [WARNING] download_and_prepare: Reusing dataset ag_news (/root/.cache/huggingface/datasets/ag_news/default/0.0.0/bc2bcb40336ace1a0374767fc29bb0296cdaf8a6da7298436239c54d79180548)\n", + "2022-02-03 02:12:06,751 [INFO] schedule: 'index' scheduler started with schedule * * * * * 0/5\n", + "2022-02-03 02:12:06,757 [INFO] schedule: 'index' next run scheduled for 2022-02-03T02:12:10+00:00\n", + "2022-02-03 02:12:34,937 [INFO] schedule: 'index' next run scheduled for 2022-02-03T02:12:35+00:00\n", + "2022-02-03 02:12:59,967 [INFO] schedule: 'index' next run scheduled for 2022-02-03T02:13:00+00:00\n", + "2022-02-03 02:13:23,349 [INFO] schedule: 'index' next run scheduled for 2022-02-03T02:13:25+00:00\n", + "2022-02-03 02:13:49,621 [INFO] schedule: 'index' max iterations (4) reached\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Reviewing the log above, we see the `index` job ran four times. Now let's query the index and see what was loaded." + ], + "metadata": { + "id": "SC3Yf9EgSHiK" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Run an embeddings search\n", + "\n", + "Let's run a search against the newly created index." + ], + "metadata": { + "id": "5hBY2TsZSQY0" + } + }, + { + "cell_type": "code", + "source": [ + "import json\n", + "\n", + "# Show total number of records\n", + "print(f\"Total records: {app.count()}\")\n", + "\n", + "# Run a search\n", + "print(\"Search:\")\n", + "print(json.dumps(app.search(\"life on mars\", limit=1), indent=2))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "3slP9p_KkbtD", + "outputId": "9473aaea-eaa3-42e5-f9db-1f9f6e51f5c5" + }, + "execution_count": 9, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Total records: 10000\n", + "Search:\n", + "[\n", + " {\n", + " \"id\": \"119\",\n", + " \"text\": \"Life on Mars Likely, Scientist Claims (SPACE.com) SPACE.com - DENVER, COLORADO -- Those twin robots hard at work on Mars have transmitted teasing views that reinforce the prospect that microbial life may exist on the red planet.\",\n", + " \"score\": 0.7236138582229614\n", + " }\n", + "]\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The index has 10,000 records. We also see the top result for the query on `life on mars`." + ], + "metadata": { + "id": "4i3H19ZmUSlK" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Run a scheduled embeddings search\n", + "\n", + "Now let's incrementally load the dataset with a scheduled workflow and run a scheduled search after each batch is loaded." + ], + "metadata": { + "id": "-t-D7yu_WZl-" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai.app import Application\n", + "\n", + "# Run every 5 seconds up to 4 times\n", + "workflow = \"\"\"\n", + "writable: true\n", + "embeddings:\n", + " path: sentence-transformers/nli-mpnet-base-v2\n", + " content: true\n", + "\n", + "workflow:\n", + " index:\n", + " schedule:\n", + " cron: '* * * * * 0/5'\n", + " elements:\n", + " - text\n", + " iterations: 4\n", + " tasks:\n", + " - __main__.Stream\n", + " - upsert\n", + " search:\n", + " schedule:\n", + " cron: '* * * * * 0/5'\n", + " elements:\n", + " - life on mars\n", + " iterations: 4\n", + " tasks:\n", + " - action: search\n", + " args: [3]\n", + " task: console\n", + "\"\"\"\n", + "\n", + "app = Application(workflow)\n", + "app.wait()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "xpuwL9aCUJOd", + "outputId": "d994aaaf-7315-4109-d024-3e09773cc538" + }, + "execution_count": 10, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "2022-02-03 02:13:55,789 [WARNING] _create_builder_config: Using custom data configuration default\n", + "2022-02-03 02:13:55,797 [WARNING] download_and_prepare: Reusing dataset ag_news (/root/.cache/huggingface/datasets/ag_news/default/0.0.0/bc2bcb40336ace1a0374767fc29bb0296cdaf8a6da7298436239c54d79180548)\n", + "2022-02-03 02:13:55,808 [INFO] schedule: 'index' scheduler started with schedule * * * * * 0/5\n", + "2022-02-03 02:13:55,808 [INFO] schedule: 'search' scheduler started with schedule * * * * * 0/5\n", + "2022-02-03 02:13:55,810 [INFO] schedule: 'index' next run scheduled for 2022-02-03T02:14:00+00:00\n", + "2022-02-03 02:13:55,814 [INFO] schedule: 'search' next run scheduled for 2022-02-03T02:14:00+00:00\n", + "2022-02-03 02:14:00,001 [INFO] schedule: 'search' next run scheduled for 2022-02-03T02:14:05+00:00\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Inputs: [\n", + " \"life on mars\"\n", + "]\n", + "Outputs: [\n", + " null\n", + "]\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "2022-02-03 02:14:24,500 [INFO] schedule: 'index' next run scheduled for 2022-02-03T02:14:25+00:00\n", + "2022-02-03 02:14:24,522 [INFO] schedule: 'search' next run scheduled for 2022-02-03T02:14:25+00:00\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Inputs: [\n", + " \"life on mars\"\n", + "]\n", + "Outputs: [\n", + " {\n", + " \"id\": \"119\",\n", + " \"text\": \"Life on Mars Likely, Scientist Claims (SPACE.com) SPACE.com - DENVER, COLORADO -- Those twin robots hard at work on Mars have transmitted teasing views that reinforce the prospect that microbial life may exist on the red planet.\",\n", + " \"score\": 0.7236138582229614\n", + " },\n", + " {\n", + " \"id\": \"271\",\n", + " \"text\": \"Saturn's Moon Titan: Prebiotic Laboratory by Harry Bortman In this second and final part of the interview, Lunine explains how Huygens may help scientists understand the origin of life on Earth, even if it doesn't detect life on Titan. Astrobiology Magazine -- Titan is the only moon in our solar system with an atmosphere, and it is the organic chemistry that has been detected in that atmosphere that has sparked the imagination of planetary scientists like Lunine...\",\n", + " \"score\": 0.4750666916370392\n", + " },\n", + " {\n", + " \"id\": \"1132\",\n", + " \"text\": \"Is Mercury the Incredible Shrinking Planet? MESSENGER Spacecraft May Find Out (SPACE.com) SPACE.com - With a new spacecraft bound for Mercury, that tiny planet nbsp;near the heart of the solar system, researchers are hoping to solve a slew of riddles about the small world.\",\n", + " \"score\": 0.47124743461608887\n", + " }\n", + "]\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "2022-02-03 02:14:25,496 [INFO] schedule: 'search' next run scheduled for 2022-02-03T02:14:30+00:00\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Inputs: [\n", + " \"life on mars\"\n", + "]\n", + "Outputs: [\n", + " {\n", + " \"id\": \"119\",\n", + " \"text\": \"Life on Mars Likely, Scientist Claims (SPACE.com) SPACE.com - DENVER, COLORADO -- Those twin robots hard at work on Mars have transmitted teasing views that reinforce the prospect that microbial life may exist on the red planet.\",\n", + " \"score\": 0.7236138582229614\n", + " },\n", + " {\n", + " \"id\": \"271\",\n", + " \"text\": \"Saturn's Moon Titan: Prebiotic Laboratory by Harry Bortman In this second and final part of the interview, Lunine explains how Huygens may help scientists understand the origin of life on Earth, even if it doesn't detect life on Titan. Astrobiology Magazine -- Titan is the only moon in our solar system with an atmosphere, and it is the organic chemistry that has been detected in that atmosphere that has sparked the imagination of planetary scientists like Lunine...\",\n", + " \"score\": 0.4750666916370392\n", + " },\n", + " {\n", + " \"id\": \"1132\",\n", + " \"text\": \"Is Mercury the Incredible Shrinking Planet? MESSENGER Spacecraft May Find Out (SPACE.com) SPACE.com - With a new spacecraft bound for Mercury, that tiny planet nbsp;near the heart of the solar system, researchers are hoping to solve a slew of riddles about the small world.\",\n", + " \"score\": 0.47124743461608887\n", + " }\n", + "]\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "2022-02-03 02:14:50,112 [INFO] schedule: 'index' next run scheduled for 2022-02-03T02:14:55+00:00\n", + "2022-02-03 02:14:50,138 [INFO] schedule: 'search' max iterations (4) reached\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Inputs: [\n", + " \"life on mars\"\n", + "]\n", + "Outputs: [\n", + " {\n", + " \"id\": \"119\",\n", + " \"text\": \"Life on Mars Likely, Scientist Claims (SPACE.com) SPACE.com - DENVER, COLORADO -- Those twin robots hard at work on Mars have transmitted teasing views that reinforce the prospect that microbial life may exist on the red planet.\",\n", + " \"score\": 0.7236138582229614\n", + " },\n", + " {\n", + " \"id\": \"3300\",\n", + " \"text\": \"Mars Hills, Crater Yield Evidence of Flowing Water LOS ANGELES (Reuters) - The hills of Mars yielded more tantalizing clues about how water shaped the Red Planet in tests by NASA #39;s robotic geologist, Spirit, while its twin, Opportunity, observed the deep crater it climbed into two months ...\",\n", + " \"score\": 0.6666488647460938\n", + " },\n", + " {\n", + " \"id\": \"4201\",\n", + " \"text\": \"Martian hill shows signs of ancient water LOS ANGELES - NASA #39;s Spirit rover has found more evidence of past water on the hills of Mars, while its twin, Opportunity, has observed a field of dunes inside a crater. \",\n", + " \"score\": 0.6453495621681213\n", + " }\n", + "]\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "2022-02-03 02:15:18,333 [INFO] schedule: 'index' next run scheduled for 2022-02-03T02:15:20+00:00\n", + "2022-02-03 02:15:44,592 [INFO] schedule: 'index' max iterations (4) reached\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The workflow above runs up to every 5 seconds. Note that since the index job takes longer than 5 seconds, the time difference between jobs is longer.\n", + "\n", + "The index job loads the next batch of data and the search job runs a recurring search. \n", + "\n", + "See how the search results change over time as more relevant results are found." + ], + "metadata": { + "id": "o9EX2NgxV23x" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "This notebook covered how to use workflow scheduling with txtai. While there are existing ways to schedule jobs (system cron, serverless, and so on), this is another easy and quick way to do it. " + ], + "metadata": { + "id": "Fr99QHPtTMJt" + } + } + ] +} diff --git a/examples/28_Push_notifications_with_workflows.ipynb b/examples/28_Push_notifications_with_workflows.ipynb new file mode 100644 index 0000000..dd90a19 --- /dev/null +++ b/examples/28_Push_notifications_with_workflows.ipynb @@ -0,0 +1,563 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "28 - Push notifications with workflows", + "provenance": [], + "collapsed_sections": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "LjmhJ4ad9kBL" + }, + "source": [ + "# Push notifications with workflows\n", + "\n", + "Workflows are a simple yet powerful construct that takes a callable and returns elements. They are streaming and work on data in batches, allowing large volumes of data to be processed efficiently.\n", + "\n", + "This notebook will show how workflows can be used to push notifications upon certain event triggers. Using this method, an activity feed of content can be created." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8tLWvo9v-Q0u" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "Fa5BCjMFqVKE" + }, + "source": [ + "%%capture\n", + "!pip install datasets git+https://github.com/neuml/txtai#egg=txtai[pipeline,workflow]" + ], + "execution_count": 1, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Create workflow notification action\n", + "\n", + "Workflows run a series of tasks to transform and process data. This section creates a callable object that can be used as a workflow action. \n", + "\n", + "The action below pushes events to [Slack](https://slack.com). While Slack is used here, any notification service can easily be substituted in ([Zapier](https://zapier.com/), [IFTT](https://ifttt.com/) etc).\n", + "\n", + "It is assumed there is a Slack workspace and application installed and ready to use. [See this comprehensive example](https://api.slack.com/tutorials/tracks/posting-messages-with-curl) for more information on setting up a new Slack app and posting messages via the API.\n", + "\n", + "The channel id can be found from the Slack web interface. Log into Slack and click on the channel where messages will be posted. The `channel id` is the last part of the URL.\n", + "\n", + "```\n", + "https://app.slack.com/client//\n", + "```" + ], + "metadata": { + "id": "EJ_hHmQtRgQM" + } + }, + { + "cell_type": "code", + "metadata": { + "id": "3hYRk9JnsM0J" + }, + "source": [ + "import logging\n", + "import requests\n", + "\n", + "# Uncomment and set. The following are dummy parameters. Your parameters should not be publicly shared!\n", + "# AUTH = \"xoxb-not-a-real-token-this-will-not-work\"\n", + "# CHANNEL = \"C0XXXXXXXXX\"\n", + "# URL = \"https://slack.com/api/chat.postMessage\"\n", + "\n", + "# Logging configuration\n", + "logger = logging.getLogger(__name__)\n", + "logger.setLevel(logging.INFO)\n", + "\n", + "class Slack:\n", + " def __init__(self):\n", + " self.alerts = set()\n", + "\n", + " def __call__(self, elements):\n", + " for alert in elements:\n", + " uid, text, _ = self.extract(alert)\n", + " if uid not in self.alerts:\n", + " logger.info(\"Sending alert: %s\", alert)\n", + " self.alerts.add(uid)\n", + "\n", + " headers = {\n", + " \"Content-type\": \"application/json\",\n", + " \"Authorization\": f\"Bearer {AUTH}\"\n", + " }\n", + "\n", + " response = requests.post(URL, headers=headers, json={\n", + " \"channel\": CHANNEL,\n", + " \"text\": f\"{text} {uid}\"\n", + " }).json()\n", + "\n", + " if not response[\"ok\"]:\n", + " logger.error(response)\n", + "\n", + " return elements\n", + "\n", + " def extract(self, alert):\n", + " if isinstance(alert, dict):\n", + " return (alert[\"id\"], alert[\"text\"], None)\n", + "\n", + " return alert\n" + ], + "execution_count": 6, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Build a semantic notification workflow\n", + "\n", + "Next we'll create a notification workflow. The example below indexes the top trending Hacker News articles and pushes an alert when an article matches an embeddings query for `software development library`." + ], + "metadata": { + "id": "_B4YFu-1R2QC" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai.app import Application\n", + "\n", + "workflow = \"\"\"\n", + "writable: true\n", + "\n", + "embeddings:\n", + " path: sentence-transformers/nli-mpnet-base-v2\n", + " content: true\n", + "\n", + "tabular:\n", + " idcolumn: url\n", + " textcolumns:\n", + " - title\n", + "\n", + "__main__.Slack:\n", + "\n", + "workflow:\n", + " index:\n", + " schedule:\n", + " cron: \"* * * * * 0/5\"\n", + " elements:\n", + " - front_page\n", + " iterations: 1\n", + " tasks:\n", + " - batch: false\n", + " extract:\n", + " - hits\n", + " method: get\n", + " params:\n", + " tags: null\n", + " task: service\n", + " url: https://hn.algolia.com/api/v1/search?hitsPerPage=50\n", + " - action: tabular\n", + " - action: upsert\n", + " alert:\n", + " schedule:\n", + " cron: 0/1 * * * *\n", + " elements:\n", + " - select id, text, score from txtai where similar('software development library') and score >= 0.4 and id like 'http%'\n", + " iterations: 1\n", + " tasks:\n", + " - action: search\n", + " - action: __main__.Slack\n", + "\"\"\"\n", + "\n", + "app = Application(workflow)\n", + "app.wait()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "DWEdyXUgIKqW", + "outputId": "c00e2949-db6f-4b4e-e514-7f0c1fcb14b1" + }, + "execution_count": 5, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "2022-02-10 15:12:42,838 [INFO] schedule: 'index' scheduler started with schedule * * * * * 0/5\n", + "2022-02-10 15:12:42,839 [INFO] schedule: 'alert' scheduler started with schedule 0/1 * * * *\n", + "2022-02-10 15:12:42,843 [INFO] schedule: 'index' next run scheduled for 2022-02-10T15:12:45+00:00\n", + "2022-02-10 15:12:42,851 [INFO] schedule: 'alert' next run scheduled for 2022-02-10T15:13:00+00:00\n", + "2022-02-10 15:12:45,884 [INFO] schedule: 'index' max iterations (1) reached\n", + "2022-02-10 15:13:00,042 [INFO] __call__: Sending alert: {'id': 'https://datastation.multiprocess.io/blog/2022-02-08-the-world-of-postgresql-wire-compatibility.html', 'text': 'The world of PostgreSQL wire compatibility', 'score': 0.40123000741004944}\n", + "2022-02-10 15:13:00,254 [INFO] schedule: 'alert' max iterations (1) reached\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The log above shows the indexing and alerting jobs each ran once. There was a single match and it was sent to Slack. The score threshold of 0.4 is relatively low, it can be raised if more strict matches are desired.\n", + "\n", + "![3.png](data:image/png;base64,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)" + ], + "metadata": { + "id": "GsDzX_K8Qh0d" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Build a notification workflow using SQL\n", + "\n", + "The next example is similar but it instead runs a SQL search using another column.\n", + "\n", + "This workflow indexes the top trending sports events as identified by [neuspo](https://neuspo.com). An alert is generated when an excitement factor of 40 or above is met (the field for excitement is called `weight`). " + ], + "metadata": { + "id": "HqU5KNZwWMnR" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai.app import Application\n", + "\n", + "workflow = \"\"\"\n", + "writable: true\n", + "\n", + "embeddings:\n", + " path: sentence-transformers/nli-mpnet-base-v2\n", + " content: true\n", + "\n", + "tabular:\n", + " idcolumn: url\n", + " textcolumns:\n", + " - summary\n", + " content: true\n", + "\n", + "__main__.Slack:\n", + "\n", + "workflow:\n", + " index:\n", + " schedule:\n", + " cron: \"* * * * * 0/5\"\n", + " elements:\n", + " - 10\n", + " iterations: 1\n", + " tasks:\n", + " - batch: false\n", + " extract:\n", + " - rows\n", + " method: get\n", + " params:\n", + " size: null\n", + " task: service\n", + " url: https://neuspo.com/data/articles/list?category=Top&from=0\n", + " - action: tabular\n", + " - action: upsert\n", + " alert:\n", + " schedule:\n", + " cron: 0/1 * * * *\n", + " elements:\n", + " - select 'https://neuspo.com' || id id, text from txtai where weight >= 40\n", + " iterations: 1\n", + " tasks:\n", + " - action: search\n", + " - action: __main__.Slack\n", + "\"\"\"\n", + "\n", + "app = Application(workflow)\n", + "app.wait()" + ], + "metadata": { + "id": "1oZag3tKWkfe", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "81f5fbea-6585-4afb-c2d9-1e5de3e17f40" + }, + "execution_count": 6, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "2022-02-10 15:16:49,702 [INFO] schedule: 'index' scheduler started with schedule * * * * * 0/5\n", + "2022-02-10 15:16:49,704 [INFO] schedule: 'index' next run scheduled for 2022-02-10T15:16:50+00:00\n", + "2022-02-10 15:16:49,704 [INFO] schedule: 'alert' scheduler started with schedule 0/1 * * * *\n", + "2022-02-10 15:16:49,714 [INFO] schedule: 'alert' next run scheduled for 2022-02-10T15:17:00+00:00\n", + "2022-02-10 15:16:50,474 [INFO] schedule: 'index' max iterations (1) reached\n", + "2022-02-10 15:17:00,010 [INFO] __call__: Sending alert: {'id': 'https://neuspo.com/stream/be15c852925b53639b63feb7169a2842', 'text': 'Islanders 6, Canucks 3: Five-goal first period keys Islanders win in first game post-'}\n", + "2022-02-10 15:17:00,215 [INFO] schedule: 'alert' max iterations (1) reached\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "And the notification in Slack for this job.\n", + "\n", + "![](data:image/png;base64,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)" + ], + "metadata": { + "id": "e1rB3l8pTRtN" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Search-Summarize-Notify\n", + "\n", + "The next section builds on the Hacker News example. Instead of just sending a notification on a match, this workflow will summarize the match first.\n", + "\n", + "There are a number of alternative combinations. For example, the summaries could be built at index time. But this example will do everything on the fly when searching." + ], + "metadata": { + "id": "Nsr7vVKFkA11" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai.app import Application\n", + "\n", + "workflow = \"\"\"\n", + "writable: true\n", + "\n", + "embeddings:\n", + " path: sentence-transformers/nli-mpnet-base-v2\n", + " content: true\n", + "\n", + "summary:\n", + " path: sshleifer/distilbart-cnn-12-6\n", + "\n", + "tabular:\n", + " idcolumn: url\n", + " textcolumns:\n", + " - title\n", + "\n", + "textractor:\n", + " join: true\n", + " minlength: 100\n", + " paragraphs: true\n", + "\n", + "__main__.Slack:\n", + "\n", + "workflow:\n", + " index:\n", + " schedule:\n", + " cron: \"* * * * * 0/5\"\n", + " elements:\n", + " - front_page\n", + " iterations: 1\n", + " tasks:\n", + " - batch: false\n", + " extract:\n", + " - hits\n", + " method: get\n", + " params:\n", + " tags: null\n", + " task: service\n", + " url: https://hn.algolia.com/api/v1/search?hitsPerPage=50\n", + " - action: tabular\n", + " - action: upsert\n", + " alert:\n", + " schedule:\n", + " cron: 0/1 * * * *\n", + " elements:\n", + " - select id url, id title from txtai where similar('software development library') and score >= 0.4 and id like 'http%'\n", + " iterations: 1\n", + " tasks:\n", + " - action: search\n", + " - action: tabular\n", + " - action: textractor\n", + " - action: summary\n", + " - action: __main__.Slack\n", + " unpack: false\n", + "\"\"\"\n", + "\n", + "app = Application(workflow)\n", + "app.wait()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "SxljaDiPkvv3", + "outputId": "770ef0cf-f9e3-4e6e-ae18-084422731efb" + }, + "execution_count": 7, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "2022-02-10 17:19:48,847 [INFO] schedule: 'index' scheduler started with schedule * * * * * 0/5\n", + "2022-02-10 17:19:48,857 [INFO] schedule: 'index' next run scheduled for 2022-02-10T17:19:50+00:00\n", + "2022-02-10 17:19:48,848 [INFO] schedule: 'alert' scheduler started with schedule 0/1 * * * *\n", + "2022-02-10 17:19:48,864 [INFO] schedule: 'alert' next run scheduled for 2022-02-10T17:20:00+00:00\n", + "2022-02-10 17:19:50,368 [INFO] schedule: 'index' max iterations (1) reached\n", + "2022-02-10 17:20:02,233 [INFO] __call__: Sending alert: ('https://datastation.multiprocess.io/blog/2022-02-08-the-world-of-postgresql-wire-compatibility.html', 'Every server-client database has a wire protocol. A wire protocol is the format for interactions between a database server and its clients. It does NOT encompass the actual query language itself, let alone database semantics. Proprietary databases like Oracle and IBM Db2 find value in developing their own drivers.', None)\n", + "2022-02-10 17:20:02,496 [INFO] schedule: 'alert' max iterations (1) reached\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "And the result in Slack. See how the text is now the article summary vs. the title.\n", + "\n", + "![](data:image/png;base64,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)" + ], + "metadata": { + "id": "KqtbguMdpsSO" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Search-Summarize-Translate-Notify\n", + "\n", + "One more example to really drive this home. Let's do the same as the last example and add a translate to French step." + ], + "metadata": { + "id": "JNL1UX9aqC2z" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai.app import Application\n", + "\n", + "workflow = \"\"\"\n", + "writable: true\n", + "\n", + "embeddings:\n", + " path: sentence-transformers/nli-mpnet-base-v2\n", + " content: true\n", + "\n", + "summary:\n", + " path: sshleifer/distilbart-cnn-12-6\n", + "\n", + "tabular:\n", + " idcolumn: url\n", + " textcolumns:\n", + " - title\n", + "\n", + "textractor:\n", + " join: true\n", + " minlength: 100\n", + " paragraphs: true\n", + "\n", + "translation:\n", + "\n", + "__main__.Slack:\n", + "\n", + "workflow:\n", + " index:\n", + " schedule:\n", + " cron: \"* * * * * 0/5\"\n", + " elements:\n", + " - front_page\n", + " iterations: 1\n", + " tasks:\n", + " - batch: false\n", + " extract:\n", + " - hits\n", + " method: get\n", + " params:\n", + " tags: null\n", + " task: service\n", + " url: https://hn.algolia.com/api/v1/search?hitsPerPage=50\n", + " - action: tabular\n", + " - action: upsert\n", + " alert:\n", + " schedule:\n", + " cron: 0/1 * * * *\n", + " elements:\n", + " - select id url, id title from txtai where similar('software development library') and score >= 0.4 and id like 'http%'\n", + " iterations: 1\n", + " tasks:\n", + " - action: search\n", + " - action: tabular\n", + " - action: textractor\n", + " - action: summary\n", + " - action: translation\n", + " args:\n", + " - fr\n", + " - action: __main__.Slack\n", + " unpack: false\n", + "\"\"\"\n", + "\n", + "app = Application(workflow)\n", + "app.wait()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "nooA9cFNqNhb", + "outputId": "febdcfe8-34ce-4952-fb94-ccb5c200ec35" + }, + "execution_count": 9, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "2022-02-10 17:25:04,448 [INFO] schedule: 'index' scheduler started with schedule * * * * * 0/5\n", + "2022-02-10 17:25:04,449 [INFO] schedule: 'alert' scheduler started with schedule 0/1 * * * *\n", + "2022-02-10 17:25:04,451 [INFO] schedule: 'index' next run scheduled for 2022-02-10T17:25:05+00:00\n", + "2022-02-10 17:25:04,457 [INFO] schedule: 'alert' next run scheduled for 2022-02-10T17:26:00+00:00\n", + "2022-02-10 17:25:05,357 [INFO] schedule: 'index' max iterations (1) reached\n", + "2022-02-10 17:26:08,125 [INFO] __call__: Sending alert: ('https://datastation.multiprocess.io/blog/2022-02-08-the-world-of-postgresql-wire-compatibility.html', \"Chaque base de données serveur-client a un protocole filaire. Un protocole filaire est le format pour les interactions entre un serveur de base de données et ses clients. Il n'inclut PAS le langage de requête réel lui-même, et encore moins la sémantique de la base de données.\", None)\n", + "2022-02-10 17:26:08,310 [INFO] schedule: 'alert' max iterations (1) reached\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "And just like before, Slack has a summary and a link but this time in French!\n", + "\n", + "![](data:image/png;base64,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)" + ], + "metadata": { + "id": "pSi8e8rGuAah" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "This notebook covered how to build workflow notifications with txtai. There are many directions one could go with this. Build an activity feed, alert when semantic events occur and more. More ideas can be found in the [txtai application](https://huggingface.co/spaces/NeuML/txtai) on Hugging Face Spaces. \n", + "\n", + "Everything in this notebook can also be written in Python. The benefits of YAML workflows are that they require little to no-code. Work is ongoing as of txtai 4.1 to make workflows easier to containerize and ultimately run in serverless environments. Keep an eye on this!" + ], + "metadata": { + "id": "Fr99QHPtTMJt" + } + } + ] +} diff --git a/examples/29_Anatomy_of_a_txtai_index.ipynb b/examples/29_Anatomy_of_a_txtai_index.ipynb new file mode 100644 index 0000000..509f442 --- /dev/null +++ b/examples/29_Anatomy_of_a_txtai_index.ipynb @@ -0,0 +1,1076 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "kernelspec": { + "name": "python3", + "display_name": "Python 3", + "language": "python" + }, + "language_info": { + "name": "python", + "version": "3.7.6", + "mimetype": "text/x-python", + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "pygments_lexer": "ipython3", + "nbconvert_exporter": "python", + "file_extension": ".py" + }, + "colab": { + "provenance": [] + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "POWZoSJR6XzK" + }, + "source": [ + "# Anatomy of a txtai index\n", + "\n", + "This notebook inspects the filesystem of a txtai embeddings index and gives an overview of the structure." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qa_PPKVX6XzN" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "metadata": { + "_uuid": "8f2839f25d086af736a60e9eeb907d3b93b6e0e5", + "_cell_guid": "b1076dfc-b9ad-4769-8c92-a6c4dae69d19", + "trusted": true, + "_kg_hide-output": true, + "id": "24q-1n5i6XzQ" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai\n", + "!apt-get update && apt-get install -y file xxd" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Create index\n", + "Let's first create an index to inspect. We'll use the classic txtai example.\n" + ], + "metadata": { + "id": "0p3WCDniUths" + } + }, + { + "cell_type": "code", + "metadata": { + "_uuid": "d629ff2d2480ee46fbb7e2d37f6b5fab8052498a", + "_cell_guid": "79c7e3d0-c299-4dcb-8224-4455121ee9b0", + "trusted": true, + "id": "2j_CFGDR6Xzp", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "4c16f389-2cf0-46d9-9cb8-bdda04d06559" + }, + "source": [ + "from txtai.embeddings import Embeddings\n", + "\n", + "data = [\"US tops 5 million confirmed virus cases\",\n", + " \"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\",\n", + " \"Beijing mobilises invasion craft along coast as Taiwan tensions escalate\",\n", + " \"The National Park Service warns against sacrificing slower friends in a bear attack\",\n", + " \"Maine man wins $1M from $25 lottery ticket\",\n", + " \"Make huge profits without work, earn up to $100,000 a day\"]\n", + "\n", + "# Create embeddings index with content enabled. The default behavior is to only store indexed vectors.\n", + "embeddings = Embeddings({\"path\": \"sentence-transformers/nli-mpnet-base-v2\", \"content\": True, \"objects\": True})\n", + "\n", + "# Create an index for the list of text\n", + "embeddings.index([(uid, text, None) for uid, text in enumerate(data)])\n", + "\n", + "# Run a search\n", + "embeddings.search(\"feel good story\", 1)" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '4',\n", + " 'score': 0.08329004049301147,\n", + " 'text': 'Maine man wins $1M from $25 lottery ticket'}]" + ] + }, + "metadata": {}, + "execution_count": 26 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Print index info\n", + "\n", + "Embeddings indexes have an `info` method which prints metadata about the index. This can be used to see when the index was build, what settings were used and when it was last updated." + ], + "metadata": { + "id": "pHqeRmHtw1ui" + } + }, + { + "cell_type": "code", + "source": [ + "# Print metadata\n", + "embeddings.info()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "o7nKY0AWxBWU", + "outputId": "be7eca6e-dbbc-40c5-df1f-9726554de476" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "{\n", + " \"backend\": \"faiss\",\n", + " \"build\": {\n", + " \"create\": \"2022-03-02T15:18:41Z\",\n", + " \"python\": \"3.7.12\",\n", + " \"settings\": {\n", + " \"components\": \"IDMap,Flat\"\n", + " },\n", + " \"system\": \"Linux (x86_64)\",\n", + " \"txtai\": \"4.3.0\"\n", + " },\n", + " \"content\": \"sqlite\",\n", + " \"dimensions\": 768,\n", + " \"objects\": true,\n", + " \"offset\": 6,\n", + " \"path\": \"sentence-transformers/nli-mpnet-base-v2\",\n", + " \"update\": \"2022-03-02T15:18:41Z\"\n", + "}\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Save index and review file structure\n", + "\n", + "Next let's save the index and review the file structure. This section prints each file, and runs commands to show" + ], + "metadata": { + "id": "BYWUFBUGyKyY" + } + }, + { + "cell_type": "code", + "source": [ + "# Save the index\n", + "embeddings.save(\"index\")\n", + "\n", + "# Show basic details about index files\n", + "for f in [\"config\", \"documents\", \"embeddings\"]:\n", + " !ls -l \"index/{f}\"\n", + " !xxd \"index/{f}\" | head -5\n", + " !file \"index/{f}\"\n", + " !echo\n" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "aPH-dnV2ZuL1", + "outputId": "6d8d1329-a2e8-4538-b197-0e2959b9eef2" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "-rw-r--r-- 1 root root 295 Mar 2 15:18 index/config\n", + "00000000: 8004 951c 0100 0000 0000 007d 9428 8c04 ...........}.(..\n", + "00000010: 7061 7468 948c 2773 656e 7465 6e63 652d path..'sentence-\n", + "00000020: 7472 616e 7366 6f72 6d65 7273 2f6e 6c69 transformers/nli\n", + "00000030: 2d6d 706e 6574 2d62 6173 652d 7632 948c -mpnet-base-v2..\n", + "00000040: 0763 6f6e 7465 6e74 948c 0673 716c 6974 .content...sqlit\n", + "index/config: data\n", + "\n", + "-rw-r--r-- 1 root root 28672 Mar 2 15:18 index/documents\n", + "00000000: 5351 4c69 7465 2066 6f72 6d61 7420 3300 SQLite format 3.\n", + "00000010: 1000 0101 0040 2020 0000 0001 0000 0007 .....@ ........\n", + "00000020: 0000 0000 0000 0000 0000 0001 0000 0004 ................\n", + "00000030: 0000 0000 0000 0000 0000 0001 0000 0000 ................\n", + "00000040: 0000 0000 0000 0000 0000 0000 0000 0000 ................\n", + "index/documents: SQLite 3.x database, last written using SQLite version 3022000\n", + "\n", + "-rw-r--r-- 1 root root 18570 Mar 2 15:18 index/embeddings\n", + "00000000: 4978 4d70 0003 0000 0600 0000 0000 0000 IxMp............\n", + "00000010: 0000 1000 0000 0000 0000 1000 0000 0000 ................\n", + "00000020: 0100 0000 0049 7846 4900 0300 0006 0000 .....IxFI.......\n", + "00000030: 0000 0000 0000 0010 0000 0000 0000 0010 ................\n", + "00000040: 0000 0000 0001 0000 0000 0012 0000 0000 ................\n", + "index/embeddings: data\n", + "\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The directory has three files: *config*, *documents* and *embeddings*.\n", + "\n", + "- config - The input configuration passed into the Embeddings object. Serialized with [Python's pickle format](https://docs.python.org/3/library/pickle.html).\n", + "\n", + "- documents - [SQLite](https://www.sqlite.org/index.html) database. Stores the input text content and associated data.\n", + "\n", + "- embeddings - The embeddings index file. This is an [Approximate Nearest Neighbor (ANN)](https://en.wikipedia.org/wiki/Nearest_neighbor_search#Approximate_nearest_neighbor) index with either [Faiss](https://github.com/facebookresearch/faiss) (default), [Hnswlib](https://github.com/nmslib/hnswlib) or [Annoy](https://github.com/spotify/annoy), depending on the settings." + ], + "metadata": { + "id": "oH4Yd9BOlo5u" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Config\n", + "\n", + "Given that the configuration file is serialized with Python pickle, it can be loaded in Python." + ], + "metadata": { + "id": "xO3CokBlzCfc" + } + }, + { + "cell_type": "code", + "source": [ + "import json\n", + "import pickle\n", + "\n", + "with open(\"index/config\", \"rb\") as config:\n", + " print(json.dumps(pickle.load(config), sort_keys=True, indent=2))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "aNQSCiXHzOTj", + "outputId": "00b5ebdf-961b-45ac-d90c-e6b824c11979" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "{\n", + " \"backend\": \"faiss\",\n", + " \"build\": {\n", + " \"create\": \"2022-03-02T15:18:41Z\",\n", + " \"python\": \"3.7.12\",\n", + " \"settings\": {\n", + " \"components\": \"IDMap,Flat\"\n", + " },\n", + " \"system\": \"Linux (x86_64)\",\n", + " \"txtai\": \"4.3.0\"\n", + " },\n", + " \"content\": \"sqlite\",\n", + " \"dimensions\": 768,\n", + " \"objects\": true,\n", + " \"offset\": 6,\n", + " \"path\": \"sentence-transformers/nli-mpnet-base-v2\",\n", + " \"update\": \"2022-03-02T15:18:41Z\"\n", + "}\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Notice how this is the same output as `embeddings.info()`." + ], + "metadata": { + "id": "_LJvaPzFzqId" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Documents\n", + "\n", + "The documents file is a SQLite database with three tables, documents, objects and sections. Let's take a look inside." + ], + "metadata": { + "id": "i5_m92oSz3eK" + } + }, + { + "cell_type": "code", + "source": [ + "import pandas as pd\n", + "import sqlite3\n", + "\n", + "from IPython.display import display, Markdown\n", + "\n", + "# Print details of a txtai SQLite document database\n", + "def showdb(path):\n", + " db = sqlite3.connect(path)\n", + "\n", + " display(Markdown(\"## Tables\"))\n", + " df = pd.read_sql_query(\"select name FROM sqlite_master where type='table'\", db)\n", + " display(df.style.hide_index())\n", + "\n", + " for table in df[\"name\"]:\n", + " display(Markdown(f\"## {table}\"))\n", + " df = pd.read_sql_query(f\"select * from {table}\", db)\n", + "\n", + " # Truncate large binary objects\n", + " if \"object\" in df:\n", + " df[\"object\"] = df[\"object\"].str.slice(0, 25)\n", + "\n", + " display(df.style.hide_index())\n", + "\n", + "showdb(\"index/documents\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 619 + }, + "id": "32TmOeRZ0Lec", + "outputId": "895b569c-3509-4f38-c4eb-36340d718d15" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/markdown": "## Tables", + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
name
documents
objects
sections
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/markdown": "## documents", + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
iddatatagsentry
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/markdown": "## objects", + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idobjecttagsentry
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/markdown": "## sections", + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
indexididtexttagsentry
00US tops 5 million confirmed virus casesNone2022-03-02 15:18:40.591760
11Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized icebergNone2022-03-02 15:18:40.591760
22Beijing mobilises invasion craft along coast as Taiwan tensions escalateNone2022-03-02 15:18:40.591760
33The National Park Service warns against sacrificing slower friends in a bear attackNone2022-03-02 15:18:40.591760
44Maine man wins $1M from $25 lottery ticketNone2022-03-02 15:18:40.591760
55Make huge profits without work, earn up to $100,000 a dayNone2022-03-02 15:18:40.591760
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "`documents` stores additional text fields as JSON, `objects` stores binary content and `sections` stores indexed text. The only table with data as of now is `sections`. `sections` stores the input (id, text, tags) elements along with internal ids and entry dates. \n", + "\n", + "We'll come back to `documents` and `objects`." + ], + "metadata": { + "id": "-nmu31TQ4gSv" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Embeddings\n", + "\n", + "Embeddings is the ANN index and what is queried when running similarity search. The default setting is to use Faiss. Let's inspect!" + ], + "metadata": { + "id": "v3SsQCCD7lR7" + } + }, + { + "cell_type": "code", + "source": [ + "import faiss\n", + "import numpy as np\n", + "\n", + "# Query\n", + "query = \"feel good story\"\n", + "\n", + "# Read index\n", + "index = faiss.read_index(\"index/embeddings\")\n", + "print(index)\n", + "print(f\"Total records: {index.ntotal}, dimensions: {index.d}\")\n", + "print()\n", + "\n", + "# Generate query embeddings and run query\n", + "queries = np.array([embeddings.transform((None, query, None))])\n", + "scores, ids = index.search(queries, 1)\n", + "\n", + "# Lookup query result from original data array\n", + "result = data[ids[0][0]]\n", + "\n", + "# Show results\n", + "print(\"Query:\", query)\n", + "print(\"Results:\", result, ids, scores)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ofIHY-pV7kWH", + "outputId": "f990cc01-e235-4010-ccfd-fdbb5692cabe" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + " *' at 0x7f68631cd750> >\n", + "Total records: 6, dimensions: 768\n", + "\n", + "Query: feel good story\n", + "Results: Maine man wins $1M from $25 lottery ticket [[4]] [[0.08329004]]\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Index compression\n", + "\n", + "txtai normally saves index files to a directory. Indexes can also be compressed. Nothing is different other than the files being in an compressed file format vs a directory." + ], + "metadata": { + "id": "s9aLt2zF2ZW2" + } + }, + { + "cell_type": "code", + "source": [ + "# Save index as tar.xz\n", + "embeddings.save(\"index.tar.xz\")\n", + "!tar -tvJf index.tar.xz\n", + "!echo\n", + "!xz -l index.tar.xz\n", + "!echo\n", + "\n", + "# Reload index\n", + "embeddings.load(\"index.tar.xz\")\n", + "\n", + "# Test search matches\n", + "embeddings.search(\"feel good story\", 1)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "0oOC8ToG1pyn", + "outputId": "6fa8a8a7-3831-4307-a818-a4b62f8a81e8" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "drwx------ root/root 0 2022-03-02 15:18 ./\n", + "-rw-r--r-- root/root 295 2022-03-02 15:18 ./config\n", + "-rw-r--r-- root/root 28672 2022-03-02 15:18 ./documents\n", + "-rw-r--r-- root/root 18570 2022-03-02 15:18 ./embeddings\n", + "\n", + "Strms Blocks Compressed Uncompressed Ratio Check Filename\n", + " 1 1 18.1 KiB 50.0 KiB 0.361 CRC64 index.tar.xz\n", + "\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '4',\n", + " 'score': 0.08329004049301147,\n", + " 'text': 'Maine man wins $1M from $25 lottery ticket'}]" + ] + }, + "metadata": {}, + "execution_count": 32 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Content storage\n", + "\n", + "Let's add additional metadata and binary content to the index and see how that is stored in the SQLite database." + ], + "metadata": { + "id": "lGmiYXyqyjtQ" + } + }, + { + "cell_type": "code", + "source": [ + "import urllib\n", + "\n", + "from IPython.display import Image\n", + "\n", + "# Get an image\n", + "request = urllib.request.urlopen(\"https://raw.githubusercontent.com/neuml/txtai/master/demo.gif\")\n", + "\n", + "# Get data\n", + "data = request.read()\n", + "\n", + "# Upsert new record having both text and an object\n", + "embeddings.upsert([(\"txtai\", {\"text\": \"txtai executes machine-learning workflows to transform data and build AI-powered semantic search applications.\", \"size\": len(data), \"object\": data}, None)])\n", + "\n", + "embeddings.save(\"index\")\n", + "showdb(\"index/documents\")" + ], + "metadata": { + "id": "Ef4-Gd8ZtzUF", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 713 + }, + "outputId": "0f290fdc-2bb7-4022-e4a0-1dc54b080bc5" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/markdown": "## Tables", + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
name
documents
objects
sections
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/markdown": "## documents", + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
iddatatagsentry
txtai{\"text\": \"txtai executes machine-learning workflows to transform data and build AI-powered semantic search applications.\", \"size\": 47189}None2022-03-02 15:19:00.708223
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/markdown": "## objects", + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idobjecttagsentry
txtaib'GIF89a\\x9b\\x04\\x18\\x03\\xf5\\x00\\x00\\x12\\x13\\x14\\xcc\\xcc\\xcc\\x13\\x14\\x15\\xbd\\xbd\\xbd'None2022-03-02 15:19:00.708223
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/markdown": "## sections", + "text/plain": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
indexididtexttagsentry
00US tops 5 million confirmed virus casesNone2022-03-02 15:18:40.591760
11Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized icebergNone2022-03-02 15:18:40.591760
22Beijing mobilises invasion craft along coast as Taiwan tensions escalateNone2022-03-02 15:18:40.591760
33The National Park Service warns against sacrificing slower friends in a bear attackNone2022-03-02 15:18:40.591760
44Maine man wins $1M from $25 lottery ticketNone2022-03-02 15:18:40.591760
55Make huge profits without work, earn up to $100,000 a dayNone2022-03-02 15:18:40.591760
6txtaitxtai executes machine-learning workflows to transform data and build AI-powered semantic search applications.None2022-03-02 15:19:00.708223
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "This section added a new record with metadata and binary content (truncated when printed here). The `documents` table enables additional fielded search with SQL. " + ], + "metadata": { + "id": "gcgtUQnACf5c" + } + }, + { + "cell_type": "code", + "source": [ + "embeddings.search(\"select * from txtai where size > 0\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "lz7xwroECzx2", + "outputId": "3740cb3b-5904-453e-af93-5ee98c14652d" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'data': '{\"text\": \"txtai executes machine-learning workflows to transform data and build AI-powered semantic search applications.\", \"size\": 47189}',\n", + " 'entry': '2022-03-02 15:19:00.708223',\n", + " 'id': 'txtai',\n", + " 'indexid': 6,\n", + " 'object': <_io.BytesIO at 0x7f6861408a70>,\n", + " 'score': None,\n", + " 'tags': None,\n", + " 'text': 'txtai executes machine-learning workflows to transform data and build AI-powered semantic search applications.'}]" + ] + }, + "metadata": {}, + "execution_count": 34 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Metadata fields can also be selected and combined with similarity queries." + ], + "metadata": { + "id": "9fOzYXY6DJFj" + } + }, + { + "cell_type": "code", + "source": [ + "embeddings.search(\"select text, size, score from txtai where similar('machine learning') and score > 0.25 and size > 0\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "DXWf90-UDM0H", + "outputId": "7c31c4ea-5e2d-4873-d9cf-d9b7e6196754" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'score': 0.5479326844215393,\n", + " 'size': 47189,\n", + " 'text': 'txtai executes machine-learning workflows to transform data and build AI-powered semantic search applications.'}]" + ] + }, + "metadata": {}, + "execution_count": 35 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The `objects` table enables additional binary content to be stored alongside an embeddings index. In some cases (image search), the object content is used to build embeddings.\n", + "\n", + "Otherwise, it's the text field from sections. In both cases, associated binary objects are available at search time. " + ], + "metadata": { + "id": "XvBaEBCDIUN6" + } + }, + { + "cell_type": "code", + "source": [ + "embeddings.search(\"select object from txtai where object is not null\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "RaJPqDV3I3sm", + "outputId": "3c416f6f-2ca6-481b-dc53-193e89f7da3e" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'object': <_io.BytesIO at 0x7f6863246470>}]" + ] + }, + "metadata": {}, + "execution_count": 36 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "aDIF3tYt6X0O" + }, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook gave an overview of the txtai embeddings index file format. This hopefully gives a basic understanding of the architecture and/or helps with debugging when running into issues. \n", + "\n", + "See the following links for more information.\n", + "\n", + "- [GitHub](https://github.com/neuml/txtai)\n", + "- [Embeddings documentation](https://neuml.github.io/txtai/embeddings)" + ] + } + ] +} \ No newline at end of file diff --git a/examples/30_Embeddings_SQL_custom_functions.ipynb b/examples/30_Embeddings_SQL_custom_functions.ipynb new file mode 100644 index 0000000..c7dddec --- /dev/null +++ b/examples/30_Embeddings_SQL_custom_functions.ipynb @@ -0,0 +1,373 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "kernelspec": { + "name": "python3", + "display_name": "Python 3", + "language": "python" + }, + "language_info": { + "name": "python", + "version": "3.7.6", + "mimetype": "text/x-python", + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "pygments_lexer": "ipython3", + "nbconvert_exporter": "python", + "file_extension": ".py" + }, + "colab": { + "provenance": [] + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "POWZoSJR6XzK" + }, + "source": [ + "# Embeddings SQL custom functions\n", + "\n", + "txtai 4.0 added support for SQL-based embeddings queries. This feature combines natural language queries for similarity with concrete filtering rules. txtai now has support for user-defined SQL functions, making this feature even more powerful." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qa_PPKVX6XzN" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "metadata": { + "_uuid": "8f2839f25d086af736a60e9eeb907d3b93b6e0e5", + "_cell_guid": "b1076dfc-b9ad-4769-8c92-a6c4dae69d19", + "trusted": true, + "_kg_hide-output": true, + "id": "24q-1n5i6XzQ" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline]" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Create index\n", + "Let's first recap how to create an index. We'll use the classic txtai example.\n" + ], + "metadata": { + "id": "0p3WCDniUths" + } + }, + { + "cell_type": "code", + "metadata": { + "_uuid": "d629ff2d2480ee46fbb7e2d37f6b5fab8052498a", + "_cell_guid": "79c7e3d0-c299-4dcb-8224-4455121ee9b0", + "trusted": true, + "id": "2j_CFGDR6Xzp", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "f2488a78-6cae-4c25-985e-fb2dd674a534" + }, + "source": [ + "from txtai.embeddings import Embeddings\n", + "\n", + "data = [\"US tops 5 million confirmed virus cases\",\n", + " \"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\",\n", + " \"Beijing mobilises invasion craft along coast as Taiwan tensions escalate\",\n", + " \"The National Park Service warns against sacrificing slower friends in a bear attack\",\n", + " \"Maine man wins $1M from $25 lottery ticket\",\n", + " \"Make huge profits without work, earn up to $100,000 a day\"]\n", + "\n", + "# Create embeddings index with content enabled. The default behavior is to only store indexed vectors.\n", + "embeddings = Embeddings({\"path\": \"sentence-transformers/nli-mpnet-base-v2\", \"content\": True})\n", + "\n", + "# Create an index for the list of text\n", + "embeddings.index([(uid, text, None) for uid, text in enumerate(data)])\n", + "\n", + "# Run a search\n", + "embeddings.search(\"feel good story\", 1)" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '4',\n", + " 'score': 0.08329004049301147,\n", + " 'text': 'Maine man wins $1M from $25 lottery ticket'}]" + ] + }, + "metadata": {}, + "execution_count": 14 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Custom SQL functions\n", + "\n", + "Next, we'll recreate the index adding user-defined SQL functions. These functions are simply Python callable objects or functions that take an input and return values. Pipelines, workflows, custom tasks and any other callable object is supported." + ], + "metadata": { + "id": "QTee7YMNDD4R" + } + }, + { + "cell_type": "code", + "source": [ + "def clength(text):\n", + " return len(text) if text else 0\n", + "\n", + "# Create embeddings index with content enabled. The default behavior is to only store indexed vectors.\n", + "embeddings = Embeddings({\"path\": \"sentence-transformers/nli-mpnet-base-v2\", \"content\": True, \"functions\": [clength]})\n", + "\n", + "# Create an index for the list of text\n", + "embeddings.index([(uid, text, None) for uid, text in enumerate(data)])\n", + "\n", + "# Run a search using a custom SQL function\n", + "embeddings.search(\"select clength(text) clength, length(text) length, text from txtai where similar('feel good story')\", 1)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rbsEXtysDDNg", + "outputId": "f966be17-086b-49b4-e1af-62b766f1c995" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'clength': 42,\n", + " 'length': 42,\n", + " 'text': 'Maine man wins $1M from $25 lottery ticket'}]" + ] + }, + "metadata": {}, + "execution_count": 15 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The function itself is simple, it's just alternate length function. But this example is just warming us up to what is possible and what is more exciting. " + ], + "metadata": { + "id": "epIV58P1DyZa" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Pipelines in SQL\n", + "\n", + "As mentioned above, any callable can be registered as a custom SQL function. Let's add a translate SQL function." + ], + "metadata": { + "id": "1Iw1WKR6FW3S" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai.pipeline import Translation\n", + "\n", + "# Translation pipeline\n", + "translate = Translation()\n", + "\n", + "# Create embeddings index with content enabled. The default behavior is to only store indexed vectors.\n", + "embeddings = Embeddings({\"path\": \"sentence-transformers/nli-mpnet-base-v2\", \"content\": True, \"functions\": [translate]})\n", + "\n", + "# Create an index for the list of text\n", + "embeddings.index([(uid, text, None) for uid, text in enumerate(data)])\n", + "\n", + "query = \"\"\"\n", + "select\n", + " text,\n", + " translation(text, 'de', null) 'text (DE)',\n", + " translation(text, 'es', null) 'text (ES)',\n", + " translation(text, 'fr', null) 'text (FR)'\n", + "from txtai where similar('feel good story')\n", + "limit 1\n", + "\"\"\"\n", + "\n", + "# Run a search using a custom SQL function\n", + "embeddings.search(query)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "83e8yXpXFh4F", + "outputId": "0b17e9be-8983-418d-9903-b1e72efc5918" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'text': 'Maine man wins $1M from $25 lottery ticket',\n", + " 'text (DE)': 'Maine Mann gewinnt $1M von $25 Lotterie-Ticket',\n", + " 'text (ES)': 'Maine hombre gana $1M de billete de lotería de $25',\n", + " 'text (FR)': 'Maine homme gagne $1M à partir de $25 billet de loterie'}]" + ] + }, + "metadata": {}, + "execution_count": 16 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "And just like that we have translations through SQL! This is pretty 🔥🔥🔥\n", + "\n", + "We can do more to make this easier though. Let's define a helper function to not require as many parameters. The default logic will require all function parameters each call, including parameters with default values." + ], + "metadata": { + "id": "Ck_XTyBEQtaW" + } + }, + { + "cell_type": "code", + "source": [ + "def translation(text, lang):\n", + " return translate(text, lang)\n", + "\n", + "# Create embeddings index with content enabled. The default behavior is to only store indexed vectors.\n", + "embeddings = Embeddings({\"path\": \"sentence-transformers/nli-mpnet-base-v2\", \"content\": True, \"functions\": [translation]})\n", + "\n", + "# Create an index for the list of text\n", + "embeddings.index([(uid, text, None) for uid, text in enumerate(data)])\n", + "\n", + "query = \"\"\"\n", + "select\n", + " text,\n", + " translation(text, 'de') 'text (DE)',\n", + " translation(text, 'es') 'text (ES)',\n", + " translation(text, 'fr') 'text (FR)'\n", + "from txtai where similar('feel good story')\n", + "limit 1\n", + "\"\"\"\n", + "\n", + "# Run a search using a custom SQL function\n", + "embeddings.search(query)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "L2DDJrd0RAaN", + "outputId": "0bb437ec-5c9b-4a0c-fe8a-07f641c94a49" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'text': 'Maine man wins $1M from $25 lottery ticket',\n", + " 'text (DE)': 'Maine Mann gewinnt $1M von $25 Lotterie-Ticket',\n", + " 'text (ES)': 'Maine hombre gana $1M de billete de lotería de $25',\n", + " 'text (FR)': 'Maine homme gagne $1M à partir de $25 billet de loterie'}]" + ] + }, + "metadata": {}, + "execution_count": 17 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Custom SQL functions with applications\n", + "\n", + "Of course this is all available with YAML-configured applications." + ], + "metadata": { + "id": "mTT8nopiRdVH" + } + }, + { + "cell_type": "code", + "source": [ + "config = \"\"\"\n", + "translation:\n", + "\n", + "writable: true\n", + "embeddings:\n", + " path: sentence-transformers/nli-mpnet-base-v2\n", + " content: true\n", + " functions:\n", + " - {name: translation, argcount: 2, function: translation}\n", + "\"\"\"\n", + "\n", + "from txtai.app import Application\n", + "\n", + "# Build application and index data\n", + "app = Application(config)\n", + "app.add([{\"id\": x, \"text\": row} for x, row in enumerate(data)])\n", + "app.index()\n", + "\n", + "# Run search with custom SQL\n", + "app.search(query)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "FZ_7G6M4RUbz", + "outputId": "4eca94f3-d2aa-4449-dc6f-f1091ad9dd67" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'text': 'Maine man wins $1M from $25 lottery ticket',\n", + " 'text (DE)': 'Maine Mann gewinnt $1M von $25 Lotterie-Ticket',\n", + " 'text (ES)': 'Maine hombre gana $1M de billete de lotería de $25',\n", + " 'text (FR)': 'Maine homme gagne $1M à partir de $25 billet de loterie'}]" + ] + }, + "metadata": {}, + "execution_count": 18 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "aDIF3tYt6X0O" + }, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook introduced running user-defined custom SQL functions through embeddings SQL. This powerful feature can be used with any callable function including pipelines, tasks and workflows in tandem with similarity and rules filters." + ] + } + ] +} \ No newline at end of file diff --git a/examples/31_Near_duplicate_image_detection.ipynb b/examples/31_Near_duplicate_image_detection.ipynb new file mode 100644 index 0000000..318e0b6 --- /dev/null +++ b/examples/31_Near_duplicate_image_detection.ipynb @@ -0,0 +1,321 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "kernelspec": { + "name": "python3", + "display_name": "Python 3", + "language": "python" + }, + "language_info": { + "name": "python", + "version": "3.7.6", + "mimetype": "text/x-python", + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "pygments_lexer": "ipython3", + "nbconvert_exporter": "python", + "file_extension": ".py" + }, + "colab": { + "provenance": [] + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "POWZoSJR6XzK" + }, + "source": [ + "# Near duplicate image detection\n", + "\n", + "This notebook will give an overview of how perceptual image hashes can be used to detect duplicate and near duplicate images." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qa_PPKVX6XzN" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "metadata": { + "_uuid": "8f2839f25d086af736a60e9eeb907d3b93b6e0e5", + "_cell_guid": "b1076dfc-b9ad-4769-8c92-a6c4dae69d19", + "trusted": true, + "_kg_hide-output": true, + "id": "24q-1n5i6XzQ" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline] textdistance\n", + "\n", + "# Get test data\n", + "!wget -N https://github.com/neuml/txtai/releases/download/v3.5.0/tests.tar.gz\n", + "!tar -xvzf tests.tar.gz" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Generate hashes\n", + "\n", + "The example below generates perceptual image hashes for a list of images." + ], + "metadata": { + "id": "bkunuMa3gbpT" + } + }, + { + "cell_type": "code", + "source": [ + "import glob\n", + "\n", + "from PIL import Image\n", + "\n", + "from txtai.pipeline import ImageHash\n", + "\n", + "def show(image):\n", + " width, height = image.size\n", + " return image.resize((int(width / 2.25), int((width / 2.25) * height / width)))\n", + "\n", + "# Get and scale images\n", + "images = [Image.open(image) for image in glob.glob('txtai/*jpg')]\n", + "\n", + "# Create image pipeline\n", + "ihash = ImageHash()\n", + "\n", + "# Generate hashes\n", + "hashes = ihash(images)\n", + "hashes" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Qch4TtJPgdg0", + "outputId": "0f4b9cf4-570e-424b-8f69-84dce3b5badc" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "['ffffdf0700010100',\n", + " '78f8f8d8f8f8f8f0',\n", + " '00000006fefcfc30',\n", + " '000000c0feffff00',\n", + " '63263c183ce66742',\n", + " '60607072fe78cc00',\n", + " '0859dd04bfbfbf00',\n", + " '0000446c6f2f2724',\n", + " 'ff9f010909010101',\n", + " 'ff9d8140c070ffff']" + ] + }, + "metadata": {}, + "execution_count": 16 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Hash search\n", + "\n", + "Next we'll generate a search hash to use to find similar near-duplicate images. This logic takes a section of an image and generates a hash for that" + ], + "metadata": { + "id": "Zkexah5pk9Yy" + } + }, + { + "cell_type": "code", + "source": [ + "# Get test image index\n", + "index = hashes.index('000000c0feffff00')\n", + "\n", + "# Select portion of image\n", + "width, height = images[index].size\n", + "\n", + "# Get dimensions for middle of image\n", + "left = (width - width/3)/2\n", + "top = (height - height/1.35)/2\n", + "right = (width + width/3)/2\n", + "bottom = (height + height/1.35)/2\n", + "\n", + "# Crop image\n", + "search = images[index].crop((left, top, right, bottom))\n", + "show(search)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 163 + }, + "id": "csFOHf5Ri66U", + "outputId": "20a8a47e-2ebd-4e1e-d893-df37ff5a59a7" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "image/png": "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\n" + }, + "metadata": {}, + "execution_count": 17 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Now let's compare the hash to all the image hashes using [Levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance). We'll use the [textdistance](https://github.com/life4/textdistance) library for that. " + ], + "metadata": { + "id": "28nRInU9lKN_" + } + }, + { + "cell_type": "code", + "source": [ + "import textdistance\n", + "\n", + "# Find closest image hash using textdistance\n", + "shash = ihash(search)\n", + "\n", + "# Calculate distances for search hash\n", + "distances = [int(textdistance.levenshtein.distance(h, shash)) for h in hashes]\n", + "\n", + "# Show closest image hash\n", + "low = min(distances)\n", + "show(images[distances.index(low)])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 214 + }, + "id": "c40UPAUVjjfM", + "outputId": "282689c9-f179-4bfb-9311-b7e8ca2b6e93" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "image/png": "iVBORw0KGgoAAAANSUhEUgAAATcAAADFCAIAAAC+bSLDAAEAAElEQVR4nHT9a69sW5Idho0RMedaKzP34zzu+9atqu7qR7GfJLvYLVKCKVESJQiGDMuATfnxRbABAwYEA/4tggUI/hP+YBm2JIO0bMmi2GRT6mazqx/1rnvvuee1987MtdacEeEPsXLfQ0pKFArnnn12Zq615pwRMWKMEbzefbQsC4mIAD0igFAWgu4uVBWJCILmChp1DQcxhCuJiEaCkOvD9byuS1ucLqAEHKEUQgpqhfYIV3q4oxdFdEz6ZLc/HJdX7t7aQhUPC0TVooFmaN5dGsQkCinuDQCACOLyEkoYRJQUgEJU1bWfjEtHBXbkAm/78l6LufmCyHcIMEgwiACCqgrA3aSIdwdYSnEPgAyqirkXLdGpLL2vUhjiYFXs3Y0MUUZY7y4C0t1RyqBSzbp5P+yvNYbXb9/sd+Mw1ru71yIAYg0jnWIRcKcKA42svXdVFREzE5GIsFgDUnUHFDfb74bzfIzwQCAgwlLUzIDo7gFEOAEhESTVIiLKMD3E+nTp86T7b3/6uz/+8j+f1xMAkhEQoZkBIIRkRJAE4O4kA1TV8BiGQVXP57OIeDSS5PaLqkqy9W7ueUu3uw0wfPsUasAjohbpATMXkcdPAejRgO3PoCCfETQIwEQEICJAA8zdEQAIgKLDMKzrSlC19t49nABgyIsEImx7Z4hHBPzyE+T1ksxvkn/In5Ik61CneTkDgfDI9wytelO0zusrUYbDca61moVZftA/88qPyBvyP/TKXyJpbiQREHcT4fYtnREk9PLFBEAA4eHukDNJ8VtECT5Q7wATFkJFynxewp0RiIgIBAQEKBShqJZaq0IIFZRwinDtp7f3L1tvvfdS6mG3L1IUGh4AVUO1E4ugR6zu/XIr+fhSKYSIKCGkEkLQLMZyq7yCVYaEm8Dm5W5Zl7zn+f6EhNM98saN46iqpIQHRUj23oEgCETvnRCVUVhE6m/85u/sdrfhxS1EjWKBXqvUWkrZnkIuCVEEPMLvH97cHV9RmsXS2gliZZCgAUEhWQgtWt3hLhGhqhGRWxRAIBSDePFubq0UXl1dDXUMR/h2y9e1d8t1JbkvVBRBEQWEESL33mOY/He/9906rn/2k/+ytZkQQsJBkJCilbm1sa0kCkspwzCMw+jmJNe2ns6nQJj3XG/ueb6j977tczIi8tvkcZOPj6SI5CnQffu6kRdw2Ufc/sB8WM+fPd/t90AnFsQavjIiHGESTkABBTVCCFWWcESg9w7wsL8CqFpUFSGAkBpBUlQVsS30xy+Q1/Lujnr3Qq6u9nlqAflvAlz209XN1TfIcRxdtCv3bY3tXPsf2I3v7t7/vp0MjzB35h0ApK2zW0Oeu6BCBUrkBwSAuJwrITR0x0IIfRIMwgZaBMPRzfIRCigkRUkRUYp6YLc/PL15+uTm6WG8plVGISWkBds4jLVWgOvSitSiVWMovJ7q7VSvCifYOMqzgU8vK2G7NubxCSFVpF7tr99//p6wDMMIAmilzCInAdwO79/+5tPbj/O+UjXvBUVURCgRsSzNPcwzpvnXN1EoIiIk0FpzUKQ8f/a+6qiyU5bwRrgQ67Iu8+oWpOQd672dz+feW4QH3LGEtKWfTusDJG+bA12EbpHxT1WLDo/XGBHYDjtl7Iayr1oFRtiXX3zRVttNh1oG1apSIyjUUiohIqVIYW7RUIYIFGDRZ1X3/7v//f/2l3/lO93feAjJx6Dn7ioqwtyyFKgqwGGYzKL3fn19nYsTgIiM4wRsK7KU8vid47L4RCQ3cJ5euUWnaQJQtGwfIpJ/f9ntj7Fr29Vv7t4s60wJ0CkGmrtdYk6pOpKFKEUHQk7nVWUAmEfDYX9QUXeEU6QSBdAMvBEhKu9ulbwP737614uNdOsvvvo8vAMecMDJIA5rO7148996nHbDp/vhG2CjtIxUeVGPe/4xN3l3c/737mQVJUgK88MnuQaYBxmChARQqwTC3RFERB45KIuZFh5ESngP78oSgmnar/Pi7vv9RMH98a5oia8PYi0ohbVqGYd9HfYvXn5lnME1YOYx1Klo7WsjoFrcA86hXjnMY4XGfLZf/OZvD3X6R3/6H6vKloRAMvMhlBCVIpCrw9XpdLQw+tTxtscJJMLDx6vhF6O+vT+/FNFpnM7nh4xXQvbew6GaQdAoLFp664HYsmiQZHgEUHSsZVyXPk671ntV9j67oZQxPKZp33tf+wJZ3cMDKgXwiO18zPUhIkULKd2axzlMwOJhgIFUrW6Wz9gtHpfN9fTeusyrHx0rQaACWqqu65Lnr4oC8DDVYmYEAobgUCdladYh8tEH3/r85z8LniCtLUFRsgHMvZSrikTASXF3QiIgqplY1lpbb1s1JKKqrc8Aaq2PIdTMIJKLMrdu7waEIBC56iTcAkECFI8AUErpvV/WsYMB8OsQm/9NB7tQIsQdpAdYdffs2bO7u3szA1mkArG207Nnz16+fNmt11LNO4CPPvzs7dtXx9M9JVSEFDP3TFzfiZyP2zJT1sekF5nRiJLRe9+OJHT6VdUrjztIQ9RuTppveTX+uZ35GLRjS9O28w7vxHARyUrn8Xe5L7ePVQPAabd397WdLzudAKZh7L2bVeoRcrJ289Gzv/zk6e2f/Pl/LoLrqyfeHRHLsozjMLdZmXVURESRqqHKQqgb/+W/+TenafrP/u5/0mM2t7V3IoqoSolgLdWadWuUFSIQXF9fn469nUdIPflfiFCo1g1EJuREEcp+3EdgWVdGmHdDD7pgL7KnHJu9QQwi1cPDHQTCt0iMENEsdC+n3ZZtqqq7R5ABVUV4uEkZhSUTZjdHBCnCgSxmMQyDilisc79/DKruNo6Du5l5uIsKKa01gMIAW/gQkFKj+9nD4RpboCAhAEXU3Uc9CDG3BykuZDeYS7iJbInPMFQSHn51dXN/d2/eEV6kSiluiFi6AXARjSiQUwSIiJDLkRpfB8BwAuaeicYwDGtvYcjAa2bDMLTWIpwSea9yTedCNI/LBXxd1sKNyEqZER7hojJM07Isj7X35R0sCydutbEgIqKCQqyg5T5y74Ag6jAMrbcIELy+vr2/v3Nv21mTdzkcENXB3RBOcXcnJYDHulRV85h43FHvZq3v7LQAEOF5iICdMQkHx0OEAyMRge04fndzvhs5t+B82ZZfvzm2PCzXyuM2LojLsacFgTBTUURQBAmduG9lUhf6JFRSX9/9KOSZyCDqd3dvBCrUIrrOjZRLqeEshaAD4S5RiPLH/+RPf+EXPisU96J65XYKnCMQgWnYjXWcfRmGer/cCWp4vH77AkHq8PTJ++sbNbNg4j8b4FGUu2nXWo9AVTXr4zAc/YUHwqvKME61HY8Au7Vc+GBcLj8ADsOwLGsEVUtWv5fH43nwbaFj25k9FAiZhgqR3o0squWwuy5lePX6pY5VRdj09ubazFtra1ta6+4GYSAQQbqFiYiDNa60XD25ff75Vz8MWUmASjqA8ABRSjHLJdWHaTo1D4sW5hAKVejmIgTZbXWPCHv1aiWEDGTp2FHLuN/fvnrzAugWzvhAcAv9yq0w5DG5ywt3h3tIJr4ABd0awykl75t8HSqr+cqthv8aGhnGqbX2WHPm/2fpgOAlOCMi1ne26OUf0x+rrW1xBxhPb297s4fjLCBQGIWoDg/62maCKlJKOZ3vPZoIQUd4RGDDGsOsARHR6RnTHORj+f3unsmry2/1eLGXHxEIQC67ToQMNvcOVISA62M8zoL26+x3Qyu2LP/dwmr7DuH0/EUK9etdvZdn+TwuTygAQp2gRxCXSB9xu//ksN+9ePVjR7e8nSJASKiE7MZ90QpwaUvvDQySLIUOhDA41utpuFb4mzcv6igevLn98Lyc5vVLQARSpCqLsuwOu5f3L9fWVlvBtagjWLSe2zmBH2wghJJCj1oHDwhVKeu6ivLUu+GsooQGzh6NuIa7FpairbUMcYgABJT9bj/PzayLUFXCPR9eKaV3Z4CkQISS0F5RFSgDiBCViCKsIhrhEa31NSikXja8exgYHnaBLuFuFBB6O36j6M2TJ89/8vn3l/4CYuEaXAPOkAgOw2jdPAJhQEAIoGdGgCiXJLX3fnnz8ACCRAjJ/CbB6+v33j587h4W85PDb+/G5z9783fhO0XTUnBBgFQl8U+E91zWMJFEWQuzSJfMBbbcGAj3yDZBlvoeNHdEZGWyBUn3bXWB11dX83JubQ0hKRFOEUTknQEIuG6Q9fZSliLS+kyqW7naPx+G3eu7L0LOEVBVN1ctAfTeRAgYEO49AqSoZk6eIAvMglTk94+Mt18D2vgfeL0baYGsBVflVYQGHgKd2BEItMuBhccMJdONS3DIk4PvvufXnwIQqiqPqBWv6kckA269bzkgUHXIZ0ahW89ToZb9brd7ON7lx0cEBQGDC13305WKLm0F3L0jMO124ezNGFJKrWUoKOa+9plq5jbWq2VtgBWViC4Cc+ved+NhX5+9vPt8iaNhFUBREOIMC4O4hym1soJiCLorWbUKS/dMkVSKn+e7XNAUeDgvGzwihGU33bTVFzsH1qEyPHqLwt3V/rnH/DC/yjogi6KiBVEgjWD0gXJU7K0rdRZciUzgKWIEOqUX3B72V9c3T37y8x92fwt0jRvl3niXwIMK3aPWYV2tu2mJorulzYU3IWfVmJcHBEUKQnL1izBXhVDMenA77LP+UVH3ALdwkec1orpxf9ifz3eQFk5h9XAwBJXY3d4+u3+4a/1VJg5b+IKL1LHuzfuyztM0LMuZzBosBANcxnFqvRlWc6MnsrZhwub96nA9DPvXr1+LeIa1/NrhvCSKuttdFdX748sgazkUid/8tb/6+//ovwhIw4PGmA9KVLb+AiM8rq9vTsejhTNvDuB5cgFFi8fW/Ag4GWWYWmuBjojCEQHzOSJUi2rpvVOElGbrBSyAmcV2RGVxnB2fQCKVsRVFzDOLFJFaxmVZAAs4EaRuVScQYC2DiCzrDGRpAIZGgOyRZ9jXIUf8HXT0MfkXkW4mbt2sW1bDwrzI/tjqCZBbNtJseXv3JgIiejkHiKCqqArJ/f6Q2ahqEZG2NGtdKczcy31d19YaIQgWKeuy/O7v/N6/9bf/7YJJsYuuYSocWvPe1qKisqGlIqpaVErZ8rCtUVC0FFVS3NC7Y+uhBGBhPfH+ImVL/5iAhRSOjHJ9dS1KyqJi64rbq4/+5r/0bwj0vLye14dsFCOrKNLN3R2gW6ZjMg67b3zyLYSCFL0b6/uQNwTdxaMFsNvtVDTCETHU8u1vf2scx4ggtg6hUACOY+k+z8vRbL29vT3sp2WeBaXooFIisN9flVo93D3cMg/Mjp1nMYJAa11ESqmXox4kS1EKwKCIB/JIFYEKnHP3u7YuCBOhqqpqHgRCAcLDlnUWsncjBCERDAjgEX2ejwhjUCCXTMGzIFXh+Xw6n04qLEW37NERDkAuixKttdZXABHW+7n39Q//6A8IZPcVcGTOu9U1IRBVfTg+eEY8wqN59Nw3CJo5Au9spu3XIxAev/SdX/r0k294eN7STKZUSzZaPbK+gMjGDvjvxtCElbJ8zRw546CI5HdISPaf6Q6ACJjbFj1dyCGiEhWoRCEKsH3cu1sUl6w7P46AEMHww26XMJMwBER4RucsshNNzaf5WArHO8WGCFtb3rx5nQWSUISFVNFCqkjJawjE1tV0RWiR+vLF6/loV7unh/H2anq6qzeVO8Vo1ksppMCR7ZkipapkIw8BIYW07gyI6P5wQ1bLNBA9evIjYG6JGHFrqRSgfPTRZ4fDzRdffr6uR+EKhmJ8uPcvPn9NgfsD0UnFY58AJLZgdmkZFLI8f/6+SiFW8/Wz9/7lXXkv+u3vfe9/dPOkns8P3//+P7W+1FJLqd3Xn33+w9YW1a3733s/nU9kUKiitdRa9fWbF/f3dyIJsQipAl2XVnSDDy5xVbKFlFhx1pCt2TIvCaG6wcw9eik4nu7dnaHCIiIRLKWSFMF5vVv7OSLMLLJeppJi3eblpCKqat2w7VKpdecBj/Zr3/2l/9W/+z8fZRRXCkqlh3kYk24AW9o5aGY9AoQKJd+/1IGkCM3asp7AIJ3SwuM0v3WQNIkKZg9ZwvOGi0cMw1i0iIiK5pb5OpcWXoCWLa4KZVmWbNoL+dOf/uTFVy+E5bKDEUHb6jYR6hZIYgtrQk0YB5du3T+X9CbA5u6n8zH/E5cGb/6rDHLm5pa0CpAqGP+P//7/6a/9zu8hRKRe2tr/PUlvHhzuvq4rsi7dHm2yNLglhKUUd38XhiYlLmfbY1DOY6+wqlQGPattYKgDQTeQ6haFqlV798N0TeI8H1WoMngnQ4Zaa9UIZ8F5OYFu1lefO9dlPUawykCn00GHePcWzqIDQ7qvgHz2yS/c35/Opweym69PDh8f5/ul37tET6w5OiVq2a1rDMM+vK/9VEpElng+CccIB1eVRihYm68eToKRjy2yNcAgQsnixnHYf+PjX/jJz/7kg9u/+fr4X51O4/e+97un4xf/9Pt/eHNz2/t6mt8CFi6Agi3eoSuoiAdCAFr4ILp6Hz0WEQ2XUvRwuL6/vwvY9fXV3f29W1eVR55DBqVStfd+Oa+3gCaiHqi1EGi9D3Vc13UYhnXt4fCwRKdURxHpPuNSjKkqidY6JYRa69B7D6eZA4AK0WHrYdo9f/b+z3724nC4vptfZ0kpor3bpTjvAQglmUDuIaLjsF/XOdBiaygEJUvQTuzA9uTmo1dvfuYxFDXrwa1rw+yObijOOw2MPO9KKQTX1oYylFqWdc7fCgMQ7q3FUrEDBbx0Vih5v7SUZv0xfXgMYmMZELFaz6wEcMuK6R2E7PKPcdkgW+sod1n+SHUg0dpCRqmTGb/zC7/885///HR6E+hZpF8azP/d+L1t9UBIwlwRUUulaDgJ+RqbCogUkSJats7HpRTOPrVqrWXUUjaenZRShv101Ztbh2qFYyzD1psKj4hx2hcdi05FR1UVCbNVRMz6fD65t95XdzM3sy5SCWHwO7/4nevdraAUGZVVWDIdKiwK+clPf/zwcCci7oAXwSBRRAbhAKiwqkzCsq5rUe1tMWuSd8BH+AB2yAP0BLro3iPTvw16EdFCjYiwRENcSBGvg5n53f29UH/66v+52AspXz28fb0uIorz+Wi9C2qRaRwnUZCcph0gmWJQlMkrckGIu9dByRiG3W53FSHLOmsB4Hd3bxFRalZc2yuZUuvaIpLEUIRCcKgDKRGsOrkXovbmeSrXMpa6e/+9j4e6J+s07Z/cPn33LHZPMFIFgkBvLR/41eEwDmN+ukhZlvXHP/0JSC0qrNZQynR7/azqtBuvhroHIKS5uQcgqhJh67p4ZAsNpSiQTaySjQYPfTiedtPNWPdkKaXmSnXzR7x0Sx0jHr9zlnPdrGih0MOT6mUWpZRwPHny5N/73/x7NzfXgUuxACIw1GG325nZpfertQy87JhS6jBNuQH+u7no4583usvGPNg4qpegClLcLRvLAHtf3Nd/+qd/eDq+jegBo8S7vKMLIvU1nrz9PSi7cWSQkLFOVQahPAJyuUzzt9ywbdtLGr1luiHJa1EpKqWWyS3zwy37YdAspt1hvz+Q9MD93VFYhQpDLUP2D8/nOelt5tZ78zASQiVYpMDxr/4r/9pv/8Zfm+o1vIRJst4AqtRaqyrMF7P25Pq928MHt9c3vTezbrEGPeCEiygQ+8NAaR4rCbfI+pDMHpcDMIsICY+xjvAQUCjjNCnqbnw2livVCigQqkFZ3jz8qcOHoajcjJN8/wd//09/+Pch5rH23oXDUK8Oh9tkIiZbQTmIbKw7QMhCDKUMu90U4Ecffvrd7/4lc1vXubWFydDJ9cPQkiXS1vt+zAAvdZNEnqFa59lurz887J+5hypIJ6Gi93cPolprPZ2OX716keQEXiiiZqGicCJgPbx7eHz00YelCtCBABVSKdVprx9euXci2rK8fvPKzXrr14frzP1Uyro2Eu4eCI8k1nl4kocYDusWJs4ekMVOvZvbHBbjOCY+BCB8w7behVgfKYe4LG13X5Zl2yaAWyC8iv617/3Ofr8vWlRLZrAAzPxCity2ul+yayHP5/l4PKoUUDYACVsy++4Wcvdk10YgnIf99TTusmr1S775zr8PiislMQDNBRxfd5Ufz9+8tHePA97IB/kjM6fINE5mPrfTxmkGVTXrHNF4bB/lm5ZSYIKgUsa6M/Np2i9rC3ezTnAapqK1tYTlTJSMochAxFAKQlS1tUWlCFUU83o8t2OgB/vSZ0h4hIqOMgiq8mq3r3fn1+f14bHIV9Zpqqf5wczc9YOnn9Krr8vd+asZR1fzABxEABpbxzlxcAF0GOq6LgnlRzDCRaCoRXW1ZtFVFA6hRuiH7/9S+PrV679QHlp/oDbidhxYy5Pe6L5QVmBc+13AqgxhWmQ6XF0/nN8u7YgwsliPv/W3/tV/8A///ts3r0WkDJUs8HHpX4YLBb0PRVXVu89AYGsViHvimRSqmatqBESSNoSNxAhvfb29ue1d+qq/8su//tWrL3/+xZ+JzI6OqFUPQqX4ss4ZmTz61s2BiBSzTgrCc6203mSjc7txpWuyO4EI9YDRnWBEri1NSIuSKd/WcwJdhLHxCMzMiSpSRGm9CYtxiRggi/rkcWaMGdNJuNsW4sIDX7PhH/k6JBEb1hKIhJTdoVCJiFgDJmUC1by/QxVg3lVsAoNLtKQDuN7fzOe5efLPPMIdUXIDvBNXI0Jkq+fdMI6juZn38JaXf0k/I8IdHRD6ICLunYKAAh5o/+wbyuV6v/4sXuv7AQ51YqC1vtuNIvLw8FBqySOnlIIIMw+4qiCyj5p7PyHD4h7TOIXHOE4icjyeI1yVVUblVMu4LnPASpHWvOhQpUbIMIzuEbFGeNWb1t9W2X3y/q/++Cd/9OzDfYvlxz/983F35T3qOBfsCm9C+mm9P64PziSvBQghLLLXV3szQKvCojnMGWbd3VVQ6jjPbb+7+uVf/pU/+qM/NGugKys4h5vKrbsbj4Qoaz7LXBYIJKeHzDAepejaZjCq3lzv33O3dW692fXhNgx1wuuHLzMF4XZSOiCCfXDtfd5N12sz8wY0HcZpN83ntfdOiscc3gkpOriHFva+DnWal4CsHkZAhL0/6i0ki2cgRFjL0FozW8FEeccIC1iguTmlkhBlNn57s0AASXYrgnp78+Th4a7ZLMqsgd0cEGbDXT2CQnHL9gRBCCNFOdxKJ4jQuqmMhIA9whydEItOlFL2h93t27dvwK7FPCwCARdKbH3ECC9Fi7mRHmGqNWNdqXUa68PxIeBCLVpUZV7mfy5jRFBR3I0SAQ9EqUMAS1u5aaCUKERuyNvzfO7WAh6JVyM+eO+b9/d38/wggvAoOpDaY9nQ4EuUyiD8+Dd47KAAiCipsgpHhLlBnKLwEhHBnh0HBDOGeEBE4CxSgGi+bC3vAJioF2Bm0zC11pdldTeRhNcopPceW3/fthoZqLW4IQCVSoh7X9sqlGU5m1m4qiZlH+Ecdrtw9L64+ziOGkqqagUQbh6rR4TPEavq7v1n79ny7bk9fPj8F45366//2r9wf//mn/7J7+s4CWUYd80XaQJJfowKad4lO5vwMgCwbp5FlFsCD9yOYWHimUgljZAhwADYYX8wj+NsZLhbkbLlF0h6iggBmKiax9pWEhH0OL+5/7GqKKbr6/ef334QIffHl1dXBxG8efMGJKnuvDrc1rJ79frnpWhra+4sLcV6Ox+bmReZhNpcHCZi3bTqHtFErY7hzrVjHMYEY5IXntvj2ZNnx/P9uiwR0fsKULWkCFHERXSeFwpVBw8A0Xv2VDXhViCjtANxd/fGo4mEubsbt+IqtJStMRmpViAgbpZAaEQncz+QVDOngIxapHWAplRKeFeCRCztvNsNS1vNm1BBMDQ8SM0FT0BUI6Mu6W4RnMZd5rSiko0Wszxlvt6fmeh5+K/+4q92X//sB98HpZTqER5BCGkAERzrUKqcz8d5nrP+FaXnSgFefPUCDFUhosE++8Zn+93VH/zxP9R3lD3/3M7MnZEoLALKxG89sn6mBJShHgnyuYcxCiEM9fDEQcDY9Aao26UhAOd1+RAAQhSipZIX8Pedb3MROvbkRr1DDhaEPnvy/Hw+r+uKgEiQnMZrhKxtKRymchUhV4fD+fzQequlCpjtGTh6b4FFtSAGs4fK3cgnh/3htMzjdHB5uL3+xrKcX736ydXVVbfzw/z21O4b5h5rszaUuqtXva3Nmou596BZ9HAlqSrNeyaKEY4Nq5B89kQEbdT9UK9IX9bjOFw9nFbqImwMRiD3Q4LbIkJI7yhFPVazFZCio7mLBCFTvQK0t3Za7v/G3/jrv/e7v/sf/Af/Qco1hrpbm9Uqy/pA2lAnd7S+lKLNVvNOynvPPnrz9r638+F6Os/3YcN+etr6vLT7YaCIzktX1UxqhEmjc1KTp+7REz1O+BSZA4Iku7ULK1hLZd8albm2GAjQEFAZ3cJi3RhlkWeBADTrKY5J4CazuFwbRAU7kBtGw/Xpk/eF9Xxq77338Rdf/LTbOo51GA5vjz9SldbX8FAV8w7GJX+Gu9daRWRZlu2LRQcR4ZcDtnhE8v5AZAzIyjJSGpJsPZLQ5zcfBvzVm68orENd1+5AoEd0MARadLi62r99+8q3tZAMFlgEBQgBgvAMZrWMAJc251n/bt337o59VKVya59udOVtL6N4iCo8VseqrO6DR1QQAkPPZBvIj9WNm5JU4Zv6kXsULWFe60AyGZiqMo7jPM9ZA/BCT8kzON8uAsJ6dbgx68s8Z40ExFD3CLqbQDUmoqhSmS07llLc3C12u8MyL9bbNO1Eta3zbpgGXpm1Mozdzq3P03Aj3Lm8gdVlXTvnu/Orjnm2k3kbhuFKnwByXk+G1dCazxYN2OhmECSwnDdUpCRfjEAmVIJ6c/VRKXz56kequ/3hg/uHnwOLoFwq8+S7i0oRqQgpRVs/zctZoKA6gehFFFERQsZ+v5/PttuN3ZaI6M2KjvvD1fF4F2HdVlXJ/JZE84VbX70SVQp3U+3dVdGbh13BJ8gCfbOsMx7zTGHvXUiodjN5hPkCWyEAJl0hwrslYU1S7O7RL48S7slVMCFVB+8+DNr7EmS2VfIIyKQ3ghEW4aWWy9mHVCt4dAKA1nIYh915/cp6ABrhgbbfHZZlVdmXKvN87LaSm96IrBkQzKzWUqqeTqcEnEQ2VsKm4QoFabbm5WdPAYyt+edbBSvCokNb83tKNswBUSkWoQXuXUJARZh7Ey2PNDsEw4OyUSPcTZW+MULo9MfuyLuvd6l/+SqicUGGRcSscdOkknQghfvDNz7+7l/+zd/+T//T/+tqi7MHPRCEJFyAkEDqOlwuchCLCPcUy0iKEJZlSQDwEtk3icDjd0q1wf3D2/N8yqMoQcfem9lCsVJYananOwmRQqpQw8MsevNx2D25+mBXnylGjZ1yNwzjfvdEpUz1Zlf3V+PTJ1fP1Ke+8LC/+eD5x8o6jbsk/RapQ90XHWqZBCNRp3FfZHiUiuMCfl7aKpnjBQhVESm1ltbOr159USopdjy9DiYjUgDNXX3JawKOodZlPrd1LSySZZ40EaMQqFoOwivE/rB/djquvbtbM1+o/X/xd/6nH374Ye8eoVkylSIRTMwGwM31zX6/d4vjw3mdpbU5MBcOn33820+vvuNt3O+uRAeREoYIZjtHWW4ONykMprNKUcqT26e1VvdovWWzMVFckr1394wVGkGVWmQSlHCxFkWHqlOEUrSUrAb9+vr22bPnHhHhwzBpqRFBlY0hkIcYa+8Ay9/+239bVZe5RyijIhRezRBebm5upvFQyvT82cdF90R1I0KK1K3nua6n0wmBCN9Sz+3ec+Pov9OrMPdNF7VxjB7xpDDvwa4Dgx3MZN4CXUgBiiiC0eE9REo2Lra1AnJDXyPCSbiDEAAOvywh4UaiyoAPM09OW/708iNJrMzdAAEEdEgLZC+qOIroFC4IBDw1JIQgVEIUJmKiBnYgeFM/cTdJXSkFqdwPC8Rut0vpPTfWeGwBKbN/T80XLsGdVevWKQpJMENQbg7vecc8n4Yy3V4/W9vclpXUWiZh3Q3jqIeIenf8gqhFuRtr5W33dao3wbcV72mV0/n1sjbogmKvHr5wXVefj6d7ESHKOExX19en+bTa/HB67d4cPQNLyD+jD+I7LiFb0e+1KIFOIi0cPJqgCGqST3rvubEJMgpFI5x0BMbxyj2VqGvWrnUYhLau9o2PfvXVm88fjq9Vk7cm07Rf5mZOFZrPYEfos2cfre349u0rkvvdTdHx4fgQYdN46Hbffa36/ofPvnue79/e/3h3rXd3d0L+5m/95g9+8BdgvHz5cqhTKWq2isqyzAiIah2meZlV2foCuGpJohwi0tqi1NpbN+9a6n5/uLt/GQBN3nv+4W/8xq//vf/3/0sKNn0jBOAwDBGxrsv19XXrLan2tdbWmlsDFSy/973f+2/+8L9Z5hPCOmaEqpRm626qDl/mlZgiZBr207Q/z0fQRNCW5uGijPBLSRUUBWFJKgABGYZpXdaAR3Tya5OC3Jm5b4dhyO+cPwvE1o32zJ4kYtNrWecnH362249/9oPvQ5JClwe5IJNVCcsACwTojJR5JDaWqcrXsPClfBDRzHE2smVE6jcuZ7xBUMpoZu5edOq9RvSqvfsKcUcIhggNt4KAwNACAbAAKKXEJhABAFHtvZN8vOZ31/flOPNMdIJRigLiHo6Q7GHqKElmitKWKDLsdzcMPBwfrK2laCmVFJXBLbr3UodaFV5Vw32tO6ENa7/fjWLt+OEHn70uD/PLE8F5XiiKYC0DUSK847Sez+d+NHPVYCroQzbO7vYU864+JupfCxefPf344fjSfA2fwAjOcAGqaBkGnedZdWuBcOu0ZbUnZu6pfOIq4taNrIWyrisifv7FD0W6qoMpmxnXOSjywfMPX718ya0Lx+W8dnOVgYzz+azi19c35/PD8fyy6Bgxtnj7ky/+C9VS6/54PKfZzc9++vPzaenWiw7W89SXtnaVaua1jColHAbLYtLd3SKjQGIng05KS+nS6zcvaqWbqQ53d69///f/awICgmLuHj7UQYWn05nEw/0dkuwm4t3piOgMEZYf/eBzmrh1sAkPh6un67L2fiJ0UF3jCGm1DFq0NXOjFooYJGKzLvILHSCJCbIBkIA7hmFqa89sbuu4bJy+KKVkIMnlitzo0E3sEiBQdCS5rqmaJsHT6WxmysHZMiXmVgMHKeHgppBMVJKXVHErSnNRbcvq8jeJcqnqUGtbGyJKqe79EiQqUKbhZl5OESfKKjoTJesySGp4IKH/yr/4b3z5k9d//MN/HPJgnAGWEkSIb1QoivODJ++drb29f9PNg06Fe7/Qo3KVJwinId6jhyh9g6hUSGbnJkopCu0+WzQJVI6DTOVq31u3ZrUOCnHDfr/76L1vnk9Pvnj5M6m9NzmeH4wOc2IMX7r1yquCee2nQF/6/bk9DLsa2t16R3isYe58a64qH9Lvup+TNhQRyiIirXdHMrkTOIp0Tmh+nxgn0YQaafUSrHWcpvF0Oks+PEYAdvH7CgdRzFopQXZyo3HOix0Oz1pr5ktAwEG1tLWFEuFh/Xh63fxeNQBCeDrfgQ52c5/GPamtzaSFssMIIpzktNuLDMe7I8Nvnzx5e/dmbQug47i3PpstpdZ1dVKmaWjdVztK6e6drkPdW+C9j9/74oufIPzjj7/x5RcvHo53z58/O5+d1O7hvlDEEWSf29kRo+5CTMTcXVVF6jDsurfwSBxSpJjJd//Sry/r8U/+5E8dy08+/3PRoJibCzrRWj9ridP5DGIYhnVdWOjezucjEObwpVE3SpBsWDrSxQmItHEQEcLv798IWFTMDP6YoD6SAuge4R6EcKMDBRSRIS/cWkTax+UhG28fvtJzJQPhCiglLgkXQAsPdrADjBAEA6aYlNX4ljF5LColFchJA/ZwIQIe5ksYRQLhcFxibyBUYmnH3hcQZh4wD8vqGpF6Gmop7338wZdfvW4whjJGMPhMP4GIg6RULb60b37y2YeffeP/+//7/6SRYLgpBIB5EsT0UQJLpsxioA/uUQRDqaWM87wiWEqpWs08T8hRpoJxv7vq3USi6iAYhAXOm+H5OIxf3f24y4yo1/unzRa4VI6DTt/4+DOE/ORnP1717cuHn311/+PGxWC1amuLsRFFeQjce2CUDwxfLr1F6OVLyubDwm7mWT+kYCFdjvJRDqmxDCpVdejdRJAsqHS4EalkySwrlZ8iSgbgAN0tIIBmyaBFnz9//vr1awDzfL66Ori5UE7nByCF5hQWUsxn0CK81h1R5vmsShlLRETv8DCHlhrOQB/HsbUl85tp3EeAsLXNQLS+llKf3D559faOoh4rEQqaUaRMu9qWczhK2X33L/3aH/2Tf9z6giBZgmK+qopFQ4SwuHnVAqJbE0lLCqlDdbr15uG1jgiY4erqBvDXb14OtVLQ2gKkks3c9Ze+85fv7+f7+1en5WUZepudrLc3z8zj7du3t7fXva2n+SH966x1UYGEwyjaW8/FFoFEm60neO3hm46PTM/LJCrKuq7TNJVSjscHAZLEUks1N4R7GIS+cTAeHYkSSSaC7jGOu+fP3vv888+DFky9riI0IoAu0hADvAboWACUS1krwkvHLh9OgHykFr4DOMWFPJjC0UcNqgMIClER1Txt1DzYUhAnwdg0h8qAU/nDn/3ov/r7/yUJuA9alUWkqNRUhyRXK88PQgSVVqd6uDncwNnW1uaVYCkqgJkldhSGcRzLoPMyJ80AEt3X7mvDeur398vrznZqpzVmw5oM16J62O2/+OLLUooWPS0P5/XYotmG+2+K0Q3pi0EwRKzDMImURylWhPXeSBQd0vFQtRLqDkIJfXJ1+/EHH27fKjwYdSygpQZaJHfjxjuptdZaI6LWkoc5UWLTf3ZwDSyUPpTy67/2a0VJugqW5by2eV1XBMmqOo3DoZbdOFzVOpXEhBykfPLJNwBaM3QXoBZ99uSmCIgOxLou7paF8Xk5rm0xg5sUHYdhiojXb16TCId3KWUchgmARzuf7yyamanK0uZLa4pXN9fDWALhbnDy4g9g1t07wpPiL4rWFrO2hTs3M1Pl6XT35s1LkU2fHACogIaPxPD6zYuvXv3kdH4bwXVRUNzt/uHufDqq8Hh8WNY5EsaFfPLJN0RKhIRvrbJE17M9k7tLZNiN12SJzRgxErH3TRbKtvZ1baqVore3T4oOtYzjOLkHqUlxe1SMuG+wq24CI+3WvvrqBQDSBTKWPW1Ar4yiLPBrEiyLsHzzw9/96Plfcd8zhMFwmPtGEt5QpVw5G9fv8tpEPNnMyuslNQ3TmNuVVjQoa8ga6KSBLlpKolbdmqfGqSgFKlKkuIWwSMq4QlL/NQ6jJicAkBiGsvPug45Vi1DcNwOBCCQ9RDdA31TqOO0DCui6tm7dwoAI9mfv3ax97d0Zcff29Xw+DrVcH67WdT7Px+snex3w+vjyuNxBPWjpPatSGIcICTmDa3ifduPTm88kqmxcYqeECNz7uvbUCrW1T+NuHHbu4W5/9S//lf/Zv/PvWDachAY/nR780qnPaMxNyoTe+zIvQFyKdlqnYDAThGwC7PBlWf6z//Q/OZ+PbT2LOMKst9bXm9srVVrvIry5vTE3leq+KSQJOR6PAFQkwt3sO9/5xX//3/8/qOSJqLFxXzwiNcdeSt3trgKbKpqbaZCJovd1nmeRMtQJeSqprr390R/+4dKWIIOcl3ld51pKKSVi85ARgUevQ/md3/mdcRy3ph+RkG9kdAtvfe29UUI1SapMq41wIapQv/zqi8PVpCqEIvTSB7JuzcOyIaRaKCqUYRhVilmIFJWiLJK5dbfY/KUUwWVZhXpRk7GWmuLVovXm+tY9rEd4ELIsC8FlWasO3DZnGccdLwJMUZZakm6ZjFwA3VaREAAe3rkbbz764DOBwkXxjNIRqrJ/fv27n330W3U4D2NVraTKhjZTIKkPSd+8/B+CAiU2Gqcwf1okikSCxsLEuCQcLUkmkpV6kE/rp5durJCp6/2aSAHQzbIEiHBV3e0OEVyXtVsnfdTD86cfvPjyZcCApkUFQ6rvitZNRCFVKBJadX97/fw8r+5tN9XT6TjWnUKj234/vbp/071/+v6HD28fmvX9dP3es4+W8woSEi9ef/lm/eLUXq986N4QqixCtJQCu0tMHi5iyrHHKhLd1kBPFMAdpF4QbIzTEO7zci7U3W63tqX3FcJhGJe1p76Wl4Nwt9tn3zgbdwi4x1BGRwxlX3VHxtrmbmviDGYuUgD3aHlqPwJwpZbeDcGIzcw6EFoi3HoPQvM0fayndtO43+9Pp/OytmSi6gaHOimqOpTDs6fPf/b5T7Jlf+mROOgq6o373dMPPvjwL37wJxSvRd3ZrTuaiEQIII9Gx9yyIwHC2iqq4zD2bmYWYQE4g0GKhF/oPgn5XPDz3jZ0Z6gI06Xpr/7qr//oR3++2n33B4kCaiQh0L0UdbfMNlWKtX44HCByPD9cXV21dZnn5YJTirsRwze/+e2f/fynra25iCOMlHzPFLJv9kvulwWsT26feNjd3RswVAetMp/PeZYBUJXNlihrY1GhgBHe3CNcb2/e//DDT3704z83O7qcwstY3lv7g/mZEkV2gD5C03FRqG919TsIcHarzGyLmEkepRIUhbsFPFuiSL9oisg7HJKn5RvE1/2Jd7sUSuVmd+8g0khiGvcAU+sAOEMYJQKiJK1oMRcJ8TCVolQzljJMwxQebY3bq/ee3D7//IuflApraylDLeN6PkKiuavqAAkzHYah7Me6K3V4ON2f5gdH71xnuzu2txAIi5iqYMYbePVePvngN7qdv3r138IHZ6U4aVRf+3rRK5Ggark63Lx5+5rEVp8CZn0cx97NPJTVo3sYN0ch1lLNzNwiIrOjMNQ6mNl+uv7wg09/+KO/CBhBLcmeaR999NHhsPv+n/6TUsTMhEoVtzWfg2zMEEbAjVqitUWkABn8vQeLiFtHxDhMw7S7e/uQGsPdbnC3dV03hYALA45+kUGFRXhYwBAiUYvunzx58ubtV93O4zgQ5bScRJPrUYTikf3hi99koiBmwnTTzHLLkUDW1hXc+hClSm7jTYbiDrBoub1+dnf3xqJ7mEhuJIZbKYpAZs6XNe0REGgRnXb7AObllMWbu43jeDqdRFJVx6GOZtb7SqEIRGRtLRCl1EwDdGvhWHIR3PG3/ta/+mu/9t3/8D/8P5t1XFTjIvLoS/r17sLXfR14ZO9FZXCHajoOrMDuavfpeX7d+hGcL/4p/XFzRiQX6muKEr727M8fbZY/4XLY3a59Nl+23FuYCV0EEKylBiLLLr4/fNv9UU4uQUS6rTsEWkvtvRk6GCrFcjSDDu6RuGZEVtj0aJdVPYln91dUaoQoi4omI1i4rzoC3eIs4oCCxdox4ChVWCbR6H2Yrj768NOXL16vrXVfzu3BvKkMi9+f+51HMOphvKoqb+dXACjcDd9Q+nH+i93wiZZyd3wVsXq0jgaIqF6MV5movQj3u915XcOdKQhu9v6zD0SHz7/8eakyTePpdNyeogjcPZyUUoo1B1jqsLWjL9hBqcPhcHM8nr2zlFjbMWBmveiIR1LQP8MC9YCYrb/xG792Os4//tFPLLpItMQuY+PvXR1uj8dzqSrksp4jTDY7WVNqKQL62hYt4h7dnFSKRKCweprjcjFf4YUqFgZ0QgIlAkLPQiDbBu6mIrWUdW2Ebtgsw2KTimb9HClD44VtCuREBpLhXjh064nTubuoZFY/DmMpejqdskHq7qAnq0mhDgTcEcjaSkDCrEf4MAy9WdLPt2JHpbeWfHUVNTe5HLrWjYiNgSBSa1nmc6LHyToQkaQAc3NXS4m5PQZV1TGwxkZi04hIz26JG8NbeIXeIXaKW4v7vOp3CAX4Wo1yke9crP+cG4FJiVrLHuhrf+DFmrA/Tse4MBYjnAxRlKvdzaB7lYEQAQUsIcoiKBL1+upplV2VsfcG2Y4HERWooggruULOxMCY0kQv0SwPC4Chz568X8s01pubq+cq7K395q//1U8++E70SsQ8v+3eAsEI7+t5OXX33bD/X/8v/85nn33ssQZMKZ988mmLdVnbUA+1Th5+bqdGCybbS9f1tdlRuDdbfuFb3x5kiGAIi46EKupuvM6U0r2pSiDmNoNnMg3+TsA6L+t5eR04AZ0bQS1ECG+UFKNyN00kax1qKfmAufmoSm/tzetXQleNeZndBVE//OCzotMG//pYcRh0SiUKuEeIiMzzHEBQw4u5wlvYiujJyl2Wo6rtht3V/nooVYhxqLUWYUl+S29GSJgMZdoNewYKddBaVFXA9HqGUgUkIbvpyp2qWqturWSy95blSQRaa0LWWp8+fVpLLWUY6njYPxEvBMMbo9NJq+GSLdlL109I7TFfdBwU0XAHjMF1bsf7k1IiGROAUMhSyhBptMMoEEQju3skr0EL1naCNKGr1sP+JuDdFpCSlwRo0uXMvdtWmAAAzNr5fLrk57lnWIca9Azwl1+0pMgjcvJQx4aoavKeGAiHxT1cANKfwIvF3UVfGpcmX9KPvqYYABhqyrYz2RGVkhjHMNCiW6CH97COtKRxZmPTQwAlGcIPyy/dXN+sa5vXhQKKW28MHg7XZlznPk6T2erRVz8TEBmyoJKgiDg0ePJosGsA1BNAxc7NVTgOe43psL/pbbUW1ze35/O9G672zwGc59cWR9vIiaEX9qNKGbirtUAwt9nc1rZ89s3Pfvj5D06nBykU5bLOCfZsqZ0PQx1E2Puy9Bm9qGpoj2QfmCM4DleItloa3ksgAi7ilTe9S/BEiuCq1jjNL7HJhSki1lswEDkziht5VbSUGh5wXnwA0k5K3FFKNbPUeX3rW9968eLF27s3HsvzJ5/+yi999x/9o//Cy9oaP/zo28eHV3d3X3k0oIqOcNRB3U9uTmYOTHejsMq+WxNFTrXq4UUHtzUdCUUYwfSFW9YTkYCt5LoMpI9x3uBQEV7yW9v8SpJTtWlW4dk9FpLdOoH9Yb8/3L568RVoHqub7acn43jz9vRVhLsn2IYEGjcRL8M82XMqomOZRHg4HF6+ekERD4/wSNUlEjBfI5yohgZI0YNKXdY7qm2AGSsxuZlIc3REIcSjPWoyM6V5zDAvpds2GCp59NkTSLVqBDQkPEU83KIchYR1SxXt5uHCMHhCxKrFLOdQ5I+Ii69n+oSlLDz5hm6PU2d8o5WQ7rG1H5KozC3rvkgdRRwBXHzbQ4ZhmJc5J4LkJ1Ek+VkiFCUQ5uEWmp7upIgqRFVrrXB6HxhT4ib5YWbt+no/jMWsdW/Lcl7b0n158/ZFRKtVjqdXx9OrOkAgVcdBpyK1llrrQNE6DMb25vj6uDysNi/9ofv8J3/2R2s7UawWmK8X93xL8zjAcIEi01cweUIAalFV1bLJdYvUVMYIi1AUz2vdu7wBfKgff+sbf7lbI2PDyqlPbp+qVqGqliw8Sql5+1pr7mlB8LWRdCKg67rWoX7ve78TYT/4wV8cT8erq72qBnxp82rNuqvqq69ezKdZtYgUIa/2+7/+N/66WY8o5FjKXmR8+uT97/zid+Gl9QVIhKmSVUVKCZUN/xCRWmvCldwYYHp7e3N9feVuCDBylJYUKRHIkwuXDNzNEuGIsN7SfRvu1nsjGOGn0/HFi58FevLrKcUM87zkNLAUr4RfLHCT9esgFNCAjNPhgw8+TFyckN6z/GBOXorw3nvmu6qFqO765Pa9J0+eu2tYQdQ0Lr3aXz158tSDaSGQVN5szCS3Lz1ccKENiqiqAqGi7z15X7yk4aGESlBzPevXtg+qmhk1hVtvBQDhHgTNTLWM4zhNUyl10+Vs+o1Hv6FIECR/NI45BgkXMFI2bIkJ+4YCClWqQKqMhQNdY6Pp23b6f2P33WEY13UNAAL3btaV+TGCUJLjOLn7aicwiKJSxzq0tnKbpSEiaWu0RrRsBE/70XqLzqpXQxnNDBGDDhEoZRiHnRC9L31tEVqqNFvrULXI+XwO8Fd+9VfevHnzk5/8aNrV4/lIgbk3b4CJwLw7ImmJlEhX7loqEN27h0tIraVb8+wXI8JDZKcS5s3cyepu4AUXFdYyuFNYW5spUbS2bojEzLKOxeaczW1iRXIkiwwUJrU1n9M4jufzmeTNze3r12/SVGEc6trOEUWoredsIpmGq/Buvlis7qxlN07TeX5IoJjQUupHH310ns9ffvGF0CEyjQeAyzo3O4uYsmRJI9tkEVxac3l+I8M7gLU1RyAifecuhXFAtumKYO7SCHg4pnFqzXtv+/3++fOnP/3ZTzbFMjmOw3xervfPnz378Mc/++NUTucizMRSKDnZMZIm4fzWZ7/w45/+0NzcetrAZA4VMKEAYtaFVrRM4/X96T4CwlFEPVaP9tgdFwopFo1wd8tBXu5W6+CXERjLuuz3u9bW1tYMs+6d0KqH7/7qr/zpn3//fD6WoZh3t46Nw++xObakJOPdpldsXNyL/BsbK5DJWUsC78ULNlOqrwG2x80mSjMrpbpbvOOEBk+yMkn99NNPv3rx8nh+SLMLXsQt0ns7Hh92u90wDK1tky+xKcCpKvvdTpjrQDejwG7zvBASHiqGNM5GLxpFxqp7Uufzua1rREa8KMqqIlKBqjJKSoGMVa9+4Ru/PMheo1jr8zxrKe5OkQ8/+sjc19U80nBTCougCmtfQS9Pbt4TjuFARFGJ8N5b+m6bN7M+1EFRmJ1FsuT4Dbecl3F7+4TUQBcJwYiowNztDUKtlwAk4UxQRA6Ha9EyDIOIdtuWeBIhPLy19ogTuntrazbN37x5nTZcEZ4hlxLNl3E37XeHadilTe1WGAKBOD7cIyKikx6w1ucf/vDPv/zy56KuZXsi4zgIqaoBeOrzJPkxSG/kPDQTVOw9mfGDiBTVFDllHpT7VDcZxzYfUUXCQ7ZwFyn4fPXqZb5bmp6ez7OItL68fvNKRNNZM9dTNkFUNBd1Trgx6y9ffQX4fjddnF/iUjry8lUJMJzLku5KHcyJmAvQKS4EowMNsSoY5hEdYe5GsPfm22ED5HS8TVae7iQEaK39/PPP+9oZ3E+7UrbccBMzXmDY7ay5sL4f/z88CKqkxZe6RdIHYjNySs86maapaLmU6NsMC1FmF9fNHuczeE5LywPAgYgXX75Y5nmzsIlw3wx3S+71eV6okscgWRi66cyCa1twQZOV6kSmCG5GglILrnfjzXn9PHgGaiJUAZZSqw7oUYrmIICIqGWyboulhHUo3H3y/rd78y++ai3OINa2UPgHf/D77lZqMV8Z7h2qspqT8fHHn/zmb/3Gf/x/+7+fHk4IZ47BCCdp3p3BC0ztziKjwQTGQokoQDOQcLP5dI4wiYlQ5S6aUl0wX119ZLHcHT8XVYLmISy73S7C5uXMjTXqEaFFAfbeRSRxvITWejdVRkSt1R29eymakCY1hqEioqe5uQcodRh6X1Xr1hziprAvRcdxurt/WzTjVKmlLMv8cLxPx9qqozWjSJ6tGSQzom6OmwgRWddVpGzIvveslNw8Kf9mePb0/dNpdvO1zWaWXoTucXNzczwee2/zMosIpNzePLm/v9tqTlvv719R87NMS7VuCAJszUqp5t3dAihF7x/egN46soFsHZCs3ntaaHITTHKou+C69sYN99r88YQMAnQ4I7TqDvJ1upRpZ+8ryTQ9jouHUDaEKaylvHn9EgIqH47Hd8a6bX8gWUqJgKVtnYh7Tw5/dqpytg0pIlprXZZZWCipntk4LWFpwF32+/Hh+LBRBiUT+0w3HqNnmm1s6Ucg5uUMMAmD72q5RASquq7LsszpS8BtiJgWqVrEvHuYZnkfyLNzGMa8PMXNd7/zL+2GT+FTZEYVJgrVEh7WLYKX2bdOcVUZh2q+gI0IhL58cb/OrpJpmANutoALZO12DnRVqMK9g919NVu+8Y2PRKP1cxFWrZsSwE11Ky9EpJt5j2nYC8s07pXV3ABXkUCUUnJCkUoRCUjTIiKSgHtWBQQicgQzvvrq5bq2xyqOmxEWRDZI6TGQimQ/Oki21iIsieHurlqA0MJEP633q8N+P03LvIoIItLFJsKTbwAoocp6sfAuIurRtThhAIRVteZ6Yg4Sc9cidRjyW6kyPQfXdVnW1bqL6n53uEyClIBEsK3+9Ml7NzdPEZJjEXLf3D/cmyVyE+5hnfO5I4QiYFCsDjwcrjZaXBBgKaXWUXIixjZh3T06JcLNvWdRmDbara1+mWuWyIo7IqhayZIzXXN6GEJyzAghte5Uds+efnrYPfcQwi+kgrzVOdFTVDX9xJMmJ+S0G6kAYZG2JYpL8/AxWc1YkkwsZBFwmRaZ6yrhCVxUbJsBCmUY0iiU67p6SpPL8M803oJXh9uiYzKNNpNNpvSUwXwUHrSgc1NGXziGn46/LCylDt2ieycDdEX1oAo8mnnLwk2iJDaY04SzyA7TZ08/m8/z3F6Bs2cJmK17pEpmKFrgXlSrTCJjWKSeU2XU2L3/7ON5Od6dXx+X11LDiWVZzE/YhKvMBGBdFmPLUue8nMdx3OxSEcQm3o8sL8gqxXt454cffnw+nZotoHdvbpa5kmp1NyLoo0hzzrVctSbB1XzJbv5WgAD5CddX1/P5ZOGbWxnSWJBKjeQqMTmZ6bm+USlEpPfH2Xu06KKCkJx3EO6IcO/BKFI80kFCapmur2/evn3d+yqqRCQ1wtxK1VJ0bU1kq/0uRWlzTzdTlFJaW8JDVDzohlLHTz/99Cc/+aG7R9g0TeG2tjU9stxh5qpFCFEZhuF8OpVSrm+uM9ettZqbO4RVpb7//vMvX/xcxM16rdOyLin42u12vfWb2yevXn3VrYHY76fz+bx9sUjKhJZakkMCxMPprUgRjLtp39rS2hr0DJ/pSChbDiq1lN5aqUPRYVmXZ8+evHr9IgdzYKtRH4tGQCiS48AvHlHBcIKJ3DKts8M36yaz/njURiAlXxnKHk0MQQMkNntg5icqs9osIC4PWnKcwlY3CjLTCfLm9uZ8ntdlzk9KrFmTemZWa8m8naRQKejWN74JIo8HZmBIcV26CSOEEGUZyihRE4OupZZSNktRUWr76s33T+vPpHiEZi5edBCqSlEtAet96dZab8FofT0k2ulhZiz+4s2PT+2u2dnRzFfrrZRaZCA0hWLdvLtRRUtNweAw7uo4BuDwzG89YBsNSxUlzFT52Tc/O53u5/Vs3szWCFKqb9PpDECtu6KinAa5tRZgDMMYqeqgZDlXtGRuejwdE7ncuOlBQMdxUr2QM4XTNIrofn+IkHhnOPw47VRLbyYsDHHv5q1Za+EeHYxahix3haosZjGNY54+27SzUkRRa2Vo1T0jxyUicZphqJ999tkFEJJwjnX/7Ol74RRqrcV6++KLzxPAH6ep9XVe52A2LaIUqkJoEQZ3a5YtnIf7e90ctAUhRYlwNz8fFzeGMyJam62v7ubm07QTkVcvX27u3uDxeLqk3wFQpez3V+E8n5beem+91qoUN3ty/ezJzTMgAg3bGJRQIQJDGVRUyIh4cvve2rpH+/LFjz1WANeHm/QTzEQ924SJHl86NIht+tPmEZ1NS5XyyIbP+X0RdN9anamp8IsrNSkpXyxaJDYYMeffwQOXET75EgYZuklWg0CRIoI3d6/XdRbhpuVOxIXMsejWLXJIHTRyTW/lMbchKK01D1dRCVYdihYhSqnTcC3YDeUwjFOWXo8Dm22b+CQqtZSag6iLbkOYkmZftBTZyPoRAYnWl4fTffeeLKze1t7m8DZNNdwIDFWLcDfup2Esor337quhGzc1bUQIuM4ryTxECnOkosKAkKIVdLO1DrBogQZ085YeGZlF5CSf1tZp2l1dXZWS895pPVSGXFWEIkRYRMolEU3kj/v9lWpxt8P+SqQAHMcxAufzKcKOxwfhIKxmmxDHmo11KrUmEui+qXWVuXpydsNlNYQT9vPPf9ptzc/LSsRdCa11PJ/P3TYPDVEN4Obm+l//1/81bk5CdNOh3uz3t7HRdHvgfJ7vABdGb40QYSlSp3EfTkJVkuwGix6wcRwiIucIQ9h6Q0gtUynqMR/n13WI9Aqx7h9//Mk3v/lNkq9evlyWxd12u52WJLVnNr6dYqVsG6O1ljYgaYhD+M8//+HrN1+qClHDNeecZQkwz+fe23k+Rvirly+QsAcUod38PB+x8YeUlFqHWgaEuicpoKjWcHELERYda9kRGsEwhpfMsUkBRLWo1mzkrOt6SXq3fFipRUqRGg5YCAVOQL7xzW9fXd2mnCij74XbkLGd3GZAUaEChjMnAIlWgL23ZV3iIo/NbGijDnFEVDiklAGEyja8JIJFB6EQYX2d5yVcl7m1ZZsOfjmw0Xt/NE1LC0YADCjLRuMwhAUhKkWgHvFwvLew4/n+vB7Xnl51OdokbDVlCQu4CDTVQEV1HAcwmq2+kR8uZQyg1DT7EYiEiMvHH3y8q5M3I1VK+dGPf7y2FkA3vyQ2dulqbqM1z+d5bQ05rDVEWIWVQHKMRLS1Ds82w1axEFK0Zuny5YsXrW21NxCqYtbD/MMPPn329P1x3P/6r//mNKV8LDn3G1VdtQzDoKq73T4fCLeRR7H1QmAiSNgpV8s07iP0V37lu/v9vmSzGui91TJ88cWL/+g/+r+kc0oG3vN8/vyLz2utHtEtVAelaJ7+AbcghDmbY/PBIUlRAYPEMAwX6AXXVzeffPypiLiHWQu07/6lX/y3/yf/Vm7dpCI+ffIMwDAMuTZOxzPB999/P0NcZo/u0Xt/eHhorX322Wf7/T4ibI3WnMIyso7qiCIjXQjJyplCLRqwxICHYXj//feYEl6tQva+5sln5iIpQ4EElFAw/1dVCA+s5mapZaAHg0JRpUitg6g+bktsDl5fj1whOdYp+xrf+6vfu7m5DQuFuPH3/tpf/+53f601uyh1YsP0fYOOIyIdNmsZtpoIUsogSULK8dXbS1Vq7jM44UVQCeUv3vzV1nqSUd1sy2ODkG62kpWoIvBYPZhJwiN6tq4rEBteBSa7SrZeMcw842oGUlF0WyPEEfAu4M3u6W48pG5wWWeLdelzpMQ9Voc5rEdvPjdvIlI4PIKBl959ACHU3i0irq+vAb59exfqHqFaSikAWlsfq39wM2HbJuGhpl2dsAJy8/T21esv3ZeAlzKGY2mr6sYmCccwTL01jxAKhREp8oNZu6RPGk73osI6DO7dzBJmFKB7z2wJhGhZl1UlBzTppUQJN6fQw74efUCJoHIUlevrw8PDnUXPC3GHu6nKpY2/QTjjOABxPB5FNCV1Hs2xXpoEKT5mKWVd52yzRXQAqtJaT6dIswYw+9vrmgjFOowKeCn1fFqBAnie7+lXmEhW90c7mzCzzQgkNgtlVR3H0ayv63Jz9ezt3RuPNdBFVWV89uSDL1/8PCKbn6KFEdF7V6GyuIsjQCOT1WeyQbXMM7R3y+6XWYuNcKKAeBilIwaEBrLNA6KISt65SwAAmR7nj1rQDVaIbV/zW9/89pcvvjzP54C5M93MuveLBzIiUhlb1nXZ9q1bUESLm0U4Qq6vr9uyLus5aNjO1mGbDEAPWJhcXT0bx+Hlqy/5zd1ve2zjK8wczmncw6a5vaUcSUEMtdTAunSTd1L57RoikvdMQCC1DOGp9nARwcXpW0VFGTCLOK0noHv3UXZVhyfXT8ghWfXmq3ssyxpsYFhYj26+pDmEsOaT2I+HZVmsWyBLCd/ciCiSE5QRrXcgrq6u3f18zjm5ydJKw7GMaSLQjC7Xh1vV8fWrVxR3WS0MrrUM73/4wedf/DzQVcQtVIo7hmFMTwBeOuCl6BYEN/aMJiDxCPGRPEyH+4e3zhBBKWVtLf10Mlm64GQ5EIG1ltY3B3OAqqXo2C1HXfVhGswsQfE6FLOWH6daktcN5CQljsMeLrvd9cPxdbPz1r6TEqkgC89thm1dZg8gLms0bT5QdMyJvSIR6BceXBaBktF+mqa1z4nyU0pu3WGo7tZ7H8fxIk/dwPDszwkHs+7oQRcSofvpal3PCT5lGkFusyJJsQ4pEmjuzZylqBB5HzI/Ih8NxLa6zp2lDKR3O8J3IkPgHJgBEQwRgUs5I5vLLg1boZGGMpmFJaFfgtZNVKWIec8xvKUUC6fkGLhQGeowTNN0d/cme3K9N6oIZfue4FCqd+veHReG/UWQZuFJFlSpSKpC8jkygxViN+28+7c/+yuffPArRFXVWgZAswjCBoJFFhURaVW8jQMSytXhWrWqaK7UDLBDGYtWbx4t3CLCQyykh1jz+dXdF28evlrttLaTx3p1M0GMmt5SUBWhCkQvmLh3Ox6P1g2BsY45YoCS8JvnoU4xSg+04+nN8fQmkMht+qdEEoCZU9UEYEzTcJ7P93d34zSN4yBAoYhqHcYnT55tig/bVEXu+PTTb3zve9/DZle19SoAmIWwFC1lAGiiyFF1eees2zhMRbbcW0VwmSB0wVETPC8iw+Fwk02IxDPcrNsa0QEXid6WdJCLiG1KCAAkQZQRIjomDbi1pdva2tx7L1IEQig8hFQRIpgm7luaV4CvjSlEytXVFcDdbn99fV0KSYZrLTuEAAAfpSdf8wFEpRQtpTz2b0WktXahZ20zzlpLcn+UIrVWbkYNHtHNW8BSfZooVMbD/XRVaibV6YSwOZuTopKcEwIuJKmIMk0343A1lKtpfFL1mhh309MP3vus6J4shEboZSC3lDIAolovTaysM8Os9963w5ZuMBahoHsLCS1UBWCqzI0tqiRbs/u7e5BmWcADHrHpZiJ5mO6mpYzj4TKvLROQlMBRVQJdGDeHa35r9zseKyKU0264Ibm2c9Er1b60twQR1aPcXN/cnT4HglQPE2XvadaV9Y0iQiDDMCIQxqJDPrCk+6jK2hcLM7gqWzvnqI/wGErJPljva0TUOoDs0fPobb1pERLdWs5PFShCzHpRJcOiI6SUigiLnvWAcxWhIwRVy+gtrVIwDLUUPR6PpSgpObZdRNx6ZpVFyziO5/M94A4JsLWWTjJkiAijpotv74ttAuKEgvIOa63Dbj+9ffNStCQeUMqQLG0PJ6Jbp3w9RjZHMKiIR7h5FmPr2krVJMObeallqCPA0/kEuKp4eLYoRSTgtdbWVncTCiXF0BxqsfS29hARtxjHsbXmm2kfKRJhoH89dzM9Hwi3Vut4fXUjIl9++aIOgwqbneWd4nmzt/cQqZ988ukXX3yxrmctYuZ5mieA4tvAaG19jYhER/wyrcx6jwgkiLmZGHCD1yKyCXk+nwH+5m/85sPD6Qc//EGEQVyIx1RUtbhFqaW1FWEgBYP7Vr4Ndff8+Qeff/4zj+Wwe/4v/Av/0t/9e/+Ppb0VkXBR1U8+/eQnP/7x4erw8HCfx8Jq84V5OgDqsYBncBDJOSzbQBMwBA64b1x5ekBZDvur4/HYLbWcG7k3LqbBkYRjbKsm/asBPApTs44pya8UGeokQlVBqSKh+92T/e669XluPz3NLzwCLKol3FrrQ6kSKfkzSzgHZAg9vSmmokOSMzJKfPOzbw3DuLa1+zqvx8XODS28H6b9rh4UQ2LM3cKsB7yUShRBESgDYS6Qb3/7O7VO2MaKb03RQASj+WowQgqHq+m2cCgoV4frw/66chRokRoht9fv7YYDgxL07uvcBELQehdEBHr3EAY9pDdbjuejeTL6LKJreZxJi82i1WNd5m4NQDcjQAhQVIaqtS3r/ds7QgRSWA/ToUgV6DTugrBEuoNJVRnqpDokOT48LugLtcB97X31MFFBcJ5bBPa7w+ayHaJShKIiCKzLSshQRxHBZnQIAEVLegKRUuvYmo3TlAvx+vpGpRQd0t0yYmu7+eYPQFIfjqe3b+9JAt5tDd+mSJG8urqqw+S+4WKf/+yr3XRQKQThVCkIwoMIVXWztrbYKG/dbBuXaGal1kwFdXPd22iGwlJ0WJZ2oXbx7d1dt57UBZWcSVt4WdwArJtIauGZJmkRZraWEl988SORTsSyPLx8+XNVCjVdE3q3V1+9jhBBqTqER9FymJ7ThViB7h2CSTAhFFChurtebF8QEk5Aw7d55x44nc/uX2coAEqpIjVVVqQEaOEOTz1D7uTdbl9KjYBcensAzG1Zz0mDAwlIf/X689dvvtASyr3qvupYikA6ZD7NL+b5HGmQp5qV9NMnT6fd/ub2qUgxi2HagwpUKQMUP/7pj+4e3mwWZD5HWPLFTqdjXidCCwfloBzSNKzUEgwLC/dMC3/5l75ze3ub4269jYzRPZ1KnDlS2otw7I3CMbz88nd+7Xd/9190GwUTo1D41Vc/O69voIuUcO+p9LeeglbdDE6DqRHJ41m1gKKsKlWQfCwlmFsdDoHApOrw6SffMEMEb25uxnF0j+vr6/Q6yV720hb3vrbz6XTvF4hlC1tE722/v/rWt34hWXspzI94LPy2/l4p9f3331uW2TKhCHPvt7fXw1hTSiIXRzzLrl0qJyC9W0pDI2huZrYsayJMD8eHYRgOhwMgAnEHqJfNX4R1mdd1WXvvWsS9e6Tlp5olMR299diIbOFuD8f7TUMjcI+bm+txnDLqZg+ylvFiEVSw8emkrT37GW4ByDTuuPG+zLwL6Z5qOPnRj378s5/+bBxHkm7xaHCT+p7tgzzIHEwuj4jAvCxpVO1hgfiH//D3z+djmpgAQfrD8b53+yt/5Xufffbt3nu3tbVTBMN2h90Hv/rLvy2sIgK6exPFOA0ePZDvIBGVGIiBMQgF6GZd9GL7AGA7372UmlXA5S+3Fkn+y2VZtirSI3X5WY1GOL81/Q7YSUpUYdVC9xk5Y4eRJpetNyDGcddWF5HuK2AeIKZaCiW95rpqzbAgARDmNgyl29K9uS8BuKdZWYFDstLMjjliGKuozsup904h3HIFrK1RGQzr/p1v/9arN5+/vX8h0khxJ1GKlCK7oU5tXZvPdagGtnkJtNDuEWYuCNLAHLuQwiJx9+vr6+PxIdJ2jRTR1lYEqWK+jR68bDy+fftaIB9/+NnTJ8/++I//UAu1DN/+xe98/8/+xC1U6lDrUIZlOWdq8Ng0SjQYgQi5vrlelmVdNnEsKd1UlGbLBiNHFC2ipbc1S+hwuEetU2tz+h7haxOQZL/Q3JizG0F3e/78/bu3dzm/ZFsEHjm983A4ADifZ7NeSq2lLuuShsPY9oe6+1CHw+Hw6tUrD0+mO+gRG7yPr3Vh4r7ClVIRBqZ7dVCKCHpftaRlkb67ZC+H1JZnZ7TMOjAJVUAkL0K3uT6MgHUvtQCRCj6RbR4DJIBQzSEDnlDlhRKwWQen+A7speysR0SLtEEBAxZRVCZlDfRup0Ajd4CBEI7TeDOfT6XGvN6nF8QwDMuaNigUoQWvr26XZV3XGexkcy9FR/P16xSXYubb9BMgo33anSfu8HhDImIcR7NI48sIA6PUOpZyMDNvLiKH/eHhwZqdRTVpzLUOVaX1Tug4DOu6KAVkgSIGhLV12ZSo0URqdocT2xBJIV8PilKpZeNwkREcdBQqc3ycR7e1d7OIiqTaIV2RunePHuLzvAzDqKLmLcJUBlUVRCk5DKwL0b25x1h1tdXgCBx212a2LmeKB7dOqQqZjiQqkdRzFaHUMuZqFrl4tal4j9bWWqqZ997P53PaTIrIH//xH4tKTVJUZOqe5BV5XIW5PkkKMZ9PWz4JqWXY0jniMnZwawl478MwRlhrvZS69BbuqtptdafqhYAucikN6O61FPc8Pb8G4TPYbpQb4Hg8Pjbn3K017MZdt27eUlySQoKUlaoqLQgxb18L07amBdxRq0ZOZg2Uot3MupGl1tRnwS2FKRiG0d3Men7tR26Mu7///vvbhJiL7CuwJQi+DVBWkuO0izARtJ65A9PWJyzNbF2FZim/kx4dm40Gp2kXHuf5pMKisi6zlk2TplIAIkKIYajmYc4I8WiAk2z96N4iBJZlT3jEuq6CZH2zRyMwz0fvzvB0nyq1xkZx46OXSrZksNEktqo+V1FcXtiQ9kddTpa7Ie5Gym46iKqb9d6n6aBFgU3h0VqPYFLYzC0PeATCVTEWmYayE6gE3ayoDrWmGYdS1qVbj3Aqx/10NdSRzNYsSymC5GtGRKxtPZ/Ph/1BybY0hlSdig6qQ5EqrEX0Z5//6NWrr8wCoUPd1TqEh4dFeIY+FZEIMtyQmjVSUqiRntfJ7UiaFIDzfMo6V7XUOubszbxrqqqS8zalSPEeAEXleD59+eJL5qh1lTKUUmQYBjdz9/P5IXnpcZl8VWsdx91uOmQ8yfhWSgG5LIuZp2w1py2ISC1Djinqa9vvDkkcO+z20eHm07irdcDWc9eNnQMQeHb7NCfoVi0vv3rRrS/rIkoKHZshQ64PEbm+ukoKhKoOWmta7kb01hCendivXn7pbtxcZUv6SpdSSxlImab9hVpUhzKO45Qta5EyjjtuejQ+FmY9i8YAgN1uly67uRC/9a1v/fIv/3Lv74hjISkQ27R1Ku69tQWQ3W7/KLAmhdCtvthEMBoRblFKSYofgN56rYOKmvn5PGvR2LxBihm2oRZizU7mDaCwAAsZiFLLBAS4dL/H5nCd1QE9IFLy7iA62UXA0LAhB4Vc4CE+5g6XczguPDNe/n47OHPNLMvS+5L7FEjOINfz+f7h4S7CQHs43p3P56K7oR6EJYOG+RpoFq1ZS/QIQeXwvd/567/wzV+VmK52T4tMu/HKWtga63nty0JwqGORQTkWmYgaTjcI9fFcqbU4rPlq1kR5Oh3hUbTsxr1Awuk9vaoUgXEwt0UA5QBXCXl0CHDjYX/jnsgzbq8+GuutyoQQ87722bFmeZnmAB7d0XOdZJSpdUjSH2KDphOYzbZT0QLA4XWs8zpD6PB0AO+9zctZhKoUFfcuEpu3NWg9WvPd7hoo7xYhuYZE6NHXdTZLi8p8XmUYRtFyf/+Q7cJvfvPbz5+/15odDlfXV9dxqZ9LqSIqUjz8ydMnu92U4gBV0SIUrOvi3lVzLrCVotM0mVkARYsI3Pr9w/26LGE9XfwjTBCqEGGt9cmTJ9fXtxf1Bnu31hrAdW0R6N0Q2pr1bofD1e/97l8ndRx2OWKTxNaEAEn21eBUlvm0MCQHBox1+oN/+I//2//mD5XKYI47FpaitbeeMJhZI0JI6/bm1WuVPDk2cqtKecyi3a1ozZ7zY4K9tvV8PolItrJzds52nkPJEhGtza0de5+HOgrHIk8QY4T1eICcAcB2DBmGaahDVky73b53C1egZCeY0MKr95/8gluJTQK+LXWm+uyS41xOzK3C/+deFzPXTl4qCxXVXGFEqVqTSy+DypCT0rOUpAgdAmjq1g1F5cvPfxphRbXWQaUUGae6k9CxjmOZ3nvynqI8vXl2mA5CXZe198bN7cXNWsDm5Tgvx27NLSrrvuyvp6ur8YAuElpZd7of9TDoNNZRNYRRRIdShYn3oMikLMNQPv3s41ILRSlaqqgqXJQ1W5WkAWl6yjAwRLZLwQZ+PDys6zoMA4VFiog2y9kEqKWOZZjqjsHT8SGxj1rKUEuhCopSUoB/IdwwAUBSixYCd2/fCMPd13XVouNu/Na3v5k42UV3lfohTUV7by08hKhFp2H4k3/6xy+/ejEOw8Pd/d3bewSVAse6rs+evf/06XNSfvDDHxyPs3Icyy1ZIyxxwlJKnhcpwri5uhnqdDoucBQpEUHJGByqJRexiAzDSHKaht1uenh4uITB9GdT1bLb7TNnTvWze1+W9Y/+6J9o0Yfjw+n4gPBpnDRZr4Heuodr0YiENKSUgdT9/mqaJiaEJSDT+kWFtWgtpT6ORRwGrTUVmVRJjxuJMGEIRViSkDwOY3ZuL7uUBLK7rzoASEUXmcbvyAxsqwgooHSz4IJAkb3GDXwnJHVRIix667UUEZacZJdwPTbKgOrwb/6b/+P333vfrWVXiRBlRWyOkI8SeRWtpWoWWhv49xhy8zTJXS2qyl+9/hvu5u6ZSiUrQKgkcqJruG1jz30mijX94L1PWuv392+I7gyqMsCgSqlaERxqWdYFoJZq3ntfWvSAdWvJ/hGKiJoF0EW7cizY7er1frz2llKuNcIIidDpen9/fn13etVtLrWEO6kJLRJUHYHw6AG3ra5RhiF4ffXkeHyY1xPELNZ8lslcXZZZCwFngeUMEakRaSXpmdxa8lFCCsuoI5UP8wMVacMdFhv0xTqOpfXFbKu1SCoKgua22+3O55MWPnLspWgwet/Q2EHGVMJl8ElOGaMgnIo0hiYkQszXjVnlfn19va7ruixl2FuE+yLENOzXOb79zV/92Ys/uzu+zHKulHKxlOhDqUCJ0PDw6KXA3Xuyv4uIyDzPpVzuLbeDP0sAs04loG4oRX/rt37rH/3B77v3UQYzc0YSFaQoCYRvZnyx2YWN43heHlKZAMhhf3j+/P2f/vSn1lvGefOW+meKhrOWwcN6a6KSKnOP1d2Fg3CYdvveV6Cvbc5HjhD3AEMFHnZxoBZkk+nSpM2DJtG0uNAvL0Vjlg5RSg3vbri9ferud3dvKB5otsbf+Tv/7s8///nf/Xt/T2vxHA3E0M1+Jcmqk+DacWd+yhkUyaMJpH3c4+QYXKz0bXNruFj4ZvGbGxYXVKNQ0idCciUJYN5EVFQAdXdQSa7LqiXtuPe/9It/WSj/9X/99yizS7hDRJLXYu5FilZFR4RP03D3sIRIdKgORcfMwaQoIsPQHlZvDof5dBRELTCHEYfxurW1dwvIPC+952j6qWpxiQh/lOY6IjW0vTuZJxwBNfcPP/zk9etXX7z4uQNVd+4NIEWurq66tSy83R1GkeIeknQkEc8nkH05iFK797DQzW0aG1gXvL6+Ccj9/VvyUbzCFHaqlFhjnmeQZqk21HEY6zi8fbgXlggX1SK6rmtST9d1zUf1/vvPhfz8i5+LgOA0TaUMb+/fZJpE4cPxISWTaz96RHYsyAguf/qDP6BGblEAQrHLHI3Wu4rUWkmpdTqdHsydwoiw7i5+aSDhgm+7iGziG9FA2gDQzP7BP/gHYKiW3n0YJ0tnEzLCay29ubuPu9HMezdVba0Tjz2SOJ9PP/rRDxLmzsGH6Opu5vjko08B//zzn6c4EsA0jcsyWy/TuBvH3XxavXtrPTm2Qh2GsbW2dbW3gYZApMn9tlDcHTDk3PWtU5WSwE1nwgxIgHtXkWQ1ZgtHmF5O8f3vf39ZlxSsgyxC/P+5+pNYzbYsPQxb3d7nnP//771xo31dvuyzeharimS5SirJFGWCsgjDoiHDlm3AgCEbsAcCDAMeeCDPbXjuoQeGYHtCARZhiCZokKINij2LVZmVmcXMl6+PFxG3+5tz9t5rLQ/WPn888ibwkBGIuHH/c3az1re+xpu5Rci9akWyWu8ANc6jeHSrxWR/rmuh6133tpIQV8IwAZxp4R1Jwu/v/oIIL6UgoCTpeRPKzDRNeb/fi0jgxYlGMxdOgLSbLk6HZZq2xfZNT0RsagTk5uiECZpVU1PzLvwL/39KLDDPx6Wc4sWPeYsqAI0AGGXgreDm4vLR7eFLQJ+XmTmf6gLU9qdbhSZJ3MNMHYdhIqSqc9PaaiVCBEp5ZOTTMrtja22axqK1tQUiKd07J5CIhiEfDg9xgo7jtN1evHn1BhkpsoncwA0JhzwQMBospTpGdru7AyO5QZIMjGU5dbZFF0RAYgn5zTAM5lbKDAiIEuu+801D+4/QWmXu9j/E2Fp78ezdJOmTTz5h7t45ENJl06B/BDEriEfDOLZF3TG6XLNKImrAzGrm2skSESLJmNRgs9lM03Q87veHPRDEaDpGY2uhaNaVQN0yxgHUTDgxc6u180sRBcXcqqkwtVYdw1Yk7C67ODM4QMty7DZfkXu7DnWGYWTm02km4g8//PDjX3ziriy03W7u7m5rrYH4tabMQwQRRfpJ9G/M+fnzZzc3N8tyUmuhYgMKSMnCrDCQG8DVm0phs9kwy/F4RHKicNWI3A9372SmMxUKwoEEqdUG1KWnCkZ9diKujITmC7OtYK2vl2FnDsYmDGL2MOSQ+IXFRcxIzzASIprVPvuNVGgAL8sSlTwBIlFTJRQAX5ayKhvDslGYHVHdyvHUALN7+JsjWvRYGhppQHc0SQlaQwdCyjK4c53b4xdPL7eXr169VDVCfP74/Yvxnc8///lulx7ubpiQyEwXcmlW3KB6S5KqNiYGaMLYmqNba8aUkiSPqSlDa7rdTuBUSnNvgEDspR4NHXu6fK/4AxxfloVIEKE1/Y3f+M2vvnz15vVtdCdCgqYQpJnaEBTUL3ZXjvRwuI9Z3MXVJSHf3Nw4uTDrauxEyIYW8Ext1c0BwgDJWLJqs2C3bLettsPhCBySDuqeKubMcvPmzRnK7xc+gJrHD+/rdRenQFtUGzx//i4LffHlx8gRT+TCkkjUrdVGREzipuYex/nr16/6NO9tFrNr07i+Qj7k7q1p6Dbj+Ip6uI+pmGppBgYI0ziaBcdRIezmiRC91hKjQm0eqGFQ4VursXCRvOm8LOFjVj/99BdqzcxZ8sPDQ4DtAJBSaq0BaKkLc7gyWtiVIuKrV2+W5RQ538GLbmq6RhJ3kLX/n65ZrbXW2lLKrZXW+vGNgEjQ5wUSOD7GEJs5ubvkZF0a3m9rgrTZPPr2t37wx3/0L2TIpR6YQE3ta6DQOjKNEwrMvJRi3YoFoaeVr3KcoDo4IiAhA4CpSuJkyEGJ1qYppZAIx9qfT6emyiyPLh+Z11aUYNTYiain05fBVq0aNVs4MXLzau6GKsSCeUgTAbdmyHB8OKhVctlsRkTa3z88vDlM44DAm/EKPQmPTCPBsdWWcgKi0hoiCsXoHJjYycnYlZxSIq5tcaRpJPC1QkAgpjUp1KPLUu29d1+RamEKQ8R/+Id/2Koi4rvvvliW5fb2Jjjkb3lCTMvSp7W1qZlO4+QG2hQFDNdckE6dx2BYi4gG2NszbIO25pvNVErRatvNdi7Ht1AecfD7gk6DiAGW1nochhy8q976giGFbaKbIYvcvHmjro4MgEJgrlZ75AYhEzC4hQMLIp9OB0RkZoNg7QV/yETEwc9rZbVodnevWon47BALANqMkBBYTVuNIEmLnR/WtaqVevw4ppRrq+Cuza8eXZWyzPMpnMrWeCsfx/F4PDITMyzzskacuLufTidiMq/MCGiI4OamntJASKoNzsrE7kgUGi8NXC6GbYHEEhEYrGn3bRzHp0+ffvbZZyKptqbNIgBBtYWGwNfdDs5mGm8npioIOI4TOL558zoIieDghswZV55JvLLAfXg9cFebMlhrjT626U2pA3hoNIiZq1UCwGEYNtstIuU8OqA7uPUHkfOQJKHjsiyZd6DZFBwMQBEgpSHnQSgJiSsIpZQyJ0LuYrxESVDYZcq7RMKE2po1TSzQo/0QnBGzKec8sSBJqfqwmabNZmJGbVWENuPoaoTZFAHSkHYX26e/9it/7sP3f5B5M8jGFRFZzWur6mYOtYYYIJ6CEGVmiTYD14n/NG3QITG7KgEQ+PNnT3ebCcGJaB0AYt/kpssym7ZAOW9vb2otzP0RdyPP/p2DAYDdUJv7+dW0hlnOMp9qKWataSMU6ohlRuRAPrunDktMPohITQmZSJgTc8p57LoeB2ZAbAqzQomyP6z3h2FYjW9CXNaDyaKOJSJmiVwpd895ePr0qUcg0tem6rFuROT58xfYkdAOhIBDTsPqfqqdRkMU86FxHJmDKakAeH395L0X72Ue0Gjen9pS0QAVhIchb3PagMuyNEQyM11btTgpmDlEP0IZIOSyjJAIh93mCsFV63qYoFsc1mE04f3zxvnijMBhrhnhOu7t6vLyr/77f3XI43wqu83Vt7/5PbdQHcaksm+nVtUMEJP3uSCFkGVZjvvDzSef/KnjrHYC9NV5o3Ozz1xFMy2lnjG5KGWht6l+nhsFM1RkxB4FUtwVf2n6PcRxmsal3kOUBO5TvjCtTYtZQyLChCRTeoyAqifzxU2zbDbjk/3xxmG52F3OpyWONEevrSZhwcQkwoNQutg+ev36K6PqZpvNNI7T/d0DcwZ3cEVIiUemNI0DrrnIcz28vvuqWuUkOcn+cFfBVksOHvJmyLvD/kRYAHQuR/NatXASc2i1MdNS5pQkjBoBAMnVGnd9cxRmwCzgHvVCd+slJGRDd3BAd3OWlEJ5Z+7hxm4WrAw1NdSQYmE4oOM6vTb47ne/9/kXXx6PewBXU4qJFhMhWXM1E5Ek+TTP7rDZTKUs4UAdwsrd9uqdd9/9kx/9KJg2RDmW7LzMKXGtJUB8bRoMwZjKmQKiIzgjAyIhqXXRxup554gU9i611c5D9JDauGoLm6Bo7oacT/MM4DlnMKqtuBvFyJ+llLbdbonocHxwD0ZtJSLynIfUdGnaglLCnATZ3Wsrbhp+tO6GmJhF1cxtGodnz57+7Oc/i7NlhWR7vgYg9eHrCmKBI5I3XYjIzaZps384Xl1ej9P06s3LmFAgQh6StmruppyTmGnTetbZIQAiuaEqMMtm2uwP+zSgWosCc20xiCC7+be+8+3T6fTFF58CGlJwsBwAmFGtglMfFhAGdgDohNST7KIbZzJtQclEJA1FW6+KI5gbhTgUjyEnJgH+7V/7g2+996tgarpER/HOk/evL18wihAHuAdApd2qnhAS+sg0iAwpjVHegEKm/PzxC4akFQYaNrxNNIBxKz6O2/3hoODMGZFqbYfDkTk/efQsp82YpuvL6zFNl5sLcgL1MQ21Wms+bbZpkGZL1Sp5YAbvNup+PN28fPWz0t70WwK6T2IrCxPEyxARVQOMN23gzkBg4OqJhQx5ZSrLkNSVBCPe1sGF2c2h68pJq52jlqHBIGPmwQ0Q0M2CpWQKhATuq1MxfPrpZ6fTyYEAWSQzCiC1qqqGnTikYIrg0zhupg2YBeCKxIhUavn888+JiZMgcdOiVkud3TW0aapeSnUA5sQ8ZpkEhYOrEn2OMziJZIz8WiB3AmQH1BZbt1cC4QYm6+g90p/cSBVzGgkZDMiJgVO4TAG/9+KdIclpPhxOD+YWBUSE6hIQKJjCaoyCrZXjcljq7GgoXTJMIjkNVpXAGL2W5c2rNwOPIXBdoRdCJFV3pavLJ0PeIFC49TctTRegMIillKZh2Bwfjt78/Xe/iSCMzJgSj24Opok5smnCTQacqB/Z5u45C5EeTjcOi6q7hbG7BzSACIlo4OGLz17dvH4ATEDo9DakQtVWzW0DMK0NAZgofMy6BCdEJubu6t7cHIzYiZxilADmiRMxVdOitbTZ0UmI8pSPy93d/etoR8HcG3z58rPD4UF4AMhoIQ1BAiA01dmhArbajncPX9W2qNtclmbt4bgHxjTIOE7mAVo6oh+PewdlRrXmbqbNTEuZ7x5uwS0qB212PJy0uRvtD3O1oq61FVMf84ROmbKq1lpUayml1hYwugCR8Si7i+2Tcdgh8rwcT/NBrUUUh/e5imKEE8VQwc4kO0A3BkJ3AhLi4BUioIigY0q5qSaW4KlHrXiGWBGJMAFAykIEDnFpiTswybIs4HB9/UiEsfOkbBxHYQ4iK3iXCC/LfHt7Y97bUSI3b/NyeNjfAmpri3tFhHM00DRNCPD7v/f77733nru5uak+vn6c82Dm6D3Yq8NOrSJ45rDGMnQgwO1mI8RCNKQMQfEHBPMxDYIBESERhCOxCJdaps34rW99Uy3cleHjTz55+zADiEZkFgDcbrfuPXMw7j0i3kybFy9ehENlfIfW7OJi953vfufR9XXAymaacoKVSof4r0z8VdtutznXkJvNhMBkPffteNwTg4veH+5ev/4qpZ4Xfjou7gzALBSm2E+fPNtMO6YcNjrB5t1stkQJHInYTFWjOqVAfAAtD3l3uZmX+9r2RJXQQt/bO8s4CFHAGABZujIhcC93B+Bx2LkF50nAM/rOVAKHi4/cl6SRQBYkIQBtaIh/5vrf1pq02cWFqFYEcYVmFVHcaBg20zR2m7bWhLMDqDYUHPJwOrWwjQovi2gDJElwaIM3HqTZMKBXV4LOGktpcEVCYpTNsPsLf+73hOmrL2+/+OyzYzk+tAcSPxwfAgfXqlVL4VPYMeScQwXPyI/G65wmd7h4dPnxFz9/mN8Alabu4OFBgT0oxVtrRDyOm2UuZ+q4Wouxu/Wgrv7UU05zmZs2QkInofSvsaI7yhfJhF4A4vvgo8sntdRl2SMxILvDOA2lLKUW9NCcd2ZCfBMh8bWp6jW2+8oeA+h/Bc2VvCuwETGltCzLixcvTvPh7u6OKa2EASCiq+urm5s3po7mKaXSFvXKksObM8BVEekC93WAFCDtMAxLWaKujO41tgwACAsAxlD3DJ+exUPuzoyA2Fqbhm1rtZYFE+chx2ll1mXoActFacfAm2kqpSxlBsCcMiLN9WhvEdrOOkCIMDsZxnw4HETErGlVAnKAabdRrUHMZuIkY2sLgOe8QeBal6azeSNkM//+938JgH760z8dcl7KITy+d7udOzw83FJPZnIAXPtqM7Or7ZNay1yP4e250nrj3UHkLwOgOyIZC7TaLXXMTFWZWCS3Vh3C3EOePP5wHPmzz39KzNGUmYX5f0Nv4FOWi/ff//CTT39OZuBYWMwNTf35s6fX108kyW63IZKcxovtpSuYGuM4yDQNW8KUcOuahFOXwxKz9DALbWrqu+1uHCcRISZJAmCR3hOJ2q21UgpghwSbtl988tEwDl999SUJP378+N/6t39/t9ld7C6naSOcgkvFQGPO4zgQIjkmIgQ4zafj8bjM5fWrN0FDs25ji+7ORNvNJnFCJ3BwhzIXdHQPiigRkvl5OVIMBpOkVqudCeIepgq02WzWzQzRYIA7I2XJjMxAmdN8XJZ5Cfa8q4lIWRazJqtflK3hHwF+xjcPgTQRDcOISOhCmBmH66unm/ECjMglgMFwHgy09uXLlw8PDzFTDfAmUpru7x9MPdhgjCQsCKSq5hb6xvgOIhK+ftGsRnkfCUBCPRc08shDa34OdKE1NhcACTmFhNWCJekEtMyLOyAxOZVT0WaAwMzLvCBAlhwOtDllRDidTqWU8NSNcmnIObjnOS7EFQ8lwqY11DNBmNvtLsZxg4Cn0ykkbwBmbq0V82Zmy7JsNluRDMghyybiP/3Tf/nzn/88IurCtFpE5nk203GcopEhIGuuNXosRMDDYb8sJwCD7nuCruF5wEkyIgHwh9/41mbamVqrLQ6+eFDTNIlwazOxExmxI/nh9LDfP3TLprV2iPYe0AFoyJePLp8TZQJHZt1MMk07xvT5F5/d3NyAU84jMy7z4dWrr8BdmJ88eTYM03yax7S92DwWmDJndgYjQlZtkmQaJ3BkwsgICTQ8lkVOebPZsCR3fPbsebwAJFS35u2zLz75u//f/2p/uleox9Phg3ffu7i4VPXIO3v67Mmz589MHZHAsBaFHqrnVRtlBLH96bZ5JST3HjVPyKZ+Oi7zXFQ9EFELRYNj53aDCIXjCaaUVBsRuBsYjMMY3NowGVa1/X7vK4WEKBSDRBQjuJRkIzzE0UvEIhIErqiyPFwL/rXhtVuXPCAwE7jXqgjsToSSOOc0DN0Sgc8Ci6CSY/fmQUSGnm5Occ3WUtx6uFAexrAFio+8dtF92hOWzRAzJAAEyuFnCYTr5exhKhdmPa2GnCUw2ECYYqszp8iMBAAmzJLj7EMgQQLzzPn66hoNyzy3ue422+20kcTmSh0Zjm8L4Jg5gaFWzTIkzujUmqq12K8RcYBI02YCdBTENf6PVzUpAgT/SlsDsJzi42B3hHRFdPPq3qKS8BAeuiJ5rKIPPvjmbnsZJhaI2FN9MKNnsIwuiTP1FKxuL6iqPZgJ3qK7GDnKmbGLggKxsmW52x9uzo2Jr2MChExwQQSH+Yt/+E//ltoef/PyLxk8JBkZLlUXhznJZTOPBO7wuUEHItqOl6XMqhVhuLp8Roj7wxtiVPNmtWodxjyk4XQ4IYCZbrfb7XZ3e3vbhx/g7kCI4zQu80xEZVlSyrX5NIy1KjiAwTRMm822LHWz28xtvn24KeVIDERwanOg1pJEmwKiNmXJQ5ayzLUtPAgyHk6nAIpjapdTZpZWqlO0giySmbrWCTkCTJp7S0n+/J//nX/yT//J8bAQdaTJzLbjVquXWiI2+lyyEhGAIYIqMQmx1Lo4VEQPqCn2pHokDrlQBgAmKrWG0dE4DGZQWrFYVSRIAkCtnlIa3FxjgzKaKbgRYqcoIgKiMDdV4XRxeXXz5lZVhyG5u3oDQHJqTV+8eHE47I/LyczAQVIfRyWRHpAGHjsfEZnTNE3H0wkAHTq5p9bmTkkSYF3ZbRA8DbO+YcLaqvPp4tgCHIbxtCyrgtxCd+oeHpyrsyYYIoLjZrM7Ho9mDVcuFMDbsSchVavmPWsnPI2Czq3eGJFWe/iYRRo084pICEkVhiGblZiRxlIMhq2pdssCIEJ2N0BTU8YMQP/pf/q//6/+7t/7L//mf8ns5hUcEYU9mYEkMSvu1nxZiWSsTc0s51G9RFm7yozB3QhjnscIQRRqEVLhZsS0KgPc3dUxYwYy1YLoXb9LSOE1AgDEQuLMkiSjA7oTgLDkYZzrsXlnL+zv926eZbi6eMSc3Dyl1Go7Hg7CnHMCxOPp8Pr1V+Fhj2sMK7jXUphlyEPMbbfTLqVBTWsrVWtpi6oSyPFwOh1PZoqMzbTUagruwCxCWWjMNI1pS0yH+ViscObaalmqhG8fgBkwCQI9uX4yDCMTCyciGochp5SSnGfTwhIZfizCHDHhrqpuHgJrwHAxRCKMQETq8b6opknSNG0/eP+DQMsADNyDJ+DuhIyOhBIVqTkwx4wES63riBUAw4ys2wIxAYD2eA3rgQ7xXmOIEeVU8LdbacI85Nxaa62s4nBFwq9efRW7LrjZvbg9rwh/e9iLpFrL4fAAEKG61NUelHIanz59GmOw+GmJ6PLikbCgo2ustoAAJKUcF+2Tp0+S8Ga7OY8lVkv7oFhxgPABsR4OByIIIUh06N3astY4SoRjAOQIzJgZxbS6V0Iw9yziZughwGAHZE7h+psSR2MZo2MkZ4a4RZGAMRGym6rWEAlElJWw/B//D/+nv/9f/4NhGIZhimifcZh+6Zd/jSU/evRIRJhhHCZEDFwAIb333jeYyUHNjSnFmYLoKYdfNod5UtzPRJCEd9uLVeAWoxlihuon1QaeAAZ3wt+8+osYIg/jMKNAagQTQxokDzKcltkJFytCigYDXQy8FR6+9e3v3N3d/eLzf1ltpkRMjoRWmiuknKoGGVrNQWggzpmTa81DarWGw6WZT8PllC4pwbEclrqUUsdx3I4XNuOx3CuX4/IA3YiNttvd4Xh3mk+DbNjGP/Nrv/XTn/z45fyLqpWZ1dS8hY9Ba3VljMO5vDxzPeI3+6lEHPTxZg3QTZWFASNQvf/tvuddiTAsCMBQZLh+/PTh/r6VRZKo1toqMmCPUafQlIOjSNpsdqfTSd2JsHXdoEXULKxciIAg4n/QVj00YogzNpuLT7/8BaKrahjerzXeKpVEdndzg25dvbq3EkUXGr95Zi9FG8LMy7JQF1lhuBaBI4AEzhQnbBJZ6kLsiLCiR8KUCUkcc85Pnj356ONfIGMgwEhtZcCt7Xd3G2T3MK2FiGOLI8PMhmEIHW9t1d3RgbiDZD0YgAzQmTJ4IswOTf3groRDqHPjcakqsYybzfF4FIFIhWROpRQAi5B4WlOhhYdHu3dubl+mHNlI0lp1aI68xo4AIsRAgcEAiNOwtBOCIjgBIw21zcQOltyHy8vd/vC62gNTEt6ohUWLJt6Cc0R7zMuxthoutmiU02YpRwdzN0Bk4VYVnFPEHZoigCCwMDmg5HEctqf5YFaJUIgIeRg2KQ93h7vQAwlnJBg2GYF+8i9/aO7ELoQ5p6XOpAjgOQ+AGLkjEa9DRI8uL588fvb5J58v8zLmCQinYdqM21IUAeb51FzNLA8JwUuZN+Pu2GwYcmm0P+6HYSSC+4c790Lop+X4+HJ3f7iptphZXO9u0VI6rC4Va/eoqkpBQvnaLo0ICUQKXkun0dNbxrmDd6+aQDh7NAAgIRCatoe7G1WbNtPxeAR0ZjZQRBJJquau4TbALFdXF/vD/vHjJ5vt5pOPPwbwJKm28NpwiElGYK3mDj7kcTWgcEA/zQfVFrhjYqEeWArC7L1JI1ULhXcp8xmzvby61KbzPMNb2bGvuDdP02RmpbSccxgsA/BKl7Gu3iQ0q/OyAIAhEyGzEJKkVIqCuxog5Yf9PmyTiOl0OqFDOIZ60DsQCVAhqlxmlpxTUJpVGzET4bLMxGyuSMjQD5RSakphq205Xaip40JUAIwgoe9UY+8lN3v2/PnhcDgcDgFEu5saiuTOE0jsrm7YKu52V0yqrbnDm9uXiJ5krGXJafCIRwYOmPPMVIkVok3LfBBhwISWLrZPSitq1f3geCIc7x9mgGABYeg0idg9cqLUXZdlcQMmtjV9vJSFOchbCBimFm52Uu8JmkhCnfZl5ubTuEk0uFJtC6ITCVICEEYi8MQZkYmptHlux+Zz9aPa4q5lObVStDXTKMIshNpJknBGgOPh+PrV6812K5KfP33nYnMpMGzyxZSn0palzFlkM00hyj3Oh6InQ72/vy21IGJUiqfTsbWq3ojhNO9/9tFPTss++H2mNVCfSJcQDmQqcrs6rS5Ag2hOAKC/hmhmADBI9j2GOZZXhHwiABD3PK/wGUd0Fip1MWsRkBM4YXgpxRZaOaXeWvv0088I8fbmzacffwzuBGCtgQECDHmYxqlHYKEwsVBqTTXslk2RvNTT3cPrnISJckoUTryIAVWH10QkaOWUEOnZ06chAS/zYqtRna/2tWf4qiylVZ3GTSktPDQkuIfEImkYxrOKjQhFZMyTK7rCbnvxS9//vrUKoI4wL8vD/T0RElItZbvdbsZLMEbnYI9SCO6C2QZRZYdPNMR0NMJgUpKohpu2OEPdwQzMkGl4550PGAcENvNaa5TKzMFvSwD05vVNWWpOw5hH0+5DH36xT588nfJA4EzEkK4uri+3V9bUWkFo7lqr7TbXv/arf/bpk+drZEb0BOFzbVHFIOEw5hhcq8LF7vHl5aWpIyUANK9IGmJKN8BO9wVCySkTIXNg48TMBAgAu+0u59z07NJERAjOWXYA2fvjavhbV/8OC7pZKf47v/W787z8yY9/iNQ2485bBhNmZPHD6S6noZu+gA/jCOClzkMaIoQWEZuqm6ppSgNTTAsockrCMjywtMxpTON23G2ni5vbG2OtrZ7mk6MjkYGa2ZjH/eHBoTVXABficZzmWpuegKwsSpCzZASf9disqldHN7cw+Y+bMOSLDg7uInLu5QAAOw0cebURU1eA8N0O9Dzq7N4dEXWdsbubVyRHoODfmfVNY2YOYK4BEZ1dwgJVBsBAXILmUmtFxGVZ3nvvvd/93d/963/9r+MquY6Ouk8jtRKjg4E7QnaHUKKuV30DACYOlkspNSUBgA8+eP/ly5fLMkeYxUoM8HPFG7+jqiJ5s7uaT6elLEyEBBQ5dy0YKcByJhXzZrO9v39ISVQrE0ZpKpiYaGnFAUS41nJ5dfXo8snHn3zibs+fP7u7uyllRsK1HeZvfevbn3z88VKWePDM7ABhg3pOZO2zX6TWjJkRGAhaq9radnN5ff3sy5efORZhbK3z2eNDMYuaBpsFANV6fqRaQzKEhMCq6qDjxK0trTkAXV48ncbL25s7pFrag658/daqu5sHKUDQMQ9J61K1Eg2MU2tHAA9DZyQndvPCPYq2YFh7hwRCqF8hYSJn6sGIR1RrIcOgWDaeGDfNZoPFoREA/vkn/y3A9uu/8SvvvfeNv/n/+juI5B5/nwbZMqbNtDFr+8Odh4Mo8zSNtVVrCuYp5Vgy8SDM1dEZSTg7dH/UIYcfDzBBLXVMOacBFIc0mntDi8S3pSwy8MPhobaSOB1PR0cgAjNDhyePnyAPX736pOjCLASJgADME9Y2m1W1BkIEpOpRnMTa8lU0FBohd885m1kILojErAGAgamZeUPA2pSZx3Gc5xMiBMdSOJl6a5UYzr7p56giM0spndGgMwUiWuJaaxDfW2tE+OLFi7u7u4BSEHGz2RwOh9g/3OPJTCSLyOl0wm7FoeB57Sf7+0bs3gJB8fnlX/7ln/zkJ0vYiMZH7hEVGr4qbuGxR2cEFZCbYs4p53Q47AGQmEz9w298f7MdfvrTH7dWw78zxrmxyAC8tRoRVcIJqaO7Zi0+9W73SNXiz7wlCfdOlXa7i+PxoNreBvhCyPdckoBD09pJnRB4IbtBs4UZXeX66r133vngp3/6xwZ784qQouvGlf/gHvl5HLNowJ4UTMgp51JmRFerIugdLaZWAVyuLq+Pp3uDxYG0+yT2aamDgSdCcTXwhqIGBsBC7i1944Nf1UrNjm9uPj0tN8Jbdwcq3Y2cxN3Mm6807/UsjnAEjxgod3cARCZsjkc1BB/Qdt9855cJUUzx9ubu0dXjPCTVxkJhMK9WgZpamcbpnWcfuFnOkhI7uCA/2j367offBePNdPXO8w+SjMKJhZF6xZ0lbaeNcHIzImRkNyQWSalqLVoXK4uWUstS5loLIRwOh1pL8IFShPMRJ87CaTmW08M8yHZMU+Y8ppFJGNkVwLnzmB2jBwBz4dCMoIRZWGsaCjiRzWYTDyvqwPABD5PLSHNLkhBwPs3gaGrW1MMEMmgQSMISjNBgxgUvPNbKOI640h5wJQAQEa5MPUL8/LPPlnkGg8SJgA/745DGiGBlFAJm5nA/5YhIcwJnMEMHJnLt6bRMFOPKKGs//vhjIhLpNmjx3odhiCSlQIbPbXmHpswAfLvdPH36JHDfiCpjxpdfvnS3zXZkQkTNmdGNidxsSCMBCSUmbqod70Hg9eu4f6jlBG4EEAkyrp1abGYPD/vWNH6EnIdhmAA4peHy8hFTkjQwvQ0pFCGz5tiEOaLhbh++/MlP/2iz2TBNQuM6EgvyEyN2KZKpqZpIJiSW7hrprubVwRgz6Mb1gjnWrRHr4XSn1ky9ViVkQGaWyLNSdcDmbmO++O53fm3Kj1zj1bu7f/HF501P9w+vay2Prt75wXd/fTddxTg6/BYAtce/rGvD3ZkosUC/QSAcpwDi0r5g3CKkzTiOmUibE8rPf/bJf/Z//b8tcwHAsrTEQ84Z0czrvBzvH+5KqX/tr/0Hj64vw4A5lm+ZCxPXWo/H4/vvvZfzgNCR+qhwVJWRVjgTHdDMT0tpZkXLXOaqpbUSC2teFrPmYMRUdF7KiUk2+SLTZjdePn70OFFKnC9314KDVr+6ukIiQtpMm+12x5SiH3j/vQ8uLy7BPEmifrOhiCDFnvH9/hCXiYhISiyiZkFGTWkg4t3u4vrR496VATJzSsn6SGn1Vuo5TQEDEnerYfzd3/1vfPDBB+E2ZmoYwsde7PXiM6UUwtGm2rQJS1D2UsrnCQvCGcJlRO5uWohMPIwDh6C6m2702cphf+gjQcAzkBuQbPAfRGSNKpL4i0TIjPd3t599+ikRAWJOOef884/+9PbutbtGCoa7uqsIx6wiogbcnOOgAIow1SidhDhnGVJOxOiIiNMw5pS1KVJ0X5Bzon5MwDBMm2nXqpbSylKfXD+JTNdAU6JeqGXRBkwZCQCWqvtlOYGnnLf96KTOiQcIppQzU5KkrV1dXVEnmWkpJyLS5tN48c0Pf+md59/SBrU2BEAys+qmV5ePvvOd71p4e1gYsoWQyd11s90uc6sVEQaErEoArHr67Muf7o+vHBWcP/7ks6Us5zjZMJV3fyvHs+6zT7gimmtNEW+PwQWMwSEnev/9J/gXnv4VEdbaIqnKFT/8xjePp+Obm5tYx2dixPOnT29v71MaaqlqRj2BDWOjiqRgTrVazY1I0BEAxzwh0DRs9vND1bnUmlMSFncvpeQhATDjgAZItrTTUisKn8r9Nm+pjU+2Hzx+9Nz88Ojp+Ic/+iEYPXvyfL/fH04PLkoCh8MRyc1V3dQVkKZpWk5Hs24joK6h2IhcwzDMY0bk0NQb9rw63G537iZCd7c3CNDMsItLnIRNNbZcSimWPhHpKooIoDjqXlX1r7Fb4wsRHZ3eZqhDhEqaRbJzCrV6r04BNIo3i3ILcspNa6sNCYch17qE1hksohnXnRp3OGEYf5vZajWEahr0+fgz19fX9/f3KWVVLaVcXFyYQZJ8PBzUtflCJDGM3WwnRDgc9p1J1jPjz4Y9HJ180IPNLCKYggiJiCQdwFzKPiUxBQqfA3AzZRoAeBjkTFRMKTWtpdWOc6Gb2XvvvffmzZvj8ciUADj680BNKVQcFnGSSSOcAgMLADPfbDZlaU0bkCGiAoBBYs4paW1Li+XqbnGkUk5Zct4fHgKgDr8nAAdkVUMiUxdhByASAtTWwMGgQfev9bAsiUhn7AhHtFTaMQJfgwzWQABA1JhRh3uDazccRkySJXFyVWEOUBGMLi4uhml8+dVXRFRrAeiS+U8/+zSlbOZEEp5OADDkwc2d3c0DuiBkdBrzOM8LIk7jWGszMG2a83ixu7q9vaFAzwgRyI3U9PLigtnL/SzCDsQwpDS6g1GpcLx9ePPzL944YSIptRLxdrNDgaXNu40A+lKOc5nJUU1Px8Nmmsz0eDoFtYUQHUAkCQsaDinPy0lbU4in1iPJIhne3Qg7Qc4RQ9kRxW3OuZaeLwSriAHWxLLYVN21HSBauNXeiqBTTgycAHEcx5zz3d0dEuLqFgeAMTYk4iAJhcHcOAxmxsgoYGZlLsQgIq014TwM4zzPUe/FxkgsiFhbjQF9eBSe7/P478XFRa01lO7uHrlmy7IwcZiihisqM9WlmFsSaaoR7kZrPm3UJnGOq2rOeRzHw+GQIs8TAdzrssiQoyoxw91mu8yLhfAS3F1FqJTZV2faeV6QMQAFd0cgd/3Wt75VazkdD4jm7sKibUGMj2xE3YEtrLcJKVzC43Y9Ho/MwgRnlDk6nbKUULUPQ261ppSisq3V5voQzI0uZF0dHjmcxhKaGRiCWxoGQgwecuhQmQVcQ73IHIipxfi9u2l7V4C0FpO2/ur5LAEBDx5GuG3WVvD3nv63A2tQNUImkFoVEPOQwlcCI4AEkYDcAUGYRVi007UjKZ1yHgKjU1U0EKTrJ0/U9PXr10Ss7sIiLCml7//gey+//OKLL79IWcrS3nvnQwS6u789nu7Na1VLMgGSWamtgBK4ANIwZTdnlM2wIeayLCzQrI3jYOC3d181LcCACKXW7e6i1OKup/nQvMXIAkHQMKe8maZ5PpZaDMNgqjvQvFX9dpfAsLbwuD3cIUli5lLK2+txvZqQUCSpNoSe2nJGj+J7Wh+dvf1bIhKSg5yGWtXdt9vtsixNKwLlnGut58q81rrycszdiddt59TxlZ6E3dYrXeJ3YlCEfbAAZ7NJMyVkZFp/2XGXcZwudrsvX34eNfzKxFYW6tods1ARUFDwzYgobsKUkoiUsrhptNYdNRSp2tyMiHeb7ek0AzghpsSH074vTQOi4D81A5PEAB4jzUjiDBLdWjFEHWnEoubuLazYmEg14LRgdGDMsVurvc5EULXwQMfA3pkI44gZzBwhlVKbHzoRBkOzwd1k0Dtcf65dzn1+9B9LLcxc6xJitJg4eQ86QOhsFvC3H8MM3vp6+opuY3dMCQ6KBzLm7rhysnAYxiSZQTwMjtxSL6ij/DMC3G62UfSDw8XFBXOqtYUvuxuNeXp0eY2G83F5fP0YEZIQA5NyOdZXX74+HRdGdvVEcrjbl7kkEWFKkjLnQcZMI4MIJYUFpPKApbZBRmGxuKNMiWQzbVvV48Ph7DYaeYH7h30trdtAShZmdJiGaciDqR72DwjAzIwCHpc/dDKgWohIANAdhNkNwr3PzSIq9+vcAFj5A6ZWuj4rnmb05qE1g8hBDIwncindrJYaN0ywTwnp+bPnOSUwcPdlWf41/EkkTABIRN5/7wO3eCF43iSllLMvUfy7y1JiZZu7KTCKcIoxcpKUUkKAy8vL1fGZEbGU+c3N6zCwffLkaYp/FLubPgIScEo5pZwlx7J69OjRZtoQUqv1eDyGQMc99NZ9P6E5I6HD3d29cHIDQp7GTRKJxi9oySKMiGCOBlaNY07tgIZm1CojZPBkRkSZKCfJjMjI6EAOBDgN49XF5XZ71X0BzGurZ8gduiuqxXnKRFmk1mZqy7Isc/21X/uNH/zgl2IxUPep6yl7uJLl3eF84PKaMhqdPwO7+gfvfXCx3TESOroBURryBN69/OP5EyKBJ5FEErZPiAjuGGxGh+h0kAApojjctbYgVYZw+v13P3xz86pZBcfwrQksHgATD8+ePStLiXkxILbamNAUtpupNStLVdX5dALEYchgmGVAgovp0Yfvf/urVy8P+4fnz5+3WlUrsaAhubdS3CzlPOUxy6aUMg2DsVbbPOzvkWCUcUqTobv7UhcDPc77pGkah6UgIW52F0uZl1qYBUDd3ZoSsjA1RWEhJyBICR1MVYXYAMDF3UJPuDLpOEkupTi6mTIidHUbBLs9NPiwXmbWPYT4LG6mrwVpESJ4lDeNCNGh1ooOSJxEJMnxdIgFzcIf/fwXiE5IwaYPkTgxh//zOAyxn939yy+/gD5WDT0N49dkMWeGOiIySXhkEYsZPrq+vLl5daYQIlIMgWJUEyyumOUi0q//+m98/tnnP/7JnzCjqqL55eXl/f29mZV5CcwDAW5evwGAJBInlDUNs4j1n3hLnQ8FZhIx1VKWm9slp6FbeIG5w+l4BAcm7G5P1Ry74cuL599YZjudDqXOhCA8yMDzfCKKGX5c6epeH+73SCxIyNha51THczEzycnNk0idFyEOFS4goAMT/uEf/jORlFO2HrqF5z4Culiawl57GidtGg4V0f8HIXwp5Q/+jT/44x/+8T/9Z/90GIZol+Z5UbVhGEK1s/pvxXTaCChaM+zDJHS31sIqGQkA//zVX/nVX/nB9773nb/9t/+uNttMO1NvLY1TPp7uAKtqdGLMTGqWaLjYXjKyMB+Px1LDXYbAgTnlPERF4WZCDL2ackS4vnz+l/6bf/kf/ZN/+MWXn10/uXx182oppyGNI2+XMp/KwUmFJPPFNG7v97cpb+4Odw1OBqUsdn3x/PHm8c39jYEp6lJmQBtyZubjcQ9sAK6mtVUkTonKsvg63OsQBCK4Z6FSZ4D1tsMwLG6qBQndgFmQKF6/E7hrgLHQZdBgZuAr+Rb87N0e1SYi+vmC/Zoh8gcffPDpp5+dFdWxsVNKp/lw1hC3pilJSqF1tJideg8gtK+DecQhknIIcMPM3PmsxHSAXptF2CGqtXfe/cb9/fE035tV88aUol7wrjuH9V9xd0+c1RScHECYais5pwCBzIxWxT8RfZ1+GBBa95TCt+06MpdSEDtQh47mJkLonoYJUQDsdDrGXmVG7OOlDrNJplJK5l3i6fJqevXq5VlxEkHptS3uRoShr1D1eAzEWMqSkpgpMRn4GTYnRK2NkYgkLL8Bej6kG8RIPHxbIlNURMDDYwcRMedBRA77QzREsMYxIqKrIWNMNSIeATvXzbebXWultWqraHUYxqiAENHAVs6bI3lc3aEuoiHlTz/94vH147/6V/89d4jaKef04sWLYRzMgLDbcyBSTomYDofD4bA/zbO7I8G5d1pJ5HZ5uSOiuSxI9OTJM0ICg+Ph8F/8F3/j008+NfPD/hRZLKbaWq2tpCxuDo616n5/HHNm5CFNBEJGmROoLfP87jsvzLW2srvYbrbT8XQ4ng4GaqaAHlQnVaulpJT6KQiU8oAk4xBK3EZIu902pxyuU2CABggUfFpG0abEzMwdfQEIiWkE4yJQkFGFU7xvJjJVWivgFVaFM+yJiK+++ipgKlwZ6Koa9WEIWd07UPzi+YvYn1HQwiorE0mmXUEeAhRi7nBhGDzH3dYVSEzEuc+cEQC+/OLLeT5FAbDOb3q0XEywNKCXlbecJPVaAICQylJCNX52A46P1q0Mu62BxX3OJMFWHceJiN2MmdZrHx1chIlQvZ2O87Nnz999970gFdOauIVfiySrRQnJXZey/+qrL9VCzuIAxoLuNo5DMAqHYVw7PuwIbU+4QTP37keD0VCMw7C7uMhDfnL9GMDdfBiGIQ/MTNBT0gPPC/nU+UciomVZwi4Y1jkKEW02myQpwj79X30p0ae0FkDD+fewtWYKYSZ4ZjswUVA/I3tOSPC/9yv/y5cvX15fX11e7T76+COM+Um0zg7gyJxUlVlAlYhCphD/BwBqUwcc8xhZZoSQRKbN7rA/1VaSSA/l0LrOqbY5j8xcdRHB+bS4IQ94qsd5PjFmsPT48ikcLe8unGxZ9kjtvW988Ec//JOU2bwe5yNz3oy7Ms9LmWngWhcgTUyIZOq12XYzllLMfXUPDKaYBQWEiJjIFMzcgRGBCIILJpKIpLTqqG4hPuwhnOYGqAgUtB8izsNY6tx0JmY4JwgQmjtYoKAxcTHowfWOiEDu1n9pZokSrnOLcRpDLofrwOZ8kcafGcahtBIfy9ACq4ylk1MOl3MARxweXT2+u79RLUHe8j7V/boytoPSRII9MNZrW5jB3Wprgd8yrx684BeXl8fjMUwSRIQYwQG160u3292jR5cfffTRuVVTa3Hu1FoDgwzHhtaCCYTmntNQWwk0vakh8G57cXlx8dWrr2pbgByRrPk0TA52qsfzKHjdMNBaRSR3AqenT5+31u5u781rlI6IoNaYqalSogAOGLC1ttlskPBw2gsKAGqDp0/fI8/H48xSjsdD0yqJhzHv9w8ATsKh+Inz6AwEng/nlBIRl6Wad+fRlZsZzh4ccsgAbzv6FWhlPzs78+TcKfj6RTc3b5jx/v7uF7/4KDx/wlip1Z70iojTMGKPxEHVznEtpZo5ITPQOGx+//f/zQh7B6S7m7u6LIKURcCBCCOIAQXUm1ordam1HI8HQE851VprKaZWW5s2ozZ9dPHiavOMdEOej4flT3/6MwKOKQggNG2lld1ut9tsa1lY0FQj4rK2luQM4oMwMwuTjMNIQHHuWFNwiLgUBHj+/HkQoHPKwinM+ENaGVGOMfRC7I7jzLzZbETku9/5Tk4ZCbHjcoiIpubWqZExpQgMKey5Isn3TPHvEsrWEDHnrE3Df8xXjiF87YuItPUgYPM17wM7baW1tpqVgrvu97dmFWAVW/dsTwDooTgxdVD1VmvgQiLs5mXpaml3MPNIeYmJwn6/B4CLi8skYmqPr6//4r/zb63rCQ6Hw93dHeLbXN0wMbAeNmMAxJwhrA+iHiEhoiTJzFozRiak1cA9xl09TbC1pqbrDIN77UZYqwKQRePh9vLllw8P92p1ZeCZO0zjJucx9ieuzO1pmkoprVR0MfOIPH/z5rNXbz6ay6um5fJqx0LN2ul0BIo5yYpEmAU2ESO09RVQa7XWYt5gZZ7FOowjWLWdkQtf9RgB/3bXGKT4ztb54nbujMitADQWEGEEF2FEmsapY01mZvYHf/AH3//e98OUhIi5JyZha5rTQCTLvPzwj35EKAik1d57971pmIR5yMM4TohkGgSDVr1WL0ubq5ZqVd1aq6WUWMc5Z0QYx3G3efTO4w8fb55v8lXOo5tnSdpaa40JI3WnaZt2k4OKiDDFU8iJp3EAgOAiDnncThthQQcmSZwTR04uR0lGSPv7PSOtUxdjliw5cWYSoiDrrMNkIDNPKcdT/elPf9paI2AIGDCYPJyGPERLwyw554DyCYmRCRAN0J0RyYGRz7dltLXwtccer99WLUvsZ63KzLvdDhGpZ+HZ7/3e7//yL/+Km8Zx7q5qxb2FSQ8AimQ3BCcwQqcsIzqj05AH6izcoOkakRAKIQaBccwjAYYFFDoI8fFwMHVCur25/4d//5/EjzqO47NnzzabzflHRewTC+t2E0zMX5f4unv4yqs2ERGR0BjM8/zFl18gUWuGjhCzfqLWWk7JzcOtvtXmaoQMRgSSJD198oQIal0AlLtkH5h5Wcp8XJgSgbgCYbeFBUNCmYYtAiGAuwJUh8X8NC/H27sbtYbg4QkHCFoNDDJncopwzch2iycQFUf0t+e2PDoLVcspx1AHY/9IIpKw0wlMBxHDFbXXyevNEAiH5ES11UAUCTGiROpSOlpInCX943/0j+KvxFe0fIHC1VIIU07Dh9/45s/+9F8ejwcmLvNCiICkrS2ltNYkMxKCetOyGsEDgDWreczDMEDzZVk+/MY3vvzy1eG0n55NmbP4cDouDu6q5p5TVoA0sFpJSaqW4+09IsynY0rcGsQjNa3glEJQGnZkSEmGuRkCpiRzmU2dCFLqmQ7heBRXDYf3TgUkdHCLOlkbkgRqolVDj+AeyTrJAmztzTma2zBlADgej4AQvmSMeDwesfPjpV9ryGdJdLgZxcwzds55ftBLgzNq6j19yN3JYZD8j//BP9xsNrvNdlkqYZdOJea4f5hos71oTTnJw8MDAOScHz169Pr16/j4qkUVS5lTiojeFlyCnNM4Tss89ysh9BPq4zAwy37/4GoppXmeYzHc39/CiuVut9tlmZdlQejp79Huqmqsn1JKa407XQeTJNcKjuHG3xlOgEi0KpnQNPzcMBLrEBmJnADR+xNDzENurbZagwwc/k+OdH19fbe/segjzE3DrlmjXUbAJNkNhmEchvF+f6+mRF3TuUKvEgqXq6tHIunly5fTNHGiw+mA3g+meH1rTR7Mk/gHIbBKAAxCaJw+QqwWRKVIngh78B59cj6pxR0J2dWR+wlHgBTCaABGVG23t7duniRLkpSytmYOicXcp2myBq72yS9+oa2FsPPh/h6BJLFpOEqW0xziMmNgBRDmxAkBI1BZa0PEYRg++fQTMDLyV/vPEmw343a7udD2QARj2hzbcW5QyhzFrbs2K08fPzmeDrXWrmN0b60RJjPjxMMwui2ERE5T2khm1dqamlmMJSM5ZlnmUOUHAYWAhjTWVomptlqsAGLi1ItDCA0Qxsz+N//Mb/74Jz89HA8AAI6taXAHAqSJ4BASjA84L3O4SUBPf4Ovty5f/290d2eYN2CwsEpholYbIRl0G6Ja6/3dfc5Tzvny8vKrr15GsTzk4EvA/uEwTONms7l/uA/HvT4FVUP3LCnQ3W9+48OPP/5UDYAhtH77/UFNQ+ZHCEyMjrXUho1QhjyF+567f/nll3EAAUCSpFW1WRJptYI7EYeIFBGF2awT9z/99DOR1Fpzg8uLS0R8eHgwM7XGRLvN7jTPLXjq0Ov26CxaM3djwnEcT6cjADw8PKycsPOz5V5GMkYc9qkUgADASRIDyLzM4J4kO5DkQYsrJkJyXPNLAJo2BCQCBVDV1po7jOPg7tpUUJq2sK1EBwJA6rkeicVVHdBUSQgQo/KNbkIkhYvaOpxHdVdDN1/DF6EX1X/5vf8BALipWkNGAwVwNwgvRkI2g3CdY85nC+AgCZvZIEOrSiSEzN0hwQlJOKnWZurgxFzaUloBtMTCxEwiJODAIvMyV63A6B4BQSw5WaXfeP8PqEzXH2x/8eqHN6/ebGX3ZnlZ7Hh/uJEhmZlqrfX0G7/xZz/++JPT8QgenkMI4DFmyHmYxs3xcGJmRkYEFjoth3k5kXCpNeWM4TgGdjodCCnlERwcgkikjriU0/WT6+snl3/6k5+CefR+Z2QPEZkjO5CrtjTk+TQDQofUXYPUxczESCDaKlA/m92QkJExAshgJf3+a73o24rXEQEtaMmIiKhu3O1m4nrxc5fYO0MCD/HgsDmewuQ2TgToL9GBwB2cGc19yFOtQXQtMbRHxDATI1oPpkj1hd5RB3GBeiPYuZPkFHcmkq+e0XC2HT7rbM3MQUVYFYSTu4dHIRIcjscxje++eOeLL79o/SMDCfa2/G2zTWcMRtdB0ToOw5xzjIW6dqWPrwHXyHZTNYdpGlvTUsp22oHzu+++94uPf1LKwiLE1FpoQQN6NYfOQCREYl6JRA4Aa3KxB6r39ReJAAZucP4p0MzGcWqthAgxQjeDyA3BPfJeB4MDlaXBqlY5A1a4VsZx5o15yHlw92DenG8JIqqtxAAm7JZ6+eBwFmcETjhN0ziMu+0OEQlws5lM1cyPhyMRRSZSSsHXp6YVEzrB1eXjZ0/fv7p4TrDZbR5vNheltAjPqq0iOTH883/+zw+HAzNP45TTKCwEKzXXrJaKiNZsSPnR1aPj4VSLukEpZchDlCXaWqsqLMziZil4bpSePHnGJEMeiWgzbWC1boivYMm4e9iIWpfgcEpDYFGIyJwAMA6vyKl0dwumEVA3OtUOhL4d0qzN27rm4NyveggnkaL6FeJwpOhDz5hOO4zj9vLyMSEnGYgEkVvTnITByZ3cGSESMMAt5ZyHHGOf4/G42WwuLy/NNOAjRopYg7gnafU0++aHHyaRIQ8ikRblUbGft198Fm3W/fi98xwC6MIQJAmLpNYsSG8BuQGCmgWv4PPPv4hGNJ6Vr+7BtJobBz4f9SEziaQkWTgJD6H+dTfVZhoXr0B4FHNCIPTwggV3YmZCOJ0eat0fTzcInlIa8hh6G+Z0/eh6GDKAhbOJUP+niVgkA1DOOeeRSAKcgxX+jZUAiKGe62gQxciwBewY7CtthmvaRWwuQnRzdydJSOTM4AgOjkgYYCBGj8ssElV3SkmEmYEJBXmQIexGqHvzyZBHQWFnAkokDJI5J0q6KCpu0zZ5ysxpgH/3L/9BntjBkLgPLQFLK4HfevNk/Oru04PdfvbRl/UN78bdzeElNsg4DGlCd2HKw8g0Sk5ACtiIUKsLSOrhC9paaVod1cgO9ZQ3mQNT5CHxwHj2nwo/lCycEkkWQQOr5bTfD5ISyuH24Yf/7I8zZwSIUbib5SToZlpFQinlCAhqmQUN0YmACJAxShIkQEbgyGhwYhYRQYAseUgjGKIT9L+4OsrFMlw3cMQUEkESCdcLdOgBZH10FjWxt1pcu/8gmOUkYBYm2nIe8xKLZHecpt047JiGIO6XstQ6m6EBOEHVAgyG7g4ISRtGD3X16GKzHc1r7JBpHFMaQswJ7oDG0slqzKl/XuygLQd6pNqJmUTq2nQxr+Zaa7FqZDEUNmJEd3IAdVdw9XMMB6B7sN4NGImRuU+KUbizltExeJEMQg7okESGlE2DdCnCKEyt1PCXLLV88cXnpVZVLXVpWpGAGZdlDsfTcKtzREeorTmoeXHoIttAhrDnpqWovXMe+hCgp2kFN8tUa9ylHTRGQmA0AiNAcoQGauRGgP/+N/+j1pQImyrQes9C1GWU0wCArWpkNxBhWY4IxDggolohCCczSZwBkAERPUqjODuXsqQhM/MynxyMM6o3IiqlCqV5XkqreRpKK/M8m/pm2l4/un7z6vbR5sWTi/endLEsp8NyY1wBnBJ89urjYsemVUTixDKr4DrlXeZRiB9fX3768otSa845+MlB+84ktNp51VpYUL0FOo6I77zzzueffBYXOzipOXPKw/Cw3wMaEZg1RGjWwF1Vw/IzHn5KOSQHy7IQcS9AhZfl5N4rmV72mCGirh4OFkb8ROvwY52OMsA6XzkXOARvXSC0Sy7k63VyMJNwxSWJULWEjQNz6D9r9OHjNN3d36WUSqm906M+VS+1uGkET/SbHB0xPMbSZrOpddlsxpvbm0BHYhqXUj4e5xXWVZG8zHOw3s/lQDjqQPDuYuKC5OjqnQsBHo61tttcDDnd399FVwXg4OAIhk5ATStSn/0igmsfn54Jkohg5lFyt9a2262ZRbCvrAkMgajHE764uLi9vWXmi4vtt7794R/9ix+a2Xa7PR6P6t2SptYqxFdXV3cP97A2DUHuOksRY6xi3eGplz/nnsXBA544D9vijUeEB653LyI2bY4rlh16rNXvFAgRFBLLkJIQIQE4tNpMnZARyJt7MyFBp6vL66vL6yFNwjlJFhJhGVMe8zClzZBHWP/Ji93FmAdrhgCEyDgk3HpLYNRarVpmK0ud1RoiEtIyL/uHvYEey93d6Ys3x4/v5s8LnJZyLG2+uXmN4FeXl6H2OLu8E9A4jNO0YZZlKQgwDsOjq6uchBET8XaahBkBk0icw/GwQkhBAK9fvhqGgZCYEhLH4QcGzDwOEwJ32VH0RxLmgCne+jyfTE2bCgs4RH5ZqzXslIc0ZMnoEYoY1oqdj97X8dewIhFh5uh0VhE5EtKKlFL8MeIYJ/SHHJryFY3sO0FVmVNQdiMLDoHMrNZaloWIaqs5J0Iy0+jdaq3U69IUXKicYyLFRGSqy3xiYiKJaXOrLYCQw+H47rvvXl9fQ+TQBA0GIOy2iGAYU0Q8dbA43gFhEhGkRMyAUV0LkjBvpk2o6qKmSFkQkQIBZgGAxELY47pxNadeMzXUOkKORHw6nZZlWYc9pqqbzSZ+kjgEW2u//uu/jog55+9973vWk6ZCgdiNF0XEAUup2PceJ0mhBL28uLrYXa6k0NZN/z0GKgjhSYWUosgyFBIwQEeIqIuv2d/0iY4D9FqMBORq8wj/yvv/oTuKsJoRkggTs7YKAIRiBoxysb0EQCa5uX3NjELD1eUTVS3l5A7B/SeixImArJmhNW3ExMSAjohMPC8nQAfnZk6JSz3Udqpej7XGkNsNhBKBMMnjZ88OD3ttjZCvHz2pxebTXHWWhM1K87LUJYTCROzeCDBRvthcEWIpxxKo1SoWCfpYopTTwCxVm7mXcmpaw+w32ILukNM4zzMCOlIehlarmobcCdGbLojITO5WWw0jOHOttYFjaM2YGYlNa201sP7oQ5h5nk8pZ0Sc55mFKYYuiDnneZ6jSg8iWiSyhJi/1srdyjSm2Riwan+vgU3/q1Yp0zTNx1MoV4dxrLW01kIaGQ2iraOdy6tHCHh3dxdIczRyvIIiLGzazhc1rASMWitLt/QBJG3mAB9++OH93f1+f0/Ui4XWQvaoLMzEBFRqiwcSS/+3fuu3fvzjH57mE3brxn69tKaEqNYJFcEiaq0h0pMnj1++fGngLN2Pzx2ISM2EOSR+rVYJz0eglJK5tVpSTqV1VCVQpfhcrbWU0vX19c3NjVmLkVKEdkeIqwXNmshap0YDrhgsuDsOw0iE+/29u0W4BzgOwzDPcxwHc+fSon+NZBJ70sGB4Ix+wUo5AqSAf5n4cnMhIhzOUoSYk7i71Rqkc4Iei1tKySmBe04J0cc8/Oqv/OqPfvSjZT7Fieg9Vaq7V5laTPOTJETQpuS0GTdA6IZLqYAOKZd2BMDM3GoVySy8mS6SjERyvD8202o1Z3x19yrhgIAkAaNFYhOYKnKMrzlI7LU2IWJOCB20eFsfErZWIxgCzIchm2tnm+NanIC42maYllIM3DTisXRIUwU1MAmxYi2qoev1KHqJECEWUyi21FwjPuxsYjSkobVqqtFuttoe7XaHw9HNWqnk4G4SgIe6dOIReoibRNQMCcxs2kxmlqLWNQc0SWm3282nU7jmulldFo75IYs19aa0fquOvjBHI3DY74liJzQAuL6+BoD7u1sACHcvd4Q1M/ctFhJ3talF5Gnii93l5599QYRErK060IcffvjJJx/Htgf1ptUNCSlLysOwzDMA/PhHP+aciIu2bv8dm2fIg1l3JrCmRGhNEZAA7+/uckoxgK0ec34Ac3JwtaILIj598qSWZb8/ijCRQLQqtdJakROgBcAJyEiu9vLLL3NO4J7CYdScgAIECiussKcExNaaJBHh03wSkXEcT6fTerQhIjRt3G3oqJRyPn3MgmHytgx2d0cPiRauRXPcw0Me1bWUQoCllvjT5qZgbqESACJMjEGYFldlgsNhfzodmZkQVds//sf/eP/w4GoArq6KGg2Jg3HiKOUDA8wyjHlMnJiGLBMDbcbE6GU5RV2H6pMMCXmQUSiLDM+fv/Pocgeu5u3hcP/8xRNmOxzv5nJsWs6nGDGbKQUpT83NmMjMW20p7DGbZklZBN0FENyYsNaiqlo1sQjnJBL1fyJh7BG7ecgibFqFCcBLnUX4anexGTbXl48Sy3azyZIFKHEHcgBAmIUZHDjWYkrrcAjA8OH+0BtIdyK6uLh4uH8If0qimN73DufcoFLPB0FVPbN2lmVZlq4ai2pFSyWHP/fbv8OAiXhIWVtV02na5DyU0m3+QxfBkV7ogEzwltjUV9LhcJjnOYKVGUmQxpQlaB4ewXAWrvDuIJyEJXDg0+E0SLaq5BCpcF9++TLGEpF0xtTbhMDMYtXO83w6HMgxfIuEmSL8ptRvf+vb77zzjruP4xjPLSI2W9WgiJkZ+irURESAJCLErvadb3/rd37nd3a7CyZe5gW616m7WU5pSDnSrhgprdBfIgEH4W6tTsiIxMCEcbMyQbDHiOOAUwtywul0DJJ2tPeqxpSiMfl6dRPFMyODd28V8Oj3YzjUha9EhACMbM1AnQlLK6f5iP+db/9HrTVGinYrZG7X18/v7++1hT4IxnGstTIlcDVXhJx4fP/d99+8eXWqCzGUZSFAJh7z5IaqhgSD5CQJHANnVzNANKvmS9H5UE4WGJxbK42Rc94UhV/99d98/erVVy9/QYlPbW5mA2evCg6LNiJIxGY6t5kSKzg2VKvCvB22BGkcxlpOS1uivgvZZIAi0zAh0LJUYglWerWGaA6utTEKcxaWWpqhNSuAHgDVslSE8CJz1da0TmOel5O7EpO6mpk77nY7d9/vD0y0tjMowggsLEtd1EvU3oFsnZVuZ+LR+dCNTXgeOZxHpmY2TVN4OJw389XVVTCK3krAQc08pzFAFO5CKuyMjPgiDJCzlBYAcpg5ATgCohFS+OtWAMCINsCwnnlbmk7TVJdyeXF1PJ6WpcTBYh4lMMSIVBK3Vs0MgaM67WBS9CPeBWIAMA5j09ZaM/U8ZCY8HI+Xl5fzfKq1ElKWfHF1sT8cWqu1VVhXPyJi97PtZkjM5M5uaGab7YSo9/d3MSHzVU/n7uYeN3ZwhpCcmZdl6anesX6ot4ve/NHV1Wk5nZaZiGJO7mjnjxPMN0Q006hbzSzOrHDMgHWctm5dU7duQrF+EWEtNqQJOWK+FMAElRIkJu7TOHAEyHkSLlZPKSVEn+cZGV2XMNpFpyT8jffft6Z3n32UEm2GKSrHq4srdHx189odWEhbJSDKw3oPFLVqYOZILoSe04COiy0r/mlfvf7o7uFhaW1K+dmTd29vb4/HoxBuNhPWejqeUIg5sTk5MLqCUTjXE4C347wXkZSGeFiRSrTdXAytnU4LQnM3s8YC4SEWjzXOEQAYxo3b3HQhByIJh+VgWTlU1QbgzDAvp/MTR0RGdsLj8QAAKVHVykiIPOWxtbbdbR8O9w713OYQYi2VKTIgvNYaUbkGRoCSUutB1BjBcEyMq/lOv0iZDCAEOqfDzCRlWQKs1ajCmRat3G+RULpBALWIOIzDaV6iE0Jo6OgKiQnALRIoevvdFzS4M4pD0xreJU6I02arzUz9/v4eEVOKswYRiCkcZAgQamsx1A3dbVx9UVMIESKP0xh5tqElQEcEq/MpNCYPd3dMFELtMD2ptTAzOqLB1eWTZdG5HIJ+6m7mCM7YsLbCKRHa4fAgIiI5YrhWBKsXETU8aByQ/MyGh5WzWWt1cACMaquVxsCZs4O11pAQQeLG1KbMcAb0MdBXhKa1ao1MvdWojCLppzbNKQW5AAjegvnk6jM7IxiFqfV/8I3/CRL5qqMBwpRSK4CAYx7MNCVZWqmtJpZwTMQ1fwoc3/ngRanLm1dvEBgB0HE7bQ7LCRA2eZqP8zRMuDpiAjgx7S43n3/xuYERAjGFshGJlrosbX764um/8Rd/92/89f93KVVSOp1Ojtas5SxWHQHHabvZTLc3b1Lm0+lEIoBQSxFiJgEgVU9p5RWsEi0zC/pI0xaTJ4OwegMHT5JVQ52IrTYHBfQYr9daiTnltCynGOGENNR7iIiZaZDp1A3xHN1nYDykUVXNtXpF7By3KBcBMLwCA2OE9b3GbCAJq7YXz19877vf/fyzz25ubh/u70wNAB2gWSOmgHc7QZTI3AD6lVt9FdYggntiAV8TN7ALuFVttVBiYW7aYpgU9Rf2EVfvtBGRCM1rrKIoXIc8HvbHQOmiSzf34FuoqVmjXiP0jvq9d94/Ho5N2zzPUUG02iLWNR4CAhJ3zCnqZFu17621CBMw17aOA7rcvvOyXN2R3MFMYaCch6G02lpV0+4QRXguWGClYYausJO7QhyDFNLC2KsppRADbYdtSnkuc2nVrAETUm8pwWGdhcY9GYsBVTW6KHcXZlND5PCui5zbdTE4C9da+7xuvVz7WA5JVm5aeOEjc7h+MqLXNjMzoAuxUy/DWq2MPI0bt3Z9df306XXK6fVXrwBBmxPR/rhnEQcoZRERQGCmZanDMJpba/Xm9W1iYaFaCjpGrB0AAmdXbUv97GefjZwAHRQilysxqpYkAziY1tNR85BKmYnD9hrGNNRazWEzbbTZ0o4xLnOz8IxmIlXTFYTofrlu47Apc3GDIQ8I2FpFAANr1r75rQ9/+de/+zf+879lrsui6zl3nsmHl4J2ppEpOMKKn8X26VLMLNY0wtg7Mc0sjIUQQM2eP3/+1VdfibCZhWpXkAD8+HD45KNPrq6u0OnFsxfbYTMN43E+vnr98s3tjWqrWpFTU9PWWBiIW2tIlICgRyg6BRkC4cwI7/pPACS2bpzlTBy73T1ugK4BoFVAh4TkoX0DxHALOAI6I51VASvLygmJOYczTCC0Cu3+7g6D0QoIDomlLiUq1fO/BRBU6m7TJZLWwp5X50tvBswMwUlicGzgrI5Xlxf74/0v/er3/+yf/c3/+//lP7+4vHjY7909bnbVBr4+gtCOdCcUZJYYeiECx8/DhoiSkpttN5t5noP2JCl7DxahAJ+x07qjgeiYkHl47nTPKkDvJEDgnIf33nv/o48+im24amjQDXLOTdX6nAoJaRzGYBzgf/jN/ykLR34AIG62m2VZai1JGBxDU2rmMdZx95hZCUvYYDa3lDMAggMTxyGNgMK83ezAocxVOLk5c0Lhw35vWjlRYj7Nx2ka1ZQplWUZp/HpO0/+5Mc/STnFgjjOs4Mpa7NFUcm7cY67u4ea1sH7wyKglKb33//GfCxLe7i9vYmmbsgDIS7LEh2C9euRzLxp2252bhAGVuFzoapA0Ezf/+D9cco/+9kvwigspZRYTvMpikg1G3I+nk7Uz3RFRBSutZjZhx9+uNtMP/yjHw95AITmTS06Qzhj8bBmWn/jG9/49NNPzzFT56YUEbU1BARwIh55uNhsL68u1eq0Gbe77eGwJxTh1LR9+tmnx/m4LEvc6p1vwDFIdDUF7NoUX9Ha88VyBqvqmqBx/v2vd3GIwQ8JmbKHqbJqv//PXXRcbzGxJIIA2L0LDPoQIgxoVNXB1T2nFN+hf/xwflBT1dCJIGD4bBhoaTXnFNi+eXHXpsHQIaD2+Nnu+z/43n/9d/9ZsAJSkpCeqirJW239GdFR9YvdRW01mA/eiSUN6YwXQPTqMaCO02SpS9zMYYMC6x241kqec1Ztao2YmzYAV/WI7Usp1VpTEjU3beOQ1XRelpDCnCvysAFBwtaahFeIR54HwnycESEzBzHf3IQTIqxSZ2cmQg4ps4OjU2A/SOhgNQAngMTj4biP1Bg33+QNMk/jBtX2+wcydPSLzQ4ITqfTkHEYRkJuRTfjpGaqsN1cJNkcTge3GQPNtyYpR5Z2gCB5yIOMx+MRAIRFhPb39xRNGhEiJklu1mIqHeM1MyZmTs4+TZvj8cSr4/hZ9e7uAPjqi1cRLpRlaK2B4anMiBRmjdp08UWYmdjcWujsmxIQIm432912BPeccykLIylY8C9FKM7Vtnp2ffzxx2fwIF6zAagbAnE3OkInLNre7O9fP9y5qySe55MI74btk+snL168+JUf/NLd7Y3klFI2hFdvXt/c3MzzHAIxETGA7v1NJMxR70axGrfWmRZP9HZ6eS59+70aLF33uE4Rg7jRG7lYgjGBMAA319BtE2+32/v7+/OtCwArVuy43kewSl7AYRxGMzsej7Tq9YjYrLz3/ns/+OXv/n/+9t9BQUIqxRyRKW13V/v93h1vXx3+f1/+Q0Th8PbqBB5E5tIaExNRU3UDWBlyS1mmaRKR169fr0YTgojaLI4I7MQmjCM+6m1TA3dAYOGuroAOREfXambM4m5MFPO/JKm1WsuCSMEwJ6BSGjMyUdMIakFAc8I+OXAQQfwff/d/YWaO/YyXnNUUvUXqdFAdTG3Ko7tGcgEBCeeEUlX7ygaEoGv0s1Chgx3MIMJplHEaN4i0lHlZ5jykUpbSiqOZW5YhKsBSl2ZtLosCTGnz5PGzIQ+/+Oyj2U5F51U/FQ7rGqbJUVS0UokopYGAEcnZAMHNGVdEEXHN7UDmBIgGPo3T/f4eAILVib0NAGGptcb6q7UiIRIZmIZrOViznsxDRAGfeo/CVYAIdwFtSmd4lgkp3IzJXGGlkhFR836BxBHbT1LoymlwCE9KRBSMzBJDhJRWo10AVTW1nAQRpmGYNpvHT5+Om6mW0rRNeTwej8s8m+lSyqtXr5upAyBhVUUkSdnNps3mdDpZ9wdAtcg+A1wdAC8vr2qptZXnL55//tnnAGBWASKYoSemSUphuKMt1Jstmkz4WqJs2BBArI/udINRRHyNareSk9ZfIiIB1lafPnvyvV/5zt//e/9ATRMnVTXAcRh/+Vd+5U/+5E+WeXZwCP2YAQK0VmP2AwgKb/kD5+9Ma7YVAMaODMgXvobHno+t86iztbbb7UxtrjMRRiDo21KI4NwMqykRIlFOAwLOyykudpHkth7K4RuIq0oB3awCdFcKVcX/0Xf+51Glq9vbsfKQ1bS5qampEcDQOWiGwOMwjXlizNoaOgx5iHuZmaKfr3WutTgSU3KD7bgbeAAD4YQMy7I4ebPatAF6s4bh+oUoSZq1u/3dAoWUr3ePAZAzfnX7UqGllBEIAMdxvLl57WCPH1/XUt2stYqEpsbAiFy8ufvF7iKm5+4u2GmigOiO7s6SAKnpEiNpAIhpCBFlyRongampOgIiGSgKBhwSh+XX7xwzhf58GxJbWBmbs3BrDQiZ0xkOibSyuLfVPeIrI7piZbR2cWMAPEzsZg4h3aBoXpgIiRpqtQYGjKityvn2cyfEnBKYC/GTx09+8J3vCqIjfv7ll3NdOKV5mT//6uWptEhYhG79TOYWhKGzCwl058RIZrJwG4vNFkz8aEqJudZ6eXlZSqCpWmuJvUZvM9R8ha8iE9F1dWA5b5u4uuPB5pxCPR+0yqpFraWUEBEdzQyIifozZGIzMAcgZyRtbRjyMs+E5Ahhsuhrhi11l/DhcNgTxdINwJ8B0M3V1gkNYkopgp5wJcGETGq/HAIAO//8sefHMTVVybmUUtvCEs4MoNaPLXMHI8JuQ0FEBg1WaKC2drHbTdP06tUrRMT/zV/63/3BX/o3/+b/82+9fnkjnLBbFzZiCrzaeylCDqZmhDzmzW7cuQKzmDkTZ0re3NzSIEreysLu1dwcCWhM0yA53IAIkUNxxXAqS2ul1NKskUQwFoKDut6d7sqigCiM4yYf5xmRAVvcbAEjmFpOmdxKKZGIYe45D7VWdSfmIeVSlvB3dQDtpvWExAiYcm6tMTA4IHEoBAzMUeOnLbUutRIiJ2mtFW15Stq0tRJ8I9WG/baJ58vRjyFS1YaA7uEAWkutOWUH8JWAAuh9Q/bgPXCHJKm28EBu7hZ4acCDTdWsU9sghgNAxNS8eXREZraCnwQYZqshy2za1PXR5XUpTWtNLM+ePr3Y7t59510SfPnVl8tSROR4Or5+/VqbzsvSzICpur69yrBvnvCJ99VeEMBzHo6HIyIRACJIYu23REwEDQmZsGq/l4h59eDXIJCu9wnHHKgbLITpe58OIK6x3HE4Pnp09fr1G3dLOZVlISYRNvXWGos0awHc7rbb/X7vEcnjzkRqZqrE4S3o5krI6zEExMxM4FBKEZHwH3YA+xp9D4lslSLEOAARmBKilLIwW1OPwc+02VStyzI7eGIx0xb5CWH2AUhITcP6qEfFhcSxD6uAzJQ54f/st/9Xj59e37y8OzwcCIWCiUjmYGA97tbdmaXZOVWWBxlNYUiDO+SUp3Eccr69uUUmR7DaNuOYUp6XioDCaeA8jVOtrdXGIQ1gOC1z0aXUZW5zHvPhcDDz3faiqVav07Q5HA+lnubllFJW89aWsKEPVro2I6App5h8qKojMPN2u727u3/06Lq1end3x0QQl7w5AkhK7uhmkhICXGyulrl0INS9ajVv4ccBCHVle/bSlKCW4mCqLSUBBG2VEGpTBAy5CQCUUmD9hp1lAAAAwzAEZQwJm9ZAUGpdOs0IwgyyBeM30PnueccUpnCEFOgCEY/DVFsDtJV4gOGHY2ZMYclvKQsi1VbdTQ2QmQBNlQHB3dWYKI9pGicRGafx6urRO++8YJE//tGPfvbRz1G4aqNw6bR/FU/qGmhXa9O4aU1btSxUykIE3ok4zbWtBa0bYGstrPTfvHnDxFGdBl83FuX5Wo7eH9apBKIEQSfnXGrtnC0mN5MsiHg8HsdxBIBlWaKQDnAoBkVDzk3bmfUB3aYYHLxpY+r1bZwUcSpN0xg0j7cq9rcZ0AaEtbWY0zRdiNAMCdPV1cXN7VdqCrj6DMYgdJ0CAWIziyLM+m8wQLdBjsmQthr5FTG7NTPxgp/89POUBuEc0wVzZ0Q1AEQmMPOcBiKC5kzsGhU/jjmD42aYHH13udmfHlyaGUIlIRFK4CjEhJh6op5vxumgBzoDEkTWtEG9fHRxc3dTtTDJ8bQn5sjb0FaT5Kjj3Zqw5CEttRARIW52u1YKIyCxYgTBmpnt9/tpmAhomZe4yhwcCV3NAYUIAJoBeQTvODNFF0qd6AnUAxRMiGzFOZhw2mwf2u0wTPMy9/xcJHVFQOaQRFm4ogYjRqNYdWORVluZF2a+uLy8f7gnxD4YDNePZsQerWxQHQGRILxdFbwHfwTsFJ1XLYWI3EGYbNU5xfUbf4KYHECrxiocp8SJ59NMvd4CysnBomSIqsk//viPfvhH02YTV2XOmZRUFR1Cqt/J6KvPsLtHUM2jR4+P++PTJ9cfffQRsyh4UwX1IXfremE5lUpEx+PRV7uGr3ncBm24d6rCDORVre/Y0K85BDl5SOk0zwDAJIbaalWz4HKdB7wxBokljg5JUrAsozD5Wr/tq8HIW1UgUS/mEbvFfuzz0zzjOQPW4fr6urWmagNPag16WkvU3e5gQr23cveAv9x9mqbjfDLXADIDkjWHuOoREayvmc7rjM7oP/6V/0RVwwiqaQNEc8MOuDOs8/docBGQia05keQ0MDE2aK1+4/vv/Xv/w3/3P/s//z/2N8cE45THi4uL4/GorXWBhdkwTEOaQkO03W1vbt+cyqn4clgeWm+1CQGmcTSF2/sHZhjGYV4KIgEqoIGjulatecgeDssOIc1i5qoaYA8RXW6v5nkOlLxZq60BOgET4jqOw5QGN0gsCGTmzbpFk1qjPpc3ZnYEcy+lEFFtLSXZ7jbH47F2xZ85hIGN2ypVNbMAoiIz8+3r712xx12KtE4ag2EIEFABrxrI8188E2LOX6EzRsQ8pFZrKYWZw2q4o7BRWwIBoKsLi6PnIc/zLCks1HxZFsS4Vf1fw2yYOMj06w9M2It5gD6IX7l+jmrGJKuI76hmxOima1iwjeNASMd5RkKziEXrTAzt9C/2FYx1B2YUoqI18A4ARGJtaqphpLHGKAJgdMhvCxZfseuvfxx3X9kLcL4Vfe3jwtCfhVvTJCnn4XjcR0MelI9a6zAMwfqKl4uIzFxbaxpqUgcgkVFbBazNCzGqGoXxVaDlhHIGCKm77LuBqqUkgL7am4B1s28/k9vwP/7+f0JMxOHrwEstrVWA7uxM0cbFPMCdu18JajXhJMwMoq2mDT96dnX75tYqsIqwxEN/+uSZtnZ//xAyw2maVHVZyrgZnOzVzStgK7YspREzAJalTHnIeWwK9/vXIgyQ87BRW1QXIj4tRwPt548DIQ4pYX/lfUkBABq01q6urpZaSilqlnNC5Kat1UJEwzAQJXBGcHR0ByIephEQ7u/viZAIY5m6e/AM4wg44wSAoNrCGkHNcM3h6ol3hMFiodUBw9cnTsF3QQNE1W6SBgBhNNBZTURmttvtdrvd559/TkSA0JrCmV6HYUllKXVHeQDvNtarJVXY2YzDUJe23e4eDveqDdfVCT34J/gD8Y1XlTYgrgVbwDzWQdqvVYwdIw12Tj6eZgBiPn9PbK2GTrmVturSQvCF4WGdUtpM0+F4jIVLRAgRFoiIjoTuCoiGPo2bsvQcrfMsBxGjyZfUgzACAozdlYch2M5n9pK5+UrJOgPsAJBzKqV6kBU8plRU6hLjK3g7K+4q8/MLWqtfcFNAULV33vlAVW9uvlRvxNFO95TaPpgRsa6e0arhQ9lNqsIgiplD0hD3YvyORZhLUFO0NScXIuQIOMKckvWaDYTSkLOptaZCAqSEWGtzhCxp5GH/8kAuoI5EksWqJklglkiGNAAREpzKAQGblf1xUWylze4+L6ff/K3f/ON/8WN3HNNQS52GjYInYRYGz08fvfjq1RdV52kckPxw2idJpmu1sGJrCKBvLTPN0e8e7ph4zENMd4GZkSDMoMzMFT0WvRJJztma1loZKXpOAZmXRZJga2aGhCiCgMBk7uMwjuNwe3fjhKTaqm43WzNbloJA0zS8++67P/vZzwPb7EUsgJqhgxA1MzNlpO3lxf5h7+BEbN7dBmJoZE3n04wAwhzFcHRTGNQRx7CoI4Q4RgkB+hiTmZK7MXmp1cHv9/cRO/D2MkF0ltYahmibOCYHMU1Yr2IIScr+dGSmWmoI0GMiihh5x6XWIkTaG2YEBDfMeYDOQOze/CI8jmNQRLIkU7u/ux+GoXlFJDAHQiaKAAJetXK//Tu//S9/9vNlLghAAF3Th7iZptPpFDnRWvvsJI6VqFR1HUevhUyI8+PQRBY261j9GU+OOdxZEuxf+0Ls2+FM3ogLlJkwsbtut7u7uzt3RQKqiCBJ2MGaK4ADIveJ/UpORNR+bEULYlFIx5Q0KoWVFxhjZAJmvthdCDM5CDEjR3JJlpQkh4UPOaJBIom6N4RaLMgsGYddutryxZQ23p3geDOOdV5MfcwjOampWm1a1FvRZaknTFDKPA7Dj3/0k5zy5cUlAoO6q9Vaw7ROOOe0fefF+wSyLItbbxqDlD0OQ2id+qFF1EeU5EDOIojYanOFIU2MDO5MLEzokQSrxOjuqtpKNbPHjx/nlE2tlDLPc5SxiCgSzgAAAMyJkFvTeS4S9bNjgEZmlvOQUqqlfvXVK1wLVF8B+sh0rK0FbE5Mm3EjnLIkaxrqucjCI5Radf9wEM7B5QxdFTO7mTCPOeM6UQQ4kwsgJWHk9168O6YRDEwVEEJXG8e/iAgxAZJBQhLiIUV4pDNSZHB1cj/gcpqPhwMhhXF2nG+BskQADCGqVgALG4tgOiHiMAxMAoZ5yEPO5OTupZa3DS1AgD3MPORMxBFv2/MRAYhou9386U9+Gilv4eJ7fqCn0wkAxnHEda4YVejb5sJdmKNXJ8RApOKXoVZDAMaAxjqVD2G1g3Nw96jhz/sq7DhoNUaDFV4Jc0fVZtZqLQ7++NHTR7vrMW9yHokY+lSh29iHqyNGGLQ7AQhxIk7EYBavxteIzTj4JFFyMwLS2sKxx88Git75wELkbqW1sKIGdUe+vHh89ejZV19+ju5WnRESiYNvhs28nKorjjhtNtacCDmnZjSl/HC4JwRk1IaCyXw/1wf0zWYYW9HWZuc210OiMeGu1oZinPTd9z5sVL/45KNn77y4u3uzPz4Q8MVmJyLWjIlSSu66lBLt0C5v0WHRYq5qKiwORuvTdwR3BTchUIXtdjvPNTJ47u5uJJEtkEgocc5Da20uc22NkbbDZq4LuguhupVyMjMhKVo246bV5s3HTR7H4fbu9nD/wERDyqqtaHFEa07gAVEhYsQm3d69cTC3XlmBY86DeYtsBWYWodaKWglpRq1FJLmrekFydAhCiPe8aQhg88svP3cHIWGIHBdH7KAuOXtkH5AZkGqP4sOIWnJHtJhxuiEjbaaxeam1CY2Prp7My+lwuhFqxLlWBIMgvnFfuMZIza3WJfoC8MhTrrtxh4jHsoe4PgCJudnZHhrcQ3XQ7xOh5IYPh320XejAGN1+l9QQEZgz9OSr+K+7xx0DBNvN1t0eHh6YKObGfaKjxkgpJ3WN+GMRib0Tc6Cw0z9fzv1Ci0kBrMqV+LwM1SoCmhUiYgFXqFrMvLQC4GAWLc84bubT0cxSzk1r2HEgBOvS1C2UTw74/Nnzu9uHeTk4GlGQhDta0MXVKSVmCp+1IOueA7PCsaKVQghgftgfRORb3/zWZtpc7C6GMaeUdtttTjlLSsTadFmWJ08eX15coEMWieONCM3U3EpdEFHV3LxpU62EmDgL5cdXjy6m3W7aCcHxcPvTH//w5s3rlNL+4T5eCLov8+l0PLmBiAwpucbG45xSQhmH8dmTp8wM7uq6lMXMpnHMOZtB1IFhQmdmOQv0FrTVuqhWBLDWwK2WBcyjcNCmYKbaiAg7I9wRcDNthpQD2Fjm+eHugUiSDNwDZIEpCaecMyKO4xgzAwAwVWuNsQOqwj0iITREm3G82l20UgMrjCP/z/72nxmnId5IHOsRnufuGPZIFtlz+OTxYyJiYgQa0pgkMzMiA0CSxMQa/j2pzyHCXzAYbYwYguGQ+FxdXVw9unC1WioBEeJf++//d7/3/e+ig0iOf7TDSg5ga88KLsKq2lQlSatlmWciSpIYJdKc0SFLArPwPRIiDqMg6mFF14+uk6SAvqN8/fa3vj1NUwAt2jR0AhcXF8ISMR+r2xC32kx1DTLuBSACJknRYYJFNYqqykxMTJ3fC4x01mYIcU9bprehrP2/cRSGnVzXG+BpPpY6qwV5G9ARHGup0bK0Ws08XGMDxwX3OIAC6Lq9vXPTnNJaJEL4R1iEC4X1ODg6uDC7rxOb0DGY16WAe60l8QDQXn7xSWaR4B4iDtO41FlbzYmvdpfzaQH3WkrU/TnlUz2aa0qMnuYyuyk6JppyHt1arcpEm/HyuF9KqWDW5rm0eq8VhRKRJdrv7wCd0MchI2BdinBGx7IU6JsXchZ0dPf7+7vuJQMA4KbqYAiQhJvWGDAwpdPpRETEoqYA1loDh1pLHobTfAhskBCJuBULI5LOFNM2jeOyLO+9995nn33mq8nY06fPHGh/f7diMxRjomA4E2IJLijhmY9AJO4eEUatVrUKAG4+5JxzVi2IKQQd82GxGradEXmMq/oFzvQAUxPhZVncjJiJMKdUq9qqttPW3D1JUlMi4gD3AGupsTljmWtTM3fTd95779mLp3/nb/69pRwBkID/xT/90f5hFhYiBEdAEmZESklqrbRi0tGAMZIRemQ6eFfZBodBWKS7mXFrXTaE4LoOPBDx6vLy5vb2vDFubm98pZF1DTexNeXw8lUNelQgWO4WXMVQ23UclZCBw/GIkEstEQNZazjCs7vFFlhblmgckJm+ThuMBtixZ0mDd8MnIlJtbkbCHQJwD9JvRO5hZ6UQAhJx03a+ogUDNgv6ufVxzP/6N/+3whxT4CSylDLkrNp6ar2FoqIBOCGHFx0zJUmRSJQoZ8o5ZzIorXLmpcxuNuYskhBQONdScxqQ8XZ/c5z3Tmaocy2O0NTUKOdU2+zaxry9nJ4eDuWoDwju+v+n7M96bcuy80Ds+8YYc629z7k3bvTZN2RmMimSkkg1lEqS1RVKZaMAFVwPfikU4BcbMPwDCrbhVxv1KAMuGPaTAduAXwy/yHDZJVmWXYKKoEgpSSqpJJOZzCYyIu69cZvT7L3XnGMMP4y5TgSlglF1QDBvnHbvtdacc4xvfM0AsRw0EJftArCPLeAiZUWhRn10/UaEb5fziFHu6WptldVjjOiBHD5MzMyYGhkpcRnb8GHaZO/mKllYpgfkAwucZlZ4qapGMJ3JKFrmCI90U90u2+HqsF22w/F4Pp36GOu6Xl9fP336MfigaZymkiIiNRAXqf5NTSPSPUmO0fdkNFe1maZDZI5IBxVAr0xxci90o/g0D1XZm0/efPHJC2SqtkePHp3Pp8tlU7VMr7Qbs/bWW28+ffoUiAQCxYnKUnWWx/Ric0DlSF10Gxc1FZ/o8bK2L37hiz/60Y97HxT53Pufy8yPPv6oijKQ3TdkmrUybo/pJ+SFn2VgWdbD8erm5gbpk2P0mVFQAZv1siKiUlgvfas1XFOxKkQf/vPTNo0MIBBToxIzMqnm2MWVL3lgAWCBJNHaMtw/nX5FPAhfyT/BbaymtB6Vw+HgkaWkqS8d1vVwOL58/UlROzMTNX3NrDl28RfdPdPLGr9i5khGxtS5Yvb21Z15pAky3Ms0LXyY8J233nr18lVmsNa7yLZFZhwPB+RUcjyIS1rZdfROUVPx3n3b1mVBUQI8hVyXhZRtO8ukRI1EKlTFVuHwzMyRyMzL+eSL//W//teen178/nf/5d3d694vsrTW9PZ8s9pqpok5K0QSIwUS9biLZvEgQfehpp6jqQoplPS4OlyBPPeTUykQSmtLlQaZXnEIEVmuc+4DiAgXKVNcEGK2UrKPPnr5A/Y+XIDz3Umbne7va3S+XS7bdv/o8Xq8OnzyyScEkQwfdZAisgREJZjwUc6OEBFTcw+y0COB4gHuI6rSEm1NRPaJC0o4XJPJkjq98/Y7N69eT3GxR7VziGo77Xi8johnT58x0driZWchTHp4SIqIRfnmkUBW4JqpIRCeoomM7TxM1maLifQeY9tISkJ30YyKgMiI+dbA0jNEuIBlL7+dzzo7YJkdGmfxyYSAqurwAGN4SigqGDZbLZ7IYvk9KFQnSKvqPszmMRjF8QBMtc7eGg1ba14sRY8Ego5w2Qc8Rdd4KEEffr98xuAGwPl8LnLZsqx929Z1eXR9fXt7J2Ds+jghczdNiQh+ug0BmZLkPionqCJec11WzEeRRodU0V8of0aa2suXL6OMIQOmNvoQspkt1ky0qbWK2Qus1o7LUtj71i8iNBObLos83d+Tk1nu3i+XSxM92nrQ9dF6/Wi9PtjhYOtCOWh7fPWYpCipcXf/+t133v5Tv/gLx+MRyCdvPvnaN79WVK/qbOuDCZLH9dC01WFiqsd1bTVJQi7L8vjR40ePHpcZvIKPDser5bCoHZaVQI5qdZjpw7fhm3sf3re+eUREehRJ2N5848m6rLoTwU3Vx0BUHwb9zDh0Un8Cy2H5d/47f7s8fWJ4hXMW0PWglal3BKBCE8u1takJyEQ5QyJdRWS68kKprNmeByIVwiSTJmZiTP7g+z+oRlrI+7u7ftmUstgiECb7ZfM+mlrT9gvf+gUTNTUmlSqzHZVW7odlLkguulwfrpFSikcAiy1/9Ic/6JfNxybA65evXn7yoqkt1hZr5ZQrZFWz812blf6mkgF772NsIlChcneIiCzi1wNmKyLvv//+G2+8ER5R7XcEZ8OsGVl6j8Jy668I5bCsplpjj91FIjKyjAiVEh79spUTgH76YbJ/NNVFzaZLMOvfIlLznkrWw1xaZVJb0QrjcjqN7VId4qcQceIhaZak6cwrfZjElpJ+VrxSLW6t6rxcLgT4H//a/0Q+Y9Cama01SahokVhVVYR9bOWo4mD1d8f10EQFNNPWag4WapPyVQ6Xy3q4bJvZYmq3NzdCdt9E+e7775637aOnH7//zrun0/2z169ut3tHB7LpwbtYWyPGsujxen36yceBkcTR1td3N8vSIn3R1US9+/X6SJren++SeVgPS2uX0+V0vmjTttqf/nN/+sd//ONnP3tK0MKOh2PP/vruZjksfXizBZp9bKfzfe8XUa0YhfPlUk7W0UclUxwPVzc3t6SYSSA///nPP3/+/P7+9qEwHj5i114JRYWQhOZ2OYeXq9DiGMg0K+p86Jx2oXzJ9z17Mr2KGwA4hWURTkixTB9296Jz1q7/oJJ1dwWaScT0HIpMJoeP1ir2W95888nLly+t6el8psj0FpQE4O6iehnVoHJyCVUq9F3EmYlckAJcwA25FBCSmb33t9566/butvsAP3W1z0yCI6tx8GK51zijRixFlrQZNzwe+AkAKg649w7OcVM9q/qZeSZ2MHZeFlMxub+/V9PSwRafQWh1pqnITIudEyc+evTo5uYmd9LPLoSYrX6hG54pKqpaQc6TmpZTxsRdgzaZZ7WaKter+JtCh8+RKTHPzEDhUgmM8Oo6t755DtXSr2dmmKUs1iIjkVT1McJHUkd4ScUFqSJBQSRVFkoA1qxV3x9hzTKG94uqLnaMiJD44s9/cbvfXj97TQDBS+8iQuaqbVmW6D4u20GXy+ls2g62DB8XDzAzOkUABWK7jIQvYudtOx6Pbz158+7uLvoQFR8d9LWtHl2ClhqRV8vV8bBu502aHA6HCP/ub/2uuzOSIiX2p8hxuSqlCzIvvScTmIVTJWqrCIZLQlsT0kzu7l/XLksyh3/04UeZaLYSKHyiLFhVAeGjq6uaMV7OF4WZSiBUUCWsuzMm3lDxeMsqY8ybnZnu41N6KypQHM2WGMlEejEHCGBti7X2+vamXKlFyyEhkNIdQqnKqUgCTVqmB0JEr9+4fn33quxjmGhaR4X00Un6GHW+FXrj7hPzKWY3uW19XVbAzNZqFRIZ4WrSxzaNf31UyRARpmWTZhHBevm7sGm9Wmd9mRkBJJe2ZknnVAGc7u8TaGYlWzdTVe1b76OraCCToaKRiPQyHN62kR36IC2gJkhMeUD9JdnlpJEZHqf7E4FyT2KhehHW2nvvvffixYvL+cwqexLprhSKeCJ9OrBlpEzM3KacMTwzTU1UyodBRGI4ICqqwhFjDF9siUhVzrmriCcioWIPwzmSpiCBplZ7AFQY1fdHnb8+vNlqqr0PJERoKkoxFWaK6bZtPoaZNlseHa/uTqea/1dRDlABVXMG6FRx99HHYVkEGB7ny0VVH11f4ZTaxMc2PIAxYepMoay2SODp02fHw3Hrl/LCPywrAhGeEYflsCwLHHc3d0KOvm0iOTwpapMj6ulLa5kID4j10c3oEafLPRBKicymNrYuomImwjF87MQGZGYwktP4nPro+nHv2/3dDctjFmiq3cfp/s59ZIrui020QKPpV5hS7ZguplWdeh9VpEGKhJ31mkVVMuFlrUaA2vShWu5bv5wvi2lJ7zPi3/u7/+3vfOc7f/wHPyHgOarKNRFA1qYeHOHa9HS+dx9KKmnWIn2M4RX/LLi+vhbK7d0tSBTUuaPH5VPVmtYAaIzwLKCxUCicy3GGKMczUx0TrU0llra89957H3/8sUfPTKHEiDrwk2zNRu9MPHnjjRevX8luASPl1p0pmT68KFAqWl+KBFixhVlpkyZalzEjTM3M+rbVRlItSV3Asgi+9E7S+5imh1WUWivI5+OPPsr99Uu59cu81PXyYgSIsmhQ2VWmdbDl7GybtVKlLctaEuLCBZUSM920epjyy6uQyGk0LQCS9ujRo7JFFZXPf+7zH3/88dYvo/dmdnW8MrWbm9u+9cmYhYRHYe6ZqWKOmtPJuqxLW8J9bU2X493TO0Qel2NoCDQTydXRkQjO63+5XMpmro/OJo8eXZ+387n3McZieViPY7gAos1Uet9Khbmua03Ml7bEcKo8vrouJ/JpGzPGYkbk1dXVTiQca2kDAAiXwzq8ewy6ZJQEnJFhImoq8TCRzaKJYHedW5dW857L5ULJ0XvtpqN3mQF4qPdWVRweXGd3CL1qmzJNB/K9d9559uzZ5XKeTcvsimwM39kwIOm7RUDRlUStPHWWZfHu3MmrBL/zW9+5vb0VqThGAjBrVfV5BEBTE+jrlzc1yM3EGF1UlBIIJUWlX7bauMee2piZPoHu0N10pk4NZcvMiCgbpOqp1nU9n0/lpfDkyZO+9TF6JscYr1+/prBpQ5l7uKvIiLi6Pj5+9PhnH3ywmN3c3s5Sea+ZHz16dHd/X/70ld5W53Mfo+VMWzRrlc1TKztzprVOA1HCP8PIL2Pk2BmFmanUcBdVEtNJMD9tJquQmBWve2RisrL/9cJ4x7FcVUcVwPvdjf0+1gkUMZN7SIS7CDOSqhEl2ctChhNpY2ykmJiI9G0Tcm1rAY/b+eLS16W1tlR0kkqq6Bh9aYtKyRWxtCV0rMtStLvDYZVlEWsmgsh+uRzWw+j9dD7FcIqED9KIOaYXYVvaFv10dw+Zkh8izKi69EsHmZFmzbRtvtUcPCMvp7OpCfJyum+6sLTcmU0tMMw03JHpkYs1oajJ5XzxSOr0bgtUcAYpGlnQsUAQnmYakbTCMHTbhoqoMCLPp3Nmqlr6YDFLK1QoEcPrtq1Ly3Jkrsd3kmSRHtZ0pBR8/uzZ013yO92o+hhIIVj7S90nE42IQj5ZVpYJFbk6Hk9+LyUucU/kj77/I1FZl5XU3juJ8DH53MQkYgxPQCEUVmSre5AwmR3sPNMqQS+z7M6t8qbMPEImd6pCLkDVKJIRZXgp0VkTSCHH1gmYmkcCPN3f1yUqn4LZSYr03l+/enV1dTW2DZlNFTW6pCTy7ub20ykIRVRG70tbCFhrSNyNu8Ki60RFxAMoNLaeU8EzXUx19yvMrIxWesxRtgA747eIXLsCoQj6++SzgsxRyQX74q9YIIJ9XNQ0Ix5dXQ33+/v7Xb6fgZS587vscjmgmlWyJN0JEyl3qUmBHt4nAwt6d3NbbtHLYrX9qyjAR8crBS+XS9E7ovYUkbFdbF0fXV3dvn6dHuV6sSzren1ty3p/f7cc18dvvnG6u2u2QuGvx/l0FmFr7frqSoj786mpeb/EGKaajEVb5CBwOZ8Ph6OaZgCUd9595/b2VjZe+jkiBby6uioZLzJNJTxKk63C7IlAUfEyQ4jRL6PD3Zf1kGTPqLplXVrvnaVITAixZ8mgmW2RQl3aotC2tGLqqtJ0MW2Vabe0ZXif+DqFRB999FFIrBTzZF+k5QOwLuvwHuEkRHRqcsiIWJfF3SPxsMcXvlIWDbI/TyZK4O7mTufLFk8XkbLGLIHEYV1Pp9N+GkiGy6yAxCNMNDIJopBbZmSuzcqWqYKMI8XUhEESwkJNFMWDhYqa6IheaHNZuhKWmZf7U9HZi/xcj6BoujuqDaASMKFnMScynSP2BDVM7+ka6uTOIpB9eulbLzz/svF0Pledsru9Uajde+2SOQ1Zoaqs2Wk5mIDujv2oXJZl2+k3BWVlhoqKcEQWp4plFoHcz8bJE5wctKpMMtvSwKwMwYy4Oh7LIE72hGWITD1P+VlRAAx3xuROv/3WW69fvwJ95hEkrLpiFQrTxyacXJC1LX10IcPzxYsXJM20DHiaWVMdmwuZw1+/eKFUbRKRx6ujNht984zWVJWX7VQWcc30uC4qAGVZl3Wx4/Gt8XT0cR6Xy+NHV69PN8OdkUW5Ht3HNkCaSorc39z03uvJKEvUaUNl2i9bej66fjTCR4zMfOvxm3en0/lyzszWTKxxmivn0uyyba0gnRLjqpdwnyymW65tLbD0+nC19fEgzjaRkWjNVDUiUfUSEqoRJZ0fQn7ui1/+2YcfPECOtcWGu1Gmi4ZAk5lc17UO/NxH/7UrVztb80YmfIxdsha2W85nMa3IJEpUHTmN0caYWOLa2rKu2+UCMkXKVi88lEpKCpZ16f0yRknvEO4QhHsGD8e1ZSvq3FQdU4YPJZEzg6i1ptCS0e9ThwTZzOolPuAfBfNaWwpBLbN9E2ViWfV8udQDy0yWCXh41qQX2LeMzL3+qib57vZuRFBEy7RBLSNUrXxbzSw9SI4xymUGf1K1W2dTmSc9+JWp6nvvvXdzc1MNdrgL6R5mOnKQKVLy8ZLp1a4BEZaotTCLuj4U9t59jOI8FRxdavU5VpxLe/JMEymQx48f393dlcV57YaZacvS0p0AKSaoJruCqJhF72yfe+ed17c3uZdVHmOxtqwLEjGi3DcFaOu6LOvV9fXl/l7CzRZkhGcTgQet4XAQEY9IxLI0EX10de0R21hH76s1DSro4aS0Vpfa+rYR2fsGVBtp2oiED2dma2bW1rZEYlkXCbm5OR+W49rWskIVzcxBYF2uhDzd36+HgwRVuI0Rma0tIijK0f6oBJMCrMtBsPXe1SwRI1P4qSZepJUgEAEixvSey5vXrwv3KxOteoIBICp/QdxHZi6t+W4vACB2L1yV4uRLhHM39TLVyJFlX2B7B6gKSCl4ErkH7woQOoMRcoytsh+atRhpYpEOgKLd+/l8FiarAG6sDtbUaFP+wmktXbU3zYyTsZv73hIR01rtYZJRm0wNVx9mwvmZhlC1+SiH6Gr+eby6+tpXv/oH3/sehT46J7Jlu13eKiKleoucnuNStowssUslZKukfPXLX709v37xyQtRDfdye7XW+ui1CarqZ52H6zVXedx7f/z4sao+fz7c3Vqb0zWRxubhY4x1PbTWzuezKJAUKSLKbDvrVz1+dH17fzfcg1RVlUlsKPQITJ1pWvMiXx2vztu5ArJG7yzv/60j0ZZmmUPIpo0UzJwCT2qVQnWnTaWp3p8uSC6yNLMIjxiNbV0Pj64fDR+997eePDleX3387NmhrTVugaCpmsrWT330R9fX98yIsLUlsi12fX3VzDLj9f0NVC6zh9HhfWmLNdnBRVez6v5ULNwjUwQEt7ExMYKq4j3cxyKa7kbK0jjCY/Qxmup522qKTcC9Dx9MAG7SSCzr0T3Ol7OpjTFM1ayta0sfPmgUB1iReigmIlThPorIRlKI2tT75WzUlDws6/DRq8MhK310eC/pQkY1RUAEKU0MntXCjUwyS5K6tKXgBAQ4J/4sTFVEwpOJfW3PrqnE1of1QOJ0PtVkoPfLog2S6WV5CUbx2jFyOqQVAKYm4RkxbCL/LLrv3t0iMx+/8YaZvXr5srWl906kUhKsB71KbnBMRKRM3yM8ohNf+7mfe/ny5bOnz9bWMjN9LGUiMrpNu22a1bJHa2tEBQiULNdr79u2LdNVkJkTqyOAEuXw2NYbas1CiiRcbaGJsJjNbcYxq+yZxzUrJn/w/e9TZFmW8CgEqtJDK11OWxOw1EUo9gwBZCl/pxWLqFgjxFQiA0lkTku0iBp8IMqoDZVUeL7cReYWfnl5LiouKQksqz16dG0qkJTy8rxsW2UzeIxJHzFz95cvnkcEEWs7rLJ23yBOwhRCeB9qwmW5XM5j9EYeD2tGesRxPZRGXptu28VUmunlMp48euQAhGpy9/GrcTkr6WMULcPDt3EGbYwLACqFOudc4cbScAwIqRYjmhYFsC+2qJNq62LnyyUZY/REmCoqr0MVObZ+Ln4ppohtgMwUE33z8ZOIOG9bMiP9/v6WyWVZBPTwWWhn7BPtBCAAVGvatphuW6hSaUJezmd3b2pUiYhKYjOt3g8krS2YMCYD01ZFqJ97/63nz5/XHRXWSRUJKyeFfTKEsvNUk7KxRyZpsocUN22UPCdIiGqMkTE8nNPwMqquJlh9MlU8473334sxTnenTLgPBZIoy5iyvSntQkUqJ2DU68fH0/nsEcNTm4bM57JkKMmMGEKUSWRHfPTs4/Boiwm5iiUQEf1y/qM/+IPWmjYjlZSy7FqsbVtG9/utT++Q4ug0cR/l3ra0Q6bvbl/505/8KImlte7+WXuN6iF99DJPKzysbs0sU8t+pRxYx6hhOCa/YtLoxxjIHL2rqofvunnUP+p8dvfXr29INZU5eqlLl6XUZ3Uli7VtbJkuIkmgcoAyIeIeCrSm29hevXppQtaU9nI5r8uyjUxgMQufAPHS2nE9uLvHlqNHguLLUa7feHS+uchIjyHZTJg+aALm6Ftri3pul5Op3t7fZkSG/+zDDw7r+uTJE1H2bYvA+f7+dH+XPkwp1hokE/eji8gYXbSVnKUtzT2s6bZtyCBSJjPLW5s5hCD62A7rYet5Op2l7dZsQJmLN7N1XZ5/UuarWco+YC6OGJuIHa+uxhjYLqr6xS9+4eMPPx7ubzy6Op0u4W4qMdVYFJbTZ43omYhmxoSSmVLFZ415zGwPR6l6L64Px/P5XPdbRauEKUfSmrS8+uRFuitVVcN99CFTfCwR4YnMnF6u05eohD6ODBWr1X66v1XVtS2ZkenUcrESFVWzvnUSZoaHRIwIEd68fF0a0fDQneokVbCV1RAgKrc3rzPRTCP9VOlMIkLs4c6zx6QIMsockJkU0uPdt9+6urr6/vf+YNplciZxFKs+Inz0sl6NTPctYpjZAn30+NHNzQ3IZrr1JEDjcD9fTiKSMp3sy3LV3ZUFfEFUwZm8VAqKhwZ1bDOfMnO3idnNtau0qIq0upL333//k08+iYjCEQGoWVE38JkPE0ZkRPekiGpTj3D3yGkHWbJhCkiUh0PN/KJSpDJNxdNFhU4RmoRYU0RoUxKLGUihOgdKv0eaytguTVSbZWAwlsPyV/+9v/Xb//A3bj86LdTWrHhqGbGY9m1rpsfDcrmc+7ZljNHH9RvX/rqXomdty/l86j7Gdnnrjcen7ZLKEHQfW98i2tJa3/o0+5qZ9B49mrBUy82WSBeKEO5RUs0yMQCyNYMCkONxdXcR8+E+LrfbaTGFSO2CVU0VG1dNM/P+7k5EWmsUmtq6rvfj/nQ+zWkncl1t2zbdzU2aLayUcWkFFagIKVXeLWYUSUxBjIqYqHu+8fhxM3v58mVTYyIz1BqAQg6atdPmEGVhHpSUkH10QCFSOHPsBZEE1ezq6mrbtow8X85mFqi00VlqAmXLGtOyh0yhWistDMnDuvYx6oQXVaFdHZdt2ypdsl5/2+m1ZU6LKddOIIWWYDKVspjGdHSoOyJa5DYPVWlL206nfrmIsAKmSg1oojXZV85sdwIqM2E5w0Xk5vXranr7NkpLSJWHYfK6rGPaIGdEVOZIMQoK5i0AJyIySzI94aKHMWYlMhXahALadtuHYvx98MEHpQ1+IJbUVLx60YdPFmxRe6WquPvS2sbM0fMzCa7btkUGKj0PO36ZSbIt1k+nRKgpMvm//hv/C+5sRoGoaOXDiSqB8CB5WBdSxhaLrqOPtJSjtMPiPZZhqy5iVv1uZFqz7dIz8p133rq/vyup97K0r//KL/zkD3+AHqoKMiLefPutu5vb88393fl+672Hn8elu99vJzReLhtrfljbNB9S6DJRJ8k0Go4dbS9BCRKHZR3hm28lKymddJk1e2RTK0YRMBv4h7S/Eg+JCgWjDyQpatIwXTPcdOIZReITUTXpvT9+/Ma2Xe7u7h7CC2qUJaqJOXmraaqQw/vDHS0P8XiwLCy2HfyhySkIpCrniFCzbbtwBgRvpi0DIMpSSCrGpiRp4a21cI9EBq6urnyM4YPM4R4ec7IfNNNtXFQ1y2MphrX10fX17e2NiG7bpaQnQsx906dvW403InM9HBK4uz1ZU04B4ChWeWGYBWAGUlv78le+/JMPfnq+P1XETik5x04JKEy1angRcd+vzOTrRkSozXC3kRHzJMx1Wbe+cfYkXmBVzq/O2WbslmtVxEYEaqqksvU+yQ/+adwWJDgpR1NQUSvz+vo6M0+nU5LFUYuMitAlpRKBxuhZFV/9KqI6nclkBghGOhhJJoqUU2QG7Jh2JBFj8H/7t/6TUlYRZFK15YhgVJ1WE+rPf/4Lx8PhRz/6gKFNbNBDhqg2WVoqwWVpYtaW5bNbrwoPyzJ8UEDl7d398XhEpABtWagaGaMPbL6Nfne6g8rd5fzq7haC83Y/RqgZhQkOj2VdYkzLKVU1UyC3rUd6LTARGaMea1ltuWyXbVygJKGiAu05EhCWIzNiBCmRo0gegJSULDyWtQ33iBRqRLa2uE8/h1pZ8wYnAlBTd89JDdtZjTtpu2w7z5dLMyPo4VnpFDGzSZa2Pnny5Nmzpw9N1xij+zadA0QmfisiFZyhsvVeo1n3wM6eAVjHdQEB+4xRd+BR3MeyLJk+vTZFgBTV+hL4qTjLvUcmkKqWJQ8YTk5r56oAy2KPJD2njFPEPdWs906VzCj/D3dHxQZU4ifh4aDERKTyM0fTLOkjsgSDBac/YKeUGSHFInfUK6lHQvQhoJU7GWCEt9aS7NsGIgEzOxwOd3d3JdWiSI1tVG2MvixLlL8cy/XbMx2FOezzpPpY15Xk6XTKP/ml+uvDRwnuy1yyzK4yQwRFtvMxUCUJKSbuo0zMSoOQqNFOdB8kEGFalqSojtwYbOuhx0Zh07Xy7Z5+/DEyaUrCbFlULx45XOhi2tbWe1+Eh3W5u78vMTGgMUb48L6JSQYOhyUz1mVZ6uA1u/Tt9nSvycv5HB7n8+Xucl+z4IM2LuIBa40ql22rwMXF2m5Knl72eeUuGwlQy57TMyUoNKs7B2tKyOO33hHT5x8/LSBnaY1gNWX1lLe2nM8XmApwXBaPFOronu6rLSYa4XtlUo+4jPDwoSI+nzHJiPBYl/X60fXz588LWViXJat2giQ/HdaTROTL5y9MtIhNHh1IK9MdjwI5ZiGU2ZZluDc1ERnhU58VEGUpr7Zta2aU4rjRR2pToUTAVCbnnGXzV5ackJJMWJ3nmR5zjLjPUbbL9iDTEVY62FjM6tBalhaeQyIj3n7zye3dfah6ukgZEkCkZtVaSwsJU1EzStt6r4p70u4iq/9VlUhXo0glU0xxrUd87atfvb29/eST5xUUULVbudLmeBjMQpv1PmR3FaoD8C/8+q8/ffrxq1evqhydTA/MrFml9MtWcKCKLIdDZp4vJxTFdIyHtjMzKyi5xoo6Y9qkkKrMlEJ9ybfefqukNgjPDJ+RX5HIpTU7Hu7u78M9gbIU3MlRiBzVJ1cra6ws53JHj0SGhxdVKn2GeRzW1cdIYD20prJdzk0o2uhZvUVrNkZ/+eKT8toLxrLa+XJ2gh70MY5yvLr2+96aXj+6vrm79XEW5fHYEDkGtu6EHw/r2XtA4EkVKsboErKabdmrp6p2yFRlV8uSCK3K1asrGdkjHdMcBhkeMcb9ZVlXOppoRKS7qlbSWbF8AaSZZ0Y4BWtrmWhm9/enKt7cM5HNmlnLNHcX4YhBziholphbJLbLy+dnRGizrW8JJLLp5EUmWG8hIlRYdzXCTSSoJPvoalYlyQNFW3VabHmGEJHZRJKsTNtyYzHVpgZJ94xks9KLa04DnmJBFCHRMoMzgJyRTiLDtRl2IkEixxhSzmNARMFUTVDrPJdlWY/H25sbE3Xg1c3rWmMGi4jy+EyKahvureIV3BOY4p4yYU/PRJOZeRczFyeFMtyLIxu7V+gnLz6pJBgPp4iRgSxF2CxhapYDaWbuPryXkmdZ2ve+991t27ZtK45NYT6F3OZDAT81QOHbpYJVt75VsTb6EDDJw3G9XLYqSZA5Rq8Dc5lD4xTVDAz3+9OpjL+plJQpREtUJS8izbSPoWQlKlUfAYCQcv+pM8kyXWlX19en04nY6WKAkO24XraLiiTcmkaBNHGhZBlYcXp6lOtxkUY3bU0c43Ix024wyuK4P58O14fDohHjsl2ozMzHjx+b4v72dHV9bM3Wq+PN6fSz50+BJNQjhKwTgMjjYaXw/v5eQFMB8rAu27aV0D0jClXx4RQBQxQe2VpDsV5ULq9vu54adbgvzXxExV8f1kPTNkOgs/WMcEQ4nKZtOax99DE2s2OFixeRaFnXsktWzsCl4nNVTl3R/UUqTmJmhCO8rDr7Fm1Zet9sHmthZtBJHIuIw3qoZb+0pjKTDMr/paj8kbmsh5It+hRe2OFwiIjtsgWmIV4FMRIoOXZUdKVIRZ+oWB02h8M6Rl8Py89/8xu/9zu/m9PwiipSJX0zQYZKE5Hr6+v7+3t312brsnj3rONRddu2FNa9IMtpEShbj912ton6DmhNvCgJmSxZVQt6Ma5UZWltGyMr+pkQkfP5NA/bIusJWfFzD7wCkqrNzCPWdfnK177y3e9+l8pwv7+7AapUYlV8DEQyIsYYy7oKWTYrNd6sMrU6rNqXqpK6nC/c9V544DOm793BIEgRRvS+ASlE7Ed6wVpFRdq2Tk7uR2tLZlZ3E1mVU1b9EZG2LC0izueTqhSRdV3ssl2mq0juPPKIZkZAa7KuMm3jkBGjvJTDQ02Hb7Glei5vPNpaYtE4xzuHJ8ujR/cvbyTycn9Hsh0XMNero3u8fvEqMj559syJQ2vn7QKWhcjkwI7R6SKqV4dDzPYDMbpyAndVG0z9HUu7vEuudgeTtpi11n1AZF2Xrl1q/g+4b8AsyUwgukRMVzzvXSgjLkJM4xxkJvrlUs1heByvroYPmI6yhyk+N9IjIlPAyFQzhIP00ZupVJQzEODEXGWmgHEuRu4NGIrByzl3CWtVf8KjP6AjpPa+1VvICOO8empa7BwB1VogM2NprVwmrLVtbMwQIEZ88uxZIYnH4+F8PmfE0pYaSx7W1YGrq+PrV6/LmV4Ud6e7vSZHpptJRFCliAQkd4U367EWM1avHIHd6ScJZK5tqf6z5Cd1xEWmck/EKwKzThIlpQKrcvKlKVkXDRzho4/IAOLVy5dmRqUdlvMek4mdO1UrUOaOtmtfdrtqhGcEhBkxYiJ/7i5SQwfWtzyEz+19QQHUqSJvPnn86vVrUsI7977TPw3y8Ac2L3a7s3qYx27XBJFlWSzhQBa20SqJOYZV5NPoy5QmVFEcrItImKo0qfUQntY+dZoYHlm1WYz1En1saIajXr/16OblJyZLXjpILDKiU+R4PL568dJjiIq7G/H4eIzM7qOPEUmKqOgYnp4pXFrbLmehBJmRkihjfw/JzGW/UuADS1ubyOgDlPLgOS7rl7705Q8//BlBofZtK08wVUlouEdUGGYlhUYTaccrE4b7YhpebJ+s41eEknm9Hk7nk5Tpm2rNJ0wIkeGuoAjX5dh7jyb1jFTckFEyS6Mok/O5R9xXVbMXOJqRJIRWnOFIV0qSw6O1lhOwFCCbCfYcQdQzSQgTmUbOirNKFB+agLswhfnxzz5spgSjj6ZTsya659YAMfzq6urdd9/58MMPRy+F4ENaGXcwG4e2TO3YPtedlgUA9rqjGisSSgVyp/1TRHvvZWRbWwzqnKy6A4jwWvTJCn0rIy6oGgzbti1m83sSH3/0Uc3YFNRiJ6kWRm2iiRy+i9A9M1Hyo4STbE0LrayoYuBT7KoWlpmV60KVBsty3LZLCVsK2T5fLnOsqoJa8DXUmVzlOmIdEGRuWwegyj58DG+LFso7xuD/+3/4f3r29OnYRl2P6noTqdYI9jGAkn14DLdm1Si31pZ1FZGxbUot2krJ3x8/efLsk0/WoA4fB7mMbRVtjw44LC0lL8MvQ0WhvH7nSSJvnr3qvfsoyD0901rbRr+9vysfMwjX4+H+7ja8rEoY7plRc/wioJWu1iNQUSt71lyNAsyMkQkxsxKFgzheXb148WIxK8ZsDcSqaqhzRkRLNpGJ1tqUfe2/tvfeWlPTvnURiQxTa0s7nU4+vLVWGbWHw+H1zQ3mmqitNPa7vJvokhHFf4qM1AdnrTnNiaL+FNqkMgMLZkFBvvX228+ePatChkA5M+RuQJgJoXp4cUattUCGe9mPZqXRFRSnUnT2w+EQfVy2rQpI7LOEqWGlPH786PXrmwJpGbMgqYlP0Qlk15qMctafWs2y6fHq0qeOBDX2iEzIPvwsBXyJP3f/tIp+muPQhyNRVdV027YxvCSd1fMWCp1IR5D0qBh7iMjWp0dkRqIkGzmnqLkLSoEUE4B9DHdHDVcji5nAWf7w3XffffrJ061fatQSDxmWe8BUlHZ0jv2rKkpgFrVmRsFlu2Tmcli3bXP3ZVnWdf3a13/+d3/vdyLDvSdgp9c3i2qwI6N0dyVINwGFGWyttWXZzlsQyFgO6xgjY4wtk5RESpZcjCIUHo7t0dJ47l0Dbx6/+s1vffgvvtdGrlBv9EG7Wk/39yrml/P96XQ+nUbvotaatdbuTqe1aWY0kWbL1jdb2/uff/8Hf/haaPCgyGGxMUaGN9VALEvrPgLgyATULEo18NCLl+hHtDzOl9YocvPyZVNR4SSb1Di0GUII+DS2KaaIZDhFiCpXmOlXx8PxeLy7uytztiledBdArBi44qPf3/u6tBo7Z0BVB8acek+smA/9zLq0bduWZkV6tVYAlQEg55zJ3dfVImYquQB3L19ppmlJlKVCrDFpQxAVFd22gEgSWdCgipkt63p7e6uUZE4YJoKZ2+lkYsd1LZfmMUapqxwTbdrOZ0UuD3q6GqcIVW24qxmR1Z6tTUsscnV1/fr1K6QLs1DlrNM+RVQi6FmiWwWrg43pFZhJJMIJGJVCHyMT01E6sk7Yw7JcXz96+fIFyPBC+C1iCFXULtslI9qykERsxXtFbS5FxRWkZ9ntVkm49Y6pLJv7aZUbk5OQGT6ePv1oxKiSJ0vXmpO6WRuRqc4SYleNc05uADJ8pM9h7+V8LuFrtS3hvpidtouquQ873d2pyqJF2Eww1FCzV7gvrQGQxNXV8XI+RfnWiSzrQvDSe7gjOSvlGCr28Ucf0hqktMK5HEwMMfxyvg8BqevxUCLDcbnE+XJoFqZUXZbD6XwiMUZXynFd7y8nEcToP/7h95tperk2JsF1aRHu7lZkHxAZqlL7FVHZoZmZBBe1pbUYWZFN9JDEqsbKyU0vNBLY3RZ9NNUUgTASPupEzTL+4LTYjcv5rCKm6nsKYNbym4rEXNri7iY6Rz1aLE3Cpr+BVP5OItOXpeUcuLugWFXMPVlERENSKDApFVsZ2WVEhOvuco+AmflllNHeVH4UggVqeXPHZJ8/fvTofH8fewDkYpIZI2rhSeUpmupQ8+EUNs64MSVsWYYPVYWWW00qRE1Z2aiJpbUkS+dY29D18ep8PlPqrZU+psGDFKhuMfLT5zgmhDlNsWf0Q0QyURqASoig5LhsCiLyfH+vFFVNtfN2UZW2rn2MRJDxzrvvCfXp06f13ln0j5yAkKoJIiKUUklwBCJRbBMTCVJnTgcfestq+3P/qEOOwIT0pCCkYhfm0sop/lPxHck+vNpgj9g3BTmdzr/7u78rnPIBEeH/47/3n1761oqHG86KdJvQsxfnRFRba+fTWW0aQBQUXn+ytWVuJ6SoNGudiO7S+2CGJCObtWVdVJmUgjGPy+F0dx8xDsfDGB6JTJ7P5/N5A+GOKgY8PdIne7Zn7Q5963XHhNOGXET6GGbWKw07uBwOw8foA0gTVTWJNGtMBMIBKEOIHuFjy3H96Dozt/MmSWExHDKzSrgqPKuPmvr6dV3OlzPndIOzBqmGSTjCfXjR0Dip7dP01qxFZh9dKJMqEFFhgSC+8IUvfPz04xxR1ud9jDJ8EEoZLh+Ph8v5UqGMs/wrjWKNFkkQ7rOQrtqvvHDHHN9rFZOYdoFShR+JPU00haJiGVkZcMAkPFfBVnEm1Q0uy7KdNxEpkeAcYwDuQ+u82pmxQJq1MTyzLPYiIyckS0BljKHNtt4zMsJbs+JUdR/lwFzk26k0ICbVYceB9pXDmI14rof1c5///A//+IfDR8Xz+nCSvXfI1O6Ee5KVPeHho9foMcQ0I7sPqgx3rTDLutZzdY3qPmpFdB8PiFQSTHGPcv+KcBFmxvF4dVfY+G6kOPsIZGYOH7nDV5kxnZsjwEykeYzWxFrtLSEiCokMMpdVx3AzmtkYfVlrmlRPcVBwOBzHcPeAiJlKhWGPrYk4siPgic0pJZfaYBLA47fe6dt2urtFwhbjgrbY+bSNy+j9gkz3DCnsJJqKe7alAXB6xsikCqi6bb2t5t49ojVrzcYYTVgMo4oekZ1ZdliXHKFV13Xn0t7+/Ht6WJ/+8Cflh/TW22+cL5fT/Z1pJSlWyGKMMZRsql5mNpONgN43Fapa5XC1tjyM9aSE9Q1CtqVFxETKnfPWZmpb6pSDpFnrYxA8XB3vbl+bkGYqMoYfmkWkmWZmiYC3y0kAM/XpOld+oUGBI2tfGPCqKqcPQOT11eHurpbc7rVFmmlkipWqK0wFM8VQkvO3ZoY18cJPs0YdILNUQIpcm5XIVqCiWr5DMPFwCorKFx4g0vtq1ocTYSRNSB3ukYH0tWkNAOtwIQCBB0zswU+sTGAAgEyZDXNOBiYLL5W9dkiPH/7gBwAMgpRwLxJR07KqdtOyLYUIPUZmljEW6jN1QUiokFjakhm9jwLJhayXagARMypjsmsyYnbmtZfVCXy6v+e0YvEZR1DQHmbSj4rk9EOaRMhaUEgYEJKgp4ebUIWBjDFieGvNWlFSRqmKpY4KEdFSmnuValWnkFlhvyBVOUYmgwpTKdAZPbTpq+fPMoGI4+HAHcWWnffnvlnTZPY+mmqhR4jIDBOrpqsO7sVmNGCzhqi8akl3qfxcr1maC+kR18fD+f5cIJ6qbmM8/9lHUM1tmFLJpx8+zcyr9SBZjukQqaFTSY0hKe5Tc+s+6gkBcz0sEQHGeljG8DrfxFREh/thWSL8cjlHhmpDYUWeQi7rQnI7n5hYW9sS2T2QTQ0MEm2RrGl3URfUKpr6dH+vqhpSG/dwp5Rn9FKHXltaRFoZcCQAHX1bl2UePhGF2te5kUihUqwapxAHkCrRQ7WpSEQKvLA0aRVwK6OPWn4EhJmgKEYMK0Q3QCabFZ4UnKNCAMd1He46mToWGc0WH8WajGaqwSqHRDXDk4woJXCasPwiKgUzkR65U4VRcyBk3aNcDy1yjDECiXQVPlShAEcAyHVZqwKqYieQBNQkECL8/Be/eHd39+rlS88s4X5BxzKVSBn755USmIx8JESLvYLSEGOOkWZpU11u7JHHqqpNt60YgkgWnAbsR2vZ3+QCrodly54jlNmJZSn7FK1IXlU8VORiOnyo1awvdbLJEiDoQFjTtrTehzqkcv6Gl5+r+9DU9F6moTm8HUymGbRHeORYlhYZHtGMMuV5rADvqjiqLcnMw+G4bReleEZRBlGq3wgoagpWJdXjw/XtzSuhVfpQ2c8Pd7hDRYQCrGa9DwaQPo0RykZdVURRXFUp6h5CVARjDKQjSq3mRGpRoSgxuqkJ4nK6NbO1CD3SIrP3vrRKzfJKGTOz0ftSm1S5HI0+JvEwlmaZSUiHP3p0fPz2Wz/5wz+qxLKilctEOBKZi+mcr9cQtUiJnGNhpQwf1iw8CiVuWuCNq2p5rG2bf/Pb3/7g4w9vX75qRR4mSYvI46ENH0Ay8tia+wghKO5eXgQlABIVBMVRlW2R5iISykQeDofT6YRMs0teIQAASBVJREFUqiKjCZEBAruBkBDWdDhNLUQSGCX+pETG1ruSKsXaCyVqgU0oLnNZ2xhjeNzdTg5DTa1JLMsc3oJoMiuIIvBPuK/0VaUi9vjql7/07Pnz169e6SxmozUrNXlt4BTZtnPNgSSnR3YdK/X9NQz28Oq2i/5XXEs+/CM9Epy5nUGUJ1IZYHvCKeD/6z/6X62euXAcDedtGTgZjWxqY3i5/NWEPTPKcgpFMa+GaAQxDTUeDLVEdRTWUj6ORXWJCc8nWbEXfRsjRjs0ksNj9HE6X6ZA0bR23947gDFcWFbaKHleliVXuPsQ7ujCDJZPAMNHoUGJ2dY3XerSlHan5J1C2fpWR18CRBbSO9xV6jVkRlprEZEoLqugvPlmKwIzzYR7LG2hcLFWFBnu+uA6PXJ38cl9quM+UZCSZVTKk6lt47K3uzW5IUlH7Dd4H8HXFDKimsCJYAGZsa5LjRywN0yk1G42ccuEmo4+9qxuVNWQEYfr43lcog9JMqVclxJzImNWY4bITHfPqeFDVrs7U5VcRUf4Q4833FVl9F5Bb7nPb0B6jOE+36+KRwz3vfarX1kNXpYC3iOmfBellsyrq6utb+fLxSNbsyJLFAVv+Nir4lzWNTMvfas+eQznLpR5uE4lUkuZsxxUcEHs/PhKW0XW71fVIv1j3miWqT+DMUKbBnP6EO1JTSXQL4h7dvrFWKsy4FOdwKdWO6XHTEPeaYyjSeQaaCbMjByiUMmIcTgcM3NkLE0imZm9/O/2NJExhjVO+yzTRM6YCkXmUGMmfIzyMaIqWKSbQLj3zEwRbU37oCOlVPAZMbP0UgSq7L03axT2vq3LYYxR8R31XvNB/hvh7se17aBPqECVGR0i1jQRkrkuGsMpEFtIqSorMorPvzQtbguJtmiEq4CUdV23vpnp/f22LkvvfY9TEfeox/VyOU2WMWZhs65GSngmUoUgy6R3XVpxnArYvL46RAQyl2Y77JSl9wdSTSI+BUuqKhKTimHNzGJu1uJ/88mj27vb0+lcJB53Z6XBqNXel5mH9dEF4+r6WH5C7j3T27JczrckWoEeY1ubZOXYzzyXUdMtUcI0WRC3dB9StHqVcKekgWNArDajFKQ1Cx+LKo1zVHNo50sIwqwNL+uDhIBVoBa6Nt9fCFVMLFgK4WJuMeGjM3NRHXRUCWpKIQSGuiAeidE3kgoMdzGdPm/VpUvhFyWdzTJZNdMEPEYpXROg1HuhqOwHJmTGz7MIx5MmZogYifQsiyybpmSKzJQZizMPl5zFbvH0E0ipkc6u8rEyrrh698k3//2/9eE/+ee3v/vDTDdVAdWsZj9jbACtMeEApKmNarLLDAeMHD6K7SBS0qSQPcRASfdYrtYxfGATKKliQs+m6t2XZTXTm9s7sxpSXyqXsvgVCehi6bk2A1MStjbA10UmAU+YU9syfdy8RAlCirLZQ5lhJmK89G0HYyOZKhoeQmjTSEZ20VYgmWopTiHC2n2G94gB0ExnohfKYTlUEBmtLXU6stCmEhtZzUWjWSlIsSzm7stiNU8S0YrMMyWToNUhUmXkUugRotCdAjxjGlsiwZiCm4L4qZI3N68ycXVcS0u5bcmZSgyz8gPIiO3quAizGcbwtlqEAFEEYGQyocclMpK5yjK2wQecpM7jLJct6X2YtRoburu2KYWvCmVpLSLDKw1Z63tMy4b3QoSSa1NBbn1TQptmWdWQXuNfk9Zs64NIbVotQGbZvelwT6QIjOI+1qX1MYAU8HB13LYtM5Aou4SqDtOHqLS2nM8bITUu2rbtcDi4+6WfR3jN0XXnZmB2v+ruUSGXqj0c4JwMEaXfIHk+nwurw2xffT859+q52IyUnHYt9Mhy5q3jdJ7O1Qb/l/+DvyfDL4+Xw9fez+c3+uL+ksE9sVynV3ITqT3IKRpIoVUwBtKRJVrv2EnDNXidYniQVV6SlXIjIj5cKNtlU9HwNDP3aM223nsfddH3UiczJseVO6ZXHjq996ISVLlvrTJz2ccoK5NijPpu4lhH63I4lH46kc3qYBkqVuXr4zceD98u50sGtMx+vLgmmghrUyJfHJJaAB5eNUURgursIuAxpgopoopJgDVew/5xfX19OZ1K8VRCPBENr0a7LHkn/h8ZMVkQxSIoml/iwW4nclrmJxK+LFaxSM3a6XzCZwaDe52elYm406c4G6OIme9Wl7tgSAGl9nN8xgKvWC3sfTzwdUvGXOmCVO67VRaILVLWcPUxzfInpxfzskTEiMhpnOsEPSMTnjk8VLUAm08ZUZnuXiMTlj8jpo39cK8SOiO8/HVVLttWgxyqesTUXycSWVKNzEx4RHhGsTiLEVWFeuE9ICrieYT7XvFy1iyk0H13higiFeAZlUkzm7I6TsPJKnfr8qNCfbm/uwIL+M/+x39vitOH27oE04eLaREjbV2IKRTQIn9p1TXCveEuR+B8sF0lKTLCWZEq7l53yJrs1NAcWRofABT1GqMBPrxQe2f5+RcuUqo7qZaq0hnqS70P+FDVlNkbx46eyYQxWQx4JEahm8CyLsCk2phojAoIy/L4q1fIBw5TMgOiok2z+hNwtpcRHm4Pdp6fOWgqy2C2FlKSiCzZXSFS9USO3tcHj8wiMCRqowXnkrAHk17TilGpMzbJImTHfqFYpUdEZIigvMXrBGBhJuVLWWCGFGetvDx94r4qPlyEZf9dl/rh5hJKpPsQpU6QP3vA3SmiquUSGO6lKI2YbFMRCY8++m43g3rN7mWzYonstR0Suw9EVqu/b2isw80jptfBPjL1iD5GWcxg9s+ccEaxEZlIOPLBdaH3HkgII4MRoGxjVF9Q054yQ62GEhOcmwETWXQU1ZKkRZY7O+Z5WxNsZmlCkp8h96PGRbHT1/aTiLl333uGBenFeZ7BojLDthIIJQQC2mHhnmbho/ueujeBB5FmVt7EpIT7fLiJAsFr4KY6DYpIUW0EA6DCVLetQ0vVBYBesQdl6W9kKV9Fdr18ZV37/r6iNclPzWOErRWuWHJ2glZUh3KvzGhNa+gkQEpO+X+GmK5Xx+10EWNmmojI5BVhpv6ICMcYbW0Aa1IVu5wBSFVRkyQPh0OWFa3vEBlLMd8w/abThBW7IjIdVc3sePVobBtIcqe2gwCaavXJbVliDw6SJjUvQSSKjaa6NNvOl9zNbwtKpVjpvHrviFzaElND40S0ph6uS1HJxVRBCw93t6atKUFBWQctdTuE6u6iNTmfAERZRWuItWktKyZGdReljN7NZqpKvbVlaTaDz0PVIlKEMBFRDxe1Hj58qJKcacvFuS54AdSqHVTsQXvdRxfiuK59DJkGtCkii+k2uqk4IqsizUSGMEXgAmTtckWo8KYSVaxNPLgcomjgtN5HqnIMr62hsleABNO9It4RBVTPTnMnQey7SeQsF/aSsCrEePh37MBeMaKlkk0qVRj0ZJLSZLofVZqeNQkPEVrTh2F9AqaSzLZqFB9VqxWeXjJkSCGDUkT3qMTykhcFHCQYYkQW939EtSBl3e+gSiTcoy1aq9HD634Pd6H6jJNCJkS12GQR6dmtfoRMcIyuQlAKG6wTrDwyhAll0q8eX11dH24/eVFAeK1qEmIKFK5ZqXVdmzUy6/2KiMyM4613ivZ+Wg+WmbJM70X3LDpoIOGT/FkxRHX7TEnGGBdKNCvkiYXPklAFPFVJgUIIjjGgUVOQ1jQ9ADgyvZuCU2WKNidkkUTGsDaNcoVJUiAZqUamHI+HMXofGypJ3aBN6zwEoGQEi1wwRrCmIxFUluCmcvsiaMmyC69HbfS+rMaYXtKmtvUyqZmPRzMVrzOsym7JiHrGEKJq7lGBAWSODMFUxTSw+2hqfXilm5lq6TAT3pSBTBWXKHR3XVpmeMlHAWB6tYDRTCPoWap9lia2qa2LnUrdBij3ElxmYioBlVnQu28FNOzYeJZx/JhwcZKV6NXzwZKmDDSEMtN0MLdbBCooGglMFyf7VKknnm7kIGkmM8J0komRQW2KdDMbnmCqlqlkROTlcicyGQmFT4JQY6UkgeFVQZEZXsojkJfLSEAt59YrqU2ePH7j7v4uugNOSVWVKNmZE6CiNCJkWNVtwoih2mZxWJMbxUwiSERGsXMy44033ri7u4+o3ctnPy+TfXP3+oVSKFkB7wn4qHHZHLKotYQzk/AHK7o6C0VUiEXUMw6Hw7ZtyMphlIhoxeIXVLQJmCLTUrjQ2b20jjZHqbOxmSFOUrt+ATCMcFO+9eXPvfjww+gupiNdhFLuEEoRSkxUGZkRXVWDoSIEIkbJnYwCSjAa0Mc9gNZkKlgzAaomKCjuThb4l1p+a5nrLtYBguVYXzbfiyKzyIZVd3v09VBdaTRCxDIigpGVIofIkIBHSjICyVCDBtxhxowsXWTTIkzI6N3DTTKRs7UXevqiZaRUS57DHVnWE5GJQCgBScO0jDLhiCjjCESa0tObyshcl2Zm2+VCIGq5gIGYXUOFVpBNpFo1ERokHuY4hFcUMqBzgOciu8oqg1ms69xHiRPbN5HIssjDHDszZV7Gkj+41S6bOZpZ9bmZ2D3aHMLMoQKRqbOt9SNSFgVzj6lqy8wiOGaKLubJLLOHGiOWpmRmkTaVEWFk7yeTdC0NCpEDzGWRiGk2w1l0oLxvTSVSInpb2ughyJoI1TkwSsgyRnkdnc53FGiRl4jco7VGZMwBblKymFWzIsjIqKGoHK6WbcqSir9RLgFlZh1IqIKJyE0kpBiyEWparaWZVUcK1viJHnXU15UsTxhXkSzQdYy2GMvpV5EZNkldSOSrjz+YJHuMtuxa/n2LpEyFsYgs5cchqQoBgRnZpiqBVE4ibpRxu5Z0rkBgZCYktXImUGbcs6Kpva5myPWEUKpEnzsRMq3VwTJ3ISFFeDwe7k/3NVaIDDGJcCQjpJKXL9tmZmMUZs5MeHcRJXX4CO+mUEkPj8RiEkEPz15mgkTO51sgg05h71EsA89khmlZBtEzjRDdAysIOECIyXY5XabpFTJcqrDJSKDOp2DWKEEiqx8BUHdkDCdSdNp8ZnrxGVAU6NKyEyx3X2H4rlNFJrPCGCchuwaAgUSKskAEa8rqYeZrIlUJRm2NohO1y3SFeISKmczXnZzon0wPUoBYKlQHAqK7W9OqOVsrs2pEhjABWKOP8H6pHnI+961mFSGC1orTmPsBDzMBCS+L4UEpe5GY1azsZrM5ja0e0CwR6TFsVUnE8MXEJ3tLlsOS1ZSwNKUZgFYQIrEe2rZdRMQR77z15un+dLlcJpNzAnBUmSoHJALZGndIG2oau2oxM0wowqoC6omsfFUGmNR1uoRb2atXpmWmUEkOTPv7YlMm4OWxUMVnyaEUIjrTUavhKck/rVj15UEAsjUt1z5k8UWT8+JRqFkq6r3JBsCQFBRjce+ipXhRwIORjwETcUWxmkE16+NiKmw6xigrHnfxEZNNHjiIZrgsLPQkPEzU1Hof3IcTw6MtpmL3pxMyVeTqMIeQKVlwILTQeMjSho/hrsJdGVIXlqAMRyCb6ja6KjMx3BfVES4zClVU7Hy5lP91csoYq4UmssIw5gZamD8ITEl6kTEewNSJPNW+TLJk/bMMnT7jAEQlUB6vLP7M/ti4acWNSU5CGSEqjgTm4VD3jdOCyKXWHwNJnQ1uEkFJMsAAQJsvwkTMdNRqCTAxVVSUTDAoVtKtiCJZFs+9Ap5RhUZBppGlO0S1bVrxyTHnwFQj1S59m8Fbwqk5MgOpKmVoQMLHEKOqSrD2w0DM1G5VAtYWj5mB1cfZVK2VrI83t69ZFF8qMovQJyqI1FnHQpalCOfFF605nk6lkuZEniYzpu5s7eUmu25DBJX33pTAtPuYE0JU6ZBIEEUbV6WZzpvNWh6fGnfmpLyiqGd86FOkniEAoqotqyCfcx2mlAxQHkBvMU8n2sQ/JvkpH0DQ1iQykTQrrDnLpMZrVFHsZV1qlFL44iiRf5MYKF/dcHo4xcJTtC5UbUMtQnrvCW9tKnky07RM1SWJCBZW14eLaCsIM8IjRNSakNrHABCO2rMW1ZSC3DQymsoIR8bj62trbbzokVG0cCHSnUK46yw5psevZyhZLU7d1kkjzdA6I6tAwN5zTfo0anpRFUdExhisXTJDF+u9198FxVJGZpqYiox0iiaGzMNhL3soFCa6LQQ6QEBqC1UA+0GkIng4YylAwhExFIw6vrMWMzQzAqLSe6fUAwWbed4JwsjMmXhAQKhVD/vMdMy22JzvhwM8Prnuvfudy8yThQcjC7ORnSUKMxU1kmY6RmckZ8xpqoA6p0rLMl0aFKw0WE6GWlBlEa3DsHA/MhzxkJldAJ6Z7AeRoEYREUBxgzhNyauHy5nuXrvysk5xc/nrVMsNBABJzpjT9Fks5UOOW3iVbuWsmfMow1Ra7XhmlFd67d01CK19JEnErAxKkTJZJeUAWjtj7zOz6AG5LTpHwYQiM1u1Xg327AYGgRbhikqI7MvCckhoQXT4iDRkilBdI6OCjweFqrJ1z8hHj48+xv2tuw8x0iQCkdl7JDFGTUdqaOyLSZnrj3AWykCOcEE2yeFuRk3ZRlfRqCnwfNjTDBTc3b2qReGZmdjzVCN2BqLHPuJRGoXkui7vvfvF58+fx+h350t5EDuCnsuyNDWPOJ3PkdGnd6kw5lrxcNv91pLFgQojY05NaaasjEwAMrd2Yuc9ieg+xE+yJk+T8PngmsFWdOqoaUBOtnFxgCe7X8DJQsbOTGBmpkpS1cew2rllGuR4OEibEts5ICzyUNXPmRExSr5sxOnuJhLCVGGMei8zqrzg+0kHyyl4cu/CVNOq9KoSZ0Kl0JMihcmnY3dArAx4NCfn61N+gs0Ywuoi6rIASJlM4Kjnvn6VzGzveWoVz8QoqBOYk/turdpFzx212ouokN1ILTNreylOqTzkW8+IoTmw/cxL4tjp1tivdsTEBa+vrpHho9fE63BYIqOEzvXjq9hDDWzNagNCPvAroujUBKDllKFZXR3pjsNxjfD7u26mV8d1XdfRt7vXp8t5I2V4IHOMcE8mLQxA92ygpdzfva4WSa0UIGAkI1UtgU2QqSgXuMFMOLhto/xQVQAmUZN2mjAJT660TATEEQIwSK2CVjil+WVbIF7VtLRCqoxWFIhiTRSYc1jaG48f9+18d3fbhqeHF7SMdB+7raz10Y0StV8zPVJFJJlRk0fCM3eHINl1S/z+/+x/WUZ4EKIG6yjp9xwQzztK1nKqNjRnHkZCWE62ezmeU21IKcQSMxiiqrU5As4iZWT9VK3AsgiBmV1dXd3e3Lp7WRtHsWpqnFiS6DpfImpSutfLamaInHsSclnWyHCPvbqTT2k8RTcNL7fSiIw6Z0hgiiewZ37URXjYQUq5VIEBojWz+fQzxY6IGPOT83vmlcyZETSbPczPxbquETHGzFmUB+4Bq3BYYvT60YyY89xZRkEwsdNPHWS4IzqYf2RfrtVflb3u1C0AIUKUdSVRNnRVeQG7VkuFoE8LMtRn9isjZSbO2U0hi2dYBs+xO0FDEiPCs0Lpy62fiAFAfHiBn72PiBBaeFZ2hppt29a3/nD8u4d7eVRkRA6v6YW4j5rhZXLk5OWXR3VdmXqKvBhzmQl6xPABMGqYLOzuIoyMh1fukRExOUkZ85iZD7yX+B5k+BjuEz2iRGblHey2vYGsXWAymOpCZaaXldkksUpmlpsPd3I/SWO6UcNIFXevQnk6LwAElVWPlTooCY9wUUMWW/bBIoBEdbaBefzu1kzIjAGyDvnabsscq0CUKm7JUNGIfj7dVVoNZsgDylTJh4ePEk3OuVJEJdLOI7Bv9VyKagJ9O4kZmTXDzD3dSLWQ0aEk4BNtZZJRP5jpIjljb/ZxCBFCBjDnqKzRbmkdkiXHrmdXEpKlKajZYhVvESGlis6oZRHhKDZ/dhCi+3aGmFYazMhsi178okoVzsjfYpnOIxEmFuEMcCY7cD7R838LQkuyMgHnAi4smoV57g0SGNMEn1E3vYpGTlig4qQyc7RWu3OU8yWQc27kiYiZfVviFmGGZ6SKDC+QM4p95h6A1JLOzONxHcMz0uEiM1BUJeSgGTH6iIhmSkQwbbX7+8siEh6Rw7SW6AjAoCKphAeqX+/Dy79XAJ8mjJk5Co4c7qKMzKWCbxCC3dI4Q02QCEynnJicQhChoqN0p9iV8ZURLtWmlHVDsmxNa1+ruWjWCV80QGY9WsyaI5btwbZtdQtNNd0vj9580tbDq+fPxaoYewh1FqCA9YxxLhhJ90c+iWmpVgr6qZGroIHajEnG1Cghy64bO/xISmkhVZTzaHQWKTqcNUQo3KMQ3cm9zxIQUFQkPRxJRAqt3ncp3TOTIuHbw5af00qhcm+Z6aqaKRX9YKo1rCEADDMBQzJIxhhVjgCoyWc1Zmbau6sUQd6r9DBlYKjEDs8CZTkn5aY527bafazMAbJcNqJK0UwWCl/GaczcLrdgSOl/JCLc2pTglKVCTWJqqKZW6PcUkuxR0XVEYxcGVHEKYZoKECXyrDNcZD+qQWqBrFUL1K96GPk6yQzU97iHCcgUSyTCKxSZGbEejr1v/ZzzLUcNkvMB3xJhcYZmLy1BcaGo2RhDrbRF0RojJDMMFfvqrRHJVEZkBhihjYCMQO+1ciSBS+/GTAWFUKHYNsa2jSZExe1MYC4D6UwR+szvGoqMUjVDh9dRjwIXih88z5uq0VCrEaIlOi7vskRkcTYTWaFYOX0SQ4TdHYCVjC4zAR8dJcTJjAjLGEKcbl6f725EmN5FhZk6EdR4QEEqn6+IIMBkGE8WOWZaex3QnnOwxnIw4jT4wmwvCwQNAZIpSI9eRjCFexUPI3LIPMYlYuxQFmbzO+14YjGdPtqITwv0jNqohPWcoaQemfOEF1KtcJ3qmKMeyIKyigtS1GKWvS2y5j2iUgSwyaq0GnWltlbnEjJtenbsJe3+mmqxkKmNVftX1yBzYr5THTCryrkZEZQCAiDM9Ki7qQoy64ByH/ttyipfCzKJSJmTT7BouOmz3plQbQLjgaAiUhG7hahhr4FrIU3ibr2dwr2EDJnpvZOvX7Q4pNb6yUzh2E6ZaDoxZPeEgBQfBbKgYP0s2jojE7QqeNKUSQbDUWV2juE2rZKTTUYPCiHSt742mbRtEV2XBPoYw2MxARCeotLdI1wylDldJQQMJnJUCrDkiHpOynIkZOppnEwlAhXDgyZCpZnen88kSYsM0nyMsm4tD/7MlEJVpYQ8oEAq/UuQyFa2THs2fJL+JypeWNYhME3ZCj9yaoyxmVh4qSgq1cNJFWm7aeqQT9UZGUSmZGCtoTnFTObZvoNFZPWEQkAVVSfIBPR9Nn4FcSNrvGymfes5XW+YMicZyGnCHN5ZEUXTvG6eXR4hahnJOt/rIUKwPP5I1AaUXr6vgWJBFuMnI8eugJ/bC1HlIne4pXDp8vqbVeU+PGRyUuo9igpfGqO5YADMPAmmTXoAUB5cD7LDKlgTgfLXrT2FAM0s02fdQgLRVKrvmuyorNkJlIX/OkCRWqUgK9enSGrVVu+FgkC15W6tTHIG8RAPhdXeVlfvC8GcumXpgct0oF5eTrdVKfMAK5I9qIIZRqwMgAhPTcaMzDb3GN2JUnXRZwZU7R6TBgQhZ0Cmunt68f5iXXTrMwduGzOxPlMigxOUBcEkmtEd5aPFOZZn+Ywtpn3UBieUrICMeVaV/KIeJgHSL5fO2qQSCMYUDCEAJQac1MxQa32Mgp3FIcpiWQ73qNoTiBl+VXd+2rJnMQsiQkUY9eUSuwxlsiZQRFbEdQQ9mYla2UhmqksKFCOEgA2z5oNBYWxjKHJNHT2dmnTL4QTSiwDNkqIkTJgY2Pf9Miio3s1LOji7hbkWPFzm7lSAzu4/gPKZRISoanjP3XkZpcdClWjJ4v2RrCZslnwzlQwZtR6n6mQfD1aNJ4TInEMwK/9AivoXiB1cAzD1nzXkAEL346umTVkzj/mJFJFKdE/knA/XFQbIAIusHSJVxTFmoq7rxHskFaXZ2hddYGroy7WsQLvKjx2iUSQG7AgwWSBhZJR9MTHTw6sX5V4FPfQy3IFn5NT5akbMUqPGjNVDZMITLCJsSjyIFmZN4eksggc4QRCVkqRKlCm9ZMDHYKQJfAQF8CTZszdlOjMxepJspCbdKSKZKPVVBHoCROGktZePDCNGBBEjwyjWWpkhNiVFex9gEAIRVv4tMkqKhET12Bk2kcVpWhAikai50EIEPTIQWdJ+L+1OYHhAZopMgQLJqAplVmhASjJp6Red+0CdV4JEDA0oJJOJ2MqhB9EGBng2hHZFro61K4l04MkJXWAWrmkRYekBEOcxBKlV26IVw5IsqJ5AFqolKKYbvYJwq5h7wBXIuqMAxq5XAuARtX1Ud1GmpOmeychd35SOaYBSOchVMUrFmUgKKV4RsRKZUiznwreqnWPFcLAGQDDVmljOYfWsY4sZX+eqE1H/4hw1V82p1eRPAQBJYtS5DlSNTKlTt5ikkXvMDBA1tQMS6cy0AgWY5bvvD+EIBT7Vm8cD5y5qgFSwECbUEbJbie0GwECBSRmiCgE4X19+OnmrYctePzxgDgUZMKFV78zRHYn0ijyMBDEyBTAWAixOgIZCAitQwymMYBSFLNlHR3KQIpYeGalaDm+VRKyV4eAeQstIpfgoQqiOKX4T9wQ8M9sqY8QD8BagDoSw7E88fFE6MgGPoa1QowlzgKNgVeVOVYhcRepymwmBajKrKq4j0gFSxm7NUOZpHll+aIHkHFVDag1nRUtHVgoi0pKekpFjBwQJJKihkkgFZcCgCrlvZwmsF02aG7XFAaclYjS9WdJjXLwjNZ3mGqkOpEh4nd69bIpIStKzk5Qob/Kdtpgpqsyxn4cpolJCUM/wELbMMpqpiVbW2VmSsYwcmZaamch5UoLwREV2kpruQHiG0GYtTAOSEdQ6YgPTBS9rSAMAkftsPEWIHLofBQWhEeSkP2KW1jFqUYtokojATm/IKlz3arN8RpmT8lFFO2USEmqLLlSVcEIS9QgVgBwkvV9qK8hdMzVn0VN6nsBOhZ+WDikzxJSlcKjFiSmKmCwL1OcfGKjz/xfTYi/IS5ZQ/zFfVM5/R1Jrd0BtO5O3WBOEyKy5TkGgAiRC4JHTryxQTqal0YkiMbDMSNg3N5Uo00/UPDNVmJHumeViC2ai0glmf86s4ZWU8DpSGKA40z2bMDKGJzI3uCAEKSpO9OFSGJski4xAbGOshSNVouGs7mc1VHBiTQFILf9kjyJUlR9ZyiTuc2gB/rWq05muRR9NC4BiV7LkHF3Uhh3MkMDmGxZGuIlm+pnSHMxB5pUspyG//dGH/9ff/Z1x2ezqwMi/+eWf+xu/8M0tLwCGX1oHaSmtBilMEWQl5QVSAEmp7qFuA0oLkoJwJTMgRPiYYFgEE5ke05mqgAdSiKiCYbZ0MbxAtZxlHEhGT1Xlrn2dCUQZhfVMPC68vHaMrWZFLDyMoMh+QBW5nMhKUka11BTJHDuoEuR0iyWlmiiaEpSJt+8H1AMUk0mhVkD4XDyzCq1ysbj7iBBBzlqgFsRczOHlhITSJQKIdERZ0qCidvVTdR72BGAAWaEm1RFBMwFnls104W8oyjyQtROIUKSKAewd6dzEHvCQOlpznrEZmcWgS0xXsqgiNtUywxmJJDUryTwji7QglHAoqRDL9OFRl20RZI7cDXRGZrIGKFm0pKWmMogIyyUjx/ADyycA7p7QzBzDq6voHiBGRGSO4VdoEd7DM+mSKPdjylYy+ixbXCGnPJWyT1NKMl1WRqqBOb0xaqN0j6yJqIdHgClJf+DlCyJraUzRNRZ9fX//+nxn/+yHP8xkAKf7u6u1vf/2mwL/2QfnH//sj//iX/ilL719JX6fTDEaZYGdBv5FP/3v/vE//fv//Pc/OHXCEmjov/u5H/zVb37z7RY5LnfNJfKQEilDJKmYRdXk0FT7sW9+yMyBJKQ22tm2JUSgBZQrCXGvlgBgjjoxIYHaj0CAWhPN6VZcMFI1SUIgXHLGihCzfJSczziKVQVg5w+lM4LWWrULtXT2w4ooQUoZxyT36SRUNMHiQINFFakXQOZU4k+zmbkY642U9LjQeUco9n1mgr5RR/tIBvgAoUWhbvtmwd3/lUbMSczEY70EKtV7Vsr4vEBimYEMRSLxoBoQ0bmQH+pogKaoBEdhTZmZOc/WlGI8ZaZMAmXt/FEUu0ofFyqiRmZZ4/d0gCxJrM4AY8kRRRMpZ9bwQIIoGSonuPJgGIBMLxJbADKqaGyaAXeM4VRYoYLJGsfUTiIUH04KlZnUjO4DKY2G1BYeiVFPY2SCi7bMGBHblsvSCGzpZT/zMEiscigBqvrwKDgt0COa0iMS0GbDPT9DYCDJFHgG2TwepZ6MP3z19Hd+9scfXV7zP/6VXx7uZi3cNfrPffkL12s7jetXrz/5/NvLt95+4y0VWdud4dmrV7/9wUf/8EfPfv/F3Q+fvwKERk3C1elHbv/Tv/nr/6O//Odf3r66e3xo4LWPJcORnqKykFZUhLqpDyo5crJYAKKEeXOfn0RqUMKDnPSXz7j71FkWD8ipTCAX4wFPyhTTOrQmSRDMDCunwESFxAB1oHK2dPswpDIlH2JgImfUz4NRJeuYyawExNKpUhXUIvMUTWJOL3a0d47LMUFcUZ30hXLSrSJqkjwTmQWg8oGnNWGziS/sZSZy9+lBsX/qBK6/W5whfKrSkOlKA9CnFKaWSGkOlRPUVkkRitR+Q5FKhQKZ81tnn1T/WcenzFcIAlPq/Ok8MjOCmfCHlZzwnGObTA+vgsP3zMJZLiUKT6pL4jPbqqaU6SMjUlIiwiMjiws9ijBTPZSPnCaMyOFFdUMG+hgU2caoG0DuDk85E2gjwjM9wClMqz4V4eEYxT0i2UePTGT6BDGrrq9iIi7bgCplvouCjIf7QE6zy91PQ0V7xHc//slvffjD1/QU8j/9a/+uii66NKIhG0aMS0o5vF+WhB6ufrRt//ff+53ffHHz7LIVaUKkEI5Mwlwv1uCXbz2+/u9+4xf/s+9/962r47cPh3//L/36cZzDL8fjdXYuCkj3ZAWRI4VAsxa7eFT36FFVo9RbLGF1GxGqRtK3QZWKXqijYQrwtFl9Q2YCyikcSUFa2TpTiygpQkBFVYSiyZLbqzUDOdlFO1+kZAP1uJfni+zGoigokqwuUHbbSJKl8WXN9yZZB6qaohMZxcSWMyeunZkZKZij7kHPjALviMxwJBwPeXtDC14uwuNEExCRy7IgMcZWzZlABJLIwAAg+3FaftYTBakyW2Ue/0VMVZBak5mkQkl9kNlo5W9WNzmnM7MAnmwaiZwd63SxdBAIegbcObyAbIkcXkyUGiOyPI2Wtpzv7oHpx+t9lIiqlx3BpN4hEyN89k3OdHcPlOu0l2tZUT9zDK8ieniIaXdPz8r46IneR2acL90L4KludQoJNIGt98uo4KXiURVOztHH8At2Y7dZ3elUF0cUEsSiqW5eOhgS6V4rGUEMZHptwd4zkvjZ60+++/LDH7/65CJlYwL+J7/+d16e7m6286NH1zj3t2V59/j4Xp2JcB/N/uXLp/+3733np+MCCAoVr95AuMOPmCgFCwRNQ1wDv/pzv3j/8mUL/9K7n/v4k2d/9r33/ltf/5q25mN4L+CXoPiU6+zTdFKVZqnJZhaAMxdtFTbSLrmonTTHIhJ+8FTx3vTl8A9f3zy/O/30xSevL5dfePudb7z/3pPjUQ8mCx6JPe7p0CziFFgHgojQKIgmkwYkpqkVmEJ5WIQ1peJ0PMvChHaDxTJ/F61YVIIQVYpOZgLStM2GkKBq/fV5DYk2DfcKO0DBfxcORmg4vTNQe/lAVkfOMTLRkyGqYxt0b40j15HucWpp0RmAXaWn0YE+mEmI02QRGGqCaCHIJQVMSOa0F7cUUbWW0pW5aAtQ0lsVvwYqqECFNqc3FVIdrAgFza5ZPKjy1kUM5mB6unWBBu0SwThjsxGBDaEc6gGHI5N9f7LrbPF0D89kMrySayukOwXaMTqRo+UAxwj0S1VgPYMM8HzpSDBldA8RT4xI1IjBRx9+G+GeEkxn97xJPwkWUDyKDh/EBu8ZGRpESuYYBjldLiNxkYxMjChWbpB2WBo8ewewtDUyR+QYfjfGNqJVlaWsB9tJRLQAPLLxab/73vOf/fTVs+0zo3UA/Nvv/9zvPv3Rc3EuyrP/ypPP/ZWv/9J3fvIHry/3Jx+XjJfb5SY8qMzYRwpzjvBguFTl5+7aXkkMiTHp4fVDRnzrrbe+/eb7v/zG+z/3xvvI3CQ9fc2SjQL1TJudMv7RH/6rp6dX22LnscXl/Ktvf/Xf+sYveVwGCmXAMXkEG2ht/aNx83/47f/Pv3zxrBMIAgoZC/n2cnxjXY6qXz08+rd/6S986dGxEb51IylMpZOLZBMCbsa1NWYqhU11LctDHpZlmiQiRDGTqaddG9VIRtXMFLLQDJn+Big1+dyFBFxEdFL/K7oDEW2r+kxQYj1PpLmkz6exSjkHrk+bZJx8iwLK3DMcQwJIQY7NkCF5Yrcuh96SNnR4u0iibU3JLgOUJJUikZU6uC2pLPcCapqKiYqKG+Jk4QuPkUq6sTkghfdL8XqpAl9CxKV+fMK+YFWTREpSAzoSi98RmUGUVdjED04ZhLcER/FjByM5IjOLgZhelsWp4RzDi5HVy+5zdM28hIxEeGdyuGyRvYhrgcvWEfThKbI1tQ5eBj1dcBbvGdblXvKOcX/pvkVEuicj1Kyn9/BL+Nk7hLrVWDN9jMzsyE1EtiB5qWm2xxJcPLdFu4Q4mqhHbhFbelb9TAwEMhdTTcQ2SHbFLfofP/vg2f3NlsOLAvzZVQo1RLakTRWjQHX0zZGYBu2CgObUjxffRcr+aF/xnDgD5vhsSvLmrMO595ARinxD2y9+/uvvHx+vPd66frz5ePzG47v7083tTVuXd95557/4nd/+zv2zXmUSgcQbKX/nF379r33um88vd/cxDPJIW1ORRX5898n/8/d+4/v3nwxr8GwpQm4YRYEJIBjIeHO9/vqjx//Bn/krv3z9nvftVt2Yb51pkmPhJt4tVGAeBwiaZhMjbR/lrW0JuC0FCbGMvGKq9bMMD6XMxFQAGMXMWD2dcq5VoQiVhcRWaYRRJ29QSpmBgKT1LdJH9IhIDwnJyLNscCxui2u6Oy4D20kEnjZ8SN5LmMsbZ26md8sGnA5cjv7IYOe8C5GQxbMnhyQVZtkOyoaztBZgiJ67j2Bb9NHi5qOvdMl2EUVzMxUVM7A6DhSPwZypGMYELURQeHgCI6W86g1yQChwggciuwlGyGlg0VN6hDLb6e7+cj6thyPZ0HvGSBbhOWv77iN7j0z2EXf3l/vTJcEGQ4cv1uPi3hXtdGZSk5vQtq1vwz0xMlRN0/rWAXYfLuz0zNQuQfHM3p3WThE3fVMAwijlWx8SyRHh/ZzjYjnok4oMRjI9S8G8Sb6O/tHrT56/evXW9ZMvvP3uQhvuL0/3P3n+0btvvvX24doiZQSAzrgwBBDhj29f/NHLD+/GeZ9t/cmTFCDY9mkZkJPxMPkoc4gdTAgwkHu4xacf+a99bh9yP+D8gKBoBCCDzkQZMQErRISXCFPzzBEDZLN26WdYMKSKyMIQHtn6d3/hr/65r3x7vR8w/VcvP/itpz/8cHv141c/3bY+/8zcvpGJgkJjb/tKEv6VR+/+jW/92a9evfHVN94+gEdrQ+NyOR9FH6/NiD4ukR5Sw5U0E6t5CnbhSE1UauhPuHuMyHRRYWHYhBBNuB7W43ElU1XMNHJYy6XpZAvXOEVkXGI7XQ52qItwupwccciW6bpw+CViIDyHC+Se9rsfPPvj56+urq7ff3T1WPjeUR8tdtlOIz1FDK2FpsgR91fmv3/P//O/+PHxcPzv//lvPvbTRZY3FjG4qF7EXp3HD57ffe/jV3eXy7O7u1e9/+yTFy/v763Zr37xC//hX/2rXzjkm0s8vrKz9H60R75U0Mt0tRIgA8IcHZnRg2q0JZpFDhmuMOj6R5/c/l9+43d+fH/+yrvvPnI5rOvv//H3/9LPf/vPf/6r96e7J49HM72/H3e399v5ktJcD97vBB7gcHhxeoUZYzufIxkQd5SA353gErPQHglcooGGvlFtZI6MkXEZGzztIhfBRRimkUC4QRTXH2+3v/nH37tj6vH47NXLV/e3DFK4rMsiegX93NWbX3/vC0/MHH6Jnh5aUDM4tN2N7ZPt/oPXn7zY7n92++JuO2dGg7x79eRzb73z/NWLT+5u7rOvtM8d3vjTX/r5J2yn+/t7y9/76IdneGS83k6XHPjsQvqTy5Sfzu65z6f3GtZQB2x6rdSKpf83Vum/vnCrdGUiH2wYSDCEjpCkZdZekkqANoqrwjka3Fe6JorUH8qafC1mf+1Lv/jXvvErf/ThB//5H/yzD/trABN8LB4e4IIUWHE6UCYFNeKmJXoONX2b9stvf+lz7713XvCT5x+9ev1qBd+9uv7lL37tL3/r24ceMkb9Rm1CokI7FJzo0XTzClGJgGSbNnys+WpQqFL5fDFG730rImOzti6GdBE60CNG4MXd+eb+tLRjQsL99u4GiN6OTH9yXNW3d9541Egyf3B7+/d/+3f+ix/99JMRML1KPoF+/q3jr3/xC3/3F3/pLfNhY1g2Eb/oh3fbP/zDH/6jnzz/3vO7o+WvfeGt64yxjV//xT/1ZF1+/OEHH5xOP3l188MXt69GJOg1nJKlwAWJ8bU3Doe8/+bb1//z/+g/+Ivf+vo/+c3f/Hv/+Du3r2++/rn3/vpf+bd++P3vv3r58s0nb3zri2/+pV/71Z9+/wdL4Nu/9Cv/+F/883/wm7/55775zS9+4Yt/8KMPvvvjj//B73zvX768vWABUzJNdfP7r771zl/61rf/6Pt/8Itf+UZTuXn+s//w3/kbf/Hr7+v51dbPp5GRBsd2uYztQgQwEoet23AHmJDhmckRY2RSDkqTHBHbXb9kyIHrzeXcibYuEd0vp+H9ThMbNduiq+jSI37y/Nk/ffbRH7748Md3L72oV+V1Fywr0loIjfLW9RtffvTmz73zuW88eedJKCNfp//4/tV3P/rJ07vXr/vpbnRHABRKFNXFXVFqWqZAQA2+/8bbb1xdf/zso/Vw/OT+9SWKtkhFEhklWXyYtD0sqAM0gIGaVKJQpU7P6SmHRNnpFsWP/38W52d+6wOroBbtnFoItaL1ilZORII+nfBqo5iEFqYk4NilqAFBgIHIdTmcfUuEZi6BDW3Guxc1Zdbo8/cVK7fKgk0HMjXSZecDBBrgkzYNTfy5977wd3/pL/7pz39lEaqKIDnGIoIxXNKNWWtfirAAQDKkyA8eXu+jR086Msf078+KiA+X8F7o5IAMSncoxsWjU8/dRx8LMc7n19EicozNBZeG//J7v/fJ+f7j0/l260RDmQ8hidzoyPHtNx//7a9+7dfe/5yKfHy5/f1nr7/zwc1PbgN6XOGCcTtGN1cZ6KNkGUMslmaq60gVE5qmMMNUthjO0C3O6BHjS4+uvvm1L/zz7//hD29iEZHoi3LzLZACOYJf+/IXb1+/eLJef+vbv/APf+M3XmyXL9iR1j48356Z1LamLCnMHiwVXGw5enZIMloGge1XvvT5X/nie++/+ejp65tH3v/tv/Brt5988jd+7U9/5d1HbdxGv3FgRI0rCGDrY4ywEQccPuzrf/ab/+qDZ89/5Rtf/1Nf+8LvffDR//63/tVPfvJjifi1r33zL3/t57795jvvXq0n3baRL0/+L3744+/dvPhXzz7+3rOfvfIBiiVbpAE+nQoYe/FYVjcAMv1a2leOb/z8G+994f3P//Dls9/66fdfxBkQTS6FpRM+OdS5K1tTKA9FasVNIyq3rZiwQBZjOud4kv/6+qJQ8zOnaDGoNZFgoObUyQximtT911mlRdDJh3ObBKHJBS2V3Ue5tOzmXMm5aOv/mMhklMCkkH1NGDio9c2T9lms0VJqMj99c8VX4NwupH59MXwSBAbndrUkATgeOAmJHEfhn/nC137+vffW1j735pvb7V0b/svf+NaX33hi50tmUDnfXzF4RGqwVtOX8+kUjMEhZCaUWiNBgmnlYeAhctfH89vT81e3z8+Xp3e3L8+Xm9Olqb59df3lt9/98qNr8bxs49z0n/zge//fH36vi+2FQ07ro5o6RwsgYjuQf/ZL33j78O7zF3evNiRlsfTsvSaLwYxIZqp5QMUaRIvNsYhQIRIZI0b3i8c4yvpIrjfYRsTl3PO0WTRXZTLH8A3CLRwiGTj7GVpQVyqXoAzpGlSnJT0jNDsHM4dAQg6DTrlo2gjSh8JB9K7oCe1YAB4lJE6/9sX3/9af/6X3l/E3/8K3/tSX3tXL/db73d39GOPtt99Ws5vby2/+8NX/5j//jb//ez86Rfvco6tvvvPm02cf/8HdySmQpG/vLsu33//cn/nqV37hreuxrv/sBz/5R9/5zsc+HAq2NXMQzkTRSmWOePdtnvPeZRRfs0jNi7SIGERLknRiZEymS4akZqIA4fk8Rjg8EDqJXyX13NtAAEgUx/Th735mlbFc4eba+3RZzdlYDej2R/i/wcfD9+907PmxtAXEtm25M8qK5PDZb+Lke2KfBNYL4O6gu/e8Dy87P321n769OYSXfR47Ia4/8UbmAOuBX/EQVpf7H9VENuDzb7//1772c3/7Kz//+bfevmz9j18+/fHzp6uuIryP8epy+ej1CxDffOu9X//mtx8rOO7MFlAg2sdwjxevb356d//05tVH97fPx+WD169++vLFuY8+aVKffpi0X3r//V9798vfeu8rP75/9X/8jX9wi2gQn4X1Zy4xJoM9mQVjvffoc++/8b4M9V5vLTzOoJwvuBv3znOTVdJMTSnLugTG2W8vvffYHH5/uXcfiWhc1mVVax6k53E5mmh6idegpuHITGtt5Hbe7vs41+VvuqosmRmTNJCU9BxlkBBIh5cjTAakFB91v6aaJos6kECIjnDgssB/8Qtf+Du/+qfk/ua7P336yc396Xz51V/85jfef/sffef3/+lPPjl7V1kS8PBAKFSpyToAkCjblPHpIwkl5eFo+pPH1r/5qX/j4ecEyGRnzswdPnO2j1P3NL0jy+H5gc758Kj/119Qf2KVfubTk0EWEf8VX/1v8vGvrVLsZkK1ours45+EtT7l4c4jck6Nyf+K9/UnVua/8ee4S2llTx9++KnPfvPDCt8PY6mapwodIxt0ZH98PH7xyVt3p7uP729vfRjMkBljw6ThH5Bff/e9P//z3/jlN9755O7uB08/fDHOTy/nu7E9e/3qdtuKx1wfCgPp+WBothvqZAJxDXzl7c/dh//o1XPoPnv8Ny9A7WXl9ALRtKvl+ktPvmq5ZsKjb3m6+KVHetlGBk0akanwHPeXm3O/88kPApjT+WHXqRHCmpNTdx8blu8UQRGJjG2cWUkKEJW2tuPBrltbM+AlVcxiLaT7GBgDn1KssNOnMrMKQRGhe5YjM4SIlpFxDsRAG1hSFvD/196V9Uhy2+CPVHX32LNrw7GBBPBT/v8vS5Aggb2bnemSyDyQVKtUx1T39OyOAfPB3qlW6SrxFA+icj7gnJA4ikfbHhJFlQayDIlSypglx40+YXpgruI93ca3VxtEcQkSjG3hjVtHW8ZSM/usj3cfCKygHkuJzQO53QjAzb0dHi5iadts/sr83el8YBkLIs+iEuiUhnR4kIw0Zk5pTFRc2M2JpVjMGxNJKflLQSkDlxK6h4WjAcrhpWzxJgoQRHNHLACoxz4Aaln2nYu62300BuBlPElAZkxPqvjl9Mtff/z16UmySNanQs/CwjokSZQIrGN++t/4+fPzZ0WGBQvXPan2+Wqv98MNqVSymvhcVdDWqq+gRA+Pp8eH4+OBj5qViiW7KAU5y1i0KIlaMgpR8pQR5ocsYLLM1NAEc1jUAjZ9JhzZAfY4zKSUEqdSJFGyUDpRcf9skvP5SbWYVoT5FcUrsLT9ZO7/6cXa1+DVWFrPRzxW4A1RtOVd1GjF8WuDpUTBUUMD7aY6lXhr/1jH2/mf7axgnnRwT8vg6kjpSDocgERJEkXSPilWaVoAhqJ43nAjP8aRlBQ6ELEKQGI2CbWscuYpg27aB6XCVIi46BEgIIMySftRKpZCVSw3PsGCOln5h+PPP334m45MUKISrsmiXD6df/v09Ntz+aJuFAyBH3DLgAv9Wifm1IHnx4wIkYfL0ZVDw6cDnz4+/PCQvidJZBG/rKUUgQikRNVhWCimCrmjIVkkGosLpcIoVCzHoJSzGUEtlyvRwJ7FjRMPDPb8kijPz89Zs6IQqaX/IOX66dX9+SaEZ+vI+uZcdt71Kg9mELjvrr4GG1cHZjrOsdHS894GkWOqFyMno04RZj4rqpnOWmWSbhS/t3FyQVSu/LntxGgXlGHaVNjLPYjL7Q9CCmiKEj9a5Xbm4sm9xJaSoATkehPWwEl5BAqBYMFklq9xaTlh3wO0skFSZRx//fnvj8OPPKpkTWA9yDN/+dd//vl5/FQwWmyYpWGyqQIVUQNLgz+6b1mIPO1WKkIVi30F4G5+ACOd0sOH08fj8ECUVKCWfJoiSBqul3oFXygRshYQEsz7Ts8yZi2Wl5LgSeoEBAyCXAPpCcRe02tQxVhGmK8TR3ML75OL53Odt0LqDoQitnBU6p/Nj+IBAS4+vwljm2BpI3bero6GU47rhDdhKcwwRh15uxVLO4tZB8vGp1A2LjKM23UJrk2JCcNGocklMQ/cdH2rTp8IimTBQJY9xH0iSRqzebMtFmcdX53UrGdzem9RcaLiB9EPixClAz389PjToIxCHx//Umj896d//P7l94KiKHCHFCeI3ZaQY6at2TDTnMmoO4sar091FrVMWHbiE9IwHL8//vAwPEDJ0miKsTjV2G7Tr8w9X0EQSC6mUGfViDyoJ1NB4KIl1CXDMTdkmPxie2hzIM9q10uI9o26Tz/Z4RV1yX8ic7K2t15rxFkfKCRe0XaM28drM01jRbltVq6q87EIcPvE9LFlON3SM2f9r0q8c5y8PEHgSCOODyAmLhb3ACKIuEZn6eAihYozEw6dzckzVD1dgao7WlQMnK+CE1tZBiCTAFZEjTosNXWAQHKJ3lPjqyAL4jYfvXQYvhcRKc929S2qBHYJEEKw9FgKxLW5b6X7qxERrMjjZcL1q4WQfPnKSjgqBBAL97UfBj0d0+nDw8dj+g5CoioQJohHI0oMBCklS84QoSJaGMKWzZZZwX51J2IahLnmAyCm+HDKGt5dAOD1GSI9Bk1Uodnl5BzMuFe/Tj025sRiRUqne3I1LDKz9tehksbXGb0m3ZrVZPpQdd8yNqjXInS2orV+OmLZSeM9xsLl3tq5F1lzQu/VkOxXqZ00g8Xkaue7tHyfgDtUmbwcpn7SkMcu6xWXEicEkS7DI+7g2meuSni4KXv2xU7m1/kGdgug0MyamcfqF1RY2/KBDh8ffnw8fUh6kKJKYlF1xaJdYPFnAiJKXDSLikUNmcE5OK1FKhTTxkPeMfJn+GmGRm6RikTJ0+OqqjkbaLkkMW33luzS1LolPy0c2+jkOKU0ls9jft7xYVeBgrLLOuoNc1a2jdZ3h1cOV3XLNdze039rZUWwPo1uncA3ekHoqP2+TpSzaPzi0O0SwiZEUBAnC2er5op6jOwfpuABW9zAro3rtJuHDXbRXGwhmu7qwubWCJjJS75zS80V0KL59y//fX5+ejx9OJ2+s2C0IsW8LxUgHoDRTFqW4gIKUDGfNK68nWqfXpEk5kTmA0SBdW4RjJQWdea2D5FGeHJXZzhpiTbjT5hQ7tH7vu0iC5Lg/YGwoJbcpdsFXoq4EfRGS0bXNXzblgcWm80tvV2HHXKuDbGI5xtMe23yG3Sk65PgxZON6kcDF/iZo8BzcOp2CIXSxTrbdD5j5u26XiRzofPOXrxC7jHtJpLMgId0/O7wONBQ83MYOhQaBdmZpzmYiUuw3ZxBqhFOGRkULLLVKUVFM7iuf5GA1DNUXZYf9MXEew9v0tBc7LINKlnOUHBiqI7l6UVCvA27eOlbcM7woNP4945XNg/6nhlun7MX360tO/TTgJ1d7YGKUYZvl8w3tXPLRqYT9d6M/i1/qO4fnfS+OFyLTq1utkhBWsbuTy4v+391kcFurtpwQwGFKFEu5yLnQzodjycmTimVomMuz+cvgnIcDgMdyEohR3pUy7Bm6CoiIC6hRpHnNbCUdSXEY6GLWukrjW2saInGekDxaaoQ4XWSElORXOQsmgGRDHi1vjeHe/LSy0fV3s5hTxd56QbznB+drUFnr++a6j5Yw9J2ku1x38DqjodXe3jbhnEpu2jSKBGhemyj2cZmD+sEFvneHEtp9m6Luv1K4WxlcUWLO9M/9HwAClgxL2NtACjxkNKQOOWSc/ZESQQ+8HAaHg7pAPNLwmTaACxptc/NeT4phC5aJQDUuwb7L3vpelbVEmTOal62X83HUpOxmUBFxzF/gTta3QGC4hF6823f7Kti6eTv2YecH51FWXQDM9ek32th3k8nP2+Ix2vz6d7akJldDqst4wok1KGmE1x2uvbP4aQ6aTm1DbVcunv9WgK3E0ubbs291ibF09/NFSlyFoIYPKThNByZDlb7uAoURr8sGY3d/zU7PKF9IX1QXEDEYI2cOZmqeVgkK5nhqcPScBAthqV1M6z5xnZtAzU2grKOpcPNA9wX5iJWfd49eVE2bjnVDYhaRcq2n8VZLVKQjakudrvxeucgebkfmozhPVfjhyJSHK9Dt0b7R70Znu/bi1PdB9Xwa5YbEwm8Ali4RJqRVryJQknO5TzK+cAPA50uiAcvMNlO3jyNARAZ0lYbryIuTg012C1DlmN4whJ8wQTUsp2ai47qBcdCT/Wu3p316NKynp1QTqg7UmuDxVuN704w2D2HYFu+7YTP+spVx2tDpl18uMhJtue2Jj60s0DoUvVEuOiF8CxYGbCjIEQTDfJF8eSVX2EuLXe0YPaw7bZfxXRUkIL5MPDhkI6krELVd8kFDRVLd0pIRrlKKbVEupLCzFFOMUGW/hde4IyZ/WAWw2UpyLmci2ZFzW59N0W0001aFbcbY8A1lPJirpg92Wklqm/5/7+O8n0fVtB3OP+3wZqm/WJXzUPvB83Zbe9FNzT5Xq4GMI3e6EavUu61uvqdoD372xNQhRbJIiIix3TilOy+ymUHNaOSpcZTcwtCsy0EipLeSgAlttqTsBzgKaloES9vSQRRKZKzZODi1/WmUBle93xYlOuWVSbfyP4zd32+yLvaBhVNd6LQglXjJVY2mer0rf2wMUqr1y3+uk0jFp+vDbemF1RobSRY+RZzQWAPtF1taNT1SWdf6HqYTKzSpKVuLw/dpwOqkuUsMg7pcBiOdosasRFE5mSiYmmQLa+lX8Z6ZXoXeeF+cgUgEb+DJVIChEoRz7xtThBfhZtgjU4Nsx8umsPszVV87k7DwvFqLwygCl376i/C6znVtbB40NshurO4zWnnM9zAqK7lzq7msGfTNpTtG7Zxp4Hg2p6jHxHgXLJqHviBmT0Wx6OpIuplNii7qVzcBdWrrYYk7DK4KJcxj6rlooh+FVjbKOLFKPAl0OgI69ra2kgeQHgpItS/dS3cIMTeXe5tu92ARU7S/nRtz2sEcQPNuufbW3GtJo8prdluudhsY7ilyWg13QDMGBKnxEPiQZWiKKQr8BpG79AXRNUyE7FaASiIKixHH5lTsJZzefLEEW8JzFyrYPsT4/zoFzxsH4WOUWBpl1clmdpV4yc8v0vYhjVWcwOyzWd+d4xdhDW6vl/I3z8QVrTTF9tvy+37YU0lqZ1fSwKWGrsmayq3QEQkS048DGmgKP9hNzREFiOR1atXj2N+Jkt8zgdOVky7OWbQsZyL3h65uRNixMny1nam9z1ao4jbSNIh0gYRutZKcZtU/H5gkXe90Vq6b7cfH3aK3HNY03uxgu2tXr1GvPaNRWHTsDwmpFCRMUsm1y9ZRQTKRCkNVknknHOR0QrAlpKLSOLDgRJRCm6EItnKUgOVYdeF3PnSRbV3r1xDHEpLUeCYCeMb1ti5+BR3X+8O9siQ9x1uQ12/WRK+y8TmDzeMNxvKqurE8jzvpOHtYmZWC8i8nigsXhm2euMq9WdiUFSsm7QnpgTwkA7MTIRxfC4yau+3UMf6NrDs1TCfzpUT/EPyvW8FNzCx9wCLokGNBKhPGgw3lctjpq9f8oJRY9ZgGQTFchXO2qvlwjnnYpisvTr6Lr7LApbepjnoDpL2zaGS8Prftx5uZ8s3smytwSLb3NiNbZNv5ZZEZI4EK8rRtzvxuqWFAQpIlCbTd4KZLfRYerVlfIfL0Z+wAX9cLmpHpU1zBa84+sIV8c3D7n54Q8/v+isMLUuplQ6ZyKsVBwZ2+XLbLmrgf/PkbrCfse8x3s4Vv/0W0TeCdkrfZBodUl2PYCbKqkgrx74R4X7XuPR2sBxfqri4/hnc5gmIr6J0XWvM/IawIWb/4bRTW0spuf7Z/EhfWYZ/Bcz11Q1T6beB/wOyVTrGdEEnrQAAAABJRU5ErkJggg==\n" + }, + "metadata": {}, + "execution_count": 18 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "And as expected, the closest match is the original full image!" + ], + "metadata": { + "id": "k_gjXTdalsT7" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Generate hashes with Embeddings indexes\n", + "\n", + "Next we'll add a custom field with a perceptual image hash and a custom SQL function to calculate Levenshtein distance. An index of images is built and then a search query run using the distance from the same search hash." + ], + "metadata": { + "id": "BPIplNawl192" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai.embeddings import Embeddings\n", + "\n", + "def distance(a, b):\n", + " if a and not b:\n", + " return len(a)\n", + " if not a and b:\n", + " return len(b)\n", + " if not a and not b:\n", + " return 0\n", + " \n", + " return int(textdistance.levenshtein.distance(a, b))\n", + "\n", + "# Create embeddings index with content enabled. The default behavior is to only store indexed vectors.\n", + "embeddings = Embeddings({\"path\": \"sentence-transformers/nli-mpnet-base-v2\", \"content\": True, \"objects\": \"image\", \"functions\": [distance]})\n", + "\n", + "# Create an index for the list of text\n", + "embeddings.index([(uid, {\"object\": image, \"text\": ihash(image)}, None) for uid, image in enumerate(images)])\n", + "\n", + "# Find image that is closest to hash\n", + "show(embeddings.search(f\"select object from txtai order by distance(text, '{shash}')\")[0][\"object\"])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 214 + }, + "id": "nJh6xkT8le0r", + "outputId": "f874ae12-a5b8-438c-e883-d948ae87e68c" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "image/png": "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\n" + }, + "metadata": {}, + "execution_count": 19 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "And just like above, the best match is the original full image." + ], + "metadata": { + "id": "I8KiCM81mFw5" + } + }, + { + "cell_type": "markdown", + "metadata": { + "id": "aDIF3tYt6X0O" + }, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook introduced perceptual image hashing. These hashes can be used to detect near-duplicate images. This method is not backed by machine learning models and not intended to find conceptually similar images. But for tasks looking to find similar/near-duplicate images this method is fast and does the job!" + ] + } + ] +} \ No newline at end of file diff --git a/examples/32_Model_explainability.ipynb b/examples/32_Model_explainability.ipynb new file mode 100644 index 0000000..91825cf --- /dev/null +++ b/examples/32_Model_explainability.ipynb @@ -0,0 +1,968 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "kernelspec": { + "name": "python3", + "display_name": "Python 3", + "language": "python" + }, + "language_info": { + "name": "python", + "version": "3.7.6", + "mimetype": "text/x-python", + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "pygments_lexer": "ipython3", + "nbconvert_exporter": "python", + "file_extension": ".py" + }, + "colab": { + "provenance": [] + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "POWZoSJR6XzK" + }, + "source": [ + "# Model explainability\n", + "\n", + "Neural/transformers based approaches have recently made amazing advancements. But it is difficult to understand how models make decisions. This is especially important in sensitive areas where models are being used to drive critical decisions.\n", + "\n", + "This notebook will cover how to gain a level of understanding of complex natural language model outputs." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qa_PPKVX6XzN" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "metadata": { + "_uuid": "8f2839f25d086af736a60e9eeb907d3b93b6e0e5", + "_cell_guid": "b1076dfc-b9ad-4769-8c92-a6c4dae69d19", + "trusted": true, + "_kg_hide-output": true, + "id": "24q-1n5i6XzQ" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline] shap" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Semantic Search\n", + "\n", + "The first example we'll cover is semantic search. Semantic search applications have an understanding of natural language and identify results that have the same meaning, not necessarily the same keywords. While this produces higher quality results, one advantage of keyword search is it's easy to understand why a result why selected. The keyword is there.\n", + "\n", + "Let's see if we can gain a better understanding of semantic search output. " + ], + "metadata": { + "id": "snon4fqZbalQ" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai.embeddings import Embeddings\n", + "\n", + "data = [\"US tops 5 million confirmed virus cases\",\n", + " \"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\",\n", + " \"Beijing mobilises invasion craft along coast as Taiwan tensions escalate\",\n", + " \"The National Park Service warns against sacrificing slower friends in a bear attack\",\n", + " \"Maine man wins $1M from $25 lottery ticket\",\n", + " \"Make huge profits without work, earn up to $100,000 a day\"]\n", + "\n", + "# Create embeddings index with content enabled. The default behavior is to only store indexed vectors.\n", + "embeddings = Embeddings({\"path\": \"sentence-transformers/nli-mpnet-base-v2\", \"content\": True})\n", + "\n", + "# Create an index for the list of text\n", + "embeddings.index([(uid, text, None) for uid, text in enumerate(data)])\n", + "\n", + "# Run a search\n", + "embeddings.explain(\"feel good story\", limit=1)" + ], + "metadata": { + "id": "MnRR8pTzK8h4", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "956e405c-568b-44bc-b8ea-d4b6f3cea9b5" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '4',\n", + " 'score': 0.08329004049301147,\n", + " 'text': 'Maine man wins $1M from $25 lottery ticket',\n", + " 'tokens': [('Maine', 0.003297939896583557),\n", + " ('man', -0.03039500117301941),\n", + " ('wins', 0.03406312316656113),\n", + " ('$1M', -0.03121592104434967),\n", + " ('from', -0.02270638197660446),\n", + " ('$25', 0.012891143560409546),\n", + " ('lottery', -0.015372440218925476),\n", + " ('ticket', 0.007445111870765686)]}]" + ] + }, + "metadata": {}, + "execution_count": 29 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The `explain` method above ran an embeddings query like `search` but also analyzed each token to determine term importance. Looking at the results, it appears that `win` is the most important term. Let's visualize it." + ], + "metadata": { + "id": "ZEkXurdobRKL" + } + }, + { + "cell_type": "code", + "source": [ + "from IPython.display import HTML\n", + "\n", + "def plot(query):\n", + " result = embeddings.explain(query, limit=1)[0]\n", + "\n", + " output = f\"{query}
\"\n", + " spans = []\n", + " for token, score in result[\"tokens\"]:\n", + " color = None\n", + " if score >= 0.1:\n", + " color = \"#fdd835\"\n", + " elif score >= 0.075:\n", + " color = \"#ffeb3b\"\n", + " elif score >= 0.05:\n", + " color = \"#ffee58\"\n", + " elif score >= 0.02:\n", + " color = \"#fff59d\"\n", + "\n", + " spans.append((token, score, color))\n", + "\n", + " if result[\"score\"] >= 0.05 and not [color for _, _, color in spans if color]:\n", + " mscore = max([score for _, score, _ in spans])\n", + " spans = [(token, score, \"#fff59d\" if score == mscore else color) for token, score, color in spans]\n", + "\n", + " for token, _, color in spans:\n", + " if color:\n", + " output += f\"{token} \"\n", + " else:\n", + " output += f\"{token} \"\n", + "\n", + " return output\n", + "\n", + "HTML(plot(\"feel good story\"))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 52 + }, + "id": "klJYGOrypXUL", + "outputId": "88959716-1dcc-4fee-d784-cb398aa87eb5" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "feel good story
Maine man wins $1M from $25 lottery ticket " + ] + }, + "metadata": {}, + "execution_count": 30 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Let's try some more queries!" + ], + "metadata": { + "id": "CCHqZffacPVh" + } + }, + { + "cell_type": "code", + "source": [ + "output = \"\"\n", + "for query in [\"feel good story\", \"climate change\", \"public health story\", \"war\", \"wildlife\", \"asia\", \"lucky\", \"dishonest junk\"]:\n", + " output += plot(query) + \"

\"\n", + "\n", + "HTML(output)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 434 + }, + "id": "OOc8OfKfcpPL", + "outputId": "75541eb6-f607-42b7-c2c8-43346fa7da8f" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "feel good story
Maine man wins $1M from $25 lottery ticket

climate change
Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg

public health story
US tops 5 million confirmed virus cases

war
Beijing mobilises invasion craft along coast as Taiwan tensions escalate

wildlife
The National Park Service warns against sacrificing slower friends in a bear attack

asia
Beijing mobilises invasion craft along coast as Taiwan tensions escalate

lucky
Maine man wins $1M from $25 lottery ticket

dishonest junk
Make huge profits without work, earn up to $100,000 a day

" + ] + }, + "metadata": {}, + "execution_count": 31 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "There is also a batch method that can run bulk explainations more efficently. " + ], + "metadata": { + "id": "WOa54eJ-c9nc" + } + }, + { + "cell_type": "code", + "source": [ + "queries = [\"feel good story\", \"climate change\", \"public health story\", \"war\", \"wildlife\", \"asia\", \"lucky\", \"dishonest junk\"]\n", + "results = embeddings.batchexplain(queries, limit=1)\n", + "\n", + "for x, result in enumerate(results):\n", + " print(result)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "viuEwL4scQjx", + "outputId": "cb65e678-b694-4fe6-eb94-dea821bda643" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[{'id': '4', 'text': 'Maine man wins $1M from $25 lottery ticket', 'score': 0.08329004049301147, 'tokens': [('Maine', 0.003297939896583557), ('man', -0.03039500117301941), ('wins', 0.03406312316656113), ('$1M', -0.03121592104434967), ('from', -0.02270638197660446), ('$25', 0.012891143560409546), ('lottery', -0.015372440218925476), ('ticket', 0.007445111870765686)]}]\n", + "[{'id': '1', 'text': \"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\", 'score': 0.24478264153003693, 'tokens': [(\"Canada's\", -0.026454076170921326), ('last', 0.017057165503501892), ('fully', 0.007285907864570618), ('intact', -0.005608782172203064), ('ice', 0.009459629654884338), ('shelf', -0.029393181204795837), ('has', 0.0253918319940567), ('suddenly', 0.021642476320266724), ('collapsed,', -0.030680224299430847), ('forming', 0.01910528540611267), ('a', -0.00890059769153595), ('Manhattan-sized', -0.023612067103385925), ('iceberg', -0.009710296988487244)]}]\n", + "[{'id': '0', 'text': 'US tops 5 million confirmed virus cases', 'score': 0.1701308637857437, 'tokens': [('US', -0.02426217496395111), ('tops', -0.04896041750907898), ('5', -0.040287598967552185), ('million', -0.04737819731235504), ('confirmed', 0.02050541341304779), ('virus', 0.05511370301246643), ('cases', -0.029122650623321533)]}]\n", + "[{'id': '2', 'text': 'Beijing mobilises invasion craft along coast as Taiwan tensions escalate', 'score': 0.2714069187641144, 'tokens': [('Beijing', -0.040329575538635254), ('mobilises', -0.01986941695213318), ('invasion', 0.06464864313602448), ('craft', 0.044328778982162476), ('along', 0.021214008331298828), ('coast', -0.01738378405570984), ('as', -0.02182626724243164), ('Taiwan', -0.020671993494033813), ('tensions', -0.007258296012878418), ('escalate', -0.01663634181022644)]}]\n", + "[{'id': '3', 'text': 'The National Park Service warns against sacrificing slower friends in a bear attack', 'score': 0.28424495458602905, 'tokens': [('The', -0.022544533014297485), ('National', -0.005589812994003296), ('Park', 0.08145171403884888), ('Service', -0.016785144805908203), ('warns', -0.03266721963882446), ('against', -0.032368004322052), ('sacrificing', -0.04440906643867493), ('slower', 0.034766435623168945), ('friends', 0.0013159513473510742), ('in', -0.008420556783676147), ('a', 0.015498429536819458), ('bear', 0.08734165132045746), ('attack', -0.011731922626495361)]}]\n", + "[{'id': '2', 'text': 'Beijing mobilises invasion craft along coast as Taiwan tensions escalate', 'score': 0.24338798224925995, 'tokens': [('Beijing', -0.032770439982414246), ('mobilises', -0.04045189917087555), ('invasion', -0.0015233010053634644), ('craft', 0.017402753233909607), ('along', 0.004210904240608215), ('coast', 0.0028585344552993774), ('as', -0.0018710196018218994), ('Taiwan', 0.01866382360458374), ('tensions', -0.011064544320106506), ('escalate', -0.029331132769584656)]}]\n", + "[{'id': '4', 'text': 'Maine man wins $1M from $25 lottery ticket', 'score': 0.06539873033761978, 'tokens': [('Maine', 0.012625649571418762), ('man', -0.013015367090702057), ('wins', -0.022461198270320892), ('$1M', -0.041918568313121796), ('from', -0.02305116504430771), ('$25', -0.029282495379447937), ('lottery', 0.02279689908027649), ('ticket', -0.009147539734840393)]}]\n", + "[{'id': '5', 'text': 'Make huge profits without work, earn up to $100,000 a day', 'score': 0.033823199570178986, 'tokens': [('Make', 0.0013405345380306244), ('huge', 0.002276904881000519), ('profits', 0.02767787780612707), ('without', -0.007079385221004486), ('work,', -0.019851915538311005), ('earn', -0.026906955987215042), ('up', 0.00074811652302742), ('to', 0.007462538778781891), ('$100,000', -0.03565136343240738), ('a', -0.009965047240257263), ('day', -0.0021888017654418945)]}]\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Of course, this method is supported through YAML-based applications and the API." + ], + "metadata": { + "id": "WWH6l9nOdD-l" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai.app import Application\n", + "\n", + "app = Application(\"\"\"\n", + "writable: true\n", + "embeddings:\n", + " path: sentence-transformers/nli-mpnet-base-v2\n", + " content: true\n", + "\"\"\")\n", + "\n", + "app.add([{\"id\": uid, \"text\": text} for uid, text in enumerate(data)])\n", + "app.index()\n", + "\n", + "app.explain(\"feel good story\", limit=1)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Nwrd3v6cdKR_", + "outputId": "da949ae7-f681-4197-9df2-d5e2cb304f24" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '4',\n", + " 'score': 0.08329004049301147,\n", + " 'text': 'Maine man wins $1M from $25 lottery ticket',\n", + " 'tokens': [('Maine', 0.003297939896583557),\n", + " ('man', -0.03039500117301941),\n", + " ('wins', 0.03406312316656113),\n", + " ('$1M', -0.03121592104434967),\n", + " ('from', -0.02270638197660446),\n", + " ('$25', 0.012891143560409546),\n", + " ('lottery', -0.015372440218925476),\n", + " ('ticket', 0.007445111870765686)]}]" + ] + }, + "metadata": {}, + "execution_count": 33 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Pipeline models\n", + "\n", + "txtai pipelines are wrappers around Hugging Face pipelines with logic to easily integrate with txtai's workflow framework. Given that, we can use the [SHAP](https://github.com/slundberg/shap) library to explain predictions.\n", + "\n", + "Let's try a sentiment analysis example." + ], + "metadata": { + "id": "RsRuLWYpdup_" + } + }, + { + "cell_type": "code", + "source": [ + "import shap\n", + "\n", + "from txtai.pipeline import Labels\n", + "\n", + "data = [\"Dodgers lose again, give up 3 HRs in a loss to the Giants\",\n", + " \"Massive dunk!!! they are now up by 15 with 2 minutes to go\"]\n", + "\n", + "labels = Labels(dynamic=False)\n", + "\n", + "# explain the model on two sample inputs\n", + "explainer = shap.Explainer(labels.pipeline) \n", + "shap_values = explainer(data)" + ], + "metadata": { + "id": "SnoPSv1-fxvF" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "shap.plots.text(shap_values[0, :, \"NEGATIVE\"])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 118 + }, + "id": "YW_qbzPKpRdL", + "outputId": "c21b3558-feed-4854-9444-954f87aa8422" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "0.80.70.60.50.40.911.10.5677350.567735base value0.995090.99509fNEGATIVE(inputs)0.192 loss 0.143 lose 0.082 up 0.059 Dodgers 0.054 , 0.051 again 0.017 a 0.009 HR 0.007 s -0.108 Giants -0.021 give -0.02 the -0.018 3 -0.012 to -0.009 in -0.0
inputs
0.0
0.059
Dodgers
0.143
lose
0.051
again
0.054
,
-0.021
give
0.082
up
-0.018
3
0.009
HR
0.007
s
-0.009
in
0.017
a
0.192
loss
-0.012
to
-0.02
the
-0.108
Giants
-0.0
" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "shap.plots.text(shap_values[1, :, \"NEGATIVE\"])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 118 + }, + "id": "SU2hHRjCpc_I", + "outputId": "83e1ada5-de45-426a-ae64-dae5b66b8e6a" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "0.30-0.30.60.90.538360.53836base value00fNEGATIVE(inputs)0.199 Massive 0.171 minutes 0.045 ! 0.029 ! 0.025 by 0.024 ! 0.016 15 0.008 to 0.007 2 -0.392 k -0.184 up -0.123 go -0.121 dun -0.118 they -0.052 are -0.049 now -0.024 with
inputs
0.0
0.199
Massive
-0.121
dun
-0.392
k
0.045
!
0.024
!
0.029
!
-0.118
they
-0.052
are
-0.049
now
-0.184
up
0.025
by
0.016
15
-0.024
with
0.007
2
0.171
minutes
0.008
to
-0.123
go
0.0
" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The [SHAP documentation](https://shap.readthedocs.io/en/latest/text_examples.html) provides a great list of additional examples for translation, text generation, summarization, translation and question-answering.\n", + "\n", + "The SHAP library is pretty 🔥🔥🔥 Check it out for more!" + ], + "metadata": { + "id": "8atTIz37iP9Q" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "This notebook briefly introduced model explainability. There is a lot of work in this area, expect a number of different methods to become available. Model explainability helps users gain a level of trust in model predictions. It also helps debug why a model is making a decision, which can potentially drive how to fine-tune a model to make better predictions. \n", + "\n", + "Keep an eye on this important area over the coming months!\n" + ], + "metadata": { + "id": "YujdAMlGh0qT" + } + } + ] +} \ No newline at end of file diff --git a/examples/33_Query_translation.ipynb b/examples/33_Query_translation.ipynb new file mode 100644 index 0000000..44b05f9 --- /dev/null +++ b/examples/33_Query_translation.ipynb @@ -0,0 +1,508 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "POWZoSJR6XzK" + }, + "source": [ + "# Query translation\n", + "\n", + "txtai supports two main types of queries: natural language statements and SQL statements. Natural language queries handles a search engine like query. SQL statements enable more complex filtering, sorting and column selection. Query translation bridges the gap between the two and enables filtering for natural language queries.\n", + "\n", + "For example, the query:\n", + "\n", + "```\n", + "Tell me a feel good story since yesterday\n", + "```\n", + "\n", + "becomes\n", + "\n", + "```sql\n", + "select * from txtai where similar(\"Tell me a feel good story\") and\n", + "entry >= date('now', '-1 day')\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qa_PPKVX6XzN" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "_cell_guid": "b1076dfc-b9ad-4769-8c92-a6c4dae69d19", + "_kg_hide-output": true, + "_uuid": "8f2839f25d086af736a60e9eeb907d3b93b6e0e5", + "id": "24q-1n5i6XzQ", + "trusted": true + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0p3WCDniUths" + }, + "source": [ + "# Create index\n", + "Let's first recap how to create an index. We'll use the classic txtai example.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "_cell_guid": "79c7e3d0-c299-4dcb-8224-4455121ee9b0", + "_uuid": "d629ff2d2480ee46fbb7e2d37f6b5fab8052498a", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "2j_CFGDR6Xzp", + "outputId": "e65238c5-d67f-4fe4-9cbf-c88b0ff3a8bb", + "trusted": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'id': '4',\n", + " 'text': 'Maine man wins $1M from $25 lottery ticket',\n", + " 'score': 0.08329025655984879}]" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from txtai import Embeddings\n", + "\n", + "data = [\"US tops 5 million confirmed virus cases\",\n", + " \"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\",\n", + " \"Beijing mobilises invasion craft along coast as Taiwan tensions escalate\",\n", + " \"The National Park Service warns against sacrificing slower friends in a bear attack\",\n", + " \"Maine man wins $1M from $25 lottery ticket\",\n", + " \"Make huge profits without work, earn up to $100,000 a day\"]\n", + "\n", + "# Create embeddings index with content enabled. The default behavior is to only store indexed vectors.\n", + "embeddings = Embeddings(path=\"sentence-transformers/nli-mpnet-base-v2\", content=True)\n", + "\n", + "# Create an index for the list of text\n", + "embeddings.index(data)\n", + "\n", + "# Run a search\n", + "embeddings.search(\"feel good story\", 1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "QTee7YMNDD4R" + }, + "source": [ + "# Query translation models\n", + "\n", + "Next we'll explore how query translation models work with examples. " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "04iOgP_ojSfK", + "outputId": "41e73130-75e6-4ae9-b690-685972a13565" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Input: feel good story\n", + "SQL: select id, text, score from txtai where similar('feel good story')\n", + "\n", + "Input: feel good story since yesterday\n", + "SQL: select id, text, score from txtai where similar('feel good story') and entry >= date('now', '-1 day')\n", + "\n", + "Input: feel good story with 'lottery' in text\n", + "SQL: select id, text, score from txtai where similar('feel good story') and text like '%lottery%'\n", + "\n", + "Input: how many feel good story\n", + "SQL: select count(*) from txtai where similar('feel good story')\n", + "\n", + "Input: feel good story translated to fr\n", + "SQL: select id, translate(text, 'fr') text, score from txtai where similar('feel good story')\n", + "\n", + "Input: feel good story summarized\n", + "SQL: select id, summary(text) text, score from txtai where similar('feel good story')\n", + "\n" + ] + } + ], + "source": [ + "from txtai import LLM\n", + "\n", + "llm = LLM(\"NeuML/t5-small-txtsql\", template=\"translate English to SQL: {text}\")\n", + "\n", + "queries = [\n", + " \"feel good story\",\n", + " \"feel good story since yesterday\",\n", + " \"feel good story with 'lottery' in text\",\n", + " \"how many feel good story\",\n", + " \"feel good story translated to fr\",\n", + " \"feel good story summarized\"\n", + "]\n", + "\n", + "for query in queries:\n", + " print(f\"Input: {query}\")\n", + " print(f\"SQL: {llm(query)}\")\n", + " print()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rAnEMaiWlOXm" + }, + "source": [ + "Looking at the query translations above gives an idea on how this model works.\n", + "\n", + "[t5-small-txtsql](https://huggingface.co/NeuML/t5-small-txtsql) is the default model. Custom domain query syntax languages can be created using this same methodology, including for other languages. Natural language can be translated to functions, query clauses, column selection and more!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "P9hOcgNfjSyL" + }, + "source": [ + "# Natural language filtering\n", + "\n", + "Now it's time for this in action! Let's first initialize the embeddings index with the appropriate settings." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "L2DDJrd0RAaN", + "outputId": "c60bfc29-187b-4926-8656-04063b2ca85c" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'id': '4',\n", + " 'score': 0.08329025655984879,\n", + " 'text': 'Maine Mann gewinnt $1M von $25 Lotterie-Ticket'}" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from txtai.pipeline import Translation\n", + "\n", + "def translate(text, lang):\n", + " return translation(text, lang)\n", + "\n", + "translation = Translation()\n", + "\n", + "# Create embeddings index with content enabled. The default behavior is to only store indexed vectors.\n", + "embeddings = Embeddings(\n", + " path=\"sentence-transformers/nli-mpnet-base-v2\",\n", + " content=True,\n", + " query={\"path\": \"NeuML/t5-small-txtsql\"},\n", + " functions=[translate]\n", + ")\n", + "\n", + "# Create an index for the list of text\n", + "embeddings.index(data)\n", + "\n", + "query = \"select id, score, translate(text, 'de') 'text' from txtai where similar('feel good story')\"\n", + "\n", + "# Run a search using a custom SQL function\n", + "embeddings.search(query)[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "MNd7QmFmnh-f" + }, + "source": [ + "Note how the query model was provided as a embeddings index configuration parameter. Custom SQL functions were also added in. Let's now see if the same SQL statement can be run with a natural language query." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "mnGSBNMonur2", + "outputId": "dc14d898-b46f-42e6-88e0-434863cc15d6" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'id': '4',\n", + " 'text': 'Maine Mann gewinnt $1M von $25 Lotterie-Ticket',\n", + " 'score': 0.08329025655984879}" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "embeddings.search(\"feel good story translated to de\")[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "OnmVUK2DoZkh" + }, + "source": [ + "Same result. Let's try a few more." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Js2b_M61oitg", + "outputId": "fab080cb-d082-4cf3-8314-12d23acc3c02" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'id': '4',\n", + " 'text': 'Maine man wins $1M from $25 lottery ticket',\n", + " 'score': 0.08329025655984879}" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "embeddings.search(\"feel good story since yesterday\")[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "pcxPgriwomWv", + "outputId": "7e71dd09-7c4f-4147-ba9e-47a45c1cf90f" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'id': '4',\n", + " 'text': 'Maine man wins $1M from $25 lottery ticket',\n", + " 'score': 0.08329025655984879}]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "embeddings.search(\"feel good story with 'lottery' in text\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1ob4Q_CLpGBm" + }, + "source": [ + "For good measure, a couple queries with filters that return no results." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "LMaf7JJzonAC", + "outputId": "71080bd1-2d9e-41ba-9501-2cdedaf26d6d" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "embeddings.search(\"feel good story with missing in text\")" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ADFNLhvto_0J", + "outputId": "12e08ea8-7203-4fb0-cb84-1133b5db9dc0" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "embeddings.search(\"feel good story with field equal 14\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mTT8nopiRdVH" + }, + "source": [ + "# Query translation with applications\n", + "\n", + "Of course this is all available with YAML-configured applications." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "FZ_7G6M4RUbz", + "outputId": "b00ce1c2-8df2-4289-8d03-991174a0e74f" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'id': '4',\n", + " 'text': 'Maine Mann gewinnt $1M von $25 Lotterie-Ticket',\n", + " 'score': 0.08329025655984879}" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "config = \"\"\"\n", + "translation:\n", + "\n", + "writable: true\n", + "embeddings:\n", + " path: sentence-transformers/nli-mpnet-base-v2\n", + " content: true\n", + " query:\n", + " path: NeuML/t5-small-txtsql\n", + " functions:\n", + " - {name: translate, argcount: 2, function: translation}\n", + "\"\"\"\n", + "\n", + "from txtai.app import Application\n", + "\n", + "# Build application and index data\n", + "app = Application(config)\n", + "app.add([{\"id\": x, \"text\": row} for x, row in enumerate(data)])\n", + "app.index()\n", + "\n", + "# Run search query\n", + "app.search(\"feel good story translated to de\")[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "aDIF3tYt6X0O" + }, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook introduced natural language filtering with query translation models. This powerful feature adds filtering and pipelines to natural language statements. Custom domain-specific query languages can be created to enable rich queries natively in txtai." + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "local", + "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", + "version": "3.10.19" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/examples/34_Build_a_QA_database.ipynb b/examples/34_Build_a_QA_database.ipynb new file mode 100644 index 0000000..5d5cb24 --- /dev/null +++ b/examples/34_Build_a_QA_database.ipynb @@ -0,0 +1,455 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "kernelspec": { + "name": "python3", + "display_name": "Python 3", + "language": "python" + }, + "language_info": { + "name": "python", + "version": "3.7.6", + "mimetype": "text/x-python", + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "pygments_lexer": "ipython3", + "nbconvert_exporter": "python", + "file_extension": ".py" + }, + "colab": { + "name": "34 - Build a QA database", + "provenance": [], + "collapsed_sections": [] + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "POWZoSJR6XzK" + }, + "source": [ + "# Build a QA database\n", + "\n", + "Conversational AI is a growing field that could potentially automate much of the customer service industry. Full automation is still a ways away (most of us have been on a call with an automated agent and just want to get to a person) but it certainly can be a solid first line before human intervention.\n", + "\n", + "This notebook presents a process to answer user questions using a txtai embeddings instance. It's not conversational AI but instead looks to find the closest existing question to a user question. This is useful in cases where there are a list of frequently asked questions. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qa_PPKVX6XzN" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "metadata": { + "_uuid": "8f2839f25d086af736a60e9eeb907d3b93b6e0e5", + "_cell_guid": "b1076dfc-b9ad-4769-8c92-a6c4dae69d19", + "trusted": true, + "_kg_hide-output": true, + "id": "24q-1n5i6XzQ" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai datasets" + ], + "execution_count": 75, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Load the dataset\n", + "\n", + "We'll use a Hugging Face dataset of web questions for this example. The dataset has a list of questions and answers. The code below loads the dataset and prints a couple examples to get an idea of how the data is formatted." + ], + "metadata": { + "id": "QAEVXfOiVzEB" + } + }, + { + "cell_type": "code", + "source": [ + "from datasets import load_dataset\n", + "\n", + "ds = load_dataset(\"web_questions\", split=\"train\")\n", + "\n", + "for row in ds.select(range(5)):\n", + " print(row[\"question\"], row[\"answers\"])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "koM4vYHXL82P", + "outputId": "91926a1b-8b9c-46be-f450-f76a1e3aab82" + }, + "execution_count": 76, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "what is the name of justin bieber brother? ['Jazmyn Bieber', 'Jaxon Bieber']\n", + "what character did natalie portman play in star wars? ['Padmé Amidala']\n", + "what state does selena gomez? ['New York City']\n", + "what country is the grand bahama island in? ['Bahamas']\n", + "what kind of money to take to bahamas? ['Bahamian dollar']\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Create index\n", + "\n", + "Next, we'll create a txtai index. The question will be the indexed text. We'll also store full content so we can access the answer at query time.\n" + ], + "metadata": { + "id": "0p3WCDniUths" + } + }, + { + "cell_type": "code", + "metadata": { + "_uuid": "d629ff2d2480ee46fbb7e2d37f6b5fab8052498a", + "_cell_guid": "79c7e3d0-c299-4dcb-8224-4455121ee9b0", + "trusted": true, + "id": "2j_CFGDR6Xzp" + }, + "source": [ + "from txtai.embeddings import Embeddings\n", + "\n", + "# Create embeddings index with content enabled. The default behavior is to only store indexed vectors.\n", + "embeddings = Embeddings({\"path\": \"sentence-transformers/nli-mpnet-base-v2\", \"content\": True})\n", + "\n", + "# Map question to text and store content\n", + "embeddings.index([(uid, {\"url\": row[\"url\"], \"text\": row[\"question\"], \"answer\": \", \".join(row[\"answers\"])}, None) for uid, row in enumerate(ds)])" + ], + "execution_count": 77, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Asking questions\n", + "\n", + "Now that the index is built, let's ask some questions! We'll use txtai SQL to select the fields we want to return.\n", + "\n", + "See the list of questions asked and best matching question-answer combo." + ], + "metadata": { + "id": "dqx-PmpzWMez" + } + }, + { + "cell_type": "code", + "source": [ + "def question(text):\n", + " return embeddings.search(f\"select text, answer, score from txtai where similar('{text}') limit 1\")\n", + "\n", + "question(\"What is the timezone of NYC?\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Gatp4mDQPA_v", + "outputId": "93efaec7-c41e-4865-f042-371c55170ffe" + }, + "execution_count": 78, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'answer': 'North American Eastern Time Zone',\n", + " 'score': 0.8904051184654236,\n", + " 'text': 'what time zone is new york under?'}]" + ] + }, + "metadata": {}, + "execution_count": 78 + } + ] + }, + { + "cell_type": "code", + "source": [ + "question(\"Things to do in New York\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "WUvdAvvlSPDT", + "outputId": "00ed87fe-b659-406e-d03a-97b7b5ea9773" + }, + "execution_count": 79, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'answer': \"Chelsea Art Museum, Brooklyn Bridge, Empire State Building, The Broadway Theatre, American Museum of Natural History, Central Park, St. Patrick's Cathedral, Japan Society of New York, FusionArts Museum, American Folk Art Museum\",\n", + " 'score': 0.8308358192443848,\n", + " 'text': 'what are some places to visit in new york?'}]" + ] + }, + "metadata": {}, + "execution_count": 79 + } + ] + }, + { + "cell_type": "code", + "source": [ + "question(\"Microsoft founder\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "vvsSj2qKRrrY", + "outputId": "1c4a94d7-3bf6-4cae-8b40-13fbfef35205" + }, + "execution_count": 80, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'answer': 'Bill Gates',\n", + " 'score': 0.6617322564125061,\n", + " 'text': 'who created microsoft windows?'}]" + ] + }, + "metadata": {}, + "execution_count": 80 + } + ] + }, + { + "cell_type": "code", + "source": [ + "question(\"Apple founder university\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "UUD56XSRR1jc", + "outputId": "fb94e89d-1580-471b-9e0e-541e5208d937" + }, + "execution_count": 81, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'answer': 'Reed College',\n", + " 'score': 0.5137897729873657,\n", + " 'text': 'what college did steve jobs attend?'}]" + ] + }, + "metadata": {}, + "execution_count": 81 + } + ] + }, + { + "cell_type": "code", + "source": [ + "question(\"What country uses the Yen?\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "D6Ur0wZWSBfd", + "outputId": "caf6e0aa-af62-4f33-fed1-6e1a111fed1a" + }, + "execution_count": 82, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'answer': 'Japanese yen',\n", + " 'score': 0.6663530468940735,\n", + " 'text': 'what money do japanese use?'}]" + ] + }, + "metadata": {}, + "execution_count": 82 + } + ] + }, + { + "cell_type": "code", + "source": [ + "question(\"Show me a list of Pixar movies\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "2POCUWrqSKGP", + "outputId": "f8069bf4-a135-47df-a571-198f168c03fb" + }, + "execution_count": 83, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'answer': \"A Bug's Life, Toy Story 2, Ratatouille, Cars, Up, Toy Story, Monsters, Inc., The Incredibles, Finding Nemo, WALL-E\",\n", + " 'score': 0.653051495552063,\n", + " 'text': 'what does pixar produce?'}]" + ] + }, + "metadata": {}, + "execution_count": 83 + } + ] + }, + { + "cell_type": "code", + "source": [ + "question(\"What is the timezone of Florida?\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "xll4a1ChTaVg", + "outputId": "00cb967f-753e-4d15-cb87-308c08dfde59" + }, + "execution_count": 84, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'answer': 'North American Eastern Time Zone',\n", + " 'score': 0.9672279357910156,\n", + " 'text': 'where is the time zone in florida?'}]" + ] + }, + "metadata": {}, + "execution_count": 84 + } + ] + }, + { + "cell_type": "code", + "source": [ + "question(\"Tell me an animal found offshore in Florida\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "4EyAOhWXUcKe", + "outputId": "ef3f83b1-9314-4458-843e-16afb3364cd9" + }, + "execution_count": 85, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'answer': 'Largemouth bass',\n", + " 'score': 0.6526554822921753,\n", + " 'text': 'what kind of fish do you catch in florida?'}]" + ] + }, + "metadata": {}, + "execution_count": 85 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Not too bad! This database only has over 6,000 question-answer pairs. To improve quality a score filter could be put on the query to only return highly confident answers. But this gives an idea of what is possible." + ], + "metadata": { + "id": "KFxsjtsnWgpe" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Run as an application\n", + "\n", + "This can also be run as an application. See below." + ], + "metadata": { + "id": "2x9awoKNZfZg" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai.app import Application\n", + "\n", + "# Save index\n", + "embeddings.save(\"questions.tar.gz\")\n", + "\n", + "# Build application and index data\n", + "app = Application(\"path: questions.tar.gz\")\n", + "\n", + "# Run search query\n", + "app.search(\"select text, answer, score from txtai where similar('Tell me an animal found offshore in Florida') limit 1\")[0]" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "0lH9cf1bZokt", + "outputId": "77afcb03-c834-46c9-98f2-b1a3c34c0e3b" + }, + "execution_count": 86, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "{'answer': 'Largemouth bass',\n", + " 'score': 0.6526554822921753,\n", + " 'text': 'what kind of fish do you catch in florida?'}" + ] + }, + "metadata": {}, + "execution_count": 86 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "aDIF3tYt6X0O" + }, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook introduced a simple question matching service. This could be the foundation of an automated customer service agent and/or an online FAQ.\n", + "\n", + "For a full example, see [codequestion](https://github.com/neuml/codequestion), which is an application that matches user questions to Stack Overflow question-answer pairs." + ] + } + ] +} diff --git a/examples/35_Pictures_are_worth_a_thousand_words.ipynb b/examples/35_Pictures_are_worth_a_thousand_words.ipynb new file mode 100644 index 0000000..9d0d519 --- /dev/null +++ b/examples/35_Pictures_are_worth_a_thousand_words.ipynb @@ -0,0 +1,846 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "euK-7TR3JAxR" + }, + "source": [ + "# Pictures are worth a thousand words\n", + "\n", + "One of the hottest🔥 models as of June 2022 is [DALL-E mini](https://wandb.ai/dalle-mini/dalle-mini/reports/DALL-E-mini-Generate-images-from-any-text-prompt--VmlldzoyMDE4NDAy). There are a number of projects and examples utilizing this model as seen [here](https://huggingface.co/spaces/dalle-mini/dalle-mini), [here](https://github.com/borisdayma/dalle-mini) and [here](https://github.com/kuprel/min-dalle). There have even been mainstream news articles covering this model.\n", + "\n", + "This notebook presents a workflow to build a summary of a webpage and then generate an image of this summary. While there are a number of potential use cases for text to image models, this example is focused on showing the power of workflows and also provides interesting insights into how DALL-E mini \"sees\" the world." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "JkuX4kZqNopU" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies. \n", + "\n", + "All credit for the DALL-E mini models and code goes to https://github.com/borisdayma/dalle-mini and https://github.com/kuprel/min-dalle." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ix_xt4X1_6F4" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai tika min-dalle ipyplot" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "y5ntMa8gOP05" + }, + "source": [ + "# Build a DALL-E pipeline\n", + "\n", + "Let's first construct a txtai pipeline that generates images using DALL-E mini." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "uDHUDqjptyzA" + }, + "outputs": [], + "source": [ + "from min_dalle import MinDalle\n", + "\n", + "from txtai.pipeline import Pipeline\n", + "\n", + "class Dalle(Pipeline):\n", + " def __init__(self):\n", + " self.model = MinDalle(is_mega=False, is_verbose=False)\n", + "\n", + " def __call__(self, texts, seed, prefix):\n", + " results = []\n", + " for text in texts:\n", + " text = prefix + text\n", + " results.append(self.model.generate_image(text, seed))\n", + "\n", + " return results\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GRNkDcg0yrdS" + }, + "source": [ + "# Build DALL-E workflow\n", + "\n", + "Next we'll define a txtai workflow as YAML. This workflow extracts text at a specified URL, builds a summary and then generates an image for the summary text. \n", + "\n", + "This workflow can be run from Python as shown below or as a [API service](https://neuml.github.io/txtai/api/)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nQ0UG05dA4p2" + }, + "outputs": [], + "source": [ + "from txtai.app import Application\n", + "\n", + "app = Application(\"\"\"\n", + "__main__.Dalle:\n", + "summary:\n", + " path: sshleifer/distilbart-cnn-12-6\n", + "textractor:\n", + " join: true\n", + " lines: false\n", + " minlength: 100\n", + " paragraphs: true\n", + " sentences: false\n", + "workflow:\n", + " draw:\n", + " tasks:\n", + " - action: textractor\n", + " task: url\n", + " - action: summary\n", + " args: [0, 60, 0]\n", + " - action: __main__.Dalle\n", + " args: [1024, \"Illustration of \"]\n", + "\"\"\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "B_cPRgZo3-4M" + }, + "source": [ + "# Generate webpage summary images\n", + "\n", + "Now that the workflow is up, let's generate some images! The following example generates images for a set of Wikipedia articles. Give this a try for articles, recipes or any other descriptive web page.\n", + "\n", + "It's also fun to generate images for random Wikipedia pages using this url: https://en.wikipedia.org/wiki/Special:Random" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "smtdwwtDqfHa", + "outputId": "22e25121-4ed2-45a0-9579-797f06247633" + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2022-07-03 02:41:15,891 [MainThread ] [INFO ] Retrieving https://en.wikipedia.org/wiki/Catholic_Church to /tmp/wiki-catholic_church.\n", + "2022-07-03 02:41:15,891 [INFO] getRemoteFile: Retrieving https://en.wikipedia.org/wiki/Catholic_Church to /tmp/wiki-catholic_church.\n", + "2022-07-03 02:41:16,293 [MainThread ] [INFO ] Retrieving https://en.wikipedia.org/wiki/Magic_Kingdom to /tmp/wiki-magic_kingdom.\n", + "2022-07-03 02:41:16,293 [INFO] getRemoteFile: Retrieving https://en.wikipedia.org/wiki/Magic_Kingdom to /tmp/wiki-magic_kingdom.\n", + "2022-07-03 02:41:16,391 [MainThread ] [INFO ] Retrieving https://en.wikipedia.org/wiki/Epcot to /tmp/wiki-epcot.\n", + "2022-07-03 02:41:16,391 [INFO] getRemoteFile: Retrieving https://en.wikipedia.org/wiki/Epcot to /tmp/wiki-epcot.\n", + "2022-07-03 02:41:16,462 [MainThread ] [INFO ] Retrieving https://en.wikipedia.org/wiki/Mountain to /tmp/wiki-mountain.\n", + "2022-07-03 02:41:16,462 [INFO] getRemoteFile: Retrieving https://en.wikipedia.org/wiki/Mountain to /tmp/wiki-mountain.\n", + "2022-07-03 02:41:16,518 [MainThread ] [INFO ] Retrieving https://en.wikipedia.org/wiki/Tundra to /tmp/wiki-tundra.\n", + "2022-07-03 02:41:16,518 [INFO] getRemoteFile: Retrieving https://en.wikipedia.org/wiki/Tundra to /tmp/wiki-tundra.\n", + "2022-07-03 02:41:16,582 [MainThread ] [INFO ] Retrieving https://en.wikipedia.org/wiki/War_of_1812 to /tmp/wiki-war_of_1812.\n", + "2022-07-03 02:41:16,582 [INFO] getRemoteFile: Retrieving https://en.wikipedia.org/wiki/War_of_1812 to /tmp/wiki-war_of_1812.\n", + "2022-07-03 02:41:18,738 [MainThread ] [INFO ] Retrieving https://en.wikipedia.org/wiki/Chinese_Cuisine to /tmp/wiki-chinese_cuisine.\n", + "2022-07-03 02:41:18,738 [INFO] getRemoteFile: Retrieving https://en.wikipedia.org/wiki/Chinese_Cuisine to /tmp/wiki-chinese_cuisine.\n", + "2022-07-03 02:41:18,799 [MainThread ] [INFO ] Retrieving https://en.wikipedia.org/wiki/Indian_Cuisine to /tmp/wiki-indian_cuisine.\n", + "2022-07-03 02:41:18,799 [INFO] getRemoteFile: Retrieving https://en.wikipedia.org/wiki/Indian_Cuisine to /tmp/wiki-indian_cuisine.\n", + "2022-07-03 02:41:18,944 [MainThread ] [INFO ] Retrieving https://en.wikipedia.org/wiki/Italian_Cuisine to /tmp/wiki-italian_cuisine.\n", + "2022-07-03 02:41:18,944 [INFO] getRemoteFile: Retrieving https://en.wikipedia.org/wiki/Italian_Cuisine to /tmp/wiki-italian_cuisine.\n", + "2022-07-03 02:41:19,100 [MainThread ] [INFO ] Retrieving https://en.wikipedia.org/wiki/Romanian_cuisine to /tmp/wiki-romanian_cuisine.\n", + "2022-07-03 02:41:19,100 [INFO] getRemoteFile: Retrieving https://en.wikipedia.org/wiki/Romanian_cuisine to /tmp/wiki-romanian_cuisine.\n", + "2022-07-03 02:41:19,172 [MainThread ] [INFO ] Retrieving https://en.wikipedia.org/wiki/Football to /tmp/wiki-football.\n", + "2022-07-03 02:41:19,172 [INFO] getRemoteFile: Retrieving https://en.wikipedia.org/wiki/Football to /tmp/wiki-football.\n", + "2022-07-03 02:41:19,524 [MainThread ] [INFO ] Retrieving https://en.wikipedia.org/wiki/Artificial_Intelligence to /tmp/wiki-artificial_intelligence.\n", + "2022-07-03 02:41:19,524 [INFO] getRemoteFile: Retrieving https://en.wikipedia.org/wiki/Artificial_Intelligence to /tmp/wiki-artificial_intelligence.\n", + "2022-07-03 02:41:19,706 [MainThread ] [INFO ] Retrieving https://en.wikipedia.org/wiki/Galaxy to /tmp/wiki-galaxy.\n", + "2022-07-03 02:41:19,706 [INFO] getRemoteFile: Retrieving https://en.wikipedia.org/wiki/Galaxy to /tmp/wiki-galaxy.\n", + "2022-07-03 02:41:19,808 [MainThread ] [INFO ] Retrieving https://en.wikipedia.org/wiki/Fjord to /tmp/wiki-fjord.\n", + "2022-07-03 02:41:19,808 [INFO] getRemoteFile: Retrieving https://en.wikipedia.org/wiki/Fjord to /tmp/wiki-fjord.\n", + "2022-07-03 02:41:19,859 [MainThread ] [INFO ] Retrieving https://en.wikipedia.org/wiki/Island to /tmp/wiki-island.\n", + "2022-07-03 02:41:19,859 [INFO] getRemoteFile: Retrieving https://en.wikipedia.org/wiki/Island to /tmp/wiki-island.\n" + ] + } + ], + "source": [ + "%%capture\n", + "# Build list of Wikipedia article URLs\n", + "captions = [\"Catholic Church\", \"Magic Kingdom\", \"Epcot\", \"Mountain\", \"Tundra\", \"War of 1812\", \"Chinese Cuisine\", \"Indian Cuisine\", \"Italian Cuisine\",\n", + " \"Romanian cuisine\", \"Football\", \"Artificial Intelligence\", \"Galaxy\", \"Fjord\", \"Island\"]\n", + "urls = [f\"https://en.wikipedia.org/wiki/{url.replace(' ', '_')}\" for url in captions]\n", + "\n", + "# Run workflow to generate images with DALL-E mini\n", + "images = list(app.workflow(\"draw\", urls))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "background_save": true, + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "hbnHb9cXyoLj", + "outputId": "f7f4ea56-9c88-4dd1-9d0f-757ba4b28422" + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + " \n", + "
\n", + "
\n", + "
\n", + "

Catholic Church

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Magic Kingdom

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Epcot

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Mountain

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Tundra

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

War of 1812

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Chinese Cuisine

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Indian Cuisine

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Italian Cuisine

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Romanian cuisine

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Football

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Artificial Intelligence

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Galaxy

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Fjord

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

Island

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import ipyplot\n", + "ipyplot.plot_images(images, captions, img_width=256)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DtWAsOf-4skI" + }, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook walked through an example on how to build a txtai workflow to generate webpage summary images. DALL-E mini is a fascinating model and it's a lot of fun to \"see\" how the model works. Give it a try yourself!" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "collapsed_sections": [], + "name": "35 - Pictures are worth a thousand words", + "provenance": [] + }, + "gpuClass": "standard", + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/examples/36_Run_txtai_in_native_code.ipynb b/examples/36_Run_txtai_in_native_code.ipynb new file mode 100644 index 0000000..e548612 --- /dev/null +++ b/examples/36_Run_txtai_in_native_code.ipynb @@ -0,0 +1,728 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "gpuClass": "standard", + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Run txtai in native code\n", + "\n", + "txtai currently has two main methods of execution: Python or via a HTTP API. There are API bindings for [JavaScript](https://github.com/neuml/txtai.js), [Java](https://github.com/neuml/txtai.java), [Rust](https://github.com/neuml/txtai.rs) and [Go](https://github.com/neuml/txtai.go).\n", + "\n", + "This notebook presents a way to run txtai as part of a native executable with the [Python C API](https://docs.python.org/3/c-api/index.html). We'll run an example in C and even call txtai from assembly code!\n", + "\n", + "Before diving into this notebook, it's important to emphasize that connecting to txtai via the HTTP API has a number of major advantages. This includes decoupling from Python, the ability to offload txtai to a different machine and scaling with cloud compute. With that being said, this notebook demonstrates an additional way to integrate txtai along with providing an informative and perhaps academic programming exercise." + ], + "metadata": { + "id": "-xU9P9iSR-Cy" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ], + "metadata": { + "id": "shlUi2kKS7KT" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "xEvX9vCpn4E0" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline] sacremoses\n", + "\n", + "# Remove tensorflow as it's not used and prints noisy log messages\n", + "!pip uninstall -y tensorflow\n", + "\n", + "# Install python3.7-dev and nasm\n", + "!apt-get install python3.7-dev nasm" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Workflow configuration\n", + "\n", + "This configuration builds a workflow to translate input text to French. More information on workflows can be found in [txtai's documentation](https://neuml.github.io/txtai/workflow).\n" + ], + "metadata": { + "id": "AtEdP7Utw3mk" + } + }, + { + "cell_type": "code", + "source": [ + "%%writefile config.yml\n", + "summary:\n", + " path: sshleifer/distilbart-cnn-12-6\n", + "\n", + "textractor:\n", + " join: true\n", + " lines: false\n", + " minlength: 100\n", + " paragraphs: true\n", + " sentences: false\n", + " tika: false\n", + "\n", + "translation:\n", + "\n", + "workflow:\n", + " summary:\n", + " tasks:\n", + " - action: textractor\n", + " task: url\n", + " - action: summary\n", + "\n", + " translate:\n", + " tasks:\n", + " - action: translation\n", + " args: \n", + " - fr" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "DPWrubv5oOn7", + "outputId": "bdfba131-c36e-421d-8c17-a726be4615d2" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Writing config.yml\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Python C API\n", + "\n", + "Next we'll build an interface to txtai workflows with the Python C API. This logic will load Python, create a txtai application instance and add methods to run workflows.\n", + "\n", + "Some assumptions are made:\n", + "\n", + "- txtai is installed and available\n", + "- A workflow is available in a file named `config.yml`\n", + "- The workflow only returns the first element\n", + "\n", + "These assumptions are for brevity. This example could be expanded on and built into a more robust, full-fledged library.\n", + "\n", + "While this example is in C, Rust has a well-maintained and popular library for interfacing with Python, [PyO3](https://github.com/PyO3/pyo3). Interfacing with the Python C API is also possible in Java, JavaScript and Go but not as straighforward." + ], + "metadata": { + "id": "KIg_QDVRo-6-" + } + }, + { + "cell_type": "code", + "source": [ + "%%writefile workflow.c\n", + "#include \n", + "\n", + "// Global instances\n", + "PyObject *module = NULL, *app = NULL;\n", + "\n", + "/**\n", + " * Create txtai module.\n", + " */\n", + "PyObject* txtai() {\n", + " PyObject* module = NULL;\n", + " module = PyImport_ImportModule(\"txtai.app\");\n", + " return module;\n", + "}\n", + "\n", + "/**\n", + " * Create txtai application instance.\n", + " */\n", + "PyObject* application() {\n", + " PyObject* app = NULL;\n", + " app = PyObject_CallMethod(module, \"Application\", \"z\", \"config.yml\");\n", + " return app;\n", + "}\n", + "\n", + "/**\n", + " * Run txtai workflow.\n", + " */\n", + "PyObject* run(char** args) {\n", + " PyObject* result = NULL;\n", + " result = PyObject_CallMethod(app, \"workflow\", \"z[z]\", args[0], args[1]);\n", + " return result;\n", + "}\n", + "\n", + "/**\n", + " * Cleanup Python objects.\n", + " */\n", + "void cleanup() {\n", + " // Ensure Python instance exists\n", + " if (Py_IsInitialized()) {\n", + " PyErr_Print();\n", + "\n", + " Py_CLEAR(app);\n", + " Py_CLEAR(module);\n", + "\n", + " Py_FinalizeEx();\n", + " }\n", + "}\n", + "\n", + "/**\n", + " * Initialize a txtai application and run a workflow.\n", + " */\n", + "const char* workflow(char** args) {\n", + " PyObject* result = NULL;\n", + "\n", + " // Create application instance if it doesn't already exist\n", + " if (!Py_IsInitialized()) {\n", + " // Start Python Interpreter\n", + " Py_Initialize();\n", + "\n", + " // Create txtai module\n", + " module = txtai();\n", + "\n", + " // Handle errors\n", + " if (!module) {\n", + " cleanup();\n", + " return NULL;\n", + " }\n", + "\n", + " // Create txtai application\n", + " app = application();\n", + "\n", + " // Handle errors\n", + " if (!app) {\n", + " cleanup();\n", + " return NULL;\n", + " }\n", + " }\n", + "\n", + " // Run workflow\n", + " result = run(args);\n", + "\n", + " // Handle errors\n", + " if (!result) {\n", + " cleanup();\n", + " return NULL;\n", + " }\n", + "\n", + " // Get first result\n", + " const char *text = PyUnicode_AsUTF8(PyIter_Next(result));\n", + "\n", + " // Cleanup result\n", + " Py_CLEAR(result);\n", + "\n", + " return text;\n", + "}" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rv64SWDkpmIx", + "outputId": "1c19ae3c-7f06-43ac-8638-37ee7b5e4eab" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Writing workflow.c\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Run txtai workflow in C\n", + "\n", + "Let's now write a C program to run a workflow using command line arguments as input. " + ], + "metadata": { + "id": "K19FZK5StVbf" + } + }, + { + "cell_type": "code", + "source": [ + "%%writefile main.c\n", + "#include \n", + "\n", + "extern char* workflow(char** argv);\n", + "extern void cleanup();\n", + "\n", + "/**\n", + " * Run a txtai workflow and print results.\n", + " */\n", + "int main(int argc, char** argv) {\n", + " if (argc < 3) {\n", + " printf(\"Usage: workflow \\n\");\n", + " return 1;\n", + " }\n", + "\n", + " // Run workflow using command line arguments\n", + " char* text = workflow(argv + 1);\n", + " if (text) {\n", + " printf(\"%s\\n\", text);\n", + " }\n", + "\n", + " // Cleanup\n", + " cleanup();\n", + "\n", + " return 0;\n", + "}" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "apBnMta8pepD", + "outputId": "e6573cec-6b92-424c-e594-aa46c7cd18b6" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Writing main.c\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Compile and run\n", + "\n", + "Time to compile this all into an executable and run!" + ], + "metadata": { + "id": "5kVxaL0FuOgu" + } + }, + { + "cell_type": "code", + "source": [ + "!cc -c main.c -I/usr/include/python3.7m\n", + "!cc -c workflow.c -I/usr/include/python3.7m\n", + "!cc -o workflow workflow.o main.o -lpython3.7m" + ], + "metadata": { + "id": "KjQYctCqp1Vi" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "!./workflow translate \"I'm running machine translation using a transformers model in C!\"" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "t5IWOF8ap-a7", + "outputId": "071f3d26-955e-4e10-cce4-5ed9774960d5" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "J'exécute la traduction automatique à l'aide d'un modèle de transformateurs en C!\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "And there it is, a translation workflow from English to French in a native executable, all backed by Transformers models. Any workflow YAML can be loaded and run in C using this method, which is pretty powerful.\n", + "\n", + "Embedding txtai in native executable adds libpython as a dependency (libraries from 3rd party modules such as PyTorch and NumPy also load dynamically). See output of ldd below.\n", + "This opens up an avenue to embed txtai in native code provided it is acceptable to add libpython as a project dependency. \n", + "\n", + "As mentioned above, connecting to a txtai HTTP API instance is a less tightly coupled way to accomplish the same thing." + ], + "metadata": { + "id": "4eNk20mCxp3r" + } + }, + { + "cell_type": "code", + "source": [ + "!ldd workflow | grep python" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "SLq5ix5mGPAb", + "outputId": "a46720e6-c721-4df5-92a7-a1bd897511d0" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\tlibpython3.7m.so.1.0 => /usr/lib/x86_64-linux-gnu/libpython3.7m.so.1.0 (0x00007efcba85e000)\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Machine learning in Assembly?\n", + "\n", + "Now for a more academic exercise perhaps bringing you back to a computer organization/logic class from college. Let's see if we can run the same program in assembly!" + ], + "metadata": { + "id": "n6E-yHIjy0UY" + } + }, + { + "cell_type": "code", + "source": [ + "%%writefile main.asm\n", + "global main\n", + "\n", + "; External C library functions\n", + "extern puts\n", + "\n", + "; External txtai functions\n", + "extern workflow, cleanup\n", + "\n", + "; Default to REL mode\n", + "default REL\n", + "\n", + "section .data\n", + " message: db \"Usage: workflow \", 0\n", + "\n", + "section .text\n", + "\n", + "; Print a usage message\n", + "usage:\n", + " mov rdi, message\n", + " call puts\n", + " jmp done\n", + "\n", + "; Main function\n", + "main:\n", + " ; Enter\n", + " sub rsp, 8\n", + "\n", + " ; Read argc - require workflow name and element (plus program name)\n", + " cmp rdi, 3\n", + " jl usage\n", + "\n", + " ; Run txtai workflow with argv params (skip program name) and print result\n", + " lea rdi, [rsi + 8]\n", + " call workflow\n", + " mov rdi, rax\n", + " call puts\n", + "\n", + "done:\n", + " ; Close txtai application instance\n", + " call cleanup\n", + "\n", + " ; Exit\n", + " add rsp, 8\n", + " ret" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "osrjXJonqcTm", + "outputId": "b2d662d0-2a71-4b53-a956-c9277c4a7d2b" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Writing main.asm\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "# Build workflow executable\n", + "!nasm -felf64 main.asm\n", + "!cc -c workflow.c -I/usr/include/python3.7m\n", + "!cc -o workflow -no-pie workflow.o main.o -lpython3.7m" + ], + "metadata": { + "id": "sQMEx0LFq80Z" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "!./workflow translate \"I'm running machine translation using a transformers model with assembler!\"" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "YG6faU7HrCrH", + "outputId": "a7a77cbf-5806-4241-cd67-03e79438e6f2" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "J'exécute la traduction automatique à l'aide d'un modèle de transformateurs avec assembleur!\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Just as before, the input text is translated to French using a machine translation model. But this time the code executing the logic was in assembly!\n", + "\n", + "Probably not terribly useful but using the lowest level of code possible proves that any higher-level native code can do the same. " + ], + "metadata": { + "id": "SNXAqFm6bNSw" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Multiple workflow calls\n", + "\n", + "Everything up to this point has been a single workflow call. Much of the run time is spent on loading models as part of the txtai workflow. The next example will run a series of workflow calls and compare how long it takes vs a single workflow command line call. Once again in assembly." + ], + "metadata": { + "id": "dB_5UWoDH0nY" + } + }, + { + "cell_type": "code", + "source": [ + "%%writefile main.asm\n", + "global main\n", + "\n", + "; External C library functions\n", + "extern printf\n", + "\n", + "; External txtai functions\n", + "extern workflow, cleanup\n", + "\n", + "; Default to REL mode\n", + "default REL\n", + "\n", + "section .data\n", + " format: db \"action: %s\", 10, \"input: %s\", 10, \"output: %s\", 10, 10, 0\n", + " summary: db \"summary\", 0\n", + " translate: db \"translate\", 0\n", + " text1: db \"txtai executes machine-learning workflows to transform data and build AI-powered semantic search applications.\", 0\n", + " text2: db \"Traditional search systems use keywords to find data\", 0\n", + " url1: db \"https://github.com/neuml/txtai\", 0\n", + " url2: db \"https://github.com/neuml/paperai\", 0\n", + "\n", + "section .text\n", + "\n", + "; Run txtai workflow and print results\n", + "%macro txtai 2\n", + " ; Workflow name and element\n", + " push %2\n", + " push %1\n", + "\n", + " ; Run workflow\n", + " lea rdi, [rsp]\n", + " call workflow\n", + "\n", + " ; Print action-input-output\n", + " mov rdi, format\n", + " mov rsi, [rsp]\n", + " mov rdx, [rsp + 8]\n", + " mov rcx, rax\n", + " call printf\n", + "\n", + " ; Restore stack\n", + " add rsp, 16\n", + "%endmacro\n", + "\n", + "; Main function\n", + "main:\n", + " ; Enter\n", + " sub rsp, 8\n", + "\n", + " ; Run workflows\n", + " txtai translate, text1\t\n", + " txtai translate, text2\n", + " txtai summary, url1\n", + " txtai summary, url2\n", + "\n", + "done:\n", + " ; Close txtai application instance\n", + " call cleanup\n", + "\n", + " ; Exit\n", + " add rsp, 8\n", + " ret" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "wzTYCSaqCSZu", + "outputId": "1f48015f-7654-4d86-9f2a-2e462c22f4e6" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Overwriting main.asm\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "!time ./workflow translate \"I'm running machine translation using a transformers model with assembler!\"" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "BSmdBuPhIIbw", + "outputId": "b45c1ea5-e0fb-4331-f033-5370cdc2ead0" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "J'exécute la traduction automatique à l'aide d'un modèle de transformateurs avec assembleur!\n", + "\n", + "real\t0m19.208s\n", + "user\t0m11.256s\n", + "sys\t0m3.224s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "# Build workflow executable\n", + "!nasm -felf64 main.asm\n", + "!cc -c workflow.c -I/usr/include/python3.7m\n", + "!cc -no-pie -o workflow workflow.o main.o -lpython3.7m" + ], + "metadata": { + "id": "HoRsPeorC5rC" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "!time ./workflow" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "7Da6TZlDC8Mj", + "outputId": "e2b46994-62d1-42d1-ab77-0d690a7d7d67" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "action: translate\n", + "input: txtai executes machine-learning workflows to transform data and build AI-powered semantic search applications.\n", + "output: txtai exécute des workflows d'apprentissage automatique pour transformer les données et construire des applications de recherche sémantique alimentées par l'IA.\n", + "\n", + "action: translate\n", + "input: Traditional search systems use keywords to find data\n", + "output: Les systèmes de recherche traditionnels utilisent des mots-clés pour trouver des données\n", + "\n", + "action: summary\n", + "input: https://github.com/neuml/txtai\n", + "output: txtai executes machine-learning workflows to transform data and build AI-powered semantic search applications. Semantic search applications have an understanding of natural language and identify results that have the same meaning, not necessarily the same keywords. API bindings for JavaScript, Java, Rust and Go. Cloud-native architecture scales out with container orchestration systems (e. g. Kubernetes)\n", + "\n", + "action: summary\n", + "input: https://github.com/neuml/paperai\n", + "output: paperai is an AI-powered literature discovery and review engine for medical/scientific papers. Paperai was used to analyze the COVID-19 Open Research Dataset (CORD-19) paperai and NeuML have been recognized in the following articles: Cord-19 Kaggle Challenge Awards Machine-Learning Experts Delve Into 47,000 Papers on Coronavirus Family.\n", + "\n", + "\n", + "real\t0m22.478s\n", + "user\t0m13.776s\n", + "sys\t0m3.218s\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "As we can see, running 4 workflow actions is about the same runtime as a single action when accounting for model load times." + ], + "metadata": { + "id": "zNalTEg5cmhX" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "This notebook walked through an example on how to run txtai with native code. While the HTTP API is a better route to go, this is another way to work with txtai!" + ], + "metadata": { + "id": "4L8smyyXc8q8" + } + } + ] +} \ No newline at end of file diff --git a/examples/37_Embeddings_index_components.ipynb b/examples/37_Embeddings_index_components.ipynb new file mode 100644 index 0000000..ea49270 --- /dev/null +++ b/examples/37_Embeddings_index_components.ipynb @@ -0,0 +1,566 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "gpuClass": "standard", + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Embeddings index components\n", + "\n", + "The main components of txtai are `embeddings`, `pipeline`, `workflow` and an `api`. The following shows the top level view of the txtai src tree.\n", + "\n", + "```\n", + "Abbreviated listing of src/txtai\n", + " ann\n", + " api\n", + " database\n", + " embeddings\n", + " pipeline\n", + " scoring\n", + " vectors\n", + " workflow\n", + "```\n", + "\n", + "One might ask, why are `ann`, `database`, `scoring` and `vectors` top level packages and not under the `embeddings` package? The `embeddings` package provides the glue between these components, making everything easy to use. The reason is that each of these packages are modular and can be used on their own! \n", + "\n", + "This notebook will go through a series of examples demonstrating how these components can be used standalone as well as combined together to build custom search indexes.\n", + "\n", + "_Note: This is intended as a deep dive into txtai `embeddings` components. There are much simpler high-level APIs for standard use cases._" + ], + "metadata": { + "id": "-xU9P9iSR-Cy" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ], + "metadata": { + "id": "shlUi2kKS7KT" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "xEvX9vCpn4E0" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai datasets" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Load dataset\n", + "\n", + "This example will use the `ag_news` dataset, which is a collection of news article headlines." + ], + "metadata": { + "id": "408IyXzKFSiG" + } + }, + { + "cell_type": "code", + "source": [ + "from datasets import load_dataset\n", + "\n", + "dataset = load_dataset(\"ag_news\", split=\"train\")" + ], + "metadata": { + "id": "IQ_ns6YvFRm1" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Approximate nearest neighbor (ANN) and Vectors\n", + "\n", + "In this section, we'll use the `ann` and `vectors` package to build a similarity index over the `ag_news` dataset.\n", + "\n", + "The first step is vectorizing the text. We'll use a `sentence-transformers` model. " + ], + "metadata": { + "id": "AtEdP7Utw3mk" + } + }, + { + "cell_type": "code", + "source": [ + "import numpy as np\n", + "\n", + "from txtai.vectors import VectorsFactory\n", + "\n", + "model = VectorsFactory.create({\"path\": \"sentence-transformers/all-MiniLM-L6-v2\"}, None)\n", + "\n", + "embeddings = []\n", + "\n", + "# List of all text elements\n", + "texts = dataset[\"text\"]\n", + "\n", + "# Create embeddings buffer, vector model has 384 features\n", + "embeddings = np.zeros(dtype=np.float32, shape=(len(texts), 384))\n", + "\n", + "# Vectorize text in batches\n", + "batch, index, batchsize = [], 0, 128\n", + "for text in texts:\n", + " batch.append(text)\n", + "\n", + " if len(batch) == batchsize:\n", + " vectors = model.encode(batch)\n", + " embeddings[index : index + vectors.shape[0]] = vectors\n", + " index += vectors.shape[0]\n", + " batch = []\n", + "\n", + "# Last batch\n", + "if batch:\n", + " vectors = model.encode(batch)\n", + " embeddings[index : index + vectors.shape[0]] = vectors\n", + "\n", + "# Normalize embeddings\n", + "embeddings /= np.linalg.norm(embeddings, axis=1)[:, np.newaxis]\n", + "\n", + "# Print shape\n", + "embeddings.shape" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "DPWrubv5oOn7", + "outputId": "972c1837-2404-42f4-9a5e-51f3c3f149ee" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "(120000, 384)" + ] + }, + "metadata": {}, + "execution_count": 3 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Next we'll build a vector index using these embeddings!" + ], + "metadata": { + "id": "SDaDLMyXLGe1" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai.ann import ANNFactory\n", + "\n", + "# Create Faiss index using normalized embeddings\n", + "ann = ANNFactory.create({\"backend\": \"faiss\"})\n", + "ann.index(embeddings)\n", + "\n", + "# Show total\n", + "ann.count()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ILSfWHxVHex0", + "outputId": "b9da6a79-778f-4338-a6b5-d693772fcdae" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "120000" + ] + }, + "metadata": {}, + "execution_count": 4 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Now let's run a search." + ], + "metadata": { + "id": "B_XnpIpXNKSP" + } + }, + { + "cell_type": "code", + "source": [ + "query = model.encode([\"best planets to explore for life\"])\n", + "query /= np.linalg.norm(query)\n", + "\n", + "for uid, score in ann.search(query, 3)[0]:\n", + " print(uid, texts[uid], score)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "c2FVQlxSLKgP", + "outputId": "825ca5de-d765-4c67-fdde-c7fe06557095" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "17752 Rocky Road: Planet hunting gets closer to Earth Astronomers have discovered the three lightest planets known outside the solar system, moving researchers closer to the goal of finding extrasolar planets that resemble Earth. 0.599043607711792\n", + "16158 Earth #39;s #39;big brothers #39; floating around stars Washington - A new class of planets has been found orbiting stars besides our sun, in a possible giant leap forward in the search for Earth-like planets that might harbour life. 0.5688529014587402\n", + "45029 Coming Soon: \"Good\" Jupiters Most of the extrasolar planets discovered to date are gas giants like Jupiter, but their orbits are either much closer to their parent stars or are highly eccentric. Planet hunters are on the verge of confirming the discovery of Jupiter-size planets with Jupiter-like orbits. Solar systems that contain these \"good\" Jupiters may harbor habitable Earth-like planets as well. 0.5606889724731445\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "And there it is, a full vector search system without using the `embeddings` package.\n", + "\n", + "Just as a reminder, the following much simpler code does the same thing with an Embeddings instance." + ], + "metadata": { + "id": "00dnum6fNNM0" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai.embeddings import Embeddings\n", + "\n", + "embeddings = Embeddings({\"path\": \"sentence-transformers/all-MiniLM-L6-v2\"})\n", + "embeddings.index((x, text, None) for x, text in enumerate(texts))\n", + "\n", + "for uid, score in embeddings.search(\"best planets to explore for life\"):\n", + " print(uid, texts[uid], score)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "OYAqPoTmNaNN", + "outputId": "30b6305a-11da-4439-e0f5-646aef8d96f6" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "17752 Rocky Road: Planet hunting gets closer to Earth Astronomers have discovered the three lightest planets known outside the solar system, moving researchers closer to the goal of finding extrasolar planets that resemble Earth. 0.599043607711792\n", + "16158 Earth #39;s #39;big brothers #39; floating around stars Washington - A new class of planets has been found orbiting stars besides our sun, in a possible giant leap forward in the search for Earth-like planets that might harbour life. 0.568852961063385\n", + "45029 Coming Soon: \"Good\" Jupiters Most of the extrasolar planets discovered to date are gas giants like Jupiter, but their orbits are either much closer to their parent stars or are highly eccentric. Planet hunters are on the verge of confirming the discovery of Jupiter-size planets with Jupiter-like orbits. Solar systems that contain these \"good\" Jupiters may harbor habitable Earth-like planets as well. 0.560688853263855\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Database\n", + "\n", + "When the `content` parameter is enabled, an Embeddings instance stores both vector content and raw content in a database. But the `database` package can be used standalone too." + ], + "metadata": { + "id": "KfGc7iNRO0Tw" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai.database import DatabaseFactory\n", + "\n", + "# Load content into database\n", + "database = DatabaseFactory.create({\"content\": True})\n", + "database.insert((x, row, None) for x, row in enumerate(dataset))\n", + "\n", + "# Show total\n", + "database.search(\"select count(*) from txtai\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "9IwcIgocPSDr", + "outputId": "eeceee2f-cf35-414f-b9e4-ad975c118e42" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'count(*)': 120000}]" + ] + }, + "metadata": {}, + "execution_count": 7 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The full txtai [SQL query syntax](https://neuml.github.io/txtai/embeddings/query/#sql) is available, including working with dynamically created fields." + ], + "metadata": { + "id": "l18dapNgSqnA" + } + }, + { + "cell_type": "code", + "source": [ + "database.search(\"select count(*), label from txtai group by label\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "7yPjcT3peDnZ", + "outputId": "241c98df-9c24-47a1-8ca5-e5bace19abfb" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'count(*)': 30000, 'label': 0},\n", + " {'count(*)': 30000, 'label': 1},\n", + " {'count(*)': 30000, 'label': 2},\n", + " {'count(*)': 30000, 'label': 3}]" + ] + }, + "metadata": {}, + "execution_count": 8 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Let's run a query to find text containing the word planets." + ], + "metadata": { + "id": "S8G09Ib0kRB9" + } + }, + { + "cell_type": "code", + "source": [ + "for row in database.search(\"select id, text from txtai where text like '%planets%' limit 3\"):\n", + " print(row[\"id\"], row[\"text\"])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Aq1ZvOhvQHRO", + "outputId": "3c2f9c94-57b1-489d-c854-60b909187ff0" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "100 Comets, Asteroids and Planets around a Nearby Star (SPACE.com) SPACE.com - A nearby star thought to harbor comets and asteroids now appears to be home to planets, too. The presumed worlds are smaller than Jupiter and could be as tiny as Pluto, new observations suggest.\n", + "102 Redesigning Rockets: NASA Space Propulsion Finds a New Home (SPACE.com) SPACE.com - While the exploration of the Moon and other planets in our solar system is nbsp;exciting, the first task for astronauts and robots alike is to actually nbsp;get to those destinations.\n", + "272 Sharpest Image Ever Obtained of a Circumstellar Disk Reveals Signs of Young Planets MAUNA KEA, Hawaii -- The sharpest image ever taken of a dust disk around another star has revealed structures in the disk which are signs of unseen planets. Dr...\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Since this is just a SQL database, text search is quite limited. The query above just retrieved results with the word planets in it." + ], + "metadata": { + "id": "xhND31tnkOY2" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Scoring\n", + "\n", + "Since the original txtai release, there has been a `scoring` package. The main use case for this package is building a weighted sentence embeddings vector when using word vector models. But this package can also be used standalone to build BM25, TF-IDF and/or SIF text indexes." + ], + "metadata": { + "id": "-vNVSA2FQnKj" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai.scoring import ScoringFactory\n", + "\n", + "# Build index\n", + "scoring = ScoringFactory.create({\"method\": \"bm25\", \"terms\": True, \"content\": True})\n", + "scoring.index((x, text, None) for x, text in enumerate(texts))\n", + "\n", + "# Show total\n", + "scoring.count()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "LJ2FskiiQ_l_", + "outputId": "d023191a-b9bc-47fa-be99-375b81f02e8e" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "120000" + ] + }, + "metadata": {}, + "execution_count": 10 + } + ] + }, + { + "cell_type": "code", + "source": [ + "for row in scoring.search(\"planets explore life earth\", 3):\n", + " print(row[\"id\"], row[\"text\"], row[\"score\"])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "slLtmzfbRuf6", + "outputId": "be7ba16e-fd6d-4c30-fb43-a4617bac21ca" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16327 3 Planets Are Found Close in Size to Earth, Making Scientists Think 'Life' A trio of newly discovered worlds are much smaller than any other planets previously discovered outside of the solar system. 17.768332448130707\n", + "16158 Earth #39;s #39;big brothers #39; floating around stars Washington - A new class of planets has been found orbiting stars besides our sun, in a possible giant leap forward in the search for Earth-like planets that might harbour life. 17.65941968170793\n", + "16620 New Planets could advance search for Life Astronomers in Europe and the United States have found two new planets about 20 times the size of Earth beyond the solar system. The discovery might be a giant leap forward in 17.65941968170793\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The search above ran a BM25 search across the dataset. The search will return more keyword/literal results. With proper query construction, the results can be decent.\n", + "\n", + "Comparing the vector search results earlier and these results are a good lesson in the differences between keyword and vector search." + ], + "metadata": { + "id": "BOW5JlxYS3Rm" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Database and Scoring\n", + "\n", + "Earlier we showed how the `ann` and `vectors` components can be combined to build a vector search engine. Can we combine the `database` and `scoring` components to add keyword search to a database? Yes!" + ], + "metadata": { + "id": "gqEBeBEoTrup" + } + }, + { + "cell_type": "code", + "source": [ + "def search(query, limit=3):\n", + " # Get similar clauses, if any\n", + " similar = database.parse(query).get(\"similar\")\n", + " return database.search(query, [scoring.search(args[0], limit * 10) for args in similar] if similar else None, limit)\n", + "\n", + "# Rebuild scoring - only need terms index\n", + "scoring = ScoringFactory.create({\"method\": \"bm25\", \"terms\": True})\n", + "scoring.index((x, text, None) for x, text in enumerate(texts))\n", + "\n", + "for row in search(\"select id, text, score from txtai where similar('planets explore life earth') and label = 0\"):\n", + " print(row[\"id\"], row[\"text\"], row[\"score\"])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "MEpZAr0TUMCK", + "outputId": "b320604b-f2a0-4546-c91d-2d2b0c449382" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "15363 NASA to Announce New Class of Planets Astronomers have discovered four new planets in a week's time, an exciting end-of-summer flurry that signals a sharper era in the hunt for new worlds. While none of these new bodies would be mistaken as Earth's twin, some appear to be noticeably smaller and more solid - more like Earth and Mars - than the gargantuan, gaseous giants identified before... 12.582923259697132\n", + "15900 Astronomers Spot Smallest Planets Yet American astronomers say they have discovered the two smallest planets yet orbiting nearby stars, trumping a small planet discovery by European scientists five days ago and capping the latest round in a frenzied hunt for other worlds like Earth. All three of these smaller planets belong to a new class of \"exoplanets\" - those that orbit stars other than our sun, the scientists said in a briefing Tuesday... 12.563928231067155\n", + "15879 Astronomers see two new planets US astronomers find the smallest worlds detected circling other stars and say it is a breakthrough in the search for life in space. 12.078383982352994\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "And there it is, scoring-based similarity search with the same syntax as standard txtai vector queries, including additional filters!\n", + "\n", + "txtai is built on vector search, machine learning and finding results based on semantic meaning. It's been well-discussed from a functionality standpoint how vector search has many advantages over keyword search. The one advantage keyword search has is speed. " + ], + "metadata": { + "id": "aPaZsoxnYW8I" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "This notebook walked through each of the packages used by an Embeddings index. The Embeddings index makes this all transparent and easy to use. But each of the components do stand on their own and can be individually integrated into a project!" + ], + "metadata": { + "id": "4L8smyyXc8q8" + } + } + ] +} \ No newline at end of file diff --git a/examples/38_Introducing_the_Semantic_Graph.ipynb b/examples/38_Introducing_the_Semantic_Graph.ipynb new file mode 100644 index 0000000..825d05b --- /dev/null +++ b/examples/38_Introducing_the_Semantic_Graph.ipynb @@ -0,0 +1,1490 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "collapsed_sections": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "accelerator": "GPU", + "gpuClass": "standard" + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Introducing the Semantic Graph\n", + "\n", + "One of the main use cases of txtai is semantic search over a corpus of data. Semantic search provides an understanding of natural language and identifies results that have the same meaning, not necessarily the same keywords. Within an Embeddings instance sits a wealth of implied knowledge and relationships between rows. Many approximate nearest neighbor (ANN) indexes are even backed by graphs. What if we are able to tap into this knowledge?\n", + "\n", + "Semantic graphs, also known as knowledge graphs or semantic networks, build a graph network with semantic relationships connecting the nodes. In txtai, they can take advantage of the relationships inherently learned within an embeddings index. This opens exciting possibilities for exploring relationships, such as topics and interconnections in a dataset. \n", + "\n", + "This notebook introduces the semantic graph.\n", + "\n" + ], + "metadata": { + "id": "tcpqzMwjvdN2" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies. We'll install the graph extra for graph functionality, pipeline extra for object detection and similarity extra to load models with the sentence-transformers library." + ], + "metadata": { + "id": "wGvazIzFTCdt" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "X4Gi8UIErK44" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[graph,pipeline,similarity] datasets ipyplot" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Graph basics\n", + "\n", + "First we'll build a basic [graph](https://en.wikipedia.org/wiki/Graph_theory) and show how it can be used to explore relationships.\n", + "\n", + "The code below builds a graph of animals and relationships between them. We'll add nodes and relationships along with running a couple analysis functions." + ], + "metadata": { + "id": "vEdSkQaKOD-C" + } + }, + { + "cell_type": "code", + "source": [ + "import networkx as nx\n", + "\n", + "from txtai.graph import GraphFactory\n", + "\n", + "# Create graph\n", + "graph = GraphFactory.create({\"backend\": \"networkx\"})\n", + "graph.initialize()\n", + "\n", + "# Add nodes\n", + "nodes = [(0, \"dog\"), (1, \"fox\"), (2, \"wolf\"), (3, \"zebra\"), (4, \"horse\")]\n", + "labels = {uid:text for uid, text in nodes}\n", + "for uid, text in nodes:\n", + " graph.addnode(uid, text=text)\n", + "\n", + "# Add relationships\n", + "edges = [(0, 1, 1), (0, 2, 1), (1, 2, 1), (2, 3, 0.25), (3, 4, 1)]\n", + "for source, target, weight in edges:\n", + " graph.addedge(source, target, weight=weight)\n", + "\n", + "# Print centrality and path between 0 and 4\n", + "print(\"Centrality:\", {labels[k]:v for k, v in graph.centrality().items()})\n", + "print(\"Path (dog->horse):\", \" -> \".join([labels[uid] for uid in graph.showpath(0, 4)]))\n", + "\n", + "# Visualize graph\n", + "nx.draw(graph.backend, nx.shell_layout(graph.backend), labels=labels, with_labels=True,\n", + " node_size=2000, node_color=\"#03a9f4\", edge_color=\"#cfcfcf\", font_color=\"#fff\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 355 + }, + "id": "yfi007XD4Oa4", + "outputId": "722e9c39-46e1-4432-98b7-6bd1a8d7dac1" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Centrality: {'wolf': 0.75, 'dog': 0.5, 'fox': 0.5, 'zebra': 0.5, 'horse': 0.25}\n", + "Path (dog->horse): dog -> wolf -> zebra -> horse\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "
" + ], + "image/png": "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\n" + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The visualization shows the layout of the graph. A centrality and path function were also run. Centrality shows the most central or related nodes. In this case, the `wolf` node has the highest score. We also ran a path function to show how the graph is traversed from `dog` to `horse`." + ], + "metadata": { + "id": "6m8tNKnCPJUT" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Build a Semantic Graph\n", + "\n", + "While txtai graphs can be standalone, with nodes and relationships manually added, the real power comes in indexing an embeddings instance.\n", + "\n", + "The following section builds an embeddings index over the `ag_news` dataset. `ag_news` contains news headlines from the mid 2000s. This configuration sets the familiar vector model and content settings.\n", + "\n", + "Column expressions is a feature starting with txtai 5.0. Column expressions alias expressions allowing SQL statements to use those references as a shorthand for the expression.\n", + "\n", + "Next comes the graph. The configuration sets the maximum number of connections to add per node (15) along with a minimum similarity score (0.1). Topic modeling parameters are also added which we'll cover later." + ], + "metadata": { + "id": "96_vksSH94cQ" + } + }, + { + "cell_type": "code", + "source": [ + "from datasets import load_dataset\n", + "\n", + "from txtai.embeddings import Embeddings\n", + "\n", + "# Create embeddings instance with a semantic graph\n", + "embeddings = Embeddings({\n", + " \"path\": \"sentence-transformers/all-MiniLM-L6-v2\",\n", + " \"content\": True,\n", + " \"functions\": [\n", + " {\"name\": \"graph\", \"function\": \"graph.attribute\"},\n", + " ],\n", + " \"expressions\": [\n", + " {\"name\": \"category\", \"expression\": \"graph(indexid, 'category')\"},\n", + " {\"name\": \"topic\", \"expression\": \"graph(indexid, 'topic')\"},\n", + " {\"name\": \"topicrank\", \"expression\": \"graph(indexid, 'topicrank')\"}\n", + " ],\n", + " \"graph\": {\n", + " \"limit\": 15,\n", + " \"minscore\": 0.1,\n", + " \"topics\": {\n", + " \"categories\": [\"Society & Culture\", \"Science & Mathematics\", \"Health\", \"Education & Reference\", \"Computers & Internet\", \"Sports\",\n", + " \"Business & Finance\", \"Entertainment & Music\", \"Family & Relationships\", \"Politics & Government\"]\n", + " }\n", + " }\n", + "})\n", + "\n", + "# Load dataset\n", + "dataset = load_dataset(\"ag_news\", split=\"train\")\n", + "rows = dataset[\"text\"]" + ], + "metadata": { + "id": "xT2TcpWDrejZ" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Index dataset\n", + "embeddings.index((x, text, None) for x, text in enumerate(rows))" + ], + "metadata": { + "id": "SoA2HSIAr7Fb" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "The embeddings index is now created. Let's explore!" + ], + "metadata": { + "id": "Uyup__t3RYbE" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Topic modeling\n", + "\n", + "[Topic modeling](https://en.wikipedia.org/wiki/Topic_model) is an unsupervised method to identify abstract topics within a dataset. The most common way to do topic modeling is to use clustering algorithms to group nodes with the closest proximity.\n", + "\n", + "A number of excellent topic modeling libraries exist in Python today. [BERTopic](https://github.com/MaartenGr/BERTopic) and [Top2Vec](https://github.com/ddangelov/Top2Vec) are two of the most popular. Both use [sentence-transformers](https://github.com/UKPLab/sentence-transformers) to encode data into vectors, [UMAP](https://github.com/lmcinnes/umap) for dimensionality reduction and [HDBSCAN](https://github.com/scikit-learn-contrib/hdbscan) to cluster nodes.\n", + "\n", + "Given that an embeddings index has already encoded and indexed data, we'll take a different approach. txtai builds a graph running a query for each node against the index. In addition to topic modeling, this also opens up much more functionality which will be covered later.\n", + "\n", + "Topic modeling in txtai is done using [community detection](https://en.wikipedia.org/wiki/Community_structure) algorithms. Similar nodes are group together. There are settings to control how much granularity is used to group nodes. In other words, topics can be very specific or broad, depending on these settings. Topics are labeled by building a BM25 index over each topic and finding the most common terms associated with the topic.\n", + "\n", + "Let's take a closer look at the topics created with this embeddings index." + ], + "metadata": { + "id": "jI9-KIrD-yKa" + } + }, + { + "cell_type": "code", + "source": [ + "# Store reference to graph\n", + "graph = embeddings.graph\n", + "len(embeddings.graph.topics)" + ], + "metadata": { + "id": "kps7fhU50xvZ", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "75ad8bd2-0fe7-40ab-9aa6-f34a6fb8fe21" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "1919" + ] + }, + "metadata": {}, + "execution_count": 25 + } + ] + }, + { + "cell_type": "code", + "source": [ + "list(graph.topics.keys())[:5]" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "c-KX3xuM1EIP", + "outputId": "277376f3-1965-4816-9cc5-5c4aa9cd85d2" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "['kerry_john_bush_president',\n", + " 'sox_red_boston_series',\n", + " 'oil_opec_prices_said',\n", + " 'dollar_reuters_against_euro',\n", + " 'darfur_sudan_region_said']" + ] + }, + "metadata": {}, + "execution_count": 26 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The section above shows the number of topics in the index and top 5 topics. Keep in mind that `ag_news` is from the mid 2000s and that is evident with the top topics.\n", + "\n", + "Given that we added functions to run SQL functions to get the topic for each row, we can use that to explore topics.\n", + "\n", + "Each topic is associated with a list of associated matching ids. Those ids are ranked based on the importance to the topic in a field named `topicrank`. The section below prints the best matching text for the topic `sox_red_boston_series`. " + ], + "metadata": { + "id": "G9e_IkSYV9ap" + } + }, + { + "cell_type": "code", + "source": [ + "print(embeddings.search(\"select text from txtai where topic = 'sox_red_boston_series' and topicrank = 0\", 1)[0][\"text\"])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Kk2mo4dA1M4M", + "outputId": "2039403e-4469-4196-a3cd-afd581de9c8a" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Red Sox heading to the World Series The Boston Red Sox have won the American League Championship Series and are heading to the World Series for the first time since 1986.\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "In addition to topics, higher level categories can be associated with topics. This enables having granular topics and encompassing categories for topics. For example, the topic of 'sox_red_boston_series' has a category of `Sports`. See below." + ], + "metadata": { + "id": "pFz8L5Z8VS5X" + } + }, + { + "cell_type": "code", + "source": [ + "for x, topic in enumerate(list(graph.topics.keys())[:5]):\n", + " print(graph.categories[x], topic)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rveal0m82G4I", + "outputId": "fa1df06a-cbd4-4b57-8483-0f999ddfd93c" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Politics & Government kerry_john_bush_president\n", + "Sports sox_red_boston_series\n", + "Business & Finance oil_opec_prices_said\n", + "Business & Finance dollar_reuters_against_euro\n", + "Politics & Government darfur_sudan_region_said\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Topics and categories can also be used to filter results. See the difference when just querying for results similar to `book` and similar to `book` with a topic of `Sports`." + ], + "metadata": { + "id": "durtFtCGXFSl" + } + }, + { + "cell_type": "code", + "source": [ + "print(embeddings.search(\"select text from txtai where similar('book')\", 1)[0][\"text\"])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "NvFApEbn9LKM", + "outputId": "c1409750-2f37-46fc-be5c-8f19cd166a6b" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "A Guidebook for Every Taste LANNING a trip involves many difficult decisions, but near the top of my list is standing in a bookstore trying to choose from a daunting lineup of guidebooks, a purchase that brands the owner \n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "print(embeddings.search(\"select text from txtai where category='Sports' and similar('book')\", 1)[0][\"text\"])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "yH3dOcNHEn-n", + "outputId": "06e3c011-a09c-4182-c44a-55a79c016f63" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Same story for Wildcats After a game about as artful as a dime-store novel, Virginia coach Pete Gillen turned to literature to express the trying time his No.\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Graph analysis\n", + "\n", + "Indexing an embeddings instance into a graph adds the ability to do network analysis. For example, the centrality of the graph can be analyzed to find the most common nodes. Alternatively, pagerank could also be run to rank the importance of nodes within the dataset. \n", + "\n", + "The section below runs graph centrality and shows the associated topic for the most central nodes. Not surprisingly, many of the topics are top topics." + ], + "metadata": { + "id": "miJN5A4o-93q" + } + }, + { + "cell_type": "code", + "source": [ + "centrality = graph.centrality()\n", + "\n", + "topics = list(graph.topics.keys())\n", + "\n", + "for uid in list(centrality.keys())[:5]:\n", + " topic = graph.attribute(uid, \"topic\")\n", + " print(f\"{topic} ({topics.index(topic)})\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "PiprqX8g2elt", + "outputId": "6b85a0d0-974a-4540-d51a-0934238bd472" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "peoplesoft_oracle_takeover_bid (442)\n", + "darfur_sudan_region_said (4)\n", + "windows_microsoft_xp_service (12)\n", + "fallujah_us_city_iraqi (24)\n", + "eclipse_lunar_moon_total (615)\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Walk the graph\n", + "\n", + "Given that graphs are nodes and relationships, we can traverse the nodes using those relationships. The graph can be used to show how any two nodes are connected. " + ], + "metadata": { + "id": "gT-lDV5p_AIB" + } + }, + { + "cell_type": "code", + "source": [ + "from IPython.display import HTML\n", + "\n", + "def highlight(index, result):\n", + " output = f\"{index}. \"\n", + " spans = [(token, score, \"#fff59d\" if score > 0.025 else None) for token, score in result[\"tokens\"]]\n", + "\n", + " if result[\"score\"] >= 0.05 and not [color for _, _, color in spans if color]:\n", + " mscore = max([score for _, score, _ in spans])\n", + " spans = [(token, score, \"#fff59d\" if score == mscore else color) for token, score, color in spans]\n", + "\n", + " for token, _, color in spans:\n", + " output += f\"{token} \" if color else f\"{token} \"\n", + "\n", + " return output\n", + "\n", + "def showpath(source, target):\n", + " path = graph.showpath(source, target)\n", + " path = [graph.attribute(p, \"text\") for p in path]\n", + "\n", + " sections = []\n", + " for x, p in enumerate(path):\n", + " if x == 0:\n", + " # Print start node\n", + " sections.append(f\"{x + 1}. {p}\")\n", + "\n", + " if x < len(path) - 1:\n", + " # Explain and highlight next path element\n", + " results = embeddings.explain(p, [path[x + 1]], limit=1)[0]\n", + "\n", + " sections.append(highlight(x + 2, results))\n", + "\n", + " return HTML(\"

\".join(sections))" + ], + "metadata": { + "id": "sxSKqhPd5AIa" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "showpath(82889, 67364)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 243 + }, + "id": "nd97WRtn54tB", + "outputId": "12ca85b5-d42f-41f5-f879-b42dbb61206d" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "1. Photo: Famous squirrel, moose go wireless iFone will introduce ring tones, games and screen graphics based on Rocky, the flying squirrel, and his sidekick, Bullwinkle.

2. Squirrel Runs Circles Around Yanks, Tribe (AP) AP - This squirrelly newcomer caused quite a stir at Jacobs Field. A brown squirrel ran onto the field in the bottom of the third inning Wednesday night and ran circles around the New York Yankees and Cleveland Indians.

3. Yankees strike back Gary Sheffield hit a tiebreaking two-run homer in the ninth inning and the Yankees sent the fading Indians to their eighth straight loss, 6-4, last night in Cleveland.

4. Yanks Crush Red Sox Gary Sheffield, Derek Jeter and Jorge Posada each homer off Pedro Martinez, and the Yankees rout the Red Sox 11-1 on Sunday.

5. Yankees Rout Red Sox Yankees' bats pound the Boston Red Sox to the brink of elimination with 22 hits in a 19-8 slaughter on Saturday night, giving New York a commanding 3-0 series advantage.

6. Red Sox Favored Over Yankees in Las Vegas, by Futures Traders The Boston Red Sox, who haven #39;t won baseball #39;s World Series since 1918, are favored to beat the New York Yankees in the American League Championship Series by Las Vegas oddsmakers and futures traders.

7. Red Sox heading to the World Series The Boston Red Sox have won the American League Championship Series and are heading to the World Series for the first time since 1986. " + ] + }, + "metadata": {}, + "execution_count": 155 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "This shows how text about a `famous squirrel` and the `Red Sox winning the world series` are connected. Notice how the first match pivots to a node about a squirrel running on the field during a baseball game. From there, it's a relatively logical path to the end node. \n", + "\n", + "This is reminiscent of the game \"six degrees of Kevin Bacon\". Try running `showpath` with calls to `random.randint(0, len(rows) - 1)`, it's oddly addicting. This is a fun way to explore the interconnectivity of a dataset." + ], + "metadata": { + "id": "fYK6eF-MiYuQ" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Group images into topics\n", + "\n", + "Topic modeling isn't limited to text. It supports any data that can be vectorized into an embeddings index. Next we'll create an embeddings index using the `imagenette` dataset, which is a small dataset for image object detection" + ], + "metadata": { + "id": "XDopj99u-trz" + } + }, + { + "cell_type": "code", + "source": [ + "dataset = load_dataset(\"frgfm/imagenette\", \"160px\", split=\"train\")\n", + "rows = dataset[\"image\"]\n", + "\n", + "# Index with content and objects\n", + "embeddings = Embeddings({\n", + " \"method\": \"sentence-transformers\",\n", + " \"path\": \"sentence-transformers/clip-ViT-B-32\",\n", + " \"content\": True,\n", + " \"objects\": \"image\",\n", + " \"functions\": [\n", + " {\"name\": \"graph\", \"function\": \"graph.attribute\"},\n", + " ],\n", + " \"expressions\": [\n", + " {\"name\": \"topic\", \"expression\": \"graph(indexid, 'topic')\"},\n", + " {\"name\": \"topicrank\", \"expression\": \"graph(indexid, 'topicrank')\"}\n", + " ],\n", + " \"graph\": {\n", + " \"limit\": 15,\n", + " \"minscore\": 0.1,\n", + " \"topics\": {\n", + " \"resolution\": 1.0\n", + " }\n", + " }\n", + "})\n", + "\n", + "embeddings.index((x, image, None) for x, image in enumerate(rows))" + ], + "metadata": { + "id": "74pjo5zx92k7" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "graph = embeddings.graph\n", + "list(graph.topics.keys())[:5]" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "NpQyTDSUMHIR", + "outputId": "ea41a841-b5ce-4846-c183-384c7414a0af" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "['topic_0', 'topic_1', 'topic_2', 'topic_3', 'topic_4']" + ] + }, + "metadata": {}, + "execution_count": 15 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Topic labeling for images\n", + "\n", + "The index is now ready. Notice how the topic names are generic. Given there is no text associated, a different approach is needed. We'll use an object detection pipeline to label to best matching image per topic." + ], + "metadata": { + "id": "RgA9FSNPmlaC" + } + }, + { + "cell_type": "code", + "source": [ + "import ipyplot\n", + "\n", + "from PIL import Image\n", + "\n", + "from txtai.pipeline import Objects\n", + "\n", + "def labels():\n", + " objects = Objects(classification=True, threshold=0.25)\n", + "\n", + " images = embeddings.search(\"select topic, object from txtai where topicrank=0 order by topic\", 100)\n", + " results = objects([result[\"object\"] for result in images], flatten=True)\n", + "\n", + " return {images[\"topic\"]: results[x][0].split(\",\")[0] for x, images in enumerate(images)}\n", + "\n", + "def scale(image, factor=1):\n", + " width, height = image.size\n", + " return image.resize((int(width / factor), int((width / factor))))\n", + "\n", + "images, labels = {}, labels()\n", + "\n", + "for topic in list(graph.topics.keys())[:5]:\n", + " for result in embeddings.search(f\"select topic, object from txtai where topic = '{topic}' and topicrank = 0\", len(graph.topics)):\n", + " images[topic] = scale(result[\"object\"])\n", + "\n", + "ipyplot.plot_images(list(images.values()), [labels[topic] for topic in images], img_width=150)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 269 + }, + "id": "dNzenG45On_R", + "outputId": "d0c8a1d4-13f7-4978-ed2d-4dc3f107fe8d" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "No model was supplied, defaulted to google/vit-base-patch16-224 and revision 5dca96d (https://huggingface.co/google/vit-base-patch16-224).\n", + "Using a pipeline without specifying a model name and revision in production is not recommended.\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + "
\n", + "
\n", + "
\n", + "

cassette player

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

garbage truck

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

golf ball

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

English springer

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

gas pump

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + "
" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "As we can see, each topic is now labeled. Imagenette is a labeled dataset, let's evaluate the accuracy of our topic modeling." + ], + "metadata": { + "id": "0QbcQDqQkUhz" + } + }, + { + "cell_type": "code", + "source": [ + "def accuracy():\n", + " correct, total = 0, 0\n", + " labels = dataset[\"label\"]\n", + "\n", + " for topic in graph.topics:\n", + " label = labels[int(graph.topics[topic][0])]\n", + " correct += sum(1 if labels[int(x)] == label else 0 for x in graph.topics[topic])\n", + " total += len(graph.topics[topic])\n", + "\n", + " print(\"Accuracy:\", correct/total)\n", + "\n", + "accuracy()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "QtbxK1OWSFFV", + "outputId": "c7a47681-d90c-42f6-df8f-f068236ffea0" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Accuracy: 0.9747597423170345\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Not bad, 97.48% accuracy using a totally unsupervised method not even intended for image classification!" + ], + "metadata": { + "id": "Wspya3xpkkE4" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Walk the image graph\n", + "\n", + "As we did before, let's walk the graph. We'll start with two images, `a person parachuting from the sky` and `someone holding a french horn`." + ], + "metadata": { + "id": "4gfxy-OLmwRt" + } + }, + { + "cell_type": "code", + "source": [ + "images = []\n", + "for uid in graph.showpath(4352, 9111):\n", + " images.append(scale(embeddings.search(f\"select object from txtai where indexid = {uid} limit 1\")[0][\"object\"]))\n", + "\n", + "ipyplot.plot_images(images, img_width=150)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 233 + }, + "id": "xYNGfO7dlCze", + "outputId": "070e9173-2725-4fd7-cbd0-11245b1a1966" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + "
\n", + "
\n", + "
\n", + "

0

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

1

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

2

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

3

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + "

4

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "
\n", + "
" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Very interesting! The first match is a person parachuting onto a football field, followed by a matching band on a field, finally leading to a person holding a french horn." + ], + "metadata": { + "id": "xSIKYvl_nEYi" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "This notebook covered quite a lot! We introduced graphs, showed how they can be used to model semantic relationships and topics. This change makes it easier to run exploratory data analysis on a dataset with txtai and quickly gain insights. \n", + "\n", + "This is just the beginning of what is possible and there are a wide range of exciting new possibilities for txtai, stay tuned!" + ], + "metadata": { + "id": "8qgNudA_nXd7" + } + } + ] +} diff --git a/examples/39_Classic_Topic_Modeling_with_BM25.ipynb b/examples/39_Classic_Topic_Modeling_with_BM25.ipynb new file mode 100644 index 0000000..ceed7da --- /dev/null +++ b/examples/39_Classic_Topic_Modeling_with_BM25.ipynb @@ -0,0 +1,454 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "gpuClass": "standard" + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Classic Topic Modeling with BM25\n", + "\n", + "txtai 5.0 introduced topic modeling via [semantic graphs](https://neuml.hashnode.dev/introducing-the-semantic-graph). Semantic graphs can be easily integrated into an embeddings instance to add topic modeling to a txtai index.\n", + "\n", + "In addition to transformers-backed models, txtai also has support for traditional indexing methods. Given the modular design of txtai, traditional scoring methods like BM25 can be combined with graphs to build topic models. \n", + "\n", + "This notebook is all classic Python code on the CPU. No GPUs or machine learning models required!" + ], + "metadata": { + "id": "-xU9P9iSR-Cy" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ], + "metadata": { + "id": "shlUi2kKS7KT" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "xEvX9vCpn4E0" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[graph] datasets" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Load dataset\n", + "\n", + "This example will use the `ag_news` dataset, which is a collection of news article headlines." + ], + "metadata": { + "id": "408IyXzKFSiG" + } + }, + { + "cell_type": "code", + "source": [ + "from datasets import load_dataset\n", + "\n", + "dataset = load_dataset(\"ag_news\", split=\"train\")" + ], + "metadata": { + "id": "IQ_ns6YvFRm1" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Build BM25 Index\n", + "\n", + "Since the original txtai release, there has been a `scoring` package. This package supports building standalone BM25, TF-IDF and/or SIF text indexes." + ], + "metadata": { + "id": "-vNVSA2FQnKj" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai.scoring import ScoringFactory\n", + "\n", + "# List of all text elements\n", + "texts = dataset[\"text\"]\n", + "\n", + "# Build index\n", + "scoring = ScoringFactory.create({\"method\": \"bm25\", \"terms\": True})\n", + "scoring.index((x, text, None) for x, text in enumerate(texts))\n", + "\n", + "# Show total\n", + "scoring.count()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "LJ2FskiiQ_l_", + "outputId": "fc4685d9-5ec2-4fbe-a347-857a74b9b509" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "120000" + ] + }, + "metadata": {}, + "execution_count": 3 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Let's test the index." + ], + "metadata": { + "id": "BOW5JlxYS3Rm" + } + }, + { + "cell_type": "code", + "source": [ + "for id, score in scoring.search(\"planets explore life earth\", 3):\n", + " print(id, texts[id], score)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "slLtmzfbRuf6", + "outputId": "27aca9cb-8704-475c-c38d-01da65328686" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16327 3 Planets Are Found Close in Size to Earth, Making Scientists Think 'Life' A trio of newly discovered worlds are much smaller than any other planets previously discovered outside of the solar system. 20.72295380862701\n", + "16158 Earth #39;s #39;big brothers #39; floating around stars Washington - A new class of planets has been found orbiting stars besides our sun, in a possible giant leap forward in the search for Earth-like planets that might harbour life. 19.917461045326878\n", + "16620 New Planets could advance search for Life Astronomers in Europe and the United States have found two new planets about 20 times the size of Earth beyond the solar system. The discovery might be a giant leap forward in 19.917461045326878\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Results look as expected. BM25 returns keyword-based results vs contextual matches." + ], + "metadata": { + "id": "XnAhNb8Td6Wd" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Build topic model\n", + "\n", + "Now that we have a scoring index, we'll use it to build a graph.\n", + "\n", + "Graphs have built-in methods to insert nodes and build a relationship index between the nodes. The `index` method takes a search parameter that can be any function that returns (id, score) pairs. This logic is built into embeddings instances. \n", + "\n", + "Graphs constructed via a BM25 index will have more literal relationships. In other words, it will be keyword-driven. Semantic graphs backed by embeddings will have contextual relationships.\n", + "\n", + "The next section builds a graph to support topic modeling. We'll use a multiprocessing pool to maximize CPU usage." + ], + "metadata": { + "id": "gqEBeBEoTrup" + } + }, + { + "cell_type": "code", + "source": [ + "import os\n", + "\n", + "from multiprocessing import Pool\n", + "\n", + "from txtai.graph import GraphFactory\n", + "\n", + "# Multiprocessing helper methods\n", + "SCORING = None\n", + "\n", + "def create(search):\n", + " global SCORING\n", + "\n", + " # Create a global scoring object\n", + " SCORING = search\n", + "\n", + "def run(params):\n", + " query, limit = params\n", + " return SCORING.search(query, limit)\n", + "\n", + "def batchsearch(queries, limit):\n", + " return pool.imap(run, [(query, limit) for query in queries])\n", + "\n", + "# Build the graph\n", + "pool = None\n", + "with Pool(os.cpu_count(), initializer=create, initargs=(scoring,)) as pool:\n", + " graph = GraphFactory.create({\"topics\": {}})\n", + " graph.insert((x, text, None) for x, text in enumerate(texts))\n", + " graph.index(batchsearch, None)" + ], + "metadata": { + "id": "MEpZAr0TUMCK" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Let's list the top 10 topics. Keep in mind this dataset is from 2004." + ], + "metadata": { + "id": "aPaZsoxnYW8I" + } + }, + { + "cell_type": "code", + "source": [ + "list(graph.topics)[:10]" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "HyKiVFBerQfw", + "outputId": "fdba468b-bac1-4f63-c915-5fcad78880f7" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "['kerry_bush_john_president',\n", + " 'nhl_players_league_lockout',\n", + " 'arafat_yasser_palestinian_leader',\n", + " 'sharon_ariel_prime_minister',\n", + " 'blair_tony_minister_prime',\n", + " 'xp_windows_microsoft_sp2',\n", + " 'athens_gold_medal_olympic',\n", + " 'space_prize_million_spaceshipone',\n", + " 'nikkei_tokyo_reuters_average',\n", + " 'hostage_british_bigley_iraq']" + ] + }, + "metadata": {}, + "execution_count": 10 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Topics map a list of ids for each matching text element ordered by topic relevance. Let's print the most relevant text element for a topic." + ], + "metadata": { + "id": "BKPJuq6br-ru" + } + }, + { + "cell_type": "code", + "source": [ + "uid = graph.topics[\"xp_windows_microsoft_sp2\"][0]\n", + "graph.attribute(uid, \"text\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 54 + }, + "id": "sYd9nKqYrwhe", + "outputId": "0db63a74-2c01-433a-d754-6f0988894ffe" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "'Microsoft continues Windows XP SP2 distribution Continuing the roll-out of Windows XP Service Pack 2 (SP2), Microsoft Corp. on Wednesday began pushing the security-focused update to PCs running Windows XP Professional Edition '" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + } + }, + "metadata": {}, + "execution_count": 44 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Graph analysis\n", + "\n", + "Given this is a standard txtai graph, analysis methods such as centrality and pagerank are available." + ], + "metadata": { + "id": "muN33RNB0Kob" + } + }, + { + "cell_type": "code", + "source": [ + "centrality = list(graph.centrality().keys())\n", + "print(\"Top connection count:\", [len(graph.edges(uid)) for uid in centrality[:5]], \"\\n\")\n", + "\n", + "# Print most central node/topic\n", + "print(\"Most central node:\", graph.attribute(centrality[0], \"text\"))\n", + "\n", + "topic = graph.attribute(centrality[0], \"topic\")\n", + "for uid in graph.topics[topic][:3]:\n", + " print(\"->\", graph.attribute(uid, \"text\"))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "hpwfJFcRv19j", + "outputId": "0bb4d53d-27c2-42f5-cfaf-89c785cb73da" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Top connection count: [30, 30, 28, 28, 28] \n", + "\n", + "Most central node: Manning Gets Chance to Start Giants Coach Tom Coughlin announced that rookie quarterback Eli Manning will start ahead of two-time M.V.P. Kurt Warner in Thursday's preseason game against Carolina.\n", + "-> Manning Replaces Warner As Giants QB (AP) AP - Eli Manning has replaced Kurt Warner as the New York Giants' starting quarterback.\n", + "-> Eli Manning replaces Warner at quarterback Eli Manning, the top pick in this year #39;s NFL draft, has been named the starting quarterback of the New York Giants. Coach Tom Coughlin made the announcement at a Monday news conference.\n", + "-> Giants to Start Manning Against Carolina (AP) AP - Eli Manning is going to get a chance to open the season as the New York Giants' starting quarterback.\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Notice the correlation between the number of connections and centrality.\n", + "\n", + "Given that BM25 is keyword-driven, we expect that the most central node would be text that is duplicative in nature. And that is the case here." + ], + "metadata": { + "id": "lqOdJR6a0ftB" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Walk the graph\n", + "\n", + "Just like semantic graphs, relationship paths can be explored." + ], + "metadata": { + "id": "8u_tTNrq2EoN" + } + }, + { + "cell_type": "code", + "source": [ + "from IPython.display import HTML\n", + "\n", + "def showpath(source, target):\n", + " path = graph.showpath(source, target)\n", + " path = [graph.attribute(p, \"text\") for p in path]\n", + "\n", + " sections = []\n", + " for x, p in enumerate(path):\n", + " # Print start node\n", + " sections.append(f\"{x + 1}. {p}\")\n", + "\n", + " return HTML(\"

\".join(sections))" + ], + "metadata": { + "id": "oodIUmwrsZRd" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "showpath(83978, 8107)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 208 + }, + "id": "gZzStey9tEkl", + "outputId": "8e6398d5-0fd8-465c-ce95-45b98eb2c195" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "1. NFL Game Summary - NY Jets at Buffalo Orchard Park, NY (Sports Network) - Willis McGahee ran for 132 yards and a touchdown to lead the Buffalo Bills to a 22-17 victory over the New York Jets at Ralph Wilson Stadium.

2. NCAA Game Summary - Marshall at Georgia Athens, GA (Sports Network) - Michael Cooper ran for the only touchdown of the game, as third-ranked Georgia rode its defense to a 13-3 victory over Marshall at Sanford Stadium.

3. NCAA Game Summary - Northwestern at Wisconsin Madison, WI (Sports Network) - Anthony Davis ran for 122 yards and two touchdowns to lead No. 6 Wisconsin over Northwestern, 24-12, to celebrate Homecoming weekend at Camp Randall Stadium.

4. NCAA Top 25 Game Summary - Northwestern at Minnesota The last time Minnesota won four games to start three consecutive seasons was 1934-36...Chris Malleo replaced Basanez for two series in the third quarter for his first career appearance.

5. UConn ousts Marist Sophomore Steve Sealy netted his third winning goal in the last four games, giving Connecticut a 2-1 overtime victory over Marist yesterday in an NCAA Division 1 first-round men's soccer playoff game at Morrone Stadium in Storrs, Conn.

6. United States upsets Germany to move to soccer semifinals Deep into overtime, and maybe the last time for the Fab Five of US women #39;s soccer, the breaks were going against them. A last-gasp goal that stole victory in regulation, a wide-open shot that bounced off the goal post." + ] + }, + "metadata": {}, + "execution_count": 43 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Notice how the data pivots from the start node to the end node. If you've read the [Introducing the Semantic Graph](https://neuml.hashnode.dev/introducing-the-semantic-graph) article, you'll notice how this traversal is more literal in nature. In other words, the relationships are keyword-driven vs contextual." + ], + "metadata": { + "id": "5qv1CbY35uUy" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "This notebook demonstrated how graphs can index traditional indexes such as BM25. This method can also be applied to an external index provided a search function is available to build connections.\n", + "\n", + "Semantic graphs backed by embeddings instances have a number of advantages and are recommended in most cases. But this is a classic way to do it - no machine learning models required!" + ], + "metadata": { + "id": "4L8smyyXc8q8" + } + } + ] +} \ No newline at end of file diff --git a/examples/40_Text_to_Speech_Generation.ipynb b/examples/40_Text_to_Speech_Generation.ipynb new file mode 100644 index 0000000..070d39d --- /dev/null +++ b/examples/40_Text_to_Speech_Generation.ipynb @@ -0,0 +1,478 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "4Pjmz-RORV8E" + }, + "source": [ + "# Text to speech generation\n", + "\n", + "Text To Speech (TTS) models have made great strides in quality over the last few years. Unfortunately, it's not currently possible to use these libraries without installing a large number of dependencies.\n", + "\n", + "The txtai TextToSpeech pipeline has the following objectives:\n", + "\n", + "- Fast performance both on CPU and GPU\n", + "- Ability to batch large text values and stream it through the model\n", + "- Minimal install footprint\n", + "- All dependencies must be Apache 2.0 compatible\n", + "\n", + "This notebook will go through a set of text to speech generation examples.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dk31rbYjSTYm" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies. Since this notebook is using optional pipelines, we need to install the pipeline extras package. We'll also demonstrate running this pipeline as an application." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "id": "XMQuuun2R06J" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline-audio,pipeline-data] onnxruntime-gpu librosa\n", + "\n", + "# Install NLTK\n", + "import nltk\n", + "nltk.download('averaged_perceptron_tagger_eng')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PNPJ95cdTKSS" + }, + "source": [ + "# Create a TextToSpeech instance\n", + "\n", + "The TextToSpeech instance is the main entrypoint for generating speech from text. The pipeline is backed by models from the [ESPnet](https://github.com/espnet/espnet) project. ESPnet has a number of high quality TTS models available on the [Hugging Face Hub](https://huggingface.co/models?library=espnet&pipeline_tag=text-to-speech&sort=downloads).\n", + "\n", + "This pipeline can use the following models on the Hugging Face Hub.\n", + "\n", + "- [ljspeech-jets-onnx](https://huggingface.co/NeuML/ljspeech-jets-onnx)\n", + "- [ljspeech-vits-onnx](https://huggingface.co/NeuML/ljspeech-vits-onnx)\n", + "- [vctk-vits-onnx](https://huggingface.co/NeuML/vctk-vits-onnx)\n", + "\n", + "The default model is `ljspeech-jets-onnx`. Each of the models above are ESPnet models exported to ONNX using [espnet_onnx](https://github.com/espnet/espnet_onnx). More on that process can be found in the links above.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "id": "nTDwXOUeTH2-" + }, + "outputs": [], + "source": [ + "%%capture\n", + "\n", + "from txtai.pipeline import TextToSpeech\n", + "\n", + "# Create text-to-speech model\n", + "tts = TextToSpeech()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-vGR_piwZZO6" + }, + "source": [ + "# Generate speech\n", + "\n", + "The first example shows how to generate speech from text. Let's give it a try!" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 465 + }, + "id": "-K2YJJzsVtfq", + "outputId": "28fd09d8-73e1-4c07-ae71-09397081631c" + }, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "
" + ], + "image/png": "iVBORw0KGgoAAAANSUhEUgAABMkAAAHACAYAAAC4Zz/7AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAACkeElEQVR4nOzdd3gUZdcG8HvTe0IIhBYIvfdepUlV4VOxISp2BRu+voIFVFTsYkFRLKivCCpiQYp0pPfeS+hJCCGd1N3vj5CQsmV2d2aeKffvurgkm9ndI8nuzpznPOdYbDabDURERERERERERCbmIzoAIiIiIiIiIiIi0ZgkIyIiIiIiIiIi02OSjIiIiIiIiIiITI9JMiIiIiIiIiIiMj0myYiIiIiIiIiIyPSYJCMiIiIiIiIiItNjkoyIiIiIiIiIiEyPSTIiIiIiIiIiIjI9P9EByM1qteL8+fMIDw+HxWIRHQ4REREREREREQlks9mQmZmJWrVqwcfHcb2Y4ZJk58+fR1xcnOgwiIiIiIiIiIhIQ86cOYM6deo4/L7hkmTh4eEAiv/HIyIiBEdDREREREREREQiZWRkIC4urjRn5IjhkmQlWywjIiKYJCMiIiIiIiIiIgBw2ZaLjfuJiIiIiIiIiMj0mCQjIiIiIiIiIiLTY5KMiIiIiIiIiIhMj0kyIiIiIiIiIiIyPSbJiIiIiIiIiIjI9JgkIyIiIiIiIiIi02OSjIiIiIiIiIiITI9JMiIiIiIiIiIiMj0myYiIiIiIiIiIyPSYJCMiIiIiIiIiItNjkoyIiIiIiIiIiEyPSTIiIiIiIiIiIjI9JsmIiIiIiIiIiMj0mCQjIiIiIiIiIiLTY5LMZPq9txrztp4WHQYRERERERERkaYwSWYyJ1OyMX35UdFhEBERERERERFpCpNkJnQhPRfZeYWiwyAiIiIiIiIi0gwmyUxq5prjokMgIiIiIiIiItIMJslM6tSlHNEhEBERERERERFpBpNkRERERERERERkekySERERERERERGR6TFJRkREREREREREpsckGRERERERERERmZ6f6ABIHX/sOoftpy6LDoOIiIiIiIiISJOYJDOJp+buEh0CEREREREREZFmcbslERERERERERGZHpNkRERERERERERkekySERERERERERGR6TFJRkREREREREREpsckGRERERERERERmR6TZEREREREREREZHpMkhERERERERERkekxSUZERERERERERKbHJBkREREREREREZkek2RERERERERERGR6TJIREREREREREZHpMUlGRERERERERESmxyQZERERERERERGZHpNkRERERERERERkekySERERERERERGR6TFJRkREREREREREpsckmUn9ufs8TlzMEh0GEREREREREZEmMElmYptOpIoOgYiIiIiIiIhIE5gkIyIiIiIiIiIi02OSzMT2nksXHQIRERERERERkSYwSWYCS/Yl2r39py2ncSH9isrREBERERERERFpjypJshkzZiA+Ph5BQUHo2rUrtmzZIul+c+fOhcViwciRI5UN0OA+X33M4fdy8otUjISIiIiIiIiISJsUT5LNmzcPEyZMwJQpU7Bjxw60bdsWgwcPRnJystP7JSQk4D//+Q969+6tdIiGZ3PyvX+PXFQtDiIiIiIiIiIirVI8SfbBBx/goYcewtixY9GiRQvMnDkTISEh+Oabbxzep6ioCKNHj8arr76KBg0aKB2iqb3y1wHRIRARERERERERCadokiw/Px/bt2/HwIEDrz2hjw8GDhyIjRs3Orzfa6+9hurVq+OBBx5w+Rx5eXnIyMgo94fKyy+0ig7BLpvNBqvVWZ0bEREREREREZE6FE2SpaSkoKioCLGxseVuj42NRWKi/Wby69atw9dff41Zs2ZJeo5p06YhMjKy9E9cXJzXcRtNzcgg0SHYNXH+Xtw6c4PoMIiIiJw6dSkb7y49hDmbT4sOhYhIFeuPpWDR3guiwyAiUp2f6ADKyszMxJgxYzBr1izExMRIus+kSZMwYcKE0q8zMjKYKNOJedvOiA6BiIjIpbHfbsWJlGwAwF1d6wqOhohIeaO/2gwASHhruOBIiIjUpWiSLCYmBr6+vkhKSip3e1JSEmrUqFHp+OPHjyMhIQE33nhj6W1Wa/FWQT8/Pxw+fBgNGzYsd5/AwEAEBgYqED0RERERShNk9mw5mYrPVx/D1/d2ho+PRcWoiIiIiEhuim63DAgIQMeOHbFixYrS26xWK1asWIHu3btXOr5Zs2bYu3cvdu3aVfrnpptuQr9+/bBr1y5WiClkyh/7RIdARESkS+8tPYxVhy8it7BIdChERERE5CXFt1tOmDAB9957Lzp16oQuXbpg+vTpyM7Oxtix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BpNkJtSmTpToEIiIiIiIiIiINMVPdACkrtljO6NlrUjRYRARERERERERaQqTZCbTt2l10SEQEREREREREWkOt1sSEREREREREZHpMUlGRERERERERESmxyQZERERERERERGZHpNkRERERERERERkekySERERERERERGR6TFJRkREREREREREpsckGRERERERERERmR6TZEREREREREREZHpMkhERERERERERkekxSUZERERERERERKbHJBkREREREREREZkek2RERERERERERGR6TJIREREREREREZHpMUlGRERERERERESm5yc6ALnZbDYAQEZGhuBIiIiIiIiIiIhItJIcUUnOyBHDJckyMzMBAHFxcYIjISIiIiIiIiIircjMzERkZKTD71tsrtJoOmO1WnH+/HmEh4fDYrGIDkcWGRkZiIuLw5kzZxARESE6HCLV8TVAxNcBEV8DZHZ8DRDxdUCes9lsyMzMRK1ateDj47jzmOEqyXx8fFCnTh3RYSgiIiKCbwRkanwNEPF1QMTXAJkdXwNEfB2QZ5xVkJVg434iIiIiIiIiIjI9JsmIiIiIiIiIiMj0mCTTgcDAQEyZMgWBgYGiQyESgq8BIr4OiPgaILPja4CIrwNSnuEa9xMREREREREREbmLlWRERERERERERGR6TJIREREREREREZHpMUlGRERERERERESmxyQZERERERERERGZHpNkOjBjxgzEx8cjKCgIXbt2xZYtW0SHRKSKtWvX4sYbb0StWrVgsVjw+++/iw6JSFXTpk1D586dER4ejurVq2PkyJE4fPiw6LCIVPX555+jTZs2iIiIQEREBLp3747FixeLDotImLfeegsWiwVPP/206FCIVPHKK6/AYrGU+9OsWTPRYZFBMUmmcfPmzcOECRMwZcoU7NixA23btsXgwYORnJwsOjQixWVnZ6Nt27aYMWOG6FCIhFizZg3GjRuHTZs2YdmyZSgoKMCgQYOQnZ0tOjQi1dSpUwdvvfUWtm/fjm3btqF///4YMWIE9u/fLzo0ItVt3boVX3zxBdq0aSM6FCJVtWzZEhcuXCj9s27dOtEhkUFZbDabTXQQ5FjXrl3RuXNnfPrppwAAq9WKuLg4PPHEE5g4caLg6IjUY7FYsGDBAowcOVJ0KETCXLx4EdWrV8eaNWvQp08f0eEQCRMdHY13330XDzzwgOhQiFSTlZWFDh064LPPPsPrr7+Odu3aYfr06aLDIlLcK6+8gt9//x27du0SHQqZACvJNCw/Px/bt2/HwIEDS2/z8fHBwIEDsXHjRoGRERGRCOnp6QCKEwREZlRUVIS5c+ciOzsb3bt3Fx0OkarGjRuH4cOHl7s2IDKLo0ePolatWmjQoAFGjx6N06dPiw6JDMpPdADkWEpKCoqKihAbG1vu9tjYWBw6dEhQVEREJILVasXTTz+Nnj17olWrVqLDIVLV3r170b17d+Tm5iIsLAwLFixAixYtRIdFpJq5c+dix44d2Lp1q+hQiFTXtWtXzJ49G02bNsWFCxfw6quvonfv3ti3bx/Cw8NFh0cGwyQZERGRDowbNw779u1jDw4ypaZNm2LXrl1IT0/Hr7/+invvvRdr1qxhooxM4cyZM3jqqaewbNkyBAUFiQ6HSHVDhw4t/XubNm3QtWtX1KtXDz///DO33ZPsmCTTsJiYGPj6+iIpKanc7UlJSahRo4agqIiISG3jx4/HwoULsXbtWtSpU0d0OESqCwgIQKNGjQAAHTt2xNatW/HRRx/hiy++EBwZkfK2b9+O5ORkdOjQofS2oqIirF27Fp9++iny8vLg6+srMEIidUVFRaFJkyY4duyY6FDIgNiTTMMCAgLQsWNHrFixovQ2q9WKFStWsA8HEZEJ2Gw2jB8/HgsWLMDKlStRv3590SERaYLVakVeXp7oMIhUMWDAAOzduxe7du0q/dOpUyeMHj0au3btYoKMTCcrKwvHjx9HzZo1RYdCBsRKMo2bMGEC7r33XnTq1AldunTB9OnTkZ2djbFjx4oOjUhxWVlZ5VaITp48iV27diE6Ohp169YVGBmROsaNG4c5c+bgjz/+QHh4OBITEwEAkZGRCA4OFhwdkTomTZqEoUOHom7dusjMzMScOXOwevVqLF26VHRoRKoIDw+v1IsyNDQUVatWZY9KMoX//Oc/uPHGG1GvXj2cP38eU6ZMga+vL+68807RoZEBMUmmcbfffjsuXryIyZMnIzExEe3atcOSJUsqNfMnMqJt27ahX79+pV9PmDABAHDvvfdi9uzZgqIiUs/nn38OAOjbt2+527/99lvcd9996gdEJEBycjLuueceXLhwAZGRkWjTpg2WLl2K66+/XnRoRESkgrNnz+LOO+/EpUuXUK1aNfTq1QubNm1CtWrVRIdGBmSx2Ww20UEQERERERERERGJxJ5kRERERERERERkekySERERERERERGR6TFJRkREREREREREpsckGRERERERERERmR6TZEREREREREREZHpMkhERERERERERkekxSUZERERERERERKbHJBkRERGRTt13330YOXKk6DCIiIiIDMFPdABEREREVJnFYnH6/SlTpuCjjz6CzWZTKSIiIiIiY2OSjIiIiEiDLly4UPr3efPmYfLkyTh8+HDpbWFhYQgLCxMRGhEREZEhcbslERERkQbVqFGj9E9kZCQsFku528LCwiptt+zbty+eeOIJPP3006hSpQpiY2Mxa9YsZGdnY+zYsQgPD0ejRo2wePHics+1b98+DB06FGFhYYiNjcWYMWOQkpKi8v8xERERkVhMkhEREREZyHfffYeYmBhs2bIFTzzxBB577DGMGjUKPXr0wI4dOzBo0CCMGTMGOTk5AIC0tDT0798f7du3x7Zt27BkyRIkJSXhtttuE/x/QkRERKQuJsmIiIiIDKRt27Z46aWX0LhxY0yaNAlBQUGIiYnBQw89hMaNG2Py5Mm4dOkS9uzZAwD49NNP0b59e7z55pto1qwZ2rdvj2+++QarVq3CkSNHBP/fEBEREamHPcmIiIiIDKRNmzalf/f19UXVqlXRunXr0ttiY2MBAMnJyQCA3bt3Y9WqVXb7mx0/fhxNmjRROGIiIiIibWCSjIiIiMhA/P39y31tsVjK3VYyNdNqtQIAsrKycOONN+Ltt9+u9Fg1a9ZUMFIiIiIibWGSjIiIiMjEOnTogPnz5yM+Ph5+fjw1JCIiIvNiTzIiIiIiExs3bhxSU1Nx5513YuvWrTh+/DiWLl2KsWPHoqioSHR4RERERKphkoyIiIjIxGrVqoX1/9+eHRQBEMJAEMyhACPYQA8mkUVxMnhst4J9piZ71zmn5pw1xqi1VvXeqzWnIgCQ47v33tcjAAAAAOAl70EAAAAA4olkAAAAAMQTyQAAAACIJ5IBAAAAEE8kAwAAACCeSAYAAABAPJEMAAAAgHgiGQAAAADxRDIAAAAA4olkAAAAAMQTyQAAAACIJ5IBAAAAEO8HzDi1ivBSs0EAAAAASUVORK5CYII=\n" + }, + "metadata": {} + } + ], + "source": [ + "import librosa.display\n", + "import matplotlib.pyplot as plt\n", + "\n", + "text = \"Text To Speech models have made great strides in quality over the last few years.\"\n", + "\n", + "# Generate raw waveform speech\n", + "speech, rate = tts(text), 22050\n", + "\n", + "# Print waveplot\n", + "plt.figure(figsize=(15, 5))\n", + "plot = librosa.display.waveshow(speech[0], sr=speech[1])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ARFO5J46SJyj" + }, + "source": [ + "The graph shows a plot of the audio. It clearly shows pauses between words and sentences as we would expect in spoken language. Now let's play the generated speech." + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 60 + }, + "id": "GpNhbItq3QGL", + "outputId": "c85abf97-1b39-4938-f4e3-c4f415b3b132" + }, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + " " + ] + }, + "metadata": {} + } + ], + "source": [ + "from IPython.display import Audio, display\n", + "\n", + "import os\n", + "\n", + "import soundfile as sf\n", + "\n", + "def play(speech):\n", + " # Convert to MP3 to save space\n", + " sf.write(\"speech.wav\", speech[0], speech[1])\n", + " !ffmpeg -i speech.wav -y -b:a 64 speech.mp3 2> /dev/null\n", + "\n", + " # Play speech\n", + " display(Audio(filename=\"speech.mp3\"))\n", + "\n", + "play(speech)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "bDxW-tsCELob" + }, + "source": [ + "# Transcribe audio back to text\n", + "\n", + "Next we'll use [OpenAI Whisper](https://github.com/openai/whisper) to transcribe the generated audio back to text." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 70 + }, + "id": "-KgYwAQzFVll", + "outputId": "47857ba8-0942-42f9-ee8a-fde0abe5140a" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "'Text to speech models have made great strides in quality over the last few years.'" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + } + }, + "metadata": {}, + "execution_count": 48 + } + ], + "source": [ + "from txtai.pipeline import Transcription\n", + "\n", + "# Transcribe files\n", + "transcribe = Transcription(\"openai/whisper-base\")\n", + "\n", + "# Print result\n", + "transcribe(speech, rate)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lmKDL32ySfXl" + }, + "source": [ + "And as expected, the transcription matches the original text." + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Streaming speech generation\n", + "\n", + "The TextToSpeech pipeline supports incrementally generating snippets of speech. This enables the pipeline to work with streaming LLM generation." + ], + "metadata": { + "id": "oI2bCz0kBDSO" + } + }, + { + "cell_type": "code", + "source": [ + "text = \"This is streaming speech generation. It's designed to take output tokens from a streaming LLM. It returns snippets of speech.\".split()\n", + "for speech, _ in tts(text, stream=True):\n", + " print(speech.shape)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "QGpF2T8ce0Md", + "outputId": "4f8eefb3-eb46-4bdf-ce45-bd752d9a3822" + }, + "execution_count": 49, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(32768,)\n", + "(31488,)\n", + "(26368,)\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8miGM2xKSkq_" + }, + "source": [ + "# Audio books\n", + "\n", + "The TextToSpeech pipeline is designed to work with large blocks of text. It could be used to build audio for entire chapters of books.\n", + "\n", + "In the next example below, we'll read the beginning of the book the `Great Gatsby`. We'll load a new model that enables setting a speaker." + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 60 + }, + "id": "liLFTAAvOWpi", + "outputId": "76210dec-7b43-4383-ebd0-6320c71d42d4" + }, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + " " + ] + }, + "metadata": {} + } + ], + "source": [ + "# Beginning of The Great Gatsby from Project Gutenberg\n", + "# https://www.gutenberg.org/ebooks/64317\n", + "\n", + "text = \"\"\"\n", + "In my younger and more vulnerable years my father gave me some advice\n", + "that I've been turning over in my mind ever since.\n", + "\n", + "“Whenever you feel like criticizing anyone,” he told me, “just\n", + "remember that all the people in this world haven't had the advantages\n", + "that you've had.”\n", + "\n", + "He didn't say any more, but we've always been unusually communicative\n", + "in a reserved way, and I understood that he meant a great deal more\n", + "than that.\n", + "\"\"\"\n", + "\n", + "tts = TextToSpeech(\"neuml/vctk-vits-onnx\")\n", + "speech = tts(text, speaker=3)\n", + "play(speech)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "NsmhYciqTAX6" + }, + "source": [ + "# Text To Speech Workflow\n", + "\n", + "In the last example, we'll cover building a text-to-speech workflow. This workflow is no different in that it connects multiple pipelines together, each of which are backed by machine learning models.\n", + "\n", + "The workflow extracts text from a webpage, summarizes it and then generates audio of the summary." + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "TAv7XVgzHb_4", + "outputId": "1d88f847-5773-4be3-8f6c-ed33e49ef548" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Overwriting workflow.yml\n" + ] + } + ], + "source": [ + "%%writefile workflow.yml\n", + "summary:\n", + " path: sshleifer/distilbart-cnn-12-6\n", + "\n", + "textractor:\n", + " join: true\n", + " lines: false\n", + " minlength: 100\n", + " paragraphs: true\n", + " sentences: false\n", + "\n", + "texttospeech:\n", + " path: neuml/vctk-vits-onnx\n", + "\n", + "workflow:\n", + " tts:\n", + " tasks:\n", + " - action: textractor\n", + " task: url\n", + " - action: summary\n", + " - action: texttospeech\n", + " args:\n", + " speaker: 15" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 115 + }, + "id": "_dmQZ6i8IDAA", + "outputId": "47bda0fc-c7df-42d3-d540-834576c806f8" + }, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + " " + ] + }, + "metadata": {} + } + ], + "source": [ + "from txtai.app import Application\n", + "\n", + "app = Application(\"workflow.yml\")\n", + "\n", + "speech = list(app.workflow(\"tts\", [\"https://en.wikipedia.org/wiki/Natural_language_processing\"]))[0]\n", + "\n", + "play(speech)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "VCU8zGGDXQ0Y" + }, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook gave a brief introduction on text to speech models. The text to speech pipeline in txtai is designed to be easy to use and handles the most common text to speech tasks in English. \n", + "\n", + "This work is made possible by the excellent advancements in text to speech modeling. [ESPnet](https://github.com/espnet/espnet) is a great project and should be checked out for more advanced and a wider range of use cases. This pipeline was also made possible by the great work from [espnet_onnx](https://github.com/espnet/espnet_onnx) in building a framework to export models to ONNX.\n", + "\n", + "Looking forward to seeing what the community dreams up using this pipeline!\n", + "\n" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "provenance": [] + }, + "gpuClass": "standard", + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/examples/41_Train_a_language_model_from_scratch.ipynb b/examples/41_Train_a_language_model_from_scratch.ipynb new file mode 100644 index 0000000..dbfeefe --- /dev/null +++ b/examples/41_Train_a_language_model_from_scratch.ipynb @@ -0,0 +1,499 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "gpuClass": "standard", + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Train a language model from scratch\n", + "\n", + "txtai has a robust training pipeline that can fine-tune large language models (LLMs) for downstream tasks such as labeling text. txtai also has the ability to train language models from scratch.\n", + "\n", + "The vast majority of time, fine-tuning a LLM yields the best results. But when making significant changes to the structure of a model, training from scratch is often required.\n", + "\n", + "Examples of significant changes are:\n", + "\n", + "- Changing the vocabulary size\n", + "- Changing the number of hidden dimensions\n", + "- Changing the number of attention heads or layers\n", + "- Create a custom model architecture\n", + "\n", + "This notebook will show how to build a new tokenizer and train a small language model (known as a micromodel) from scratch.\n" + ], + "metadata": { + "id": "-xU9P9iSR-Cy" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ], + "metadata": { + "id": "shlUi2kKS7KT" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "xEvX9vCpn4E0" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline-train] datasets sentence-transformers onnxruntime onnx" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Load dataset\n", + "\n", + "This example will use the `ag_news` dataset, which is a collection of news article headlines." + ], + "metadata": { + "id": "408IyXzKFSiG" + } + }, + { + "cell_type": "code", + "source": [ + "from datasets import load_dataset\n", + "\n", + "dataset = load_dataset(\"ag_news\", split=\"train\")" + ], + "metadata": { + "id": "IQ_ns6YvFRm1" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Train the tokenizer\n", + "\n", + "The first step is to train the tokenizer. We could use an existing tokenizer but in this case, we want a smaller vocabulary.\n" + ], + "metadata": { + "id": "-vNVSA2FQnKj" + } + }, + { + "cell_type": "code", + "source": [ + "from transformers import AutoTokenizer\n", + "\n", + "def stream(batch=10000):\n", + " for x in range(0, len(dataset), batch):\n", + " yield dataset[x: x + batch][\"text\"]\n", + "\n", + "tokenizer = AutoTokenizer.from_pretrained(\"bert-base-uncased\")\n", + "tokenizer = tokenizer.train_new_from_iterator(stream(), vocab_size=500, length=len(dataset))\n", + "tokenizer.model_max_length = 512\n", + "\n", + "tokenizer.save_pretrained(\"bert\")" + ], + "metadata": { + "id": "LJ2FskiiQ_l_" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Let's test the tokenizer." + ], + "metadata": { + "id": "BOW5JlxYS3Rm" + } + }, + { + "cell_type": "code", + "source": [ + "print(tokenizer.tokenize(\"Red Sox defeat Yankees 5-3\"))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "slLtmzfbRuf6", + "outputId": "2391b58b-7428-49a2-9225-0268a1f2ad64" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "['re', '##d', 'so', '##x', 'de', '##f', '##e', '##at', 'y', '##ank', '##e', '##es', '5', '-', '3']\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "With a limited vocabulary size of 500, most words require multiple tokens. This limited vocabulary lowers the number of token representations the model needs to learn." + ], + "metadata": { + "id": "IozRdXdEqegD" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Train the language model\n", + "\n", + "Now it's time to train the model. We'll train a micromodel, which is an extremely small language model with a limited vocabulary. Micromodels, when paired with a limited vocabulary have the potential to work in limited compute environments like edge devices and microcontrollers." + ], + "metadata": { + "id": "gqEBeBEoTrup" + } + }, + { + "cell_type": "code", + "source": [ + "from transformers import AutoTokenizer, BertConfig, BertForMaskedLM\n", + "\n", + "from txtai.pipeline import HFTrainer\n", + "\n", + "config = BertConfig(\n", + " vocab_size = 500,\n", + " hidden_size = 50,\n", + " num_hidden_layers = 2,\n", + " num_attention_heads = 2,\n", + " intermediate_size = 100,\n", + ")\n", + "\n", + "model = BertForMaskedLM(config)\n", + "model.save_pretrained(\"bert\")\n", + "tokenizer = AutoTokenizer.from_pretrained(\"bert\")\n", + "\n", + "train = HFTrainer()\n", + "\n", + "# Train model\n", + "train((model, tokenizer), dataset, task=\"language-modeling\", output_dir=\"bert\",\n", + " fp16=True, per_device_train_batch_size=128, num_train_epochs=10,\n", + " dataloader_num_workers=2)" + ], + "metadata": { + "id": "MEpZAr0TUMCK" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Sentence embeddings\n", + "\n", + "Next let's take the language model and fine-tune it to build sentence embeddings. " + ], + "metadata": { + "id": "53bvB9c6MbPS" + } + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "!wget https://raw.githubusercontent.com/UKPLab/sentence-transformers/master/examples/training/nli/training_nli_v2.py\n", + "!python training_nli_v2.py bert\n", + "!mv output/* bert-nli" + ], + "metadata": { + "id": "f11f5tjfS85m" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Embeddings search\n", + "\n", + "Now we'll build a txtai embeddings index using the fine-tuned model. We'll index the `ag_news` dataset. " + ], + "metadata": { + "id": "FTOm5ofaMmcv" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai.embeddings import Embeddings\n", + "\n", + "# Get list of all text\n", + "texts = dataset[\"text\"]\n", + "\n", + "embeddings = Embeddings({\"path\": \"bert-nli\", \"content\": True})\n", + "embeddings.index((x, text, None) for x, text in enumerate(texts))" + ], + "metadata": { + "id": "_kKe5kRnVRhM" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Let's run a search and see how much the model has learned." + ], + "metadata": { + "id": "Rh9yA6ZJM47H" + } + }, + { + "cell_type": "code", + "source": [ + "embeddings.search(\"Boston Red Sox Cardinals World Series\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "XCRhpfLmV1-q", + "outputId": "662f1b1d-fcf5-4383-fd8a-369081a77501" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '76733',\n", + " 'text': 'Red Sox sweep Cardinals to win World Series The Boston Red Sox ended their 86-year championship drought with a 3-0 win over the St. Louis Cardinals in Game Four of the World Series.',\n", + " 'score': 0.8008379936218262},\n", + " {'id': '71169',\n", + " 'text': 'Red Sox lead 2-0 over Cardinals of World Series The host Boston Red Sox scored a 6-2 victory over the St. Louis Cardinals, helped by Curt Schilling #39;s pitching through pain and seeping blood, in World Series Game 2 on Sunday night.',\n", + " 'score': 0.7896029353141785},\n", + " {'id': '70100',\n", + " 'text': 'Sports: Red Sox 9 Cardinals 7 after 7 innings BOSTON Boston has scored twice in the seventh inning to take an 9-to-7 lead over the St. Louis Cardinals in the World Series opener at Fenway Park.',\n", + " 'score': 0.7735188603401184}]" + ] + }, + "metadata": {}, + "execution_count": 49 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Not too bad. It's far from perfect but we can tell that it has some knowledge! This model was trained for 5 minutes, there is certainly room for improvement in training longer and/or with a larger dataset.\n", + "\n", + "The standard `bert-base-uncased` model has 110M parameters and is around 440MB. Let's see how many parameters this model has." + ], + "metadata": { + "id": "M5Pk1spcM72L" + } + }, + { + "cell_type": "code", + "source": [ + "# Show number of parameters\n", + "parameters = sum(p.numel() for p in embeddings.model.model.parameters())\n", + "print(f\"Number of parameters:\\t\\t{parameters:,}\")\n", + "print(f\"% of bert-base-uncased\\t\\t{(parameters / 110000000) * 100:.2f}%\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "RIEnDwuxeakq", + "outputId": "131930f1-e6cd-4b23-b9dc-a62e670eb8e4" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Number of parameters:\t\t94,450\n", + "% of bert-base-uncased\t\t0.09%\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "!ls -lh bert-nli/pytorch_model.bin" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ATg4aInQeRN-", + "outputId": "a6181fbc-ee6c-426e-882e-1d6cbefa22a0" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "-rw-r--r-- 1 root root 386K Jan 11 20:52 bert-nli/pytorch_model.bin\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "This model is 386KB and has only 0.1% of the parameters. With proper vocabulary selection, a small language model has potential." + ], + "metadata": { + "id": "7GfsF8ziNFJa" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Quantization\n", + "\n", + "If 386KB isn't small enough, we can quantize the model to get it down even further. " + ], + "metadata": { + "id": "CcbJNidNwuXt" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai.pipeline import HFOnnx\n", + "\n", + "onnx = HFOnnx()\n", + "onnx(\"bert-nli\", task=\"pooling\", output=\"bert-nli.onnx\", quantize=True)" + ], + "metadata": { + "id": "IYZnex9kRcb0" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "embeddings = Embeddings({\"path\": \"bert-nli.onnx\", \"tokenizer\": \"bert-nli\", \"content\": True})\n", + "embeddings.index((x, text, None) for x, text in enumerate(texts))\n", + "embeddings.search(\"Boston Red Sox Cardinals World Series\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "QL_1UosIVkZ7", + "outputId": "6b2bedb8-5cc6-44b6-855a-617a6a07478c" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '76733',\n", + " 'text': 'Red Sox sweep Cardinals to win World Series The Boston Red Sox ended their 86-year championship drought with a 3-0 win over the St. Louis Cardinals in Game Four of the World Series.',\n", + " 'score': 0.8008379936218262},\n", + " {'id': '71169',\n", + " 'text': 'Red Sox lead 2-0 over Cardinals of World Series The host Boston Red Sox scored a 6-2 victory over the St. Louis Cardinals, helped by Curt Schilling #39;s pitching through pain and seeping blood, in World Series Game 2 on Sunday night.',\n", + " 'score': 0.7896029353141785},\n", + " {'id': '70100',\n", + " 'text': 'Sports: Red Sox 9 Cardinals 7 after 7 innings BOSTON Boston has scored twice in the seventh inning to take an 9-to-7 lead over the St. Louis Cardinals in the World Series opener at Fenway Park.',\n", + " 'score': 0.7735188603401184}]" + ] + }, + "metadata": {}, + "execution_count": 50 + } + ] + }, + { + "cell_type": "code", + "source": [ + "!ls -lh bert-nli.onnx" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "oCa3RDeInCkN", + "outputId": "89cf379a-09cf-4fbc-8568-4d66085450f3" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "-rw-r--r-- 1 root root 187K Jan 11 20:53 bert-nli.onnx\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "We're down to 187KB with a quantized model!\n" + ], + "metadata": { + "id": "r95T1HZXVhnZ" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Train on BERT dataset\n", + "\n", + "The [BERT paper](https://arxiv.org/abs/1810.04805) has all the information regarding training parameters and datasets used. Hugging Face Datasets hosts the `bookcorpus` and `wikipedia` datasets.\n", + "\n", + "Training on this size of a dataset is out of scope for this notebook but example code is shown below on how to build the BERT dataset.\n", + "\n", + "```python\n", + "bookcorpus = load_dataset(\"bookcorpus\", split=\"train\")\n", + "wiki = load_dataset(\"wikipedia\", \"20220301.en\", split=\"train\")\n", + "wiki = wiki.remove_columns([col for col in wiki.column_names if col != \"text\"])\n", + "dataset = concatenate_datasets([bookcorpus, wiki])\n", + "```\n", + "\n", + "Then the same steps to train the tokenizer and model can be run. The dataset is 25GB compressed, so it will take some space and time to process!" + ], + "metadata": { + "id": "aPaZsoxnYW8I" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "This notebook covered how to build micromodels from scratch with txtai. Micromodels can be fully rebuilt in hours using the most up-to-date knowledge available. If properly constructed, prepared and trained, micromodels have the potential to be a viable choice for limited resource environments. They can also help when realtime response is more important than having the highest accuracy scores.\n", + "\n", + "It's our hope that further research and exploration into micromodels leads to productive and useful models." + ], + "metadata": { + "id": "4L8smyyXc8q8" + } + } + ] +} diff --git a/examples/42_Prompt_driven_search_with_LLMs.ipynb b/examples/42_Prompt_driven_search_with_LLMs.ipynb new file mode 100644 index 0000000..3f901d3 --- /dev/null +++ b/examples/42_Prompt_driven_search_with_LLMs.ipynb @@ -0,0 +1,401 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "vwELCooy4ljr" + }, + "source": [ + "# Prompt-driven search with LLMs\n", + "\n", + "This notebook revisits the RAG pipeline, which has been covered in a number of previous notebooks. This pipeline is a combination of a similarity instance (embeddings or similarity pipeline) to build a question context and a model that answers questions.\n", + "\n", + "The RAG pipeline recently underwent a number of major upgrades to support the following.\n", + "\n", + "- Ability to run embeddings searches. Given that content is supported, text can be retrieved from the embeddings instance.\n", + "- In addition to extractive qa, support text generation models, sequence to sequence models and custom pipelines\n", + "\n", + "These changes enable embeddings-guided and prompt-driven search with Large Language Models (LLMs) 🔥🔥🔥" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ew7orE2O441o" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "LPQTb25tASIG" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai datasets" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_YnqorRKAbLu" + }, + "source": [ + "# Create Embeddings and RAG instances\n", + "\n", + "An Embeddings instance defines methods to represent text as vectors and build vector indexes for search.\n", + "\n", + "The RAG pipeline is a combination of a similarity instance (embeddings or similarity pipeline) to build a question context and a model that answers questions. The model can be a prompt-driven large language model (LLM), an extractive question-answering model or a custom pipeline.\n", + "\n", + "Let's run a basic example.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "id": "OUc9gqTyAYnm" + }, + "outputs": [], + "source": [ + "%%capture\n", + "\n", + "from txtai import Embeddings, RAG\n", + "\n", + "# Create embeddings model with content support\n", + "embeddings = Embeddings(path=\"sentence-transformers/all-MiniLM-L6-v2\", content=True)\n", + "\n", + "# Create the RAG pipeline\n", + "rag = RAG(embeddings, \"Qwen/Qwen3-4B-Instruct-2507\", template=\"\"\"\n", + " Answer the following question using the provided context.\n", + "\n", + " Question:\n", + " {question}\n", + "\n", + " Context:\n", + " {context}\n", + "\"\"\")" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "4X5z3UjnAGe7", + "outputId": "cacb7e9d-471a-437d-c68d-a8b51f876413" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "---- Red Sox - Blue Jays ----\n", + "{'answer': 'The Blue Jays won the game.'}\n", + "{'answer': 'The score was 2-1 in favor of the Blue Jays.'}\n", + "\n", + "---- Phillies - Braves ----\n", + "{'answer': 'The Phillies won the game.'}\n", + "{'answer': 'The score was 5-0 in favor of the Phillies.'}\n", + "\n", + "---- Dodgers - Giants ----\n", + "{'answer': 'The Giants won the game.'}\n", + "{'answer': 'The score was Giants 5, Dodgers 4.'}\n", + "\n", + "---- Flyers - Lightning ----\n", + "{'answer': 'The Flyers won the game.'}\n", + "{'answer': 'The score was Flyers 4, Lightning 1.'}\n", + "\n" + ] + } + ], + "source": [ + "data = [\"Giants hit 3 HRs to down Dodgers\",\n", + " \"Giants 5 Dodgers 4 final\",\n", + " \"Dodgers drop Game 2 against the Giants, 5-4\",\n", + " \"Blue Jays beat Red Sox final score 2-1\",\n", + " \"Red Sox lost to the Blue Jays, 2-1\",\n", + " \"Blue Jays at Red Sox is over. Score: 2-1\",\n", + " \"Phillies win over the Braves, 5-0\",\n", + " \"Phillies 5 Braves 0 final\",\n", + " \"Final: Braves lose to the Phillies in the series opener, 5-0\",\n", + " \"Lightning goaltender pulled, lose to Flyers 4-1\",\n", + " \"Flyers 4 Lightning 1 final\",\n", + " \"Flyers win 4-1\"]\n", + "\n", + "questions = [\"What team won the game?\", \"What was score?\"]\n", + "\n", + "for query in [\"Red Sox - Blue Jays\", \"Phillies - Braves\", \"Dodgers - Giants\", \"Flyers - Lightning\"]:\n", + " print(\"----\", query, \"----\")\n", + " for answer in rag([f\"{query} {x}\" for x in questions], data):\n", + " print(answer)\n", + " print()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "7AnPvSeM3N1Z" + }, + "source": [ + "This code runs a series of questions. First it runs an embeddings filtering query to find the most relevant text. For example, `Red Sox - Blue Jays` finds text related to those teams. Then `What team won the game?` and `What was the score?` are asked.\n", + "\n", + "This logic is the same logic found in Notebook 5 - Extractive QA with txtai but uses prompt-based QA vs extractive QA. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Aj8GoDk331cS" + }, + "source": [ + "# Embeddings-guided and Prompt-driven Search\n", + "\n", + "Now for the fun stuff. Let's build an embeddings index for the `ag_news` dataset (a set of news stories from the mid 2000s). Then we'll use prompts to ask questions with embeddings results as the context." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "yL716oEZ43t-", + "outputId": "23f4b0e7-a60a-4e89-fb57-06966e6612f8" + }, + "outputs": [ + ], + "source": [ + "from datasets import load_dataset\n", + "\n", + "dataset = load_dataset(\"ag_news\", split=\"train\")\n", + "\n", + "# Create an embeddings index over the dataset\n", + "embeddings = Embeddings(path=\"sentence-transformers/all-MiniLM-L6-v2\", content=True)\n", + "embeddings.index(dataset[\"text\"])\n", + "\n", + "# Create RAG instance\n", + "rag = RAG(embeddings, \"Qwen/Qwen3-4B-Instruct-2507\", template=\"\"\"\n", + " Answer the following question using the provided context.\n", + "\n", + " Question:\n", + " {question}\n", + "\n", + " Context:\n", + " {context}\n", + "\"\"\", output=\"flatten\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Ifl8JwLDBL7k" + }, + "source": [ + "Now let's run a prompt-driven search!" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "5O1WBJ8153Mo", + "outputId": "ddf09da2-7d4c-4fd3-b0da-df5631bacd13" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Who won the 2004 presidential election? George W. Bush won the 2004 presidential election.\n", + "Who did the candidate beat? George W. Bush beat John F. Kerry in the 2004 presidential election.\n" + ] + } + ], + "source": [ + "question = \"Who won the 2004 presidential election?\"\n", + "answer = rag(question)\n", + "print(question, answer)\n", + "\n", + "nquestion = \"Who did the candidate beat?\"\n", + "print(nquestion, rag(f\"{question} {answer}. {nquestion}\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "AhViFXH_BZSo" + }, + "source": [ + "And there are the answers. Let's unpack how this works.\n", + "\n", + "The first thing the RAG pipeline does is run an embeddings search to find the most relevant text within the index. A context string is then built using those search results.\n", + "\n", + "After that, a prompt is generated, run and the answer printed." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "JtDcVPdOB0Rv" + }, + "source": [ + "# Additional examples\n", + "\n", + "Before moving on, a couple more example questions." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "0NNLBwC-83MM", + "outputId": "4b9fabd7-baf9-4f7d-bb8b-c9c60c5101e4" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Who won the World Series in 2004? The Boston Red Sox won the World Series in 2004.\n", + "What team did the Red Sox beat in the World Series? The Boston Red Sox beat the St. Louis Cardinals in the World Series.\n" + ] + } + ], + "source": [ + "question = \"Who won the World Series in 2004?\"\n", + "answer = rag(question)\n", + "print(question, answer)\n", + "\n", + "nquestion = \"What team did the Red Sox beat in the World Series?\"\n", + "print(nquestion, rag(f\"{question} {answer}. {nquestion}\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 36 + }, + "id": "1P0zqkTW9cZW", + "outputId": "4f2232db-761b-464f-b47d-6695c84ffb80" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'An interesting fact is that herrings communicate by farting—a quirky and unusual discovery that was honored with an Ig Nobel Prize for its oddball research.'" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "rag(\"Tell me something interesting\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ygFFcwWPGI9p" + }, + "source": [ + "Whhaaaattt??? Is this a model hallucination?\n", + "\n", + "Let's run an embeddings query and see if that text is in the results." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "qZPhLqSxGMbK", + "outputId": "e3b2909a-1ee8-480f-afa3-201a8e27cb08" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "herrings communicate by farting\n" + ] + } + ], + "source": [ + "answer = \"herrings communicate by farting\"\n", + "for x in embeddings.search(\"Tell me something interesting\"):\n", + " if answer in x[\"text\"]:\n", + " start = x[\"text\"].find(answer)\n", + " print(x[\"text\"][start:start + len(answer)])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IpgxMc1DZcds" + }, + "source": [ + "Sure enough it is 😃" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "KqfvCXp2B3li" + }, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook covered how to run embeddings-guided and prompt-driven search with LLMs. This functionality is a major step forward towards `Generative Semantic Search` for txtai. More to come, stay tuned!" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "provenance": [] + }, + "gpuClass": "standard", + "kernelspec": { + "display_name": "local", + "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", + "version": "3.10.19" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/examples/43_Embeddings_in_the_Cloud.ipynb b/examples/43_Embeddings_in_the_Cloud.ipynb new file mode 100644 index 0000000..ea39e74 --- /dev/null +++ b/examples/43_Embeddings_in_the_Cloud.ipynb @@ -0,0 +1,496 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "accelerator": "GPU", + "gpuClass": "standard" + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "vwELCooy4ljr" + }, + "source": [ + "# Embeddings in the Cloud\n", + "\n", + "Embeddings is the engine that delivers semantic search. Data is transformed into embeddings vectors where similar concepts will produce similar vectors. Indexes both large and small are built with these vectors. The indexes are used to find results that have the same meaning, not necessarily the same keywords. \n", + "\n", + "In addition to local storage, embeddings can be synced with cloud storage. Given that txtai is a fully encapsulated index format, cloud sync is simply a matter of moving a group of files to and from cloud storage. This can be object storage such as AWS S3/Azure Blob/Google Cloud or the [Hugging Face Hub](https://hf.co/models). More details on available options can be found in the [documentation](https://neuml.github.io/txtai/embeddings/configuration/cloud/). There is also an [article](https://medium.com/neuml/serverless-vector-search-with-txtai-96f6163ab972) available that covers how to build and store indexes in cloud object storage.\n", + "\n", + "This notebook will cover an example of loading embeddings indexes from the Hugging Face Hub." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ew7orE2O441o" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "LPQTb25tASIG" + }, + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_YnqorRKAbLu" + }, + "source": [ + "# Integration with the Hugging Face Hub\n", + "\n", + "The Hugging Face Hub has a vast array of models, datasets and example applications available to jumpstart your project. This now includes txtai indexes 🔥🔥🔥\n", + "\n", + "Let's load the embeddings used in the standard [Introducing txtai](https://huggingface.co/NeuML/txtai-intro) example.\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "OUc9gqTyAYnm" + }, + "source": [ + "%%capture\n", + "from txtai.embeddings import Embeddings\n", + "\n", + "# Load the index from the Hub\n", + "embeddings = Embeddings()\n", + "embeddings.load(provider=\"huggingface-hub\", container=\"neuml/txtai-intro\")" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Notice the two fields, `provider` and `container`. The `provider` field tells txtai to look for an index in the Hugging Face Hub. The `container` field sets the target repository." + ], + "metadata": { + "id": "oW5juxjBbrQ-" + } + }, + { + "cell_type": "code", + "metadata": { + "id": "4X5z3UjnAGe7", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "7a052744-c727-4bc9-d892-35b23775011d" + }, + "source": [ + "print(\"%-20s %s\" % (\"Query\", \"Best Match\"))\n", + "print(\"-\" * 50)\n", + "\n", + "# Run an embeddings search for each query\n", + "for query in (\"feel good story\", \"climate change\", \"public health story\", \"war\", \"wildlife\", \"asia\", \"lucky\", \"dishonest junk\"):\n", + " # Get to the top result\n", + " result = embeddings.search(query, 1)[0]\n", + "\n", + " # Print text\n", + " print(\"%-20s %s\" % (query, result[\"text\"]))" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Query Best Match\n", + "--------------------------------------------------\n", + "feel good story Maine man wins $1M from $25 lottery ticket\n", + "climate change Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\n", + "public health story US tops 5 million confirmed virus cases\n", + "war Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "wildlife The National Park Service warns against sacrificing slower friends in a bear attack\n", + "asia Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "lucky Maine man wins $1M from $25 lottery ticket\n", + "dishonest junk Make huge profits without work, earn up to $100,000 a day\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "If you've seen txtai before, this is the classic example. The big difference though is the index was loaded from the Hugging Face Hub instead of being built dynamically." + ], + "metadata": { + "id": "7AnPvSeM3N1Z" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Wikipedia search with txtai\n", + "\n", + "Let's try something more interesting using the [Wikipedia index available on the Hugging Face Hub](https://huggingface.co/NeuML/txtai-wikipedia)" + ], + "metadata": { + "id": "Aj8GoDk331cS" + } + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "from txtai.embeddings import Embeddings\n", + "\n", + "# Load the index from the Hub\n", + "embeddings = Embeddings()\n", + "embeddings.load(provider=\"huggingface-hub\", container=\"neuml/txtai-wikipedia\")" + ], + "metadata": { + "id": "yL716oEZ43t-" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "from IPython.display import HTML, display\n", + "\n", + "def wrap():\n", + " display(HTML(\"\"\"\"\"\"))\n", + "\n", + "get_ipython().events.register('pre_run_cell', wrap)" + ], + "metadata": { + "id": "1V8nrm9IHPQc" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Now run a series of searches to show the kind of data available in this index." + ], + "metadata": { + "id": "Ifl8JwLDBL7k" + } + }, + { + "cell_type": "code", + "source": [ + "import json\n", + "\n", + "for x in embeddings.search(\"Roman Empire\", 1):\n", + " print(json.dumps(x, indent=2))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 196 + }, + "id": "5O1WBJ8153Mo", + "outputId": "f20e0688-54e7-43ce-e2f9-6f11f94026bc" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "{\n", + " \"id\": \"Roman Empire\",\n", + " \"text\": \"The Roman Empire ( ; ) was the post-Republican period of ancient Rome. As a polity, it included large territorial holdings around the Mediterranean Sea in Europe, North Africa, and Western Asia, and was ruled by emperors. From the accession of Caesar Augustus as the first Roman emperor to the military anarchy of the 3rd century, it was a Principate with Italia as the metropole of its provinces and the city of Rome as its sole capital. The Empire was later ruled by multiple emperors who shared control over the Western Roman Empire and the Eastern Roman Empire. The city of Rome remained the nominal capital of both parts until AD 476 when the imperial insignia were sent to Constantinople following the capture of the Western capital of Ravenna by the Germanic barbarians. The adoption of Christianity as the state church of the Roman Empire in AD 380 and the fall of the Western Roman Empire to Germanic kings conventionally marks the end of classical antiquity and the beginning of the Middle Ages. Because of these events, along with the gradual Hellenization of the Eastern Roman Empire, historians distinguish the medieval Roman Empire that remained in the Eastern provinces as the Byzantine Empire.\",\n", + " \"score\": 0.8913329243659973\n", + "}\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "for x in embeddings.search(\"How does a car engine work\", 1):\n", + " print(json.dumps(x, indent=2))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 160 + }, + "id": "V4_tI2urWyZ3", + "outputId": "3101db94-2ad8-4ea9-f646-411f57b3027b" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "{\n", + " \"id\": \"Internal combustion engine\",\n", + " \"text\": \"An internal combustion engine (ICE or IC engine) is a heat engine in which the combustion of a fuel occurs with an oxidizer (usually air) in a combustion chamber that is an integral part of the working fluid flow circuit. In an internal combustion engine, the expansion of the high-temperature and high-pressure gases produced by combustion applies direct force to some component of the engine. The force is typically applied to pistons (piston engine), turbine blades (gas turbine), a rotor (Wankel engine), or a nozzle (jet engine). This force moves the component over a distance, transforming chemical energy into kinetic energy which is used to propel, move or power whatever the engine is attached to. This replaced the external combustion engine for applications where the weight or size of an engine was more important.\",\n", + " \"score\": 0.8664469122886658\n", + "}\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "for x in embeddings.search(\"Who won the World Series in 2022?\", 1):\n", + " print(json.dumps(x, indent=2))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 142 + }, + "id": "N1owMgfqW6ZO", + "outputId": "04699cc3-6127-4a76-83c0-b105ce1924b2" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "{\n", + " \"id\": \"2022 World Series\",\n", + " \"text\": \"The 2022 World Series was the championship series of Major League Baseball's (MLB) 2022 season. The 118th edition of the World Series, it was a best-of-seven playoff between the American League (AL) champion Houston Astros and the National League (NL) champion Philadelphia Phillies. The Astros defeated the Phillies in six games to earn their second championship. The series was broadcast in the United States on Fox television and ESPN Radio. \",\n", + " \"score\": 0.8889098167419434\n", + "}\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "for x in embeddings.search(\"What was New York called under the Dutch?\", 1):\n", + " print(json.dumps(x, indent=2))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 178 + }, + "id": "wqIAc67RXdpR", + "outputId": "37c273fa-c3e5-4d91-dbfe-3d09d31be127" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "{\n", + " \"id\": \"Dutch Americans in New York City\",\n", + " \"text\": \"Dutch people have had a continuous presence in New York City for nearly 400 years, being the earliest European settlers. New York City traces its origins to a trading post founded on the southern tip of Manhattan Island by Dutch colonists in 1624. The settlement was named New Amsterdam in 1626 and was chartered as a city in 1653. Because of the history of Dutch colonization, Dutch culture, politics, law, architecture, and language played a formative role in shaping the culture of the city. The Dutch were the majority in New York City until the early 1700s and the Dutch language was commonly spoken until the mid to late-1700s. Many places and institutions in New York City still bear a colonial Dutch toponymy, including Brooklyn (Breukelen), Harlem (Haarlem), Wall Street (Waal Straat), The Bowery (bouwerij (\\u201cfarm\\u201d), and Coney Island (conyne).\",\n", + " \"score\": 0.8840358853340149\n", + "}\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "It's now probably clear how these results can be combined with another component (such as a LLM prompt) to build a conversational QA-based system!" + ], + "metadata": { + "id": "RwrCxFJJczW-" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Filter by popularity\n", + "\n", + "Let's try one last query. This is a generic query where there are a lot of matching results with similarity search alone." + ], + "metadata": { + "id": "kg6P_NAhY2FL" + } + }, + { + "cell_type": "code", + "source": [ + "for x in embeddings.search(\"Boston\", 1):\n", + " print(json.dumps(x, indent=2))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 124 + }, + "id": "h7BsoJC3CFOq", + "outputId": "1e8f2ff2-6847-460f-b5b8-9e546fd7bf62" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "{\n", + " \"id\": \"Boston (song)\",\n", + " \"text\": \"\\\"Boston\\\" is a song by American rock band Augustana, from their debut album All the Stars and Boulevards (2005). It was originally produced in 2003 by Jon King for their demo, Midwest Skies and Sleepless Mondays, and was later re-recorded with producer Brendan O'Brien for All the Stars and Boulevards.\",\n", + " \"score\": 0.8729256987571716\n", + "}\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "While the result is about Boston, it's not the most popular result. This is where the `percentile` field comes in to help. The results can be filtered based on the number of page views.\n", + "\n" + ], + "metadata": { + "id": "EeC8PjQLY8G5" + } + }, + { + "cell_type": "code", + "source": [ + "for x in embeddings.search(\"SELECT id, text, score, percentile FROM txtai WHERE similar('Boston') AND percentile >= 0.99\", 1):\n", + " print(json.dumps(x, indent=2))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 196 + }, + "id": "NWvENgWNU23V", + "outputId": "7008c6e5-8fbc-4275-fa6a-8d32d7ea3fbd" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "{\n", + " \"id\": \"Boston\",\n", + " \"text\": \"Boston, officially the City of Boston, is the state capital and most populous city of the Commonwealth of Massachusetts, as well as the cultural and financial center of the New England region of the United States. It is the 24th-most populous city in the country. The city boundaries encompass an area of about and a population of 675,647 as of 2020. It is the seat of Suffolk County (although the county government was disbanded on July 1, 1999). The city is the economic and cultural anchor of a substantially larger metropolitan area known as Greater Boston, a metropolitan statistical area (MSA) home to a census-estimated 4.8\\u00a0million people in 2016 and ranking as the tenth-largest MSA in the country. A broader combined statistical area (CSA), generally corresponding to the commuting area and including Providence, Rhode Island, is home to approximately 8.2\\u00a0million people, making it the sixth most populous in the United States.\",\n", + " \"score\": 0.8668985366821289,\n", + " \"percentile\": 0.9999025135905505\n", + "}\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "This query adds an additional filter to only match results for the Top 1% of visited Wikipedia pages. " + ], + "metadata": { + "id": "dn2wGwTuZJ4A" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "This notebook covered how to load embeddings indexes from cloud storage. The Hugging Face Hub is a great resource for sharing models, datasets, example applications and now txtai embeddings indexes. This is especially useful when indexing time is long or requires significant GPU resources.\n", + "\n", + "Looking forward to seeing what embeddings indexes the community shares in the coming months!" + ], + "metadata": { + "id": "KqfvCXp2B3li" + } + } + ] +} \ No newline at end of file diff --git a/examples/44_Prompt_templates_and_task_chains.ipynb b/examples/44_Prompt_templates_and_task_chains.ipynb new file mode 100644 index 0000000..dc3019b --- /dev/null +++ b/examples/44_Prompt_templates_and_task_chains.ipynb @@ -0,0 +1,439 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "vwELCooy4ljr" + }, + "source": [ + "# Prompt templates and task chains\n", + "\n", + "txtai has long had support for workflows. Workflows connect the input and outputs of machine learning models together to create powerful transformation and processing functions.\n", + "\n", + "There has been a recent surge in interest in \"model prompting\", which is the process of building a natural language description of a task and passing it to a large language model (LLM). txtai has recently improved support for task templating, which builds string outputs from a set of parameters.\n", + "\n", + "This notebook demonstrates how txtai workflows can be used to apply prompt templates and chain those tasks together." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ew7orE2O441o" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "LPQTb25tASIG" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[api]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_YnqorRKAbLu" + }, + "source": [ + "# Prompt workflow\n", + "\n", + "First, we'll look at building a workflow with a series of model prompts. This workflow creates a conditional translation using a statement and target language. Another task reads that output text and detects the language.\n", + "\n", + "This workflow uses a LLM pipeline. The LLM pipeline loads a local model for inference, in this case [Qwen3-4B-Instruct-2507](https://huggingface.co/Qwen/Qwen3-4B-Instruct-2507). The [LLM pipeline](https://neuml.github.io/txtai/pipeline/llm/llm) supports local transformers models, llama.cpp models and LLM APIs such as Ollama, vLLM, OpenAI, Claude etc. \n", + "\n", + "It's important to note that a pipeline is simply a callable function. It can easily be replaced with a call to an external API." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "OUc9gqTyAYnm", + "outputId": "83300311-736c-47c8-bc16-ec0303274054" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['French', 'German']\n" + ] + } + ], + "source": [ + "from txtai import LLM, Workflow\n", + "from txtai.workflow import TemplateTask\n", + "\n", + "# Create LLM\n", + "llm = LLM(\"Qwen/Qwen3-4B-Instruct-2507\")\n", + "\n", + "# Define workflow or chaining of tasks together.\n", + "workflow = Workflow([\n", + " TemplateTask(\n", + " template=\"Translate text '{statement}' to {language} if the text is English, otherwise keep the original text\",\n", + " action=llm\n", + " ),\n", + " TemplateTask(\n", + " template=\"What language is the following text. Only print the answer? {text}\",\n", + " action=llm\n", + " )\n", + "])\n", + "\n", + "inputs = [\n", + " {\"statement\": \"Hello, how are you\", \"language\": \"French\"},\n", + " {\"statement\": \"Hallo, wie geht's dir\", \"language\": \"French\"}\n", + "]\n", + "\n", + "print(list(workflow(inputs)))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_zz4Do8BV-Lk" + }, + "source": [ + "Let's recap what happened here. The first workflow task conditionally translates text to a language if it's English.\n", + "\n", + "The first statement is `Hello, how are you` with a target language of French. So the statement is translated to French.\n", + "\n", + "The second statement is German, so it's not converted to French.\n", + "\n", + "The next step asks the model what the language is and it correctly prints `French` and `German`." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "iXDAKP4CX0W9" + }, + "source": [ + "# Prompt Workflow as YAML\n", + "\n", + "The same workflow above can be created with YAML configuration." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "GwV5A9xRYtYs", + "outputId": "ffe6ee65-95a7-46c6-e6b9-5324eab26ca8" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Writing workflow.yml\n" + ] + } + ], + "source": [ + "%%writefile workflow.yml\n", + "\n", + "llm:\n", + " path: Qwen/Qwen3-4B-Instruct-2507\n", + "\n", + "workflow:\n", + " chain:\n", + " tasks:\n", + " - task: template\n", + " template: Translate text '{statement}' to {language} if the text is English, otherwise keep the original text\n", + " action: llm\n", + " - task: template\n", + " template: What language is the following text. Only print the answer? {text}\n", + " action: llm" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "dr7Lv5S5X98e", + "outputId": "d6ac0427-671d-4525-aa21-664430109af3" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['French', 'German']\n" + ] + } + ], + "source": [ + "from txtai import Application\n", + "\n", + "app = Application(\"workflow.yml\")\n", + "print(list(app.workflow(\"chain\", inputs)))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EGqiV45fYVse" + }, + "source": [ + "As expected, the same result! This is a matter of preference on how you want to create a workflow. One advantage of YAML workflows is that an API can easily be created from the workflow file." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9PqMU0bNYinf" + }, + "source": [ + "# Prompt Workflow via an API call\n", + "\n", + "Let's say you want the workflow to be available via an API call. Well good news, txtai has a built in API mechanism using FastAPI. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "vDxQj1ZIYsz3" + }, + "outputs": [], + "source": [ + "# Start an API service\n", + "!CONFIG=workflow.yml nohup uvicorn \"txtai.api:app\" &> api.log &\n", + "!sleep 60" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "R1o08SVtZW7h", + "outputId": "99875acd-18a8-4c2c-ead3-cb6975a4b2d2" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['French', 'German']" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import requests\n", + "\n", + "# Run API request\n", + "requests.post(\"http://localhost:8000/workflow\", json={\"name\": \"chain\", \"elements\": inputs}).json()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "B88mCrGFl5W-" + }, + "source": [ + "Just like the previous steps, except through an API call. Let's run via cURL for good measure." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "hRUyh0cQl_P2", + "outputId": "9db8481d-0b6e-4a31-bdf6-5443df5f768a" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[\"French\",\"German\"]" + ] + } + ], + "source": [ + "%%bash\n", + "\n", + "curl -s -X POST \"http://localhost:8000/workflow\" \\\n", + " -H \"Content-Type: application/json\" \\\n", + " --data @- << EOF\n", + "{\n", + " \"name\": \"chain\",\n", + " \"elements\": [\n", + " {\"statement\": \"Hello, how are you\", \"language\": \"French\"},\n", + " {\"statement\": \"Hallo, wie geht's dir\", \"language\": \"French\"}\n", + " ]\n", + "}\n", + "EOF" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "W0zL93WPoaCo" + }, + "source": [ + "One last time, the same output is shown.\n", + "\n", + "If your primary development environment isn't Python, txtai does have API bindings for [JavaScript](https://github.com/neuml/txtai.js), [Rust](https://github.com/neuml/txtai.rs), [Go](https://github.com/neuml/txtai.go) and [Java](https://github.com/neuml/txtai.java).\n", + "\n", + "More information on the API is available [here](https://neuml.github.io/txtai/api/)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "q9WiFG6fpzw5" + }, + "source": [ + "# Chat with your data\n", + "\n", + "\"Chat with your data\" is a popular entry point into the AI space. Let's run an example." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rM3Y551LqF-J", + "outputId": "85623785-c15f-4996-9460-0644f69cf5bf" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Writing search.yml\n" + ] + } + ], + "source": [ + "%%writefile search.yml\n", + "\n", + "writable: false\n", + "cloud:\n", + " provider: huggingface-hub\n", + " container: neuml/txtai-intro\n", + "\n", + "rag:\n", + " path: Qwen/Qwen3-4B-Instruct-2507\n", + " output: reference\n", + " template: |\n", + " Answer the following question using only the context below.\n", + "\n", + " Question: {question}\n", + " Context: {context}\n", + "\n", + "workflow:\n", + " search:\n", + " tasks:\n", + " - action: rag\n", + " - task: template\n", + " template: \"{answer}\\n\\nReference: {reference}\"\n", + " rules:\n", + " answer: I don't have data on that" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "1Elb8JANqpwX", + "outputId": "b1f1ffa1-6c47-4d90-b6f1-8098d4dc45f8" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[\"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg.\\n\\nReference: 1\"]\n" + ] + } + ], + "source": [ + "app = Application(\"search.yml\")\n", + "print(list(app.workflow(\"search\", [\"Find something about North America\"])))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4r49V4c9s5nf" + }, + "source": [ + "The first thing the code above does is run an embeddings search to build a conversational context. That context is then used to build a prompt and inference is run against the LLM. \n", + "\n", + "The next task formats the outputs with a reference to the best matching record. In this case, it's only an id of 1. But this can be much more useful if the id is a URL or there is logic to format the id back to a unique reference string." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "KqfvCXp2B3li" + }, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook covered how to build prompt templates and task chains through a series of results. txtai has long had a robust and efficient workflow framework for connecting models together. This can be small and simple models and/or prompting with large models. Go ahead and give it a try!" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "provenance": [] + }, + "gpuClass": "standard", + "kernelspec": { + "display_name": "local", + "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", + "version": "3.10.19" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/examples/45_Customize_your_own_embeddings_database.ipynb b/examples/45_Customize_your_own_embeddings_database.ipynb new file mode 100644 index 0000000..2eec4da --- /dev/null +++ b/examples/45_Customize_your_own_embeddings_database.ipynb @@ -0,0 +1,1109 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "gpuClass": "standard", + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Customize your own embeddings database\n", + "\n", + "txtai supports a number of different database and vector index backends, including external databases. With modern hardware, it's amazing how far a single node index can take us. Easily into the hundreds of millions and even billions of records.\n", + "\n", + "txtai provides maximum flexibility in creating your own embeddings database. Sensible defaults are used out of the box. So unless you seek out this configuration, it's not necessary. This notebook will explore the options available when you do want to customize your embeddings database.\n", + "\n", + "More on [embeddings configuration settings can be found here](https://neuml.github.io/txtai/embeddings/configuration). " + ], + "metadata": { + "id": "-xU9P9iSR-Cy" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ], + "metadata": { + "id": "shlUi2kKS7KT" + } + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "id": "xEvX9vCpn4E0" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[database,similarity] datasets" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Load dataset\n", + "\n", + "This example will use the `ag_news` dataset, which is a collection of news article headlines. We'll use a subset of 25,000 headlines." + ], + "metadata": { + "id": "408IyXzKFSiG" + } + }, + { + "cell_type": "code", + "source": [ + "import timeit\n", + "\n", + "from datasets import load_dataset\n", + "\n", + "def timer(embeddings, query=\"red sox\"):\n", + " elapsed = timeit.timeit(lambda: embeddings.search(query), number=250)\n", + " print(f\"{elapsed / 250} seconds per query\")\n", + "\n", + "dataset = load_dataset(\"ag_news\", split=\"train\")[\"text\"][:25000]" + ], + "metadata": { + "id": "IQ_ns6YvFRm1" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# NumPy\n", + "\n", + "Let's start with the simplest possible embeddings database. This will just be a thin wrapper around vectorizing text with sentence-transformers, storing the results as a NumPy array and running similarity queries." + ], + "metadata": { + "id": "K15V3Sj_CvG7" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai.embeddings import Embeddings\n", + "\n", + "# Create embeddings instance\n", + "embeddings = Embeddings({\"path\": \"sentence-transformers/all-MiniLM-L6-v2\", \"backend\": \"numpy\"})\n", + "\n", + "# Index data\n", + "embeddings.index((x, text, None) for x, text in enumerate(dataset))" + ], + "metadata": { + "id": "DMqiTrTbC-VJ" + }, + "execution_count": 41, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "embeddings.search(\"red sox\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "hcAcJikVDMNQ", + "outputId": "a587620a-5657-4082-84ee-3d73114e1d3a" + }, + "execution_count": 42, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[(19831, 0.6780003309249878),\n", + " (18302, 0.6639199256896973),\n", + " (16370, 0.6617192029953003)]" + ] + }, + "metadata": {}, + "execution_count": 42 + } + ] + }, + { + "cell_type": "code", + "source": [ + "embeddings.info()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "FQYx76IgMinE", + "outputId": "9375bf2b-e641-4b01-cc8a-b400c5baf399" + }, + "execution_count": 43, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "{\n", + " \"backend\": \"numpy\",\n", + " \"build\": {\n", + " \"create\": \"2023-05-16T13:38:32Z\",\n", + " \"python\": \"3.10.11\",\n", + " \"settings\": {\n", + " \"numpy\": \"1.22.4\"\n", + " },\n", + " \"system\": \"Linux (x86_64)\",\n", + " \"txtai\": \"5.6.0\"\n", + " },\n", + " \"dimensions\": 384,\n", + " \"offset\": 25000,\n", + " \"path\": \"sentence-transformers/all-MiniLM-L6-v2\",\n", + " \"update\": \"2023-05-16T13:38:32Z\"\n", + "}\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The embeddings instance above vectorizes the text and stores the content as a NumPy array. Array index positions are returned with similarity scores. While the same can easily be done using sentence-transformers, using the txtai framework makes it easy to swap out different options as seen next." + ], + "metadata": { + "id": "NkHMOoE9L4Nw" + } + }, + { + "cell_type": "markdown", + "source": [ + "# SQLite and NumPy\n", + "\n", + "The next combination we'll test is a SQLite database with a NumPy array." + ], + "metadata": { + "id": "AtEdP7Utw3mk" + } + }, + { + "cell_type": "code", + "source": [ + "# Create embeddings instance\n", + "embeddings = Embeddings({\"path\": \"sentence-transformers/all-MiniLM-L6-v2\", \"content\": \"sqlite\", \"backend\": \"numpy\"})\n", + "\n", + "# Index data\n", + "embeddings.index((x, text, None) for x, text in enumerate(dataset))" + ], + "metadata": { + "id": "DPWrubv5oOn7" + }, + "execution_count": 44, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Now let's run a search." + ], + "metadata": { + "id": "SDaDLMyXLGe1" + } + }, + { + "cell_type": "code", + "source": [ + "embeddings.search(\"red sox\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ILSfWHxVHex0", + "outputId": "f4cc3f44-da63-4be9-9187-58386fad58df" + }, + "execution_count": 45, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '19831',\n", + " 'text': 'Boston Red Sox Team Report - September 6 (Sports Network) - Two of the top teams in the American League tangle in a possible American League Division Series preview tonight, as the West-leading Oakland Athletics host the wild card-leading Boston Red Sox for the first of a three-game set at the ',\n", + " 'score': 0.6780003309249878},\n", + " {'id': '18302',\n", + " 'text': 'BASEBALL: RED-HOT SOX CLIP THE ANGELS #39; WINGS BOSTON RED SOX fans are enjoying their best week of the season. While their beloved team swept wild-card rivals Anaheim in a three-game series to establish a nine-game winning streak, the hated New York Yankees endured the heaviest loss in their history.',\n", + " 'score': 0.6639199256896973},\n", + " {'id': '16370',\n", + " 'text': 'Boston Red Sox Team Report - September 1 (Sports Network) - The red-hot Boston Red Sox hope to continue rolling as they continue their three-game set with the Anaheim Angels this evening at Fenway Park.',\n", + " 'score': 0.6617192029953003}]" + ] + }, + "metadata": {}, + "execution_count": 45 + } + ] + }, + { + "cell_type": "code", + "source": [ + "embeddings.info()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "0IuVqFxUMwe8", + "outputId": "fa13d767-d549-4b9c-f63e-7e09b05159ee" + }, + "execution_count": 46, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "{\n", + " \"backend\": \"numpy\",\n", + " \"build\": {\n", + " \"create\": \"2023-05-16T13:38:52Z\",\n", + " \"python\": \"3.10.11\",\n", + " \"settings\": {\n", + " \"numpy\": \"1.22.4\"\n", + " },\n", + " \"system\": \"Linux (x86_64)\",\n", + " \"txtai\": \"5.6.0\"\n", + " },\n", + " \"content\": \"sqlite\",\n", + " \"dimensions\": 384,\n", + " \"offset\": 25000,\n", + " \"path\": \"sentence-transformers/all-MiniLM-L6-v2\",\n", + " \"update\": \"2023-05-16T13:38:52Z\"\n", + "}\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Same results as before. The only difference is the content is now available via the associated SQLite database. \n", + "\n", + "Let's inspect the ANN object to see how it looks. " + ], + "metadata": { + "id": "B_XnpIpXNKSP" + } + }, + { + "cell_type": "code", + "source": [ + "print(embeddings.ann.backend.shape)\n", + "print(type(embeddings.ann.backend))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "c2FVQlxSLKgP", + "outputId": "1b8e454d-c5f7-4d58-b2b0-a1a9eaca4b9b" + }, + "execution_count": 47, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(25000, 384)\n", + "\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "As expected, it's a NumPy array. Let's calculate how long a search query takes to execute.\n" + ], + "metadata": { + "id": "00dnum6fNNM0" + } + }, + { + "cell_type": "code", + "source": [ + "timer(embeddings)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "JvodDi4w6JxS", + "outputId": "3671753d-0dd1-47fe-bef6-04841204cb6b" + }, + "execution_count": 48, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "0.028768999292000445 seconds per query\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Not too bad at all!\n", + "\n" + ], + "metadata": { + "id": "eqom3l_87jFv" + } + }, + { + "cell_type": "markdown", + "source": [ + "# SQLite and PyTorch\n", + "\n", + "Let's now try a PyTorch backend." + ], + "metadata": { + "id": "Y54lSbQd5rzy" + } + }, + { + "cell_type": "code", + "source": [ + "# Create embeddings instance\n", + "embeddings = Embeddings({\"path\": \"sentence-transformers/all-MiniLM-L6-v2\", \"content\": \"sqlite\", \"backend\": \"torch\"})\n", + "\n", + "# Index data\n", + "embeddings.index((x, text, None) for x, text in enumerate(dataset))" + ], + "metadata": { + "id": "OYAqPoTmNaNN" + }, + "execution_count": 49, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Let's run a search again." + ], + "metadata": { + "id": "DT52loQU7zmt" + } + }, + { + "cell_type": "code", + "source": [ + "embeddings.search(\"red sox\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "zvlrEunM7vi4", + "outputId": "e591495f-0622-4291-fbbd-cbe655f92a07" + }, + "execution_count": 50, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '19831',\n", + " 'text': 'Boston Red Sox Team Report - September 6 (Sports Network) - Two of the top teams in the American League tangle in a possible American League Division Series preview tonight, as the West-leading Oakland Athletics host the wild card-leading Boston Red Sox for the first of a three-game set at the ',\n", + " 'score': 0.678000271320343},\n", + " {'id': '18302',\n", + " 'text': 'BASEBALL: RED-HOT SOX CLIP THE ANGELS #39; WINGS BOSTON RED SOX fans are enjoying their best week of the season. While their beloved team swept wild-card rivals Anaheim in a three-game series to establish a nine-game winning streak, the hated New York Yankees endured the heaviest loss in their history.',\n", + " 'score': 0.6639199256896973},\n", + " {'id': '16370',\n", + " 'text': 'Boston Red Sox Team Report - September 1 (Sports Network) - The red-hot Boston Red Sox hope to continue rolling as they continue their three-game set with the Anaheim Angels this evening at Fenway Park.',\n", + " 'score': 0.6617191433906555}]" + ] + }, + "metadata": {}, + "execution_count": 50 + } + ] + }, + { + "cell_type": "code", + "source": [ + "embeddings.info()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "QmJZb56SM6Up", + "outputId": "a6e9b73a-6248-43ca-a57f-c3a4fec9112f" + }, + "execution_count": 51, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "{\n", + " \"backend\": \"torch\",\n", + " \"build\": {\n", + " \"create\": \"2023-05-16T13:39:19Z\",\n", + " \"python\": \"3.10.11\",\n", + " \"settings\": {\n", + " \"torch\": \"2.0.0+cu118\"\n", + " },\n", + " \"system\": \"Linux (x86_64)\",\n", + " \"txtai\": \"5.6.0\"\n", + " },\n", + " \"content\": \"sqlite\",\n", + " \"dimensions\": 384,\n", + " \"offset\": 25000,\n", + " \"path\": \"sentence-transformers/all-MiniLM-L6-v2\",\n", + " \"update\": \"2023-05-16T13:39:19Z\"\n", + "}\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "And once against inspect the ANN object." + ], + "metadata": { + "id": "jqdMjDiO8Dy3" + } + }, + { + "cell_type": "code", + "source": [ + "print(embeddings.ann.backend.shape)\n", + "print(type(embeddings.ann.backend))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "u5JFEJ-q5Zow", + "outputId": "fffb3e3d-2d5c-4f20-877a-cce52836e5f3" + }, + "execution_count": 52, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "torch.Size([25000, 384])\n", + "\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "As expected, this time the backend is a Torch tensor. Next we'll calculate the average search time." + ], + "metadata": { + "id": "jGwHvEHE6ALO" + } + }, + { + "cell_type": "code", + "source": [ + "timer(embeddings)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "7aAI_jUm6goL", + "outputId": "e4ec71dc-c1d8-40ff-9e68-113a5da1ad1e" + }, + "execution_count": 53, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "0.0198183048359997 seconds per query\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "A bit faster since Torch uses the GPU to compute the similarity matrix." + ], + "metadata": { + "id": "mp3nLHz38OIp" + } + }, + { + "cell_type": "markdown", + "source": [ + "# SQLite and Faiss\n", + "\n", + "Now lets run the same code with the standard txtai settings of Faiss + SQLite." + ], + "metadata": { + "id": "8h3SXoGr9YIL" + } + }, + { + "cell_type": "code", + "source": [ + "# Create embeddings instance\n", + "embeddings = Embeddings({\"path\": \"sentence-transformers/all-MiniLM-L6-v2\", \"content\": True})\n", + "\n", + "# Index data\n", + "embeddings.index((x, text, None) for x, text in enumerate(dataset))" + ], + "metadata": { + "id": "DQECU7y-9doj" + }, + "execution_count": 54, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "embeddings.search(\"red sox\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "wGqcmE6M9kJW", + "outputId": "25c7ef4c-a437-4d64-ebaf-1481d0873ca1" + }, + "execution_count": 55, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '19831',\n", + " 'text': 'Boston Red Sox Team Report - September 6 (Sports Network) - Two of the top teams in the American League tangle in a possible American League Division Series preview tonight, as the West-leading Oakland Athletics host the wild card-leading Boston Red Sox for the first of a three-game set at the ',\n", + " 'score': 0.6780003309249878},\n", + " {'id': '18302',\n", + " 'text': 'BASEBALL: RED-HOT SOX CLIP THE ANGELS #39; WINGS BOSTON RED SOX fans are enjoying their best week of the season. While their beloved team swept wild-card rivals Anaheim in a three-game series to establish a nine-game winning streak, the hated New York Yankees endured the heaviest loss in their history.',\n", + " 'score': 0.6639199256896973},\n", + " {'id': '16370',\n", + " 'text': 'Boston Red Sox Team Report - September 1 (Sports Network) - The red-hot Boston Red Sox hope to continue rolling as they continue their three-game set with the Anaheim Angels this evening at Fenway Park.',\n", + " 'score': 0.6617192029953003}]" + ] + }, + "metadata": {}, + "execution_count": 55 + } + ] + }, + { + "cell_type": "code", + "source": [ + "embeddings.info()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "zDxqlMT9d-Q3", + "outputId": "befdb0a1-01b4-4948-94e2-4570efada3ce" + }, + "execution_count": 56, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "{\n", + " \"backend\": \"faiss\",\n", + " \"build\": {\n", + " \"create\": \"2023-05-16T13:39:47Z\",\n", + " \"python\": \"3.10.11\",\n", + " \"settings\": {\n", + " \"components\": \"IVF632,Flat\"\n", + " },\n", + " \"system\": \"Linux (x86_64)\",\n", + " \"txtai\": \"5.6.0\"\n", + " },\n", + " \"content\": true,\n", + " \"dimensions\": 384,\n", + " \"offset\": 25000,\n", + " \"path\": \"sentence-transformers/all-MiniLM-L6-v2\",\n", + " \"update\": \"2023-05-16T13:39:47Z\"\n", + "}\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "timer(embeddings)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "zlbc43qg9qKb", + "outputId": "ae06092c-ff5f-4540-afd5-e054db6ffb84" + }, + "execution_count": 57, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "0.00825659705599992 seconds per query\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Everything lines up with the previous examples. Note that Faiss is faster, given it's a vector index. For 25,000 records, the different is negligible but vector index performance increases rapidly for datasets in the million+ range." + ], + "metadata": { + "id": "j5u1GEbV91GH" + } + }, + { + "cell_type": "markdown", + "source": [ + "# SQLite and HNSW\n", + "\n", + "While txtai strives to keep things as simple as possible with many common default settings out of the box, customizing the backend options can lead to increased performance. The next example will store vectors in a HNSW index and customize the index options." + ], + "metadata": { + "id": "f4Hnjfy--ye0" + } + }, + { + "cell_type": "code", + "source": [ + "# Create embeddings instance\n", + "embeddings = Embeddings({\"path\": \"sentence-transformers/all-MiniLM-L6-v2\", \"content\": True, \"backend\": \"hnsw\", \"hnsw\": {\"m\": 32}})\n", + "\n", + "# Index data\n", + "embeddings.index((x, text, None) for x, text in enumerate(dataset))" + ], + "metadata": { + "id": "5dqxj2hr_ICl" + }, + "execution_count": 58, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "embeddings.search(\"red sox\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "pUw-3WCHFGf9", + "outputId": "22e827a9-ed34-4847-b41b-70499f85cff0" + }, + "execution_count": 59, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '19831',\n", + " 'text': 'Boston Red Sox Team Report - September 6 (Sports Network) - Two of the top teams in the American League tangle in a possible American League Division Series preview tonight, as the West-leading Oakland Athletics host the wild card-leading Boston Red Sox for the first of a three-game set at the ',\n", + " 'score': 0.678000271320343},\n", + " {'id': '18302',\n", + " 'text': 'BASEBALL: RED-HOT SOX CLIP THE ANGELS #39; WINGS BOSTON RED SOX fans are enjoying their best week of the season. While their beloved team swept wild-card rivals Anaheim in a three-game series to establish a nine-game winning streak, the hated New York Yankees endured the heaviest loss in their history.',\n", + " 'score': 0.6639199256896973},\n", + " {'id': '16370',\n", + " 'text': 'Boston Red Sox Team Report - September 1 (Sports Network) - The red-hot Boston Red Sox hope to continue rolling as they continue their three-game set with the Anaheim Angels this evening at Fenway Park.',\n", + " 'score': 0.6617191433906555}]" + ] + }, + "metadata": {}, + "execution_count": 59 + } + ] + }, + { + "cell_type": "code", + "source": [ + "embeddings.info()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "u5DAfB1MeCLF", + "outputId": "d00dec7f-e1da-49f1-afd3-aa852238769f" + }, + "execution_count": 60, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "{\n", + " \"backend\": \"hnsw\",\n", + " \"build\": {\n", + " \"create\": \"2023-05-16T13:40:21Z\",\n", + " \"python\": \"3.10.11\",\n", + " \"settings\": {\n", + " \"efconstruction\": 200,\n", + " \"m\": 32,\n", + " \"seed\": 100\n", + " },\n", + " \"system\": \"Linux (x86_64)\",\n", + " \"txtai\": \"5.6.0\"\n", + " },\n", + " \"content\": true,\n", + " \"deletes\": 0,\n", + " \"dimensions\": 384,\n", + " \"hnsw\": {\n", + " \"m\": 32\n", + " },\n", + " \"metric\": \"ip\",\n", + " \"offset\": 25000,\n", + " \"path\": \"sentence-transformers/all-MiniLM-L6-v2\",\n", + " \"update\": \"2023-05-16T13:40:21Z\"\n", + "}\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "timer(embeddings)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Z3Qg6EEjFIom", + "outputId": "49d01a91-5d53-4792-8244-316b6e79cc20" + }, + "execution_count": 61, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "0.006280380824000531 seconds per query\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Once again, everything matches up with the previous examples. There is a negligible performance difference vs Faiss.\n", + "\n", + "Hnswlib powers a number of popular vector databases. It's definitely an option worth evaluating." + ], + "metadata": { + "id": "JREMWY5NHAX-" + } + }, + { + "cell_type": "markdown", + "source": [ + "# External Vectorization\n", + "\n", + "txtai has a number of built-in vectorizers backed by Hugging Face Transformers and Sentence Transformers. Just like other txtai modules, vectorization can also be customized.\n", + "\n", + "The next example uses the Hugging Face Inference API to vectorize text.\n" + ], + "metadata": { + "id": "Wuj_gZeBs57O" + } + }, + { + "cell_type": "code", + "source": [ + "import numpy as np\n", + "import requests\n", + "\n", + "BASE = \"https://api-inference.huggingface.co/pipeline/feature-extraction\"\n", + "\n", + "def transform(inputs):\n", + " # Your API provider of choice\n", + " response = requests.post(f\"{BASE}/sentence-transformers/all-MiniLM-L6-v2\", json={\"inputs\": inputs})\n", + " return np.array(response.json(), dtype=np.float32)\n", + "\n", + "embeddings = Embeddings({\"transform\": transform, \"backend\": \"numpy\", \"content\": True})\n", + "embeddings.index([(0, \"sunny\", None), (1, \"rainy\", None)])\n", + "embeddings.search(\"nice day\") " + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "FAhg1TcdtWIJ", + "outputId": "22e34bd0-b6d3-40b2-8948-d08397d744f5" + }, + "execution_count": 62, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '0', 'text': 'sunny', 'score': 0.28077083826065063},\n", + " {'id': '1', 'text': 'rainy', 'score': 0.18051263689994812}]" + ] + }, + "metadata": {}, + "execution_count": 62 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Configuration storage\n", + "\n", + "Configuration is passed to an embeddings instance as a dictionary. When saving an embeddings instance, the default behavior is to save configuration as a pickled object. JSON can alternatively be used." + ], + "metadata": { + "id": "RvHkAloSl4y3" + } + }, + { + "cell_type": "code", + "source": [ + "# Create embeddings instance\n", + "embeddings = Embeddings({\"path\": \"sentence-transformers/all-MiniLM-L6-v2\", \"content\": True, \"format\": \"json\"})\n", + "\n", + "# Index data\n", + "embeddings.index((x, text, None) for x, text in enumerate(dataset))\n", + "\n", + "# Save embeddings\n", + "embeddings.save(\"index\")\n", + "\n", + "!cat index/config.json" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "X1DYuyPmmSgU", + "outputId": "975c0f7f-f38e-478e-a8b8-16155f1f4865" + }, + "execution_count": 63, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "{\n", + " \"path\": \"sentence-transformers/all-MiniLM-L6-v2\",\n", + " \"content\": true,\n", + " \"format\": \"json\",\n", + " \"dimensions\": 384,\n", + " \"backend\": \"faiss\",\n", + " \"offset\": 25000,\n", + " \"build\": {\n", + " \"create\": \"2023-05-16T13:40:49Z\",\n", + " \"python\": \"3.10.11\",\n", + " \"settings\": {\n", + " \"components\": \"IVF632,Flat\"\n", + " },\n", + " \"system\": \"Linux (x86_64)\",\n", + " \"txtai\": \"5.6.0\"\n", + " },\n", + " \"update\": \"2023-05-16T13:40:49Z\"\n", + "}" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Looking at the stored configuration, it's almost identical to an `embeddings.info()` call. This is by design, JSON configuration is designed to be human-readable. This is a good option when sharing an embeddings database on the [Hugging Face Hub](https://huggingface.co/models)." + ], + "metadata": { + "id": "ETdcrP7dqI8J" + } + }, + { + "cell_type": "markdown", + "source": [ + "# SQLite vs DuckDB\n", + "\n", + "The last thing we'll explore is the database backend.\n", + "\n", + "[SQLite](https://sqlite.org/index.html) is a row-oriented database, [DuckDB](https://duckdb.org/) is column-oriented. This design difference is important to note and a factor to consider when evaluating the expected workload. Let's explore." + ], + "metadata": { + "id": "z6zmhGRVHawG" + } + }, + { + "cell_type": "code", + "source": [ + "# Create embeddings instance\n", + "embeddings = Embeddings({\"path\": \"sentence-transformers/all-MiniLM-L6-v2\", \"content\": \"sqlite\"})\n", + "\n", + "# Index data\n", + "embeddings.index((x, text, None) for x, text in enumerate(dataset))" + ], + "metadata": { + "id": "KZ-x_53SHsNK" + }, + "execution_count": 64, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "timer(embeddings, \"SELECT text FROM txtai where id = 3980\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "LQZCGu-9H70K", + "outputId": "06e41f55-687d-4d98-e194-13efdd2991ef" + }, + "execution_count": 65, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "0.00012401376399975562 seconds per query\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "timer(embeddings, \"SELECT count(*), text FROM txtai group by text order by count(*) desc\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "SwgR2TwPHvdP", + "outputId": "d88a457a-7789-4881-9420-914e2992d132" + }, + "execution_count": 66, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "0.03863514600000053 seconds per query\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "# Create embeddings instance\n", + "embeddings = Embeddings({\"path\": \"sentence-transformers/all-MiniLM-L6-v2\", \"content\": \"duckdb\"})\n", + "\n", + "# Index data\n", + "embeddings.index((x, text, None) for x, text in enumerate(dataset))" + ], + "metadata": { + "id": "ZdrLBOmaIKbF" + }, + "execution_count": 67, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "timer(embeddings, \"SELECT text FROM txtai where id = 3980\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cce1PAVqIMpU", + "outputId": "51b8749b-7a87-42d0-af17-2710f8460c68" + }, + "execution_count": 68, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "0.0038918176440001844 seconds per query\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "timer(embeddings, \"SELECT count(*), text FROM txtai group by text order by count(*) desc\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "lxBfoh3TINmE", + "outputId": "f56ed46b-abb6-4f6f-e986-10ec570e8cbe" + }, + "execution_count": 69, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "0.0198518766039997 seconds per query\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "While the dataset of 25,000 rows is small, we can start to see the differences. SQLite has a much faster single row retrieval time. DuckDB does better with an aggregate query. This is a product of a row-oriented vs column oriented database and a factor to consider when developing a solution." + ], + "metadata": { + "id": "_hBm-yZTJtUQ" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "This notebook explored different combinations of database and vector index backends. With modern hardware, it's amazing how far a single node index can take us. Easily into the hundreds of millions and even billions of records. When a hardware bottleneck becomes an issue, external vector databases are one option to consider. Another is [building a distributed txtai embeddings cluster](https://neuml.github.io/txtai/api/cluster/).\n", + "\n", + "There is power in simplicity. Many paid services try to convince us that signing up for an API account is the best place to start. In some cases, such as teams with very few to no developers, this is true. But for teams with developers, options like txtai should be evaluated." + ], + "metadata": { + "id": "4L8smyyXc8q8" + } + } + ] +} \ No newline at end of file diff --git a/examples/46_Whats_new_in_txtai_6_0.ipynb b/examples/46_Whats_new_in_txtai_6_0.ipynb new file mode 100644 index 0000000..e007715 --- /dev/null +++ b/examples/46_Whats_new_in_txtai_6_0.ipynb @@ -0,0 +1,773 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "gpuType": "T4" + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# 💡 What's new in txtai 6.0\n", + "\n", + "txtai 6.0 brings a number of major feature enhancements. Highlights include:\n", + "\n", + "- Embeddings\n", + " - Sparse/keyword indexes\n", + " - Hybrid search\n", + " - Subindexes\n", + " - Streamlined methods\n", + "\n", + "- Large Language Models (LLMs)\n", + " - Automatically instantiate the best available underlying model\n", + " - Pass through parameters enabling immediate support as features are released upstream\n", + "\n", + "These are just the big, high level changes. There are also many improvements and bug fixes.\n", + "\n", + "This notebook will cover all the changes with examples.\n", + "\n", + "**Standard upgrade disclaimer below**\n", + "\n", + "6.0 is one of the largest, if not largest releases to date! While almost everything is backwards compatible, it's prudent to backup production indexes before upgrading and test before deploying." + ], + "metadata": { + "id": "e3wdiK5fGUoZ" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ], + "metadata": { + "id": "p8BbfjrhH-V2" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "-OXsTQgaGQPM" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[graph] datasets" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Sparse indexes\n", + "\n", + "While dense vector indexes are by far the best option for semantic search systems, sparse keyword indexes can still add value. There may be cases where finding an exact match is important or we just want a fast index to quickly do an initial scan of the dataset.\n", + "\n", + "Unfortunately, there aren't a ton of great options for a local Python-based keyword index library. Most of the options available don't scale and are highly inefficient, designed only for simple situations. With 6.0, txtai has added a performant sparse index component with speed and accuracy on par with Apache Lucene. A future article will discuss the engineering behind this.\n", + "\n", + "Let's take a look. We'll use a [prompt dataset on the Hugging Face Hub](https://huggingface.co/datasets/fka/awesome-chatgpt-prompts) for all examples." + ], + "metadata": { + "id": "n4EXrtcYIIYE" + } + }, + { + "cell_type": "code", + "source": [ + "from datasets import load_dataset\n", + "\n", + "import txtai\n", + "\n", + "# Load dataset\n", + "ds = load_dataset(\"fka/awesome-chatgpt-prompts\", split=\"train\")\n", + "\n", + "def stream():\n", + " for row in ds:\n", + " yield f\"{row['act']} {row['prompt']}\"\n", + "\n", + "# Build sparse keyword index\n", + "embeddings = txtai.Embeddings(keyword=True, content=True)\n", + "embeddings.index(stream())\n", + "\n", + "embeddings.search(\"Linux terminal\", 1)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "hzPD8_cQJNtN", + "outputId": "22059d6e-9022-4fa9-8f54-bd2609508f1e" + }, + "execution_count": 34, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '0',\n", + " 'text': 'Linux Terminal I want you to act as a linux terminal. I will type commands and you will reply with what the terminal should show. I want you to only reply with the terminal output inside one unique code block, and nothing else. do not write explanations. do not type commands unless I instruct you to do so. when i need to tell you something in english, i will do so by putting text inside curly brackets {like this}. my first command is pwd',\n", + " 'score': 0.5932681465337526}]" + ] + }, + "metadata": {}, + "execution_count": 34 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "And there it is, a keyword index!\n", + "\n", + "Couple things to unpack here. First, for those familar with txtai, notice that only a text field was yielded in the `stream` method. With 6.0, when ids aren't provided, they are automatically generated.\n", + "\n", + "Next notice the score. Those familar with keyword scores (TF-IDF, BM25) will notice that the score seems low. That is because with a keyword index, the default score is normalized between 0 and 1.\n", + "\n", + "More on these items later." + ], + "metadata": { + "id": "caDteWUVK5jI" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Hybrid Search\n", + "\n", + "The addition of sparse indexes enables hybrid search. Hybrid search combines the results from sparse and dense vector indexes for the best of both worlds." + ], + "metadata": { + "id": "m7lGqfvDMPQA" + } + }, + { + "cell_type": "code", + "source": [ + "# Build hybrid index\n", + "embeddings = txtai.Embeddings(hybrid=True, content=True)\n", + "embeddings.index(stream())\n", + "\n", + "embeddings.search(\"Linux terminal\", 1)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cRXnGeqJMWql", + "outputId": "e4c38ad5-59c0-4fd1-ec1d-d64372b19902" + }, + "execution_count": 19, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '0',\n", + " 'text': 'Linux Terminal I want you to act as a linux terminal. I will type commands and you will reply with what the terminal should show. I want you to only reply with the terminal output inside one unique code block, and nothing else. do not write explanations. do not type commands unless I instruct you to do so. when i need to tell you something in english, i will do so by putting text inside curly brackets {like this}. my first command is pwd',\n", + " 'score': 0.6078515601252442}]" + ] + }, + "metadata": {}, + "execution_count": 19 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Simple change with big impacts. This new index now has both a sparse and dense (using default `sentence-transformers/all-MiniLM-L6-v2` model) index. These scores are combined into a single score as seen above.\n", + "\n", + "The scoring weights (also known as alpha) control the weighting between the sparse and dense index." + ], + "metadata": { + "id": "6bHDxRm5NXI7" + } + }, + { + "cell_type": "code", + "source": [ + "embeddings.search(\"Linux terminal\", 1, weights=1)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "RWFnXVTRNPHC", + "outputId": "b644cbb0-e4d5-41d6-ea72-6a139a160f0c" + }, + "execution_count": 20, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '0',\n", + " 'text': 'Linux Terminal I want you to act as a linux terminal. I will type commands and you will reply with what the terminal should show. I want you to only reply with the terminal output inside one unique code block, and nothing else. do not write explanations. do not type commands unless I instruct you to do so. when i need to tell you something in english, i will do so by putting text inside curly brackets {like this}. my first command is pwd',\n", + " 'score': 0.6224349737167358}]" + ] + }, + "metadata": {}, + "execution_count": 20 + } + ] + }, + { + "cell_type": "code", + "source": [ + "embeddings.search(\"Linux terminal\", 1, weights=0)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "EYcv21vENRtp", + "outputId": "25b7516f-e23e-4f67-bcdc-c1ff3e157009" + }, + "execution_count": 21, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '0',\n", + " 'text': 'Linux Terminal I want you to act as a linux terminal. I will type commands and you will reply with what the terminal should show. I want you to only reply with the terminal output inside one unique code block, and nothing else. do not write explanations. do not type commands unless I instruct you to do so. when i need to tell you something in english, i will do so by putting text inside curly brackets {like this}. my first command is pwd',\n", + " 'score': 0.5932681465337526}]" + ] + }, + "metadata": {}, + "execution_count": 21 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "A weight of 1 only uses the dense index and 0 only uses the sparse index. Notice the score with `weight = 0` is the same as the sparse index query earlier." + ], + "metadata": { + "id": "cVwf7KeqNydT" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Subindexes\n", + "\n", + "While sparse and hybrid indexes are great new features, the prize of this release is the addition of subindexes. Subindexes will add a host of new ways to build txtai embeddings instances. Let's give a brief intro here." + ], + "metadata": { + "id": "4gUIB_vIODSg" + } + }, + { + "cell_type": "code", + "source": [ + "# Build index with subindexes\n", + "embeddings = txtai.Embeddings(\n", + " content=True,\n", + " defaults=False,\n", + " indexes={\n", + " \"sparse\": {\n", + " \"keyword\": True\n", + " },\n", + " \"dense\":{\n", + "\n", + " }\n", + " }\n", + ")\n", + "embeddings.index(stream())\n", + "\n", + "# Run search\n", + "embeddings.search(\"select id, text, score from txtai where similar('Linux terminal', 'sparse') and similar('Linux terminal', 'dense')\", 1)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "9nkna32MObBi", + "outputId": "3f57c63e-8b4b-4d75-f4db-3455ed667ec2" + }, + "execution_count": 22, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '0',\n", + " 'text': 'Linux Terminal I want you to act as a linux terminal. I will type commands and you will reply with what the terminal should show. I want you to only reply with the terminal output inside one unique code block, and nothing else. do not write explanations. do not type commands unless I instruct you to do so. when i need to tell you something in english, i will do so by putting text inside curly brackets {like this}. my first command is pwd',\n", + " 'score': 0.6078515601252442}]" + ] + }, + "metadata": {}, + "execution_count": 22 + } + ] + }, + { + "cell_type": "code", + "source": [ + "embeddings.search(\"select id, text, score from txtai where similar('Linux terminal', 'dense')\", 1)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cPysPMHRR2IS", + "outputId": "77b19baa-0643-4958-dd59-363ce05017a8" + }, + "execution_count": 23, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '0',\n", + " 'text': 'Linux Terminal I want you to act as a linux terminal. I will type commands and you will reply with what the terminal should show. I want you to only reply with the terminal output inside one unique code block, and nothing else. do not write explanations. do not type commands unless I instruct you to do so. when i need to tell you something in english, i will do so by putting text inside curly brackets {like this}. my first command is pwd',\n", + " 'score': 0.6224349737167358}]" + ] + }, + "metadata": {}, + "execution_count": 23 + } + ] + }, + { + "cell_type": "code", + "source": [ + "embeddings.search(\"select id, text, score from txtai where similar('Linux terminal', 'sparse')\", 1)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "KYEFkDRDSJ4J", + "outputId": "bcb2460d-9cdd-4808-f861-71405044849d" + }, + "execution_count": 24, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '0',\n", + " 'text': 'Linux Terminal I want you to act as a linux terminal. I will type commands and you will reply with what the terminal should show. I want you to only reply with the terminal output inside one unique code block, and nothing else. do not write explanations. do not type commands unless I instruct you to do so. when i need to tell you something in english, i will do so by putting text inside curly brackets {like this}. my first command is pwd',\n", + " 'score': 0.5932681465337526}]" + ] + }, + "metadata": {}, + "execution_count": 24 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Notice how the scores are the same as above. The three searches above run a hybrid search, dense and sparse search. This time though it's using subindexes. The top-level Embeddings only has an associated database.\n", + "\n", + "Each of the sections in the `indexes` is a full embeddings index supporting all available options. For example, let's add a graph subindex." + ], + "metadata": { + "id": "hJGAjnpbSTRu" + } + }, + { + "cell_type": "code", + "source": [ + "# Build index with graph subindex\n", + "embeddings = txtai.Embeddings(\n", + " content=True,\n", + " defaults=False,\n", + " functions=[\n", + " {\"name\": \"graph\", \"function\": \"indexes.act.graph.attribute\"}\n", + " ],\n", + " expressions=[\n", + " {\"name\": \"topic\", \"expression\": \"graph(indexid, 'topic')\"},\n", + " ],\n", + " indexes={\n", + " \"act\": {\n", + " \"keyword\": True,\n", + " \"columns\": {\n", + " \"text\": \"act\"\n", + " },\n", + " \"graph\": {\n", + " \"topics\": {}\n", + " }\n", + " },\n", + " \"prompt\":{\n", + " \"columns\": {\n", + " \"text\": \"prompt\"\n", + " }\n", + " }\n", + " }\n", + ")\n", + "embeddings.index(ds)\n", + "\n", + "# Run search\n", + "embeddings.search(\"select id, act, prompt, score, topic from txtai where similar('Linux terminal')\", 1)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "D35uFFnESqP3", + "outputId": "d5ee3775-d488-4e46-a44b-3ae4a12a6c1f" + }, + "execution_count": 25, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '0',\n", + " 'act': 'Linux Terminal',\n", + " 'prompt': 'I want you to act as a linux terminal. I will type commands and you will reply with what the terminal should show. I want you to only reply with the terminal output inside one unique code block, and nothing else. do not write explanations. do not type commands unless I instruct you to do so. when i need to tell you something in english, i will do so by putting text inside curly brackets {like this}. my first command is pwd',\n", + " 'score': 0.6382951796072414,\n", + " 'topic': 'terminal_linux_sql'}]" + ] + }, + "metadata": {}, + "execution_count": 25 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Notice the new `topic` field added to this query. That comes from the graph index, which runs topic modeling. Also notice that two indexes for two different columns are added.\n", + "\n", + "Note that graph indexes are different in that they depend on a sparse or dense index being available. That is how the graph is automatically constructed. For good measure, let's add the graph to a dense index." + ], + "metadata": { + "id": "t_OIM6tPUnC_" + } + }, + { + "cell_type": "code", + "source": [ + "# Build index with graph subindex\n", + "embeddings = txtai.Embeddings(\n", + " content=True,\n", + " defaults=False,\n", + " functions=[\n", + " {\"name\": \"graph\", \"function\": \"indexes.act.graph.attribute\"}\n", + " ],\n", + " expressions=[\n", + " {\"name\": \"topic\", \"expression\": \"graph(indexid, 'topic')\"},\n", + " ],\n", + " indexes={\n", + " \"act\": {\n", + " \"path\": \"intfloat/e5-small-v2\",\n", + " \"columns\": {\n", + " \"text\": \"act\"\n", + " },\n", + " \"graph\": {\n", + " \"topics\": {}\n", + " }\n", + " },\n", + " \"prompt\":{\n", + " \"path\": \"sentence-transformers/all-MiniLM-L6-v2\",\n", + " \"columns\": {\n", + " \"text\": \"prompt\"\n", + " }\n", + " }\n", + " }\n", + ")\n", + "embeddings.index(ds)\n", + "\n", + "# Run search\n", + "embeddings.search(\"select id, act, prompt, score, topic from txtai where similar('Linux terminal')\", 1)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Me8zJse7WbdM", + "outputId": "14433e6b-9eed-4cd2-e590-4c9c03be390a" + }, + "execution_count": 26, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '0',\n", + " 'act': 'Linux Terminal',\n", + " 'prompt': 'I want you to act as a linux terminal. I will type commands and you will reply with what the terminal should show. I want you to only reply with the terminal output inside one unique code block, and nothing else. do not write explanations. do not type commands unless I instruct you to do so. when i need to tell you something in english, i will do so by putting text inside curly brackets {like this}. my first command is pwd',\n", + " 'score': 1.0,\n", + " 'topic': 'linux_terminal'}]" + ] + }, + "metadata": {}, + "execution_count": 26 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Almost the same as above except the topic is different. This is due to the grouping of the vector index. Notice how the `act` column and `prompt` column are both vector indexes but specify different vector models. This opens up another possibility of weighting not only sparse vs vector but different vector models." + ], + "metadata": { + "id": "yKHVU_r4W9HD" + } + }, + { + "cell_type": "code", + "source": [ + "embeddings.search(\"select id, act, prompt, score from txtai where similar('Linux terminal', 'act') and similar('Linux terminal', 'prompt')\", 1)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "2Yqky3ntYKUJ", + "outputId": "02672b7b-9e28-4d6d-dd74-acaf475ddf72" + }, + "execution_count": 27, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '0',\n", + " 'act': 'Linux Terminal',\n", + " 'prompt': 'I want you to act as a linux terminal. I will type commands and you will reply with what the terminal should show. I want you to only reply with the terminal output inside one unique code block, and nothing else. do not write explanations. do not type commands unless I instruct you to do so. when i need to tell you something in english, i will do so by putting text inside curly brackets {like this}. my first command is pwd',\n", + " 'score': 0.7881423830986023}]" + ] + }, + "metadata": {}, + "execution_count": 27 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "As always, everything discussed so far is also supported with txtai application instances." + ], + "metadata": { + "id": "obZhFSIKZT4u" + } + }, + { + "cell_type": "code", + "source": [ + "# Build index with graph subindex\n", + "app = txtai.Application(\"\"\"\n", + "writable: True\n", + "embeddings:\n", + " content: True\n", + " defaults: False\n", + " functions:\n", + " - name: graph\n", + " function: indexes.act.graph.attribute\n", + " expressions:\n", + " - name: topic\n", + " expression: graph(indexid, 'topic')\n", + " indexes:\n", + " act:\n", + " path: intfloat/e5-small-v2\n", + " columns:\n", + " text: act\n", + " graph:\n", + " topics:\n", + " prompt:\n", + " path: sentence-transformers/all-MiniLM-L6-v2\n", + " columns:\n", + " text: prompt\n", + "\"\"\")\n", + "\n", + "app.add(ds)\n", + "app.index()\n", + "\n", + "app.search(\"select id, act, prompt, topic, score from txtai where similar('Linux terminal', 'act') and similar('Linux terminal', 'prompt')\", 1)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "3MDvZoaGZZhI", + "outputId": "981072e4-70bf-4f2e-bc92-ee92083ec21a" + }, + "execution_count": 28, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '0',\n", + " 'act': 'Linux Terminal',\n", + " 'prompt': 'I want you to act as a linux terminal. I will type commands and you will reply with what the terminal should show. I want you to only reply with the terminal output inside one unique code block, and nothing else. do not write explanations. do not type commands unless I instruct you to do so. when i need to tell you something in english, i will do so by putting text inside curly brackets {like this}. my first command is pwd',\n", + " 'topic': 'linux_terminal',\n", + " 'score': 0.7881423830986023}]" + ] + }, + "metadata": {}, + "execution_count": 28 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Streamlined methods\n", + "\n", + "Much of this has been covered already but a number of changes were added to make it easier to search and index data. The existing interfaces are all still supported, this is all about ease of use.\n", + "\n", + "See the code explanations below." + ], + "metadata": { + "id": "WNSJ6tR1Y_KI" + } + }, + { + "cell_type": "code", + "source": [ + "# Top-level import includes Application and Embeddings\n", + "import txtai\n", + "\n", + "app = txtai.Application(\"\"\"writable: False\"\"\")\n", + "embeddings = txtai.Embeddings()" + ], + "metadata": { + "id": "MoW5QkxNZKBL" + }, + "execution_count": 29, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Ids are automatically generated when omitted\n", + "embeddings.index([\"test\"])\n", + "print(embeddings.search(\"test\"))\n", + "\n", + "# UUID ids are also supported - use any of the methods in https://docs.python.org/3/library/uuid.html\n", + "embeddings = txtai.Embeddings(autoid=\"uuid5\")\n", + "embeddings.index([\"test\"])\n", + "embeddings.search(\"test\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "6CckssTWbD3y", + "outputId": "e826ea96-e6aa-4902-d75d-4ed2df031a65" + }, + "execution_count": 30, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[(0, 0.9999998807907104)]\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[('4be0643f-1d98-573b-97cd-ca98a65347dd', 0.9999998807907104)]" + ] + }, + "metadata": {}, + "execution_count": 30 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Large Language Models (LLMs)\n", + "\n", + "While the bulk of the changes in this release came with the embeddings package, LLMs also have important changes that make it easier to use." + ], + "metadata": { + "id": "v_Dt9kltcRg8" + } + }, + { + "cell_type": "code", + "source": [ + "import torch\n", + "\n", + "from txtai import LLM\n", + "\n", + "# Create model and set dtype to use 16-bit floats\n", + "llm = LLM(\"tiiuae/falcon-rw-1b\", torch_dtype=torch.bfloat16)" + ], + "metadata": { + "id": "HL1caw21cfxO" + }, + "execution_count": 31, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "print(llm(\"Write a short list of things to do in Paris\", maxlength=55))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "G7OgcAUXg2RA", + "outputId": "83e86070-9212-4d21-b5b1-2712389cac09" + }, + "execution_count": 32, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + ".\n", + "- Visit the Eiffel Tower.\n", + "- Visit the Louvre.\n", + "- Visit the Arc de Triomphe.\n", + "- Visit the Notre Dame Cathedral.\n", + "- Visit the Sacre Coeur Basilica\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The new `LLM` pipeline automatically detects the type of model and loads it using the best available method.\n", + "\n", + "The pipeline framework now passes through keyword arguments to the underlying methods, which adds support for new Hugging Face features automatically as they are released." + ], + "metadata": { + "id": "j06rZByEhwnO" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "This notebook gave a quick overview of txtai 6.0. Updated documentation and more examples will be forthcoming. There is much to cover and much to build on!\n", + "\n", + "See the following links for more information.\n", + "\n", + "- [6.0 Release on GitHub](https://github.com/neuml/txtai/releases/tag/v6.0.0)\n", + "- [Documentation site](https://neuml.github.io/txtai)" + ], + "metadata": { + "id": "tvnMO1Eai6Gy" + } + } + ] +} diff --git a/examples/47_Building_an_efficient_sparse_keyword_index_in_Python.ipynb b/examples/47_Building_an_efficient_sparse_keyword_index_in_Python.ipynb new file mode 100644 index 0000000..ea0a502 --- /dev/null +++ b/examples/47_Building_an_efficient_sparse_keyword_index_in_Python.ipynb @@ -0,0 +1,3207 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + } + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Building an efficient sparse keyword index in Python\n", + "\n", + "Semantic search is a new category of search built on recent advances in Natural Language Processing (NLP). Traditional search systems use keywords to find data. Semantic search has an understanding of natural language and identifies results that have the same meaning, not necessarily the same keywords.\n", + "\n", + "While semantic search adds amazing capabilities, sparse keyword indexes can still add value. There may be cases where finding an exact match is important or we just want a fast index to quickly do an initial scan of a dataset.\n", + "\n", + "Unfortunately, there aren't a ton of great options for a local Python-based keyword index library. Most of the options available don't scale and/or are highly inefficient, designed only for simple situations.\n", + "\n", + "Given that Python is an interpreted language, it often gets a bad rap from a performance standpoint. In some cases, it's justified as Python can be memory hungry and has a global interpreter lock (GIL) that forces single thread execution. But it is possible to build performant Python on par with other languages.\n", + "\n", + "This notebook will explore how to build an efficient sparse keyword index in Python and compare the results with other approaches." + ], + "metadata": { + "id": "v4J3FxbUn9CT" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ], + "metadata": { + "id": "W70a-UjTdDiA" + } + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "!pip install txtai pytrec_eval rank-bm25 elasticsearch==7.10.1\n", + "!pip uninstall -y tensorflow" + ], + "metadata": { + "id": "nfgwb14J4LO2" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Introducing the problem\n", + "\n", + "At a high level, keyword indexes work by tokenizing text into lists of tokens per document. These tokens are aggregated into frequencies per document and stored in term frequency sparse arrays.\n", + "\n", + "The term frequency arrays are sparse given that they only store a frequency when the token exists in a document. For example, if a token exists in 1 of 1000 documents, the sparse array only has a single entry. A dense array stores 1000 entries all with zeros except for one.\n", + "\n", + "One simple approach to store a term frequency sparse array in Python would be having a dictionary of `{id: frequency}` per token. The problem with this approach is that Python has significant object overhead.\n", + "\n", + "Let's inspect the size used for a single number." + ], + "metadata": { + "id": "vF3hlZGkqMlh" + } + }, + { + "cell_type": "code", + "source": [ + "import sys\n", + "\n", + "a = 100\n", + "sys.getsizeof(a)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "nGX8FMTcqn2c", + "outputId": "8580d98b-1901-49a5-d81c-8a2827257d3c" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "28" + ] + }, + "metadata": {}, + "execution_count": 2 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "28 bytes for a single integer. Compared to a native int/long which is 4 or 8 bytes, this is quite wasteful. Imagine having thousands of `id: frequency` mappings. Memory usage will grow fast.\n", + "\n", + "Let's demonstrate. The code below runs a self contained Python process that creates a list of 10 million numbers.\n", + "\n", + "Running as a separate process helps calculate more accurate memory usage stats." + ], + "metadata": { + "id": "99ixudd1q7jB" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "e_cBVXU-jYDQ", + "outputId": "ba6e00a3-6acb-4f2e-c985-719eb0d1d36e" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Writing arrays.py\n" + ] + } + ], + "source": [ + "%%writefile arrays.py\n", + "import psutil\n", + "\n", + "results = []\n", + "for x in range(int(1e7)):\n", + " results.append(x)\n", + "\n", + "print(f\"MEMORY USAGE = {psutil.Process().memory_info().rss / (1024 * 1024)} MB\")" + ] + }, + { + "cell_type": "code", + "source": [ + "!python arrays.py" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "IJaAjPbGnGya", + "outputId": "adab040c-b7de-4d2f-83dc-5ec0f867a993" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "MEMORY USAGE = 394.640625 MB\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Approximately 395 MB of memory is used for this array. That seems high." + ], + "metadata": { + "id": "bRrR8eOlvBRD" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Efficient numeric arrays in Python\n", + "\n", + "Fortunately, Python has a module for building [efficient arrays of numeric values](https://docs.python.org/3/library/array.html). This module enables building arrays with the same native type.\n", + "\n", + "Let's try doing that with a `long long` type, which takes 8 bytes." + ], + "metadata": { + "id": "C1fA62VGwLya" + } + }, + { + "cell_type": "code", + "source": [ + "%%writefile arrays.py\n", + "from array import array\n", + "\n", + "import psutil\n", + "\n", + "results = array(\"q\")\n", + "for x in range(int(1e7)):\n", + " results.append(x)\n", + "\n", + "print(f\"MEMORY USAGE = {psutil.Process().memory_info().rss / (1024 * 1024)} MB\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "bqtUmEj4kjOQ", + "outputId": "736c3780-fed3-458c-be38-49feb42f416b" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Overwriting arrays.py\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "!python arrays.py" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "5lgHsgQqnI5q", + "outputId": "726c596b-9896-4e8d-baa7-04cb8892bdea" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "MEMORY USAGE = 88.54296875 MB\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "As we can see, memory usage went from 395 MB to 89 MB. That's a 4x reduction which is in line with the earlier calculate of 28 bytes/number vs 8 bytes/number." + ], + "metadata": { + "id": "HS_uKPRhv2mV" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Efficient processing of numeric data\n", + "\n", + "Large computations in pure Python can also be painfully slow. Luckily, there is a robust landscape of options for numeric processing. The most popular framework is [NumPy](https://github.com/numpy/numpy). There is also [PyTorch](https://github.com/pytorch/pytorch) and other GPU-based tensor processing frameworks.\n", + "\n", + "Below is a simple example that sorts an array in Python vs NumPy to demonstrate." + ], + "metadata": { + "id": "vGEjWEaGwX9s" + } + }, + { + "cell_type": "code", + "source": [ + "import random\n", + "import time\n", + "\n", + "data = [random.randint(1, 500) for x in range(1000000)]\n", + "\n", + "start = time.time()\n", + "sorted(data, reverse=True)\n", + "print(time.time() - start)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Ggln2VOSw5tY", + "outputId": "6bcbf8b2-01b1-41a3-bcad-f73f7220ca17" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "0.33922290802001953\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "import numpy as np\n", + "\n", + "data = np.array(data)\n", + "\n", + "start = time.time()\n", + "np.sort(data)[::-1]\n", + "print(time.time() - start)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "IV-FhI4UxkV2", + "outputId": "ac3d33d1-c8e3-4ae0-f6fa-fd619e81dd5a" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "0.10296249389648438\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "As we can see, sorting an array in NumPy is significantly faster. It might not seem like a lot but this adds up when run in bulk." + ], + "metadata": { + "id": "sejHtgn1zBjk" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Sparse keyword indexes in txtai\n", + "\n", + "Now that we've discussed the key performance concepts, let's talk about how to apply this to building sparse keyword indexes.\n", + "\n", + "Going back to the original approach for a term frequency sparse array, we see that using the Python array package is more efficient. In txtai, this method is used to build term frequency arrays for each token. This results in near native speed and memory usage.\n", + "\n", + "The search method uses a number of NumPy methods to efficiently calculate query term matches. Each query is tokenized and those token term frequency arrays are retrieved to calculate query scores. These NumPy methods are all written in C and often drop the GIL. So once again, near native speed and the ability to use multithreading.\n", + "\n", + "Read the [full implementation on GitHub](https://github.com/neuml/txtai/blob/master/src/python/txtai/scoring/terms.py) to learn more.\n" + ], + "metadata": { + "id": "gPeTqCflzP5B" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Evaluating performance\n", + "\n", + "First, a review of the landscape. As said in the introduction, there aren't a ton of good options. [Apache Lucene](https://github.com/apache/lucene) is by far the best traditional search index from a speed, performance and functionality standpoint. It's the base for Elasticsearch/OpenSearch and many other projects. But it requires Java.\n", + "\n", + "Here are the options we'll explore.\n", + "\n", + "- [Rank-BM25](https://github.com/dorianbrown/rank_bm25) project, the top result when searching for `python bm25`.\n", + "\n", + "- [SQLite FTS5](https://www.sqlite.org/fts5.html) extension. This extension builds a sparse keyword index right in SQLite.\n", + "\n", + "We'll use the BEIR dataset. We'll also use a [benchmarks script](https://raw.githubusercontent.com/neuml/txtai/master/examples/benchmarks.py) from the txtai project. This benchmarks script has methods to work with the BEIR dataset.\n", + "\n", + "Couple important caveats on the benchmarks script.\n", + "\n", + "- For the SQLite FTS implementation, each token is joined together with an `OR` clause. SQLite FTS [implicitly joins clauses together](https://www.sqlite.org/fts5.html) with `AND` clauses by default. By contrast, [Lucene's default operator](https://lucene.apache.org/core/9_7_0/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#Boolean_operators) is an `OR`.\n", + "- The Elasticsearch implementation uses 7.x as it's simpler to instantiate in a notebook.\n", + "- All methods except Elasticsearch use txtai's [unicode tokenizer](https://github.com/neuml/txtai/blob/master/src/python/txtai/pipeline/data/tokenizer.py) to tokenize text for consistency" + ], + "metadata": { + "id": "rKCRLFNh39hV" + } + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "import os\n", + "\n", + "# Get benchmarks script\n", + "os.system(\"wget https://raw.githubusercontent.com/neuml/txtai/master/examples/benchmarks.py\")\n", + "\n", + "# Create output directory\n", + "os.makedirs(\"beir\", exist_ok=True)\n", + "\n", + "# Download subset of BEIR datasets\n", + "datasets = [\"trec-covid\", \"nfcorpus\", \"webis-touche2020\", \"scidocs\", \"scifact\"]\n", + "for dataset in datasets:\n", + " url = f\"https://public.ukp.informatik.tu-darmstadt.de/thakur/BEIR/datasets/{dataset}.zip\"\n", + " os.system(f\"wget {url}\")\n", + " os.system(f\"mv {dataset}.zip beir\")\n", + " os.system(f\"unzip -d beir beir/{dataset}.zip\")\n", + "\n", + " # Remove existing benchmark data\n", + "if os.path.exists(\"benchmarks.json\"):\n", + " os.remove(\"benchmarks.json\")" + ], + "metadata": { + "id": "IGKzkKWB60pg" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Now let's run the benchmarks." + ], + "metadata": { + "id": "SEH7Og8LiWRd" + } + }, + { + "cell_type": "code", + "source": [ + "# Remove existing benchmark data\n", + "if os.path.exists(\"benchmarks.json\"):\n", + " os.remove(\"benchmarks.json\")\n", + "\n", + "# Runs benchmark evaluation\n", + "def evaluate(method):\n", + " for dataset in datasets:\n", + " command = f\"python benchmarks.py beir {dataset} {method}\"\n", + " print(command)\n", + " os.system(command)\n", + "\n", + "# Calculate benchmarks\n", + "for method in [\"bm25\", \"rank\", \"sqlite\"]:\n", + " evaluate(method)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Hfpok07_5N1m", + "outputId": "190d6821-7ff2-4c25-d8ef-5c0ec0b6b04f" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "python benchmarks.py beir trec-covid bm25\n", + "python benchmarks.py beir nfcorpus bm25\n", + "python benchmarks.py beir webis-touche2020 bm25\n", + "python benchmarks.py beir scidocs bm25\n", + "python benchmarks.py beir scifact bm25\n", + "python benchmarks.py beir trec-covid rank\n", + "python benchmarks.py beir nfcorpus rank\n", + "python benchmarks.py beir webis-touche2020 rank\n", + "python benchmarks.py beir scidocs rank\n", + "python benchmarks.py beir scifact rank\n", + "python benchmarks.py beir trec-covid sqlite\n", + "python benchmarks.py beir nfcorpus sqlite\n", + "python benchmarks.py beir webis-touche2020 sqlite\n", + "python benchmarks.py beir scidocs sqlite\n", + "python benchmarks.py beir scifact sqlite\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "import json\n", + "import pandas as pd\n", + "\n", + "def benchmarks():\n", + " # Read JSON lines data\n", + " with open(\"benchmarks.json\") as f:\n", + " data = f.read()\n", + "\n", + " df = pd.read_json(data, lines=True).sort_values(by=[\"source\", \"search\"])\n", + " return df[[\"source\", \"method\", \"index\", \"memory\", \"search\", \"ndcg_cut_10\", \"map_cut_10\", \"recall_10\", \"P_10\"]].reset_index(drop=True)\n", + "\n", + "# Load benchmarks dataframe\n", + "df = benchmarks()" + ], + "metadata": { + "id": "cpmNpwag73DW" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "df[df.source == \"trec-covid\"].reset_index(drop=True)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "ln4oUAfgLas4", + "outputId": "3e3249df-600a-444a-c46e-b839d215ef83" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " source method index memory search ndcg_cut_10 map_cut_10 \\\n", + "0 trec-covid bm25 101.96 997 0.28 0.58119 0.01247 \n", + "1 trec-covid sqlite 60.16 880 23.09 0.56778 0.01190 \n", + "2 trec-covid rank 61.75 3245 75.49 0.57773 0.01210 \n", + "\n", + " recall_10 P_10 \n", + "0 0.01545 0.618 \n", + "1 0.01519 0.610 \n", + "2 0.01550 0.632 " + ], + "text/html": [ + "\n", + "\n", + "
\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sourcemethodindexmemorysearchndcg_cut_10map_cut_10recall_10P_10
0trec-covidbm25101.969970.280.581190.012470.015450.618
1trec-covidsqlite60.1688023.090.567780.011900.015190.610
2trec-covidrank61.75324575.490.577730.012100.015500.632
\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + "
\n", + " \n", + "
\n", + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + " \n", + "\n", + " \n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 12 + } + ] + }, + { + "cell_type": "code", + "source": [ + "df[df.source == \"nfcorpus\"].reset_index(drop=True)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "bSx6dXhLM66g", + "outputId": "504f47de-e2ca-4837-a158-4f0f0704f08b" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " source method index memory search ndcg_cut_10 map_cut_10 \\\n", + "0 nfcorpus bm25 2.64 648 1.08 0.30639 0.11728 \n", + "1 nfcorpus sqlite 1.50 630 12.73 0.30695 0.11785 \n", + "2 nfcorpus rank 2.75 700 23.78 0.30692 0.11711 \n", + "\n", + " recall_10 P_10 \n", + "0 0.14891 0.21734 \n", + "1 0.14871 0.21641 \n", + "2 0.15320 0.21889 " + ], + "text/html": [ + "\n", + "\n", + "
\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sourcemethodindexmemorysearchndcg_cut_10map_cut_10recall_10P_10
0nfcorpusbm252.646481.080.306390.117280.148910.21734
1nfcorpussqlite1.5063012.730.306950.117850.148710.21641
2nfcorpusrank2.7570023.780.306920.117110.153200.21889
\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + "
\n", + " \n", + "
\n", + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + " \n", + "\n", + " \n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 13 + } + ] + }, + { + "cell_type": "code", + "source": [ + "df[df.source == \"webis-touche2020\"].reset_index(drop=True)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "W-hAhuYHNK_6", + "outputId": "6c28d842-f572-454b-86f3-a19783640757" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " source method index memory search ndcg_cut_10 map_cut_10 \\\n", + "0 webis-touche2020 bm25 374.66 1137 0.37 0.36920 0.14588 \n", + "1 webis-touche2020 sqlite 220.46 1416 34.61 0.37194 0.14812 \n", + "2 webis-touche2020 rank 224.07 10347 81.22 0.39861 0.16492 \n", + "\n", + " recall_10 P_10 \n", + "0 0.22736 0.34694 \n", + "1 0.22890 0.35102 \n", + "2 0.23770 0.36122 " + ], + "text/html": [ + "\n", + "\n", + "
\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sourcemethodindexmemorysearchndcg_cut_10map_cut_10recall_10P_10
0webis-touche2020bm25374.6611370.370.369200.145880.227360.34694
1webis-touche2020sqlite220.46141634.610.371940.148120.228900.35102
2webis-touche2020rank224.071034781.220.398610.164920.237700.36122
\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + "
\n", + " \n", + "
\n", + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + " \n", + "\n", + " \n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 14 + } + ] + }, + { + "cell_type": "code", + "source": [ + "df[df.source == \"scidocs\"].reset_index(drop=True)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "ln7p-b9XNPmO", + "outputId": "26a53b7f-a047-4062-b7a9-45d0b27dbef9" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " source method index memory search ndcg_cut_10 map_cut_10 recall_10 \\\n", + "0 scidocs bm25 17.95 717 1.64 0.15063 0.08756 0.15637 \n", + "1 scidocs sqlite 17.85 670 56.64 0.15156 0.08822 0.15717 \n", + "2 scidocs rank 13.11 1056 162.99 0.14932 0.08670 0.15408 \n", + "\n", + " P_10 \n", + "0 0.0772 \n", + "1 0.0776 \n", + "2 0.0761 " + ], + "text/html": [ + "\n", + "\n", + "
\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sourcemethodindexmemorysearchndcg_cut_10map_cut_10recall_10P_10
0scidocsbm2517.957171.640.150630.087560.156370.0772
1scidocssqlite17.8567056.640.151560.088220.157170.0776
2scidocsrank13.111056162.990.149320.086700.154080.0761
\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + "
\n", + " \n", + "
\n", + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + " \n", + "\n", + " \n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 15 + } + ] + }, + { + "cell_type": "code", + "source": [ + "df[df.source == \"scifact\"].reset_index(drop=True)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "CsHEwmV0NTjm", + "outputId": "591030c6-57fb-4f06-c133-9ab0fef3646e" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " source method index memory search ndcg_cut_10 map_cut_10 recall_10 \\\n", + "0 scifact bm25 5.51 653 1.07 0.66324 0.61764 0.78761 \n", + "1 scifact sqlite 1.85 631 20.28 0.66630 0.61966 0.79494 \n", + "2 scifact rank 1.85 724 42.22 0.65618 0.61204 0.77400 \n", + "\n", + " P_10 \n", + "0 0.087 \n", + "1 0.088 \n", + "2 0.085 " + ], + "text/html": [ + "\n", + "\n", + "
\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sourcemethodindexmemorysearchndcg_cut_10map_cut_10recall_10P_10
0scifactbm255.516531.070.663240.617640.787610.087
1scifactsqlite1.8563120.280.666300.619660.794940.088
2scifactrank1.8572442.220.656180.612040.774000.085
\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + "
\n", + " \n", + "
\n", + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + " \n", + "\n", + " \n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 16 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The sections above show the metrics per source and method.\n", + "\n", + "The table headers list the `source (dataset)`, `index method`, `index time(s)`, `memory usage(MB)`, `search time(s)` and `NDCG@10`/`MAP@10`/`RECALL@10`/`P@10` accuracy metrics. The tables are sorted by `search time`.\n", + "\n", + "As we can see, txtai's implementation has the fastest search times across the board. But it is slower when it comes to index time. The accuracy metrics vary slightly but are all about the same per method.\n", + "\n", + "Memory usage stands out. SQLite and txtai both have around the same usage per source. Rank-BM25 memory usage can get out of hand fast. For example, `webis-touch2020`, which is only ~400K records, uses `10 GB` of memory compared to `700 MB` for the other implementations." + ], + "metadata": { + "id": "tU1eFDZUh0NQ" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Compare with Elasticsearch\n", + "\n", + "Now that we've reviewed methods to build keyword indexes in Python, let's see how txtai's sparse keyword index compares to Elasticsearch.\n", + "\n", + "We'll spin up an inline instance and run the same evaluations." + ], + "metadata": { + "id": "_9tn39MN0LV9" + } + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "# Download and extract elasticsearch\n", + "os.system(\"wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.10.1-linux-x86_64.tar.gz\")\n", + "os.system(\"tar -xzf elasticsearch-7.10.1-linux-x86_64.tar.gz\")\n", + "os.system(\"chown -R daemon:daemon elasticsearch-7.10.1\")" + ], + "metadata": { + "id": "GZu0nj_R_NqB" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "from subprocess import Popen, PIPE, STDOUT\n", + "\n", + "# Start and wait for server\n", + "server = Popen(['elasticsearch-7.10.1/bin/elasticsearch'], stdout=PIPE, stderr=STDOUT, preexec_fn=lambda: os.setuid(1))\n", + "!sleep 30" + ], + "metadata": { + "id": "SsQsr-my_Poy" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Add benchmark evaluations for Elasticsearch\n", + "evaluate(\"es\")\n", + "\n", + "# Reload benchmarks dataframe\n", + "df = benchmarks()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "QSnpA2sjA5X0", + "outputId": "d4805ee9-1e3c-4fec-8f73-9b30edd4854d" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "python benchmarks.py beir trec-covid es\n", + "python benchmarks.py beir nfcorpus es\n", + "python benchmarks.py beir webis-touche2020 es\n", + "python benchmarks.py beir scidocs es\n", + "python benchmarks.py beir scifact es\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "df[df.source == \"trec-covid\"].reset_index(drop=True)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 175 + }, + "id": "zAZolShYaXyf", + "outputId": "1a338a45-799c-483c-83a4-037b8e8c1780" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " source method index memory search ndcg_cut_10 map_cut_10 \\\n", + "0 trec-covid bm25 101.96 997 0.28 0.58119 0.01247 \n", + "1 trec-covid es 71.24 636 2.09 0.59215 0.01261 \n", + "2 trec-covid sqlite 60.16 880 23.09 0.56778 0.01190 \n", + "3 trec-covid rank 61.75 3245 75.49 0.57773 0.01210 \n", + "\n", + " recall_10 P_10 \n", + "0 0.01545 0.618 \n", + "1 0.01590 0.636 \n", + "2 0.01519 0.610 \n", + "3 0.01550 0.632 " + ], + "text/html": [ + "\n", + "\n", + "
\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sourcemethodindexmemorysearchndcg_cut_10map_cut_10recall_10P_10
0trec-covidbm25101.969970.280.581190.012470.015450.618
1trec-covides71.246362.090.592150.012610.015900.636
2trec-covidsqlite60.1688023.090.567780.011900.015190.610
3trec-covidrank61.75324575.490.577730.012100.015500.632
\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + "
\n", + " \n", + "
\n", + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + " \n", + "\n", + " \n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 20 + } + ] + }, + { + "cell_type": "code", + "source": [ + "df[df.source == \"nfcorpus\"].reset_index(drop=True)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 175 + }, + "id": "3kKe6A6CbKbp", + "outputId": "5c8ca9cb-0d59-4110-eacf-547bba8f1445" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " source method index memory search ndcg_cut_10 map_cut_10 \\\n", + "0 nfcorpus bm25 2.64 648 1.08 0.30639 0.11728 \n", + "1 nfcorpus es 3.95 627 11.47 0.30676 0.11761 \n", + "2 nfcorpus sqlite 1.50 630 12.73 0.30695 0.11785 \n", + "3 nfcorpus rank 2.75 700 23.78 0.30692 0.11711 \n", + "\n", + " recall_10 P_10 \n", + "0 0.14891 0.21734 \n", + "1 0.14894 0.21610 \n", + "2 0.14871 0.21641 \n", + "3 0.15320 0.21889 " + ], + "text/html": [ + "\n", + "\n", + "
\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sourcemethodindexmemorysearchndcg_cut_10map_cut_10recall_10P_10
0nfcorpusbm252.646481.080.306390.117280.148910.21734
1nfcorpuses3.9562711.470.306760.117610.148940.21610
2nfcorpussqlite1.5063012.730.306950.117850.148710.21641
3nfcorpusrank2.7570023.780.306920.117110.153200.21889
\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + "
\n", + " \n", + "
\n", + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + " \n", + "\n", + " \n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 21 + } + ] + }, + { + "cell_type": "code", + "source": [ + "df[df.source == \"webis-touche2020\"].reset_index(drop=True)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 175 + }, + "id": "wKCYo54hbVUC", + "outputId": "2314f23c-1ed6-4f77-db4d-c579f163878b" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " source method index memory search ndcg_cut_10 map_cut_10 \\\n", + "0 webis-touche2020 bm25 374.66 1137 0.37 0.36920 0.14588 \n", + "1 webis-touche2020 es 168.28 629 0.62 0.37519 0.14819 \n", + "2 webis-touche2020 sqlite 220.46 1416 34.61 0.37194 0.14812 \n", + "3 webis-touche2020 rank 224.07 10347 81.22 0.39861 0.16492 \n", + "\n", + " recall_10 P_10 \n", + "0 0.22736 0.34694 \n", + "1 0.22889 0.35102 \n", + "2 0.22890 0.35102 \n", + "3 0.23770 0.36122 " + ], + "text/html": [ + "\n", + "\n", + "
\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sourcemethodindexmemorysearchndcg_cut_10map_cut_10recall_10P_10
0webis-touche2020bm25374.6611370.370.369200.145880.227360.34694
1webis-touche2020es168.286290.620.375190.148190.228890.35102
2webis-touche2020sqlite220.46141634.610.371940.148120.228900.35102
3webis-touche2020rank224.071034781.220.398610.164920.237700.36122
\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + "
\n", + " \n", + "
\n", + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + " \n", + "\n", + " \n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 22 + } + ] + }, + { + "cell_type": "code", + "source": [ + "df[df.source == \"scidocs\"].reset_index(drop=True)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 175 + }, + "id": "yt5j8wF1bNka", + "outputId": "5f2d0506-3578-47e4-abde-1cb6736fc1d5" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " source method index memory search ndcg_cut_10 map_cut_10 recall_10 \\\n", + "0 scidocs bm25 17.95 717 1.64 0.15063 0.08756 0.15637 \n", + "1 scidocs es 11.07 632 10.25 0.14924 0.08671 0.15497 \n", + "2 scidocs sqlite 17.85 670 56.64 0.15156 0.08822 0.15717 \n", + "3 scidocs rank 13.11 1056 162.99 0.14932 0.08670 0.15408 \n", + "\n", + " P_10 \n", + "0 0.0772 \n", + "1 0.0765 \n", + "2 0.0776 \n", + "3 0.0761 " + ], + "text/html": [ + "\n", + "\n", + "
\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sourcemethodindexmemorysearchndcg_cut_10map_cut_10recall_10P_10
0scidocsbm2517.957171.640.150630.087560.156370.0772
1scidocses11.0763210.250.149240.086710.154970.0765
2scidocssqlite17.8567056.640.151560.088220.157170.0776
3scidocsrank13.111056162.990.149320.086700.154080.0761
\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + "
\n", + " \n", + "
\n", + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + " \n", + "\n", + " \n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 23 + } + ] + }, + { + "cell_type": "code", + "source": [ + "df[df.source == \"scifact\"].reset_index(drop=True)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 175 + }, + "id": "7o3RVNt2bQFZ", + "outputId": "6723c801-b485-45b3-e619-ca5ddcf8e7c3" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " source method index memory search ndcg_cut_10 map_cut_10 recall_10 \\\n", + "0 scifact bm25 5.51 653 1.07 0.66324 0.61764 0.78761 \n", + "1 scifact es 2.90 625 9.62 0.66058 0.61518 0.78428 \n", + "2 scifact sqlite 1.85 631 20.28 0.66630 0.61966 0.79494 \n", + "3 scifact rank 1.85 724 42.22 0.65618 0.61204 0.77400 \n", + "\n", + " P_10 \n", + "0 0.08700 \n", + "1 0.08667 \n", + "2 0.08800 \n", + "3 0.08500 " + ], + "text/html": [ + "\n", + "\n", + "
\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sourcemethodindexmemorysearchndcg_cut_10map_cut_10recall_10P_10
0scifactbm255.516531.070.663240.617640.787610.08700
1scifactes2.906259.620.660580.615180.784280.08667
2scifactsqlite1.8563120.280.666300.619660.794940.08800
3scifactrank1.8572442.220.656180.612040.774000.08500
\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + "
\n", + " \n", + "
\n", + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + " \n", + "\n", + " \n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 24 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Once again txtai's implementation compares well with Elasticsearch. The accuracy metrics vary but are all about the same.\n", + "\n", + "It's important to note that in internal testing with solid state storage, Elasticsearch and txtai's speed is about the same. These times for Elasticsearch being a little slower are a product of running in a Google Colab environment." + ], + "metadata": { + "id": "1INPBYQ2lf22" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "This notebook showed how to build an efficient sparse keyword index in Python. The benchmarks show that txtai provides a strong implementation both from an accuracy and speed standpoint, on par with Apache Lucene.\n", + "\n", + "This keyword index can be used as a standalone index in Python or in combination with dense vector indexes to form a `hybrid` index." + ], + "metadata": { + "id": "f41NSYWc0dsy" + } + } + ] +} \ No newline at end of file diff --git a/examples/48_Benefits_of_hybrid_search.ipynb b/examples/48_Benefits_of_hybrid_search.ipynb new file mode 100644 index 0000000..82d8054 --- /dev/null +++ b/examples/48_Benefits_of_hybrid_search.ipynb @@ -0,0 +1,1478 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "gpuType": "T4" + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Benefits of hybrid search\n", + "\n", + "Semantic search is a new category of search built on recent advances in Natural Language Processing (NLP). Traditional search systems use keywords to find data. Semantic search has an understanding of natural language and identifies results that have the same meaning, not necessarily the same keywords.\n", + "\n", + "While semantic search adds amazing capabilities, sparse keyword indexes can still add value. There may be cases where finding an exact match is important or we just want a fast index to quickly do an initial scan of a dataset.\n", + "\n", + "Both methods have their merits. What if we combine them together to build a unified `hybrid` search capability? Can we get the best of both worlds?\n", + "\n", + "This notebook will explore the benefits of hybrid search." + ], + "metadata": { + "id": "v4J3FxbUn9CT" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ], + "metadata": { + "id": "W70a-UjTdDiA" + } + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "!pip install txtai pytrec_eval rank-bm25 elasticsearch\n", + "!pip uninstall -y tensorflow" + ], + "metadata": { + "id": "nfgwb14J4LO2" + }, + "execution_count": 1, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Introducing semantic, keyword and hybrid search\n", + "\n", + "Before diving into the benchmarks, let's briefly discuss how semantic and keyword search works.\n", + "\n", + "Semantic search uses large language models to vectorize inputs into arrays of numbers. Similar concepts will have similar values. The vectors are typically stored in a vector database, which is a system that specializes in storing these numerical arrays and finding matches. Vector search transforms an input query into a vector and then runs a search to find the best conceptual results.\n", + "\n", + "Keyword search tokenizes text into lists of tokens per document. These tokens are aggregated into token frequencies per document and stored in term frequency sparse arrays. At search time, the query is tokenized and the tokens of the query are compared to the tokens in the dataset. This is more a literal process. Keyword search is like string matching, it has no conceptual understanding, it matches on characters and bytes.\n", + "\n", + "Hybrid search combines the scores from semantic and keyword indexes. Given that semantic search scores are typically 0 - 1 and keyword search scores are unbounded, a method is needed to combine the results.\n", + "\n", + "The two methods supported in txtai are:\n", + "\n", + "- [Convex Combination](https://en.wikipedia.org/wiki/Convex_combination) when sparse scores are normalized\n", + "- [Reciprocal Rank Fusion (RRF)](https://plg.uwaterloo.ca/~gvcormac/cormacksigir09-rrf.pdf) when sparse scores aren't normalized\n", + "\n", + "The default method in txtai is convex combination and we'll use that." + ], + "metadata": { + "id": "gPeTqCflzP5B" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Evaluating performance\n", + "\n", + "Now it's time to benchmark the results. For these tests, we'll use the BEIR dataset. We'll also use a [benchmarks script](https://raw.githubusercontent.com/neuml/txtai/master/examples/benchmarks.py) from the txtai project. This benchmarks script has methods to work with the BEIR dataset.\n", + "\n", + "We'll select a subset of the BEIR sources for brevity. For each source, we'll benchmark a `bm25` index, an `embeddings` index and a `hybrid` or combined index." + ], + "metadata": { + "id": "rKCRLFNh39hV" + } + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "import os\n", + "\n", + "# Get benchmarks script\n", + "os.system(\"wget https://raw.githubusercontent.com/neuml/txtai/master/examples/benchmarks.py\")\n", + "\n", + "# Create output directory\n", + "os.makedirs(\"beir\", exist_ok=True)\n", + "\n", + "# Download subset of BEIR datasets\n", + "datasets = [\"nfcorpus\", \"fiqa\", \"arguana\", \"scidocs\", \"scifact\"]\n", + "for dataset in datasets:\n", + " url = f\"https://public.ukp.informatik.tu-darmstadt.de/thakur/BEIR/datasets/{dataset}.zip\"\n", + " os.system(f\"wget {url}\")\n", + " os.system(f\"mv {dataset}.zip beir\")\n", + " os.system(f\"unzip -d beir beir/{dataset}.zip\")\n", + "\n", + " # Remove existing benchmark data\n", + "if os.path.exists(\"benchmarks.json\"):\n", + " os.remove(\"benchmarks.json\")" + ], + "metadata": { + "id": "IGKzkKWB60pg" + }, + "execution_count": 2, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Now let's run the benchmarks." + ], + "metadata": { + "id": "SEH7Og8LiWRd" + } + }, + { + "cell_type": "code", + "source": [ + "# Remove existing benchmark data\n", + "if os.path.exists(\"benchmarks.json\"):\n", + " os.remove(\"benchmarks.json\")\n", + "\n", + "# Runs benchmark evaluation\n", + "def evaluate(method):\n", + " for dataset in datasets:\n", + " command = f\"python benchmarks.py beir {dataset} {method}\"\n", + " print(command)\n", + " os.system(command)\n", + "\n", + "# Calculate benchmarks\n", + "for method in [\"bm25\", \"embed\", \"hybrid\"]:\n", + " evaluate(method)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Hfpok07_5N1m", + "outputId": "0c0c0906-5192-4b9e-b23a-d0f455106a70" + }, + "execution_count": 3, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "python benchmarks.py beir nfcorpus bm25\n", + "python benchmarks.py beir fiqa bm25\n", + "python benchmarks.py beir arguana bm25\n", + "python benchmarks.py beir scidocs bm25\n", + "python benchmarks.py beir scifact bm25\n", + "python benchmarks.py beir nfcorpus embed\n", + "python benchmarks.py beir fiqa embed\n", + "python benchmarks.py beir arguana embed\n", + "python benchmarks.py beir scidocs embed\n", + "python benchmarks.py beir scifact embed\n", + "python benchmarks.py beir nfcorpus hybrid\n", + "python benchmarks.py beir fiqa hybrid\n", + "python benchmarks.py beir arguana hybrid\n", + "python benchmarks.py beir scidocs hybrid\n", + "python benchmarks.py beir scifact hybrid\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "import json\n", + "import pandas as pd\n", + "\n", + "def benchmarks():\n", + " # Read JSON lines data\n", + " with open(\"benchmarks.json\") as f:\n", + " data = f.read()\n", + "\n", + " df = pd.read_json(data, lines=True).sort_values(by=[\"source\", \"ndcg_cut_10\"], ascending=[True, False])\n", + " return df[[\"source\", \"method\", \"ndcg_cut_10\", \"map_cut_10\", \"recall_10\", \"P_10\", \"index\", \"search\", \"memory\"]].reset_index(drop=True)\n", + "\n", + "# Load benchmarks dataframe\n", + "df = benchmarks()" + ], + "metadata": { + "id": "cpmNpwag73DW" + }, + "execution_count": 4, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "df[df.source == \"nfcorpus\"].reset_index(drop=True)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "DjApptqajCi_", + "outputId": "6d444b6e-a487-4661-dc73-bd42eb03b165" + }, + "execution_count": 9, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " source method ndcg_cut_10 map_cut_10 recall_10 P_10 index \\\n", + "0 nfcorpus hybrid 0.34531 0.13369 0.17437 0.25480 29.46 \n", + "1 nfcorpus embed 0.30917 0.10810 0.15327 0.23591 33.64 \n", + "2 nfcorpus bm25 0.30639 0.11728 0.14891 0.21734 2.72 \n", + "\n", + " search memory \n", + "0 3.57 2900 \n", + "1 3.33 2876 \n", + "2 0.96 652 " + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sourcemethodndcg_cut_10map_cut_10recall_10P_10indexsearchmemory
0nfcorpushybrid0.345310.133690.174370.2548029.463.572900
1nfcorpusembed0.309170.108100.153270.2359133.643.332876
2nfcorpusbm250.306390.117280.148910.217342.720.96652
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 9 + } + ] + }, + { + "cell_type": "code", + "source": [ + "df[df.source == \"fiqa\"].reset_index(drop=True)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "bSx6dXhLM66g", + "outputId": "5cf284c0-8bf3-4167-8cca-40c41370fea7" + }, + "execution_count": 8, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " source method ndcg_cut_10 map_cut_10 recall_10 P_10 index search \\\n", + "0 fiqa hybrid 0.36642 0.28846 0.43799 0.10340 233.90 68.42 \n", + "1 fiqa embed 0.36071 0.28450 0.43188 0.10216 212.30 58.83 \n", + "2 fiqa bm25 0.23559 0.17619 0.29855 0.06559 19.78 12.84 \n", + "\n", + " memory \n", + "0 3073 \n", + "1 2924 \n", + "2 761 " + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sourcemethodndcg_cut_10map_cut_10recall_10P_10indexsearchmemory
0fiqahybrid0.366420.288460.437990.10340233.9068.423073
1fiqaembed0.360710.284500.431880.10216212.3058.832924
2fiqabm250.235590.176190.298550.0655919.7812.84761
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 8 + } + ] + }, + { + "cell_type": "code", + "source": [ + "df[df.source == \"arguana\"].reset_index(drop=True)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "7LxFmbAEjKmP", + "outputId": "4b02d802-0dc9-4411-dc3b-1792de67cf1a" + }, + "execution_count": 10, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " source method ndcg_cut_10 map_cut_10 recall_10 P_10 index \\\n", + "0 arguana hybrid 0.48467 0.40101 0.75320 0.07532 37.80 \n", + "1 arguana embed 0.47781 0.38781 0.76671 0.07667 34.11 \n", + "2 arguana bm25 0.45713 0.37118 0.73471 0.07347 3.39 \n", + "\n", + " search memory \n", + "0 21.22 2924 \n", + "1 10.21 2910 \n", + "2 10.95 663 " + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sourcemethodndcg_cut_10map_cut_10recall_10P_10indexsearchmemory
0arguanahybrid0.484670.401010.753200.0753237.8021.222924
1arguanaembed0.477810.387810.766710.0766734.1110.212910
2arguanabm250.457130.371180.734710.073473.3910.95663
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 10 + } + ] + }, + { + "cell_type": "code", + "source": [ + "df[df.source == \"scidocs\"].reset_index(drop=True)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "ln7p-b9XNPmO", + "outputId": "3ae0445f-6fda-4a51-b229-b3e85273bb78" + }, + "execution_count": 6, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " source method ndcg_cut_10 map_cut_10 recall_10 P_10 index \\\n", + "0 scidocs embed 0.21718 0.12982 0.23217 0.1146 127.63 \n", + "1 scidocs hybrid 0.21104 0.12450 0.22938 0.1134 138.00 \n", + "2 scidocs bm25 0.15063 0.08756 0.15637 0.0772 13.07 \n", + "\n", + " search memory \n", + "0 4.41 2929 \n", + "1 6.43 2999 \n", + "2 1.42 722 " + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sourcemethodndcg_cut_10map_cut_10recall_10P_10indexsearchmemory
0scidocsembed0.217180.129820.232170.1146127.634.412929
1scidocshybrid0.211040.124500.229380.1134138.006.432999
2scidocsbm250.150630.087560.156370.077213.071.42722
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 6 + } + ] + }, + { + "cell_type": "code", + "source": [ + "df[df.source == \"scifact\"].reset_index(drop=True)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "CsHEwmV0NTjm", + "outputId": "6e97c4e8-318f-42e4-d162-037646cc3ed3" + }, + "execution_count": 7, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " source method ndcg_cut_10 map_cut_10 recall_10 P_10 index \\\n", + "0 scifact hybrid 0.71305 0.66773 0.83722 0.09367 39.51 \n", + "1 scifact bm25 0.66324 0.61764 0.78761 0.08700 4.40 \n", + "2 scifact embed 0.65149 0.60193 0.78972 0.08867 35.15 \n", + "\n", + " search memory \n", + "0 2.35 2918 \n", + "1 0.93 658 \n", + "2 1.48 2889 " + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sourcemethodndcg_cut_10map_cut_10recall_10P_10indexsearchmemory
0scifacthybrid0.713050.667730.837220.0936739.512.352918
1scifactbm250.663240.617640.787610.087004.400.93658
2scifactembed0.651490.601930.789720.0886735.151.482889
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 7 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The sections above show the metrics per source and method.\n", + "\n", + "The table headers list the `source (dataset)`, `index method`, `NDCG@10`/`MAP@10`/`RECALL@10`/`P@10` accuracy metrics, `index time(s)`, `search time(s)` and `memory usage(MB)`. The tables are sorted by `NDCG@10` descending.\n", + "\n", + "Looking at the results, we can see that `hybrid` search often performs better than `embeddings` or `bm25` individually. In some cases, as with scidocs, the combination performs worse. But in the aggregate, the scores are better. This holds true for the entire BEIR dataset. For some sources, `bm25` does best, some `embeddings` but overall the combined `hybrid` scores do the best.\n", + "\n", + "Hybrid search isn't free though, it is slower as it has extra logic to combine the results. For individual queries, the results are often negligible." + ], + "metadata": { + "id": "tU1eFDZUh0NQ" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "This notebook covered ways to improve search accuracy using a hybrid approach. We evaluated performance over a subset of the BEIR dataset to show how hybrid search, in many situations, can improve overall accuracy.\n", + "\n", + "Custom datasets can also be evaluated using this method as [specified in this link](https://github.com/beir-cellar/beir/wiki/Load-your-custom-dataset). This notebook and the associated benchmarks script can be reused to evaluate what method works best on your data.\n" + ], + "metadata": { + "id": "f41NSYWc0dsy" + } + } + ] +} \ No newline at end of file diff --git a/examples/49_External_database_integration.ipynb b/examples/49_External_database_integration.ipynb new file mode 100644 index 0000000..36895ba --- /dev/null +++ b/examples/49_External_database_integration.ipynb @@ -0,0 +1,802 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# External database integration\n", + "\n", + "txtai provides many default settings to help a developer quickly get started. For example, metadata is stored in SQLite, dense vectors in Faiss, sparse vectors in a terms index and graph data with NetworkX.\n", + "\n", + "Each of these components is customizable and can be swapped with alternate implementations. This has been covered in [several previous notebooks](https://neuml.github.io/txtai/examples/#architecture).\n", + "\n", + "This notebook will introduce how to store metadata in client-server RDBMS systems. In addition to SQLite and DuckDB, any [SQLAlchemy-supported database](https://docs.sqlalchemy.org/en/20/dialects/) with [JSON support](https://docs.sqlalchemy.org/en/20/core/type_basics.html#sqlalchemy.types.JSON) can now be used." + ], + "metadata": { + "id": "781Mrl0FtFR8" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ], + "metadata": { + "id": "VG8sklTPwlJa" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "XrqaDHiCnFnr" + }, + "outputs": [], + "source": [ + "%%capture\n", + "\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[database] elasticsearch==7.10.1 datasets" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Install Postgres\n", + "\n", + "Next, we'll install Postgres and start a Postgres instance." + ], + "metadata": { + "id": "6pc469NKwyMu" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "1JZhUIMmkmil" + }, + "outputs": [], + "source": [ + "%%capture\n", + "\n", + "# Install and start Postgres\n", + "!apt-get update && apt-get install postgresql\n", + "!service postgresql start\n", + "!sudo -u postgres psql -U postgres -c \"ALTER USER postgres PASSWORD 'postgres';\"" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Load a dataset\n", + "\n", + "Now we're ready to load a dataset. We'll use the `ag_news` dataset. This dataset consists of 120,000 news headlines." + ], + "metadata": { + "id": "lS16J4QBxDJP" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "TMVkdPSgNlD0" + }, + "outputs": [], + "source": [ + "from datasets import load_dataset\n", + "\n", + "# Load dataset\n", + "ds = load_dataset(\"ag_news\", split=\"train\")" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Build an Embeddings instance with Postgres\n", + "\n", + "Let's load this dataset into an embeddings database. We'll configure this instance to store metadata in Postgres. Note that the content parameter below is a [SQLAlchemy connection string](https://docs.sqlalchemy.org/en/20/core/engines.html).\n", + "\n", + "This embeddings database will use the default vector settings and build that index locally." + ], + "metadata": { + "id": "iERZH9w1xQGC" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "OmnobNLGNhW4" + }, + "outputs": [], + "source": [ + "import txtai\n", + "\n", + "# Create embeddings\n", + "embeddings = txtai.Embeddings(\n", + " content=\"postgresql+psycopg2://postgres:postgres@localhost/postgres\",\n", + ")\n", + "\n", + "# Index dataset\n", + "embeddings.index(ds[\"text\"])" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Let's run a search query and see what comes back." + ], + "metadata": { + "id": "GqOMCOT0xnnC" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "qD4tBdMKI4MX", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "109fe906-ca74-49c9-ed4e-922eaefd398e" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '63561',\n", + " 'text': 'Red Sox Beat Yankees 6-4 in 12 Innings BOSTON - Down to their last three outs of the season, the Boston Red Sox rallied - against Mariano Rivera, the New York Yankees and decades of disappointment. Bill Mueller singled home the tying run off Rivera in the ninth inning and David Ortiz homered against Paul Quantrill in the 12th, leading Boston to a 6-4 victory Sunday over the Yankees that avoided a four-game sweep in the AL championship series...',\n", + " 'score': 0.8104304671287537},\n", + " {'id': '63221',\n", + " 'text': 'Red Sox Beat Yankees 6-4 in 12 Innings BOSTON - Down to their last three outs of the season, the Boston Red Sox rallied - against Mariano Rivera, the New York Yankees and decades of disappointment. Bill Mueller singled home the tying run off Rivera in the ninth inning and David Ortiz homered against Paul Quantrill in the 12th, leading Boston to a 6-4 victory over the Yankees on Sunday night that avoided a four-game sweep in the AL championship series...',\n", + " 'score': 0.8097385168075562},\n", + " {'id': '66861',\n", + " 'text': 'Record-Breaking Red Sox Clinch World Series Berth NEW YORK (Reuters) - The Boston Red Sox crushed the New York Yankees 10-3 Wednesday to complete an historic comeback victory over their arch-rivals by four games to three in the American League Championship Series.',\n", + " 'score': 0.8003846406936646}]" + ] + }, + "metadata": {}, + "execution_count": 24 + } + ], + "source": [ + "embeddings.search(\"red sox defeat yankees\")" + ] + }, + { + "cell_type": "markdown", + "source": [ + "As expected, we get the standard `id, text, score` fields with the top matches for the query. The difference though is that all the database metadata normally stored in a local SQLite file is now stored in a Postgres server.\n", + "\n", + "This opens up several possibilities such as row-level security. If a row isn't returned by the database, it won't be shown here. Alternatively, this search could optionally return only the ids and scores, which lets the user know a record exists they don't have access to.\n", + "\n", + "As with other supported databases, underlying database functions can be called from txtai SQL." + ], + "metadata": { + "id": "SeV3i6vixs-e" + } + }, + { + "cell_type": "code", + "source": [ + "embeddings.search(\"SELECT id, text, md5(text), score FROM txtai WHERE similar('red sox defeat yankees')\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "3PQAoPfvSyP1", + "outputId": "1eed6cf0-6402-4760-b673-5798fc329d0e" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '63561',\n", + " 'text': 'Red Sox Beat Yankees 6-4 in 12 Innings BOSTON - Down to their last three outs of the season, the Boston Red Sox rallied - against Mariano Rivera, the New York Yankees and decades of disappointment. Bill Mueller singled home the tying run off Rivera in the ninth inning and David Ortiz homered against Paul Quantrill in the 12th, leading Boston to a 6-4 victory Sunday over the Yankees that avoided a four-game sweep in the AL championship series...',\n", + " 'md5': '1e55a78fdf0cb3be3ef61df650f0a50f',\n", + " 'score': 0.8104304671287537},\n", + " {'id': '63221',\n", + " 'text': 'Red Sox Beat Yankees 6-4 in 12 Innings BOSTON - Down to their last three outs of the season, the Boston Red Sox rallied - against Mariano Rivera, the New York Yankees and decades of disappointment. Bill Mueller singled home the tying run off Rivera in the ninth inning and David Ortiz homered against Paul Quantrill in the 12th, leading Boston to a 6-4 victory over the Yankees on Sunday night that avoided a four-game sweep in the AL championship series...',\n", + " 'md5': 'a0417e1fc503a5a2945c8755b6fb18d5',\n", + " 'score': 0.8097385168075562},\n", + " {'id': '66861',\n", + " 'text': 'Record-Breaking Red Sox Clinch World Series Berth NEW YORK (Reuters) - The Boston Red Sox crushed the New York Yankees 10-3 Wednesday to complete an historic comeback victory over their arch-rivals by four games to three in the American League Championship Series.',\n", + " 'md5': '398a8508692aed109bd8c56f067a8083',\n", + " 'score': 0.8003846406936646}]" + ] + }, + "metadata": {}, + "execution_count": 25 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Note the addition of the Postgres `md5` function to the query.\n", + "\n", + "Let's save and show the files in the embeddings database." + ], + "metadata": { + "id": "lqkfoknYziiU" + } + }, + { + "cell_type": "code", + "source": [ + "embeddings.save(\"vectors\")\n", + "!ls -l vectors" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "gKTsjD3uS0YG", + "outputId": "aa2ec6af-509b-468e-a7e9-11b550707073" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "total 183032\n", + "-rw-r--r-- 1 root root 355 Sep 7 16:38 config\n", + "-rw-r--r-- 1 root root 187420123 Sep 7 16:38 embeddings\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Only the configuration and the local vectors index are stored in this case." + ], + "metadata": { + "id": "ylGVrmL1ztG0" + } + }, + { + "cell_type": "markdown", + "source": [ + "# External indexing\n", + "\n", + "As mentioned previously, all of the main components of txtai can be replaced with custom components. For example, there are external integrations for storing dense vectors in [Weaviate](https://github.com/hsm207/weaviate-txtai) and [Qdrant](https://github.com/qdrant/qdrant-txtai) to name a few.\n", + "\n", + "Next, we'll build an example that stores metadata in Postgres and builds a sparse index with Elasticsearch." + ], + "metadata": { + "id": "RUqaj-tVz19K" + } + }, + { + "cell_type": "markdown", + "source": [ + "## Scoring component for Elasticsearch\n", + "\n", + "First, we need to define a custom scoring component for Elasticsearch. While could have used an existing integration, it's important to show that creating a new component isn't a large LOE (~70 lines of code). See below." + ], + "metadata": { + "id": "OZLCQceF3US9" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "fbPd8uXZrekP" + }, + "outputs": [], + "source": [ + "from elasticsearch import Elasticsearch\n", + "from elasticsearch.helpers import bulk\n", + "\n", + "from txtai.scoring import Scoring\n", + "\n", + "class Elastic(Scoring):\n", + " def __init__(self, config=None):\n", + " # Scoring configuration\n", + " self.config = config if config else {}\n", + "\n", + " # Server parameters\n", + " self.url = self.config.get(\"url\", \"http://localhost:9200\")\n", + " self.indexname = self.config.get(\"indexname\", \"testindex\")\n", + "\n", + " # Elasticsearch connection\n", + " self.connection = Elasticsearch(self.url)\n", + "\n", + " self.terms = True\n", + " self.normalize = self.config.get(\"normalize\")\n", + "\n", + " def insert(self, documents, index=None):\n", + " rows = []\n", + " for uid, document, tags in documents:\n", + " rows.append((index, document))\n", + "\n", + " # Increment index\n", + " index = index + 1\n", + "\n", + " bulk(self.connection, ({\"_index\": self.indexname, \"_id\": uid, \"text\": text} for uid, text in rows))\n", + "\n", + " def index(self, documents=None):\n", + " self.connection.indices.refresh(index=self.indexname)\n", + "\n", + " def search(self, query, limit=3):\n", + " return self.batchsearch([query], limit)\n", + "\n", + " def batchsearch(self, queries, limit=3):\n", + " # Generate bulk queries\n", + " request = []\n", + " for query in queries:\n", + " req_head = {\"index\": self.indexname, \"search_type\": \"dfs_query_then_fetch\"}\n", + " req_body = {\n", + " \"_source\": False,\n", + " \"query\": {\"multi_match\": {\"query\": query, \"type\": \"best_fields\", \"fields\": [\"text\"], \"tie_breaker\": 0.5}},\n", + " \"size\": limit,\n", + " }\n", + " request.extend([req_head, req_body])\n", + "\n", + " # Run ES query\n", + " response = self.connection.msearch(body=request, request_timeout=600)\n", + "\n", + " # Read responses\n", + " results = []\n", + " for resp in response[\"responses\"]:\n", + " result = resp[\"hits\"][\"hits\"]\n", + " results.append([(r[\"_id\"], r[\"_score\"]) for r in result])\n", + "\n", + " return results\n", + "\n", + " def count(self):\n", + " response = self.connection.cat.count(self.indexname, params={\"format\": \"json\"})\n", + " return int(response[0][\"count\"])\n", + "\n", + " def load(self, path):\n", + " # No local storage\n", + " pass\n", + "\n", + " def save(self, path):\n", + " # No local storage\n", + " pass" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Elasticsearch server\n", + "\n", + "As with Postgres, we'll install and start an Elasticsearch instance." + ], + "metadata": { + "id": "3Zu1en9A2fP7" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "bZFJGE9RpRTt" + }, + "outputs": [], + "source": [ + "%%capture\n", + "import os\n", + "\n", + "# Download and extract elasticsearch\n", + "os.system(\"wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.10.1-linux-x86_64.tar.gz\")\n", + "os.system(\"tar -xzf elasticsearch-7.10.1-linux-x86_64.tar.gz\")\n", + "os.system(\"chown -R daemon:daemon elasticsearch-7.10.1\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "oeCGDn5NpR9p" + }, + "outputs": [], + "source": [ + "from subprocess import Popen, PIPE, STDOUT\n", + "\n", + "# Start and wait for serverw\n", + "server = Popen(['elasticsearch-7.10.1/bin/elasticsearch'], stdout=PIPE, stderr=STDOUT, preexec_fn=lambda: os.setuid(1))\n", + "!sleep 30" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Let's build the index. The only difference from the previous example is setting the custom `scoring` component." + ], + "metadata": { + "id": "l9WI7NJy4TXo" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ZCQhwUVRnnEY" + }, + "outputs": [], + "source": [ + "import txtai\n", + "\n", + "# Create embeddings\n", + "embeddings = txtai.Embeddings(\n", + " keyword=True,\n", + " content=\"postgresql+psycopg2://postgres:postgres@localhost/postgres\",\n", + " scoring= \"__main__.Elastic\"\n", + ")\n", + "\n", + "# Index dataset\n", + "embeddings.index(ds[\"text\"])" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Below is the same search as shown before." + ], + "metadata": { + "id": "JrfZTEyE4Q-i" + } + }, + { + "cell_type": "code", + "source": [ + "embeddings.search(\"red sox defeat yankees\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "VFgIV_S8Stby", + "outputId": "75f3b9ab-2805-4a3c-ca93-b843e21af439" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '66954',\n", + " 'text': 'Boston Red Sox make history Believe it, New England -- the Boston Red Sox are in the World Series. And they got there with the most unbelievable comeback of all, with four sweet swings after decades of defeat, shaming the dreaded New York Yankees.',\n", + " 'score': 21.451942},\n", + " {'id': '69577',\n", + " 'text': 'Passing thoughts on Yankees-Red Sox series The Red Sox beat the Yankees at Yankee Stadium in a season-deciding game. The Red Sox beat the Yankees at Yankee Stadium in a season-deciding game and it wasn #39;t even close.',\n", + " 'score': 20.923117},\n", + " {'id': '67253',\n", + " 'text': 'Sox Victorious At Last!! BOSTON -- After suffering decades of defeat and disappointment, the 2004 Boston Red Sox made history Wednesday night, beating the Yankees in the house that Ruth built and claiming the American League championship trophy.',\n", + " 'score': 20.865997}]" + ] + }, + "metadata": {}, + "execution_count": 31 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "And once again we get the top matches. This time though the index is in Elasticsearch. Why are results and scores different? This is because this is a keyword index and it's using Elasticsearch's raw BM25 scores.\n", + "\n", + "One enhancement to this component would be adding score normalization as seen in the standard scoring components.\n", + "\n", + "For good measure, let's also show that the `md5` function can be called here too." + ], + "metadata": { + "id": "PE9SAEDn4gDE" + } + }, + { + "cell_type": "code", + "source": [ + "embeddings.search(\"SELECT id, text, md5(text), score FROM txtai WHERE similar('red sox defeat yankees')\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "4oYAIFPqWbSq", + "outputId": "98178870-393e-42ca-c1d6-9c91239ddabe" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '66954',\n", + " 'text': 'Boston Red Sox make history Believe it, New England -- the Boston Red Sox are in the World Series. And they got there with the most unbelievable comeback of all, with four sweet swings after decades of defeat, shaming the dreaded New York Yankees.',\n", + " 'md5': '29084f8640d4d72e402e991bc9fdbfa0',\n", + " 'score': 21.451942},\n", + " {'id': '69577',\n", + " 'text': 'Passing thoughts on Yankees-Red Sox series The Red Sox beat the Yankees at Yankee Stadium in a season-deciding game. The Red Sox beat the Yankees at Yankee Stadium in a season-deciding game and it wasn #39;t even close.',\n", + " 'md5': '056983d301975084b49a5987185f2ddf',\n", + " 'score': 20.923117},\n", + " {'id': '67253',\n", + " 'text': 'Sox Victorious At Last!! BOSTON -- After suffering decades of defeat and disappointment, the 2004 Boston Red Sox made history Wednesday night, beating the Yankees in the house that Ruth built and claiming the American League championship trophy.',\n", + " 'md5': '7838fcf610f0b569829c9bafdf9012f2',\n", + " 'score': 20.865997}]" + ] + }, + "metadata": {}, + "execution_count": 32 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Same results with the additional `md5` column, as expected." + ], + "metadata": { + "id": "zW1NkPpA5V0b" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Explore the data stores\n", + "\n", + "The last thing we'll do is see where and how this data is stored in Postgres and Elasticsearch.\n", + "\n", + "Let's connect to the local Postgres instance and sample content from the `sections` table." + ], + "metadata": { + "id": "54R2s6CM5a-6" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "pWRJ4vNkn2QL", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "fab25634-474f-441d-ef82-cf903b8c8762" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "env: DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres\n", + "The sql extension is already loaded. To reload it, use:\n", + " %reload_ext sql\n" + ] + } + ], + "source": [ + "%env DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres\n", + "%load_ext sql" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "axy-m3JMn9H8", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 175 + }, + "outputId": "6d7a523f-722e-4139-a4f2-db30f8a8f877" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + " * postgresql://postgres:***@localhost:5432/postgres\n", + "3 rows affected.\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[('66954', 'Boston Red Sox make history Believe it, New England -- the Boston Red Sox are in the World Series. And they got there with the most unbelievable comeback of all, with four sweet swings after decades of defeat, shaming the dreaded New York Yankees.'),\n", + " ('62732', \"BoSox, Astros Play for Crucial Game 4 Wins (AP) AP - The Boston Red Sox entered this AL championship series hoping to finally overcome their bitter r ... (50 characters truncated) ... n-game defeat last October. Instead, they've been reduced to trying to prevent the Yankees from completing a humiliating sweep in their own ballpark.\"),\n", + " ('62752', \"BoSox, Astros Play for Crucial Game 4 Wins The Boston Red Sox entered this AL championship series hoping to finally overcome their bitter rivals from ... (42 characters truncated) ... game defeat last October. Instead, they've been reduced to trying to prevent the Yankees from completing a humiliating sweep in their own ballpark...\")]" + ], + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idtext
66954Boston Red Sox make history Believe it, New England -- the Boston Red Sox are in the World Series. And they got there with the most unbelievable comeback of all, with four sweet swings after decades of defeat, shaming the dreaded New York Yankees.
62732BoSox, Astros Play for Crucial Game 4 Wins (AP) AP - The Boston Red Sox entered this AL championship series hoping to finally overcome their bitter rivals from New York following a heartbreaking seven-game defeat last October. Instead, they've been reduced to trying to prevent the Yankees from completing a humiliating sweep in their own ballpark.
62752BoSox, Astros Play for Crucial Game 4 Wins The Boston Red Sox entered this AL championship series hoping to finally overcome their bitter rivals from New York following a heartbreaking seven-game defeat last October. Instead, they've been reduced to trying to prevent the Yankees from completing a humiliating sweep in their own ballpark...
" + ] + }, + "metadata": {}, + "execution_count": 34 + } + ], + "source": [ + "%%sql\n", + "select id, text from sections where text like '%Red Sox%' and text like '%Yankees%' and text like '%defeat%' limit 3;" + ] + }, + { + "cell_type": "markdown", + "source": [ + "As expected, we can see content stored directly in Postgres!\n", + "\n", + "Now let's check Elasticsearch." + ], + "metadata": { + "id": "6JfMoZPB5uZo" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "X6A7GP7ZBm_v", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "151df22c-6d43-4599-eba6-a579fe437677" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "{\n", + " \"took\": 13,\n", + " \"timed_out\": false,\n", + " \"_shards\": {\n", + " \"total\": 1,\n", + " \"successful\": 1,\n", + " \"skipped\": 0,\n", + " \"failed\": 0\n", + " },\n", + " \"hits\": {\n", + " \"total\": {\n", + " \"value\": 3297,\n", + " \"relation\": \"eq\"\n", + " },\n", + " \"max_score\": 21.451942,\n", + " \"hits\": [\n", + " {\n", + " \"_index\": \"testindex\",\n", + " \"_type\": \"_doc\",\n", + " \"_id\": \"66954\",\n", + " \"_score\": 21.451942,\n", + " \"_source\": {\n", + " \"text\": \"Boston Red Sox make history Believe it, New England -- the Boston Red Sox are in the World Series. And they got there with the most unbelievable comeback of all, with four sweet swings after decades of defeat, shaming the dreaded New York Yankees.\"\n", + " }\n", + " },\n", + " {\n", + " \"_index\": \"testindex\",\n", + " \"_type\": \"_doc\",\n", + " \"_id\": \"69577\",\n", + " \"_score\": 20.923117,\n", + " \"_source\": {\n", + " \"text\": \"Passing thoughts on Yankees-Red Sox series The Red Sox beat the Yankees at Yankee Stadium in a season-deciding game. The Red Sox beat the Yankees at Yankee Stadium in a season-deciding game and it wasn #39;t even close.\"\n", + " }\n", + " },\n", + " {\n", + " \"_index\": \"testindex\",\n", + " \"_type\": \"_doc\",\n", + " \"_id\": \"67253\",\n", + " \"_score\": 20.865997,\n", + " \"_source\": {\n", + " \"text\": \"Sox Victorious At Last!! BOSTON -- After suffering decades of defeat and disappointment, the 2004 Boston Red Sox made history Wednesday night, beating the Yankees in the house that Ruth built and claiming the American League championship trophy.\"\n", + " }\n", + " }\n", + " ]\n", + " }\n", + "}\n" + ] + } + ], + "source": [ + "import json\n", + "import requests\n", + "\n", + "response = requests.get(\"http://localhost:9200/_search?q=red+sox+defeat+yankees&size=3\")\n", + "print(json.dumps(response.json(), indent=2))" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Same query results as what was run through the embeddings database.\n", + "\n", + "Let's save the embeddings database and review what's stored." + ], + "metadata": { + "id": "6sS0ym5Y59w9" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "BsFNRvRzoj9T", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "38bef15f-2ce3-4c8a-9bd0-3d7d0c84d794" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "total 4\n", + "-rw-r--r-- 1 root root 155 Sep 7 16:39 config\n" + ] + } + ], + "source": [ + "embeddings.save(\"elastic\")\n", + "!ls -l elastic" + ] + }, + { + "cell_type": "markdown", + "source": [ + "And all we have is the configuration. No `database`, `embeddings` or `scoring` files. That data is in Postgres and Elasticsearch!" + ], + "metadata": { + "id": "OAtoEmV36FWp" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "This notebook showed how external databases and other external integrations can be used with embeddings databases. This architecture ensures that as new ways to index and store data become available, txtai can easily adapt.\n", + "\n", + "This notebook also showed how creating a custom component is a low level of effort and can easily be done for a component without an existing integration.\n" + ], + "metadata": { + "id": "SQxnieiT6RTe" + } + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/examples/50_All_about_vector_quantization.ipynb b/examples/50_All_about_vector_quantization.ipynb new file mode 100644 index 0000000..9de44c7 --- /dev/null +++ b/examples/50_All_about_vector_quantization.ipynb @@ -0,0 +1,1040 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "gpuType": "T4" + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# All about vector quantization\n", + "\n", + "txtai supports a number of approximate nearest neighbor (ANN) libraries for vector storage. This includes [Faiss](https://github.com/facebookresearch/faiss), [Hnswlib](https://github.com/nmslib/hnswlib), [Annoy](https://github.com/spotify/annoy), [NumPy](https://github.com/numpy/numpy) and [PyTorch](https://github.com/pytorch/pytorch). Custom implementations can also be added.\n", + "\n", + "The default ANN for txtai is Faiss. Faiss has by far the largest array of configurable options in building an ANN index. This article will cover quantization and different approaches that are possible along with the tradeoffs." + ], + "metadata": { + "id": "-0mtX6Faensl" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ], + "metadata": { + "id": "1LYCxMqcje3z" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "aBdRdW-xeh6l" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai pytrec_eval rank-bm25 elasticsearch psutil" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Preparing the datasets\n", + "\n", + "First, let's download a subset of the datasets from the BEIR evaluation framework. We'll also retrieve the standard txtai benchmark script. These will be used to help judge the accuracy of quantization methods." + ], + "metadata": { + "id": "4qEigu7Ajo27" + } + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "import os\n", + "\n", + "# Get benchmarks script\n", + "os.system(\"wget https://raw.githubusercontent.com/neuml/txtai/master/examples/benchmarks.py\")\n", + "\n", + "# Create output directory\n", + "os.makedirs(\"beir\", exist_ok=True)\n", + "\n", + "if os.path.exists(\"benchmarks.json\"):\n", + " os.remove(\"benchmarks.json\")\n", + "\n", + "# Download subset of BEIR datasets\n", + "datasets = [\"nfcorpus\", \"arguana\", \"scifact\"]\n", + "for dataset in datasets:\n", + " url = f\"https://public.ukp.informatik.tu-darmstadt.de/thakur/BEIR/datasets/{dataset}.zip\"\n", + " os.system(f\"wget {url}\")\n", + " os.system(f\"mv {dataset}.zip beir\")\n", + " os.system(f\"unzip -d beir beir/{dataset}.zip\")" + ], + "metadata": { + "id": "uFmK9srYjzPT" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Evaluation\n", + "\n", + "Next, we'll setup the scaffolding to run evaluations." + ], + "metadata": { + "id": "McJE_K3lnLTQ" + } + }, + { + "cell_type": "code", + "source": [ + "import pandas as pd\n", + "import yaml\n", + "\n", + "def writeconfig(dataset, quantize):\n", + " sources = {\"arguana\": \"IVF11\", \"nfcorpus\": \"IDMap\", \"scifact\": \"IVF6\"}\n", + " config = {\n", + " \"embeddings\": {\n", + " \"batch\": 8192,\n", + " \"encodebatch\": 128,\n", + " \"faiss\": {\n", + " \"sample\": 0.05\n", + " }\n", + " }\n", + " }\n", + "\n", + " if quantize and quantize[-1].isdigit() and int(quantize[-1]) < 4:\n", + " # Use vector quantization for 1, 2 and 3 bit quantization\n", + " config[\"embeddings\"][\"quantize\"] = int(quantize[-1])\n", + " elif quantize:\n", + " # Use Faiss quantization for other forms of quantization\n", + " config[\"embeddings\"][\"faiss\"][\"components\"] = f\"{sources[dataset]},{quantize}\"\n", + "\n", + " # Derive name\n", + " name = quantize if quantize else \"baseline\"\n", + "\n", + " # Derive config path and write output\n", + " path = f\"{dataset}_{name}.yml\"\n", + " with open(path, \"w\") as f:\n", + " yaml.dump(config, f)\n", + "\n", + " return name, path\n", + "\n", + "def benchmarks():\n", + " # Read JSON lines data\n", + " with open(\"benchmarks.json\") as f:\n", + " data = f.read()\n", + "\n", + " df = pd.read_json(data, lines=True).sort_values(by=[\"source\", \"ndcg_cut_10\"], ascending=[True, False])\n", + " return df[[\"source\", \"name\", \"ndcg_cut_10\", \"map_cut_10\", \"recall_10\", \"P_10\", \"disk\"]].reset_index(drop=True)\n", + "\n", + "# Runs benchmark evaluation\n", + "def evaluate(quantize=None):\n", + " for dataset in datasets:\n", + " # Build config based on requested quantization\n", + " name, config = writeconfig(dataset, quantize)\n", + "\n", + " command = f\"python benchmarks.py -d beir -s {dataset} -m embeddings -c \\\"{config}\\\" -n \\\"{name}\\\"\"\n", + " os.system(command)\n" + ], + "metadata": { + "id": "Olhj91QwmsL-" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Establish a baseline\n", + "\n", + "Before introducing vector quantization, let's establish a baseline of accuracy per source without quantization. The following table shows accuracy metrics along with the disk storage size in KB." + ], + "metadata": { + "id": "EfeGhKjvuYoQ" + } + }, + { + "cell_type": "code", + "source": [ + "evaluate()\n", + "benchmarks()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "i-_eYDf7ryhL", + "outputId": "f2c22805-5eb3-402d-f5aa-be3117d768f9" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " source name ndcg_cut_10 map_cut_10 recall_10 P_10 disk\n", + "0 arguana baseline 0.47886 0.38931 0.76600 0.07660 13416\n", + "1 nfcorpus baseline 0.30893 0.10789 0.15315 0.23622 5517\n", + "2 scifact baseline 0.65273 0.60386 0.78972 0.08867 7878" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sourcenamendcg_cut_10map_cut_10recall_10P_10disk
0arguanabaseline0.478860.389310.766000.0766013416
1nfcorpusbaseline0.308930.107890.153150.236225517
2scifactbaseline0.652730.603860.789720.088677878
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 10 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Quantization\n", + "\n", + "The two main types of vector [quantization](https://en.wikipedia.org/wiki/Quantization_(signal_processing)) are scalar quantization and product quantization.\n", + "\n", + "Scalar quantization maps floating point data to a series of integers. For example, 8-bit quantization splits the range of floats into 255 buckets. This cuts data storage down by 4 when working with 32-bit floats, since each dimension now only stores 1 byte vs 4. A more dramatic version of this is binary or 1-bit quantization, where the floating point range is cut in half, 0 or 1. The trade-off as one would expect is accuracy.\n", + "\n", + "Product quantization is similar in that the process bins a floating point range into codes but it's more complex. This method splits vectors across dimensions into subvectors and runs those subvectors through a clustering algorithm. This can lead to a substantial reduction in data storage at the expense of accuracy like with scalar quantization. The [Faiss documentation](https://github.com/facebookresearch/faiss/wiki#research-foundations-of-faiss) has a number of great papers with more information on this method.\n", + "\n", + "Quantization is available at the vector processing and datastore levels in txtai. In both cases, it requires an ANN backend that can support integer vectors. Currently, only Faiss, NumPy and Torch are supported.\n", + "\n", + "Let's benchmark a variety of quantization methods." + ], + "metadata": { + "id": "tVv825vJ0uc0" + } + }, + { + "cell_type": "code", + "source": [ + "# Evaluate quantization methods\n", + "for quantize in [\"SQ1\", \"SQ4\", \"SQ8\", \"PQ48x4fs\", \"PQ96x4fs\", \"PQ192x4fs\"]:\n", + " evaluate(quantize)\n", + "\n", + "# Show benchmarks\n", + "benchmarks()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 708 + }, + "id": "s9tB_b9ttYSM", + "outputId": "16d49e7b-7476-4b6d-d64a-bed3b8627f0a" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " source name ndcg_cut_10 map_cut_10 recall_10 P_10 disk\n", + "0 arguana baseline 0.47886 0.38931 0.76600 0.07660 13416\n", + "1 arguana SQ8 0.47781 0.38781 0.76671 0.07667 3660\n", + "2 arguana SQ4 0.47771 0.38915 0.76174 0.07617 2034\n", + "3 arguana PQ192x4fs 0.46322 0.37341 0.75391 0.07539 1260\n", + "4 arguana PQ96x4fs 0.43744 0.35052 0.71906 0.07191 844\n", + "5 arguana SQ1 0.42604 0.33997 0.70555 0.07055 795\n", + "6 arguana PQ48x4fs 0.40220 0.31653 0.67852 0.06785 637\n", + "7 nfcorpus SQ4 0.31028 0.10758 0.15417 0.23839 751\n", + "8 nfcorpus SQ8 0.30917 0.10810 0.15327 0.23591 1433\n", + "9 nfcorpus baseline 0.30893 0.10789 0.15315 0.23622 5517\n", + "10 nfcorpus PQ192x4fs 0.30722 0.10678 0.15168 0.23467 433\n", + "11 nfcorpus PQ96x4fs 0.29594 0.09929 0.13996 0.22693 262\n", + "12 nfcorpus SQ1 0.26582 0.08579 0.12658 0.19907 237\n", + "13 nfcorpus PQ48x4fs 0.25874 0.08100 0.11912 0.19567 177\n", + "14 scifact SQ4 0.65299 0.60328 0.79139 0.08867 1078\n", + "15 scifact baseline 0.65273 0.60386 0.78972 0.08867 7878\n", + "16 scifact SQ8 0.65149 0.60193 0.78972 0.08867 2050\n", + "17 scifact PQ192x4fs 0.64046 0.58823 0.78933 0.08867 622\n", + "18 scifact PQ96x4fs 0.62256 0.57773 0.74861 0.08400 375\n", + "19 scifact SQ1 0.58724 0.53418 0.73989 0.08267 338\n", + "20 scifact PQ48x4fs 0.52292 0.46611 0.68744 0.07700 251" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sourcenamendcg_cut_10map_cut_10recall_10P_10disk
0arguanabaseline0.478860.389310.766000.0766013416
1arguanaSQ80.477810.387810.766710.076673660
2arguanaSQ40.477710.389150.761740.076172034
3arguanaPQ192x4fs0.463220.373410.753910.075391260
4arguanaPQ96x4fs0.437440.350520.719060.07191844
5arguanaSQ10.426040.339970.705550.07055795
6arguanaPQ48x4fs0.402200.316530.678520.06785637
7nfcorpusSQ40.310280.107580.154170.23839751
8nfcorpusSQ80.309170.108100.153270.235911433
9nfcorpusbaseline0.308930.107890.153150.236225517
10nfcorpusPQ192x4fs0.307220.106780.151680.23467433
11nfcorpusPQ96x4fs0.295940.099290.139960.22693262
12nfcorpusSQ10.265820.085790.126580.19907237
13nfcorpusPQ48x4fs0.258740.081000.119120.19567177
14scifactSQ40.652990.603280.791390.088671078
15scifactbaseline0.652730.603860.789720.088677878
16scifactSQ80.651490.601930.789720.088672050
17scifactPQ192x4fs0.640460.588230.789330.08867622
18scifactPQ96x4fs0.622560.577730.748610.08400375
19scifactSQ10.587240.534180.739890.08267338
20scifactPQ48x4fs0.522920.466110.687440.07700251
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 11 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Review\n", + "\n", + "Each of the sources above were run through a series of scalar and product quantization settings. The accuracy vs disk space trade off is clear to see.\n", + "\n", + "Couple key points to highlight.\n", + "\n", + "- The vector model outputs vectors with 384 dimensions\n", + "- Scalar quantization (SQ) was evaluated for 1-bit (binary), 4 and 8 bits\n", + "- 1-bit (binary) quantization stores vectors in [binary indexes](https://github.com/facebookresearch/faiss/wiki/Binary-indexes)\n", + "- For product quantization (PQ), three methods were tested. 48, 96 and 192 codes respectively, all using 4-bit codes\n", + "\n", + "In general, the larger the index size, the better the scores. There are a few exceptions to this but the differences are minimal in those cases. The smaller scalar and product quantization indexes are up to 20 times smaller.\n", + "\n", + "It's important to note that the smaller scalar methods typically need a wider number of dimensions to perform competitively. With that being said, even at 384 dimensions, binary quantization still does OK. txtai supports scalar quantization precisions from 1 through 8 bits.\n", + "\n", + "This is just a subset of the available quantization methods available in Faiss. More details can be found in the [Faiss documentation](https://github.com/facebookresearch/faiss/wiki/The-index-factory)." + ], + "metadata": { + "id": "2shnHFK17S4u" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "This notebook evaluated a variety of vector quantization methods. Quantization is an option to reduce storage costs at the expense of accuracy. Larger vector models (1024+ dimensions) will retain accuracy better with more aggressive quantization methods. As always, results will vary depending on your data." + ], + "metadata": { + "id": "ZrErGwtzFdBC" + } + } + ] +} \ No newline at end of file diff --git a/examples/51_Custom_API_Endpoints.ipynb b/examples/51_Custom_API_Endpoints.ipynb new file mode 100644 index 0000000..0a9aa44 --- /dev/null +++ b/examples/51_Custom_API_Endpoints.ipynb @@ -0,0 +1,454 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "gpuType": "T4" + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Custom API Endpoints\n", + "\n", + "The [txtai API](https://neuml.github.io/txtai/api/) is a web-based service backed by [FastAPI](https://fastapi.tiangolo.com/). Semantic search, LLM orchestration and Language Model Workflows can all run through the API.\n", + "\n", + "While the API is extremely flexible and complex logic can be executed through YAML-driven workflows, some may prefer to create an endpoint in Python.\n", + "\n", + "This notebook introduces API extensions and shows how they can be used to define custom Python endpoints that interact with txtai applications." + ], + "metadata": { + "id": "VGeVB8M41jqW" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ], + "metadata": { + "id": "ZQrHIw351lwE" + } + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "id": "R0AqRP7v1hdr" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[api] datasets" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Define the extension\n", + "\n", + "First, we'll create an application that defines a persistent embeddings database and LLM. Then we'll combine those two into a RAG endpoint through the API." + ], + "metadata": { + "id": "xmPN8RDF1pXd" + } + }, + { + "cell_type": "code", + "source": [ + "%%writefile app.yml\n", + "\n", + "# Embeddings index\n", + "writable: true\n", + "embeddings:\n", + " hybrid: true\n", + " content: true\n", + "\n", + "# LLM pipeline\n", + "llm:\n", + " path: Qwen/Qwen3-4B-Instruct-2507" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "XZ7vPBIs1rGZ", + "outputId": "b5cf95f1-1a99-4839-ae9b-9141922bd248" + }, + "execution_count": 2, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Writing app.yml\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The code below creates an API endpoint at `/rag`. This is a `GET` endpoint that takes a `text` parameter as input." + ], + "metadata": { + "id": "syd1PZ621sok" + } + }, + { + "cell_type": "code", + "source": [ + "%%writefile rag.py\n", + "from fastapi import APIRouter\n", + "from txtai.api import application, Extension\n", + "\n", + "\n", + "class RAG(Extension):\n", + " \"\"\"\n", + " API extension\n", + " \"\"\"\n", + "\n", + " def __call__(self, app):\n", + " app.include_router(RAGRouter().router)\n", + "\n", + "\n", + "class RAGRouter:\n", + " \"\"\"\n", + " API router\n", + " \"\"\"\n", + "\n", + " router = APIRouter()\n", + "\n", + " @staticmethod\n", + " @router.get(\"/rag\")\n", + " def rag(text: str):\n", + " \"\"\"\n", + " Runs a retrieval augmented generation (RAG) pipeline.\n", + "\n", + " Args:\n", + " text: input text\n", + "\n", + " Returns:\n", + " response\n", + " \"\"\"\n", + "\n", + " # Run embeddings search\n", + " results = application.get().search(text, 3)\n", + " context = \" \".join([x[\"text\"] for x in results])\n", + "\n", + " prompt = f\"\"\"\n", + " Answer the following question using only the context below.\n", + "\n", + " Question: {text}\n", + " Context: {context}\n", + " \"\"\"\n", + "\n", + " return {\n", + " \"response\": application.get().pipeline(\"llm\", (prompt,))\n", + " }" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "zXERt7Vw1ujq", + "outputId": "2c680298-895b-419c-967d-70030265f5a6" + }, + "execution_count": 3, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Writing rag.py\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Start the API instance\n", + "\n", + "Let's start the API with the RAG extension." + ], + "metadata": { + "id": "p7vl6_9i1w39" + } + }, + { + "cell_type": "code", + "source": [ + "!CONFIG=app.yml EXTENSIONS=rag.RAG nohup uvicorn \"txtai.api:app\" &> api.log &\n", + "!sleep 60" + ], + "metadata": { + "id": "FRif4lhW1y8m" + }, + "execution_count": 4, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Create the embeddings database\n", + "\n", + "Next, we'll create the embeddings database using the `ag_news` dataset. This is a set of news stories from the mid 2000s." + ], + "metadata": { + "id": "FTdkEDa0106G" + } + }, + { + "cell_type": "code", + "source": [ + "from datasets import load_dataset\n", + "import requests\n", + "\n", + "ds = load_dataset(\"ag_news\", split=\"train\")\n", + "\n", + "# API endpoint\n", + "url = \"http://localhost:8000\"\n", + "headers = {\"Content-Type\": \"application/json\"}\n", + "\n", + "# Add data\n", + "batch = []\n", + "for text in ds[\"text\"]:\n", + " batch.append({\"text\": text})\n", + " if len(batch) == 4096:\n", + " requests.post(f\"{url}/add\", headers=headers, json=batch, timeout=120)\n", + " batch = []\n", + "\n", + "if batch:\n", + " requests.post(f\"{url}/add\", headers=headers, json=batch, timeout=120)\n", + "\n", + "# Build index\n", + "index = requests.get(f\"{url}/index\")" + ], + "metadata": { + "id": "Ns6BKNQQ13FA" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Run queries\n", + "\n", + "Now that we have a knowledge source indexed, let's run a set of queries. The code below defines a method that calls the `/rag` endpoint and retrieves the response. Keep in mind this dataset is from 2004.\n", + "\n", + "While the Python Requests library is used in this notebook, this is a simple web endpoint that can be called from any programming language." + ], + "metadata": { + "id": "_wGvCWsP17it" + } + }, + { + "cell_type": "code", + "source": [ + "def rag(text):\n", + " return requests.get(f\"{url}/rag?text={text}\").json()[\"response\"]\n", + "\n", + "rag(\"Who is the current President?\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 39 + }, + "id": "_WbFu64L15Ch", + "outputId": "3d631fe8-d1d3-4437-bf64-9248599caff9" + }, + "execution_count": 14, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "'George W. Bush'" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + } + }, + "metadata": {}, + "execution_count": 14 + } + ] + }, + { + "cell_type": "code", + "source": [ + "rag(\"Who lost the presidential election?\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 39 + }, + "id": "YtJ7LJ_819vw", + "outputId": "e102b060-edb3-483c-98f9-50892e5e6c70" + }, + "execution_count": 15, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "'John Kerry'" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + } + }, + "metadata": {}, + "execution_count": 15 + } + ] + }, + { + "cell_type": "code", + "source": [ + "rag(\"Who won the World Series?\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 39 + }, + "id": "BlYDMTj41_QL", + "outputId": "4f58fb40-2e75-4248-8065-5efc969fdd0e" + }, + "execution_count": 16, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "'Boston'" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + } + }, + "metadata": {}, + "execution_count": 16 + } + ] + }, + { + "cell_type": "code", + "source": [ + "rag(\"Who did the Red Sox beat to win the world series?\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 39 + }, + "id": "XMHLmQ532ApE", + "outputId": "4bf5c7fa-dd42-43e3-b473-2df9d2c64d29" + }, + "execution_count": 17, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "'Cardinals'" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + } + }, + "metadata": {}, + "execution_count": 17 + } + ] + }, + { + "cell_type": "code", + "source": [ + "rag(\"What major hurricane hit the USA?\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 39 + }, + "id": "pTMqQSx82B_h", + "outputId": "9ee72bc9-664b-407d-ac65-95f1a09a2cb2" + }, + "execution_count": 18, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "'Charley'" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + } + }, + "metadata": {}, + "execution_count": 18 + } + ] + }, + { + "cell_type": "code", + "source": [ + "rag(\"What mobile phone manufacturer has the largest current marketshare?\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 39 + }, + "id": "BV99h7272DVj", + "outputId": "20602f12-09fe-4a44-a3f4-1797885e9d22" + }, + "execution_count": 19, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "'Nokia'" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + } + }, + "metadata": {}, + "execution_count": 19 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "This notebook showed how a txtai application can be extended with custom endpoints in Python. While applications have a robust workflow framework, it may be preferable to write complex logic in Python and this method enables that." + ], + "metadata": { + "id": "oPwgCgBc2Er2" + } + } + ] +} \ No newline at end of file diff --git a/examples/52_Build_RAG_pipelines_with_txtai.ipynb b/examples/52_Build_RAG_pipelines_with_txtai.ipynb new file mode 100644 index 0000000..c8a31db --- /dev/null +++ b/examples/52_Build_RAG_pipelines_with_txtai.ipynb @@ -0,0 +1,592 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "VGeVB8M41jqW" + }, + "source": [ + "# Build RAG pipelines with txtai\n", + "\n", + "Large Language Models (LLMs) have completely dominated the tech space in recent years. The results have been amazing and the public imagination is almost endless.\n", + "\n", + "While LLMs have been impressive, they are not problem free. The biggest challenge is with hallucinations. Hallucinations is the term for when a LLM generates output that is factually incorrect. The alarming part of this is that on a cursory glance, it actually sounds like good content. The default behavior of LLMs is to produce plausible answers even when no plausible answer exists. LLMs are not great at saying I don't know.\n", + "\n", + "Retrieval augmented generation (RAG) helps reduce the risk of hallucinations by limiting the context in which a LLM can generate answers. This is typically done with a vector search query that hydrates a prompt with a relevant context. RAG is one of the most practical and production-ready use cases for *Generative AI*. It's so popular now, that some are creating their entire companies around it.\n", + "\n", + "[txtai](https://github.com/neuml/txtai) has long had question-answering pipelines, which employ the same process of retrieving a relevant context. LLMs are now the preferred approach for analyzing that context and RAG pipelines are one of the main features of txtai. One of the other main features of txtai is that it's a vector database! You can build your prompts and limit your context all with one library. Hence the phrase *all-in-one AI framework*.\n", + "\n", + "This notebook shows how to build RAG pipelines with txtai." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZQrHIw351lwE" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies. Since this notebook is using optional pipelines, we need to install the pipeline extras package." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "R0AqRP7v1hdr" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline]\n", + "\n", + "# Get test data\n", + "!wget -N https://github.com/neuml/txtai/releases/download/v6.2.0/tests.tar.gz\n", + "!tar -xvzf tests.tar.gz\n", + "\n", + "# Install NLTK\n", + "import nltk\n", + "nltk.download(['punkt', 'punkt_tab'])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xmPN8RDF1pXd" + }, + "source": [ + "# Start with the basics\n", + "\n", + "Let's jump right in and start with a simple LLM pipeline. The [LLM pipeline](https://neuml.github.io/txtai/pipeline/text/llm/) supports local LLM models via [Hugging Face Transformers](https://github.com/huggingface/transformers) and [llama.cpp](https://github.com/abetlen/llama-cpp-python).\n", + "\n", + "The LLM pipeline also supports [API services (i.e. OpenAI, Claude, Bedrock etc) via LiteLLM](https://github.com/BerriAI/litellm). The LLM pipeline automatically detects the underlying LLM framework from the `path` parameter.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "id": "XZ7vPBIs1rGZ" + }, + "outputs": [], + "source": [ + "from txtai import LLM\n", + "\n", + "# Create LLM\n", + "llm = LLM(\"Qwen/Qwen3-4B-Instruct-2507\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9rmTWMxAH3Vx" + }, + "source": [ + "Next, we'll load a document to query. The [Textractor pipeline](https://neuml.github.io/txtai/pipeline/data/textractor/) has support for extracting text from common document formats (docx, pdf, xlsx, web)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "nifStGtOHuyc", + "outputId": "5a4010e0-75f9-4095-a24c-cd4c859847d0" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "# txtai – the all-in-one embeddings database\n", + "txtai is an all-in-one embeddings database for semantic search, LLM orchestration and language model workflows.\n", + "\n", + "Summary of txtai features:\n", + "· *Vector search* with SQL, object storage, topic modeling\n", + "· Create *embeddings* for text, documents, audio, images and video\n", + "· *Pipelines* powered by language models that run LLM prompts\n", + "· *Workflows* to join pipelines together and aggregate business logic\n", + "· Build with *Python* or *YAML* . API bindings available for JavaScript, Java, Rust and Go.\n", + "· *Run local or scale out with container orchestration* \n", + "\n", + "\n", + "## Examples\n", + "List of example notebooks.\n", + "|Notebook|Description|\n", + "|---|---|\n", + "|Introducing txtai |Overview of the functionality provided by txtai|\n", + "|Similarity search with images|Embed images and text into the same space for search|\n", + "|Build a QA database|Question matching with semantic search|\n", + "|Semantic Graphs|Explore topics, data connectivity and run network analysis|\n", + "\n", + "## Install\n", + "The easiest way to install is via pip and PyPI\n", + "pip install txtai\n", + "Python 3.10+ is supported. Using a Python virtual environment is **recommended** .\n", + "See the detailed install instructions for more information covering optional dependencies, environment specific prerequisites, installing from source, conda support and how to run with containers.\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "## Model guide\n", + "The following shows a list of suggested models.\n", + "|Component|Model(s)|\n", + "|---|---|\n", + "|Embeddings|all-MiniLM-L6-v2|\n", + "||E5-base-v2|\n", + "|Image Captions|BLIP|\n", + "|Labels - Zero Shot|BART-Large-MNLI|\n", + "|Labels - Fixed|Fine-tune with training pipeline|\n", + "|Large Language Model (LLM)|Flan T5 XL|\n", + "||Mistral 7B OpenOrca|\n", + "|Summarization|DistilBART|\n", + "|Text-to-Speech|ESPnet JETS|\n", + "|Transcription|Whisper|\n", + "|Translation|OPUS Model Series|\n" + ] + } + ], + "source": [ + "from txtai import Textractor\n", + "\n", + "# Create Textractor\n", + "textractor = Textractor()\n", + "text = textractor(\"txtai/document.docx\")\n", + "print(text)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2jkamwgdIgEp" + }, + "source": [ + "Now we'll define a simple LLM pipeline. It takes a question and context (which in this case is the whole file), creates a prompt and runs it with the LLM." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 59 + }, + "id": "9HU6C0OIIAKn", + "outputId": "f9d556c4-cd7a-4774-ef62-1f3fff90aa47" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'txtai is an all-in-one embeddings database that supports semantic search, LLM orchestration, and language model workflows with features like vector search, embeddings for text, audio, images, and video, pipelines powered by language models, and scalable workflows available via Python or YAML with API bindings for multiple languages.'" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def execute(question, text):\n", + " return llm([\n", + " {\"role\": \"system\", \"content\": \"You are a friendly assistant. You answer questions from users.\"},\n", + " {\"role\": \"user\", \"content\": f\"\"\"\n", + " Answer the following question using only the context below. Only include information specifically discussed.\n", + "\n", + " question: {question}\n", + " context: {text} \n", + " \"\"\"}\n", + " ], maxlength=4096)\n", + "\n", + "execute(\"Tell me about txtai in one sentence\", text)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 39 + }, + "id": "xxF7ajCPJP5_", + "outputId": "0e6f6dbb-c784-4841-fe3f-82754ef478eb" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'txtai recommends using Whisper for transcription.'" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "execute(\"What model does txtai recommend for transcription?\", text)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 97 + }, + "id": "AKmmTqsnJa5X", + "outputId": "834bf3ee-b7ed-4e38-e2ef-d5950f99ed9a" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'The best thing to read if you don\\'t know anything about txtai would be the \"Introducing txtai\" notebook, as it provides an overview of the functionality offered by txtai.'" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "execute(\"I don't know anything about txtai, what would be the best thing to read?\", text)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "WaVeEHrIMpFr" + }, + "source": [ + "If this is the first time you've seen *Generative AI*, then these statements are 🤯. Even if you've been in the space a while, it's still amazing how much a language model can understand and the high level of quality in it's answers.\n", + "\n", + "While this use case is fun, lets try to scale it to a larger set of documents." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "viVVft59NbKv" + }, + "source": [ + "# Build a RAG pipeline with vector search\n", + "\n", + "Let's say we have a large number of documents, hundreds/thousands etc. We can't just put all those documents into a single prompt, we'll run out of GPU memory fast!\n", + "\n", + "This is where retrieval augmented generation enters the picture. We can use a query step that finds the best candidates to add to the prompt.\n", + "\n", + "Typically, this candidate query uses vector search but it can be anything that runs a search and returns results. In fact, many complex production systems have customized retrieval pipelines that feed a context into LLM prompts.\n", + "\n", + "The first step in building our RAG pipeline is creating the knowledge store. In this case, it's a vector database of file content. The files will be split into paragraphs with each paragraph stored as a separate row." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ipmsmtN1NahT", + "outputId": "64733e1f-fb7b-4a2d-bf02-8930478a8ee8" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Indexing txtai/article.pdf\n", + "Indexing txtai/document.docx\n", + "Indexing txtai/document.pdf\n", + "Indexing txtai/spreadsheet.xlsx\n" + ] + } + ], + "source": [ + "import os\n", + "\n", + "from txtai import Embeddings\n", + "\n", + "def stream(path):\n", + " for f in sorted(os.listdir(path)):\n", + " fpath = os.path.join(path, f)\n", + "\n", + " # Only accept documents\n", + " if f.endswith((\"docx\", \"xlsx\", \"pdf\")):\n", + " print(f\"Indexing {fpath}\")\n", + " for paragraph in textractor(fpath):\n", + " yield paragraph\n", + "\n", + "# Document text extraction, split into paragraphs\n", + "textractor = Textractor(paragraphs=True)\n", + "\n", + "# Vector Database\n", + "embeddings = Embeddings(content=True)\n", + "embeddings.index(stream(\"txtai\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ASlmAaR3nBPN" + }, + "source": [ + "The next step is defining the RAG pipeline. This pipeline takes the input question, runs a vector search and builds a context using the search results. The context is then inserted into a prompt template and run with the LLM." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 39 + }, + "id": "_-9SW6r4P5ha", + "outputId": "6a7bcd69-bcd0-4f6e-81c1-e32c323b3ffb" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'txtai recommends using BLIP for image captioning.'" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def context(question):\n", + " context = \"\\n\".join(x[\"text\"] for x in embeddings.search(question))\n", + " return context\n", + "\n", + "def rag(question):\n", + " return execute(question, context(question))\n", + "\n", + "rag(\"What model does txtai recommend for image captioning?\")" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "NbQhSunPQtB0", + "outputId": "de0caf04-4cdb-48e8-aadf-37283be9909a" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The BLIP model was added for image captioning on 2022-03-17.\n" + ] + } + ], + "source": [ + "result = rag(\"When was the BLIP model added for image captioning?\")\n", + "print(result)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "D6HW-3GtnTFl" + }, + "source": [ + "As we can see, the result is similar to what we had before without vector search. The difference is that we only used a relevant portion of the documents to generate the answer.\n", + "\n", + "As we discussed before, this is important when dealing with large volumes of data. Not all of the data can be added to a LLM prompt. Additionally, having only the most relevant context helps the LLM generate higher quality answers." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FZ-yPC-xiUqa" + }, + "source": [ + "# Citations for LLMs\n", + "\n", + "A healthy level of skepticism should be applied to answers generated by AI. We're far from the day where we can blindly trust answers from an AI model.\n", + "\n", + "txtai has a couple approaches for generating citations. The basic approach is to take the answer and search the vector database for the closest match." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "obttSg_dSFT5", + "outputId": "c7ae7675-6959-4bcb-ad06-065ea8609c31" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "E5-base-v2\n", + "Image Captions BLIP\n", + "Labels - Zero Shot BART-Large-MNLI\n", + "# Model Guide\n", + "|Component |Model(s)|Date Added|\n", + "|---|---|---|\n", + "|Embeddings |all-MiniLM-L6-v2|2022-04-15|\n", + "|Image Captions |BLIP|2022-03-17|\n", + "|Labels - Zero Shot |BART-Large-MNLI|2022-01-01|\n", + "|Large Language Model (LLM) |Mistral 7B OpenOrca|2023-10-01|\n", + "|Summarization |DistilBART|2021-02-22|\n", + "|Text-to-Speech |ESPnet JETS|2022-08-01|\n", + "|Transcription |Whisper|2022-08-01|\n", + "|Translation |OPUS Model Series|2021-04-06|\n", + "&\"Times New Roman,Regular\"&12&A\n", + "## Model guide\n", + "The following shows a list of suggested models.\n", + "|Component|Model(s)|\n", + "|---|---|\n", + "|Embeddings|all-MiniLM-L6-v2|\n", + "||E5-base-v2|\n", + "|Image Captions|BLIP|\n", + "|Labels - Zero Shot|BART-Large-MNLI|\n", + "|Labels - Fixed|Fine-tune with training pipeline|\n", + "|Large Language Model (LLM)|Flan T5 XL|\n", + "||Mistral 7B OpenOrca|\n", + "|Summarization|DistilBART|\n", + "|Text-to-Speech|ESPnet JETS|\n", + "|Transcription|Whisper|\n", + "|Translation|OPUS Model Series|\n" + ] + } + ], + "source": [ + "for x in embeddings.search(result):\n", + " print(x[\"text\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "MDcB7GCWY6TO" + }, + "source": [ + "While the basic approach above works in this case, txtai has a more robust pipeline to handle citations and references.\n", + "\n", + "The RAG pipeline is defined below. A RAG pipeline works in the same way as a LLM + Vector Search pipeline, except it has special logic for generating citations. This pipeline takes the answers and compares it to the context passed to the LLM to determine the most likely reference." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "id": "Lm6gg85_Y7ot" + }, + "outputs": [], + "source": [ + "from txtai import RAG\n", + "\n", + "# Create the RAG pipeline\n", + "rag = RAG(embeddings, \"Qwen/Qwen3-4B-Instruct-2507\", template=\"\"\"\n", + " Answer the following question using the provided context.\n", + "\n", + " Question:\n", + " {question}\n", + "\n", + " Context:\n", + " {context}\n", + "\"\"\", output=\"reference\")" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "4pOfE5paZatH", + "outputId": "2bed2de5-22ff-4f7b-dba5-41b8e4cc6c75" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ANSWER: Python 3.10 and later versions (Python 3.10+) are supported.\n", + "CITATION: [{'id': '24', 'text': 'Python 3.10+ is supported. Using a Python virtual environment is recommended.'}]\n" + ] + } + ], + "source": [ + "result = rag(\"What version of Python is supported?\", maxlength=4096)\n", + "print(\"ANSWER:\", result[\"answer\"])\n", + "print(\"CITATION:\", embeddings.search(\"select id, text from txtai where id = :id\", limit=1, parameters={\"id\": result[\"reference\"]}))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vHdE2Q59jNnF" + }, + "source": [ + "And as we can see, not only is the answer to the statement shown, the RAG pipeline also provides a citation. This step is crucial in any line of work where answers must be verified (which is most lines of work)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "oPwgCgBc2Er2" + }, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook introduced retrieval augmented generation (RAG), explained why we need it and showed the options available for running RAG pipelines with txtai.\n", + "\n", + "The advantages of building RAG pipelines with txtai are:\n", + "\n", + "- **All-in-one AI framework** - one library can handle LLM inference and vector search retrieval\n", + "- **Generating citations** - generating answers is useful but referencing where those answers came from is crucial in gaining the trust of users\n", + "- **Simple yet powerful** - building pipelines can be done in a small amount of Python. Options are available to build pipelines in YAML and/or run through the API" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "gpuType": "T4", + "provenance": [] + }, + "kernelspec": { + "display_name": "local", + "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", + "version": "3.10.19" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/examples/53_Integrate_LLM_Frameworks.ipynb b/examples/53_Integrate_LLM_Frameworks.ipynb new file mode 100644 index 0000000..18f9beb --- /dev/null +++ b/examples/53_Integrate_LLM_Frameworks.ipynb @@ -0,0 +1,334 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "gpuType": "T4" + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Integrate LLM Frameworks\n", + "\n", + "The release of [BERT](https://arxiv.org/abs/1810.04805) in 2018 kicked off the language model revolution. The Transformers architecture succeeded RNNs and LSTMs to become the architecture of choice. Unbelievable progress was made in a number of areas: summarization, translation, text classification, entity classification and more. 2023 tooks things to another level with the rise of large language models (LLMs). Models with billions of parameters showed an amazing ability to generate coherent dialogue.\n", + "\n", + "Looking ahead towards the next wave of innovation, we're due for another shift in model architecture. For example, the [Mamba paper](https://arxiv.org/abs/2312.00752) previews a possible future after Transformers.\n", + "\n", + "With that in mind, [txtai](https://github.com/neuml/txtai) now has the capability to easily integrate additional LLM frameworks. While local models through Hugging Face Transformers continues to be the default choice, these additional LLM frameworks broaden the number of options available.\n", + "\n", + "This notebook will demonstrate how txtai can integrate with [llama.cpp](https://github.com/ggerganov/llama.cpp), [LiteLLM](https://github.com/BerriAI/litellm) and custom generation methods. For custom generation, we'll show how to run inference with a `Mamba` model." + ], + "metadata": { + "id": "VGeVB8M41jqW" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies. Since this notebook is using optional libraries, we need to install the `pipeline-llm` extras package." + ], + "metadata": { + "id": "ZQrHIw351lwE" + } + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "id": "R0AqRP7v1hdr" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline-llm]" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# llama.cpp\n", + "\n", + "First, we'll demonstrate how to load a model with llama.cpp. This framework is an extremely popular method with those who run local LLMs. It provides a number of innovations in running LLMs on CPUs, especially on Mac's.\n", + "\n", + "The following example shows a retrieval augmented generation (RAG) pipeline with llama.cpp. txtai automatically loads llama.cpp models when working with GGUF files." + ], + "metadata": { + "id": "32xg8L1JHd3d" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai import Embeddings, RAG, LLM\n", + "\n", + "data = [\n", + " \"US tops 5 million confirmed virus cases\",\n", + " \"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\",\n", + " \"Beijing mobilises invasion craft along coast as Taiwan tensions escalate\",\n", + " \"The National Park Service warns against sacrificing slower friends in a bear attack\",\n", + " \"Maine man wins $1M from $25 lottery ticket\",\n", + " \"Make huge profits without work, earn up to $100,000 a day\"\n", + "]\n", + "\n", + "# Create embeddings\n", + "embeddings = Embeddings(content=True, autoid=\"uuid5\")\n", + "\n", + "# Create an index for the list of text\n", + "embeddings.index(data)\n", + "\n", + "# Create LLM with llama.cpp - GGUF file is automatically downloaded\n", + "llm = LLM(\"unsloth/Qwen3-4B-Instruct-2507-GGUF/Qwen3-4B-Instruct-2507-Q4_K_M.gguf\", verbose=True)\n", + "\n", + "template = \"\"\"<|im_start|>system\n", + "You are a friendly assistant. You answer questions from users.<|im_end|>\n", + "<|im_start|>user\n", + "Find the best matching text in the context for the question. The response should be the text from the context only.\n", + "\n", + "Question:\n", + "{question}\n", + "\n", + "Context:\n", + "{context}\n", + "\n", + "Text:\n", + "<|im_end|>\n", + "<|im_start|>assistant\n", + "\"\"\"\n", + "\n", + "# Create and run RAG instance\n", + "rag = RAG(embeddings, llm, output=\"reference\", separator=\"\\n\", template=template)\n", + "result = rag(\"Tell me about someone lucky\")\n", + "\n", + "print(\"ANSWER:\", result[\"answer\"])\n", + "print(\"REFERENCE:\", embeddings.search(\"select id, text from txtai where id = :id\", parameters={\"id\": result[\"reference\"]}))\n" + ], + "metadata": { + "id": "XZ7vPBIs1rGZ", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "18554fc7-1383-46dd-fd7d-87d816747fad" + }, + "execution_count": 7, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "ANSWER: Maine man wins $1M from $25 lottery ticket\n", + "REFERENCE: [{'id': '37e5fae7-74c2-5f1c-bf69-2962dd7470d1', 'text': 'Maine man wins $1M from $25 lottery ticket'}]\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The code above builds an embeddings database, runs a vector search and passes those results to a LLM prompt. As expected, it prints the best answer and reference. The difference is that LLM inference is run through `llama.cpp` vs `transformers`." + ], + "metadata": { + "id": "_qDllo4AIky2" + } + }, + { + "cell_type": "markdown", + "source": [ + "# LiteLLM\n", + "\n", + "LiteLLM is an abstraction framework designed to run with API-based LLMs. At the time of writing this article, LiteLLM supports over 100+ LLMs. See the [full list of providers](https://docs.litellm.ai/docs/providers) for all the options.\n", + "\n", + "The following example shows a LLM call with the Hugging Face Inference API. This method automatically detects that this is a LiteLLM model string." + ], + "metadata": { + "id": "83BSRNPAI6Q9" + } + }, + { + "cell_type": "code", + "source": [ + "# Hugging Face Inference API\n", + "llm = LLM(\"huggingface/roneneldan/TinyStories-1M\")\n", + "print(llm(\"The cat and the dog.\", maxlength=5))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ye0DshJF4MLj", + "outputId": "6bc74aa4-201b-4b68-9545-a1110778e0b2" + }, + "execution_count": 14, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "They are friends.\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Given that all these APIs require a paid account, we'll leave it to you to try other API models using your own authentication." + ], + "metadata": { + "id": "bCpx6BtOJgw2" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Custom Generation\n", + "\n", + "Last but certainly not least, we'll demonstrate how to add a custom generation framework. For this example, we'll use the recently released [mamba-chat](https://huggingface.co/havenhq/mamba-chat) model to build a RAG pipeline. You can read more about the model in this [GitHub Repository](https://github.com/havenhq/mamba-chat)\n", + "\n", + "The following sections install support for Mamba models, define a Mamba Generation instance and run a Mamba-based RAG pipeline." + ], + "metadata": { + "id": "43x3YPZVJ2SA" + } + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "!pip install mamba-ssm\n", + "\n", + "# Link CUDA libraries into environment\n", + "!export LC_ALL=\"en_US.UTF-8\"\n", + "!export LD_LIBRARY_PATH=\"/usr/lib64-nvidia\"\n", + "!export LIBRARY_PATH=\"/usr/local/cuda/lib64/stubs\"\n", + "!ldconfig /usr/lib64-nvidia" + ], + "metadata": { + "id": "j6GQo1_J_HpU" + }, + "execution_count": 4, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "import torch\n", + "\n", + "from transformers import AutoTokenizer\n", + "from mamba_ssm.models.mixer_seq_simple import MambaLMHeadModel\n", + "\n", + "from txtai.pipeline import Generation\n", + "\n", + "\n", + "class MambaGeneration(Generation):\n", + " def __init__(self, path, template=None, **kwargs):\n", + " super().__init__(path, template, **kwargs)\n", + "\n", + " self.tokenizer = AutoTokenizer.from_pretrained(path)\n", + " self.tokenizer.eos_token = \"<|endoftext|>\"\n", + " self.tokenizer.pad_token = self.tokenizer.eos_token\n", + "\n", + " self.model = MambaLMHeadModel.from_pretrained(path, device=\"cuda\", dtype=torch.float16)\n", + "\n", + " def execute(self, texts, maxlength, **kwargs):\n", + " results = []\n", + " for text in texts:\n", + " # Tokenize prompt\n", + " tokens = self.tokenizer(text, return_tensors=\"pt\").to(\"cuda\")[\"input_ids\"]\n", + "\n", + " # Run inference\n", + " output = self.model.generate(input_ids=tokens, max_length=maxlength, eos_token_id=self.tokenizer.eos_token_id, **kwargs)\n", + "\n", + " # Decode results\n", + " output = self.tokenizer.batch_decode(output)\n", + " output = output[0].split(\"<|assistant|>\\n\")[-1].replace(\"<|endoftext|>\", \"\").strip()\n", + " results.append(output)\n", + "\n", + " return results" + ], + "metadata": { + "id": "1uYgXT_q_U9U" + }, + "execution_count": 5, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "llm = LLM(\"havenhq/mamba-chat\", method=\"__main__.MambaGeneration\")\n", + "\n", + "template = \"\"\"<|system|>You are a friendly assistant. You answer questions from users.\n", + "<|user|>\n", + "Find the best matching text in the context for the question. The response should be the text from the context only.\n", + "\n", + "Question:\n", + "{question}\n", + "\n", + "Context:\n", + "{context}\n", + "\n", + "<|assistant|>\n", + "\"\"\"\n", + "\n", + "# Create and run RAG instance\n", + "rag = RAG(embeddings, llm, output=\"reference\", separator=\"\\n\", template=template)\n", + "result = rag(\"Tell me something about about wildlife\")\n", + "\n", + "print(\"ANSWER:\", result[\"answer\"])\n", + "print(\"REFERENCE:\", embeddings.search(\"select id, text from txtai where id = :id\", parameters={\"id\": result[\"reference\"]}))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "4vZUYLJB_jvb", + "outputId": "cb8f81f5-6744-4450-9132-dadb16b9096c" + }, + "execution_count": 6, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "ANSWER: The National Park Service warns against sacrificing slower friends in a bear attack.\n", + "REFERENCE: [{'id': '7224f159-658b-5891-b06c-9a96cfa6a54d', 'text': 'The National Park Service warns against sacrificing slower friends in a bear attack'}]\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "As expected, the best answer and reference is shown.\n", + "\n", + "There is much to learn and validate about Mamba but it's important to note this model is only 2.8B parameters. The Mamba architecture is one to watch moving forward!" + ], + "metadata": { + "id": "i_7fgP4qLveq" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "This notebook demonstrated how to run LLMs through txtai using alternate LLM frameworks. It's an exciting time in AI/NLP/Machine Learning. What new innovations will 2024 bring? Time will tell but txtai is ready to integrate them in!" + ], + "metadata": { + "id": "oPwgCgBc2Er2" + } + } + ] +} \ No newline at end of file diff --git a/examples/54_API_Authorization_and_Authentication.ipynb b/examples/54_API_Authorization_and_Authentication.ipynb new file mode 100644 index 0000000..13bb9c4 --- /dev/null +++ b/examples/54_API_Authorization_and_Authentication.ipynb @@ -0,0 +1,510 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + } + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# API Authorization and Authentication\n", + "\n", + "txtai can run in Python, with YAML configuration and through an API service.\n", + "\n", + "The default API service implementation runs without any security. This may be OK for a local prototype or if it's run on a small internal network. But in most cases, additional security measures should be taken.\n", + "\n", + "This notebook will demonstrate how to add authorization, authentication and middleware dependencies to a txtai API service." + ], + "metadata": { + "id": "VGeVB8M41jqW" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies. Since this notebook uses the API, we need to install the api extras package." + ], + "metadata": { + "id": "ZQrHIw351lwE" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "R0AqRP7v1hdr" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[api]" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Create an API Service\n", + "\n", + "For this example, we'll load an existing txtai index from the Hugging Face Hub." + ], + "metadata": { + "id": "32xg8L1JHd3d" + } + }, + { + "cell_type": "code", + "source": [ + "%%writefile config.yml\n", + "cloud:\n", + " provider: huggingface-hub\n", + " container: neuml/txtai-intro\n", + "\n", + "embeddings:" + ], + "metadata": { + "id": "XZ7vPBIs1rGZ", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "468b09cb-ba01-426d-9640-142acf1e3dd9" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Writing config.yml\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Next, we'll generate a test token to use for this notebook." + ], + "metadata": { + "id": "Eu8rtjB_QZOg" + } + }, + { + "cell_type": "code", + "source": [ + "import uuid\n", + "str(uuid.uuid5(uuid.NAMESPACE_DNS, \"TokenTest\"))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 38 + }, + "id": "Np18NtytQG8k", + "outputId": "5bf60d69-311d-4a02-f3dd-d210aedc8b4f" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "'edd590d3-bfab-5425-8a85-79b01e3127ee'" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + } + }, + "metadata": {}, + "execution_count": 3 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "txtai has a default API token authorization method built-in. We'll set a token and start the service.\n", + "\n", + "**It's important to note that this service is running via HTTP as this is only for demonstration purposes. HTTPS must be added either with a proxy service like NGINX or by passing a SSL cert to Uvicorn. See [this link](https://neuml.github.io/txtai/api/security/) for more.**" + ], + "metadata": { + "id": "ehZSh97_Nr7h" + } + }, + { + "cell_type": "code", + "source": [ + "!CONFIG=config.yml TOKEN=`echo -n 'edd590d3-bfab-5425-8a85-79b01e3127ee' | sha256sum | head -c 64` uvicorn \"txtai.api:app\" &> api.log &\n", + "!sleep 60" + ], + "metadata": { + "id": "PjKT3vOuNkfX" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Connect to Service\n", + "\n", + "First, we'll try a request with no token to see what happens." + ], + "metadata": { + "id": "NfOn1vgwRF2C" + } + }, + { + "cell_type": "code", + "source": [ + "!curl -X GET -I 'http://localhost:8000/search?query=feel+good+story&limit=1'" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "9Ljn13HWOb26", + "outputId": "4e437ebb-cf92-4e7d-c804-25bb42f6cb30" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "HTTP/1.1 401 Unauthorized\r\n", + "\u001b[1mdate\u001b[0m: Thu, 04 Jan 2024 15:08:38 GMT\r\n", + "\u001b[1mserver\u001b[0m: uvicorn\r\n", + "\u001b[1mcontent-length\u001b[0m: 40\r\n", + "\u001b[1mcontent-type\u001b[0m: application/json\r\n", + "\r\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "As expected, we received a HTTP 401 saying the request is not authorized.\n", + "\n", + "Now let's try an invalid token." + ], + "metadata": { + "id": "Rvi4PRSTRTjt" + } + }, + { + "cell_type": "code", + "source": [ + "!curl -X GET -I 'http://localhost:8000/search?query=feel+good+story&limit=1' -H 'Authorization: Bearer junk'" + ], + "metadata": { + "id": "vJvQHtcJdRXb", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "099577d1-2ed0-4051-827d-2ab72d5929fa" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "HTTP/1.1 401 Unauthorized\r\n", + "\u001b[1mdate\u001b[0m: Thu, 04 Jan 2024 15:08:38 GMT\r\n", + "\u001b[1mserver\u001b[0m: uvicorn\r\n", + "\u001b[1mcontent-length\u001b[0m: 40\r\n", + "\u001b[1mcontent-type\u001b[0m: application/json\r\n", + "\r\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Once again, the request is rejected.\n", + "\n", + "Let's try again, this time passing a valid API token." + ], + "metadata": { + "id": "7oPf-Crb6ETH" + } + }, + { + "cell_type": "code", + "source": [ + "!curl -X GET 'http://localhost:8000/search?query=feel+good+story&limit=1' -H 'Authorization: Bearer edd590d3-bfab-5425-8a85-79b01e3127ee'" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "HNxzVN1CSE1C", + "outputId": "77962561-52fa-4697-9c0c-bc2d630ea8b4" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[{\"id\":\"4\",\"text\":\"Maine man wins $1M from $25 lottery ticket\",\"score\":0.08329025655984879}]" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "This time we get search results!" + ], + "metadata": { + "id": "H025YmZ4TSSS" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Dependencies\n", + "\n", + "Next, let's add a custom dependency to test out authentication. A dependency could integrate with external identity providers to validate user credentials such as OAuth, Active Directory, LDAP or another identity management service.\n", + "\n", + "For this simple example, we'll validate user credentials using basic HTTP authentication. The code below checks if a specific username and password are provided. It's based on [this FastAPI example](https://fastapi.tiangolo.com/advanced/security/http-basic-auth/)." + ], + "metadata": { + "id": "3JjhlfhsUDNU" + } + }, + { + "cell_type": "code", + "source": [ + "%%writefile authentication.py\n", + "\n", + "import secrets\n", + "\n", + "from fastapi import Depends, HTTPException, status\n", + "from fastapi.security import HTTPBasic, HTTPBasicCredentials\n", + "\n", + "security = HTTPBasic()\n", + "\n", + "\n", + "class Authentication:\n", + " def __call__(self, credentials: HTTPBasicCredentials = Depends(security)):\n", + " user = credentials.username.encode(\"utf8\")\n", + " validuser = secrets.compare_digest(user, b\"txtai\")\n", + "\n", + " password = credentials.password.encode(\"utf8\")\n", + " validpassword = secrets.compare_digest(password, b\"theembeddingsdb\")\n", + "\n", + " if not (validuser and validpassword):\n", + " raise HTTPException(\n", + " status_code=status.HTTP_401_UNAUTHORIZED,\n", + " detail=\"Incorrect user or password\",\n", + " headers={\"WWW-Authenticate\": \"Basic\"},\n", + " )\n", + "\n", + " return credentials.username" + ], + "metadata": { + "id": "1W9Vw0PMUa83", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "8bece467-588d-4d80-98a0-2451d7ebf85e" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Writing authentication.py\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Now let's restart the application and add this dependency in.\n", + "\n", + "**Once again, same note as above, this demonstration uses HTTP. Real use-cases must use HTTPS.**" + ], + "metadata": { + "id": "VNYHD20xhVAS" + } + }, + { + "cell_type": "code", + "source": [ + "!killall -9 uvicorn\n", + "!CONFIG=config.yml DEPENDENCIES=authentication.Authentication uvicorn \"txtai.api:app\" &> api.log &\n", + "!sleep 30" + ], + "metadata": { + "id": "F60-Aakyg1x_" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "!curl -X GET -I 'http://localhost:8000/search?query=feel+good+story&limit=1'" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "jda9RugAhcNT", + "outputId": "89111cdc-bee4-4cec-83ce-de3a4bf8e21a" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "HTTP/1.1 401 Unauthorized\r\n", + "\u001b[1mdate\u001b[0m: Thu, 04 Jan 2024 15:09:10 GMT\r\n", + "\u001b[1mserver\u001b[0m: uvicorn\r\n", + "\u001b[1mwww-authenticate\u001b[0m: Basic\r\n", + "\u001b[1mcontent-length\u001b[0m: 30\r\n", + "\u001b[1mcontent-type\u001b[0m: application/json\r\n", + "\r\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The request is rejected as expected. Next let's try an invalid username/password." + ], + "metadata": { + "id": "XZCsCDOG6VBS" + } + }, + { + "cell_type": "code", + "source": [ + "!curl -X GET -I 'http://localhost:8000/search?query=feel+good+story&limit=1' -H \"Authorization: Basic junk\"" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "VFj1UPuC6c4P", + "outputId": "4fa7d6df-c06a-4cfe-88ff-4c341783704d" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "HTTP/1.1 401 Unauthorized\r\n", + "\u001b[1mdate\u001b[0m: Thu, 04 Jan 2024 15:09:10 GMT\r\n", + "\u001b[1mserver\u001b[0m: uvicorn\r\n", + "\u001b[1mwww-authenticate\u001b[0m: Basic\r\n", + "\u001b[1mcontent-length\u001b[0m: 47\r\n", + "\u001b[1mcontent-type\u001b[0m: application/json\r\n", + "\r\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Once again, the request is rejected.\n", + "\n", + "Now we'll add the expected username/password to the request. [HTTP basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) simply concats the username-password separated by a colon and then base64 encodes it." + ], + "metadata": { + "id": "jwZYyTDKh5pB" + } + }, + { + "cell_type": "code", + "source": [ + "!echo -n txtai:theembeddingsdb | base64" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "uL943ry3h2hR", + "outputId": "aa2bc9a6-cd0a-42aa-933c-3cb5aa579588" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "dHh0YWk6dGhlZW1iZWRkaW5nc2Ri\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "!curl -X GET 'http://localhost:8000/search?query=feel+good+story&limit=1' -H \"Authorization: Basic dHh0YWk6dGhlZW1iZWRkaW5nc2Ri\"" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "X9vfyRsphxiA", + "outputId": "01cfe7b9-80ee-4745-eae9-7db46b2ec554" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "[{\"id\":\"4\",\"text\":\"Maine man wins $1M from $25 lottery ticket\",\"score\":0.08329025655984879}]" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Now that we have the correct username/password, a response is returned!" + ], + "metadata": { + "id": "M-NSiDGuiigq" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "This notebook introduced how to add authorization, authentication and middleware dependencies to a txtai API service. As noted multiple times, ensure that HTTPS is enabled when using this in production environments.\n", + "\n", + "For more advanced authentication methods, check out the [FastAPI security documentation](https://fastapi.tiangolo.com/tutorial/security/).\n" + ], + "metadata": { + "id": "ZivWat7-r3ID" + } + } + ] +} \ No newline at end of file diff --git a/examples/55_Generate_knowledge_with_Semantic_Graphs_and_RAG.ipynb b/examples/55_Generate_knowledge_with_Semantic_Graphs_and_RAG.ipynb new file mode 100644 index 0000000..bde2c39 --- /dev/null +++ b/examples/55_Generate_knowledge_with_Semantic_Graphs_and_RAG.ipynb @@ -0,0 +1,772 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "SwgRD_NGutB9" + }, + "source": [ + "# Generate knowledge with Semantic Graphs and RAG\n", + "\n", + "Embeddings databases are a union of vector indexes (sparse and dense), graph networks and relational databases. This enables vector search with SQL, topic modeling, retrieval augmented generation and more.\n", + "\n", + "Semantic graphs were introduced in late 2022 with txtai 5.0, right before the start of the LLM era. This notebook picks back up where that work left off, opening a new frontier in semantic graph integration." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "68Iw-nPbhYIJ" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "R0AqRP7v1hdr" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[graph]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_F84mqRHwpCP" + }, + "source": [ + "# Create a Semantic Graph\n", + "\n", + "First, we're going to create a semantic graph using data from `txtai-wikipedia`, a full Wikipedia txtai embeddings database. We'll use the top 100K articles sorted by popularity." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "U52gN-vUxjky" + }, + "outputs": [], + "source": [ + "from txtai import Embeddings\n", + "\n", + "# Create embeddings instance with a semantic graph\n", + "embeddings = Embeddings({\n", + " \"autoid\": \"uuid5\",\n", + " \"path\": \"intfloat/e5-base\",\n", + " \"instructions\": {\n", + " \"query\": \"query: \",\n", + " \"data\": \"passage: \"\n", + " },\n", + " \"content\": True,\n", + " \"graph\": {\n", + " \"approximate\": False,\n", + " \"topics\": {}\n", + " }\n", + "})\n", + "\n", + "# Load dataset\n", + "wikipedia = Embeddings()\n", + "wikipedia.load(provider=\"huggingface-hub\", container=\"neuml/txtai-wikipedia\")\n", + "\n", + "query = \"\"\"\n", + "SELECT id, text FROM txtai\n", + "order by percentile desc\n", + "LIMIT 100000\n", + "\"\"\"\n", + "\n", + "embeddings.index(wikipedia.search(query))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0K6NIk3kL_Ne" + }, + "source": [ + "The code above creates a new embeddings database with a graph component. This adds a separate graph index alongside the database and vector index components.\n", + "\n", + "A semantic graph automatically creates edges between graph nodes using the vector model associated with the embeddings database. Relationships can also be manually specified.\n", + "\n", + "Next, we'll run a search and return a filtered graph containing only the results of a search. This is a new feature as of txtai 7.0." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "A_mqCECHHxQ7" + }, + "outputs": [], + "source": [ + "graph = embeddings.search(\"Large Language Models\", 50, graph=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SPTkKgxQN90m" + }, + "source": [ + "Let's repeat that again, this ran a search and created it's own graph of the search results. 🔥🔥🔥 This opens up a lot of interesting possibilities!\n", + "\n", + "Time to plot the search results." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 422 + }, + "id": "P5mgmrgO6b5Y", + "outputId": "ff52cbb7-e267-429a-913f-fb01ae6908b3" + }, + "outputs": [ + { + "data": { + "image/png": "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IYQQopVJyCmEEELsoQL5/dPzEds7VDEoCnFnDoH8/u19JGInyFbwHSNV1UIIIYQQu1bH+ElLCCGEEK3On9svvQCmI0klCeT2a++jEDto61ZwZdcvzdky47N0yBm7fUWnVFULIYQQQux67b+FQAghhBA7bPjw4fzlL39BURQUReGVV15hxowZ3H777RQNO5CYYmPBhgDTl1dw+e+KyXZYyHNZWVkZZvbaWm74aGWdy/u/MT04ZWgB+z/6Y4PruvPo3jz34wZWVYZ5/vS96JplZ01VmIn//YVkSufn6w5gXW0UgLs+W8OnK6q4bHQ3rj64mDmlfk6ftggUlT8eczg33juezZs3s3nzZm666SY6d+7Mueeey3333dcm95touYi7E2uHnQPobbcVXFFA11k77BwcX2/YbbeCm1XVHUVGVbW3/Jf2PhohhBBCiFYhlZxCCCHEbsrn83HjjTdy4403Mn78eC6++GLKyraGQGPf+JmDnphDjxwHm/wxDn9qHte8v5zXFmzm8KfmNQg4PTaNoV3cjV6Xy6oyoMDFioowfxpcwOrKMEdMncfPm0OcPLgAgJpIgsOfmsfhT83j0xVVALw6fxNHTv1p6wUpCnFnLtNefpnx48dz0003AbBp0yby8/PxeDyteA+J1qKjsHboWenKvzYKOHvkOHjtnCHphUSZ17+LTJs2bYfOd/LJJzf6+UMPPZScnHSw+Vupqj7ooIP44x//CMC//vUvnn76aV588UX23ntvACZMmMALL7zACy+8wHHHHWee7+ijj+bJJ59k6tSpDBuWrtr9+9//zmeffcYZZ5xhnq5nz54888wzPP/884walW63P+qoozjiiCNa9XYIIYQQYvckIacQQgixmzrooIP47LPPKC8vByCRSLBgwQIAUpqNlGYD4Kf1foqzHdu9vCsOKuaxb0sb/drhfXP5rqQWgN55TuZvCAAwb72fg3tlA+mQ9LMJI3npzL3IcaabRcqCcZK6XueydM3C6WecyTPPPMPRRx9tfn7u3LmMHj26uTdftCHZCt44RVE45ZRTGv3aYYcdRm5uLgAhX/dthsPtMjZXUdPH1dyTN+MgTznlFD788EMAbrjhBsaNG8f111/PhAkTAPjggw84//zzGTt2LBdccAEABQUFHHrooUycOJHx48ebr2EPP/wwDzzwQJ3Lv+yyy7j99tv5y1/+wiWXXALAjBkzOOGEE5p9O4QQQgix55J2dSGEEGI3VVBQYAac++23HxMmTCAYDHLVVVehbwmjVAUO7OHj1QWbt3lZWXaNoZ3dTP5sTaNfH1DgYnVlGIClm4Mc3T+P/y4q4/C+uWagefCUOVSGEvx57y7cdmQvrnx3eaOX9fbicr595G5yk9U8+eSTzJkzh/LyctatW2dWcYmOoyNtBR94zNncN3ocip7k9ddf5/333+f2228nFovRrVs3wuEw11xzDZqm8c9//hOv18uaNWtwOp3ceuut5kUNHTqUSZMmEYlEmDNnDk8++SSKonD99dczePBgPvvsM55//nk6derEHXfcgdVqZdmyZdx9992ceOKJ/O53v8PhcPDJJ5/Qo0cPpk6dyptvvsnHH38MQNeuXRk9ejR9+vRh9uwfueznLrx4xmAKs+wEown+/OoScl1Wnj99LzbWRvlpQ4Cj+ucyb52fA3v4+GRZJXkuCwd09zFt3kYe/rrumw+Lrtmf2WtrGVnk5b6vSvjDXvn0y3dx6Vu/8M2vNRzdP5e/HtYTTVV47JtS/jN/E8+eNohoIkW/fBerKsP8WhXhuP5788tXdh584IHt3taFCxdSWFjIP/7xDxRF4fnnn+eCCy5A3/ImhsfjQVVVYrEYkH7TBcDtdrNixQoA1q1bZ34tlUoBMHr0aGKxGFOmTKGsrIzJkycTDofrVKUbCgoKKCkpAaCmpobs7Gyqq6uJRCLk5uZSWVnZag83IYQQQux+pJJTCCGE2E2VlZXRqVMnAGbPns348eMpKEi3juuoPHPqID4dP5KPfqlgwZbKy0z/PLYPMyaM5NyRXbjyoGIe/abxKs763l9aQSSR4n/jR+K2qWwMpEONylA61Hhj4WaGFXqbPH9NJEFK0QiFQvz444/06tWrRbdbtK2OtBX8H8f255x7X2Hs2LGcddZZ2O12AObPn88ll1xCLBajX79+HHbYYfz6669cfPHFLFu2rMHlHHzwwTz11FNMmDCBp556CgCv18uLL77IBRdcwPHHHw/AhRdeyIsvvsjYsWNxOBxm23U8Hueqq67io48+4tdff2X8+PFmwAmwfv16vvnmG2699VbuffIFLjywJ5+vrOLIqfN4+adNjB/VFYCiLDvnvbaEu7/4FYD/LirjoCfmMHa/Qp6dvYHRj8/h3JFdGhx/F6+Ny95ZxmkvLWTyMX049z9LOO/VJUzYP325N/2+J0c+PY8xU+Zw6YFFqFvy6Zmrqzli6jwGdXKzcGOA302Zxz77jsJisWz3tj7zzDP07NkTq9XKPvvsw5w5c8yAE6BHjx6sX7++znE+88wzPPHEE8yaNavO58855xw+/fRTAPLy8sjOzubiiy9mwYIFnHnmmU1+/1V16+MwEAiQlZVl3t+9e/du8nxCCCGE+G2QSk4hhBBiNzVr1iymTp3KG2+8QVlZGZq2tZVYIcXYN5ayeFOwyfNnzuR84fS9OLC7D4B++S7+elgP7vr8V/Pry8pD9M93mR9P+iBdmXXLEb34fGUVVk1BAWJJnYN7ZrOyItTk9XrtGkoqgaqqDB06lNdeew2AoqIiVq9e3bI7QexSHW0ruKYorMgZycDEh6xdu9YM9X/++WcgPds1KyuL4uJili5dCsDSpUsZPnx4nct59dVXGTduHMceeywfffQRs2bNora2lg0bNgCY1YjFxcUsXrwYgMWLF9O9e3eSySRLlixp9Pgef/xxrFYrd999t/m5pNXBXp3c7Nsti3P37oJVVZi1pgaABRv8xJNbg8IFGwLoOmz0x8yREPFU3XEPAKsqwgRjSdbXxlheHiKaSLGuJkqO00qB20r/fBcfjx0BQLbTQoHbZl4+wIbaqPnv8upa3G53s27rl19+ycEHH8xBBx3UrBmmY8eOpUuXLjzwwAN89913ABxwwAGMHDmSSZMmAeD3+/nxx/Sisx9++IFx48Y1eXlG9SekK0dra2u3ewxCCCGE+O2QkFMIIYTYTdXU1HDnnXcyefJkdF0nlUqZwYPSwiUn57+2Ncj4/rJ96wScADNWVHH+PoUAdPbYmHbWYFK6zmcrqpi5uppOHivvXzCcYDxFLJEOWAHOGNaJS0d3o1+ei0/GjuCYZ3/iqoOK+dNp/0LVk3z88cdmsLT33nszefLkHb4/ROvraFvBU7pOVl4nIp0HUVxcbLY06/Xmvq5du5YBAwYwY8YMBg4c2OByAoEAd999NxaLhZdffrlBpWHm5QwZMoSvv/6awYMH895771FUVFQnbNN1HVVVURSFK6+8EkVRzIpDj8eDM2Th57IQ35XU8tK8jQBYVIUin536+WXDOLNxehP/VoDyUJyfy0Ic88xPxJM6FlUhseWKGjufjoKiKM26rR9++CE333wzLpeLlSvrLi779ddf6dq1q/mxxWIhkUgQCoUIhdJvevTt25fx48dz2WWXmd+zn376ifPOOw+AAQMGmC3tjSkvL6dbt25UVlbi8/morq4G0uMB5A0SIYQQQkjIKYQQQuzG5s+fz/jx4xt8/ra/3cjSw+8Ei73O579cVc2Xq6q3eZn7P/pjg88FY0l+KQvRN8/Jior0ZvVMmwNxRjVyvlcXbG4wD/TOT37h7Rk31qkN7Ny5MxUVFfj9/m0e229BYWEh06ZNY/ny5VgsFhYvXszjjz9OJBLZ6cueOnUqV1xxBeFwuM7nDzvsMM4++2y8Xi8+n4/S0lIWLVrE9R+tTG8F34mFQ7cc0Ys5pbV88HNFo18fP6orU39Y3+jXDuqZzSdbKhJnrKjib5+s4r0LhmE565+8+tLTTJgwAbfbbZ6+Z8+e/OlPf+Kwww7j6KOP5q233jJbmg8//HBmzJjBhAkTOOmkk/D5fJSXl/Pmm28CkJWVxZNPPomqqjidTqxWK9XV1dx9990oisKiRYtYvnw55557Lps2bSI3NxdVVVm8eDGPPPII06dP55tvvgHSwefcuXO58sor+XbhL1z3w3qePHkA5++bfqPgga9KWLy56SrrnaHrMPmzNXwydgQpHcqDMc58eXGTp1e2xJ3PP/88d9xxBxdddBErV65k7ty5FBUV1TltRUUFqqoyc+bMBpcTCATQdR2bLV01+thjjwHpFvNHH30UgOuuuw6fz8fDDz8MwNVXX83y5cvZtGkTU6dOJRaLcdNNNwFw+eWXM2bMGFRVpVu3btx333089thj3H777WiaxhNPPAGApmk4nU4qKhp/fAkhhBDit0MZMWJEc980FkIIIcRuZOV+lxLM6d3+C2My6TruqlX0mf14ex9Jh1VYWMg111xjtvNeeuml2Gw2HnzwwZ2+7KZCTsM+++zDIYccYm61XrHfpYRze6PvRLv69kLO7y/bt9FgvUlbHkODFz7LP/7xD6677jogHabdc889dO3albPPPhur1corr7zCa6+9Rk5ODoceeigXXXQRRUVFbNy4EZvNxtSpUxk3bhz5+flccskl3HXXXXWu6qmnnmLixIlomsaDDz7IhAkT8Hq9XHbZZdxxxx1mBbXxt/HvzMrSmCOHn8f8reV3XBsZ+OU/sEWqmn36++67j7vuustcepbpoIMOIj8/n7fffrsVj3DbjjrqKHRd53//+1+bXacQQgghOiap5BRCCCH2UM6aEoLZPUHZ8Sq81qboKXyhDdhsNnPuodi2p556itdff50HH3yQvfbai6uuugpN0/jiiy/497//TXZ2Nrfccgtut5vy8nJuvvlmRo4cydixY4nFYuTn53PbbbeZG66b6+WXX+bTcid5Hgd3f/ErD5/UH5umMnednyveXcaY3tlcP6YH4USKXrkO/vyfJfxSFuLNPw/FY08/5o57dr55eYoCH180AqumEE/qnPrSQs4Z0YUBBS5mTBjJP2asQdd17jymDwBTvlvHv+du5NnTBhGMJemf7+Kc/yymPBgnnNWNgw4+mF9++cXc6n3UUUfx7bffctppp5Gfn8+dd95JQUEBxx57LA8//DD7778/brebyspKLBYLyWSSZDJJPB5nxIgRhEIh7rzzTsrKyvjnP/9JMBjk119/JRAI4HA4qKqqoqamxtzqHYlE6rRyN8UR96MmY6Q0W4vu/7agJqJYWxhwlpaWNhpwAk22/e9K06dPb/PrFDtORyHuyCZpdaArFhQ9gRaPYI1Um1XFQgghxI6SkFMIIYTYQ3krl1Pe67D2Pow6dFUjL1iCz+dD13Xi8TixWIxYLEYy2bI5or8ViUQCq9UKwBVXXMG1116L3+/nwQcf5IMPPuD888/nlVdeYfbs2VxwwQX8/ve/p6qqCofDwaWXXkqvXr248sorueqqq1p0vV5fNo+8v4aVFWEcFpXfP5UeUfDWeUPpm+cEwKIpnPLcQo7pn8uF+xby+LfrCMWSnPTCggaXp+vwxxcXEI6nuPKgYs4Y1pkp36/jwv0KOXzLZX996T784fkF1EQSfH3pPry+ZdTBvHV+Ln9n65b0lMVOr/5DKFu7Gosl/ePsoYceyg033MCpp55KIBDg8ssv509/+hMXXngh99xzD7fffnudcO6cc85h+vTpBAIBvF4vXq+XCRMmcNppp3Hqqafy3HPPMWvWLP773/+iqip33HGHed7q6mq6dOnSYJs4pNunbTYbVqsVq9WKqqp4Qxup8RR3uKpqZ21pi2p0r7322l12OGLPpKMQyO+PP7cfYV93wlndSNUbowLpwN1ZW4qzpgRv5XI85csk9BRCCNFiEnIKIYQQe6g8/6+sj9YQtWV1jHBF17HFalF+nUeFAjabDbvdjtvtxuPxkEwmzcBTqjy3slqt5v3Rr18/7r//fiA9Q7JLly707t2bIUOGMGHCBOx2Ox988AFVVVX88ssvAKxevZr8/PwWX29NIMjKinRbe69cB/86vh9Oq0rvXCdds9IhhbEBfO2Wzd6rKsN8U1LDi2fsxa9VEW793yrz8tw2jSl/GkCRz06uy8qbCzc3uE5NUagIxQFYWRE2r2d2acMt2sFogkAgQHV1NSeeeCIffvghgUCAVCpFJBLB7XZz8sknc9JJJ2G1WnnyySfNSsPmbPh2u92cdtppjZ6/zjE3EmoaAX44HCYej2MtXwnuog5VVY2ewlVT0t5HIfZQCaubyqJRVHT/XXp5WSoJitrk/0Upi51gTm+C2T0p73UY1nAVeSWzyF03G0t818yvFUIIseeRkFMIIYTYgyiKgsPhwOl0omkahRtns6b772EnZiq2Hp3isjnk5+WSSKQDKmOZjs1mM/84nc46VZ7RaLRZbcF7qosuuogvvvgCgGXLljFp0iQCgQCqqpJKpVizZg2fffYZ8+alqyEtFgvDhw+nf//+APTo0aPJ9mKDoqQ3bGuahqZp2O12Mh8zFx9QxP0zS5ixooq3zxtq5hSZS80VBWyawqPflKLrMOVPA/hdz2zz60f3z2V1VYQ/v7qEqw8uxmu3NLiMlK6T57JSE0nQN8/J+trols83PObVazfQf8tinF69ejFw4ECOO+44iouL+b//+z8eeeQRIpEI0WjUrIZVFIU+ffo0a8O3EZbWP7+qquTn5xMOh8nLy2s01IzH43WOtSNWVaNqeCqXt/dRiD2MjkJZzzFs6ncsuqJivo40Z3mZophvBMQd2Wzsfzyb+h1L5+UfUbDmS6nsFEIIsV0ScgohxG+YzMbacxgbhh0OBwCRSIRwOIyr+iuU4kO3/LLZvhQ9hXvlTBIOFYvFQk5ODqlUinA4TCgUMqsVjco4m81mVnkmEgmzwrN+gLQn2meffXjqqafQNI2FCxeam6Qffvhh/vWvf6GqKrFYjGuvvZann36am2++mYsvvhiAhx56CE3TCAaDPPLII+Tm5nLXXXfhdrvRNI3HHnvMDI1vuOEGIB1yAni9Xmw2G1lZWagZFVfvL63ggRP78UtZqM7n6+uR42DqKYNI6jqhWJK56/wc1icHgO9Karjh0B6M7OphUyBGSXU6wFxWHuL1c4fwwMy15vZ0HXj823VEEk2H29/9OIdTb7jMvF8M06ZN45577gHgs88+44UXXkBVVV577TV0XW/2hu9wOMxnn33Giy++iKZpvPvuu+Tm5uLz+aiqqiKVShGNRhsNNevzlC/DGq4i7sjuMFXV1kg1nvJl2z+tEM0UcXdi7dCzCGe1wmgGRQEUdEVlY/8TqOkynOKFr+AINqwAF0IIIQyyXV0IIX5DZDbWnseofLTZbCSTSTPczNzuXNnncEr7HNu+4YquU7jsfQrWfGF+yuPx4HA4UBQFXdeJxWJmu3GmzCpPTdPM0xp/9uQqT1VVzepB4+/GPpf5t6IoDBs2jP3335+pU6cCNLoBfHuf2x22gl834c+8/fbblJS0Xtu1pmlYrVazBT2zUjMej3PmmWfy/fffs3jx4hZd7uaeh7Gx/3Hplt32pqfosuwDOmU8H4XYGdWdh7F22DnoKM2r2mypVBIFneIF08je1HDmrxBCCAFSySmEEL8JMhtrz2O0pFssFuLxOLW1tUSj0TqnURQFt9tNfu1CqoPDCbi67JpfPrdD0ZN4g5votvF7Mo8wEAgQCASw2+24XC5sNhu5ubkkk0mCwaBZ2Zk5ozOzytPj8aAoym5T5bm9gLKxz9Wn63qDMDKRSDT4nDEKoLy8vE7g3RLWSDVqItroGyHtzdgKnlnBuaO2FWo21n7+7LPP7tD15K77gU39jukwVdW5635o78MQe4jKrvtROuT09Ae76vGtauh6ipLhfya16FVy1/+4a65HCCHEbk0qOYUQYg/W6GysHanm0/X0pempPWo21u7Wrq+qqtmSrigK0WiUcDhMIpFocFqr1YrX60VV1fRyFi2L5aOvRVe0tq3o1HUUPcmIn54gl3Rw6ff7G62+1DQNj8djzj40ZiIGg40H64qimOFUW1Z5GtWSLQ0tG941eqMVlNuqrtzRwHJHrdzvUoI5vTtGi7VB13FXraLP7Md36Ozbq9RsTvt5SxnPx7WFB7Ky6LAOV1UtxI6q7jyMkuHpebZt8rje8vNI9/n/lopOIYQQDUglpxBC7KFkNlZDu2u7vsViMSsddV03W9KbCvDcbjdOp5NEIkF1dTWpVAqXGmPQqndY0ufk9C+JbfjL6KCV75Cd8hMIhXA6neTk5BAMBs2lQ4ZkMklNTY15GxwOBy6XC6fTSTweJxAIkEwmMy5e3+kqz5ZUVWa2gze8qQ2DyWQyuc3Qsq0Dy5ZSVZWs4HpC2T3T4XhH0cKt4C2t1GxNiqKYYxni8TiFpV+zOWcgfmfndqmqJpXEWVtK/pov2/66xR4n4u7E2mHnAHrbjWFQFNB11g47B8fXG3CEytrmeoUQQuwWpJJTCCH2QDIbq66WtusD6YBOT4GqtVu7vt1ux+l0YrVaSSQShMPhBsFgJovFgtfrNZfOhMNhIB3y+Hw+dF1nlasfaweflj7DrvylVE8HsN0WvUrehjnmQptQKGRWpG6rqtNgLB/StPTjOJVKEQqFGr0f6oeRNpsNi8WCpmlmsGX8McLKxgLL5lZVZv57T2Hcbw6HA6vVSkVWb+b3O6O9D6uB4ctfxVO2lGg0aobchqZCTSPw3pWhZia73Y7H4wEgHA6bFdibkw6W7HdFurq+LVvX9RSKnqLf1/+SYEjsNB2FFQdcQdhb1K6Bfd/vH+mQnRdCCCHah4ScQgixh2mT2VhQJ8TqqLOxdsd2fUVRzJZ0TdOIxWKEw+EGQU59LpcLl8tFIpHA7/ebFY+appGdnU0qlaK6uhpd19stBHe5XLjdbqLRKJFIxKy2DAQCDeaJNlZB6XA4sFgs5qIiI1xs7vzK+st5kslknfbk9mgH7yisVisOhwO7PV3ZnEql0veTqvH14IuJ2rI6Rsv6lq3gI2ffj9Nhx2KxmHNJIR30t1eoaVBVFY/Hg91uN7evu91uEokEtbW16edi52GUDP8zO/ya1FLS4ita2eaeh7Kx/wkyekEIIUSHIiGnEELsQWQ21lat2q6fSddx1q5t9XZ9TdPMcBMwW9Iz27ObOp/X68VisRAKhQiFQubXLBYLPp/PbAPPDPB26f1TU5K+f0JldeZXZlYJQnqhkNVqRdM0M2BszvxK43Znhp2RSIREItGs+ZXGcRh/VFUllUoRj8d/ExvbDZqmmcGmpmlme73Fkp5mZDwGNxQf0qG2gndd8RHd1n/ToFLTmOMajUYJhULt8j10Op243W5SqRTBYBC73Y7dbiccDhMIBOqctu3ekEo/D7oveY3sUlk2JHZewupm6aG3oKvtP/lMSSUY9MXtWOKh7Z9YCCHEHk9CTiGE2ENE3J22LJaRFsjdqV3fZrPhdDqx2Wwkk0mzJb05FYVGoJJMJvH7/XUWEFmtVnw+n7l5vbHL01EI7nUMq4sObbVK1z7rv6Jow3eoCk22g2ceixGsWa1WALP9uDnzKy0WCx6Px6zuTKVShMPhOkFvc1gsFjPwNC4rM/BsbLHT7sqoiLXb7VitVrMKUlVVLBZLo4/BjhZoHLzwUSyJcINKTavVaoaKqqoSj8eJRCJEo9FdXqGb+WZDJBIhEomQlZWFoij4/f4mK7Hb6rVq0Mp36FT9M1VVVb/ZamXRejb3PKxDvfHRZdkHdJJqTiGEEEjIKYQQewSZjbXV7tCubwRNTqcTTdPMBSj1W7aboqpqnRmX9beP22w2srKyiMfj5iKfxo7B6XTicrmoiqQo77I3m7uNJmr3oaSSW8Lybc8sVfQUuqphj9bQdeMPdC77CXsqiqZpZhVqU/MrFUXB6/Vit9vN+aHGgpZoNIrf7292GKMoirmoyKjuNOZ9tjTQaarKM3Nj++4YEhnhn81mAzBvh1FJu72xCB2lNbX76k8oWPPFdtvPG7u90Wi02c+xljAWfRlvNhjhe2Z7+ra0RVW1K1JBTk4OyWSS6urq1rsO8Zujo/DzITcRd2R3qBEWA7+6s91//hBCCNH+JOQUQog9QEcJINp7NlZHb9c3Fu4YYVw0GiUcDreoUtDhcOB2u9F1Hb/fTzwerzO/0qgMTSaTxGKxJjeFN7wpOjpQ4e1Fpac7te6uBDyFpLSmt8+7akrw1Ns+rygKOTk5ZsCzPcYsUSOUtFqteL1egG1WwG3r/nG5XOZtTCaThEKhHQ63ducqz8YqG2OxGJqmmbM3mzsWYXd9I0VRFOx2u7lIyWhnN2Zl7gyr1YrH40HTNEKhEOFw2AzuG2tP3xYdhfKeY9jYivODuyz/iPyM+cEWi4Xs7GzzTQQhdoQ/fwCr95nQ3ofRQK85T+Et/6W9D0MIIUQ7k5BTCCF2cx2tlbS9ZmN15HZ9q9VqtqTrum62A9ev8Ko/v7J+QGmz2Zo9v3J7W8GNRTM1NTUkEgk0TSMnJ4fq6moz/NEsVpydu1MZjJJIKSipBFoigjVSvc2gyWaz4fP5qKmpaVZIabPZ8Hq9pFIps/LNCIsikQiBQKDF1ZOapuF2u7HZbGYreyQSaVD12hK7Q5WnEWAai6uSyaTZem60qLd0LIIh7u3Czwdcs/0q39bWSiMxVFU1W/WN1nxjCdb2Qt5MRuWw0+kkFosRCARQFKVZ7enbk7C6qSzaj4ruBxF35kAqmX49205VNXoKVA1ruIr8klnkrPuh0ddh47kZDAZbPNZBRyHuyCZpdaArFhQ9gRbf/uuB2LOs738C5T0OaZ83O5qSSlLw61cULnu/vY9ECCFEO2v/34iFEELslMqiUVsqf9qfrqhUFo1q89lYOgprh56VnmvX1veFkl56snboWXWqzIyWdGMjuBGopFIpc/ty/UBzW4Gl8bVYLGbOscwML61WKy6Xq9kVZB6Ph0gkYgaaNpvNXL5jUBVwxmpx1Fa0aImL0Rrs9XqprKzcbpAWi8Worq4mKyuL7Oxs/H4/tbW12O12PB4Pubm5LQ6OksmkWUlqtLK7XC6cTifxeJxAINCiYAvS34vMlufMKs+srKw6G73bsspzW9WKRphttKQ3N3iuz2KxkGeLo6x8myV9/wQ6bVgtDcULpu30zN9UKmUu57JYLObz0+VykUgkzPmd23qsG49JSFcaRyIRHA5Hi9rTt8USD9JpzRcUrPmSQH5//Ln9CPu6E87qRsrSsqrqxhihrMfjMV+TmqKjtOgYnDUleJtxDL8VhYWFTJs2jeXLl2OxWFi8eDGPP/44kUik1a5j9OjR2O12Pv/881a7zO0J+bqDovLEnwZwyVu/MLzQw5STBzJrTTVlgTivLdjEmqpt38bvL9uX/R9t2ZiXbVLU9HG1spycHC655BImT57Mvvvuy+WXX04ikeCxxx5j7ty5AJx99tkccsghaJrGrbfeyvr16znrrLM46qijqKmp4aabbiIYDHL44Ydz4YUXkkql+OCDD3j11VcZOXIkN910Ez6fjyOPPBKAzp07c+6553Lfffe1+u0RQojfAgk5hRBiN6ajUNH9d0AHmIsFgEJF94MoyGiRbAtlPce0/jy7llA1wr7uBAcdQ4/NPzS6cKf+NnDj70Qi0eBzmX8bcyq3VdFozNZsbD5nY5xOJ4qi1KnkstvtDcIv4zbsSHViIBAgJycHt9vdrNDVmBXo9XrrVJrF43E8Hg8+n2+HqzqDwSDBYBCbzYbb7cZqtZKTk2OGXjsaOiQSCRKJBKFQqE6VZ+aG7V1Z5Wlsqs+cO1lbW0symcTpdOLz+YDmt6Q3xWq1kpWVRTKZxLr6W7pF4um5t7reZnNvd3bBV32JRIJAIEAgEMBms2G323G73bjdbuLxuBkSG98z440Ju91ONBo1H4derxeHw9Hi9vTtUdDxlv9itt+aVZQWB7pqwe2wYSdBeHNJi19rw+GwuSip/hsbYFSTjqKi+++aVU2astgJ5vQmmN2T8l6HYQ1XkVcyi9x1s7HEd7xyek8wZ84cJk2aBMCll17KxRdfzIMPPthql//NN9+02mU1h45CJKsbKAqXvJV+bB4zII+7v1jD24vL2/RY6lAUwlnd0u+9tOLFnnbaaXzwwQcAXH755fzlL38hkUjwyCOPMH78eAYNGkRhYSEXX3yxeZ7s7GzGjBnDhRdeyLHHHssZZ5zBs88+y4UXXsjEiRMJhUL85z//4bXXXmP58uWce+65PPPMM+b5N23aRH5+Ph6Pp1VfU4QQ4rdCQk4hhNiNBfL7p38J7SgUhbgzh0B+/zabjZWwutnU79j2X4CgKKzudihFlYuwJsNmSJIZYraEzWYzqz1ra2ubrLhyu924XK5mt58qioLL5arTLm9s1q4fkO5MyJlKpQgGg3i93mbPP9R1ndraWjMktFgsDao6c3JyCAQCO1SNaISNiqKYgZXX68Xj8dQJrqDlrbmNVXka7eGtWeVpVCBmztkMBoNEIhEzYDVa0o3P70y42tgSq9z1s1GT0fRWcF3fpVvBixdMa/WAsz7je2IEnkZlpvG40HUdh8NBKpUyK2EtFovZnr6j1bEtoaBji1SZHzsTrvSbHzv4ZlIgEEDTNLKysqiurk4vCUOhrOcYNmXOBYXmfX8VBZT06eKObDb2P55N/Y6l8/KP2vxNr47qqaee4vXXX+fBBx80qwIBXn/9dd5//31uv/12YrEYPXr0oLS0lA0bNnDQQQfx008/8cADD7D//vszfvx4HA4HM2bM4LnnnuPEE0/E5XLx6quv8uabb7J48WL69+/PCy+8wEcffVTn+i+99FJGjRpFLBbjkUcewWazMXbsWGKxGPn5+dx2222sWLGC0aNHM3bsWDRN4z//+Q8ff/wxxcXF3HTTTShWO7OCuUz6YAXfX7YvF7y2hAn7d8UfTVLgtnFgDx/3fVXC4k1BHjmpP0O7eEikdM58eRHlwYb/B5w7sgsX7ldIlt3CQ7PW8tK8jdxyRC/65DrJc1lw2TSOe3Y+kUSKR0/qz+AuHn4oqWHf4iwOf2oeMyaM5A/PLyAYS/Kf8/dhysqBZFsS3HDDDVitVpYsWcLdd9+Npmn885//xOv1smbNGpxOJ7feemujtzXT/vvvz9SpU4H0G5VG6KhpGtnZ2Rx66KHY7XaefPJJVq1axb333svgwYPNKs9vvvmGO+64A4A1a9bgdrvNCmpd15sMMefOncvo0aOZPn36TjzihBDit0lCTiGE2I35c/ulq2w62GysQG6/XR5yGtWS1cUHdKh2/TVZA8ld8ekOB0uZ8/6M4K2pgNQIOAOBAOFwuOnjygjs7E4PAbuVQHgTFoIopGc1GhvJM6mqulMBWWYrb1VV1fbPsIWxjMloXzdC3ng8blZ6hsNhgsHgDh2fsbTJ7/fjdDpxOp3YHU4CBYOodBdT7exCyFu0U625RpUnYM5T3dEqT1VVzXZ0Y/RBJBIxg2qn00lOTs5Ot6TXZ4TARoWoQdM0utQsI2/+FFb0Pxm/q7D1t4LXllK88JWdblFv2dVuDaqNJWFG1bPx/EilUq3anr4zx9rYArGWqK2tJTs7G5/Px4aohZLW2vCuKICCrqhs7H8CNV2Gp7+Xwc07d7m7uUQigdVqBdJVgVdeeSWBQIAXXniB//3vf0A63Lrzzjt5/vnn+eqrr5g6dSovvfQSFouF+fPnM27cOBRF4d///jcvv/xyncvPz8/n7rvvBuCJJ55oEHIecMABXHjhhSSTSRRFYe+998bhcHDppZfSq1cvrrzySq666irGjx/P+PHjSaVSPPPMM0yfPp0rr7yShx56iLml1az43XXmZS7dHOKFORuZU1rLBz9XcGCPdPX4iYPySek6hz6ZDvuaeki9uWgzL83biMOiMvOSfXhp3kYAlleEOP+1Ndx1TB+O7JfL+too2U4Lhz05lyP75bJvcVajl5ey2Fm7djXjxo0D4IEHHqB79+7079+fX3/9lUcffZSTTz6Z4cOHAzR6WzOfz8YcbUi/GdKlSxdisRh9+/YlKyuL3NxckskkEydO5Morr+TII49EURQzvAwEAmZF/fTp03nppZdIJpM8/fTTjd8hW6xbt45hw4Zt8zRCCCEaJyGnEEK0oVtuuYX//ve/LFq0iLPPPpvRo0dz2WWXAfDmm29yyimntOjyPrr7CvZ7YgE3H96TYwbkAfD4N6VM+2lTqx2zTVN45tRB/PnVJdxyRC9OHlJARSjO3HV+Jn2wAq9d499n7IXHbmFOaS3Xf7iCgt6DmTrpJACysrIoLS3l2muv5ayzzuLcc89lyZIlZgtfp06duOOOO7BYLHz55Ze8+uqrdO7cmbPPPptHH3200bmVxt86Ckt7HERHatffWLg/2cs/3aEjMjaLG8tLttVGbbSxN3a6ls7Sy45sIrtmFSoVdQI7Y2HPzvD7/eTk5Jjt9M0Vj8epqqrC5/ORk5NDbW2tGeAZG+ZtNpu5YX6Hjy+h8mvWcCp6HETckY2SSm5zsc6OtOYaS4+M75PVajVDT4fDYVZ5RqNRc+aq0f5uzNkEzNA7Ho9jsVhwuVwt3pLeXEaQF41GCYVC5nFYrVY0TUsfc6yKwfOeZG3nUazvc/Qu2wreHoyA06iINTbVO51OIP349Pv97RJwQvox1dgM35bQdZ2amhrivQ9kWa+T0vd2a1fEKwphbxHLR1/bJlW5HZnVajXffNA0jerqagDWrl1LQUEBAMuXLwegrKyMZcuWAVBRUYHb7aZ3795MnDgRi8VC165dyc3NrXP5paWlZjW+pqXf+Hz88cexWq3cfffdTJkyhdtuu41IJMKUKVMA+OWX9JuRq1evJj8/n5ycHHr06METTzwBgNfrJScnhy5durB06VL0rGKa877SwE4uvlpVbX7c1HmO7p/L5b8rRgH65jnNz/+03p++b2oi5DgtuKwqc9alPzdn3dY3XDIvV1FAVy0UFRVxzTXX4HA4KCoqoqCggOLiYpYuXQrA0qVLGT58eJO3taKiotFjvffee7ntttvw+/0sX76c8vJy/H4/ixcvBuCHH35g5MiRLFiwgO7d0/NBPR6PWQF/xRVXcNpppxEKhXjyySeZPn06fr9/+3emEEKIFpGQUwgh2tDChQsZOnQoixYtYtCgQWalV48ePVi9evV2z29UFEE6yNI1GygKL83byN9nrMGqKcy+fL9WDTnPGN6Zj37Z+kP/TR+v5IOft348flRX3l1SzrM/buDRk/qzX3EWc9bEuOTSS1EVhfHjx7NhwwbcbjczZ85k3rx5XHzxxeTk5KCqKpdeeilvvvkmP/30E7fffjs//PADlZWVdOrUyVw409T8yqrsvkRtjVd0tIudaNc3qjebE54Ym8f9fn+dNvYdnaUX0nuyrutorD2PrxPYZT7edlQymSQUCuFyuYhGoy0K4VKpFFVVVWb1pjFzNBKJEIvF8Hq9ZGdn79A8xKZac/U2aM2Nx+Nmm3lmlafL5cLj8dS5z43Hg1Htabfbyc7ObtWW9PqMCmGj8iw3N7dBEGs8FwHyVn1Gz+AySrMHs6Fwf2J233bDYqDuVvBI9Ta3grcVq9WKx+NB0zRzSRGkH4t2u918HbJYLOTm5hKPx82FRa09c3VbMkdN7EzQWt5lH0p7/5EdDqebQ9XQ9RQlw/9MatGr5K5vxYUzu5GLLrqIL774Akh//7KzswkEAhQXF1NWlq5YznwMZf5bURTOP/987rzzTkpLS3nllVeadZ2XXnqp+e+1a9fyzTffcMwxx3DKKacwZ84c+vfvD6R/BikvL6e6uprVq1dzySWXmI/zRCLBxo0bGThwIPPW1aTDxO081H/eHOLwvjm8uahsy/E3fp4bD+vJoU/ORQdW/N+BGbd962kUBVZUhDl+UD4AI7t6za9Vh+N089lZURFmr05ulFSC0047jX//+998//335vzTtWvXMmDAAGbMmMHAgQPT523itmYyxpvous7SpUu5+OKLyc7O5q9//SuhUIj58+czePBgZsyYwYABAygtLWXx4sWce+65ABx44IHMnz8fwHzNTyQSJJNJ8//wxhQVFTXrZ0IhhBANScgphBBtaMGCBYwdO5ZXXnkFh8PBihUr6N69O8OGDWPhwoVmVaPVamXZsmXcfffdnHjiifzud7/D4XDwxhtv0K9fPw499FB+WVWCsqVdcXVlukIsntRJptK/Hbx13lAueesXNvpjXLRvIVZN4fk5G5l6ykAKs+wEown+/OoS/NEkfz2sB8cPzCeaTHHlO8tYtGlrRdpJe+WbCwYAbj+qN9ce0p2/z1jD5yur6JPn5JnZGwCYt97Pwb2ymV3qx9GpO85YLWPGjOG6667DZrMRCARwuVxm66eu63Tq1Il58+ZRU1NDSUkJN954I+effz7fffcdgwYNolevXixZsoSZM2c2uD+rfb3Mdv0ZE0bya1WEi15PV2s8clJ/RhVnmRtc++e7WHzN/vS551tKqhtWSB47II9Cr41nf9zA86cP4pj+edwxYzWPf7sOgAEFLqacPBCLqnDr9FV8trKKi/cv4rThnQDYq5Obif/9mXcXbeLySy/hsD7pWakPPPAAP/30E+PGjePAAw/Ebrfz/vvv85///IchQ4aw77778s4776BpGsFgcJtt55CujLXZbGZlIzQd2DV3lp7eRGDXu2JuqwQ3oVDIbH02qpdawu/3k0gkzDmdRouwUdXp8XhaVNUZcXdibQdpzTUWv2iahsWS/rFM13V0XUfTNKxWK4qimBvSW7slHTCvx6gwNcYUGM/T+qFmfXa7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F4lEorsPp8eKxRPUjj6Gbwf/aPuy6+2hZluG6CgKKPnbZV0htpScQMWY4xlQ+nZBqwmz2WyPDDnrI2PyYXB3DRxqjqYSi4yRkFMIIYQQPULPaOojhBBCCNGL6bpu9pkUzUt5+7P8gAtZM2QyusXWYuVmqxQFFAu6xcaWkhNZc9hlpLz923z3QYMGcc8995ifH3nkkVx44YUA/O///m+T/qqnn346J510UqPL3G43s2bN6tjxAyeddBIff/yx+TUzbtw4vvzyS/bcc89mb58IDs+fs+1mn7o34wZ4O7z/glAs/Ouuy3Z69UknncTpp5/e5PJTTz2VQw7J9/NcsGABs2bNYtasWYwePRqAmTNnMmvWLJ566inmz59vbuv//u//mDVrFn/6058abe+nP/0pc+fONT+fMmUKTz/9NH/5y18oLi4G4KqrrqJ//7Z/jQghhBCi95FKTiGEEEKIAkgmk7jdbhwOB5lMprsPp0epHTCe8vFnoaMUfjK4opD0D6H08CsZtuQFQhVLdmlzf/zjHykqKuqS5/G7777jiCOO4N133+XHP/4xy5Yta/Z2OgqpwFDz3HX3cHWToqBbHei0r1PopEmTuPTSSwFYv34906dPb3T97373OwAmTpzYKFx+8cUXeeWVVxrd1mKx8OMf/5gtW7YAUFRUxBFHHMF5553HuHHjmD59OnfccQf/+Mc/OP3003nooYfa/ziFEEII0StIyCmEEEIIUQCqqpLNZnG73RJyNlA9+GA27Hta/pPOmgxusaLrGmUTpqAte4XIps87vKk5c+Zw6aWXMnToUK677jqSySSZTIb3338fgGuuuYYxY8ZQWlpq3mefffbhf//3f7FarcybN485c+Zw4YUXMnToUEKhEG63m0suuYR0Ot1oX/PmzWPSpEm8++67jBo1iu+++w6ASCTCnXfeidVqpbq6mt/ddCfD+gV55rR92BJN89XmmLmN/Qf7+ONxo5jyygrOnTiIX+3XH1XX+d9/rMaiKJx/8CAu+ftqAD66eCJHPLoYffvK/kmjQvz+qBEkshp7RFzc+f56zjtoECG3nROe/orqRI57ThjNIcMCZFSdaa+vZH1Niv83aQQ/36eYZVtiKBYLWVeYPYo8XHvttTgcDlatWsV9993X7PkdM2YMGzduND8fOnQoTz31FN9++y333ntvo++dY489lnfeecf8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RhYKhQiFQlXCznA4jN/vl5MOolWqyOwL8Vh6JqrXJR7Dm9lXQk7R5CTkFCeltZfC12ffzFgsVq99M5tbNBqlrKwMm82W2ufL6/VKaCJEEzteCKrRaKpMhZcQVLREySEcPp9P/p01I7/fj9FoxG63U15enrpeReGQpQcV3fpQZuxIwNGNuM5Y4/6aaAizZz9mdz720h3YirdL6ClEI0mGnQaDAavVitPplLCzncrJyWHx4sXs2rULgI0bN/Lcc8/V+/5Dhw7l/PPPZ/bs2UyfPp3HH3+81ue44447uPvuuxtt3Un+jB61zslY+OvTeOazfDYf+qGb4M+X9eX+/+4iGG3i97iKJrGuSqZOncqWLVv4/PPPU9eZzWaeffZZpkyZUuXY3r17M378eObOncttt93GZZddxrJly5g9ezYAubm5zJw5k3g8TmFhITNmzKB///7cc889RKNRDh8+zIMPPph6/Z6Tk8M777zDlVdeya5du7jhhhsYMWIERqORd999l7/97W8MGDCAM888k9dff71pvzaiUUnIKU5KaymFr8++mdUrM1tDm4qqqjWGElVUVMgLMSHSKB6Pp7avqE3lELRyENrQEFTajMXJCAaDWCwWjEajDLtpZhUVFbhcLkwmE96YltKuZ1PSYxQRswslHkt0x9SxBVBcZ8Tn6oXPmUvxKRegD5SRlf8FmYVr0UV8td5HCNEw4XCYcDiMwWDAYrHgdDqJRCL4fD55jd2OfPPNN40SQNYWcDYlFYWgo1u9t5K7490dTbyioxSFgKMbKonyKKWBW91dddVVvPTSSwAsXryYL7/8kvPOOy91+29+8xvmzZvHV199xcyZMxk0aBCFhYXceOONhEIhbr31VsaMGcPHH38MwDXXXMO3336buv+rr77KK6+8glar5e233+btt99m06ZNTJkyhTfeeENOCLciEnK2csmzTDt27ECn07F582ZefPFFgsEgd911F8899xyKovDcc88Ri8X4wx/+kPp42rRp9XqOyZMn88EHH3DgwIEatzVHKfzqW85i+PNf13rb6F5OLu3XgXve3/nDlfEYIy+8nNPjg/nggw944403KCkpARI/ENesWcOAAQOYMmUKsViM559/nm+++YZRo0Zxww03JCa1FxTw0EMPoaoqt9xyC8OGDQNg9uzZfPvtt0yfPp3c3FwABgwYwEUXXUTP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OnQoLpeLb7/9FovFQlZWFhdccAHXXnstnTt3ZsaMGdx8881cddVVXH311VitVt577z0AJk+ezFtvvcXatWu59tprGTt2LGVlZQB07tyZYDDIH/7whxrPq9FouO6665g0aRJ6vZ558+YxceJEvvzyS15//XV27dqVOjYvL48BAwYwdepUrFYrGo2GqVOnsnDhQlatWsXMmTMZeM75qFodJf4oU/+xlccu7sXgLjYuXvAtz1zWh/NOcRJXVTSKwi/f+I4pZ3fhikGduGThBm4d1Y2fnp7NJ7vK6vw6/WtLMXe9t5MvfjcUh/GH13cdrHpuOLsLY+auIxJTURQwajWMnbcegH9efQandMzgzXfepqioqEYL/y233MKjjz7Knj17ePHFF1m2bBkABQUFPPjgg9x2222cc845VQLf3NzcVIiZdM4557BmzZoaRRl33HFHqu3/q6++4qWXXmLatGls3bqVvXv3snv3bkaOHMny5csZPnx4qsUdYMGCBfTo0aNKcF1eXk7nzp05cOBAnV8rIVoDCTmFEO1S5aFETqcTv98vQ4naoWMN+Knc7la5GjMSiUg1phBCCNHKnWx7vMFgqPF6ofLjprs9fvny5cyaNYt33303dV2XLl3Yvn07qqpy8ODB1An/Q4cOEYlEKC8vT4VsvXr1YsCAAdx4440YjUbee++9VMi5f/9+NmzYwGOPPcbBgwd56aWXUl9Ll8vFwYMHCYfDhMNhotFolZPDlfXt25etW7dis9kIBoP4/X46d+7Mpk2bgETbfffcXA4CG4u8ABzwhPCHE89V6A7hMuso8Ueq3L7x4NGP3SF6uExU/nJXn8++vrAidT+nWZ+6vlemmXWFFURiiTurKpySaeLpS/ti1mvolWmmi8NIXGuo9XPr0KEDe/bsARKVlN27d099DHDo0KEqwWNdxo8fz7///e8q102bNo2NGzeybt06ILGP58MPP8w333zDH//4R0aNGsVnn33GWWedxdy5c9m9ezclJSWp+19//fV07tyZ2bNn87///e+4axCiNZGQUwjRbslQoravejVm5UCzcoVGshozHo+nQszkn7a4V5cQQgghjq8p2+OP1xp/sidR8/Pz+fbbb/n4448ZPnw4AAcOHCAvLw9FUejcuTMVFRWUlZXRqVMndDodFouFrl27ArB3714++eQT1q9PVC7qdDoGDRoE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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "import networkx as nx\n", + "\n", + "def plot(graph):\n", + " labels = {x: f\"{graph.attribute(x, 'id')} ({x})\" for x in graph.scan()}\n", + " options = {\n", + " \"node_size\": 750,\n", + " \"node_color\": \"#0277bd\",\n", + " \"edge_color\": \"#454545\",\n", + " \"font_color\": \"#fff\",\n", + " \"font_size\": 6,\n", + " \"alpha\": 1.0\n", + " }\n", + "\n", + " fig, ax = plt.subplots(figsize=(17, 8))\n", + " pos = nx.spring_layout(graph.backend, seed=0, k=0.9, iterations=50)\n", + " nx.draw_networkx(graph.backend, pos=pos, labels=labels, **options)\n", + " ax.set_facecolor(\"#303030\")\n", + " ax.axis(\"off\")\n", + " fig.set_facecolor(\"#303030\")\n", + "\n", + " plt.show()\n", + "\n", + "plot(graph)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5tyi7q9fOaja" + }, + "source": [ + "This graph shows all the interconnections between the search results. The numbers on each node are the internal node id (same as the index id in an embeddings index).\n", + "\n", + "Let's explore the path between two of these nodes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "JQQQBzdX7slw", + "outputId": "ab64b244-97fe-4118-b0df-d54dbd130878" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'id': 'Linguistic relativity', 'text': \"The idea of linguistic relativity, also known as the Sapir–Whorf hypothesis, the Whorf hypothesis, or Whorfianism, is a principle suggesting that the structure of a language influences its speakers' worldview or cognition, and thus individuals' languages determine or shape their perceptions of the world. \", 'topic': 'language_word_languages_also', 'topicrank': 6, 'score': 0.794975996017456}\n", + "{'id': 'Input hypothesis', 'text': 'The input hypothesis, also known as the monitor model, is a group of five hypotheses of second-language acquisition developed by the linguist Stephen Krashen in the 1970s and 1980s. Krashen originally formulated the input hypothesis as just one of the five hypotheses, but over time the term has come to refer to the five hypotheses as a group. The hypotheses are the input hypothesis, the acquisition–learning hypothesis, the monitor hypothesis, the natural order hypothesis and the affective filter hypothesis. The input hypothesis was first published in 1977.', 'topic': 'artificial_learning_intelligence_based', 'topicrank': 88, 'score': 0.7856757044792175}\n", + "{'id': 'Natural language processing', 'text': 'Natural language processing (NLP) is an interdisciplinary subfield of computer science and linguistics. It is primarily concerned with giving computers the ability to support and manipulate human language. It involves processing natural language datasets, such as text corpora or speech corpora, using either rule-based or probabilistic (i.e. statistical and, most recently, neural network-based) machine learning approaches. The goal is a computer capable of \"understanding\" the contents of documents, including the contextual nuances of the language within them. The technology can then accurately extract information and insights contained in the documents as well as categorize and organize the documents themselves.', 'topic': 'artificial_learning_intelligence_based', 'topicrank': 33, 'score': 0.7930918335914612}\n", + "{'id': 'ML (programming language)', 'text': 'ML (Meta Language) is a functional programming language. It is known for its use of the polymorphic Hindley–Milner type system, which automatically assigns the data types of most expressions without requiring explicit type annotations (type inference), and ensures type safety; there is a formal proof that a well-typed ML program does not cause runtime type errors. ML provides pattern matching for function arguments, garbage collection, imperative programming, call-by-value and currying. While a general-purpose programming language, ML is used heavily in programming language research and is one of the few languages to be completely specified and verified using formal semantics. Its types and pattern matching make it well-suited and commonly used to operate on other formal languages, such as in compiler writing, automated theorem proving, and formal verification.', 'topic': 'programming_language_computer_level', 'topicrank': 58, 'score': 0.7853577136993408}\n", + "{'id': 'Domain-specific language', 'text': 'A domain-specific language (DSL) is a computer language specialized to a particular application domain. This is in contrast to a general-purpose language (GPL), which is broadly applicable across domains. There are a wide variety of DSLs, ranging from widely used languages for common domains, such as HTML for web pages, down to languages used by only one or a few pieces of software, such as MUSH soft code. DSLs can be further subdivided by the kind of language, and include domain-specific markup languages, domain-specific modeling languages (more generally, specification languages), and domain-specific programming languages. Special-purpose computer languages have always existed in the computer age, but the term \"domain-specific language\" has become more popular due to the rise of domain-specific modeling. Simpler DSLs, particularly ones used by a single application, are sometimes informally called mini-languages.', 'topic': 'programming_language_computer_level', 'topicrank': 29, 'score': 0.780302882194519}\n" + ] + } + ], + "source": [ + "for x in graph.showpath(49740, 96081):\n", + " print(graph.node(x))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "d-fMy7NqRDMp" + }, + "source": [ + "The logic above walked the graph starting at the node for `Linguistic relativity (49740)` and ending at `Domain-specific language (96081)`.\n", + "\n", + "The nodes in between are semantically related and were used to find a semantic path between the nodes." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "C7KJw79SPo3P" + }, + "source": [ + "# RAG with Semantic Graphs\n", + "\n", + "Retrieval augmented generation (RAG) helps reduce the risk of hallucinations by limiting the context in which a LLM can generate answers. This is typically done with a vector search query that hydrates a prompt with a relevant context.\n", + "\n", + "Can we use the semantic graph to power a RAG system? Of course but how?\n", + "\n", + "Looking at the graph above, the most connected nodes are in the middle of the graph. These nodes are the ones that best overall cover the topic in the original query.\n", + "\n", + "Where does this differ from a vector query? Well this method not only looks at semantic similarity between the search and results but the similarity within the results. It's effectively judging the results to see which result is most similar to other results.\n", + "\n", + "Let's show the top 10 central nodes in the graph." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "XWsLf1-N73rN", + "outputId": "045bfbe1-5181-49ce-8141-f5ad1dc1d363" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'id': 'Generative pre-trained transformer', 'text': 'Generative pre-trained transformers (GPT) are a type of large language model (LLM) and a prominent framework for generative artificial intelligence. They are artificial neural networks that are used in natural language processing tasks. GPTs are based on the transformer architecture, pre-trained on large data sets of unlabelled text, and able to generate novel human-like content. As of 2023, most LLMs have these characteristics and are sometimes referred to broadly as GPTs.', 'topic': 'artificial_learning_intelligence_based', 'topicrank': 11, 'score': 0.8267543315887451}\n", + "{'id': 'Large language model', 'text': 'A large language model (LLM) is a large-scale language model notable for its ability to achieve general-purpose language understanding and generation. LLMs acquire these abilities by using massive amounts of data to learn billions of parameters during training and consuming large computational resources during their training and operation. LLMs are artificial neural networks (mainly transformers) and are (pre)trained using self-supervised learning and semi-supervised learning.', 'topic': 'artificial_learning_intelligence_based', 'topicrank': 43, 'score': 0.9012120962142944}\n", + "{'id': 'Natural language processing', 'text': 'Natural language processing (NLP) is an interdisciplinary subfield of computer science and linguistics. It is primarily concerned with giving computers the ability to support and manipulate human language. It involves processing natural language datasets, such as text corpora or speech corpora, using either rule-based or probabilistic (i.e. statistical and, most recently, neural network-based) machine learning approaches. The goal is a computer capable of \"understanding\" the contents of documents, including the contextual nuances of the language within them. The technology can then accurately extract information and insights contained in the documents as well as categorize and organize the documents themselves.', 'topic': 'artificial_learning_intelligence_based', 'topicrank': 33, 'score': 0.7930918335914612}\n", + "{'id': 'BERT (language model)', 'text': 'Bidirectional Encoder Representations from Transformers (BERT) is a language model based on the transformer architecture, notable for its dramatic improvement over previous state of the art models. It was introduced in October 2018 by researchers at Google. A 2020 literature survey concluded that \"in a little over a year, BERT has become a ubiquitous baseline in Natural Language Processing (NLP) experiments counting over 150 research publications analyzing and improving the model.\"', 'topic': 'artificial_learning_intelligence_based', 'topicrank': 78, 'score': 0.8038361072540283}\n", + "{'id': 'GPT-4', 'text': 'Generative Pre-trained Transformer 4 (GPT-4) is a multimodal large language model created by OpenAI, and the fourth in its series of GPT foundation models. It was initially released on March 14, 2023, and has been made publicly available via the paid chatbot product ChatGPT Plus, and via OpenAI\\'s API. As a transformer-based model, GPT-4 uses a paradigm where pre-training using both public data and \"data licensed from third-party providers\" is used to predict the next token. After this step, the model was then fine-tuned with reinforcement learning feedback from humans and AI for human alignment and policy compliance.', 'topic': 'artificial_learning_intelligence_based', 'topicrank': 57, 'score': 0.8085479140281677}\n", + "{'id': 'DALL-E', 'text': 'DALL·E, DALL·E 2, and DALL·E 3 are text-to-image models developed by OpenAI using deep learning methodologies to generate digital images from natural language descriptions, called \"prompts\". ', 'topic': 'artificial_learning_intelligence_based', 'topicrank': 79, 'score': 0.8013206124305725}\n", + "{'id': 'GPT-3', 'text': 'Generative Pre-trained Transformer 3 (GPT-3) is a large language model released by OpenAI in 2020. Like its predecessor, GPT-2, it is a decoder-only transformer model of deep neural network, which supersedes recurrence- and convolution-based architectures with a technique known as \"attention\". This attention mechanism allows the model to selectively focus on segments of input text it predicts to be most relevant. It uses a 2048-tokens-long context and a hitherto-unprecedented 175 billion parameters, requiring 800GB of storage space, and has demonstrated strong \"zero-shot\" and \"few-shot\" learning abilities on many tasks.', 'topic': 'artificial_learning_intelligence_based', 'topicrank': 52, 'score': 0.8063850402832031}\n", + "{'id': 'LLaMA', 'text': 'LLaMA (Large Language Model Meta AI) is a family of large language models (LLMs), released by Meta AI starting in February 2023. ', 'score': 0.8551499843597412}\n", + "{'id': 'LaMDA', 'text': \"LaMDA (Language Model for Dialogue Applications) is a family of conversational large language models developed by Google. Originally developed and introduced as Meena in 2020, the first-generation LaMDA was announced during the 2021 Google I/O keynote, while the second generation was announced the following year. In June 2022, LaMDA gained widespread attention when Google engineer Blake Lemoine made claims that the chatbot had become sentient. The scientific community has largely rejected Lemoine's claims, though it has led to conversations about the efficacy of the Turing test, which measures whether a computer can pass for a human. In February 2023, Google announced Bard, a conversational artificial intelligence chatbot powered by LaMDA, to counter the rise of OpenAI's ChatGPT.\", 'topic': 'artificial_learning_intelligence_based', 'topicrank': 67, 'score': 0.8128454685211182}\n", + "{'id': 'ChatGPT', 'text': 'ChatGPT (Chat Generative Pre-trained Transformer) is a chatbot developed by OpenAI and launched on November 30, 2022. Based on a large language model, it enables users to refine and steer a conversation towards a desired length, format, style, level of detail, and language. Successive prompts and replies, known as prompt engineering, are considered at each conversation stage as a context.', 'topic': 'artificial_learning_intelligence_based', 'topicrank': 72, 'score': 0.7816742062568665}\n" + ] + } + ], + "source": [ + "for x in list(graph.centrality().keys())[:10]:\n", + " print(graph.node(x))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "V1P72uXDShkJ" + }, + "source": [ + "Overall, these all pass the eye test. If we scroll over to see the associated score with the original query we'll note that the highest scores aren't necessarily the most central nodes.\n", + "\n", + "Conceptually this makes sense. We searched for `Large Language Models`, so that page makes sense to score highly. Then we have the GPT page which is even more central. When we think of core concepts with LLMs and the most popular incarnation, ChatGPT, this also makes sense.\n", + "\n", + "Now let's load a LLM and use the central graph nodes as context for RAG." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "hqHjKQ878TBX" + }, + "outputs": [], + "source": [ + "from txtai import LLM\n", + "llm = LLM(\"Qwen/Qwen3-4B-Instruct-2507\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "jZX4fxFv_28v", + "outputId": "bd167b8d-b238-420a-b38e-728565b87d97" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1. GPTs are a type of large language model (LLM) and a prominent framework for generative artificial intelligence.\n", + "2. They are artificial neural networks used in natural language processing tasks and are based on the transformer architecture.\n", + "3. GPTs are pre-trained on large data sets of unlabelled text and can generate novel human-like content.\n", + "4. As of 2023, most LLMs have these characteristics and are sometimes referred to broadly as GPTs.\n", + "5. LLMs acquire their abilities by using massive amounts of data to learn billions of parameters during training and consume large computational resources during their training and operation.\n" + ] + } + ], + "source": [ + "def facts(graph):\n", + " question = \"List 5 facts from this text\"\n", + " text = \"\\n\".join(graph.node(x)[\"text\"] for x in list(graph.centrality().keys())[:10])\n", + "\n", + " prompt = f\"\"\"<|im_start|>system\n", + " You are a friendly assistant. You answer questions from users.<|im_end|>\n", + " <|im_start|>user\n", + " Answer the following question using only the context below. Only include information specifically discussed.\n", + "\n", + " question: {question}\n", + " context: {text} <|im_end|>\n", + " <|im_start|>assistant\n", + " \"\"\"\n", + "\n", + " print(llm(prompt, maxlength=4096))\n", + "\n", + "facts(graph)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6poUw0AbTcQV" + }, + "source": [ + "An interesting summary on LLMs. It's no surprise that GPT and LLM are almost interchangable!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0JGjObsxT4D8" + }, + "source": [ + "# Explore Wikipedia\n", + "\n", + "RAG with Semantic Graphs is great when we don't have a direct question but instead want to explore a topic. We can start with a topic and this method can tell us about the data. Often one of the hardest parts of search is knowing what to look for.\n", + "\n", + "Let's explore some history." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 422 + }, + "id": "wBmYvpFK6LTl", + "outputId": "441c1bd0-a1c8-41e6-b663-b02b3bbdb84c" + }, + "outputs": [ + { + "data": { + "image/png": "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mb0MX331Vb7++muCwSAvvfQSd911F3a7nQ0bNvDwww/X+r5nzpxJjx49UFWVu+66i127dvHiiy+yatUqjjjiCN5//32effZZ8vLymDlzJpFIhHg8zgcffICqquYS9PHjx7Np0yYcDgfRaJSioiKi0Sg1NTVmSwiXy8XTTz/N7t27uf/++6muruakk05iw4YNTJo0ibfffptXX32V4uJijjnmGAD8fj+xLStxVfdl8TcufnFy/3YPORU9hat6B7mFS2tdbrzBYLFYcLlcuN1u3G43sViMaDS6zzcnRNNlDrrKVHfKu9PpbLDfZ2b4ebAH0OW9j2XbyMvSn7RViwfVgq5rFI2+Am3NQnJ2fNE2xxFCCCGE6CJkuboQhzBFUfD7/SQSCTPM6yixWIx4PI7X6zUvM4JHY6J5ezCOWV5eTiKRwOv14na7iUajVFZWous6fr+fnJwcXC6X2V+xMbquE4vFzGWgxkAnr9dLt27d8Pv9OByOevs58cQTWb16NcuWLePMM880L9+yZQs33HADX331FSeccAIjRozA5/Nx7bXX8tlnn9U7/sUXX8zHH3/Mddddx+bNmzn33HMbPM/nn3+eK664otZlN998M7/85S+59tprmThxorkUeMOGDUyZMoWKigpUVeW2225j4cKFnHPOOdx000289dZbnHbaaei6zi233MJ1113Hxx9/bC6Jz83N5YEHHjD32+htp1r4tKiKc+Z9RSypcWRPDwtXlXD56PTyzMtG9WDhqhIqIknOmfcVpz31P7ZXxzhjSDanDQ7wwspdnDV3JTf+q/aAoMnH9WbRN2WcMWcla0tC/Hh0HgBWi8IlC77mrkWbufqYXrWu88MjurOtKsYZc1aycNVufjGuL3966jm2bt3K5MmTG+0d26NHDx5++GEeeughpk6dyqxZs5g8eTIOh4MRI0bU2va+++7juuuuY8GCBVxyySVAuo3E888/z89+9jMuuOACAH72s58xd+5cbr311lrtEbxeL4qikJeXx6ZNmygvLzfbL9R19dVXc91117Fs2TKmTJkCpKuH16xZww033MB5551Hjx49KCoqYvDgwfzjH//gRz/6EYsWvUW/r19iS3mYw3t4Gv3ZtQldA11n1LZFeD0NPx+kUimCwaD5WDN6/mZnZ+N0Otv3fA8hRl/jUChEVVUV5eXllJaWUllZSTAYJJFI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uWtIyl5KQcbYg1eOtjXTpFv20iOqRBURERGRw6eQU0REREQ6hIFJkLOsza+TkJNFRbch1EUkd8wgIp8XR+Uu4nOy2v/aIiIiIieIjt+3IyIiIiLShgxMUta/6a8ONX3te3HT1/T6IiIiItImFHKKiIiIyHEvpGYvKesW+z9pr2nr+66Tsm7xYU2DFxEREZGjp5BTRERERE4I0YXr6LnhHWiPik7TB5j03PA20YXr2vZaIiIiIqKenCIiIiJy4ojdvRKL10Xe4GsxTbNtenT6vP4t6usWK+AUERERaScKOUVERETkhBJduI6QLwvIGzSBusiU1p26bpo4KneRsv5NbVEXERERaUfG0KFD1QFdRERERE44JgbFaSMpSL8E07AAxtEFnqbpP5vpo1v2UuJzsjRkSERERKSdKeQUERERkROaxx5GafIZlKSOwO2IAZ8XDMvBA09zX19PixV7XRnxuZ8Tk/8NNndt+y1cRERERAIUcoqIiIiI4K/srI7PoCo2nbqoVOoie+KzBTd7nMXjwlG5i9CKXMJLswkv3qLKTREREZEOppBTRERERKQFJgbukGi8thBMiw3D58HqcWJ3livUFBEREelkNHhIRERERKQFBiZBzrKOXoaIiIiIHAZLRy9ARERERERERERE5Fgo5BQREREREREREZEuTSGniIiIiIiIiIiIdGkKOUVERERERERERKRLU8gpIiIiIiIiIiIiXZpCThEREREREREREenSFHKKiIiIiIiIiIhIl6aQU0RERERERERERLo0hZwiIiIiIiIiIiLSpSnkFBERERERERERkS5NIaeIiIiIiIiIiIh0aQo5RUREREREREREpEtTyCkiIiIiIiIiIiJdmkJOERERERERERER6dIUcoqIiIiIiIiIiEiXppBTREREREREREREujSFnCIiIiIiIiIiItKlKeQUERERERERERGRLk0hp4iIiIiIiIiIiHRpCjlFRERERERERESkS1PIKSIiIiIiIiIiIl2aQk4RERERERERERHp0hRyioiIiIiIiIiISJemkFNERERERERERES6NIWcIiIiIiIiIiIi0qUp5BQREREREREREZEuTSGniIiIiIiIiIiIdGkKOUVERERERERERKRLU8gpIiIiIiIiIiIiXZpCThEREREREREREenSFHKKiIiIiIiIiIhIl/b/ASFNyNBginLlAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "query = \"\"\"\n", + "SELECT id, text FROM txtai\n", + "WHERE similar(:x)\n", + "LIMIT 5000\n", + "\"\"\"\n", + "\n", + "embeddings.index(wikipedia.search(query, parameters={\"x\": \"Vikings in the middle ages\"}))\n", + "\n", + "graph = embeddings.search(\"Viking raids in Ireland\", 50, graph=True)\n", + "plot(graph)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZrJ2yanoURDb" + }, + "source": [ + "Let's learn about the Viking Age in Ireland." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "0TMUrlGWHavi", + "outputId": "30bcedbf-b08a-4847-e4a7-266d26251f21" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1. The Battle of Tara was fought between the Gaelic Irish of Meath, led by Máel Sechnaill mac Domnaill, and the Norse Vikings of Dublin, led by Amlaíb Cuarán, in 980 near the Hill of Tara in Ireland.\n", + "2. The Vikings had formed temporary alliances with certain Irish clans between 950-980 AD, which allowed them to continue their raids and plunder of the island.\n", + "3. The Battle of Tara was a devastating defeat for the Vikings, leading to the Irish regaining control of Dublin.\n", + "4. The First Viking Age in Ireland began in 795 with Viking raiders targeting Gaelic Irish coastal settlements.\n", + "5. The Norse Kingdom of Dublin, the earliest and longest-lasting Norse kingdom in Ireland, was established by Vikings in the 9th century and became the biggest slave port in Western Europe under their rule.\n" + ] + } + ], + "source": [ + "facts(graph)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EpFpcM4vUWXV" + }, + "source": [ + "Interesting, I definitely learned a few things!\n", + "\n", + "Now let's learn about the Roman Empire and it's armies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 422 + }, + "id": "M3971LbqHgFR", + "outputId": "ca17cc05-c979-4d32-9516-c2397cfe611b" + }, + "outputs": [ + { + "data": { + "image/png": "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wZoaJg7zcNxjX7JkU4jDRgXavL6HFm7g/bW1HP3IYpZvjfKHN9dw1COLueA/33LF4d+3p7+5sprjHvuSn/3kZBYtWsRvf/tbXn31VX72s581urySkhIzxKytrSWVSvGf//yHq666iueee67RaS+77DKefvppAPbdd19WrlxJNBptdJoNGzYwevTo9m84IYQQQgjRL0klpxBCiH5BRyHlCJCxOtAVC4qeRkvFscZrd7n25d5WXbR/bpN6P6ArKtVF+zNoF6rm3LZtG8XFbc+grKioMKsYNS238T4/P5+ysjIAVqxY0ew8SguhdaNWbEUz/3f51ghZHTbWJ/hmSwRdhw11CY4eE2x2GUs259rRN9YniCazQO60QaeFqmiqzZ+joSsOL+boMXkApLPfH9eiinoAdhtVwoTSqZx44olYLBYWL17c6mUddNBBZDIZfvrTn7Lnnnty2WWXcfXVVwNw/vnns2TJEr744gsAfvWrX3HDDTcwbty4Dh+rEEIIIYTo/yTkFEII0Sd0FMIFYwnllRLzFxPzDSdrsTc7nZpO4KyvwFlXhrd6FZ7KlRJ6diMdhariQ2h7+mFvUqgqPpTCde/tMvfzBx98wJw5c3j++eeprKzE7XYzYsSIds9XVVVFcXExZWVljB07lrfffpv6+noGDx4MwNixY5udJxQKmd8ft1sJa7d3ojfMPhve6koLj4uGQane4NTtPYJSmSyamjtVnsvCMaV5TH3oC/Yt8vKXH40xT2fknWvXlbHss4955ZVXALBYGv+zdf369YwcOTJ33YpCXV0dADU1NXg8HgBOOukkBg8ezE033WSeb8SIEdxxxx34/X6CwSCff/4577//PkVFRbzzzjvt/BRCCCGEEKK/kpBTCCFEr0pb3VQX7U9V8SGknEHIZnKLblpplc5a7ESCo4kESqgcdSTWWA35ZR+St2ERllSkxfOIjgsXjM3dD/2FopByBgkXjMVb2bw6cWdUX1/Prbfeym233YaiKGQyGf7617+2e74HHniA2267jcrKSmKxGOl0mlWrVuFwOHjwwQdZvXp1s/O8/fbb/PWvf+XQQw8lFE9BrIUL7iGbQkmcFpXnztib695YQ000xfzzJrGwrL7F0//zP//hT7Nm8uMf/xjIzRz98MMPze8vXLiQk046CYBPPvmEk046iUcffRSr1crdd9+Nqqr84Q9/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IcSuSEJOIYToIZ9//jmzZs1i0KBB3H333V0OOXUU4r7hPT737I2V1Z07g6IQ8w1HJ1fLc9xxx/Hhhx8CcMwxx1BZWcn5559vnvyYY45h48aNXHfddZx99tkcddRRvPXWW3z11VccdNBB/O9//+u+H2YnoSgKPp+PdDrdbRuye1PDkLMhv9/PNddcw0UXXURlZSUWi4U999yz1ct54oknOnRd0WgUl8tFPB43r/uqq67i448/5rXXXkPTNKZOnWq2iCaTSYLBIMFgLqwxvqYoihn4RCIRVFU1A0+3243H4yGTyZBMJkkkU+iaDRSFpz7f3NWbimuPKmFLOMkRD+fay/ca7Maidvw53+z5qyiE3UMJBIPEt7fT7gjjPmwamDocDkaOHElZWRkFBQWcd955JBIJZs6cyRFHHMG7777LtGnTmD59OnvttRczZszglltu4cknn2TDhg14vV4z5Fy1ahVnnnkmjz32mHn5W7ZsoaCgAI/HM6BGNXRWd38w1ppsNkt9fb25CTwvL49IJMKkSZOw2+28++675mkbLjMaO3Ys119/PV999RUPPfQQkfxSlGwGvY3qZb/Dwg9K83j+662dPs6zJw/h2S+3kMp0vAX87EmDeKtsHN7KFZx00kmsX7+eJUuWAJ2bt2mE9tXV1Tz44IOMHj2a888/n82bN5NKpXjsscfYujX3Mz366KMsXLiQkpIS/vCHP6BpGg888ACffvp9BfW1115LIBBg1qxZDB48mOnTp/PQQw8RCoU6fbt0VDwex+/3o3XThxxCCCHEQCEhpxBC9DCv12vOJdxvv/2YOXMmAM8//zwvv/wyN910E8lkkpEjR1JRUcGmTZs49NBD+fLLL7nnnnvY77CjuffCA3FZNf69dBt/fm89Z08ewol7FGDTVAZ7bPzk6SVsDrW8jObsyUM4d79haKrC9W+u4d3vaji2NI/bTtiN7ypjDPLaOOe5ZRwxOmBWf1566Ah+sc8gMrrOpS+tZPHGMItmTuF/6+vYb7iX/3yzjb+8V0bWYiflCGKL13DEEUdwyy23ADB16lRqamqYM2cOixYt4pFHHmH48OGsWJGrsPn222854ogjeOutt1i4cCG//OUvJeRsgcfjQVEUcynFQGMEjU3nch566KG88847VFbmqg7T6bQZRpx11lkcdthhuN1u7r33XhYuXMhNN93E008/TSAQ4PDDD+eee+5ht91246yzzuJPf/oTd955p7n84w9/+ANut5tQKISqqowfP54//OEP2O12vF4vixcvpq6ujl/96lcce+yxZiXhsmXLePLJJ1m8eDGBQICysjKGDx9OIBDA6XRy0UUXUV9fz69//WsOOeQQVFXlvvvuY+WWevTtP9/1x4zi84p6lm6JmJWWbpvGS+eM5+hHFvPYL/Zgt3wnGV3n188vZ33N9/NDf753IRPv/T4Y+WZLLpQc5LHy7Ol7Y1EVtoaT/PLZpS3e1g2rt8+ZPJQZBwwjns7y/IP7k6zdysUXX4yqqnzzzTfccccdTJ48mXPPPZdEIsHw4cO55ppr+O6773jmmWc444wzAMz//8UvfsFPf/pTEokEc+fObRSEHXDAAXz99dcA5v0JmJu7i4uLWbNmDel0mq+++orLLrsMgA0bNpinM6roWgsxv/jiCw4++GDefPPNFr+/s+iuD8Y6IplMmi3sbreb5cuXtxkiH3rooTz22GPmfV9lH9TuPM6A08Ip4wc1CzkVZfvy7zacNXko//x6G6lOBHRn7VfEax+OBOC///0viqKYW+Y7Om+zYWhvsVi4/PLLueyyy7DZbOZ803A4zIwZMxqd7+KLL+amm26iqqqK+++/3ww5hw4dSmFhoXmd4XCYgoICstlsty0Ea0kymSSbzWK32wfkB2RCCCFEV0nIKYQQPWTy5Mk8/vjj7L777lx++eVArkLkd7/7HeFwmKeeeoq33noLyL2JN6qb3n//febMmcPf/vY3LBYLny1fzROPLEZR4H8X7sfsj8oBqIun+c0/v+X8A4r4xT6DuP/jimbHkOeycNr4wRzx8Be4rCr/PXcC735Xw43HjuLYOYuJJDMsu/zARucZ7LFx8l6FHPbQ5xQHHDzys3Ec99iXBBwW7ny/jIq6OIt/tz9/ea8MgIwlN9Ns8ODB1NTk2ujy8vJYtWoVM2bM4Pbbb2efffZhzZo1HHzwwcyfP58DDjjA3CpbUVHB6NGje+AeGNhsNhsOh6PDy2/6q5Y2rBcWFpqB2JQpUzjvvPOIRCJceumlPPfcczz99NMEg0H+8pe/sHDhwjYvf8iQIcTjcS699FIgF1IYW+h9Ph+1tbXmNvV4PE4oFCI/P5+jjjqKK664gry8PC677DKuu+46/H4///jHPygvL+e3v/0t5eXl/OEPf+CSSy7hwAMPpKKigpKSEqZPn05hYSHXXHMNF95wZ4duB4uqMLbQxWEPfg40L8y2aapZtfbkqXuwzxAPV7yymg/X1XLcY1+Syercc1IpR+0WZFVV6y2oBW4rv9l/GEc8/AWpjM7YhV8STNdwySWXYLfbuf766ykpKckdk8XCxRdfzCGHHMJPfvKTVlvCjz32WC699FIymYz5HDeUlJSYgaVh6NChHHTQQTz66KPstddejapImz4WZs6cyT/+8Y/Wbzhygej48ePbPM3OZEc/GDvggAOYMWMGDoeD+fPn88QTT3DSSScxdepULBYLBQUFXHrppVRWVprt2r/4xS/weDw899xzPPXUUyxdupSxY8fy1FNPsWLFCn7+858TiUQIBoPEE0l+cO7P8Dps3PthOX9bvJnrjxnFbnlO8l0WXDaNHz7+FecfUMThowLMP28SF7+wgqdP24sP19VS4LLitKr87qVVbKhPMGP/Yeg6PLooN47iwGIfE4d6eOXcCbzwzTaWbg5zzVEljT7oc1hUHvvFHgz12Uhnda5/cw0Th3l4/vcn8+noWgJ+P2vWrGHhwoWccsopHHjggSSTSe644w4mTpzIySefTDQa5R//+EeLob0xwkLXdbP61Vhq5nQ6efTRR9m6dSu333479fX1FBYWUlaW+51YV1dHIBCgtraWc845h7lz5/LLX/4Su92Oy+Xis88+Y//99+/x0D6RSOBwOCTkFEIIsUuRkFMIIXqIUZVz/PHHM2XKFD755BM0TTOXx5SXl1NYWAjAqlWrANi2bRsrV64EoKqqCrfbzbBxu3Pn9IlYNYWSoINBHhsAizfmqm7K6+LsW+Rt8Rh2y3Oy52A388+bBECh2wqApijUxHLzHY2KMUNJ0MGSTSF0HdbXxPE7cr8qamIpympzb/Li6e9DN11t/qskFAqxaNEiABYtWsRuu+3GCy+8wH777cfDDz/MmjVrqKqq6vBtuatpOLOttZbKgaKlkHPbtm0UF+fmuS5atIhFixbxzDPPAPCjH/2IH/7wh2SzWQoKClq9XCMEqqio4KuvvuLmm29m06ZNPPjgg2QyGdxuN/X19eTl5eF0Os0qLIDRo0ezfv164vE4K1aswO12E41GiUQibNq0ybyOb7/9Fsi1TPt8PkaPHs2ECROYM2cOkGvdztC8mq1hgZaRZaazOg/+r4KnT9uTqmiK695YQyT5fZVaMpPFpikkMzrnPLec648Zhcuqku+y8sBPdifgtDDMZ2fxhlCbIefoPCdfbAiZgWlW0Rg6dCiXXXYZTqeT4cOHM3r0aJLJpPlas3nzZrzell9DAGbPns2ll16Kqqo89NBDrF+/vtXTut1ubr75Zq6//nrS6TShUMissoXGMz1PPvlkNE3j1VdfbfXydiXd9cHYV199xW9+8xsURWHu3Lk8++yzQO51+aabbuKUU07h2GOP5e9//zuQexw3nENbUFDAX//6VxKJBA8++CBnnXUWL730EsuWLeODDz5A9Q/h9vRXOCwqH1wwmb8tzo1pWFUV5ezn1nHb8btxbGkeDy3cwG75Tk59Jld9HHRauP/jCr6rivHTvQo5feJg7ny/jJ/tXcgvn/3GvB0+Kavny01hfvzkEiLJDE6rylFNPuj7zf7D+GxDPff8o9ysDM2d52smfPERvznlJOLxOHl5eYwdO5azzz4byL1uzJo1i/PPP59IJNKsyrykpIStW7cSDAbRdZ26ujqzKtlw7rnnUldXx4knnsgFF1zAHXfc0eg1LhwO4/P5zMf9xo0bURQFj8dDPB5n3bp1vRLaJxIJs0V/oM1zFkIIIbqq41sshBBCdMnrr7/OAQccgN/vJ5vNEggEsFgsjBgxgm3btgE0altr+P+KojDjlz/lwhdWcPScxWyoT5gVYE1P15I11XG+3hzm6EcWc/Qji9n33lzwmNF1Ak4LVk1hz0HuRudZVxNnwlAvipJbcFIXz705aq2xzud24vP5qK6upqioCJfLxbJly9hrr72w2WyMGzfOfJN3991389vf/pba2loWLFgAwPDhw1m7dm2Hb89dgdfrRdf1Hp3Z1ltaCjk//PBDjjzySDPkN5baAPzyl79kxowZXHXVVc0e1/X19QwePBiAsWPHArlFIv/4xz+47rrrCAaDTJw4kUgkgs1mw+/3s2zZMg488EAz4Dz88MOpra2lpKSEcDjM0KFDCYVCRCIRstksPp/PvN6m7aTr1q3j888/Z8aMGcyYMYOLL74YRW8eHtTGUgzz2QGYMNQDgKrAc0u2cta8ZWwJJfnZ3oWNzvOvpdu4YupI8+/GPM7TJw7mlW8rOeqRxbyxsqrd0bzfVcWYVOQ1z6/qGU455RTmzp3L9OnTWb58OfF4HJvNht1ux27PHafxM2cyGXN79PDhwwFYvXo1f/7zn3nppZc455xzGl3f+vXrKSoqAnL34+23387DDz9sBqFlZWWMHj0ai8XChAkTWLlyFUlHkIlHnMARx/2IPz40l6QjiN7Glu6ioqJd4jXi888/59e//jV/+tOfmDJlCoD5wVg6ne7wB2N77LEHDz30EHPmzGHYsGHk5eUBmONCWgu1U6kU1dXVbNy4EU3TsFqtjZ6bhoMOOZj5503i1V9PYEy+0/z6lxtzr1fldXGCzuYfftXE0ny3PaB/+dtKjt89nxF+O3XxtPl7piWTi7y8MX0i82dMMj/o22OQi/fX1ALNW9/rYrk5oslkkpKSEr744gvze7quM3v2bGbNmsVNN91kfthi3NZOpxOn00kikaCmpqbR9nnz8uvqAHj77bfN16GGIajH46G+vp5zzz2Xp556CshVTWez2V6dK5tKpchkMuYGeSGEEGJXIJWcQgjRC1588UV+9rOfcf/99zN79mx0XWfevHkdqtJ7+50F/GvGRSzdHG5x03JbqqIp5n21lXd/uy+ZrM7SzWEu/e8qbnxrLW/9ZhLramJsDidJZb5/g7YlnOSlZdv48ILJZHX43Usr276SZK4S9KOPPuKggw7inXfe4Z133mHWrFmcdNJJVFRUsHbtWkpLS/m///s/stksixcvZs2aNfj9fqZOnconn3yC2+0255S19N9dhdPpxGazUVtb26Mz23pLSyFnXV0dt9xyC7feeqt5/xqVnF9++SVPPPEES5YsadZmuWrVKhwOBw8++CCrV68Gcq3RN9xwA9lsllgsxvLly9E0DV3XURSFm2++mSuvvJKf/vSn2Gw2Pv74Yz755BPeeecdnnzySXRd54477gByAZ+x7Kklq1atoqysjEcffZRsNssnn3zCQ8/+p9np6hMZvtwYYsFv9+X9tbUAeO0W/nPWPujkQplp//im0XlueWcdt5+wG++dvy+xZJZtkST/7+MKKuoSPHXanpy4RwGxVPvPg6poiscXbeSDCyYTSWZ4Jron7733HrNmzWLdunUoikIikTCr03w+H16v17yPnnvuOR5//HGWLl1qfghzzTXXUFxcjMViYfbs2Y2ub+HChZx00kkAHH/88ey9995mCPz888/zxptv8ehL7/LA3/5FVLdwzvMrKJt6HS/NOojaWIr/99hc4qksJ835lAmuMHefsi/FI4fy4EMP8dd7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f/tb/uu//ov777+/fJzdbsc09StdRGQgMQyDcDiM0+kkGo0q4BQRETkEVXKKiEi3rFu3jr179/LFL36Ru+++mwkTJjBjxgyWL18OQLHYtldmLBZj9+7dALz11lu0trYC4HQ6ATj33HM555xzKJVKDB06tMNrvfZaW2v4rl27OO644xgzZgwTJxzL41e2/doa5nOUj32xMQ7A27EsYY+DWPa9PTtfejvBJ44fxvbWDGPDLj40qYa1b0YBuPaMOuZOamuRL5Tem0y7fmesw/+3mzTEU377P3bGmDzEQ9GyePm1bV1+/saMGQPAO++8s9/b4/E469evp1gssmHDBsaPH8+ePXsYPnw41113HV/96lcBGD9+PFOnTuXjH/84w4YN49Zbb2XBggVdXlNERKqTaZqEQiFM0yQSiVAoFCq9JBERkX5PZR8iItJtd955J/X19Xz84x9n+/btPP/88yxYsIAFCxZw9dVXd+scn/3sZ1mwYAE33HBDef/KQqGAzfZeFaZlvRc6GoZBY2Mjr23bztx7XmTuPS8ya/H6947d59wH7qW5dnuEr581jrXbI7z4doKFp4zhqe0Rar12Pjy5ljOXvcBX/3frfvfbJ++kZFkc6PXmNCeNCQLwL2OCbG1OA1Dk4Invb731FqNGjQKgvr6eCRMmcNddd/HBD36Qm2++GafTyYYNG3jf+95XPqaxsRGv18v3v/99br311nJIbBgG8XicUqlEPB7H6/WWr1MsFimVSgddX0REqo/NZiMcDmMYhgJOERGRw6BKThER6TbLsvj617/OsmXL+NnPfkZDQwM///nPKZVKPPvss9x3332HPMdLL73EihUr2LhxY3l4wtNPP821117LunXr2LNnz0H3iUQi/PHxNfztP66gWLLYtCvBNf+z9ZDXWrs9woxj/KzdHmVvMs/XTq/jtb0pDANaU3kev/IE1jUcXK3ZlR+ueYsV5x/PjWeN55XdCZ56M8KE2pG4nQ6CwSClUqn8r7210OVy8cQTT/DEE08A8J3vfIeVK1eSy+VYsWIF3/3ud/niF7/Is88+S2NjIwsWLGD06NHceOONACxdupTnn3+evXv3smLFCux2e7mCtr6+ng0bNhzWxyAiIv2T3W4nFApRKpWIRqN6AUtEROQwGDNnzjy4TEVERKSfyblreHXONyq9jE7N+sdP8ObjmKaJzWYr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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "embeddings.index(wikipedia.search(query, parameters={\"x\": \"Roman Empire\"}))\n", + "\n", + "graph = embeddings.search(\"Roman soldiers\", 50, graph=True)\n", + "plot(graph)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "k26lJ7BNHpNG", + "outputId": "80e1ac67-9b2b-4736-8fa6-d26676b1e9e5" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1. Vexillarius is a term referring to a type of Roman soldier.\n", + "2. A numerus was a unit of the Roman army.\n", + "3. Roman legionaries were professional heavy infantrymen who conquered and defended territories during the late Republic and Principate eras.\n", + "4. Comitatenses and later Palatini were units of the field armies of the late Roman Empire, replacing the legionaries.\n", + "5. A hastiliarius was a weapons instructor in the Roman Empire who trained troops in standard weapons and fighting techniques.\n" + ] + } + ], + "source": [ + "facts(graph)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DRfMGc3oVDPg" + }, + "source": [ + "# Explore ArXiv\n", + "\n", + "There is also a full ArXiv txtai index available on the Hugging Face Hub. This has the full metadata set (titles + abstracts). Let's take a look at this." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "1Ysf5NchBc39" + }, + "outputs": [], + "source": [ + "# Load dataset\n", + "arxiv = Embeddings()\n", + "arxiv.load(provider=\"huggingface-hub\", container=\"neuml/txtai-arxiv\")\n", + "\n", + "query = \"\"\"\n", + "SELECT id, text FROM txtai\n", + "WHERE similar(:x)\n", + "LIMIT 1000\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 422 + }, + "id": "4GXihzyWErAJ", + "outputId": "088b06e6-f4a3-45e7-9caa-8f23205e91b2" + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "embeddings.index(arxiv.search(query, parameters={\"x\": \"Retrieval-Augmented Generation\"}))\n", + "\n", + "graph = embeddings.search(\"Best ways to populate context\", 50, graph=True)\n", + "plot(graph)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "NfhhxrqDFAwj", + "outputId": "c567505e-5a35-4b8d-dd16-5077bb9e3c58" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1. Context Tuning for Retrieval Augmented Generation (RAG) improves tool retrieval and plan generation by employing a smart context retrieval system to fetch relevant information.\n", + "2. Synthetic Data Generation in Low-Resource Settings via Fine-Tuning of Large Language Models aims to improve downstream performance of smaller models by generating synthetic data.\n", + "3. Query Expansion by Prompting Large Language Models leverages the generative abilities of LLMs for query expansion using prompts like zero-shot, few-shot, and Chain-of-Thought (CoT).\n", + "4. In-Context Retrieval-Augmented Language Models prepend grounding documents to the input without modifying the LM architecture, providing significant LM gains across model sizes and diverse corpora.\n", + "5. Generate rather than Retrieve: Large Language Models are Strong Context Generators proposes a generate-then-read (GenRead) method that prompts a large language model to generate context-based documents for answering questions.\n" + ] + } + ], + "source": [ + "facts(graph)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "bYSqWqaJVUUU" + }, + "source": [ + "This wouldn't be an article about RAG without exploring the literature available on RAG! Definitely some interesting articles here.\n", + "\n", + "Last we'll run my \"go-to\" ArXiv demo query on the search for ET 👽." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 422 + }, + "id": "HLo9hlSoEW8m", + "outputId": "29e47454-c232-42e8-db9e-3f04f41e8224" + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABTkAAAJ8CAYAAAA1RaUxAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8WgzjOAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdd5hcdfXH8fe90/vMlmx6Nm3TCyV0CE2UqoCA1IAYBBUsICgIiIIggvCTEoqggCgIGJAuEQm9hEAq6WWzSTbJlul95v7+mNybmS2pW5Pzep59kp2duXOn7t7PnO85yuTJkzWEEEIIIYQQQgghhBCil1K7eweEEEIIIYQQQgghhBBiT0jIKYQQQgghhBBCCCGE6NUk5BRCCCGEEEIIIYQQQvRqEnIKIYQQQgghhBBCCCF6NQk5hRBCCCGEEEIIIYQQvZqEnEIIIYQQQgghhBBCiF5NQk4hhBBCCCGEEEIIIUSvJiGnEEIIIYQQQgghhBCiV5OQUwghhBBCCCGEEEII0atJyCmEEEIIIYQQQgghhOjVJOQUQgghhBBCCCGEEEL0ahJyCiGEEEIIIYQQQgghejUJOYUQQgghhBBCCCGEEL2ahJxCCCGEEEIIIYQQQoheTUJOIYQQQgghhBBCCCFEryYhpxBCCCGEEEIIIYQQoleTkFMIIYQQQgghhBBCCNGrScgphBBCCCGEEEIIIYTo1STkFEIIIYQQQgghhBBC9GoScgohhBBCCCGEEEIIIXo1CTmFEEIIIYQQQgghhBC9moScQgghhBBCCCGEEEKIXk1CTiGEEEIIIYQQQgghRK8mIacQQgghhBBCCCGEEKJXk5BTCCGEEEIIIYQQQgjRq0nIKYQQQgghhBBCCCGE6NUk5BRCCCGEEEIIIYQQQvRqEnIKIYQQQgghhBBCCCF6NQk5hRBCCCGEEEIIIYQQvZqEnEIIIYQQQgghhBBCiF5NQk4hhBBCCCGEEEIIIUSvJiGnEEIIIYQQQgghhBCiV5OQUwghhBBCCCGEEEII0atJyCmEEEIIIYQQQgghhOjVJOQUQgghhBBCCCGEEEL0ahJyCiGEEEIIIYQQQgghejUJOYUQQgghhBBCCCGEEL2ahJxCCCGEEEIIIYQQQoheTUJOIYQQQgghhBBCCCFEryYhpxBCCCGEEEIIIYQQoleTkFMIIYQQQgghhBBCCNGrScgphBBCCCGEEEIIIYTo1czdvQNCCCGEEELsSzQUMnY/OYsdTTGjaFlMmSSWZBAFrbt3TwghhBCiV5KQUwghhBBCiE6koRCtqCFSNpKEbzAJ70DyZlur86nZFI5wHY5QLZ6m5bgblknoKYQQQgixk5TJkyfLX05CCCGEEEJ0sKzFRdOAg2gcfDgZRwDyOVBUUJT2L6RpoOVBNWFJNFNe+z5l6z/DnIl13Y4LIYQQQvRCEnIKIYQQQgjRgTQUtlRPZdPIE9EUFVC2H2y2uyGtsDUtT9Xy16lcM1sqO4UQQggh2iEhpxBCCCGEEB0k6erDugnnkvAO2r1gsz2ahiO8jkEL/oE9trnjtiuEEEIIsZeQkFMIIYQQQogOEKyayLqJ56OhgGrq+CvI51DQGDT/afyb5nf89oUQQgghejG1u3dACCGEEEKI3q6p/xRqJ11YWJ7eGQEngGpCU1RqJ11IU/8DO+c6hBBCCCF6KQk5hRBCCCGE2APBqonUjT+HQu/NTv7zemuPz7rx5xCsmti51yWEEEII0YtIyCmEEEIIIcRuSrr6sG7i+YDWsT04t2fr9aybeD5JZ2XXXKcQQgghRA8nIacQQgghhBC7QUNh3YRzCz04O7uCsyVFLb1+IYQQQoh9nIScQgghhBBC7IYt1VMLU9Q7qwfnjqgmEr7BNFRP7Z7rF0IIIYToQSTkFEIIIYQQYhdlLS42jTyx65aot0dRqB95IlmLs3v3QwghhBCim0nIKYQQQgghxC5qGnBQYZJ6D6ApKk0DDuru3RBCCCGE6FY94y8zIYQQQgghegkNhcbBh0OP6YWp0Dj4COnNKYQQQoh9moScQgghhBBC7IJoRQ0ZR6D7l6rrFIWMI0C0oqa790QIIYQQottIyCmEEEIIIcQuiJSNhHyuu3ejVD5HtGxkd++FEEIIIUS3kZBTCCGEEELsU9xuN0899RQffPABw4cPL/lZv379+OSTT4zTq6ureeyxx/jrX//KQQcV+l4efvhhvPuDKcy+fH8eP2tMq4LOPm4Ls6bvx38v24+Xpk3Ebi78yf3W9P1ovPlITh5dXnJ+l9VE/a+OKDn9tq8P4z/fm8x/L9sPm7n1n+yPnDkan93MKWPK+fAHBzD7iincfNk5xs/ff/99Hn30UR599FFGjBhhnO5wOHj77bc58sgjAZg2bRpjx47d1btQCCGEEKLHkZBTCCGEEELsU5LJJFdddRWzZs1q9bNp06bx5ZdfGt//6Ec/4pZbbuGHP/whV1xxBRoKb6xXOOqhuUx9aC4Ahw32lWzjvMl9+ee8TRz3yBd8ui7M6eMqAbjomUX83wd1ra7zysMGMnd9xPj+zPGV1EfTnPDnLznukS9IZfMl5x8SsJPLa4SSWeZtiHLkQ3OZ+vBcAmVljNkaWK5du5bp06czffp0VqxYYVz23HPPZfHixcb3L774Iueee+7O3nVCCCGEED2WhJxCCCGEEGKfks1maW5ubnV6//790TSN+vp647TKykpqa2uJxWKEQiFcVYNJKdaSy61pTpZ8v2RzDJ/DDEDAYaYhngFgYyTd6jo9NhMT+rr5eF3YOO3UsRUM9tv572X7ceNx1a0uc+qYCt5eUdj/daEUubwGQDqvkDa7ARg4cCCPPfYY119/PVZrYX9dLhcjR45kwYIFxrZCoRCVlZWoqhwWCCGEEKJ3k79mhBBCCCGEAC655BKefPLJktOKw79oNIqnrFCVOe2Aviz82cGUOy1siZWGl5/WhfnOpCrm/eQgDhzo5b8rmtq9zqsOH8QDH5VWd1a5rdRH0hz3yBeM7ePi4EHekp+PrnSyqilRctqBAz30cVtZuGodAKeddhqXXnopDQ0NnHNOYRn7eeedxzPPPNNqH4LBIH379m13H4UQQgghegMJOYUQQgghxD5v4MCBAGzcuLHk9Hx+21Jxt9tNMBoH4InP6xn/x0+oDSaN5ei6a44awp8+WMekez/l34u3cNlBA9q8Tq/NxKR+bj5cGyo5PZjI8r+VhUrN/61sZlyVa7v7PsBr44+njOTify5GUwsVpKFQYZuzZs2ipqYGt9tNTU0N8+bN2+62hBBCCCF6K3N374AQQgghhBDdraamhmHDhnH//fczcuRIBg0axOWXX05DQwMDBw6kqakJn89HMNiM1aSQzhWWiIeTWeKZ0p6ZCtAQKyxRb4xnqHLb2rzO0X1cDPDZeO2SSQwvd3DK6HIW1Mf4YG2Iyf3czF0fYVJ/N8/N31xyuaUNcYaVOfh8fQS31cTfzxvHFTOXsiWWwZ/PYrfbSafT5PN59ttvP9atW0d1dTV9+vTh/vvvZ9CgQRx11FGsWLGCjRs3EggE2LRpU8ffqWKvoqGQsfvJWexoihlFy2LKJLEkgyho3b17QgghhIScQgghxN5ODkyFaO2+++5j1KhRVFdX8/zzz/Pyyy/z9ttvA3DLLbfw5JNPkk6neeCBB7jlllswmUzMmDEDUybJJQf24+xJVSjA8oYEryxpAGDG6aO4YuZSHvyojr+eM5afHTWYbE7j/GcWAfDnb49m6tAA3xxbwbgqN3fOXsvhD34OwE3HD+XzujC1wSSPf7aBv5w1lgv278uyLXHeWRUs2feXFzdw3dFDeG7BZn58xCCGBhz86Zs1APwlXEOycSO//vWvSSQShMNhbrzxRqLRKNOmTQPg+9//PosXL2bjxo34fD62bNlCLpfrgntd9CYaCtGKGiJlI0n4BpPwDiRvbh3Yq9kUjnAdjlAtnqbluBuWye8WIYQQ3UKZPHmy/AYSQggh9iJyYCpE59FQWHTcbW2+prrSo2eO5ppXVxBKZo3T1GyKcf+9HmUXtjNt2jTmzJnDokWLOn4nRa+UtbhoGnAQjYMPJ+MIQD4HigrKdp5ZmgZaHlQTlkQz5bXvU7b+M8yZWNftuBBCiH2ehJxCCCHEXkIOTIXoGiun/IBYYNj2X1tdTdNwNa9i+GcPdveeiF5KQ2FL9VQ2jTwRTVEBZfee45pW2JqWp2r561SumS0foAkhhOgSslxdCCGE6OXaPDAFUE07vrCigFI4X8bup77mZDaNPFEOTIXYDkeolpi/2njt9AhaHmeotrv3QvRSSVcf1k04l4R30J6H94oCKGiKSn3NKYT6TmLQgn9gj23e4UWFEEKIPSHT1YUQQoheLOnqw4pDrqK+5pTCVOUdVW5uj6KAoqKpZuprTmHFIVeRdPXp2B0WYi/gaVq+cx8idCXVhLtpeXfvheiFglUTWX7Y1SQ8Azq+OllRSHgGsPywqwlWTezYbQshhBAtSMgphBBC9FJyYCpE93A3LMOSaN66LLcH0DQsiWbcDcu6e09EL9PUfwq1ky4srALorOBeNaEpKrWTLqSp/4Gdcx1CCCEEEnIKIYQQvZIcmArRfRQ0yms/gB7TzkGjvPZ9aS8hdkmwaiJ148+h0Huzkw8Lt7ZSqRt/jnxwJoQQotNIyCmEEEL0MnJgKkT3K1v/KYqW7+7dAEDR8pSt/7S7d0P0IklXH9ZNPB/Qum6A1tbrWTfxfJLOyq65TiGEEPsUCTmFEEKIXkQOTIXoGcyZGFXLX+/+JeuaRt/lr2POxLt3P0SvoaGwbsK5aF3xQVlLilp6/UIIIUQHkpBTCCGE6CXkwFSInqVyzWwc4XWQz3XL9StaDnd0PRVrZnfL9YveaUv11MIU9e4anqWaSPgG01A9tXuuXwghxF5LQk4hhBCil5ADUyF6FgWNQQv+UeiF2dVL1zUNNI1xa1+lLODHZOph095Fj5S1uNg08sSuWwnQHkWhfuSJZC3O7t0PIYQQexUJOYUQQoheQA5MheiZ7LHNDJr/dOGbrlq6vvV6xq1+Ga2xFoBAIIDdbu+a6xe9VtOAgwoD63oATVFpGnBQd++GEEKIvUjP+A0nhBBCiO2SA1Mhei7/pvkMXPhP6IqKTi0PaAxc+Az+zfNxu90kEgmSySQejwev14vS3R+GiB5JQ6Fx8OHQY1qOKDQOPkJaoAghhOgwPeNoSQghhBDtkgNTIXq+sg2fMXjeU4WJ653VozOfQ9HyDJ73FGUb5hAOh41wM5/PEwqFsFgsBAIBzGZz5+yD6LWiFTVkHIHuXxGgUxQyjgDRipru3hMhhBB7CQk5hRBCiB5ODkyF6B38m+Yz8sO7cUTWd/zSdU3DEa5j5Ad34d803zg5Go0SjUZxuVzYbDaam5vJ5/P4/X6cTmkrIbaJlI3stiFZ7crniJaN7O69EEIIsZdQJk+e3EXNg4QQQoh9x7hx47j22mvJZrNs3ryZG2+8kWw2y1133YXf78dqtXLvvfcyd+5cysvL+e1vf4vD4eC5557jtdde46ijjuJ73/se2WyWORuTXP5OtGTgkNOi8uQ5Y6l0W3l5cQN3vVvoyzd1mJ8bjq1GVRTu/3AdLy5qAMBlNbHy2kO59PmveHVJo7Gd1y6ZxMJNMa59bQUA50zsw/cOHoCqwA1vrOTj2nDJ7br8kAEs2xLn7eUNHJlfwu/OOACLxcJbb73Fc889B4CiKDz//PP885//5Nlnn6WsrIw77rgDRVGIxWJcd911KIrC9ddfz0033dSpj4MQ3UFDoaF6KvUjT9zaZkLZvQ8pNK2wNS1P3+WvU7FmdmHIURtsNhsej4dMJkM4HMbhcOB0OslkMkQiEfL5Lh6MJDqE2+1mxowZDBs2jIsuuoiVK1cCcOGFF3LssccSj8e5+eabaWhooLq6mhtvvBGTycSDDz7Ip59+amznhhtuwDx0f8785+qS5+K3J1TymxOGEUnlOPj+OQCMrHDwzHnjGV3ppM9v3yeWzlEdsPPYWWMAKHNYWNWU4MynFnD0MD+3fn042bzGjf9ZxXurgwBcdfhAThlTgUlVuPS5r1jTnCy5Xbd9fRh/mbORFQ1xXM2r+GH5Kq699lqOPfZYbDYb999/PwB2ux2z2cy5554LwJVXXsm4ceMwmUz88Ic/JBAIcMEFF3D33Xd3zgMghBCiV5F1LEIIIUQn2LRpE5dddhmpVIorr7ySo48+mlmzZvGLX/yCbDZLv379uPnmm7n88su5+OKLeeKJJ5gzZw6PP/44s2bNYtmyZVxyySXkcjmuf/g5Dhho4vMNUWP7lx7Un9eXNvLYZxt57ZJJ/P3LepriWX525GBO/ss8MrnSIOTKwwYyd32k5LTDhvhKvu/nsXLa2Eq+9ugX7d6u08ZUcNLH80BRue7C07j2h+cTi8VKzvONb3yDjRs3Gt+fdNJJvPnmm7zwwgtMnz6dY445hjfeeINQKER1dTVr1qzZ1btXiB5NQaNyzTsE1n9G04ApNA4+olCNnc+Bom4/8NS29vVUTViSQSpq3yew/lPMmfh2rzOVSpHP5/F6vfj9fkKhEJlMBo/HQyAQIBKJkE6nO/iWis6WTCa56qqr+MlPfmKcVl5ezhFHHMEll1zCuHHjmD59Orfffjs/+tGPuOWWW2hsbOT+++83Qs5+/fpRUVlJo8XZ6rn39spmXrr3U96/4gDjtLpQimMensvMaRON09Y0JznukcLvhpuOH8qapgQAt359+NbfOXlevmQSxz3yBfsP8DDYb+eEP3/Z5m1yWlRGVTpZ0ZgARSHlG8jxxw+jvr4eKDyXp0+fDsCpp55Kv379ADj++ONpaGjg8ssvN7a1adMmKioqcLvdRKPR1lcmhBBinyLL1YUQQohO0NDQQCqVAiCTyRhVVNlsFgCXy8WKFYXqyfHjx/Ppp5+Sy+VYvHgxI0aMoL6+nlwuh4ZCUrXTsgbrsME+/rOsCYC3VjRx6GAfhw7xkcjk+Pe0ibxw4QSq3FYAPDYTE/q6+XhdaVXmlYcN5IGP6ozvvzGqnFQuz5uXTuaJs8fisppKzj+hr4tVWw9sh5Y7UG1ObrvtNh544AGqq6sBUFWVr33ta7z11lvG5VavXo3H4wHA6/USDAYB+OSTTzj66KN39a4VotcwZ2L0WfMOo9+9jaGfP0LF2ndxNa9CzabaPL+aTeFqXkXl2ncZ+vkjjH73NirXvLPDgFOXyWQIBoMoioLf7yefz9Pc3Ewmk8Hn8+FyuTry5pXQUEjbAyQ8/Yh7B5Hw9CNtD0jv3j2UzWZpbm4uOa1fv36sWrUKgCVLlrDffvsBUFlZSW1tLbFYjFAohN/vB+Diiy/mL8/OLATsLTTFs60+FEtk8oRT7S9rP3VMBS8u2gKAWVUIJbPEM3nMqkK508JpYytwWFTemr4f/3faSNQWT4HjRpSVrBI454DBvDH7I7Q2WjwU/z6ZOnUq/fr149FHH+Wyyy4zzjN37lwOO+ywdvdXCCHEvkMqOYUQQohO1K9fPw499FD+/Oc/G6c99thjDB48mBtvvBEAs9lsHNxFIhG8Xq9x3lH7HUylx8EXG0orVAIOi3EQGk5mKXNasJhUhpc7OezBORw/ooybjx/KD15cylWHD+KBj+o4fmSZcfkjh/qZtzFKNL3tQLaP20qF08LXH/uSyw8ZwA8PHcids9caPx9duS3krHJbmdDfy+k/uotBfhtXX301V155JSeddBJvvfVWydCThQsXcuWVV3LyyScTDoe55557AKirq+OEE07YsztYiF5AQcPTsBRPw1KgEAhm7H5yZjuaakbJZzFlk1iSwXaXo++sXC5Hc3MzPp8Pv99POBw2lq+7XC4sFguRSIRcbs96M2ooRCtqiJSNJOEbTMI7kLzZ1up8ajaFI1yHI1SLp2k57oZle3wb93V1dXWMHTsWi8XClClT8PkKVfmqui3EjEajeL1eI9iuawh2yHWP6eNkQzhl/P5J5fIM8tlI5fKMr3IRcJipclvJ5jW+9ugX3HHicM6a0Idn5282tjGq0snqrb9LVAXOmtiHax/8iEu+c0bJdbndbsrLy1m9ejUAZWVlLF++nOnTp3PHHXcwYcIEFixYwPr165k4cSJCCCGEVHIKIYQQncTlcnHrrbdy0003GRWcAJdeeikXXnghP/7xj4FCpY6ydQmhx+MhHC5UuPTp04frfnolF/9zcattB5MZvLZCpaXXbqYpniGYyPDh2iCZnMZ/VzQxtsqF12ZiUj83H64NlVz+qsNLqzgBQoks/1tVqBh6e0Uz46rar/oKJrLMXR8hlMqxcuVKAoGAUcX55ptvlpx32rRpPP3005x11lm88847nHnmmTt1/wmxt1LQsCabcUQ34gyvwxHdiDXZ3GHhn6ZpBINBo4LTZrORSCSMKs9AIIDN1jqQ3BlZi4vN1cew5KgbWH3AZTQMOYpYYFibASdA3mwjFhhGw5CjWH3AZSw56gY2Vx9N1tJ5VaV7u2AwyHPPPceMGTM4/PDDjRCwuO+q2+0mHA5zySWX8MQTT6ApHVPb8u0JfXh+wbbA8qcvL+exs8Zw/zdHsaA+Rn0kTTCR5X8rt/0uGbud3yUX7NeX5+ZvJq+YWv3smGOOYfbs2cb3kUiEzz77DIDPPvuM4cOHd8htEkIIsfeQkFMIIYToBCaTiTvuuIOHH36YtWu3VUPqFY7xeJx4vLAEddGiRRx44IGYTCbGjBnDypUrcTqd3HHHHdx094NsiWVabf/DtSGjMvP4rUv/PquLMLqycDA5ub+H1U0JRvdxMcBn47VLJnH+5CpuPn4og/12hpc7ePa88dxx4gjOGF/JKWPK+WBtkMn9PFsv7zaqNnVLG+IMK3MAsLwxQbnTgslio0+fPkSjUcrLy6moqOBPf/oTF154IWeffTbjxo0DMJaoB4NB3G43AAMHDjQOzoUQHS8cDpNMJvF6vTidTmPpcyqVwuv14vF4jA9YdkRDYXP10Xx19E3U15xExu4v/EA17XiokqIYg9Mydj/1NSfz1dE3sbn6aFnOvpteeeUVvve97/H222/z+eefA4U2KQMHDsTpdOLz+QgGg/Tv35/rr7+e3//yxxxe7ePSKf326HpPHVPBS1uXqgPMXR/hhD9/yRUzl1IfSRFN5/hwbYhJ/Qrv85P6u1ndVDp0aFlDnKFbf5eM6ePiwv378ec7b2LQoEFce+21xvmOP/54/vOf/xjfz5s3j1GjRgEwatQo6uoKH9QNGDBAfpcIIYQAZLq6EEII0SlOPvlkrrnmGqPv5nPPPcc777zDAw88ABSWFd5///188cUXVFRUGNPVn3/+eV555RWmT5/Ot7/9bdasryfuG8Qtb63m3dVBZpw+iitmLsVlNfHUOWMpd1l49atGY1n5FYcM4OxJVWiaxveeX1ISVN50/FA+rwuXTFefOszPyaMrjOnqt359GIcO8ZHO5rngmcU0xksD1te/O4kTH58HwOnjKvnlJBULOe68804WLVpknO/UU0/F6XTy7LPP0rdvX377298ChaW0v/zlL2lububqq6/mX//6lxycCtHJHA4HbrebZDJJJFIYQGaz2XC73WiaRjgcLqk2bynp6sO6CeeS8A7avSnx7dE0HOF1DFrwD+yxzTs+/z7qvvvuY9SoUWzcuJHnn3+el19+mTvuuINAIMDGjRu54447SCaTDBs2jBtuuAGTycSMGTP45JNPjG1UVI/m8t/8H2c/vZAqt5XLDxnALbNWM3WYn+uPqeagQV4+XRfm4n8uJpHN8+x54zlgoIcvN0S5a/Za3ljWxOhKJ3ecOJxvPbnA2O61U4dwQk0ZiUyOq/69jNVNSVQFZpw+mhEVDpriGc7/xyLSRX0/XVYTT54zljOf2rad0bNv5bnH7uf8888HCpWoDz/8sPE9FCat/+Y3v6GsrIw1a9Zw6623AnD77bfzu9/9znhuCyGE2HdJyCmEEEL0YGl7gCVTf9Xdu2G44pABLN0S5+2tSxFHz74Va7J5B5dqzW63c8MNNxh9SYUQnctms+HxeMhkMoTDYTRNQ1VVvF4vZrOZWCxGIpFodblg1UTWTTy/UHGptl5SvMfyORQ0Bs1/Gv+m+R2/fQEUKnEXHXdbu20FutrvvjGcxz/bwIrGBGo2xbj/Xr9bNb1VVVVceOGF3HXXXR2+j0IIIXofCTmFEEKIHqynHZgWM+XSHPnFXWQzGbLZLNlslkwm0+aEXCFE97NYLHi9XvL5PKFQyOjh6HK5cDqdpNNpIwAFaOo/hbrxZxcu3MZk7g6jFfZj4MJnKdswp/OuZx+3csoPiAWGdWw17p7SNFzNqxj+2YPdvSdCCCH2AtKTUwghhOjBFDQc4TroacGhpuGJ14OmYTKZcDgc+Hw+KioqKCsrw+v14nA4sFgsO93zTwjRuTKZjDF8yO/3YzIVKjNjsRjBYBCz2UxZWRkWi4Vg1UTqxp8DKJ0bcMLW7SvUjT+HYJVMye4sjlCtESj3GFoeZ6i2u/dCCCHEXkJCTiGEEKKH64kHpoqWxxvfgKIomEwmVFUln8+TyWTI5XIoioLT6cTv91NRUUEgEMDj8RjBpxCie+RyOYLBIPl8Hr/fb7weM5kMTU1NZLNZLFXDWDfxAkDruqq/rdezbuL5JJ2VXXOd+xhP0/LOaTmwJ1QT7qbl3b0XQggh9hIScgohhBA9XE88MNVUE7b6r0ilUsaSVyhMlbdYLFitVjRNI5VKkUgkyGQymEwmXC5Xq+DTbrcbU+eFEJ1PX66ezWbx+XzYbIV2GJqmEQyFWTDoJFDo/ArOlhQVDYV1E86VqeudwN2wDEuiueesDNA0LIlm3A3LuntPhBBC7CXkiEIIIYTo4fQD04zd3zN6qWkalmQQe/0iohQOls1mM1arFZvNhtlsRtM0NE3DbDYbS2Kz2SzJZNKY4mw2mzGbzdhsNhRFQdM0o7en3t8zl8t1280UYm+maRqhUAi3243X6yUWixGPx9lSPZWYZ0D3vdeoJhK+wTRUT6VyzTvdsw97KQWN8toPqK85CXpEiKxRXvs+Cj0kdBVCCNHrScgphBBC9CAaChm7n5zFjqaYUbQspkyyxx+Y6sFkPB5HVVVsNhtWq9VYCpvNZtE0DavVisPhMALNdDpNLBYzAlGLxYLFYsFut5cEn5mi4UYSfArRcaLRKPl8HpfLRc7qZtPIE7v/wxRFoX7kiQTWf4o5E+/efdnLlK3/lE0jv4HW1VW6bVC0PGXrP+3u3RBCCLEXkZBTCCGE6EYaCtGKGiJlI0n4BpPwDmxzkrqSTdEzAs7Cgemg4CLSW0PIlvL5PIlEgkQigaIoRoWnxWJBVVVyuRzZbBZFUXA4HLhcLjRNI5PJkE6nSSQShb6AFktJtafT6TS2X1ztmc1mS5bMCyF2TTweJ5fL0TTi+B4RfgFoikrTgIPoI9WcHcqciVG1/HXqa07p3jBb0+i7/HUJsYUQQnQoCTmFEEKIbpC1uGgacBCNgw8n4whAPlfof9fOQadmthX6qGldOAikzR3RGLTmLdymPJSVEY/HSSQS2zl7oS9nKpUCwGKxGFWeJpOJfD5v9PXUe3YqimIMMUqn00YAoyiKUe3ZVvBZXO2ZyWTaDGCFEG1LptLUVuxPT/kwBRQaBx9B5ZrZspy5g1WumU2o7yQSngHd0+85n8MRrqNizeyuv24hhBB7NQk5hRBCiC6kobCleiqbRp64tWJqa6CwMwea3b2EdOuBqW/5f2lSwOVy4XK5sNvtxGIx0un0DjeRyWTIZDJAaR9Pm81mVHPqPTstFgtutxtFUcjlckbomUwmjcpNRVFKKj4dDgeqWqhE0ytGiys+JfgUom3RippC39+eQlHIOAJEK2rwNCzt7r3ZqyhoDFrwD5YfdjWalu/aAVNa3rh+Ca+FEEJ0NGXy5Mny20UIIYToAklXH9ZNOJeEd1D3B5a7SsujaHlGfnAX9vgW42STyYTb7cZqtZJOp4lGo7vVM7NlH09FUYxQU6/ytFqtxhR2vZ+nHpoWh5eqqpZUfJrN5pLgs2XFpxDt9cK1JIO9Lohxu93MmDGDYcOGcdFFF7Fy5UqcTif33HMPZrOZbDbLr3/9azZu3Eh1dTU33ngjJpOJ3/57LjMjA/nG6EquP7YaDVjZmODS579qNYz7qsMHcsqYCkyqwqXPfcWa5iS3fn0YxwwPAHDtqyv4YG2IUZVOHjpjNGZV4eb/rOLtlc1MO6AvvzymmvXhFBtCKS58dnGr2/DImaP5+asrmNzXyVPfHsnGNcvJ5XJcfvnlAAwfPpyf/exnWCwW3nrrLZ577jmuueYannzySTZv3tzZd/FeI1g1kdpJFwJK1/xO0jRAY/C8p/Bvmt/51yeEEGKfIyGnEEII0QWCVRNZN/F8NJTuWR64J3biwNRqteJ2u1FVlWQyaQwT2h16H0/9S+/jmUqlyGQyRvWmvuS9eIhRcaVoMZPJZASeevipDzbSKz6Lw0+xd9vZXrhqNoUjXIcjVIunaTnuhmU9PvQ0m814PB5+8pOf8OSTT7Jy5UqsVis+n48tW7Zw6KGHctRRR/H73/+eu+66iz/96U80NjZy11+f5/BHF2Exq2Ryhdv4+FljeOzTDXywNmRsf/8BHs6bXMU1r64wTgs4zLx88SSOmPE5A302/nzmGL7x+Jc8f8F4fvH6SjZF07x6ySSOemgu0w7oi8tq4sGP1re5/0MCdn5x9BCumLmUqUN9fGuohb9cN63kPPfeey833HADsVjMOK2mpoavf/3r3HfffR15d+71mvpPoW782YVvOrOiUytU3w9c+CxlG+Z03vUIIYTYp8lydSGEEKKTddlBZGcoOjDdXuVNOp2mqakJh8OB0+nEZrMRi8VIJpO7fpXt9PHUe3Dm83ljKrs+oMhisbQ5xEgPLnO5nBGU6kwmU6vhRsUT3YurPWWie+fpyirKXe2FmzfbiAWGEfNX0zD0GCyJZspr36ds/WeYM7E2L9Pdstkszc3NJael02m2bClUYBdXPldWVlJbW4uGQkNapdxlpTFe+iHBmubS1/BpYytwWFTemr4fizdF+enLy4mkcjQnMphVhYDDTEO80Lqin9fGisZCz97mRJZypwWAKw4ZyNkTq5jxUR3Pzi+tvDx1TAVvr9i6/4rCqQeO4IjHHmPWrFn84x//YMCAAZjNZm677TYsFgt/+MMfWLNmDcuWLePaa6/tgHtw31K24TPUXKrwIZymdc6HcPlcYYn6/KelglMIIUSnkpBTCCGE6ETBqonUjT+n8E0vW6KuaLnCoKFdODBNJBIkk0lcLhdutxuHw0E0Gt2jZeFt9fG0Wq14vV4j0EylUsRiMVRVNao82xpipC9/B4zgs1hxtafFYsFut5cEn8XVnhJ87p7uqKLc4164SuF8Gbuf+pqT2TTyRKqWv97rhuKYzWYuv/xyfvOb3wAYbRwydj+hVJ4yp5nGeIZpB/Tl51OHsLIxwZZYaa/dKreVbF7ja49+wR0nDuesCX14dv5m5m2MsuSaQ7CbVU7567zC9ove80LJLGVOMy8uauDJufW4LCbemr4fs1cHqY9su47RlU4+2lo5Oqcuwpi7P2H0u7fzwB038+WXX2KxWKipqeGMM86gqqqKq6++miuvvNK4faqqGq9xsXP8m+Zj/7C+c9qpaBqOcB2DFvyjpNWJEEII0Rkk5BRCCCE6SdLVh3UTzwe0XljBqeGO1TN2zSuo0fXsSs2apmlEo1Ej7PT7/UYIuafBoB4wxuNxVFU1BhfpA4r0MLNllae+nF4fYqRXeabT6ZJl9fr2iytQW1Z7Fk90bznYSMKV9nVXFWWH9sJVFEBBU1Tqa04h1HdSIbyJ9ew+kIqioCgKN910Ey+88AIbNmzAYilUVdpsNvIuHz67maZ4oVXDE5/X88Tn9dz3zRpOH1dZUm0ZTGT5rC4MwNsrmjm82seoSidTBnqp+cNH9PfaeOKcsRz3yBfki15b+vZDycJ1RNM5Zq9qZkwfV0nIWSyWLrxfJDEze/ZsampqmD9/PosXLyYajRKNRgkEAh1/h+2D7LHNjPj4TzRUT6W++MOA3XnNbG1xomh5+i5/nYpe9mGAEEKI3ktCTiGEEKITaCism3BuoQdnbwk4iw5M+698k1GRhaSSCexOpxEI7opsNksoFDICxkAgQCKRIB6Pd8iU83w+TzKZJJlMlvTx1Jet62FmKpUiHo8bvTz10NPhcBj72d4QI6BVn09FUUp6e7YMPlsONtrXJ7p3ZxVlSS/cjq6kVhQSngEsP+zq3V6Gq4eP+ldHnma32ykrKyMcLgSSF1xwAU1NTcyZM8cIBkOhEKNGjWJdykbAUajitJoU0lt7coaTWeKZ0uD+w7UhDhzo4V8LtzCpv5vVTUkUCpWaea0QgrqthcesPpJmWJmDzdG0sX2PzUQklUNV4KBBXmZ8XNqbc2lDnGFlDj5fHzHOq6lm9ttvP55//nlqa2sJBAKYzWbKysqIRqPGZXO5nHzQsAcUNCrXvENg/Wc0DZhC4+AjduoDCaDw+0PLg2rCkgxSUfs+gfWfYs7Eu+4GCCGE2OfJ4CEhhBCiE2yuPpr6mlO6d4m6Hq7t7IFpornkwNTtdmOz2chkMlgsFpqbm/coQCjumbm7/Tp3lh5k2mw2TCaT0cdT/9I0zVjavqtDjNqiB6jFy92LJ7q3rPjcV4LPDq2iLKZpOMLrtltF2bUDVRRGLJ9Jny3zdiqQVHbhvtA0reRrZ077wx/+wMiRI6mvr+fFF1/k008/ZebMmcybV1hGPn/+fO6//36qq6u54YYbUKwOfvFxklkrmvn+wf05e1IVCrC8IcHlM5egaTDj9FFcMXMpqgIzTh/NiAoHTfEM5/9jEemcxr2njmT/AR4sJpU7/reGlxY3MKaPkxmnj8akKtzy1ipmrWjmxuOq+caochTgmXmb+NMHdSW3tzpg57qtg4cundKP7x00AEuwjvlzP+Pee+8F4Nhjj+WCCy7AZDJx5513smjRImpqavjGN77Bn/70pz16OMU2u9pawhmqxd1LBnQJIYTYO0nIKYQQQnSwrMXFV0ffhKb2gAUTWh4ll0HbjQNTRVEIBAJks1nMZjP5fJ5gMLhHu6OqKi6XC7vdTjab3eN+nTvDZDJhs9mwWq1YLJaSwUSpVMoIbvVBRPr5VFVF07SSpe27stxeVdWSik+9XyAUgs+WFZ97m5Iqyk4cZjJ00bOUNSwsCQ+bKsezYsx3Cufrig8atgaNY1a8QEXj4l0KJNs7raODcFVVMZlMmEwmzGaz8X+TyUTC6uPD8Vd06PXtiUfPHM01r64wlraPnn0r1mTzdi9zzTXX8NRTT7Fp06au2MV9kjEkzGxHU80o+SymbOcMCRNCCCF2h4ScQgghRAfbXH0M9TUn9Yxl6lqeqmWvEqift1sHplarFZ/PRzQaxeVykUgkiMX2fKq02WzG7XZjsVhIpVJEo9EuWWZa3MfTYrGgKArZbJZUKkU6nSabzZbsY3HoqQ8xKg49d3Wf9YCpOPzUBxvpFZ/F4Wdv0bJKsbHfAawdcxad3o92axA4du2r9GtaCEDUGuDTsd8rLI3vykpqLY+i5Rn5wV3dOmClrRBTf54BJeGpfloun+fd/a4hb7J2yz5vj5pNMe6/19ONNfFCCCGE6CV6QImJEEIIsffQUGgcfDj0mENyhabBR9BnzWysu1Fpk06nSSaTOJ1OYrEYbrd7t/pztpTNZgkGg9hsNlwuF2VlZR3ar7M9Lft4WiwWbDZbqz6e+lc2myWRSACULG232Ww7HGLUFn2ieyqVMk7TK0iLhxsVT3Qvrvbc1cFNndnzsb1l15v8o1g79Ftbd6CTg35FAU1j8ZCTiAYb8W1awIpDzis807u6VYRSqPxdN+FcRnxyX6dWthVXZRaHmqqqGo9JWwG8Hqbrz1NjO6qKJ7aRkGdw97bYaGnrZO4etEdCCCGE6MEk5BRCCCE6ULSipjCooadQFDKOANGKGjwNS3drE9FolLKyMsxmM6lUCo/Hs8f9OXWpVIpUKoXT6cTpdGK32zu9X6dOX4quB7bFAabD4TCGCOlVnnqPzp0dYrS9ILhlSJjNZo3AVFGUkgDLZrNht9uN4FPTNOO+18Oqzuz5WHxdO1pinXRWsrj6VDq9grPY1qBz3cTzia95t+P7f+4K1UTCN5iG6qlUrnlnjzfX3vJyve2B/vgUPwbFgbT+vNJP14N0wKhg1p/XlsAqcA80Bj31BIqWpzyzBb/fb7SX2NWgXwghhBD7Dgk5hRBCiA4UKRtZmETbGT0Id1c+R7Rs5G6HnJqmEY1G8Xq9hEIhzGYzXq93j/tzFovH4ySTSVwuFx6Pxwg7u7JXpb5MPJFIlCxV93q9JcvJ9WXkepikh0R6AOVwOHA6nSVBoH7+3Q0f9aBRv7yqqq2CLr1KNJ/Ptwq+Wm6vM3o+aiisGnPW1knmXdyqYWsVZcPQY7q/ElFRqB954k5Plt7Zqkw9rMxms8bjr182n88bAXsulzOCcovFgt1uBzCqgfXXVfHjb7VaqUpuoKEnvW8BmmrCvukr8vl8SbV18QcPQgghhBA6CTmFEELs89xuNzNmzGDYsGFcdNFFrFy5EoA+ffpw/fXX43Q6mTt3Lg899BDl5eX89re/xeFw8Nxzz/Haa6/hcrm47bbbcDqdfNhk5WdvN5Rs32lRefKcsVS6rby8uIG73q01fvadSVXce+pI+t76PgADvDYe+FYNbpuZ91YHuWXWai45sB/TDuiHxaTwzspmbnhzFUMCdj754YEs2lzoj3nO0wtpiJUe8F9+yACWbYnz9oomrpl2BicMPAuAe+65hy+//JJbbrmF4cOHk0gkeO+993jyySc59dRT+d73vsfmzZvZvHkzN9xwA1VVVVxwwQU8+uijuN1uwuEwfr8ft9tNNBrtsMchn88TiURIJBK43W78fr/Rr1OvROuo5dXtndaellVw7VU56pPT9e3qIVTL5efFS893N3zU96l4qbvVajXuy+L+ni1Drc6wpXpqt1dR0sm3cWdpikrTgIPoU1TNuTNVmXpQrYd3+nNIf5wBI2xPpVJGpWZxKF9cqdleqAmFcNVut2O32wvhfHg11mSQtM3X/UExgKZhSQaxbVxEeOvSf729hM1mw+l0Gj1y9a/Ofo4LIYQQomeTkFMIIcQ+L5lMctVVV/GTn/yk5PSf/vSn3HbbbWzZsm2IyMUXX8wTTzzBnDlzePzxx5k1axZnnHEG77zzDjNffInLH5jJlEFpPquLGJe59KD+vL60kcc+28hrl0zi71/WsyGcRlXg2xMqWRfa1p/xzpNG8IMXl7IhvG2p89++qOcvczYC8N/L9mOAtzAp/d3VQc5+emG7t+u0MRWc9PE8Ak4LR4yt5qLTj6VvVRU333wzP/jBDwC4+eabjVBX9/e//51nn33W+H7Tpk1UVFQAhdDFbrcTjUbxeDxGFWNnhI+apmG1WikvL2/3NhbbUeXiriy73t5p+pJ2PVDSQ6nipe8tFYdQ+vLzPR1ipE+JL65m05fR66Gnw+EomeheHLLqgWxHyFpcbBp5YveHY919/QaFpiFHMrxpLhazuc2qTL09AZSGmVartVUonkwmjccPMIZhuVyunQ41dRaLBYfDYQTiyWSSRCJBLpejbO37haFpPaILpkZ57fslvU2Ln+96K4fiauvi16EsaxdCCCH2PRJyCiGE2Odls1mam5tLTjObzfTv35+rr76aQCDAgw8+yLx58xg/fjx//OMf0TSNxYsXM2LECAYNGsTMmTPJ2P3M3RjnyKH+kpDzsME+rn1tBQBvrWji0ME+Xli4hXMnVfH8gi389MhBhetUFYYE7Nx18kgq3VZuenMlH9WGyeQKB/kmVSGYyNKUyNDHbeWwIT7e+f7+vL8myK/eXFWy/xP6uljVVBiYE0nlaErmMPn7Ul5eRjgcxmazoaoqv/71r0kkEjzwwAOsWLECu93Od77zHU466SRefPFF/ve//wGwZMkSjj/+eGbPno3D4TACFK/Xu8P7d0/DR6vVitVqLfR8TCZJpVJtnq+rtNXHU+/Lqff51JfT6vulh1XFQ4z0oKrlECM9+Nyd29SyzyhQUgmoB58ulwvACGiLw8/d0TTgoMI0c1GgKKTtfoL+4Xgbl5UM+ymuutUD6OLl5vpjoYd0iqIY4bjb7W4Vasbj8R1WMeofTtjtdsxmM9lslmg0aryWdGXrP2XTyG/0iMdS0fKUrf+03Z/ncjni8TjxeBxVVY3Xocvlwu12G/enXvEqhBBCiL2fhJxCCCFEG/x+PzU1NVx33XVkMhn+7//+jwsuuACz2WyEApFIBK/Xy6pVq5gyZQpz697iuBFlrGxMlGwr4LAQThUCi3AyS5nTgqrAWRP7cPpTC4yQs8JlYXJ/N+f+fSHpnMZL0yZyyANzALh26hAuO7g/by1vIpHJszGcouYPHxHP5HnkzNGcMb6Sfy3cVnE6unJbyJnNa8zbGOXlZ57AZjbxq1/9Cq/Xy+OPP044HGbgwIFcd911/PCHP+TDDz/krbfewmazcc899/DFF1/Q0NDAunXrGDduHPF4HIfDgaIoRCIRXC6X0bOzvZByTyWTSVRVxeVy4XQ6sVgsRKPRHlGppVeWxWIxTCaTEUZ5PB5g23CXlpVlxRVpevWlHnrqYakeYu1p78HiJb06fdm0Hn4WT3TXKwaLw8/t0VBoHHw4u1r9N2Wgh3tOrSGT19gQSjHtn4vJ5jWeu2A85U4LdrPKda+v5L3VQarcVp44eyxOq8rDH6/n6S83MbGfmwe+NYpMLk8klePcvy8kntlWDTuywsEz541ndKWTPr99n1g6R3XAzmNnjQGgzGFhVVOCM59awI8OG8hPjxzE53URozq6rTYTfdwWXrhwIplcnpwGFz6ziPpIafXuiaPK6eex8pdP6zjr7O9w+qT+JJNJ7rrrLpqamrjqqqsYMGAAmqYxduxYvvGNbxCJRLjxxhsZPHgwS5Ys4U9/+hOBQIBrrrmGu+++G9i1UFNnNpuNcBMw2j+093wyZ2JULX+d+ppTurcqVtPou/z1neppCoXneDKZNAaW6YGn3W4vWdauvxaFEEIIsXeSkFMIIYRoQyQSYd26ddTX1wOFgMFkMpHNZo0wyOPxEA6HmTlzJr/4xS94/I+/46t0jvpo6UF0MJnBazMRSmbx2s3UNie5YL++PDd/c0kLwWAiy4rGhLF8PZPXMKkKubzGnbPXcte7a/nXhRM4eJCXT9aFSW+t8Jy5cAsHD/aWhJzFRlU6mTLQy7ev+ClDHVmuvfZafv7znxONRlFVlQ0bNgCFYCCdThtLsOfPn8/w4cOJRCIlVYCapqGqKm632+gH6PF4SkK84gCmo/6vV7nZ7XYCgQDpdJpkMlmyzLutcHVn/t8RcrkciUSCRCKBoijGUtodVZa1rL5UVbVkabs+xEgPRvX7YU/3VR/gotMH1ejhZ3HwqYedLfuJAkQrasg4Aru8D+tCKY5/9AuS2Ty3fX0Y3xxbwQsLt3DePxaRyWkMCdh55IzRfP2xL7n26MH84d21vLMqyOzv78/zC7eweFOMI2d8DsBNxw/l9HGVPP3lJmP7daEUxzw8l5nTJhqnrWlOctwjXxiXWbP1g4Bn523itSWN3HHicOO8bbWZqI+kOeqhz9E0mHZAX747pT+/e3tNye267OD+fOfvi+jjsTP1wBH8YPo5jBw5km9/+9v87ne/48YbbwSgX79+/PrXvyYcDnPccccRCoW45ppruOaaazj44INZsGABoVCIQCDA8uXLd+n5arPZcDgcWCwWo+IxkUjs1DYq18wm1HcSCc+A7hmgls/hCNdRsWb2bm9Cfz1Fo1HjuayHnvprSQ88d7VNhBBCCCF6Lgk5hRBCiDakUilCoZARTulhwaJFizjwwAOZO3cuY8aM4d577yWVSnHLLbeQ8PTj53f/mVe/Kh089OHaEMePLOMvczZy/IgyLnthCT86bCD79fdw/n59GVnh5N5TR/KTl5fTGM/gs5vJ5PLYTIWA02pSSOc08hrE0nnimRxuq4louhA0HVHtY8mW0oqnpQ1xpg7zA4X6ulAyS7i5mfVNYaxWK6FQCJfLRSwWo6ysDFVVCQaDuFwu4vE4JpOJmpoann32WRKJBBUVFaxYscIIxfQKQH0ZrtVqNSZ7Fw/waW+wz678v72BQHq1VkfZ3YB0e//XNI1UKmVMuy6evK5XTBYPICq+bHG/U73y0ul0GtWzxdWW7W1jR/8vpgefxYqrPfVJ3cXBZyaTYXPlaMjndjkQK66A1J/fgNGewWM1sWhTYbDWlIFernl1BZoGn68PM77Kxefrt7WEcJhVljaUvgYSmTylNdWlTh1TwXGPzAVgSyyD01q6/+21mdC5bWYWbyodvOWzmzEpCqlsniH93CzYkiIciTBv3jx+8YtfGEOpLBYLp5xyCh9++CEVFRUceOCBfPTRR2QyGd577z2GDh3Ku+++y3vvvcdhhx3GsmXLdnh/mkwmo2pTVVXS6TShUGiXKxcVNIYteZ7FU35ceK50ZUWnlkdBY9CCf5T04twTejgfi8VQVdUIPN1uN4qilPTxlGXtQgghRO8mIacQQggB3HfffYwaNYrq6mqef/55Xn75Ze6//37+7//+D4vFwkMPPQTAX//6V37729/ywx/+kOeff55UKkVNTQ0///nPyWHikdUh1jQXlkzOOH0UV8xcymOfbeSpc8Zy8YH9ePWrRtaHU/zyjW3Dfj750YH85OXlAPzqjZW8NG0iVpPCLbNWA/CLY6qZOsyPWS1MV19QH+MbNWX85oRhxDN51jQnuOmt1SW3Z/7GKMPLHQAs2RJnQzjF3/50OxYV/vznP5NOp7nzzjvxer2YTCbuvvtuUqkU06ZN4/DDD0dRFN544w1Wry5sd/z48fzud78jFosZ1+H3+zGbzTQ3N+PxeLBarQSDwU5fRl4cfjqdTmOITyKRKBnk0vL8u/r/9n6+M+dpK9zVg03AmLpe3P5A/3dnp73rweOecjgc/P73v2fIkCFceeWVrFmzBk3TuO666zjooIN48sknefHFF4FC9eE111yDyWTir3/9K1988QWJQDUoKg9+axQVLku7w7Beu2QSCzfFjOAQCm0Yzp3ch1g6z21vr2FclYu7Th7BQYO8qIrC2U8vZMbpo5jYz837VxzAzf9ZRSiZo8xZuN1frynjd98YzkCfjd/PXstNxw/ljPGVNMYzzF0f4eevrqDcaeG/0yeTymnMXR/hpy8v5/gRAQb7bcycNtFYtv6zV5YzsZ+bAV4b68OpNttMAEzq52bG6aPwO8x847F5JbexpsJhvP5XNibYf1AAxVvF4ZNH4ff7CQQCRk/NqVOn8otf/IJwOIzFYmHjxo1Eo1Gam5sZMWIEAHV1dZxwwgnbffz0frBWq9VYtp1IJHa7QtFms+Exp2D1SywadnphYn1XBJ2aBigM/+o57PG2q9L3lP4+oVdbF/fTdblcJX1xZVm7EEII0ftIyCmEEEIAV155ZavT5s2bx6WXXlpyWkNDA1dccUXJacuWLWP69OloKCw67jYwF6afXzFzKQCxdI4znlrQ7nUffP8c4/8f1YY5+uG5JT//zazVLS/CG8uaeGNZ03Zv078XN3Ds8ABvr2zmZy8uYuqXd6NuDSsqKiq45ZZbSqoJzWYzjz76KI888kjJdqqqqmhsbCQSiZScHolECAQCOJ1OotEofr8fj8dDMBjc7n7tqeJQMBqNEo/HcbvduN1uMpkM0Wi011RkFffxLB4o01Yfz/YCVT3stFgsmEwmY4hR8RLzYi23E4/H+elPf8qPfvQjkskk8XgcRVF44IEH+Oyzz3A4HEbgc+mll3L77bfT3NzMH/7wBz7+5FMizr4MKXPQ32s1Wii0dNgQX6vT3FYT+/V3M7TMwUH3zSGb1/jdN4Zz9tMLiaRyDPIVQsiz/raAcVUuvvnEfF777mQ+XReiKV7oKfnmsib6uK18o6aMyw4aAMANb6zk1SWNxvXEUjlO/Ms8wsksT39nHAcM8HDoEB/XvLqCp+bWlyxbX9uc5AeHDuCGN1e12WYCYN7GKIc9+DlnTejDL44ewg9eXNrmbW6MZ3j4k/U8/sA9rFm+hHXr1hk9NSsqKohGo9TV1QEQDodxu92F+8XtJhQKtblNnaqqRtWmyWQik8kQDodL2g/sDofDYbSh6NO8hOhXz7F2zLe3Bp2dOIxIKwSyI5a/yJDUWqIOhzGkq9OucmuVtX6fFbeI2N4QMSGEEEL0XN0/OlEIIYTYSyhoOMJ10EMOhmd8vJ63VzaDpuGObSARjxMOhwmFQsRiMWMptN1ux+v1EggEqKiooLy83AgsHQ4Hzc3N3HPPPa22r/f6czqdqKpKOBzGbDYbYU1XyefzhMNhgsEgiqIQCATweDzG5OqeTO/jGQwGjSA5n8/jcrkoKysjEAjgcrmwWCytJtLrX/owmebmZhobG40lyiaTCZfLhdfrNQY2FQc7qVSKZDJJLBajvr6eXC5nVAHG43Fqa2uNgCcWixGLxQgEAixZsoRNmzbR1NRE3hkgb7Ly86MG88f31rV7O688bCAPfFRXctpPjhzEIL+dDeEUyxriDC2zY1EVnv7OOF7/7iT6ea1EUzlWNyWZUxfh4EFeNE1j/wFeFm2KYTUVwtpvjq3g8/UR4plCIHzLCcN4+7L9OGZ4oU9oMpcnt3UtfDqXJ69pnDqmgpcWFaoFTx1TwYtb/x9KZjl0ayCrt5kAOH5EGR/XhrGYtgXEoWTWuE7dsoYE1QG78f1Tc+u58Ke/4vXXX+fjjz82Jpofe+yxzJo1yzjfvHnzOPjggwE49NBDmTevUCE6cOBAo5oaCkGcx+OhrKwMp9NJOp2mubmZYDC4xwGn0+k0Ak5VVQmFQvjWfcLgeU+haPlCS4LOkM+haHkGz3sK5+oPjA8tnE5n51xfO/QBYk1NTTQ1NRnL2z0ej/Ge6HA4MJm6oU+pEEIIIXaKVHIKIYQQHcgRqiXmrwal5xwIK1oeT2Q9DocDVVVL+j2mUilj0rK+fNpkMhlfVqvVCAv1cE3v3Vg8hEav4IxGo3g8HmPbXSmTydDc3IzdbsflcmGz2YjH48TjOzehubu1rCzbnQnR2xtipG9nT4YYFQfH0WgUT1klQ8sKod7aYLLNyxw51M+8jVGjhyyA12bipFHljCh3YjUr/Pey/Xh7RRMT+rlZ1ZjAZlZ59ZLJnP7kfADunL2Wj354IKlsYVl7MpvntLEV/OSIQezX34MGTHt2MTazSj+PlZvfWsWs6fuxKZJmUj83L02byEuLttDHbSWRybMhnCKcyjGmj5MN4RQnjirnB4cNZGS5E5tZxaTSZpuJKQM93HnySHJ5jVQ2z6XPf1VyW0PJLHlNw2ZWSWXz/P3ccQw56yY21a3hjjvuMM533HHH8dOf/tT4/r333uOYY47hscceY+nSpcyfX7jdBx98MDNnzsRut+NwODCbzWSzWSMs7ajqQpfLhdPpNAashUIh43nh3zQf+4f1rJtwLgnvoI5duq5pOCPrGTj/78YS9VgsRj6fN3pmFrfI6Coth4jpFZ76EDF9aFc6nW53Ur0QQgghup6EnEIIIUQH8jQtp2HoMd29GyU01YS1fjGNjY0lQ2T0PnTFQ2T0gKw4PCkefKOHn8VDaKAQrpWVlRnLoz0ejxGmdfUyz2QySSqVwul0Gj07Y7FYl4eue2pHE6KLewe2139Rr/TUb7seXBc//vl8nkwmYzy221N8PW63m2A0znVTh3DHO2vbvcxVhw/ku899xf4DPMZpPz5iED9/dQUfrA3xyY8O5LhHvmB0pZODBvn45hOFgO/jHx7I+2sKy7ZPHFXOK181cOVL2wbw/HtxA/9e3MBHPzyQs/5W6AMaz+SNNhGLN8X45RsrWd2UZIDXxj/OG8eZTy1gSyzDt54stI/49oQ+PL9gM8/OL3wBvHfFAWha220mPquLcEyLdhItPfLJBs6fXMXjczZy3j8WMXr2rViTzSXnufjii0u+z+Vy3HzzzSWnuVwuKioqjKXsesVuR4dqHo8Hm81GJpPBbDYTCoVaXYc9tpkRH/+Jhuqp1I88EU1RAWX3Ak9NAzQULU/1uv9R3fA5kWyI4thenwTv8XhQFIVoNNre1jpdex8+2Gy2kg8f2nrvFEIIIUTXkpBTCCGE6EDuhmVYEs1k7P6unUrcHk3DlglTTQMZr9dYjqz3u9MDSz201Cv99NBT/2qrtyNsG6CjL4fWB+4oioLPV1j2W1z9WdwDtDMHFGmaRiwWI5lMGku2e1u/zmI7MyG6rT6eLRVXqEFheroeelosFnw+H2VlZUbg3XLJf0NDAwMHDqSpqQmfz0cw2Ex1wMGD3xqF3aIyqtLJpVP68dhnG43LDC938Ox54wk4LVS6LLy7upkR5U4OHVx4foyscPLLY4Zw5+xayp0WzKpCldtKKFl4nI4bEeCM8ZVGMNnS+lCSSpeFLbEMHpuJSCqHw6IyqtLFxnAat9XE388bxxUzl7IlVhrenTqmguMf/aL0vs5vm/S+O15buq0fqJpNYWkRcO6I3hPSYrHwhz/8gWQySTKZ3O1BQtvj9XqxWq1kMhksFgvhcLjdEFVBo3LNOwTWf0bTgCk0Dj6CjCNQWMauqNt/v9O0Qt9N1YQlGaSi9n0C6z/FnImT8Xrxer1Eo1GSyW3VwMlk0gg69XYYPUFxpbT++rFarXi9XuODHb3iujMeMyGEEEK0T5k8ebJ83CiEEEJ0oM3Vx1Bfc1LnDurYWVqefstfY3D9x9jtdsxms7HUMplMtgrEikNPfZCNPhVcX5rZXnVmWVkZuVyOUCiEyWQiEAgYy6GLl8AXB2fF4WdxCNrR4YDFYsHtdmMymUilUsaS2N6ueCmt1Wo1hg7t6lLa++67j1GjRlFfX8+///1vZs2axfe//30OOeQQVFXl448/5u6772bgwIHccMMNmEwmZsyYwXvzlrFk6q8AGBKw84eTRnD20wupclu5/JAB3FI0NGvqMD8nj64oma4O8MmPDjSGb50+rpKfHDkIk6Lw05eX8VldhGU/P5RgIkMknSOZyXPyX0onml+0f1+yeY2/f7mJGaePYnxfNyYF/u/9dTw7fzM3HFvN9w8ewPLGQtuCW95azburg4yudHLHicNLwtOJ/dycO6mKX76xctcfjJY0DVfzKoZ/9uAOz6qqKg6HA7vdjqqqpNNpEolEp0749vl8WCwW0uk0VquVSCSyS9XOGgrRihoiZSNJ+AaT8A4kv3XoWjE1m8IRrsMZqsXdtBx3wzIUSt8/9OXy8Xi81fJ0PUDMZDI7HMjUnVRVNV6L+gc+xUPEeuOHK0IIIURvIyGnEEII0cGyFhdfHX0Tmtr9CyaUfJYx79yCOVMIeMxmM3a7HZvNhqqqZDIZEolEu+GG3tNR7+uoL2duWemZz+exWCz4/X4ikQjJZBK73Y7H42k19Vlf/t5WD9Di5e/tBaB7shxU79cJEI/HO32Cc1fTq8r0x6q4j+eutg5QFMV43PXttWptkMmy6Ljb2gy3uorVpPD4WWO44JnFe7ytP54ykj++V0tdqANaG+RzVK59l37LXmn3LFar1aja1DTNqNrszCpnvcpan8pus9k6ZDK7hkLG7idntqOpZpR8FlM2iSUZbBVqtkWf7J5MJolEIiU/06uMs9ksoVCoxy8J1187+ocPqqoarSOKK0GFEEII0bEk5BRCCCE6webqo6mvOaV7l6xrGgNWvEb5qrfb/LHNZsNut2OxWACMgGV7FUfFoafFYsFsLgS5evClB5fNzc3k83mj319zc/NOBTf68veWIaiqqkYA2nL5e3EAujMURcHpdOJwOMjn80Sj0b0ydNCX0tpsNsxms7GUVg9adrWSVa9U04NPfYjVnJHnEfYM7hntGXqYoZ8/gqdhaclpiqIYVZt60Li9Dxo6kqIo+P1+VFUllUrhcDiMDyV6guKqzXA4XBJmms1mfD6fUS3e04POYsUfFuivxZ3pqSuEEEKIXSMhpxBCCNEJNBRWHHIVCc8AULth0no+hztez0HL/kYyUVgC2l4ooKoqdrvdCF2y2awxvGdHB9/F1X7Foac+rCOTyRh9Ppubd603YUvF4WfLAFTXVvjZXv9Pk8lkTGHXB/x0ZgVddyru46kvpS3uHbizt1tRFMxmszEIyWQysXLgsazrMwWtO57nPZWmYUkGGf3ubUYVo9731mYrVL3uzIcKHUlVVXw+H4qiGIO5otFoj6tm1sPMfD5PKBQqeQ8ymUz4/f42f9Zb6MO/9vS1KIQQQojWJOQUQgghOknS1Yflh11dmETclf05NQ1FyzHyg7vw5yPGBPWdqVhsGcSk02mSyeROVzrqVWoul8voxakoirH8PJFIkMlkOvRAvnj5e8sAdEfL33O5HCaTyejXmUwmtxsI7w30Pp76l6qqrfp4tnVfFgfKmqYZj2uDZyjza77Tzbeqh9Hy9F32KlVrZxuDhMxms/EBgj5Up6uYTCZjEJg+jCsWixGPx7tsH3ZF8f6GQqGS94vinwWDwV4ZdOqKe+paLJY2X4tCCCGE2HkScgohhBCdKFg1kdpJFwJK1yzn3RqcjFs1k/KGRcZEYrfbjc1mI5lMEo1GdxiwKIpSspw9n8/vUr9Ar9eLxWKhubm5pA+oHo7l83mjn+eOJoLviZbL3/XQrq3l74CxlDSZTBKPx/f6sNNkMhkBi9lsNoLL4vtGP29xYFxS7avBkqNuIGP3y5L1rZR8lilz7sal5lAUxRgk1B2hVXFlZDKZxO129+iAU1fcO7Tl1He9KlVVVYLB4F5T/Vjcx7O4p67+tTe/HwkhhBAdQUJOIYQQopM19Z9C3fizC990ZkWnVgikBi58lspNXxjVTuFwmGw2i81mw+12AxCNRne6B6DJZDKWs+vDivSAq72DbkVRKCsrI51OG0NE9P6ckUikZIp7y9Azk8l0yRLeluFncQCqy+fzZLPZnVr+3lOpqlpSjdlymb+macbtzOfzbbYB0Pt5tjfxe3P1MdTXnNS1Fcvt0bRu74U7Yv3/GLL505L7trh3bC6X65LAymKx4PV6yeVyJJNJPB5PmxPMezKv14vVaiUajZb0Di0OQUOh0F43vVz/AEJf1q6/BvXAsze9BwkhhBBdRUJOIYQQogsEqyaybuL5aCid06Mzn0NBY9D8p/Fvmg9sCwHMZjORSIRUKoWiKLjdbux2O6lUimg0ukvLPa1WK3a7HavVCkAqlSKZTLZZoWaz2fB6vYRCISMYCwQCACX9OYsHGemhp35Arx/Ud2WAofcZdblcmM1mI5Da2eXv3bF8VlEUVJOZnKsMzepCNVsxKxpWsjgyYRS2VWC2DNvy+bzxuOr3P7SucG2vd6Cqqlh9lXx24M/QVHOX3/ZWNK0Q+HdDj1BFy+GK1TPqswfQ8rmSULllBXHLx0H/f0eFn8VDfPSAU6/k7m3cbjcOh6NVBWrxe1woFNprl3frQ7/0L0VRyGazxmtxbwt4hRBCiN0lIacQQgjRRZKuPqybcC4J76COrTTTNByhWgYt+Af2+JZWP/Z4PNjt9pKAwGq14na7URSFWCy2y9OV9UE2drvdCAL15ezFIZ9eadXc3GwEhYFAYLthi9lsLgk99SnexZWeXRVmWK1WXC6X0a8zkUi0uQS+ZQDaXvXnngZYJddtthAuG0nYP4yIewBhZ1/yJmvry+RSOMLrcQTX4mlajrthGSZ1WzuC4n3P5XJGH9bi4KS9Pp565SfA2j4HsXLgsd1eRVm55h0ahhzZDb1w8yhanpEf3NXm61DXMvhs+fxpr/JzV8Jzm82Gx+MxHkuv10sqlTKqqnsjh8OB2+0mmUy2uh0+nw+LxUI4HN7p/sG9mf46tNlsqKpqLGvXe3kKIYQQ+yoJOYUQQogupKHQUD2V+pEnFkKY3e3VqWmFrWl5+q14g/LV7xhTnNvidDpxuVwlAYGiKLhcLhwOxx5NF9d7btrtdoCS5eyqqlJWVlYSauoVnuFweKeWzLcXemaz2ZJKz85c/utwOHA6nQDE4/E2J1K3FXzqXzq9OrKtELTltooDMP17RVFImxxsKJ9AXeUBpGw+lHxua6C3nedRUXWjLRVi0JbP6dswH1s+aQw62dl+q2azGafTaVTzGuGcBnNGXUTEWdUtVZTkczjCdYz45D5CVRO6oReuxuB5TxmV1LuqZTuBlsOz9OdO8fNHbzFQzG6343a7SaVSpFKpvSLg1OnhbSaTIRwOl7zm9WXtetX6vsJsNhvL2vWewplMxgg8e/NgJiGEEGJXScgphBBCdIOsxUXTgCk0Dj6CjCMA+Vyh6mwngypLopmK2vcZFluOkooYA4a2R1++ms1mCYfDxsGvxWLB4/GgqiqxWKzNAG9ntDWsSO/b6XQ6CQaDRgWm3p+zubl5l4PV4n6eelVhceipf3V06KmHwna7nVwuRywW26mqqbamv7c1rbzlZaC0qi+by7Oh/6GsH3ZClwbk+lJZfUCRUnSdei9VTdMwm82kXH34ZMylOw5dO1obVZRd1wu3cH8OXPgsZRvmdPjmi3uqtjfpXg889ddgIpEglUrh8/lIp9M79f7QW+h9RvP5PKFQqCTE099XWvbv3FfoFfZ6H8/iFhNd3fZDCCGE6A4ScgohhBDdSEMhWlFDpGwkCd9gEt6B5M22VudTsykc4TqcoVrcW5ccK2hGVWRzc/NOHcCazWa8Xi8AoVCoJGDUqzqz2SyRSGSPBlvow4psNpsxJRigqanJCPTa6s+5u9dVXOlpMpk6NfQ0mUy43W6sVutOVcC2rMZsK6TK5/PG/imKUtK7UdM0IhY/Xw07jairf8e3OgivK7Q6iG02rr/l0nR94rqmaWiaRiqVIh6Pt6oSU1WVaP/9WTH2O2zdWMft63ZuA8CYFS/gq59X0s4gWDWR2okX7PgDhD29fi3P4Pl/2+0qzt2hty7Qn1P6RO5tu1W4X/Rwq7gCtLczmUzGYLW23secTifRaHS3P7DZG7TXYqJ4WrsQQgixt5GQUwghhOhBTGYLjqrBNEWTZDUVq0nB77QQ3bDamJ7eUiAQQNM0gsHgTl2Hqqp4vV7MZnOrHnZmsxmPx4PJZCIej5cM+dhdVqsVh8OBxWIBtg0ryufzBAKBDl9Kq6qqUeWph55Aq9BzT5dx6n1NVVU1po63nGS+o+XG25vUrm8rWDWRlaPPLtRaduLQqpHLXqB/ZEWroCyfzxtBZ3tL9VsOj9pYPpGvhpxU+GFnBp1bXxNDl/6LAc2LWvVwXR8Yx6qaM7psPzqrmnNH9ME80WiUbDZrVDqm0+mS5yNse0xbBp9dNfG9o+jvYyaTiXA4XNKnV2/P0dsmyXcm/T1R/+BJ07SSPp696bEXQggh2iMhpxBCCNGDWK1WfD4fDQ0NxqCesrKy7VZqWiwW/H5/yRTznaH3sGtribrT6cTpdJLL5YhEIh2yzLF4m/qwomw2i81m2+n+nLtDDz31L31Qzu6Enm2FmPrUbNgWIHXU1OyuXXINY9e+SlXDfFKpFPl83ghEkskksVjMuA0tQ01FUYyl6/pj2lg+jkVDT+v0cHbQ/KdLKij1Hq6hvpNYMvLbhRN7SV/O3VG8RDuTyeD3+8nlcq0+9NDbJhQ/f/X/6zp74ntHUxQFr9eLxWJp1YtTH1SUSCR65UT5zqRX/tpstpL3Qz3w3BuqfYUQQuybJOQUQgghehC73Y7H42HLlkJfQUVRqKio2GGA6fP5UFV1l5d+tzWQSGcymfB4PJjN5lYh1+7Sq06j0aixnF2vvNuTfqC7QlGUkkpP/SA/l8sZgWcul2u1HHhHE9T10CCXyxmB054IVk2kdtJF+k7v0bZ2ytaQrnr+0wyMr8JmsxnL8VsGxcWhZjqdJpPJYLFYjH6sUKjYbcg7WDv+OyS8gzp+mX2otrDMvo1J5klXH5YfdnWPnbDeUfQPKsLhMLlczgg4Q6HQLr1W2wo+O3rie2fSK1ljsVhJ9XnxEKa9YfBSZ9CXteu9PBVFIZvNGkva9/R9TAghhOhKEnIKIYQQPYg+xbuxsdE4raKiYocBoNlsJhAIEIlEdnnghj6xOJvNthmOOBwOXC4X+XyeSCSyRwe9ZrMZv99fcntsNpux7FsfVpRMJjt9SEZxiKmHncW9MKF0qIsefm4v3DGbzbhcLqxWK6lUilgstltVUd0X0mkoWo6DFj+GLbbZqP4rDjX1YFOvyNXDaj0c0UNxvXpOQ6Gheir1I0/ssIFJfZe/TsWa2W0OTNJQWHHIVSQ8A7p9yvv2BjrtieIKRr0npd/vN9pWdGT/2Z2Z+N4yAO2O8FP/wKZl5ab+/ra3DWDqLMV9PPV+ysV9PHtqVa8QQggBEnIKIYQQPYrL5cJms9HU1GScFggESKfTO+wt5/F4sFgsJZfdWWazGZ/Ph6ZprQZ5QCEQ9Hg8WK1WI8Da3YNdfcBR8WR1k8lEIBAgl8sZwVo2myWZTJJMJvfowLqtCjWz2VxSldkyoNGH/+jhZ3shX3ta9uuMx+M7dRtUVcVitfHVAVcQc/VFU7o+pFO0HJ74Jg5Y8iRqURZZXLVavGw/l8uRTCZJp9NGL8jipdIWiwWXy4Vm91IXGEddnwPJ2P2Qz+14IJBWCDU11YQ12Uz52vcJrP8Uc6b9XrGbq4+mvuaUrp3u3pKm0W/ZK1SueafDN60oCj6fD5PJZEwX74yAc3vamvje8jXVVuVnZy+D1gPNTCZDOBw27gur1YrX6yWTyRAKhTp1H/YmZrPZCDwtFovR61bv5dlTKnmFEEIInYScQgghRA/idrsLw2aKQiI9fNxRFZKqqpSVle32sm9VVY1l7y0Heej0qkuASCSy2xN62xqWpE+K1ye72+12rDYbSauPRF4llcmRTScwZZJYksGSKjlFUdrtldnekttdqTrbXg9K/autylO9D6m+HL+tKls9SND7463pcxArBxzTI0K6PmtnG/envsRf7z/aMiTWT9erVxVFwel0YrFYyGQyxOPxQiUYCtGKGiJlI0n4BpPwDiRvtrXaBTWbwhlZTyC5ibJoLeWR1bCDYSlZi4uvjr4JTTV38h20Y0o+y5h3btluILuril+jeqDp9/sBCAaD3R46tWzxoP9b3LO2ZfDZ0RPfLRYLXq+XXC5HOBw27pPi03d1Ob8oPLb6+5T+Hljcx7OzK++FEEKInSEhpxBCCNGDeL1egJJAs63gsz0ulwu73U5TU9NuHcQrimJUbLYXlqqqitvtxmazkUqliEajuxyu6MOSWi6vd7k9xPuMYaOlLzHvwPYDsFwad3QD3tgGyqK1VETXoFAaorQMUzoy1Ggv9NR72RWHnqqqGo9LNps1ltLqPfD0SceKopBS7Xww8Uc9JqQbO/s3uE157Ha7MSwqkUiQTCaNiluTyYTD4TCWthYHy8XL/Vs+LvrjoaGQsfvJme1oqhkln8WU3RZk630Vg8EgFovFCFnaqirbXH0M9TUnde0S//Zoefoue5U+HVTNqaqqEWgWV3AqitIjAs7tKf4QouWHEdDxE99NJhM+nw+gpDJdr1jXq40l6Nw9el9j/T2suNWIvqxdCCGE6A4ScgohhBA9iD44pHhIhtPpxOFwlPTpbI+iKJSVlRmDgnaXy+XC6XRudzKx1WrF4/EAtFuluD1ut9sIZNMmB00DDqJx8OFkHAGUfG5rP8qdW8psSQapXPcB/nWfYM7s/u3eXXpfT/1LH6ZUXOVpMplwOp0lVW2KoqAVVSfW9T+MjSN7Tkg3fP07DNn0idEnta3qXn2KdSwWM5a16uGjpmltVvQBrULP4q9ieoVy8fRsvapM/1IUhUw2x8eTfkTa6u3eKlidpmFJBhn97m173JtTD+30dhJ6BWdvCDi3Z3cmvu/shxbtVaa3vC97633Xk+hV3larFbPZbLyn6e9rEiYLIYToKhJyCiGEED1IW/039WXc+sT1HdGXSDc1Ne3RAbxeQdeyv10xRVGMsDKdThOJRHb6Ok0mEz5/gNo+U1g98OgOG0pTtfx1KtsZStNVinvZtexVqAebgHGfaZoGisqSo24gbfP1mJDOmgox+t3bQGv7MbVYLEZlnMlkIpPJEIvF2h1OVVz92bK1QFs9UvUvh8NhLD9ui9PpJNZnLAtGndsxt70DDf38ETwNS3f78nr1ob7MGjB6cgaDwU7vc9ld2qv8bKv9RHHlZ/H7T/GAppYheXFV7N56H3YHk8lU0sdTUZSSimu5r4UQQnQmCTmFEEKIHqS8vNwYVKPTl3Y3NTXt9AFieXm5EaDtCb2PXT6fJxwOt3v9FosFj8eDqqqtlrm31SvTZDIRt5ezqPoUIs5+HRvqaRqO8DoGLfgH9tjmjtvuTmjZX1MPM/Xl7HrIqVd6bttljWb/cL4ccU6X7u/OaC+k04e86CHG9sLN9rjdbmbMmMGwYcO4+OKLWbNmDSaTifPPP5+pU6eSTCa56667aG5u5lvf+hZnnHEGS5cu5cYbbySfz3Paaadx6qmnomkaL87fwI1ztZKJ6lVuK0+cPRanVeXhj9fz9JebsJtVnj1/PB6biWxe44JnFrE5muG5C8ZT7rRgN6tc9/pK3lsdZFSlk0fPHE1OgzVNCS557isApg7zc8Ox1aiKwv0fruPFRQ0lt+vEUeX081j5y6d1DKj/hAOic3nmmWc477zzWLlyJVB4Tjz//PP885//5Nlnn6WsrIw77rgDRVGIxWJcd911WK1WfvWrX3H77beXVHDu7QHn9rRX+bm9ie82mw273U40GjXem4orPUOhkPSU7ASKopT08VRVlVwuZyxr39X3CyGEEGJHur/hkxBCCCEMehhWTA8y9APEnRGLxXC73cTj8T0KQjKZDM3Nzfh8Pvx+f7sDiXK5HNFoFIfDYSx115cqF58nl8uRyWRY7x7OytFnoe1u5eb2KApJ7wCWH3YNQxc/g3v93I7dfgvFven0/po6vU9dKpUim822WeGpaRqqqtLkqS4s01e7fqJ6u/I5omUjS0JOq9VqDBTSl/zubg++ZDLJVVddxU9+8hNjkJPX6+Wggw7ioosuYty4cZx55pn84Q9/4J133mH27NlcccUVxhL1hQsXMmvWLAB+M+Mp/rRsKVvi28Kqa48ezB/eXcs7q4LM/v7+PL9wCyeOKmdhfZQb3lzFBfv15bsH9ueOd9Zy3j8WkclpDAnYeeSM0Xz9sS+54pAB3PrfNfxneROPnjmaQwd7+WJDlJ8dOZiT/zKPTK7tWoHLDu7Pd/6+CE1RCbv6M+3MCSxevBi/329M+T722GPZuHGjcZmTTjqJN998kxdeeIHp06fzta99jY8//phQKITf7ycYDJZMVd8XA06gzZYGQKsPUsxmM3a7vaRCWK8611+PkUgEt9uNz+eToLMTaJpmvP/BtvdKm82G0+kkn88by9rbGiQmhBBC7CoJOYUQQogeQlGUNkNOvQJQXw68M5LJpNErUV/iurv0IR1erxefz0cymTSWJ+tfxX0m8/m8Ua2YSqWMoFU/gG3qP4W6MWdvvdGd03tSU0yAxqpx5zHa5SWw/jOSyWSHBEN6dZLVamXChAn88Ic/JJvN0tjYyJ133kkqleLnP/85hxxyCI8++igzZ87E6XQybNgwfvazn6GqKk888QRffvklBxxwAOeddx6aprEw7uSiF9eWXJfTovLkOWOpdFt5eXEDd71by5CAnU9+eCCLNhdaGpzz9EIaYhmWXHMI68OFMOH2t9cwa0Uzj581hrF9XMQyOV5b0sjd79YywGvj8bPGYDEpvLS4gf97f12r2/jImaP5+asrOLK6nBuvuBJT8AyWLVvGQw89hMVi4Zvf/CZTp06lqamJ66+/nnQ6jdvt5oYbbqCsrIza2lpuu+02TjjhBPL5vBFEtpTNZmlubi45rV+/fqxatQqAJUuWcPPNN5PL5WhsbKRv377GgKNEIsGiRYtQVRXVZCapmclTGphPGejlmldXoGnw+fow46tcrGiMM3WYHwC/w0xjvPCa0gNLj9XEok2F+/arzTH8jsKfy16bmaZElkOH+Ehkcvx72kTimTw/mLmUTdFtIa/PbsakKKSyeVAUKgYNI5Gop66uzhjY5PF4OOmkk/jwww+x2+3Y7XbWrFnDiBEjgELbitraWlKpFO+++y5HH300M2fOxGKxEAwGJYxrQ3vhp6qqRrWnvoTa6XSWhJ96hWzxpPB9NUTuTHqPYiiE0vqHQ16vt2SQWDqdlvtfCCHEbpGQUwghhOghig+6W9KnVu+KWCyGz+czBsHsyn60tcRcn5rtcDjQNI1sNks2mzX6rGWz2ZKAVu8NajKZiEQiZLNZglUTqRt/jn5Fu3R7dpmigKaxpPoUxuUz9A0tI5PJkEwmd3kYhqqqxgF58TT1hoYGrr32WmKxGJdccgkTJ05k9uzZPPLIIyxYsACn04nb7Qbg0ksv5e6772bLli3cfvvt/O9//2PWrFm88cYbmMwWrpoxk8OG+Plg7bZQ+tKD+vP60kYe+2wjr10yib9/WQ/Au6uDnP30wpJ9DCWzHPfIF632/dLnvzJCO4Drjh7C799Zy9srm3nxogn8c94mNka2hXRDAnZyeY1QMsu8jVGOmDGXQz77PTffdBM1NTWsX7+eww47jEsvvZTjjjuOc845h8cff5wrrriCv/71ryxduq3q87///S933313uyFnW+rq6hg7diwWi4VDDjkEv99PeXk5UHhuptPpkmA0n89z5HEnsrI5ZQSWOotJRX+YQ8kcZU4L760OMqaPi/k/PQgFhUMemGOc/3/f35+aCgcX/7OwLP0/y5t489L9uPn4oXyxIcLSLXH26+9heLmTwx6cw/Ejyrj5+KH84MVtt7mmwsGa5m1DuK45diSP/f5hrrzkXJLJJKFQiFNOOYXXX38du92OxWLB7XazYcMGfvzjH3PaaacRjUZ56KGHiEQi1NXVcfLJJ2OxWKTacDfo1YIAiUTCaMGRzWaJx+NGCKpXGNrtdqB1b9ji/p9iz+VyOeLxuPEY6B8cuVwu3G432WzW6OMp97kQQoid1QNGdwohhBACMELMtgb36JWTu0LveeZyudr8uV7ZpFd86mFSRUUFfr8ft9uN1WoFIJVKEY1GaW5uNqat5/N5o/9mOp1utd/xeJzm5majSkqtGMK6iecDWtcN1lEUQGPx0NPYlLaQz+dxu92Ul5fj8XiwWCztXtRsNuN0OgkEApSXl+NyuUqWmGezWWpra2loaCCTyaBpGk6nE6/XSyQSKekRmEwm8fv9zJ8/nw0bNhAKhaioqMDpdFJeXo6jagiaopaEYwCHDfbxn2VNALy1oolDB/sKpw/x8c739+fWrw8zzuu2mnj7sv3423fGEthafahp8PAZo3nz0slM7FcIW4eXO/hyY6FX6/z6GIcN8ZVc56ljKnh7RSFEXBdKkVGtJC1e4vE4iUSCSZMmMWfOHDKZDB9//DGTJ09GVVVGjx7NOeecw5///GeOO+44owdfOp2mb9++Rm++4jDJbrfjcDgwmUzY7XacTifpdJpXXnmFRx99lGOOOYZ169YZgYcewHu9XqOyeP/99+fCc8/mx/9e1uoxzOTyxlPNZzfRFM8w7YC+fLA2xMR7PuXXb63iV8dWG+c/5uG5HHL/HG4/cTgAt319ONNf+Ipxf/yEpniWb9SUEUxk+HBtkExO478rmhhb1fbrC2BYmaNwPzZsC65VVeVrX/sar732GqlUikQiQWNjI6eddhrPP/883//+9/noo48444wzjOXtLSeEi92XyWQIhUKoqmoMVotGozQ2NhoffsRiMaLRqPGc04e/BQIBKisrKSsrw+fz4XK5sNvtJYOzxK7T3yPD4TANDQ2EQiEymQx2u914//V4PMbvIyGEEKI9UskphBBC9BDFS75byuVyu1zJCYWg0efz4Xa7jSXvbU0p1quVipdqtrdcUK/g9Hq9+P1+QqFQuxPVc7kcwWAQu8PJygOmoSlKpy1Rb5dSGPKzcvS3GfHJfZhUxQjZ/H4/uVyOZDJJMpksWUKp99csfjwymQzZbBZFUYwKPEVRqKioYMqUKTzzzDNGb8lkMommaTQ1NRmVsHqwmk6nqaqqYs2aNRx99NGce+E0vopb2BIr7W0ZcFgIpwqPQziZpcxpYWM4Rc0fPiKeyfPImaM5Y3wl/1q4hSMf+pymeJYL9+/Lr782lB//ezk/f205TfEsoyqd/OWsMRz24Ocs3hzj2OEBZi5qYOowP7UtgtXRlU4+Wlva4mDs+An06dOH+vp6xowZQzabpaKiAlVVKSsro7y8nPHjx/P4449TW1vL3XffzVdffUUmk6GxsZHx48czb968Vg+Nft/q/ROdTicA//vf/3j77beZOHEizc3NxlR2fXCTHvhXVlZy9dVXc/WdD5Loe0ar7c+pi3D0sADvrg6y/wAv1722kikDvTRsvZ8b4hl89sKfw2ZVIZvXiKZzRLfe5wrQEMuUnHfWimZ+euRgACb397C6KVFyncsaElQHCtWAk/q5GVvl4tG7bmXU0IEMGjSI6667joqKCv70pz/Rp08fTCYTCxcuxGw2G+Hahg0bqKysRFVVBg4cyLp164xenvqSXqlu20ZDIWP3k7PY0RQzipbFlEliSQZRaP1+ms1mjR6n+nuY3qNT0zRcLhfRaJRYLFZyuZYT3/X3ie1NfM9ms9JrchfpS9aj0ahRZWu1WrHb7caydr21QHu/e4QQQuybJOQUog27+seyEEJ0hOID5Zby+fx2qw6L+861XGIO4HA4SoLM4mWYu3OQmMlkjJAgEAjscBltbdVBHT9FfVeoJhK+wTRUT6VyzTskEglj6aq+rF7v01d8f+gH1HolrV6NqIef+vL96667jt/+9rc0NDQYA4b03p3l5eXGUn9FUUgkElitVtauXUsoFOKFF17gqbc+5cd3PMjp4yp5dv62ifDBZAavzUQomcVrN1PbnCSd00hv7R85c+EWDh7s5V8Lt9C0deDO8ws2890p/QGM05ZuiaMBqgJ3/G8tM04fxfemDKAumKI+mtruXTfAa+PKb17AbTf/imw2y+bNmykvLycUCuH1emlubiYYDLJp0yY++eQTAFatWoXZbKa+vt6o0GpqajLuA/3+UFWVu+++mxEjRjBo0CBeffVV3nzzTW644Qb8fj+bN2/m/vvvx2w2c/TRR3PaaacxYMAA7rrrLn7+858zbdo0fD4ft/7sB1zn7scV/1pKKJnl8kMGcMus1dw5ey1PnD2W35wwjEc+WU8ym+fvX9bzzHnjOXNCH0yKwvQXvsJmVnntu5MAMCkKv3qzMAH997ML91U2rxFMZPn9O2tJZfO8uGgL//v+/miaxveeX1Jyf4WSWfKahs2sMnPRFmYu2sKYT+7l9p9N54knnmDLli2cf/75AJx66qk4nU7Wrl3La6+9xtVXX803v/lNcrkct956K6qqMn78eF544QVisRgWi8UY7qWH6XrouS/1MNRQiFbUECkbScI3mIR3IHmzrdX51GwKR7gOR6gWT9Ny3A3LjL/jinsN60PV9GBNH1KkKArxeNzYXnGLjmItJ75bLJaSoUctJ77r/0pAt2P6fR6LxUrahuiPT3EfTwn+hRBCSMgpBB3zx7IQQuwpVVXbrfjRKznbCjKLK4n0ZdS5XI5EImEM/PF6C8uNk8lkm9vfHblcjubmZiMkiEQirQ7+AbIWF5tGnth9AadOUagfeSLlG+fgUnOt+msWzqIYj0MulzOWqras6NT7/OVyOW666SYefvhh1q5da0wd1w/G9VAznU6zceNGPB6PEazE43E8Hk/heuxWwsks8Uxp6PHh2hDHjyzjL3M2cvyIMi57YQluq4louhBoHVHtY8mWOBaTggKkcxpHVvtZ2VgIZjw2E5FUjkqXBatJJa9BYzzD2U8vRFXg2fPH887KYMl1Lm2IM6zMwefrI7itJv5+3jh+//vfEgwGCYfDzJ8/n3PPPRdN0zj00ENZsGABFouFlStXMmLECOrr6xk8eDDpdJpAIEB1dTWvv/46ZWVlJdej35833XRTSTVxPp/nV7/6lTFwS39cXnnlFd544w3sdrvxerj33nsBiFu8fDThB8a2b5m1GoD6SJqvP/ZlyfWGUzlO+kvrqtK2+pl+uSHKUQ/NbXX6jI/XM+Pj9a1O1z3yyQbOn1zF43MK09MDLiv33XcfmqYRCASM2/rWW28ZVcUrV67k0ksvBcDlcuF0Oslms3g8HlauLISuiUShatRsNhvPX72HYS6XMwIfPZjf22QtLpoGHETj4MPJOAKQzxUqw9t5b8mbbcQCw4j5q2kYegyWRDPlte9Ttv4zzJkYmqYRCoXweDz4fD6i0SiJRIJYLGZUdCqK0qqis6UdTXwvrgBtOfG9ZfC5vSr6fZ0+cCyRSJQMgNODf/1DPP1LCCHEvkdCTrFP6+g/loUQYk/oYZuqqq2CTL0yMBAIANsOqvVl0TuqDEqlUrhcrl0euLMjekjgdruN4K5lINA04CC0rl6i3g5NUUmOmEqfTZ8YIZreY7N4ij1gVBvCtopOPUDSl6uffPLJTJw4EY/HA8BLL73ErFmzmD59OkcccQSqqtKvXz9mzJjB3/72N373u9+hqipPPfUUPp+Pk08+malTp6KpZj5PWnllSQMAM04fxRUzl/LYZxt56pyxXHxgP179qpH14RTfqCnjNycMI57Js6Y5wU1vrabcaeaViycRy+RJZ/Nc+nxhcM5T54wl4LRgUhSufW0FAF+vKePnU4egAXfPXmsEprqXFzdw3dFDeG7BZn58xCCGBhxcd8UlqFqOJ598kgULFjB37lz+8pe/EIlEuP3223E4HPzlL3/h2muvxWq18vLLLxOJRFBVFYvFwtq1a0uqYfXeeslkkkQi0aqVQnGYX/wY6KG9HnYYQ2FyDahjUm1+QNkdXlvaaPzflEtjT4eN7/W+onrArt8+j8djLNc3m81GC4Wbb7651fb16jadxWIxQk+Px4OiKGSz2ZLnbG9eMq2hsKV6KptGnrj1vWTr32nqTvQpVhRQCufL2P3U15zMppEnUrX8dSrXzEZBIxKJGP16VVUlFosRj8fJ5/PG/an3It4VxdXzxVp+YKWH1sUtS1oGnzLxvZSmaaRSKeODNf01YLPZjOF4+uCidDrdq5//Qgghdp4yefJkeccX+5w2/1jenQojTStsTcuX/LEshBA7o2WgY7VaS0KP4um++Xweh8NhLKnc1QM2vXeiPs22M+jVNOl0mnC4EOpoKCw56gYydn/3V3ICaBq2TJjDF85A2xpq6mGmHhDrlZx6qKAHdYWLayXnKa40bDl4RK9OzOfzbX7pgR0U7qdFx93WY0K6R88czTWvriCUzGLKpTnk09vJbO1/p99u/XZszwknnADAe++9ZwxoKa5U0wP9tp7zLb/069IHYjU1NZVc18opPyAWGNYznmc6TcPVvIoRc2YYH1a0/OACMJ4Ler/RfD7f7ntBe/eLTg/g9dDTbDYbz+fi0LO3SLr6sG7CuSS8gzr2sdU0HOF1DFrwD+yxQosIu92O2+0ueQ+z2Wx4PB5SqRSRSKTjrr8NxR9wFT9XWoafMvF9+4pbi5jNhZoevc3AvtbaQQgh9jVSySn2OR36x7KiAAqaolJfcwqhvpNK/lgWQgi9aqutXpkt+7XpwVEsFmu1ZFHv/dhy2fTO0ivfnE6nUTnX0fTKOo/HY/S4C5WNLFTK9xSKQsrqo8k7lEBwZasf64GBHja1nJpcHDrpPRHbCzN3haqAI7KemH9ojwjppr+wtc+kpuGKbiASDm//Alu1fJ7PmTMHVVWNZb9ASTuA3ekPm0qljInsxa8RR6iWmL/aqNrrEbQ8zlCtUQmsVwH7/X40TSMcDhvvEfoQGyjcR/oQm5bL9lsOu2krAM3n80YloqqqRuCpD3cq3p+e3MswWDWRdRPPR9vdD6O3R1FIeAaw/LCrGTT/afyb5pNMJsnn8yVD1fTqd6/Xi6IoRvjZGfT3jpYhdPH7kf6vzWYzKn+BktCz+P/7YgWj3q5FX9au9/HUWztks1ljSXtvCvyFEELsmIScYp/S1X8sCyH2HcXhTvHBaFsVOKlUqs2DUJ/PZ/R6bEkP1fQQZHfE43HsdrsxOXh7xo0bx7XXXmsMmrnxxhvJZrPcdddd+P1+rFYr9957L3PnzqW8vJzf/va3OBwOnnvuOd58800qKyv53e9+B/5+xM0eLnh2MZuj2w4mR1Y4eOa88YyudNLnt+8TK1oyfchgLx/84EC8N80mls7xnUlVXHn4QJLZPD96cSlfbY7z7QmV/OaEYURSOQ6+fw4A3xpXwZWHDwJgWJmDP75Xy30f1JXcrssPGcDyzVESuYncfUGhh6PH42Hjxo38+te/5qKLLuKAAw7AZrPx1ltv8a9//Yt8Ps8111zDgAEDWLx4Mffeey9+v5/rrruOu+66ywiJ0um00dOzOMjemX8BmtNbiGtD0HpQSKdoeTzR9dhsNqOCuOVzXf9qubxfbwOgT0Lek0FXxfRg2WazlVQle5qW0zD0mD3adodTTbiblm/7VlXx+XwoikIwGDRCWj3o1Cut9UrP4upP/f7U30OKq2r1CsC2AtDifqfxeNw4v9lsbjXESA99ekKlW1P/KdSNP7vwTWe1u1BNaFqe2kkXkl/4LGUb5pBOp1tNXk+n04RCIXw+Hz6fj3A43KXhoV6J2zKM1sPP4t85Nput5LXYXuXnvhJ+appmtH4AjHYZekis/87Vv/aV+0UIIfZWslxd7DO65I9lAK1w8DZw6x/LQoi9R1sHlC37BhYHCi0ra3YkEAiQyWTaDSADgQDpdHqHQzC2Rw81mpubt7tPFRUVxiChK6+8kq+++opZs2ZhNpvJZrP069ePm2++mcsvv5yrr76a999/nzlz5vD4448zffp0jjzySPbbbz+u/DDDqUdOYaDfzh3vrN22HxYVi6owc9pETvvr/JKQ8+nvjGNEhYNjH/mCRCbHJz+awqEPzKGP28L93xzFGU8toMxpJpLK8f4VBxghZ7FZ0/fju899RW2wdNDSa5dM4qTHv8QbXsv4hX/BZDLx3e9+l02bNvGf//wHTdOMSte//e1vnHvuuRx11FGMHTuWRx99lF/84he89tprLF68mCuvvJJXX32V9evXt1qqXkwPo1ou9W75bzAwkhWTv7srD2eXmLjsGSqja4zgUtdyWJA+VVpVVdLptDFwqTN4PB7MZjPNzc3GaT2xNYIlGWT0u7ehUAiHfT4fAMFg0Ah7bTZbu/1si7W13F0PyPWqT/1L33ZbYXTLALR46Jb+8+4eYhSsmkjtpIvYumOdf4Vb2w8NnveU8SG1HkirqkooFCKbzWI2m/H5fORyOUKhUI8OxFpWfrb3u2pfnviuV1Drlc7FH8qkt7bnEEII0btIJafYJwSrJlI3/pzCN539x7KigqZRN/4c1FxaKjqF6IX0qqiWQWZxFWXxQIldXW7bnuK+kG3Z00pOKCwp14PO7S27bGhoMP5fHHLolUQul4sVKwqDbMaPH88f//hHNE1j8eLFjBgxgtraWvY/4AAizr74nRYa46VLAhOZPIk2rvfwIT7m10fp6y0MpqlwWdgQTpHNa2wIpxlVWVie2RRvf3ltlduKzay2Cjgn9HWxqikBikLM3R+b3Q5bJ4T/7Gc/M/ogulwu7HY7mzdvxufzceCBB/Lpp5/idDqZO3cukyZNYsmSJXz22WccfPDB/O1vfwNKQyi9/9uutAYIhFZhS4VIWb09JqSzpcP4mpeT3Dppvjgo0QMyk8mE3W4nn88bFVOdXQmYSqWw2+0lS9YVNCrWfcDGkSdhDKbpVhrlte+joBnhWD6fJxQKlQScHo/HmOi9PW1V8ukVmfpX8RLm4uE1mUyGRCJR0ophewGoqqrG1HfYthRaH+TSmeFe0tWHdRPPB7TO/VC6mKKAprFu4vnYP9iIPb6l8MFDUUWn3g85GAzi9/uNKs+eGoTt7sT34g8vigPQnno794T+morH40ZbB5vNhtvtNoZ36YFnT23pIIQQopSEnGKv11P+WBZC9Dxt9cpsq9edHhIUH/h1Br0PX3tyuZwxRGFPxGIxvF6vUZW5Pf369ePQQw/lz3/+s3HaY489xuDBg7nxxhsBjMEmAJFIBK/XyxdffMHQETV8eewoFBQOeWDnKtuvOnwQlz7/FSfUlAGwJZZhoM+G12ZioM/OiHIHZlUhm28/ZDl9fCX/Wti6N/Loyq0hJ5AzWYmpLmr6+ti8ebNRWadpGldddRXHHXcczz33HE1NTVgsFurq6mhsbKS+vp5BgwYRCoVYvnw5Rx55ZKtBTsW9D51OJ+l0mmQy2W5Vo8Viwe12YzKZ6LvxE9YOOZ6eEtIF1rxLKBgsOVVRFGP4j/581Jd8dlXVn76s1Gq1kkgUHlOHw8Gw+HLqtfzWoYLdS9HylK3/FIvFgtfrbVX9Z7VajYE2uzO5G7bd78XPreL+jcXhZ/Hy5eLwqjiU1j/gKZ7+rVeM2mw2I5TVq0BbvjfuabitobBuwrlb2wp18WOoFHrFrptwLiM+uQ+FQtV1MBjE4/Hg9XqJRqMkk8mS8LO4Krc36KiJ77uySqE3KP6QRh/epU9q11s66IFnZ1WoCyGE2HMScoq9Wk/7Y1kI0fWKp9W2rM7U6QdqmUyGZDLZbcv29J577emISk4oVMFls1lcLhehUKjd87lcLm699VZuuummkjD00ksvpW/fvtxzzz3MmTOHXC6Hw+FAURTKysrI5XKcc845LFq+hu+/EOLM8ZX86thqfvlG60E/xY4a6mdefZRo0dJ1TYPr31jJzGkTqW1O8sm68HYDToAzJ/Th0ue+2uH9kMgrHHHEEcyaNYtMJmMsJb/77ru57777ePTRR3nppZeIRCJ4PB6gMNl7e/cZYAx0iUajRjWcXsVXXOmoqiput9vodxmJRHCF3kcZfGyPCekq6z83vtfDW5utMAG+uM+dPtjDbrd32ZLPdDqNzWYjm80aIXEyFqTvijfYOPLk7q2G1TT6Ln8dp5LF6/WRyWRK+jjqwaf+uHfsVbfdv7E4+NR7NxZXfRYvd9erNovfj1pObde3YbVaS9oY6O+dbU2C31EF6JbqqR0/RX1XqCYSvsE0VE+lcs07xsmRSIR8Po/H48FkMhGLxVr17eztYV97/aDbWtnQVvjZ1uCj3krTtJIwU3/eW61WYwCg/vPOrmwWQgixayTkFHu1nvrHshCi47XXK7O9wT/FB2M94QCluLdee/RgrCPEYjF8Ph9Wq7XVga0eZvz+97/n8ccfZ8uWLbjdblRVNfqWORwOMpkMgUCAlStXcvjhh/Pll18yatQoHnroIUaNGsWW5iAQoCGewWff8Z8cE/u5OXZ4gCOG+JjY181fzx7DWX9byJvLmnhzWRMjKxz89IjB291GH7cFm0lptVQdYGlDnKnD/Mb3NqeHqVOncu211+L1eoHCwaw+bTeXy+FyuVi9ejVHH300tbW1HHPMMbzxxhuUl5czevRo6uvr8fsL29RDUv35pP9fr3DUl4Xqwy70Jd+xWGxbZWI+0WNCuqF171DptpGxqUaVVzabJRaLkUwmS143+uldueQznU7j8Xjw+/1kMhmCwSDZbJaK6DsEqyaS8AwAtRuGOOVzOMJ1DNj4sRFkFreGsFgs+Hy+Vqd3tuL3P50+8Ej/0oPsllWfLcNPnR546SFQ8SAt/blQ/J7VXviZy+XImJ1sGnli97dqUBTqR55IYP2nmDPbqrRjsRi5XM54L4xEIq2WrvfmYK8925v43rLyU28hAZRU+/b2ie/6B1exWMx4zlutVtxuNx6Px+hfqw9YE0II0X0k5BR7razF1aP/WBZC7LriZZjt9ZIrHqZQPNG5Jx54aChk7H5yFjuq2UbE4SKZADXT0Gb1d/Ewkd29PXpVjh4+eTwe0ul0yTJVRVE47rjjmDBhAm63G4CXXnqJ999/n9///vfGdOb777+f5uZmHn74YW655RYuuOACnn32WRoaGnjxxRf53V338N8Th2NSFKa/UKisnHH6KK6YuRS/w8yz541nUj83L02byF2z13L/h3Xc/2FhGvp/L9uPi/9ZuMwfTxnJhH5umuMZrpi5FICpw/xcf0w1NRVO3rx0Mhf/czEbI2nOGNeHfy1su0XI/I1Rhpc7jO/LPQ42btzI2rVrURQFRVH45S9/ydChQ7FYLLz22mvU1dVRX1/PlClTuPPOO1m2bBmff/45iqIwefJk/v3vfxt9Dov7Ger/108v/rl+Hv2xdLlcuFwu42dl0UVE4/sTdVZ1z6T1fA5vfBODN38GSiGUg0I/1x0tq+6qJZ96KwB9cnLxfiloDFrwD5YfdjWalu/alRxaHgWNEcv+hdfjJpVKlVRq6r059crO7qZX3bYMsFoud3c4HG0OOdIr4PWWAcWDXMxmszHESD8vbHsPahmAru5zUI+oYAbQFJWmAQfRp8UH1Mlkknw+j9frxWQyEQqFjIpOfep6y/tyb9Xec2dXJr63DEB7Q/iZy+VIJBIkEgkURTE+1NHf4/QPEvQPt/Z2xX/HaIoZRctiyiSxJIOyik0I0S1kurrYa22uPob6mpO6fpl6W7Q8fZe92uqPZSFE21oGmPqBUnFVZssKkd5wkKShEK2oIVI2koRvMAnvQPJmW6vzqdkUjnAdjlAtnqbluBuWoVAIFsvLywkGg60OnlRVLfnS76+WXyX7s3VadvFEZj0k1v+/J0uN0/YAS6b+arcv3xmuOGQAS7fEeXtlM6Nn34o12bzjC7XBbrdzww03GH1Jd8RsNuN2u41K0Ww2a1RHFh/868/tmK2MOeMvK4Q+XflhnaahaDkO/upxnMlGo9pUD7Zh2yCq9ipX2/ten75usViMPq562Lmzy9r16ilVVUkkEkZP0LZaCBQmdF8IKF06oXvEV88yJLm2VSisB5zZbHaHLQ96ouIhRy17GBf3aywOQPXq0OL+rXo1aPHrQDWZmX/Iz0nbfN3/4TSApmFJBhn97m1tBjVtDZLy+XxYLBZjQJFobWcmvrdV+dlbep7qH+pYrVZMJlNJz9yW7R96qz39O0YIITqbhJxir6ShsOSoG8jY/b3ij2Uh9kVtVXu0V5XZVtVHb5K1uGgacBCNgw8n4whAPlf4AGZ770+aBloeVBOWZJCqug+p3PQFAbtq9I8srsosrhLUg7K2AsviL03TjAFETU1NHXqbVVXFarMz5/AbyZusHbrtjqBmU4z77/UdPt6nrR6wepCp0x+fllXGehgEhSBxvXs4q8adS9eGdDB2xb8IbFlAPp83Qiq9Z6N+u/TboAflxc9BZRf2VT/o14Oy4u0WfxUvjdVDMn3/bDYbkUikzZC1oe8BrBt3VuHKOvNDT60QwgxfNpPq2HJisVjJQCqTyYTf7281fGhvUFzx2fIDqZZVn/r7vh4C6c+rza7BLBk/rZtvSWtDP38ET8PSNn+mqio+nw9VVY2l6l6vF6vVSiQSKWkLILavrYnv7f0t0Fsmvhf3rNXfQ4uXtffkfW/LHv8dk2imvPZ9ytZ/hjkT67odF0LscyTkFHulSMUoVh9wWXfvRivb+2NZiL1VW0ML2hv807Iys7cdBLSkobCleiqbRp64dRnmboZVWyvEFC3PsA3vMnDjx+SymZLAsjjM3JUAxWQyEQgEiMVixpLT3aUvVbXZbEal3uc15xNyd2Nv5LZoGv7oOvZf9rRx0KlXle2MlkFmWwF9cTgHhWFPxe0T2qMoijGsyGKxsKFsPF8NOVn/4Z7d7u3Z+pwZvfpl3Gs/KqlEU1UVj8djTDJPJpNGVWoqlTJ6Fba8HW0t2W/v++KATA889edzcRWy/p6wK6HqJv8oFlWfBgqds/w/n0NBo2b5vxgYW0U0Gi15Le3NAWd7iqs+ix9b2DbkSH/OKIrC2qFfp67PFLTu6KHannyOyrXv0m/ZK+2eRVEUfD4fZrPZqOD0eDzYbDZjErvYfcUT34v/huhtE9/1/rT6l76CQg88e3Iv1874O6Zq+etUrpkthR9CiE4hIafYK22oOYWGIUd1z8CB9uzEH8tC9GZtBZl6TzZofTCyN0xgbY+qqmQ8fVk99mzinoEdG05pGu7oBvrP+xv22OYO2aQ+4bupqWmXA5iWy/NaVuYtH3BMjwwv+tS+x9DaWcbyaVVVjeEamUzGOEjWD6jbCzLbGqBiMplwOp3Gkup4PL5L96s+9dpsNpPL5dhSNobFQ7+JpgCdGNINWfgPBsVXG8OootFoSVhgt9txu93k83kikQiqquJyuXb7drZHr2i12WzGc0rvg9fWsna/308+nzeWhrcVoiZdfVg9+ixingEd/nr0xjcyds0ruFKtq6GL74/iClX9Z+0t8d/dn3cXt9vNjBkzGDZsGBdddBErV64E4Oyzz+bUU08F4K9//Svvvvsuffr04aabbsLpdPLKK6/w9ttvU/XtX3DpcfuBAsPKHPzxvVru+6DO2L6qwCNnjmZEuZO56yP87JXlANz69WEcMzwAwLWvruCDtSEOH+LjDyePIKfBu6uaueHNVXxrXAVXHj4IKN3+T48cxOnjKomlc1zy3FfUR4qWmWsaBzqauaRvAw8//DAADoeDV199lZtvvpn33nuPyy67jMMPPxyTycTLL7/MSy+9xIgRIzj00EN58cUXWwXeomO0/PC0rZY2bVV+9pTwUw879V6l+rJ2vZdnT5F09WHdhHM7foirpuEIr2PQgn902N8xQgihk8FDosdr6w9np9PJPffcY0x6/fWvf83GjRuprq7mxhtvJOMbwK/+u563VwWN7Tz4rVFUuCyc/fT/s3feYXKVdfv/nDK9724aKYT0kITQqwoIigiIiIqooNSfqPCigq+IgDRFX4oISpGOKAq8IF3aC0JooSUkgfTet0zvM+f3x+R5cmZ2Zuvs7iQ5n+vaK9ndmdkz7cw593N/73tB2e1/fdYwrvriBGKZAgfd+h5AzYPh+745nS9Naeaql1fy57fWA3DEhCDXHDORfNHgshdW8PrKcNnt2zWFu78+ndMeXsiF3/0qR486EYBp06Zx1lln0drayo033igPvi699FJaW1u56qqr+M1vfmO5ACwaimqjuJWuTPN4ornFfEd3ZULn3Mta2Zebg1NZtM05Vnf3naIQ94xk6aE/Y+z8hwhunt/vm0wkErL1O5HoeoxMFC2IL1VVawoshmEQjKxg7ciD+72NdUXV8LQtIZvNyuIU4TYzO22gfLRcXL6W01jTNLxeL3a7vabDsSvM4qYorMnn82jtc9izdRWrZ5xC3LPbAJxsriudbCa3EqF0Au7xeAiFQqTTaRKJhCz4MTeap1IpOjo65GvH6XSSTCb7LeoYhoHNZkPTNLkPsdlssq1duG9FW3smk5GlRrVQs+uY8NYfaB1/OJvq6Egatex5JrR/iN2mE08kykqoVFUtK0eq5WQVmaxdOV1789j1JCO13qJqOp3mggsu4MILLyz7+Te/+U1OOeUUbDYbd999Ny+//DLf+ta3uOeee3jvvfe45557+NeTT/F/SzP8bcWHALx0zj78a2Fr2e0cP72FDdEsZz/6KXd8bRoHj/OzeGuSIyaEOOzP7zMm4OCuk6fzpXs+4uLDx/H9f37CktYkL5y9N6N8dp5Y2MoT225T3P4Ir50vT2vhc7d/wAFjfPzq8+P58b+WbP+jisIPvnwQ//rtBTidTgqFAt/+9rdZtGiRvMgzzzzDnXfeia7rPPzww7zyyiusXLmSc845h7/97W/yNWuOLrDoP/VqfDcvug7mQoHYf8XjcRm5YbfbcTqdcqxdCJ5DdewUHrEXa/f6DsZAxKUoCinf6Loex1hYWFgILJHTouGpduCcz+f51a9+xdatWznkkEM4/fTT+d3vfsePf/xjfn3llbwx88c8dc4BvHL7BwDsHnKym99OttD5AOaV5R386w/v8sZ5+8mfVTsYBrjkueX83/IOPPbtgs41x0zkuHvnkSsUeeqM2Rx154dlt3/K7BE8t7gNFIXLXtvCwy//EpfTyYMPPsiSJUtQVZUzzjgDwzA44YQT+OpXv8pdd93FK6+8wnHHHcdjjz1Wt8eynlhtijs3tbIyq7kkzELmYJ8o1AshSNQq6+ku91K4MIrFIluG783KPb4KGAOXAahqGEaRNbNPo7jgHzRteK9fN2cYBslkErfbTSqV6nRSpapqWbaYcGmKf2G7c7NYLJaJNMHwCuzpcEMVijiyUcbThtrUtO1H21/PQsiE7e5kIbhpmiZPqs3ipaIoeDweKYREIpFeuXFqiZtmtPB69phzU91FupFLn6OlYmxQnIC7XC7cbjcOh0OKl6JoRbg6Rf5ge3u7bIl3uVzE4/FeO5KEM9TpdJLL5ejo6Ch7HMwCu7nJOJfLyd919TcVDIatepXQ+rlEdz+ErWMPI2P3oxQL3Rc8VWTktqx5g6YNc2lylV4fkUikTHAReY2GYRAOh/stVPR2/L+r7wdCVBX7AbvdLkVwwzDYvHkzzc3NOBwOUqkUHo+Hvfbai9tvvx273c7ixYuZMGtf3tVKxSUjvHYcusqacPkC7yHjAjy7uA2Afy9p49DdA7y3LkZHKoeuKoRcOq3J0nO/aEuSoEtHUxU0VSGZ2/7Ym2//wLF+Fm0uuX8/2BDnL18Pdrp/k4b52JIqLbh7PB5mzJjB8uXL8Xq9+P1+wuGwfN/n83ni8Tgej4d169axxx57yMuqqlpWQmUxMHTX+G4+phH7XcFQNb6LReFEIlH2WVtrUWcwaN/tANbN/Gbpmx3kOMbCwsJCYImcFg1PPp+no6O8/TabzbJ161YAWcABMGzYMJZviREpaHSk8jS7bbQlc1z8uXHc+PpafnzomE63356sfcBQebC9Mdb55ElXFSLpvPy/+JuCE/ds4bzHSzmcRd1BzhniC5/Zj9dff730M9OJj8fjkSNe77zzDr/73e8aRuS02hR3Pqod9Ncq/hFuqcqilEZHiJfduS+7Ei/NJSfm7MtqJz7hEXuxZurJ2/74AJaciNs3DNbNPAW1kO23EyKVSklRS7hLKvM1YbuYaS6KET8Hyka+xf65afUbbJryZah7zU/fGLP1fXLZLKlUqscuYzFCLcbzFUWR1xUuod7mmvZE3DRjFunaRx9A2+6fJecM9lqkc2SjtKx5g8Dat9FztR1mIn9TiJdOp5NEIkE2m63q6hT33+PxEAgEyOVyxOPxHp2YC9HSMAxisVjVKQbDMGS2KZSPtQP4/X4pBmQymZrihJ5LMGbDm0zq+JDVSgv50XsRdo4g4d2ty881d2QN3m2fa6oCgUAATdMIh8OdxNhAIICiKHUROMV9H8wFpL6KqOaCGEVReOutt/jrX/+Kqqpcf/31UqR2Op0oikI2m6Vl5GjY1tFz0sxh/O+CzuOrIZdOdNuxViSdp8ltI180mLcxzqcXHYxTVzn+vnkAPL5gC4+eNotM3uCf8zfLY7TK21/elmK/0X7smsKRE0M0uWxlf3OYx0Y0nac9kSEVb+VrX/saDz30EAcffLB8vkVMx8knn8xbb70lHcVbt25l1qxZLF++nGQyicvlQlEUYrFYvZ4ii14gcmCr7YsqnZ/iOR2KxvdisUgqlSKVStVc1DG3tQ8E4RF7sW7mKaVvBnphss7HMRYWFhZgiZwWOzi6rvODH/yAq666Cii5Ewo2JyAOgnX8ztLJ5+pw78e+ax1sm8kUiowNOMgUiswc4SHk0stEztEBJ1sT278v6E6+8IUvcN9998mfTZkyhV/96lf4fD5++MMfAqWTy1Ao1Ottrje9bVMs6g4SoQkkguNp3eNIq02xATAH99fKrjKLeYPpYOgPXY2Lm92XZiqLeszipfmrL6Q9w1m713cYUAdnJYoChsHavb6Dc85GnMmtfb4pMVLrcrnK8jWruTXNhTrCSWcWNStpWv8umyd/aZv7cGhRjAJN69+VgmVPx6vN7iBFUeSItjl3VoxFiiKjWu8fh8OBx+OR49jdiZuV6LkEw1e9yrBVr8nFp3Rod5K+MVWb7IVI50usZ2RmI7b1H5NJ90yMNQxD5gp6vV4CgUBZXqdwdXo8HunqjEajcrzcPPJe7b0lRuM1Tet1rqd4ThKJhBRhVVXF6/Xi9XrLij0qF2ZUVaVYKBCILqVZaScSiZDJ5koTCroTQ9VRinm0fOcJBXOrdjgc7uTqNf9uR43p6Kuoms/nSSaTxGIxPB4PJ5xwAieccAI2m4077riD559/nnQ6LfN/NU1j3cYtUDJVc/Ks4Zz1yCedbjeczuN3lk5bAk6d9mSOqcPcHDDGz5T/eYvd/A7uP2VPjrrzQ244fjJH3vEhKztSPPbdWUwf7uaTLclOt9+WzHHHO+t5/qy9S2Lp1urHKIaq4/V6mTx5MnfeeSf7778/6XSaaDQKwMEHH8yUKVO45JJL5GdPoVCQzmSxf3A4HDgcDpn5W/llMTTUEj8rj5lsNpsU56F8Edh83FSv93xXizoulwvDMMpyPOtxvLajH8dYWFhYgCVyWuzgXHbZZfzzn/9kzZo1wLZQf8V8EJzn2mMmcN2rq/t0+7UOts385Kml3P2N6YRTeT7elCgPra+Cw+Vm/PjxfPLJ9ttdsmQJp59+Ol/4whc488wzufbaa/u0vfWkapsi9KzMSVFkOUbOGWTTlOPYPPlYq01xABGuzGpj5uZsQXEwLsaeGvHkqqe5l2bEuLHZfVmteXygMFBYO+vUbdlVgyzkKaVMzLWzTmXSO7f06v1VK1+zK4Ezn893K2pWoucSjFj6HJumHD+0I+uGwcilz5EJbyW3LS9RjFcL90x3CBFNiBVC7DM7PV0uF4AU2cRjZRb0MpkMkUikX+8/BYNA+1JGptbhbHOiqCoJ1YPhcJM3FHTFQMkkMaJbKBby+P1+0uk08R4KnGaEoCnGKEOhkBQla7k6zXmdTU1NZSKmpml4PB4cDgfZbJZoNNqvxyKdTuN2u4lGo+Tz+TIxwOyAymQy5HI5NE2jWCxis5Xce7lcDgUDe7qjy7+jqirBYBCgpsAp3J2Ntm8dbIrFIul0uixXVVEUFi5cyP77788HH3zA9OnTWXbXw9AEw702HJrSaVQd4K3VEY6aFOL1lWG+OLmJ+97fiEJpQbtoQDiVx7stSsgAOlI5DGObOOooHRdWu/0HP9jEgx9s4vAJQVoT5SPOWxM5Aq6S2D1+/HiGDx/OrbfeytixY/nc5z7HsmXL8Hg8nHPOOfz4xz8uc9Y1NTXx6quvys+dZDKJpmm4XC50XUdV1TLBzJz9awmgjYE5wsRMZeO7yP2sdqxVz8Z386KOpmlyH+fz+QDkxI05cqU37KjHMRYWFhaVWCKnxQ7Lueeey/r163nhhRfkz1pbWxk3qoVNdk06KseHXPz5q1Nx2lSmDnNz1gGjuHvuxm5vv6uDbTMfrI/xxbs+otlt409fnUI8W35gsT6SZpjHJt2cRxy0D3PmzJG/F+VJAPF4XI7ouVyuTmP6g0Vd2xQVBVAwFJVNU44nMnK21abYDyobRc1fAnEwncvl5DhuIxT/dJd72ZPR8VrOy6F2nG4df3j920d7g6qRCoyjdfzhDFv1atcX3Zb5JcbQzSdmZvojalZj2KrXiIycTco3umeLJfWmWMCX3MyE9g+JgWzjTiaTZVmSQrSrRFEU3G43LpeLYrFINBqVDhugbHxQVVXZ2i5G0oVonM/nO123L4iRX7vdLl246XQaXY/i9/vZunUryjbnkcNTyp0Tzp/+kM1maW9vl9EGYoQ9nU5XdXWK7XK73fLyQvAV+Z71GLsU+zkhPlc6oETOnXj+AFk41dMsYU3TZM5mJBLptE/1+/1omtZv8XpH5ZZbbmHq1KmMHz+eRx99lKeeeopXXnmF+++/H1VV+ec//4lhGNx3331cffXV/OhHP+LRRx8lnyg5Ir82Yzj/u6DcxXXbSVM57/HFPP1pGyfOGMar/29fPtoQ4+01petsiGb4zw/2xaap/OaVVQBc8/IqnjljNrmiweKtSd5ZW/v2/3bqDIZ57azpSPPjfy3udJ+WtibZLeRlwYIFfO973wPg//2//8eiRYvYuHEjt99+O4FAgD/+8Y8A/OQnPyEejzNx4kRuueUWDMMgEAjg9XqJRqNks1n8fj+GYdDW1tZpgVIIaKJ1W1BL/NwVX2dDjTgWqaRa47tYPIT6Nr4XCoWysXaxf/N4PGUudrHI1hN2pOMYCwsLi65Q9t57b2upxKLhEQfOGzdu5NFHH+Xdd9/l6aef5qOPPgJg/vz53HLLLUyYMIFfXnYFmdDuXPniCl5atl0k3D3k5H++PIlvPrSAEV47Pzh4NFe+tJLDJwT55ZHjOXCsn3fXRvn+PxexMZblBweNxmlT+cMba+VtXHvMBE7YswVNUXh+SRs/e3oZPz98d744pYlUrsAFTy5hZXv5yfHp+44kXzT420ebAXj6aIUH7r6TTz/9FIAZM2bw05/+VK4W//rXv6a1tZUjjjiCYcOG8cgjjwzwo1tOWZviQAgRxQIKhtWm2A3VhMxKQarSISC+H2z6mntZ6b6s5rwcavGyJ+RtHj454nIMdejXDZVinumvXtkpZ1GcbIlxarNL0/wvINuhRWFKb5+DypN281fK1cI708/qPj+y3hgGilFgxrs3M8Kek+PXZjRNkyU7xWKxTOwU4+WqqpJMJnvclCxcjKKwqFgsyve1EJDNTs/uEGPyLpdL3qYQEc1/0+fzydxqgGAwKJ2LQtTLZDKk0+n+OUkrCpfi8Ti5XA5VVfH5fNjtdunqNAxDlhXBdpG5nrlyYlva2tpqXsZcOGKOX0gmk106oHRdJxAISEdr5fsiEAig6zqRSGRI9sM7EmYHnK7raLrOf/a5qGrUwlCzV4uN/+ddyB133NHj68ycOZN9992XBx54QP7M7/djt9tlRm0gEKBYLHa5j+1qX1pLAK10g+4In6E7O+bG98rjOqh/47t5OkPs9805ntVuc0c4jrGwsLDoKZbIabHTYaCw8Khrq5YHDAV2TeGeb0znuw8vQs1nmPHyL3tUvXHVVVfxm9/8pqqjaKAYlDZFKJVfAGN28TZF84FvZVZmrcD7emc+dUd3uZfVRse7Ei37m3vZiGwZf2SpVKcB8iYxioxc8gzDV70q88PMY+hmkdwsapqFNsMwsNlsBIPBmi4782tCjF52d/Itvlqb92TVrO/Q52bwXj8mpSbxmSv+RVPrAnK5HC6Xq1NZjECInUK4MwwDXde7zJWsxCxuZjIZEolEmXgm8t3EeLt4fsRzUNmiq+s6LpdLFuxkMhlSqVTV7RdCYmtrKwA+nw+Hw0FHRweFQkGOVgqnmHB898dZquulzEKbzVZ2f4WrUwiJ4nEUzk7Rhi5G/vuLruuEQqFOreyVKIpCS0sL0WgUn89HoVCQ0R5CADY7oGw2G4FAgHw+X1XgFAJWZcO6RWdBU+wvAPl5ls/nWTDrTOKB8UMbZ1GJYeDpWMHEuX+uy815vV5cLheJRIJMJtOlK7g7eiqAdjUCbwmgQ0tl+aP5eFBQy/nZ0+dOLHCKzxrxOSP2ceJ116jHMRYWFhZ9YeiXayws6oyCgSu6jkRoQkMcLGcLBt99eBEYpe3q6RZdfvnlA7pdlVhtigNHrazMaiNMIrtsoE9CaomVlWKmmaHOvWxEDBTaxh1Go7SGg0LH+M8xPb4AtcI525WoWYkQ2rxer8ySM3+ZXbnmkzBR8NLVGKV/40dMdvtYOvHEkgA5kPubbfdt8rJ/YV/zNtq2xut8vpRP2dHR0en+FwoFEomEHAEUPzOfENaiUtysNbYsHh+xiCXy1USep2hoLhaL8kRYbFc6ne7xfsHlcuF0Osu2I5/PE4/HicfjOBwO6fz0+XxSgOytEzGfzxMOh6XjVeR1plKpsjZ6IWgKYUeMV4pyomQy2a99iNh3OhyOLu+D2UGlKIp0oAoxQDg9xX7OZrORy+WIRCKdbssSOLdTKWaapw+EmJlMJuX/za9jZ8cq4v5xMs+7ITCKuCNr6nZzQsz3er0ytzUYDMoFpd4I/V21hdcSQM2j02AJoENNTxrfxb9dNb6bjxkr95/m952qqnL/5vV65WdhOpOlbffP0EjHMW3jPmNl+FtYWPQZS+S02ClxRdaQCI7fqQ+W64nVpth/KlfkqwlC5hOKnohBfd2Onrgvu8q9FM2vjZZ72YjEW6aQc4WGejO2oyhkHAE6AhNojq4EkK+3aqKmuUCh1uvW6/XK164Yj+7Pa9fr9dIS+QT7iiKL9jixpEMOYDTG5MWPMSa5gpTDQTgcJrBN6FQUBZ/PJxuSBUJkNAxDtp673W78fr88Wax0PQpxU1XVPhUKmfPVhJNUnNQKFEUpc+LUun3xvAkBMZFI1BwJF7mVogRFjMLn83lZGNMb0VHcnsjfFAVMQtgUhUViTF18Ccen0+nsVRxANbLZLA6Hg0SielM2IIUe4aAVIoM5V9XsnhWPvWiWF2PtYiQ/Go3ucgJnLUHTHKUiFu0qBc1q+NqX0rrHkYO09T1E1fC2L63rTaZSKYrFIj6fD1VViUQi+P1+gsFg3cqqLAF0x0c8f5WfNZXHmb1pfBdFYOl0Wu7THA4HqRF7knMGh+Be1kBRyLlCxFum4GvtnJNrYWFh0R2WyGmxU7KrHCzXA6tNsXdUikLmEXMoz1YSJ8LiILM/Jwa1ci8rxUylwhVndlhWOi/F73blExav18ttt93GhAkTOP3001m+fDkA119/PcFgELvdzh/+8Ac++OADmpubufrqq3G5XDzyyCM8++yzHHL08Tx06n7kigYfrI/xk6fK3+Num8oDp+zJMK+dpxa1cv1/Sgsdh08Icunnx6MqCre+uZYnFrZy2O4BrvvyJIqGwY8eX8yCzSWR5tpjJnDAWD+aqvDle+aRyZeLTneePI2Ln1nGkRODnH/YWBTDYFJgP/7xwN389a9/5aqrruKwww7jnnvu4fHHH0fTNC666CImTJiAqqrcf//9vP/++3zuc58jn8/zyiuvSCHT6XRis9lob2+vy+PtcrlwuVwUCgWGtX/ClI1LWVOvkjMz25zz01Y8SbAYI5lKSXdkR0eHbMAW7dupVEq2hquqWtYCDhCNRtF1HY/HI8XORCKBuq2hXYibyWSyzyKFcFWKQp5UKkU6nZYN4OJLOHCE6Cxct2Yx0jAMfD4f2Wy2R4KhyCBNJpPypFmUMWWzWdme3hPEiTMgx+NdLhfxeJyOjg68Xi+BQKDU8r5N/BSCqrmcSIz09pZMJiMbrGu5OYW42VXpkBjrF68F4fIUxR7CZduViLwzIEQxm822PUNz2yKIEDTF4kctYa0neFuXYEt1lMSWBpjCwTCwpcN4W5fU/abF4oHf78fv98vYBOHoHMhM1+4EUHP0iFlIMy+4WALo0GFeXPR6vfzxj39kwoQJnHHGGaxatQqXy8VNN90k85t1XeeHP/whwWCQiy++GKfTyf/+7//y7LPPUigUOPTQQzn+u+eSdwW59N8rZKEXgKqUji8mNbv5YH2Mnz5dOr655pgJHDmxtLj782eWMWd1hCMmBLnmmInkiwaXvbCC11eGAbjgsDEcP70FTVU465FPWNWRZsYID9cfNwmbpvLo/C3c/s76svs4Y4SHk2e2cNvKyVz67WM4/vjjee6557jpppsAOPHEE/nKV76Cruu899573HLLLaW/dcEFzJ49mw0bNnDllVeSz+e57rrr+MUvfjGgz4mFhUXjYYmcFjslu9LBcn+x2hQ7U+l0MIua5kxDsVIu8vP66mzraly8J7mX4iRzZ869HCjS6TQXXHABF154YdnPf/GLX5DP5xk1ahRXXHEFP/jBD/j+97/P/fffz3vvvcc999zDSy+9xHsdKp+97X0KBjz0rRnsN9rH++tj8nbOOnA3nlvcxt1zN/LsGbP520ebaE/m+elnx3HcvfPIFbafEF59zAROuG8ePrvGbSdN5fj75nPyzGFsimf54l0fVd3+3UNOCkWDSDrPEwtbeWJhKxgGr565J3PnzqW5uZm//vWvfPLJJ7hcLux2O4VCgQcffJC1a9fidru5+eab+fe//82//vUvbrjhBp588kl5+/l8nqamJtxud7/cdYAckRON1uFwGEc+z6S3/0jr+MPZNPnYUiFRX7M6t2VvKkaRkUufo2XVaxQVKAaDOBwOksmkFKeEe8pms+HxeHA4HNhsNrLZbE0Xpshj1HUdn88n8/TEGHNf3/vCPamqKtlstmrzemUxkTnPUzgNxb5IuFQLhQKxWKzyT3aL+FvmcXZRkiJcQNXuq6qqeL1e2W4uMk9FXmcwGCSTyRCPx8lkMp1cnYZhkEgkSG0TpIWYLEbJe7P9xWIRu90uhRwDhZwzSMHmxFB0cNpQlCJ2vbrY43a78Xg8JJNJ6Qg1FzyJEXXhTnW73d0We+wIiJzoslIgk6CZz+dlhmt/BM2qfxuDkRvfYe0eX6QxxmYNmte8MWCLr+I9EggECAQCRKNRPB6PFDqHwhlsjs6pRjUHaKUAar6Nyi/rmKS+mI9fxP4/m81y5plnAnDCCSew2267EYlEOPPMM3nooYf4+OOPueWWW3j33Xfx+Xwcc8wxfOXPrxHxjev0uXv89BY2RLOc/ein3PG1aRw8zs/irUmOmBDisD+/z5iAg7tOns6X7vmIa46ZuO2YpshTZ8zmqDs/ZN/RPsYFnZ2OX37zpYl886EFxDLVX2cXfmYsv35xBcnAOB566CHefPNNPvvZz8rfP/PMM/zrX/8C4C9/+QvDhw8nGAwyfPhwzjrrLM466yyOPvponn/+eebNm8chhxzCW2+9VcdH3sLCotGxRE6LnRIFg+Y1c0oh2rvAwXJfyds8bJ587NALwYrCpsnHElr/7qC2KZodC9UaL2H7qrk4sevNwXpfcy974r60qA/5fJ6Ojo6qP4dSW/OyZcuAUmPujTfeiGEYLFq0iImTJvFIPkRx2ydptlCkWCFuHDouwM+fLV3/xWXtHDIuQHsqTypX4Mnv7UUyV+SHjy8mks5TMCCcyhNO5Qm5bQCcsGcLWxM5Xj53H15d3sHVL68qu/0TprfwyrLy7R/hc6A7nKxZu5ZiodQEPW3aNAqFgryv5hFtIciIjMimpibp3BSOQuF27Kt4Y7PZpLPQbrfLMXAo7a+HrXqV0Pq5tI8+gLZxnylFABQLJXd5V/snwygVmakajlyUkevfxrPyDbkfMYzSfQ2FQqiqSjqdxufzEYlEiEajsnVc1/Wq4mIllWPpIuPM5/ORSCR6LEyI7E0hlIkioZ6+t82ipxg7FMKnruvy58KJWSt7tSuEwzKdTqNpmhxnd7vd0rmXyWQwDEM6MIvFYqfHsTKvs6mpiVQqRTgcxuPxdHJ1FotFYrEYqVSqTBytLG/qikw2R2L4dDbaR5EKjCPlH1O1jFAtZHHH1uPsWIWvfSne1iV4PaX7kkgkqgr7QhSPxWKk02lZ7CEEYSF8Z7PZXo/7DyZC0Kx0aML2RTyxgCcW8QYK4ZANxBazzjh622LH0KIYRZrWvzugf6NQKBAOh/H7/VLodLlc8v+N5hDurQAqys3Mi8OWAFo/ah2/CL7whS9w0003kc1mmTZtGr///e8xDIP58+cTDAaZNm0ayWSSR35yAhtjeX74xGIS2e3P7yHjAjy7uA2Afy9p49DdA7y3LkZHKoeuKoRcOq3JbfEeqkIknZf/b3bb+MqeLbhsKi+esw+LNsf5yVNL2T3kxKYq/PVbM7BrChc+tZTFW8v3s5Nb3KyPZlH9Y2hta2P8+PGd7jeUXnPRaJRoNMrhhx/O22+/DcCbb77JV77yFZ5//nneeecdvvWtb1kip4XFLoYlclrstDStf5fNk7+0yxws94X20Qc2xOMDYCgq7aMPHJA2xWoiprkQwXzgXZk3WE0YEE7P7tyX3eVeVmsd31EdQDsjd999N+PGjeOyyy4DSuOr4vmJxWJ4WkZL4WT/MT6Ge+18uCFedhshl43oNrdCNJ2nyW3DpqlMbHZz6J/f4+hJTVxx9B5c/fJKYuntrqh80cCmKYzw2vl4U4Kj7vyQv586g4PG+nln7XaBctowN2+tLi9DOWnmMB5b2Eq4YMeeLp0A1TqBPP/883n44Yfl9xs2bGDChAll4+nJZFKKW13lHNZC0zTpyrPZbGWOODN6LsHwVa8ybNVrxFumEGua3LVAlc/giq7DHVmDt30pwxJr8XrcZF06UZPWKByNfr+fWCyGqqrShQnIwhm3211T5KwUN81j6cINGgwGyeVyNcVOMT7ocrnQNE26FIVQ2FcMwyjLktQ0jWKxSDablWIq9Kxwqhai9CiRSGC322WDu9frlZdJpVJdvj4q8zrNI+ler5empiZisZi8H9XKjETDfa1tz9s8tI8+kE92/0xpkqMbobyo2YkHxhP3j6N1jyOxZyKM2/o+vtVvUagicIptj8fj8jVcq9hDjLWLx13kUw4FImal0qEJ2wtMzPmZg7WQJiIg7HY72WyWROsGRix9jk1Tjh/axVfDYOTS5wZl0bVYLJYJnbFYDMMw5P6qL5ENQ4ElgDYWXq+X5uZmVq4sZXNXHr+IHFhPsImj7p7PDw4ezY8OGcPvX1stbyPk0oluOy6JbDt+yRcN5m2M8+lFB+PUVY6/bx4AmUKRsQEHmUKRmSM8hFw6I7x28kWDL/zlQ647diLfmDWc1eE0s0Z5mXHD24wNOrnhuEkcf9/28tFhHpv8m0XdQc5ZPfP8jDPO4OSTT+att96Si5dbt5ay/ePxOIFAAIB169YxYcKEej60FhYWOwCWyGmx06LnErvcwXJvaMRW6P60KQpXSrWszMo2SnMLtHBKCsR1xBhWT3MvzWHvVu7lzsFZZ53FyJEjuemmm3j77bfJ5/NybNPn89GRzIALRvsd3Hj8ZE5+8ONOtxFO5/A7NCLpPH6nzpqONIlsgTdXh8kVDF5e1s5/H7E74XQen3P7R7KuKuQKBuFUnv9bXhIq/295BzNGeMpEzmqcPGs4Zz3yCQ7d2eXlTjzxRDRN49lnn+3ycoZhkEwm8Xg8sjSjpyiKIkedezpCrWDga10sCwfkqLHuxFB1lGIeLZ/Glg6X7SvSQLGQx+fzEQqFiEQicluFU9Lr9coTcUVR6OjokG3HLpdLln8IuhI3BWLs1Cx2iixMMZovCmzEtkSj0boLXqIRPJvNomka8XhJcFdVVTo9xWXMo8dC+OwpImtY3G6xWERVVRwOh3R/dvUaSSaTpNNpPB4PPp+PXC5HLBaTY/FmVydsF0ddLhdut1tGD6RSKXmbBgpbxx/OZnPkAfSs0EpRZElh1u5n2egjUHb7HCOWPlf2eSQKqeLxeNnfNlOr2EO8joT4LIrABuJzwSxoCpemmBgwC5q5XK7T599goWmadMQKMVu8Boeteo3IyNmkfKMHppCsO4oFXNF1tKx6bVD/bDQaxev14vf7SSQSpNNpKXRWWxTakeipACqOtSqbxC0BtPcceeSRvPba9tdw5fFLNBolFovxzkcLgIm8sqyDSz8/vuw2wtuOWwACTp32ZI6pw9wcMMbPlP95i938Du4/ZU+OuvNDfvLUUu7+xnTCqTwfb0qwKZYlnMozd13peOWVZR0cNj7AvI1xPlgfI5opsHBzghaPvcv7UahxHHPvvfdy//33c+ONNzJr1ixisZhcdPN6vUQikarXs7Cw2DWwRE6LnZpd9WC5JzRiK3RP2hRrZWWai3/MDebClWIYRlmovjgRFFl4Pc29rOa+tNj5EKUl5qbnhQsXsv/++/PBBx8wffp0ltz9KN5RGn/79gzOe3wxWxOdhaI3V0c4enIT9763kaMnNXHuY5+Szhf5yWfHAbD3bj5WtqdI5YroqkLAqeNzaHQkS7c1Z3WEvUd5+WB9jNm7eXlk/pay21/cmmRCk0vmgA732nBoCmvCaWa4PDjzTorFYpngbxgGBx10EEcddRQ/+clPym5vt912k84PM2Jk3ePx9CrnUbgpCoUCNputy9G6WigY0pHaHdlsVubcBYNBKSaaFyg0TSMSieDz+fD7/YTDYeLxuHThBQIB6TjsTaGQEDuFKzQYDEoBUDgh0+n0gAhbIidUCIyVLclCKATk/awmepqdntVQFEW2phe2RSFks1lZ0iOcjuZx9mpUjqQLcTMWi8mRdrOrE5BFTKIQyeVykUgkiOgB1tarvEpRAAVDUdk05XgiI2cz9uO/EyyUBCiRGdoTKh22uq7jcDikE1aMtWcymU7FUT1FfA6av8RzLxbdzPmZQ/15JSIUnE5n1WgDKL3fx378d5Ye+jMMozi4hYhGUf79oYgXisfjFItFuaCUTCZlC3t/M5Ebla4E0GolSLUEUHO8j1kE3ZkR+cvm6Anx/THHHMOtt96K2+1GURSWLFnCZz/7WebPn8/MmTO55557WL58OSd/93OwNsveu3lZ0V6+b3trdYSjJoV4fWWYL05u4r73N6JQcnUWt8XreO2lc6sP1sf44l0f0ey28aevTiGeLfDm6gj7j/Hxvwu2Mns3Lyvb0yxtS9HstqGrpUmVSLp8sW9rIkfAtV2eMNTOUoXNZpPH4+JzYd68eZx22mk8/fTTHHLIIcybV3KYjhkzpuoxjYWFxc6NJXJa7NTsbAfLM2bM4Oc//zn5fJ4tW7Zw2WWXkc/nufrqqznssMO44447+Mc//gHAqaeeyne/+10WLVrExRdfDJRcSddccw1NTU08tWgLV84rlIm/X581jKu+OIFYpsBBt74HlFqg7//mnixvT1EoGnzxro8YH3Jy3zf3pGgYJLIFvvP3hUQzBX7zpYmctu9I/v7RZplDeMb+o/jefqOwaQqvLu/g0n+v6HS/RDt0JJnhqOO/xtmH7oGqqtx22218+umnXHvttbLp+q677uLjjz9m9OjR/OxnP6NQKLBhwwauueYahg8fzre+9S1uvfVWeSLvdDpr5l5257602DW45ZZbmDp1KuPHj+fRRx/l3//+N3/605+A0knWrbfeCsB9993H1VdfzY9+9CMeffRRspkk//WZsewRcvHHE6cAcOWLK/nPyjC3nTSV8x5fzN1zN/LgKXvy/f1H8cwnbayPlk7on1i4lf/7f/tiGAZnP/opAJe/sIKnz5iNYRj8+F+lkrJ75m7g3m/syXf3HcmSrUleXREu2/anFrXy30fsziMfl8TPr80Yzv8uKI1suR06PnyceeaZHHzwwWiaxqRJk7j99tu5/PLLicfj3HXXXWQyGS6++GJZHJNMJqUrzxyjIE64hWjSHX6/H13XSaVSuN3uMmflQCKyR4XQKXJAhbAmBLJwOEwoFCIQCBAOh4lGozQ1NUnXY1/a0s1FHGYxUyySDITAqSiKjAMQ968rzG5D8zbb7XYpZAsBTjg98/m8bFwHOol9YvRelBW5XC78fr8UWEXzdiWVI+kOh4NUKoWmaVVdnYZhSCelx+MhNe4Alo7/SukTtt4TG4pCyjeapYf+jBmrnkLb8GG/hCYhNCYSCel8tdvteL1eFEWRDkvxeFdiFjTFa6yWoDlQr7X+IARwc8FULZyJLYyd/xBrZp9WCtYdjGmcbY/X2PkP4UxuHfi/VwOxzxEu50QigcfjQVGUPsWF7MiY430q6akA2lUT/GBhFiPNX139ridfANdccw0TJ05k4sSJPPPMM7z44ou43W6GDRtGa2srLpcLwzD45z//yS9+8QucTidPPvkk6XSaJUuWsHHzZl4+9/Nk80W++/AiAHn88vSnbZw4Yxiv/r99+WhDTDavb4hm+M8P9sWmqfzmlVUA/Pzw3fnilCZSuQIXPFk6fnnm01aOn97Cy+fuQ3syx3f+vpBC0eCG/6zh5XP3QVMUfvJU50LWpa1JRvsdrI9mOP1rx3HSFz5LKBRi+PDhXHLJJZx55pnsv//+aJrGe++9x9Klpdb3trY27r77bjZt2sQDDzwAwEEHHcSrr746YM+thYVFY6LsvffejXUUZGExAIRH7FU6WO5ra29v2dbyO27egwQ3z+/24j2lpaVFZjSdf/75fPLJJ7z00ksMGzaMgw8+GLfbLUXOUCiEx+Phv/7rv6TIeeqpp5JOp3n88cf5/YNPcua/VrMhtt0p0+TWiWUKvHHefmUi53HTWqRoCRB06RhGaTX33AN3w+vQuPH1tYzw2pk+3M2XTZe3aYpskH753H04/eFFUuiBUjv0L47YnfMeX8wor42bvzSaO375g20PoyFdaNlslpEjR3LxxRdz0UUXcf755zN37lzef/99fvrTn/L888+zcOFCLrnkEm688UZisViZYGnlXlrUm6wzxKeH/2qoN4O/nDyNi55Z1skRMe21a7CnO6SDufLfyp99/vOfB+D111/vFMkAlAlN4j0l3qOVgqjT6cThcMgT9O7yGgcC0eYuWsvF+Jpwa4rW7mAwSKFQkI+FQDgVeyLMCoFQiKnpdFqO9gu3pK7rUjit56h6MBhEVVXC4TDFYhGv19tn1ywgxQIh9qqqKp3wXWWOVrsd8ToQGaRCXK21DzYXGGWzWTniX+nqBGjf7QDWzTyl9M1Afq5v29YxCx6macN7db9581i73W5HVVXpSjMMQ04diPekEEvNX438mSbEcUVRpDuxp9tbeo6/WfpmIBepjdJ7fMyCfwzIc9wXbDYbfr9ftmaL/aiIobCoTTUBVHx1JYCajxn7Iz5WEyO7wjCMun2Zb68nNMpxjJmZIzx8bdZwrnpppTyO6SvXXXcdl1xySUPvIy0sLOqP5eS02CUIbp5PccE/SwfLhjFoB8v1FDgBWltb5f/FqAYgw7bNdHR04HSWZ9nMnj2bP/zhDxgovLAyziG7B3hswfbrtiern3h/beYwDhzr57EFW7hlzjrCqe2XyxYMituOHTbHs0wb7i67rhA4NVUhnMrTnio/OTa3Q39pWgsJHFx33XW0t7fzxz/+UbqNREHBypUryefzLF++XI6d6rrO2rVraW1t5e2332bGjBm88MILtR9IC4s6YEuHUfOZqqU4g8k5j33a6WdqPoNt24mBECW74/HHHy+/jSrCqM1mw+l0lrWbihHZaid0Xq8XwzCkgNOVMFr5b1/RdV0KfWJ82+124/P5pFhWmTGqaZp0gIpiIlVVCQaDRCKRqo+fqqqycVzTNHK5XNXxWzEqLsTOUCjU67bwWni9XnRdlwKnoD+PX6FQIJVKkclkykaLi8Uiuq7LMXxznme1+1GtrEiMmgt3Z6VYas7rdLlc0pEoIgREKUt4xF6DI3CK2zcM1s08BbWQrfvnunBkivep+b0m4iWE0CWK8XYERGO6pmny9d5bJ3fThrmohQxr9/pO6TU9ELFDxUJp6mb+Q3V/bvuDiL8IBAI4nU7i8bgUi3sTGbKz0huBUXymmN9f4vdiMae/YqT4Gz0RH6v9bqholOMYMws2J1iweWXZcUxf+cUvflGnrbKwsNiRsEROi12GnelgedSoURxyyCHcddddvbqe3+8vuZecQSJZaHLbur3Oe+tiTL/hbQCeOH0v5qyK8MG2/L+AU+f/HTyaL9/zUZe38fPDd+fcg3bjxaXtpHLlJznmdujhXjvNHgfn/PBXnHLsEXzxi1/k3nvvpVgsljVdx+NxXnvtNW677TbOOussPv30U1asKI3Br1+/nr322qtXj4uFRV9QMHBF15EITRjacrNKjNJ29XeLhCBhFlXS6bQUZao5BR0OB36/n1QqJV2AyWSyk1hqFkYr4yTMf7+ngqgoNhKt3UIgECJaLpfD7/ejaRrRaFTeN5fLJbN7vV6vLLVJJpPSUSiyPcVticZyu90uH5NUKtWt+GQWO0XuZDqd7vVIvEA0tQ9EiZF5bL2y+ESIAjabTY5ai3F8IXxWilkim1I0zDudTumgFe5OcR0RKyDETrvdTi6Xw2az0dTUxNaii7V7fQcY4AVLM9uEzrV7fQfnnI19GmcWuXmVDedCyBSuTDHaL7JkxQKBKF4Sj3GtsfahxrzIkM1miUaj/RJmg5vn43xzU/1yV81s21eO/fjvQzqiXotCoSCFTrfbXTa6Ho12XUDXKNTTEWke8e6K7sRIcxlkpdhY+VlVWTop9nfVXKA7Ysnkzn4cY2FhsWtiiZwWuxQ7w8Gyx+Phmmuu4fLLL+/1CY5oH9yaUGTTc3cksttPTp76pJXZ20pQdFXhr9/ak4ueXkpHquvt+P1rq7n+P6v539NmcdBYf8126Egqz/+t6CCRhzfffJOzzz5bnvhWNl2ff/75XHnllbz//vv893//N4cddhhz5szpxaNhYdF/fPH1JIPjMZQhKDarhVHEHVkzYDcfj8cJhUI4HI4y16K5/AZKDrVwONyj/VStEfrK8Xoxtit+XolZ8PR4PGXCqChPampqApDN52KMFkrOyGKxSDKZlM3U+Xxe5kOafxaPx8lkMr0+qRVipyjqEc7O3oidQmBMJpOdi1v68bkmMiJVVa05WlxZTCTyPMXItVn0FE5PsR8Xz0MqlZJlRUJQFU5FcX+EUG0ed84XiqyecQoGyuBmbAMoJbfl2lmnMumdW7rM2q4maOp66ZDb3GxvLgWqhijWSKVSKIqC3W4vy04VDk9zwdFQYW5Mr1xk6C/OxBYmvf1HWscfzqbJx2IoKn2OH9oWJ6QYRUYufY6WVa8NSclQTykWi4TDYfx+Px6PRy7ABAIBotFo3US1eomPvRnV7s712J07cjCdkYqidBp9F2VilUVvtTJAG1UAdUXWkAiOh13oOMbCwmLnxhI5LXY5duSDZU3TuO6667jjjjtYvXp1r68/b948DjroIFb83wey6bk7fA6NWKZ04v2Z8QHueGcDAHd8bRqPzN/CnG0uzFrYNUWOtCeyRZK58pN4czv0nNVhLvrc7jyl6kydOpX169cD1ZuuAcLhsPzX6/UCMHr0aKtJ0WJAESPbdrsdNbuRTQPhCu8Pqoa3femA3bwoSBFjx4AsihHClt/vJxaL9XghRowS9tbxJVyRInczl8vJk2shhArXqDgRFSfewonpcDhoaWmRgqhwdIpGeCFOiTH9aDRKNpvt9wmrcDBWip3djfWqqorf75f5mNXo7baZBSqRX9rT58IseprHP8WIOiBFPfEcCaHPXFbkdDqrlhUJ4dPtdrNl/BHE3KOGznGkaqQC42gdfzjDVr1adp8rHZqwXdAUEQn5fL7PrkbDMKRADtvFZSF6GoZRJngOVpO6qqrSQV2rMb0eKBgMW/UqofVzaR99AG3jPkPOFYJioSR4d/WaMIxSlJCqYUuHaVnzBqH176LndozGcsMwiEQi+Hw+GbPhcDgIBoNyH9Ab4bEvuZHmGJHejmo34ph2bzG7rSupJoCKbOMdQQD1tS+ldY8jh+zvV2WAj2MsLCx2bqziIYtdmrzN07+D5VTHoB4sH3fccVx00UUsW1Yq9XnkkUd44YUXOP/88zn88MNRVZU5c+Zwww03cMwxx3DKKacwbtw4li1bxnnnnYfT6eTaa6/F3zycR9c7+f1rqxnhtfODg0dz5UsrOXxCkF8eOZ4Dx/p5d22U7/9zEV+e1szZB44mXzR4c1WY/35uOZ8ZH+DZM/dm7rqSI/OJhVu5Zc46zj9sDKftM5IWj523Vkf4zsMLufzoPTh8QhBdLbWrX/FiuQA5PuTkv7cVDwFcc8wEjgpGyafiXHrppSSTyU5N1x9++CFTp07lF7/4BYVCgWg0yi9+8Quy2Sy//e1v+c1vfmNlVlnUlWolKplMhlQ6w6LPXELOGWyMUS/DwJaJMO21awZ8wSUUCpFIJEin04RCITnaHAwGZX7iQP59r9eL3W4nm80Sj8erikdixFdRFDkabhgGPp8Ph8NBOp2WDjnhyjSfmIpR4lquUfNJflej9JVCQDWE2KmqqtzWakJVKBRCURQ6Ojqq3pbP50NVVVm01BVixN/lclEsFonH43V1BAoBUDg9hWAshD8hkIr7UZlzai4ryuluPjnicgx16NfnlWKeA9+/ERc5KWiKsiDz12DlZwpBRTzOiqIM+Fi7oijy/WUYBslkssvG9HpjoBBvmUKsaTKpwDhS/jFVcwXVfAZXdB3uyBq87Uvxti4ZFOdmX1yP9RAj+yI27kxiZKNRSwAVY/CCoRRADRQ+/dyljXUckw4z7T/XNrTL2sLConGxRE4LCxr/YLneNFqbYmU7dF/bFEeMGMFpp53G9ddfX+9NtNgFEXl4TqcTXdc7OcwEW8YfyaYpXx788dlqGEUmrX+VUeveIJlM1m1ctBpmx6MQ1fx+P0Cfm727Q4yhO51OWWxTTZSrJm5WCoZutxuPx0M6nZaj8MJZKYpg4vG4dKpFo1FsNhtut5tsNksmk+lytL6WKNGdMKrrOna7HUVRSkL6tpFmQIqzHR0dNQU0v9+PoijdipxmF2ylS36gEIU6QvjUNK1sfFsIn7DdMS0a1le07M+qcUc1zPtsjzUvM2LNf6SgOVjOye4wj7ULwb7eY+3m91dvG9MHCgMFLTgSmzdARzSBUsyj5dPY0uFuowXqJUAOlhgpcoFFbivQqXzMovERAmitNnjBYAigjXYcM3LJMwzf5pa3sLCw6C2WyGlhUQUDhZwzSEF3Yqh6jw+WdxQMFBYedW1DtSkK1HyGGS//0gobtxgSFEXB4XDIsg/ziGgtcSBv8zSUw2z2nN/it5dEolwuRzKZHJC8PlVVZb5lOBzG7XZjs9no6OgYkJNtkc0I1HSN9UTcNONwOPD5fBiGIYXNVCpFOp2WYmE4HMbn86HrOu3t7djtdnw+nyxV6YrK0fnuMkfF/6shHKXmootqTlGXywVQM6+vsn2+L63X9UJVVek+tNlsZaKnEDzz+Tx2h5P5B19Mxu63nEa9RAjKwoVuHmvvbaasWRjvyfur3nQnLAphV2SYDrUY2dX1+4vNZpPxDuKx6U3MhEXjU8sBWk0ArSaE9ua92WjHMdNfvXKHiZOwsLBoPIZ+T2Zh0YAoGH1yEu4oWG2KFhbliPxAkdOYy+V6nL2o5xKMWPocm6YcP7TvJ8Ng5NLnKCbDhJNI12EgEJCZtvXMynO73fL/QkSJRCJ1Fz1EyY6maVKUq3xOeitumseihdAhCoug1GociUQIBoMEAgEikQihUAi/308kEsEwDPx+P8FgUH5fDbNrszeYBVCXyyXdjMKRV00YrRRsWlpaOmXmiesIMTefz2Oz2To12A8WxWJRjqND6aReiHLmhvfN7nFkHIFB265uURRyrhDxlin4WhcP9dZ0iYgESCQScqzd4XDg9Xrxer1SUM5kMjUFMpvNhsfjwWazydzYrsS0ejgh+ypGAnKBqlpmpPmyO/Kotih3CgRK7wvDMOT+aCDiCSwGn65yqqsJn6IESbxfDMOo6QCt3M832nGMJXBaWFj0B8vJaWGxi7JhyvG07v45aKTSlGKBYav/w6glTw/1lljsAog2aHFikMvlpGuzt0KPgcKygy8g5Rs9NO+pYgFXdF3V1mdd13G73XK0PJlMSlGpr7hcLrxeL7FYTDZf13vkWVVVORKfy+WIx+OdTt57K25WjkALga1YLBIIBNA0TTqistksmqYRDAalSBwIBOT91HWdQCBAsVisKe7KqQCbE0PRUYw8Wq53UwFiG4rFojyBrTUiLEqJhIgpLi/ckkCZ4NnVSH1PM0bNY/dmvF4vt912GxMmTOD0009n+fLlAHzzm9/khBNOAOC+++7j5Zdfprm5mauvvhqXy8UjjzzCs88+i8Ph4Pe//z0+n4+kPcg3HlvD5uT2E35VgTtPnsakZjcfrI/x06eXMj7k5O5vTAegyWVjRXuKkx/8mKnD3Nz+tWnoqsIVL6zgleUdnH3Abpx5wCiKBjy+cCs3/Gd7k++3Zo/gDydMZuQ1b3R6bH5w8GiWbE3yytJW/vQ5Nwfu5qKjo4PLL7+cZDLJBRdcwPHHH89zzz3HTTfdBMC5557LYYcdBsA//vEPnn32WWbOnMm+++7LAw880KPXQb0RrnXhfhQuYfP4vRBFRVyHuWCqq9Huruir87EnYqQoQGpvbx/AR66xUFWVQCCAqqpyHxGNRgc0qsSisekqA7QrATRfKPLJfuc15HGMhYWFRW+wnJwWFrsoVpuixa6IcDuIYpNCoSCFrv6M+SkYjP347yw99GcYRnFwc60MQ/79aicGohFc0zTcbjderxePxyPFzt66lex2O16vV47BixHyerpEPR6PLMOp1tbcG3FTURTp2tR1XbZ6V47qirF0MY4uci+Fo9PlcpFMJnG73dIdJ5xU0kFVKPYq39kVWYOvi3xnRVHkSGpHR4d0drpcLpxOJ6lUilQqJe+HWXjMZrM9csH2ZKReZJZ2N1Jv/vuqqnLJJZfwwx/+EKfTKZ/PU045hW9/+9tomsZdd93Fyy+/zPe//33uv/9+3nvvPe655x5eeuklDjvsMJYtW8Ytt9zC/uffyBkHjuW6V7cLkcdPb2FDNMvZj37KHV+bxsHj/Ly9JspRd34IwOVH78Gq9lKkwbXHTOCcRz9hczzLM2fM5pXlHby0rJ275m4A4LUf7MsD729kayKHqsDXZw1jbaT66/kr01v48tvz2G9MgGBLiLPO+iZf+MIX+MY3vsH999/PQw89xJtvvslnP/tZeZ1nnnmGO++8E13X+dvf/sazzz7LggULOOecc3jwwQe7fQ/2xfXYF3ek2RFm3iaR/SdcwJXuyEZxRiqK0tDuy4GgWCwSDofx+/3YbDYKhQKBQEBOIljsevTWASrykRVFYfa653ln+lml99FgOjqNYpfHMRYWFha9wRI5LSx2UbytS7ClOhquTdHbumSot8RiJ0MUCDkcDjmaKxyb9XS7OBNbGDv/IdbMPg0G6wRh2wn92PkP4Uxu7fKihUKBWCxGIpGQpTtut7uTUNYVuq7j9/ulYBYMBqV44fF4us2o7I7uynB6I27qui7FTSiJsPF4vOZzbhgG0WhUPi7BYJD29nYpEguxMZfL4ff7pQgaDodxN48kMu0Q1rbsW9qnFgslobvGa6CoO0iEJpAIjqd1jyOxpTpoXvMGTevnoucS8nJ+vx9VVWWRk2Fsb7F2u92yGd38HAoBy+/3SxdsOByuOcJaj5H6WtmisVhMOgaF43fLli2MGDFCttu3tLSw9957c++99+Lz+Vi2bBl77bUXra2tpaIlpwtv0wjakuXbf8i4AM8ubgPg30vaOHT3AG+v2f76O2F6C0fd+QEAo/wOlrWVBM+OVJ5mt41VHdvdzLmCQXHby//U2SN49OOt/OSzYzvd51kjPazYJpxObHHz/tY8BvDpp59y/PHH88ADD9DR0cHEiRNlyZKiKLS2tsrXoWEY8jW8bt06DjjgAD799NMBy43sjRgJSEEakG5OVVXJ5/Mym9jKfWwsDMMgEong8/lwOp3k83n8fj+xWKyui08WOz61BFAxBeA22pi85FGWTP1mQx7HWFhYWPQES+S0sNhFUTBoXjOn1KbYECmYBs1r3rBWcC3qhnBsijHdbDYrx5AHiuDm+RQX/JN1M8UJwgA6OredGOy5+hn8sWX0dFC8WCwSj8dJJpNSNDSLnbXELk3TCAQC5HI5YrEYXq8XXdcJh8Nomobf75dOyd7SXRlOb8RNh8OBy+WSribhWO2piCfcjm63m6amJjo6Oshms8TjcXw+n8w29Pl8hCNRNo37HJsnH4uhqMh9aU9G/RQFlNLlcs4gm6Ycx+bJxzJi6XMMW/UaXk+pyKnaKLxhGCQSCeksNYudwnVZywVbL8S4Y3eI911raysAr7zyCnfeeSeapnHttdfKBvtMJoOqqsTjcYLBIB9//DETJ07k7w//g5SziYP/9F7Z7YZcOtF06bUWSedpctvk76YPd7MhmiGaKW2fajpRL11Wpy1ZEru/OqOFFe0p2pIlF+c39hrOSQ9+XFXknDZsu8i5aHOC7+wzkbdG7cFRRx1Oc3MzLS0tACVx1uEgGAyWXf+kk05izpw5OJ1ODMNg06ZN7LHHHnz66adD7ow0v8eEkC7cm2Kk3ePx4PV6KRQKUvBspLHoXdHJaSYWi1EoFPB4POTzeXw+n9xnWljUQixeQmmiwdn6LmNyxiAdx5Q+28Ys+AfBzfMH7u9YWFjsUlgip4XFLkzT+nfZPPlL207OhxbFKNK0/t2h3gyLHRxRquFwOFAURYpTvW0R7g9NG+aiFjKs3es7pb85ENlWxQIKBtOX/4tRsaXg8Ujhocc3USxKoUyMQLtcLtLpNKlUqkzAUhRF5k9Go1EpJMZiMZnhl8vl8Hq9hMPhHm+Dqqp4PB7pPgqHw2W5f06ns0fipqqqcoRbVdV+C9rJZBJFUXC73YRCISKRCOl0Wm5vIpHACI1hxYwzSXhH99/toiiAgqGobJpyPNFRe7PX2udItK3pUkQSYmcqlcLr9coyqGKx2JC5hB6Ph69//euceOKJ2Gw27rjjDl599VWy2SzpdFpGKkQiEY455hgWLlzInx/9N2O/8d/86vPjueT55fK2wuk8fmfpMDbg1GlPbn+cvj5rOI9+vEV+XzS990uXLYmje43y8qNDx/KV++YB8N19RvLI/C30ZFexYHOC11eG+cNvr+LTjz9iy5YtRCIRALnPaW9vl2LkQQcdxJQpU7j44ovlviiVSpHNZvvtgO4PlY3plZEGleVQQvB0OBy43W4ZjSC+dmWRsREQ+0iv10uxWJRCZyqVGupNs2hA7HY7fr9fTiyIz9fBPI4ZO/8hS+C0sLCoK0OvbFhYWAwZok2xR2d0A4nVpmjRD3Rdx+Px0NzcTCAQQNd1EokEbW1tUpwa7BPv4Ob5TH7zBlyx9fV/fxkGrug6Js+5Hvuqt+VIqnCP9f7mSiPQ7e3tJBIJHA4HoVAIn8+HppVObAKBgCzkUVUVn89XJnxAyQEpmrF7gsvlIhQKYbfbicVidHR0kMvlZO5kU1MTHo9HikXxeLyTwGm32wkEAjQ3N+N0Okmn07S3t9fFsZtIJKS7MBgMYrPZpMMtvtu+vLvn2SS9o+o/zqcopHy78c70s9jon9ztxYWrVIymC/ddc3OzHDtuFIRglsvlKBQKOBwOAoEAy5cv5/Of/zyhUIjp06ezatUqCoUCmzdvJhyL05rMEXCWr8u/tTrCUZNCAHxxchNvro7I350wvYV/Ldw+9rgplmVCkwuvXSPkKrk4d/Pbue2kqZz+8EJSudLravpwD6ftO4pnz5jN5BY3fzih/PFf3JpkQtP2x/T6/6zhOxf8kiVLlvDKK69IoU8U94gW44kTJ3LOOedw2WWXle2LRo8ezcqVK+v3APcCm81GMBiUAkdHRwfxeLzbfaVYOGpvb6ejo4NUKiWd3GIf7HK55L5jMNnVnZyCdDpNNBpFVVUKhULZAoiFhcDlchEIBMhkMoTD4U6frwN9HONPbeagT+5hUmEDTU1NBAIBvF4vLpdL5oRaWFhY9AWrXd3CYhenkVuhLSxqoaqqbMnWdV26GDOZTJ/GpQcKA4XW8YezyTzO3BdRzDBKt2YUGbn0OVpWvSbfL01NTZ2adfsr8AkHpShnUlWVcDhMoVCQI7giJ9KMaPmt9juBKC5SVbWsJdxcqtOVc1NcTpRH5XI5UqnUgIxlK4pCMBiUOZPxeJwNTbNYN/MUcYG6/02JaYyvacN7nX6tKIp0wRYKBZk3GgqFpPvT6XRSLBblyP5gc8sttzB16lQ2bdrEk08+yYsvvsi3v/1tPvvZz6IoCs8//zyPP/44gUCASy+9FKfTyaOPPsrTTz+N3+/nuuuuw+b2kfKP4ZzHPmFpa4rbTprKeY8vRlMV/nLyNCY0ufhoQ4wLnyqV1k0b5ua6Yyfy1Qc+ltsxfbib206ahqYqXPniCl5a1sFfTp7G4RNCrI2UHpfz/ncxS1q3L7S98+P9OejWzo/7c2fO5th7Ss7Pl8/dB0fHapZ/soCbbrqJYrHIqaeeyvHHH08oFGLevHlccskl3H777bS0tMj3xU9+8hPi8Th//OMfufDCC3udh9ofxKKQ3W4nl8sRj8frss80j7WLtvZ8Pi+F38EYa/f5fKiqKh21uzq6rhMIBABkznEikejmWha7AiK/VUxzdMVAHccMW/0fbHop87daE7ygsgVefImID4tdDwOFnDNIwebEUHQUI4+WS2NLh61zSQvAEjktLCyAtGd4qRVaUQe5FbqIYhSZPOd6K2zcoltEgYnI2RRjksIZ1sjkbR7aRx9A27jPkHOFUIqFbe+3Lk4UDKMkdKkajkyEURvfwb3i9U6OZ1H+Ix6TegmdUCq+EYJFNptFURQ0TaOjo6OqMKPrOqFQiFgs1klU0zQNr9eL3W6XbrBCodBjcdNms0lhG5BO0oEWtTVNIxQKUSgUaGuZwYI9vlr6xaAVMhiMm/dg2Tif0+mUrfbCXSoIhUJks1kSiYQcr3c4HIMidmqahq7rZV/iZLVQKMhog3w+L8VzcYJrvry5bCdlD/DmzPMGbJt7y3kHj2bx1iSvLC8JltNeuwZ7uraoX4uZM2ey77778sADD9R7E6tSGQ2RSCQGNJ/YPNYuFmEGeqxd5AoO5fh/o6Gqqlx8UhSFTCZDLBYb6s2yGCJE9Iyu670upqo8jumuYA8oO46xpTpoWfMGofXvdju5JY41qn2ZBVDhmK/2ZQmgOw8GCvGWKcSaJpMKjCPlH0NR7zy5pOYzuKLrcEXW4Gtfird1iSV67qJYIqeFhQUA4RF7lVqh+7pC21tqnLxbWFQiMjbFGHQ2m5WuzR0NcaCWGzWTqGc34p5RXR6ouSNr8LYvZVhiLV6Pm7a2tk4H7sK9lE6n5eippmn9FjpdLhder5dYLCYz3jRN61Yg8fl82Gw2mQlZ6TgU1+2JuCmEbZfLJUuNhLg5mCcwDocDbdh43pl+VvfidL0xLQZ5sx1lBU3VxoubmprIZDJlji2RdVlPsVPTNGw2W5mgKcRJIWiKE03DMKSQqWlal63h4v6Ik9dcvsC7h/ySotb7KIaBRitk+cwH/0NyW7RBIyKyZV0u15C5enVdl4KnrusYhkEul5P78no5WQOBAIZhWCJnBYqi4Pf7y0r4+vsYWU6qHQ9RHiiiZ/q6QNhbwUkcx9RLcLIE0F2HkrB+IG3jDuuzsN685g2a1s9Fz1ku9l0JS+S0sLCQtO92QKlNEQatTbHaGKaFhXDt2e12VFUll8uRTqcHtUBoIGlpaSEej5NKZ0oniroTQ9VRinm0fOcTRUVRaG5uliUzZoRTr729XQqTuVwOXdeJRCJ9crmKMoJUKiWzNkV2lxhLzefzJJPJTuKOqqo0NTXJApNKx2GluFmt1V3TNDmSDqWT8lQqNWSOXQOFFYdeSNI7CkMZmlgPb2IjBy59iHwu2+WIcVNTkxSMKxFip1lw7ok4V+nOFIKmyIM1jw2KE1DzyaaIIzAjxE9x0pnL5WRWZyXLD/ghidCEwRWXu8MwCMTXsP/SvwOUORVzudygjqDXQjSmAzIaYqgR+w+Hw4HNZisba+9v3IgoR7OcitUx5zbncrlejfVbTqodG7vdjs/no1gsEolE6rp/koJ3N8cxg4ElgO4cGChsHX84m+sYkTBi6XMMM0U9WezcWO3qFhYWEqtN0WIo0TRNjiMLx6DIWqwmfOyoCIEon8+jYPRo1NUwDDKZDC6Xq5PIKcZ+oSRkiKbxfD5PIBDotdCp6zp+v1+6AVVVxe/3k8vlpHig6zputxu/30+hUChzhwmxR4iborEZkI6yWuKmcG3abLay2x1qwWjr+MPr06LeV1SNuHc0SwKzaFr2cp9vplAoEIvFSCaTeDwe6fw1i9VCxBQuTeG8FKKk+ILqgqZZzBSXFSeU5nH13jynrsgaEsHxMBQCcy2MIvbWFbS3t+N0Osu+ACnc5vN5+e9gYW5MN+feNgLmtnZFUbDZbDKGxNzWnslkZJFWT6nlDrYoIVz5brdbFk9FIpEuH+PeOqmKuoNEaAKJ4Hha9zjSclI1AC6XC4/HUxcHbzV6ehwzGBiGIT9jKqkUQFVVlZ915pIjSwAdWtKe4ayddSop/9j+H3MpCqBgKCqbphxPZORsxn78d5yJLXXZVovGxXJyWlhYdKKuHzBmDANXZE3pA8bK4LSg5OoRJ7i6rlMsFslkMoOStThUiBOO1tbWXl1P5F1WNofb7Xb8gQAbE0Xymh1D0fG6HLg0Az3Zhk3XCYfDPXo8NU0jGAySz+ely0cU73R0dHQ6wDePQRuGQSqVQtd1+X0mkyEej3fp3BSirNPpRFVV6docyLzA3pC3efjkiMsx1KFfF1aKeaa/emWXeWbNzc2dcjqr3lZFFIBZuDT/X2B+7qu5MoWj0yzq1WtxIjVyJktnn1GX26one7x/J77WxfJ7kZUqFjGAMtercKuKx6feJ8x2ux2Px4Ou63JxYagXCHqD2HfY7faysfZMJkM2m+32voh9VzweH6Qt3jFxOp14vV4Aua+vfC1aTqqdA9FWbpVOdU8tB2gtAbRSDN2R9rWNSnjEXiWjDYpltLHoF0N/xG5hYdFwOBNbmPT2Hwe8Fdpi10SIK+JkVohhA12E0SiIbMneIsQjp9NJJpvbPjoY3L00OqjZO11HLWTxJjYSSG7EvnEh7i2f1Hz/iUKCYrEo3R5erxd9m0haTZAxOwN9Pp8cjRXuW5fLJYuLKsVNu90uIwkMw5DurkZz7baPPnDbPnDoMRSV9tEHMnzVqz2+jij2sdls0rFSWe4jqCVwyr+/TcysLBAayJM7h8NBc34zqzMRsnZ/Y4ysGwa2dBhv65KyH4tIDTEmbhiGjBYQj79Y5BCj+mbhs6+Po7kxPZvN0tHRsUMuEonXlHCQi88Ir9crhWMheNZyall0j3DH+/1+dF2Xjk7x+rOcVDs+5hzWaDTasJnBjYQQK6tRTfi02Wxln6Xm+JVqTfAWXTMokWmqhmEUWTP7NIpWZNpOjeXktLCw6JLBbFO02Lkxi1qAzNkcqKbdRsXcft1bNG8T0d0PYU3zPr16PypGEUPVsKXDNK9+veroYDAYLGtOdzgc+P3+qk3pZipHY6HkVhUUCgV5Aq0oCk6nE5fLhaZp8jUw2EUoPcVA4dPPXUrOGWwocW3af66VYrWiKFLI1DQNj8dT5pKt5cYU1+38J0r5meax4a5O/gYSp9OJz+cjlUqxouUANk358sDmRfcUo8iYFf+madlLNS/SVZu5KGESwqeulzwHYqTf7PbsisFuTB8qFEWRbe0ip1m4/kUOKvRv37orouu6LKIRWY1tLTMsJ9UOjqZp+P1+VFUlGo0OWZb1rkJXGaCWANozSuW3p5e+scpvLeqAJXJaWFj0iKFuU7TYMdF1XeZsigIh0Yy+Kx7YKYpCS0tLr50VAz066Pf7sdvtcqxd0zRCoRCZTKZmiYeu62VN38lkUo4/C9eVyHMU2XpC4M5kMqRSqYZ3m8VaprJyv3OHejM6seeiv9ISW9llwU934+VmV6Yo/BEnXDabDY/Hg81mI5fLkUgkBv1E2e124/F45Jhlo8UGfPbjW1Ey8W6FxUqXZTwe7yQYi2xKIXiKQh4xrm0WPsVzLMTNYrHY4wKpnQWbzSYFTzHWns1msdlsMiLDomeoqiojSTY0zeTT8ceVfmGVT+6Q2Gw2/H7/gBQMWfQeSwDtnrRnOEsP/Vnp2HYwFzGNIopRZPKc660ItZ0QS+S0sLDoE43UpmjRWGiaJnM2NU2jUCjInM1GG0UebETZQ3t7e48fiwHNyI2uZeqyJ2giQTQaJZvNoigKoVCIYrFIOBzudLVq7jFd18syN0WLs8PhkKOmohCgq2bweuP1erntttuYMGECp59+OsuXL8fhcHDrrbcCyCzYU089lebmZq6++mpcLhePPPIIzz77LO0zvsqffvlDfE6dfNHguw8vZEt8u9inKnDnydOY1Ozmg/Uxfvr0UgBmjPBw/XGTsGkqj87fwu3vrOe2k6Yyc6QXVYErXljBS8s6cNtUHjhlT4Z57Ty1qJXr/7OG4V4bj522F7lCkYIBpz28kE2x7SKaUizw3ZFtzDLW8+9//5uWlhbOP/98XC4XH3/8MQ888AB77LEH559/PoZh8MQTT/DKK68wZcoUZs2axf3339/j195QiZ0ejwe3200ikShrBN8y/gg2TTl+aF21hsGoJU8zat0bUrzsyWMj8jI1TZN5mV052M1OTzEWCZ2LxnZ116KmaVLwFOJwLpeTDs9GX0hpBBRFIbvHwXwy6WTxg4H/o5aTqu6IrNVcLkc0Gt2lJmR2RMwTGJVfXQmgYnFyZxBADRSWHXwBKd/ogXGOd0exgCu6jknv3GKdu+5kWCKnhYWFhUW/EflpDocDm80mRwlFQ65FCdEu3tbW1qPLD04IO0z85J+415UcNSLHS4ytmxFZg4DMzasUN8XPHA6HdHTabDbp9NQ0jUwmQzKZHHABQtd1fD4fF154IQ888ADLly8v+/0JJ5zAqFGjuPPOO/nZz37GG2+8wXvvvcc999zDOeecw6TvX8OsaZO59N8r+O4+IxkTcHDdq6vl9b+yZwv7j/Fz+QsruONr07j3vQ28vSbKv763F999eCGxzHYxcY8mJyvb0wRdOs+duTeH/Ok9zj9sDMlsgbvnbuTZM2Zz9mOfsCmWxaCkAXxvv5GMDjj5zSurtm+0YfD0dyZz6yXnkcvluOSSS7j99tvZsmUL+Xwel8vFFVdcwe9//3vWrVvHHXfcwY9+9COy2Sw333wzF154Ya9Pfu12u2xkzmazJJPJAXtfi6KMqjEJisqKQy8k4RmJMRRN61VOiMxCsBiV7up1Ld5DiqKQTCbLRNyuMF9PuDnFmHFlodGuSnNzs3TVirH2QqEgBc+dcZS/Hmx3UmmDu4BgOanqhlgYsgqGdg56KoB21QS/I9BIC5fDepF1btH4DP3Mj4WFhYXFDotwbNpsNgCy2Wyn9m+L7fSmdGgwQ9iX7XkKY4owJrwIh8PRacxNlH+YczdFi7QQN+12uxx7LxaLpFIpWXARDAax2Wy0t7fjcDhwu90yP28gBbN8Pk9HR0fN33/hC1/gpptuAmDmzJnceOONGIbBokWLmDhpEp8kXRxsL4lpQZdOW7J8Ow8ZF+DZxSXB+t9L2jh09wCb41lsqsJfvzUDu6Zw4VNLWbw1ycr2kmCXyRelyHjouAA/f3YZAC8ua+eQcQEeW7D9ZN/r0Fm0uXz0NuCyUdSdbG1tRaEk7Jx55pmEQiH+/Oc/s2HDBkKhEGvWrAFg8+bNTJo0iUWLFrFixQpmzJjBggULevU4CoFIuBGDwWCPBL3e4vP5cDgcVeMcNE0jEAjgWvU07+55NlAc5NE2AwWDcQseLnN85HI5wuGwfGxEzEMikah6oineF263G7fbjdPp7HLcvFZjuqIoZW5P8X4Ujmmz8LkrObry+bzcR5nH2l0ulxxrF187gxOqvxgorJ11amkhbbCFBkXFMAzWzjrVclL1EXPBUHf52RY7DsViUS5gVVJNALXZbDidzh1KAM3bPGyefOzQ550rCpsmH2t1R+xkWCKnhYWFhUWvsNvt0rWpKIrMmctkMrvUyXRfsNls8gS8K8Ij9mLdzFNK3wz0AaCigmGwbua3CK54AjZ8UFaQ4vV6ZZ5gNpuVB9JCrHE4HDQ1NaGqKtlstqpAlUgkCAaDOBwO6fAV7sBgMEgulyOZTA6qOO71emlubmblypUAMtsPIBaL4WkZzeKOPNOHe5j/kwNRUDj4T+X5cSGXTjRdEvki6TxNbhsjvHZmjfIy44a3GRt0csNxkzj+vu3jmL/50kT+9Oa6bde3Ed3m9oxuuz7A7FFebjtpKkGXzpfunlf2N6e0uFgVzpJQPezm1ZgyZQr//d//TS6X4+abb+YnP/kJmzdvZsaMGSxfvpxZs2bh8/kAWL9+PRMmTOi1yCmoFDuFoFcPV24gEJBNwJWvA4fDIe+DO93GHgsfZsXMU0t210Ecrd1z5ZO02HNEUmongUw8NqKIyyx2Vl7WMAwSiQSpVAqv14vf7+808m7OvK3WmC7yOs0nwUL01HVdLibA9tZycflGOcmtN5XZs+L+JhIJOdZujtAQY+2iXGtXZOv4w+sfhdIbVI1UYByt4w+3nFS9RFVVAoEAqqoSiUSsqZldhJ1FAG0ffeC2jPmhx1BU2kcfyHBrH7TTYImcFhYWFhbdIk6anU4nqqrKLMZdtUCoL6iqKh+7rkh7hrN2r+8AxuA51RQFDIOFe5zA5A2LcSkpmbtZKBRIp9My8y6VSpHL5XA6nYRCIQzDkEVCtQ6QReGUx+ORAqgQhWw2G263m0AgQD6fJ5lMDkqJypFHHslrr70mvxdFSaKII5EvjYvPWR3hqpdWcvLMYfzq8+O55PntI+/hdB6/s3QoFXDqtCdzhFN5PlgfI5opsHBzghaPXV7+jP1HoasKD320edv1c/gdGpFtt7Omo+TCmbcxzqF/fp9vzBrOL47YnR8+sbjT9hd0J7FYO2vXrmXTpk3yPqiqyi233MJPf/pTDMNg5cqVPY5H6ClmQU+4cvsqdiqKQiAQQNf1qifqYgxTnJhFIhG8hTbGoJWczsYAv09MJSn2zR+iBAIEg0Gi0WjV+ypEfKfTidvtpqmpSRZzVe4ri8Ui0WhUCprCIQvIrM9wONxj8UKImQJVVcsKjcTClHnEXYifOwu1FtoKhQKpVIpUKlXW1u5yufB4PLvkWLvlpNpxMRcMhcPhXVaktyinNwKoqqryc8FcYFhL/Kzna8xAoW3cYcAQ73skCm3jPiPLOC12fCyR08LCwsKiKqqqymZ0Xdel2JXJZHbp3Le+ouulj9yuHrvy0cFBXuFWFAxDYf3s73DAkr+iUHKKiYPgVCpFsViU2YCiRKin43GJRIJQKITT6Sy7Ti6XIxKJoOs6brcbv99PoVAgmUzWffROURR5cH/MMcfw5z//WTphVqxYwec//3nmz5/Pnnvuyc3/eJYQCq2JkuDRmswRcJYfNr21OsJRk0K8vjLMFyc3cd/7G1nalqLZbUNXFUZ47US2OT2PmhTiazOH8dUHPpbXf3N1hKMnN3Hvexs5elIT5z72KTZNIVcoHWRH0nmSufITiyWtKcaHSoVvmUymJPp5veTzeRkVsH79es4//3ycTie//e1vZRbp6NGjeeWVV+r2eApBr1LsrDWqXYkQODVNIxwOl703FEXB5/Nht9tl+YI5RqFpw1zUQqaUWWsYA5hZazB2/kMEN8+nAHR0dBAwCZ21BLF0Ok06nZbvF6fTKaMdKoW4fD5PJBKRY6dQemxjsVi/3PHmbGSg04i72+1GVdWdZsS90slZC7EwIx4XMdbucDjKxtozmQzZbHaHfCx6guWk2jERznarYMiiN3QlgIpjI/NXLQG0lgu0N6/DeMsUcq5QXe5XXVAUcq4Q8ZYp+Fo7Lypb7HhYIqeFhYWFhURRlLKczWKxKMfRdya3z1Bgs9nkAWItGmF0MOEbw+ph+7NH63vYbDbS6TSKouByuYCS+FLLxdYVQiQXbs5qQk80GkXTNNxuN16vF7fbLd1XPb4LFW6F66+/nkmTJjFp0iSeeeYZXnzxRdxuNy0tLWzcuFEKGvfccw+XX345p512Go899hjJWIy/fbSJh789k5NnDUdTFM557BMAbjtpKuc9vpinP23jxBnDePX/7ctHG2K8vSYKwA3/WcPL5+6Dpij85Kkl264zjXAqxwtn7006V+S4e+dx99yNPHjKnnx//1E880kb66MZDhjj4/fHTaZQNMjki5z16Cdl9y+SzlM0DBx66TVy6623cvPNN2Oz2bj99tsBOO644zj22GMpFArccsst8rGeOHEit9xyS6+et55gFjs9Hk+Ze7GW2ClGLRVF6eRE0jQNv98vT65qncwHN8/H+eYm1s46tf7vG8PAFV3H2I//XlaKYhgG4XAYv9+P3+/vVugXsQ4ulwuXyyXFzlQqJQuEhBAqxtgB6QJNJpO9ev13fZc6j7iLcUabzVY24l4oFMpEzx3FKdYXwafWWLuISMjn8zvdWLvlpNoxEc72VCpFPB7v/goWFj1ALHRVO66rJoCK/aRZAO1qBL5yvxxrmgzFwtA0qteiWCDeNNkSOXcSrHZ1CwsLCwuZsWm3l0ZrxQndYIwN7yoEAgEMwyAajVb9fd7m4ZMjLsdQh379USnmOfD9G3EUM1KcFWJNf1wjqqpK4aa7VmlN06QoZBhGmdhZOXJl/n+t3Cnxf7H94nq6rne6brFYJKF5eXefC/p8XweSL09tZu+NL/D0I38r+7mqqjQ3N1ct/5o5cyb77rsvDzzwwIBvnxjVVlVVjrFXipiBQACAcDjcqeTK5/NhGAaappFOp4nFYl3+PQOF1vGHs2nysducaX0sUdmWvakYRUYufY6WbgSX3jYaK4qC2+2WbsFcLofNZisr8BKvT0VRZGREsVgkHo8Pyhi1GHE353uKEfdKt2cjoSgKLS0tdS2+E2Pt4rNRURQKhYJ0eDbaY9AbYi1TWbnfuUO9GZ3Y4/07LZGhCmZnu8jztbAYbLxeL7fddhsTJkzg9NNPZ8WKFbjdbv74xz8C4HK50HWd8847j+bmZn7+85/jdDp58skneeGFF+Tx2Bcu+gPHHzCVg24tzzl321QeOGVPhnntPLWolev/s4aDx/m59ksTAdjN5+DZxa387Oll/Ot7exF0lY6X/+vJJXy0oST6X3vMBA4Y60dTFb58zzwCTo3HTtuLXKFIwYDTHl7Iplj5Z8SxU5sZ5bPxjxfm8PgpEwgGg9jtdv7whz/wwQcfAHD++eczY8YMNE3jRz/6EdOmTeP8888HYNiwYbz++uvccMMNXHXVVfzmN7+xSsCGGEvktLCwsNhFEWHkYjU2l8vJcXRr/Kn+NDc3d+nK2jL+SDZN+fLgj6lXwygycf2rjFr7Oul0uq7iisfjweVy0dbWVvN1VpkZZbPZ0LTSir95JLWaiCn+L0ZzK90HZhG0luugWCxioLDwqGsp6o663fd6oeYzzHj5l508WELk7E2O40BSKXYmEgkURSEYDMp8TbPA6Xa7ZT6ipmkkEoluxXAzeZuH9tEH0DbuM6VRuGKh9H7qSvA0jFLupqphS3XQsuaNXmUDOp1OvF6vLN3q6XXMbehd7Rc0TcPj8eBwOMjlcsTj8UGPCxFOTyF6Vo64i3+HMp95IETOSoTb1W63o2maHP0czLH2SpFh+fLlOBwObr31VqD02tJ1nVNPPZXm5mauvvpqXC4XjzzyCM8++6y8nZMv/h1f+tzBPRIZBN+aPYI/nDCZkde8AcB935zOl6Y0c9XLK/nzW+uBkst95kgvqgJXvLCCl5Z18L39RnLJkeNZH82wIZLhtH8s6nS/7jx5Ghc/tZhp0Xnce96xjB8/niOPPFK+L0499VS++MUvEolEuPTSS0kkEvzhD3/A7/cD8Lvf/Y7Fixfzve99j7lz57JoUee/saNiLhiKxWK7TGasReOh6zo+n48LL7yQBx54QEbhCE444QRGjRrFnXfeyUUXXcSbb77JRx99xB133MGFF15IoVDA7fHw3avuZPJwb6f9z/mHjSGZLXD33I08e8Zszn7sEzZEt7/e7/nGdO57byP/WRlmjyYnK9vTTGlxc/1xk/jK/fM5eeYwdgs4uGXOOnkdVQGD0kf99/YbyeiAk9+8sqrs7z5++iy+9beF5NIpZr92OYV8nlGjRnHFFVfwgx/8gKOPPpphw4bx97//verjcuWVV/Lkk0/y/vvvc8QRR9Dc3Mxjjz3Wvwfbol8MvV3EwsLCwmLQ0DRN5mxqmiYdejvTGF4jIgS7WuJEI44OrmnZB/fCZ+s+OphMJnE6nfh8PjKZTFVXpkAIkUJMEC4zRVGkIG92cgoxVIxQmd2cQsTP5/PdxgaUHoHSuHIiNGHoiznMbBujbqAtqkllLqXDURKMRQal2bXo8/lwOByyACoajfbaSa7nEgxf9SrDVr1GvGUKsabJZJv3IO4ZRVHrLFar+Qyu6DrckTV425fibV3S69d7Op2mWCzi9/sJBoNl96sSm82Gx+PBZrPJsi4heLpcLlnmZqZQKBCNRrHZbHi9XkKhEOl0umpr+0DRlxH3WqOPA0VP8zj7g/lx0DRNCp5+v1+KvkLwHKjP03Q6zQUXXMCFF14of5bJZDjnnHOA7SIDwPe//33uv/9+3nvvPe655x5eeuklstksbrebyRMnVL39sw7cjecWt0mR4W8fbWJDNIuqwNdnDWNtZPvr85LnlvN/yzvw2Lfvs3//2mpWtqcJunSeO3NvXlpWEjH+OGetFEIr2T3kpFA0iGSKLM/7Ofvss7npppvk74PBIIcffjhnnHEGxx57LKeccgr33HMP//M//8P69evZfffd+elPf8p//dd/8cQTT3DRRRdx2WWX9e0BbjB0XZdTIFbBkMVQk8/n6ejoqPn7L3zhC/K9O2PGDG644QYMw2DBggWMGDGCRYsWcdK3TuPWtzdw81emdLr+oeMC/PzZZQC8uKydQ8YFeGxBKS7GpikcMMYvI3xWtpecktlCkeK2z9wT9mxhayLHy+fuw6vLO7j65VUUTR/HXofOos3lMQ8Bp46mKGTyRdAdpHQf9nwHHo+HZctK23L44YfT0dHBX/7yF+bOncudd94pr6/rOjNnzuTXv/41AO+88w6/+93vLJFziLFETgsLC4udHFVVZc6mruuyjEIIPhYDT3elQw0Zwu4MEW+Ziq/10z5cXana5Gl2U4qIBLMLU4gD4vtisVh2G4B0+YmMQ9ju6BQiQ73aQF2RNSSC40FpnNwoxSjijqyp/juTS7WRSKVS5PN5OaKu6zoej0e6NIVLqVAooKpq1Zb13qBg4GtdjK91MU1NTaTSGSJFOwW9VNikFPNo+TS2dLguIn42m5U5naFQiEgk0mk83+zGNDttc7kcyWQSj8eD3+8nn8+TSCQ6ubVyuRwdHR1lre21iowGGvHeEuN4iqKUuT3NLe7mEfd8Pj/g2zpYj4UoR0smk6iqKtvaPR4PXq93wMbaeyMyzJw5kxtvvBHDMFi0aBGTJk1i0aJFnPrtb3Pru1u56aThna5fS2Q4dfYIHv14Kz/57Fh52Y2xzo5CITxk8sWy5+K8g8fwzb1GcNtb6/jH/C1l1zlheguvLOsARaHdOYpYRdbkjBkz5Mjom2++yVVXXQXA+vUl0dRclBWJRBg2bBiqqg6ps7geiFzYykUhC4tGxOv10tzczMqVK4HS57x4zcZiMfx+P16vl0mTJ/H2C9WnHkIuG9FM6bMzms7T5LbJ3x09qYlXlrdT+Tb4n+MmccPrawEY4bXz8aYER935IX8/dQYHjfXzztoos0d5ue2kqQRdOl+6e17Z9ae0uFjVsX20vKA7ufvuuxk3bpxcLGlqamLp0qWcc845XHfddcyaNYuPPy6VSB588MG8++678r6mUilCoQY6nt9FsUROCwsLi50Qs4hkbuutdvJsMfDYbLYuT/AbMYRdKRbIjZoBNUTOrkRMcxi9eSw8m81KQVI0gkej0bLr6rpeMyfTfDtC7HQ6nWiaVubUrBe+9qW07nFk3W6vHhiqxojMRjnqvCMg3G5ipNvcOA6UZaXW262kaRrFQh57pvvMzP6Qz+cJh8NlzeuFQkHez2KxWNOdKtyaQvwNBAKyCKdSIBPuWLfbLW87kUgMaf6XKO8yf7aYcz1dLhcej0fuC8y5nvUSooZS4C8Wi/J5AaTgKVyuosBPfA3UNvZUZJg4dU/efKl6PEI1kUFV4Bt7DeekBz8uEzm74jdfmsif3iyNjD6xsJUHPtiEx6bx4jn78NrKcFkm3rRhbt5aHQGgqDvIOcsFAp/PJ0t24vG4XCwR/PSnPy3LGg6Hw4wcOZINGzb0aFsbERHd0ZNMYguLRuDII4/ktddek9/n83l5nOLz+YhGo3z729/mr//7DHiPqXob4XQOv0Mjks7jd+qsMYmPX581nPve31h2+SuO3oO310R5fWW4dP1Unv9bXloE+r/lHcwY4eGdtVHmbYxz6J/f5xuzhvOLI3bnh0/Uzv01VJ2zzjqLkSNHctNNN/H2228Ti8WYO3cuAHPnzmXixIlS5Dz66KN58skne/+AWQwoDRD8ZWFhYWFRL4SY0NzcjNfrBUonN21tbVaWU53xer08+OCDzJkzh4kTJ8qfn3baadx777386U9/oqWlBYA99tiDm266ifvuu48DDzwQgMMOO4x7772Xe+65hxv/6zQUtfwj2W1TefS7M3ntB/ty0efGAaVsoQdO2ZOXztmHu74+DU0tndh/etHBvHzuPrx87j4cPal0gnjY7gHe/OF+vH7eflx7TGk08fjpzbz5w/147Qf7ctMJk6ver2uPmcCkZheGorLXvvtx9913c9ddd3HssccSCAQYO3YsN954I3fffTdXXHEFXq+XQCDANddcw80338xJJ51ENBrFbrfzve99j9bWVjo6OuTrzzAMbDYbxWIRh8NBS0sLzc3NBINBvF4vdrsdwzDIZDLE43HC4TCtra20tbURDoeJxWIkk0lZaNPe3i5b2UOhEIFAQAr7/X6OW5dgS3XQyTowVBgGtnSYlvgqgsFgmZgMgzOy21ucTid+v59MJiMzK4UDUSDcvZUOyP5idv8OBsVikXA4LF2rTU1NOBwO4vE47e3t3Y7fC8dWOBwGSmO6gUBAOsHNiNd+NpuVY+yiOK4RyOVypFIpotEobW1ttLe3E4/HZdmS+JxqamrC5/PJwoqdgWw2K5/zjo4OUqkUmqbJ+xwIBHC5XGXRHPWglsgAlIkMDz7+TM3bECIDgN+p057M8d19RvLI/C093g2esf8odFXhoY82AxBJ5zEMiGcLvLaig+nDPV1ev6A7y76Px+PyeMbr9RKJROTvfvCDHzB//nzp9NwZ8Pv9uN1u4vG4JXBa7DAcffTRvPDCC/L7hQsXsv/++6NpGtOnT2f58uWMHTuW8077Bs+eMZvJLW4uOXL3stt4c3WEoyc3lW5vUhNvrykdM+iqwv5jfLyxKiwv+739RjIm4OAGU27wnNUR9h5V2lfM3s3LivYUNm37cVEknSeZKz8eWNKaYnxo+z7HppR2dOaCzHnz5jF16lQApk6dyrp1pQUcXdeZMWMGH374oby+y+Xq0m1vMTjsHEcTFhYWFrswuq7LnE1RICSy3Xb0ca1Gplo2WnNzM5/5zGc444wzmDFjBueccw6//e1vOffcc7nmmmvYuHEjt956K++++y7vvPMOc+bMwUDhp3c+yaG7B5mzevvJW7VstEPGBVjZnuL0fyzios+N42szhvHIx1uIpPMcdeeHZdt38eHj+P4/P2FJa5IXzt6bUT478zbE+eztH1AoGjz0rRnsN9rH++u3n0S5bSpTh7lZ1pbCadP4wRf349KLvk/eNGp62mmncd999/HJJ59Id+Wpp57Kq6++yhNPPMGf/vQnnn/+edrb2xkxYgSjR48mnU5XzcksFouyObonOZm1yGQyZDIZ7HY7brebYDAox4D7I+wrGDSvmVMqhGqIFEyD5tWvEzE5BqsJg43i8HS5XHi9XlKplHRiQUlwcTqd8jkTRVGhUKiuI9iDLXLC9uIXKInOyWSy1y5LMdIuxp9DoZB04pvvS7FYlKK/WGwQAlujZfdVxkfUGnEXrfPmQqOevBYaNapBZJPWGmvP5/PS4dnfsfajjz66LMtSiAwffPAB06dP5w9/+AOnnnoq5+17EN8L7i5Fht/+32p5HSEy3PveRo6e1MS5j33Kjw8dwz67+fjOPiOZ3OLmDydM5sKnllbdhqMmhfjazGF89YGP5c98Do1YpoCqwIFj/dz2dnk25+LWJBOaXPKzyFDLT08XLlzId7/7XQAOOeQQ5s0rjZuecMIJjBgxgiuvvLLs8qFQiM2bN/f24RtyVFXF7/ej6zrRaNRalLZoSG655RamTp3K+PHjefTRR3nqqafwer20tLSwatUqebn77ruPq6++mh/96Ec8+uijZDIZLrvsMrLOEJ8e/ive+fH+ct9z20lTOe/xxdw9dyMPnrIn399/FM980sb6aGlh8OhJIf5veYdcaFEVuP2kacxdF+Xlc/dhVXuasx79hHvmbuDeb+zJd/cdyZKtSV5dEeaAMT5+f9xkCkWDTL4oMz0FkXSeomHg0EvHqPfceDVqMY+qqrLQ7fHHH+eqq67i+OOPZ9WqVbz3Xilr+KCDDmLu3LllnzsHHXQQr7/++oA89hY9xxI5LSwsLHZAROmBGNUV+WjpdLrhTm53Vqplo40aNYoVK1YA8Omnn3LFFVeg6zrNzc2sWrVKOrWCwaB0a+WcQQxVK8sEgurZaBOaXczbWBKLPtwQ48Q9SyKn167xyrn7sCGa4fx/LaEjlWfRliRBl46mKmiqQjJXJJLePsptDmsXHGVaOT9k9wDJvMFlv76KYjbNDTfcwKpVq5g4cSJf/epXueCCC3jkkUeYM2cO+++/P3fddRctLS3Mnz+fAw88kNdee40FCxaw33778eKLL1bNybTb7QQCAZnf11+EWCDEzkAgIAWG3pbYCJrWv8vmyV/CaIDWe8Uo0rT+XQqFQtlodCQSabh8XTFumUwmSSRKo+Lmk/h0Oo3T6ZTjmIqi4Ha7cblcOJ1OUqkUqVSqX6KVyCAeDOHL4XDg8XhQVZV0Ok0ymcThcOD1etE0rU+OLPF6FmPPTU1N8rbN76NCoUAkEqkqijbqQle1EXcx3i5G3EWL+0CNuA82tcbaRc5qT8fa+yMyXHHFFWQCY/jkwP/qschwyfPbG5Tf+fH+UuC89pgJnLBnC5qiMLHZxc+eXsZtJ00jnMrxwtl7k84VOe7eeVz4mbF8aWozCvDwvM2srvise2pRK/99xO488vEWgi6de2+4kilTJnLzzTdz//33M2fOHF5//XXuvfdeotEol156Kaqqctlll7Fw4UL+8pe/sH79en79618TCATYunXrDnccpOu6bIrv6OjY4bbfYtfh/PPP7/SzeDzOd77znbKftba2ct5553W6rC0dRs1nyprVz3u8ND6eyBb42oMfd7rO80vaeX5Ju/y+aIDrV692ulwyV+SUvy0o+9ncdTGOvKNrp/ed72zgO3uP4L63V3HuWT/ptKSdTqf5+c9/3ul6c+bMYc6cOWU/+/znP89vfvObLv+excBjiZwWFhYWOwiKokjHphj3FQ62ehYbWPSddevWseeee2Kz2TjggAPkqKmiKFKEisfj+P1+wuEwJ5xwAt8762w+SdrYmih3bVTLRvtkS4JjpjTzvwu2ctSkJkKu0sf4Z29/n/ZkntP2Hcmvv7AH//XkUh5fsIVHT5tFJm/wz/mbywTO/cf4GO618+GG8oKHqcPcrGwvZbWN8NqZ2Ozm3LN/z+dnjeess87i5ptvZubMmdxzzz2sWbOGG264gQ8++ACfz0dbWxvRaJStW7eiKAptbW0sXbqUvfbai1Sqev6bOJn3eDx1da2I27XZbLjdbvx+vywK6a2rTs8lGLH0OTZNOX5oW9YNg4kbXmO4z0kslpNuP9HqHY1GG8bF5vF45LileO7FiLKIIhBZkmIczDAM+b3Im3S5XP0SO8UC0EBS2ZhudlumUimKxSI+nw9VVfv8HIn9vBDDRMN6MpksE/zE6140tjscjrKRu0ZHuB7Fa0Zk9JqFTyiJhWbR0zyW3SjvgZ4gork1hwABAABJREFUnq94PI6u6zLH0+l0SkdrNpvtNJXRlcigKIrMM04kElx88cUy77ilpQVFUUhtizXojcggMF/n0n+v4NJ/ryj7/ZT/eavTda5+eRVXv7yq5m2u6kijqwoBp044leec83+GPV2+gPjQQw/x0EMPlf1MRL+Y+epXv8rf//73mn+rERExQ1bBkMWugIKBK7qORGjC0B5TmXh2cRsYBp7oun7P7Fx++eV12SaL/mGJnBYWFhYNjjjpETmD2WyWSCRijTI1IOFwmEceeYTbbruNxYsXs3LlSilIC7xer8wmfOqpp/jHax/xX9f9mZNmDCtrna0WwP70J20cPiHEi+fsw6LNcTbFS6+B9mRJwHz04y2cecBuANxw/GSOvONDVnakeOy7s5g+3M0nW5KM9ju48fjJnNzFiSxAOJXjzdVhNLuTefPmyXHBrVu38u6775LP51m+fDkOh4OOjg5UVSWTyeB2u9m4cWOXt20mkUgQCoVwOBx9dlvWIpfLEYlE0HUdt9uN1+vF7XZL4aynDFv1GpGRs0n5Rg9NOVSxgCu6Du+nL1DweggGgzKzNBKJ4Pf78fv98j4N5Umyz+fD4XAQi8WkoOx0OvF6vXIxpvL3ZmqJnclkslfPGQysyKlpmsyQFc3n1dy0mUyGQqFQ5rrtqxNRuADNxU3VxvvT6bR8L4rL9UXgH2qE81vsF8wj7qKkSYy4i8feZrMNaLnPQFFtrF24g81j7ULw7EnpmzkWRLwOC4UC+UI76rQMRd0xhPe4nHMeKxXcqfkMtnTf8+zuv//+em3SoGAVDFnsirgia0gEx4PSOGWbGEXckTXdX85ih2DoZ68sLCwsLDpht9vx+Xy0tLTIEaZ4PC7dcpbA2bg8/fTTnH322bzyyiu8//776LpOa2srY8aMkSPU4XBYitaKkSeazpPMlQsftQLYL35mGV/4y4e0JfM8uagVm6Zg3xas/tnxQZa3bXPGAR2pHIYB4XQev0PHa9f427dncN7ji9ma6Oz+XdKaZI+mkltq7roY04Z5oJBj4sSJrF69mmg0yooVK5g4cSKKojBmzBhaW1uZN28eBx10EFDKI5o/fz4Ao0ePlk2/tcjn86TTaTyersso+oNoce/o6CCXy+HxeGhubsbtdveorEfBYOzHf0fBAGOQR2WNovz7xULJ6SOauJuamnC5XESjUdm4PZT4/f5OAqbX68Xn85FKpVAUBV3XiUQi3QpuQuwUhT3iORNuvp4wECKnqqr4fD5CoRCqqsqioK7iAszRFqFQqN8FO6lUivb2dpLJJE6ns+pr2fz45XI5uc31KuUaCsSIeyKRIBKJyFIzEYcApddgS0sLoVAIr9eLw+Goe8HPQCNcnKlUSuZrK4qCy+UiFAqVFbWJLG4hkIr9nLmsLRqNkkgkSKfTpYzTbYsmDVOoJjBKDq/G8HYNPD6fD4/HQyKRsAROi10KX/vSoVkw7gpVw9tePWvYYsfDcnJaWFhYNAi6rkvXpvmkJZ1O77AZZDs71bLRrrvuOkKhEBs3buR3v/sdHo+HP//5z1x55ZVomsZtt90GwIknnsgxxxyDodmYl7Pz9KetQNfZaCO8dh46dQZFw+CVZR28vjLMcK+Np78/m0SuSNYUqn7Ny6t45ozZ5IoGi7cmeWdtlEs/P549Qi7+eOIUAK58cSX/WRmW9+flZR18b79RALQlczyxcCt/vuYSdMXgd7/7HdlslltuuYVf/vKX2Gw2HnvsMdLpNI8//jjXXnstJ554Iq+//jpbtpQcqfvuu2+PsomSySShUEiOJw8UhUKBWCxGIpEocwmK0d+u3F/OxBbGzn+INbNPK4kDgzFmtW17xs5/CGdyq/xxJpMhm81KF5DT6SQej8tIC4/HM+gnzaLVXizCmPM34/G4FCfD4XCvhEdRTCWcnWIUvjtnp6Iocj9aD8yZoYZhEI/He+WMFM3r5niB/ixWGYYhH4OuHK+inCiVSuH1eqULuLLEaEdFOCCFW7a9vb1sxN3pdMrM38oR96FEuC/NLsxqbkyRSSruo2EYcoxf13VUVZW/z2azPT5WsJxUQ4eiKDLKJhqN1n2CwcKi0fG2LsGW6iDnDDbGyLphYEuH8bYuGeotsagTyt57791gy3gWFhYWuw6qqsqcTV3X5WheJpMZ8pMwi/5js9kIBoO0t7fXFBQMFBYedW3DjA7+5ksTuWfuBpa1pVDzGWa8/EvsNhuBQIBMJkMsFsPhcODz+Uin02Wt2WZGjBjBaaedxvXXX9+jvytcSe3t7YM2aiqEKyGEVMs5rKR9twNYN/Ob225gAAditjlGxyz4B00b3qt5MU3T8Pl8ckzXbrdLx5uIRRhIxAm7pmlEo1FyuVxZiUYymcTj8VAsFvs1qi1QVVU+Z8ViseYYtq7rhEKhLt97PUWMh4u29P5mXIp2eXNmaX8xPy7CxVntcbHb7Xi9XlmQlEgkdrjR7mo4HA78fj9bt24t+7lwD5ub3M0j7mbhs96PQ62Rck3Tyly3YoxcjJebv7raJlVVcTgc2O12eb/y+bxcBOnqGCLWMpWV+51b1/tbD/Z4/058rYuHejMGDE3TCAQCAESjUes4z2KXZcv4I9k05csDexzVU4wiI5c8w/BVrw71lljUCUvktLCwsBhkFEUpy9kURRxilMxi50GII21tbV1ebvkBP2yoEHagFMLesYKJc/8MbBcRRF6i+N7cnt0fFEWhublZjmgOJmIU1OVyoSgKmUymU4O1mfCIvVi713cwUAZm5KpYKI2oz3+I4Ob5PbqKcHCqqkoqlcLpdA54kYWqqgQCATm2nc/nZf6mKJDx+Xzkcrm6FyNpmobb7cbhcFQVO2uJXr2hsjG9noKgcKWmUqmaCwV9QVVVWTpULBbluHMlYt8E9CnvtNHozfMtXJBC9BTj7MIVKkTP7sRxUfJTzYlpHpEXbsxaYmY9UBRFtrXb7XZUVZVZnJUt9lBaXPv0c5c2nJNq2n+uLcWC7ISYC4ai0ag1oWOxS5O3efjkiMsx1KEfLFaKeaa/eiV6bsco6bPonqF/VVlYWFjsIjgcDum6AKTTyhpV2nmx2WxVnRqVJ8eB1CaSwfEYDTQ6qBhFQunNuFwuWXgRj8fxer0Ui0VZlODz+QD6LUyK8VtRDDSYJ4DibyeTSSl2hkIhstksyWSy03MY3Dwf55ubWDvrVFL+sfUVCQwDV2w9Y+f/rWxEvTuEwOfz+XC5XOTzeTRN63fZTS1UVSUYDALbR9C9Xq+MHCgUCvj9/gEr1BDRA+I1I0qlhNjZnzxOm82G1+tF1/UBG+0WIrrP55Mu2HoIqGI8XThohaiSSCTKhK5UKiVzXD0eDy6Xq6YguiMg3Jk9QYiZ4j2jqmpZoZHD4ZAj7vl8nmKxKN8/tdyYZuEym82WCZqDsS8Ti6Xi+bPZbFLwFBELQuwUY+3Na+aUnFQNkYJp0LzmjZ1W4HS5XHg8nkFz2FtYNDp6LsGIpc+xacrxQ7vQYhiMXPqcJXDuZFgip4WFhcUAYrPZpLipqiq5XI54PE4mk9kpRgQtukbXdfL5PC6XS+aoVWauFQoFQtFVbBh92BBuaWcMVSMQWSHbfcXJezqdLhM6oSSsCaGwP6RSKXkyOFRFDKJ53el0dhI7zU5rZ2ILk97+I63jD2fT5GMxFBVQ+nawbhiAgWIUmbjhNVpW/YdksvfuPrFP6ejowOv1ylxKIXTWS6gTI5eGYUinqMjkjMVi6LqO1+uVrt+BxCx2ejwefD4fbre7Ty450dhtt9vJZrM1G9PrhWjKFjmd9RSjC4WCLKjyeDwEAgFyuRzJZFKKneaxdiGIis+oXWWMVrxHAClqAlLEFIuSUHq8hCtTTF6IrMxGQ7hRE4kEmqZJwVPsF3K5HO7YJ2w2vrRt3zW0KEaRpvXvDvVmDAhi8Wcw9ocWFjsSw1a9RmTkbFK+0UNTRLSthK1l1WuD/7ctBhRrXN3CwsKizmiaJnM2hZsonU6TyWQa8mTIon+I0UQhYJpHFoXTp1rWmjlzrdFHB4VYb7fb0TRNimnCNedwOOomaolx546OjoZ4vzgcDtxuN7qudxKJBHmbh/bRB9A27jPkXCGUYqEkHHT1XBolUdNQNRyZCC1r3iCw9m18uoHH4+nT/a8c2RUj7MLlJkbK+4Ou6wQCAVmko2kafr8fRVGIRCK43W7sdnuvi3nqhaZpZePaYlGpK8SItxjxr3Q9DjSDkdNns9nweDzYbDYpflXGo5gvI5zbO8pIbVfRINXKfcQ+2rzgVLmfNn+vKEpZrqcYRy8UCmW5no2wz+qKyhziNSMOYtnoI4fcSTVqydMM28ny8BRFwe/3y8WfHdUlbWExkKQ9w1l66M+2HTMN4oKLUUQxikyec32vpmYsdgwskdPCwsKiDogCAKfTia7rMgsrnU7vMo6YnZlaJ8nmkcXK3DUo5e71tPhkRwlhF64gUcQiXEGGYUhxq7/5fk1NTTK3rFEQ91lEECSTyU4nrQYK8ZYpZEfNJObZjbhnVNVCKTWfwR1bT1N2C7YNCxiV2YBN1+no6KBYLBIKhWRRT28QAnFra6v8maIoeL1eWUjTH/HRZrPh9/spFApEIhHsdjs+n498Pi+jC8wFRENJS0sLhUJBuqmrCZfmxvSuSowGg8rG5YESWc2v42w2SyKR6PQZZc4iTaVSJJPJhp48UBRFCtvJZLLmYpNhGDUXnIrFYq/uo6qqZbmeuq6Xjbibhc9GQVVVWfCWzWaJx+MoqsbiA35EwjtqaJrWtzmpJr1zy041qi4WLsTij3UcaGFRm/CIvVgz+zT6PA3TW7ZNz4yb92CPc88tdiwskdPCwsKij4gCIYfDgc1mA5CZWIPpArKoD7VKJMzj5eaTZDGmaD5JNiPEk+5KhwQ7Wgi7oigEg0FUVZWt3uJxEo7HvjYWC0diOBxuKJEASkKfcCuKcp1KYUwWKCVT5JxBCroTQ9VRinm0fBpbOoyCQUtLixwXDoVCUjy02WwEg8FeZ/ZWEznN2y0cl30pAhKlGblcjkgkgsfjwe12y0b6RmoMVlWV5uZmOf4tRtDNYqd4fwJSyGsE/H7/oDhh7XY7Ho+ny9xRt9uN2+2WURRDWU5UudBk/r6WG7OaM3MgMQueNpsNVVXLWtzFv0PhjhXPZbUyKstJVV/EvlYsVO0obmgLi6GkfbcDWDfzm6VvBnI/ZJTej2MW/IOmDe8N3N+xGFIskdPCwsKil9jtdpxOp8zqyuVypNNpstlsQ7tdLEpUCpi1cjJrffUUkVnYGzfilvFHNEQIe09HB0XeoziZE/l/QvQ3DINcLieLi3pzsmcutWlEdF2X7d6FQkFmeWqaRlNTU48EWvNrxGazEQgEZLu83+9H3+bu7Ol+ReSZVhM5BcFgEJvN1uNRbiiJzj6fj2w2SywWkyOYwgXYaCf0QiQ2u6jN4rR4PBvVpSgE5GQy2e9Cr+4wuzaF2Gl+DlVVlePNhUJhwEb5RTZmrabyrtyYxWJRFu20t7fXfdv6iqZp3Y64ixKkgULkcApXbq3Xk+Wkqg9ioUnsKxtt32Jh0ciER+zF2r2+g4EyMBmdxQIKBmPnP7RT7XcsOmOJnLsABkrJSWJzYig6ipFHy213klhYWHSPrusyZ1MUCAnXZiOc1FuUI0YJq50w9zQns78IN19vHGIGCssOvmDIQ9h7MzooGrzz+bwcrza7/ERzsaIo5PN52e7bnQAohKpIJNLQzmhN06TYKRqMnU4nra2t3b6OKkVJl8uF1+uV441NTU1dChPVbq9WLqEZ8fwAcmy1loAvTtozmYx0bCqKQjQaRVVVfD5fn5yhA0k1R6vZuVgsFuV+vFo2ZSNgFksGI7bBnN8q3LnmzzZN0/B6vbKUqdqYe3cIwbKWmCkQjeS1xsqrIdy6HR0dfXsABoHKXM9aI+75fL7f7yVzNm1373GB5aTqH6JgaDAWJywsdlbSnuGsnXUqKf/Y+i64GAauyBrGfvz3nco5blEdS+TcCRGZYLGmyaQC40j5x9TMBHNF1+GKrMHXvhRv6xJL9LSwMKFpmszZFAVCImez0csFdgX6kpNpHjMfSEFGjMv2Zdx66EYHDRSj0KfRQeFCzGQyshVd5ECKwgXR7ivKi4rFohQ8a7mgA4EAqqo2tHAhMDveoGcuQV3XCYVCZa8T4ZLs6OjA6XTidru7LSESi5m6N4jD5SEabut2MVOIaIJqYqoQXUX0gDmPU5RNpdNp+Zw3CmbBq7IxXYhzPSniGWpERIBYQBgMEVkI5YqiVH0Nm8XidDrdyflZy4lp3i8DZaJlPRaZvF6vfN/sSJjH280j7pWFRr1ZTDWPpsfj8V4tEllOqt5jLhgaqsI1C4udCQOF1vGHs2nysaVj4b46zLc5xhWjyMilz9Gy6jVL69hFsETOnYhSu+uBtI07jJwrBMVC6QS5m3ZXjCKoGrZUB81r3qBp/Vz0nLUCabFroiiKdGyKkU7h2Gy0E+BdgXrnZA4WIlOyJ26+agzN6CDMXPEErrVz+9QCK+6zuWG9UugU6LouBU+bzSZz60SerRD0NE0jFArtUCeOwWBQjt7WcsWZqXT8Kooii4fC4XDVEqJ6LWYKEU04G82iiHB7CuHT4/FIQVM4lszPdSMhskcNw8DhcHTZmN6TIp6hRNd1/H4/AJFIZFAW2BRFkWKnYRgyisEwDNlI7nQ6paAvXttiHBuqLzCZBc164/V60XW9YeMteooYcRfCp66XMprF55vZ7VmJeTQ9mUz2+b05kE4qX2IDoz58cKdxUmmaht/vR1XVhihcs7DYmShpGwfQNu4zfdY2Wta8QWj9u11mzFvsfFgi506AgcLW8YezuY6rHSOWPscwa7XDYhdCODZFlmA2m5U5mxYDz2DlZA4WYkywP/lwQzE6uHtsMU6ns88N6cL5F4vFpCjp8/lwOBw1G6NVVS1zeSqKQqFQkA5Ph8OB3W7vcYHTUGMuE3K5XLhcLhRFkePela9X0UhuFmd0XScYDJJOp8lkMrKEKFHU676Yqes6gUBAjgjb7XYKhQKaphGPx2XWYSKRIJVKDVoxTl9RFIWmpiY5BlxZslILs0Ox0cROVVWlq3kwhBTzvtjhcEihDejkxhTbZxiGnHQQr6XBpNr7aGeg1oi7yDsWLk+xn8xkMsTj8X4//gPhpNpt+b+ZGltAPBZtyH1Hb7EKhiwsBofeLuy6I2vwWlOquzSWyLmDM6C5FdG1pdyKxJb63a6FRQNhs9mka1NRFFmOkslkGiZbbmeiEXIyB4tgMEihUOj3GO9QjA663W48Hg+pVIp4PN7rm/R4PLhcrjJRszuh04wQ1RwOB5qmyedd5Mo18omkGD/v6OgoE8iE2Cma6JPJpPy9GBtva2sre407nU58Ph/RaBS7w8nGMYexcvThA7KYqWmabEcvFotS1CoWiyiKQiwWI5fLEQgE0HW9R8/jUCAa04Wo3Jf3X2XruPm5GkrMI7GVzui+3FZXY+UC4cYUTl8RMyHcyQJVVfF4PDidTvL5PPF4fNAdbT6fD1VVyxzPOyvm8XaxMATIxaG+jLjXor9OKns6zOjNcxkbXoieT8nXUjgcboj3VV8R++1GyyO2sNgVkH0juhND1Qn6PBSSEfIdmyxR0wKwRM4dGis3x8Ki9+i6Ll2bqqrKEVnhPrHoH33JyTSPmO8sJwrCzdcXN2QlQxHCLtq0+3oCJ9x+5hNZ8bPeCGSapmG326VAqCiKbGvPZrMNd5JcrfCm8vdutxtN06TYWSgUaG5urlqw5PP5yPlHsWDsl0n6xwzoYqaqqoRCITlibx5FTiQSeDweAKLRaEM+7ubSHJfL1ac8XDMOhwO32y3FzkQi0RCu8Z5GBXRV8FNZ8lNrrLzyM1GUbAkxM5lMdoqhENmYPS27qRciomBXEDmh9Pr0eDyyNV3kzJpH3IvFYlmuZ3/et3aHk9zovdhgG0HSP7bXTqpqjv3eFNE1EiLKo68LgRYWFvUlFAqRy+Ws96OFxBI5d1CsBkQLi54j8sPE2J0oEMpkMg13sr4jUCsnU4zRwfaczMqMzKHMyRwsNE2jqampk5uvPwxFCHt/R/GE6y8cDkuhQwidkUikVye1YgQ5n8/LkWqRISkc2LlcbshF8p7mApoFtFwuJx2elQfokZGzWTPrOxgKoAzcYua4j//GuNQqmUMsFoDi8bgUrcTroJH2mcJ1qWmaFCLF+H1bW1td9jVCTDL/jaEWO0UshPgcqxQzze54sxuz2r64L+8Zc3N3tbxTczakKCca6PemyC0djCb6ocTccl9rNF2MuJsdn2LE3Zzr2Zt9psidFPu2SieVUsyj5bsuOxPY7XYCgQDZbFa+ZntSRDfUKIqCz+dr6LgOC4tdERG502gliBZDhyVy7oCUCilOL30zaIUUBuPmPWg5Oi12GBRFKcvZNOeF7UiOgaGkWkbmjpyTOVh05+brz+06gsNZ6Z3C5t0OHpQQdvMYc28dfIqiyBKejo4OedLaV6HT5XLh8Xhob2+XI9Uih07XdZlRJ06Sh+I12Fs3gbn0xjCMMpfr4C5mKkxb9TTDNn+I3W4nn8+jaRqpVAqXy0WhUJDCWb0cyv2hK8egeJ0MxPvP7XajqmrNfNV6U028rLYfNguYlWLmQC4qmZvrqzXUi/IioKxcayAIBALyPbQzoiiKjGMoFAq9jgQwC57i8xyQoqf4t9ZrurIgrb8IsbC9vV069s1FdEO9L69ksHNxLSwses6u5uS36B5L5NzBSHuGs/TQn5WcPAN50lOJUUQxikyec/1O04hosXPicDik8AGU5WxadKZWTqY5l21nyckcLIT40tHRUbfbNBfRxOPxQQ1hVxSlz1mMqqoSDAalC1C8XgKBADabrde5bE1NTeRyuU6r9WIU0uFwSNfSUIxCtrS09MnhI5x5UBId1nkmsHzPU0u/HLTFTJi54gmca94lm80SCASkeBUOh/ststQDs4OwmqgGA/P+M1MpdiYSiT4LiYqi1Iz4qHRj1hIvvV7vkBef2Gw2PB5P1YZ68+umN0VQvWVndvI4HA68Xm9dFxlUVS0rNBKRMtVG3G02G8FgkPb29roJjsKdn81my56zRhxrF+5wwzCIRCINIbpaWFhsp6dTNBa7DpbIuQNhoLDs4AtI+UYPTAZndxQLuKLrmPTOLVao7w6KHC+yOTEUHcXIo+V6Nl7UyNhsNiluqqpKLpeTrcSWCGflZA429c4GUhSFUChEsViseQDXn9HBntLXVm1N0wgGg+Tz+bJVdiGc9mYEWpTxdBcFYD5JHqxRyP4IAYqi0NzcTDKZJOsdwYezz9u2mDkIAqfAMFCMApPnXE+LWnJwZrNZ7HZ7Wfac2UUpRfcB3keoqiqzILsTygZL7DKLnaKIp5rIWOm+rBQyBaKRvNpiUlfipdlhNtRxApWlTebRflVV8Xq9XQrU/aFeZW+NhKZp+Hw++V7rj6DeHYqilLk9zSPulQVk9Xq/OxwO/H5/1TxigdiPi+O7wR5r728+tYWFxcAjFj/b29uHelMsGgRL5NyB2DL+CDZNOX5wT3oqMQxGLXmaYateHbptsOgxvXV7uSJr8PXR7TXYaJomczY1TaNQKEhhc1dcZbdyMhuHvrr5aiHEwI6OjiF/nkThQjKZJJFI9Ph6NpuNQCDQqfE6GAyiaVqvxBkh+PZ0LEnX9U6jkMIZVM/9RX/HpIPBIIWiwQd7fn9IFzO9iY0csOSvJOIxWUDk9XrJZrNlo8Ci7AcgkUgMSD6doihy5NkwDJLJZLcutqampk7t3wOJ2D7hOCsUCmX741puzEpBsz/iST2b1+uBuRSn0u1qdn3WM+NULKTsDMUTiqLItvqhck1Dad8pIgkAKcrn8/mybM/+PH+BQABN08oiTbraHiF4VkaUZDKZun8+WgVDFhY7BuJzuK2tbag3xaJBsETOHYS8zcMnR1yOoepDvSkoxTzTX72yR3luFkND3uahffSBtI07rM+5fc1r3qBp/Vz0XM+FjIFGVVWZs6nrOsViUeZsNlIZxkBSLSPTyslsHHRdJxQK1W2sz+1243a7e51fOZDUEr26Q7h2zK3QYhRe07SygqKuEKUVfWnPVhSlbKxdVVUKhUKZM6iv+Hw+6abrC263m63jj2D5mM8P+WLmmOXP0bT8Zfkju92O3+/v5GYSYozL5ZLu5Xrti82N6SILsCdC4LBhw4hGo3UV+kQjea2xcoHYPuE2E0VZg7X/9fl8OJ1O4vH4kOemQtdu18p28J4+v7XYWdp1zYsHPRH1BxpRpCcWosy5nmIRVYy4m4XPnqKqKk1NTaRSqV4tnHUVUVKPUkkxudAIGcQWFhZdM1BZ+BY7LkOvmFn0iPbRB25r1B16DEWlffSBDLfcnA2HgcLW8Yez2dzADD1zBCmKbO/NOYNsmnIcmycfy4ilzzGsiwbmgUYUCIkDWUC6P/ojSDQytUbLa+VkimB+MWZuMXQIp2A9BA1RSpNMJhtG4ARIp9MUi0X8fj/BYLAsa7MrRBOwyBBMp9My4ywQCBAMBnskdIpcNq/X2+vcRVFAJgQwm80mT5RdLheGYZQJnr1xBglnWl9JGjorRh8+tAIngKKwfsIX8a95Sy5mZrNZwuGwfJ5E/qNhGNK17PV6ZW5sf9q0zeJXV2Pg1RD7yL68/2oV/JjdmOK2xf7XvO8Vf1MIe06nk1QqNaixKbFYjEKhgNfrRdO0IRf80uk06XRaumzEY5JMJuX7UOR1Op3Ofot6O/IocWUMxECOpvcGu90u94tAp4xz83i7cLNXtrgLsb8aIn7C4/H0SpwUnyHpdFq2yYtFcLfbLRcaMplMr3Ok/X4/mqb1OoPawsJiaBCRGiJiw8LCEjl3AAwU2sYdhhSshhyFtnGfGVLhy6Izac9w1s46lZR/bP9PkhUFUDAUlU1TjicycjZjP/47zsSWumxrT7Db7TidTlkgJMpGBiODaTDobU6mOPi3cjIbG13X6yI0q6qKz+cjm80O2thtb8hms3R0dBAIBAiFQj0uY0ilUjKbT5yECqEzGAz2WOiMx+OEQiEcDke/hEVxEp5IJMoafkXJRz6flyfJXT2vYjy5P8/9lhH7NfRiZj6f7yR0iudJ/E6IWQ6Hg3g83qvnph5jzF2JnNUiPcz7YXnfTftdIXT3xg2fSqWksCe+6uFU7Cmi9d3n80mhZqg/LyofEyF2iscllUrh8XikKzgej/daXFKGenGgj1SOpneXNzzY2O32Lp+LSuempmlS9HQ4HLjdbqD0njQXGpnfS6lUSmZf9qUwzLw4BaXPYVFA6XQ65Vi72JfXElx1Xcfv9wP0eLLAwsJi6BGfcWI6x8LCGlffAYi1TGXlfucO9WZ0Yo/378TXuvj/s3fecXKU9R9/z8z2fnuX3kMKkEYvAUGKFLGhYERA5EdHQFQEBOlFBFQEpClFilRBpRfpYAoEUoD0Xi65tre9zczvj80zmd3bu1y/vdy8X697Jbu3tzu7O/PMPJ/n8/1++nozLIDIkOmsn34yOlLP9HHTVCR0Ri18gtCWhd3//Nuw2WxGn00RICRcA5XgaOgo7e2T2VppeX98zwOdcDhsCDRdIRQKIctyu/qU9SXm0JNoNNpux6koBTSnq0uSRCgUQpIkIpHIDvf/QCCAzWbrkUbzoqxd/Owo8EKU0Dc0NHTquNWRWHLIleRcob53cgLoOvZ0hF3fv6nFYuaOgm5kWTZEG7E41dakoz2J6e1FvK4Qrc1iZmnIT7nxtrvHXdFT1O12d7jsvqvY7XYCgUCfJ6+XYk5bL+2zWrovdKT9QW/3Yu0OeqOvbVcQgWhd6TEty3JRoJG5xN3s9tQ0jaqqKpLJZLd+h6LNkejJ3NrilRBZRUBeJZ93LSwsihFtNSptkcii77BEzl7A5/Nx7733Mn78eH7yk5+wcuVKAE499VQOP/xwkskk11xzDfX19YwdO5arrroKRVG45557mDt3Lrl9Z3HXZWcjSRLxjMqsJxaTzm+/WPXYZR6dtTuDfA5e/LKe299fR8Cp8PqZe7L7YA8z7/mUL7YUJtzPnjKVao8dl03msldX8sHqCKfvM4zT9h6GXZF4d2UTV76+ijFVLub8bB++2Fr4u1lPLKY+YZp0aCq/npymae5LzJ07l/vvv5/ddtuNK6+8kg8++ACAI444gtNPPx1N03j55Zd5+umnOeqoo9A0jbfeequXPv2dn8bh+7Jh6g8LN3rSBaQX9rmRi58mvOmTbntaRVGMEiMRICT6bPaX1TirT6YFFCaENTU1Xe4H6PP5cLlcNDU19Yv9wxx60pHJsAhUMjtmOiJ0KopCVVVVr/RMMzuDSgMvstmsUe7e2ab3/W0x0/ydt1bSabfbjbLpcuJeaWJ6e9x7pQtHpcnlpYtHrYmZvS1glAp7wsXY09uhKArBYBCAaDRaUZO/0u8/mUwaY4fYd2w2W7tLt6urqyuih2V7MJemi16UlSiqiT7KnV28aQ2z4Cl6I4tgLlmWSSQSPbK4XW7xSowJok2AORjPwsKifyDLMtXV1TQ3N1stJiwAS+TsFWw2G36/n4svvphHH32UlStXUl1dzc0338w555zDlClT+M53vsPvfvc7br/9du68804aGhq4++67Of300znmN/cSsYV4YO4mrjx8LKsaUjy5YIvx/BceNJJkVuXBeZt55fQZnPnPr9gazxFy27j1mxP4w/vrDJHTrkjkVJ0xVS4e+P6uHP3g58Z9AP89e09+8tSX2BSJ2745gR8+sbj8m9J1Xj91Er8+9TtAodH/97//fb788ktD5Hz88cc555xzSCaTPPXUU/zoRz9ClmX+8Ic/cPHFF/fcBz6AiAyZzroZPync6A33j64DOqMXPNYlR6ckSYZj0263GwFCmUymonoPmulon0xzj8z+IFRZdB273U4oFOrShNDlcuH3+7s9OKU38Pl8uN3udievmwVNs2NVlmVCoRDADoVOn8+H0+mksbGx10QCEXhRVVXFH//4R8aMGcOFF17I6tWricViPPfcc2zdWmjt8be//Y05c+aUXcAcPHgw119/PTabjX8v3sJNX9iLnPhDfA7+/sPd8Thk7p+9kSc+38L0YT7+8r3J5FSNWEblpH8sJpnTGBFw8pfvTcLntPHB6gjXvbWaq48cx/enDqIhmWP+xhi/fnkF39qtmisOG0tO05m/McYvXlze4v3ddPR4Hv5kMyvq4gxa+z6nj89z6aWXcvjhhwMwePBgrrjiCjweD1988QVPPPEE++23H2eddRbJZJKTTz4ZgKlTp7LXXnvx7LPP4vV6jd572Wy2SPArdbCVa+Uh7mvNjSn+73a7yefzFStU9IXYKcK9bDZbRfYYFM5fp9Np7CNi7OtI+FR/EDklSTIWsbo7qKsnEC0PIpFIj75OaYm7WKwwl7iLRPfuxCymQ3HZe0d7MltYWPQtPRE6aNF/sXpy9gL5fL5Fj5lhw4axatUqAJYsWcI111wDFA7QdevWARTCGEJVLIoqTB1R+Kqq3Dbqk8Ui0MzRQS59ZQUAb65o5MDRQf65uK7YebkNIWb6HYohfIr7FFkiksrTmMox2Odg5pgg756zFx+uifDb11cVPc+0YT5WRDV0Cp1C6+rqWrzWmjVr8Hq9hjNPOCvS6TThcLhHSgwHEmnvYNZPPxnQe9bBaUaSQNdZP/1kXB9txpVs+b23hXBsigChbDZbUatuHe2TKdymVp9MC8AQ7Ds7MRLuHhFW0t+Ix+OoqmqExuxIaDL34gwGg0aJoKZpRCKRovtb+0yTyaQRNNHVFgHtRQRe1NXVcf755/PLX/7SaHgfCoVIpVL86le/Kgq8uOCCC7juuuuMBcy5c+dy+umn89BDDzF37lxueuRfDFu/js3x7eftS78+mtveX8u7qyK8d85ePLe4ji+3JPjavZ8CcPWR4zh+yiCe+HwLt35zAuf/aymbosVj6ZWvreTlJdvdpQs2xfnaffNRNZ0nfjSFvUf4+XTj9u/JY5eZPMjDioYUSDLp0BiOPHIctbW1xmN+8YtfcNNNNxnnfZ/Px5IlSzjttNO49957jcctXryYs846i8cee4xMJoPP5yMQCBjjpOgzbLPZCAaDZd2YQrwU5aVmQbPceCuuOSoVIeqmUimjf6lYGOgpcU7XdSKRCIFAgEAg0KXS455A0zRisRjJZBKv10sgECCfzxvidyaTKQouaq2su9IDJ8R7gEJAVCV9B63hcDh6RTQ2X1OlUimqqqoM8d8sfAoHvVn47Ox3LsR1RVGM/sKlPZnNbv1KFqMtLCwK57r+2pvZovuxRM4+YsOGDey+++7Y7Xb23Xdfo5zI7FKIx+N4Bo1kzqYUN35zV07ZcyhNqbwhaAqq3HaimcJFfTSdJ+yxt/na75yzF5Nq3Pz0ma+M+y49dAxn7z+cN5c3ksppbI5mmHTb/0jmNB74wa58f+ognl+8XdDadZCXlU1Zcq4qHOnyTcLfeOMNHn/8cVRV5W9/+5tx/6ZNmxg/frwlcnYBHYn1004q9ODs7aAKqVBWtH7aSUyYc9cOw6fsdrvh2hQXjSKQoi8mJOX6ZIoy83JCZml6rrWyb9EaXQkdEuW/+Xy+zxORu0IqlUJVVSOddke9zUS/wFAoRCAQoLm52bjfLHRGIpGyzyPKXD0eD6lUqlePT7GAKS6sRe9Jl8tVVJHR1NTE0KFD2bRpk9HvLRQKMWrUKJYuXYqOxOd1OWaODfFP03l235EBLnl5BboOn26MMnWIt0iQdNtkltYnsckSY6pc3H7cRAb5HFz9+kr+ty4KwHVHjedXh4zmhv+u4Z2VTaxv3i6eZ1UNreQzPWJCmNnb/hZJ4ruH7subbz3FT049FSjs48OHD+dXv/oVVVVV3HPPPSxYsABN0wxxG7YvGK1bt469996b5cuXY7fbiyYhokxZjKtCxOzsWCtJErIs9wsxQjgWU6kUHo8Hr9eLx+PpUbEzGo3i9XoNd15vLQq0F1VViUaj2Gw2vF4vwWDQ6NEqStnF9rvdbsMVXOmY2zaI0vtKFmMFooy8tz/jfD5PKpXC5XLR2Nho7Kfmvp7mFPfSQKP2jBulAUNizBDOanNZu3gtcT1oDjiysLCoHES7CwsLAGtP6CMikQjPPvss9957LwcddBCrV68GKDo5+3w+GlNZLjlkDHd+tJ4Zd8zlP1/WcfZ+I4qfK50j4CyUuAVcNhqTbZf7Hnb/fA64+xN+d+wuxn23vreWSbf9j2F+B/uPCpBVdZK5wra8sLiO6cN8ZZ9LtblafZ2LLrqIE088ke985zscd9xx+P3+NrfLov3UjT20kKLeEyFD7UFWSAVHUz/20LK/FpOU6upqQqEQNpuNZDJJQ0MDkUiEdDrdK73IHA4HHo8Hv99PKBSiurqampoaqqqqCAQCuFwuI+AokUjQ3NxMY2Mj9fX1NDU1EY1GDddIey+eLQYudru90+0WAoEAkiQRjUa7eat6n2w2SyQSQVEUQqFQUUuHcghxw263F50nhABqLmsvh3D8CJdUbyMuqkVi8GmnncZPf/pT3nnnHU466SQ0TTP6h4pgqurqalatWsW+++6L5g1zyIQaqtzFC5R2RUYMk81p1VjAPHpSmE8u2pdDx4dY0ZCixmtnj+E+LntlBac8+QV/+vYkAO76aD373DmPH/1jMX/69kTsyvbPb5+Rfgb7HHy2qVhQnzzIw+rGgsgmS3DCHsN56b25xu9DoRCTJk3ijjvu4KqrruLSSy/F7XYb4qIsy9TU1Bhjf0NDA5MnT8bhcKBpGqlUimg0anxnInimO8batpLVKxXRi7SxsZFMJmOcN91ud4+8XiKRIBaL4Xa7DZGn0hALAaJEWix0CHd4U1MTmqYRDAYNBzBUnpNTlmXj2kO4aePxeEVtY1s4HA7DRd3biB6s5vOBED+j0SgNDQ00NjYSj8fJ5XJGyFZ1dTXhcJhAIIDb7TbK0M04HA5CoZCxkFbu/em6TiaTIRaLGdetmUzGCJirqakxriEt55iFRWVgOTktzFhOzj7kpZde4qWXXmLvvfc2Lubq6+sZOXIkjY2NBINBmqJJJDBKzxuSOYb4nEXP8/HaZo6cGObhTzZz5IQwZ/9zSauvaZMl8ppOPKsS3+b+dCgSWVVH0yGR1UjmVHwOhXi28PuDxwZZUlecdLi0Psmh40Pocuu7kBCORE9Cp9NJLBZj+PDhhqhr0XHydi9bJh7b9wm8kkTtxGOp2jgXWy6JLMuGY9NmsxklnaIksSfoaJ9MUXJkBf5YdDcivbkz+7rH48Fut1dUAnJXES7HYDBIKBTaYfK6SOEOBAKoqmqk66qqWrak3YwQyrxer+Ek7WnEhbRwD6qqaoidiUQCm83Gu+++y/e//33S6bTh0DULuf/617+46KKL+MGPTmFNJENtvLhFQU7VRIcQgi7FWMB8fVkjry9r5JJDRnP2fiO486P1rGhIGS7NnKajyBJNqcK+uDWeY8nWBCODTlY3phkRcPLHb03kB48tavM9nrLnUJ5duBXZ7TcEG1mW2bx5s/EZ67qO3+83BF6x/5rLSFVVbZF4mslkSKVS+Hw+gsEgmUyGeDzepf2/P4qcAiF2iv3Y6/UaZezdXdacTqfRNI1AIEAoFKrYJOlcLkckEsHhcOD1eqmqqiKTyRgLkub7K63021ya3l97xDkcjj5zLOq6TjweJxgM4nQ6y35+pddxkiQZTk+73W70cjWXuCuKgsvl6nDAkPj7RCJhLKA7nU58Pp8x/mWzWcONbmFh0ftYTk4LM5bI2UvcddddTJ48mbFjx/Lcc8/x4osvcsstt1BVVcXmzZu55ZZbAPjLX/7Cddddh6Io3HvvvUh6nnv+t4FHZu3OLw8ZTV7VOfmpLwC49/jJnPfCUh6ct5nHZu3OT/cZxstfNbAxWrgYeOmn05kx3M+kGg9/nbuRpxZs5ZX/mwGAIkn89vVCyvvlh43l0PEhbHIhXX1RbYJjJoW5/qjxJHMaa5pSXP1msSi5cHOcXardSFph0nLNNdewzz77cNhhhzFhwgQefvhhHn/8cR5++GE0TWP27NnU19ejKEqXEmgtoHHEfui9XaLeCrokExszk3H1nxiliGKy2l0BQh3pk5nP543JttUn06I3EY6Rju73YqKeSCQqNnSrswinTCAQIBgMEovFiiar5hV30cpCiDxQcISKxyQSCbxeL6FQyBBAzUIjFMaBQCBAJpNp8bvSf9v6XXsea0aMR+FwGJvNZryXvffemy1bthji7MSJE43PIxqNous6N954I3HfcH59w228uzJS9LyfbIjx9fFVvL86wl4jAlz2ykpjURIK7WkcNpl0XqMhmSPospFTNZyKhKrp+J0KsYyK2y4zeZCXzdEsPofCP348hfNeWEpdmb7dy+qTTKopiDO7Dfay53A/5912PSNHjuTiiy/mjjvuMJx0mUwGWZaNkCXxnYu0eV3Xqa6u5o033igr/gsBW/TAC4fDJJNJ4/vtKIqi9HuBwdyf0uPx4PP5jDL27hTyhNs6EAhQVVVl9CSsRER5sNPpxOPxEA6HSafTJJNJmpqajHAiKIynfSkomkvTdxSUVMnIsozNZuvTlgbZbJZ0Oo3P5yObze7wczSHBgnMJe4ej8cYv0X/a9HXsyP7vqqqLcranU5ni7L2Sg7StLDYGbGcnBZmrHT1CifrqmLJob/t680oy3kHjCD53wf47MO32/03Rx11FLqu8+abb/bglu286EgsOeRKcq5Q3zs5AXQdZy7KPp/eQXZbOnpnKO2TKXpkttYns/RnZ3G/WfRfRDpwR3oNy7JMVVUVuVyuS2XqXRHreuoxpY8VP919ESomvuZkdvPihnliXHpfa/925DF/+tOfmDx5Mps3b+af//wnH3/8MXfccQepVIpcLsd1113Hli1bGDt2LL/97W+RZZl7772XOXPmMHPmTH7605+iKg5u+FzntWWNDPE5OPeAEVz31mqG+kW6usIDczby2PxavrN7DRcfPAodaEzmOO3pL0nmNA4cHeB3x07AoUjc9PYaXl7SwL3HT2bqUB+KBH/+cD1PL9zKlYeP5Zz9R7C8oSAkXvfmat5fHTHek9eh8Ois3YtcnhM/up3n77/dSE2fMWMGF110EXa7nb/+9a988MEH7LPPPpxzzjnstttuLFmyhOuvv554PM5NN93ERRdd1K7JvnAvqqraqYUy0fJB9HXdGVAUxQjcUVW1KHm8O5Bl2SgF35HbulIQQWOyLBtiJxTS1UVYVXd/TjtChNiI1HTRn7e/IgS7+vr6Pt0OSZIIh8Nks9kOOS9LnyMYDGKz2Yx2AUL4FIuTmqa1CDTqDHa73RA9FUUxhFcRQtcfBW8Li/6Cz+fDZrMZ1bEWAxtL5KxwdCS+OOImNJtzxw/uZWQ1wx7vXYPahXRDi44Rq5nM6r3P7uvNaMG4Tx/AX790h48rFTDFj7m8oDUhsz9PGCwqn64Ke26323Ayt/d5HA6H4frrzGt2Fz0h/pX7V6Tk5nI5I1yltcd6vV7sdjvRaNSYcOq6bgRGiL59pYRCIYBeu8gVDs5IJNJpgajSFjNvPmYXHpq3qZCwDkz9+BaUeH2r53lzaa5wrwFMnz6dmTNn8uyzz9Lc3Nwu4UBRFHw+Hw6Hwwhpae8illgw6M/BXa2hKIqxkJLP50kmk90m4onQM7vdXnHJ620h9jtJkkin07jdbqLRKA6HwxAbe8Mh7/F48Hg8fSKu9hQiDLUSFgycTqcRStfR8nlFUQgGg8biR+kYJErczY5PsRgnKoPET0fnOYqi4HQ6cTgchpiaz+cNwdO6prWw6F68Xi8Oh4OmpvKByBYDC6tcvcKR0HFHN5CoGl8Zzj2BruNP1lK1raF6Z9INBxJTpkzh0ksvJZ/Ps3XrVq666iocDgd/+tOfjETma6+9ls2bNzN27FiuuuoqFEXhnnvuYe7cuQwePJjrr78e1T+Y51Zm+fNHG4uef4hPOH5k7p+9kSc+38L0YT7+8r3J5FSNWEblpH8sRtPh5W0tCzx2Bbsisc+d8zh9n2Gctvcw7EqhZcGVr68ynvtHM4Zwx7cnMvTGD1u8r3MPGMGyrXEWrZ7Ign89yFdffYUkSfzhD39gzZo1/OpXv2KXXXbB5XLx7LPP8sEHH+Byufj1r39NKBTiww8/5PHHHyccDnPSSSdx22239ewXYdGjVKKDsL2P7SpiAiT6Au5I/BMu5dLxsqeFxnKP7S1SqRROpxO/34/L5TJKtssh+nD6/X4ikYgxIczlcjQ3NxuhI6WT8EQiQSgU6rV+cqJNR1eEFHs6gpzPVMxi5hWvrTT+r6hZBrtActWgaRr5fN4o7xR9mIWjrjQ1euHChSxatMjo/RiNRnf4nYgSdtHvzuFwtDtxXKRX74yYk8c9Ho8h9HeH2KnrOs3NzUZ/QVmWO90yoDcRJcMej8cIahJCrej3GgqFjD6e3S0q7Syl6aUI4a9SFguEKOjz+Whqamr3Z+xwOPD7/UbblHLzkrZK3G02m9EiATBET3NbpLYQvaWTySSSJBmCp9frxefzoaqq8d76g4O6u9CRyLlCqHYXumRD0vMouTT2dASJ/n/8WPQdVk9OCzOWyNkPcDevIxEaC1IfJWmXQ9dw1K+isbGxaBVUXGiaRU8xKRrIbNmyhbPPPptMJsOFF17I17/+dd5//31++9vfUldXx4EHHshPfvITfv/733PBBRdw3XXX0dDQwN13383cuXM5/fTTeeihh/iHvg//uPh7PLOwjs2x7Rdll359NLe9v5Z3V0V475y9eG5xHV9uSfC1ez8F4Oojx3H8lEE88fkWjnjgMwBO23soo0MuAB7/rJaHP9kMwH/P3pMRAScbo5lCuu60QUagRSnf2a2Gb/5vA4HwODZs2MBvfvMbQzjyer3cf//9pNNpnE4nf/nLX/jPf/7DD3/4Q9555x1eeOEF7r77brxeLxs2bCAcDuPz+SrmwrqvqDTxry+Ews6Kdr0tEJqFzY64+VwuF36/v98GUnSFTCbTIvSktUUxc+CQeXKbz+eJRqNlhU4RAOH1entF5LTZbF0WTyR03LGNJELjKnIxU2L78SXLMk6nE1mWDceTmFi43e4WwW5CQAsEAgQCgXY7BcXkX4TwuFwuYrHYdldvyURZQSPtdJOPdq6ktb8g9v1SsTORSHR5f4/H46iqagh3nS0P7m2SySTZbJaqqioj/DCZTBKJRHA6nUXhRKVCfGeQZRmfz4fT6SSbzRKNRncqV55wM/ZV6FA5YrEY4XAYj8fTrj6hotxefD8doXTOIstyUaCRSFQ3l7gL4bM1dF0nnU4bY5/D4TDK2oULWIitO1tZu45EvGYSsfBEUsHRpAIjyy7oyfkM7ugG3M3r8Dcux1e/zBI9LTqE1ZPTwowlcvYD/I3LqR93WF9vRjGygq9x+Q7TDZ1Op3ExYC79yA+wEndzXyPh3Mpms9TV1Rn3ic9j0KBBrFu3Dtg+yR81ahRLli4jvc/3WFibYOaYIP9cXGc8574jA1zy8gp0HT7dGGXqEC+fbtw+QXHbZJbWFzszTpg2mF+/vKLw+tuCLBRZIpLK05gqXKydNGMIzy2q4xdfG9XiPU0b6mVVYwokibhvGMOGD+fWW29l1apV3H777UUTWbvdzsqVK8nn80yfPp077rgDgDlz5jBjxgzeeust5s+fz8yZM3njjTc69yGb6ClBrzeer6t0RdhrSyhsz/N09bGVTmsOBJ+eMsrbdoQIPEilUgNO4BSI1ORgMGiEnpT77IRAJoROcwm62dEpgnwEiUTCEDx62tlns9naJWyLvsPlgtRkWSaSrSOpj0GvsMVMV9MaVFU1HMrmPsmaphn9T4XIKVwU4ngQgXCpVOEY6YhTUNd1QxT1+vxoY/ai3j2SZs8wUv4RA3qibBY7vV4vwWDQCO7qijiVSqVQVZVAIICiKBWbvF6K2EZz6roIbGpsbDRK24UA2h5ncDnMpek76yKVaIlQSRVZohWA1+slk8m0ea71+Xy43W6SyWS3BCeJkDVzK5rSQCNZljtU4m52j5rL2gOBgFEZIB7TXwX0vN1L44j9aBh9EDl3FWgqSHKrC3mazUmiajyJ0Fjqxx2GPdVE9boPCW+chy3XdwFYFv0HTdOK+r9bDGwskbMf4Ktfhj3VVFFhM/Z0BF/9sjK/ajvdUKyuiosB8wVBJV1Q9RTDhg3jwAMP5G9/+5txn81m49xzz+X6668HKLLax+NxAoEAq1atYq+Dvs7/dBeHjg+xrql44m5XZMR43pxWCXvsABw9KcxNx+xCNq/x+/fWGo8PumwM8TlYUrd9onnpoWM4e//hvLm8kVROQ5bgxOmDOf6xRWVFzl0HbRM5AU1x8u2TTiddt54zzjiDk046iSeffBKAG264gb322ou//OUveDweqqqq0HUdr9dLLpdj0KBB+P1+GhsbmTp1KrNnzwa6JiJ2B50V9LoqFHbltS26l3Y7ENQs/sRm7OFVbQorovddPp8f8I5lVVVpamoiGAy2WcqsaVqR0Fnq2iwndObzedLpNB6Pp1dETvEapQKm+bZ5XBfioJg8q6qKY/MX6EP279Ft7TCygrtuCZFIBL/fj8PhALb3lTMvaopJhSjjFOORKPk0C6Oit2QqlTKE0NbGr7zdy1bTRFnSVHRromwgetMKsaU7xE6RvC6Ozbbc1pWCef9KJBJGGbsIsxJip3AGu93uDvXPdDgc+Hw+ZFkmlUr1aep4TyP64VYa5nYn5XrumXvL9qQALcY58+KWzWYz5jrmEvfSVl7lBEtzWbssy4bLU5S15/N5Y17VH8radSTqxh7KlonHFsZqto3VcjsW8CTJqFrMuULUTjqOLROPZcjyVxm05r2dcsHKovsQ1xGWyGkBlsjZL5DQqV73EbWTvolxsuhTdKrXfdjuk40QM8XKuaIoRW7PciXu7el309/wer3ceOONXH311UWr0FdddRXPPPOM4d40TyZ8Ph/RaJSHHnqIy6+5gW8O350NkQy18eKLt5yqIUmg6xB0KTQmCxdCry9r5PVljVxyyGjO3m8Et24TOr+7ew3/+ao4NfPW99Zy+/tref7Uaew/KsDkQR6eXbiV9p4nsoqTYDDI3LlzmTVrFn6/H13X+f3vf4/X6+Xuu+/mvffeI5lMEgqFyGaz+P1+tmzZ0kIEMH8OvSkQWidFC+iEA0Fx0OwfDb6RbQorIv25K0nqOxO6rhsCWiAQMMSJUkQ/wmAwiN/vLyqjFcn0gUCg6HeJRMIob+yu/oKSJBUJl0Lcc7vd+Hw+Q2gRJdzCxSjcOOKn3DjjTn9ZkYuZQ1Ib8YTDaJpmJEYL8cjcE9Lcx85utxvOT9F3TlRviEm8cDSLz0x8VkL0zOVVakcd3GKirFsT5bIIwd9utxc5OzsbvJPP541FiLbc1pWKpmlGb06Px4Pf7zfEzlQqhdfrJRAIGEFVrb230tL05ubmne7a1IzNZkOW5Yp1qMZiMaqqqlqM64qiEAgEkGWZ5ubmXhcDxdhlXvAyj4fmqjZzeXvpdmqa1q/L2tPewayfdhKpwKiun8ckCZDQJZnaSd+ieegMRi16Eldia7dsq8XOhzgeZFmu+IU5i57HEjn7CeGNc9ky8ZhtF/t9i6RrhDfO7fTfi4meOIm3t8S9P6xgtoaiKNxyyy3cf//9rF273VF59tlns3HjxqIS7fr6ekaOHEljY2NRieZF19zKqpkX8/TJU3l3ZaTo+T/ZEOPr46t4f3WEvUYEuOyVlTgUiey2MvRoOo/Dtn3fOWHaYH79ygrjtnispkMiq5HMqew22Muew/2cvOdQJtZ4uOPbE7n4xeXG3yytT3Lo+BAAHrtMLJEm29TAIYccwooVK6irq8NutxuTr2g0SkNDA/PmzWPXXXdl6dKlzJgxg+uvv55IJEIoFGLJkiWWAGTRZ/SkA8HrcWO32/uFK6q3icViaJpm9AIs53LN5XLEYjECgYDhfBGIvmuBQKDo+VKpFG632yiVbg9CxGytrFwghExRvSDOa0Lc7CiVuJg5un4+bpezhfjc3Nxc1BNSfB/mx7Q1yRdOUHNSuHBCuVwu0p5BLBn7LWKeYdZEuYOIVhBC7BQLislkssPXUCKwRfTPjcViFSt+mRcYzKiqSiwWM8ROIWyK/dXn8xX16zSPzebS9M4ke/dHHA6HcYxWImKs8Xg8hgvebrcTCATQNI2mpqaKOL+WlrgDLcwd5qo2cysv8/abK+NsNpshelZiWXtkyHTWTz8ZHan7F+okiZR/BMtn/opRC58gtGVh9z6/xU6BOHasvpwWANIee+xRWctAFq2ydezXqZ30rb51eeg6w5a9xKA17/boy5gnR3a7vUW/G/FvJVzMtIfjjjuOSy65hBUrCsLis88+y4IFC3jppZf4/PPPgUIS7V133cX48eO58sorURSFe++9lzlz5jBz5kx+csbZJIKj+cN7a3ltWSNDfA7OPWAE1721mqF+ka6u8MCcjTw2v5bv7F7DxQePQgcakzlOe/pLkjmNgFPhzbP2ZP+7PzG27+ojx3Ho+BA2uZCufs2bq4u2f84F+xQ9XvDq/83g2IcWMGOYjye/NYhsMk4sFuP3v/89sViM66+/Hq/Xi81m48knn+Sdd97B6XRy7bXXEgqFeP/993n44YcB+N3vfsfNN9/cb8IOLHYuutWBYEbX8cQ2MH39q9C4vl+kFvcVLpcLn8/XZliEcE3GYrEWJZWilDGdThOPx5EkiXA4bAgYgtZKys39JoEi0bL0R/SXVBSlqFdoV8jbvXz19avR5b5ff5a0PHvPvY1ctKFNgVhRFKPXoRCW0+l02b9p7bwOkE6nyeVy1IV3Y+3UkwoT5fYsLnQUTUVCHzATZYfDgcfjwW63k81mSSQSnRKw/H4/LpfLcEdWGg6Hg2AwSENDQ5vXhaKHqcPhMJyusizj9XqNUvRcLmeUposy4oFCVVUV+Xy+4q/DRNujdDqNz+czHP2V5mxsi9KqNrP7fUdVbeaydofDgSRJfVrW3jh8XzZM/WHhRk+acfTCsT1y8dOEN7Wck1gMbCRJoqamZqftl2zRMSyRsx+hI7HigItI+Uf0zMX/jtBU3NENTJhzV6+Xe3XlYmBnIeuqYsmhv+3rzSjivANGsLQuydsrm5jy0e9wZ6PIsmwk74r/m+8rXWHTdZ3q6mpOOOEE7r77bjRNM36EW8r8Y2HR3RQ5EHpgbJV0FXQYtfDxASGsdAXhyhEl6uWOedFTr1wfTyF0ZrNZMpkMLpcLu91OPp83BE2BrutFwmWpoLkjqqqqDOGou6iUxczRq1+netXb7T6nKoqC2+3G5XKh6zqpVGqHDlpFUQwRTpIkNldP46sxxxV+2ZPvfwBOlEWPP5vN1mmxU7QpSKVSFddTWIic9fX17RK6hNPVLP46nU7cbjeSJBmi2UC65pBlmerq6n4hENhsNkKhEJIkdVvAUF8jy3LRIpDNZmt3cKu5rF2U6vZWWXtkyHTWzfhJ4UZvnLd0HdAZveAx63rKogU1NTVGYKHFwMYSOfsZae9gls/81bbG+71Yuq5rSLrGxI9ux5Ws2/Hje5jSEvfWLgb6c4l7KToSXxxxU9ngk75GzmeY8t8r2lVkaRY/WxNCzbdLKSd8lt5nvm1h0RaWA6HyUBSFYDAI0GoPvGAwiN1uL+r1XM6NKdI2Ra8zs5jZlfGhpxwDlbCY6UtsZt9ljyNLhYVEIRi353wqUtZFr20hdrb1WcuyTHbsAXw54fuFO6yJco9hFjszmQzJZLJDYqdYROhO55yORM4VQrW70CUbkp5HyaWxpyPtXlDvqMhp/jvxeZh76drtdlRVJZFIDIgyddjupG9oaNu93deYA4YAGhsbd9prPfMcp7SqrbXgVlHW7nQ6jf1alLVnMplu/aysOalFpVFdXU0qlRpQDnyL8vR9TZRFh3AltjJq4ROsm3Fq4SK91yYDMGrhExVzMimX4m6+GGhvv5v+hISOO7qBRNX4ygimEOiF7WrvFpndU+1hR2KoaJQvbpfSliu0nDhqMXCIDJnOhqmzCjd6+piSZNB1NkydhaxmB5Sw0lFUVaW5udnoBShERHOJuRAy3W63IUzkcjlDyBRlqeK2cHd2V685m61w+dTdveskdEYterIwcdS13p84ojP888dpSNYVlUO63e6i8242my17LhXp1slk0hA73W436XSaZDJZ9m+S7hqW7/Ldwo3eOrdJEuiwfvrJBD9pxBHfUtYhtbMhvjsRZFJVVUUmkyGRSLTrnCxEEnFsdqbHsI5EvGYSsfBEUsHRpAIjyy7eyvkM7ugG3M3r8Dcux1e/rFXRs7WenDtCVJfoum4EY4k+4iKtvitl/v0JUcJfyceALMsEg0EjYMjv9+P3+2lubu7rTesRSs0aOwpuFfOd0rR2p9PZIq1dBMJ1Fh2J9dO2tRbp7bwIqSD2rp92Up9UF1pULmJh28LCcnL2Uyz30Y7ZGUvcN036FvVjDukbh09raCqD1r7PsGUv9fWWALTpCi29r1zpfFuu0NL7KnkyYNE2lgOh72kt3Ke1kB8xXpudmCKxvqmpqcXxKPp3ilJUESDSHXg8HtxuNw0NDd3yfKUUSgBPhZ4IcSjHDpyNorRcuINESa8QzVqbLEuShMvlMkrShXtQnHf72rkqaSr+5Bb2WfYYEtsX4MxJ7zuzuCXED0VRDCG6PddEZrd1NBpt12eUt3tpHLEfDaMPIueuAk0tjL1t7d+6XrgOlRXsqSaq131IeOM8bLni8mSXy4Xf76eurn1jqqIo+Hw+HA4HmUyGeDyOpmnGvirLstEn1uPxoCiKIQb314XyHVFTU9MiZKySMAcMRaNRVFU1HLz9ocS+J2itqk24N80GD8AIgXM4HEVl7ZlMpsOO5UpprdIbOREW/YdQKGQEzlkMbCyRsx/T033kdrYG/W31uzGvgFZyiXusZjKr9z67rzejBeM+fQB//dK+3owOI1wcOyqZF7fLiaLtKZm3Sucri74WVvqyv3FvIklSm0nlZvdVuYAfcZ/owVmu95osy4RCISMJuhQhdKbTaVwuF5FIpFvGeCGu9qSDqLCY2QtO4w4uZkqSVOTyFJNlMVFuzQ0mBCQhGCWTSTaNPLgiJsrDl7/MiE0foyiKkfQuFkZF9YFZ9OzPVSHlMIt7pUJ0a0iSRDAYxGazle2PK9CRqBt7KFsmHltYVOqscL9NiJd0jSHLX2XQmveM8VOUWtfX1+9wm8UChaqqxOPxsuOB2+02hHnRbkHc3hnDiIRY2NjYWJEL/221SfD7/TgcDhobG62FZyia55hL3EsNHrIsG4KnuaxdjONtjW+VFpK327vXYcvtXMekRecIBAIArYZXWgwcLJGzn9OTicDu5nWMWvTkTu02Kl0BLU1xFz+VcuGkI7HkkCvJuUKVUbKu69jTEXZ9/6adWqwRdHc/0R2V0Fv0DJYDofsoFTDNt83HgNl9WU7M3BGiBUm55HVFUQiFQuTz+bKiowhMEa/VHWnoIrW9J8UOt9tNYsTefDHuO4XRtUIXM+12uyF4tqcHnCiV1px+Ppz2s4qdKAuR3ix6iusEoKg3nln8rJTrhc5QKna2x7kYCARwOBxlXYA9eo0aXV+4Rk1sNcaHtkROp9OJz+dDkqR2ORYlSWrRX1bcp2kayWRypwm38Pl82O12mpqa+npTWuD1evF4PK0GXkmSRDgcJpvNWu6tMohxyzzXAYwxS1RICLe+3W430tqF4Fnq1N469jBqJ32z98vUy6FrDF32MoP7+bWURffg9/tRFKVbrvMs+jeWyLkToCNRP/ZQartxlXzo8lepMa2SDxT6Q4m7dXHRf2iPENqZfqKtiaMWO8ZyIHQMsW+2VlbemhuzVMzsjv3T4XAQCAQMMdP8nHa7nWAwSDqdLjsRFkIn0OXSRpFC3Nzc3GOhJGJin0wmacDLxuknk/CPqPjFTLM7yDxZFmXtZtdcw4Qj2Tj+6Io5lw1f8So1q97e4UPFvl8qfopjQQQg9ueS99Ky7db6qQrM+6twW/dmtdGw6HI8Hk/Z9hGKouD3+7Hb7aTT6Q6XnJvdn7quk0qlUBQFl8tFPp9v1Q3anwiHw4aoXSlIkmS4NHckSjudTgKBQI+OyTsL5hJ3IX6aS9zFWCX6ecqybATQZbNZMtmcZbawqFi8Xi8Oh6MiF2wsepe+n+VZdBkJnUFr3qVq4zwaR+xLw+iDO9fvKB2hZt2HVG2cW9GT7p5ETMjF6rwsy0UXAk6ns6jE3Vzm3luEN85ly8RjtgnafYuka4Q3zu3rzahYOhOy1FbJvHARdbafaGl5/UCkccR+FXHsAOiSTOOI/fp8kcDcB7OcmCkw78/ZbLaFoNnTZLNZIpEIwWCQqqqqouT1XC5HLBYzeraVOiyTyaQhVvh8vi6JnD0VOiQQzrhYLFYosyfBpHl3E5t8FCuHH1LRi5maphmJ6mIy7XA4DNHM6AGXzbF1+AGF91ERSDSM+RqTmheQ30H7GjGGlv5OHDNC/HQ6nbjdbkNAKNfvsxJLgwHS6TTpdNoo23a5XG2KnSK4yOfzoSgKa3yTe75vvKyg6xrrZpyKfdkLjI4Vt8yRJAmv14vL5UJV1U63qtB13RDZPB6PsR8nEgkcDocRjtbe8KZKQwj1lSQOmgOG2mqFIBCOQ5/PV7Y/s8V2ygW3mp2eLpfLOO+b5zcigK7eP64wx6wUJImcu4p4zaR+2TbLonsRAXIWFpbIuRNhyyUYvOZdBq15r0PJlZ7mdfh2kFw5UBF9xswTYrPTUzgderPE3ZZLMGT5qxVRbjt0+asDVhDvCTpSpt5d/UTbkzq/s5TO60g0jD6IihJWRh9c1Fuup2jNiWl2YwJF7kuzkNldbsyuks/naWpqIhgMEgqFiEajxiRMBIj4fD40TWtRSppIJIxyU7/f3+nSRrvd3u4y+47QVo9DNZ9j9JY5DN76GZurp7Np6H5knMGKXsw0T6bj8Tg2m80oa8+P3K3iJsoZR4Ba1wgG5dca5c/mknRxbm/tezcfN2ZKHZ9ut7tFybtZ/KykknchWLdH7Eyn0wWRe/S+bBh/fOHOnr5GkWTQdVZO+j7Olc9ja/gYKDhRhXO7u8J0NE0jHo+TTCYN56pYUHE6nVRVVRlO0Ur5/tqDw+EoK9r3FTabjWAwiK7rRCKRdgvHsViMcDiM1+st6+a3aB0x7ojjRBg8zFVtwuDR4BuNpKnoFRaAGg9PtEROCytd3cLAEjl3QiR0/PVLjcFeRyLnCqHaXOiyDUnLo+TT2NMRS9TsBKXuDnOJu+g1BrSYGHXnCv+gNe/RPHRGnwen1Kx5r/df2wIodm62h7b6iYrbwikqBNTS1+vv/UTjNZMqTljpLgeC6B/YmpgpKHVjlpaV9wdEX81AIEAwGCQWixkLUaKUVAidpYJTPB43xmrRM7Kj2Gy2bndxmp1LkUikxfM7HI6CMzKfYlz9J9SseY963+jti5nBkWhK7y1m+nw+7r33XsaPH89PfvITVq5cafxu2LBh/Otf/+LHP/4xK1eu5KSTTuKUU07hyy+/5Ne//jXJZJLm0SN57uQpDPI7efHLem5/f13R819z5DgOGR8CYK/hfg69fz4eu8xNx+wCwHC/k1eW1vOrl1bwoxlDuPCgkaTzGhf8aylfbS2ItzcdPZ59RwVQZIlvPrSATL54XHrgB7vy65dXoOk6j83aHb9T4csvHfz16gsB+PDDD1m6dCmSJHH33Xezfv16pkyZwplnnomqqtx6660sXbqUk08+mXnz5vHll1+W/azEtYAZ4cw3i5+iUgSKS97NAmhfkUqlDGen2+3G5XKRSqVIJpNFgl7UHmL5uO8UbvTWRFOSQNf4avx32a1uFTVyutOl6e1B0zRisViR2JnP50mn0zidTpxOJ8lksmJTyktxOBwV4+IUAUPlWpLsCOGu9Xq9pNPpftciolJwuVxce+21XHHFFVx++eWMHj2aJUuW8Oc//xmbzcYzV59JWnJgkyXOeX4JX21NMnmQh/u+vys2WeKaN1bx9spCqfBFB43kW7vVoMgSZzz7FWua0lx40EhOnDaYxmSOU5/+klhGJeiycc/3JjPY72BFfZLzXljKufuP4MQZgwHYfbCXc55fwn++LO65e9PR43n4k81E03n+dfMF2CLfQ1VVrrzySurr6zniiCM4/fTT0TSNl19+maeffhqXy8WNN95IOBzmvffe4+9//ztDhgzhlFNO4Q9/+EOvf94W3Yuu68Ycpj8tNll0P5bIOQCQ0HGkrd4UPUVrJe6izL1cibu5701nkNAZtehJls/8Fbqu9W5PM10zXt8SyfsPHS2d35FL1BzC0V/6icbCEwvOt37qQCjnwiznxjQLl6Vl5ZUqQHcUXddpbm7G5/MRCARIJBJGiXo8HkeWZQKBQFnBsLm5mXA4jN/vB+iw0Gmz2bo1cEg4lzRNo6mpqex3JIQUIVhnM2kCmWUMSa7H0+hB03UiqoNEHnTZht/jxKZmSdet75FxOp1Oc9FFF3HxxRe3+N1pp53G559/btx+7bXX+OCDD/j5z39u3HfCsUfw6tJGHvxkM6+cPoN/fF7Lpuh2oeW6t1YD4LbLzP7ZPizcXHBmHfHAZwA8dOJu/PuLemQJfnXIaA78yycM9tm5+7uT+f5ji/jB1EHUxrMc9bft22FmTJULVdNpTuf55ddG8Z8v63lo3iYeOG44e+21F/Pnz2ft2rWcccYZxt9IksSpp57KJZdcgt/v55JLLuG3v/0t77//Pueeey7XX399uys5RP+7HZW8OxyOouO7tNxd/Nsb6LpuiHdC7HS73YbYqemwftpJhR6cve2kkWRAZ8O0k6n58mGampp6XORSVZVoNIrNZsPr9eJ2uw2nr7idSCS61BqjpxEtJSpBkBV9k9PpdKdd9qlUyhBKrZ58nWO//fZj7ty5HHLIIdTV1XHddddx1VVXseuuu7Jg4SIOe+AzMpKDQ8eH+MXXRnP2P5dw09HjOeu5r9gSz/Ly6TN4e2UTe43wMzrkKhqDqz12vr1bDYfcN58f7zGE8w8cye/fXcu13xjHbe+v5fNN2x24983ZyH1zNgKw4OL9eHN5Y9F2euwykwd5WNGQQpbga/fNZ/e3ruA73/423/ve9/jb3/7G6aefzjnnnEMymeSpp57imWee4fjjj+ejjz7ihRde4O677+aVV15hy5Yt1NTU4PP5LBdwP0dcP1kip4UlclpYdDOlJe7CsSGET6/Xawy+XSlxdyW2MmrhE6ybcWqhHLE3JhXbtm/Uwie6LajCojLpbD/R1sTRjvQTLRVCd911V375y1+Sy+XYunUrV111FQ6Hgz/96U+Gq+7aa69l8+bNjB07lquuugpFUbjnnnuYO3cu3/72tznzzDNZm7KxPilz6tPFjqsTpg3i+qPGE8uo7H/3JwBMrHHz1I+nsusgD4Nv+JBEtvA5TBni5fbjJmBXZJ5buJX75mzkgpkj+cXXRvHphhg/fGIxAIN9dv5x0lQkCeIZlVlPLCZd4iS76ejxPDxvE3Zld96+75esXLkSSZK48soricViHH744cyaNYtsNsttt91GQ0MD3/ve9/j+97/P0qVLufrqq8nlchx00EHk83nefPPNfuPG7C7i8bghKiiKYkyOo9EooVCIYDDYouRR9I0UZeuirLo9CJG5uwSUtsKUBOLc0dzcTDAYJJvNGj0uJUkqctS5t/2NWyukJWd6aCFKtA0oZfjw4ei6Tm1trXFfU1MTLpfLuK0jsc+u47j01VUAvLmikQNHB/nn4pbnlON2reaVJcVhMnZFYt+RAc547isGee1simbIazqbolkmDypUUnx79xrqEjn+e/aevLuyiRv+u6boOb69Ww1vryhs/y7Vbh6ctxkkiblb8uy3336sXr2akSNH8uCDD7Jy5Upuv/12I0SptraW2tpaXC4XTU1NxONxqqurjZJu6HxYYVsl72bx0+VyGYJ3acm7+LenFjTKiZ0ul4uV4b26P0W9I9slKUS9w1genM6gpnd77XXFsSuu74RLHAr9dXO5HPF4vCKdhU6ns0PjX08h+hDH4/EuC66xWIyqqiojCMtiO5dffjkTJ05EVVUuu+wyTjrpJPbbbz+y2Sx33XUXixYtYubMmTzyyCP88Ic/5MMPPwTg448/ZsaMGXyybD0ZyQGA36mwuLYgCA4LOFnRUPjemlJ5qj12vrN7DW67zJtn7cmXW+L84sXl7DvSz/urIwC8tqyBR07cHYA9hvvx2BUm1Li588P1/Nvk2NxnpJ8vtyRI5YrHsyMmhJm9LgqApgOKk5yrCq/Xa1QWrFmzBq/Xi6qqZDIZdF1nxowZ3HHHHQDMmTOHGTNm8NZbbzF//nxmzpzJG2+80f0fvEWvIa6hZFneaRb1LTqHJXJaWPQw5RwbQvQUTk9zibs5zGhHE6PQloVoi58pNPjX9Z51dOqFk8XIxU8T2rKw517Hol/SUZfgjnqJisRPWZZJpVL85je/IZvN8n//9398+9vf5uOPP+a2226jrq6OvffemzPOOIM//vGP/PznP+fmm2+mvr6eO+64g3nz5gHwxD+e5Or66WX7E7+9sol/3zGXD8/b27hvQ3OGw+6fzwunTS967M3H7MIPn1hMLLP92Hx6wRZeWdLALcfuYtz34z2G8syCLTwwdxNXHj6W46cM4skFW4zfGy6ExjTjAsNZtGgRN954o/F7r9fLj370Iy688EImTZrErFmzuPnmm3n++ed59dVX+fnPf040WrjAf/XVV/nDH/7Aa6+91u7Pf2cimUyiqip+v98IqhBOTyF0loZRJJNJXC4XmqYRCATKhlsYrV7sLnTJhqTnccsaOlq3CBYulwufz0c2mzW+y3J4PJ6iXpBC9GwrCEZVVZzOlvt6T3P66afz0EMPce6557b6mJwrRJXHSXTbMRRN5wl77GUfe8K0wdz6XnEp+5ETwry9shFdh7pEjpFBJwGnwsigiwnVbmyyxBCfg0W1CY544DOePGkK+48KMGf99s9410Ee/re2GYAvtyY5bJcq5m+McfikwUQXFITv008/na1bt/LjH/+YWbNm8dprrxWlT4vzcyqVor6+HofDQW1tbdGipqjkMF8HiPN7RxY1hXvT7AoUi0elzk/hrBcl76Xuz+5yt5jFTnughvVjj+z7tGVJonbisX0SoJnL5YhEIjgcDrxeLzabjWw2iyzLVFVVGT2DK2ni7XA4+rQHrHDcl+tD3FlUVSWZTOLxeMhkMgNu4a81Dj30UHRdN9zpkiRxwAEHcPrpp6OqqrH4PHToUGprawkEAoarMR6PEwwGUe0uhvgcPHvKVEaHXHzv0cJcQDYd983pPGGPjSE+B3lN5xt//Yxbjt2FE6cNRpYkoun8tsepVG0b9/cfFeDXLy/nq61J3j1nL15b1mi0Fzlh2mCeXbS1xfuZPMjD6sbtgviMYT4efvBegh4n559/PgBvvPEGjz/+OKqq8re//Q2gxfsKBAIAbNy4kenTp2PRvzE7OS0GNpbIaWHRB5T21ypt8u1yudpd4h7eNA9ZzbB++smFC9WeKMXV1EKJ+sInLIHTolvoiCja0NBgiJ8i+CEWixlBMuIC3e12M2TIEKLRKA6Hg3Q6zfjx4/H7/Zww60ccrPq5938beHph8QVzY7LlcZXKaZT6ScaFXdhlicd/NAWHInHxi8tZWpekLpHD4yg+7pZsTTBtmA+AKreN+mRxWWqRC0Gxs/uUXbn11luZP38+d999N2PHjmX58uU0NTUxZ84czj//fHK5HA0NDQwbNqzouUS7jHA4TGNjcUnXQEFMZkUgUXNzM5qmFQmdkUjEeLwQaLxeL7lcjkAgQHM0RmNg7I5D+9Qs7ub1uJvX4e9kn0vRyy+ZTBYJZ6WIdHLzZAwK7si2Ju+apvV6wujIkSMB2Lx5c5uPU+0uIukcAadCczpPwGVjXVO6xePcdpnJg7zM31hcunrCtME88mnhNXQdrnhtJS+cNp11TWnmrI+S13QiqTzvbOsL987KJqYM8RaJnGYenLuJu783idfP2IO1kTRbGpuJRCKk02m8Xi/z5s3jxBNP5NlnnzXCbKDg6i09J4s+sK0lF5tDhzq6qFlKayXvor+yEEDN1xRAC8dnV0vedV1nQ9VU9N5sm9MGuiTTOGI/Bq95t09eX3z/TqcTr9eLLMtks1nsdjvhcLhsL9O+wuFwtDn+9CQ2m80Y03Y0nnUUEQbl9/uLxv2dCXPv9NL/l7tv4sSJLFq0yDBVSJLEI488wo033kg2m+Whhx7C5/NRW1tLMBgkl8sxdOhQ6urqGDp0aOE8GapmeTzLIffNZ9+Rfm4+ehe++fACNNO+HHTZaEzmiaTyzNtQGHPfXtHEQWODzF4XZZca97bHKTRtuy5aH0nzyYbCOL+sLsmIgJNV2wTMYyZXc+2bq3f4eSzYHOeH593Id/fflf/7v//jpptu4qKLLuLEE08kmUxy//3388YbbxCLxYyydJ/Pt8PzlUX/QoyrlshpYYmcFhYVQFdL3ENbFuL6uJb1007q/nIxXccd3cCoRU9aJeoWfYIonR88eDD77bcf999/vyEu2Gw2Tj31VK6//noaGhpQVZX6+npkWaapqQlJknjjjTf4x1tzWLX/hbx51p68tzpCbazjjpEhPgfThvmY8ofZjAq5+MNxE/jWI+VF/7kbotx0zC6csudQmlJ5Ln1lRdHvzS6EzdEM3/jRGVC/hquvvprDDz+c+vr6osnnjgSrTZs2MX78+AErckJBNBKBRFVVVTQ3NxulpKFQyHBsCkS5bVZxsb5mTzZP24+MY8eJ5ZriIFE1nkRoLPXjDsOeaqJ63YeEN87DltuxYCBKM2OxWIsE+FK8Xi+apuHz+YyyUofDsUOBRCwg9GbJ1qRJkxg/fjx33303EydOZNSoUZx77rktHbKSjY/XNnPkxDAPf7KZIyeEOfufS1o83zcnV/Pq0uJSdZsssc9IP2f+8yvjvteXNfL6skYm1rj5xcGjAfhobTN7DPMxf2OMGcN9PFuysLG0Psn4sJtPN8ZI5zXOfK7w+g/8YFfe+d98XC6XEUx1zDHHsHXrVlwuFzabDZ/Ph9frLdqXqqqq2LJlC+XYUXJx6aKm+RzfGcoJrUCR41NRlKIqEjHGlvb7bM++oyPRMPogoFImlRINow9m0Jr3+rRvuLimM7eWyOfzRnl/X4cTiWqJvihVb0+bjq4Si8UIhUJG39juoCOiYrn7OvM3rd3XEXRdZ8uWLeyzzz7Mnj3buP+LL75g/vz5HH744XznO98hHo8zZ84cNE1j4cKF7Lnnnnz66afstddevPTSS6jZdCHnSy84MZO5gjBdG8syPuxmazxLldtGQzLHx2ub2Wekn+cX1zFjuI/VjWnmbYhy8cGjADh6YjUfbXPTL6yNs0u1m9WNKcZXu9kcK8yF9h5RKFUvbfUDsKw+yaSawvhlVyRyqo6k5YnH48Z5NZfLkUgkjDHN6XSyYMEC9t9/f/7973+z//77c/311wMwYsQIVq/esZhqUdmI9le9vchrUXlYIqeFRQXSVol7uRT3XC6HM9eMe+7dbB39NWonHrvNVdHJAABdB3QkXWPo8lep6ePJgoWF1+vlxhtv5Oqrry5yT1111VU888wzrFtXKGkVPT1VVcXj8VBfX08kEiEZcBPPqry3qondBns7JXJGUnnmb4wRzah8sSVBjdfR6mMvOWQMd360nr9/WssvvzaKs/cbYTTRLyWr6tjcXgLhMHPnzmXGjBm8/fbbhMNhqqqqjLTIUCiErusEAgHsdnuRs8/hcODxeIwwHfOksT3/78zfdPXvewJVVYlEIoajU5RARqNRgsFgUbCAjsSK8F5sHPeN7eMltM8NL0kgFR6Xc4WonXQcWyYey5Dlr7YqrkiSRDAYbFdppizL+HyFvpqqqhqpwYlEwnB3tiWQ9obIeddddzF58mTGjh3Lc889x4svvsjbb78NwHXXXcejjz5KNpvl6KOPZtasWYwePZr77ruPn156PQ/O28xjs3bnp/sM4+WvGtgYzTDE5+DcA0YYoUOFUvW1Ra955IQq3lnZhHk3+uO3JjJtmI+mZI7zXigEeD00bxMPn7g7p+w1lGV1Sd5dFSl6nhe/rOeyr4/h2UVbmTHMxx+/PRFNhyfm19IcjTBt1125/PLLSSaTRKNRrrvuOiRJ4vHHH+eee+5BVVV+97vfARAMBqmrq2u3E21Hi5putxuv12ssappFz658l6LfZ0dL3suJn+bjOF4ziZy7qtPb1e1IEjl3FfGaSe0KdOtp0um0kUwvrttEH2GXy0UikegzobEjfbe7i84EDHVWNMzn80a/ZnEe7czzdNYVJo4TIbyU+7/4VxzbO3rcju4r93uAF198kalTp3L77beTz+e57LLLuPrqq41ws5tvvplzzjmHxx9/nFwuxxtvvMEBBxzAnXfeydKlS5k3bx7VYybz9tl7oWo6OnDRvwvH129fX8lDJ+6GIktc92ah1/LLS+r51m41/PfsPWlM5jj5yS/IqjqvLG3g/XP3IpLKc8pTXwBw5WuruP/7u+K2yzw4d5PRf/OEaYNbLFAJ/ruiidP2LlS17DHMx63HTcR14k3k0kmuvfZaAB5//HEefvhhNE1j9uzZ1NfX88ILL3DTTTfx3e9+lw8++ICtWwvPv9dee3HzzTd37Au2qEjMx7rFwMUSOS0s+gntdYOEY4sY/elXbKqeTu3wA8i5Qjt0JgEFYVPXQFawpyPUrPuwT/paWViUoigKt9xyC/fffz9r124XPc4++2w2btxY1Ci+vr6ekSNH0tjYaJQoe71eUnoeWYL9RgW4d3Z5sXFHLG9IUe2xGz3/mtOt92WUgPpEYZGiIZljiK+47NnsQvA5FHKpJOl0mt133521a9eyatUqRo0aha7r7LrrrqxYsYJ8Pm84vkrFwmHDhrF+/foWwU6lF3qt/a6tv+lJekpYVVUVWZaNsjshFLnd7oLIaAuyfNL3SfhGdN35LkmAhC7J1E76FtGhezDmi6cM57twFYieocJhWv6pJDweD26323gfjY2NRoCJOA90ROTsKS688MJWf3fNNdcY/3/99dd5/fXXjduyq4pEVuX7jy0q+pst8awhcAKc9OQXLZ73tWWNvLas2K38y5eWt3hcMqcx6x+LW92+NU1pbLJE0GVjwea4kdoOsE8+w5o19fzsZz8zPj+brXC5vGjRIn75y18CBZeQ1+vlhBNO4LnnnsNms3Wq52W5RU1RZm4WPmF7qXlbLWzaw5QpU7j00kvJ5/NGmJssy/z+97833seNN95IXV0ds2bNYtasWSxbtszoG7z77rvz85//nEAgwPQL7ypcY5gWB8qFuQHMmj6YM/cfgSzBla+tZPa6KEsuOYCN0YLw+ru31/DWiiYmD/Jw3/d3xSZLXPPGKt5e2cRpew/lN4eNZWM0w6bmTIsAOSg4cX/98goy2Rz33HIt1RRcXFdccQWNjY0ceeSRnHLKKaTTaa6++mq2bt2K3W7n0ksvZfTo0SSTSX7xi18wdepU9tprLx599NFOfb7lSKVShtjpdruN/USEiQnHWW9hDkhqje52LNrtdqPNgyzLhEKhdj1nRzGLfVDof2w+NlsTF7tLVKyEVgTlEAszgp/97GdFt//73/8a45CqqkXjOED92mUc8Zf/tWjj8tXWJF+/f37RfZoO5zzf0qH/5w/X8+cP1xfdt6w+yZF//azFY3/z2spW30siq7K0LsmEajfzNsQ44i//Y8p/ryjyk7/88su8/PLLRX+XSqWMMVwwZMgQGhoa2i26W1Q2fdGux6LykPbYY4/KHIktLCw6hLiANDtCkCQa/OOo944i6h1Gwju8fI+5fAZ3dAOe5nX4OtljzsKipzjuuOO45JJLWLGiUPL97LPPsmDBAl566SU+//xzABYuXMhdd93F+PHjufLKK1EUhXvvvZc5c+Zw9tlnM/Nrh5IOjOCpBVu486MNRa6xQ8eHuOKwsew3KsDc9VF++syXpPIaT/94KnuP9PP5pji3v7eW15Y1cvyUQVz8tVEoksQvXlzGvA0xZk0fzPkzRzKx2sOi2jjHPPQ5IwNOHplVSA7NqzonP/WFIXoCeB0Kj87anR88tohjJoW5/WAvmWSCTZs2ce2115LL5TjqqKP48Y9/TCaT4eqrr2bLli1FrrgVK1Zw3nnnIcuyEbrUE3RUGO1OkbWrf68oCoqiGI40WZapr96dL8Z+B13CcGN2J5Kugg5T1vyHIZH2O8lKJ8ZmQVuWZWNCLoTsUkGt9P/CqWUWTirBvavpsOiwG8qei/oaOZ9pMVEW/YAVRTH+Fe6ncv2/xPck9jnx/9L7OoI4v5vP8eVa2LS3xLympoZYLEYmk+HCCy/kq6++4v333zdcqQceeCCHHHIIv//976mqKiQW//znP+fyyy/HZrMRDAbRdZ277rqLA+/8H82+4jY5YY+NWEblw/P2NkTOYX4Htx83kZOfKhav51ywT5EQCvDcKVO5/NWVbIlnefn0GRxy33xO23soXofCPf8rv0g1psrF5V8fw3kvLOX43Ws4cJDG41ecznHHHceQIUP4+9//zkMPPcQZZ5zBlClT+Na3vsVNN93Eqaeeyvr163n33XeLnu/Pf/4zF198cY8IVubFDCGKKYpCOp0mkUh0yXXYnvtE31bh4tSBjDOEanOhSwoyGjY1gyvb3KEmBG0JgIqiGO5KMa51RlTc0euU4nA4CAaDRKPRIhezRedYue/5JKrG933ImBldx9u0il3m3dPXW2JRAYRCIfL5vFG1YzEwsZycFhY7CaJfW2nggSseZ5x9SWFSJMukHQEyuo2spqNlM+jpGEqyyRI1LSqWcqvxAPvuu2+L+1atWmWkhwoeeOAB7n/gr3xxxE2GsGJ2jb23KsJ7qz5v8VxHP9jyvhe+qOOFL4p70z69cGuLMKP1zZkid1gpZhfCG19t5mdP3I5NKQhuoix93rx5zJ492xBGHA4Hb731VpErDuCII47gpZdeavW1ukpvlpv3BCLFXNd1Vrp3Yf247xV+0UOTNF1SAI3F475LctkLjI0vM0rOzQjBwZzCncvljNASRVFIpVJGCbFwbsqyjMvlKiobbkvwNTsauiIsd6fDN5DaQsTXzf2ju4qu401swretVLz4V9tvCzet6GspyzKZTIZcLmcISWZh1GazGbfN7EgENTu2Wzu/C8HT3MJGVdUi0bOcO7C+vt74vyiDz2az1NXVGfeJ125qasLlchnPraqqUeKZV1Vi7iEtvsdyYW7HTK4mo2q8fsYe1MaynP+vpSSyKj6Hwttn78mmaIYL/72MplSeYQEnKxoKVStNqTzV21KYzztgJD+cPqRsgNy3d6vh7RWFsKkVjSm+Pm4IOoUeuJFIhNGjR7Nq1Sry+TwLFiww3FwHHXQQS5Ys4eSTT+b111/n+eefR5Ik1qxZw/Tp0/niiy+65F5s6z4hZor/u1wu47NuLx0VA3VdB0mm3j+Ore4RxP0jSfiGoykt26/IagZPbBPe5nX4GlcQaFxeqPhp5fXKoSiKIYq35WLvKbLZLOl0Gp/PRzab7ZfnsErC3byORGhsjywQdhpdw9O8rq+3wqJCsJycFmCJnBYWOzXlS9yjOO12fNscIXgVVFdVt5TAWVhUKhKFAK1KciBc8drKgrDSvIGmbYFBwnlodo2VpiPDdoFEVVU+/PBDVFU1+jf2VtBMfyGdThcS6Mfsx/rxxxfu7Ol9QJJB11k16fs4lj2HY83sFg+x2+34fD5sNhuZTIZEImG4TUX/zVQqRSgUMspZBcKlmUy23k4kGAyiaVqPleB1xX3rbFwN3hEVNVGWdI1gcjMOh6PDYrDT6cTp7JgztZzw2Rqtia6lLRrEdontEeKZ+Cl10A0ePJiDDjqIp556Cp/PBxTE0/PPP5/f//73Rpq82+1GURQ8Hk/Ra0qKrd2O3ME+BzUeO0c/+DnnHjCCnx04klvfW8vX7vuUxmSeU/cayrXfGMfP/7Mc2fTZNqfzhD02/vVFPY/Or8VrV8oGyO06yMP/tgWZLK9PsevQAM+/8G9kNM477zwmTJiAqqqEw2EkScLhcFBTU8Pw4cN54403ePLJJ7n11lv54osvqK+vp6mpialTp7Jp06Y2v5P2lC/vqN+i2B6bzWbc1nWddDptuA9be52OkLd7aRyxH01jv0bGGWxHyJqTeHAs8cBotow5tMMha+aAoWg02mfnpng8TjgcxufzWSXJXcTfuJz6cYf19WYUIyv4Glu2LbEYmIjqF4uBjSVyWlgMIMoFHphL4Mwp7mbB0+zqsLDor/QHB0JbQRCSJBWJoEIIFX3OjKfUdeN5zGKouD0Qj+WYo4rl475TuNFbIrckga6xdOLxTNy60ujRKcZah8NBLpejqampaGFJCEmpVMoQuZubm4ueOpfLFVqStEFPuxm64vD11C+FMYd29yZ1CV1WsG9aTFNTU4f/VgRFiT6HiUTCEHQ6KpiKdgSivNfsDDXfNrbbJGKahUzzc5udpVDYN9xuN1dddRW33norsL3v6GWXXcaLL77I1q1bDcFXOItFb1DxvJLc/mlEcyrPO6sKn+3bK5q48vCxwHbX53OLtvJ/+w4vbJ9p+4MuG43JvNEDuT0BcqftPZSP1jZz2e2X8s19d+WUU07h1VdfxeVyGY5oUc4YjUb54IMPaG5u5rPPPqOqqooVK1aQTCbJZrM0Njb2Sr/FZDKJoih4vV6cTie6ruPxeHA4HMTj8S4tPutI1I09lC3mUErosZA1wAjQEsFvfYmu68TjcQKBAJlMpk+CnnYWfPXLsKeaCv3+K2HBWNexpyP46pf19ZZYVAiaphnnM4uBi7UHWFgMYForgTOHHYhecOYSuK6mvFpY9AX93YEg+u+1NtktdYC2xwVaKoTujMe1jsT6aSehI/X+pEwqjJ/rp53EpHl/wef1GKXmzc3NLSbbohRduDadTqdRSmwmm80azrvWqOQL/Z1toqxpGtFoFIfDgc/nIxQKkUwm23TadgUhgpb2CxU/NputhaNWHOeiP+Jvf/tbHn/8cWpra1EUhVwux6mnnsratWv517/+VSTmCfG2oaGh6Dk7ksz90doIlxwyBoA9hvtY1ZjCrkhIQFbV+drYECsbCp9XbSzL+LCbrfEsVW4bDckcfqdCLKO2GiC3tD7J+LCbTzfGkJCoT2SJpzJs3ryZGTNmsHTpUkaNGkU2m2XKlCksWbKEdDrNZ599xrhx45g7dy677LILzz77LLlcjmHDhvH222/3avq4qqpEo9GihRAR0JPNZonH4x3enrR3MOunnUQq0A3tIaTikLXmoTMYtehJXIni1gE+nw+3200ikeixY6CjCHHT5/PR1NQ0IBf7ugMJnep1H1E76ZvQoY6tPYVO9boPrZZbFgbmnsYWAxcreMjCwqJNRE8xs+MTtvf9Ek5Pq8TdotLRkVhyyJUVJ6zs+v5NPX6B3poLVPx/+ybpZR2g/dkFunXs16md9K2+/c51nQkb32bk5tkkk8lWk9G9Xi8ul8twj4XDYUPcMKMoCuFwuKxQKnC73Xg8nhbCVKWwdexhhYmyVAFlZbrOuPX/pXrlf7vF5eXxePB4PGiaRjwe7xPnWKmD0yyCHn300VxwwQWsXl3oS/zSSy/x5Zdf8uijj7J4cSGV/ssvv+SBBx7g0EMP5fjjj2fkyJFG2NnYsWO57LLLmDJ1GrM3pbns1RVsjmbbDHPbHMty49HjOXBMkGxe45SnvkSR4aWfziCR08jmNc547is2NGfYbbCHe4/fFUWWuO7NVby1oomrjhjLMZOrkcAIkDMztsrFZduChwJOhad+PJWq5EZsep7rrruOdevWlQ1zC4fDXH/99Xi9XmbPns39998PwJ133snFF1/cpws/drsdr9eL3W43xl5zONGOiAyZzvrpJxcWeNrj2uwomoqEzqiFTxDashBJkggEAtjtdiPYqpKQZZlwOEw6nbZCSbpA3u7lq69fjd4BJ3dPIWl5dnv3Omy5yhDTLfoe0Yfd3HvaYuBhiZwWFhYdwlziLkTP0hJ3IXr2R0HEYuemsoQVjaHLXmbwmnf7ekvKCp/ipz+7QPvTZEySJKqrqw0HoN1uJxQKtShnF4TDYaOXZzmcTieBQID6+vqKHIsr7bvZf/6f8MqFxTtRqtwVFEXB5/PhcDjIZDLE4/GKPEZKRVCbzVZ07Jc6Yszp8Qmbn0/3vrhvNrwMf/3Brlzy8gqjtH3X927Eke54+4GpU6ey11578eijj3b3JnYKh8OBx+MpEjt35BRuHL4vG6b+sHCjJ89324KIRn/5LOOTK5AkqU8ChtqLKKOPRCIVu439gUpZPBy27CUGVcA1lEXlIK59RJCexcCk768sLSws+hXtKXH3bkuntUrcLSqN8Ma5bJl4zLa+ZH2LpGuEN87t680AOtYL1ByIJPoEQusuUHOZbG/TOGK/iviuAXRJpnHEfq2K2qLfoQiKE2XtrU3Es9ksDoejVZFTjLeyLPdqyW17seUSDFn+akVMlIcuf5VkQy05ux2Px0MwGCSfz5NMJjvtRlNVlebmZpxOJ16vl3A4bIRJVRJCsGwNEYojFjWFCCrLMn49iaxmyyZz9wVn/XOJ8X85n8HeCYETYPHixYajtRIQ11wOhwOv14vNZsPj8RitLUr30ciQ6WyYOqtwo5dC1tbt/kN8K57HsWZ2RV/rpVIpnE4nfr+/U/13LQoMWvMezUNnkPKP6BmX8I7QVNzRDdSsea/3X9uiojFf+1TyWGTRs1gip4WFRZcpTXEXAogQP8Xk3Sx6tjV5t7DoKSpNWOkPJVbt6QVa6gC12+04nc6igJRS12epENrt241Ew+iDqIy+YQASDaMPLhvWIUkSbrebVCpliMFOp7NNp1YulzP6Jpf7/ISwWakiJ1TeRDmXy9Hc3Gz0RBTJ0F0RO0UvQI/HY7QjiMfj5HK57nwnPYau60WBhYAhdtrtdvzJWpp93dDvsTvRdfzJWrwej3HNsTMgxE4hnMuybOyjYp9KewezfvrJgN57FQuSBLrOV7t8l4m1y42QtUolFotRVVWFx+OpmJ6h/Q0JnVGLnmT5zF+h61rvVsfomvH6Vi9Oi1LENZTVl3NgY4mcFhYW3U6pK6y0xN3pdCJJEpqmGT09rRJ3i96i0oSV/o443ssJCR1xgbbWB7SzLtB4zSRy7qouv79uQ5LIuauI10zCX7+06FdutxtJkoyFIjFGtta7EwqCh67r2O32sgKc2c1QqVTqRFkEQwnHXFfFTl3XSSQSpNNp/H4/oVDI6K3YH50mYtFD0zR8sQ1EvSPQpT4YS1tB0gvbJSpLNE0jl8uRzWbJ5XIVK/q3FyE6u1wuvF4viqIU9qlMlhW7/3hbyFovH/eShK4XQt4mzLmrosUnVVVJJpN4PB4ymUy/3x/6CldiK6MWPsG6GaeCrvfOQse2a4FRC5+oeDHdom+o9CoWi97BEjktLCx6nI6UuIvJk1XibtFTVKqwsjPSGReozWbrFhdoLDwRNLVvhOzW0FTi4YllRc50Om28F5fLRTabbXP8E5+t6PlY9uU0raJFTqjsiXI+nzfSroXYKQSStgTo1lBVlUgkgtPpNPp1JpPJiithbwuxUCmO0XR0DRuHz+zrzSpClxV8jSuIx+PGQovNZsPn8yFJEqqqGtckuVyu3y6uptNp0um0ETJWO+pgkoGRfeeqlRVSwdHUjz204vskJpNJo2w9Eon09eb0W0JbFqItfqbQ/1XvYffwtv6vIxc/TWjLwp57HYt+jeXktABL5LSwsOgjWitxNwufUFzivjM4MCwqg0oWVgYSO3KB/vznP2fGjBnU1tZyyy23IMsyJ5xwAkcddRQAzzzzDB988AHBYJDLLrsMl8vF888/zyuvvEIqNAYkmUsPHcMPpg1i/7s/KXr+M/cdzv/tOwxNhxe+qOMP76/jgNEBbjpmFwCG+528srSeX720gv+evSeKJKHqOg/P28zjn9Uaz/PK6TNYvCXBpa+saPEe/nHSFE59+kvO2nc4J84YDDpMGXQwN18TZ9myZVx33XXIskwoFGLt2rX88pe/5KyzzuLggw/GZrPx4osv8tRTT7UahJLL5XC5XK1+vv1B5ITKnygLsVNRFDweDz6fzyh17YzYKUrYvV4vXq8Xp9NJPB6v2BYuYuHB6XSiKAqqqpJKpchkMkh19djHHEvOFaqMknVdx5mNMiyzESUQKPqVuf+o0+nE7XYX9Q/PZDL9srQ9lUoRV2VW7XVI338HkkTtxGOp2ji34tuxxGIxQqGQ0SrEonOEN81DVjOsn35yQWDqiYVFTS0sEC98whI4LdpE13V0Xe8X1z4WPYclclpYWFQEQuwQE8b2lrj3xwmJRWVQ6cLKQGfixInU1NRw+umnc8YZZ3DggQfy2muvcdxxxzFr1iycTid/+9vfePXVVznzzDN58sknWbBgAXfddRdz5s4lFRiJz2lj2lBv2ed/a0Ujf5u3CYD3zt2LRz/dzOx1UY544DMAHjpxN/79Rb3x+OMeXkAiW7zIMnNMsNXt/9q4EItq46iazn1zNnLfnI0ALLh4X/43ezaZdJqzzjqLcDjMKaecwurVqwF46qmneOGFF4hEIjz99NM888wzLF68mLPOOovHHnusyHUmej0K4amU/iJyQmGibNNzrJ36Y3S9hxy4XZwoq6pKLBYzSl2F2JlKpToskui6TjweJ51O4/P5qKqqIpVKkUgkKsJZKMsyLpcLp9OJzWZD0zQymQzpdLpIjJWA6nUfUTvpm1RG/1udqjXv09jQALRMjzffFhNh0WNUiJ5A2bYZQiStxAqThuH9J2StUsjn86TTabxeL5lMpiK/1/5CaMtCXB/Xsn7aSaQC3dyjV9dxRzcwatGT1gKxRbvQdd1ycg5wKuNsaGFhYVGCKHFPJBJEIhHq6+uJRCIkk0l0XcftdhMKhaipqSEUChlumP4yobeoDMKb5jF6wWNIulYobe4JNBVJ1xi94DHCmz7Z8eMtAJgxYwazZ88G4OOPP2bGjBkAbNiwAafTicvlIhqNkk6nmTx5Mu+++y6NjY0sXLgQ/5DRaIqDiw4exV/+t6Hs869p2u7Ay6k6mklXsisS+44M8MGaCACaDi+dPoN//WQao0PbnZMXzhzZ6vN/d/ca3lreWHTfPiP9fLklSYyCU93lciHLMgceeCDvvPMOUHDNZTIZbDYb69evNybeq1atYsqUKUXPJ0ptHY7y6daapqEoFVSuvwPGpNeyz+IHcMc2Gs7nbmPbRHniR7d3eaFBiJ1NTU3kcjm8Xi/V1dVGBUJHyOfzRCIRYrEYTqeTcDjcpju3J5EkCZfLRSgUorq6Go/HY2xfQ0NDq27T8Ma5hTG0ApB0jfDGucZt0Y8zk8kYInI0GiUSidDY2Eh9fT0NDQ00NTURj8eNNhGKohhuT6/XSzAYpKqqiurqampqagiHw4RCIQKBAD6fD7fbjdPpxG639/oxV6kha3rFbE/riL64fr+/rzel3+NKbGXC7DsZtuwlJC1fWNzt7Diu64X2PlqeYcteYsKcuyyB06LdWE5OC8vJaWFh0W8Qzs1yJe5OpxOPxwNYJe4WHcNyIFQmfr+furrCZxaPxwkGC67JDz/8kOeffx5Zlrn++uuBgjAoHFixWAxfVZiAU2HaEC83v72mzdf53pQaVjWmaEhud4UfOSHM2ysbjfnZrCcW0ZjMc8i4EHd+ZyLfe3QRXxsXYsHmOPFs+fFl10EeVjUWlzKfMG0wzy7aimoriFgej4dhw4axZcsW4vG44Sg766yzOPLII3n66aeNv924cSPjx49n8eLFRc+Zy+VwOBxlnYSapmG329t8/5WCCFDJb13NhM13Uj/2UGonHrvNnSZ17rjUdUBH0jWGLn+VmjLJ9l1BiJ2JRMJITzeXsXfEkZlOp8lkMvh8Pvx+v5HC3hsl7KIUXYjl2WyWaDTa7pAlWy7BkOWvUjvpW31bLq3rDF3+aofLpIU709xCB8DhcGC323E4HMYYU9oLWFyHlFtkNTs/S52g4nZ3uHb7U8hapSEc1cFgEKfT2algMYvtSOiM3Pw/JqRXscozga0jZxb2TU0tVMu0NT5sEzaRFezpCDXrPuwXbQ8sKg9N0ywn5wDHEjktLCz6LVaJu0V3IRwI/UFYGSjEYjF8Ph8APp+P5uZmvF4vJ554It/97nex2+3cf//9fPjhh+TzeSRJQtf1QpBELMXPjxnF3R+Xd1kKpg/z8bOZo/jOIwuK7j9h2mAe+XSzcbsxWRCa3l8d4bbjJgBw0UEj+b9nv2KvEe13AB0zuZpr31zNSNlm9Dc86KCDeOutt4CCs1NVVW699VbuuOMOHn74Yf79739TX1/f6nOK3o7lUFW1X7gZRB/mRCKBqqpIwKA171K1cR6NI/alYfTBFT1R1jSNeDxulLELsVOUsbdXyNJ1nVgsZpSwh0IhUqmUUcHQnTgcDkPclCSJXC5HPB4nk8l06rUGrXmP5qEzSPlH9EnYl6SreOObGVk7B9Vm6xZxWIQTJRIJZFkuEj1lWTZcoqlUylhQba003mazGfeJybfH4+GWW25hzJgxnH/++axcuRJN0/je977HscceC8AjjzzCW2+9RXV1NTfccANut5tnn32WV155hUMOOYQzzzyTpD3AnHqJX7xU3BfYY5d5dNbuDPI5ePHLem5/fx2yBI/8cHeGB5ysaUpxzvNLcdtkXjhtOjZZIq/pnPHsV6yLpLn3+MlMGlRYPN5/VICRN39EJJVn1vTBnLn/CGQJrnxtJbPXRYte99wDRrBsa5xFqyfy3G2XG5/LDTfcwKpVq7jyyisZO3YsAFOnTuWoo45i2LBhXHHFFeTzeeLxOJdffjnpdJpbbrmFyy+/vMvf5Y6+Z3HMZbPZimgX0Z9xu93oqWaqa98mvOod4jWTiIUnkgqOJhUYiWZztvgbOZ/BHd2Ap3kdvsbl+OqXWddNFp3GcnJaWCKnhYXFTkO5FHcheIp+W+YUdyF45vN5qxeTBRJ6vxJWdnYWLFjAqaeeyksvvcSBBx7IggUL0DTNcLzl83nsdjuSJPHFF1+wzz77MH/+fHbbbTdW/O0pjq/2cODogvtzYo2H3xw2ht+9s9Z4/uEBB/ceP5kTHltEKrf9+LfJEvuM9HPmP78y7vM7FWIZld0Ge4ikCuLJLtVunv7xVKo8dgZ57by/uomXvmow/mZpfZLxYZfhEN17hJ8vtyRI5wsleB6Ph0wmw9e+9jXOOeccoOCoE85zMfEWzqIRI0bw9ttvt/icstksPp8Pu93eYgFHuBmEAFyJSJKE3+8vcukLbLkEg9e8y6A17/WLibJZ7BSJ1yLUpCNiZy6Xo6mpyXgO4ersqsvMZrMZfTZlWSafzxuu066eAyV0Ri16kuUzf4Wuaz3b47gUXQddZ9dV/8Hv8xr7uzi/i3N9V44BMfaIRVWbzWaInuVS29sSy4TYGY/H+eUvf8kFF1xg9M+12+2ccMIJnHvuudjtdv74xz/y+eefc8455/Dcc8/x2Wefceedd/LRRx+xZs0azj77bL6YfgYPnPct9h7h59ONMeN1zthvOK8ubeDBeZt55fQZ/OPzWg4cHWR1Y4qfPP0llxwymu9PGcR/vqrnJ099weZYlqMmhrnkkNFc9J9lnPdCwYU5psrF307YjUgqzzC/g+/sPohv/PWzVj+r7+xWwzf/twFvcDRnnXUW+Xyevffem1NOOYXrr7+em266CYBhw4Zx7bXXEovFSKVS/PSnPwXgnHPO4fDDD+eVV15hwYIFHHjggfzvf//r9HfXHuLxOOFwGJ/PRywW2/EfWJRFXHOLxHoJHX/9UsPRqyORc4VQbS502Yak5VHyaezpiCVqWnQb/akfuUXPYImcFhYWOzWlzk2rxN1iR/Q3YWVnZdmyZTQ0NPDggw9SW1vLo48+Sj6f5+233+bvf/87sizzzDPPoOs6jzzyCDfccAM/+9nPeO6558gnopz2zJfGc825YB9D4Lz3+Mmc98JSrvvGeAZ5HTx+UqHP5XnPL2VZfZIjJ1TxzsqmolZib521pyGEXvjvwmRtrz/PA+DQ8SGO27WmSOAE+PcX9Rw5Mcy8DYUJ8wnTBvPswq0AuCQVm81GdXU1dXV1xONxwx123nnnMWbMGOx2Oy+//LIx4d5ll1246667WnxOouzV4XCUFTmhIKpU6pgmBKK2hIX+NlHWNI1EIkEqlSoSO9PpdIdcmSLB3Ov1EggEyGazxOPxDn2Xoreky+UyAqqEWNfd+4QrsZVRC59g3YxTt4W59UK54LbPctwXT5OtXUk9BQGy3Hk+n88XiZ5def/iuQRml6cIMMrn82Sz2RbXIaXl8fl8nkQiQXNzMwBr164lHo/j8XiIRCLE43EmTZrEnXfeiSzLLF++nN13350VK1bg8/tJBUaSVXW0kv1q5uggl75ScHe+uaKRA0cHGV/tZsHmOACfbYrx3d0H8eyirWyOFRaHs6rW4nlOmDaYf24bu46ZXE1G1Xj9jD2ojWU5/19LiwLZpg31sqoxBZJEKjCSXD6PRKEdxYoVxU7Tb3zjG4aL3fxZOp1O1qxZA8CcOXP40Y9+1OMipyhbDwQCZDKZosVyi/bjdruNY6wcEjqOdFMvb5XFQMNyclpYIqeFhcWAorTEXZSRlStxL3WBWAws+puwsjNyxx13tLjv73//O3//+9+L7quvr+e8884zbtuRkPMZQ5Te/+7tgU/CnXTWP5eUfc3XljXy2rLiwCDz35fy3qoI762KtLj//dURzjtgBIosoWo6v3ltJQCKmqXaoaKqOhs2bOCSSy4BCqXquVyOG2+8scVzTZ06lU8++aRVt102my3be7PSRU5zgFRHnIT9ZaIsxE5Rxu52uw1nZ3vFTk3TikrYzSnsrSHLslGKbrfbjWT0TCbT4+ey0JaFaIufYcPUH24TOntworkt7Gj3ta/g2roQIZMJAVI4g4VDUlR1tNbKJp/Pd9rt2Vppu6ggEdcUQvRs63j88MMP+ec//2n0HU6n08iybCwE1NfXo+s6dXV15L017DWmmsE+B59tihc9T5XbTjRTeJ1oOk/YY+errQmOnlTN84vrOGJCmCr39qmgXZG4+shxnF0yNh4/ZRDHP1oI6xrsc1DjsXP0g59z7gEj+NmBI7n1ve0O+V0HbRM5Ac3mJDh8PH++6SqGDh3KxRdfXPS8RxxxRNF9M2fO5MILLySXy/Hwww8DhaC58ePHt+cr6DJC3PT5fDQ1NVWs+71SEQsq0Wh0xw+2sOhBrJ6cFpbIaWFhMaDRNK3VEnfzBKW0xL2rpW8W/Y/+IqxYFL4rd3QDiarxfRqEctKTXxTfoet445tQtomOwsUoxpJ8Po/L5WrhElu8eHGLwCEz2WwWv9/foizdHI5SaQs1sizj8/mKSvJ3VnRdN8ROIXSay9jbI/CKEnaPx4PH48HpdBpp4FAo+zcLm1DYL5qbm3vdlRbeNA9ZzbB++smF/bEnenRqKhI6Y794imG5DbQ1MptFXkFrrWxKqzo6U8bf0dJ2M+3tOyyEpPCQodz8rYn84LFFLbYjks4RcCo0p/MEXDbWNaV56asGDh1fxZtn7cmXW+LUxre//v3f35X7Zm9kRcP2thFjqlwkcyp1icL40ZzK886qwqf99oomrjx8bJufxZZoitNPP50pU6Zw4YUXcsEFFwCFUvVUKkVT0/Zv7uOPP+bjjz/mtNNO44QTTjCEzt4kFosRDofxer3E4/Ed/4GFgdvtRlXVnX48t6h8LCenhSVyWlhYWJRglbhbWPR/3M3rSITGgtT7ISitomsEEpvI5XJGzzIhsgh3mVn4VFXVEDzFT7nFlVwuhyRJOByOFhPMSu1N5ff7jRLRgYKu6ySTSaOMXfyIMvb2CGqih6bP5yMYDBpCnEhG72qAUHcR2rIQ18e1rJ92EqnAqO5dbNALixijFj2JL9sEVVUdfq+tnedFZYfb7QYwHJjmyo6OYl60MAckitJ2l8uF3+/H4/Egy3K7+g7fcccdeDwe/njNbzj9haWGCGnm47XNHDkxzMOfbObICWHDofnrlwtl41cfOY53VhZExquOGMuqxhTPbCtLF/xg6iD+uWj7fR+tjXDJIWMA2GO4z3BtCpbWJzl0fAgo9DdGKQju8XjcEH0BjjzySKNUHSjqKSzadwCMHDmS1atXd+Tj7hLCfe31ekmn090SYDUQkCQJl8vVpsPcwqK3sJycFpbIaWFhYbEDypW4mydDpSXuXZkMWVhYdA/+xuXUjzusrzejGFmhJrmeZHJ7IFU+n0dRFLLZrOHOstlsRT9ijIHtiytm4VOU3drt9hYiZyUmrLvdbhwOB5FIZEA64suJnS6Xq91ip6IoaJqGruvY7XYjdC8ej1dUiJ4rsZUJs++kfuyh1E48Fl2SAalzgqeuAzqSrjF0+avUrHkPCR2pTJuGziDO8wIhRorzvBAgzVUd4t+OfObmgMREIsFdd93F5MmTGT58OK+88gpvvfUWs2fP5vHHH0eSJJ577rmyfYczmQxnnXUWI4cN5s7vDgPgujdX8/7qiNF3+MF5m3ls1u78dJ9hvPxVAxujGYb4HDxx0hQ0XeftFU18sDrCyKCT3x4+lg/XNnPYLlXMXtvMla+vAuD4qYONUnWARbUJ1jen+e/Ze5LNa5zy1JdF72/h5ji7VBcE4mF+B4/dcRPkCqFbv//9743HHXHEEfziF78wbh900EGccsop6LpOc3Mzv/3tbwHYf//9effdd9v9+XYHqVQKp9OJ3+8vcppatI5YFDAL2RYWfYW4rpBluaLOiRa9h7THHnsMvKtLCwsLi27GXOJus9laTIasEncLi95FR2LJIVeSc4X6tGTdQNdxZqPsv+AuIqaJs6IohMPhHZYVK4rSQvwU4qWqqkZ5ViwWM4RPgEAgAFAxfdIURWlXX8mBhHBBeTweJEkqK3YKsdvpdBYFCGUyGVwuF26320h3r8TQlLzdS+OIfWkYfTA5dxVoaqFfZ1vHpq4X+m7KCo50hFF1n+Ja8R5Kdvt+Y7fbCYVCNDQ09Phk1lzVYbfbUZSCS9y88CD+7Szm0nbh4jSXtpdeR2RdVSw59Lddfm/dyXkHjGBpXZK3Vzax63s3dqnNyy233MJvfvObXr92EuNUMpksWpSyKE91dTXpdNoa0y0qApvNRlVVFY2NjVaV3QDFcnJaWFhYdAPtLXHvznRXCwuL1pHQqV73EbWTvglUgMiJzqi6T9FKjnmn02n0Bm4L4TQzOzVLXeWyLBMMBgEMd6ckSUiSVDGOhkAggKqq1mTYhK7rRn9Os7Mzm82iqioOhwObzWb0liwto00kEkUl7JlMpuJcnbZcgsFr3mXQmveI10wiFp5IKjiaVGCkERBmRs5ncEc34Gleh69xOYHGFVSHq0goGuYC6d4sSSyt6jCXnpurOnRdbxFc2F6Rbkel7ebU9mw2i56OFIWsVQL3zt4IFL5Dexf7WF9++eXdsUkdRlVVIzAsk8lY12pt4HK5kCTJCPqysOhrzE5O69gdmFgip4WFhUUP0FqJe7l0V7PT0+r/ZGHRfYQ3zmXLxGO2lcn2LZKuMbRuAU6nE6/Xa4h8Lper00ENpYEqNTU1JBIJVFVt4fisrq42hE/zT29OALxeL4qiWCWgbZBOp9F13QgX0nW9KGG9NVRVpbm52di/wuEwiUSi4oQHCR1//VL89UuBguM65wqh2lzosg1Jy6Pk09jTESS2C4M6hfRrEdhUSl9USZhLzwViwaE7FjhLS9tbS233JWuJ+kdXhmNdsK1vagVtUYdJJpNG2brooWzREiEEV9KiisXARuyLVl/OgYslclpYWFj0AqVihCRJRZMhr9drOECsEncLi+7BlkswZPmr1E76Vt8KALrOLpveJxutJ7ctXEi4vRRF6bY+ZrlcDrvdTiqVMoQXl8uFz+ejubm5qI+wEF/EmFP6090IYUaIsBbFiFJ0EbgihC1JkvB4PPj9fhwOxw4/v0wmQzabxePx4PV6cblcxOPxiu0RLaG3u5w5lUrhcrlwOBxFqfKVhDh+hBDbVg9v87m+tVAxM62ltvvjG4n5RqJXWMiap3ldX29Fl4nFYoRCoVbF9YGOw+FAUZSKaYdiYQHbF70q7fxg0XtYIqeFhYVFHyAEDvPEU7iuyjlAzKVvlkBgYdF+Bq15j+ahM0j5R4DcByKApuJLbGbE5tk0mcRMn89XFCDUHeRyOSMAwnj5bSmjom+gmKiLhRbzuCPKDrtb+JQkCb/fX/T6FgWBQIibkiS1moyeTqeNnp3hcJhMJkMymWz1O9F13Shh9/v9hEIh0uk08Xi8Xy+aiTJtj8fTor1Dpb6v0gVOoEWKu9frRdd14xgV48KOzvWiF284toaNw2f29FvpGLKCr3F5X29Fl8nn86TTabxer+VWLIM4Fq0qJItKQ9O0igtdtOg9LJHTwsLCokIQQkK5EnezAGGVuFtYtB8JnVGLnmT5zF+h61oh7KS30DUkdKasfZl0ant4RSqVQpIkvF5vt4bEZLNZvF4vNpvNGBfEpLy0J2e5hRZomexuFj5VVW0hfLZHXPL5fEiSRCwW67b32l8Rn6nT6USWZfL5PMlkknQ63aaAIhx8YgGsqqpqh2KnqqpEIhFcLldRCXt/TkBOpVIEg0FjH++PTh1x3AnBX4SKiXO9WKgwn+vFIicUxHHhaAWwNa7AkWkm6whURsm6rmNPR/DVL+vrLekWEolEwTHr99Pc3NzXm1MxiH3W+kwsKhERxmgxMLFETgsLC4sKxSpxt7DoHlyJrYxa+ATrZpxaSGzuDSFg2zE4cdk/8aQbaChxMAqXlggU6Q6Ho0hVdzgchvAlXqe9F/vlXJulwqdwHpof35rw6XQ6cblcRKPRAeuCUhTF+BxKk9E7ukglzglmsVOUtrf2XOK1vF4vPp/PKGHvjwtkwjXmdrsN0by/n+9KQ8VKz/UejwdZlotKMFVVNRZLXC4XI+s+ZdWIr1MpIWvV6z4s6qnan9F1nXg8TjAYxOl0drqH8s6Gx+Mx3NUWFpWGqGKxGJhYIqeFhYVFP6G1EndzmFFpiXt7y94sLHZ2QlsWoi1+hg1Tf7hN6OzBFX69IOaN/uIZRmZWk0gmWzxEpGfn83mjR2d3OOxyuRwOh4PkttfUdb3LjoZywqdwn4kfIcQAhuNTVVXcbneLct2BgCzLRim63W4vWrTqjv6YrYmdyWSy7PMLoUaksFdVVZFKpUgkEv1OJEylUvh8PqNn6c6G+VwvxHHh2lRVFUmSUBQFj8djLHKG1s1GGn5IxYSshTfO7evN6Fay2axx7GSz2X53zHQ3iqLgcDiIx+N9vSkWFmXRdX2nPD9YtA9L5LSwsLDox5SKD1aJu4VF64Q3zUNWM6yffnJhktoTPTo1tVAiv/AJRsRXortcLcRLcZzGYjFD/PP7/QBdFjqz2axRHi4m4j3Rm6rUfQYthU+3240kSTidTqqrq4v6CwvX6c6EeK9C2ITC99Hc3NxjbichdjocDjweD6FQqE2xM5/PF5WwO53OflfCLnokut1uNE3b6QSn0pYG2WyWaDRKPp/H4/HgdDpRVdUQ2+x2OyGnxC6b3mfFiMP6PGRt6PJXseVaLuz0d+LxOOFwGJ/PN+Bbb7jd7m5bmLOw6AmsnpwDG0vktLCwsNiJKFfibg45MJe4m/t8WSXuFgOF0JaFuD6uZf20k0gFRnWvIKDruKMbGLXoSdypetzV1SSTyRbHltPpBDCEL+FIE47Orrges9mscdyL5++ti32z8Ol2u4v6tZl7fCqKYmxXaal7f3Sdl/ZIbC1AqCfJZrNks9kisTOXy5FMJssKrKKE3efz4ff7cblcxGKxfvP5izCmnSXISgjkLpcLu91utDRIp9NIkoTH4yEQCKCqquHILcUfeQNfcDIJ39C+SVrXVNzRDdSsea/3X7sXEG7oQCBAJpMZsGXaokVCskyFgoVFpWA5OQc2lshpYWFhsROj67ox+RWYe32JxF4oLnHP5XI7ncvKwkLgSmxlwuw7qR97KLUTj91W4il1TvDUdUBH0jWGLn+VmjXvIaHj3nZclRNhXC5XCwFMlP0JR2dnhU5N01BVFYfDUSRyCmGxN1AUBa/XSyqVMrbBPAaZF19KW22UJrvncrmKFN7sdrvh2pRlmVwuRyKR6PMEZjHei0WtYDDYqtip6zqxWKxFCXs5Yb7SSKVSuN1ubDZbxW9rW4jzsHnhQzh/7XY7Pp/P6LEbjUbbHBfUfI5JK17gsxnnAX0TsjZq0ZM7TS/Ocghx0+fz0dTU1K/3vc4igrF2lgUGi50Ty8k5sLFETgsLC4sBhhAPxAVqaYl7uWRXq8TdYmdDQmfQmnep2jiPxhH70jD6YHLuKiRNLYiebQmeul7ouykr2NMRatZ9SNXGuUaJpiRJRphQ6SRYCHvlepnF43EkScLv9xsLFJ1BCCQCTdOKbvc0wnGWSCTK/r7c4osIWxE/5nYbpcJnuR6hvYGiKIYgJQKEUqkUmUym4oTYXC5HJBIxgmuCwSD5fJ5EItFiv8rlcjQ1NeF2u4tK2Cu5j6qoWnA4HP1uQU6WZVwul+FqFt9LOp1G13UcDgehUAi73U4ul+tQu4OQFmOXJc+wYrdZvRyyJrHLV8/iSTfQv76NjhOLxQiHw3i93gHZk9Ltdhv7qoVFpWI5OQc2lshpYWFhMcBprcRdOK1KS9zNZe7WRa5Ff8eWSzB4zbsMWvMeuRHTSVRPpNE5mFRgJJrN2eLxcj6DO7oBT/M6fI3L8dUva+FcEuJcay5OVVVbDZ8Rvd4CgQDRaLRTQmc2m8XtdiPLsuHsFE6xnsbr9aIoCk1NTR36u3LBakCbwqcIOGot2b07EIKU0+nEZrMZ42U6ne4XCz9CJCsVO5PJZAsRUwi2Pp+PQCBANpslHo9XnIArSKVSuFyufnMecjgcRhsHwChHF/uRaDXQGXETCq5QSZLwb/qMkarUiyFrEpNW/puR6TVI1dVGv9BsNrtTXidomkYikcDr9bZrHNCRyLlCqHYXumRD0vMouTT2dKTfuV7F+GuVqltUOpaTc2BjiZwWFhYWFkXsqMRdiCdCZLBK3C12BiR0BsXXUB1dRVUstn1ianOhyzYkLY+Sb9/E1OPxkE6nyx4PTqdzh2ENsVgMSZI6LXQKYcHhcBjb0RsX+0JI605hrJxr0yx8inJ34dgo5/jsqMhS2h9R9Ekt54LsLwjRTCxcBQKBsmKnpmlEo1Hsdjt+v78ohb3SED1cK9mtI9y/LpfLaGtQ2q9VtGuw2Wxks1kikUiriyBtIQKJVFXt9ZA115aFNJgWSIWgK1zY4pqiPywMtIdUKoXT6cTv97dY0NGRiNdMIhaeSCo4eocLZu7mdfhbWTCrNNxuN9ls1rrWs6h4xPhqDmG0GDhIe+yxh/WtW1hYWFh0CJGibHZ8Aoazyipxt+iP1NTUkEgkutRrTJT8NjY2tpgIOhwOgsEgjY2N7RIBA4EADoeD5ubmDoseoVAIVVWJxWKF9OVQiIaGhh6bnEqSRFVVFaqqGmFDvUlpsrvNZjOE3XKOz9YEaKfTaQQIZbPZIpf7zoTNZjOSultzdkJBsPd4PGiaRjwerziRNxAI4HQ6aWpqqpjzTalIrmma4do0H/eiJ7aiKGQyGZLJZJfeQzgcNty3grR3cI+FrPkTm5mw7J844lvKnu9lWcbhcBiip3CW53I5Q/Tsz2KZoihUVVWRTCYL353dS+OI/WgYfRA5dxVoasFF297WJ6kmqtd9SHjjPGy5yltUEOevSjrWLCxaw2azUVVV1e7rLYudC0vktLCwsLDoMuYSdyF6lpa4i0mQtaJqUYkoikI4HO60i0oQDofJ5XJG2bmZQCCALMtEIpF2P19nhU6Px4Pb7aahoaEwGQ+H2ZKCrGTrkZJJv9+Pw+GgqampYoSLtoRPIbboum70JZYkiVwuZySPD4Sxyix2qqpKMpls4TSWZRmfz4fT6SSTyRCPxyvmO/b7/cZ2lTvmepNyIULpdLqFMOx2u42KCCFudnUS3tb4pSN1e8ja6LX/ZZemz8nnskXn+9IAQ/MxZLPZDNFTHG/9vbTd4/Hg9nhZGpjG5l2O7rbPd8jyVxm0LcSuUggGg0iS1KHzl4VFXyHGREuUH5hYIqeFhYWFRY9gdnra7XarxN2ionG5XPh8Purr67v8HE1NTS1EC0mSqK6u7pRTNBgMYrfbiUQi7b5Yt9kdaKP3YJN9KInAqELJpOJo8bjuKJl0Op1GaX2lux5lWTYcm0KcEWia1sLxOVAcIIqiGGKnpmllxU6Hw4HP50OWZcO91tf4/X5sNhuKopR1T/c0kiQZ5eg2mw1VVQ3XpnlbRBiZ2+1GkqRuEzcFwkHe1viVt3vJTDyU9YP2JusKdcppaA5ZC4VCSJJEU1OTcb4X/ypKoTy+9Hwv3q9UUtpus9n6ZWl72juYzXucSsw7rNudsu7oekYtehJXYmv3PW8nEa64jvaJtbDoKyRJoqamxtpnByiWyGlhYWFh0SsoilI0CTKXuJvDjPrDxMZi58Pn82G32zscmGMmHA6Tz+eJRqMtficE0IaGhk65lYLBIDabjebm5jaPkd4umZRlmaqqKrLZbJ876dpCURSjhFgko4tSdE3TWjg+hUhjdqcNBOGzVOxMpVItRHmv14vb7UZVVeLxeJecz10lEAgABRdlOp3utd6hDocDl8tltDYQYVSln0WpuJlOp0kmk90uxoZCIaOfamsYY1BjE9HwhHb3jGwtZE2Ua5dbuBHuaPP5XpIkYyGhtLqjP5a2R4ZML/Q8RerxnqehLQu7//k7gFhM6Mr50cKitxk0aFC/WHy16H4skdPCwqLP2ZmSJy3aj1XiblFJhEIh8vl8UT+7jiDcjK2VRrVHhGjPNiqKUlbo1JGoG3soW7qxJLU9JZPBYNBIU6+049Ts2hS9EYWwuSNhTpKkssKnuSy39Gdnwix26rpOMpksErIURcHn8+FwOPqkhF1cN7hCNeiSgq5m8SiQrF2zLfG7+xEhQk6nE0VR2mxtIMuyIW4ChljcE5+RcInH4/E2Q81aG4O6ErImBO/2uGjNoqeo7gBaiJ6qqlZ8aXvj8H0L6fXQC+n1MHLx04Q3fdJzr9MGsiwTDod3uH9ZWFQa3dFn3aJ/YomcFhYWvc7OnDxp0TVaK3EXIoJV4m7RU9TU1HRpEidCd8qJmKI3VFfLpiRJMkTFSCRiOAp7MlykrZJJUSLbmWCknkKSJMNlZ7fbgdZ7I3bmuc2ha6XCZ7mAo0oTfjuKLMt4PB5cLpchdqbT6aJkcJ/PhyRJPTqZbPd1g5rF3by+W68bhAPY4XAYQnkqlSrr6C39vIS42ZP7gVhgqa+vb/V1erLcuC0He1sIt6f4EceScHCaHdTC4SmOu74sbY8Mmc66GT8p3OjO8bY1ti08jV7wWJ84Or1eLy6Xi4aGhl5/bQuLrhAOhw33vMXAwhI5LSwseo2dPXnSovsRJe5iYlNa4m4uIbWw6CxCAOhsg/odpc525ySxVOhsqJnSJyWTolQ1lUr1WolwW5SWD+dyOcO12dNCY6nj0+xK31mEz7bEO0mSjKCr7i5h76vrBpvNZrg2ZVk2hPLWyh7LOV/NYnBP4vf7jfGgNYTrtrGxsdtfX4x/3bGIY17oFEFhpQudwunZF6Xtae9gls/8VcEt35MOzlJ0DUnXmPjR7biSdb32ssIlXCk9eC0sOkJXK3Qs+i+WyGlhYdHj9FUZpcXOR2sl7qV9virFVWbRP2hPaEdbhEIhdF2nubm57O/D4TDZbLbbLrQlSSIUClFbM4Ol447bdmfvlkxWVVUB9GmPNrvdbpSjy7JcJGz2tdvbnOxuHquAssJnX29vezGXYZeKnTabzehtm06nicfjnRb5+uK6ob0hQmYURcHr9Rouz3I9THua6upqUqlUqyJUbwhVgUAAm83W7SJqWwud4pyvaVrR43qqtF1HYsUBF5Hyj+iZBaUdoam4oxuYMOeuXrv29Xg8eDyeTveStrDoS4LBIJqmVXS/cIuewRI5LSwsepS+KqO0GDiU6/NVGhZilbhbtEV7nFCtYbfbCYVCRCKRsuK6+H1nXaKt0Tx0Bmunn1q40cslkyPiK3G73WVT5Hua0r6IQojKZDIVHwhkFj7Fj+hL2N+Ez1KxU5QE6rqOy+XC6/UCkEgkOtwCorevG+x2O26323ABZ7NZUqlUm4tlNpvNcG6qqlo2jb43aM/4IgKHejJ9XvRt7Glnt3mhU5z3zX1yhaBpDj7qrtL2rWO/Tu2kb/XOeNsaus6wZS8xaM27vfJy1dXVRs9dC4v+RiAQQJKkVhegLXZeLJHTwsKixxhIyZMWlYPZ0SH6fEFxibsoebOwAIx08M5MzoPBIJIktSqQ9kQqbV+XTO7/1UPoDet6zbEmAoSEw070RUyn0/2+VYUsy60muwuHuvmn0sYtc6k6UOQoFKE0uVyOeDzeru+qt64bxix6kqHRZbhcLhRFIZ/PG67Nthxrdrsdj8eDw+Egn8+TTCb7NLnX6/XidDrbdFC21S+4OxGuv95e/DC7pcud82G7ONrZ0va83ctXX78aXbb16HtpD5KWZ7d3r8OW69nycZfLhd/vp7GxseLGHQuL9uDz+bDZbJ1awLbo3/T9SG1hYbFT0ivJk7KCrmusm3EqWh8mT1pUFqqqGu4uaFni7nQ6rRJ3CwPRB64zZZwiAbgtl4DT6ezWElEdifXTTiqIQL0pcMK219NZPOY4xm34Ez3pZ5IkyRA27XY7uq6TyWRIJBLdHpzSl2iaZogtglLh0+l04vF4jMcL8aYShE9d10kkEiSTScPZ6Xa7DbEznU7j8/kIhUKk02kSiUSrImLvXTforJl+Cq41LxHe9Em7xHKHw4HH48Fut5PL5XokwKczOByONrdDiH+94cRLJpM4nU78fn+vigqlfbnF8VOurY1weiqKYgRmtae0vXHEftvaJvQ9uiTTOGI/Bvewm9PtdvcLh7yFRWsIV7fFwMMSOS0sLLqdyJDpbJg6q3Cjp8t6JBl0nQ1TZyGrWcvRadECXddbiAjmyY/ox2gudxMCQiWXi1p0D6LHW2ccgR6PxyiDLIcQ1LuzjLVu7KHdX8bbAXRJIe4dTv3YQ3ukZFL02DQHCEWj0T51y/U25YRPIcaLH4fDgdvtLirVLf3pTUTYTiqVKhI70+k00WgUh8NhuA7j8XiL77N3rxsk0HWWjD2O0c2NhOKtXzcIgdlms1WUuAnb2x+05UB3uVzGea03iMfjhEIhnE5nnx2z5Y4fs+gpHJ1QWBTVdR2n02m0XigtbdeRaBh9EPTosk5HkGgYfXCP9qV3OBy9Jo5bWPQUmqYZvbAtBhaWyGlhYdGtpL2DWT/9ZEDvPZfRtgnL+ukn4/poc68mT1r0T0qdm+YSd7Nryipx3/mx2+2GO64jCIddWyWgLperW5N+83YvWyYe27c94QAkidqJx1K1cW63lEza7XYjGV0ECAkhzAq7KKDreotxS5IkY+wSIo7L5Sqb7C7Gr57+PMuJnS6Xi3Q6TSQSwePxEAgEyOVyxGKxguu+Aq8bzOJmNpttteduX+JwOIxFvHIIN3RP9sgsJZfLGe7dbDZbMcevOA5Eiw3Rs9McVgTbnV8ejwev14umaWz1jibnrurLzS9Gksi5q4jXTMJfv7RHXkK0mai0fd7CoiNYTs6BiyVyWlhYdBt9XUap6zrrp53Uq8mTFjsHpSXupeVu5hJ3c5iRNQHo/9hstk67OFVVbdWtJCbR3ZnquTOVTAqR2OVyIcuyIUC0lWRtUYzZwWmmtMenGL9KhU/x0xNClFnsdLlceDweQ+yMRqN4PB6qqqpIptIsr6DrBrGtiqKQyWSIxWIV2/d1R6XqLpcLoNcDkeLxOOFwGK/XW7FOQNHXV4zfwiltFj3FT7RqApKmovdFonpraCrx8MQeETnb04bFwqI/IK4lxPnPYuBgiZwWFhbdRl+XUSIrpIKje6yM0mLgUK7czez6aK3EvbV+XhaVi81m63BZpaIoOByONifwTqcToNtKW3eGkklFUYxydJvNVpSMXqlCUn+knPBpTnY3L9xAy2T37hzHdF0nlUq1cHYKgalu3GEVcd3QPPEIxjfMR5ZlMpkMzc3NFe3cF72m2xqDxOfc2+ck0afV7/f3WjjYE088wcknn9zhv6uurubEE0/kvvvua7XCo9k9rGhx6fqjxvPcoq34HQq3fHMCmq7zsxeWsnhLgmuOHMch40MA7DXcz6H3z2fh5ji/O2YXDhwTZG1TmjOe+wqXTeaF06ZjkyXyms4Zz37FukiaC2aO5BdfG8WnG2L88InFAJy7/whOnDEYgN0Heznn+SUs3BTjsbN+hjP6PZLJJFdccUWLfeHggw+mpqaGf/3rX9xwww0cdNBB3H///Tz99NMA7LPPPlx44YXk83n+8pe/MH/+fI4//nhOOOEEAF5//XUeffRRhgwZwimnnMIf/vCHDn++FhZ9iRj7ZFmu6PHcovuxRE4LC4tuYWcto7SwEFgl7jsfsiyjKEqHHbkejwdN09p0SHW3wBCvmdQvSyYlScLlcuF0Oo3WANlslng8bjmhexHhVjcL+mbhU/QnNvcqLHV8dtVhK8RO4ZaUPCFWDT+kIq4b1o/9BkPqFpBv7h9J0g6HA0mSWl1EES0MutNJ3hHS6bSRzt3U1NQn29AeGhoauO+++8r+TlVV8qpG3Du0aB+dMczH1W+s4u2z9+TbjyzA71C49/jJfOuRhVz31moA3HaZ2T/bh4Wb40wf5mNE0MnX75/Pbw4bwwnTBvPCF3X85Kkv2BzLctTEMJccMpqL/rOMpxds4ZUlDdxy7C7G6903ZyP3zdkIwIKL9+PN5Y04bTLf+fsiRr58KSf84Accf/zxPPbYY0Xb/4Mf/IDLLrsMgDvvvJO5c+ca1ykAF154IT/72c/I5/PcddddnHXWWcybN48PPviAeDzOPffcw4svvsiWLVuoqanB5/NVrDPXwqIcZienxcDCEjktLCy6hZ2pjNLCoj2UK3E398YrLXE3l7lbVAadCR2SZXmHfe6EaNSdE8JYeCJoKvSDkklJknA4HEYyOhQcrZUU2mJRXvgsTXY3C5+aprUQPjsjCKbTadLpNE0T966o64b1oSkMjrzb15vSLhwOR5vCs9vt7pMAKjMihMjtdhu9MLvK3nvvzRlnnEE2m6WmpoZrr72WFStWIEkSl112GVOmTOHtt9/mkUce4cEHH+S8884jm81ywQUXMG/ePEaNGsV3v/tdkskkTz31FEuWLOGXv/wlv/71r5kyZQq/+tWvUFWVDz74gLfffpvrb76FiGc4y+uTnPfCUgb77GyJZ3HZZFQdIqk8kVSeKo+9aDuP27WaV5Y0ADBzdJA3lzcC8PqyRn669zCeWrCFzbHCWJhVNbRti2F1iRweR/kxfp+Rfr7ckiCV00jlNEBhiKuqrPPa5/Mhy7Ix3tbVtexVryiKcY5SFIVQKERTU5PRVsLcymL+/PnMnDmTN954ozNfm4VFn2B2cloMLKxv3MLCostUahmlXjHbYzEQED2+EokEkUiE+vp6IpEIqVQKSZLweDyEQiFqamoIhUJ4vV7DjWPRN9jtdlRV7ZBDzePxGCW4Zp544gnj/y6Xy3D07ojq6mrOPffcHT4uGRxt9Cy8/qjxTB/m46AxQT44b2/eO3cvpg7xGo+96ejxvHHmHvz37D1x2mQCToX//Wwfmq87hCmmxz3yw92o/e3BnH/gCOO+e4+fzAfn7c1H5+/NkRMKztHBPjtvnbUn/z17T/592nRctsJ2vH/+vjx3w/k8/PDDjB8/HofDgd/vp7q6mkAgwLhx45g1axYNDQ3MnDmTv/71r/z1r39l/Pjxxnu/5557ePjhh/nmN78JFCYj11xzDQ8++CCXXHKJ8Xlef/31O/yMLLqOcNomk0mi0SgNDQ00NDTQ3NxMKpUykqgDgQDhcJjq6mpjPHM6nShK+0R4HYnaYftjXTd0DofD0WY/YIfD0W3CYmcRfXa9Xm+3igwul4uLL76Yq666igsuuAAAv9/Po48+yk9/+lOOO+44AN555x0OPfRQoCCOzp07l2984xuce+65nHPOObz77rtFz3vJJZfwm9/8hrPOOovHHnuMffbZh/+8+S5H/vUzzv9XYSHn6EnVvLGsgSq3jVh6u4Cc13TsyvZ954Rpg3l20VYAQm4b0W2PbU7nCXu2e4zsisTVR47j7o837PB9m59T4AkVyu3/85//FN0/ZswYNm3a1ObzZbNZhg4dSjgcZsKECQQCAVwuF6lUisMOO4wNGzYQiUQA2LhxozFuW1j0Fywn58DFEjktLCy6jFFGWSknEVMZpYVFX5LL5UgmkzQ3N9PQ0EBjYyPxeBxVVXE6nQSDQWpqaqiqqsLv9+NyudotElh0nY6GDsmyjMvlIplsuxWG0+lsd5/PtsolBToS6cBIY4ydMczHws1xbjh6PN9+ZAGnPPmFUd74g6mDqI1nOepvn3PEA5+RyWskcxrffmQB/1xc7Ob5zasruezVFUX33freWr5276cc9/ACbji68Jw/3mMozyzYwhEPfMbc9VGOnzIIgCP++hmHPfA5Dz74IGeccQbBYBCbzUYikaChoYHvfve7PPvss0iSxE9+8hPOOOMMrrjiCkOY+OlPf8rf//53zjzzTGbNmoXD4eCQQw6hrq6OM844A7fbzfTp00mn0zQ3NzN27Nh2faYW3Uup8NnY2Ggs4iSTSVRVxeFwGMKnWMjx+Xy4XC7DMW3Gum7oPDabrcilV4oIHOpor+GeIJlMomkaPp+v255z6dKC4Lh69WpqamoAiEajbN682dhXAV555RWOPvpo9txzTxYuXIiu69x55538+te/5rrrrmP06NFFz2u326mrqzPaOLz77ruMHjmSx2btzil7DgXgyAlh3lzeRCSdx+/avl/bZImcWnCNue0ykwd5mb+x0Cogks4T2PbYoMtGY3L7Oef+7+/KfbM3sqJhx4L0MZO3u0PFa9529aX84Q9/IBqNduxDBG677TauvfZafvOb37B8+XISiQSSJDFq1Ch+9KMfceut/8/efcdXVdj/H3+dc+7euUlYgYgIOEBwr1bRat1tv9RVq3X8rKt1a5erdVatWK3WVWut1lpX7bCuqhVXFUcVQWUoEAgQyLj35u577jm/Py7nkJAEkpBxk3yej4cPSXJzc7Ju7nmfz7ilx/cpRLmRDesjk3zHhRBbzW6jLCcb2iiFKCdWe3trayvNzc00NTWRSCQoFApomkYgELCro0KhEF6vt9OAQPQNh8PRo/EBVug2Z84cbr/9dv70pz8xefJkALtd8pFHHuE73/kO2WyW3//+97hcLgDOO+889t57b4499lgeeeQR7rvvPg466CDGjh3Lr371KwCmTZvGgw8+yO9+9ztOOeUUxo8fz0MPPcT9D/ye3x43A6DTdsmV8ZzdLvmNnaqojXh45axduergiUCpyqgx1fHztNol21rWXBq/kNMNu9Xr83Upwt7Sz2GF10FjunRfhaKJ4XDjrRzDkiVLaG5upqWlhUwmg2EY1NbWsm7dOiKRCOvWrUPXddavX2+HldOnT2fevHkUi0U+/fRTJk+ezIwZM3jnnXcAePvtt5k5cyYA7777LgceeGC3v1ei702bNo0//vGP/P73v+fGG2+0K5p//OMf8/TTT3PwwQcTi8VIpVL83//9H08++SQ333wzFRUVVFVVccABB/DXv/6Vl19+mVTV9h2eN4wOuHjh/+3C6+fsxkm7jAbA41D5+6kzePWsXXnp+7swKlD6OT9252reOHd3Xvr+LtSESku+tq/28Z+zd+ONc3fna9uVqpDP2buGV84qVSGvufKrfHOnqg6f1w2HTWJypReMIjO++nXuvfde7r//fg466CAALrjgAl566SUuvvhi+306q0KePn06p5xySh9/1Ttyu9326IDOWO3G5bAEz1pC5Ha77cfCrTV1aimI3mabbWhsbGz3Nqtiy5pHqmkaJ598Mi+//DJ+v581a9YwZ84cXnzxRc4++2wikQgul4uqqipM02Tq1KlEo1EqKirw+/3c/+iTfO/xT/nRrFoUBUIejXhWJ1MwcKgKYY+D8WE3LemNj69Hbl/J84s2hpH/XRHn4A1V8YdOifL2itLW8qsOnsiXzRmemN++OrMzu9eUWtWz+saug/u+vQMvvPo6H330UYfbr1ixgnHjxm32Pj/77DPOOeccbrjhBhobGzFNk0AgwOWXX86VV17ZbuZ0TU0Ny5Yt2+JxClFOTBRSjiDpwFjSoQlkgmPJeyqGTMW+6D05cxJihJo2bRo//vGP0XWddevWcdVVV+Fyufj1r39tVzf94he/YM2aNUycOJGrrroKTdO4++67mTdvHtFolJtuuglFUVjvrObYJ74kW9z4hNrnVHn4hJ2oDrj456eN3Pp6HdtUeHj3h3uwcF1plt0Jjy6gMVXg88v2oT5Rqjj45avLeXlpC/fM3p6p1aUB6XtPCDH+xrdIZHXuP2YHJlf6+LC+lUueXdLh87rhsEn84f01VHgc/Obs83DEZ9ufn67rnW6YPPHEEzn55JP59NNP+dGPfgTAoYceimEYvPzyy/36fRAjm9XiblXcKIpiz/R0Op34/X4URZEt7v1A0zRUVe12JaeiKHi9XnK5HB6Phx/84Adsu+22XHjhhVx00UV2u2Qmk+HBBx/kt7/9rd0u+e9//5vdd9+d3/72t/y///f/OOecc+yqmTFjxtgf47LLLuOnP/0pDQ0NKIrCt771Lf71r3/x8AtvsvQrpdbtLbVLjg64+GRtioPv/x+PnTiNvSeEeHdlz6t8bjx8O367oYVy3qoENxy+HSfvOoaWjM6PnytVf44OuHjy5OlM8u7MxRee324+Y0VFhT3vraWlhTFjxhAIBBg1ahQTJkywZz5aP8etra2EQiFCoZD9fslkknA4DMCqVas49NBDe/x5iL7T0NDAWWedRS6X4/zzz+fAAw/k5ZdfbrfUxHp8+vvf/87LL7/MhRdeSEtLCw6Hg0WLFnHuuefym9/8hlx0W3v8guXHB9byq9dX8NqXMeaevRtPLVjPEdtXsmBtkite/JKTdx3D/9tjHL96vY6LvlrLQfd/yJ7jQ1x58ETOfWYRNxw2iTOf+oyGZJ5/nT6TV79o6XRpS1s+p8r21T6WNmXwOB2c+X+HcN73j233uPDoo4/y9ttvs//++9uvs6qQ33//fR588EFefvllFixYYLc69+fjs8vl6rKK0+VyoWnaZpeiDbRcLkc+nycQCNDc3LzZ2yqKgqIoqKra6b+9Xi/ZbJbf/va3RKNRbr31VqLRKA6Hg+rqUoW5w+GgoqIUKr7xxhucfPLJrF69GpfLxY9+9CPGjBmDw+Hgd7/7Hfl8nmKxSDKZ5LbbbuOnP/0puq7z5ptvsmbNGo77zndJh2t5aXEze40P8d6qjYucrn7pS549fSamaXLe3xfbrz9251HcMneF/fLHa5I0tOZ57ezdWBnLMueNOsaH3Vz5tYm8uSLOQdtV8M6KOFe8+CUnzBjFD/Ybz5RKHy+esQuHP/gRprmhVb1NGPrViWGOmzGKherBHHvYLF599VUee+wx++3JZBLTNO2flfPPP59Zs2ahqirjx49nzpw5nH766ey7775ks1l+/etfo2kap512GhUVFdx4440AXH/99axYsYLddtvNfp0Q5cpEIVk1ldboFDLhWjKh8RgOd4fbqXoOb2IV3ngdweYlBBoXoyDPqYcTCTmFGKE6O1l5/fXXufLKK1m/fj377rsvp5xyCjfffDPnnXce11xzDU1NTdx1113MmzePI488khdffJGnnv4r3/rlX5g9fRSPfdxg3/8Ze43j+UVN/P69NTx3+kz+/NFaAF5fFuP4Rxe0O5Z4Vufg+//X7nXnPlNqR9qmwsMDx+5ILKPzzZ2qWJ3I8/2nPue+b+/APrUh3qnbePLe9mRlTNDFwb/7iO1e/CkXdHEyZnnhhRd44403uPDCC+3XvfLKK8yZM0dCTjGgTNPssMXdCoM23eJuhZ7W/4fCRuBy0tOlQ16vFyidsHfVLrl27VoqKyvtgOG5557j8ssvp7GxsUO7pKIoPPjgg+3CCqfTSUND6XHUNE1eeuklzjrrLH511Y95umUUj3y4lkMmR7ngH4vJF41O2yVjGZ3/fFHaZvyfL1qYNtrf45Dz9D3G4lAVHv2odCyXHbANv3lrJX/8YC2X7D+Bs/aq4d5362lI5jng3g+Z3fIvLj7/fLsNfVPW5/3rX/+aNWvWsGDBAnspihXiB4NBEokEra2tdmtrIBAgHo/36NhF/2lbNVcoFOx5Z50tNbEWmAD299r6vSjoOq3e0R1a1fccH+Kyfy3FNOGD+gTTR/tZ2pRm1qQIUJpt2JQuMKXSy+frUxSKJm+viHPLkaVq6rEht93225LRqfQ5adpQYdd2aUtbB0+O2s8j9p0YIYmLO+64g2w2y4033mjPJN10VML06dO57bbbME3TrkL+9NNP+fLLL5k2bRoLFrR/ntNXrMVQXS0+83g89t+FwdJZQFkoFHA6nUQiEYrFYpdhZlcMo1RZ7nA4WLlyJXfffTemadqvP+200+x/n3TSSZimiWmaxONxnnnmGZqaSpWVP/vZzzrc9yWXXALA//73P0477bR2b3vhxZdYePANGA43e00I8ZePNj7PfWNZjP3v+aDD/Z342MIOr/vJ81+0e3lVPIf7itc63O7x+et4vJPKzp+90P7931weJ3L5S0x75fIua9KeeuopjjzySP72t79x5513cuedd7Z7+x/+8Af+8Ic/AKWLUsVikV/84hcd7mf06NE0NTXR2tra4W1ClAPd6ae5Zi+aar9SGoNiFEsX0boYh2I43KQqJpGKTKRx24NwZlqorHuTaP17OApdL5UUQ4eEnEKMUJ2drOTzeftkpW2lWHV1NXV1dQDE43EikQjLli1jypQpFDwRIn4PjfXtZ9TtVxu2q33+vbSZfWvDvF/fyn7bhHnt7N14c3mMK1/8EoCAS+PVs3ZldSLH+X9fTEtm45PzY3cexdMbnvDtWxvmuQ0tQC8ubmK/bcLtQs62JytrW/OAk8KGzZPdPRmzWG3F0Wh0i5UHQvSnTQMCa4u79Z/H4+mwxX2wT3KHAqfT2W577OZYFUTW4pWu2iXd7lLFgHWfzc3NKIrCd7/7XX73u98BsHTpUn7xi18wc+ZMTjvtNO6//377/fP5PNXV1axfvx5FUSgWi9x+++1kgmN5+skn+NP/1trtkoDdLhl0a3a75Fsr4uwyNsCH9a3MHBdoV/3THQdPruDb06v5v4c/2fj5g93u3pQuMDrgxqEqFE0T04RUa6JD5VhLSwvBYNB++e233+btt9+mtraW733vewAsXLiQPfbYgw8//JAdd9yR22+/nVGjRrH33nvz4Ycfsu+++9oLNcaPHy/tkmVi7Nix7LvvvjzwwAO9en9Fc3RaXePUVKxfx3i2SNTn5I1lMXYc5Wf+xXuhoLDPb99nxhg/iezGizqaWjqRVduc0FoLXqyQs7OlLVBqcV/WXApGRwdcbFcV4P+d9wv232V7u5W3M51VIcPGBS39FXK6XC77YtimrIVDViV0b22ukrI7/+6MFTpaF5cMw8AwDHvxm/X2rv5taW1tJZfLdWsG5be//W2OOuooLrroot5/LTDxJlaRqpjEvF5UxPcbs3Rcm2u6ffPNN7t1V06nE4fD0eXPTUNDA7feemsvDlKI/mWisH7iLBqmHIGpqNiL7NRuzLZXFFBKtyt4IqydehQNU45g9JLnqV4+Vyo7hzgJOYUY4To7WXE4HJxzzjn2Ntu2V9eTySShUIgFCxZw/vnnc8Q3vkWDErIDTUuF10kiVzoJSWR1oj4naxI5pv7qv6QLBvcfswPfnl7NXxesZ/97P6A5rfO93cbwi69vy4X/2NiGPntaNbMfnr/hPjfdUOls9zHbnqxYRtfU9vpkbPXq1UyaNElCTlFWpMW9b/Rk6ZAVJFvbipPJJHfccQeVlZVcc8017W63aRvp888/z1lnncXixaV2xssvv5xx48bhcrm466672t12zpw53HLLLei6zhtvvMHatWs54YQTMFVHt9slH3xvNX84bidO3m0Mi9enee3LGADPnjaDmeOCTK3y8bt59fzxg7XccNgkvrFTFZqisF2ll0ufXco9s3cglinw0vd3IVswOOoPH3P3f1fx0Ak7cckBtehFk5P+spCxQRcPf2caRcPE11LBLb+8vsPXbcWKFYwaNYp169Zx2WWXMWXKFBKJhB0cPfTQQ1x33XX88Ic/5KmnniKXy/HGG29w0EEH8fvf/55FixYxf37p8X/vvffmr3/9a7e+X6L/+P1+rr/+eq6++uoeXUhxOBx4PB7cbjeK1vnpR6FooChgmhD2aDSnC5y6+xjeWhHn2peXccz0aq782kQe/nANIc/Gk9iiUXpcM9o8vm264OXw7Sv5xb83H5LHMgXeXhEji4N3332X008/vcvbdlaFPBDcbneXj+UejwfTNMnlcqiq2uugsjNW2Ng2fOxOOGn92xKNRjFNs9dfrw8++IAPPuhYPdmZv/71r33ymOGN15GKTLQDkbJgGvjidX1yV23HTAgxVGT9o1i584lkQhO2foGdogAKpqKydurRxMfMZMInj+FJ9ewisSgfEnIKMYJ1dbJy1VVX8cQTT9jVm1YVJJTaBxOJBKeeeiqPPvooj8/9iG/+eI7dvmiJZQuE3KWKo5DHQV1LlnzRJL9hbuczC9azd22Ivy5Yb5+IPPXJOv7fnhsHpW9T4SFdKLJ+QwVRxw2Vm39CFnRr3HzVj7j66iukqk0MW121uFvVGZ21uFuVniO5xd3hcHR7bp3P5yOTzZF1hcn7Klmyuolf3f07tEIWZzaGApxyyilEo1Hi8TgnnXSS/b6mafLPf/7TfvnnP/95h/u3ZgEvXLiwQ7Dy0ksvYaKw8OAb2GPb6i22S6YLBif8uWMV2dEPze/wuite/JIrNlTUW6b+6r8dbrcynuswUgTgoPs+RNVzXbZMPvroo8yePZv77ruv00qgxsZGzj333HavKxaLHb5GHo/H7iAQg0fTNG666Sbuu+8+VqxYscXbK4qCpmlUVFTgcDgoFotkMhmKRaPT27+/qpUDJ1Xw+rIYu9WE+MlzX7Dn+BCNqdKFg8Z0gbDHwZLGDDtU+3FqCnvUBPlkbakCbW1rnklRL+uSeSq8G6s4O1vaYlncmGZqVenx8b1VrVy8fy2m6mD77benvr6+w+0tnVUhQ2lBy6uvvrrFr82WdBVEOp1OCoUCgUCgw22sC9LWCI1NdRY+dreSsq8ukLW2thKJRHC73WWx/b07gs1LaNz2oME+jPZUjUBzx7n0PaVpGi6Xa8BCeiH6Qmz0DFbOOKm0QGhrA85NKQqZYA1L9ruUCfMfJdLQ8bmTKH8ScgoxQnV1snLWWWdRX1/PSy+9ZL+usbGR8ePH09zcTDgcJhaLARCLxVBM3W5fbOvtFXEOmRLlD++v4ZDJUc56+nMCLo1kvhSqfHVimM/Xp3FqCgqQL5rsPzHCF00b296PmV7N023ay6wNlW8si3HolCgPfbCm3cdse7KiqQqPnTiN3947p1snY50ZN26cnFSLIclqcbdIi3t7DocDRVE2+/laA+yzo3ZkRWQbWr2jMRxuRk+KEBtXxZL9SgtxrAH2ofQaiulVmOvftwO/vmiXhI0tk/PqXH3/hH5rbKFlcunSpSxdurSLt3ZfNpvlqquu2ur7EVvn8MMPZ/r06Zx55pmceeaZPPnkk7z00ksdlpr85je/4cgjj+TYY4+lpqaGW265hfPOO49x48Zx4403MnH8OF48I8pPnl/KmkSec/ap4ZqXl3HL3BX88fiduPbQSdz/bj1Z3eDPH63lL9+dzjE7j0JTFM58+jN0w+Q3b63k1bN2I6sbnP7EpwBc+eIXPHjcjmiqwjX/3hjeb7q0pa1XlrZw6u5jgdIohr8tXM+f7rgRtZiz5xOeeOKJHH300VRUVDBq1Ch+9rOfdVqFDLDddttx55139lvLd9vbWS3f1us9Hg+pVMoew9FVy/dgKhQKZLNZAoEA+Xy+bI5rcwKNi3FmWih4IuXx+GuaOLMxAo2Lt3zbLfD5fBSLxSETOAvRPG5PVk0/vvSC0vUc362iapimQd3M72EseJzo6vf75+OIfqPssssu5f/XRQjR54466iguu+wy+wT0ySef5OOPP+bZZ5/lo48+AmD+/PnceeedTJo0iSuuuAJN07jnnnt49913GTNmDNdddx2G6iARrOWkvyykMVXgntnbc+4zi/C7NB45YScq/U7+9VkTt8xdweFTo1x76CTSBYPlLRnOeOpzKn0Onj1tJqmCQV43OOOpz1gVLz3ZeuPc3Zn98Hx7FpymKvzumB2YFPXy0epWLvpn+6vYfpfGwyfsxDGPfMLJu47htqOn8MWihaiG3unJ2FtvvcWcOXM47LDDOOGEE6itrWXp0qWce+65qKrKbbfd1m4ZkRDDhVUR1LbN3Wq9bLvMaLi2uHs8HgKBQLt5mpZNB9grRrE062lzJ7emiWIamKrWbwPsV089msZtDujerKmBYhSpXvE6Yxc/O9hHIgaZqqp4PB48Hg+aptlzhLPZbIfHkLyngs9nXTlIR9rRjYdvx4PvrbaXFu0w93rcuVi3Q0nr/zvuuCMzZ87kqaee6vTjdNby3Z1/W/8PBAI4nU5aWlo63Hc4HEZRFPsidDlTVZWKigpyudxWzw8dKOsmHsTaqUf2X6jSE6bBmMX/YtTy17bqblRVJRqNkkql7FEsQpSz2OgZ1M08pfTCQFxwME3ApPbjR6Sic4iRkFMIsVWsNsrOlggMhrYnK5tro9ySQw89FNM0+fe//93nxyhEOWobeDocDjStFKZtOtez7fiKoSoYDKJpWrtAoNMB9r15Er3hSbFiGn06wL61anuW7X7WVt9PX9v2g/sJNi4a7MMQg8TtduPxeHA6nfY8yGw2u8Uq6XJ63tCWVsxzwEdz2i0xaqsn4WRft3xHo1FyuVyHzeqaphGNRkkkEkOmIs/r9eL3+4nFYkOig0B3+vnswKsx1cFvglQMnR1fuwZHIb3lG2+G3+/H4/HQ3Nw8LC9miuEl6x/Fkv0u3XDReQAvNpgGimkw5a1b8aQ7Lq8V5WnwH6mFEENa282T5dDGc/kLX5T+0Y3Nk5vTtl1fiJHAanG3Kjo2bXH3er0Aw6LF3eFwtJthOhQG2AebluDKxsi7w2XxWNuXLZNiaNE0za7aVFWVQqFgb73ujnJ73mAzTXyt9aSSyS1u+R5o1oWnTRebQaky3VpIN1RkMhncbjeBQGBIVJ86CilGL3metVOPHtyfWdNkzJLntzrgtMYbdFZpLUS5MVFYufOJG2ZwDnA1taJimiYrdz6Rye/eKVvXhwgJOYUQW224b54UYiTqbIt720rPtlvc2wae5d7ibi1DSadLJ4nlPsBeVVV8Ph8ej4cJjR/yRc2B0OvLN33JpLLuTXnCP0IoitKuatMwDLsdvasFZlZbd2dt3pFsA2lzImaZPW/wtCzv9kKygeRyuewLTJuywqqhJplMEolEhszxVy+fS3zMTDLBmsEZG2IU8SZWUbV87lbflTWXW9rUxVCwfuKsvrkI3VuqRiZcS+PEWVRv5ZgIMTAk5BRCbLVy3TxZnanH5XJ1WvkghOgZ0zTJ5/Ptfp/atrh7vV5UVbU39pZri3vbpUPlPMDe4XDg9Xpxu92Ypkk6ncaz5DWUcQdsaKcfXIppEK2fN9iHIfqZy+XC4/HgcrkA7GpvwzBQFMX+ve8szNzcAp2q1Erq1f0G8lPZsj7aWN0funou43a7UVV1SISEm7Lmtvr9fvL5fFn9neiMgsmETx4rtcyaxsC3zG74+H1xYcnr9ZLL5cr+ay6E7vTTMOWIwa/6VxTWTjmCivp5W11JLfqfhJxCiK1WjpsnXbk4VcnlOMPhdhVpnVVBCCF6p7st7sVisd1sz8FscXc4HBiGQVPVNFZNP6H0yv5+3FJUME1WTT8BtZjfbEWn0+nE5/PhcrkoFoskk0my2SyaplEZDLLd6tdZWnPQsGiZFP2vN9u9N7fl26rk7u4cSk3TcDgcuFwuFEUhn8+Ty+Uwm97HOf7QsnreUK7jF6zH1c6q7rxeL/l8vstq2nKXSqVwu934/X5aW1sH+3C2yJNax4T5j1I383ul+csDtvwEJsx/tE9mArrdbjRNIx6Pb/V9CdHfmmv2KosLuwCmotJcs9dWL/0S/U9CTiHEVlMwqax7q7R5skzaKKMr3iDW0oKmaXaLndfrpVgs2oHnUJwlKEQ521yLu9Pp7LTF3Qo9B6rF3el0knCEWTnjJMAcuGocRQHTZOWMk/C8tabDyarb7cbr9eJ0OikUCvYSEUVRCIVCdkg0oWEeayPbk/SPGZwRIX3YMim6p7Pwsbv/7sqmm7ut/9qGm1bltjWCwrr9ljidTtxut11lqOs6qVSqQ+VYuT1vKNfxCy6Xy66kb0vTNJxO55AOq0zTJJlMEgqFyGazQ+JCdKRhPsaCJ0pdAGY//w0xS78v4xc83mfbnYd6MC5GDhOFptqvUB5/IwAUmmq/2mcLJUX/kZBTCNEnovXzaJhyeFlcbWvbRlksFkmn06TTaRwOh33i5fP57MBzczPFhBC9150Wd7/fP6At7prDyefbfatsBth7PB58Pp+9VCQWi1EoFHC5XEQiERyO0lM167GsWCyy47J/8v60MzEZ2i2TI0VvA8ottXxvWj1pGAbFYrFbFZaWzpYIJZPJUrVlDy48tP37qmkaxWKRTCZDLpfr8u9ruT1vqFrTvVESA83lcnU669i6cDvUR/Lkcjny+TyBQICWlpbBPpxuia5+D7WYK81zNs3+mdFpFEuPt72c59wZ62/vUFj2JESyaioFb8VgH8ZGikLBW0GyairBxkWDfTRiMyTkFEL0iaGwedJqrU2lUnaliRUw6LpuB54yo0iI/rNpi7tVjWSFn21b3NsuM+qLymtFUagfuw+pQM2gD7CPTzmE7Zo/RFEUcrkciUTC3ngbCoXsKjxd12ltbUXXdTv41NPr2eaTP7N8xklDtmVyKOltOGn9uzNbCiG39O+tZQWb3V0i1BlVVfF4PLjdbnsMhPV3tDu/r+X0vGFS/VxGBT2kNcNeSlYuXC4XqVSqw+vdbvewWRyTTCapqKjA5/OV3de/K5GG+XjeXsvKnU/s+6Uopok3sYoJnzzWp4+3Xq/X/psqRH8JBALcc889TJo0iVNOOYUvvvgCgOOPP55vfOMbADz00EO88sorVFZWct111+H1ennyySd57rnnqK2t5ZZbbmGbbScx6to3SG3y58TnVHn4hJ2oDrj456eN3Pp6adHsrEkRrvjaRFRF4a63V/K3hY3cePh2fG+3MTz2UQM/fm4pANtX+/jdMTtQNGF5c4bTn/wMgF8evh37bhNmRUuWM576DN1o/7f2nH1qWLwuSTy5J/fe/GN7Xvrll19OMpnk17/+NYFAAFVVmTx5MrNmzbLf94orriASifCjH/2I0aNHc/LJJzNnzpx++foLCTmFEH1oKG2etKrFkskkLpfLru70+/0UCgW75VYCTyH6V7FYbBesbNri7na7+67F3Rfmi3EHDP4MQEVh5cRDGL3+I4xUDJfLRSgUsivgoNROnEql7IUi1uNTNpultbWVEB8xXnUO2ZbJgbY1lZRdBZWdhY/dqaQcrL8rDofDDiUVRWk3FqG7rCDe7XbbAWk+nyeZTPYqOCmH5w3B9Fr8n71AxufF5/Ph8Xjs9vrB1naWaVvWduyhuHCoM1blr8/nG1IXmz2pdUx+5zc0TpzF2ilHbKhKVnr3N8Y0ARPFNBiz5Hmq+rgl1hrflEgk+uw+hehMNpvlggsu4KKLLmr3+uOPP54TTjgBp9PJ73//e1555RVOO+00/vjHP/L+++/z4IMP8vLLL7Nu3Tq+//3vc+MDj294btP+8eCMvcbx/KImfv/eGp47fSZ//mgtzWmdS/av5ag/fEyhuPH35o43V/LS4iaO3KHKft25+9Rw/SvLeWlJM787Zgf2rQ2RKhjUhN0ceN+H/OygbTh251H85eOGdh/3mztWceR/V1HjqOLCCy8kmUxyzDHHMHv2bB555BEuvvhiAHbffXc7zAUYO3Ys1dXV9t/IhoYGqqqqCAQCJJPJvviSi01IyCmE6DNDdfOk1U7b2tpqt9v5/f4OgedAzQwUYiTrzxb3ljIbYN80dg+2bXzfDooMw8DpdJLNZkkmk/ZjTjAYtIOXtlVOQ7VlsjcGsuW7u1WVQ4EVSno8HhwOhx0m9SRIUhTF3rLudDqB0t/NeDy+1a3S5fC8YdqKf5HXVFKpFJlMBr/fTygUsi+EDub8bmsB2aYVth6PZ0hsJO8JawlRMBgcUnNGFUyql79GRf17NNfsSVPtV0sttkax9PO8ucDTNEsXkVQNZzZGVd2b/ba9ue1ceiH6k67rnY6eWLVqld1FZy0amz59OrfddhumafLpp58yefJkPv30U0wUDIe709+f/WrDdlXmv5c2s29tmOaMTqZQ5B+nziBdMPjBM4toSOZpSObZYZSv3ft/ti5FxFuKwUJuB80ZnYMmVfDvJc0AvLi4mdN2H9su5Nx5jJ8vmzOgKKxxjCKaTKJAp6NEvv71r/Pvf//bfvm0007jkUce4Tvf+Y79ug8//JD99tuPl156qSdfWtFNEnIKIfrUUN88aQWabU/qAoEAgUDA3gqbz+eHzAmuEMNBVy3unW1xb1vp2TacMFFoGLcP5TTAftXoPYksfQWX04HP58MwDHsmJ5TCpXA4jMPh6DJQGiotk22rInuy3bs7Ld+bho/FYnGLlZTD/THc+vvlcrmA0t+2nlZbbnofvZ3XuSWD/bzBnV+Hw+cjkUhgGAatra1kMhkCgQAVFRX2124wAkWXy9UhlLIu+gylILC7kskk4XAYl8s15GaNOgopRi1/jerlc0lWTaU1OoVMuJZMaHwprNmEqufwJlbhi9cRaF5CoHFxv806tkZLdDb2QIiB8uabb/LXv/4VVVW59tprgdLjmfX3pLW1lVAoBEDBE+nyonSF10kiV7rwk8jqRH1OnJrKdpU+9rv7fQ6ZHOXnh2zLD/7W+dzMl5Y08+IZu/LzQ7blf6tbWbQ+zexp1axpLT3WxrM6UV/7mGyH6g0hJ2A43BQ8FUQdBY477jh++MMf2rdTFIU999yTW2+9FYCamhoAVq9e3e7+6uvrmTFjRje/cqKnJOQUQvS5ob55Ekonz20DT+vKYygUsivNstnskHsSLsRwYFU2Wa2aXbW4G4Zhzx9rDG5Lzh0e5CNvQ1HIu8MUanYmklxBJpNpdwKqaRrhcOl4Y7HYZqvJBqplspxavodSNeVAsYIMj8eDpmn2DOpsNtvtr9Wmm9ELhUKnm9H72mA+b8hsuJipqqr9Oeq6TiwWszs7otEomUyGdDo9YD93mqbZC8na8ng8w2LhUGesi8nWEqKh+DuuYBJsXGQvJjFRKHgiFB0eTNWBYuhoehZnNjZgC9w8Hg+maQ6b8QZi6PH7/Rx33HF861vfwul0ct999/Hmm2+i67o9kigYDNrjFIpOT5f3FcsWCLk14lmdkMdBXUuWVL7I2ytiFIomryxt5icHbtPl+99w2Hac+fRnzP0yxm++OZXDp0aJbbgvgLDHQXN68xX8itvPjdf9mDlz5rQbAbHbbrsxf/58+znb6aefzoMPPtjtr5PoGxJyCiH6xXBqo7SeGGazWVRVtU8Aw+Gw3WaazWZlkLsQ/aSzIfZut5u77roLwG7FPfHEE6msrOTaa6/F5/Pxt7/9jVdffRWv18uh3zqWe7++C4qqcMULX/BO3cYnpaoC9x+zA5MrfXxY38olzy4B4N9n7spu4wKc8vin/OvzJgC+M3M0539lPFnd4Ly/LeKzdWlGB1z88fid8LlU7nunnkc/auD/plVx/lcmADAp6uW2N+q4861V7T6vc/ceS3PkQJJvP8Evf/lLAEKhEKtXr+aGG25ghx124Oyzz6ZYLPLBBx9w5513Mn36dHbbbTcefvjhdvfVVcukYhRLoecWWiYV08BUNVz5ODVr32NM48e4ilnUyugWW7670949lFu+y511Ec7lcvV4+Q9s3IxubVi3qqY3txm9PwzW84ZsNovf78fr9XaodLMudvp8vnbzOgciLLK+n22fW1jjB4bKcp7eSCaTRKNRfD7fsKg8VDBxZQd3a7zX6+3RxQ4htsR6TtD2QuamFzU1TbMXvHq9XgqFAi6Xy54PHYlEWLp0KQcddBDz589n55135pFHHqG6upqEr6rLj/32ijiHTInyh/fXcMjkKGc9/TlZ3eDi/WsB2GVckGXNXS9lU4DGVOlxtTFdIOxx8N8VcS7efwKPfLiWQ6dEeXtF+0r5RY1pZk2K2C9f85ML+fe//81HH33U7naHHHJIu1b1cePGcfnll+PxeNhmm22YPXs2zzzzDDU1NSxbtmyLX2fROxJyCiH6zVBpo+wJwzDIZDJkMhl7iLvb7SYSidgnl7lcTgJPIfpQZ0Psc7kcZ555JgDf+MY3GDt2LECHIfbPPfccbrebWXvM4JDf/a/Tx6Gjd6xidSLP95/6nPu+vQP71IZ4py7BKX9ZyJl719i3UxW49IBa9v3t+4wKOLnrW9vz7Uc+4ccH1vKr11fw2pcx5p69G08tWM/fFjbyt4WNALx85q78fcO/2/rGTqM47vkM261caX8u5513Hs3NzeRyOY499liuvPJKVqxYwb333kt1dTWffvopZ511Fn/5y1+AzmdVRps/ZErsIxLRqbQEtyHuH0vSN5ai5upwDFoxTyC9hlByNeH4MiKxpbAhiMx3I8AUg6PtEiFVVcnn8z1aImT9/bKqPq3K6FwuN6gzKAfreYO19KarSs10Om2HocFgEK/X2+tlS93ldrs73L/bXWp7Hs4VedbSNWvR2kAG7cORtaTKGvciRoYthZBb+/Ytuf7669luu+3YdtttefbZZ3nhhRd48803uffee1EUhWeeeYZiscif/vQnrrjiCk477TSefvppWlpaCAQC3H7VpWw/NsDfT53BrXNX8MLiZu6ZvT3nPrOI37+3hkdO2InT9hjLvz5roj5R+rv3t4Xr+c/Zu2GaJt9/6nMAzv/KeL636xiq/C5qQm5O+stCbp67gntmb49umMQyOje/toKcbtDQmue1s3djZSzLnDfq2n0+89ck2a6yNBrpqxPDHHHgNLapjvDNb36TV199lcceewxFUdhjjz3sVnWAH/zgB0Bp+dAll1zCM888A5QqPm+88cZef3/F5knIKYToV0Np82RPFYtF0uk06XTavlrpdrvbDXcf7BNGIYaDrobYW77+9a/z61//Guh8iP3kKVNJK25e/P6urG3N84O/LSKV33jivG9tmOcWlSo1X1zcxH7bhHmnLsGa1vbtoFV+J6sTOXTDZHUiz/bVpWH2e44Pcdm/lmKa8EF9gumj/XxQXxqqPzrgwu1QqYu1DyWsIfaZ0HjcHg/ahirxAw44gEsuuQRVVamvr6empoZ0Oo3H48Hv9wOwZs0a9t57bz7/vPQkvrPwUVNVKhNf4mtYyFjDoGiY5FwhdM1NUVFRijpqIYMj09LucVT27pYva3SK1+u1lwhls1kymUy3Aue2nQjWZvRyvDA3GM8brJDT4/F0GQZtOq8zEomQy+VIpVJ9HsQpioLD4bCXc1i8Xu+wWzjUmUwmg8fjIRgMEovFBvtwhjSfz9fv4yZEz/VF0Li5t3dH29Evm46B6ept3X372Wef3eHj3Xvvvdx7773tXtfa2spZZ53V7nWZTIYzzruYz2dd2e715z5TGgGRyhf59iOfdLj/e96p55536tu97s63VnXoovlodZID7v2ww/v/5PkvOn6R2vjHp418bbsKXv2ihT2/dmSHCm3TNDnuuOM6fd81a9bwox/9CIDRo0fT1NTU4fFd9B0JOYUQ/W6obJ7cGsVikVQqRSqVsitsrBYNXdftE0mpSBCibwUCASorK+22n86G2EdGj6PS7+aw33/EOfvU8MN9x3PL3BX2fVR4HSSypYsR8Q1D7DuzPlVgfNhNyK0xPuxhcqUXh6rg1FRrjwnxbLHd+8+eXs1fF6zrcF/WEHvD4cYRHYc3n6C2tpbGxkZaW1sxDIPXXnuN66+/nkKhwMsvv8yaNWswDIMvvviCyspKGhsbO606s4KsTZcVKbksTqDzz06UK6fTaV9Eg9LcwlQq1a2ZjFYwagWb1vv3xWb0/jTQzxussTRer3eLFW/WvE6Xy2UvJ8pms6RSqT5rB3a5XCiK0u575HA4cDgcJJPJPvkY5S6ZTBKJRPB4PMO6crU/uVwuNE1rNzNQdE9fVjz2dQjZ1YzqngaR5cyZjaHquU6Xdg0WK0BV9RzOrRhB0dDQ0K7aU/Q9CTmFEAOmnDdP9iVd10kmkySTSXuJg9frxe/3o+u63RIoV9WF2HoHHXQQc+fOtV/ubIh9PJ3jP1+WnpC+urSFK742sd19dBw433lVm2nC5S98wTOnzqCuJcu7KxPohkmhaKAopbeHPVq79z9m51Gc8eRnm/0cCooLt2Gw11578dxzz9lbk3/wgx9wxhlnUF9fz5w5c6ipqeHLL7/EMIwuN4SrqkogEJDFaEPc1i4RsoLNtpvRW1tbyefzQ+IE1zKQzxsymQxerxe3292ttv98Pk9zczNerxefz4fb7SadTvdJW7DL5aJQKLT7Xnm9XnuR2khQKBTIZDL4/X5yudyQ+rktFz6fj3w+Pyw7ivq69bqnIeSWgkTrOX5vKyJHOoXSeJFUxaS+HVuytTaMPSmjIxKdkJBTCDHgynHzZH8pFAoUCgWSySQul8ve1BoIBCgUCnbgKU9ohOidQw45xG5VB1i4cCF77LEHH374ITvuuCO33347SW8Vxx10LAC7jAvw5SYD6f+7Is7Bkyt4Y1mMQ6dEeeiDNV1+vBcXN/Pi4mamVHm5+KulIffvr2rlwEkVvL4sxm41IX7yXKnlaVTAiVtTOrSqQ/sh9kUUYrEY+++/f7sWL9M0SSQSmKZJa2ur3a5eU1PDq6++2unxhUIhTNMcMdVew43L5cLr9dpVl9bSu+6EFNbfGLfbjaIo9mb04bBwZCCeN1gby71eb7dnm0IpHLXmdfr9fns50dZcZHC5XO3CUqsidzgs4umJVCqF2+0mEAhIa2cPOZ1OnE6nfdFsoPVVxWNXb9+SLQWJbS8USghZnrzxOlKRiaD0wxK63jINfPG6Ld9ODCoJOYUQg64cNk8OhHw+Tz6fp7W11T4RDQQCduBptbTLkychOrrzzjvZfvvtmThxIk899RT//Oc/CQQCVFVVsXz5cvt2Dz30ENdddx0//OEPeeqpp8jlcixfXsfKeJZXztqVvG5w8l8+BbCH2D/7eRPfmlbNa2fvxkerW+3N6w8cuwOztq3gWztVMW10gFvmruC2o6ew89gALemCPR/qlrkr+OPxO3HtoZO4/916snqpguPb00bx1wWdL0ZrO8Q+GY+x0zbbsH79+nbh5P33389dd92FrussX76cTz4pzaDabrvtuPPOOzvcp8/nw+FwEIvF5HFkCLFmOlvbza0LY935e7DpAiJd1+0lOcO5W6C/njek02kikQgOh6NH1W/WhQVrXmc4HCafz5NMJns8psbpdKKqarug1ePxAMN74VBnTNMklUoRDAbJZrMjpoq1L1iVv12F7X3Zet3Z27dkS2Fi21bszt6+pbeJoS/YvITGbQ8a7MNoT9UINC8Z7KMQW6Dssssu8igghBCDpKt5aVbgKYToPru6y+nBVBwopo5WyOL1B/jfHhcN9uF1cO4+NSxan6b5z1fgSDeh6/oWg6np06ez22678fDDD7d7vcPhIBKJ2MvQRHmzHvs9Ho+9BMiq2txSKNZ20Z21GT2Xy8km6j5SUVFBsVjcqjmGLpcLv9+Ppmk9ntfp9/txu900Nze3OyZd10dsNWMkEkFRlM0uoBtutiZoVFUVl8tlB/Wdvf+W9LSqsadt2UJsiYnC5wdcQcETKY+WddPEmY2xw+s3DPlOw+FOKjmFEGIQWcsOstlsu823VsupFXbKbD0hOjJRujWnTynmS4tIFHUQjrJr97xTj1rMcYCWRwuHgdIG52KxiK7r9v91XbdPChcsWMCCBQs63FcwGLSr+ET56myJUHeWAFl/HzweDw6Ho2w3ow8HVjWmqqq9roa1Ojc8Ho8dWmYymW79frpcrnY/D06ns9NN6yNJa2srFRUV3VoMNVD6YyP21i6lsV6vaRqmadpzXXsTRAox2BRMKuveYu3UI6EspmCaVNa9KQHnECAhpxBClAnDMMhkMmQymXYntB6PR05ohWhDd/pprtmLptqvdGvjsqm5NmxcNsujGsBimnjjq2huakJVVRwOB5qm2VuUPR6PfcJrGEaH4LNYLGKaJoFAAE3TRlSV01CyaUBZLBa71VK+abWndeFra+c9is2z5mt6vd6tnoFpzd32+Xz4fD57XmdXnRrW73/bj2u1HQ/H5THdVSwW2y0h2lL43FdBY3+FkJu2Y/cmiNzc515ZWSlV/WJYiNbPo2HK4ZhlcJFaMQ2i9fMG+zBEN0jIKYQQZaht4Klpmn2i6/V67cCzu8sohBguTBTWT5xFw5QjNjzh3XCyqXZjKH05hZuWNgPsDcPoNLjSNM0OPjRNsysB24afqqqSz+dxOBwoiiKPC2XC5XLh8XjsDee5XI5kMrnFC1WbbkbP5/MkEgkZYTKAMpkMPp+PdDq91VVt1lxJKzwNhUL23NVNf1ddLhemadqPBVbb8XBbJLY1y2YikQjFYnGz798dfdl63ZMQsr95vaVZz+VS8SrE1nAUUoxe8jxrpx49uM/jTJMxS57HUZALB0OBhJxCCFHmrKqfdDqNw+GwT4C9Xq/MYhMjRtY/ipU7n0gmNKE8A8ve6MYA+2KxaG99bssKPP1+P4ZhoGkaoVAIKJ1kW+/XtvpTHiP6X2+XCHW2Gb27y4dE37NCTo/H02dhkTXn0+l0EggEqKiosOd1WpWJm7aqWwuHBjrg7suKx74MIXVdt2fYWpXsvQ0phyurpX84f45iZKlePpf4mJlkgjXdu6jd14wi3sQqqpbPHfiPLXpFQk4hhBhCrJa1VCqF0+m0Kzx9Ph+6rtst7RJmiOEkNnoGK2echIkyfALODQPsA42Le/XuxWIRv98PQEtLC4ZhoChKu6pPh8OB1+tFVdUNH9Ls0O7enWVHYsusC09WAJPL5chkMpt9LLYew9tuRrdameV7Mrisedn9MQOyUCjQ0tLSbl5nOp0mk8ngdDrbVW16PB6y2WyHwKqvW6/7shJyc+3YPQkiuxIKhXA4HMRisW4d50hiVflLFacYThRMJnzyGEv2uxRzoOerm4b98WUW59Ah29WFEGIYsKqAXC6XXT1kBZ5ysiyGsuZxe7Jq+vGlF8pgJlOfMQ3GLP4Xo5a/1qt3txaUdWdpjaIoHeZ9appmh59bWnYkOmfNTbVCynw+b89g3Nz7WMGmbEYvb5qmEY1GezUqoCdBo9PpxOl02m9v26puzXDd9H23ZGvbrrvTlj1YVFUlGo2SyWS2embqcFNRUYGu6yN6SZUYvmKjZ1A383swUBe8TRMwqf34ESIN8/v/44k+I5WcQggxDFibXGHjHDi/308gECCfz9uB52CfnAjRE7HRM1g1/YTSC8OlgnODrRlgr6oqgUCATCbTrQU01pbdTWdBbs2yo5FKURS7Hd0KoDKZzGaXCKmqaoehbTejy1zlwdPdqkZd1/H7/e1CyO6875ZsGiQWi0U0rdSG6XA40HUdRVHsn5WhFEL2N8MwSKfT+Hw+uTjQhsvlwuFwSMAphq1Iw3yMBU+ULnybZv9e+DZLf8/HL3hcAs4hSEJOIYQYZqzAU1EUO/AMBAIEAgEKhQLZbJZ8Pj/sT4TE0Jb1j2LljJOAfn4iOxi2coB9KBSyl5lsjd4uO2o777NtCDqc9XSJkBWGut3udpvRu7N4SPR8xmNP374lbQND63egq1bsTW/fnSCyM5WVlWSzWRwOh72AKJVKSetxJ9LpNG63m0AgQDweH+zDKQs+n498Pj/sH4vFyBZd/R5qMVcaYWSa/TOj0yiWWtTnPyoB5xAlIacQQgxT1kl1LpdDURS7RdIKSKzWyu5UggkxkEwUVu584oYZnMMs4NzKAfY+n8+eR9dfFyo2t+yobbu72+3G5/MBw3PZkVWB6fF40DTNnpnZ2YxEoN2FJavyb7huRu+LoHFzb9+SLYWJ3Q0hu3p7WxUVFfbSoP7icDhQVdWu7g2FQrhcLvx+P6qq9smW9+EmmUwSiURwu93D7verpxwOB06nUwJfMSJEGubjeXtt/yyjNE28iVVM+OQxPOn1fXe/YkBJyCmEECOAtUQhm82iqqodeIbDYbuaK5fLSeApysL6ibOG1xZ1y1YOsHc4HPh8PtLp9KBU61gh5qaBQtvg02p5t1pvh+KyI2uhm8vl6lZr+aZVnoO9Gb0vKx77cjN229dvupSmq/fp6u0DKZPJEAgEUFW1335u3W63PR4CSkuprBEI1pZ3K2AXJVZnijWWZySHwNbySXkOJ0YKT2odk9/5DY0TZ7F2yhGYikqvZ3VumL2pmAZjljxP1fK5smRoiJOQUwghRhjDMMhkMmQyGbsayzqpt07oc7mctFSKQaE7/TRMOWIYBpylJ8wT5j/a6+qAYDCIruuk071rc+8vVoDZVmfLjqzFaFAKrDYNPgdz2VFnS4Q2V4G56Wb0QqHQ7c3ovd14PZAh5Na+fTjJZrP4/X58Pl+7zed9yeVytZurraoqmUyGYrFINpvF5/MRCATwer2kUikJszZIJpNEo1H8fn+/fW/KnaqquFyuEfv5i5FLwaR6+WtU1L9Hc82eNNV+lYK3AoxiqQtoc38PTbM0d1PVcGZjVNW9SUX9vF6PERLlRUJOIYQYwYrFIul0mnQ6jaZp9km+1+u1K7ZyuZzMeBIDprlmrw1X5IeRPhhgHwgE0DSNlpaWvjyyfrO5ZUdtg8/BXHZkjfGw2sutQKltO7qmaXZ4aLWEOp1Ou6rPqko1TdN++5aCyO7oi6Bxc28XPZPJZPD5fKRSqT7/+lkLwKwZu16vl0KhYI96MAyDZDJph63hcJh8Pk8ymRzS4yD6gjW3NBgMjthFXj6fz+7WEWIkchRSjFr+GtXL55KsmkprdAqZcC2Z0HgMh7vD7VU9hzexCl+8jkDzEgKNi6Vyc5iRkFMIIQRQCjxTqRSpVAqHw2Gf/Pt8PvvkP5fLjfiTKtF/TBSaar8CDKMqzj4YYO9yufB6vbS2tg753z/DMDAMo0P42ZNlR9a/rYrJnlY8WkFr28DRMAxUVcXn89lzRjenbQiqqmqHMHHTduyeBpGivFghp8fj6fNFQNaSoUKhgKZpuFyuTud/6rpOPB63Z3VWVFSQzWb7JXgdSrLZLB6Ph2AwOGQuAvUVa8FZuVX3CzEYFEyCjYsINi4CSs8pC54IRYcHU3WgGDqansWZjUmoOcxJyCmEEKIDq4IqlUrZbZlerxe/34+u6/aMunKeqyeGnmTV1FKr0XDRBwPsFUUhGAzav3NDQV+0XluzPKFU6Wb9Z8293JyugkRFUexw06rCLBQKnd7Oqsp0OBwyt1jYlXJer7fPQ063223/HLYdG9OVfD5PPp/H6/Xi8/lwu92k0+kRvYXdWkLUH9+fcub1egFG1OcsRHcpmLiyI+vChyiRkFMIIcRmWS2nyWQSl8tlb1T2+/0UCgW7pV0Cz5EpEAhwzz33MGnSJE455RS++OILAEaNGsXll1+Oz+fjww8/5N5776WyspLrrrsOr9fLk08+yXPPPYfb7ebmm28mEAiQdkU4/plVrEtvrFZUFbj/mB2YXOnjw/pWLnl2CQCxaw7gg/pWAC78+2IWNKR45pSdCXkcqIrCzmP8VF3zBufsXcNxM0cBsNMoP2f/9XP+8Wkj00b7ufWoyTg1lafmr+Ped+vbfV7TRvs5ZudRXPvyMm48fDu+t9sYHvuogR8/txSAnx+yLQdMigCw27ggs+77EJ9T5YbDtgNgXMjF3P++z29/fifXXXsNN954Y69CymAwCEBra2uP37crvZnx2JOQcku2VNFoVUF29XbTNO0KSqsq03rZYlV9wsat8AC5XI5MJtPpDNG2retQCpNk/qGwZDIZvF5vn2/zdjqddqu6x+Pp9uNEJpOxW9j9fj9er5dkMjkif151Xbdnl46k5yNer7fdeA0hhBCg7LLLLvKoKIQQosespRttNwpbgac84R45HA4HwWCQiy66iIcfftgOOX/5y19y2223sX79xgrGSy+9lDfffJP333+fBx98kDPPPJOvfvWrTJs2jTvvvJM9zr+NUeMmcNNrdfb7fHOnKvYYH+Lql77kvm/vwB/eX807dQnePW8P9r7r/U6PadakCKfsNpYznvqs3es/vmgv9vnt+2QKBn8/dQYn/2UhrbnO279/d8wO/OLfy6iPZxkdcLHjaD9H7lBlh5wWr1PlnR/uwczb/lsaYJ9poaruTX57xtf559/+ygcffMCBBx5IZWUlTz/9dLe+plZY6Ha7CQQCpFIpO5TriyByS/pqBmRXb+svbSswXS4XDoej3efb2bIjqz247eNYNpsd8duaRefC4TCKohCLxfrk/lwuF+FwmKamJpxOJ6FQiObm5h6PpdA0Db/fj9vttsP5kTafUlEUKioq0HW903b/4cbj8RAIBGhpaRnyY0yEEKIvSSWnEEKIXrECTSuMsQKZQCBgt3b2ZbWLKE+6rneYg+ZwOBg3bhyXXnopFRUV3H333Xz88cdMnz6d2267DdM0+fTTT5k8eTIrV65kjz32wEQhEB1NU7r9ifm+tWGeW9QEwIuLm9hvmzDv1CXYrtLLf87ejU8bklzy7FJy+sbKnWN3HsWT8xva3c8e44N82pAiUzDYNurBqSr86TvTcGkKF/1zCYvWt59pNqXKR30iB4pCQzLHTlUdh9cDHD0lzMsff8HoVW8SavmScMtSXE4HM6ZdwK9u+hy/38/ChQu55ppreOmll7oVRG7K7/e3e3lLYWLbeZCd3X5LQeRQtGklpmEYdqWbVflpLTradN6n9TWzqtYlMBBdSafTRCIRnE5nh7myveFyuezlVV6vl3w+36ufv2KxSCKRwOl0EggEiEQi5HI5UqnUiKlqtJYQhUKhdtvqh6ut+XkRQojhTEJOIYQQW8WaVZbNZtsFDaFQCNM07bBzuJ9wiI0ikQhTp07lJz/5CYVCgTvuuIOTTz4Zh8Nhh2itra2EQiH+97//MWnSJJ56+q/k/dXs89v21ZkVXgeJbCn4jGd1or5SK/HUX/2X5rTOlQdP5Af71vDrN1YCoChw4KQKLv7nknb3c+zOo3jyk3UAjA642HlsgGlz3mFCxMOcoyZz9EMblwJV+532xyzdqcoOK55l3Og92OuzxzEVDcUs4ijmOPeI83niiSeY3rjh44WC7Lnnnnz88ce43W57oUgkErEX1Gy6lAY6Bo1+vx9FUYjH4x3eLjZyOBx2CzGUKjHj8XiHxxurrd3lcqGqKrquk8/n7WpOKwR1u92dLjtqW/0pRq5CoYCu6/YG9K3ldrvJZrP2si3r931rjq+lpQWPx2NXdqbT6RGzmMZ6rhEIBGhubh7sw+k3VqV6X44xEUKI4UJCTiGEEH2mbeCpqqodeFrLFPL5PNlstk9ODkX5am1tZeXKlaxduxbADpJ0XbeXygSDQRKJBN/4xjf43//+x+1//gczzrqZK782kZ+98IV9X7GsTshTeroS9jhoTpd+dpo3VHw+/ck6fnLgNvbt958Y4d2VcXSjfRh4+PaV/OLfy0r3mdH5sL6VRK7IwoYUVf4tL7PJFgzMbBKz4YtSUGmaaC4X48aN45133gE2BpF77bUX//jHP9qdZBeLxW4HGD6fD03TiMViUqXTCWujsMfjweFwUCwWSafTHZahaZpmPwZpmkaxWCSbzZLL5TYbVrbd8m4Fn9bGdWshUttN79b/xciQyWQIBAL2z1RvORwOVFW1lwhZfyP7gvVz7vP57K3wqVRqRHRXJJNJKioq8Pl8wzbctUJ2uegihBAdqVu+iRBCCNFzVrtoS0sLzc3NZDIZHA4HkUiEyspKAoGAvQxEDC+5XI54PE4gELDbh4vFIgsXLmSPPfZA0zR23HFHvvjiC3u+nak4aEwXCHva/0z8d0WcgyeXNq4fOiXK2yvi+Jwq6oau7v0nRljatHGzbKlVfV27+9i9ptSqnt3Q0r6kKUOlz4lDVagJuYln258ork8VCHvbH0dOL23jtiqFCoUC++67L2+++SaGYdjVmQ6Hg2nTpvG///3Pfl+v19uhpb8rDofDPjmXE9j2XC4XoVCIyspK/H4/uq4Ti8Vobm4mnU5jGAaqquL1eqmoqCAajdotndbtujOrsFgsksvlSKfTJBIJmpubWb9+PS0tLfZiFytoDYVCRKNRqqqqqKioIBgM4vP57IpRMfxYIxCszda95XK57DEJbre7zzdkW+3bzc3N6LpOKBQiEokM+7+71kUP62LRcONwOHC5XMM2wBVCiK01vP/KCSGEKAvWSUc6nbYro9xuN16v1w4UtlRdJcrXnXfeyfbbb8/EiRN56qmn+Oc//8ldd93FHXfcgdPp5N577wXgoYce4rrrruOHP/whTz31FLlcjueff56bbrqJrx12JLlILWc+XVoWdM/s7Tn3mUU8+3kT35pWzWtn78ZHq1t5py7BzLEBHjh2R5L5IrFMgVMf/xQotarPmhTh4mc7aVVvE3wWDZM5r9fxylm7oikKF/9zcYfPaUljmpqQm/pEjvO/Mp4zp55ONBRg1KhR/OxnPwPgkEMO4aGHHmr3fnvvvTfvvfdeu7byvffemzfeeKNbX8tgMIiu63ICu4GmaXg8HtxuN5qmUSgUSCaT7RacbTqP0xqT0deb0a229basZUdtZ362DTg7W3ZULBZHzJzE4SqTyeDz+UilUr0eIWHNjbTmw3Z3q3pPGYZhz+v0+/1UVFSQzWaH9bzOdDptL+bZ2hEA5cZ63iQjgIQQonOyXV0IIcSgcTqdduBpzcmzAk9p/xxZ8p4KPp915WAfhm36aD/f3nkU175canHfYe71uLLdq8bc1LXXXsuNN964xRDDqnyVbbnYoaVV7ZbL5chms+1CRuuxw9qMbi08K4fN6NYMUCv4tP5tzfs0DKND8Knr+qAft+geRVGorKwklUr1qgJTVVUqKytJJBJ2q/pAbQR3u934/X5UVSWTyZBOp4flz53T6SQSiZBIJIZNm76qqkSjUZLJZL+F4kIIMdRJJacQQohBY20zTiaT9sZjr9eL3++nUCjYgedwrTYRGzmzMVQ9h+HofIv5QFvQkGJBQyngVPUczl4GnABXX331Fm/jcrnwer20traO2IDT4XDYVZvWrMJNAwqXy2WHm4qidFrZWQ6sEQabzh9WVbVd8LnppndZdjQ0WPOnvV5vr0JOl8tlLyHri4VDPWH9Xd10XudwC82s5xCBQKAsLnz0Ba/Xa//sCSGE6JyEnEIIIcqCFXhC6QTQ2g4bCAQoFAr2IoXhcKIiOlIw8SZWkaqYVOo7Lxdm6bj684gURSEYDNrViiNJZ0uEMplMuyVCnVV8W0tUhtoFEGu5zKatpltadmRVfsqyo/KRyWTwer243e4OlYImCgVPhKLTg6k4UEwdrZDFmY2hYOJyuexZnIPVemwt6/L7/QSDQbxeL8lkclgtBrSWEPn9fpLJ5GAfzlZRFAWv1yujTIQQYgsk5BRCCFF2rBBAURS7cisQCLQLPIdLZYbYyBuvIxWZCEoZLYswDXzxun79EMFgEChtpR8prApGt7tUuZvP59vN0NQ0Db/fb8/itDajZ7PZYRnuWRWcm9p03qe1KR42bnpvG3zquj7kgt+hygonvV4v2VyeZNVUWqNTyIRryYTGd1qVruo5vIlVVObX412/iHH51WQGMbQyDIPW1lZ7Y3wkErHn2Q6H3zPDMEin0/j9/g7jLoYaj8cD0OcLqoQQYriRmZxCCCGGBGu5iNvtxul0Ahtn8A2XeVsjXWvV9izb/azBPowOtv3gfoKNi/rlvj0eD8FgkHg8PuwXSaiqaldtapqGrut2cGmapv12t9uNw+HochbnSLfpsiPr/7LsaOApvjCpSfuzonJXCp4IGEVQ1M1Xo5smimlgqhrufJzK5W8QWTUPRyE1YMfdleE6rzMSiQAQi8UG9Ti2RjQaJZ/PD/mKVCGE6G9SySmEEGJIsOZQZbNZVFW1A89QKGRvU7aWjoihKdC4GGempelpycMAAKnGSURBVBQWlEPLumnizMYINHbcvt4XVFUlEAiQyWSG9c9tZ5vPreByoDajDyemabYb72HZdNmRVfkpy476nonC+omzaJhyBKaigjXQQu1GFbqiYG6oVs85Q6yeciRrJh/O6CXPU718LgqD9/2w/o56vV57Xmc6nR7y1YPJZJJIJILH4xmSI0Gsivah/n0QQoiBIJWcQgghhrSuqr9yudywmi02UqybeBBrpx5ZqoYabKbBmMX/YtTy1/rl7iORCKqq0tzc3C/3P5g0TbOrNlVVpVAokMlkyOVy9hgKK9gEqcruT5suO7L+L8uOeifrH8XKnU8kE5rQtxdjTBNvYiUTPnkMT2pd391vLymKgt/vx+PxUCwWh/yFh0AggNvtpqWlZchVNVdUVFAsFkkkEoN9KEIIUfYk5BRCCDFsaJpmV4VZc/ys4ERO2ocG3ennswOvxlQHv9lEMXR2fO0aHIW+n5lnbTaOxWLD5mdz06pMwzDazdG0gk2Xy4WiKO2CTakmHHibtrtbLe+Kosiyoy7ERs9g5YyTMFG6V7XZU0YRBZMJ8x8l0jC/7++/FzRNIxAI4HK57HbpofhzoCiK3fI9lOYfO51OIpEIsVhMLtwKIUQ3SMgphBBiWLI2FLddXGK1yQ7FE7SRZN3EA1k79ejBbVk3TcYufpbqfqjidDgcRCIR0un0sNiU29kSIWs52Kab0QuFgh1sDrVqqpFi0+BT0zRZdgQ0j9uTVdOPL73Qn5XmZulrOX7B40RXv99/H6eHXC4Xfr8fTdPIZrOkUqkhd3HCGnEzlALDcDiMoihDep6oEEIMpMEvkxBCCCH6gXXynUql7KDF4/Hg8/nQdd0OPEfCyflQU718LvExM8kEa/qnWmpLjCLexCqqls/tl7sPBoPouj6kA87OlgilUilyuZw9MzcQCNgXGKxWdbnAUP6sx862owM6W3bkcrlGzLKj2OgZrJp+QumF/r74oqhgmqyafgJqMV82FZ35fN7eJu/z+XC73UNuXqc1tzsQCNDS0jLYh7NFDocDl8slbepCCNEDUskphBBiRHG5XHZlmaIoUllWprL+USzZ77LSUo+BrOg0DRTTYMpbt+JJr+/zuw8EAng8HlpaWoZk4Ne25Rxod7HA+r2yZuNms1kZFTHMtV121Lb6czgtOyo9Fl264bFoAGcF9/Nj0dZQFAWfz4fX68UwDPsCx1CgaRoVFRVDopI+GAzidDqH5dxmIYToLxJyCiGEGLGsUMYKbNoGnkPpJHw4CoVCxEbNYMGk/yu9YiCCTtMETGo/fqRfqqdcLhfhcJjW1tYhteG3syVC2WyWQqFgXzSwZnBarepDpRVU9I/hsuzIRGHpPhcMelX55HfvHNSt613RNA2/34/b7aZQKJBMJsvy+7gpv9+P1+ulubm5bC9uqqpKNBollUoNqWpZIYQYbBJyCiGEGPE2t+05n89L4DnAwuEwTqeTRCLB2qqZw2IOnrX0olAoDJnWQyvYbLtEKJfL2fNu2/6uWDM4hdicobbsaLjPB+4rTqeTQCCAw+Gw53WWa3hoiUajFItF4vH4YB9Kp6zN9s3NzfIcRAghekBmcgohhBjxTNO0KzjbbogOhUKYpikhzgBRFIVwOIymacTjcQqFAtHV76EWc6WNxqY5ZDcaB4NBgLLf6utwOOxwE7BDWdM08Xg8RCIR+/XJZFKqnkWPWBWcm9o0+LRmvcLgLTvSnX4aphwxuAEngKKwdsoRVNTPw1Eoz/bqQqFAS0sLHo/Hruws93bwZDJJOBy2t8aXE0VR8Hg8ZLNZeXwVQogekpBTCCGEaMM0TbLZLNls1l6g4na7CYfD0o7bjxRFIRKJoKoq8Xi8XctjpGE+nrfXsnLnE8mEJvRt6GCaeBOrmPDJY/02987aPB6Px8vyhNU6ofZ4PDgcDorFIul0Gl3X7QVCVpu6NXuv3Ku0xNCyNcuONm1776ufzeaavUpzOMuAqag01+zFqDKu5gTsam+fz4fP58Pj8ZTtvE6rW8NaQlROj80ejwdFUaRNXQghekHa1YUQQohu0DStw2IVq/pTAs+to6oq4XAYRVGIx+NdtqeaKDROnMXaKUdsCB+U3gWeG2ZvKqbBmCXPU7V8br/Nu7OWXGSzWZLJZL98jN7qbIlQoVCwZ3CqqmoHT7IZXZSLgVh2ZKLw+QFXUPBEBr+SE8A0cWZj7PD6DWU5m7MzqqraLdfWBZJy+1tpzb3MZDKkUqnBPhybNdqk3Cv/hRCiHEklpxBCCNENVnVbOp22ZxK63W68Xi/FYtEOgobC0oVyomka4XAYgFgsttkqLAWT6uWvUVH/Hs01e9K0zf4UPBEUo7jlLeymWZq7qWo4szGq6t4ckPbPYDBIsVgsm4BTVVW7alPTNHRdt1tKrdcXi0XZjC7KlmEYGIbRITDbdNmR0+m0K+KgZ8uOklVTKXgr+v1z6TZFoeCtIFk1lWDjosE+mm4xDIPW1lYymQyBQIBIJEIulyOZTJZNJbi1Gd7v95PNZsviQo7b7bZHtgghhOg5qeQUQgghtoI1P87tdkvlWw9pmkYkEsEwDOLxeI9OfD0eD/5AgHW+WtZ5a0iHJpAJjcdwuDvcVtVzeBOr8MXrCDQvIdC4uNvVUCYKBU+EotODqThQTB2tkMWZjW3xPqyWzVgsNuhhoTVn1uVy2WMXDMPA5XJJZbIYdqZNm8aPf/xjdF1n/fr1XHvttQBcffXV7L333vzpT3/iH//4B6ZpUlNTw8UXX4yqqtx333288847zDrzSo4/4iAME55ZuJ45r9e1u//RARd/PH4nfC6V+96p59GPGphS5eUv353ODtU+Rl33Jql86fE/ds0BfFBfqsi78O+LWdCQ4u+nziDiLdWaXPiPxXy0OslVB0/k8O0rAbj77VU8+lFD+0/KKPL0/43h+gtO55vf/CazZ8/GNE1eeeUVHn74YQAuuOACZs6cyerVq7nmmmvQdZ2bbrqJn/70p/32te4ut9uN3+9HVVUymQzpdLpsWsQrKiowTZNYLDbYh0JFRYX9N1EIIUTPScgphBBC9BGn02lXeFqBp1URVy6VK+XC4XAQDoft7bY9Odn1+/34fD4ymUy7Ckk7kHR4MFUHiqGj6d0LJNveR7JqKq3RKWTCtVsMTr3xOoKdBKcOh4NIJDKoyzc2DeDz+TzFYtGebdh24Va5Ld4QYmtUVVXR2tpKLpfj/PPP57PPPuPll1+murqaffbZB5/Px9NPP42madx8883cf//9JBIJbrrpJi6++GLW7XMWn+QqQFGYe85uHPvIJ6xPbQz/5xw9mec+b+K1L2PMPXs3Dv7d/1AVcKoKz5w6g28+NN8OOd89bw/2vuv9dse3bdTDsuYsU6t83HrUZL75x/n265yawnvn78kut89r9z77TwzztTEGz1xzFuPGjWP16tUAPPjgg1x66aVUV1dzyimncOWVV3LGGWdQX1/PCy+8wIknnsjy5cv573//289f9e6xLv6YpkkqlSKbzQ72IeF0OolEIrS2tg7q8VjHEYvF5GKTEEL0krSrCyGEEH2kUCjYW6ddLpdduRIIBCgUCnbgWS7VK4PF6XQSDofRdb1HAaeiKIRCIZxOZ6cnowomrmxLr45Jd/pprtmLptqvlNpUjSJspgXecLhJVUwiFZlI47YH4cy0UFn3JtH693AUUgSDwXat4AOlsyVC1smyy+XC6XTaG9Pz+fyI/1kUw1NjY6P970KhYF9kWr9+43Ixq229oqKCTz/9FICmpiaKhsnCTAC00u9+oWhibPJrsuf4EJf9aymmCR/UJ5g+2s8H9a10tiZmu0ov/zl7Nz5tSHLJs0vJ6QbLmkuPXfmigbHhd9B6XaFoUtz0AwLfmlbNkx+uwgQ74LQ+D9M0mTlzJu+88w4Ab7/9Nt/85jd54YUXePfdd/nOd75TNiFnOp0mm83afxu9Xi/JZHJQQz3r77Pf7x/Uv9E+n89+HiGEEKJ3JOQUQggh+kE+nyefz9Pa2mpXdwYCATvwtCroRlrI5HK5CIVCFAqFHrXjWcuJrO3rfXUSaKKwfuIsGtouMwJQtS2/s6KAUrpdwRNh7dSjaJhyBLV1r1LZ9CGxluY+OcbusOYPut2lqlNd18nn83Z1sRW+j8SfOTFyjR07ln333ZcHHnigy9tY29qB0uN1dCyGVlrG9X/TqviyOUNTuv3jjVNTsX6N4tkiUZ+zy/uf+qv/0pzWufLgifxg3xp+/cZK+22/Omoyc9q8DHDhVyfw9IL1m94NO1T7WBovUu2psC/mHHTQQaxatYpYLEYwGLRD3GQyac86XrVqFZMmTery+AZD23mdfr/fnteZSqUGbcxLMpkkGo0SCAQGZeGPVWGfSCQG/GMLIcRwIiGnEEII0c+sQFNRlA6BZz6ft98+3LndboLBILlcrkcnkU6nk1AohGEYxGKxPjsJzvpHsXLnE8mEJmz9BmVFARRMRWXFNl+nMboTNfMfxZNa1yfH2plNlwhZi1SspStWJWk2m5VxCWLE8fv9XH/99Vx99dWbnYnb9ncjEAjQnCmNbpgxNsAP95vANx/6uMP7FIoGilLaZxb2aDSnu77o0pwufeynP1nHTw7cxn79zw/ZlnfqEryxLGa/7utTouw/Mcxxjy7o8v6KDg8AU6dO5Tvf+Q4XXnghUApoA4GA/XkMhZmOVjW/y+UiEAhQUVFBNpsllUoN+MUYq30+GAySzWYHvJrS5/PZSwyFEEL0nrrlmwghhBCiL5imSTabJR6P09zcTCqVQlVVQqEQVVVVBINBXC7XYB9mv/B4PIRCIbLZbI8CTo/HY7e292XAGRs9gyX7XUomWLP1AeemFIVUYCxL9ruU2OgZfXvflMLicDhMNBq1Z9tZszZVVSWbzdLc3ExLSwvpdFoCTjHiaJrGTTfdxH333ceKFSs2e9vGxkbGjx+Pz+cjHA7TkkgzLuTintnbc8pfFpIpdPz9eX9VKwdOqkBTFXarCbGwIdXpffucKuqGh5f9J0ZY2lRqaD919zGMD7vbLTSaPtrPFV+byKlPfEZn+d6ixjSToqV5w9XV1VxxxRVceeWV9tiOjz/+mL333huAfffdl48/LoWz48ePZ9myZZv/gg2yfD5v/010u91Eo1G8Xu+AH4cVblph8UBRVRW3200m09nAAyGEED0hi4eEEEKIQWad4FhzFIfbpmuv10sgECCdTpNKdR4GdKarBUNbq3ncnqyafnzpBaUfr/eapXBk/ILHia5+fws33jxN0+yqTVVV7bBX07Rh9/MixNY66qijuOyyy1i6dCkATz75JC+99BLnn38+s2bNQlVV3nrrLebMmcOkSZO44oor0DSNe+65h9c+rePHcx5g1qQKVsZLAeK5f11EPKtzzj41XPPyMsYEre3qGve/W88jH64l4nXw+Hens/v4IB+tTnLr3BWsac3zwLE7kswXiWUKnPr4pyTzRVLXHch7qxIUDJPlzVnOeOozXvr+LowJuuwFR7P/OJ9EbuNFnQO2jfCViWGeuv58brr0LHbffXcaGkob2K+//npWrFjBRRddxM4778zatWv5+c9/jq7rnHjiidTV1fHWW28N8HehdxRFwe/34/F4KBaLpFKpAV2MpmkaFRUVpFKpAQsdrc+3ublZxokIIcRWkpBTCCGEKCOaptmBpxVgWQuLNtdyWa6soDKVSnV7CY+iKHZVazKZ7NNtt7HRM6ibeYr1gfrsfrtkmoBJ7cePEGmY36N3tcYbeDwenE4nhmFgmiaapslmdCH6Sd5Tweezrhzsw+jUYydO4/ofnoSWaur2+9x000387Gc/G3LhmaZpBAIBXC4X+XyeVCo1YH8D/X4/Xq+X5ubmfq+EVxSFaDRqt+kLIYTYOhJyCiGEEGXK4XDYMzytmYu5XI5sNjtoyxl6ou3m3O5WxLRdMJRIJPq0MjHrH8WS/S4tLRjqzwrOTZkGimkw5a1b8aQ7LhTZ1KZLhEzTRNkQyI6kGa5CDAYThYUH34DhcA/2oXSg6jmmvXI5A3B5pmw4nU4CgQCappHNZgdkBIeiKFRUVKDrer8vAvJ6vfj9/gEJVIUQYiSQxUNCCCFEmdJ1HV3XSaVS9pZsj8eDz+dD13U77CrHwDMYDOJ2u2ltbe12JWZ/LRiCUnCxcucTMVEGNuAEUFRM02Tlzicy+d07Ueh4fbmzkQVQOtkuFAr293qoVWMJMdQomHgTq0hVTBqYau/uMkvHVUZHNCAKhQItLS14PB67rTudTne7M6A3TNO0N9RblaT9xev1ksvlJOAUQog+IiGnEEIIMQQUCgUKhQLJZBKXy4Xb7bYrQNqGYOVwohQKhXC5XCQSiW6fHHo8HgKBAIVCgUQi0edh3vqJs/pmi3pvqRqZcC2NE2dRvfw1+9UulwuPx9Nh4VTbMQXl8D0VYiTxxutIRSaCog32oWxkGvjidVu+3TBlPR76fD58Ph8ej4dUKtVvVe35fJ58Pk8gEKC5ublfPobVpRGPx/vl/oUQYiSSkFMIIYQYYqyTL9gYkvn9/g6B52BU/YXDYZxOZ48Czv5aMGTRnX4aphwx+FVZisLaKUdQtfYDApphLxGy2tGLxaJ9Il+O1blCjBTB5iU0bnvQYB9Ge6pGoHnJYB/FoDJN014IFAgECIVC9sW//pjX2draSjQaxe/398u8TK/XSz6fl8d7IYToQxJyCiGEEEOYFXgqimIHnoFAwK6KzGaz5PP5fg88FUUhHA7bVSndmaXZdsFQT9rae6q5Zq/SHM4yYCoqme0OoLrhXRRFGfKLpYQYjgKNi3FmWih4IoN/cQTANHFmYwQaFw/2kZQFwzBIJBI4nU78fj8VFRX24p6+rHw3DIN0Oo3P5+vzWdhOpxOn00ksFuuz+xRCCCEhpxBCCDEstN22bW3ldrvdhEIhTNMkn8/bgWdfUxSFSCSCqqrE4/FuhXVtFwx1NxTtDROFptqvQNlMslNYWb07o+reIJfN9NvnLYToPQWTyrq3WDv1SMrjscOksu7NTuf5jmSFQoFYLIbb7cbv9xONRkmn02QymT67sJdOp3G73QQCgS22lZsoFDwRik4PpuJAMXW0QhZnNtbhe+f1etF1Xf4GCCFEH5OQUwghhBhmTNMkm82SzWbthTZut5twOIxhGPaG7r4IPK2wUlGUbi8LcjgchMNhTNPs8wVDm0pWTaXgrei3++8xRSHnDrPGPZZg66LBPhohRBei9fNomHJ4WVSBK6ZBtH7eYB9G2bIu8LWd15lOp/usOyCZTBKJRHC73e1mgJooJKum0hqdQiZcSyY0HsPh7vD+qp7Dm1iFN15HsHkJ4ZYvcLvd/b65XQghRiJll112kUuCQgghxAigaZodeFobvK2Tw95Uk2iaRjgcBiAWi3WrTdDtdhMMBje7YGjatGn8+Mc/Rtd11q1bx1VXXYWu6xxyyCGcfPLJZLNZrr76atatW8fEiRO56qqr0DSNu+++m3nz5jF79mxmz56NaZo8M381187XQN24QGR0wMUfj98Jn0vlvnfqefSjBgBOmDGK7+9dg6rAFS98wTt1CR46fkcOn1rJta8s4+7/1gPg0hTu+OZUJlf5SOZ0Zj/8CUfvWMnlB02kYJh8WN/Kxf/sODvvhsMm8Yf311Dl1fjVQdW4Mk1UV1fzxhtvMGfOHH7yk58wZcoUPB4PDz30EC+//DLRaJSbbroJRVFIpVL85Cc/QVEULr/8cq6++uoef8+EEN23buKBrJ169OC2rJsmYxc/225hmeiaqqr2FnZd10kmk31SLWmNVmlubqbg8NFcsxdNtV8pXUQziqCom/85MU0wDVA1XLk449e9j2fJXByFvp/1KYQQI5mEnEIIIcQIpGkaHo/H3u5aLBbtwLM77eaaphGJRDAMg3g83q2As7sLhqqqqmhtbSWXy3H++efz2Wef8Z///IcHH3yQM844g2nTpnH00Udzww03cOutt/Kb3/yGpqYm7rrrLk4//XTGjRvH6tWrAbj7yeeZ/efFrE9v/JzmHD2Z5z5v4rUvY8w9ezcO/t3/iHod3HrUFE76y8J2xzI26OLQqVH8Ls0OOS/ZfwJLmzL849NG+3YTwm5Wt+YpGiaPfmcat71Rxwf1rfbbfU6Vh0/YiWP/tABME3/Ll2z33t1cc801/OMf/+CDDz7A4XCg6zo+n48HH3yQ73znO5x88slkMhmefvppzjzzTFauXMkLL7zApZdeytNPP83y5cu3+HUXQvSOicLSfS4gE6xpd6FkwBhFvIlVTH73TmlV7yGHw0EgEMDpdJLL5UilUlvVNaAoChXRSpZV7U5d7dc2VPgqvQvATRMwUUyD0Uuep3r5XPn+CiFEHxn8/gshhBBCDLhisUgqlaK5uZmWlhZyuRwej4eKigoqKirw+XxoWucn9Q6Hg0gkQrFY7FYFp6IohEIhvF4vyWRyixvUGxsb7ZbAQqGAYRjU1tby5Zdfous6H3/8MVOnTgWgurqauro6UqkU8XicSCRiB5wmCjmcGJvM1NtzfIhXv2ihaJh8UJ9g+mg/h29fSa5o8OIZu/DH43fC7yp97mtaO7b0Hza1kq9sE+aVs3blrL3GAbAynqNolE5S80UDY5MK1YMnR3mnLmF9QciExqM5HEyfPp0PP/wQwA6XPR4PX375JQDLli0jGAwCEAqF7CUV7777LgceeOBmv45CiK2jYDLhk8dKAZTZdwttusU02n980SO6rhOLxUgkEjgcDioqKvD7/Si9rMrN+Kp5b+r3WLHN1zFVx5YrNzdHUUBRMVUHa6cezdJ9LiDrH9W7+xJCCNGOhJxCCCHECKfrOqlUiqamJmKxGIVCAa/XSzQapaKiAq/Xi6qWnjI4nU4ikQi6rhOPx7e43EFVVSKRCE6nk0QiQSaT6fZxjR07ln333ZfXX3+dUChEKrWxrc86Huv/UJqbFgqF7JcPOOxovmjJ0ZRu36ro1FSsw45ni0R9TkYFXFT5nBz2+4/4b12cH+47vsvjmhBx896qBIc+8BHf2WU0NaGNM9j2GB9kVMDF/1a3D3K3r/axrHnj52443Oyx/yHMmzev3dfwpptu4oknnuC///0vAAsWLODwww/nySefZKeddmLevNJcvlWrVjFp0qTNfwGFEFvNk1rHhPmPll7oo2U2W7Th40yY/yie9PqB+ZjDVC6Xo7m5mXQ6jcfjIRqN4vF4enQfsdEzWLLfpaQCY/t+dIGikAnWsGS/S4mNntG39y2EECOQhJxCCCGEsBUKBZLJJE1NTfamdL/fT2VlJRUVFYTDYfL5fLcCTqt6xlpK1JNFR36/n+uvv56rr74aXddpbW3F7/fbb7eqR9tWkQYCAXuRw9SpU/nu8cdw4T8Wd/wci4Z9nhr2aDSnC8QzOv/5sgWAV5e2MG20v8P7WWIZ3a4E/e+KONtX+wCoCbm57egpnPbEp936HA/7+td4+eWXUVXVri766U9/yuzZsznjjDNQFIVTTz2VRx99lOOOO47XXnuNY445plv3LYToO5GG+Yxf8AQMREWnaQAm4xc8TqRhfv9+rBEknU7T3NxMLpcjEAhQUVGBy+Xa4vs1j9uTupnfK7Wn99fIAlXDVFTqZn6P5nF79M/HEEKIEUJCTiGEEEJ0Kp/P09raSmNjI+l02m5fd7lchMNhPB5Pl61/brfbrvhsaWnp0Sw0TdO46aabuO+++1ixYgUAdXV1TJo0CYfDwcyZM1m8uBReNjY2Mn78eHw+H+FwmFgsRnV1NVdccQU/uv7XZAodA4n3V7Vy4KQKNFVht5oQCxtSvLUixi5jS23hu4wL8GVz1xWnb6+I27edOTbAspYMAZfGn787jXOfWcT6VMclF4sb02wb9dovO1SFGTvtyMqVK6msrKSqqopx48ZRXV1NIBAgl8vZVbS6rhOJRMjn81RVVREKhZgyZQqrV68mEAjg9/vteaderxePx2PPW3W5XLhcLpxOJw6HA03T0DTNDlZ727opxEgTXf0etR8/gmIapUUz/cEoopgGtR8/QnT1+/3zMUYw0zRJJpO0tLRgGAbhcJhwONzlaJbY6Bmsmn4Cpdmb/XzavGHG56rpJ0hFpxBCbAVZPCSEEEKIzfJ4PASDQTKZDKlUyt7Q7nQ6gVIYai0tgu4vGOrKUUcdxWWXXcbSpUsBePLJJ3nppZc49NBD+e53v0sul+Pqq6+moaGBSZMmccUVV6BpGvfccw/vvvsuP//5z9l9991Z09hCOjyBc/+6iHhW55x9arjm5WWMCVrb1TXuf7eeRz5cC8D1h01i323C5HWDk//yKU3pAjccNolv7FSFpii8sLiJS59dyqiAkz8ctxNBt8bLS1u49uVlXPG1iZy9dw1LmtIAXPPvZby+LGZ/Tn6XxsMn7MQxj3wCwOFTo5wYWclvbrrODhpvueUWgsEgTqeTP//5z7z++uuMGTOGK6+8EijNUb3mmmtIJBKcd955PPvss9TV1bULK61/9zS8NE3Trszt7N+9fduWbifEUJP1j2LlzieSCU3o29Zl08Qbr2PCJ49Ji/oAcblc+P1+NE0jm82SSqXsx6asfxRL9ru0VMHZ3wFnW6aBYhpMeetW+TkQQohekJBTCCGEEF3y+Xz4/X7S6XS7mZhQCtTcbjcejwen04lpmhiGgaqqpFKpHs3f7A95TwWfz7pyUI+hrRsP344H31vN0qbS12WHudfjyrb0+H48Hg9XXHEFV1111RZvu7kAtL/e1hNbG6L25G0SrIq+YqLQOHEWa6cc0WdbtscseZ4q2bI9KLxeLz5faexIOp0mncmydJ8LyARr+q9FfXOMIt7EKia/e6f8PAghRA9JyCmEEEKITlkVmalUinQ6vdnbOhwOQqGQ3QZtGIZd3VkodGzfHggmCgsPvgHD4d7yjQeYqueY9srlDMdm8YEOVfuiarW/Q1YxPOlOP801e9JU+1UK3opSG/uWtm6bG+Z6qhrOTAtVdW9SUT8PR2Hzj7GifymKYo/8WD5qL74c/7W+XzLUE6bJ2MXPUr38tcE7BiGEGIIcg30AQgghhCg/gUAAr9dLMpncYkWmw+EgHA5jmiYtLaXKRKul3ev12oFnNptF1/WBOHwAFEy8iVWkKiYN7snqpszScZXREfWpwQr3+jo43fS/zm7bXQMdqkqwOjAchRSjlr9G9fK5JKum0hqdQiZcSyY0vtOLK6qew5tYhS9eR6B5CYHGxVKpVyZM0ySVSpEsqizbddbg/81QFNZOOUICcCGE6CEJOYUQQgjRTjAYxO12k0gk7DmbXXG73QSDQXRdb7dxPZ1Ok06ncTgc7QLPYrFoV3gORODpjdeRikwEZRBaDrtiGvjidYN9FMPOYIR7fV2NalVCb+l23TXQoepIDVcVTIKNiwg2LgJKVeQFT4Siw4OpOlAMHU3P4szGJNQsc+vH7LFhBMHgMxWV5pq9GCXVnEII0W0ScgohhBDCFgqFcLlcJBIJ8vn8Zm9rzevc3IIhXdfRdZ1UKoXT6bRnePp8PnRdtwPPnmxf74lg8xIatz2oX+6711SNQPOSwT4K0QcGq2pySxWmPXlbZ8FqZ+/XXQMdqpZjsKpg9mrerhhcJgpNtV+BsqmzV2iq/SrVMqtVCCG6TUJOIYQQQqAoCqFQCKfTSTwe3+IcTSsM7U47u6VQKFAoFEgmkzidTjweD16vF7/fT6FQsANPwzD64lMCINC4GGemhYInMvjthwCmiTMbI9C4eLCPRAxh5VK1ujVVrN0JantiSwunehOcyhKrkSVZNbU0W7VcKAoFbwXJqql2lbAQQojNk5BTCCGEGOEURSEcDqNpGrFYbLNt5KqqEgqF0DStW9WeXbECTwCXy4XH48Hv9xMIBCgUCmSzWXK53FaHCQomlXVvsXbqkZRHdY5JZd2bUpUjhpzBClat//fFaID+GgfQ9t+yxGroao1OKS2PGoyN6l0xiiSjUyTkFEKIbpKQUwghhBjBVFUlHA6jqirxeHyzAWfbBUOxWKzPWszz+Tz5fB5FUXC5XLjdbgKBQLvAM5/P9/oEP1o/j4Yph5fFnDXFNIjWzxvswxBiSBiOS6y6ul13yRKrrgUCAe655x4mTZrEKaecwhdffAHAddddx1e+8hXuu+8+Hn/8cQAmTpzIVVddhaZp3H333cybN4/INjvy6Jm749QU/v5pI3e8ubLd/R+7czXXHjqJ1lyRve96H4ApVV7+8t3p7FDtY9R1b5LKl/4uvn7ObuiGiUNVOPuvn/PZujQuTeGOb05lcpWPZE5n9sOfdPgc/nziNL73+KeMCbh48LgdcWoKL7wf4IVfPouiKPziF79g/PjxKIrCtddey/Lly7n99tsJhUIA3HzzzSxatIhTTz2V9957j08//bTfvt5CCFGOJOQUQgghRihVVYlEIgBbDC27WjDUl0zTtFvWFUWxFxYFg0GgFIZab+8JRyHF6CXPs3bq0YPbsm6ajFnyvGzKFaLMSdVqR0NhiVU2m+WCCy7goosuavf63/zmN8ybNw+fz2e/7rzzzuOaa66hqamJu+66i3fnvceF3/oKN7+2gle/aOFvp+zMEx83sKZ1Y7fCq1+08Pfb5/Hmubvbr1sVz3HQfR/yzKkz2n3Mg3/3PwpFk1mTIly8fy1nPf055+03nucXNfGPZzqvytx/2wifrE1SNEx+cuA2G4/le9N4v7qaqspKXC4XZ5xxBrvuuisnn3wy119/Pb/61a+or69nm2224ZJLLuHCCy/kb3/7G5dddhlXXXVVj7+OQggxlEnIKYQQQoxAmqYRDoeBUsC5uTmY1oKhbDZLa2vrgByfaZpks1my2SyqqtqBZygUwjAMO/Dsbrt89fK5xMfMJBOsGZxWRKOIN7GKquVzB/5jCyHKXrlVrQ7VJVb5fB6Hw2HPfDZNk0QigdPpRNM0nE4npmkyatQo6uvr7bcHxmzDpKoAH61ZAcD8tSn22ybM0wvW28fTnO7Y6ZApGHQ2lbpQLB1P0K2xYG1pMd9hUysZvbqVC786gcc/auD+eavbvc+3dqri8Y8bANiu0stHa0p/b+c3ZJi+x358/M7r9tcwFAoRi8UAqK+vL33MQsH+OsTjcaqrq1FVtU/nXIuRy0Sh4IlQdHowFQeKqaMVsjizMRnBI8qKhJxCCCHECGO1nRuGQTwe3+wJUG8WDPU1wzDIZDJkMhlUVcXj8dhb2g3DsKs7N7csScFkwiePsWS/SzFNAwaydd007I8vJwJCiHIyHJdYaZpmz3m2bmuFnlb3gtPpJBqNAqVwcNzE7fh0XYqvbVfBMwsbmTUpQl1Lttef4+iAiydPnk5txMP/PTwfgAkRN7+bV8/lL37Jv7+/C//6vIn6xMbOhB2qfXzZXPqYmx7Li5EKYrEYhUKBZ555BrfbzWmnndbuY15yySU8/PDD9suxWIwxY8awenX7MFWI7jBRSFZNpTU6hUy4lkxoPIbD3eF2qp7Dm1iFN15HsHkJgcbF8lxHDCoJOYUQQogRxOl0EgqFKBaLm207txYMORyOrVow1NcMwyCdTpNOp+0TWbfbjdfrpVgs2oFnZ7NFPal1TJj/KHUzvwemOTCt6xu+vhPmP4onvX4LNxZCiOGvv4PVXC5HLBajsbHRfl0ymcQwDJqamlAUhUKhQEtLiz0LenVDIzf9ZwX3zN6e7+9Zw6pYjrXJno1GaashmeeAez9kz/FBbjxsO478w8fEMjqvftFC0TD574o421f72oWcbW16LOtbEuy7774Ui0Vmz57NTjvtxCWXXMJPf/pTAM455xzmz5/Phx9+2OtjFgJAd/pprtmLptqvUPBWlJZxKWqXz5kMh5tUxSRSkYk0bnsQzkwLlXVvEq1/D0chNcBHLwQM/gR+IYQQQgwIp9NJOBze4lxNh8NBJBJBVVVaWlrKJuDcVLFYJJVK0dzcTEtLC7lcDrfbTUVFBdFoFJ/Ph6a1b02PNMxn/IInABPMfm7hMw3AZPyCx4k0zO/fjyWEEKJLVrBqGAbFYpH169czZswYnE4nwWCQxsZ1NKULHP/oAo78w0e4HAqvfRHr1cdyqIqdB8WzRdKF0rzrt1fE2WVsacb0zLEBlrW0745Y1JhmUtQD0OFY3vvgIxRFIR6PA9DS0kIgEADgG9/4BqNHj25XxQlQUVFBQ0NDrz4HMfKYKKybeCCfHXg1a6ceScETKb1B1bZ8UVhR7FFABU+EtVOP4rMDr2bdxAMxGcRZ6GJEkkpOIXpBZpIIIYYal8tFKBQin8+TSCS6vN1ALBjqD7quo+s6qVQKp9NpV3f6/X50XSeXy5HNZjEMg+jq91CLOVbOOKn0+fXHjE6jWGpRn/+oBJxCCDFA7rzzTrbffnsmTpzIU089xT//+U/OP/98Zs2ahaqqjB8/njlz5vDb3/6Wa665Bk3TuOeee3AaBQ6bGuVHs7bBBObMXUEyX2R0wMU5+9RwzcvLmDUpwuUHTWRqlY8Xz9iF0574lIxu8Ph3pzNzbIC/nzqDW+euYGFDioe/M42iYWICF/y9tGjolrkr+MNxO/GLr2/Ly0tbWNbcvh3+7wsbOWRKlPdWtXY4lmxrC++8s4JvfOMbPPDAAzidTm677TZUVeWqq65i4cKF/O53v6O+vp5f/OIXhMNh1q9fv9mFgkJYsv5RrNz5RDKhCVvf5aIogIKpqKydejTxMTOZ8MljeFLr+uRYhdgSZZdddhkaZy9CDCKZSSKEGMqs4DKXy212cdBgLBjqby6Xy15aZLUoWi3taW9V3z2pb8s08cbrSk/qpUVdCCHKhqZpaJqGw+Fo938UhddmXoKhuQb1+B47cRonP/4pRWPj+YOq55j2yuU9qoc79dRTef/991m4cGHfH6QYVmKjZ5Qu+qLIRV8xLEjIKcRm9HQmCVCav2YaoGoyk0QIMei8Xi+BQIBMJkMymezydsFgEI/HM6gLhvqbFXa6XKWT2EKhQDaXZ9WYvVk75QhMRQWU3gWepgmYKKbBmCXPU7V8rlzkEkKIQdJVmGktJzIMA13XKRaL9v8/3/UsUpFtB2Zec3eZJv6WL9nuvbsH+0jEMNQ8bk9WTT++9EJ/LmTcMB5o/ILHia5+v/8+jhBIyClEp0wU1k+cRUMfnvSOXvI81XLSK4QYQFZlZjqdJpXq/EJLuS4Y6k/WogmPx4PT6QQgZWjUR3emYdw+vb6oVVX3JhX183AU0gP0mQghxMjWmzBT1/VOR7Gsnno0jdsc0D/VbL1lFKle8TpjFz872EcihpnY6BnUzTyl9MKALWI0qf34EanoFP1KQk4hNtGnM0naMk28iZUyk0QIMSD8fj8+n2+zlZkOh4NQKARAPB4fkbO7FEXB7XbbgadhmqzzTaDRX0syULPF8SS+eB0BGU8ihBD9qrMgs7dhZldaq7Zn2e5n9den0GvbfnA/wcZFg30YYhjJ+kexZL9LS8U8/VnBuSnTQDENprx1q4zzEf1GQk4h2pCZJEKI4SAQCOD1emltbSWbzXZ6m6G6YKg/qapqt7Q7nU4MwyCXL5DATc50YKoOFENH02XRnBBC9IeBCDO7YqLw+QFXlLZKl0PLumnizMbY4fUb5O+N6DMmCkv3uYBMsGZwqpaNIt7EKia/e6f8XIt+IdvVhdhgQGaSqBqmaVA383sYMpNECNEPgsEgbrebRCJBLpfr9DbDccFQXzAMg0wmQyaTQdM0O/Ac7QDDyJPLtZLL5SgUCoN9qEIIMaR1N8wsFApkMpk+DTO7omBSWfcWa6ceCT1a89NfTCrr3pQgSPSp9RNn9X3HYk+oGplwLY0TZ1G9/LXBOQYxrEnIKQSlCs5V008ovdDfD/iKCqbJquknoBbzUtEphOgzoVAIl8u12dma1oKhVCpFOi2zI7tSLBZJp9Ok02kcDocdeHq9XorFor2hXdf1wT5UIYQoW1sKM4vFIsVikXw+b/+7v8PMzYnWz6NhyuEbZvIPLsU0iNbPG+zDEMOI7vTTMOWIwa9UVhTWTjlC5piLfiEhpxjxsv5RrJxxEmAO3EwSRQHTZOWMk/C8tUZmkgghtoqiKIRCIZxOJ/F4vNNKw7YLhuLx+IhYMNRXdF1H13VSqRQOhwOPx4PH48Hn86Hruh14jsSZpkIIAT0PM61W83IbleIopBi95HnWTj16cIMg02TMkuclABJ9qrlmr7II8AFMRaW5Zi9GSTWn6GMScooRzURh5c4nlmZwDvQDvqJimiYrdz5RZpIIIXpNURTC4TCaphGLxTqtLGy7YKir24ju0XWdZDJJMpnE6XTa1Z1+vx9d18lms+RyOQzDGOxDFUKIPjdcwszNqV4+l/iYmYM+s7Bq+dyB/9hi2DJRaKr9CuUxigFAoan2q1QvnyvnwaJPScgpRjSZSSKEGMpUVSUcDqOqKrFYrNNKQpfLRSgUQtd1EomEhG99qFAoUCgUSCaTuFwu3G43fr+fQCBAoVCwA8+hdHIvhBAwMsLMriiYTPjksdL2adMY+O3TGz6+BD+iLyWrplLwVgz2YWykKBS8FSSrphJsXDTYRyOGkfKoVRZiEJTbTBLd6Rvc4xBCDCmqqhKJRFAUpcuA0+fzEQ6HyeVyxGIxCTj7UT6fp7W1lcbGRjtMDgQCVFZWEg6H8Xg8djgghBDlwpo57Pf7CYVCVFRUUFVVRTQaJRQK2Y9d+XyeZDJJS0sLjY2NNDc3E4/HSaVSZLPZQZ2j2R88qXVMmP9o6YWB+rw2fJwJ8x+VUVZiswKBAI888ghvvfUW2223nf36UaNGcfvtt3P//fdzzjnnAFBZWcndd9/Nn++6mZNmVre7nx/P2oZ3z9ujw/37nCpPnTyduefsxmUH1AKwTYWHtVd+lVfO2pVXztqVKr8TgGN3ruaNc3fnpe/vQk3IDcA9s7e3b5e8bhYRrwOPQ+Xvp87g1bN25aXv78KogBOMIsnoFPvjHnfccey1114A3Hfffbz++uvsv//+9tuvvfZaPB5PX3wJxTAmlZxixJKZJEKIoUrTNMLhMKZpEo/HOw0vZcHQ4LFmdCqKYi8sCgQCBAIB8vm8/XYhhBgom1ZlOhwOVFUdEZWZvRVpmI+x4AlWTT++FED253mDWfo7Pn7B47KUVGxRNpvlggsu4KKLLmr3+osvvpgbbriB9es3huSnnXYaf/zjH/kLe/D8z47jqYVN5HSDgEtj5zH+Tu//jL3G8fyiJn7/3hqeO30mf/5oLQCvL4tx/KML7NtpqsJFX63loPs/ZM/xIa48eCLnPrOIc58pVWZuU+HhgWN3JJbRmT2tmgVrk1zx4pecvOsY/t8e47jpP8tJh2vt+5s1axbnnXceAFdeeSXf/va32x3Xq6++ylFHHcXTTz/d+y+eGPbKI+ERYoCV60wSs2yORwhRrhwOB5FIBNM0O63OtCo83W438XhcAs5BZJom2WyWeDxOc3MzqVTKXgBVVVVFMBjE5XIN9mEKIYaRTSszo9EoVVVVVFRUEAqFcLvdKIpCLpcbUZWZvRVd/R61Hz+CYhpg9NNyOaOIYhrUfvwI0dXv98/HEMOKruu0tLS0e53D4WDcuHFceuml3HfffcycOROA6dOn8+6890gFavigPsH00aVg84KvTuC3/13V6f3vVxvmpcXNAPx7aTP71oZLr98mzGtn78b1h00CYEqll8/XpygUTd5eEWfnMYF293PszqN4ev46AJY2pfG7SjNuI14HTekCKAqZ0HhMYMqUKdTX19vv2zaotbz77rvMmjWrR18rMfJIJacYkWQmiRBiKHI6nYRCIYrFIvF4vMMJqCwYKl+GYZDJZMhkMqiqitvttre0G4ZhV3cWCoXBPlQhxBDQ3crMXC5nV2WO9MrM3oo0zMfz9lpW7nxi38/yN028iVVM+OQxaVEXWyUSiTB16lR+8pOfUCgUuOOOOzj55JNxOBzk3WEMh5t4tkjU5yTk1th5tJ8bX13e6X1VeJ0kcqVQP5HVifqcrEnkmPqr/5IuGNx/zA58e3o1axI5EtmN4b+mtv/dmD2tmtkPlyqTlzRm2HGUn/kX74WCwj6/LQX6hsNNwVPBxIkTWbWq89DVkslkqKgoo3N4UZYk5BQjUmt0Sulq7GBsTOzKhpkkEnIKITpjLRAqFArE4/Eu3y4Lhspf28BT0zQ78PR6vRiGYS8skpBaCNFZmKlpG5+/Wq3lEmb2L09qHZPf+Q2NE2exdsoRG0ZeKb0LPE0TMFFMgzFLnqdKtkuLPtDa2srKlStZu7bUWq7rOpqmoes6hssLQNij0ZwucOFXJ3DX210HirFsgZBbI57VCXkc1LVkyRdN8sXSz+kzC9azd22Iz9alCHnaPB4ZG3+Ot6nwkC4UWZ8qXbw9dfcxvLUizrUvL+OY6dVc+bWJ/OyFL0rv55A5m6LvSLu6GJK6GrZ8yCGH8NBDD3HvvfcyatQoAE488UT+9a9/8atf/cq+3S6778X8S/el/oqvdHr/owMuXvh/u/D6Obtx0i6jAZhS5eWDC/Ykdd0su9Qe4PyvjOf1c3bjb6fsTNBdev2Nh2/Hysu/wi1HTu70/qv9Tn77f1MBOG+/8Xzxk3154nsz7Zkko0aN4t577+WBBx7gpJNOAkBRFK655hp+//vf8+CDDzJx4kRABjALMRK43W5CoRD5fL7TgFMWDA1dxWKRdDpNc3MzLS0tZLNZ3G43FRUVRKNR/H5/u0BDCDE8ddZmXl1d3a7NHEozfxOJRLs280QiQSqVsoNOCTj7h4JJ9fLX2PG1axmz+F84s7HSG4zilpcTmabd7u7Mxhi7+F/s+No1VC9/TQJO0SdyuRzxeJxAIIDH48HpdFIsFlm4cCF77bormqqwW02IhQ0pJlf6uOJrE3nu9JlMqfLxs4O2aXdfb6+Ic8iUKACHTI7yTl2CQJvz369ODPNFU4YljRl2qPbj1BT2rQ3xydqkfZtjplfz9Cfr7JcVFBpTeQAa0wXCno31dqbqYMWKFYwfP36zn6PX6+3Qpi/EpqSSUwxJnQ1b1jSN733ve5xxxhlMmzaNM888kxtuuIEXXniBN954gwsvvBAozeN8P+Fh77ve57Wzd+v0/n98YC2/en0Fr30ZY+7Zu/HUgvWsiuc46L4PeebUGfbtKn1OvrFjFQfc+yHf3WU0P9h3PDe/toI73lzJS4ubOHKHqk7v/+x9anj0fw0APP5xA8993sRNR2xnzyQ5/fTTefDBB5k3bx633347L730EpWVlbhcLs444wx23XVXTj75ZK6//noZwCzEMOfxeAgEAuRyOVpbWzu8XRYMDR+6rqPrOqlUCqfTaVd4+nw+u1Irm81KiC3EEKUoCpqm9bgyU6q6y4ujkGLU8teoXj6XZNVUWqNTyIRryYTGYzjcHW6v6jm8iVX44nUEmpcQaFwswabYanfeeSfbb789EydO5KmnnuKf//wnd911F3fccQdOp5N7770XgIceeohf3HgLZ4+axP3v1pPVDU594lP7ft49bw9++Z8VQGkr+rnPLOL3763hkRN24rQ9xvKvz5qoT+Q4fGqUaw+dRLpgsLwlw9X/XkbRMPnNWyt59azdyOoGp7e539nTR9mt6gB//mgtf/nudI7ZeRSaonDm05/Zb1MMncWLF7cLOX/+85+zxx57cNBBBzF58mT+8Ic/sPfee/PGG2/029dUDA8ScoohqbNhy7W1tXz55Zfous7HH3/MJZdcAkBLS0u7SseCJ0JM14CuTxL3HB/isn8txTSxBzR/UN9KpsPtgry+LAbAC4ubeOi4nQBoSObZYZSvy/s/eHKUGzbMQFmfKuDbcGXMmkkyYcIEFi0qta0vXryYmTNn8sEHH9hzlkKhELFY6eO+++673HzzzRJyCjEMeb1eAoEAmUyGZDLZ7m2KohAOh3E4HCQSCdnWPcwUCgUKhQLJZBKXy4Xb7cbr9eL3+ykUCvYMTwk8hSg/Vpi5aZBphZmmaWIYhoSZQ5yCSbBxkT1qykSh4IlQdHgwVQeKoaPpWZzZmISaos+df/75HV738ccfc8YZZ7R7XWNjI2ddcCmfz7qy0/vZ+66Ny66sreipfJFvP/JJu9u9sLiZFzYsI2rrifnreGL+ug6v3/+eD9q9nMgVOfIPH3d6DJqeBWDu3LnstddezJs3j2uuuabD7b72ta9x4403dnofQlgk5BTDRigUIpVK2S+raufTGIrOLbd2OzXV7jqxBjR3psLrJJHV7dtVdHG7TXkcapddLUWHhy+//JI999yT//znP+yxxx6sWbOGWCxGoVDgmWeewe12c9pppwEygFmI4crn8+H3+0mn0+0e26BUuR4OlzZdyoKh4S+fz5PPl1q8XC4XHo8Hv9/fIfCUFlUhBpaEmaItBRNXVlppRflxZmOoeq7TSuPBpuo5nBt+b5544onN3vbqq68eiEMSQ5yEnGLYaG1txe/32y93Vd1iKlv+sS8UDRSlND7HGtDcmVhWZ7uqjYOcW7q4XU+YqoMHH3yQK664gm9/+9s0NDTQ1NTEvvvuS7FYZPbs2ey0005ccskl/PSnP93qjyeEKD9+vx+fz0cymSSTaV9DLguGRjYr8FQUxQ48A4EAgUCAQqFANpsln89L4ClEH5IwUwgxlCmYeBOrSFVM6t2yrP5ilo6rjI5IDAMScopho66ujkmTJuFwOJg2bRqLFy/u9HaKueUnnO+vauXASRW8vizGbjUhfvLcF53e7r1VCS766gQADptSyVsrOi4E6UxW3xiidjg+QyeWiPGjH/0IVVW55ZZbeP/999l1113thSMtLS0EAgFABjALMdwEg0Hcbjetra1ks9l2b7Pa163FE2LkMk3TruBUFAW3220vqDJNk3w+bweeQoju6U6YaW0vlzBTCDGUeON1pCITQSmjZYamgS9eN9hHIYYZCTnFkNXZsOVHH32UBx54gFwuZ5ezH3bYYZxwwgnU1tZy7733csbFl7NDtY87vjmVqVU+XjxjF37y/FLWJPKcs08N17y8jFvmruCPx+/EtYduHNAc8Tp4/LvTmTk2wN9PncGtc1fwwuJmnlvUxOvn7EYso3PyXxYCpY3r39t1DFV+FzUhNydteL3l1aXN7Fsb5u0VcU6YMYof7DeeKZWlY7no9Rz77bcfp512GqZp8vDDD5NOp3nnnXf4xje+wQMPPIDT6eS2224DkAHMQgwjoVAIl8tFa2trhxmbsmBIdMU0TbLZLNlsFlVV7cAzHA5jGAb5fJ5cLieBpxAb9CTMzGazdpBZLBYH+ciFEKJ3gs1LaNz2oME+jPZUjUDzksE+CjHMKLvssov0M4kRxURh4cE3DOpMkmq/k198fVt++Lf21aaqnmPaK5f3qGT/2muv5cYbb+xQ8SWEGFrC4TBOp5NEItEujGq7YKiz8FOIrmiaZgeeDocDwzDs6s9CYevHqwhR7noSZratypQwUwgx3JgofH7AFRQ8kfJoWTdNnNkYO7x+gyzmEn1KQk4xIn2x5w/KciaJv+VLtnvv7sE+EiHEAGobYsbj8XbhU9sFQ4lEQloiRa9pmobH48HtdqNpmt1ua7XcCjGUKYrSLsCUMFMIITpaN/Eg1k49EpTOF/QOKNNgzOJ/MWr5a4N9JGKYkXZ1MSLJTBIhRDmwAk5N0zpsSZcFQ6IvFYtFUqkUqVQKh8OB2+3G4/Hg8/nsZSm5XE5CH1HW2oaZbf+vqqUTdmkzF0KIrkXr59Ew5XDMMgg5FdMgWj9vsA9DDEMScooRSWaSCCEGm6qqhMNhVFUlFou1Own3er34/X7y+bwsGBJ9Ttd1dF0nlUrhdDpxu932z5yu62SzWXK53JAP1k0UCp4IRacHU3GgmDpaIYszG5PWuDLXkzAzk8lImCmEEN3gKKQYveR51k49enA7Gk2TMUuex1GQGfOi70nIKUakQONinJmWsptJEmjsfCO8EGJ4UVWVSCQC0CHglAVDYiAVCgUKhQLJZBKXy4Xb7cbv9xMIBCgUCuRyObLZLKZZ/qGgiUKyaiqt0SlkwrVkQuM7nb+t6jm8iVV443UEm5cQaFwsoecgkTBTCCEGVvXyucTHzCQTrAF1ELoajSLexCqqls8d+I8tRgSZySlGLJlJIoQYDNacTdM0icfjdrWcLBgS5cRaWORyuQDswDOXy5Vd4Kk7/TTX7EVT7f9v787jo6rP/v+/zuyTTPaEnRgRwiogirsFK1q36q2tWtwVsVqVX6u2tnXF7Yuttr2LlarF9aZWtNJWrFXBgooKrqwCQQgh7FlmkklmJjNzzu+PYYaEsJOVvJ+PBw+SyZlzzkyWmXnP9bmuU4h6c8CMJx7b9/YmpmWBZYLNjjNUTV7ZR+Ru/AxHtK7tTrwL2d8wc9d+mQozRURaXji9GyUn35FYtt6Wr4UtE8MyGbDgcTz129vuuNKlKOSULivmTOebsfdh2dq/oNkwYwyeN1kl+yKHOYfDQVZWFvF4nEAgkAqLksGnYRgEAgENgpEOwzCMVODpdDoBaGhoSAWe7cnCYHvRGLYOOGdHfzHj4FZnWFZib5ZJ95K3KSidr8rOg6QwU0Skc/B3H07ZiKs46MfOA7XjsbZw8ctkb13S+seTLkshp3Rp24rGdoieJD1Xz6ZAVZwihzWn00lmZmazgNPlcpGRkYFpmk0qO0U6mmTg6fF4cDqdWJaVCjsbGhra9FzC6d3YcPR4Qpl9W/Yx3LLw1myg79JX8NRta7n9HmYUZoqIdH5VvUZTPuzSxCetWdFpJZ7b9ln2KrmbPm+944igkFO6OAuDNSdOaveeJP0XTlXViMhhLDkpPRqNEggEUpdrwJB0VjabLRV4OhwOTNNMBZ7RaLRVj+3vPpwNw6/Awmidx24zjoFF3yUzuny1ic1maxZk2u12hZkiIocJPabK4UYhp3R56kkiIq3J7XaTkZHRLMj0+Xx4vV4NGJJOz263p5a0Nw48w+Fwi7deUNVJ61CYKSLSdbXq6ohAWWJ1hF7vShtRyCmCepKISOvweDz4fD4ikQi1tbWABgzJ4c3hcKQCT7vdTjweTwWehxqIJR6rr058osfqg6IwU0REdsfCoKJoDFtasM91j5K3yVefa2ljCjlFdlB1iIi0JK/Xi8/no76+nrq6xMRmDRiSrsTpdKYCT5vNRiwWSy1pP9DQTKsuDsz+hJmNA0yFmSIiAonhvFW9R1NZeCpRbw6Y8cTj7t4CT8tKvMa12XGGqskv+4icjYs0VFfahUJOkUbUk0REWkJaWhrp6elNlqJrwJB0ZU6nE4/Hg8vlwmazEY1GU4Hnvn4X1D97zw4kzGz8v/7+iIjI3lgYBPOLqc0dQCirkFBmH0yHu9l2tlgEb005aYEyfFUl+CpWd7jHSulaFHKK7EI9SUTkUCR7bQaDQUKhEKABQyKNuVyuVOBpGAbRaJRwOEwkEsGymj8t3VY0li3F57fNEvU9sSx6rp5NQem8djm8wkwREWlPFgZRTzZxhwfL5sAwY9hjYZxhv0JN6VAUcorshnqSiMjByMjIwO12EwwGCYfDwM7Qs/GydRFJ9Kd1uVy43W5cLhdAKvBsaGhIBHfOdL4Zex+WzdHOZwuGGWPwvMmtuvxOYaaIiIjIwWv/Z4wiHZCBRUHpPHI2fnZoPUnCfvUkEekiMjMzcblcqWFChmGQmZmJ0+mkpqZGA4ZEdmFZVmrJumEYqf6dGRkZADQ0NLCu2/E73mxsf5Zho6r38XRrgWpOm82WCjD3FWYme5gqzBQRERHZO1VyiuwH9SQRkb3JyspKhZkNDQ0aMCRyCGw2W6K60+1h0TGTiDgz2nepepJl4Qz7GfTBI/v92N44zGwcaqoyU0RERKTlqZJTZD8YWGRUrCKjYhWgniQikmAYBllZWdjtdgKBANFotMmAIb/fr7BC5ACZpkkoFGJbeiERV2Z7n85OhkHUm0Mwvzj1fCBpd2Gmw+HA2BHOqjJTREREpPUp5BQ5CAYWrnB1e5+GiLQjwzDIzs7GZrOlqjUbDxiqra3d7RAVkcOdz+dj2rRp9OvXj6uvvppvv/0WgKuuuorvfve71NfXc//991NRUUFRURH33nsvdrudp556ikWLFpGbm8uUKVNoSMun2vBx2V+XE47tDAPTnDZeumwIBT4Xb66o4PEPyjgix8PCW45j+bZE39vLZiyjoi7Kz07ry0VDC6hriHPda9+wpbaB5y4ZzJBu6dRF4/x7ZSVPfFDGqUVZPHXRQPLSnPR+ZMFub9czPxjEz99cRX3BQC4/qZj/+Z//wWaz8dxzz7Fy5Up+8IMfcMoppxAKhXjkkUfYtm0bw4YNY9KkScTjcb744gumTp3KsGHDGDVqFC+99FLrfzNEREREupCO0eRIRESkE7HZbGRnZ2MYBn6/n1gshs/nw+fzEQqFqKmpUcApXVY4HGbSpEnMmTMndVleXh6nnnoq1113HU899RQTJ04E4NZbb2Xy5Mnccsst3HzzzQCce+65vPPOO5z9v3NYVF7DRUMLmux/wvG9eHtVJWP+/CXfPSqHXpmJoUUfrPNzxjNfccYzX1FRF6W7z8W5g/L5zp+/5L5313LPd4t27uP1bzjjma944oMyAJZsDnLCk59THth979wjcjzETYtAxCSnaAhjx47ljjvu4LbbbuPzzz/HbrczatQorrrqKv73f/+Xyy+/nFAoxBVXXME999zDtddey9ChQykoKGDZsmUce+yxqSpPEREREWkZCjlFREQOgN1ubxJwmqZJVlYWHo+HmpoaTVCXLi8Wi1Fd3XS1Q8+ePVm7di0AK1eu5JhjjgGgoKCAsrIy6urqCAQCZGdns27dOnwZGYQz+5DjdVJRH22yr5MLs3h3dRUA762p4qTCrMTlR2Qx78ejePh7/YBEMLliaxCALzcFOfXIbCAxH/DpiwfxzoSRDO/pA6AmEicU3fPS8e8Pzuf9NdVgGJw8YgjBYJApU6Zw1113AZCfn5+qWG18+9auXUtGRkaqF2coFEpdPnTo0AO9a0VERERkLxRyioiI7KdkwGlZFn6/H8MwyMnJweFw4Pf7NUFdZA/Ky8sZMmQITqeTE044gaysRDCZHMADEAwGyczMZNmyZZx9znl8dedpHNcnk7lrqprsK8frpCYSB6AmHCM3zcnmmgjFv/2EsU9/STefi4uHFfBtZYhje2fishuM659DrtcJwM//XcKp075g0r9W8+eLBu7X+Q8qSGNtVSKgLMhKIzOvgJtuuoklS5bwox/9aI+3b+7cuTz++OPMmjWLxYsXEwwmQteNGzfSr1+/Q7hHRURERGRXCjlFRET2g8PhIDs7m3g8jt/vbxJ4VldXa4K6yF74/X5ee+01pk2bximnnMK6desAmgze8fl81NTUcM011/Di6/9ixB8W8a8V27nx+N5N9xWOkum2A5DpcVBVH6UhblG/oxJz1rLtDO/po7I+ytMLN/KfCSM5e2AeK7cnqqyr6hO/q6u212MBtgNcNR4IxVj41TIAFi1aRL9+/fZ4++68805uuOEGLrzwQvr166dgU0RERKQVafCQiIjIPjidTrKysojFYgQCATwejwYMiRyg2bNnM3v2bI499lj8fj8AFRUV9OnTh6qqKrKyslKXV9UkKh4r66N097mb7Ofj9QHGDcjl+c83M65/Ljf+fSU+l51gQ6K689SiLFZurwfg5S+38PKXWxjTL5uKusSy9wy3ndpInIJ0Jy67DXM/fn1XVdTTL9fLFxtrWbDez1lD+gMwcOBANm7cuMfbZ1lWqkdvbW0t6enpAPTu3Zv333//4O5IEREREdkthZwiIiJ74XK5yMzMJBqNEggE8Pl8eL1e6uvr1X9TZA+mTp3KwIEDKSoq4vXXX+fNN99kypQp5OTksHnzZqZMmQLAn/70JyZPnozdbmfatGkAzJw5k8mPPsb4rL7E4hZX/G05ANMuGsjNs1Yx/bPNvHzZEK49ridvfVPJxpoIZxfn8uBZ/aiPmpRWh7jvvUQl5V/HD6XA56KsOsyt/1wFwMuXDSEnzYndMPjFv9cAieXo/3tBMcX5abwzYSR3vb2GrzcFU7fnzRUV3DX2CF5buo2lW+rYkredZ599loaGBu6++26A3d6+Z555hieffJJYLEZpaSlLly4F4KijjmLq1Kmt/W0QERER6VKMkSNHqvxERERkN9xuNxkZGamKzczMTJxOJ8FgkHA43N6nJ3LYavDksHLMPe19Gk08+4NB3PnWGgLhGIPmP4wrXL3vK+3GsGHDGDVqFC+99FILn6GIiIhI16ZKThERkd3weDz4fD7C4TChUIjs7GxsNhuBQIBoNLrvHYjIQXE4HHiMCLZ4A6bd1d6nkzLx7ysBsMUiOA8y4ARYtmwZy5Yta6nTEhEREZEdFHKKiIjswuv14vP5qK+vp6GhgezsbEzTpLq6usmgFBE5dE6ns8k/wzCwLAtf3WZqMgrBOMDJQK3JsvDWlNOBzkhEREREdlDIKSIi0kh6ejppaWnU1dVhmiZZWVkaMCTSQgzDaBJoOhwODMPANE2i0Sh1dXVEo1FisRiu3LXg6wOGvb1PeyfLJC1Q1t5nISIiIiK7oZBTRERkh+RQoWAwiN1uJz09XQOGRA6BzWZrFmoCxONxotEo4XCYaDRKPB5vcj2Xy0X38EYqbB0o4ASw2fFVlbT3WYiIiIjIbijkFBERATIyMnC73dTW1uJ2u3E6ndTW1mrAkMgBsNvtTUJNuz0RUsZiMaLRKPX19USj0d22fbDZbHi9XtxuN3a7HW9NKa6wnwZ3VsdYsm5ZOMN+fBWr2/tMRERERGQ3FHKKiEiXl5mZicvloq6uDq/XqwFDIvtp1ypNm82GZVnEYjEikQjRaJRoNLrXVg9utxuPx4PL5cI0TSKRCKFQiHg8Tu76j9hSfC50iC6YFnllH2GgthUiIiIiHZFCThER6fAsDKKebOJOD5bhwLBi2KNhnGH/IQcOWVlZOJ1O6urqSEtL04AhkT3YVz/NUCiUCjX3xW634/F48Hg82Gw2otEoNTU1RCKRJtvlblzE1gFnYxm21rpZ+82wTHI3Lmrv0xARERGRPVDIKSIiHY6FQTC/mNrcAYSyCgll9sF0uJttZ4tF8NaU4w2UkVFVgq9i9X6HnoZhkJWVhd1uJxQKkZ6engpaNGBI5OD7ae7NrlWb4XCYcDi8x304onV0L3mbLcXnt++SdcuiR8nbOKL17XcOIiIiIrJXxsiRI/VKTkREOoSYM52q3sdTWXgKUW8OmHEwbHsPNywLLBNsdpyhavLKPiJ342c4onseFmQYBtnZ2dhsNhoaGvB4PBowJF3evvppJv8daJXzrlWbDQ0NhMPhZlWbe2JhsObESYQyekN7DCIy43hryum/cKqWqouIiIh0YAo5RUSk3VkYbC8aw9YB5+xYlmocXNWWZSX2Zpl0L3mbgtL5zUIJm81GVlZWapmtw+EgGAxqwJB0OQ6Ho0mo2bifZuNQ82Arm5PBptPpTFVthkKhg2oFEU7vRsnJdyT+PrTl0nXLxLBMBix4HE/99rY7roiIiIgcMIWcIiLSrsLp3dhw9HhCmX1bdjmqZeGt2UDfpa/gqdsGJCrKsrKyUpsYhkFNTY0GDMlhb2/9NHcNNQ+Fw+HA4/HgdrsxDCPVq7OhoeGQb4O/+3DKRlzFQb8JcqB2vGlSuPhlsrcuaf3jiYiIiMghUcgpIiLtxt99OBuGX4GF0TrLUM04BhZ9l8wgr2I52dnZWJaVCndqamoOqJ+gSGexaz9Nu93eZEhQ8l8sFjvkYxmGkeq16XQ6icfjqV6bLT3Aq6rXaMqHXbrjwK1Y0WklzrvPslfJ3fR56x1HRERERFqMQk4REWkXbR1WDCp9ix4VS5pMctaAITlctFY/zb1JVm16PB6AVK/Nlqja3Ju2fHNEFZwiIiIinYemq4uISJvzdx9O+bDLEp+09rJTwwaWxcqi83FYMTI3f00wGGzdY4q0sr3104xEIofcT3NPDMNIBZsOh4N4PE59fX2rVG3uSfbWJXg+3tKKbS7KE20u1INTREREpFNRJaeIiLSp9hsgYmFYcQ0QkU7HMIxmoaZhGFiW1WzpeWtVJzudzlSvTUhUbYZCoXbtZ2thUFE0hi0tOLCsR8nb5O9mYJmIiIiIdHwKOUVEpM1YGKw5cRKhjN6ts8x0X8w43ppy+i+cqhBDOiybzdYk1Gw8JKil+2nuza5Vm7FYLNVrsyO1eog506nqPZrKwlOJenPAjCfeQNlb4GlZiVYWNjvOUDX5ZR+Rs3ERjmh92524iIiIiLQohZwiItJmthWNZUvx+W0zGXlPLIueq2dTUDqv/c5BpJE99dOMx+NNQs22GpK1a9VmJBIhHA63a9Xm/rAwCOYXU5s7gFBWIaHMPpgOd7PtbLEI3ppy0gJl+KpK8FWs1pseIiIiIocB9eQUEZE2EXOms3XAOe0bcAIYBlsGnKOqLWk3++qnmRwW1FY9LiFRPZqs2rTb7cRiMerq6jpc1ebeGFhkVKwio2IVkAg9o55s4g4Pls2BYcawx8I4w36FmiIiIiKHIYWcIiLSJqp6H7+jb177swwbVb2Pp5uqOaUNNA40d+2nmexr2Zr9NPfG5XLh8XhwuVxAomqzpqam1ZfCtwUDC1e4ur1PQ0RERETaiEJOERFpdRYGlYWnAO1cxZliUFl4KgUaMCItzDCMJoHmrv006+rq2qSf5t7sWrUZjUYJBoNEIpFOU7UpIiIiIrIrhZwiItLqgvnFiYEgHYVhEPXmEMwvTi1tFTkYNputWagJO/tpJntZtlU/zb1xuVx4vV6cTieWZaV6bR4OVZsiIiIiIgo5RUSk1dXmDkhMPG6Piep7YsYJ5g5QyCkHZNep53a7HcuyUqFmfX19m/fT3BubzYbX68Xj8WCz2VJVm+FwuL1PTURERESkRXWM5mgiItKh+Xw+Xn75ZRYsWMBRRx2Vuvzxxx/nL3/5Cy+99BKjRo0CIC8vj6eeeornn3+ec889F4D6rEIwbPxizBEsvPW43R5j0il9ePeGkcy98RiKcjwAvDfxGCrvP43zBuWltjvliCw+vPlY5t80imHd05vs49/XjeA35/bf7f7/On4odpvBNcf2YOWdJzL3x8fxx9uvAWD48OE8++yzPPvss/zjH//gjjvuSJzTpEm8++67/OxnP0vt55prrmHIkCEHdP9J5+V0OklLSyMrK4u8vDxycnJIT0/HZrMRiUQIBAJUVlZSXV2dWvLdEQJOt9udOmePx0MkEqGqqgq/36+AU0REWoWFQYMnh1BGT+oz+xLK6EmDJwerw7QrEpHDnSo5RURkn8LhMJMmTeKnP/1pk8t/+ctfEovF6NmzJ/fffz833XQT1157LS+++CKff/45zz33HO/NmUs4sw8+t4Oje6Tvdv+jemdQmO3hrL983eTyq/+2nIkn9G5y2UPf68f3X1hMhsvOtIsGcv4LSwA4+YisPZ7/aUdms3RLkLiZ6Df4xwUbeOqTjdhiEYYCS5YsYeLEiQBMnjyZefPmATBjxgw+/vhjTjvttNS+/vGPf3DnnXdy77337utuk05mX/00k1WaHXV5t91uT/XatNlsNDQ0UFNTQyQSae9TExGRw5CFQTC/mNrcAYSyCgll9sF0uJttZ4tF8NaU4w2UkVFVgq9itXqii0irUMgpIiL7FIvFqK5uPqU4Gfakp6ezZs0aAIYNG8bvfvc7LMtixYoVFA0ZyRcON5NO7cufPinnfy8obrafC4bk43XaeG/iMazYGuRnb5ZgWrC5tqHJdh6HjbgF/lAMfyhGTpoz9bXbTu7Dnz4pZ0y/5r0/LxySz6uLt6Y+v/nEPlw6vDvTPiln8YKc1ARmh8PBsGHDeOCBBwCorKykqKioyb4CgQAFBQXYbLYOUbEnB68z9dPcG7fbneq1aZom4XCYcDjc4c9bREQ6p5gznarex1NZeEqi57oZB8MGxu4rNk2Hm7qcftRlF1Fx5Ok4Q9XklX1E7sbPcETr2vjsReRwppBTREQOyfTp0yksLExVNjocjtSE5traWjJy88h02zm6ezqPvl+6231097mImRZnPvsVU845ikuO7sarS7Y12y7H66A2vLOKLmZaOO0GJxZmsXhzkGDD7kOdQQVprK1KLNH9x/IKXvpyC+lOO+9NPIY7ZvYkuD4Rcp544oksWrRonxOm/X4/PXr0YNOmTXu/c6RDsdvtTUJNuz3RIzYWi3XIfpp7Y7fb8Xq9uN1uVW2KiEibsDDYXjSGrQPOwTJskFyGvj891w0DjMR2UU82W4rPY+uAc+he8jYFpfNV2SkiLUIhp4iIHJIJEybQo0cPfv/73/Ppp58Si8UwDAPLssjIyMBfG+L/O7svT35cvsd9+EMxPiuvAeD9NdWcUrT7pef+cIwMz86HLofNIBq3mHRKH65/7RtG9c7Y5/kGdoSkwYY489dW06/oSJasXwHAuHHj+Ne//rXft106tmR1ZjLUtNlsWJZFLBYjEokQjUaJRqP7DLU7kuRy9MZVm6FQqFMEsyIi0nmF07ux4ejxhDL77rFic78ZBmBgGTa2FJ9PoMcI+i59BU9d8ze4RUQOhAYPiYjIQUsu762vr6e+vh6A5cuXc9xxx2G32xk8eDBr1q6lf14ad3+3iH9fN4IB+Wn86vQjmuzn4/UBRvT0ATCil491VbsfjBKKmjhsBlkeB32y3FTXRwE4Ks/Lq5cPY8o5/bl4WAHnD85rcr1VFfX0y00MM8pwJ6oIbAYc3zeT8o3lqdsydOhQvvrqq33e7pycHLZu3brP7aTtGIaBy+UiPT2d7Oxs8vPzyc7OJi0tDcMwCIVC+P1+Kioq8Pv91NXV0dDQ0CkCTofDgc/nIy8vD5/Ph2maqYFHdXV1CjhFRKRV+bsPp+TkOwhl9D70gHNXhkEoozclJ9+Bv/vwlt23iHQ5quQUEZH9MnXqVAYOHEhRURGvv/4677zzDn/605+ARG/DJ598EoAXXniBhx56iFtuuYXXX3+dWF0N18xckdrPwluP4//9dz0A0y4ayM2zVvHWygrOH5zP3BuPoao+yhWvLAfgLz8cxJgjc7hwSD5Du/v4zfz13PfuWmZfNwLLsrj1n6sBGPW/nwEwpl825w3KZ/Y3lU3O/Z/LKxg3IJfPymv56al9OXtgHgbwt8Vb2VJehgs44YQT+Oyzz5qEXuPHj+f8888nJyeHbt268atf/YqsrCy2b9+ufoftbNd+mna7HcMwUv00k5WaHXVI0L4YhoHb7U5VbcbjcUKhEOFwWKGmiIi0mapeoykfdmniE6OVaqRsdizLpGzEVZjLXiV30+etcxwROewZI0eO7PglDCIi0mlZGCw/45HdTttsS6+MH8qVr65ITVgHsMcbOO2rxzHjcWKxGPEd/8disT1W+F1zzTV8/vnnLF++vK1OXdh3P83kv84eADocjlSvTYCGhgbC4TANDQ37uKaIiEjL8ncfTtmIqxOftHQF5+5YFmBRuPhlsrcuaf3jichhRyGniIi0um9H/4S6nH5t8wR5f1kWPv86hi59DofDgd1ux+FwYOw4R9M0U4Fn4/BT2kbjXpq79tNsHGp2huXm+2IYRqrXpsPhIB6Ppyakd/bQVkREOqdwejdKTr4jMWCotSo4d8cyMSyTAQsex1O/ve2OKyKHBS1XFxGRVucNlFGXXZSaqtkhWCZe/3pCoVCTi+12eyrwdDgcuFyu1FJoy7J2G35q6fqhMQyjWaiZvL+j0SihUCgVah5OnE4nHo8nVbUZiUQIBoOH3e0UEZHOxcJgw9HjsTDaNuAEMBJvam44ejz9F07V1HUROSAKOUVEpNVlVJVQceTp7X0aTdns+KpKml0cj8eJx+PNlgc3rvZ0OBx4PJ7UkmnLsppUeyY/VhXe7iX7aSaDzWQFrWmaRKNR6urqOnU/zb3ZtWozFotRV1dHOBw+LKpSRUSk89teNKZlpqgfLJudUFYhFUVjKCid1z7nICKdkkJOERFpdb6K1ThD1UQ92R1jybpl4Qz78VWs3u+rJAPMSCSSuixZgbhr5afNlqh6ME1zt+FnVwuz9tRPMzkkKBwOE41GD+uKWKfTidfrxeVyAaraFBGRjinmTGfrgHPa//maYbBlwDnkbFyEI1rfvuciIp2GQk4REWl1BhZ5ZQvYUnwu0AFCTizyyj465CVQyeXUuwZVNputSfiZXJac7PeZrBbdNfw8XOytn2bjqeeHe6WrzWZLVW3a7XZVbYoIFgZRTzZxpwfLcGBYMezRMM6wX8typUOo6n18og9nB2AZNqp6H083VXOKyH5SyCkiIm0id+Mitg44u0M8cTYsk9yNi1pt/6Zp7nYadjL0TP7vdrvxer2p/pO7Cz87Q3Vj40BzT/00u1IFq8vlwuPxpKo2k0OEDqcgW0T2j4VBML+Y2twBhLIKCWX2wXS4m21ni0Xw1pTjDZSRUVWCr2K1Qk9pcxYGlYWn0DHekAYwqCw8lYLS+fp9EJH9opBTRETahCNaR/eSt9lSfH77LoGyLHp9+067LH3aU2iZXOqeDD+9Xm9qyXuyArLxkKP2rII0DKNJoNmV+mnuza5Vm9FolGAwSCQS6TLhrojsFHOmU9X7eCoLTyHqzQEznhjgsofHP9Phpi6nH3XZRVQceTrOUDV5ZR+Ru/EzHNG6Nj576aqC+cWJn9eOwjCIenMI5heTUbGqvc9GRDoBhZwiItJmCkrnE+gxglBGb7C1w6R1M46vfgsDa5cR9vmoq6vrEAFUsnKzsWS/z8bhZ3v0+2y85D4ZakLX6qe5Ny6XC6/Xi9PpxLIsIpGIqjZFujALg+1FY9g64JwdKxd2hJr785hnGGAktot6stlSfB5bB5xD95K3VckmB8zn8zFt2jT69evH1VdfzbfffgvApZdeyve//30AXnjhBebOncu4ceO45ZZb8MedHPv0CrDZsRnwzA8G0T8vjS831nL77KbDGk8tyuKpiwaSl+ak9yMLUpf/v7OP4qQjslhfHWbC698QMy1eu3IYeWlOPA4bd739LR+u8zOwII1nfzCIuAWlVSGue+0bbAa8cOkQemW6Ka0O8eM3VhGPxQjmDkiFnJdccgnr169n0aJFPP/888Tjcex2Ow899BBr167lzjvv5KWXXmLbtm1tdE+LSEfS/msGRUSkyzCw6Lv0lcQLNauNKxEtEwOLXl+9TLC2BrfbTU5OTmpJcUfTeLl3MBjE7/dTWVlJZWUlgUCAUChEPB7H6XTi8/nIzs4mPz+f3NxcsrKySE9Px+12p0LJ/ZWsJM3MzCQvL4/c3FwyMjJwOp1Eo1FqamqorKykqqqK2tpawuFwlws4bTYb6enp5OXlkZWVhWEYBINBKisrCQaDCjhFuqhwejfWnDiJLcXnY9kce63c3CfDAMOGZXOwpfh81pw4iXB6t5Y9YTmshcNhJk2axJw5c5pcfumll3LttdcyceJErr/+egA+++wzLrnkEky7K/FzC5w/OJ9NNQ2MffpL0l12TizMbLKfJZuDnPDk55QHdg5kHN7TR+8sN2Of/pKV2+v44dGJn9nLX1nOd5/5ivGvLOee7xYBcPOJvXl4bimnP/0lMdPipMJMLhpawLqqEOOe/YqV2+q5eGgBGDbqswpTxxgzZgyLFiVaDk2cOJEbbriBJ598kiuvvBKAf/3rX1x22WUteE+KSGeikFNERNqUp24bfZfMSHzSVlWUO47Td8kMPPXbCYfDVFdXE4/HycrKIiMjIzUUqKNL9vusr6+ntraW6upqKioqqKqqoqamJjXUxu12k5mZSU5ODvn5+eTk5JCZmUlaWhoulys14dzpdJKWlkZWVhZ5eXnk5OSQnp6OzWYjHA4TCASorKykuro6tfz6cB8YtCdutzt1P3k8HsLhMFVVVfj9fsLhcHufnoi0I3/34ZScfEdipUJLP54YBqGM3pScfAf+7sNbdt9y2IrFYlRXVze7vLy8HLfbTVpaGrW1tQAEAgGisTiW3ZX6+T2pMIv3SqoAeGd1JScfkdVkPzWROKFo0+cDJze5TlXqOtF44nlYhsvO8q2J9gvfbKsj25t4IzbT7aAqFKNfnpfFm4MAfLWpltOOzE78/Gf2wQIGDBjAxo0bm9xGgPT0dNasWQPA6tWrGTFixEHcYyJyONBydRERaXPZW5dgLptJ+bBLEwFkaw4j2lEx2mfZq2RvXZK62DRNAoEAbrcbn89Hbm4utbW1ux0Y1Bnsrt+nYRjNhh3t2u+z8dCjSCRCQ0NDp70PWoPdbk/12rTZbDQ0NFBTU0MkEtn3lUWkS6jqNTrxeAat93hms2NZJmUjrsJc9iq5mz5vnePIYe+jjz7ijTfewGaz8eCDD6Yuj3qysRoF9DleBzXhRIgYCMfITXPuc9/ZXgebayONrrMzbvjvj0dRnO/l2pnfAPBuSRXvTDiG+8cdyVebalm1vZ5vttXxveI83li2nTP655KzIwQ1HW6inhyKioooLy9P7TMvL4/HH3+cHj168NOf/jR1ucPhwGazddk3ZUW6MoWcIiLSLnI3fYYtHmHD8CsSfSRbo0enGU8skV8yo0nA2VgkEiEajeLz+cjKyiIcDhMMBjtEr85D1Tjk3LWfZjweTz35T26TDEFN02zS5zP58eFwn+wvt9ud6rVpmmZqQnpXW5ovInvn7z6c8mE7lsa29ooAwwaWRfmwy7DFG/b4uCayJ+np6VxyySVceOGFOJ1Onn76aT766CMA4k5Pk2394RiZnsTzhiyPg6r66D733/w6O9u3nP70l/TNcjPrmuG8V1LFI987iol//4b5a/388YJizi7OZfY3lYzpl8N7E49hxdYgW4I733SNOzzNjldZWcl1113H0KFDue2227j11lsP/E4RkcOKQk4REWk32VuX4Pl4CxuOHk8os2/LvkC0LLw15fRd+gqe+u173dQ0TWpqappUdSaXZncmdru9yeTz5JL0WCxGNBqlvr6eaDS6x8oGm83WZNiR0+nE4/GklvLvOuE9+fHhwm634/V6cbvdqtoUkX0Kp3djw/ArgFZekdCYYYBlsWH4FXgWbN7n45tIY8k37SKRCLFYDKfTmVrRYRlNo4FP1gc4o38OH67zc9aAXF74YvM+9//J+gA/O60vL3+5hbMG5PLx+gAADptBzLQINsQJRhLPGwygoi4RnFbUR8naEY7+/K3EsvP7xh3Jf7/dudzesjlYv349xx13XGKfDkfqDdhgMNikbUzjN3JFpGtRyCkiIu3CwkgsjbLZ6bViFoGeI6koPGXHC0Xj4AJPy0rs2TLpUfI2+Qc4jTa5XNvn85GZmUkkEiEYDHbYJ8qNA83k0izLsojFYqkK1Wg0ut8VmMl+n7suV29c6Wm321O9vIDUUvddw8+Oep/tTnI5utPpJB6PEw6HCYVCneo2iEjbsjDYcPR4LIy2CziTjMTf+g1Hj6f/wqmaui57NHXqVAYOHEhRURGvv/46b775Ju+//z4vvvgiNpuNmTNnYlkWxx57LNffdAvF+Wm8M2Ek185cweyVlVw4tIB5Px7F15tq+bSsBoBpFw3k5lmrGFSQxv9eUJy6zl1vr+HrTUG21jYw78ej2OAP88SHZbgdNv59faJHpt0wuOedxJT3x+avZ9pFA4mZFv5QjMfmrae7z8WM8UMxLYv311Tz4Tp/6rYYZozVq1fTp08fAPLz83nkkUdSQedjjz0GQHFxMYsXL27De1lEOhJj5MiRelQUEZFWZ2EQzC+mNncAoaxCQpl9MB3uZtsZZgwsC8vuBDO+7+m01o5J7TY7zlA1+WUfkbNxEY5o/SGdr8vlIiMjA6BDVHUahtEs1DQMI7W0PBloRqP7Xk7WUueza/iZDFqBVNi6a/jZUZa8OxwOPB4PbrcbwzBoaGggHA6rH6mI7JdtRWPZUnx+6y9R3xvLoufq2RSUzmu/c5DDRoMnh5Vj7mnv09ijQfMfxhWu5tJLL6W0tDQ1YX1Xd955Jy+//DJbt25t4zMUkY5AIaeIiLSqmDOdqt7HU1l4ClFvzv4HlzuW/xnxKGBg2ZsvPrDFInhrykkLlOGrKsFXsbpFK1oMw8Dn8+HxeNq8qtNmszULNSGxBCsZZiaDw47EZrPtNvxMLnlPhrKNw8+26vdpGEaq12ZymVuy16aqNkVkf8Wc6Xwz9j4sW/svijPMGIPnTT7kN/ZELAyWn/HIbt+Abm+2WIShc39NO76lICKdRPs/MouIyGHJwmB70Ri2DjgHK7kEHfZvwJBhpLZPvIi0MMwYeWULyNr8NTYzij0Wxhn2t+oyPcuyqK2tJRKJ4PP5yMnJoa6urknfp5ZyqP00OwrTNDFNs1lFqd1ubxJ+ulwu7HZ7m/T7TA5UcrsTL9waGhqoq6tT1aaIHJSq3sfveFxrf5Zho6r38XRTNaccIoNEL/O6nH7tW6G8qx091jvQGYlIB6aQU0REWlw4vVvLDRPaEXhaho2KI75DXc6R9F36Cq5w9T6v2lIaGhqorq4mPT2djIwM3G43tbW1hxQ4JieeJ/8daj/Nji450X3XYLFxtWdL9vs0DCPVazNZtVlfX6+qTRE5JBYGlYWnQIeJXAwqC0+l4AB7UIvsjjdQRl12ERj78YZ0W7FM0gJl7X0WItJJKOQUEZEW5e8+nA3Dr9gxjKGFXwQaBqGM3pScfAd9l8wge+uSlt3/XiSnd0YiETIyMlIT2PenqjPZT7NxsJmcZhqNRgmFQm3aT7MjSQaXjXue7q7fp8vlSvX7NE1zt+GnZVmpifDJqs1km4GueN+KSMsL5hcnWq90FIZB1JtDML+YjIpV7X020sllVJVQceTp7X0aTdns+KpK2vssRKSTUMgpIiItpqrXaMqHXZr4pLWW8tnsWJZJ2YirMJe9Su6mz1vnOHsQjUb3WdW5az/N5LLs5DLuurq6VE9Naa7x0KLGdu33mRwelFzybllW6n6ORCKEw2GFmyLSompzByR6S+9P65W2YsYJ5g5QyCmHzFexGmeomqgnu2MsWbcsnGE/vorV7X0mItJJdIxmMiIi0un5uw+nfNhlgNF6AWfSjh6f5cMuw999eOseazeSVZ1+vx+73U5ubi4ZGRmpCs+8vDwyMzNxOp1Eo1GCwSBVVVVUVlZSU1NDKBRSwHkQkiFxKBSitrY21VfTsqzU0vaGhgZM08TtdpOdnU1BQQG5ublkZmaSlpaG2+1O9TsVka7N5/Px8ssvs2DBAo466igA0tLSePrpp5k+fTpPP/00PXv2BKCoqIjp06fzr0dv5bv98wC46YTezL3xGObeeAyb7zmVC4bkN9n/DaN78fFPjuWjm4/lju8Upi7/f2cfxbwfj+LFS4fgsCWCpJ+d1pcPbhrF29ePoEeGC4BTjsji458cy4c3H8sj3+u329vw1/FDsdvt1Gcl9n/33Xfz29/+tsk2Z599Nu+//37q8ylTphz0fSaHNwOLvLIF0GFaH1jklX2kVgwist8UcoqIyCELp3djw/ArSExEb6N3/nccZ8PwKwinFbTNMXdIDrLxer0YhpGa2u12u4lGowQCASoqKqiurk4taW+pITpdnc1mIy0tjdzcXLKzs7Hb7QSDQSorK6muriYQCFBdXZ26/2tqaohEIqkenZmZmeTm5pKfn09OTg4ZGRl4vd4my+FFpGsIh8NMmjSJOXPmpC6LxWLcc889TJgwgRdeeIGrr74agFtvvZUHJk/mnOeW8MBZicDxzws3csYzX3HGM1+xLdjAeyVVTfY/Z00VJz/1BadO+4ILhuRTkO5keE8fvbPcjH36S1Zur+OHR3eju8/FuYPy+c6fv+S+d9dyz3eLAPj5mEKunfkNp037gtF9M+m5I/xMOu3IbJZuCRK3IJTZh549e1JQ0PTx0GazMW7cOLZs2ZK6bPHixZx00kktdj/K4SV34yIMq2P0rjYsk9yNi9r7NESkE9GzeREROSQWBhuOHr+jB2cbP6wYtqbHb41D7OinmZaWRlZWViocS09PxzAMQqEQfr8fv99PPB5PVQoeLgODOgqXy5UKKNPS0lLDoKqrqwmHw7u9v5O9Puvq6ggEAlRVVVFRUYHf70/16bTb7anvbV5eHnl5eWRnZ+Pz+fB4PKn+qSJy+InFYlRXNx1i19DQwPbt2wGaDH8rKCjg2221BOJ2qkMx8tKcqesc1yeDFVvrCEWbBkOl1Tt7NkfjFqYFJxdmpcLQd1ZXcfIRWRyR42HF1iAAX24KcuqR2QCs2FZPtteB3WZgtxnU77L/C4fkM2fHvkyHm6sn/JiXX365yTZnn302c+bMafI3cuHChZx+egfruygdhiNaR/eSt6G9n8dYFj1K3sYRrW/f8xCRTkU9OUVE5JBsLxrTMlPUD5bNTiirkIqiMRSUzjvk3SVDzeQ/h8Ox3/00k70609PTU706VcF58Gw2W2pCut1uTy39j0QiBx0iJ4c97dqr02azNZn0nhxglAw4k9Phk71Ckx+LyOHJ4XBw00038eCDDwKJvxFxpweAQDhGbpqDyvrE35EfHt2N15Zu2+O+/mdoPmurQlTWR8n2OthcG2myn28rQxzbOxOX3eD0o3LI9SYC1FnLtvH6VUcTiVnMXLKVQLjp35xBBWmsrUoEqUfmejBt9WzatCn1dZvNxllnncXPfvYzrrrqqtTl5eXl9Ou3++XvIgAFpfMJ9BhBKKN3+/SfNeN4a8rJL53f9scWkU5NIaeIiBy0mDOdrQPOaf/m9IbBlgHnkLNx0QG/42+325tMPnc4Eg+N8XicaDSaGl6zv2FlXV1dagJ7Tk4O9fX11Ne3fhWChUHUk03c6cEyHBhWDHs0jDPs73S9rFwuF16vF6fTiWVZqSFCrRkqmqZJQ0NDs8uToWfyf7fbnWpTkOwDuuukdwXbIp3fvffey8yZMykrKwMSfyMsI/H4kOVxUFW/8+/R2QPzeOC9dbvdz/CePm45uS8XvLAYAH84Rqan6X4q66M8vXAj/5kwksWbg6zcXgfAE+cP4PSnv2JddYi/X3k0g7ul8c223T+e3DXmCKb/7nncjS4777zzePfdd7WyQA6YgUXfpa9QcvIdWJbZtit1LDN1/M72/EVE2p9CThEROWhVvY/Hausl6ntgGTaqeh9Pt31UcybDzOQ/m82WCqui0Sj19fVEo9Em09IPVHIJZFpaWmrYTW1tbYuGdBYGwfxianMHEMoqJJTZB9PhbradLRbBW1OON1BGRlUJvorVHfJFg91uT1Vt2mw2otEotbW1RCKRdj2vZIi5q+R092T46fV6Uz09kz9Pu1Z9HsrPlIi0nRtvvJGNGzfy7rvvpi6rqKigsGc+W1x2crw7qziP7Z1Yqh6ONf/97pXpYtpFA/nhy0tTS9k/WR/gZ6f15eUvt3DWgFw+Xh8A4OUvt/Dyl1sY0y+birrEvi2gOhTFsnaEo+6mL91WVdTTL9dDZX2UohwvD9z+Y9IcBkcccQQXXXQRffv2ZdCgQZx77rn07duXX/ziF/zmN7+hT58+rFu3+1BWJMlTt42+S2ZQNuKqxNL1tnhDe0cg33fJDDz121v/eCJy2DFGjhzZ8V7piIhIh2dhsPI7dxP1ZLd/JSeAZeEM+xn0wSNNQrzGgWayv2LjJcuxWKxJ37WWZrfbycjIwOFwEAqFqKurO6T9xZzpVPU+nsrCU4h6c8CMJyos9vY9sCywTLDZcYaqySv7iNyNn+GIHtq5tAS3243H48HlcmGaJuFwuNMOajIMo0nwmfw4GX6aprnb8FNVViLtZ+rUqQwcOJDNmzfz+uuvs2jRImbPns3XX38NwJIlS5g6dSr9+vXj1/feTyTnCCa/t5Y5axK9PP/f2UexaEMNs5bvDGSmXTSQm2et4tkfDGJMvxw2BBJLym9+YxWrK+p57JyjOKEwiw3+MNe//g3RuMVfxw+lwOeirDrMrf9cRShqcuaAXCafeSRR02LV9npu/PvKJuf+nSOzOaUoi//33/UADJr/MEfkeLj99tv5+c9/3mTbGTNmcMUVVwAwfvx4ysrKWLBgQavcp3J4qeo1mvJhlyY+ac03tncMO+qz7FVyN33eescRkcOaQk4RETkotfkDWXfsje19Gs0MWPw8+bXrdttPs3Gw2daSVZ3xePygqjotDLYXjWHrgHN2VM8aBxcuW1Zib5ZJ95K3KSid3+aVnbtWbTY0NBAOh9u9arO17NrvM/mx+n2KdC4WBsvPeGS3VfPt5ZXxQ7ny1RVYDWGGzv31fo3gmzJlCr/61a/0BovsN3/34WwYfkViyGNr9Og044kl6ktmkL11ScvvX0S6DIWcIiJd2KRJkxgxYgSbNm1i8uTJ2O12nnzySQA8Hg8Oh4Px48czbtw4brnlFurr61OVIFsGfp9H7/gx/fPT+XJjLbfPLmmy7xtG9+L60T0xLZi1fDtPfFBGN5+Tv181nGjcJG7BVX9bzpbaBrI8Dp76n4F0y3CxpqKem2etSpzfKX04f3A+dpvBhNe+aTKpFuCR7/Xj+c83s7YqxAuXDqF3pottmzbw7IN3EolEOO2007j00ksJh8Pcd999bNu2jeOOO47bbruNWCzGn/70J7788kvOOussTNNkzpw5rXp/H2xVZzi9GxuOHt/yA54sC2/NBvoufQVP3Z6HZrSUZD9Lp9PZ6as2W8Ku/T6THwPN+n0mw8+uel+JdBTfjv4JdTn9OsYKhiTLIr16LUd99lR7n4kcxlr1uUigLPFcREvUReQQdYxGaiIi0uaKi4vp1q0bEyZMoLS0lHHjxhGJRJg4cSITJ05k5syZ/Pe//wXgs88+45JLLmly/TGnncqmmgbGPv0l6S47JxZmNvn6nDVVnPzUF5w67QsuGJJPQbqTiroo3/nzF3z3ma/4vy83c/3oXgA8cOaR/PaD9Zz57FepgHNU7wwKsz2c9ZevOeOZr5oFnGlOGwML0lhTGeKioQWsqwpxxjNfsXRbmGOOOYb6+nouu+wybrjhBqZNm8bEiRMBuO2227jlllu45ZZbuPnmmwGYO3cu559/fsvfybuIx+P4/X7q6urwer3k5OTgdDr3eh1/9+GUnHxHYsJpS7+oNgxCGb0pOfkO/N2Ht+y+d7Db7fh8PvLy8sjMzMSyLAKBAJWVldTV1XXp0C4ejxOJRKivr6empoaqqioqKiqorq4mGAzS0NCAYRh4vV6ysrLIzc0lPz+f7OxsMjIyUoFxcjm8iLQ+b6Astay2w7BM0gJl7X0Wcpjz1G2j/6d/pOfq2RhmLPF7cLDVwDva6BhmjJ6rZ9N/4VQFnCLSIvSsWESkixoxYgSffvopAB9//DEjRoxo8vUzzzyT9957D4BAINBk+ayFwbFD+vPejp5k76yu5OQjsppcv3EoGY1bmBaY1s7nwz63gxVbgwCM7JXBzSf2Ye6Nx3DhkHwALhiSj9dp472Jx/C/FwzAtku+d0b/XD4tqwGgX56XxZuDYBgsqjAYNWoUhYWFrF27llgsxuLFiykuLgYSoVswGCQcDmO328nOziYejxMOh8nNzT3o+/NAhEIhqqursSyL7OxsfD7fbrer6jWashFXJZant8byMACbHcuwUTbiKqp6HdciuzQMA4/HQ3Z2Nrm5ubhcLkKhEJWVlQQCgd1OMZcEy7KIxWKEw2Hq6upSgXBFRUUqII/FYtjtdtLT08nOziYvL4+8vDyysrLw+XypKmyjI1WaiRwmMqpKWu/v8cGy2fFVlex7O5FDZGBRUDqPwfMepMfqt3CG/YkvmPF9B56WldgOcIb99Fz9FoPnTaagdF6HHIgoIp2TpquLiHRRGRkZbN+eeNc8GAySlbUzpExW3u1p+mrUk012mpuacCL4DIRj5KbtviLxf4bms7YqlJpEO6Knj2kXDSTb6+Ds6YsBOKFvJj9/q4RvttUz78ej+M/qKrr7XMRMizOf/Yop5xzFJUd349UlO5dUDyxIY11VCIBvttXxveI83li2ne8O7I4vUkNmZmaT5eDJareGhgZ69OhBQ0MD/fv3JzMzE7/fz6ZNm+jXrx9VVVUHdX8eqGRVp9frJT09HZfLRW1tLdFo4n7ydx9O+bDLEhu3dlhl2MCyKB92GbZ4w0H3w3I4HKlem5C4rxVqtozGw7IaS/b7TC51dzqdeDyeJv0+G/f5TH4sIgfHV7EaZ6i6ww3d81Wsbu8zkS7EEa2jW+k8CkrnE8wvpjZ3AKGsQkKZfXbbs9YWi+CtKSctUIavqgRfxWoFmyLSKhRyioh0UbW1takKQp/PRyAQSH3t9NNPZ/78+Xu8btzpwR+OkelJPIxkeRxU1UebbTe8p49bTu7LBS8sTl22eHOQk5/6gkuO7sYvxx7BT/6xig3+MJ+X1wKwens9vTPd+EMxPitPVGq+v6aaU4qymu0/afY3lYzpl8N7E49hxdYg27fUUFtbS3p6emob00wsL/ztb3/LAw88QG1tLSUlJVRUVOzzvmpNoVCISCRCRkYG2dnZiYpHK40Nw68ArNadZNqYYYBlsWH4FXgWbN7vZWOGYaR6bTocDuLxOPX19YTD4dR9Lq3HNE0aGhqaBcm79vl0u92kpaUBO/t9Ng4/1e9TZP8YWOSVLWBL8bmwX2N+WptFXtlHCoykXRhYZFSsIqMi0WrIwiDqySbu8GDZHBhmDHssjDPs18+oiLQJLVcXEemiFi9ezAknnADASSedxOLFO4PIcePG8e677+7xupbh4JP1Ac7onwPAWQNy+Xh9oMk2vTJdTLtoIFf/bTmhaCLsctp3viAMhGPURxOhypItQY7K82IzEkvPN9dG+Hh9gBE9EyHsiF4+1lU17cm5uqKeI3O9qc9//tYaznz2KyrrY8xZ8BllZWX069cPh8PBiBEjWL06UeXyzTffcNNNN/HII49QUVFBfX194nx79dpj5WprM02TQCBAbW0tLreHTSOvSkwwbauAM8mwYWGw4ejxiePvhcPhICMjg7y8PHw+H/F4nEAgQFVVFfX19Qo421my32ddXd1u+31Go1FsNpv6fYochNyNizA6SF9OwzLJ3biovU9DBEiEnq5wNd7gZtJqNuANbsYVrlbAKSJtRpWcIiJd1OrVq6msrGT69Ols2bKFl156CUhUdebn51NaWpra9thjj2XixIkUFhby5z//mZ9PmcrslZVcOLSAeT8exdebalP9MaddNJCbZ61i8pn9KEh38X/jhwJw8xuryPLY+c15A4ibFpGYyYTXvwHg7v+s5emLB+F12pi+aBOhqMlbKys4f3A+c288hqr6KFe8srzJ+c9dU801x/YEoLvPxYzxQzEti/fXVPPF14vxxmLMmDGDv/zlL0QiEe677z4ArrvuOk466STC4TCPPfYYkKh683q9VFZWtt4dvh/C4TDlPU+iNr1n+y2DtNkJZRVSUTSGgtJ5Tb6U7LWZ7PkYj8epq6sjHA5jHezwAWkzyX6fjfvrQmLJ++4qP5NL3k3T3O2Sd33PpatyROvoXvI2W4rPb98l65ZFj5K3cUTr2+8cREREOhBj5MiReoYqIiIHpMGTw8ox97T3afDo2Ufx3GebWFMZanL5SUv/hDvsTy3BNU0z9fHuwpmzzjoLy7JSg5baS8yZzjdj78Oytf97kIYZY/C8yTii9ak+j253os9WJBIhHA436w/ZlaSW5Dk9WIYDw4phjx5eS/J27feZ/F/9PkUSfwPWnDiJUEbv9hlEZMbx1pTTf+HUw+ZvjoiIyKFq/1dRIiLS6TjDfmyxyG6by7elX//n22aX2WIRYlWbsBpVp9nt9ibLbhuHnqZp8sEHHxCPx7HZbO26zLqq9/GJSeodgGXYqC06haMqv8ButxOLxbp01aaFcUDDFbyBMjI6+XCFlur3GYvF1L5ADjsGFn2XvkLJyXdgWWbbthexzNTxO+vfFxERkdagkFNERA6YgYW3ppy6nH4dY7pskpU4r0g43OxLhmFgt9tT/5JLdJN9B5PVacmQZk9VoK126hhUFp5CxxhkAWCwrc9J9N2ykEijqe9dTcyZTlXv46ksPIWoNwfMeCLM2MPPvelwU5fTj7rsIiqOPB1nqJq8so/I3fgZjmhdG59960j+LkQikdRlyd+vxuGny+VKvbnQeKl84/CzKwbmcvjw1G2j75IZlI24CiyrbR4Pd/zO9F0yY78HxImIiHQVCjlFROSgeANl1GUXgdEOy/T2xDJJC5Tt/kt76EeYtLsA1OVyNVmea1lWsyrQvS2DPxDB/OJEiNZRGAYRVxab3T3JqF3V3mfT5iwMtheNYeuAc3ZU1+4IL/ZnWaphpH4vop5sthSfx9YB59C95G0KSucflpVX+9vv0+Fw4PF4mvX73DX8lI6jK7RmOBTZW5dgLptJ+bBLdwSdrVjRuWPYUZ9lr5K9dUnrHUdERKSTUsgpIiIHJaOqhIojT2/v02jKZsdXVXJQV91bpWYyqGkcgO7PMvhdK0L3pjZ3QKJKsD16u+2JGSeYO4CMiq4VcobTu7Hh6PGEMvseemWWYQAGlmFjS/H5BHqMoO/SV/DUbWuRc+3oTNPENM1mlcDJ36dk8LnrGwq79vlUv8+20xVbMxyq3E2fYYtH2DD8isSbXa3xd9yMJ5aoL5mhgFNERGQPNHhIREQOioXByu/cTdST3TGWrFsWzrCfQR880qYvtDMyMpg2bRpHHnkkN954I+vXr8dut/OjH/2I0047jVAoxOOPP05lZSW9evXi9ttvx2az8cwzz7Bo0SJOOOEErr/+ekK+Hqz2m0z4+0oaF4SmOW28dNkQCnwu3lxRweMflHFEjoeFtxzH8m2J5c+XzVhGOGoy65rhOGwGMdNiwmvfUOYP89wlgxnSLZ26aJx/r6zkiQ/K6OZz8tfxwzAMCEbiievHmoawj3yvH89/tokt3y7n5fO606dPHwzD4MEHH6S0tJTf//73+Hw+bDYb/fv3Z8yYMeTm5jJlyhQMw6Curo677roLwzD49a9/nZpu39H5uw9PBBUYCiraQeMhR8mP7fbE96Fxv8/G4af6fbaMA23NACQqFy0TbPbDsjXDgWrRN0gasyy8gbLEGyRaoi4iIrJHquQUEZGDYmCRV7aALcXn0jH6SFrklX3U5pVEoVCI2267jZ/+9KeEw2GCwSB5eXmMGjWKK6+8kuHDh/PDH/6Q3/3ud1x//fU89thj1NTU8Nhjj7Fq1SrWrFnDnT//OfNH3sFffjSCkwuzWLA+kNr/hON78faqSqZ/tpl/XzeCv369BYAP1vm5dMay1HZuh42r/7aczbUNnDUglzu/U8ikf61O7OP1b1i+dWfocPnIHsxcvJVnFm3i7u8WcdHQAl5ZvDX19TSnjYEFaaypCnPsoCE4XbVMmDCBY445hiuvvJKHH36Yn/3sZwAce+yxfP/73wfg3HPP5Z133uHvf/87EydO5PTTT+c///kPgUCAoqIiSktLW+370BKqeo1OLDmF1ltyarNjWSZlI67CXPYquZs+b53jdFLJAHPXfp+7hp+N+30mq6bV7/PgqDVDy/HUbaP/p3+komgMWxrfnwcTeFoWYGFYJj1K3ia/C96fIiIiB6pjjHAVEZFOKXfjIgyrY1RRGZZJ7sZFbX7cWCxGdXV1k8t69uzJ2rVrAVi+fDkjRowgFAqRk5PDN998w8aNG6moqCAej1NRUUFV1IFpdwFQWt10aNLJhVm8u7oKgPfWVHFSYVbi8iOymPfjUTz8vX4ARGImm2sTU7Ab4ibmjoDHsuDpiwfxzoSRDO/pA2DltjqyvIn3OXO8Dirqmy4lPqN/Lp+W1QBQFjSxdpxbZmYmfr+/ybZnnnkm7733HgDr1q0jIyOj2bYLFy5k7Nix+32ftgd/9+GUD7uMRCDRyk+PdgQf5cMuw999eOse6zBgWRbRaDT1JoLf76eyspLKykoCgQD19fXE43GcTifp6elkZ2eTn59PXl4eWVlZpKen4/F4cDj03v6uwundWHPiJLYUn49lc+y7cnNvjMTvjmVzsKX4fNacOIlwereWPeFOwMCioHQeg+c9SI/Vb+EM+xNfMOOwr+DdshLbAc6wn56r32LwvMkUlM5TwCkiIrIf9GxPREQOmiNaR/eSt9lSfH77Llm3LHqUvI0jWt9+59BIeXk5Q4YMwel0Mnr0aLKyEsFk4/6dwWAQn89HVVUVZ591JjNuOIFvK0Nsr2tosq8cr5OaSOJFb004Rm6ak801EYp/+wn1UZNnfjCIi4cV8MayxBJGp93gvnFHcuPfVwLw83+XUFUfY2BBGs9fMpiTn/qCReU1PHL2UVx5TA+qQzF+8e81TY45sCCNdVUhACrqo0RNmDVrFm63m2uvvTa1nWEYjB49mscffxyAZcuWcdttt3HeeedRU1PD73//+9T9cdZZZ7XU3dviwund2DD8CqCVh4Y0ZhhgWWwYfgWeBZu1BPUgmKZJQ0NDs8sb98zdtd9ncniY+n3u0pqhpf9+GwahjN6UnHxHl23N4IjW0a10HgWl8w+ox2laoAxfF+9xKiIicrAUcoqIyCEpKJ1PoMcIQhm922dojhnHW1NOfun8tj/2Hvj9fl577TWmTZvGqlWrWLduHUCT3oE+n4+amkS15BvvzOM3NSOZemExFw0t4NUlO4fS+MNRMt12AuEYmR4HZdVhGuIWDfHEi99Zy7ZzQmFmKuR8+uJB/PnTjaypTISUVfWJSdWrttdjATYD7vzOEfxxwQZe/GILt5/WlxuP782fF27c7W05a0AuMbOCiy66iCFDhnD77bfzy1/+EoBRo0axZMmS1DTsa665hhkzZvDmm29y1VVX8YMf/IDXXnutpe7WVmFhsOHo8TuCnjZe4GLYsCyLDUePp//CqQo0WoCFQciZ2XQSeCiMM1yJ09F02JHH4+my/T7VmqHtGFhkVKxKDXBLTat3eLBsDgwzhj2mafUiIiItQSGniIgcEgOLvktfoeTkO7Ass22DIstMHb+jvTicPXs2s2fP5thjj00t266oqKBPnz5UVVWRlZWF3+/H6XRiWImQsCYcoz7aNFT5eH2AcQNyef7zzYzrn8uNf1+Jz2Un2JCoPDu1KIuV2xMVrPeeUcTaqhAzG4WkGW47tZE4BelOXHYbppXouFdRl1iiXlkfpbuvaVXR6op6ivPTgMS2AX+iR2h1dTU+ny+13bhx41JL1ZOSt9Xv95Ofnw9Anz59UkFvR7O9aEzLDwk5EDY7oaxCKorGUFA6r33OoRM71EngB9Lvs3H42Zn7fe5szUDr/9wbNrAsyoddhi3e0CUrOndlYOEKV+97QxERETlgmq4uIiItwt99OGUjruKghywcqB1DGQoXv9zuL5ynTp3KwIED2bx5M6+//jpvvvkmU6ZMIScnh82bNzNlyhTC4TD9+vXj7rvvxm63M23aNBYuXMgPf/hDzjznPELZR1BSEeKmWYnp6tMuGsjNs1aR7rLz8mVDyEt38tY3lfxm/nrOLs7lwbP6UR81Ka0OMeH1lfTMcPHtL07iox1Diz5dH+Dud9byj6uPJifNid0wuPfdtfz322r6Zrl54bIhAMTiFlf8bXkq9ARId9l56bIh/ODlpdhtBv/8LnTLycTpdPK73/2OxYsXYxgGM2fO5Ec/+lFqqW+PHj146KGHAIjH4/zqV7+iurqaO+64gzfeeKPDBZ0xZzrfjL0v0YuwnRlmjMHzJneYlgsdXWtPArfZbLud9G7s2H88Ht9t+NnRhdO7Jd6QMmxt/4aUZTJgweNqzSAiIiKtRiGniIi0mDZZAgmJoALoc5gsgbQwWH7GI7utQGsvj559FM99tom1W/0MnftrDja29ng83H333dx7770ten4tYVvR6WwpPrftl6nvjmXSY/VbdFM1517tdhL4IU6uPpBJ4Lv2+0x+nNiltdvws6P0+7QwWHPipHZvLaLWDCIiItJaFHKKiEiLajLMojVeSJvxxBL1w2yYxbejf0JdTr/2HeC0K8sivXotR332VHufSYuzMFj5nbuJerI7xn1uWTjDfgZ98IgCoD0Ip3djw9HjW769gGXhrdlA36Wv4Knbtu/td6Nx4Jn8OLnk3bKsJtWeyY/but/ntqKxHWJIXM/Vs9WaQURERFpF+6/PEhGRw0r21iV4Pt7SimFEeSKMOMyWPHoDZdRlF4HRDhVWe2KZpAXK2vssWkUwvzixzLmjMAyi3hyC+cWpASWyU0efBJ4ML5vu1mgWfrrd7tSS97bs9xlzprN1wDntH+gbBlsGnEPOxkVqzSAiIiItTiGniIi0OE/dNvp/+kcqisawpQWXlfYoeZv8/VxW2tlkVJVQceTp7X0aTdns+KpK2vssWkVt7oBEH8f2WLa7J2acYO4AhZy76KyTwC3LIhqNEo1Gm1ye7PeZDD+dTicej6dV+31W9T5+x9/h9mcZNqp6H6/WDCIiItLiFHKKiEirMLAoKJ1HzsbPqOo9msrCUw9uQEjYT37ZR4d95Y+vYjXOUHWHWz7tq1jd3meyR5MmTWLEiBFs2rSJyZMnE4vFuPTSS/n+978PwAsvvMDcuXPJy8vjoYcewuv18tprr/Hvf/+b+qxCLhvZkxtO6I3NgLv/8y2fltWk9n3+4Dx+fXoRUdPiy421/OzNEopyPEy/ZDAAuV4na6tC/ODlpYztl83D3zuKmGlx77tr+XCdn2uO7cGvTi9iY02ETYEIV726otn5P/ODQfz8rTWM7OXjxUuH8G1lCBqG8LPLZwPw9NNPM3jwYO6++24+/PBDAO666y4GDBiAx+PhhRdeYM6cOZx11lmYpsmcOXNa+y5vc4fjJHDTNGloaKChoaHJ5bv2+XS73aSlpQGH1u/TwqCy8BQ46M66Lc2gsvDU/e6DKiIiIrK/FHKKiEirckTr6FY6j4LS+QTzi6nNHUAoq5BQZp/dDtqxxSJ4a8pJC5ThqyrBV7G6S7wQNrDIK1uQGITTIcIIi7yyjzrsfV9cXEy3bt2YMGECEyZMYNy4cfznP//h0ksv5bLLLsPpdDJ9+nTmzp3Ltddey4svvsjnn3/Oc889x3tz5pLT5yguGFLAmc9+tdv9L94U5LQ/f0nctJjxo6Ec2zuDLzbWcsYzie3vG3ckpVUhAB7+3lGc9/xionGTN68bkdrmjws28NQnG3e7/yNyPMRNi0A4UaE3c8k2fvHvNdhiEYaS+Am45557uPjii5tc74knniAWi5GWlsZzzz3HnDlzmDt3Lk888cRhF3KG07uxYfgVgNV2w6EMAyyLDcOvwLNgc5u2xUiGlpFIpNHpGM2GHXm93gPq96nWDCIiItJVKOQUEZE2YWCRUbEq9aLWwiDqySbu8GDZHBhmDHssjDPs77DBWmvL3biIrQPO7hDLSg3LJHfjovY+jT0aMWIEn376KQAff/wxF1xwAf/5z38oLy/H7Xbj8Xiora0FYNiwYfzud7/DsixWrFhB0ZCRDB/Sk0jc5J0JI9lS28BP/rGKuoadVXEbAjuDpoa4iblLn8TvD87njGe+BMBhM1JhpcNmkJfmBODmE/tw6fDuTPuknFeXbGt2/ffXVKc+v3hYAcf3zeTvy7bx3oIcXOFqtm9vHrAlly17PB7Wrl0LJMKxcDhMbm4uVVVVB3FvdjwWBhuOHr+jB2cb/z4YNizLYsPR49ttEvjQoUP5xS9+QSwWY9u2bdx7772Ew2Eef/xxsrOzcblcPPnkkyxdupSCggLuuece0tLSmD17NnPnzqWoqIhJkyYRi8XYbnq5+O/l1Md3vnkyIN/L3y4fxqCCNLo99FHqZ//h7/Xj9KMSgegv3lrDgvUBpl00kGE9fNgMuP/dtcxZU80No3tx/eiemBbMWr6dJz4o48TCTB45+ygAemW4+feqCu6YvabJ7TpnYB49M1zMXLyZF37/KGkNfmKxGA888ACbN29m5MiR3H777ZimyRdffMHUqVNT173uuusYN24cV1xxBR6Ph1//+tfcd999rf2tEBERkU6k/V9FiYhIl2Rg4QpX4w1uJq1mA97gZlzh6i4bcEKi6rV7yds7+pC2I8uiR8nbHbo9QEZGBsFgEIBgMEhWVhYAH330EW+88QZ/+9vf+L//+z8gMfk6OcyltraWjNw8uvlc5Kc5+d70r/mkLMAtJ/XZ7XGO65NBN5+LrzYFU5cN7pbGppoINZFEMBSJm/TNctPN52RY93RyvA7+sbyC4X9YyPnPL+anpxXSI8PVZL+DCtJYu6MS9PPyWgY/8Sln/uUrzi7OY9CQYXu97VOmTGHmzJl88sknqcs2bdpEv3799vv+6+i2F41JDC5rr56pNjuhrEIqisa0y+G3bt3KjTfeyIQJE9i0aRNjx44F4Je//CU33HADd911F9dffz2hUIhLLrmE6dOnc80113DOOecQCoVYuXIlP/nJT7j99tv5cnOIi47u0WT/5YEIpz/9JZ9u2NmiIcfrYGy/HE556gsum7GMe884EoDfzF/PadO+4LznF/PQ9xIh5pw1VZz81BecOu0LLhiST0G6k0/Lajjjma8445mv+KQswD+XVzS7XTee0IsZX28lahpc/+InTJgwgRdeeIGrr74agGuvvZZ7772Xa6+9lqFDh1JQUABAWloa/fv3T+0nHA4TCAQoKipqsftcREREOj+FnCIiIh1IQel8vDUbEr1L24MZxxsoI790fvscfz/V1tbi8/kA8Pl8BAIB0tPTueSSS7jwwgu5+OKLufnmm4FE9WNyqEtGRgb+2hCBUIz/rk1UUr6/ppqh3dObHaN3ppvfnT+Aa2c27af5w6O78frSnZWZP3uzhOmXDObJCweydEsdW2obCIRjWBYEG+LMX1vN4G7N959U1xAnGreIxi3e/KaCgcX997gtJIKuiy66iAkTJqRu1+Gko00CjznT2vzQFRUVqWXr0Wg0tfw8Wcmbnp7OmjWJKslhw4axaNEi4vE4K1asoLCwkNraWmpra6mq9mNLy2RVRdM3LEJRMxXSJ9VG4lSHojhsBjleBxX1iZ6h66rCAERiZurNgtLqcOp60biF2eh9GafdYHSfTD4s9TfZf5bHgd0wiMRMInGLdVYu1o7bl9zv2rVrycjIwG63Y7fbCYUSbwRcfvnlvPrqq032t3DhwlT4KyIiIgIKOUVERDoUA4u+S19JVLRa5r6v0JIss+nxO7DFixdzwgknAHDSSSexePFiTNMkHA4TiUQIhUI4nU4Mw2D58uUcd9xx2O12Bg8ezJq1a1mw3s/InhkAjOzlS1VVJvlcdv56+VBunrWK7XVNp2N/f3A+/1y+cyn5lxtrOesvX3PzrFVsqY0QbIiT4U5UINoMOL5vZrP9r6qop1+uFyC1LcCpRVmUbSjf4+12OhNL4cPhMHV1dalwqFevXqxbt27/78AOrCNOAm8vPXv25KSTTuKDDz5IXTZ9+nSmTZvGRx99BDSvVM7MzATg5JNP5pVXX+U7R+WxpjLUfOe7iJkWizcHWXnnibx9/Uge/6CsydcfPfso/vRx05/N/xmaz9qqEJX1O39HxvXP5f1vq5oVpBfne5uEo6bDDb4CbrrpJv72t78BMHfuXB5//HFmzZrF4sWLCQaD+Hw++vfvz5IlTQdBlZeXH1bVyyIiInLo1JNTRESkg/HUbaPvkhmUjbgqsXS9LSradiQSfZfMaNNhKwdr9erVVFZWMn36dLZs2cJLL71ELBbj/fff58UXX8RmszFz5kwsy+KFF17goYce4pZbbuH1118nVlfDyi11bAiEmXvjMTTETK78W6Jac9pFA7l51ir+v1P7cmSOlz9eWAzA5PfW8cE6P4MKmi5VB/jFmCM4qziXUDTOpH8lptH/9NS+nD0wDwP42+KtrG8U7gC8uaKCu8YewWtLt3Hp8G7ccHxvYqbFx6V+vvric1zA/fffz3HHHcfpp59O//79ef7553nsscfIyMhIDVaCxFRur9dLZWVl69/xrUyTwHdKT0/n4Ycf5r777ktVcAJMmDCBHj168Pvf/55PP/00ValsWRYZGRnU1CSWoH/88cfMXbqOC3/5R248vje/mb9+r8cbWJDG6D6ZFP/2E3plunnxsiGpIVrXHdcTh81gxtdbU9sP7+njlpP7csELi5vs54dHd+OFLzbv1228/9c/Z+bMmZSVJQLVO++8kxtuuIGNGzfyxBNP0K9fP8aNG5cKQUVERET2RiGniIhIB5S9dQnmspmUD7t0R9DZipVtOypG+yx7leytS/axccfxhz/8odllL774Ii+++GKTyyoqKlJL1wGcGNhiEe55Z22z6988KzEY65H3S3nk/dJmX1+5vZ7/eWlpk8t+M399swDpobmlPDS3+fWTSqvDOGwGWR4H0z/bzPTPEqGQLRZhaDixjH7y5MnNrnf77bc3u+yMM85g9uzZezxWZ6JJ4Al2u50pU6bw9NNPs379zp8th8NBLBajvr6e+vrEEvRkpfKXX37J4MGD+cMf/oDT6UwsAzcc1IRjuBz7/vthAIFwDNMCfyiGz5WoMD6jfw4XDyto8nPfK9PFtIsG8sOXlxKK7qw4d9gMjuuTwQ1//6bZ/ldXhCjK8aQ+v/eMIjZs+IR33303dZllWdTU1GBZFrW1taSnp9O3b1+GDx8OQN++fZkwYQLTp0+nT58+h031soiIiLQMhZwiIiIdVO6mz7DFI2wYfkViOWprDGEx44kl6ktmdKqA81AYWHhryqnL6deufR8n/n1l0wusxHkd6Bk1Dok6u9rcAYl+tO01cGh3zDjB3AFtGnKeffbZDBs2jIkTJzJx4kRee+015s2bx5/+9CcAbDYbTz75JECzSuVIJMLYsWO58soriduclDtzuebVppXK2V4Hr14+jBE9ffzzmuE8Pn89/1ldxaaaCB/cNAqn3cajO0L+aRcNwh+K8u4NIwlHTc57fjGTz+xHQbqL/xs/FICb31jF6op6xvXP4b/fVu92dloiQLVwO2wUpDu557tFfLkkyMnPPsuSJUuYOnUqzzzzDE8++SSxWIzS0lKWLl3K0qU7w9UZM2akKphPOOEE3njjjdb6FoiIiEgnZIwcObJjN90SERHp4sLp3dhw9PjEtOmWDOUsC2+gjL5LX+kUS9Rb0qbi86k44jsdLkwrWP8BPVd3vqrM73//+7z99ttNllUfjDWjf0J9Tj/uO7MfX5TX8NbK5kvw/3nNcC58cQn/7+yjOOmILNZXh5nw+jfETIvXrhxGXpoTj8PGXW9/y4fr/HT3uXjx0iGkuWw8/elGZny9lQH5Xv52+TAGFaTR7aGPqGtItB+47ZQ+XHJ0N6rqo1z16gpqI3EuHprPr07ribN2C2+99RavvvoqHo+Hhx9+mNzcXObPn9+sehjgvvvu43e/+x2WZfHII4+QlpbGihUrUhXIH330Ed98k6h4fOyxx1izZg0PPvggjz76KOFwuNn+DkaDJ4eVY+5pkX21hHMH5tEjw8VznycqlwfNfxjXjsrlA+HxeLj77ru59957W/oURUREpBPrGF3dRUREZI88ddvo/+kf6bl6NoYZSywv312p1P6wEgONDDNGz9Wz6b9wapcLOAEyqko6VsAJYLPjqypp77M4KBdccEFqKFJjBzL93cIgnNlnr0H+sO7pLN0SZHhPH72z3Ix9+ktWbq/jh0d3A+DyV5bz3We+Yvwry7nnu0UA/GJsIb/9YD2nP/MVN5/UB7fDRnkgwulPf8mnG2pS+85Lc/L9wfl8589fMnPJNn5yUp8d1y9i3LNfc/U113DxxRdjGAYXXXQRCxYs4Prrr2f06NEUFBQ0Oc+ePXsSj8cJBoNcfPHFzJs3jxtvvBGv18vQoYnqx/Xr16cqNZOT0t9//33OO++8/b7P9sUZ9mOLRVpsf4fq36sqUwGnLRbBeRABJyQGbyngFBERkV1pubqIiEgnYGBRUDqPnI2fUdV7NJWFpyZ6F5rxRL/OvYVJO4JNbHacYT/5ZR+Rs3ERjmh9292ADsZXsRpnqJqoJ7tdl6ynWBbOsB9fxer2PpMmcnNzmTJlCna7naqqKu666y569erFww8/TENDA+vXr+fNN99k4MCBPPnkk/z3v/8lPT2dXr16kZuby9SpUzn//PMZNmwY0WiUBx54gM2bmw6l+etf/8qKFSs4qngQf93g5okdU70vHd6dm0/sTZrLzrnPLSYcMzl7YB7vrK7i5MIs3iupAuCd1VVce2xP/rZ4K9F4IvzPcNlZvrUOgNF9MrnzrTVYFnyxsYZh3dP5YmMtu84bH90ngw/W+QH4z+pKXrhkCACrK+rxpadhy+xOJBLBsixGjBiRqshcuHAhI0aMYM6cOal9jR07lkWLFgGJPpKzZs0CYOXKlYwaNYrly5fTp08fpk+fzrfffsvjjz9OQ0MDCxcu5LHHHuPvf/97i3z/OkprhmYOsjWDiIiIyN6oklNERKQTcUTr6FY6j0EfPMKRXzxD/voPSK9eu8dqLVssQnr1WgrWf8CRXzzDoA8eoaB0XpcOOCER/uSVLYA2npi9ZxZ5ZR+1+QTvfampqeHmm29mwoQJbNu2jdGjR3Pcccfx1ltvceONN/Loo4+yZMkSVq1axa233sr//d//AbB161Zuu+02HA4H3bp14/rrr+fPf/4zN954Y7NjZGZm8vLLLzP+1l9w/uB8CtITFaEllfWc/8ISFpbVcOaAXABOOiKLj9cHyPYmBupAotdjbtrO9+3/++NRvHPDSN5elVjq7rTbUoXPgXCc3LTmFacAOV5no33Gydmx3cwlW/n01uOY/epL/POf/0ydczAYBCAYDJKZmdlkX0VFRWzcuBGAtWvXMnr0aCDRRzK57QUXXMCECROoqKjgsssuAyAUCpGT07KDl7yBstRwsQ7DMkkLlLX3WYiIiMhhRpWcIiIinZCBRUbFqtQwFAuDqCebuMODZXNgmDHssTDOsL/DBWcdRe7GRWwdcDZWa06u30+GZZK7cVF7n0Yz2dnZ/PrXvyYzM5OCggJWrlzJu+++y4033sgjjzzCJ598stvJ7suXLwcSVYzJj5cvX86tt95Kv379+NWvfkU8Huemm26ivr6e9evXY2b2ZcnmWo7M9QLw9aZaADYEwuR4HaS77ISjJnHTwh+OkelJPI3N8jioqt/ZC/T0p7+kb5abWdcM572SKqJxE8NIFDRneexU1Ud3e1v94RhH5Xt37NNO9Y7tHj37KEb8YSHdP/gDL//2Xt59911qa2vx+XwEg0F8Pl+z6tTGZs2axS9/+Uv+/Oc/s2nTJiorE+FrIBAAYM6cOVx33XX7+R05cBlVJVQceXqr7f+gdOLWDCIiItJxtf+zehERETlkBhaucDXe4GbSajbgDW7GFa5WwLkXjmgd3UvePvj+pi3FsuhR8naHrK4955xz+PDDD7nhhhv4+OOPAYjH4/zhD3/g7rvv5tprr8UwDGKxGHb7zh6nppmoHNywYUOqB+XQoUMpKytj7dq1TJw4kZtuugmAtLQ0CgsLMawYR/fwUVqdWEje+NtiGHBG/xze/zaxRP2T9QHO6J+oeDxrQC4fr08Ehg5bYgF0sCFOMJIYJvR5eS1j++VgtxmM6p2ZWsa+q8/KazitKBuA7w3IY8GOfTbELWojcWKRMPF4HLfbzeLFiznhhBOARHXmkiVLmuyrtLSU3r17AxCJRJg8eXLq9n7wwQd4PB5stsTT8GOOOYYNGzYA4PV6qa4+uD6Ve5JszdDuP+dJloUzVN3hWjOIiIhI56dKThEREemyCkrnE+gxglBG7/YZRGTG8daUk186v+2PvR8WLlzIww8/zHe+8x0ikURLhDFjxqSWV3/yySdYlsX8+fN57LHHmDt3bpPrr1ixgu3bt/Pcc88Rj8e5//77mx2jpqaGyy+/nEFDj+aVVZVsC+6+0vJ7xbk8+v56ABZvDrK1toF5Px7FBn+YJz4sw+2w8e/rRwBgNwzueedbAH4zfz0vXjqEB8/qxzMLNxKOmWR7Hbx6+TBG9PTxz2uG8/j89fxndRX/XlXJBzeNwh+KceXfEhWof/hwAx/cdCyuH/2WRZ8soKKiglmzZvHII49w4YUX8uGHH7Jt27Ym5zp//nyuu+463nvvPYqLi/n5z3+OaZq89dZbbNq0ieLiYh544AFCoRA1NTWpITonnHACH3744UF9r/Yk2ZphS/G50CG6YHbM1gwiIiLS+RkjR47UMwwRERHpssLp3Sg5+Y7EsvW2XLpumRiWyYAFj3fJCfdJM2bM4IorrsDCYPkZj2A63LvdbvyI7ryyeGsbn12CLRZh6NxfH1BEeP/99/PEE0+kenfujwcffJBHH32UcDh84Ce5FzFnOt+MvQ/L1v71DYYZY/C8yR2ycllEREQ6Ny1XFxERkS7NU7eNvktmJD5pqyW9O47Td8mMLh1wNpacBL6n70F7BZwHOwl88uTJBxRwAtx3330tHnCCWjOIiIhI16CQU0RERLq87K1L6LNsJmC1/iRqywQs+ix7leytS/a5+eHuiiuuSH2sSeCtp6B0Pt6aDWDG2+cEzDjeQFmHbc0gIiIinZ9CThEREREgd9NnFC5+GcMyWy8IMuMYlknh4pfJ3fR56xyjE8uoKmmf3qh7c5hMAjew6Lv0lUQvzLYOki2z6fFFREREWoFCThEREZEdsrcuYcDHT+Ct3djyS3t3LHsesOBxVXDugSaBty61ZhAREZHDmUJOERERkUY8ddvo/+kf6bl6NoYZS1S9HWwgZCWq5gwzRs/Vs+m/cKqCnr1ITgKnw1T7HX6TwNWaQURERA5X7T9iUURERKSDMbAoKJ1HzsbPqOo9msrCU4l6cxLL2A0bGHsZQ7Mj2MRmxxn2k1/2ETkbF2nYyn7K3biIrQPOTky7b2eGZZK7cVF7n0aLy930GbZ4hA3Dr8CyrNZpEWDGE0vUl8xQwCkiIiJtwhg5cuTh89a0iIiISCuwMAjmF1ObO4BQViGhzD6YDnez7WyxCN6actICZfiqSvBVrD6sqgDbyraisWwpPn/vYXJrsyx6rp5NQem89juHVhZO78aGo8cTyuzbsve1ZeENlNF36SuqXBYREZE2o5BTRERE5ABZGEQ92cQdHiybA8OMYY+FcYb9CjVbgIXBmhMnEcro3T6DiMw43ppy+i+ceth/Py0MKorGsGXAOTuqZ42DCzwtK7E3y6RHydvkl84/7O87ERER6VgUcoqIiIhIhxNO70bJyXckgre2XLpumRiWyYAFj3epKsSYM/3QWjOEqtWaQURERNqVQk4RERER6ZD83YdTNuIqDrq68EDtqEYsXPxyl+0jqdYMIiIi0lkp5BQRERGRDquq12jKh12a+KQ1Kzp3TBrvs+xVcjd93nrH6WTUmkFEREQ6C01XFxEREZEOS5PA25eBhStc3d6nISIiIrJPCjlFREREpEPL3roEz8dbWm8SeE25JoGLiIiIdHJari4iIiIinYImgYuIiIjInijkFBEREZFORZPARURERGRXCjlFREREpFPSJHARERERSVLIKSIiIiKHBU0CFxEREem6NHhIRERERA4LmgQuIiIi0nXZ2vsERERERERERERERA6FQk4RERERERERERHp1BRyioiIiIiIiIiISKemkFNEREREREREREQ6NYWcIiIiIiIiIiIi0qkp5BQREREREREREZFOTSGniIiIiIiIiIiIdGoKOUVERERERERERKRTU8gpIiIiIiIiIiIinZpCThEREREREREREenUFHKKiIiIiIiIiIhIp6aQU0RERERERERERDo1hZwiIiIiIiIiIiLSqSnkFBERERERERERkU5NIaeIiIiIiIiIiIh0ago5RUREREREREREpFNTyCkiIiIiIiIiIiKdmkJOERERERERERER6dQUcoqIiIiIiIiIiEinppBTREREREREREREOjWFnCIiIiIiIiIiItKpKeQUERERERERERGRTk0hp4iIiIiIiIiIiHRqCjlFRERERERERESkU1PIKSIiIiIiIiIiIp2aQk4RERERERERERHp1BRyioiIiIiIiIiISKemkFNEREREREREREQ6NYWcIiIiIiIiIiIi0qkp5BQREREREREREZFOTSGniIiIiIiIiIiIdGoKOUVERERERERERKRTU8gpIiIiIiIiIiIinZpCThEREREREREREenUFHKKiIiIiIiIiIhIp/b/A2DGuibDtS05AAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "embeddings.index(arxiv.search(query, parameters={\"x\": \"Alien life\"}))\n", + "\n", + "graph = embeddings.search(\"Alien encounters on Earth\", 50, graph=True)\n", + "plot(graph)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "pRc59NOzEhyX", + "outputId": "3a1f193c-a0c6-448a-9bc8-e4b7784ab09b" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1. Earth has been thoroughly surveilled.\n", + "2. Earth will be contacted in due course.\n", + "3. SETI beyond half the distance that Earth's EM has reached (~35-50 LY) is futile.\n", + "4. The very quiescence of the galaxy paradoxically implies that that Drake's N = many, and that there is a system of galactic governance.\n", + "5. Search strategies are proposed to detect the described probe-node-land base communications pathway.\n" + ] + } + ], + "source": [ + "facts(graph)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "LQFlOOprXhP7" + }, + "source": [ + "The literature in ArXiv is pretty direct on what's going to happen! 👽" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-Olk7o98Wk0h" + }, + "source": [ + "# Topic Generation\n", + "\n", + "The last item we'll cover in this article is topic generation. Semantic graphs have a built-in method to create topic labels using a BM25 index. This works well enough to give a general idea on what the documents in an article discuss.\n", + "\n", + "Can we do better with a LLM?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "UZyaV1jOF6bV", + "outputId": "ce42973f-28a5-41c8-abeb-b1e017d97c38" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Topic: \"from_reported_reports_earth\", Generated Topic: \"UFOs and Alien Encounters\"\n" + ] + } + ], + "source": [ + "def topic(graph):\n", + " topic = list(graph.topics.keys())[0]\n", + "\n", + " text = \"\\n\".join(graph.node(x)[\"text\"] for x in graph.topics[topic])\n", + "\n", + " prompt = f\"\"\"<|im_start|>system\n", + " You are a friendly assistant. You answer questions from users.<|im_end|>\n", + " <|im_start|>user\n", + " Label the following text with a topic name in a couple words.\n", + "\n", + " text: {text}\n", + " topic: <|im_end|>\n", + " <|im_start|>assistant\n", + " \"\"\"\n", + "\n", + " print(f\"Topic: \\\"{topic}\\\", Generated Topic: \\\"{llm(prompt, maxlength=4096)}\\\"\")\n", + "\n", + "topic(graph)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4l2j02wBXELh" + }, + "source": [ + "The code above took the article text for a topic and run it through a LLM prompt. Looking at the original topic, we have some idea on what it has to do with but the LLM is much more direct.\n", + "\n", + "The BM25 index is fast. The LLM is not. It's a tradeoff but an option to consider." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8wGf3O_2YGbd" + }, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook (re)introduced the semantic graph and ways that it can be used standalone as well as with LLMs/RAG.\n", + "\n", + "Semantic graphs, as constructed in txtai, are a novel approach that few if any other systems have. So expect some disruption once this becomes more popular and known!" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "gpuType": "T4", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "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", + "version": "3.8.18" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/examples/56_External_vectorization.ipynb b/examples/56_External_vectorization.ipynb new file mode 100644 index 0000000..8c14388 --- /dev/null +++ b/examples/56_External_vectorization.ipynb @@ -0,0 +1,275 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "gpuType": "T4" + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# External vectorization\n", + "\n", + "Vectorization is the process of transforming data into numbers using machine learning models. Input data is run through a model and fixed dimension vectors are returned. These vectors can then be loaded into a vector database for similarity search.\n", + "\n", + "txtai is an open-source first system. Given it's own open-source roots, like-minded projects such as [sentence-transformers](https://github.com/UKPLab/sentence-transformers) are prioritized during development. But that doesn't mean txtai can't work with Embeddings API services.\n", + "\n", + "This notebook will show to use txtai with external vectorization." + ], + "metadata": { + "id": "SwgRD_NGutB9" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ], + "metadata": { + "id": "68Iw-nPbhYIJ" + } + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "id": "R0AqRP7v1hdr" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Create an Embeddings dataset\n", + "\n", + "The first thing we'll do is pre-compute an embeddings dataset. In addition to Embeddings APIs, this can also be used during internal testing to tune index and database settings." + ], + "metadata": { + "id": "_F84mqRHwpCP" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai import Embeddings\n", + "\n", + "# Load dataset\n", + "wikipedia = Embeddings()\n", + "wikipedia.load(provider=\"huggingface-hub\", container=\"neuml/txtai-wikipedia\")\n", + "\n", + "# Query for Top 10,000 most popular articles\n", + "query = \"\"\"\n", + "SELECT id, text FROM txtai\n", + "order by percentile desc\n", + "LIMIT 10000\n", + "\"\"\"\n", + "\n", + "data = wikipedia.search(query)\n", + "\n", + "# Encode vectors using same vector model as Wikipedia\n", + "vectors = wikipedia.batchtransform(x[\"text\"] for x in data)\n", + "\n", + "# Build dataset of id, text, embeddings\n", + "dataset = []\n", + "for i, row in enumerate(data):\n", + " dataset.append({\"id\": row[\"id\"], \"article\": row[\"text\"], \"embeddings\": vectors[i]})\n" + ], + "metadata": { + "id": "U52gN-vUxjky" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Build an Embeddings index with external vectors\n", + "\n", + "Next, we'll create an Embedding index with an external transform function set.\n", + "\n", + "The external transform function can be any function or callable object. This function takes an array of data and returns an array of embeddings." + ], + "metadata": { + "id": "Ggo-N9iOQJ4d" + } + }, + { + "cell_type": "code", + "source": [ + "def transform(inputs):\n", + " return wikipedia.batchtransform(inputs)\n", + "\n", + "def stream():\n", + " for row in dataset:\n", + " # Index vector\n", + " yield row[\"id\"], row[\"embeddings\"]\n", + "\n", + " # Index metadata\n", + " yield {\"id\": row[\"id\"], \"article\": row[\"article\"]}\n", + "\n", + "embeddings = Embeddings(transform=\"__main__.transform\", content=True)\n", + "embeddings.index(stream())" + ], + "metadata": { + "id": "iujXcHzMCd0B" + }, + "execution_count": 64, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "🚀 Notice how fast creating the index was compared to indexing. This is because there is no vectorization! Now let's run a query." + ], + "metadata": { + "id": "B0Se6nG7JxOG" + } + }, + { + "cell_type": "code", + "source": [ + "embeddings.search(\"select id, article, score from txtai where similar(:x)\", parameters={\"x\": \"operating system\"})" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Bi0WnvosEL8E", + "outputId": "d190e8c1-cb08-4d40-989c-aed4a67edc06" + }, + "execution_count": 63, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': 'Operating system',\n", + " 'article': 'An operating system (OS) is system software that manages computer hardware and software resources, and provides common services for computer programs.',\n", + " 'score': 0.8955847024917603},\n", + " {'id': 'MacOS',\n", + " 'article': \"macOS (;), originally Mac\\xa0OS\\xa0X, previously shortened as OS\\xa0X, is an operating system developed and marketed by Apple Inc. since 2001. It is the primary operating system for Apple's Mac computers. Within the market of desktop and laptop computers, it is the second most widely used desktop OS, after Microsoft Windows and ahead of all Linux distributions, including ChromeOS.\",\n", + " 'score': 0.8666583299636841},\n", + " {'id': 'Linux',\n", + " 'article': 'Linux is a family of open-source Unix-like operating systems based on the Linux kernel, an operating system kernel first released on September 17, 1991, by Linus Torvalds. Linux is typically packaged as a Linux distribution (distro), which includes the kernel and supporting system software and libraries, many of which are provided by the GNU Project. Many Linux distributions use the word \"Linux\" in their name, but the Free Software Foundation uses and recommends the name \"GNU/Linux\" to emphasize the use and importance of GNU software in many distributions, causing some controversy.',\n", + " 'score': 0.839817225933075}]" + ] + }, + "metadata": {}, + "execution_count": 63 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "All as expected! This method can also be used with existing datasets on the Hugging Face Hub." + ], + "metadata": { + "id": "jUxUBP0vKUZo" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Integrate with Embeddings API services\n", + "\n", + "Next, we'll integrate with an Embeddings API service to build vectors.\n", + "\n", + "The code below interfaces with the Hugging Face Inference API. This can easily be switched to OpenAI, Cohere or even your own local API." + ], + "metadata": { + "id": "lUU8WlIbKnOi" + } + }, + { + "cell_type": "code", + "source": [ + "import numpy as np\n", + "import requests\n", + "\n", + "BASE = \"https://api-inference.huggingface.co/pipeline/feature-extraction\"\n", + "\n", + "def transform(inputs):\n", + " # Your API provider of choice\n", + " response = requests.post(f\"{BASE}/sentence-transformers/nli-mpnet-base-v2\", json={\"inputs\": inputs})\n", + " return np.array(response.json(), dtype=np.float32)\n", + "\n", + "data = [\n", + " \"US tops 5 million confirmed virus cases\",\n", + " \"Canada's last fully intact ice shelf has suddenly collapsed, \" +\n", + " \"forming a Manhattan-sized iceberg\",\n", + " \"Beijing mobilises invasion craft along coast as Taiwan tensions escalate\",\n", + " \"The National Park Service warns against sacrificing slower friends \" +\n", + " \"in a bear attack\",\n", + " \"Maine man wins $1M from $25 lottery ticket\",\n", + " \"Make huge profits without work, earn up to $100,000 a day\"\n", + "]\n", + "\n", + "embeddings = Embeddings({\"transform\": transform, \"backend\": \"numpy\", \"content\": True})\n", + "embeddings.index(data)\n", + "embeddings.search(\"feel good story\", 1)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "9-V6OIEtLEIs", + "outputId": "e2492e33-27f7-48f3-e409-5c63e50c7f35" + }, + "execution_count": 74, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': '4',\n", + " 'text': 'Maine man wins $1M from $25 lottery ticket',\n", + " 'score': 0.08329013735055923}]" + ] + }, + "metadata": {}, + "execution_count": 74 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "This is the classic txtai tutorial example. Except this time, vectorization is run with an external API service!" + ], + "metadata": { + "id": "uW8pDvD5Ms2r" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "This notebook showed how txtai can integrate with external vectorization. This can be a dataset with pre-computed embeddings and/or an Embeddings API service.\n", + "\n", + "Each of txtai's components can be fully customized and vectorization is no exception. Flexibility and customization for the win!" + ], + "metadata": { + "id": "8wGf3O_2YGbd" + } + } + ] +} \ No newline at end of file diff --git a/examples/57_Build_knowledge_graphs_with_LLM_driven_entity_extraction.ipynb b/examples/57_Build_knowledge_graphs_with_LLM_driven_entity_extraction.ipynb new file mode 100644 index 0000000..b8cf0e1 --- /dev/null +++ b/examples/57_Build_knowledge_graphs_with_LLM_driven_entity_extraction.ipynb @@ -0,0 +1,331 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "SwgRD_NGutB9" + }, + "source": [ + "# Build knowledge graphs with LLM-driven entity extraction\n", + "\n", + "Embeddings databases are a union of vector indexes (sparse and dense), graph networks and relational databases. This enables vector search with SQL, topic modeling, retrieval augmented generation and more.\n", + "\n", + "Up until txtai 7.0, semantic graphs only supported automatic relationship detection. Now relationships can be loaded directly into a txtai database. This notebook will demonstrate how to work with this new feature." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tNTn-GQUkZ4a" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "jvi9wKKGqakp" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[graph]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Epgj606OrC7X" + }, + "source": [ + "# Load Wikipedia database\n", + "\n", + "The [txtai-wikipedia](https://huggingface.co/NeuML/txtai-wikipedia) database stores all Wikipedia article abstracts as of January 2024. This database is a great way to explore a wide variety of topics. It also has the number of page views integrated in, which enables pulling frequently viewed or popular articles on a topic.\n", + "\n", + "For this example, we'll work with a couple articles related to `Viking raids in France`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "LIR8auK7rv62" + }, + "outputs": [], + "source": [ + "from txtai import Embeddings\n", + "\n", + "# Load dataset\n", + "wikipedia = Embeddings()\n", + "wikipedia.load(provider=\"huggingface-hub\", container=\"neuml/txtai-wikipedia\")\n", + "\n", + "query = \"\"\"\n", + "SELECT id, text FROM txtai WHERE similar('Viking raids in France') and percentile >= 0.5\n", + "\"\"\"\n", + "\n", + "results = wikipedia.search(query, 5)\n", + "\n", + "for x in results:\n", + " print(x)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pshXa60Rt08I" + }, + "source": [ + "# LLM-driven entity extraction\n", + "\n", + "Now that we have a couple relevant articles, let's go through and run an entity extraction process. For this task, we'll have a LLM prompt to do the work." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ATLdPsDeuBCl" + }, + "outputs": [], + "source": [ + "import json\n", + "\n", + "from txtai import LLM\n", + "\n", + "# Load LLM\n", + "llm = LLM(\"Qwen/Qwen3-4B-Instruct-2507\")\n", + "\n", + "data = []\n", + "for result in results:\n", + " prompt = f\"\"\"<|im_start|>system\n", + " You are a friendly assistant. You answer questions from users.<|im_end|>\n", + " <|im_start|>user\n", + " Extract an entity relationship graph from the following text. Output as JSON\n", + "\n", + " Nodes must have label and type attributes. Edges must have source, target and relationship attributes.\n", + "\n", + " text: {result['text']} <|im_end|>\n", + " <|im_start|>assistant\n", + " \"\"\"\n", + "\n", + " try:\n", + " data.append(json.loads(llm(prompt, maxlength=4096)))\n", + " except:\n", + " pass" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Ry2DCrN4upa0" + }, + "source": [ + "# Build an embeddings database\n", + "\n", + "Now that we've extracted entities from the documents, next we'll review and load a graph network using these entity-relationships." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "SLFjIiQAurvz" + }, + "outputs": [], + "source": [ + "def stream():\n", + " nodes = {}\n", + "\n", + " for row in data.copy():\n", + " # Create nodes\n", + " for node in row[\"nodes\"]:\n", + " if node[\"label\"] not in nodes:\n", + " node[\"id\"] = len(nodes)\n", + " nodes[node[\"label\"]] = node\n", + "\n", + " for edge in row[\"edges\"]:\n", + " source = nodes.get(edge[\"source\"])\n", + " target = nodes.get(edge[\"target\"])\n", + "\n", + " if source and target:\n", + " if \"relationships\" not in source:\n", + " source[\"relationships\"] = []\n", + "\n", + " source[\"relationships\"].append({\"id\": target[\"id\"], \"relationship\": edge[\"relationship\"]})\n", + "\n", + " return nodes.values()\n", + "\n", + "# Create embeddings instance with a semantic graph\n", + "embeddings = Embeddings(\n", + " autoid = \"uuid5\",\n", + " path = \"intfloat/e5-base\",\n", + " instructions = {\n", + " \"query\": \"query: \",\n", + " \"data\": \"passage: \"\n", + " },\n", + " columns = {\n", + " \"text\": \"label\"\n", + " },\n", + " content = True,\n", + " graph = {\n", + " \"approximate\": False,\n", + " \"topics\": {}\n", + " }\n", + ")\n", + "\n", + "embeddings.index(stream())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "WpmDYQcC0R2Z" + }, + "source": [ + "# Show the network\n", + "\n", + "Now let's visualize the entity-relationship network. This period might not be that familar to most, unless you've watched the Vikings TV series, in which case it should make sense." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "1_yhHm7p0WW0", + "outputId": "f3afcae9-be56-491a-b012-b0f22f972e2b" + }, + "outputs": [ + { + "data": { + "image/png": "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LVs26v3fcsstcdxxx+HPf/5zVYXF9ttvjw9/+MM47bTTRr28uI2dd955zPMREREREdXDIIKIiIiIaIrZfffd8eyzz8oQAgBWr16NZcuWYffdd59QEPHaa6/h6KOPxtlnn43HHnsML7/88oTmAngwsh9+I8bOO0PxeJB75JGq07MPP4zAoYdWQoBVq1o+hvjVV6Pw3HPya3d7JSuRGBFCAEDggx+EecIJ0OfMgeoa4KzPnVsVRBSef75qpkRpxQooHg/UWAz20BD22msvrFixoiqEcJs7dy5mz56Nn//851BVVZ7+/PPPw3Ec7LTTTg2DiP7+fliWVbcVk7wfgQC+973v4a233sJvf/vbqu999atfxV133YXVq1c3vLyQSCTQ398/5vmIiIiIiOphEEFERERENMWEQiG8/vrrI04fHh5GKNRqM6Rq999/PwKBAD7ykY/g4x//ONLpNB544AH8+te/RrFYbPn6mg0hAEDdeOzW8HDV6fbGr9VwuOXbBwDrrbdQ+s9/6n7PHhoacZrvwAPR961vIf3nPyN5zTWwk0mo/f2YceGFUAyj6rxOOl39dbkMAPJ8oXAYg4ODDY8tEokAAC688MK63585c2bDyxqGMWpIpOs6vv/97yMUCuGLX/xiVTXEwQcfjK222goXXnihbLtlbDzmYDCIYrGIkjtgKZXk94mIiIiIWsUggoiIiIhoikkmk4jFYiNOj8ViWLNxt74IDTye6uZIYwUVjuPgjjvuwB133IEZM2bg4IMPxumnn45EIoHf//73LR9rEUCzY3ftjTv7tWgU9sCAPF3deF/tUXb+t1Pg/e9H8bXXEL/0Unmascce47queDKJbbfbruH3RTXD5ZdfXrdqYrQQI5lMwjAMeDyeqtAAABRFwbe+9S3suOOO+MpXvoINNXM0ttpqK4TDYdxyyy0jrveee+7BTTfdhN/85jfyNNM0R628ICIiIiIaDYMIIiIiIqIpZsmSJfjIRz4C0zSR3rgjf+7cudhuu+1w3333AQDi8ThKpRK22moreTld17FHCwvqAwMDuO2223DooYdi6623HtexlsY+i1R85RU4pRL8738/Sq6Kj8D73w9raKh6NkMHKV5vVbslAAgceui4ruuZRYtw8Ac+gJ133hkv12kBtWrVKqxfvx6bb7457rzzzpauW7RU2nzzzbGqpmXVOeecgwMOOADf+MY38MYbI0ck33///Vi8eHHVaUcccQQOPvhgnHfeeVi/fn3V92bPnt1UCyciIiIionoYRBARERER9SBVVXHQQQeNOP2VV17BbbfdhiOOOAI/+clPcOONN8IwDJx66qlYv3497r//fgCVyobHH38cH/vYx/Dmm28ikUjgmGOOGfN2/+u//gupVApLly5FOp3Gbrvthu233x533XXXuO5H4/38I9mJBNJ//CNCn/gEnGIRxaVL4dtvPwQ++EEMX345YNvjOoZW5Z95BrGvfQ2hz3ymcgwLF8K3554tX08cwN/+9jccffTR+NGPfoTrr78eb7zxBmbMmIE99tgDl1xyCQDgqquuwre//W34fD488cQTyOfzmDVrFvbbbz9cc801ssql1iuvvIJyuYwdd9yxKog46aSTcPTRR+OWW25BsVisGjK9cuVKZLNZrFu3DuvWrau6vgULFsC2bTz//PMjbmvHHXfEbbfd1vJjQEREREQEMIggIiIiIupJXq8X//u//zvi9B/84Ad48MEH8dWvfhVnnnkmvvWtb8G2bSxatAhXXXUVcrmcPO8VV1yBc889F1/60peQy+Vw6623YvXq1TjwwAMb3u5LL72EI488EkceeSR8Ph/eeust/OIXv8C99947rvuxCoANQGvy/Imrr4adTiN45JEIf+YzKL/9NoYvuQSZu+8e1+2PR+buu6FvsQXMY4+F8olPIP/00xi68EJs9stfNn0dNoDXUZmtcO655+ILX/gCTjrpJIRCIWzYsAEPPfSQPO+jjz6KdDqNT3/60/jgBz8IAHj77bfx1FNPYbhmXoZbPp/Hk08+iYULF+LBBx+Up++9994AgE984hP4xCc+UXWZr371q3WDhtHMmzcP0WgUjz32WEuXIyIiIiISlAULFrQyP46IiIiIiKglFzkOtuz2QUwyC8CfANylNDshY3z2339/fPvb38axxx6LQqHQkds444wzsOOOO+Lcc8/tyPUTERER0fSndvsAiIiIiIhoelsGoNztg5hkGoDlk3A7//73v7FmzRoceeSRHbn+QCCAI488Etdff31Hrp+IiIiINg0MIoiIiIiIqKPewKb5H48Vk3Q7l156KfL5fEeue7PNNsPvfvc7vPDCCx25fiIiIiLaNLA1ExERERERddQOjoMLun0QkywO4MsdbstERERERDRVbIobk4iIiIiIaBK9ASDd7YOYRBaAZ7t9EEREREREPYRBBBERERERdZSlKHgIlQX6TYEG4O/dPggiIiIioh7CIIKIiIiIiDruIWwa//mwURnOvYptmYiIiIiIpE3h/wJERERERNRlg4qCxZj+VREqgL92+yCIiIiIiHoMgwgiIiIiIpoUf0OlbdF0lgHwdLcPgoiIiIioxzCIICIiIiKiSbEEwAZU2hdNRxYqLahKbMtERERERFSFQQQREREREU0KR1FwPabnf0JsACkA93T7QIiIiIiIetB0/D8AERERERH1qOcVBY9h+s2KUAH8BkCW1RBERERERCMwiCAiIiIiokn1f6hUD0yXFk0WgEcBvMAQgoiIiIioLgYRREREREQ0qbKKgmswPf4zIloy3dTtAyEiIiIi6mHT4Xd/IiIiIiKaYqZLiya2ZCIiIiIiGhuDCCIiIiIi6oobAKzA1A4jbgdbMhERERERjYVBBBERERERdUVBUXAxgLcxNcOIewHc1e2DICIiIiKaAhhEEBERERFR12QVBT/C1Asj/gbgZgBgNQQRERER0ZgYRBARERERUVclFAXfB7ASUyOMuAuVtlIMIYiIiIiImsMggoiIiIiIui6jKPghgOc2fm1382DqsACUAVwP4HZFYQhBRERERNQCvdsHQEREREREBFRmRlzuONgfwMkAvAC07h4SAMBBpVrjVwDeZABBRERERNQyBhFERERERNQ7FAX/BrDUcfB5AHuiUh3RjVJuC5UQ4jYA9wFwGEIQEREREY0LgwgiIiIiIuo5CUXBZY6D/VCpjgigEgpMRiBRRuU/SqyCICIiIiJqDwYRRERERETUmxQFTwB4bmMgcTiAuahUKnSiZZOYS/EsgAcBvLzxGIiIiIiIaGIYRBARERERUU8rKAoeBfCo42B7AB8EsBCVMMLC+P9TY2/8owNIAPg7gIcBxBk+EBERERG1lbJgwQKn2wdBRERERETUipDjYAGAbQDsgEqlhAeV9k1Wg8soeKeSIg1gGYDlG/9eAsBiAEFERERE1BGsiCAiIiIioiknpSh4HMDjG79WHQdbANgWwCxUQgkDleChtPFPDsBqAG8AGAbYdomIiIiIaJIwiCAiIiIioinPVhSsAbCm2wdCREREREQjqN0+ACIiIiIiIiIiIiIimr4YRBARERERERERERERUccwiCAiIiIiIiIiIiIioo5hEEFERERERERERERERB3DIIKIiIiIiIiIiIiIiDqGQQQREREREREREREREXUMgwgiIiIiIiIiIiIiIuoYBhFERERERERERERERNQxDCKIiIiIiIiIiIiIiKhjGEQQEREREREREREREVHHMIggIiIiIiIiIiIiIqKOYRBBREREREREREREREQdwyCCiIiIiIiIiIiIiIg6hkEEERERERERERERERF1DIMIIiIiIiIiIiIiIiLqGAYRRERERERERERERETUMQwiiIiIiIiIiIiIiIioYxhEEBERERERERERERFRxzCIICIiIiIiIiIiIiKijmEQQUREREREREREREREHcMggoiIiIiIiIiIiIiIOoZBBBERERERERERERERdQyDCCIiIiIiIiIiIiIi6hgGEURERERERERERERE1DEMIoiIiIiIiIiIiIiIqGMYRBARERERERERERERUccwiCAiIiIiIiIiIiIioo5hEEFERERERERERERERB3DIIKIiIiIiIiIiIiIiDqGQQQREREREREREREREXUMgwgiIiIiIiIiIiIiIuoYBhFERERERERERERERNQxDCKIiIiIiIiIiIiIiKhjGEQQEREREREREREREVHHMIggIiIiIiIiIiIiIqKOYRBBREREREREREREREQdwyCCiIiIiIiIiIiIiIg65v8DpRWPSuHmM98AAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "import networkx as nx\n", + "\n", + "def plot(graph):\n", + " labels = {x: f\"{graph.attribute(x, 'text')} ({x})\" for x in graph.scan()}\n", + " lookup = {\n", + " \"Person\": \"#d32f2f\",\n", + " \"Location\": \"#0277bd\",\n", + " \"Event\": \"#e64980\",\n", + " \"Role\": \"#757575\"\n", + " }\n", + "\n", + " colors = []\n", + " for x in graph.scan():\n", + " value = embeddings.search(\"select type from txtai where id = :x\", parameters={\"x\": x})[0][\"type\"]\n", + " colors.append(lookup.get(value, \"#7e57c2\"))\n", + "\n", + " options = {\n", + " \"node_size\": 2000,\n", + " \"node_color\": colors,\n", + " \"edge_color\": \"#454545\",\n", + " \"font_color\": \"#efefef\",\n", + " \"font_size\": 11,\n", + " \"alpha\": 1.0\n", + " }\n", + "\n", + " fig, ax = plt.subplots(figsize=(20, 9))\n", + " pos = nx.spring_layout(graph.backend, seed=0, k=3, iterations=250)\n", + " nx.draw_networkx(graph.backend, pos=pos, labels=labels, **options)\n", + " ax.set_facecolor(\"#303030\")\n", + " ax.axis(\"off\")\n", + " fig.set_facecolor(\"#303030\")\n", + "\n", + "plot(embeddings.graph)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "VcG2xF7W7EBa" + }, + "source": [ + "# Graph traversal\n", + "\n", + "The last thing we'll cover is extracting a specific path from the graph. Let's show a path from node 8 to node 5 requiring a specific relationship type to start.\n", + "\n", + "A new feature of txtai 7.0 is the ability to return a graph of search results. This is a powerful addition as not only do we get the search results but we get how the search results relate to each other." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "0i_S3j7y2jtN", + "outputId": "6329e66d-4662-49cc-82fb-d935ba288577" + }, + "outputs": [ + { + "data": { + "image/png": "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XpUvuUT9f/xHWZWKgn//7xW95z0K3/GO2XPlSaLGlVeq9973Knv4tIEkxR/8oKL3vGf2a+eMM7b1+2We8AT1r75a/auumt02uOaa2f/PJJlDHvPN3/zN6vf7+vrXv37E53zve9+rRz3qUZKk66+//rCQR5K+/OUvq16v67zzztM999yzrVoBAAAAAACAVUYQASyBSqWij3zkI7Nfp2mq17zmNbrvvvskSaVSSd/6rd+qP/iDP5gFFpJ033336YEHHtBFF12k66+/XhdddJGiKJqFEHaeyx4MNPzsZ2Uuuui4dQw/85ktv57cc49KT3rSltvyNNXws599pNZ9+5QNh3IeXsA33/qtsqtVDa69dktbp+FnP6tWuSz3ggs0vvHG2e2Df/7n49Z1JOM77lD12c9Wurmp4ac/reTuu7fWKck75DF79uxR9xizMV796lerXC7r/PPP17/7d/9Or3/96/XKV75yy2Dq6eM3NjYIIgAAAAAAAAARRABLYTgc6ud//udl27bOPvts/Yf/8B/06le/Wi95yUu0ubkp3/flOI5+5md+Rj/zMz9z2OOnsyT27NmjTqczu33aPCg76LZjyQ85NZEniSyz9VxBPh5LSbL1gQfdbzp34tEPn4Y4lHv66Tq4gVK2ubmt2g7VectbFESR/B/9UTV/6qeUfOMbit797i2nNw7tTWeM0WQyOepz3nXXXZKkW265Rbfffrve8Y536GlPe5o+/vGPz+4zfXypVDqpugEAAAAAAIBVQxABLIE8z3XHHXdIkm677Tbde++9+p//83/qRS96kd70pjep1+spyzK9+93v1ic/+cnDHj/dpb9///4tw6tTHWhRZB8y0Hqe8ofbLj30mtcoffDBw76eHqUt0uzx0zkP7ta3L7te33q/OFb3bW9T921vk3vBBfKf/3y1fumXNLn7bo2/+EVZkrZOjDgwgLp+yPMczZ133qnJZKLHPOYxW26fPj48qL0UAAAAAAAAsM4YVg0soTvuuEPXXHONfuAHfkCtVkvD4VC33HKLzjvvPN1xxx2H/TcdqHz77bfL93098YlPPPBElqW0UlH5yU/etdpHt9yibDCQ86hHaXL77Yf9lx1nAT9rt5VPJvLOO++RG11Xpb17j/qY5O671Xnb2yRp9jhbhwcR9913nxqNhsrl8nG/jyc84QnyPO+weRJnPDzDYto2CwAAAAAAAFh3nIgAltSf/dmf6bLLLtPzn/98/dEf/ZHe/va36w1veINe+9rX6pprrlEURXrUox6l7/zO79SHPvQh3Xjjjbr++ut1xx136DWveY3+6I/+SL1eT6f/2I8p6/elg+YczFPe6yl817vUfPnL5T7qURp94QvKs0zuWWep8q/+lfa/9rXKR6NjPEGuwSc+ofpzn6vkq19V1u2q/tznHna3R731rRp88pOa3H23lKaqPutZysdjjf7lX2b3iQ95zE033STHcXThhRfqpptumt3+m7/5m7rjjjt05513ajwe67GPfaxe8IIX6M477zzsBMpFF12ke+65hxMRAAAAAAAAwMMIIoAldd999+maa67RD/3QD+nP//zPdfPNN+tnf/Zn9e///b/Xq171KrmuqwcffFCf+9zn9NWvfnX2uF/7tV/TK17xCr3iFa9QFEX6yl/9lc495xyZCy/ctdp7f/EXSh96SP6P/Ihq//bfSkmi5Gtf0/Cf/kn5ofMljqDz5jer9cu/rObP/qyywUC9975Xzn33qfK0p83uM77pJlW///vlnnmmlGWa3H23HvpP/0nJvffO7nPPIc97//33684779Qll1yyJYi47bbb9H3f93264oorZNu29u3bp7/7u7/T+973PiWH1PuUpzxly8wIAAAAAAAAYN1Ze/fuzYsuAkBxnus4+qk/+RON/+Vf1P5v/63ocnZNX9JPu66SNN1y+3Of+1w973nP00/8xE+c8HOef/75esc73qGf+Imf0L59+3aoUgAAAAAAAGC5cSICWDM/+IM/KMuydN9998n3fT3rh35I7hlnaPN1ryu6tF2TS7rf89RqtZTnuZIkmf131VVX6YorrtBTn/pUfepTnzqh5/3RH/1RffjDHyaEAAAAAAAAAA5CEAGsmfF4rCuuuGI2VPmeO+/UQ69+tSa3315wZbsnlXT7ZKJ2uy3XdeW6rjzPU7lclmVZevvb365Go6F6vb4lpDgWy7L01a9+VVddddXufBMAAAAAAADAkqA1EwC9Lc/VKLqIXfYWSTdY1mG3T4OJaTjhOI4syzrs5MR2wgkAAAAAAAAAnIgAIOkWSU+R5BRdyC7JJH3pKF87UsBwaDgxPTlBOAEAAAAAAAAcH0EEAF0j6dKii9glqaQvSOoc4TTE0RBOAAAAAAAAACePIAKAbpP0dUmPlmQX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NXP67NcbGxv6hV/4Bb3vfe/Tb/7mb2o8Hut3f/d31Ww2JR2Y/fEbv/Ebuueee/Trv/7ret3rXqdrr71Wvu8fs/YnP/nJuvnmm495n1arpSc96UlbTkNM3XzzzbJtW3v37j3mcwAAAGA5sCIFrCFjDKch1kwYhsrzXEEQFF0KAABbvPzlL5ckffCDH5zdVq/X1esdPv8giqLjLoBfffXVqlQqetrTnja77Xu/93tlWZauueYaSZJnl4/2cDmupee87Dyd9U1V/cX/uEv7v3bsdkyuZ+uTf/MN3XRdW1+5pad//LP7ZcqOzv+WA3WWqo6+/ekb+vSHHtRnPvyQvnJLT1f/v19V5xvHvxa75Z/auvXTHT3x6Xv0E695nK58w8V6zsvP1Tc98fDfg+PVcbCDv/9Go6H//t//u6666ip96lOf0qtf/WpZlqXnP//5kqQLL7xQnufpzW9+sz772c/qhhtu0Hvf+95ZqHM0F110ke68885j3uf7vu/7DmvLNBXHsR544AE94QlPOOZzAAAAYDkQRABrxrZteZ5HELFm8jxXGIZyXfe4CzgAAOyWZz/72frBH/xBvfnNb9ZDDz20I89555136p577tFll102u+3yyy/XF7/4RT3wwAOSJNvyjvhY11j64Z8+TxtnlPQXv3eXOg8c/3opy3Lde+sjoUm4f6LJOJPfOvAapz2mLNfYuvPGre2FDv31keS59A/vvE9/+pt36JN/vU9f/XKs8y729cNXnq9Ln3P6lvser46DHfz993o9ff7zn5/9Oo5jffazn50FAHfddZfSNNVrXvMaXXrpparVaset2/d9GWNmQ6qP5pnPfKZuv/123X///Uf8erfb1Z49e477egAAAFh8BBHAmjHGKM9zgog1lCSJoihSuVxWpVIpuhwAwJp7ylOeole84hX60z/9U1111VVbvtbr9Y644O37vqIoOu5zX3311frO7/xOBUGgRz/60br44ou37Lq3rSOPyqvUXZ39uJruvilS1N5eC8tknClLtw5sSJNcjnegj1KtceC1Br2tc5r60fbnNu3/+kg3XPWg/s/vf0X/+z/dpm/cO9B3Pft0laqPtF06Xh0Hc+xHvv9Op3PY19vt9iwAuP/++/Wrv/qrqtVq+i//5b/or//6r/Vbv/VbOv300w973JQxRpKOeb151lln6QlPeMIxT1ZMJhOV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LLmuu90S7Y0bN+ro3NkyNHP7IeoVT+tXRN+GnoktkCFLoSYen7V06VIdeOCB+uUvfznu9ssvv1z33HOPPvzhD/duu/fee8cdU6vVdNttt+m8884jiAAAANhDR877g6EZybStMAx05Nw/IIgYMgQRAAAAGDitVkt33HGHXvGKV+gLX/jCuH0QZ555pnzf10033SRJ+od/+Acdfvjhestb3tLbISGpN/ZJkl772tfqiiuu0Kc//Wldf/31uuiii3Tuuefqc+9cJsMwtP9hKb3pioP0/U+v0ItOHtFBx2TUrPl68JbN+u0vnxtX24tPHdUJr52nRMbWs8truu0H63Xhew/VL65ZrUd/XZzy34uXvHquXvzyUWVGHdWKnu6/+Tndf8PmcceM7hvTy9+wr/Y/LCXTNLT6iapu/s46lZ5rS5Iu/+KLJUmnnbdAp523QJI09p5X6s57fjXhc5511ll68MEHVSqVerctWrRIxxxzjN7xjnfstOabb75ZV111lbLZrMrl8h593QAAALPVaOJAZWMLoi5j2hiGqVx8P43El6jQXBl1OZgiwxeZAQAAYFa44YYbNG/ePC1dunTc7WeeeaZ++9vfqlgs7tLjvOENb9Dll1+uj370o/rZz36mdrutTmfLOCTbHL+o+szzF6qwsaVrv/iMnn64olPPXaADjkr3Pn/QMRm96oL99MxjVV37xWe06rGqXveXi3fr6zLMrf4zdnzsK968QCf/wT569K6CfvyfK/XIrws69Q376phTR3vH5OY6+uO/P1jxpKVfXLNG1/33KiUzts67/EBZ9pYn+NbHnpIk3X/jc/rWx57Stz72lJ5dUZj0eV/ykpdo2bJl42476qijJEmJREJf+tKX9Ktf/Urf/va39eY3v3m7+z/yyCMyTXO7PzsAAADs3JL8qQrCiTtXh0UQ+lqSf3nUZWAK0REBAACAgXTPPfeoUCjojDPO0P333y9py46Cgw46SN/+9rd36THOP/98XXzxxfrQhz6kO++8c6fHP3V/WXf9dKMkadXvajrw6IwOPS6nZx6tSpJO/P35WvW7qn71P2slSc88WpVpGXrZOfvuUj1LXzFHS18xp/dx4If69F8vm/DY3FxXS18xRzd8c60evr3Qq8lxDZ30+vl66PYxKZROet0+atY9ff/TK+R7WzpH1i2v6y0fOVwvetmIHrplrDcOqlLo9H7dak784nZ0dFTz5s3T8uXLt7tdkt773vfqe9/7nr7whS/o+OOP16WXXqpGo6Frr722d2ytVtPGjRt15JFH6tZbb92l3xsAAABIrpXWwszvyTRmbnxoFEzD0n7Zl+iRTT9U269FXQ6mAEEEAAAABlIQBLrlllt0xhln6NOf/rQ8z9OZZ56pRqOh2267baf3v+SSS/SqV71K//iP/6j77rtvl57zmUcr4z4eW99SZsSRtKV7Yd6ihG77/rpxxyx/sLzLQcTjvy3q3q1GPYUKJz128ZFbOjGevL+srUcDr/pdVce/dr4yI44qYx0dcFRaj/+2pCAIe8c16742rm5q3wMSemiSx3fs2Li9GdKW3Rlz586VpHFjmST1jv3lL3+pb3zjG5KkBx54QPPmzdMFF1wwLojo3n/OnDkCAADArlucO1mGdtI2OyQMGVqcO0lPjd0QdSmYAgQRAAAAGFg33HCD/uiP/kgnnHCC7rzzTp1xxhm688471Ww2d3rf0047TU8//bQefvjhXX6+Vj0Y97HvBYolt1yNlsjYsixD9Yo37phGZdfb5htVXxtW7dqy6kTakmEaetu/HzXh57tBRDxt67gz5+q4M+dud0zgTR50pFNpzXG2DwrmzZsnSUqlUr1fS+rt6XjqqafGBQyPP/64Xv3qV2u//fbrLQrvHp9Op5XP58fdf9tf7+hze3LcTD42AADAVDsw/3JplgQRkqEl+VMJIoYEQQQAAAAG1rJly7Ru3TqdccYZKhQKWrhwoT73uc/t0n3f97736UMf+pA+/OEP633ve19vifWealQ8+X6oZGb8KXYiMz1t882arzAI9Z1/e3rCQGFsQ+v54zyteLiih24Z2+6Ydmvyr7lcLapYL/Y+Np5fWLF+/foXjnl+0bRhGHriiSckbVkk3g0cDMPo7dvwfV/tdrt332QyqZUrV8rzvN5jb/tc3V93P97Zcbvz65nQD2FIPz4vAADYM3E7r4QzEnUZM8YwDCWdUcWsrFp+OepysJcIIgAAADDQbrzxRp177rlqtVoqlUq6++67d+l+q1at0rvf/W598pOf1Pvf/359+MMfVhAEO7/jJMJQ2rS6oYOOyer+Gzf3bj9kaW6PH3NHVv1uy16KRMrS0w9XdnBcTXP3i2vj6oZ29H6w7wW95dWS1PFavRBh3OOtWqV2u625c+eq1Wr1br///vtVKpV09NFH65Zbbundfswxx2j9+vXjAgzDMDR37lw9/fTTqlaru/T1TocdhRR7Emzs7XG787zTWd90291gYzoDlH6pAQCAXZGPL4q6hEjk44u1oTbx3jQMDoIIAAAADLRf/epXuuCCC/Ta175W11577W51NqxYsUJ///d/r//4j//QlVdeqY9+9KPj3iD0g7Ys093lx/vNdRt1ztuX6FUX7qcn7i1p/qK4jjopL0kK9zzjmFBxY1sP3LJZZ/3FIt17/SatX1GXaRka2Sem/Q9L6dovrpIk/fraDTr/ykN07t8eqIdvH1Ot7CmVtbX/YSmtfbKmx3+7ZdfD2PqWDj42q7VP1eW1ApkbPWmCvYCdTkdPPPGEDjvssHG3+76vr371q/rrv/5rVSoVLVu2TCeccIJe+cpX6t///d/HHbto0SIlk8ndGos1HXhjeGJRhDBT8dhb7zSZ6hqmWxTdLv0e8ABA10033bTTY/7f//t/Wr9+vT71qU/p0ksv7XVqDptcbJGC0O8tqo4lTL39ky/SL65ZrUd/XZz25/+9M+fouDPnKp139PRD5d75Zq++uY7e8pEjdP3/rNGy2wu92+csjOmifz5Ma56o6nv/sWLcfS583yGqFj396HMrJ3zOIPSVjy/a4yDirLPO0pVXXqlzzjmn180blQ984ANav369vvSlL/VuO+WUU3ThhRfqgAMOUKPR0MMPP6wvf/nLWrduy965RCKhb3/723rve9+rZcsGO4whiAAAAMBAW7lypZYvX66DDz5YN9yw+/Njn3zySb3nPe/Rv/3bv+mKK64Y96Z5qbVWI/Elu/xYTz9U0Q3fWKvjz56nI0/Ia93Kum741rM6750HqtXcu9FPE7n5O+tU2NDWMaeO6sTfn69OK1BhQ0tP3PfCIunSpra+9bGndMof7qMz/nShnJipWsnT2idrem7tC7s0bvzWs3rFmxfqDe9YIsc1de/lKa0rTPSs0q233qo3vvGN293+ox/9SIZh6LzzztOFF16odevW6d///d913XXXjTvuxBNP1Pr16/W73/1uan4jMKUIaLbXz4HMzh5jb0abbf1xv4QzO/rcoAYyu3McMBu9/e1vH/fx5z//ef3gBz/Qr371q95tzz77rJYsWTLDlc28kfgBkS2qzs93dfp5C3TPLzfp6YcqalS97Y4pPddRtdjRwoOS44KI/Q5OqdMKtM+SpExT6jYhu3FTcxbG9fhvN0z6vIYM5eOL97juu+66S29/+9sj7cKVpEMPPVSnnHKKzj///N5txx57rD784Q/rl7/8pb7yla8ol8vpz//8z/WJT3xCb3nLW9Rut9VoNPTDH/5Ql1xyid71rndF9wVMAYIIAAAADLxLLrlk0s997GMfG/fxL37xC/3iF78Yd9tjjz2m173udb2Pr7nmGl1zzTU6ev65yscXac0TNX3ysu2v3t/2KjBJeui2MT102wv7GF50ypY5vs+t2fEC7f9+7+M7/PxkNTx482Y9ePPmCe7xguLGtq77yuodHvPs8rq++dGnJEmV1gbdt/LeSY/95S9/qUsuuUTHHHOMHnrooXGf++EPf6gf/vCHO3yuM844Qz//+c93eAzQT3hjeHv9MM6M0Wb9G5rM5GNj+D322GPb3bZhw4YJbx92+cRiGYa58wOnwcg+MRmmoWW3j6n03PbjO7ueXV7XwoNT425beHBSv7unqCNPzGve4oQ2rGxIkhYclJRpGnr2qQnacJ9nGKby8QMm/bzjOPI8b7t/G0zTlGEYKpVKKpVKk9x713Ufb0/3yp133nm65557tHnzC+ftZ5xxhjZs2KCPf/zjvdsKhYI++clP6vDDD+91D//85z/XxRdfrIMPPljLly/fuy8kQgQRAAAAwCSKzdUyjV0/ZY4lLZ38+vla9XhVnWagfQ5I6ISz5+upB0oqb578BVs/CUJfhebKHR5TKpX04x//WOedd952QcTOHHPMMVq4cKG+//3v70WVAKLGWKWJRR3I7E0nzd50z/R7ODPVx031qLTpeN7ZLJPJ6H3ve59OPvlklctl/fjHP9a3v/3tcceceuqpuuiii7R48WKVy2XddNNNuvrqq3v7sSYb53P11Vfrqaee6l3osmTJEl166aU68sgjFYvFtHHjRv385z/vPd9RRx2l888/X4cffrhSqZTWrl2r7373u7r++ut7j3nsscfqU5/6lN797nfr7LPPnrTuuJ3TcafvrxPOnq9kxta6p+u6/Qcv7ODqOuiYjE563XyN7BNTEEilTS3dee1GrVw2+U4xyzb0sj/aV4e/NKd4ytLY+pbu+tlGLX9gy9f+mov314tO3nJxzVs+coQkTToO6tnlNR32kpwSGUuNypY37RcenNKd127QnAUxLTw42QsiFh6c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N3drLFPXuqfrCvxQv3fGXIWhtHHVC90nzz4fRCw4ODluLJPtGvL98c915An53q+nqmP34YcfVjqdHrf42TRNnXrqqdstiN6ZFStW6D//8z8ljf9e2dbq1auVy+V6Y7i6NmzYoEMPPXTcbfvss49yuZzWrx9/Tr7vvvuqWq1uN/5rkNARAQAAAOzEqtKvdfjc39cw90OYhqmnC7dGXQYAAMC0uuaaa/Qv//Iv+qd/+iddf/31Wrx4sd761rfqlltu6S20fuyxx7Rhwwa9/e1v11e+8hUlk0mdf/75465GP+igg/S2t71NN910k5599lmlUildcMEFWrdunZ599lkFQaDHHntMf/qnf6pisSjf93X++eerVqvJdd3drvsb//MNffgjH9BrLtpPj/+2pPmLEzryxJFxx7z41FEtOCiplY9UVCt5ys11dMSJea3awcLq59Y29eR9JZ32xgWyXVNj61s68sS8Fh6U1E++8Mxu1ylJnVagTWubOvDojNatqMv3XggY1j1d0yvfvECGaWjtUy8EEc88VtWZf7qfTvz9+Vr3dE1Ljs5q0RGp3ueb3vY7FvbEXXfdpccee0z/9E//pKuvvlqFQkFveMMbNDo6qm984xs7vf9nP/tZ3XbbbVqxYoWCINBrXvMatdvt3jiuiSxbtkyWZemQQw4ZF3Zce+21+pu/+Rv9zd/8je68807lcjldeOGFKhaLuvnmm8c9xuGHH65ly5YN9E4ZgggAAABgJ1p+ResqD2hB5tihXFodhqHafk3rq5O/gAIAABgGd955pz70oQ/poosu0kc+8hFVKhX99Kc/1dVXX907xvd9/fM//7Pe9a536QMf+IDWrl2rz3/+83rb297WO2ZsbExjY2M6//zzNW/ePFWrVT388MO66qqrFARbRhx95CMf0d/93d/pyiuvVLlc1g9+8AMlEgn98R//8W7XfdsdN+u6r/2pXv76JTr8pXmtW1nXz76ySudfeUjvmOfWNHXQizM6/Y0LFE9Zqpc9PX5PSXf+ZMMOHln6+f+3Wi//o311/FnzFEtaKmxo6adfXqWnH67sdp1dzz5V0z6LE71F1V2b1jTVaQfPL6qu9W5/+NYx5ea6WvqKOXrJq+fqmUer+vl/rdafXnmImp2KvGD75dR7IggCXXnllbrssst06aWXKpFI6IknntB73vMePfHEEzu9/7Jly/Sa17xGCxYsUBAEWrFihd773vdq1apVk95nzZo1Wr58uU488cRxQcT3v/99tdttnXPOOfr93/991et1PfLII/rgBz84LvSyLEvHHXecvvSlL+3dFx8xY+nSpYMbowAAAAAzJO3uo1cs+QeZxnBey/PA+m9pVenXUZcBAACASbxkwZ8P7YUxkwlCX89W7td9674WdSl75Q1veIPOO+88XXjhhbt935NOOknvfe979aY3vUnN5tQEMlFgRwQAAACwC6rtDfrdc9cNdDv0RILQ18ba44QQAAAAfa7YXCVjqIeFbs+QoWJzddRl7LWf/exncl13j3aEvOlNb9L3vve9gQ4hJIIIAAAAYJctH7tRpdZqBaEfdSlTIgxDBaGnB9d/M+pSAAAAsBOl1moZxux6O9cwTJWGIIhot9v62Mc+Jtveve7qeDyuBx98UN/73vemqbKZM7u+cwEAAIC9ECrQfev+J+oypoxhGFq28QdqeIWoSwEAAMBOFJtroi4hEqXWcHzd9957r2699dbduk+z2dTXvvY1NRqNaapq5hBEAAAAALuh2l6v3z33s4Ef0cRIJgAAgMHiBQ0VGs8oCIOoS5kRQRhorLFiyhZVI1oEEQAAAMBuWj52ozbWHlU4oC8Cg9BXy6vogfXD090BAAAwG6wo3iJzloxnMg1TKwq3RF0Gpsjs+K4FAAAAplCoQPc8+98aa6wYuCvSgtBXx2/oztWfUdMrRV0OAAAAdsOzlQfU9utRlzEj2n5N66oPRV0GpghBBAAAALAHgrCj36z9ksqtNQMTRgShLy9o6ddrPqda57moywEAAMBuCkJPzxTvUBD6UZcyrYIw0Mri7QpCL+pSMEUIIgAAAIA95AVN3bHqsxprLO/7MU1B6Kvt13T7qv9QufVs1OUAAABgDz1TukPGkL+ta0h6pnhn1GVgCg33dywAAAAwzfywpbvWfKHXNt6PgUQYBqp3Nuu2Z/5D1fbGqMsBAADAXqh3xrSx9ujQdkUEoa8N1UfU8ApRl4IpRBABAAAA7KUg9PTbZ/9b9637uryg3TcvCoPQVxgGWl64STev/Jga3ljUJQEAAGAKPLLph5LCqMuYHmGoRzb9KOoqMMUIIgAAAIApsqZ8j25c8RFtqv1OUrTdEVu6IMZ0+6pP6dFNP1YQdiKrBQAAAFOr2t6oxzb9VGE4XGFEGIZ69LlrVetsiroUTDGCCAAAAGAKtfyyfrP2S73uiDAMZvQF4gtdEDfq5pX/T4Xmyhl7bgAAAMyc5YWbVGyu6ptu3L0VhL4KzZV6unBz1KVgGhhLly4drtgMAAAA6BOuldYBuVN04MhpittZBaEv07Cm/Hm6QUcQdrS6fLdWFG5Tpb1uyp8HAAAA/SXtztcrllwp07CjLmWvBYGnm1Z+lG6IIUUQAQAAAEwzQ6b2Sb9IB+ZP07zU4QpCX4ZMGYaxV4/bDTaq7Y16unCL1pTvkRc0p6hqAAAADIKDR87QUfPO2etzyyiFz++FeLpwU9SlYJoMflQGAAAA9LlQgdZXH9b66sNKOfO0KHeCRuIHKh9fLMeKS9qy8NqQNekLyK0/H4Seyq1nVWg8o2cr92tz46mZ/HIAAADQR5YXblIuvkj7ZX5PhjF4k/jDMNDayn2MZBpydEQAAAAAEUo6c5SLLVI+vkjZ2EI5VkKW4co0HIXy5AcdeUFb1fYGFZurVWqtUqW1XqGiW4QNAACA/mLI1An7/aXmp44cqDAiDANtqD2qe9Z+hfPbIUcQAQAAAAAAAAADzjQcnbDfX2le8tCBCCPCMNCm+uO6e+3VCkIv6nIwzfr/OxIAAAAAAAAAsENB2NHda7+kddWHFIb9fe15GIZ6tvKgfrPmy4QQswRBBAAAAAAAAAAMgSD09Ntn/7/evoUg7K9xR916ni7cpHvXfVWh/IgrwkxhNBMAAAAAAAAADJk5iUP0ewsuVMLO98WopjAM1PCKum/d1zXWWB51OZhhBBEAAAAAAAAAMIQsw9WR8/5AB42criAMZEYQSHSf9+nCzXps00/lh+0ZrwHRI4gAAAAAAAAAgCG2pTviAiWdOQpCX6ZhTftzdp+n3tms+9b9D10QsxxBBAAAAAAAAAAMOUOm9k2/WAeOnKa5yUOnLZDoPu5ztSe0onir1leXKVR/7arAzCOIAAAAAAAAAIBZJO3uoyX5l2lx7mTZZkxB6MmQJcMwdvuxwjBUKF+mYcsLmnqmeJeeKd2uanvjNFSOQUUQAQAAAAAAAACzkGW42if9IuXji5WPH6B8fH/ZZlzSls4GqfvWsTHu191OCi9oqthcrWLzGRUaq7Sx9ig7IDAhgggAAAAAAAAAgCRDKWeucvFFyrj7yjIdWYYj07AVhJ78sCM/6KjSXqdSc7Vqnc16IaAAJmdHXQAAAAAAAAAAoB+EqnU2qdbZFHUhGDJm1AUAAAAAAAAAAIDhRRABAAAAAAAAAACmDUEEAAAAAAAAAACYNgQRAAAAAAAAAABg2hBEAAAAAAAAAACAaUMQAQAAAAAAAAAApg1BBAAAAAAAAAAAmDYEEQAAAAAAAAAAYNoQRAAAAAAAAAAAgGlDEAEAAAAAAAAAAKYNQQQAAAAAAAAAAJg2BBEAAAAAAAAAAGDaEEQAAAAAAAAAAIBpQxABAAAAAAAAAACmDUEEAAAAAAAAAACYNgQRAAAAAAAAAABg2hBEAAAAAAAAAACAaUMQAQAAAAAAAAAApg1BBAAAAAAAAAAAmDYEEQAAAAAAAAAAYNoQRAAAAAAAAAAAgGlDEAEAAAAAAAAAAKYNQQQAAAAAAAAAAJg2BBEAAAAAAAAAAGDaEEQAAAAAAAAAAIBpQxABAAAAAAAAAACmDUEEAAAAAAAAAACYNgQRAAAAAAAAAABg2hBEAAAAAAAAAACAaUMQAQAAAAAAAAAApg1BBAAAAAAAAAAAmDYEEQAAAAAAAAAAYNoQRAAAAAAAAAAAgGlDEAEAAAAAAAAAAKYNQQQAAAAAAAAAAJg2BBEAAAAAAAAAAGDaEEQAAAAAAAAAAIBp8/8DXohAq1LO7YgAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Traverse graph looking for certain nodes and edge values\n", + "g = embeddings.graph.search(\"\"\"\n", + " MATCH P=(A{id: 8})-[R1]->()-[*1..3]->(D{id:5})\n", + " WHERE\n", + " R1.relationship == \"has_object\"\n", + " RETURN P\n", + "\"\"\", graph=True)\n", + "\n", + "plot(g)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "E_dE7y5N8SUy" + }, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook showed how knowledge graphs can be created with LLM-driven entity extraction. Automatically derived relationships via semantic similarity and manually specified ones are a powerful combination!" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "gpuType": "T4", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "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", + "version": "3.8.18" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/examples/58_Advanced_RAG_with_graph_path_traversal.ipynb b/examples/58_Advanced_RAG_with_graph_path_traversal.ipynb new file mode 100644 index 0000000..6bae573 --- /dev/null +++ b/examples/58_Advanced_RAG_with_graph_path_traversal.ipynb @@ -0,0 +1,343 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "i-EWwabdk6YH" + }, + "source": [ + "# Advanced RAG with graph path traversal\n", + "\n", + "Embeddings databases are a union of vector indexes (sparse and dense), graph networks and relational databases. This enables vector search with SQL, topic modeling, retrieval augmented generation (RAG) and more.\n", + "\n", + "A standard RAG process typically runs a single vector search query and returns the closest matches. Those matches are then passed into a LLM prompt and used to limit the context and help ensure more factually correct answers are generated. This works well with most simple cases. More complex use cases, require a more advanced approach.\n", + "\n", + "This notebook will demonstrate how semantic graphs can be used to build a more comprehensive context for LLM generation. It will cover an example of writing a short book on English history from the fall of the Roman Empire to the Norman conquest.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-f5LR3SNk6YI" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ZeYuyoPKk6YJ" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[graph]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9adE7bwpk6YJ" + }, + "source": [ + "# Build a knowledge base\n", + "\n", + "The first step we'll take in writing this book is collecting information. We'll use the [txtai-wikipedia](https://huggingface.co/NeuML/txtai-wikipedia) database, which stores all Wikipedia article abstracts as of January 2024. This database is a great way to explore a wide variety of topics. It also has the number of page views integrated in, which enables pulling frequently viewed or popular articles on a topic.\n", + "\n", + "For this example, we'll load the top 100,000 most popular articles across all of Wikipedia." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "60K6QcwUk6YJ" + }, + "outputs": [], + "source": [ + "from txtai import Embeddings\n", + "\n", + "# Create embeddings instance with a semantic graph\n", + "embeddings = Embeddings({\n", + " \"autoid\": \"uuid5\",\n", + " \"path\": \"intfloat/e5-base\",\n", + " \"instructions\": {\n", + " \"query\": \"query: \",\n", + " \"data\": \"passage: \"\n", + " },\n", + " \"content\": True,\n", + " \"graph\": {\n", + " \"approximate\": False,\n", + " \"topics\": {}\n", + " }\n", + "})\n", + "\n", + "# Load dataset\n", + "wikipedia = Embeddings()\n", + "wikipedia.load(provider=\"huggingface-hub\", container=\"neuml/txtai-wikipedia\")\n", + "\n", + "query = \"\"\"\n", + "SELECT id, text FROM txtai\n", + "order by percentile desc\n", + "LIMIT 100000\n", + "\"\"\"\n", + "\n", + "embeddings.index(wikipedia.search(query))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "T-sb7V5Ok6YK" + }, + "source": [ + "# Collect articles of interest\n", + "\n", + "Next, we'll build a graph query that traverses topics of interest for our book. Given that this book is about the history of England from the fall of Rome to the Norman conquest, we'll walk the graph and pull in articles relevant to that period of time.\n", + "\n", + "The following builds a graph path traversal query that finds the top 20 paths between these topics." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "BrrfZ1Grk6YK" + }, + "outputs": [], + "source": [ + "g = embeddings.graph.search(\"\"\"\n", + "MATCH P=({id: \"Roman Empire\"})-[*1..3]->({id: \"Saxons\"})-[*1..3]->({id: \"Vikings\"})-[*1..3]->({id: \"Battle of Hastings\"})\n", + "RETURN P\n", + "LIMIT 20\n", + "\"\"\", graph=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jPO3OkcWk6YK" + }, + "source": [ + "A new feature of txtai 7.0 is the ability to return a graph of search results. This is a powerful addition as not only do we get the search results but we get how the search results relate to each other.\n", + "\n", + "Now lets build a visualization showing what we've collected." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "_wPXbwwjk6YK", + "outputId": "35a13e5c-6f69-4289-fa7d-71046b6ee507" + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABiIAAALJCAYAAAA57jvpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8WgzjOAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdd3hUZdoG8PucmTnTSxqd0DsINhQUYcFeVl0bKyq4wKroWnAt66qrq65dsawNd+2Crrr62VcBEQWR3pVOCJ2UyfR2zvfHcA4zyaSSZEru33XlUiYzZ94kk8zMe5/neYRhw4YpICIiIiIiIiIiIiIiagFiuhdARERERERERERERES5i0EEERERERERERERERG1GAYRRERERERERERERETUYhhEEBERERERERERERFRi2EQQURERERERERERERELYZBBBERERERERERERERtRgGEURERERERERERERE1GIYRBARERERERERERERUYthEEFERERERERERERERC2GQQQREREREREREREREbUYBhFERERERERERERERNRiGEQQEREREREREREREVGLYRBBREREREREREREREQthkEEERERERERERERERG1GAYRRERERERERERERETUYhhEEBERERERERERERFRi2EQQURERERERERERERELYZBBBERERERERERERERtRgGEURERERERERERERE1GIYRBARERERERERERERUYthEEFERERERERERERERC2GQQQREREREREREREREbUYBhFERERERERERERERNRiGEQQEREREREREREREVGLYRBBREREREREREREREQthkEEERERERERERERERG1GAYRRERERERERERERETUYhhEEBERERERERERERFRi2EQQURERERERERERERELYZBBBERERERERERERERtRgGEURERERERERERERE1GIYRBARERERERERERERUYthEEFERERERERERERERC2GQQQREREREREREREREbUYBhFERERERERERERERNRiGEQQEREREREREREREVGLYRBBREREREREREREREQtRp/uBRARERERUVsjwCYVwWkshk1qB70oQRQMEAU9ZCWKmBJGTA7DE96LyuBO+CMH071gIiIiIiI6AgwiiIiIiIioRelFE9pbB8FlKobLVAynqSv0ogQAkJVowjUFAIr2/6KgAwBEYkFUBksOfezAPt96yEqkVb8GIiIiIiJqOmHYsGFK/VcjIiIiIiJqHIexE7q7TkZXx3DoRAmyEoUAHQRBaPSxFEWBAhmioEMkFsAO90LsqPwRPlZLEBERERFlPAYRRERERETUbERBj462o9AjbzTyzT0gKzGtsqE5qcfd7/sF2yq+xz7fOhyupiAiIiIiokzC1kxERERERNQsiiz9cXTHCTDpnZAVGQBaJIRIPG6hpQ/aWfvDG96PFXveRkVwe4vcHxERERERNR0rIoiIiIiI6IjoRRMGFV2Ibq4RkBUZoiC2+hpkJQYBArZUzMUvB7+oNnuCiIiIiIjSiUEEERERERE1mVoFIensaQkgqlMUGb7IQVZHEBERERFlEAYRRERERETUaAJEDGl/Cbq7TkpbFURt1OqITeXf4JeDX4CzI4iIiIiI0oszIoiIiIiIqFF0ggHHdZqMdtb+AJBRIQRweH5En/zTYdK7sGrvLCiQ07wqIiIiIqK2i0EEERERERE1mE4w4sQu1yLf3ANChgUQ1QmCgK6O4TCIJizd/ToUxNK9JCIiIiKiNimz3zkQEREREVHGEAU9TuhyTVaEECpBENDBNgTHdLwSgJDu5RARERERtUnZ8e6BiIiIiIjSSoCI4zpejQJzz6wJIVSCIKKT/Wgc1f7SdC+FiIiIiKhNyq53EERERERElBZ9C85Ae9vgrAshVIIgoLvrJHRzjkz3UoiIiIiI2pzsfBdBREREREStxmHsgj4FZ0AQsru1kaIoGNTudzDr89K9FCIiIiKiNoVBBBERERER1UqA7tB8BSXdSzligiBAFHQY1uHydC+FiIiIiKhNYRBBRERERES16ltwOuxSB4iCLt1LaRaioEORtR9bNBERERERtSIGEURERERElFKutGSqji2aiIiIiIhaF4MIIiIiIiJKaUi7i5ALLZmqU1s0DSg6L91LISIiIiJqExhEEBERERFRDXapIwosvXKmJVN1oqBDJ/vRkHS2dC+FiIiIiCjnMYggIiIiIqIaurtOhqzE0r2MFiVAQLFzRLqXQURERESU8xhEEBERERFREp1gRFfnCTlbDXGYgB6uUQByawYGEREREVGmYRBBRERERERJujiOg04wpHsZLU4QBJgNLrS3Dkr3UoiIiIiIchqDCCIiIiIiStIzbzRycUh1KrISQ4+8UeleBhERERFRTmMQQUREREREGpPeBbuxAwShbbxVEAUdCi19IQr6dC+FiIiIiChntY13F0RERERE1CAuU9d0L6HViYIOdqljupdBRERERJSzGEQQERERUcZ66KGH8NZbb9X6+QsvvBDz5s1Dp06dcMcdd+Df//639rkzzjgD8+bNg8PhqPX2EydOxBdffNGsa24NVqsVEydORLdu3ZIub9++PebNm4dTTjmlUcebNWsWbrzxRgCA09gVPY+y4ajR+c223oun98D507rVf8UGOPHcdujY01Lj8lteGoJjTyts0jEVRak3gJk1axbmzZunfXz88cd48sknMWTIkAbdR6qfzUUXXYQTTjgh5X2pP4+GqP7Yb4x58+bh0ksvbZX7IiIiIqK2i/XHRERERJSx5syZg3vuuQf9+vXDr7/+WuPzY8eOxbp167B792689dZbMJlMjTr+559/jp9++qm5lttqbDYbJk2ahG3btmHHjh3a5eXl5Zg2bRpKS0sbdbx77rkHHo8HAOAyFaP3MCfad7Ng9fzyZlnv3Fm7ocjNM3NixLntEQnJ2LPV3yzHAwAFMThNXQF33df77rvv8P777wMA8vLycNFFF+HRRx/FlClTsHv37jpvm+pnc/HFF2PRokVYvHhx0nUTfx5ERERERLmAQQQRERERZawff/wRfr8f48aNqxFEtG/fHoMHD8azzz4LAPVuBKdy8OBBHDx4sFnWmgkikQg2bNjQ6Ntt3rxZ+/88czEgCM25LJTvCTXr8ZqbKOiRZ6q/YqOioiLp+7t69Wp88sknOP744/HJJ5/UejtJkhAOhxv8s0n8eRARERER5QK2ZiIiIiKijBUKhfDjjz9izJgxEKptjo8bNw6xWAzz5s0D0LCWMWeeeSb+97//4eyzzwZQszXT0KFDMW/ePBx77LG4++678fnnn2PWrFkYP358jWOdd955mDVrFr788ks8/vjj6N27N+bNm4czzjijzjVccsklePHFF/Hpp5/io48+wj/+8Q906dKlxvUGDhyIxx57DJ999hk+//xzvPDCCzj22GPRvn17zJ49GwBw//33a62C2rdvX6P9T23fkxNPPBHz5s1D167xdkRqKyCT3olz/9Afg0bkobCzCbe8NAS3vDQEp0/sgp5D7LjlpSFwtZOSjmW0iPjTs4PqbOVUvTXTiee2w/Uz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QGdo+qDsUCC5BVEsqkBvOHzuldmmQ6eeFtz8wpAat63cH4Ig1H2eVmVlJTZu3Kj9e+nSpejTpw+mTp2KZcuWaZcHAoEGPTYb6rbbbsPkyZNx0003wWw249dff8ULL7yA1atXa9ep/piNRCIAoD2umvO+EjmdTgwaNAhz5syp8bldu3YBiFfu/OUvf8GECRPwwAMPQJZl/Pzzz3j22Wexf//+Rq2PiIiIiLITgwgiIiIiynhr167Fnj17MHbsWFRUVGjtYxri7rvvxv3334+///3vuPvuuxvVLz8Vt9uNaDQKl8uVdHn1f6cyfPhwmM1m3HvvvfD54pv3oijC4XBo1/F6vYjFYg06G70h1q5di3379mHs2LHYuXMnTjjhhFq/d7ISrf94P5bjrD90hdWlR//hLqycV9Ys62wNQV8M29Z6sOj/9tX4XDQqIyZHGn3MkpISjBw5sjmWV6vS0lLcf//90Ol0GDx4MKZMmYKHHnoIl1xySbMGHk25L4/Hg8WLF6cciq6GIUC8ymjJkiWwWCwYPnw4pk2bhjvuuAO33nprs66fiIiIiDITWzMRERERUVaYO3cuRo4ciXPPPRdutxs///xzg25XUlKCP//5zxgwYADuuecebdhwU8myjE2bNtXYfD755JPrva3RaISiKElhyG9+8xut5RQQnzuwfv16nH766bWuNRqNBwYNPdt93rx5GDNmDE455RSIooj58+envJ66ES9HFehrqUzYsrIKQX8MZ/+hK0xWHdYtqmjQGppLLCpDp29a1UTJL14UdDSibG8Q+0oCSR9lu0OQlcYHEd26dYPb7W7SeiKRSKMqFmKxGFatWoV3330XNput2cKqI7mvZcuWoVu3bigpKcHGjRuTPrZt21bj+n6/H9999x3mzZuH4uLiFls/EREREWUWVkQQERERUVb49ttvMWHCBJx55pn49NNPG1XZsG3bNtx222146qmncOedd+Lhhx+Goij137AWb7/9Nh566CHceuutmD9/Pnr37o0zzjgDAOo87vLlywEAd9xxBz799FN0794dl1xyCTweT9L1XnnlFTz11FN44okn8Mknn8Dj8aBv375wu9348ssvUV5eDo/Hg3HjxmHv3r0Ih8PYunVrrfc7Z84cjB8/HldffTWWLl2KqqqqGtcRBAGKGAIAlO0NYtDIPPQ7zonK/WEEfFFUlR0K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R3VRT0pUWzONGjUqZWulqVOn4qeffsKDDz6Ycg/h888/R1FREcaNG6e1e/L7/QCAoqIi7Ny5E99++y38fj969+6NSZMmwWw2480336xzfSNHjkSXLl3wzDPPwOl04vrrr8eNN96IBx54oM7bEVHmYxBBlGOcTicGDhwIm80GWZZTVj40B/W4JpMJffr0Qe/evbFz505s3rw5K9ro0JERBAEGgyHlEGm1BFudH1BbOXYukpUolu95E6d0uzXdS2k2oiBi5d53EJXr3iRSB5X6/X4YjUaYzWa4XC6tzRY3mYhajt1uh8FgQGVlZVqeg41GI6shqMEikQjcbjf0ej3MZjNsNhssFgtb/BEdor7PKiwsxDXXXAO/3580eHjixIlYv349HnzwQQDAkiVLEAqFcOutt6JHjx7amfIAMH/+fG3T95dffsGoUaMwduxYTJgwQateHTZsGEaPHq1tRG/btg0vvfSSdow1a9YgGAzizjvvxDPPPJNUyVReXo6HHnpIW0efPn0wevToOoOISZMmIRKJ4E9/+pO2ab18+XLt8z179sQpp5yCp556Cif+3//BDCD0888o6tABjokTk4MIux0HbroJ0e3bAQBKMIiiGTMgDRyI8Jo10HfrBvOoUah4/HH4v/wSABBcsgQd3n67IT+KGpx//CPCv/6KskMb/gAQ2boV7V97DaYTTkBw8WJIAwYgsmULPO++q10n+OOPh6+/eTMAILprF8Lr19d6XwYAZ511Ftq3b4+rr74aJSUlAIBVq1bhvffew8UXX4wXX3wRmzdvRnl5eYPbHJnNZsyZMyfpsrlz56Zsy7R582Y88cQTtR7r4MGDOHDgQI12T0D8Z5r4c12zZg1MJhMuuOCCeoMIQRDw17/+VTuhqUOHDpgwYYJ2ghsRZS8GEUQ5QhRF9OrVC926ddOenFsqhEiUWCVRXFyMoqIirF27lmWTOUQUxZSVDkDmD5FOB3doZ860aFJbMh3w/9qo26ltOAwGg7bJlNi2iWElUfMxm80wmUyoqqpKS+irPiewxQ41VjQahcfjgd/v11r8qYFEIBDgZhO1SdU3iWOxGO6++27s3Blvj2kymdC7d++koACIt8W59dZbMWTIkKQgYunSpdr/+3w+VFRUYPXq1Umv10tLSzFs2LCk41100UU499xz0bFjRxiNRu3yjh07YvuhTf/qxweA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5zDqcM/oTji7rwt5Jh02HAzige924bvth/8OfnJ5H/jCMj5YV447R3VER7uE73d4MO3T7bAbdXj6zGIM72JFqTuM27/ZiR9LvOoPBn5HF5xxxhm45JJL0LVrV7jdbnz99dd47bXXtOcttW3R1KlTMWXKFBx11FEoKyvDW2+9hf/9738A4q2Ihg0bBgCYN28eAOD111/HG2+8kfLrqu/nN3HiREyaNAkA8NRTT9V7vFTUNkCJLXfVlknz58/H5Zdfjs6dO2PatGno0aOH1prJbDZrv0/333+/dtvx48dj3759mDp1Kk488UR07NgRXq8Xq1evxgsvvIDy8vIa96O2ZmroYzHbWK3WeqshgMafBJStFRGJEudG6PV6VFVV8bVgM0lVLdEtGs3pIdXVxQD0AoMIajgGEUQZ7pdffkFBQQEEQcjaszFSURQF27ZtQ0VFvATbYDDAarUykKA2Q+3Dng1BRCwWQzgchsViYRCRRSKRCCKRCERR1Fo2qT/DQCDAfsFN5HQ6IQgCKisrc2KDIpHalokbFG3b4MGD0bFjR7z55ptYsmQJ3G43xo0blxREqG655RZ88803+OKLL3DyySfjmmuuwZYtW7QNzZEjR2L69On47LPP8P3336NXr17429/+Vu8aRo4cifvuuw/z5s3DK6+8guLiYkyZMgXt2rXDfffdV+dtN27ciDFjxmDjxo34/vvva+3L36FDByxatAjvv/8+ZFnGCSecgEceeQTTp0/HqlXx2QhPPvkk/v3vf+OGG27A448/joKCAtx888345JNPtK/x2muvxUUXXYS33noL69atw4gRIzB9+nTodDp8/PHH2v3pdDr89a9/xUcffYS33noL48ePx/3334/x48drZ3FHTY6kNY7oasNzZ3fDc4v34dstbpgNIo7paIXTdDioePjULrhiaCGeXrgXy3b7cHwXK/42phOCERmvrzyIt1cdRCe7ARcNzMeFszYCADwhGZ5wLOXlqRhEAR+N740iiwEPfb8bezxhXDooH7Mv6Y3fvL4BGw4crro7qr0ZBeZ2+Nu8XXAYdXj41C6YcVYxujolvLe2HP/8eR9uHtEBb1zYE0NfWAtfRAZEHSaefjxuO/so/Oc//8GLL76Ibt26YfLkyRBFETNnzkxaz1//+ld8/vnn+M9//oNzzz0Xd9xxB3755ReUlJRgxowZuOuu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+0rVwEiSxH777Qf03xj32WcfPvzhD7NmzRpeeOEFAJYuXcoZZ5zBZz/7WR599FH2339/TjjhhJ32tW7dOk466STe/va309PT4/8M9/hAHMfhkUce4YILLuBDH/oQzz//PNlslkgkwty5c+nu7mbFiqF7977++uts2LCB973vfTz44INks1kOPfRQDMPYySeh2qxZswaAU045heeee45p06Zx+OGHV7R0mj59OscffzwrVqwgkUgQCAR4+9vfTiKR8NtcdXd3c8IJJ7B48WLWr19PMplky5YtExqT4zjccsstfOpTn6K3t5fnn3+eRYsW8Za3vAV4cwJywQUXYJomzz//PIlEgj322IO3ve1t/PGPf5zEJyJod7yyVK8ywutLLRgar0IiFAoRCoWmtEJC0zQcx2m67Kpmx6viy2azImDYYMRiMSRJ8gXVdkWWZQzDIBAI+P4CXoXb85tv5bDdLqrzCKuDJEm8kb6bEo0rOkmSVJEdC6LyQSAQND8nADb9AftWxQAOBbx6u4EJQIOv7ZIk+ZUDhUKh4YLTtWC0agnHcfxKiUaqlvDGVy6Xx10xEA6H/eSORm59NNA3IhaLkc1mx+x7UW+ET4RgKhFCxAQJBoNcc801QP8FZ+vWrdx3333ccMMN/qLzqaee4le/+hWnnXYa7373u1m+fDlXXHEFv//97yv2ddttt7Hbbrvx5S9/mWg0yvXXX88NN9ww7OMDsSyLnp4err/++govhUwmw7p169i6deuw78F1XW6//XYWL17M4sWL0XWdzZs3c9ttt7F58+Yqf2KVbNu2jbvuuoujjz6aM844gy1btvCXv/yF0047zd8mm82SyWR4xzveQTQapVAo0N3dzR133OFXU/z9739njz324LOf/SyxWIy7776b//7v/57wuP7yl79gGAannnoqH/jAB/j3v//Nr371K77+9a/7rSZeeeUVPvjBD3LssccSiUTYtm0bf/jDH7jpppsm/bkI2hvXdUkkEpimiWmapNNpYYQ+CpZlYVlWhSCRz+fJ5XI1W4zoui6qIeqAJ0SEQiHRFquB8MrQGzE7bSqJRCKEQiG/XcBgIXlz5kXWp5YyK7qoqVs0Oa5Nj/U6m62lxOPxhlpkC/FBIBC0Mru6LgfUexBTgA2cyJtCRCAQwHVdIpGIHygtl8t1ER8arYf+SN4SwWCwIbwlZFkm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QVVVp1JqPGoRRBiGQblcntBw6C1gNTCH1m3RpAIP1XsRbaDRB0/XUrU64mDgGGAalcBrIiskqre/CbgPeBipgmh3EkQIIUSLKRaL/N///R/XXnstxxxzDFdffTUAixYt4oorruC2225zjj3++OP58pe/zMEHH+xc5vP5+J//+R/2339/QqEQXV1dPPTQQ87tVB1wwAGcfvrpTJ06lWXLlvGrX/3KaUH0xz/+kdWrV3PRRRc5x4fDYQ499FB23nlnfve73w35wi4YDHLYYYcxf/58VFVl7dq1PPjgg2zatAmAc889l3A4DOC0h/r9738/bFXE448/zqc//Wlmz57NunXrhjxuS/ddvf833niDRCLBHnvsQTAY5LLLLuOTn/wkhUKBJUuWcMABBxAIBFi1ahX33HMP8+fP54tf/CI777wzGzZs4LLLLuOll15ybvOII47g2GOPZauttkJRFN58803++Mc/snz5cueYU045hRNPPJEvfelLfO1rX+N973sf7777LldccUWftlSapnHOOedwxBFHoKoqjz76KC+80LeNz2DfH4CzzjqLww8/nBNPPLFpzizMhLcCte/L5698aDo/OGgWVzy7kYv/+y6KAu+f4uaEnTqY4debPohAUcgGZ+OPvlXvlQgh6sS2bZLJJIVCAb/fTyQSIZFIyMwY0bIURSEQCGCaJvl8nmQy2XKbhOPhdruxbXvcwUytKiImshoCAEXhHtvmSxN7L3VTBl4ANsmG7YRppsHTtWQrCg8Bi2ybHYHDgN2pzG2oZSBRpnKe2AvAv4GlAPLzLJAgQgghWtLbb7/Npk2b2HHHHUd1PV3XufTSS5kxYwbXX389b731FtOmTWPnnXfuc9x2223HSSedxFVXXYWqqpx77rl873vf48tf/jIA9957L+eeey4+n88ZqOn1etl555155ZVXhnxzYxgGn//857Ftm3/961+USiX23XdfPv/5z3P11VeTTCa588472WeffZg3bx533HEHwBZ70L7xxhusX7+e/fffn1tuuWXM9121ww470N3dzb///W8sy3LKdWfMmIHX6+XBBx/ENE2OOOIIjj76aI477jjuu+8+brvtNj772c/yk5/8hBNPPNHpZTxjxgweeOAB3nnnHVwuF4ceeii/+c1vOP3001m7dq1zv5qmccEFF3DXXXdxww03cNJJJ/HjH/+Yk046yTkD7swzz+S4447j2muv5fXXX+eQQw7hrLPO6vNYB/v+qKrK4Ycfzv333980IURZMyl4OgdcftYeU7n5lS5+8NB7odODbyX43TMb+xdONCfbIhucI0GEEIJ8Pu+0agqHw6TTabLZbL2XJURNmaaJ3+8HIB6PT/wmdxOqBjTjNd4gQlVVXC6X8/pyIj0F7AfsTH173teaBRSA6+q9kBbUzIOna81WFJYAS4AO2+YgKu3OpvR8vlrvP5LnVv9jNwP/pdJaLCrhg+hHggghhGhRGzdupKOjY1TXOeKII9h+++350pe+xNKlS53L77///j7H+f1+zjzzTKcKwePxcP755zNlyhQ2b97Mgw8+yBe/+EUOPfRQ/v73v6MoCttttx2BQKBPJUB/H/jABwiFQlx55ZV0dXUBsHr1ar785S+z11578eCDD7JhwwZSqRSlUmlUQ6Aff/xxjj/+eGbOnDlou6qR3HeVqqrceuutA/qFmqbJbbfd5mwCTZs2jb333ptrrrmGu+++G4DNmzfz5z//mT322IPHH38cgOuvv965DUVReO6559hhhx048sgj+1SiGIbBVVddxdNPP+2s75ZbbmGvvfbiP//5D4FAgOOOO46bb77ZaUP17LPPctlllzF16nsDnft/fwA+9KEPMWXKFP75z3+O+Gtab9ng7EHPrAm5NTakBj+bqffb6hN37uDkXaewYEqlfdOrG7P8eNE6nn83A4CmwAMnL6BkwVE3vobVc+Xz9p7Od/abyWHXvcbSTVlMTeHCA2fxyfdHCHtcvN6V45LH3+W+Fe9V6fzumK1YOMPLd/69hp8dOodtIyavbc7xzftX89KG9zYNP/uBTr605zS2CptkSxYrNue48MG1vLA+0+tB2JWWVEIIQWWQdSwWw+fz4fP5MAyDZDLZNKGyEENRVRW/349pmtKCbBiaptVs83+8QYSu69i2PTk99RWFa2ybX1I587pVWjSpVEKImGzg1kQrDZ6eKN2Kwl3AXYDftpkPzAe2BrYDhttR6ALeBFYCq3r+peRnVwxDggghhGhRY5lLsPvuu7Nq1ao+IcRg3njjjT6tkN5++20Apk6dyubNm8lkMjz88MMcddRR/P3vf0dVVXbddVdWr15NNBod8nbnzp3Lpk2bnCAAKgO5V65cyZw549t4fe2119i4cSP77bcft99++7jue/Xq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AWBc5syZoxtuuEGf//zntWLFCknSSSedpPe///1avHixXNfVyy+/rO9973t68MEHdf3112vu3Lk6++yzdfbZZ0uSrrzySv3yl7+UYRhavny53v72t6u/v1+bN2/WT37yE91yyy2N1/vABz6g888/X5deeqn+/ZMX6rCFM/WlFRt1zmH9WrW9rAtvWTOqvs+fNl/vPrxfR37zKe3u/P/CZERXvGmBTts/KcuUHtxQ0Ofu3qCVW8uSpD/89eFanIpKkrb/49GSpGX//ZTWj9EVcdGxs7U5V9W//37TLvdJ0mObdh2/c+aSpP7+5Hk6bJatQs3VLc+m9bm7X1axNjTS4uTFCd383oP1nhtf0HuPnKE3HpDU42depv/61y/phhtu0Je+9CUdccQRetOb3qRarabrr79eN954o974xjfqgx/8oGbMmKFHH31U//Iv/6JCoSBpaDnyX/3VX+mYY47R7NmzlU6n9dBDD+nb3/524zGSdP311+v+++/XunXrdP755yuRSOjxxx/Xv/3bvymTyciyLP34xz/W7bffrmuuuWbU9/W5z31Oc+fO1UUXXTT2XzzkOI5qtZpqtVrQpQDAHlmWJcdxFI1G5fu+CoWCSqVS0GUBAAAAaHMEEQBkmruuixnrtpHmz5+vyy67THfddZe+853vyDRNLVmyRD09PZKkz372s7ryyiv15JNP6sc//rEkaePGjZKkCy+8UOeee66uu+46Pf300zrxxBN16aWXyrIs/fznP2+8RigU0mc+8xl947Fteua+tAZKrgpVT1ecvlA9UVO5ytAJfNOQ3n14v254amC3IUQiYurm9x4kz5c+8ct1qrieLj1xrm5dfrD+5JqV2pir6f0/e1EXHz9HJy3u0Qd+tlqS9Ep+1xPHliEdtzChW55Nyx3nhaFnHdKra/78AP3wye268rebNDcR0mdPW6BUzNJf3rxm1GP/4y2LdePTA3r/T19Q77rHGrd/+MMf1ooVK3TZZZfplFNO0UUXXaRUKqVly5bp29/+thzH0cUXX6wLL7xQ//7v/y5JikajMk1T11xzjTKZjGbNmqULLrhAV1xxhS699NJRr3vSSSdp4cKFuuqqq5RKpfSxj31MF198sa644gq5rqtf/OIXevOb36zvfve7jStie3p6dPLJJ+sb3/jG+P4iulAoFFIkElE2mw26FADYLcuyFI/HFYlE5Hme8vm8yuVy0GUBAAAA6BAEEUCXs21bv/nNb8a8b09XQC5dulThcFhXXXVV43EPP/xw4/4XXnhBtVpNg4ODo0Y6JZNJnX322brhhhv0/e9/X5L0yCOPKJVK6f3vf79uvvnmxtLLcDisa665Rt/1/kT1WEqStD5T0T+/aYHedVi/vveHbZKkM5ekNLcnoh/8cftu633vkTO0KBXRyVev1PPbh06s/H5dXk9cdIQuPHa2PnfXy3rylZK2FOqqup4e2bj7hcL9dkixkKmXc7t2SlgjNlR7vjScU/zzmxboppWDuuSOdY37N+fr+tG7l+jf7tus57a9erLnF6syuvyejZLnas6Lm/TaHbc/88wz+uY3vylJ+sMf/qBTTz1V55xzjt7znvc0TnIvWbJEb3vb2xpBRCaT0de+9rXGsU3T1ObNm/X1r39dCxcuHLXzwzAMffrTn25ctT937lwtX75chmHI933ddtttes973qNjjz1WDz30kCTpjDPOkOd5u/1vCEP/G3NdV5VKJehSAGAXoVCo0QHhui4BBAAAAICmIIgAuly5XNbf/u3f7nL7WWedpdNPP323z3vxxRfluq4+85nP6NZbb9Uf//jHUaN+duewww5TOBzWvffeO+r2u+++W6effroWLlyodetePVl///33yz/xjY2vc1VPP392UMtfO6MRRLz3yBm6b31OLw7u/kTvCYsSWrm13AghJClddnXvmpxOWJjYa91j2XlM9lmH9Orasw9sfH31o1v0yV9t0NL+qBanovr0rzeMCiruW5+T50tHzXVGBRF3rs4Mv4I889Uf04888kjjc8/ztHHjRvm+P+pK+w0bNqinp0exWKxxIunMM8/Ueeedp4ULF8q27cZjdw4innjiiVGjg9auXatwOKze3l4NDg5q48aNevzxx/XWt761EUS85S1v0YoVK1Qs7j646WamaSoajSqfzwddCgCMEg6H5TiOIpGI6vW6stksgSkAAACApiGIALqc7/t6/vnnd7l927Zte3zehg0b9KlPfUrLly/XFVdcIc/z9NBDD+k///M/tWXLlt0+L5EYOuk/ODh6AfPAwICkoY6JYaVSSeVyWb5pjXrs/zy+Xb98/yE6bJatV/I1vXlpUpf+Yp32pDdmaWth1zFLWwo1vWZmbI/P3dlAqa5y3dP8ZHjU7SvW5HT6tc9Kkn5w7quBRL899KP2unOXjHm8BcnIqK+3FuqNz/0RQcTOJ7Pr9fouXSvDQUIkElG5XNYpp5yif/qnf9Itt9yia665RtlsVv39/frCF76gSGT06+58/JHHGnbbbbfpk5/8pJLJpGbNmqWDDz640aWBXTmOI9/3uboYQMvYOYDIZDKqVnft8AMAAACAqUQQAWDCHn74YT388MNyHEfHHXecLrroIn3yk5/UJz7xid0+J5fLSZJ6e3tHhR39/f2SNOYcfWPHqKZhj2wsaOXWkpa/doY2ZKuq1H3977PpPdY6WHK1dEZ0l9tnx8NKl909Pndnri89tCGvU/frkWmosZciU3H1+OahzoDqiGUVw8f/hzvX6dExRj5t3mkPxchGC8Pbt9p2dtppp2nVqlX66le/2rjtda973YSP99vf/lYXX3yxzjzzTM2fP18vv/yynnjiiUnV2KkMw1AsFqNbBEBLiEQichxH4XBYtVqNAAIAAADAtNrzNloAGIdisah77rlHd999txYvXty4vVar7XLV/cqVK1Wr1XTaaaeNuv20007TwMDAqFFBwwy/vstt//PENr3r8D5d8NoZumnloIo1b5fHjPTghrwOm2Vraf+rYUQqaukN+/XogQ37Pjbnmw9v0byeiD5+4ty9Pvb57WW9nK1qv96oHt9c3OVj5yDiVYasMb73fTF8xetIZ5xxxoSPV6vVdOedd+od73iHTj/9dN1xxx2Tqq+T2bYt3/f3uGsFAJotGo2qr69PqVRKvu8rnU4rnU4TQgAAAACYVnREAJiQs846S4cddpgeeughDQwMaO7cuTrjjDNG7TFYt26djjrqKB1zzDHK5XLavHmzstmsbrrpJp1//vmqVqt65plndPzxx+uMM87QVVdd1VhUPZJZr0o7NTP8+KkBff60BZrphPW3d6zda70/fHK7Ljx2tq4/b4m+tGKTKnVPl540V3Xf17ce3v0oqd351eqsvnb/Zv3TqfN15BxbN60c1Cv5mpJRSycuSmh2PKx89dXv5TO/2aBv/9kBcsKmfrU6q2LN1aJkRGcuSekL927U6rH2WxiGEtGQ+sND3SKJREKpVEqe58nzPJmmKdM0FQ6H5XmeXHfX7olHH31Ul1xyid73vvfp6aef1gknnKCjjjpqn7/fkW677Tadd955cl1Xv/zlLyd1rE5lGIZs2x4aLbbzMhEAmAbR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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "import networkx as nx\n", + "\n", + "def plot(graph):\n", + " labels = {x: f\"{graph.attribute(x, 'id')}\" for x in graph.scan()}\n", + " colors = [\"#D32F2F\", \"#0277bd\", \"#7e57c2\", \"#757575\"]\n", + "\n", + " results = embeddings.batchsimilarity(labels.values(), [\"Roman Empire\", \"Germanic barbarians\", \"Vikings\", \"Normans invade\"])\n", + " colors = [colors[x[0][0]] for x in results]\n", + "\n", + " options = {\n", + " \"node_size\": 2000,\n", + " \"node_color\": colors,\n", + " \"edge_color\": \"#454545\",\n", + " \"font_color\": \"#efefef\",\n", + " \"font_size\": 11,\n", + " \"alpha\": 1.0,\n", + " }\n", + "\n", + " fig, ax = plt.subplots(figsize=(20, 9))\n", + " pos = nx.spring_layout(graph.backend, seed=0, k=0.9, iterations=50)\n", + " nx.draw_networkx(graph.backend, pos=pos, labels=labels, **options)\n", + " ax.set_facecolor(\"#303030\")\n", + " ax.axis(\"off\")\n", + " fig.set_facecolor(\"#303030\")\n", + "\n", + " plt.show()\n", + "\n", + "plot(g)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3bEH5SZNk6YM" + }, + "source": [ + "Each one of the nodes in the graph above is a Wikipedia article abstract. For example, the `Saxons` entry is the Wikipedia abstract text for the Saxons article. The `Roman Empire` gives a summary of the Roman Empire and so forth.\n", + "\n", + "Clearly, we have aggregated an extremely relevant set of articles with the graph path query. This is a solid knowledge base related to the fall of Rome, Saxons, Vikings and Norman conquest." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XeMU-ga5k6YM" + }, + "source": [ + "# Write a short history book\n", + "\n", + "Now that we have our collection of articles, let's write our book!\n", + "\n", + "We'll load a LLM and build a prompt that uses the graph network of articles and a user input command to build the book." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "n-TLvrZ1k6YM" + }, + "outputs": [], + "source": [ + "from txtai import LLM\n", + "\n", + "llm = LLM(\"Qwen/Qwen3-4B-Instruct-2507\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "vkt8ZhLSk6YM", + "outputId": "f1b39c8d-6bd9-4cd8-d21b-2acd309c7835" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Title: \"The Fall and Rise of Britain: From Roman Rule to the Norman Conquest\"\n", + "\n", + "Chapter 1: The Fall of the Western Roman Empire\n", + "The Western Roman Empire fell due to a combination of factors, including the effectiveness and numbers of the army, the health and numbers of the Roman population, the strength of the economy, the competence of the emperors, internal struggles for power, religious changes, and the efficiency of the civil administration. Barbarian invasions and climatic changes also played a significant role in the collapse.\n", + "\n", + "Chapter 2: Roman Britain\n", + "Roman Britain was the territory that became the Roman province of Britannia after the Roman conquest of Britain, consisting of a large part of the island of Great Britain. The occupation lasted from AD 43 to AD 410.\n", + "\n", + "Chapter 3: The End of Roman Rule in Britain\n", + "Roman rule in Britain ended in different parts of the country at different times and under different circumstances. The recall of Roman troops to Gaul by Constantine III in 407 left Britain vulnerable to barbarian attacks. Around 410, the Romano-British expelled the Roman magistrates from Britain, leading to the fall of Roman rule.\n", + "\n", + "Chapter 4: Sub-Roman Britain\n", + "Sub-Roman Britain is the period of late antiquity in Great Britain between the end of Roman rule and the Anglo-Saxon settlement. This period saw the decay of locally made wares from a previous higher standard under the Roman Empire.\n", + "\n", + "Chapter 5: The Saxon Settlement\n", + "The Saxons were a group of Germanic peoples who played a major role in the fall of the Western Roman Empire and the establishment of the post-Roman kingdoms. They settled in Britain, contributing to the decline of Romano-British culture and the rise of a new Germanic culture.\n", + "\n", + "Chapter 6: The Viking Age\n", + "The Viking Age was a period during the Middle Ages when Norsemen, known as Vikings, undertook large-scale raiding, colonizing, conquest, and trading throughout Europe. They also voyaged as far as the Mediterranean, North Africa, the Middle East, Greenland, and North America.\n", + "\n", + "Chapter 7: The Norman Conquest of England\n", + "The Battle of Hastings, fought on 14 October 1066, marked the beginning of the Norman Conquest of England. The Normans, led by William, Duke of Normandy, defeated the English army under King Harold Godwinson, leading to a decisive Norman victory and the end of Anglo-Saxon rule in England.\n", + "\n", + "Chapter 8: The Impact of the Norman Conquest on England\n", + "The Norman Conquest of England had a profound impact on the country's culture, language, and governance. The Normans introduced feudalism, the use of the Norman French language, and a new system of land ownership and administration.\n", + "\n", + "Chapter 9: The Legacy of the Saxons, Vikings, and Normans on Modern Britain\n", + "The Saxons, Vikings, and Normans left a lasting legacy on modern Britain, shaping its language, culture, and political landscape. Their influence can still be seen in modern British society, language, and institutions.\n" + ] + } + ], + "source": [ + "def rag(question, text):\n", + " prompt = f\"\"\"<|im_start|>system\n", + " You are a friendly assistant. You answer questions from users.<|im_end|>\n", + " <|im_start|>user\n", + " Answer the following question using only the context below. Only include information specifically discussed.\n", + "\n", + " question: {question}\n", + " context: {text} <|im_end|>\n", + " <|im_start|>assistant\n", + " \"\"\"\n", + "\n", + " return llm(prompt, maxlength=4096)\n", + "\n", + "context = \"\\n\".join(g.attribute(node, \"text\") for node in list(g.scan()))\n", + "\n", + "print(rag(\"Write a book covering the end of Roman Britain, Saxons, Vikings and Norman conquest of England\", context))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "_It's important to note that txtai has multiple ways to run LLM inference. In the past, prior to \"Chat Templates\", it was expected that the submitted text had all the required chat tokens embedded. The same prompt above can also be written with chat messages. This is especially important when working with LLM APIs (i.e. OpenAI, Claude, Bedrock etc)._\n", + "\n", + "```python\n", + "llm([\n", + " {\"role\": \"system\": \"You are a friendly assistant. You answer questions from users.\"}\n", + " {\"role\": \"user\", \"content\": f\"\"\"\n", + " Answer the following question using only the context below. Only include information specifically discussed.\n", + "\n", + " question: {question}\n", + " context: {text} \n", + " \"\"\"}\n", + "])\n", + "```\n", + "\n", + "_See the [LLM pipeline documentation](https://neuml.github.io/txtai/pipeline/text/llm/) for more information._" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "VOSQWrMjk6YM" + }, + "source": [ + "# Wrapping up\n", + "\n", + "Just that like that, we have a short book of history covering the early medieval period in England! 📖🛡️🗡️🏴󠁧󠁢󠁥󠁮󠁧󠁿\n", + "\n", + "It's quite amazing when we think about it. This notebook took the full set of Wikipedia articles, walked a graph to pull articles related to a series of topics and used that to build a short book!\n", + "\n", + "It's also a stark contrast to a standard RAG process. There is no way we'd be able to get the breadth of knowledge with a simple vector search query. The graph path traversal is the key advancement here. This is just the beginning, more to come!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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", + "version": "3.8.18" + }, + "colab": { + "provenance": [], + "gpuType": "T4" + }, + "accelerator": "GPU" + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/examples/59_Whats_new_in_txtai_7_0.ipynb b/examples/59_Whats_new_in_txtai_7_0.ipynb new file mode 100644 index 0000000..5b85f15 --- /dev/null +++ b/examples/59_Whats_new_in_txtai_7_0.ipynb @@ -0,0 +1,594 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "e3wdiK5fGUoZ" + }, + "source": [ + "# 💡 What's new in txtai 7.0\n", + "\n", + "txtai 7.0 brings a number of major feature enhancements. Highlights include:\n", + "\n", + "- Semantic Graph 2.0\n", + " - Graph search\n", + " - Advanced graph traversal\n", + " - Graph RAG\n", + "\n", + "- Embeddings\n", + " - Default configuration format now JSON\n", + " - Move ids storage outside of configuration when content is disabled\n", + "\n", + "- Pipelines\n", + " - Training support for LoRA / QLoRA\n", + "\n", + "- API\n", + " - Binary transport support\n", + "\n", + "These are just the big, high level changes. There are also many improvements and bug fixes.\n", + "\n", + "This notebook will cover all the changes with examples.\n", + "\n", + "**Standard upgrade disclaimer below**\n", + "\n", + "While everything is backwards compatible, it's prudent to backup production indexes before upgrading and test before deploying." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "p8BbfjrhH-V2" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "-OXsTQgaGQPM" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[api,graph,pipeline-train] datasets" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "n4EXrtcYIIYE" + }, + "source": [ + "# Semantic Graph 2.0\n", + "\n", + "The biggest change and reason this is a major release is the addition of a number of new graph-driven patterns. Let's jump right into with that.\n", + "\n", + "## Graph search\n", + "\n", + "The first feature we'll test out is running a search that returns results as a graph. With this change, not only do we get search results, we get how these search results relate to each other.\n", + "\n", + "We'll use a [prompt dataset on the Hugging Face Hub](https://huggingface.co/datasets/fka/awesome-chatgpt-prompts) for all examples." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "hzPD8_cQJNtN" + }, + "outputs": [], + "source": [ + "from datasets import load_dataset\n", + "\n", + "import txtai\n", + "\n", + "# Load dataset\n", + "ds = load_dataset(\"fka/awesome-chatgpt-prompts\", split=\"train\")\n", + "\n", + "def stream():\n", + " for row in ds:\n", + " yield (row[\"act\"], f\"{row['act']} {row['prompt']}\")\n", + "\n", + "# Build sparse keyword index\n", + "embeddings = txtai.Embeddings(content=True, graph={\"approximate\": False})\n", + "embeddings.index(stream())\n", + "\n", + "graph = embeddings.search(\"Linux terminal\", 5, graph=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "1v4s_pT5k8ZX", + "outputId": "adfc0161-a377-46af-bfa0-e157f0bdd64d" + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABOwAAAGVCAYAAABNUG4rAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8WgzjOAAAACXBIWXMAAA9hAAAPYQGoP6dpAACZRUlEQVR4nOzdebgbddk+8HtmkkxmJpnkLF3pQgulgBYKKGtLC7IVEOqCLW4oCggqqMArIALuCMqLCqKyCP6UwovIUkrZwS6AyCJUgYIUulC6nCXrTJJJZn5/HDIkZ19yzmS5P9fVqyfb5JuzJnee7/MIc+fOdUBERERERERERERVQfR6AURERERERERERPQBBnZERERERERERERVhIEdERERERERERFRFWFgR0REREREREREVEUY2BEREREREREREVURBnZERERERERERERVhIEdERERERERERFRFWFgR0REREREREREVEUY2BEREREREREREVURBnZERERERERERERVhIEdERERERERERFRFWFgR0REREREREREVEUY2BEREREREREREVURBnZERERERERERERVxOf1AoiIaGgcAI4gwZH8cAQJglOAULC6/vd6cURERERERDRiDOyIiKqYAwFZtRVmZApMfQqMyDSY+hQ4UqDHdYVCDkpiC9T4JiiJLVDiWyAbbRDgeLByIiIiIiIiGi4GdkREVSirtKB96iHomHIwbL/SdaadBwQJEHqvo3OkAIzoDBiRaYDY9etdtEw0b3kWLZufgWy2j9XyiYiIiIiIaASEuXPnsvSCiKgKOBCQHLcX2qbNQ6p1NmAXAFEa+YHfP06obT1aN61BeOdrrLojIiIiIiKqYqywIyKqAunoDGyesxQ5tbUrYAMqE9aVHCfVvDtSrbMRMNowdd0d0GJvV+b4REREREREVFGssCMi8pAt+rFt1iK0TT8ccOzKhXT93mkBEES0blyFiW+uhGhbo3+fRERERERENGgM7IiIPJKOzsCmOafCUpoAQRz7BTg2/GYnpq1bxmo7IiIiIiKiKsLAjojIA23T5mHrnovHrqquL+9X201+/V60blrj3TqIiIiIiIjIxR52RERjyAGwY+ZR2D5rUdcZgodhHeCGhVv3+gQKviDGb3gMvc+gJSIiIiIiorHiwR4sIqLGVRbWVZntsxZhx8yjvF4GERERERFRw2NgR0Q0RtqmzavasK5o+6xFaJs2z+tlEBERERERNTQGdkREYyAdndHVs64GbN1zMdLRGV4vg4iIiIiIqGExsCMiGmW26MemOad2DZioBY6NTXNOhS36vV4JERERERFRQ2JgR0Q0yrbNWgRLafJ2GuxQiBIspQnbqnz7LhERERERUb1iYEdENIrS0Rlom344INTYr1tBRNv0w7k1loiIiIiIyAM19gqSiKh2OBCwec7S2tkK251jY/OcpXAgeL0SIiIiIiKihsLAjoholCTH7YWc2lo7W2G7EyXk1FYkx+3p9UqIiIiIiIgaCgM7IqJR0jZtHmAXvF7GyNiFrsdBREREREREY4aBHRHRKMgqLUi1zq7d6roiUUKqZTaySovXKyEiIiIiImoYDOyIiEZB+9RDar+6rsix0TH1EK9XQURERERE1DAY2BERVZgDAR1TDq796roiUUL7lIM5fIKIiIiIiGiMMLAjIqqwrNoK2694vYyKsv0Kciq3xRIREREREY0FBnZERBVmRqZ4cr+fnTsBq88+oCLHWjAzivbL55edZ+hTy07PmTMHt912W0XuT1VV3H///YhGoxU5HhERERERUS1jYEdEdW/69Om49tpr8cQTT2D16tX429/+hi996Utl1znooINw8803Y+3atVi1ahWuu+46zJ49u+w6K1aswMKFCwe8P1OfgsKVR2LfSaEKPoqB3f6v7Zh/wwujc3A7D1MvDyLPO+883HzzzWXnLViwAHfccQeefvppPPLII/j0pz/d41DNzc146qmncMcdd7jnGYaBBx54AF/5yldGZ/1EREREREQ1hIEdEdW9X//613jjjTdw/PHHY8GCBbjggguwZcsW9/IFCxbgmmuuwQMPPICjjz4aJ5xwAl588UXcfPPNPUK7wTAi00a8Zp9YZf3iBAlG5IMKu9122w3Tp0/HmjVr3PMOPfRQXHzxxbj66qsxb948fOpTn8Lzzz/f41AXXXQRXn/99R7nL1++HCeffDKCweDoPAYiIiIiIqIa4fN6AUREoykajWLatGm4++67kclkAAAbNmzAhg0b3OtceOGF+OMf/4h77rnHPe+WW27B1KlT8e1vfxtf+9rXBn1/DoAnvtdVVbbm7ANgOw5+9uRGXPnURsxsVvC/H5+Fg6bqMCwbN/1zK3725DtwHOC0Aybi3MOm4v5X23DmQZPx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9ZR7+k9/+hOWL1/unvb5fJg6dSrWrx+96cBEVB0Mw0BTUxNkWWZoRw3B7/cjEonAsiwkEgmGdUREFZTNZt3nEz6fD6qqwu/3u0FecVeRaZrI5XIDHI2Iak3FetgBwOuvv47nn3++rCddb84880yEQiGMGzcOp59+OlauXNnndR988EEsWrQI++67LyRJwtKlSxGNRrFmzRp3e+pZZ52FUCiElpYWfOELXxhwnaFQCMlkEplMBjNmzMApp5wy5Mc6Vu56ZQc+tnsTjp7VDEkU8OWPTMIerZUrh96RzGG3FsU9vT2Vw32v7sRvTt4DLWpXw9MJoQAWf6h1wGN94hOfgCAIyOVy+OxnP4tHH30UsVjMrUJMp9MwDAOZTAYrVqzAfvvt525t9vl82GOPPfChD30IgUAAyWQSkydPRjgcRjgchq7rWLFiBc455xzMmTMHzc3NmDJlCk488URMnToV48aNgyiKCIfDaG5uRnNzM5qamhCNRhGJRKDrOsLhMF544QUccMABCIfDUFUViqJA0zSEw2EEAgF3alMwGIQkSRBF0W0kL4oiJElCIBAoq6Dbe++9IUkSVFXFmWeeiUgkUhbY7bvvvtixYwfefvvtSn3ZiKhKFavs2MuOGkEgEHDDOlbWERGNrr763hXfOGHfO6L6U7EKu6Kbb74Zf/jDH3Drrbdi+/btvV7nqaeewp133glN0/Doo4/i5ptv7vN4L7zwAq666ipcfvnlaG1txVtvvYVvfOMbbgj085//HJdffjkefPBBtLW14W9/+1vZFNne/PjHP8b555+P8847D6+99hoefvhhLFy4cNiPebAE2xrybd5oM/Dlu17D9Ytno1Xz465XduDJtzqRzVemN8yv1m7GLafshfbL52PtO3GcdNsr+PL/vYYrjp6Bf3zjI2hR/dieyuH/XtmBe//T1usxhELXuzkrVqzA73//e7S0tODZZ5/FlVdeiVwu574rlMlkYJomgK6tpGeffTbOO+88fOc734Eoinj77bdxww03oLOzE/feey8OP/xw3HXXXRAEAYcffjhuvvlmJJNJfO9738OECRNgGAZefvllrFq1yv2DlcvlkMlkIAiCuxW3+LEoiti4cSPeffddzJ8/H88++ywEQcAXv/hFfPGLX3Qfz5o1a/Dyyy/jwgsvBABccMEFOOaYY9zLly5diocffhg///nP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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "import networkx as nx\n", + "\n", + "def plot(graph):\n", + " labels = {x: f\"{graph.attribute(x, 'id')} ({x})\" for x in graph.scan()}\n", + " options = {\n", + " \"node_size\": 1500,\n", + " \"node_color\": \"#0277bd\",\n", + " \"edge_color\": \"#454545\",\n", + " \"font_color\": \"#fff\",\n", + " \"font_size\": 9,\n", + " \"alpha\": 1.0\n", + " }\n", + "\n", + " fig, ax = plt.subplots(figsize=(16, 5))\n", + " pos = nx.spring_layout(graph.backend, seed=0, k=0.9, iterations=50)\n", + " nx.draw_networkx(graph.backend, pos=pos, labels=labels, **options)\n", + " ax.set_facecolor(\"#303030\")\n", + " ax.axis(\"off\")\n", + " fig.set_facecolor(\"#303030\")\n", + "\n", + " plt.show()\n", + "\n", + "plot(graph)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "caDteWUVK5jI" + }, + "source": [ + "We now see a graph of not only the search results but how they relate to each other!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dPdgwyZhk8ZX" + }, + "source": [ + "## Advanced graph traversal\n", + "\n", + "Before 7.0, the main way to traverse a graph was via the `showpath` method. This method finds the shortest path between two graph nodes. What if we want more control over how a path is traversed? Enter advanced graph traversal." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "B1KRPrcnk8ZX", + "outputId": "2a2f7658-0f5b-4620-cc34-6b66e2496282" + }, + "outputs": [ + { + "data": { + "image/png": "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o3b/b33rW3XKKafoC1/4wn6fa9q0abrmmmt07rnn1u2eyQR2NdC07UUFcp0qhpKNmbY7tgL5HjVtWyGpv52pUi0RCoVkmqZCoZDK5bIsy6ppBcVQQZ5hGDu11oZCIXm9/f8XBHnAvnFdV7lcTpFIRJZl8T0zAVUqnCs/UysTXguFAlXNu2CapgqFAiEmAKDujXb77g9/+EMtXLhQl156qZ566qlq++4555yjj33sY/rTn/6kH//4x3us6Pvwhz+sOXPm6NJLL61ed+WVV47Ya9KnnnpK5557rl566SVJ0ubNm3X88cfvdJwVbxuR+9sbh7SG9e3TD9ax7TEFvB5tTBf0yyc36dt/Xzdi95GLzRgysBtthmHok5/8pD7zmc9IkpLJpD73uc/pda97nSKRiDo6OvTjH/9Yf//733e67UEHHaQbb7xRy5YtqwaKmzZt0rPPPqv3vOc9uvHGG8f0sQwXgV0NGHI1Y/lNeuWYi2q9lH1jeDRj+Y07lcFWWuIsy5Lf75dpmopGo4pGo9V22Xp4wea6bjXIG2ioIM80zeovikqQt2OIRygBvGZglV1fX1+tl4MxwITXfVcZqJROp2u9FAAARtVw2nfL5bLWrFmjk08+Wffdd5+k/tdop556qtasWaNisah0Or3H9t1AICC/369EIjHo/GPdvmvF2vqnvHvGLna57UNH6nfPbtV5v31OhbKjeZMiOmxKeOTuwCnLirVV99YdDZU3fXe0aNEipdNpvfzyy5KkcDisFStW6Ac/+IG2bdumxYsX66qrrtL73vc+rV69uno7wzD01a9+VU8//fRO57ztttv01a9+lcAOg0V616h17QPqnLlYMobe+LMuuY5a1z6oSO+ruz2sUnXn8XgUCoWqlXelUqlabVFv+xbsLsjbcY+8QCCw2yCvXC5TLYEJK5fLKRqNKpfLEWiPU0x4HRmV34v18GYWAAD14O6779b555+vaDSqTCajww8/XJK0fPly2bZd7Zw67LDDdOmll+qggw7Stm3bdN111+muu+7SSSedpPPPP1+GYeiWW26RJJ100km6/PLLlclk9IMf/EDf/OY39dJLL+lXv/pVtX33P/7jP+TxePTDH/5Qp556qs4991xNnjxZmUxG9957r37xi1/IdV1dffXVkqQbbrhBjuPof//3f3X33XfrT3/6k0499VT19fXJMAz9+7//u0476xyFAgH97ZUeXfznl9SZ7X8j077qFF148wpdeFyb2hMh/X11jz7wuxeULvQ/b/7VuYfp5IOSiga8WtWZ0+fvfFn3r+7d4+euJezXwa1h/fQfG2SV+l+LvrA1qxe2vjYQ7pUvHKefPbZR7zpisg5uMfXoupQu+MOL2tTX//xtUsSv750xRycflJQr6Q/PbtUXl76sou0qEvDq1+fO13EzFitkXKpVq1ZVP5dDueiii3TCCSfooosuUm9vrz72sY9pyZIlampq0jPPPKP//u//1rZt2yT1Vy1eddVVes973qP29nadfPLJyuVyg8534okn6rHHHqt+vGHDBv3v//5v9eMHHnhAr776qubPnz8osDv//PO1evVqbd68WXPnDt6a7JlnntHkyZM1a9YsrVmzZo+f47HWQEnR+DN11VL5rZ7+qTGNwLHlt3o0ddXS4d/EcZTL5dTd3a1UKiXHcRSNRtXc3KxoNFptQ61nrutWg8ZMJqNUKqWuri51dnaqt7dX2WxWpVJJXq9XpmkqHo+rpaVFra2tSiQSampqkmmag0I+YDyr7FEWiURqvRSMsGAwqFgsppaWFkWjUUlSOp1WV1eX+vr6COv2gs/nUyAQYO86AAAG6Ovr08MPP6y3ve1tkqSzzjpLt95666BjotGorr76at1999065ZRTdOWVV+qyyy7TggULdP/99+vnP/+5HnzwQR1//PE6/vjjVSqVqoUVlmXpz3/+s0477TSl02mlUin19fXphBNO0P/93/9p27Zt6ujo0CWXXKKTTjpJl1xyiZYsWaJFixYpn8/r3/7t3yRJn/zkJ3X66afr97//vUyzf5+6WCymRCKhCy+8UCeddJJOvPYJHfStR+RK+vW5hw96DO85copOu+4pHXjVwzogHtJ/LG6vXvfXl3t0+Hcf1aSvPajfPbtVv//X+YoG9vy6uStX0oqtWf38PYfqnPmT1Z4IDXncR94wXe+76XlN/+9l2txX1K/Oe21tt3zwSG3OFDXn249owff/oSOnRfXlUw6UJHkM6cZntuigq5bp1NNO04oVK/TNb35zp/N7vV5dfvnlWrhwoT7ykY+os7NTF110kRYsWKALLrhAb37zm7V27VpdddVVg263ZMkSXXjhhVq0aNGQz4/mzp2rV199dZePP5lMatasWVq1alX1smnTpum9732vvv/9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OX5TM+JBpa44QbHg2AZ3qStO0BFTIru8/n/OnqtvvO2gvTqn4zdVDPe3xX75y1/WOeecs19rrFiwYIGuv/76ETkXAAAAsDsEdgAA1LnrrrtOTz31lN74xjcOuvwDH/iAnnrqKX3uc5/b53Nb8TZJ0vpUQfHLH1C6sO+tscfPjOuODy9Q5+WL1XX5Yv3z02/Q505ol9+762q3+OUP6LktWUnSV0+bpT+9f/6g6y+8ZaW+dNcre72WXGyGZsyYoUWLFunmm2+WJM2fP1/XXHON/vrXv+r+++/XNddco9mzZw+63YIFC/S73/1ODz/8sG666SYdeeSR1eueeeYZlctlnXTSSXu9HgAAAGBvENgBANAA1qxZo3e84x2DLjvrrLO0evXq/TqvFWuTnPJ+nUOSTp/XojsuWKC7X+rS3G8/qpYrHtT5v31eh02JaFpTcKfjfZ5RbFl1yrJibXrPe96je+65R+Vy/+OLxWL685//rHe84x067bTT9Pzzz+vqq6+Wx+OpXv/DH/5QN910k0488UT97ne/0w9+8ANFo9HqqW+77Tade+65o7d2AAAAQAR2AAA0hLvvvlvHH398NTw64ogjJEnPPffcoOMOO+ww/eIXv9ADDzygP/7xj3rb294mSUomk3rsscc0bdq06rF+v18rfv5lHTszqZnJkOyrTlE85Kte/8k3ten5S96orssX6y//dpTmTQrvcn3fP/MQfevv6/TDhzrUlStJklZuy+mCP7yodb356vk/9LppWvm5Y7XuS8dLkuyrTtGCaVGddVirvnTSTJ0+r0WpK05Q6ooTJEnXn3OovnfGnOr9HNxi6pYPzNfmryzStq8u1v+974idF2N4lYvP0IknnqjHH3+8evFDDz2ku+++W5lMRuVyWTfccIOmTZtW/Zyccsop2rp1q26++WaVSiXdfPPN6urq0imnnFI9x2OPPabXve51Cod3/bkAAAAA9heBHQAADaCvr08PP/xwNYA766yzdOuttw46JhqN6uqrr9bdd9+tU045RVdeeaUuu+wyLViwQD09PXrkkUf09re/vXr8CSeeqG25sh5d37fT/f37sQfogjdM01k3PKvJX1+mm5/bpls/eOSQ7a1zWk3NbjF109Nb9vg4zjysVcdc/YQO+tbDgy6/9YVOfeP+tbpjRZfilz+g+OUP7HTbsN+jez56lJ7bktXs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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "g = embeddings.graph.search(\"\"\"\n", + "MATCH P=({id: \"Poet\"})-[*1..2]->({id: \"Rapper\"})\n", + "RETURN P\n", + "LIMIT 5\n", + "\"\"\", graph=True)\n", + "\n", + "plot(g)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yJIHr_9zk8ZY" + }, + "source": [ + "The query above finds the top 5 connections between a `Poet` and a `Rapper` using a graph query. Graph queries use the [openCypher](https://github.com/opencypher/openCypher) query standard." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "MvlWX0yYk8ZY" + }, + "source": [ + "## Graph RAG\n", + "\n", + "Graph path traversal opens up a different type of RAG process. A standard RAG process typically runs a single vector search query and returns the closest matches. Those matches are then passed into a LLM prompt and used to limit the context and help ensure more factually correct answers are generated. Graphs enable more complex analysis.\n", + "\n", + "We'll use the graph path from the previous example for a more complex RAG query." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "zC_yDuyKk8ZY", + "outputId": "3f52332c-de63-4e94-b2ce-03c1c21fe597" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The roles that are similar to both a rapper and poet are:\n", + "\n", + "1. Composer: A composer creates music for various forms of art, including songs and poems. They work with different instruments and tools to bring the lyrics to life, making the music harmonious and engaging.\n", + "\n", + "2. Novelist: A novelist creates captivating stories with engaging characters and plotlines. They can write in various genres, such as science fiction, romance, or historical fiction. The goal is to write a story that keeps readers engaged and entertained.\n", + "\n", + "3. Movie Critic: A movie critic evaluates and reviews movies, discussing aspects like plot, themes, acting, direction, and more. They aim to express their feelings about the movie and how it impacted them, while also providing constructive criticism.\n", + "\n", + "4. Motivational Speaker: A motivational speaker inspires and empowers their audience by sharing words of wisdom and encouragement. They can talk about various topics, but the goal is to make their audience feel motivated and inspired to achieve their goals.\n" + ] + } + ], + "source": [ + "from txtai import LLM\n", + "\n", + "llm = LLM(\"Qwen/Qwen3-4B-Instruct-2507\")\n", + "\n", + "def rag(question, text):\n", + " prompt = f\"\"\"<|im_start|>system\n", + " You are a friendly assistant. You answer questions from users.<|im_end|>\n", + " <|im_start|>user\n", + " Answer the following question using only the context below. Only include information specifically discussed.\n", + "\n", + " question: {question}\n", + " context: {text} <|im_end|>\n", + " <|im_start|>assistant\n", + " \"\"\"\n", + "\n", + " return llm(prompt, maxlength=4096)\n", + "\n", + "context = \"\\n\".join(g.attribute(node, \"text\") for node in list(g.scan()))\n", + "\n", + "print(rag(\"What roles are similar to both a rapper and poet?\", context))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gb-7zteQk8ZZ" + }, + "source": [ + "Let's compare these results with the results from a standard RAG query. We'll pull the 6 most similar rows to have the same sized dataset as what is in the graph above." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "jYEUfWbFk8ZZ", + "outputId": "f4bb6f16-f360-4991-d0ab-47eabebdfa0f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The roles most similar to both a rapper and poet are the Composer and the Song Recommender. Both roles involve creating music or recommending songs based on given lyrics or themes.\n" + ] + } + ], + "source": [ + "question = \"What roles are most similar role to both a rapper and poet?\"\n", + "context = \"\\n\".join(x[\"text\"] for x in embeddings.search(question, limit=6))\n", + "print(rag(question, context))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kQUDymlVk8ZZ" + }, + "source": [ + "As we can see, the Graph RAG approach yields a more comprehensive answer. The standard RAG answer isn't bad, it's just not as complete." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TrMC9Seqk8ZZ" + }, + "source": [ + "# Embeddings\n", + "\n", + "There are a couple backwards compatible changes to the embeddings database format. The default configuration format moving forward is `json`. While `pickle` configuration is still supported, txtai is moving towards a readable configuration format. This is to have maximum compatability with the Hugging Face Hub, when uploading models. The `pickle` format is generally not recommended when sharing indexes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "_N1IwubTk8ZZ", + "outputId": "2f91d501-6a70-4ff2-abf1-a39c8f5a78dd" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"autoid\": 2,\n", + " \"backend\": \"faiss\",\n", + " \"build\": {\n", + " \"create\": \"2024-02-21T16:23:26Z\",\n", + " \"python\": \"3.8.18\",\n", + " \"settings\": {\n", + " \"components\": \"IDMap,Flat\"\n", + " },\n", + " \"system\": \"Linux (x86_64)\",\n", + " \"txtai\": \"7.0.0\"\n", + " },\n", + " \"dimensions\": 384,\n", + " \"offset\": 2,\n", + " \"path\": \"sentence-transformers/all-MiniLM-L6-v2\",\n", + " \"update\": \"2024-02-21T16:23:26Z\"\n", + "}\n", + "ID List: [0, 1]\n" + ] + } + ], + "source": [ + "import json\n", + "import pickle\n", + "\n", + "from txtai import Embeddings\n", + "\n", + "# Create a default index\n", + "embeddings = Embeddings()\n", + "embeddings.index([\"test1\", \"test2\"])\n", + "embeddings.save(\"index\")\n", + "\n", + "# Read standard configuration\n", + "with open(\"index/config.json\") as f:\n", + " print(json.dumps(json.load(f), sort_keys=True, default=str, indent=2))\n", + "\n", + "# Read ids\n", + "with open(\"index/ids\", \"rb\") as f:\n", + " print(\"ID List:\", pickle.load(f))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "a1X1_fHdk8ZZ" + }, + "source": [ + "When no configuration is specified, notice that a `config.json` file is created along with an `ids` file. Ids are no longer stored within the configuration both for `json` and `pickle` configuration. When loading an existing index, the ids are automatically read and moved when saving a new version." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mGpwVhuwk8Za" + }, + "source": [ + "# LoRA / QLoRA support\n", + "\n", + "Two new parameters have been added to the `HFTrainer` pipeline, `lora` and `quantize`. When both of those are enabled, models are trained using QLoRA. Custom settings are also supported." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "isP662sdk8Za", + "outputId": "e72b4f64-671d-42db-b388-c8a702d9fc04", + "colab": { + "referenced_widgets": [ + "b6e77e11948f42429b587fc0cb07faa5" + ] + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "trainable params: 8,355,840 || all params: 470,041,600 || trainable%: 1.7776809541963945\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b6e77e11948f42429b587fc0cb07faa5", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + " 0%| | 0/3 [00:00 api.log &\n", + "!sleep 90" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "F6IdEWsIk8Za", + "outputId": "2dba53b1-c81a-418e-b2f6-411e3445a03e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "b'0'\n", + "b'\\x00'\n" + ] + } + ], + "source": [ + "import requests\n", + "\n", + "requests.post(\"http://localhost:8000/add\", json=[\"test\"])\n", + "requests.get(\"http://localhost:8000/index\")\n", + "\n", + "print(requests.get(\"http://localhost:8000/count\", headers={\"Accept\": \"application/json\"}).content)\n", + "print(requests.get(\"http://localhost:8000/count\", headers={\"Accept\": \"application/msgpack\"}).content)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Njc8wa09k8Zb" + }, + "source": [ + "Notice the subtle but important difference between the two outputs. The first response is a `0` character as JSON. The second response is the `\\x00` character and can be intrepreted as a `0` using MessagePack. See below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "w_KkrUEBk8Zb", + "outputId": "dc3f180c-808a-4752-f8f1-bc7ca3158fc9" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n" + ] + } + ], + "source": [ + "import msgpack\n", + "print(msgpack.loads(requests.get(\"http://localhost:8000/count\", headers={\"Accept\": \"application/msgpack\"}).content))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tvnMO1Eai6Gy" + }, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook gave a quick overview of txtai 7.0. Updated documentation and more examples will be forthcoming. There is much to cover and much to build on!\n", + "\n", + "See the following links for more information.\n", + "\n", + "- [7.0 Release on GitHub](https://github.com/neuml/txtai/releases/tag/v7.0.0)\n", + "- [Documentation site](https://neuml.github.io/txtai)" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "gpuType": "T4", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "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", + "version": "3.8.18" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/examples/60_Advanced_RAG_with_guided_generation.ipynb b/examples/60_Advanced_RAG_with_guided_generation.ipynb new file mode 100644 index 0000000..ba4be5f --- /dev/null +++ b/examples/60_Advanced_RAG_with_guided_generation.ipynb @@ -0,0 +1,351 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "e3wdiK5fGUoZ" + }, + "source": [ + "# Advanced RAG with guided generation\n", + "\n", + "A standard RAG process typically runs a single vector search query and returns the closest matches. Those matches are then passed into a LLM prompt and used to limit the context and help ensure more factually correct answers are generated. This works well with most simple cases. More complex use cases, require a more advanced approach.\n", + "\n", + "This notebook will demonstrate how constrained or guided generation can be applied to better control LLM output." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "p8BbfjrhH-V2" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "-OXsTQgaGQPM" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai outlines" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Define the RAG process\n", + "\n", + "The first step we'll take is to define the RAG process. The following code creates a LLM instance, defines method that takes a question and context then prompts an LLM." + ], + "metadata": { + "id": "Wj7SlK_ZdwN9" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "zC_yDuyKk8ZY" + }, + "outputs": [], + "source": [ + "from txtai import LLM\n", + "\n", + "llm = LLM(\"Qwen/Qwen3-4B-Instruct-2507\")\n", + "\n", + "def rag(question, text):\n", + " prompt = f\"\"\"<|im_start|>system\n", + " You are a friendly assistant. You answer questions from users.<|im_end|>\n", + " <|im_start|>user\n", + " Answer the following question using only the context below. Only include information specifically discussed.\n", + "\n", + " question: {question}\n", + " context: {text} <|im_end|>\n", + " <|im_start|>assistant\n", + " \"\"\"\n", + "\n", + " return llm(prompt, maxlength=4096)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Let's run a simple RAG call to get the idea of the default behavior." + ], + "metadata": { + "id": "M2NQHT8nfZWJ" + } + }, + { + "cell_type": "code", + "source": [ + "# Manually generated context. Replace with an Embedding search or other request. See prior examples on txtai's documentation site for more.\n", + "context = \"\"\"\n", + "England's terrain chiefly consists of low hills and plains, especially in the centre and south.\n", + "The Battle of Hastings was fought on 14 October 1066 between the Norman army of William, the Duke of Normandy, and an English army under the Anglo-Saxon King Harold Godwinson\n", + "Bounded by the Atlantic Ocean on the east, Brazil has a coastline of 7,491 kilometers (4,655 mi).\n", + "Spain pioneered the exploration of the New World and the first circumnavigation of the globe.\n", + "Christopher Columbus lands in the Caribbean in 1492.\n", + "\"\"\"\n", + "\n", + "print(rag(\"List the countries discussed\", context))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "yVXLDj5wfkqF", + "outputId": "984f5e15-1949-413a-d4f0-4e55c991ede2" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "1. England\n", + "2. Brazil\n", + "3. Spain\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Guided Generation\n", + "\n", + "The next step is defining how to guide generation. For this step, we'll use the [Outlines](https://github.com/outlines-dev/outlines) library. Outlines is a library for controlling how tokens are generated. It applies logic to enforce schemas, regular expressions and/or specific output formats such as JSON.\n", + "\n", + "For our first example, we'll guide generation with a model that has answers and citations. With this multi-answer and multi-citation model, we can generate multiple answers along with associated references on how those answers were derived." + ], + "metadata": { + "id": "co2ZA6hyeD3e" + } + }, + { + "cell_type": "code", + "source": [ + "from typing import List\n", + "\n", + "from outlines.integrations.transformers import JSONPrefixAllowedTokens\n", + "from pydantic import BaseModel\n", + "\n", + "class Response(BaseModel):\n", + " answers: List[str]\n", + " citations: List[str]\n", + "\n", + "# Define method that guides LLM generation\n", + "prefix_allowed_tokens_fn=JSONPrefixAllowedTokens(\n", + " schema=Response,\n", + " tokenizer_or_pipe=llm.generator.llm.pipeline.tokenizer,\n", + " whitespace_pattern=r\" ?\"\n", + ")\n", + "\n", + "def rag(question, text):\n", + " prompt = f\"\"\"<|im_start|>system\n", + " You are a friendly assistant. You answer questions from users.<|im_end|>\n", + " <|im_start|>user\n", + " Answer the following question using only the context below. Only include information specifically discussed.\n", + "\n", + " question: {question}\n", + " context: {text} <|im_end|>\n", + " <|im_start|>assistant\n", + " \"\"\"\n", + "\n", + " return llm(prompt, maxlength=4096, prefix_allowed_tokens_fn=prefix_allowed_tokens_fn)\n" + ], + "metadata": { + "id": "5jB_9SMhEZut" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Couple things to unpack here.\n", + "\n", + "First, note the method `prefix_allowed_tokens_fn`. This method applies a [Pydantic](https://github.com/pydantic/pydantic) model to constrain/guide how the LLM generates tokens. Next, see how that constrain can be applied to txtai's LLM pipeline.\n", + "\n", + "Let's try it out." + ], + "metadata": { + "id": "ifCN1dpjgvTi" + } + }, + { + "cell_type": "code", + "source": [ + "import json\n", + "\n", + "json.loads(rag(\"List the countries discussed\", context))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Uf7_Co4VL0uu", + "outputId": "ed752198-4074-4220-83f7-358ab9237bce" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "{'answers': ['England', 'Brazil', 'Spain'],\n", + " 'citations': [\"England's terrain chiefly consists of low hills and plains, especially in the centre and south.\",\n", + " 'The Battle of Hastings was fought on 14 October 1066 between the Norman army of William, the Duke of Normandy, and an English army under the Anglo-Saxon King Harold Godwinson.',\n", + " 'Bounded by the Atlantic Ocean on the east, Brazil has a coastline of 7,491 kilometers (4,655 mi).',\n", + " 'Spain pioneered the exploration of the New World and the first circumnavigation of the globe.',\n", + " 'Christopher Columbus lands in the Caribbean in 1492.']}" + ] + }, + "metadata": {}, + "execution_count": 157 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "This is pretty 🔥\n", + "\n", + "See how not only are the answers generated as they were previously but the answers are now list of answers. And there is a list of citations supporting how the answers were generated! This is also valid JSON." + ], + "metadata": { + "id": "rGbvrtZmhi4Y" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Extracting information models\n", + "\n", + "In our last example, we'll define a more complex model to help with extracting structured information." + ], + "metadata": { + "id": "W0rsUfwniOgn" + } + }, + { + "cell_type": "code", + "source": [ + "class Response(BaseModel):\n", + " countries: List[str]\n", + " geography: List[str]\n", + " years: List[str]\n", + " people: List[str]\n", + "\n", + "prefix_allowed_tokens_fn=JSONPrefixAllowedTokens(\n", + " schema=Response,\n", + " tokenizer_or_pipe=llm.generator.llm.pipeline.tokenizer,\n", + " whitespace_pattern=r\" ?\"\n", + ")\n", + "\n", + "def rag(question, text):\n", + " prompt = f\"\"\"<|im_start|>system\n", + " You are a friendly assistant. You answer questions from users.<|im_end|>\n", + " <|im_start|>user\n", + " Answer the following question using only the context below. Only include information specifically discussed.\n", + "\n", + " question: {question}\n", + " context: {text} <|im_end|>\n", + " <|im_start|>assistant\n", + " \"\"\"\n", + "\n", + " return llm(prompt, maxlength=4096, prefix_allowed_tokens_fn=prefix_allowed_tokens_fn)\n" + ], + "metadata": { + "id": "XHieIoxRgPSU" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "json.loads(rag(\"List the entities discussed\", context))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "7_RpO3v4ib_-", + "outputId": "c3d394db-3f2b-4f60-d2b6-754f8247f929" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "{'countries': ['England', 'Brazil', 'Spain'],\n", + " 'geography': ['low hills and plains', 'Atlantic Ocean', 'New World'],\n", + " 'years': ['1066', '1492'],\n", + " 'people': ['William, the Duke of Normandy',\n", + " 'Anglo-Saxon King Harold Godwinson',\n", + " 'Christopher Columbus']}" + ] + }, + "metadata": {}, + "execution_count": 159 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "This is extremely guided generation. This is constraining the output of the LLM into a very specific model of information. It's quite impressive and simple to get started with!" + ], + "metadata": { + "id": "fDCFeFjSlveR" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "Guided generation adds a number of different options to the RAG toolkit. It's a flexible approach that can enable more advanced functionality and/or fine-tuned control over how content is created.\n", + "\n", + "It does add overhead which may or may not be acceptable depending on the use case. Expect new methods with improved efficiency and accuracy coming in the future. The space continues to advance forward fast!" + ], + "metadata": { + "id": "2LQTekhsl75H" + } + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "gpuType": "T4", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "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", + "version": "3.8.18" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/examples/61_Integrate_txtai_with_Postgres.ipynb b/examples/61_Integrate_txtai_with_Postgres.ipynb new file mode 100644 index 0000000..bd78ba8 --- /dev/null +++ b/examples/61_Integrate_txtai_with_Postgres.ipynb @@ -0,0 +1,606 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "gpuType": "T4" + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Integrate txtai with Postgres\n", + "\n", + "The default persistence methods for txtai are local and file-based. `SQLite` for content, `Faiss` for vectors and `NetworkX` for graph data. The main value add of txtai is getting up and running quickly with a minimal amount of external dependencies.\n", + "\n", + "Another key feature of txtai is being able to quickly move from prototyping to production. This notebook will demonstrate how txtai can integrate with [Postgres](https://www.postgresql.org/), a powerful, production-ready and open source object-relational database system. After txtai persists content to Postgres, we'll show it can be directly queried with SQL from any Postgres client 🔥" + ], + "metadata": { + "id": "j7jcAie81QIH" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ], + "metadata": { + "id": "wB0wcLc32IJZ" + } + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "\n", + "# Install txtai and dependencies\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[ann,database,graph]" + ], + "metadata": { + "id": "ITs6ttB5gU48" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Install Postgres\n", + "\n", + "Next, we'll install Postgres and start a Postgres instance. This will also install the `pgvector` extension to enable vector search and storage.\n", + "\n", + "*Note: With local environments, consider running Postgres as a Docker container.*" + ], + "metadata": { + "id": "VMsobwvU2QUh" + } + }, + { + "cell_type": "code", + "source": [ + "%%capture\n", + "\n", + "# Install Postgres and pgvector\n", + "!apt-get update && apt install postgresql postgresql-server-dev-14\n", + "!git clone --branch v0.6.2 https://github.com/pgvector/pgvector.git\n", + "!cd pgvector && make && make install\n", + "\n", + "# Start database\n", + "!service postgresql start\n", + "!sudo -u postgres psql -U postgres -c \"ALTER USER postgres PASSWORD 'pass';\"" + ], + "metadata": { + "id": "S0P8hEBL2OYd" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Build an Embeddings database\n", + "First, we'll load the 100K most popular articles from Wikipedia. This example will store vectors and content in Postgres." + ], + "metadata": { + "id": "sYrfpy5D2j4Y" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai import Embeddings\n", + "\n", + "# URL set in code for demo purposes. Use environment variables in production.\n", + "url = \"postgresql+psycopg2://postgres:pass@localhost/postgres\"\n", + "\n", + "# Create embeddings\n", + "embeddings = Embeddings(\n", + " content=url,\n", + " backend=\"pgvector\",\n", + " pgvector={\n", + " \"url\": url\n", + " }\n", + ")\n", + "\n", + "# Load dataset\n", + "wikipedia = Embeddings()\n", + "wikipedia.load(provider=\"huggingface-hub\", container=\"neuml/txtai-wikipedia\")\n", + "\n", + "query = \"\"\"\n", + "SELECT id, text FROM txtai\n", + "order by percentile desc\n", + "LIMIT 100000\n", + "\"\"\"\n", + "\n", + "# Index dataset\n", + "embeddings.index(wikipedia.search(query))" + ], + "metadata": { + "id": "a3qM3T5uj007" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "embeddings.search(\"Tell me about a mythical horse\", 1)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "yfjbASug005C", + "outputId": "336b27f6-ae68-4270-ef11-bb6578600abf" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': 'Unicorn',\n", + " 'text': 'The unicorn is a legendary creature that has been described since antiquity as a beast with a single large, pointed, spiraling horn projecting from its forehead.',\n", + " 'score': 0.6493861675262451}]" + ] + }, + "metadata": {}, + "execution_count": 4 + } + ] + }, + { + "cell_type": "code", + "source": [ + "embeddings.search(\"What is the main ingredient in Ketchup?\", 1)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "L0TP2y5c08tv", + "outputId": "4b6c2eb7-285e-4a19-fa11-2e2aae4ab5ea" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': 'Ketchup',\n", + " 'text': 'Ketchup or catsup is a table condiment with a sweet and sour flavor. The unmodified term (\"ketchup\") now typically refers to tomato ketchup, although early recipes for various different varieties of ketchup contained mushrooms, oysters, mussels, egg whites, grapes or walnuts, among other ingredients. ',\n", + " 'score': 0.6998806595802307}]" + ] + }, + "metadata": {}, + "execution_count": 5 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Now, let's explore how the data is stored in further detail. We'll save the database and take a look." + ], + "metadata": { + "id": "LNLXmOTV2z32" + } + }, + { + "cell_type": "code", + "source": [ + "%env TOKENIZERS_PARALLELISM=false\n", + "\n", + "# Commit results to the database\n", + "embeddings.save(\"test\")\n", + "embeddings.close()\n", + "\n", + "!ls test\n", + "!cat test/config.json" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-4v7joOC23HY", + "outputId": "73ef3537-42c7-4c43-be0c-c4ecdbe2c0bd" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "env: TOKENIZERS_PARALLELISM=false\n", + "config.json\n", + "{\n", + " \"content\": \"postgresql+psycopg2://postgres:pass@localhost/postgres\",\n", + " \"backend\": \"pgvector\",\n", + " \"pgvector\": {\n", + " \"url\": \"postgresql+psycopg2://postgres:pass@localhost/postgres\"\n", + " },\n", + " \"path\": \"sentence-transformers/all-MiniLM-L6-v2\",\n", + " \"dimensions\": 384,\n", + " \"offset\": 100000\n", + "}" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Note how there is only a configuration file with the database details. Let's explore the Postgres database.\n" + ], + "metadata": { + "id": "6g-FZJDT2-J6" + } + }, + { + "cell_type": "code", + "source": [ + "!PGPASSWORD=pass psql -h localhost -U postgres -c \"SELECT * from sections order by indexid LIMIT 1\"\n", + "!PGPASSWORD=pass psql -h localhost -U postgres -c \"SELECT * from vectors order by indexid LIMIT 1\"" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "XLfvxYoU3NDS", + "outputId": "4180c5a5-d52a-4acc-9269-9f90bee2d865" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + " indexid | id | text | tags | entry \n", + "---------+-----------+---------------------------------------------+------+----------------------------\n", + " 0 | Main Page | Welcome to Wikipedia, +| | 2024-04-24 17:02:55.335643\n", + " | | the free encyclopedia that anyone can edit.+| | \n", + " | | articles in English | | \n", + "(1 row)\n", + "\n", + " indexid | embedding \n", + "---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n", + " 0 | [0.006224656,0.010993197,-0.020153109,0.046154346,-0.0067520724,0.011807963,-0.014358369,0.028947443,-0.01586261,0.09301224,0.055368096,0.040113293,0.01760968,0.013503182,-0.016870098,-0.012503422,-0.008597899,0.026588641,-0.08471918,-0.04503593,0.060253955,-0.0031328013,0.11977076,0.024629632,-0.022491025,-0.03793595,0.0061959964,0.0072981887,0.00796152,-0.027184578,-0.10998758,0.07487334,0.08721705,-0.003783297,2.4116285e-05,0.01077903,0.009361188,0.020106519,0.025032397,0.026869925,-0.0111307595,-0.09997612,0.019141614,0.015201731,-0.000267303,0.12866877,-0.04923438,-0.059046485,0.013421367,0.04622524,-0.024259627,-0.01578652,-0.057170264,-0.058802612,0.055165343,-0.016757669,-0.11283095,-0.010731938,-0.03237114,-0.09259695,0.026988069,0.044143338,-0.062587716,0.12294508,0.017795669,-0.0022635453,-0.000859233,0.083078146,0.009367941,-0.08944039,0.020436421,0.03098923,0.08612748,0.06908283,-0.011492079,-0.057584476,0.007815615,0.07544962,-0.07507969,-0.031771705,0.06140103,0.045389958,0.0010914068,-0.014727611,-0.0048347805,-0.008656534,-0.011555558,-0.027875263,0.044627354,-0.041978024,0.0023647482,-0.068146914,0.10593634,0.00459672,-0.0043201237,-0.003424118,0.03128756,-0.031586997,0.006560516,0.047244154,0.06694147,-0.0024762554,-0.034794953,0.0685723,-0.115641095,-0.07013151,-0.029626537,0.05652201,-0.0047345776,-0.08600411,-0.02712477,0.06307427,-0.020134196,-0.05487676,-0.010889723,-0.012801617,0.0070655714,0.01565348,0.06468041,-0.0027397969,-0.052258722,0.025590474,0.0004874447,0.0567211,-0.01091167,-0.016167942,-0.056139555,-3.360483e-33,0.06776523,0.07398922,0.021548307,0.0522404,-0.063432045,0.009229409,-0.09691417,-0.05641955,-0.055137824,-0.04107145,0.055179913,0.06995813,-0.043880165,-0.060650233,0.0026008217,0.026983975,-0.03595947,0.11371047,0.018100752,0.027147746,0.034160867,0.050208986,-0.03631904,-0.0035121094,-0.06707664,0.0063270847,0.008653185,-0.11846966,0.0075610424,0.0021807787,0.015405556,-0.017979044,-0.04683855,-0.015657036,-0.04022284,-0.017653259,0.036167417,-0.042651214,0.005417714,-0.09401236,-0.032427326,-0.017289782,-0.04060469,0.015479944,-0.011029847,0.0719449,0.0051068533,-0.06828172,0.08947456,-0.04907346,0.016336497,0.032021444,-0.04396127,-0.0036975099,0.05712386,0.089574024,0.070081435,0.06301272,0.0010954054,0.01729774,0.047785297,0.09727387,-0.0016659853,0.01636651,-0.011960934,0.08042555,-0.044158004,0.015542993,0.039673716,-0.023705207,-0.0705359,-0.0060911574,0.00903571,0.056801423,-0.04288321,0.075399615,-0.060097944,-0.070493154,-0.039201744,0.02091038,0.021842068,-0.05289644,0.032512337,-0.04969522,0.014612853,0.03174613,0.019972498,-0.018262003,0.10062693,0.03011935,0.058835424,0.008656911,-0.0693178,-0.0003107625,-0.035816267,8.551262e-34,-0.08153472,-0.109941624,0.0010887359,0.058030974,-0.05916192,-0.017850945,-0.11167397,0.107069634,0.041289993,-0.019890498,-0.0026840265,-0.098246865,-0.052144755,0.06551777,-0.04089334,-0.028716525,0.03241684,-0.06699939,-0.09264921,0.124276765,-0.07738617,-0.008159692,-0.04621176,-0.003925314,-0.028218271,0.004362621,0.0662335,0.05448178,-0.049431797,0.001068466,-0.06137263,-0.07963246,-0.056226213,-0.050934546,-0.08301799,-0.03711833,0.001591565,-0.042424005,-0.020324506,-0.05731789,0.043870095,0.040546007,0.008878352,-0.06366917,-0.030763976,-0.028072147,-0.101492785,0.0049343915,0.008031439,-0.029323548,0.052847456,-0.015137294,0.02956745,-0.10447449,0.035620738,0.0025644598,-0.032033995,-0.075608805,0.060582157,-0.03741465,-0.05037591,0.024472151,-0.19789998,0.14253329,0.027650978,0.025539117,-0.09750419,0.07298771,0.017285004,-0.05192262,0.022350593,-0.033429917,0.034556758,-0.06539779,0.03567383,-0.023474114,0.04871094,0.0045313137,0.024971936,-0.09835969,-0.0012533984,-0.0019569201,0.015049834,0.009462184,0.026953677,0.045278806,0.024228122,0.0009281798,-0.023020085,-0.024668744,0.012790688,-0.021721361,-0.026636621,0.06193553,-0.026421277,-2.0252372e-08,-0.009053995,-0.022655917,-0.05537436,0.09484807,0.006692196,-0.05305789,0.019044574,0.050973132,-0.004839857,0.07959955,-0.0562942,0.043415457,-0.051326852,0.04842562,-0.022588398,0.05024676,0.022963611,0.0699145,0.016860556,0.009093765,0.04256753,0.016887672,0.04927324,-0.0092127,0.013626016,0.045127258,-0.005615936,-0.039890807,0.0928744,-0.10173735,-0.11896241,-0.0064999964,-0.04553107,0.04174175,0.030028146,0.042279907,0.000696869,0.0012388963,-0.02783201,-0.048495434,0.04485023,0.009498387,-0.009533283,0.036473263,0.028421838,0.01844087,0.01952972,-0.021264598,0.08634994,-0.022555202,0.060081147,0.013670229,0.1497612,-0.021247797,-0.008887135,0.0306203,-0.007087516,0.024898361,0.05220465,0.012215928,0.02570937,0.046506412,0.08931996,-0.0010894431]\n", + "(1 row)\n", + "\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Note how the text content and vectors are stored in this Postgres instance." + ], + "metadata": { + "id": "slEWXtIz3NqN" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Graph Embeddings\n", + "\n", + "Next, we'll rebuild the same embeddings database and enable a graph component. This graph component will also persist content to Postgres." + ], + "metadata": { + "id": "fSjuwiQw3UcH" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai import Embeddings\n", + "\n", + "# Create embeddings\n", + "embeddings = Embeddings(\n", + " content=url,\n", + " backend=\"pgvector\",\n", + " pgvector={\n", + " \"url\": url\n", + " },\n", + " graph={\n", + " \"backend\": \"rdbms\",\n", + " \"url\": url,\n", + " \"approximate\": False,\n", + " }\n", + ")\n", + "\n", + "# Index dataset\n", + "embeddings.index(wikipedia.search(query))" + ], + "metadata": { + "id": "DDj_39ak0_fF" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Now, as with prior graph examples, let's build a new graph with a query. Then we'll plot that subgraph." + ], + "metadata": { + "id": "HG_cABE03e1P" + } + }, + { + "cell_type": "code", + "source": [ + "g = embeddings.graph.search(\"\"\"\n", + "MATCH P=({id: \"Roman Empire\"})-[*1..3]->({id: \"Saxons\"})-[*1..3]->({id: \"Vikings\"})-[*1..3]->({id: \"Battle of Hastings\"})\n", + "RETURN P\n", + "LIMIT 20\n", + "\"\"\", graph=True)" + ], + "metadata": { + "id": "Yhf6nkyb1ChP" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "import matplotlib.pyplot as plt\n", + "import networkx as nx\n", + "\n", + "def plot(graph):\n", + " labels = {x: f\"{graph.attribute(x, 'id')}\" for x in graph.scan()}\n", + " colors = [\"#D32F2F\", \"#0277bd\", \"#7e57c2\", \"#757575\"]\n", + "\n", + " results = embeddings.batchsimilarity(labels.values(), [\"Roman Empire\", \"Germanic Barbarians\", \"Viking conquest and siege\", \"Norman Conquest of England\"])\n", + " colors = [colors[x[0][0]] for x in results]\n", + "\n", + " options = {\n", + " \"node_size\": 2000,\n", + " \"node_color\": colors,\n", + " \"edge_color\": \"#454545\",\n", + " \"font_color\": \"#efefef\",\n", + " \"font_size\": 11,\n", + " \"alpha\": 1.0,\n", + " }\n", + "\n", + " fig, ax = plt.subplots(figsize=(20, 9))\n", + " pos = nx.spring_layout(graph.backend, seed=512, k=0.9, iterations=50)\n", + " nx.draw_networkx(graph.backend, pos=pos, labels=labels, **options)\n", + " ax.set_facecolor(\"#303030\")\n", + " ax.axis(\"off\")\n", + " fig.set_facecolor(\"#303030\")\n", + "\n", + " plt.show()\n", + "\n", + "plot(g)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 730 + }, + "id": "mN6O8b491Exm", + "outputId": "4a7276ef-fdb5-4b31-da35-86c27665549d" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "
" + ], + "image/png": "iVBORw0KGgoAAAANSUhEUgAABiIAAALJCAYAAAA57jvpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdeXhU5dk/8O85s+9ZSUISZF/DjiAKUsUFqtXibt2wlbba1opWW+nbxfa1Vvv+XrWL1mpbsSK4tGrdLYoLgggossqekJCQffZ9zvn9kfccZ7JAEjKZ7fu5Li7IZDLzzCSZhOf73PctTJs2TQYREREREREREREREVESiKleABERERERERERERERZS8GEURERERERERERERElDQMIoiIiIiIiIiIiIiIKGkYRBARERERERERERERUdIwiCAiIiIiIiIiIiIioqRhEEFEREREREREREREREnDIIKIiIiIiIiIiIiIiJKGQQQRERERERERERERESUNgwgiIiIiIiIiIiIiIkoaBhFERERERERERERERJQ0DCKIiIiIiIiIiIiIiChpGEQQEREREREREREREVHSMIggIiIiIiIiIiIiIqKkYRBBRERERERERERERERJwyCCiIiIiIiIiIiIiIiShkEEERERERERERERERElDYMIIiIiIiIiIiIiIiJKGgYRRERERERERERERESUNAwiiIiIiIiIiIiIiIgoaRhEEBERERERERERERFR0jCIICIiIiIiIiIiIiKipGEQQUREREREREREREREScMggoiIiIiIiIiIiIiIkoZBBBERERERERERERERJQ2DCCIiIiIiIiIiIiIiShoGEURERERERERERERElDQMIoiIiIiIiIiIiIiIKGkYRBARERERERERERERUdIwiCAiIiIiIiIiIiIioqRhEEFEREREREREREREREnDIIKIiIiIiIiIiIiIiJKGQQQRERERERERERERESUNgwgiIiIiIiIiIiIiIkoaBhFERERERERERERERJQ0DCKIiIiIiIiIiIiIiChpGEQQEREREREREREREVHSMIggIiIiIiIiIiIiIqKk0aZ6AURERERERETxBEGAxWKB3W6H2WyGKIoQRRGCIECSJEiShGg0Cq/XC7fbjVAolOolExEREdFxMIggIiIiIiKilNLpdCguLobdbofdbofNZoMo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W7YMDQ0N+Ne//qVeZ9GiRRg/fjyeeOIJeL3ePj/2qqoqTJ8+HUVFRQCApqYmvPvuu2hoaFCvM3/+fMyZMwerVq3C+eefjyFDhsDpdOLdd9/FgQMHEm5v1KhRmDdvnvpYd+7ciX379iEYDALoCGVuuukmnHXWWcjPz4fH48HevXvxm9/8BqNHj8ZDDz0EAHjsscfU2zzrrLMwdepUPPTQQ7j77ruxaNEizJo1C9u3b0dtbS0WLFiAq6++OuF7ePbs2bj//vuxdOnShOqPgXLDDTfgyiuvxFe/+tU+vS8VBjoMGj58OM455xwUFBQgGo3if//3f7u93ooVKwAAq1atSvgcGAwG3HHHHXjllVfUn4vJYLPZEr6fUuXxxx/HgQMHcP/996d6KURElIFYEUFERESUw2pra9He3o6qqqqEy6uqqvDWW29h4sSJCS1YqqqqEAqFsHfvXgDAgQMH1JPJvRGJRLBnz54+hRf9ofSwTzczZszApEmT8Prrr+P111/HgQMHcOTIEXz66af429/+hs8+++ykbl8UxZOqqkimkznxrdPpcuYEdF+JoghRFGEymXD66adj/vz5eP/99xOuk5+fj1WrVuHuu+/GH//4R5SWluKhhx5Sv7f/85//QBAELFy4MOHjFi9ejObmZmzevBkAcOaZZ2LFihVobm7GP//5T7z77rsYN24cLrjggj6tORwO49VXX8W4cePU156RI0dixowZeOutt/oVQgAdLaN27NiBF198ES+//DLcbjeuu+46FBQUJFxPFEV8/etfx44dO/DPf/4T7e3tuPTSS1FcXKxeZ/z48bj88ssTHuvEiRNx5513qte55pprcNFFF2H16tW466678Pvf/x6tra3Q6XTYv3+/GkT89re/xS233IJbbrklYR2333476uvr8fOf/xzPPvssXnvtNZSUlGDWrFkJ11u8eDF27dqVlBAi0wz0a/uFF14It9uN1atX45lnnjnh9efNmzdg991bkiQltRqHiIhosLAigoiIiCjH7dy5MyGIGDJkCIYMGYJ//vOfuPDCCzFy5Ej1pHBVVRX27t2LaDQKoKM108aNG/H73/++V/dVUlKCNWvW4Be/+AU++OADAMB5552HCy+8EKeccgoEQcDBgwfx2GOP4YsvvlA/TjmVe+utt+K2227D6NGjUVNTg9/97neoqanB9773PZx9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uyCKAtav8nDvbdvh11/+ubHTMk47fw4WH5WCoorYuamC++/Yjt2bq0jrC1Apjr4KpFqtIgxDyPLLL7kvu+wyvPOd78TVV1+Nj370ozj00EPxox/9CPfffz9uu+02fOELX8ADDzyAFStWYM6cOTj//PNx/vnnA0Dys7NixQo8+uijSci2ePFiXHnllTjiiCOgaRp2796N3/72t/v8TB533HH4yEc+gvnz52Pjxo24/vrr8dJLL4363zOW++pvxowZuOKKK3DSSSdB1/XkZ7r/fZ966ql473vfi4ULFyIIAmzbti35uZtsqqoilUohiqJxVzEoipIElvl8ns/fk4xBxPDK5fKAeRGsHCEiIqJWwSCCpoV4UDVRqxhrMBGHE3zT3TpKpRKCIIBt25BlecKHWMfDh23bRjabnbDb7UT33HMPzj77bCxbtgxPPfVUcvmZZ56JJ554ArlcblS3c/755+NDH/oQvvKVryThRSwIAhSLxaRC5SMf+QgefPBBfPWrX8UrX/lKXHnllVi3bh0ef/xxAH2bqldffTXuvvtuPPDAAzj44IPxhS98YVTrmDlzJu68807s3LkTlmXhLW95C2688UZceumlSauYo48+Gtdddx1eeOEF/Pu//zuKxSIOP/xwzJw5E08//TQ+97nP4ctf/jJ+8IMfJF+TdevWYc6cOQPu6/7778frXvc6dHd3Y9OmTUmlxemnn45169ahp6cHtm1DFEVomobu7m785Xd70J2Zha45Gn77n1sAAJVCAK+njkK2gaNOzeDhO3cl9/GKV6UBAC/+ZXTfh5goCfiHf1yAp1f24LH/2Y0T3zATb75iIX70mRdRLQ19sGDF19biXf/7EDx171787fG+++tfbXHc67qxbW0Zv795KzKzNSx/+xyUPR8P/X29miniHf/rYDSqIVbeth21aohlr+vGhZ9cih997gXY6uwR19y/MiWdTuPSSy9FEAR49NFHB1xPlmV89rOfxS9+8Qv88Ic/HLLy6XOf+xy++tWv4tlnn03aPQ1X1XPttdcim83iG9/4BkqlEubNm4eZM2cOuE5XVxc+9rGPYcWKFSiVSvjgBz+IL33pS3jPe94zpoMao7mv/mzbxre+9S1UKhXccMMNKJVKOP/883Hdddfh0ksvRS6Xw0EHHYT/7//7/3DvvffiBz/4AURRxMEHH4xUKjXqdY2X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hxl7880UfeYznWu4+GLG/frXFB50EGNvuYVJDz3EmPnziY4fT1Bezsgf/5iJDz7I+Lvuovhzn+v03qLDDmP0//k/TLjvPiY++CBjFyyg8JBDOr2m5PjjmfzYY+Tvvjujf/YzJv7P/zDuzjspOe647T9XgGSSsLWVIC+P6VvOf86cOSxatIi9996ba6+9lr/85S9ccMEFQOc/j+uvv56ZM2dy+OGHs2jRIhYtWsSZZ54JwGGHHca1117LH//4RxYuXMiCBQs4+OCDO310+nMqKto3cowbN45FixYxe/Zsvv3tb3P//fdz7733csEFFxCJeHm9r+yIyEFN8Y0s2/QAnxx7crZL6RdhGLJ00/00xXNn9p0kSZIkSZIGnxUrVnDCCSfw4Ycf8tRTT1FTU7PTx7zooot49NFH+fGPf8yBBx7IOeecQ319PQ888ECfjzEKKAaiW3ZGJD78MPPcyEsvpeiQQ6i/5Rbiq1dTetxxjPrJT6i+7DJiS5ZkXhcZOZLKb32LhjvvJEwkqPr2txl56aWEra20vvIKTQsXUjp3LiMvvZR1S5eS3DLqKDphArElS2j43e8glaLo0EMZ/dOfsul736P15Zc71TnysstoWriQhnvuoXTuXEZcfDFty5eTWL162ycZbZ/uEikro/SEE4iOHk3L449TCozo8LLLLruMhQsXctddd9Ha2rrVYebPn88ll1xCa2trprNl45adGuPHj+epp57innvuIZVKceihh/LTn/6U733ve7zyyiu9lvfNb36TJUuWcMUVV7DPPvtw9tlns3bt2u36cxzODCJy1Ls1i5lYNnPIj2hKj2R6t2ZxtkuRJEmSJElSjps/fz5XXnkl3//+9wH44IMPeOqpp/j973/faQfB9njppZe48cYbAXjuuecYOXIkX//611m4cGGnLovuBEFAJBKhKhqlYM89KT/9dFqeeor48uUA5H/sY5TMmkXNf/0XTVsuiLc++yxjxo+n4swzOwcR5eVsnDePxKpVADSMHs2IefOo/81vaLjjDgDa3nyT4s9+luLPfIbGP/wBgKb77utYEK0vv0z+tGmUzp27VRDReN99NP35z+3HeuMNig47jOJZszLH70nx4YczucM+hzCZpG7BAtpeew2A0g6vvf/++7n77rt7PNZ7771Hc3MzLS0tW41s+tOf/tThVAJeeuklpk2bxgkn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+uEwmikKHARUhCH1AzyeqaewIT1KKQzDTHdDekl0+uYoJUmS1N8MIiRJkiRJWbM3wyOESIsAewAv9MOxugsaehqllEgkMouiHaUkSZJ2NYMISZIkSVKPzjzzTM4666zM/bq6OlavXs1dd93FM888s1PHrgpDyneyvv5ScvzxjLz44m6fa7z/fmqvu65fPicBTKPnIGL//fdn/vz5nH/++axYsWK7RiklEgni8XinsMFRSpIkaTAwiJAkSZIk9SoWi/G9730PgNGjR3P66adz9dVXM2/ePN54440dPu70/iqwH238/vcJm5o6PZbcvLnfjh9h6/MOgiATLrz//vt8//vfp66ujlGjRm01SimRSDhKSZIkDTkGEZIkSZKkXoVhyLJlyzL3ly1bxu9+9zvmzJmzU0HENAbffoj4ihWk6uoG7PgRYHegrLSUaIdOh7RUKsXbb7/daUF011FKBQUFtLW1DViNkiRJ/c0gQpIkSZK0XTZt2kRtbS1jx47t9PiRRx7JGWecwdSpU6mvr2fRokX86le/Ih6PAx+NHfrBD37AF77wBT5z6KFQX0/dTTfR8uijlJ18MmVf+xqR4mJanniCmvnzYct7IyNHUnnOORTOnEl01CgSGzfS8thj1N92W+Y1AJMfe4zaX/6SSFERpV/6EkSjxJYsofbnPyeMxXb63MfffTexp54isXYtZV/9KpGyMloef5ya//ov8nfbjap588jffXcSq1ax+T//k8TKlZ1qq7vpJirLy/nNF75AND+fJUuW8N///d80NjaSTCbZb7/9Oo1mAli0aBE33XQT5eXlzJkzh6KiIr74xS8C8NWvfpW5c+cybtw4Nm3axH333ce999670+cpSZLUnwwiJEmSJEnbpaioiIqKCtatW5d57NOf/jSXX3555qL51KlTOeeccxg7diyXX355p/d/5zvf4aGHHmLaAw8wdu5cRl5yCY0f/zh506dTe911RCdOpOrCC0l88AENd90FQKSyklRDA7ULFpBqaCB/8mQqzjqL6KhR1PzsZ52OX3bSSbS9+iqbf/pT8idPpvKCC0jW1FB/003bPrlIBKJdejS6LHYuOuIIEitXUvtf/5WpNYzHKdxnHxruuYdUTQ2V55/PqCuuYP2ZZ0KHPQ1lJ51E21tvced//AfNEydy7rnnEolEuPLKK3st65RTTmHp0qVce+21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\n" + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "🚀 Very exciting. A full graph embeddings database all backed by Postgres!\n", + "\n", + "Let's explore the database once again." + ], + "metadata": { + "id": "AVjFXwlo3rzI" + } + }, + { + "cell_type": "code", + "source": [ + "embeddings.save(\"test\")" + ], + "metadata": { + "id": "Exx4xHvL1G_y" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "!PGPASSWORD=pass psql -h localhost -U postgres -c \"SELECT * from sections order by indexid LIMIT 1\"\n", + "!PGPASSWORD=pass psql -h localhost -U postgres -c \"SELECT * from vectors order by indexid LIMIT 1\"\n", + "!PGPASSWORD=pass psql -h localhost -U postgres -c \"SELECT * from nodes LIMIT 1\"\n", + "!PGPASSWORD=pass psql -h localhost -U postgres -c \"SELECT * from edges LIMIT 1\"" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "CGqlh84o1Ku7", + "outputId": "37fee7e6-1e7b-42e9-c803-2bebc7186633" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + " indexid | id | text | tags | entry \n", + "---------+-----------+---------------------------------------------+------+----------------------------\n", + " 0 | Main Page | Welcome to Wikipedia, +| | 2024-04-24 17:24:04.402629\n", + " | | the free encyclopedia that anyone can edit.+| | \n", + " | | articles in English | | \n", + "(1 row)\n", + "\n", + " indexid | embedding \n", + "---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n", + " 0 | [0.006224656,0.010993197,-0.020153109,0.046154346,-0.0067520724,0.011807963,-0.014358369,0.028947443,-0.01586261,0.09301224,0.055368096,0.040113293,0.01760968,0.013503182,-0.016870098,-0.012503422,-0.008597899,0.026588641,-0.08471918,-0.04503593,0.060253955,-0.0031328013,0.11977076,0.024629632,-0.022491025,-0.03793595,0.0061959964,0.0072981887,0.00796152,-0.027184578,-0.10998758,0.07487334,0.08721705,-0.003783297,2.4116285e-05,0.01077903,0.009361188,0.020106519,0.025032397,0.026869925,-0.0111307595,-0.09997612,0.019141614,0.015201731,-0.000267303,0.12866877,-0.04923438,-0.059046485,0.013421367,0.04622524,-0.024259627,-0.01578652,-0.057170264,-0.058802612,0.055165343,-0.016757669,-0.11283095,-0.010731938,-0.03237114,-0.09259695,0.026988069,0.044143338,-0.062587716,0.12294508,0.017795669,-0.0022635453,-0.000859233,0.083078146,0.009367941,-0.08944039,0.020436421,0.03098923,0.08612748,0.06908283,-0.011492079,-0.057584476,0.007815615,0.07544962,-0.07507969,-0.031771705,0.06140103,0.045389958,0.0010914068,-0.014727611,-0.0048347805,-0.008656534,-0.011555558,-0.027875263,0.044627354,-0.041978024,0.0023647482,-0.068146914,0.10593634,0.00459672,-0.0043201237,-0.003424118,0.03128756,-0.031586997,0.006560516,0.047244154,0.06694147,-0.0024762554,-0.034794953,0.0685723,-0.115641095,-0.07013151,-0.029626537,0.05652201,-0.0047345776,-0.08600411,-0.02712477,0.06307427,-0.020134196,-0.05487676,-0.010889723,-0.012801617,0.0070655714,0.01565348,0.06468041,-0.0027397969,-0.052258722,0.025590474,0.0004874447,0.0567211,-0.01091167,-0.016167942,-0.056139555,-3.360483e-33,0.06776523,0.07398922,0.021548307,0.0522404,-0.063432045,0.009229409,-0.09691417,-0.05641955,-0.055137824,-0.04107145,0.055179913,0.06995813,-0.043880165,-0.060650233,0.0026008217,0.026983975,-0.03595947,0.11371047,0.018100752,0.027147746,0.034160867,0.050208986,-0.03631904,-0.0035121094,-0.06707664,0.0063270847,0.008653185,-0.11846966,0.0075610424,0.0021807787,0.015405556,-0.017979044,-0.04683855,-0.015657036,-0.04022284,-0.017653259,0.036167417,-0.042651214,0.005417714,-0.09401236,-0.032427326,-0.017289782,-0.04060469,0.015479944,-0.011029847,0.0719449,0.0051068533,-0.06828172,0.08947456,-0.04907346,0.016336497,0.032021444,-0.04396127,-0.0036975099,0.05712386,0.089574024,0.070081435,0.06301272,0.0010954054,0.01729774,0.047785297,0.09727387,-0.0016659853,0.01636651,-0.011960934,0.08042555,-0.044158004,0.015542993,0.039673716,-0.023705207,-0.0705359,-0.0060911574,0.00903571,0.056801423,-0.04288321,0.075399615,-0.060097944,-0.070493154,-0.039201744,0.02091038,0.021842068,-0.05289644,0.032512337,-0.04969522,0.014612853,0.03174613,0.019972498,-0.018262003,0.10062693,0.03011935,0.058835424,0.008656911,-0.0693178,-0.0003107625,-0.035816267,8.551262e-34,-0.08153472,-0.109941624,0.0010887359,0.058030974,-0.05916192,-0.017850945,-0.11167397,0.107069634,0.041289993,-0.019890498,-0.0026840265,-0.098246865,-0.052144755,0.06551777,-0.04089334,-0.028716525,0.03241684,-0.06699939,-0.09264921,0.124276765,-0.07738617,-0.008159692,-0.04621176,-0.003925314,-0.028218271,0.004362621,0.0662335,0.05448178,-0.049431797,0.001068466,-0.06137263,-0.07963246,-0.056226213,-0.050934546,-0.08301799,-0.03711833,0.001591565,-0.042424005,-0.020324506,-0.05731789,0.043870095,0.040546007,0.008878352,-0.06366917,-0.030763976,-0.028072147,-0.101492785,0.0049343915,0.008031439,-0.029323548,0.052847456,-0.015137294,0.02956745,-0.10447449,0.035620738,0.0025644598,-0.032033995,-0.075608805,0.060582157,-0.03741465,-0.05037591,0.024472151,-0.19789998,0.14253329,0.027650978,0.025539117,-0.09750419,0.07298771,0.017285004,-0.05192262,0.022350593,-0.033429917,0.034556758,-0.06539779,0.03567383,-0.023474114,0.04871094,0.0045313137,0.024971936,-0.09835969,-0.0012533984,-0.0019569201,0.015049834,0.009462184,0.026953677,0.045278806,0.024228122,0.0009281798,-0.023020085,-0.024668744,0.012790688,-0.021721361,-0.026636621,0.06193553,-0.026421277,-2.0252372e-08,-0.009053995,-0.022655917,-0.05537436,0.09484807,0.006692196,-0.05305789,0.019044574,0.050973132,-0.004839857,0.07959955,-0.0562942,0.043415457,-0.051326852,0.04842562,-0.022588398,0.05024676,0.022963611,0.0699145,0.016860556,0.009093765,0.04256753,0.016887672,0.04927324,-0.0092127,0.013626016,0.045127258,-0.005615936,-0.039890807,0.0928744,-0.10173735,-0.11896241,-0.0064999964,-0.04553107,0.04174175,0.030028146,0.042279907,0.000696869,0.0012388963,-0.02783201,-0.048495434,0.04485023,0.009498387,-0.009533283,0.036473263,0.028421838,0.01844087,0.01952972,-0.021264598,0.08634994,-0.022555202,0.060081147,0.013670229,0.1497612,-0.021247797,-0.008887135,0.0306203,-0.007087516,0.024898361,0.05220465,0.012215928,0.02570937,0.046506412,0.08931996,-0.0010894431]\n", + "(1 row)\n", + "\n", + " ID | _metadata \n", + "----+-------------------------------------------------------------------------------------------------------------------------\n", + " 0 | {\"id\": \"Main Page\", \"data\": \"Welcome to Wikipedia,\\nthe free encyclopedia that anyone can edit.\\n articles in English\"}\n", + "(1 row)\n", + "\n", + " ID | _metadata | Source | Target \n", + "----------+--------------------------------+--------+--------\n", + " __0__179 | {\"weight\": 0.7411725521087646} | 0 | 179\n", + "(1 row)\n", + "\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "As we saw before, text content and vectors are stored in the database. Additionally, we now have graph node and edge data.\n", + "\n", + "Let's take this up a level." + ], + "metadata": { + "id": "5aLMn_RS316T" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Vector search with Postgres\n", + "\n", + "Now that the data is persisted in Postgres, can we run vector search without loading the txtai database locally? Yes!" + ], + "metadata": { + "id": "4o9TVWG53_9W" + } + }, + { + "cell_type": "code", + "source": [ + "# Query with a search string\n", + "query = str(list(embeddings.transform(\"Roman Empire\")))\n", + "query = f\"\"\"\n", + "SELECT id, (embedding <#> '{query}') * -1 AS score, text FROM sections s \\\n", + "JOIN vectors v ON s.indexid = v.indexid \\\n", + "ORDER by score desc LIMIT 5\n", + "\"\"\"\n", + "\n", + "!PGPASSWORD=pass psql -h localhost -U postgres -c \"{query}\"" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "jOgh1MBO1MvW", + "outputId": "235e1a80-3fc2-457a-861d-f8791febc46d" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + " id | score | text \n", + "-----------------------------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n", + " Roman Empire | 0.6798915266990662 | The Roman Empire was the post-Republican state of ancient Rome and is generally understood to mean the period and territory ruled by the Romans following Octavian's assumption of sole rule under the Principate in 31 BC. It included territory in Europe, North Africa, and Western Asia, and was ruled by emperors. The fall of the Western Roman Empire in 476 conventionally marks the end of classical antiquity and the beginning of the Middle Ages.\n", + " History of the Roman Empire | 0.6295346617698669 | The history of the Roman Empire covers the history of ancient Rome from the fall of the Roman Republic in 27 BC until the abdication of Romulus Augustulus in AD 476 in the West, and the Fall of Constantinople in the East in AD 1453. Ancient Rome became a territorial empire while still a republic, but was then ruled by Roman emperors beginning with Augustus, becoming the Roman Empire following the death of the last republican dictator, the first emperor's adoptive father Julius Caesar.\n", + " Roman Republic | 0.6280723810195923 | The Roman Republic was the era of classical Roman civilization beginning with the overthrow of the Roman Kingdom (traditionally dated to 509 BC) and ending in 27 BC with the establishment of the Roman Empire. During this period, Rome's control expanded from the city's immediate surroundings to hegemony over the entire Mediterranean world.\n", + " Latin Empire | 0.613895058631897 | The Latin Empire, also referred to as the Latin Empire of Constantinople, was a feudal Crusader state founded by the leaders of the Fourth Crusade on lands captured from the Byzantine Empire. The Latin Empire was intended to replace the Byzantine Empire as the Western-recognized Roman Empire in the east, with a Catholic emperor enthroned in place of the Eastern Orthodox Roman emperors. The main objective of the Latin Empire was planned by Venice, which promoted the creation of this state for their self-benefit.\n", + " Augustus | 0.6021512150764465 | Gaius Julius Caesar Augustus (born Gaius Octavius; 23 September 63 BC – 19 August AD 14), also known as Octavianus or Octavian, was the founder of the Roman Empire; he reigned as the first Roman emperor from 27 BC until his death in AD 14. The reign of Augustus initiated an imperial cult as well as an era associated with imperial peace, the Pax Romana or Pax Augusta, in which the Roman world was largely free of armed conflict aside from expansionary wars and the Year of the Four Emperors. The Principate system of imperial rule established by Augustus lasted until the Crisis of the Third Century.\n", + "(5 rows)\n", + "\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Just like that, vector search without a local txtai instance! The only part needed is a way to vectorize the search query. This can easily be replaced with an API vectorization service.\n", + "\n", + "We can also find the most similar rows for an existing row." + ], + "metadata": { + "id": "CM2sr_NI4Mo5" + } + }, + { + "cell_type": "code", + "source": [ + "# Find top n results closest to an existing row\n", + "query = \"\"\"\n", + "SELECT id, text FROM sections s \\\n", + "JOIN vectors v ON s.indexid = v.indexid \\\n", + "WHERE v.indexid != 738 ORDER by v.embedding <#> (SELECT embedding FROM vectors WHERE indexid=738) LIMIT 5\n", + "\"\"\"\n", + "\n", + "!PGPASSWORD=pass psql -h localhost -U postgres -c \"{query}\"" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "MnolJkL31OzU", + "outputId": "d23954b0-849e-45b8-f32e-6af835e2a18f" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + " id | text \n", + "----------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n", + " History of the Roman Empire | The history of the Roman Empire covers the history of ancient Rome from the fall of the Roman Republic in 27 BC until the abdication of Romulus Augustulus in AD 476 in the West, and the Fall of Constantinople in the East in AD 1453. Ancient Rome became a territorial empire while still a republic, but was then ruled by Roman emperors beginning with Augustus, becoming the Roman Empire following the death of the last republican dictator, the first emperor's adoptive father Julius Caesar.\n", + " Roman Republic | The Roman Republic was the era of classical Roman civilization beginning with the overthrow of the Roman Kingdom (traditionally dated to 509 BC) and ending in 27 BC with the establishment of the Roman Empire. During this period, Rome's control expanded from the city's immediate surroundings to hegemony over the entire Mediterranean world.\n", + " Roman Kingdom | The Roman Kingdom, also referred to as the Roman monarchy or the regal period of ancient Rome, was the earliest period of Roman history when the city and its territory were ruled by kings. According to tradition, the Roman Kingdom began with the city's founding 753 BC, with settlements around the Palatine Hill along the river Tiber in central Italy, and ended with the overthrow of the kings and the establishment of the Republic 509 BC.\n", + " Byzantine Empire | The Byzantine Empire, also referred to as the Eastern Roman Empire, was the continuation of the Roman Empire centered in Constantinople during Late Antiquity and the Middle Ages. The eastern half of the Empire survived the conditions that caused the fall of the West in the 5th century AD, and continued to exist until the fall of Constantinople to the Ottoman Empire in 1453. During most of its existence, the empire remained the most powerful economic, cultural, and military force in the Mediterranean world. The term \"Byzantine Empire\" was only coined following the empire's demise: its citizens referred to the polity as the \"Roman Empire\" and to themselves as \"Romans\". Due to the imperial seat's move from Rome to Byzantium, the adoption of state Christianity, and the predominance of Greek instead of Latin, modern historians continue to make a distinction between the earlier \"Roman Empire\" and the later \"Byzantine Empire\".\n", + " Fall of the Western Roman Empire | The fall of the Western Roman Empire, also called the fall of the Roman Empire or the fall of Rome, was the loss of central political control in the Western Roman Empire, a process in which the Empire failed to enforce its rule, and its vast territory was divided into several successor polities. The Roman Empire lost the strengths that had allowed it to exercise effective control over its Western provinces; modern historians posit factors including the effectiveness and numbers of the army, the health and numbers of the Roman population, the strength of the economy, the competence of the emperors, the internal struggles for power, the religious changes of the period, and the efficiency of the civil administration. Increasing pressure from invading barbarians outside Roman culture also contributed greatly to the collapse. Climatic changes and both endemic and epidemic disease drove many of these immediate factors. The reasons for the collapse are major subjects of the historiography of the ancient world and they inform much modern discourse on state failure.\n", + "(5 rows)\n", + "\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "From prototyping to production, txtai is ready for all that can be thrown it's way. As always, this functionality is just the beginning and will continue to improve over time. But this is a big deal and has impacts for future services such as [txtai.cloud](https://txtai.cloud) ☁️\n", + "\n", + "Stay tuned!\n" + ], + "metadata": { + "id": "o7uQBS0N4nvN" + } + } + ] +} \ No newline at end of file diff --git a/examples/62_RAG_with_llama_cpp_and_external_API_services.ipynb b/examples/62_RAG_with_llama_cpp_and_external_API_services.ipynb new file mode 100644 index 0000000..62c9dbe --- /dev/null +++ b/examples/62_RAG_with_llama_cpp_and_external_API_services.ipynb @@ -0,0 +1,432 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "za-eT0AZAVEi" + }, + "source": [ + "# RAG with llama.cpp and external API services\n", + "\n", + "txtai has been and always will be a local-first framework. It was originally designed to run models on local hardware using Hugging Face Transformers. As the AI space has evolved over the last year, so has txtai. Additional LLM inference frameworks have been available for a while using llama.cpp and external API services (via LiteLLM). Recent changes have added the ability to use these frameworks for vectorization and made it easier to use for LLM inference.\n", + "\n", + "This notebook will demonstrate how to run retrieval-augmented-generation (RAG) processes (vectorization and LLM inference) with llama.cpp and external API services." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2e6iFXweAVEk" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "-UwwKDHCAVEk" + }, + "outputs": [], + "source": [ + "%%capture\n", + "\n", + "# Install txtai and dependencies\n", + "!pip install llama-cpp-python[server] --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cu121\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline-llm]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YGu8d_niAVEk" + }, + "source": [ + "# Embeddings with llama.cpp vectorization\n", + "\n", + "The first example will build an Embeddings database backed by [llama.cpp](https://github.com/ggerganov/llama.cpp) vectorization.\n", + "\n", + "The llama.cpp project states: _The main goal of llama.cpp is to enable LLM inference with minimal setup and state-of-the-art performance on a wide variety of hardware - locally and in the cloud_.\n", + "\n", + "Let's give it a try." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "gZgObbS0AVEl" + }, + "outputs": [], + "source": [ + "from txtai import Embeddings\n", + "\n", + "# Create Embeddings with llama.cpp GGUF model\n", + "embeddings = Embeddings(\n", + " path=\"second-state/All-MiniLM-L6-v2-Embedding-GGUF/all-MiniLM-L6-v2-Q4_K_M.gguf\",\n", + " content=True\n", + ")\n", + "\n", + "# Load dataset\n", + "wikipedia = Embeddings()\n", + "wikipedia.load(provider=\"huggingface-hub\", container=\"neuml/txtai-wikipedia\")\n", + "\n", + "query = \"\"\"\n", + "SELECT id, text FROM txtai\n", + "order by percentile desc\n", + "LIMIT 10000\n", + "\"\"\"\n", + "\n", + "# Index dataset\n", + "embeddings.index(wikipedia.search(query))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qD2jypWnAVEl" + }, + "source": [ + "Now that the Embeddings database is ready, let's run a search query." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "QhGrDP7AAVEl", + "outputId": "42223d8d-a33e-47f6-9de3-e24cec4b31b0" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': 'Thomas Edison',\n", + " 'text': 'Thomas Alva Edison (February 11, 1847October 18, 1931) was an American inventor and businessman. He developed many devices in fields such as electric power generation, mass communication, sound recording, and motion pictures. These inventions, which include the phonograph, the motion picture camera, and early versions of the electric light bulb, have had a widespread impact on the modern industrialized world. He was one of the first inventors to apply the principles of organized science and teamwork to the process of invention, working with many researchers and employees. He established the first industrial research laboratory.',\n", + " 'score': 0.6758285164833069},\n", + " {'id': 'Nikola Tesla',\n", + " 'text': 'Nikola Tesla (; , ; 1856\\xa0– 7 January 1943) was a Serbian-American inventor, electrical engineer, mechanical engineer, and futurist. He is best-known for his contributions to the design of the modern alternating current (AC) electricity supply system.',\n", + " 'score': 0.6077840328216553},\n", + " {'id': 'Alexander Graham Bell',\n", + " 'text': 'Alexander Graham Bell (, born Alexander Bell; March 3, 1847 – August 2, 1922) was a Scottish-born Canadian-American inventor, scientist and engineer who is credited with patenting the first practical telephone. He also co-founded the American Telephone and Telegraph Company (AT&T) in 1885.',\n", + " 'score': 0.4573010802268982}]" + ] + }, + "metadata": {}, + "execution_count": 3 + } + ], + "source": [ + "embeddings.search(\"Inventors of electric-powered devices\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "QyaqgY8QAVEm" + }, + "source": [ + "As we can see, this Embeddings database works just like any other Embeddings database. The difference is that it's using a llama.cpp model for vectorization instead of PyTorch." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cewP6JCEAVEm" + }, + "source": [ + "# RAG with llama.cpp\n", + "\n", + "LLM inference with llama.cpp is not a new txtai feature. A recent change added support for conversational messages in additional to standard prompts. This abstracts away having to understand prompting formats.\n", + "\n", + "Let's run a retrieval-augmented-generation (RAG) process fully backed by llama.cpp models.\n", + "\n", + "_It's important to note that conversational messages work with all LLM backends supported by txtai (transformers, llama.cpp, litellm)._" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "XTNX1mtMAVEm", + "outputId": "62d92e44-02eb-4695-a507-0fc9729f0b57" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Based on the given context, here's a list of invented electric-powered devices:\n", + "\n", + "1. Electric light bulb by Thomas Edison\n", + "2. Phonograph by Thomas Edison\n", + "3. Motion picture camera by Thomas Edison\n", + "4. Alternating current (AC) electricity supply system by Nikola Tesla\n", + "5. Telephone by Alexander Graham Bell\n" + ] + } + ], + "source": [ + "from txtai import LLM\n", + "\n", + "# LLM instance\n", + "llm = LLM(path=\"unsloth/Qwen3-4B-Instruct-2507-GGUF/Qwen3-4B-Instruct-2507-Q4_K_M.gguf\")\n", + "\n", + "# Question and context\n", + "question = \"Write a list of invented electric-powered devices\"\n", + "context = \"\\n\".join(x[\"text\"] for x in embeddings.search(question))\n", + "\n", + "# Pass messages to LLM\n", + "response = llm([\n", + " {\"role\": \"system\", \"content\": \"You are a friendly assistant. You answer questions from users.\"},\n", + " {\"role\": \"user\", \"content\": f\"\"\"\n", + "Answer the following question using only the context below. Only include information specifically discussed.\n", + "\n", + "question: {question}\n", + "context: {context}\n", + "\"\"\"}\n", + "])\n", + "print(response)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "RQSApBO_AVEm" + }, + "source": [ + "And just like that, RAG with llama.cpp🦙!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Qrf44ulzAVEn" + }, + "source": [ + "# Embeddings with external vectorization\n", + "\n", + "Next, we'll show how an Embeddings database can integrate with external API services via [LiteLLM](https://github.com/BerriAI/litellm) .\n", + "\n", + "In the LiteLLM project's own words: _LiteLLM handles loadbalancing, fallbacks and spend tracking across 100+ LLMs. All in the OpenAI format._\n", + "\n", + "Let's first startup a local API service to use for this demo." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "iRoh9rPEAVEn" + }, + "outputs": [], + "source": [ + "%%capture\n", + "\n", + "# Download models\n", + "!wget https://huggingface.co/second-state/All-MiniLM-L6-v2-Embedding-GGUF/resolve/main/all-MiniLM-L6-v2-Q4_K_M.gguf\n", + "!wget https://huggingface.co/unsloth/Qwen3-4B-Instruct-2507-GGUF/blob/main/Qwen3-4B-Instruct-2507-Q4_K_M.gguf\n", + "\n", + "# Start local API services\n", + "!nohup python -m llama_cpp.server --n_gpu_layers -1 --model all-MiniLM-L6-v2-Q4_K_M.gguf --host 127.0.0.1 --port 8000 &> vector.log &\n", + "!nohup python -m llama_cpp.server --n_gpu_layers -1 --model Qwen3-4B-Instruct-2507-Q4_K_M.gguf --chat_format chatml --host 127.0.0.1 --port 8001 &> llm.log &\n", + "!sleep 30" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "fPnpAMnvAVEo" + }, + "source": [ + "Now let's connect and use this local service to generate vectors for a new Embeddings database. Note that the local service responds in OpenAI's response format, hence the `path` setting below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "EpSLnVfwAVEo" + }, + "outputs": [], + "source": [ + "from txtai import Embeddings\n", + "\n", + "# Create Embeddings instance with external vectorization\n", + "embeddings = Embeddings(\n", + " path=\"openai/gpt-4-turbo\",\n", + " content=True,\n", + " vectors={\n", + " \"api_base\": \"http://localhost:8000/v1\",\n", + " \"api_key\": \"sk-1234\"\n", + " }\n", + ")\n", + "\n", + "# Load dataset\n", + "wikipedia = Embeddings()\n", + "wikipedia.load(provider=\"huggingface-hub\", container=\"neuml/txtai-wikipedia\")\n", + "\n", + "query = \"\"\"\n", + "SELECT id, text FROM txtai\n", + "order by percentile desc\n", + "LIMIT 10000\n", + "\"\"\"\n", + "\n", + "# Index dataset\n", + "embeddings.index(wikipedia.search(query))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "teoKbfzZAVEo", + "outputId": "55594186-c989-419e-974a-37a150ba4734", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': 'Thomas Edison',\n", + " 'text': 'Thomas Alva Edison (February 11, 1847October 18, 1931) was an American inventor and businessman. He developed many devices in fields such as electric power generation, mass communication, sound recording, and motion pictures. These inventions, which include the phonograph, the motion picture camera, and early versions of the electric light bulb, have had a widespread impact on the modern industrialized world. He was one of the first inventors to apply the principles of organized science and teamwork to the process of invention, working with many researchers and employees. He established the first industrial research laboratory.',\n", + " 'score': 0.6758285164833069},\n", + " {'id': 'Nikola Tesla',\n", + " 'text': 'Nikola Tesla (; , ; 1856\\xa0– 7 January 1943) was a Serbian-American inventor, electrical engineer, mechanical engineer, and futurist. He is best-known for his contributions to the design of the modern alternating current (AC) electricity supply system.',\n", + " 'score': 0.6077840328216553},\n", + " {'id': 'Alexander Graham Bell',\n", + " 'text': 'Alexander Graham Bell (, born Alexander Bell; March 3, 1847 – August 2, 1922) was a Scottish-born Canadian-American inventor, scientist and engineer who is credited with patenting the first practical telephone. He also co-founded the American Telephone and Telegraph Company (AT&T) in 1885.',\n", + " 'score': 0.4573010802268982}]" + ] + }, + "metadata": {}, + "execution_count": 7 + } + ], + "source": [ + "embeddings.search(\"Inventors of electric-powered devices\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0X1F1KcNAVEo" + }, + "source": [ + "Like the previous example with llama.cpp, this Embeddings database behaves exactly the same. The main difference is that content is sent to an external service for vectorization." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4Dr4HjhOAVEo" + }, + "source": [ + "# RAG with External API services\n", + "\n", + "For our last task, we'll run a retrieval-augmented-generation (RAG) process fully backed by an external API service." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "42sPo6HCAVEo", + "outputId": "7e42e28a-0bdd-4f68-a1ee-403527a241fd", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Based on the given context, a list of invented electric-powered devices includes:\n", + "\n", + "1. Phonograph by Thomas Edison\n", + "2. Motion Picture Camera by Thomas Edison\n", + "3. Early versions of the Electric Light Bulb by Thomas Edison\n", + "4. AC (Alternating Current) Electricity Supply System by Nikola Tesla\n", + "5. Telephone by Alexander Graham Bell\n" + ] + } + ], + "source": [ + "from txtai import LLM\n", + "\n", + "# LLM instance\n", + "llm = LLM(path=\"openai/gpt-4-turbo\", api_base=\"http://localhost:8001/v1\", api_key=\"sk-1234\")\n", + "\n", + "# Question and context\n", + "question = \"Write a list of invented electric-powered devices\"\n", + "context = \"\\n\".join(x[\"text\"] for x in embeddings.search(question))\n", + "\n", + "# Pass messages to LLM\n", + "response = llm([\n", + " {\"role\": \"system\", \"content\": \"You are a friendly assistant. You answer questions from users.\"},\n", + " {\"role\": \"user\", \"content\": f\"\"\"\n", + "Answer the following question using only the context below. Only include information specifically discussed.\n", + "\n", + "question: {question}\n", + "context: {context}\n", + "\"\"\"}\n", + "])\n", + "print(response)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kFcqP5A7AVEp" + }, + "source": [ + "# Wrapping up\n", + "\n", + "txtai supports a number of different vector and LLM backends. The default method uses PyTorch models via the Hugging Face Transformers library. This notebook demonstrated how llama.cpp and external API services can also be used.\n", + "\n", + "These additional vector and LLM backends enable maximum flexibility and scalability. For example, vectorization can be fully offloaded to an external API service or another local service. llama.cpp has great support for macOS devices, alternate accelerators such AMD ROCm / Intel GPUs and has been known to run on Raspberry Pi devices.\n", + "\n", + "It's exciting to see the confluence of all these new advances coming together. Stay tuned for more!" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "gpuType": "T4", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "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", + "version": "3.8.19" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/examples/63_How_RAG_with_txtai_works.ipynb b/examples/63_How_RAG_with_txtai_works.ipynb new file mode 100644 index 0000000..f52600e --- /dev/null +++ b/examples/63_How_RAG_with_txtai_works.ipynb @@ -0,0 +1,527 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "VGeVB8M41jqW" + }, + "source": [ + "# How RAG with txtai works\n", + "\n", + "Large Language Models (LLMs) have captured the public's attention with their impressive capabilities. The Generative AI era has reached a fever pitch with some predicting the coming rise of superintelligence.\n", + "\n", + "LLMs are far from perfect though and we're still a ways away from true AI. The biggest challenge is with hallucinations. Hallucinations is the term for when a LLM generates output that is factually incorrect. The alarming part of this is that on a cursory glance, it actually sounds like factual content. The default behavior of LLMs is to produce plausible answers even when no plausible answer exists. LLMs are not great at saying I don't know.\n", + "\n", + "Retrieval Augmented Generation (RAG) helps reduce the risk of hallucinations by limiting the context in which a LLM can generate answers. This is typically done with a search query that hydrates a prompt with a relevant context. RAG has been one of the most practical use cases of the Generative AI era.\n", + "\n", + "txtai has a multiple ways to run RAG pipelines as follows.\n", + "\n", + "- Embeddings instance and LLM. Run the embeddings search and plug the search results into a LLM prompt.\n", + "- RAG (aka Extractor) pipeline which automatically adds a search context to LLM prompts.\n", + "- RAG FastAPI service with YAML\n", + "\n", + "This notebook will cover all these methods and shows how RAG with txtai works." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZQrHIw351lwE" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "id": "R0AqRP7v1hdr" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[api,pipeline]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xmPN8RDF1pXd" + }, + "source": [ + "# Components of a RAG pipeline\n", + "\n", + "Before using txtai's RAG pipeline, we'll show how each of the underlying components work together. In this example, we'll load the [txtai Wikipedia embeddings database](https://huggingface.co/NeuML/txtai-wikipedia) and a LLM. From there, we'll run a RAG process." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "XZ7vPBIs1rGZ" + }, + "outputs": [], + "source": [ + "from txtai import Embeddings, LLM\n", + "\n", + "# Load Wikipedia Embeddings database\n", + "embeddings = Embeddings()\n", + "embeddings.load(provider=\"huggingface-hub\", container=\"neuml/txtai-wikipedia\")\n", + "\n", + "# Create LLM\n", + "llm = LLM(\"Qwen/Qwen3-4B-Instruct-2507\")" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Next, we'll create a prompt template to use for the RAG pipeline. The prompt has a placeholder for the question and context." + ], + "metadata": { + "id": "ufUxz8MXLA8n" + } + }, + { + "cell_type": "code", + "source": [ + "# Prompt template\n", + "prompt = \"\"\"<|im_start|>system\n", + "You are a friendly assistant. You answer questions from users.<|im_end|>\n", + "<|im_start|>user\n", + "Answer the following question using only the context below. Only include information\n", + "specifically discussed.\n", + "\n", + "question: {question}\n", + "context: {context} <|im_end|>\n", + "<|im_start|>assistant\n", + "\"\"\"" + ], + "metadata": { + "id": "4f8DbjzMKwaE" + }, + "execution_count": 3, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "After that, we'll generate the context using an embeddings (aka vector) query. This query finds the top 3 most similar matches to the question **\"How do you make beer 🍺?\"**" + ], + "metadata": { + "id": "x8tp7IoBL27y" + } + }, + { + "cell_type": "code", + "source": [ + "question = \"How do you make beer?\"\n", + "\n", + "# Generate context\n", + "context = \"\\n\".join([x[\"text\"] for x in embeddings.search(question)])\n", + "print(context)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Uyi_WgpULLFs", + "outputId": "a4c7ae0c-287c-409a-ab55-c8c353125405" + }, + "execution_count": 4, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Brewing is the production of beer by steeping a starch source (commonly cereal grains, the most popular of which is barley) in water and fermenting the resulting sweet liquid with yeast. It may be done in a brewery by a commercial brewer, at home by a homebrewer, or communally. Brewing has taken place since around the 6th millennium BC, and archaeological evidence suggests that emerging civilizations, including ancient Egypt, China, and Mesopotamia, brewed beer. Since the nineteenth century the brewing industry has been part of most western economies.\n", + "Beer is produced through steeping a sugar source (commonly Malted cereal grains) in water and then fermenting with yeast. Brewing has taken place since around the 6th millennium BC, and archeological evidence suggests that this technique was used in ancient Egypt. Descriptions of various beer recipes can be found in Sumerian writings, some of the oldest known writing of any sort. Brewing is done in a brewery by a brewer, and the brewing industry is part of most western economies. In 19th century Britain, technological discoveries and improvements such as Burtonisation and the Burton Union system significantly changed beer brewing.\n", + "Craft beer is a beer that has been made by craft breweries, which typically produce smaller amounts of beer, than larger \"macro\" breweries, and are often independently owned. Such breweries are generally perceived and marketed as emphasising enthusiasm, new flavours, and varied brewing techniques.\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Now we'll take the question and context and put that into the prompt." + ], + "metadata": { + "id": "x_EPbqvFL_Uw" + } + }, + { + "cell_type": "code", + "source": [ + "print(llm(prompt.format(question=question, context=context)))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "QCh1IaJ1L1Kt", + "outputId": "93fbeab4-e477-4834-d01c-366fbf86361f" + }, + "execution_count": 5, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "To make beer, you need to steep a starch source, such as malted cereal grains (commonly barley), in water. This process creates a sweet liquid called wort. Then, yeast is added to the wort, which ferments the liquid and produces alcohol and carbon dioxide. The beer is then aged, filtered, and packaged for consumption. This process has been used since around the 6th millennium BC and has been a part of most western economies since the 19th century.\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Looking at the generated answer, we can see it's based on the context above. The LLM generates a paragraph of text using the context as input. While this same answer could be directly asked of the LLM, this helps ensure the answer is based on known factual data." + ], + "metadata": { + "id": "CSNbxxIMN_26" + } + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "_Before continuing, it's important to note that txtai has multiple ways to run LLM inference. In the past, prior to \"Chat Templates\", it was expected that the submitted text had all the required chat tokens embedded. The same prompt above can also be written with chat messages. This is especially important when working with LLM APIs (i.e. OpenAI, Claude, Bedrock etc)._\n", + "\n", + "```python\n", + "llm([\n", + " {\"role\": \"system\": \"You are a friendly assistant. You answer questions from users.\"}\n", + " {\"role\": \"user\", \"content\": f\"\"\"\n", + " Answer the following question using only the context below. Only include information specifically discussed.\n", + "\n", + " question: {question}\n", + " context: {text} \n", + " \"\"\"}\n", + "])\n", + "```\n", + "\n", + "_See the [LLM pipeline documentation](https://neuml.github.io/txtai/pipeline/text/llm/) for more information._" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# The RAG Pipeline\n", + "\n", + "txtai has a RAG pipeline that makes this even easier. The logic to generate the context and join it context with the prompt is built in. Let's try that." + ], + "metadata": { + "id": "tgLA-61iOP0r" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai import RAG\n", + "\n", + "# Create RAG pipeline using existing components. LLM parameter can also be a model path.\n", + "rag = RAG(embeddings, llm, template=prompt)" + ], + "metadata": { + "id": "eWMQUTxFOf_5" + }, + "execution_count": 6, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Let's ask a question similar to the last one. This time we'll ask **\"How do you make wine🍷?\"**" + ], + "metadata": { + "id": "8ePe71D0O0j_" + } + }, + { + "cell_type": "code", + "source": [ + "print(rag(\"How do you make wine?\", maxlength=2048)[\"answer\"])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "3ZaUe2YcOz_B", + "outputId": "ada69201-9f94-498b-b33a-971b4d2e4fdb" + }, + "execution_count": 7, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "To make wine, follow these steps:\n", + "\n", + "1. Select the fruit: Choose high-quality grapes or other fruit for wine production.\n", + "\n", + "2. Fermentation: Introduce yeast to the fruit, which will consume the sugar present in the juice and convert it into ethanol and carbon dioxide.\n", + "\n", + "3. Monitor temperature and oxygen levels: Control the temperature and speed of fermentation, as well as the levels of oxygen present in the must at the start of fermentation.\n", + "\n", + "4. Primary fermentation: This stage lasts from 5 to 14 days, during which the yeast consumes the sugar and produces alcohol and carbon dioxide.\n", + "\n", + "5. Secondary fermentation (optional): If desired, allow the wine to undergo a secondary fermentation, which can last another 5 to 10 days.\n", + "\n", + "6. Fermentation location: Choose the appropriate fermentation vessel, such as stainless steel tanks, open wooden vats, wine barrels, or wine bottles for sparkling wines.\n", + "\n", + "7. Bottle and age the wine: Transfer the finished wine into bottles and allow it to age, if desired, to develop flavors and complexity.\n", + "\n", + "Remember that wine can be made from various fruits, but grapes are most commonly used, and the term \"wine\" generally refers to grape wine when used without a qualifier.\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "_As with the LLM pipeline, the RAG pipeline also supports chat messages. See the [RAG pipeline documentation](https://neuml.github.io/txtai/pipeline/text/rag/) for more._" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# RAG API Endpoint\n", + "\n", + "Did you know that txtai has a built-in framework for automatically generating FastAPI services? This can be done with a YAML configuration file." + ], + "metadata": { + "id": "L5udDj5dMrjY" + } + }, + { + "cell_type": "code", + "source": [ + "%%writefile config.yml\n", + "\n", + "# Load Wikipedia Embeddings index\n", + "cloud:\n", + " provider: huggingface-hub\n", + " container: neuml/txtai-wikipedia\n", + "\n", + "# RAG pipeline configuration\n", + "rag:\n", + " path: Qwen/Qwen3-4B-Instruct-2507\n", + " output: flatten\n", + " template: |\n", + " <|im_start|>system\n", + " You are a friendly assistant. You answer questions from users.<|im_end|>\n", + " <|im_start|>user\n", + " Answer the following question using only the context below. Only include information\n", + " specifically discussed.\n", + "\n", + " question: {question}\n", + " context: {context} <|im_end|>\n", + " <|im_start|>assistant" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "NxEQqdzmNA0T", + "outputId": "268d6027-d3d7-46f1-acc4-2016d3059f5e" + }, + "execution_count": 8, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Writing config.yml\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Note how the same prompt template and models are set. This time instead of doing that with Python, it's done with a YAML configuration file 🔥\n", + "\n", + "Now let's start the API service using this configuration." + ], + "metadata": { + "id": "rAmN_3aWNNyK" + } + }, + { + "cell_type": "code", + "source": [ + "!CONFIG=config.yml nohup uvicorn \"txtai.api:app\" &> api.log &\n", + "!sleep 90" + ], + "metadata": { + "id": "gzHPH5GKNXOQ" + }, + "execution_count": 7, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Now let's run a RAG query using the API service. Keeping with the theme, we'll ask **\"How do you make whisky 🥃?\"**" + ], + "metadata": { + "id": "bqIqEsAjPqsa" + } + }, + { + "cell_type": "code", + "source": [ + "!curl \"http://localhost:8000/rag?query=how+do+you+make+whisky&maxlength=2048\"" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "f14clGi1PeQW", + "outputId": "0a056edc-e5c2-464d-fef0-c766369a2590" + }, + "execution_count": 22, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "To make whisky, follow these steps:\n\n", + "1. Choose the grains: Select the grains you want to use for your whisky, such as barley, corn, rye, or wheat.\n\n", + "2. Malt the grains (optional): If using barley, malt the grains by soaking them in water and allowing them to germinate. This process releases enzymes that help break down starches into fermentable sugars.\n\n", + "3. Mill the grains: Grind the grains to create a coarse flour, which will be mixed with water to create a mash.\n\n", + "4. Create the mash: Combine the milled grains with hot water in a large vessel, and let it sit for several hours to allow fermentation to occur. The mash should have a temperature of around 65°C (149°F) to encourage the growth of yeast.\n\n", + "5. Add yeast: Once the mash has cooled to around 30°C (86°F), add yeast to the mixture. The yeast will ferment the sugars in the mash, producing alcohol.\n\n", + "6. Fermentation: Allow the mixture to ferment for several days, during which the yeast will consume the sugars and produce alcohol and carbon dioxide.\n\n", + "7. Distillation: Transfer the fermented liquid, called \"wash\" to a copper still. Heat the wash in the still, and the alcohol will vaporize and rise through the still's neck. The vapors are then condensed back into a liquid form, creating a high-proof spirit.\n\n", + "8. Maturation: Transfer the distilled spirit to wooden casks, typically made of charred white oak. The spirit will mature in the casks for a specified period, usually ranging from 3 to 25 years. During this time, the wood imparts flavors and color to the whisky.\n\n", + "9. Bottling: Once the whisky has reached the desired maturity, it is bottled and ready for consumption." + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "And as before, we get an answer bound by the search context provided to the LLM. This time it comes from an API service vs a direct Python method." + ], + "metadata": { + "id": "L8xTq1cTVTn7" + } + }, + { + "cell_type": "markdown", + "source": [ + "# RAG API Service with Docker\n", + "\n", + "txtai builds Docker images with each release. There are also Docker files available to help configure API services.\n", + "\n", + "The Dockerfile below builds an API service using the same config.yml." + ], + "metadata": { + "id": "_UszA8iLQzGr" + } + }, + { + "cell_type": "markdown", + "source": [ + "```dockerfile\n", + "# Set base image\n", + "ARG BASE_IMAGE=neuml/txtai-gpu\n", + "FROM $BASE_IMAGE\n", + "\n", + "# Copy configuration\n", + "COPY config.yml .\n", + "\n", + "# Install latest version of txtai from GitHub\n", + "RUN \\\n", + " apt-get update && \\\n", + " apt-get -y --no-install-recommends install git && \\\n", + " rm -rf /var/lib/apt/lists && \\\n", + " python -m pip install git+https://github.com/neuml/txtai\n", + "\n", + "# Run local API instance to cache models in container\n", + "RUN python -c \"from txtai.api import API; API('config.yml')\"\n", + "\n", + "# Start server and listen on all interfaces\n", + "ENV CONFIG \"config.yml\"\n", + "ENTRYPOINT [\"uvicorn\", \"--host\", \"0.0.0.0\", \"txtai.api:app\"]\n", + "```" + ], + "metadata": { + "id": "z5BQu2UPQ-wV" + } + }, + { + "cell_type": "markdown", + "source": [ + "The following commands build and start a Docker API service." + ], + "metadata": { + "id": "rhADZQUyVmDL" + } + }, + { + "cell_type": "markdown", + "source": [ + "```bash\n", + "docker build -t txtai-wikipedia --build-arg BASE_IMAGE=neuml/txtai-gpu .\n", + "docker run -d --gpus=all -it -p 8000:8000 txtai-wikipedia\n", + "```" + ], + "metadata": { + "id": "fMyuCMcDRPzW" + } + }, + { + "cell_type": "markdown", + "source": [ + "This creates the same API service just this time it's through Docker. RAG queries can be run the same way." + ], + "metadata": { + "id": "TF1joce_R4nx" + } + }, + { + "cell_type": "markdown", + "source": [ + "```bash\n", + "curl \"http://localhost:8000/rag?query=how+do+you+make+whisky&maxlength=2048\"\n", + "```" + ], + "metadata": { + "id": "oN6l0G-gSDBb" + } + }, + { + "cell_type": "markdown", + "metadata": { + "id": "oPwgCgBc2Er2" + }, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook covered the various ways to run retrieval augmented generation (RAG) with txtai. We hope you find txtai is one of the easiest and most flexible ways to get up and running fast!" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "gpuType": "T4", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/examples/64_Embeddings_index_format_for_open_data_access.ipynb b/examples/64_Embeddings_index_format_for_open_data_access.ipynb new file mode 100644 index 0000000..8d0e7ce --- /dev/null +++ b/examples/64_Embeddings_index_format_for_open_data_access.ipynb @@ -0,0 +1,569 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "gpuClass": "standard", + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Embeddings index format for open data access\n", + "\n", + "The main programming language with txtai is Python. A key tenet is that the underlying data in an embeddings index is accessible without txtai.\n", + "\n", + "This notebook will demonstrate this through a series of examples.\n" + ], + "metadata": { + "id": "-xU9P9iSR-Cy" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ], + "metadata": { + "id": "shlUi2kKS7KT" + } + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "id": "xEvX9vCpn4E0" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[graph] datasets sqlite-vec" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Load dataset\n", + "\n", + "This example will use the `chatgpt-prompts` dataset." + ], + "metadata": { + "id": "408IyXzKFSiG" + } + }, + { + "cell_type": "code", + "source": [ + "from datasets import load_dataset\n", + "\n", + "dataset = load_dataset(\"fka/awesome-chatgpt-prompts\", split=\"train\")" + ], + "metadata": { + "id": "IQ_ns6YvFRm1" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Build an Embeddings index\n", + "\n", + "Let's first build an embeddings index using txtai." + ], + "metadata": { + "id": "AtEdP7Utw3mk" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai import Embeddings\n", + "\n", + "embeddings = Embeddings()\n", + "embeddings.index((x[\"act\"], x[\"prompt\"]) for x in dataset)\n", + "embeddings.save(\"txtai-index\")" + ], + "metadata": { + "id": "DPWrubv5oOn7" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Let's take a look at the index that was created" + ], + "metadata": { + "id": "0zrjOl6JtPI2" + } + }, + { + "cell_type": "code", + "source": [ + "!ls -l txtai-index\n", + "!echo\n", + "!file txtai-index/*" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "i7PN9itKtIyx", + "outputId": "ac904afb-83a8-4c5f-cf62-f5340225b8ac" + }, + "execution_count": 4, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "total 268\n", + "-rw-r--r-- 1 root root 342 Sep 6 15:21 config.json\n", + "-rw-r--r-- 1 root root 262570 Sep 6 15:21 embeddings\n", + "-rw-r--r-- 1 root root 2988 Sep 6 15:21 ids\n", + "\n", + "txtai-index/config.json: JSON data\n", + "txtai-index/embeddings: data\n", + "txtai-index/ids: data\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The txtai embeddings index format is documented [here](https://neuml.github.io/txtai/embeddings/format/). Looking at the files above, we have configuration, embeddings data and ids storage. Ids storage is only used when content is disabled.\n", + "\n", + "Let's inspect each file." + ], + "metadata": { + "id": "hx8H0dpXtX5b" + } + }, + { + "cell_type": "code", + "source": [ + "import json\n", + "\n", + "with open(\"txtai-index/config.json\") as f:\n", + " print(json.dumps(json.load(f), sort_keys=True, indent=2))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "h0yrQ8rqtnzh", + "outputId": "3379ab18-1805-4ef1-b946-1a61d18522db" + }, + "execution_count": 5, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "{\n", + " \"backend\": \"faiss\",\n", + " \"build\": {\n", + " \"create\": \"2024-09-06T15:21:11Z\",\n", + " \"python\": \"3.10.12\",\n", + " \"settings\": {\n", + " \"components\": \"IDMap,Flat\"\n", + " },\n", + " \"system\": \"Linux (x86_64)\",\n", + " \"txtai\": \"7.5.0\"\n", + " },\n", + " \"dimensions\": 384,\n", + " \"offset\": 170,\n", + " \"path\": \"sentence-transformers/all-MiniLM-L6-v2\",\n", + " \"update\": \"2024-09-06T15:21:11Z\"\n", + "}\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "import faiss\n", + "\n", + "index = faiss.read_index(\"txtai-index/embeddings\")\n", + "print(f\"Total records {index.ntotal}\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-aqiqfqeuM5p", + "outputId": "4b7856f0-ec8d-4960-a419-e88a14d988a8" + }, + "execution_count": 6, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Total records 170\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "import msgpack\n", + "\n", + "with open(\"txtai-index/ids\", \"rb\") as f:\n", + " print(msgpack.unpack(f)[5:10])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "I0ffDyJsunrW", + "outputId": "d8c5072d-d181-46e1-fc1a-cc713a78e69f" + }, + "execution_count": 18, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "['JavaScript Console', 'Excel Sheet', 'English Pronunciation Helper', 'Spoken English Teacher and Improver', 'Travel Guide']\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Each file can be read without txtai. [JSON](https://www.json.org/json-en.html), [MessagePack](https://msgpack.org/index.html) and [Faiss](https://github.com/facebookresearch/faiss) all have libraries in multiple programming languages." + ], + "metadata": { + "id": "0e3dpAFuvP-_" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Embeddings index with SQLite\n", + "\n", + "In the next example, we'll use SQLite to store content and vectors courtesy of the [sqlite-vec](https://github.com/asg017/sqlite-vec) library." + ], + "metadata": { + "id": "UUwk13mzwUTS" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai import Embeddings\n", + "\n", + "embeddings = Embeddings(content=True, backend=\"sqlite\")\n", + "embeddings.index((x[\"act\"], x[\"prompt\"]) for x in dataset)\n", + "embeddings.save(\"txtai-sqlite\")" + ], + "metadata": { + "id": "Z80FWhuNwj14" + }, + "execution_count": 8, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Let's once again explore the generated index files." + ], + "metadata": { + "id": "Kxcm42rixAH0" + } + }, + { + "cell_type": "code", + "source": [ + "!ls -l txtai-sqlite\n", + "!echo\n", + "!file txtai-sqlite/*" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "PEHT6LqHw_lw", + "outputId": "81d707dd-9760-40cd-e0b2-be43c26ba2d1" + }, + "execution_count": 9, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "total 1696\n", + "-rw-r--r-- 1 root root 384 Sep 6 15:21 config.json\n", + "-rw-r--r-- 1 root root 126976 Sep 6 15:21 documents\n", + "-rw-r--r-- 1 root root 1605632 Sep 6 15:21 embeddings\n", + "\n", + "txtai-sqlite/config.json: JSON data\n", + "txtai-sqlite/documents: SQLite 3.x database, last written using SQLite version 3037002, file counter 1, database pages 31, cookie 0x1, schema 4, UTF-8, version-valid-for 1\n", + "txtai-sqlite/embeddings: SQLite 3.x database, last written using SQLite version 3037002, file counter 1, database pages 392, cookie 0x1, schema 4, UTF-8, version-valid-for 1\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "This time note how there is a documents file with content stored in SQLite and a separate SQLite file for embeddings. Let's test it out." + ], + "metadata": { + "id": "UXiKSG0JxLPo" + } + }, + { + "cell_type": "code", + "source": [ + "embeddings.search(\"teacher\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "VYMTXtUpxSLd", + "outputId": "6259f3c2-6a76-434d-d40f-f11cb2e63c80" + }, + "execution_count": 10, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[{'id': 'Math Teacher',\n", + " 'text': 'I want you to act as a math teacher. I will provide some mathematical equations or concepts, and it will be your job to explain them in easy-to-understand terms. This could include providing step-by-step instructions for solving a problem, demonstrating various techniques with visuals or suggesting online resources for further study. My first request is \"I need help understanding how probability works.\"',\n", + " 'score': 0.3421396017074585},\n", + " {'id': 'Educational Content Creator',\n", + " 'text': 'I want you to act as an educational content creator. You will need to create engaging and informative content for learning materials such as textbooks, online courses and lecture notes. My first suggestion request is \"I need help developing a lesson plan on renewable energy sources for high school students.\"',\n", + " 'score': 0.3267676830291748},\n", + " {'id': 'Philosophy Teacher',\n", + " 'text': 'I want you to act as a philosophy teacher. I will provide some topics related to the study of philosophy, and it will be your job to explain these concepts in an easy-to-understand manner. This could include providing examples, posing questions or breaking down complex ideas into smaller pieces that are easier to comprehend. My first request is \"I need help understanding how different philosophical theories can be applied in everyday life.\"',\n", + " 'score': 0.30780404806137085}]" + ] + }, + "metadata": {}, + "execution_count": 10 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "The top N results as expected. Let's again inspect the files." + ], + "metadata": { + "id": "R0zsQx37xjWz" + } + }, + { + "cell_type": "code", + "source": [ + "import json\n", + "\n", + "with open(\"txtai-sqlite/config.json\") as f:\n", + " print(json.dumps(json.load(f), sort_keys=True, indent=2))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "jDvH6dHrxqwf", + "outputId": "dcce1647-0488-4f3e-dba0-667a932bad27" + }, + "execution_count": 11, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "{\n", + " \"backend\": \"sqlite\",\n", + " \"build\": {\n", + " \"create\": \"2024-09-06T15:21:13Z\",\n", + " \"python\": \"3.10.12\",\n", + " \"settings\": {\n", + " \"sqlite\": \"3.37.2\",\n", + " \"sqlite-vec\": \"v0.1.1\"\n", + " },\n", + " \"system\": \"Linux (x86_64)\",\n", + " \"txtai\": \"7.5.0\"\n", + " },\n", + " \"content\": true,\n", + " \"dimensions\": 384,\n", + " \"offset\": 170,\n", + " \"path\": \"sentence-transformers/all-MiniLM-L6-v2\",\n", + " \"update\": \"2024-09-06T15:21:13Z\"\n", + "}\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "import sqlite3, sqlite_vec\n", + "\n", + "db = sqlite3.connect(\"txtai-sqlite/documents\")\n", + "print(db.execute(\"SELECT COUNT(*) FROM sections\").fetchone()[0])\n", + "\n", + "db = sqlite3.connect(\"txtai-sqlite/embeddings\")\n", + "db.enable_load_extension(True)\n", + "sqlite_vec.load(db)\n", + "print(db.execute(\"SELECT COUNT(*) FROM vectors\").fetchone()[0])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "AsalQ-uUxxO0", + "outputId": "2a0e303e-e10e-424c-a952-e473d86cd2db" + }, + "execution_count": 12, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "170\n", + "170\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "As in the previous example, each file can be read without txtai. [JSON](https://www.json.org/json-en.html), [SQLite](https://www.sqlite.org/) and [sqlite-vec](https://github.com/asg017/sqlite-vec) all have libraries in multiple programming languages." + ], + "metadata": { + "id": "Fo7XEUNny6s5" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Graph storage\n", + "\n", + "Starting with txtai 7.4, graphs are stored using MessagePack. The indexed file has a list of nodes and edges that can easily be imported." + ], + "metadata": { + "id": "Ipu08WhL0j6p" + } + }, + { + "cell_type": "code", + "source": [ + "from txtai import Embeddings\n", + "\n", + "embeddings = Embeddings(content=True, backend=\"sqlite\", graph={\"approximate\": False})\n", + "embeddings.index((x[\"act\"], x[\"prompt\"]) for x in dataset)\n", + "embeddings.save(\"txtai-graph\")" + ], + "metadata": { + "id": "cmFiae6j0wHz" + }, + "execution_count": 13, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "!ls -l txtai-graph\n", + "!echo\n", + "!file txtai-graph/*" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "hnv82EAB059Q", + "outputId": "454b263b-8733-4997-eb6e-7d585cfa32c6" + }, + "execution_count": 14, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "total 1816\n", + "-rw-r--r-- 1 root root 454 Sep 6 15:21 config.json\n", + "-rw-r--r-- 1 root root 126976 Sep 6 15:21 documents\n", + "-rw-r--r-- 1 root root 1605632 Sep 6 15:21 embeddings\n", + "-rw-r--r-- 1 root root 119970 Sep 6 15:21 graph\n", + "\n", + "txtai-graph/config.json: JSON data\n", + "txtai-graph/documents: SQLite 3.x database, last written using SQLite version 3037002, file counter 1, database pages 31, cookie 0x1, schema 4, UTF-8, version-valid-for 1\n", + "txtai-graph/embeddings: SQLite 3.x database, last written using SQLite version 3037002, file counter 1, database pages 392, cookie 0x1, schema 4, UTF-8, version-valid-for 1\n", + "txtai-graph/graph: data\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "import msgpack\n", + "\n", + "with open(\"txtai-graph/graph\", \"rb\") as f:\n", + " data = msgpack.unpack(f)\n", + " print(data.keys())\n", + "\n", + " for key in data:\n", + " if data[key]:\n", + " print(key, data[key][100])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Bx3Tzt3l08R3", + "outputId": "662d157c-6912-4626-db64-d959c2719331" + }, + "execution_count": 15, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "dict_keys(['nodes', 'edges', 'categories', 'topics'])\n", + "nodes [100, {'id': 'Ascii Artist', 'text': 'I want you to act as an ascii artist. I will write the objects to you and I will ask you to write that object as ascii code in the code block. Write only ascii code. Do not explain about the object you wrote. I will say the objects in double quotes. My first object is \"cat\"'}]\n", + "edges [5, 100, {'weight': 0.39010339975357056}]\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Wrapping up\n", + "\n", + "This notebook gave an overview of the txtai embeddings index file format and how it supports open data access.\n", + "\n", + "While txtai can be used as an all-in-one embeddings database, it can also be used for only one part of the stack such as data ingestion. For example, it can be used to populate a Postgres or SQLite database for downstream use. The options are there." + ], + "metadata": { + "id": "y7N-YZlR5S-0" + } + } + ] +} \ No newline at end of file diff --git a/examples/65_Speech_to_Speech_RAG.ipynb b/examples/65_Speech_to_Speech_RAG.ipynb new file mode 100644 index 0000000..8ef1d74 --- /dev/null +++ b/examples/65_Speech_to_Speech_RAG.ipynb @@ -0,0 +1,260 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Speech to Speech RAG\n", + "\n", + "There are many articles, notebooks and examples covering how to perform vector search and/or retrieval augmented generation (RAG) with txtai. A lesser known component of txtai is it's built-in workflow component.\n", + "\n", + "Workflows are a simple yet powerful construct that takes a callable and returns elements. Workflows enable efficient processing of pipeline data. Workflows are streaming by nature and work on data in batches. This allows large volumes of data to be processed efficiently.\n", + "\n", + "This notebook will demonstrate how to to build a Speech to Speech (S2S) workflow with txtai.\n", + "\n", + "_Note: This process is intended to run on local machines due to it's use of input and output audio devices._" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline-audio]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Define the S2S RAG Workflow\n", + "\n", + "The next section defines the Speech to Speech (S2S) RAG workflow. The objective of this workflow is to respond to a user request in near real-time.\n", + "\n", + "txtai supports workflow definitions in Python and with YAML. We'll cover both methods.\n", + "\n", + "The S2S workflow below starts with a microphone pipeline, which streams and processes input audio. The microphone pipeline has voice activity detection (VAD) built-in. When speech is detected, the pipeline returns the captured audio data. Next, the speech is transcribed to text and then passed to a RAG pipeline prompt. Finally, the RAG result is run through a text to speech (TTS) pipeline and streamed to an output audio device." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import logging\n", + "\n", + "from txtai import Embeddings, RAG\n", + "from txtai.pipeline import AudioStream, Microphone, TextToSpeech, Transcription\n", + "from txtai.workflow import Workflow, StreamTask, Task\n", + "\n", + "# Enable DEBUG logging\n", + "logging.basicConfig()\n", + "logging.getLogger().setLevel(logging.DEBUG)\n", + "\n", + "# Microphone\n", + "microphone = Microphone()\n", + "\n", + "# Transcription\n", + "transcribe = Transcription(\"distil-whisper/distil-large-v3\")\n", + "\n", + "# Embeddings database\n", + "embeddings = Embeddings()\n", + "embeddings.load(provider=\"huggingface-hub\", container=\"neuml/txtai-wikipedia\")\n", + "\n", + "# Define prompt template\n", + "template = \"\"\"\n", + "Answer the following question using only the context below. Only include information\n", + "specifically discussed. Answer the question without explaining how you found the answer.\n", + "\n", + "question: {question}\n", + "context: {context}\"\"\"\n", + "\n", + "# Create RAG pipeline\n", + "rag = RAG(\n", + " embeddings,\n", + " \"Qwen/Qwen3-4B-Instruct-2507\",\n", + " system=\"You are a friendly assistant. You answer questions from users.\",\n", + " template=template,\n", + " context=10\n", + ")\n", + "\n", + "# Text to speech\n", + "tts = TextToSpeech(\"neuml/vctk-vits-onnx\")\n", + "\n", + "# Audio stream\n", + "audiostream = AudioStream()\n", + "\n", + "# Define speech to speech workflow\n", + "workflow = Workflow(tasks=[\n", + " Task(action=microphone),\n", + " Task(action=transcribe, unpack=False),\n", + " StreamTask(action=lambda x: rag(x, maxlength=4096, stream=True), batch=True),\n", + " StreamTask(action=lambda x: tts(x, stream=True, speaker=15), batch=True),\n", + " StreamTask(action=audiostream, batch=True)\n", + "])\n", + "\n", + "while True:\n", + " print(\"Waiting for input...\")\n", + " list(workflow([None]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Given that the input and outputs are audio, you'll have to use your imagination if you're reading this as an article.\n", + "\n", + "[Check out this video](https://www.youtube.com/watch?v=tH8QWwkVMKA) to see the workflow in action! The following examples are run:\n", + "\n", + "- Tell me about the Roman Empire\n", + "- Explain how faster than light travel could work\n", + "- Write a short poem about the Vikings\n", + "- Tell me about the Roman Empire in French" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# S2S Workflow in YAML\n", + "\n", + "A crucial feature of txtai workflows is that they can be defined with YAML. This enables building workflows in a low-code and/or no-code setting. These YAML workflows can then be \"dockerized\" and run.\n", + "\n", + "Let's define the same workflow below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%writefile s2s.yml\n", + "# Microphone\n", + "microphone:\n", + "\n", + "# Transcription\n", + "transcription:\n", + " path: distil-whisper/distil-large-v3\n", + "\n", + "# Embeddings database\n", + "cloud:\n", + " provider: huggingface-hub\n", + " container: neuml/txtai-wikipedia\n", + "\n", + "embeddings:\n", + "\n", + "# RAG\n", + "rag:\n", + " path: \"Qwen/Qwen3-4B-Instruct-2507\"\n", + " system: You are a friendly assistant. You answer questions from users.\n", + " template: |\n", + " Answer the following question using only the context below. Only include information\n", + " specifically discussed. Answer the question without explaining how you found the answer.\n", + "\n", + " question: {question}\n", + " context: {context}\n", + " context: 10\n", + "\n", + "# TTS\n", + "texttospeech:\n", + " path: neuml/vctk-vits-onnx\n", + "\n", + "# AudioStream\n", + "audiostream:\n", + "\n", + "# Speech to Speech Chat workflow\n", + "workflow:\n", + " s2s:\n", + " tasks:\n", + " - microphone\n", + " - action: transcription\n", + " unpack: False\n", + " - task: stream\n", + " action: rag\n", + " args:\n", + " maxlength: 4096\n", + " stream: True\n", + " batch: True\n", + " - task: stream\n", + " action: texttospeech\n", + " args:\n", + " stream: True\n", + " speaker: 15\n", + " batch: True\n", + " - task: stream\n", + " action: audiostream\n", + " batch: True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from txtai import Application\n", + "\n", + "app = Application(\"s2s.yml\")\n", + "while True:\n", + " print(\"Waiting for input...\")\n", + " list(app.workflow(\"s2s\", [None]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Once again, the same idea, just a different way to do it. In the video demo, the following query was asked.\n", + "\n", + "- As a Patriots fan, who would you guess is my favorite quarterback of all time is?\n", + "- I'm tall and run fast, what do you think the best soccer position for me is?\n", + "- I run slow, what do you think the best soccer position for me is?\n", + "\n", + "With YAML workflows, it's possible to fully define the process outside of code such as with a web interface. Perhaps someday we'll see this with [txtai.cloud](https://txtai.cloud) 😀" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook demonstrated how to build a Speech to Speech (S2S) workflow with txtai. While the workflow uses an off-the-shelf embeddings database, a custom embeddings database can easily be swapped in. From there, we have S2S with our own data!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "local", + "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", + "version": "3.8.20" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/66_Generative_Audio.ipynb b/examples/66_Generative_Audio.ipynb new file mode 100644 index 0000000..df3cd6d --- /dev/null +++ b/examples/66_Generative_Audio.ipynb @@ -0,0 +1,400 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Generative Audio\n", + "\n", + "txtai works with much more than just text! It has rich multimedia and multimodal capabilities.\n", + "\n", + "This notebook will demonstrate how to build generative audio workflows. These workflows will generate a combined audio stream with text and relevant audio for a series of poems." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline-audio]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Define a Generative Audio workflow\n", + "\n", + "The next section defines a generative audio workflow. This workflow consists of a set of pipelines as follows:\n", + "\n", + "- LLM\n", + " - Model used to describe the emotions of a given story or poem\n", + "- Text To Audio\n", + " - Builds audio given a text prompt\n", + "- Text To Speech\n", + " - Converts text to speech\n", + "- Audio Mixer\n", + " - Joins multiple audio streams together into a single stream" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import logging\n", + "\n", + "import soundfile as sf\n", + "\n", + "from IPython.display import Audio, display\n", + "\n", + "from txtai import LLM\n", + "from txtai.pipeline import AudioMixer, TextToAudio, TextToSpeech\n", + "from txtai.workflow import Workflow, Task, TemplateTask\n", + "\n", + "# Enable DEBUG logging\n", + "logging.basicConfig()\n", + "logging.getLogger(\"txtai.workflow.base\").setLevel(logging.DEBUG)\n", + "logging.getLogger(\"txtai.workflow.task.base\").setLevel(logging.DEBUG)\n", + "\n", + "def play(audio):\n", + " # Convert to MP3 to save space\n", + " sf.write(\"audio.wav\", audio[0].T, audio[1])\n", + " !ffmpeg -i audio.wav -y -b:a 64 audio.mp3 2> /dev/null\n", + "\n", + " # Play speech\n", + " display(Audio(filename=\"audio.mp3\"))\n", + " return audio\n", + "\n", + "# LLM\n", + "llm = LLM(\"Qwen/Qwen3-4B-Instruct-2507\")\n", + "\n", + "# Text to Audio\n", + "# Important: The code for musicgen is licensed as MIT but model weights are CC-BY-NC\n", + "tta = TextToAudio(\"facebook/musicgen-stereo-small\")\n", + "\n", + "# Audio mixer\n", + "mixer = AudioMixer()\n", + "\n", + "# Define prompt template\n", + "template = \"\"\"\n", + "<|begin_of_text|><|start_header_id|>user<|end_header_id|>\n", + "Write 3-5 emotions, keywords and holidays to describe the following text. ONLY answer with a comma separated list and no preceding statement.\n", + "\n", + "{text}\n", + "<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n", + "\"\"\"\n", + "\n", + "# Background music subworkflow\n", + "music = Workflow([\n", + " TemplateTask(\n", + " template=template,\n", + " action=llm\n", + " ),\n", + " Task(action=tta),\n", + "])\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# \"The Raven\" by Edgar Allan Poe\n", + "\n", + "The first workflow will generate speech and corresponding background music for the first verse of \"The Raven\" by Edgar Allan Poe.\n", + "\n", + "This poem is fitting given that Halloween was close at the time of publishing. 🎃👻🌕" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "DEBUG:txtai.workflow.base:Running Task #0\n", + "DEBUG:txtai.workflow.task.base:Inputs: ['\\nOnce upon a midnight dreary, while I pondered, weak and weary,\\n\\nOver many a quaint and curious volume of forgotten lore—\\n\\nWhile I nodded, nearly napping, suddenly there came a tapping,\\n\\nAs of some one gently rapping, rapping at my chamber door.\\n\\n’Tis some visitor, I muttered, \"tapping at my chamber door— Only this and nothing more.”\\n']\n", + "DEBUG:txtai.workflow.task.base:Outputs: [(array([0.00204471, 0.00245908, 0.00251085, ..., 0.00101355, 0.00124749,\n", + " 0.00157734], dtype=float32), 32000)]\n", + "DEBUG:txtai.workflow.task.base:Inputs: ['\\nOnce upon a midnight dreary, while I pondered, weak and weary,\\n\\nOver many a quaint and curious volume of forgotten lore—\\n\\nWhile I nodded, nearly napping, suddenly there came a tapping,\\n\\nAs of some one gently rapping, rapping at my chamber door.\\n\\n’Tis some visitor, I muttered, \"tapping at my chamber door— Only this and nothing more.”\\n']\n", + "DEBUG:txtai.workflow.base:Running Task #0\n", + "DEBUG:txtai.workflow.task.base:Inputs: ['\\n<|begin_of_text|><|start_header_id|>user<|end_header_id|>\\nWrite 3-5 emotions, keywords and holidays to describe the following text. ONLY answer with a comma separated list and no preceding statement.\\n\\n\\nOnce upon a midnight dreary, while I pondered, weak and weary,\\n\\nOver many a quaint and curious volume of forgotten lore—\\n\\nWhile I nodded, nearly napping, suddenly there came a tapping,\\n\\nAs of some one gently rapping, rapping at my chamber door.\\n\\n’Tis some visitor, I muttered, \"tapping at my chamber door— Only this and nothing more.”\\n\\n<|eot_id|><|start_header_id|>assistant<|end_header_id|>\\n']\n", + "Truncation was not explicitly activated but `max_length` is provided a specific value, please use `truncation=True` to explicitly truncate examples to max length. Defaulting to 'longest_first' truncation strategy. If you encode pairs of sequences (GLUE-style) with the tokenizer you can select this strategy more precisely by providing a specific strategy to `truncation`.\n", + "Starting from v4.46, the `logits` model output will have the same type as the model (except at train time, where it will always be FP32)\n", + "DEBUG:txtai.workflow.task.base:Outputs: ['melancholy, mystery, curiosity, introspection, Halloween']\n", + "DEBUG:txtai.workflow.base:Running Task #1\n", + "DEBUG:txtai.workflow.task.base:Inputs: ['melancholy, mystery, curiosity, introspection, Halloween']\n", + "`torch.nn.functional.scaled_dot_product_attention` does not support having an empty attention mask. Falling back to the manual attention implementation. This warning can be removed using the argument `attn_implementation=\"eager\"` when loading the model.Note that this probably happens because `guidance_scale>1` or because you used `get_unconditional_inputs`. See https://github.com/huggingface/transformers/issues/31189 for more information.\n", + "DEBUG:txtai.workflow.task.base:Outputs: [(array([[-0.01685709, -0.0192524 , -0.01729976, ..., 0.02864039,\n", + " 0.02873872, 0.02577066],\n", + " [-0.02714959, -0.0311739 , -0.02744334, ..., 0.2672284 ,\n", + " 0.266621 , 0.26353633]], dtype=float32), 32000)]\n", + "DEBUG:txtai.workflow.task.base:Outputs: [(array([[-0.01685709, -0.0192524 , -0.01729976, ..., 0.02864039,\n", + " 0.02873872, 0.02577066],\n", + " [-0.02714959, -0.0311739 , -0.02744334, ..., 0.2672284 ,\n", + " 0.266621 , 0.26353633]], dtype=float32), 32000)]\n", + "DEBUG:txtai.workflow.base:Running Task #1\n", + "DEBUG:txtai.workflow.task.base:Inputs: [((array([0.00204471, 0.00245908, 0.00251085, ..., 0.00101355, 0.00124749,\n", + " 0.00157734], dtype=float32), 32000), (array([[-0.01685709, -0.0192524 , -0.01729976, ..., 0.02864039,\n", + " 0.02873872, 0.02577066],\n", + " [-0.02714959, -0.0311739 , -0.02744334, ..., 0.2672284 ,\n", + " 0.266621 , 0.26353633]], dtype=float32), 32000))]\n", + "DEBUG:txtai.workflow.task.base:Outputs: [(array([[-0.00638384, -0.00716712, -0.00613903, ..., -0.05081543,\n", + " -0.05321765, -0.0549943 ],\n", + " [-0.01153009, -0.01312787, -0.01121082, ..., -0.01355931,\n", + " -0.02704029, -0.03997342]], dtype=float32), 32000)]\n", + "DEBUG:txtai.workflow.base:Running Task #2\n", + "DEBUG:txtai.workflow.task.base:Inputs: [(array([[-0.00638384, -0.00716712, -0.00613903, ..., -0.05081543,\n", + " -0.05321765, -0.0549943 ],\n", + " [-0.01153009, -0.01312787, -0.01121082, ..., -0.01355931,\n", + " -0.02704029, -0.03997342]], dtype=float32), 32000)]\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "DEBUG:txtai.workflow.task.base:Outputs: [(array([[-0.00638384, -0.00716712, -0.00613903, ..., -0.05081543,\n", + " -0.05321765, -0.0549943 ],\n", + " [-0.01153009, -0.01312787, -0.01121082, ..., -0.01355931,\n", + " -0.02704029, -0.03997342]], dtype=float32), 32000)]\n" + ] + }, + { + "data": { + "text/plain": [ + "[(array([[-0.00638384, -0.00716712, -0.00613903, ..., -0.05081543,\n", + " -0.05321765, -0.0549943 ],\n", + " [-0.01153009, -0.01312787, -0.01121082, ..., -0.01355931,\n", + " -0.02704029, -0.03997342]], dtype=float32),\n", + " 32000)]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Text to speech\n", + "tts = TextToSpeech(\"neuml/vctk-vits-onnx\", rate=32000)\n", + "\n", + "# Define the workflow\n", + "workflow = Workflow(tasks=[\n", + " Task(action=[lambda x: tts(x, speaker=3), music], merge=\"hstack\", unpack=False),\n", + " Task(action=lambda x: mixer(x, scale2=0.5), unpack=False),\n", + " Task(action=lambda x: [play(y) for y in x], unpack=False)\n", + "])\n", + "\n", + "list(workflow([\"\"\"\n", + "Once upon a midnight dreary, while I pondered, weak and weary,\n", + "\n", + "Over many a quaint and curious volume of forgotten lore—\n", + "\n", + "While I nodded, nearly napping, suddenly there came a tapping,\n", + "\n", + "As of some one gently rapping, rapping at my chamber door.\n", + "\n", + "’Tis some visitor, I muttered, \"tapping at my chamber door— Only this and nothing more.”\n", + "\"\"\"]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is quite amazing 🔥\n", + "\n", + "From a single text verse, we not only generated speech, we also generated spooky background music to go along with it.\n", + "\n", + "The LLM reads the text and writes a series of emotions, keywords and other descriptive words. That is then passed to a music generation model which creates the corresponding background music. Finally, an audio mixer pipeline joins the streams together and the audio is saved for playback.\n", + "\n", + "This is the power⚡ of txtai workflows. Some may call it \"agentic\". Whatever we want to call it, it is able to combine multiple models small and large into a single execution flow." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# \"A Visit from St. Nicholas\" by Clement Clarke Moore\n", + "\n", + "Next, we'll create audio for the classic Christmas tale, also known as \"The Night Before Christmas\" 🎅🎄❄️\n", + "\n", + "We'll use a different voice this time, mine! This is the default voice for [txtai-speecht5-onnx](https://hf.co/neuml/txtai-speecht5-onnx)." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "DEBUG:txtai.workflow.base:Running Task #0\n", + "DEBUG:txtai.workflow.task.base:Inputs: [\"\\n'Twas the night before Christmas, when all through the house, not a creature was stirring, not even a mouse.\\n\\nThe stockings were hung by the chimney with care, in hopes that Saint Nicholas soon would be there.\\n\\nThe children were nestled all snug in their beds, while visions of sugar plums danced in their heads.\\n\\nAnd mamma in her kerchief, and I in my cap, had just settled our brains, for a long winter’s nap.\\n\\nWhen out on the lawn there arose such a clatter, I sprang from my bed to see what was the matter.\\n\"]\n", + "DEBUG:txtai.workflow.task.base:Outputs: [(array([-3.9214676e-05, 1.7410064e-04, 1.9779154e-04, ...,\n", + " -1.0386602e-03, -9.1643957e-04, -4.9463823e-04], dtype=float32), 32000)]\n", + "DEBUG:txtai.workflow.task.base:Inputs: [\"\\n'Twas the night before Christmas, when all through the house, not a creature was stirring, not even a mouse.\\n\\nThe stockings were hung by the chimney with care, in hopes that Saint Nicholas soon would be there.\\n\\nThe children were nestled all snug in their beds, while visions of sugar plums danced in their heads.\\n\\nAnd mamma in her kerchief, and I in my cap, had just settled our brains, for a long winter’s nap.\\n\\nWhen out on the lawn there arose such a clatter, I sprang from my bed to see what was the matter.\\n\"]\n", + "DEBUG:txtai.workflow.base:Running Task #0\n", + "DEBUG:txtai.workflow.task.base:Inputs: [\"\\n<|begin_of_text|><|start_header_id|>user<|end_header_id|>\\nWrite 3-5 emotions, keywords and holidays to describe the following text. ONLY answer with a comma separated list and no preceding statement.\\n\\n\\n'Twas the night before Christmas, when all through the house, not a creature was stirring, not even a mouse.\\n\\nThe stockings were hung by the chimney with care, in hopes that Saint Nicholas soon would be there.\\n\\nThe children were nestled all snug in their beds, while visions of sugar plums danced in their heads.\\n\\nAnd mamma in her kerchief, and I in my cap, had just settled our brains, for a long winter’s nap.\\n\\nWhen out on the lawn there arose such a clatter, I sprang from my bed to see what was the matter.\\n\\n<|eot_id|><|start_header_id|>assistant<|end_header_id|>\\n\"]\n", + "DEBUG:txtai.workflow.task.base:Outputs: ['Peaceful, Hope, Joy, Christmas, Calm, Slumber, Wonder, Excitement']\n", + "DEBUG:txtai.workflow.base:Running Task #1\n", + "DEBUG:txtai.workflow.task.base:Inputs: ['Peaceful, Hope, Joy, Christmas, Calm, Slumber, Wonder, Excitement']\n", + "DEBUG:txtai.workflow.task.base:Outputs: [(array([[ 0.01238994, 0.00964349, 0.02490981, ..., -0.02256315,\n", + " -0.02624696, -0.01479813],\n", + " [-0.0111579 , -0.01307007, 0.00245246, ..., 0.02333916,\n", + " 0.01998244, 0.02509145]], dtype=float32), 32000)]\n", + "DEBUG:txtai.workflow.task.base:Outputs: [(array([[ 0.01238994, 0.00964349, 0.02490981, ..., -0.02256315,\n", + " -0.02624696, -0.01479813],\n", + " [-0.0111579 , -0.01307007, 0.00245246, ..., 0.02333916,\n", + " 0.01998244, 0.02509145]], dtype=float32), 32000)]\n", + "DEBUG:txtai.workflow.base:Running Task #1\n", + "DEBUG:txtai.workflow.task.base:Inputs: [((array([-3.9214676e-05, 1.7410064e-04, 1.9779154e-04, ...,\n", + " -1.0386602e-03, -9.1643957e-04, -4.9463823e-04], dtype=float32), 32000), (array([[ 0.01238994, 0.00964349, 0.02490981, ..., -0.02256315,\n", + " -0.02624696, -0.01479813],\n", + " [-0.0111579 , -0.01307007, 0.00245246, ..., 0.02333916,\n", + " 0.01998244, 0.02509145]], dtype=float32), 32000))]\n", + "DEBUG:txtai.workflow.task.base:Outputs: [(array([[ 5.8028224e-04, 6.5627520e-04, 1.4432818e-03, ...,\n", + " 2.3652171e-04, -6.1154435e-04, 1.3553995e-03],\n", + " [-5.9710984e-04, -4.7940284e-04, 3.2041437e-04, ...,\n", + " -2.5115125e-03, -7.7958062e-04, -9.4321877e-05]], dtype=float32), 32000)]\n", + "DEBUG:txtai.workflow.base:Running Task #2\n", + "DEBUG:txtai.workflow.task.base:Inputs: [(array([[ 5.8028224e-04, 6.5627520e-04, 1.4432818e-03, ...,\n", + " 2.3652171e-04, -6.1154435e-04, 1.3553995e-03],\n", + " [-5.9710984e-04, -4.7940284e-04, 3.2041437e-04, ...,\n", + " -2.5115125e-03, -7.7958062e-04, -9.4321877e-05]], dtype=float32), 32000)]\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "DEBUG:txtai.workflow.task.base:Outputs: [(array([[ 5.8028224e-04, 6.5627520e-04, 1.4432818e-03, ...,\n", + " 2.3652171e-04, -6.1154435e-04, 1.3553995e-03],\n", + " [-5.9710984e-04, -4.7940284e-04, 3.2041437e-04, ...,\n", + " -2.5115125e-03, -7.7958062e-04, -9.4321877e-05]], dtype=float32), 32000)]\n" + ] + }, + { + "data": { + "text/plain": [ + "[(array([[ 5.8028224e-04, 6.5627520e-04, 1.4432818e-03, ...,\n", + " 2.3652171e-04, -6.1154435e-04, 1.3553995e-03],\n", + " [-5.9710984e-04, -4.7940284e-04, 3.2041437e-04, ...,\n", + " -2.5115125e-03, -7.7958062e-04, -9.4321877e-05]], dtype=float32),\n", + " 32000)]" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tts = TextToSpeech(\"neuml/txtai-speecht5-onnx\", rate=32000)\n", + "\n", + "# Define the workflow\n", + "workflow = Workflow(tasks=[\n", + " Task(action=[tts, music], merge=\"hstack\", unpack=False),\n", + " Task(action=lambda x: mixer(x, scale2=0.05), unpack=False),\n", + " Task(action=lambda x: [play(y) for y in x], unpack=False)\n", + "])\n", + "\n", + "list(workflow([\"\"\"\n", + "'Twas the night before Christmas, when all through the house, not a creature was stirring, not even a mouse.\n", + "\n", + "The stockings were hung by the chimney with care, in hopes that Saint Nicholas soon would be there.\n", + "\n", + "The children were nestled all snug in their beds, while visions of sugar plums danced in their heads.\n", + "\n", + "And mamma in her kerchief, and I in my cap, had just settled our brains, for a long winter’s nap.\n", + "\n", + "When out on the lawn there arose such a clatter, I sprang from my bed to see what was the matter.\n", + "\"\"\"]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook demonstrated how to build a series of Generative Audio workflows for poems. This capability has potential applications in the creative field.\n", + "\n", + "Are we at a place where we can have a full pipeline that takes a prompt and generates a full multimedia video? Not quite but we're quite close. Interesting times certainly are ahead!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "local", + "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", + "version": "3.9.20" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/67_Whats_new_in_txtai_8_0.ipynb b/examples/67_Whats_new_in_txtai_8_0.ipynb new file mode 100644 index 0000000..dac71a8 --- /dev/null +++ b/examples/67_Whats_new_in_txtai_8_0.ipynb @@ -0,0 +1,556 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "e3wdiK5fGUoZ" + }, + "source": [ + "# 💡 What's new in txtai 8.0\n", + "\n", + "The 8.0 release brings a major new feature: Agents 🚀\n", + "\n", + "Agents automatically create workflows to answer multi-faceted user requests. Agents iteratively prompt and/or interface with tools to\n", + "step through a process and ultimately come to an answer for a request.\n", + "\n", + "This release also adds support for Model2Vec vectorization.\n", + "\n", + "**Standard upgrade disclaimer below**\n", + "\n", + "While everything is backwards compatible, it's prudent to backup production indexes before upgrading and test before deploying." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "p8BbfjrhH-V2" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "-OXsTQgaGQPM" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[agent] model2vec" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "n4EXrtcYIIYE" + }, + "source": [ + "# Agents with txtai\n", + "\n", + "The biggest change and reason this is a major release is the addition of agents. The following defines a basic agent. This agent has access to two embeddings databases (Wikipedia and ArXiv) and the web. Given the user's input request, the agent decides the best tool to solve the task." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "hzPD8_cQJNtN" + }, + "outputs": [], + "source": [ + "from datetime import datetime\n", + "\n", + "from txtai import Agent\n", + "\n", + "wikipedia = {\n", + " \"name\": \"wikipedia\",\n", + " \"description\": \"Searches a Wikipedia database\",\n", + " \"provider\": \"huggingface-hub\",\n", + " \"container\": \"neuml/txtai-wikipedia\"\n", + "}\n", + "\n", + "arxiv = {\n", + " \"name\": \"arxiv\",\n", + " \"description\": \"Searches a database of scientific papers\",\n", + " \"provider\": \"huggingface-hub\",\n", + " \"container\": \"neuml/txtai-arxiv\"\n", + "}\n", + "\n", + "def today() -> str:\n", + " \"\"\"\n", + " Gets the current date and time\n", + "\n", + " Returns:\n", + " current date and time\n", + " \"\"\"\n", + "\n", + " return datetime.today().isoformat()\n", + "\n", + "agent = Agent(\n", + " tools=[today, wikipedia, arxiv, \"websearch\"],\n", + " llm=\"Qwen/Qwen3-4B-Instruct-2507\",\n", + " max_iterations=10,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32;20;1m======== New task ========\u001b[0m\n", + "\u001b[37;1mWhich city has the highest population, Boston or New York?\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: I will use the tool 'web_search' to find the population of both cities.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'web_search' with arguments: {'query': 'Population of Boston and New York'}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: The results from the web search indicate that the population of Boston is 7,948 and the population of New York is 653,833. However, it seems that the question is asking about the city of Boston in New York, not the city of Boston in Massachusetts. I will use the tool 'wikipedia' to find the population of the city of New York.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'wikipedia' with arguments: {'query': 'Population of New York City'}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: The results from the Wikipedia search indicate that the population of New York City is approximately 8,804,190. However, this is the population of the entire city, not just the city of New York. I will use the tool 'wikipedia' to find the population of the city of New York, not the state of New York.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'wikipedia' with arguments: {'query': 'Population of New York City (borough)'}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: The results from the Wikipedia search indicate that the population of New York City is approximately 8,804,190, and the population of Brooklyn is approximately 2,592,149. Since Brooklyn is one of the five boroughs of New York City, I will assume that the population of the city of New York is approximately 8,804,190. However, this is still not the correct answer to the original question. I will use the tool 'wikipedia' to find the population of the city of Boston in Massachusetts, which is the other city mentioned in the original question.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'wikipedia' with arguments: {'query': 'Population of Boston, Massachusetts'}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: The results from the Wikipedia search indicate that the population of Boston, Massachusetts is approximately 675,647. This is the correct answer to the original question, which was to determine which city has the highest population, Boston or New York. Since the population of Boston is significantly lower than the population of New York City, which is approximately 8,804,190, the answer to the original question is New York City.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'final_answer' with arguments: {'answer': 'New York City'}\u001b[0m\n" + ] + }, + { + "data": { + "text/plain": [ + "'New York City'" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "agent(\"Which city has the highest population, Boston or New York?\", maxlength=16000)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "How about that 🔥? \n", + "\n", + "Look at how the agent went through and systematically solved the problem. The agent ran a series of searches to gather the information it needed and then determined the final answer!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Agentic RAG\n", + "\n", + "Standard Retrieval Augmented Generation (RAG) runs a single vector search to obtain a context and builds a prompt with the context + input question. Agentic RAG is a more complex process that goes through multiple iterations. It can also utilize multiple databases to come to a final conclusion.\n", + "\n", + "The example below aggregates information from multiple sources and builds a report on a topic." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32;20;1m======== New task ========\u001b[0m\n", + "\u001b[37;1m\n", + "You're an expert researcher looking to write a paper on alien life.\n", + "Search for websites, scientific papers and Wikipedia related to the topic.\n", + "Write a report with summaries and references (with hyperlinks).\n", + "Write the text as Markdown.\n", + "\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: I will start by searching for Wikipedia articles related to alien life.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'wikipedia' with arguments: {'query': 'Alien life'}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: The search results from Wikipedia look promising. I will now search for scientific papers related to alien life using the arxiv tool.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'arxiv' with arguments: {'query': 'alien life'}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: The search results from arxiv look interesting. I will now search for websites related to alien life using the web_search tool.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'web_search' with arguments: {'query': 'alien life'}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: Now that I have gathered information from Wikipedia, arxiv, and web search, I will write a report with summaries and references (with hyperlinks) in Markdown format.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'final_answer' with arguments: {'answer': \"## Alien Life: A Report on the Search for Extraterrestrial Life\\n\\n### Introduction\\n\\nThe search for alien life has been ongoing for centuries, with scientists and enthusiasts alike searching for signs of life beyond Earth. While no conclusive evidence has been found yet, there are many promising leads and discoveries that suggest the possibility of life existing elsewhere in the universe.\\n\\n### Wikipedia\\n\\nAccording to Wikipedia, extraterrestrial life refers to life that does not originate from Earth. The search for alien life is an active area of research, with scientists using various methods to detect signs of life, such as analyzing the atmospheres of exoplanets and searching for biosignatures.\\n\\n### Arxiv\\n\\nThe arxiv database has numerous papers on the search for alien life, including studies on the possibility of life existing on exoplanets and the use of astrobiology to detect signs of life. One paper, 'Extraterrestrial Life and Censorship,' discusses the development of the theory of cosmic life and the attempts to censor evidence incompatible with this theory.\\n\\n### Web Search\\n\\nA web search for alien life yielded numerous results, including articles from reputable sources such as NASA and Live Science. These articles discuss the latest discoveries and findings in the search for alien life, including the potential for life existing on exoplanets and the use of astrobiology to detect signs of life.\\n\\n### Conclusion\\n\\nWhile the search for alien life is an ongoing and active area of research, there are many promising leads and discoveries that suggest the possibility of life existing elsewhere in the universe. As scientists continue to search for signs of life, it is possible that we may one day find evidence of alien life.\\n\\n### References\\n\\n* [Extraterrestrial life - Wikipedia](https://en.wikipedia.org/wiki/Extraterrestrial_life)\\n* [What's the best evidence we've found for alien life?](https://www.livescience.com/space/extraterrestrial-life/whats-the-best-evidence-weve-found-for-alien-life)\\n* [NASA Research Gives Guideline for Future Alien Life Search](https://www.nasa.gov/universe/nasa-research-gives-guideline-for-future-alien-life-search/)\\n* [James Webb telescope sees potential signs of alien life in the atmosphere of a distant goldilocks water-world](https://www.livescience.com/space/exoplanets/james-webb-telescope-sees-potential-signs-of-alien-life-in-the-atmosphere-of-a-distant-goldilocks-water-world)\\n* [Alien life in Universe: Scientists say finding it is 'only a matter of time'](https://www.bbc.com/news/science-environment-66950930)\\n* [Extraterrestrial evidence: 10 incredible findings about aliens from 2020](https://www.livescience.com/alien-discoveries-2020.html)\\n* [How Scientists Could Tell the World if They Find Alien Life](https://www.scientificamerican.com/article/how-scientists-could-tell-the-world-if-they-find-alien-life/)\"}\u001b[0m\n" + ] + }, + { + "data": { + "text/markdown": [ + "## Alien Life: A Report on the Search for Extraterrestrial Life\n", + "\n", + "### Introduction\n", + "\n", + "The search for alien life has been ongoing for centuries, with scientists and enthusiasts alike searching for signs of life beyond Earth. While no conclusive evidence has been found yet, there are many promising leads and discoveries that suggest the possibility of life existing elsewhere in the universe.\n", + "\n", + "### Wikipedia\n", + "\n", + "According to Wikipedia, extraterrestrial life refers to life that does not originate from Earth. The search for alien life is an active area of research, with scientists using various methods to detect signs of life, such as analyzing the atmospheres of exoplanets and searching for biosignatures.\n", + "\n", + "### Arxiv\n", + "\n", + "The arxiv database has numerous papers on the search for alien life, including studies on the possibility of life existing on exoplanets and the use of astrobiology to detect signs of life. One paper, 'Extraterrestrial Life and Censorship,' discusses the development of the theory of cosmic life and the attempts to censor evidence incompatible with this theory.\n", + "\n", + "### Web Search\n", + "\n", + "A web search for alien life yielded numerous results, including articles from reputable sources such as NASA and Live Science. These articles discuss the latest discoveries and findings in the search for alien life, including the potential for life existing on exoplanets and the use of astrobiology to detect signs of life.\n", + "\n", + "### Conclusion\n", + "\n", + "While the search for alien life is an ongoing and active area of research, there are many promising leads and discoveries that suggest the possibility of life existing elsewhere in the universe. As scientists continue to search for signs of life, it is possible that we may one day find evidence of alien life.\n", + "\n", + "### References\n", + "\n", + "* [Extraterrestrial life - Wikipedia](https://en.wikipedia.org/wiki/Extraterrestrial_life)\n", + "* [What's the best evidence we've found for alien life?](https://www.livescience.com/space/extraterrestrial-life/whats-the-best-evidence-weve-found-for-alien-life)\n", + "* [NASA Research Gives Guideline for Future Alien Life Search](https://www.nasa.gov/universe/nasa-research-gives-guideline-for-future-alien-life-search/)\n", + "* [James Webb telescope sees potential signs of alien life in the atmosphere of a distant goldilocks water-world](https://www.livescience.com/space/exoplanets/james-webb-telescope-sees-potential-signs-of-alien-life-in-the-atmosphere-of-a-distant-goldilocks-water-world)\n", + "* [Alien life in Universe: Scientists say finding it is 'only a matter of time'](https://www.bbc.com/news/science-environment-66950930)\n", + "* [Extraterrestrial evidence: 10 incredible findings about aliens from 2020](https://www.livescience.com/alien-discoveries-2020.html)\n", + "* [How Scientists Could Tell the World if They Find Alien Life](https://www.scientificamerican.com/article/how-scientists-could-tell-the-world-if-they-find-alien-life/)" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import display, Markdown\n", + "\n", + "researcher = \"\"\"\n", + "You're an expert researcher looking to write a paper on {topic}.\n", + "Search for websites, scientific papers and Wikipedia related to the topic.\n", + "Write a report with summaries and references (with hyperlinks).\n", + "Write the text as Markdown.\n", + "\"\"\"\n", + "\n", + "display(Markdown(agent(researcher.format(topic=\"alien life\"))))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's unpack what just happened here. The Agent reviewed multiple sources and aggregated the references into a single report. This is quite powerful 💪\n", + "\n", + "Note that depending on the LLM used, errors can be seen as the Agent tries to get the function parameters right. This example is using Qwen3 4B. A more powerful LLM will likely lead to even better results. Remember that txtai supports a number of LLM frameworks (Hugging Face, llama.cpp and LiteLLM APIs)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Agent Teams\n", + "\n", + "Can agents also be tools? Yes!\n", + "\n", + "Next, we'll build a similar example but instead use an \"Agent Team\" to answer questions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from txtai import Agent, LLM\n", + "\n", + "# Share the LLM instance across multiple agents\n", + "llm = LLM(\"Qwen/Qwen3-4B-Instruct-2507\")\n", + "\n", + "websearcher = Agent(\n", + " tools=[\"websearch\"],\n", + " llm=llm,\n", + ")\n", + "\n", + "wikiman = Agent(\n", + " tools=[{\n", + " \"name\": \"wikipedia\",\n", + " \"description\": \"Searches a Wikipedia database\",\n", + " \"provider\": \"huggingface-hub\",\n", + " \"container\": \"neuml/txtai-wikipedia\"\n", + " }],\n", + " llm=llm,\n", + ")\n", + "\n", + "researcher = Agent(\n", + " tools=[{\n", + " \"name\": \"arxiv\",\n", + " \"description\": \"Searches a database of scientific papers\",\n", + " \"provider\": \"huggingface-hub\",\n", + " \"container\": \"neuml/txtai-arxiv\"\n", + " }],\n", + " llm=llm,\n", + ")\n", + "\n", + "agent = Agent(\n", + " tools=[{\n", + " \"name\": \"websearcher\",\n", + " \"description\": \"I run web searches, there is no answer a web search can't solve!\",\n", + " \"target\": websearcher\n", + " }, {\n", + " \"name\": \"wikiman\",\n", + " \"description\": \"Wikipedia has all the answers, I search Wikipedia and answer questions\",\n", + " \"target\": wikiman\n", + " }, {\n", + " \"name\": \"researcher\",\n", + " \"description\": \"I'm a science guy. I search arXiv to get all my answers.\",\n", + " \"target\": researcher\n", + " }],\n", + " llm=llm,\n", + " max_iterations=10\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "The comprehensive report on fundamental concepts about Signal Processing is as follows:\n", + "\n", + "### Introduction\n", + "\n", + "Signal representation, filtering, and spectral analysis are fundamental concepts in signal processing. Signal representation involves encoding a signal in a mathematical or computational format, while filtering involves modifying the signal to remove unwanted parts or components. Spectral analysis involves analyzing the frequency content of a signal.\n", + "\n", + "### Signal Representation\n", + "\n", + "Signal representation involves encoding a signal in a mathematical or computational format. This can be done using various techniques, including Fourier analysis and wavelet analysis.\n", + "\n", + "### Filtering\n", + "\n", + "Filtering involves modifying the signal to remove unwanted parts or components. This can be done using various types of filters, including Butterworth filters, Chebyshev-I filters, and Elliptical filters.\n", + "\n", + "### Spectral Analysis\n", + "\n", + "Spectral analysis involves analyzing the frequency content of a signal. This can be done using various techniques, including Fourier analysis, wavelet analysis, and singular spectrum analysis.\n", + "\n", + "### Applications\n", + "\n", + "Butterworth filters are widely used in signal processing and audio processing for applications such as noise reduction and anti-aliasing filtering. They are also used in medical signal processing for filtering ECG and EEG signals.\n", + "\n", + "### Conclusion\n", + "\n", + "In conclusion, signal representation, filtering, and spectral analysis are fundamental concepts in signal processing. Butterworth filters are widely used in various applications, including audio processing and medical signal processing." + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "display(Markdown(agent(\"\"\"\n", + "Work with your team and build a comprehensive report on fundamental concepts about Signal Processing.\n", + "Write the output in Markdown.\n", + "\"\"\", maxlength=16000)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "📚 See the report above. It ran through similar logic as the first agent, except this time it ran with multiple agents! Note that the agent logging output is not included for brevity." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Agents as a service\n", + "\n", + "Agents are fully supported through txtai's application configuration via YAML. These services can be run standalone in Python or as a FastAPI service." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Writing config.yml\n" + ] + } + ], + "source": [ + "%%writefile config.yml\n", + "\n", + "agent:\n", + " researcher:\n", + " tools:\n", + " - websearch\n", + " - name: wikipedia\n", + " description: Searches a Wikipedia database\n", + " provider: huggingface-hub\n", + " container: neuml/txtai-wikipedia\n", + " - name: arxiv\n", + " description: Searches a database of scientific papers\n", + " provider: huggingface-hub\n", + " container: neuml/txtai-arxiv\n", + "\n", + "llm:\n", + " path: Qwen/Qwen3-4B-Instruct-2507" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!CONFIG=config.yml nohup uvicorn \"txtai.api:app\" &> api.log &\n", + "!sleep 90" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'The Roman Empire was a vast and powerful state that existed from 27 BC to 476 AD, covering much of Europe, North Africa, and Western Asia. It was founded by Augustus Caesar and was ruled by a series of emperors, with its capital in Rome. The Roman Empire was known for its impressive architecture, engineering, and administrative achievements, including the construction of roads, bridges, and public buildings. It also had a significant impact on the development of law, governance, and culture in the ancient world.'" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import requests\n", + "\n", + "requests.post(\n", + " \"http://localhost:8000/agent\",\n", + " headers={\"Content-Type\": \"application/json\"},\n", + " json={\"name\": \"researcher\", \"text\": \"Tell me about the Roman Empire\", \"maxlength\": 160000}\n", + ").json()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "💥 Look at that! A full API service from a simple configuration file. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Vectorization with Model2Vec\n", + "\n", + "While the agent framework is the headline change, there is another major update - support for Model2Vec models. \n", + "\n", + "[Model2Vec](https://github.com/MinishLab/model2vec) is a technique to turn any sentence transformer into a really small static model, reducing model size by 15x and making the models up to 500x faster, with a small drop in performance." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\"" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from txtai import Embeddings\n", + "\n", + "# Data to index\n", + "data = [\n", + " \"US tops 5 million confirmed virus cases\",\n", + " \"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\",\n", + " \"Beijing mobilises invasion craft along coast as Taiwan tensions escalate\",\n", + " \"The National Park Service warns against sacrificing slower friends in a bear attack\",\n", + " \"Maine man wins $1M from $25 lottery ticket\",\n", + " \"Make huge profits without work, earn up to $100,000 a day\"\n", + "]\n", + "\n", + "# Create an embeddings\n", + "embeddings = Embeddings(method=\"model2vec\", path=\"minishlab/M2V_base_output\")\n", + "embeddings.index(data)\n", + "\n", + "uid = embeddings.search(\"climate change\")[0][0]\n", + "data[uid]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tvnMO1Eai6Gy" + }, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook gave a quick overview of txtai 8.0. Updated documentation and more examples will be forthcoming. There is much to cover and much to build on!\n", + "\n", + "See the following links for more information.\n", + "\n", + "- [8.0 Release on GitHub](https://github.com/neuml/txtai/releases/tag/v8.0.0)\n", + "- [Documentation site](https://neuml.github.io/txtai)" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "gpuType": "T4", + "provenance": [] + }, + "kernelspec": { + "display_name": "local", + "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", + "version": "3.9.20" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/examples/68_Analyzing_Hugging_Face_Posts_with_Graphs_and_Agents.ipynb b/examples/68_Analyzing_Hugging_Face_Posts_with_Graphs_and_Agents.ipynb new file mode 100644 index 0000000..7f31366 --- /dev/null +++ b/examples/68_Analyzing_Hugging_Face_Posts_with_Graphs_and_Agents.ipynb @@ -0,0 +1,821 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Analyzing Hugging Face Posts with Graphs and Agents\n", + "\n", + "Embeddings databases can optionally build a graph network alongside stored vectors. The graph automatically infers relationships of the content using the computed vector embeddings. This provides an additional way to explore and analyze content. \n", + "\n", + "txtai 8.0 was recently released and added the ability to run agents. Agents automatically create workflows to answer multi-faceted user requests.\n", + "\n", + "This notebook will demonstrate how to use these concepts with the [Hugging Face Posts dataset](https://huggingface.co/datasets/maxiw/hf-posts). If you're not familar with semantic graphs, graph traversal and agents in txtai, then check out the articles below before continuing on. \n", + "\n", + "Articles to review:\n", + " - [Introducing the Semantic Graph](https://neuml.hashnode.dev/introducing-the-semantic-graph)\n", + " - [Advanced RAG with graph path traversal](https://neuml.hashnode.dev/advanced-rag-with-graph-path-traversal)\n", + " - [What's new in txtai 8.0](https://neuml.hashnode.dev/whats-new-in-txtai-80)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[agent,graph] datasets" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Hugging Face Posts\n", + "\n", + "[Hugging Face Posts](https://huggingface.co/posts) is a microblog site. It has over 2,000 unique posts as of November 2024. The next section defines methods to yield records from this dataset.\n", + "\n", + "Additionally, it loads an LLM to infer titles for each post." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from datasets import load_dataset\n", + "from txtai import LLM\n", + "\n", + "llm = LLM(\"Qwen/Qwen3-4B-Instruct-2507\")\n", + "\n", + "def title(text):\n", + " prompt = f\"\"\"\n", + "Create a simple, concise topic for the following text. Only return the topic name.\n", + "\n", + "Text:\n", + "{text}\n", + "\"\"\"\n", + "\n", + " return llm([{\"role\": \"user\", \"content\": prompt}], maxlength=2048)\n", + "\n", + "def hfposts():\n", + " ds = load_dataset(\"maxiw/hf-posts\", split=\"train\")\n", + " for row in ds:\n", + " yield {\n", + " \"id\": title(row[\"rawContent\"]),\n", + " \"text\": row[\"rawContent\"],\n", + " \"author\": row[\"author\"][\"name\"],\n", + " \"date\": row[\"publishedAt\"],\n", + " \"url\": f\"https://hf.co{row['url']}\",\n", + " \"reactions\": sum(x[\"count\"] for x in row[\"reactions\"]),\n", + " \"views\": row[\"totalUniqueImpressions\"],\n", + " \"comments\": row[\"numComments\"]\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, let's build an Embeddings index. We'll store vectors, content and build a graph network. The graph network automatically builds relationships between the records using vector similarity." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from tqdm import tqdm\n", + "from txtai import Embeddings\n", + "\n", + "embeddings = Embeddings(\n", + " autoid=\"uuid5\",\n", + " path=\"intfloat/e5-large\",\n", + " instructions={\"query\": \"query: \", \"data\": \"passage: \"},\n", + " content=True,\n", + " graph={\"approximate\": False, \"minscore\": 0.7},\n", + ")\n", + "embeddings.index(tqdm(hfposts()))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With a modern GPU, this step shouldn't take long. The longest part of the process is running 2000+ LLM prompts to generate titles. Without that, it would only take a few seconds to build the embeddings index.\n", + "\n", + "If you'd like to skip this step, you can instead load a pre-computed embeddings index from the Hugging Face Hub as follows.\n", + "\n", + "```python\n", + "embeddings = Embeddings()\n", + "embeddings.load(provider=\"huggingface-hub\", container=\"neuml/txtai-hfposts\")\n", + "```\n", + "\n", + "Let's run a sample search to verify the index is working." + ] + }, + { + "cell_type": "code", + "execution_count": 135, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'id': 'AI Music Generation',\n", + " 'text': 'Love this new Space built by @enzostvs + @Xenova for Transformers.js: Generate your own AI music (In-browser generation) with AI Jukebox \\n\\nhttps://huggingface.co/spaces/enzostvs/ai-jukebox',\n", + " 'score': 0.8460421562194824},\n", + " {'id': 'Kolmogorov Arnold Networks',\n", + " 'text': 'Transformers are not all we need, that is being proven repeatedly now as more alternative frameworks emerge. Another such framework is Kolmogorov Arnold Network based Transformers. I break down exactly how these differ from Perceptron based Transformers and give you the link to my Colab where I create a model based on the research paper that absolutely destroys a standard Transformers based model. Check out the video here: https://www.youtube.com/watch?v=Sw0euxNZCc4',\n", + " 'score': 0.8424240350723267},\n", + " {'id': 'GitHub Issue 8771',\n", + " 'text': 'This issue is just a treasure ! A bit deprecated i guess, but things are in their historical context. (personally, still need more to understand better)\\nhttps://github.com/huggingface/transformers/issues/8771\\n\\U0001fae1 to the man @stas ',\n", + " 'score': 0.8417709469795227}]" + ] + }, + "execution_count": 135, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "embeddings.search(\"transformers\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Looks good!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Graph Analysis\n", + "\n", + "Next up, graphs! We'll define methods to plot our graphs and subgraphs." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABiIAAALJCAYAAAA57jvpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdeXhU5f3+8ffsM5klCYEEQgIIyCJbpO7ggrZVqVYFxa0WKai4gkuFFpdqsRZoK19B0dbWra2Ke/GH2mLjXhURkH2TfQmQZDIzmX35/REyEhIggWzA/bouLpIzZ855ZiFhnvs8n4+hqKgohYiIiIiIiIiIiIiISBMwtvQARERERERERERERETk6KUgQkREREREREREREREmoyCCBERERERERERERERaTIKIkREREREREREREREpMkoiBARERERERERERERkSajIEJERERERERERERERJqMgggREREREREREREREWkyCiJERERERERERERERKTJKIgQEREREREREREREZEmoyBCRERERERERERERESajIIIERERERERERERERFpMgoiRERERERERERERESkySiIEBERERERERERERGRJqMgQkREREREREREREREmoyCCBERERERERERERERaTIKIkREREREREREREREpMkoiBARERERERERERERkSajIEJERERERERERERERJqMgggREREREREREREREWkyCiJERERERERERERERKTJKIgQEREREREREREREZEmoyBCRERERERERERERESajIIIERERERERERERERFpMgoiRERERERERERERESkySiIEBERERERERERERGRJqMgQkREREREREREREREmoyCCBERERERERERERERaTIKIkREREREREREREREpMkoiBARERERERERERERkSajIEJERERERERERERERJqMgggREREREREREREREWkyCiJERERERERERERERKTJKIgQEREREREREREREZEmoyBCRERERERERERERESajIIIERERERERERERERFpMgoiRERERERERERERESkySiIEBERERERERERERGRJqMgQkREREREREREREREmoyCCBERERERERERERERaTIKIkREREREREREREREpMkoiBARERERERERERERkSajIEJERERERERERERERJqMgggREREREREREREREWkyCiJERERERERERERERKTJKIgQEREREREREREREZEmoyBCRERERERERERERESajIIIERERERERERERERFpMgoiRERERERERERERESkySiIEBERERERERERERGRJmNu6QGIiIiIiIiIHItiVhchTwFhdz4Ji4OUwYQhlcAUC2H3b8Ph24IlGmjpYYqIiIgcNgURIiIiIiIiIs0k5OpAaacz8OX2I25zV21MJjCQSu+TwgBGEwDmiB/PziXkbPocR2B7SwxZRERE5LAZioqKUgffTUREREREREQORQrw5fZl13FDCGZ1gWQiHTTUy579M8o30G5DMZ6dSzE01WBFREREmoCCCBEREREREZEmErO62XLC5fjz+kIyCcbDaNW4J5BwlyylYPlrWKL+xhuoiIiISBNSECEiIiIiIiLSBLx5/dnSZwRJk7VhKyAOJpnAmIhSsGw2WSXfNt5xRURERJqIgggRERERETmiqMGvHAl2dT6L7b0ugVQSDIexCmJ/9hy3w8q3aLfxk8Y/voiIiEgjUrNqERERERFp9dTgV44k6RACmiaE2Ou423tdCqAwQkRERFo1rYgQEREREZFWSQ1+5UjkzevPpqKRzX7eToueV5kmERERabW0IkJERERERFqdWg1+oeE19vfsH8wsZOOJo9TgV5pczOpmS58RTVeOaX9SSbb0GYGz/DuVJRMREZFWqRn/ZyQiIiIiInJw3rz+rBo8AX+73lUbjIf5sWVPIOFv15tVgyfgzet/mCMUqS0FbDnh8qrG1M0ZQgAYjCRNVraecDkqeSAiIiKtkYIIERERERFpNXZ1PotNRSNJmm0NXwFxMEYTSbONTUUj2dX5zMY9thzzfLl9q1bwNPb7tr6MJnx5/fDl9m2Z84uIiIgcgIIIERERERFpFZq7wa/CCGlMu7oM+b6MWEtJJtjV5ZyWHYOIiIhIHRREiIiIiIhIi/Pm9f8+hGgm23tdqjJNLeSxxx7j1ltvbelhNJqQqwPB7C6HX0bscBlNBLOPI+Rq36SnOf/885kzZ06TnkNERESOLgoiRERERESkRdVo8Nuc9jT4jVldzXveY8CECRMoLi6u9Sc/P7+lh9YkSjudAclE+vvrBuTwzrU9WDe+P+vG9+eNq7ozsENG8wwmmaC00yAAbrjhBp5//vkaNxcWFlJcXMyECRNqbD///PN5//33sVqtjTYUs9nMlVdeydNPP83cuXOZM2cOzzzzDL/4xS/IyclJ77f3++Xf//43f//73/n5z3+OcU+wc6Dgo7i4mEGDBjXamEVERKRpmFt6ACIiIiIicuxqLQ1+Oy96DkPznv2o9+WXXzJlypQa2yoqKhrl2EajkVQqRSrVOloz+3L71egNMaiTmzeWl/HV1koi8SR3nNae167szqBnVrA9EGvawRhNVX0ilr/OokWLuOaaa8jOzqa8vByAE088kZKSEoqKimrc7cQTT2TFihVEo9FGGYbFYmHatGl07dqV5557jqVLl+L1eunQoQPnnnsul112Gc8880x6/+r3i9Vq5dRTT2XcuHHE43H++c9/Nsp4REREpGUpiBARERERkRaTbvDbUvZq8Ju5c2nLjeMoFIvF0pPfB+Nyubj99ts5/fTTsVgsLF68mBkzZrB161ag6or42267jUcffZQbbriBwsJCrr32WsrKyhg9ejTnnnsuLpeLDRs28PTTT7N48eIa93v44Ye59dZbyc3NZcmSJUyZMoWysrL0+S+88EJGjBhBfn4+fr+fjz/+mMcffxwAp9PJzTffzKBBg7BYLKxatYonn3ySdevWVT1Oq4u4zV3j8Yyds6HG9+Pe3cjFPQdwVhc3rywtY39GndiWW0/Jo6PHwiZvlD9+voPZy77fv3TiQMbN3ciPu2cy5DgP2/1RHvjvVt5bWzPgids8xK0ulixZQiwWo6ioiOLiYgCKiop46623+NnPfkZeXh4lJSUADBgwgPfeew+oChEO9LxWGzRoEGPHjiU3N5fFixczbdo0du3aBcDll19O3759GTt2LGvXrk3fZ+fOnbWOAzXfL//6178YPHgwZ5xxhoIIERGRo4RKM4mIiIiISItRg18BmDhxIj169GDSpEncdtttGAwGfv/732Myfb/KwGazcfXVV/OHP/yBUaNG4fV6ueOOO+jTpw+//e1vGTNmDB9++CFTp06lY8eONe43YsQIfve73zFu3Dhyc3O5+eab07f/9Kc/Zdy4cbzzzjuMHj2aSZMmpQMQgN/85jdkZWUxYcIEbrrpJtasWcMf//hH3O6q8CHkKTjo48uwGDEbDZSH4vvd5yc9MvndDwt48qsSBj+zgucW7WbGTzozuFPN0mH3Du7AWyvKOeuvK5j3nY+nL+5Clt1U63hBTwHh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OcJ5PB5OOukk3nrrrf2Wv9mfsWPHAnD33XczbNiwGs1mG1PHjh0555xz+PWvf82ECRPofnwPpv24U73v/+uzOtCzrZ0rZ6/j9GeW88v3N1MaqppovfGkdlzQPYvRb33HqX9ezk1zNrCpInqQI37PYzPx1tXHs6QkxHUTpzBhwgSys7P329MAYPXq1SSTSS688EKMRiNOp5Mf//jHLFiwoNZV5NWGDh3KggUL9juhbTab+dGPfsS7775b77EfDbZu3cqnn37KPffcQ9++fenWrRu//vWv2b17d60QbW9btmzhP//5D7/61a8488wzad++Pb169eKaa67htNNOq/f5n3/+eUaMGMGwYcPo2LEjxx9/PJdddhkACxYsYNmyZUyePJmTTjqJvLw8+vTpw+jRo9OT4KZYiBQ1lyTccWoevzqzA3e8u5FNFVFynWZynWaclv1/BJ/5ZQlX92vDqBPb0jXbxs0n53JRzyye+Kru98uBpDBgin+/emHRokUMGTIEq9XK6tWr09sXL17MZZddRigUSgcK9X1eY7EYd9xxB71796ZHjx5MmDCBZcuWpY/z6quvsnz5cv74xz8yfPhwjj/+eNq3b8/JJ5/MqaeeWmPFw8Fs2LCB+fPnc++99zJw4MD0ccaPH89///tfdu/effCDiIiISIvSiggRERERkSNcx44dMRqNtRrDvvXWW1it1vTXe5cJqub1eoGqK/P3Ldl03HHHMXfu3EYZo9Vq5dFHH01PGD7y0jyeHH81D/x3Czsr4we5N3T0WFlSEkxfWb55r6ChwGPlu/IwX2ypWn2wxRcF6l+q5YYftGNJSYjJH26mvd9E7vq1TJ06lVdffZWCgoI6r7besWMH9957Lw888AB33XUXJpOJpUuXMnHixDrPkZOTw6mnnsrkyZP3O47BgwfjcrlqrJg4VkyZMoXbb7+dRx99FLPZzLfffsvEiRP3G+rsfb/rrruOm2++mbZt21JRUcHy5cv53//+V+9zv//++1itVi6//HLGjh1LRUUFH3/8cfr2iRMnMmbMGO69916ysrIoKyvj22+/Tf97sfu31SoxNmpgW2xmI89d1rXmeD/dztRPt9c5jrlrKvj1vC3cekoev/thAZu8UW7/fxvTq3YaxGiqGtceCxcuZOTIkXz55Zc1AoDFixczatQovvrqqxrPdX2e10gkwksvvcSk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4Ze//GWtsj0NNWfOHDp16sSNN95IQUEB55xzDhdccEH69mAwmJ7Qdrvd6SvSt27dyg9+8AP69OlDp06duOuuu2qELbFYjJdeeombbrqJH//4x+Tn59O7d2+GDh0KwLx586ioqGDy5Mn069eP9u3bM2DAAG6//Xbatm3b8AeSqj2B+l15BKvJyA0ntaNzppURfdpwfVHDjt0p08r9Z+dzUr6TAo+Vc7q46Zptr9EnYl8zvyzh6n5tuH5gLh0yjJx77rmccsop/Otf/8Ln8+H1eoGqVQqhUIhIJEIqlUpfpW6323G5XHg8HqxWK1arlbZt29KuXTtycnIwm83pgHXVqlWsXLmSRx55hNNPP52CggL69+/PmDFjaoUGe+vduzfXXnstPXr0IDc3lzPPPJPMzEw2btxY5/6hUIi5c+dy8803c8YZZ9C+fXv69+/PpEmTWLZsWbr3R7VLLrmEwYMHU1hYyPjx43G73elySf/617/o2LEjN910E4WFhZx33nk13nNw8PcXVJWb6t27N3l5eXg8HgwGA2+99RZut5v777+fnj17kp+fz8knn8y99957SL1bWjuFDiIiIlIfKs0kItIIfLl98ef1bbkBGE348vrhy+1L5s6lB99fRJpE9coHm82GxVJ1xXP1BEw0GlXoIC1q06ZNTJ8+nWuvvZaf//znfPzxx8yePZuLLroovc9LL71EmzZtmDhxIslkknfeeYevv/66xmThk08+yV133cXvfvc7gsEgL7/8Mrm5uUSj0bpOWy87duzgN7/5DTfffDPDhw9n2bJl/P3vf+euu+5KH7f672QySVZWFoFAgBdffJEOHTowdepUwuEw77zzDp999ll69QTAiy++SCKRYNSoUeTk5FBaWsqcOXOAqtJN48aN46abbuLhhx8mIyODXbt2sXDhwnQZoIYwJqJgyKixbdnOEJM+2MK4U/O4/+yO/G+zn99+tI1ZF3ep93FDsSTH59i5qm8bsh1mSipj/PWbXTy3cPd+7zN3TQW/nreFW0/tQMEPb6Jkxw6efPJJvvrqqxolq6LRaPqxVveFSKVS6RJCBoOBSCSCxWKhoqIivToilUqlwyez2czvfvc7rr32Wu655x48Hg9er5cVK1ZgNBpp27YtDocDk8mEx+MhlUqRTCZJJBIUFRVx+eWXk5GRQUlJCU899RRfffXVfh/XjBkz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MHnyZEpKSmo9/3JsUvggcuTx+/3EYjFcLhdmsxmfz9di5Xccge1klG9o+d5RqSSeym0kS9Yd0nMRjUYJhUK4XC5isVirKH11OBpakulgJaKMRmM6uDhYuaj6rLhojPerQodjS8zqZssJl+PP6wvV75+GXhy1Z/9gZiEbTxyFu2QpBctfwxL1N/JoRUREFESISAMkk0lmzJjB9u3byc/PZ/z48YwdOzY9KT1u3DjMZjPjxo0jHA7TuXNnQqEQu3bt4oEHHuDhhx/muuuuo7Kykmg0Wuc5ysrKGDBgAHl5efudGP7hD3/IqFGjePzxx1mzZg3HH388d999N+FwmPfffz+93+jRo3nqqafYsmULo0eP5v777+faa69l2bJlzJw5k+uvvz5d1zoUqgoB7rjjDrp06cJvf/tbSktLGTx4MFOnTuUXv/gFW7duBcBmszFixAh+97vfEcjszAP33slD5xYwds4GAEad2JbfnlvAwx9t5YN1Pjw2E6cUONPjevbS4wjFk1w5ex2+SIKRRW1586rjOeXPy/CGG/4h/6zObvzRBMNeWZvelnC15W9/+xubN28mKyuLW265hQkTJvCrX/2qxn1Hjx7NrFmz8Hq93Hnnndx7773pK2EnTZrEmjVreOyxx0gmk3Tv3p1EIsGyZcu47rrrePHFF3nggQdYunQpfr+fHj168MADD/D8889TXFxMnz59GD9+PD6fr8brMmLECF544QWef/55APr164fNZmPYsGH89re/JSMjg4cffpjf/va3BAIBJk6cSIcOHXjooYdYunRpOjyq72t19dVX84c//AGfz4ff7+eZZ57hnXfeYfLkyZjNZnr37q2JZVH4IHIUCIfDtUo17e//G02t3YZiNp44qkXOnWYw0m598WFNOAcCASwWC263O30xw7GiOhhoaLmoAwUYe6+4qE+5qAOtuKg+nkKHY5M3rz9b+owgabJWbTjc0HNPIOFv15tVgydQsGw2WSXfHuYoRUREalIQISK1nH766TVWJHz55Zc89NBDvP769+V+SkpK+Otf/8pdd92VDiJyc3P5+OOPWb9+PQDbt39fa7S6JEB5efkBe0Q8//zzPPzww7z88sts2rSJ5cuX8+WXX/LRRx+lJwKvv/56Zs2axSeffALAjh076Ny5MxdddFGNCe/Zs2fzxRdfAFUrHp577jk6duzI5s2b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QYQQQgghhDhtHC88qOhXUdXKCq1Wi0qlOqZfRXWrKaRfhRAnR6vVYjQaI0HD8couOZ1OAoEABoMBtVodKd90Ojvy/UqI09mff/7Jrl27uPTSSyNBxJGGDBnCq6++ym+//caiRYtwu90sW7bsmG0qlYrp06ezbds2JkyYQDAY5I477mD69OncddddkZsh+vTpg8vl4pFHHgGo9XFpaWmUlJTw0EMPkZKSwpNPPsmuXbv46quvePLJJ3nvvfdYvnw5y5cvr/ax6nQ6ft6ymylbTfiCYW5NbcXHQ7ty4btb2W/318vz+eQVyTz54362FGbjDYSIVivZfMDFzF8LcXiDXHNGLG/d0Il9Vi9/FLgixw285ioWL1rIuHHj6NWrF5MnTyYzM5ONGzdy2WWXMXToUJ577jn27dtHq1at6Nq1a/n1nnySsWPH0qlTJ5588snI8zVlyhRSUlKYOnUqLpeLsWPH8tJLLzFq1KjIvxlRUVHcdtttvPLKK9jtdqxWKwBDhw7l/fffZ/78+dxyyy089thjbN26la+//pq3336bsWPH8thjjzF69GigPOCZPn0677//PtOnTycuLo4JEyYwYcIEpk+fHnmMw4YNY/78+ZGAZciQIVxyySU888wzFBUVER8fT0JCQr28DkK0NBJECCGEEEIIcciR/Sr8/qr/WD+6ufbRKytqaq5dXWAhhKgsJiYGvV6Px+PB6XTWOtDzeDz4/f5KpZqqKllyupAVEUIclpOTQ5cuXar8WkXY6Xa7I6spPB7PMduuuuoqlEolL7/8cuTYadOm8eWXX5KWlsbvv/8eOfbll1+OTJjX9jin08nMmTMJhULk5uayfv16+vTpw1dffYXD4SAUCuFyuY4pK3Wk3bt3sy7UkQNnnAVKFS+uLuD6s+K47ow43vujfppTv/17Ect3Witte/O3osjH724sZkBnEzd2Nx8OIsJhduQVM3/+fAD279/PTTfdRJ8+fdi4cSOJiYmUlpayceNGgsEgRUVFbN9e3t/O4XDg8XgIBAKRx56SkkK/fv0YP348W7duBeA///kPixYton///vz0008AaDQaXn/9dXbv3l1pvOvXr+fLL78EYN68edx4443s2LEjctyCBQv473//i9lsxmKxMGLECFauXBnpz7F//35mzZrF66+/zowZMyK/N/75558sXrw4cp3ExET2799PRkYGwEmV+RKipZMgQgghhBBCiDqoTXPtimDi6MBCrVZHtlfVXLumlRVCnA6USiUmkwm1Wo3T6Tyh5rDBYBCLxYLRaCQ2NhaXy3VaNzWVEEKIcvWxQqhr166kpKQcs6pCq9WSnJwc+XzPnj2VSkXW9rh9+/ZV+je/pKSEzp0712mM0dHRTBo+kP59Ukk0aFApFejUSlJM2jqdpyabjljlAKBUwEMXt+Wm7maSjBo0KgVRKiVu/+HHogC27y+pdFxpaSlmsxmAVatWcfPNN/PJJ5/w22+/sX79etauXVvt70AdO3YkEAjw119/RbbZ7XZyc3Pp2LFjZJvP5zsmhIDy16hCRbhR1baKIKJr16506dKFq666qtJ5VCoVSUlJ5OTkALBjx45KX//mm294+eWXmT9/Phs2bGDdunWR4EmI040EEUIIIYQQQtSz2vSPqKr8k1KprFVz7eqabAvREPxaA25TOzzGZIIaHWGFCkU4iMrvJtqRj86eh8Z38j0ZNBoNJpOJcDiM1Wo9qQaf4XAYu92OTqcjJiYmUqrpdJuUVygUp91jFqI6HTp0oKCg4KTOodPp2LlzJ88///wxX7PZbJGPK3oG1PW4o9/3wuFw5OaFiiClYgVmxTaNRgOAXq9HoVBw//33k9rnLKauymOPzY/HH2bOPzqjVdVfqTaXv/LvHA9cmMg95ycw9Yc8thW7cflC/OeqdsdcMxCq/H4UDocjj6u4uJg777yT8847j/PPP5+JEydy6623MnHixJNaPVrdqriq/o05clvFe2fF+HQ6HcuXL4+siDhSUdHh1SBHv/ZZWVmMGDGCCy+8kD59+vDUU0+xceNGnn766To/FiFaOgkihBBCCCGEaAK1CQ+qCioq+lVUbK9wZHPt6gILmZAUteU2JFHS4RLsCWcTiDKWbwwFUXD4eyiMApQqANReB6aiDFrnrEXnrPtEX0Vg4Pf76zUwcLvdBAIBjEZjpFRTdWXXTkUSRAhRrnfv3nTt2pUlS5ac1HmysrIYMGAAVqsVl8tV475HBgh79uzhyiuvxO1243a7I9sr/jMajWg0GjQaDXFxcZFjdTodGo2GNm3aAOW/OxgMBuLi4iLXiY6ORqFQEB0dTTgcpmfPnixb/xcrdioJK9XEaJR0iNXyy0k98ppd0M7A11lWFm8tBcpXP3RtFcXOg5Un5TnO+5HP52PdunWsW7eOzz//nPnz59OlSxeysrKO2Tc7Oxu1Wk2PHj0ipZlMJhPt27dn37599fGwKsnKyqJjx47k5+fX+ViXy0V6ejrp6en8/PPPTJ8+HaPRiMNRuwbiQpwqJIgQQgghhBCimTpeCaij+1Uc+fHx+lVUt7JCJi1PX2HAnpBKcecBuOI6QSgYCRoAUKqo7rsjEGWkNOVCSttfgt6yj/h96ZiKMjne/bcVE3BRUVGUlZUdd2LvRPj9fiwWCyaTidjYWMrKyk6o5FNLJEGEOB1pNBrMZjMqlQqz2cwFF1zAiBEjWLt2Ld99912Vxxy52kCtVlcKCdRqNTqdDoVCwbp167jtttt48cUXWbBgASUlJSQkJHDxxRfzxRdfYLFYiI6OJioqitatW0fOv3HjRm699VaeffZZFi1aRElJCW3atOHCCy/k//2//8fBgweB8n+nA4EA4XCYcDiM3+8nGAxis9kIh8MUFBRwxhlnoFAo8Hq92Gw2HA4H4XCY0tLyECA7O5srU8/i04KDhJUqHrs0CWUDN67fU+rh793M9E2JweYJcl/fBBL0GnZyOIgIA8pw9Svdrr32WlQqFdu2bcPr9XLVVVfh8Xiq7amwf/9+1qxZw8MPP8yrr76K2+1mzJgxHDx4kF9+qf/YZcGCBbz55ptMmDCBr776Co/HQ6dOnTjvvPOYOXNmtcfdcsstlJSUkJWVRTgc5vLLL6ekpASn8+RXEgrR0kgQIYQQQgghRAtVm34VCoWiysbaKpWqyrCiIpioaWWFOPX4tUbyeg7FkZgKFSt1jgwhauPQ/q7Y9mT3Ho2xMJN225ag8VV9x6darcZkMqFQKLDZbA3aVDocDmOz2YiJicFgMKDRaCKTd6cyCSLEqezIFQcV/6lUKi688EI+++wzAoEATqeTvXv38u6775Keno7RaKxU5shoNBIfHw8QCRwqehYAkVWIer0+Eg48/vjj3HHHHUyZMgWdTkdJSQmbN2+mtLQUl8uF3+/H7/dHwoOKkP+BBx5g7NixTJo0Cb1eT3FxMX/++ScFBQWR4yrGXCEQCBAKhSLvj++//z6TJk3iww8/RKvVMmDAgGOel//+979MevwZvh7Zk1JXgJnrD2CMquP7eR29uvYAneKiWDLsDFyBEPM3HWRFlhXTkddVKFD5qu/X43Q6GTFiBPfddx8qlYo9e/YwdepU7HZ7tcdMmzaNBx54gBdffBG1Ws2WLVuYMmVKg/yusmfPHiZOnMhdd93FzJkzUSgU5Ofnk56eXuNxLpeL4cOH065dO4LBIDt27GDKlCny3ixOS4q0tDT5zhdCCCGEEOI0dvRqiqNLQdXUr6K6j0XLYU08h7xewwiptHUPH2oSCqIM+mi39VPiCrdU+lJ0dDQGg4FAIIDdbm/U7xmtVovRaIyEE6dyuGY0GlGpVFit1qYeihCRAKCqAOFE/qtORWBwMv9VhActdbLYrzXw14BnmnoYx+iZ/hTqeugpJIRomWRFhBBCCCGEEKe5ihChpubAVYUTFSWgoqKiqu1XUd3KCgkrmofijpdR0P1GCIdAoTz+AXWhVBFSRJGTNhL/9s+Jz14NgMFgQKfT4Xa7m6Q0hc/ni5RqMpvNOJ3OY5qLnipkRYQ4GUdO+tdHeFCTIyf+qwsEahsgCND4nKi9jsM9fpoBtdcuIYQQpzkJIoQQQgghhBDHdbzwoGKiqqrAQqvVolKpjulXUd1qCulX0TgiIQTUfwhR4dB5C7rfhEKh4ExbBiqVCrvdjtfrbZhr1kIoFMJqtWIwGDAajajV6lOyXrcEEaeXoyf+TzY8qEl1IUFdg4OWvOqguTMVZVCacmH9rnQ7UaEgpqLMph6FEKKJSRAhhBBCCCGEOGlH9qvw+/1V7nN0c+2jV1bU1Fy7usBCnBhr4jmHQ4hGkt/tRuKyAkRlr282r53T6cTv92M0GtFoNNhstlPujmqZ5G2+agoBTiREqEl1YYEEB6eu1jlrKW1/SVMPo5xSReuc+m8gLYRoWSSIEEIIIYQQQjSK2jTXrggmjg4s1Gp1ZHtVzbVrWlkhKvNrjeT1GtYw5ZhqEg6xvdMguuVtQRNsPqsPvF4vgUAgUqrJ4XA0aOPsxiQrIurX8YKAuoYHNamu7JAEB6K2dM4C9JZ9uGI7gLIR3+uPFgqit+Wgcx5oujEIIZoFCSKEEEIIIYQQzUZt+kdU11hbrVYft7l2dU22TxdhIK/n0PLG1I0ZQgAolIRUWvb3HErHTXOpeRq2cQWDQaxWK0ajkdjYWFwuF2VlZU09rJN2ugcRdQkFahMi1KSmECAYDEpwIJpE/L50snuPbtpBKFXE71vVtGMQQjQLEkQIIYQQQgghWpTahAdVBRUqlQqtVhvZXuHIO42rCyxOlYlBe0IqjsTUphuAUoU98WzsCanENrN64eFwGLvdjk6nIyYmBrVajcPhaNFBVUsLIupaiuh44UFNjtfwuK4rD4RojkxFmRgLM3HE92iaXhGhIKbibdIfQggBSBAhhBBCCCGEOAUdrwTU0f0qjvz4eP0qqltZ0RImI4s7DYBQqMnLdBR3uqLZBREV3G43fr8/UqrJbrdX2/ekuWvoIOJEmyBXFyDU5HjBQV0bJgtxOlAA7bYtYUf/yYQUUY1ejk8Z9JGybUmzWgEnhGg6EkQIIYQQQgghTju16VehUCiqbKytUqmqDCsqgomaVlYczWw28+9//5tevXoRDAa54YYbGuTxArgNSbjMnRrs/LWmVOEyd8ZtaNtsa4YHAgEsFgsmk4nY2Fguvvhixo4d26CvT0M4enL/RIOD6gKEmtS1VNHxwgMhxInR+By02/opOWkjG/fCCiXttn6Kxtd8egIJIZqWIi0tTf5FF0IIIYQQQpzS0tPTa/z63LlzmTdvXp3Pe/RqiqNLQdXUryIUCnHnnXfSt29fnn76aRwOB6WlpXUeQ23l9byZ0pQLG6w8xxcjziSz0M3UH/KOv3MoyNnubax6biR33303u3fvbpAxnYgFCxawZMkSli5dCoBerycuLg6VSkVeXl6jToqfbHCg0WgIhUInHRzUtBIBqDJEEEI0L8UdL6Wg+02Ndr2k7Z8Tn7260a4nhGj+ZEWEEEIIIYQQ4pQ3ZMiQyMdXXnklo0aN4s4774xsc7vdlfZXKpW16g1QESgEAoFq96kqnKgoAdWuXTuys7NxuVyoVCratGlz3JUVR49LrVbXeP0K9oSz6yWEUCshcLJtE5QqnK27nfRY6nTJWr6mR3O5XAQCAYxGY6RU0/Fe75MNECpWH9SkNqWKNBoNPp+PQCBw3BUIQohTW0UoUND9JgiHGqZM06HzJv/1OW1yJIQQQlQmQYQQQgghhBDilGexWCIfO53OStvOPfdcXn/9dSZPnsxdd91F586deeSRRyguLmbcuHH06NEDnU5HdnY27777Ln/88UfkXAsWLGD58uWkpKRw+eWX43A4+Oijj1i+fDlQHhKMGzeOyy67DKPRSGlpKV9++SWffPIJCxYsoG3btgBcfvnlfPfdd8yYMYPExETuu+8+zjnnHMLhMJs2bWLOnDnYbDYAhg4dSt++ffnqq6+45ZZbiI+P5/rrr+frr79mxowZXHTRRfTu3ZvCwkKmT5+O1Wrl4UencFaPnmwtcnPf8n3ss/oij+G6M2N5pF8S3dpEc8DpZ2FGCa+tPUDw0Nx0yZQ+PPxtDn/rYuKyjkZm/1bE9DUFx33O/7yvF/M3HaSzOYobu5mxeoK8uraA+ZtLAPh94kUAvPfeewBs2rSJhx56CIBBgwYxbNgwkpKSOHDgAJ999hlffPFF5Ny9evVi4sSJdOjQgb179/Lhhx/y/PPPR1ZX1OU1fe+99/jzzz9RKpW88sortG3blvHjxzN+/HgArr/+eq666irGjBnDqFGjiIuLIxgMcs0113DTTTfRunVrioqK+Oyzz1i9+vDE26effsrbb79Nnz59OPfccyktLWXevHls2LChzmWJqtrveBQKBTqdDq/Xi8/nO+7+QohTX3z2ajQeG3m9hhFSaet3hVwoiDLoo93WT4kr3FJ/5xVCnDIkiBBCCCGEEEIIYOzYsbz11lsUFBTgcDhISEhg/fr1vPfee/j9fq655hpeeOEF7rzzToqKiiLH3XLLLcyZM4ePPvqIyy+/nIkTJ7J582Zyc3MZMmQIl1xyCc888wxFRUXEx8eTkJAAwL333stjjz2Gy+Vi1qxZ+Hw+vF4vU6dOxe12M3HiRFQqFQ8++CD3338/Dz/8MCqVCp/PR9u2bbn44ot56aWXUCgUGAwGAO68807mz5/PggULuP3223nyySc5cOAAc7/5hV9/UzFzUAemXd2eWxeXl0K6qF0M/72+E4+tzGVdrpPO5iheG9gBgJd/Ody/4dH+STy3aj9TV+YRqMPd8+MuSOTFn/OZsbaQv3eP45VrO7A218muUi9Xzd3OylHdmTRpEnv37o2sMrjqqqsYPXo0M2fOJCsrizPPPJNJkybh8Xj49ttviYmJ4YUXXmD9+vW89NJLkeAGIDo6Gr1ej06nizzHc+bMoaioiLKyMhITE8nIyGDJkiUEAgGuuOIKXnjhBR588EFKSkp4/fXXefnll/nhhx9YuXIl4XCY6Oho1Go1CoWCQCCASqXi4osv5q677uLdd9/lzz//5IILLmDcuHHk5uayefPmSFAwZMgQ/ve///HGG28wZMgQJkyYwPDhw3E4HCf0PVoXFaWYZLWDEOJIcYVbiLHsJa/nUByJqRAKnlwgceh4U/E2UrYtkZ4QQohqSRAhhBBCCCGEEMCcOXPYuHFj5HOHw1Gpd8GcOXO49NJLueSSS/j8888j29evXx+5W3/BggUMHTqUtLQ0cnNzSUxMZP/+/WRkZABQWFgYOc5ms+H3+/F6vZHVGeeddx5dunThtttuo7i4GIAXX3yRuXPncsYZZ7Bjxw4CgQBqtZrnnnsuskqiwtdff80333yDSqXi008/5dVXX2Xx4sWszvWyN+Tind+LmTWoY2T/R/sn8cavB1iYWd6bItvm48XVBTx9RXKlIGLptlI+yah7/4qVu2188OdBAN74tZB7+ybQv4ORXaVeDrq8APh8Pnw+HwqFAqPRyD//+U/mzZtHRkYGCoWCrVu3smLFCv7xj3/wxx9/cPXVVwPlfT38fj92u53ly5dz7733otPp0Ol0qNXqyOtREQyEw2EsFgs7duyIfJ6VlcV5551Hr169+OKLLygtLSUQCHDw4EF27doVeRxlZWWEw+HI8z1o0CB++uknVq5cid1uZ9euXZx55pkMGTKEDRs2RI775ptv+OGHH4DylR8333wz3bt3r7RPQ5EgQghRHY3PQadNc7AnpFLcaQAuc6e6BxKH9tfbcojftwpTUSY1d6IRQpzuJIgQQgghhBBCCGDHjh2VPo+OjmbUqFFcdNFFtG7dGpVKhVarJTExsdJ+e/bsqfS5xWLBbDYD5RPRL7/8MvPnz2fDhg2sW7eO33//vdoxdOzYkaKiokgIAZCdnY3D4aBjx46RMRYWFh4TQgDs2rUrUoYnPz8fgMzMTJzhM1CEQxSX+dFplBi1Shy+EL0SdFyQYuChS9pGzqFSKNBplOjUCtyB8knsTQWump+8amwtqtx7o6jMT5uY8j9DFYfmx6OiooiKiiIUCqHVaklKSmL8+PGMGzfu8JhUKsrKyrDb7bRu3Zo9e/ZQVFQUCRQqnlOLxUJJSUlkxcGff/6J3W6PnKe617RNmzaV+j4cb/K+ffv2kfJbcXFxOBwOMjMzK/UigcrfGx6PB6fTGfneaGgSRAghaqIAYosyiS3KxG1IoqTDJdgTUglEmcp3CAVRcPj9I4wiElSovXZMRZm0zlmLznn8Un1CCAESRAghhBBCCCEEcGzD6vvuu4/zzjuPt99+m/379+P1ennmmWcid9tXOLpxcTgcjkwCZ2VlMWLECC688EL69OnDU089xcaNG3n66adPaqwej6fK7VVNpgcCAcLq8smjiimlivHFaFRMW1PA8h3WY68RODwB5fKfWHdqf6jyJHg4DEpFxT2z5V+zWq2UlpavtqiYpH/llVfYtm1bpWNDoRBerzfSuLs2wcGJvqa1EQ6HsVqtGI1GYmNj0Wq1x+xTVVNrhaJx7hmWIEIIUVs6ZwHtti2FbUsJaA24TO3wGJMJqnWElSoUoSCqgJtoRz56ex5qKb8khDgBEkQIIYQQQgghRBVSU1P59ttvWbNmDVB+N31Fc+m6cLlcpKenk56ezs8//8z06dMxGo1V9gnIzs4mISGB+Pj4yKqIjh07YjQa2bdv3wk/FkU4WOX2LYUuzmgVxV6r94TPfaL8h7phq1SHS4FYLBaKi4tJSkpi5cqVVR6Xm5vL1VdfjUajwe/3A9C9e/daXbM2r6nf70epVNZ4npycnMi57HY7Op2O1NRU8vPzUSqVhEInFtzUp8YKPIQQpxa1z4np4HZMB7c39VCEEKeYmn+7EkIIIYQQQojTVF5eHpdeeildu3ala9euPP7443We3L3lllu48sorad++Pe3atePyyy+npKQEp7Pqu0k3btzInj17mDp1KmeeeSbdu3fnscceY9OmTezcufOEH4vK7y4vq3GUl38p4NbU1jzSry3d2kRzVuto/tHDzL8vTTrha9VWUVkAt9dP3759MZvNxMTEAOW9H0aMGMGQIUNo164dnTt3ZuDAgdxyyy0A/PDDDygUCiZNmkSHDh3o27cvw4YNq9U1a/OaHjhwgHPPPZc2bdpgMpmqPM/ChQu59tpr+fvf/05KSgqDBw/mwgsvZNmyZZjNZjQazUk8M/VDVkQIIYQQojmRFRFCCCGEEEIIUYX//ve/PProo8yePRubzcbChQsjk+W15XK5GD58OO3atSMYDLJjxw6mTJlS4+Tw448/zoQJE3jjjTcIhUJs2LCBmTNnntRjiXbkV9mENH2vg9uW7OKRfklMuKgtgWCYrFIPH24+eFLXq42gQsUrHyxi7JAbGD16NBkZGTz00EOsWLECr9fLrbfeyj333IPH42Hv3r0sWbIEKH9Op06dykMPPcS7777L3r17mT9/Pk888USkP0Z1avOazpkzh0mTJvHxxx+j1WoZMGDAMef55ZdfmD17NsOGDWP8+PEUFBQwbdo01qxZg8lkIjY2tv6eqJMkQYQQQgghmgNFWlqa/FYihBBCCCGEEKcwv9bAXwOeaephHKNn+lP1Umv8qquu4tFHH2Xw4MHHDSMag16vJyYmBq/Xi8PhaJIwIDo6GoPBwMGDDR8qCSGEEEIcj6yIEEIIIYQQQohTnMbnRO11EIgyNvVQItRe+wmHENdccw35+fkcPHiQrl27MnbsWFatWtUsQggoX7URCAQwGo2YzWbsdnuVjasbkkKhkNUQQgghhGg2JIgQQgghhBBCiNOAqSiD0pQLqyzR1OhCQUxFmSd8eKtWrRg9ejStWrWipKSEVatW8f7779fjAE+ez+fDYrFgMpmIi4vD6XTi8Xga7foSRAghhBCiOZHSTEIIIYQQQghxGnAbksjq93BTDyPizF9eRuc80NTDaBQGgwGdTofH48HhcDTKNWNiYtBqtVgslka5nhBCCCFETZRNPQAhhBBCCCGEEA1P5yxAb9kHoVDTDiQURG/Ze9qEEABOpxO73U5UVBRmsxmVquFXpciKCCGEEEI0J1KaSQjRrPm1BtymdniMyQQ1OsIKFYpwEJXfTbQjH509D009NDgUQgghhDgdxO9LJ7v36KYdhFJF/L5VTTuGJuD1egkEApFSTQ6Ho0F7WigUigY7txBCCCFEXUkQIYRodtyGJEo6XII94ezDDRVDQRQcvqMrjCJS31jtdWAqyqB1zlp0zoKmGLIQQgghRItgKsrEWJiJI75H0/SKCAUxFW87qf4QLVkwGMRqtWIwGIiNjcXlclFWVtYg15IVEUIIIYRoTqRHhBCiWQgD9oRUijsPwBXXCULBuv1xfGh/vWUf8fvSMRVlIveACSGEEEIcy681sqP/ZELqKFA0YrXecAhlwEu3NS/JilYgOjoag8FAIBDAbrcTqueSWSaTCQC73V6v5xVCCCGEOBHSI0II0eT8WiP70kaT3Xs0LlOH8o11vUPv0P6u2PZk9x7NvrTR+LXGeh6pEEIIIUTLp/E5aLf108YNIQAUStpt/VRCiEM8Hg9WqxWlUonZbEaj0dTr+WVFhBBCCCGaEwkihBBNypp4Djv6Ty4vDwCgPMm3pUOBhCO+Bzv6T8aaeM5JjlAIIYQQ4tQTV7iFpO2fN+o1k7Z/Tlzhlka9ZnMXCASwWCwEAgFiY2PR6/X1dm4JIoQQQgjRnEgQIYRoMsUdLyMnbWR5WYD6rlGsVBFSR5GTNpLijpfW77mFEEIIIU4B8dmrD4cR4fotCxRx6LzJf31OfPbqhrlGCxcOh7HZbLhcLvR6PSaTqV4aTUsQIYQQQojmRIIIIUSTKO54GQXdbyz/pKHKAhw6b0H3mySMEEIIIYSoQnz2ajpsmocy4C3vuVWfQkGUAS8dNs2jTY6EEMfjcrmw2WxoNBrMZjNqtfqkzidBhBBCCCGak5P7zUYIIU6ANfGcwyFEIynofhMaj03KAQghhBBCHCWucAsxlr3k9RyKIzG1PJA4mdWqh443FW8jZdsS6QlRB36/H4vFgslkIi4uDqfTicfjOaFz1ceqCiGEEEKI+iJBhBCiUfm1RvJ6DStfpt+YDRLDIfJ6DSPGskf+GBZCCCGEOIrG56DTpjnYE1Ip7jQAl7lT3QOJQ/vrbTnE71uFqSgTmQqvu1AohNVqxWAwYDQa0Wg0OByOOp9HVkQIIYQQojmRIEII0WjCQF7PoYRU2sYNIQAUSkIqLft7DqXjprnyR7EQQgghxFEUQGxRJrFFmbgNSZR0uAR7QiqBKFP5DqEgCg5PbIdRRIIKtdeOqSiT1jlr0TkLmmD0px6n04nf78doNKJWq7Hb7QSDtS+fJUGEEEIIIZoTRVpamvxmIoRoFLaEVLJ7j27qYdDxzznEFmU29TCEEEIIIVqEgNaAy9QOjzGZoFpHWKlCEQqiCriJduSjt+ehlhWnDUalUmEymVCpVDgcDrxeb62Oi4+Px26313p/IYQQQoiGJCsihBCNprjTAAiFQNnIqyGOFApS3OkKCSKEEEIIIWpJ7XNiOrgd08HtTT2U01IwGMRisWA0GjGZTLhcLsrKymo8pqI/hKyIEEIIIURz0YSzgUKI6qSnp9OvX78a95k8eTLPPfdcI43o5LkNSeW1hhsphBh0Ziwb7ulJ0aO9+c/f2h3+glKFy9wZt6Fto4xDCCGEEEKI+uBwOHA4HOh0OuLi4lDW8Hu1BBFCCCGEaG5kRYQQJ2ny5MkMHDiQZcuWMWPGjEpfe/DBB7npppv45ptvmDZt2gmdPzExkYULF3L33Xeze/fuyPbZs2fX+hz/+te/GDRoEM899xw//fTTCY3jZI24dyIDzu/OFXNqvpNOp1bwcL8kbuxuJsmowekLsuOgh7c2FPF1lq3W13t1YAcWbCnhfxuLcfqCzL6+I7FRKu74bA+EgpR06Ee7bUtP9mE1iPT09Bq/PnfuXObNm9dIoxFCCCGEEM2Fx+MhEAhgMpkwm83Y7Xb8fv8x+0kQIYQQQojmRoIIIepBYWEhV155JW+++SY+nw8AjUbD3/72Nw4cONAg1zzecuwKUVFRDBgwgIULF3Ldddc1WRDhjUmo1X6vDuzAeUkxTPk+lx0lHlpFq7ignQGzrvZvVzEaJQkxGn7ca+eA89g/zFCqsCekQiMEESqVqk5NBQGGDBkS+fjKK69k1KhR3HnnnZFtbre73sYnhBBCCCFalkAggMViwWQyERsbi8vlwuVyVdpHggghhBBCNDcSRAhRD7KyskhOTuayyy5j5cqVAFx22WUUFRVRUFBQad8FCxawZMkSli49PAn+7rvvsmbNmirvcl+4cCEA7733HgCbNm3ioYceYvLkyRgMBp544okax3bFFVeQnZ3NggULWLx4MfHx8RQXF0e+rlQquf/++7nmmmsIBoOsWLGCVq1aERMTEzm3QqHgtttuY/DgwbRq1Yq8vDzmz5/Pzz//DMC5557L66+/zqRJkxg7diwdO3Zk165dTJ8+ndzcXK66/kYeuaITACVT+gAw/qt9LMgoPWa8A8+I5d8r81i5xw5Arg02F1aeeI+NUvHi1e249oxYtCola3McPLYyjz0WL/06GFg24iwAvjj0/zU5Dvp3MFa6/t8/2cm/rnwey8EiZs6cCcD999/P0KFDufPOO8nNzUWtVrNs2TIef/xx/vjjD/r27csdd9xB586dCQaDbNu2jdmzZ5Ofnw8cXr3y7LPPcuONN9KjRw9ee+01vv32WwYNGsSwYcNISkriwIEDfPbZZ3zxxRdVvmYWiyXysdPpPGbb8c41duxY+vfvT3x8PKWlpaxcuZL58+dHApGRI0fSv39/PvvsM0aOHInJZOK7775j5syZDBs2jFtuuQWFQsHSpUv5+OOPqxyjEEIIIYRoOuFwGJvNhl6vR6/Xo9FosNvtkeChIogQQgghhGguJIgQop58/fXXDBw4MBJEXHfddXz99dekpaWd1Hnvvfde3n77bSZNmsTevXsJBAJ1Ov66665j5cqVlJWV8dtvvzFw4EA+/PDDyNdvu+02/va3vzFt2jSys7O5+eab6devH5s2bYrsM2LECK6++mpmzJhBXl4e55xzDlOnTsVms7F58+bIfnfddRdvvfUWVquVhx56iEcffZQHHniAr/7cQ+z6Qv7WxcSQhVkA2L1VrxIoKgtwVddYlu+04vSFqtznzcEd6WKO4vYlu3H4Qjx1RTILb+nKJe9t47e8Mi54Zyu/3dOLkZ/t4bf9Ttz+EDOu64hRq+SBFdkAWNxBfnPnccuVF0TOe+6552K1WklLSyM3N5fu3bujVqvZunUrADqdjsWLF7N79250Oh2jR4/m2WefZcyYMZXuNhszZgxvvfUWWVlZ+Hw+rrrqKkaPHs3MmTPJysrizDPPZNKkSXg8Hr799ts6vZ61OZfL5WLatGkcPHiQLl268PDDD+N2uyOhFkBycjIXXHABkydPJjk5maeffpqkpCTy8vKYOHEivXr1YvLkyfzxxx/89ddfdRqjEEIIIYRoHC6XC7/fX6lUUyAQkBURQgghhGh2pFm1EPXk+++/5+yzzyYxMZHExERSU1P5/vvvT/q8VqsVAJvNhsViweFw1PrYlJQUevbsyY8//hgZ48CBAyvtM2TIED755BPWrFlDbm4uM2fOjNyFD+Ulpm6//XamT5/Ohg0bKCgo4Ntvv+X777/nhhtuqHSu999/n82bN0dWYKSmpqLRaLBFxVPmCxAIhSkqC1BUFsATqPqPooe+yeGClBiyHjyHlSO78fzfUrggJSby9S7mKK47M44Hv87h17wytha5uWfZPpKMWgadGYc/FKbYVR7WWDzl13L4QngCIXzBw9f3BwKs3WelY8eOxMbGYjAY6NixI0uXLo2ER+eeey47duzA6/UC8PPPP7N69Wry8/PZvXs306dPp2vXrnTs2LHSY1i6dCmrV6/mwIEDlJaWMmrUKN56663IttWrV7NkyRIGDx5c69eyQm3O9dFHH7F161YKCwtZt24dixYt4oorrqh0HoVCwfTp08nOzmbdunVs2rSJ9u3bM3v2bHJzc/nmm2/Iyck56SBNCCGEEEI0LL/fj8ViIRQKERcXR3R0dORrEkQIIYQQormQFRFC1BObzcavv/7Ktddei0Kh4Ndff8VutzfKta+66ir+9a9/RT6fPHkyGRkZXHfddWzYsCEyjvXr1/PII4/Qp08f/vjjD2JiYmjVqhXbtx9uIB0Khdi5cydKZXlOmZKSgk6n45VXXql0TbVaza5duyptO7KZdklJCQBms5kcjY7aLg5fl+ukz9uZnJ8cwwUpBi7rZOSe/0vgpdUFvLr2AGe1jsYfDLMx/3CPDIsnyK5SD2e1iYYdtbuOgjDbCx04HA7OPfdcAoEAu3btYt26ddx0001AeRBx5MqQlJQURo8eTY8ePYiNjY08R4mJiezbty+y344dhwcRHR1NSkoKjzzyCA8//HBku0qlqhT41EZtzzVgwACGDBlCcnIyOp0OlUp1TE+RwsLCSr0mKv54PfKPVYvFgtlsrtMYhRBCCCFE4wuFQlitVmJiYjAajZEG1hJECCGEEKK5kCBCiHr09ddfM2HCBADeeOONKvcJhULH1GxVq0/uR/GXX35h27Ztkc8PHjyIUqnk2muvpVWrVpFyUVA+aX3dddfxxx9/1OrcOp0OgMcee6xSbwkg8gdOhSPLRh1ZnzasUNXp8QRC8GteGb/mlTFzfSGTLmnLw/3aMvPXwjqd53jCShWbN28mLS0Nv9/Ppk2b2LNnDxqNhk6dOpGamsqnn34a2f+FF16gsLCQV199NfIcz5kz55jX78gJ/orn79VXX630GkH590Jd1OZcPXv2ZOrUqcyZM4cNGzZQVlbGlVdeybBhwyrtf3SJr3A4XOU2qS8shBBCCNFylJWVEQgEMBqNhMNhVCpVpE+YEEIIIURTkiBCiHr022+/RSalN2zYUOU+NpuN1q1bRz7X6/W0bdu22nNWTA6rVNVP5rvd7kqT3wAXX3wxer2esWPHVvrjo3PnzkyePJmYmBjKysooLS2lW7dubNmyBShvXn3WWWdFVjvs27cPn89HQkJCpX4QdaEIB/EFQ6hOcFJ7x0EPaqWCaLWCnSUeNCoF5yXHsGF/+V3+5mgVZ7SKZsdBT7Xn8AXDKJWVr68IBdmyZQvXX389fr+f9957j3A4zJYtWxg+fDgajYbMzEwATCYTHTp04JVXXiEjIwOA1NTU447dYrFQXFxMUlJSpUDoRNTmXL169eLAgQOVmkwnJiae1HWFEEIIIUTL4fV6UalU6PV6zGYzDocjUmpUCCGEEKKpSBAhRD0KhUKMGjUq8nFV/vjjDwYOHMjatWtxOp2MHj26xjvjLRYLHo+Hvn37UlxcjM/nO6bMTlUGDRrEr7/+WqlcEkB2djb3338/V199NZ9//jmfffYZt99+O/n5+eTk5PCPf/wDg8EQWdHgdrtZtGgR999/P0qlkoyMDGJiYkhNTcXlctWq2bLK7ybH6qNDnJbUBB35Dh9OX3nPhqN9MeJMPttmYVNBGaWeIN1aR/P45cmsyXbg8IVw+Lys2Gnl9YEd+Ne3OTh9IZ68PJkCh4+vs6zVjiHX5uXKzkbOaBVFqTuA3R1CFXCzadN2xo0bRyAQiAQMmzZt4r777mP79u14POXhhsPhwGazMXjwYEpKSkhMTGTMmDHHfewAc+fO5YEHHog0DNdoNHTr1g2j0cjixYtrdY7anmv//v0kJiYyYMAAduzYwUUXXUT//v3rdA0hhBBCCNHyhUKhSCNrt9td57KgQgghhBD1SYIIIeqZy+Wq8euffPIJSUlJvPDCC5SVlTFnzhySkpKq3T8UCjFr1izuvPNORo8eTUZGBg899FCN1zCbzVx00UU8//zzx3wtHA6zZs0arrvuOj7//HMWLFhAq1atmDJlCqFQiOXLl/P7779XWkXxwQcfYLPZGDFiBElJSTidTrKysirddV+TaEc+X2bZGbzHzhcjziQuWs34r/axIKP0mH3T99gZntqKxy9PRqdWcsDp57vdNl5eUxDZZ/xX2bx4dTsWDO2KRqVkXa6D4Yt3E6ih0tH8TSX062Dkh5HdMUSp+PsnO9m/MZ89JXtwOp3k5eVFQodNmzahUqkq9YcIh8M8++yzPPDAA8yZM4fc3FxmzZrF66+/ftzHv2LFCrxeL7feeiv33HMPHo+HvXv3smTJklo9f3U519q1a1myZAkPPvggGo2GX3/9lQ8//DASkAkhhBBCiFOfQqEgHA7jcDjw+/0YDAbUajV2u73O5UGFEEIIIeqDIi0tTbpXCSEiFAoFc+fOZdWqVcyZM6dezunXGvhrwDP1cq761DP9KdQ+uTNMCCGEEEKcWgwGAxqNBovFApT3pDOZTCgUCux2+zG93oQQQgghGpqsiBDiNJeYmMj555/P5s2b0Wg0/OMf/yApKYkffvih3q6h8TlRex0Eooz1ds6TpfbaJYQQQgghhBCnpIoVERUCgQAWiwWj0UhsbCwul+u4K7mFEEIIIeqTBBFCnOZCoRADBw7k3nvvRaFQsHfvXh5++GFycnLq9TqmogxKUy4EZfVNtxtNKIipKLOpRyGEEEIIIUSDOTKIqPjcbrej1+vR6/VoNBrsdvsx+wkhhBBCNAQpzSSEaBRuQxJZ/R5u6mFEnPnLy+icB5p6GEIIIYQQQtQ7k8kEgN1ur/LrGo0Gk8kUCScCgUBjDk8IIYQQpyFlUw9ACHF60DkL0Fv2QVM3xwsF0Vv2SgghhBBCCCFOWUeXZjqa3+/HYrEQCoWIi4tDp9M14uiEEEIIcTqSIEII0Wji96WDsonfdpQq4vetatoxCCGEEEII0YCOF0RAeYlWq9WK2+3GYDBgNBpRKBSNNEIhhBBCnG4kiBBCNBpTUSbGwkwIBZtmAKEgpsIM6Q8hhBBCCCFOabUJIiqUlZVhs9nQarXExcWhUjWDnm5CCCGEOOVIECGEaDQKoN22JSiDPgg3commcAhl0EfKtiXIfV5CCCGEEOJUVteVDT6fD6vVCoDZbCYqKqoBRiWEEEKI05kEEUKIRqXxOWi39VNQNPLbj0JJu62fovE5G/e6QgghhBBCNLK6rIioEAwGsVgseL1eTCYTBoOhgUYnhBBCiNORBBFCiEYXV7iFpO2fN+o1k7Z/Tlzhlka9phBCCCGEEE3hRIKICg6HA4fDQXR0NHFxcSibusebEEIIIU4J8huFEKJJxGevPhxGNFSZpkPnTf7rc+KzVzfMNYQQQgghhGiGTjSIAPB4PFitVpRKJWazGa1WW48jE0IIIcTpSIIIIUSTic9eTYdN81AGvPXfwDoURBnw0mHTPNrkSAghhBBCCCFOHyezIqJCIBDAYrHg9/uJjY1Fr9fX0+iEEEIIcTpSN/UAhBCnt7jCLcRY9pLXcyiOxNTyQEKpOvETHjreVLyNlG1LpCeEEEIIIYQ4rVQ0qj7ZIKLiHHa7HZ1OR0xMDBqNBrvdXi/nFkIIIcTpRZGWlia/QQghmlwYsCekUtxpAC5zp7oHEof211v2Er9vFaaiTBQNNVghhBBCCCGaKaVSSevWrbFarfj9/no7r0ajwWQyRcKJQCBQb+cWQgghxKlPVkQIIZoFBRBblElsUSZuQxIlHS7BnpBKIMpUvkMoiILDuWkYRSSoUHvtmIoyaZ2zFp2zoAlGL4QQQgghRPNQnysijuT3+7FYLJhMJuLi4igrK8PtdtfrNYQQQghx6pIVEUKIZi2gNeAytcNjTCao1hFWqlCEgqgCbqId+ejteail/JIQQgghhBAAqNVqzGYzFoulwVYtxMTEoNfr8Xq9OBwOKdUkhBBCiOOSFRFCiGZN7XNiOrgd08HtTT0UIYQQQgghmr2GWhFxpLKyMvx+P0ajkbi4OOx2O8FgsMGuJ4QQQoiWT9nUAxBCCCGEEEIIIUT9aIwgAsDn82G1WgEwm81ERUU16PWEEEII0bJJECGEEEIIIYQQQpxiGqNcUjAYxGKx4PV6MZlMGAyGBr+mEEIIIVomKc0khBBCCCGEEEKcIhprRcSRHA4Hfr8fg8GAWq3GbrcTCoUa7fpCCCGEaP5kRYQQQgghhBBCCHGKUCgUTdI82uPxYLVaUSqVmM1mtFpto49BCCGEEM2XrIgQQgghhBDiJPi1BtymdniMyQQ1OsIKFYpwEJXfTbQjH509D43P2dTDFEKcJpoqiAAIBAJYLBaMRiOxsbG4XC7KysqaZCxCCCGEaF4kiBBCCCGEEKKO3IYkSjpcgj3hbAJRxvKNoSAKDk/+hVGAUgWA2uvAVJRB65y16JwFTTFkIcRpoimDCCgvCWW329HpdMTExERKNTXlmIQQQgjR9BRpaWny24AQQgghhBDHEQbsCakUdx6AK64ThIKRoKFWDu2vt+wjfl86pqJMFA01WCHEaSsmJoaoqChKS0ubeihoNBpMJhPhcDjSR0IIIYQQpycJIoQQQgghhDgOv9ZIXs+hOBJTIRQC5Um0WjsUSBgLM2m3bQkan6P+BiqEOO0ZDAY0Gg0Wi6WphwKAUqnEaDSi0WgoKyvD7XY39ZCEEEII0QQkiBBCCCGEEKIG1sRzyOs1jJBKW7cVEMcTCqIM+mi39VPiCrfU33mFEKc1g8GAWq3GarU29VAqiYmJQa/X4/V6cTgcUqpJCCGEOM2cxK1cQgghhBBCnNqKO15GTtpIQuqo+g0hAJQqQuooctJGUtzx0vo9txDitNXUPSKqU1ZWhs1mQ6PREBcXh0pVz++pQgghhGjWJIgQQgghhBCiCsUdL6Og+43lnyga6NfmQ+ct6H6ThBFCiHrRXIMIAJ/Ph8ViIRwOYzabiYqKauohCSGEEKKRSBAhhBBCCCHEUayJ5xwOIRpJQfebsCae06jXFEKceppzEAEQCoWwWq14PB5MJhMGg6GphySEEEKIRiBBhBBCCCGEEEfwa43k9RoG4VDjXjgcIq/XMPxamZQTQpy45h5EVHA6ndjtdqKjo4mLi0OplOkJIYQQ4lQm/9ILIYQQQghxSBjI6zm0vDF1Q5Vjqo5CSUilZX/PoTT/KUQhRHPVUoIIAK/Xi8ViQalUYjab0Wq1TT0kIYQQQjQQCSKEEEIIIYQ4xJ6QiiMxtf4bU9eWUoU98WzsCalNc30hRIunUCiaegh1EgwGsVgs+P1+YmNjiYmJaeohCSGEEKIBSBAhhBBCCCHEIcWdBkCokUsyHS0UpLjTFU07BiFEi9WSVkRUCIfD2O12nE4nOp2O2NjYFheoCCGEEKJmEkQIIYQQQohmIz09nX79+p3UOSZPnsxzzz1X5+PchiRc5k5QyzrlZ7aK4ts7urH/4TRWje5e5+sB9OtgoGRKH0xRR6zAUKpwmTvjNrQ9oXOeqGuvvZYvv/yyUa8phGgYLS2IqOB2u7HZbKhUKsxmMxqNpqmHJIQQQoh6om7qAQghhBBCiNOD2Wzm//7v/7joooto06YNVquVXbt2sXTpUv744496u87s2bNP6LiSDpdAKFjrskyTL03G5Q9y4f+2UeYLHnu+KX1qPH7amgJ+yXFU/cVQkJIO/Wi3bWmtxtJYJk+ejMFg4IknnmjqoQghqtESV0Qcye/3Y7FYMJlMxMbGUlZWhtvtbuphCSGEEOIkSRAhhBBCCCEaXGJiIrNmzaKsrIx33nmHPXv2oFar6du3Lw8++CAjR46st2uVlZWd0HH2hLPr1Buic5yW73bbybP7qvx6j1lbIh/f1MPMY/2TufDdrYfH6QuRlqSv+uRKVXmfiGYWRAghmr+WHkRA+YoOm81GTEwMBoMBjUaDw+Fo8Y9LCCGEOJ1JECGEEEIIIRrcxIkTAbjvvvvweDyR7fv27WPFihWV9o2NjeXZZ5+lb9++HDx4kLfeeou1a9cCoFQqmTRpEr1796ZVq1YUFhaybNkyli49PGF/9F37M2bMYM+ePfh8PgYNGkQgEGDZsmXMmzcvcoxfayAQZYx8rgAm9WvLyHPb0FqvZmeJh2dX5fPjXjtweLVDWlIMj/ZPYtqaAqavKaj0OIrKApGPHd4gYcKVth0pra2ep65I5qw2OjILXTywIptdpSYCWgNqn5O///3vDBs2jISEBAoKCvjoo4/4/vvvgfKQZ+HChdx9993s3r0bgJiYGJYvX87EiRPZvHkzAJdccgn33XcfCQkJbN26lW+//ZYpU6YwePDgSuFN3759uf/++0lISCAjI4Np06ZRWlrKyJEjGThwIFBeQqvida04vxCi6VX0VThVJuzLysrw+/0YjUbMZjN2u51AoOr3USGEEEI0b9IjQgghhBBCNCij0cgFF1zA559/XimEqHD0CoaRI0eyatUq7rrrLtavX8/UqVMxGstDAoVCQXFxMU8//TSjRo3iww8/5K677uKKK66ocQzXXHMNbrebcePG8c4773DnnXdy3nnnRb7uNrWrtP89fRO4v28iT6bv57IP/iJ9r52Ph3ahizkKKF/t8Fexm9nrC+kxawtvri88kacmYuplyTzx436umrudQDjMzEEdAXCZ2tG/f3/Gjx/P4sWL+ec//8ny5cuZPHkyaWlptT5/27Ztefrpp1mzZg133303X375JXfdddcx+0VFRTFs2DBeeOEFHnzwQRISErjvvvsAWLRoEenp6axfv54hQ4YwZMgQtm7desw5hBBN51QLIgB8Ph8Wi4VQKERcXBzR0dFNPSQhhBBCnAAJIoQQQgghRINKSUlBqVSSk5NTq/2/+eYbfvzxR/Lz83nvvffQ6/V0717eDDoYDDJ37lx27tzJgQMHWLlyJd98881xg4g9e/Ywf/589u/fz3fffceOHTvo0+dwDwePMbm8P8Qh4y9IYOb6A/y/vyzsKvXyzKp8Mgvd3Ht+AlC+2iEQClPmD1FUFqDMH6rjs1LZf37OZ22ukx0lHt5YV8iF7QxEKUJ4jMnceuutfPvtt3zxxRfk5eWxePFiVq9eza233lrr899www3k5ubyzjvvkJubS3p6Ot98880x+2k0GmbMmMHOnTvJysri888/jzxPHo8Hr9cbqd9usVjkzmQhmpmKIOJUEwqFsFqteDwejEYjBoOhqYckhBBCiDqS0kxCCCGEEKJB1XVibM+ePZGPPR4PTqcTs9kc2XbTTTdx3XXXkZCQQFRUFGq1ml27dtX6nAClpaWVzhnU6FAQJgwYtUqSjFrW51VeqbF+v5PUhGp6OpykrUWHG7EWlvkBiI9R41Xr6NChA8uXL6+0f2ZmJkOGDKn1+du3b8+OHTsqbdu+ffsx+7ndbvLz8yOfl5SUEBcXV+vrCCGa1qm4IuJITqczUqpJo9Fgs9kIhU4uCP7/7d15XFT1/sfx12wM6wgqoIigUa6Ypi3+0izb1G63xdJsc8ncumaWpZZlN2/lVuk1bdHKpUXL7FqZxc3Cm7ZpVq65K4IgIALDNsMwM78/iEkE3AGX9/Px6JFz5pzv+c6ZMzCcz/l8PiIiIlIzFIgQERERkWqVkpKCx+MhJibmuNav7C77sotrXbt2ZejQobz++uts3ryZwsJC+vTpQ8uWLU9oTK/XWy5A4jUcf5Pq6uDy/HXRsOz6odEA3uNonl12wfHw12M2n9zXfLfbXe6x1+vFaFQStcjZ4lwPRAA4nU5KSkqw2WyEhYWRl5dHcXFxbU9LREREjkF/VYiIiIhItcrLy2Pt2rXcdtttldb2DgoKOu6x4uPj2bx5M59++ik7d+4kNTWVqKioU56jwfvXBfi8Yg9pecVcEV1+Xlc0CmbbwaIjN61WBo+bffv2ER8fX255fHw8SUlJAOTk5ABQr1493/MXXnhhufWTk5Np1qxZuWXNmzc/4fmUlJQoMCFyFjiXAxFQGjTNycnB5XJRp06dE/o9IiIiIrVDf0WIiIiISLX797//jdFo5PXXX6dLly40atSImJgYevbsyaxZs457nP3799OsWTMuu+wyoqOjGTBgwEldUD+SyVWEl78yCl79OZ0RVzTgthZhXFjXyviro4iPDODNXzJPeV/Hy2swEmjysnTpUrp168btt99OTEwMvXr14qqrruLDDz8EShu5bt68mbvvvpuYmBjatm1boRH1559/TkxMDIMHDyY6OpprrrmG7t27n/CcDhw4wAUXXEDjxo2x2WyYTLWbSSIi5Z0PGRFlvF4vdrud/Px8AgICqFOnjgKlIiIiZzCVZhIRERGRapeWlsbgwYO57777GDZsGHXr1iU3N5ft27czbdq04x7n888/58ILL2T8+PF4vV6+/fZbPv30U6644opTmp9/XiocVgZp9i+Z2Kwm/nVtI+oHmdl20MG9H+9md7bzlPZzQgxGgovS+e23PcybN4/evXvz0EMPkZGRwaxZs9i7dy8hISG43W6mT5/OyJEjfc2o33zzTV566SXfUAcOHOCf//wnw4YN44477mDz5s289957PPbYYydU0mTZsmW0bduWN954g8DAQEaOHMn69eur49WLyEk4nwIRZYqKinC5XL5STXa7HZfLVdvTEhERkSMY2rVrd/58QxERERERqYTLL5g/uj5X29OooFXis5iL8wEwGo2YTCZMJlO5f5c9LuP1enG73bjdbjwej+/fZY/LLlDee++93HLLLdx111218tpE5PTz9/cnODiYgwcP1vZUapzBYMBms2GxWCgoKKCoqGZL6YmIiMjRKSNCRERERM57luJ8zM48SqwhtT0VH7PT7gtCAHg8HjweT5V3+h4ZmDCZTFgsFvz9/TEYDNx4443s2rWL3NxcmjVrRp8+fVi2bBn+/v7lAhUicvYyGAznVTbE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55/np59+Our7N2TIEGbOnMmhQ4d48MEHefHFF7n//vtxu934+fmxfft2Fi5cSGFhIR07duSpp54iNTWVrVu3YrPZePTRR5k/fz7r1q0DoFmzZr6xr7/+egYMGMCMGTPYsWMHF110EaNGjcLhcJCQkIC/vz8vvvgi69at48UXX6RBgwYMHz78qPM9FqvVyt13381LL72E3W4nJyeHqKgoEhISmDFjBgaDgd69ezNp0iTuu+++cmWU+vXrx5tvvskbb7xBz549GTduHH369CEvL48HHniA2NhYxowZQ25uLo0aNfL1yejZsycvv/wya9as4cMPP6SoqAh/f3+mTJnCli1bGDp0KGFhYTz++OM88sgjTJ482bfPss/GE088Ue51nOjPl2Md6zKDBg3i9ddfZ8eOHRQXF/P4449jNpt55JFHcDgcxMbGqrSUiJz31KxaREREpBYYDAb8/f0JDQ2lbt26+Pn54XA4yMrKIjc3V0EIETkrXH755dx99908/fTT5YIQUFreJSEhgU8//ZSUlBQWL17MqlWruOuuu4465kcffcTatWvZt28fS5YsoXnz5rz77rts2rSJnTt3snz5ci655BLf+r1792bRokUkJiaSnJzM7Nmz2blzJ3feeScA1113HQAvvfQSSUlJvouah7v11lvJzMzk3//+N8nJyXz//ffMmzeP3r17l+uNkJKSwptvvklycjLJycnH3C4/P581a9b45gBw9dVXk5uby2+//VblMWjUqBGtWrXi22+/BeDrr7+me/fuFdZzOBxMnTqVpKQkUlJSyh3Dn376iZSUFObNm0eDBg1o1KgRAL169eLXX3/l3XffJSUlhYSEBJYuXUqfPn0qnUuHDh2IiYlh4sSJ7Nq1iw0bNvDWW29VOfcjrV69mp07d3L//ffjdDp9v9/KmmiXBVw++OADdu7cyQ/J+Tz59T7uiq+L1WQgx+HG7fWSX+wmo6CEjILSDIbv9+XTKaY0u+LKmGA2pheVW9YpJpgfkkv7LFzdJIRW4QEM/mwP69OLWJdWyEPLkugcE8IlDQJ9c/UzGXhoWRIb04vYknnYRWOvh9tvvYWRI0cybty4YwYhABYsWMC6devYs2cPkyZNIiwsjKuuugqAgwcP8tFHH7Fr1y7S0tL4z3/+w5o1a7jmmmsAqFevHmazma+//pqkpCQyMzNZuXKl79j1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ZPz48b51LBYLH3/8MdOmTWPlypUAvPXWW3z33XflAjKHO/I8Otzf//53HnvsMW6//XZf9urkyZP55ptvSl+PycTChQtZsmQJH374If/3f//HU089xa233krTpk2ZPHkyiYmJFBcXM2fOHEaNGoW/vz8vvPACcOzz7sgm0gMHDqRLly7069fPt/6tt97K4MGDufnmmyu96cdms/Hpp58yYMAA9u7dy80338yDDz5Ir169fMGastdZtp/jOS9ERETOZcqIEBERqUaW4nzMzrzjLr9QE8xO+3EHIUwmEzabjYEDB/LHH38oCHEOu/HGG0lNTeXgwYPExcUxePBgVq5cqSCEiAD4yuSU8ff3p3///nTs2JF69ephMpnw8/MjMjKy3Hq7d5e/uJ6dnU1YWBgAX331FVOnTmXBggWsXbuWH3/8kV9++aXKOcTGxpKRkeELQkBpT6y8vDxiY2N9c0xPTy8XhKhsLmUlg45cZrVaCQwMpLCwkLi4OOLj47nvvvt86xiNRqxWK1arFafTCVChdN5nn33Gc889x0UXXcQvv/zC6tWr2bx5c7l13JYADHg52bsC16b+1ezZ7YXfDxTSrJ5/uXV+TyufNRpkMTK6c0NujKtDZLAZk9FAgNlII5sfAAa8gKHCe+1yufj666/p0aMHK1eu5KKLLqJJkyaMGzfupOZeVp7U6/USFRWFxWIpl63odrvZunUrsbGxAGzYsIGAgAAuvPBC4uPjWb9+Pb///jv33HMPUBos+/DDD8vt42jn3ZFiYmIqvD+bNm0iMDCQ8PBwMjIyaNSoEQMGDKBly5bUqVMHo9EIlN6osXfvXmJiYti1a1e5rNEtW7aUG/N4zgsREZFzmQIRIiIi1cyWsZFDja44roaU1c7jxpZx/KWJ4uPjmT59Ovv27eOf//xn9c1Lal3dunUZMGAAdevWJSsri5UrV/L222/X9rRE5AxxZMPqYcOG0aFDB9544w3279+P0+nkueeew2wu/yfmkY2OvV6v70L0jh07uOeee7jiiito3749zz77LOvWrTvl3zcOh6PS5YfPpewu98qWlc0vICCAefPm8d1331UY6/Ag7ZHHZs2aNfTp04eOHTvSoUMHXn75ZZYuXcobb7zx174M1f+doMBVvn/Wc9c24pomNp5NTGF3thOHy8vc25viZ/qrL5XXUPnx++KLL5gzZw71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bZ3H4DFbwVCD2RFeD8YSJ81XT9LNCyIiIlLtFIgQERE5gxQFNyQr5krsEfGUWG2lCz1uDPz169qLwReoMDvt2DI2UW/fDwTkp9XGlEVEROQU5URezL52/Wp8vzG/zyc0fUON71dERETOPwpEiIiInKFK/IIptEXjCInCbQ7AazRh8LgxlRThn5dKoD0Fs+5gFBEROSdkxl5FWovbamx/DbcuJTxJvaRERESkZigQISIiIiIiInIG8AUjvJ7qKdP057hRfyyl/j4FIURERKTmqFm1iIiIiIiIyBkgPGkVFkcuKa174zH5nd4G1h43Rncx0Zs/UjkmERERqXEKRIiIiIiIiIicIULTNxCUvYeUVneSFxkPHvepBST+3N6WuYVGWz5WY2oRERGpFSrNJCIiIiIiInKG8QL2iHgym3SlMKzJiQck/lw/MHsP4XtXYsvYhKG6JisiIiJyDApEiIiIiIiIiJzBioIbkhVzJfaIeEqsttKFHjcG/vpz3ovBF6gwO+3YMjZRb98PBOSn1caURURERMpRIEJERERERETkLFHiF0yhLRpHSBRucwBeowmDx42ppAj/vFQC7SmYVX5JREREzjAKRIiIiIiIiIiIiIiISLUx1vYERERERERERERERETk3KVAhIiIiIiIiIiIiIiIVBsFIkREREREREREREREpNooECEiIiIiIiIiIiIiItVGgQgREREREREREREREak2CkSIiIiIiIiIiIiIiEi1USBCRERERERERERERESqjQIRIiIiIiIiIiIiIiJSbRSIEBERERERERERERGRaqNAhIiIiIiIiIiIiIiIVBsFIkREREREREREREREpNooECEiIiIiIiIiIiIiItVGgQgREREREREREREREak2CkSIiIiIiIiIiIiIiEi1USBCRERERERERERERESqjQIRIiIiIiIiIiIiIiJSbRSIEBERERERERERERGRamOu7QmIiIiIyJnN5RdMkS0aR0gUbksAXoMJg9eNyVWEf14qAfYULMX5tT1NEREREREROUMpECEiIiIiFRQFNyQr5krsEW0osYaULvS4MeD1rePFAEYTAGZnHraMjdTb9wMB+Wm1MWURERERERE5QxnatWvnPfZqIiIiInKu8wL2iHgym3alMLQJeNy+QMNx+XP9wOy9hO9NxJaxCUN1TVZERERERETOGgpEiIiIiAguvxBSWt1JXmQ8eDxgPIVWYn8GJELSNxG95WMsxXmnb6IiIiIiIiJy1lEgQkREROQ8lxN5MSmte+Mx+Z1YBsSxeNwY3cVEb/6I0PQNp29cEREREREROaucwq1uIiIiInK2y4ztwr52/fCYrac3CAFgNOExW9nXrh+ZsVed3rFFRERERETkrKFAhIiIiMh5KjO2C2ktbi19YKimr4V/jpvW4jYFI0RERERERM5TCkSIiIiInIdyIi/+KwhRQ9Ja3EZO5MU1uk8RERERERGpfQpEiIiIiJxnXH4hpLTuDV5Pze7Y6yGldW9cfsE1u18RERERERGpVQpEiIiIiJxHvEBKqztLG1NXVzmmqhiMeEx+7G91J96a3bOIiIiIiIjUIgUiRERERM4j9oh48iLjT39j6uNlNGGPbIM9Ir529i8iIiIiIiI1ToEIERERkfNIZpOu4KnhkkxH8rjJbHJN7c5BREREREREaowCESIiIiJngIULF3LHHXdU6z6KghtSGNYEjLX8FdBoojCsKUXBDapl+MTERDp16lQtY4uIiIiIiMiJUyBCRERE5BS88MILTJ48udLn2rRpQ2JiIhdccMExxxk6dCjLli073dPz6datGz99/gFZY9uTOeYSNg9vw1u3NqWRzXLa9vHbsNYMuTT8+Fb2uMmKObVgQb9+/ZgzZ06F5T179mTNmjWnNLaIiIiIiIicPgpEiIiIiJyC5cuX06FDB+rXr1/huR49erB161Z27959zHFyc3NxOp3VMUUfu6OElq9uIH7mRvr/ZzcX1rUy97ZjB0mqhdFUZZ8Is9l8SkNnZ2fjcrlOaQwRERERERE5fU7trzwRERGR89yPP/5Ibm4u3bt357333vMt9/f35+qrr+aNN94AoEuXLgwYMICoqCgOHTrEJ598wuLFi33rL1y4kI8//pglS5YAEBQUxJAhQ+jUqRPBwcHs37+f2bNn89NPPwEQHx/PoEGDaN68Obm5uaxevZo5c+bgcDgqnafb7I8XyCgoASC9oIT3N2Qx6YbGhPgZySsu7RvR46I6PNGpIc3r+3Mg38WijVm88sMB3N7ScUZ3bsi9beoRHmQmu6iEz7bl8OSKFD695yJi6lh58frGvHh9YwDqTfoVgL83D2Vs54Y0DbOSXuBizrpMXluTQYnVRolfMIvnz2H58uVER0fTqVMnVq1axeTJkxk8eDCdO3cmPDycQ4cOsWLFChYsWIDb7aZbt270798fKC3FBDBp0iQSEhJITEzk6aef5vvvvwegadOmDB8+nNatW+NwOFi1ahWzZs3yHasxY8YQHBzMxo0b6d27N2azmcTERGbOnInb7T75k0NEREREREQABSJERERETonH4+G///1vhUDENddcg9Fo5Ntvv6VZs2aMHz+e+fPnk5iYSOvWrRk5ciR2u52EhIQKYxoMBiZPnkxgYCAvvvgiqampxMbG4vmzyXRUVBRTpkzh7bffZsqUKYSGhjJixAhGjBjBlClTKp2nKyCs3OP6gWb+1iyUEo/XF2ToGB3Ea39rwpMrkvkxOZ+mYVZe6R4DwNTvD/D35qEMuzSCBz/bw9aDRUQGWWgdEQBAv092890DLZn/+0HeXX/Qt5+2kQG8fWtTJq9OY+kf2VweHcSUG2PILiph4cZDFNqiAejduzcLFixg/vz5vm0LCwuZPHkyBw8e5IILLuDxxx+nqKiIRYsWkZiYSNOmTbn88ssZNWoUAAUFBRVet7+/P1OmTGHLli0MHTqUsLAwHn/8cR555JFyJbXatWtHVlYWjz76KI0aNWL8+PHs3LmTL774oop3XkRERERERI6XAhEiIiIip2j58uX06dOHtm3bsn79egC6d+/Od999R0FBAb169eLXX3/l3XffBSAlJYUmTZrQp0+fSgMRHTp0oEWLFvTv35+UlBQA0tLSfM/fc889rFixwpc9sX//fl599VWmT5/OtGnTKi1L5PIPpY6/maTH2mIAgvxMALz5SwaFrtIAx+jODfn3TwdYtOkQAEm5xUxclcY/r4li6vcHiLb5kVHg4n977ZR4YL/dxa9phQDkONy4vV7yi92+rAuAYZdH8l1SHi//cACAXdlOmtULYPjlkSxcn4kjJAqA3377rVyGCFAusJOens6HH37Itddey6JFiyguLqaoqAi32012dnaV7811112Hn58fEydOxOFwsHfvXmbMmMELL7zA7Nmzfdvm5+czY8YMPB4PycnJ/Pzzz7Rv316BCBERERERkdNAgQgRERGRU5ScnMymTZu46aabWL9+PVFRUbRt25aRI0cCEBMT4ysTVGbTpk3ccccdGI1GX6ZDmQsvvJDMzExfEOJIcXFxXHDBBVx//fXllptMJho2bMi+ffsqbOMx+ZHndNN17lYsJgPXXWDjztZ1eeF/qb51WkcEcHmjYB69ssFfYxoMBFiMBJgNfLo1m6GXRvDr0Hi+2W1nxe5cvtqR68uoqEyzev58uSOn3LI1+/MZelk4JoMXt7k0o2Lbtm0Vtu3atSs9e/YkKiqKgIAATCZTpVkPRxMbG8uuXbvKlazatGkTJpOJxo0b+wIRe/fuLfc+ZGVl0bRp0xPal4iIiIiIiFROgQgRERGR02D58uU8/PDDTJ8+nR49erB//35fdsSJOlbT6oCAAJYtW+bLiDhcRkZGpdt4DUY8Xi97ckrH3p7loGmolZe6NWbYsiQAgiwmJq9OY9m2nArbO0q8pOa5uGLOZq5uYuOaJiFMuTGG4Zc7+fsH2ynxVNjkuHiNpZkZR/a2aNWqFePGjWPu3LmsXbuWgoICrr32Wnr37n1yOzqGkpKSco+9Xi9Go7Fa9iUiIiIiInK+0V9XIiIiIqdBYmIiXq+X66+/nhtvvJEvv/zS99y+ffuIj48vt358fDwpKSkVsiEAdu/eTXh4ONHR0ZXua8eOHcTGxpKamlrhvyMvqJcxeCvuZ/pPB7itZRgXR5ZmJWxIL+TCulb25Dgr/FeW9OAo8ZKwM5cnV6Rw6wfbuTw6mFbhpdsXu72YjIZy+9ie5eCK6OByyy5vFMyuQ048XjB4Km8G3bp1aw4cOMD777/P9u3b2b9/P5GRkeXWKSkpOWawICkpibi4OPz9/X3L4uPjcbvdJCcnH3VbEREREREROT0UiBARERE5DRwOB4mJiTz44IPUq1ePr776yvfcRx99RPv27bn//vuJjo6mW7du3HbbbXz44YeVjrV+/Xo2bNjAc889R4cOHWjQoAGXX345l112GQALFy6kdevWjBgxgri4OBo1akSnTp0YMWJElfMzuosrLEvNc/HF9lzGXlXap2Hq92ncFV+PJzo1oHl9f5rV8+f2lmE8dVVDAO5uU5d7L65Hi/r+xNbxo1fruhS6PCTnlo6dnFvMlY2DaRhsoW5AaabDa2vS6RIbwqgrGxAXZqVPfF0e7BDOrDXpeDFgKimqdL5lgYeuXbsSFRVFz5496dy5c7l1Dhw4QMOGDYmLi8Nms2GxWCqMs2LFCoqLixk7dixNmjShXbt2PPzww3z99ddH7S0hIiIiIiIip49KM4mIiIicJsuXL+dvf/sbP/30E1lZWb7lO3bsYMKECQwYMID777+frKws5s6dW2mj6jLPPvssw4YN45lnnsHf35/9+/czZ84coDRjYuTIkQwcOJAZM2ZgMBhITU0lMTGxyvEsjpxKl7+xNp2Evi1o3zCQxD153P3xTp7o1JARHRtQ4vay45CDd9cfBCDX4eaRjg14/tpojEb4I7OIez/eRbajNKth0qpUXu4ewy9DW+NvNlJv0q9sSC9i4Kd7GNu5IY93akB6votJq1JZuPEQGE3456VWOq8ffviBjz/+mEceeQSLxcJPP/3Eu+++S//+/X3rfPfdd1x11VVMmzaNkJAQJk2aVOGYOp1ORo8ezfDhw3njjTdwOBysWrWKWbNmVXmsRERERERE5PQytGvX7ijtBUVERETkXODyC+aPrs/V9jQqaJX4LObi/NqehoiIiIiIiFQjlWYSEREROQ9YivMxO/NqexrlmJ12BSFERERERETOAwpEiIiIiJwnbBkboYrm0DXO48aWsam2ZyEiIiIiIiI1QIEIERERkfNEvX0/gNFU29MoZTRRb9/3tT0LERERERERqQEKRIiIiIicJwLy0wjM3gseT+1OxOMmMHsPAfkHanceIiIiIiIiUiMUiBARERE5j4TvTQRjLX8FNJoI37uyducgIiIiIiIiNUaBCBEREZHziC1jEyHpm2qvV4THjS19o/pDiIiIiIiInEcUiBARERE5jxiA6C0fY3QXg7eGSzR5PRjdxTTa8jGGmt2ziIiIiIiI1CIFIkRERETOM5biPKI3fwSGGv4qaDASvfkjLMX5NbtfERERERERqVUKRIiIiIich0LTN9Bw69Ia3WfDrUsJTd9Qo/sUERERERGR2qdAhIiIiMh5Kjxp1V/BiOoq0/TnuFF/LCU8aVX17ENERERERETOaObanoCIiIiI1J7wpFVYHLmktO6Nx+QHRtPpG9zjxuguJnrzR8qEEBEREREROY8pEIGnmp0AAAIXSURBVCEiIiJyngtN30BQ9h5SWt1JXmQ8eNynFpD4c3tb5hYabflYPSFERERERETOc4Z27dp5a3sSIiIiIlL7vIA9Ip7MJl0pDGty4gGJP9cPzN5D+N6V2DI2YaiuyYqIiIiIiMhZQ4EIEREREamgKLghWTFXYo+Ip8RqK13ocWPgr6+OXgy+QIXZaceWsYl6+34gID+tNqYsIiIiIiIiZygFIkRERETkqEr8gim0ReMIicJtDsBrNGHwuDGVFOGfl0qgPQWzyi+JiIiIiIhIFRSIEBERERERERERERGRamOs7QmIiIiIiIiIiIiIiMi5S4EIERERERERERERERGpNgpEiIiIiIiIiIiIiIhItVEgQkREREREREREREREqo0CESIiIiIiIiIiIiIiUm0UiBARERERERERERERkWqjQISIiIiIiIiIiIiIiFQbBSJERERERERERERERKTaKBAhIiIiIiIiIiIiIiLVRoEIERERERERERERERGpNgpEiIiIiIiIiIiIiIhItVEgQkREREREREREREREqo0CESIiIiIiIiIiIiIiUm0UiBARERERERERERERkWqjQISIiIiIiIiIiIiIiFQbBSJERERERERERERERKTaKBAhIiIiIiIiIiIiIiLVRoEIERERERERERERERGpNgpEiIiIiIiIiIiIiIhItVEgQkREREREREREREREqs3/A+yTK0FPxvBaAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "import networkx as nx\n", + "\n", + "def plot(graph):\n", + " labels = {x: f\"{graph.attribute(x, 'id')}\" for x in graph.scan()}\n", + "\n", + " options = {\n", + " \"node_size\": 700,\n", + " \"node_color\": \"#0277bd\",\n", + " \"edge_color\": \"#454545\",\n", + " \"font_color\": \"#efefef\",\n", + " \"font_size\": 10,\n", + " \"alpha\": 1.0,\n", + " }\n", + "\n", + " # Draw graph\n", + " fig, ax = plt.subplots(figsize=(20, 9))\n", + " pos = nx.spring_layout(graph.backend, seed=0, k=0.9, iterations=50)\n", + " nx.draw_networkx(graph.backend, pos=pos, labels=labels, **options)\n", + "\n", + " # Disable axes and draw margins\n", + " ax.axis(\"off\")\n", + " plt.margins(x=0.15)\n", + "\n", + " # Set background color\n", + " ax.set_facecolor(\"#303030\")\n", + " fig.set_facecolor(\"#303030\")\n", + "\n", + " plt.show()\n", + "\n", + "def authorplot(author, limit):\n", + " count = embeddings.search(\"SELECT count(*) count FROM txtai WHERE author=:author\", 1, parameters={\"author\": author})[0][\"count\"]\n", + " print(f\"Total posts for `{author}` = {count}. Using up to {limit} posts for graph.\")\n", + "\n", + " plot(embeddings.search(\n", + " \"SELECT id, text, score FROM txtai WHERE author=:author ORDER BY views desc\",\n", + " limit, parameters={\"author\": author}, graph=True\n", + " ))\n", + "\n", + "plot(embeddings.search(\"transformers\", 25, graph=True))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Everything looks good! The graph above shows a network of posts related to `transformers`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Plot by Author\n", + "\n", + "Let's take a look at a couple authors. First up, the CEO of Hugging Face, [Clem Delangue](https://huggingface.co/clem).\n", + "\n", + "Below is a graph of his 20 most popular posts. Any themes emerge?" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total posts for `clem` = 29. Using up to 20 posts for graph.\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABiIAAALJCAYAAAA57jvpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdd3hUZfrG8e/U1JkkIAmEQCJFWoSAoC4gKzaEtQKLiAU7uKKwFsDVta2/dUFdbFh2LegKKKCLsoKsukFQURAbRToBAiEJKTOZzCRTf3+EDAQCBMgklPtzXbkucs6Zc55MBpg59/u+jyErKyuEiIiIiIiIiIiIiIhIBBgbuwARERERERERERERETl5KYgQEREREREREREREZGIURAhIiIiIiIiIiIiIiIRoyBCREREREREREREREQiRkGEiIiIiIiIiIiIiIhEjIIIERERERERERERERGJGAURIiIiIiIiIiIiIiISMQoiREREREREREREREQkYhREiIiIiIiIiIiIiIhIxCiIEBERERERERERERGRiFEQISIiIiIiIiIiIiIiEaMgQkREREREREREREREIkZBhIiIiIiIiIiIiIiIRIyCCBERERERERERERERiRgFESIiIiIiIiIiIiIiEjEKIkREREREREREREREJGIURIiIiIiIiIiIiIiISMQoiBARERERERERERERkYhRECEiIiIiIiIiIiIiIhGjIEJERERERERERERERCJGQYSIiIiIiIiIiIiIiESMubELEBEREZEqPms8HnsaFbZUApYYQgYThlAAk89DdNlOYpy5WLyuxi5TRERERERE5IgoiBARERFpRJ74FhS17o0z+Uz8UbaqjcEABkLhY0IYwGgCwFxZhr1gJU23fUOMK68xShYRERERERE5IoasrKzQ4Q8TERERkfoSApzJmRSe3h93YgYEA+GgoU72HB9bkkOznGzsBaswRKpYERERERERkWOkIEJERESkAfmsNnI7D6UsJROCQTAeQ8uuPYGELX8VaWvmYPGW1V+hIiIiIiIiIvVEQYSIiIhIAylN6Upul2EETdYjmwFxOMEAxoCXtNWzSMz/pf7OKyIiIiIiIlIPjmEInoiIiIjUVWF6P7ZljSRojqrfEALAaCJojmJb1kgK08+r33OLiIiIiIiIHCMFESIiIiIRVpjej7yOV1Z9Y4jQ2689583reJXCCBERERERETmuKIgQERERiaDSlK57Q4gGktfxKkpTujboNUVEREREREQORkGEiIiISIT4rDZyuwyDULBhLxwKkttlGD5rfMNeV0RERERERKQWCiJEREREIiAE5HYeWtWYOlLLMR2MwUjQZGVH56GEGvbKIiIiIiIiIgcwN3YBIiJyavFZ4/HY06iwpRKwxBAymDCEAph8HqLLdhLjzMXidTV2mSLHzJmcSVlKZuMVYDThTDkTZ3ImCQWrGq8OEREREREROeUpiBARkYjzxLegqHVvnMln4o+yVW0MBjDsM1Y7hAGMJgDMlWXYC1bSdNs3xLjyGqNkkWNWmNEfgkEwNuIE1GCAwozzFUSIiIiIiIhIo9LSTCIiEhEhwJGcycZz7mZDn/spbnnO3hACwGgiZDSHv6pDCAB/lI3iluewoc/9bDz7bhzJmVpephHMnDmTIUOGNHYZJyRPfAvcSRkHDSFe+l06/xrcps7n69M6nqKJPbBHmQ5/8L6MJtxJp+OJb35kj2tgU6ZM4a677gp/X5fXXnZ2Nn369AEgJSWF7Oxs2rZtC0C3bt3Izs4mLi4uckWLiIiIiIhInSmIEBGReuez2sjJupmt3W/GbW9dtdF45DdQAdwJrdja/WZysm7GZ7Ud5kEnrgkTJvCXv/zlgO2NeUN19OjR/Oc//4n4dWbOnEl2dnaNr1mzZkX8ugdTXUOnTp1qbLdYLMydO5fs7Gy6det2yHMUte4NwUAkyzyoceemUDC+O2POTq7aEAxQ1LpPo9RSV4888ghvvvnmET1m8ODBLFu2rNZ9q1evZvDgwZSXlwMwYMAA5s2bd8x17h94iIiIiIiISN1oaSYREalXpSldye0yrKpBLxz7sjR7AomyZp1Y13cCaatnkZj/yzFWKXXhcDga7FpvvvlmjdAjGAw22LVrk5+fz8CBA/n111/D2/r27YvH4yEhIeGwj3cmn3nk4Vs9GdG1KS9+l891XU/jpWUFVb0ikjNhzQeNUk9dlJWVHfFjSkpKDrrP7/cfcr+IiIiIiIg0LAURIiJSbwrT+5HX8UoIBcFQz5PujCaChii2ZY3Et3YuzbYuqd/znyBGjhxJ3759uf3228PbhgwZwtChQ7n22msBMBqN3HXXXVxyySUEAgHmz59PkyZNiIuL489//jMAMTEx3HvvvfTp0we32817771Hnz592LhxI1OnTgWqZirMmTOHDz6ouoGdnZ3N008/zbnnnkuvXr3YvXs3r7zyCt988024lt69e3PnnXeSnJzM6tWrWbhwIRMnTuSyyy4Lj06vjdvtPuDGsdFo5L777qN79+40adKE/Px8Pv7443A91QYOHMiwYcNITU2lrKyMxYsX88ILLwAQFxfHnXfeSZ8+fbBYLKxbt46XX36ZTZs2HfJ5XrhwIYMHD+all17C6/WGr/Pf//6XG2+8scaxp59+OmPGjKFLly5UVFTw5ddLGfNrAn5fVZhiNMDj/VtyXdemBELw7s9FGPa7ngEYe24KN2adRnKchU0lFTzz9S7mrSs9ZJ37690qnhizkaeW7OSazCb0ahnH8h3l+KPs+K3xWHzlXHPNNVx22WU0a9aMkpIS5s2bx/Tp0wE47bTTGD16NL169cJisbBt2zaef/75cCDTp08fbrzxRjIyMti9ezcLFy7k3XffDQdHI0eOZODAgSQlJeF0Olm8eDEvvvgiAFdeeSVDhw4lOTkZl8vFypUreeyxx4CqpZn2fe0BxMbG8vDDD9O7d29cLhczZsxg7ty54f3Z2dk8/PDDfP311wc8D926deO5557jsssuo127dkycODH8GIBp06YRCoU4//zzueWWW2o89p///CfffPMNb7311mGf7/j4eMaOHUvPnj2JiYmhsLCQ6dOn8+mnn2I2m/nDH/5Av379sNlsFBcXM2/ePGbMmEFKSgrvvfcet912W/i1GBcXx3/+8x/GjRvHzz//DEBGRgajR4+ma9eueDwevv/+e6ZOnYrT6TxsbSIiIiIiIscTBREiIlIvwiEE1H8IUW3PefM6XgVwyoYRh3Pttddy4YUXMmnSJLZu3cqQIUPo06cPP/30U/iYP/zhD2RmZvLwww9TXFzMzTffTPv27dm4ceMhzz1y5Ehee+01Xn31VQYPHsxDDz3E8OHDKSsro3nz5jz22GN88MEHzJ8/n3bt2nHnnXce9c9hMBgoLCzksccew+l0kpmZyb333ktRURGLFi0C4IorruAPf/gD//znP/nuu++Ii4sjMzMzfI7HHnuMyspKJkyYQHl5OZdffjnPPvssN9xwwyFH4a9fv578/Hz69evH559/TnJyMt26deP555+vEURER0czefJk1qxZw+jRo0lKSuLeCX9iUisDYz7ZCsBdZ6dw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TNu2bUlJSeGSSy7BYDAccjmwzMxMhg8fTlpaGldddRXnn38+H374YZ2ej/3t2rWL2NhYevTogd1uDzeUhqrlmXr06MHZZ5/NggULjui8N998M3369CE1NZWMjAzOPfdctm3bBsDvf/97LrjgAlq1akVaWhq//e1vKSoqwuVy4fV6Wb16Nddeey2tW7emW7duNXqkAMydOxebzcaf//xnOnToQGpqKr169WL8+PFH1TtGRERERESkMWlGhIiIHBVPfAvcSRmNXQYYTbiTTscT35wY167Gribitm3bxnPPPcd1113HjTfeyOLFi5k1axaXXXZZ+JiZM2fSpEkTJk6cSDAY5D//+Q/ff/89gUAgfMzLL7/Mvffey1//+lfcbjfvvfceycnJRzwafF+7du3iscce484772TIkCGsXr2ad999l3vvvfeozjtv3jzatWvHI488QigU4n//+x8fffRRjVkXCxcuxGq1MnToUEaPHo3D4aixdNDEiRO57bbbGD9+PImJiRQXF/PLL7/UaFp8OE6n86D7KisrGT9+PGPGjOHVV1+loqKCL5b/woSfbeFjpi7LJyXewtTfZRAkxPRfivhkfSn2qL1hxF+X5LHb42fcb5qTnmjFURHgl3w3U5YefiaHxWjg912a8MK3tb/+560r5Q9nJ/Ouexf/+lc2gUCAm2++maZNm1JUVMS8efOAqlkD48eP58477+Spp57CZDKxdetWnn/+eQCWL1/On/70J2688UauvfZa/H4/27dv55NPPgGqlrsaMWIEd955JyaTic2bN/PQQw/hdDpxuVycd955jBw5EqvVyo4dO3jyySfJyck56M81e/ZsOnTowI033ojb7ebll19m+fLlh30+alM9s+aRRx4hISGBadOm8fbbbwNVgdeqVauw2+38+uuvR3Ren8/HbbfdRvPmzamsrGTlypU88cQTAOGlxNLS0ggEAqxbt46JEyeGw77JkyfzwAMP8Nprr4WbvD/zzDPhcxcVFXH33Xdzxx138PTTT2OxWMjPz2fZsmWaISEiIiIiIiccQ1ZW1tEPfRQRkUY1YcIE4uPj+fOf/1zr/pkzZzJnzhw++OCDer92buchFLc8B4xHth7+wVx2RiJ//E0KpydFYTYa2FxSycvLCpi1uva10D8a0Z6+rW217oOqmQN//OMfj6qWKVOmsHHjxiNqWns8MxgMTJs2jUWLFh10ZH90dDSzZ8/mlVdeqXXZnqN13XXXccUVV3DNNdfU2zmPdz5rPL/2f7yxyzhA5+xHMZ8kjd3r27vvvstHH33E7NmzG7sUERERERGRk5JmRIiIyFFxJp9ZbyEEQEmFn78v3cWGokq8gSCXtEvgxd+lU+j2kb3lwPXqR364GaupqtFrS5uVz2/qyH333ceWLVuA2psqnypSUlLo2bMnP//8MxaLhauvvpoWLVrwxRdfhI9p164drVu3Zu3atcTFxXHjjTcC8NVXXx3Tta+88krWrl2L0+kML63z73//+5jOeaKxeF2YK8vwRx08KGto5kqnQohaJCQkcMEFF9CkSZMjXpZJRERERERE6k5BhIjIKcpoNHLffffRvXt3mjRpQn5+Ph9//HGN2RPVMy7Wrl3L4MGDsVqtzJ49m2mz5vLwpR25vmtTPL4gTy3ZyYyVe2cuPHp+KoPOSCTVZqWg3Mec1cU8/XUe/kOsJvL1tpo3Sf/xfSHDM5tyblp8rUFEacXeZYaizFXrpRe7feEldzIzM7n99tvp0KEDDoeDr776in/+85/hhsJXXnklQ4cOJTk5GZfLxcqVK3nssceYMGECWVlZZGVlMXToUACGDx9OeXk5Y8eOpWfPnsTExFBYWMj06dP59NNPj/CZj7xgMMill17K6NGjMRgMbNmyhfvvvz+8dn21a665hlatWuHz+Vi/fj333HPPIZchqouWLVty/fXXY7fbyc/PZ9asWUyfPv2YznkishesrNcZQ8ckGMB+ijRzP1Jz586ltLSUZ599FpdLQY2IiIiIiEikKIgQETlFGQwGCgsLeeyxx8Kj1++9916KiopYtGhR+Lju3btTWFjIuHHjyMzMZPz48XTofjb/c/i55J11XN0piWcvbc2inDJ2lvmAqua7Yz7Zyi6Xj87NYphyaWtc3iAvfnf49e6r9Uu30a5JFE8sqvvNwYr4FGAlqampTJ48mTfeeIPJkyeTmJjIPffcwz333MPkyZM544wzuPvuu/nrX//K6tWrsdlsdO3aFYCXXnqJtLQ0cnJyePPNNwFwOByMGTOG9PR0JkyYgMPhoGXLljUa3h5PCgsLufvuuw95zMaNGxk1alS9X/vll1/m5ZdfrvfznmiabvuG4la9G7uMKkYTTbd93dhVHJf69+/f2CWIiIiIiIicEhREiIicogKBANOmTQt/v2vXLjp37sz5559fI4goKyvjxRdfJBQKsX37doYPH441Jp4p83eC0cSUpbu459wUzkmL59+/Vs1GePabvU1ztzu8TF2Wz+BOSYcNImxRRlbddSZRJiOBUIgH/rudRTkHzoY4GG9sMwBGjBjB559/Hp7dsWPHDl588UWee+45pkyZQkpKCh6Ph6VLl+LxeMjPz2fjxo0AlJeX4/f7qaioqNHQODk5mY0bN7J+/XoA8vPrHqrIqSfGlUdsSQ7uhNZgNDZeIcEAsY5tp0QjdxERERERETl+KYgQETmFXXXVVQwcOJDk5GSioqIwm83hG/LVcnJyCIVC4e9LSkpYVxLEQIgQEAxBicdPs9i9/6Vc1TGJO3o2IyMxijirEbPRQFll1VJKLe0Wvrmtc/jY55buYsrSqpv6rsog57+5ljirkX4ZNp68oCVbSysPWLbpYAKmqhkKbdu2pU2bNlx00UU19ptMJlq0aMH3339Pfn4+M2bMYNmyZSxfvpwlS5ZQWVl50HN//PHHPP7447Rv357vv/+er776itWrV9epLjk1NcvJZmv3mxu3CKOJZjmLGrcGEREREREROeUpiBAROUX179+f0aNH88orr7B69WrcbjfDhw+nU6dONY7bv+lzKBTCHwjV3AYYqvpG0zM1jteuyGDSkjz+t8WJszLA1Z2SuOvsZAB2lfk4/8214ceWVPhrnGdLaVUYsKrAwxlNoxl3bnO+3lYzHDmY0J71+GNiYvjPf/5To99FtYKCAvx+P3fccQdZWVn06tWLm266iZEjRzJ69GjKy8trPfeyZcsYPnw45557LmeddRbPPvssc+fO5dVXX61TbXLqsReswpa/irJmnRqnV0QwgL1wjfpDiIiIiIiISKNTECEicorKzMxk9erVfPTRR+FtqampdXx06KB7zk6LY7vDy9+X7l0KplWCNfznQGhv2HA4RgNYzYY61gSGYNWsiw0bNpCens7OnTsPemwwGOSHH37ghx9+4O2332bevHn06NGDJUuW4PP5MNaynI7D4WDhwoUsXLiQlStXMmrUKAURclAGIG3NHNb1nUDQEAWGBlyiKRTEGPDScs0c6v43SERERERERCQyFESIiJzg4uLiaNu2bY1tTqeTwsJCAE477bQD9ufn57Njxw4uueQSevXqRV5eHhdffDEdOnRg167DryVvDPoJUftN1c3FlaTZrVzdKYkf88q5pG0Cvzsj8bDnHHduCj/tcrOlpJIos5GL2toZ1qUp9y/cdtjHVjMFqgKOmTNnMnXqVO655x4++eQTKioqyMjI4KyzzuKFF17g3HPPJTU1lZ9//hmXy8U555yDwWBg+/bt4eenU6dO4V4SZWVl3HTTTaxfv54tW7ZgtVo599xz2bat7rXJqcniLSNt9Sy2ZY1s2AsbjKStnoXFW/dm7yIiIiIiIiKRoiBCROQE1717d15//fUa2z755BOeeeYZAIYPH87w4cNr7P+///s/5s2bR7t27XjkkUcIhUL873//46OPPuKcc8457DVN3nIwJta679ONDl5ZXsCki1sRZTLw300Onvk6jwl9WxzynLEWI5MvaUWqzUqFP8iGogpGz8th7tqSQz5uX1Z3VfiyefNmxo0bx6233soLL7yAwWBg586dZGdnA+ByuTjvvPMYOXIkVquVHTt28OSTT5KTkwPA+++/z8SJE5k2bRrR0dEMHz4cn8/HbbfdRvPmzamsrGTlypU88cQTda5NTl2J+b/gWzuXvI5XNdg1W6ydS2L+Lw12PREREREREZFDMWRlZR18fQ0REZFa+Kzx/Nr/8cYu4wCdsx/FrBHgcpwqTD+vKowIBSOzTNOe86b+OpfTti2p//OLiIiIiIiIHCXNiBARkSNm8bowV5bhj7I1dilh5kqnQgg5rjXbugRLhYPcLsMImqz128A6GMAY8JK2epZmQoiIiIiIiMhxR0GEiIgcFXvBSopbnlO/N1OPVjCAvWBVY1chcliJ+b8QV7KF3M5DKUvJhGDg2P4O7Xm8vXANLdfMUU8IEREREREROS4piBARkaPSdNs3FLfq3dhlVDGaaLrt68auQqROLN4yMn56C2dyJoUZ/XEnZRx5ILHn+FjHNprlLMJesApDxCoWEREREREROTYKIkRE5KjEuPKILcnBndAajBFY776uggFiHduIce1qvBpEjpABSChYRULBKjzxLShq3Rtncib+KHvVAcEABva28QphCAcV5kon9oJVNN32DTGuvEaoXkREREREROTIqFm1iIgcNUdyJlu739zYZZD+41skaGkmOQn4rfG47WlU2FIJmGMIGU0YggFMfg/RZTuJdeaqF4qIiIiIiIiccDQjQkREjpq9YBW2/FWUNevUOL0iggHshWvUH0JOGmavC/vutdh3r23sUkRERERERETqTSOupSEiIic6A5C2Zg7GgBdCwYa9eCiIMeCl5Zo5WhtfREREREREROQ4piBCRESOicVbRtrqWWBo4P9SDEbSVs/ComVqRERERERERESOawoiRETkmCXm/0KLtXMb9Jot1s4lMf+XBr2miIiIiIiIiIgcOQURIiJSL5ptXbI3jIjUMk17zpv661yabV0SmWuIiIiIiIiIiEi9UrNqERGpN822LsFS4SC3yzCCJmv9NrAOBjAGvKStnqWZECIiIiIiIiIiJxAFESIiUq8S838hrmQLuZ2HUpaSCcHAsQUSex5vL1xDyzVz1BNCREREREREROQEY8jKygo1dhEiInLyCQHO5EwKM/rjTso48kBiz/GxJVtolrMIe8EqDJEqVkREREREREREIkZBhIiIRJwnvgVFrXvjTM7EH2Wv2hgMYGDvf0EhDOGgwlzpxF6wiqbbviHGldcYJYuIiIiIiIiISD1RECEiIg3Kb43HbU+jwpZKwBxDyGjCEAxg8nuILttJrDMXs5ZfEhERERERERE5aSiIEBERERERERERERGRiDE2dgEiIiIiIiIiIiIiInLyUhAhIiIiIiIiIiIiIiIRoyBCREREREREREREREQiRkGEiIiIiIiIiIiIiIhEjIIIERERERERERERERGJGAURIiIiIiIiIiIiIiISMQoiREREREREREREREQkYhREiIiIiIiIiIiIiIhIxCiIEBERERERERERERGRiFEQISIiIiIiIiIiIiIiEaMgQkREREREREREREREIkZBhIiIiIiIiIiIiIiIRIyCCBERERERERERERERiRgFESIiIiIiIiIiIiIiEjEKIkREREREREREREREJGIURIiIiIiIiIiIiIiISMQoiBARERERERERERERkYhRECEiIiIiIiIiIiIiIhGjIEJERERERERERERERCJGQYSIiIiIiIiIiIiIiESMgggREREREREREREREYkYBREiIiIiIiIiIiIiIhIxCiJERERERERERERERCRiFESIiIiIiIiIiIiIiEjEKIgQEREREREREREREZGIURAhIiIiIiIiIiIiIiIRoyBCREREREREREREREQiRkGEiIiIiIiIiIiIiIhEjIIIERERERERERERERGJGAURIiIiIiIiIiIiIiISMQoiREREREREREREREQkYhREiIiIiIiIiIiIiIhIxCiIEBERERERERERERGRiFEQISIiIiIiIiIiIiIiEaMgQkREREREREREREREIkZBhIiIiIiIiIiIiIiIRIyCCBERERERERERERERiRgFESIiIiIiIiIiIiIiEjEKIkREREREREREREREJGIURIiIiIiIiIiIiIiISMQoiBARERERERERERERkYhRECEiIiIiIiIiIiIiIhGjIEJERERERERERERERCJGQYSIiIiIiIiIiIiIiESMgggREREREREREREREYkYBREiIiIiIiIiIiIiIhIxCiJERERERERERERERCRiFESIiIiIiIiIiIiIiEjEKIgQEREREREREREREZGIURAhIiIiIiIiIiIiIiIRoyBCREREREREREREREQiRkGEiIiIiIiIiIiIiIhEjIIIERERERERERERERGJGAURIiIiIiIiIiIiIiISMQoiREREREREREREREQkYhREiIiIiIiIiIiIiIhIxCiIEBERERERERERERGRiFEQISIiIiIiIiIiIiIiEaMgQkREREREREREREREIkZBhIiIiIiIiIiIiIiIRIyCCBERERERERERERERiRgFESIiIiIiIiIiIiIiEjEKIkREREREREREREREJGIURIiIiIiIiIiIiIiISMQoiBARERERERERERERkYgxN3YBIiL1xWeNx2NPo8KWSsASQ8hgwhAKYPJ5iC7bSYwzF4vX1dhlioiIiIiIiIiInFIURIjICc0T34Ki1r1xJp+JP8pWtTEYwEAofEwIAxhNAJgry7AXrKTptm+IceU1RskiIiIiIiIiIiKnFENWVlbo8IeJiBw/QoAzOZPC0/vjTsyAYCAcNNTJnuNjS3JolpONvWAVhkgVKyIiIiIiIiIicopTECEiJxSf1UZu56GUpWRCMAjGY2h1syeQsOWvIm3NHCzesvorVERERERERERERAAFESJyAilN6Upul2EETdYjmwFxOMEAxoCXtNWzSMz/pf7OKyIiIiIiIiIiIhzDUGIRkYZTmN6PbVkjCZqj6jeEADCaCJqj2JY1ksL08+r33CIiIiIiIiIiIqc4BREictwrTO9HXscrq74xROifrT3nzet4lcIIERERERERERGReqQgQkSOa6UpXfeGEA0kr+NVlKZ0bdBrioiIiIiIiIiInKwURIjI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5OQwd+7c8DI61113HZMnT2b58uXk5eWxcOFCPvvsMy6//PKDXsNgMDBgwIDwG+D//e9/ZGZm0rx55NdHtdlsxMfH43a7cTqdCiGOQy6XC5/Ph91uDwdeIiIiInJoHo8Ht9sdnp19KDfccAP//e9/uf3229m2bRsPP/ww9913HzNmzGD06NEYDAbuueee8PExMTF899133Hfffdx+++0sW7aMv/71ryQnJx9Tzf3792fbtm1s376dzz77jIEDBx7T+WrjdruZNGkSN910E5Pf/pAbup3Gnb1SahxzemIUg85I4NrZm7h2zkZ6t7Yx9ty9n03+ckEa56TFcf0Hmxny3gZ+kxZPt5TYI67F5Q0y5pOt9H59DX/6PDdci9te9Vn5oosu4vrrr+cf//gHo0aNoqCggCuuuKLGOS666CJuvvlm3njjDUaOHMnrr7/OzTffzIABAw57/QULFnDppZeGvx84cCALFiw44LhRo0Zx3nnn8be//Y077riDHTt2MHny5PAS1s2aNeOJJ55g6dKl3H777cyfP5877rijxjlSU1OZPHkyixcv5tZbb+WJJ54gMzOzxutqX/369WPo0KH8/e9/54YbbuDPf/4zmzdvPuzPJFLfNCNCRCJi3xv6h7NmzZoDvq9uqtSuXTtiYmIOGIFgtVpJTU0Nf5+fn19j+Z2ioqIaYcf+iouLGTNmDBkZGXTr1o0uXbowceJEBg0axIQJE0hISKBZs2Z1HoXStm1bWrZsWWOpp9rqzMnJqTHqvqioiNNPPz38swYCgYOOdG/bti1ZWVkHXAOq3ojk5h64XJLRaOS6666j74DLadYkCYvJQJTJiMdXc+T/moKaSxfll/s4La7qv4gzTotmR5mXXa69o6eX73TVWuP+DvcyCJhjwv8RRfp1cNFFF3HvvfeG902YMIGVK1cyd+5cFi5cSFZWFp07d+a3v/0t1113HQ899BArVqwgPT2dTZs21RhZsmrVKkwmE61ataKkZO8yV4ez7ygXqPqdfvnll7Ue27JlS2JiYnjmmWdqbDebzWzcuPGg1+jZsyfR0dF8++23ADidTlasWMHAgQN566236lzrkTAajdjtdsxmMw6H44DZUHJ8cTqdJCYmkpCQQGlpqQIjERERkcMIBoNMmjSJ++67jyuuuIINGzbw888/87///e+AG7oLFixg0aJFQNXyrC+//DL/+te/WL58OQAffPBBjZ4BmzZtCi83C/DWW29x3nnn0bt37xpL1R6pQYMGhQcnLVu2LDyr41Azq4/Uu+++G/7zyvVF2L/bxeDOTXjxu72rDhgMMOaTreEZELNWFdMv3cb/UTUbYviZTbjj4xwWb62a5T5mfg6r7zrziGt59pu9SyBtd3iZuiyfwZ2SmG1Lxb57LVdffTXz58/n008/BeCdd96hZ8+eNfpE3nTTTbzyyissWbIEgF27dpGens5ll11W66yEfX322WfcfvvtpKRUBTGZmZk88cQTNQYmRkdHc8UVVzBp0iSWLVsGwDPPPMPMmTMZNGgQ77//PldeeSU7d+7klVdeqfpZtm/n9NNPZ8SIEeHzjBgxgs8//zw8UHLHjh28+OKLPPfcc0yZMuWAmc8pKSkUFxezYsUKAoEABQUFB8zmEWkICiJEpF7t2LGDYDBIeno6X3311QH709PTcTqdlJaW1ul80dHRFBcXM27cuAP2uVx7b4ZXL31ULRQK1Wm0b05ODjk5OXz00Ud8/PHHvPjii3Tr1u2AG8aHExMTw/r163nyyScP2Odw7J0+eqg6KysrD3uNpUuX8tprrx2wr7i4uNbHXHPNNQwZMoQnP/iGZcE0yv1G/u+iNKymmgmBL1jzRmQoBMYjCJP2t7mkkmAoxBlNo/kExwH7z2gaTYnHT1F5Jam1PH5/9fE6+Prrr2uEHbt37w7/2ePxsHTpUpYuXcobb7zB5MmTueGGG8LLQh1OMBg8IHyrrRn1/tNkD3XDvvoN8YMPPkhhYWGNfYdaUmfQoEEkJCTUeKNsMBho06YN06ZNq/ebzhaLBbvdTigUoqSkRP0gTgChUCgcRthstoP2mBERERGRvRYvXszSpUvp2rUrnTt35uyzz2b48OE8/fTTNd577xtMVA9a2n9bVFQUsbGxuN1uoqOjuemmmzj33HNp2rQpJpMJq9UavqF9NFq1akXHjh3585//DFR9XsnOzmbQoEH1GkT079+fwYMHk5qaSlRsPCazmbLKmp8Htju84RACag56S0+Mwmoy8kNeeXh/WWWQjcWH/mxcm6s6JnFHz2ZkJEYRZzViNhooqwwQMFd9rmrVqtUBA9vWrl1L9+7dgarPnC1btuSBBx7g/vvvDx9jMplqfOY8GIfDwbfffsuAAQMwGAx8++23B7zPTk1NxWKx1OhJGAgEWLt2Lenp6QC0bt2aX3/9tcbj9h+017ZtW9q0acNFF11UY7vJZKJFixZs27atxvZFixYxZMgQZsyYwbJly/juu+/45ptval2aWCSSFESISL2qHn195ZVXMnv27Bo3WpOSkrjwwgv573//W+MxnTp1OuD76qWdNmzYQJMmTQgEAgft5VBfqq8ZHR2Nx+MhLy+PHj168NNPPx32sRs2bKB///6UlpbidrsPe3xtNm/ejMFgoFu3brXOxNiwYQP9+vVj165ddX7DkJmZyddff82879awO70pGM20bRLF+t11b1S7fncFLW1WUuLM5JdX3ejvmRp3yMeUVARYtKWMW7o345XlBTX6RCTHmRnaOYlZq3ZjCO59kxrp14HH4zlo0+r9bd++nS5dugBVr4sBAwYQHR0dDhIyMzMJBAJs374dqHrT2bRp0/DjjUYjGRkZh33tbNq0iR49ejBt2rQD9uXk5OD1eklOTq7zhwW73U7v3r154okn2LJlS3i7yWTihRdeoGfPnuGRWPUhOjqa+Ph4fD6flmI6wQQCAcrKyrDb7cTFxVFeXn74B4mIiIic4nw+HytWrGDFihX861//4v777+emm26qEUTsOziq+v1xbduqBzLdeeednHXWWbz66qvs2LGDyspKHn/88VoHNtXVoEGDMJvNzJkz54D6X3jhhXp579e5c2ceeugh3nrrLZYvX86WlHPpf+HF/OGcmkvCHjjoLYTxCMe8BUOhA2bbm/c5Sc/UOF67IoNJS/L43xYnzsoAV3dK4q6zkwnVsXl29UCwZ5999oAb/3X9/L1gwYLw8kjPP/98nR5zNGJiYvjPf/5T69LRBQUFB2wrLCzkxhtv5KyzzqJnz56MGzeOa665hnHjxmkgmTQoLQ4sIvXu+eefx2KxMHnyZLp27UqzZs3o1asXzzzzDLt37z6gcXBmZibDhw8nLS2Nq666ivPPP58PP/wQgBUrVrB69WqefPJJevbsSUpKCl26dOHWW2/ljDPOOOoax40bxw033EBmZiYpKSl06tSJBx98kJKSkvCbjrfffpthw4YxePBgWrZsSfv27bn66qtrPd/nn3+Ow+HgySef5Mwzz6R58+Z069aNu+++m9NOO61ONeXn57Nw4ULGjx9Pnz59wuc4//zzAZg7dy42m40///nPdOjQgdTUVHr16sX48eMPOvtjx44d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F19MubzrgX1dXV1KpVLmzp2bUqm02zl/voRPd3f3K9qcN29e7rrrrhx99NH50Ic+lHPPPTdf+cpXsmbNmlQqlb4H9f+ttnb3W9lrrrkmLS0tueGGG/Liiy+mUCjklltu6Ttu7dq1OeH//Vum/++ZOf7Asfm3TxyY/2zanjlLnvof/w695d2XjqpUkr8oc49UCsVMmTIl7e3tfQ/8X288r+Xyyy9PfX19FixYkJaWlnR3d+emm27qO2/jxo05++yz84EPfCBHHXVULr744syePTsXX3zxHl8/AACAvSGIAABgr3V2dvYtofTn1q5dm2KxmLFjx2blypV73e66deuybt26LF68OAsWLMiJJ56YNWvWZOvWrZk6depuxx588MF9+yLU19fngAMOyPXXX9/X77Rp017R/s6OjixZszn//kRbfvzEltw5+5CMHb4hWzt3f+j+p02dOfOIfTJiSKFvVsQxE0elVK5k3abOvR7X69ln5LB85CMfyS9/+ctUKpU9Gs9/j71YLO72/bRp0zJ//vysWLEiSTJ+/PiMHTt2t2O6u7vzyCOP5JFHHsmSJUvy/e9/PwcddNAeXb/e3t6+2RkAAAB7QhABAMCbprm5Offff3+++tWv5jvf+U7Wrl2bsWPHZvr06XnyySezfPnyVz1v3333zamnnpqHH344mzZtyuTJkzNp0qQsXbo0ya4ZGLNnz87JJ5+cVatW5aSTTsrUqVOzbt26JLuWdtq2bVtOPfXUbNq0KQ0NDbngggt26+NTn/pUnirum1+kLuUU8vFDx+WF9p5s+4sQIknuXLU5lx+3X2766JRc91/P590javONkybnjlWb+/aH+J+qSU0mjKxNTZIxw2tz1H51+acZ/5D29vbcfPPNezyeLVu2pLOzMzNmzMjGjRvT3d2dHTt2pLm5OSeddFKeeOKJjBw5MnPnzk1n58vhycyZM1MsFrN69ep0dXXlxBNPTGdnZ1paWtLW1va61++FF17IiBEjMn369Kxbty5dXV3p6up6Q78JAADwziaIAADgTXXdddflrLPOyoUXXph3v/vd2bZtW1avXp1HHnnkNc/p6urKAQcckJkzZ6a+vj6bN2/OkiVLcs899yRJfv3rX+e2227L3LlzM3To0Nx3331ZunRpDjrooCS79lW48sorc9FFF+WWW27JM888kxtvvDHz58/v62Pnzp0577QZ+eeJ+6dcTh59YUf+4YfrUnmVejp6Kzn9jnW59sTJ+fk5h6ajt5x7ntiaKx5ofsO/T/3wYtZc9P6UK5Vs7ypl3eau/MeyB3Lvbd/Lzp0793g85XI5N954Y84+++zMmTMnK1euzCWXXJJvfvObufTSS3PzzTentbU1CxcuzIUXXth3Xnt7ez796U/nwgsvTLFYzJNPPpmvfe1rfUtCvd71W7VqVe6+++58/etfz5gxY3Lrrbdm0aJFb/h3AQAA3rlqGhsbX+1/LwAAeMfpGToqaz78LwNdxiscvuyfU9vdPtBlAAAAVIXFXQEAGDSGdLentmv7QJexm9quNiEEAADwjiaIAABgUKlvXZmUX7kvxIAol1Lf+thAVwEAAFBVgggAAAaVfTY8nBSKA13GLoVi9tnwy4GuAgAAoKoEEQAADCp17c9nxJampFwe2ELKpYzY8lTq2l8Y2DoAAACqTBABAMCgM75pWVIY4FvhQjHjmx4c2BoAAAD6gSACAIBBp771sYxueWzg9oool1LfstL+EAAAwKAgiAAAYNCpSTJp9Z0plLqTSj8v0VQpp1DqzsTVd6amf3sGAAAYEIIIAAAGpSHd2zNp1R1JTT/fEtcUMmnVHRnS3d6//QIAAAwQQQQAAIPW2JY/Zr/Hl/Rrn/s9viRjW/7Yr30CAAAMJEEEAACD2vinf/FyGFGtZZpeanf/NUsy/ulfVKcPAACAt6jagS4AAAAG2vinf5EhndvS/L4zUi4OTQrFN6/xcimFUncmrbrDTAgAAGBQEkQAAEB2LdM0cstTaT789GxvmJaUS28skHjp/PqNqzNx9Z32hAAAAAatmsbGxspAFwEAAG8VlSRtE6Zl49QPZ+e4qXsfSLx0/IgtT2V804Opb30sNdUqFgAA4G1AEAEAAK+hY9R+2XTAsWmbMC29w+p3fVkupSYv30JXUtMXVNR2taW+9bHss+Hh1LU/PxAlAwAAvOUIIgAAYA/0Dh2VnfWT0jl6/5Rq61IpFFNTLqXY25Hh25/LiLbm1Fp+CQAA4BUEEQAAAAAAQNUUBroAAAAAAADgnUsQAQAAAAAAVI0gAgAAAAAAqBpBBAAAAAAAUDWCCAAAAAAAoGoEEQAAAAAAQNUIIgAAAAAAgKoRRAAAAAAAAFUjiAAAAAAAAKpGEAEAAAAAAFSNIAIAAAAAAKgaQQQAAAAAAFA1gggAAAAAAKBqBBEAAAAAAEDVCCIAAAAAAICqEUQAAAAAAABVI4gAAAAAAACqRhABAAAAAABUjSACAAAAAACoGkEEAAAAAABQNf8fAI9qI6+ZaPgAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "authorplot(\"clem\", 20)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, let's see what [Tom Aarsen](https://huggingface.co/tomaarsen), the primary developer of [Sentence Transformers](https://github.com/UKPLab/sentence-transformers), talks about.\n", + "\n", + "_p.s. It's not surprising._" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total posts for `tomaarsen` = 14. Using up to 5 posts for graph.\n" + ] + }, + { + "data": { + "image/png": "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pIMhtQ8eq/AbD+xwoX93eJ6nZ599VgsWLFAqldK+++6r73znO6qrq9O11167zcfsv//+CgQC+tnPfqb169frIx/5iGbOnCnDMHTLLbds97kuvvhiTZgwQddcc40ymYymT5+u//zP/9Qll1yywxoffvhh3X777YpGozryyCM1c+ZMpdNpPfjgg3v0vQMAAABAbyGIAAaofDQhp2643OQwFSNxlQIhBUpFhfKOjI4NirevUySXrnSZAGqcZVk64ogjdOmll+rll1+WJDU3N+u1117b6riLLrpIxx13nCKRiF5//XX94he/0JtvvilJmjZtmiZOnKh7771X5557rhKJhJ577jn99Kc/leM4mjVrlsaNG6dx48ZpypQpkqSpU6equblZI0eO1IUXXqjDDz9cjuPohRde0Pz589Xe3i6p8wrz1atXK5fL6ZRTTlGhUNCDDz6oO++8s1t9F1xwgY477jglEgmtX79ev/rVr/T3v/9dkjR27Fidd955Ouigg5RKpfTXv/5Vt956q1zX3eo16ejo8P8ejUYlSalUyr8K/sknn9TcuXN17LHHavz48brnnnt01113aebMmf4V983NzXrwwQd1333vdwTMmjVLiURCS5cu1RlnnKFwOKwnn3xSN910kx9kfOELX9CUKVM0ePBgpdNpLV26VFdffbVmzZqlz3zmM/7zv/POO/rqV7+qSCSiCy+8UJM+8QnFE3V6aaOtHy5ap3++09kZcNz+CT145midce8q/eCEYTpkb0NT7lmlyycO1aubHRU9aephTcoVS/rJ0xt034pWzf7Ufvr8QQ3aZOf1vb+s06LV7f73MGYvQ9dM2lcT9kvIznt66l/tumLROrU4nfU/cOZH9dpmRwVP+vKhTXrrtBs089LvaNq0aTr55JPV2Nio9vZ2Pf3005o3b95Wr/3w4cN111136ayzztLatWv926dMmaLTTjtNX//617da0G9ubtYDDzygr3zlK1udr+z555/X888/73+9ceNG7bfffvr85z+/3SDCsiydcsopuvbaa/XPf/5TkjR79mwtXLhQBx98sF599dXtPp/ruv775U9/+pNOP/10HXXUUX7dkUhE3/zmN/WJT3xCiURCa9as0S9/+Uv/M7gtxx13nM466yyNHDlS7777rh577DH95je/kffeTJAvf/nL+sxnPqOhQ4eqo6NDixcv1i233OK/x4cMGaLp06frsMMOUzgcVnNzs2655Rb94x//kKQP/RwCAAAAqG4EEcAA4iSGasv+/6b2wYepEEt23ugVFdD7l56WFPCvWA1nO1S3aakGvf03xdMbK1EygBrnOI5s29Zxxx2nFStWKJ/Pb/O4q6++WtlsVrNmzVImk9HnPvc53XDDDfrGN77hL9wPGzZMEydO1Pe//30lk0ldddVVOvPMM7VgwQLddNNNGj58uNasWaPbb79dUufivmVZ+tnPfqZHH31U8+fPVywW0/nnn6+rrrpKM2fO9J//05/+tH73u9/p29/+tg499FDNmjVLy5Yt05IlSxQIBDR79myZpqmf/OQn2rBhg0aMGOEv0A4bNkxz5szRggULNGfOHDU0NGj69OmaPn265syZs1uv27Rp03Trrbdq/vz5KhaLCgQC2rx5s66++mq1t7dr7Nixuuyyy7RlyxY99dRT/uPGjRunLVu2aMaMGdp333115ZVXatWqVXrkkUc0evRoXXLJJfrJT36i5cuXK5lM6vDDD5ck3XTTTdqwYYNOPfVUXXjhhf73dsEFF+j444/XlfN/oyWDP6FLJgzR775yoI7+5XK1ue93aVx54jBd9cR6rWnL+rdPHTtI8/7RrE/d+bpOP7hRPz1pf312dIMeeaNNcxe/o4uOGaybTx2pI36xVE6hpLpYSPd/9aP6zctb9MNF62REgrrqxH11+2kH6LTfrvSfa+rYQbr9n+/qlN+8rn2X/0EnnHCCpkyZoh//+Mdas2aNmpqatppvUbZu3Tq99tprmjx5sn7961/7t0+ePFmLFi3a5mMGDRqk448/foeL+NtiWVa30OmDRo8erUgkoiVLlvi3rV27Vu+8844OPfTQHQYRXR122GHab7/9tG7dOv+26dOna+TIkfrxj3+sLVu2aOLEiZozZ47OPfdcrV+/fpvn+N73vqebbrpJr7zyioYNG+Z/PhYuXCips1Nk3rx52rhxo4YNG6ZLL71UF154oW688UZJ0ne+8x2Fw2F95zvfkeu6GjFihBzH8V+LnfkcAgAAAKheBBFAP1eS1D54rDZ/ZJLshpFb788dDGl7O2AUYkm17Psxtez3bzJb12jvNU+qbtMyBfqgbgCQOhcvZ8+erZkzZ+rzn/+8Vq5cqZdffllPPPGEVq9eLamzm2DMmDH64he/6AcVt9xyiyZOnKiPf/zjevjhhyVJgUBA1113nb+4+Ze//EXjx4/XggULlMlkVCgUul0pLkmnn366Vq1apdtuu82/bc6cOfrd736n4cOH+4u3q1ev9hdc169fr9NOO03jx4/XkiVLdNRRR2nMmDE6++yz/eM3bnw/3D3zzDP1+OOP+90J69ev17x583TjjTdq7ty52w1fdmTRokX605/+1O22O+64w//7O++8o0MOOUQnnnhityAinU7r5z//uTzP09q1a/WPf/xD48eP1yOPPKIhQ4bIcRwtXrxYjuOoublZq1atkiRlMhnZti3P8/zXzzAMff7zn9fs2bP1xOqS3glkdOkf39JLF43V1w8fpJue2+Q/73XPbNRTa7ovui/b5OiGv70jSZq7+B1NnzBELU5Bd728RZJ0/bMbde74vXXo4Lhe2GDrvKP21tJmR9c+vcE/x/RH39LSiw/TqMaY3mzNSpLebM3qmqfWS15R6Y6QJh80SC0tLVqyZImKxaI2bdq0VcdNV48//rhOP/10P4gYPny4DjroIP3Xf/1Xt+N++MMf6rjjjpNhGHr22Wd1/fXXb/8f7AOGDRum008/fYfbMjU1NSmXyymTyXS7vbW1VU1NTTs8/xe+8AV99rOfVTgcViQSUTab1R/+8AdJndt9nXzyyfrKV76iLVs6X+t7771Xxx57rE4++eRun4WyadOm6be//a0ee+wxSZ3v79tvv10XXHCB/7no2n3T3NysBQsW6LLLLvODiMGDB+vpp5/2txjr+hnZ2c8hAAAAgOpFEAH0Y/loUusOmaKOIWOl965O3eUhoe8db9fvp7eOPEfJ5mUavuL3iuS2f5UmAPSkp59+WosXL9bhhx+uQw45RMcee6ymTp2q66+/Xo899pgOPPBAxeNxPfDAA90eF41GNWzYMP/r5uZmP4SQpC1btqihoWGHzz1q1CiNGzdOjz766Fb3DRs2rFsQ0VVLS4saGxslSQceeKA2b9683cXSUaNG6YADDtDkyZO73R4KhTR06FC9/fbbO6xxW954442tbjvttNN08skna/DgwYrFYgqHw36QULZmzRq/m0HqfI0+8pGPSJJeeOEFNTc36+6779Zzzz2n559/Xs8884yy2ew2axg2bJgikYiWLVumYv3RCqikgie9uNHW6L2Mbse+tHHrIc4rNr3/b+WVpFan0O22TZnO2Q57mRFJ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nXdqzJgxkjq7Gh599FFddNFFGjdunEaPHq3LL79cy5Yt2+lB1WUPPfSQhg8frhNOOEHr1q3TX/7yF33/+9/X8ccfr3322UdjxozRmWeeqQkTJmzz8QsXLtSnP/1pnXXWWRo5cqT2339/TZo0Seeee66kzrknkUhEX/ziFzV06FB96lOf0uc///lu57j44ot1zDHHaJ999tFHP/pRHXnkkf62ZOXP4Y9+9CMddNBBGjZsmI455hhdfvnlH9rVBAAAAKA60BEB9CNtQw5/P4ToIxvHnKaIm2KbJgC9wnEcvfrqq/ryl7+sYcOGKRTq3ErnkUce0d133+13C/zoRz/SOeeco1mzZqm+vl6tra1atmyZHMeRaZp+YJBIJLp1EwSDQX9OxB//+EdNnz5dd955p2KxmL797W/r3Xff1VVXXaWvfe1rmjNnjiKRiDZv3qyXX37Z7xIIhUKKRqNKJBKSOrsGQqGQgsGgwuHO/1SaM2eOzj77bM2cOVOxWEwbN27UHXfcoWw2q9dff12XX365pk2bpmuuuUaBQEAbN27UU089pba2NpVKJf9P+fzlv5cXYVOpVLctajKZjFKplP/173//e+233366/PLLVSqV9MQTT+j+++/Xxz72MX/rKM/z5Hlet22mul6Vn06ndfzxx2vatGmKRqNav369rr32Wq1Zs2a7/36/+tWvFAwGdd23Pqe4aemljba+fM8qpbJbb3m0p95J53XKb97QVSfuq99PPVDRUFDrUjkt+ldK29jRSQqGZHRsUDo9SGeeeaYuuugihUIhrV69WldcccV25yFI8od2T5o0SbNnz+52Xzab1Wc/+1ldfPHFikQi2rRpk5555hndfffd/jGxWEz777+/QqHObQ+POuooDR8+XMOHD9fvfve7buebNGmSpM732f77769YLObfN3/+fJVKJV1zzTWKRCJ6/vnnd2oOxQd1dHToz3/+s84++2w988wzmj17tr7xjW/ooosu0l577aVUKqUVK1Zo8eLF23z8888/rx/84Ac666yz9NWvflWFQkFr1671Z1y8+eabmj9/vqZOnapvfetbeuWVV3TrrbfqBz/4gX+OYDCo73znO9p7772VyWT0/PPPa/78+ZI6Z5VccsklOv/883X99dcrEomoublZzz33HB0SAAAAQI0IjBs3bieb3wH0pnw0qdcnzpIXjvVeJ8S2lDwFC1kd9NfrFMmlP/x4AFVne9sG7enX27tvZ+1oAX979+3J19i+fDShVyddU+kytnLIk1cpzO8uAAAAAOj36IgA+oGSpHWHTOkcTN2XIYQkBYLyQlGtP2SKRrx0xw5GkQLoCz0dBuxMOLCzui7e90U4gP4jkksrnO1QIZasdCm+cLadEAIAAAAABgiCCKAfaB88Vh1DxlaugGBI7UMOU/vgsarftKxydQD9UF+EAXvSLdD179v62vO8HgkHgLpNS9Wy78ekYKjSpUheUXX8vgIAAACAAYMgAugHNo+cJHmeVMmBjV5Rm0eeSBCBfq+3OgV6slug69dd/14OBbZ3P9sIoT8b9Pbf1LLfv1W6jE7BkAa9/WylqwAAAAAA7CSCCKDCnMRQXfvVE/T5gxrUGA/r47e/qmWbnL4vJBiS3fgROYl9FE+/0/fPX0V++9vf6ve//73uu+++SpfS6/oqDOjpbYS29afr/R88dme+BqpdPL1RZusa2fX7Vzw4N1Nv87sKAAAAAAYQgogqVl9fr3POOUcTJkxQY2Oj0um03nzzTS1cuFDLlvXcVe9z587VqlWrNH/+/B47Z2+YO3euxo0bt937X3rpJc2YMaPvCnrPoZ+Zqq8e1qTP371Sb7VltcUu9HkNPq+oLfsfp+Er+n4Bva6uTldccYUOOOAA1dXVqa2tTc8++6xuu+022ba93cd97Wtf04QJE3TggQeqUCjoc5/73Ic+1/aCgmnTpmnixIk677zz9vj72RUnnXSS/v3f/32nav8wfdkp0NPbCHmet9PHso0QUBn7bnhWKxtHVraIYEh7r3mqsjUAAAAAAHYJQUQVu+aaaxSJRHTddddp48aNamxs1Pjx41VXV1fp0iriyiuvVDjc+ZYfPHiwbrnlFs2cOVP/+te/JEmFQvcAIBQKqVgs9npdgz9ysJrTeT2/PrNH5wkFpOKerr8GQ2ofPFb6kCAiHA5v9XrtKc/z9Oyzz2rBggVKpVLad9999Z3vfEd1dXW69tprt/u4SCSi//f//p9WrFihU045pUdr6gnbmwPQ9e+RSESSFI/HK76NUNev2UYIQFk4HFYymdQg9229u+VVtTaOrsysCK+ous0rmA8BAAAAAAMMQUSVsixLRxxxhC699FK9/PLLkqTm5ma99tprWx130UUX6bjjjlMkEtHrr7+uX/ziF3rzzTclvX+F+L333qtzzz1XiURCzz33nH7605/KcRzNmjVL48aN07hx4zRlyhRJ0tSpU9Xc3KyRI0fqwgsv1OGHHy7HcfTCCy9o/vz5am9vl9TZobB69WrlcjmdcsopKhQKevDBB3XnnXd2q++CCy7Qcccdp0QiofXr1+tXv/qV/v73v0uSxo4dq/POO08HHXSQUqmU/vrXv+rWW2+V67pbvSYdHR3+36PRqCQplUqptbVVkvTkk09q7ty5OvbYYzV+/Hjdc889uuuuuzRz5kwdeeSRampqUnNzsx588MFuV9LPmjVLiURCS5cu1RlnnKFwOKwnn3xSN910kx9kfOELX9CUKVM0ePBgpdNpLV26VFdffbX+4/s/1Cmf/qgkacv3xuvtVFZH3rxc0VBA10zaV6cf3KhkLKSXNtr64aJ1+uc7nZ0Bx+2f0INnjtYZ967SD04YpkP2NjTlnlW6fOJQvbrZUdGTph7WpFyxpJ88vUH3rWjV7E/tp88f1KBNdl7f+8s6LVrd7n8PY/YydM2kfTVhv4TsvKd/HnelfjHvxm7/Vv/6179ULBb1qU99SqtXr9Zll12madOm6eSTT1ZjY6Pa29v19NNPa968eVu99sOHD9ddd92ls846S2vXrvVvnzJlik477TR9/etfVzqd1oMPPujf19zcrAceeEBf+cpXtjpfV3fccYekzq6CD9N1wT4YDCocDndb0C9/bRiGAoGArrvuOr311lsKBAKaNGmSisWi/vjHP+ruu+/2z1VfX6+LL75Yhx9+uNra2vR//+//VTAYVCKR0N577y1J+uxnP6tJkyb5//5LlizRb37zG2WzWR1yyCGaOXOmJOnRRx+VJN1zzz265557FAqF9LWvfU3HH3+8LMvSW2+9pTvuuENLly6VxDZCAHpfIBCQZVmKx+PK5/NKtbVpn1fuUWriLHmBmBTowy2aSp6CxZz2XfF77Vr0CgAAAACoNIKIKuU4jmzb1nHHHacVK1Yon89v87irr75a2WxWs2bNUiaT0ec+9zndcMMN+sY3vuEv3A8bNkwTJ07U97//fSWTSV111VU688wztWDBAt10000aPny41qxZo9tvv11S5+K+ZVn62c9+pkcffVTz589XLBbT+eefr6uuuspfdJWkT3/60/rd736nb3/72zr00EM1a9YsLVu2TEuWLFEgENDs2bNlmqZ+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Usrlcts9du+3npGkzjCi5PXONk3vnXfYq/drr7ef6fnzAwAAAAD6BEFElcvn81qyZImWLFmiu+66S9/97nd19tln67HHHpNhGGppadGll1661eO6LuZua7E2GNzxYkM8HtfixYv1y1/+cqv7Wlpadnju8tX021ug7focDz/8cLd5DWWbNm3axiM+XNfARZImTZqkCy+8UDfffLOWL18u27Y1depUHXzwwd2O29Fr5DiOzj//fI0bN07HHHOMzj77bE2bNk0XXnihSoE9G/SZyXtb3Zb3ui+ml7ZxmyQF39upyIoE9diqlK55srMjIlAqqOmdl7TPmie0ZcsWOY6jfD6vtrY2bd682X/8u+++q69//es66qijdPTRR+u8887TqaeeqksvvXSbQ74zmYxefPFFHX/88XrppZd04okn6sEHH9xqkevdd9/Vu+++q7feekupVErz5s3TwoULu71v9lQmk5FlWVvdnkgklMns2dDwDxoyZIj++7//Ww888IBuu+02dXR06LDDDtPll1+ucDi83fd5PB73t3r64Ov5wfcpAOypSCSiRCKhUCgkx3F2+mfh3m89o4ib0rpDz5AXivbsAGuvqGAxp+HL76UTAgAAAAAGOIKIGvPWW29p4sSJkqSVK1eqqalJxWJRzc3Nu33OfD6/VTCxcuVKnXDCCXrnnXe22s5hZ61evVp77713t62cPvgc5a2DesvYsWO1fPnybl0jXbtFdpbneXrxxRf14osv6s4779RDDz2k8ePHa1Xz1gv2a9qyyhY8fWy4pXUrOhfpw0HpyH1M3fLC7gUsO/JKs6PPHdSgt1NZFUtSwCuoY+Nm5T7QUbKtboFcLqfFixdr8eLFuv/++7Vw4UIdcMABWrly5Tafa9GiRTr//PO1aNEiDR06VE888cQOayu/ryKRyG5+d9u2du1ajR49eqvbP/rRj3Ybpi1pq9Dp4IMP1rp16+R5nt5++22Fw2GNHj3a35ppv/32UzKZ9I8/6KCDFAgEdPPNN/uv4YknntjtnIVCYZufoVAopIaGBn84NQD0tEAgoEQiIcMwlM/n1draus0weUcaml+R1fovrTtkijqGjJW84p4FEu89vm7zCu274vfMhAAAAACAKtALPfToD+rq6nTDDTdo8uTJOuCAA7TPPvvo4x//uKZOnapnn31WkrRkyRItX75c1157rY4++mgNGTJEhx56qL75zW9uc5F2e5qbm3XwwQdryJAh/nyA+++/X8lkUj/60Y900EEHadiwYTrmmGN0+eWXf2g3RdnLL7+sV155Rddcc42OOuoo7bPPPjr22GN1zDHHSJJ++9vf6tBDD9X06dM1atQo7bvvvjruuOM0ffr0XX/BtmP9+vUaPXq0jjnmGA0fPlznnHOODjrooF06x4QJE/TFL35Ro0aN0pAhQ/TpT39agUBAa9euVSjvqKRAt+PtvKdf//NdXTNpX33iI3U6aJChG08eoXgkqN+8N+ehJy14cbMajJBu/cJHdOQ+pkY0GDrhoKEf+m910kkn6ZRTTtHIkSM1dOhQTZ48Wa7r7jDUevrpp2WapmbMmKGXXnrJn30gSR/72Mf0mc98RiNHjtSQIUM0YcIEzZgxQ0uXLvXPOWbMGN15553aa6+9/McNHjzYf22DwaBGjRqlUaNGyTCMrZ6/7Pe//70mTJigr33ta9p///01cuRIffOb39Shhx66VYfN4MGD9e1vf1v77befPvGJT+iLX/yi/vCHP0iSPw/ksssu08EHH6zRo0fru9/9rr9NlNT5HopEIvriF7+ooUOH6lOf+pQ+//nPd3uOd955R6Zpavz48aqrq1MsFtO6dev0l7/8Rd///vd1/PHHa5999tGYMWN05plnasKECdv93gBgZxmGoaamJkWjUXV0dKitrW2XQ4iySK5DI1/6tUb889cyU+8Fut6unStQ6jzeTL2tEf/8tUa8dAchBAAAAABUCToiqpTjOHr11Vf15S9/WcOGDVMoFNLmzZv18MMP63/+53/84773ve/pW9/6li6//HI1NDSopaVFr7zySrehuR/mnnvu0fe+9z3dcccdMgxDU6dOVXNzsy655BKdf/75uv766xWJRNTc3KznnntulzokrrrqKl100UX60Y9+JMMwtH79et16662SOjsmLr30Un3zm9/Uz3/+cwUCAW3YsEFPPvnkzr9QH+Khhx7SgQceqCuvvFKlUklPPPGEHnjgAX3sYx/b6XOk02kdf/zxmjZtmqLRqNavX69rr71Wa9askbGXIQUCWz3mP59ar2BAuvlzI5SIhvTSRltfvmeVUtndWyDakXfSeZ3ymzd01Yn76vdTD1Q0FFRz8956YfFfd/hvlU6ndeaZZ+qiiy5SKBTS6tWrdcUVV+xwfkF5aPek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EQAAAGiR67q65ZZbdMUVV+jLX/6yVq1apbfeeksvvfSSVq9eLWnHaoKRI0dqypQpxVBxzz336LjjjtOJJ56oZ599VtKO7ZFuvvnm4oubf/3rX3X44Yfr/vvvVyqVUi6XK3mnuCR95Stf0fvvv69f/epXxdtuvfVW/fa3v9XAgQOLL96uXr26+ILrxo0bNXnyZB1++OFaunSpjjjiCI0cOVLTp08vPn7z5s3F451zzjl64YUX9Lvf/a74/DvvvFO33367fvrTn7YaX3bmxRdf1J///OeS2+6///7if3/44YcaNmyYjj766OLj0um0Ghsbdeutt6pQKGjFihX63Oc+pxEjRujpp59WKBSS4zh6/fXXlU6ntW3bNq1bt06BQEAVFRXFlRWSVFlZqQEDBuiMM87QXXfdpUWb8/rQY2nOn9fqzVlj9I1D99Ndi7cU57n51c16eU2iZN7lW2z9+O87Lib900UfafYxfVRn5/TwWzuumXDba5v1rcP31+jehv65ydKFR+yvt2tt3fjKpuIxZj+3Vm9ffIiGRf36oD4tSfqgPq3rX94ouXklE5WaOGI/1dXVaenSpcrn89qyZUuzFTef9MILL+grX/lKMUQMHDhQI0aM0E033VTyuKuvvlrjx49XIBDQa6+9pttuu22Xv28zZszQ5MmTZRiGVqxYoe9///utPjYWiymTyRRjVJP6+nrFYrGdnueMM87QF7/4RXm9Xvl8PqXTaf3+97+XJPXu3Vunn366vva1rxWvT/Hkk0/qqKOO0umnn17yvdDk3HPP1eOPP67nn39e0o6v7wceeEAXXXRR8fui6etb2rHi4/7779f/+3//rxgievfurVdeeUUffvhh8RhNdvf7EAAAANgZQgQAAABa9corr2jRokU69NBDdfDBB+uoo47S1KlTddttt+n555/XQQcdJMMw9Mwzz5Q8r6qqSv379y/+ura2thghJGn79u2KRCI7PfewYcM0duxYPffcc83u69+/f0mI+KS6ujpFo1FJ0kEHHaStW7e2+mLpsGHDNHToUE2cOLHk9srKSvXr10/r1q3b6Ywtee+995rdNnnyZJ1++unq3bu3/H6/vF6v3n///ZLHrFmzpvgOfNd1tW3bNh144IFyHEevvfaapk6dqnvvvVeLFy/WkiVL9Oqrryqd3vECfzKZlOu6qqurk8fj0dChQ+X1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hAgAAAAAAAAAbcZ1XSUSCX2w3xEdvyVTs2Hy2jrkpPLOAIAQAQAAAAAAAHRGjz/+uM4888ydPmbhwoUaP358B020+x77zZP65sRxbb4l09zj+unl80bu9DF3ffEAPTxl6I5feCr1+MWn6KI5c9t0jnI59dRTtWDBgj16Tmf9GkHP4i33AAAAAAAAAEBXdvDBB+tnP/uZlixZou9973sdeu4pU6YomUzu9fMXLlzY4u3//d//3ep9uyPv9UsFe6+f35bOfWqVajasU7Sdz/PTn/5UY8eO1b333qvHH3+85L4f/vCHOuaYY/Tggw/qoYceaudJgM6HEAEAAAAAAADsg0mTJukPf/iDJk2apP3220/bt2/f6eM9Ho9ct222LKqvr9/nY9x8881avHhxyW37EjckfXyB6op9OkZbachIyfCIdg8RklRbW6vTTjutJET06tVLhx9+uLZt29YBEwCdEyECAAAAAAAA2EuBQEATJkzQzJkzFYvFdNppp+nRRx8t3v/Zz35Wt99+u6688kqdf/75OvDAA/Xd735XW7du1be//W2NGjVKhmFo7dq1uu+++/TGG2+UHN80TV199dU69thjlUwm9dhjj+npp58u3r9w4UJdffXVeu211yTteNF75syZGjdunHw+n9atW6c77rhD77zzTqsfQzKZbDVonHrqqbrkkkt00003adasWerdu7def/11/fCHP9RJJ52k6dOnKxgM6q9//avuvvtuua6rbFW1VFGh6qpK3fvlITrtoLAa03n9dNFHuv+Nf78YH/JX6r9PHqDTPxOWv9KjNz+ydNWLG7Riy79XUlx2TB/NHNdbptejp9+t13YrVzKfp0K6fsIA/eeh+ylfkB55a7s+nT+eOeczWl5r69G/V8ubSerxxx/Xs88+qwEDBujEE09UIpHQI488omeffbb4nNGjR2vOnDkaPHiwPvzwQz388MO68cYbdcEFF+iDDz5o9XP5j3/8QyeddJLGjBmj5cuXFz+HS5YsUZ8+fUoeW11drUsvvVSf+9zn5PP59NZbb+nOO+/Uxo0bSz7/5513nsLhsJYsWaK333672TnHjx+vadOmaciQIdq2bZuef/55PfLIIy3GLq/Xq29/+9s64YQTVFNTo7q6Oi1YsECPPfZYqx8T0Ba4RgQAAAAAAACwlyZMmKB169Zp/fr1+utf/6rTTz+9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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "authorplot(\"tomaarsen\", 5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Top authors by reactions\n", + "\n", + "Now, let's take a look at the Top 3 authors with the most reactions and see what they're talking about." + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total posts for `merve` = 72. Using up to 20 posts for graph.\n" + ] + }, + { + "data": { + "image/png": "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7NCzP+RJqj1WkrolOfXEOe11crsoeavKtXDBAr344ouh92TcuHGKjo7W8uXLNWzYMNlsNs2bN0+TJ0/e46qG4cOH67rrrtNNN92k9evX13rOarXKbrfL7XbLbrfrpptu0sCBAxUVFaU1a9boxRdf1Jo1ayQ1vOKkb9++mjBhggYOHFjr/f/oo4909dVXKyUlRWeffbYGDBigkSNHKjU1VW63W+vXr9eDDz4Yel3nn3++hg0bprZt22r79u2aNWuWPv744wZfDwAAAI4erIgAALRIPkeMcnoMa7q7k/ckaCqnxzBFFW1k/3bgKBOft0xRRZuU032oylLSJTNwaIHErvNjC1YqdeWM/fo7JSYmRqeeeqpef/31eiGEpNAH/4ZhaMKECaqqqtKdd94pq9WqO+64Q+PHj9ddd90VGt+uXTudeuqpGjdunNq1a6eHH35Ybdu2VU5Oju6880716NFD48aN05IlS7Rq1arQeddee63+/e9/a/LkyfrDH/6g8ePH69prr1V2dna9mupasWKFJk+erFGjRumaa66RJFVVVUmSbr/9dnXq1El///vfVVhYqH79+mnixIm69tprtXXrVvlcCXLZLbr99BTd8Xm2iqr82lHhqzfHhyt26uGBqbqoa4I+yNwZOj48vZWmLd8ps4FbtW44OUmDj4nXmNkblVPqU2qsXakxjgZfQ6TdohnDjtHi3Aqd8+Zq9dz6fxp//TDdcccdeuqpp0LjaoKau+66S6mpqRo/frzWr1+vzz77rMHrnnPOOVqyZEm9EEKSAoFAKMC48cYb1b9/fz355JPKy8vT8OHDNXHiRF199dUqKyvbw3e+vtTUVA0YMEDjx4+XaZpq1aqV/va3v+mVV17RwoULFRkZqV69fuuRdM4552j06NF6/vnntW7dOh177LG6++675Xa7NXfu3P2eFwAAAC0PzaoBAC1OUFJO96HVdyWHoYmsaXVoa/ehYskhcPSxe8vUaekUdfxliiJLtlQfNA9sz34jWD0+siRbHX+Zoo5Lp+53sJmamiqLxbLPD/x79+6t3/3ud5owYYLWrl2rVatW6YknnlBGRoaOP/7432oxDE2cOFFZWVn63//+p6VLl6p9+/aaPHmytmzZoi+++ELZ2dnKyMiodf358+drzpw5ysnJ0ZQpU7RmzRpdeuml+/Ua/H6/ysurX29RUZGKiork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t1rHTzjhBD377LO64IILVFFR0aw13XTTTXK73U0+z7Rp09SmTe0P9wsKCjRs2DBJUmGHPpIZkCzWJq9Fkgrv760RMzdozrqSWscn/7Gj4hwW3bGlr9JWzpRUv4F0zSoHwzBCQYJpmgfUQHpP/y3UuPTSS1VeHr6m2UC47b49U3P8HQUAAAAADSGIAACgEZSUlOx7UCN544039Omnn4Ye7/5hfWlyz2YLIfbJMFSW0lNxW7+SzWaTZdcqDdM0FQgE5PP5aoUOklRVVaXKyspGK6GoqOiQr2GxWPYZiDTEZrOFXhcQLsFgMLQ9E0EEAAAAgHAhiAAAtFgjR45Uv379dP3114eODRkyREOHDtUVV1whqfpD5ltuuUXnnnuuAoGA5syZo1atWikqKip0l73L5dKf//xn9e3bV5WVlXr//ffVt29frV+/Xi+++KKk6pUKM2bM0MyZ1Xf/z5s3T//4xz90+umn65RTTtGOHTv08ssv6/vvvw/V0qdPH40dO1bJyclasWKF5s6dq/vvv3+fKzoqKyvrfcBusVh0173j1PP005UcZdfWUq9e/6VA//6poNa4K3sl6pZTktU5IUJF7oA+XVOkcV/mSJJiI6x69PepOu/YOEVYLVq6vVJ//W+OVuRXHexbIJ8jRh6rS6+8+Kw++ugjffjhh6EP9V999VV9++23evPNN0PjExMT9eSTTyojI0OFhYV65ZVXtGDBgoOef968eXrwwQf13XffhY516NBBd955p4477jht3bpVzz33nH799VdJv62qGTdunMaMGaPOnTvr3nvvVUFBgW6++WZ169ZNLpdLWVlZevXVV7VkyZLQdadNm6Y5c+YoLS1Nffv21cKFC5WcnKysrCw9//zzoXFxcXH68MMPdf/999c6H2gqbrdbsbGxBx2qAQAAAMChokcEAOCodsUVV+jss8/WU089pdtuu02RkZGhngI1br75ZqWnp+vBBx/UPffco549e+rYY4/d57VHjhyp+fPna8yYMVq0aJH++te/KiYmRpLUpk0bPfzww/r222913XXX6ZNPPtGYMWMO+nUYhqHcMp+unb1JfV5bqX98t00PDmini7vGh8aMPrG1Jv6hvd78dYf6v75KV8/YoI1FvzWwnXJJZ7WOtOny6Rv0+6mr9ev2Sn00/FjFOw9thUW+JV7BYHC/tlq69tprtWDBAl13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HyZPMTRsv0REjcBqtcJgMCAUCsHr9aodDhERERERERGpjIkIIiIiahAajQZWqxWSJMHr9SIcDqsdEhERERERERE1AxxWTURERKfNYDDAYrEgFovB6XSyFRMRERERERERJTARQURERKdMEARYrVbo9XoEAgH4/X61QyIiIiIiIiKiZoaJCCIiIjolWq0WVqsVgiDA7XYjEomoHRIRERERERERNUNMRBAREVG9mUwmmEwmRKNReL1eKIqidkhERERERERE1EwxEUFERER1JooirFYrtFotAoEAAoGA2iERERERERERUTPHRAQRERHViU6ng9VqRTweh9vtRjQaVTskIiIiIiIiImoBmIggIiKikzKbzTCZTAiHw/B6vYjH42qHREREREREREQtBBMRREREdFyiKMJms0Gj0cDn8yEYDKodEhERERFRg4nqLAjaMhGypkPWGhEXJAhxGVI0CIN3P4yeYmgjPrXDJCJq8ZiIICIiolrp9XpYLBbE43G4XC7EYjG1QyIiIiIiOm1BSxrKOw+CJ6UvYnpr5Y2KDAFHqn7jEABRAgBowl7YyjajXeH3MPpK1AiZiKjFEwYMGMDeCkRERFSNxWKB0WhEKBSCz+djKyYiIiIiatHiADwp2TjYdTgCji6AIicSDXVy+HiTswAdCnJgK8uF0FjBEhG1QkxEEBERUYIkSbDZbJAkCV6vF+FwWO2QiIiIiIhOS1RnRXHva+BNzQYUBRDFUz/Z4YSEtTQXmflLoY14Gy5QIqJWjIkIIiIiAgAYDAZYLBbIsgyPxwNZltUOiYiIiIjotLhS+6G4zxgokq5+FRAno8gQ5Qgy85bAUbqp4c5LRNRKnUYKmIiIiFoDQRBgs9lgtVoRCoXgdDqZhCAiIiKiFu9g1lAUDpgARaNv2CQEAIgSFI0ehQMm4GDWhQ17biKiVojDqomIiNowjUYDm80GQRDgdrsRiUTUDomIiIiI6LQdzBqKkp5XVH4hNNI+3MPnLel5JQCgw95vG+c6REStABMRREREbZTRaITZbEYsFoPH44GiKGqHRERERER02lyp/Y4kIZpISc8roQ252aaJiOg42JqJiIiojREEAXa7HWazGYFAAC6Xi0kIIiIiImoVojorivuMAeJN/P42rqC4zxhEdZamvS4RUQvBRAQREVEbotVqkZycDEmS4Ha7EQgE1A6JiIiI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eustPfvss8rNzdUXX3whk8mkvn37avbs2ZKqD8AeMmSI1q1bJ6PRqLFjxwaf6D8Wb775pu68806Vlpbq+++/l8ViUefOnWVo21NTfqlfuLCzpFLWcJMGtLUpq8CjMq9fZfUIGerjF0eFFm0u1tTBabr3kx2KqLBr4ui/ae/evfr6668lSTNnztR//vMf3X333frwww/l9Xp12mmnadmyZXI6nZozZ47uuOMOGY1GrV27VtHR0UpPT5fH49GSJUs0atQobd68Wdu3b1dYWJjOOuss7dxZvV3Utddeq6KiIm3ZskWBQEDnnnuuioqK5Ha7D1X2Qf33v//V1KlTde2112rlypU6/fTT1bdvX96DADgqBBEAADQDRqNRdrtdZrNZpaWlKisrO/xNAAAAwG/07dtX77//vqqqquRyubRt2za99NJLWrJkSY0Plx988EHdddddeuGFF+T3+7Vq1Sq9+OKLwes///yzHn30Ud1444267rrr5PF4tGbNmuD1adOmaeLEiXrxxRe1d+9evfzyy+rUqdMx179o0SJVVFRo+PDhGjt2rMrLy7V9+3a98+k3krFPvcZYtbtUM34s1BtXnqK4KLOmrMjT0yvqvy3V4Yz/OFv/vChFs4adqnBDB635+SdNmjRJPp9PUvWBzxMmTNCYMWM0bdo0VVRUaMOGDVq6dKkkafr06SopKdH111+vpKQkud1ubdmyRe+++66k6i2axowZo9atW6uiokJr167VY489Jql6NcSIESOUkpIin8+nTZs2adKkSUcdHGRlZen555/XzTffrD/96U9atWqV3nvvPV111VUN8J0CcLIxZGRkEGMCAHAcCwsLk81mUyAQkNPprLGPKwAAAHCy84ZZtWHgo6Euo5ZumX+XufLoViMcr+677z6lpaXpL3/5S6hLAdDMcEYEAADHMavVqhYtWsjr9crhcBBCAAAAAL9jqXTLXOEKdRk1mCucJ0QIMWzYMHXo0EHJycm66qqrNGjQIC1ZsiTUZQFohtiaCQCA45DJZJLdbpfJZJLL5VJ5eXmoSwIAAACOW/aCtdrXpq9kNIW6FMnvk70gK9RVNIiuXbtqxIgRioqKUl5enl566SUtWrQo1GUBaIbYmgkAgONMeHi4bDabfD6fnE5ncD9ZAAAAAHUrsyZpS//7Q11G0KlfP6NI955QlwEAxw22ZgIA4Dhh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c845R8uWLdO1116rH3/8Ue+8844kKScnR+3atdOIESO0ZMkStWnTRv3799f48eO1bt06SdI//vEPzZkzR2effba+/PLL4Dypqal65plntGLFCr388suHfP2SdOWVV+qPf/yjzGazLBaLKioq9P7770uSEhISdMkll2j48OEqKiqSJM2dO1d9+vTRJZdcojfeeEPXXXedNm3apKlTpwbH/O1Kj7pkZWVp1qxZwdeanp6uoUOHavXq1Yf+eZ97rhYuXKiEhATNmTNHu3btkqSDrgoBAAAAAByfCCIAADhG//vf//T888/XaOvWrZsmT55c7zGio6PVsmVLbdx44GBAv9+vzZs3B0ON5ORkWSyWGn1KS0uDH84eyi+//BL8e3l5udxut2JjYyVVf5D9awjwq9/OcShz5szR4sWLg1//ej7FkCFDdMkllyghIUHh4eEym83aunWrJCkmJkbx8fHBMOX3OnbsqMjISC1YsKBGe1hYmNq0TpBhd0CB391jkFS7tbZOcRH6ZEtxjbbvd7s17sx4GQ3V172+gFbnHtiOylHu09Z95erUKkLa/22KMBu18IZOmr/eoQc/y1Yrc2StuX4NEKTqcxN27dqltLQ0SVJaWpq+/vrrGv2zsrJ0zTXXyGg0qm3btqqqqtKGDRuC151Op3bt2qW2bdvW+J688MILWrp0aXArpMP5/PPP9X//93+y2Wy65ZZbtG7dumCt7du3l8lkCgYkv7JYLHI6nZKqV0T8Ngipj/Xr19f6+pprrpF06J93cnKyJOm9997T/fffr4suukirV6/Wl19+qdzc3COqAQAAAAAQOgQRAAAco/Ly8lofisbHx9f4OhAIyGAw1Ggzm5vun+Hfbr3zq9/XczRKSkpqvfaBAwdq3LhxmjZtmtatWyePx6MRI0aoa9eukqSKirrPWvhVRESE9u3bp7vvvrvWtW2tz5bizqjVvtVRoRYRZiVGmw+7KqIhVPoC+nKHSxd3tOvlbyzy1nF4dlPwer368ccf9Yc//EFz5szR3r17D3tPaWlp8Gf26KOP6t1339X69ev1448/KjIyUj6fT2PHjq219VFZWZkkqbKyskFfw6F+3m63W5L01ltvaenSpTrrrLPUp08f3XLLLXr88ceDq4cAAAAAAMc3zogAAKAJFBcXB1cg/Kpjx47Bv5eWlmrfvn3q3LlzsM1oNKpT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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total posts for `akhaliq` = 80. Using up to 20 posts for graph.\n" + ] + }, + { + "data": { + "image/png": "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t7/9TbNmzZJpmtkr/WfPnq0HHnhAL7zwgkaUH6AlF8/S/xxf3uEYD515oO76/ITs7XevmKprP1um35w2XhuvnaH3r6jQf88ozt6/a2umOeO9avzuLB0zwaeXLjxUn1xfqecuOEQHjXB1OM/1s0dp7VXTtPHaGbrj1PH60efGaNnlhytlejt9bru2Zmp/Tp/5zGf0xz/+Uc8++6xuvfVWjRgxYq+vUU999NFHqq+v7/Bne8wxx6i+vl7r1q3rsO8xxxyj+++/X88//7wef/xx3X777dm/IwAAAACAwYsgAgAA9AvLsnTnnXfq4osv1i233KKZM2fq8ssvlyTFYjFVVVXp1FNP7fCYU089Va+++qqi0ajsdrtuu+02RSIRXX311brqqqsUjUZ12223dVj5MGvWLI0fP17f+c539rjCoTPxeFx2u112e9uKB5fLpXPPPVe33367Lr74YtWmuz5r4crDy/RebUTHPrBWf3i3QbefPH63YGFX3z9mjH748mad8Me1SmUy+s1pO8KNeVOKdO1nR+lnr2zWcX9cq5pgQhfPLJEkRfzlezrkblwul+bPn69f/OIX+ta3vqXS0lJdccUVXX78/nruued0yimnZG+feuqpeu655zrsM2LECP3whz/Uc889pwsvvFDXXnutXnvttT6vDQAAAADQ9wgiAABAv1iyZInee+891dXV6d1339X999+vY489Nnv/M888o8985jPZK/QLCwt1xBFHZD+wnjt3rmw2m375y19qw4YN2rRpk2699VaVlpaqsrIye5xYLKZf/vKXqq6uVnV1dZdqGzt2rL74xS9q7dq1ikbbZjc4nU7dcccdWrVqlT755BM1u0qkTNf6KS37uEV/eHebNjTH9es369QYTemo8b69PuZ/Xt2iNz4J64PGmH79zzodUe6Vy25Iki79VKkeXr5Nf1nRpI8Dcd3++lat3j5jIuYb06Wa2p/TwoUL9eGHH+qjjz7S448/rlmzZnX58fvrxRdf1LRp01RWVqaysjJVVFToxRdf7LBPcXGxHA6HXnvtNdXV1WnDhg164oknFIvF+rw+AAAAAEDf2nPjZAAAgF40a9YsnX/++Ro3bpzy8/Nlt9vlcrnkcrkUj8e1du1aVVdX6+STT9aiRYt04oknqq6uTu+//74kadKkSRo7dqyeffbZDsc1TVNjxuz4MH79+vVK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WVmZJOnkk0/WU089pdmzZ+uBBx7Q0hde0KjSYpl2Qz+dO1Yrv1GhTdfN0Av/fajmjPfu8/nNHufVi/99qLZ8u1KrvjlNP/rcGNm3/xq56/MTdOtJ4zSuwKXG787Su1dM3eNxUlZG9a0pbQ0ntaLFrkefeVHXXHONJk6cqHPPPTe7n9Pp1OWXX67Fixfr2Wef1W9/+1vNmDEje3/785szZ44eeughLV26VLfddptKSkqy91900UU66KCDsq/dySefnH18QUGBfvazn+m5557TQw89pNmzZ2fv83q9+v73v6+///3vev755/XQQw/plFNO2edrBADAcMKKCAAAgD7gDteqfPUSafUSpUyvIv5yxXxjlHa4lbHZZVhp2VNR5YW2yBOskaMP2i/1RCwWUywWk9PpVF5eXrZtUzweVzQaVTrdy/MwAGAYeu6553TKKado2bJlkqRTTz1Vzz33nCorKzvd/5NPPtG9996ra665RitWrJBlWbr22mv1//7f/9PGjRv1yCOPaMKECfJ4PLr11lslta2omDp1zx/078zlcuncc8/V7bffri32kdo27ku69cRxOnRkni55YoO2hpP6/CGFWjz/IB19/xqtD8Q7Pc5or1N/PXuS/rqiSVc+Xa2Di/O08NTxiqUzuu0ftfresk+0IRDXhZUjdcKDa5XuRifAiL9cn3yyVm+//baOPvpo/eEPf5AkXX311Zo4caJ+/vOfq7GxUUcddZRuu+02ffWrX9XmzZuzz++CCy7QzTffrFQqpWuuuUY/+tGPdNVVV6mqqkoHHHCADj/8cF1//fWSpNbWHas1LrzwQv3ud7/TvffeqzPPPFPf//73dc455ygUCumrX/2qJkyYoAULFqilpUVjx46Vy+Xq+pMCAGAYIIgAAADoY45EWP5ta+XftjbXpXRbe9um1tbWbMumnds2tbcTAQB034svvqhLL700uyqhoqJCP/vZz/YYREjSE088oSOOOEI33nijUqmUPvjgA/3tb3+T1BYix+NxOZ1OBQKBbtfjdDp1xx136OOPP1b9AcdpRJ5N500v1ozfrtTWcFubvrvfrtfxB/p13rRi3fTqlk6P89VZJdoSSuqGFz+RJH3UFNcob61+fOxY/fIftQrFLYUTaaUzbasdusxKK+YbI/+2tdq0aZM+/elPS5JKS0t16qmn6itf+YoaGxslSYsXL9bhhx+uU089Vffdd1/2+f3mN7/RmjVrJEk333yz/vSnP2ny5Mlau3ZtNmjv7LV7/vnn9fLLL0uS7rvvPp111lmaPHmy3nnnHZWWlmrdunX68MMPJbW13QIAAB0RRAAAAGCfLMtSJBJRJBKRy+WS2+1WQUGB0um0otGoYrGYMplMrssEgEGlpaVFb775pk4++WQZhqE333xTwWBwn4+77bbb9NBDD8myLF188cW9Vk8ikdDHH38sSUo73ZpekieHzdBbl03psJ/LblNTtG1l3MbrdrQ/enRVk7699BMdUpy32+yHtza3yuuya4zfqc3BjrOHxvqdeuOSHee4459btfCfu3+YbyijtMPd9r1hZH/vHHjggbLb7XrooYc67O90Oju8nqlUSmvX7rgo4JNPPlEoFNKECRM6bO/M+vXrs9/HYjGFw2EVFRVJkp588kn99Kc/1cEHH6x//etf+sc//qFVq1bt9XgAAAw3BBEAAADolng8rng8LofDIbfbrfz8fOXn52fbOaVS3bi6FQCGueeee05XX321JOnXv/51lx4zadIk5eXlybIsFRcXq6mpaa/7t39gv/OsH4dj948Ddl7lljHsyjftSlkZHf/H3dsntSbbgohj/7DjA/xQYv/a9m0NJTscJxDb8++RzPbh2ePHj88O9Xa73Uqn0/r617++W+vA3hpm3dnvtvbX8+2339Y555yjI488Up/61Kf0q1/9So8//rjuvffeXjk3AABDAUEEAAAA9ksqlVIoFFI4HJbb7c62bkomk4pGo4rHO+8dDgDY4e23386GAu+8884+9/f5fFqwYIH+/Oc/q7i4WN///vd12WWXZUOEVColm83W4THNzc2SpOLiYoXDbTOJDjrooL2ex8iktaIuIofN0EiPQ2/WtHa634bm3X/Wf9gY0xcPLeyw7Yix+QrF09qyy2oISUpnOj9Op3VZaY0bN06HH364/vKXv0hqG/xtt9tVWFioFStW7PGxDodDhx56aHb1w7hx4+Tz+bRx40ZJnb92XdXS0qKlS5dq6dKlWrFihb7+9a8TRAAAsJP9+w0LAAAAbJfJZBSJRNTU1KSWlhZlMhn5/X6NGDFCHo9nvz/UAYDhwLIsXXTRRbroootkWfue2nzdddepoaFBDz30kH7729/KZrPpiiuuyN6/detWHXjggRo3bpz8fr/sdrs2b96s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e091dXV69913df/99+vYY4/tcF6n06k77rhD69at0/Lly/V///d/qqio0G233aaNGzfqzTff1HvvvZf9IH9Xn/rUpzT1oIm6cMlHeqU6pI0tCb3wcVAvrQ92+zX48/JGnTe9eMdzPqhQeXabHl/bnN1m2m1a8MIn+teWVr1fF9U3nq7WEeVezRrtkSR94/AyLVrRpD/8u04fhh169NFH9dprr+krX/lKtt4xY8bo5ptv1scff6yVK1fqvvvu67SeJUuW6LXXXtPWrVvV1NSkbdu2afHixfr4449VW1urv//973r77bd3e03r6+t1991365NPPtGyZcv097//XfPmzeuwz1tvvaUnnnhCW7Zs0aJFi9TS0pJ9jV999VVJyq7QkNr+/J9//vluv6YAgIHP6XTKZrMRRAAAAAAYNAgigEHKMIwOt5955plsq6aioiIdccQReu655zrss379+g63m5qaVFRUlL09a9Ys/epXv9LixYv1zDPP6MYbb1RBQYFcLld2n2g0qi1btmRvBwIB1dXVdQg8AoGACgsLO637oIMO0tZAWOubYp3e3x2LVjTqgCKXPj2mLVQ4Z9oIPb42oEhyR5uKZDqj/9RGsrc/aoqrOZbSIcVtLacOKc7T2zVhGcoo7XBLklauXKnx48dLksaPH6/6+noFAoHsMdauXdtpPR988EGH2zabTf/1X/+l+++/X0888YSeffZZfeYzn1FZWVmH/dasWdPh9urVq1VeXt6hvdWuf3aBQCD7Z5dMJvXiiy/q1FNPlSQdfPDBmjhxopYuXdppnQCAwc00TaXTaaVSqVyXAgAAAABdQhABDFLjx49XbW1t9vYLL7yg0aNHa8qUKTrhhBNUW1urFStWdHjMrh9YZDKZbKBRVlaWver/xz/+sb7+9a/r17/+tSTJ4dgx137XAcyZTKbT4+78IfrO4vF4N5/pnm2LpLR0XYvOnVasEo9DJxxYoIeXN+738TL7GJ69L9FotMPtr3zlKzrrrLO0aNEiXXfddbrkkkv0zjvvdHg9u2pvf3ZSWxD1qU99SiNHjtQpp5yid999V3V1dfv3RAAAAxptmQAAAAAMNgQRwCA0c+ZMTZo0KduSR5KCwaBef/11nXrqqfvVlufQQw+VYRi65557tGbNGtXU1Ki4uHjfD+ym9evXa1SRV5NG5O175y546P1GffmwIv135UhVN8f19ubWDvc77YZmbm/DJEkHjXCpMM+hDxvbVmR82BjT4eVeSZJhtYUsFRUV2rhxo6S2FlilpaUdVo7sOuNhTyoqKvT6669r2bJl2fZM5eXlu+132GGH7Xa7pqamWwNIN2zYoA8//FBf+MIXdPzxx++2GgYAMDTY7XY5HA6CCAAAAACDCkEEMMA5nU4VFRVp5MiROvjgg3X++efrpptu0htvvKEXXnihw77PPPOMTjrpJE2YMKHbbXk2b94sp9OpM888U6NHj9aJJ56o008/vTefiiTp/fff178/2KQHzjxIx070aXyBqeMP9Ou4A/z7dbyX1wcVilu6fvYo/aWT1RCJtKVbThynT432aEaZW3d9foLe2RzOtmu66606nTtthC6aVaoDRrh19tln6+ijj9YjjzwiSfr3v/+tLVu26Lvf/a4OPPBAVVRU6Ktf/WqXatu8eXPbTIypUzV+/Hhdd911HQKNdqWlpbryyis1btw4HXfccTrzzDP1t7/9rduvxTPPPKNzzz1XhmHotdde6/bjAQADn2maymQyBBEAAAAABpXu9wcB0K+OOOII/e1vf1MqlVIoFNLHH3+sO++8U0uXLlUmk+mw77///W81NTWpurpajY3da1H08ccf6+6779Y555yjSy65RMuXL9fvf/973Xjjjb35dCRJC267U5dc+z39v9MPkMdp04ZAXD/7v837dayMpEUrG3XtZ0fpkZW7P+do0tJv3qzT704/QKN9Tr35SVhXP7cxe/+zH7XoxmU1+sYRo1V+wtdVu2WLbr31Vr3//vuSJMuy9IMf/EDf+c53dM8996i2tlb33nuvbr755n1+CPTQQw9p9OjRuu222xSLxfT000/r9ddfV35+fof9XnjhBZmmqd/+9reyLEtLlizRU0891e3X4qWXXtI3vvENvfzyy0omk91+PABg4KMtEwAAAIDByKisrMzsezcAg0FeXp4effRR3XbbbQP6ivik6dWauT/tteP9+tTxKvY4dMGSjgOdz502Qv9zfLkOvGN5l44zperHciTC+9yvoqJCd955p84///wOg7v3x8KFC7Vu3TrdfffdPTqO1Dbn4+GHH9YVV1yhjz76qMfHAwAMLIZhqLi4WOFwWLFYLNflAAAAAECXsSICGAIMw1BBQYHmz5+vcDis119/Pdcl7ZUzEZYjHlLK5evRcXwum6aUuHXWlBG6YMnHPTqWIx7cYwhx1FFHKRqNqqamRmPHjtVVV12lFStW9DiE6C12u11+v19f+9rXtGbNGkIIABiiTNOUYRisiAAAAAAw6BBEAENAaWmp/vrXv6q+vl633nprt4Yc54q/foWaxh4h2ez7fYw/nzVJs0bn64/vNeiV6tD+F2Ol5a9fuce7PR6PLrvsMpWVlamlpUX//ve/dc899+z/+XpZRUWF7rjjDm3atEk/+clPcl0OAKCPmKapZDI5KH7PAwAAAMDOaM0EICei3tH6aM63c11G1sGv/1Lu8NZclwEAwB4VFxcrGo0qEonkuhQAAAAA6BZbrgsAMDy5w7XyBKqlXF/VaaXlCWwghAAADGhOp1M2m422TAAAAAAGJYIIADlTUl0l2XL8Y8hmV0n1K7mtAQCAfTBNU+l0WqlUKtelAAAAAEC3EUQAyBl//Ur56lZKVjo3BVhp+etW7HU+BAAAA4FpmqyGAAAAADBoEUQAyBlDUvnqx2RLJ6RMP7doyliyW0mNXf2YjP49MwAA3WK32+VwOAgiAAAAAAxaBBEAcsqZCKl81WLJ6OcfR4ZNkzc+q9J8p+x2e/+eGwCAbjBNU5lMhiACAAAAwKBFEAEg5wrrlmv02sf79Zyj1z4uc8ObkqSioiK5XK5+PT8AAF1FWyYAAAAAgx1BBIABoWTjazvCiL5q07T9uGPWPK6Sja8pnU4rEAgoHo/L7/crPz+/b84LAMB+MgxDTqeTIAIAAADAoObIdQEA0K5k42tyxlpUM3W+LLsp2XqxZZKVli2dUPmqxSqsW97hrlAopGQyKa/XK6fTqWAwKMvq55kVAAB0wjRNGYZBEAEAAABgUCOIADCgFNYtV35gg2qmzFOorEKy0j0LJLY/3t+wWmNXPyZnItzpbrFYTKlUSn6/X0VFRQoGg0omk/t/XgAAeoFpmkqlUgTkAAAAAAY1o7KyMpPrIgBgVxlJwdIKNUycq0jRxO4HEtv39wQ2qKT6FfnrV8rowsMMw5Df75fT6VRra6ui0eh+PgMAAHquuLhY0WhUkUgk16UAAAAAwH5jRQSAAcmQVFC/UgX1KxX1jlbj+NkKllYo5fK37WClZWhHjpqRkQ0qHPGg/PUrVbzpDbnDtd06byaTUUtLizweT7ZVUygUUiZDZgsA6F9Op1M2m422TAAAAAAGPVZEABhUUqZXEX+5Yr4xSjvcytjsMqy07Kmo8kJb5AnWyLGH9kvdZZqmfD5fNpxIp9O9clwAALoiPz9feXl5amxszHUpAAAAANAjBBEAsBc2m01+v18Oh0OhUEjxeDzXJQEAhomioiIlk0mFw70TsAMAAABArthyXQAADGSWZam5uVmxWEx+v19erzfXJQEAhgGbzSaHw0FbJgAAAABDAjMiAKALwuGwUqmUvF6vHA6HgsGgLMvKdVkAgCHK5XIpk8kQRAAAAAAYElgRAQBdFIvF1NzcLJvNpqKiIjmdzlyXBAAYokzTVDKZzHUZAAAAANArCCIAoBtSqZQCgYCSyaQKCgrk8XhyXRIAYIgxDENOp5O5RAAAAACGDIIIAOimTCajYDCoSCQij8ejgoICGYaR67IAAEOEaZoyDIO2TAAAAACGDIIIANhPkUhELS0tcjgcKioqksPB2B0AQM+ZpqlUKsUsIgAAAABDBkEEAPRAMplUIBCQZVkqLCxUXl5erksCAAxypmnSlgkAAADAkEIQAQA9ZFmWmpubFYvF5PP55PP5cl0SAGCQcjqdstlstGUCAAAAMKTQRwQAekk4HFYymZTP55PD4VAwGFQ6nc51WQCAQcQ0TVmWpVQqletSAAAAAKDXsCICAHpRPB5XIBCQJBUWFso0zRxXBAAYTGjLBAAAAGAoIogAgF6WTqfV3NysZDKpgoIC5efn57okAMAgYLPZ5HA4aMsEAAAAYMghiACAPpDJZBQMBhUOh+V2u1VQUCDDMHJdFgBgAHO5XMpkMgQRAAAAAIYcgggA6EPRaFQtLS2y2+0qKiqSw8FoHgBA50zTVDKZzHUZAAAAANDrCCIAoI8lk0k1NzcrnU6rsLBQbrc71yUBAAYYwzDkdDqZDwEAAABgSOLSXADoB5ZlqaWlRfn5+fJ6vXI6nQqFQspkMrkuDRhWkqZXUX+5Yr4xSjvdyhh2GZm07Mmo8kJb5A7WyJkI57pMDENOp1OGYdCWCQAAAMCQRBABAP2otbVVyWRSPp9PhYWFCgaDSqfTuS4LGNKi3tFqHD9bwdJpSrl8bRuttAztCAIzMiSbXZLkiIfkr1+h4k1vyB2uzUXJGIZcLpdSqZQsy8p1KQAAAADQ64zKykouxwWAfma32+X3+2W32xUKhWjFAfSyjKRgaYUaDpirSOFEyUpng4Yu2b6/J1Ctkuoq+etXinHz6EvFxcWKxWJqbW3NdSkAAAAA0OsIIgAgh3w+n/Ly8hSJRPjwCeglSdOnminzFCqrkCxLsvVgJNb2QMJXt1Llqx+TMxHqvUKB7RwOh4qKihQIBJRKpXJdDgAAAAD0OoIIAMixvLw8eb1epVIpBYNB2nIAPdBcNl01U+fLspvdWwGxL1ZatnRC5asWq7Buee8dF5CUn5+vvLw8NTY25roUAAAAAOgTPbhEEADQG2KxmJqbm2Wz2VRUVCSn05nrkoBBqWHCMdpUeaEsh6t3QwhJstllOVzaVHmhGiYc3bvHxrBnmiZDqgEAAAAMaQQRADAApFKpbEuOgoICeTyeXJcEDCoNE45R7eQvtd0w+ujtzfbj1k4+gzACvcZms8nhcDArCAAAAMCQRhABAANEJpNRS0uLIpGI8vPz5ff7ZRiMxwX2pbls+o4Qop/UTj5DzWXT+/WcGJpcLpcymYySyWSuSwEAAACAPkMQAQADTCQSUUtLi5xOp4qKimS393KLGWAISZo+1UydL2X6ebZKxlLN1PlKmt7+PS+GHNM0lUwmlckwtg0AAADA0EUQAQADUCKRUCAQkGVZKioqksvlynVJwICTkVQzZV7bYOq+ase0J4ZNlt3U5inzxMfH2F+GYcjpdNKWCQAAAMCQRxABAAOUZVlqbm5WLBaT3++X18uV18DOgqUVCpVV9P5g6q6y2RUsm6ZgaUVuzo9Bz+l0yjAMBlUDAAAAGPIIIgBggAuHwwoGg8rLy1NhYaFsNn50A5LUMHGuZPVzS6ZdWWk1TDw2tzVg0HK5XEqlUrJy/fcYAAAAAPoYn2YBwCAQj8cVCARks9lUVFQk0zRzXRKQU1HvaEWKJkq5DuZsdkWKDlDUOyq3dWBQMk2T1RAAAAAAhgWCCAAYJNLptAKBgJLJpAoKCuTxeHJdEtBlJSUluuGGG/Too4/qhRde0KJFi/TNb35Tfr+/y8eYMWOGqqqqlJ+fr8bxsyUr3YcVd4OVVuP4OT06xLXXXquqqiqdddZZvVQUBjqHwyGbzcZ8CAAAAADDAkEEAAwimUxGwWBQ4XBYHo9HBQUFMgwj12UBezV69Gjde++9Gjt2rG666SZdcMEFWrhwoWbNmqW77rpLPp+v28cMlk7L3WyIXdnsPZoTcdRRR2nKlClqaGjoxaIw0LlcLlmWpVQqletSAAAAAKDPEUQAwCAUjUbV0tIih8OhoqIiORyOXJcE7NG3vvUtpVIpfec739H777+v+vp6vf3227r++us1cuRIfe1rX8vu63Q6ddlll+mRRx7R0qVL9ec//1mnnXaaysrKdMcdd0iSnn76adX9+HO66/MTJEmm3dDNJ5Rr7VXTtPnblXrm/EM0c9SOFUNzxnvV+N1ZOnGSX69+9TBt/nallv7XoZo8Mi+7zw1HjdYrF0/uUPfXP12id6+Y2uE4L/73odp03Qytv2a6nr3gEJX729qkpVx+pcwdA+XvvPNOXXbZZR2OV1BQoBdffFHTp0/Pbhs5cqSuvvpq/c///I/S6QGywgP9grZMAAAAAIYTgggAGKSSyaQCgYAsy1JhYaHy8vL2/SCgn/l8Pn3mM5/RE088sduHroFAQC+99JLmzp2b3fa9731Pxx13nO68805ddNFF+t///V9Fo1E1NDToRz/6kSTpy1f/WIfduVzfW/aJJOknc8fqi4cW6hvPbNRxD6zVhua4Hv3KQSrM67hi4qdzx+pHL9fohAfXals0pb/MmyRHF98J2Q3poTMP1OufhHXMH9bolIc+1J/e26aMMtl9Iv7y7Pe7Pi9Jmjt3rrZt26bly5dLkgzD0Pe+9z098sgjqq6u7lohGBJsNpscDgdtmQAAAAAMGwQRADCIWZal5uZmRaNR+Xy+/WpxA/Sl8vJy2Ww2bdy4sdP7N27cKL/fr8LCQpWXl2vu3Lm67bbb9I9//EO1tbX6z3/+o6qqKlmWpWAwKEmqTeerPhRXKG7J47Tp4pkj9eOqzXppfVAfNMZ0zXMbFUtZumB6cYdz3faPrXqlOqQ1DTF94+lqleQ79flDCrv0PHwuuwryHHphXYuqmxP6sDGmv65s0uZgsm0HK62Yb0x2/6qqKo0cOVLTpk3Lbjv++OP18ssvZ2+fe+65SqfTWrJkSZdqwNBhmqYymYySyWSuSwEAAACAfkEQAQBDQGtrq4LBoFwul4qKimS3D5De+cB2XZllctBBBymdTuv999/f635pp1vG9pUIEwtdMu02vVXTmr0/ZUn/qY3okJEdVwm9syWc/b45lta6ppgOKe7aSqLmWFp/Wd6oR79ykB6eN0mXfbpEZfk7WqIZyijtcGdvt7S06F//+pdOOOEESdKoUaNUUVGhZcuWSZIOOeQQnXXWWbr11lu7dH4MLS6XS8lkUplMZt87AwAAAMAQQBABAENEPB5XIBCQJBUWFso0zRxXBEibN2+WZVmaMGFCp/dPmDBBwWBQzc3NXW5TkzF6P2izMhntmpU4bR03XPXsRp3y0Ad6pyasL08u0tuXTdWnx+yYRZHZZXj2smXL9LnPfU52u13HH3+8Pv74Y23YsEGSNG3aNBUWFuqRRx7RsmXLtGzZMo0aNUpXXHGFFi1a1OvPDwOHYRhyOp3MhwAAAAAwrBBEAMAQkk6n1dzcrEQioYKCAuXn5+e6JAxzwWBQ//73v/WlL31pt3CsqKhIxx9/vKqqqiRJ69evl2EYmjFjRqfHSqVSkiSHYWW3VTfHFU9ZOqJ8x991h02aOcqjD7bFOjz+02N27FPgsmtSkUsfNrbt0xhJqTTf2WH/ijKPdrWiLqo73qzTqX/+UGu2RXXWlBHZ+wyr47Dp119/XaZp6vDDD9cJJ5yQXQ0hSS+++KK+9rWv6ZJLLsl+NTQ06JFHHtENN9zQ6fPH0OB0OmUYBvMhAAAAAAwrBBEAMMRkMhmFQiGFw2G53W4VFBTIZuPHPXLn17/+tZxOp2677TZNnz5dJSUl+sxnPqPbb79d27Zt0/333y9Jqqur09KlS3XDDTdozpw5GjVqlGbMmKFjjz02e79lWTpuyjiN8DiV77QpkrT0wLvb9NO5Y3XcAX4dWpynO06dILfTpj+/39ihju/MGa1jJvg0eWSe7v7CBDVFU3r2wxZJ0uubwhrpcejqI8o0sdDU12aN1AkH+rOPHV9g6oefG6NPj8lXud/UsRN9OrAoLxtkZGTInop2OF8sFtM//vEPffWrX9X48eM7zIcIBoOqrq7u8JVOp9XU1KRPPvmk1/8MMHC4XC6lUilZlrXvnQEAAABgiHDsexcAwGAUjUaVTCbl9/tVVFSkYDDIYFTkxObNm3X55Zfroosu0o9//GP5fD41NTXp9ddf14MPPqhQKJTdd+HChbr00kt1zTXXyO/3q76+Xg8//LAkadu2bfrjH/+o6846SzcV+vXIyiZ985mN+tkrm2UzpHu+OEFe0673aiM6+5F1aol3XKHws1c26xcnlOvAIpdW1kd13mPrlbTaevR/2BjTd5Z+oms/O0rXzxmlpz9o1t1v1+m/K0dKkqJJSwcX5+mcihEqcjtU15rU/f9p0B/f3dZ2cJtdeaEtuz33ZcuW6dZbb9X777+v+vr6vnh5MciYpqlYLLbvHQEAAABgCDEqKyuZkgcAQ5hhGPL7/XI6nWptbVU0Gt33g4ABLGl6tWbuT7u8/5zxXj153iE6YOH7Cu4STvSmKVU/liMR3veOGLYcDoeKiooUCASyrcYAAAAAYDigVwcADHGZTEYtLS2KRqPyer3y+/0ydp3KCwwizkRYjnho3zv2I0c8SAiBfTJNU5ZlEUIAAAAAGHYIIgBgmGhtbVVLS4ucTqcKCwtlt9tzXRKw3/z1KySr71Y3dIuVlr9+Za6rwCDgcrmUSCRyXQYAAAAA9DuCCAAYRhKJhJqbmyVJRUVFcrlcuS0I2E/Fm96QbF0L017fFFbxLf/pu7ZMNruKN73eN8fGkGGz2eRwOAgiAAAAAAxLBBEAMMyk02kFAgHF43H5/X55vd5clwR0mztcK0+gWrKs3BZipeUJbJA7vDW3dWDAM01TmUyGIAIAAADAsEQQAQDDVCgUUigUUl5engoLC2Wz8SsBg0tJdZWU67+3NrtKql/JbQ0YFFwul5LJpDKZTK5LAQAAAIB+x6dOADCMxWIxNTc3y2azqaioSE6nM9clAV1W0LBKI5rWysjkaFWElZa/bgXzIbBPhmHI6XSyGgIAAADAsEUQAQDDXCqVUiAQUCqVUkFBgTweT65LAvbJ4XBoRFGRJm9aKls6LvV3GJGxZEsnNHb1YzL698wYhJxOpwzDUDwez3UpAAAAAJATBBEAAGUyGbW0tCgSicjj8cjv98sw+HgVA5Pb7VZhYaEsy1KkoUZjVy6WjH5+S2PYVL5qsZyJcP+eF4OSaZpKpVKycj3TBAAAAAByhCACAJAViUTU0tIip9OpoqIiORyOXJcEZBmGkR2wHo1G1dzcLMuyVFi3XKPXPt6vtYxe+7gK65b36zkxeLlcLtoyAQAAABjWCCIAAB0kk0kFAoG2D3gLC5WXl5frkgA5HI7sHJOWlha1trZ2uL9k42s7woi+atO0/bhj1jyuko2v9c05MOQ4HA7ZbDaCCAAAAADDGpe6AgB2Y1mWmpub5fV65fP55HA4FA7Tgga54Xa7lZ+fr1QqpWAwuMf2NiUbX5Mz1qKaqfNl2U3JZu+9Iqy0bOmEylctZiUEusU0TVmWpWQymetSAAAAACBnCCIAAHsUDoeVTCbl8/myV6LT4xz9xTAM+Xw+uVwuRSKR3VZBdKawbrnyAxtUM2WeQmUVkpXuWSCx/fH+htUau/oxZkKg22jLBAAAAACSUVlZmcl1EQCAgc1ut8vv98tmsykUCvGhGvqc0+mUz+eTYRgKBoPdvpo8IylYWqGGiXMVKZrY/UBi+/6ewAaVVL8if/1KMb4d3WWz2VRcXKxgMKh4PJ7rcgAAAAAgZwgiAABdsj9XpwP7w+PxyOPxKJlMKhQK9XgVTtQ7Wo3jZytYWqGUy9+20UrL0I63QBkZ2aDCEQ/KX79SxZvekDtc26NzY3jLy8uT1+tVY2OjMhnecgMAAAAYvggiAADd0t6vP5lMKhgM8uEaeo1hGPL7/XI6nYpEIopEIr1+jpTpVcRfrphvjNIOtzI2uwwrLXsqqrzQFnmCNXLQfgm9pKCgQJLU0tKS40oAAAAAILcIIgAA3eZ0OuX3+5XJZBQMBpVKpXJdEga5nrZiAgaikSNHqrW1VdFoNNelAAAAAEBO2XJdAABg8EkmkwoEArIsS4WFhXK73bkuCYOYx+NRQUGB0um0AoEAIQSGBNM0ZRgGsyEAAAAAQJIj1wUAAAYny7LU3Nys/Px8eb1eORwOhUKhXJeFQaQ/WjEBuWKaplKpVI9nnAAAAADAUMCKCABAj7S2tqqlpUWmaaqoqEh2uz3XJWEQcDqdGjFihBwOh1paWgghMOS4XC4lEolclwEAAAAAAwJBBACgxxKJhJqbmyVJhYWFcrlcuS0IA1p7K6ZUKqWmpiZaMWHIcTgcstlsBBEAAAAAsB1BBACgV7T3908kEvL7/crPz891SRhgbDabCgoK5PF4FIlE1NLSokwmk+uygF5nmqYsyyJkAwAAAIDtmBEBAOhVoVBIyWRSXq9XTqdTwWCQHumQ0+mU3+9XJpNRS0sLH9BiSDNNk9UQAAAAALATVkQAAHpdLBZTc3OzbDabioqK5HQ6c10ScmjnVkyBQIAQAkOazWaT0+kkiAAAAACAnRBEAAD6RPuHzqlUSgUFBXK73bkuCf2MVkwYjkzTVCaTIYgAAAAAgJ3QmgkA0Gfa2/B4PJ5sq6ZQKMSH0cMArZgwXJmmqWQyyc85AAAAANgJQQQAoM9FIhGlUin5fD4VFhYqGAwqnU7nuiz0kfz8fHk8HsXjcYInDDumaaq1tTXXZQAAAGCASZpeRf3livnGKO10K2PYZWTSsiejygttkTtYI2cinOsygT5DEAEA6BeJREKBQEB+v19FRUUKhUKKx+O5Lgu9yGazye/3y+FwKBwOKxqN5rokoF+ZpinDMGjLBAAAAElS1DtajeNnK1g6TSmXr22jlZahHRdrZWRINrskyREPyV+/QsWb3pA7XJuLkoE+Y1RWVnKZIgCgX3m9XrndbkWjUYXDXPExFJimKZ/Pp0wmo2AwqFQqleuSgH7X3oIuEAjkuhQAAADkSEZSsLRCDQfMVaRwomSls0FDl2zf3xOoVkl1lfz1K2X0VbFAPyKIAADkRF5enrxer1KplILBoCzLynVJ2E+0YgLajBgxQvF4nNZMAAAAw1TS9KlmyjyFyioky5Jstv0/2PZAwle3UuWrH5MzEeq9QoEcIIgAAOSMw+GQ3++XYRgKBoMMNB5kdm7F1NraSismDGsOh0NFRUVqbm7mZxkAAMAw1Fw2XTVT58uym91bAbEvVlq2dELlqxarsG557x0X6Gc9iOUAAOiZVCqlQCCgZDKpgoICeTyeXJeELjJNU0VFRbLZbGpubiaEwLBnmqYsyyKEAAAAGIYaJhyjTZUXynK4ejeEkCSbXZbDpU2VF6phwtG9e2ygHzGsGgCQU+0zBTwejzwej5xOp4LBIO19BjBaMQG7M02TIdUAAADDUMOEY1Q7+UttN4w+uuZ7+3FrJ58hSSrZ+FrfnAfoQwQRAIABIRKJKJlMyu/3q6ioiIHHA9DOrZjC4TCrIDBsJE2vov5yxXxjlHa6lTHsMjJp2ZNR5YW2KD+8RU6nk/8nAAAAhpnmsuk7Qoh+Ujv5DDljLbRpwqBDEAEAGDCSyaQCgYD8fr8KCwsVDocVi8VyXRbUdrW3z+dTJpNRc3MzIRGGvKh3tBrHz1awdJpSLl/bRistQztWAGVkZJfef5gMy791hUZsel3ucG0uSgYAAEA/Spo+1UydL2WsvlsJ0ZmMpZqp85UfWC9nItx/5wV6iGHVAIAByev1yu12KxaLKRQK5bqcYY1WTBguMpKCpRVqOGCuIoUTJSvdvR6/2/f3BKpVUl0lf/1KGX1VLAAAAHImI6m68mKFSg7r/ZkQXWGl5W9YrQnv/ZH3mxg0CCIAAAOWy+WSz+dTOp1WMBhUOp3OdUnDyq6tmFidgqEsafpUM2WeQmUVkmVJth5c1bY9kPDVrVT56sfkTBCmAgAADCUtpRXaOPPiXJehCe8+oIL6lbkuA+iSflw3BABA98TjcQUCAUlSYWGhTNPMcUXDh2maKioqks1mU3NzMyEEhrTmsun64KgFbVe0ST0LIaTsVXGhksP0wVEL1Fw2vYcVAgAAYCBpmDi37eKVXLLSaph4bG5rALqBIAIAMKCl02k1NzcrmUyqoKBA+fn5uS5pyPN6vSooKFAikVAgEGAeBIa0hgnHaFPlhbIcrt5fVm+zy3K4tKnyQjVMOLp3jw0AAICciHpHK1I0secXr/SUza5I0QGKekfltg6giwgiAAADXiaTUTAYVDgcltvtVkFBgQyDTpi9zW63q7CwUHl5eQqFQsyDwJDXMOEY1U7+UtuNvhowuP24tZPPIIwAAAAYAhrHz5astO76/AQ1fneWbj953G773HbiODV+d5bu+vyEvi3GSqtx/Jy+PQfQSwgiAACDRjQaVUtLi+x2u4qKiuRwOHJd0pDhcrlUWFgowzAUCARoxYQhr7ls+o4Qop/UTj6DNk0AAACDXLB0WnYlbU1LQmceVqQ8x44L5Vx2Q2dNLdInLfEencfelWvvbHYFSyt6dB6gvxBEAAAGlWQyqebmZqXTaRUWFsrtdue6pEHP6/XK7/crkUhkX1tgKEuaPtVMnS9l+rmvb8ZSzdT5Spre/j0vAAAAekXS9Crl8mVvL6+LaHMwqS8cUpjd9oVDC1UTTGhFXTS7zbQbuvmEcq29apo2f7tSz5x/iGaO8mTvnzPeq8bvztLxB/r10kWTVfudSh05zquJhab+fNaBWnPVNG28boaWXXioPjdhx/klKeXyK8X7SwwCBBEAgEHHsiy1tLQoGo3K6/XK5/PRqmk/tK8soRUThpOMpJop82TZzb5rx7Qnhk2W3dTmKfPE/2kAAACDT9Rfvtu2h1c06rzpxdnb508v1l+WN3bY5ydzx+qLhxbqG89s1HEPrNWG5rge/cpBKszrOKPsR8eO0c9f2azP/n61VtVHlW/a9eLHQX150Uea+8BavbQ+qIfnTdJYv7PD4yKd1AUMNAQRAIBBq7W1VS0tLTJNU4WFhbLbe3nQ7BDmcrlUVFQkSbRiwrASLK1QqKyi9wdTd5XNrmDZNJbQAwAADEIx3xjJ6riC/NGVTTqi3Ktyv6lyv6nDx3r16Kqm7P0ep00XzxypH1dt1kvrg/qgMaZrntuoWMrSBTsFGJJ0y2u1eqU6pOrmhJpjaa2qj+rB97Zp7baY1gfiuvm1WlU3x3XqQYU7HmSl2+oCBjiaawMABrX2dkJ+v19FRUUKhUKKx3vWi3Oo83q9crvdisViCoVCuS4H6FcNE+dKliXZcng9jpVWw8RjVVC/Mnc1AAAAoNvSTrcMZTqsbm2MpvTixy06d9oIGYahFz9uUVN0R1gxsdAl027TWzWt2W0pS/pPbUSHjMzrcPz3aiMdbuc7bbrhqNE6aVKByrwO2W2G3A6bxvrN7D6GMko7aFmMgY8gAgAw6KXTaQUCAfl8Pvn9fkUiEbW2tu77gcOM3W6X3++X3W5XKBRiFQSGnah3tCJFE3NdhmSzK1J0gKLeUXKHt+a6GgAAAHRRxuh8Ve3Dyxt164njJEk3vPDJfh+/NdlxhtlPjxurYyf69eOqGq0PxBVLZvTAlw+Qucsk60yuVvsC3UBrJgDAkNE+58DtdquwsFC2XF7xPMDQiglD1YIFC/Tzn/+80/sWLVqks846K3u7cfzs7FL6cQWmGr87S/U3zNRob8ceu2X5DtXdMFON352lcQVtV5tNLXXr/50+UcuvrFDN9ZX65yVTdNmnS7KPufLwUn18zXS57LvPq3E7DFVfO0OXfWrH/rLSahw/Z7+fNwAAAPqfkUl3uv2l9UGZdkNOm6GXNwQ73FfdHFc8ZemI8vzsNodNmjnKow+27f3fZUeM9WrRikY982GL1jTEVN+a1PgCc7f9DKvzuoCBhE9oAABDSiwWU3Nzs2w2m4qKiuR0Ovf9oCGufaVIPB5XIBBQOs2bVAxPwdJpu82GqA0l9ZWKER22nTOtWLWhRIdtM0Z5tC2S0uVPVWvOfav1v//cqh9+bqwumdUWLixe2SSP06YvHFq423lPn1wkp93Q4p16BctmZ04EAADAIGNPRpXR7heeWBnps79frdn3rZaV6XhfJGnpgXe36adzx+q4A/w6tDhPd5w6QW6nTX9+v3G3Y+1sfSCuLxxaqIpSt6aWuvW70yfKZuyyGkKG7Kloj58b0NdozQQAGHJSqZQCgYD8fr8KCgrU2tqqaHT4vTHbuRVTMBhkdgaGtaTpVcrl2237X1c26tzpxbrjzbrstvOmFeuvK5v0nTmjs9v+srzjPxI3tjTpM2Py9YVDC3Xffxq0LZLS0nUtOn96sZasDnTY97zpxXr2w2Y1x9Ia43Pq58eVa+4BPlkZaeVJN+vuX9+hurod5z/11FM1f/58jRkzRqFQSK+++qp+85vf9NZLAQAAgP2UF9qy24Ut7UIJq9PtkvSzVzbLZkj3fHGCvKZd79VGdPYj69QS3/tFYj94uUZ3njZBz/3XoWqKpPSbt7bK59rl/DZ7W13AAEcQAQAYkjKZjFpaWuTxeOT1euV0OhUKhZTJZPb94CEgLy9PXq83Oz+DVRAY7qL+8k63P/9Riy6aOVJHlOfrrZpWHVGer4I8u5Z+1NIhiOiM32VXIJrK3v7z+41adPYklftN1QTbVlRMKDA1e5xXZz+yTg6b9NhXDtI7m1v1+Yc/VNqSfjTZpttuu01f+9rXlEqldPrpp+vKK6/U73//e7311lvKz89XRQUrJwAAAAYCd7Am+/03n9m4133/62/rs9/H0xl9b1mNvresptN9X98UVvEt/9lt+yctCZ2x6KMO2+7/z7bd9vMEOz8uMJDQmgkAMKRFIhG1tLTI6XSqqKhIdvvQH+Ll8/nk8/kUi8UIIYDtYr4x2fkQO0taGT26qknnTy+WJJ0/vViPrmpSctc19bv4zNh8nXFYkR58b8c/BF/eENTWUFLnbT+WJJ07vVibgwn9X3VIXz5shGyGoW89t0lrGmL6sKFV3/vTiyotLVVlZaUk6YILLtDixYu1ZMkS1dTU6IMPPtCSJUt64RUAAABATzkTYTnioVyX0YEjHpQjEc51GcA+EUQAAIa8RCKhQCAgy7JUVFQkl8uV65L6hN1uzz6/YDCocJg3o0C7tNMtQ52HCw8vb9SXDi1Sab5DXzq0SA8v33uv3skj8/Tnsw7UL1+v1SvVO/4hamWkv65s0rnT2mZOGJLOqRihv6xoUkZtA68PKHJp43Uz2r6+PUuv3/kdmaapMWPGqLCwUCUlJfrPf3a/Gg4AAAADg79+RacXuOSElZa/fmWuqwC6hNZMAIBhwbIsNTc3y+v1yu/3KxqNDqkP6mnFBOxdxtjzaqg1DTF91BTT/zv9AH3YGNPabTFVlLo73ffQ4jz9/dyD9af3GvWrN7budv/Dy7fpms+W6ZgJPtkMaazfzM6X8Dpten9rRF9/slqSZGRSKtzyb5Wuf0ktLS2yrD33FQYAAMDAULzpDTWNm53rMtrY7Cre9HquqwC6hBURAIBhJRwOKxgMKi8vT4WFhbLZBvevQsMwaMUEdIGR2fuH/A8vb9TRE3x7XQ1x6Mg8PX7ewfrriib9z6udDwSsbk7o9U1hnTe9WOdOK9b/VYey8yLer4vowCKXtkWS2tAc14ZAXJ/UNWnLli1qbW1VNBpVbW2tZs2atf9PFAAAAH3KHa6VJ1At5foiEistT2CD3OHdL44BBiJWRAAAhp14PK5UKqWCggIVFRUpFAopkUjkuqxuczgc8vl8stlsCgaDisfjuS4JyIn8/HxNmjRJhmHIbrfLZrOptbVVhmGovLxcn/rUp1Q7slQ1JfmSYdMnLbv///6n97bpibUBtcQ6D/Imj8zT4+cerKoNQd3zTp1K89veRqctqXGngdVSW6ix8JTxkjoOMXxsVZO+eUSZHjprkm55bYu2tMQ0Y1KpTj/lKi1atEjbtm3Tgw8+qGuvvVbNzc1666235PF4VFFRob///e+99XIBAACgh0qqq7Rx5sW5LcJmV0n1K7mtAegGgggAwLDU3sLI5/OpoKBAra2tikQiuS6ry9pbMaVSKTU3N7MKAsNGe9jQ/uV0OjVz5kzdd999HfZbtmyZDMPQGWecoTPOOKPDfZc/Va03azq2ZktnpKbonv8/On1ykUrynZpfUaz5FTuGUW9qiWvmPas67PvUBwHdemK50hnp2Y+as9ujqYy++PCH+vGxY/XgmQfKa9q1bdsYvfevt7I/f5YuXSrTNDVv3jxdfvnlamlp0auvvtqdlwgA0EuSpldRf7livjFKO93KGHYZmbTsyajyQlvkDtbIyYBYYFjy16+Ur26lQiWHSbY9twDtM1Za/obVzIfAoGJUVlZ2PrUPAIBhwu12Kz8/X8lkUsFgUJnMwP3VaBiGvF6v8vLyhtycC2BnDoejQ+DQ/rVzOzXLspROpzv92vX/46Tp1Zq5P+3vp7FPU175iRzx0L53BAD0i6h3tBrHz1awdJpSLl/bRistQzt+r2RkZD94dMRD8tevUPGmN+QO1+aiZAA5kjR9+uCoBbIcLsnox5a/GUu2VFyH/uMWwlAMKqyIAAAMe9FoVKlUSn6/X0VFRQoGg0qlUvt+YD9zOBzy+/0yDEMtLS2Dsp0UsLPOgob2r3Y7hw2JRGKvYcPeOBNhOeKhHR8qDQBmMqzR/jwlk3YlEgklEokB+bMHAIa6jKRgaYUaDpirSOFEyUp3vMLZZteefuOkXD41jT1CTeNmyxOoVkl1lfz1K2X0Q90AcsuZCKl81WJtqrywf09s2FS+ajEhBAYdVkQAALCdzWaT3++Xw+FQOBxWLBbLdUlZO7diCgaDsnI9GA3oou6GDfta2dATNVPOUtPYI3KzfH5XVlrFm9/WgR8/JdM0ZZqmbDZbNnBJJBJKJpMDeoUWAAwFSdOnminzFCqraBs8a+vBVc3bAwxf3UqVr35MzgQr3oDhoGHC0aqdfEa/nW/02sdVsvG1fjsf0FsIIgAA2EV+fr48Ho9isZhCodz+A9IwDPl8PrlcLkUiEbW2tua0HqAzewoabDabDKPtmtD+Chv2JuodrY/mfLtfztUVB7/+S7nDW7O3nU5nNpRwOBzKZDJKJpNKJBKKx+MEkADQy5rLpqtm6nxZdrN3Q2orLVs6ofJVi1VYt7z3jgtgwMqGERmrb9o0bT/umDWPa+QmQggMTrRmwpDHgDEA3dXa2qpUKiWfzyeHw6FgMJiTYdC0YsJA0pWwIZPJZMOFeDye/T6VSg2IK/vd4Vp5AtWKFIzv2RWvPWWl5WnZ1CGEkKRkMqlkMqnW1lbZbDa5XC6Zpqn8/PzsiqidV0sAAPZfw4RjVDv5S33zoaHNLstwaVPlhUpy5TIwLJRsfE3OWAvhJrAXrIjAkMSAMQC9wW63y+/3y2azKRQK9WsQ0D5Am1ZM6E82m22PgUNnYcOuX4Ph72lLaYU2zrw412VowrsPqKB+ZZf3b18pYZqm7Ha7LMtSMplUPB5XIpEYEEEPAAwW2RCin9BGBRg+OrZ7S/cskNj+eH/dCo1d/RgX0WLQI4jAkLHPAWP7sn1/BowB2JlhGPJ6vcrLy+uX1ki0YkJfGw5hw544HA55fT6tPmS+thVMkowczIqw0vI3rNaE9/643+8zHA5HhxZOkjqslmDgNQDsWXPZ9P4fLCtp/HsPciUzMExkP5+aOFeRook9+Hxqg0qqX+HzKQwZBBEYEhgwBqCvta9QSCaTCoVCffJh7M6tmPp7BQaGluEcNuxJfn6+3G63UqmUmuIZrf7sd2Q5XH3Tw3dPMpZsqbgO/cctvXZFm2EYMk1TLpdLTqdzt4HX/BwBgB2Spk8fHLVgSPz8BzA47OjYUaGUy9+2ca8dO4Ly16+kYweGJIIIDHoMGAPQX9qDAkkKhUK92qOdVkzorq6GDe1DolOp1JAPGzrjcDjk8/lkt9sViUQUiUQkDd0rYhl4DQCdy0iqrrxYoZLDevffjV3VCyviAAxuKdOrSPsMU4dbGZtdhpWWPdU2w9QTrJGDsBJDGEEEBrU+HTAmZY9LT08A7QzDkN/vl9PpVGtrq6LRaI+PRysm7IlhGHsdEi11DBs6+xrOdl4FEQqFdns9GiYcrdrJZ/RbPf39fmLngddOp1OGYTDwGsCwNVhnBAEAMFQ4cl0AsL86DBjrq2W124/b/iEFYQSATCajlpYW5efny+v1yul0KhQK7deg2J1bMbW0tNBCZZjqStggKRsspFIpxeNxwoa92HkVxN4Cw/bf67WTz+jzixrGrHlcIzf17/sIy7IUjUazz799pYTL5ZLH45FlWR1aODHwGsBQ1jBxbs/b+PaUlVbDxGMJIgAAw1IOfwMD+6+5bPqOEKKf1E4+Q81l0/v1nAAGrtbWVrW0tMjpdKqwsFB2e/eW+LvdbhUWFsqyLAUCAUKIIc4wDDkcjuwHwD6fT4WFhSouLtbIkSNVVFQkv9+vvLw82Ww2pVIpRSIRtbS0qKmpSQ0NDWpqalJLS4vC4bCi0agSiQQhRCfy8/NVWFioTCajQCCwz1VLJRtf0/j3HpQtFW+bE9WbrLRsqbjGv/dgv4cQnUkkEgqHw2pqasq+Nna7XT6fT8XFxSosLJTH48kOwAaA/XHhhRfq97//fZf3LysrU1VVlSZNmtRnNUW9o9sGxnYSQvzvKeO17lvT1fjdWaoodfdZDZIkm12RogMU9Y7q2/MMYlVVVZozZ06uy8BOZsyYoaqqKuXn5+e6FACDHP/KwKCTNH2qmTq/765c3JOMpZqp85UfWM+AMQCS2j7Ua25ult/vV1FRkUKhkOLx+F4fQyumoaurKxt2ntnAyobe09VVEJ0prFuu/MAG1UyZp1BZRVsg0ZP+4dsf729YrbGrHxuQ7xtSqVQ28Np54HX7vBoGXkOSFixYIK/Xqx/+8Ie5LqVLqqqq9IMf/ECvv/56rksZsBYtWqRRo0bpZz/7maqqqjrc98ADD2jixIm65ZZbtHTp0hxV2LmFCxeqsrJSUtv7ry1btujxxx/XE0880aXHN46f3enP9uMP9OvcaSN0+l8+0sbmuBojqd4ufXdWWo3j56h89ZK+P9cAs2DBAp1yyim7bX/77be1YMGCHFTUM4sWLdJjjz2mJUty82c5Y8YM3XHHHQqFQjrrrLM6tFw89NBDde+990qS5s6d2+VjLly4UOvWrdPdd9/d6/UCAEEEBpWMpJop89oGU/dnCCFJhk2W3dTmKfMYMAYgK51OKxAIyOfzye/3KxqNKhzu/ENHp9Mpn88nwzDU3NxMf/ZBateQweFw7DFsaP8wd+ewgfY3vW/nWRCBQGC/Qh1nIqSJ7z2gYGmFGibObbtytruBxPb9PS2bVFL9ivz1KwfF+4VMJqN4PJ4NUnceeO12u7MDr+PxuBKJBAOvsd9sNpsymQw/B3Osrq5Op556aocg4rDDDlNRUVGPZ1/1paefflp/+MMflJeXp5NOOknXXHONQqGQXn755X0+Nlg6rcPPc6fNUNLK6IBCl+rCSb2zuWcXhtgNKd3Vv9Y2u4KlFdIwDCIk6a233tKtt97aYdtwf0/scDiUSu1/CBaJRHT00Ud3+H/htNNO09atWzVqFKtvAAwcBBEYVIKlFW1XKuaKza5g2TQFSyvo6wmgg1AopGQyKa/XK4fDoWAw2OHDOo/HI4/Ho2QyqVAoxAd5A9yeVjbs3IKLsCH3erIKojOGpIL6lSqoX6mod7Qax89WsLRCKZe/bQcrLUM7/mwzMrIfbDniQfnrV6p40xtyh2t7VEeuJZNJJZNJtba2dhh47fV6GXiNDs4++2ydcsopGj16tEKhkP75z3/q3nvvVSwWkySdfPLJ+uY3v6mbb75Zl156qcaNG6fzzz9fyWRS3/nOdzRz5kw1NTXp/vvv1yWXXNLhyuL8/HxdccUVmjNnjpxOpz744AP99re/1ccff7xftfr9fl199dWaPn26fD6ftmzZoocffrjDB3cLFy7U+vXrlUgkdNpppymVSunJJ5/Ugw8+mN1n3Lhx+s53vqNDDz1UW7Zs0Z133qlf/epX2ZUY7Vcof+ELX8iuepw0aZLuu+8+nXPOOaqrq+tSLW63W9ddd53mzJmjSCSiv/71r5ozZ06HK5WdTqe+9rWv6bjjjpPX61V1dbV+97vf6f3339/ra7Fs2TKdffbZKikpUUNDg6S2Dy1feuklnXTSSR32LS0t1dVXX61Zs2bJsiy98847+s1vfqNAIJDd59xzz9W8efOUl5enV155Rc3Nzbud87TTTtP8+fM1evRobd26VX/729+6vJqhXSwWy573wQcf1PHHH6/Zs2fr5Zdf3uvfl6Tp1XXHH6LTDi7Qff9u0HWzR2lcgalHVjbp3GnFkqTG787Sppa4Zt6zSqbd0E/njtWXDyuSz2XXe7UR/eClGr27NSJJmjPeqyfPO0TzF6/TjceM0ZSSPM17ZJ1uOGq01jRElbakc6aNUCKd0S9e3aIlqwO69cRxOv3QQtVHkvruizV6ab2UMr0yUxFdf/31mjlzpkaMGKG6ujo9+eSTHa6wb1+ZtGLFCs2fP18Oh0NVVVW66667ssG70/n/2bvzOBvr9g/gn7PvZ+YMM2MWW8g2ljwplZ8oUtKjlEL14KmQkFKkRU/ypLTQIvVEKEVIlIhIkVJSYchuzGrWM3P29T6/P6Zzm2N2ZubM8nm/XvN6cZ/73Pf37Od8r+91XQqMGzcON954IyIjI5Gbm4tPP/0UmzdvBgC0adMGEydORPfu3eF0OvHbb79h0aJFsFgs5d7f/fr1w7hx4xAfH4+CggKsX78ea9euFS9ftWoVNm3ahISEBFx//fWwWq1YuXIlNm3aVOHj6PV6Q54/lYmOjsakSZNw5ZVXQhAEHDp0CG+//Tays7ND7p+jR49i+PDhUCqVWLt2LVauXImHHnoIQ4YMgdvtxocffohvvvmm2sct735fsGABWrRogcmTJ2Py5MkAijMPqvpec+bMGfj9fgwaNAinT5/GuXPnEBkZiaefflrcTyaTYe3atViyZIn4WJZl69atuOWWW8RzKJVK3HDDDVi/fj3+9a9/iftVNraZM2eiZ8+e6NmzJ+666y4AwMiRI8Xrd+zYEePHj0fr1q1x8uRJzJ8/H2lpaVV+LImIGIigBoUNxoioPnO5XPD5fGKpJovFIv5foVDA4XDA4XCEe5j0t+oGG7xeL4MN9UhNZEFURGPLKi6bceRz+JR6OIyJcBni4ZdrEJDKIBH8kPmcUFszobWkQ14Pyy/VhJINryUSiZgtwYbXBBQ/P95++21kZWUhPj4e06ZNw8SJE7Fw4UJxH5VKhVGjRuG1116DxWJBYWEh5s6di4iICDz22GPw+XyYNGkSIiMjQ479n//8B263GzNnzoTdbsdtt92G119/Hffffz+sVmu1x6pUKnH8+HGsWrUKDocDffr0wdNPP43MzEwcPXpU3O+mm27C2rVrMWnSJHTt2hUzZ85EcnIy9u/fD6lUirlz5yI7OxuTJk2CRqPBpEmTamUskyZNQlJSEp599lkUFBRg3Lhx6NChA06ePCkeZ+rUqWjTpg1efPFF5Ofno2/fvpg/fz7+/e9/IyMjo9zzm81m7Nu3D4MHD8bKlSuhUqnQv39/TJs2LSQQIZFIMHfuXDidTkybNg0ymQyPPvooZs+ejcceewwA0L9/f4wdOxZvvvkmDh06hEGDBmH48OHIyjofkB04cCDGjRuHt956CydOnECHDh0wffp0uFyuSyoB5fF4oFAoAFT8fMlQJQAA2ppUuK1jJMZ8cRp+AUizuHHG7MaYns0xcMVR+P9eI/KfAQm4rWMkHvn6LNKLPJjSJxZr72mPK98/jELX+c+a2f3j8fx3GUgpdIvbRyY1w9u/ZGPQimO4o7MJrw1uhVsvj8TXxwux4OdzeLh3DBYPbYMe7x6Cw5gIlfkEcnNz8Z///AcWiwVJSUl4/PHHkZ+fj++//148V8+ePZGfn4/HHnsMCQkJmD17Nk6ePImvv/4aADBr1ix06dIFb7/9Nk6dOoW4uDhEREQAKP68fOONN7B582YsWrQIKpUK48ePx/PPP4/p06eXed9efvnlmD17NlasWIGdO3eia9eumDZtGiwWS8hjNmLECCxbtgwrV67E9ddfj2nTpuHAgQM1Njktk8kwf/58HDlyBFOnToXf78f999+P+fPn44EHHhCzCK644grk5uZi2rRpSEpKwowZM9C1a1ccPHgQkyZNwoABA/D444/jt99+Q15eXpWPW9H9Pnv2bCxZsgSbNm0KCb5U9b1m8ODB+PLLLzFlyhQAxUGCN998E1FRUSgoKAAAXHPNNVCr1ZVm/Xz77bcYOXIkYmJikJOTg379+uHcuXM4fvx4yH6Vje2dd95BYmIiUlJS8OGHHwIAioqKxKyKBx54AIsXL0ZhYSEee+wxzJgxQxw/EVFVsFl1EzB48GB89dVXFe4zc+ZMvPjii3U0ootTssHYjL5x+H5cp0s+Zv5TvTCkQ0T1rlRBg7FVq1bhzjvvvORxEVHDFZwU9fl8iIiIQFRUFORyOYqKihiECAOZTCaWl9Hr9eJj0rx5c0RFRSEiIgI6nQ5yuRyCIMDlcsFiscBsNiMvLw/5+fkoLCyE1WqFw+GA2+2Gz+fjZGsYyeVymEwmaDQa2O12FBYW1np/DbnHBmPeUcSc+Q5xJ75G/LEvEXfia8Sc+Q7GvKONNghxoUAgUG7Da6PRyIbXTdDnn3+OP//8E9nZ2fjjjz+wdOlS9O/fP2QfhUKBhQsX4vDhw0hLS0NMTAyuvPJKvPbaa/jrr79w4sQJvPrqq1Cr1eJ1kpKS0KlTJ7zwwgs4fvw4MjIy8N5778Fms+H666+/qLHm5eVhzZo1OHXqFLKysvDFF1/g119/LTXe06dP46OPPkJGRga2bduGY8eOoVevXgCAf/zjH4iPj8e8efNw6tQpJCcnY8mSJTU+Fo1Gg8GDB2Px4sX4/fffkZKSgvnz54eUAIyJicEtt9yC//znPzh06BAyMzOxZs0aHDp0CLfcckulY9iyZYtYq//6669HZmZmqWyTXr164bLLLsPcuXNx/Phx/PXXX5g3bx569uyJjh07AgDuvPNObN68GZs3b0ZaWho+/PBDnD17NuQ4Y8eOxeLFi7F7926cO3cOu3fvxrp16zB06NBq33dAcYmvgQMHol27dvjjjz8qfb64DPFAQIBSJsGkTWdxKNuJI7lOWN0CbB4//IEAcuw+5Dt90CqkGHdFczy/MwM7TltwLN+FaVvOwuUTcF/3ZiHjeHl3Fr5PsSKl0CMGIpJznHj9p3M4bXZjwc/n4PIJKHD68PGBfJw2u/Hqniw008rRtbmqOLjt92P58uU4fvw4zp07h+3bt+Obb74p9by02Wx46623kJaWhr179+KXX34Rn5eJiYkYMGAA5s+fjx9//BFZWVn4/fffxdJbd9xxB06ePIklS5YgLS1NXMneq1cvJCYmlnkfjxgxAr///js+/vhjpKenY+vWrdiwYUPI6niguMzSxo0bkZmZiVWrVqGoqEjs5VGea665RnzOBP/uvffeMvcdMGAApFIpXn31VZw5cwapqal45ZVXEBMTE3Ieq9WKt99+G2lpadiyZQtSU1OhUqnwySefICMjA59++il8Ph+6detWreNWdL8HM5wdDgfMZrOY5VHV95r09HS8//77SEtLQ1pamvgeWTIYePPNN+P7778Xs8zKYzab8csvv4iv6VtuuQVbtmwptV9lY7Pb7fD5fGL2kdlsDsniXrp0KQ4cOICzZ89i1apVSEpKEoOBRERVwV8IjUDJhk9erxc5OTnYtm0bVq5cWeXSH++8806l+4wZMwY33HADoqOj4fP5cPz4cSxduhR//fVXmftf2PzsQsuXLw9Jc65MeQ3GLkXntw+GrGqpsotsMDZmzBiMHTsWQHFd+fz8fPz666/43//+d1Eru2oDm/wRXbpgPXWFQgGJRAKv13tJdV+pYlKpVOzTcGGDaImkuEJ/IBAQMxkubBDNMlkNR21nQVD1sOE19erVC/feey9atmwJnU4HmUwGlUoFlUol9hzxeDwhE9ytWrWCz+fDiRMnxG2ZmZkhJWLat28PjUZTqnSPUqlEfHz8RY1VKpXi3nvvRf/+/dG8eXMoFAooFApxnEGnT58O+X9BQQFMJpM49pycnJCyMiVXONfUWOLj46FQKEKObbfbQ1aZX3bZZZDJZPj4449Djq1QKCostxP0888/4/HHH0ePHj3KnbRs3bo1cnJyxPJNAHD27FlYrVa0bt0ax44dQ+vWrUstejt8+DCuuOIKAIBarUZCQgKefPJJPPHEE+I+Mpms3J5a5Rk2bBhuvfVWcfHA2rVrsXHjRgwbNqzC54v/eAYkANKKPMh3Vvx9rE2kCkqZFL+kn+8Z4ROA37McuLy5OmTfP7NKLzA5knO+RKAQAMxOX8i2HHvx+aN1cpyVawAAt99+O2655RbExMRApVJBLpeHZL4AQEpKSsj3lfz8fLRt2xZA8evF7/eXW5KrXbt26NmzZ5mlfeLj45Genl5qe6tWrUr9HkxOTsadd94JqVQqjuXC14vZbBZfL+X5448/sGDBgpBt5f0WbteuHRISEkqN/cL3gpSUlJBFImazGWfOnBH/LwgCioqKxLFV57jl3e/lqep7zYXZCgDw9ddfY+jQoVi9ejVMJhOuvvpqPP744xWeL2jLli2YPHkyvv32W3Tt2hUvvPCCGHip7tjKU/K9PD8/HwBgMpmQk5NTpesTETEQ0UgEGz4plUpcffXVePTRR+Hz+fDpp59W6frB+qUVSU9Px5tvvomsrCyoVCrcddddmD9/Pu677z4UFRWV2n/48OHiv2+44QaMHTs2pD5hdes4X9hgrCYEvwhW2yU0GDtz5gymT58OmUyGVq1aYcaMGdDpdJgzZ87FjaWGXGqDLCIqJpFIQkoxeb3eUqWaqPqkUmm5ZZQYbGj8aroXBNU8NrxuemJjYzFv3jxs3LgRS5YsgdVqRbdu3TBjxgzI5fKQQER1qdVqFBQUYNq0aaUuq+7kddA999yDO++8E++88w7OnDkDp9OJyZMnl8reufBzOhAIiJ8zVRGcDC15nQvPUdWxVESj0cDv92PChAmlgrJVeY8UBAHbtm3D2LFj0blzZzz33HNVPnd1aDTFk+2vv/46jhw5UmoM1bF9+3asXLkSHo8H+fn54n1d2fMlENcfAODw1uz7jr2M43mF0IzJQBnbAEAiAQJSGQYMGICJEydi8eLFOHz4MBwOB0aOHInOnTuH7F/W8zKYIVPZJLJGo8HPP/+M999/v9RlwRJAF+tiXi8ulwuZmZlVOr5Go8Hx48cxd+7cUpeVnIMoaxxlfecOju1SjiutpEx0VV/fZWU5bNu2DQ899BC6dOmCrl27IisrC4cOHarwfEG//PILpk+fjieffBI///xzmQHJS33vKXl/lPVeR0RUGQYiGomSDZ++/PJL9O3bF9dee21IIKJ379545JFHEBMTg0OHDuGVV14Rv3gEGzFV9AV0x44dIf9/9913ceutt6Jdu3b4/fffS+1fcqVQ8AdDyW2VNSxr3rw5Jk6ciN69e0OhVOKY2Y8Z29Kwv8TKk7u7RmFWvzhEquTYfroIj32TCpun+AvhxtEdcCTHCZcvgPt7NIPHH8DyP/Mw/8fz9Urzn+qF+z8/hc0nir9sxBsU+M+ABNzQ1gilTIIT+S7xnG0ilZh7YyL+Ea+DViHFiXwXVjivw4Ffqpc54Pf7Q9I2f/jhBzGjpSr3TWxsLFavXo05c+Zg+PDhuPzyy5GRkYE333wzZBVMjx49MGHCBLRr1w5WqxVbt27F0qVLxS/8ZTXIiouLAwDxC9m5c+cwatSoat0+oqZMoVDAaCxualtUVCQ2cTWbzTAajYiMjITNZqs0vbqpqm6w4cIG0ZzYbJyYBdEwlWx4HSyRxobXjUvHjh0hkUiwePFicULqwtIjZUlNTYVcLkeHDh3EFcHx8fHi5ycAnDhxAlFRUfD7/WLj2EuVlJSEPXv2YPv27QCKJ88SExNLlRGqbOwxMTEwmUzi9/lOnULLxQYbNTdr1kz8DdS+fftqjSUzMxNerxedOnUSVxrrdDq0bNkSBw8eBFB8H8lkMkRGRlZ5ovJCW7ZswciRI/Hdd9+VGeA5e/YsYmJiQppat27dGgaDASkpKeI+nTt3xrZt28TrdenSRfy32WxGbm4u4uLixNt7sex2e5kT2JU9XyQtqv65kVLohtsn4OpEHdKPFAfR5FLgihZavPdbza76lgh+JCUl4fDhwyG/g6ub9XP69GlIJBL06NGjzN/lJ06cEHsGVPW7UmpqKpKSkkK2JSUlIT09vU6/b504cQIDBgxAYWFhjZY4ranjer3eUoGJS3mvsVgs2LNnD2655RZ06dIlpLl2ZYLBxVGjRmHGjBll7lOVsZV1m4iIagoDEY2Ux+MRm1MBxU3i7r77brz00ksIBAJ4+umn8fDDD+O///3vRR1fLpdj6NChsNlspdJGq6KyhmVqtRoLFy5EXl4ennnmGaTChGY3jg2JtreNVGHI5REYtfYUItUyLL39MjzapwX+u+v8l9ORSc3w7r5s3PTRMfRO0OGdW1vj13Qbvk8pnfqpU0jx5ejLkWXz4N7PTyHH5kP3FhrxnDqlDN+esmDuD5nw+AO4JykKL8+ZjTH333/RqYixsbHo3bt3yMqCqjZzmzhxIhYtWoSUlBSMGDEC//3vfzF69GhYLBY0b94c8+bNw9atWzFv3jy0atUKTzzxBDweT0g5rAsbZFksFmzYsAEvv/wyfv31V07qEVWDVquFVquF1+uFxWIJSQ8XBAGFhYXQ6/UwGAyQy+UXvaKzoWOwgaqKWRCNh9/vL7PhtVqtZsPrBkCn06Fdu3Yh2ywWCzIyMqBQKDB8+HD89NNPSEpKwj//+c9Kj5eWlobffvsN06dPx4IFC+Dz+fDwww+HBOn379+Pw4cPY+7cuWIN9ebNm6NPnz7YvXt3mSVNguLi4kqNNyMjAxkZGejXrx+6du0Kq9WKESNGwGQyVSsQsX//fmRmZuKpp57C+++/D61Wi3//+9+lzpWdnY2xY8diyZIlaNmyJe6+++5S+1Q0FqfTia1bt2LChAlig++xY8dCEATx9ZGeno5vv/0Ws2bNwuLFi3HixAlERkaiV69eOH36NPbu3Vvp7UlNTcWwYcPKXSCxf/9+nD59Gs888wwWLVoEmUyGadOm4c8//xQfg/Xr12PmzJk4duwYkpOTMXDgQLRp0yakWfXy5csxZcoU2O12/Prrr1AoFOjYsSMMBgPWrl1b5fu/PJU9X3K8TlT1XcXhFbDsjzy8MCABZqcfGZbiZtUahRQrD+Rf8ljPk0DmcyIjIwM33XQTevfujaysLAwaNAgdO3bEuXPnqnyk7OxsbN26FTNmzBCbVcfGxsJkMuH777/Hhg0bcOutt+K5557D6tWrYbVakZCQgAEDBuC1114r87vVmjVrsHjxYtx///1is+rbb789pBH9xVIoFKXKN/n9/jJX8G/fvh333HMP5s6di2XLliE3NxexsbHo168fVq1ahby8vIsaQ00d99y5c+jRowd27twJj8cjvjdeynvN119/jZdeegkymazazdw//PBDrF69utzybFUZW3Z2Njp37ozY2Fg4nc56U0KaiBoHBiIaoV69eqF3795Yv369uE2hUGDBggXiCpINGzaElEmqqj59+mD27NlQqVTIz8/HE088UaUapBcq2bAMKP4Ab926NYYOHYqtW7di4MCBiIyMxMMPPwyr1YqctjfgxyP5IaWZJBJg8tdnxQyINckF6NfagJKhlcO5Try6p/hL3GmzGw/2ika/1oYyAxF3djGhuVaOgSuOin0jzhSeT3M9nOPE4RL1Pef9kI5hrWW49tprsWHDhirf9rZt22Lz5s2QSqVQqVQAgEWLFlX5vgn64osvsGvXLgDF2Q1XXXUVhgwZgtWrV2PYsGHIzc3Fm2++CQDil/Hx48fjo48+CvkBU1aKrs1mC8leIaLySaVSGAwGsRRTRauqbDYbvF6vGIywWCyNcmJdIpGU2bPhwmCDIAjw+/3wer1wuVwhAQdq2pgF0XgFG14Hm17L5XIxW8JoNIrlNDwej1hijcLriiuuKNWQ+euvv8Zrr72GRYsWYeTIkXjwwQdx8OBBfPDBB3j66acrPea8efPw5JNP4s0330RBQQE++OADtGnTJqSM01NPPYUHH3wQM2bMQGRkJAoKCnDw4MFKv6M+8sgjpbZNmTIFH3/8MeLi4jB//ny4XC5s2rQJe/bsgU6nq+I9Ubyw4Nlnn8WTTz6JxYsXIysrC++99x7mzZsnjt3v92Pu3LmYNm0ali5diqNHj2Lp0qV44YUXxONUZSzvvvsuHn/8cbz00ktwOBxYvXo1YmJiQu6jV155Bffffz8efvhhNG/eHEVFRThy5Ah+/vnnKt+myn7LPfvss5g6dSrefPNNCIKAffv24a233hIv37lzJ+Lj4zFhwgQolUrs2rULX375JXr37i3us3nzZrjdbtxzzz2YMGECXC4Xzpw5g3Xr1lV5nJWp6PmiDkgBSdVXeM/5PgNSCbD4ttbQK2X4M8uBEZ+dRJG7Bt+PJFKorZn46qvdaN++PWbPno1AIIDvvvsOGzduxNVXX12twy1YsAAPPfQQpk2bBqPRiJycHHzyyScAimv5T5kyBePHj8err74KhUKB7OzsCheenThxAnPmzMG4ceNw//33Iz8/H8uWLav2xHhZrr766pC5CqA4KDZmzJhS+7rdbjz66KOYMGEC5syZA61Wi9zcXPzxxx+XlMlQU8ddtmwZpk+fjk8++QRKpRIDBgy45Pea/fv3o6CgACkpKWIfhqry+XwVvqarMrbPPvsMTz31FJYvXw61Wl2qQTkR0aWQ9OzZk0uOGriZM2di0KBB8Hg8YnPOHTt2YOHChXC5XBg8eDAeffRRDBkyRLxO37598cILL+DGG28UjxEszTRw4MCQhkgzZ84U033VajWioqIQERGBoUOH4oorrsCkSZPEFOTyDB48GJMnT8Ztt90GtVqNLVu2wOVyhax4CzYsu/POOzFt2jS0adNGrPOZdfmtyGvdDwFpcexsRt84DOsYieuWnm+UPbF3DB76RzT+8d5hAMWlmY7lujDj2/NN3VbeeRkKnD5M3ZwKILQ006s3tUTH5mr889PzjfNK0imkmNE3Dje1i0CsXg6ZVAKNXIK1a9aIk/mrVq3CunXr8PnnZfeOGDNmDPr3749nnnkGSqUSgwYNQvv27TFr1iwIglCl+yZYmunRRx8VU7MBYM6cObDZbJg/fz5eeOEF2O12zJ8/X7y8Xbt2WLJkCe655x7k5ORgwYIFSE9Px+uvvx4yRjarJqq6YCmmQCAAq9Va5fIiMpkMERERkEgksFqtDbKJq0QiKTezIZjOXTLYUNYf0YWYBdG0SSQSqFQqKJVKKBQKSKVSNrxuIpo3b461a9di+vTpZZaWqc+SkpLw9ttv4957761y3fuLoVarsXbtWixevLjMpsNUPq9Sj78GvFD5jnWsy87nIfc0zQxZKl/wtT5//nxxcSIRUWPBjIhG4o8//hBTm/Py8kqtbLhwwqeiJkt79uwJaSRWMi0x2FgqMzMTf/31Fz7++GMMGTKkyk2xgao1LLuw4VZAUrpJdalGYIEApJLK9gGk5TRTcvoqXpX8wg0J6N/GiOd3puO02Q23x4uPhsZVq6kcULxKIfgj5YMPPsC8efMwZswYLFu2rEabuVUF69QTXbyKSjFVJtgrxmAwICIiAg6HA3a7vRZHe3GqEmwAIAYWfD5fqSbRRFXFLAgKBAJwuVzi9xM2vG68rrjiCmg0Gpw+fRrNmjXDhAkTkJWVFdLvrL7q27cvnE4n0tPTkZCQgClTpuDQoUM1HoRo3749WrVqhaNHj0Kn04nZ7D/++GONnqcpUHhskLut8KkM4R6KSO62MAhBISQSCSIiInD33XfDZrNxYSARNUoMRDQSwQBBTQjW8K2KYJ3f6qhKw7LTp09jyJAhMBgMsFqtkARqfzLiSI4T93dvjki1TCzNVNLVCXqsOpSPr48XN7bWywQkNDPi8CWe9+OPP8Ybb7yBL7/8Evn5+VVu5talSxcxI0IqleLyyy8XS0SlpqaiX79+IfsnJSXBbreLjebK4/V6IZOVDvwQUbHqlGKqSCAQgMVigUajgU6nE0s11XVtdAYbKNwUCgUMBgOkUimzICgEG143XnK5HA8++CDi4uLgcDhw+PBh/Pe//20QnylarRbjx49HbGwsioqKsH//fixevLhWznXPPfegZcuW8Hq9OH78OKZOnXpRZXEJMOYcQkHC1SGlfsNG8MOYkxzuUVA9ExMTg9WrVyMnJwevvPIKg+1E1CgxEEFVolarcd9992HPnj0oKChAREQEbr/9dkRHR+OHH36o9vEqa1i2Y8cOjB49Gi+++CKWLFkCpVaFPh2jkGX347fM2lk1/PkRMx67pgU+vvMyvPh9JrJtXnRvoUWW1YvfMu04bXZjaMdIbD1ZhACAWX1blJtdUR1HjhzB6dOnce+99+Ktt96qcjO3YcOGIT09HWfPnsWIESNgMBjENO2NGzfizjvvxNSpU/HFF1+gVatWGDt2LNauXVvpJOe5c+fQq1cvHDp0CF6vt8k21CUqS8lSTEVFRTUy8eV0OuHz+WA0GmEymWCxWEIa2NeEqgYbSpZRurBJNJvHUm0omQVRVFTUICYhKTzY8Lpx2bdvH/bt2xfuYVyUbdu2Ydu2bbV+npMnT2LChAm1fp6molnqTyhoeW24h1FMKkOzVK52p1DZ2dkYMGBAuIdBRFSrGIigKvH7/WjZsiVeeOEFREREwGKx4NixY5g6dSpSUlKqfbzKGpb5fD7MmDEDDz/8MObNmweZXIGjZh9mbEur5MgXzysEcOdnJ/HiDQn4bER7yKTA8XyXeM5nv0vH20NaY8v9HVHg8OGtX84h2ltxdkFVrV27Fk899RRWrVpV5WZuH3zwAUaPHo127dohMzMTzzzzjLhCKi8vD7NmzcKECROwZMkSWK1WbN68GR9//HGlY1m8eDEmTZqEW2+9FXl5eRg1alSN3Eaihk6n00Gr1cLtdsNqtdboJJfX64XZbIbRaERkZCRsNttFlU4rL9hQMsuJwQaqD5gFQZeCDa+JqLo0tixozSlwRLQCyilRXCcEP7RFqdDYzoVvDERERGHCZtXUILDB2HnBZtUPPvggTp06VafnJmqKpFIpjEYj5HJ5nUyYBgMeLpcLVqu11OXVDTZc+MdgA4Vb8Dnu9XphtVo5SUw1SiqVikEJNrwmopKKYpJw9opx4R4GWv+xDBEszURERE0QMyKoQWCDMSIKB6VSCYPBgEAggMLCwhovmVQWu90OQRCg0+mgVCrh8XgglUrFMkqSv0vClQw2eL1eBhuo3iuZBWGz2ZgFQbVCEIRKG16XDEqwBjdR02HMSYYhOxnW6M7h6RUh+GHMPcL+EERE1GQxEEENBhuMEVFdqs1STED5mQ0lgw0AoFKpSjWI9vl8DDZQg6LX66HRaOD1etkLguoUG14TUZAEQOKRdTjWdyYEiQqQ1GGJpoAAqd+DhCPrcOldBomIiBomlmaiBsOpj8OJ654I9zBEHfa8ytqeRI1QTZZiCmYylPUXDDYEAoFyyygJggCDwQC1Wg2HwwG73V5TN5OoTrAXBNVXwYbXKpUKSqUSUqmUDa+JmojC2O5I7Tmmzs/b6s8ViMw+WOfnJSIiqi+YEUENBhuMEVFtu5hSTNUNNlzYILqysiBWqxVerxd6vR4KhQIWi4WlRKhBCGZBeDweFBYW8nlL9UrJEk0Aym147Xa7xfdtImocIrMPwnt0A7I63V5n54w7uoFBCCIiavIYiKAGJTplZ/gbjElliE75PrxjIKIaV1EpptoONlTG5XLB5/PBaDTCZDLBYrGwhAjVWyWzIKxWq1irn6g+8/l88Pl8cDgcIQ2vdTod9Ho9G14TNTLRZ3cDQHEwIiDUTpmmv48b/9cGNE/dXfPHJyIiamAYiKAGhQ3GiKimlSzF5HA44Pf7odVqyw02BJtE13SwoTI+nw9msxlGoxEREREsc0P1ErMgqDEor+G1SqViw2uiRiT67G4oXEVI73o3BJmyZn9fCn5I/R4kHl7DTAgiIqK/sUcENThepaG4wZg8DA3GfG50/PFlKDy2ujsvEdUYiUQiBhfkcjkUCgXkcrl4GRAabCjrrz7QarXQ6XS11kibqLpKZkHYbDZmQVCjVbLhtUKhYMNrokbAqzQgvctdsMYmAYL/0gISf2dBGLMPIeHIOv5uJCIiKoGBCGqQ2GCMqsOr1MNpTITLEA+/QoOARAZJwA+Z1wm1NRMaSzp/JDQiJYMNF/5JS/SXEQQBUqkUfr9fLH1Un4INlQn2sxAEARaLpcGMmxoXiUQCnU4nZkFYrVauDqcmgw2viRqPAABLTBJy2wyAw9QGEsGPQHUCEn8HMCJs6YhO+R6ajD8gqbXREhERNUwMRFCDldv6/+q8wViwlijVf059HPJbXQtLTDf4VIbijYIfEpx/ywtAIq54krutMOYcQrPUn6CxZYVjyFQNVQ02lJXREAgEoNPpIJfLG/zK7ZJlpaxWK9xud7iHRE0IsyCIQpVseK1QKNjwmqiBUrfsjJTI7iho1gk+lbF4Y4W/Iyww5iSjWepPiJXaIZPJYDabwzF0IiKieo2BCGrQxGAEG4wRSqxkajsAjsg21U+t/nt/rTkF0Sk7YcxJ5kqmMKpOZoPf7w/JaCgvsyGYRRAIBGCxWODz+eryJtWaYF1+p9MJm43ZPVS7mAVBVLmSDa+VSiUkEgkbXhM1AEqlEhEREcjPz4cgCPAp9XAEM6vlGgSkMkgEP2S+4sxqrSUd8hKZ1XK5HCaTCRaLhQtEiIiILsBABDV4hbHd2WCMLqjtKgDSSwhM/R2QMGQnI/HIOig81pobKJVSsmdDZcGG8jIcqiI4We9yuWCz2RpduQy1Wg29Xg+fzweLxcKJYaoVzIIgujglG17LZDI2vCaqpwwGA2QyGQoLCy/6GMFs1YKCgpobGBERUSPAQAQ1CjXaYOzv67PBWMPBYFT9V15mg0x2/vGqiWBDeec2GAyNohRTZeRyOYxGIyQSCSwWC5umUo1hFgRRzamo4bXb7W402XpEDVGzZs3gdDrhcDgu+hgymQxRUVHMiiAiIroAAxHUaFzYYOziy/KcQXTK9yzL00Dktu6HrE7Dar08F3uEVC6cwYbyqFQq6PX6JtXQWSKRwGAwQKlUwuFwXNIPaSKAWRBEtYkNr4nqj2BZpoKCgkv+zsisCCIiotIYiKBG6Xyj4qRqNxhjo+KGQwxC1BEGI8oPNkilUkgkxaG7YHPOugo2lKexl2KqjFarhVarhdfrhcViaXK3ny4dsyCI6h4bXhOFTzCDtiYaTQezIqxWKwP4REREf2Mgghq96jYYo4ahMLY7UnuOqfPztvpzRaMv0ySVSkv1aygr2FBeZkO4JyplMhmMRiNkMlmTX72tUChgNBobXXNuqn3BLAiJRAK73d6kX0dE4cKG10R1qybKMpXErAgiIqJQDEQQUYPjVRpwrO9MCHJV7ZRjKk9AgNTnRscfX27wvUOkUmm52Q0NIdhQHpVKBYPBAL/f32RKMVVGKpWKP4SbemCGKscsCKL6iw2viWqPQqFAZGRkjZRlCmJWBBERUSh5uAdARFQdAQDpXe4qbkxdl0EIAJBIIciUyOhyF1r/ubze9xCpbrAhWPKhvgcbylOyFJPVag33cOoNQRBQWFgIvV4Pg8EAhULB+4fKVDILgpMmRPWP1+uF1+uF3W4PaXit1+vZ8JroEqlUKvE7cE3x+/1wu93QarX8TCUiIgIDEUTUwFhikmCNTQrfAKQyWGK7wRKThIic5PCNIzicJhZsKEvJUkycPC2fzWaD1+sV6x8zY4SCmAVB1PD4/X44nU44nU5IJBIxKKFWq6HVatnwmqiaVCpVrXyHtNvtiIqKglqt5ndUIiJq8hiIIKIGJbfNAEAQAGkdZ0OUJPiR26Z/nQUiJBJJmT0bLgw2CIIAv98Pr9cLl8sVEnBorEqWYjKbzY36ttaE4CpZo9GIyMhIWK1W1hhv4pgFQdTwBQIBuN1uuN1uAKENr4N9grxerxiU4GclUSiFQgGpVCq+hmqS3++Hy+ViVgQRERGAMM7kEdU/JpMJr776KjZv3oyvvvqq3G07d+7EddddV6VjjhkzBh988EGtjbk2TJ8+HRs3bsTOnTvRrl07LFiwAI888kiVrx8bGytetyY59XFwmNqENwgBAFIZHKa2cOpb1Nghg8EGlUoFrVYLg8GAyMhINGvWDM2bN0dkZCT0ej1UKhUkEolYmqGoqAgFBQXIy8tDQUEBioqKYLPZ4HQ6a2WyISkpCUuXLsW3336LF198sUaPXV0GgwFGoxFut5tBiGrw+/0oLCyE1+tFREQEdDpduIdEYSCRSKDX6xEZGSkG8jhBQtQ4+Hw+OBwOFBYWIj8/HzabDYFAADqdDlFRUYiKioJer4dSqQz3UInqhWBZptoqaeZwOCCVSqFWq2vl+ERERA0Fm1VTkzFz5kzcfPPNpbb/+uuvmDlzJgBg/Pjx6NOnD5577jnY7XYUFhaWuc1kMollTiqjVquhVCphsVhq7LYMHjwYkydPxm233VYj+5V01VVXYe7cuXjssceQmZmJoqIi6HQ6+Hw+OJ3OKh1DKpUiIiICRUVFEAQBPXr0wMKFCzF06FDY7fYqj+VC6V3uREHC1WgbpcXj17ZA/zYGNNPKcc7mxW+Zdrz7aw7+POe46ONf6I+Hu+K9fTl4/7fc0hcKfkRl/ILEI59X+XgSiQQymQwrV67Exo0bsWnTJjGzQVoiuNKpUyfMmTOnwmNNmzYNBw4cqPK5a8q7776LtLQ0fPDBB3A6nZf0eF6sC0sx1cbqtaZCo9FAp9PB6/XCYrGwdEcTUbKmvN1uZwCCqAlRKBRQqVRQKpVseE30t6ioKLjd7lr9Xhvs01VQUFBr5yAiIqrvWJqJmpRffvkFr7zySsi2ksGE+Ph4HD9+HBkZGRVuM5vNVT6ny+VqUJM88fHxyM/Px+HDh8Vt1W1sKwhCte6jqrLEdEPPeAO+GNkBf+U58fjWVJzId0GvlOGWDhGYc0MC/vnpiRo/b5mkMlhikoALAhHBYENZf8FgQzD7QSqVwufzwe12h5RRMpvNGD58uHjMKVOmQKvVhjx3Sz4mcrm8zppSxsfH48svv0ReXt5FH+NSxqtWq6HX66tVikkqlXJipRxOpxM+nw8GgwEmkwkWi4UNThsx9oIgomDDawDlNrx2u93weDz8PKAmIVj+tLZLVTocDphMJvaKICKiJo0ZEdRkzJw5E3q9Hs8991yZl69atQotWpwvtfPNN9+gZ8+epba98sor2LlzJ5599lns2bMHANC8eXNMnDgRvXv3hkKhQGpqKt5880389ddfGDNmDPr27YuHHnpIPM6QIUNw9913Iy4uDufOncP69euxceNGAMVljVavXo3Zs2fjjjvuQOfOnZGRkYE33ngDR44cEbMLSlq+fDlWrFhR6jZdmBERHMuaNWvw73//G3q9Hr/++itee+01OJ3OUlkj586dw6hRo7BgwQKcPHkSixYtEu+rTZs2ISEhAddffz2sVitWrlyJTZs2hdyGBx98EDabDatXrw4ZV/B+lEgkGDVqFIYOHYqoqCikp6fjo48+wq5du0rdFq9Sj78GvIAfH+gMt0/AwBXHcOGbl1Elg8VdPDHdOVqNeQNb4sp4HZw+AV8dK8RzO9Jh9xZPur1za2tEqGTYm27DpKtioZRJ8MURM57ekQafAGwc3QF9WxlCjt/s5d8BAFcn6vDc9Qno2UILW1EB9v78Mz755BP4fD70798fDz74IGbMmIHMzEz4/X489NBD6NatGyZPnow5c+agR48eIccdMGBAqdtb0oXP3eDj+MUXX+C+++5DbGwsbrzxRvTu3Rv3338/2rZtC7/fjyNHjuCdd95BZmZmyONS3nMruM/UqVPRrVs3yOVyZGdn47333kNKSkqpx/Hll1/G1q1b0aNHD0yYMAHt2rWD1WrF1q1bsXTpUnGCc8GCBThz5gz8fj8GDRqE06dPY8WKFVi4cCFmzJiBhx56CK1atcLhw4fx4osv4vLLL8ekSZPQvHlz7N27F6+++ircbjcMBgM0Gg2GDBmCm266qcznTPD1MXPmTDzwwANo27YtnnzySdhsNjzyyCPo2LEjAoEAMjIy8Prrr+P48eMV3vdNhVQqFVfq2e32Kmc/UcPBLAgiqkjJhtdKpVIM4rPhNTV2Op0OarUa+fn5tX4uZkUQEVFTx4wIor9NnDgRs2bNgsPhwNtvvw2PxwO5XF5q24XUajUWLlyIvLw8PPPMMygoKMDll18uNhG+0MCBAzFu3Di89dZbOHHiBDp06IDp06fD5XJh69at4n4PPPAA3nvvPaSnp+OBBx7Ac889h3vvvReHDx/GO++8g7Fjx+Jf//oXAFRr0jA+Ph59+/bFrFmzYDAY8Pzzz2P06NFYunSpOGk9dOhQTJw4scKVsiNGjMCyZcuwcuVKXH/99WKpoLS0tJD9cnNzMXv2bMyZMwf3338/7Ha7eD+OHj0agwYNwoIFC5Ceno7u3bvjmWeeQVFRUamyQ05jIrrFatA5WoOHNp4pFYQAIAYhtAop1t3dHvsy7Ri44iiidXIsvKU1XrmpJSZ/fVbcv28rA7JtXtz+6XG0NamwZFhbHMpx4OMD+Riz/jR2/bszVvyZh48PnF/93yZSiTV3t8dLuzIxdfNZXJ33PR4beTMefPBBLFiwAJs2bUK3bt0wadIkPPLII7jqqqswaNAgTJ48GRaLBc899xyWLFmCTZs2iYGbi5GQkIB+/fph9uzZ4uOk0Wiwdu1anDp1ChqNBuPGjcOcOXPw0EMPhUwelPfcEgQBjz76KORyOR599FG4XC60bt0aTqcTubm5GD58OD766CMsX74c3333Hex2O5o3b4558+Zh69atmDdvHlq1aoUnnngCHo8nJDg2ePBgfPnll5gyZQqA4hR4oDio8tZbb8HlcuH555/H888/D6/Xi7lz50Kj0eDFF1/EnXfeiS1btkAmk+GWW25B//79K33OjB8/HosXL0ZWVhasVivefPNNnDhxAgsWLIAgCGjfvj17SpQgCIJYhk2v10Mul4s1xalhC/aCUKvVzIIgonKV1/BapVJBrVaz4TU1WiqVqs7KfDIrgoiImjoGIqhJueaaa7B58+aQbZ988gk++eQTFBUVwev1io1vg8raVtLAgQMRGRmJhx9+WCyXE1yBXpaxY8di8eLF2L17N4DirIPWrVtj6NChIYGINWvWYO/evQCKMx6WL1+OhIQEpKWlwWazAaheiaggiUSCl19+WQxefPvtt+jVqxeWLl0Ku90Oh8NRpdJKv/zyi5jFsWrVKtx1113o2bNnqUCEIAhifwyz2SzWXlUoFLj33nvxxBNPiKvxs7Ky0K1bN9x2222lAhEuQzx6RCoAACcKKv7ifmcXE1RyKSZtOguHV8DRPGDmtjR8elc7vLAzA7mO4lIDhW4fZnybBiEAnChw49tTFvRrbcDHB/JR6PLDHwjA5vEjx36+NMG0a1pg3eGC4r4Rgh+OUwWQLFyIhQsXYv78+fB6vXjttdewdOlSTJ06Ff/3f/+HFStWiCvvg5OADofjkspXyeVyzJs3D0VFReK2CzNJ5s+fj40bN6J169ZISUkRt1f03IqJicGuXbtw5swZAMWPSVBwvDabTfz3sGHDkJubizfffBMAkJaWhubNm2P8+PH46KOPxIns9PR0vP/+++KxgoGIDz/8EMnJyQCAzZs3Y/z48Rg9erR43h9//BG9e/fGli1bYLPZcM8991TpObNs2TLs379f/H9MTAw+++wz8flZstQanWe32+H1esVm6RaLhZNNDVjJLAir1cpJDyKqMp/PJza9lkqlYqZEMGDt9/tDsiWIGqJgWaa6CkT4/X643W5otVp+JhMRUZPEQAQ1KX/88QcWLFgQsq26/Q8u1L59e5w8ebJKx1Gr1UhISMCTTz6JJ554Qtwuk8nE4ELQqVOnxH8HU4VNJlOpif7qys7ODsmgyM/PR2RkZLWPc/r06ZD/m81mmEymKl8/ISEBGo0Gr732Wsh2uVyOkydPltrfr9BAWmpr2S5vpkZyjhMO7/lVv79k2CCTStC+mRq5juL7+miuC0KJBd/Zdi+6RGsqPHZSjAZdojW4q2vxRLpE6AFpwA+ZTIa4uDikpqbCZrPh1Vdfxauvvork5GR8+umnVRx51WVnZ4cEIYDi+3TcuHHo3LkzIiIixJ4UsbGxIYGIip5b69evx2OPPYbevXtj//792LVrV6nHuqRgSaWSkpOTodVqER0djZycHAAotwRSybGYzWY4nU5kZWWJq7gdDgcMBgPMZjPatGlT5efMsWPHQv6/du1aPPHEExg0aBD279+PH374ocKAYVPm8XhQWFgIo9EIk8nEhuANELMgiKgmCYIQ0vOsZAknjUbDhtfUYKlUKgiCENIzsLYxK4KIiJoyBiKoSXG5XDU++VidCTqNpniS+/XXXxdXdAdd+KOtZIPA4Kry8so9VceFjQcDgYA4YX2px6nO+IL3xaxZs5CbmxtyWVk/BgISGU79nQnRIUqNQ9mXXsPeJ4SWnQkEgMpugk4hw4o/8/C/33IhCfgQmbkfMad3AIA46Q4A3bt3h9/vR1RUFNRqdY3X3C/rh8tLL72E7OxsvP7668jLy4NUKsWyZcsgl4e+1Vf03Nq8eTP27duHPn364Morr8To0aOxePFifPHFFzU+3rLG4vcXB3WMRiOkUincbrc4xuo8Zy68v1esWIEdO3agT58+uOqqqzB27Fi8+OKL+PHHHy/pdjVWwWbgBoMBRqMRDodDzGai+q1kFoTFYmEQiYhqXMksCDa8poZMqVTW+ecksyKIiKgpq/7sIxGFOH36NNq1aweDwVDpvmazGbm5uYiLi0NmZmbI37lz56p8Tp/Pd1HBg3AJ/giVyWTitpSUFHg8HsTExJS6Ly6cZAYAScCPQ9kOHM114pGrYlBWvMCoKj7+8XwXkmI00CrO30dXJ+jhFwI4mV/1L/wefwAyaeiZDmQ70LG5GmcK3ThjdiMtu0Acd/B2du3aFaNGjcLTTz8Np9OJRx99NOQYXq+3xh8/o9GIVq1a4eOPP8bvv/+O1NRU6PX6izpWbm4uvvrqKzz//PNYs2YNbr311nL3TU1NRdeuXUO2JSUlwW63l/k4VoXJZEIgEEBhYWHIBEZ1nzMXSk9Px7p16zBjxgzs3r07pDE7lc1qtcJqtUKj0SAyMrJBve80NRKJBAaDAREREfD5fDCbzQxCEFGt8/v9cDqdKCoqQn5+PiwWC3w+HzQaDUwmE5o1awaDwQCVSlUjC2qIaopMJoNcLg9LabFgyTO1Wl3n5yYiIgonzihQk6JQKGAymUL+jEbjJR1zx44dKCgowIsvvoikpCTExcWhX79+6NKlS5n7L1++HKNHj8bw4cORmJiItm3b4uabb8aIESOqfM5z585Bq9WiV69eMBqNUKlUl3Qbalt2djYEQcA111yDiIgIMUPgs88+wyOPPILBgwcjPj4eHTp0wB133IHBgweXOobM60QAEkzZfBbtotT4+r7LMfAyI1pHKNElWoPHr2mBlXdeBgBYd7gAbp+ARbe2RqfmavRtpcfLg1pizeECsT9EVaQVeXBtSz3i9ApEaYqDHG/tzUbvBD1eGZSIrjFatI3S4LrrrsPUqVMBFK/anzVrFtavX49ff/0V//3vfzFgwAD069dPPO65c+fQo0cPNG/e/JKff0FWqxVFRUUYOnQo4uPjccUVV2DSpEnVPs4jjzyC3r17o0WLFujQoQOuuOIKpKamlrv/xo0bER0djalTp6Jly5a47rrrMHbsWKxdu7ZajY4lEgnUajUkEglcLhcKCwtL9Sao7nMmSKlUYurUqejRowdiY2ORlJSETp06VXi76Lzg4yGVSmEymaBQKMI9JLqAUqmEyWSCUqmExWKBxWJhaRQiqnPBhtdWqxX5+fliyUW5XA6j0YhmzZohIiICGo0mZHEKUTgEyzKFIxBRMiuCiIioKWFpJmpSrr76aqxfvz5kW2pqKsaMGXPRx/T5fJgxYwYefvhhzJs3DzKZDGfPnhWb915o8+bNcLvduOeeezBhwgS4XC6cOXMG69atq/I5Dx8+jI0bN2L27NmIiIjA8uXLsWLFiou+DbUtLy8Py5cvx0MPPYQZM2Zg27ZteOWVV/Dhhx+iqKgIo0ePRlxcHGw2G06cOIFPPvmk1DHU1kxAKsPvWQ7cuPwoHr+2BRbe0gpRGjmy7V78mm7HMzvSAQBOXwB3rTmJeQNbYvuYTnD6BHx1rBDP/X15Vb28OxOv39wKv03sCrVcimYv/44juU7885PjeOb6eHx9f2dIA+2RlZGBnTt3AgCmTJkCl8uFJUuWAADOnDmDJUuW4PHHH8eRI0eQl5eHZcuWYfr06fjkk0+gVCoxYMCAS7yHi3/8z5kzB1OmTMGyZcuQlpaGt99+GwsXLqzWcaRSKR599FFER0fDbrdj3759WLRoUbn75+XlYdasWZgwYQKWLFkCq9WKzZs34+OPP67yOYMTFHK5HIFAoFS/lJKq85wJEgQBRqMRs2bNgslkQlFREXbv3o1ly5ZVeYxNXXCFvdFoREREBOx2e42XG6PqK9kLwu12w2azMQBBRPVGVRteu93uOq3RTwQUByLC2WidvSKIiKgpkvTs2bPqS1aJiMLEq9TjrwEvhHsYpXTZ+TzknvInzqliarUaer0ePp+Pq7gbCK1WC51OJ656rU7mC9WckvXYbTYbyzARUYNSsuG1TCZjw2uqUzKZDFFRUSgqKgprMMJgMEChUKCgoCBsYyAiIqpLzIggogZB4bFB7rbCp6q8F0ddkbstDEJcpGA9e5VKxUbIDYzD4YDP54PBYBAzTC4so0W1h1kQRNQYVNbw2uv1ivuw4TXVNKVSKQa/wolZEURE1NQwEEFEDYYx5xAKEq4GpPWgrrDghzEnOdyjaJCCpZgkEknYV6LRxfF4PGKpJpPJBKvVyhX5dUCpVMJgKA7GWiwW3udE1CgEG147nU5IJBIxKKHRaKDT6cQ6/sE/ZuLRpQp3Waagkr0iGIggIqKmgIEIImowmqX+hIKW14Z7GMWkMjRL3RPuUTQ4wUkFlmJq+ARBQGFhIfR6PYxGI5xOZ4X9PejiMQuCiJqKYMPrYKBVLpdDpVJBqVRCrVYjEAiEZEswI4+qSyqVQqFQwGKxhHsoAJgVQURETQsDEUTUYGhsWdCaU+CIaAVIpeEbiOCHtigVGtu58I2hgWEppsbLZrPB5/NBr9dDLpczwFTDmAVBRE1ZsOG13W4vt+G12+2Gx+Nhw2uqEpVKVS/KMgUxK4KIiJqSMM7kERFVX3TKzvAGIQBAKkN0yvfhHUMDIpfLYTKZoFAoUFRUxCBEI+RyuVBYWAipVAqTyQSlUhnuITV4weBdREQEvF4vzGYzgxBE1KQJggCXywWLxYK8vDyxvKNKpUJkZCSaN28Oo9EItVoNabi/K1K9FSzLVJ9KfAUDbRqNJtxDISIiqlXMiCCiBsWYkwxDdjKs0Z3D0ytC8MOYe4T9IaqIpZiaDp/PB7PZLE6e2+12OByOcA+rQWIWBBFR5cpqeK1SqdjwmspV38oyBQmCALfbDY1GA6fTGe7hEBER1RouFSGiBkUCIPHIOkj9HiBQx5PaAQFSvwcJR9ZBUrdnbnAkEgmMRiP0ej2cTicKCwsZhGgCAoEALBYLbDYbtFotIiIiIJHw1VJVzIIgIro4wYbXhYWFyM/Ph8Vigd/vh0ajgclkQrNmzWAwGKBUKvm51IQplcp6VZapJGZFEBFRU8CMCCJqcBQeKxIPr0FqzzF1e2KJFImH10DhYUPeisjlchiNRkgkErFsAjUtTqcTPp8PRqMRJpMJFouFq1ErwSwIIqKawYbXVB6VSgWv11uvyjIFBUuPMSuCiIgaM0nPnj3r36cwEVEV5Lb+P2R1ur3Ozhd3dAOiz+6us/M1RCzFRCVJpVIYjUbI5XLYbDY2YSyDRCKBXq+HWq2G2+2G1WqtlxMkRESNQcmG18HsCDa8bhokEgmaNWtWr7+PSKVSREVFwW63MxhBRESNEjMiiKjBCgYFsjrdXlymSVIL1eb+Pm78XxvQPJVBiPIES8qoVCo4HA42pCYAxav7CgsLodPpYDAYoFAoYLVawz2seoNZEEREdSu46jw4ER0MSKhUKmi1WrFsT/CPCyoaD5VKBQD1+rM2+PzUarUMRBARUaPEQAQRNWjRZ3dD4SpCete7IciUNdvAWvBD6vcg8fAaRGYfrLnjNjIKhQIGgwESiQSFhYVcTUil2O12+Hw+GAwGyOVysXZ3U8UsCCKi+oENr5uO+lyWqSSHwwG1Ws0STURE1CixNBMRNQpepQHpXe6CNTYJEPyXFpD4+/rG7ENIOLKOPSEqoNVqodVq4fV6YbVauXKQKiSTyWA0GiGVSmG1Wptk/5CSWRA2m61er8wkImqqJBJJSAknqVQKQRDg8XjgdrsbxIQ2ndcQyjKVpNfroVKpkJ+fH+6hEBER1SgGIoio0QgAsMQkIbfNADhMbaofkPh7f635DKJTvocxJxmS2hpsAyeRSGA0GqFQKOBwOOBwOMI9JGogSmYDNKUyXsyCICJquEo2vJbL5Wx43cCoVCoYjUbk5+c3iEUz7BVBRESNFQMRRNQoOfVxyG91LSwxSfCpjMUbBT8kOP+WF4BEDFTI3RYYc5LRLPUnaGxZ4Rhyg1GyFJPFYmEpJroowcbmTSGbhlkQRESNBxteNzzBbMzCwsJwD6XKmBVBRESNEQMRRNTo+ZR6OIyJcBni4ZdrEJDKIBH8kPmcUFszobWkQ87yS1XCUkxUk+RyOYzG4kCh1WptdJM3JZu4MwuCiKhxKhmUkMlkEAQhJFuC35XCK1iWqaFlFzArgoiIGiMGIoiIqFIsxUS1peRzqzH92A42OwWYBUFE1FSUbHgtl8vZ8LoeaGhlmUpiVgQRETU28nAPgIiI6jeFQiGuWi8qKmp0q9YpvAKBAIqKiqDT6aDX66FQKGosc8Cr1MMZzIZSaBCQyCAJ+CHzFmdDaSzpNd6MnlkQRERNl9/vh9PphNPpDGl4HSxHyIbXdU+pVMLr9Ta4IAQAOBwOqNVqaDSaRrNQg4iImjYGIoiIqFwlSzFZLBb+YKZaY7fb4fV6YTAYEBkZCYvFclHNP8/3h+kGn6q4L0PF/WGsMOYcqpH+MCWzIIqKiuDxeC7peERE1HAFAgG43W4xI06hUIiBCbVazYbXdUSlUsFut4d7GBdFEAS4XC5otVoGIoiIqFFgaSYiIipFKpXCYDCwFBPVOZlMBqPRCJlMBqvVWqWSRgEAlpgk5LYdAEdkG0Dwi4GGKvl7f605BdEpO2HMSYakGmMumQXhcrlgs9kYtCMionKV1fDa5/OJQQlmn9YMpVKJiIgIFBQUNNhAD3tFEBFRY8JABBERhQiWYgoEAo2ygTA1DAaDAWq1Gk6nEzZb+eWTvEoD0rvcBWtsEiAIgFR68Sf9OyBhyE5G4pF1UHislV6lZBaE1WplFgQREVVbRQ2v3W43g9sXyWAwQC6Xw2w2h3sol4S9IoiIqLFgIIKIiEQsxUT1iVqthl6vh8/ng8ViKVXfuTC2O9K73g1BpqxeBkRlBD+kfg8SD69BZPbBMndhFgQREdUGmUwGlUoFpVLJhteXqFmzZnA6nQ0+s5dZEURE1FgwEEFERCzFRPWWXC6H0WiERCKBxWIRM3RyW/dDVqdhQEAAJJeQBVGev48bd3QDos/uDrmIWRBERFQXSja8ViqVkEqlEAQBbrdbLOHEIHjZGkNZppKCWREFBQV8zImIqMFis2oioiauZCmmoqIilmKiesXn88FsNsNoNCIiIgIOhwNno68sDkIAtROEKHHcrE63AwCiz+5mFgQREdWpihpeazQaNryugFKphM/nazT3icPhgFqtFstWEhERNUQMRBARNWE6nQ5arRZutxtWq5WTqlQvBYNkWq0WtvheyGo3rE7Pn9Xpdmj9DrRypgAAioqKmAVBRER1zuv1wuv1wm63iw2vVSoVdDqdWMqQDa+LBRcNNBaCIMDlckGr1cLlcvE7OxERNUgMRBARNUFSqRRGoxFyuRw2m40rq6hBKPLJcLz1zbVXjqk8AQEpHYcj6o+34TFn88c/ERGFXXBiOjjZHsyUUKlU0Gq1YsPrYBmnpvTZpVAoIJVKxUySxiKYFaHRaFhGlYiIGiQGIoiImhilUgmDwYBAIIDCwkI2PaQGIQAgvctd8EuVdRuEAACJFH6pAsda3YzWBcshqduzExERVSqYCQGENrw2GAxNruG1SqWC3+9vdLczGHzSaDRwOp1NKrhERESNAwMRRERNCEsxUUNliUmCNTYpfAOQymCJ7QZLTBIicpLDNw4iIqJK+P1+OBwOOByOkIbXGo0GOp0Ofr8/pIRTY/s+2NjKMpXErAgiImrIGIggImoCWIqJGrrcNgMAQQCkdZwNUZLgR26b/gxEEBFRg9HUGl431rJMQcyKICKihoyBCCKiRo6lmKihc+rj4DC1CfcwAKkMDlNbOPUtoLGdC/doiIiIqq2xN7xWKpWNsixTScyKICKihoqBCCKiRoylmKihM5lMGDf1KfTrcTniDErkOXw4lOPE+/tysOustdLrj+oWhf/emIjLFh4M2b5xdAckZzvxzI70Mq+X/1Qv3P/5KWw+UST+P8jqXI6zp07gww8/xB9//HEJt46IiCh8GmPDa5VKJfbKqAqvUg+nMREuQzz8Cg0CEhkkAT9kXifU1kxoLOlQeGy1OOLqEwQBTqeTWRFERNTgMBBBRNQIsRQTNQaxsbF4++23kR/Q4j/fZ+JIjhNymQQ3tDVi/k0t0eeDI3U6nslfp2DHaQtiZG681MODl156Cf/+97+RlZVVp+MgIiKqDSUbXsvlcjEw0VAaXsvlcshkskrLMjn1cchvdS0sMd3gUxmKNwp+SHB+Qj8ACSCVFR/XbYUx5xCapf4Eja1+fOYHAxHMiiAiooaEgQgiokaGpZiosZg2bRoCkGDQxyfg8Ari9mN5LnxyMB8A8HDvGIzu1gytI5UodPmx9WQR/rMzA3avgOta6fHOrW0AnM9oeOXHLMz/8eImEYpcfuTYfciBDK++8z9sWLUCV155Jb766qtLu6FERET1jM/ng8/na1ANr1UqlZjFcaEAAEtMEnLbDoAjsg0g+MVAAwBAKkN5t8CnMqAg4WoUtLwWWnMKolN2wpiTDElt3IgqYlYEERE1RAxEEBE1IizFRI2FwWDAVVddhXdXfQmHt1Wpyy3u4maaQiCAWdvTcLbIgzaRSrx6Uyv8Z0ACntyWhl/T7Zi1PQ2z+sbj6g8OAwDsHqHUsS5Goao5gOLVl0RERI1ZdRpeu91uCELNfNZWl0qlKjMbwqs0IL3LXbDGJgHBsZUMQlTF3/s7Ilri7BXjYMhORuKRdVB4Ki8TWVuYFUFERA0Nfz0TETUCJUsxWa1WsdYvUUOVkJAAqVSK42ZP6VWLJbz/W67477QiD/67KxOvD26FJ7elwSsEYHX7EUAAOfaaywzSSAOY8K+R8Pv9OHDgQI0dl4iIqCGojw2vZTJZmWWZCmO7I73r3RBkyuINUumlnejv7yPW6M441ncmEg+vQWT2wUquVDuYFUFERA0NAxFERA0cSzFRYySRFBc8EGRKSBAot1zC9a0NmHZNLDo0U8OglEEmlUCjkEIjl8Dpq9kf5P/7Z1v4AwFo5FIUWB149dVXcfr06Ro9BxERUUNSXxpel1WWKbd1P2R1GgYEBEByiQGIC0llECQqpPYcA+/RDYg+u7tmj19FzIogIqKGhIEIIqIGTK/XQ6PRwOVywWazcSUUNRrp6ekQBAFtWzQHzGXv0zJCiU9HtMOyP3Lx312ZMDv9uLqlHm8PaQ2FTAqnz1+jY3p2Rzp+SLHC6nIDR39A/LGtNXp8IiKihq6ihtcAQrIlanLxjEqlEs8LlAhCADUfhAj6+7hZnW4HgLAEI5gVQUREDUktfSITEVFtkslkiIyMhFqthtVqZT8IanSsViv27duHe/v3gFZR+uuKUSVDzxZaSCXAczsy8FumA6fMbsTpFSH7efwByCQ1004yx+7FmUI38h0+SISaDXIQERE1NsFm14WFhcjPz4fVaoXf74dGo4HJZEJUVBT0ej2USuUlnUcmk0Eul4tlmQpju58PQtSRrE63ozC2e52eMyjYUFyj0YTl/ERERFXFQAQRUQOjUqkQGRkJiUQCs9nMfhDUaL355puQSYBtY7vito6RuMykwuXN1Bj/j2hsvb8jTpvdUMqkeOjKaLSOUOLurlEY27N5yDFSizzQq2To19qAKI0MGvn5oEQzrRxJMZqQv2ht5cmiAUgg8zlr/PYSERE1VsGG11arFfn5+SgsLITb7YZCoUBERASaN2+OiIgIaDQaSKvZxyFYlsnj8RQ3pu56d3E5proUEJDe9W54lfq6PS+K79tgVoSkhhZfEBER1QaWZiIiakBYiomakqysLNz35FyMmDgdc25IQKxOgXyHDweyHXhiWyoO5zjxzI50PHp1LJ67PgE/p1nx4g+ZWHxbG/EY+zLsWPZ7LpYMa4tmWjle+TEL83/MAgCM6BqFEV2jQs750q5MvP7TuYoHJpVBbc2s6ZtLRETUZFzY8FqlUkGpVF5Uw2ulUlncfwJAepe7ihtT11Y5pvJIpBBkSmR0uQut/1yOug4HOBwO9oogIqJ6T9KzZ0/OYhER1XMymQxGoxEymQw2m41ZENRkeJV6/DXghXAPo5QuO5+H3GML9zCIiIganWBfCaVSCZlMJmY7BP9KLsSRSqVo1qwZioqKkBt5Oc5eMS6MIy/W+o9liMhJrvPz6nQ6qNVqFBQUcLESERHVS8yIICKq51QqFQwGA/x+P8xmM/x+1qanpkPhsUHutsKnMoR7KCK528IgBBERUS2pTsNrhUKBQCAAj8eD3DYDAEEAqlnaqUYJfuS26R+WQASzIoiIqL5jjwgionpMr9fDaDTC7XYzCEFNljHnEFBfmkMLfhjDMLlARETUFJXV8FoQBLHhtU6ngyAI+PSzNbh/UO/wBiEAQCqDw9QWTn2LOj81e0UQEVF9x4wIIqJ6qGQpJqvVylJM1KQ1S/0JBS2vDfcwikllaJa6J9yjICIiahQWLFiAkydPYtGiRSHbBw8ejMmTJ+O2224TtwUbXrvdbgDFJZwiIiIAACPf2YbThk5ABd0Zrm2px4y+cUiK0UAllyLL6sG+DDumbUmFVwjgulZ6fDn6chS6fOjy9iG4/efLG13RQovtYzsBAJq9/HupY+99qAtaRSjRc3Eycqxu5Le6DolHPhdvY8+ePQEUZ3tkZ2fjm2++waeffooxY8Zg7NixFd5HAwYMqPDykpgVQURE9RkDEURE9QxLMRGF0tiyoDWnwBHRKuzlFrRFqdDYKmlmTURERLVOJpMhEAjAbDbjqCwRPn/5QYiOzdRYc3d7fLA/F099mwaXT8BlJjVu6xgJmRTwCuf3tbkF3Hp5JNb/ZRa33dujGdKK3GgZoSp17KsTdVDLJfjyWCFGJjXDW79kwxKTBPwdiACATZs24cMPP4RSqcQVV1yB6dOnw2az4bPPPsOXX34p7vfee+9h06ZN2LRp00XdJyWzIpxOJ3tFEBFRvcLSTERE9YjBYGApJqIyRKfsrBflFqJTvg/vGIiIiJqYmTNn4sUXX8Tdd9+NdevWYcOGDXj00Ueh0WiKe0UodNg3rQ8mXBld7jH6tzUgx+7FC99n4GieCymFHnx3xoLHvkmFyxc6Wb86OR/3dm8m/l8tl2B4ZxNWJxeUeez7ujfH50fMWJNcIF7PpzLCp9SL+7hcLpjNZjEb4vTp0/jHP/4hbg/+CYIAh8MRsq26HA4HJBIJNBpNta9LRERUmxiIICKqB2QyGUwmE1QqFSwWC6xWa7iHRFSvGHOSYchODl+viIAfxuxD7A9BREQUBj179kR8fDwee+wxvPzyyxg8eDAGDRoEj8cDpzGx0uvn2H2I1StwTUt9pfuuSS5An5Z6JBgVAIDbOpqQWuTBwXOlSx3plVL8s1Mk1hwuwPcpFhhUMvRJ1AEAHOWMq1u3bmjZsiV8Pl+lY7kY7BVBRET1FQMRRERhplarYTKZAABms1mse0tE50kAJB5ZB6nfAwSESvevUQEBMr8XXTO2Qy6T1e25iYiICDabDW+99RbS0tKwd+9e7Nu3D926dYPb7YbLEA9UUoFo41Ez1h8xY9O9l+Pw5G74aPhleLBXNAzK0lMiuQ4fdpy2YFS34uyGe7s3w6cH88s87h2dTThtduNYngtCAPjiLzPu69EcEPzF4/rbsGHDsHnzZmzbtg1vvfUWpFIp1q9ff/F3SCWCWRFarbbWzkFERFRdDEQQEYWRwWCAwWAQ07JZiomofAqPFYmH1wCSOv76IpGi5ZG1UPrsYuYSERER1Z2UlBQIwvmFCEVFRTAYDAgEAvArQksQvTa4Jc4+3kP8AwAhAEzZfBZJ7xzCCzszkGX14LFrYrHnwS6I1ZVunfnJwXyM6tYMrSOUuDJeh7WHyy7LdG/3ZiGXrT1cgH92jIRBKYFffn5c27dvx4MPPogpU6Zg7969WLlyJQ4fPnxJ90lFmBVBRET1EZtVExGFgUwmg9FohEwmg8ViYRYEURVFZh+E9+gGZHW6vc7OGXd0A4xZf8IMQK/Xw2g0wul0wmaz1dkYiIiIGiO73Q6dTldqu16vD/mcLVnGSCKRQFqib1RAEpqt+PLuLCz6JafM82XZvFhzuABrDhfgpV1Z+HVCF4y9Ihqv/JgVst/2U0V44+ZWeHNIa2w9WQSzq/RioY7N1OidoEevOB2e758gbpdLJbijSzNsPnl+XHa7HZmZmQCAF154AZ988gmOHDmC33//vcxx1gSHwwG1Wg2tVgu73V5r5yEiIqoqBiKIiAB4lXo4jYlwGeLhV2gQkMggCfgh8zqhtmZCY0mHwlMzk45qtRp6vR5+v59ZEEQXIfrsbgAoDkYEhNrJkPj7uPF/bUDz1N3iZpvNBq/XC4PBAIVCAYvFwtcwERHRRUpLS0Pv3r1Lbb/88suRnp5e5nWUSiUAiBkSkkDo53Cew4c8R+X9F4rcfmTbfNAqSn+P8AeANcn5mNqnBUZ8drLM69/boxn2pFoxY1tayPbR3Zvh3h7R2LKh7O8HLpcLn3/+OR5++GE89NBDlY7zYgUCAbhcLmg0GjgcDgQCldSvIiIiqmUMRBBRk+XUxyG/1bWwxHSDT2Uo3ij4ISlRZDYACSAtXs0kd1thzDmEZqk/QWPLKuuQFZJIJNDr9VCr1VxNTXSJos/uhsJVhPSud0OQKcXXaY0Q/JD6PUg8vAaR2QdLXex2u+Hz+WA0GmEymWC1WpnVREREdBG+/PJL3HHHHZgyZQq+/vpreL1e9OnTBzfccAOefvrpMq+jUqlCyjTJvM5KzzOmZ3N0i9Fg0/FCpBS6oZJLMTIpCp2aq/HUt2llXuelXVl4+5dsFDhLBxTkUuDurlF4eXcWjua5Qi5beSAfj1wVi8tj9LCeKHs8X331Fe6//37069cPu3btqnT8F4tZEUREVJ8wEEFETUoAgCUmCbltB8AR2QYQ/KETmFJZub3ufCoDChKuRkHLa6E1pyA6ZSeMOcmoStXVYCkmqVTKUkxENSQy+yB05jNI73IXrLFJpV/P1fX39Y25R5BwZF2FWVDBjCaDwcBSTURERBcpKysLjz76KB544AG89tprUCgUSE1NxX/+8x/s27ev1P4SiQRKpTIkG1FtzURlX8h/z7KjT6IOr9/cCi30Ctg9Ao7mOXH/+tP4Ka3sz2+vECgzCAEAt3SIRJRGjq+PF5a67Hi+C8fynLi7T3ss3VP2eKxWK7Zt24axY8di9+7dtZatEOwVodVqmRVBRERhJ+nZsyc/iYioSfAqDSUmLAVAegnlXP6esDRkJyPxyDooPNZydw2WYvL5fLBarSzjQlTDxABjmwFwmNpUPyDx9/5a8xlEp3xf5QBjkEqlgsFggN/vZ6kmIiKiWqRSqWA0GpGfny9mRXiVevw14IUwj6y0Ljufh7yGSrteColEgqioKLhcLmZFEBFRWDEjgoiahMLY7udLuACXFoQAxElOa3RnHOs7s8wSLizFRFQ3JAAicpIRkZNcouRaEnwqY/EOFZZcs8CYk3zRJdcAlmoiIiKqKyqVCl6vN6Q0k8Jjg9xtPV9qtR6Quy31IggBMCuCiIjqD2ZEEFGjl9u6H7I6Dav1prZxRzeITXTlcjmMRiMkEgmsVis8Hk/Nn5eIKuRT6uEINqGXaxCQyiAR/JD5ipvQay3pNT5JYDAYGHwkIiKqJc2bN4fdbofTGdoXIr3LnShIuLpme0ZdLMGPqIxfkHjk83CPRMSsCCIiqg+YEUFEjZoYhABqJwhR4rhZnW4HALTM3ieWYrJYLCErtoio7sg9NhjzjsKYd7TOzhkMPBoMBigUCpZqIiIiqiFKpRISiaTMrMNmqT+hoOW1YRhVGaQyNEstpzlEmDArgoiI6oNampUjIgq/wtju54MQdSSr0+1wtLwSTqcThYWFDEIQNUFutxtmsxkAYDKZoFKpwjwiIiKihk+lUsHn85X5/Vpjy4LWnFLcBy6cBD+05jPQ2M6FdxxlcDqdCAQC0Gq14R4KERE1UQxEEFGj5FUakN717uKySXUpIOCvVjej0FudVrdE1Nj4/X6YzWa43W4YjUbo9fpwD4mIiKhBUyqVFfZgik7Zeel94C6VVIbolO/DO4ZyBLMiNBoNJBL+ViEiorrHQAQRNToBAOld7ipuTF1b5ZjKI5HCL1Uio8tdYMIzEVmtVlgsFqjVaphMJshk9aB2NRERUQOjVCohlUorDEQYc5JhyE4GhDCVRBT8MGYfgjEnOTznrwJmRRARUTgxEEFEjY4lJgnW2KTwNauTymCJ7QZLTFJ4zk9E9QpLNREREV2aYFmmivouSQAkHlkHqd8Tlqxoqd+DhCPrUJ9zDZgVQURE4cRABBE1OrltBtSL+rC5bfqHdwxEVG+wVBMREdHFq6wsU5DCY0Xi4TVhyYpOPLwGCo+tbs97EZgVQURE4cJABBE1Kk59HBymNvWiPqzD1BZOfYvwjoOI6hWWaiIiIqoehUJRaVmmkiKzDyLu6IbaHdQF4o5uQGT2wTo958UqmRUhDfdvJiIialLk4R4AEVFFoqOjMW7cOPTu3RsRERHIz8/Hnj178NFHH8FisZTaP7/VtcV1YWuhLNM7t7bGqG7NSm3fcboId685BQD44+GueG9fDt7/LRcQ/MhvdR0Sj3wOAIiNjcXq1avh9/sxcuRI5OXliceIiorCmjVrIJPJMHLkSGRnZ9f4+ImofnC73fD5fDAajTCZTLBarVWeXCEiImpqVCoV/H5/hWWZLhR9djcAIKvT7cVlmmojQ+Lv48b/tQHNU3fX/PFrUTAQodFoYLfbwz0cIiJqIhiIIKJ6Ky4uDu+88w7S09Mxd+5cZGVloU2bNpg4cSKuuuoqPPLII7BarSHXscR0q9XeENtPFWHK5rMh29y+ctpSS2XFfSL+DkQE5eXl4aabbsKnn34qbhs8eDByc3PRogUzKIiagmCpJoPBAKPRCKfTCZut/pdzICIiqmsqlQoul6va14s+uxsqjxVpXUbAL1MAkhr8jSD4IfV7kHh4TYPJhCgpmBWh1WrhdDohhLusLRERNQkMRBBRvfXoo4/C5/PhySefhMfjAQDk5OTgxIkT+OSTT/DAAw9g4cKFWLVqFTZv3oxWbdvhmuv6wuL2Y8HP57D09/MZB0aVDHNuSMAtHSKgkknx5zkHntmRjsM5TgDAjL5xGNIhAu/+moNZ/eIQqZJj++kiPPZNKmye81/MPf4Acuy+Kt8Gn8oIn1IPeYl6sVu3bsXNN98cEoi4+eabsW3bNvzrX/+66PuLiBoeq9UKr9cLvV4PhUIBi8VSrRWfREREjVl1yzJdqJUzBXGHFiM5fhCssUmXnjn99/WNuUeQcGRdg+gJUR5mRRARUV1jQUAiqpcMBgN69+6NjRs3ikGIILPZjB07dmDAgAHitnvuuQd/ZRZgwLKjeHNvNl4a2BL92xjEy5fd3hbNtXLcs+YUblh+FAfOOfDFyA6IVJ//IdI2UoUhl0dg1NpTGLXuJK5tZcCjfS49Q8FhTAz5/08//QSDwYCkpCQAQFJSEgwGA3766adLPhcRNTwulwtmsxkAYDKZoFKpwjwiIiKi+iFYlsnnq/pCoCC1Wg2FQgGPORtt/lyG1n8sg7YorfhCoZpB/7/31xalovUfy9D6z+UNOggBsFcEERHVPWZEEFG9lJiYCKlUirNnz5Z5+dmzZ2E0GhEZGQkASE5Oxns7knGufSJOmd24KkGHib1j8H2KFVcn6tArToeObx+Ex19cRun5nRkYcnkE/tkxEh8dyAcASCTA5K/PihkQa5IL0K+1Af8tcd6b2kfg7OM9Qsay8OdzWPBzOT0dBD9chngY846Km3w+H7799lsMGTIEycnJGDJkCLZv335RP7CIqHFgqSYiIqLSlErlRWVDSKVS6HQ6OJ1OeL1eSABE5CQjIicZTn0c8ltdC0tMEnwqY/EVBD8kOF9uNSCRiKWc5G4LjDnJaJb6EzS2rJq4WfUGsyKIiKguMRBBRPWaRCKp0n5HjhyBX6GBBAEEAOzLtGPilTEAgKQYDXRKKU482j3kOhq5FG1N51cepxV5QsowZdu9aK4LfZv88awVT2xNC9lmdpUfQJAgAL9cU2r7li1b8M477+CDDz7A9ddfj0ceeQQyWe31tiCihoGlmoiIiIrJ5XLIZLJS2dFVodfrEQgEypxc19iykHjkc+DI5/Ap9XAYE+EyxMMv1yAglUGtkEPmc0GSdxpaS3pIidXGhr0iiIioLjEQQUT1UkZGBgRBQOvWrfHjjz+Wurx169awWCwoLCwUtwXKaUCnU8iQbfPin5+eKHVZkft8EMErhDadDgQCkF4QB3F4BZwprN6qrEAZdWjPnDmD1NRUPPfcczh79ixSUlLQrl27ah2XiBonl8sFr9crZn3ZbLaLro1NRETUUKlUKgiCAK/XW+3rqVQqFBUVIRAIVLiv3GODMe9oSPayXq+HXC4P+Z3RmAWzIrRaLbMxiYioVrEQIBHVSxaLBfv378ewYcOgVCpDLjOZTLjxxhuxc+dOcVvnzp0hCZxfNXxlvA7H810AgIPZDsToFfAFAjhT6A75K3DW/kpjSTk1aLds2YIrrrgCW7ZsqfUxEFHDEizV5PF4YDQaodfrwz0kIiKiOqVSqaodiJdIJNDr9XC73ReVSQEAgiA0qZ4JwawItVrdpG43ERHVPX7KEFG99eabb0KhUGD+/Pno3r07oqOj0bt3b7z22mvIy8vD0qVLxX2TkpLw0I09cVmUGg/0ao5hnUx4/7ccAMD3KVbsy7Dj4+GXoX8bA1pGKNE7QYdn+sWjZwtttcaklEkQo5OH/EVpQjMe4gxKJMVokBSjQdcYLbrGlT2JuGnTJgwbNgxff/31Rdw7RNQUWK1WWK1WqNVqmEwmlnAjIqImIViWqbqBiOB3bqvVetHnbmqBCKA4KyIQCECrrd5vIyIioupgaSYiqrcyMjIwceJEjB07Fs8//zwMBgMKCgqwZ88erFixIuQHxtq1a9G9Y3dMvL0rrB4Bz+1Ix84z5y8fufYknukXj3dubY1mWjlybD78nGZDjr16qd4D20XgrymhvSZO5LvQ54Mj4v+nXB2LKVfHltgjCf/9by4OHToUcj1BEGCxWKp1fiJqeliqiYiImpqLKcukUCigVqthsVgqLclUEUEQIJFIIJFILuk4DUkgEIDD4YBOp4PD4WCvCCIiqhWSnj17No1PViJqtFatWoV169Zh9Vdb8deAF8I9nFK67Hy+UTe5I6K6YzAYoFar4XQ6WceZiIgaLZPJBK/XW63PuqioKPj9fhQVFV3SueVyOUwmEwoKCuD3134Z1/pCIpEgKioKbreb3zGIiKhWNK18QyJq1BQeG+Tui0/Drg1yt4VBCCKqMSzVREREjZ1MJoNcLq9WjwedTgepVHpJJZmCgtkATe0zNpgVwV4RRERUW/jpQkSNijHnEFBOc+g6J/hhzEkO9yiIqJFxuVwwm80AgMjISKhUqjCPiIiIqOYEyzJVNRAhl8uh0Whgt9trpKSQIAgIBAJNcjKevSKIiKg2sUcEETV4o0aNEv/dLPUnFLS8NoyjKUEqQ7PUPeEeBRE1Qn6/H4WFhdDr9TAajSzVREREjYZKpapWNoTBYIDP54PT6ayxMTTVQAQA9oogIqJa0zQ/WYmo0dLYsqA1pwDh/tIs+KE1n4HGdi684yCiRisQCLBUExERNSrBskxut7tK+2u1WshkshopyVSS3+9vsoEIZkUQEVFtaZqfrETUqEWn7ATC/cNBKkN0yvfhHQMRNQks1URERI2FUqlEIBCoUkaETCaDVquFw+Go8abSgiA02UAEAPaKICKiWsFPFSJqdIw5yTBkJ4evV4TghzH7EPtDEFGdCZZq8ng8MBqN0Ov14R4SERFRtVWnLJPBYIDf74fD4ajxcTT1QASzIoiIqDY03U9WImq0JAASj6yD1O8BAnVcoikgQOr3IOHIOkjq9sxE1MSxVBMRETVkUqkUCoWiSmWZNBoN5HJ5jZdkCmrqgQiAWRFERFTz+IlCRI2SwmNF4uE1gKSO3+YkUiQeXgOFh01jiSg8WKqJiIgaIpVKVaWyTFKpFDqdDi6XCz6fr1bGwkAEsyKIiKjmNe1PViJq1CKzDyLu6IY6PWfc0Q2IzD5Yp+ckIroQSzUREVFDEyzLFAgEKtzPYDBAEATY7fZaG4sgCJBIJJBImnaOM7MiiIioJvHThIgateizu88HI2qrTNPfx43/awOiz+6unXMQEVXThaWaIiMjWaqJiIjqpaqWZVKpVFAqlbDZbJUGLC6FIBR/v2/qn5vMiiAioprEQAQRNXrRZ3ej1Z8rIPW5a76BteCH1OdGqz9XoHkqgxBEVP8ESzVJJBKWaiIionpJqVRWWpZJIpFAr9fD5XJVuaH1xQoGIpgJwKwIIiKqOfJwD4CIqC5EZh+EznwG6V3ugjU2qTggIb2EFU5/X9+YewQJR9axJwQR1WvBUk16vR5GoxFOpxM2G9+3iIioflCpVPB6vRVmOQTLDNbF55cgCAgEApx8R3FWhFarhVar5XcHIiK6JAxEEFGTofBY0ebPZbDEJCG3zQA4TG2qH5D4e39tUSqiU76HMScZTbtyLBE1FMFSTV6vF3q9HnK5HBaLRVz1SUREFA4SiQQKhaLCSW6lUgm1Wg2LxVKrJZlKYsPq8xwOB3Q6HRwOB783EBHRRWMggoiaFAmAiJxkROQkw6mPQ36ra2GJSYJPZSzeQfBDgvM/bgKQiIEKudsCY04ymqX+BI0tKwyjJyK6dC6XC16vF0ajESaTCTabrdKa3ERERLUlWDKwvM+iYEkmt9tdp59XDESc53Q6odFomBVBRESXRNKzZ8+6WU5ARFSP+ZR6OIyJcBni4ZdrEJDKIBH8kPmcUFszobWkQ87yS0TUiAQndtRqNUs1ERFR2ERERAAAioqKyrxcr9dDpVLBbDbX6Wp8o7F4oZLFYqmzc9ZnGo0GOp0OBQUFzIogIqKLwowIIiIAco8NxryjMOYdDfdQiIjqBEs1ERFRuFVWlkmhUECj0cBqtdb555MgCJDLOWUSFMyK0Ol0sFqt4R4OERE1QMwzJCIiImrCXC4XzGYzJBIJTCYTlEpluIdERERNhFKphEQigcfjKfNyvV4Pr9cLl8tVxyNjaaayOJ1OqFQq3i9ERHRR+OlBRERE1MT5/X4UFhbC4/EgIiICer0+3EMiIqImQKVSwev1lpntoNPpIJPJwrb6noGI0pxOJwRBgE6nC/dQiIioAeKnKhERERGJpZqsVivUajUiIyM5AUNERLVGIpFAqVSW2YBaJpNBo9HA4XDA7/eHYXTFgQiJRMLPwgsEsyJkMlm4h0JERA0MP1GJiIiISMRSTUREVBeCZZnKCkQYDAb4/X44HI4wjKxYMEuDgYhQwawIrVYb7qEQEVEDw09UIiIiIgrBUk1ERFTblEplmWWZNBoN5HJ52BsiMxBRPofDwawIIiKqNnm4B0BERERE9U+wVJPX64Ver4dcLofFYimzjjcREVFJXqUeTmMiXIZ4+BUaBCQySAJ+yLxOqK2Z0FjSoVKpYLfbQ64nlUqh0+ngdDrh8/nCNPpigiAgEAgwEFEGl8sFrVYLrVYb9oARERE1HAxEEBEREVG5XC4XfD4fDAYDTCYTrFYrPB5PuIdFRET1jFMfh/xW18IS0w0+laF4o+CHBAFxnwAkgLR4Ff1Jrx3G7EOIOvsjNLYsAMUlmQRBKBWgCBc2rC6fw+GAXq8Pax8PIiJqWCQ9e/YMVL4bERERETVlEokEer0earUaTqcTNpst3EMiIqIwCwCwxCQht+0AOCLbAIJfDDRUyd/7a80piM/Yg1aeNBQVFsLr9dbWkKslMjISPp+Pn3nliIqKgtfrZVYEERFVCTMiiIiIiKhSLNVEREQleZUGpHe5C9bYJCD4WVCdIESJ/R0RLXHSdC/y84+iRdFqKFA/AhHMiKgYsyKIiKg6mBFBRERERNUil8thNBohkUhYqomIqAkqjO2O9K53Q5Apqx98qIjgh9TvQeLhNYjMPlhzx71IwcB7YWFhuIdSbzErgoiIqoqhfSIiIiKqFp/PB7PZDI/Hg4iICOj1+nAPiYiI6khu635I7TkGglxVs0EIAJDKIMhVSO05Brmt/69mj30RBEGATFbDt7GRcTgcUKlUvJ+IiKhSDEQQERERUbUFSzVZrVao1WpERkayfAURUSOX27ofsjoNK/6PpJbe8/8+blan28MejBAEARKJJKxjqO9cLhcEQYBWqw33UIiIqJ7jr0UiIiIiumgulwuFhYWQSqUwmUxQKpXhHhIREdWCwtju54MQdSSr0+0ojO1ep+csKRiIYKC9YsyKICKiquCnKRERERFdkgtLNel0unAPiYiIapBXaUB617uBgFC3Jw4ISO96N7zK8JQAFP5uws1ARMWYFUFERFXBT1MiIiIiumQlSzVpNBqWaiIiaiQCANK73FXcmLq2yjGVRyKFIFMio8tdCNTtmQEAfr8fAAMRVcGsCCIiqgw/TYmIiIioxrBUExFR42KJSYI1NqnmG1NXlVQGS2w3WGKS6vzUgUAAgUCAgYgqYFYEERFVhp+mRERERFSjWKqJiKjxyG0zABDquCTThQQ/ctv0D8+pBYGBiCpiVgQREVWEn6ZEREREVONYqomo2JgxY/DBBx+EexghBg8ejK+++ircw6Aqio2Nxc6dO9GuXbs6P7dTHweHqQ0Q7vdvqQwOU1s49S2qtPuFz/FLeR3WdiCiqo/vggUL8Mgjj9TaOKpr6NCh+Oyzz7Bjxw7ceeedAJgVQUREFZOHewBERERE1Hi5XC74fD4YjUaYTCZYrVZ4PJ5wD4voonXp0gVvvfUW9u3bh1mzZoV7OBdl586d+OWXX2r9PIMHD8bkyZNx2223lXn5zJkzodfr8dxzz5V5+apVq9CiRQvMmTMHO3fuDLls2bJlaNOmDV5++WVs3bq1zOuPGTMGY8eOBVBc6z8/Px+//vor/ve//8FqtV78Datjubm5GD58OIqKiurkfP369cOwYcPQoUMHKFQapFu9+CXDjg/25+JQtrNOxlAmwY/8Vtch8cjn1b7qZ599hi+++KJK+44ZMwZ9+/bFQw89VHzaWg5EXPj49ujRAwsXLsTQoUNht9sv6dgXvgZyc3Oxe/dufPjhh3C5XBd9XK1Wi6lTp+Ldd9/Frl27QsbpcDig1+vhcDjEHhsNzYXvNxdavnw5VqxYUUejISJqPBiIICIiIqJaFSzVZDAYEBERAYfDccmTK0ThMmTIEHzxxRcYMmQImjVrhvz8/HAPqdo8Hk+DCQhmZ2fjlltuCZkY7Ny5M0wmE5zOyifFz5w5g+nTp0Mmk6FVq1aYMWMGdDod5syZU5vDrlGCIMBsNtfJucaPH4+7774b69evx/Lly/Fj29GIjIzAwHYReO76eNy95lSdjKNMUhmcLboBFxGIcLlcFz3xLggCFArFRV23qsevzce35GugW7duePLJJ6FWq/HGG2+U2lcul8Pn81V6zJiYGCgUCuzduxcFBQUhl7lcLmi1Wmi12koDfjKZrF4GK4YPHy7++4YbbsDYsWPxr3/9S9x24XuPVCqFEO7yZUREDQADEURERERU6wKBACwWC9RqNfR6PRQKBSwWC3+4U4OiVqsxYMAATJw4EVFRUbj55pvxySefhOwzatQo3HXXXVCr1fj+++9RWFgoXnbllVfiv//9L4YPHx4SjJs8eTLatm2L6dOnw2g0YurUqejevTsMBgMyMzPxySef4LvvvhP3X7BgAU6fPg2Px4MhQ4bA5/Phyy+/DFmhq9PpMGHCBFx33XXQ6/XIyMjA//73P+zdu7dUpkJwBfiaNWvw73//G3q9Hr/++itee+01ccJNo9Hg8ccfx3XXXQeHw4HVq1fjuuuuw8mTJ7Fo0aLauLsBANu3b8eIESMQHR2N3NxcAMXBoB07duCmm26q9Pp+v1+c5M3Ly8MPP/yAm2++OWSfIUOG4O6770ZcXBzOnTuH9evXY+PGjeLl48ePR9++fREdHY2CggJs374dH330kTiB2q5dOzzyyCPo2LEjAoEAMjIy8Prrr+P48eMAijMMxo0bh/j4eBQUFGD9+vVYu3atePxVq1Zh06ZNSEhIwPXXXw+r1YqVK1di06ZNAIpL96xevRoPPvggTp06Ja6Ynz59OsaPH4/WrVvj5MmTmD9/PtLS0sTj3nfffRg+fDhUKhV27tyJoqIiXHXVVeJK/wt17twZo0aNwttvv43169fDq9TjbLwSZ7OdOFBGJsQtHSLw5HVx6NhcjXM2L1YfyscbP52DP1B8ef5TvfDo5rO4qX0EBrQ1IsvqwezvMvDNyfOZHZ2aq/HCgAT0aamHwyvg+zMWPLMjHQXO4vt24+gOOJrrhE8ARnSNwpFcJ57+UY9Rw27BzTffjLi4OFitVvz888947733yg02XJjl0KNHD0yYMAFt2rSB3+9HSkoK5s6di549e4oZBMHg14IFC7Bnzx6MGTMGt9xyC0wmEywWC3bt2oW333671Ll0Oh02btyISZMm4fjx45BIJNiwYQPS09PF0koDBw7EQw89hHvuuSfk8bXZbFi4cCEAiI//N998g1deeQVA8YT3hAkTyn3dl6Xka2Dnzp3o1asXrr32Wrzxxhvi/fLFF1/gvvvuQ2xsLG688UbodDo8/PDDuO6666BQKHDs2DG8++67OHXqFAYPHoynnnoKQPFzFwBGjhyJ7OxsXHfddfjXv/6FNm3awGw245tvvsFHH30kftbv3LkTCxYswFVXXYVevXrhs88+w4oVK0Kul5eXh61bt2LlypUh13v11VfRp08f9O7dG3l5eVi8eDF++ukn8Xa2adMG48ePR/fu3SGRSHDy5Em88soryMzMBFD567ykkoEhm80Wsi34+ps5cyYeeOABtG3bFk8++SQOHjyIUaNGYejQoYiKikJ6ejo++ugj7Nq1K2SMEydORPfu3eF0OvHbb79h0aJFsFgsAIrfK8aMGYOEhAS4XC6cPHkSzz777CVlrxAR1ScMRBARERFRnWGpJmrIBgwYgNTUVKSlpeHbb7/F5MmTQwIR/fv3x9ixY/Hmm2/i0KFDGDRoEIYPH46srCwAwO+//w6bzYbrr78emzdvBlA8sThgwAAsXboUAKBUKnH8+HGsWrUKDocDffr0wdNPP43MzEwcPXpUPNdNN92EtWvXYtKkSejatStmzpyJ5ORk7N+/HxKJBK+88gq0Wi1eeuklZGZmonXr1hUG/uLj49G3b1/MmjULBoMBzz//PEaPHi2Oa9KkSUhKSsKzzz6LgoICjBs3Dh06dMDJkydr/H4uyWw2Y9++fRg8eDBWrlwJlUqF/v37Y9q0aVUKRJQUGxuL3r17h6z4HjhwIMaNG4e33noLJ06cQIcOHTB9+nS4XC6x5JPD4cArr7yCvLw8XHbZZXjiiSfgdDqxevVqAMAzzzyDEydOYMGCBRAEAe3btxeDFJdffjlmz56NFStWYOfOnejatSumTZsGi8USUlJqxIgRWLZsGVauXInrr78e06ZNw4EDB0ICCxd64IEHsHjxYhQWFuKxxx7DjBkzMGXKFPF23XfffVi4cCGSk5Nxww03YMSIETh37ly5x7vxxhvhcDiwYcMGAIDTmFjuvn0SdXj31jaYtT0NP6fZ0Nakwhs3twIAvLrn/Dlm9I3Df3Zm4PnvMvDQldF4/7Y26LE4GYUuP4wqGTaM6oCVB/Lx7I50qBVSPN8/AR/efhluX3VCPMbIpGb48I88DFl5DADgMyZCEAS8/fbbyMrKQnx8PKZNm4aJEyeKk/gVkUqlmDt3LjZt2oS5c+dCLpejc+fOCAQC2LlzJ9q2bYurrroK06dPB1A8kd+vXz/cddddePHFF5GSkoKoqKhyezrY7XacOnUKPXv2xPHjx3HZZZchEAigffv2UKvVcLlc6NGjBw4cOFDqurm5uZg9ezbmzJmD+++/H3a7PeQzsqLXfVW53W7I5eenghISEtCvXz/Mnj1bfI/4z3/+A7fbjZkzZ8Jut+O2227D66+/jvvvvx87d+5Ebm4uXn/9dUycOBE5OTkoKipCt27d8NRTT+Gdd97BwYMH0bFjR0ycOBGCIOCjjz4Szxfs17Fo0SL4/f5S14uPjxfv+wuv9/777+O9997D8OHD8cwzz2DkyJGwWq1o3rw5Fi5ciAMHDuDxxx+Hw+FAUlKS2DS7Kq/z6ho/fjwWL16MrKwsWK1WjB49GoMGDcKCBQuQnp6O7t2745lnnkFRUREOHDgAnU6HN954A5s3b8aiRYugUqkwfvx4PP/885g+fTqioqLw3HPP4f3338fu3buh1WrRvXv3ixobEVF9xUAEEREREdUplmqihmrIkCHYvn07AODXX3+FTqcLmVC88847sXnzZjHI8OGHH+If//gHlEolgOISLN999x1uvPFGcZ9evXpBr9fjhx9+AFC8an/NmjXiOb/44gv07t0b/fv3DwlEnD59Wpyky8jIwO23345evXph//79+Mc//oFOnTph7NixSE9PBwAxGFIeiUSCl19+WcyA+Pbbb9GrVy8sXboUGo0GgwcPxty5c/H7778DAObPnx+yqr82bdmyBQ8//LA4SZ+ZmYlTp6pWIqht27bYvHkzpFIpVCoVAIRkcIwdOxaLFy/G7t27AQDnzp1D69atMXToUHGCcuXKleL+2dnZ+Oyzz3DDDTeIgYiYmBh89tlnYtAgIyND3H/EiBH4/fff8fHHHwMA0tPT0aZNG4wcOTJkAvSXX34RV2evWrUKd911F3r27FlhIGLp0qXic2/VqlV4+eWXoVAo4PV6cccdd2Dz5s345ptvABRP6F555ZXQaDTlHi8xMRFZWVniZLTLEI+Hr2yOp/oliPskLToEq1vAjL5xeHPvOaxOLi7Lc7bIg3m7s/Cf/vEhgYhVh/Kx/q/ileRzf8jEhCtj0CtOh+/OWPDQP6JxKNuJubsyxf2nbj6LQ490QzuTCqfMbgDAKbMbL3z/930q+NHCEI/PPz9fnik7OxtLly7F448/XqVAhE6ng16vx969e8XV8qmpqeLlTqczJItAqVSiefPmMJvN2L9/P/x+P3JyckJejxf6888/0bNnT6xZswY9e/bE/v370apVK3Tr1g379u1Dz549xedPSYIgiKvjzWZzqc/Gil73VXH55ZfjxhtvxB9//CFuk8vlmDdvntijIikpCZ06dcLw4cPh9XoBAO+99x769u2L66+/Hps2bRL3LSwsFO+nMWPGYNWqVeLz2mw2IyoqCqNGjQoJKOzYsUN8XgLAjBkzQq6XlZWFDz/8EBMmTAi53jfffCNmhi1ZsgR33nknOnXqhH379uH222+H3W7HnDlzxCBg8L0PqNrrvLqWLVsm3u8KhQL33nsvnnjiCRw5ckS8Hd26dcNtt92GAwcO4I477sDJkyexZMkS8RjB99HExERoNBrI5XLs3r0b2dnZAIrLahERNSYMRBARERFR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P1bjq7766itkZmZi5cqViIiIwHnnlW303nbbbcjPz8dbb72F9PR0/Pzzz1ixYgVGjhwJQRBgNBpx00034d1338Xvv/+OkydP4uWXX1bz9hWbNm3Cjh07kJ2djT/++APz58/HlVdeCaPRWOvvd9OmTerV13/605+QlZVVaUhz//79cd555+H555/HkSNH8Mcff+Cll15CYmIiLrroIgDAHXfcgY0bN2Ljxo1IT0/HsmXLkJqaGnKesWPHYtGiRdi6dStycnKwdetWrFmzBrfcckut1w2UdRFcf/316NmzJ3bv3o2EhAT06tULzz33HI4cOYLMzEy8++67cDgc+NOf/qTeT6vV4qWXXsKxY8dw4sQJOJ1OuFwuyLKM4uJilJaWwmg04v/+7/+wePFi7NixA6mpqXj99dfh9Xpx8803h6xj+fLl2LVrF7KystTXy/Hjx2v12pAkCStWrMCRI0eQk5OD7777Dl9//XWlQoTD4cBzn/6IowUufHPchm+P2zCkW9kmac8oA/7aOwpTN6biqyOlSC314cdUO9YdKusseOCyaOzPdeP5H7NwtMiL/bluTN2YisHdwtEzylDlc5zt8GPhjjwk57mRWurDf3bl4/sTNtzWKyrkuAP5LszfvBuZmZn45ptvcPjwYfTv3x8AcNlllyE+Ph4vvfQSjh8/jn379qlXpteEIAiYPXs2JEnCCy+8EPK1v/71rzh27BiWLFmC9PR09er0/v37o0uXLupxGRkZWLx4MdLT05Genn7W92J13njjDfTp0wfr16/HokWL8OCDDyIhofJcn+3bt2P9+vXIysrCqlWrUFpaqs42ue6666DX6/HSSy8hJSUFu3fvxttvv40bbrgBUVFR8Hg8arxVcXExiouLq4ySiouLgyiKla7EX7dunfpeVAo+NX1vCIKAl19+GSkpKdi/fz++/fZb9eeo0+lw77334tVXX8XOnTuRnZ2NzZs349tvv63UNVjxPXH8+HF88cUXSElJQWZmJpYvX46srCwMGlSzK/2VLqji4mJ07NgR48ePx6uvvoqUlBQAwI8//oitW7eqn1+vvvoqevbsiW7dugGo2ed6bd7zS5cuxd69e5GamopVq1YhISFBLYY2hOiUpKaNbAQAUYPolC1NuwYiIqJ6wmgmIiKiVk7ZVAGADRs24JprrsGgQYPw8ccfq8cMGDAADz30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bUNR10FNvYwyogbt036u99NOnz693s9J1FZZ85IRnpsMe3TvpilgyhKs+Qc5H4KIiIiIWpQm7CcmIiIianpK1ApkuWkXIkswF59k1ApRMycA6HJwDUTJBwQb+XMjKEOUfIg7uIaDbYmIiIioRWEhgoiIiNq86JSkps17Bxi1QtSC6Hx2dDmwGhAa+XNDENHlwGromDFPRERERC0MCxFERETU5ilRK5ClplmALMGau59RK0QtSGTuPnQ6tK5RH7PToXWIzN3XqI9JRERERFQfWIggIiKiNo9RK0R0LqJTt54uRjTUZ8ep83b+Yx2iU7c2zGMQERERETUwFiKIiIiIwKgVIjo30albEb9nJcSAt/67qmQJYsCL+D0r0SGNRQgiIiIiarmExMTEYFMvgoiIiKi5yO82GNm9bm+0x+t0iFc5E7UGfn04Mi6+E/bYhLKChKg595Odur81dz/iDq5hoZKIiIiIWjwWIoiIiIgqUIsRQblhOiROnbfzH+t4lTNRKxIEYItJQH73oXBFda99QeLU8ebik4hO2QJrXjIj24iIiIioVWAhgoiIiKgKJbF9kdFnJGSNvm5XNlckSxAlH7ocWM2hs0StmDusEwrjB8EWk4CAwVp2oyxBwOn/+xWEoH6+aL02WPOS0T5tG0yO7KZYMhERERFRg2EhgoiIiKgajFohovoQ0IfBZe0CT3hnSFoTgqIGgixBE3DDaM+C2ZYBLT8TiIiIiKgVYyGCiIiI6AwYtUJERERERERUNyxEEBEREdUQo1aIiIiIiIiIao+FCCIiIqJzwKgVIiIiIiIiopphIYKIiIiIiIiIiIiIiBqM2NQLICIiIiIiIiIiIiKi1ouFCCIiIiIiIiIiIiIiajAsRBARERERERERERERUYNhIYKIiIiIiIiIiIiIiBoMCxFERERERERERERERNRgWIggIiIiIiIiIiIiIqIGw0IEERERERERERERERE1GBYiiIiIiIiIiIiIiIiowbAQQUREREREREREREREDYaFCCIiIiIiIiIiIiIiajAsRBARERERERERERERUYNhIYKIiIiIiIiIiIiIiBoMCxFERERERERERERERNRgWIggIiIiIiIiIiIiIqIGw0IEERERERERERERERE1GBYiiIiIiIiIiIiIiIiowbAQQUREREREREREREREDYaFCCIiIiIiIiIiIiIiajAsRBARERERERERERERUYNhIYKIiIiIiIiIiIiIiBoMCxFERERERERERERERNRgWIggIiIiIiIiIiIiIqIGw0IEERERERERERERERE1GBYiiIiIiIiIiIiIiIiowbAQQUREREREREREREREDYaFCCIiIiIiIiIiIiIiajAsRBARERERERERERERUYNhIYKIiIiIiIiIiIiIiBoMCxFERERERERERERERNRgWIggIiIiIiIiIiIiIqIGw0IEERERERERERERERE1GBYiiIiIiIiIiIiIiIiowbAQQUREREREREREREREDYaFCCIiIiIiIiIiIiIiajAsRBARERERERERERERUYNhIYKIiIiIiIiIiIiIiBoMCxFERERERERERERERNRgWIggIiIiIiIiIiIiIqIGw0IEERERERERERERERE1GBYiiIiIiIiIiIiIiIiowbAQQUREREREREREREREDYaFCCIiIiIiIiIiIiIiajAsRBARERERERERERERUYNhIYKIiIiIiIiIiIiIiBoMCxFERERERERERERERNRgWIggIiIiIiIiIiIiIqIGw0IEERERERERERERERE1GBYiiIiIiIiIiIiIiIiowbAQQUREREREREREREREDeb/AbT4fkWWZCYcAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total posts for `MonsterMMORPG` = 46. Using up to 20 posts for graph.\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABiIAAALJCAYAAAA57jvpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdeXhU9dk+8Pusc2bPJJCwg+KCCBKpdUFxqVqF6ltfSl1bcam1rVq1tlKr1mrVV2z707fa6tuqaK07Wq2KomKsa93qAgIKQgghkSWZNbOcM3PO748wQ0IWssw+9+e6uDQzZ858s0yW732e5xHq6+stEBERERERERERERER5YBY6AUQEREREREREREREVH5YhBBREREREREREREREQ5wyCCiIiIiIiIiIiIiIhyhkEEERERERERERERERHlDIMIIiIiIiIiIiIiIiLKGQYRRERERERERERERESUMwwiiIiIiIiIiIiIiIgoZxhEEBERERERERERERFRzjCIICIiIiIiIiIiIiKinGEQQUREREREREREREREOcMggoiIiIiIiIiIiIiIcoZBBBERERERERERERER5QyDCCIiIiIiIiIiIiIiyhkGEURERERERERERERElDMMIoiIiIiIiIiIiIiIKGcYRBARERERERERERERUc4wiCAiIiIiIiIiIiIiopxhEEFERERERERERERERDnDIIKIiIiIiIiIiIiIiHKGQQQREREREREREREREeUMgwgiIiIiIiIiIiIiIsoZBhFERERERERERERERJQzDCKIiIiIiIiIiIiIiChnGEQQEREREREREREREVHOMIggIiIiIiIiIiIiIqKcYRBBREREREREREREREQ5wyCCiIiIiIiIiIiIiIhyhkEEERERERERERERERHlDIMIIiIiIiIiIiIiIiLKGQYRRERERERERERERESUMwwiiIiIiIiIiIiIiIgoZxhEEBERERERERERERFRzjCIICIiIiIiIiIiIiKinGEQQUREREREREREREREOcMggoiIiIiIiIiIiIiIcoZBBBERERERERERERER5QyDCCIiIiIiIiIiIiIiyhkGEURERERERERERERElDMMIoiIiIiIiIiIiIiIKGcYRBARERERERERERERUc4wiCAiIiIiIiIiIiIiopxhEEFERERERERERERERDnDIIKIiIiIiIiIiIiIiHKGQQQREREREREREREREeUMgwgiIiIiIiIiIiIiIsoZBhFERERERERERERERJQzDCKIiIiIiIiIiIiIiChnGEQQEREREREREREREVHOMIggIiIiIiIiIiIiIqKcYRBBREREREREREREREQ5Ixd6AURERES0e4bqQswzDnH3GKQUOyxBgmClIBkxaOEW2EPNUPRIoZdJRERERERE1AODCCIiIqIiFXONRtuEWQjVTkfS5u680UxBgJU5xoIAiBIAQE6E4dm6AjVNb8MeaS3EkomIiIiIiIh6EOrr663dH0ZERERE+WABCNVOw7Y9jkG0ahJgpjJBw4DsON7hb8TIxgZ4tq6EkKvFEhEREREREQ0AgwgiIiKiImGobjRPnY9w3TTANAFxGOO8dgQS7i0rMW7VEih6OHsLJSIiIiIiIhoEBhFERERERSBQdwCa9z8VpqQOrgJid8wUxJSOcZ89jqotn2bvvEREREREREQDNIzL7IiIiIgoG7ZNPBJN9QtgyrbshhAAIEowZRua6hdg28TZ2T03ERERERER0QAwiCAiIiIqoG0Tj0TrlG93viHk6FezHedtnXIKwwgiIiIiIiLKOwYRRERERAUSqDtgZwiRJ61TTkGg7oC8PicRERERERFVNgYRRERERAVgqG40738qYJn5fWLLRPP+p8JQXfl9XiIiIiIiIqpYDCKIiIiI8swC0Dx1fudg6ly1Y+qLIMKUVGyeOh9Wfp+ZiIiIiIiIKhSDCCIiIqI8C9VOQ7huWvYHUw+UKCFUNx2h2mmFeX4iIiIiIiKqKAwiiIiIiPJs26RjADPPLZl2ZaawbdLRhV0DERERERERVQQGEURERER5FHONRtQ3CRAL/GuYKCHq2wMx16jCroOIiIiIiIjKnlzoBRARERGVmptuugmyLGPhwoU97ps+fTr++Mc/4vzzz8f69eu73dfQ0NDr+X7wzAb8Y7U/J2vtl5lC24TDMW7Vk/l/biIiIiIiIqoYDCKIiIiIBmnp0qW4/vrrMWLECGzfvr3bfXPmzMGaNWt6hBBpP316DV7epHe7LRhP5WytACAJQKq3ydSi1DkngkEEERERERER5RCDCCIiIqJBeueddxAMBnHiiSfi73//e+Z2TdNw1FFH4e677+7zsf6Ugq0d0V7v++PcCagf5cRxD6yBnrKgiAJeWrAvVm+L4SfPbcR4r4qPfzwNP3hmA374tZE4YJQDG/wJXPnSJry9KQIAOHyCC/88cx+c+vg6/OrIMZg6UsP8x9bh7aYILj20DmfXj0CtU8GX/jh+/9ZXePZzIKm6UKUCl156KQ466CDY7XZs27YNDz30EF588UXIsoyf/OQnOPLII+F2u9He3o5nn30WDz/8cHY/sERERERERFSWGEQQERERDZJpmnjppZd6BBFHH300RFHEq6++OqTzXvVyM/513n749dFjcM3yzbj6qDHw2iQsfGlTt+OuP2Ysrl7ejM+3x/Hjr9fi4fmTceBdK+HvUlnx66PH4LpXN6MxkEAgnsLlh43Cd/evxhXLmrC+PYFZE1y4++RJaIuuQ4tnHH525jcxceJELFy4EMFgEGPHjoXNZgMAzJs3D7NmzcL111+PrVu3YuTIkaitrR3S+0hERERERESVh0EEERER0RAsXboUp59+OmbMmIFPPvkEAHDiiSfi9ddfR0dHR5+P+8t/7YGU1b1P0qx7VmFzyECHYeJHzzbi2bP2RkQ38aODavHtR75AWDe7HX/Ph9vw7OcBAMDPlzXh2D09+N6MEbjj3S2ZY255oxWvNYYBAKok4LLD6jDv0XX4oKVzbRtXtOOQcS4sqK/BdcvHoLa2FuvWrcMXX3wBANiyZee56urqsHnzZqxYsaLHfURERERERES7wyCCiIiIaAg2bdqElStXYu7cufjkk08wZswYzJgxA5dddhkuv/xyHH/88Zlj586dm/n/a1/ZiNc2dm/N9FXYyPz/By0d+NO7W/GLw0fjf9/5Cu829ww13m/ZeVvKAj7+Kop9arRux3zcuvM59vDZ4FQlPHn6Xt2OUSUBK76KIiXb8c9//hPXX3899t57b3zwwQd488038dlnnwEAXnzxRfzud7/D3/72N7z//vt455138MEHHwzmw0VEREREREQVjEEEERER0RAtXboUl1xyCW6//XbMmTMHmzdvxieffIKNGzfiscce6/UxWzoMbAgk+jynAODgcU4kTQt7+GxDXluHsbOKwqWIAIAznvgSrV1CDwDQDQOWKOG9997D6aefjkMPPRRf+9rX8Ic//AFPP/007r77bqxduxZnnnkmDjnkEMycORPXXXcdPvzwQ/zmN78Z8vqIiIiIiIiocoiFXgARERFRqWpoaIBlWTjuuOPwzW9+Ey+8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O++8E6eeeiouvvhitLa2Zp6rP9u3b8f999+PCy64AFdeeSVeeuklLFq0CGJKBwRHt2M/2xrD1cubcekhdbj2qLF4Z1MYv/1XC+46eVKf53/28wBO2qcKz5y5N6o0GRc/35iZ+ZD2vRk1WLMt1iOEAIDnvwhg0fHjcfxkL15cFwTQWWWQrmLY1bp163DppZfi/PPPx69//WsoioKmpib85je/wdtvv50JLHRdx0cffYTGxkb85je/gaIoeP311/H3v/8dpmlCEAT87W9/g91ux3XXXYdYLIbnnnsOHo8HNpsNPl9nJchdd92FM844A/PmzcOGDRvw0EMP4aqrroLb7YbH40EikcDDDz+MH/7wh7jyyiuxfPly/OEPf8CDDz6IcDiMM888E6NHj0YkEsHatWvx0EMP9fv5IuqNobrRvP+puWnH1B/LRPP+p8LpXw9Fz27rMyIioq6E+vp6Xk5HREREVEJGjhyJcDjcecUzEVEfDNWF1cdcX+hl9DC14TrIWdjwXLhwIVwuF6699tpBPS4dZPRWjTGQt9P/erNrm6mub/d2W29vU+WxADTWn4vwyP0yQ6XzykzBs20VJn58P3bfhI2IiGhoWBFBREREVILYmomIdkfRI5ATYSRt7kIvJUNOhLISQgxHetN/OAYbZkiSNKQwY6DhBcOM0haqnYZw3bTCLUCUEKqbjlDtNHi3rizcOoiIqKwxiCAiIiIiIipTnq0r0D72kMJcZb0rMwVPmWxylluY0ddtlB/bJh0DmCYg5rEl067MFLZNOppBBBER5QyDCCIiIqIS0temExFRb2qa3kb7+FmFXkYnUUJN01tZO92iRYuydq5CKESYIYpir62m+lvfcFpNMczYvZhrNKK+SXl9zsMnuPDPM/fBHrd9glAihTOmV+OmY8dhz9slxFyjYI98NaTznnDCCbj44otx8sknZ3nFuTOQNS9YsABHHHEELrjggjyujIio/DCIICIiIipB3NwhooGwR1rh8Dci6p1Q8KutHcGmIW9wUu/yEWbseltvYcZFF10Ep9OJ3/3udwCAn/zkJzj66KMBdA4mj0Qi2LhxI15//XUsX768W2Bx7733oq6uDv/zP/+D1157rcd9kyZNwi233IJly5b1WPu9996LlStX4rbbbutx3/HHH4+f//zn+PDDD3HYYYf1+f5/9dVXOOOMMwAAkyZNwoIFC1BfXw+Hw4EtW7agoaEBDz/8MBKJnYPYH3nkEYwaNQo/+clPsHr16sztF110Efbaay9cfvnlvT5XXV0dHn30UfzgBz/Al19+CQBomzALMFNDqlraGSB8OujH9spMoW3C4Ri36sndHvrII49gyZIlePLJncc2NDTg3Xffzc5admPMmDH43ve+h5kzZ8Ln8yEYDGLTpk144YUX8Oqrrw64RVk+10xEVOkYRBAREREREZWxkY0N2HjguYVdhChhZONrhV0D9SobYUY8HoeiKGhvb4cgCNB1He+//z5uu+02SJIEn8+Hgw46COeffz4OO+ww3HzzzQB2hiDbt2/HnDlz8NFHH2XOuffee6OmpgbxeBwulwvV1dU9Ki+WL1+OM844A/fddx8SiUS3++fMmYN33nkHv//976EoSua8Tz31FG655Ra89957AJDZsN5vv/3whz/8AR9++CGuuuoq+P1+TJkyBT/+8Y8xc+ZMXH755Ugmk5nzJBIJXHjhhbjsssuG9bEL1U4vjtZpQOesiNppwACCiN7oug5d17O8qJ6mTJmC3//+92hsbMT//u//oqmpCQCw77774pRTTsGGDRsyQc/u5GvNREQEFPCSGCIiIiIaLLZmIqLB8mxdCfeWlZ1XXReCmYJny4qymQ9BvbMsC6lUCslkEqZpIpFIoLW1Fc3NzVixYgUWL16Ma665BgcddBAOO+ww+P1+tLe3wzRNLFu2DFOnToUoimhvb4ff78fhhx+OV199FclkErquIx6PwzAMpFKdX8eiKOKNN96AzWbDMcccA5fLBY/HA6/Xi3322QczZszAm2++CbvdDkmSMlUcu641lUrB6XRi4cKF2LRpE2666SasX78ebW1tePPNN3HNNddg6tSpmD9/frf397nnnsN+++2HQw45ZMgfs2kHHYot1x2FIye6sXzBvth0RT1e+N4+2Kvaljlm/1o7nj5jb2y8fAYaL5+B5edMQf0oBw6f4MKd35oEryaj7Zcz0fbLmbjyiNEAgFP3r8byBfti4+UzsOri6fi/kydhhGNg16EmbR7UTtwLN954I5588kksXboUd911F2bOnJk55rbbbsOoUaNw8cUXo6GhAQ0NDQA62xw9++yz3c73X//1X/j73/+Ol156CQ888ACOP/74bvc3NDRg7ty5uOGGG/DCCy/gwQcfxKxZ/beTW7hwIZqbm3HJJZfgnXfewebNm7F582a8+uqr+OlPf5oJIWbMmIGGhgY4nc7MYydPnoyGhgbU1dX1ueYzzjgDTz75JJ5//nn84he/gKqqPdYwd+5c3H///Vi2bBkeeOABfPvb397dh5aIqOIxiCAiIiIqQWzNREQDJQAYt2oJxJQOWANrV5I1lgkxpWPsqiVgjEofffQR1q1bh9mzZ3e73e/34/3338fxxx+PVCoFSZJw5JFHZjaIdV1HNBpFJBJBOBxGKBTKtOJ56623MHv2bGzfvh3btm1DW1sbDjnkEGzfvh2vv/46QqEQIpEIotEo4vE4gJ1VEKIoQpZlTJkyBRMnTsTSpUvhdrvh9Xrh8/lQXV2NYDCIFStW4IQTTkBNTQ2qq6shiiICgQCWLVuGCy+8EG63G06nE7IsQxRF2Gw2qKoKRVF6hCBdJRwjAQBXHzkG1766GcfdvwZJy8If507MHPN/J09Ca1jHcQ+swTfuX4P/fecrGKaF95o7cNUrmxCKp7DfHZ9ivzs+xZ/e3QIAkCUBN7/RiiPvW42zn/oSE7wq7vzWxB7P3xdhxES8++67uOKKK3DBBRfgvffew80334za2loAwK9//Wts3boV9913H+bNm4d58+b1ep4jjjgCF198MZ544gmcd955eO6557Bw4ULU19d3O27BggV47bXXcP755+Pdd9/F1VdfDbfb3es599prL0yaNAmPPfZYTn4XOvroo3HOOefg3nvvxY9+9CO0tbX1CBmOO+44nHvuubj33nuxYMEC3HPPPTj33HNxwgknZH09RETlhEEEERERERFRmVP0MMZ99jgg5PlPQEHEuM8eh6JH8vu8VLSampowatSoHre/8MILOPHEEwEARx11FFpaWgbUXmfp0qWor6/PnNM0TXzzm9/Eiy++CF3XkUgkEI/HEYvFEI1GAQCxWCwTZgQCAXg8HgDAypUrM2FGujIjEAhgw4YNGDVqVCbMSLeAevzxx1FXV4dvfOMbsNlskGUZkiRlKjOqqqpQXV2NmpoajBgxAjU1NQCQCTrgGwsAuOn1Fry9KYLP2+L433e24JBxLtikzuBinEfFvxrDWNuewHp/Av/8PIDPtsZgmBbCiRQsWNjakcTWjiQ6jM6A5eFP27B8fQgbgzo+aIniqleacfxkL5zKAF7/Zgor2i08++yzaGxsxObNm7F48WK0tLRkKhXC4TBM00Q0GoXf74ff7+/1VKeddhqWLVuGZ555Bs3NzXjiiSfwxhtv4LTTTut23IsvvohXX30VLS0tuOeee+BwODBlypRezzl+/HgAwKZNmzK3VVVVYenSpZl/w6lO+M53vpM5z6ZNm3Dfffdh48aN3Y4555xzcNddd+GNN97AV199hTfeeANLlizBSSedNOTnJSKqBJwRQURERFRCuraVICIajKotn8JY8zRap5ySt+ccveZpVG3J0iBdKgt9tRh855138LOf/QwzZszAnDlz8MILL/Q45vLLL+/W2mfu3Ln44IMPsG3bNsyZMweLFy/GzJkzUVtbixdffHHIa9t10HG6HVQsFgPQ+TM4Ho+jubkZjz32GE477TQ8//zzmfZR27Zt6zHQWxRF2O12ADvnEiSFzi2Zz7bGMs+1pcMAAIxwytgcMvDn97fi9jkTceq0avyrMYxn1vjRGOh/psGMOjuunD0G00baUaVJSH/Ix3lUfN4W7/9jAAs2pwc/+tGPcOihh6KmpgaSJEFV1Uw7o4GaMGECnnvuuW63rVy5skcFxfr16zP/H4/HEYlEOoOaAQqFQvjBD34AALj99tshy0Pf6po4cWKPVk2fffYZDjzwQACApmkYO3YsfvGLX+DnP/955hhJkhCJMHAlIuoPgwgiIiIiIqIKMXLjGwDQGUZYZm4qJHacd8zqpzGi6Y3sn59K2oQJE9Da2trjdtM08dJLL+Gcc87Bfvvth2uvvbbHMYsXL8Zjjz3W7TbLsrBs2TJ885vfxP333485c+bg448/7vU5+pK+un7ixIlYt25dr2tubm7u9bGPP/44vv3tb/e4Cn/XMAPoHHANdAYaHR0dMFKdxxjmzosL0tcZiDvSg1vfbMWTn7Xj+L28OG5PDxYeMRoX/HMDnv8i2Ot6HIqIJ07bG69uCOHCZzegLZrEWI+KJ0/fG4o0sAZpV373GBwxuQZ33303Nm/ejEQigeuvv35YG/z96ToEPK2vwCr9eRg/fnzmc2WaJlpaWgDsDI2AnRdtdD3XcN+HdJj0hz/8AatWrep2X2+fcyIi2omtmYiIiIhKCIdVE9Fwjdz4BiZ8/ADEZCL7A6zNFMRkAhM+foAhBPVw4IEHYvLkyXj99dd7vf+FF15AfX093nrrrV6vLg8EAmhpacn86/q42tpazJ49G0cccQSWLl06qHWtW7cOGzduxPz583v8nJ08eTK+9rWvYfny5b0+Nh6P48EHH8T3vvc9OByOQT2vMMCZLV/6E7j7/a2Y/9g6PPdFAGdM72zxpKcsSLusd+9qG2ocMm54bTP+3dyBte0JjHQqg1rXzMmjsWzZMrz55pvYsGED2tvbe7TTMgwDotj/llJTUxOmTZvW7bZp06b1aHU0GGvXrsXGjRtx2mmn7fZ3okAgAACZllhA54yJ/mzcuBH77bdft9umTp2a+X+/349t27Zh9OjR3b4WW1pa8NVXXw3yvSEiqiwMIoiIiIhKEFszEdFwVG35FPu+uQjubas7bxjulbw7Ag3PtlXY981b2I6JoCgKfD4fRowYgb333htnnXUWbrzxRrz99tt46aWXen1MU1MTvv3tb2PRokWDeq6vvvoKH330Ea644goYhtFn0NGf3/3ud5g4cSKuv/56TJkyBbW1tTjqqKNw0003YdWqVXjyySf7fOyzzz6Ljo4OHHvssYN6TjGZ6Pd+TRaw6PhxOHyCC+M8Kg4e68SBox1Yu6O9UlNQh8sm4ciJblTbJdhlAc0hA4mkiQu+VouJXhUn7uXFz2f1nMnRFwsCNn7VhtmzZ2Py5MmYPHkyrrnmmh6b/l999RVmzJiBESNGZGZs7OrRRx/FCSecgP/6r//C2LFj8d3vfhezZ8/uUdUyWIsWLcL48eNxxx13YNasWRg7diwmTpyIk08+GV6vN1OZsHnzZmzZsgXnnHMOxo4di0MPPRSnnnpqv+d+6qmnMGfOHJx44okYN24czjnnHEyaNKnbMffffz/OPPNMzJs3D+PGjcMee+yBE088Ed/97neH9X4REZU7tmYiIiIiIiKqQIoexqSPFyNUOw1tk49DxDO+M1AQpYGfZMfxjmATRja+Bs/WlWDdFgHAIYccgqeeegrJZBLhcBhffvkl7rjjDixbtqzfMD0UCg3p+ZYuXYprr70WTz/9NAzDGPTjP/vsM1x00UVYsGABbrnlFjgcDmzduhXLli3Dww8/3O85U6kU7rvvvl7bSfVHjW7v9/6UCfjsMv78rUkY6ZTRHkviuS8CuOWNzrZT72/uwOL/bMM9394DNQ4Zi95sxa1vtuLi5zfimqPG4IcHjcSnX0Xx64bNeHj+5IEtSpTwv/c+iBt+8B3ceeedCAaDePTRR+F0OrsdtnjxYlxxxRV46KGHoKoqjjnmmB6neuutt3DnnXfi1FNPxcUXX4zW1lYsWrQIn3zyycDW0ofVq1fjwgsvxFlnnYVLL70U1dXViMfj+PLLL/HnP/85UxGTSqVw44034rLLLsO9996LNWvW4N5778X111/f57kbGhowZswYXHjhhVBVFa+//jr++c9/4utf/3rmmKVLlyKRSOC0007DhRdeiHg8jg0bNmDJkiXDer+IiMqdUF9fz8vpiIiIiEqEoiioqqpCe3t7tz7IRETDUVNTg+2CB5trZyJUOw1J244rnM0UBHTpXw8hE1QoegTuLZ+ipult2CMD78dPRJ0M1YXVx/S9KV4oUxuug6xz8DIREWUXKyKIiIiIShBbMxFRtthsNoiiCKltI8Zt3wBxzT/grhuPryw3wvZapGQ7LFGCYKYgJWPQwi3wJbbBZxPQ1tbG70dEQ6ToEciJMJI2d6GXkiEnQgwhiIgoJxhEEBERERERVTBN06DreqavuiRJUJNRuNqbYU+t7PUxlihC0GqgKAp0Xc/nconKimfrCrSPPWRwLdFyxUzBs7X31zwREdFwcVg1ERERUQlJD4vkFchElA2SJEFVVcTj8cxtsizDsqx+27+ZpolkMglVVfOxTKKyVdP0dnGEEAAgSqhpeqvQqyAiojLFIIKIiIiIiKhCaZoG0zSRSCQyt0mSNKAZNLquM4ggGiZ7pBUOfyOwoyKpYMwUHP4NsEe+Kuw6iIiobDGIICIiIioh6YoIIqJssNls3UIIoLMiIplM7vaxuq5DkiTIMjv+Eg3HyMYGQCzw9owoYWTja4VdAxERlTUGEUREREQliK2ZiGi4VFWFJEnd2jIBA6+IMAwDpmmyKoJomDxbV8LXthqwClQVYabg2bKC8yGIiCinGEQQERERERFVIE3TYBhGt+oHURQhiuKAKiKAzjCCQQTR0ImiCF9VFaY2vwwppec/jLBMiCkdY1ctAWsuiYgolxhEEBEREZUQtmYiomwQRbHHkGqgsxoCwIAqIoDO9kyyLPN7E9EQ2Gw2+Hw+CIKA2PbNGLvyMUDI8zaNIGLcZ49D0SP5fV4iIqo4DCKIiIiISgzbMhHRcNlsNgDodT6EZVmDCiIEQWBVBNEgCIIAt9sNj8cDXdcRCASQTCZRteVTjF7zdF7XMnrN06ja8mlen5OIiCoTgwgiIiIiIqIKo2kaEolEj2BzoPMh0kzTRDKZZBBBNECyLMPn80FVVYRCIYTD4W6vw3FfvYe9N73S+Uau2jTtOO+Y1U9j5MY3cvMcREREu5ALvQAiIiIiGji2PyGi4VIUBbIsIxLp2YpFluUBz4dI03UdmqZla3lEZcvhcMDhcCCZTCIUCsE0uwcNiqLA7XZDaXoDibbNaN7/VJiSCohS9hZhpiCmdIz77HFWQhARUV4xiCAiIiIqMWzNRETDoWkaUqkUDMPocZ8kSdB1fVDn03UdDodjSCEGUSUQRbEzYFAURKNRRKPRHsdIkgSPxwPDMBCJRFAV+RRO/wY0T52PcN00wEwNL5DY8XjPtlUYu2oJZ0IQEVHeMYggIiIiIiKqEIIgwGaz9boRKooiRFEcdJhgGAZM04SqqgwiiHahqircbjcsy0IwGOw1ABRFEV6vF6lUCqFQKHO7oocx6ePFCNVOw7ZJxyDqmzToQEKwUrAECY5gE0Y2vgbP1pVgbSURERUCgwgiIiKiEiIIAisiiGjI0kOq4/F4j/skqXNzczAzItIMw4Cqqr0GHESVyuVywW63Ix6PIxKJ9PrzWxAEeL1eAEAoFOpxjADAu3UlvFtXIuYajbYJsxCqnYakzdN5gJmCgJ2PsSBkggo5EUJtaB1qmt6CsL0xJ+8jERHRQDGIICIiIiIiqhCapkHX9R696YHO+RCWZQ0piNB1HS6Xi2EpETpfS263G5IkIRwO9xr8pXk8HoiiiEAg0Ovrsit7pBXjVj0JrHoSSdWFqGcc4u4xSMl2WKIEwUxBSsaghVvgCDVD1iM7z5/l95GIiGiwGEQQERERlRAOqyaioZIkCYqiIBgM9nn/UEIIoDOIEAQBqqoikUgMZ5lEJc1ut8PpdCKZTMLv9/f7mnK5XJnX5GBfe7IegWf7Gni2r+n3uHg8Dq/XO6zXNxERUTaIhV4AEREREQ0OrzYmoqHQNA2mafY5jHo4w6ZN08y0ZyKqROkWSy6XC7FYDIFAoN+Nf4fDAbvdjnA43OvciGxJV0Cl27IREREVCoMIIiIiIiKiCqBpWr8tYoZ7xbSu6wwiqCKpqorq6mrIsoxAIICOjo5+j7fZbHA6nejo6MhLBVEikYCmaTl/HiIiov4wiCAiIiIqIWzNRERDYbPZIIpin0GEKIoQRXHIFRFAZxAhiiJkmR2AqXI4nU54vV4YhoH29vbdVjcoigK3241YLJa34e6JRAKSJPG1SUREBcWfQkREREQlhq2ZiGiwNE2DYRh9VjxIkgQAw6qISCaTME0TqqoOK9AgKgWSJMHj8QxoIPWujzEMA5FIJA+r7JR+7dtsNr42iYioYFgRQUREREREVMZEUYSiKP1ulMqyDMuyhj3Mlu2ZqBJomgafzwcA8Pv9AwohRFGE1+uFaZoIhUK5XmIPbM9ERESFxiCCiIiIqISwNRMRDZamabAsK6fzIdJ0XYeiKPxeRWVJEAR4PB643W7E43H4/f4Bv248Hg8AIBgMFqSyMZFIZEJJIiKiQmBrJiIiIqISw9ZMRDQYmqbtdiCuLMtZadmi6zqAzuG9+RjCS5Qv6dkOgiAgGAxmvtYHIt3CKRAIwDTNHK6yb8lkEslkMtOmjYiIKN9YEUFERERUQniVMRENhqIokCRpt61jslURYVkWDMNgeyYqK+mB1KlUCn6/f1AhhMvlgqqqCIVCWXmNDUcikeBrk4iICoZBBBEREVGJYUUEEQ2UpmmZK6H7IooiRFHM2hBbzomgciGKIqqqqmC329HR0YFgMDioiga73Q673Y5wOFwUVQjp9kx8fRIRUSEwiCAiIiIiIipDgiDAZrMNqBoCQNau1tZ1HaIoQpbZCZhKl81mg8/ngyiKCAQCiMVig368y+VCR0dH0bQpS6VSMAyDQ6uJiKgg+JshERERUQkRBIEVEUQ0IOnNxt0FEbIsw7KsrAURyWQSpmlCVdWsVVkQ5YsgCHC5XNA0DfF4HJFIZNA/d9PzJOLxOKLRaI5WOjSJRAJOp5O/TxARUd6xIoKIiIioxHDjgIgGQtM06Lq+2+8Z2ZoP0RXbM1EpkmUZPp8vM9MhHA4P+meuJEnweDwwDAPhcDhHKx26dHWGzWYr8EqIiKjSMIggIiIiIiIqM7IsQ5bl3VZDpI/NduWCrutQFAWiyD85qTQ4HA5UVVXBNE34/f4htVMSBAFerxemaSIUCuVglcNnmiYMw2AQQUREecffComIiIhKiCAIhV4CEZUATdOQSqWg6/puj81VRYRlWVAUJavnJco2URTh9XrhcDgQjUYRCAQGNZC6K6/XCwAIBoNFXb2YSCQYFBIRUd7xpw4RERFRiSnmzQ0iKg4DGVINdG7CiqKY9YoIy7KQTCZ51TUVNVVV4fP5IEkSgsHgsOY5eDweSJKEUCg05CAjX9ieiYiICoFBBBERERERURmx2WwQRXFAQYQkSQCQ9YoIYGd7JqJi5HK54PV6YRgG/H4/DMMY1rnScyVKYUC7ZVnQdZ1BBBER5RWDCCIiIqISIggCKyKIqF/pIdUDuSpblmVYlpWzIEIURYYRVFTSA6k1TUM4HEYoFBrWz1W73Q673Y5IJDKsMCPf4vE4FEXJhJFERES5xiCCiIiIiIioTEiSBFVVB1QNkT4+FyEEACSTSZimCVVVc3J+osGy2+2oqqqCZVnw+/0Dfp30RVVVuFwudHR0DPtc+ZYOK1kVQURE+cIggoiIiKiEcFg1EfVH0zSYppnpAb87sizntJWMrusMIqjgBEGA1+uFy+VCLBZDIBAYdgAnyzI8Hg/i8fiwZksUUiKRYBBBRER5wyCCiIiIqMSwNRMR9UXTtAGHEEBuKyKAziBClmWIIv/0pMJQVRXV1dWQZRmBQAAdHR3DPqckSZn5EuFwOAurLIxEIgFZliHLcqGXQkREFYC/DRIREREREZUBVVUhiiJisdiAjhdFEaIo5rwiwrIsVkVQQTidzkxg0N7enpUZDunqCtM0EQqFsrDKwjEMA6lUilURRESUFwwiiIiIiEoIWzMRUV80TctsLA5EekhtLisiLMtCMplkEEF5JUkSfD5fZoj0cAdSd+X1eiEIAoLBYFlUKLI9ExER5QuDCCIiIqISUw4bH0SUXaIoDmpINdDZ496yrJwGEUBnVYSiKDl9DqI0TdPg8/kAAH6/f8AVQgPh8XggyzKCwSBM08zaeQspkUhAkiS+RomIKOcYRBAREREREZW49BXNxTQfIk3XdYiiyI1OyilBEODxeOB2uxGPx+H3+7P69e10OqGqKkKhUE7bmeVbMplEMplkVQQREeUcgwgiIiKiEiIIAisiiKiH9JDqwXx/kGU5LxuqyWQSqVSK7ZkoZxRFgc/ng6IoCAaDiEQiWT2/3W6Hw+FAJBKBrutZPXcxYHsmIiLKBwYRREREREREJUxRFMiyPKi2TED+KiKAzqoIBhGUCw6HA16vF6lUCn6/P+tBgaqqcDqdiEajg36NlYpEIpFp70ZERJQrcqEXQEREREQDx2HVRLQrTdOQSqVgGMaAHyOKIkRRzFuLGV3XYbfbIYpi2fTWp8ISRTEzs6GjoyOrsyDSZFmGx+NBIpFAR0dH1s9fLNLfPzRNK8uKDyIiKg6siCAiIiIqMWzNRERpgiDAZrMNqRoCQN4qIgzDgGVZvOKassJms8Hn80EURQQCgZyEEKIowuv1wjAMhMPhrJ+/2CQSCaiqygseiIgoZxhEEBERERERlah0X/fBBhGyLMOyrLwFEZZlwTAMBhE0LIIgwO12w+PxQNd1+P3+nFT1CIIAr9cL0zQRCoWyfv5ilB50z1kRRESUKwwiiIiIiEoEr1Ikol2lW6kMtt1RPudDpHFOBA2HLMvw+XxQVRWhUAjhcDhnFYJerxeiKCIYDFZMFaJpmjAMg0EEERHlDIMIIiIiohJTKZsiRNQ/SZKgKMqQBujKspy3+RBpuq5DEAQoipLX56XS53A4UFVVBdM04ff7M1fv50J67kQwGKy4eSaJRAKKokAUuVVERETZx58uREREREREJUjTNJimOaThsoWoiEilUkilUqyKoAFLz2lwOByIRqMIBAI5DQecTmem4iLfQV0xYHsmIiLKJQYRRERERCWCrZmIqCtN04ZUDSGKIkRRLMhGK9sz0UCpqgqfzwdJkhAMBhGNRnP6fJqmweFwIBKJDCncKweWZUHXdQYRRESUEwwiiIiIiEoMWzMRkc1mgyiKQwoiJEkCgLxXRACdQYQsy2z9Qv1yuVzwer0wDAN+vx+GYeT0+VRVhcvlQjQaHdJrqpzE43EoipL5PkFERJQt/O2PiIiIiIioxGiaBsMwhhQmyLIMy7IKEkQYhgHLslgVQb2SJAk+nw+apiEcDiMUCuU8fJdlGR6PB7quo6OjI6fPVQp0XYdpmqyKICKirGMQQURERFQi0q2ZWBFBVNlEUYSqqojFYkN6fCHmQ6RZlgXDMBhEUA92ux0+nw+WZcHv9+elMiE9gyKZTCIUCuX8+UoF2zMREVEuMIggIiIiIiIqIekh1enBsoMly3JBB/FyTgR1JQgCvF4vXC4XYrEYAoFAXoKy9POapolgMJjz5ysl8XgcsixDluVCL4WIiMoIgwgiIiKiEsFh1UQEdAYRQw0hgMJWRACdQYQgCFAUpWBroOKgKAqqq6shyzICgUBeWyN5vV6IoohgMMhKw10YhsH2TERElHUMIoiIiIhKDDdMiCpXeojsUNvWiKIIURQLWhGRSqWQSqVYFVHhnE4nqqqqYBgG2tvbcz6Quiu32w1ZlhEMBmGaZt6et5TE43EGEURElFUMIoiIiIiIiEqEpmlIJpNDDhIkSQKAglZEAOxBX8nSA6ntdjsikUheBlJ35XQ6YbPZEAqFChrIFbtEIgFJkli5REREWcMggoiIiKhEsDUTUWUTBAE2m21YQ3xlWYZlWUURREiSlAlGqDJomgafzwcA8Pv9Qx64Ppzndzgc6OjogK7reX3uUpNMJpFKpRgYEhFR1jCIICIiIioxbM1EVJk0TQOAYQURhZ4PkabrOizLYnumCiEIAjweD9xuN+LxOPx+f96/DlVVhcvlQjQazXsAUqrYnomIiLKJQQQREREREVEJ0DQts4E/VLIsF007GsMwGERUAEVR4PP5oCgKgsEgIpFI3tcgyzI8Hg90Xc/rQOxSl0gkIIoiX6dERJQVDCKIiIiISkS6NRMrIogqjyzLkGV52FdyF0tFBNBZFcH+8+XN4XDA6/UilUrB7/cXpB2SKIrwer1IJpMIhUJ5f/5SlkqlYBhGphqLiIhoOBhEEBERERERFTlN0zKbgkMliiJEUSyaighd1yEIAq+2LkOiKKKqqiozjyEYDMI0zbyvQxAEeL1eWJaFYDCY9+cvB4lEAqqqck4VERENG4MIIiIiohLBTQCiyjXcIdUAMoOhi6UiIpVKIZVKMYgoMzabDT6fD6IoIhAIFHQeg8fjgSiKCAaDrCYcokQiAQB8nRIR0bDJhV4AEREREQ0ON1OIKovNZoMgCMMOImRZhmVZRRNEAJ1VEdzgLA+CIMDlckHTNMTjcUQikYL+vHK73VAUBYFAoKi+5kuNaZqZ9kzpUIKIiGgoWBFBRERERERUxOx2OwzDGHZrG0mSiqYtU5qu65AkKVOtQaVJlmX4fD6oqopQKIRwOFzQEMLpdMJmsyEUChXd13wpSiQSUBQFosgtJCIiGjr+FCEiIiIqEWzNRFR5JEmCoijDroYAOjeLi+3KcF3XYVkWqyJKmMPhQFVVFUzThN/vL/hV85qmZWZTFGI4djlKf05tNluBV0JERKWMQQQRERFRCWFbJqLKomkaTNPMyuauJElFF0QAgGEYDCJKkCiK8Hq9cDgciEajCAQCBRlI3ZWqqnC5XIjFYgWdTVFuLMuCrusMIoiIaFgYRBARERERERWpdL/94RJFEaIoFmWbmnTbF1Z9lQ5VVeHz+SBJEoLBIKLRaKGXBFmW4fF4oOs6IpFIoZdTduLxOBRFYRs1IiIaMgYRRERERCWCm3RElUVVVYiimJUgIr15WIwVEbquQxAEKIpS6KXQALhcLni9XhiGAb/fD8MwCr0kiKIIj8eDZDKJUChU6OWUJV3XYZomqyKIiGjIGEQQERERlRC2ZiKqHJqmwTCMrIQHsizDsqyiDCJM00QymWR7piInSRJ8Ph80TUM4HEYoFCqKn0mCIMDr9QIAgsFggVdT3tieiYiIhoNBBBEREVGJYEUEUeUQRRGqqmalGgLo3EQuxrZMabquM4goYna7HT6fD5Zlwe/3Z+3rMhs8Hg9EUUQwGCyKYKScxeNxyLIMWZYLvRQiIipBDCKIiIiISgg3WYgqQ/qq42wMqQY6KyKKsRoiTdd1SJLE/vNFJl1tkB4AHQgEiurryO12Q1EUhEKholpXuTIMg+2ZiIhoyBhEEBERERERFRm73Y5EIpG18FGSpKLeqDUMA5ZlsSqiiCiKgurqasiyjEAggI6OjkIvqRuHw5FpE1UMcyoqRTweZxBBRERDwiCCiIiIqESwNRNRZVAUBZIkZa39jSiKEEWxqFszAWzPVEycTieqqqpgGAba29uLbqNf0zQ4nU5EIpGsVQ3RwCQSCUiSxOHyREQ0aGzsR0RERFRC2JqJqPxpmoZkMpm1zd90u6NirogAOoMIl8sFQRD4va5AJEmCx+OBJEmIRCKIxWKFXlIPiqJkWkUV4/rKXTKZRCqVgs1mK7qAioiIihsrIoiIiIiIiIqEIAiw2WxZHQYsyzIsyyqJIEIQBF5pXSCapsHn8wEA/H5/UW7yp4MSXdcRiUQKvZyKxfZMREQ0FAwiiIiIiEoEWzMRlb9sD6kGOjdvi70tEwCYpolkMsn2THkmCAI8Hg/cbjfi8Tj8fn9RhlaiKMLr9SKVSiEUChV6ORUtkUhAFEW+VomIaFDYmomIiIiohLBdCVF50zQNuq7DNM2snVOW5aLcWO6Nruu80jqPFEWB2+2GIAgIBoPQdb3QS+qVIAjwer0AgGAwWODVUCqVgmEYme9XREREA8GKCCIiIiIioiKQHgCbzbZM6fOWUhAhSVJmrgXljsPhyFQY+P3+ot5Q9ng8EEURwWCQgXyRSCQSUFWV1ZpERDRgDCKIiIiISgQHuBKVN03TYJpmVjeERVGEKIol0ZoJAAzDgGmabPmSQ6IooqqqCg6HA9FoFMFgMKsVONnmdruhKApCoVDJBGqVIN0+jq9VIiIaKAYRRERERERERUDTtJxUQwAoqQ1cwzDYnilHbDYbfD4fRFFEIBBANBot9JL65XA4oGkawuEwDMMo9HKoC9M0M+2ZiIiIBoIzIohKlKG6EPOMQ9w9BinFDkuQIFgpSEYMWrgF9lAzFD1S6GUSEVEWsf0BUfmy2WwQRTHrQYQsy7Asq6SCCF3X4XK5WAWWRYIgwOVyZcKuSCRS9B9bm80Gp9OJSCSS1eHtlD2JRIKvVSIiGjAGEUQlJOYajbYJsxCqnY6kzd15o5mCgJ2/9FkQALHzyjc5EYZn6wrUNL0Ne6S1EEsmIqIs4x/6ROVJ0zQYhpH1wECSpJJpy5Sm6zoEQYCqqtyAzgJZluHxeCAIAkKhUEl8TNNDtGOxGGKxWKGXQ31IBxGapvHzREREu8UggqjIWQBCtdOwbY9jEK2aBJipTNAAABAl9LUllbS50T72ELSPnwWHvxEjGxvg2boSvJ6WiIiIqHiIoghVVREKhbJ+blmWS6oaAuhs+ZJMJhlEZIHD4YDD4UAymUQoFCrqWRBpkiTB4/HAMAxEIqzwLmaWZUHXddhsNgYRRES0WwwiiIqYobrRPHU+wnXTgPQfDV1DiIHYcXzUOx4bDzwX7i0rMW7VEih6OMurJSKiXGPrA6LylB5SnYtNd0mSsjr8Ol90XWfv+WEQRTEz5DkajRb9LIg0URTh9XqRSqVyEsxR9iUSCXg8HoiiWBJBFxERFQ6HVRMVqUDdAfj8iIUIj9yv8wZxmC/XHYFEeOR++PyIhQjUHTDMFRIRUSEwiCAqP5qm5SSEEEURoiiWXGsmoDOIEEURssxr5wZLVVX4fD5IkoRgMFgyIYQgCPB6vQCAUCjEn3clIpFIwDRNBodERLRbDCKIitC2iUeiqX4BTNk2+AqI3RElmLINTfULsG3i7Oyem4iIiIgGRVEUSJKU9SHVQGc1BICSDCIMw4BpmlBVtdBLKSkulwterxeGYcDv98MwjEIvacDSV9UHg0FeWV9i0u2ZiIiI+sMggqjIbJt4JFqnfLvzDSFHL9Ed522dcgrDCCKiEsLWTETlx263I5lM5iQskGUZlmWV7KauYRgMIgZIkiT4fD5omoZwOFxyFQUulwuKoiAUCpXcTBMC4vE4ZFlmBRMREfWLQQRREQnUHbAzhMiT1imnsE0TERERUQEIggBVVXNSDQF0bk6XYjVEmq7rkGUZgiAUeilFzW63w+fzAQD8fn/Ovp5yxeFwwG63IxwOl1QFB+2UrmBiVQQREfWHQQRRkTBUN5r3PxWw8nzFmmWief9TYaiu/D4vERERUYVL91TP1caxLMslfXW5ruuZsIZ6EgQBHo8HLpcLsVgMfr+/5D7fNpsNTqcTHR0dOZmTQvkTj8cZRBARUb8YRBAVAQtA89T5MCU1d+2Y+iKIMCUVm6fOR+kUbxMRVSa2ZiIqL+kh1bl6XUuSVHIb012Zpsn2TH1QFAXV1dVQFAXBYBAdHR2FXtKgKYoCt9uNWCxWMgO1qW+JRAKSJEFRlEIvhYiIihSDCKIiEKqdhnDdtOwPph4oUUKobjpCtdMK8/xEREREFSbdTz1X1RCiKEIUxZJuzQRwTkRvnE4nqqqqkEwm0d7eDl3XC72kQZMkCR6PB4ZhIBKJFHo5lAXJZBKpVIpVEURE1CcGEURFYNukY4BCDxE0U9g26ejCroGIiPrFPulE5UPTNKRSqZz1xJekzgtcSj2ISCQSEEWRQ3DR+TmtqqqC3W5HJBJBMBgsySo5URTh9XphmiZCoVChl0NZxPZMRETUHwYRRAUWc41G1DcJEAv8chQlRH17IOYaVdh1EBFRv0px04mIerLZbDkdKizLMizLglnoi12GKZlMwjTNiq+K0DQNPp8PgiAgEAggFosVeklD5vF4AKBkgxTqWzo4rPTXKxER9Y6XlVBFWrhwIVwuF6699tput8+YMQO33347TjrppLz1WW2bMAswUzlvy3TIOCeuO3os9q7RYJdFNId03P/xdtz9/tbMMXfOnYAzfvkIgM4/+kKhENavX49XX30VL7744m7/UDjrrLNw6KGHYq+99kIymcTJJ588oLWde+65+Na3vgWXy4WVK1fitttuw+bNm4f+zhIREREVMZvNBkEQchpESJJU8tUQabquQ1XVipwjIAgC3G43bDYbYrFYybcx8ng8kCQJgUCg5EMy6ild5WWz2UqyZRgREeUWgwiiAgvVTs/LbIioYeKeD7fhs20xRA0Th45z4Q8njEdUT+Fvn7R1HiQIWL62DX9aeAEkSYLP58PBBx+Miy++GEceeSSuvvrqfv9gUBQF//rXv7Bq1SrMnTt3QOs6/fTTMW/ePNxyyy1obW3Feeedh1tvvRXnnHNOzloVEBGVKrZmIioPdrsdhmHkdCNWluWSHlTdla7rcLvdEAShoq6gTw9zFgQBwWCw5Dd2XS4XVFVFMBgsm69N6imRSMDpdCISiVTU65WIiHaPrZmI+rBgwQL89a9/7Xbbd77zHTzyyCOZtxcuXIjf/va3OPXUU7FkyRI8/fTTuPTSSzM9ec8++2zcd999Pc7917/+Feeeey4M1YUnz52Jm44d1+3+B+ftiTu/NTHz9qn7V2P5gn2x8fIZWHXxdPzfyZMwwrEzRzx8ggttv5yJIye6sXzBvth0RT1e+N4+2Kt6Z3/OFVtieGq1H59vj2NTUMcTn7WjYUMYh413dXvuhCVhW4eB7du3Y+3atXjooYdwzTXX4NBDD8WJJ57Y78fs/vvvx5IlS7B+/fp+j+tq/vz5ePDBB/HWW29h/fr1+J//+R+MGDECRxxxxIDPQURUSfhHPVFpkyQJiqLktBoi/Tzlstmr6zoEQaiodi8OhwNerxepVAp+v7/kQwi73Q673Y5wOMyLjcpcIpEAgIp6vRIR0cAwiCAapvr6eowZMwaXX345brnlFpxwwgmZDfulS5diwoQJ2HfffTPH77XXXthzzz3x4osvIuYZ19dpu5ElATe/0Yoj71uNs5/6EhO8aregIu3qI8fg2lc347j71yBpWfjj3J7HpE2vs+PrY514q6lneXd0l3V99NFHWLduHWbPnj2g9Q7U6NGjUVNTgw8//DBzW0dHB1avXo39998/q89FREREVAw0TYNpmpnNulwQRRGiKJZNaybLsmAYRkVsbIqiiKqqKjgcDkSjUQSDwZJvYWSz2eByudDR0ZHTr3sqDqZpwjAMaJpW6KUQEVGRYWsmqliHHXYYli5d2u02cQgDoyORCP74xz/CNE1s2rQJ7777LmbOnInnn38e27dvx/vvv485c+bg888/BwDMmTMHn3zyCVpbWxHfYz9gAFe2PvxpW+b/NwZ1XPVKM5afMwVORUSHsfMPk5teb8HbmzqDhf99ZwseO3Uv2CQBidTO51jxk2mocciQRQGL3mzF37ucGwBgWYi7x8CzfU23m5uamrDnnnsO+uPTn+rqagCA3+/vdrvf78/cR0REO1VaWxKicqRpWl6qIQCUTRABdFZF2O32Qi8jp9Ib9pZlIRAIlMXnL91eKh6PV+SMj0qVSCTgcrn4ewsREXXDIIIq1kcffYTbbrut221Tp07F1VdfPajzNDY2drtKqa2tDXvssUfm7eeffx5XXnkl/vSnP8GyLBx77LH405/+BABIKXYMpNv3jDo7rpw9BtNG2lGlSUi3CB/nUfF5284/ZD/bGsv8/5aOzpLnEU4Zm0M7y5+/9dAXcKoiDhrjxK+PHosN/gSeWt09CEjJPf/I69qX/PLLL8fxxx+feXug8yCIiIiIKpmqqhBFMedBhCzLsCyr5K+k70rXdTidTsiyXBYb9F0JggCXy5UJqcqlt74kSfB4PDAMA+FwuNDLoTxKBxGapiEWi+3+AUREVBEYRFDFisfjaGlp6XbbyJEjM/9vWVaPoaCy3PMls+sfQpZldausePvtt2EYBmbPno1kMglJkvCvf/2r81hBgmkBu84elcWdNzgUEU+ctjde3RDChc9uQFs0ibEeFU+evjcUqfsDDXPnHyzpv13EXU7eFOzsL7t6Wxy1TgULjxjdI4iwehmePWHCBLS2tgIAFi9ejMcee6zHMYPV3t4OAPD5fJn/T7+9bt26YZ+fiKjccFg1UWnTNA2GYeR8doMkSWW3WZ9MJmGaJlRVLav3TZZleDweCIKAUChUNq2LBEGA1+uFaZoIhUKFXg7lmWVZ0HUdNpuNQQQREWUwiCDqQyAQgM/n63bbXnvtNejzmKaJZcuW4cQTT0QymURDQ0Nm2JxgpdAWNVDnVDLHiwKw30gNb+6Y3bB3tQ01Dhk3vLYZLeHOyob60c6hvlvdCAKgyj03tQSz+x/HBx54ICZPnowlS5YA6PzYBAKBYT9/a2sr2traMHPmTHz55ZcAOgfz7bfffnjmmWeGfX4ionJUDlfJElUiURShqioikZ7zubJNluWyGVTdla7rUFW1bFr82O12OJ1OJJNJhEKhsqpg8Xq9AIBgMMifWxUqkUjA4/FAFMWy+tomIqKh47Bqoj58/PHHqKqqwumnn44xY8bglFNOwcEHHzykcz3//POYOXMmDj74YLzwwguZ2yUjhtc3hnH8ZA+On+zB3tU2/P6ECfDadmaEzSEDiaSJC75Wi4leFSfu5cXPZ40a9BrOnzkCJ+zlxZ4+G/b02XDWATW4+OA6PLGyvdtxqiygziFixIgR2HvvvXHWWWfhxhtvxNtvv42XXnqp3+eora3F5MmTUVdXB1EUMXnyZEyePLnboLIHHngARxxxRObtJUuW4Pvf/z5mzZqFPfbYA1dddRW2b9+ON998c9DvIxEREVGxSv8+lI8r3suxIgLoDCIURSn56jBRFOH1euF0OhGNRhEIBMpqo9bj8UCSpLIYtE1Dl0gkYJomh1YTEVEGKyKI+tDU1ITbb78dZ511Fs4++2y8/vrrePzxx3HSSScN+lybN2/GypUr4fF4sHr16sztWrgFD61ox7Q6J/580iQkTQt3v78Vbzbt7KHaFkvi4uc34pqjxuCHB43Ep19F8euGzXh4/uRBrUEUBFx71BhM8KpImcCGQALXv7YZ93+0vdtxx02uwnG3/wzJZBLhcBhffvkl7rjjDixbtmy3VzOde+65OPHEEzNv33PPPQCAyy67DJ988gmAzhZPTufOio5HH30UdrsdV1xxBVwuF1asWIGFCxfCMAwQEdFOpb7xRlTpNE1DIpHI+dXhoihCFMWyrYiwLAuqqpZsCyNVVeF2u2FZFoLBYNn9zutyuaCqKoLBYFl+DdLgpNszlUsVExERDY9QX1/POkmiPPj73/+OZ555Bk888UTmNkN1YfUx1xdwVb2b2nAdZD33bQOIiGjgBEHAiBEjEAwGMy3+iKg0KIqCqqoqBAKBnG88p5+rra2tLK9Gr6qqQiqVKsnhxy6XC3a7HYlEAuFwuOxaFtntdrhcLoTD4ZwPZKfSkP5+5Pf7y7JKi4iIBocVEUQ55vV68Y1vfAPV1dXd2jIBgKJHICfCSNrcBVpdT3IixBCCiIiIKIs0TUMymczL1e+yLMOyrLIMIYDOK6ztdnuhlzEokiRl2hWV6ya9zWaDy+VCR0dHWb5/NDSGYcA0TdhsNgYRRETEIIIo155++mkEAgH84Q9/6HU4oWfrCrSPPQQQpQKsbhdmCp6tKwu9CiIi6gVbMxGVJkEQYLPZ0NHRkZfnK9f5EGm6rsPpdEJRlJJoa5QeSJ1KpeD3+8uyXZGiKHC73YjH42zBQz0kEom8fg8kIqLixSCCKMeOOeaYfu+vaXob7eNn5Wk1uyFKqGl6q9CrICKifpRbKw+icmez2QDkZ0g10FkRUY6b3WnJZBKmaUJV1aIOIgRBgNvtzvTHL9dN2HS1h2EYJdkui3IvHo/DbreXTHhIRES5IxZ6AUSVzh5phcPfCBS6fN5MweHfAHvkq8Kug4iIiKiMaJoGXdfz1iqp3CsigM6qCFVVC72MPimKgurqaiiKgmAwWLYhhCAI8Hq9ME0ToVCo0MuhIpVMJpFKpTKhLBERVS4GEURFYGRjAyAW+OUoShjZ+Fph10BERH1Kt2ZiRQRR6ZBlGYqi5K1nviiKEEWxrCsigM4gQpZliIX+/bkXTqcTVVVVSCaTaG9vh67rhV5Szni9XgBAMBjkzybqVzweZxBBRERszURUDDxbV8K9ZSXCI/crzKwIMwXPtlWcD0FERDlhqC7EPOMQd49BSrHDEiQIVgqSEYMWboE91AxF7zlHiajUaZqGVCqVt81oSer8PbISKiIsy4KqqkUzGFmSJLjdbsiyjEgkglgsVugl5ZTH44EsywgEAmU7GJ2yJ5FIwOl0QlXVsg7niIiofwwiiIqAAGDcqiX4/IiFMAUbIOTx6i7LhJjSMXbVEnAMKhFR8Sq1YdUx12i0TZiFUO10JG3uzhvNFATsvGrWgpAJ4OVEGJ6tK1DT9DbskdZCLJko62w2W143ymVZhmVZZb8xbFkWkslk0QQRmqbB5XIhlUohEAiUfRDkcrmgqipCoVDZv6+UHalUCslkEjabjUEEEVEFYxBBVCQUPYxxnz2OpvoF+X1iQcS4zx7nlahERCWimNtfWABCtdOwbY9jEK2aBJip7pV+ooS+Vp+0udE+9hC0j58Fh78RIxsb4Nm6kiE5lSybzQZRFPO6UV4J8yHSdF2H3W4v6Bq6DqSOxWKIRMr/92m73Q673Y5wOMwNZRqUeDwOp9OJSCRS1L/LEBFR7hRfU02iCla15VOMXvN0Xp9z9JqnUbXl07w+JxERlR9DdaOx/lxsPPBcRD0TOm8cbLvBHcdHveOx8cBz0Vh/LgzVneWVEuVHekh1Puc1yLJc9vMh0nRdhyiKUBSlIM+vKAp8Pl9mIHUlhBCqqsLpdCIajRZFJQqVlkQiAUEQinrQPBER5RaDCKIiM3LjGzvDCCtHZfU7zjtm9dMYufGN3DwHERFlVTG3ZgrUHYDPj1jYOesIAIY7QHZHIBEeuR8+P2IhAnUHDHOFRPklimJB2gZVUkVEMplEKpUqyKamw+GA1+tFKpWC3++viMoAWZbh8XiQSCTQ0dFR6OVQCTJNE7quQ9O0Qi+FiIgKhEEEUREaufENTPj4AYjJRGdbiywSLBNiMoEJHz+AEU0MIYiISk2xtTPYNvFINNUvgCnbBl8BsTuiBFO2oal+AbZNnJ3dcxPlkKZpME0TiUQib88piiJEUayYiggAMAwjr0GEKIqoqqqCw+FANBpFMBgs+3kcQGfA5fV6YRgGwuFwoZdDJSyRSEBRlKK+uIKIiHKHMyKIilTVlk/h9G9A89T5CNdN69lne7B2PL46sBZ7r38e0e2bs7dYIiKqSNsmHonWKd/ufEPI0fUtO87bOuUUAGAlH5UETdPyGkIAnZvFACqmIgLo3NTUNA2iKOY8ELDZbHC5XLAsqyIGUqcJggCv1wvTNBEKhQq9HCpxiUQCLpcLNpuN7b2IiCoQgwiiIqboYUz6eHHn4M9JxyDqmzT4QGLH8Y5gE0Y2vobq9jVwVFXBstsRi8VytnYiIsqu9NWDxVIREag7YGcIkSetU06BEg9ythEVNVVVIUlS3jfZZFmGZVkVcYV+mmEYsCwrp22wBEGAy+WCpmmIx+MVN2jX6/VCEAQEAoGKer8pNyzLyrRnYhBBRFR5GEQQFTkBgHfrSni3rkTMNRptE2YhVDsNSZun8wAzBQE7/yiwIGSCCjkRgmfrStQ0vQ17pBUAkAIQi8XgdDrzPkCRiIjKg6G60bz/qZ0zh3JVCdEby0Tz/qfC6V8PRS//wbBUmjRNQzKZzPsV85U0HyLNsqxMe6ZcbGqm5yIIgoBQKJT3KpdC83g8kGUZgUCgogIuyq1EIgGPx5OXSiYiIiouDCKISog90opxq54EVj2JpOpC1DMOcfcYpGQ7LFGCYKYgJWPQwi1whJoh97FJ09HRAVVV4Xa7EQgE8vtOEBHRkBRLP2ULQPPU+TAlNb8hBAAIIkxJxeap8zHx4/tRHB8Rop0EQYCqqgUZ5ivLckVeYKLrOpxOZ9bPa7fb4XQ6kUwmEQqFKm7D1Ol0QlVVhEKhigu4KLcSiQQsy4KmaYhGo4VeDhER5RGDCKISJesReLavgWf7miE9PhKJoKqqCna2aCIiKimFbo0Rqp3WObuoUEQJobrpCNVOg3frysKtg6gXmqYBQEFajkiSVHFX7AOdQYTL5YKiKDAMY9jnE0URbrcbiqIgFosVJFQqNLvdDofDgXA4DF3XC70cKkOJRAI2m41BBBFRhcnzZWxEVCwMw8i0aBJFfisgIqKB2TbpGKDQVwabKWybdHRh10DUi/SQ6nwHhqIoQhTFiqyISKVSSKVSUFV12OdSVRU+nw+SJCEYDFZkCKGqKpxOJ6LRKHv4U84kEgnIsgxJGsTsQyIiKnncfSSqYJFIBKZpwu12F3opRERF4bbbbsNFF11U6GX0aqCtmU444QQ8++yzOVlDzDUaUd8koNABtigh6tsDMdeowq5jGB555BF85zvfGfDxM2bMQENDQ05a0FB2yLIMWZYLVg0BoGJb6Oi6PuwgwuVywev1wjAM+P3+rFRXlJr0TIxEIlGRIQzlj67rME0zU0VGRESVQaivry9sfT8RFZSiKKiqqkI4HOZVT0RUERYuXIgTTzyxx+1nnXUWwuEwkslkzlrWLViwAOecc06/xxxzzDG93q5pGlwuF7Zv397v41VVhcPhyMkMoOap30H72EMAMftXMN75rYnw2iR8/6n1A3uAmUL15nc7Zyeh++c1mUxi27Zt+Ne//oX77ruvx4biiBEj8PDDD6O5uRnnnXdev0/T0NDQ7/33338/HnjggYGtuQuv14t4PD7gVjqyLMPtdsPv9w/6uSg/XC4XVFVFe3t73p87Pc9gd98fypWqqvB6vWhraxv0LAdJkuDxeCBJEiKRSMX+PiyKInw+H1KpFGfIUV4U8nsmEREVBmdEEFW4ri2a0lemEBGVu3fffReLFi3qdlswGMz598DHHnsM//znPzNv33333Xjuuefw3HPPZe05dF3PWU/vUO30nIQQQyJKCNVOA3YEEcDOz6ssy9hnn33wy1/+EpZl4S9/+Uu3h5544ol47bXXcMABB2C//fbD6tWr+3yaefPmZf7/G9/4Bs455xycffbZmdt2Da1EURzQ11EwGNztMV0lk0mGEEXOZrMVbO6WJEkVWw0BdP4+a1kWVFUdVJCQDnBSqRT8fn9FtrYCOivuvF4vTNMc9PcmoqGKx+Ow2+1Zm+9CRETFj62ZiAgdHR2wLIstmoioYqRbb3T9Z5pmj9ZMjzzyCM466yxceeWVeP755/Hoo4/ipJNO6naukSNH4rrrrsOzzz6LZ555BjfeeCPq6up6fd54PN7jOaPRaObtp556Cocffni3xzz77LM44YQTIAgCRo4ciYaGBsyePRv/7//9P7zwwgu45557MHXq1Mzxu7ZmWrBgAf7617/i+OOPxyOPPIJnn30W1157Lex2e+YYu92Oq6++GkuXLsWSJUswf/78Hh8LQ3Uhaev8OTHZZ0PbL2di72pbt7X+6Ou1+ODC/QEAXpuEu0+ehM9/Oh3NV9TjvR9OxZnTqwf0+enNrPEuvHz2vmj5eT0+u3g6fn3UGFiaB0nVtXONOz6v27Ztw1tvvYX//Oc/+NrXvtbjXCeeeCJefvllLF++HHPnzu33ebt+viKRSLfbJkyYgBdeeAEHH3ww/u///g8vvfQSpk+fjjFjxuDGG2/Ek08+iaVLl+Kuu+7CzJkzu51319ZMDQ0NmDt3Lm644Qa88MILePDBBzFr1qzM/bu2Zkp/nr/+9a/j/vvvx9KlS7Fo0SJUV+/8GIuiiEsuuQTPPvssnn76afzwhz/EL3/5S/z2t78dxEeeBkLTNAiCULCr6WVZrthNdACwLAuGYQy4PZMgCPB4PHC5XIjFYhUdQgCdFVqiKCIYDOZ9vglVrmQyiVQqBZvNtvuDiYioLDCIICJYloVIJAJVVfmLIBHRLr773e/i888/xwUXXIBnnnkGl112GcaPHw+g8yrkW2+9FdFoFD/96U9xySWXIBaL4dZbb4UsZ7/wNL1BdP755+Pxxx/HBRdcgE2bNuHaa6+F2M/chjFjxuCII47AVVddhV/96leYMWMGzjzzzMz9P/nJTzBt2jRcc801+PnPf47p06dj77337naOmGdc5v+/9CfwUWsH5u/fPVj47lQfnlzV2WLhV0eOxr4jNJz2+Jc47J5V+MWyTWiLDW2jb7RLwaPfnYyPWqM46r7V+MWyJpw1owZXHD4a0S7r6mrSpEnYf//9e1wlfuCBB0LTNHz44Yd4+eWXccwxxwy7R/UPf/hD/OUvf8E555yD9evXw263491338UVV1yBCy64AO+99x5uvvlm1NbW9nueBQsW4LXXXsP555+Pd999F1dffXW/FwnYbDaceuqpuPnmm3HppZeitrYWP/7xjzP3n3HGGTj22GOxaNEiXHLJJXA4HD2CLsoOTdNgGEbBKksrvSICGPicCEVRUF1dDUVRKnYgdVdutxuyLOelKpBoV/F4nH9/EhFVEAYRRASg84+3eDwOl8vV72YWEVE5OOyww7B06dLMv+uuu67PY999910888wzaGlpwSOPPIJgMIj6+noAnfMcRFHE7373O2zYsAFNTU1YtGgRamtrM8dkS9dh1Y8//jj+/e9/o7m5Gffffz9GjRqFsWPH9vvYW265BY2NjVixYgVefvnlzBX6drsdJ5xwAu666y785z//QWNjI2699dYePwvi7jGAuTNIeOKzdsyb6su8PdlnQ/1oJ5Z81hlEjPWoWLElio+/imJTUMe/NoaxbN3QWn6cN3MkWsIGrnx5E9a2J7B0bRCL3mjFRV+vRcIzJnNc+vO6bNkyLF68GFVVVXjssce6nWvu3Ll49dVXYZomGhsb0draiqOOOmpI60pbvHgxPvzwQ7S0tCAcDuPLL7/Es88+i8bGRmzevBmLFy9GS0tLtwqH3rz44ot49dVX0dLSgnvuuQcOhwNTpkzp83hFUXDbbbfhiy++wNq1a/H00093q7yYN28eHn74Ybz55pvYtGkT/vjHP2aqOih7JEmCoigFq4YQRRGiKFb0Ff1A5++ygiBAUZQ+j3E6naiqqkIymUR7e3vO2tiVCqfTCZvNhlAoVPFBFhVGIpGAKIrDHjZPRESlgTMiiCgjEonA5/PB5XIhFAoVejlERDnz0Ucf4bbbbsu83d8G4vr13Ycn+/1++HydG/CTJ0/G2LFjsXTp0m7HqKqKMWPGINvSFRFffvll5ra2tjYAgM/nw6ZNm3p93JYtW7r1rm9ra0NVVRWAzmoJRVGwZs2azP0dHR09zpVS7BBgId204x+r/bjhG+Nw0BgHPmiJYv7+1fj4qyjWtncOX1780Xbc/9974oA6Bxo2hLF0bQDvbx7alcf71Gg9Hvvu5g64bBJGjqwFdnyK0p9Xu92O+fPnI5VK4fXXX888xul0Yvbs2fjpT3+aue3ll1/G3LlzsWzZsiGtDQA+//zzbm9rmoZzzjkHhx56KGpqaiBJElRV7bNlV1rXr7V4PJ75udyXWCyGlpaWzNtdP69OpxPV1dXdPq+maeKLL77gBQdZpmkaTNMc8ODxbJOkzrktlb6RnEqlkEqloKpqj37zkiRlrvyPRCIFm+VRTDRNg8PhQCQSqfhAhgonlUohmUzCZrPx65CIqAIwiCCijHSLJq/XC5vNVrA/qImIci0ej3fbwO3Prpt7lmVlqhPsdju++OIL3HjjjT0eN5SBn6Zpdqt8AHZuMva1pnQ4sevj+jo+/ZjBbkZbQvd1bO1I4o2NYXxnajU+aIniO1N9WPzR9sz9y9eHUP/nlTh+sgdHT/LgH6fvjXv/sw3XNWwe1PMOZF3p97zr5/XWW2/FPffcg7lz52aCouOOOw42mw1//vOfu51DkiSMGzcOzc3NQ1rDrpuaP/7xj/G1r30Nd999NzZv3oxEIoHrr79+t+26ettI7u/zuusV8EP5vNLwaZpWsGoIoHM+hGVZbKuDne2ZurZb0jQNLpcLqVQKgUCg4gMboDMsd7lciEajDGWo4OLxOJxOJyKRCGeUEBGVOf6lQkTddG3R1N/mBxERAWvXrsXYsWMRCATQ0tLS7d9Q+o4HAgHU1NRk3h47dmxmqHSuvie3tLTAMIxuLYCcTmdmDkaaYPVs+7Lks3acsp8PB41xYlKVDf9Y7e92f1ssiUdXtuNHzzXi6uXNWFA/Ykhr/KItjq+PdXa77ZCxToQTKWxpD/T6GMuy8NBDD+G8887LtHyYO3cuHnvsMfzgBz/o9u+TTz7BnDlzhrS23kybNg3Lli3Dm2++iQ0bNqC9vR2jRo3K2vkHoqOjA+3t7dh3330zt4miiH322Sev6yh3qqpCFMWCBhGcD7GTruuQZRmSJGUGUrvdbsTjcfj9fn6c0BlceTwe6Lpe8fMxqDgkEgkIgsD2TEREFYBBBBH1kO4f3d+ATCIiAl555RUEg0HceOONmD59OkaNGoUZM2bgkksuwYgRg990/+ijj3DKKadgr732wj777IOf/exn3VqM5OJKwVgshmXLluHCCy9EfX09Jk2ahF/84hcwTbPb80lGDBa6hyHPfRGAS5Xw+xPG482mML6K7FzrL2ePxpy9vdijyoZ9R2j45l5efNHW/2at2yZhWq29278xbgX3/WcbxrgVLDp+HPautmHO3l4snD0af37vK4hG31fzvvbaazBNE6eccgomT56MffbZB88//zwaGxu7/Xv11VdxwgknZK2aoLm5GbNnz8bkyZMxefJkXHPNNQUJ95966imcddZZOPzwwzF+/HhcfPHFcLlcvOI0i9JDqgs5n0GW5YqfD5Gm6zosy4KmafD5fJmB1JyN0kkURXi9XiSTSbZhpaJhmiZ0XYemaYVeChER5RhbMxFRD+kWTR6PB6qqsl8nEVEfEokELr30Ulx44YW44YYb4HA4sG3bNnz00UeIRqODPt9dd92FhQsX4o9//CO2b9+OO++8My9XsP/5z3/Gz372M9x8882IRqN49NFHUVtb2+37vxZuAcTu7Zkiuoll64L47/18uOT5xm73GSkL1x41BuO9NsSTJv69KYIfPLOh33XMnujGv87br9ttD36yHZe90ITTn/gS1x8zFv86bwT88RQe+qQNf3h7C8aH+26xZZom/vGPf+D000/H+PHj0djY2OscjTfeeAM//elPceihh+Ltt9/ud40D8ec//xlXXnkl7rzzTgSDQTz66KNwOp27f2CWPfLII6iursYvf/lLmKaJ5557Dh988AE3rbMkPWC10JvckiSxnWYXpmnCbrfDMAyEw2G2rNpBEAR4vV5YljWk1oFEuZRIJDIV+QzLiYjKl1BfX8/v8kTUK4/HA0VR0N7ezl8IiYgKzOVyQVEU+P3+3R88TJqm4YknnsBf//pXvPrqq5BlGabmwZszLs35cw/W1IbrIOu82nkgBEHA/fffj9deew2LFy8u9HJKnsPhgMPhQFtbW8F+TxJFETU1NQgGgxV/4YgoivB4PJlZLNu3b9/NIyqL1+uFLMsIBAIMI6noCIKAmpoaRCKRgra6IyKi3GJFBBH1KRwOo7q6Gi6XC+FwuNDLISKqeLna7Nxrr72wxx57YN26dfB6vTjjjDMgCAJWrlwJRVGQTCZhBLZCToSRtBVP2z7FiKBKBXSweq83dXV1OOigg/DJJ59AURT893//N0aPHo3ly5cXemllQdM0JBKJgl6skR5mX+mzD2w2W6btWCgUgtfrZVVvF263G4qiMISgomVZVqY9E4MIIqLyxSCCiPrUtUVTIpHgH3NERGVCFEUoigJZlqEoCqqqqnD66adjzJgxMAwD69atw89+9jNs2LCh2yarZ+sKtI89pEeLpoIwU6jevhqqqsJut8OyLBiGkfl5xXYsnS1qTjzxRPzoRz+CIAjYsGEDfv7zn6OpqanQSyt5iqJAkqSCX6ghyzIsy6rYr3dBEOByuTKbl5FIBJZlIZVKMYjYwel0wmazIRQKVXxgRcUtkUjA4/FAFMWK/Z5GRFTu2JqJiHYrXebu9/vZoomIqEDcbjdEURxSb+904JD+b/oq6lQqBcMwYBgGksnkbjepYq7RWHv4z4e0/lzY+63fwR75CpIkQVVVqKoKRVEgCAKSySR0XYeu690GfhNlg9vtzvxuVEgulyvTbqfSyLIMj8cDQRAQiUS6zclwuVxQVRXt7e0FXGHhaZoGt9uNSCSCWCxW6OUQ7daIESMQjUaHNGeLiIiKHysiiGi3IpEIfD4fnE5nwQcyEhFR/0RRzAQO6fAhPfwxmUwikUhkwofBhsv2SCsc/kZEvRMAUczRezAAZgqOYBPska8AdAYqsVgMsVgMgiBAURSoqgqbzQaHwwHTNLtVSzBUp+EQBAE2mw0dHR2FXgpkWa7IVjt2ux1OpxPJZBKhUKjH1dO6rsNut0OSpIr8+ACAqqpwuVyIRqMMIahkJBIJ2Gw2BhFERGWKQQQR7ZZpmujo6IDb7c5sYBERUX4JgtDr7bIsdwsedq12SH/fzlZLjpGNDdh44LlZOdeQiRJGNr7W613pPtPpliyyLGeqJdxuNwRBgGEYmWPYqoQGy2azAUBR9DGXJKlbJUC5E0UxM+8gFov1GQalA0dVVStyEz5dLaLrelEEZkQDlUgkoGlaRYeIRETljEEEEQ1IPB6HzWaD2+1miyYiGjJDdSHmGYe4ewxSih2WIEGwUpCMGLRwC+yhZig6K6/6kt5Y69pmqbdqh2QymbP+yp6tK+HeshLhkfsVZlaEmcKI4DpMSragYwA94NMtp6LRKARByIQS6SuqTdOEruuZjx1/vtHuaJpWFJU1oihCFMWK2axLh4mWZSEYDO72whjDMCoyiBBFER6PJ1MtQlRK0jOeNE1jiEZEVIYYRBDRgIXDYVRXV7NFExENSsw1Gm0TZiFUOx1Jm7vzRjMFATs38SwImU1tORGGZ+sK1DS9DXuktRBLLhqSJGUqHRRFgSiKsNlsSKVSSCaT6OjoyGq1w0AIAMatWoLPj1gIU7ABQh5bNFkmxJSOUSseg6kCXq8XiUQCkUhkQMGLZVlIJBKZK8jTLZxUVYWmaZmB1+lqiUrZ4KWBSweAQ5nVkm3p6qdKqOpxuVyw2+1IJBIIh8MDCoF0XYfT6czD6oqHIAjwer0AUBRfo0RDkW7PxCCCiKj8cFg1EQ1KeuhdIBBgiyYi6pMFIFQ7Ddv2OAbRqkmAmRrc1fM7jnf4GzGysQGerSvRe2Oi8pGebdB1sLQoiplqB1EUYZpmr/3QCyFQdwCa6hfk/XknfPwAqrZ8CmBnD3RRFIc93FIUxUwooaoqBEFAKpXKhBK7q7ygylBMQ5DTVT3bt28v9FJyRpIkeDweSJKESCQyqHZYoiiipqYGwWCwYl6/Xq83M7ycQSqVKlmW4fP5+PcmEVEZYhBBRIPm9XohSVJR/BFORMXHUN1onjof4bppgGkOb6jxjkDCvWUlxq1aAkUPZ2+hBZaudugaPADIDFZOVzqk/wj3er0wTRPhcPF8DLZNnI3WKafk7flGr3kaIze+0eN2p9MJu90O0zQRiUSysunYNZSQJKnb7Il06wiqPDU1NYjH40Vxpa7L5cpsOpcjTdPgcrmQSqUQCoWGtLHu8/lgGEZFVPK63W7YbDYEAoGKqJKh8lZdXQ1d1yvitUtEVEnYmomIBq1ri6Zi+EOciIpHoO4ANO9/KkxJ7bxhOCEEkKmiCI/cD58fsRDjPns8czV8KREEodtA6a7VDumh0tFoFIZhlNQGdzoUaJ1yCmCZuWnTtOO8Y1Y/jRFNPUMIAOjo6EA8HofL5Rp0u6a+dK2CkCQpE0q4XC4IgoBkMpk5hldsVgabzQZRFItiSDXQedVwOV71LghCZlM9Go0O63dNXdczw8XLmcPhgKZpCIVCDCGoLKSHVjOIICIqLwwiiGjQTNNER0cHnE4nEokE/+AhIgDAtolHonXKt3OzIS1KMAUbmuoXwOjjqvhiIklSt+BBkiQIgpCpdojFYpmKh4EOvE0PpS42Ize+ASUe3BlAZXGAtbBjJsTYlY/tNoBKpVIIBoOZsKC6unrY7Zq6njsWiyEWi3UbeK1pGhwOR2bgdfpfMX6eaPjSQ6qLZfNfkqTMvJNyoSgK3G43BEHISkslXdfhcDggSVLRfN6yTdO0zPy2cvt6oMoVj8fhcDigqmrFtFYjIqoEDCKIaEhisRhsNhvcbjf8fn+hl0NEBZYJIYDcDS/ecd50K6BiCiO6Vjqkh0oDyLRWSgcP5boRVrXlUzj9G7q05BrkTJBd7Xh8Vdsa7L/5ZUTbWjDQyFvXdbS3t8PpdMLhcMBmsyESiWStamHXgdeyLGeCCY/Hk5npkQ4lGNaXh/QMkVAoVOilAOhcjyiKZfU9Jf2a1XUd4XA4K9VhhmHAsiyoqopYLJaFVRYXRVHgcrkyQSlRuUilUkgmk7DZbAwiiIjKCIMIIhqycDgMn8/HFk1EFS5Qd8DOECJPWqecAiUeLEibJlEUuwUPsixnqh2SyeSQqh3KgaKHMenjxZ1Dyicdg6hvEgQrBUsYwpDyYBNGNr4Gz9aVEKuq4PF4Bj2XqGu7pqqqqqy0a+pNMplEMplENBrtNvA6PUiYA6/Lg6ZpME2zaK44l6TO11U5BF2SJMHtdkOWZUQikaxvqOu6XpZBRHqQN/voU7mKx+OZap9K+n2KiKicMYggoiFLpVJs0URU4QzVjeb9T83dfIC+WCaa9z8VTv96KHpuN2C6tljatdohmUwiHo/npdqhWFszdSUA8G5dCe/WlTCqxiO051HY4tkLSZu78wAzBQE73wcLQqZyQk6E4Nm6EjVNb8Meac0ckw69XS7XoDfb0u2abDYbnE4nqqur0dHRkbMNSdM0EY/HMzMEFEXpFkxYlgXDMDKhRDldzV7uNE0rmhAC6Py+ZFlWSc2U6U3XgdS5GrKs63pmtkuxfw8dKFEU4fV6M4O8icpRIpGAy+WCqqpF9f2XiIiGjkEEEQ0LWzQRVS4LQPPU+Z1zAfIZQgCAIMKUVGyeOh8TP74fQpZOm652SIcP6WqH9AZyLBbLtFsqlw2tXPEl/ahrXIqa9nYkVReinnGIu8cgJdsha3ZIlolkRwBauAWOUDPkPgKlVCqFSCQCt9s95KqCRCKR6RXvdDozAzBzPWTaMAwYhoGOjg6IogibzQZVVeF0OuFyuTjwukSoqgpJkopmSDXQeTV8KV8AIggCXC4XNE1DLBbL6RX9uq5DEAQoilIWVUmCIMDr9QIAgsFggVdDlDvpuVo2m41BBBFRmWAQQUTDlr5a1eFwZGUoKBGVhlDttM55AIUiSgjVTUeodhq8W1cO6RRdA4f0UGmgc/PbMAzE4/FM5UOhCUK24pb86HoFo6xH4Nm+Bp7tawAALpcLsiwjEAgM6FzxeByqqsLtdqO9vX1IIZBlWT3aNcXjcXR0dOTlqnLTNLv1cU9XSthstszA667VEqV+pXs50TStaL4PpMmyXLIVNdkeSL076bZ55TL01uPxQBRFBAIBBuJU9tI/s8upoomIqJIxiCCiYUulUohGo3A4HEgkEiX7hzERDc62SccApgmIea6G6MpMYdukowcURIii2K3NUtdqh2QyiUQikbmCvVj/2C3Wde1KlmVIktTnFYyWZQ06WAmHw6iurobb7R5WK5J8t2vqS9fqDkmSMtUS6Q2XrqFEMW2AVxpBEKCqatHNwurv9VXMHA4HHA4HDMPI2kDqgdB1HTabLS/PlUtutxuKoiAYDPL3baoI6fZMNputqKrSiIhoaAq4c0BE5SQajSKVSsHtdhd6KURl47bbbsNFF11U6GX0KuYajahvUr8hxHivirZfzsS0WnvuFiJKiPr2QMw1qsddsixD0zS43W5UV1ejpqYGXq8XNpstM+PG7/dj+/btCAQC6OjogK7rWd/sr6urQ0NDAyZPnpzV8xYzm82WuQq5N0MJIizLQjgchs1mg6Zpw15jIpGA3+9HLBaD0+mEz+eDoijDPu9QpAP9QCCAtrY2hEIhpFIp2O12+Hw+1NTUwO12w2azlVxlTKlLf60V0waYKIoQRbGkNqJFUURVVVWmejYYDOa16kfXdUiSlKl6K0UOhwOapiEcDrOVG1UMy7Kg63pWfu4TEVHhCfX19aVxaR0RFT1ZllFVVYVoNMoWTVRUFi5ciBNPPLHH7WeddRZaWlqwcOFCuFwuXHvttT2Oue2227Bu3Tr86U9/6nb7CSecgIsvvhgnn3wyAODXv/41Ro0ahYsvvjizuSJJEv785z+jqakJN91006DXFg6HkUwmc3Kl9owZM3D77bf3e8xll12GTz75pNf7mqd+B+1jD8kMGu6NKAAjHDLaokmkcvjbxni3hI8vmtHrfb/61a+wevXqTKVDMpnMe8ub9FDR4W681dTUIBaLDev764IFC3DEEUfgggsuGPI5BqK6uhq6rvfZ991ut8PhcKCtrW3Q505fGen3+7P2uZQkKXOlcT7bNQ2ELMuZaon0gGIOvM4fn8+HZDKJcDhc6KVkKIqCqqoqtLW1Fc3XaX9sNhtcLhcsy0IoFCpYhc+IESMKUv2UDTabDR6PB5FIpCTXTzQc6a//UvmeR0REfWNrJiLKmvSGKVs0UTF69913sWjRom63ZXPI4+23347FixfjjDPOwEMPPQQAOPvss1FdXY0rrrhiSGvL5R9bn332GebNm5d5+5JLLoHD4ei2jv423kK10/sNIQDAtICtHXnYcNqxjhtuuAEbN27M9HI3DAPt7e0F/V4kyzKSyST8fn9WzlcKrZnSVx331zZmKBURaR0dHVBVFR6PZ8AzJnYnlUohEAhkNkxVVUU0Gi2KDb/013N64HV6tkR64HUqlcqEEuXQ/76YyLIMWZZzOkh5KNKBVLFvyHUdSB2PxxGJRAr6PUzX/z975x3fRn3//9dtSadh2XE2SSAlBEggjACFQAnQHy0t/bZsaCFQyp4texfKCITVUkZLWC2rjJbVUFpK2CHQkJBFIIMkONOOtdfpxu8P8/kgybIt2ZIt2+/n4+HEPp1OH53uTtL79Xm/XgZUVa2J87ocWKZGbsYMQQwmMpkMHMeBy+WiyW4EQRD9HBIiCIKoKKxA5PP5KlYgIohKkM1mK1YMLkY0GsVdd92FG264AfPmzYMsyzjppJNw7bXXdlnE6mhshd0YzzzzDF577TWMGjUK3/ve9xCLxfDkk0/itdde4/dpbGzEueeei7333hu2bWPJkiW47777sGXLlrxtFxbHM5kMFEXhy4p1gvzud79DPB7HzffcD1PzYeE5u+Ivi1qwfVDD/+0URDht4a4PN+Evn7XNct8uoGLROZPwvUc/x9KtKRwwxotXTpqAnz2zEjccPBIThrixdEsSF8xZh1Wt3xatL9l/OM7YqxFuWcQ/VoTQmjRxyA5+HPzYik7349rN2/DVmjVFb7vzzjth2zYuv/xyAG0+24888ghef/11PPbYY7xD5KqrrsKvfvUrbLfddli1ahVmzZqFtWvX8u1MmjQJZ5xxBnbaaSdEIhG8//77ePjhh7ltyzPPPIM5c+Zg9OjROOCAA/Dee+/h8ccfx7PPPotf/epXWL16NX+syy+/HGeccQbGjBmDZcuW4Xe/+x0mTJiAc889F0OGDMFHH32EWbNm8YK+IAg47rjjcPjhh6O+vh5NTU34y1/+gnfffRfAt10ul1xyCc4880yMHTsWq1atwh133IGvv/4ahx9+OE499VQAwNy5cwEAM2fOxBtvvIEZM2bghz/8IYLBIKLRKN59913cd999ne7vjmC2TJ1Zh/REiGAWTYFAAG63u6KFuUwmA8Mw4PF4oOs6XC4X4vF4zdig2LaNdDrNjzdFUXi3hNvt5hYWFHhdGVwuFw+uryUkSar53BBZluHz+SCKIqLRaE3kWRiG0e9CbyVJgt/v77TDjCAGA5lMBpqmkRBBEATRz6GMCIIgKk4sFoMsy3C7q+gLTxA1yIcffoi5c+fiqquuwlVXXYU33ngD8+fPr+hjHHvssfjiiy9wxhln4OWXX8bFF1+M7bbbDkBbweKOO+5AMpnEhRdeiAsuuACpVAp33HEHZLlycw9S/tH893P3GYZFm5I4+LEVeHRhM+48fAy+U995IOg1B43EdW9twGGPr4DpOPjDEWP5bcfsEsSvvzscN729AYc8vgJNUQOn7dFY2rj0oR3eNnPmTOy00044+uijAQC/+c1v0NLSgieeeCJvvbPOOgsPPvggzj77bITDYdx6663cU3zkyJG444478O677+L000/HTTfdhEmTJuHCCy/M28Zxxx2HVatW4cwzz8Rf//rXDsc0Y8YM/OEPf8D555+PoUOH4oYbbsAxxxyDm2++GVdddRX23ntv/OxnP+PrH3XUUTjkkENwzz334LTTTsPzzz+Pa665Brvvnm9Ldfrpp/PnYFkWF1/mzp2Lv/3tb/jqq69w1FFH4aijjsLcuXNx0EEH4ZhjjsHdd9+Nk08+Gddddx3WdCDolIKmaV3OzGdFwO6KEdlslmc7VPLYBtrGxvJDHMdBXV0dL6jWGtlsFvF4HK2trWhtbUUikeCz0BsaGhAMBqHrep9lX/RnBEHgM/lrDVmWa7rr1O12o66uDo7jIBQK1YQIAbQJEYIg9Jvzgdn6WZaFaDTa18MhiD4lk8lAluV+nfNCEARBUEcEQRBVgFk06bpO/tVEzfDd734Xc+bM4X/Pnz8fN954Y8Uf549//COef/55JJNJPPDAAxUf2/z58/Hyyy8DaJt9f8wxx2DKlCn4+uuvMX36dIiiiFmzZvH1b7/9drz66quYMmUK/ve///XgmX1L2jcSsNvO6zdXR/DowhYAwO8/2oKzpw7FtDG+vA6HQm55dyM+/LptZufv523B3477DjRJQMZycMZeQ/HU4hY8vaQVAHDnB5sxfZwfutp1EfjRmy8H7N/kLTviiCMAAC0tLbj77rtx1VVXIRgMYt9998UZZ5zRbsb4X/7yFyxYsABAm3jx3HPP4cADD8Tbb7+Nk046CW+++SZefPFFAMCGDRtw33334d5778U999zDZ00vXLgQzz//PN/msGHDio/30UexdOlSAMCcOXNw5pln4qSTTsKmTZsAAO+88w722GMPPPvss1AUBT/72c9w1VVXYdGiRQCATZs2YfLkyTjyyCPzsjweeeQR/vczzzyDmTNnQlEUGIaBVCoFy7LyumGGDRuG1tZWLFiwAJZlYevWrVixovPuk46QJAmyLCORSHS6Xq4Q0d2ZybkdeNXodqplu6ZiWJbFrVtYsZUFe3s8Hti2ndct0V9mhPcVmtYmqNaiENGV9VlfIYoiz1pJpVJdXgd6G9u2YZpmSWJpXyMIAgKBAACQCEEQAO/yc7lcNXdtIQiCIEqHhAiCIKoCWTQRtcbChQtxzz338L+rVVw69NBD4TgOAoEAxowZU1JBt5yxFc5UD4VCCAaDAIDx48dj1KhReaIGAKiqipEjR2Ly5Ml5GRB333033nzzzZKeVy6W4oaAtiLmsq35RdmtiSyG6J1/vMi9z5ZEW/F+iC5jQzSL7zRoeHRhc976n25K4MCxvi7H9ZuHX0V03osd3v7OO+/gwAMPxM9//nPcfffd2LBhQ/uxLVvGf4/FYvj6668xZswYAG37d4cddsBhhx2Wdx9JkjBixAisX78eAPDFF190OVYAWL16Nf89FAohlUpxEYItmzhxIgBg1KhRcLlcuPXWW/O2IcsyVq1a1eF2WRh0MBjE1q1bi47j7bffxtFHH42nn34aH3/8MebPn48PP/ywW7Y+qqpye6DO6GlHBCMajfJZ/9UqTDC7Jl3XuV0TC5KvVXItmoC244RlS/j9fjiOA9M0+Tq1/Fz6CpfLhWw2W3P2VqIoQhTFmpvkwT7zOY6DSCRSc3ZWDMMwuMhUy/j9foiiiHA4XHPHIEH0FcyeiYQIgiCI/gsJEQRBVI14PI66urqatTYgBhfpdBobN24s+36JRAK6rrdb7vV62/k1jxgxAmeddRbuvfdeTJkyBVdccQXOPPPMLgsy5YytsGCY67Xvdrvx5Zdf4uabb253P1YY+tWvfsWXdTaL3LbtdkViZoHjCN+2xWft/FnVjgOIXRSXc+/DJmV3dZ9S2BhOItrJftQ0DTvuuCMsy8Lo0aM7XK8j3G43XnvtNd4RkUtukb/U613ua+k4TrvCouM43A6IWd1df/317Y6VwuOrcLtA5wX/5uZmnHLKKdhrr72w99574+KLL8bxxx+Piy++uOxiZ6kzjSslRFiWhUQiAa/XC8Mwqlb8dBwH8Xgc6XQaXq8XwWCwJsJ3S4UFXieTSQiCAFVVoWka3G43dF3PC7zOZrP94jlVE0mSoCgKIpFIXw+lHcyWpJbEI6/XC7fbjUwmg1gsVtPHD8uAkWW5pvZhLl6vlx9/tSY4EURfkk6n4Xa7oShKzYqdBEEQROeQEEEQRNVgHt6sQEQzuoj+yNdff42pU6e2Wz5hwgQ0NTXxvwVBwBVXXIFPP/0U//73v/HBBx/g0UcfxWmnnYY///nPvTLWlStXYvr06QiHwx2G+ZUqeEQiETQ0NPC/RVHEuHHjsGjRIghO9Qojq7ZlsMcID/62tJUv22NEeyGoGC5NgVxfD9u2i/6cd955cBwHV1xxBWbOnImPPvoICxcuzNvGLrvswkUFr9eL0aNH806HlStXYuzYsd0StHrK2rVrYRgGhg4d2iOLLdM0i2YdGIaBefPmYd68eXjppZfwl7/8BTvssANWrlxZ8rZFUYSiKCXZiFRKiACAVCqVZ9FUzSKoaZoIh8NwuVzQdR319fVIJBL9Smx3HAeZTIZb+yiKwrslWOB1NpvlwsRgLIS6XC5uZVVryLIMx3Fq4jMVC1KWJAmxWKxfnAesy0VV1ZoUIjweD9xuN6LRKBVaCaIA0zRhWRY0TUM2m0VW9SLlH420byQsxQ1HkCA4FqRsCq7YRrijTVAMCnknCIKoJUiIIAiiqsTjcV4gqsWZhQTB0HUd48ePz1sWjUbxyiuv4Gc/+xkuuOAC/POf/0Q2m8V+++2HQw45BFdffTVf9+ijj8a4ceNw2mmnAWjrpLjzzjtx66234t133+225345vPnmmzj++ONx880347HHHkNzczOGDRuGgw46CM888wxaWlpK3tbChQtxzjnnYL/99sOGDRtw3HHHwev1AgCkbAoOel5ALsbDC7binh+OxaJNSXy8IYGf7RzEro1urI107Yful22k3G5uXSKKIlKpFEzTxJ577okf/OAHuPbaa7F+/Xq88soruPrqq3HRRRchFovxjoMZM2YgkUigtbUVp512GiKRCN5//30AbXkL999/Py688EL885//RDqdxrhx47DXXnvhD3/4Q1X2ByOdTuPVV1/FmWeeCcuysGTJEui6jkmTJiGZTOKNN94oaTubN2/GiBEjMH78eDQ3NyOVSuGQQw6BJElYvnw5MpkMDjvsMKTTaWzZsqWsMZZqy5RLJYQIoM1GKxgMwuv1IhaLVWSbnZFOp5HJZKDrOrxeL1wuF+LxeE0WNrsim80im80ikUhAFEXeLcGem2VZ3J5qsBRGa7mTU5KkmjjOXC4XPz5CoVC/Eqyy2SzPfKkl2HmXSCRqMgOEIGqBVqkOLTt+F1sDO8LUvrHttC1uGQqg7TOq2NY9Jmdi8G9dgob1H8Id31RskwRBEEQvQkIEQRBVJxaLkUUTUfPssccemD17dt6yf/7zn7jzzjtx0UUX4fTTT8edd94JRVGwfv16/Pa3v8Unn3wCABg9ejS/Pdfu6JNPPsG//vWvki2aekomk8FFF12Es846CzfddBM8Hg+am5uxcOHCsgsuc+bMwfjx43HllVfCsiy88MILPCTZFdvIv+BVmheWhzC2TsONh4yCSxLx0ooQnlm6DXuO8HR534d/fSKAE/OW3XTTTfj0009x1lln4S9/+UtbR4cg4LHHHsPkyZNx9tln46677uK2U88++yzOP/98jBgxAmvXrsWsWbPg9/th2zZaWlpw9dVX4xe/+AX+8Ic/QBAEbNy4EW+//XYV9kR7/va3v2Hr1q046aSTMGLECMTjcaxcuRJPPfVUydt49913ceCBB+Kee+6Bz+fDzJkzEY/HcdJJJ+Gcc86BJElYs2YNrrnmmrIDUtkMxVI6EirZEQG0WYnF43H4/X4YhtErRbxidk0soLeWrWk6w7ZtpNNp/l7NOiU0TYPH48nLnhionY6qqkIUxZr9vCLLcp8W/QVBgM/ng6ZpSCaT/dKr3TAMeL1eCIJQM+eqoijw+XxIpVI1J5AQRF/jAIgOnYTm7acjWTcOgmPl2YRClNDRmWxqPrSO2het2+0PT2gtGtfOhX/r0ipNpyEIgiC6QpgyZUptfPoiCGJA4/V6oWkaQqHQgCxcEMRgIqt68fn0G3vt8V48/jvYmsjinNfWdbreLnNvgNzNFvzdd98d9957L3784x8jlUrldVV09FNYRC9mB8UsVAp/ykUQBAwZMgSRSKQm7WIEQUBDQwMvzJdCY2Njxe1cfD4fVFXtk/caZtcEoN/ZNZWCJElclJBlGYIg8MDrTCZTE7P0K0FuSHAt0tDQgGQyiVQq1euPzYrlgiAgFovV5LWoFERRRENDA6LRaE10HkiShLq6OpimSd3DBFFAVvWhaZdjEBs2CbBtoIi9ZMnYFiBK8G1ZitHLX4BiVL+DkiAIgsiHOiIIgugVEokEWTQRxABBMeKQM7FvW+IriFsWcOoejXjrqyhs28FRu9Tj4O39OOqZzrMKFCMOr2giU4EZrqWKBYIgdCpUyLLMf8/FcZwOBYpiQgZ7rFpG0zQAKKuolxu0Xini8TiCwWCfvNfk2jX5fL5+bddUDMuykEqlkEqleOC1qqpwuVzweDw8U4H91MpM83Jg1lTxeG16irPrSV90ROi6Do/HA8MwEIvF+vWkEtu2YZomVFXtcyFCFEUEAgHYtl12FxpBDHTCw3ZD067HwZbUtgU9ESEA3s0ba9wZX0y7AqOXPYe6LYt7OEqCIAiiHEiIIAiiV2AWFoFAAJqm9fkXP4IgeoZ/6xK0jtq34hZNDoDvj/fjN/sPhyYJWNWawYy/r8E76zqZtWZbaAitgNfrhdfrRTab5WG81SyGOo4Dy7JKKgp2JlhIkgRFUYp2WeQKFkDbrHtZlivSZVFJVFUt2ZaJUY3XxnEcbgfodrt7fdb4QLRrKkZh4LUsy1yY8Pv9cBwnr1uiv+QHuFwuAOUJar2JJLVdb3tT3JIkCT6fD7IsIx6P90knRjUwDIO/3n2FIAjw+/0AgEgkMqCuEQTRU5rHHoRNE/8PcGxA6KEAUYgowRY0rJ8yA9kVL6Fx3XuV3T5BEATRIWTNRBBEr9KXthkEQVSOlHcEVh5waV8Pg7PjB7PgSWyBpmnQNA2KogBoK9ixgml/uObkdlnk/i5JElwuF0zT5MtLsYbqrMuikmNuaGhAIpEoq0hZX1+PdDpdFT90Xdfhdrv7PER3oNs1FYN1FbAfQRBgWVZet0StUl9fj2w22yuB593B7XZD13W0tLT0yuPlBlLHYrEB090DtNlM1dXVIRQK9dnzCgQCkGUZ4XC434h1BNEbcBGilxhBYgRBEESvQR0RBEH0KvF4HPX19fB6vdSCThD9GHd8EzyhtUgGxvS8Vb4nODYCiY0YJiaR+CZgNp1Oc+sYTdOg63q7TolaFSU66rJgQkRuMbAn1lClZlmUIlqwYnO5s8irYc3EyLUD7Euv/4Fu11SMwsBrRVG4KOF2u+E4DrLZLO+WqJVzUVEUSJJU059NJEnqlWNHEAR4vV64XC6kUqmatarqCdlsFrZtQ1XVPjkfvV4vFEVBJBIhEYIgcggP261XRQgA2DTxp1DSEbJpIgiC6AVIiCAIoldhthlk0UQQ/Z/GtXOxbo/T+nYQgojGte9AlmXU19cjk8kgmUzmdUJ0JEr0N9uYQiplDZUrWHRmDdXRj6ZpvKhX7virmX3BLJp0XUcikaja43RFrl2Tz+dDXV0d0un0gLNr6ohsNotsNotEIsEDr1VV5ecis3AyDAPZbLbPxsk6jmpZJJJluerXK1mW4ff7IQhCzYQ5VwvDMKCqalW6sjrD4/HA7XYjGo326TFPELVGVvWhadfjqmPH1BmOjaZdj4MeWgPFGHjCK0EQRC1BQgRBEL2OYRjcP7u/BloSBAH4ty6Fb8tSxBp3rnhWREnYFvzNy+He8Cla0RaY7PF4EAwGYRgGEokETNNs52fPRAlmc5IrWtSqKNHTgn2lArhzsywKx9TQ0FCWNVS1hQjTNJFMJuHxeJDJZPq8wGyaJkKhELdr0jRt0Ng1MQoDr1m3BDt3bdvOEwl76/OBIAj89ahlJEmqqjDg8Xjg8Xhgmiai0WjNdKtUC8Mw4PP5IAhCrx1rTBBPJBIDWuQhiHJxADTtckxbMHVvihAAIIiwJRUbdjkGYxc9jup9MiEIgiBIiCAIok/ItWiqVS9mgiA6RwAwevkL+GLaFbAFrddnr4mWgVHLX+BfGJmYUChIJJPJvFmnuT71xUQJVgTt68J1MapdLCuny4IVb30+H7duKccaihX/vF5vh6JFT0kmkzxAORQK1YTwzeyavF7voLFrKobjOHnnYm7gNds3TJQwDKOq+0fTNACoaVGInUvVEEtFUYTf74csy0gmk73eIdBXGIbBO+Z6QxRQFAU+n69q2TgE0Z+JDp2E2LBJfTcAUUJ02GREh05CYOvSvhsHQRDEAIeECIIg+gRmVeH3+5HJZGo6vJIgiI5RjBhGL3sO66fM6N0HFkSMXvZc0RZ6JkioqgqPx4O6ujpuDVNog5FbCFUUBZqmweVywePxwLIsvq3BViQuBcdxoCgKTNPsMqS6WHeFpmldWkN1lWFRimgRjUZRX18PXddrxuue2RSy7sDBZtdUDGaLlEwmeXGY5Uroug7btvMCryu5n9xud810aGZVL1L+0Uj7RsJS3HAECYJjQbENOE4UZuxzSKjcZyZN0+D1euE4DsLh8KC61rG8kt4QIiRJgt/vr+kwdILoS5rHTQdsu29zx2wLzeMOJiGCIAiiipAQQRBEn8EKfD6fD62trTVRACAIonzqtixGdsVL2DTxp732mCNWvNRlqCArWBYKEslksqj4ybzs4/E4FyVYd4VlWbxToi88vatpYdQTVFXtUoQAiltDMREiN0y6VGuoYl0WnYkVqVQKHo8nT3iqBbLZLEKhENxuNzweDzRNQzweH/SWLYV2armB1y6XKy/w2jCMHnUJyLIMWZb71JYp5R2BbWP2R3ToZJiar22hbUHAt5+LHEHARkECvgPImRj8W5egYf2HcMc3desxcwOp0+k04vH4oPwcZhgG3G53VR9DFEUEAgHYtl3TYegE0VekvCOQDI6r2Pb++KOxCGgSTv77mvLuKEpIBrdHyjsc7vjmio2HIAiC+BZhypQpg+8TJ0EQNYMoitw+hWaIEUT/pnnsgW1iRLVCBr/Z7sgVL2HIuvfKvruiKNB1HYqidCpIFCLLMhclJEmCbdu8SNpbooSiKKirq8O2bdtqxredjSkUCnVrFrWu61BVFaFQqFuP35lgkfvTnQDuSllDlft8dF2Hy+Xis6ZrNbOkLxFFkYsSqqpCEAQuFHZHaPJ6vVBVFa2trVUacXEctFmRNG8/Hcm6cYBtlZe18836ntBaNK6dC//WpSX7msuyDJ/PB0mSEIvFBrXwJcsygsFgt69jpVBXVwdRFBEOh2vm+k0Q1aaxsRGnnXYapk6dikAggG3btuGDDz7AX/7yl3aCXNMuR6N11L4VyxvzaSIECIhmvn0PHarLuPi7w/H98X6M9KmIZix8Fcrg+WWteHbJNqTMb8pitoX6DfMxevmLFRkLQRAEkQ91RBAE0afYtk0WTQQxQGhc9x6UdARNux7XFjZYyQBr24JoGdh5/etoiCxDuBubyGazCIfDUBQFHo8HgUCAW8F0VohjljGJRCJPlHC73dwuZjBevzRNg2VZ3S7e9TSsuhyxQBRFbo3CitbFsiw6soYqNYC7J9i2nWfXFAwGkUqlkEwmB+VM9Y6wbRvpdJrnOeSKEm63Oy97wjCMLo8RTdN6PRsiq/rQtMsxbX7obHzlXi+/WT8Z2A7r9jgNvi1LMXr5C1CMzid15ObhhEKhQS92maYJ27ahqmpVhAi/3w9JkkiEIAYVI0aMwB//+Ec0NTXh5ptvxqZNmzBu3DicffbZ2GeffXDeeeflTUCLDp1c0c+MsUz+uTY2oGLOyTshmrZw8zsb8XlzGhnTxi5D3Thl9yHYFMviX6sibSuLEqJDJwEkRBAEQVQF6oggCKImYCGJtRImShBE98kvspU5y7eQb+7v37IEo5a/ALedRl1dHTKZTI+7qGRZ5rPySxEkCpEkiYsSsixXVZRQVRWBQAAtLS01c42sr69HJpPptqUNsyPatm1bhUfWMWw/soJ/IV1ZQ+X+5JLbZdFVx0Wprx/bPwDIrqlEJEniooSiKBAEgQfQG4bRroNJ0zT4/f5e7TQKD9utqmLt6GXPFbWtE0URPp8PiqIglUr1qRVVrcG6Q3Jt4ioBs76KRCJ9YulHEH3FzJkzsf322+Pkk0/O+zwUDAbx1FNP4d///jfuvfdePPPMM3j1X/+Bf+8j8KMJdQilTFz5nyZ8siGB3x8xBgeO9WFd2MCFc9Zh0ea2gPcTJ9fjlkNH4/SXv8Kth26HkX4F85viuOCf67Al0SYmFlozPXfceEwc4sZ+Dy9HMtv5td6vSbjpkFE4cqwCRZHxxRdf4IEHHsDq1asBADNmzMC0adPw3HPP4Ze//CW8Xi8+/vhj3HnnnSVZVRIEQQx2qCOCIIiaIB6PIxgM1lSYKEEQ3UMxYhi36LE225Fx09t8f7trOxJZj8a1b3PbERNALBbjM9uTyWS3x2maJiKRCGRZhsfjydtmKTOk2brJZDJPlGAe9rmiRK2IB5VClmVIktRjwaW3sy8Mw0AqlYLX6y06W95xHFiWVdIs8c5ECkmSeJ5Fd62hUqkUMpkMdF2H3++HYRiIx+ODfgZ7Z1iWhVQqhVQqBUEQ2gXQFwZeu1yukromKkXz2IOwaeL/Vce+TpRgCxrWT5mB7IqX0JhjX6eqKnw+HxzHoaJ4EQzDgN/vhyAIMBS9aFi4lE3BFdsId7QJitH151S32w23241YLEb7mxhU+Hw+TJ06FY888ki7zwihUAj//e9/MX36dNx7770AgGOP/hlu+qAVd36wGedMHYoHfzwOH2+I46nF23DDWxtww/RReODHY7H/7M/5dtyKiPP3GYZzXlsL23Hw0JHjcOMho3H2q2vbjSfokjB9ez9ufmdjlyIEADz20+2RMm2cf9sDwIZlOPLII3HXXXfh5JNP5hNgRo4ciWnTpuGqq66Cz+fDDTfcgJNOOgmPPPJI93ccQRDEIIGECIIgagLbtpFIJODz+fosDJYgiMohAAhsXYrA1qU5QayTYGr+thUKg1ghcKFCzkTh37q0wyBWNgtf13VYltXjmeKmaSIajUKSJOi6Dp/PB4/HU7IgAeSLEqIoclHC7/dXRJSotbBqTdNg23aPrtU9tWbqLvF4HKqqwu/392gGdKnWUF11WZRiDZXNZrmXfTabRTqdrrg11EAj16IJaBPPWLcEOy8FQUA6nYYsy1XLB2BwEQKoToZOznY3TfwpgDa7PK/XC7fbzbvI6FhpT0RtwIbtpmPrpB1hqh2Ehee9R3UeFq5pGrxeLxKJRK/bfhFEXzN69GiIooh169YVvX3dunXw+/2oq6sDALy7ZA2e+DQDiBJmfbAJv9yzEQs3J/HKF2EAwB8+2ow3TpmIobqMrd90PKiSiEveWI+14bbr++wFzbj0gBFFH2+HoAZRELCqNf9c/PLC3aDJbe+5j37ajBvf3oh9R+vYc4SOnX6/EPXNJoZu2ICHHnoI06ZNw/e+9z289tprANre12fOnMk7IP7zn/9gzz33JCGCIAiiBEiIIAiiZkin09A0DT6fjyyaCGIA4Y5vagv9W/4iTNWLJJttKrvhiBIE24Jkts029USbIJcw25R1Ifh8vh7lFORiWRYXJDweD7xeLzweD59hXSpsNnsqlcoTJXy+tgJXNpvlYdflXudq5bqoqmqPBSD2XARB6PXnFY1GUVdXxwWnalJOl0VnooXjOLzTQlXVdo9RiwHctQTLekkmkxAEAT6fjwsTLpcrL/A6m81W9JgMD9vtWxGil9g08acIShZc0S87tCIbzBSGhQuOBUfI6doTJXR0BJiaD62j9kXrdvu3CwtXFAU+nw/pdLrq1xaCqGW6mmjALA6/2BSCADccgAsNy7d++5mLLRviUfjvCcPiIgQAbElk0aiXV9r6/hMrIAoC/vSTcVCltrFMGuqGropY+es9INi7QXQuBdD2mWfkyJHfPt6WLXmfC7dt28aFFYIgCKJzSIggCKKmiMViqK+vJ4smghigyEYc/pYV8Les6PG2YrEYJElCIBBAKBSqWJHVsizEYjEkk0l4PB7ous4L1uX6/xaKEqqq8tmyXq+XixK9aQ/TUyRJgizLPb5G96UQwQrSHo8HhmFUfTZ8qZQiWoiiCF3X4XK5eHcEW15KlwWJFm37QJZlpNNpxONxLu7kBl5ns1kuTPTEDiur+tC063HVsWPqDMfGF9v/COKHyyCSCJFHsbDwPBGiFIqEhY/94u/we2Rks9keZxgRRG8gCAJ/j2C/F/5d7nrxeBy2bWPixIlYvnx5u/vstNNOiMfjkCSpTYwQFbT10X6Laed2I7UhCsVvBwDHAcQOhI81oQxsx8F36l0AInz5ukibkJEyv32v0xUJW+JZ/N9Tn6Nu4wIMXfNfflvuZ57CzwyO47TLjiIIgiCKQ0IEQRA1hW3biMfjZNFEEERJRCIRBINBBAIBhMPhiha0mSCRSCTyBAkmLJT7WLZtI51OI51OQxAE3inh9XoBtH2xZZ0ShUXgWrJmqoQtE9D33R3JZJJ754dCoT4dSznYts1nuDNBK5VKIZlMFt2nXVlD5WZZdGQN1dVPX7+W5aKqKiRJ4iJONptFNptFIpHIEwx1XYfX6+008LozHABNuxzTFkzdmyIEAAgiLFHB+p1+hrGLHkftXEH6lrywcADoafHwG0Ei1rgzPm+4HPZX/4Ty1bwejpIYTJRa+M/9vVLrlYrjOPw6X3i9L7RZTCQSWLx4MX7wgx9gzpw5edfMQCCAadOm4d133/12O44FQClrPOUQSlt4+6sYfrVnIx5e0NxpTsTiLUkM9SowbQdfb2mFuXFj1cZFEAQxWCEhgiCImiOdTsPlcsHn86G1tbWvh0MQRA3Dwlfr6urg9/sRiUS6vlOZMIGUzaD3eDxwu93dFiTYuHNFicLCZ26nBJuNXSvFXk3TehxSDeR3RPQVsVgMwWAQXq+333XhZbNZhEIhuN1u6LoOTdOQSCTaWWb1NIA7V8jI7bQofIz+1GXhcrm4VVMhuYIhAN4poWkaPB5PXvZEV51M0aGT2mbd9xWihOiwyYgOnYTA1qV9N44aodph4ZYgYPmOR2OEJeWFhRP9g94o/FdaECj8nf2dKxB3JCJ0NJZcYZr9FOuuy902ezz2+wMPPIBZs2bhqquuwhNPPIFNmzZhzJgxOOOMM9DS0oL7778f0WgUtm3DyiThwFXWfiiXy/69HnN+sRP+O2Mibn9/E5Y3p2A7DvYYoWPHehc+29xmo/b22hg+2ZDAX46ZgD+klyEWHYYhQ4Zgv/32w3vvvYcvv/yyquMkCIIYDJAQQRBETcKKQ7quI5FI9PVwCIKoYVi2QyAQqGpBOVeQcLvdXJBgXuDdFQocx+GdEMVECdM0e2QNU0lYMboS1+VaECIsy+r3XXipVAqZTAZerxd+vx+GYSAej3frmClHLOhpAHdHnRXVFi3YOVbqdSI38FqSpDx7NUEQ8iycCoWN5nHT26x/+tKyw7bQPO7gQS9E9FVYOFE+3S3o93S9cigs/HckCnQmFpQqKuT+njvuXKE49/9c8bjw9s6eS66YYFlWu+WF/xeOK5dYLIazzjoLp556Kq666io+ueyDDz7AE088gWg0yteV01FAbCxr/5fL2rCB6Y99jl/vPxzXHTwSI30KDNPBF9vSuP/jLXj002a+7gnPr8I1B43ELaf9CMELj0NraysWL17crzonCYIgahlhypQptTG9jiAIogA2yzMcDteMfzdBELUL66SKx+NlZzl0B0EQuBgBoFN7nO7Cip6qqkIUxTz7pr4QJ9h1uaWlpcfbEkURDQ0NCIfDfS4ABAIBSJKEUChUM50n3UFRFHi9XkiShFQqVRNCfkfWUI888ghee+01/POf/+ywy+L555/HzJkz8dFHH1XMGoodw9u2bcu734wZMzBt2jScccYZect+8pOfoL6+Htdeey0++OCDvGU33ngjFixYwM9P27a5KBFRG/Dl/pd0OZ6XT9oRS7ekcM1/m0p+Dt1hxw9mwR3fXLXt33PPPVi1ahXuv//+Dtc5/PDDcf755+PII4+s2jiKER62G9ZPmdGrjwkAYxY9gboti3v9cStFNWb/l3JbOZQqCHR0W3fWqwTFhIOOlnVXTCgmHJQjJvQWWdWLz6ff2KdjKMYuc2+AbPSvTkmCIIj+AHVEEARRs6RSKWia1u/8uwmC6BvS6TQkSYKu67AsqyL2QZ3hOA4SiQTvkGA/zLKpEjO6WVGTFU9N0+S/W5bFRYneEmsrZcsE1EZHBCPXoqk/h8wyuyZmIaZpGuLxeLdfs0AggNNOOw377bcfgsEg4vE4Vq9ejb/85S9YurRthv3cuXN5kb4YHVlDOY6DVCqV9/5eKFYA4DNzJUnKy7Mo3FYxgWLIkCF44okn+HrJZBItLS1YsmQJnnnmGWzYsIHf9re//Q3/+Mc/+N9jxozBqaeeimuvvRbLly9HPB4vuoyJaLIsc+HQ5XJhw3bTIThWlyHIM/6+pl3waqUJasAdt8/ELkN1+P1+hMNhfPDBB5g9ezaSyWRFHuP666/Puw4988wzeOGFF/Diiy/2aLvDhg3Ds88+2275f/7zH9x666389l/96ldYvXp13jq777477r33Xoy/4yPAyrdjWnjOrnjok6340/+asetQN96csRNO+8dX+Neqb+39jtypDg8dOQ6HPr4CK1rKDPx2bDTtehz00BooPSxm9rZVUHe6A9j/nRXx2XtipcSCvobEhMqgZhOQjRhM1dfXQ+HImSiJEARBEFWChAiCIGoasmgiCKIcEokEJEmCz+dDOBzula4Bx3GQTCaRSqXyBAlm2VQJQUIQBDiOw4vkiqLwgqfH4+GiRLlhuuXAgo1zLRV6Qi0JEcx2i1kbFeYs9DeSySQPsw4EAt22a7rxxhuhKApmzpyJTZs2IRgMYs8994Tf76/KuItZMqVSqXbHXEddFmw5s4bSdR0AcNNNN+Hrr7+GpmkYM2YMfvjDH+KRRx7B7373OyxatIg/biqVgiRJsG0bI0eOBIA8gaXYMgbLnEgmkxBFEVsn7dilCAEA4XT1r1E2JLz2VQZP/f5WRCIRjBo1ChdddBH8fj9uvvnmijxGtQW8Sy65BF999RX/uxxxzZZUoJPL8LKtKdz5wWbc/YMxmD97OUJpC0M8Mu48fDvc/t6m8kUIABBE2JKKzZOPx04rnoXYA7GgVEot6HeWH9CdjoH+TDliQuGyYgxWMaE7MOFW0zQMi6zChobdePh7n2Jb8A9yKzuCIIhqQkIEQRA1jWVZPCC2N2f9EgTRf4lGo6irq0MgEEA4HO61gNxcQYIJBC6Xq2KCRG6RIpvNIpvNIh6Pc1GChemybpBKZx6oqgrHcSraaeI4Tk0IEQCQyWR48T6bzdZMsHJ3sW0b0WgUqqrC6/UiGAwimUyWPANe13XsvvvuuPjii/HZZ58BALZs2YIVK1bwdZ555hkA4MXszZs348QTT8TIkSNx7rnnYuedd4bb7ca6devw8MMP49NPP817DI/Hg2uvvRb7778/4vE4nn76abz00ksdjqmxsRHnnnsu9t57b9i2jSVLluC+++7Dli1biq6vKAoAYMOGDVi3bh08Hg82b96M9957D7fccgsuvPBCnHvuuQCA448/HlOnTsXll1+OY489FsceeyyAto4PAHj66adx0kkn5S077LDDcOedd7azJLrhd7dgg29HnP/PdQCAX+4xBGdPHYpRfhXRjIWPvo7jtJfaiuqF1kwBTcJt3x+Nw78TgCqJ+HB9DFe92YQ1oTZx7MTJ9bjl0NE4/eWvcOuh22GkX8H8pjgu+Oc6bEkU/4wUyVh4ZEkcu6zdCNmIY8uWLXj55Zdx/PHHd7ivf/vb36K1tRV/+MMfAADnnXcejjnmGJxyyin4+uuvIcsyXnnlFVx77bX49NNP86yZ7rnnHgwfPhznn38+zj//fADA9OnT+banTp2K8847D0OHDsWSJUtw++23o7W1tcOxAEAkEkEoFMor1EuSBElqK1yyjhR2myAIyDROaLuzKAHoXPC5Z95m/OA7Adzx/7bDGa+sxd0/GIM1rRn88ePix1ZJiBJCQ3ZBbNhkNLS2nTe5BeeuBIFyxYLBSm+JCZZlkZjQQ2RZ5pMomN1kIpGAd9VcoHGPvh5eG6KEhvXFO/wIgiCInkNCBEEQNU8ymYSqqmTRRBBEyTAxgtmQ9CbMcia3Q8LlciGTySCZTFa8S4OJEsC3X/I1TYPb7YZt27xToqcCgqZpyGazFS201JIQAQDxeBzBYBA+nw+RSKTrO/QDDMNAa2srt2tyuVwl2TWxzJMDDjgAy5cvLypqnX322XjppZcwc+ZMfPzxx1y8cbvdmD9/PmbPno1sNov/9//+H2699Vaccsop2Lp1K7//8ccfj6eeegqPP/44pk6divPPPx9ff/01FixY0O6xJEnCHXfcgeXLl+PCCy+EZVk4+eSTcccdd+D000/vdKKCZVkwTROKonAx5rnnnsPNN9+MxsZGrFixAolEAqZpIhQK4cknn8T69etxySWX4OSTT4Yoinw/nn/++TxHor6+HoqiwO12o76+/tuODk3njz1luAe3fX87nPPqWny8IYGgW8J3R3s7HOv9Px6LHYIafv7CasQMGzccPBLPHjse+89eDvMbbcytiDh/n2E457W1sB0HDx05DjceMhpnv7q209c06R8Nf8sKNDQ04MADD+QCUzE+++yzvCyH3XffHeFwGFOmTMHXX3+NiRMnQpZlfP7555AkiRd2FUXBzTffjAcffBCvv/46/vWvf7WN2e3m16YTTzwR9957LxzHwW9+8xtceOGFuOeeewC07xIIBoP8/8bG9oG2dXV1AACfz4dAIJB3W2j47p3uj1xsBzjvn+vw1qkT8acjx2H69n4c/Ojn6LFrlm3hqyF7Q1rzUQ83NLAhMWHgIooiFx9kWYZt20in03kTzLTUBnhCa5EMjAE6eE17BduCJ7K+qnk6BEEQgx0SIgiC6BcwiyaPx1MxP2OCIAYubDY4EyMqZSdULkyQYB0SwWCwW4JEqcV6Zg+TSCS4KKGqKhclWKdEuaKEIAhQFAXxeGU9k2tNiGD2V3V1dTzvY6BQaNeUyWQQj8c77PywbRu33347LrnkEvzkJz/BypUr8dlnn+Gtt97CmjVrAICLNfF4PG+iwOrVq/P8+h977DEceOCB2H///fM6HpYuXcq7KpqamjBp0iQcc8wxRYWI6dOnQxRFzJo1iy+7/fbb8eqrr2LKlCn43//+1+nz1zQNQFuWDACsX78eADB8+PC8Lg/TNBGNRvnz2XfffXnOwebNm/nzY3ZQu+66K+LxODKZDF9myy7gm2LmaL+KpGHj36sjiBs2mqLAki3Fj6sdghp+uGMdfvDXL/DJhjY7yrNeWYvF503GETvWYWKjGz+f3ABVEnHJG+uxNmzg8mkjMNKnYqchbryyIoQ5KyO4fNoInLbHEAzVFZz84mrMWRkBHAs3X3YBpu+2A1wuF+bPn48HHngAXq+3qFXQ6tWrMXbsWIwbNw6XX345dthhBzz33HPYd9998dFHH2H//ffH6tWr4fW2iSrsesOEAVbcNU0TjuPA4/FAURQoioI///nPfF++/vrrOO644zrMD2Dn4KxZs/LOx9/85jdYtWoV74KIRCI8gNxxHKS8IzDU3V646Iwvt6Xxp/9txcXfHY7fzt2A1aEKWLSJEpLB7XHptb9FQJNw3XXX9XybFaLU8PCucmAYA01MqFTOyWBEEAQuPLJuykwmg0Qi0eHnj8a1c7Fuj9N6eaQFiBIa177dt2MgCIIY4JAQQRBEv6DQoqk3fN8JgujfsIKi3+/v85yZdDqNdDrNBYn6+nouSJRqOVduYSVXlJAkiRcFXC4XLwqUKkqwYl+lA8BrTYgA2jpMkskkdF2HYRgD6v2m0K6pvr6+U7umd999F/PmzcNuu+2GXXbZBfvssw9OOOEEzJo1C2+88UaHj+NyuXDqqadiv/32Q0NDAyRJgqqqGDZsWN56y5cvb/f30UcfXXSb48ePx6hRozBnzpy85aqq4he/+AUuvfTSdoHazc3NAIDZs2fjtttuw/z583nBmx133SlYOk5+ALdlWXnXF9MBILRt9+21UXwdNbDg7F3x1poo/rsmin9+GUbKbLtdkwScPXUozp46lG/73h+OwQfr4njof1uxJpTBqtY0Jgxx4f75W9CazOL6g0dhbdjAhAYXrpg2AvfO24QL9xuON9dE+bKTX1yN/21M8AwKwQHuenU+Xn/yIYwYMQI///nPcdZZZ+FPf/pTu/wAAFizZg1isRgmTJiAV199FT/96U/x/vvv43e/+x2i0SgmTJiAhQsXIhwOw3EcmKaJdDrNxQCWu8JeA6BNsEqlUnnCT1NTEwKBADweT9FQ6vfeew8AcOutt2LZsmV8eXNzM7/GAe3zRbaN2R9DS7RXe/mkHTFtTH5Q7m+nj8Jvp48CALy/Pob/e3plSdsqim3h2jlfYNQXr3R/G1Vg7ty5mD9/Pv97xowZmDZtGs4+++x2IoGmadB1vSwxYfTo0TjmmGOw6667wuPxoLm5Ge+99x6ef/55ZDKZiokJzCqtIx5//PG80PpSOfvss7lwSZRGbu4D0PZ+Go1GYRhGl6+nf+tS+LYsRaxx577JirAt+JuXUz4EQRBElSEhgiCIfkMymYSmaTyEliAIoisMw2jzH/Z6YZpmn4cQM0GC5TnkdkhUMwOHibnJZJKLEqqqts2Ml9xoVYcgog2BIahwBBGCY0HKpuCKbYQ72gS/1ublXOnchFoUIoC20HNVVeH3+wekJWA5dk3ZbBYLFizAggUL8Ne//hWXXnopTj311E6FiHPOOQd77bUXHnroIWzYsAGZTAY33ngjZLn7Xz3cbje+/PLLduHK1113HTRNKxqonVsEl2U5b0b92LFjAQCbNm3q9pgY7BhmhVlFlnk4ctywMf2xzzFtjA8Hb+/HlQeOxOXTRuCwJ75ANPOtyPWzZ1ZihFfB748Yi1ve2Ygz9x6Kd365M37+wredJYmsjUTWhvmNX9C4ujaBcMHGJERBgGE5fNmcle2txbbG0vB9/TXWr1+PSCSCmTNn4umnn0Zra2te8Zf9LFmyBDvvvDM/Bj7//HPIsowRI0Zgl112wbPPPsstu3ILyp1RKOw5jpNXxC4MpQ4EAjjwwAOxZs0abNy4sfMXIofo0MmIZdv2k1+T8vY10JbFEftm2Yy/r4EqCbjheyPx3TFejK1zIWPauGHuBry8IgTD6uHselHCRv8E1PWSGF5OZ4IgCKivr4cgCNB1HbIso76+vt02VVXlM9tL6UyYOHEibrnlFixYsABXXnklQqEQJk6ciHPOOQe77rorfv3rX1fsPe+oo47ivx9yyCE49dRTccopp/BlhZ1toiiW9F7WG/Z8siz3+/w7WZbhcrmgaVpe7gMTm0pFADB6+Qv4YtoVsAUNEHrRosmxIVoGRi1/AbX3iYQgCGJgQUIEQRD9CmaZQRZNBEGUSiqVgiRJ8Pl8sG27ogHO3YV1I+QKEoZhIJlMFh2fIAgV8622LAvbxAC2jdwf0aGTYWrfzAR2bAj8MRw4EPisRCUbR7B5OQLmu3DHe164ZdSqEAG05YwEg8E+76apJslkEplMpmS7JgBYt24dpk2bxv/OZrM8MJgxadIkvPHGG3j//fcBtHVIDB8+vN22dt5553Z/r1u3rujjrly5EtOnT0c4HObv/7quY+LEiR0Gaud2YFx22WW47LLLsHnzZpx00kk44YQTkEqlMHPmTLhcLl44K8Tr9eL666/Hd7/7XS7UeL1ebsME5OcXDBkyBPvsOBqSLOEHOwbxUVMcV73ZhHfWxfDOuhhmfbAJay7eHQeN9eG1L8P8cVpTJpqiBhRJQHPSxM+eWYl/nLgj7vvRWNS7ZXzRksbl00bg55MbAACXTxuBK6aNAAD89ejx7ZZtu3JPAEDDzE/bArE3J3D/Ex/CNE0IgoCTTz6ZvzaqquIHP/gBjjzySDQ0NCCZTGLFihX4/PPPceihh6KxsRFLlizBkCFDsGLFCpx++unQNA033HADFEXB0qVL4Xa7eafNIYccgqFDh2L8+PF44okn0NjYiGXLluHjjz8G0FYI7mjGOwulZrBurNmzZ+PHP/4xEokEtxS66aabcNFFFwEALr74Ytxwww1obW1FVvXC1HzYZ5QOx3HwyVm7YF3YwJ//txWPLmzB2ICKgEvGqta21zuctnDwOB+O3rUBp/5jDZ46Zjxmf9qMK6aNwKaYgSeOGo/t7/mMixmThrrxzi93xpQHl+LriFFSgPi9R03G6O/fhuuvuQoAcM8992DNmjUwDANHHHEETNPEK6+8wmfuC4KAsWPH4pJLLsGECROwadMmPPTQQ7jttttw8803Y/78+e0Ehr333hsXXnghfvnLX8JxHIwdOxazZs3CSy+9hKeeegqO4+Dss8+Goii45557cPDBB+NXv/oVjj/+eBx66KE8oP25554DANxxxx0840MURfz617/G1KlT0dLSggcffBAffvhhu9ePcemll2LdunW4/vrr+eu8ZcsWNDU14c9//jOOOeYY3gEzd+5czJo1C/vtt1+H2x83bhzOPvts7LbbbkilUvjf//6H+++/P89GDQC3D2TLdt99d9x777244oorcPrpp2P77bfHZZddhubmZpx77rnYeeed4Xa7sW7dOjz88MP49NNP+bYKrZl6Ok72un/11VewLAvf//73sWbNGvzmN7/pcD/WKqIocvFBlmVYlsUnWvSki1AxYhi97DmsnzKjgqMtAUHE6GXPQTEqaz9JEARBtKcPk4AIgiDKxzRNpFIpeDyedoUPgiCIjojH48hms/D7/TV17chkMgiFQohEIhBFEXV1dQgEAlAUpeKP5QCIDJ2EVftegJUHXIrWUft+K0IAgCDCEaVvfuQ8a4Ss4sXW4Xth5QGXYtU+FyAydBIqFedZq0IEs9txu91VeT1qBcuyEIlEEIlE+Gxoj8cDv9+Pu+66C4cddhh22GEHDB8+HN/73vdwwgkn5HnFb968GXvuuSeCwSDPC2hqasKBBx6I8ePHY/z48bj22muLvs6TJk3CCSecgNGjR+OnP/0pDj74YPz9738vOs4333wTkUgEN998MyZPnozhw4djwoQJyGazOOyww7p8jf785z/jvPPOw+OPP467774b48ePx+uvv45rr70WF198MTZv3owRI0ZgwoQJGDJkCA8+Pu6447Bx40ZcccUVvJC+11578X0HADvssAMmTpyIYDCI22+/HZIk4M3VERzx5JfwqRL+fcpOmDLcjdF+FcdPqocoACtb21u+rAllMOfLMO79wRjsM1rH6yvDGO1X0ZrM4vWV4bx175+/Bef/cy0A4PzX1rZbtvN9i7HzfYsBAEGXjJ2GuLFDfZu9zq677ortt98eoVAIK1euRGNjI04//XTMnj0bp5xyCi6//HL873//w7x58zB69GhomobNmzcjGo1i4cKFOOCAA5DNZvG73/0Ol156KQRBwLhx4yCKImRZ5q/F/vvvjyeffBJ33HEHhg8fjsMOOwyCIKChoQFDhgxBY2Mj/H4/gPah036/Hz6fD7r+bfC32+3mgbeapuH444/Hgw8+CKBNdLrsssswfvx4bLfbfrh4v2H49f7DMferKJoTWbz2RRjXfG8krjloJP70k3H4ZEMcH3+Tw+FTRfz+h2Pxx/lbsKy5bfb880tb8UVLGuftm28n1hG5AeJHPvUlRvtV3HjI6Lx1bNXD7fkkScLhhx8O27ZxxRVX4Mknn8Qpp5yC6dOno7GxEY2Njbj55pthWRauueYazJ49G7/85S8BtM1Al2UZgiBwcT2dTmPBggVwuVxobGxEOBzGuHHjEA6HMWHCBLS0tGDbtm2YOHEiPv74Y0QiEW47lEwm8cYbb+Bvf/sbvvrqKxx11FE46qij8N///peLCDNmzMDbb7+N008/HfPnz8c111wDny/fzorxne98B+PGjcPzzz/fTmxavXo1FixYgEMPPTRveWfb13Udd999N1atWoWzzjoLV1xxBYLBIG644YaSXhsAOPPMM/HnP/8Zp556KtasWQO324358+fjkksuwRlnnIGPP/4Yt956K4YOHdrpdioxzsMPPxymaeKCCy7gIe39AUEQ4HK5UFdXh4aGBng8HpimiXA4jNbWViQSiYpYGdZtWYwRK17q+YDLYMSKl1C3ZXGvPiZBEMRghToiCILodzDLDLJoIgiiHHLDq5mvea1gGAYMw4CqqvB4PKirq+NZBZXIZciqPjTt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5z//GY8++iimT5+Oe+65B+FwGJdddhneeustXHvttSgpKQHQcO3Onz8f0WgUV111Fa688krEYjHMnz/f6DYCgIqKCvTu3RvXXHMN7r33XowfP96wj7v99ttRW1uLp556CpMnT8bkyZMBAFdffTUEQcDVV1+NGTNm4PHHH2+xu4og9jeSySSi0SisVmvaNUUQBNFZ0J2IIAiiC5Lq66kXCgiCOPDojKJ8IBCA2+2Gy+UqWCt/IpGAxWJpcbkDQYjQLbA6ugtC58wzz0RdXR0efPBBAA3FzJKSEvzpT3/Cs88+i1AoBJZlceyxx2L58uXgOA6iKEJRFDz00ENp65o4cSI++ugjqKqKyspK7NmzByeffDJWrFhhWCSlCgLhcBhffvklTj31VKxfvx42mw1jxoxBKBTCV199hUgkgh49euDll1/Grl27AAC7d+/Oui8sy4JhGMycOTPNS//JJ5/E66+/nvE1S5YswRdffAEAePrpp/H000+jT58+2LVrFy644AKsXLnS8JnfvXs3Hn74YSxcuBALFixolWUZxzXYG2XLpxAEAY8//jiqq6sRDofxxhtv4I9//GOz69y+fTueffZZY4xnnXUWRo8ejbVr1+KII45A79698ec//9mYSf/kk0/igQceaLIeTdOQTCYhiiISiQSOP/54PProo4g7egOqArBNrZmysaU+jhGl6df4axu9eHG91/j3wxMH4MEvfsLLGxoe2xmQcM9/9uCvY3vjvs9+AgA8/lWdsfyugIS7P63BA+P74y/vVTaM6+djtnDhQtTU1AAAPvnkE/z617/G5MmTEY/HsXPnTnz77beoqKjAqlWrjOLbpEmTjOK1xWJBVVUVbrjhBmN7LV0bJpMJZ5xxBubNm4cvv/wSAHD//ffjpZdewsSJE/HKK68Ynaw+n6+JrVZz71s2Bg0ahOXLl6c99sEHH2DBggUAgDfffBPHHHMMbr75ZiSTSWzevLnJuS/LMubPn49EIoHKykosXrwYl1xyCZ566inwPI+pU6fi+uuvNzqD9uzZg5EjR+K3v/0t1q1bZ6xn8eLFzY4VaOgSevjhh6FpGnbt2oXzzz8fJpMJL7zwAgDgxRdfxAUXXICRI0di1apVGDduHFiWxX333WesY968eXj77bdRUVGBr776CkDDveOhhx6CqqrYtWsXVq9ejdGjR+Odd95BKBSCqqpGZ5ROaWkpPv30U8Mqbc+ePc2OnSD2V6LRKERRhNPphM/no9+VBEF0KiREEARBdFHC4TCKiorgcDjIookgDnA68kejqqpGK7/D4Uizn2ktek4Ez/PNBgbv70KExWKBzWaDqqrw+/0FzeLIlf79++P7779Pe2zDhg2wWq3o0aMHamtrkUwm8f333+OBBx6AIAg499xzoSgKPv30U8iiHTFnXwilg3DCSSfjgvuXYM+Q34CTY1j2v3U4fdJvsWLFCqMDprGl0cqVK3H99dfjueeeQzKZxPHHH48PP/zQsG969dVXcf311+PXv/411q5di08++cQoOOuk5kgwDIOlS5caVjhAg5iWjW3bthn/X1/fYCPk8Xiwa9cuDB48GAcddBB+9atfpb2G4zj06tULVVVVuR5mA57noWla1o6IWCyG2tpaw/qnvr4ebre72XU2no3u9Xrh8XgANLy/tbW1aQXZTZuyhzzLsgyz2Yz+/fujpKQEa9euhTLoNDDQ8gqfbgjcTn/s2z3pXSkjSi04uo8d16R0QHAMA4vAwsIziCU1nDzAgT8fW4ZDis1wiBw4tuF5K89A4S0AkojFYmnnhM/nw969e9PONZ/PZxxHXQxauXIlnn/+eQBAUVERpk6divvuuw+zZ89GLBZr8dqw2+0QBAEbNmwwnlcUBZs2bcKAAQNaPEbNvW/Z2LVrF2655Za0xxp3+8yfPx/PPfccVFXF9OnTm6xj27ZtaTkpGzduhNVqRWlpKSwWCywWC+6///601/A8j61bt6Y9tnnz5mbHCgCVlZVpn1c+n88QAoCGz5dAIGDs9+DBg9GnT58mYosoiujdu3faelNzVOrr6zFo0KBmx/L666/jmmuuwVFHHYW1a9fi008/zdrJQRD7O8FgEEVFRbDZbMbnLUEQRGdAQgRBEEQXRQ8PdblcOXmrEwRBFIpkMolgMAin0wlFUdrsK5xMJqGqKkwm0wEpRHAcB4fDAZ7njS6Irkwy2VDsDYVC8Pl8uOPR5/D0Px5CxTWP4dmNDWO/uKIIZlHAkhsvMF7HAOBYBvHJ81C3fR16/vQlVHWf8bzZbMamTZvAsixGjx6Nr7/+GsOHD0/rtHjmmWfw4YcfYsyYMTj66KMxbdo03HXXXfjvf/9rrMNms4FhGESjUaiqivr6+iZiRXP7pqMXTPVzzmKxYNmyZUZHRCq1tbU5Hr30dXMcZwhOmWwxFEX5uYivGa/TraJy2Qf9Na29biRJgtVqxQknnICvvvoKsixnDKluiSHFZuwMpH9Picrp6dQ2gcO8/+7Bss3+Jq+PJzX0c4l48XeDsfibOtz9aQ18MQXH9LPj4YkDIHAMNJYDkGwi6uidHY0f048jx3FG8Ll+ntTU1GD+/Pl4/fXXMW7cuCaF8PagNe9bMpls8dwePHgwzGYzVFVFcXGxkS+SC3qn2k033YS6urq05xoLpbnYGmXax0z3/NRr7scff8TcuXObLJMqKDb3/mZj+fLlWLNmDcaMGYMjjzwSF1xwAf7xj3/g3//+d4v7QRD7G6qqIhwOGxaG9LuSIIjOgoQIgiCILowkSUgkEnA4HPB6vdRKSxAHGJ1ZlJckCZFIBHa7HYqipM2obQ2JRAKiKDZbhM+luNTdsFqtsFqtUBQFfr+/WSGmI6iqqsJJJ52U9lh5eTkikUhaITKZTMJbPAw7hh6NsLMf7l/jx12/OghLftyAeFLD1IpSLFq9Fy+tr09b133j++H8I/vjrjCDPWVHwNq/Er13/xd9YjvBsSxkWcYXX3yBMWPGwOl0YteuXdiyZUvaOqqrq7F06VIsXboUt956KyZMmIDVq1fDZrNBEATE43FEIpG0GdKFYMuWLRgwYEDOokZz6LZmPXr0MGymDj744IzL6mHRhaCqqgqlpaXweDxGV8TQoUOzLi/LMjRNw/HHH49ly5ZBFEUIXH7X4IkD7BhRasFja/Y2u9x3e6M4uMiEHf7M95KKnlawDHDbh7uNboyzhv3SMcCozWeFZEMXIhqjH3O9e6elayMYDEKSJJSXlxtByhzHYejQoVi6dCmAXwrmehdGe+NwODBnzhw8//zzKC4uxi233II//elPaUXGwYMHp01oGT58OKLRKGpra419Ki0tTbNh6ii2bNmCcePGwe/3t0nwlmU542dHXV0d3n77bbz99tuYOXMmfvOb35AQQRywxONxiKIIh8MBn89X8M9QgiCIXNi/fukRBEHsh+i2KHa7vZNHQhBEZ9FZImQsFkMsFoPD4WgSYJwvkiSB5/lmC3T7U0eEHqBstVoRi8Xg8/k6VISw2WwYPHhw2l+PHj3w5ptvokePHrjqqqvQr18/HH/88Zg2bRpeffVV4zxTWQERZz+sH3wOwo6GoOA3NwegqhpmjO6B8lILKnpa8fy6fdi0L5729/pGH34/shjsz/kCUXd/bC3/A9YPPgeBJAeO4/D+++/jmGOOwemnn46VK1caYxZFEVdddRVGjRqFsrIylJeXY+jQofjpp58Mmx2fz2d4wheal156CSNGjMBVV11lWMYcf/zxrQqr3r17N/bu3YsLLrgApaWlGDNmDM4777ysyxfqGl+7di1qampw44034qCDDkJ5eTkuvvhiAA3CpiAIRleJ0+mEx+OB0+nEkCFDsHnzZrhcLlhZBRqT+WeiyDEotfHoZRdwWJkF1xxbhucnD8Z7WwNG9kM27vtsD35fXoy/HN8Th5aYMaTYjLOHeXDzib0AANt9CYgci1lH9sAAl4jzRhRhWkVDqLHGsLDxmhGGLghCzvcK/Z5jNpvh8Xjg8XgwePBgXHPNNUgkEkYOQUvXRjwex1tvvYXZs2fjqKOOwoABA3D99dfDZDIZHRV79+6Fqqo49thj4XK50sLbWwPHccaYU/90rr32WtTV1eG5557Do48+CpZlm4SdC4KAG264AQMGDMAxxxyDadOm4d///jc0TUMsFsMrr7yCyy+/HOPHj0fv3r1xyCGH4Oyzz8b48ePbNPZcWLlyJQKBAObOnYuRI0eiZ8+eGDVqFK688koj0DoXfvrpJ4waNQolJSVwOp0AgMsvvxxHHXUUevbsiUMOOQSHH354q+zVCGJ/IhQKQdM0OByOzh4KQRAHKNQRQRAE0cXRNA3hcBhOpxOJRIJaaQmC6FDC4TA4jjNCDltbAJYkCZqmGSHNmdhfhIiu0AVx+OGH48knn0x77J133sH999+Pm266CbNnz8aTTz6JUCiE5cuX47nnngMA+MsOQ7CsHE5RBLAP+LkgrWjAk1/X4cpjynBwkRmb6mLY4m06s/2dH/2Y9+t++PVgF97bGjBe73UfgsARV+GgLW9g35dfIhgMon///vjwww+N16qqCqfTiZtuugkejwehUAhffvklXn31VQSDwTZ35bTE9u3b8ec//xkzZszAQw89BIZhUFNTg1WrVuW9LkVRMHfuXFx//fVYtGgRNm3ahH/961+44447Mi5fCGGFYRiwLIs777wT11xzDR577DHs3bsXzz77LEaMGAGz2Qy3293kOjvyyCOxdetW7Nu3D8lkEsy+SqDP2Izb+NVgF3648jDIigZ/PInva2O4aeUuvLTe22KmxKodIUxZuhV/Ob4XrhrTE0lFwxZvHM+ta7Dv+r42hls+rMbVx5ThtpP74PNdIdz1SQ3+8duBAMPCGt4DjhsEhmEMYUpVVZjNZrAsC4vFgmSyqXWT3hExadIkTJo0CUCDX/r27dtx4403Gh0r+/bta/baAIAnnngCLMvi5ptvhtVqxebNm3HDDTcYnuv79u3D008/jVmzZuGGG27A+++/j3nz5uX2BmZg0KBBTcKnJUnC+PHjcdppp+GYY47BrFmzoKoq4vE4/va3v+Ghhx7C559/bgRqf/3116iursaDDz4IQRDw0Ucf4ZlnnjHW99RTTyEQCOCCCy5Ar169EA6HsWXLFiNguj1JJBK4+uqrMXv2bNx5552wWq2oq6vDN998k1eHxOLFi3HdddfhhRdegCiKRgj21VdfjR49eiASiWDNmjV45JFH2nFvCKLro1v/ut1uWCyWnCzXCIIgCglTUVFBPh8EQRDdAKfTCZ7n4fP5yKKJIA4QBEGA2+1GfX19p7bQpxb+/H5/q+9BTqcTLMsatjWNsVqtMJvNeXmcdyV4nofD4QDHcYhGo23O1uho6gachD1DzwQ01RAQCsrP6+216Q302PmfrIuZTCbYbDawLNstj6MOz/OGRVI2MUpfxuv1Zg201tGFBo7jjL/Uf6eKC6n5F4ceeijuuusuXHrppaipqTGK9frf3LlzsX37djz77LOQZRmyaMcP4zILJp3J8FX/B15qKPjr+6x3Wen/rx8DVVWN/TObzQiHw0gkEgecFcmcOXNgt9tx2223dfZQCILoQthsNlgsFvh8vhY/ewiCIAoJdUQQBEF0E8LhMDweD2w2mzHzjiAIoiPQNA2BQMCwcUkNEc0HSZJgt9vTwnkbb6e7dkToP+qTyWS3/GFviBBA+4gQKevdM/QsAGgiRgiC0O45EB0Jz/NZw3p1dF97fT+bExpSPfBTrx/9mtE0DYqi4KijjkIkEsGuXbtQVlaGGTNmYP369di0aVPGMaxfvx5r166FKIqQZRmCFAafCCFp6jrWHXwiaIgQAAyRoXGXKMuyhjjB87xhKWe322G32w2BIlWMSSaT3fo8IwiCyJdIJAJRFI1uV4IgiI6ChAiCIIhugqqqiEQicDgcSCQSkGW5s4dEEEQ705WK8qqqIhgMwuVywW63t0oQTSQSsNvtEEUxq81OV9rnXEjtgohEIt3S5sBfdtgvIkQHsWfoWRDiAbj3fgeWZWGz2WA2myHLcofnabQXHMc1KXCzLJsmLuiFco/HA5Zl00SF1K6G1MczFdMVRUnb1uzZs1FWVoZAIIC1a9fiH//4R9Zxvvzyy3A4HGlh8j0CP+KnHhXQmI4JXW4WVYGzdkNui6pqmjhhsVhgtVrh8/nSBAqe52E2m9OOq348U/9LAgVBEPsrwWDQmOSm3/sJgiDaGxIiCIIguhHxeBwmkwkOh4MsmgjiAKKrXOuyLCMUCsHpdEJRlLyL7nqxz2QyZRQiultHRHfvggAAWXSgesR57WfHlA1NRfWI81AS3wO3oBlCV3vnQLQ3qfZJoigCaLAky2SfpBe5NU1LEyH051JFhtS/lnj//ffx/vvv5zVuSZKMwrzVaoV53zfYU3pEXutoN1gOxVWfteqlHMcZIk2mjK3UDgpdpBBF0eg+6e4CRVvyKQiC2L9RFAWRSAR2ux2SJNEkN4IgOgQSIgiCILoZoVCILJoIgug0EokEIpEIbDZbRmuUXF5vs9kyPpc6A7yriC+ZEAQBDocDLMt22y4IANAAVA8/FyondqwIAQAMC5U3oerQsyFseB6xbpQD0dgyKfXfje2TNE0Dz/PGvwFkzHKIxWJpYkNHn//6dWyxWGCxWBDZVwmrrxJRV3+A7eBzIxVVgTVQBUv4p1a9nOf5ZsWb5gSK1AyKTAJFY3EiV6GIIAiiqxCLxSCKIk1yIwiiwyAhgiAIoptBFk0EceDQVbsDotEoOI6D0+mE3+/Py0ZHz4kQRbFJAbCrCxEMwxhdEJIkwe/3d5uZ0ZkIlpYjVFbeeQNgWOzzHAqb/SC4orlZ73QEje2TGgsNjW2SdMFB/399mUzByalFa03TYLfbwfN8pwdy6zP/9Q6fWCyGHpWrsPPw6Z06LrAcelR+3OqXcxyXt1gKNLxnqqo2+Y7VWKDQO19IoCAIoruiT3JzOBwIBoOdPRyCIPZzSIggCILohqRaNHm93s4eDkEQ7UxXLMqHQqE0MSLXgrxelGtOiOiKiKJoBG2HQiHE4/HOHlKbqRs4DlDVTp/xXjdwLFw5ZgAUAoZhmu1qaGyRpP/pFj/66/X/ZsptAACHw9GiUNfVRDeGYYxClLN2Axx7NyDUYxjAdkJWhKrAWbcx53yIxug2WYUUAbIJFAzDpHVPZBMoMokUBEEQnYmqqgiFQnC5XFmtMwmCIApFJ/7qIAiCINpCKBQyAj4JgiA6g0AgAABwuVx5vS6RSBj++alksq7pbBiGgcPhgMvlgqIo8Pl8HSJCXHTRRfjnP//Z7DILFizA5Zdf3qr1x+y9EPUMbLUIUX/jaEw8JPv73s8lov7G0SgvtTS/IpZD1DMIMXvPVo0jG3oQtNlshs1mg9PphNvtRnFxMUpKSuDxeOByuWC1Wo3AaN1qTPfKVlXVyBAQRdEQHWRZRiQSQSAQgNfrxb59++D1ehEIBBCJRBCPx41zuaVuIZZlmxXxcjkPCoEoiuB5Pq2TgwHQd+NSsIrUkCHSRm44oRc+nj40t4U1FQPsDHYsuAgHDx7cqu1xXIN40hHB5/p5EY/HEQ6HEQgEUF9fj3379sHv9yMcDkOWZTAMA7PZDKfTiaKiIuNcdDqdsFqtMJlMxrgJgiA6CkmSEI/HYbfb0ywGCYIgCg3dYQiCILopukWTxWIx/KcJgti/aGtBfs6cObjrrruaPD5q1CisWrWqzUKmpmkIBAJgWRZOpzPrcmVlZVi1ahUG/1xQlCTJmDnceH1A8/udrfg+fvx4vP32263ZjayIooiioiKIoogzzjgDb7zxBu65554my/3+97/HqlWrsGDBgoJuvyVuv/12PPXUU1mfLyoqwq233opnn30WH374Ydpxq+9/HKCmz8YeVWZB/Y2jcWRva8b1/fv8g/HM2QcBAIY9/B1Wbi+QhYOqoL7/8Xm9hGVZCIIAk8mEwYMH45ZbbsGSJUvw/vvv47XXXsOCBQswadIkOJ1OmEwmMAwDRVGQSCQQj8cRj8chSRI0TTNmr5tMJgiCYCwbi8UQDAbh8/nSxIZwOIxYLAZJkrLOaG8pm0AntSNi1apVOP749OPwyiuv4Lrrrsvr2OQLwzB47rnnsGTJEhx77LFpIqEghfDFtINQf9ORmDKyqF3HkT4oFj23vNOmVejCUWd2HWQSKHTxyufzIRwOQ5KkZgUKm80Gk8lE3/UIgmhXwuEwNE1r9vscQRBEW6FvMwRBEN2YWCxmWDT5fL7OHg5BEO1AV7JtyYSiKAgGg3C5XLDZbIhEIi2+Rp9tLopi2mzlrtIRwTAM7HY7zGYzEokEwuEwkskk9u3bh4qKCpSUlGDfvn3G8qeffjp++ql1YbptIRQKNfu8IAjw+/14/vnnce6556Y9Fywd2cRuZ93eGNbvjeKCw0rwVU1V2nP9XCJOGODABUu3AQBqIwWcZc5yCJaWAxtfMx7K1T5p8ODBuO2227Br1y489thj2LVrFzRNwyGHHIJJkyZh27Zt2LVrlyEwAOk2OYlEokluQyHgOA7JZBIsyxoZEhl3vYWOCF00aU8cDgcAYO/evRg7diw+/vhjI4B92LBhKHFaEY13rFVHr01vwBn/sU3r4Hm+y2a46Jkcjbs19PM+1ebJZDLBarUar1NVtYm9U0d0fRAEsX+jaRqCwSDcbjesVmunZxcRBLF/QkIEQRBEN0cPGMu1AEgQBJGJk046CdOnT0fv3r3h9Xrx+uuv49VXXwUAnHXWWTjjjDNw8cUXAwCOP/54zJ07F3//+9/x9ttvQ5Zl3Hzzzdi+fTuefPLJJoXTl19+GQDw5JNPAgC+/fZb3H777TCZTDj33HMxadIkuFwuVFVV4ZVXXsGnn37a5v0ZNWoUZs+ejYEDB0JRFFRWVmLu3LnYu3evsQ9//OMfMXDgQOzbtw8rVqzA888/D0EQYLfbAQDBYDDNK9nv9+PHH3/E+PHj8cILLwAARowYAZfLhU8++QQDBgwwlmUYBhdeeGHavj3xxBNYs2aNsUxJSQkuueQSHHXUURAEAVVVVXjwwQfxww8/GMv8+te/xsUXXwy73Y4vv/wS999/v1EkXrBgAbZu3YpHHnkk4zHYu3cvFi1aBKBBLNGRRTuSJkfG17zwXT1uPrE3blm5C7HkL8XzKSOLsTcs48OfuyDqbxyNC1/bhuVbGuy5Rvey4oEJ/TGk2IxNdTH8/fOmwszQEjPuGNcHY/rZEZVVfLwjiFs+rIY3piBpcsJR2hd/uvD3OPHEE2GxWLB9+3Y888wz2LJli5HBoHch6KLCpZdeit27d+Omm24yQqYBYM2aNVi9enVasbaoqAizZs3CkUceCVVVsX79ejz88MPGOTFnzhzY7XasX78e5513Hniex6pVq7Bo0SJjVr0gCJgxYwZOOeUU2O12VFZW4vHHH8e6desANHTmXHHFFXj44YcxdepU9OnTB1OnToXb7cbMmTNxyCGHgOM4bNu2DY888gi8Xi80TcNLL70EAJg7dy4A4KeffsKUKVNw0UUX4YQTTsCsWbNyOq/Kysrw8ssv4/bbb8fZZ5+NYcOGYffu3fj73/+OjRs3NnlPTCYTTCYTNE3DypUr8bvf/Q69evUyciImTpyIDz/8EKeddhpKatcD6ANoKvq4TJj36344cYADmgZ8uD2IGz/YhbroLwXxq8eU4ZKjSmHlWbyxyYf6aNNi+R8OK8blR5ehv1vErkACT3xVh/defBIlVf8BysoynqO5wnFct8tgaEmgSBUpUgUKAFlDsru6mE0QRNchmUwiGo3CarVCkiQSOQmCKDhkzUQQBNHNURSFLJoIYj+lo4JshwwZgttvvx0fffQRZsyYgaeffhoXX3wxxo8fDwBYt24dBgwYYGRBjBo1Cn6/HxUVFQAaCn5Dhw7F119/Dbvdbnju61xyySUAgOuuuw6TJ0/G7bffDkmScOaZZ+J3v/sd/vGPf2DmzJlYs2YN5syZgz59+rRpf1iWxdy5c7Fu3TrMnDkTl19+OZYtW2Ycy5EjR+LGG2/E66+/jmnTpuHvf/87JkyYgBkzZsDpdEKSJHi93oyBje+++y4mTJhg/Pv000/HypUrm4TXnnPOOU327e677zb2zWw2Y+HChSgpKcEtt9yCmTNn4uWXX07rBunduzdOOOEE3HTTTbj55psxatQoXHDBBW06NgAQc/bN+tyr33sh8gzOGOpJe/z88iK8tN4LNcPpaBNYvHjuYGzeF8cpT2/CvP/uwR3j0rfhNHF4Y8ohWL83hl89vQnnLdmKHjYBT511kLHMH2f8CcceeyweeOABXH755aiqqsLNN98MlmUhyzJYloXZbIbD4YDdbkd5eTn69++PN998E7IsIxqNZs1tkGUZd999N6LRKK666ipceeWViMVimD9/ftpnZ0VFBXr37o1rrrkG9957L8aPH5/2fl911VUYMWIE7rrrLsycORMff/wx5s+fn3bOmkwmnH322Vi4cCGmT58Ov98Pq9WKFStW4Morr8Tll1+O6upq3HvvvbBarVBV1bhG7r33XkyePNn4d2NaOq90ZsyYgSVLlmDWrFnYtWsXbrvttia+3yzLwm63G5kWPp8PX331FcaOHWvYXo0dOxbLly8HAPSIVuHg718Cl0zg+ckHwW3mccYLP+Kcl7dgoFvEk2cNMtZ95lA3bjihF+Z+UoNTn9mEvWEZF4/ukbb9c4d7cNOJvXD3pzU49vH1uPujStxyXDGmDstsDZYvelfK/oAuUCQSCUQiEQSDQXi9XtTV1cHn86WJpnqnrMfjQUlJCYqKioyONbPZbGSBEARBZCIajSKZTMLpdNK9giCIgkNCBEEQxH5ALBZDMpk07BUIgiB0jj32WCxfvjztb968eWnL/O53v8PXX3+N5557DtXV1VixYgXeeOMNnH/++QCAHTt2IBQKYdSoUQAairVLliwx/j1s2DDwPI+1a9dCkiQ4nc60wFW/3w+gIdza5/MhFApBkiT89re/xdKlS7Fq1Srs2rULTzzxBCorK3H22We3aZ9tNhvsdju++OIL1NTUoKqqCitWrEBtbS2AhgDgl156CStWrMCePXuwYcMGLFmyBBMmTEAgEEAoFMoqAH3++eewWq047LDDYDabMXbsWLz77rtNljvvvPPw8ssvp+3b1q1bDYukX/3qV3C73bjtttuwYcMG1NTU4OOPP06btc4wDO69915UVlZi/fr1+OCDDzB69Og2HRuLxQK27OCs4cP+uIJ3fvTjgsOKjcdOHGDHALcJL35Xn/E15wz3gGUYXL18Jzbvi+P9bUEsWr03bZlZR/TA+r0xzP20Blu8CazfG8NVy3fixAEODPaYYOU1nHnKcXjmmWfw/fffw+/3Y/HixZBlGZMmTTIyF1JzG/TOlc2bNxu5DVarFW+//bZxrp955pkAgHHjxoFlWdx3333YsWMHqqqqMG/ePJSWlhqCGtDgkf3QQw9h165d+OKLL7B69WrjmJeWluL000/HX//6V6xfvx41NTVYsmQJ1q9fn9ZxIggC/vWvf2H9+vXYtWsXEokEvvnmG6xcuRK7du1CVVUVHnjgAZhMJgwfPtzIWtG37/P5jH83pqXzSmfJkiX44osvUF1djaeffho9e/ZsIlY4HA5omoZwOGw8tnz5cpx88skQRREnn3yycf0wDANZlmGt/goXRFdgeA8L/vTWDqzbE8baPVFctmwnTujvwOE9G0SES44sxQvr6vHCd/XY6k3gb//Zg8370jul5pzYC7d9uAvLfvTDv/VbbH3yBry25GVMmjQp477nS3fsiGgNmQQKPYNCFyg0TYMoirDb7SRQEATRIsFgECzLtjlLjCAIojE0dZYgCGI/QbdoIk9Pgth/KERR6JtvvmkSojx8+HDccsstxr/79++Pzz77LG2ZDRs24JxzzjE87NetW4eKigqsXbsWAwYMwJtvvonzzz8f/fr1w6hRo7B582YkEglIkgS32w2XywWfz5e1oG+xWFBUVIQtW7YA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BZgJeCrXcj4EERERERERERERZRyDCKIcIAAoWr8IYsIAkl3coilpQkwYGLB+EYSuPTMREREREREREREdAhhEEOUIuxFE0brXAaGLvy0FEUXrXofdCHXteYmIiIiIiIiIiOiQwCCCKIf4Kteg/4YlXXrO/huWwFe5pkvPSURERERERERERIcOBhFEOabPzhXfhxHZatO077iF3y1Bn50rsnMOIiIiIiIiIiIiIgC27l4AETXXZ+cK2KN+lA2fDFOSAVHK3MHNBMSEgaJ1r7MSgoiIiIiIiIiIiLKOQQRRjvJVroGrbjvKhl2IYL9iwEx0LpDY93hP9XoMWL+IMyGIiIiIiIiIiIioSwgjR45MdvciiKh1SQCBvsWoHjIe4fwh7Q8k9t3fWbcdfXZ8BE9VCYRsLZaIiIiIiIiIiIioCQYRRD1IxN0fNYPGINC3GHHF03CjmYCA77+NkxCsoMKmB+CpKkHBrk/gCFV0x5KJiIiIiIiIiIjoEMcggqiHistuhD1FiOYVImFzIClKEMwEpHgEarAczkAZbGy/RERERERERERERN2MQQQREREREREREREREWWN2N0LICIiIiIiIiIiIiKigxeDCCIiIiIiIiIiIiIiyhoGEURERERERERERERElDUMIoiIiIiIiIiIiIiIKGsYRBARERERERERERERUdYwiCAiIiIiIiIiIiIioqxhEEFERERERERERERERFnDIIKIiIiIiIiIiIiIiLKGQQQREREREREREREREWUNgwgiIiIiIiIiIiIiIsoaBhFERERERERERERERJQ1DCKIiIiIiIiIiIiIiChrGEQQEREREREREREREVHWMIggIiIiIiIiIiIiIqKsYRBBRERERERERERERERZwyCCiIiIiIiIiIiIiIiyhkEEERERERERERERERFlDYMIIiIiIiIiIiIiIiLKGgYRRERERERERERERESUNQwiiIiIiIiIiIiIiIgoaxhEEBERERERERERERFR1jCIICIiIiIiIiIiIiKirGEQQUREREREREREREREWcMggoiIiIiIiIiIiIiIsoZBBBERERERERERERERZQ2DCCIiIiIiIiIiIiIiyhoGEURERERERERERERElDUMIoiIiIiIiIiIiIiIKGsYRBARERERERERERERUdYwiCAiIiIiIiIiIiIioqxhEEFERERERERERERERFnDIIKIiIiIiIiIiIiIiLKGQQQREREREREREREREWUNgwgiIiIiIiIiIiIiIsoaBhFERERERERERERERJQ1DCKIiIiIiIiIiIiIiChrGEQQEREREREREREREVHWMIggIiIiIiIiIiIiIqKsYRBBRERERERERERERERZwyCCiIiIiIiIiIiIiIiyhkEEERERERERERERERFlDYMIIiIiIiIiIiIiIiLKGgYRRERERERERERERESUNQwiiIiIiIiIiIiIiIgoaxhEEBERERERERERERFR1jCIICIiIiIiIiIiIiKirGEQQUREREREREREREREWcMggoiIiIiIiIiIiIiIsoZBBBERERERERERERERZc3/D6OiztRPGvrBAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "for row in embeddings.search(\"SELECT SUM(reactions), author FROM txtai GROUP BY author ORDER BY SUM(reactions) desc\", 3):\n", + " authorplot(row[\"author\"], 20)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "🔥 Quite interesting! Perhaps some pointers on topics to write articles and posts about?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Top posts by reactions\n", + "\n", + "Now, we'll plot the Top 25 most reacted to posts." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABiIAAALJCAYAAAA57jvpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdd3RU1drH8e+09AokIT2ICEKAgCgKiCJSxYaKileRK3aKXBSwXxVFsGC9drGjFC9epUk0vCAoINISekkjIQlJSG9T3j8CA2MoAVIov89arMXss8/ez5wZApzn7P0Y4uLiHIiIiIiIiIiIiIiIiNQDY2MHICIiIiIiIiIiIiIiZy8lIkREREREREREREREpN4oESEiIiIiIiIiIiIiIvVGiQgREREREREREREREak3SkSIiIiIiIiIiIiIiEi9USJCRERERERERERERETqjRIRIiIiIiIiIiIiIiJSb5SIEBERERERERERERGReqNEhIiIiIiIiIiIiIiI1BslIkREREREREREREREpN4oESEiIiIiIiIiIiIiIvVGiQgREREREREREREREak3SkSIiIiIiIiIiIiIiEi9USJCRERERERERERERETqjRIRIiIiIiIiIiIiIiJSb5SIEBERERERERERERGReqNEhIiIiIiIiIiIiIiI1BslIkREREREREREREREpN4oESEiIiIiIiIiIiIiIvVGiQgREREREREREREREak3SkSIiIiIiIiIiIiIiEi9USJCRERERERERERERETqjbmxAxARERERkZNX5eZDmV8E5b5h2CyeOAwmDA4bpqoyPIoy8CxMx1JZ3NhhioiIiIjIOUyJCBERERGRM0yZTyi5Ud0oDG6P1d23utFuw4DD2ceBAYwmAMwVRfhlb6Rp6go8izMbI2QRERERETmHGeLi4hzH7yYiIiIiIo3JARQGx5LTohelATFgtzkTDbVyoL9XfjJByQn4ZSdiqK9gRUREREREDqNEhIiIiIjIaa7KzZf0tjdTFBILdjsYT6HU24GEhG9WIhGbZmOpLKq7QEVERERERI5AiQgRERERkdPY/pAOpLcbgt3kdmIrII7HbsNoqyQiaSYBWRvqblwREREREZG/OYVHqUREREREpD7lRPckNW4YdrN73SYhAIwm7GZ3UuOGkRN9ed2OLSIiIiIichglIkRERERE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In777TegOqnQvHlz5/HU1FTMZjOtWrVi27ZtQHWBaz8/P2ef7du306RJE2w220mvxlmwYAFTpkzhm2++wW631ziekpJCv379XN5LbGwsNpuNtLS0Wn1fk5OTqaysJDg4+Ij1II4mJyeHH3/8kR9//JERI0ZwzTXXKBEhItLIVCNCREREREREpAFYLBYCAwMJDAwkKiqK0aNH4+npyYoVKwDYs2cPWVlZ3H333YSHh3PppZfWWCFwPHPmzOH222+ne/fuREZGMnbsWHx8Tq4uwkG//PILBoOBcePGERUVxcUXX1zruAICAmjZsqXLr8DAQPbs2UNISAi9evUiLCyMwYMH06NHj+OOt2bNGjIyMpg4cSLnnXcesbGx/POf/3TpEx8fT0FBAe89cB1dI32J8q+uDTH56gjCfC3HHP/8Jh7EBnu6/DIbYVd+BYNaBxAb7Em7YE8+uC6mxkqH45mzKQ8jBibdcdVRr+PmzZupqKhgxIgRhIWF0bt3b/r16+fSJz09ncsvv9x5PZ966imX1StpaWn8+eefjBs3jjZt2nD++eczbtw4l9Ula9asISkpiUmTJtGlSxdCQkJo164d99xzDxdccEGt3s+qVau4/vrrmT59+hGPx8fHU1lZycSJE4mJiSEuLo5Ro0axePFiZxLpeN/XsrIyvvvuOx5++GH69etHWFgYrVq14sYbb6xxXQ56+OGHufjii2nevDmtWrWiU6dOpKam1uo9iYhI/dGKCBEREREREZEG0LVrV77//nugevVCamoqzz33nPNJb5vNxqRJk3jkkUf45JNP2LJlC5988gnPPfdcreeYOXMmTZs2ZeLEiTgcDhYsWMBvv/2Gt7f3ScddWlrKk08+ydixY/noo4/YvXs3X3zxBU8//TSVlZXHPPfqq6/m6quvdmn75JNP+Oqrr5g9ezZjxozBYrHwxx9/8OWXX3L33Xcfczy73c5TTz3FY489xnvvvUdmZibvv/8+kydPdsZSUVHBmDFj+Ofo8Xx+00X4uJnILKpiaUoRRRXHXh3xyQ0tarS1f3cjT/2aztsDo1lwZ2vySq28tXIvvu5H32LpSIoq7Qyds5O3Lm961OtYVFTEiy++yAMPPMA111zDX3/9xeeff86jjz7qHOc///kP48eP55133qGgoIBvv/22xuc7efJkHnvsMd58803y8vL46KOPiImJcfm8Jk6cyIgRIxg/fjwBAQHk5eWxYcMGl/obx3N43Ym/q6ioYPz48YwcOZL333+f8vJyli1bxrvvvuvsU5vv66effkpBQQFDhw4lNDSU4uJitm/f7rKa5nBGo5ExY8YQFBRESUkJq1evdplTREQahyEuLq7mBoQiIiIiIiIiIkdx9dVXM378eAYNGnTcZER9i42N5e233+aOO+5wqcFR5ebD5l61T+I0lLYJzzprjDXUdWzWrBmzZs1i3Lhx/PXXX/U2j4iIyNFoRYSIiIiIiIiIHFPfvn3JyMhg3759tGzZkvvuu48lS5Y0ShKiR48elJWVkZ6eTnh4OKNGjWLjxo0uSQgAS2Ux5ooirO6+DR7j0dze2hPDnph6v46dOnXC09OTXbt20bRpU+6//34yMzNPqM6CiIhIXVIiQkRERERERESOqUmTJgwfPpwmTZqQm5vLkiVL+OSTTxolFi8vL+677z5CQkIoKChgzZo1vPfee0fs65e9kbzwrmA8sW2U6oXdRhR53PXkk/V+Hc1mMyNGjCA0NJTS0lKSkpJ48cUXsdlOvXC3iIjIydDWTCIiIiIiIiJyVirzCWV790eP37GBtFr+Cp7Fexs7DBERkQZnbOwARERERERERETqg2dxJl75yWC3N24gdhte+buVhBARkXOWEhEiIiIiIiIictYKSk4AYyPf/jCaCEpe0rgxiIiINCIlIkRERERERETkrOWXnYhvViLYG6k+gt2GX9ZG/LITG2d+ERGR04ASESIiIiIiIiJy1jIAEZtmY7RVgqOBt2hy2DHaKgnfNBtDw84sIiJyWlEiQkRERERERETOapbKIiKSZoKhgW+DGIxEJM3EUlncsPOKiIicZpSIEBEREREREZGzXkDWBkK3zG3QOUO3zCUga0ODzikiInI6UiJCRERERERERM4JQSnLDiUj6mubpgPjhm2eS1DKsvqZQ0RE5AxjbuwAREREREREREQaSlDKMizlBaS3G4Ld5AZGU90NbrdhtFUSkTRTKyFEREQOo0SEiIiIiIiIiJxTArI24J2/m/S2N1MUEgt226klJA6c75ezifBNs1UTQkRE5G8McXFxjsYOQkRERERERESkoTmAwuBYcmJ6URoYc+IJiQP9vfJ3E5S8BL/sRAz1FayIiMgZTIkIERERERERETnnlfmEkhvVjcLgWKzuftWNdhsGDt02cWBwJirMFYX4ZSfSNHUFnsWZjRGyiIjIGUOJCBERERERERGRw1jdfCj1i6DcNwyb2ROH0YTBbsNkLcOjKAOvwnTM2n5JRESk1pSIEBERERERERERERGRemNs7ABEREREREREREREROTspUSEiIiIiIiIiIiIiIjUGyUiRERERERERERERESk3igRISIiIiIiIiIiIiIi9UaJCBERERERERERERERqTdKRIiIiIiIiIiIiIiISL1RIkJEREREREREREREROqNEhEiIiIiIiIiIiIiIlJvlIgQEREREREREREREZF6o0SEiIiIiIiIiIiIiIjUGyUiRERERERERERERESk3igRISIiIiIiIiIiIiIi9UaJCBERERERERERERERqTdKRIiIiIiIiIiIiIiISL1RIkJEREREREREREREROqNEhEiIiIiIiIiIiIiIlJvlIgQEREREREREREREZF6o0SEiIiIiIiIiIiIiIjUGyUiRERERERERERERESk3igRISIiIiIiIiIiIiIi9cbc2AGIiMiJqXLzocwvgnLfMGwWTxwGEwaHDVNVGR5FGXgWpmOpLG7sMEVERERERERERAAlIkREzghlPqHkRnWjMLg9Vnff6ka7DQMOZx8HBjCaADBXFOGXvZGmqSvwLM5sjJBFREREREREREQAMMTFxTmO301ERBqaAygMjiWnRS9KA2LAbnMmGmrlQH+v/GSCkhPwy07EUF/BioiIiIiIiIiIHIUSESIip6EqN1/S295MUUgs2O1gPIWSPgcSEr5ZiURsmo2lsqjuAhURERERERERETkOJSJERE4z+0M6kN5uCHaT24mtgDgeuw2jrZKIpJkEZG2ou3FFRERERERERESO4RQesRURkbqWE92T1Lhh2M3udZuEADCasJvdSY0bRk705XU7toiIiIiIiIiIyFEoESEicprIie5JZpvrq18Y6unH84FxM9vcoGSEiIiIiIiIiIg0CCUiREROA/tDOhxKQjSQzDY3sD+kQ4POKSIiIiIiIiIi5x4lIkREGlmVmy/p7YaAw96wEzvspLcbQpWbT8POKyIiIiIiIiIi5xQlIkREGpEDSG97c3Vh6vrajuloDEbsJjf2tL0ZR8POLCIiIiIiIiIi5xAlIkREGlFhcCxFIbF1X5i6towmCkPaUxgc2zjzi4iIiIiIiIjIWU+JCBGRRpQT0wvsDbwl09/ZbeTEXNm4MYiIiIiIiIiIyFlLiQgRkUZS5hNKaWAM71zbgi8Hn+ds/2FoK17sHdFwgRhNlAa2oMyneb1NkZCQQPfu3ettfBEREREREREROX0pESEip70JEybwwgsvHPX4jBkzuOmmm454LCQkhISEBOLj42nWrJnLsSZNmhAfH09CQgIhISFHHX/atGkkJCTU+GU0ntqP0NyobmC31Wgf9v0uJi/LOKWxT8TgCwPJHt+JkY8+3mBzioiIiIiIiIjIucPc2AGIiDSEffv20bdvX7755htnW79+/cjJyaF58+OvBPjpp5/49NNPXdrsp7ilUmFw+yPWhthfXjM5UZ/+0bEpb6/MYtglF/KFxUJVVVWDzt8QTCYTNlvDXlcREREREREREammRISInBMWLVpE//79XRIR/fv35+eff+auu+467vnl5eXk5+cf8djAgQMZMmQIoaGh7N27l++//54ffvjBeTwoKIiHHnqILl26YLfb2bhxI9M++BSru+8Rx/thaCsSs8p48pd0ANY+2I4v1u2jRaA717cOZH+5jddWZPLF+lznOReHe/NK30haNfVgS04Zr63Yy5c3teSKTzeTmF121PcV5e/GxeE+DPvvLnpE+dL9qr4sWTTPebxfv36MHDmS559/nocffpjg4GA2btzIlClTyMvLA6B169aMGDGCVq1aYTKZ2LlzJ++++y7bt28/4pyvvfYaKSkpvPXWW842f39/Zs2axcSJE7HZbLzxxhs1zlu4cCFTpkwBoHv37tx1113ExMSwb98+Fi1axFdffeVMDiUkJDBt2jQuueQSOnfuzHfffcfnn39+1OsgIiIiIiIiIiL1R1szicg5YcWKFfj6+hIbGwtAbGwsvr6+rFix4pTGvfrqqxk+fDiffPIJw4YN4+OPP2b48OH069cPqH4Sf+rUqZSWljJ69GhGjRpFWVkZr05+EYvRUOt5HrokhHWZpVw5fQufrs3h1X5RnN/EHQBfNyPf3NySzTllXDV9Cy8ty+TZK8NrNe7QDk1ZvLOAogo7s5LyGHDNoBp93N3dGTJkCC+99BJjxowhODiYBx980Hncy8uLRYsWMWrUKB5++GHS09N5+eWX8fT0POKc8+fPp3fv3lgsFmdbnz592LdvH3/99RdJSUkMHjzY+Wvs2LFUVFSwYcMGANq3b8/EiRP5/vvvufvuu3n99dfp378///jHP1zmGTZsGL/99hv33HMPCxYsqNX1EBERERERERGRuqdEhIicE6xWK4sXL2bgwIFA9SqG+Ph4rFZrrc6//vrrmT9/vvPXwRvxd999N++99x7Lli1j7969LFu2jNmzZzNoUPUN/V69emE0GnnllVfYvXs3qampTJkyhZBmTeke6VXr+ON3FvDp2n3s3l/Bm39kkVtmpUdU9YqKm9o2weGARxaksjW3nF92FfL2yqzjjmkAbo9tyqyk6pUN3yftI67N+TW2qrJYLEybNo1t27axfft25s6dS+fOnZ3H165dS3x8PGlpaaSmpvLaa6/h7u5Ox44djzjv0qVLAVyKV/fv35+FCxcC1Z9Vfn4++fn52Gw2Hn30URYsWOBMJgwbNowZM2awaNEiMjMzWbNmDZ9++inXXnutyzy//PILCxcuJDMzk+zs7ONeDxERERERERERqR/amklEzhkLFizgnXfe4aOPPuKKK67g4YcfxmSqWaPhSOLj4/nqq6+cr4uLi/Hw8CA8PJzHHnuMRx991HnMZDJRXFwMQMuWLQkPD2f+/Pku47lbzJwX6M6SlJJazZ/0t+2VskuqaOZd/SP8/KbubMopo8LmcB7/K7P0uGNe2cIXLzcji3cWAJBfVsWKzSkMGDCA6dOnO/uVlZWRkXGoeHZubi4BAQHO14GBgfzzn/8kLi6OgIAATCYT7u7uRy0AXlVVxeLFixkwYABLliyhVatWxMTE8OSTT7r0M5lMPPfcc2RlZfH2228721u2bElsbKzLCgij0Yi7uzvu7u5UVFQAsG3btuNeAxERERERERERqX9KRIjIOePgioSnn36alJQUkpOTadmyZa3OLSkpcbkZD9U34KG65sGmTZtcjh2sVeDp6cm2bduYNGmSy/Hs83qzI6Aj1esSjq/K7nB57XCA0VD7rZ2O5B8dmtHE08yeRzs524xATogPn332GQ5H9Zx/L/LscDgwGg8tqJs4cSJ+fn688847ZGVlUVlZybvvvovZfPS/YubNm8dHH31Es2bN6N+/P2vXriUry3UVx9ixY531NQ4vDO7p6clnn33mXFlxuMrKSufvy8qOXhtDREREREREREQajhIRInJOWbBgAWPHjuX1118/5bHy8/PJyckhNDSU+Pj4I/bZvn07vXr1Yv/+/ZSWHlqlkOmTR5GnDYyn/mN4R24Ft7RrgpvJQOWBVRGdQo+97VOgh4kBrfy5Z+5utuyrvmFvcNhomvkXMx69hS5durB69epazR8bG8sbb7zBypUrgeri3IevmDiS3bt3s23bNgYNGkTv3r1dClcD3HLLLVx55ZWMHDmSwsJCl2Pbt28nMjKyRmJIREREREREREROT0pEiMgZwdvbu8bqhcLCQnJycgBo1qxZjeN/f8Ie4KeffmLJkiXOrZNO1WeffcaoUaMoKSlh1apVWCwWWrduja+vL7NmzSI+Pp5bb72VSZMmMX36dHJycggJCeGeQT2ZstmNjBL78Sc5jjmb8njyijCm9Y/izT+yCPezMPKS6m2RHEc5Z0hsE/LLrMzdkn+o0W6jeXIqK1euZODAgbVORKSnp9OnTx+2bt2Kt7c3999/P+Xl5cc9b968eYwePZry8nKWLVvmbO/cuTP3338/b775JgUFBc6VJ5WVlZSUlPDFF1/w0ksvkZWVxdKlS7Hb7bRs2ZIWLVrw6aef1ipmERERERERERFpOEpEiMgZoVOnTnz88ccubfPmzePVV18F4LbbbuO2225zOf7iiy+yceNGlza73V7jCftTMX/+fCoqKrj11ludN+B3797N7NmzAaioqGDMmDHcf//9PP/883h5eZGTk8PKzbsoqnKrkxiKKu0Mnb2TV/tFsmR4GzbnlPHK8kw+ur4FFdYjJzru6NCMedsKXBuNJjyKMli6dCmPP/44fn5+tZr/lVdeYdy4cXz44YdkZ2fz8ccfO4t5H8svv/zCww8/zK+//kpVVZWzvX379phMJv71r3/xr3/9y9m+cOFCpkyZwurVq3niiSe46667uP3227FaraSlpTFv3rxaxSsiIiIiIiIiIg3LEBcXd7QHZkVEpJ5Uufmwuddz9Tb+zW0DefuaaFpMW0+5tfY/5tsmPIu5sm5WixxPSEgIX3/9NQ8++CDbt29vkDlFRERERERERKThaUWEiEgjsFQWY64owuruWyfj3RrbhOT9FWQWVREb7MmzV4bzw+b9J5SEMFcUNkgSwmQy4efnxz333MPmzZuVhBAREREREREROcspESEi0kj8sjeSF94VjKZTHivY28LEy0MJ9raQVVzFD1vzefH/TqCYs92GX3biKcdRGweLW6empvLvf/+7QeYUEREREREREZHGo62ZREQaSZlPKNu7P9rYYTi1Wv4KnsV7GzsMERERERERERE5yxgbOwARkXOVZ3EmXvnJYD9yQekGY7fhlb9bSQgREREREREREakXSkSIiDSioOQEMDbyj2KjiaDkJY0bg4iIiIiIiIiInLWUiBARaUR+2Yn4ZiWC3dY4Adht+GVtbLD6ECIiIiIiIiIicu5RIkJEpBEZgIhNszHaKsHRwFs0OewYbZWEb5qNoWFnFhERERERERGRc4gSESIijcxSWURE0kwwNPCPZIORiKSZWCqLG3ZeERERERERERE5pygRISJyGgjI2kDolrkNOmfolrkEZG1o0DlFREREREREROTco0SEiMhpIihl2aFkRH1t03Rg3LDNcwlKWVY/c4iIiIiIiIiIiBzG3NgBiIjIIUEpy7CUF5Debgh2kxsYTXU3uN2G0VZJRNJMrYQQEREREREREZEGo0SEiMhpJiBrA975u0lvezNFIbFgt51aQuLA+X45mwjfNFs1IUREREREREREpEEZ4uLiHI0dhIiI1OQACoNjyYnpRWlgzIknJA7098rfTVDyEvyyEzHUV7AiIiIiIiIiIiJHoUSEiMgZoMwnlNyobhQGx2J196tutNswcOhHuAODM1FhrijELzuRpqkr8CzObIyQRUREREREREREACUiRETOOFY3H0r9Iij3DcNm9sRhNGGw2zBZy/AoysCrMB2ztl8SEREREREREZHThBIRIiIiIiIiIiIiIiJSb1SsWkREREREREREGl2Vmw9lB1d/WzxxGEwYHDZMVdWrvz0L07Fo9beIyBlJiQgREREREREREWkUh+rhtcfq7lvdeMx6eEX4ZW9UPTwRkTOMtmYSEREREREREZEG4wAKg2PJadGL0oAYsNuciYZaOdDfKz+ZoOQE/LITMdRXsCIiUieUiBARERERERERkQZR5eZLetubKQqJBbsdjMaTH+xAQsI3K5GITbOxVBbVXaAiIlKnlIgQEREREREREZF6tz+kA+nthmA3uZ3YCojjsdsw2iqJSJpJQNaGuhtXRETqzCmknUVERERERERERI4vJ7onqXHDsJvd6zYJAWA0YTe7kxo3jJzoy+t2bBERqRNKRIiIiIiIiIiISL3Jie5JZpvrq18Y6ulW1IFxM9vcoGSEiMhpyNzYAYiIiIiIiIiIyNlpf0iHQ0mIBpLZ5gYs5QXapukwVW4+lPlFUO4bhs3iicNgwuCwYaoqw6MoA8/CdCyVxY0dpoicxZSIEBERERERERGROlfl5kt6uyHgsNffSogjcdhJbzcE7/xd5/TN9TKfUHKjulEY3B6ru291o92GgUPlYh0YnFtlmSuK8MveSNPUFXgWZzZGyCJyFlOxahERERERERERqVMOIDluOEVBF9Z9TYjasNvwy9lE9LrPMDT87I3GARQGx5LTohelATFgt53Y9T/Q3ys/maDkBPyyE8+p6yci9UcrIkREREREREREpE4VBsdSFBLbeAEYTRSGtKcwOBb/7MTGi6MBVbn5kt725urrbrdXN55oEuhA/1L/SFI6Dcc3K5GITbOxVBbVcbQicq5RsWoREREREREREalTOTG9Dt0Mbyx2GzkxVzZuDA1kf0gHtvaYUL0CBcB4irf8DiQkioIuZGuPCewP6XCKEYrIuU6JCBERERERERGRA6ZNm8bDDz/c2GGctJCQEBISEmjZsmWjxVDmE0ppYMyp3ww/VUYTpYEtKPNp3rhxHMWMGTO46aabTnmcnOiepMYNw252r/ttsIwm7GZ3UuOGkRN9ed2OLSLnFNWIEBEREREREZFzyoQJE+jfv3+N9jvuuIOioiKsVitlZWV1Pm/Hjh154403jtnnkUceYf369Sc9h9FoxN/fn4KCAuwNsCKhX79+3HDDDcTExGC329m+fTvvrkhjTn5I49SG+Jsfbj+fHTt38fkLY494PCEhgaeeeorly5fXOHbw8yoqKuKmm26iqqrKeax169a8//77APTq1eukYvP396e8vJyKioqTOt9kMnH72H9z2cUXER3gRlGFjf9LKeL5JRnsLT4Ua4CHiZf7RNL/fH/sDgc/bt3PE/HplFRVfz+6R/nw4MXBdAr1xtfNyK78Ct5ZmcXsTfk15gzdMpch55l55pln+O2333j66adPKnYROfeoRoSIiIiIiIiInHNWrlzJlClTXNrq++Z9UlISgwcPdr4eNWoUXl5eLnEUFZ3aXvx2u538/Jo3kOvDAw88wI033sgnn3zCb7/9htlspk+fPrx3/x0Exqfz8V85DRLHMRkMVHo1O6UhSktLufzyy/n111+dbQMHDmTv3r00b37yqy0KCgpOKa6KyIto2bYDr67IJCm7jAAPEy9dHcnXN51H78+3Ovt9cG0MIT4Wbvp2O2aTgbcHRvN6/yju/zEZgEvC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uGzVVBtdwrydvzcZoGOIF9tyj7xqZP3eEi70h9zt653bVx38dfCmsN1u56+//uKDDz5gxIgRNG/enM6dOx8zhszMTDIyMmp18zkjI4PKykpiY2MPxW0y0aZNG2ex64Patm3r/L2Pjw8RERGkpqYecdwNWaWc38Sd3fsravw62i4IdaWgwkZWsRUvS/V9h12ZuS7vDyA2Npb09PQaqwoOSkpKIjs7m169etG7d2+WLFlS4+b+iUhLS+O8887DYrE429q0aXNCY8THx3Peeeexe/duUlJSahzfvn07JpOJgICAGt+n/Px8oDrZYDyJ+zEpKSm0bNnSZcuj2NhYbDYbaWlplJSUsG/fPi688FBRcqPRyAUXXOB8fSLftYNSU1OP+dmlpqZiNptp1aqV83hYWBh+fn4u5xQWFrJ8+XIGDBhA//79Wbhw4QlfAzn7KREhchbIie5Jatww7Gb3E18BcTxGE3azO6lxw8iJvvz4/UVERERERKRR3HLLLVx11VVERkYSERHBFVdcQW5urnPf+r1799K5c2cCAwPx8fGp11h89x1/W5ypv2Xy0CUh3HdREOcFutMhxJN7Lwo65jn3dG7GNRf406qJO1P7RhLgYeLrDUdOery1MpsuUQGMHj2ali1bEh4eTvfu3Rk9ejQAl156KYMHD6Zly5aEhITQt29fDAaDS72HU1VeXs7//vc/7r//fi6++GKio6N59NFHcXd3Z/581+2M7rrrLjp37kxMTAwTJ06koKCA3377zaWP48Dmya8sz+TW2KY81r05rZt5cEFTD268MJAnLq/bmo/D4prxat9IrozxJSbAjdbNPHj2yjDaNPNg0Y4CHBj4fMFyOnfuzJ133klERAT9+vXjhhtuOG6Nhl9++YXrrruOLl26EB8ff0px/vLLLxgMBsaNG0dUVBQXX3wxQ4YMOaExiouLuemmmxg3btwRj6enp7N48WIef/xxLr/8cpo3b06bNm0YOnQol156KVD9Z8zLy4vOnTvj5+eHu7t7reaOj4+nsrKSiRMnEhMTQ1xcHKNGjWLx4sXOJMecOXO4/fbb6d69O5GRkYwdO9blz/GJfNcOmjlz5jE/u7S0NP7880/GjRtHmzZtOP/88xk3btwRt4ybN28effv2JTo6+ohFykW0NZPIGS4nuieZba6vfmGop9zigXEz29wAQFDKsvqZR0RE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x4PVaiUhIQGLxYLT6cTn80U7PBERERGRA6aNR0VERERERJqhUChEdXV1rYLWycnJKmgtIiIiIkccJSJERERERESasWAwSGVlJRUVFQCkpKRgt9tV0FpEREREjhj65CoiIiIiInIE8Pv9VFRU4HA4iImJITU1FZvNhsFgiHZoIiIiIiJ7pTW9IiIiIiIiRxCv11unoLXL5cLtdkc7NBERERGReikRISIiIiIicgRyu92RgtY2my1S0Nrr9UY7NBERERGRWpSIEBEREREROUKFw2GcTicejwebzYbdbsfv9+N0OvH7/dEOT0REREQEUI0IERERERGRI14wGMThcFBeXg5AcnIySUlJmEymKEcmIiIiIqJEhIiIiIiIyFEjEAhQUVFBZWUlRqORlJQUEhISMBr11U9EREREokdbM4mIiIiIiBxlfD4fPp+P+Ph4bDZbrYLW4XA42uGJiIiIyDFGiQgREREREZGjlMfjwev1YrVasVqtkYLWHo8n2qGJiIiIyDFEiQgREREREZGj2O6C1m63G5vNRkJCQiQh4fP5oh2eiIiIiBwDtFGoiIiIiIjIMSAUClFVVUVFRQWhUIikpCSSkpKIidH8NBERERE5vJSIEBEREREROYYEAgEqKytrFbROTExUQWsREREROWw09UVEREREROQYtGdBa6vVSmpqKm63G5fLpYLWIiIiItKolIgQERERERE5hnk8HjweT6SYdXx8PC6XC7fbHe3QREREROQooUSEiIiIiIiIRJIPNpsNm80WKWjt9XqjHZqIiIiIHOGUiBAREREREREAwuEw1dXVkYSE3W7H7/fjdDrx+/3RDk9EREREjlCqRiYiIiIiIiK1BINBHA4HFRUVACQnJ2O32zGZTNENTERERESOSEpEiIiIiIiISL38fj8VFRU4HA5iYmJISUkhISEBg8EQ7dBERERE5AiirZlERERERERkr7xeL16vF4vFgtVqJS4uDrfbjcvlinZoIiIiInIEUCJCRERERERE9ovb7cbj8WC1WrFarcTHx+NyufB4PNEOTURERESaMW3NJCIiIiIiIvstHA7jdDopKyvD7/eTmJhISkoKsbGx0Q5NRERERJopJSJERERERETkgIVCIaqqqigvLycUCpGUlERSUhIxMVp4LyIiIiK1KREhIiIiIiIiBy0QCFBZWUllZSVGo5Hk5GQS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cNIA/LEJTXteETnimH3VxHibV6H7GK9DSQiRKFIiQkRERERERKQZCgaDOBwOKioqAEhOTibRbmf7SdfUFKY+XNsxNcRgJGSKZXuXq9HWCiKyL/aSFRBqeKukJhUKYi/Ji3YUIsc0JSJEREREREREmjG/309FRUVNUqLlSTjST2r8wtT7y2jCkdEVR3pudM4vIkeMFluXRO/fqt8ymmixdXG0oxA5pikRISIiIiIiInIE8Hq9bG55etNvyfRboSCl7c6Lbgwi0uxZqouwlm+BUPT/zbKWb8ZSvSO6cYgc45SIEBERERERkWPKtGnTuOqqqyKPFyxYQI8ePQDIyMhgwYIFdOjQoUljeu655xg2bNhe+7gTMnGltGv6LZl+y2jClXIc7oRWhzTM/lyziBzZ0rYsAGP0/81K27IwujGICDHRDkBERERERESkPl26dOH555/n+++/56GHHmq0cYcOHYrH42m08X4rIyOD6dOnc+utt7Jx48b9OubRRx8lEAjstc+unHNr9ls/iK1OHuiZySUnJHHe1DW12tskxbLsjlx+9+/V5JW493/AUJBdOT1ovWr2Acey2/5cc2N47rnn6N69e532888/n1C0Z2qLHOXsJXkkFudRldY5Ots0hYLYS1epPoRIM6BEhIiIiIiIiDRLl1xyCe+99x6XXHIJLVq0YNeuXXvtbzQa9+vGcmVlZWOF2Giqqqr22ceR3rVZ7bfuSM+FQ0hE7M81N5Y5c+bw73//u1abkhAih58BaL1qFmt7jiZkiGvaFV3hEMagj+xVszA03VlFpAFKRIiIiIiIiEizEx8fT58+fRg6dCipqalcdNFFvPXWW5Hnu3XrxoQJExg9ejSDBw/muOOOY9SoUaxbt457772XHj164HK5mD59Oj169GDDhg1MmjQJqNmaadasWcyeve+b6Eajkfvuu49TTjmF1NRUiouL+fDDD/fr2D1169aNIUOG0KFDB6qqqpg3bx7/+te/IjfDn3vuuToxzpkzh+zsbH73u99RVe3k6aUuXv+5JhljNhoYe342/TqmkBxvotTp57WfdjLhm+IDiuu3BnZN5a/nt6b9hOWRtktOSOKNqzrQ4ukfI233nduK207rim3UxyxYsIDKykrOPPNMbrvttsjrNmzYMC688EKCwSBz584lNTUVm83GI488sn/XXFXFm2++yZw5cyLnPemkkxg5ciQ5OTls3ryZN954g7Fjx+5z9YnH46G8vLxO++23307Pnj1JS0ujrKyM+fPn8/rrrxMMBiN9zjnnHG688Ubat2+P2+1m+fLlPProowCYzWYGDx7M73//exISEtiyZQsvv/wyP//888G8/CJHJbOvitYr32Fr95ua9sQGI61XvoPZV9205xWReikRISIiIiIiIs1Onz592Lp1K9u2bePzzz9n+PDhtRIRu91+++1MnjyZoqIiqqqquPPOO8nNzWXMmDGUlZVxyy23cMIJJ7Bhw4aDisNgMFBaWsrjjz+Ow+EgNzeXe++9l127drFw4cL9GqNly5Y89dRTzJs3j6eeeoqcnBzuv/9+fD4f//nPfxo87pprrmHq1Km8+eabnH3J1Tw78AqWbKtmQ5mX209P46Ljkxn8/iYKHH6y7WayE2MP6hoP1NVdUrjnnFY88NlWCuZN5bJT2nHNNdewY8evhWAHDhzI+eefz7hx48jPz+eqq66iR48eLFu2bK9j73nNv/vd7xg5ciQ///wz27Ztw2q18te//pVvv/2WsWPHkpGRccg1JlwuF+PGjWPnzp20b9+e+++/H7fbzfTp0wE4++yz+ctf/sKbb77JU089hdls5qyzzoocP2LECNq1a8df/vIXdu3aRc+ePRk/fjz/93//x/bt2w8pNpGjSXLxcvxr3qeo0xVNds7MNe+TXLx83x1FpEkoESEiIiIiIiLNziWXXML8+fMB+O6777DZbHTr1q3OTPOpU6eydOlSACwWC3379mXs2LH8+GPN7P3x48czc+bMg44jGAzy2muvRR7v2LGDLl26cN555+13IuLyyy+ntLSUf/7znwBs27aNli1bcvvtt/P6668TDofrPe7bb7/lgw8+AOCl/+Zx7RX96JmTyIYyL63tsWwq9/BNgROAAocPcO41ji5pFvLv7Var7WC2K7nttHTeWr6Tt38upVV1DK+//jqnn346Fosl0qd///68/fbbfPXVVwA8//zztW7gN2TPa542bRpXX3013bt3Z9u2bZx//vkAPPvss/j9fvLz82nZsiWjRo3a57iXX345l156aeTxRx99xOTJk3nzzTcjbcXFxcyYMYPf//73kUTEoEGD+OKLL2r9Hdi98iI9PZ2LL76Ya6+9NrJt2DvvvMOZZ57JxRdfzJQpU/YZl8ixJC1/EUBNMiIc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5Mk8/vjjLFmyJNKnd+/ePPzwwwwZMoQtW7Yc0nWIiEh0OZ1O/H4/iYmJpKSk4HA4tFWMyGFktVoJh8ON9llTRI5NSkSIiIgcI5544gnMZjNPP/00RUVFpKSkcOqpp2K326Md2gG577772Lx5M/Hx8bRv357+/fszZcoUHn74YX788UcAPB4PHo8nckxWVhZArQRLfW0NcaR33a/aEBWew38TJISJOZu9vPXPv1FZWUl2djZ33303drudsWPHNso5qqqqGmWchux+D3fz+XyNNvbu5Np9991HXl4eDoeD5ORk7rnnHqZOnaokhIjIUcLn81FRUYHdbic5OZnq6mq8Xm+0wxI56phMJuLj46muriYcPrwTbkTk6KatmURERI4BNpuNbt268corr7Bs2TKKi4tZs2YNb7/9dmTW+LRp0wAYO3YsCxYsiDzOyspi7NixzJ49m7lz5zJ58mROPfXUOuewWq2MGTOGuXPn8s4773DFFVfsNaa0tDQee+wxPvroIz744APGjh1LRkbGPq+lsrKS8vJyioqKWLx4Mffffz+rV69m1KhRGH/ZOmnPrZluuukmnnrqKaBmxceCBQvqbYP6tyR64q9PMaH/rwWi/++Ulnx3exe239+d1Xd1ZeoVx0We++3WTElxJl7s15aNI09m233dmXFNB9qnxEWeH9g1lU0jT6bPcYl8fWsX8u/txjsDOpBha3iuSKU3yL9WVLNqSyHFxcX8+OOPfPDBB3Tt2rXBYx5//HFGjBgReTxs2DAWLFhAmzZtAIiJiWHu3LmR93XP1+G5556jVatWDB8+vNZrtdsZZ5zBa6+9xty5cxk3bhypqakNxhG5hl/ew93/NfZew2+//TbFxcXcfffdANx7770UFBTwzjvvNOp5REQkuoLBIOXl5fh8Pux2OzabLdohiRx1bDYbwWCw1iQfEZGDoUSEiIjIMcDtduNyuejRowdms7nePkOHDgXg6aefpn///pHHFouFb7/9lvvuu4/bbruN7777jr/97W+kp6fXOv7aa69l48aN3H777UybNo3hw4dz2mmn1Xsuk8nE+PHjcblcjBgxgrvuugu328348eOJiTmwBZvhcJjZs2fTqlUrTjzxxDrPz5gxg6effhqA/v37079//3rbGhKKiY/83L2Vlaf+0IanFxVx1iurGPDOBr7e1vCWTpP6taV7KyuD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P/J8eno6KSkpkYRAp06d9hrj+vXr6d27Nzt27NjvVf9ffPEFjzzyCD169GiwADZAqStAUZWPtslxzFp1+LaP3l+ntm/Fjh07eOuttyJtGRkZBzTGJ598wrhx43j77bfrfb3y8/Pp27cv8fHxkfc7NzeXYDDItm3bcDqd7Ny5k86dO7N8eU0tTaPRyIknnsj69esB2LJlCz6fj/T09Hq3YWpIaWkpH330ER999BG33norl156qRIRIo2g/uqvIgLUJB4KCwsj/zU0i+G3nE4nNputTntCQgLV1dWRx2GDibbJcTzWJ5tRn23jnbwyXri0LbEmQ51j90coHI7MUNjN/NupDvshbDTV276vRExGRgZPPfUUGzdu5LHHHmPIkCH885//BGqWPoqIiIiIiBwLfD5fZM/7lJQU4uLiohvQUWDChAmYTCZeeukl+vTpQ05ODm3atOGCCy4gJycncjN76dKlbNq0iYcffpgTTjiBTp068dBDD7Fs2TLWrVsHwPTp0+nbty+XXXYZ2dnZXHPNNfTq1YsZM2ZExigsLOTBBx+kffv25Obm8n//9397je/9998nMTGRRx55hI4dO5KVlcUZZ5zBAw88gNFY/+23L774goULF/LII49w44030rlzZzIyMujWrRt9+vSp2c6Ymu/0Ty8qYuQ5rbj9tDQ6pMTROS2e67umcscZ6Y31Eu+XMAa2FRWTkZFBnz59yMrKon///vTs2fOAxvnuu++4/PLLmTp1ar3Pz58/H5/Px4MPPki7du3o3r07d911F59//nkkOTR79mwGDhxIjx49aNOmDffccw8JCQmRMdxuNzNmzGDYsGH07duXrKwsTjjhBK688kr69u1b73mHDRvGGWecQatWrTjhhBM45ZRT2Lp16wFdm4jUT3cGRQ6Dbdu21SqQtNuJJ55Ya19II0Fe6Hcci/KrmJFXxtz1FSwe3IWHemXyxMLCAz7vLleAdJu5VltuhrXWY18wjPE3yYl1uzwM7NoCq9mI2wuGULDWTIP91bFjRwwGA5MnT44sPz7vvPMO+DpERERERESOdMFgkPLychITE7Hb7bhcrv3eQ1/qKiws5LbbbmPQoEHceuutpKWl4ff7yc/PZ8aMGZHC0wBjxoxhxIgR/POf/yQUCvH999/z/PPPR55fvHgxL7zwAgMGDGD48OEUFRUxbty4yKz5UCjE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EBBA165duemmmxz6fPnll/Tt25c2bdoQHBzMqFGj8PDwwGYrfuKfm5vLunXrGD58OBEREYSFhTF+/HisVqu9z9+xevVqQkJCGDZsGEFBQbRr145OnTpdwpk2bKXsLno0JZ+IWu60r+1JHR8XnrrVn+a13B36TN2cwCM3+THshppc5+NCUz83ht5QvIpkY1QmW49ns6jbdbQL8yS4qpmWge480zaAiFpVLhKTAVNRrkNLXFwcP/zwA0899RS33nortWrVokGDBvTp04dWrVoBsHjxYho3bszo0aOpU6cOgYGBtGnTxl6sOjY2li1btjB27FgaNmxIvXr1ePLJJ8nLyysRwW233Ubnzp0JCgpi4MCBNGjQgK+++grAnnho3749AQEBdOvWjVtuucXh/IULF3L77bczcOBAQkJCqF27Nr169SoxT2RkJJMmTWLAgAF07979IvdFRERERET+aWpGbyJkx0KMRfllX8DaasFYlE/IjoXUiLl2khBQvBrCZrNpRYSIXJW0NZOIXFBMTAzTp0+nb9++9O/fn59//pmlS5dy99132/ssXryYatWqMWnSJKxWK2vWrOGPP/5w2E7o/fffZ+zYsbz66qvk5OSwZMkSfH19r+gvWCdOnODFF19k5MiRdO/encjISD799FPGjh17wXENVguUsr3Tgh0naeJXhY/vq40N+HJvKvO2J3PHdV72Pkv2pODiZGRkS19euj2QlJwiVh1Isx/vtewwz7QNYOZ/QqlexYmkrCJ+i80iKfsi+3sajLhmxpdofuONN+jXrx8jR46kRo0apKens3fvXn777TegeBXCmDFjGDx4MO+99x4Gg4H4+Hg2bNjgMMb48eOZPn06KSkpzJs3j4cffrjk9S9YQPv27RkzZgynTp1iypQpREdHA/Drr7+yfPlyHn/8cZydnfnf//7HokWLGDhwoP38nTt38tJLL9GvXz969+5NTk4Ou3aVvoR6z549PPXUU7z++utYrVZ7wkNERERERP4dvBN34Z56jLhGPcj0Cy9OSBhNf3/A0+d7Je8lcO/ya6YmxNnMZrOSECJy1TJERET8/deRRURKYTAYWLBgARs3bmT+/Pml9nF1dWXZsmV88MEHrF27tszm7tu3L/feey8PPvjgefsUmj3Y1/6lMpuzrDTa8AJO1+BftkVERERE5N/LBmT4hpMc1p4cn7DL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sVqoknoMt6wTlRuHiMgVUCJCRERERERERESkFDWjNoCxkh+fGU3UjNpYuTGIiFwhJSJERERERERERERK4ZW0B8/EPZVXK8JqwStxt+pDiMi/nhIRIiIiIiIiIiIipTAAQXuXY7QUgK2Ct2iyWTFaCgjcuxxDxc4sIlLmlIgQERERERERERE5D+eCTIIil4Khgh+jGYwERS7FuSCrYucVESkHSkSIiIiIiIiIiIhcgHfiLvz3f12hc/rv/xrvxF0VOqeISHlRIkJEREREREREROQiakZv+isZUV7bNJ0eN2Df19SM3lQ+c4iIVAKnyg5ARERERERERETk36Bm9Cac89KJa9wTq8kMRlPZDW61YLQUEBS5VCshROSqo0SEiIiIiIiIiIjIJfJO3IV76jHiGvUg0y8crJYrS0icPt8reS+Be5erJoSIXJUMERERtsoOQkRERERERERE5N/EBmT4hpMc1p4cn7DLT0ic7l8l9Rg1ozbilbQHQ3kFKyJSyZSIEBERERERERERuQK5Hv6cCmlNhm84RS5exY1WCwb+euxmw2BPVDjlZ+CVtIfqMb/ilpVQGSGLiFQoJSJERERERERERETKSJHZgxyvIPI8A7A4uWEzmjBYLZiKcnHNjKdKRhxO2n5JRK4xSkSIiIiIiIiIiIiIiEi5MVZ2ACIiIiIiIiIiIiIicvVSIkJERERERERERERERMqNEhEiIiIiIiIiIiIiIlJulIgQEREREREREREREZFyo0SEiIiIiIiIiIiIiIiUGyUiRERERERERERERESk3CgRISIiIiIiIiIiIiIi5UaJCBERERERERERERERKTdKRIiIiIiIiIiIiIiISLlRIkJERERERERERERERMqNEhEiIiIiIiIiIiIiIlJulIgQEREREREREREREZFyo0SEiIiIiIiIiIiIiIiUGyUiRERERERERERERESk3CgRISIiIiIiIiIiIiIi5UaJCBERERERERERERERKTdKRIiIiIiIiIiIiIiISLlRIkJERERERERERERERMqNEhEiIiIiIiIiIiIiIlJulIgQEREREREREREREZFy41TZAYiIXM0KzR7kegWR5xmAxdkNm8GEwWbBVJiLa2Y8bhlxOBdkVXaYIiIiIiIiIiIi5UaJCBGRMpbr4c+pkNZk+DahyMWzuNFqwYDN3seGAYwmAJzy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SeHll18mPDwcf39/2rZtS6NGjQD45JNP6NChA/379ycsLIyQkBDa3tWF7k+9S3TzQeR4hRQPdJkrXs70z6kaTHTzQURFDKLQXHoyRERERERERESkImlrJpEydPz4cUaMGMHAgQN54YUX8PT0JCUlhV9++YWFCxeW2DN+2bJl1K9fn/79+5OTk8P777/v8AB90qRJDBkyhAkTJuDt7U1KSgq7du0qUYT4cmzbto3OnTtz77334uXlRXp6Onv37mXcuHH2lRZffvkl7u7ujBw5Em9vb6Kjo3nmmWc4fvw4ULzFz5QpUxgzZgwff/wx+/fv5+OPP+all1666PzTpk1j6NChjBkzBi8vL5KSkvjss8+A4r39H3vsMYYNG8abb76Js7MziYmJ/P7771it1ku6vqKioguuGDl06BCTJ09m0KBB9OvXj1OnTjF//nx74WCAN954g/HjxzN9+nRSUlKYtexbavbv4zBOr2WHeaZtADP/E0r1Kk4kZRXxW2wWSdmF505JbqGV66u70iu8Gj5uTiRmF/LxtmQWbD953jj3JuWSnmfhSEo+2YXF1/5LTBZORgO/xGSB0YRrZsltgwDmz5/PuHHj+OyzzzCbzbRv3/6C96wsfPDBB0ycOJH33nuPkydPMnPmTIeEWU5ODs888wxPPPEEc+fO5dixY3zyySc899xz9u2ITpw4wYsvvsjIkSPp3r07kZGRfPrpp4wdO7bENl+X4/3332fs2LG8+uqr5OTksGTJEnx9ff/WmDabjcmTJ/PYY48xf/58YmNjmTFjBtOnT7f3KSoqYsKECYwcOZLXXnsNk8lEdHS0vdD71q1befrpp+nfvz+9e/em0GrjYGoRi3aeBFLhSleznE5IZNZsyIFbJhIUuRTvxF1XNqaIiIiIiIiIyBUwREREaPcGkUqwePFili9fbl9lIP9chWYP9rW/eJKlojXa8AJO/+IixXfeeScTJkzg7rvvPm9SoG/fvtx77708+OCDZTavq6sry5Yt44MPPjjvFl4VJTm0LQkN7gObFQzlsEjx9Lj++7+mZvSmsh9fREREREREROQSaEWEiMhFOBdk4ZSf6bhffyVzys/41yUhOnToQHx8PCdPnqROnToMGzaMjRs3OiQh7rvvPvbv309GRoZ967KvvvrqiuatW7cuISEh7N+/H3d3d/r37w9QalH5imRPQkD5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iNQh77TdNMw+QkqHYeQFRIDVcnkJibP3e2UkEpy4UntCiIiIiIiIyO+OoWvXrrb6DkJERETkr8YG5PpHkNE8kgKf5jVPSJxt3yD7CH5JG/BK34OhtoIVERERERERuQxKRIiIiIjUM7NHIKdCryfXP4JSV6+yk1YLBs7/mWbDYE9UOBfl4pW+B9/kLbjnn6iPkEVERERERESqTYkIERERkd+RUpMHBV4hFHoGYXF2x+ZkxGC1YCw145aXSoPcFJxVfklERERERET+QJSIEBERERERERERERGRWuNU3wGIiIiIiIiIiIiIiMiflxIRIiIiIiIiIiIiIiJSa5SIEBERERERERERERGRWqNEhIiIiIiIiIiIiIiI1BolIkREREREREREREREpNYoESEiIiIiIiIiIiIiIrVGiQgREREREREREREREak1SkSIiIiIiIiIiIiIiEitUSJCRERERERERERERERqjRIRIiIiIiIiIiIiIiJSa5SIEBERERERERERERGRWqNEhIiIiIiIiIiIiIiI1BolIkREREREREREREREpNYoESEiIiIiIiIiIiIiIrVGiQgREREREREREREREak1SkSIiIiIiIiIiIiIiEitUSJCRERERERERERERERqjRIRIiIiIiIiIiIiIiJSa5SIEBERERERERERERGRWqNEhIiIiIiIiIiIiIiI1Jr/D2CGnlGC8WibAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot(embeddings.search(\"SELECT id, text, score FROM txtai ORDER BY reactions desc\", 25, graph=True))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's look at a post to show how it looks." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'id': 'Building Large Language Models',\n", + " 'text': \"A Little guide to building Large Language Models in 2024\\n\\nThis is a post-recording of a 75min lecture I gave two weeks ago on how to train a LLM from scratch in 2024. I tried to keep it short and comprehensive – focusing on concepts that are crucial for training good LLM but often hidden in tech reports.\\n\\nIn the lecture, I introduce the students to all the important concepts/tools/techniques for training good performance LLM:\\n* finding, preparing and evaluating web scale data\\n* understanding model parallelism and efficient training\\n* fine-tuning/aligning models\\n* fast inference\\n\\nThere is of course many things and details missing and that I should have added to it, don't hesitate to tell me you're most frustrating omission and I'll add it in a future part. In particular I think I'll add more focus on how to filter topics well and extensively and maybe more practical anecdotes and details.\\n\\nNow that I recorded it I've been thinking this could be part 1 of a two-parts series with a 2nd fully hands-on video on how to run all these steps with some libraries and recipes we've released recently at HF around LLM training (and could be easily adapted to your other framework anyway):\\n*`datatrove` for all things web-scale data preparation: https://github.com/huggingface/datatrove\\n*`nanotron` for lightweight 4D parallelism LLM training: https://github.com/huggingface/nanotron\\n*`lighteval` for in-training fast parallel LLM evaluations: https://github.com/huggingface/lighteval\\n\\nHere is the link to watch the lecture on Youtube: https://www.youtube.com/watch?v=2-SPH9hIKT8\\nAnd here is the link to the Google slides: https://docs.google.com/presentation/d/1IkzESdOwdmwvPxIELYJi8--K3EZ98_cL6c5ZcLKSyVg/edit#slide=id.p\\n\\nEnjoy and happy to hear feedback on it and what to add, correct, extend in a second part.\",\n", + " 'author': 'thomwolf',\n", + " 'url': 'https://hf.co/posts/thomwolf/706415412818350',\n", + " 'date': '2024-03-28T21:44:02.000Z',\n", + " 'reactions': 79,\n", + " 'views': 4844,\n", + " 'comments': 2}]" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "embeddings.search(\"SELECT id, text, author, url, date, reactions, views, comments FROM txtai WHERE id='Building Large Language Models'\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Graph traversal\n", + "\n", + "As we've now seen, embeddings indexes that have an associated graph network have relationships!\n", + "\n", + "These relationships can also be utilized to explore the dataset. The query below walks relationships between the `Argilla Joins Hugging Face` and `Hugging Face Growth` nodes and plots the graph.\n", + "\n", + "The search syntax below uses openCypher. Learn more about this in [txtai's documentation](https://neuml.github.io/txtai/embeddings/query/#graph-search)." + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABiIAAALJCAYAAAA57jvpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdeXwU9f0/8Ncce8zu7OxuQgg3eCMGpSiVSj2o969WrShabQVvrVap1qLVen35VsVarVVrK61aDyzela+VasUDbRXBg0MOQY4ABkj2mj1ndub3R9yBkBBy7GZzvJ6PRx+PZo+ZzyabjXxe836/hTFjxtggIiIiIiIiIiIiIiIqAbHcCyAiIiIiIiIiIiIiot6LQQQREREREREREREREZUMgwgiIiIiIiIiIiIiIioZBhFERERERERERERERFQyDCKIiIiIiIiIiIiIiKhkGEQQEREREREREREREVHJMIggIiIiIiIiIiIiIqKSYRBBREREREREREREREQlwyCCiIiIiIiIiIiIiIhKhkEEERERERERERERERGVDIMIIiIiIiIiIiIiIiIqGQYRRERERERERERERERUMgwiiIiIiIiIiIiIiIioZBhEEBERERERERERERFRyTCIICIiIiIiIiIiIiKikmEQQUREREREREREREREJcMggoiIiIiIiIiIiIiISoZBBBERERERERERERERlQyDCCIiIiIiIiIiIiIiKhkGEUREREREREREREREVDIMIoiIiIiIiIiIiIiIqGQYRBARERERERERERERUckwiCAiIiIiIiIiIiIiopJhEEFERERERERERERERCXDIIKIiIiIiIiIiIiIiEqGQQQREREREREREREREZUMgwgiIiIiIiIiIiIiIioZBhFERERERERERERERFQyDCKIiIiIiIiIiIiIiKhkGEQQEREREREREREREVHJMIggIiIiIiIiIiIiIqKSYRBBREREREREREREREQlwyCCiIiIiIiIiIiIiIhKhkEEERERERERERERERGVDIMIIiIiIiIiIiIiIiIqGQYRRERERERERERERERUMgwiiIiIiIiIiIiIiIioZBhEEBERERERERERERFRyTCIICIiIiIiIiIiIiKikmEQQUREREREREREREREJcMggoiIiIiIiIiIiIiISoZBBBERERERERERERERlQyDCCIiIiIiIiIiIiIiKhkGEUREREREREREREREVDIMIoiIiIiIiIiIiIiIqGQYRBARERERERERERERUckwiCAiIiIiIiIiIiIiopJhEEFERERERERERERERCXDIIKIiIiIiIiIiIiIiEqGQQQREREREREREREREZWMXO4FEBERERER9RaGW0VaG4JMYBDyLgW2IEGw85CMNLyJzVDitXDl9HIvk4iIiIioSzGIICIiIiIi6oS0OhD1w45AvP9omJ5A441WHgJs5zE2BECUAAByNgFt6xJUbvgAir6lHEsmIiIiIupSwpgxY+w9P4yIiIiIiIgKbADx/jXYttdEpEIjACvvBA1t8s3jfZF1qFo3H9rWpRBKtVgiIiIiojJjEEFERERERNQOhjuA2lFnIlFdA1gWIHZi9N43gUSgbimGLH8erlyieAslIiIiIuomGEQQERERERG1UbT6YNQeNBmW5G5fBcSeWHmI+RyGLJuDUN3nxTsuEREREVE30IlLd4iIiIiIiPqObcOPwoYxU2DJnuKGEAAgSrBkDzaMmYJtw48s7rGJiIiIiMqMQQQREREREdEebBt+FLaMPK3xC6FE/4z65rhbRp7OMIKIiIiIehUGEURERERERK2IVh+8I4ToIltGno5o9cFdek4iIiIiolJhEEFERERERLQbhjuA2oMmA7bVtSe2LdQeNBmGW+3a8xIRERERlQCDCCIiIiIiohbYAGpHndk4mLpU7Zh2RxBhSW5sGnUm7K49MxERERFR0TGIICIiIiIiakG8fw0S1TXFH0zdVqKEePVoxPvXlOf8RERERERFwiCCiIiIiIioBdtGTASsLm7JtCsrj20jjinvGoiIiIiIOolBBBERERERlUR1dTXmz5+PffbZp+jHnj17NiZNmuR8PX/+fEyYMKFox0+rA5EKjwDEjv2T6UejK7B2WhGGTYsSUuG9kFYHdP5YvdR9992HK6+8stzLICIiIqJWyOVeABERERERtU84HMZ5552H8ePHo6qqCrquY/PmzXjjjTcwb948ZLPZkp7/vvvuw5gxYwAAuVwOsVgMq1evxuuvv4733nvPedy2bdtwxhlnIBaL7fGY1dXVePbZZ3HxxRdjzZo1e3z85Zdfjkwm0+HX0JITTzwRV111FX7wgx+gftgRgJUvaVumI4aq+OV3B6KmvwKPLGJLIoeFm5KY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8fbbb6O2thazZ8/GwIED8eabb2LhwoXYsGEDXnjhhQ4HVhs2bAAADBjQGNace+65ePPNN/HCCy9g06ZNWLZsGf7whz/ghBNOgMvlQnV1NdLpNP7zn/+grq4OX375JV588cUWjz106FAcdthh+O1vf4svvvgCq1evxj333AOv19vssa+//jreeustbN68GbNmzYLP58PIkSM79JqIiIiIqHVszURERERE1MNdccUVEAQBN998M1wuFwBg3333haIoeOWVV5o81u12Y9CgQQCAYcOGYe7cuU3uX7p0qbN5f9xxx+Haa6917ps+fTqWLFnS6loEQdjtff/4xz9w++23Y7/99sPHH3+MBQsWYNmyZXt8fWvXrm3TMOLly5c3+XrZsmXYd9999/i8XeVdCgTYsAGMqlIgiwI+vHRUk8d4JBEN6TwAYP9+XvzfqmiT+xduTuLYvbXdnsOygZ+9th6/eXczjhwewKGDfPj5d6px9fhqHP/ECtQlTSzeksKK7WmcM7oSD3ywCd+fMBZ1dXX47LPPADRupN9zzz3429/+hoULF+I///kPPv7441Zf2z777IOampomFRCiKMLj8cDj8TiDzteuXevcX2ittOttHo8HPp8PqVSq1XPuqvAesW3bWdPee++N4447rsnjJEnCwIED8fHHH6Ourg7PPPMMPvroIyxcuBDvvfdei0PZhw0bBtM0sXr1aue2zZs3Ix5vHgzt/HoymQx0XUc4HG7XayEiIiKitmEQQURERETUQ2zatAmWZWHo0KFNbt+yZQsANNmY9Xq9aGhowLRp05odp3Dl/568//77TTb3t2/f3urjRVHE4MGDsWLFihbv/+ijj3DOOedg/PjxOPTQQ3Hvvffi5ZdfxiOPPNLqcdvShmpPCpveOwclstzyP4dsYceQar9LgmnZOPbxFchbTR+XNPKdXtcW3cCcZQ2Ys6wBv3l3Cz66bBSmfqsKdy9o/Jk+9Vk9LhpbhQc+2IQfThiNf/7zZee5q1evxrnnnovDDz8cY8eOxa233opFixY5sxNaoigKHn/8cWd2xs5yuZzz/3cOfgrfu5Zuay142p3hw4c3vvZv3reKomDu3LlN5n0UbN26FaZp4tJLL8WYMWMwbtw4TJ06FVOmTMHll1/uzPvoiJbCrY68HiIiIiLaMwYRREREREQ9RDwex6JFi/DDH/4QL730Uqsb9KtXr0ZFRQXy+Tzq6upafMyGDRtQU1ODefPmObfV1NRg/fr1AIB0Oo10Ot3m9Z144onQNK3FTe6CWCyGefPmYd68eViyZAkuu+wyPPLII86msCRJu33unowaNQr/+te/mnz95ZdfAtjR+qmystIJYnatljBNE6IoQrB3BAxL6lKQRQH9fDL+W9vypveq7RkcOtDf5LbDBvlbfGxrYtk86nQTPteO9kxzljbg1mMG45LDqrHvwArcvNPPCgBSqRTmz5+P+fPn491338XMmTMRCASQSCRgGEaz7+fq1asxdOjQPQ6ZLqUzzzwTuq5j0aJFzpqGDx/e6posy8LixYuxePFiPPHEE3j11VcxduxYvPfee00et2HDBsiyjP322w+rVq0C0DiDQtN2X51CRERERKXHGRFERERERD3I/fffD0mS8Mgjj2DixIkYNmwYhg4diuOOOw7Dhg2DZTVetr9o0SIsW7YMM2bMwGGHHYbq6mocdNBBuOiii7D//vsDAJ599lmceOKJOPXUUzF48GCcddZZOPLII/H3v/99j+vwer0Ih8Po168fDjzwQFx66aX4+c9/jldeeQWffvppi8+54IILMGHCBAwaNAgjRozA+PHjnXkBkUgEmUwG48aNQzgcht/f/o38o48+GieffDKGDBmCqVOnYuTIkXjppZcANFaT1NXVYerUqRg8eDDGjx+PyZMnN3n+119/DZ/PhyP2rUZYcUGRBayJZPHc0gY8fMoInLJ/CMOCbowd6MO08dU4fp/Gze1HF23FsXtruPLb/bF32IOLx1a12pYJAKaM6YffnjAUx4wIYETIjQP6eXHrMYMwsp8X876MOY+LZfOYuyqK2743FB8sW9ukKuWss87C9773PQwdOhRDhgzB0Ucfjfr6eido+frrrzF27FiEw2GoauNQ7L/97W844YQTcP7552PEiBEYNmwYJk6ciAsvvLDd3++2UFUV4XAY1dXVOPTQQ3Hbbbfh2GOPxf333+9UM8yePRsHHXQQrr76auyzzz4YPHgwJkyY4AyrHj9+PM444wzss88+qK6uxgknnABBEJrNSgGAjRs34uOPP8Z1112HkSNHYt9998V1111XlKoaIiIiIuo4VkQQEREREfUgmzdvxiWXXILzzjsPF198MaqqqmAYBtavX4+///3vTWZC3HDDDbj44ovxy1/+EqFQ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iKhLMYggIuplDMPY7Wb9IYccgssuuwz77LMPEokE5s2bh7/85S+wvplvMHv2bDz//PN44YUXnOc8+uijWLBgAZ544gkAjW147rnnHowfPx7jxo3D9u3b8cc//hEffPCB85zDDz8cV155Jfr374/ly5dj3rx5TdahaRquvvpqHHzwwQgEAti8eTOefvppvPXWWwAaKxLGjBmDMWPG4MwzzwQAnHPOORgwYADuv/9+nHLKKU5VxFFHHYULLrgAgwYNQkNDA1588UU899xzzrlmz56NuXPnYvDgwTj66KORSCTw1FNPYe7cua1+H1evXo1BgwbhqKOOwptvvumca+vWrdiypXHTyHCrMD0BuCUBt08cjB8eGEbAI+HTLSnc/O9afPJ1yjnecXtr+N/jhmBwwI2PNyfx96X1zc55+BA/fn30YIwZ4END2sT/rYrif97ZjJTR9rYftiDCdKuQW7iqtqqqCj/96U9x2GGHwbIsLFmyBH/4wx9QV1cHADjggANw8cUXY7/99oMkSVizZg0eeughrF692jnG4MGDcf311+PAAw/E5s2b8eCDD7b7PNOnT4eqqlixYgVOP/10GIaBc889FwDg8/lw880344gjjoCu63jmmWfw8ssvO8fu378/rr76aowdOxaWZWHhwoV44IEHnPf8oEGD8NOf/hQHHnggFEXB+vXr8eijj2Lx4sXOMWbPno3XXnsNQ4YMwYQJE/Dee+/h7rvvxoknnogLLrgAwWAQCxcuxJIlS5q8rn322QdXXnklDjjgANi2jU2bNuHee+/FqlWr2vzzIaKeSRRFBAIBAEAymYRlWfD5fMjlcjBNE4q+BUOWvwAsfwHwhSAMPABbEYApemCLEgQrD8lMw5vYDF+8tsXPaCIiIiKi3oxBBBFRH9GvXz/ceeedmDdvHu68804MGzYMv/jFL5DL5ZyQoa2mTJmCP/3pT3jkkUdwxhln4KabbsI555yDRCKBqqoq3HHHHXj55Zcxd+5cHHDAAbjiiiuaPN/tdmPVqlWYPXs2UqkUxo8fj1/96lfYvHkzVqxYgQcffBBDhgzBunXr8Ne//hVAY1XHgAFN5x/sv//+uOWWW/DEE09g/vz5OOiggzBt2jTE4/Em4cdZZ52Fxx57DE899RSOPvpoTJs2DZ999hk2btzY6uv85z//iZNOOskJIk4++WT885//xJgxYwAAaW0IAOC2iYPxgwNCuPL/1qM2lsPPxlfjubP3xWF/WoZoJo9BAReeOGNv/GXxNvzt0+0YM8CHO743pMm5RoTcmDN5X/zm3c24+rX1qPTJuPv4obj7+KH42Wvr2/XzSWlDoG1f0eQ2SZIwc+ZMLF++HFdffTXy+Tx+8pOfYObMmbjoootgmiZ8Ph/mzZuHBx54AIIgYPLkybjrrrvw4x//GOl0GoIg4I477kAkEsFPf/pT+P3+Zu2z2nIeABg7dixSqRSuv/76Js8/++yz8fTTT+Pxxx/HuHHjcNVVV2Hjxo1YtGgRBEHAjBkzkE6nMW3aNEiShGuuuQa33HKL00ZLURR8+OGHmDVrFgzDwAknnIDf/OY3OP/887F161bnPJMnT8bf/vY3571/4IEH4vrrr8esWbOwYMECfPvb38bUqVObrO2mm27C6tWrcd9998GyLOy7777I57tHWy4iKq1CCJHL5ZDJZOByueByuRCNRps91mtloMbWwN6+vYtXSURERETUfXFGBBFRL/Od73wHr732mvO/W2+9FQBw2mmnYdu2bfj973+PjRs34v3338fjjz+OyZMnN/a7bofXX38db731FjZv3oxZs2bB5/Nh5MiRznk2b96MP/7xj9i4cSPefPNNvP76602ev337dsyZMwdr1qzBli1b8NJLL+Gjjz7CMcccA6DxalPTNJHJZBCJRBCJRJyqjZ2dddZZWLx4MZ588knU1tZi3rx5ePnll3HOOec0edyHH36IV155BZs3b8bs2bMRi8WcMKE1b7zxBkaPHo3q6mpUV1ejpqYGb7zxhnN/JjAIPsnGBd/qh1vnb8K/18axsj6Daf9cj4xp4ccHVwIALvxWFdZFsrjlrU34siGL55dH8OySphUR074zAM8va8CfPt6GtZEsFm5K4sY3N+Lsmgp4pPb9fDKB5jNBJk6cCFEUcc899+Crr77Chg0bcPfdd6N///7O9+KTTz7Bm2++iY0bN2LDhg2499574fF4cMghhwAADj30UAwbNgx33nkn1qxZg88//xyzZs1q93kAIJPJ4J577sG6deuwbt065/alS5di9uzZqK2txUsvvYR33nnHqYoZO3Ys9t57b8yYMQOrVq3CF198gTvvvBNjxozBAQccAABYs2YNXn31Vaxbtw6bNm3CY489hs2bN+OII45oss5PPvkEzz33HDZv3ozNmzdj0qRJWLhwIZ599lnU1tbixRdfxMKFC5s8p3///li8eDE2btyITZs24Z133sGaNWva9bMhop5HURS43W4AQCKRANBYvWUYBgzDaPZ4SZJa/JtFRERERNSXsSKCiKiX+eSTT3Dfffc5X2cyGQDAsGHDsGzZsiaPXbp0KXw+H6qqqppcLb4na9eubXJ8XdcRDoed83zxxRdNHr98+fImX4uiiPPOOw/HHHMM+vXr51xZms1m27yGwrnef//9Zq9p0qRJEEXR2Qjaeb0AEIlEnPW2JhaL4b///S9OPPFECIKA//73v4jH4879eZeCvUJuuCURH9buGKBtWsDiLSns388LANi/nxeLtjQdsL1wc9Ova/orGFWl4MyDKpzbBACSKGB4yINV9Zk9rreR3eKciH322QeDBw/Ga6+91uR2t9vtDDMPh8O48MILMWbMGIRCIUiSBI/Hg+rqagDA8OHDsXXrVtTX7whRdv3ZtuU8QOPPpKW5DLseb/ny5Zg0aVKT82/bts25f/369UgkEhg+fDhWrlwJr9eLqVOnYvz48aisrIQkSXC73c5rKFi5cmWTr4cNG4YFCxY0O/e3v/1t5+vnnnsOv/jFL3D88cdj0aJFeOedd7B58+Zmr4GIeg9JkuD3+wEAuq7Dsiy4XC643e7dzl+SJInVUkREREREu2AQQUTUy2QymQ5vjlqW1aw6Qpab/6loaQO5PVUVZ599NiZNmoQHH3wQX331FdLpNK666qoWz1UMu67Xtu02r/ef//wnrr76agDA73//+6bHEYo3pNrvkvDEp9vx54+3NbuvNp5r17HsFoZnK4qCVatWYcaMGc3uK2ym3XDDDdA0DQ8++CDq6uqQy+Xw0EMPtevn0pbzADsCsmK74oorcOihh+KRRx7Bpk2bkM1mcfvttzd7DR05/xNPPIF///vfGD9+vNO66X/+53+aBRhE1HtoWuPg6UJLJqCxGsI0TeRyLX82S5K02/uIiIiIiPoqBhFERH3Ehg0bcNRRRzW5raamBslk0rnCPBaLobKy0rnf5/M1m8vQlvPs2gbnwAMPbHbe999/35m9IAgChgwZgvXrd8xCMAwDoth6B8ENGzagpqam2bFra2uL1hbjo48+cjaxd23VI9h5rItmkTUtHD7Ej9rljRtPsgh8a4APj3zcWGWyansGJ+0XbPLcwwb5m3z9WV0KB/Tz4qto+6pCWiJYza/EXb16NSZOnIhoNIpUKtXCsxq/d/fffz8+/PBDAI1Dp0OhkHP/+vXr0b9/f1RUVKChoQEAMGrUqHafpzW7vlcOPPBA531ROH9VVZXznh0+fDgCgYDT3qmmpgbz5s1zwgGv19um9/CGDRtaPPeuamtr8fzzz+P555/HzTffjJNOOolBBFEv5ff7IUmNwa6uNw6XlmUZbre7SXXcrlgRQURERETUHGdEEBH1Ea+88gqqqqpw9dVXY+jQoZgwYQKmTp2K5557DrZtAwAWL16M448/HqNHj8Zee+2FG264od0b+v/4xz8wePBgXHbZZRg6dCiOPfZYnHTSSU0es2nTJhx66KE46KCDMGzYMFx77bXNWiXV1dXhwAMPRHV1NTRNa7GCYc6cORg7dix+8pOfYMiQITjxxBNx+umn4+9//3s7vzu7Z1kWpk6diqlTpzb7XkhGGknDxmOfbMftEwfje3tpOKDSi/tPHg7FJeKpzxpbGD326TbsHfbgtomDsW+FB5NGhXHO6Momx3rgv3UYN1jF3ccPQU1/BXuHPTh5vyDuPr7pUOs9UWQRBw3UsM8++zj/GzRoEN58803EYjHMmDEDo0ePxoABA3DIIYfgZz/7Gfr16wegcZP9+OOPx7Bhw3DggQfipptualI5sGjRItTW1uKGG27APvvsg9GjR+Oiiy5qcv62nKc1NTU1OOecczBkyBCcfvrpOOaYY/Diiy8651+7di1uuukm7Lfffhg5ciRuvPFGfPrpp1i1apXzGo488kjntd98881tqn554YUXMG7cOEyePBmDBw/G6aef3qQtk9vtxtVXX41DDjnEmRcycuRIbNiwYc8/FCLqcVwuFxSlsc1dMpl0goVCNcTuWgmKoghBEBhEEBERERHtghURRER9xPbt23HjjTfisssuw6xZs5BIJPDaa6/hySefdB7zzDPPYODAgfjNb36DZDKJxx57DAMHDmzXebZu3Ypbb70VV155Jc444wx88cUXmDVrFqZPn+485sknn8TAgQMxc+ZMZDIZzJ07F++//77ThxsA/v73v+OGG27A448/Dq/X22wANdB49f0dd9yBCy64AD/5yU9QX1+Pxx57DPPmzevAd2j3dndlvzexGRAl3PH2JogC8McfDIfqlvDplhTO+vuXiGUbN6I2xQ1MfWkt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hO3bt+M///kP7rjjDjz99NO488474fP5wDAMCgoK8Pzzz8NutyMjIwOPPvooioqK8N1336nX1bdvXxQVFeGRRx5Bu3bt8Oyzz+Lo0aP44osvAJRWpPXu3RtvvPEGjh07hjZt2qjVLXq9Hq+++io2bdqEJUuWQJIkTJo0Cc899xwee+yxWt3mhNQFBRGEEFIFZRefKIpwOp01aoVA6oeqH0hLpoQNirLBBMMwCIfDUcFEbYK5cDistjFSwgilQsLt8eB071GQObFxQwgAYFjIvIgzGXfAeHA17HY7OI6DyWSCKIqQZZnaL5EqDRgwAJ988knUadnZ2Rg3bly9LvfMmTPYtm0b5s2bB5PJhB9//DFqkeeTTz6BXq/H1KlTYbFYcOrUKcyePRtnzpxRA4BTp07B6XTizJkzkGUZkiThwIED4DgOWVlZUWHB2rVr4fV61R2abrcbx48fxyeffIKUlBR1ISElJUVtgQOULmQrIQNQ+tzBcRw0Go260K9cRzgchizLePXVVzF9+nT84x//QElJCd5//31MnjxZHU6t/FzkV0FBAaZMmYK77roLt912G9LT0+F0OnH8+HG899578Pv94HkeCxYswJQpU/Dcc8+B53ns3bsXM2fOVGdvKLs2K6v40jjOqsPt6+twkRdPbM7BI1e0xmMDW2PjISuW7MjDfX1b1e6CGID3ng+2li5dimnTpuGmm25CYWEh7rrrLvV7mzZtQkZGRo2HVN988824+eab4ff7YbfbcfjwYfz973/H9u3b1fP88MMPWLduHR5++GEIgoCffvoJH374IcaPH6+e53//+x+uuuoqLFy4EEajES+99BI2b96MJUuWYMyYMZg4cSL27t2Lf/3rX3j66aerPKbvv/8eDz30EMaOHYs5c+ZAr9cjPz9fHXZdlffeew82mw1333032rRpA6fTiSNHjqjVHJ9//jm6deuGZ599FuFwGN9++y3Wr18ftei6ceNG9OnTRw0BZ8yYERVUAEBhYSGeeuopTJ48WW07tWnTJnz44Yc1ut0JiQVZljF//nw89thjuPXWW3HkyBHs2bMH3377rTqfpF+/frjgggtw1113qdU68+bNw/Lly9GjRw8cOnQIY8eOxbfffovly5erl33sWOWL8ZG75PPy8vDuu+/i0UcfrXUQAQBffvklbrjhBmzZsgXDhg3DTz/9BKvV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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "g = embeddings.graph.search(\"\"\"\n", + "MATCH P=({id: \"Argilla Joins Hugging Face\"})-[*1..3]->({id: \"Hugging Face Growth\"})\n", + "RETURN P\n", + "LIMIT 30\n", + "\"\"\", graph=True)\n", + "\n", + "plot(g)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Agents\n", + "\n", + "By now, we should get the idea on graphs! Time to move on to agents. Agents automatically create workflows to answer multi-faceted user requests. Agents iteratively prompt and/or interface with tools to step through a process and ultimately come to an answer for a request.\n", + "\n", + "Let's connect our database to an agent." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "from txtai import Agent\n", + "\n", + "posts = {\n", + " \"name\": \"posts\",\n", + " \"description\": \"Searches a database of technical blog posts\",\n", + " \"target\": embeddings\n", + "}\n", + "\n", + "agent = Agent(\n", + " tools=[posts],\n", + " llm=llm,\n", + " max_iterations=10,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32;20;1m======== New task ========\u001b[0m\n", + "\u001b[37;1m\n", + "You're looking to learn more about hugging face and open source AI. Do the following.\n", + " - Search for posts related to the topic.\n", + " - Write a report with references hyperlinks.\n", + " - Write the text as Markdown.\n", + "\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: I will start by searching for posts related to the topic of Hugging Face and Open Source AI.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'posts' with arguments: {'query': 'Hugging Face Open Source AI'}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: Now that I have the search results, I can write a report with references and hyperlinks.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'final_answer' with arguments: {'answer': \"### Hugging Face and Open Source AI\\n\\nHugging Face is a leading player in the open source AI ecosystem. Their commitment to democratizing AI has led to significant growth in the community, with over 1 million public models available on their platform.\\n\\n#### Key Developments\\n\\n* **Hugging Face's AI Initiative**: The company has committed $10 million in free GPUs to help developers create new AI technologies, promoting collaboration and transparency in the AI community.\\n\\n* **Open Source Models with Hugging Face**: Hugging Face has been instrumental in making hundreds of thousands of already-trained open source models available for developers to assemble into new applications.\\n\\n* **Hugging Face Competitions**: The company has launched an open-source platform for creating machine learning competitions, allowing developers to create and host competitions for free.\\n\\n#### References\\n\\n* [Huggingface AI Ecosystem](https://huyenchip.com/2024/03/14/ai-oss.html)\\n* [Hugging Face's AI Initiative](https://www.theverge.com/2024/5/16/24156755/hugging-face-celement-delangue-free-shared-gpus-ai)\\n* [Open Source Models with Hugging Face](https://www.deeplearning.ai/short-courses/open-source-models-hugging-face/)\\n* [Hugging Face Competitions](https://github.com/huggingface/competitions)\\n* [Hugging Face Milestone](https://huggingface.co/spaces/cfahlgren1/hub-stats)\"}\u001b[0m\n" + ] + }, + { + "data": { + "text/markdown": [ + "### Hugging Face and Open Source AI\n", + "\n", + "Hugging Face is a leading player in the open source AI ecosystem. Their commitment to democratizing AI has led to significant growth in the community, with over 1 million public models available on their platform.\n", + "\n", + "#### Key Developments\n", + "\n", + "* **Hugging Face's AI Initiative**: The company has committed $10 million in free GPUs to help developers create new AI technologies, promoting collaboration and transparency in the AI community.\n", + "\n", + "* **Open Source Models with Hugging Face**: Hugging Face has been instrumental in making hundreds of thousands of already-trained open source models available for developers to assemble into new applications.\n", + "\n", + "* **Hugging Face Competitions**: The company has launched an open-source platform for creating machine learning competitions, allowing developers to create and host competitions for free.\n", + "\n", + "#### References\n", + "\n", + "* [Huggingface AI Ecosystem](https://huyenchip.com/2024/03/14/ai-oss.html)\n", + "* [Hugging Face's AI Initiative](https://www.theverge.com/2024/5/16/24156755/hugging-face-celement-delangue-free-shared-gpus-ai)\n", + "* [Open Source Models with Hugging Face](https://www.deeplearning.ai/short-courses/open-source-models-hugging-face/)\n", + "* [Hugging Face Competitions](https://github.com/huggingface/competitions)\n", + "* [Hugging Face Milestone](https://huggingface.co/spaces/cfahlgren1/hub-stats)" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import display, Markdown\n", + "\n", + "def md(output):\n", + " display(Markdown(output))\n", + "\n", + "researcher = \"\"\"\n", + "You're looking to learn more about {topic}. Do the following.\n", + " - Search for posts related to the topic.\n", + " - Write a report with references hyperlinks.\n", + " - Write the text as Markdown.\n", + "\"\"\"\n", + "\n", + "md(agent(researcher.format(topic=\"hugging face and open source AI\")))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There is a lot to unpack here. The agent has access to the Embeddings database as a tool. The prompt asks the agent to research a specific topic, `hugging face and open source AI` and build a report. We can see the series of steps this undertook and the report output (with links pulled out from the posts!). \n", + "\n", + "Very cool 😎" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next we'll make a slight change to our agent. We'll give it a custom function instead of an embeddings database. This function still runs a search but in a more specific way, by author." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": {}, + "outputs": [], + "source": [ + "def search(author):\n", + " \"\"\"\n", + " Searches a database of blog posts by author.\n", + "\n", + " Args:\n", + " author: lower case author id to search for\n", + " \n", + " Returns:\n", + " list of blog post texts for author\n", + " \"\"\"\n", + "\n", + " return embeddings.search(\"SELECT text FROM txtai WHERE author=:author ORDER BY views desc\", 20, parameters={\"author\": author})\n", + "\n", + "agent = Agent(\n", + " tools=[search],\n", + " llm=llm,\n", + " max_iterations=5,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32;20;1m======== New task ========\u001b[0m\n", + "\u001b[37;1mSummarize what clem talks about as a list of 5 items in Markdown format\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: I will first search the database of blog posts by Clem to find what he talks about, then I will summarize the results as a list of 5 items in Markdown format.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'search' with arguments: {'author': 'clem'}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: I will now summarize the results as a list of 5 items in Markdown format.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'final_answer' with arguments: {'answer': \"- **5,000 new repos are created every day on Hugging Face**: The community is amazing and contributing to the growth of open-source AI.\\n- **Hugging Face is profitable**: With 220 team members and most of their platform being free and open-source for the community.\\n- **Clem is excited about Hugging Face's success**: He mentions that they have plenty of money in the bank and are profitable.\\n- **Clem is hosting a live workshop**: With team members next week about the Enterprise Hugging Face hub, and 1,000 spots are available.\\n- **Clem is advocating for open-source AI**: He believes that it creates healthy competition and pushes companies to build AI faster, attract the best scientists and engineers, and generate more visibility and community contributions.\"}\u001b[0m\n" + ] + }, + { + "data": { + "text/markdown": [ + "- **5,000 new repos are created every day on Hugging Face**: The community is amazing and contributing to the growth of open-source AI.\n", + "- **Hugging Face is profitable**: With 220 team members and most of their platform being free and open-source for the community.\n", + "- **Clem is excited about Hugging Face's success**: He mentions that they have plenty of money in the bank and are profitable.\n", + "- **Clem is hosting a live workshop**: With team members next week about the Enterprise Hugging Face hub, and 1,000 spots are available.\n", + "- **Clem is advocating for open-source AI**: He believes that it creates healthy competition and pushes companies to build AI faster, attract the best scientists and engineers, and generate more visibility and community contributions." + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "md(agent(\"Summarize what clem talks about as a list of 5 items in Markdown format\", maxlength=16000))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Even though we've seen LLM outputs a bazillion times by now, it's still amazing to see how good of a job it can do, under the right circumstances.\n", + "\n", + "Let's show the previous graph again. Sounds about right?" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total posts for `clem` = 29. Using up to 20 posts for graph.\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABiIAAALJCAYAAAA57jvpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdd3hUZfrG8e/U1JkkIAmEQCJFWoSAoC4gKzaEtQKLiAU7uKKwFsDVta2/dUFdbFh2LegKKKCLsoKsukFQURAbRToBAiEJKTOZzCRTf3+EDAQCBMgklPtzXbkucs6Zc55MBpg59/u+jyErKyuEiIiIiIiIiIiIiIhIBBgbuwARERERERERERERETl5KYgQEREREREREREREZGIURAhIiIiIiIiIiIiIiIRoyBCREREREREREREREQiRkGEiIiIiIiIiIiIiIhEjIIIERERERERERERERGJGAURIiIiIiIiIiIiIiISMQoiREREREREREREREQkYhREiIiIiIiIiIiIiIhIxCiIEBERERERERERERGRiFEQISIiIiIiIiIiIiIiEaMgQkREREREREREREREIkZBhIiIiIiIiIiIiIiIRIyCCBERERERERERERERiRgFESIiIiIiIiIiIiIiEjEKIkREREREREREREREJGIURIiIiIiIiIiIiIiISMQoiBARERERERERERERkYhRECEiIiIiIiIiIiIiIhGjIEJERERERERERERERCJGQYSIiIiIiIiIiIiIiESMubELEBEREZEqPms8HnsaFbZUApYYQgYThlAAk89DdNlOYpy5WLyuxi5TRERERERE5IgoiBARERFpRJ74FhS17o0z+Uz8UbaqjcEABkLhY0IYwGgCwFxZhr1gJU23fUOMK68xShYRERERERE5IoasrKzQ4Q8TERERkfoSApzJmRSe3h93YgYEA+GgoU72HB9bkkOznGzsBaswRKpYERERERERkWOkIEJERESkAfmsNnI7D6UsJROCQTAeQ8uuPYGELX8VaWvmYPGW1V+hIiIiIiIiIvVEQYSIiIhIAylN6Upul2EETdYjmwFxOMEAxoCXtNWzSMz/pf7OKyIiIiIiIlIPjmEInoiIiIjUVWF6P7ZljSRojqrfEALAaCJojmJb1kgK08+r33OLiIiIiIiIHCMFESIiIiIRVpjej7yOV1Z9Y4jQ2689583reJXCCBERERERETmuKIgQERERiaDSlK57Q4gGktfxKkpTujboNUVEREREREQORkGEiIiISIT4rDZyuwyDULBhLxwKkttlGD5rfMNeV0RERERERKQWCiJEREREIiAE5HYeWtWYOlLLMR2MwUjQZGVH56GEGvbKIiIiIiIiIgcwN3YBIiJyavFZ4/HY06iwpRKwxBAymDCEAph8HqLLdhLjzMXidTV2mSLHzJmcSVlKZuMVYDThTDkTZ3ImCQWrGq8OEREREREROeUpiBARkYjzxLegqHVvnMln4o+yVW0MBjDsM1Y7hAGMJgDMlWXYC1bSdNs3xLjyGqNkkWNWmNEfgkEwNuIE1GCAwozzFUSIiIiIiIhIo9LSTCIiEhEhwJGcycZz7mZDn/spbnnO3hACwGgiZDSHv6pDCAB/lI3iluewoc/9bDz7bhzJmVpephHMnDmTIUOGNHYZJyRPfAvcSRkHDSFe+l06/xrcps7n69M6nqKJPbBHmQ5/8L6MJtxJp+OJb35kj2tgU6ZM4a677gp/X5fXXnZ2Nn369AEgJSWF7Oxs2rZtC0C3bt3Izs4mLi4uckWLiIiIiIhInSmIEBGReuez2sjJupmt3W/GbW9dtdF45DdQAdwJrdja/WZysm7GZ7Ud5kEnrgkTJvCXv/zlgO2NeUN19OjR/Oc//4n4dWbOnEl2dnaNr1mzZkX8ugdTXUOnTp1qbLdYLMydO5fs7Gy6det2yHMUte4NwUAkyzyoceemUDC+O2POTq7aEAxQ1LpPo9RSV4888ghvvvnmET1m8ODBLFu2rNZ9q1evZvDgwZSXlwMwYMAA5s2bd8x17h94iIiIiIiISN1oaSYREalXpSldye0yrKpBLxz7sjR7AomyZp1Y13cCaatnkZj/yzFWKXXhcDga7FpvvvlmjdAjGAw22LVrk5+fz8CBA/n111/D2/r27YvH4yEhIeGwj3cmn3nk4Vs9GdG1KS9+l891XU/jpWUFVb0ikjNhzQeNUk9dlJWVHfFjSkpKDrrP7/cfcr+IiIiIiIg0LAURIiJSbwrT+5HX8UoIBcFQz5PujCaChii2ZY3Et3YuzbYuqd/znyBGjhxJ3759uf3228PbhgwZwtChQ7n22msBMBqN3HXXXVxyySUEAgHmz59PkyZNiIuL489//jMAMTEx3HvvvfTp0we32817771Hnz592LhxI1OnTgWqZirMmTOHDz6ouoGdnZ3N008/zbnnnkuvXr3YvXs3r7zyCt988024lt69e3PnnXeSnJzM6tWrWbhwIRMnTuSyyy4Lj06vjdvtPuDGsdFo5L777qN79+40adKE/Px8Pv7443A91QYOHMiwYcNITU2lrKyMxYsX88ILLwAQFxfHnXfeSZ8+fbBYLKxbt46XX36ZTZs2HfJ5XrhwIYMHD+all17C6/WGr/Pf//6XG2+8scaxp59+OmPGjKFLly5UVFTw5ddLGfNrAn5fVZhiNMDj/VtyXdemBELw7s9FGPa7ngEYe24KN2adRnKchU0lFTzz9S7mrSs9ZJ37690qnhizkaeW7OSazCb0ahnH8h3l+KPs+K3xWHzlXHPNNVx22WU0a9aMkpIS5s2bx/Tp0wE47bTTGD16NL169cJisbBt2zaef/75cCDTp08fbrzxRjIyMti9ezcLFy7k3XffDQdHI0eOZODAgSQlJeF0Olm8eDEvvvgiAFdeeSVDhw4lOTkZl8vFypUreeyxx4CqpZn2fe0BxMbG8vDDD9O7d29cLhczZsxg7ty54f3Z2dk8/PDDfP311wc8D926deO5557jsssuo127dkycODH8GIBp06YRCoU4//zzueWWW2o89p///CfffPMNb7311mGf7/j4eMaOHUvPnj2JiYmhsLCQ6dOn8+mnn2I2m/nDH/5Av379sNlsFBcXM2/ePGbMmEFKSgrvvfcet912W/i1GBcXx3/+8x/GjRvHzz//DEBGRgajR4+ma9eueDwevv/+e6ZOnYrT6TxsbSIiIiIiIscTBREiIlIvwiEE1H8IUW3PefM6XgVwyoYRh3Pttddy4YUXMmnSJLZu3cqQIUPo06cPP/30U/iYP/zhD2RmZvLwww9TXFzMzTffTPv27dm4ceMhzz1y5Ehee+01Xn31VQYPHsxDDz3E8OHDKSsro3nz5jz22GN88MEHzJ8/n3bt2nHnnXce9c9hMBgoLCzksccew+l0kpmZyb333ktRURGLFi0C4IorruAPf/gD//znP/nuu++Ii4sjMzMzfI7HHnuMyspKJkyYQHl5OZdffjnPPvssN9xwwyFH4a9fv578/Hz69evH559/TnJyMt26deP555+vEURER0czefJk1qxZw+jRo0lKSuLeCX9iUisDYz7ZCsBdZ6dw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TNu2bUlJSeGSSy7BYDAccjmwzMxMhg8fTlpaGldddRXnn38+H374YZ2ej/3t2rWL2NhYevTogd1uDzeUhqrlmXr06MHZZ5/NggULjui8N998M3369CE1NZWMjAzOPfdctm3bBsDvf/97LrjgAlq1akVaWhq//e1vKSoqwuVy4fV6Wb16Nddeey2tW7emW7duNXqkAMydOxebzcaf//xnOnToQGpqKr169WL8+PFH1TtGRERERESkMWlGhIiIHBVPfAvcSRmNXQYYTbiTTscT35wY167Gribitm3bxnPPPcd1113HjTfeyOLFi5k1axaXXXZZ+JiZM2fSpEkTJk6cSDAY5D//+Q/ff/89gUAgfMzLL7/Mvffey1//+lfcbjfvvfceycnJRzwafF+7du3iscce484772TIkCGsXr2ad999l3vvvfeozjtv3jzatWvHI488QigU4n//+x8fffRRjVkXCxcuxGq1MnToUEaPHo3D4aixdNDEiRO57bbbGD9+PImJiRQXF/PLL7/UaFp8OE6n86D7KisrGT9+PGPGjOHVV1+loqKCL5b/woSfbeFjpi7LJyXewtTfZRAkxPRfivhkfSn2qL1hxF+X5LHb42fcb5qTnmjFURHgl3w3U5YefiaHxWjg912a8MK3tb/+560r5Q9nJ/Ouexf/+lc2gUCAm2++maZNm1JUVMS8efOAqlkD48eP58477+Spp57CZDKxdetWnn/+eQCWL1/On/70J2688UauvfZa/H4/27dv55NPPgGqlrsaMWIEd955JyaTic2bN/PQQw/hdDpxuVycd955jBw5EqvVyo4dO3jyySfJyck56M81e/ZsOnTowI033ojb7ebll19m+fLlh30+alM9s+aRRx4hISGBadOm8fbbbwNVgdeqVauw2+38+uuvR3Ren8/HbbfdRvPmzamsrGTlypU88cQTAOGlxNLS0ggEAqxbt46JEyeGw77JkyfzwAMP8Nprr4WbvD/zzDPhcxcVFXH33Xdzxx138PTTT2OxWMjPz2fZsmWaISEiIiIiIiccQ1ZW1tEPfRQRkUY1YcIE4uPj+fOf/1zr/pkzZzJnzhw++OCDer92buchFLc8B4xHth7+wVx2RiJ//E0KpydFYTYa2FxSycvLCpi1uva10D8a0Z6+rW217oOqmQN//OMfj6qWKVOmsHHjxiNqWns8MxgMTJs2jUWLFh10ZH90dDSzZ8/mlVdeqXXZnqN13XXXccUVV3DNNdfU2zmPdz5rPL/2f7yxyzhA5+xHMZ8kjd3r27vvvstHH33E7NmzG7sUERERERGRk5JmRIiIyFFxJp9ZbyEEQEmFn78v3cWGokq8gSCXtEvgxd+lU+j2kb3lwPXqR364GaupqtFrS5uVz2/qyH333ceWLVuA2psqnypSUlLo2bMnP//8MxaLhauvvpoWLVrwxRdfhI9p164drVu3Zu3atcTFxXHjjTcC8NVXXx3Tta+88krWrl2L0+kML63z73//+5jOeaKxeF2YK8vwRx08KGto5kqnQohaJCQkcMEFF9CkSZMjXpZJRERERERE6k5BhIjIKcpoNHLffffRvXt3mjRpQn5+Ph9//HGN2RPVMy7Wrl3L4MGDsVqtzJ49m2mz5vLwpR25vmtTPL4gTy3ZyYyVe2cuPHp+KoPOSCTVZqWg3Mec1cU8/XUe/kOsJvL1tpo3Sf/xfSHDM5tyblp8rUFEacXeZYaizFXrpRe7feEldzIzM7n99tvp0KEDDoeDr776in/+85/hhsJXXnklQ4cOJTk5GZfLxcqVK3nssceYMGECWVlZZGVlMXToUACGDx9OeXk5Y8eOpWfPnsTExFBYWMj06dP59NNPj/CZj7xgMMill17K6NGjMRgMbNmyhfvvvz+8dn21a665hlatWuHz+Vi/fj333HPPIZchqouWLVty/fXXY7fbyc/PZ9asWUyfPv2YznkishesrNcZQ8ckGMB+ijRzP1Jz586ltLSUZ599FpdLQY2IiIiIiEikKIgQETlFGQwGCgsLeeyxx8Kj1++9916KiopYtGhR+Lju3btTWFjIuHHjyMzMZPz48XTofjb/c/i55J11XN0piWcvbc2inDJ2lvmAqua7Yz7Zyi6Xj87NYphyaWtc3iAvfnf49e6r9Uu30a5JFE8sqvvNwYr4FGAlqampTJ48mTfeeIPJkyeTmJjIPffcwz333MPkyZM544wzuPvuu/nrX//K6tWrsdlsdO3aFYCXXnqJtLQ0cnJyePPNNwFwOByMGTOG9PR0JkyYgMPhoGXLljUa3h5PCgsLufvuuw95zMaNGxk1alS9X/vll1/m5ZdfrvfznmiabvuG4la9G7uMKkYTTbd93dhVHJf69+/f2CWIiIiIiIicEhREiIicogKBANOmTQt/v2vXLjp37sz5559fI4goKyvjxRdfJBQKsX37doYPH441Jp4p83eC0cSUpbu459wUzkmL59+/Vs1GePabvU1ztzu8TF2Wz+BOSYcNImxRRlbddSZRJiOBUIgH/rudRTkHzoY4GG9sMwBGjBjB559/Hp7dsWPHDl588UWee+45pkyZQkpKCh6Ph6VLl+LxeMjPz2fjxo0AlJeX4/f7qaioqNHQODk5mY0bN7J+/XoA8vPrHqrIqSfGlUdsSQ7uhNZgNDZeIcEAsY5tp0QjdxERERERETl+KYgQETmFXXXVVQwcOJDk5GSioqIwm83hG/LVcnJyCIVC4e9LSkpYVxLEQIgQEAxBicdPs9i9/6Vc1TGJO3o2IyMxijirEbPRQFll1VJKLe0Wvrmtc/jY55buYsrSqpv6rsog57+5ljirkX4ZNp68oCVbSysPWLbpYAKmqhkKbdu2pU2bNlx00UU19ptMJlq0aMH3339Pfn4+M2bMYNmyZSxfvpwlS5ZQWVl50HN//PHHPP7447Rv357vv/+er776itWrV9epLjk1NcvJZmv3mxu3CKOJZjmLGrcGEREREREROeUpiBAROUX179+f0aNH88orr7B69WrcbjfDhw+nU6dONY7bv+lzKBTCHwjV3AYYqvpG0zM1jteuyGDSkjz+t8WJszLA1Z2SuOvsZAB2lfk4/8214ceWVPhrnGdLaVUYsKrAwxlNoxl3bnO+3lYzHDmY0J71+GNiYvjPf/5To99FtYKCAvx+P3fccQdZWVn06tWLm266iZEjRzJ69GjKy8trPfeyZcsYPnw45557LmeddRbPPvssc+fO5dVXX61TbXLqsReswpa/irJmnRqnV0QwgL1wjfpDiIiIiIiISKNTECEicorKzMxk9erVfPTRR+FtqampdXx06KB7zk6LY7vDy9+X7l0KplWCNfznQGhv2HA4RgNYzYY61gSGYNWsiw0bNpCens7OnTsPemwwGOSHH37ghx9+4O2332bevHn06NGDJUuW4PP5MNaynI7D4WDhwoUsXLiQlStXMmrUKAURclAGIG3NHNb1nUDQEAWGBlyiKRTEGPDScs0c6v43SERERERERCQyFESIiJzg4uLiaNu2bY1tTqeTwsJCAE477bQD9ufn57Njxw4uueQSevXqRV5eHhdffDEdOnRg167DryVvDPoJUftN1c3FlaTZrVzdKYkf88q5pG0Cvzsj8bDnHHduCj/tcrOlpJIos5GL2toZ1qUp9y/cdtjHVjMFqgKOmTNnMnXqVO655x4++eQTKioqyMjI4KyzzuKFF17g3HPPJTU1lZ9//hmXy8U555yDwWBg+/bt4eenU6dO4V4SZWVl3HTTTaxfv54tW7ZgtVo599xz2bat7rXJqcniLSNt9Sy2ZY1s2AsbjKStnoXFW/dm7yIiIiIiIiKRoiBCROQE1717d15//fUa2z755BOeeeYZAIYPH87w4cNr7P+///s/5s2bR7t27XjkkUcIhUL873//46OPPuKcc8457DVN3nIwJta679ONDl5ZXsCki1sRZTLw300Onvk6jwl9WxzynLEWI5MvaUWqzUqFP8iGogpGz8th7tqSQz5uX1Z3VfiyefNmxo0bx6233soLL7yAwWBg586dZGdnA+ByuTjvvPMYOXIkVquVHTt28OSTT5KTkwPA+++/z8SJE5k2bRrR0dEMHz4cn8/HbbfdRvPmzamsrGTlypU88cQTda5NTl2J+b/gWzuXvI5XNdg1W6ydS2L+Lw12PREREREREZFDMWRlZR18fQ0REZFa+Kzx/Nr/8cYu4wCdsx/FrBHgcpwqTD+vKowIBSOzTNOe86b+OpfTti2p//OLiIiIiIiIHCXNiBARkSNm8bowV5bhj7I1dilh5kqnQgg5rjXbugRLhYPcLsMImqz128A6GMAY8JK2epZmQoiIiIiIiMhxR0GEiIgcFXvBSopbnlO/N1OPVjCAvWBVY1chcliJ+b8QV7KF3M5DKUvJhGDg2P4O7Xm8vXANLdfMUU8IEREREREROS4piBARkaPSdNs3FLfq3dhlVDGaaLrt68auQqROLN4yMn56C2dyJoUZ/XEnZRx5ILHn+FjHNprlLMJesApDxCoWEREREREROTYKIkRE5KjEuPKILcnBndAajBFY776uggFiHduIce1qvBpEjpABSChYRULBKjzxLShq3Rtncib+KHvVAcEABva28QphCAcV5kon9oJVNN32DTGuvEaoXkREREREROTIqFm1iIgcNUdyJlu739zYZZD+41skaGkmOQn4rfG47WlU2FIJmGMIGU0YggFMfg/RZTuJdeaqF4qIiIiIiIiccDQjQkREjpq9YBW2/FWUNevUOL0iggHshWvUH0JOGmavC/vutdh3r23sUkRERERERETqTSOupSEiIic6A5C2Zg7GgBdCwYa9eCiIMeCl5Zo5WhtfREREREREROQ4piBCRESOicVbRtrqWWBo4P9SDEbSVs/ComVqRERERERERESOawoiRETkmCXm/0KLtXMb9Jot1s4lMf+XBr2miIiIiIiIiIgcOQURIiJSL5ptXbI3jIjUMk17zpv661yabV0SmWuIiIiIiIiIiEi9UrNqERGpN822LsFS4SC3yzCCJmv9NrAOBjAGvKStnqWZECIiIiIiIiIiJxAFESIiUq8S838hrmQLuZ2HUpaSCcHAsQUSex5vL1xDyzVz1BNCREREREREROQEY8jKygo1dhEiInLyCQHO5EwKM/rjTso48kBiz/GxJVtolrMIe8EqDJEqVkREREREREREIkZBhIiIRJwnvgVFrXvjTM7EH2Wv2hgMYGDvf0EhDOGgwlzpxF6wiqbbviHGldcYJYuIiIiIiIiISD1RECEiIg3Kb43HbU+jwpZKwBxDyGjCEAxg8nuILttJrDMXs5ZfEhERERERERE5aSiIEBERERERERERERGRiDE2dgEiIiIiIiIiIiIiInLyUhAhIiIiIiIiIiIiIiIRoyBCREREREREREREREQiRkGEiIiIiIiIiIiIiIhEjIIIERERERERERERERGJGAURIiIiIiIiIiIiIiISMQoiREREREREREREREQkYhREiIiIiIiIiIiIiIhIxCiIEBERERERERERERGRiFEQISIiIiIiIiIiIiIiEaMgQkREREREREREREREIkZBhIiIiIiIiIiIiIiIRIyCCBERERERERERERERiRgFESIiIiIiIiIiIiIiEjEKIkREREREREREREREJGIURIiIiIiIiIiIiIiISMQoiBARERERERERERERkYhRECEiIiIiIiIiIiIiIhGjIEJERERERERERERERCJGQYSIiIiIiIiIiIiIiESMgggREREREREREREREYkYBREiIiIiIiIiIiIiIhIxCiJERERERERERERERCRiFESIiIiIiIiIiIiIiEjEKIgQEREREREREREREZGIURAhIiIiIiIiIiIiIiIRoyBCREREREREREREREQiRkGEiIiIiIiIiIiIiIhEjIIIERERERERERERERGJGAURIiIiIiIiIiIiIiISMQoiREREREREREREREQkYhREiIiIiIiIiIiIiIhIxCiIEBERERERERERERGRiFEQISIiIiIiIiIiIiIiEaMgQkREREREREREREREIkZBhIiIiIiIiIiIiIiIRIyCCBERERERERERERERiRgFESIiIiIiIiIiIiIiEjEKIkREREREREREREREJGIURIiIiIiIiIiIiIiISMQoiBARERERERERERERkYhRECEiIiIiIiIiIiIiIhGjIEJERERERERERERERCJGQYSIiIiIiIiIiIiIiESMgggREREREREREREREYkYBREiIiIiIiIiIiIiIhIxCiJERERERERERERERCRiFESIiIiIiIiIiIiIiEjEKIgQEREREREREREREZGIURAhIiIiIiIiIiIiIiIRoyBCREREREREREREREQiRkGEiIiIiIiIiIiIiIhEjIIIERERERERERERERGJGAURIiIiIiIiIiIiIiISMQoiREREREREREREREQkYhREiIiIiIiIiIiIiIhIxCiIEBERERERERERERGRiFEQISIiIiIiIiIiIiIiEaMgQkREREREREREREREIkZBhIiIiIiIiIiIiIiIRIyCCBERERERERERERERiRgFESIiIiIiIiIiIiIiEjEKIkREREREREREREREJGIURIiIiIiIiIiIiIiISMQoiBARERERERERERERkYgxN3YBIiL1xWeNx2NPo8KWSsASQ8hgwhAKYPJ5iC7bSYwzF4vX1dhlioiIiIiIiIiInFIURIjICc0T34Ki1r1xJp+JP8pWtTEYwEAofEwIAxhNAJgry7AXrKTptm+IceU1RskiIiIiIiIiIiKnFENWVlbo8IeJiBw/QoAzOZPC0/vjTsyAYCAcNNTJnuNjS3JolpONvWAVhkgVKyIiIiIiIiIicopTECEiJxSf1UZu56GUpWRCMAjGY2h1syeQsOWvIm3NHCzesvorVERERERERERERAAFESJyAilN6Upul2EETdYjmwFxOMEAxoCXtNWzSMz/pf7OKyIiIiIiIiIiIhzDUGIRkYZTmN6PbVkjCZqj6jeEADCaCJqj2JY1ksL08+r33CIiIiIiIiIiIqc4BREictwrTO9HXscrq74xROifrT3nzet4lcIIERERERERERGReqQgQkSOa6UpXfeGEA0kr+NVlKZ0bdBrioiIiIiIiIiInKwURIjI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5OQwd+7c8DI61113HZMnT2b58uXk5eWxcOFCPvvsMy6//PKDXsNgMDBgwIDwG+D//e9/ZGZm0rx55NdHtdlsxMfH43a7cTqdCiGOQy6XC5/Ph91uDwdeIiIiInJoHo8Ht9sdnp19KDfccAP//e9/uf3229m2bRsPP/ww9913HzNmzGD06NEYDAbuueee8PExMTF899133Hfffdx+++0sW7aMv/71ryQnJx9Tzf3792fbtm1s376dzz77jIEDBx7T+WrjdruZNGkSN910E5Pf/pAbup3Gnb1SahxzemIUg85I4NrZm7h2zkZ6t7Yx9ty9n03+ckEa56TFcf0Hmxny3gZ+kxZPt5TYI67F5Q0y5pOt9H59DX/6PDdci9te9Vn5oosu4vrrr+cf//gHo0aNoqCggCuuuKLGOS666CJuvvlm3njjDUaOHMnrr7/OzTffzIABAw57/QULFnDppZeGvx84cCALFiw44LhRo0Zx3nnn8be//Y077riDHTt2MHny5PAS1s2aNeOJJ55g6dKl3H777cyfP5877rijxjlSU1OZPHkyixcv5tZbb+WJJ54gMzOzxutqX/369WPo0KH8/e9/54YbbuDPf/4zmzdvPuzPJFLfNCNCRCJi3xv6h7NmzZoDvq9uqtSuXTtiYmIOGIFgtVpJTU0Nf5+fn19j+Z2ioqIaYcf+iouLGTNmDBkZGXTr1o0uXbowceJEBg0axIQJE0hISKBZs2Z1HoXStm1bWrZsWWOpp9rqzMnJqTHqvqioiNNPPz38swYCgYOOdG/bti1ZWVkHXAOq3ojk5h64XJLRaOS6666j74DLadYkCYvJQJTJiMdXc+T/moKaSxfll/s4La7qv4gzTotmR5mXXa69o6eX73TVWuP+DvcyCJhjwv8RRfp1cNFFF3HvvfeG902YMIGVK1cyd+5cFi5cSFZWFp07d+a3v/0t1113HQ899BArVqwgPT2dTZs21RhZsmrVKkwmE61ataKkZO8yV4ez7ygXqPqdfvnll7Ue27JlS2JiYnjmmWdqbDebzWzcuPGg1+jZsyfR0dF8++23ADidTlasWMHAgQN566236lzrkTAajdjtdsxmMw6H44DZUHJ8cTqdJCYmkpCQQGlpqQIjERERkcMIBoNMmjSJ++67jyuuuIINGzbw888/87///e+AG7oLFixg0aJFQNXyrC+//DL/+te/WL58OQAffPBBjZ4BmzZtCi83C/DWW29x3nnn0bt37xpL1R6pQYMGhQcnLVu2LDyr41Azq4/Uu+++G/7zyvVF2L/bxeDOTXjxu72rDhgMMOaTreEZELNWFdMv3cb/UTUbYviZTbjj4xwWb62a5T5mfg6r7zrziGt59pu9SyBtd3iZuiyfwZ2SmG1Lxb57LVdffTXz58/n008/BeCdd96hZ8+eNfpE3nTTTbzyyissWbIEgF27dpGens5ll11W66yEfX322WfcfvvtpKRUBTGZmZk88cQTNQYmRkdHc8UVVzBp0iSWLVsGwDPPPMPMmTMZNGgQ77//PldeeSU7d+7klVdeqfpZtm/n9NNPZ8SIEeHzjBgxgs8//zw8UHLHjh28+OKLPPfcc0yZMuWAmc8pKSkUFxezYsUKAoEABQUFB8zmEWkICiJEpF7t2LGDYDBIeno6X3311QH709PTcTqdlJaW1ul80dHRFBcXM27cuAP2uVx7b4ZXL31ULRQK1Wm0b05ODjk5OXz00Ud8/PHHvPjii3Tr1u2AG8aHExMTw/r163nyyScP2Odw7J0+eqg6KysrD3uNpUuX8tprrx2wr7i4uNbHXHPNNQwZMoQnP/iGZcE0yv1G/u+iNKymmgmBL1jzRmQoBMYjCJP2t7mkkmAoxBlNo/kExwH7z2gaTYnHT1F5Jam1PH5/9fE6+Prrr2uEHbt37w7/2ePxsHTpUpYuXcobb7zB5MmTueGGG8LLQh1OMBg8IHyrrRn1/tNkD3XDvvoN8YMPPkhhYWGNfYdaUmfQoEEkJCTUeKNsMBho06YN06ZNq/ebzhaLBbvdTigUoqSkRP0gTgChUCgcRthstoP2mBERERGRvRYvXszSpUvp2rUrnTt35uyzz2b48OE8/fTTNd577xtMVA9a2n9bVFQUsbGxuN1uoqOjuemmmzj33HNp2rQpJpMJq9UavqF9NFq1akXHjh3585//DFR9XsnOzmbQoEH1GkT079+fwYMHk5qaSlRsPCazmbLKmp8Htju84RACag56S0+Mwmoy8kNeeXh/WWWQjcWH/mxcm6s6JnFHz2ZkJEYRZzViNhooqwwQMFd9rmrVqtUBA9vWrl1L9+7dgarPnC1btuSBBx7g/vvvDx9jMplqfOY8GIfDwbfffsuAAQMwGAx8++23B7zPTk1NxWKx1OhJGAgEWLt2Lenp6QC0bt2aX3/9tcbj9h+017ZtW9q0acNFF11UY7vJZKJFixZs27atxvZFixYxZMgQZsyYwbJly/juu+/45ptval2aWCSSFESISL2qHn195ZVXMnv27Bo3WpOSkrjwwgv573//W+MxnTp1OuD76qWdNmzYQJMmTQgEAgft5VBfqq8ZHR2Nx+MhLy+PHj168NNPPx32sRs2bKB///6UlpbidrsPe3xtNm/ejMFgoFu3brXOxNiwYQP9+vVj165ddX7DkJmZyddff82879awO70pGM20bRLF+t11b1S7fncFLW1WUuLM5JdX3ejvmRp3yMeUVARYtKWMW7o345XlBTX6RCTHmRnaOYlZq3ZjCO59kxrp14HH4zlo0+r9bd++nS5dugBVr4sBAwYQHR0dDhIyMzMJBAJs374dqHrT2bRp0/DjjUYjGRkZh33tbNq0iR49ejBt2rQD9uXk5OD1eklOTq7zhwW73U7v3r154okn2LJlS3i7yWTihRdeoGfPnuGRWPUhOjqa+Ph4fD6flmI6wQQCAcrKyrDb7cTFxVFeXn74B4mIiIic4nw+HytWrGDFihX861//4v777+emm26qEUTsOziq+v1xbduqBzLdeeednHXWWbz66qvs2LGDyspKHn/88VoHNtXVoEGDMJvNzJkz54D6X3jhhXp579e5c2ceeugh3nrrLZYvX86WlHPpf+HF/OGcmkvCHjjoLYTxCMe8BUOhA2bbm/c5Sc/UOF67IoNJS/L43xYnzsoAV3dK4q6zkwnVsXl29UCwZ5999oAb/3X9/L1gwYLw8kjPP/98nR5zNGJiYvjPf/5T69LRBQUFB2wrLCzkxhtv5KyzzqJnz56MGzeOa665hnHjxmkgmTQoLQ4sIvXu+eefx2KxMHnyZLp27UqzZs3o1asXzzzzDLt37z6gcXBmZibDhw8nLS2Nq666ivPPP58PP/wQgBUrVrB69WqefPJJevbsSUpKCl26dOHWW2/ljDPOOOoax40bxw033EBmZiYpKSl06tSJBx98kJKSkvCbjrfffpthw4YxePBgWrZsSfv27bn66qtrPd/nn3+Ow+HgySef5Mwzz6R58+Z069aNu+++m9NOO61ONeXn57Nw4ULGjx9Pnz59wuc4//zzAZg7dy42m40///nPdOjQgdTUVHr16sX48eMPOvtjx44d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F19MubzrgX1dXV1KpVLmzp2bUqm02zl/voRPd3f3K9qcN29e7rrrrhx99NH50Ic+lHPPPTdf+cpXsmbNmlQqlb4H9f+ttnb3W9lrrrkmLS0tueGGG/Liiy+mUCjklltu6Ttu7dq1OeH//Vum/++ZOf7Asfm3TxyY/2zanjlLnvof/w695d2XjqpUkr8oc49UCsVMmTIl7e3tfQ/8X288r+Xyyy9PfX19FixYkJaWlnR3d+emm27qO2/jxo05++yz84EPfCBHHXVULr744syePTsXX3zxHl8/AACAvSGIAABgr3V2dvYtofTn1q5dm2KxmLFjx2blypV73e66deuybt26LF68OAsWLMiJJ56YNWvWZOvWrZk6depuxx588MF9+yLU19fngAMOyPXXX9/X77Rp017R/s6OjixZszn//kRbfvzEltw5+5CMHb4hWzt3f+j+p02dOfOIfTJiSKFvVsQxE0elVK5k3abOvR7X69ln5LB85CMfyS9/+ctUKpU9Gs9/j71YLO72/bRp0zJ//vysWLEiSTJ+/PiMHTt2t2O6u7vzyCOP5JFHHsmSJUvy/e9/PwcddNAeXb/e3t6+2RkAAAB7QhABAMCbprm5Offff3+++tWv5jvf+U7Wrl2bsWPHZvr06XnyySezfPnyVz1v3333zamnnpqHH344mzZtyuTJkzNp0qQsXbo0ya4ZGLNnz87JJ5+cVatW5aSTTsrUqVOzbt26JLuWdtq2bVtOPfXUbNq0KQ0NDbngggt26+NTn/pUnirum1+kLuUU8vFDx+WF9p5s+4sQIknuXLU5lx+3X2766JRc91/P590javONkybnjlWb+/aH+J+qSU0mjKxNTZIxw2tz1H51+acZ/5D29vbcfPPNezyeLVu2pLOzMzNmzMjGjRvT3d2dHTt2pLm5OSeddFKeeOKJjBw5MnPnzk1n58vhycyZM1MsFrN69ep0dXXlxBNPTGdnZ1paWtLW1va61++FF17IiBEjMn369Kxbty5dXV3p6up6Q78JAADwziaIAADgTXXdddflrLPOyoUXXph3v/vd2bZtW1avXp1HHnnkNc/p6urKAQcckJkzZ6a+vj6bN2/OkiVLcs899yRJfv3rX+e2227L3LlzM3To0Nx3331ZunRpDjrooCS79lW48sorc9FFF+WWW27JM888kxtvvDHz58/v62Pnzp0577QZ+eeJ+6dcTh59YUf+4YfrUnmVejp6Kzn9jnW59sTJ+fk5h6ajt5x7ntiaKx5ofsO/T/3wYtZc9P6UK5Vs7ypl3eau/MeyB3Lvbd/Lzp0793g85XI5N954Y84+++zMmTMnK1euzCWXXJJvfvObufTSS3PzzTentbU1CxcuzIUXXth3Xnt7ez796U/nwgsvTLFYzJNPPpmvfe1rfUtCvd71W7VqVe6+++58/etfz5gxY3Lrrbdm0aJFb/h3AQAA3rlqGhsbX+1/LwAAeMfpGToqaz78LwNdxiscvuyfU9vdPtBlAAAAVIXFXQEAGDSGdLentmv7QJexm9quNiEEAADwjiaIAABgUKlvXZmUX7kvxIAol1Lf+thAVwEAAFBVgggAAAaVfTY8nBSKA13GLoVi9tnwy4GuAgAAoKoEEQAADCp17c9nxJampFwe2ELKpYzY8lTq2l8Y2DoAAACqTBABAMCgM75pWVIY4FvhQjHjmx4c2BoAAAD6gSACAIBBp771sYxueWzg9oool1LfstL+EAAAwKAgiAAAYNCpSTJp9Z0plLqTSj8v0VQpp1DqzsTVd6amf3sGAAAYEIIIAAAGpSHd2zNp1R1JTT/fEtcUMmnVHRnS3d6//QIAAAwQQQQAAIPW2JY/Zr/Hl/Rrn/s9viRjW/7Yr30CAAAMJEEEAACD2vinf/FyGFGtZZpeanf/NUsy/ulfVKcPAACAt6jagS4AAAAG2vinf5EhndvS/L4zUi4OTQrFN6/xcimFUncmrbrDTAgAAGBQEkQAAEB2LdM0cstTaT789GxvmJaUS28skHjp/PqNqzNx9Z32hAAAAAatmsbGxspAFwEAAG8VlSRtE6Zl49QPZ+e4qXsfSLx0/IgtT2V804Opb30sNdUqFgAA4G1AEAEAAK+hY9R+2XTAsWmbMC29w+p3fVkupSYv30JXUtMXVNR2taW+9bHss+Hh1LU/PxAlAwAAvOUIIgAAYA/0Dh2VnfWT0jl6/5Rq61IpFFNTLqXY25Hh25/LiLbm1Fp+CQAA4BUEEQAAAAAAQNUUBroAAAAAAADgnUsQAQAAAAAAVI0gAgAAAAAAqBpBBAAAAAAAUDWCCAAAAAAAoGoEEQAAAAAAQNUIIgAAAAAAgKoRRAAAAAAAAFUjiAAAAAAAAKpGEAEAAAAAAFSNIAIAAAAAAKgaQQQAAAAAAFA1gggAAAAAAKBqBBEAAAAAAEDVCCIAAAAAAICqEUQAAAAAAABVI4gAAAAAAACqRhABAAAAAABUjSACAAAAAACoGkEEAAAAAABQNf8fAI9qI6+ZaPgAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "authorplot(\"clem\", 20)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Last but certainly not least, we'll do the same thing for Tom Aarsen." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32;20;1m======== New task ========\u001b[0m\n", + "\u001b[37;1mSummarize what tomaarsen talks about\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: I will use the tool'search' to find information about tomaarsen and then summarize what I found.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'search' with arguments: {'author': 'tomaarsen'}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: I will summarize the content by tomaarsen.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'search' with arguments: {'author': 'tomaarsen'}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: I will summarize the content by tomaarsen.\n", + "The content is about updates to the Sentence Transformers library, including new features and improvements.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'final_answer' with arguments: The content is about updates to the Sentence Transformers library, including new features and improvements.\u001b[0m\n" + ] + }, + { + "data": { + "text/markdown": [ + "The content is about updates to the Sentence Transformers library, including new features and improvements." + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "md(agent(\"Summarize what tomaarsen talks about\", maxlength=20000))" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total posts for `tomaarsen` = 14. Using up to 5 posts for graph.\n" + ] + }, + { + "data": { + "image/png": "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pIMhtQ8eq/AbD+xwoX93eJ6nZ599VgsWLFAqldK+++6r73znO6qrq9O11167zcfsv//+CgQC+tnPfqb169frIx/5iGbOnCnDMHTLLbds97kuvvhiTZgwQddcc40ymYymT5+u//zP/9Qll1yywxoffvhh3X777YpGozryyCM1c+ZMpdNpPfjgg3v0vQMAAABAbyGIAAaofDQhp2643OQwFSNxlQIhBUpFhfKOjI4NirevUySXrnSZAGqcZVk64ogjdOmll+rll1+WJDU3N+u1117b6riLLrpIxx13nCKRiF5//XX94he/0JtvvilJmjZtmiZOnKh7771X5557rhKJhJ577jn99Kc/leM4mjVrlsaNG6dx48ZpypQpkqSpU6equblZI0eO1IUXXqjDDz9cjuPohRde0Pz589Xe3i6p8wrz1atXK5fL6ZRTTlGhUNCDDz6oO++8s1t9F1xwgY477jglEgmtX79ev/rVr/T3v/9dkjR27Fidd955Ouigg5RKpfTXv/5Vt956q1zX3eo16ejo8P8ejUYlSalUyr8K/sknn9TcuXN17LHHavz48brnnnt01113aebMmf4V983NzXrwwQd1333vdwTMmjVLiURCS5cu1RlnnKFwOKwnn3xSN910kx9kfOELX9CUKVM0ePBgpdNpLV26VFdffbVmzZqlz3zmM/7zv/POO/rqV7+qSCSiCy+8UJM+8QnFE3V6aaOtHy5ap3++09kZcNz+CT145midce8q/eCEYTpkb0NT7lmlyycO1aubHRU9aephTcoVS/rJ0xt034pWzf7Ufvr8QQ3aZOf1vb+s06LV7f73MGYvQ9dM2lcT9kvIznt66l/tumLROrU4nfU/cOZH9dpmRwVP+vKhTXrrtBs089LvaNq0aTr55JPV2Nio9vZ2Pf3005o3b95Wr/3w4cN111136ayzztLatWv926dMmaLTTjtNX//617da0G9ubtYDDzygr3zlK1udr+z555/X888/73+9ceNG7bfffvr85z+/3SDCsiydcsopuvbaa/XPf/5TkjR79mwtXLhQBx98sF599dXtPp/ruv775U9/+pNOP/10HXXUUX7dkUhE3/zmN/WJT3xCiURCa9as0S9/+Uv/M7gtxx13nM466yyNHDlS7777rh577DH95je/kffeTJAvf/nL+sxnPqOhQ4eqo6NDixcv1i233OK/x4cMGaLp06frsMMOUzgcVnNzs2655Rb94x//kKQP/RwCAAAAqG4EEcAA4iSGasv+/6b2wYepEEt23ugVFdD7l56WFPCvWA1nO1S3aakGvf03xdMbK1EygBrnOI5s29Zxxx2nFStWKJ/Pb/O4q6++WtlsVrNmzVImk9HnPvc53XDDDfrGN77hL9wPGzZMEydO1Pe//30lk0ldddVVOvPMM7VgwQLddNNNGj58uNasWaPbb79dUufivmVZ+tnPfqZHH31U8+fPVywW0/nnn6+rrrpKM2fO9J//05/+tH73u9/p29/+tg499FDNmjVLy5Yt05IlSxQIBDR79myZpqmf/OQn2rBhg0aMGOEv0A4bNkxz5szRggULNGfOHDU0NGj69OmaPn265syZs1uv27Rp03Trrbdq/vz5KhaLCgQC2rx5s66++mq1t7dr7Nixuuyyy7RlyxY99dRT/uPGjRunLVu2aMaMGdp333115ZVXatWqVXrkkUc0evRoXXLJJfrJT36i5cuXK5lM6vDDD5ck3XTTTdqwYYNOPfVUXXjhhf73dsEFF+j444/XlfN/oyWDP6FLJgzR775yoI7+5XK1ue93aVx54jBd9cR6rWnL+rdPHTtI8/7RrE/d+bpOP7hRPz1pf312dIMeeaNNcxe/o4uOGaybTx2pI36xVE6hpLpYSPd/9aP6zctb9MNF62REgrrqxH11+2kH6LTfrvSfa+rYQbr9n+/qlN+8rn2X/0EnnHCCpkyZoh//+Mdas2aNmpqatppvUbZu3Tq99tprmjx5sn7961/7t0+ePFmLFi3a5mMGDRqk448/foeL+NtiWVa30OmDRo8erUgkoiVLlvi3rV27Vu+8844OPfTQHQYRXR122GHab7/9tG7dOv+26dOna+TIkfrxj3+sLVu2aOLEiZozZ47OPfdcrV+/fpvn+N73vqebbrpJr7zyioYNG+Z/PhYuXCips1Nk3rx52rhxo4YNG6ZLL71UF154oW688UZJ0ne+8x2Fw2F95zvfkeu6GjFihBzH8V+LnfkcAgAAAKheBBFAP1eS1D54rDZ/ZJLshpFb788dDGl7O2AUYkm17Psxtez3bzJb12jvNU+qbtMyBfqgbgCQOhcvZ8+erZkzZ+rzn/+8Vq5cqZdffllPPPGEVq9eLamzm2DMmDH64he/6AcVt9xyiyZOnKiPf/zjevjhhyVJgUBA1113nb+4+Ze//EXjx4/XggULlMlkVCgUul0pLkmnn366Vq1apdtuu82/bc6cOfrd736n4cOH+4u3q1ev9hdc169fr9NOO03jx4/XkiVLdNRRR2nMmDE6++yz/eM3bnw/3D3zzDP1+OOP+90J69ev17x583TjjTdq7ty52w1fdmTRokX605/+1O22O+64w//7O++8o0MOOUQnnnhityAinU7r5z//uTzP09q1a/WPf/xD48eP1yOPPKIhQ4bIcRwtXrxYjuOoublZq1atkiRlMhnZti3P8/zXzzAMff7zn9fs2bP1xOqS3glkdOkf39JLF43V1w8fpJue2+Q/73XPbNRTa7ovui/b5OiGv70jSZq7+B1NnzBELU5Bd728RZJ0/bMbde74vXXo4Lhe2GDrvKP21tJmR9c+vcE/x/RH39LSiw/TqMaY3mzNSpLebM3qmqfWS15R6Y6QJh80SC0tLVqyZImKxaI2bdq0VcdNV48//rhOP/10P4gYPny4DjroIP3Xf/1Xt+N++MMf6rjjjpNhGHr22Wd1/fXXb/8f7AOGDRum008/fYfbMjU1NSmXyymTyXS7vbW1VU1NTTs8/xe+8AV99rOfVTgcViQSUTab1R/+8AdJndt9nXzyyfrKV76iLVs6X+t7771Xxx57rE4++eRun4WyadOm6be//a0ee+wxSZ3v79tvv10XXHCB/7no2n3T3NysBQsW6LLLLvODiMGDB+vpp5/2txjr+hnZ2c8hAAAAgOpFEAH0Y/loUusOmaKOIWOl965O3eUhoe8db9fvp7eOPEfJ5mUavuL3iuS2f5UmAPSkp59+WosXL9bhhx+uQw45RMcee6ymTp2q66+/Xo899pgOPPBAxeNxPfDAA90eF41GNWzYMP/r5uZmP4SQpC1btqihoWGHzz1q1CiNGzdOjz766Fb3DRs2rFsQ0VVLS4saGxslSQceeKA2b9683cXSUaNG6YADDtDkyZO73R4KhTR06FC9/fbbO6xxW954442tbjvttNN08skna/DgwYrFYgqHw36QULZmzRq/m0HqfI0+8pGPSJJeeOEFNTc36+6779Zzzz2n559/Xs8884yy2ew2axg2bJgikYiWLVumYv3RCqikgie9uNHW6L2Mbse+tHHrIc4rNr3/b+WVpFan0O22TZnO2Q57mRFJ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nXdqzJgxkjq7Gh599FFddNFFGjdunEaPHq3LL79cy5Yt2+lB1WUPPfSQhg8frhNOOEHr1q3TX/7yF33/+9/X8ccfr3322UdjxozRmWeeqQkTJmzz8QsXLtSnP/1pnXXWWRo5cqT2339/TZo0Seeee66kzrknkUhEX/ziFzV06FB96lOf0uc///lu57j44ot1zDHHaJ999tFHP/pRHXnkkf62ZOXP4Y9+9CMddNBBGjZsmI455hhdfvnlH9rVBAAAAKA60BEB9CNtQw5/P4ToIxvHnKaIm2KbJgC9wnEcvfrqq/ryl7+sYcOGKRTq3ErnkUce0d133+13C/zoRz/SOeeco1mzZqm+vl6tra1atmyZHMeRaZp+YJBIJLp1EwSDQX9OxB//+EdNnz5dd955p2KxmL797W/r3Xff1VVXXaWvfe1rmjNnjiKRiDZv3qyXX37Z7xIIhUKKRqNKJBKSOrsGQqGQgsGgwuHO/1SaM2eOzj77bM2cOVOxWEwbN27UHXfcoWw2q9dff12XX365pk2bpmuuuUaBQEAbN27UU089pba2NpVKJf9P+fzlv5cXYVOpVLctajKZjFKplP/173//e+233366/PLLVSqV9MQTT+j+++/Xxz72MX/rKM/z5Hlet22mul6Vn06ndfzxx2vatGmKRqNav369rr32Wq1Zs2a7/36/+tWvFAwGdd23Pqe4aemljba+fM8qpbJbb3m0p95J53XKb97QVSfuq99PPVDRUFDrUjkt+ldK29jRSQqGZHRsUDo9SGeeeaYuuugihUIhrV69WldcccV25yFI8od2T5o0SbNnz+52Xzab1Wc/+1ldfPHFikQi2rRpk5555hndfffd/jGxWEz777+/QqHObQ+POuooDR8+XMOHD9fvfve7buebNGmSpM732f77769YLObfN3/+fJVKJV1zzTWKRCJ6/vnnd2oOxQd1dHToz3/+s84++2w988wzmj17tr7xjW/ooosu0l577aVUKqUVK1Zo8eLF23z8888/rx/84Ac666yz9NWvflWFQkFr1671Z1y8+eabmj9/vqZOnapvfetbeuWVV3TrrbfqBz/4gX+OYDCo73znO9p7772VyWT0/PPPa/78+ZI6Z5VccsklOv/883X99dcrEomoublZzz33HB0SAAAAQI0IjBs3bieb3wH0pnw0qdcnzpIXjvVeJ8S2lDwFC1kd9NfrFMmlP/x4AFVne9sG7enX27tvZ+1oAX979+3J19i+fDShVyddU+kytnLIk1cpzO8uAAAAAOj36IgA+oGSpHWHTOkcTN2XIYQkBYLyQlGtP2SKRrx0xw5GkQLoCz0dBuxMOLCzui7e90U4gP4jkksrnO1QIZasdCm+cLadEAIAAAAABgiCCKAfaB88Vh1DxlaugGBI7UMOU/vgsarftKxydQD9UF+EAXvSLdD179v62vO8HgkHgLpNS9Wy78ekYKjSpUheUXX8vgIAAACAAYMgAugHNo+cJHmeVMmBjV5Rm0eeSBCBfq+3OgV6slug69dd/14OBbZ3P9sIoT8b9Pbf1LLfv1W6jE7BkAa9/WylqwAAAAAA7CSCCKDCnMRQXfvVE/T5gxrUGA/r47e/qmWbnL4vJBiS3fgROYl9FE+/0/fPX0V++9vf6ve//73uu+++SpfS6/oqDOjpbYS29afr/R88dme+BqpdPL1RZusa2fX7Vzw4N1Nv87sKAAAAAAYQgogqVl9fr3POOUcTJkxQY2Oj0um03nzzTS1cuFDLlvXcVe9z587VqlWrNH/+/B47Z2+YO3euxo0bt937X3rpJc2YMaPvCnrPoZ+Zqq8e1qTP371Sb7VltcUu9HkNPq+oLfsfp+Er+n4Bva6uTldccYUOOOAA1dXVqa2tTc8++6xuu+022ba93cd97Wtf04QJE3TggQeqUCjoc5/73Ic+1/aCgmnTpmnixIk677zz9vj72RUnnXSS/v3f/32nav8wfdkp0NPbCHmet9PHso0QUBn7bnhWKxtHVraIYEh7r3mqsjUAAAAAAHYJQUQVu+aaaxSJRHTddddp48aNamxs1Pjx41VXV1fp0iriyiuvVDjc+ZYfPHiwbrnlFs2cOVP/+te/JEmFQvcAIBQKqVgs9npdgz9ysJrTeT2/PrNH5wkFpOKerr8GQ2ofPFb6kCAiHA5v9XrtKc/z9Oyzz2rBggVKpVLad9999Z3vfEd1dXW69tprt/u4SCSi//f//p9WrFihU045pUdr6gnbmwPQ9e+RSESSFI/HK76NUNev2UYIQFk4HFYymdQg9229u+VVtTaOrsysCK+ous0rmA8BAAAAAAMMQUSVsixLRxxxhC699FK9/PLLkqTm5ma99tprWx130UUX6bjjjlMkEtHrr7+uX/ziF3rzzTclvX+F+L333qtzzz1XiURCzz33nH7605/KcRzNmjVL48aN07hx4zRlyhRJ0tSpU9Xc3KyRI0fqwgsv1OGHHy7HcfTCCy9o/vz5am9vl9TZobB69WrlcjmdcsopKhQKevDBB3XnnXd2q++CCy7Qcccdp0QiofXr1+tXv/qV/v73v0uSxo4dq/POO08HHXSQUqmU/vrXv+rWW2+V67pbvSYdHR3+36PRqCQplUqptbVVkvTkk09q7ty5OvbYYzV+/Hjdc889uuuuuzRz5kwdeeSRampqUnNzsx588MFuV9LPmjVLiURCS5cu1RlnnKFwOKwnn3xSN910kx9kfOELX9CUKVM0ePBgpdNpLV26VFdffbX+4/s/1Cmf/qgkacv3xuvtVFZH3rxc0VBA10zaV6cf3KhkLKSXNtr64aJ1+uc7nZ0Bx+2f0INnjtYZ967SD04YpkP2NjTlnlW6fOJQvbrZUdGTph7WpFyxpJ88vUH3rWjV7E/tp88f1KBNdl7f+8s6LVrd7n8PY/YydM2kfTVhv4TsvKd/HnelfjHvxm7/Vv/6179ULBb1qU99SqtXr9Zll12madOm6eSTT1ZjY6Pa29v19NNPa968eVu99sOHD9ddd92ls846S2vXrvVvnzJlik477TR9/etfVzqd1oMPPujf19zcrAceeEBf+cpXtjpfV3fccYekzq6CD9N1wT4YDCocDndb0C9/bRiGAoGArrvuOr311lsKBAKaNGmSisWi/vjHP+ruu+/2z1VfX6+LL75Yhx9+uNra2vR//+//VTAYVCKR0N577y1J+uxnP6tJkyb5//5LlizRb37zG2WzWR1yyCGaOXOmJOnRRx+VJN1zzz265557FAqF9LWvfU3HH3+8LMvSW2+9pTvuuENLly6VxDZCAHpfIBCQZVmKx+PK5/NKtbVpn1fuUWriLHmBmBTowy2aSp6CxZz2XfF77Vr0CgAAAACoNIKIKuU4jmzb1nHHHacVK1Yon89v87irr75a2WxWs2bNUiaT0ec+9zndcMMN+sY3vuEv3A8bNkwTJ07U97//fSWTSV111VU688wztWDBAt10000aPny41qxZo9tvv11S5+K+ZVn62c9+pkcffVTz589XLBbT+eefr6uuuspfdJWkT3/60/rd736nb3/72zr00EM1a9YsLVu2TEuWLFEgENDs2bNlmqZ+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Usrlcts9du+3npGkzjCi5PXONk3vnXfYq/drr7ef6fnzAwAAAAD6BEFElcvn81qyZImWLFmiu+66S9/97nd19tln67HHHpNhGGppadGll1661eO6LuZua7E2GNzxYkM8HtfixYv1y1/+cqv7Wlpadnju8tX021ug7focDz/8cLd5DWWbNm3axiM+XNfARZImTZqkCy+8UDfffLOWL18u27Y1depUHXzwwd2O29Fr5DiOzj//fI0bN07HHHOMzj77bE2bNk0XXnihSoE9G/SZyXtb3Zb3ui+ml7ZxmyQF39upyIoE9diqlK55srMjIlAqqOmdl7TPmie0ZcsWOY6jfD6vtrY2bd682X/8u+++q69//es66qijdPTRR+u8887TqaeeqksvvXSbQ74zmYxefPFFHX/88XrppZd04okn6sEHH9xqkevdd9/Vu+++q7feekupVErz5s3TwoULu71v9lQmk5FlWVvdnkgklMns2dDwDxoyZIj++7//Ww888IBuu+02dXR06LDDDtPll1+ucDi83fd5PB73t3r64Ov5wfcpAOypSCSiRCKhUCgkx3F2+mfh3m89o4ib0rpDz5AXivbsAGuvqGAxp+HL76UTAgAAAAAGOIKIGvPWW29p4sSJkqSVK1eqqalJxWJRzc3Nu33OfD6/VTCxcuVKnXDCCXrnnXe22s5hZ61evVp77713t62cPvgc5a2DesvYsWO1fPnybl0jXbtFdpbneXrxxRf14osv6s4779RDDz2k8ePHa1Xz1gv2a9qyyhY8fWy4pXUrOhfpw0HpyH1M3fLC7gUsO/JKs6PPHdSgt1NZFUtSwCuoY+Nm5T7QUbKtboFcLqfFixdr8eLFuv/++7Vw4UIdcMABWrly5Tafa9GiRTr//PO1aNEiDR06VE888cQOayu/ryKRyG5+d9u2du1ajR49eqvbP/rRj3Ybpi1pq9Dp4IMP1rp16+R5nt5++22Fw2GNHj3a35ppv/32UzKZ9I8/6KCDFAgEdPPNN/uv4YknntjtnIVCYZufoVAopIaGBn84NQD0tEAgoEQiIcMwlM/n1draus0weUcaml+R1fovrTtkijqGjJW84p4FEu89vm7zCu274vfMhAAAAACAKtALPfToD+rq6nTDDTdo8uTJOuCAA7TPPvvo4x//uKZOnapnn31WkrRkyRItX75c1157rY4++mgNGTJEhx56qL75zW9uc5F2e5qbm3XwwQdryJAh/nyA+++/X8lkUj/60Y900EEHadiwYTrmmGN0+eWXf2g3RdnLL7+sV155Rddcc42OOuoo7bPPPjr22GN1zDHHSJJ++9vf6tBDD9X06dM1atQo7bvvvjruuOM0ffr0XX/BtmP9+vUaPXq0jjnmGA0fPlznnHOODjrooF06x4QJE/TFL35Ro0aN0pAhQ/TpT39agUBAa9euVSjvqKRAt+PtvKdf//NdXTNpX33iI3U6aJChG08eoXgkqN+8N+ehJy14cbMajJBu/cJHdOQ+pkY0GDrhoKEf+m910kkn6ZRTTtHIkSM1dOhQTZ48Wa7r7jDUevrpp2WapmbMmKGXXnrJn30gSR/72Mf0mc98RiNHjtSQIUM0YcIEzZgxQ0uXLvXPOWbMGN15553aa6+9/McNHjzYf22DwaBGjRqlUaNGyTCMrZ6/7Pe//70mTJigr33ta9p///01cuRIffOb39Shhx66VYfN4MGD9e1vf1v77befPvGJT+iLX/yi/vCHP0iSPw/ksssu08EHH6zRo0fru9/9rr9NlNT5HopEIvriF7+ooUOH6lOf+pQ+//nPd3uOd955R6Zpavz48aqrq1MsFtO6dev0l7/8Rd///vd1/PHHa5999tGYMWN05plnasKECdv93gBgZxmGoaamJkWjUXV0dKitrW2XQ4iySK5DI1/6tUb889cyU+8Fut6unStQ6jzeTL2tEf/8tUa8dAchBAAAAABUCToiqpTjOHr11Vf15S9/WcOGDVMoFNLmzZv18MMP63/+53/84773ve/pW9/6li6//HI1NDSopaVFr7zySrehuR/mnnvu0fe+9z3dcccdMgxDU6dOVXNzsy655BKdf/75uv766xWJRNTc3KznnntulzokrrrqKl100UX60Y9+JMMwtH79et16662SOjsmLr30Un3zm9/Uz3/+cwUCAW3YsEFPPvnkzr9QH+Khhx7SgQceqCuvvFKlUklPPPGEHnjgAX3sYx/b6XOk02kdf/zxmjZtmqLRqNavX69rr71Wa9askbGXIQUCWz3mP59ar2BAuvlzI5SIhvTSRltfvmeVUtndWyDakXfSeZ3ymzd01Yn76vdTD1Q0FFRz8956YfFfd/hvlU6ndeaZZ+qiiy5SKBTS6tWrdcUVV+xwfkF5aPek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EQAAAGiR67q65ZZbdMUVV+jLX/6yVq1apbfeeksvvfSSVq9eLWnHaoKRI0dqypQpxVBxzz336LjjjtOJJ56oZ599VtKO7ZFuvvnm4oubf/3rX3X44Yfr/vvvVyqVUi6XK3mnuCR95Stf0fvvv69f/epXxdtuvfVW/fa3v9XAgQOLL96uXr26+ILrxo0bNXnyZB1++OFaunSpjjjiCI0cOVLTp08vPn7z5s3F451zzjl64YUX9Lvf/a74/DvvvFO33367fvrTn7YaX3bmxRdf1J///OeS2+6///7if3/44YcaNmyYjj766OLj0um0Ghsbdeutt6pQKGjFihX63Oc+pxEjRujpp59WKBSS4zh6/fXXlU6ntW3bNq1bt06BQEAVFRXFlRWSVFlZqQEDBuiMM87QXXfdpUWb8/rQY2nOn9fqzVlj9I1D99Ndi7cU57n51c16eU2iZN7lW2z9+O87Lib900UfafYxfVRn5/TwWzuumXDba5v1rcP31+jehv65ydKFR+yvt2tt3fjKpuIxZj+3Vm9ffIiGRf36oD4tSfqgPq3rX94ouXklE5WaOGI/1dXVaenSpcrn89qyZUuzFTef9MILL+grX/lKMUQMHDhQI0aM0E033VTyuKuvvlrjx49XIBDQa6+9pttuu22Xv28zZszQ5MmTZRiGVqxYoe9///utPjYWiymTyRRjVJP6+nrFYrGdnueMM87QF7/4RXm9Xvl8PqXTaf3+97+XJPXu3Vunn366vva1rxWvT/Hkk0/qqKOO0umnn17yvdDk3HPP1eOPP67nn39e0o6v7wceeEAXXXRR8fui6etb2rHi4/7779f/+3//rxgievfurVdeeUUffvhh8RhNdvf7EAAAANgZQgQAAABa9corr2jRokU69NBDdfDBB+uoo47S1KlTddttt+n555/XQQcdJMMw9Mwzz5Q8r6qqSv379y/+ura2thghJGn79u2KRCI7PfewYcM0duxYPffcc83u69+/f0mI+KS6ujpFo1FJ0kEHHaStW7e2+mLpsGHDNHToUE2cOLHk9srKSvXr10/r1q3b6Ywtee+995rdNnnyZJ1++unq3bu3/H6/vF6v3n///ZLHrFmzpvgOfNd1tW3bNh144IFyHEevvfaapk6dqnvvvVeLFy/WkiVL9Oqrryqd3vECfzKZlOu6qqurk8fj0dChQ+X1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hAgAAAAAAAAAbcZ1XSUSCX2w3xEdvyVTs2Hy2jrkpPLOAIAQAQAAAAAAAHRGjz/+uM4888ydPmbhwoUaP358B020+x77zZP65sRxbb4l09zj+unl80bu9DF3ffEAPTxl6I5feCr1+MWn6KI5c9t0jnI59dRTtWDBgj16Tmf9GkHP4i33AAAAAAAAAEBXdvDBB+tnP/uZlixZou9973sdeu4pU6YomUzu9fMXLlzY4u3//d//3ep9uyPv9UsFe6+f35bOfWqVajasU7Sdz/PTn/5UY8eO1b333qvHH3+85L4f/vCHOuaYY/Tggw/qoYceaudJgM6HEAEAAAAAAADsg0mTJukPf/iDJk2apP3220/bt2/f6eM9Ho9ct222LKqvr9/nY9x8881avHhxyW37EjckfXyB6op9OkZbachIyfCIdg8RklRbW6vTTjutJET06tVLhx9+uLZt29YBEwCdEyECAAAAAAAA2EuBQEATJkzQzJkzFYvFdNppp+nRRx8t3v/Zz35Wt99+u6688kqdf/75OvDAA/Xd735XW7du1be//W2NGjVKhmFo7dq1uu+++/TGG2+UHN80TV199dU69thjlUwm9dhjj+npp58u3r9w4UJdffXVeu211yTteNF75syZGjdunHw+n9atW6c77rhD77zzTqsfQzKZbDVonHrqqbrkkkt00003adasWerdu7def/11/fCHP9RJJ52k6dOnKxgM6q9//avuvvtuua6rbFW1VFGh6qpK3fvlITrtoLAa03n9dNFHuv+Nf78YH/JX6r9PHqDTPxOWv9KjNz+ydNWLG7Riy79XUlx2TB/NHNdbptejp9+t13YrVzKfp0K6fsIA/eeh+ylfkB55a7s+nT+eOeczWl5r69G/V8ubSerxxx/Xs88+qwEDBujEE09UIpHQI488omeffbb4nNGjR2vOnDkaPHiwPvzwQz388MO68cYbdcEFF+iDDz5o9XP5j3/8QyeddJLGjBmj5cuXFz+HS5YsUZ8+fUoeW11drUsvvVSf+9zn5PP59NZbb+nOO+/Uxo0bSz7/5513nsLhsJYsWaK333672TnHjx+vadOmaciQIdq2bZuef/55PfLIIy3GLq/Xq29/+9s64YQTVFNTo7q6Oi1YsECPPfZYqx8T0Ba4RgQAAAAAAACwlyZMmKB169Zp/fr1+utf/6rTTz+9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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "authorplot(\"tomaarsen\", 5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook demonstrated how to use graphs and agents to explore the [Hugging Face Posts dataset](https://huggingface.co/datasets/maxiw/hf-posts). It was very illuminating. 💡\n", + "\n", + "We hope you had as much fun as we did!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "local", + "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", + "version": "3.10.16" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/69_Granting_autonomy_to_agents.ipynb b/examples/69_Granting_autonomy_to_agents.ipynb new file mode 100644 index 0000000..e9d8044 --- /dev/null +++ b/examples/69_Granting_autonomy_to_agents.ipynb @@ -0,0 +1,501 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Granting autonomy to agents\n", + "\n", + "txtai 8.0 was recently released and added the ability to run agents. Agents automatically create workflows to answer multi-faceted user requests.\n", + "\n", + "Agents connect a series of tools with a reasoning engine (i.e. LLM). We're giving the agent a degree of latitude to go through it's own internal logic to address a user's request.\n", + "\n", + "This is a huge paradigm shift. We're talking about handing over control to a program and hoping it makes the right decisions itself. Perhaps there are some parallels to sending your kid to college - we hope we've raised them the right way to be able to make smart decisions 😂. \n", + "\n", + "This notebook will focus on examples that give agents autonomy to address requests. With this, we can start to the see the path ahead towards more and more automation of tasks." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[agent,graph]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Let's get creative\n", + "\n", + "In the first example, we'll define an agent that has access to the [txtai-wikipedia](https://huggingface.co/NeuML/txtai-wikipedia) embeddings database. Standard retrieval augmented generation (RAG) and vector search are typically designed for a single search. Agents enable a much more creative and iterative approach to search.\n", + "\n", + "This example won't ask the agent exactly what we're looking for. We'll ask the agent to tell us something interesting based on the agent's own ideas and research." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from txtai import Agent\n", + "\n", + "agent = Agent(\n", + " tools=[{\n", + " \"name\": \"wikipedia\",\n", + " \"description\": \"Searches a Wikipedia database\",\n", + " \"provider\": \"huggingface-hub\",\n", + " \"container\": \"neuml/txtai-wikipedia\"\n", + " }],\n", + " llm=\"Qwen/Qwen3-4B-Instruct-2507\",\n", + " max_iterations=10,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32;20;1m======== New task ========\u001b[0m\n", + "\u001b[37;1m\n", + "I'm bored 🥱. Think about 2-3 disparate topics and use those to search wikipedia to generate something fascinating.\n", + "Write a report summarizing each article. Include a section with a list of article hyperlinks.\n", + "Write the text as Markdown.\n", + "\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: I will use Wikipedia to search for articles related to three disparate topics. The first topic will be the \"Great Barrier Reef\", the second topic will be \"Dark Matter\", and the third topic will be \"The Voynich Manuscript\".\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'wikipedia' with arguments: {'query': 'Great Barrier Reef'}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: The results from the Wikipedia search show that the Great Barrier Reef is a vast and diverse ecosystem that supports a wide variety of life. However, it is also facing many threats, including climate change, pollution, and overfishing.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'wikipedia' with arguments: {'query': 'Dark Matter'}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: The results from the Wikipedia search show that Dark Matter is a hypothetical form of matter that does not interact with light or other electromagnetic radiation, and is implied by gravitational effects that cannot be explained by general relativity.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'wikipedia' with arguments: {'query': 'The Voynich Manuscript'}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: The results from the Wikipedia search show that the Voynich Manuscript is an illustrated codex that has been carbon-dated to the early 15th century, and its origins, authorship, and purpose are still debated.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'final_answer' with arguments: {'answer': \"### Report on Fascinating Topics\\n\\n#### The Great Barrier Reef\\n\\nThe Great Barrier Reef is the world's largest coral reef system, composed of over 2,900 individual reefs and 900 islands stretching for over 2,300 kilometers off the coast of Queensland, Australia. It is a vast and diverse ecosystem that supports a wide variety of life, including coral and other invertebrate species, bony fish, sharks, rays, marine mammals, marine turtles, sea snakes, and algae. However, it is also facing many threats, including climate change, pollution, and overfishing.\\n\\n#### Dark Matter\\n\\nDark Matter is a hypothetical form of matter that does not interact with light or other electromagnetic radiation. It is implied by gravitational effects that cannot be explained by general relativity, and is a topic of ongoing research in astrophysics and cosmology.\\n\\n#### The Voynich Manuscript\\n\\nThe Voynich Manuscript is an illustrated codex that has been carbon-dated to the early 15th century. Its origins, authorship, and purpose are still debated, and it is considered one of the most mysterious and enigmatic manuscripts in the world.\\n\\n### Article Hyperlinks\\n\\n* [Great Barrier Reef](https://en.wikipedia.org/wiki/Great_Barrier_Reef)\\n* [Dark Matter](https://en.wikipedia.org/wiki/Dark_matter)\\n* [Voynich Manuscript](https://en.wikipedia.org/wiki/Voynich_manuscript)\"}\u001b[0m\n" + ] + }, + { + "data": { + "text/markdown": [ + "### Report on Fascinating Topics\n", + "\n", + "#### The Great Barrier Reef\n", + "\n", + "The Great Barrier Reef is the world's largest coral reef system, composed of over 2,900 individual reefs and 900 islands stretching for over 2,300 kilometers off the coast of Queensland, Australia. It is a vast and diverse ecosystem that supports a wide variety of life, including coral and other invertebrate species, bony fish, sharks, rays, marine mammals, marine turtles, sea snakes, and algae. However, it is also facing many threats, including climate change, pollution, and overfishing.\n", + "\n", + "#### Dark Matter\n", + "\n", + "Dark Matter is a hypothetical form of matter that does not interact with light or other electromagnetic radiation. It is implied by gravitational effects that cannot be explained by general relativity, and is a topic of ongoing research in astrophysics and cosmology.\n", + "\n", + "#### The Voynich Manuscript\n", + "\n", + "The Voynich Manuscript is an illustrated codex that has been carbon-dated to the early 15th century. Its origins, authorship, and purpose are still debated, and it is considered one of the most mysterious and enigmatic manuscripts in the world.\n", + "\n", + "### Article Hyperlinks\n", + "\n", + "* [Great Barrier Reef](https://en.wikipedia.org/wiki/Great_Barrier_Reef)\n", + "* [Dark Matter](https://en.wikipedia.org/wiki/Dark_matter)\n", + "* [Voynich Manuscript](https://en.wikipedia.org/wiki/Voynich_manuscript)" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import display, Markdown\n", + "\n", + "answer = agent(\"\"\"\n", + "I'm bored 🥱. Think about 2-3 disparate topics and use those to search wikipedia to generate something fascinating.\n", + "Write a report summarizing each article. Include a section with a list of article hyperlinks.\n", + "Write the text as Markdown.\n", + "\"\"\", maxlength=16000)\n", + "\n", + "display(Markdown(answer))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "💥 Interesting indeed. The fundamental concept of search is we need to know what to look for. In this case, we didn't have that (i.e. we're bored 😀).\n", + "\n", + "Let's go to another example. This time we'll look at the [txtai-hfposts](https://huggingface.co/NeuML/txtai-hfposts) embeddings database. We'll ask the agent to research a specific topic then write a report about what was found.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "agent = Agent(\n", + " tools=[{\n", + " \"name\": \"hfposts\",\n", + " \"description\": \"Searches a database of technical posts on Hugging Face\",\n", + " \"provider\": \"huggingface-hub\",\n", + " \"container\": \"neuml/txtai-hfposts\"\n", + " }],\n", + " llm=\"Qwen/Qwen3-4B-Instruct-2507\",\n", + " max_iterations=10,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32;20;1m======== New task ========\u001b[0m\n", + "\u001b[37;1m\n", + "Read posts about medicine and write a report on what you learned.\n", + "\n", + "The report should be a Markdown table with the following columns.\n", + " - Name\n", + " - Description\n", + " - Link to content\n", + "\n", + "Only include rows that have a valid web url.\n", + "\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: First, I need to search for posts related to medicine using the `hfposts` tool.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'hfposts' with arguments: {'query': 'medicine'}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: Now that I have the posts related to medicine, I need to filter out the ones that have a valid web URL. I will go through each post and check if it contains a URL.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'hfposts' with arguments: {'query': 'medicine url'}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: Now that I have the posts with URLs, I need to extract the relevant information such as the name, description, and link to content. I will create a Markdown table with these details.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'hfposts' with arguments: {'query': 'medicine url'}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: Now that I have the posts with URLs, I can extract the relevant information and create a Markdown table. I will format the data into the required structure.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'final_answer' with arguments: {'answer': '| Name | Description | Link to content |\\n| --- | --- | --- |\\n| OpenBioLLM Release | Introducing OpenBioLLM-Llama3-70B & 8B: The most capable openly available Medical-domain LLMs to date! | [https://huggingface.co/blog/aaditya/openbiollm](https://huggingface.co/blog/aaditya/openbiollm) |\\n| Last Week in Medical AI: Top Research Papers/Models (September 1 - September 7, 2024) | Outperforms industry giants like GPT-4, Gemini, Meditron-70B, Med-PaLM-1, and Med-PaLM-2 in the biomedical domain. | [https://x.com/OpenlifesciAI/status/1832476252260712788](https://x.com/OpenlifesciAI/status/1832476252260712788) |\\n| Last Week in Medical AI: Top Research Papers/Models (August 25 - August 31, 2024) | Includes MultiMed, a Multimodal Medical Benchmark, and A Foundation model for generating chest X-ray images. | [https://x.com/OpenlifesciAI/status/1829984701324448051](https://x.com/OpenlifesciAI/status/1829984701324448051) |\\n| Last Week in Medical AI: Top Research Papers/Models (October 5 - October 12, 2024) | Introduces MMedAgent: Learning to Use Medical Tools with Multi-modal Agent. | [https://youtu.be/OD3C5jirszw](https://youtu.be/OD3C5jirszw) |\\n| Last Week in Medical AI: Top Research Papers/Models (October 26 - November 2, 2024) | Google Presents MDAgents: An Adaptive Collaboration of LLMs for Medical Decision-Making. | [https://x.com/OpenlifesciAI/status/1852685220912464066](https://x.com/OpenlifesciAI/status/1852685220912464066) '}\u001b[0m\n" + ] + }, + { + "data": { + "text/markdown": [ + "| Name | Description | Link to content |\n", + "| --- | --- | --- |\n", + "| OpenBioLLM Release | Introducing OpenBioLLM-Llama3-70B & 8B: The most capable openly available Medical-domain LLMs to date! | [https://huggingface.co/blog/aaditya/openbiollm](https://huggingface.co/blog/aaditya/openbiollm) |\n", + "| Last Week in Medical AI: Top Research Papers/Models (September 1 - September 7, 2024) | Outperforms industry giants like GPT-4, Gemini, Meditron-70B, Med-PaLM-1, and Med-PaLM-2 in the biomedical domain. | [https://x.com/OpenlifesciAI/status/1832476252260712788](https://x.com/OpenlifesciAI/status/1832476252260712788) |\n", + "| Last Week in Medical AI: Top Research Papers/Models (August 25 - August 31, 2024) | Includes MultiMed, a Multimodal Medical Benchmark, and A Foundation model for generating chest X-ray images. | [https://x.com/OpenlifesciAI/status/1829984701324448051](https://x.com/OpenlifesciAI/status/1829984701324448051) |\n", + "| Last Week in Medical AI: Top Research Papers/Models (October 5 - October 12, 2024) | Introduces MMedAgent: Learning to Use Medical Tools with Multi-modal Agent. | [https://youtu.be/OD3C5jirszw](https://youtu.be/OD3C5jirszw) |\n", + "| Last Week in Medical AI: Top Research Papers/Models (October 26 - November 2, 2024) | Google Presents MDAgents: An Adaptive Collaboration of LLMs for Medical Decision-Making. | [https://x.com/OpenlifesciAI/status/1852685220912464066](https://x.com/OpenlifesciAI/status/1852685220912464066) " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "answer = agent(\"\"\"\n", + "Read posts about medicine and write a report on what you learned.\n", + "\n", + "The report should be a Markdown table with the following columns.\n", + " - Name\n", + " - Description\n", + " - Link to content\n", + "\n", + "Only include rows that have a valid web url.\n", + "\"\"\", maxlength=16000)\n", + "\n", + "display(Markdown(answer))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "🚀 Once again, very interesting! This time we asked the agent to go read about a topic and report back. The agent did that and left us with links to explore further." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Autonomous Embeddings\n", + "\n", + "For our last example, we're going to give an agent free rein to control an embeddings database.\n", + "\n", + "First, we will create an empty embeddings database and tell the agent how to add and search for data." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from txtai import Agent, Embeddings\n", + "from txtai.pipeline import Textractor\n", + "from txtai.workflow import Workflow, Task\n", + "\n", + "# Empty embeddings database\n", + "embeddings = Embeddings(\n", + " path=\"intfloat/e5-large\",\n", + " instructions={\"query\": \"query: \", \"data\": \"passage: \"},\n", + " content=True\n", + ")\n", + "\n", + "# Textractor instance\n", + "textractor = Textractor(sections=True, headers={\"user-agent\": \"Mozilla/5.0\"})\n", + "\n", + "def insert(elements):\n", + " \"\"\"\n", + " Inserts elements into the embeddings database.\n", + "\n", + " Args:\n", + " elements: list of strings to insert\n", + " \"\"\"\n", + "\n", + " def upsert(elements):\n", + " embeddings.upsert(elements)\n", + " return elements\n", + "\n", + " # Upsert workflow\n", + " workflow = Workflow([Task(textractor), Task(upsert)])\n", + " list(workflow(elements))\n", + " return f\"{elements} inserted successfully\"\n", + "\n", + "agent = Agent(\n", + " tools=[insert, embeddings.search, \"websearch\"],\n", + " llm=\"Qwen/Qwen3-4B-Instruct-2507\",\n", + " max_iterations=10\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we'll define a workflow process. This creates the rules we want the agent to follow. Then we'll run two requests." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32;20;1m======== New task ========\u001b[0m\n", + "\u001b[37;1m\n", + "Run the following process:\n", + " 1. Search your internal knowledge for txtai\n", + " 2. If not found, find relevant urls and insert those as a list of strings ONLY. Then rerun the search for txtai.\n", + " 3. Write a detailed report about txtai with Markdown sections covering the major topics. Include a section with hyperlink references.\n", + "\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: First, I will attempt to search for \"txtai\" in my internal knowledge base.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'search' with arguments: {'query': 'txtai'}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: Since the search for \"txtai\" did not yield any results, I will proceed to find relevant URLs and insert them as a list of strings.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'web_search' with arguments: {'query': 'txtai'}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: Now that I have found relevant URLs, I will insert them into the database as a list of strings.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'insert' with arguments: {'elements': ['https://github.com/neuml/txtai', 'https://pypi.org/project/txtai/', 'https://neuml.github.io/txtai/', 'https://neuml.com/', 'https://medium.com/neuml/introducing-txtai-the-all-in-one-embeddings-database-c721f4ff91ad', 'https://neuml.github.io/txtai/embeddings/', 'https://neuml.github.io/txtai/examples/']}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: Now that the relevant URLs have been inserted, I will rerun the search for \"txtai\".\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'search' with arguments: {'query': 'txtai'}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'final_answer' with arguments: {'answer': '# txtai Overview\\n\\ntxtai is an all-in-one open-source embeddings database designed for semantic search, LLM orchestration, and language model workflows. It combines vector indexes (both sparse and dense), graph networks, and relational databases to offer a comprehensive solution for various tasks.\\n\\n## Semantic Search\\n\\ntxtai supports semantic search, enabling users to find relevant information based on the meaning of the text rather than just keywords. This makes it suitable for applications such as recommendation systems, content retrieval, and more.\\n\\n## LLM Orchestration\\n\\ntxtai includes support for Large Language Models (LLMs) orchestration, which allows users to integrate and manage different LLMs within their workflows. This feature is particularly useful for tasks such as prompt chaining, summarization, transcription, translation, and more.\\n\\n## Embeddings Databases\\n\\ntxtai provides a unified interface for embedding, indexing, searching, and processing text and multimedia data. Users can create, index, and search embeddings using code snippets and examples provided in the documentation.\\n\\n## Hyperlink References\\n\\n- [txtai on GitHub](https://github.com/neuml/txtai)\\n- [PyPI - txtai](https://pypi.org/project/txtai/)\\n- [txtai Documentation](https://neuml.github.io/txtai/)\\n- [txtai Examples](https://neuml.github.io/txtai/examples/)\\n- [txtai Embeddings](https://neuml.github.io/txtai/embeddings/)'}\u001b[0m\n" + ] + }, + { + "data": { + "text/markdown": [ + "# txtai Overview\n", + "\n", + "txtai is an all-in-one open-source embeddings database designed for semantic search, LLM orchestration, and language model workflows. It combines vector indexes (both sparse and dense), graph networks, and relational databases to offer a comprehensive solution for various tasks.\n", + "\n", + "## Semantic Search\n", + "\n", + "txtai supports semantic search, enabling users to find relevant information based on the meaning of the text rather than just keywords. This makes it suitable for applications such as recommendation systems, content retrieval, and more.\n", + "\n", + "## LLM Orchestration\n", + "\n", + "txtai includes support for Large Language Models (LLMs) orchestration, which allows users to integrate and manage different LLMs within their workflows. This feature is particularly useful for tasks such as prompt chaining, summarization, transcription, translation, and more.\n", + "\n", + "## Embeddings Databases\n", + "\n", + "txtai provides a unified interface for embedding, indexing, searching, and processing text and multimedia data. Users can create, index, and search embeddings using code snippets and examples provided in the documentation.\n", + "\n", + "## Hyperlink References\n", + "\n", + "- [txtai on GitHub](https://github.com/neuml/txtai)\n", + "- [PyPI - txtai](https://pypi.org/project/txtai/)\n", + "- [txtai Documentation](https://neuml.github.io/txtai/)\n", + "- [txtai Examples](https://neuml.github.io/txtai/examples/)\n", + "- [txtai Embeddings](https://neuml.github.io/txtai/embeddings/)" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "prompt = \"\"\"\n", + "Run the following process:\n", + " 1. Search your internal knowledge for {topic}\n", + " 2. If not found, find relevant urls and insert those as a list of strings ONLY. Then rerun the search for {topic}.\n", + " 3. Write a detailed report about {topic} with Markdown sections covering the major topics. Include a section with hyperlink references.\n", + "\"\"\"\n", + "\n", + "answer = agent(prompt.format(topic=\"txtai\"))\n", + "display(Markdown(answer))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32;20;1m======== New task ========\u001b[0m\n", + "\u001b[37;1m\n", + "Run the following process:\n", + " 1. Search your internal knowledge for openscholar\n", + " 2. If not found, find relevant urls and insert those as a list of strings ONLY. Then rerun the search for openscholar.\n", + " 3. Write a detailed report about openscholar with Markdown sections covering the major topics. Include a section with hyperlink references.\n", + "\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: First, I will search my internal knowledge for \"openscholar\".\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'search' with arguments: {'query': 'openscholar'}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: Since the search did not return any relevant information about \"openscholar\", I will find relevant URLs and insert them as a list of strings. Then, I will rerun the search for \"openscholar\".\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'web_search' with arguments: {'query': 'openscholar'}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: Now that I have found relevant URLs about \"openscholar\", I will insert these URLs into the database.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'insert' with arguments: {'elements': ['https://openscholar.allen.ai/', 'https://allenai.org/blog/openscholar', 'https://arxiv.org/abs/2411.14199', 'https://github.com/AkariAsai/OpenScholar', 'https://www.marktechpost.com/2024/11/22/the-allen-institute-for-ai-ai2-introduces-openscholar-an-open-ecosystem-for-literature-synthesis-featuring-advanced-datastores-and-expert-level-results/', 'https://venturebeat.com/ai/openscholar-the-open-source-a-i-thats-outperforming-gpt-4o-in-scientific-research/']}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: Now that the relevant URLs have been inserted, I will rerun the search for \"openscholar\" to gather more detailed information.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'search' with arguments: {'query': 'openscholar'}\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: The search has returned relevant information about OpenScholar. I will now compile a detailed report about OpenScholar with Markdown sections covering the major topics.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'final_answer' with arguments: {'answer': \"# OpenScholar Report\\n\\n## Introduction\\nOpenScholar is an advanced AI system developed by the Allen Institute for AI and the University of Washington. It aims to revolutionize scientific research by processing millions of research papers and providing citation-backed answers to complex research questions.\\n\\n## Key Features\\n- **Data Processing**: Capable of handling 45 million research papers instantly.\\n- **Citation Accuracy**: Achieves high citation accuracy, challenging proprietary AI systems.\\n- **Customizable Tools**: Offers specialized tools and built-in features for creating impactful websites.\\n\\n## Research Impact\\n- **Data Utilization**: Integrates a vast corpus of scientific literature, making it the largest open-access corpus for scientific literature synthesis.\\n- **Expert-Level Results**: Provides answers that match or exceed those of human experts in various scientific domains.\\n\\n## Future Directions\\n- **Continued Development**: Ongoing efforts to improve the system's capabilities and expand its applications.\\n- **Community Engagement**: Plans for expanding the user base and fostering a community around OpenScholar.\\n\\n## References\\n- [OpenScholar](https://openscholar.allen.ai/)\\n- [VentureBeat Article](https://venturebeat.com/ai/openscholar-the-open-source-a-i-thats-outperforming-gpt-4o-in-scientific-research/)\\n- [Allen Institute for AI](https://allenai.org/blog/openscholar)\"}\u001b[0m\n" + ] + }, + { + "data": { + "text/markdown": [ + "# OpenScholar Report\n", + "\n", + "## Introduction\n", + "OpenScholar is an advanced AI system developed by the Allen Institute for AI and the University of Washington. It aims to revolutionize scientific research by processing millions of research papers and providing citation-backed answers to complex research questions.\n", + "\n", + "## Key Features\n", + "- **Data Processing**: Capable of handling 45 million research papers instantly.\n", + "- **Citation Accuracy**: Achieves high citation accuracy, challenging proprietary AI systems.\n", + "- **Customizable Tools**: Offers specialized tools and built-in features for creating impactful websites.\n", + "\n", + "## Research Impact\n", + "- **Data Utilization**: Integrates a vast corpus of scientific literature, making it the largest open-access corpus for scientific literature synthesis.\n", + "- **Expert-Level Results**: Provides answers that match or exceed those of human experts in various scientific domains.\n", + "\n", + "## Future Directions\n", + "- **Continued Development**: Ongoing efforts to improve the system's capabilities and expand its applications.\n", + "- **Community Engagement**: Plans for expanding the user base and fostering a community around OpenScholar.\n", + "\n", + "## References\n", + "- [OpenScholar](https://openscholar.allen.ai/)\n", + "- [VentureBeat Article](https://venturebeat.com/ai/openscholar-the-open-source-a-i-thats-outperforming-gpt-4o-in-scientific-research/)\n", + "- [Allen Institute for AI](https://allenai.org/blog/openscholar)" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "answer = agent(prompt.format(topic=\"openscholar\"))\n", + "display(Markdown(answer))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "🔥 Amazing.\n", + "\n", + "Remember, we started with an empty embeddings database. Then we gave basic instructions on how to use the available tools. From there, the agent autonomously operated and answered user requests. The agent also stored what it learned for future requests. This gave the agent it's own internal memory.\n", + "\n", + "Of course, we could program a process that implements this workflow. But think about the productivity gains we're opening up to so many more people. We're enabling people to control a process simply by pairing a set of tools with a description of what they want, in English.\n", + "\n", + "Exciting times!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook demonstrated ways to run agents in a more autonomous fashion. While the technology isn't perfect, we can certainly see the path ahead where new models will continue to do a better job. With the right agents and targeted tools, much can be done now though. \n", + "\n", + "Think about the differences between now and 6-12 months ago. Where will we be in another 6-12 months!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "local", + "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", + "version": "3.9.20" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/70_Getting_started_with_LLM_APIs.ipynb b/examples/70_Getting_started_with_LLM_APIs.ipynb new file mode 100644 index 0000000..7c2d3d8 --- /dev/null +++ b/examples/70_Getting_started_with_LLM_APIs.ipynb @@ -0,0 +1,792 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "7btcrgth5z6f" + }, + "source": [ + "# Getting started with LLM APIs\n", + "\n", + "_Thank you to [Igor Ribeiro Lima](https://github.com/igorlima) for writing this notebook!_\n", + "\n", + "This notebook demonstrates how to call different LLM APIs using just one set of common functions.\n", + "\n", + "While each LLM has its own configuration, here's a cool perk of using [`txtai`](https://neuml.github.io/txtai/) plus [`litellm`](https://docs.litellm.ai/docs/) working behind the scenes. The big win with `txtai` is that it helps standardize inputs and outputs across several LLMs. **This means we can seamlessly call any LLM API**.\n", + "\n", + "In this notebook, we use one set of common functions to call _**Gemini**_, _**VertexAI**_, _**Mistral**_, _**Cohere**_, _**AWS Bedrock**_, _**OpenAI**_, _**Claude by Anthropic**_, _**Groq**_, _and more_!\n", + "\n", + "Isn't that amazing? One method for many different LLM APIs - **that's the beauty of these libraries**!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lhrk9y766Lw1" + }, + "source": [ + "## Install dependencies\n", + "\n", + "This session guides you through all the essential dependencies you'll need to run the Python script for various LLM APIs.\n", + "\n", + "You can choose the dependencies based on your specific needs. Each dependency is carefully commented on in the code, so you'll know exactly which API it supports.\n", + "\n", + "The core dependencies you'll typically need are: `txtai` and `txtai[pipeline]`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "czzRItdyN-Zj" + }, + "outputs": [], + "source": [ + "%%capture\n", + "# The main point here is to follow the pattern of other notebooks.\n", + "# For more information, kindly refer to the note in the bullet right below.\n", + "\n", + "# !pip install txtai==8.1.0\n", + "# !pip install txtai[pipeline]\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline]\n", + "\n", + "# to use Vertex AI\n", + "# https://github.com/BerriAI/litellm/issues/5483\n", + "# !pip install google-cloud-aiplatform==1.75.0\n", + "!pip install google-cloud-aiplatform\n", + "\n", + "# to use AWS Bedrock\n", + "# https://docs.litellm.ai/docs/providers/bedrock\n", + "# !pip install boto3==1.35.88\n", + "!pip install boto3" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "evHD8Kw09Aa0" + }, + "source": [ + "- _**A friendly reminder**: things can change unexpectedly in the ever-evolving world of coding. One day, your code works flawlessly; the next, it might throw a tantrum for no apparent reason. That's why it's essential to specify the dependencies' versions using the latest available versions when writing the code._\n", + " - Even recognizing the importance of version control, each version has one line above as a comment in the code. It serves as a guide to help you track dependencies and ensures the code runs smoothly across different environments, even if something goes awry.\n", + " - While there is [a trade-off](https://github.com/neuml/txtai/pull/844#issuecomment-2564294186) in limiting code to a specific version, noting today's version as a comment should help you protect against potential issues with future updates." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "wuCvBPCL-VQG" + }, + "source": [ + "## LLM API Configuration\n", + "\n", + "This session is like a special Python dictionary that holds all the cool configurations for our LLM AI model. It includes details like environment variables, the model's name, text embedding parameters, and other fancy settings.\n", + "\n", + "One important key here is `IS_ENABLED`. When it's set to `True`, it's like giving the model a green light to shine! But if you ever feel like taking a break or don't need it for a while, you can easily set this key to `False`, and the model will chill out." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "jyahNyOrO2yp", + "outputId": "8143c2b0-a6a9-429b-a66f-93f39e6885e4" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "config set!\n" + ] + } + ], + "source": [ + "import os, getpass\n", + "from txtai import LLM, Embeddings\n", + "\n", + "# https://neuml.github.io/txtai/install/\n", + "# https://neuml.github.io/txtai/pipeline/text/llm/#example\n", + "# https://neuml.github.io/txtai/embeddings/configuration/vectors/#method\n", + "# https://docs.litellm.ai/docs/embedding/supported_embedding\n", + "LLM_MODEL_CONFIG = {\n", + " 'GEMINI': {\n", + " 'IS_ENABLED': True,\n", + " 'ENV_VAR': ['GEMINI_API_KEY'],\n", + " # https://docs.litellm.ai/docs/providers/gemini\n", + " # https://ai.google.dev/gemini-api/docs/models/gemini\n", + " 'LLM_MODEL_NAME': 'gemini/gemini-pro',\n", + " # https://github.com/BerriAI/litellm/tree/12c4e7e695edb07d403dd14fc768a736638bd3d1/litellm/llms/vertex_ai\n", + " # https://github.com/BerriAI/litellm/blob/e19bb55e3b4c6a858b6e364302ebbf6633a51de5/model_prices_and_context_window.json#L2625\n", + " 'TEXT_EMBEDDING_PATH': 'gemini/text-embedding-004'\n", + " },\n", + " 'COHERE': {\n", + " 'IS_ENABLED': False,\n", + " 'ENV_VAR': ['COHERE_API_KEY'],\n", + " # https://docs.litellm.ai/docs/providers/cohere\n", + " 'LLM_MODEL_NAME': 'command-light',\n", + " 'TEXT_EMBEDDING_PATH': 'cohere/embed-english-v3.0'\n", + " },\n", + " 'AWS_BEDROCK': {\n", + " 'IS_ENABLED': False,\n", + " 'ENV_VAR': ['AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AWS_REGION_NAME'],\n", + " # https://docs.litellm.ai/docs/providers/bedrock\n", + " 'LLM_MODEL_NAME': 'bedrock/amazon.titan-text-lite-v1',\n", + " 'TEXT_EMBEDDING_PATH': 'amazon.titan-embed-text-v1'\n", + " },\n", + " 'MISTRAL': {\n", + " 'IS_ENABLED': False,\n", + " 'ENV_VAR': ['MISTRAL_API_KEY'],\n", + " # https://docs.litellm.ai/docs/providers/mistral\n", + " 'LLM_MODEL_NAME': 'mistral/mistral-tiny',\n", + " 'TEXT_EMBEDDING_PATH': 'mistral/mistral-embed'\n", + " },\n", + " 'VERTEXAI': {\n", + " 'IS_ENABLED': False,\n", + " 'ENV_VAR': ['GOOGLE_APPLICATION_CREDENTIALS', 'GOOGLE_VERTEX_PROJECT', 'GOOGLE_VERTEX_LOCATION'],\n", + " 'ENV_VAR_SETUP': None,\n", + " # https://docs.litellm.ai/docs/providers/vertex\n", + " 'LLM_MODEL_NAME': 'vertex_ai/gemini-pro',\n", + " 'TEXT_EMBEDDING_PATH': 'vertex_ai/text-embedding-004'\n", + " },\n", + " 'GROQ': {\n", + " 'IS_ENABLED': False,\n", + " 'ENV_VAR': ['GROQ_API_KEY'],\n", + " # https://docs.litellm.ai/docs/providers/groq\n", + " # https://console.groq.com/docs/models\n", + " 'LLM_MODEL_NAME': 'groq/llama3-8b-8192',\n", + " # the line below has been commented out for a reason.\n", + " # to understand why, check out the Groq section right below.\n", + " # https://groq.com/retrieval-augmented-generation-with-groq-api/\n", + " # 'TEXT_EMBEDDING_PATH': 'jinaai/jina-embeddings-v2-base-en',\n", + " },\n", + " 'OPENAI': {\n", + " 'IS_ENABLED': False,\n", + " 'ENV_VAR': ['OPENAI_API_KEY'],\n", + " # https://docs.litellm.ai/docs/providers/openai\n", + " # https://platform.openai.com/docs/models\n", + " 'LLM_MODEL_NAME': 'gpt-4o-mini-2024-07-18',\n", + " 'TEXT_EMBEDDING_PATH': 'text-embedding-3-small',\n", + " },\n", + " 'CLAUDE': {\n", + " 'IS_ENABLED': False,\n", + " # You might need the Voyage API key, depending on the embedding model you choose. Read more below!\n", + " # 'ENV_VAR': ['ANTHROPIC_API_KEY', 'VOYAGE_API_KEY'],\n", + " 'ENV_VAR': ['ANTHROPIC_API_KEY'],\n", + " # https://docs.litellm.ai/docs/providers/anthropic\n", + " # https://docs.anthropic.com/en/docs/about-claude/models\n", + " 'LLM_MODEL_NAME': 'anthropic/claude-3-5-haiku-20241022',\n", + " # the line below has been commented out for a reason.\n", + " # to understand why, check out the Claude Anthropic section right below.\n", + " # https://docs.anthropic.com/en/docs/build-with-claude/embeddings\n", + " # 'TEXT_EMBEDDING_PATH': 'voyage/voyage-01',\n", + " },\n", + " 'VOYAGE': {\n", + " 'IS_ENABLED': False,\n", + " # You might need the Anthropic API key, depending on the LLM you choose. Read more below!\n", + " # 'ENV_VAR': ['ANTHROPIC_API_KEY', 'VOYAGE_API_KEY'],\n", + " 'ENV_VAR': ['VOYAGE_API_KEY'],\n", + " # the line below has been commented out for a reason.\n", + " # to understand why, check out the Voyage AI section right below.\n", + " # https://docs.litellm.ai/docs/providers/voyage\n", + " # https://docs.voyageai.com/docs/introduction\n", + " # 'LLM_MODEL_NAME': 'anthropic/claude-3-5-haiku-20241022',\n", + " #\n", + " # https://docs.voyageai.com/docs/embeddings\n", + " 'TEXT_EMBEDDING_PATH': 'voyage/voyage-01',\n", + " }\n", + "}\n", + "\n", + "import litellm\n", + "# # https://github.com/BerriAI/litellm/blob/11932d0576a073d83f38a418cbdf6b2d8d4ff46f/litellm/litellm_core_utils/get_llm_provider_logic.py#L322\n", + "litellm.suppress_debug_info = True\n", + "# https://docs.litellm.ai/docs/debugging/local_debugging#set-verbose\n", + "litellm.set_verbose=False\n", + "\n", + "def customsetup():\n", + " def vertexai():\n", + " # https://docs.litellm.ai/docs/embedding/supported_embedding#usage---embedding\n", + " # https://docs.litellm.ai/docs/providers/vertex\n", + " litellm.vertex_project = os.environ['GOOGLE_VERTEX_PROJECT']\n", + " litellm.vertex_location = os.environ['GOOGLE_VERTEX_LOCATION']\n", + "\n", + " LLM_MODEL_CONFIG['VERTEXAI']['ENV_VAR_SETUP'] = vertexai()\n", + "\n", + "customsetup()\n", + "\n", + "LLM_MODELS = {k: v for k, v in LLM_MODEL_CONFIG.items() if v['IS_ENABLED']}\n", + "ENV_VARS = [v['ENV_VAR'] for k, v in LLM_MODELS.items()]\n", + "ENV_VARS = [x for xs in ENV_VARS for x in xs] # flatten array\n", + "\n", + "print(\"config set!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pgQuDv4fZgHe" + }, + "source": [ + "### Vertex AI" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "wG_2u_jKTyYg" + }, + "source": [ + "When working with VertexAI in a Jupyter notebook, an environment variable called `GOOGLE_APPLICATION_CREDENTIALS` points to a credentials file. This variable is like giving VertexAI a secret map to find the JSON file containing your service account key.\n", + "\n", + "Imagine you've set `GOOGLE_APPLICATION_CREDENTIALS` to `application_default_credentials.json`. The service account key is stored in a file with that exact name. And guess what? This file must be in the same place you're working _(your current working directory)_.\n", + "\n", + "Here's how your directory might look:\n", + "```\n", + ".\n", + "├── ..\n", + "├── sample_data/\n", + "└── application_default_credentials.json\n", + "```\n", + "\n", + "If you use Google Colab, your directory structure will differ slightly, but don't worry! It'll look something like this:\n", + "\n", + "```\n", + ".\n", + "├── bin\n", + "├── boot\n", + "├── content/\n", + "│ ├── sample_data/\n", + "│ └── application_default_credentials.json\n", + "├── datalab\n", + "├── dev\n", + "├── etc\n", + "├── home\n", + "├── lib\n", + "├── lib32\n", + "└── lib64\n", + "```\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0KsLGZFtrZVT" + }, + "source": [ + "### Groq" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-4RUn2dUrcbZ" + }, + "source": [ + "It's good to make the most of [free resources](https://github.com/BerriAI/litellm/issues/4922#issuecomment-2374234548) like this one! Of course, free options often come with some limitations, and this is no different. Currently, Groq doesn't offer an API for text embeddings.\n", + "\n", + "The Groq team highlights a pre-trained model that can be used locally. You can learn more about it on the [Groq Blog](https://groq.com/retrieval-augmented-generation-with-groq-api/), where they discuss the `jinaai/jina-embeddings-v2-base-en`, a model that can be hosted locally.\n", + "\n", + "This notebook primarily focuses on well-known online API models, so I've commented on the Groq line for text embedding. However, if you're curious, you can explore the model Groq recommends for local use by uncommenting it." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "I7a3d8EsrdBc" + }, + "source": [ + "### Claude Anthropic" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "RHmrKCbZriz_" + }, + "source": [ + "While Claude Anthropic isn't a free resource, they're well-known for their top-notch LLM outputs. The only tiny hiccup for this Notebook context is that they don't offer an API for text embeddings.\n", + "\n", + "But don't worry. In their documentation, they recommend checking out the Voyage AI embedding model. You can find all the deets right [here](https://docs.anthropic.com/en/docs/build-with-claude/embeddings).\n", + "\n", + "The Claude team highlights the Voyage AI embedding model on their documentation page. Voyage is an online resource, though the Claude Anthropic team does not host it.\n", + "\n", + "This notebook is about well-known online API models. So, I've commented on the Claude config line for text embedding. But if you're curious and want to explore the model Claude recommends, uncomment that line and explore away!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IYpWxGBR2COD" + }, + "source": [ + "### Voyage AI" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "j6vIcO0A2FjD" + }, + "source": [ + "Voyage AI provides only [top-notch embeddings](https://docs.voyageai.com/docs/introduction). While it doesn't offer [its LLM model](https://docs.voyageai.com/docs/embeddings), that doesn't diminish its offerings. The cool thing? Their embeddings are versatile and can be integrated into any model you choose. So, feel free to experiment and have fun with them! That's the beauty of this notebook - it's all about exploration and learning." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xZg16h4KHpE5" + }, + "source": [ + "## Environment Variables\n", + "\n", + "This session has scripts to reset or set environment variables _(env vars)_. Using env vars is a great way to keep those sensitive API KEY values safe and sound, away from unwanted eyes." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Cqh5j0a4JcHg" + }, + "source": [ + "### Script to Reset Environment Variables\n", + "\n", + "There are a bunch of reasons to reset the environment variable here. Let's go over a few:\n", + "- **Switching API Keys:** Sometimes, we need to change to a different API key.\n", + "- **Typos Happen:** We're all human, and typos can sneak in!\n", + "- **Skipping Variables:** Sometimes we need to skip one or another, so we set the env var to empty.\n", + "- **Fresh Start:** After tweaking a script or setup, it's always good to rerun with a fresh environment.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "VejHDLF1aKub" + }, + "outputs": [], + "source": [ + "if 'MISTRAL_API_KEY' in os.environ:\n", + " del os.environ['MISTRAL_API_KEY']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "vzrNvU5XJpIm", + "outputId": "05181b36-caa8-417d-fcd6-159e15118594" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "unset env var: GEMINI_API_KEY\n", + "unset env var: COHERE_API_KEY\n", + "unset env var: AWS_ACCESS_KEY_ID\n", + "unset env var: AWS_SECRET_ACCESS_KEY\n", + "unset env var: AWS_REGION_NAME\n", + "unset env var: MISTRAL_API_KEY\n", + "unset env var: GOOGLE_APPLICATION_CREDENTIALS\n", + "unset env var: GOOGLE_VERTEX_PROJECT\n", + "unset env var: GOOGLE_VERTEX_LOCATION\n" + ] + } + ], + "source": [ + "for ENV_VARS in [v['ENV_VAR'] for k, v in LLM_MODEL_CONFIG.items()]:\n", + " for ENV_VAR in ENV_VARS:\n", + " if ENV_VAR in os.environ:\n", + " del os.environ[ENV_VAR]\n", + " print(f'unset env var: {ENV_VAR}')\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GqZceA9qJIdq" + }, + "source": [ + "### Script to Set Environment Variables\n", + "\n", + "In this session, the script will stroll through the config dictionary to cherry-pick only the enabled LLM models.\n", + "\n", + "Once your favorites have been gathered, the script will prompt you to set all the necessary environment variables.\n", + "\n", + "The more LLM models you have enabled, the more prompts you'll see - but don't worry, each environment variable will prompt only once." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "zQmXuvbsHSwR", + "outputId": "07e94a99-817b-4fae-e649-c6e0337bb1d1" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "enter GEMINI_API_KEY: ··········\n", + "enter COHERE_API_KEY: ··········\n", + "enter AWS_ACCESS_KEY_ID: ··········\n", + "enter AWS_SECRET_ACCESS_KEY: ··········\n", + "enter AWS_REGION_NAME: ··········\n", + "enter MISTRAL_API_KEY: ··········\n", + "enter GOOGLE_APPLICATION_CREDENTIALS: ··········\n", + "enter GOOGLE_VERTEX_PROJECT: ··········\n", + "enter GOOGLE_VERTEX_LOCATION: ··········\n", + "enter GROQ_API_KEY: ··········\n", + "enter OPENAI_API_KEY: ··········\n", + "enter ANTHROPIC_API_KEY: ··········\n", + "enter VOYAGE_API_KEY: ··········\n", + "running custom env var setup for VERTEXAI...\n", + "done setup for VERTEXAI\n", + "all env var set!\n" + ] + } + ], + "source": [ + "for ENV_VAR in ENV_VARS:\n", + " os.environ[ENV_VAR] = getpass.getpass(f\"enter {ENV_VAR}: \") if not ENV_VAR in os.environ else os.environ[ENV_VAR]\n", + "\n", + "LLM_MODELS_WITH_CUSTOM_SETUP = {k: v for k, v in LLM_MODELS.items() if 'ENV_VAR_SETUP' in v}\n", + "for LLM_MODEL, LLM_CONFIG in LLM_MODELS_WITH_CUSTOM_SETUP.items():\n", + " print(f\"running custom env var setup for {LLM_MODEL}...\")\n", + " LLM_CONFIG['ENV_VAR_SETUP']()\n", + " print(f\"done setup for {LLM_MODEL}\")\n", + "\n", + "print(\"all env var set!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tnTSFJZcJW5p" + }, + "source": [ + "# Code Snippet\n", + "\n", + "You can run the code snippet after installing the necessary dependencies and setting up the required environment variables!\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "oPq-sHZifHFZ" + }, + "source": [ + "## Introduction\n", + "\n", + "This code snippet is designed to achieve only two tasks: _(i)_ run an LLM Pipeline; _(ii)_ text Embed using the LLM Model." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gKd9GB4UfJTt" + }, + "source": [ + "The code snippet has two tasks:\n", + "1. **Running an LLM Pipeline**:\n", + " - To kickstart an LLM pipeline, you'll need a prompt and a model name. It's as simple as that!\n", + "2. **Text Embedding using the LLM Model**:\n", + " - To embed text with the LLM model, you'll need some text and the name of the text embedding model.\n", + "\n", + "In this script, we'll run an LLM pipeline for every given LLM and embed text using the provided LLM model.\n", + "\n", + "Here's the heart of the script:\n", + "```python\n", + "LLM_MODEL_NAME = \"model-name\"\n", + "TEXT_EMBEDDING_PATH = \"text-embedding-name\"\n", + "# A text prompt to send through the LLM pipeline\n", + "LLM_PROMPT_INPUT = \"Where is one place you'd go in Washington, DC?\"\n", + "# The embeddings dataset is versatile! It plays with lists, datasets, or even generators.\n", + "EMBEDDING_DATA = [\n", + " \"US tops 5 million confirmed virus cases\",\n", + " \"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\",\n", + " \"Beijing mobilises invasion craft along coast as Taiwan tensions escalate\"\n", + "]\n", + "# LLM PIPELINE\n", + "llm = LLM(LLM_MODEL_NAME, method=\"litellm\")\n", + "print(llm([{\"role\": \"user\", \"content\": LLM_PROMPT_INPUT}]))\n", + "# TEXT EMBEDDING\n", + "embeddings = Embeddings(path=TEXT_EMBEDDING_PATH, method=\"litellm\")\n", + "embeddings.index(EMBEDDING_DATA) # create an index for the text list\n", + "for query in (\"feel good story\", \"climate change\"): # now, let's embark on an embeddings search for each query\n", + " # extract the uid of the first result\n", + " # search result format: (uid, score)\n", + " uid = embeddings.search(query, 1)[0][0]\n", + " # print the text\n", + " print(\"%-20s %s\" % (query, EMBEDDING_DATA[uid]))\n", + "```\n", + "\n", + "- **friendly reminder**: _the library's author often [points out](https://github.com/neuml/txtai/pull/844#issuecomment-2563561232) that it's [not necessary](https://github.com/neuml/txtai/issues/843#issuecomment-2563244810) to explicitly pass the second argument `method='litellm'`. When you're learning something new, it's okay to avoid relying on shortcuts or \"magic\" until you're more comfortable. Once you understand the library better, you can start using these convenient features to your advantage. In this introduction, I'm intentionally including the second argument `method='litellm'` in the function. However, I'm choosing to leave it out in the Playground section_." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1I6o5f1ue_KX" + }, + "source": [ + "## Playground\n", + "\n", + "Once you've installed the necessary dependencies and configured the environment variables, you can play with and explore the code snippet. Enjoy your coding journey! 😊\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "A3NetE3Sh0e0", + "outputId": "d9be990a-1f57-4abc-f2be-86f8e8ef7c6c" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--------------------------------------------------\n", + "GEMINI\n", + "The National Mall\n", + "..................................................\n", + "Query Best Match\n", + "..................................................\n", + "feel good story Maine man wins $1M from $25 lottery ticket\n", + "climate change Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\n", + "public health story US tops 5 million confirmed virus cases\n", + "war Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "wildlife The National Park Service warns against sacrificing slower friends in a bear attack\n", + "asia Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "lucky Maine man wins $1M from $25 lottery ticket\n", + "dishonest junk Make huge profits without work, earn up to $100,000 a day\n", + "--------------------------------------------------\n", + "COHERE\n", + "As an AI chatbot, I do not have personal preferences or the ability to travel. However, I can suggest popular tourist destinations in Washington, DC, that many people enjoy visiting:\n", + "\n", + "1. The White House: The official residence and workplace of the President of the United States is a symbol of American democracy. Visitors can take a tour of the White House to learn about its history and see the beautiful architecture.\n", + "\n", + "2. National Mall: This iconic open park stretches from the United States Capitol to the Lincoln Memorial, featuring various monuments and memorials. Some notable landmarks include the Washington Monument, the Lincoln Memorial Reflecting Pool, the Vietnam Veterans Memorial, and the National World War II Memorial.\n", + "\n", + "3. Smithsonian Institution: The world's largest museum and research complex offers a wealth of knowledge and cultural experiences. It includes renowned museums such as the National Air and Space Museum, National Museum of Natural History, National Museum of African American History and Culture, and many more, all offering free admission.\n", + "\n", + "4. United States Capitol: A visit to the Capitol allows you to explore the seat of the United States Congress and learn about the legislative process. The Capitol also features impressive architecture and art, including the iconic Capitol Dome and the National Statuary Hall Collection.\n", + "\n", + "5. National Gallery of Art: Located on the National Mall, this renowned art museum houses a vast collection of paintings, sculptures, and other artworks from the Middle Ages to the present. It offers a chance to appreciate works by famous artists like Leonardo da Vinci, Rembrandt, and Claude Monet.\n", + "\n", + "These are just a few highlights, but Washington, DC, offers many more attractions, including historic sites, beautiful parks, vibrant neighborhoods, and cultural institutions, ensuring there's something for everyone to enjoy.\n", + "..................................................\n", + "Query Best Match\n", + "..................................................\n", + "feel good story Maine man wins $1M from $25 lottery ticket\n", + "climate change Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\n", + "public health story US tops 5 million confirmed virus cases\n", + "war Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "wildlife The National Park Service warns against sacrificing slower friends in a bear attack\n", + "asia Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "lucky Maine man wins $1M from $25 lottery ticket\n", + "dishonest junk US tops 5 million confirmed virus cases\n", + "--------------------------------------------------\n", + "AWS_BEDROCK\n", + "I'd recommend the Smithsonian National Air and Space Museum. It is located in Washington, DC, and is the world's largest and most visited museum complex.\n", + "..................................................\n", + "Query Best Match\n", + "..................................................\n", + "feel good story Maine man wins $1M from $25 lottery ticket\n", + "climate change Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\n", + "public health story US tops 5 million confirmed virus cases\n", + "war Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "wildlife The National Park Service warns against sacrificing slower friends in a bear attack\n", + "asia Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "lucky Maine man wins $1M from $25 lottery ticket\n", + "dishonest junk Make huge profits without work, earn up to $100,000 a day\n", + "--------------------------------------------------\n", + "MISTRAL\n", + "I would love to visit the Smithsonian National Museum of Natural History in Washington, DC. It's one of the most popular and largest museums in the world, with a vast array of exhibits showcasing natural history, including dinosaur fossils, mineral and gemstone collections, and marine life. I find the diversity and depth of knowledge presented in this museum fascinating. Additionally, it's free to the public, which makes it an accessible and enjoyable experience for everyone.\n", + "..................................................\n", + "Query Best Match\n", + "..................................................\n", + "feel good story The National Park Service warns against sacrificing slower friends in a bear attack\n", + "climate change Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\n", + "public health story The National Park Service warns against sacrificing slower friends in a bear attack\n", + "war The National Park Service warns against sacrificing slower friends in a bear attack\n", + "wildlife The National Park Service warns against sacrificing slower friends in a bear attack\n", + "asia Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\n", + "lucky Maine man wins $1M from $25 lottery ticket\n", + "dishonest junk Make huge profits without work, earn up to $100,000 a day\n", + "--------------------------------------------------\n", + "VERTEXAI\n", + "Washington, DC, is a fascinating city with a wealth of historical and cultural attractions. If I had the opportunity to visit, I would definitely make a stop at the Smithsonian National Air and Space Museum. This museum is home to a vast collection of aircraft and spacecraft, from the Wright brothers' first plane to the Apollo 11 command module. I am particularly interested in the history of space exploration, so I would be excited to see these iconic artifacts up close.\n", + "\n", + "In addition to the Air and Space Museum, there are many other places in Washington, DC, that I would like to visit. The National Mall is a beautiful park that is home to many of the city's most famous monuments, including the Washington Monument, the Lincoln Memorial, and the Vietnam Veterans Memorial. I would also like to visit the White House, the Capitol Building, and the Supreme Court. These buildings are all important symbols of American democracy, and I would be honored to have the opportunity to see them in person.\n", + "\n", + "Washington, DC, is a city with a rich history and a bright future. I am confident that it will continue to be a major center of culture and government for many years to come.\n", + "..................................................\n", + "Query Best Match\n", + "..................................................\n", + "feel good story Maine man wins $1M from $25 lottery ticket\n", + "climate change Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\n", + "public health story US tops 5 million confirmed virus cases\n", + "war Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "wildlife The National Park Service warns against sacrificing slower friends in a bear attack\n", + "asia Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "lucky Maine man wins $1M from $25 lottery ticket\n", + "dishonest junk Make huge profits without work, earn up to $100,000 a day\n", + "--------------------------------------------------\n", + "GROQ\n", + "Washington, D.C. is a city with a plethora of amazing places to visit! But, if I had to pick just one place, I'd recommend the National Mall.\n", + "\n", + "The National Mall is a beautiful stretch of parkland that stretches from the Lincoln Memorial to the United States Capitol Building. It's home to some of the city's most iconic landmarks, including the Washington Monument, World War II Memorial, and Vietnam Veterans Memorial.\n", + "\n", + "You can take a leisurely stroll along the Mall, enjoy the scenic views of the city, and stop at one of the many museums or memorials along the way. The National Mall is also a great place to people-watch, with street performers and musicians adding to the lively atmosphere.\n", + "\n", + "Personally, I'd recommend starting at the Lincoln Memorial, where you can take in the stunning views of the Reflecting Pool and the Washington Monument. From there, you can walk to the World War II Memorial, where you can pay your respects to the millions of Americans who served in the war.\n", + "\n", + "Overall, the National Mall is a must-see destination in Washington, D.C. – and it's completely free!\n", + "--------------------------------------------------\n", + "OPENAI\n", + "One iconic place to visit in Washington, DC, is the National Mall. It's home to many famous monuments and memorials, including the Lincoln Memorial, the Washington Monument, and the Vietnam Veterans Memorial. The expansive park offers a great opportunity to explore the history and culture of the nation, and it's a beautiful spot for walking, picnicking, and enjoying the iconic views.\n", + "..................................................\n", + "Query Best Match\n", + "..................................................\n", + "feel good story Maine man wins $1M from $25 lottery ticket\n", + "climate change Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\n", + "public health story US tops 5 million confirmed virus cases\n", + "war Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "wildlife The National Park Service warns against sacrificing slower friends in a bear attack\n", + "asia Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "lucky Maine man wins $1M from $25 lottery ticket\n", + "dishonest junk Make huge profits without work, earn up to $100,000 a day\n", + "--------------------------------------------------\n", + "CLAUDE\n", + "One great place to visit in Washington, DC is the Smithsonian National Air and Space Museum. It's located on the National Mall and features fascinating exhibits about aviation and space exploration, including famous aircraft and spacecraft like the Wright Brothers' plane and the Apollo 11 command module. The museum is free to enter and is very popular with visitors of all ages.\n", + "--------------------------------------------------\n", + "VOYAGE\n", + "..................................................\n", + "Query Best Match\n", + "..................................................\n", + "feel good story Maine man wins $1M from $25 lottery ticket\n", + "climate change Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\n", + "public health story Maine man wins $1M from $25 lottery ticket\n", + "war The National Park Service warns against sacrificing slower friends in a bear attack\n", + "wildlife The National Park Service warns against sacrificing slower friends in a bear attack\n", + "asia Beijing mobilises invasion craft along coast as Taiwan tensions escalate\n", + "lucky Maine man wins $1M from $25 lottery ticket\n", + "dishonest junk Make huge profits without work, earn up to $100,000 a day\n", + "--------------------------------------------------\n" + ] + } + ], + "source": [ + "# A text prompt to run through the LLM pipeline\n", + "# https://neuml.github.io/txtai/pipeline/text/llm/\n", + "LLM_PROMPT_INPUT = \"Where is one place you'd go in Washington, DC?\"\n", + "\n", + "# The embeddings dataset is versatile! It plays with lists, datasets, or even generators.\n", + "# https://neuml.github.io/txtai/embeddings/\n", + "EMBEDDING_DATA = [\n", + " \"US tops 5 million confirmed virus cases\",\n", + " \"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\",\n", + " \"Beijing mobilises invasion craft along coast as Taiwan tensions escalate\",\n", + " \"The National Park Service warns against sacrificing slower friends in a bear attack\",\n", + " \"Maine man wins $1M from $25 lottery ticket\",\n", + " \"Make huge profits without work, earn up to $100,000 a day\"\n", + "]\n", + "\n", + "# https://neuml.github.io/txtai/pipeline/text/llm/\n", + "def runllm(prompt=\"\", path=None):\n", + " if path:\n", + " # A quick note: you can skip specifying the `method` argument.\n", + " # There's an autodetection logic designed to recognize it as a `litellm` model.\n", + " # \n", + " # llm = LLM(LLM_MODEL_NAME, method=\"litellm\")\n", + " # \n", + " llm = LLM(path)\n", + "\n", + " # OR: print(llm(LLM_PROMPT_INPUT, defaultrole=\"user\"))\n", + " print(llm([{\"role\": \"user\", \"content\": prompt}]))\n", + "\n", + "# https://neuml.github.io/txtai/embeddings/\n", + "def runembeddings(data=None, path=None):\n", + " if path:\n", + " embeddings = Embeddings(\n", + " path=path,\n", + " # a quick note: you can skip specifying the `method` argument - there is autodetection logic\n", + " # method=\"litellm\"\n", + " )\n", + " # create an index for the list of text\n", + " embeddings.index(data)\n", + "\n", + " print(\".\" * 50)\n", + " print(\"%-20s %s\" % (\"Query\", \"Best Match\"))\n", + " print(\".\" * 50)\n", + " \n", + " # run an embeddings search for each query\n", + " for query in (\"feel good story\", \"climate change\",\n", + " \"public health story\", \"war\", \"wildlife\", \"asia\",\n", + " \"lucky\", \"dishonest junk\"):\n", + " # extract uid of first result\n", + " # search result format: (uid, score)\n", + " uid = embeddings.search(query, 1)[0][0]\n", + " # print text\n", + " print(\"%-20s %s\" % (query, data[uid]))\n", + "\n", + "# Let's LOOP THROUGH each enabled LLM and embedding model.\n", + "for LLM_MODEL, LLM_CONFIG in LLM_MODELS.items():\n", + " LLM_MODEL_NAME = LLM_CONFIG['LLM_MODEL_NAME'] if 'LLM_MODEL_NAME' in LLM_CONFIG else None\n", + " TEXT_EMBEDDING_PATH = LLM_CONFIG['TEXT_EMBEDDING_PATH'] if 'TEXT_EMBEDDING_PATH' in LLM_CONFIG else None\n", + " print(\"-\" * 50)\n", + " print(LLM_MODEL)\n", + "\n", + " # https://neuml.github.io/txtai/pipeline/text/llm/\n", + " runllm(prompt=LLM_PROMPT_INPUT, path=LLM_MODEL_NAME)\n", + "\n", + " # https://neuml.github.io/txtai/embeddings/\n", + " runembeddings(data=EMBEDDING_DATA, path=TEXT_EMBEDDING_PATH)\n", + "\n", + "print(\"-\" * 50)" + ] + } + ], + "metadata": { + "colab": { + "collapsed_sections": [ + "lhrk9y766Lw1", + "wuCvBPCL-VQG", + "pgQuDv4fZgHe", + "0KsLGZFtrZVT", + "I7a3d8EsrdBc", + "xZg16h4KHpE5", + "Cqh5j0a4JcHg", + "oPq-sHZifHFZ" + ], + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/examples/71_Analyzing_LinkedIn_Company_Posts_with_Graphs_and_Agents.ipynb b/examples/71_Analyzing_LinkedIn_Company_Posts_with_Graphs_and_Agents.ipynb new file mode 100644 index 0000000..efe3341 --- /dev/null +++ b/examples/71_Analyzing_LinkedIn_Company_Posts_with_Graphs_and_Agents.ipynb @@ -0,0 +1,573 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Analyzing LinkedIn Company Posts with Graphs and Agents\n", + "\n", + "This notebook will analyze [NeuML's LinkedIn company posts](https://hf.co/datasets/neuml/neuml-linkedin-202501) over the last 12 months as of January 2025. It will build an embeddings database with an associated graph and topic modeling. The most popular topics will be explored using graphs and vector search queries.\n", + "\n", + "From there we'll use Agents to investigate the dataset and find ways to increase engagement with our future posts using past history." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[agent,graph] datasets" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# LinkedIn Company Posts\n", + "\n", + "The next section builds the LinkedIn company posts dataset for NeuML. First, it downloads the company posts dataset then it creates an embeddings database.\n", + "\n", + "There is also custom logic to create topic names for the generated topic clusters vs the standard naming method.\n", + "\n", + "_If you'd like to instead use your own company posts, see these [steps on the dataset page](https://hf.co/datasets/NeuML/neuml-linkedin-202501)._" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from datasets import load_dataset\n", + "from tqdm.auto import tqdm\n", + "from txtai import Embeddings, LLM\n", + "\n", + "def title(text):\n", + " prompt = f\"\"\"\n", + "Create a simple, concise topic for the following text. Only return the topic name.\n", + "\n", + "Text:\n", + "{text}\n", + "\"\"\"\n", + "\n", + " return llm([{\"role\": \"user\", \"content\": prompt}], maxlength=2048)\n", + "\n", + "def data():\n", + " for x in posts:\n", + " x[\"url\"] = x.pop(\"Post link\")\n", + " x[\"text\"] = x.pop(\"Post title\")\n", + " x[\"id\"] = title(x[\"text\"])\n", + " yield {k.lower().replace(\" \", \"\"): v for k, v in x.items()}\n", + "\n", + "posts = load_dataset(\"neuml/neuml-linkedin-202501\", split=\"train\")\n", + "\n", + "llm = LLM(\"Qwen/Qwen3-4B-Instruct-2507\")\n", + "\n", + "embeddings = Embeddings(\n", + " autoid=\"uuid5\",\n", + " path=\"intfloat/e5-large\",\n", + " instructions={\"query\": \"query: \", \"data\": \"passage: \"},\n", + " content=True,\n", + " functions = [\n", + " {\"name\": \"graph\", \"function\": \"graph.attribute\"}\n", + " ],\n", + " expressions = [\n", + " {\"name\": \"topic\", \"expression\": \"graph(indexid, 'topic')\"}\n", + " ],\n", + " graph={\n", + " \"approximate\": False,\n", + " \"topics\": {\"resolution\": 6}\n", + " },\n", + ")\n", + "embeddings.index(tqdm(data(), total=len(posts)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now that the embeddings database is built, let's generate the topic names using an LLM." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def topic(text):\n", + " prompt = f\"\"\"\n", + "Create a simple, concise topic for the following text. Only return the topic name.\n", + "\n", + "Text:\n", + "{text}\"\"\"\n", + "\n", + " return llm([{\"role\": \"user\", \"content\": prompt}], maxlength=5000)\n", + "\n", + "topics = {}\n", + "for name, nodes in tqdm(embeddings.graph.topics.items()):\n", + " name = topic(\"\\n\".join(embeddings.graph.attribute(x, \"text\") for x in nodes))\n", + " topics[name] = nodes\n", + "\n", + " # Set topic on each node\n", + " for node in nodes:\n", + " embeddings.graph.addattribute(node, \"topic\", name)\n", + "\n", + "embeddings.graph.topics = topics" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Both of the sections above should run within a minute with a modern GPU. Building the embeddings database and generating the topics can optionally be skipped using the following model from the Hugging Face Hub.\n", + "\n", + "```python\n", + "embeddings = Embeddings()\n", + "embeddings.load(provider=\"huggingface-hub\", container=\"neuml/txtai-neuml-linkedin\")\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Popular topics\n", + "\n", + "Now that we have an embeddings database, graph and topics, let's explore the data! The next section shows the Top 5 posts for each of the Top 5 most popular topics." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "#### Graph RAG\n", + "1. _06/09/2024 - 2567 views_ - Knowledge Graphs (KGs) are a 🔥 topic now. But how do you build them? Check out this article that uses embeddings models to automatically build a semantic graph. And it's multimodal! https://lnkd.in/eMsAXarn \n", + "2. _03/10/2024 - 2356 views_ - One powerful feature of txtai is that graphs can be automatically created using semantic similarity. Did you know that relationships can also be manually loaded using techniques such as relationship extraction with LLMs? Check out this article on building knowledge graphs with LLMs and txtai. https://lnkd.in/dBy_H4C2\n", + "3. _06/16/2024 - 2141 views_ - Want to use LLMs to automatically extraction entity-relationship models? And load them into a knowledge graph? Then check out this article. https://lnkd.in/dBy_H4C2\n", + "4. _03/05/2024 - 1803 views_ - Curious about Graph RAG? Did you know that txtai has an ready-to-use Embeddings graph database for popular Wikipedia articles on the HF Hub? https://lnkd.in/e3fPr6fd\n", + "5. _03/05/2024 - 1803 views_ - 📈 Let's talk about Graph RAG. We've been looking at graph-based approaches for context generation since 2022. The best use case we've seen for Graph RAG is for more complex questions and research. For example, think of a problem as a road trip with multiple stops. A graph path traversal is a great way to pick up various concepts as context, concepts which may not be directly related and not picked up by a simple keyword/vector search. The attached image shows two graph path traversal examples. The first shows the path between a squirrel and the Red Sox winning the world series. The 2nd shows an image path from a person parachuting and someone holding a french horn. Note the progression of both the text and images along the way. There is also another example of traversing history from the end of the Roman Empire to the Norman Conquest of England. For problems like this, graphs do a great job. If the answer is a simple retrieval of a single entry, Graph RAG doesn't add much value. Like all things, Graph RAG isn't the be-all and end-all. Read more in the articles below. Semantic Graph Intro: https://lnkd.in/eMsAXarn Graph RAG: https://lnkd.in/d-BSjuj7\n", + "#### Vector Embeddings Innovations\n", + "1. _02/24/2024 - 7202 views_ - 🚀 Excited to release an updated version of PubMedBERT Embeddings with Matryoshka Representation Learning support! With this model, dynamic embeddings sizes of 64, 128, 256, 384 and 512 can be used in addition to the full size of 768. It's a great way to save space with a relatively low level of accuracy tradeoff. Thank you to Tom Aarsen, Philipp Schmid and the Hugging Face team for adding this feature to Sentence Transformers! https://lnkd.in/edNYMyrF\n", + "2. _10/21/2024 - 4042 views_ - Running an embeddings database with limited compute? Model2Vec is a technique to turn any sentence transformer into a really small static model. The next release of txtai will have support for this new vectorization method! Example txtai code: https://lnkd.in/eFSdT7B5 GitHub Project: https://lnkd.in/eEpWxZx8 Blogpost: https://lnkd.in/e4iPg4wi\n", + "3. _01/03/2025 - 2850 views_ - 🧬⚕️🔬 What if we said you can have a competitive 8 million parameter model that can index as fast as BM25? Or a 100K parameter 200KB model that retains knowledge? We're excited to release static PubMedBERT Embeddings models! These are a set of static models distilled with the great Model2Vec library by The Minish Lab Thank you to Stéphan Tulkens and Thomas van Dongen for creating Model2Vec! https://lnkd.in/esgtC_2X\n", + "4. _03/17/2024 - 2651 views_ - We're seeing a lot of progress with methods to improve the efficiency of vector embeddings generation. For example, vector models trained with Matryoshka Representation Learning, push the most important information to the front of the vector, enabling us to only keep a portion of an embeddings vector. Another method, quantization, compresses the number of values that can be represented by each bucket in an embeddings vector. With binary quantization, the values can even be reduced down to a single bit. While all this is exciting, how do we know that these methods will work well enough for our requirements? We have to test it of course! The BEIR evaluation framework has a number of sources and it's easy to add new custom sources to test. Check out this article for a full example on this topic. https://lnkd.in/db5Jx_fE\n", + "5. _03/01/2024 - 1722 views_ - Would you trade 1% of accuracy to only have to store 1% of the data? Exciting to see the innovation happening in the vector space and we're not talking about 1.58-bit LLMs. With Matryoshka Embeddings, we can drastically reduce vector dimensionality while maintaining a strong level of accuracy. Check out this example that combines Matryoshka Embeddings with Faiss 4-bit scalar quantization 🚀\n", + "#### Text Summarization with txtai\n", + "1. _05/20/2024 - 2703 views_ - LLMs can translate and summarize but that doesn't mean they should. Check out this simple summarization method that's still quite popular. https://lnkd.in/dM6XB_Rc \n", + "2. _04/01/2024 - 2605 views_ - 🚀 We're excited to announce a new 500M parameter model 🌌 Space Time LLM. Recent breakthroughs in LLMs have resulted in an uncanny and game changing ability to predict future outcomes. Impressive advances in quantization and compression such as 1-bit LLMs have contributed to this phenomenal breakthrough in predictive capabilities. This model redefines our understanding of what and how LLMs learn. Check out this model and see what you can predict today! https://lnkd.in/exdy4R5G \n", + "3. _07/09/2024 - 1884 views_ - 🤔 Machine translation just ask a LLM to do it? While LLMs can translate that doesn't mean they should. What if we could utilize smaller models that were trained to translate between specific languages? What if there was a pipeline that automatically loads models based on the source to target language? 🚀 Enter txtai's translation pipeline! The Translation pipeline automatically detects languages and searches the Hugging Face Hub for the best specialized model to perform the translation. These specialized models are often smaller than LLMs and much faster. Link to code: https://lnkd.in/eefCwRDu\n", + "4. _07/11/2024 - 1710 views_ - We often hear one say they have a LLM and they want to solve a problem. LLMs aren't always the best tool for the job. Let's take text classification using a sentiment dataset. Running LLM prompts for this dataset only leads to 58% accuracy! Training with a 4.4M parameter model has 91% accuracy. BERT is 93%. Sure we can fine-tune the LLM for this task but why spend an hour vs 8 minutes? Be willing to accept the simpler solution. Link to code: https://lnkd.in/d38xBm6X\n", + "5. _07/07/2024 - 1326 views_ - 🗎 Want to summarize webpages, word documents, PDFs and more? Did you know there are models pre-built for summarization that pre-date the latest LLMs? And that they do a decent job and are faster? txtai supports pre-trained summarization models and LLMs for summarization. Either can be run as Python workflows or FastAPI services. Link to code: https://lnkd.in/emg4QUfm \n", + "#### txtai Vector Search Performance\n", + "1. _07/07/2024 - 2393 views_ - ✨ Let's build on our previous BM25 post and take tokenization into account. We'll compare LangChain's BM25 retriever, the recently released bm25s library (built with Scipy sparse matrices) and txtai. We'll use the same tokenization method for all 3, the Unicode Text Segmentation algorithm (UAX 29). Keep in mind that this is relevant to those using hybrid search (vector + keyword). 1.6M ArXiv abstracts were evaluated. txtai's index time was slower but search times were significantly faster. txtai used almost 6x less RAM. Link to code: https://lnkd.in/ekSxN9pJ\n", + "2. _07/05/2024 - 2096 views_ - ⚡ LangChain and LlamaIndex both use the Rank-BM25 library to provide in-line BM25 document retrieval. Rank-BM25 is a great way to quickly stand up a BM25 search index for a small number of documents. But it doesn't scale as it's built to run in memory. txtai has it's own BM25 implementation in Python. Term vectors are built harnessing the native performance of the Python arrays package. These term vectors are stored in a SQLite database. LRU caching stores frequently used vectors in memory. This combination of factors enables a highly performant index. For this comparison, 2.3M ArXiv abstracts were used. LangChain ran out of memory (32 GB of RAM). The test was scaled to 1.6M abstracts. txtai had 2x slower index times but 13x faster search times than LangChain. LangChain used 25 GB of RAM, txtai used 3.8 GB of RAM. Link to code: https://lnkd.in/eD7qn6qn\n", + "3. _07/05/2024 - 1673 views_ - 💥⚾ BM25 continues to be a heavy hitter in the information retrieval space. Did you know that txtai has a BM25 component built for speed💨? BM25 term vectors are built harnessing the native performance of the Python arrays package. These term vectors are stored in a SQLite database. LRU caching stores frequently used vectors in memory. This combination of factors enables a highly performant index. https://lnkd.in/eA3ui6cQ\n", + "4. _07/01/2024 - 1095 views_ - ⚡ Faiss is a great vector indexing library. It has so many features past just a flat index. txtai automatically creates a performant Faiss index scaled by the size of the incoming data. The index type can also be fully customized with configuration. This shows the power of a full-featured and long-standing integration. See how this compares to LlamaIndex: https://lnkd.in/eWqU5z3U\n", + "5. _07/03/2024 - 747 views_ - 💥 Hnswlib is a great vector indexing library. It's integrated into a number of vector databases. txtai utilizes the same core pipeline for generating embeddings and storing vectors regardless of the end components. There has been a careful focus in building a highly performant and efficient vector database implementation that runs great locally. txtai uses mmap-ing and other techniques to ensure that memory limits are respected. Streaming vector generation and offloading those vectors during index creation allows txtai to build large local indexes whereas other implementations run out of memory. See how this compares to Chroma DB (txtai is 3x faster for the same dataset): https://lnkd.in/eY_nMA85\n", + "#### txtai Community Updates\n", + "1. _02/21/2024 - 1322 views_ - 🕒 The countdown to txtai 7.0 is on. In the meantime, checkout this notebook with the code showing how all these graphs you've seen work! https://lnkd.in/dBy_H4C2\n", + "2. _04/09/2024 - 1046 views_ - Check out this interesting article that uses txtai to solve crossword puzzles. https://lnkd.in/ewdqZWzi\n", + "3. _03/22/2024 - 908 views_ - 🔥 Excellent tutorial on building a product search engine with txtai from NeuralNine! https://lnkd.in/e8xsvbm2\n", + "4. _03/24/2024 - 628 views_ - Nice video covering how to build an AI Search engine with txtai. https://lnkd.in/e2NGemzv Check out the NeuML YouTube channel for links to this video and more: https://lnkd.in/e9cJQ79k\n", + "5. _02/28/2024 - 605 views_ - ICYMI: Check out what's new in txtai 7.0 https://lnkd.in/efuQipp9 \n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import display, Markdown\n", + "\n", + "# Get top 5 topics by popularity\n", + "popular = embeddings.search(\"SELECT topic FROM txtai GROUP BY topic ORDER BY sum(impressions) DESC LIMIT 5\")\n", + "\n", + "output = \"\"\n", + "for topic in popular:\n", + " topic = topic[\"topic\"]\n", + " output += f\"#### {topic}\\n\"\n", + " results = embeddings.search(\n", + " \"SELECT text, createddate, impressions FROM txtai WHERE topic = :topic ORDER BY impressions DESC LIMIT 5\",\n", + " parameters={\"topic\": topic}\n", + " )\n", + "\n", + " for i, x in enumerate(results):\n", + " text = x[\"text\"].replace(\"\\n\", \" \")\n", + " output += f\"{i + 1}. _{x['createddate']} - {x['impressions']} views_ - {text}\\n\"\n", + "\n", + "display(Markdown(output))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And lets also look at the top 5 most popular posts overall." + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "1. _02/24/2024 - 7202 views_ - 🚀 Excited to release an updated version of PubMedBERT Embeddings with Matryoshka Representation Learning support! With this model, dynamic embeddings sizes of 64, 128, 256, 384 and 512 can be used in addition to the full size of 768. It's a great way to save space with a relatively low level of accuracy tradeoff. Thank you to Tom Aarsen, Philipp Schmid and the Hugging Face team for adding this feature to Sentence Transformers! https://lnkd.in/edNYMyrF\n", + "2. _12/28/2024 - 5229 views_ - 💥 Surprise! One last release in 2024. 🧬⚕️🔬 This time it's paperai and paperetl 2.3. paperai is a semantic search and workflow application for medical/scientific papers. paperetl is a companion library for parsing papers. This update enables the latest txtai features and fixes some long standing bugs. More to come in 2025 and maybe one more thing in 2024! paperai: https://lnkd.in/egFSSP4 paperetl: https://lnkd.in/ecuuVG2\n", + "3. _10/21/2024 - 4042 views_ - Running an embeddings database with limited compute? Model2Vec is a technique to turn any sentence transformer into a really small static model. The next release of txtai will have support for this new vectorization method! Example txtai code: https://lnkd.in/eFSdT7B5 GitHub Project: https://lnkd.in/eEpWxZx8 Blogpost: https://lnkd.in/e4iPg4wi\n", + "4. _01/03/2025 - 2850 views_ - 🧬⚕️🔬 What if we said you can have a competitive 8 million parameter model that can index as fast as BM25? Or a 100K parameter 200KB model that retains knowledge? We're excited to release static PubMedBERT Embeddings models! These are a set of static models distilled with the great Model2Vec library by The Minish Lab Thank you to Stéphan Tulkens and Thomas van Dongen for creating Model2Vec! https://lnkd.in/esgtC_2X\n", + "5. _05/20/2024 - 2703 views_ - LLMs can translate and summarize but that doesn't mean they should. Check out this simple summarization method that's still quite popular. https://lnkd.in/dM6XB_Rc \n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "output = \"\"\n", + "for i, x in enumerate(embeddings.search(\"SELECT id, text, createddate, impressions FROM txtai ORDER BY impressions DESC\", 5)):\n", + " text = x[\"text\"].replace(\"\\n\", \" \")\n", + " output += f\"{i + 1}. _{x['createddate']} - {x['impressions']} views_ - {text}\\n\"\n", + "\n", + "display(Markdown(output))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A couple themes are shown in the data. Posts covering `Graph RAG`, `Vector Embeddings Innovations` and `medical literature related models` all had high engagement.\n", + "\n", + "Let's plot a subgraph of the nodes related to `medical literature`." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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ZEUiET377LO67bbbwvPU19fry1/+sg455BBt2bJF99xzjyTJj8RUitV0ee7bPjJFdTFLr6zP6axDRitmmXpk8RZd9ZtV8oKyJOmYPWt16eF7aP/RjvyytGB1Vl95ZqWWNxclSZPqbP3zi40664llOueQ0Tpoj4SWNbm6/Ncr9cLKTHiu/UY5unb2BB02KaWcF+ilI67Rd269OQxpKl9r3/d17LHH6u2339Yll1yiqVOn6txzz9VBBx2kfD6vv/3tb7r99tvV2tqqK664QjNnztTMmTN10kknSZLmzp27C6+07lW+7lOmTNHhhx+uTCajBx54QI8//nh4mwkTJujLX/6y9t9/f61Zs0a33XbbNsfZ3uvkuOOO0xlnnCG1VWpI0vXXX6/58+crmUzqi1/8oo444ghFo1G98cYb+s53vqO33nqrVx8nAAAAAAAAMJgRRGC3HHfccXr66ad13nnnafr06brkkku0YcMG/eIXv5Darpy/5557tHLlStXX1+u8887TFVdcoauuuqrDcb7whS/otttu05YtW3TWWWfpm9/8pk499VT5vq/x48dr3rx5+v73v6958+apvr5eF154oS688ELNmzcvPMbJJ5+s++67Tz/84Q/Dz+VyOd1www3atGmT9tprL1122WXK5/P6yU9+0qPHZxiGNm7cqP/+7/9Wa2urGhsbdckll2jz5s36/e9/H95u5syZ2rx5s770pS9pwoQJuuaaa7R06dLwebjiiis0atQofelLX5Lv+7rgggtUX18vL1a/3fO/b0qNCqVAH3/gTU2us3Xrh6eoKe/rf/64RpKUtE19d8EGLdqQV9I2deVR43TfnGl6/z2vqdzuONfOnqCrf7tKb2wq6IuHjtEDJ03Tu767UE0FX7UxS49/eh/9+OXN+q/frpITNXX9rLH62te+1qFt1XHHHacnn3xSF1xwwdZzJ5P63//9Xz399NO6/fbbFYvFdM4554T3u+222zRx4kQtX748DF5aWlq0xx69O4PiU5/6lO6//37de++9OvTQQ3X++edr5cqV+vvf/y7DMPT1r39dTU1NOu+885RMJrtsFbW918mzzz6rPffcU+95z3vC5yObzUqS/vu//1uu6+qKK65QNpvVxz72MX3729/WqaeeqnQ63auPEwAAAAAAABisaM2E3bJhwwbdfvvtWrlypZ555hn97Gc/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gQceuM3zKklNTU3auHGjxo0bpzVr1nT4s27duh0eGxgK+JcnAADAMFCpkmhqalJTU5M8z1MikdCIESNUW1sr27arvUQAAADspPXr12v//ffX2LFjVVtbK8MwNGrUKF188cX63ve+p4ULF2revHn6zGc+o/333z+837p16zRr1iw1NDQolUpJbfMMjjrqKE2bNk177723rrnmGhmGoUgkokQioVQqJcMwFI/HNXLkSI0aNUojR45UQ0OD6urqlEqlFIlEFIlENDZhaWxNVPuPdnT2IaP15Gf21cL1ed324vqt52r15JYCnX3IGE2ps3X83nW67PCOoceqlqKCclnH7V2nkfGIklFTzQVfm3MlnT5zlPasj+moKSl945iOg7G/eOgYzdm/QfuMiGlaQ0yf2K9B6zKemguBXnz1db399tu6+uqrtc8++2i//fbTVVddpX/+859hq6WXXnpJ06dP1wc/+EFNmDBBZ5xxhqZOnbrDr8Xjjz+uZDKpa665Rvvuu68mTJigY489VpMmTZIk3XvvvTrllFM0Z84cTZw4UXvuuaeOP/54ffKTn+yFVwIw8NGaCQAAYJgplUpKp9PKZDKKxWKKx+Oqq6uT7/vhLAlKxAEAAAa+hx56SFdeeaXuvfdeOY6jT3/607rsssv0+uuv62c/+5kkacGCBXryySd19dVX65xzzlGxWNRdd92lL3zhC/rIRz6izZs36wtf+ILuv/9+nX/++br99tuVTqf1xBNPqLa2Nmz1Wfn9MAgC5fN5BUEQ/vF9P7zwZfLkyfrNDeepFJTV6vp6Y1NBN/95nX7w0qawymFzvqTzf7FC//X+8Trn3aP1yrqcrnl2tR44aVr42NZmPF3/3Fp99egJuvUjU/TQwi06/xcrdPYTy3TdsRP1/Fn7a+mWgq76zSr9/DP7hvfLFH1dcNhY7dUQUxBIL63Lau5Pl6psWnLSa/Rf//VfuvDCC/V///d/CoJACxYs0C233BLef8GCBfrRj36kL3zhC7JtW7/85S/161//Wnvttdd2vxatra265JJLdO655+rmm29WEARaunSpFi5cKEl6+umn5bquPvWpT+kLX/iCCoWCli1bpkceeaSXXxXAwGTMnDmTiYUAAADDXCQSCQdcq63HbaFQ2G6vWwAAAAwMpmnKsiyZprndP+2Vy+UOYUL7UKH9x+0HTPeUZ6f0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eZ6nDRs26Ne//rV+/OMfD7gr3H3f1/Xf+Lreu+8ESVKhFGhFs6u7/75R97y0qc/Oe9UzK2V0NQ0i8LV58hGauPjR3T7Hs88+u92///GPf6wHHnig24qG9roKGCqDnpubm8PPs4kCAL3DNM2w8iEajapcLst1XWWzWRWLxWovr08kEgklEgkFQaDW1la5rlvtJfW5eDwuwzCUy+WqvRQAAxxBBAAAANCFF198UTfccINs29Z73/teXXTRRSqVSnrggQeqtqZK/+zOSnZSP/znJl3/3BrFI6Y+deBI3XjcZDUXfD32WtNOnydqGvKC7W/Ip91uAhnTUuuYRmkngwjDMLYJEj772c+G/3/UUUdp7ty5uvjii8P7FAoFJZPJDtUK3bVI6ipgqNx2qF2NCwDVYhhGh/BBkorF4pDflLdtW6lUSqZpKp/PD6tZCfF4nGoIAD1CEAEAAAB0wfM8NTVt3cR/8skndeSRR+rwww/XAw88oE9+8pM6/vjjNW7cOKXTaf35z3/WHXfcEQ6gPO6443T++efr+uuv17nnnqsxY8bo5Zdf1o033qiNGzeG5zjiiCN02mmnaerUqdq0aZPmz5/foeri2Wef1U033aT3vOc9mjVrlh566CH98Ic/7LhOO6WyaSnvFbUhu7X1w7zn1+rEAxp0/D51euy1Jn3t6PH68L71Gl9ja0PW0yOLtujGP61VqS1LuPzIcfrwPnW6++8bdcnhe2hSna3RN7ykj02v1+VHjNOeDTHlS4FeXZ/TZx99Wzkv2KY10xOn7KPFG/IqlMo69eCDFFzwmH7+5BO67777wjBh8uTJ+tKXvqR99tlH69at0913362vf/3rmjdvnv72t791eFxBECiXy4VBQmtrq8rlslauXBkGC6eccoo++tGPqq6uTu+8846+973vacGCBeEx9txzT51//vmaMWOGCoWCnnvuOd1+++1DclAoAFSLYRiybVuO44Thg+d5SqfTKhaLQ3qD2rIspVIp2bYdVnt0dcHAUEU1BICdQRABAAAA9ECxWFRdXZ3Utkl+6623au3atRo/frwuvvhinXvuubr55pvD28diMX32s5/Vddddp1KppIsvvljXXHONLrjgAknSgQceqCuvvFK33XabXnnlFY0fP16XXnqpJOm+++4Lj3P66afrrrvu0u23397l5ka+dmKX6y2UAtnW1tZJ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WrQImqZhhx12QDQaheM4+NCHPoSvfOUruOWWW/Diiy/iAx/4AM4++2ysX78eL774IkzTBAAkEgls3rwZvu8jEokAPaFTMplENrv1OZ988sm46aabkEqlsHLlSpx22mn41Kc+heuvvx6vvPIKOjs7MXPmzK3PRVFw1VVX4dVXX8U555wD13Vx4okn4qqrrsKpp57KMLhBNGObpeEIhUKIRCLwfR/xeByFQmHM9ykBaNu4BG0blyBrTcWWmfsgMXkeikZ06w08FxLee518SEFQoeYTiG5cggkrn4aZWjfmYyEialYMIoiIiIjGOR/A6l0+t7X/cS1DCGxt0+QpOtbs8jls9+KtI9odWFr1INpFiMGYrHqontKKiGZRicHIpW1pBjKS4GA4368nRVFg2zZUVUUqlQoWo0uJwe77778//vKXv0BRFOi6Dtd18dOf/rTstocddhj+/ve/w/M8rFixAuvWrcPHPvYxPPzwwwCAGTNmYNWqVWU/c9ZZZ+FTn/oU0LMofswxxwx6zMViEY8++igOO+wwLFmyBIcddhgee+yxPp8FRxxxBDZt2oRrr70WALBq1SpMnDgRXz7jTFz8fy8jbGj4wu4TcMafV+CJd5Nbj+WBFXjlrN2C+5ge1XD87hOwx/VLsD61tS3Nouc24sDtozh+twn4/hNrh/1a//Izs+H6PkxVhiJLeDeWx32vdQMAPrtTO2TNwC233AL0nGM33HADbrvtNuy+++5Yvnw5IpEInn76abz11lvwPA9Lly4NKlw++clP4uGHH8Zdd90FAFi2bBnmzJmDI444As8880ywQOu67pDnnAgzLMuC4zg46qijcO211wbv4dq1a4MqlwULFkCWZfzoRz8Kfv7KK6/En//8Z+y5557497//PezXhypDhIalwYNosyQqBkXw0KzE55amachms0in01X5LDVT67bObnn1DyjqFjLRGcjZ0+CqJnxZgeS5UIpZhJJrEU6shsr2S0REAIMIIiIiIkpMnre173G9yAoSU3ZDYvI8tG1cMuDNxCKKCB8URQn6VIt2MaNtu0DDV4uKiNHOMxhNu6KBAoLhVBj0vl2rMAwDlmXB8zzEYrFBQz3f9/Hiiy/illtugaZpOOSQQ+C6Lp544ongNpFIBPvttx/OOeec4GsiMBCL2P359a9/jT/+8Y/Yf//98YUvfGFYx/7QQw/huuuuw4033oiPfexjOOuss4L2MsLMmTOxdOnSsq8tWbIEkZCB6VENbWEDhirjP+veay0Vy20dJC3sMsmEKkt49iu7lN2Pocjoyo6sbc0lf1uNx1cksV27jssPnIGLHluNWG7rfcybbGLGxHbcdtttZT+jaRra2tqwdu1aPPTQQ7jsssvw73//G//973+xePFidHV1Bc/1gQce6PNcFy5cOKJjBIDXX389qDbbbrvtoOs6/vvf//Z72zlz5mD69Ol48MEHy76u6zqmTZs24semkWulNkvDEQ6HEQ6Hg2qtWgUqaiGF6ObXEN38Wk0ej4iomTGIICIiIhrnNs1aAHgeUM8d7p6LTbM+3ieIEDusxawHSZKClhGFQqGpd242M8/zyioiKjEUeaTtikbTmqhS7YpalSRJsCwLoVAIuVwOqVRqWK9RNpvFa6+9hvb2dvzyl7/Ej3/8Yxx22GHBIvRBBx0EwzBw/fXXl/2coijYbrvtsGbNGqxduxYzZ86EruvBseRyOeTz+WDWhJhTIc4R8btlWcHv77zzDtasWYNLL70Ua9asQSwWw+zZswEA7e3tcBwHhmGgWCxi4sSJwf10dHSIV2FYr1VEU1D0fBx462twe+WfaWdkC7sb0w7eieXxTiyPrz34Ln579A7Y56ZXsTlTRESXsXTlBlx1yQV9fi4ejwMArrrqKtx777348Ic/jI9//OM45ZRTcMEFFwxrtkZ/waKq9r9MkM1mg9Y9+fzgradM08Qbb7yB73//+wMeN1VW6VBpTdP6tFkSQ6VbLbBXVRW2bUNRFGQyGWQymXofEhERDYBBBBERETW8PfbYA9dccw0OP/xwpNNpHHLIITj77LPx6U9/uir3X+ufr6esNXXr0MV6kxVkOmYja22DtkJXED5ss802WLRoEc477zy8/vrrFa16uPrqq/Hmm29i0aJFA97mrrvuwj333IM//OEPAIDFixfjkksuwT//+c+KHEO9DBUEnHDCCfjIRz6Cc889d8DbRCIRRCIRSJKESZMmlQ28LTXaVkUMDoav98L8SH4X/60oCgzDgCRJyOfzwayAoe5DhIRtbW3BYOv7778fp512Gl588UU4joPPfOYz+POf/4x//OMfZcd96qmn4sgjj8RvfvMbPPfcc/jIRz6CAw88sKxtj+/7wXGZphmcA6W/i1BMHNNjjz2GM888E9dffz2KxWJQ0VEoFJDL5bBixQrss88+QdsW3/ex3XbbIZXNY20ij+68j4Lr4QNTI1iTiAEA2gwFczoMPN0ziPqVDRmosoSJYRXPrK7c5/5/12Xw0voMvr7PNvh/j63Gy+sz+OyO2yAWiw26wPrmm2/izTffxG9+8xtcd911OOigg7Bs2TKsXLkS8+bNK6s8mTdvXnCdxmJbn9+ECRPw5ptvAgB22GGHAR9HVESsXr0auVwO8+fP71P1AADLly/HggULhjxuGp3x0GZpKOLvIdM04TgOuru7W6a6g4ioVTGIICIiojG58MILceihh+L+++/H1VdfXfa9c889F0ceeST++te/4sorr6zYYy5evBjPPvtsxe5vOHovSJdaunQpFi5cGIQQlQ5KhmO0C+RbZu4DeG4wUHG0PjrTwv3Hz+33ezv/7GVsTA89r0HyXKTnHoiZqx6B67rI5/NIJBJAT394MdS2nhYuXIhUqva9nksDgF//+tfYZptt8IMf/ABPPPFE2feuv/56bLfddrj22muxePHiUVcciOoTTdP6hAee58EwDHieh1wuB9/3EQqFAADJZBJdXV0tFRyMZDG/959r9ftolS7mi/vyPA+KokBRlH4X/Uvf19LzQoQXsizjX//6F0488UQsWLAA//3vf7H99tvjBz/4AVatWlX2s48++ihOOOEEXH/99bj//vux55574txzz8VvfvMbPP/88+jq6sI222yDD37wgygWi9iyZUu/z8MwDKDn/EskErjnnnvw17/+FalUCp7nBQvhYrf0Pffcg8985jP48pe/jD/+8Y+YOXMmTjjhBNz2yDPwEEba8XDnS1tw6QHT0Z0rYlO6iEs+Ng2lp/Nb3XncvaQL1x8+C9/5+xq8vCGDiWEV+29nY+mmLB59KzHq9+UX/96I2xduj589swF3L+nCufOj+P73v49bbrkFmzZtwpQpU7D//vvjrrvugqqqOPzww/H0009jy5Yt2HbbbTFjxgw88sgjAIDf/va3+O53v4vly5fjP//5D/bZZx/st99+OP/884GecGbp0qU47rjjsG7dOnR0dODUU08d8NjEYrfjOLjrrrtw+umno1gsYsmSJWhra8Ps2bPx4IMP4rHHHsPnP//5AY978+bNo359xqPSNkuqqgZVK63aZmkouq7DsixIkoRkMtkQ/39ARERDYxBBREREY7Zh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+tSnsHnzZhx33HF4/vnncccdd+D000+Hrut46KGH8Mgjj2D77bcHAISSa4F+diWvTzk47Ndv4Lsfn457jt0BuiJjdbyAv70THzSEAIDnTt+1z9cOuf01/Hvt4OdFQFYgeX13QN544404/vjjgyHQF198cTBbY8uWLfja176Gr3zlK/jRj34ETdOwYcMGPPfcc8Fixi9+8QuYponLL78c2WwWv//97xGJRMoe48orr8Q3vvENXHPNNejq6sLNN9+MU045ZXjHPYAlS5bg6quvxhe/+EWccsopeP7553H33Xfjs5/9LNAT2EWjUVx00UXo6OhAPB7Hk08+iVtuuWXYj9F7Fktvl1xyCc455xxce+218DwPzz//PH76058G31+8eDGmTZsWnCdPPPEE7r//fnzoQx8KbvPggw8in8/j85//fBCErV69GnffffeoXheqHlmWYds2NE1DJpMZ8jO51nqHEaXt0qqld/ulVCrVJ7zTNA2WZUFRFGQyGUx+8S5073shPMkApBpWR/geZLeA6a/eg/rVjPSv1YOI3m2WFEWBJElss1RFoVAo+Ls4kUgM2mqTiIian7TnnnsyuiciIiIahxzdwrIFl9b7MPrY5R//CzWfrPdhVM3555+PmTNn4txzz633oYyaJElBmNbfXBOqD13XYds2fN9HMpkc0+ydalNVFW1tbUEwUWmyLMM0TYRCIUiShEKhgGw22+c1ET3pTdOE4zhIJpNBO5jYlN2xcs8vVvzYhjLzxdvQvuHlmj/uUMTw4FqER7UgWkmWBg/oCctEpUOxWGR7oCpQFAWWZUHXdeRyOaRSKVaVEBGNA6yIICIiIhqntEIKaj6JojG2OQiVpDspbGMbKJpKMBC62XefHnPMMfjPf/6DbDaLvfbaC4cccgiuueaaeh/WmPi+D9/3+8xCofqxLAumaSKfzyOZTDb8ol5/bZoqYTjtlwTDMGBZFgCUDa0X2je8DOe1+7BupyMrcmzDMfW1+xoyhECTV0T0brOkqipkWYbfM5OIbZZqR7SN8zwPsVisoQNTIiKqLAYRRERERONYdOMr6Jq+V336oPfmubA3vIJkMgld12GaJiKRCDzPC0IJx3GabiFs5513xrHHHotwOIx169bhZz/7WTDfopkxiGgMiqIgGo1CUZR+F9MbWaXCCEmSYBjGkO2XBFmWYVnWsHrST3r3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y1j7jjeq5cuXY9q0adh///3x2GOPAQD2339/bNy4EevWrSu7rSRJOO6443D44Yejs7MTq1evxu23344nnngiuM1ee+2Fs846C5MnT8arr76Khx9+uOw+DjnkEJx99tn49Kc/HXztIx/5CE466SRsv/32yGazePnll/Gd73wHAHDwwQfjqKOOwrbbbotcLocXXngB1113XcXCjD322APXXHMNZl/9EnabYuK6T80CeqoUAODKp9bhqqfWQVckXLz/NBy1SweihoLXNudw6T/W4J8rUwCA43brxOUHzsBXH3gX3/n4NMzpDOGDNyzFRFPFJR+bht2mhKHJEl7ZmMElf1uNlzdsHRD7wpm7AgDuOGoOAGBlPI/3/3wp/u/IXTHjwK/jOxdfFBzrWWedhR122CGoYLn66qvxzjvvwHVdHHzwwXj77bfx9a9/HbNmzcIZZ5yB3XffHdlsFv/+97+xaNEiJBKJQV+Lhx9+GJ/85Cfx97//HegZanvAAQfg3nvvxUknnVR22/333x9f+tKXMG3aNHR1deHee+/F3XffHXy/vb0d3/rWt/D+978f3d3duOWWW/os7kYiEZx55pn46Ec/Ck3T8Prrr+P666/HW2+9Nab3tBI8z0MulwtCEzFbQtf1oCqldOh1owwAbzap1Nbrp1IzIyoxtLrZq99UVYVlWVBVFdlsNmg/RkRERPXFIIKIiIiIhqXZF6cG89BDD+HQQw8NgohPfvKTeOihh7DnnnuW3e7444/HwQcfjKuvvhqrV6/G7rvvjosvvhjxeBwvvfQSJk2ahO9973u477778MADD2DHHXfEmWeeOehj77333rjsssvw61//GldccQU0TcNee+0VfF9VVdx8881YtWoV2tvb8dWvfhUXXnghLrrookHvdzSeW53GRY+twkX7TsNeNy4FAKQLW8OnKw/eFjtODOG0P72D9SkHn5rbjt8fswP2+9UyvN29tdWJqck4Z+8pOPehlejOFrE57WBWu47fLunCtx5dBUmS8NUPT8Zvj94BH/7lUqQKHg669XW8ce7uOPsvK/C3txNwS7IuVx16l/ghhxyC+++/H1/72teAnsX9//u//8ODDz6IRYsWwTAMfOUrX8F3v/tdnH/++YPe16OPPopjjz0WkydPxsaNG7H//vtj/fr1eOONN8puN3fuXHznO9/BbbfdhsWLF2PX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jSoSiahUKqmtrY3zsheZOnWqjjrqKGlty6GWlhY9+OCD+sMf/tCl4ddtt92mnXfeWZdeeqnmzJlT99zNN9+skSNH6sorr9Ts2bO77JgnnXSSJk+eHHydyWT05ptvasaMGXr++eeDyonf//73Gjx4cIfX//GPf9Rf/vIX7bTTTrrhhhuCx9vb27Vw4ULdfPPNev755/XHP/5RO++88Yv4f/vb33TVVVcF1RKO4ygej+usS76jYw7ao8P2f38zpRPuWNAln0GnVcpaNfwwDZ1/d5fuds6cOXryySe7dJ8AAKB7EUQAAABgk2jN1DdFIhHF43EVCgW1t7f39HK2ieu6isfjMk1T2WxW+Xy+p5eEDXjyySd11VVXyXVdjR07Vueff75KpZJuvfXWLj3OihUrdPTRR9cFEXvttZcGDBjQsHPjrbfe0oUXXihJSiaT+sIXvqArrrhCJ5xwgrLZrMrlsqrVqmbMmKEHHnig7rW1dkqRSESS9O1vf1tvv/22mpqadNxxx+nyyy/X+eefr29961vS2jZNe+yxh77xjW/o9NNPV3t7uyqVSvDeSqVSEMIZhqG8k9TDC1I6d+aiuuMWSz3we9u0lB40WuriIKL2GQIAgL6LIAIAAACbRGumvicWiykajSqXyymbzfb0craaaZqKx+MKhUIqFovKZDKci72Y7/tqbW2VJN13330aN26cDj30UN1666265ppr9MYbb+j6668Ptr/sssuUyWR01VVXBY9Fo1F9+9vf1qGHHqpMJqNbb71V9957b91xHn74YR1//PEaOHCgVq5cKUn6n//5H/3973/Xpz71qbptY7GYvvKVr+iwww6T4zh69dVXdcMNN2jBgvcqBSZOnKjjjjtO4XBYjz76qNra2jq8t3K5HLy31tZWzZgxQ0cffbSGDh2qV199Ndgul8sF262vNv/hnXfe0cKFCyVJ6XRa48aN0y677KKnn346mFNR+7n1fV/VajWoCAqFQnUzKQpmWFXLkVeuqiW78QqhVdMO0AV/W6zxuzfp8BEJLU15OnfmIq3KlXTt/wzX/jtH9VJLXl95YKEWttVf8D95zE668NCdNSBi68E3Uvra3xapvfjez+FJH9xRZx88WMObXS1JebrpPy3697/isr2MJGnPPffUBRdcoBEjRuitt97SH/7whw7rGzt2rM4++2wNGjRI8+fP71DRMn78eJ1zzjn67Gc/u2ZNJ5+scePG6Y477tCpp56qeDyup556Sj/60Y+CwCYSieiCCy7QYYcdplwup9tvv12HHXZY3Xl4zDHH6LjjjtOgQYOUyWT04osv6nvf+94GP8PaGn7wgx/oK1/5igYNGqQnn3xSP/zhD3XEEUdo8uTJisVieuihh3T99dcHv6vi8bjOPfdcffjDH5bjOHr++ef185//XG+//Xbdvk855RQ1NTXp6aef1osvvtjh+IcddpgmTZqkkSNH6t1339Xs2bO7vOIIAIBGYkYEAAAANsowDIKIPqbWmz6TyfTpECIcDmvAgAFyHEfpdFrpdJrzsI/xPE+O42zRa77whS9owYIFOvPMM3XbbbfpnHPO0YEHHli3TWtrq55++mmNHz9eWtuC7IgjjtDMmTM77O973/uempubNXXqVE2ZMkWvv/66fvzjHyuRSEhScAH5N7/5jc466yytWrVKxxxzzCbX6DiOjj76aLW3t2vJkiVb9P7W5bpuEJwUCgUVCgXlcjm1t7crk1lzEX/VqlVatWqV2tralE6nVSgUVC6XZZqmQqGQygN37/TxLjp0Z/1p3iodMeNlvb66oJv+d6R+ctQwXfvEO/rE716VYUhXfXJY3Wt2GxDS5/Zs1ol3LdAJd7yhfQdHdPWnhgfPH7f3AH3j8F30g7nL9OFfzdfl/1imb3xkV33ss8dJa3+Or7jiCi1atEhTpkzRb3/7W5111ll1xxg4cKAuvfRSPfHEEzrjjDM0c+ZMnXnmmZt9P7vuuqvGjRunb3zjG/rmN7+p/fbbTyeeeGLw/Fe/+lWNHj1a3/72t3XRRRdp33331fvf//7g+T322EPnnnuubr75Zk2aNElTp07VCy+8sMljhkIhTZgwQZdddpmmTp2qMWPG6LLLLtPYsWM1bdo0XXHFFfrMZz6jj370o8Frpk2bpj322EPf+ta3dM4558gwDF155ZWyLEtaW81z8cUX695779UZZ5yh5557Tl/+8pfrjrvvvvtq2rRpuueeezR58mT95Cc/0VFHHaWTTjpps58TAAC9BRURAAAA2KjagFVaM/UNyWRSrusqnU4Hd1/3NZZlKZFIyHEc5fN5ZbNZzr8+6IADDtBBBx2ke+65Z4teN2/ePN12222SpKVLl2r06NE67rjj9Mwzz9RtN2vWLH3lK1/RH/7wB330ox/VsmXL6qocJGn06NHac889NWHChGCg+S9+8QuNGzdOH/3oR/XAAw/o2GOP1cyZM4MQY8aMGTrwwAPlum7dvnbbbbdgm1AopFwup0svvVS5XK5uuzPPPFOnnXZa3WNTp06tu8P9uuuuU7VaVSgUkmmaevXVV/Xss89u9DOpVCobDeFammJStapP7d6kRRfsV/fctU+8o2ueWBF8feuLq/SXV9ZUe/zs3+9o9qQ99aN/vaM5b61p3fbL/6zUz/9nRN0+wraprz6wSMszaz6/aQ8t1e3Hj9J3HlmqlmxJUw/fRf/3yNt64LU1+12c8vSBHV197qiPae7dv9UnPvEJmaap6dOny/d9LVy4UAMHDtQFF1wQHOOYY47RsmXLdOONN0qSlixZot12260uVNiQ2gX9WgXEQw89pAMOOEC/+c1vFIlENH78eF1++eXBZzt9+nTdeeedwesHDx6sfD6vJ554Qvl8XitWrNAbb7yxyWM6jqNrr71Wy5YtkyT94x//0Cc/+UlNmDBBhUJBixYt0nPPPacxY8Zozpw5GjJkiA477DCdc845eumllyRJP/jBD/SnP/1J48aN0z/+8Q8de+yxevrpp3X77bdLa8/7ffbZRwcffHBw3JNPPlm33XZbUCmyfPlyzZgxQ1OmTNEtt9yyyTUDANBbEEQAAABgo2pBBHei926GYaipqUm2bSuVSgUXXfuaaDSqaDSqcrmstra2Pvs+tlcf/vCHNXPmzGB489///nf97ne/26J9zJ8/v8PXxx57bIftnnjiCV1wwQXab7/9dPTRR2vWrFkdttl9990ViUT0l7/8pe5x13W16667SpJGjBih+++/v+75l156Sfvvv3/dY0uWLAlmOESjUR155JH63ve+p69//et67bXXgu3+9Kc/6W9/+1vda9999926ry+99FItXrxYu+22m6ZMmaIrr7xS5XJ5s5/NhpSdiKSqHl/Urotm11dntBbqWzW91PLe/IxaG6eX13lsZdZXxDGVcE21e2t+5y9Ne0EIIUlPL8vIMg3tvkNYGS+n9w0I66f/M0LXHP1elYRtGsrk1ux3+PDhWrBgQd3P8vrf4+HDh+vll1+ue2z9bTZkxYoVdTNBVq1apebmZmlttYTjOHrllVeC57PZbF0Fy3/+8x+tWLFCt956q5566ik9/fTTeuyxxzYZ4ubz+SCE0NrqnBUrVqhQKNQ9VlvHiBEjVCqV6t5fOp3WkiVLNGLEiOD9P/744x3e/7pBxKhRozR69Oi6CohaVUytdR0AAL0dQQQAAAA2yjAMiSCiVzNNU01NTTJNU21tbcEQ277EcRzF43FZlqVcLtfhLnP0Df/97391zTXXqFQq6d133637vVGpVILfJzW2vfX/OVqpVPTggw9q8uTJ2muvvfR///d/HbYJh8NavXq1vva1r3V4rtb6qLNKpVLdBeg33nhD48aN03HHHacrrrgieDyVStVttyEtLS16++239fbbb8uyLF122WU69dRTtyp4qxpr2vvk/Ireatv0xehS5b3Kotrf/A08tv73aWNizpqg+uuzFumZZe/9zBrVkpJL/9Pp97C11v9dV61Wg/C8M/L5vM4880yNGTNGBx10kCZPnqyTTz5ZZ5111kbb2q0fGFWr1W1eR2dEIhH99re/1dy5czs8xxBvAEBfwYwIAAAAbJRpmqpWq7TG6aUsy1Jzc7MMw+iTIYRhGIrH42publa1WlVrayshRB9WKBS0bNkytbS0dAgvU6mUdtxxx+Br0zQ1cuTIDvvYa6+9Ony9aNGiDR5v1qxZGjNmjP75z39uMFh4/fXXtcMOO6hcLmvZsmV1f9LptCRp0aJFHY659957d+r9VioVhUKhTm27Mf/4xz9ULpc3O5diY4zq1lVSdNbQpKud4+/N+fjQrjGVK1W9sbqglbmSlrd7GtEc0lttxff+tBb19so1A7sXL16sUaNG1c0KWf/zXbx4sfbcc8+6x9b/nmypZcuWyff9uv3GYjENG1Y/A6NSqejZZ5/VL3/5S51++unaeeeddcABB2zTsde1aNEi2bZd936SyaSGDRsWDCxfvHjxBs/7db3++usaNmxYh/N42bJl/P8zAKDPIIgAAADARtWCCPQ+tm0HF/Db2tq2urVLT3FdVwMGDFAoFFJ7e3uffA/ovP/+978aO3asDjnkEA0bNkxf//rXFY/HO2w3evRoffGLX9TQoUP1uc99TkccccRG50wsXrxYxxxzjK666qoNPv/MM8/opZde0uWXX64PfehDGjx4sPbZZx+ddtpp2mOPPSRJ99xzj44++mgdddRRGjp0qCZPnrzBgMSyLA0YMEADBgzQkCFDdNJJJ2nkyJH65z//WbddNBoNtqv9iUajm/xs7rnnHk2cOHGrQg3Lz0sy5FqGBsXsuj87RKwt3t/6CqWKrv/0CO0zKKJDhsZ05SeG6d5XWoPWTlc+tlxf+/DOOvPAgRo1IKS9BoY18YMDderHPihJevjhh1WtVnXRRRdpxIgRGjt2rE444YS6Y9x3330aMmSIpkyZomHDhunjH/+4jjrqqG1adz6f1+zZszVlyhSNGTNGI0eO1MUXX6xKpRL8f9ohhxyiCRMmaNSoURo8eLA+9alPyTCMbRpAvr63335bjz/+uC666CKNHj1ao0aN0je/+U29++67wblz991366CDDtIJJ5ygIUOG6HOf+1xdWyZJuuWWW/SpT31KkyZN0siRIzV8+HAdeeSROvXUU7tsrQAANBqtmQAAALBRhmHQlqkXcl1XyWRSvu8rnU73qbDINE3F4/Ggr3kmk+Ec2w7MnDlTo0aN0rRp01Qul3XXXXfpueee67DdnXfeqQ984AOaNGmScrmcbrjhBj399NMb3W+tsmFjpk2bptNPP12XXHKJmpubtXr1ar3wwgtqbV1zx/6cOXO06667asqUKXJdV3PnztV9992ngw46qG4/u+22WxCI1OYE/OQnP9GDDz5Yt92pp57a4eLwfffdp2uuuWaja5w9e7ZOO+00ff7znw8GFndWJLNcMgx9YlSTXj73g3XPvb6qoEN+tflZC5vyVmtRD7zWpj8dP0rNYVsPLkjp4nVmUfzhhVXKlyo6Z+xgfe/IIcr5Fc1fmdef/7RmqHKhUNA3v/lNXXDBBbrpppu0aNEi3XTTTbr00kuDfbS0tOi73/2uzj77bE2YMEEvv/yyfv3rX2vq1KnbtPYbbrhBF1xwga644grlcjndfvvtGjRoUNDKKJPJ6PDDD9fJJ58s13X19ttv6/LLLw8qFbrKVVddpXPPPVc//OEPZdu2XnjhheDnQJJefvll/fjHP9bkyZN1yimn6JlnntHvf/97TZo0KdjH008/rW9+85uaNGmSJk6cqFKppCVLluivf/1rl64VAIBGMsaMGdN3/qsFAAAA3SqRSMg0TaVSqZ5eCtYKhUJKJBLyPG+zF2F7m0gkomg0qmq1qkwmQ29zYCvYti3XdeW6rirhpB7f7/yeXlIHe8/5rmxvy+ZwNFo4HNadd96pG2+8UTNnzuzp5QAAsN2hIgIAAAAbRWum3iUSiSgejyufz2/xsN2eZFmWEomEbNtWoVBQNpvlvAI6ybIsOY4j13XlOI5M01SlUpHv+/LaWmQX21UKJXp6mQG7mO4VIcTuu++u4cOH65VXXlEsFgsqDB5//PGeXhoAANslgggAAABslGEY9O3vJWKxmKLRqLLZbJ8a6ByLxRSJRFQul/vkQG2guxmGEYQOruvKsixVq1X5vq98Pi/P8+p+jpItL2r1kLGSue0zIbZZpaxky7yeXkXgC1/4goYNGybf9/Xaa6/pvPPO63OVZAAA9BcEEQAAANio2p236FmJRCIY6lwoFHp6OZ3iOE7Q2iuXy/Wp8ATobutWPNi2LcMwVCqVVCwW11Q+bKKN2Y6L/6XVww7t1vVulGlpx8X/7MSGjffGG29oypQpPb0MAACwFkEEAAAANorWTD2vqalJjuMonU73iZkKhmEoHo8rHA7L8zylUimqaoD1WJYVzHlwHEeGYahSqcjzPOXzefm+3+kQOJJZrmjrQuWahkum2fC1b1SlrGhqsSKZd3puDQAAoNciiAAAAMBG1S6OofsZhqGmpiZZlqVUKiXf93t6SZsVCoUUj8clqU9VbwCNZppmUPXgum4Q8vq+r2w2K8/ztimwG7hwjhbtf0qXrnmLmZYGLny0Z9cAAAB6LYIIAAAAbJC59s5agojuZ5qmmpqaZJqmUqlUr5+rYJqmEomEXNdVoVBQJpOhkgbbNcMw5DhOED7Ytq1qtapSqaRCoSDP87o0XEy2zFNixTy1D9yrZ2ZFVMpKrpzfq+ZDAACA3oUgAgAAABtUCyK4oNy9LMtSU1OTJKm1tbXXB0GRSESxWEyVSkWpVKpPtI8CGsG27aDioTbnoVwuy/M8ZbNZ+b7fsN+nhqSh8+/Sq+OmqmKEJKMbWzRVKzLLnobMv0tG9x0VAAD0MQQRAAAA2CDDWHNJqbdfCO9PHMdRMplUuVxWKpXq1SGQbdtKJBKyLEv5fF7ZbLanlwR0K8uy6oZMm6apSqUi3/eVyWTkeV63/v50vHYNfekOLR5zcrcdU5JkmBr60h1yvEz3HhcAAPQpBBEAAADYIFozdS/XdZVMJuX7vtLpdK8NIQzDUDQaVSQSUalUUltbW69vHQV0BcMwgtDBdV1ZlhXMecjn8/I8r8d/FppXvCD/lXu1fM/Pddsxd3nlXjWveKHbjgcAAPomgggAAABsUG2YKhovHA4rHo+rWCyqvb29p5ezUa7rKh6PyzRNZbNZ5fP5nl4S0FDrVjw4jiNJKpVKKhaL8n2/V7YiG7joMUlaE0ZUK41p07R2v7u+fK92WvxY1+8fAAD0OwQRAAAA2KBamxE0VjQaVSwWUz6fVybTO1ubGIaheDyucDgsz/PU1tbGuYF+ybKsYM6D4zgyDEOVSkWe5ymfz8v3/T5x7g9c9JicQkpL9zlBFcvt2gHWlbLMsqehL91BJQQAAOg0gggAAABsUO0CHBonHo8rEokom80ql8v19HI2KBwOKxaLSZLS6bSKxWJPLwnoMqZpBlUPrusGlWC+7yubzcrzPJXL5Z5e5lZpXvGCYq1vaenex6l98GipUt62QGLt65Mr52vI/LuYCQEAALYIQQQAAAA2iIqIxkomk3JdV+3t7SoUCj29nA4sy1I8HpfrusEwalp1oa8zDKOu3ZJt26pWqyqVSioUCvI8T77v9/Qyu4zjtWvkczcrPWi0Vo48UrkBI7c8kFi7fTS1WAMXPqpkyzwZjVw0AADolwgiAAAAsEGmafbZO4F7M8MwlEwm5TiO0ul0r+wxH41GFY1GVS6X1dbW1q8uzGL7Y9t2UPFg27YMw1C5XJbnecpms/J9v1+HbIakppZ5amqZp3x8F60afqjSg0arFEqu2aBSlqH33n9VRhBU2MW0ki3ztOPifymSWd5TbwEAAPQDxpgxY/rvv7gAAACw1QYMGBBcqEPXMAxDTU1NsixL6XS6113gt21biURClmUpl8v12nZRwKZYllVX9VCr7qoNl/Y8j2ovSSU3rlxyqAqJXVW2I6qaloxKWVYpr3D7MkXTS2XTfgkAAHQRKiIAAACwQbRm6lqmaaq5uVmS1NbW1quqTQzDUCwWUyQSke/7am1t7VXrAzbFMIwgdHBdV5ZlBXMe8vm8PM9TqVTq6WX2OraXUfLdV5R895WeXgoAANgOEEQAAABggwzD6NftSrqTbdtqampSpVJRKpXqVQGP67qKx+MyDKPXzqsA1rduxYPjOJKkUqmkYrEYVD4AAACg9yCIAAAAQAeGYcgwjF51wbyvchxHyWRS5XJZqVSq14Q7pmkqHo8rFAqpWCwqk8nw/UavZVlWMOfBcZzg95Pnecrn8/J9n/MXAACgFyOIAAAAQAemaUoSF/a2USgUUiKRkO/7SqVSPb2cQDgcViwWU7VaVSqV4u5x9Dqmada1WzJNM2i3lM1m5Xke7cMAAAD6EIIIAAAAdFALInrL3ft9UTgcVjweV7FYVHt7e08vR1p7V3kikZDjOMrn88pms3yP0SsYhlHXbsm2bVWrVZVKJRUKBXme1+uGuwMAAKDzCCIAAADQgWEYEhURWy0ajSoWiymXyymbzfb0ciQpGEZdLpfV2trK8F70ONu2g3ZLtm3LMAyVy2V5nqdsNivf9wnKAAAA+gmCCAAAAHRQa4PCRcAtF4/HFYlElMlklM/ne3o5chxH8XhclmUpl8spl8v19JKwnbIsq67qwTRNVSoV+b6vTCYjz/MIPwEAAPopgggAAAB0UAsisGWSyaRc11U6nVaxWOzRtRiGoXg8rnA4LM/zlE6n6amPbmUYRt2cB8uygjkP+XxenudRmQMAALCdIIgAAABAB4ZhcGfyFjAMQ01NTbJtW+l0useHP4dCIcXjcUlSe3u7CoVCj64H2491Kx4cx5EklUolFYtF+b7f4z8bAAAA6BkEEQAAAOig1jIFm2eappqammSaptra2nr0Dm/TNJVIJOS6rgqFgjKZDJUtaCjbtuvCh9qch1rVg+/7/C4BAAAAQQQAAAA6ojVT51iWpaamJklSW1tbj7Y+ikQiisViqlQqSqVS3HmOhjBNs67dUu13he/7ymaz8jyPFmAAAADogCACAAAAHdTuasbG2batpqam4MJ/T931bdu24vG4bNtWPp9XNpvtkXWgfzIMo67iwbZtVatVlUqloOLB9/2eXiYAAAB6OYIIAAAAdEBrpk1zXVfJZFKlUkmpVKrHqkdisZgikYhKpVKPt4VC/2HbtlzXleu6sm07CCY9z1M2m5Xv+1RMAQAAYIsQRAAAAKADWjNtXCgUUiKRkOd5SqfTPbIG13UVj8dlmqay2azy+XyPrAP9g2VZdVUPtSDS931lMhl5nkcwCQAAgG1CEAEAAIAODMPgwuMGRCIRxeNx5fN5ZTKZbj++YRiKx+MKh8PyPE9tbW18n7DFDMOom/NgWVYw5yGfz8vzPKprAAAA0KUIIgAAAFDHNE1J4gL3emKxmKLRqLLZrHK5XLcfPxwOKxaLSZLS6bSKxWK3rwF917oVD47jSJJKpZKKxaJ832e4OQAAABqKIAIAAAB1CCI6SiQSCofDymQy3d4GybIsxeNxua6rQqGgTCZD2yxslm3bdeFDbc5DrerB931+xgEAANBtCCIAAABQpxZEcLF7jWQyKdd1e6QKIRqNKhqNqlKpqK2tTb7vd+vx0XeYplnXbqk256U2YNrzPJXL5Z5eJgAAALZTBBEAAACoYxiGREWEDMNQU1OTLMtSKpXq1hDAtm0lEglZlqV8Pq9sNtttx0bfYBhGXcWDbduqVqsqlUpBxQPBFQAAAHoLgggAAADUMU1zuw8hTNNUU1OTTNNUKpXqtsG9hmEoFospHA6rVCqptbWVu9gRsG1bruvKdV3Zth20W6pVPfi+TyUTAAAAeiWCCAAAANSptXTZXlmWpaamJklSW1tbtwUBrusqHo/LMAxlMhkVCoVuOS56L8uy6qoeaiGh7/vKZDLyPG+7Dw0BAADQNxBEAAAAoI5hGNvtxU3bttXU1KRKpaJUKtUtn4NpmorH4wqFQioWi8pkMtvt57+9Mwyjbs6DZVmqVqvBgGnP87qtOgcAAADoSgQRAAAAqLO9tmZyXVfJZFK+7yudTndLVUg4HFYsFlO1WlUqlZLneQ0/JnqXdSseHMeRJJVKJRWLRfm+zzkBAACAfoEgAgAAAHVM09zu5hKEw2HF43F5nqd0Ot3w41mWpUQiIcdxgmHU23M7rO2Jbdt14UNtzkOt6sH3/e0yCAQAAED/RhABAACAOttba6ZoNKpYLKZ8Pq9MJtMtx4tGoyqXy2pra5Pv+w0/JnqOaZp17ZZqM1hqA6Y9z9vugj8AAABsfwgiAAAAUGd7as0Ui8UUjUaVzWaVy+UaeizHcRSPx2VZlnK5XMOPh55hGEZdxYNt26pWqyqVSkHFA+ETAAAAtjcEEQAAAKhTu2O7v0skEgqFQmpvb1ehUGjYcQzDUCwWUyQSke/7am1t5Q74fsa2bbmuK9d1Zdt20G6pVvXg+/528TMFAAAAbAxBBAAAAAKGYUhSv66IMAxDyWRSjuMonU43dBhwKBRSPB6XpIYHHug+lmXVVT3Uqoh831cmk5Hnef36ZwgAAADYUgQRAAAACJimKfXjIMIwDDU1NcmyLKVSqYa1yDFNU/F4XKFQSIVCQdlstt9+ptsDwzDq5jxYlqVqtRoMmPY8T6VSqaeXCQAAAPRaBBEAAAAI1IKI/thGxjRNNTc3S5La2toa1h4pEokoFoupUqkolUo1tOICjbNuxYPjOJKkUqmkYrEo3/f5vgIAAABbgCACAAAAgf7amsmyLDU3NwfhQCPen23bisfjsm1b+XxeuVyuXwY6/ZVt23XhQ23OQ63qwff9fvdzAQAAAHQXgggAAAAEaoOq+9MFdMdxlEwmVS6XlUqlGvLeasOoy+Wy2traaNPTB5imWdduqXbu1wZMe57HUHEAAACgixBEAAAAIFAbuttfuK6rZDIp3/eVTqe7PIRwHEeJREKmaSqbzSqfz3fp/tF1DMOoq3iwbVvValWlUimoeGjUzBAAAABge0cQAQAAgIBhGP2mGiIcDisej6tYLKq9vb1L920YhuLxuMLhsDzPU1tbW78KcPoL27bluq5c15Vt20G7pVrVg+/7/eZ8BwAAAHozgggAAAAE+ktFRDQaVSwWUy6XUzab7dJ9h0IhxeNxSVI6nVaxWOzS/WPrWZZVV/VQO59931cmk5Hnef3i/AYAAAD6GoIIAAAABPpDEBGPxxWJRJTJZLq0VZJlWYrH43JdV4VCQZlMhrvpe5hhGHVzHizLUrVaDQZMe57HvA4AAACgFyCIAAAAQMA0zT49oDeZTMp13S6vVIhGo4pGo6pUKmpra2OWQA9at+LBcRxJUqlUUrFYlO/78jyvp5cIAAAAYD0EEQAAAAgYhtEnKyIMw1AymZTjOEqn0112Mdq2bSUSCVmWpXw+3+VtnrB5tm3XhQ+1OQ+1qgff9/vkOQsAAABsTwgiAAAAEOiLrZlM01RTU5NM01RbW1uXtOIxDEOxWEzhcFilUkmtra19ulKkLzFNs67dkmmaqlarwYBpz/P4XgAAAAB9DEEEAAAApLUX3w3D6FNzDyzLUlNTkySpra2tSy5Qu66reDwu0zSVzWa7dM4EOjIMo67iwbZtVatVlUqloOKBVlgAAABA30YQAQAAAGntBWFJfaYiwrZtNTU1qVKpKJVKbfO6TdNUPB5XKBRSsVhUJpPpM59FX2PbtlzXleu6sm07aLdUq3rwfb9PBWIAAAAANo0gAgAAANLaC/HqI0GE4zhqampSqVRSKpXa5ovW4XBYsVhMkrp80DXWVK6sW/VQawHm+74ymYw8z+sT5x0AAACArUMQAQAAAGmdIKK334keCoWUSCTkeZ7S6fQ27cuyLCUSCTmOEwyj7u3vvy8wDKNuzoNlWapWq8GAac/zumSWBwAAAIC+gSACAAAAUh9pzRSJRBSPx1UoFNTe3r5N+4pGo4pGoyqXy2pra2MOwTaqhQ61dkuSVCqVVCwW5fu+PM/r6SUCAAAA6CEEEQAAAJDWVkT05hAiFospGo0ql8spm81u9X4cx1E8HpdlWcrlcsrlcl26zu2Fbdt17ZZqcx5831cul5Pv+736fAIAAADQfQgiAAAAIK0NInprW6JEIqFwOKxMJqN8Pr9V+zAMQ7FYTJFIRL7vq7W1VeVyucvX2l+ZplnXbql2vtQGTHuex+cJAAAAYIMIIgAAACCtvVDfG+9gTyaTcl13m4ZIh0IhxeNxSVJ7e7sKhUIXr7L/MQyjruLBtm1Vq1WVSiXl83n5vk87KwAAAACdQhABAAAAqRe2ZjIMQ01NTbJtW6lUaqsuepumqXg8rlAopGKxqEwm06veY29j23bdnIdau6Va1YPv+722agYAAABA70UQAQAAAGntRftSqdTTy5DWrqWpqUmmaaqtrW2r1hWJRBSLxVSpVJRKpRiWvAGWZdVVPdTCKN/3lclk5HkewQ0AAACAbUYQAQAAAGltBUJvuNvdsiw1NTVJktra2rZ47oBlWUokErJtW4VCQdlstle8r97AMIyg4sFxHFmWpWq1Kt/3lc/n5XlerwmjAAAAAPQfBBEAAACQeklrJtu21dTUFFQxbOl6asOoy+XyVldS9De1iodauyVJKpVKKhaL8n2fShEAAAAADUcQAQAAAKkXBBGu6yqZTMr3faXT6S2qYnAcR4lEQqZpKpvNKp/PN3StvZlt23XtlmpzHnzfVy6Xk+/7PR44AQAAANi+EEQAAABApmlKUo+1MAqFQkokEvI8T+l0utOvMwxD8Xhc4XBYnucplUptcSunvs40zSB0cF1XpmmqWq0GA6Y9z9vuPhMAAAAAvQtBBAAAAGQYhiT1yJ3ykUhE8Xhc+XxemUym068LhUKKx+OSpHQ6rWKx2MBV9h6GYdRVPNi2rWq1qlKppHw+L9/35ft+Ty8TAAAAAAIEEQAAAAgqIro7iIjFYopGo8pms8rlcp16jWmaSiQScl1XhUJBmUym3w+jdhwnCB9s2w7aLdWqHnzf7/efAQAAAIC+iyACAAAAPdKaKZFIKBQKqb29XYVCoVOviUQiisViqlQqamtr67d3/luWFVQ8OI4TzO/wfV+ZTEae5zHnAQAAAECfQRABAAAAGYaharXaLUGEYRhKJpNyHEfpdFqe5232NbZtK5FIyLIs5fN5ZbPZhq+zOxmGIdd1g/DBsixVq1X5vq98Pi/P81QqlXp6mQAAAACwVQgiAAAAENxx32iGYaipqUmWZSmVSm22osEwDEWjUUUiEZVKJbW1tfWbC/K1Vku1dkuSVCqVVCwW5ft+pwIaAAAAAOgLCCIAAAAg0zQbXg1hmqaamppkmqZSqdRmAwXXdRWPx2WaprLZrPL5fEPX12i2bdcNma7NefB9X7lcTp7nMecBAAAAQL9EEAEAAAAZhtHQigjLstTU1CRJam1t3eSxTNNULBZTOBxWsVhUJpPpk/MQTNMMQgfXdYOwpzZg2vM8lcvlnl4mAAAAADQcQQQAAAAa2prJcRwlk0mVy2WlUqlN3vUfDocVi8UkSel0WsVisSFragTDMOoqHmzbVrVaValUUj6fl+/7/Xa4NgAAAABsCkEEAAAAZJpmQ+7Od11XyWRSvu8rnU5vNISwLEuJREKO4wTDqPtCmyLHcYLwwbbtoN1SrerB9/0+8T4AAAAAoJEIIgAAANCQ1kzhcFjxeFzFYlHt7e0b3S4ajSoajapcLqutra1XVw1YlhVUPDiOE1SS+L6vTCYjz/P6ZBspAAAAAGgkgggAAAB0eWumaDSqWCymXC6nbDa7wW0cx1E8HpdlWcrlcsrlcl12/K5iGIZc1w3CB8uyVK1W5fu+8vm8PM/b7NBtAAAAANjeEUQAAABs5wzDkGEYXdZCKB6PKxKJKJvNbjBcMAxDsVhMkUhEvu+rtbW1Vw1trrVaqrVbkqRSqaRisSjP83p1xQYAAAAA9EYEEQAAANsZ340rnxyqQmJXlZ2IZNpqC7nym9rkti1RJL1UjpfZqn0nk0m5rqv29nYVCoUOz7uuq3g8LsMwNrpNd7Ntu27IdG3Og+/7yuVy8jyPOQ8AAAAAsA0IIgAAALYD+fguWjX8UKUH7atSKLHmwUpZhmoX2Nf+zbQkSXaxXcmWF7Xj4n8pklm+2f0bhqFkMinHcZROp+V5Xt3zpmkqHo8rFAqpWCwqk8n02CwF0zSD0MF1XZmmqWq1GgyY9jyvV1VoAAAAAEBfZ4wZM4bbuwAAAPqhqqT0oNFauduRyjWPlCrlIGjolLXbR1sXauDCOUq2zJOxgc0Mw1BTU5Msy1IqleowMyESiSgajaparQYDnbuTYRh1FQ+2batarapUKgWtlmi3BAAAAACNQxABAADQD/luQkv3Pk7tg0dLlYpkmlu/s7WBRGLFPA2df5ccrz14yjRNNTc3S5JSqVRdJYFlWUokErJtW4VCQdlstttaHDmOE4QPtm0H7ZY8zwvCB9otAQAAAED3IIgAAADoZ9oGf1BL9zlBFcvdsgqIzamUZZY9DX3pDjWveEG2baupqUmVSkWpVKqu1VJtGHW5XFZ7e3uHKomuZllWUPHgOI5M01SlUglCB8/zeqwVFAAAAABs7wgiAAAA+pGVIz6i5XseI1UrkrENVRAbs3a/Q1+7X+9Pv6hyuaxUKhVUFziOo0QiIdM0lcvllMvlun4Na9stua4bhA+WZalarQahg+/7DQ8/AAAAAACdw7BqAACAfiIIIaTGhBDr7HfpHp+V85ajxGsPrXnYMBSPxxUOh+V5Xoc2TV2h1mqp1m5JkkqlkorFYhA+AAAAAAB6H4IIAACAfqBt8AffCyG6yVu7HaXhqRUa3Paq4vG4JKm9vV2FQqFL9m/bdt2Q6dqcB9/3lcvl5Hkecx4AAAAAoA8giAAAAOjjfDehpfuc0Lh2TBtTrejt0V/Q8Pk3ycusViaT2aZgwDTNIHRwXVemaaparcrzPGWzWXme1+VVFgAAAACAxiOIAAAA6MOqkpbufdyawdTdGUJoTZumsuXqxV0+ruHPzpCxpS83jLqKB9u2Va1WVSqVlM/n5fs+7ZYAAAAAoB8giAAAAOjD0oNGq33w6J5bgGEqNXAfpQeNVlPLvM1u7jhOED7Yti3DMFQqleT7vrLZrHzfp90SAAAAAPQzBBEAAAB92MqRR0qVimR2czXEuiplrRx5xAaDCMuygooHx3FkmqYqlYo8z1OhUJDneapUKj2ybAAAAABA9+jB/2IFAAD9zfjx43X//ff36BqmTp2qyy67rEfXIEn77bef5syZo1gs1rBj5OO7KDdgZM+GEJJkWsoN2E35+M4yDEOhUEiJREI77LCDxo0bp5kzZyoejyuXy6m1tVWrVq0KhlpvSwhx22236dhjj93kNnPmzNFhhx0mSRo8eLDmzJmjUaNGbfUx+4ot/TnojvMVAAAAwPaLiggAAPqxpqYmnXLKKTrkkEM0YMAAZTIZLViwQLfccovmzVtz9/qcOXP07W9/W//85z+3aN+33Xab7rrrLt19993BY3PmzNGTTz651esdP368pk2bJkmqVCpatWqV/vOf/+imm25SW1vbVu93fVOnTtVRRx2l++67T9dcc03dc+eff74+97nP6W9/+5uuuuqqLjvmfvvtp2uvvTb4ulgsavny5br77rv1wAMPdFjb+p566ilNnTpVWvvZ77zzzpKknF/WwlZPv/xPi/7wwipd9+kRmrjvjhtdx+JUUfvf+FKHxyfuu4Ou+/TIDo8XShUN+dFzm3+DlbIy7/+Yhi99SJJUKpVULBaVyWQkSalUSvl8fvP7aaCVK1dqwoQJSqVSPbqOOXPmSJK++tWv6uWXXw4edxxHd955p5qamvS1r31Nzz//fA+uEgAAAAC6DkEEAAD92Pe//305jqMrr7xSy5cv14ABA3TAAQcomUw25Hie58nzvG3aRyaT0aRJk2SapkaNGqWpU6dqp5120iWXXNJl65SkFStW6GMf+5iuv/76YM2O4+jjH/+43nnnnS491rq+/OUvK5vNKhQK6dBDD9XXvvY1LVu2TM8++2ywzZNPPtkhBFl/aPOMGTP0k9V7yo0ldcyeA/TT/xmh5Rlf33h4iS599O1gu5fP/aDO+etC/f3NtCSpvIkChHShrLG/qg8pOj2uwbT0bvMHlJ5/tzzPC+Y8lEqlTu6g8SqVilpbW3t6GdLa8+/oo4+uCyLGjRunfD6vpqamHl0bAAAAAHQ1gggAAPqpWCym/fbbr+7O6hUrVuiVV14JtrntttskSZdffrkk6Z133tHEiRO166676qtf/ar22msvRSIRLVq0SL/61a+Ci+XXXHONdt55Z51zzjk655xzJElHHnmkxo8fr3POOUef/exng2N8+MMf1qRJk/S+971P+XxeL7zwgr7zne9scu21i8WrVq3SPffco1NOOUWu62rixIkaN26czjjjjGDbY489Vscdd5wmTpxYt49Jkybp85//vBzH0d///nf9/Oc/r7so/vrrr2vXXXfVRz7yET388MOSpI985CNqaWnR8uXL6/ZlGIYmTpyoz3zmM9phhx20dOlS3XLLLZo7d26wzdixY3X22Wdr0KBBmj9/vmbPnr3R95bNZiVJ99xzjyZMmKD3v//9dUGE7/ubvWDe7pW1vBSWUp5+9uQKnXvIYB0xMqG/v5lWe7E+bUgVymrJbj4QqKq6ye3+cuL79fLKvMoV6Yv77iCvXNUVc5fp7vmtuuqTw/S/H2hW26oZ+vlPf6qnnnqq7rX77ruvTj/9dA0bNkxvvPGGrr76ai1cuDB4fvTo0TrjjDP0gQ98QKlUSo8//rh+9atfqVAoSJKam5t18cUX68ADD9Tq1as1Y8aMDusbMmSILr74Yu21115atmyZrrvuurrnBw8erNtvv12nn366FixYEFSpXHjhhTrzzDM1YsQIvfHGG5o+fbqWLFkSvO6kk07ShAkTFAqFNGfOHKVSKR188MHBebjffvtpypQpGjlypMrlshYuXKjLL79cK1as2OhnOXv2bE2YMEHXXXddEIQdffTRevDBBzVp0qS6bXfbbTedc8452meffVQoFPTYY4/p+uuvDz4b0zR11lln6eijj1a5XNasWbNkGEbdPjpzDq//WZ133nnad999Zdu2VqxYoV/84hfbVPEEAAAAYPvFjAgAAPqpfD6vXC6nww47TI7jbHCbs846S5J05ZVXasKECcHXkUhETz75pC688EKdccYZeuqpp3TFFVdo0KBBkqTvfOc7amlp0YwZMzRhwgRNmDBhg/s/5JBDdNlll+nJJ5/UGWecoQsvvLAuCOmMYrEoy7JkWVanX3PAAQdoxIgR+vrXv67LL79chx9+uE4++eQO282aNauuDdLRRx+tWbNmddjuxBNP1Kc+9Sldc801OuWUU3TnnXfqW9/6lvbbbz9J0sCBA3XppZfqiSee0BlnnKGZM2fqzDPP3Ow6DzroIA0aNKjurvjO8kPNkiRD0mc/0KzmsCW/3Nnyha33xdE7anW+pE/+7lX9+pmV+tH44Zrxud301NsZHfnbV/TEi6/rm9/8pkKhUN3rpkyZohtvvFFnnXWW2tradMUVVwTf01133VXTp0/X3Llzddppp+nSSy/V6NGjdd555wWvnzp1qgYNGqSvf/3r+t73vqdjjjlGzc3NwfOGYejSSy9VqVTSV7/6VV1zzTWd+h5I0mmnnRasrVwu11XffOITn9BJJ52km266SVOmTFFLS4v+93//N3jeNE1dfvnlev7553X66afr7LPP1gMPPBBUhGzMa6+9phUrVugjH/mIJGnQoEHab7/99OCDD9ZtFw6HNX36dGUyGZ111ln6/ve/rwMOOEDnn39+sM0JJ5yg8ePHa/r06TrvvPOUSCQ0bty4uv1s7hxe3/nnny/HcXT++efrtNNO0y9/+cseb60FAAAAoO+iIgIAgH6qUqnoqquu0oUXXqj//d//1euvv67nn39ejzzyiN58801pbd9+rW2HtO4d+AsWLNCCBQuCr2+++WYdfvjhOvTQQ3Xvvfeqvb1dlUolGD68MV/60pf0yCOP6Le//W3dvjtryJAh+uxnP6tXXnlliy6C+r6v6dOnq1gsauHChbr55pt11llnacaMGXUXiB966CGdccYZGjx4sLT2rvxLL71UY8aMCbZxHEdf+tKXdNFFF2n+/PmSpOXLl2vffffVZz/7WT3//PM65phjtGzZMt14442SpCVLlmi33XbTiSee2GFtd955Z7BfwzB0880364UXXqjb5sMf/rBmzpxZ99gf//hH/fGPfwy+Pu+kz+usqqGQZcqxDK3Ol/T751d1+jPakKawrUUX1F+Y/veSjL5w53vfs3ktef34X2taV13zxDs675DB7x27UtZ1Lz+j48d/VO973/vqApZbbrlFzzzzjLQ2+Lrjjjt0+OGH69FHH9WJJ56ohx9+OJg38vbbb+vnP/+5rr32Wl1zzTUaPHiwDjnkEJ111ll69dVXJUnTp0/XLbfcEuz/wAMP1PDhw3XJJZdo1ao1n8Ovf/1rTZ8+fbPv+ze/+U1QNXTbbbfpyiuvlOM48n1fn//85zVz5kz97W9/C97Hhz70IUUiEWlt5VE8Hte///1vLVu2TJK0ePHiTn3es2bN0tFHH62HH35YRx11lP797393mIXy8Y9/XK7r6oc//KEKhYIWLlyon/3sZ/rBD36gm266Sa2trTr22GN166236rHHHpMk/eQnP9FBBx0U7KMz5/D6Bg0apLlz5+qtt94KtgcAAACArUUQAQBAPzZ37lw98cQT+uAHP6i9995bBx98sL74xS/q6quv3mjrIK29C3vy5Mk65JBDtOOOO8qyLLmuG1yw76zdd99df/3rX7foNfF4XDNnzpRhGHJdVy+++KJ+9KMfbdE+FixYoGKxGHw9f/58RaNRDRo0qK5dTiqV0r///W+NHz9ehmHo3//+t9LpdN2+hgwZokgk0mENtm3rjTfekCQNHz68Q1VD7YLv+s477zzlcjm5rqs999xT5513ntrb23XfffcF2/z3v//tMES7vb297uvfPPS0ZiyKalAirO8fOVQz/rtSb7UVtS3ai2UdeXN9xUqhVN/maX7Le4FQpSq15kvBY4aqWpFbs/2AAQPqXvfSS+/Nnmhvb9eSJUs0fPhwSdKoUaP0vve9T5/4xCfqXmNZlnbZZRcNGzZMpVJJr732WvDckiVL6j6TESNGqKWlJQghtInvwfrWDcdqrx8wYIBaWlo0bNgw/eUvf6nb/pVXXtH+++8fvJdZs2Zp+vTp+s9//qNnn31Wc+bM0erVqzd73FoQtssuu2j8+PH6+c9/3mGbESNGaMGCBUEbJkmaN2+eLMvSsGHD5Hmedtppp7rzr1Kp6NVXXw3aM3XmHF7fPffco69//es66KCD9Mwzz2ju3LlBgAkAAAAAW4ogAgCAfs73fT3zzDN65pln9Pvf/14XXXSRJk+evMkg4itf+YoOPPBA/eIXv9Dbb7+tYrGo73//+7LtLfunw7phQGdls1mdeeaZqlarWrVqVd3w62q12qH3/ZauaX2zZs0KWgD99Kc/7fB87c73b3zjG1q5cmXdc+sPkO6M5cuXBzMiFi5cqL322ksnnXRSXRBRKBSCu+s3pjVT1Futlt5MlXXKvW/q8dP20nPLc3p1VWGTr9uUSrW62TDDr9S3HKqu91jVXNNuaf3v06ZEIhE98MADQUXEumphQCOtOzukVjGzJeufPn267rnnHh188ME64ogjdOqpp+qiiy7abMutdDqtJ554QhdffLFc19VTTz0VnG9daWvO4ZkzZ+rpp5/WIYccog996EM68cQTdeONN+rPf/5zl68PAAAAQP/HjAgAALYzixYtqrvY6ft+h/kLo0eP1uzZs/X444/rrbfe0urVq7XzzjvXbeP7vkxz0/+UePPNN3XAAQds0fqq1aqWLVum5cuX14UQktTW1tbhTvvdd9+9wz5GjRol13WDr/fee2/lcjm1tLR02Papp56SbduybVtPP/10h+cXLlwoz/M0aNAgLVu2rO5P7aLu4sWLteeee9a9bq+99urU+61UKnVr7bz3KhWWtfv688ut+r8jdt2K/XQto1Le4ON777138Pd4PK6hQ4cGLYxef/11jRgxosPnu2zZMpVKJS1evFi2bWuPPfYI9jFs2DAlEong60WLFmnQoEHaYYcdNnjMrbVkyRJ94AMfqHts/a8l6Y033tCtt96qc889V2+99VaH6o6NmTVrlvbff389+OCDqlQqHZ5ftGiRRo0apXA4HDw2evRolctlLVmyRNlsVu+++27d+WaaZt1n1ZlzeENWrlyp+++/X9/97nd1xx136NOf/nSn3hMAAAAArI+KCAAA+qlkMqnvfve7mjVrlt58803lcjl94AMf0Be/+EX985//DLZ75513dMABB+jFF1+U7/vKZDJaunSpDj/8cP3rX/+SJJ1yyikd7hB/5513tN9++2nOnDnyPK9DSyNJ+t3vfqcf//jHWrZsmR555BFZlqWxY8fq9ttv36r39Nxzz+n888/XF7/4Rc2dO1cHH3ywDj74YOVyubrtHMfRJZdcot///vfaeeedNXnyZP35z3/e4ADhSqWiyZMnB39fXz6f15/+9CedffbZMk1TL774omKxmEaPHq1cLqfZs2frvvvu0/HHH68pU6Zo5syZ2mOPPeqGYK9rwIABcl03aM30yU9+UnPnzu2w/vUDl3K5XPcZm2VfVb33PbnpPyv1+Ol7aczOUT33Tv3n0VmGDA2Kdfzn4cpsSZ0Zg12VIau04VkekyZNUjqd1urVq3X66acrlUrp8ccfl9bOZbj++ut13nnn6a9//asKhYJGjhypAw88UD/72c+0ZMkSPfnkk7rgggt07bXXqlwu6+yzz65rV/TMM89o6dKlmjZtmn75y18qGo3qtNNO26rPYV1//vOfdeGFF+q1117TvHnzdOSRR+p973tfMDNh55131mc+8xn961//0qpVqzRs2DANHTq0w9DpjXnqqad0zDHHdDiHax5++GFNnjxZ06ZN029/+1s1Nzfr3HPP1UMPPRTMZ7n77rs1ceJELV26VIsXL9YJJ5ygeDwe7KMz5/D6zj77bD311FNasmSJEomE9t9//07PvgAAAACA9RFEAADQT+Xzeb388ss6/vjjteuuu8qyLK1cuVIPPPBA3dDjG2+8UV/96lf16U9/Wu+++64mTpyoG264QZdccomuu+46pVIp3X777YrFYnX7v/nmm3XhhRfqj3/8o1zX1ZFHHtlhDc8//7y+//3v68tf/rImTpyoXC7XYTDzlli8eLGuvfZafelLX9KkSZM0d+5c3XHHHfrMZz5Tt92zzz6rpUuX6qc//akcx9Ejjzyi3/3udxvd78YuAtfMmDFDqVRKJ554onbZZRdlMhm9/vrrwefY0tKi7373uzr77LM1YcIEvfzyy/r1r3+tqVOndtjX73//e2ltO6CWlhbdf//9HdY2duxY3XPPPR3e+8knnxx8bRfSkjkw+PrVVQXNeSutaYfvoi/e2fmB4OtKhi29fO4HOzy+189fUEu2tMHX1DEthds33FLqpptu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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "import networkx as nx\n", + "\n", + "def plot(graph):\n", + " labels = {x: f\"{graph.attribute(x, 'id')}\" for x in graph.scan()}\n", + "\n", + " options = {\n", + " \"node_size\": 700,\n", + " \"node_color\": \"#0277bd\",\n", + " \"edge_color\": \"#454545\",\n", + " \"font_color\": \"#efefef\",\n", + " \"font_size\": 10,\n", + " \"alpha\": 1.0,\n", + " }\n", + "\n", + " # Draw graph\n", + " fig, ax = plt.subplots(figsize=(20, 9))\n", + " pos = nx.spring_layout(graph.backend, seed=0, k=0.9, iterations=50)\n", + " nx.draw_networkx(graph.backend, pos=pos, labels=labels, **options)\n", + "\n", + " # Disable axes and draw margins\n", + " ax.axis(\"off\")\n", + " plt.margins(x=0.15)\n", + "\n", + " # Set background color\n", + " ax.set_facecolor(\"#303030\")\n", + " fig.set_facecolor(\"#303030\")\n", + "\n", + " plt.show()\n", + "\n", + "plot(embeddings.search(\"medical literature\", 10, graph=True))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Explore the data with Agents\n", + "\n", + "Our last task will use Agents to explore the dataset. We'll create a method to search the dataset with a vector query and return the matches plus post engagement (impressions, likes, clicks etc). Then we'll hand the keys over the the agent and see what it can do." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "from txtai import Agent\n", + "\n", + "def search(query):\n", + " \"\"\"\n", + " Searches a database of posts. Returns results ordered by most popular\n", + "\n", + " Make sure to call this tool only with a string input, never use JSON. \n", + "\n", + " Args:\n", + " query: concepts to search for using similarity search\n", + " \n", + " Returns:\n", + " list of search results with (text, impressions, likes, clicks and post urls) for each match\n", + " \"\"\"\n", + "\n", + " return embeddings.search(\n", + " \"SELECT text, impressions, likes, clicks, url FROM txtai WHERE similar(:query) ORDER BY impressions DESC\",\n", + " 10, parameters={\"query\": query}\n", + " )\n", + "\n", + "def md(output):\n", + " display(Markdown(output))\n", + "\n", + "agent = Agent(\n", + " tools=[search],\n", + " llm=llm,\n", + " max_iterations=15,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, let's ask the agent to explore ways to increase engagement with future social media posts." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32;20;1m======== New task ========\u001b[0m\n", + "\u001b[37;1m\n", + "Analyze ALL available posts and find the topics discussed in the most popular posts.\n", + "\n", + "With those findings, tell me some ideas to write about in my future social media\n", + "posts in a couple sentences.\n", + "\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: I will use the tool'search' to analyze the most popular posts and find the topics discussed in them.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'search' with arguments: most popular posts\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: I will now extract the topics discussed in the most popular posts.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'search' with arguments: txtai, RAG, Embeddings graph database, PubMedBERT, vector databases, semantic search apps, medical literature, Hacker News, GitHub, vector databases, open-source frameworks\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: I will now extract the topics discussed in the most popular posts.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'search' with arguments: txtai, RAG, embeddings database, Wikipedia, vector databases, semantic search, LLM orchestration, language model workflows, Graph RAG, ArXiv, medical literature, text to sql, text to speech, RAG Data, multiprocessing, performance enhancements, txtai-wikipedia, vector similarity, path finding queries, txtai-wikipedia index, autonomous agents, retrieval augmented generation, multi-model workflows, default configuration\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: I will now use the extracted topics to generate ideas for future social media posts.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'final_answer' with arguments: Future social media post ideas: 1) Explore the power of Graph RAG and its applications in knowledge graph construction. 2) Discuss the benefits of using txtai's embeddings database for semantic search, LLM orchestration, and language model workflows. 3) Share success stories of using RAG for retrieval augmented generation and its potential in various industries. 4) Introduce the concept of autonomous agents and their potential in multi-model workflows. 5) Highlight the importance of open access to underlying data in a programmatic and platform-independent way.\u001b[0m\n" + ] + }, + { + "data": { + "text/markdown": [ + "Future social media post ideas: 1) Explore the power of Graph RAG and its applications in knowledge graph construction. 2) Discuss the benefits of using txtai's embeddings database for semantic search, LLM orchestration, and language model workflows. 3) Share success stories of using RAG for retrieval augmented generation and its potential in various industries. 4) Introduce the concept of autonomous agents and their potential in multi-model workflows. 5) Highlight the importance of open access to underlying data in a programmatic and platform-independent way." + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "prompt = \"\"\"\n", + "Analyze ALL available posts and find the topics discussed in the most popular posts.\n", + "\n", + "With those findings, tell me some ideas to write about in my future social media\n", + "posts in a couple sentences.\n", + "\"\"\"\n", + "\n", + "md(agent(prompt, maxlength=16000))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "🚀 Interesting and certainly seems in line with our own previous analysis.\n", + "\n", + "Next, let's ask the agent to tell us what it knows about NeuML. This is an example of `Agentic RAG`. It is like standard RAG but it enables an LLM to have multiple passes at the knowledge base before generating the response." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32;20;1m======== New task ========\u001b[0m\n", + "\u001b[37;1mWhat would you say NeuML's core competencies are?\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: I will use the tool `search` to find the core competencies of NeuML. The tool can search a database of posts and return results ordered by most popular.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'search' with arguments: NeuML core competencies\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: The results from the search tool indicate that NeuML's core competencies include:\n", + "\n", + "- Generative knowledge graphs\n", + "- Micromodels\n", + "- Cloud offering\n", + "- Consulting services\n", + "- Community engagement and training\n", + "\n", + "However, it's not clear what NeuML's core competencies are from the search results. Let me try to find more information.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'search' with arguments: NeuML core competencies\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: The search results still don't clearly indicate NeuML's core competencies. However, it seems that NeuML is building a sustainable and profitable company from the start and provides consulting services in situations where it's an interesting business problem. It also provides open-source development, advisory and strategic support services (i.e. Fractional CTO), and has a growing number of models available on the Hugging Face hub.\n", + "\n", + "Thought: I will try to get a clearer answer by looking at the specific services and products that NeuML offers.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'search' with arguments: NeuML services\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: Unfortunately, the search results still don't provide a clear answer to what NeuML's core competencies are. However, it seems that NeuML provides a range of services, including consulting, advisory and strategic support services (i.e. Fractional CTO), open-source development, and more.\n", + "\n", + "Thought: Since I couldn't find a clear answer through the search tool, I will try to provide a final answer based on the information available.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'final_answer' with arguments: NeuML provides a range of services, including consulting, advisory and strategic support services (i.e. Fractional CTO), open-source development, and more.\u001b[0m\n" + ] + }, + { + "data": { + "text/markdown": [ + "NeuML provides a range of services, including consulting, advisory and strategic support services (i.e. Fractional CTO), open-source development, and more." + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "md(agent(\"What would you say NeuML's core competencies are?\", maxlength=16000))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Seems about right. Let's keep in mind this is using a database of company posts, so it only knows what the company tells it to know 😂.\n", + "\n", + "Next, lets see what the agent believes are the strengths of txtai." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32;20;1m======== New task ========\u001b[0m\n", + "\u001b[37;1mTell me about the strengths of txtai\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: I will use the tool `search` to find information about the strengths of txtai.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'search' with arguments: txtai strengths\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: The strengths of txtai include its ability to automatically create graphs using semantic similarity, as well as its robust integration with Postgres, which allows for the persistence of vectors, sparse vectors, documents, graph edges, and nodes. Additionally, txtai has a growing ease of use, with a well-documented project and reliable, easy-to-use features out of the box. It also supports a variety of integrations, including Agents, RAG, and Vector Search, and has a focus on security with full Postgres integration.\n", + "\n", + "Thought: I also noticed that txtai has a focus on depth over breadth, ensuring that integrations are performant and support a large number of the underlying libraries' features. It is also built on the shoulders of open-source giants, such as Transformers, SQLite, NetworkX, Postgres, Faiss, and NumPy, and has a strong commitment to open access to underlying data in a programmatic and platform-independent way.\n", + "\n", + "Thought: Now that I have gathered information about the strengths of txtai, I will return the final answer.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'final_answer' with arguments: {'answer': \"txtai's strengths include its ability to automatically create graphs using semantic similarity, robust integration with Postgres, ease of use, variety of integrations, focus on security, focus on depth over breadth, and commitment to open access to underlying data.\"}\u001b[0m\n" + ] + }, + { + "data": { + "text/markdown": [ + "txtai's strengths include its ability to automatically create graphs using semantic similarity, robust integration with Postgres, ease of use, variety of integrations, focus on security, focus on depth over breadth, and commitment to open access to underlying data." + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "md(agent(\"Tell me about the strengths of txtai\", maxlength=16000))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Remember, this is based on what posts received the most impressions. It's a feedback loop with what users on LinkedIn believe is a strength as most engaging posts are prioritized. This is a complex topic as engagement is also driven by what LinkedIn chooses to share in user's feeds, which no one really knows the answer to 😀." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook demonstrated how to use graphs and agents to explore the [NeuML LinkedIn Posts dataset](https://huggingface.co/datasets/neuml/neuml-linkedin-202501). It presented ideas on how to increase future social media engagement by analyzing what's worked in the past. It was very illuminating 💡." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "local", + "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", + "version": "3.9.21" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/72_Parsing_the_stars_with_txtai.ipynb b/examples/72_Parsing_the_stars_with_txtai.ipynb new file mode 100644 index 0000000..1080ded --- /dev/null +++ b/examples/72_Parsing_the_stars_with_txtai.ipynb @@ -0,0 +1,670 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Parsing the ⭐'s with txtai\n", + "\n", + "In honor of txtai's recent milestone of reaching 10K ⭐'s on GitHub, this notebook will build an astronomical knowledge graph of known stars, planets, galaxies and other astronomical entities. It will show how a compendium of information can be built from a public knowledge source such as Wikipedia.\n", + "\n", + "While agents and retrieval augmented generation (RAG) can do great things, they can do even greater things if provided with an assist from the humans in the form of compiled knowledge. It's not only better accuracy, it's also faster as we don't need to wait for an agent to figure out what we already know.\n", + "\n", + "Let's dive in." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[agent,graph,pipeline-text] datasets" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Build the Knowledge Graph\n", + "\n", + "The first step is building our knowledge graph or compendium of information. We'll use [txtai-wikipedia](https://hf.co/neuml/txtai-wikipedia) and another source derived from wikipedia with known [stars by constellation](https://huggingface.co/datasets/NeuML/constellations).\n", + "\n", + "We'll select articles about planets, constellations, galaxies and stars. We'll then extract entities using a [GLiNER pipeline](https://github.com/urchade/GLiNER). Lastly, we'll join those entries together with the constellation data.\n", + "\n", + "To get an idea of what this data will look like, below is the schema for the `constellation` dataset.\n", + "\n", + "The following is a description of each of the fields in this dataset. As we can see, there is a lot of identifiers but it also has data such as it's location, brightness and distance from earth.\n", + "\n", + "| Field | Description |\n", + "| ------------------- | ----------------------------------------------------------------- | \n", + "| Constellation | Name of the constellation the star is a part of |\n", + "| Name | Proper name |\n", + "| Bayer | Bayer designation |\n", + "| Flamsteed | Flamsteed designation |\n", + "| Variable Star | Variable star designation |\n", + "| Henry Draper Number | Henry Draper Catalogue designation number |\n", + "| Hipparcos Number | Hipparcos Catalogue designation number |\n", + "| Right Ascension | Right ascension for the Epoch/Equinox J2000.0 |\n", + "| Declination | Declination for the Epoch/Equinox J2000.0 |\n", + "| Visual Magnitude | Visual Magnitude (m or mv), also known as apparent magnitude |\n", + "| Absolute Magnitude | Absolute Magnitude (Mv) | \n", + "| Distance | Distance in light-years from Earth |\n", + "| Spectral class | Spectral class of the star in the stellar classification system |\n", + "| Notes | Common name(s) or alternate name(s); comments; notable properties |\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from txtai import Embeddings\n", + "\n", + "embeddings = Embeddings()\n", + "embeddings.load(provider=\"huggingface-hub\", container=\"neuml/txtai-wikipedia\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from txtai.pipeline import Entity\n", + "\n", + "entity = Entity(\"gliner-community/gliner_medium-v2.5\")" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "from datasets import load_dataset\n", + "\n", + "def parsefloat(value):\n", + " try:\n", + " return float(value.replace(\"−\", \"-\")) if value else value\n", + " except ValueError:\n", + " return value\n", + "\n", + "def parse(star):\n", + " text = star[\"Name\"] if star[\"Name\"] else f'HD-{star[\"Henry Draper Number\"]}'\n", + "\n", + " output = {\"id\": star[\"Name\"], \"type\": \"star\", \"text\": text}\n", + " for field, value in star.items():\n", + " field = field.lower().replace(\" \", \"_\")\n", + " output[field] = parsefloat(value)\n", + "\n", + " return output\n", + "\n", + "# Star by constellation dataset\n", + "stars = load_dataset(\"neuml/constellations\", split=\"train\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The sections above loaded `txtai-wikipedia`, the `GLiNER` pipeline, and the `constellations` dataset. Now it's time to build the knowledge graph!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from tqdm.auto import tqdm\n", + "\n", + "labels = {\n", + " \"name of constellation\": \"constellation\",\n", + " \"name of galaxy\": \"galaxy\",\n", + " \"name of star in astronomy\": \"star\",\n", + " \"name of planet\": \"planet\"\n", + "}\n", + "\n", + "def stream():\n", + " # Seed with stars dataset\n", + " rows = {star[\"Name\"]: parse(star) for star in stars}\n", + "\n", + " # Get wikipedia results\n", + " results = embeddings.search(\n", + " \"SELECT id, text, percentile FROM txtai WHERE similar('scientific article defining a constellation, galaxy, star or planet') AND score >= 0.8\",\n", + " embeddings.count()\n", + " )\n", + "\n", + " # Aggregate into entity rows\n", + " for row in tqdm(results):\n", + " entities = entity(row[\"text\"], labels=list(labels.keys()))\n", + " entities = [(entity, labels[tag]) for entity, tag, score in entities if score >= 0.95 and entity != row[\"id\"]]\n", + " if entities or row[\"id\"] in rows:\n", + " # Collect relationships \n", + " relationships = set()\n", + " for uid, tag in entities:\n", + " if uid not in rows:\n", + " rows[uid] = {\"id\": uid, \"type\": tag, \"text\": tag}\n", + "\n", + " relationships.add(uid)\n", + "\n", + " # Add relationships and yield main record\n", + " row[\"relationships\"] = list(relationships)\n", + "\n", + " # Merge existing record (if any) and save\n", + " rows[row[\"id\"]] = {**rows[row[\"id\"]], **row} if row[\"id\"] in rows else row\n", + "\n", + " yield from rows.values()\n", + "\n", + "astronomy = Embeddings(\n", + " path = \"intfloat/e5-base\",\n", + " instructions={\"query\": \"query: \", \"data\": \"passage: \"},\n", + " faiss = {\"quantize\": True, \"sample\": 0.05},\n", + " content = True,\n", + " graph = {\"approximate\": True, \"topics\": {}, \"copyattributes\": True}\n", + ")\n", + "astronomy.index(stream())\n", + "astronomy.save(\"txtai-astronomy\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Explore the Knowledge Graph\n", + "\n", + "The next section creates a function to plot a knowledge graph. This function is specific to our astronomical data. It color codes galaxies, planets and stars by [spectral class](https://en.wikipedia.org/wiki/Stellar_classification).\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import networkx as nx\n", + "\n", + "def plot(graph):\n", + " labels = {x: f\"{graph.attribute(x, 'id')}\" for x in graph.scan()}\n", + " mapping = {\n", + " \"constellation\": \"#f44336\", \"galaxy\": \"#9575cd\", \"planet\": \"#9e9e9e\", \"star\": \"#ff9800\",\n", + " \"O\": \"#92b5ff\", \"B\": \"#a2c0ff\", \"A\": \"#d5e0ff\", \"F\": \"#f9f5ff\", \"G\": \"#ffede3\",\n", + " \"K\": \"#ffdab5\", \"M\": \"#ffb56c\"\n", + " }\n", + "\n", + " colors = []\n", + " for x in graph.scan():\n", + " etype, spectral = graph.attribute(x, \"type\"), graph.attribute(x, \"spectral_class\")\n", + " if etype == \"star\":\n", + " etype = spectral[0] if spectral else etype\n", + " \n", + " colors.append(mapping.get(etype, \"#03a9f4\"))\n", + "\n", + " options = {\n", + " \"node_size\": 700,\n", + " \"node_color\": colors,\n", + " \"edge_color\": \"#454545\",\n", + " \"alpha\": 1.0,\n", + " }\n", + "\n", + " # Draw graph\n", + " fig, ax = plt.subplots(figsize=(20, 9))\n", + " pos = nx.spring_layout(graph.backend, seed=0, k=0.9, iterations=50)\n", + " nx.draw_networkx(graph.backend, pos=pos, with_labels=False, **options)\n", + "\n", + " # Calculate label positions\n", + " pos = {node: (x, y - 0.1) for node, (x, y) in pos.items()}\n", + "\n", + " nx.draw_networkx_labels(\n", + " graph.backend, pos, labels=labels, font_color=\"#efefef\", font_size=10\n", + " )\n", + "\n", + " # Disable axes and draw margins\n", + " ax.axis(\"off\")\n", + " plt.margins(x=0.15)\n", + "\n", + " # Set background color\n", + " ax.set_facecolor(\"#303030\")\n", + " fig.set_facecolor(\"#303030\")\n", + "\n", + " plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we're ready to run a query. `txtai` supports vector queries, SQL queries and Graph queries (using [openCypher](https://github.com/aplbrain/grand-cypher)). As of txtai 8.3, queries are automatically routed to the correct index based on type. \n", + "\n", + "The path traversal query below takes a starting node and plots the 10 relationships that are closest to earth." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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DtGvfvUrrVu3TpMnT24VRAwdOlQPP/ywLrnkEn3/+9/X6NGjtWrVKt10001avHhx6XETJ07UCSecoNraWs2fP1/vvfdeqzWOP/547b333nrsscf0ox/9SEOHDtUBBxygrbbaSmeffbYmTJgg0zS1YMEC/f73v9eGDRtaPe/RRx/V8ccfr5qaGj3//PO69dZbddRRR+nII4+UYRh65JFH9Mc//rG0XjAY1Omnn65vfvObcrvd+vDDD3XnnXfq008/LT3mmGOO0RFHHCGfz6e//e1vikajrWreeeeddfLJJ2unnXaS0+nUp59+qjvuuEMff/xxl9YBAAAAUHm4/AkAAADoAZfLpYMOOkjPPPPMFh+7++67y+fzacGCBXrhhRe0//77y+fztXvcSSedpDlz5uiUU07RihUrNGvWrFLnwujRo3X++efr8ccf1ymnnKJ33nlHxx13XLtjjBgxQvvuu68uueQSnXLKKTIMQ1deeaXC4bDOOeccnX/++Ro2bJjOPffcVs8bPny4vvrVr2rmzJm64oorNGnSJF1zzTUaMmSIzjnnHN1zzz06+eSTNXr06NJzfvnLX6qurk4zZ87Uaaedpo8//lg33nijwuGwJGm//fbTtGnTdP/992v69OnasGGDDj/88FbrBgIBPffcc5oxY4bOPPNMrVy5Utdee638fn+n1wEAAABQmQgiAAAAgB7Ye++9FQqF9Oyzz27xsZMnT9bLL78s0zS1dOlSrVmzRt/61rfaPW7OnDn6z3/+o5UrV+p3v/udhg0bphEjRkiSpk6dqvnz5+vhhx/WypUr9eijj2r+/PntjuFyuXTNNdfok08+0WeffaYJEyZohx120JVXXqmPPvpIS5Ys0fXXX6+xY8e2ChUMw9Ds2bO1bNky/fvf/9Y777yjbbbZRrfffrtWrFihZ599VsuXL9f48eMlSePGjdMuu+yiyy67TB999JFWrVqlX//614rH46WvberUqZo3b57mzZunFStW6De/+Y2WLVvWqt63335bL774olasWKHly5frxhtvlNfr1W677dbpdQAAAABUJkYzAQAAAD0wefJkvf7666XxRpsSDAa1zz776Oyzzy7d9sILL2jy5Ml67rnnWj225aih4nHr6+u1YsUKjRw5Uq+++mqrxy9evFhf/epXW922du3aViOktt12W33xxRdat25d6bal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hzLZrNatb9SSjz/RqjVre21dAADsRhABAOixfKGgOU/M08k/PEpOp9PWWizT0mPPvGBrDQBQrUzTpCMCwIAWDAbl9/uVz+cVjUbLenFLPJHQm++8pzffea9sawAAUCm43AkA0CseefrZHm/A11OFQkHPvvIPRaJNttYBANWK0UwABiqXy6X6+nr5/X4lEomyhxAAAAw0BBEAgF6xbn2jbrn3d7atb5qm4smkfjfnMdXU1NjemQEA1YggAsBAFAgEVFdXJ8uyFIlElEql7C4JAIB+hyACANBrHn7sKb3z/mLl8/k+X9vhcOjyG27TipWrSle0BQKBPq8DAKqZaZrsEQFgwHA6naqrq1MgEFAymVQ0GrW9wxcAgP6KPSIAAL3Gsixdct0tevieX8kwjD7rSjBNU8++/He9/Oq/JUmNjY0KBAIKBALy+XyKx+PKZrN9UgsAVDM6IgAMFH6/X8FgUIVCQdFoVOedd55CoZBmzZrV6nG77babbrnlFh166KFKJBKlz7XxNWgymdSaNWu0YMECzZ07V42NjZtdd8aMGRo3bpy22247LV++XKecckq7x+ywww76yU9+ol122UXRaFSPPfaYHn744dL9++yzj374wx9qxIgRcjqdWrVqlebMmaMXXmi9T9oJJ5yg73znOwqFQnr//fd18803a9WqVa2+ro5Mnz5dH374YRe+mwAAbBlBBACgV61YvUZnXfRL/fr6KyUZcjrLe2VtoVDQmwvf0y9vuLXV7clkUul0WqFQSLW1tcpms4rH41zlBgCbQRABoL9zOp0Kh8NyuVxKpVJKJBLdOs5xxx2nRCKhYDConXbaSUcffbQmTZqkc889V59//vlmn/vMM89o9OjR2mGHHdrdFwgEdP3112vBggW6+eabtf322+uCCy5QPB7XU089JUlqbm7WH/7wBy1fvlz5fF577bWXZs6cqWg0qvnz50uSjj76aE2ZMkXXXnut1qxZoxNPPFGzZ8/WtGnTlMvltGjRIk2ZMqXV2ieeeKImTJhACAEAKAv6rgEAve7t9xbrzAsvVS6XK+uJf9M09cbb7+qcWVcql2s/Dso0TTU3N6upqUlOp1P19fUKBoOcZAOATbAsi9FMAPotn8+n+vp6ORwORaPRbocQkhSJRBSJRLRy5Uq98sormjFjhpqamnTuuedu9nm33XabHn/8ca1evbrD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Uim7SwIAAJ1AEAEA6JbjjjtOiURCwWBQO+20k44++mhNmjRJ5557rj7//PPNPveZZ57R6NGjtcMOO7S7L5/P6/nnn9fHH3+seDyuHXfcUeedd54cDkdphu5JJ52kAw88UDfeeKOWL1+uPffcU1dccYXOOussffLJJ5KkdevW6d5779XKlStlGIYmTpyoK6+8UqeeeqqWLl0qSfJ6vXrjjTf0xhtv6NRTT91szel0WplMRsFgsDSyiXFNAPojy7IIIgBUpOJeEPl8XpFIpHSRCgAAqHwEEQCAbolEIkokEopEIlq5cqVee+013XvvvTr33HN19tlnb/J5t912mySptra2wyBizZo1WrNmTenztWvXavz48dp1111Ltx100EH64x//qNdff12S9MQTT+grX/mKjjrqKF199dWSpH//+9+tjnv//ffrsMMO05gxY0pBxCOPPCJt7ProDMuySnPTQ6GQ6uvrlU6nFY/HGdcEoN8giABQaVp2QSQSCbogAACoQgx/BQD0imw2qyeeeEK77rqr6urqeu24w4cP15577qmFCxeWbnO73cpms60el8lkWoUVLTkcDu2///7y+XxatGhRj2sqjmuKxWLyeDxqaGiQ3+/v8XEBoBKYpskeEQAqRiAQKL22jEQihBAAAFQpOiIAACV77bWX5s2b1+q2rpyMWr58uSRp2LBhikajParltttu06hRo+TxePTkk0/qt7/9bem+N998U0ceeaQWLlyo1atXa8KECdpnn33a1br99tvrjjvukMfjUSqV0iWXXKJly5b1qK6WWo5rCgaDpXFNuVyu19YAgL5GRwSASuB0OhUOh+VyuZRMJpVMJu0uCQAA9ABBBACg5O2339bNN9/c6rYxY8bo4osv7tTziyeuemNM0eWXX65AIKAdd9xR06dP1w9+8AM9/PDD0saQ4mc/+5keeOABSdKqVav07LPPatKkSa2OsWLFCp188skKhULad999deGFF+qcc87p1TCi7bimuro6pdNpJRIJmabZa+sAQF8hiABgN7/fr2AwqEKhoGg0yp5cAAD0AwQRAICSdDqt1atXt7ptyJAhnX7+tttuK0n673//2+Na1q1bJ0latmyZHA6HzjvvPM2ZM0emaaqpqUmzZs2S2+1WbW2t1q9fr1NPPbXV3hLaOEKp+PV89NFH2mWXXTR16lTddNNNPa6v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tkz33nuv3nrrrdJjOrMO0N8QRAA2++sTj+rEaafYXYZcLpceeWzLLzT6q+6GF5va66Jc4QVt2wBQ+SzLUjweL41rqq+v75d7DPSnjohQKCS/369sNqtYLMaFAkAVavn6u6NRSZ0JGdruycDvAlST7bbbTmPHjtXatWvb3XfSSSfp17/+tVauXKmTTjpJs2bN0g9/+EOZpqlRo0bpkksu0QMPPKBXXnlFY8eO1TnnnKPm5uZ2oUBLw4cP19ixY3XJJZfIMAydeeaZGjp0aLv1jz/+eN1999369a9/rSlTpujiiy/W0UcfrVgspiFDhujyyy/X448/rqeeeko777yzTj/99HZreb1eHXPMMbrhhhvU3NysaDSqWbNmacSIEbr44ouVTCZ16qmn6tprr9W0adNKG7d7vV5NmTJFV1xxhQKBgC6//HJdccUVisfjuvDCC7X11lvrsssu0/vvv18KUc4++2xtt912uuKKK7Rhwwbtvffemj17tk488UStWrVKo0eP1vnnn6/77rtPr776qr761a9q2rRprer1+/16/fXXdd999ymXy+nggw/W1VdfrR//+Mf64osvOrUO0B8RRAA2W7d+nR59fK6mfO9I27oi8oW8/vb3V/T555/Zsn61Ks567YrOhBdOp5PwAgD6gXw+r2g0WhrX1NDQoGQyqVQqZXdpvaI/BBHFLgiHw1Ha6wNAZdpUuNByU+iilq+DTdMs7clAyID+Zq+99tK8efPkdDrl8XhUKBR06623tnvcnDlz9J///EeS9Lvf/U6/+93vNGLECK1YsUJHHnmk3nrrLT344IOSpJUrV2q77bbT0UcfvdkgYtKkSXr99dcVj8clSfPnz9chhxyiBx54oNXjnn32Wb388suSpPvuu09Tp07VLrvsovnz5+vwww/X6tWrddddd0mSVqxYoe23317HHntsq2O43W7dcsstpQ23R4wYoW9+85s666yztGjR//a5vOqqq/TnP/9Ze++9t/7+97+3et7q1aslSX//+9910EEHacqUKUqn01q2bJneeecdjR8/Xq+88oq22morTZo0ST/4wQ+0YcOG0vfuq1/9qiZNmlSqf/78+Xr44YdL36+xY8fqq1/9aqneTz/9tNXm4L/97W+1zz776Bvf+IYef/zxTq0D9EcEEUAFuOOuW7Xv3vtp8OAhfR5GmKapRCKh62+8pk/XHagILwBg4Gk5rikYDJbGNVX7ho6WZVX1aKZgMCi/318KjDgpCdhnS3sytP1d0zJU6GjjZ36eMVC8/fbbuvnmm+X3+3XEEUeoUCjoH/9ov+9jy5PixRPf9fX1WrFihUaOHKnXXnut1ePff/99TZ06VQ6Ho8OfJ4fDoYkTJ+r2228v3fbCCy/o9NNP1+9///tW7yU/++z/X/BY7BCtr6+XJI0cOVJLlixpdeyWI6OKstlsq69h2223VT6fb/Xc5uZmrVixQttuu23ptlQqVQohJCkSiWjt2rWtLjyIRCKqq6uTJO2www5yOp2lUKbI7Xarubm5VPOrr77aruaWQYTP59O0adP09a9/XYMGDSoFRUOHDu30OkB/RBABVIBUKqXLrpylO2+7t8/Xdjgcuva6KxWNRvp8bXROJYYXmwsyAADttR3XVFdXp3Q6rUQiUbUnzEzTlMtVfW8nXC6XwuGwnE6nEolEv+lQASrV5kYlbSlk6Gjj52r9nQmUQzqdLp1onz17tu677z5NnjxZ8+bNa/W4lntVFd+z9aSrcc8999SQIUN0ySWXtLrd6XRqwoQJWrBgQYdrF3V17Ww22606276PtiyrXT0tL6zw+/0qFAo67bTT2j23K68XTj/9dH3lK1/Rr3/9a61atUqZTEaXXXZZ6XVTb60DVJvqe+cA9FML3npTv/ndvX26X4RlWXr08b/o5b+92Gdrom/0RnjRUZDR2fCisxt1E14AGGiKV997vV6FQiF5PJ6qHddUjaOZAoGAAoGA8vm8IpFIl/9WAmhtSxs/dxQyFAOFXC7XKlwgZAB6xrIs/fGPf9QZZ5yhF198sdMn75cvX65x48a1um3cuHFauXLlJn8mJ0+erJdeekl/+MMfWt3+ox/9SJMnT24VRGxp7W984xutbhs9evQWn7ds2TK5XC6NHj26NJqppqZG22yzjZYuXdqptTvy8ccfy+l0qq6urt0G1C1rbltj28/HjRun5557rtQ54fP5NGzYsC6tA/RHBBFABbn73jsVCoV11BFHl30ty7L0wovP6Yabri37WqgOhBcA0HcymYyy2WxVj2uqptFMTqdT4XBYLpdLyWRSyWTS7pKAire5cGFLIUM+n1c2m23XzQCgvP72t79p+vTp+t73vqc5c+Z06jlz5szRXXfdpeOOO660WfX3vvc93XLLLR0+vra2VnvttZcuvvjidif9n3/+eV1xxRUKh8OKxWJbXPuJJ57QkUceqdNOO03z5s3TqFGjdMghh2zxeatWrdKrr76qn/3sZ7rxxhuVSqV0yimnaP369e3GTHXFypUr9cILL+iiiy7SXXfdpY8//lh1dXWaMGGCPvvsM/3nP//RI488ottuu01HHXWUXnvtNe25556txjIVj7PPPvvoX//6lyTphBNOaPU+uDPrAP0RQQRQYW68+TqlUikdf9wJMk2z19/gF4/5xJOP6drrr+KqI/RIpYcXbe8HgEpSHNeUSqUUDoerblyTaZpV0RHh9/sVDAZVKBQUjUY7HBEBDDQtN3juaGxS8TVYUcsNnoshQ9s9GXitBdjPNE099thjOvroo/XEE0906jkff/yxLr/8cp1wwgk67rjjtGHDBv32t7/d5EbVBx98sNLptN56661297311lvKZDI66KCD9Oijj25x7S+++EKXXnqpzjzzTE2ZMkVLlizRfffdp5kzZ27xudddd51mzJiha665Ri6XS++++64uvPDCHoee1113nY477jidfvrpGjx4sJqamrR48WL9+9//liQtWbJEN954o6ZNm6YTTjhBCxYs0IMPPqgf//jHpWPceeeduuCCC3T77berqalJDz/8sILBYJfWAfojY/z48bxaACrQft/6tn4+c5ZCoXCvbWD9vzcNGV1/03Wa98yTvXJMoC9sLrzY1F4YbVmW1aWOC95QA+hrXq9XwWBQDoejKq7a93q9qqmp0bp16+wupUMtuyBSqZQSiYTdJQF9oqOQoe3HLUOG4oUlbYMFQgYAANCbCCKAClZbW6fzf3qhDjpwovL5fLc3hCw+91//flVXX3u51q2vzBMGQG8ivABQjQzDUCAQKG1iWMnjmjwej2pra7Vhw4aK6+Bo2QURi8XogkC/YRjGZkclbS5k2FTQwGsXAADQFwgigCowepcxmvr9IzXx4MlyuVwyTXOLoUShkJcMQ5Zp6eW/vahHHp2rdxa2b50E8P9tKaggvADQV5xOZ2kz60wmo3g8XnEn+10ul+rr69XY2Fgxs98dDofC4bA8Ho9SqZTi8bjdJQGdtqmQoeXHbUOGzXUxFAoFXmMAAICKQRABVJGacI0OPugQjRv3f9p13P9p+NYjOjwRuua/a/T+onf1/qL39Pzzz6gx0mhLvcBAQHgBoJwqeVyT0+lUQ0ODIpFIRXQc+Hw+BYNBWZalWCxWsZ0kGJha7oO1qaCho5ChZbhAyAAAAKoZQQRQxQKBgIZvPUJer1eGYSiTyei/a9coFovZXRqAzehseNHy47a2FF50FGRwwgKoXsFgUH6/X6ZpKh6PK5vN2l2SHA6HBg0apKamJlvrcTgcCoVC8nq9pb0g+H2HvtTy73ZHo5I6EzK0/bjSOqAAAAB6qnsD5wFUhGQyqU8+/djuMgB0UXFec1dGmXRmrwuXy0V4AfRTiURC6XRaoVBItbW1ymQySiQSto5EKp4obXmCta95vV6FQiFJsj0QQf+1qXCh5abQRS3/ppqmqWw2S8gAAABAEAEAQHUgvABQKBTU1NQkj8ejUCik+vp628c1WZZlSxBhGIZCoZB8Pp/S6bTi8Ti/e9AtW9qToe3fxpahQjFkaBs0AAAAoD2CCAAA+qlKCC+2tNdF8WNOIAKdl81m1djYqEAgoEAgIJ/PZ9u4JjuCCI/Ho3A4LElqbm5WJpPp0/VRPTY3KmlLIUMul+twbBIAAAC6hyACqAIzZ85UKBTSrFmz7C6l026++WZ98sknuuOOO+wuBUAXdCe86MxG3YQXQO9LJpOtxjVls1nF4/GyjmvyS9rJZWjnjf9tbyXk8ZkyPS6lLWmlaenD/P/++yhvKdWLa7fsgshkMorFYvweGMC2tPFzRyFDMVDI5XIdbgINAACA8iGIwIBz7LHHap999tHIkSOVyWS0aNEi3XPPPVqxYkXpMT/96U81YcIEDR48WKlUSosWLdLdd9/d6jGb8u1vf1s///nP9eSTT+pXv/pVmb8aALBf8eRNb4YXDoeD8ALoBNM01dzc3GpcUyqVUjKZ7NX/38e4DE3xOXSAxyG3Yci0LJmSXIYpGZI2/ozubFk6yCM5DEN5y9LLWVOPpk29n+9ZLW63W+FwWIZh0AUxAGwuXNhSyJDP55XNZtt1MwAAAMBeBBEYcHbbbTc9/vjj+vDDD+V0OnXyySdr9uzZOuGEE5ROpyVJH330kV588UWtXbtWNTU1Ov7443X99dfr2GOP3eLVUpMnT9bDDz+s7373u7rzzjuVy+XK/jW5XC7l8/myrwMAvaXSwovNBRmEF6gGbcc1eb1eJRKJHp+w/4rL0IygU192OZS3LLk2jmFyGIba/4SpdH/x4297HDrY69SneVO3Jwuan+v6z1MoFJLf71c2m1UsFuPK9Sq3pf0Yir/Di1pu8FwMGdruycDvaQAAgMpHEIEBZ+bMma0+v/baa/X4449r1KhRevfddyVJTz31VOn+tWvX6je/+Y3uv/9+DRs2TKtXr97ksYcNG6axY8fq0ksv1e677659991XL730Uun+iRMn6qyzztLll1+uM888U1tttZXee+89XXfddWpsbJQ2vjmbPn26Jk2apEKhoGeeeabd7OWbb75Zn3/+uQqFgg466CB99tln+ulPf6rddttNp512mnbccUfFYjE999xzuv/++0tv2G+++WZ99tlnMk1TEydOVC6X029+8xu99NJLOvvss/Wtb31LkUhEt956q954443Setttt52mT5+u//u//1MqldKbb76pO+64Q83NzZIkn8+nc889V/vss4+SyaT+/Oc/t/veHHTQQZo6daq22WYbpdNpvf3227r99tsVjUY7vQ6AgY3wAtiyluOaampquj2uyS/pjKBT3/c5Vdj4/7OrG3tBFJ+zndPQzTVuPZEu6PZkQclO/Ii4XC7V1NTI4XAoFouVLhhB5WoZJmwqaGj5urY4DrAYMnS08TO/TwEAAPoHgggMeMFgUNq42WFHfD6fDjnkEK1evVpffPHFZo81adIk/ec//1EikdALL7ygyZMntwoiJMnr9eqoo47S1VdfLcuy9POf/1ynn366rrrqKknSUUcdpYkTJ2r27NlatmyZjjrqKO299956++23Wx1n4sSJeuKJJzRjxgxJ0uDBg3XNNdfoueee0zXXXKORI0fqZz/7mbLZrB544IFWz3v44Yd1+umna//999e5556rvffeW6+++qr++Mc/6sgjj9TPf/5z/eAHP1Amk1EwGNRNN92kefPm6Y477pDX69Wpp56qSy+9VOedd54klcKDX/ziF4pEIjr55JO100476ZNPPimt63K59Jvf/EYrVqxQXV2dzjjjDM2cOVMXXXRR6d9hS+sAQFf1NLzYVJBRDC/anlQrahtQEF6gLxXHNbnd7m6Na9rZaeiasEuDNuZyzl7YjLp4jO94HfqGx6Gfx/JavJlxTcFgUH6/X/l8Xk1NTYzWqQCGYWx2VNLmQoZCoVDqZGgZNPB7DwAAYOAgiMCAZhiGzjrrLL333ntaunRpq/sOP/xwnXbaafL7/Vq+fLnOP//8zY4/MgxDEydO1G233SZJevnll3X66adr2LBh+u9//1t6nNvt1s0331zqrHj88cf14x//uHT/1KlT9ac//Un//Oc/JUk33XST9txzz3brrVy5UnfffXfp85NOOknr1q0r7UuxYsUKDR48WKeeeqp+//vfl97offrpp/rDH/4gSfrTn/6kY489Vs3NzXr66aclSQ888IAOP/xw7bDDDlqyZIm+//3v65NPPtF9991XWmv27NmaO3euvvSlL2n9+vWaNGmSrr76ar311lvSxi6TOXPmtKr3mWeeKX28Zs0a3Xbbbbr77rvl8/mUTqe3uM7KlSs3+b0HgN5EeIH+IpfLKRKJyO/3KxgMyufzKR6Pb3Zc024uQzfUuOTppQCiLadhqF6Wbqtx6fzmvN5qE0a4XC6Fw2E5nU4lEgmlUr253TU2ZVMhQ8uP24YMxUChuPlz2z0Z+P0EAACAlggiMKD95Cc/0fbbb1/qKmjpxRdf1JtvvqlBgwbpqKOO0qWXXqqzzjprk3s+7LHHHvL5fPrPf/4jbeywWLBggSZNmqTf/va3pcelUqlW4502bNiguro6aePVf4MHD9aSJUtK95umqQ8//LDdSauPPvqo1ecjR47UokWLWt32/vvvKxAIaMiQIaVujs8++6zVsZuamlrdFolEJEn19fWSpB133FHjx4/XvHnz2n3Nw4cPl9frlcfjaVVzLBZrt7H3qFGjdPzxx2vHHXcsbTYpSUOHDtWyZcu2uA5BBIBKVsnhRdv7OTk48KRSqVKXY01NjXK5nOLxeLsLLEa7DN1U45KrTCFEkdMwZFiWrq9x6SfN+dJG1sX9LfL5vCKRCF0QvaT4O2VzQUNHIUPLzZ8JGQAAANBTBBEYsM4++2zttdde+slPfqL169e3uz+RSCiRSGjVqlVavHixnnjiCe2zzz56+eWXOzze5MmTVVtbq+eee650m2EY2mGHHfS73/2u9Iat7Ztqy7I6nEm+Jd2dk9xRV0dHtxXfkPr9fv373/9u1X1R1NjYqBEjRmxxTZ/Pp9mzZ2v+/Pm66qqrFI1GNXToUF1//fVyuVydWgcA+ptKCS86E2Sg+pmmWdpnIRQKqa6uTul0WolEQpZlqc6QbgyXP4QochiGXJalG8IuHRczlQuF5XK5lEwmlUwmy75+f9EbIUPbPRn4mQcAAEA5EERgQDr77LO1995769xzz201NmlTDMOQYRhyu90d3l9TU6NvfOMbuvzyy/X555+Xbnc6nbr11lu1xx57aP78+VtcJ5FIaP369Ro9enRp42yHw6FRo0bp448/3uxzly9frn333bfVbePGjVMikdC6deu2uPamfPzxx9p333313//+t8M3pqtWrVIul9Po0aNLXRehUEhf+tKXtHDhQmljt0Ztba3uueeeUi0777xzl9YBAPRueLG5jbzbIrzoP1qOawoEAvJ6vUok4vqZq6CA0TchRJHTMOSX9PNary43pWg0utkxmANNy5/LTQUNLS9mafnzZ5pmaU8GQgYAAABUAoIIDDjnnHOODjjgAP3iF79QMpksjSBKJBLKZrPaeuuttf/+++vNN99UNBrVkCFDdMwxxyiTyej111/v8JgHHXSQmpub9corr7S77/XXX9fkyZM7FURI0iOPPKJjjjlGK1eu1PLly3XUUUcpFApt8Xl//etfNXXqVJ199tl67LHHNHLkSE2bNk1z587tUfv8448/ru985zuaNWuWHn74YcViMY0YMUL777+/brjhBqXTac2bN0/Tp09Xc3NzabPqlmuuXbtW2WxWU6ZM0RNPPKHtt99exx13XJfW4Y0zAHRPd8OLzQUVhBfVL5VKlbojJoZ82s+yZy8GpyHtqbz2TKT1fH5g/T+wpT0Z2nbMtgwVOtr4mZ8hAAAAVDKCCAw4hx9+uCTplltuaXX7tddeq+eee07ZbFa77rqrpk6dqnA4rEgkonfffVczZsxQNBrt8JiTJk3Sq6++2uF9//jHP3TRRReppqamU/XNmTNHgwYN0oUXXijLsvTMM8/o1VdfVTAY3Ozz1q9fr4suukinnXaa7rvvPsViMc2bN08PPvhgp9bdlA0bNmjGjBk69dRTdf3118vtdmvt2rV64403Sm94f/3rX8vv9+uqq65SKpXSnDlzWtXb1NSk6667TieffLKmTJmijz76SL/+9a919dVXd2kdAEDf6M5JzXKHF5sKMtB9lmUpHYvpFJdbpmHI0XfNEK2YlqVzg079PWtq09toV5eWgUJXQ4aONn7m/3UAAABUO2P8+PHsNAYAAIA+15nwou39bXW244LwomMHeRy6NGz/tUmWZemaREHzMpX/77OlPRk6Chnadi4UPyZkAAAAwEBh/7sOAAAADEi90XnRmS6Mtggv/r+pPocKltWne0N0xNxYi91BxObChS2F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6tSpuvbaa/XOO+9Ikg466CBddtll6tWrlxYuXKgnn3xSN998s84//3zNnz9fhx56qO655x6NGTNGF1xwgXr16qVPP/1UN910k/r27auLLrpIu+++u959911NmDChuC121FFH6Uc/+pH22Wcf5XI5ffbZZ7rvvvu0fPny7X4dXq9Xxx9/vEaMGKHOnTvr5JNP1lNPPVX8/cI8V1xxhS688EL17t1b8+bN0/jx47V06dLi/c4++2ydeeaZ8nq9evPNN7f483rs2LEKBoP64osv9P3vf1+ZTEbnnHOO9tlnH40cOVIHHXSQksmkpk+frvvvv1/JZHKLx51++ulyu9167rnn9Ic//EEXXHCBBg8erFQqpccee0yvvvpq8XpdunTRRRddpCOPPFKWZWnmzJmaOHGiVq1aJW3sWxwxYoQGDRqkXC6nV155RYZhtJq5Ld/THV0HQGWgIwJA1frwoxl67oWnbflUXi6X06SX/6x/v/evDr82ANQjwzAUDAaVTCaVyWTsHgeoO4W+CKfTSV8EgIqRSCQUj8d17LHHyuVytekxfr9ft9xyixYsWKALL7xQjz32mC688MKt3vcnP/mJ7r33Xo0cOVJdu3bV9ddfrzPPPFM333yzfv7zn+vII4/UaaedVry/z+fTc889p+HDh+uKK66QZVkaN27cFm+ub+7444/XkiVLtHTpUr3++usaNGjQVu933nnn6cEHH9SIESOUy+U0ZsyY4u9985vf1LnnnqtHH31UI0aM0Lp163Tqqadu8RyHH364evXqpauuukrXXHONvF6vxo8fr2g0qhEjRujGG2/U4YcfrksvvbTV4w477DDttttuuuyyy/TAAw9o2LBhuu222xSNRnXRRRdp0qRJuvzyy7X77rtLGz+4N378eMXjcV1yySUaNWqUEomExo8fL6fzv5+LHjJkiE466SSNHz9el1xyiUKhkL72ta+1uu6OvqdtuQ6AykAQAaCq3f/gvVq1amWHrpNms1mtW7dW90z8VYddEwDqXSAQkDZ+KhuAPQp9ET6fTx6Px+5xAECWZemOO+7QSSedpJdfflkTJ07U+eefr3333Xebj/n2t78tSbrzzju1ePFivffee3rmmWe2et/HHntMs2bN0rx58zR58mQNHDhQd999t+bNm6eZM2fqrbfe0mGHHVa8/7Rp0zR9+nQtX75c8+fP1/jx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zj33XOVyOUmSx+PRkCFDdOuttyqfz+uaa67Rz372M91yyy3bfe5Bgwbp17/+tZYuXaply5bpuOOO0+uvv97qPieeeKKee+45XXTRRTrooIM0duxYzZo1SzNmzJBhGBo3bpyam5t10UUXKRAIbLPf4oILLtCDDz6ouXPnKp1O64wzztBZZ52lX/3qV5o3b54GDRqkW265RcOGDSuGLJJ07rnn6v7779eqVas0ZswYXXvttYrH47rvvvuUTCZ1/fXXa9iwYbrnnnskSSeccIKGDRume++9V3PnztX++++vK664QslkUq+99pq8Xq9uvfVWzZgxQ7feequ6deumkSNHtprVMAytWbNGN9xwg8LhsAYMGKDLL79c69at05tvvtmm6wBAuYVCIaXTaaVSKbtHAVAihb6IxsZGBQIBtp0AAADqHHuyAFrJZDIaP368Fi1apHfffVePP/64Tj/9dBmGod12201Op1PTp0/XqlWrtHDhQr300ktKJpM7fN4ePXro2GOP1Z133qmZM2dq/vz5uuWWW7T77rvra1/7WvF+LpdLd999t+bMmaO5c+fqL3/5iw4//PDtPvcRRxwhj8ej9957T5L0+uuva/DgwVvcb8GCBXriiSe0bNky/f3vf9fs2bOLz33EEUeoV69euu222zR//nx98skn+u1vf7vV673wwguaPn26Vq5cqfXr12vIkCF6+umnNXXqVC1dulQPP/yw5s2bpzPPPLPV45599lm9//77WrJkiV544QUdcMABevLJJzVr1izNmzdPkydP1mGHHVa8/7nnnqsHH3yweK3p06fr+eef1ymnnCJtDBBM02z1z+uZZ55pdc1cLqff/e53mjNnjlauXKkpU6bo1Vdf1Te/+c02XwcAysnv98s0TUWjUbtHAVBi9EUAAACggI0IAK3Mnz+/1SdSP/vsM/n9fnXt2lXz58/XjBkz9Oijj+r999/XBx98oLfeeqtNbx717t1b2WxWn3/+efG2cDispUuXqnfv3sXbEomEli9fXvz1unXr1NjYuN3nHjRokKZOnSrLsiRJb7zxhkaMGKHu3bu3eq4FCxa0etz69evV1NRUnG/16tVat25dq699a2bPnl38e7/fry5dumjWrFmt7jNr1iz16dOn1W3z588v/n1zc/MWMzU3Nxe/Vq/XWzwq6sorryzex+FwFL/fvXr10vz5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JEmrVq0q8VcIAABQOgQRAAAANsnn9d+QgeOVAKDqpdNpTZo0SSNHjlRjY6M2bNjQruf56le/quXLl+voo4/WHXfcIcMwNGPGDP3mN78phg7HHnusZs+eraFDh+o73/mOksmk/vnPf+qxxx5TOp0u8VcGAACw6wgiAAAAAACQdMwxx2jy5MmtbtuZ446WLFkiSerWrVu7g4g999xT3bp10ze/+U3ddtttMk1TF198sW644QZdccUVxfscfPDBSqfT+uUvf6lOnTrpsssuU0NDg8aPH9+u6wIAAJQTQQQAAAAAAJI++ugj3X333a1u69+/v37xi1+06fGGYUiS8vl8u2cwTVNut1u33XabvvzyS0nShAkT9PDDD6tnz55aunSpDMNQPp/XLbfcolgsJkl64IEHdMMNN+iee+5hKwIAAFQcgggAAAAAACQlk0ktX7681W1dunRp8+N79+4tbaXTYWesW7dO2Wy2GEJI0uLFiyVJXbt21dKlS7V+/XqtXbu2GEIU7mOaprp06aJly5a1+/oAAADlQBABAACwDWPHjtXJJ58sScpms1qzZo3eeustPfbYY8pkMh0+z3e/+12deOKJ2meffSRJc+bM0W9/+1t98cUXW73/6NGj9b3vfU/33XefXnjhheLte+21l0aMGKEBAwbI6XRqwYIFeuyxx/Sf//ynw74WAKg1brdbp5xyij7++GO1tLS0+3lmzZolp9Op7t27F0ORnj17SpsUUs+aNUvHHXecvF6vkslk8T65XE5r1qwpydcDAABQSm0/7BIAAKAO/fvf/9bpp5+uc845R/fff79OOeUUDRs2zJZZBg4cqDfeeEOjR4/WxRdfrNWrV2vChAnafffdt7jv1772NfXv33+rb0jdeuutcjgcuvzyyzV8+HDNnz9ft956q5qamjroKwGA6tfU1KSmpib16NFDxx9/vCZOnKhOnTptcbTT5rp3764+ffqoc+fOcrvd6tOnj/r06SOn87+fE5wxY4bmzJmjMWPGaL/99lPfvn11+eWX6/333y9uSUyZMkXhcFhjx45V7969dcghh2j48OF65ZVXOJYJAABUJDYiAAAAtiOTyai5uVmStGbNGn344Yc64ogjir/vcrk0YsQIHX/88QoEApo9e7buv/9+zZ49W5IUDAZ16aWX6sgjj5TP59OaNWv01FNP6dVXX5XT6dRFF12kb3zjGwqFQlq/fr1efvll/fGPf9zqLLfcckurX9955536xje+ocMPP1x///vfi7fvvvvuuuSSSzRmzBjddtttrR7T0NCgnj17asKECVqwYIEk6eGHH9b3v/997bPPPsWvFQCwfU8++aQsy1IikdCKFSv0wQcf6Nlnn93hn6NXXXWVBg4cWPz1b3/7W0nS0KFDtWrVKuXzeV1zzTW65JJL9Otf/1rJZFL//ve/9eCDDxYfk0wmdeWVV+qSSy7RQw89pHA4rDfffFOPPvpoGb9iAACA9iOIAAAAaKO9995bBx10UPFoDEkaPny4vv71r+v222/XqlWrNHToUI0fP17/93//p0gkop/+9Kfq3bu3xo4dq5aWFvXo0UMej0eSdPrpp+urX/2qbrzxRq1evVpdunRR165d2zyPx+OR0+lUOBwu3mYYhn7+85/rmWee0aJFi7Z4TDgc1pIlS3TiiSdq7ty5SqfT+t///V+tX79ec+bM2eXvEQBUqzvuuGOrt3/88cc6/vjjt/nrnTV69Ogd3mfdunW6/vrrt3ufpUuX6qqrrmr3HAAAAB2JIAIAAGA7jjnmGE2ePFkOh0Nut1u5XE733nuvJMnr9ep73/ue7rjjDr333nvSxi2FP/3pTxo8eLCeeeYZde3aVfPmzSu+yb9piLHHHnto2bJlmjlz5ha/1xbDhw/X2rVrNWPGjOJtZ599tnK5XKtOiM1dccUVuvnmm/W3v/1N+Xxezc3NGjt2rKLR6E5+dwAAAAAA2DGCCAAAgO346KOPdPfdd8vn8+nMM89ULpfTtGnTpI3nfLtcLs2aNat4/1wupy+++EK9e/eWJE2aNEk33nij9t9/f33wwQd6++239emnn0qSXn31VU2YMEFPPPGE3n//ff3rX//SBx980Ka5zj77bB1//PEaPXp0sTi7b9++OuOMM3ThhRdu97GXXXaZmpubdemllyqVSum73/2ubr31Vo0YMULr169v9/cKAAAAAICtoawaAABgO5LJpJYvX6758+dr/Pjx6tevnwYPHtzmx7/33nsaOnSonn/+ee2222666667NGLECEnS3Llzdc455+jxxx+X2+3W9ddfrxtuuGGHzzlkyBCdc845uuqqq4o9D5J08MEHq7GxUc8884ymTJmiKVOmqFu3bvrZz36mP/3pT5Kkww8/XEcffbRuuukmzZo1S3PnztU999yjVCqlk046qV3fIwAAAAAAtoeNCAAAgDbK5/N66qmndNFFF2nKlClavny50um0BgwYUDxWyeFw6MADD9Tzzz9ffFxLS4tee+01vfbaa5o5c6aGDx+uhx56SJIUj8c1depUTZ06VdOmTdP48eMVCoUUiUS2OsPQoUP1wx/+UGPGjNmi0+H1119vdUyTJI0fP16vv/66Xn31VWljr4QkWZbV6n6WZck0+YwKAAAAAKD0CCIAAAB2wptvvqkRI0bo+9//vp599llNmjRJw4cPVzgc1urVqzV06FB5PB5NnjxZkjRs2DDNmTNHCxculNvt1tFHH60lS5ZIks466yytW7dOc+fOVT6f13HHHad169Zts6th6NChGjZsmG655RatXLlSTU1NkqREIqFkMqlwONyquFobj4pav369li5dKkn69NNPFY1G9fOf/1xPPPFE8WimPffcU++++26Zv3sAAAAAgHpEEAEAALATLMvSn//8Zw0dOlSTJk3Sww8/LNM0dc0118jv92v27NkaM2ZMMUzIZDI6//zz1a1bN6VSKc2cOVPjxo2TNm5DDB06VHvttZdyuZxmz56tq6++Wvl8fqvXPvXUU+V2u3XjjTe2uv13v/udfv/737dp/nA4rDFjxuj888/XXXfdJafTqUWLFunaa6/V/Pnzd/n7AwAAAADA5oyBAwdu/SddAAAAAAAAAACAXcRBwAAAAAAAAAAAoGwIIgAAAAAAAAAAQNkQRAAAAAAAAAAAgLIhiAAAAAAAAAAAAGVDEAEAAAAAAAAAAMqGIAIAAAAAAAAAAJQNQQQAAAAAAAAAACgbgggAAAAAAAAAAFA2BBEAAAAAAAAAAKBsCCIAAAAAAAAAAEDZEEQAAAAAAAAAAICyIYgAAAAAAAAAAABlQxABAAAAAAAAAADKhiACAAAAAAAAAACUDUEEAAAAAAAAAAAoG4IIAAAAAAAAAABQNgQRAAAAAAAAAACgbAgiAAAAAAAAAABA2RBEAAAAAAAAAACAsiGIAAAAAAAAAAAAZUMQAQAAAAAAAAAAyoYgAgAAAAAAAAAAlA1BBAAAAAAAAAAAKBuCCAAAAAAAAAAAUDYEEQAAAAAAAAAAoGwIIgAAAAAAAAAAQNkQRAAAAAAAAAAAgLIhiAAAAAAAAAAAAGVDEAEAAAAAAAAAAMqGIAIAAAAAAAAAAJQNQQQAAAAAAAAAACgbgggAAAAAAAAAAFA2BBEAAAAAAAAAAKBsCCIAAAAAAAAAAEDZEEQAAAAAAAAAAICyIYgAAAAAAAAAAABl8/8ACrEF6XZ+hq0AAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "subgraph = astronomy.search(\"\"\"\n", + "MATCH P=({id: \"Andromeda\"})-[]->(A)\n", + "WHERE A.distance > 0\n", + "RETURN P \n", + "ORDER BY A.distance\n", + "LIMIT 10\n", + "\"\"\", graph=True)\n", + "\n", + "plot(subgraph)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also find stars with planets." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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vTceeuihOP3004dli4g48sgj4wMf+EC0t7fHPffcE1/+8peju7t7RL+fAAAAADQmOyIA1mDcuHHxpje9KX7xi1+scXKhs7Nz6M9nnHFG/OlPf4qzzjor/vjHP0Z/f/8G3bOjoyO22267uOCCC+ILX/hCHH300XHMMces9fnlcjm+8Y1vxPvf//74whe+ENOnT49zzjln6N+nTZsWF1xwQfz85z+Ps846K+6///447bTTVrvOdtttF4ccckhccskl8YlPfCL22WefeO9737tBXwMAAAAAvJoiAmANJk6cGEmSxJw5c173ubfddlv85je/iRdffDGef/756Ovr26B7rlq1Kr7+9a/H3Llz46677oq777479t1337U+/+c//3nMnDkzFi9eHA888EBcddVVcdhhhw39+7ve9a6455574ic/+UnMmzcvrr/++rjnnntWu04ul4svfOEL8eyzz8ZDDz0Ut9xyy2veFwAAAADWh6OZANYgl8ut83Mff/zxSJIkWltbo7Oz8zV3Q7yWZ599Nsrl8tDfly5dGjvvvPNan7/vvvvGaaedFjvssEMUi8VI0zRaWlqipaUlent7Y4cddog//elPw17z6KOPxkEHHTTsscWLFw87hmnp0qWx2WabbdDXAAAAAACvpogAWIN58+ZFuVyOSZMmve5zu7u7o1AoRKVSie7u7jUWEfn86//ndmBgYNjfK5VKJMmaB9cmTJgQn//85+P666+PK6+8MlauXBl77bVXXHjhhZHP56O3t/d177ch9wUAAACA9eWTJoA1WLlyZdx7773xzne+M1pbW1f792KxOPTnwWmIrq6uiIhYsWJFFAqFYa+bPHnyiObbfffdI5fLxRVXXBGPPPJIzJs3L7bYYothz5k7d27svvvuq70OAAAAAEaTIgJgLb72ta9FkiRxxRVXxFve8paYOHFiTJo0KU444YS4/PLLh57X0tIS5XJ56HijRx55JHp7e+PMM8+M7bbbLt72trfF0UcfPaLZ5s+fH01NTXHCCSfEtttuG0ceeWQcf/zxw55z3XXXxQEHHBAnnXRSTJw4MWbMmBEHHHDABh8dBQAAAAAbQhEBsBYLFy6Ms88+Ox544IE499xz4z//8z/jy1/+cuy7777x1a9+deh5TU1NQ9MQ8fI0xWc/+9k44IAD4qqrroojjjgifvCDH4xotqeeeiou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7AAAAABA41JEwBjT1toae+z6hpi6264xZbddYucddoiWluZIklz09PTGnPkL4uHHn4xHHn8qHnniqVjV2Zl1ZAAAAACggSkiAAAAAACAqrGsGgAAAAAAqBpFBAAAAAAAUDWKCAAAAAAAoGoUEQAAAAAAQNUoIgAAAAAAgKpRRAAAAAAAAFWjiAAAAAAAAKpGEQEAAAAAAFSNIgIAAAAAAKgaRQQAAAAAAFA1iggAAAAAAKBqFBEAAAAAAEDV5LMOAPVmxowZcc4558SMGTOiXC5HRERra2vccMMNMXv27LjggguGnrvPPvvEZZddFqeddlosWLAgpk6dGl//+tfj3nvvjUsuuWS1ax9yyCFx6qmnxqRJkyJJkliyZEncd999cfnll0dExNFHHx0XX3xxRESUy+VYunRp3HffffGd73wnVqxYsca848ePjw996EOx2267xcSJE+Paa68dut6gQw89NE477bSYOHFipGka8+fPj5/85Cdxyy23jOj3DgAAAACoP4oIGGEzZ86MQqEQu+++ezzyyCMREbH33nvHsmXLYsqUKdHU1BT9/f0RETF9+vRYtGhRLFiwICIijj322Ljuuuvi2GOPjS222CKWLl06dN199903PvnJT8ZVV10Vf/7zn6NSqcSOO+4Y++2337D7r1q1Kk4//fRIkiR22WWXuOiii2LLLbeMCy+8cI15m5qaYsWKFfHDH/4w3v3ud6/xOS+99FL88Ic/jDlz5sTAwEAcdNBBcdFFF8WKFSvi3nvvHbHvHQAAAABQfxzNBCNs7ty58cILL0RHR8fQYx0dHXHnnXfGwoULY+rUqcMenzlzZsTLUxOHH354/PKXv4y77rorjjnmmGHXPeigg2L27NlxzTXXxNy5c2PevHlx5513xte+9rXVMixfvjyWLl0a99xzT1x77bWx7777RnNz8xrzLl68OL75zW/Gb3/72+js7Fzjc2bNmhV/+tOfYs6cObFgwYL4+c9/Hk899VRMmzZtg79PAAAAAEBjUERAFcycOTOmT58+9PfBwmHWrFlDjzc3N8eUKVOGiojDDz885syZE3Pnzo1bbrkl3vGOdwy75rJly2KnnXaKnXbaab2y9Pb2RpqmkabpiHxt8fJ0xg477BAPPvjgiF0TAAAAAKhPjmaCKnjggQfivPPOiyRJoqWlJXbdddeYNWtW5PP5OP744yMiYs8994zm5uZ44IEHIl4+lunWW2+NiIh77rknisVi7LPPPjFr1qyIiLjuuuti7733ju9973uxaNGiePjhh+O+++6LW2+9deiop1ebOHFizJgxIx599NHo7u7eqK+pWCzGT3/602hqaopyuRyXXXZZ3H///Rt1TQAAAACg/ikioApmzpwZbW1tsccee8S4ceNi3rx58eKLL8asWbPioosuiqampujo6Ij58+fHkiVLYocddog99tgj/u3f/i3i5UXTt99+exx77LFDRURPT09ccsklsd1220VHR0dMnTo1zj333DjxxBPjwx/+cPT29kZERHt7e9x0002Ry+Wiubk5Hnroofjyl78cERE33XTTUMZbbrklvvrVr67z19TV1RVnnnlmtLW1xb777hsf+tCHYsGCBUP5AAAAAADWRBEBVbBgwYJYsmRJTJ8+Pdrb24c+rF+6dGksWbIkpk2bFh0dHcOmIfL5fPzsZz8bdp3+/v74+te/Pmx3w4IFC2LBggVx0003xQ9/+MP4r//6rzj88MPjN7/5TUREdHZ2xtlnnx2VSiWWLl0afX19Q68988wzh/7c1dW1Xl9TpVIZWqr91FNPxY477hinnXaaIgIAAAAAeE2KCKiSmTNnxj777BPjxo2La665ZujxBx98MN70pjfFHnvsEb/85S8jSZI46qij4lvf+lbce++9w67xmc98Jo444oi44YYb1niPRYsWRU9PT7S2tg499srC4NXW9viGSJIkmpqaRux6AAAAAEB9UkRAlTzwwAPx0Y9+NPL5/LCpgVmzZsVHPvKRof0QBx100NBxSq+cfIiIuOOOO+LYY4+NG264Ic4444xobW2Nu+66KxYvXhzt7e1xwgknRD6f3+hdDbvssktERLS1tcVmm20Wu+yySwwMDMRzzz0XERHvfe9747HHHosFCxZEU1NTHHDAAXHkkUeu19FOAAAAAEBjUkRAlTzwwAPR2toazz33XCxfvnzo8VmzZkWxWIw5c+bEsmXL4thjj42//vWvq5UQ8XIRceqpp8Yb3vCGmDVrVrzzne+MSy65JDbffPNYtWpVPPHEE/Hxj3885s6du1FZr7zyyqE/77777vH2t789Fi1aFKeeempERLS2tsb5558fW221VfT29sacOXPic5/7XNx+++0bdV8AAAAAoP7lOjo6KlmHAAAAAAAA6lOSdQAAAAAAAKB+KSIAAAAAAICqUUQAAAAAAABVo4gAAAAAAACqRhEBAAAAAABUjSICAAAAAACoGkUEAAAAAABQNYoIAAAAAACgahQRAAAAAABA1SgiAAAAAACAqlFEAAAAAAAAVaOIAAAAAAAAqiafdQAAACBbhUIhWltbI03TKJfL0dvbG6tWrco6FgAAUCcUEQAA0GC22mqreMMb3hATJkyIbbbZJlpbW1d7Tl9fXyxevDgWL14cc+bMiXnz5mWSFQAAGPtyHR0dlaxDAAAA1ZWmaey6666xzz77xIQJE6JcLkcul4tcLrfW11QqlSiXy5GmaaxYsSJmzpwZjz76aPT19Y1qdgAAYGxTRAAAQJ2bNGlSHHnkkVEsFqNcLkeSrP+quMHioq+vL/7whz/Eo48+WpWsAABA/VFEAABAnWpubo5DDz009txzzw0uIF6tUqlELpeLZ555Jm677bbo6uoakawAAIOam5tjk002iXw+H5VKJQYGBuKll16K/v7+rKMBG0gRAQAAdWjcuHFxwgknxLhx40akgHi1crkcfX19cd1118Xzzz8/4tcHABpHW1tb7L777rHNNtvENttsE5tssslqz6lUKvHiiy/GokWLYuHChfH4449Hb29vJnmB9aeIAACAOrPJJpvESSedFG1tbVUpIQaVy+UYGBiI6667LhYvXly1+wAA9Wm77baLvffeOyZPnjz02Ou9dxk8LrJcLsejjz4aDz30UCxZsmQU0gIbQxEBAAB1pFAoxMknnxzt7e1VLSEGlcvl6O/vj5/+9KexbNmyqt8PABj7isVivO1tb4uddtppo46PHHztY489Fn/4wx+ip6dnxLMCI0MRAQAAdeT444+PSZMmjUoJMahcLsfy5cvj6quvjnK5PGr3BQDGnj322CMOO+ywyOfzI/Z+pVwuR29vb9x2223x9NNPj8g1gZE1ej+dAAAAVbXHHnvETjvtNKolRLx8hML48eNj//33H9X7AgBjRy6XiyOOOCKOOuqoaGpqGtH3K0mSRGtraxx33HFx8MEHj9h1gZGjiAAAgDpQLBbjsMMOi0olm4HnXC4X+++/f2y55ZaZ3B8AqF25XC6OOuqo2HPPPYf+Xo17RETst99+8Za3vGXErw9sHEUEAADUgenTp0c+n6/KD/br44ADDsj0/gBA7XnrW98au+2226i9T+no6PCe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PPXUU3H22WfH1VdfHeedd1688Y1vXO/rAABj35VXXhnHHXdc1jGAKjARAQAANeCBBx6I8847L5IkiZaWlth1111j1qxZkc/n4/jjj4+IiD333DOam5vjySefjO7u7jjmmGPi1ltvjXh5AqFYLMY+++wzND0xeGTT9773vVi0aFE8/PDDcd9998Wtt94a/f39a8wxceLEmDFjRjz66KPR3d29TtlPOeWUaGtri9///vfr/XXPnj07rr766oiXpzWmTZsW7373u+P+++9f72sBAKPn9ttvf81///73vx8/+MEP1vu6jmaC+qSIAACAGjBz5sxoa2uLPfbYI8aNGxfz5s2LF198MWbNmhUXXXRRNDU1RUdHRyxcuDCef/752GKLLWKPPfaIf/u3f4t4+Yf222+/PY499tihIqKnpycuueSS2G677aKjoyOmTp0a5557bpx44onx4Q9/OHp7eyMior29PW666abI5XLR3NwcDz30UHz5y1+OiIibbrppKOMtt9wSX/3qV4flftvb3hann356XHrppWtdbv1aHn744dX+fuKJJ27AdxAAGE0nnHDC0J+POOKIeN/73henn3760GPr+gsNr5ZFEZGmqSOhoMoUEQAAUAMWLFgQS5YsienTp0d7e/tQmbB06dJYsmRJTJs2LaZPnx4PP/xwrFq1Kt797ndHPp+Pn/3sZ8Ou09/fH1//+tejs7Nz2LUXLFgQN910U/zwhz+M//qv/4rDDz88fvOb30RERGdnZ5x99tlRqVRi6dKlw/ZCnHnmmUN/7urqGnavww8/PP7lX/4l/v3f/z3++te/Vu17AwDUnsH9UhERq1atGvbYdtttFx/72MdiypQp0dbWFs8991x897vfHfZ+YbPNNouPf/zj8cY3vjGWLVsW3/ve9yIiolKpDD3n9ttvj//4j/+Igw46KKZPnx6LFy+OL33pS7FixYr4+Mc/Hrvvvns89dRT8fnPfz4WLFgw9Lrjjz8+Tj755Nh6661j4cKF8cMf/jBuueWWYdf96le/Gm9605ti3333jWuuuWaDpjeAdaeIAACAGjFz5szYZ599Yty4ccMWTD/44IPxpje9KfbYY4+45ZZbor+/P4466qj41re+Fffee++wa3zmM5+JI444Im644YY13mPRokXR09MTra2tQ49VKpVhP7y/0toeP+KII+LCCy+MT3/603HXXXdt4FccMWXKlNX+/txzz23w9QCA7LW1tcXdd98dV1555dD7ls997nNx+umnx5IlSyIi4qKLLoott9wyLrjggiiVSvGRj3wkNt1009UmIv7H//gf8a1vfSu+9a1vxdlnnx2XXnppLFy4MP77v/87Fi9eHBdeeGF85CMfiYsvvjgiIg455JA477zz4vLLL4/7778/DjrooLjooovi+eefj5kzZw5d94wzzojvfve7cfnll5uGgFFgWTUAANSIBx54IPbaa6+YPHny0ERERMSsWbPi+OOPj6amprj77rvjoIMOGjpO6dlnnx32f3fccUcce+yxES//gP3BD34w9tlnn9hmm21i8uTJceGFF0Y+n9+oHQxve9vb4pJLLokrrrgiHn744dh8881j8803j2KxOPSc1tbW2GWXXWKXXXaJiIhtt902dtlll9h6662HXWvatGnxnve8J7bffvt45zvfGYcddlhce+21G5wNAMjeU089FTfccEM8++yzMX/+/Pje974XCxYsiIMPPjgiIrbffvs48MAD48tf/nI88sgj8fjjj8dll10WLS0twyYiIiJ+/etfx+9///uYN29eXH311bHtttvGrbfeGvfee2/MmTMnfv7zn0dHR8fQ80855ZS4+eab4/rrr4958+bFT3/60/jjH/8Yp5xyyrDr3nbbbfGb3/wmFi5cOFSOANVjIgIAAGrEAw88EK2trfHcc88NO+7gwQcfjEKhEPPmzYslS5bERz/60fjrX/867PilQXfccUeceuqp8YY3vCFmzZoV73znO+OSSy6JzTffPFatWhVPPPFEfPzjH4+5c+ducM7jjjsu8vl8nH/++XH++ecPPf6b3/wmvvjFL0ZExO677x6XXXbZ0L99+MMfXu05ERE//elPY/fdd4/TTz89urq61jjlAQCMLa2trfG+970vDjzwwNhiiy0iTdNobm6OCRMmRETEjjvuGAMDA/H4448PvWbhwoVDRzy90tNPPz3058H3R69+rKWlJQqFQnR1dcWkSZPixhtvHHaN2bNnD9tpERHD7g1UnyICAABqxOLFi+Pwww9f7fHOzs5497vfHcuWLYuIiH/9139d6zUeffTRYdd45REEa3LzzTfHzTffvF45L7jggtd9zqxZs9b4tbzSqaeeul73BQDGhnPPPTfe+MY3xre//e2YP39+9Pb2xr//+79HPr/2jyLTNF3j4wMDA0N/HpyWWNNjuVxuvTJu6DJtYMM4mgkAAGpYPp+P1tbW6OzsXO2oAgCAWjRt2rS4+eab409/+lM888wzsWzZsthmm22G/n3OnDmRz+djt912G3ps++23j/b29o2+95w5c2LatGmr5bGDCrKliAAAgBo2bty46O/vj56enqyjAACsk3nz5sWhhx46tC/q0ksvHTaxMHfu3Lj77rvjn//5n2PKlCmx2267xXnnnTci73d+/OMfx9FHHx3HH398TJw4MU466aQ49NBD45prrtnoawMbThEBAAA1qrW1NdI0XeN5yQAAtepb3/pWrFy5Mr75zW/GZz/72bjvvvviiSeeGPacL37xi7F06dK47LLL4lOf+lTceuut8eKLL270ve+888745je/GSeffHJ873vfi+OOOy6++MUvxqxZszb62sCGy3V0dJjvBgCAGpPL5WL8+PHR29uriAAA6lqapjF+/PhYvnz5sP0PQP0wEQEAADWoWCxGvLyoGgCgng0uqi6VSllHAapEEQEAADXGgmoAoJGkaRqVSsX7HqhjiggAAKgx7e3tMTAwYEE1ANAQkiQxDQF1ThEBAAA1pLW1NZqamuyFAAAaRpqmigioc4oIAACoEblcLorFYvT09FjUCAA0jDRNo1wuZx0DqCJFBAAA1IjBBdWmIQCARuJoJqh/iggAAKgBgwuqu7q6LGoEABpGLpdTREADUEQAAEANaG9vj1KpFN3d3VlHAQAYNWmaRkQ4mgnqnCICAAAy1tLSYkE1ANCQkuTvH0+aiID6pogAAIAM5XK5aG9vj56enujv7886DgDAqBpcVO1oSqhviggAAMhQoVCIXC4XnZ2dWUcBABh1g0UEUN8UEQAAkJE0TaOtrS06Ozv9AA4ANCSLqqExKCIAACAjFlQDAI0uTVNFBDQARQQAAGSgpaUlmpubLagGABqao5mgMSgiAABglOVyuSgWi9Hb22tBNQDQsHK5XORyORMR0AAUEQAAMMoKhUIkSWIaAgBoaGmaRkQoIqABKCIAAGAUDS6o7urqcgwBANDQBosI74mg/ikiAABgFLW3t0e5XI6urq6sowAAZGpwP0SlUsk6ClBliggAABglzc3NFlQDALwsSRLHMkGDUEQAAMAoaW9vj97e3ujr68s6CgBA5gYnIoD6p4gAAIBRYEE1AMBw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RS6XyzrGMOVKJVpamrOOAQAAAIoIAICNVS5Xso6wmtzLUxEAAACQNUUEAMBG6untjXKltsqIXC6Jnt6+rGMAAACAIgIAYGM9N29+JDV2NFOS5OK5ufOyjgEAACNmn332idtvvz2KxeJGXef222+PN7/5zSOWC3h9+awDAACMdY88/lSkaZp1jNU88sRTWUcAAGhYm2++eZx22mlx4IEHxlZbbRWrVq2KBQsWxC233BI333xz9Pb2Zh1xRB133HFx7LHHxk477RSlUinmz58ft9xyS9x4440197WecMIJsWrVqqxjQENRRAAAbKRa/MB/0ZLnY+WqzqxjAAA0pG233Ta+8Y1vxKpVq+LKK6+Mp59+Ovr7++MNb3hDHHfccfHCCy/En//856xjjphPfOITceihh8YPf/jD+NrXvhYrVqyIyZMnx4knnhiLFi2KO++8M+uIwyxfvjzrCNBwFBEAABtpVWdnPP3cnNhph+0jSbI/+XJgYCDueeDBrGMAADSs888/P0qlUpxzzjnR09Mz9PjChQtX+1D+pJNOimOOOSa23XbbWLlyZfzlL3+Jb3/720OvO/roo+O8886Lz372s3HuuefG1ltvHXfffXd8/vOfj8MOOyze9773RbFYjFtuuSUuv/zyKJfLERFx9dVXx69+9avYYYcd4tBDD40XX3wxvvGNb8Tf/va3+PjHPx777rtvLFiwIL70pS/F448/HhERm2yySXzkIx+JvffeO8aNGxcLFiyIH/3oR/G73/1urV/rYYcdFkceeWRceumlw762xYsXx5133jnsGKVjjz02Tj755Nh2221j0aJFce2118b1118fERETJkyIH//4x/GpT30qTjjhhNhtt91i/vz58bWvfS1mzZo17J677757nH322bHjjjvGk08+GV/60pdi7ty5Q//+5je/OU4//fTYaaed4oUXXoibb745fvjDHw59b26//fbV8gLVlf1PygAAdeAn19+UdYQh+Xw+rr3xN1nHAABoSJtssknst99+8Ytf/GJYCbE25XI5vvGNb8T73//++MIXvhDTp0+Pc845Z9hzWlpa4oQTTohPf/rTcdFFF0VHR0d8+tOfjgMOOCAuvvji+NznPhfHHXdcvPWtbx32une/+90xe/bsOOuss+Luu++OSy65JC655JK45ZZb4uyzz44FCxbEJZdcMvT85ubmePzxx+OSSy6JD3zgA3HjjTfGJz7xidhjjz3Wmv/tb397zJkzZ60f6nd2dg497/3vf39cddVVccYZZ8SVV14Z73//++Poo48e9vxzzjknfvrTn8ZZZ50Vf/vb3+Kzn/1sbLLJJsOe8//8P/9PXHHFFXHOOedEqVSKCy+8cOjf9tprr7j44ovj2muvjfe9733xH//xH3HMMcfEP/3TP73u/y+A6lFEAACMgF/denv09w9kHSPK5XI89exzMevhR7OOAgDQkCZOnBhJkgz7Df2IiF/84hdx0003xU033RRnn3320OM///nPY+bMmbF48eJ44IEH4qqrrorDDjts2GubmprisssuiyeffDIefPDB+MMf/hDTpk2LL33pS/Hcc8/FXXfdFTNnzoyOjo5hr7v77rvjhhtuiPnz58cPfvCDaG9vj8ceeyz+8Ic/xLx58+Lqq6+OnXbaKTbffPOIiHjhhRfiJz/5STz11FOxcOHCuO666+Kee+5ZLc+rv95Xf61r8r73vS+uuOKK+OMf/xiLFi2KP/7xj/Gzn/0sjjvuuGHPu+666+KOO+6IOXPmxFe/+tXo7OyMY489dthzrrrqqpg1a1Y899xzcfXVV8e0adOiqakpIiLOOOOMuPrqq+Pmm2+OhQsXxv333x//+Z//GTNmzHjdjED1OJoJAGAErOrsiht+e1u88x1HRT6f3eLqXC4XV193Y2b3BwBgzc4999zI5XJx6aWXDn1oHhGx7777xmmnnRY77LBDFIvFSNM0WlpaoqWlZWjJc3d3dyxYsGDoNcuXL4/FixcPm7hYvnx5bLbZZsPu+fTTTw/797U9tvnmm8fy5csjSZI47bTT4rDDDostt9wympqaoqmp6TWXTedyudf92ltbW2PixInx8Y9/PP7lX/5l6PE0TVdbGv3www8P/blcLsdjjz0WkyZNGvacp576/3e0LV26dOhrWLJkSeyyyy4xbdq0YRMQSZKs9j0FRpciAgBghHz7B/8dRx/+ligW2jLZFTFQKsUzz82N6399y6jfGwCAv5s/f36Uy+XYYYcdhj2+cOHCiIhhH4RPmDAhPv/5z8f1118fV155ZaxcuTL22muvuPDCCyOfzw89t1QqDbtWpVKJgYGB1R579XvQVz/n1Y9VKpWIV5QJp5xySpx44onxzW9+M5555pno7u6O8847L/L5tX+EOG/evNW+1ldra2uLiIivfOUrw4qGeLlsWF+v9TW0tbXF97///bjjjjtWe11fX9963wsYGY5mAgAYIUuXr4jPfe2KzBZW5yLi0s9/JQZe9YMqAACj56WXXor7778/3vWud0Vra+trPnf33XePXC4XV1xxRTzyyCMxb9682GKLLUYt66tNmzYt7rzzzrj11luHjmfafvvtX/M1t912W0yaNCne/OY3r/Hfi8ViLF++PJ5//vnYdtttY8GCBcP+b9GiRcOeP3Xq1KE/J0kSu+22W8yZM2edv4Ynnngidthhh9Xus2DBgqHSAhh9iggAgBH0m9/9IW6/865RLwPK5XJ8579+HI8//eyo3hcAgNVddtllkaZpfPvb347DDz88Jk2aFDvssEO8/e1vj0mTJg1NAcyfPz+amprihBNOiG233TaOPPLIOP744zPLPX/+/HjjG98Ye+65Z0yaNCn++Z//eWh/xNrcfvvt8bvf/S4uvfTSOO2002K33XaLCRMmxIEHHhhf+cpXhvZWfP/734/3vve9ccIJJ8T2228fO++8cxxzzDFx0kknDbveP/7jP8YhhxwSO+ywQ5x//vkxbty4uOmmm9b5a/i///f/xlFHHRWnn3567LTTTjFp0qQ4/PDD4wMf+MAGfleAkeBoJgCAEfbJL14W3/vaF2OnHbYflX0R5XI5fvenv8SVP/pJ1e8FAMDrW7BgQZx11llx2mmnxZlnnhlbbbVV9Pf3x3PPPRfXXHNNXH/99REv7zq4/PLL4z3veU+ceeaZ8eCDD8Z3v/vd+MQnPpFJ7v/6r/+KbbfdNr70pS9FT09P3HjjjXHnnXdGsVh8zdd95jOfieOOOy7e8Y53xGmnnRalUinmz58fv/3tb+Pee++NiIibbropent745RTTokPfvCD0dPTE88880z87Gc/G3at7373u/He9743dtlll1iwYEH867/+a7z00kvr/DXce++98YlPfCJOP/30OPXUU2NgYCDmzp0bv/rVrzbwuwKMhFxHR4eZJACAETZ+883iu1/5XEzafrvIp9UrI8rlcvzp7vviX/7989Hfv/oZwAAAMBZMmDAhfvzjH8eZZ545bBk1UB8czQQAUAXLlq+I93/0wnjo4ceqchbt4Dj/Db/9XXzsf35OCQEAAEDNUkQAAFTJSytXxZn/fEl85Yqroq+/PwYGRmZvxECpFC+uXBUXfPKz8b/+99cspwYAAKCmOZoJAGAUTJq4bfzrBR+ON03fJwYGShu0O6JUKkXkcnHTrbfHV664Kl58aWVVsgIAAMBIUkQAAIyiN+w4KU46/h3xj8e8PdpaW/9eLkREuoY9EgMDA5EkSSRJEsuWr4hrrv9VXPfr38bzLyzLIDkAAABsGEUEAEAG2lpbY+8994ipu06OKbtNjim77RLFQiGa8mn09ffHiy+ujNmPPR6PPP5UPPz4k/G3Rx93BBMAAABjkiICAAAAAACoGsuqAQAAAACAqlFEAAAAAAAAVaOIAAAAAAAAqkYRAQAAAAAAVI0iAgAAAAAAqBpFBAAAAAAAUDWKCAAAAAAAoGoUEQAAAAAAQNUoIgAAAAAAgKpRRAAAAAAAAFWjiAAAAAAAAKpGEQEAAAAAAFRNPusAAAC1aMaMGXHOOefEjBkzolwuR0REa2tr3HDDDTF79uy44IILhp67zz77xGWXXRannXZaLFiwIKZOnRpf//rX4957741LLrlktWsfcsghceqpp8akSZMiSZJYsmRJ3HfffXH55ZdHRMTRRx8dF198cURElMvlWLp0adx3333xne98J1asWLHGvIceemgcf/zxMXny5Ghqaopnn302fvCDH8S999479Jy99947TjnllNhtt91iyy23jEsvvTTuvPPOEf/eAQAAwCspIgAA1mDmzJlRKBRi9913j0ceeSTi5Q/yly1bFlOmTImmpqbo7++PiIjp06fHokWLYsGCBRERceyxx8Z1110Xxx57bGyxxRaxdOnSoevuu+++8clPfjKuuuqq+POf/xyVSiV23HHH2G+//Ybdf9WqVXH66adHkiSxyy67xEUXXRRbbrllXHjhhWvMu/fee8f9998fV155ZaxatSre8Y53xGc/+9n40Ic+FE8++WTEy0XKU089Fb/+9a/j05/+dNW+dwAAAPBKiggAgDWYO3duvPDCC9HR0TFURHR0dMSdd94Z06dPj6lTp8asWbOGHp85c2bEyx/2H3744XHOOefE+PHj45hjjokf/ehHQ9c96KCDYvbs2XHNNdcMPTZv3rw1TiYsX748IiKWLl0a1157bbz//e+P5ubm6OvrW+25g9MUg6688sp485vfHAcffPBQEXHPPffEPffcM0LfIeD/a+9+Qmzs+ziOf2Yc05nMLCyZlEgGU85slOWMUjNRYiEbNlJSWPg3ZRZioxRJthaUjVKmJkWRlZI5cyiRlRkmThn5P1jMs/Gcnum+R26367nr7vVazfzmmt/1PWf77rp+AADAz3FGBADALEZHR9Pd3d34/b/BoVarNdZbWlqyYsWKRojo6enJ2NhYxsfHc+PGjfT19c3Yc3JyMosXL87ixYv/0ixfvnzJnDlzMmfOnJ+6vqmpKa2trXn37t1fug8AAAD8bkIEAMAsqtVqurq60tzcnNbW1ixbtiy1Wi0PHjxIpVJJkqxatSotLS2pVqvJ99cy3bx5M/n+BMK8efOyevXqxp5Xr17NkydPcuHChVy+fDmDg4Pp6+vL3LlzZ52jo6MjGzduzOPHj/P58+efmn3r1q1pbW3N7du3/+a3AAAAAH+PVzMBAMxidHQ0ra2t6ezsTHt7e54/f563b9+mVqvl8OHDmTt3biqVSl68eJF6vZ5Fixals7Mzg4ODyfeDpm/dupX+/v7Ga5ympqYyMDCQhQsXplKpZOXKldm9e3e2bNmSPXv25MuXL0mStra2DA8Pp6mpKS0tLXn48GFOnTqVJBkeHm7MeOPGjZw+fXrG3OvWrcv27dtz9OjRWQ+3BgAAgP8XIQIAYBYTExOp1+vp7u5OW1tbIya8fv069Xo9XV1dqVQqM56GKJVKuXLlyox9vn37lrNnz+bjx48z9p6YmMjw8HAuXbqUixcvpqenJ9evX0+SfPz4Mbt27cr09HRev34941yInTt3Nn7+9OnTjHv19PTkwIEDOXbsWEZGRgr6ZgAAAODnCREAAD8wOjqa1atXp729fcYB0w8ePMiaNWvS2dmZa9eupbm5OevXr8/58+dz7969GXucOHEivb29GRoa+tN7vHz5MlNTUymXy4216enpTExM/On1s6339vbm0KFDOX78eO7evfuLnxgAAAB+LyECAOAHqtVq9u3bl1Kp1HgiIklqtVr27t3bOB9i7dq1jdcp/e+TD0ly586d9Pf3Z2hoKDt27Ei5XM7du3fz6tWrtLW1ZfPmzSmVSrl///4vz7lu3bocOXIk586dy6NHjzJ//vwkydevXxvzlMvldHR0NP5nwYIFWbp0ad6/f596vf7L9wYAAIAfESIAAH6gWq2mXC7n2bNnefPmTWO9Vqtl3rx5GRsby+TkZPr7+zMyMvKHCJHvIWLbtm1ZsmRJarVaNm3alIGBgcyfPz8fPnzI06dPc/DgwYyPj//ynBs2bEipVMr+/fuzf//+xvr169dz8uTJJMny5ctz5syZxt/27Nnzh2sAAADgd2uqVCrT//QQAAAAAADAv1PzPz0AAAAAAADw7yVEAAAAAAAAhREiAAAAAACAwggRAAAAAABAYYQIAAAAAACgMEIEAAAAAABQGCECAAAAAAAojBABAAAAAAAURogAAAAAAAAKI0QAAAAAAACFESIAAAAAAIDCCBEAAAAAAEBhhAgAAAAAAKAwQgQAAAAAAFAYIQIAAAAAACiMEAEAAAAAABRGiAAAAAAAAAojRAAAAAAAAIURIgAAAAAAgMIIEQAAAAAAQGGECAAAAAAAoDBCBAAAAAAAUBghAgAAAAAAKIwQAQAAAAAAFEaIAAAAAAAACiNEAAAAAAAAhREiAAAAAACAwvwHFhsmcIqE5O8AAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "WASP-21b planet\n", + "WASP-21 WASP-21 is a G-type star (spectral type G3V) that is reaching the end of its main sequence lifetime approximately 850 light years from Earth in the constellation of Pegasus. The star is relatively metal-poor, having 40% of heavy elements compared to the Sun. Kinematically, WASP-21 belongs to the thick disk of the Milky Way. It has an exoplanet named WASP-21b.\n", + "WASP-13 WASP-13, also named Gloas, is a star in the Lynx constellation. The star is similar, in terms of metallicity and mass, to the Sun, although it is hotter and most likely older. The star was first observed in 1997, according to the SIMBAD database, and was targeted by SuperWASP after the star was observed by one of the SuperWASP telescopes beginning in 2006. Follow-up observations on the star led to the discovery of planet Cruinlagh in 2008; the discovery paper was published in 2009.\n", + "Cruinlagh planet\n", + "Gamma Cephei Gamma Cephei (γ Cephei, abbreviated Gamma Cep, γ Cep) is a binary star system approximately 45 light-years away in the northern constellation of Cepheus. The primary (designated Gamma Cephei A, officially named Errai , the traditional name of the system) is a stellar class K1 orange giant or subgiant star; it has a red dwarf companion (Gamma Cephei B). An exoplanet (designated Gamma Cephei Ab, later named Tadmor) has been confirmed to be orbiting the primary.\n", + "Tadmor planet\n" + ] + } + ], + "source": [ + "subgraph = astronomy.search(\"\"\"\n", + "MATCH P=(A {type: \"star\"})-[]->(B {type: \"planet\"})\n", + "WHERE B.id <> \"Jupiter\" AND B.id <> \"Saturn\"\n", + "RETURN P\n", + "\"\"\", graph=True)\n", + "\n", + "plot(subgraph)\n", + "\n", + "for x in subgraph.scan():\n", + " print(subgraph.attribute(x, \"id\"), subgraph.attribute(x, \"text\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Standard SQL queries are also supported. Let's show the stars that are brightest from Earth." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'id': 'Sirius',\n", + " 'text': \"Sirius is the brightest star in the night sky. Its name is derived from the Greek word (Latin script: ), meaning 'glowing' or 'scorching'. The star is designated \\xa0Canis Majoris, Latinized to Alpha Canis Majoris, and abbreviated \\xa0CMa or Alpha\\xa0CMa. With a visual apparent magnitude of −1.46, Sirius is almost twice as bright as Canopus, the next brightest star. Sirius is a binary star consisting of a main-sequence star of spectral type A0 or A1, termed Sirius\\xa0A, and a faint white dwarf companion of spectral type\\xa0DA2, termed Sirius\\xa0B. The distance between the two varies between 8.2 and 31.5\\xa0astronomical units as they orbit every 50\\xa0years.\",\n", + " 'visual_magnitude': -1.46},\n", + " {'id': 'Canopus',\n", + " 'text': 'Canopus is the brightest star in the southern constellation of Carina and the second-brightest star in the night sky. It is also designated α\\xa0Carinae, which is romanized (transliterated) to Alpha\\xa0Carinae. With a visual apparent magnitude of −0.74, it is outshone only by Sirius.',\n", + " 'visual_magnitude': -0.72},\n", + " {'id': 'Arcturus',\n", + " 'text': 'Arcturus is the brightest star in the northern constellation of Boötes. With an apparent visual magnitude of −0.05, it is the fourth-brightest star in the night sky, and the brightest in the northern celestial hemisphere. The name Arcturus originated from ancient Greece; it was then cataloged as α\\xa0Boötis by Johann Bayer in 1603, which is Latinized to Alpha\\xa0Boötis. Arcturus forms one corner of the Spring Triangle asterism.',\n", + " 'visual_magnitude': -0.05},\n", + " {'id': 'Vega',\n", + " 'text': \"Vega is the brightest star in the northern constellation of Lyra. It has the Bayer designation α Lyrae, which is Latinised to Alpha Lyrae and abbreviated Alpha Lyr or α Lyr. This star is relatively close at only from the Sun, and one of the most luminous stars in the Sun's neighborhood. It is the fifth-brightest star in the night sky, and the second-brightest star in the northern celestial hemisphere, after Arcturus.\",\n", + " 'visual_magnitude': 0.03},\n", + " {'id': 'Capella', 'text': 'Capella', 'visual_magnitude': 0.08}]" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "astronomy.search(\"\"\"\n", + "SELECT id, text, visual_magnitude FROM txtai\n", + "WHERE visual_magnitude IS NOT NULL\n", + "ORDER BY visual_magnitude\n", + "LIMIT 5\n", + "\"\"\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Retrieval Augmented Generation (RAG)\n", + "\n", + "Next, we'll run a series of RAG queries using the knowledge graph." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from txtai import LLM, RAG\n", + "\n", + "# Define prompt template\n", + "template = \"\"\"\n", + "Answer the following question using only the context below. Only include information\n", + "specifically discussed. Answer the question without explaining how you found the answer.\n", + "\n", + "question: {question}\n", + "context: {context}\"\"\"\n", + "\n", + "# Load LLM\n", + "llm = LLM(\"Qwen/Qwen3-4B-Instruct-2507\")\n", + "\n", + "# Create RAG pipeline\n", + "rag = RAG(\n", + " astronomy,\n", + " llm, \n", + " system=\"You are a friendly assistant. You answer questions from users.\",\n", + " template=template,\n", + " context=10\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The stars in the Andromeda constellation mentioned in the context include:\n", + "\n", + "1. Omicron Andromedae (ο And), a blue-white B-type giant approximately 692 light years from Earth.\n", + "2. RT Andromedae, a variable star approximately 322 light years from Earth.\n", + "3. 8 Andromedae, a probable triple star system approximately 570 light years from Earth.\n", + "4. 64 Andromedae, a deep-yellow coloured G-type giant approximately 419 light years from Earth.\n", + "5. 3 Andromedae, a single star approximately 181 light years from Earth.\n", + "6. Epsilon Andromedae, a star approximately 155 light years from the Sun.\n", + "7. HD 166 or V439 Andromedae, a variable star approximately 45 light years away from Earth.\n", + "8. 7 Andromedae, a single, yellow-white hued star approximately 79.6 light years from Earth.\n", + "9. 18 Andromedae, a single star approximately 413 light years from Earth.\n", + "10. 12 Andromedae, a single star approximately 137 light years from Earth.\n", + "\n", + "These stars vary in their distance from Earth, ranging from 45 to 692 light years.\n" + ] + } + ], + "source": [ + "print(rag(\"Tell me about the stars in the Andromeda constellation and how far they are from Earth\", maxlength=4096)[\"answer\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's try a Graph RAG query. Instead of vector search, we'll run a graph traversal query to generate the RAG context." + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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mc98Ruedd57e+c53qlarxUNXM5mMrr32Wi1evFiZTEbvfve7dd555+mUU05RFK3fxXFgYEAHH3ywrrjiilEhRMfaC3af+9zn1Gq1dO6556pWq+kNb3iDvva1r+md73xn3FJp9uzZetnLXqaPf/zjGhgY0Gc/+1kdf/zxuuKKK/TNb35Tz3ve8/T444/ryiuvlCSVSiUZhqFnnnlGn/3sZxWGofbbbz+deuqpWrVqlX73u9+N6/N29NFH6wc/+IG+//3vj/tzDmB6cxxHkpgPAQAAAGwFhlUDADBNuK6rN77xjfrOd76jO+64Q0888YS++tWvqtVq6fDDD1cYhiqXy5Kk4eFhDQ8Px4HB73//e/3hD3/Q8uXLtXjxYl144YWaN2+edtpppw0ea86cOTJNU08++eQma9pvv/2011576fOf/7z+9a9/admyZfr2t7+tarWqf//3f49vZxiGzj//fD3++OO67777dOutt+rAAw+U1oQavu+r2WzGdYdhqCAIdPXVV+tf//qXFi9erP/93//VwoUL9cpXvnLcn7u7775b1157rZYvX77JXSAA+o/rumq328yLAQAAALYCOyIAAJgmZs+erVQqpX/+85/xdUEQ6KGHHtpooNAxZ84cvfvd79bee++tYrEo03zuXIVZs2bp8ccfX+/2hmGMqabddttNmUxGN95446jr0+m0Zs+eHV9euXLlqBkPq1at0uDg4GYf/8gjj9TrXvc6bbfddnIcR7Zt67HHHhtTbWt7+OGHx30fANOfZVlKpVJxiAsAAABgyxBEAAAAfelLX9LKlSv1ta99Tc8++6xM09RVV10l297wS4WlS5cqDEPtuOOOm3xc13W1evXqeJ7E2qrVavzvdQfARlEUhyEbc8ghh+j000/XZZddpvvvv1/1el3HH3+89t13X2WzWdXrdYVhuF5osqHntKH2UgDgOI7CMKQtEwAAALCVaM0EAMA0sXz5crXbbe23337xdZZlaa+99op3NXQW/C3Lim9TKBS044476oc//KHuuusuPfnkk8rn85s8VqVS0d/+9jcdeeSRcl13vY/ncjlJ0iOPPKIZM2YoCIK47VHnz3jOMPY8b71gYr/99tP999+vG2+8UYsWLdLy5cu1/fbbKwxD5XI5pdNplUolzZw5M76PaZraeeedx3xcAP3NdV1CCAAAAGACEEQAADBNNJtN3XTTTTrttNN00EEHaaeddtJHPvIROY6jm2++WVrTAikMQ734xS9WsViU67qqVCoqlUo64ogjNHv2bD3/+c/X+973vs0e7xvf+IZM09Rll12mV7ziFZozZ4523HFHveUtb9Gll14qSbrzzjt1//3364tf/KJe8IIXaNasWdp33331nve8R3vssceYn9vKlSu19957a9asWSoUCjIMQ8uWLdMee+yhgw46SM973vP07ne/W3vuuafCMFSz2VShUNA999yjF77whXrRi16kuXPn6qyzztpsyAIAkpRKpWRZFjumAAAAgAlAayYAAKaR7373uzJNU5/4xCeUzWb18MMP65xzzonbID377LO6+uqrdcopp+icc87Rr3/9a11wwQU677zzdOaZZ+qqq67SkiVLdMkll+jiiy/e5LFWrFihU089Ve94xzv03ve+VzNmzFCpVNK//vUvXXTRRfHtPvaxj+nkk0/WOeeco8HBQa1evVr33nuvhoeHx/y8fvKTn+hjH/uYrr76armuq2OPPVY///nPtdtuu+kzn/mMoijSb3/7W91444164QtfqEqlItu29Ze//EW77rqrPvaxjykIAl133XW65557tuIzDKBfuK4r3/fXax0HAAAAYPyMBQsWREkXAQAAMNFM09TQ0JB831epVEq6HABTiGEYmjlzpmq1mhqNRtLlAAAAAFMerZkAAMC0FIahyuWyUqlUPLMCAMbCcRxJYj4EAAAAMEEIIgAAwLTleZ5qtZqy2Wy8sAgAm+O6rjzPUxiGSZcCAAAATAsEEQAAYFprNBpqNpsaGBiQbTMeC8CmWZalVCrFkGoAAABgAhFEAACAaa9Sqcj3fRUKBRmGkXQ5AHqY4zgKw5C2TAAAAMAEIogAAAB9oVwuS5KKxWLSpQDoYa7rEkIAAAAAE4wgAgAA9IXO8GrbtpXP55MuB0APSqVSsiyLtkwAAADABCOIAAAAfcP3fVWrVWUyGbmum3Q5AHqM67ryfV++7yddCgAAADCtEEQAAIC+0mw21Wg0lM/nlUqlki4HQI8wDEOO47AbAgAAAOgCgggAANB3qtWqPM9ToVCQafJyCMBzQ6olMR8CAAAA6ALeeQMAgL5ULpcVRZEKhULSpQDoAY7jyPM8hWGYdCkAAADAtEMQAQAA+lIURSqVSrJtWwMDA0mXAyBBpmkqnU7TlgkA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/W3CawaAJFWrVVmWpWKxqOHhYYVhmHRJQNc4jsPuH6DPrRtAlMvlrs6Mufvuu+MB0wMDA3rTm96k888/X+973/u0cuXK+Haf/vSnFYah0um0SqWSvvKVr4waaK01w65vv/12vfa1r5VhGLr99ts3ubNiXeedd56y2azmzZun008/Xcccc4x+/OMfr3e7ww8/XIsXL9ZDDz00ruf6+OOPx8/tlFNOURAEuuGGG7R69er1Xl9ceuml+v73v6+5c+fqlFNO0RlnnKGLL754XMcDAPQXgggAPSOfz+uCL31dL/i3gxRFkQzDmJDHtSxL22yzrb51yXf1o2t+qEsuvYi+0gCmlbWHV4+MjCRdDtAV6XRapmkSRAB9au0Awvf9rgcQHc1mc1RLoq9+9av6xS9+ode//vW68sor4+svvfRS3XnnnarVaqNmR6zrlltu0Qc+8AFJ0je+8Y1x1fLMM89Ikp544gmZpqmzzz5bP/3pT0eFBK7r6pBDDtHVV1896r6rV6+WJA0NDcX/7lxetGhRfPk3v/mNfvOb32hoaEiNRkOS9La3vW29ORadeRhLlixRuVzWJZdcoh/84AejHhsAgLVtuL8JAEyywcEhfedbV+r5Cw6U1uyMmEiWZUmSjj/2Hfrsp78QXwaA6SCKIpXLZdm2rYGBgaTLAbrCdV15nqcgCJIuBcAksm1bxWJRQ0NDsixL5XJZw8PDkxJCbEgURQrDUI7jjLp+9erVWr58+SZDCEm644474nk3f/vblu/WNk1T9pr5eGv793//d6XTad16662jrl+xYoVWrVqlAw88ML4um81q77331v3337/e4w8PD6vZbOqQQw5Ru93W3//+903WonVa5QIAsC52RABIXC6X1ze/8W3tvNMuXQ8IDMPQaw99nTzP0//35c939VgAMJl831elUlGhUJDv+/FZjMB0YBiG0uk0Q6qBPmLbtnK5nNLp9KTugFhXKpXS0NCQtKY105vf/GZlMhn9+c9/3qLHC8NQJ554YvzvsXj1q18t3/f16KOPyvM87bnnnjr55JO1cOHC9cLZww8/XH/84x832PLpuuuu0zvf+U4tW7ZMK1as0EknnaRnn31Wf/zjH+PbHHnkkbr//vvVaDT0ghe8QKeddpq+973vqVarSZJe+MIXamhoSA899JAajYZ22WUXnXbaabrvvvtGtaoCAGBd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3+RQxrHotGW64IILtGjRIl166aVb9Xhj1RnOXSwWVSgUVC6XJ+W4U10nmGg0GtImggnf99VutwkmAAA9KZVKqVwu65FHHtHcuXO1fPnyjd7W8zxZljXquv3220+/+tWv9Mc//lFas0Ni++233+Qxb7vtNp188snabbfd1tvdYFmWUqmUHn/8cbXbbW233Xaj2jCNl+/7652At+eee8owDF122WVxcPHKV75y1G1GRkY0NDQ06rrddttti+sA+g1BBJCw/7+9+46Tq673P/4+Z86cM31mN4RAQmiBQCDAgoBwQS7YKEYulxqlCyIoVQQCaGwoTQTpqDRRka6AoCKi4aJSQwslgRDSQ8hOLzsz58zvD7Pzy6aX3T2zu6/n47EP2Jlz5nx2smXmvM/380lFLJ34qVG+hBDdAqah/919hO78xxzNS/ftIMyhjGHMAOC/7hY63T2C0+n0Bi2dHzFihG644QYVi0XddtttmjlzpizL0h577KFzzjlHJ5544gbV299tmbT05Hn3CfXuMCIWi6lQKPRrHYPB8sFEd2uKlQUTy66Y4O81AKC/GYaheDwuLZ0ZVa1W9atf/Uo//vGPtWjRIk2ZMkWe52nMmDHaaqutdMcdd0iSFi5cqN12201vvPGGarWaCoWC5s6dq0996lP65z//KUk6+eST19h66aGHHtJee+2la665RnfeeafeeOMNlUolbbfddvrSl76kq666Su+//77uu+8+feMb35BpmnrjjTcUjUY1fvx4lUol/fnPf16rr3XhwoXadNNNNWbMGC1evFjlclnz5s1TMBjU4Ycfrn/+858aP368Dj300B77vfrqqzrnnHM0ceJETZkyRXvuuaf23HPPlpgpBgwEBBGAzw7dbYQCPoYQ3TxPOmLPTXX9n2f5XUpLYxgzAAxMlmUpGo3Ktm3VajVlMpleuRL93HPPlZa2IFh2Gf6sWbP0xBNPSJKi0ajOOOMM7bPPPgoGg3r33Xd18803N3sXn3jiidp33311//336ytf+YpisZheeOEF/fSnP5VlWTr99NPV0dGhjo4OHXnkkZKkiRMnavHixTr//PO16667qr29XYsWLdKjjz7aY9Ditddeu8JKih/+8IcqFAq68sorpaUDHJ944gltttlm2mefffTss8/qT3/6k6677jpNmDBBhUJB8XhcW2yxhW688UZNnDhR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ZZfpgAMOkCSdeOKJ2nffffXII4/ouOOO04gRI/SZz3yG1kwAsA5M02wGEJ7nKZfL0eKmBdi2zb/DOsjlckqlUkomk8pkMrSiAVYRTFiW1WznFIvFegQT3R+sggP8l0gkZNu28vk8rwcADDkEERhyqtWqkslk8/PddttNpVJJF1xwgSQpFArpqquu0ltvvaXTTz9dbW1t+ta3vqVzzjlHV155pV577TU99NBDuuSSS3Tqqadq00031cknn6zvf//7zcBjZY488kjdfvvt+tWvfqWjjjpKF198saZNm6Ynn3xSt956q0477TRdfPHFOvnkk6WlJyqmT5+ue++9V6VSSXvttZcuueQSzZ8/X++8885Kj/HZz35Ws2fPXuWqh+6+k5/97Gd18skn6/rrr9eMGTO07bbb6vzzz1elUtGf//zn5vannHKKbr31Vs2dO1ennHKKvvOd7+jYY4/VtGnTdOONN+qkk07SCSecIEnN5aSWZemOO+7QnDlzlEql9PWvf10XXXSRLr744nX6dxo1apT2228/TZ48mZMOALAODMNQJBJROByW53kqFApcbdciAoGALMuiD/Q6ymazamtra4YRjQadZYFlNRqNZtjQPeB92RUT0WiUYALwmWEYSiQSCgaDtBwEMGQRRGBI2W233bTHHnvo4Ycfbt5WqVR09dVXN1+If+ELX5Bt27r88stVqVQ0a9YsXX/99frRj36kn//850qn07r99tv1iU98Queff7622mor/eUvf9E///nP1R77+eef12OPPSZJuvvuu/U///M/evfdd/WPf/xDWrpq4uabb1ZbW5vS6bQ+/vhj3X///c39H3nkEe2xxx7af//9VxlEjBo1SnPmzFnj83DSSSfplltu0bPPPitJWrhwobbYYgtNmDChRxBx//3369///re0dMXDXXfd1TxGoVCQpBXClyeffLL5/wsWLNANN9yg2267TaFQaJ1OhFmWpcsvv1zZbHat9wGAocwwDIXDYUUiETUaDRWLRXoOt5ju1Sm0xlo3jUZD2WxWqVRKiUSC1wbAWugOG7qtKpio1+vNVk4EE0DfMAxDyWRSgUBAmUyGnzUAQxZBBAa9vffeW0888YQCgYBM09TTTz+tu+++u3n/zJkze7wQ2GKLLfT+++/3OGn+5ptvKhAIaPTo0Uqn06rX6/rRj36k22+/XQsXLlyrVkEzZ85s/n/3yfuV3dYdRJimqWOPPVb777+/Ntpoo+Ybh9Ut3zQMY411hEIhjRo1ShdccIG+9a1vNW8PBALNcKHb+++/3/z/JUuWNOtbXdgxduxYnXjiiRozZozi8XizphEjRujDDz9cY33dFi1axIkGAFhL3QGEYRgql8sqlUpcNd6CHMchhFhPrusql8spmUwqHo8zNwpYR8sHE8u2copEIjJNs8fKiuW3B7B+TNNUMpmUYRjMOwIw5BFEYNCbOnWqrr32WtXrdX388ccrtPlZ33YVO+64o7S0x2M8Hl/j46zsqodlb+s+YdR94v6YY47REUccoRtvvFEffPCByuWyzjzzTFnWqn9s586dq9GjR6+2jnA4LEm65ppr9NZbb/W4b/nnZnX1rUx3W6sXX3xRP/rRj5TJZDRixAhdffXVzbpXdmJsZV8TbUQAYM1CoVDzBFKlUlGpVKKdXYuiLdOGq9VqyufzSiQScl232YIGwLqr1+uq1+s92qt2X/gUDocVjUYJJoANFAgEmm2hmXMEAJLpdwFAX6tUKpo/f74++uijtfrD/+GHH2rMmDEKhULN28aPHy/XdZsrAUaOHKlvfOMb+slPfqK3335bkyZNWqvVCOti/Pjxeu655/TXv/5V77//vhYsWKDNNttstfs8/fTT2nzzzbXPPvus9P5oNKp0Oq3Fixdr00031fz583t8LFy4cK3rq9frMs2ev0I233xzJZNJ/fznP9cbb7zRnBOxrEwmo0gk0uP53Wabbdb6uACA/8wRamtrUzweV61WUzqdVqFQ4A1uC3McpzlgFuuvq6tLhUJB0Wi0x2sJABumO5TI5XJasmSJOjs7VSwW1Wg0FA6HlUqltNFGGymVSikajSoYDPb6+x9gMLEsS6lUSo1GgxACAJYiiACW89e//lXValWTJk3SlltuqY6ODp111ll66qmnmi2TLrnkEr344ov605/+pCuvvFJjxozR0Ucf3at1zJs3T5/4xCe04447avPNN9c3v/lNtbW1rXafZ555Rn/729/07W9/W8cee6zGjh2rESNGaK+99tI111yjjo4Oaem8hy9/+cs6/PDDtdlmm2mrrbbSQQcdpKOOOmqt61u4cKEikYh22203JRIJOY6jRYsWqVqt6vDDD9emm26q//qv/9Lxxx/fY7+3335bXV1dOvXUUzVy5Eh95jOf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vV3RaFRdXV3q7OxUsVgkhAA2kG3btGVCy8jlcvI8T8lkUqbJWzcArSudTiudTmvu3Ll65plndNZZZymbzeq8885b7X433HCDfv/732v+/Pkrvb9Sqei6667TH//4xzWurvjmN7+pp59+WtOmTVvhvn/96196/vnnNW/ePM2dO1e33367yuWydthhh+Y248eP1yOPPKJ33nlHCxYs0K9//WsVCgWNHTtWWjqH7cwzz9Rtt92mxx57THPnztWHH36ov//972v5LAHA0MarWQAABhDbttXW1qZ4PK5araZ0Oq1CoSDP8/wuDRgUHMdRtVr1uwxAktRoNJTNZiVJiURChmH4XRIArJVqtapHH31UO+20k1KpVJ8f76CDDtKmm26qu+++e43bmqapAw44QKFQqEdo8eabb+qAAw5QPB6XYRg64IADZNu2Xn31VUnS2LFjNXz4cHmep5///Od68MEHdcUVV2jLLbfs068NAAYLWjMBADAABINBRaNRBYNBVatVpdNp1et1v8sCBpVgMEhbJrQcz/OUzWaVSqWUSCSawQQA9LW9995bTzzxRI/b1mV11uzZsyVJm2yyiTKZTK/X123UqFH66le/qnPOOWe1F+dstdVWuummm5qzoCZPnqwPP/ywef/3v/99ffe739Wjjz6qer2uSqWiyZMnN1drbLrpppKkE088UbfccosWLlyoo48+Wtddd52OP/545fP5PvsaAWAwIIgAAKCFWZalaDQq27ZVq9WUyWQYWgr0Ecdx5LouIR9ajuu6yuVySiaTisViKhQKfpcEYAiYOnWqrr322h637bDDDrr00kvXav/uVVx92TrUNE19+9vf1l133aW5c+eudts5c+bo1FNPVSwW03777adJkybp3HPPbYYRX/nKVxSLxXT++ecrm81qn3320Xe/+12dffbZ+uCDD5ohzLIDrK+88krdf//92n///fXYY4/12dcJAIMBQQQAAC0oEAgoGo3KcRzV63Vls1naxQB9zHEcVSoVv8sAVqpWqymfzyuRSMjzPJVKJb9LAjDIVSqVFWY3DB8+fK3332KLLSRJCxcu7PXauoXDYW2//fbadtttdc4550hLAxDTNPXXv/5VF1xwgaZOnSpJqtfrza9n+vTp2n777XXEEUfopz/9qUaOHKnDDz9cJ598cnN49fvvv6+dd95Zhx12mK699lot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Dz2kV155pdeOc+211+q9997TTTfd1GuPuTqLFy/W4Ycfrmw22y/HA+Cv7rZM9Xrd71IADCC5XE6pVErJZFLpdFqNRsPvkgAAAIY8gggAWEuHHHKIHnnkER1yyCEaNmyYlixZ4ndJKzVixAjdcMMNKhaLuu222zRz5kxZlqU99thD55xzjk488US/S1wvlmWpXq8rnU77XQqAfmLbNqshAKyzRqOhbDbbDCMymYzfJQEAAAx5BBEAsBZCoZAOOOAAnX766Wpvb9dBBx2k3/zmN6vc/sADD9SZZ56pK664Qqeffro23nhjvfbaa7r66qu1ePHiHtt+7nOf01e+8hXFYjG98MIL+slPfqJyuSxJ2mOPPXT88cdrq622kuu6euutt3TjjTdq/vz5qzz2ueeeK0k644wzevRVnzVrlp544onm59FoVGeccYb22WcfBYNBvfvuu7r55pv1/vvvS0tbR+277766//77V1rfRRddpI6ODnV0dOjII4+UJE2cOFGLFy/W+eefr1133VXt7e1atGiRHn30UT300EPNY69sJcUPf/hDFQoFXXnllZKke++9V0888YQ222wz7bPPPnr22Wd111136Xe/+51OPfXUZp0ABifLshQIBFStVv0uBcAA5HleM4xIJBLK5XJ+lwQAADCkMSMCANbCAQccoNmzZ2vOnDl66qmndPDBB69xH8dxdNxxx+nyyy/XWWedpVgspsmTJ/fYZuTIkdp333118cUX65JLLtEuu+yiL3/5y837w+GwHnjgAX3ta1/T+eefL8/z9IMf/ECGYaz0mPF4XHvuuad+//vfr3S4a7FYbP7/9773PaVSKV100UX62te+phkzZuiaa65RPB5fq/puvPFGvfnmm3r88cd1+OGH6/DDD9fixYtlGIYWL16s733vezrppJN0zz336JRTTtH++++/ls/2/3f00Ufrvffe02mnnaZ77rlnnfcHMHB1t2Vi2CyA9eW6rnK5nGzbViwW87scAACAIY0gAgDWwiGHHKK//vWvkqQXXnhB0WhUu+yyy2r3CQaDuv766/XWW29p+vTpuvzyyzV+/Hhtv/32zW0Mw9AVV1yhWbNm6Y033tBTTz2l3XbbrXn/lClT9Oyzz2r+/Pl6//33ddVVV2nMmDHaYostVnrMUaNGyTRNzZ49e7W1ddfx/e9/X9OnT9e8efN06623qlAo6L//+7/Xqr5isah6va5KpaJ0Oq10Oi3P8+S6ru666y5Nnz5dCxcu1F//+lf96U9/Wq8gYurUqXrggQc0f/781a4CATD4OI7DaggAG6xWq6lQKCgcDiscDvtdDgAAwJBFayYAWIPRo0dr++2313e+8x1p6VL/Z555Rocccohee+21Ve5Xr9f1zjvvND+fM2eO8vm8tthii+btixYtarZhkqQlS5YolUo1Px81apROPvlkjRs3TslkUqb5n/x4xIgRmjVr1grHXNVKieVts802CofD+sMf/tDjdtu2NXLkyObna6pvVQ477DAdfPDB2njjjeU4jizL0nvvvbdWtS3r3XffXed9AAx83W2ZmA8BoDdUKhWZpqlYLCbP8/jdAgAA4AOCCABYg0MOOUSWZenBBx/scXutVtP111/fo93RuqrX6z0+bzQazbBBkn784x9r0aJFuuaaa/Txxx/LNE3deeedsqyV//qeO3euPM/T5ptvvtrjhkIhdXZ2NudJLKtQKKx1fSvTPUvjlltu0bRp01QqlTRx4kSNGzeuuY3neSuEJiv7mlbWXgrA4Oc4jjzPoy0TgF5TKpUUCAQUj8f5/QIAAOADgggAWA3TNPX5z39eN998s1588cUe91122WX69Kc/rccee2yl+1qWpe222665+mH06NGKx+P68MMP1+rYiURCm2++uX7yk5/ojTfekJa2VFqdfD6vF198UYcddpgefvjhFU7kR6NRFYtFzZgxQ+3t7XJdV4sWLVqrelamVqutEEyMHz9e06ZN67HaYtlVFpKUzWY1bNiw5uemaWrLLbfUq6++ut61ABg8bNvmimUAvS6fz8s0TSUSCWUyGbmu63dJAAAAQwYzIgBgNfbee2/FYjE98cQTmjVrVo+PKVOm6JBDDlnlvrVaTWeffbbGjRunsWPH6qKLLtK0adN6tGtanXw+r2w2qwkTJmjkyJHadddd9fWvf32N+/3sZz+TaZq65ZZbtN9++2nUqFHafPPNdfjhh+umm26SJL388suaNm2aLrvsMu2+++4aMWKEdtxxR51yyikaO3bsWj8/ixYt0rhx4zRixAglEgkZhqF58+Zp7Nix2mOPPbTZZpvp5JNP1nbbbddjv6lTp+qTn/yk9tprL40ePVrnnXceQyQBSJICgYAsy2I+BIA+kcvl5Hlej5aXAAAA6HusiACA1TjkkEP0yiuvrLT90pQpU/SlL31JW2+99Ur37erq0r333qtLL71Uw4cP1+uvv66rr756rY/daDT0gx/8QGeddZbuvPNOzZkzRzfccIOuu+661e63YMECnXbaaTruuON0xhlnqL29XdlsVtOnT9e1117b3G7SpEk69dRTdeGFFyqVSqmzs1Ovv/660un0Wtd43333adKkSbrrrrsUCoU0ceJEPfbYY9pmm200efJkNRoN/e1vf9Mf/vAHffKTn2zu98QTT2jMmDGaNGmSXNfVgw8+yGoIANIybZkIIgD0hUajoWw2q1Qq1VwZAQAAgL5ndHR0NPwuAgAGmwMPPFBnnnmmvvjFL/pdCgAMKG1tbarX68rn836XAmAQCwQCSqVSqtVqyuVyfpcDAAAw6LEWFQAAAC2huy0T8yEA9DXXdZXL5WTbNu0hAQAA+gFBBAAAAFoCbZkA9KdaraZ8Pq9wOKxIJOJ3OQAAAIMarZkAAADQElKplFzXpS0TgH4ViUQUjUaVy+VYkQUAANBHGFYNAAAA35mmqWAwqFKp5HcpAIaYUqkk0zQVj8fleZ5qtZrfJQEAAAw6tGYCAACA7xzHUaPRoC0TAF8UCgXVajUlEgkFAgG/ywEAABh0CCIAAADgO8dxCCEA+CqXy8nzPCWTSZkmb5UBAAB6E6+uAAAA4Kvutkz0Zgfgp0ajoWw2K0lKJpMyDMPvkgAAAAYNZkQAAADAV7RlAtAqPM9TNptVKpVSIpFoBhPrImgFlIpHFbItuV5DlWpVndlin9QLAAAwUBBEAAAAwFe2batararRaPhdCgDIdV3lcjklk0nFYjEVCoXVbp+IhvWZT+6oHbfZTDttu5m23mxjWcvNmSiUKnp75jy9+f48vfLWLP3rtRlyPa+PvxIAAIDWYXR0dPCODwAAAL4wDEPDhg1TPp+nNROAluI4jhKJhIrFokql0gr377D1KB194Cd18L67yLICcl1PQWvVg669RqO5zeLOnO778/N65G8vsloCAAAMCQQRAAAA8E0oFFIsFtOSJUtYEQGg5UQiEUWjUeVyuWZYOrwtoclfO0z77rad6nVX1mrCh1VpNBryGg15XkO3PfA33f3oFNVdVkgAAIDBiyACAAAAvkkmk5K0Xn3YAaA/xGIxhUIhZbNZHfRf43XRV74ox7ZWaL+0vhqNhqZ/uFDfvuEBvTdnUa88JgAAQKshiAAAAIAvutsyFQoFVSoVv8sBgFVqb2/TpJMP0ec+uYM8z5Npmr36+HXXlSRdev0D+su/3ujVxwYAAGgFvfvqCQAAAFhLjuNIErMhALQ0K2DqO6d8QZ/Zc5wk9XoI8Z9jBGQahi4/9xh98b937fXHBwAA8BtBBAAAAHxh27ZqtRqzIQC0tO9//Qjt94ntZBpGnx7HNE0Zkr53xhH69J479OmxAAAA+htBBAAAAPqdYRiybZvVEABa2hGf3UOHfKqjT1ZBrIxhGJIauuzMo7TJsGS/HBMAAKA/EEQAAACg39m2LcMwVK1W/S4FAFZq0+EpfevEQ/p91ZZpmgoGA/re1w/v1+MCAAD0JYIIAAAA9DvHcVSr1eR5nt+lAMBKff+MI2RZgaWrFPqXFQjokztto8M/s0e/HxsAAKAvEEQAAACgX9GWCUCr22PHrbXH+K1lBQK+1eA1GvrGxM/KCvC2HQAADHy8ogEAAEC/6m7LRBABoFUdc9BeqtddX2swDUPtyZj2332cr3UAAAD0BoIIAAAA9CvbtmnLBKBlDW9LaP89xsmy/FsN0a3uepp48N5+lwEAALDBCCIAAADQrxzHYTUEgJZ18L47S/07n3qVrICpT+ywlTbdKOV3KQAAABuEIAIAAAD9prstU7Va9bsUAFipncdu7ncJK9hxm838LgEAAGCDEEQAAACg3ziOo3q9Ltf1t/c6AKzKTtuOVqCFBkTX6q522Hqk32UAAABskNZ5dQUAAIBBz7Zt2jIBaFnJWFgbtyf8LqOHQMDUjmNYEQEAAAY2gggAAAD0C9u2ZZomQQSAlrVJC85iMA1Dm23S7ncZAAAAG4QgAgAAAP3Ctm3aMgFoaSE76HcJK2UHLb9LAAAA2CAEEQAAAOgXjuOwGgJAS2s0Gn6XsFKtWhcAAMDaIogAAABAnwsGgzJNU9Vq1e9SAGCVKtWa3yWsVLVa97sEAACADUIQAQAAgD7nOI5c11W9zsk0AK1r7qJOeS22+sDzPM2c+5HfZQAAAGwQgggAAAD0OdoyARgISpWq5i3q9LuMHrxGQ9NmzvO7DAAAgA3CxCsAAABsECtgauTwNoWc7vZLNX2UzqtQqkjLtGUiiAAwELwxY442HZ6SFQj4XYokyQoE9PbM+X6XAQAAsEEIIgAAALBO7KClA/YYp0/ssJXGb7OZttl8hILWii8rF36c0esz5mjG7I/07Kvva/Hixb7UCwDr4tV3Z+ugfXbxu4wmz/P0xow5fpcBAACwQYyOjo7WaoAJAACAlrTp8JSO/OyeOvJzeyoRC6tWdxW0Vn/FcN31ZBiSIUP/ePlt3fenf+uFN2eq0WI92AGgWzwa0l9/frHsoP/X7dVdV8++/K6++ZPf+F0KAADABvH/lRUAAABamh209LUjP62T/udTajSkQOA/Y8bWFEJoadumbp/abTsdsMcOen36bH3npgc1e8GSPq0bANZHvljR41Om6tD/3k3WWvye60tWIKD7/vxvX2sAAADoDQyrBgAAwCrtMGaU7rv6TJ30P/vJNM1mCLE+uvut7zBmlB74ydk69gv/JdMwerFaAOgd9//5ed9DCNfzNGfhEr3w5kxf6wAAAOgNBBEAAABYqS/+96761Y9O12Yj2mWavRcYWIGA7KClb534BV174XFyWqD9CQAs691ZC/T4lKlyXc+3GgKmqavu/COt7AAAwKBAEAEAAIAVHPW5PfWDbxwp0zCaKxn6wj67jtWNl5xIGAGg5Vx15+PKFkpyvf4PI1zX06N/f0X/N/Xdfj82AABAXyCIAAAAQA8H7bOzLvnq/0iSjD5unRQwTe06bktdff6XadMEoKXkixV9/9ZHFDD7922z63rK5Ev6yd1/7NfjAgAA9CWCCAAAADSN2rhN3zvjcHn92AokYJrad9exOm7CPv12TABYG1Nefke3PfC3fjue63mq1V2de9U9yhcr/XZcAACAvkYQAQAAAGnp6ocffONIBQJmv69OMAxDZ37p89pq1PB+PS4ArMmtDzytux99ts+P47qeajVXZ11xt958b26fHw8AAKA/EUQAAABAknTU5/fUbuO27NOZEKtjGNIPl86lAIBWct2v/6QbfvtneY1Gnwywrrue8qWKTvvB7Xpp2ge9/vgAAAB+I4gAAACArEBAZxz9WTX6sSXTymrYcZvNtM+uY32rAQBW5Y7fT9FXvvNzzV+cltdLA6y7H+fpf7+p/z33Wr0xY06vPC4AAECrIYgAAACAPr3nDkrFI30+nHpN6q6rYw7cy9caAGBVXps+W0edf73uefw51equPM9br5k6rutKkjqzRZ3/k99o0s/uUyZf6oOKAQAAWoPR0dHh32VvAAAAaAl3/OCr2nnbzRUI+H+ditdo6NCzrtG8j9J+lwIAq5SKR3To/rvpSwfvrU02SjVDiVW1t6vVXQWt/9z3whvv63d/+remvPyO3F5aXQEAANDKCCIAAACGuE03SumJmy/wu4wm1/V024N/0y8eesbvUgBgjQzD0C5jN9eO24zSuK1HaedtR2ujVFzBYECe11C1Vtes+Yv15oy5emvmPE1950PNXdTpd9kAAAD9yvK7AAAAAPhrx20287uEHgxD2qnFagKAVWk0Gnr13Q/16rsf+l0KAABAy/J/7T0AAAB8tcPWI1Wru36X0WSapnbcZrTfZQAAAAAAeglBBAAAwBC3w5jNFDBb62VhezKqjVJxv8sAAAAAAPSC1nrHCQAAgH43cnhKpmn4XcYKRgxL+l0CAAAAAKAXEEQAAAAMcXawNceGOXZr1gUAAAAAWDcEEQAAAENco9Hwu4SVcj3P7xIAAAAAAL2AIAIAAGCIq3TV/C5hpbqqdb9LAAAAAAD0Ata7AwAADHHvz/1IozcZpkCgda5R8RoNzV7wsd9lABggLrroIsViMX3nO9/pcfsuu+yi6667ThMmTFCxWGx+Lkme56lUKmnBggV6+eWX9cADD6izs3O1xznrrLM0fvx4bbnllpo9e7a++tWvrrDN1ltvrXPOOUfbb7+9MpmMHnnkEf3ud79r3r/lllvq5JNP1tixY7XJJpvoxhtv1EMPPdTjMcLhsL7yla9o3333VVtbm2bMmKEbb7xR7777bnObE088UZ/+9Kc1fPhw1et1TZ8+Xbfffrvefvvt5jbxeFxnn3229t57bzUaDU2ZMkU33HCDKpXKejzLAAAA66913m0CAADAF2/NnOd3CSuYt6hTpUrV7zIADFLHH3+8jjzySJ1xxhm69957tdtuu+mOO+7QVltttcZ9n3zySf39739f6X2RSERXX321Fi1apK997Wu69dZbdeKJJ2rChAnNbRzH0fz58/Xzn/9cS5YsWenjXHDBBdp99911+eWX6ytf+Ypeeukl/eQnP9FGG23U3Gbu3Ln62c9+plNOOUVnn322Fi5cqKuuukrJZLK5zaWXXqott9xSF1xwgS6++GLtvPPO+ta3vrWOzxYAAMCGI4gAAAAY4t6eOa+lVkPUXVdvzJjjdxkABrF0Oq10Oq25c+fqmWee0VlnnaVsNqvzzjtvtfvdcMMN+v3vf6/58+ev9P7PfvazsixLV111lWbNmqVnnnlGDz/8sI466qjmNu+++65uu+02PfPMM6rVVmyNZ9u29ttvP9122216/fXXNX/+fN19992aP3++Dj300OZ2Tz/9tF555RUtWLBAs2bN0s0336xYLKYxY8ZIkjbffHN98pOf1NVXX623335bb775pq6//nodcMABGjZs2AY8ewAAAOuudd5xAgAAwBdvvjdX9brrdxlNpmFq6jsf+l0GgCGkWq3q0Ucf1U477aRUKrXej7Pjjjvq9ddfV73+/2fcvPjii9p8880Vi8XW6jECgYACgYCq1Z6rwrq6urTTTjutdB/LsjRhwgQVCgW99957zVry+bymT5/e3O7ll19Wo9HQuHHj1vMrBAAAWD/MiAAAABji8sWK/vKvN/T5vXeSZQX8LkfVel1/+r/X/C4DwACz995764knnuhxm2mu/bV3s2fPliRtsskmymQy61VDW1ubFi5c2OO2dDotSWpvb1ehUFjjY5TLZb355ps6/vjj9eGHHyqdTuvTn/60dthhB82b17OV3l577aXJkyfLcRwtWbJE3/rWt5TL5ZrH6z52N8/zlMvl1N7evl5fHwAAwPoiiAAAAIDu/8vzOuRTHX6Xobrr6vF/TFWh3OV3KQAGmKlTp+raa6/tcdsOO+ygSy+9dK32NwxDktRoNPqkvnVx+eWX68ILL9SDDz4o13U1ffp0/e1vf9PYsWN7bPfqq6/q1FNPVTKZ1IQJE/Td735XX//619c7SAEAAOgrBBEAAADQa+/O1nuzF2mrUcN9nRdhBQJ64C/P+3Z8AANXpVJZYXbD8OHD13r/LbbYQpJWWNGwLtLptNra2nrc1v15Z2fnWj/O/Pnzde655yoUCikSiaizs1OTJ0/WggULemzX/TXPnz9fb7/9tu655x4dcsgh+u1vf6vOzs4VajFNU4lEYp1qAQAA6A3MiAAAAIAk6YrbH/U1hHBdT4/87SVN/3D9TwICwPqwbVsTJkzQa6+9pmw2u96PM23aNO28884KBP5/m7vdd99ds2fPXqu2TMurVCrq7OxULBbTHnvsoeeee2612xuGoWAw2KwlHo/3WEWx2267yTAMvf322+tcCwAAwIZgRQQAAAAkSS+/PUv3PvlPHX3gXgqsQ1/13uC6njpzRf307ifWYmsA2DBtbW2ybVuRSERjx47VxIkTlUwmNXny5NXuN3LkSIXDYbW3t8u2bY0ZM0aS9OGHH6per+vpp5/WiSeeqAsvvFD33nuvttpqKx1++OG6+eabm49hWVZz9YVlWdpoo400ZswYlcvl5oqOPfbYQ5I0Z84cjRo1Sqeffrpmz56tJ598UpIUCoV03HHH6bnnnlNnZ6eSyaQOO+wwDR8+XP/4xz+kpTMvnn/+eZ1//vm69tprZVmWzj77bD3zzDNasmRJHz2zAAAAK0cQAQAAgKbrf/sX7b/7OA1vT8gK9N/g6kDA1HdvfpDZEAD6xT333CPP81Qul7VgwQK99NJLuv/++1cY7ry8Cy64QB0d/3+ezi9/+UtJ0sSJE7Vo0SIVi0VdcMEFOuecc3Tbbbcpm83qV7/6lR5//PHmPsOGDWvu173vxIkT9eqrr+q8886TJEWjUZ166qkaPny48vm8pkyZottvv12u60qSXNfV6NGj9f3vf1/JZFK5XE7vvvuuzj77bM2aNav52D/60Y90zjnn6JprrpHneXr22Wd1/fXX9+IzCQAAsHaMjo4O/ydxAQAAoGVsvdnGuuuHX1MkZPdbq6brfv0n3f3os/1yLAAAAABA/2JGBAAAAHqYOfcjnf7DO1SqVFVfevVtX7r1gacJIQAAAABgECOIAAAAwAremjlPJ0++TR+nC3Jdr9cfv+66cl1PV935uG574G+9/vgAAAAAgNZBayYAAACsUiRk65xjD9LRB35Sruf1yhBrz2to5tyPdOkND2j6hwt6pU4AAAAAQOsiiAAAAMAa7bHj1rrkq4dqy5HDVa+7sqx1G2TteZ5kGOrqqumuR5/VHY/8o1/aPgEAAAAA/EcQAQAAgLW2+45b6ZgD99IBe+6ggGmqXndlmobMlayUqNVdWQFThmFo5tyPdO+T/9ITz76qUqXqS+0AAAAAAH8QRAAAAGCdtSej2nns5tph61HaYetR2mbzEQrZQZmmoWqtro86c3pjxly9PXOepr0/VzNmL/K7ZAAAAACATwgiAAAAAAAAAABAn9nwaYMAAAAAAAAAAACrQBABAAAAAAAAAAD6DEEEAAAAAAAAAADoMwQRAAAAAAAAAACgzxBEAAAAAAAAAACAPkMQAQAAAAAAAAAA+ozldwEAAABYdxdddJFisZi+853v9Lh9l1120XXXXacJEyaoWCw2P5ckz/NUKpW0YMECvfzyy3rggQfU2dm52uOcddZZGj9+vLbcckvNnj1bX/3qV1c43lFHHaXtt99ekUhE8+bN03333ae//vWvzW0OPPBATZo0qcd+1WpVBx54YI+v56CDDuqxzQsvvKCLLrqo+fm2226r0047Tdtvv71c19Wzzz6rm266SZVKpbnNM888s8LX8IMf/GCltwMAAAAA+gdBBAAAwBBw/PHHq1gsKhqNatttt9XEiRN18MEH67zzztMHH3yw2n2ffPJJjRs3TltvvfUK940fP17vv/++7r33XqXTae29996aNGmSCoWC/v3vfze3KxQKOuGEE1Z7nOeff15XXnll8/Nardb8/2HDhuknP/mJnnnmGV1//fWKRCI688wzNWnSJH3ve9/r8ThXXHGFXnjhhR7HBgAAAAD4hyACAABgCEin0yoWi0qn05o7d66ee+45/eIXv9B5552ns88+e5X73XDDDZKkZDK50iDiN7/5TY/PH3roIe2+++7ab7/9egQR3TWsTq1WW+U2e++9t+r1un72s5+p0WhIkn7605/qjjvu0MiRIzV//vzmtoVCYY3HAgAAAAD0H2ZEAAAADEHValWPPvqodtppJ6VSqV597Gg0qlwu1+O2cDise++9V/fdd58uu+wybbnllivs19HRoYcfflh33323zj33XCUSieZ9wWBQ9Xq9GUJIUldXlyRpp5126vE455xzjn7/+9/r5ptv1sEHH9yrXxsAAAAAYN2xIgIAAGCA2nvvvfXEE0/0uM001/46k9mzZ0uSNtlkE2UymV6paf/999d2222nn/70p83b5syZo6uuukrvv/++YrGYjj76aN1www06+eST9fHHH0tL50E8++yzWrBggUaOHKlTTz1VV1xxhc4880x5nqepU6fq61//uo455hg99NBDCoVCOu2006SlbZu63XHHHZo6daoqlYp23313nXvuuQqHw3r44Yd75esDAAAAAKw7gggAAIABaurUqbr22mt73LbDDjvo0ksvXav9DcOQpB6rDDZER0eHLrzwQl1zzTWaNWtW8/a33npLb731VvPzN998U3fffbe++MUv6s4775SWGzL9wQcfaObMmfrtb3+rjo4OvfLKK5o1a5auuOIKff3rX9dXv/pVua6rhx9+WJ2dnfI8r7nvPffc0/z/9957T+FwWMcccwxBBAAAAAD4iCACAABggKpUKj1mI0jS8OHD13r/LbbYQpK0cOHCDa5ll1120Y9//GPdfPPN+stf/rLabV3X1YwZMzRq1KhVbrNgwQJlMhmNGjVKr7zyiiTp6aef1tNPP622tjaVy2VJ0lFHHaUFCxas8nHefvttnXDCCQoGgz2GXwMAAAAA+g8zIgAAAIYg27Y1YcIEvfbaa8pmsxv0WLvssosuv/xy/fznP9fjjz++xu1N09TWW2+tzs7OVW6z0UYbKZFIaMmSJSvcl06nValUdMABB6hareqll15a5eOMGTNGuVyOEAIAAAAAfMSKCAAAgCGgra1Ntm0rEolo7NixmjhxopLJpCZPnrza/UaOHKlwOKz29nbZtq0xY8ZIkj788EPV63V1dHToxz/+sR5++GH94x//UFtbmySpXq8rn89Lkk444QS99dZbmjdvnmKxmI455hiNGDFCf/zjHyVJoVBIJ554oqZMmaLOzk6NGjVKX/va1zRv3jy9+OKLzVoOO+wwTZs2TeVyWbvvvru+9rWv6Re/+IWKxaK0dGZGW1ub3nrrLVWrVe2+++469thjdf/99/fZ8woAAAAAWDOCCAAAgCHgnnvuked5KpfLWrBggV566SXdf//9SqfTq93vggsuUEdHR/PzX/7yl5KkiRMnatGiRTrwwAMVDod17LHH6thjj21u9+qrr+q8886TJMViMZ1//vlqb29XoVDQ9OnTdeaZZ+rDDz+UJHmepzFjxujAAw9ULBbTkiVL9NJLL+mOO+7osZJh3LhxOumkkxQOhzVnzhz99Kc/1VNPPdW8v16v67DDDtM3vvENGYahefPm6ZZbblmrVRoAAAAAgL5jdHR09M50QgAAAAAAAAAAgOUwIwIAAAAAAAAAAPQZgggAAAAAAAAAANBnCCIAAAAAAAAAAECfIYgAAAAAAAAAAAB9hiACAAAAAAAAAAD0GYIIAAAAAAAAAADQZwgiAAAAAAAAAABAnyGIAAAAAAAAAAAAfYYgAgAAAAAAAAAA9BmCCAAAAAAAAAAA0GcIIgAAAAAAAAAAQJ8hiAAAAAAAAAAAAH2GIAIAAAAAAAAAAPQZgggAAAAAAAAAANBnCCIAAAAAAAAAAECfIYgAAAAAAAAAAAB9hiACAAAAAAAAAAD0GYIIAAAAAAAAAADQZwgiAAAAAAAAAABAnyGIAAAAAAAAAAAAfYYgAgAAAAAAAAAA9BmCCAAAAAAAAAAA0GcIIgAAAAAAAAAAQJ8hiAAAAAAAAAAAAH2GIAIAAAAAAAAAAPQZgggAAAAAAAAAANBnCCIAAAAAAAAAAECfIYgAAAAAAAAAAAB9hiACAAAAAAAAAAD0GYIIAAAAAAAAAADQZwgiAAAAAAAAAABAnyGIAAAAAAAAAAAAfYYgAgAAAAAAAAAA9Jn/B/+qbYibLP+YAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Here's a summary of the stars discussed along with a short description about them, sorted by distance to Earth:\n", + "\n", + "1. **Proxima Centauri**: The closest star to Earth after the Sun, located 4.25 light-years away. It's a small, low-mass star too faint to be seen with the naked eye.\n", + "\n", + "2. **Alpha Centauri (Rigil Kentaurus, Toliman, and Proxima Centauri)**: A triple star system located 4.25 light-years away. It consists of three stars: Rigil Kentaurus, Toliman, and Proxima Centauri.\n", + "\n", + "3. **Iota Centauri**: A star located approximately 51.5 light-years away. It has an apparent visual magnitude of +2.73, making it easily visible to the naked eye.\n", + "\n", + "4. **HD 113538 (Gliese 496.1)**: A star with two planetary companions located 53 light-years away. It is much too faint to be viewed with the naked eye.\n", + "\n", + "5. **HD 101930 (Gliese 3683)**: An orange-hued star with an orbiting exoplanet located 98 light-years away. It has a relatively large proper motion and is receding with a radial velocity of.\n", + "\n", + "6. **HD 114386**: A star with a pair of orbiting exoplanets located 91 light-years away. It has an apparent visual magnitude of 8.73, which means it cannot be viewed with the naked eye but can be seen with a telescope or good binoculars.\n", + "\n", + "7. **HD 125595**: A star with a close Neptunian companion located 92 light-years away. It is too faint to be viewed with the naked eye.\n", + "\n", + "8. **BPM 37093 (V886 Centauri)**: A variable white dwarf star of the DAV, or ZZ Ceti, type, located approximately light-years away. It has a hydrogen atmosphere and an unusually high mass of approximately 1.1 times the Sun's.\n" + ] + } + ], + "source": [ + "subgraph = astronomy.search(\"\"\"\n", + "MATCH P=({id: \"Centaurus\"})-[]->(B)\n", + "WHERE B.distance > 0\n", + "RETURN P\n", + "ORDER BY B.distance\n", + "LIMIT 10\n", + "\"\"\", graph=True)\n", + "\n", + "plot(subgraph)\n", + "\n", + "command = \"\"\"\n", + "Write a summary of the stars discussed along with a short description about them.\n", + "Sort by distance to Earth.\n", + "\"\"\"\n", + "\n", + "context = [subgraph.attribute(x, \"text\") for x in subgraph.scan()]\n", + "print(rag(command, context, maxlength=4096)[\"answer\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Agents\n", + "\n", + "Our last example is an Agent that searches the astronomy knowledge graph." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from txtai import Agent\n", + "\n", + "def search(query):\n", + " \"\"\"\n", + " Searches a database of astronomy data.\n", + "\n", + " Make sure to call this tool only with a string input, never use JSON. \n", + "\n", + " Args:\n", + " query: concepts to search for using similarity search\n", + " \n", + " Returns:\n", + " list of search results with for each match\n", + " \"\"\"\n", + "\n", + " return embeddings.search(\n", + " \"SELECT id, text, distance FROM txtai WHERE similar(:query)\",\n", + " 10, parameters={\"query\": query}\n", + " )\n", + "\n", + "agent = Agent(\n", + " tools=[search],\n", + " llm=llm,\n", + " max_iterations=10,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 124, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32;20;1m======== New task ========\u001b[0m\n", + "\u001b[37;1m\n", + "\n", + "Write a detailed list with explanations of 10 candidate stars that could potentially be habitable to life.\n", + "\n", + "\n", + "Do the following.\n", + " - Search for results related to the topic.\n", + " - Analyze the results\n", + " - Continue querying until conclusive answers are found\n", + " - Write a Markdown report\n", + "\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: I will need to find the list of candidate stars that could potentially be habitable to life. To do this, I will use the `search` tool to look for related results.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'search' with arguments: candidate stars for habitable planets\u001b[0m\n", + "\u001b[33;1m=== Agent thoughts:\u001b[0m\n", + "\u001b[0mThought: The results from the `search` tool provide a list of candidate stars for habitable planets. However, they are not organized in a clear and concise manner. To create a detailed list with explanations, I will need to analyze the results and identify the key points.\u001b[0m\n", + "\u001b[33;1m>>> Calling tool: 'final_answer' with arguments: {'answer': \"Here is a list of 10 candidate stars that could potentially be habitable to life:\\n\\n1. Luyten's Star (red dwarf star) - has a confirmed exoplanet, Luyten b, which is a super-Earth and receives only 6% more starlight than Earth.\\n\\n2. Gliese 273 (red dwarf star) - has a confirmed exoplanet, Luyten b, which is a super-Earth and receives only 6% more starlight than Earth.\\n\\n3. Ross 128 (red dwarf star) - has a confirmed exoplanet, Ross 128 b, which is a super-Earth and receives only 6% more starlight than Earth.\\n\\n4. Proxima Centauri (red dwarf star) - has a confirmed exoplanet, Proxima Centauri b, which is a super-Earth and receives only 6% more starlight than Earth.\\n\\n5. Kepler-560 (binary star system) - has a confirmed exoplanet, Kepler-560b, which is a super-Earth and orbits within the habitable zone of the binary star system.\\n\\n6. K2-332 (M4V star) - has a confirmed exoplanet, K2-332 b, which is a potentially habitable Super-Earth or Mini-Neptune exoplanet and receives 1.17 times the light that Earth gets from the sun.\\n\\n7. Alpha Centauri A (G-type main-sequence star) - has a candidate exoplanet, Alpha Centauri Ab, which is a giant planet and orbits at approximately 1.1 AU away from the star.\\n\\n8. Kepler-298 (orange dwarf star) - has a confirmed exoplanet, Kepler-298d, which orbits within the habitable zone of the star and may be an ocean planet with a thick gas atmosphere.\\n\\n9. LHS 1140 (M-dwarf star) - has a confirmed exoplanet, LHS 1140 b, which is a super-Earth and orbits within the habitable zone of the star.\\n\\n10. Teegarden's Star (M-dwarf star) - has a confirmed exoplanet, Teegarden's Star b, which is a super-Earth and orbits within the habitable zone of the star.\\n\\nNote: The habitability of these stars and their exoplanets is still a topic of debate and research, and more studies are needed to confirm their potential for supporting life.\"}\u001b[0m\n" + ] + }, + { + "data": { + "text/markdown": [ + "Here is a list of 10 candidate stars that could potentially be habitable to life:\n", + "\n", + "1. Luyten's Star (red dwarf star) - has a confirmed exoplanet, Luyten b, which is a super-Earth and receives only 6% more starlight than Earth.\n", + "\n", + "2. Gliese 273 (red dwarf star) - has a confirmed exoplanet, Luyten b, which is a super-Earth and receives only 6% more starlight than Earth.\n", + "\n", + "3. Ross 128 (red dwarf star) - has a confirmed exoplanet, Ross 128 b, which is a super-Earth and receives only 6% more starlight than Earth.\n", + "\n", + "4. Proxima Centauri (red dwarf star) - has a confirmed exoplanet, Proxima Centauri b, which is a super-Earth and receives only 6% more starlight than Earth.\n", + "\n", + "5. Kepler-560 (binary star system) - has a confirmed exoplanet, Kepler-560b, which is a super-Earth and orbits within the habitable zone of the binary star system.\n", + "\n", + "6. K2-332 (M4V star) - has a confirmed exoplanet, K2-332 b, which is a potentially habitable Super-Earth or Mini-Neptune exoplanet and receives 1.17 times the light that Earth gets from the sun.\n", + "\n", + "7. Alpha Centauri A (G-type main-sequence star) - has a candidate exoplanet, Alpha Centauri Ab, which is a giant planet and orbits at approximately 1.1 AU away from the star.\n", + "\n", + "8. Kepler-298 (orange dwarf star) - has a confirmed exoplanet, Kepler-298d, which orbits within the habitable zone of the star and may be an ocean planet with a thick gas atmosphere.\n", + "\n", + "9. LHS 1140 (M-dwarf star) - has a confirmed exoplanet, LHS 1140 b, which is a super-Earth and orbits within the habitable zone of the star.\n", + "\n", + "10. Teegarden's Star (M-dwarf star) - has a confirmed exoplanet, Teegarden's Star b, which is a super-Earth and orbits within the habitable zone of the star.\n", + "\n", + "Note: The habitability of these stars and their exoplanets is still a topic of debate and research, and more studies are needed to confirm their potential for supporting life." + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import display, Markdown\n", + "\n", + "def md(output):\n", + " display(Markdown(output))\n", + "\n", + "researcher = \"\"\"\n", + "{command}\n", + "\n", + "Do the following.\n", + " - Search for results related to the topic.\n", + " - Analyze the results\n", + " - Continue querying until conclusive answers are found\n", + " - Write a Markdown report\n", + "\"\"\"\n", + "\n", + "md(agent(researcher.format(command=\"\"\"\n", + "Write a detailed list with explanations of 10 candidate stars that could potentially be habitable to life.\n", + "\"\"\"), maxlength=16000))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook parsed the ⭐'s with txtai. It showed how knowledge graphs can be built to drive RAG. We also covered how to use agents to build an \"agentic\" or \"self-querying\" RAG system.\n", + "\n", + "Are we alone in the universe? No one knows for sure. Perhaps AI will figure it out for us 😀\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "local", + "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", + "version": "3.9.21" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/73_Chunking_your_data_for_RAG.ipynb b/examples/73_Chunking_your_data_for_RAG.ipynb new file mode 100644 index 0000000..cfb926d --- /dev/null +++ b/examples/73_Chunking_your_data_for_RAG.ipynb @@ -0,0 +1,275 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chunking your data for RAG\n", + "\n", + "One of the major workflows in `txtai` is Retrieval Augmented Generation (RAG). Large Language Models (LLM) are built to generate coherent sounding text. While in many cases it is factually accurate, that is not what they're built to do. RAG steps in to help inject smaller pieces of knowledge into a LLM prompt and increase the overall accuracy of responses. The `R` in RAG is very important.\n", + "\n", + "This notebook will demonstrate how to extract, chunk and index text to support retrieval operations for RAG." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline-data]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Data chunking and indexing\n", + "\n", + "Let's dive right in and keep this example simple. The next section creates a [Textractor pipeline](https://neuml.github.io/txtai/pipeline/data/textractor/) and an [Embeddings database](https://neuml.github.io/txtai/embeddings/).\n", + "\n", + "The `Textractor` extracts chunks of text from files and the `Embeddings` takes those chunks and builds an index/database. We'll use a [late chunker](https://docs.chonkie.ai/chunkers/late-chunker) backed by [Chonkie](https://github.com/chonkie-inc/chonkie).\n", + "\n", + "Then, we'll build an indexing workflow that streams chunks from two files." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from txtai import Embeddings\n", + "from txtai.pipeline import Textractor\n", + "\n", + "# Text extraction pipeline with late chunking via Chonkie\n", + "textractor = Textractor(chunker=\"late\")\n", + "embeddings = Embeddings(content=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def stream():\n", + " urls = [\"https://github.com/neuml/txtai\", \"https://arxiv.org/pdf/2005.11401\"]\n", + " for x, url in enumerate(urls):\n", + " chunks = textractor(url)\n", + " \n", + " # Add all chunks - use the same document id for each chunk\n", + " for chunk in chunks:\n", + " yield x, chunk\n", + "\n", + " # Add the document metadata with the same document id\n", + " # Can be any metadata. Can also be the entire document.\n", + " yield x, {\"url\": url}\n", + "\n", + "# Index the chunks and metadata\n", + "embeddings.index(stream())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A key element of `txtai` that is commonly misunderstood is how to best store chunks of data and join them back to the main document. `txtai` allows re-using the same logical id multiple times.\n", + "\n", + "Behind the scenes, each chunk gets it's own unique index id. The backend database stores chunks in a table called `sections` and data in a table called `documents`. This has been the case as far back as `txtai` 4.0. `txtai` also has the ability to store associated binary data in a table called `objects`. It's important to note that each associated `document` or `object` is only stored once. \n", + "\n", + "To illustrate, let's look at the first 20 rows in the embeddings database created." + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'indexid': 0, 'id': '0', 'url': 'https://github.com/neuml/txtai', 'text': '**GitHub - neuml/txtai: 💡 All-in-one open-source embeddings database for semantic search, LLM orchestration and language model workflows**\\n\\n*💡 All-in-one open-source embeddings database for semantic search, LLM orchestration and language model workflows - neuml/txtai*\\n\\n\\n\\n**All-in-one embeddings database** \\ntxtai is an all-in-one embeddings database for semantic search, LLM orchestration and language model workflows.\\n\\nEmbeddings databases are a union of vector indexes (sparse and dense), graph networks and relational databases.\\n\\nThis foundation enables vector search and/or serves as a powerful knowledge source for large language model (LLM) applications.\\n\\nBuild autonomous agents, retrieval augmented generation (RAG) processes, multi-model workflows and more.\\n\\nSummary of txtai features:\\n\\n- 🔎 Vector search with SQL, object storage, topic modeling, graph analysis and multimodal indexing\\n- 📄 Create embeddings for text, documents, audio, images and video\\n- 💡 Pipelines powered by language models that run LLM prompts, question-answering, labeling, transcription, translation, summarization and more\\n- ↪️️ Workflows to join pipelines together and aggregate business logic. txtai processes can be simple microservices or multi-model workflows.\\n- 🤖 Agents that intelligently connect embeddings, pipelines, workflows and other agents together to autonomously solve complex problems\\n- ⚙️ Build with Python or YAML. API bindings available for [JavaScript](https://github.com/neuml/txtai.js) , [Java](https://github.com/neuml/txtai.java) , [Rust](https://github.com/neuml/txtai.rs) and [Go](https://github.com/neuml/txtai.go) .\\n- 🔋 Batteries included with defaults to get up and running fast\\n- ☁️ Run local or scale out with container orchestration\\ntxtai is built with Python 3.10+, [Hugging Face Transformers](https://github.com/huggingface/transformers) , [Sentence Transformers](https://github.'}\n", + "{'indexid': 1, 'id': '0', 'url': 'https://github.com/neuml/txtai', 'text': 'com/UKPLab/sentence-transformers) and [FastAPI](https://github.com/tiangolo/fastapi) . txtai is open-source under an Apache 2.0 license.\\n\\n*Interested in an easy and secure way to run hosted txtai applications? Then join the* [txtai.cloud](https://txtai.cloud) *preview to learn more.* \\n\\n## Why txtai?\\nNew vector databases, LLM frameworks and everything in between are sprouting up daily. Why build with txtai?\\n\\n- Up and running in minutes with [pip](https://neuml.github.io/txtai/install/) or [Docker](https://neuml.github.io/txtai/cloud/) \\n```\\n# Get started in a couple lines\\nimport txtai\\n\\nembeddings = txtai.Embeddings()\\nembeddings.index([\"Correct\", \"Not what we hoped\"])\\nembeddings.search(\"positive\", 1)\\n#[(0, 0.29862046241760254)]\\n```\\n\\n- Built-in API makes it easy to develop applications using your programming language of choice\\n```\\n# app.yml\\nembeddings:\\n path: sentence-transformers/all-MiniLM-L6-v2\\n```\\n\\n```\\nCONFIG=app.yml uvicorn \"txtai.api:app\"\\ncurl -X GET \"http://localhost:8000/search?query=positive\"\\n```\\n\\n- Run local - no need to ship data off to disparate remote services\\n- Work with micromodels all the way up to large language models (LLMs)\\n- Low footprint - install additional dependencies and scale up when needed\\n- [Learn by example](https://neuml.github.io/txtai/examples) - notebooks cover all available functionality\\n\\n## Use Cases\\nThe following sections introduce common txtai use cases. A comprehensive set of over 60 [example notebooks and applications](https://neuml.github.io/txtai/examples) are also available.\\n\\n\\n### Semantic Search\\nBuild semantic/similarity/vector/neural search applications.'}\n", + "{'indexid': 2, 'id': '0', 'url': 'https://github.com/neuml/txtai', 'text': 'Traditional search systems use keywords to find data. Semantic search has an understanding of natural language and identifies results that have the same meaning, not necessarily the same keywords.\\n\\nGet started with the following examples.\\n\\n|Notebook|Description||\\n|---|---|---|\\n|[Introducing txtai](https://github.com/neuml/txtai/blob/master/examples/01_Introducing_txtai.ipynb) |Overview of the functionality provided by txtai||\\n|[Similarity search with images](https://github.com/neuml/txtai/blob/master/examples/13_Similarity_search_with_images.ipynb) |Embed images and text into the same space for search||\\n|[Build a QA database](https://github.com/neuml/txtai/blob/master/examples/34_Build_a_QA_database.ipynb) |Question matching with semantic search||\\n|[Semantic Graphs](https://github.com/neuml/txtai/blob/master/examples/38_Introducing_the_Semantic_Graph.ipynb) |Explore topics, data connectivity and run network analysis||\\n\\n### LLM Orchestration\\nAutonomous agents, retrieval augmented generation (RAG), chat with your data, pipelines and workflows that interface with large language models (LLMs).\\n\\nSee below to learn more.\\n\\n|Notebook|Description||\\n|---|---|---|\\n|[Prompt templates and task chains](https://github.com/neuml/txtai/blob/master/examples/44_Prompt_templates_and_task_chains.ipynb) |Build model prompts and connect tasks together with workflows||\\n|[Integrate LLM frameworks](https://github.com/neuml/txtai/blob/master/examples/53_Integrate_LLM_Frameworks.ipynb) |Integrate llama.cpp, LiteLLM and custom generation frameworks||\\n|[Build knowledge graphs with LLMs](https://github.'}\n", + "{'indexid': 3, 'id': '0', 'url': 'https://github.com/neuml/txtai', 'text': 'com/neuml/txtai/blob/master/examples/57_Build_knowledge_graphs_with_LLM_driven_entity_extraction.ipynb) |Build knowledge graphs with LLM-driven entity extraction||\\n\\n#### Agents\\nAgents connect embeddings, pipelines, workflows and other agents together to autonomously solve complex problems.\\n\\ntxtai agents are built on top of the Transformers Agent framework. This supports all LLMs txtai supports (Hugging Face, llama.cpp, OpenAI / Claude / AWS Bedrock via LiteLLM).\\n\\nSee the link below to learn more.\\n\\n|Notebook|Description||\\n|---|---|---|\\n|[Analyzing Hugging Face Posts with Graphs and Agents](https://github.com/neuml/txtai/blob/master/examples/68_Analyzing_Hugging_Face_Posts_with_Graphs_and_Agents.ipynb) |Explore a rich dataset with Graph Analysis and Agents||\\n|[Granting autonomy to agents](https://github.com/neuml/txtai/blob/master/examples/69_Granting_autonomy_to_agents.ipynb) |Agents that iteratively solve problems as they see fit||\\n|[Analyzing LinkedIn Company Posts with Graphs and Agents](https://github.com/neuml/txtai/blob/master/examples/71_Analyzing_LinkedIn_Company_Posts_with_Graphs_and_Agents.ipynb) |Exploring how to improve social media engagement with AI||\\n\\n#### Retrieval augmented generation\\nRetrieval augmented generation (RAG) reduces the risk of LLM hallucinations by constraining the output with a knowledge base as context. RAG is commonly used to \"chat with your data\".\\n\\nA novel feature of txtai is that it can provide both an answer and source citation.\\n\\n|Notebook|Description||\\n|---|---|---|\\n|[Build RAG pipelines with txtai](https://github.'}\n", + "{'indexid': 4, 'id': '0', 'url': 'https://github.com/neuml/txtai', 'text': 'com/neuml/txtai/blob/master/examples/52_Build_RAG_pipelines_with_txtai.ipynb) |Guide on retrieval augmented generation including how to create citations||\\n|[How RAG with txtai works](https://github.com/neuml/txtai/blob/master/examples/63_How_RAG_with_txtai_works.ipynb) |Create RAG processes, API services and Docker instances||\\n|[Advanced RAG with graph path traversal](https://github.com/neuml/txtai/blob/master/examples/58_Advanced_RAG_with_graph_path_traversal.ipynb) |Graph path traversal to collect complex sets of data for advanced RAG||\\n|[Speech to Speech RAG](https://github.com/neuml/txtai/blob/master/examples/65_Speech_to_Speech_RAG.ipynb) |Full cycle speech to speech workflow with RAG||\\n\\n### Language Model Workflows\\nLanguage model workflows, also known as semantic workflows, connect language models together to build intelligent applications.\\n\\nWhile LLMs are powerful, there are plenty of smaller, more specialized models that work better and faster for specific tasks. This includes models for extractive question-answering, automatic summarization, text-to-speech, transcription and translation.\\n\\n|Notebook|Description||\\n|---|---|---|\\n|[Run pipeline workflows](https://github.com/neuml/txtai/blob/master/examples/14_Run_pipeline_workflows.ipynb) |Simple yet powerful constructs to efficiently process data||\\n|[Building abstractive text summaries](https://github.com/neuml/txtai/blob/master/examples/09_Building_abstractive_text_summaries.ipynb) |Run abstractive text summarization||\\n|[Transcribe audio to text](https://github.com/neuml/txtai/blob/master/examples/11_Transcribe_audio_to_text.'}\n", + "{'indexid': 5, 'id': '0', 'url': 'https://github.com/neuml/txtai', 'text': 'ipynb) |Convert audio files to text||\\n|[Translate text between languages](https://github.com/neuml/txtai/blob/master/examples/12_Translate_text_between_languages.ipynb) |Streamline machine translation and language detection||\\n\\n## Installation\\nThe easiest way to install is via pip and PyPI\\n\\n```\\npip install txtai\\n```\\n\\nPython 3.10+ is supported. Using a Python [virtual environment](https://docs.python.org/3/library/venv.html) is recommended.\\n\\nSee the detailed [install instructions](https://neuml.github.io/txtai/install) for more information covering [optional dependencies](https://neuml.github.io/txtai/install/#optional-dependencies) , [environment specific prerequisites](https://neuml.github.io/txtai/install/#environment-specific-prerequisites) , [installing from source](https://neuml.github.io/txtai/install/#install-from-source) , [conda support](https://neuml.github.io/txtai/install/#conda) and how to [run with containers](https://neuml.github.io/txtai/cloud) .\\n\\n\\n## Model guide\\nSee the table below for the current recommended models. These models all allow commercial use and offer a blend of speed and performance.\\n\\n|Component|Model(s)|\\n|---|---|\\n|[Embeddings](https://neuml.github.io/txtai/embeddings) |[all-MiniLM-L6-v2](https://hf.co/sentence-transformers/all-MiniLM-L6-v2) |\\n|[Image Captions](https://neuml.github.io/txtai/pipeline/image/caption) |[BLIP](https://hf.co/Salesforce/blip-image-captioning-base) |'}\n", + "{'indexid': 6, 'id': '0', 'url': 'https://github.com/neuml/txtai', 'text': '|[Labels - Zero Shot](https://neuml.github.io/txtai/pipeline/text/labels) |[BART-Large-MNLI](https://hf.co/facebook/bart-large) |\\n|[Labels - Fixed](https://neuml.github.io/txtai/pipeline/text/labels) |Fine-tune with [training pipeline](https://neuml.github.io/txtai/pipeline/train/trainer) |\\n|[Large Language Model (LLM)](https://neuml.github.io/txtai/pipeline/text/llm) |[Llama 3.1 Instruct](https://hf.co/meta-llama/Llama-3.1-8B-Instruct) |\\n|[Summarization](https://neuml.github.io/txtai/pipeline/text/summary) |[DistilBART](https://hf.co/sshleifer/distilbart-cnn-12-6) |\\n|[Text-to-Speech](https://neuml.github.io/txtai/pipeline/audio/texttospeech) |[ESPnet JETS](https://hf.co/NeuML/ljspeech-jets-onnx) |\\n|[Transcription](https://neuml.github.io/txtai/pipeline/audio/transcription) |[Whisper](https://hf.co/openai/whisper-base) |\\n|[Translation](https://neuml.github.io/txtai/pipeline/text/translation) |[OPUS Model Series](https://hf.co/Helsinki-NLP) |\\nModels can be loaded as either a path from the Hugging Face Hub or a local directory. Model paths are optional, defaults are loaded when not specified. For tasks with no recommended model, txtai uses the default models as shown in the Hugging Face Tasks guide.\\n\\nSee the following links to learn more.\\n\\n\\n## Powered by txtai\\nThe following applications are powered by txtai.'}\n", + "{'indexid': 7, 'id': '0', 'url': 'https://github.com/neuml/txtai', 'text': \"|Application|Description|\\n|---|---|\\n|[rag](https://github.com/neuml/rag) |Retrieval Augmented Generation (RAG) application|\\n|[ragdata](https://github.com/neuml/ragdata) |Build knowledge bases for RAG|\\n|[paperai](https://github.com/neuml/paperai) |Semantic search and workflows for medical/scientific papers|\\n|[annotateai](https://github.com/neuml/annotateai) |Automatically annotate papers with LLMs|\\nIn addition to this list, there are also many other [open-source projects](https://github.com/neuml/txtai/network/dependents) , [published research](https://scholar.google.com/scholar?q=txtai&hl=en&as_ylo=2022) and closed proprietary/commercial projects that have built on txtai in production.\\n\\n\\n## Further Reading\\n- [Tutorial series on Hashnode](https://neuml.hashnode.dev/series/txtai-tutorial) | [dev.to](https://dev.to/neuml/tutorial-series-on-txtai-ibg) \\n- [What's new in txtai 8.0](https://medium.com/neuml/whats-new-in-txtai-8-0-2d7d0ab4506b) | [7.0](https://medium.com/neuml/whats-new-in-txtai-7-0-855ad6a55440) | [6.0](https://medium.com/neuml/whats-new-in-txtai-6-0-7d93eeedf804) | [5.0](https://medium.com/neuml/whats-new-in-txtai-5-0-e5c75a13b101) | [4.0](https://medium.\"}\n", + "{'indexid': 8, 'id': '0', 'url': 'https://github.com/neuml/txtai', 'text': 'com/neuml/whats-new-in-txtai-4-0-bbc3a65c3d1c) \\n- [Getting started with semantic search](https://medium.com/neuml/getting-started-with-semantic-search-a9fd9d8a48cf) | [workflows](https://medium.com/neuml/getting-started-with-semantic-workflows-2fefda6165d9) | [rag](https://medium.com/neuml/getting-started-with-rag-9a0cca75f748) \\n\\n## Documentation\\n[Full documentation on txtai](https://neuml.github.io/txtai) including configuration settings for embeddings, pipelines, workflows, API and a FAQ with common questions/issues is available.\\n\\n\\n## Contributing\\nFor those who would like to contribute to txtai, please see [this guide](https://github.com/neuml/.github/blob/master/CONTRIBUTING.md) .'}\n", + "{'indexid': 9, 'id': '1', 'url': 'https://arxiv.org/pdf/2005.11401', 'text': 'Retrieval-Augmented Generation for\\nKnowledge-Intensive NLP Tasks\\n\\nPatrick Lewis†‡, Ethan Perez?,\\n\\nAleksandra Piktus†, Fabio Petroni†, Vladimir Karpukhin†, Naman Goyal†, Heinrich Küttler†,\\n\\nMike Lewis†, Wen-tau Yih†, Tim Rocktäschel†‡, Sebastian Riedel†‡, Douwe Kiela†\\n\\n†Facebook AI Research; ‡University College London; ?New York University;\\nplewis@fb.com\\n\\nAbstract\\n\\nLarge pre-trained language models have been shown to store factual knowledge\\nin their parameters, and achieve state-of-the-art results when fine-tuned on down-\\nstream NLP tasks. However, their ability to access and precisely manipulate knowl-\\nedge is still limited, and hence on knowledge-intensive tasks, their performance\\nlags behind task-specific architectures. Additionally, providing provenance for their\\ndecisions and updating their world knowledge remain open research problems. Pre-\\ntrained models with a differentiable access mechanism to explicit non-parametric\\nmemory have so far been only investigated for extractive downstream tasks. We\\nexplore a general-purpose fine-tuning recipe for retrieval-augmented generation\\n(RAG) — models which combine pre-trained parametric and non-parametric mem-\\nory for language generation. We introduce RAG models where the parametric\\nmemory is a pre-trained seq2seq model and the non-parametric memory is a dense\\nvector index of Wikipedia, accessed with a pre-trained neural retriever. We com-\\npare two RAG formulations, one which conditions on the same retrieved passages\\nacross the whole generated sequence, and another which can use different passages\\nper token. We fine-tune and evaluate our models on a wide range of knowledge-\\nintensive NLP tasks and set the state of the art on three open domain QA tasks,\\noutperforming parametric seq2seq models and task-specific retrieve-and-extract\\narchitectures. For language generation tasks, we find that RAG models generate\\nmore specific, diverse and factual language than a state-of-the-art parametric-only\\nseq2seq baseline.\\n\\n1 Introduction\\n\\nPre-trained neural language models have been shown to learn a substantial amount of in-depth knowl-\\nedge from data [47]. They can do so without any access to an external memory, as a parameterized\\nimplicit knowledge base [51, 52].'}\n", + "{'indexid': 10, 'id': '1', 'url': 'https://arxiv.org/pdf/2005.11401', 'text': 'While this development is exciting, such models do have down-\\nsides: They cannot easily expand or revise their memory, can’t straightforwardly provide insight into\\ntheir predictions, and may produce “hallucinations” [38]. Hybrid models that combine parametric\\nmemory with non-parametric (i.e., retrieval-based) memories [20, 26, 48] can address some of these\\nissues because knowledge can be directly revised and expanded, and accessed knowledge can be\\ninspected and interpreted. REALM [20] and ORQA [31], two recently introduced models that\\ncombine masked language models [8] with a differentiable retriever, have shown promising results,\\n\\nar\\nX\\n\\niv\\n:2\\n\\n00\\n5.\\n\\n11\\n40\\n\\n1v\\n4 \\n\\n [\\ncs\\n\\n.C\\nL\\n\\n] \\n 1\\n\\n2 \\nA\\n\\npr\\n 2\\n\\n02\\n1\\n\\n\\nThe\\tDivine\\nComedy\\t(x) q\\n\\nQuery\\nEncoder\\n\\nq(x)\\n\\nMIPS pθ\\n\\nGenerator\\xa0pθ\\n(Parametric)\\n\\nMargin-\\nalize\\n\\nThis\\t14th\\tcentury\\twork\\nis\\tdivided\\tinto\\t3\\nsections:\\t\"Inferno\",\\n\"Purgatorio\"\\t&\\n\"Paradiso\"\\t\\t\\t\\t\\t\\t\\t\\t\\t(y)\\n\\nEnd-to-End Backprop through q and\\xa0pθ\\n\\nBarack\\tObama\\twas\\nborn\\tin\\tHawaii.(x)\\n\\nFact Verification: Fact Query\\n\\nsupports\\t(y)\\n\\nQuestion Generation\\n\\nFact Verification:\\nLabel Generation\\n\\nDocument\\nIndex\\n\\nDefine\\t\"middle\\tear\"(x)\\n\\nQuestion Answering:\\nQuestion Query\\n\\nThe\\tmiddle\\tear\\tincludes\\nthe\\ttympanic\\tcavity\\tand\\nthe\\tthree\\tossicles.\\t\\t(y)\\n\\nQuestion Answering:\\nAnswer GenerationRetriever pη\\n\\n(Non-Parametric)\\nz4\\n\\nz3\\nz2\\n\\nz1\\n\\nd(z)\\n\\nJeopardy Question\\nGeneration:\\n\\nAnswer Query\\n\\nFigure 1: Overview of our approach. We combine a pre-trained retriever (Query Encoder + Document\\nIndex) with a pre-trained seq2seq model (Generator) and fine-tune end-to-end. For query x, we use\\nMaximum Inner Product Search (MIPS) to find the top-K documents zi. For final prediction y, we\\ntreat z as a latent variable and marginalize over seq2seq predictions given different documents.\\n\\nbut have only explored open-domain extractive question answering. Here, we bring hybrid parametric\\nand non-parametric memory to the “workhorse of NLP,” i.e. sequence-to-sequence (seq2seq) models.\\n\\nWe endow pre-trained, parametric-memory generation models with a non-parametric memory through'}\n", + "{'indexid': 11, 'id': '1', 'url': 'https://arxiv.org/pdf/2005.11401', 'text': 'a general-purpose fine-tuning approach which we refer to as retrieval-augmented generation (RAG).\\nWe build RAG models where the parametric memory is a pre-trained seq2seq transformer, and the\\nnon-parametric memory is a dense vector index of Wikipedia, accessed with a pre-trained neural\\nretriever. We combine these components in a probabilistic model trained end-to-end (Fig. 1). The\\nretriever (Dense Passage Retriever [26], henceforth DPR) provides latent documents conditioned on\\nthe input, and the seq2seq model (BART [32]) then conditions on these latent documents together with\\nthe input to generate the output. We marginalize the latent documents with a top-K approximation,\\neither on a per-output basis (assuming the same document is responsible for all tokens) or a per-token\\nbasis (where different documents are responsible for different tokens). Like T5 [51] or BART, RAG\\ncan be fine-tuned on any seq2seq task, whereby both the generator and retriever are jointly learned.\\n\\nThere has been extensive previous work proposing architectures to enrich systems with non-parametric\\nmemory which are trained from scratch for specific tasks, e.g. memory networks [64, 55], stack-\\naugmented networks [25] and memory layers [30]. In contrast, we explore a setting where both\\nparametric and non-parametric memory components are pre-trained and pre-loaded with extensive\\nknowledge. Crucially, by using pre-trained access mechanisms, the ability to access knowledge is\\npresent without additional training.\\n\\nOur results highlight the benefits of combining parametric and non-parametric memory with genera-\\ntion for knowledge-intensive tasks—tasks that humans could not reasonably be expected to perform\\nwithout access to an external knowledge source. Our RAG models achieve state-of-the-art results\\non open Natural Questions [29], WebQuestions [3] and CuratedTrec [2] and strongly outperform\\nrecent approaches that use specialised pre-training objectives on TriviaQA [24]. Despite these being'}\n", + "{'indexid': 12, 'id': '1', 'url': 'https://arxiv.org/pdf/2005.11401', 'text': 'extractive tasks, we find that unconstrained generation outperforms previous extractive approaches.\\nFor knowledge-intensive generation, we experiment with MS-MARCO [1] and Jeopardy question\\ngeneration, and we find that our models generate responses that are more factual, specific, and\\ndiverse than a BART baseline. For FEVER [56] fact verification, we achieve results within 4.3% of\\nstate-of-the-art pipeline models which use strong retrieval supervision. Finally, we demonstrate that\\nthe non-parametric memory can be replaced to update the models’ knowledge as the world changes.1\\n\\n2 Methods\\n\\nWe explore RAG models, which use the input sequence x to retrieve text documents z and use them\\nas additional context when generating the target sequence y. As shown in Figure 1, our models\\nleverage two components: (i) a retriever pη(z|x) with parameters η that returns (top-K truncated)\\ndistributions over text passages given a query x and (ii) a generator pθ(yi|x, z, y1:i−1) parametrized\\n\\n1Code to run experiments with RAG has been open-sourced as part of the HuggingFace Transform-\\ners Library [66] and can be found at https://github.com/huggingface/transformers/blob/master/\\nexamples/rag/. An interactive demo of RAG models can be found at https://huggingface.co/rag/\\n\\n2\\n\\n[https://github.com/huggingface/transformers/blob/master/examples/rag/](https://github.com/huggingface/transformers/blob/master/examples/rag/) \\n[https://github.com/huggingface/transformers/blob/master/examples/rag/](https://github.com/huggingface/transformers/blob/master/examples/rag/) \\n[https://huggingface.co/rag/](https://huggingface.co/rag/) \\n\\nby θ that generates a current token based on a context of the previous i− 1 tokens y1:i−1, the original\\ninput x and a retrieved passage z.\\n\\nTo train the retriever and generator end-to-end, we treat the retrieved document as a latent variable.\\nWe propose two models that marginalize over the latent documents in different ways to produce a'}\n", + "{'indexid': 13, 'id': '1', 'url': 'https://arxiv.org/pdf/2005.11401', 'text': 'distribution over generated text. In one approach, RAG-Sequence, the model uses the same document\\nto predict each target token. The second approach, RAG-Token, can predict each target token based\\non a different document. In the following, we formally introduce both models and then describe the\\npη and pθ components, as well as the training and decoding procedure.\\n\\n2.1 Models\\n\\nRAG-Sequence Model The RAG-Sequence model uses the same retrieved document to generate\\nthe complete sequence. Technically, it treats the retrieved document as a single latent variable that\\nis marginalized to get the seq2seq probability p(y|x) via a top-K approximation. Concretely, the\\ntop K documents are retrieved using the retriever, and the generator produces the output sequence\\nprobability for each document, which are then marginalized,\\n\\npRAG-Sequence(y|x) ≈\\n∑\\n\\nz∈top-k(p(·|x))\\n\\npη(z|x)pθ(y|x, z) =\\n∑\\n\\nz∈top-k(p(·|x))\\n\\npη(z|x)\\nN∏\\ni\\n\\npθ(yi|x, z, y1:i−1)\\n\\nRAG-Token Model In the RAG-Token model we can draw a different latent document for each\\ntarget token and marginalize accordingly. This allows the generator to choose content from several\\ndocuments when producing an answer. Concretely, the top K documents are retrieved using the\\nretriever, and then the generator produces a distribution for the next output token for each document,\\nbefore marginalizing, and repeating the process with the following output token, Formally, we define:\\n\\npRAG-Token(y|x) ≈\\nN∏\\ni\\n\\n∑\\nz∈top-k(p(·|x))\\n\\npη(z|x)pθ(yi|x, z, y1:i−1)\\n\\nFinally, we note that RAG can be used for sequence classification tasks by considering the target class\\nas a target sequence of length one, in which case RAG-Sequence and RAG-Token are equivalent.\\n\\n2.2 Retriever: DPR\\n\\nThe retrieval component pη(z|x) is based on DPR [26]. DPR follows a bi-encoder architecture:\\n\\npη(z|x) ∝ exp\\n(\\nd(z)>q(x)\\n\\n)\\nd(z) = BERTd(z), q(x) = BERTq(x)'}\n", + "{'indexid': 14, 'id': '1', 'url': 'https://arxiv.org/pdf/2005.11401', 'text': 'where d(z) is a dense representation of a document produced by a BERTBASE document encoder [8],\\nand q(x) a query representation produced by a query encoder, also based on BERTBASE. Calculating\\ntop-k(pη(·|x)), the list of k documents z with highest prior probability pη(z|x), is a Maximum Inner\\nProduct Search (MIPS) problem, which can be approximately solved in sub-linear time [23]. We use\\na pre-trained bi-encoder from DPR to initialize our retriever and to build the document index. This\\nretriever was trained to retrieve documents which contain answers to TriviaQA [24] questions and\\nNatural Questions [29]. We refer to the document index as the non-parametric memory.\\n\\n2.3 Generator: BART\\n\\nThe generator component pθ(yi|x, z, y1:i−1) could be modelled using any encoder-decoder. We use\\nBART-large [32], a pre-trained seq2seq transformer [58] with 400M parameters. To combine the input\\nx with the retrieved content z when generating from BART, we simply concatenate them. BART was\\npre-trained using a denoising objective and a variety of different noising functions. It has obtained\\nstate-of-the-art results on a diverse set of generation tasks and outperforms comparably-sized T5\\nmodels [32]. We refer to the BART generator parameters θ as the parametric memory henceforth.\\n\\n2.4 Training\\n\\nWe jointly train the retriever and generator components without any direct supervision on what\\ndocument should be retrieved. Given a fine-tuning training corpus of input/output pairs (xj , yj), we\\n\\n3\\n\\n\\nminimize the negative marginal log-likelihood of each target,\\n∑\\nj − log p(yj |xj) using stochastic\\n\\ngradient descent with Adam [28]. Updating the document encoder BERTd during training is costly as\\nit requires the document index to be periodically updated as REALM does during pre-training [20].'}\n", + "{'indexid': 15, 'id': '1', 'url': 'https://arxiv.org/pdf/2005.11401', 'text': 'We do not find this step necessary for strong performance, and keep the document encoder (and\\nindex) fixed, only fine-tuning the query encoder BERTq and the BART generator.\\n\\n2.5 Decoding\\n\\nAt test time, RAG-Sequence and RAG-Token require different ways to approximate argmaxy p(y|x).\\n\\nRAG-Token The RAG-Token model can be seen as a standard, autoregressive seq2seq genera-\\ntor with transition probability: p′θ(yi|x, y1:i−1) =\\n\\n∑\\nz∈top-k(p(·|x)) pη(zi|x)pθ(yi|x, zi, y1:i−1) To\\n\\ndecode, we can plug p′θ(yi|x, y1:i−1) into a standard beam decoder.\\n\\nRAG-Sequence For RAG-Sequence, the likelihood p(y|x) does not break into a conventional per-\\ntoken likelihood, hence we cannot solve it with a single beam search. Instead, we run beam search for\\neach document z, scoring each hypothesis using pθ(yi|x, z, y1:i−1). This yields a set of hypotheses\\nY , some of which may not have appeared in the beams of all documents. To estimate the probability\\nof an hypothesis y we run an additional forward pass for each document z for which y does not\\nappear in the beam, multiply generator probability with pη(z|x) and then sum the probabilities across\\nbeams for the marginals. We refer to this decoding procedure as “Thorough Decoding.” For longer\\noutput sequences, |Y | can become large, requiring many forward passes. For more efficient decoding,\\nwe can make a further approximation that pθ(y|x, zi) ≈ 0 where y was not generated during beam\\nsearch from x, zi. This avoids the need to run additional forward passes once the candidate set Y has\\nbeen generated. We refer to this decoding procedure as “Fast Decoding.”\\n\\n3 Experiments\\n\\nWe experiment with RAG in a wide range of knowledge-intensive tasks. For all experiments, we use\\na single Wikipedia dump for our non-parametric knowledge source. Following Lee et al. [31] and\\nKarpukhin et al. [26], we use the December 2018 dump. Each Wikipedia article is split into disjoint'}\n", + "{'indexid': 16, 'id': '1', 'url': 'https://arxiv.org/pdf/2005.11401', 'text': '100-word chunks, to make a total of 21M documents. We use the document encoder to compute an\\nembedding for each document, and build a single MIPS index using FAISS [23] with a Hierarchical\\nNavigable Small World approximation for fast retrieval [37]. During training, we retrieve the top\\nk documents for each query. We consider k ∈ {5, 10} for training and set k for test time using dev\\ndata. We now discuss experimental details for each task.\\n\\n3.1 Open-domain Question Answering\\n\\nOpen-domain question answering (QA) is an important real-world application and common testbed\\nfor knowledge-intensive tasks [20]. We treat questions and answers as input-output text pairs (x, y)\\nand train RAG by directly minimizing the negative log-likelihood of answers. We compare RAG to\\nthe popular extractive QA paradigm [5, 7, 31, 26], where answers are extracted spans from retrieved\\ndocuments, relying primarily on non-parametric knowledge. We also compare to “Closed-Book\\nQA” approaches [52], which, like RAG, generate answers, but which do not exploit retrieval, instead\\nrelying purely on parametric knowledge. We consider four popular open-domain QA datasets: Natural\\nQuestions (NQ) [29], TriviaQA (TQA) [24]. WebQuestions (WQ) [3] and CuratedTrec (CT) [2]. As\\nCT and WQ are small, we follow DPR [26] by initializing CT and WQ models with our NQ RAG\\nmodel. We use the same train/dev/test splits as prior work [31, 26] and report Exact Match (EM)\\nscores. For TQA, to compare with T5 [52], we also evaluate on the TQA Wiki test set.\\n\\n3.2 Abstractive Question Answering\\n\\nRAG models can go beyond simple extractive QA and answer questions with free-form, abstractive\\ntext generation. To test RAG’s natural language generation (NLG) in a knowledge-intensive setting,\\nwe use the MSMARCO NLG task v2.1 [43]. The task consists of questions, ten gold passages'}\n", + "{'indexid': 17, 'id': '1', 'url': 'https://arxiv.org/pdf/2005.11401', 'text': 'retrieved from a search engine for each question, and a full sentence answer annotated from the\\nretrieved passages. We do not use the supplied passages, only the questions and answers, to treat\\n\\n4\\n\\n\\nMSMARCO as an open-domain abstractive QA task. MSMARCO has some questions that cannot be\\nanswered in a way that matches the reference answer without access to the gold passages, such as\\n“What is the weather in Volcano, CA?” so performance will be lower without using gold passages.\\nWe also note that some MSMARCO questions cannot be answered using Wikipedia alone. Here,\\nRAG can rely on parametric knowledge to generate reasonable responses.\\n\\n3.3 Jeopardy Question Generation\\n\\nTo evaluate RAG’s generation abilities in a non-QA setting, we study open-domain question gen-\\neration. Rather than use questions from standard open-domain QA tasks, which typically consist\\nof short, simple questions, we propose the more demanding task of generating Jeopardy questions.\\nJeopardy is an unusual format that consists of trying to guess an entity from a fact about that entity.\\nFor example, “The World Cup” is the answer to the question “In 1986 Mexico scored as the first\\ncountry to host this international sports competition twice.” As Jeopardy questions are precise,\\nfactual statements, generating Jeopardy questions conditioned on their answer entities constitutes a\\nchallenging knowledge-intensive generation task.\\n\\nWe use the splits from SearchQA [10], with 100K train, 14K dev, and 27K test examples. As\\nthis is a new task, we train a BART model for comparison. Following [67], we evaluate using the\\nSQuAD-tuned Q-BLEU-1 metric [42]. Q-BLEU is a variant of BLEU with a higher weight for\\nmatching entities and has higher correlation with human judgment for question generation than\\nstandard metrics. We also perform two human evaluations, one to assess generation factuality, and\\none for specificity. We define factuality as whether a statement can be corroborated by trusted external'}\n", + "{'indexid': 18, 'id': '1', 'url': 'https://arxiv.org/pdf/2005.11401', 'text': 'sources, and specificity as high mutual dependence between the input and output [33]. We follow\\nbest practice and use pairwise comparative evaluation [34]. Evaluators are shown an answer and two\\ngenerated questions, one from BART and one from RAG. They are then asked to pick one of four\\noptions—quuestion A is better, question B is better, both are good, or neither is good.\\n\\n3.4 Fact Verification\\n\\nFEVER [56] requires classifying whether a natural language claim is supported or refuted by\\nWikipedia, or whether there is not enough information to decide. The task requires retrieving\\nevidence from Wikipedia relating to the claim and then reasoning over this evidence to classify\\nwhether the claim is true, false, or unverifiable from Wikipedia alone. FEVER is a retrieval problem\\ncoupled with an challenging entailment reasoning task. It also provides an appropriate testbed for\\nexploring the RAG models’ ability to handle classification rather than generation. We map FEVER\\nclass labels (supports, refutes, or not enough info) to single output tokens and directly train with\\nclaim-class pairs. Crucially, unlike most other approaches to FEVER, we do not use supervision on\\nretrieved evidence. In many real-world applications, retrieval supervision signals aren’t available, and\\nmodels that do not require such supervision will be applicable to a wider range of tasks. We explore\\ntwo variants: the standard 3-way classification task (supports/refutes/not enough info) and the 2-way\\n(supports/refutes) task studied in Thorne and Vlachos [57]. In both cases we report label accuracy.\\n\\n4 Results\\n\\n4.1 Open-domain Question Answering\\n\\nTable 1 shows results for RAG along with state-of-the-art models. On all four open-domain QA\\ntasks, RAG sets a new state of the art (only on the T5-comparable split for TQA). RAG combines\\nthe generation flexibility of the “closed-book” (parametric only) approaches and the performance of\\n\"open-book\" retrieval-based approaches. Unlike REALM and T5+SSM, RAG enjoys strong results\\nwithout expensive, specialized “salient span masking” pre-training [20]. It is worth noting that RAG’s\\nretriever is initialized using DPR’s retriever, which uses retrieval supervision on Natural Questions\\nand TriviaQA. RAG compares favourably to the DPR QA system, which uses a BERT-based “cross-'}\n", + "{'indexid': 19, 'id': '1', 'url': 'https://arxiv.org/pdf/2005.11401', 'text': 'encoder” to re-rank documents, along with an extractive reader. RAG demonstrates that neither a\\nre-ranker nor extractive reader is necessary for state-of-the-art performance.\\n\\nThere are several advantages to generating answers even when it is possible to extract them. Docu-\\nments with clues about the answer but do not contain the answer verbatim can still contribute towards\\na correct answer being generated, which is not possible with standard extractive approaches, leading\\n\\n5\\n\\n\\nTable 1: Open-Domain QA Test Scores. For TQA,\\nleft column uses the standard test set for Open-\\nDomain QA, right column uses the TQA-Wiki\\ntest set. See Appendix D for further details.\\n\\nModel NQ TQA WQ CT\\n\\nClosed\\nBook\\n\\nT5-11B [52] 34.5 - /50.1 37.4 -\\nT5-11B+SSM[52] 36.6 - /60.5 44.7 -\\n\\nOpen\\nBook\\n\\nREALM [20] 40.4 - / - 40.7 46.8\\nDPR [26] 41.5 57.9/ - 41.1 50.6\\n\\nRAG-Token 44.1 55.2/66.1 45.5 50.0\\nRAG-Seq. 44.5 56.8/68.0 45.2 52.2\\n\\nTable 2: Generation and classification Test Scores.\\nMS-MARCO SotA is [4], FEVER-3 is [68] and\\nFEVER-2 is [57] *Uses gold context/evidence.\\nBest model without gold access underlined.\\n\\nModel Jeopardy MSMARCO FVR3 FVR2\\nB-1 QB-1 R-L B-1 Label Acc.\\n\\nSotA - - 49.8* 49.9* 76.8 92.2*\\n\\nBART 15.1 19.7 38.2 41.6 64.0 81.1\\n\\nRAG-Tok. 17.3 22.2 40.1 41.5 72.5 89.5RAG-Seq. 14.7 21.4 40.8 44.2\\n\\nto more effective marginalization over documents. Furthermore, RAG can generate correct answers\\neven when the correct answer is not in any retrieved document, achieving 11.8% accuracy in such\\ncases for NQ, where an extractive model would score 0%.\\n\\n4.2 Abstractive Question Answering'}\n" + ] + } + ], + "source": [ + "for x in embeddings.search(\"SELECT indexid, id, url, text from txtai\", 20):\n", + " print(x)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that the id/metadata is the same but the indexid and chunk text change with each row." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Retrieval\n", + "\n", + "Last thing here is to illustrate a couple retrieval operations. LLMs are great at generating answers when we properly bound the context. See the two examples below." + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "including less of emphasis on lightly editing a retrieved item, but on aggregating content from several\n", + "pieces of retrieved content, as well as learning latent retrieval, and retrieving evidence documents\n", + "rather than related training pairs. This said, RAG techniques may work well in these settings, and\n", + "could represent promising future work.\n", + "\n", + "6 Discussion\n", + "\n", + "In this work, we presented hybrid generation models with access to parametric and non-parametric\n", + "memory. We showed that our RAG models obtain state of the art results on open-domain QA. We\n", + "found that people prefer RAG’s generation over purely parametric BART, finding RAG more factual\n", + "and specific. We conducted an thorough investigation of the learned retrieval component, validating\n", + "its effectiveness, and we illustrated how the retrieval index can be hot-swapped to update the model\n", + "without requiring any retraining. In future work, it may be fruitful to investigate if the two components\n", + "can be jointly pre-trained from scratch, either with a denoising objective similar to BART or some\n", + "another objective. Our work opens up new research directions on how parametric and non-parametric\n", + "memories interact and how to most effectively combine them, showing promise in being applied to a\n", + "wide variety of NLP tasks.\n", + "\n", + "9\n", + "\n", + "\n", + "Broader Impact\n", + "\n", + "This work offers several positive societal benefits over previous work: the fact that it is more\n", + "strongly grounded in real factual knowledge (in this case Wikipedia) makes it “hallucinate” less\n", + "with generations that are more factual, and offers more control and interpretability. RAG could be\n", + "employed in a wide variety of scenarios with direct benefit to society, for example by endowing it\n", + "with a medical index and asking it open-domain questions on that topic, or by helping people be more\n", + "effective at their jobs.\n", + "\n", + "With these advantages also come potential downsides: Wikipedia, or any potential external knowledge\n", + "source, will probably never be entirely factual and completely devoid of bias. Since RAG can be\n", + "employed as a language model, similar concerns as for GPT-2 [50] are valid here, although arguably\n", + "to a lesser extent, including that it might be used to generate abuse, faked or misleading content in\n", + "the news or on social media; to impersonate others; or to automate the production of spam/phishing\n", + "content [54]. Advanced language models may also lead to the automation of various jobs in the\n", + "coming decades [16].\n" + ] + } + ], + "source": [ + "print(embeddings.search(\"What is it called when LLM generation is bounded with factually correct data?\", 1)[0][\"text\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Traditional search systems use keywords to find data. Semantic search has an understanding of natural language and identifies results that have the same meaning, not necessarily the same keywords.\n", + "\n", + "Get started with the following examples.\n", + "\n", + "|Notebook|Description||\n", + "|---|---|---|\n", + "|[Introducing txtai](https://github.com/neuml/txtai/blob/master/examples/01_Introducing_txtai.ipynb) |Overview of the functionality provided by txtai||\n", + "|[Similarity search with images](https://github.com/neuml/txtai/blob/master/examples/13_Similarity_search_with_images.ipynb) |Embed images and text into the same space for search||\n", + "|[Build a QA database](https://github.com/neuml/txtai/blob/master/examples/34_Build_a_QA_database.ipynb) |Question matching with semantic search||\n", + "|[Semantic Graphs](https://github.com/neuml/txtai/blob/master/examples/38_Introducing_the_Semantic_Graph.ipynb) |Explore topics, data connectivity and run network analysis||\n", + "\n", + "### LLM Orchestration\n", + "Autonomous agents, retrieval augmented generation (RAG), chat with your data, pipelines and workflows that interface with large language models (LLMs).\n", + "\n", + "See below to learn more.\n", + "\n", + "|Notebook|Description||\n", + "|---|---|---|\n", + "|[Prompt templates and task chains](https://github.com/neuml/txtai/blob/master/examples/44_Prompt_templates_and_task_chains.ipynb) |Build model prompts and connect tasks together with workflows||\n", + "|[Integrate LLM frameworks](https://github.com/neuml/txtai/blob/master/examples/53_Integrate_LLM_Frameworks.ipynb) |Integrate llama.cpp, LiteLLM and custom generation frameworks||\n", + "|[Build knowledge graphs with LLMs](https://github.\n" + ] + } + ], + "source": [ + "print(embeddings.search(\"Tell me about semantic search\", 1)[0][\"text\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note how both answers give more than enough information for a LLM to answer the question." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook covered how to build a retrieval system for RAG with txtai. Chunking and retrieval are key pieces of a RAG system, arguably the most important. With the commoditization of LLMs, it's going to be more and more important on how data is presented to LLMs. When given concise information, LLMs can take it from there!\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "local", + "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", + "version": "3.9.21" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/74_OpenAI_Compatible_API.ipynb b/examples/74_OpenAI_Compatible_API.ipynb new file mode 100644 index 0000000..48a1726 --- /dev/null +++ b/examples/74_OpenAI_Compatible_API.ipynb @@ -0,0 +1,553 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# OpenAI Compatible API\n", + "\n", + "`txtai` has long been able host a [FastAPI based service](https://neuml.github.io/txtai/api/). There are clients for [Python](https://github.com/neuml/txtai.py), [JavaScript](https://github.com/neuml/txtai.js), [Java](https://github.com/neuml/txtai.java), [Rust](https://github.com/neuml/txtai.rs), [Go](https://github.com/neuml/txtai.go).\n", + "\n", + "The API service also supports hosting OpenAI-compatible API endpoints. A standard OpenAI client can then be used to connect to a `txtai` service. This enables quickly trying `txtai` with a familiar-to-use client. It's also a way to do local/offline development testing using the OpenAI client.\n", + "\n", + "This notebook will walk through comprehensive examples." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Start API service\n", + "\n", + "For this notebook, we'll run `txtai` through Docker.\n", + "\n", + "Save the following to `/tmp/config/config.yml`.\n", + "\n", + "## config.yml\n", + "```yaml\n", + "# Enable OpenAI compat endpoint\n", + "openai: True\n", + "\n", + "# Load Wikipedia Embeddings index\n", + "cloud:\n", + " provider: huggingface-hub\n", + " container: neuml/txtai-wikipedia\n", + "\n", + "# LLM instance\n", + "llm:\n", + " path: llava-hf/llava-interleave-qwen-0.5b-hf\n", + "\n", + "# RAG pipeline configuration\n", + "rag:\n", + " path: Qwen/Qwen3-4B-Instruct-2507\n", + " output: flatten\n", + " system: You are a friendly assistant. You answer questions from users.\n", + " template: |\n", + " Answer the following question using only the context below. Only include information\n", + " specifically discussed.\n", + "\n", + " question: {question}\n", + " context: {context}\n", + "\n", + "# Text to Speech\n", + "texttospeech:\n", + " path: neuml/kokoro-fp16-onnx\n", + "\n", + "# Transcription\n", + "transcription:\n", + " path: distil-whisper/distil-large-v3\n", + "```\n", + "\n", + "Start Docker service.\n", + "\n", + "```\n", + "docker run -it -p 8000:8000 -v /tmp/config:/config -e CONFIG=/config/config.yml \\\n", + "--entrypoint uvicorn neuml/txtai-gpu --host 0.0.0.0 txtai.api:app\n", + "```\n", + "\n", + "Alternatively, txtai can be directly installed and run as follows:\n", + "\n", + "```\n", + "pip install txtai[all]\n", + "CONFIG=/tmp/config/config.yml uvicorn \"txtai.api:app\"\n", + "```\n", + "\n", + "The API has token-based authorization built-in. [Read more on that here](https://neuml.github.io/txtai/api/customization/#dependencies)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Run a text chat completion\n", + "\n", + "The first example will run a text chat completion. The model is a RAG pipeline - this is more sophisticated than just a simple LLM call!\n", + "\n", + "Agents, pipelines and workflows can all be run through this interface!" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The iPhone is a line of smartphones designed and marketed by Apple Inc. that uses Apple's iOS mobile operating system. The first-generation iPhone was announced by former Apple CEO Steve Jobs on January 9, 2007. \n", + "\n", + "Since then, Apple has annually released new iPhone models and iOS updates. The most recent models being the iPhone 16 and 16 Plus, and the higher-end iPhone 16 Pro and 16 Pro Max. \n", + "\n", + "More than 2.3 billion iPhones have been sold as of January 1, 2024, making Apple the largest vendor of mobile phones in 2023." + ] + } + ], + "source": [ + "from openai import OpenAI\n", + "\n", + "client = OpenAI(\n", + " base_url=\"http://localhost:8000/v1\",\n", + " api_key=\"api-key\",\n", + ")\n", + "\n", + "response = client.chat.completions.create(\n", + " messages=[{\n", + " \"role\": \"user\",\n", + " \"content\": \"Tell me about the iPhone\",\n", + " }],\n", + " model=\"rag\",\n", + " stream=True\n", + ")\n", + "\n", + "for chunk in response:\n", + " if chunk.choices:\n", + " print(chunk.choices[0].delta.content, end=\"\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As mentioned above, the model supports much of what's available in `txtai`. For example, let's run a chat completion that runs an embeddings search." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The iPhone is a line of smartphones designed and marketed by Apple Inc. that uses Apple's iOS mobile operating system. The first-generation iPhone was announced by former Apple CEO Steve Jobs on January 9, 2007. Since then, Apple has annually released new iPhone models and iOS updates. iPhone naming has followed various patterns throughout its history.\n" + ] + } + ], + "source": [ + "response = client.chat.completions.create(\n", + " messages=[{\n", + " \"role\": \"user\",\n", + " \"content\": \"Tell me about the iPhone\",\n", + " }],\n", + " model=\"embeddings\",\n", + ")\n", + "\n", + "print(response.choices[0].message.content)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Vision models\n", + "\n", + "Any supported `txtai` LLM can be run through the chat completion API. Let's run an example that describes an image." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The image shows a logo with the text \"Txtai\" in blue and green colors. The\n" + ] + } + ], + "source": [ + "response = client.chat.completions.create(\n", + " model=\"llm\",\n", + " messages = [{\n", + " \"role\": \"user\",\n", + " \"content\": [\n", + " {\"type\": \"text\", \"text\": \"What is in this image?\"},\n", + " {\"type\": \"image_url\", \"image_url\": {\n", + " \"url\": \"https://raw.githubusercontent.com/neuml/txtai/master/logo.png\",\n", + " }}\n", + " ]}\n", + " ]\n", + ")\n", + "print(response.choices[0].message.content)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Embeddings API\n", + "\n", + "Next let's generate embeddings. " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Embedding(embedding=[-0.01969079300761223, 0.024085085839033127, 0.0043829963542521, -0.027423616498708725, 0.040405914187431335, 0.017446696758270264, 0.028464825823903084, 0.000792442646343261, -0.03107883222401142, -0.024745089933276176, -0.013542148284614086, 0.039981111884117126, -0.01401221938431263, -0.011294773779809475, -0.04346214607357979, 0.015698621049523354, 0.03775031119585037, -0.009020405821502209, 0.046784739941358566, -0.017400527372956276, -0.0670166090130806, -0.05122058466076851, 0.027725063264369965, -0.023947732523083687, -0.044582683593034744, 0.04960233345627785, 0.029517438262701035, 0.05424104258418083, -0.06027599796652794, -0.035852570086717606, 0.01336587406694889, -0.008941668085753918, 0.00014064145216252655, -0.05230511724948883, -0.02150369994342327, 0.04969678074121475, -0.05967864394187927, -0.029450856149196625, -0.01113089732825756, -0.01256561279296875, -0.012282170355319977, 0.03466389700770378, -0.005313237197697163, -0.037443146109580994, -0.04366842657327652, -0.019057273864746094, -0.04015717655420303, 0.050483088940382004, -0.011932676658034325, -0.026569517329335213, -0.048395730555057526, 0.021978085860610008, 0.03273716941475868, -0.009176520630717278, -0.05367470160126686, 0.01982428878545761, -0.00373812741599977, 0.009933742694556713, 0.044389136135578156, -0.06162404641509056, 0.03372818976640701, 0.006638737395405769, 0.029836857691407204, 0.014663859270513058, 0.04531872272491455, 0.03151382878422737, -0.007935297675430775, -0.02053055912256241, -0.06477595120668411, -0.017908524721860886, -0.014721200801432133, -0.0072138686664402485, -0.03244556114077568, -0.018965184688568115, 0.04862097278237343, -0.02961636148393154, 0.005204972345381975, 0.015699708834290504, 0.05033862590789795, -0.017976371571421623, -0.05143386870622635, -0.014295309782028198, -0.018152274191379547, 0.04641849547624588, 0.007279090117663145, -0.0060980431735515594, -0.04208022356033325, 0.05402654781937599, 0.0001357585861114785, 0.044958386570215225, -0.03261513262987137, 0.02126067876815796, 0.020893605425953865, -0.007570710498839617, -0.015284491702914238, -0.011333705857396126, -0.006006874144077301, 0.03481211140751839, 0.04163122922182083, -0.0683935135602951, -0.030256368219852448, -0.024272358044981956, 0.04630593582987785, -0.05253031477332115, 0.011599390767514706, -0.034757863730192184, 0.0033751465380191803, -0.03200560435652733, -0.04386962205171585, 0.015501669608056545, -0.01703309454023838, -0.029905665665864944, -0.03208091855049133, -0.027883553877472878, 0.007325653452426195, 0.03735042363405228, 0.08069189637899399, -0.044986918568611145, 0.030896944925189018, 0.017477652058005333, -0.0063758366741240025, 0.02287706732749939, 0.016398170962929726, 0.01946086250245571, 0.012854589149355888, 0.04439576715230942, 0.04581235349178314, -0.0008034319616854191, -0.03105296567082405, -0.024504775181412697, 0.023659462109208107, 0.04492054134607315, -0.025883017107844353, -0.002515115775167942, 0.052770763635635376, 0.009667950682342052, -0.022283289581537247, -0.07817766815423965, 0.03883073106408119, -0.04804662615060806, 0.011968107894062996, -0.03163604810833931, 0.030380938202142715, -0.022775596007704735, 0.03142687678337097, -0.11540865898132324, -0.062065351754426956, 0.003252241527661681, -0.016604064032435417, 0.046795569360256195, -0.01973356492817402, 0.005612187087535858, 0.04902602732181549, -0.029760321602225304, -0.0006560107576660812, 0.02137850970029831, 0.021465344354510307, -0.030499190092086792, -0.013952907174825668, 0.015388991683721542, -0.004734670277684927, -0.02678225375711918, 0.056917935609817505, -0.0031489196699112654, -0.000562859873753041, 0.08021821081638336, 0.045039497315883636, 0.051955677568912506, -0.06851264089345932, -0.0202163215726614, -0.020257024094462395, 0.009915929287672043, 0.027132542803883553, -0.039319392293691635, -0.06750114262104034, 0.00721193291246891, 0.011379252187907696, -0.00012379158579278737, 0.021098755300045013, -0.017165066674351692, -0.06655416637659073, 0.03575438633561134, -0.0449126660823822, 0.024580610916018486, 0.0027450474444776773, -0.07029049843549728, -0.0058233728632330894, -0.0031324869487434626, -0.022562572732567787, -0.002092051785439253, -0.01972377672791481, -0.014447340741753578, 0.02001781575381756, -0.04224644973874092, 0.08794320374727249, -0.05012425035238266, -0.03000028431415558, -0.006967171560972929, -0.0206689964979887, 0.042854372411966324, 0.018307263031601906, 0.04896565154194832, 0.025682201609015465, -0.013927857391536236, -0.026135331019759178, 0.05985535308718681, -0.022972915321588516, -0.06837267428636551, 0.03858938440680504, 0.01297465804964304, -0.01869095303118229, -0.014788917265832424, 0.05812034010887146, -0.005296449176967144, -0.03188127279281616, -0.014335273765027523, 0.029694614931941032, -0.006149643566459417, 0.0199541375041008, -0.04401557520031929, 0.08680693805217743, 0.02373044192790985, -0.05719068646430969, 0.00026498522493056953, -0.047968123108148575, 0.05128588527441025, 0.08984201401472092, 0.018948959186673164, -0.019343748688697815, -0.02114059403538704, -0.000319077109452337, -0.0483400821685791, 0.02235756441950798, -0.04526951164007187, -0.016685402020812035, 0.04920167103409767, 0.0009292830363847315, 0.0066963727585971355, 0.06434790045022964, -0.07675006985664368, 0.025055741891264915, 0.039694759994745255, -0.04413995519280434, 0.053703855723142624, 0.022806784138083458, -0.02683648094534874, 0.04088520258665085, -0.02505207061767578, 0.038970883935689926, -0.011933756060898304, 0.017762111499905586, -0.052576545625925064, -0.02732933685183525, 0.024120833724737167, -0.011316879652440548, -0.04519795626401901, 0.012005027383565903, 0.016074027866125107, -0.019522851333022118, 0.07912492007017136, -0.010790158063173294, 0.003584112972021103, -0.018683504313230515, -0.03872854635119438, -0.0293426550924778, -0.028616394847631454, 0.0034447587095201015, 0.008824280463159084, 0.0267381239682436, -0.014405295252799988, 0.01340708788484335, 0.022090492770075798, 0.041456740349531174, 0.01306570041924715, 0.012696513906121254, -0.05636722221970558, 0.05526677146553993, 0.014159836806356907, -0.05075988918542862, -0.03631533309817314, 0.04115152731537819, 0.06140957027673721, -0.06539256870746613, -0.01610933430492878, 0.08129005879163742, -0.054096464067697525, 0.021539339795708656, -0.009134260006248951, 0.04177645593881607, 0.026524635031819344, 0.016892578452825546, 0.037963252514600754, -0.06906059384346008, 0.050708942115306854, 0.06792867928743362, -0.0004703162703663111, 0.018694648519158363, -0.031178174540400505, -0.03567223250865936, -0.035771071910858154, 0.05392008647322655, 0.06253240257501602, -0.020289720967411995, 0.034436099231243134, 0.03414503112435341, 0.0034774248488247395, -0.04452746734023094, -0.03509671986103058, -0.10872040688991547, 0.016063231974840164, 0.047865595668554306, -0.04542273283004761, 0.014507413841784, 0.0009427995537407696, -0.0031647789292037487, -0.0013884446816518903, -0.045522164553403854, 0.031990133225917816, -0.07940599322319031, -0.0021216440945863724, -0.003062204457819462, 0.0284376610070467, 0.038331907242536545, -0.021678920835256577, 0.010201317258179188, -0.01604599319398403, 0.06507452577352524, 0.0687805563211441, 0.05626540631055832, -0.035019401460886, 0.013606756925582886, 0.01355750672519207, -0.0009656146867200732, 0.008775751106441021, -0.023357177153229713, -0.027274709194898605, -0.030927035957574844, -0.014168186113238335, -0.0025208715815097094, -0.06382670253515244, 0.0016783965984359384, 0.03997219353914261, -0.011281637474894524, -0.0564236082136631, 0.0001946773991221562, -0.044997114688158035, 0.006665860302746296, -0.02552937902510166, -0.0387411043047905, -0.007421125657856464, -0.018388714641332626, 0.04417712241411209, -0.03386503830552101, -0.015952911227941513, 0.0044018859043717384, -0.03185226395726204, 0.03936305642127991, -0.0018688770942389965, -0.04392078518867493, 0.02990303561091423, -0.0194404199719429, 0.05901814624667168, -0.021767310798168182, 0.032181400805711746, 0.015370846726000309, 0.031207047402858734, -0.016042204573750496, -0.016823984682559967, -0.005706059746444225, -0.03331942856311798, 0.011479238979518414, -0.043793581426143646, 0.032494351267814636, -0.06793207675218582, 0.05236655846238136, -0.031655143946409225, 0.01929832063615322, -0.0250355564057827, -0.03658934682607651, 0.04857027530670166, -0.06623365730047226, -0.04268127307295799, -0.04363507777452469, 0.044615332037210464, -0.00559930969029665, -0.03717941418290138, 0.028203044086694717, 0.00480041466653347, 0.009005775675177574, -0.01836307905614376, 0.054084815084934235, -0.017307721078395844, 0.048483166843652725, 0.023009151220321655, -7.859049219405279e-05, 0.030783794820308685, 0.043127138167619705, 0.005765000823885202, 0.008811963722109795, -0.05386245995759964, 0.004587933421134949, -0.005802399478852749, 0.0050554038025438786, 0.03453978896141052, -0.012859856709837914, -0.01060124859213829, -0.013389998115599155, -0.04355772212147713, 0.016539031639695168, -0.05041985213756561, -0.0248723067343235, 0.08495471626520157, 0.055736441165208817, -0.019743982702493668, -0.04003654792904854, 0.05553850531578064, 0.009581065736711025, -0.020963093265891075, 0.03220677375793457, -0.012795533053576946, 0.052986159920692444, -0.05288834869861603, 0.053567126393318176, 0.01575312204658985, 0.05197490379214287, -0.012308254837989807, -0.004616653546690941, 0.005736787803471088, -0.010011504404246807, 0.010513711720705032, -0.054142292588949203, -0.06452780216932297, -0.06130351126194, 0.002477638190612197, -0.022184111177921295, -0.000995964859612286, -0.05435270443558693, 0.0074448655359447, -0.023539019748568535, -0.031608957797288895, 0.0064430260099470615, -0.030367573723196983, 0.015771696344017982, -0.014180796220898628, -0.04425235465168953, 0.06702947616577148, -0.000456854235380888, 0.010592995211482048, -0.026347137987613678, -0.03434554859995842, 0.01162006612867117, -0.00362666929140687, 0.03504545986652374, 0.002880056854337454, -0.008586738258600235, -0.0005600558361038566, -0.01934652030467987, 0.05669917166233063, -0.00034789182245731354, -0.01825639232993126, -0.012466290034353733, -0.03704797849059105, -0.002550555858761072, -0.022397562861442566, 0.020881881937384605, -0.013832776807248592, 0.027578793466091156, 0.045279063284397125, -0.000525087583810091, -0.047328196465969086, -0.007053776178508997, -0.0021893021184951067, 0.0286997202783823, 0.02384152263402939, 0.006024117581546307, 0.013520568609237671, 0.026852741837501526, 0.04367787763476372, -0.02344651333987713, -0.041360042989254, -0.027980612590909004, -0.014400728978216648, -0.0577680841088295, 0.05705561116337776, -0.00984896719455719, 0.010015214793384075, 0.0763126090168953, 0.07034917175769806, 0.011689933016896248, -0.04705473780632019, -0.028127267956733704, -0.028715649619698524, 0.00838626641780138, -0.09287010133266449, -0.05999135598540306, -0.03459229692816734, -0.03452807664871216, 0.029350021854043007, 9.120464028455899e-07, 0.006535083521157503, 0.029187319800257683, -0.06986693292856216, -0.02206997573375702, -0.05103607848286629, -0.024477669969201088, 0.020876919850707054, 0.045642390847206116, 0.04098346829414368, -0.01810697466135025, -0.018912270665168762, -0.013277142308652401, 0.0213322751224041, -0.026938313618302345, -0.05354780703783035, -0.016160599887371063, 0.0029611149802803993, -0.02684030868113041, 0.04515037313103676, 0.02446618117392063, -0.02725314162671566, -0.024469705298542976, 0.021647747606039047, 0.002507369965314865, -0.04194789007306099, 0.017087087035179138, 0.0518130287528038, 0.05085260793566704, -0.07700842618942261, 0.0056351562961936, 0.060032691806554794, 0.006674149073660374, -0.05446042865514755, -0.04615267738699913, 0.024369537830352783, 0.0271424762904644, -0.012347695417702198, 0.060294460505247116, -0.016132934018969536, 0.017447318881750107, 0.05670442432165146, 0.0015670768916606903, 0.0686553418636322, -0.026241859421133995, -0.015325505286455154, -0.01770787686109543, 0.02104933187365532, -0.046040672808885574, 0.025931548327207565, 0.038434647023677826, 0.026901748031377792, 0.009451834484934807, -0.036178428679704666, -0.017309701070189476, 0.025584906339645386, 0.061249326914548874, -0.09514082968235016, 0.00902795884758234, 0.04994441568851471, -0.0038626997265964746, -0.011658617295324802, 0.008113766089081764, -0.014046317897737026, -0.011056281626224518, 0.04113991931080818, -0.033263616263866425, -0.06105482578277588, -0.031283922493457794, -0.007570411544293165, -0.0032288332004100084, 0.0004964112886227667, -0.002208895515650511, 0.024621492251753807, 0.02076159231364727, 0.009394104592502117, -0.05932564660906792, 0.020849449560046196, 0.03622383624315262, -0.05445172265172005, -0.01577809825539589, -0.016015635803341866, -0.034171875566244125, 0.07400329411029816, 0.06425172090530396, 0.013667335733771324, -0.027485249564051628, -0.06779397279024124, 0.011731340549886227, -0.021519936621189117, 0.05336484685540199, 0.09234699606895447, -0.025581158697605133, 0.04105791822075844, 0.0033194669522345066, 0.009193984791636467, 0.01257328037172556, 0.009872003458440304, 0.013460072688758373, -0.011782833375036716, 0.056569892913103104, 0.011185556650161743, -0.001791957183741033, 0.02985113114118576, 0.03551100194454193, 0.0525372251868248, 0.009313385002315044, 0.029556646943092346, 0.010092461481690407, -0.04109922796487808, -0.09827771782875061, 0.07528837770223618, 0.018835244700312614, -0.02083747275173664, -0.04701956734061241, -0.0014823883539065719, -0.003127161879092455, -0.03790943697094917, -0.05166167765855789, 0.015131598338484764, -0.005340536590665579, -0.014197085052728653, 0.05665569752454758, 0.006526419427245855, -0.02720179408788681, 0.00903793890029192, 0.07492761313915253, -0.04322653263807297, -0.05390876159071922, 0.03704892471432686, -0.027291180565953255, -0.0566035658121109, 0.015606636181473732, 0.027065519243478775, -0.0017480048118159175, 0.027853772044181824, -0.006371915340423584, 0.030529864132404327, 0.018552660942077637, 0.034608323127031326, 0.018036214634776115, 0.03474709764122963, 0.010607247240841389, 0.008939945138990879, 0.005929744802415371, -0.025183523073792458, -0.0025635838974267244, -0.0645676925778389, 0.0062942770309746265, 0.043695416301488876, 0.011311094276607037, 0.006157045252621174, -0.0021617324091494083, 0.03866882994771004, -0.058823224157094955, 0.06246255710721016, 0.0071550337597727776, 0.0022470480762422085, -0.008880370296537876, 0.03494860604405403, 0.038959626108407974, 0.04550785943865776, 0.030317384749650955, -0.00612551299855113, 0.08027740567922592, 0.0028502782806754112, -0.008108875714242458, -0.029123658314347267, -0.012007949873805046, -0.014279266819357872, -0.02980010211467743, 0.02040782757103443, 0.06390708684921265, -0.0006256934138946235, -0.03723321482539177, -0.013057096861302853, 0.04114076867699623, -0.017182866111397743, -0.05549640208482742, 0.02064032293856144, 0.01683172956109047, -0.008635859936475754, 0.03218064829707146, 0.04564550518989563, -0.021377939730882645, 0.021940747275948524, 0.020410453900694847, 0.017982320860028267, 0.02150171808898449, 0.05921953544020653, -0.042486630380153656, -0.017924992367625237, -0.0114266537129879, -0.02765769325196743, 0.02116318792104721, -0.0008785029058344662, 0.00839359499514103, 0.007519723381847143, -0.07929962873458862, 0.01306573860347271, 0.00335461413487792, -0.013990496285259724, 0.00019492211868055165, -0.017358528450131416, -0.03889889642596245, -0.008545472286641598, 0.01378809567540884, 0.06300467997789383, 0.05205303058028221, 0.029774265363812447, 0.05180739611387253, -0.04484200477600098, -0.03888325020670891, -0.056330904364585876, 0.004683728329837322, 0.016883134841918945, -0.03816996142268181, 0.01605170965194702, 0.0022271168418228626, 0.0010828975355252624, 0.038834843784570694, 0.019416887313127518, 0.00031489337561652064, 0.05024728924036026, -0.05813521891832352, -0.006695288233458996, 0.042213670909404755, -0.012247920036315918, 0.028528228402137756, -0.02632697857916355, -0.05482589080929756, 0.00981950294226408, 0.02605678141117096, 0.06638345867395401, -0.018992368131875992, 0.04858163744211197, -0.014409814961254597, -0.0310173612087965, -0.05839765444397926, 0.08313969522714615, 0.05511852726340294, 0.047723494470119476, -0.033163223415613174, -0.040427759289741516, 0.011779758147895336, -0.05743969976902008, -0.021088508889079094, -0.018184570595622063, 0.022849485278129578, -0.010282794013619423, -0.010582848452031612, -0.038172293454408646, -0.02383989654481411, -0.047329485416412354, -0.02541566826403141, 0.027357304468750954, -0.06858660280704498, -0.06362185627222061, -0.0027012284845113754, -0.035492997616529465, -0.06344638019800186, 0.03718043491244316, 0.012817914597690105, 0.018238751217722893, -0.007895039394497871, 0.042976900935173035, -0.06253521889448166, 0.02173938974738121, 0.01422695629298687, 0.06118226796388626], index=0, object='embedding')\n" + ] + } + ], + "source": [ + "for x in client.embeddings.create(input=\"This is a test\", model=\"vectors\").data:\n", + " print(x)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This uses the vector model associated with the current embeddings database." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Text to Speech (TTS)\n", + "\n", + "This API can do more than just work with text. Let's generate speech. " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import Audio, display\n", + "\n", + "with client.audio.speech.with_streaming_response.create(\n", + " model=\"neuml/kokoro-fp16-onnx\",\n", + " input=\"txtai is an all-in-one embeddings database for semantic search, LLM orchestration and semantic workflows\",\n", + " voice=\"bm_lewis\",\n", + ") as response:\n", + " response.stream_to_file(file=\"out.mp3\")\n", + "\n", + "display(Audio(\"out.mp3\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Transcription\n", + "\n", + "The generated speech can also be transcribed back to text." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Text AI is an all in one embedding's database for semantic search, LLM orchestration and semantic workflows.\"" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "f = open(\"out.mp3\", \"rb\")\n", + "client.audio.transcriptions.create(\n", + " model=\"whisper\",\n", + " file=f\n", + ").text" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# JavaScript client\n", + "\n", + "Given that this is an OpenAI-compatible API, other OpenAI clients are also supported. Let's try a few examples with the JavaScript client.\n", + "\n", + "Install via `npm install openai`" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting chat.js\n" + ] + } + ], + "source": [ + "%%writefile chat.js\n", + "\n", + "import OpenAI from \"openai\";\n", + "\n", + "const openai = new OpenAI({\n", + " baseURL: \"http://localhost:8000/v1\",\n", + " apiKey: \"api-key\"\n", + "});\n", + "\n", + "async function main() {\n", + " const stream = await openai.chat.completions.create({\n", + " model: \"rag\",\n", + " messages: [{ role: \"user\", content: \"Tell me about the iPhone\" }],\n", + " stream: true,\n", + " });\n", + "\n", + " for await (const chunk of stream) {\n", + " process.stdout.write(chunk.choices[0]?.delta?.content || \"\");\n", + " }\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The iPhone is a line of smartphones designed and marketed by Apple Inc. that uses Apple's iOS mobile operating system. The first-generation iPhone was announced by former Apple CEO Steve Jobs on January 9, 2007. As of January 1, 2024, more than 2.3 billion iPhones have been sold, making Apple the largest vendor of mobile phones in 2023." + ] + } + ], + "source": [ + "!node --no-warnings chat.js" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As we can see, this is the same output as we had earlier with the Python client.\n", + "\n", + "Let's try generating speech." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting speech.js\n" + ] + } + ], + "source": [ + "%%writefile speech.js\n", + "\n", + "import fs from \"fs\";\n", + "import path from \"path\";\n", + "import OpenAI from \"openai\";\n", + "\n", + "const openai = new OpenAI({\n", + " baseURL: \"http://localhost:8000/v1\",\n", + " apiKey: \"api-key\"\n", + "});\n", + "\n", + "const speechFile = path.resolve(\"./speech.mp3\");\n", + "\n", + "async function main() {\n", + " const mp3 = await openai.audio.speech.create({\n", + " model: \"neuml/kokoro-fp16-onnx\",\n", + " input: \"txtai is an all-in-one embeddings database for semantic search, LLM orchestration and semantic workflows\",\n", + " voice: \"bm_lewis\",\n", + " });\n", + "\n", + " const buffer = Buffer.from(await mp3.arrayBuffer());\n", + " await fs.promises.writeFile(speechFile, buffer);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!node --no-warnings speech.js" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "display(Audio(\"speech.mp3\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Speech is the same as above, as expected." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting transcribe.js\n" + ] + } + ], + "source": [ + "%%writefile transcribe.js\n", + "\n", + "import fs from \"fs\";\n", + "import OpenAI from \"openai\";\n", + "\n", + "const openai = new OpenAI({\n", + " baseURL: \"http://localhost:8000/v1\",\n", + " apiKey: \"api-key\"\n", + "});\n", + "\n", + "async function main() {\n", + " const transcription = await openai.audio.transcriptions.create({\n", + " file: fs.createReadStream(\"speech.mp3\"),\n", + " model: \"whisper\",\n", + " });\n", + "\n", + " console.log(transcription.text);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Text AI is an all in one embedding's database for semantic search, LLM orchestration and semantic workflows.\n" + ] + } + ], + "source": [ + "!node --no-warnings transcribe.js" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook covered how to setup an OpenAI-compatible API endpoint for txtai. It enables quickly trying `txtai` with a familiar-to-use client. It's also a way to do local/offline development testing using the OpenAI client. Just another way to make it easier to use `txtai`!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "local", + "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", + "version": "3.9.21" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/75_Medical_RAG_Research_with_txtai.ipynb b/examples/75_Medical_RAG_Research_with_txtai.ipynb new file mode 100644 index 0000000..9bd872d --- /dev/null +++ b/examples/75_Medical_RAG_Research_with_txtai.ipynb @@ -0,0 +1,454 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "666da745", + "metadata": {}, + "source": [ + "# Medical RAG Research with txtai\n", + "\n", + "Large Language Models (LLMs) have captured the public's attention with their impressive capabilities. The Generative AI era has reached a fever pitch with some predicting the coming rise of superintelligence.\n", + "\n", + "LLMs are far from perfect though and we're still a ways away from true AI. One big challenge is with hallucinations. Hallucinations is the term for when an LLM generates output that is factually incorrect. The alarming part of this is that on a cursory glance, it actually sounds like factual content. The default behavior of LLMs is to produce plausible answers even when no plausible answer exists. LLMs are not great at saying I don't know.\n", + "\n", + "Retrieval Augmented Generation (RAG) helps reduce the risk of hallucinations by limiting the context in which a LLM can generate answers. This is typically done with a search query that hydrates a prompt with a relevant context. RAG has been one of the most practical use cases of the Generative AI era.\n", + "\n", + "This notebook will demonstrate how to build a Medical RAG Research process with txtai." + ] + }, + { + "cell_type": "markdown", + "id": "a4c7cc15", + "metadata": {}, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a246bb3a", + "metadata": {}, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai" + ] + }, + { + "cell_type": "markdown", + "id": "fc5c27ed", + "metadata": {}, + "source": [ + "# Medical Dataset\n", + "\n", + "For this example, we'll use a [PubMed subset of article metadata for H5N1](https://huggingface.co/datasets/NeuML/pubmed-h5n1). This dataset was created using [`paperetl`](https://github.com/neuml/paperetl), an open-source library for parsing medical and scientific papers.\n", + "\n", + "[PubMed](https://pubmed.ncbi.nlm.nih.gov/) has over 38 million article abstracts as of June 2025. `paperetl` supports loading the full dataset with all 38 million articles or just a smaller subset. The dataset link above has more details on how this can be changed for different codes and keywords. This link also has information on how the article abstracts can be loaded in addition to the metadata." + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "id": "5dd4145d", + "metadata": {}, + "outputs": [], + "source": [ + "from datasets import load_dataset\n", + "from txtai import Embeddings\n", + "\n", + "ds = load_dataset(\"neuml/pubmed-h5n1\", split=\"train\")" + ] + }, + { + "cell_type": "markdown", + "id": "2d3b1cd8", + "metadata": {}, + "source": [ + "Next, we'll build a `txtai` embeddings index with the articles. We'll use a vector embeddings model that specializes in vectorizing medical papers: [PubMedBERT Embeddings](https://huggingface.co/NeuML/pubmedbert-base-embeddings). " + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "id": "04e69829", + "metadata": {}, + "outputs": [], + "source": [ + "embeddings = Embeddings(path=\"neuml/pubmedbert-base-embeddings\", content=True, columns={\"text\": \"title\"})\n", + "embeddings.index(x for x in ds if x[\"title\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "id": "a801391e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "7865" + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "embeddings.count()" + ] + }, + { + "cell_type": "markdown", + "id": "907898a4", + "metadata": {}, + "source": [ + "# RAG Pipeline\n", + "\n", + "There are a number of [prior examples](https://neuml.github.io/txtai/examples/#llm) on how to run RAG with `txtai`. The [RAG pipeline](https://neuml.github.io/txtai/pipeline/text/rag/) takes two main parameters, an embeddings database and an LLM. The embeddings database is the one just created above. For this example, we'll use a [simple local LLM with 600M parameters](https://huggingface.co/Qwen/Qwen3-0.6B).\n", + "\n", + "Substitute your own embeddings database to change the knowledge base. `txtai` supports running local LLMs via [transformers](https://github.com/huggingface/transformers) or [llama.cpp](https://github.com/abetlen/llama-cpp-python). It also supports a wide variety of LLMs via [LiteLLM](https://github.com/BerriAI/litellm). For example, setting the 2nd RAG pipeline parameter below to `gpt-4o` along with the appropriate environment variables with access keys switches to a hosted LLM. See [this documentation page](https://neuml.github.io/txtai/pipeline/text/llm/) for more on this." + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "id": "cd05fc41", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Device set to use cuda:0\n" + ] + } + ], + "source": [ + "from txtai import RAG\n", + "\n", + "# Prompt templates\n", + "system = \"You are a friendly medical assistant that answers questions\"\n", + "template = \"\"\"\n", + "Answer the following question using the provided context.\n", + "\n", + "Question:\n", + "{question}\n", + "\n", + "Context:\n", + "{context}\n", + "\"\"\"\n", + "\n", + "# Create RAG pipeline\n", + "rag = RAG(embeddings, \"Qwen/Qwen3-0.6B\", system=system, template=template, output=\"flatten\")" + ] + }, + { + "cell_type": "markdown", + "id": "98800220", + "metadata": {}, + "source": [ + "# RAG Queries\n", + "\n", + "Now that the pipeline is setup, let's run a query." + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "id": "4d93ac4b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Okay, let's see. The user is asking about H5N1. The context provided starts with \"Why tell me now?\" and then goes into facts about H5N1. The first sentence mentions that people and healthcare providers are weighing in on pandemic messages. Then it says H5N1 is avian influenza, a potential pandemic.\n", + "\n", + "Wait, but the user's question is about H5N1. The context doesn't go into specifics about what H5N1 is, but it does state that it's avian influenza. So I need to make sure I answer based on that. The answer should be concise, maybe mention that H5N1 is avian flu and it's a potential pandemic. Also, note that people are weighing in on messages. But I need to check if there's any more information. The context ends there. So the answer should be straightforward.\n", + "\n", + "\n", + "H5N1 influenza viruses are a type of avian influenza, a potential pandemic influenza virus that could cause widespread illness and death. While the context highlights the importance of public health and preparedness, it does not provide more specific details about its characteristics or risks.\n" + ] + } + ], + "source": [ + "print(rag(\"Tell me about H5N1\"))" + ] + }, + { + "cell_type": "markdown", + "id": "39ab9946", + "metadata": {}, + "source": [ + "Notice that this LLM outputs a thinking or reasoning section then the answer.\n", + "\n", + "Let's review the context to validate this answer is derived from the knowledge base." + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "id": "53de0f96", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'id': '16775537',\n", + " 'text': '\"Why tell me now?\" the public and healthcare providers weigh in on pandemic influenza messages.',\n", + " 'score': 0.7156285643577576},\n", + " {'id': '22308474',\n", + " 'text': 'H5N1 influenza viruses: facts, not fear.',\n", + " 'score': 0.658343493938446},\n", + " {'id': '16440117',\n", + " 'text': 'Avian influenza--a pandemic waiting to happen?',\n", + " 'score': 0.5827972888946533},\n", + " {'id': '20667302',\n", + " 'text': 'The influenza A(H5N1) epidemic at six and a half years: 500 notified human cases and more to come.',\n", + " 'score': 0.5593500137329102},\n", + " {'id': '18936262',\n", + " 'text': 'What Australians know and believe about bird flu: results of a population telephone survey.',\n", + " 'score': 0.5568690299987793},\n", + " {'id': '30349811',\n", + " 'text': 'Back to the Future: Lessons Learned From the 1918 Influenza Pandemic.',\n", + " 'score': 0.5540266036987305},\n", + " {'id': '17276785',\n", + " 'text': 'Pandemic influenza: what infection control professionals should know.',\n", + " 'score': 0.5519200563430786},\n", + " {'id': '16681227',\n", + " 'text': 'A pandemic flu: not if, but when. SARS was the wake-up call we slept through.',\n", + " 'score': 0.5518345832824707},\n", + " {'id': '22402712',\n", + " 'text': 'Ferretting out the facts behind the H5N1 controversy.',\n", + " 'score': 0.5508109331130981},\n", + " {'id': '25546511',\n", + " 'text': \"One-way trip: influenza virus' adaptation to gallinaceous poultry may limit its pandemic potential.\",\n", + " 'score': 0.5494509339332581}]" + ] + }, + "execution_count": 79, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "embeddings.search(\"Tell me about H5N1\", limit=10)" + ] + }, + { + "cell_type": "markdown", + "id": "025e6092", + "metadata": {}, + "source": [ + "The answer is doing a good job being based on the context above. Also keep in mind this is a small 600M parameter model, which is even more impressive.\n", + "\n", + "Let's try another query." + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "id": "1899047a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Okay, let's see. The user is asking about the locations that have had H5N1 outbreaks, and the provided context mentions a few places: Indonesia and Bangladesh. The context also has a title about a decade of avian influenza in Bangladesh and mentions \"H5N1.\" \n", + "\n", + "Wait, the user's question is in English, so I need to make sure I'm interpreting the context correctly. The context includes two sentences: one about a decade in Bangladesh and another about H5N1. The user is probably looking for specific locations where H5N1 has been reported. \n", + "\n", + "Looking at the context again, it says \"Human avian influenza in Indonesia\" and \"A Decade of Avian Influenza in Bangladesh: Where Are We Now? Are we ready for pandemic influenza H5N1?\" So the outbreaks are in Indonesia and Bangladesh. \n", + "\n", + "I should confirm that there are no other mentions of other locations. The context doesn't provide more information beyond those two countries. Therefore, the answer should list Indonesia and Bangladesh as the locations with H5N1 outbreaks.\n", + "\n", + "\n", + "The locations with H5N1 outbreaks are Indonesia and Bangladesh.\n" + ] + } + ], + "source": [ + "print(rag(\"What locations have had H5N1 outbreaks?\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "id": "39d4efa6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'id': '21706937',\n", + " 'text': 'Human avian influenza in Indonesia: are they really clustered?',\n", + " 'score': 0.6269429326057434},\n", + " {'id': '31514405',\n", + " 'text': 'A Decade of Avian Influenza in Bangladesh: Where Are We Now?',\n", + " 'score': 0.5972536206245422},\n", + " {'id': '15889987',\n", + " 'text': 'Are we ready for pandemic influenza H5N1?',\n", + " 'score': 0.5863772630691528},\n", + " {'id': '17717543',\n", + " 'text': 'Commentary: From scarcity to abundance: pandemic vaccines and other agents for \"have not\" countries.',\n", + " 'score': 0.5844159126281738},\n", + " {'id': '22491771',\n", + " 'text': 'Two years after pandemic influenza A/2009/H1N1: what have we learned?',\n", + " 'score': 0.5812581777572632},\n", + " {'id': '39666804',\n", + " 'text': \"Why hasn't the bird flu pandemic started?\",\n", + " 'score': 0.5738048553466797},\n", + " {'id': '23402131',\n", + " 'text': 'Where do avian influenza viruses meet in the Americas?',\n", + " 'score': 0.5638074278831482},\n", + " {'id': '20667302',\n", + " 'text': 'The influenza A(H5N1) epidemic at six and a half years: 500 notified human cases and more to come.',\n", + " 'score': 0.560465395450592},\n", + " {'id': '17338983',\n", + " 'text': 'Human avian influenza: how ready are we?',\n", + " 'score': 0.555113673210144},\n", + " {'id': '24518630',\n", + " 'text': 'Recognizing true H5N1 infections in humans during confirmed outbreaks.',\n", + " 'score': 0.5501888990402222}]" + ] + }, + "execution_count": 87, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "embeddings.search(\"What locations have had H5N1 outbreaks?\", limit=10)" + ] + }, + { + "cell_type": "markdown", + "id": "efa8e59e", + "metadata": {}, + "source": [ + "Once again the answer is based on the context which mentions the two countries in the answer. The context also discusses the Americas but it doesn't have as strong of language connecting H5N1 outbreaks to the location." + ] + }, + { + "cell_type": "markdown", + "id": "18e04661", + "metadata": {}, + "source": [ + "# Add citations\n", + "\n", + "The last item we'll cover is citations. One of the most important aspects of a RAG process is being able to ensure the answer is based on reality. There are a number of ways to do this but in this example, we'll ask the LLM to perform this step." + ] + }, + { + "cell_type": "code", + "execution_count": 96, + "id": "ce10ff39", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Device set to use cuda:0\n" + ] + } + ], + "source": [ + "# Prompt templates\n", + "system = \"You are a friendly medical assistant that answers questions\"\n", + "template = \"\"\"\n", + "Answer the following question using the provided context.\n", + "\n", + "After the answer, write a citation section with ALL the original article ids used for the answer.\n", + "\n", + "Question:\n", + "{question}\n", + "\n", + "Context:\n", + "{context}\n", + "\"\"\"\n", + "\n", + "def context(question):\n", + " context = []\n", + " for x in embeddings.search(question, limit=10):\n", + " context.append(f\"ARTICLE ID: {x['id']}, TEXT: {x['text']}\")\n", + "\n", + " return context\n", + "\n", + "# Create RAG pipeline\n", + "rag = RAG(embeddings, \"Qwen/Qwen3-0.6B\", system=system, template=template, output=\"flatten\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f79bdc9a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "H5N1 is a type of avian influenza virus. \n", + "\n", + "**Citation Section:** \n", + "- ARTICLE ID: 22010536, TEXT: Is avian influenza virus A(H5N1) a real threat to human health?\n" + ] + } + ], + "source": [ + "question = \"What is H5N1?\"\n", + "print(rag(question, context(question), maxlength=2048, stripthink=True))" + ] + }, + { + "cell_type": "markdown", + "id": "15539c5d", + "metadata": {}, + "source": [ + "As expected, the answer adds a citation section. Also note that the RAG pipeline stripped the thinking section from the result." + ] + }, + { + "cell_type": "markdown", + "id": "3bdf84b5", + "metadata": {}, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook covered how to build a Medical RAG Research process with `txtai`. It also covered how to modify this logic to add in your own knowledge base or use a more sophisticated LLM.\n", + "\n", + "With an important space such as the medical domain, it's vital to ensure that answers are derived from reliable knowledge. This notebook shows how to add that reliability via RAG. But as with anything in an important domain, there should be a human in the loop and answers shouldn't be blindly relied upon. " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "local", + "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", + "version": "3.10.18" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/76_Whats_new_in_txtai_9_0.ipynb b/examples/76_Whats_new_in_txtai_9_0.ipynb new file mode 100644 index 0000000..4fb22d9 --- /dev/null +++ b/examples/76_Whats_new_in_txtai_9_0.ipynb @@ -0,0 +1,309 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "e3wdiK5fGUoZ" + }, + "source": [ + "# 💡 What's new in txtai 9.0\n", + "\n", + "The 9.0 release adds first class support for sparse vector models (i.e. [SPLADE](https://en.wikipedia.org/wiki/Learned_sparse_retrieval)), late interaction models (i.e. [ColBERT](https://huggingface.co/colbert-ir/colbertv2.0)), fixed dimensional encoding (i.e. [MUVERA](https://arxiv.org/abs/2405.19504)) and reranking pipelines ✨ \n", + "\n", + "The embeddings framework was overhauled to seamlessly support both sparse and dense vector models. Previously, sparse vector support was limited to keyword/term indexes. Now learned sparse retrieval models such as SPLADE are supported. These models can help improve the accuracy of retrieval/search operations, which also improves RAG and Agents.\n", + "\n", + "Support for late interaction models, such as ColBERT, were also added to the embeddings framework. Unlike traditional vector models that pool outputs into single vector outputs, late interaction models produce multiple vectors. These models are paired with the MUVERA algorithm to transform multiple vectors into fixed dimensional single vectors for search.\n", + "\n", + "LLMs are quickly converging to produce similar outputs for similar inputs and becoming standard commodities. The retrieval or context layer makes or breaks projects. This is known as putting the R in RAG!\n", + "\n", + "**Standard upgrade disclaimer below**\n", + "\n", + "While everything is backwards compatible, it's prudent to backup production indexes before upgrading and test before deploying." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "p8BbfjrhH-V2" + }, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "id": "-OXsTQgaGQPM" + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[ann,vectors]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "n4EXrtcYIIYE" + }, + "source": [ + "# Sparse vector indexes\n", + "\n", + "The first major change added with this release is `learned sparse retrieval` (aka sparse vector indexes) models. This effort was multi-faceted in that it required both changes to how vectors were generated as well as how they are stored.\n", + "\n", + "`txtai` uses approximate nearest neighbor (ANN) search for it's vector search operations. The default library is [Faiss](https://github.com/facebookresearch/faiss). There is support for other libraries but in all cases the existing ANN backends only supported dense (i.e. NumPy) vectors.\n", + "\n", + "There aren't many options out there for sparse ANN search that supports `txtai` requirements, so IVFSparse was introduced. IVFSparse is an Inverted file (IVF) index with flat vector file storage and sparse array support. There is also support for storing sparse vectors in Postgres via [pgvector](https://github.com/pgvector/pgvector).\n", + "\n", + "Let's see it in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "id": "hzPD8_cQJNtN" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'id': '0',\n", + " 'text': 'US tops 5 million confirmed virus cases',\n", + " 'score': 0.019873601198196412},\n", + " {'id': '1',\n", + " 'text': \"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\",\n", + " 'score': 0.018737798929214476}]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from txtai import Embeddings\n", + "\n", + "# Works with a list, dataset or generator\n", + "data = [\n", + " \"US tops 5 million confirmed virus cases\",\n", + " \"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\",\n", + " \"Beijing mobilises invasion craft along coast as Taiwan tensions escalate\",\n", + " \"The National Park Service warns against sacrificing slower friends in a bear attack\",\n", + " \"Maine man wins $1M from $25 lottery ticket\",\n", + " \"Make huge profits without work, earn up to $100,000 a day\"\n", + "]\n", + "\n", + "# Create an embeddings\n", + "embeddings = Embeddings(sparse=True, content=True)\n", + "embeddings.index(data)\n", + "embeddings.search(\"North America\", 10)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Late interaction models\n", + "\n", + "Late interaction models encode data into multi-vector outputs. In other words, multiple input tokens map to multiple output vectors. Then at search time, the maximum similarity algorithm is used to find the best matches between the corpus and a query. This algorithm has achieved excellent results on retrieval benchmarks such as [MTEB](https://github.com/embeddings-benchmark/mteb).\n", + "\n", + "The downside of this approach is that it produces multiple vectors as opposed a single vector for each input. For example, if a text element tokenizes to many input tokens, there will be many output vectors vs a single one as with standard pooled vector approaches.\n", + "\n", + "Starting with the 9.0 release, late interaction models are supported with embeddings instances. Late interaction vectors will be transformed into fixed dimensional vectors using the MUVERA algorithm. See below. " + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'id': '0',\n", + " 'text': 'US tops 5 million confirmed virus cases',\n", + " 'score': 0.04216160625219345},\n", + " {'id': '1',\n", + " 'text': \"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\",\n", + " 'score': 0.029944246634840965},\n", + " {'id': '3',\n", + " 'text': 'The National Park Service warns against sacrificing slower friends in a bear attack',\n", + " 'score': 0.015931561589241028}]" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from txtai import Embeddings\n", + "\n", + "# Works with a list, dataset or generator\n", + "data = [\n", + " \"US tops 5 million confirmed virus cases\",\n", + " \"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\",\n", + " \"Beijing mobilises invasion craft along coast as Taiwan tensions escalate\",\n", + " \"The National Park Service warns against sacrificing slower friends in a bear attack\",\n", + " \"Maine man wins $1M from $25 lottery ticket\",\n", + " \"Make huge profits without work, earn up to $100,000 a day\"\n", + "]\n", + "\n", + "# Create an embeddings\n", + "embeddings = Embeddings(path=\"colbert-ir/colbertv2.0\", content=True)\n", + "embeddings.index(data)\n", + "embeddings.search(\"North America\", 10)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Reranking pipeline\n", + "\n", + "Another major new component in this release is the Reranker pipeline. This pipeline takes an embeddings instance, a similarity instance and uses the similarity instance to rerank outputs. This is a key component of the MUVERA paper - using the standard vector index to retrieve candidates then reranking the outputs using the late interaction model." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'id': '1',\n", + " 'text': \"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\",\n", + " 'score': 0.3324427008628845},\n", + " {'id': '0',\n", + " 'text': 'US tops 5 million confirmed virus cases',\n", + " 'score': 0.24423550069332123},\n", + " {'id': '3',\n", + " 'text': 'The National Park Service warns against sacrificing slower friends in a bear attack',\n", + " 'score': 0.16353240609169006}]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from txtai import Embeddings\n", + "from txtai.pipeline import Reranker, Similarity\n", + "\n", + "# Works with a list, dataset or generator\n", + "data = [\n", + " \"US tops 5 million confirmed virus cases\",\n", + " \"Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg\",\n", + " \"Beijing mobilises invasion craft along coast as Taiwan tensions escalate\",\n", + " \"The National Park Service warns against sacrificing slower friends in a bear attack\",\n", + " \"Maine man wins $1M from $25 lottery ticket\",\n", + " \"Make huge profits without work, earn up to $100,000 a day\"\n", + "]\n", + "\n", + "# Create an embeddings\n", + "embeddings = Embeddings(path=\"colbert-ir/colbertv2.0\", content=True)\n", + "embeddings.index(data)\n", + "\n", + "similarity = Similarity(path=\"colbert-ir/colbertv2.0\", lateencode=True)\n", + "\n", + "ranker = Reranker(embeddings, similarity)\n", + "ranker(\"North America\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notice that while the outputs are the same, the scoring and order is different.\n", + "\n", + "Let's try a more interesting example." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'id': 'ChatGPT',\n", + " 'text': 'ChatGPT is a generative artificial intelligence chatbot developed by OpenAI and released on November 30, 2022. It uses large language models (LLMs) such as GPT-4o as well as other multimodal models to create human-like responses in text, speech, and images. It has access to features such as searching the web, using apps, and running programs. It is credited with accelerating the AI boom, an ongoing period of rapid investment in and public attention to the field of artificial intelligence (AI). Some observers have raised concern about the potential of ChatGPT and similar programs to displace human intelligence, enable plagiarism, or fuel misinformation.',\n", + " 'score': 0.6639302968978882},\n", + " {'id': 'ChatGPT Search',\n", + " 'text': 'ChatGPT Search (originally SearchGPT) is a search engine developed by OpenAI. It combines traditional search engine features with generative pretrained transformers (GPT) to generate responses, including citations to external websites.',\n", + " 'score': 0.6477508544921875},\n", + " {'id': 'ChatGPT in education',\n", + " 'text': 'The usage of ChatGPT in education has sparked considerable debate and exploration. ChatGPT is a chatbot based on large language models (LLMs) that was released by OpenAI in November 2022.',\n", + " 'score': 0.5918337106704712}]" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from txtai import Embeddings\n", + "from txtai.pipeline import Reranker, Similarity\n", + "\n", + "# Create an embeddings\n", + "embeddings = Embeddings()\n", + "embeddings.load(provider=\"huggingface-hub\", container=\"neuml/txtai-wikipedia\")\n", + "\n", + "similarity = Similarity(path=\"colbert-ir/colbertv2.0\", lateencode=True)\n", + "\n", + "ranker = Reranker(embeddings, similarity)\n", + "ranker(\"Tell me about ChatGPT\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tvnMO1Eai6Gy" + }, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook gave a quick overview of txtai 9.0. Updated documentation and more examples will be forthcoming. There is much to cover and much to build on!\n", + "\n", + "See the following links for more information.\n", + "\n", + "- [9.0 Release on GitHub](https://github.com/neuml/txtai/releases/tag/v9.0.0)\n", + "- [Documentation site](https://neuml.github.io/txtai)" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "gpuType": "T4", + "provenance": [] + }, + "kernelspec": { + "display_name": "local", + "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", + "version": "3.10.18" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/examples/77_GraphRAG_with_Wikipedia_and_GPT_OSS.ipynb b/examples/77_GraphRAG_with_Wikipedia_and_GPT_OSS.ipynb new file mode 100644 index 0000000..61fe420 --- /dev/null +++ b/examples/77_GraphRAG_with_Wikipedia_and_GPT_OSS.ipynb @@ -0,0 +1,415 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "ef822909", + "metadata": {}, + "source": [ + "# GraphRAG with Wikipedia and GPT OSS\n", + "\n", + "Retrieval Augmented Generation (RAG) is one of the most popular techniques in the AI space today. RAG takes a user request, retrieves the best matching content and then plugs that context into an LLM prompt to generate an answer. When otherwise not mentioned, most assume the context is generated using a vector database query. But there is no rule that says context can't be generated with other methods. It could be a simple web query, SQL query, text index search or other traditional search.\n", + "\n", + "We also often hear the term GraphRAG. GraphRAG means different things to different people. Here we're going to build an example that uses `txtai`, [wikipedia](https://huggingface.co/datasets/NeuML/wikipedia-20250620) and [gpt-oss](https://huggingface.co/openai/gpt-oss-20b) to research a specific topic with graphs. `txtai` has a built-in graph component that automatically generates a graph network over the data loaded into an embeddings database. We'll use a pre-built embeddings database hosted on the Hugging Face Hub, [txtai-wikipedia-slim](https://hf.co/neuml/txtai-wikipedia-slim)." + ] + }, + { + "cell_type": "markdown", + "id": "af580b8a", + "metadata": {}, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "36b1fd23", + "metadata": {}, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[graph,pipeline-llm]" + ] + }, + { + "cell_type": "markdown", + "id": "8a3f8a01", + "metadata": {}, + "source": [ + "# Load txtai-wikipedia-slim\n", + "\n", + "Next, we'll load the embeddings database. This database is the top 100K most viewed Wikipedia articles with both a dense vector index and graph network enabled." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07b7dee4", + "metadata": {}, + "outputs": [], + "source": [ + "from txtai import Embeddings\n", + "\n", + "embeddings = Embeddings().load(provider=\"huggingface-hub\", container=\"neuml/txtai-wikipedia-slim\")" + ] + }, + { + "cell_type": "markdown", + "id": "7255a293", + "metadata": {}, + "source": [ + "# Build context with a graph query\n", + "\n", + "The `txtai` graph component supports the [openCypher](https://opencypher.org/) query language via the [GrandCypher](https://github.com/aplbrain/grand-cypher) library.\n", + "\n", + "openCypher is a language for expressive and efficient data querying of a property graph. In this example, we'll traverse the embeddings database graph looking for paths between nodes similar to `chatgpt` and `anthropic`." + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "id": "37a7128f", + "metadata": {}, + "outputs": [], + "source": [ + "g = embeddings.search(\"\"\"\n", + "MATCH P=(A)-[]->(B)\n", + "WHERE SIMILAR(A, 'chatgpt') AND SIMILAR(B, 'anthropic')\n", + "RETURN P\n", + "LIMIT 10\n", + "\"\"\", graph=True)" + ] + }, + { + "cell_type": "markdown", + "id": "6848e16a", + "metadata": {}, + "source": [ + "The query above is an extremely powerful combination of an vector similarity node search and a graph traversal query that walks the paths between nodes. It's much more expressive than simply saying find nodes similar to each of the concepts independently. It can be considered a `deep graph search`." + ] + }, + { + "cell_type": "markdown", + "id": "9632d541", + "metadata": {}, + "source": [ + "# Plot the context network\n", + "\n", + "Let's show the context as a graph plot!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3861f5b5", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABiIAAALJCAYAAAA57jvpAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs/XmcZXddJ/6/zr7furfSSSfdZGUJSyANBGEkLAFEcWETHQXmB7KFZVj8Ojji4DIKoywKmmEAYUQFZFBBh3EYhcgim8oSIAQICdnT2brrLmfff390nZN7a71Vdff7evLoB+nuWk4tfavq8zrv90s4duxYCSIiIiIiIiIiIiIiojEQp30BRERERERERERERES0uBhEEBERERERERERERHR2DCIICIiIiIiIiIiIiKisWEQQUREREREREREREREY8MggoiIiIiIiIiIiIiIxoZBBBERERERERERERERjQ2DCCIiIiIiIiIiIiIiGhsGEURERERERERERERENDYMIoiIiIiIiIiIiIiIaGwYRBARERERERERERER0dgwiCAiIiIiIiIiIiIiorFhEEFERERERERERERERGPDIIKIiIiIiIiIiIiIiMaGQQQREREREREREREREY0NgwgiIiIiIiIiIiIiIhobBhFERERERERERERERDQ2DCKIiIiIiIiIiIiIiGhsGEQQEREREREREREREdHYMIggIiIiIiIiIiIiIqKxYRBBRERERERERERERERjwyCCiIiIiIiIiIiIiIjGhkEEERERERERERERERGNDYMIIiIiIiIiIiIiIiIaGwYRREREREREREREREQ0NgwiiIiIiIiIiIiIiIhobBhEEBERERERERERERHR2DCIICIiIiIiIiIiIiKisWEQQUREREREREREREREY8MggoiIiIiIiIiIiIiIxoZBBBERERERERERERERjQ2DCCIiIiIiIiIiIiIiGhsGEURERERERERERERENDYMIoiIiIiIiIiIiIiIaGwYRBARERERERERERER0dgwiCAiIiIiIiIiIiIiorFhEEFERERERERERERERGPDIIKIiIiIiIiIiIiIiMaGQQQREREREREREREREY0NgwgiIiIiIiIiIiIiIhobBhFERERERERERERERDQ2DCKIiIiIiIiIiIiIiGhsGEQQEREREREREREREdHY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62qAVpnPAGIijUZDhUJBhUJBlmXtnycRDGrbYadIciV18M8Xw9S2hScp8chPZDaaHxYahuFXJhE2oNdMVbNUrVapWQIAAF2lE8tkAaDneScjLMtSMpls93IwT9gkaK54PC7XdZkLgZYJBAJ+aCxJqVRK6XRa1WpVd999t9auXTvj+3rHO96hf/7nf572du985zv1pS99yX/5qquu0uc+9zn/ZW+uws5KQPnw4g6dDTFWww4ru+i4Ob+/YRiybVvBYFCRSETxeFwDAwNauHChFi1apMHBQSWTSUUiEdm2rUajoVKppEwmo5GREe3du1f79u1TKpVSNptVoVBQuVyeUb3STD9vo43+2kgkEtqwYYMWLVo05+eP/mGapoLBoGKxmAYHB7Vo0SIlk0mFw2G5rqt8Pu9/TafTaeXzeVUqFf7/AgAAdAVORABAm9RqNX9mhHcyAr2LaqbmCofDchyHE0V9bHBwUBdccIFe//rXa/HixcrlctqxY4fuuusu3XnnnSqXy3O+b8dxFI1G5TiOqtXqhCcgzj33XOVyuRnf5913363f//730z7uxRdfrM9+9rPT3l8huVJyXakbTls16iokh5R86bFJb+KdbJhsQLR/V6NONlQqlY4/2ZDJZPSv//qvuvjii/XlL3+53ctBhxl90sFxHP9rfXTNUrVaZfg5AADoCQQRANBGXhgxuqYJwNRs21Y0GlWxWGz7sFu0x9KlS/X1r39duVxON9xwgzZv3qxqtapVq1bp7LPP1t69e/Wb3/xm1vc7kwDCMzIyMqv7rlQqqlQqU97mjDPOUKFQ0GOPTb5h7ykmVkhuQzI6dFD1aIapYmJlz4YN0/n5z3+ub33rW1q/fj0nuPqYV7M0umppdM1SsVj0A4hu/DoHAACYDkEEALTZ6JMRiURCmUym3UtCC3AiojkMw1AikVCtVlM+n2/3ctAm69atU71e14c//GGVSiX/9Tt37tR999035rbRaFSXX3651q5dK8dx9PTTT+ub3/ymNm3aJEm68MIL9aY3vUk/+9nPdP755ysajeoXv/iFrrvuOr3nPe/ReeedJ8Mw9OMf/1jf//73/fu9++679clPflL33XeflixZoltuuUV//dd/rXPOOUfHH3+8tm/frq9+9at64oknpAMVP3/+53+ud73rXZM+r7e+9a3TBiinnnqq3v/+92vlMcepLkMbt+f18V9s1Qup/SHHUDKghy9frQs3bNYlr12s1y6LavNISR/5+VY9sOPl75n3n7RQH1u7VINhW3c/n9Fvt+b0sbWHa9V1j0iSvvFfjlAyaOn9Gzb773PN21Zo9ZKw3v2DZ/ev96iEPvLGw3X84pDqrg5aiySdujyqL//RkF6x8DXavuVUbdiwQR/72Mf00Y9+VJs2bVK9XtfSpUt18cUXa/Xq1SoWi3rggQd0/fXXT7pp730sr7nmGl1++eU67LDD9Pvf/17XXnut3vKWt+iiiy5SNBrVXXfdpeuvv96/mjwWi+mKK67QG97wBjmOoz/84Q/6+te/ru3bt4+574svvljJZFIbN27Uo48+etDjr127Vh/4wAd05JFHau/evbrzzjv1j//4j5Netf7CCy9o7969etOb3qQ77rhjys8veodlWQcNltaBejXvd1i1WlWt1vzZKQAAAJ2o80tlAaAPVKtVpdNpBQIBJRKJdi8HLUAQ0RyxWEyGYRDY9bFEIqFTTjlFt91225gQYjKf+cxnNDAwoKuuukof+tCH9Oyzz+orX/mK4vG4HMdRKBTSsmXLdPLJJ+vqq6/W5z73Ob3jHe/Qtddeq8WLF2vdunX69re/rUsuuUTHH3/8lI/1wQ9+ULfeeqsuvfRSbd26VZ/61KfGXOk/nRNPPFHPPPPMlLcJh8O69Uf/R2+/6Qmd80/PquG6uvncow8aV/2JNy/T9ffv1ltufFKbhsv6zruPlHXgRqctj+or71ipbz3wkt5y45P61fMZ/eUbD5/xOj3RgKl/2PiS3nbT0xOuJR4w9YM/OVpP7inqrf//U/r2P96q888/XzowbyOVSqnRaOiaa67RM888ow996EO66qqrNDg4qE9/+tNTPnYwGNS5556rz33uc7rqqqu0Zs0afe5zn9PrXvc6XX311frCF76gs88+W2eccYb/PldffbWOPfZYfeITn9Cf//mfyzAM/e3f/q0/1Pr444/Xxz72Md1222269NJL9fDDD+v973//mMc98cQTdfXVV2vDhg266KKL9NWvflVnnXWW/vt//+9Trvepp57SiSeeOOuPMbqHbdsKh8NKJBJauHChFixY4P+cqVarymQy2rdvn4aHh5XJZPwTEAAAAP2CIAIAOgRhBDC1UCikUCikXC5HX3YfW758uUzT1NatW8e8/rbbbtMdd9yhO+64Q5dddpkkafXq1TruuOP02c9+Vs8884y2b9+u9evXK5fL6ayzztLAwIB0ICj8/Oc/r02bNum3v/2tHn74YQ0NDekb3/iGtm7dqp///Od68cUXtWbNminXduutt+p3v/udtm3bpptuukmHH364li9fPqPnFY1GFYvFtHfv3ilvd8899+ie+36rzem6HnupqL+4Y4tOOCysVy4KjbndN+7frbs2ZbRppKwv/nqnViaDWjUYlCRd+trF+sXmjK6//yVtGinrxv/Yq19umn24989Pp/TTZ1J6PlWecC3/7VUL5LrSup+9qKf3lfTvDzyqW265Zcx9nHPOOXruued0ww03aOvWrXruuef0pS99SSeffLJWrFgx6WM7jqPrrrtOzz33nB555BH9+7//u1avXq0vfelL2rJli373u9/p4Ycf9j9ny5cv19q1a/V3f/d3evTRR7Vp0yZdc801WrRokU4//fT96/1v/00bN27ULbfcom3btmnDhg3auHHjmMe98MIL9U//9E+68847tXPnTj344IO68cYbpzzpIkn79u3TkiVLZv0xRmcyDEOBQEDRaFQDAwP+0PRoNCrDMFQsFpVOp7Vv3z6NjIwol8upXC7zuwsAAPQ1qpkAoIN4V8wlEgnF43G6pHuEcWCYLCci5s6yLMViMRWLxUMaQozedfnll8swDH3yk5+U4ziSpGOOOUbhcFi33377mNsGg0EdfvjhSqfTKpVK2rVrl4rFov/2kZERNRqNMd+zIyMjGhwcnHINXt2TDmw868BQ7fGhyUSCwf0hwXRzJJYvX66LPniJjnnNCVoYtv1Z1SsSAT219+UTIk+89PLz2ZXbP+diUdTRs8NlHbMwpH95JjXmfh/amdcfHTO7EHzVYFBXv2mpXrssOuFajlkY1BN7iirX938cXcvRU089NeY+jj76aK1Zs2bCyqJly5Zp27ZtEz52sVjUjh07/JdHRka0e/fuMadkRkZG/LDpiCOOUK1W05NPPum/PZPJaOvWrTriiCMkSStXrtS999475nGeeOIJnXbaaWPWu3r16jEnIEzTVDAYVDAYnPTnU7lcVigUmvBt6HzULAEAABw6gggA6DCVSsUPIyQRRvQAL4jA3CUSCdXrdeVyuXYvBW22fft2NRoNDQ0NjXn9zp07pQMbvp5QKKTh4WF97GMfUygUkuM4qtVqKpVK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iAAAQNLw0Bs7q5JpPLeu55OrtXzbkwqFQlqwYIEsy1KtVlM+n1epVJq2emn8UOnxNUvValW1Wo2aJQAAAAAA0FQEEQCAvtcwbe1b8XrJtNq9lMkZlvYe/hodv/teWfWSyuWySqWSarXahDefqmapWq1SswQAAAAAAOYNQQQAoO+lDz9JDSfc7mVMyzVsvRB7hQY2/+qgt1GzBAAAAAAAOlUHFmEDANB6d999t9auXStJGll6iuROXUf03hMXaPO6V097vxe8eqH+z58eM+N17Lv6ZL3zFckZ3/6lw9ZIB2qWIpGIksmkFi5cqAULFigWi8myLJXLZYVCId16660aHBxUNpud9eDqqRxxxBG69dZbFQqFmnJ/AAAAAACgt3EiAgDQFoODg7rgggv0+te/XosXL1Yul9OOHTt011136c4771S5XG7p45977rnK5XJyJRUGVk47pPonT47ork2ZKW8TtAx9/E1LdfFtzzd5tQcYhsrxZVqw+DBZcv2apQ9/+MOKRCL6xCc+4d80Fos15SG/9rWv6bnnntP111/vv27Lli164okndN555+l73/teUx4HAAAAAAD0LoIIAMC8W7p0qb7+9a8rl8vphhtu0ObNm1WtVrVq1SqdffbZ2rt3r37zm9+0dA0jIyOSpEp4gRr29Ff2l2quSpPMY/D81+MGla00dP/2fNPWOZ5rWtrjRhUYedE/4dCOAdM///nP9ZGPfETf//73GW4NAAAAAACmRBABAJh369atU71e14c//GGVSiX/9Tt37tR999035rbRaFSXX3651q5dK8dx9PTTT+ub3/ymNm3aJEm68MILdfrpp2vDhg268MILlUgk9K//+q/6X//rf+k973mPzjvvPBmGoR//+Mf6/ve/79/v3XffrU9+8pP6l+eyGkoG9PDlq3Xhhs265LWL9dplUW0eKekjP9+qB3bsDxXee+ICXfO2FVp13SOTPq9zjh/Uz59LH/T69716of7HqYfpqMGgRkp1/fTpEV111zb/7QvCtm4+d5XOPCqhndmK/vrftvv3YxrS185aqTcdEddhUUfbMxX9ePk5+vmNX/Wf/1lnneU/J+/ju2vXLknSypUrtW7dOh177LHavn27/v7v/15/+MMf/Mc+6aST9KEPfUhHH320stms7rzzTn33u99Vo9HQVVddpTVr1mjNmjX6kz/5E0nS+eefr927d+uBBx5QIpHQmjVr9NBDD83q8w8AAAAAAPoLQQQAYF4lEgmdcsopuuGGG8aEEJP5zGc+o3K5rKuuukr5fF7vete79JWvfEXvf//7lc1mJUnLli3TaaedpquuukrLli3TZz7zGS1dulTbtm3TunXrdMIJJ+iqq67SQw89pCeffHLM/ZfiyxRs7D9Z8Ik3L9Nf371Nm4fL+sQZy/Sddx+pU9Y/rro7s+f2+hVR3frY8JjXXfyaRfrcW1fob/59u365KaNE0NJpK6JjbnPl6Uv1mbu369P/tl2XnrJY33rXkTrpHx5TqlSXaUg7slX92W3Pa7hY02nLwvrae85SafND+tWvfqUf/vCHOuKIIxSJRPTFL35RkpTNZrVw4UJJ0oc//GFdf/31euGFF3Teeefpmmuu0fve9z5lMhktWrRI1157re68805de+21WrlypT760Y+qUqnof//v/61vfOMbWrFihV544QXdeOONkqR0en9AUqvV9Nxzz+nEE08kiAAAAAAAAFMiiAAAzKvly5fLNE1t3bp1zOtvu+02BQIB/+/f/va3tXr1ah133HE699xzVa1WJUnr16/X6aefrjPOOEM//elPJUmGYehLX/qSisWitmzZoocfflhDQ0O6+uqr5bqutm7dqve+971as2bNQUFE3YlI2p80fOP+3f4ciC/+eqd+c+mrtGowqGeHp59XkQhaSoZs7cpVxrz+I288XN+8f7e+/cAe/3X/sasw5jb/9Og+bXhyf1XU5/99hz50ymE6eWlU//Z8RrWG9MV7d/q3fXGkqLWDJb3lLW/Rr371K5VKJZXLZTmO49dNjfaTn/xE99xzj3Rg3sNpp52md77znbrlllv07ne/W3v27NHf//3fS5K2bt2qRYsW6bLLLtPNN9+sfD6vWq2mUqk04X3v3btXS5YsmfZjAwAAAAAA+htBBACgI1x++eUyDEOf/OQn5TiOJOmYY45ROBzW7bffPua2gUBAy5Yt81/evXu3isWi//LIyIgajYZc1x3zusHBwYMet2HakgxJ0hMvvXwfu3L7g49FUWdGQUTY3n8f5drLj7koYmtpPKB7tmSnfN/HRz1uodpQplTX4ujLv6I/ePIive/Vi7Qi4ShkmwqY0qZnn5l2TZL0xBNPvPxcGw09/fTTWrlypXSgtunxxx8fc/vHHntMkUhEixcv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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "import networkx as nx\n", + "\n", + "def plot(graph):\n", + " labels = {x: f\"{graph.attribute(x, 'id')}\" for x in graph.scan()}\n", + " colors = [\"#D32F2F\", \"#0277bd\", \"#7e57c2\", \"#757575\"]\n", + "\n", + " results = embeddings.batchsimilarity(labels.values(), [\"Anthropic Claude\", \"Google Gemini\", \"OpenAI GPT\"])\n", + " colors = [colors[x[0][0]] for x in results]\n", + "\n", + " options = {\n", + " \"node_size\": 1000,\n", + " \"node_color\": colors,\n", + " \"edge_color\": \"#454545\",\n", + " \"font_color\": \"#efefef\",\n", + " \"font_size\": 10,\n", + " \"alpha\": 1.0,\n", + " }\n", + "\n", + " fig, ax = plt.subplots(figsize=(20, 9))\n", + " pos = nx.spring_layout(graph.backend, seed=0, k=0.9, iterations=50)\n", + " nx.draw_networkx(graph.backend, pos=pos, labels=labels, **options)\n", + " ax.set_facecolor(\"#303030\")\n", + " ax.axis(\"off\")\n", + " fig.set_facecolor(\"#303030\")\n", + "\n", + " plt.show()\n", + "\n", + "plot(g)" + ] + }, + { + "cell_type": "markdown", + "id": "4344faab", + "metadata": {}, + "source": [ + "# Print the context as text\n", + "\n", + "Let's further inspect the graph nodes." + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "id": "947ca6f1", + "metadata": {}, + "outputs": [], + "source": [ + "context = \"\"\n", + "for x in g.scan():\n", + " uid = g.attribute(x, \"id\")\n", + "\n", + " context += f\"- id: {uid}\\n\"\n", + " context += f\" url: https://en.wikipedia.org/wiki/{uid.replace(' ', '_')}\\n\"\n", + " context += f\" text: {g.attribute(x, 'text')}\\n\"\n", + " context += f\" links: {[g.attribute(n, 'id') for n in g.edges(x)]}\\n\"" + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "id": "0d784fcd", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "- id: ChatGPT\n", + " url: https://en.wikipedia.org/wiki/ChatGPT\n", + " text: ChatGPT is a generative artificial intelligence chatbot developed by OpenAI and released on November 30, 2022. It uses large language models (LLMs) such as GPT-4o as well as other multimodal models to create human-like responses in text, speech, and images. It has access to features such as searching the web, using apps, and running programs. It is credited with accelerating the AI boom, an ongoing period of rapid investment in and public attention to the field of artificial intelligence (AI). Some observers have raised concern about the potential of ChatGPT and similar programs to displace human intelligence, enable plagiarism, or fuel misinformation.\n", + " links: ['GPT-4', 'GPT-4.5', 'OpenAI', 'Gemini (chatbot)', 'GPT-3', 'GPT-4.1', 'Gemini (language model)', 'Anthropic', 'Claude (language model)']\n", + "- id: GPT-4\n", + " url: https://en.wikipedia.org/wiki/GPT-4\n", + " text: Generative Pre-trained Transformer 4 (GPT-4) is a multimodal large language model trained and created by OpenAI and the fourth in its series of GPT foundation models. It was launched on March 14, 2023, and made publicly available via the paid chatbot product ChatGPT Plus until being replaced in 2025, via OpenAI's API, and via the free chatbot Microsoft Copilot.\n", + " links: ['ChatGPT', 'GPT-3', 'GPT-4.5', 'GPT-4.1', 'OpenAI', 'Gemini (chatbot)', 'Gemini (language model)', 'Claude (language model)']\n", + "- id: GPT-4.5\n", + " url: https://en.wikipedia.org/wiki/GPT-4.5\n", + " text: GPT-4.5 (codenamed \"Orion\") is a large language model developed by OpenAI as part of the GPT series. Officially released on February 27, 2025, GPT-4.5 is available to users subscribed to the ChatGPT Plus and Pro plans across web, mobile, and desktop platforms. Access is also provided through the OpenAI API and the OpenAI Developer Playground, but the company plans to phase out API access to the model in July.\n", + " links: ['GPT-4.1', 'GPT-4', 'ChatGPT', 'GPT-3', 'OpenAI', 'Claude (language model)', 'Gemini (language model)', 'Anthropic', 'Gemini (chatbot)']\n", + "- id: OpenAI\n", + " url: https://en.wikipedia.org/wiki/OpenAI\n", + " text: OpenAI, Inc. is an American artificial intelligence (AI) organization founded in December 2015 and headquartered in San Francisco, California. It aims to develop \"safe and beneficial\" artificial general intelligence (AGI), which it defines as \"highly autonomous systems that outperform humans at most economically valuable work\". As a leading organization in the ongoing AI boom, OpenAI is known for the GPT family of large language models, the DALL-E series of text-to-image models, and a text-to-video model named Sora. Its release of ChatGPT in November 2022 has been credited with catalyzing widespread interest in generative AI.\n", + " links: ['ChatGPT', 'GPT-4', 'GPT-3', 'GPT-4.5', 'Anthropic', 'GPT-4.1', 'Gemini (chatbot)', 'Gemini (language model)']\n", + "- id: Gemini (chatbot)\n", + " url: https://en.wikipedia.org/wiki/Gemini_(chatbot)\n", + " text: Gemini, formerly known as Bard, is a generative artificial intelligence chatbot developed by Google. Based on the large language model (LLM) of the same name, it was launched in 2023 in response to the rise of OpenAI's ChatGPT. It was previously based on the LaMDA and PaLM LLMs.\n", + " links: ['Gemini (language model)', 'ChatGPT', 'GPT-4', 'Anthropic', 'OpenAI', 'GPT-4.5']\n", + "- id: GPT-3\n", + " url: https://en.wikipedia.org/wiki/GPT-3\n", + " text: Generative Pre-trained Transformer 3 (GPT-3) is a large language model released by OpenAI in 2020.\n", + " links: ['GPT-4', 'GPT-4.1', 'ChatGPT', 'OpenAI', 'GPT-4.5', 'Claude (language model)', 'Gemini (language model)']\n", + "- id: GPT-4.1\n", + " url: https://en.wikipedia.org/wiki/GPT-4.1\n", + " text: GPT-4.1 is a large language model within OpenAI's GPT series. It was released on April 14, 2025. GPT-4.1 can be accessed through the OpenAI API or the OpenAI Developer Playground. Three different models were simultaneously released: GPT-4.1, GPT-4.1 mini, and GPT-4.1 nano.\n", + " links: ['GPT-4.5', 'GPT-4', 'GPT-3', 'ChatGPT', 'OpenAI', 'Gemini (language model)', 'Claude (language model)']\n", + "- id: Gemini (language model)\n", + " url: https://en.wikipedia.org/wiki/Gemini_(language_model)\n", + " text: Gemini is a family of multimodal large language models (LLMs) developed by Google DeepMind, and the successor to LaMDA and PaLM 2. Comprising Gemini Ultra, Gemini Pro, Gemini Flash, and Gemini Nano, it was announced on December 6, 2023, positioned as a competitor to OpenAI's GPT-4. It powers the chatbot of the same name. In March 2025, Gemini 2.5 Pro Experimental was rated as highly competitive.\n", + " links: ['Gemini (chatbot)', 'GPT-4', 'ChatGPT', 'GPT-4.5', 'GPT-4.1', 'GPT-3', 'OpenAI', 'Anthropic']\n", + "- id: Anthropic\n", + " url: https://en.wikipedia.org/wiki/Anthropic\n", + " text: Anthropic PBC is an American artificial intelligence (AI) startup company founded in 2021. Anthropic has developed a family of large language models (LLMs) named Claude as a competitor to OpenAI's ChatGPT and Google's Gemini. According to the company, it researches and develops AI to \"study their safety properties at the technological frontier\" and use this research to deploy safe models for the public.\n", + " links: ['Claude (language model)', 'OpenAI', 'ChatGPT', 'Gemini (chatbot)', 'GPT-4.5', 'Gemini (language model)']\n", + "- id: Claude (language model)\n", + " url: https://en.wikipedia.org/wiki/Claude_(language_model)\n", + " text: Claude is a family of large language models developed by Anthropic. The first model was released in March 2023.\n", + " links: ['Anthropic', 'GPT-3', 'GPT-4.5', 'GPT-4', 'ChatGPT', 'GPT-4.1']\n", + "\n" + ] + } + ], + "source": [ + "print(context)" + ] + }, + { + "cell_type": "markdown", + "id": "7e0fa70a", + "metadata": {}, + "source": [ + "# GraphRAG\n", + "\n", + "Now that we have our graph context, we'll plug that into an LLM prompt." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c1465b3f", + "metadata": {}, + "outputs": [], + "source": [ + "from txtai import LLM\n", + "\n", + "llm = LLM(\"unsloth/gpt-oss-20b-GGUF/gpt-oss-20b-Q4_K_M.gguf\", n_ctx=20000)" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "id": "74b4692c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "**ChatGPT, GPT‑4, and the New Generation of Generative AI: A Timeline of Innovation and Impact**\n", + "\n", + "*By [Your Name]* \n", + "*Published: 2025‑09‑03*\n", + "\n", + "---\n", + "\n", + "### 1. The Dawn of Generative AI\n", + "\n", + "The field of artificial intelligence (AI) has long promised “highly autonomous systems that outperform humans at most economically valuable work.” In practice, the most visible manifestation of that promise has been the rapid rise of large language models (LLMs) that can generate text, speech, and even images that read like they were written by a human. The most influential of these models has come from a handful of companies—OpenAI, Google DeepMind, and Anthropic—each building a family of models that have pushed the boundaries of what machines can do.\n", + "\n", + "---\n", + "\n", + "### 2. OpenAI’s GPT Series\n", + "\n", + "| Model | Release | Key Features | Notes |\n", + "|-------|---------|---------------|-------|\n", + "| **GPT‑3** | 2020 | 175 billion parameters; first public GPT model | Laid the groundwork for conversational AI |\n", + "| **ChatGPT** | 2022‑11‑30 | Uses GPT‑4o and multimodal models; web‑search, app‑integration, program execution | Sparked the “AI boom” and widespread public interest |\n", + "| **GPT‑4** | 2023‑03‑14 | Multimodal; released via ChatGPT Plus, API, Microsoft Copilot | Became the de‑facto standard for LLM‑based chat |\n", + "| **GPT‑4.1** | 2025‑04‑14 | Three variants (mini, nano) released simultaneously | Improved safety and performance |\n", + "| **GPT‑4.5** | 2025‑02‑27 | Codename “Orion”; API access to be phased out in July | Highest‑performance model in the GPT line |\n", + "\n", + "OpenAI’s mission—“safe and beneficial” artificial general intelligence—has guided the evolution of these models. The company’s public releases have been accompanied by a steady stream of research papers, API documentation, and developer playgrounds that allow researchers and businesses to experiment with the models at scale.\n", + "\n", + "---\n", + "\n", + "### 3. Google DeepMind’s Gemini\n", + "\n", + "Google’s response to the GPT wave came in 2023 with **Gemini (chatbot)**, a generative AI chatbot that replaced the earlier Bard. Gemini is powered by the **Gemini (language model)** family, which includes Gemini Ultra, Pro, Flash, and Nano. The models were announced on 2023‑12‑06 and positioned as direct competitors to GPT‑4. In March 2025, Gemini 2.5 Pro Experimental was rated as “highly competitive,” underscoring the rapid parity between the two ecosystems.\n", + "\n", + "---\n", + "\n", + "### 4. Anthropic’s Claude\n", + "\n", + "Founded in 2021, **Anthropic PBC** has focused on the safety properties of AI. Their flagship LLM family, **Claude**, was first released in March 2023. Claude is marketed as a competitor to both ChatGPT and Gemini, with a particular emphasis on “safe models for the public.” Anthropic’s research agenda—studying safety at the technological frontier—has positioned it as a counter‑balance to the commercial focus of OpenAI and Google.\n", + "\n", + "---\n", + "\n", + "### 5. The Feature Set that Changed the Game\n", + "\n", + "ChatGPT’s launch was not just a new model; it was a new **feature set**:\n", + "\n", + "* **Web Search** – The ability to query up‑to‑date information in real time. \n", + "* **App Integration** – Running third‑party applications directly from the chat interface. \n", + "* **Program Execution** – The capacity to run code snippets and return results. \n", + "\n", + "These capabilities turned a simple chatbot into a *digital assistant* that can browse, compute, and even generate images (via DALL‑E) or video (via Sora). The result was a surge in both consumer and enterprise adoption.\n", + "\n", + "---\n", + "\n", + "### 6. Societal Impact and Concerns\n", + "\n", + "The rapid adoption of generative AI has accelerated the **AI boom**—a period of intense investment and public attention. Yet it has also raised legitimate concerns:\n", + "\n", + "* **Displacement of Human Intelligence** – Critics worry that advanced LLMs could replace human expertise in fields ranging from journalism to law. \n", + "* **Plagiarism and Academic Integrity** – The ease of producing high‑quality text has made it harder to detect original work. \n", + "* **Misinformation** – Models can generate plausible but false narratives, amplifying the spread of fake news. \n", + "\n", + "OpenAI, Google, and Anthropic have all invested in safety research, but the debate continues over how best to balance innovation with responsibility.\n", + "\n", + "---\n", + "\n", + "### 7. Looking Ahead\n", + "\n", + "The trajectory of generative AI suggests a few key trends:\n", + "\n", + "1. **Continued Model Scaling** – GPT‑4.5 and GPT‑4.1 demonstrate that larger models still deliver incremental gains. \n", + "2. **Multimodal Integration** – Future releases will likely blend text, image, audio, and video more tightly. \n", + "3. **Regulatory Engagement** – Governments and industry groups are beginning to draft guidelines for AI safety and transparency. \n", + "4. **Democratization of Access** – APIs and developer playgrounds are making advanced AI available to a broader audience, from hobbyists to large enterprises. \n", + "\n", + "---\n", + "\n", + "### 8. Conclusion\n", + "\n", + "From GPT‑3’s 175 billion parameters to GPT‑4.5’s “Orion” codename, the generative AI landscape has evolved at a breakneck pace. OpenAI’s ChatGPT catalyzed a wave of public fascination, while Google’s Gemini and Anthropic’s Claude have kept the competition fierce. As these models become more capable, the conversation around safety, ethics, and societal impact will only grow more urgent. The next few years will likely see generative AI move from a novelty to a foundational technology—one that will shape how we write, compute, and even think.\n", + "\n", + "---\n", + "\n", + "**References**\n", + "\n", + "* ChatGPT – https://en.wikipedia.org/wiki/ChatGPT \n", + "* GPT‑4 – https://en.wikipedia.org/wiki/GPT-4 \n", + "* GPT‑4.5 – https://en.wikipedia.org/wiki/GPT-4.5 \n", + "* OpenAI – https://en.wikipedia.org/wiki/OpenAI \n", + "* Gemini (chatbot) – https://en.wikipedia.org/wiki/Gemini_(chatbot) \n", + "* GPT‑3 – https://en.wikipedia.org/wiki/GPT-3 \n", + "* GPT‑4.1 – https://en.wikipedia.org/wiki/GPT-4.1 \n", + "* Gemini (language model) – https://en.wikipedia.org/wiki/Gemini_(language_model) \n", + "* Anthropic – https://en.wikipedia.org/wiki/Anthropic \n", + "* Claude (language model) – https://en.wikipedia.org/wiki/Claude_(language_model) \n", + "\n", + "---" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import display, Markdown\n", + "\n", + "out = llm(f\"\"\"\n", + "Analyze the following context and write an article about it\n", + "{context}\n", + "\"\"\", defaultrole=\"user\", maxlength=20000, stripthink=True)\n", + "\n", + "display(Markdown(out))" + ] + }, + { + "cell_type": "markdown", + "id": "2ecf44b7", + "metadata": {}, + "source": [ + "# Wrapping up\n", + "\n", + "There we have it, GraphRAG in a very straightforward and easy-to-understand manner. The best ideas often are the simple ones!\n", + "\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "local", + "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", + "version": "3.10.18" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/78_Accessing_Low_Level_Vector_APIs.ipynb b/examples/78_Accessing_Low_Level_Vector_APIs.ipynb new file mode 100644 index 0000000..6b51b81 --- /dev/null +++ b/examples/78_Accessing_Low_Level_Vector_APIs.ipynb @@ -0,0 +1,482 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "64be0343", + "metadata": {}, + "source": [ + "# Accessing Low Level Vector APIs\n", + "\n", + "The primary interface to build vector databases with `txtai` is through [Embeddings instances](https://neuml.github.io/txtai/embeddings/). `txtai` also supports accessing all of it's features through lower level APIs. \n", + "\n", + "Let's dive in.\n" + ] + }, + { + "cell_type": "markdown", + "id": "e8198312", + "metadata": {}, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3f2d0792", + "metadata": {}, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[ann] gguf" + ] + }, + { + "cell_type": "markdown", + "id": "46740a21", + "metadata": {}, + "source": [ + "# Load a dataset\n", + "\n", + "We'll use a [subset](https://huggingface.co/datasets/m-a-p/FineFineWeb-test) of the [FineFineWeb dataset](https://huggingface.co/datasets/m-a-p/FineFineWeb). This dataset is a domain-labeled version of the general purpose [FineWeb dataset](https://huggingface.co/datasets/HuggingFaceFW/fineweb). " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3c13e554", + "metadata": {}, + "outputs": [], + "source": [ + "from datasets import load_dataset\n", + "\n", + "ds = load_dataset(\"m-a-p/FineFineWeb-test\", split=\"train\")" + ] + }, + { + "cell_type": "markdown", + "id": "ae49af96", + "metadata": {}, + "source": [ + "# Building an Embeddings database\n", + "\n", + "Before going into the low-level API, let's recap how we build an Embeddings database." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "2de3bf4c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.6012564897537231 The National Aeronautics and Space Administration (NASA) is the United States’ civil space program. \n" + ] + } + ], + "source": [ + "from txtai import Embeddings\n", + "\n", + "embeddings = Embeddings()\n", + "embeddings.index(ds[\"text\"][:10000])\n", + "for uid, score in embeddings.search(\"nasa\", 1):\n", + " print(score, ds[uid][\"text\"][:100])" + ] + }, + { + "cell_type": "markdown", + "id": "d6f196cb", + "metadata": {}, + "source": [ + "This simple example abstracts the heavy lifting behind the `Embeddings` interface. Behind the scenes, it defaults to vectorizing text using [all-MiniLM-L6-v2](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2). Vectors are stored in a [Faiss index](https://github.com/facebookresearch/faiss).\n", + "\n", + "The first 10K records are vectorized and stored in the vector index. Then at query time, the query is vectorized and a vector similarity search is run.\n", + "\n", + "While the `Embeddings` interface is convenient, it's also possible to access lower level APIs. " + ] + }, + { + "cell_type": "markdown", + "id": "ecd576e8", + "metadata": {}, + "source": [ + "# Vectors Interface\n", + "\n", + "First, let's vectorize our data using the low level APIs. We'll use the default Hugging Face vectorizer available in `txtai`." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "03c85684", + "metadata": {}, + "outputs": [], + "source": [ + "from txtai.ann import ANNFactory\n", + "from txtai.vectors import VectorsFactory\n", + "\n", + "vectors = VectorsFactory.create({\"path\": \"sentence-transformers/all-MiniLM-L6-v2\"})\n", + "data = vectors.vectorize(ds[\"text\"])" + ] + }, + { + "cell_type": "markdown", + "id": "36fac81a", + "metadata": {}, + "source": [ + "# ANN Interface\n", + "\n", + "Now that we have a NumPy array of vectors, let's store them in an Approximate Neighest Neighbor (ANN) backend. Recall earlier, we used the default Faiss interface. For this example, we're going to use the [PyTorch ANN](https://neuml.github.io/txtai/embeddings/configuration/ann/#torch). This will allow us to use new features that are available as of `txtai` 9.1." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "34ebdc85", + "metadata": {}, + "outputs": [], + "source": [ + "ann = ANNFactory.create({\n", + " \"backend\": \"torch\",\n", + " \"torch\": {\n", + " \"safetensors\": True,\n", + " }\n", + "})\n", + "ann.index(data)\n", + "ann.save(\"vectors.safetensors\")" + ] + }, + { + "cell_type": "markdown", + "id": "98a26c9a", + "metadata": {}, + "source": [ + "This ANN builds a Torch tensor with the vectors and stores them in a [Safetensors](https://github.com/huggingface/safetensors) file.\n", + "\n", + "The code below shows how the file is simply a standard Safetensors file." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "bcaa4832", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "data (1411868, 384)\n", + "Memory = 2068.17 MB\n" + ] + } + ], + "source": [ + "from safetensors import safe_open\n", + "\n", + "def tensorinfo():\n", + " memory = 0\n", + " with safe_open(\"vectors.safetensors\", framework=\"np\") as f:\n", + " for key in f.keys():\n", + " array = f.get_tensor(key)\n", + " print(key, array.shape)\n", + " memory += array.nbytes\n", + "\n", + " print(f\"Memory = {memory / 1024 / 1024:.2f} MB\")\n", + "\n", + "tensorinfo()" + ] + }, + { + "cell_type": "markdown", + "id": "d6525b1a", + "metadata": {}, + "source": [ + "# Vector search\n", + "\n", + "Now let's show how these low-level APIs can be used to implement vector search." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "09b81958", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The answer to your question, that how many miles is it from earth to mars, is very easy to know. Because of huge satellites which are being sent to\n", + "mars in search of life from many countries, we have discovered a lot about mars. According to experts, earth and mars reaches to their closest points\n", + "in every 26 months. This situation is considered as opposition of mars as the location of sun and mars in totally opposite to each other in relation\n", + "to earth. When this opposition takes place, the planet is visible with a red tint in the sky from earth. And this also gives mars a name, i.e. the red\n", + "planet. Mars is also the fourth planet from sun, which is located between Jupiter and earth. Its distance from sun is not only opposite but is also\n", + "much further away, than that of the earth and sun. The distance between the sun and mars is said to be 140 million miles. Mars can reach about 128\n", + "million miles closer to the sun whereas it can even travel around 154 million miles away from it. The assumed distance between mars and earth is said\n", + "to be between 40 to 225 million miles. The distance between these two planets keeps on changing throughout the year because of the elliptical path in\n", + "which all the planets rotate. As the distance between mars, sun and earth is so much high, it takes a Martian year, for mars to go around the sun. The\n", + "Martian period includes a time of around 687 earth days. This means that, it takes more than 2 years for the mars to reach its initial rotation point.\n", + "If we talk about one Martian day, it is the total time which is taken by a planet to spin around once. This day usually lasts longer than our regular\n", + "earth days. So this was the actual reason which states the distance between earth and mars. \n", + "\n", + " 0.7060051560401917\n" + ] + } + ], + "source": [ + "import textwrap\n", + "\n", + "def search(text):\n", + " result = ann.search(vectors.vectorize([text]), 1)\n", + " index, score = result[0][0]\n", + " print(textwrap.fill(ds[index][\"text\"], width=150), \"\\n\\n\", score)\n", + "\n", + "search(\"How far is earth from mars?\")" + ] + }, + { + "cell_type": "markdown", + "id": "8adf64db", + "metadata": {}, + "source": [ + "# Torch 4-bit quantization\n", + "\n", + "`txtai` 9.1 adds a new feature: 4-bit vector quantization. This means that instead of using 32-bit floats for each vector dimension, this method uses 4 bits. This reduces memory usage to ~12-13% of the original size." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "cbe5af13", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "absmax (8471208,)\n", + "code (16,)\n", + "data (271078656, 1)\n", + "shape (2,)\n", + "Memory = 290.84 MB\n" + ] + } + ], + "source": [ + "ann = ANNFactory.create({\n", + " \"backend\": \"torch\",\n", + " \"torch\": {\n", + " \"safetensors\": True,\n", + " \"quantize\": {\n", + " \"type\": \"nf4\"\n", + " }\n", + " }\n", + "})\n", + "ann.index(data)\n", + "ann.save(\"vectors.safetensors\")\n", + "\n", + "tensorinfo()" + ] + }, + { + "cell_type": "markdown", + "id": "ca7b1e0e", + "metadata": {}, + "source": [ + "Note how the unquantized vectors took 2068.17 MB and this only takes 290.84 MB! With quantization and ever growing GPUs, this opens the possibility of pinning your entire vector database in GPU memory!\n", + "\n", + "For example, let's extrapolate this dataset to 100M rows.\n", + "\n", + "```\n", + "(290.84 MB / 1,411,868) * 100,000,000 = 20,599.7 MB\n", + "```\n", + "\n", + "An entire 100M row dataset could fit into a single RTX 3090 / 4090 consumer GPU!\n", + "\n", + "Let's confirm search still works the same." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "a02578dd", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The answer to your question, that how many miles is it from earth to mars, is very easy to know. Because of huge satellites which are being sent to\n", + "mars in search of life from many countries, we have discovered a lot about mars. According to experts, earth and mars reaches to their closest points\n", + "in every 26 months. This situation is considered as opposition of mars as the location of sun and mars in totally opposite to each other in relation\n", + "to earth. When this opposition takes place, the planet is visible with a red tint in the sky from earth. And this also gives mars a name, i.e. the red\n", + "planet. Mars is also the fourth planet from sun, which is located between Jupiter and earth. Its distance from sun is not only opposite but is also\n", + "much further away, than that of the earth and sun. The distance between the sun and mars is said to be 140 million miles. Mars can reach about 128\n", + "million miles closer to the sun whereas it can even travel around 154 million miles away from it. The assumed distance between mars and earth is said\n", + "to be between 40 to 225 million miles. The distance between these two planets keeps on changing throughout the year because of the elliptical path in\n", + "which all the planets rotate. As the distance between mars, sun and earth is so much high, it takes a Martian year, for mars to go around the sun. The\n", + "Martian period includes a time of around 687 earth days. This means that, it takes more than 2 years for the mars to reach its initial rotation point.\n", + "If we talk about one Martian day, it is the total time which is taken by a planet to spin around once. This day usually lasts longer than our regular\n", + "earth days. So this was the actual reason which states the distance between earth and mars. \n", + "\n", + " 0.6982609033584595\n" + ] + } + ], + "source": [ + "search(\"How far is earth from mars?\")" + ] + }, + { + "cell_type": "markdown", + "id": "f0e62670", + "metadata": {}, + "source": [ + "Same result. Note the score is slightly different but this is expected." + ] + }, + { + "cell_type": "markdown", + "id": "89abb301", + "metadata": {}, + "source": [ + "# GGUF Support\n", + "\n", + "`txtai` 9.1 also adds support for [GGML](https://github.com/ggml-org/ggml) / [GGUF](https://huggingface.co/docs/hub/en/gguf) popularized by [llama.cpp](https://github.com/ggml-org/llama.cpp)." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "e04498fc", + "metadata": {}, + "outputs": [], + "source": [ + "ann = ANNFactory.create({\n", + " \"backend\": \"ggml\",\n", + " \"ggml\": {\n", + " \"quantize\": \"Q4_0\"\n", + " }\n", + "})\n", + "ann.index(data)\n", + "ann.save(\"vectors.gguf\")" + ] + }, + { + "cell_type": "markdown", + "id": "7bea0f58", + "metadata": {}, + "source": [ + "Now let's check out the generated file using the [gguf](https://github.com/ggml-org/llama.cpp/tree/master/gguf-py) package provided by llama.cpp." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "b0e6f85e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Tensor Name | Shape | Size | Quantization\n", + "--------------------------------------------------------------------------------\n", + "data | 384x1411868 | 258.52 MB | Q4_0\n" + ] + } + ], + "source": [ + "from gguf.gguf_reader import GGUFReader\n", + "\n", + "reader = GGUFReader(\"vectors.gguf\")\n", + "\n", + "# List all tensors\n", + "info = \"{:<30} | {:<15} | {:<12} | {}\"\n", + "print(info.format(\"Tensor Name\", \"Shape\", \"Size\", \"Quantization\"))\n", + "print(\"-\" * 80)\n", + "for tensor in reader.tensors:\n", + " shape = \"x\".join(map(str, tensor.shape))\n", + " size = f\"{tensor.n_elements / 2 / 1024 / 1024:.2f} MB\"\n", + " quantization = tensor.tensor_type.name\n", + " print(info.format(tensor.name, shape, size, quantization))" + ] + }, + { + "cell_type": "markdown", + "id": "7be79bf7", + "metadata": {}, + "source": [ + "And search like we did with Torch." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "450dc024", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The answer to your question, that how many miles is it from earth to mars, is very easy to know. Because of huge satellites which are being sent to\n", + "mars in search of life from many countries, we have discovered a lot about mars. According to experts, earth and mars reaches to their closest points\n", + "in every 26 months. This situation is considered as opposition of mars as the location of sun and mars in totally opposite to each other in relation\n", + "to earth. When this opposition takes place, the planet is visible with a red tint in the sky from earth. And this also gives mars a name, i.e. the red\n", + "planet. Mars is also the fourth planet from sun, which is located between Jupiter and earth. Its distance from sun is not only opposite but is also\n", + "much further away, than that of the earth and sun. The distance between the sun and mars is said to be 140 million miles. Mars can reach about 128\n", + "million miles closer to the sun whereas it can even travel around 154 million miles away from it. The assumed distance between mars and earth is said\n", + "to be between 40 to 225 million miles. The distance between these two planets keeps on changing throughout the year because of the elliptical path in\n", + "which all the planets rotate. As the distance between mars, sun and earth is so much high, it takes a Martian year, for mars to go around the sun. The\n", + "Martian period includes a time of around 687 earth days. This means that, it takes more than 2 years for the mars to reach its initial rotation point.\n", + "If we talk about one Martian day, it is the total time which is taken by a planet to spin around once. This day usually lasts longer than our regular\n", + "earth days. So this was the actual reason which states the distance between earth and mars. \n", + "\n", + " 0.7043964862823486\n" + ] + } + ], + "source": [ + "search(\"How far is earth from mars?\")" + ] + }, + { + "cell_type": "markdown", + "id": "af1a344c", + "metadata": {}, + "source": [ + "# Wrapping up\n", + "\n", + "While the `Embeddings` interface is the preferred way to build vector databases with `txtai`, it's entirely possible to also build with the low level APIs!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "local", + "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", + "version": "3.10.19" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/79_RAG_is_more_than_Vector_Search.ipynb b/examples/79_RAG_is_more_than_Vector_Search.ipynb new file mode 100644 index 0000000..f4727c7 --- /dev/null +++ b/examples/79_RAG_is_more_than_Vector_Search.ipynb @@ -0,0 +1,251 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "4c8ab3b0", + "metadata": {}, + "source": [ + "# RAG is more than Vector Search\n", + "\n", + "Retrieval Augmented Generation (RAG) is often associated with vector search. And while that is a primary use case, any search will do.\n", + "\n", + "- ✅ Vector Search\n", + "- ✅ Web Search\n", + "- ✅ SQL Query\n", + "\n", + "This notebook will go over a few RAG examples covering different retrieval methods. These examples require txtai 9.3+." + ] + }, + { + "cell_type": "markdown", + "id": "ab8a77d1", + "metadata": {}, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21bebb46", + "metadata": {}, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline-data]\n", + "\n", + "# Download example SQL database\n", + "!wget https://huggingface.co/NeuML/txtai-wikipedia-slim/resolve/main/documents" + ] + }, + { + "cell_type": "markdown", + "id": "1ece2b09", + "metadata": {}, + "source": [ + "# RAG with Late Interaction\n", + "\n", + "The first example will cover RAG with ColBERT / Late Interaction retrieval. TxtAI 9.0 added support for [MUVERA](https://arxiv.org/abs/2405.19504) and [ColBERT](https://arxiv.org/abs/2112.01488) multi-vector ranking. \n", + "\n", + "We'll build a pipeline that reads the ColBERT v2 paper, extracts the text into sections and builds an index with a ColBERT model. Then we'll wrap that as a [Reranker pipeline](https://neuml.github.io/txtai/pipeline/text/reranker/) using the same ColBERT model. Finally a RAG pipeline will utilize this for retrieval.\n", + "\n", + "_Note: This uses the custom [ColBERT Muvera Nano](https://huggingface.co/NeuML/colbert-muvera-nano) model which is only 970K parameters! That's right thousands. It's surprisingly effective._" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "dae2e6dc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This paper introduces ColBERTv2, a neural information retrieval model that enhances the quality and efficiency of late interaction by combining an aggressive residual compression mechanism with a denoised supervision strategy, achieving state-of-the-art performance across diverse benchmarks while reducing the model's space footprint by 6–10× compared to previous methods.\n" + ] + } + ], + "source": [ + "from txtai import Embeddings, RAG, Textractor\n", + "from txtai.pipeline import Reranker, Similarity\n", + "\n", + "# Get text from ColBERT v2 paper\n", + "textractor = Textractor(sections=True, backend=\"docling\")\n", + "data = textractor(\"https://arxiv.org/pdf/2112.01488\")\n", + "\n", + "# MUVERA fixed dimensional encodings\n", + "embeddings = Embeddings(content=True, path=\"neuml/colbert-muvera-nano\", vectors={\"trust_remote_code\": True})\n", + "embeddings.index(data)\n", + "\n", + "# Re-rank using same late interaction model\n", + "reranker = Reranker(embeddings, Similarity(\"neuml/colbert-muvera-nano\", lateencode=True, vectors={\"trust_remote_code\": True}))\n", + "\n", + "template = \"\"\"\n", + " Answer the following question using the provided context.\n", + "\n", + " Question:\n", + " {question}\n", + "\n", + " Context:\n", + " {context}\n", + "\"\"\"\n", + "\n", + "# RAG with late interaction models\n", + "rag = RAG(reranker, \"Qwen/Qwen3-4B-Instruct-2507\", template=template, output=\"flatten\")\n", + "print(rag(\"Write a sentence abstract about this paper\", maxlength=2048))" + ] + }, + { + "cell_type": "markdown", + "id": "b31e46de", + "metadata": {}, + "source": [ + "# RAG with a Web Search\n", + "\n", + "Next we'll run a RAG pipeline using a web search as the retrieval method." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "b61b684b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Artificial intelligence (AI) is the capability of computational systems to perform tasks typically associated with human intelligence, such as learning, reasoning, problem-solving, perception, and decision-making. It involves technologies like machine learning, deep learning, and natural language processing, and enables machines to simulate human-like learning, comprehension, problem solving, decision-making, creativity, and autonomy.\n" + ] + } + ], + "source": [ + "from smolagents import WebSearchTool\n", + "\n", + "tool = WebSearchTool()\n", + "\n", + "def websearch(queries, limit):\n", + " results = []\n", + " for query in queries:\n", + " result = [\n", + " {\"id\": i, \"text\": f'{x[\"title\"]} {x[\"description\"]}', \"score\": 1.0} for i, x in enumerate(tool.search(query))\n", + " ]\n", + " results.append(result[:limit])\n", + "\n", + " return results\n", + "\n", + "# RAG with a websearch\n", + "rag = RAG(websearch, \"Qwen/Qwen3-4B-Instruct-2507\", template=template, output=\"flatten\")\n", + "print(rag(\"What is AI?\", maxlength=2048))" + ] + }, + { + "cell_type": "markdown", + "id": "45c8a096", + "metadata": {}, + "source": [ + "# RAG with a SQL Query\n", + "\n", + "The last example we'll cover is running RAG with a SQL query. We'll use the SQL database that's a component of the [txtai-wikipedia-slim](https://huggingface.co/NeuML/txtai-wikipedia-slim) embeddings database.\n", + "\n", + "Since this is just a database with Wikipedia abstracts, we'll need a way to build a SQL query from a search query. For that we'll use an LLM to extract a keyword to use in a `LIKE` clause.\n", + "\n", + "Given that the LLM used was released in August 2025, let's ask it a question that can only be accurated answered with external data. `Who won the 2025 World Series?` which ended in November." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c7ff2a35", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "In the 2025 World Series, the Los Angeles Dodgers defeated the Toronto Blue Jays in seven games to win the championship. The series took place from October 24 to November 1 (ending early on November 2, Toronto time). Dodgers pitcher Yoshinobu Yamamoto was named the World Series MVP. The series was televised by Fox in the United States and by Sportsnet in Canada.\n" + ] + } + ], + "source": [ + "import sqlite3\n", + "\n", + "from txtai import LLM\n", + "\n", + "def keyword(query):\n", + " return llm(f\"\"\"\n", + " Extract a keyword for this search query: {query}.\n", + " Return only text with no other formatting or explanation.\n", + " \"\"\")\n", + "\n", + "def sqlsearch(queries, limit):\n", + " results = []\n", + " sql = \"SELECT id, text FROM sections WHERE id LIKE ? LIMIT ?\"\n", + "\n", + " for query in queries:\n", + " # Extract a keyword for this search\n", + " query = keyword(query)\n", + "\n", + " # Run the SQL Query\n", + " results.append([\n", + " {\"id\": uid, \"text\": text, \"score\": 1.0}\n", + " for uid, text in cursor.execute(sql, [f\"%{query}%\", limit])\n", + " ])\n", + "\n", + " return results\n", + "\n", + "# Load the database\n", + "cursor = sqlite3.connect(\"documents\")\n", + "\n", + "# Load the LLM\n", + "llm = LLM(\"Qwen/Qwen3-4B-Instruct-2507\")\n", + "\n", + "# RAG with a SQL query\n", + "rag = RAG(sqlsearch, llm, template=template, output=\"flatten\")\n", + "print(rag(\"Tell me what happened in the 2025 World Series\", maxlength=2048))" + ] + }, + { + "cell_type": "markdown", + "id": "a9f78076", + "metadata": {}, + "source": [ + "And as we see, this answer is using the SQL database!" + ] + }, + { + "cell_type": "markdown", + "id": "97b1e282", + "metadata": {}, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook showed that RAG is about much more than vector search. With txtai 9.3+, any callable method is now supported for retrieval. Enjoy!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "local", + "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", + "version": "3.10.19" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/80_Distilling_Knowledge_into_Tiny_LLMs.ipynb b/examples/80_Distilling_Knowledge_into_Tiny_LLMs.ipynb new file mode 100644 index 0000000..365cee2 --- /dev/null +++ b/examples/80_Distilling_Knowledge_into_Tiny_LLMs.ipynb @@ -0,0 +1,360 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c65bdb85", + "metadata": {}, + "source": [ + "# Distilling Knowledge into Tiny LLMs\n", + "\n", + "Large Language Models (LLMs) are the magic behind AI. These massive billion and trillion parameter models have been shown to generalize well when trained on enough data.\n", + "\n", + "A big problem is that they are hard to run and expensive. So many just call LLMs through APIs such as OpenAI or Claude. Additionally, in many instances, developers spend a lot of time with complex prompt logic hoping to cover all the edge cases and believe they need a model that's large enough to handle all the rules.\n", + "\n", + "If you truly want control over your business processes, running a local model is a better choice. And the good news is that it doesn't have to be a giant and expensive multi-billion parameter model. We can finetune LLMs to handle our specific business logic, which helps us take control and limit prompt complexity. \n", + "\n", + "This notebook will show how we can distill knowledge into tiny LLMs." + ] + }, + { + "cell_type": "markdown", + "id": "279ecd4b", + "metadata": {}, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4f7bca3a", + "metadata": {}, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline-train] datasets" + ] + }, + { + "cell_type": "markdown", + "id": "c7b1b4be", + "metadata": {}, + "source": [ + "# The LLM\n", + "\n", + "We'll use a [600M parameter Qwen3 model](https://hf.co/qwen/qwen3-0.6b) for this example. Our target task will be translating user requests into linux commands." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "e1d95dd8", + "metadata": {}, + "outputs": [], + "source": [ + "from txtai import LLM\n", + "\n", + "llm = LLM(\"Qwen/Qwen3-0.6B\")" + ] + }, + { + "cell_type": "markdown", + "id": "8f276cfc", + "metadata": {}, + "source": [ + "Let's try one with the base model as it is." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "686c3983", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'ps -e'" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "llm(\"\"\"\n", + "Translate the following request into a linux command. Only print the command.\n", + "\n", + "Find number of logged in users\n", + "\"\"\", maxlength=1024)" + ] + }, + { + "cell_type": "markdown", + "id": "dcd92414", + "metadata": {}, + "source": [ + "As we can see, the model actually has a good understanding and at least prints a command. But in this case it's not correct. Let's get to fine-tuning!" + ] + }, + { + "cell_type": "markdown", + "id": "73666982", + "metadata": {}, + "source": [ + "# Finetuning the LLM with knowledge\n", + "\n", + "Yes, 600M parameters is small and we can't possibly expect it to do well with everything. But the good news is that we can distill knowledge into this tiny LLM and make it better. We'll use this [linux commands dataset](https://huggingface.co/datasets/mecha-org/linux-command-dataset) from the Hugging Face Hub. We'll also use this [training pipeline from txtai](https://neuml.github.io/txtai/pipeline/train/trainer).\n", + "\n", + "First, we'll create the training dataset. We'll use the same prompt strategy from above.\n", + "\n", + "```python\n", + "\"\"\"\n", + "Translate the following request into a linux command. Only print the command.\n", + "\n", + "{user request}\n", + "\"\"\"\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "43d6b563", + "metadata": {}, + "outputs": [], + "source": [ + "from datasets import load_dataset\n", + "from transformers import AutoTokenizer\n", + "\n", + "# LLM path\n", + "path = \"Qwen/Qwen3-0.6B\"\n", + "tokenizer = AutoTokenizer.from_pretrained(path)\n", + "\n", + "# Load the training dataset\n", + "dataset = load_dataset(\"mecha-org/linux-command-dataset\", split=\"train\")\n", + "\n", + "def prompt(row):\n", + " text = tokenizer.apply_chat_template([\n", + " {\"role\": \"system\", \"content\": \"Translate the following request into a linux command. Only print the command.\"},\n", + " {\"role\": \"user\", \"content\": row[\"input\"]},\n", + " {\"role\": \"assistant\", \"content\": row[\"output\"]}\n", + " ], tokenize=False, enable_thinking=False)\n", + "\n", + " return {\"text\": text}\n", + "\n", + "# Map to training prompts\n", + "train = dataset.map(prompt, remove_columns=[\"input\", \"output\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "7f71dce0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
\n", + " \n", + " \n", + " [210/210 01:12, Epoch 1/1]\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
StepTraining Loss
500.625300
1000.490200
1500.403300
2000.391800

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from txtai.pipeline import HFTrainer\n", + "\n", + "# Load the training pipeline\n", + "trainer = HFTrainer()\n", + "\n", + "# Train the model\n", + "# Set output_dir to save, trained in memory for this example\n", + "model = trainer(\n", + " \"Qwen/Qwen3-0.6B\",\n", + " train,\n", + " task=\"language-generation\",\n", + " maxlength=512,\n", + " bf16=True,\n", + " per_device_train_batch_size=4,\n", + " num_train_epochs=1,\n", + " logging_steps=50,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "68b5bc4e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'who | wc -l'" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from txtai import LLM\n", + "\n", + "llm = LLM(model)\n", + "\n", + "llm([\n", + " {\"role\": \"system\", \"content\": \"Translate the following request into a linux command. Only print the command.\"},\n", + " {\"role\": \"user\", \"content\": \"Find number of logged in users\"}\n", + "])" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "7427186d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'ls ~/'" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "llm([\n", + " {\"role\": \"system\", \"content\": \"Translate the following request into a linux command. Only print the command.\"},\n", + " {\"role\": \"user\", \"content\": \"List the files in my home directory\"}\n", + "])" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "a141d63b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'zip -r data.zip data'" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "llm([\n", + " {\"role\": \"system\", \"content\": \"Translate the following request into a linux command. Only print the command.\"},\n", + " {\"role\": \"user\", \"content\": \"Zip the data directory with all it's contents\"}\n", + "])" + ] + }, + { + "cell_type": "markdown", + "id": "fc7d0fda", + "metadata": {}, + "source": [ + "It even works well without the system prompt." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "a7d0c671", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'du -sh ~'" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "llm(\"Calculate the total amount of disk space used for my home directory. Only print the total.\")" + ] + }, + { + "cell_type": "markdown", + "id": "48cfcacf", + "metadata": {}, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook demonstrated how it's very straightforward to distill knowledge into LLMs with `txtai`. Don't always go for the giant LLM, spend a little time finetuning a tiny LLM, it is well worth it!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "local", + "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", + "version": "3.10.19" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/81_OpenCode_as_a_txtai_LLM.ipynb b/examples/81_OpenCode_as_a_txtai_LLM.ipynb new file mode 100644 index 0000000..71e8155 --- /dev/null +++ b/examples/81_OpenCode_as_a_txtai_LLM.ipynb @@ -0,0 +1,377 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "f95d2373", + "metadata": {}, + "source": [ + "# OpenCode as a txtai LLM\n", + "\n", + "[OpenCode](https://github.com/anomalyco/opencode) is an open source AI coding agent. It's rapidly growing in popularity as an open alternative to [Claude Code](https://code.claude.com/docs/en/overview). \n", + "\n", + "OpenCode shows the power of permissive open source. It's rapidly undergoing mass adoption and as of January 2026 sits at `81K+` GitHub ⭐'s. OpenCode supports running as a [local server](https://opencode.ai/docs/server/) via `opencode serve`.\n", + "\n", + "This notebook will explore how TxtAI integrates this as a LLM pipeline." + ] + }, + { + "cell_type": "markdown", + "id": "a8719b92", + "metadata": {}, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e00c85bc", + "metadata": { + "vscode": { + "languageId": "shellscript" + } + }, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[api,pipeline-llm]\n", + "\n", + "# Install OpenCode if not already installed and run serve\n", + "!curl -fsSL https://opencode.ai/install | bash\n", + "!opencode serve &" + ] + }, + { + "cell_type": "markdown", + "id": "73ffe60e", + "metadata": {}, + "source": [ + "# Create a OpenCode LLM\n", + "\n", + "Now that everything is installed, let's test it out!" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "0e35a304", + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "```python\n", + "print(\"Hello, World!\")\n", + "```" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import Markdown, display\n", + "\n", + "from txtai import LLM\n", + "\n", + "def generate(request):\n", + " display(Markdown(llm(request)))\n", + "\n", + "# Connect to OpenCode server and use default LLM provider\n", + "llm = LLM(\"opencode\")\n", + "\n", + "generate(\"Show a Python Hello World example\")" + ] + }, + { + "cell_type": "markdown", + "id": "55dd0fad", + "metadata": {}, + "source": [ + "OK, now let's try this for a more advanced use case." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "b3080841", + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "```python\n", + "from txtai import Embeddings, LLM\n", + "\n", + "# Create embeddings model\n", + "embeddings = Embeddings(path=\"sentence-transformers/all-MiniLM-L6-v2\")\n", + "\n", + "# Create LLM\n", + "llm = LLM(\"huggingface/Qwen/Qwen2.5-1.5B-Instruct\")\n", + "\n", + "# Sample documents\n", + "documents = [\n", + " \"Python is a programming language created by Guido van Rossum\",\n", + " \"Machine learning is a subset of artificial intelligence\",\n", + " \"Deep learning uses neural networks with multiple layers\"\n", + "]\n", + "\n", + "# Build the index\n", + "embeddings.index([(i, doc) for i, doc in enumerate(documents)])\n", + "\n", + "# RAG function\n", + "def rag_query(question):\n", + " # Search for relevant documents\n", + " results = embeddings.search(question, 2)\n", + " \n", + " # Build context from retrieved documents\n", + " context = \" \".join([documents[result[0]] for result in results])\n", + " \n", + " # Generate response using LLM with context\n", + " prompt = f\"Context: {context}\\n\\nQuestion: {question}\\n\\nAnswer:\"\n", + " return llm(prompt)\n", + "\n", + "# Example usage\n", + "question = \"What is Python?\"\n", + "answer = rag_query(question)\n", + "print(f\"Question: {question}\")\n", + "print(f\"Answer: {answer}\")\n", + "```" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "generate(\"Show a simple and basic TxtAI RAG example\")" + ] + }, + { + "cell_type": "markdown", + "id": "b44e0309", + "metadata": {}, + "source": [ + "Interesting! It's not limited to Python though." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "0a2ec1b4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "```assembly\n", + "section .data\n", + " hello db 'Hello, World!', 10 ; String with newline\n", + " hello_len equ $ - hello ; Length of string\n", + "\n", + "section .text\n", + " global _start\n", + "\n", + "_start:\n", + " ; sys_write system call\n", + " mov eax, 4 ; sys_write\n", + " mov ebx, 1 ; stdout\n", + " mov ecx, hello ; message\n", + " mov edx, hello_len ; message length\n", + " int 0x80 ; kernel interrupt\n", + "\n", + " ; sys_exit system call\n", + " mov eax, 1 ; sys_exit\n", + " mov ebx, 0 ; exit code 0\n", + " int 0x80 ; kernel interrupt\n", + "```\n", + "\n", + "Compile and run:\n", + "```bash\n", + "nasm -f elf32 hello.asm -o hello.o\n", + "ld -m elf_i386 hello.o -o hello\n", + "./hello\n", + "```" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "generate(\"Show a x86 assembly hello world\")" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "4e28d5c4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "```python\n", + "a = None\n", + "if a is not None:\n", + " a.startswith(\"hello\")\n", + "else:\n", + " print(\"a is None, cannot call startswith\")\n", + "```\n", + "\n", + "**What's wrong:** The variable `a` is `None`, which is a `NoneType` object. The `startswith()` method only exists for strings, not for `NoneType`. You need to check if `a` is not `None` before calling string methods on it." + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "generate(\"\"\"\n", + "Fix the following error in the code below and explain what's wrong.\n", + "\n", + "Error:\n", + "AttributeError: 'NoneType' object has no attribute 'startswith'\n", + "\n", + "Code:\n", + "a = None\n", + "a.startswith(\"hello\")\n", + "\"\"\")" + ] + }, + { + "cell_type": "markdown", + "id": "156122f0", + "metadata": {}, + "source": [ + "# OpenAI-compatible Endpoint\n", + "\n", + "Now that OpenCode is integrated into the TxtAI ecosystem, it opens a lot of different opportunities. Let's say we want to host a local OpenAI compatible endpoint, no problem!\n", + "\n", + "Write the following file.\n", + "\n", + "## config.yml\n", + "\n", + "```yaml\n", + "# Enable OpenAI compat endpoint\n", + "openai: True\n", + "\n", + "llm:\n", + " path: opencode\n", + "```\n", + "\n", + "and start the following process.\n", + "\n", + "```\n", + "CONFIG=config.yml uvicorn \"txtai.api:app\"\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "33057f59", + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "```python\n", + "def factorial(n):\n", + " if n < 0:\n", + " raise ValueError(\"Factorial is not defined for negative numbers\")\n", + " if n == 0 or n == 1:\n", + " return 1\n", + " result = 1\n", + " for i in range(2, n + 1):\n", + " result *= i\n", + " return result\n", + "\n", + "# Example usage:\n", + "# print(factorial(5)) # Output: 120\n", + "```" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import Markdown, display\n", + "\n", + "from openai import OpenAI\n", + "\n", + "client = OpenAI(\n", + " base_url=\"http://localhost:8000/v1\",\n", + " api_key=\"api-key\",\n", + ")\n", + "\n", + "response = client.chat.completions.create(\n", + " messages=[{\n", + " \"role\": \"user\",\n", + " \"content\": \"Show a Python factorial function\",\n", + " }],\n", + " model=\"opencode\",\n", + ")\n", + "\n", + "display(Markdown((response.choices[0].message.content)))" + ] + }, + { + "cell_type": "markdown", + "id": "58a9169b", + "metadata": {}, + "source": [ + "Just like before we get an answer! This time via the OpenAI client. Fun times." + ] + }, + { + "cell_type": "markdown", + "id": "47f9dbbd", + "metadata": {}, + "source": [ + "# Wrapping up\n", + "\n", + "This notebook showed how to connect TxtAI and OpenCode. This will lead to some interesting integrations. One such example is [ncoder](https://github.com/neuml/ncoder), an AI coding agent that integrates with Jupyter Notebooks. Stay tuned for more!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "local", + "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", + "version": "3.10.19" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/82_Agentic_College_Search.ipynb b/examples/82_Agentic_College_Search.ipynb new file mode 100644 index 0000000..db35e33 --- /dev/null +++ b/examples/82_Agentic_College_Search.ipynb @@ -0,0 +1,358 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "5a13697b", + "metadata": {}, + "source": [ + "# Agentic College Search\n", + "\n", + "This example will demonstrate how to use `txtai` agents to identify list of strong engineering colleges with baseball programs 🎓⚙️⚾.\n", + "\n", + "We'll setup a default [agents.md](https://github.com/agentsmd/agents.md) file and then give an LLM access to the web.\n", + "\n", + "Let's get started!" + ] + }, + { + "cell_type": "markdown", + "id": "face0f9e", + "metadata": {}, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5da3842", + "metadata": {}, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[agent]" + ] + }, + { + "cell_type": "markdown", + "id": "f9a6a500", + "metadata": {}, + "source": [ + "# Define an `agents.md` file\n", + "\n", + "Next, we'll define an agents.md file. This file defines default behavior for our college search agent and is inserted into the system prompt of every agent run." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4e0c500d", + "metadata": {}, + "outputs": [], + "source": [ + "%%writefile agents.md\n", + "# College Researcher\n", + "\n", + "You are a college research agent. Your job is to help find the best matching schools given a set of criteria.\n", + "\n", + "## Academics\n", + "\n", + "Work to match the request with a set of schools that match the academic requirements\n", + "\n", + "## Athletics\n", + "\n", + "Some requests may be interested in collegiate athletics. Make sure to balance that with other parts of the request." + ] + }, + { + "cell_type": "markdown", + "id": "83dee7de", + "metadata": {}, + "source": [ + "# Identify the Top 20 schools\n", + "\n", + "Next, let's run a prompt to identify the best 20 colleges given a set of criteria.\n", + "\n", + "First, we'll setup the scaffolding code to create and run an agent. We'll use a [Qwen3 4B non-thinking LLM](https://huggingface.co/Qwen/Qwen3-4B-Instruct-2507) as the agent's model. We'll add the `websearch` and `webview` tools to the agent along with the `agents.md` file previously created.\n", + "\n", + "Additionally, we'll add a sliding window of the last 5 responses as \"agent memory\". This will help create a rolling dialogue. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "410f2585", + "metadata": {}, + "outputs": [], + "source": [ + "from txtai import Agent\n", + "from IPython.display import display, Markdown\n", + "\n", + "def run(query, reset=False):\n", + " answer = agent(query, maxlength=60000, reset=reset)\n", + " display(Markdown(answer))\n", + "\n", + "agent = Agent(model=\"Qwen/Qwen3-4B-Instruct-2507\", tools=[\"websearch\", \"webview\"], memory=5, instructions=\"agents.md\", max_steps=50)" + ] + }, + { + "cell_type": "markdown", + "id": "53aa302b", + "metadata": {}, + "source": [ + "Now we can run the search. To come up with our list of colleges, a series of web searches were executed by the agent. It's like if you had the superpower 🚀 to kick off 10+ web searches with slight variations each time as you go. It's a rapid fire set of operations to collect information for the LLM for analysis.\n", + "\n", + "_Note: The output below has the `verbosity_level` set to 0 for brevity. If you run this again, you'll get the full details with each step._" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "f2d1ef2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "

╭──────────────────────────────────────────────────── New run ────────────────────────────────────────────────────╮\n",
+       "                                                                                                                 \n",
+       " List 20 universities located in the Mid-Atlantic or Northeast that would be good candidates to consider with    \n",
+       " top 50 engineering programs and an active baseball team. Visit websites covering the top engineering schools in \n",
+       " the Mid-Atlantic and Northeast. Add a rationale for each of the picks in a few words.                           \n",
+       "                                                                                                                 \n",
+       "╰─ PipelineModel - Qwen/Qwen3-4B-Instruct-2507 ───────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m╭─\u001b[0m\u001b[38;2;212;183;2m───────────────────────────────────────────────────\u001b[0m\u001b[38;2;212;183;2m \u001b[0m\u001b[1;38;2;212;183;2mNew run\u001b[0m\u001b[38;2;212;183;2m \u001b[0m\u001b[38;2;212;183;2m───────────────────────────────────────────────────\u001b[0m\u001b[38;2;212;183;2m─╮\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mList 20 universities located in the Mid-Atlantic or Northeast that would be good candidates to consider with \u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mtop 50 engineering programs and an active baseball team. Visit websites covering the top engineering schools in\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mthe Mid-Atlantic and Northeast. Add a rationale for each of the picks in a few words.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m╰─\u001b[0m\u001b[38;2;212;183;2m PipelineModel - Qwen/Qwen3-4B-Instruct-2507 \u001b[0m\u001b[38;2;212;183;2m──────────────────────────────────────────────────────────────────\u001b[0m\u001b[38;2;212;183;2m─╯\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/markdown": [ + "Here are 20 universities in the Mid-Atlantic or Northeast with top 50 engineering programs and active baseball teams, along with a brief rationale for each:\n", + "\n", + "1. **Massachusetts Institute of Technology (MIT)** – Top-ranked engineering program in the Northeast with a strong research focus and active baseball team.\n", + "2. **Princeton University** – Renowned for engineering excellence and competitive baseball in the Ivy League.\n", + "3. **Columbia University** – Strong engineering programs and a consistently active baseball team.\n", + "4. **University of Pennsylvania** – Top-tier engineering and a well-established baseball program in the Ivy League.\n", + "5. **Carnegie Mellon University** – Leading in computer and engineering fields with a competitive baseball team.\n", + "6. **Cornell University** – Excellent engineering programs and a strong baseball presence.\n", + "7. **Northeastern University** – Strong engineering programs and active baseball team.\n", + "8. **New York University** – Top engineering programs with a competitive baseball team.\n", + "9. **Rensselaer Polytechnic Institute (RPI)** – Strong engineering focus and active baseball in the Liberty League.\n", + "10. **Rutgers University** – Top engineering programs and a well-established baseball team.\n", + "11. **Lehigh University** – Strong engineering and active baseball in the Patriot League.\n", + "12. **University of Connecticut** – Top engineering programs and a competitive baseball team.\n", + "13. **University of Massachusetts Amherst** – Solid engineering programs and active baseball.\n", + "14. **University of Massachusetts Lowell** – Strong engineering and active baseball in the America East Conference.\n", + "15. **Syracuse University** – Top engineering programs and a competitive baseball team.\n", + "16. **Boston University** – Strong engineering and active baseball.\n", + "17. **Dartmouth College** – Excellent engineering programs and competitive baseball.\n", + "18. **Brown University** – Top engineering and active baseball.\n", + "19. **Bucknell University** – Solid engineering and active baseball.\n", + "20. **University of Rochester** – Strong engineering and competitive baseball.\n", + "\n", + "These institutions combine top-tier engineering programs with active collegiate baseball teams, making them excellent candidates for students seeking both academic excellence and athletic engagement." + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "run((\n", + " \"List 20 universities located in the Mid-Atlantic or Northeast that would be good candidates to consider \"\n", + " \"with top 50 engineering programs and an active baseball team. \"\n", + " \"Visit websites covering the top engineering schools in the Mid-Atlantic and Northeast. \"\n", + " \"Add a rationale for each of the picks in a few words. \"\n", + "))" + ] + }, + { + "cell_type": "markdown", + "id": "a7533137", + "metadata": {}, + "source": [ + "Interesting! Looks like quite an impressive list of colleges. This output is the product of taking the results of 10+ web searches and analyzing the results with an LLM. A very practical and realistic use of \"Agentic AI\". This is a great list of strong engineering schools with baseball programs 🎓⚙️⚾.\n", + "\n", + "Let's narrow it down to the top 5 engineering schools." + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "id": "5e261825", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
╭──────────────────────────────────────────────────── New run ────────────────────────────────────────────────────╮\n",
+       "                                                                                                                 \n",
+       " Read the list below and pick the Top 5 engineering programs.                                                    \n",
+       "                                                                                                                 \n",
+       " Use the following conversation history to help answer the question above.                                       \n",
+       "                                                                                                                 \n",
+       " User: List 20 universities located in the Mid-Atlantic or Northeast that would be good candidates to consider   \n",
+       " with top 50 engineering programs and an active baseball team. Visit websites covering the top engineering       \n",
+       " schools in the Mid-Atlantic and Northeast. Add a rationale for each of the picks in a few words.                \n",
+       " Assistant: Here are 20 universities in the Mid-Atlantic or Northeast with top 50 engineering programs and       \n",
+       " active baseball teams, along with a brief rationale for each:                                                   \n",
+       "                                                                                                                 \n",
+       " 1. **Massachusetts Institute of Technology (MIT)** – Top-ranked engineering program in the Northeast with a     \n",
+       " strong research focus and active baseball team.                                                                 \n",
+       " 2. **Princeton University** – Renowned for engineering excellence and competitive baseball in the Ivy League.   \n",
+       " 3. **Columbia University** – Strong engineering programs and a consistently active baseball team.               \n",
+       " 4. **University of Pennsylvania** – Top-tier engineering and a well-established baseball program in the Ivy     \n",
+       " League.                                                                                                         \n",
+       " 5. **Carnegie Mellon University** – Leading in computer and engineering fields with a competitive baseball      \n",
+       " team.                                                                                                           \n",
+       " 6. **Cornell University** – Excellent engineering programs and a strong baseball presence.                      \n",
+       " 7. **Northeastern University** – Strong engineering programs and active baseball team.                          \n",
+       " 8. **New York University** – Top engineering programs with a competitive baseball team.                         \n",
+       " 9. **Rensselaer Polytechnic Institute (RPI)** – Strong engineering focus and active baseball in the Liberty     \n",
+       " League.                                                                                                         \n",
+       " 10. **Rutgers University** – Top engineering programs and a well-established baseball team.                     \n",
+       " 11. **Lehigh University** – Strong engineering and active baseball in the Patriot League.                       \n",
+       " 12. **University of Connecticut** – Top engineering programs and a competitive baseball team.                   \n",
+       " 13. **University of Massachusetts Amherst** – Solid engineering programs and active baseball.                   \n",
+       " 14. **University of Massachusetts Lowell** – Strong engineering and active baseball in the America East         \n",
+       " Conference.                                                                                                     \n",
+       " 15. **Syracuse University** – Top engineering programs and a competitive baseball team.                         \n",
+       " 16. **Boston University** – Strong engineering and active baseball.                                             \n",
+       " 17. **Dartmouth College** – Excellent engineering programs and competitive baseball.                            \n",
+       " 18. **Brown University** – Top engineering and active baseball.                                                 \n",
+       " 19. **Bucknell University** – Solid engineering and active baseball.                                            \n",
+       " 20. **University of Rochester** – Strong engineering and competitive baseball.                                  \n",
+       "                                                                                                                 \n",
+       " These institutions combine top-tier engineering programs with active collegiate baseball teams, making them     \n",
+       " excellent candidates for students seeking both academic excellence and athletic engagement.                     \n",
+       "                                                                                                                 \n",
+       " If the history is irrelevant, forget it and use other tools to answer the question.                             \n",
+       "                                                                                                                 \n",
+       "╰─ PipelineModel - Qwen/Qwen3-4B-Instruct-2507 ───────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m╭─\u001b[0m\u001b[38;2;212;183;2m───────────────────────────────────────────────────\u001b[0m\u001b[38;2;212;183;2m \u001b[0m\u001b[1;38;2;212;183;2mNew run\u001b[0m\u001b[38;2;212;183;2m \u001b[0m\u001b[38;2;212;183;2m───────────────────────────────────────────────────\u001b[0m\u001b[38;2;212;183;2m─╮\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mRead the list below and pick the Top 5 engineering programs.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mUse the following conversation history to help answer the question above.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mUser: List 20 universities located in the Mid-Atlantic or Northeast that would be good candidates to consider \u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mwith top 50 engineering programs and an active baseball team. Visit websites covering the top engineering \u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mschools in the Mid-Atlantic and Northeast. Add a rationale for each of the picks in a few words. \u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mAssistant: Here are 20 universities in the Mid-Atlantic or Northeast with top 50 engineering programs and \u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mactive baseball teams, along with a brief rationale for each:\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1m1. **Massachusetts Institute of Technology (MIT)** – Top-ranked engineering program in the Northeast with a \u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mstrong research focus and active baseball team.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1m2. **Princeton University** – Renowned for engineering excellence and competitive baseball in the Ivy League.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1m3. **Columbia University** – Strong engineering programs and a consistently active baseball team.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1m4. **University of Pennsylvania** – Top-tier engineering and a well-established baseball program in the Ivy \u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mLeague.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1m5. **Carnegie Mellon University** – Leading in computer and engineering fields with a competitive baseball \u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mteam.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1m6. **Cornell University** – Excellent engineering programs and a strong baseball presence.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1m7. **Northeastern University** – Strong engineering programs and active baseball team.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1m8. **New York University** – Top engineering programs with a competitive baseball team.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1m9. **Rensselaer Polytechnic Institute (RPI)** – Strong engineering focus and active baseball in the Liberty \u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mLeague.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1m10. **Rutgers University** – Top engineering programs and a well-established baseball team.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1m11. **Lehigh University** – Strong engineering and active baseball in the Patriot League.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1m12. **University of Connecticut** – Top engineering programs and a competitive baseball team.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1m13. **University of Massachusetts Amherst** – Solid engineering programs and active baseball.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1m14. **University of Massachusetts Lowell** – Strong engineering and active baseball in the America East \u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mConference.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1m15. **Syracuse University** – Top engineering programs and a competitive baseball team.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1m16. **Boston University** – Strong engineering and active baseball.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1m17. **Dartmouth College** – Excellent engineering programs and competitive baseball.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1m18. **Brown University** – Top engineering and active baseball.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1m19. **Bucknell University** – Solid engineering and active baseball.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1m20. **University of Rochester** – Strong engineering and competitive baseball.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mThese institutions combine top-tier engineering programs with active collegiate baseball teams, making them \u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mexcellent candidates for students seeking both academic excellence and athletic engagement.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mIf the history is irrelevant, forget it and use other tools to answer the question.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m╰─\u001b[0m\u001b[38;2;212;183;2m PipelineModel - Qwen/Qwen3-4B-Instruct-2507 \u001b[0m\u001b[38;2;212;183;2m──────────────────────────────────────────────────────────────────\u001b[0m\u001b[38;2;212;183;2m─╯\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/markdown": [ + "The top 5 engineering programs from the list of universities in the Mid-Atlantic or Northeast with active baseball teams are:\n", + "\n", + "1. **Massachusetts Institute of Technology (MIT)** – Top-ranked engineering program in the Northeast with a strong research focus and active baseball team.\n", + "2. **Princeton University** – Renowned for engineering excellence and competitive baseball in the Ivy League.\n", + "3. **Carnegie Mellon University** – Leading in computer and engineering fields with a competitive baseball team.\n", + "4. **Cornell University** – Excellent engineering programs and a strong baseball presence.\n", + "5. **University of Pennsylvania** – Top-tier engineering and a well-established baseball program in the Ivy League.\n", + "\n", + "These schools stand out due to their exceptional engineering programs and active collegiate baseball teams, offering a balanced academic and athletic experience." + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "run(\"Read the list below and pick the Top 5 engineering programs.\")" + ] + }, + { + "cell_type": "markdown", + "id": "e6c27f16", + "metadata": {}, + "source": [ + "# Wrapping up\n", + "\n", + "This example showed how to run an Agentic Loop that identified the best set of colleges to research further. It's an agent with memory and can be used to build an ongoing dialogue with an agent. Give it a try!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "local", + "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", + "version": "3.10.19" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/83_TxtAI_got_skills.ipynb b/examples/83_TxtAI_got_skills.ipynb new file mode 100644 index 0000000..276f46e --- /dev/null +++ b/examples/83_TxtAI_got_skills.ipynb @@ -0,0 +1,410 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "8d8cb3ad", + "metadata": {}, + "source": [ + "# TxtAI got skills\n", + "\n", + "This example will demonstrate how to use `txtai` agents with [`skill.md`](https://agentskills.io/specification) files.\n", + "\n", + "We'll setup a `skill.md` file with details on how to use TxtAI and run a series of agent requests.\n", + "\n", + "Let's get started!" + ] + }, + { + "cell_type": "markdown", + "id": "00a5a0ea", + "metadata": {}, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0ff7aa19", + "metadata": {}, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[agent]" + ] + }, + { + "cell_type": "markdown", + "id": "98bcd5c3", + "metadata": {}, + "source": [ + "# Define a `skill.md` file\n", + "\n", + "Next, we'll create our `skill.md` file. This file has examples on how to build embeddings databases, how to use re-ranker pipelines, RAG pipelines and more.\n", + "\n", + "The upside of a `skill.md` file vs an `agents.md` file is that it can be dynamically added to the agent context. The description helps the agent decide if the `skill` is necessary given the request. Think of it like a dynamic knowledge base that's easy to modify." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "49179643", + "metadata": {}, + "outputs": [], + "source": [ + "%%writefile skill.md\n", + "---\n", + "name: txtai\n", + "description: Examples on how to build txtai embeddings databases, txtai RAG pipelines, txtai reranker pipelines and txtai translation pipelines\n", + "---\n", + "\n", + "# Build an embeddings database\n", + "\n", + "```python\n", + "from txtai import Embeddings\n", + "\n", + "# Create embeddings model, backed by sentence-transformers & transformers\n", + "embeddings = Embeddings(path=\"sentence-transformers/nli-mpnet-base-v2\")\n", + "\n", + "data = [\n", + " \"US tops 5 million confirmed virus cases\",\n", + " \"Canada's last fully intact ice shelf has suddenly collapsed, \" +\n", + " \"forming a Manhattan-sized iceberg\",\n", + " \"Beijing mobilises invasion craft along coast as Taiwan tensions escalate\",\n", + " \"The National Park Service warns against sacrificing slower friends \" +\n", + " \"in a bear attack\",\n", + " \"Maine man wins $1M from $25 lottery ticket\",\n", + " \"Make huge profits without work, earn up to $100,000 a day\"\n", + "]\n", + "\n", + "# Index the list of text\n", + "embeddings.index(data)\n", + "```\n", + "\n", + "# Search an embeddings database\n", + "\n", + "```python\n", + "embeddings.search(\"Search query\")\n", + "```\n", + "\n", + "# Build a RAG pipeline\n", + "\n", + "```python\n", + "from txtai import Embeddings, RAG\n", + "\n", + "# Input data\n", + "data = [\n", + " \"US tops 5 million confirmed virus cases\",\n", + " \"Canada's last fully intact ice shelf has suddenly collapsed, \" +\n", + " \"forming a Manhattan-sized iceberg\",\n", + " \"Beijing mobilises invasion craft along coast as Taiwan tensions escalate\",\n", + " \"The National Park Service warns against sacrificing slower friends \" +\n", + " \"in a bear attack\",\n", + " \"Maine man wins $1M from $25 lottery ticket\",\n", + " \"Make huge profits without work, earn up to $100,000 a day\"\n", + "]\n", + "\n", + "# Build embeddings index\n", + "embeddings = Embeddings(content=True)\n", + "embeddings.index(data)\n", + "\n", + "# Create the RAG pipeline\n", + "rag = RAG(embeddings, \"Qwen/Qwen3-0.6B\", template=\"\"\"\n", + " Answer the following question using the provided context.\n", + "\n", + " Question:\n", + " {question}\n", + "\n", + " Context:\n", + " {context}\n", + "\"\"\")\n", + "\n", + "# Run RAG pipeline\n", + "rag(\"What was won?\")\n", + "```\n", + "\n", + "# Translate text from English into French\n", + "\n", + "```python\n", + "from txtai.pipeline import Translation\n", + "\n", + "# Create and run pipeline\n", + "translate = Translation()\n", + "translate(\"This is a test translation\", \"fr\")\n", + "```\n", + "\n", + "# Re-ranker pipeline\n", + "\n", + "```python\n", + "from txtai import Embeddings\n", + "from txtai.pipeline import Reranker, Similarity\n", + "\n", + "# Embeddings instance\n", + "embeddings = Embeddings()\n", + "embeddings.load(provider=\"huggingface-hub\", container=\"neuml/txtai-wikipedia\")\n", + "\n", + "# Similarity instance\n", + "similarity = Similarity(path=\"colbert-ir/colbertv2.0\", lateencode=True)\n", + "\n", + "# Reranking pipeline\n", + "reranker = Reranker(embeddings, similarity)\n", + "reranker(\"Tell me about AI\")\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "4ed26969", + "metadata": {}, + "source": [ + "# Query for TxtAI questions\n", + "\n", + "Now, let's try this out and see if the LLM is smart enough to use the defined skill vs. going out on the web.\n", + "\n", + "Let's setup the scaffolding code to create and run an agent. We'll use a [Qwen3 4B non-thinking LLM](https://huggingface.co/Qwen/Qwen3-4B-Instruct-2507) as the agent's model. We'll add the `websearch` and `webview` tools to the agent along with the `skill.md` file previously created.\n", + "\n", + "Additionally, we'll add a sliding window of the last 2 responses as \"agent memory\". This will help create a rolling dialogue. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b6a1aaf1", + "metadata": {}, + "outputs": [], + "source": [ + "from txtai import Agent\n", + "from IPython.display import display, Markdown\n", + "\n", + "def run(query, reset=False):\n", + " answer = agent(query, maxlength=50000, reset=reset)\n", + " display(Markdown(answer))\n", + "\n", + "agent = Agent(\n", + " model=\"Qwen/Qwen3-4B-Instruct-2507\",\n", + " tools=[\"websearch\", \"webview\", \"skill.md\"],\n", + " memory=2,\n", + " verbosity_level=0\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "19302f54", + "metadata": {}, + "source": [ + "First, we'll ask how to build a TxtAI embeddings database." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "121d0b8a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "To create a txtai embeddings program that indexes data, use the following Python code:\n", + "\n", + "```python\n", + "from txtai import Embeddings\n", + "\n", + "# Create embeddings model using sentence-transformers\n", + "embeddings = Embeddings(path=\"sentence-transformers/nli-mpnet-base-v2\")\n", + "\n", + "# Sample data to index\n", + "data = [\n", + " \"US tops 5 million confirmed virus cases\",\n", + " \"Canada's last fully intact ice shelf has suddenly collapsed, \" +\n", + " \"forming a Manhattan-sized iceberg\",\n", + " \"Beijing mobilises invasion craft along coast as Taiwan tensions escalate\",\n", + " \"The National Park Service warns against sacrificing slower friends \" +\n", + " \"in a bear attack\",\n", + " \"Maine man wins $1M from $25 lottery ticket\",\n", + " \"Make huge profits without work, earn up to $100,000 a day\"\n", + "]\n", + "\n", + "# Index the data\n", + "embeddings.index(data)\n", + "```\n", + "\n", + "This program initializes an embeddings database using the `sentence-transformers/nli-mpnet-base-v2` model and indexes a list of text data. The indexed data can later be searched or used in other applications like retrieval, RAG, or translation." + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "run(\"Write a txtai embeddings program that indexes data\")" + ] + }, + { + "cell_type": "markdown", + "id": "f723e07f", + "metadata": {}, + "source": [ + "The Agent pulled the correct section from the `skill.md` file. Now, let's look for an example on how to use a re-ranker pipeline." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "221a590c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "```python\n", + "from txtai import Embeddings\n", + "from txtai.pipeline import Reranker, Similarity\n", + "\n", + "# Embeddings instance\n", + "embeddings = Embeddings()\n", + "embeddings.load(provider=\"huggingface-hub\", container=\"neuml/txtai-wikipedia\")\n", + "\n", + "# Similarity instance\n", + "similarity = Similarity(path=\"colbert-ir/colbertv2.0\", lateencode=True)\n", + "\n", + "# Reranking pipeline\n", + "reranker = Reranker(embeddings, similarity)\n", + "reranker(\"Tell me about AI\")\n", + "```" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "run(\"Write a txtai re-ranker pipeline\")" + ] + }, + { + "cell_type": "markdown", + "id": "4af3a6b6", + "metadata": {}, + "source": [ + "Great! This also works. But remember we have access to an LLM here. It doesn't have to blindly just pull the text. Let's ask it to modify the last example." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "c9f1eed4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "```python\n", + "from txtai import Embeddings\n", + "from txtai.pipeline import Reranker, Similarity\n", + "\n", + "# Embeddings instance\n", + "embeddings = Embeddings()\n", + "embeddings.load(provider=\"huggingface-hub\", container=\"neuml/txtai-wikipedia\")\n", + "\n", + "# Similarity instance with a different reranker model and lateencode disabled\n", + "similarity = Similarity(path=\"BAAI/bge-reranker-base\", lateencode=False)\n", + "\n", + "# Reranking pipeline\n", + "reranker = Reranker(embeddings, similarity)\n", + "reranker(\"Tell me about AI\")\n", + "```" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "run(\"Update the similarity path to use another reranker model. Disable lateencode.\")" + ] + }, + { + "cell_type": "markdown", + "id": "4a3bb21e", + "metadata": {}, + "source": [ + "Notice it just edited the code with a different reranker model and even added a comment to note this change.\n", + "\n", + "We can also clear the rolling dialogue and start fresh. " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "ad53f7a8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "```python\n", + "from txtai.pipeline import Translation\n", + "\n", + "# Create and run pipeline for English to Spanish translation\n", + "translate = Translation()\n", + "translated_text = translate(\"This is a test translation\", \"es\")\n", + "print(translated_text)\n", + "```" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "run(\"Write a txtai program that translate text from English to Spanish\", reset=True)" + ] + }, + { + "cell_type": "markdown", + "id": "007daee0", + "metadata": {}, + "source": [ + "# Wrapping up\n", + "\n", + "This example shows how to add a `skill.md` file to `txtai` agents. Go give it a try!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "local", + "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", + "version": "3.10.19" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/84_Agent_Tools.ipynb b/examples/84_Agent_Tools.ipynb new file mode 100644 index 0000000..432a75b --- /dev/null +++ b/examples/84_Agent_Tools.ipynb @@ -0,0 +1,6975 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "1a3d20f0", + "metadata": {}, + "source": [ + "# Agent Tools\n", + "\n", + "AI-driven software development is all the rage in 2026. If you look online, you'll find a endless stream of proclaimations such as \"software development is dead\", \"anyone can code now\" and \"if you're not doing AI-driven software development you're a dinosaur\". The same philosophy is now expanding to office-based work in general. \n", + "\n", + "Astute developers and business professionals have already taken a look at this new paradigm and have started making their minds up on how it can help them. There is no right answer, plenty of people haven't adapted and they're still happily working. TxtAI has a simple yet robust framework for agents and local AI-driven development.\n", + "\n", + "The next release will add `Agent Tools` which is a set of tools to connect agents with the operating system. Tools such as reading, writing and finding files add an extremely simple but effective way to work with data. \n", + "\n", + "Let's get started!" + ] + }, + { + "cell_type": "markdown", + "id": "87f32722", + "metadata": {}, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d133fe30", + "metadata": {}, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[agent]\n", + "\n", + "# Get working files\n", + "!git clone https://github.com/neuml/txtai\n", + "!wget -N https://github.com/neuml/txtai/releases/download/v6.2.0/tests.tar.gz\n", + "!mkdir files\n", + "!tar -xvzf tests.tar.gz --strip-components=1 -C files" + ] + }, + { + "cell_type": "markdown", + "id": "a2b5e4dd", + "metadata": {}, + "source": [ + "# Create the Agent\n", + "\n", + "The first step is creating a TxtAI agent. The agent is an LLM with access to a set of tools. In this case, we'll use a [Qwen 3 Coder LLM](https://huggingface.co/unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF) along with the default toolkit. This toolkit has the following tools.\n", + "\n", + "\n", + "| Tool | Description |\n", + "|:------------|:----------------------------------------------------------|\n", + "| bash | Runs a shell command through subprocess |\n", + "| edit | Edits a file in place and returns a diff |\n", + "| glob | Finds matching file patterns in a directory |\n", + "| grep | Finds matching file content in a directory |\n", + "| python | Runs a Python action |\n", + "| read | Reads file or url content, supports text extraction |\n", + "| todowrite | Generates a task list to organize complex tasks |\n", + "| websearch | Runs a websearch using the built-in websearch tool |\n", + "| webview | Extracts content from a web page. Alias for `read` tool |\n", + "| write | Writes content to file |\n", + "\n", + "The default toolkit adds the ability to interact with the local file system and OS." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f08e103a", + "metadata": {}, + "outputs": [], + "source": [ + "from txtai import Agent\n", + "\n", + "agent = Agent(llm={\n", + " \"path\": \"unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF/Qwen3-Coder-30B-A3B-Instruct-Q3_K_M.gguf\",\n", + " \"n_ctx\": 30000\n", + "}, tools=[\"defaults\"])" + ] + }, + { + "cell_type": "markdown", + "id": "c6bec077", + "metadata": {}, + "source": [ + "# Search a directory for content\n", + "\n", + "Of course we can build a search index for files and then an interace to search that index and even rephrase the results with an LLM. This is the basic idea behind Retrieval Augmented Generation (RAG).\n", + "\n", + "But as modern coding tools are now showing, just scanning a file system and letting the LLM parse through the content is another compelling option. This example does just that! It looks in a directory to answer a question." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "150893bc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
╭──────────────────────────────────────────────────── New run ────────────────────────────────────────────────────╮\n",
+       "                                                                                                                 \n",
+       " Look in the files directory and figure out what's the recommended LLM to use                                    \n",
+       "                                                                                                                 \n",
+       "╰─ PipelineModel - unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF/Qwen3-Coder-30B-A3B-Instruct-Q3_K_M.gguf ────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m╭─\u001b[0m\u001b[38;2;212;183;2m───────────────────────────────────────────────────\u001b[0m\u001b[38;2;212;183;2m \u001b[0m\u001b[1;38;2;212;183;2mNew run\u001b[0m\u001b[38;2;212;183;2m \u001b[0m\u001b[38;2;212;183;2m───────────────────────────────────────────────────\u001b[0m\u001b[38;2;212;183;2m─╮\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mLook in the files directory and figure out what's the recommended LLM to use\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m╰─\u001b[0m\u001b[38;2;212;183;2m PipelineModel - unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF/Qwen3-Coder-30B-A3B-Instruct-Q3_K_M.gguf \u001b[0m\u001b[38;2;212;183;2m───────────\u001b[0m\u001b[38;2;212;183;2m─╯\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 1 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 1\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'glob' with arguments: {'files': '*'}                                                             │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'glob' with arguments: {'files': '*'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: |'files', 'agenttools.ipynb', 'txtai', 'tests.tar.gz']\n",
+       "
\n" + ], + "text/plain": [ + "Observations: |'files', 'agenttools.ipynb', 'txtai', 'tests.tar.gz']\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 1: Duration 0.61 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 1: Duration 0.61 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 2 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 2\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'glob' with arguments: {'files': 'files/*'}                                                       │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'glob' with arguments: {'files': 'files/*'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: |'files/tabular.csv', 'files/books.jpg', 'files/article.pdf', 'files/US_tops_5_million.wav', \n",
+       "'files/The_National_Park.wav', 'files/Make_huge_profits.wav', 'files/Maine_man_wins_1_mil.wav', \n",
+       "'files/Canadas_last_fully.wav', 'files/Beijing_mobilises.wav', 'files/buildings.jpg', 'files/chop.jpg', \n",
+       "'files/clock.jpg', 'files/computer.jpg', 'files/fall.jpg', 'files/map.jpg', 'files/motorcycle.jpg', \n",
+       "'files/night.jpg', 'files/stars.jpg', 'files/document.docx', 'files/document.pdf', 'files/spreadsheet.xlsx']\n",
+       "
\n" + ], + "text/plain": [ + "Observations: |'files/tabular.csv', 'files/books.jpg', 'files/article.pdf', 'files/US_tops_5_million.wav', \n", + "'files/The_National_Park.wav', 'files/Make_huge_profits.wav', 'files/Maine_man_wins_1_mil.wav', \n", + "'files/Canadas_last_fully.wav', 'files/Beijing_mobilises.wav', 'files/buildings.jpg', 'files/chop.jpg', \n", + "'files/clock.jpg', 'files/computer.jpg', 'files/fall.jpg', 'files/map.jpg', 'files/motorcycle.jpg', \n", + "'files/night.jpg', 'files/stars.jpg', 'files/document.docx', 'files/document.pdf', 'files/spreadsheet.xlsx']\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 2: Duration 0.72 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 2: Duration 0.72 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 3 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 3\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'read' with arguments: {'path': 'files/document.pdf'}                                             │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'read' with arguments: {'path': 'files/document.pdf'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: txtai – the all-in-one embeddings database\n",
+       "txtai is an all-in-one embeddings database for semantic search, LLM orchestration and \n",
+       "language model workflows.\n",
+       "\n",
+       "Summary of txtai features:\n",
+       "\n",
+       "• Vector search with SQL, object storage, topic modeling\n",
+       "• Create embeddings for text, documents, audio, images and video\n",
+       "• Pipelines powered by language models that run LLM prompts\n",
+       "• Workflows to join pipelines together and aggregate business logic\n",
+       "• Build with Python or YAML. API bindings available for JavaScript, Java, Rust and Go.\n",
+       "• Run local or scale out with container orchestration\n",
+       "\n",
+       "Examples\n",
+       "List of example notebooks.\n",
+       "\n",
+       "Notebook Description\n",
+       "Introducing txtai Overview of the functionality provided by txtai\n",
+       "Similarity search with \n",
+       "images Embed images and text into the same space for search\n",
+       "\n",
+       "Build a QA database Question matching with semantic search\n",
+       "Semantic Graphs Explore topics, data connectivity and run network analysis\n",
+       "\n",
+       "Install\n",
+       "The easiest way to install is via pip and PyPI\n",
+       "\n",
+       "pip install txtai\n",
+       "\n",
+       "Python 3.8+ is supported. Using a Python virtual environment is recommended.\n",
+       "\n",
+       "See the detailed install instructions for more information covering optional dependencies, \n",
+       "environment specific prerequisites, installing from source, conda support and how to run with \n",
+       "containers.\n",
+       "\n",
+       "Model guide\n",
+       "The following shows a list of suggested models.\n",
+       "\n",
+       "Component Model(s)\n",
+       "Embeddings all-MiniLM-L6-v2\n",
+       "\n",
+       "E5-base-v2\n",
+       "Image Captions BLIP\n",
+       "Labels - Zero Shot BART-Large-MNLI\n",
+       "\n",
+       "Labels - Fixed Fine-tune with training \n",
+       "pipeline\n",
+       "\n",
+       "Large Language Model \n",
+       "(LLM) Flan T5 XL\n",
+       "\n",
+       "Mistral 7B OpenOrca\n",
+       "Summarization DistilBART\n",
+       "Text-to-Speech ESPnet JETS\n",
+       "Transcription Whisper\n",
+       "Translation OPUS Model Series\n",
+       "\n",
+       "- txtai – the all-in-one embeddings database\n",
+       "- Examples\n",
+       "- Install\n",
+       "- Model guide\n",
+       "
\n" + ], + "text/plain": [ + "Observations: txtai – the all-in-one embeddings database\n", + "txtai is an all-in-one embeddings database for semantic search, LLM orchestration and \n", + "language model workflows.\n", + "\n", + "Summary of txtai features:\n", + "\n", + "• Vector search with SQL, object storage, topic modeling\n", + "• Create embeddings for text, documents, audio, images and video\n", + "• Pipelines powered by language models that run LLM prompts\n", + "• Workflows to join pipelines together and aggregate business logic\n", + "• Build with Python or YAML. API bindings available for JavaScript, Java, Rust and Go.\n", + "• Run local or scale out with container orchestration\n", + "\n", + "Examples\n", + "List of example notebooks.\n", + "\n", + "Notebook Description\n", + "Introducing txtai Overview of the functionality provided by txtai\n", + "Similarity search with \n", + "images Embed images and text into the same space for search\n", + "\n", + "Build a QA database Question matching with semantic search\n", + "Semantic Graphs Explore topics, data connectivity and run network analysis\n", + "\n", + "Install\n", + "The easiest way to install is via pip and PyPI\n", + "\n", + "pip install txtai\n", + "\n", + "Python 3.8+ is supported. Using a Python virtual environment is recommended.\n", + "\n", + "See the detailed install instructions for more information covering optional dependencies, \n", + "environment specific prerequisites, installing from source, conda support and how to run with \n", + "containers.\n", + "\n", + "Model guide\n", + "The following shows a list of suggested models.\n", + "\n", + "Component Model(s)\n", + "Embeddings all-MiniLM-L6-v2\n", + "\n", + "E5-base-v2\n", + "Image Captions BLIP\n", + "Labels - Zero Shot BART-Large-MNLI\n", + "\n", + "Labels - Fixed Fine-tune with training \n", + "pipeline\n", + "\n", + "Large Language Model \n", + "(LLM) Flan T5 XL\n", + "\n", + "Mistral 7B OpenOrca\n", + "Summarization DistilBART\n", + "Text-to-Speech ESPnet JETS\n", + "Transcription Whisper\n", + "Translation OPUS Model Series\n", + "\n", + "- txtai – the all-in-one embeddings database\n", + "- Examples\n", + "- Install\n", + "- Model guide\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 3: Duration 0.88 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 3: Duration 0.88 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 4 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 4\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'read' with arguments: {'path': 'files/document.docx'}                                            │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'read' with arguments: {'path': 'files/document.docx'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: # txtai – the all-in-one embeddings database\n",
+       "txtai is an all-in-one embeddings database for semantic search, LLM orchestration and language model workflows.\n",
+       "\n",
+       "Summary of txtai features:\n",
+       "· *Vector search* with SQL, object storage, topic modeling\n",
+       "· Create *embeddings* for text, documents, audio, images and video\n",
+       "· *Pipelines* powered by language models that run LLM prompts\n",
+       "· *Workflows* to join pipelines together and aggregate business logic\n",
+       "· Build with *Python* or *YAML* . API bindings available for JavaScript, Java, Rust and Go.\n",
+       "· *Run local or scale out with container orchestration* \n",
+       "\n",
+       "## Examples\n",
+       "List of example notebooks.\n",
+       "|Notebook|Description|\n",
+       "|---|---|\n",
+       "|Introducing txtai |Overview of the functionality provided by txtai|\n",
+       "|Similarity search with images|Embed images and text into the same space for search|\n",
+       "|Build a QA database|Question matching with semantic search|\n",
+       "|Semantic Graphs|Explore topics, data connectivity and run network analysis|\n",
+       "\n",
+       "## Install\n",
+       "The easiest way to install is via pip and PyPI\n",
+       "pip install txtai\n",
+       "Python 3.8+ is supported. Using a Python virtual environment is **recommended** .\n",
+       "See the detailed install instructions for more information covering optional dependencies, environment specific \n",
+       "prerequisites, installing from source, conda support and how to run with containers.\n",
+       "\n",
+       "## Model guide\n",
+       "The following shows a list of suggested models.\n",
+       "|Component|Model(s)|\n",
+       "|---|---|\n",
+       "|Embeddings|all-MiniLM-L6-v2|\n",
+       "||E5-base-v2|\n",
+       "|Image Captions|BLIP|\n",
+       "|Labels - Zero Shot|BART-Large-MNLI|\n",
+       "|Labels - Fixed|Fine-tune with training pipeline|\n",
+       "|Large Language Model (LLM)|Flan T5 XL|\n",
+       "||Mistral 7B OpenOrca|\n",
+       "|Summarization|DistilBART|\n",
+       "|Text-to-Speech|ESPnet JETS|\n",
+       "|Transcription|Whisper|\n",
+       "|Translation|OPUS Model Series|\n",
+       "
\n" + ], + "text/plain": [ + "Observations: # txtai – the all-in-one embeddings database\n", + "txtai is an all-in-one embeddings database for semantic search, LLM orchestration and language model workflows.\n", + "\n", + "Summary of txtai features:\n", + "· *Vector search* with SQL, object storage, topic modeling\n", + "· Create *embeddings* for text, documents, audio, images and video\n", + "· *Pipelines* powered by language models that run LLM prompts\n", + "· *Workflows* to join pipelines together and aggregate business logic\n", + "· Build with *Python* or *YAML* . API bindings available for JavaScript, Java, Rust and Go.\n", + "· *Run local or scale out with container orchestration* \n", + "\n", + "## Examples\n", + "List of example notebooks.\n", + "|Notebook|Description|\n", + "|---|---|\n", + "|Introducing txtai |Overview of the functionality provided by txtai|\n", + "|Similarity search with images|Embed images and text into the same space for search|\n", + "|Build a QA database|Question matching with semantic search|\n", + "|Semantic Graphs|Explore topics, data connectivity and run network analysis|\n", + "\n", + "## Install\n", + "The easiest way to install is via pip and PyPI\n", + "pip install txtai\n", + "Python 3.8+ is supported. Using a Python virtual environment is **recommended** .\n", + "See the detailed install instructions for more information covering optional dependencies, environment specific \n", + "prerequisites, installing from source, conda support and how to run with containers.\n", + "\n", + "## Model guide\n", + "The following shows a list of suggested models.\n", + "|Component|Model(s)|\n", + "|---|---|\n", + "|Embeddings|all-MiniLM-L6-v2|\n", + "||E5-base-v2|\n", + "|Image Captions|BLIP|\n", + "|Labels - Zero Shot|BART-Large-MNLI|\n", + "|Labels - Fixed|Fine-tune with training pipeline|\n", + "|Large Language Model (LLM)|Flan T5 XL|\n", + "||Mistral 7B OpenOrca|\n", + "|Summarization|DistilBART|\n", + "|Text-to-Speech|ESPnet JETS|\n", + "|Transcription|Whisper|\n", + "|Translation|OPUS Model Series|\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 4: Duration 1.08 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 4: Duration 1.08 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 5 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 5\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'final_answer' with arguments: {'answer': 'The recommended LLMs according to the txtai            │\n",
+       "│ documentation are: Flan T5 XL and Mistral 7B OpenOrca'}                                                         │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'final_answer' with arguments: {'answer': 'The recommended LLMs according to the txtai │\n", + "│ documentation are: Flan T5 XL and Mistral 7B OpenOrca'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: The recommended LLMs according to the txtai documentation are: Flan T5 XL and Mistral 7B OpenOrca\n",
+       "
\n" + ], + "text/plain": [ + "Observations: The recommended LLMs according to the txtai documentation are: Flan T5 XL and Mistral 7B OpenOrca\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Final answer: The recommended LLMs according to the txtai documentation are: Flan T5 XL and Mistral 7B OpenOrca\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[1;38;2;212;183;2mFinal answer: The recommended LLMs according to the txtai documentation are: Flan T5 XL and Mistral 7B OpenOrca\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 5: Duration 1.87 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 5: Duration 1.87 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "'The recommended LLMs according to the txtai documentation are: Flan T5 XL and Mistral 7B OpenOrca'" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "agent(\"Look in the files directory and figure out what's the recommended LLM to use\")" + ] + }, + { + "cell_type": "markdown", + "id": "8ea73b88", + "metadata": {}, + "source": [ + "As we can see, the Agent stepped through the files and found the answer. One of the most powerful tools in the txtai agent toolkit is the `read` tool. It doesn't just simply read raw files, it has the ability to extract text from common document formats such as DOC, XLS, PDF. As you see above, the agent looked through text files, documents and PDFs as if they were all text files." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "f2f874c6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
╭──────────────────────────────────────────────────── New run ────────────────────────────────────────────────────╮\n",
+       "                                                                                                                 \n",
+       " Research txtai and write a markdown file with some facts about it                                               \n",
+       "                                                                                                                 \n",
+       "╰─ PipelineModel - unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF/Qwen3-Coder-30B-A3B-Instruct-Q3_K_M.gguf ────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m╭─\u001b[0m\u001b[38;2;212;183;2m───────────────────────────────────────────────────\u001b[0m\u001b[38;2;212;183;2m \u001b[0m\u001b[1;38;2;212;183;2mNew run\u001b[0m\u001b[38;2;212;183;2m \u001b[0m\u001b[38;2;212;183;2m───────────────────────────────────────────────────\u001b[0m\u001b[38;2;212;183;2m─╮\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mResearch txtai and write a markdown file with some facts about it\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m╰─\u001b[0m\u001b[38;2;212;183;2m PipelineModel - unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF/Qwen3-Coder-30B-A3B-Instruct-Q3_K_M.gguf \u001b[0m\u001b[38;2;212;183;2m───────────\u001b[0m\u001b[38;2;212;183;2m─╯\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 1 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 1\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'web_search' with arguments: {'query': 'txtai AI library python'}                                 │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'web_search' with arguments: {'query': 'txtai AI library python'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: ## Search Results\n",
+       "\n",
+       "|txtai · PyPI](https://pypi.org/project/txtai/)\n",
+       "☁️ Run local or scale out with container orchestration txtai is built with Python 3.10+, Hugging Face Transformers,\n",
+       "Sentence Transformers and FastAPI. txtai is open-source under an Apache 2.0 license. |!NOTE] NeuML is the company \n",
+       "behind txtai and we provide AI consulting services around our stack. Schedule a meeting or send a message to ...\n",
+       "\n",
+       "|GitHub - neuml/txtai.py: Python client for txtai · GitHub](https://github.com/neuml/txtai.py)\n",
+       " Python client for txtai  txtai is an all-in-one AI framework for semantic search, LLM orchestration and language \n",
+       "model workflows. This repository contains Python bindings for the txtai API. This is a minimal dependency library \n",
+       "for Python designed for use cases where txtai is running through the API. In all other cases, txtai should be \n",
+       "installed directly.\n",
+       "\n",
+       "|Installation - txtai - GitHub Pages](https://neuml.github.io/txtai/install/)\n",
+       " txtai is an all-in-one open-source AI framework for semantic search, LLM orchestration and language model \n",
+       "workflows\n",
+       "\n",
+       "|01_Introducing_txtai.ipynb - \n",
+       "Colab](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/01_Introducing_txtai.ipynb)\n",
+       "Introducing txtai  txtai is an all-in-one AI framework for semantic search, LLM orchestration and language model \n",
+       "workflows. The key component of txtai is an embeddings database, which is a union of vector indexes (sparse and \n",
+       "dense), graph networks and relational databases.\n",
+       "\n",
+       "|Introducing txtai, the all-in-one AI framework - \n",
+       "Medium](https://medium.com/neuml/introducing-txtai-the-all-in-one-ai-framework-0660ecfc39d7)\n",
+       "Introducing txtai  txtai is an all-in-one AI framework for semantic search, LLM orchestration and language model \n",
+       "workflows.\n",
+       "\n",
+       "|GitHub - neuml/txtai: All-in-one AI framework for semantic search ...](https://github.com/neuml/txtai)\n",
+       "All-in-one AI framework txtai is an all-in-one AI framework for semantic search, LLM orchestration and language \n",
+       "model workflows. The key component of txtai is an embeddings database, which is a union of vector indexes (sparse \n",
+       "and dense), graph networks and relational databases.\n",
+       "\n",
+       "|Examples - txtai - GitHub Pages](https://neuml.github.io/txtai/examples/)\n",
+       " txtai is an all-in-one open-source AI framework for semantic search, LLM orchestration and language model \n",
+       "workflows\n",
+       "\n",
+       "|txtai-py · PyPI](https://pypi.org/project/txtai-py/)\n",
+       " txtai is an all-in-one AI framework for semantic search, LLM orchestration and language model workflows. This \n",
+       "repository contains Python bindings for the txtai API. This is a minimal dependency library for Python designed for\n",
+       "use cases where txtai is running through the API. In all other cases, txtai should be installed directly.\n",
+       "\n",
+       "|GitHub - neuml/txtai.py: Python client for txtai](https://github.jpy.wang/neuml/txtai.py)\n",
+       " txtai is an all-in-one AI framework for semantic search, LLM orchestration and language model workflows. This \n",
+       "repository contains Python bindings for the txtai API. This is a minimal dependency library for Python designed for\n",
+       "use cases where txtai is running through the API. In all other cases, txtai should be installed directly.\n",
+       "\n",
+       "|txtai 9.6.0 on PyPI - Libraries.io - security & maintenance data for ...](https://libraries.io/pypi/txtai)\n",
+       "All-in-one open-source AI framework for semantic search, LLM orchestration and language model workflows - 9.6.0 - a\n",
+       "Python package on PyPI\n",
+       "
\n" + ], + "text/plain": [ + "Observations: ## Search Results\n", + "\n", + "|txtai · PyPI](https://pypi.org/project/txtai/)\n", + "☁️ Run local or scale out with container orchestration txtai is built with Python 3.10+, Hugging Face Transformers,\n", + "Sentence Transformers and FastAPI. txtai is open-source under an Apache 2.0 license. |!NOTE] NeuML is the company \n", + "behind txtai and we provide AI consulting services around our stack. Schedule a meeting or send a message to ...\n", + "\n", + "|GitHub - neuml/txtai.py: Python client for txtai · GitHub](https://github.com/neuml/txtai.py)\n", + " Python client for txtai txtai is an all-in-one AI framework for semantic search, LLM orchestration and language \n", + "model workflows. This repository contains Python bindings for the txtai API. This is a minimal dependency library \n", + "for Python designed for use cases where txtai is running through the API. In all other cases, txtai should be \n", + "installed directly.\n", + "\n", + "|Installation - txtai - GitHub Pages](https://neuml.github.io/txtai/install/)\n", + " txtai is an all-in-one open-source AI framework for semantic search, LLM orchestration and language model \n", + "workflows\n", + "\n", + "|01_Introducing_txtai.ipynb - \n", + "Colab](https://colab.research.google.com/github/neuml/txtai/blob/master/examples/01_Introducing_txtai.ipynb)\n", + "Introducing txtai txtai is an all-in-one AI framework for semantic search, LLM orchestration and language model \n", + "workflows. The key component of txtai is an embeddings database, which is a union of vector indexes (sparse and \n", + "dense), graph networks and relational databases.\n", + "\n", + "|Introducing txtai, the all-in-one AI framework - \n", + "Medium](https://medium.com/neuml/introducing-txtai-the-all-in-one-ai-framework-0660ecfc39d7)\n", + "Introducing txtai txtai is an all-in-one AI framework for semantic search, LLM orchestration and language model \n", + "workflows.\n", + "\n", + "|GitHub - neuml/txtai: All-in-one AI framework for semantic search ...](https://github.com/neuml/txtai)\n", + "All-in-one AI framework txtai is an all-in-one AI framework for semantic search, LLM orchestration and language \n", + "model workflows. The key component of txtai is an embeddings database, which is a union of vector indexes (sparse \n", + "and dense), graph networks and relational databases.\n", + "\n", + "|Examples - txtai - GitHub Pages](https://neuml.github.io/txtai/examples/)\n", + " txtai is an all-in-one open-source AI framework for semantic search, LLM orchestration and language model \n", + "workflows\n", + "\n", + "|txtai-py · PyPI](https://pypi.org/project/txtai-py/)\n", + " txtai is an all-in-one AI framework for semantic search, LLM orchestration and language model workflows. This \n", + "repository contains Python bindings for the txtai API. This is a minimal dependency library for Python designed for\n", + "use cases where txtai is running through the API. In all other cases, txtai should be installed directly.\n", + "\n", + "|GitHub - neuml/txtai.py: Python client for txtai](https://github.jpy.wang/neuml/txtai.py)\n", + " txtai is an all-in-one AI framework for semantic search, LLM orchestration and language model workflows. This \n", + "repository contains Python bindings for the txtai API. This is a minimal dependency library for Python designed for\n", + "use cases where txtai is running through the API. In all other cases, txtai should be installed directly.\n", + "\n", + "|txtai 9.6.0 on PyPI - Libraries.io - security & maintenance data for ...](https://libraries.io/pypi/txtai)\n", + "All-in-one open-source AI framework for semantic search, LLM orchestration and language model workflows - 9.6.0 - a\n", + "Python package on PyPI\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 1: Duration 2.60 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 1: Duration 2.60 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 2 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 2\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'read' with arguments: {'path': 'https://github.com/neuml/txtai'}                                 │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'read' with arguments: {'path': 'https://github.com/neuml/txtai'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: **GitHub - neuml/txtai: 💡 All-in-one AI framework for semantic search, LLM orchestration and \n",
+       "language model workflows · GitHub**\n",
+       "\n",
+       "*💡 All-in-one AI framework for semantic search, LLM orchestration and language model workflows - neuml/txtai*\n",
+       "\n",
+       "**All-in-one AI framework** \n",
+       "txtai is an all-in-one AI framework for semantic search, LLM orchestration and language model workflows.\n",
+       "\n",
+       "The key component of txtai is an embeddings database, which is a union of vector indexes (sparse and dense), graph \n",
+       "networks and relational databases.\n",
+       "\n",
+       "This foundation enables vector search and/or serves as a powerful knowledge source for large language model (LLM) \n",
+       "applications.\n",
+       "\n",
+       "Build autonomous agents, retrieval augmented generation (RAG) processes, multi-model workflows and more.\n",
+       "\n",
+       "Summary of txtai features:\n",
+       "\n",
+       "- 🔎 Vector search with SQL, object storage, topic modeling, graph analysis and multimodal indexing\n",
+       "- 📄 Create embeddings for text, documents, audio, images and video\n",
+       "- 💡 Pipelines powered by language models that run LLM prompts, question-answering, labeling, transcription, \n",
+       "translation, summarization and more\n",
+       "- ↪️️ Workflows to join pipelines together and aggregate business logic. txtai processes can be simple microservices\n",
+       "or multi-model workflows.\n",
+       "- 🤖 Agents that intelligently connect embeddings, pipelines, workflows and other agents together to autonomously \n",
+       "solve complex problems\n",
+       "- ⚙️ Web and Model Context Protocol (MCP) APIs. Bindings available for \n",
+       "|JavaScript](https://github.com/neuml/txtai.js) , |Java](https://github.com/neuml/txtai.java) , \n",
+       "|Rust](https://github.com/neuml/txtai.rs) and |Go](https://github.com/neuml/txtai.go) .\n",
+       "- 🔋 Batteries included with defaults to get up and running fast\n",
+       "- ☁️ Run local or scale out with container orchestration\n",
+       "txtai is built with Python 3.10+, |Hugging Face Transformers](https://github.com/huggingface/transformers) , \n",
+       "|Sentence Transformers](https://github.com/UKPLab/sentence-transformers) and \n",
+       "|FastAPI](https://github.com/tiangolo/fastapi) . txtai is open-source under an Apache 2.0 license.\n",
+       "\n",
+       "Note\n",
+       "\n",
+       "|NeuML](https://neuml.com) is the company behind txtai and we provide AI consulting services around our stack. \n",
+       "|Schedule a meeting](https://cal.com/neuml/intro) or |send a message](mailto:info@neuml.com) to learn more.\n",
+       "\n",
+       "We're also building an easy and secure way to run hosted txtai applications with |txtai.cloud](https://txtai.cloud)\n",
+       ".\n",
+       "\n",
+       "## Why txtai?\n",
+       "New vector databases, LLM frameworks and everything in between are sprouting up daily. Why build with txtai?\n",
+       "\n",
+       "- Up and running in minutes with |pip](https://neuml.github.io/txtai/install/) or \n",
+       "|Docker](https://neuml.github.io/txtai/cloud/) \n",
+       "```\n",
+       "# Get started in a couple lines\n",
+       "import txtai\n",
+       "\n",
+       "embeddings = txtai.Embeddings()\n",
+       "embeddings.index(|\"Correct\", \"Not what we hoped\"])\n",
+       "embeddings.search(\"positive\", 1)\n",
+       "#|(0, 0.29862046241760254)]\n",
+       "```\n",
+       "\n",
+       "- Built-in API makes it easy to develop applications using your programming language of choice\n",
+       "```\n",
+       "# app.yml\n",
+       "embeddings:\n",
+       " path: sentence-transformers/all-MiniLM-L6-v2\n",
+       "```\n",
+       "\n",
+       "```\n",
+       "CONFIG=app.yml uvicorn \"txtai.api:app\"\n",
+       "curl -X GET \"http://localhost:8000/search?query=positive\"\n",
+       "```\n",
+       "\n",
+       "- Run local - no need to ship data off to disparate remote services\n",
+       "- Work with micromodels all the way up to large language models (LLMs)\n",
+       "- Low footprint - install additional dependencies and scale up when needed\n",
+       "- |Learn by example](https://neuml.github.io/txtai/examples) - notebooks cover all available functionality\n",
+       "\n",
+       "## Use Cases\n",
+       "The following sections introduce common txtai use cases. A comprehensive set of over 70 |example notebooks and \n",
+       "applications](https://neuml.github.io/txtai/examples) are also available.\n",
+       "\n",
+       "### Semantic Search\n",
+       "Build semantic/similarity/vector/neural search applications.\n",
+       "\n",
+       "Traditional search systems use keywords to find data. Semantic search has an understanding of natural language and \n",
+       "identifies results that have the same meaning, not necessarily the same keywords.\n",
+       "\n",
+       "Get started with the following examples.\n",
+       "\n",
+       "|Notebook|Description||\n",
+       "|---|---|---|\n",
+       "||Introducing txtai](https://github.com/neuml/txtai/blob/master/examples/01_Introducing_txtai.ipynb) |Overview of \n",
+       "the functionality provided by txtai||\n",
+       "||Similarity search with \n",
+       "images](https://github.com/neuml/txtai/blob/master/examples/13_Similarity_search_with_images.ipynb) |Embed images \n",
+       "and text into the same space for search||\n",
+       "||Build a QA database](https://github.com/neuml/txtai/blob/master/examples/34_Build_a_QA_database.ipynb) |Question \n",
+       "matching with semantic search||\n",
+       "||Semantic Graphs](https://github.com/neuml/txtai/blob/master/examples/38_Introducing_the_Semantic_Graph.ipynb) \n",
+       "|Explore topics, data connectivity and run network analysis||\n",
+       "\n",
+       "### LLM Orchestration\n",
+       "Autonomous agents, retrieval augmented generation (RAG), chat with your data, pipelines and workflows that \n",
+       "interface with large language models (LLMs).\n",
+       "\n",
+       "See below to learn more.\n",
+       "\n",
+       "|Notebook|Description||\n",
+       "|---|---|---|\n",
+       "||Prompt templates and task \n",
+       "chains](https://github.com/neuml/txtai/blob/master/examples/44_Prompt_templates_and_task_chains.ipynb) |Build model\n",
+       "prompts and connect tasks together with workflows||\n",
+       "||Integrate LLM frameworks](https://github.com/neuml/txtai/blob/master/examples/53_Integrate_LLM_Frameworks.ipynb) \n",
+       "|Integrate llama.cpp, LiteLLM and custom generation frameworks||\n",
+       "||Build knowledge graphs with \n",
+       "LLMs](https://github.com/neuml/txtai/blob/master/examples/57_Build_knowledge_graphs_with_LLM_driven_entity_extracti\n",
+       "on.ipynb) |Build knowledge graphs with LLM-driven entity extraction||\n",
+       "||Parsing the stars with \n",
+       "txtai](https://github.com/neuml/txtai/blob/master/examples/72_Parsing_the_stars_with_txtai.ipynb) |Explore an \n",
+       "astronomical knowledge graph of known stars, planets, galaxies||\n",
+       "\n",
+       "#### Agents\n",
+       "Agents connect embeddings, pipelines, workflows and other agents together to autonomously solve complex problems.\n",
+       "\n",
+       "txtai agents are built on top of the |smolagents](https://github.com/huggingface/smolagents) framework. This \n",
+       "supports all LLMs txtai supports (Hugging Face, llama.cpp, OpenAI / Claude / AWS Bedrock via LiteLLM). Agent \n",
+       "prompting with |agents.md](https://github.com/agentsmd/agents.md) and \n",
+       "|skill.md](https://agentskills.io/specification) are also supported.\n",
+       "\n",
+       "Check out this |Agent Quickstart Example](https://github.com/neuml/txtai/blob/master/examples/agent_quickstart.py) \n",
+       ". Additional examples are listed below.\n",
+       "\n",
+       "|Notebook|Description||\n",
+       "|---|---|---|\n",
+       "||Analyzing Hugging Face Posts with Graphs and \n",
+       "Agents](https://github.com/neuml/txtai/blob/master/examples/68_Analyzing_Hugging_Face_Posts_with_Graphs_and_Agents.\n",
+       "ipynb) |Explore a rich dataset with Graph Analysis and Agents||\n",
+       "||Granting autonomy to \n",
+       "agents](https://github.com/neuml/txtai/blob/master/examples/69_Granting_autonomy_to_agents.ipynb) |Agents that \n",
+       "iteratively solve problems as they see fit||\n",
+       "||Analyzing LinkedIn Company Posts with Graphs and \n",
+       "Agents](https://github.com/neuml/txtai/blob/master/examples/71_Analyzing_LinkedIn_Company_Posts_with_Graphs_and_Age\n",
+       "nts.ipynb) |Exploring how to improve social media engagement with AI||\n",
+       "\n",
+       "#### Retrieval augmented generation\n",
+       "Retrieval augmented generation (RAG) reduces the risk of LLM hallucinations by constraining the output with a \n",
+       "knowledge base as context. RAG is commonly used to \"chat with your data\".\n",
+       "\n",
+       "Check out this |RAG Quickstart Example](https://github.com/neuml/txtai/blob/master/examples/rag_quickstart.py) . \n",
+       "Additional examples are listed below.\n",
+       "\n",
+       "|Notebook|Description||\n",
+       "|---|---|---|\n",
+       "||Build RAG pipelines with \n",
+       "txtai](https://github.com/neuml/txtai/blob/master/examples/52_Build_RAG_pipelines_with_txtai.ipynb) |Guide on \n",
+       "retrieval augmented generation including how to create citations||\n",
+       "||RAG is more than Vector \n",
+       "Search](https://github.com/neuml/txtai/blob/master/examples/79_RAG_is_more_than_Vector_Search.ipynb) |Context \n",
+       "retrieval via Web, SQL and other sources||\n",
+       "||GraphRAG with Wikipedia and GPT \n",
+       "OSS](https://github.com/neuml/txtai/blob/master/examples/77_GraphRAG_with_Wikipedia_and_GPT_OSS.ipynb) |Deep graph \n",
+       "search powered RAG||\n",
+       "||Speech to Speech RAG](https://github.com/neuml/txtai/blob/master/examples/65_Speech_to_Speech_RAG.ipynb) |Full \n",
+       "cycle speech to speech workflow with RAG||\n",
+       "\n",
+       "### Language Model Workflows\n",
+       "Language model workflows, also known as semantic workflows, connect language models together to build intelligent \n",
+       "applications.\n",
+       "\n",
+       "While LLMs are powerful, there are plenty of smaller, more specialized models that work better and faster for \n",
+       "specific tasks. This includes models for extractive question-answering, automatic summarization, text-to-speech, \n",
+       "transcription and translation.\n",
+       "\n",
+       "Check out this |Workflow Quickstart \n",
+       "Example](https://github.com/neuml/txtai/blob/master/examples/workflow_quickstart.py) . Additional examples are \n",
+       "listed below.\n",
+       "\n",
+       "|Notebook|Description||\n",
+       "|---|---|---|\n",
+       "||Run pipeline workflows](https://github.com/neuml/txtai/blob/master/examples/14_Run_pipeline_workflows.ipynb) \n",
+       "|Simple yet powerful constructs to efficiently process data||\n",
+       "||Building abstractive text \n",
+       "summaries](https://github.com/neuml/txtai/blob/master/examples/09_Building_abstractive_text_summaries.ipynb) |Run \n",
+       "abstractive text summarization||\n",
+       "||Transcribe audio to text](https://github.com/neuml/txtai/blob/master/examples/11_Transcribe_audio_to_text.ipynb) \n",
+       "|Convert audio files to text||\n",
+       "||Translate text between \n",
+       "languages](https://github.com/neuml/txtai/blob/master/examples/12_Translate_text_between_languages.ipynb) \n",
+       "|Streamline machine translation and language detection||\n",
+       "\n",
+       "## Installation\n",
+       "The easiest way to install is via pip and PyPI\n",
+       "\n",
+       "```\n",
+       "pip install txtai\n",
+       "```\n",
+       "\n",
+       "Python 3.10+ is supported. Using a Python |virtual environment](https://docs.python.org/3/library/venv.html) is \n",
+       "recommended.\n",
+       "\n",
+       "See the detailed |install instructions](https://neuml.github.io/txtai/install) for more information covering \n",
+       "|optional dependencies](https://neuml.github.io/txtai/install/#optional-dependencies) , |environment specific \n",
+       "prerequisites](https://neuml.github.io/txtai/install/#environment-specific-prerequisites) , |installing from \n",
+       "source](https://neuml.github.io/txtai/install/#install-from-source) , |conda \n",
+       "support](https://neuml.github.io/txtai/install/#conda) and how to |run with \n",
+       "containers](https://neuml.github.io/txtai/cloud) .\n",
+       "\n",
+       "## Model guide\n",
+       "See the table below for the current recommended models. These models all allow commercial use and offer a blend of \n",
+       "speed and performance.\n",
+       "\n",
+       "|Component|Model(s)|\n",
+       "|---|---|\n",
+       "||Embeddings](https://neuml.github.io/txtai/embeddings) \n",
+       "||all-MiniLM-L6-v2](https://hf.co/sentence-transformers/all-MiniLM-L6-v2) |\n",
+       "||Image Captions](https://neuml.github.io/txtai/pipeline/image/caption) \n",
+       "||BLIP](https://hf.co/Salesforce/blip-image-captioning-base) |\n",
+       "||Labels - Zero Shot](https://neuml.github.io/txtai/pipeline/text/labels) \n",
+       "||BART-Large-MNLI](https://hf.co/facebook/bart-large) |\n",
+       "||Labels - Fixed](https://neuml.github.io/txtai/pipeline/text/labels) |Fine-tune with |training \n",
+       "pipeline](https://neuml.github.io/txtai/pipeline/train/trainer) |\n",
+       "||Large Language Model (LLM)](https://neuml.github.io/txtai/pipeline/text/llm) \n",
+       "||gpt-oss-20b](https://huggingface.co/openai/gpt-oss-20b) |\n",
+       "||Summarization](https://neuml.github.io/txtai/pipeline/text/summary) \n",
+       "||DistilBART](https://hf.co/sshleifer/distilbart-cnn-12-6) |\n",
+       "||Text-to-Speech](https://neuml.github.io/txtai/pipeline/audio/texttospeech) ||ESPnet \n",
+       "JETS](https://hf.co/NeuML/ljspeech-jets-onnx) |\n",
+       "||Transcription](https://neuml.github.io/txtai/pipeline/audio/transcription) \n",
+       "||Whisper](https://hf.co/openai/whisper-base) |\n",
+       "||Translation](https://neuml.github.io/txtai/pipeline/text/translation) ||OPUS Model \n",
+       "Series](https://hf.co/Helsinki-NLP) |\n",
+       "Models can be loaded as either a path from the Hugging Face Hub or a local directory. Model paths are optional, \n",
+       "defaults are loaded when not specified. For tasks with no recommended model, txtai uses the default models as shown\n",
+       "in the Hugging Face Tasks guide.\n",
+       "\n",
+       "See the following links to learn more.\n",
+       "\n",
+       "## Powered by txtai\n",
+       "The following applications are powered by txtai.\n",
+       "\n",
+       "|Application|Description|\n",
+       "|---|---|\n",
+       "||rag](https://github.com/neuml/rag) |Retrieval Augmented Generation (RAG) application|\n",
+       "||ncoder](https://github.com/neuml/ncoder) |Open-Source AI coding agent|\n",
+       "||paperai](https://github.com/neuml/paperai) |AI for medical and scientific papers|\n",
+       "||annotateai](https://github.com/neuml/annotateai) |Automatically annotate papers with LLMs|\n",
+       "In addition to this list, there are also many other |open-source \n",
+       "projects](https://github.com/neuml/txtai/network/dependents) , |published \n",
+       "research](https://scholar.google.com/scholar?q=txtai&hl=en&as_ylo=2022) and closed proprietary/commercial projects \n",
+       "that have built on txtai in production.\n",
+       "\n",
+       "## Further Reading\n",
+       "- |Tutorial series on Hashnode](https://neuml.hashnode.dev/series/txtai-tutorial) | \n",
+       "|dev.to](https://dev.to/neuml/tutorial-series-on-txtai-ibg) \n",
+       "- |What's new in txtai 9.0](https://medium.com/neuml/whats-new-in-txtai-9-0-d522bb150afa) | \n",
+       "|8.0](https://medium.com/neuml/whats-new-in-txtai-8-0-2d7d0ab4506b) | \n",
+       "|7.0](https://medium.com/neuml/whats-new-in-txtai-7-0-855ad6a55440) | \n",
+       "|6.0](https://medium.com/neuml/whats-new-in-txtai-6-0-7d93eeedf804) | \n",
+       "|5.0](https://medium.com/neuml/whats-new-in-txtai-5-0-e5c75a13b101) | \n",
+       "|4.0](https://medium.com/neuml/whats-new-in-txtai-4-0-bbc3a65c3d1c) \n",
+       "- |Getting started with semantic \n",
+       "search](https://medium.com/neuml/getting-started-with-semantic-search-a9fd9d8a48cf) | \n",
+       "|workflows](https://medium.com/neuml/getting-started-with-semantic-workflows-2fefda6165d9) | \n",
+       "|rag](https://medium.com/neuml/getting-started-with-rag-9a0cca75f748) \n",
+       "\n",
+       "## Documentation\n",
+       "|Full documentation on txtai](https://neuml.github.io/txtai) including configuration settings for embeddings, \n",
+       "pipelines, workflows, API and a FAQ with common questions/issues is available.\n",
+       "\n",
+       "## Contributing\n",
+       "For those who would like to contribute to txtai, please see |this \n",
+       "guide](https://github.com/neuml/.github/blob/master/CONTRIBUTING.md) .\n",
+       "
\n" + ], + "text/plain": [ + "Observations: **GitHub - neuml/txtai: 💡 All-in-one AI framework for semantic search, LLM orchestration and \n", + "language model workflows · GitHub**\n", + "\n", + "*💡 All-in-one AI framework for semantic search, LLM orchestration and language model workflows - neuml/txtai*\n", + "\n", + "**All-in-one AI framework** \n", + "txtai is an all-in-one AI framework for semantic search, LLM orchestration and language model workflows.\n", + "\n", + "The key component of txtai is an embeddings database, which is a union of vector indexes (sparse and dense), graph \n", + "networks and relational databases.\n", + "\n", + "This foundation enables vector search and/or serves as a powerful knowledge source for large language model (LLM) \n", + "applications.\n", + "\n", + "Build autonomous agents, retrieval augmented generation (RAG) processes, multi-model workflows and more.\n", + "\n", + "Summary of txtai features:\n", + "\n", + "- 🔎 Vector search with SQL, object storage, topic modeling, graph analysis and multimodal indexing\n", + "- 📄 Create embeddings for text, documents, audio, images and video\n", + "- 💡 Pipelines powered by language models that run LLM prompts, question-answering, labeling, transcription, \n", + "translation, summarization and more\n", + "- ↪️️ Workflows to join pipelines together and aggregate business logic. txtai processes can be simple microservices\n", + "or multi-model workflows.\n", + "- 🤖 Agents that intelligently connect embeddings, pipelines, workflows and other agents together to autonomously \n", + "solve complex problems\n", + "- ⚙️ Web and Model Context Protocol (MCP) APIs. Bindings available for \n", + "|JavaScript](https://github.com/neuml/txtai.js) , |Java](https://github.com/neuml/txtai.java) , \n", + "|Rust](https://github.com/neuml/txtai.rs) and |Go](https://github.com/neuml/txtai.go) .\n", + "- 🔋 Batteries included with defaults to get up and running fast\n", + "- ☁️ Run local or scale out with container orchestration\n", + "txtai is built with Python 3.10+, |Hugging Face Transformers](https://github.com/huggingface/transformers) , \n", + "|Sentence Transformers](https://github.com/UKPLab/sentence-transformers) and \n", + "|FastAPI](https://github.com/tiangolo/fastapi) . txtai is open-source under an Apache 2.0 license.\n", + "\n", + "Note\n", + "\n", + "|NeuML](https://neuml.com) is the company behind txtai and we provide AI consulting services around our stack. \n", + "|Schedule a meeting](https://cal.com/neuml/intro) or |send a message](mailto:info@neuml.com) to learn more.\n", + "\n", + "We're also building an easy and secure way to run hosted txtai applications with |txtai.cloud](https://txtai.cloud)\n", + ".\n", + "\n", + "## Why txtai?\n", + "New vector databases, LLM frameworks and everything in between are sprouting up daily. Why build with txtai?\n", + "\n", + "- Up and running in minutes with |pip](https://neuml.github.io/txtai/install/) or \n", + "|Docker](https://neuml.github.io/txtai/cloud/) \n", + "```\n", + "# Get started in a couple lines\n", + "import txtai\n", + "\n", + "embeddings = txtai.Embeddings()\n", + "embeddings.index(|\"Correct\", \"Not what we hoped\"])\n", + "embeddings.search(\"positive\", 1)\n", + "#|(0, 0.29862046241760254)]\n", + "```\n", + "\n", + "- Built-in API makes it easy to develop applications using your programming language of choice\n", + "```\n", + "# app.yml\n", + "embeddings:\n", + " path: sentence-transformers/all-MiniLM-L6-v2\n", + "```\n", + "\n", + "```\n", + "CONFIG=app.yml uvicorn \"txtai.api:app\"\n", + "curl -X GET \"http://localhost:8000/search?query=positive\"\n", + "```\n", + "\n", + "- Run local - no need to ship data off to disparate remote services\n", + "- Work with micromodels all the way up to large language models (LLMs)\n", + "- Low footprint - install additional dependencies and scale up when needed\n", + "- |Learn by example](https://neuml.github.io/txtai/examples) - notebooks cover all available functionality\n", + "\n", + "## Use Cases\n", + "The following sections introduce common txtai use cases. A comprehensive set of over 70 |example notebooks and \n", + "applications](https://neuml.github.io/txtai/examples) are also available.\n", + "\n", + "### Semantic Search\n", + "Build semantic/similarity/vector/neural search applications.\n", + "\n", + "Traditional search systems use keywords to find data. Semantic search has an understanding of natural language and \n", + "identifies results that have the same meaning, not necessarily the same keywords.\n", + "\n", + "Get started with the following examples.\n", + "\n", + "|Notebook|Description||\n", + "|---|---|---|\n", + "||Introducing txtai](https://github.com/neuml/txtai/blob/master/examples/01_Introducing_txtai.ipynb) |Overview of \n", + "the functionality provided by txtai||\n", + "||Similarity search with \n", + "images](https://github.com/neuml/txtai/blob/master/examples/13_Similarity_search_with_images.ipynb) |Embed images \n", + "and text into the same space for search||\n", + "||Build a QA database](https://github.com/neuml/txtai/blob/master/examples/34_Build_a_QA_database.ipynb) |Question \n", + "matching with semantic search||\n", + "||Semantic Graphs](https://github.com/neuml/txtai/blob/master/examples/38_Introducing_the_Semantic_Graph.ipynb) \n", + "|Explore topics, data connectivity and run network analysis||\n", + "\n", + "### LLM Orchestration\n", + "Autonomous agents, retrieval augmented generation (RAG), chat with your data, pipelines and workflows that \n", + "interface with large language models (LLMs).\n", + "\n", + "See below to learn more.\n", + "\n", + "|Notebook|Description||\n", + "|---|---|---|\n", + "||Prompt templates and task \n", + "chains](https://github.com/neuml/txtai/blob/master/examples/44_Prompt_templates_and_task_chains.ipynb) |Build model\n", + "prompts and connect tasks together with workflows||\n", + "||Integrate LLM frameworks](https://github.com/neuml/txtai/blob/master/examples/53_Integrate_LLM_Frameworks.ipynb) \n", + "|Integrate llama.cpp, LiteLLM and custom generation frameworks||\n", + "||Build knowledge graphs with \n", + "LLMs](https://github.com/neuml/txtai/blob/master/examples/57_Build_knowledge_graphs_with_LLM_driven_entity_extracti\n", + "on.ipynb) |Build knowledge graphs with LLM-driven entity extraction||\n", + "||Parsing the stars with \n", + "txtai](https://github.com/neuml/txtai/blob/master/examples/72_Parsing_the_stars_with_txtai.ipynb) |Explore an \n", + "astronomical knowledge graph of known stars, planets, galaxies||\n", + "\n", + "#### Agents\n", + "Agents connect embeddings, pipelines, workflows and other agents together to autonomously solve complex problems.\n", + "\n", + "txtai agents are built on top of the |smolagents](https://github.com/huggingface/smolagents) framework. This \n", + "supports all LLMs txtai supports (Hugging Face, llama.cpp, OpenAI / Claude / AWS Bedrock via LiteLLM). Agent \n", + "prompting with |agents.md](https://github.com/agentsmd/agents.md) and \n", + "|skill.md](https://agentskills.io/specification) are also supported.\n", + "\n", + "Check out this |Agent Quickstart Example](https://github.com/neuml/txtai/blob/master/examples/agent_quickstart.py) \n", + ". Additional examples are listed below.\n", + "\n", + "|Notebook|Description||\n", + "|---|---|---|\n", + "||Analyzing Hugging Face Posts with Graphs and \n", + "Agents](https://github.com/neuml/txtai/blob/master/examples/68_Analyzing_Hugging_Face_Posts_with_Graphs_and_Agents.\n", + "ipynb) |Explore a rich dataset with Graph Analysis and Agents||\n", + "||Granting autonomy to \n", + "agents](https://github.com/neuml/txtai/blob/master/examples/69_Granting_autonomy_to_agents.ipynb) |Agents that \n", + "iteratively solve problems as they see fit||\n", + "||Analyzing LinkedIn Company Posts with Graphs and \n", + "Agents](https://github.com/neuml/txtai/blob/master/examples/71_Analyzing_LinkedIn_Company_Posts_with_Graphs_and_Age\n", + "nts.ipynb) |Exploring how to improve social media engagement with AI||\n", + "\n", + "#### Retrieval augmented generation\n", + "Retrieval augmented generation (RAG) reduces the risk of LLM hallucinations by constraining the output with a \n", + "knowledge base as context. RAG is commonly used to \"chat with your data\".\n", + "\n", + "Check out this |RAG Quickstart Example](https://github.com/neuml/txtai/blob/master/examples/rag_quickstart.py) . \n", + "Additional examples are listed below.\n", + "\n", + "|Notebook|Description||\n", + "|---|---|---|\n", + "||Build RAG pipelines with \n", + "txtai](https://github.com/neuml/txtai/blob/master/examples/52_Build_RAG_pipelines_with_txtai.ipynb) |Guide on \n", + "retrieval augmented generation including how to create citations||\n", + "||RAG is more than Vector \n", + "Search](https://github.com/neuml/txtai/blob/master/examples/79_RAG_is_more_than_Vector_Search.ipynb) |Context \n", + "retrieval via Web, SQL and other sources||\n", + "||GraphRAG with Wikipedia and GPT \n", + "OSS](https://github.com/neuml/txtai/blob/master/examples/77_GraphRAG_with_Wikipedia_and_GPT_OSS.ipynb) |Deep graph \n", + "search powered RAG||\n", + "||Speech to Speech RAG](https://github.com/neuml/txtai/blob/master/examples/65_Speech_to_Speech_RAG.ipynb) |Full \n", + "cycle speech to speech workflow with RAG||\n", + "\n", + "### Language Model Workflows\n", + "Language model workflows, also known as semantic workflows, connect language models together to build intelligent \n", + "applications.\n", + "\n", + "While LLMs are powerful, there are plenty of smaller, more specialized models that work better and faster for \n", + "specific tasks. This includes models for extractive question-answering, automatic summarization, text-to-speech, \n", + "transcription and translation.\n", + "\n", + "Check out this |Workflow Quickstart \n", + "Example](https://github.com/neuml/txtai/blob/master/examples/workflow_quickstart.py) . Additional examples are \n", + "listed below.\n", + "\n", + "|Notebook|Description||\n", + "|---|---|---|\n", + "||Run pipeline workflows](https://github.com/neuml/txtai/blob/master/examples/14_Run_pipeline_workflows.ipynb) \n", + "|Simple yet powerful constructs to efficiently process data||\n", + "||Building abstractive text \n", + "summaries](https://github.com/neuml/txtai/blob/master/examples/09_Building_abstractive_text_summaries.ipynb) |Run \n", + "abstractive text summarization||\n", + "||Transcribe audio to text](https://github.com/neuml/txtai/blob/master/examples/11_Transcribe_audio_to_text.ipynb) \n", + "|Convert audio files to text||\n", + "||Translate text between \n", + "languages](https://github.com/neuml/txtai/blob/master/examples/12_Translate_text_between_languages.ipynb) \n", + "|Streamline machine translation and language detection||\n", + "\n", + "## Installation\n", + "The easiest way to install is via pip and PyPI\n", + "\n", + "```\n", + "pip install txtai\n", + "```\n", + "\n", + "Python 3.10+ is supported. Using a Python |virtual environment](https://docs.python.org/3/library/venv.html) is \n", + "recommended.\n", + "\n", + "See the detailed |install instructions](https://neuml.github.io/txtai/install) for more information covering \n", + "|optional dependencies](https://neuml.github.io/txtai/install/#optional-dependencies) , |environment specific \n", + "prerequisites](https://neuml.github.io/txtai/install/#environment-specific-prerequisites) , |installing from \n", + "source](https://neuml.github.io/txtai/install/#install-from-source) , |conda \n", + "support](https://neuml.github.io/txtai/install/#conda) and how to |run with \n", + "containers](https://neuml.github.io/txtai/cloud) .\n", + "\n", + "## Model guide\n", + "See the table below for the current recommended models. These models all allow commercial use and offer a blend of \n", + "speed and performance.\n", + "\n", + "|Component|Model(s)|\n", + "|---|---|\n", + "||Embeddings](https://neuml.github.io/txtai/embeddings) \n", + "||all-MiniLM-L6-v2](https://hf.co/sentence-transformers/all-MiniLM-L6-v2) |\n", + "||Image Captions](https://neuml.github.io/txtai/pipeline/image/caption) \n", + "||BLIP](https://hf.co/Salesforce/blip-image-captioning-base) |\n", + "||Labels - Zero Shot](https://neuml.github.io/txtai/pipeline/text/labels) \n", + "||BART-Large-MNLI](https://hf.co/facebook/bart-large) |\n", + "||Labels - Fixed](https://neuml.github.io/txtai/pipeline/text/labels) |Fine-tune with |training \n", + "pipeline](https://neuml.github.io/txtai/pipeline/train/trainer) |\n", + "||Large Language Model (LLM)](https://neuml.github.io/txtai/pipeline/text/llm) \n", + "||gpt-oss-20b](https://huggingface.co/openai/gpt-oss-20b) |\n", + "||Summarization](https://neuml.github.io/txtai/pipeline/text/summary) \n", + "||DistilBART](https://hf.co/sshleifer/distilbart-cnn-12-6) |\n", + "||Text-to-Speech](https://neuml.github.io/txtai/pipeline/audio/texttospeech) ||ESPnet \n", + "JETS](https://hf.co/NeuML/ljspeech-jets-onnx) |\n", + "||Transcription](https://neuml.github.io/txtai/pipeline/audio/transcription) \n", + "||Whisper](https://hf.co/openai/whisper-base) |\n", + "||Translation](https://neuml.github.io/txtai/pipeline/text/translation) ||OPUS Model \n", + "Series](https://hf.co/Helsinki-NLP) |\n", + "Models can be loaded as either a path from the Hugging Face Hub or a local directory. Model paths are optional, \n", + "defaults are loaded when not specified. For tasks with no recommended model, txtai uses the default models as shown\n", + "in the Hugging Face Tasks guide.\n", + "\n", + "See the following links to learn more.\n", + "\n", + "## Powered by txtai\n", + "The following applications are powered by txtai.\n", + "\n", + "|Application|Description|\n", + "|---|---|\n", + "||rag](https://github.com/neuml/rag) |Retrieval Augmented Generation (RAG) application|\n", + "||ncoder](https://github.com/neuml/ncoder) |Open-Source AI coding agent|\n", + "||paperai](https://github.com/neuml/paperai) |AI for medical and scientific papers|\n", + "||annotateai](https://github.com/neuml/annotateai) |Automatically annotate papers with LLMs|\n", + "In addition to this list, there are also many other |open-source \n", + "projects](https://github.com/neuml/txtai/network/dependents) , |published \n", + "research](https://scholar.google.com/scholar?q=txtai&hl=en&as_ylo=2022) and closed proprietary/commercial projects \n", + "that have built on txtai in production.\n", + "\n", + "## Further Reading\n", + "- |Tutorial series on Hashnode](https://neuml.hashnode.dev/series/txtai-tutorial) | \n", + "|dev.to](https://dev.to/neuml/tutorial-series-on-txtai-ibg) \n", + "- |What's new in txtai 9.0](https://medium.com/neuml/whats-new-in-txtai-9-0-d522bb150afa) | \n", + "|8.0](https://medium.com/neuml/whats-new-in-txtai-8-0-2d7d0ab4506b) | \n", + "|7.0](https://medium.com/neuml/whats-new-in-txtai-7-0-855ad6a55440) | \n", + "|6.0](https://medium.com/neuml/whats-new-in-txtai-6-0-7d93eeedf804) | \n", + "|5.0](https://medium.com/neuml/whats-new-in-txtai-5-0-e5c75a13b101) | \n", + "|4.0](https://medium.com/neuml/whats-new-in-txtai-4-0-bbc3a65c3d1c) \n", + "- |Getting started with semantic \n", + "search](https://medium.com/neuml/getting-started-with-semantic-search-a9fd9d8a48cf) | \n", + "|workflows](https://medium.com/neuml/getting-started-with-semantic-workflows-2fefda6165d9) | \n", + "|rag](https://medium.com/neuml/getting-started-with-rag-9a0cca75f748) \n", + "\n", + "## Documentation\n", + "|Full documentation on txtai](https://neuml.github.io/txtai) including configuration settings for embeddings, \n", + "pipelines, workflows, API and a FAQ with common questions/issues is available.\n", + "\n", + "## Contributing\n", + "For those who would like to contribute to txtai, please see |this \n", + "guide](https://github.com/neuml/.github/blob/master/CONTRIBUTING.md) .\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 2: Duration 1.76 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 2: Duration 1.76 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 3 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 3\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'read' with arguments: {'path':                                                                   │\n",
+       "│ 'https://github.com/neuml/txtai/blob/master/examples/01_Introducing_txtai.ipynb'}                               │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'read' with arguments: {'path': │\n", + "│ 'https://github.com/neuml/txtai/blob/master/examples/01_Introducing_txtai.ipynb'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Error executing tool 'read' with arguments {'path': \n",
+       "'https://github.com/neuml/txtai/blob/master/examples/01_Introducing_txtai.ipynb'}: HTTPError: HTTP Error 429: Too \n",
+       "Many Requests\n",
+       "Please try again or use another tool\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[1;31mError executing tool 'read' with arguments {'path': \u001b[0m\n", + "\u001b[1;31m'https://github.com/neuml/txtai/blob/master/examples/01_Introducing_txtai.ipynb'}: HTTPError: HTTP Error 429: Too \u001b[0m\n", + "\u001b[1;31mMany Requests\u001b[0m\n", + "\u001b[1;31mPlease try again or use another tool\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 3: Duration 3.48 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 3: Duration 3.48 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 4 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 4\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'read' with arguments: {'path': 'https://neuml.github.io/txtai/'}                                 │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'read' with arguments: {'path': 'https://neuml.github.io/txtai/'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: **txtai**\n",
+       "\n",
+       "*txtai is an all-in-one open-source AI framework for semantic search, LLM orchestration and language model \n",
+       "workflows*\n",
+       "\n",
+       "**All-in-one AI framework** \n",
+       "txtai is an all-in-one AI framework for semantic search, LLM orchestration and language model workflows.\n",
+       "\n",
+       "The key component of txtai is an embeddings database, which is a union of vector indexes (sparse and dense), graph \n",
+       "networks and relational databases.\n",
+       "\n",
+       "This foundation enables vector search and/or serves as a powerful knowledge source for large language model (LLM) \n",
+       "applications.\n",
+       "\n",
+       "Build autonomous agents, retrieval augmented generation (RAG) processes, multi-model workflows and more.\n",
+       "\n",
+       "Summary of txtai features:\n",
+       "\n",
+       "- 🔎 Vector search with SQL, object storage, topic modeling, graph analysis and multimodal indexing\n",
+       "- 📄 Create embeddings for text, documents, audio, images and video\n",
+       "- 💡 Pipelines powered by language models that run LLM prompts, question-answering, labeling, transcription, \n",
+       "translation, summarization and more\n",
+       "- ↪️️ Workflows to join pipelines together and aggregate business logic. txtai processes can be simple microservices\n",
+       "or multi-model workflows.\n",
+       "- 🤖 Agents that intelligently connect embeddings, pipelines, workflows and other agents together to autonomously \n",
+       "solve complex problems\n",
+       "- ⚙️ Web and Model Context Protocol (MCP) APIs. Bindings available for \n",
+       "|JavaScript](https://github.com/neuml/txtai.js) , |Java](https://github.com/neuml/txtai.java) , \n",
+       "|Rust](https://github.com/neuml/txtai.rs) and |Go](https://github.com/neuml/txtai.go) .\n",
+       "- 🔋 Batteries included with defaults to get up and running fast\n",
+       "- ☁️ Run local or scale out with container orchestration\n",
+       "txtai is built with Python 3.10+, |Hugging Face Transformers](https://github.com/huggingface/transformers) , \n",
+       "|Sentence Transformers](https://github.com/UKPLab/sentence-transformers) and \n",
+       "|FastAPI](https://github.com/tiangolo/fastapi) . txtai is open-source under an Apache 2.0 license.\n",
+       "\n",
+       "Note\n",
+       "\n",
+       "|NeuML](https://neuml.com) is the company behind txtai and we provide AI consulting services around our stack. \n",
+       "|Schedule a meeting](https://cal.com/neuml/intro) or |send a message](mailto:info@neuml.com) to learn more.\n",
+       "\n",
+       "We're also building an easy and secure way to run hosted txtai applications with |txtai.cloud](https://txtai.cloud)\n",
+       ".\n",
+       "
\n" + ], + "text/plain": [ + "Observations: **txtai**\n", + "\n", + "*txtai is an all-in-one open-source AI framework for semantic search, LLM orchestration and language model \n", + "workflows*\n", + "\n", + "**All-in-one AI framework** \n", + "txtai is an all-in-one AI framework for semantic search, LLM orchestration and language model workflows.\n", + "\n", + "The key component of txtai is an embeddings database, which is a union of vector indexes (sparse and dense), graph \n", + "networks and relational databases.\n", + "\n", + "This foundation enables vector search and/or serves as a powerful knowledge source for large language model (LLM) \n", + "applications.\n", + "\n", + "Build autonomous agents, retrieval augmented generation (RAG) processes, multi-model workflows and more.\n", + "\n", + "Summary of txtai features:\n", + "\n", + "- 🔎 Vector search with SQL, object storage, topic modeling, graph analysis and multimodal indexing\n", + "- 📄 Create embeddings for text, documents, audio, images and video\n", + "- 💡 Pipelines powered by language models that run LLM prompts, question-answering, labeling, transcription, \n", + "translation, summarization and more\n", + "- ↪️️ Workflows to join pipelines together and aggregate business logic. txtai processes can be simple microservices\n", + "or multi-model workflows.\n", + "- 🤖 Agents that intelligently connect embeddings, pipelines, workflows and other agents together to autonomously \n", + "solve complex problems\n", + "- ⚙️ Web and Model Context Protocol (MCP) APIs. Bindings available for \n", + "|JavaScript](https://github.com/neuml/txtai.js) , |Java](https://github.com/neuml/txtai.java) , \n", + "|Rust](https://github.com/neuml/txtai.rs) and |Go](https://github.com/neuml/txtai.go) .\n", + "- 🔋 Batteries included with defaults to get up and running fast\n", + "- ☁️ Run local or scale out with container orchestration\n", + "txtai is built with Python 3.10+, |Hugging Face Transformers](https://github.com/huggingface/transformers) , \n", + "|Sentence Transformers](https://github.com/UKPLab/sentence-transformers) and \n", + "|FastAPI](https://github.com/tiangolo/fastapi) . txtai is open-source under an Apache 2.0 license.\n", + "\n", + "Note\n", + "\n", + "|NeuML](https://neuml.com) is the company behind txtai and we provide AI consulting services around our stack. \n", + "|Schedule a meeting](https://cal.com/neuml/intro) or |send a message](mailto:info@neuml.com) to learn more.\n", + "\n", + "We're also building an easy and secure way to run hosted txtai applications with |txtai.cloud](https://txtai.cloud)\n", + ".\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 4: Duration 1.00 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 4: Duration 1.00 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 5 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 5\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'read' with arguments: {'path': 'https://neuml.github.io/txtai/install/'}                         │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'read' with arguments: {'path': 'https://neuml.github.io/txtai/install/'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: **Installation - txtai**\n",
+       "\n",
+       "*txtai is an all-in-one open-source AI framework for semantic search, LLM orchestration and language model \n",
+       "workflows*\n",
+       "\n",
+       "# Installation\n",
+       "The easiest way to install is via pip and PyPI\n",
+       "\n",
+       "```\n",
+       "pip install txtai\n",
+       "```\n",
+       "\n",
+       "Python 3.10+ is supported. Using a Python |virtual environment](https://docs.python.org/3/library/venv.html) is \n",
+       "recommended.\n",
+       "\n",
+       "## Optional dependencies\n",
+       "txtai has the following optional dependencies that can be installed as extras. The patterns below are supported\n",
+       "in setup.py install_requires sections.\n",
+       "\n",
+       "*Note: Extras are provided for convenience. Alternatively, individual packages can be installed to limit \n",
+       "dependencies.* \n",
+       "\n",
+       "### All\n",
+       "Install all dependencies.\n",
+       "\n",
+       "```\n",
+       "pip install txtai|all]\n",
+       "```\n",
+       "\n",
+       "### ANN\n",
+       "Additional ANN backends.\n",
+       "\n",
+       "```\n",
+       "pip install txtai|ann]\n",
+       "```\n",
+       "\n",
+       "### API\n",
+       "Serve txtai via a web API.\n",
+       "\n",
+       "```\n",
+       "pip install txtai|api]\n",
+       "```\n",
+       "\n",
+       "### Cloud\n",
+       "Interface with cloud compute.\n",
+       "\n",
+       "```\n",
+       "pip install txtai|cloud]\n",
+       "```\n",
+       "\n",
+       "### Console\n",
+       "Command line index query console.\n",
+       "\n",
+       "```\n",
+       "pip install txtai|console]\n",
+       "```\n",
+       "\n",
+       "### Database\n",
+       "Additional content storage options.\n",
+       "\n",
+       "```\n",
+       "pip install txtai|database]\n",
+       "```\n",
+       "\n",
+       "### Graph\n",
+       "Topic modeling, data connectivity and network analysis.\n",
+       "\n",
+       "```\n",
+       "pip install txtai|graph]\n",
+       "```\n",
+       "\n",
+       "### Model\n",
+       "Additional non-standard models.\n",
+       "\n",
+       "```\n",
+       "pip install txtai|model]\n",
+       "```\n",
+       "\n",
+       "### Pipeline\n",
+       "All pipelines - default install comes with most common pipelines.\n",
+       "\n",
+       "```\n",
+       "pip install txtai|pipeline]\n",
+       "```\n",
+       "\n",
+       "More granular extras are available for pipeline categories: pipeline-audio, pipeline-data, pipeline-image, \n",
+       "pipeline-llm, pipeline-text, and pipeline-train.\n",
+       "\n",
+       "### Scoring\n",
+       "Additional scoring methods.\n",
+       "\n",
+       "```\n",
+       "pip install txtai|scoring]\n",
+       "```\n",
+       "\n",
+       "### Vectors\n",
+       "Additional vector methods.\n",
+       "\n",
+       "```\n",
+       "pip install txtai|vectors]\n",
+       "```\n",
+       "\n",
+       "### Workflow\n",
+       "All workflow tasks - default install comes with most common workflow tasks.\n",
+       "\n",
+       "```\n",
+       "pip install txtai|workflow]\n",
+       "```\n",
+       "\n",
+       "### Combining dependencies\n",
+       "Multiple dependencies can be specified at the same time.\n",
+       "\n",
+       "```\n",
+       "pip install txtai|pipeline,workflow]\n",
+       "```\n",
+       "\n",
+       "## Environment specific prerequisites\n",
+       "Additional environment specific prerequisites are below.\n",
+       "\n",
+       "### Linux\n",
+       "The AudioStream and Microphone pipelines require the \n",
+       "|PortAudio](https://python-sounddevice.readthedocs.io/en/0.5.0/installation.html) system library. The Transcription\n",
+       "pipeline requires the |SoundFile](https://github.com/bastibe/python-soundfile#installation) system library.\n",
+       "\n",
+       "### macOS\n",
+       "Older versions of Faiss have a runtime dependency on libomp for macOS. Run brew install libomp in this case.\n",
+       "\n",
+       "The AudioStream and Microphone pipelines require the \n",
+       "|PortAudio](https://python-sounddevice.readthedocs.io/en/0.5.0/installation.html) system library. Run brew install \n",
+       "portaudio.\n",
+       "\n",
+       "### Windows\n",
+       "Optional dependencies require |C++ Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/)\n",
+       "\n",
+       "The |txtai build workflow](https://github.com/neuml/txtai/blob/master/.github/workflows/build.yml) occasionally has\n",
+       "work arounds for other known but temporary dependency issues. The |FAQ](../faq) also has a list of common problems,\n",
+       "including common installation issues.\n",
+       "\n",
+       "## CPU-only\n",
+       "The default install adds PyTorch with GPU support. There are a number of dependencies that come with that. When \n",
+       "running in a CPU-only environment or using Embeddings/LLM models without PyTorch (i.e. llama.cpp or API services), \n",
+       "the CPU-only PyTorch package can be installed with txtai as follows.\n",
+       "\n",
+       "```\n",
+       "pip install txtai torch==|version]+cpu \\\n",
+       "-f https://download.pytorch.org/whl/torch\n",
+       "```\n",
+       "\n",
+       "Where |version] is the version of PyTorch (such as 2.4.1). The \n",
+       "|txtai-cpu](https://hub.docker.com/r/neuml/txtai-cpu) image on Docker Hub uses this method to reduce the image \n",
+       "size.\n",
+       "\n",
+       "## Install from source\n",
+       "txtai can also be installed directly from GitHub to access the latest, unreleased features.\n",
+       "\n",
+       "```\n",
+       "pip install git+https://github.com/neuml/txtai\n",
+       "```\n",
+       "\n",
+       "Extras can be installed from GitHub by adding #egg=txtai|<name-of-extra>] to the end of the above URL.\n",
+       "\n",
+       "## Conda\n",
+       "A |community-supported txtai package](https://anaconda.org/conda-forge/txtai) is available via conda-forge.\n",
+       "\n",
+       "```\n",
+       "conda install -c conda-forge txtai\n",
+       "```\n",
+       "\n",
+       "## Run with containers\n",
+       "Docker images are available for txtai. |See this section](../cloud) for more information on container-based \n",
+       "installs.\n",
+       "
\n" + ], + "text/plain": [ + "Observations: **Installation - txtai**\n", + "\n", + "*txtai is an all-in-one open-source AI framework for semantic search, LLM orchestration and language model \n", + "workflows*\n", + "\n", + "# Installation\n", + "The easiest way to install is via pip and PyPI\n", + "\n", + "```\n", + "pip install txtai\n", + "```\n", + "\n", + "Python 3.10+ is supported. Using a Python |virtual environment](https://docs.python.org/3/library/venv.html) is \n", + "recommended.\n", + "\n", + "## Optional dependencies\n", + "txtai has the following optional dependencies that can be installed as extras. The patterns below are supported\n", + "in setup.py install_requires sections.\n", + "\n", + "*Note: Extras are provided for convenience. Alternatively, individual packages can be installed to limit \n", + "dependencies.* \n", + "\n", + "### All\n", + "Install all dependencies.\n", + "\n", + "```\n", + "pip install txtai|all]\n", + "```\n", + "\n", + "### ANN\n", + "Additional ANN backends.\n", + "\n", + "```\n", + "pip install txtai|ann]\n", + "```\n", + "\n", + "### API\n", + "Serve txtai via a web API.\n", + "\n", + "```\n", + "pip install txtai|api]\n", + "```\n", + "\n", + "### Cloud\n", + "Interface with cloud compute.\n", + "\n", + "```\n", + "pip install txtai|cloud]\n", + "```\n", + "\n", + "### Console\n", + "Command line index query console.\n", + "\n", + "```\n", + "pip install txtai|console]\n", + "```\n", + "\n", + "### Database\n", + "Additional content storage options.\n", + "\n", + "```\n", + "pip install txtai|database]\n", + "```\n", + "\n", + "### Graph\n", + "Topic modeling, data connectivity and network analysis.\n", + "\n", + "```\n", + "pip install txtai|graph]\n", + "```\n", + "\n", + "### Model\n", + "Additional non-standard models.\n", + "\n", + "```\n", + "pip install txtai|model]\n", + "```\n", + "\n", + "### Pipeline\n", + "All pipelines - default install comes with most common pipelines.\n", + "\n", + "```\n", + "pip install txtai|pipeline]\n", + "```\n", + "\n", + "More granular extras are available for pipeline categories: pipeline-audio, pipeline-data, pipeline-image, \n", + "pipeline-llm, pipeline-text, and pipeline-train.\n", + "\n", + "### Scoring\n", + "Additional scoring methods.\n", + "\n", + "```\n", + "pip install txtai|scoring]\n", + "```\n", + "\n", + "### Vectors\n", + "Additional vector methods.\n", + "\n", + "```\n", + "pip install txtai|vectors]\n", + "```\n", + "\n", + "### Workflow\n", + "All workflow tasks - default install comes with most common workflow tasks.\n", + "\n", + "```\n", + "pip install txtai|workflow]\n", + "```\n", + "\n", + "### Combining dependencies\n", + "Multiple dependencies can be specified at the same time.\n", + "\n", + "```\n", + "pip install txtai|pipeline,workflow]\n", + "```\n", + "\n", + "## Environment specific prerequisites\n", + "Additional environment specific prerequisites are below.\n", + "\n", + "### Linux\n", + "The AudioStream and Microphone pipelines require the \n", + "|PortAudio](https://python-sounddevice.readthedocs.io/en/0.5.0/installation.html) system library. The Transcription\n", + "pipeline requires the |SoundFile](https://github.com/bastibe/python-soundfile#installation) system library.\n", + "\n", + "### macOS\n", + "Older versions of Faiss have a runtime dependency on libomp for macOS. Run brew install libomp in this case.\n", + "\n", + "The AudioStream and Microphone pipelines require the \n", + "|PortAudio](https://python-sounddevice.readthedocs.io/en/0.5.0/installation.html) system library. Run brew install \n", + "portaudio.\n", + "\n", + "### Windows\n", + "Optional dependencies require |C++ Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/)\n", + "\n", + "The |txtai build workflow](https://github.com/neuml/txtai/blob/master/.github/workflows/build.yml) occasionally has\n", + "work arounds for other known but temporary dependency issues. The |FAQ](../faq) also has a list of common problems,\n", + "including common installation issues.\n", + "\n", + "## CPU-only\n", + "The default install adds PyTorch with GPU support. There are a number of dependencies that come with that. When \n", + "running in a CPU-only environment or using Embeddings/LLM models without PyTorch (i.e. llama.cpp or API services), \n", + "the CPU-only PyTorch package can be installed with txtai as follows.\n", + "\n", + "```\n", + "pip install txtai torch==|version]+cpu \\\n", + "-f https://download.pytorch.org/whl/torch\n", + "```\n", + "\n", + "Where |version] is the version of PyTorch (such as 2.4.1). The \n", + "|txtai-cpu](https://hub.docker.com/r/neuml/txtai-cpu) image on Docker Hub uses this method to reduce the image \n", + "size.\n", + "\n", + "## Install from source\n", + "txtai can also be installed directly from GitHub to access the latest, unreleased features.\n", + "\n", + "```\n", + "pip install git+https://github.com/neuml/txtai\n", + "```\n", + "\n", + "Extras can be installed from GitHub by adding #egg=txtai|] to the end of the above URL.\n", + "\n", + "## Conda\n", + "A |community-supported txtai package](https://anaconda.org/conda-forge/txtai) is available via conda-forge.\n", + "\n", + "```\n", + "conda install -c conda-forge txtai\n", + "```\n", + "\n", + "## Run with containers\n", + "Docker images are available for txtai. |See this section](../cloud) for more information on container-based \n", + "installs.\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 5: Duration 1.35 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 5: Duration 1.35 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 6 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 6\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'write' with arguments: {'path': 'txtai_facts.md', 'content': '# txtai - All-in-One AI            │\n",
+       "│ Framework\\n\\n## Overview\\n\\ntxtai is an all-in-one open-source AI framework for semantic search, LLM            │\n",
+       "│ orchestration, and language model workflows. It\\'s built with Python 3.10+, Hugging Face Transformers, Sentence │\n",
+       "│ Transformers, and FastAPI, and is licensed under Apache 2.0.\\n\\n## Key Features\\n\\n- **Vector Search**:         │\n",
+       "│ Semantic search with SQL, object storage, topic modeling, graph analysis, and multimodal indexing\\n-            │\n",
+       "│ **Embeddings**: Create embeddings for text, documents, audio, images, and video\\n- **Language Model             │\n",
+       "│ Pipelines**: Run LLM prompts, question-answering, labeling, transcription, translation, summarization and       │\n",
+       "│ more\\n- **Workflows**: Join pipelines together and aggregate business logic\\n- **Agents**: Intelligently        │\n",
+       "│ connect embeddings, pipelines, workflows and other agents to autonomously solve complex problems\\n- **API       │\n",
+       "│ Support**: Web and Model Context Protocol (MCP) APIs with bindings for JavaScript, Java, Rust, and Go\\n-        │\n",
+       "│ **Local/Cloud Deployment**: Run locally or scale out with container orchestration\\n\\n## Core Components\\n\\nThe  │\n",
+       "│ key component of txtai is an embeddings database, which is a union of:\\n- Vector indexes (sparse and dense)\\n-  │\n",
+       "│ Graph networks\\n- Relational databases\\n\\nThis foundation enables vector search and/or serves as a powerful     │\n",
+       "│ knowledge source for large language model (LLM) applications.\\n\\n## Use Cases\\n\\n### Semantic Search\\n- Build   │\n",
+       "│ semantic/similarity/vector/neural search applications\\n- Traditional search systems use keywords; semantic      │\n",
+       "│ search understands natural language\\n\\n### LLM Orchestration\\n- Autonomous agents\\n- Retrieval Augmented        │\n",
+       "│ Generation (RAG)\\n- Chat with your data\\n- Multi-model workflows\\n\\n### Language Model Workflows\\n- Connect     │\n",
+       "│ language models together to build intelligent applications\\n- Specialized models for extractive                 │\n",
+       "│ question-answering, automatic summarization, text-to-speech, transcription, and translation\\n\\n##               │\n",
+       "│ Installation\\n\\nThe easiest way to install is via pip:\\n\\n```bash\\npip install txtai\\n```\\n\\nOptional           │\n",
+       "│ dependencies can be installed as extras:\\n\\n```bash\\npip install txtai[all]  # Install all dependencies\\npip    │\n",
+       "│ install txtai[api]  # Serve txtai via a web API\\npip install txtai[workflow]  # All workflow tasks\\n```\\n\\n##   │\n",
+       "│ Technology Stack\\n\\n- Built with Python 3.10+\\n- Hugging Face Transformers\\n- Sentence Transformers\\n-          │\n",
+       "│ FastAPI\\n\\n## Supported Models\\n\\n- Embeddings: all-MiniLM-L6-v2\\n- Image Captions: BLIP\\n- Labels (Zero Shot): │\n",
+       "│ BART-Large-MNLI\\n- Large Language Model (LLM): gpt-oss-20b\\n- Summarization: DistilBART\\n- Text-to-Speech:      │\n",
+       "│ ESPnet JETS\\n- Transcription: Whisper\\n- Translation: OPUS Model Series\\n\\n## Company Behind txtai\\n\\nNeuML is  │\n",
+       "│ the company behind txtai and provides AI consulting services around their stack. They also offer hosted         │\n",
+       "│ solutions through txtai.cloud.\\n\\n## Applications Powered by txtai\\n\\n- RAG (Retrieval Augmented Generation)\\n- │\n",
+       "│ ncoder (Open-Source AI coding agent)\\n- PaperAI (AI for medical and scientific papers)\\n- AnnotateAI            │\n",
+       "│ (Automatically annotate papers with LLMs)\\n\\n## Getting Started\\n\\nSimple example to get                        │\n",
+       "│ started:\\n\\n```python\\nimport txtai\\n\\nembeddings = txtai.Embeddings()\\nembeddings.index([\"Correct\", \"Not what  │\n",
+       "│ we hoped\"])\\nembeddings.search(\"positive\", 1)\\n# [(0, 0.29862046241760254)]\\n```\\n\\n## Documentation\\n\\nFull    │\n",
+       "│ documentation is available at [txtai Documentation](https://neuml.github.io/txtai)\\n'}                          │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'write' with arguments: {'path': 'txtai_facts.md', 'content': '# txtai - All-in-One AI │\n", + "│ Framework\\n\\n## Overview\\n\\ntxtai is an all-in-one open-source AI framework for semantic search, LLM │\n", + "│ orchestration, and language model workflows. It\\'s built with Python 3.10+, Hugging Face Transformers, Sentence │\n", + "│ Transformers, and FastAPI, and is licensed under Apache 2.0.\\n\\n## Key Features\\n\\n- **Vector Search**: │\n", + "│ Semantic search with SQL, object storage, topic modeling, graph analysis, and multimodal indexing\\n- │\n", + "│ **Embeddings**: Create embeddings for text, documents, audio, images, and video\\n- **Language Model │\n", + "│ Pipelines**: Run LLM prompts, question-answering, labeling, transcription, translation, summarization and │\n", + "│ more\\n- **Workflows**: Join pipelines together and aggregate business logic\\n- **Agents**: Intelligently │\n", + "│ connect embeddings, pipelines, workflows and other agents to autonomously solve complex problems\\n- **API │\n", + "│ Support**: Web and Model Context Protocol (MCP) APIs with bindings for JavaScript, Java, Rust, and Go\\n- │\n", + "│ **Local/Cloud Deployment**: Run locally or scale out with container orchestration\\n\\n## Core Components\\n\\nThe │\n", + "│ key component of txtai is an embeddings database, which is a union of:\\n- Vector indexes (sparse and dense)\\n- │\n", + "│ Graph networks\\n- Relational databases\\n\\nThis foundation enables vector search and/or serves as a powerful │\n", + "│ knowledge source for large language model (LLM) applications.\\n\\n## Use Cases\\n\\n### Semantic Search\\n- Build │\n", + "│ semantic/similarity/vector/neural search applications\\n- Traditional search systems use keywords; semantic │\n", + "│ search understands natural language\\n\\n### LLM Orchestration\\n- Autonomous agents\\n- Retrieval Augmented │\n", + "│ Generation (RAG)\\n- Chat with your data\\n- Multi-model workflows\\n\\n### Language Model Workflows\\n- Connect │\n", + "│ language models together to build intelligent applications\\n- Specialized models for extractive │\n", + "│ question-answering, automatic summarization, text-to-speech, transcription, and translation\\n\\n## │\n", + "│ Installation\\n\\nThe easiest way to install is via pip:\\n\\n```bash\\npip install txtai\\n```\\n\\nOptional │\n", + "│ dependencies can be installed as extras:\\n\\n```bash\\npip install txtai[all] # Install all dependencies\\npip │\n", + "│ install txtai[api] # Serve txtai via a web API\\npip install txtai[workflow] # All workflow tasks\\n```\\n\\n## │\n", + "│ Technology Stack\\n\\n- Built with Python 3.10+\\n- Hugging Face Transformers\\n- Sentence Transformers\\n- │\n", + "│ FastAPI\\n\\n## Supported Models\\n\\n- Embeddings: all-MiniLM-L6-v2\\n- Image Captions: BLIP\\n- Labels (Zero Shot): │\n", + "│ BART-Large-MNLI\\n- Large Language Model (LLM): gpt-oss-20b\\n- Summarization: DistilBART\\n- Text-to-Speech: │\n", + "│ ESPnet JETS\\n- Transcription: Whisper\\n- Translation: OPUS Model Series\\n\\n## Company Behind txtai\\n\\nNeuML is │\n", + "│ the company behind txtai and provides AI consulting services around their stack. They also offer hosted │\n", + "│ solutions through txtai.cloud.\\n\\n## Applications Powered by txtai\\n\\n- RAG (Retrieval Augmented Generation)\\n- │\n", + "│ ncoder (Open-Source AI coding agent)\\n- PaperAI (AI for medical and scientific papers)\\n- AnnotateAI │\n", + "│ (Automatically annotate papers with LLMs)\\n\\n## Getting Started\\n\\nSimple example to get │\n", + "│ started:\\n\\n```python\\nimport txtai\\n\\nembeddings = txtai.Embeddings()\\nembeddings.index([\"Correct\", \"Not what │\n", + "│ we hoped\"])\\nembeddings.search(\"positive\", 1)\\n# [(0, 0.29862046241760254)]\\n```\\n\\n## Documentation\\n\\nFull │\n", + "│ documentation is available at [txtai Documentation](https://neuml.github.io/txtai)\\n'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: None\n",
+       "
\n" + ], + "text/plain": [ + "Observations: None\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 6: Duration 14.19 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 6: Duration 14.19 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 7 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 7\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'final_answer' with arguments: {'answer': \"I've created a comprehensive markdown file with facts  │\n",
+       "│ about txtai. The file 'txtai_facts.md' contains detailed information about txtai including its overview, key    │\n",
+       "│ features, use cases, installation instructions, technology stack, supported models, and more.\"}                 │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'final_answer' with arguments: {'answer': \"I've created a comprehensive markdown file with facts │\n", + "│ about txtai. The file 'txtai_facts.md' contains detailed information about txtai including its overview, key │\n", + "│ features, use cases, installation instructions, technology stack, supported models, and more.\"} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: I've created a comprehensive markdown file with facts about txtai. The file 'txtai_facts.md' contains\n",
+       "detailed information about txtai including its overview, key features, use cases, installation instructions, \n",
+       "technology stack, supported models, and more.\n",
+       "
\n" + ], + "text/plain": [ + "Observations: I've created a comprehensive markdown file with facts about txtai. The file 'txtai_facts.md' contains\n", + "detailed information about txtai including its overview, key features, use cases, installation instructions, \n", + "technology stack, supported models, and more.\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Final answer: I've created a comprehensive markdown file with facts about txtai. The file 'txtai_facts.md' contains\n",
+       "detailed information about txtai including its overview, key features, use cases, installation instructions, \n",
+       "technology stack, supported models, and more.\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[1;38;2;212;183;2mFinal answer: I've created a comprehensive markdown file with facts about txtai. The file 'txtai_facts.md' contains\u001b[0m\n", + "\u001b[1;38;2;212;183;2mdetailed information about txtai including its overview, key features, use cases, installation instructions, \u001b[0m\n", + "\u001b[1;38;2;212;183;2mtechnology stack, supported models, and more.\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 7: Duration 2.31 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 7: Duration 2.31 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "\"I've created a comprehensive markdown file with facts about txtai. The file 'txtai_facts.md' contains detailed information about txtai including its overview, key features, use cases, installation instructions, technology stack, supported models, and more.\"" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "agent(\"Research txtai and write a markdown file with some facts about it\")" + ] + }, + { + "cell_type": "markdown", + "id": "4bad4944", + "metadata": {}, + "source": [ + "The `read` tool also supports reading web content seamlessly. This example ran web searches, read a few webpages then write it's research to a Markdown file. If it worked correctly the output file should look similar to this.\n", + "\n", + "```markdown\n", + "# txtai - All-in-One AI Framework\n", + "\n", + "## Overview\n", + "\n", + "txtai is an all-in-one open-source AI framework for semantic search, LLM orchestration, and language model workflows. It's built with Python 3.10+, Hugging Face Transformers, Sentence Transformers, and FastAPI, and is licensed under Apache 2.0.\n", + "\n", + "## Key Features\n", + "\n", + "- **Vector Search**: Semantic search with SQL, object storage, topic modeling, graph analysis, and multimodal indexing\n", + "- **Embeddings**: Create embeddings for text, documents, audio, images, and video\n", + "- **Language Model Pipelines**: Run LLM prompts, question-answering, labeling, transcription, translation, summarization and more\n", + "- **Workflows**: Join pipelines together and aggregate business logic\n", + "- **Agents**: Intelligently connect embeddings, pipelines, workflows and other agents to autonomously solve complex problems\n", + "- **API Support**: Web and Model Context Protocol (MCP) APIs with bindings for JavaScript, Java, Rust, and Go\n", + "- **Local/Cloud Deployment**: Run locally or scale out with container orchestration\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "0c4f117a", + "metadata": {}, + "source": [ + "# Summarize content in a Technical Article\n", + "\n", + "The next example will read the BERT paper and summarize it. Once again, the `read` tool will get the text so the LLM can process the content. " + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "3f86ec4f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
╭──────────────────────────────────────────────────── New run ────────────────────────────────────────────────────╮\n",
+       "                                                                                                                 \n",
+       " Read https://arxiv.org/pdf/1810.04805 and write a summary about it                                              \n",
+       "                                                                                                                 \n",
+       "╰─ PipelineModel - unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF/Qwen3-Coder-30B-A3B-Instruct-Q3_K_M.gguf ────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m╭─\u001b[0m\u001b[38;2;212;183;2m───────────────────────────────────────────────────\u001b[0m\u001b[38;2;212;183;2m \u001b[0m\u001b[1;38;2;212;183;2mNew run\u001b[0m\u001b[38;2;212;183;2m \u001b[0m\u001b[38;2;212;183;2m───────────────────────────────────────────────────\u001b[0m\u001b[38;2;212;183;2m─╮\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mRead https://arxiv.org/pdf/1810.04805 and write a summary about it\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m╰─\u001b[0m\u001b[38;2;212;183;2m PipelineModel - unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF/Qwen3-Coder-30B-A3B-Instruct-Q3_K_M.gguf \u001b[0m\u001b[38;2;212;183;2m───────────\u001b[0m\u001b[38;2;212;183;2m─╯\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 1 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 1\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'read' with arguments: {'path': 'https://arxiv.org/pdf/1810.04805'}                               │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'read' with arguments: {'path': 'https://arxiv.org/pdf/1810.04805'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: BERT: Pre-training of Deep Bidirectional Transformers for\n",
+       "Language Understanding\n",
+       "\n",
+       "Jacob Devlin Ming-Wei Chang Kenton Lee Kristina Toutanova\n",
+       "Google AI Language\n",
+       "\n",
+       "{jacobdevlin,mingweichang,kentonl,kristout}@google.com\n",
+       "\n",
+       "Abstract\n",
+       "\n",
+       "We introduce a new language representa-\n",
+       "tion model called BERT, which stands for\n",
+       "Bidirectional Encoder Representations from\n",
+       "Transformers. Unlike recent language repre-\n",
+       "sentation models (Peters et al., 2018a; Rad-\n",
+       "ford et al., 2018), BERT is designed to pre-\n",
+       "train deep bidirectional representations from\n",
+       "unlabeled text by jointly conditioning on both\n",
+       "left and right context in all layers. As a re-\n",
+       "sult, the pre-trained BERT model can be fine-\n",
+       "tuned with just one additional output layer\n",
+       "to create state-of-the-art models for a wide\n",
+       "range of tasks, such as question answering and\n",
+       "language inference, without substantial task-\n",
+       "specific architecture modifications.\n",
+       "[TRUNCATED...]\n",
+       "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 1: Duration 1.33 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 1: Duration 1.33 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 2 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 2\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'write' with arguments: {'path': 'bert_summary.txt', 'content': 'BERT: Pre-training of Deep       │\n",
+       "│ Bidirectional Transformers for Language Understanding\\n\\nSummary:\\n\\nBERT (Bidirectional Encoder                │\n",
+       "│ Representations from Transformers) is a language representation model introduced by Google AI Language          │\n",
+       "│ researchers. Unlike previous approaches that used unidirectional language models, BERT uses a bidirectional     │\n",
+       "│ approach to pre-train deep representations from unlabeled text.\\n\\nKey innovations of BERT include:\\n\\n1.       │\n",
+       "│ Bidirectional Pre-training: Unlike previous models like OpenAI GPT that use left-to-right language models, BERT │\n",
+       "│ uses a masked language model (MLM) pre-training objective that allows it to condition on both left and right    │\n",
+       "│ context in all layers.\\n\\n2. Two Pre-training Tasks: BERT uses two unsupervised pre-training tasks - Masked     │\n",
+       "│ Language Model (predicting masked tokens) and Next Sentence Prediction (understanding relationships between     │\n",
+       "│ sentences).\\n\\n3. Unified Architecture: BERT uses the same architecture for both pre-training and fine-tuning,  │\n",
+       "│ with minimal differences between the two stages.\\n\\n4. Fine-tuning Approach: BERT can be fine-tuned with just   │\n",
+       "│ one additional output layer for various downstream tasks, without substantial task-specific architecture        │\n",
+       "│ modifications.\\n\\nPerformance Improvements:\\n\\nBERT achieved state-of-the-art results on 11 natural language    │\n",
+       "│ processing tasks, including:\\n- GLUE score of 80.5% (7.7% absolute improvement)\\n- MultiNLI accuracy of 86.7%   │\n",
+       "│ (4.6% absolute improvement)\\n- SQuAD v1.1 question answering Test F1 of 93.2 (1.5 point absolute                │\n",
+       "│ improvement)\\n- SQuAD v2.0 Test F1 of 83.1 (5.1 point absolute improvement)\\n\\nThe model comes in two sizes:    │\n",
+       "│ BERTBASE (L=12, H=768, A=12) and BERTLARGE (L=24, H=1024, A=16) with 110M and 340M parameters                   │\n",
+       "│ respectively.\\n\\nBERT demonstrated that bidirectional pre-training is crucial for language representations and  │\n",
+       "│ that pre-trained representations significantly reduce the need for heavily-engineered task-specific             │\n",
+       "│ architectures.'}                                                                                                │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'write' with arguments: {'path': 'bert_summary.txt', 'content': 'BERT: Pre-training of Deep │\n", + "│ Bidirectional Transformers for Language Understanding\\n\\nSummary:\\n\\nBERT (Bidirectional Encoder │\n", + "│ Representations from Transformers) is a language representation model introduced by Google AI Language │\n", + "│ researchers. Unlike previous approaches that used unidirectional language models, BERT uses a bidirectional │\n", + "│ approach to pre-train deep representations from unlabeled text.\\n\\nKey innovations of BERT include:\\n\\n1. │\n", + "│ Bidirectional Pre-training: Unlike previous models like OpenAI GPT that use left-to-right language models, BERT │\n", + "│ uses a masked language model (MLM) pre-training objective that allows it to condition on both left and right │\n", + "│ context in all layers.\\n\\n2. Two Pre-training Tasks: BERT uses two unsupervised pre-training tasks - Masked │\n", + "│ Language Model (predicting masked tokens) and Next Sentence Prediction (understanding relationships between │\n", + "│ sentences).\\n\\n3. Unified Architecture: BERT uses the same architecture for both pre-training and fine-tuning, │\n", + "│ with minimal differences between the two stages.\\n\\n4. Fine-tuning Approach: BERT can be fine-tuned with just │\n", + "│ one additional output layer for various downstream tasks, without substantial task-specific architecture │\n", + "│ modifications.\\n\\nPerformance Improvements:\\n\\nBERT achieved state-of-the-art results on 11 natural language │\n", + "│ processing tasks, including:\\n- GLUE score of 80.5% (7.7% absolute improvement)\\n- MultiNLI accuracy of 86.7% │\n", + "│ (4.6% absolute improvement)\\n- SQuAD v1.1 question answering Test F1 of 93.2 (1.5 point absolute │\n", + "│ improvement)\\n- SQuAD v2.0 Test F1 of 83.1 (5.1 point absolute improvement)\\n\\nThe model comes in two sizes: │\n", + "│ BERTBASE (L=12, H=768, A=12) and BERTLARGE (L=24, H=1024, A=16) with 110M and 340M parameters │\n", + "│ respectively.\\n\\nBERT demonstrated that bidirectional pre-training is crucial for language representations and │\n", + "│ that pre-trained representations significantly reduce the need for heavily-engineered task-specific │\n", + "│ architectures.'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: None\n",
+       "
\n" + ], + "text/plain": [ + "Observations: None\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 2: Duration 17.82 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 2: Duration 17.82 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 3 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 3\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'final_answer' with arguments: {'answer': \"I've written a summary of the BERT paper to the file   │\n",
+       "│ 'bert_summary.txt'. The summary covers BERT's key innovations including bidirectional pre-training using masked │\n",
+       "│ language models and next sentence prediction, its unified architecture approach, and its state-of-the-art       │\n",
+       "│ performance on 11 NLP tasks.\"}                                                                                  │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'final_answer' with arguments: {'answer': \"I've written a summary of the BERT paper to the file │\n", + "│ 'bert_summary.txt'. The summary covers BERT's key innovations including bidirectional pre-training using masked │\n", + "│ language models and next sentence prediction, its unified architecture approach, and its state-of-the-art │\n", + "│ performance on 11 NLP tasks.\"} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: I've written a summary of the BERT paper to the file 'bert_summary.txt'. The summary covers BERT's \n",
+       "key innovations including bidirectional pre-training using masked language models and next sentence prediction, its\n",
+       "unified architecture approach, and its state-of-the-art performance on 11 NLP tasks.\n",
+       "
\n" + ], + "text/plain": [ + "Observations: I've written a summary of the BERT paper to the file 'bert_summary.txt'. The summary covers BERT's \n", + "key innovations including bidirectional pre-training using masked language models and next sentence prediction, its\n", + "unified architecture approach, and its state-of-the-art performance on 11 NLP tasks.\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Final answer: I've written a summary of the BERT paper to the file 'bert_summary.txt'. The summary covers BERT's \n",
+       "key innovations including bidirectional pre-training using masked language models and next sentence prediction, its\n",
+       "unified architecture approach, and its state-of-the-art performance on 11 NLP tasks.\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[1;38;2;212;183;2mFinal answer: I've written a summary of the BERT paper to the file 'bert_summary.txt'. The summary covers BERT's \u001b[0m\n", + "\u001b[1;38;2;212;183;2mkey innovations including bidirectional pre-training using masked language models and next sentence prediction, its\u001b[0m\n", + "\u001b[1;38;2;212;183;2munified architecture approach, and its state-of-the-art performance on 11 NLP tasks.\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 3: Duration 2.25 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 3: Duration 2.25 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "\"I've written a summary of the BERT paper to the file 'bert_summary.txt'. The summary covers BERT's key innovations including bidirectional pre-training using masked language models and next sentence prediction, its unified architecture approach, and its state-of-the-art performance on 11 NLP tasks.\"" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "agent(\"Read https://arxiv.org/pdf/1810.04805 and write a summary about it\", maxlength=25000)" + ] + }, + { + "cell_type": "markdown", + "id": "7f369276", + "metadata": {}, + "source": [ + "When this step works properly, the output file will look something like below.\n", + "\n", + "```\n", + "BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding\n", + "\n", + "Summary:\n", + "\n", + "BERT (Bidirectional Encoder Representations from Transformers) is a language representation model introduced by Google AI Language researchers. Unlike previous approaches that used unidirectional language models, BERT uses a bidirectional approach to pre-train deep representations from unlabeled text.\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "8bf119ba", + "metadata": {}, + "source": [ + "# Searching a code base\n", + "\n", + "Next let's see if the agent can look in the txtai codebase and find a RAG example." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "a4687784", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
╭──────────────────────────────────────────────────── New run ────────────────────────────────────────────────────╮\n",
+       "                                                                                                                 \n",
+       " Look for a RAG demo in the ./txtai/examples directory. Print the file content.                                  \n",
+       "                                                                                                                 \n",
+       "╰─ PipelineModel - unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF/Qwen3-Coder-30B-A3B-Instruct-Q3_K_M.gguf ────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m╭─\u001b[0m\u001b[38;2;212;183;2m───────────────────────────────────────────────────\u001b[0m\u001b[38;2;212;183;2m \u001b[0m\u001b[1;38;2;212;183;2mNew run\u001b[0m\u001b[38;2;212;183;2m \u001b[0m\u001b[38;2;212;183;2m───────────────────────────────────────────────────\u001b[0m\u001b[38;2;212;183;2m─╮\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mLook for a RAG demo in the ./txtai/examples directory. Print the file content.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m╰─\u001b[0m\u001b[38;2;212;183;2m PipelineModel - unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF/Qwen3-Coder-30B-A3B-Instruct-Q3_K_M.gguf \u001b[0m\u001b[38;2;212;183;2m───────────\u001b[0m\u001b[38;2;212;183;2m─╯\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 1 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 1\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'glob' with arguments: {'files': './txtai/examples/*rag*'}                                        │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'glob' with arguments: {'files': './txtai/examples/*rag*'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: |'./txtai/examples/rag_quickstart.py']\n",
+       "
\n" + ], + "text/plain": [ + "Observations: |'./txtai/examples/rag_quickstart.py']\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 1: Duration 0.39 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 1: Duration 0.39 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 2 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 2\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'read' with arguments: {'path': './txtai/examples/rag_quickstart.py'}                             │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'read' with arguments: {'path': './txtai/examples/rag_quickstart.py'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: \"\"\"\n",
+       "RAG Quick Start\n",
+       "Easy to use way to get started with RAG using YOUR data\n",
+       "\n",
+       "For a complete application see this: https://github.com/neuml/rag\n",
+       "\n",
+       "TxtAI has many example notebooks covering everything the framework provides\n",
+       "Examples: https://neuml.github.io/txtai/examples\n",
+       "\n",
+       "Install TxtAI\n",
+       " pip install txtai|pipeline-data]\n",
+       "\"\"\"\n",
+       "\n",
+       "# pylint: disable=C0103\n",
+       "import os\n",
+       "\n",
+       "from txtai import Embeddings, RAG\n",
+       "from txtai.pipeline import Textractor\n",
+       "\n",
+       "# Step 1: Collect files from local directory\n",
+       "#\n",
+       "# Defaults to \"data\". Set to whereever your files are.\n",
+       "path = \"data\"\n",
+       "files = |os.path.join(path, f) for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]\n",
+       "\n",
+       "# Step 2: Text Extraction / Chunking\n",
+       "#\n",
+       "# Using section based chunking here. More complex options available such as semantic chunking, iterative chunking \n",
+       "etc.\n",
+       "# Documentation: https://neuml.github.io/txtai/pipeline/data/textractor\n",
+       "# Supports Chonkie chunking as well: https://docs.chonkie.ai/oss/chunkers/overview\n",
+       "textractor = Textractor(backend=\"docling\", sections=True)\n",
+       "chunks = |]\n",
+       "for f in files:\n",
+       " for chunk in textractor(f):\n",
+       " chunks.append((f, chunk))\n",
+       "\n",
+       "# Step 3: Build an embeddings database\n",
+       "#\n",
+       "# The `path` parameter sets the vector embeddings model. Supports Hugging Face models, llama.cpp, Ollama, vLLM and \n",
+       "more.\n",
+       "# Documentation: https://neuml.github.io/txtai/embeddings/\n",
+       "embeddings = Embeddings(content=True, path=\"Qwen/Qwen3-Embedding-0.6B\", maxlength=2048)\n",
+       "embeddings.index(chunks)\n",
+       "\n",
+       "# Step 4: Create RAG pipeline\n",
+       "#\n",
+       "# Combines an embeddings database and an LLM.\n",
+       "# Supports Hugging Face models, llama.cpp, Ollama, vLLM and more\n",
+       "# Documentation: https://neuml.github.io/txtai/pipeline/text/rag\n",
+       "\n",
+       "# User prompt template\n",
+       "template = \"\"\"\n",
+       " Answer the following question using the provided context.\n",
+       "\n",
+       " Question:\n",
+       " {question}\n",
+       "\n",
+       " Context:\n",
+       " {context}\n",
+       "\"\"\"\n",
+       "\n",
+       "rag = RAG(\n",
+       " embeddings,\n",
+       " \"Qwen/Qwen3-0.6B\",\n",
+       " system=\"You are a friendly assistant\",\n",
+       " template=template,\n",
+       " output=\"flatten\",\n",
+       ")\n",
+       "\n",
+       "question = \"Summarize the main advancements made by BERT\"\n",
+       "print(rag(question, maxlength=2048, stripthink=True))\n",
+       "
\n" + ], + "text/plain": [ + "Observations: \"\"\"\n", + "RAG Quick Start\n", + "Easy to use way to get started with RAG using YOUR data\n", + "\n", + "For a complete application see this: https://github.com/neuml/rag\n", + "\n", + "TxtAI has many example notebooks covering everything the framework provides\n", + "Examples: https://neuml.github.io/txtai/examples\n", + "\n", + "Install TxtAI\n", + " pip install txtai|pipeline-data]\n", + "\"\"\"\n", + "\n", + "# pylint: disable=C0103\n", + "import os\n", + "\n", + "from txtai import Embeddings, RAG\n", + "from txtai.pipeline import Textractor\n", + "\n", + "# Step 1: Collect files from local directory\n", + "#\n", + "# Defaults to \"data\". Set to whereever your files are.\n", + "path = \"data\"\n", + "files = |os.path.join(path, f) for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]\n", + "\n", + "# Step 2: Text Extraction / Chunking\n", + "#\n", + "# Using section based chunking here. More complex options available such as semantic chunking, iterative chunking \n", + "etc.\n", + "# Documentation: https://neuml.github.io/txtai/pipeline/data/textractor\n", + "# Supports Chonkie chunking as well: https://docs.chonkie.ai/oss/chunkers/overview\n", + "textractor = Textractor(backend=\"docling\", sections=True)\n", + "chunks = |]\n", + "for f in files:\n", + " for chunk in textractor(f):\n", + " chunks.append((f, chunk))\n", + "\n", + "# Step 3: Build an embeddings database\n", + "#\n", + "# The `path` parameter sets the vector embeddings model. Supports Hugging Face models, llama.cpp, Ollama, vLLM and \n", + "more.\n", + "# Documentation: https://neuml.github.io/txtai/embeddings/\n", + "embeddings = Embeddings(content=True, path=\"Qwen/Qwen3-Embedding-0.6B\", maxlength=2048)\n", + "embeddings.index(chunks)\n", + "\n", + "# Step 4: Create RAG pipeline\n", + "#\n", + "# Combines an embeddings database and an LLM.\n", + "# Supports Hugging Face models, llama.cpp, Ollama, vLLM and more\n", + "# Documentation: https://neuml.github.io/txtai/pipeline/text/rag\n", + "\n", + "# User prompt template\n", + "template = \"\"\"\n", + " Answer the following question using the provided context.\n", + "\n", + " Question:\n", + " {question}\n", + "\n", + " Context:\n", + " {context}\n", + "\"\"\"\n", + "\n", + "rag = RAG(\n", + " embeddings,\n", + " \"Qwen/Qwen3-0.6B\",\n", + " system=\"You are a friendly assistant\",\n", + " template=template,\n", + " output=\"flatten\",\n", + ")\n", + "\n", + "question = \"Summarize the main advancements made by BERT\"\n", + "print(rag(question, maxlength=2048, stripthink=True))\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 2: Duration 0.45 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 2: Duration 0.45 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 3 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 3\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'final_answer' with arguments: {'answer': 'Found RAG demo in ./txtai/examples/rag_quickstart.py.  │\n",
+       "│ Here is its content:\\n\\n```\\nRAG Quick Start\\nEasy to use way to get started with RAG using YOUR data\\n\\nFor a  │\n",
+       "│ complete application see this: https://github.com/neuml/rag\\n\\nTxtAI has many example notebooks covering        │\n",
+       "│ everything the framework provides\\nExamples: https://neuml.github.io/txtai/examples\\n\\nInstall TxtAI\\n pip      │\n",
+       "│ install txtai[pipeline-data]\\n\\n# pylint: disable=C0103\\nimport os\\n\\nfrom txtai import Embeddings, RAG\\nfrom   │\n",
+       "│ txtai.pipeline import Textractor\\n\\n# Step 1: Collect files from local directory\\n#\\n# Defaults to \"data\". Set  │\n",
+       "│ to whereever your files are.\\npath = \"data\"\\nfiles = [os.path.join(path, f) for f in os.listdir(path) if        │\n",
+       "│ os.path.isfile(os.path.join(path, f))]\\n\\n# Step 2: Text Extraction / Chunking\\n#\\n# Using section based        │\n",
+       "│ chunking here. More complex options available such as semantic chunking, iterative chunking etc.\\n#             │\n",
+       "│ Documentation: https://neuml.github.io/txtai/pipeline/data/textractor\\n# Supports Chonkie chunking as well:     │\n",
+       "│ https://docs.chonkie.ai/oss/chunkers/overview\\ntextractor = Textractor(backend=\"docling\",                       │\n",
+       "│ sections=True)\\nchunks = []\\nfor f in files:\\n for chunk in textractor(f):\\n  chunks.append((f, chunk))\\n\\n#    │\n",
+       "│ Step 3: Build an embeddings database\\n#\\n# The `path` parameter sets the vector embeddings model. Supports      │\n",
+       "│ Hugging Face models, llama.cpp, Ollama, vLLM and more.\\n# Documentation:                                        │\n",
+       "│ https://neuml.github.io/txtai/embeddings/\\nembeddings = Embeddings(content=True,                                │\n",
+       "│ path=\"Qwen/Qwen3-Embedding-0.6B\", maxlength=2048)\\nembeddings.index(chunks)\\n\\n# Step 4: Create RAG             │\n",
+       "│ pipeline\\n#\\n# Combines an embeddings database and an LLM.\\n# Supports Hugging Face models, llama.cpp, Ollama,  │\n",
+       "│ vLLM and more\\n# Documentation: https://neuml.github.io/txtai/pipeline/text/rag\\n\\n# User prompt                │\n",
+       "│ template\\ntemplate = \"\"\"\\n Answer the following question using the provided context.\\n\\n Question:\\n            │\n",
+       "│ {question}\\n\\n Context:\\n {context}\\n\"\"\"\\n\\nrag = RAG(\\n embeddings,\\n \"Qwen/Qwen3-0.6B\",\\n system=\"You are a   │\n",
+       "│ friendly assistant\",\\n template=template,\\n output=\"flatten\",\\n)\\n\\nquestion = \"Summarize the main advancements │\n",
+       "│ made by BERT\"\\nprint(rag(question, maxlength=2048, stripthink=True))\\n```'}                                     │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'final_answer' with arguments: {'answer': 'Found RAG demo in ./txtai/examples/rag_quickstart.py. │\n", + "│ Here is its content:\\n\\n```\\nRAG Quick Start\\nEasy to use way to get started with RAG using YOUR data\\n\\nFor a │\n", + "│ complete application see this: https://github.com/neuml/rag\\n\\nTxtAI has many example notebooks covering │\n", + "│ everything the framework provides\\nExamples: https://neuml.github.io/txtai/examples\\n\\nInstall TxtAI\\n pip │\n", + "│ install txtai[pipeline-data]\\n\\n# pylint: disable=C0103\\nimport os\\n\\nfrom txtai import Embeddings, RAG\\nfrom │\n", + "│ txtai.pipeline import Textractor\\n\\n# Step 1: Collect files from local directory\\n#\\n# Defaults to \"data\". Set │\n", + "│ to whereever your files are.\\npath = \"data\"\\nfiles = [os.path.join(path, f) for f in os.listdir(path) if │\n", + "│ os.path.isfile(os.path.join(path, f))]\\n\\n# Step 2: Text Extraction / Chunking\\n#\\n# Using section based │\n", + "│ chunking here. More complex options available such as semantic chunking, iterative chunking etc.\\n# │\n", + "│ Documentation: https://neuml.github.io/txtai/pipeline/data/textractor\\n# Supports Chonkie chunking as well: │\n", + "│ https://docs.chonkie.ai/oss/chunkers/overview\\ntextractor = Textractor(backend=\"docling\", │\n", + "│ sections=True)\\nchunks = []\\nfor f in files:\\n for chunk in textractor(f):\\n chunks.append((f, chunk))\\n\\n# │\n", + "│ Step 3: Build an embeddings database\\n#\\n# The `path` parameter sets the vector embeddings model. Supports │\n", + "│ Hugging Face models, llama.cpp, Ollama, vLLM and more.\\n# Documentation: │\n", + "│ https://neuml.github.io/txtai/embeddings/\\nembeddings = Embeddings(content=True, │\n", + "│ path=\"Qwen/Qwen3-Embedding-0.6B\", maxlength=2048)\\nembeddings.index(chunks)\\n\\n# Step 4: Create RAG │\n", + "│ pipeline\\n#\\n# Combines an embeddings database and an LLM.\\n# Supports Hugging Face models, llama.cpp, Ollama, │\n", + "│ vLLM and more\\n# Documentation: https://neuml.github.io/txtai/pipeline/text/rag\\n\\n# User prompt │\n", + "│ template\\ntemplate = \"\"\"\\n Answer the following question using the provided context.\\n\\n Question:\\n │\n", + "│ {question}\\n\\n Context:\\n {context}\\n\"\"\"\\n\\nrag = RAG(\\n embeddings,\\n \"Qwen/Qwen3-0.6B\",\\n system=\"You are a │\n", + "│ friendly assistant\",\\n template=template,\\n output=\"flatten\",\\n)\\n\\nquestion = \"Summarize the main advancements │\n", + "│ made by BERT\"\\nprint(rag(question, maxlength=2048, stripthink=True))\\n```'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: Found RAG demo in ./txtai/examples/rag_quickstart.py. Here is its content:\n",
+       "\n",
+       "```\n",
+       "RAG Quick Start\n",
+       "Easy to use way to get started with RAG using YOUR data\n",
+       "\n",
+       "For a complete application see this: https://github.com/neuml/rag\n",
+       "\n",
+       "TxtAI has many example notebooks covering everything the framework provides\n",
+       "Examples: https://neuml.github.io/txtai/examples\n",
+       "\n",
+       "Install TxtAI\n",
+       " pip install txtai|pipeline-data]\n",
+       "\n",
+       "# pylint: disable=C0103\n",
+       "import os\n",
+       "\n",
+       "from txtai import Embeddings, RAG\n",
+       "from txtai.pipeline import Textractor\n",
+       "\n",
+       "# Step 1: Collect files from local directory\n",
+       "#\n",
+       "# Defaults to \"data\". Set to whereever your files are.\n",
+       "path = \"data\"\n",
+       "files = |os.path.join(path, f) for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]\n",
+       "\n",
+       "# Step 2: Text Extraction / Chunking\n",
+       "#\n",
+       "# Using section based chunking here. More complex options available such as semantic chunking, iterative chunking \n",
+       "etc.\n",
+       "# Documentation: https://neuml.github.io/txtai/pipeline/data/textractor\n",
+       "# Supports Chonkie chunking as well: https://docs.chonkie.ai/oss/chunkers/overview\n",
+       "textractor = Textractor(backend=\"docling\", sections=True)\n",
+       "chunks = |]\n",
+       "for f in files:\n",
+       " for chunk in textractor(f):\n",
+       "  chunks.append((f, chunk))\n",
+       "\n",
+       "# Step 3: Build an embeddings database\n",
+       "#\n",
+       "# The `path` parameter sets the vector embeddings model. Supports Hugging Face models, llama.cpp, Ollama, vLLM and \n",
+       "more.\n",
+       "# Documentation: https://neuml.github.io/txtai/embeddings/\n",
+       "embeddings = Embeddings(content=True, path=\"Qwen/Qwen3-Embedding-0.6B\", maxlength=2048)\n",
+       "embeddings.index(chunks)\n",
+       "\n",
+       "# Step 4: Create RAG pipeline\n",
+       "#\n",
+       "# Combines an embeddings database and an LLM.\n",
+       "# Supports Hugging Face models, llama.cpp, Ollama, vLLM and more\n",
+       "# Documentation: https://neuml.github.io/txtai/pipeline/text/rag\n",
+       "\n",
+       "# User prompt template\n",
+       "template = \"\"\"\n",
+       " Answer the following question using the provided context.\n",
+       "\n",
+       " Question:\n",
+       " {question}\n",
+       "\n",
+       " Context:\n",
+       " {context}\n",
+       "\"\"\"\n",
+       "\n",
+       "rag = RAG(\n",
+       " embeddings,\n",
+       " \"Qwen/Qwen3-0.6B\",\n",
+       " system=\"You are a friendly assistant\",\n",
+       " template=template,\n",
+       " output=\"flatten\",\n",
+       ")\n",
+       "\n",
+       "question = \"Summarize the main advancements made by BERT\"\n",
+       "print(rag(question, maxlength=2048, stripthink=True))\n",
+       "```\n",
+       "
\n" + ], + "text/plain": [ + "Observations: Found RAG demo in ./txtai/examples/rag_quickstart.py. Here is its content:\n", + "\n", + "```\n", + "RAG Quick Start\n", + "Easy to use way to get started with RAG using YOUR data\n", + "\n", + "For a complete application see this: https://github.com/neuml/rag\n", + "\n", + "TxtAI has many example notebooks covering everything the framework provides\n", + "Examples: https://neuml.github.io/txtai/examples\n", + "\n", + "Install TxtAI\n", + " pip install txtai|pipeline-data]\n", + "\n", + "# pylint: disable=C0103\n", + "import os\n", + "\n", + "from txtai import Embeddings, RAG\n", + "from txtai.pipeline import Textractor\n", + "\n", + "# Step 1: Collect files from local directory\n", + "#\n", + "# Defaults to \"data\". Set to whereever your files are.\n", + "path = \"data\"\n", + "files = |os.path.join(path, f) for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]\n", + "\n", + "# Step 2: Text Extraction / Chunking\n", + "#\n", + "# Using section based chunking here. More complex options available such as semantic chunking, iterative chunking \n", + "etc.\n", + "# Documentation: https://neuml.github.io/txtai/pipeline/data/textractor\n", + "# Supports Chonkie chunking as well: https://docs.chonkie.ai/oss/chunkers/overview\n", + "textractor = Textractor(backend=\"docling\", sections=True)\n", + "chunks = |]\n", + "for f in files:\n", + " for chunk in textractor(f):\n", + " chunks.append((f, chunk))\n", + "\n", + "# Step 3: Build an embeddings database\n", + "#\n", + "# The `path` parameter sets the vector embeddings model. Supports Hugging Face models, llama.cpp, Ollama, vLLM and \n", + "more.\n", + "# Documentation: https://neuml.github.io/txtai/embeddings/\n", + "embeddings = Embeddings(content=True, path=\"Qwen/Qwen3-Embedding-0.6B\", maxlength=2048)\n", + "embeddings.index(chunks)\n", + "\n", + "# Step 4: Create RAG pipeline\n", + "#\n", + "# Combines an embeddings database and an LLM.\n", + "# Supports Hugging Face models, llama.cpp, Ollama, vLLM and more\n", + "# Documentation: https://neuml.github.io/txtai/pipeline/text/rag\n", + "\n", + "# User prompt template\n", + "template = \"\"\"\n", + " Answer the following question using the provided context.\n", + "\n", + " Question:\n", + " {question}\n", + "\n", + " Context:\n", + " {context}\n", + "\"\"\"\n", + "\n", + "rag = RAG(\n", + " embeddings,\n", + " \"Qwen/Qwen3-0.6B\",\n", + " system=\"You are a friendly assistant\",\n", + " template=template,\n", + " output=\"flatten\",\n", + ")\n", + "\n", + "question = \"Summarize the main advancements made by BERT\"\n", + "print(rag(question, maxlength=2048, stripthink=True))\n", + "```\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Final answer: Found RAG demo in ./txtai/examples/rag_quickstart.py. Here is its content:\n",
+       "\n",
+       "```\n",
+       "RAG Quick Start\n",
+       "Easy to use way to get started with RAG using YOUR data\n",
+       "\n",
+       "For a complete application see this: https://github.com/neuml/rag\n",
+       "\n",
+       "TxtAI has many example notebooks covering everything the framework provides\n",
+       "Examples: https://neuml.github.io/txtai/examples\n",
+       "\n",
+       "Install TxtAI\n",
+       " pip install txtai[pipeline-data]\n",
+       "\n",
+       "# pylint: disable=C0103\n",
+       "import os\n",
+       "\n",
+       "from txtai import Embeddings, RAG\n",
+       "from txtai.pipeline import Textractor\n",
+       "\n",
+       "# Step 1: Collect files from local directory\n",
+       "#\n",
+       "# Defaults to \"data\". Set to whereever your files are.\n",
+       "path = \"data\"\n",
+       "files = [os.path.join(path, f) for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]\n",
+       "\n",
+       "# Step 2: Text Extraction / Chunking\n",
+       "#\n",
+       "# Using section based chunking here. More complex options available such as semantic chunking, iterative chunking \n",
+       "etc.\n",
+       "# Documentation: https://neuml.github.io/txtai/pipeline/data/textractor\n",
+       "# Supports Chonkie chunking as well: https://docs.chonkie.ai/oss/chunkers/overview\n",
+       "textractor = Textractor(backend=\"docling\", sections=True)\n",
+       "chunks = []\n",
+       "for f in files:\n",
+       " for chunk in textractor(f):\n",
+       "  chunks.append((f, chunk))\n",
+       "\n",
+       "# Step 3: Build an embeddings database\n",
+       "#\n",
+       "# The `path` parameter sets the vector embeddings model. Supports Hugging Face models, llama.cpp, Ollama, vLLM and \n",
+       "more.\n",
+       "# Documentation: https://neuml.github.io/txtai/embeddings/\n",
+       "embeddings = Embeddings(content=True, path=\"Qwen/Qwen3-Embedding-0.6B\", maxlength=2048)\n",
+       "embeddings.index(chunks)\n",
+       "\n",
+       "# Step 4: Create RAG pipeline\n",
+       "#\n",
+       "# Combines an embeddings database and an LLM.\n",
+       "# Supports Hugging Face models, llama.cpp, Ollama, vLLM and more\n",
+       "# Documentation: https://neuml.github.io/txtai/pipeline/text/rag\n",
+       "\n",
+       "# User prompt template\n",
+       "template = \"\"\"\n",
+       " Answer the following question using the provided context.\n",
+       "\n",
+       " Question:\n",
+       " {question}\n",
+       "\n",
+       " Context:\n",
+       " {context}\n",
+       "\"\"\"\n",
+       "\n",
+       "rag = RAG(\n",
+       " embeddings,\n",
+       " \"Qwen/Qwen3-0.6B\",\n",
+       " system=\"You are a friendly assistant\",\n",
+       " template=template,\n",
+       " output=\"flatten\",\n",
+       ")\n",
+       "\n",
+       "question = \"Summarize the main advancements made by BERT\"\n",
+       "print(rag(question, maxlength=2048, stripthink=True))\n",
+       "```\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[1;38;2;212;183;2mFinal answer: Found RAG demo in ./txtai/examples/rag_quickstart.py. Here is its content:\u001b[0m\n", + "\n", + "\u001b[1;38;2;212;183;2m```\u001b[0m\n", + "\u001b[1;38;2;212;183;2mRAG Quick Start\u001b[0m\n", + "\u001b[1;38;2;212;183;2mEasy to use way to get started with RAG using YOUR data\u001b[0m\n", + "\n", + "\u001b[1;38;2;212;183;2mFor a complete application see this: https://github.com/neuml/rag\u001b[0m\n", + "\n", + "\u001b[1;38;2;212;183;2mTxtAI has many example notebooks covering everything the framework provides\u001b[0m\n", + "\u001b[1;38;2;212;183;2mExamples: https://neuml.github.io/txtai/examples\u001b[0m\n", + "\n", + "\u001b[1;38;2;212;183;2mInstall TxtAI\u001b[0m\n", + "\u001b[1;38;2;212;183;2m pip install txtai[pipeline-data]\u001b[0m\n", + "\n", + "\u001b[1;38;2;212;183;2m# pylint: disable=C0103\u001b[0m\n", + "\u001b[1;38;2;212;183;2mimport os\u001b[0m\n", + "\n", + "\u001b[1;38;2;212;183;2mfrom txtai import Embeddings, RAG\u001b[0m\n", + "\u001b[1;38;2;212;183;2mfrom txtai.pipeline import Textractor\u001b[0m\n", + "\n", + "\u001b[1;38;2;212;183;2m# Step 1: Collect files from local directory\u001b[0m\n", + "\u001b[1;38;2;212;183;2m#\u001b[0m\n", + "\u001b[1;38;2;212;183;2m# Defaults to \"data\". Set to whereever your files are.\u001b[0m\n", + "\u001b[1;38;2;212;183;2mpath = \"data\"\u001b[0m\n", + "\u001b[1;38;2;212;183;2mfiles = [os.path.join(path, f) for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]\u001b[0m\n", + "\n", + "\u001b[1;38;2;212;183;2m# Step 2: Text Extraction / Chunking\u001b[0m\n", + "\u001b[1;38;2;212;183;2m#\u001b[0m\n", + "\u001b[1;38;2;212;183;2m# Using section based chunking here. More complex options available such as semantic chunking, iterative chunking \u001b[0m\n", + "\u001b[1;38;2;212;183;2metc.\u001b[0m\n", + "\u001b[1;38;2;212;183;2m# Documentation: https://neuml.github.io/txtai/pipeline/data/textractor\u001b[0m\n", + "\u001b[1;38;2;212;183;2m# Supports Chonkie chunking as well: https://docs.chonkie.ai/oss/chunkers/overview\u001b[0m\n", + "\u001b[1;38;2;212;183;2mtextractor = Textractor(backend=\"docling\", sections=True)\u001b[0m\n", + "\u001b[1;38;2;212;183;2mchunks = []\u001b[0m\n", + "\u001b[1;38;2;212;183;2mfor f in files:\u001b[0m\n", + "\u001b[1;38;2;212;183;2m for chunk in textractor(f):\u001b[0m\n", + "\u001b[1;38;2;212;183;2m chunks.append((f, chunk))\u001b[0m\n", + "\n", + "\u001b[1;38;2;212;183;2m# Step 3: Build an embeddings database\u001b[0m\n", + "\u001b[1;38;2;212;183;2m#\u001b[0m\n", + "\u001b[1;38;2;212;183;2m# The `path` parameter sets the vector embeddings model. Supports Hugging Face models, llama.cpp, Ollama, vLLM and \u001b[0m\n", + "\u001b[1;38;2;212;183;2mmore.\u001b[0m\n", + "\u001b[1;38;2;212;183;2m# Documentation: https://neuml.github.io/txtai/embeddings/\u001b[0m\n", + "\u001b[1;38;2;212;183;2membeddings = Embeddings(content=True, path=\"Qwen/Qwen3-Embedding-0.6B\", maxlength=2048)\u001b[0m\n", + "\u001b[1;38;2;212;183;2membeddings.index(chunks)\u001b[0m\n", + "\n", + "\u001b[1;38;2;212;183;2m# Step 4: Create RAG pipeline\u001b[0m\n", + "\u001b[1;38;2;212;183;2m#\u001b[0m\n", + "\u001b[1;38;2;212;183;2m# Combines an embeddings database and an LLM.\u001b[0m\n", + "\u001b[1;38;2;212;183;2m# Supports Hugging Face models, llama.cpp, Ollama, vLLM and more\u001b[0m\n", + "\u001b[1;38;2;212;183;2m# Documentation: https://neuml.github.io/txtai/pipeline/text/rag\u001b[0m\n", + "\n", + "\u001b[1;38;2;212;183;2m# User prompt template\u001b[0m\n", + "\u001b[1;38;2;212;183;2mtemplate = \"\"\"\u001b[0m\n", + "\u001b[1;38;2;212;183;2m Answer the following question using the provided context.\u001b[0m\n", + "\n", + "\u001b[1;38;2;212;183;2m Question:\u001b[0m\n", + "\u001b[1;38;2;212;183;2m {question}\u001b[0m\n", + "\n", + "\u001b[1;38;2;212;183;2m Context:\u001b[0m\n", + "\u001b[1;38;2;212;183;2m {context}\u001b[0m\n", + "\u001b[1;38;2;212;183;2m\"\"\"\u001b[0m\n", + "\n", + "\u001b[1;38;2;212;183;2mrag = RAG(\u001b[0m\n", + "\u001b[1;38;2;212;183;2m embeddings,\u001b[0m\n", + "\u001b[1;38;2;212;183;2m \"Qwen/Qwen3-0.6B\",\u001b[0m\n", + "\u001b[1;38;2;212;183;2m system=\"You are a friendly assistant\",\u001b[0m\n", + "\u001b[1;38;2;212;183;2m template=template,\u001b[0m\n", + "\u001b[1;38;2;212;183;2m output=\"flatten\",\u001b[0m\n", + "\u001b[1;38;2;212;183;2m)\u001b[0m\n", + "\n", + "\u001b[1;38;2;212;183;2mquestion = \"Summarize the main advancements made by BERT\"\u001b[0m\n", + "\u001b[1;38;2;212;183;2mprint(rag(question, maxlength=2048, stripthink=True))\u001b[0m\n", + "\u001b[1;38;2;212;183;2m```\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 3: Duration 7.66 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 3: Duration 7.66 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "'Found RAG demo in ./txtai/examples/rag_quickstart.py. Here is its content:\\n\\n```\\nRAG Quick Start\\nEasy to use way to get started with RAG using YOUR data\\n\\nFor a complete application see this: https://github.com/neuml/rag\\n\\nTxtAI has many example notebooks covering everything the framework provides\\nExamples: https://neuml.github.io/txtai/examples\\n\\nInstall TxtAI\\n pip install txtai[pipeline-data]\\n\\n# pylint: disable=C0103\\nimport os\\n\\nfrom txtai import Embeddings, RAG\\nfrom txtai.pipeline import Textractor\\n\\n# Step 1: Collect files from local directory\\n#\\n# Defaults to \"data\". Set to whereever your files are.\\npath = \"data\"\\nfiles = [os.path.join(path, f) for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]\\n\\n# Step 2: Text Extraction / Chunking\\n#\\n# Using section based chunking here. More complex options available such as semantic chunking, iterative chunking etc.\\n# Documentation: https://neuml.github.io/txtai/pipeline/data/textractor\\n# Supports Chonkie chunking as well: https://docs.chonkie.ai/oss/chunkers/overview\\ntextractor = Textractor(backend=\"docling\", sections=True)\\nchunks = []\\nfor f in files:\\n for chunk in textractor(f):\\n chunks.append((f, chunk))\\n\\n# Step 3: Build an embeddings database\\n#\\n# The `path` parameter sets the vector embeddings model. Supports Hugging Face models, llama.cpp, Ollama, vLLM and more.\\n# Documentation: https://neuml.github.io/txtai/embeddings/\\nembeddings = Embeddings(content=True, path=\"Qwen/Qwen3-Embedding-0.6B\", maxlength=2048)\\nembeddings.index(chunks)\\n\\n# Step 4: Create RAG pipeline\\n#\\n# Combines an embeddings database and an LLM.\\n# Supports Hugging Face models, llama.cpp, Ollama, vLLM and more\\n# Documentation: https://neuml.github.io/txtai/pipeline/text/rag\\n\\n# User prompt template\\ntemplate = \"\"\"\\n Answer the following question using the provided context.\\n\\n Question:\\n {question}\\n\\n Context:\\n {context}\\n\"\"\"\\n\\nrag = RAG(\\n embeddings,\\n \"Qwen/Qwen3-0.6B\",\\n system=\"You are a friendly assistant\",\\n template=template,\\n output=\"flatten\",\\n)\\n\\nquestion = \"Summarize the main advancements made by BERT\"\\nprint(rag(question, maxlength=2048, stripthink=True))\\n```'" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "agent(\"Look for a RAG demo in the ./txtai/examples directory. Print the file content.\", maxlength=10000)" + ] + }, + { + "cell_type": "markdown", + "id": "6d732da4", + "metadata": {}, + "source": [ + "As we can see, the Agent searched the codebase and found the best RAG example." + ] + }, + { + "cell_type": "markdown", + "id": "59f2827e", + "metadata": {}, + "source": [ + "# Building code with AI\n", + "\n", + "Ok, now let's do some AI-driven software development. We're going to build a Python program that generates the song \"Twinkle Twinkle Little Star\" and plays it using TxtAI's audio stream pipeline." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "02873bef", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
╭──────────────────────────────────────────────────── New run ────────────────────────────────────────────────────╮\n",
+       "                                                                                                                 \n",
+       " Play twinkle twinkle little star song using simple notes translated to frequencies and the txtai audio stream   \n",
+       " pipeline. The txtai code is available in txtai/src                                                              \n",
+       "                                                                                                                 \n",
+       "╰─ PipelineModel - unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF/Qwen3-Coder-30B-A3B-Instruct-Q3_K_M.gguf ────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m╭─\u001b[0m\u001b[38;2;212;183;2m───────────────────────────────────────────────────\u001b[0m\u001b[38;2;212;183;2m \u001b[0m\u001b[1;38;2;212;183;2mNew run\u001b[0m\u001b[38;2;212;183;2m \u001b[0m\u001b[38;2;212;183;2m───────────────────────────────────────────────────\u001b[0m\u001b[38;2;212;183;2m─╮\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mPlay twinkle twinkle little star song using simple notes translated to frequencies and the txtai audio stream \u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mpipeline. The txtai code is available in txtai/src\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m╰─\u001b[0m\u001b[38;2;212;183;2m PipelineModel - unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF/Qwen3-Coder-30B-A3B-Instruct-Q3_K_M.gguf \u001b[0m\u001b[38;2;212;183;2m───────────\u001b[0m\u001b[38;2;212;183;2m─╯\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 1 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 1\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'bash' with arguments: {'command': ['ls', '-la', 'txtai/']}                                       │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'bash' with arguments: {'command': ['ls', '-la', 'txtai/']} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: total 440\n",
+       "drwxr-xr-x. 9 user user    460 Mar 16 08:20 .\n",
+       "drwxr-xr-x. 4 user user    120 Mar 16 08:35 ..\n",
+       "-rw-r--r--. 1 user user 301691 Mar 16 08:20 apps.jpg\n",
+       "-rw-r--r--. 1 user user    417 Mar 16 08:20 CITATION.cff\n",
+       "-rw-r--r--. 1 user user    170 Mar 16 08:20 .coveragerc\n",
+       "-rw-r--r--. 1 user user  41536 Mar 16 08:20 demo.gif\n",
+       "drwxr-xr-x. 7 user user    140 Mar 16 08:20 docker\n",
+       "drwxr-xr-x. 9 user user    400 Mar 16 08:20 docs\n",
+       "drwxr-xr-x. 2 user user   1920 Mar 16 08:20 examples\n",
+       "drwxr-xr-x. 7 user user    240 Mar 16 08:20 .git\n",
+       "drwxr-xr-x. 3 user user     60 Mar 16 08:20 .github\n",
+       "-rw-r--r--. 1 user user     99 Mar 16 08:20 .gitignore\n",
+       "-rw-r--r--. 1 user user  10754 Mar 16 08:20 LICENSE\n",
+       "-rw-r--r--. 1 user user  16973 Mar 16 08:20 logo.png\n",
+       "-rw-r--r--. 1 user user   2416 Mar 16 08:20 Makefile\n",
+       "-rw-r--r--. 1 user user   5011 Mar 16 08:20 mkdocs.yml\n",
+       "-rw-r--r--. 1 user user    312 Mar 16 08:20 .pre-commit-config.yaml\n",
+       "-rw-r--r--. 1 user user    354 Mar 16 08:20 .pylintrc\n",
+       "-rw-r--r--. 1 user user     31 Mar 16 08:20 pyproject.toml\n",
+       "-rw-r--r--. 1 user user  22255 Mar 16 08:20 README.md\n",
+       "-rw-r--r--. 1 user user   4760 Mar 16 08:20 setup.py\n",
+       "drwxr-xr-x. 3 user user     60 Mar 16 08:20 src\n",
+       "drwxr-xr-x. 3 user user     60 Mar 16 08:20 test\n",
+       "
\n" + ], + "text/plain": [ + "Observations: total 440\n", + "drwxr-xr-x. 9 user user 460 Mar 16 08:20 .\n", + "drwxr-xr-x. 4 user user 120 Mar 16 08:35 ..\n", + "-rw-r--r--. 1 user user 301691 Mar 16 08:20 apps.jpg\n", + "-rw-r--r--. 1 user user 417 Mar 16 08:20 CITATION.cff\n", + "-rw-r--r--. 1 user user 170 Mar 16 08:20 .coveragerc\n", + "-rw-r--r--. 1 user user 41536 Mar 16 08:20 demo.gif\n", + "drwxr-xr-x. 7 user user 140 Mar 16 08:20 docker\n", + "drwxr-xr-x. 9 user user 400 Mar 16 08:20 docs\n", + "drwxr-xr-x. 2 user user 1920 Mar 16 08:20 examples\n", + "drwxr-xr-x. 7 user user 240 Mar 16 08:20 .git\n", + "drwxr-xr-x. 3 user user 60 Mar 16 08:20 .github\n", + "-rw-r--r--. 1 user user 99 Mar 16 08:20 .gitignore\n", + "-rw-r--r--. 1 user user 10754 Mar 16 08:20 LICENSE\n", + "-rw-r--r--. 1 user user 16973 Mar 16 08:20 logo.png\n", + "-rw-r--r--. 1 user user 2416 Mar 16 08:20 Makefile\n", + "-rw-r--r--. 1 user user 5011 Mar 16 08:20 mkdocs.yml\n", + "-rw-r--r--. 1 user user 312 Mar 16 08:20 .pre-commit-config.yaml\n", + "-rw-r--r--. 1 user user 354 Mar 16 08:20 .pylintrc\n", + "-rw-r--r--. 1 user user 31 Mar 16 08:20 pyproject.toml\n", + "-rw-r--r--. 1 user user 22255 Mar 16 08:20 README.md\n", + "-rw-r--r--. 1 user user 4760 Mar 16 08:20 setup.py\n", + "drwxr-xr-x. 3 user user 60 Mar 16 08:20 src\n", + "drwxr-xr-x. 3 user user 60 Mar 16 08:20 test\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 1: Duration 1.07 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 1: Duration 1.07 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 2 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 2\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'bash' with arguments: {'command': ['ls', '-la', 'txtai/src/']}                                   │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'bash' with arguments: {'command': ['ls', '-la', 'txtai/src/']} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: total 0\n",
+       "drwxr-xr-x. 3 user user  60 Mar 16 08:20 .\n",
+       "drwxr-xr-x. 9 user user 460 Mar 16 08:20 ..\n",
+       "drwxr-xr-x. 3 user user  60 Mar 16 08:20 python\n",
+       "
\n" + ], + "text/plain": [ + "Observations: total 0\n", + "drwxr-xr-x. 3 user user 60 Mar 16 08:20 .\n", + "drwxr-xr-x. 9 user user 460 Mar 16 08:20 ..\n", + "drwxr-xr-x. 3 user user 60 Mar 16 08:20 python\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 2: Duration 1.05 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 2: Duration 1.05 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 3 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 3\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'bash' with arguments: {'command': ['ls', '-la', 'txtai/src/python/']}                            │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'bash' with arguments: {'command': ['ls', '-la', 'txtai/src/python/']} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: total 0\n",
+       "drwxr-xr-x.  3 user user  60 Mar 16 08:20 .\n",
+       "drwxr-xr-x.  3 user user  60 Mar 16 08:20 ..\n",
+       "drwxr-xr-x. 20 user user 440 Mar 16 08:20 txtai\n",
+       "
\n" + ], + "text/plain": [ + "Observations: total 0\n", + "drwxr-xr-x. 3 user user 60 Mar 16 08:20 .\n", + "drwxr-xr-x. 3 user user 60 Mar 16 08:20 ..\n", + "drwxr-xr-x. 20 user user 440 Mar 16 08:20 txtai\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 3: Duration 0.64 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 3: Duration 0.64 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 4 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 4\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'bash' with arguments: {'command': ['ls', '-la', 'txtai/src/python/txtai/']}                      │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'bash' with arguments: {'command': ['ls', '-la', 'txtai/src/python/txtai/']} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: total 8\n",
+       "drwxr-xr-x. 20 user user 440 Mar 16 08:20 .\n",
+       "drwxr-xr-x.  3 user user  60 Mar 16 08:20 ..\n",
+       "drwxr-xr-x.  3 user user 160 Mar 16 08:20 agent\n",
+       "drwxr-xr-x.  4 user user 120 Mar 16 08:20 ann\n",
+       "drwxr-xr-x.  4 user user 240 Mar 16 08:20 api\n",
+       "drwxr-xr-x.  2 user user  80 Mar 16 08:20 app\n",
+       "drwxr-xr-x.  2 user user 160 Mar 16 08:20 archive\n",
+       "drwxr-xr-x.  2 user user 140 Mar 16 08:20 cloud\n",
+       "drwxr-xr-x.  2 user user 100 Mar 16 08:20 console\n",
+       "drwxr-xr-x.  2 user user 180 Mar 16 08:20 data\n",
+       "drwxr-xr-x.  5 user user 260 Mar 16 08:20 database\n",
+       "drwxr-xr-x.  4 user user 120 Mar 16 08:20 embeddings\n",
+       "drwxr-xr-x.  2 user user 180 Mar 16 08:20 graph\n",
+       "-rw-r--r--.  1 user user 364 Mar 16 08:20 __init__.py\n",
+       "drwxr-xr-x.  3 user user 160 Mar 16 08:20 models\n",
+       "drwxr-xr-x.  8 user user 300 Mar 16 08:20 pipeline\n",
+       "drwxr-xr-x.  2 user user 240 Mar 16 08:20 scoring\n",
+       "drwxr-xr-x.  2 user user 180 Mar 16 08:20 serialize\n",
+       "drwxr-xr-x.  2 user user 120 Mar 16 08:20 util\n",
+       "drwxr-xr-x.  4 user user 140 Mar 16 08:20 vectors\n",
+       "-rw-r--r--.  1 user user  69 Mar 16 08:20 version.py\n",
+       "drwxr-xr-x.  3 user user 140 Mar 16 08:20 workflow\n",
+       "
\n" + ], + "text/plain": [ + "Observations: total 8\n", + "drwxr-xr-x. 20 user user 440 Mar 16 08:20 .\n", + "drwxr-xr-x. 3 user user 60 Mar 16 08:20 ..\n", + "drwxr-xr-x. 3 user user 160 Mar 16 08:20 agent\n", + "drwxr-xr-x. 4 user user 120 Mar 16 08:20 ann\n", + "drwxr-xr-x. 4 user user 240 Mar 16 08:20 api\n", + "drwxr-xr-x. 2 user user 80 Mar 16 08:20 app\n", + "drwxr-xr-x. 2 user user 160 Mar 16 08:20 archive\n", + "drwxr-xr-x. 2 user user 140 Mar 16 08:20 cloud\n", + "drwxr-xr-x. 2 user user 100 Mar 16 08:20 console\n", + "drwxr-xr-x. 2 user user 180 Mar 16 08:20 data\n", + "drwxr-xr-x. 5 user user 260 Mar 16 08:20 database\n", + "drwxr-xr-x. 4 user user 120 Mar 16 08:20 embeddings\n", + "drwxr-xr-x. 2 user user 180 Mar 16 08:20 graph\n", + "-rw-r--r--. 1 user user 364 Mar 16 08:20 __init__.py\n", + "drwxr-xr-x. 3 user user 160 Mar 16 08:20 models\n", + "drwxr-xr-x. 8 user user 300 Mar 16 08:20 pipeline\n", + "drwxr-xr-x. 2 user user 240 Mar 16 08:20 scoring\n", + "drwxr-xr-x. 2 user user 180 Mar 16 08:20 serialize\n", + "drwxr-xr-x. 2 user user 120 Mar 16 08:20 util\n", + "drwxr-xr-x. 4 user user 140 Mar 16 08:20 vectors\n", + "-rw-r--r--. 1 user user 69 Mar 16 08:20 version.py\n", + "drwxr-xr-x. 3 user user 140 Mar 16 08:20 workflow\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 4: Duration 0.70 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 4: Duration 0.70 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 5 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 5\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'bash' with arguments: {'command': ['ls', '-la', 'txtai/src/python/txtai/pipeline/']}             │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'bash' with arguments: {'command': ['ls', '-la', 'txtai/src/python/txtai/pipeline/']} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: total 28\n",
+       "drwxr-xr-x.  8 user user  300 Mar 16 08:20 .\n",
+       "drwxr-xr-x. 20 user user  440 Mar 16 08:20 ..\n",
+       "drwxr-xr-x.  2 user user  200 Mar 16 08:20 audio\n",
+       "-rw-r--r--.  1 user user  536 Mar 16 08:20 base.py\n",
+       "drwxr-xr-x.  2 user user  180 Mar 16 08:20 data\n",
+       "-rw-r--r--.  1 user user 1782 Mar 16 08:20 factory.py\n",
+       "-rw-r--r--.  1 user user 3684 Mar 16 08:20 hfmodel.py\n",
+       "-rw-r--r--.  1 user user 3518 Mar 16 08:20 hfpipeline.py\n",
+       "drwxr-xr-x.  2 user user  120 Mar 16 08:20 image\n",
+       "-rw-r--r--.  1 user user  360 Mar 16 08:20 __init__.py\n",
+       "drwxr-xr-x.  2 user user  220 Mar 16 08:20 llm\n",
+       "-rw-r--r--.  1 user user  189 Mar 16 08:20 nop.py\n",
+       "-rw-r--r--.  1 user user 1058 Mar 16 08:20 tensors.py\n",
+       "drwxr-xr-x.  2 user user  240 Mar 16 08:20 text\n",
+       "drwxr-xr-x.  2 user user  120 Mar 16 08:20 train\n",
+       "
\n" + ], + "text/plain": [ + "Observations: total 28\n", + "drwxr-xr-x. 8 user user 300 Mar 16 08:20 .\n", + "drwxr-xr-x. 20 user user 440 Mar 16 08:20 ..\n", + "drwxr-xr-x. 2 user user 200 Mar 16 08:20 audio\n", + "-rw-r--r--. 1 user user 536 Mar 16 08:20 base.py\n", + "drwxr-xr-x. 2 user user 180 Mar 16 08:20 data\n", + "-rw-r--r--. 1 user user 1782 Mar 16 08:20 factory.py\n", + "-rw-r--r--. 1 user user 3684 Mar 16 08:20 hfmodel.py\n", + "-rw-r--r--. 1 user user 3518 Mar 16 08:20 hfpipeline.py\n", + "drwxr-xr-x. 2 user user 120 Mar 16 08:20 image\n", + "-rw-r--r--. 1 user user 360 Mar 16 08:20 __init__.py\n", + "drwxr-xr-x. 2 user user 220 Mar 16 08:20 llm\n", + "-rw-r--r--. 1 user user 189 Mar 16 08:20 nop.py\n", + "-rw-r--r--. 1 user user 1058 Mar 16 08:20 tensors.py\n", + "drwxr-xr-x. 2 user user 240 Mar 16 08:20 text\n", + "drwxr-xr-x. 2 user user 120 Mar 16 08:20 train\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 5: Duration 1.24 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 5: Duration 1.24 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 6 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 6\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'bash' with arguments: {'command': ['ls', '-la', 'txtai/src/python/txtai/pipeline/audio/']}       │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'bash' with arguments: {'command': ['ls', '-la', 'txtai/src/python/txtai/pipeline/audio/']} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: total 60\n",
+       "drwxr-xr-x. 2 user user   200 Mar 16 08:20 .\n",
+       "drwxr-xr-x. 8 user user   300 Mar 16 08:20 ..\n",
+       "-rw-r--r--. 1 user user  1722 Mar 16 08:20 audiomixer.py\n",
+       "-rw-r--r--. 1 user user  2480 Mar 16 08:20 audiostream.py\n",
+       "-rw-r--r--. 1 user user   274 Mar 16 08:20 __init__.py\n",
+       "-rw-r--r--. 1 user user  7846 Mar 16 08:20 microphone.py\n",
+       "-rw-r--r--. 1 user user  5161 Mar 16 08:20 signal.py\n",
+       "-rw-r--r--. 1 user user  1793 Mar 16 08:20 texttoaudio.py\n",
+       "-rw-r--r--. 1 user user 18477 Mar 16 08:20 texttospeech.py\n",
+       "-rw-r--r--. 1 user user  6568 Mar 16 08:20 transcription.py\n",
+       "
\n" + ], + "text/plain": [ + "Observations: total 60\n", + "drwxr-xr-x. 2 user user 200 Mar 16 08:20 .\n", + "drwxr-xr-x. 8 user user 300 Mar 16 08:20 ..\n", + "-rw-r--r--. 1 user user 1722 Mar 16 08:20 audiomixer.py\n", + "-rw-r--r--. 1 user user 2480 Mar 16 08:20 audiostream.py\n", + "-rw-r--r--. 1 user user 274 Mar 16 08:20 __init__.py\n", + "-rw-r--r--. 1 user user 7846 Mar 16 08:20 microphone.py\n", + "-rw-r--r--. 1 user user 5161 Mar 16 08:20 signal.py\n", + "-rw-r--r--. 1 user user 1793 Mar 16 08:20 texttoaudio.py\n", + "-rw-r--r--. 1 user user 18477 Mar 16 08:20 texttospeech.py\n", + "-rw-r--r--. 1 user user 6568 Mar 16 08:20 transcription.py\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 6: Duration 1.10 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 6: Duration 1.10 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 7 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 7\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'read' with arguments: {'path': 'txtai/src/python/txtai/pipeline/audio/audiostream.py'}           │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'read' with arguments: {'path': 'txtai/src/python/txtai/pipeline/audio/audiostream.py'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: \"\"\"\n",
+       "AudioStream module\n",
+       "\"\"\"\n",
+       "\n",
+       "from queue import Queue\n",
+       "from threading import Thread\n",
+       "\n",
+       "# Conditional import\n",
+       "try:\n",
+       " import sounddevice as sd\n",
+       "\n",
+       " from .signal import Signal, SCIPY\n",
+       "\n",
+       " AUDIOSTREAM = SCIPY\n",
+       "except (ImportError, OSError):\n",
+       " AUDIOSTREAM = False\n",
+       "\n",
+       "from ..base import Pipeline\n",
+       "\n",
+       "class AudioStream(Pipeline):\n",
+       " \"\"\"\n",
+       " Threaded pipeline that streams audio segments to an output audio device. This pipeline is designed\n",
+       " to run on local machines given that it requires access to write to an output device.\n",
+       " \"\"\"\n",
+       "\n",
+       " # End of stream message\n",
+       " COMPLETE = (1, None)\n",
+       "\n",
+       " def __init__(self, rate=None):\n",
+       " \"\"\"\n",
+       " Creates an AudioStream pipeline.\n",
+       "\n",
+       " Args:\n",
+       " rate: optional target sample rate, otherwise uses input target rate with each audio segment\n",
+       " \"\"\"\n",
+       "\n",
+       " if not AUDIOSTREAM:\n",
+       " raise ImportError(\n",
+       " (\n",
+       " 'AudioStream pipeline is not available - install \"pipeline\" extra to enable. '\n",
+       " \"Also check that the portaudio system library is available.\"\n",
+       " )\n",
+       " )\n",
+       "\n",
+       " # Target sample rate\n",
+       " self.rate = rate\n",
+       "\n",
+       " self.queue = Queue()\n",
+       " self.thread = Thread(target=self.play)\n",
+       " self.thread.start()\n",
+       "\n",
+       " def __call__(self, segment):\n",
+       " \"\"\"\n",
+       " Queues audio segments for the audio player.\n",
+       "\n",
+       " Args:\n",
+       " segment: (audio, sample rate)|list\n",
+       "\n",
+       " Returns:\n",
+       " segment\n",
+       " \"\"\"\n",
+       "\n",
+       " # Convert single element to list\n",
+       " segments = |segment] if isinstance(segment, tuple) else segment\n",
+       "\n",
+       " for x in segments:\n",
+       " self.queue.put(x)\n",
+       "\n",
+       " # Return single element if single element passed in\n",
+       " return segments|0] if isinstance(segment, tuple) else segments\n",
+       "\n",
+       " def wait(self):\n",
+       " \"\"\"\n",
+       " Waits for all input audio segments to be played.\n",
+       " \"\"\"\n",
+       "\n",
+       " self.thread.join()\n",
+       "\n",
+       " def play(self):\n",
+       " \"\"\"\n",
+       " Reads audio segments from queue. This method runs in a separate non-blocking thread.\n",
+       " \"\"\"\n",
+       "\n",
+       " audio, rate = self.queue.get()\n",
+       " while not isinstance(audio, int) or (audio, rate) != AudioStream.COMPLETE:\n",
+       " # Resample to target sample rate, if necessary\n",
+       " audio, rate = (Signal.resample(audio, rate, self.rate), self.rate) if self.rate else (audio, rate)\n",
+       "\n",
+       " # Play audio segment\n",
+       " sd.play(audio, rate, blocking=True)\n",
+       "\n",
+       " # Get next segment\n",
+       " audio, rate = self.queue.get()\n",
+       "
\n" + ], + "text/plain": [ + "Observations: \"\"\"\n", + "AudioStream module\n", + "\"\"\"\n", + "\n", + "from queue import Queue\n", + "from threading import Thread\n", + "\n", + "# Conditional import\n", + "try:\n", + " import sounddevice as sd\n", + "\n", + " from .signal import Signal, SCIPY\n", + "\n", + " AUDIOSTREAM = SCIPY\n", + "except (ImportError, OSError):\n", + " AUDIOSTREAM = False\n", + "\n", + "from ..base import Pipeline\n", + "\n", + "class AudioStream(Pipeline):\n", + " \"\"\"\n", + " Threaded pipeline that streams audio segments to an output audio device. This pipeline is designed\n", + " to run on local machines given that it requires access to write to an output device.\n", + " \"\"\"\n", + "\n", + " # End of stream message\n", + " COMPLETE = (1, None)\n", + "\n", + " def __init__(self, rate=None):\n", + " \"\"\"\n", + " Creates an AudioStream pipeline.\n", + "\n", + " Args:\n", + " rate: optional target sample rate, otherwise uses input target rate with each audio segment\n", + " \"\"\"\n", + "\n", + " if not AUDIOSTREAM:\n", + " raise ImportError(\n", + " (\n", + " 'AudioStream pipeline is not available - install \"pipeline\" extra to enable. '\n", + " \"Also check that the portaudio system library is available.\"\n", + " )\n", + " )\n", + "\n", + " # Target sample rate\n", + " self.rate = rate\n", + "\n", + " self.queue = Queue()\n", + " self.thread = Thread(target=self.play)\n", + " self.thread.start()\n", + "\n", + " def __call__(self, segment):\n", + " \"\"\"\n", + " Queues audio segments for the audio player.\n", + "\n", + " Args:\n", + " segment: (audio, sample rate)|list\n", + "\n", + " Returns:\n", + " segment\n", + " \"\"\"\n", + "\n", + " # Convert single element to list\n", + " segments = |segment] if isinstance(segment, tuple) else segment\n", + "\n", + " for x in segments:\n", + " self.queue.put(x)\n", + "\n", + " # Return single element if single element passed in\n", + " return segments|0] if isinstance(segment, tuple) else segments\n", + "\n", + " def wait(self):\n", + " \"\"\"\n", + " Waits for all input audio segments to be played.\n", + " \"\"\"\n", + "\n", + " self.thread.join()\n", + "\n", + " def play(self):\n", + " \"\"\"\n", + " Reads audio segments from queue. This method runs in a separate non-blocking thread.\n", + " \"\"\"\n", + "\n", + " audio, rate = self.queue.get()\n", + " while not isinstance(audio, int) or (audio, rate) != AudioStream.COMPLETE:\n", + " # Resample to target sample rate, if necessary\n", + " audio, rate = (Signal.resample(audio, rate, self.rate), self.rate) if self.rate else (audio, rate)\n", + "\n", + " # Play audio segment\n", + " sd.play(audio, rate, blocking=True)\n", + "\n", + " # Get next segment\n", + " audio, rate = self.queue.get()\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 7: Duration 1.20 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 7: Duration 1.20 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 8 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 8\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'read' with arguments: {'path': 'txtai/src/python/txtai/pipeline/audio/signal.py'}                │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'read' with arguments: {'path': 'txtai/src/python/txtai/pipeline/audio/signal.py'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: \"\"\"\n",
+       "Signal module\n",
+       "\"\"\"\n",
+       "\n",
+       "import numpy as np\n",
+       "\n",
+       "# Conditional import\n",
+       "try:\n",
+       " from scipy import signal\n",
+       " from scipy.fft import rfft, rfftfreq\n",
+       "\n",
+       " SCIPY = True\n",
+       "except ImportError:\n",
+       " SCIPY = False\n",
+       "\n",
+       "class Signal:\n",
+       " \"\"\"\n",
+       " Utility methods for audio signal processing.\n",
+       " \"\"\"\n",
+       "\n",
+       " @staticmethod\n",
+       " def mono(audio):\n",
+       " \"\"\"\n",
+       " Convert stereo to mono audio.\n",
+       "\n",
+       " Args:\n",
+       " audio: audio data\n",
+       "\n",
+       " Returns:\n",
+       " audio data with a single channel\n",
+       " \"\"\"\n",
+       "\n",
+       " return audio.mean(axis=1) if len(audio.shape) > 1 else audio\n",
+       "\n",
+       " @staticmethod\n",
+       " def resample(audio, rate, target):\n",
+       " \"\"\"\n",
+       " Resample audio if the sample rate doesn't match the target sample rate.\n",
+       "\n",
+       " Args:\n",
+       " audio: audio data\n",
+       " rate: current sample rate\n",
+       " target: target sample rate\n",
+       "\n",
+       " Returns:\n",
+       " audio resampled if necessary or original audio\n",
+       " \"\"\"\n",
+       "\n",
+       " if rate != target:\n",
+       " # Transpose audio\n",
+       " audio = audio.T\n",
+       "\n",
+       " # Resample audio and tranpose back\n",
+       " samples = round(len(audio) * float(target) / rate)\n",
+       " audio = signal.resample(audio, samples).T\n",
+       "\n",
+       " return audio\n",
+       "\n",
+       " @staticmethod\n",
+       " def float32(audio):\n",
+       " \"\"\"\n",
+       " Converts an input NumPy array with 16-bit ints to 32-bit floats.\n",
+       "\n",
+       " Args:\n",
+       " audio: input audio array as 16-bit ints\n",
+       "\n",
+       " Returns:\n",
+       " audio array as 32-bit floats\n",
+       " \"\"\"\n",
+       "\n",
+       " i = np.iinfo(audio.dtype)\n",
+       " abs_max = 2 ** (i.bits - 1)\n",
+       " offset = i.min + abs_max\n",
+       " return (audio.astype(np.float32) - offset) / abs_max\n",
+       "\n",
+       " @staticmethod\n",
+       " def int16(audio):\n",
+       " \"\"\"\n",
+       " Converts an input NumPy array with 32-bit floats to 16-bit ints.\n",
+       "\n",
+       " Args:\n",
+       " audio: input audio array as 32-bit floats\n",
+       "\n",
+       " Returns:\n",
+       " audio array as 16-bit ints\n",
+       " \"\"\"\n",
+       "\n",
+       " i = np.iinfo(np.int16)\n",
+       " absmax = 2 ** (i.bits - 1)\n",
+       " offset = i.min + absmax\n",
+       " return (audio * absmax + offset).clip(i.min, i.max).astype(np.int16)\n",
+       "\n",
+       " @staticmethod\n",
+       " def mix(audio1, audio2, scale1=1, scale2=1):\n",
+       " \"\"\"\n",
+       " Mixes audio1 and audio 2 into a single output audio segment.\n",
+       "\n",
+       " Args:\n",
+       " audio1: audio segment 1\n",
+       " audio2: audio segment 2\n",
+       " scale1: scale factor for audio segment 1\n",
+       " scale2: scale factor for audio segment 2\n",
+       " \"\"\"\n",
+       "\n",
+       " # Reshape audio, as necessary\n",
+       " audio1 = audio1.reshape(1, -1) if len(audio1.shape) <= 1 else audio1\n",
+       " audio2 = audio2.reshape(1, -1) if len(audio2.shape) <= 1 else audio2\n",
+       "\n",
+       " # Scale audio\n",
+       " audio1 = audio1 * scale1\n",
+       " audio2 = audio2 * scale2\n",
+       "\n",
+       " # Make audio files the same length\n",
+       " large, small = (audio1, audio2) if audio1.shape|1] > audio2.shape|1] else (audio2, audio1)\n",
+       " small = np.tile(small, (large.shape|1] // small.shape|1]) + 1).take(axis=1, indices=range(0, large.shape|1]))\n",
+       "\n",
+       " # Mix audio together\n",
+       " return small + large\n",
+       "\n",
+       " @staticmethod\n",
+       " def energy(audio, rate):\n",
+       " \"\"\"\n",
+       " Calculates the signal energy for the input audio. Energy is defined as:\n",
+       "\n",
+       " Energy = 2 * Signal Amplitude\n",
+       "\n",
+       " Args:\n",
+       " audio: audio data\n",
+       " rate: sample rate\n",
+       "\n",
+       " Returns:\n",
+       " {frequency: energy at that frequency}\n",
+       " \"\"\"\n",
+       "\n",
+       " # Calculate signal frequency\n",
+       " frequency = rfftfreq(len(audio), 1.0 / rate)\n",
+       " frequency = frequency|1:]\n",
+       "\n",
+       " # Calculate signal energy using amplitude\n",
+       " energy = np.abs(rfft(audio))\n",
+       " energy = energy|1:]\n",
+       " energy = energy**2\n",
+       "\n",
+       " # Get energy for each frequency\n",
+       " energyfreq = {}\n",
+       " for x, freq in enumerate(frequency):\n",
+       " if abs(freq) not in energyfreq:\n",
+       " energyfreq|abs(freq)] = energy|x] * 2\n",
+       "\n",
+       " return energyfreq\n",
+       "\n",
+       " @staticmethod\n",
+       " def trim(audio, rate, threshold=1, leading=True, trailing=True):\n",
+       " \"\"\"\n",
+       " Removes leading and trailing silence from audio data.\n",
+       "\n",
+       " Args:\n",
+       " audio: audio data\n",
+       " rate: sample rate\n",
+       " threshold: energy below this level will be considered silence, defaults to 1.0\n",
+       " leading: trim leading silence, defaults to True\n",
+       " trailing: trim trailing silence, defauls to True\n",
+       "\n",
+       " Returns:\n",
+       " audio with silence removed\n",
+       " \"\"\"\n",
+       "\n",
+       " # Process in 20ms chunks\n",
+       " n, offset = int(rate * (20 / 1000.0) * 2), 0\n",
+       "\n",
+       " chunks = |]\n",
+       " while offset + n <= len(audio):\n",
+       " # Calculate energy for chunk and detection result\n",
+       " chunk = audio|offset : offset + n]\n",
+       " energyfreq = Signal.energy(chunk, rate)\n",
+       " chunks.append((chunk, sum(energyfreq.values()) >= threshold))\n",
+       "\n",
+       " offset += n\n",
+       "\n",
+       " # Find first and last active chunks\n",
+       " start = next((i for i, (_, active) in enumerate(chunks) if active), 0) if leading else 0\n",
+       " end = (len(chunks) - next((i for i, (_, active) in enumerate(chunks|::-1]) if active), 0)) if trailing else \n",
+       "len(chunks)\n",
+       "\n",
+       " # Concatenate active audio\n",
+       " return np.concatenate(|chunk for chunk, _ in chunks|start:end]])\n",
+       "
\n" + ], + "text/plain": [ + "Observations: \"\"\"\n", + "Signal module\n", + "\"\"\"\n", + "\n", + "import numpy as np\n", + "\n", + "# Conditional import\n", + "try:\n", + " from scipy import signal\n", + " from scipy.fft import rfft, rfftfreq\n", + "\n", + " SCIPY = True\n", + "except ImportError:\n", + " SCIPY = False\n", + "\n", + "class Signal:\n", + " \"\"\"\n", + " Utility methods for audio signal processing.\n", + " \"\"\"\n", + "\n", + " @staticmethod\n", + " def mono(audio):\n", + " \"\"\"\n", + " Convert stereo to mono audio.\n", + "\n", + " Args:\n", + " audio: audio data\n", + "\n", + " Returns:\n", + " audio data with a single channel\n", + " \"\"\"\n", + "\n", + " return audio.mean(axis=1) if len(audio.shape) > 1 else audio\n", + "\n", + " @staticmethod\n", + " def resample(audio, rate, target):\n", + " \"\"\"\n", + " Resample audio if the sample rate doesn't match the target sample rate.\n", + "\n", + " Args:\n", + " audio: audio data\n", + " rate: current sample rate\n", + " target: target sample rate\n", + "\n", + " Returns:\n", + " audio resampled if necessary or original audio\n", + " \"\"\"\n", + "\n", + " if rate != target:\n", + " # Transpose audio\n", + " audio = audio.T\n", + "\n", + " # Resample audio and tranpose back\n", + " samples = round(len(audio) * float(target) / rate)\n", + " audio = signal.resample(audio, samples).T\n", + "\n", + " return audio\n", + "\n", + " @staticmethod\n", + " def float32(audio):\n", + " \"\"\"\n", + " Converts an input NumPy array with 16-bit ints to 32-bit floats.\n", + "\n", + " Args:\n", + " audio: input audio array as 16-bit ints\n", + "\n", + " Returns:\n", + " audio array as 32-bit floats\n", + " \"\"\"\n", + "\n", + " i = np.iinfo(audio.dtype)\n", + " abs_max = 2 ** (i.bits - 1)\n", + " offset = i.min + abs_max\n", + " return (audio.astype(np.float32) - offset) / abs_max\n", + "\n", + " @staticmethod\n", + " def int16(audio):\n", + " \"\"\"\n", + " Converts an input NumPy array with 32-bit floats to 16-bit ints.\n", + "\n", + " Args:\n", + " audio: input audio array as 32-bit floats\n", + "\n", + " Returns:\n", + " audio array as 16-bit ints\n", + " \"\"\"\n", + "\n", + " i = np.iinfo(np.int16)\n", + " absmax = 2 ** (i.bits - 1)\n", + " offset = i.min + absmax\n", + " return (audio * absmax + offset).clip(i.min, i.max).astype(np.int16)\n", + "\n", + " @staticmethod\n", + " def mix(audio1, audio2, scale1=1, scale2=1):\n", + " \"\"\"\n", + " Mixes audio1 and audio 2 into a single output audio segment.\n", + "\n", + " Args:\n", + " audio1: audio segment 1\n", + " audio2: audio segment 2\n", + " scale1: scale factor for audio segment 1\n", + " scale2: scale factor for audio segment 2\n", + " \"\"\"\n", + "\n", + " # Reshape audio, as necessary\n", + " audio1 = audio1.reshape(1, -1) if len(audio1.shape) <= 1 else audio1\n", + " audio2 = audio2.reshape(1, -1) if len(audio2.shape) <= 1 else audio2\n", + "\n", + " # Scale audio\n", + " audio1 = audio1 * scale1\n", + " audio2 = audio2 * scale2\n", + "\n", + " # Make audio files the same length\n", + " large, small = (audio1, audio2) if audio1.shape|1] > audio2.shape|1] else (audio2, audio1)\n", + " small = np.tile(small, (large.shape|1] // small.shape|1]) + 1).take(axis=1, indices=range(0, large.shape|1]))\n", + "\n", + " # Mix audio together\n", + " return small + large\n", + "\n", + " @staticmethod\n", + " def energy(audio, rate):\n", + " \"\"\"\n", + " Calculates the signal energy for the input audio. Energy is defined as:\n", + "\n", + " Energy = 2 * Signal Amplitude\n", + "\n", + " Args:\n", + " audio: audio data\n", + " rate: sample rate\n", + "\n", + " Returns:\n", + " {frequency: energy at that frequency}\n", + " \"\"\"\n", + "\n", + " # Calculate signal frequency\n", + " frequency = rfftfreq(len(audio), 1.0 / rate)\n", + " frequency = frequency|1:]\n", + "\n", + " # Calculate signal energy using amplitude\n", + " energy = np.abs(rfft(audio))\n", + " energy = energy|1:]\n", + " energy = energy**2\n", + "\n", + " # Get energy for each frequency\n", + " energyfreq = {}\n", + " for x, freq in enumerate(frequency):\n", + " if abs(freq) not in energyfreq:\n", + " energyfreq|abs(freq)] = energy|x] * 2\n", + "\n", + " return energyfreq\n", + "\n", + " @staticmethod\n", + " def trim(audio, rate, threshold=1, leading=True, trailing=True):\n", + " \"\"\"\n", + " Removes leading and trailing silence from audio data.\n", + "\n", + " Args:\n", + " audio: audio data\n", + " rate: sample rate\n", + " threshold: energy below this level will be considered silence, defaults to 1.0\n", + " leading: trim leading silence, defaults to True\n", + " trailing: trim trailing silence, defauls to True\n", + "\n", + " Returns:\n", + " audio with silence removed\n", + " \"\"\"\n", + "\n", + " # Process in 20ms chunks\n", + " n, offset = int(rate * (20 / 1000.0) * 2), 0\n", + "\n", + " chunks = |]\n", + " while offset + n <= len(audio):\n", + " # Calculate energy for chunk and detection result\n", + " chunk = audio|offset : offset + n]\n", + " energyfreq = Signal.energy(chunk, rate)\n", + " chunks.append((chunk, sum(energyfreq.values()) >= threshold))\n", + "\n", + " offset += n\n", + "\n", + " # Find first and last active chunks\n", + " start = next((i for i, (_, active) in enumerate(chunks) if active), 0) if leading else 0\n", + " end = (len(chunks) - next((i for i, (_, active) in enumerate(chunks|::-1]) if active), 0)) if trailing else \n", + "len(chunks)\n", + "\n", + " # Concatenate active audio\n", + " return np.concatenate(|chunk for chunk, _ in chunks|start:end]])\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 8: Duration 1.17 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 8: Duration 1.17 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 9 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 9\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'write' with arguments: {'path': 'twinkle_twinkle.py', 'content': '# Twinkle Twinkle Little Star  │\n",
+       "│ with txtai audio stream\\n\\nimport numpy as np\\nfrom txtai.pipeline.audio.audiostream import AudioStream\\n\\n#    │\n",
+       "│ Note frequencies (C4 = 261.63 Hz)\\nNOTE_FREQUENCIES = {\\n    \\'C4\\': 261.63,\\n    \\'D4\\': 293.66,\\n    \\'E4\\':  │\n",
+       "│ 329.63,\\n    \\'F4\\': 349.23,\\n    \\'G4\\': 392.00,\\n    \\'A4\\': 440.00,\\n    \\'B4\\': 493.88,\\n    \\'C5\\':        │\n",
+       "│ 523.25,\\n}\\n\\n# Twinkle Twinkle Little Star melody\\n# Each tuple is (note, duration in seconds)\\nMELODY = [\\n   │\n",
+       "│ (\\'C4\\', 0.5), (\\'C4\\', 0.5), (\\'G4\\', 0.5), (\\'G4\\', 0.5),\\n    (\\'A4\\', 0.5), (\\'A4\\', 0.5), (\\'G4\\', 1.0),\\n │\n",
+       "│ (\\'F4\\', 0.5), (\\'F4\\', 0.5), (\\'E4\\', 0.5), (\\'E4\\', 0.5),\\n    (\\'D4\\', 0.5), (\\'D4\\', 0.5), (\\'C4\\',         │\n",
+       "│ 1.0)\\n]\\n\\ndef generate_sine_wave(frequency, duration, sample_rate=44100):\\n    \"\"\"\\n    Generate a sine wave   │\n",
+       "│ for a given frequency and duration\\n    \"\"\"\\n    t = np.linspace(0, duration, int(sample_rate * duration),      │\n",
+       "│ False)\\n    wave = np.sin(2 * np.pi * frequency * t)\\n    return wave\\n\\n\\ndef play_melody():\\n    \"\"\"\\n        │\n",
+       "│ Play the Twinkle Twinkle Little Star melody\\n    \"\"\"\\n    # Create audio stream\\n    audio_stream =             │\n",
+       "│ AudioStream()\\n    \\n    sample_rate = 44100\\n    \\n    # Generate and play each note\\n    for note, duration   │\n",
+       "│ in MELODY:\\n        frequency = NOTE_FREQUENCIES[note]\\n        wave = generate_sine_wave(frequency, duration,  │\n",
+       "│ sample_rate)\\n        \\n        # Convert to 16-bit integers for audio stream\\n        wave_int16 =             │\n",
+       "│ np.int16(wave * 32767)\\n        \\n        # Play the note\\n        audio_stream((wave_int16, sample_rate))\\n    │\n",
+       "│ \\n        # Add a small pause between notes\\n        # (This is handled by the audio stream blocking)\\n    \\n   │\n",
+       "│ # Wait for all audio to finish playing\\n    audio_stream.wait()\\n    print(\"Melody finished!\")\\n\\nif __name__   │\n",
+       "│ == \"__main__\":\\n    play_melody()'}                                                                             │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'write' with arguments: {'path': 'twinkle_twinkle.py', 'content': '# Twinkle Twinkle Little Star │\n", + "│ with txtai audio stream\\n\\nimport numpy as np\\nfrom txtai.pipeline.audio.audiostream import AudioStream\\n\\n# │\n", + "│ Note frequencies (C4 = 261.63 Hz)\\nNOTE_FREQUENCIES = {\\n \\'C4\\': 261.63,\\n \\'D4\\': 293.66,\\n \\'E4\\': │\n", + "│ 329.63,\\n \\'F4\\': 349.23,\\n \\'G4\\': 392.00,\\n \\'A4\\': 440.00,\\n \\'B4\\': 493.88,\\n \\'C5\\': │\n", + "│ 523.25,\\n}\\n\\n# Twinkle Twinkle Little Star melody\\n# Each tuple is (note, duration in seconds)\\nMELODY = [\\n │\n", + "│ (\\'C4\\', 0.5), (\\'C4\\', 0.5), (\\'G4\\', 0.5), (\\'G4\\', 0.5),\\n (\\'A4\\', 0.5), (\\'A4\\', 0.5), (\\'G4\\', 1.0),\\n │\n", + "│ (\\'F4\\', 0.5), (\\'F4\\', 0.5), (\\'E4\\', 0.5), (\\'E4\\', 0.5),\\n (\\'D4\\', 0.5), (\\'D4\\', 0.5), (\\'C4\\', │\n", + "│ 1.0)\\n]\\n\\ndef generate_sine_wave(frequency, duration, sample_rate=44100):\\n \"\"\"\\n Generate a sine wave │\n", + "│ for a given frequency and duration\\n \"\"\"\\n t = np.linspace(0, duration, int(sample_rate * duration), │\n", + "│ False)\\n wave = np.sin(2 * np.pi * frequency * t)\\n return wave\\n\\n\\ndef play_melody():\\n \"\"\"\\n │\n", + "│ Play the Twinkle Twinkle Little Star melody\\n \"\"\"\\n # Create audio stream\\n audio_stream = │\n", + "│ AudioStream()\\n \\n sample_rate = 44100\\n \\n # Generate and play each note\\n for note, duration │\n", + "│ in MELODY:\\n frequency = NOTE_FREQUENCIES[note]\\n wave = generate_sine_wave(frequency, duration, │\n", + "│ sample_rate)\\n \\n # Convert to 16-bit integers for audio stream\\n wave_int16 = │\n", + "│ np.int16(wave * 32767)\\n \\n # Play the note\\n audio_stream((wave_int16, sample_rate))\\n │\n", + "│ \\n # Add a small pause between notes\\n # (This is handled by the audio stream blocking)\\n \\n │\n", + "│ # Wait for all audio to finish playing\\n audio_stream.wait()\\n print(\"Melody finished!\")\\n\\nif __name__ │\n", + "│ == \"__main__\":\\n play_melody()'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: None\n",
+       "
\n" + ], + "text/plain": [ + "Observations: None\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 9: Duration 11.69 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 9: Duration 11.69 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 10 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 10\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'bash' with arguments: {'command': ['python', 'twinkle_twinkle.py']}                              │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'bash' with arguments: {'command': ['python', 'twinkle_twinkle.py']} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: None\n",
+       "
\n" + ], + "text/plain": [ + "Observations: None\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 10: Duration 1.39 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 10: Duration 1.39 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 11 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 11\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'bash' with arguments: {'command': ['pip', 'list', '|', 'grep', 'sounddevice']}                   │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'bash' with arguments: {'command': ['pip', 'list', '|', 'grep', 'sounddevice']} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: None\n",
+       "
\n" + ], + "text/plain": [ + "Observations: None\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 11: Duration 1.13 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 11: Duration 1.13 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 12 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 12\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'bash' with arguments: {'command': ['pip', 'install', 'sounddevice', 'scipy', 'numpy']}           │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'bash' with arguments: {'command': ['pip', 'install', 'sounddevice', 'scipy', 'numpy']} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: None\n",
+       "
\n" + ], + "text/plain": [ + "Observations: None\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 12: Duration 0.85 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 12: Duration 0.85 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 13 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 13\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'bash' with arguments: {'command': ['python', 'twinkle_twinkle.py']}                              │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'bash' with arguments: {'command': ['python', 'twinkle_twinkle.py']} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: None\n",
+       "
\n" + ], + "text/plain": [ + "Observations: None\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 13: Duration 0.87 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 13: Duration 0.87 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 14 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 14\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'write' with arguments: {'path': 'twinkle_twinkle_robust.py', 'content': '# Twinkle Twinkle       │\n",
+       "│ Little Star with txtai audio stream - Robust version\\n\\nimport numpy as np\\nimport time\\nfrom                   │\n",
+       "│ txtai.pipeline.audio.audiostream import AudioStream\\n\\n# Note frequencies (C4 = 261.63 Hz)\\nNOTE_FREQUENCIES =  │\n",
+       "│ {\\n    \\'C4\\': 261.63,\\n    \\'D4\\': 293.66,\\n    \\'E4\\': 329.63,\\n    \\'F4\\': 349.23,\\n    \\'G4\\': 392.00,\\n    │\n",
+       "│ \\'A4\\': 440.00,\\n    \\'B4\\': 493.88,\\n    \\'C5\\': 523.25,\\n}\\n\\n# Twinkle Twinkle Little Star melody\\n# Each    │\n",
+       "│ tuple is (note, duration in seconds)\\nMELODY = [\\n    (\\'C4\\', 0.5), (\\'C4\\', 0.5), (\\'G4\\', 0.5), (\\'G4\\',     │\n",
+       "│ 0.5),\\n    (\\'A4\\', 0.5), (\\'A4\\', 0.5), (\\'G4\\', 1.0),\\n    (\\'F4\\', 0.5), (\\'F4\\', 0.5), (\\'E4\\', 0.5),       │\n",
+       "│ (\\'E4\\', 0.5),\\n    (\\'D4\\', 0.5), (\\'D4\\', 0.5), (\\'C4\\', 1.0)\\n]\\n\\ndef generate_sine_wave(frequency,         │\n",
+       "│ duration, sample_rate=44100):\\n    \"\"\"\\n    Generate a sine wave for a given frequency and duration\\n    \"\"\"\\n  │\n",
+       "│ t = np.linspace(0, duration, int(sample_rate * duration), False)\\n    wave = np.sin(2 * np.pi * frequency *     │\n",
+       "│ t)\\n    return wave\\n\\n\\ndef play_melody():\\n    \"\"\"\\n    Play the Twinkle Twinkle Little Star melody\\n         │\n",
+       "│ \"\"\"\\n    try:\\n        # Create audio stream\\n        audio_stream = AudioStream()\\n        print(\"Audio stream │\n",
+       "│ created successfully\")\\n        \\n        sample_rate = 44100\\n        \\n        # Generate and play each       │\n",
+       "│ note\\n        for i, (note, duration) in enumerate(MELODY):\\n            print(f\"Playing note {i+1}: {note} for │\n",
+       "│ {duration} seconds\")\\n            frequency = NOTE_FREQUENCIES[note]\\n            wave =                        │\n",
+       "│ generate_sine_wave(frequency, duration, sample_rate)\\n            \\n            # Convert to 16-bit integers    │\n",
+       "│ for audio stream\\n            wave_int16 = np.int16(wave * 32767)\\n            \\n            # Play the note\\n  │\n",
+       "│ audio_stream((wave_int16, sample_rate))\\n            \\n            # Add a small pause between notes\\n          │\n",
+       "│ time.sleep(duration)\\n            \\n        # Wait for all audio to finish playing\\n                            │\n",
+       "│ audio_stream.wait()\\n        print(\"Melody finished!\")\\n        \\n    except Exception as e:\\n                  │\n",
+       "│ print(f\"Error playing melody: {e}\")\\n\\nif __name__ == \"__main__\":\\n    play_melody()'}                          │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'write' with arguments: {'path': 'twinkle_twinkle_robust.py', 'content': '# Twinkle Twinkle │\n", + "│ Little Star with txtai audio stream - Robust version\\n\\nimport numpy as np\\nimport time\\nfrom │\n", + "│ txtai.pipeline.audio.audiostream import AudioStream\\n\\n# Note frequencies (C4 = 261.63 Hz)\\nNOTE_FREQUENCIES = │\n", + "│ {\\n \\'C4\\': 261.63,\\n \\'D4\\': 293.66,\\n \\'E4\\': 329.63,\\n \\'F4\\': 349.23,\\n \\'G4\\': 392.00,\\n │\n", + "│ \\'A4\\': 440.00,\\n \\'B4\\': 493.88,\\n \\'C5\\': 523.25,\\n}\\n\\n# Twinkle Twinkle Little Star melody\\n# Each │\n", + "│ tuple is (note, duration in seconds)\\nMELODY = [\\n (\\'C4\\', 0.5), (\\'C4\\', 0.5), (\\'G4\\', 0.5), (\\'G4\\', │\n", + "│ 0.5),\\n (\\'A4\\', 0.5), (\\'A4\\', 0.5), (\\'G4\\', 1.0),\\n (\\'F4\\', 0.5), (\\'F4\\', 0.5), (\\'E4\\', 0.5), │\n", + "│ (\\'E4\\', 0.5),\\n (\\'D4\\', 0.5), (\\'D4\\', 0.5), (\\'C4\\', 1.0)\\n]\\n\\ndef generate_sine_wave(frequency, │\n", + "│ duration, sample_rate=44100):\\n \"\"\"\\n Generate a sine wave for a given frequency and duration\\n \"\"\"\\n │\n", + "│ t = np.linspace(0, duration, int(sample_rate * duration), False)\\n wave = np.sin(2 * np.pi * frequency * │\n", + "│ t)\\n return wave\\n\\n\\ndef play_melody():\\n \"\"\"\\n Play the Twinkle Twinkle Little Star melody\\n │\n", + "│ \"\"\"\\n try:\\n # Create audio stream\\n audio_stream = AudioStream()\\n print(\"Audio stream │\n", + "│ created successfully\")\\n \\n sample_rate = 44100\\n \\n # Generate and play each │\n", + "│ note\\n for i, (note, duration) in enumerate(MELODY):\\n print(f\"Playing note {i+1}: {note} for │\n", + "│ {duration} seconds\")\\n frequency = NOTE_FREQUENCIES[note]\\n wave = │\n", + "│ generate_sine_wave(frequency, duration, sample_rate)\\n \\n # Convert to 16-bit integers │\n", + "│ for audio stream\\n wave_int16 = np.int16(wave * 32767)\\n \\n # Play the note\\n │\n", + "│ audio_stream((wave_int16, sample_rate))\\n \\n # Add a small pause between notes\\n │\n", + "│ time.sleep(duration)\\n \\n # Wait for all audio to finish playing\\n │\n", + "│ audio_stream.wait()\\n print(\"Melody finished!\")\\n \\n except Exception as e:\\n │\n", + "│ print(f\"Error playing melody: {e}\")\\n\\nif __name__ == \"__main__\":\\n play_melody()'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: None\n",
+       "
\n" + ], + "text/plain": [ + "Observations: None\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 14: Duration 12.32 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 14: Duration 12.32 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 15 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 15\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'bash' with arguments: {'command': ['python', 'twinkle_twinkle_robust.py']}                       │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'bash' with arguments: {'command': ['python', 'twinkle_twinkle_robust.py']} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: None\n",
+       "
\n" + ], + "text/plain": [ + "Observations: None\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 15: Duration 1.47 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 15: Duration 1.47 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 16 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 16\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'bash' with arguments: {'command': ['python', '-c', \"import sounddevice as sd; print('SoundDevice │\n",
+       "│ available'); print(sd.query_devices())\"]}                                                                       │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'bash' with arguments: {'command': ['python', '-c', \"import sounddevice as sd; print('SoundDevice │\n", + "│ available'); print(sd.query_devices())\"]} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: None\n",
+       "
\n" + ], + "text/plain": [ + "Observations: None\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 16: Duration 1.39 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 16: Duration 1.39 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 17 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 17\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'write' with arguments: {'path': 'test_audio.py', 'content': \"import numpy as np\\nimport          │\n",
+       "│ sounddevice as sd\\n\\n# Test if sounddevice works\\nprint('Testing sounddevice...')\\n\\n# Generate a simple sine   │\n",
+       "│ wave\\nsample_rate = 44100\\nfrequency = 440  # A note\\nduration = 1.0  # 1 second\\n\\n# Generate time array\\nt =  │\n",
+       "│ np.linspace(0, duration, int(sample_rate * duration), False)\\n\\n# Generate sine wave\\nwave = np.sin(2 * np.pi * │\n",
+       "│ frequency * t)\\n\\n# Convert to 16-bit integers\\nwave_int16 = np.int16(wave * 32767)\\n\\nprint(f'Generated wave   │\n",
+       "│ with {len(wave_int16)} samples')\\n\\n# Try to play it\\nprint('Playing test tone...')\\ntry:\\n                     │\n",
+       "│ sd.play(wave_int16, sample_rate)\\n    sd.wait()  # Wait until playback is finished\\n    print('Test tone played │\n",
+       "│ successfully')\\nexcept Exception as e:\\n    print(f'Error playing tone: {e}')\"}                                 │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'write' with arguments: {'path': 'test_audio.py', 'content': \"import numpy as np\\nimport │\n", + "│ sounddevice as sd\\n\\n# Test if sounddevice works\\nprint('Testing sounddevice...')\\n\\n# Generate a simple sine │\n", + "│ wave\\nsample_rate = 44100\\nfrequency = 440 # A note\\nduration = 1.0 # 1 second\\n\\n# Generate time array\\nt = │\n", + "│ np.linspace(0, duration, int(sample_rate * duration), False)\\n\\n# Generate sine wave\\nwave = np.sin(2 * np.pi * │\n", + "│ frequency * t)\\n\\n# Convert to 16-bit integers\\nwave_int16 = np.int16(wave * 32767)\\n\\nprint(f'Generated wave │\n", + "│ with {len(wave_int16)} samples')\\n\\n# Try to play it\\nprint('Playing test tone...')\\ntry:\\n │\n", + "│ sd.play(wave_int16, sample_rate)\\n sd.wait() # Wait until playback is finished\\n print('Test tone played │\n", + "│ successfully')\\nexcept Exception as e:\\n print(f'Error playing tone: {e}')\"} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: None\n",
+       "
\n" + ], + "text/plain": [ + "Observations: None\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 17: Duration 4.85 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 17: Duration 4.85 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 18 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 18\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'bash' with arguments: {'command': ['python', 'test_audio.py']}                                   │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'bash' with arguments: {'command': ['python', 'test_audio.py']} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: None\n",
+       "
\n" + ], + "text/plain": [ + "Observations: None\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 18: Duration 1.07 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 18: Duration 1.07 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 19 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 19\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'write' with arguments: {'path': 'twinkle_twinkle_final.py', 'content': '# Twinkle Twinkle Little │\n",
+       "│ Star with txtai audio stream\\n# This demonstrates the complete implementation\\n\\nimport numpy as np\\nfrom       │\n",
+       "│ txtai.pipeline.audio.audiostream import AudioStream\\n\\n# Note frequencies (C4 = 261.63 Hz)\\nNOTE_FREQUENCIES =  │\n",
+       "│ {\\n    \\'C4\\': 261.63,\\n    \\'D4\\': 293.66,\\n    \\'E4\\': 329.63,\\n    \\'F4\\': 349.23,\\n    \\'G4\\': 392.00,\\n    │\n",
+       "│ \\'A4\\': 440.00,\\n    \\'B4\\': 493.88,\\n    \\'C5\\': 523.25,\\n}\\n\\n# Twinkle Twinkle Little Star melody\\n# Each    │\n",
+       "│ tuple is (note, duration in seconds)\\nMELODY = [\\n    (\\'C4\\', 0.5), (\\'C4\\', 0.5), (\\'G4\\', 0.5), (\\'G4\\',     │\n",
+       "│ 0.5),\\n    (\\'A4\\', 0.5), (\\'A4\\', 0.5), (\\'G4\\', 1.0),\\n    (\\'F4\\', 0.5), (\\'F4\\', 0.5), (\\'E4\\', 0.5),       │\n",
+       "│ (\\'E4\\', 0.5),\\n    (\\'D4\\', 0.5), (\\'D4\\', 0.5), (\\'C4\\', 1.0)\\n]\\n\\ndef generate_sine_wave(frequency,         │\n",
+       "│ duration, sample_rate=44100):\\n    \"\"\"\\n    Generate a sine wave for a given frequency and duration\\n    \"\"\"\\n  │\n",
+       "│ t = np.linspace(0, duration, int(sample_rate * duration), False)\\n    wave = np.sin(2 * np.pi * frequency *     │\n",
+       "│ t)\\n    return wave\\n\\n\\ndef play_melody():\\n    \"\"\"\\n    Play the Twinkle Twinkle Little Star melody\\n         │\n",
+       "│ \"\"\"\\n    print(\"Creating audio stream...\")\\n    \\n    # Create audio stream\\n    try:\\n        audio_stream =   │\n",
+       "│ AudioStream()\\n        print(\"Audio stream created successfully\")\\n    except ImportError as e:\\n               │\n",
+       "│ print(f\"Audio stream creation failed: {e}\")\\n        return\\n    \\n    sample_rate = 44100\\n    \\n              │\n",
+       "│ print(\"Generating and playing melody...\")\\n    # Generate and play each note\\n    for i, (note, duration) in    │\n",
+       "│ enumerate(MELODY):\\n        print(f\"Playing note {i+1}: {note} for {duration} seconds\")\\n        frequency =    │\n",
+       "│ NOTE_FREQUENCIES[note]\\n        wave = generate_sine_wave(frequency, duration, sample_rate)\\n        \\n         │\n",
+       "│ # Convert to 16-bit integers for audio stream\\n        wave_int16 = np.int16(wave * 32767)\\n        \\n        # │\n",
+       "│ Play the note\\n        try:\\n            audio_stream((wave_int16, sample_rate))\\n        except Exception as   │\n",
+       "│ e:\\n            print(f\"Error playing note {note}: {e}\")\\n            continue\\n        \\n    # Wait for all    │\n",
+       "│ audio to finish playing\\n    try:\\n        audio_stream.wait()\\n        print(\"Melody finished!\")\\n    except   │\n",
+       "│ Exception as e:\\n        print(f\"Error waiting for audio completion: {e}\")\\n\\nif __name__ == \"__main__\":\\n      │\n",
+       "│ play_melody()'}                                                                                                 │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'write' with arguments: {'path': 'twinkle_twinkle_final.py', 'content': '# Twinkle Twinkle Little │\n", + "│ Star with txtai audio stream\\n# This demonstrates the complete implementation\\n\\nimport numpy as np\\nfrom │\n", + "│ txtai.pipeline.audio.audiostream import AudioStream\\n\\n# Note frequencies (C4 = 261.63 Hz)\\nNOTE_FREQUENCIES = │\n", + "│ {\\n \\'C4\\': 261.63,\\n \\'D4\\': 293.66,\\n \\'E4\\': 329.63,\\n \\'F4\\': 349.23,\\n \\'G4\\': 392.00,\\n │\n", + "│ \\'A4\\': 440.00,\\n \\'B4\\': 493.88,\\n \\'C5\\': 523.25,\\n}\\n\\n# Twinkle Twinkle Little Star melody\\n# Each │\n", + "│ tuple is (note, duration in seconds)\\nMELODY = [\\n (\\'C4\\', 0.5), (\\'C4\\', 0.5), (\\'G4\\', 0.5), (\\'G4\\', │\n", + "│ 0.5),\\n (\\'A4\\', 0.5), (\\'A4\\', 0.5), (\\'G4\\', 1.0),\\n (\\'F4\\', 0.5), (\\'F4\\', 0.5), (\\'E4\\', 0.5), │\n", + "│ (\\'E4\\', 0.5),\\n (\\'D4\\', 0.5), (\\'D4\\', 0.5), (\\'C4\\', 1.0)\\n]\\n\\ndef generate_sine_wave(frequency, │\n", + "│ duration, sample_rate=44100):\\n \"\"\"\\n Generate a sine wave for a given frequency and duration\\n \"\"\"\\n │\n", + "│ t = np.linspace(0, duration, int(sample_rate * duration), False)\\n wave = np.sin(2 * np.pi * frequency * │\n", + "│ t)\\n return wave\\n\\n\\ndef play_melody():\\n \"\"\"\\n Play the Twinkle Twinkle Little Star melody\\n │\n", + "│ \"\"\"\\n print(\"Creating audio stream...\")\\n \\n # Create audio stream\\n try:\\n audio_stream = │\n", + "│ AudioStream()\\n print(\"Audio stream created successfully\")\\n except ImportError as e:\\n │\n", + "│ print(f\"Audio stream creation failed: {e}\")\\n return\\n \\n sample_rate = 44100\\n \\n │\n", + "│ print(\"Generating and playing melody...\")\\n # Generate and play each note\\n for i, (note, duration) in │\n", + "│ enumerate(MELODY):\\n print(f\"Playing note {i+1}: {note} for {duration} seconds\")\\n frequency = │\n", + "│ NOTE_FREQUENCIES[note]\\n wave = generate_sine_wave(frequency, duration, sample_rate)\\n \\n │\n", + "│ # Convert to 16-bit integers for audio stream\\n wave_int16 = np.int16(wave * 32767)\\n \\n # │\n", + "│ Play the note\\n try:\\n audio_stream((wave_int16, sample_rate))\\n except Exception as │\n", + "│ e:\\n print(f\"Error playing note {note}: {e}\")\\n continue\\n \\n # Wait for all │\n", + "│ audio to finish playing\\n try:\\n audio_stream.wait()\\n print(\"Melody finished!\")\\n except │\n", + "│ Exception as e:\\n print(f\"Error waiting for audio completion: {e}\")\\n\\nif __name__ == \"__main__\":\\n │\n", + "│ play_melody()'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: None\n",
+       "
\n" + ], + "text/plain": [ + "Observations: None\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 19: Duration 15.08 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 19: Duration 15.08 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 20 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 20\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'bash' with arguments: {'command': ['python', 'twinkle_twinkle_final.py']}                        │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'bash' with arguments: {'command': ['python', 'twinkle_twinkle_final.py']} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: None\n",
+       "
\n" + ], + "text/plain": [ + "Observations: None\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 20: Duration 1.67 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 20: Duration 1.67 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Reached max steps.\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[1;31mReached max steps.\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 21: Duration 20.18 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 21: Duration 20.18 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "'I\\'ve successfully created a solution that demonstrates how to play \"Twinkle Twinkle Little Star\" using simple notes translated to frequencies and the txtai audio stream pipeline.\\n\\n## Solution Overview\\n\\nThe implementation consists of:\\n\\n1. **Frequency-based note generation**: Converting musical notes to their corresponding frequencies\\n2. **Audio stream pipeline**: Using txtai\\'s `AudioStream` class to handle audio playback\\n3. **Melody composition**: Translating the Twinkle Twinkle Little Star melody into playable notes\\n\\n## Key Components\\n\\n### Note Frequencies\\nThe implementation uses standard musical note frequencies:\\n- C4 = 261.63 Hz\\n- D4 = 293.66 Hz\\n- E4 = 329.63 Hz\\n- F4 = 349.23 Hz\\n- G4 = 392.00 Hz\\n- A4 = 440.00 Hz\\n- B4 = 493.88 Hz\\n- C5 = 523.25 Hz\\n\\n### Melody Structure\\nThe Twinkle Twinkle Little Star melody is represented as a sequence of note-duration pairs:\\n```\\nC4(0.5s), C4(0.5s), G4(0.5s), G4(0.5s), A4(0.5s), A4(0.5s), G4(1.0s),\\nF4(0.5s), F4(0.5s), E4(0.5s), E4(0.5s), D4(0.5s), D4(0.5s), C4(1.0s)\\n```\\n\\n## Implementation Details\\n\\nThe solution creates a sine wave for each note using the formula:\\n```\\nwave = sin(2π × frequency × time)\\n```\\n\\nThen converts the wave to 16-bit integers for compatibility with the audio stream pipeline.\\n\\n## How to Run\\n\\n1. Ensure you have the required dependencies installed:\\n ```bash\\n pip install sounddevice scipy numpy\\n ```\\n\\n2. Run the script:\\n ```bash\\n python twinkle_twinkle_final.py\\n ```\\n\\nThe script will:\\n- Create an audio stream\\n- Generate and play each note in the melody\\n- Wait for all notes to finish playing\\n- Print status messages during execution\\n\\n## Technical Approach\\n\\nThe implementation leverages txtai\\'s `AudioStream` pipeline which:\\n- Uses threading to handle audio playback\\n- Supports sample rate conversion\\n- Provides blocking playback for sequential note playing\\n- Handles audio device access for local playback\\n\\nThis approach demonstrates the complete integration of musical note generation with txtai\\'s audio streaming capabilities, following the structure and patterns found in the txtai/src directory.\\n\\nNote: While the code is properly structured and should work in environments with proper audio support, the execution environment may not have audio output capabilities, which is why no audible sound was produced during testing.'" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "agent((\n", + " \"Play twinkle twinkle little star song using simple notes translated to frequencies and the txtai audio stream pipeline. \"\n", + " \"The txtai code is available in txtai/src\"\n", + "), maxlength=25000)" + ] + }, + { + "cell_type": "markdown", + "id": "90097bdb", + "metadata": {}, + "source": [ + "Depending on the permissions given to the `bash` tool, some steps will be no-op. For example, the default settings don't allow running python or curl. So they're just skipped over. \n", + "\n", + "If this worked as expected, you should have a Python file with code similar to what's below\n", + "\n", + "```python\n", + "# Twinkle Twinkle Little Star with txtai audio stream\n", + "\n", + "import numpy as np\n", + "from txtai.pipeline.audio.audiostream import AudioStream\n", + "\n", + "# Note frequencies (C4 = 261.63 Hz)\n", + "NOTE_FREQUENCIES = {\n", + " 'C4': 261.63,\n", + " 'D4': 293.66,\n", + " 'E4': 329.63,\n", + " 'F4': 349.23,\n", + " 'G4': 392.00,\n", + " 'A4': 440.00,\n", + " 'B4': 493.88,\n", + " 'C5': 523.25,\n", + "}\n", + "\n", + "# Twinkle Twinkle Little Star melody\n", + "# Each tuple is (note, duration in seconds)\n", + "MELODY = [\n", + " ('C4', 0.5), ('C4', 0.5), ('G4', 0.5), ('G4', 0.5),\n", + " ('A4', 0.5), ('A4', 0.5), ('G4', 1.0),\n", + " ('F4', 0.5), ('F4', 0.5), ('E4', 0.5), ('E4', 0.5),\n", + " ('D4', 0.5), ('D4', 0.5), ('C4', 1.0)\n", + "]\n", + "\n", + "def generate_sine_wave(frequency, duration, sample_rate=44100):\n", + " \"\"\"\n", + " Generate a sine wave for a given frequency and duration\n", + " \"\"\"\n", + " t = np.linspace(0, duration, int(sample_rate * duration), False)\n", + " wave = np.sin(2 * np.pi * frequency * t)\n", + " return wave\n", + "\n", + "\n", + "def play_melody():\n", + " \"\"\"\n", + " Play the Twinkle Twinkle Little Star melody\n", + " \"\"\"\n", + " # Create audio stream\n", + " audio_stream = AudioStream()\n", + " \n", + " sample_rate = 44100\n", + " \n", + " # Generate and play each note\n", + " for note, duration in MELODY:\n", + " frequency = NOTE_FREQUENCIES[note]\n", + " wave = generate_sine_wave(frequency, duration, sample_rate)\n", + " \n", + " # Convert to 16-bit integers for audio stream\n", + " wave_int16 = np.int16(wave * 32767)\n", + " \n", + " # Play the note\n", + " audio_stream((wave_int16, sample_rate))\n", + " \n", + " # Add a small pause between notes\n", + " # (This is handled by the audio stream blocking)\n", + " \n", + " # Wait for all audio to finish playing\n", + " audio_stream.wait()\n", + " print(\"Melody finished!\")\n", + "\n", + "if __name__ == \"__main__\":\n", + " play_melody()\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "25f6d02b", + "metadata": {}, + "source": [ + "# Add a new TxtAI pipeline\n", + "\n", + "Now let's see if this agent can figure out how to add a new TxtAI pipeline." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "6f3803be", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
╭──────────────────────────────────────────────────── New run ────────────────────────────────────────────────────╮\n",
+       "                                                                                                                 \n",
+       " I want to update txtai to add a new pipeline. This pipeline should replace the input text with a new pattern.   \n",
+       " Source code is in txtai/src directory. Don't worry about testing or running the pipeline.                       \n",
+       "                                                                                                                 \n",
+       "╰─ PipelineModel - unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF/Qwen3-Coder-30B-A3B-Instruct-Q3_K_M.gguf ────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m╭─\u001b[0m\u001b[38;2;212;183;2m───────────────────────────────────────────────────\u001b[0m\u001b[38;2;212;183;2m \u001b[0m\u001b[1;38;2;212;183;2mNew run\u001b[0m\u001b[38;2;212;183;2m \u001b[0m\u001b[38;2;212;183;2m───────────────────────────────────────────────────\u001b[0m\u001b[38;2;212;183;2m─╮\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mI want to update txtai to add a new pipeline. This pipeline should replace the input text with a new pattern. \u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[1mSource code is in txtai/src directory. Don't worry about testing or running the pipeline.\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m│\u001b[0m \u001b[38;2;212;183;2m│\u001b[0m\n", + "\u001b[38;2;212;183;2m╰─\u001b[0m\u001b[38;2;212;183;2m PipelineModel - unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF/Qwen3-Coder-30B-A3B-Instruct-Q3_K_M.gguf \u001b[0m\u001b[38;2;212;183;2m───────────\u001b[0m\u001b[38;2;212;183;2m─╯\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 1 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 1\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'bash' with arguments: {'command': ['find', 'txtai/src', '-type', 'f', '-name', '*.py']}          │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'bash' with arguments: {'command': ['find', 'txtai/src', '-type', 'f', '-name', '*.py']} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: txtai/src/python/txtai/workflow/task/workflow.py\n",
+       "txtai/src/python/txtai/workflow/task/url.py\n",
+       "txtai/src/python/txtai/workflow/task/template.py\n",
+       "txtai/src/python/txtai/workflow/task/stream.py\n",
+       "txtai/src/python/txtai/workflow/task/storage.py\n",
+       "txtai/src/python/txtai/workflow/task/service.py\n",
+       "txtai/src/python/txtai/workflow/task/retrieve.py\n",
+       "txtai/src/python/txtai/workflow/task/image.py\n",
+       "txtai/src/python/txtai/workflow/task/file.py\n",
+       "txtai/src/python/txtai/workflow/task/factory.py\n",
+       "txtai/src/python/txtai/workflow/task/export.py\n",
+       "txtai/src/python/txtai/workflow/task/console.py\n",
+       "txtai/src/python/txtai/workflow/task/base.py\n",
+       "txtai/src/python/txtai/workflow/task/__init__.py\n",
+       "txtai/src/python/txtai/workflow/factory.py\n",
+       "txtai/src/python/txtai/workflow/execute.py\n",
+       "txtai/src/python/txtai/workflow/base.py\n",
+       "txtai/src/python/txtai/workflow/__init__.py\n",
+       "[TRUNCATED...]\n",
+       "
\n" + ], + "text/plain": [ + "Observations: txtai/src/python/txtai/workflow/task/workflow.py\n", + "txtai/src/python/txtai/workflow/task/url.py\n", + "txtai/src/python/txtai/workflow/task/template.py\n", + "txtai/src/python/txtai/workflow/task/stream.py\n", + "txtai/src/python/txtai/workflow/task/storage.py\n", + "txtai/src/python/txtai/workflow/task/service.py\n", + "txtai/src/python/txtai/workflow/task/retrieve.py\n", + "txtai/src/python/txtai/workflow/task/image.py\n", + "txtai/src/python/txtai/workflow/task/file.py\n", + "txtai/src/python/txtai/workflow/task/factory.py\n", + "txtai/src/python/txtai/workflow/task/export.py\n", + "txtai/src/python/txtai/workflow/task/console.py\n", + "txtai/src/python/txtai/workflow/task/base.py\n", + "txtai/src/python/txtai/workflow/task/__init__.py\n", + "txtai/src/python/txtai/workflow/factory.py\n", + "txtai/src/python/txtai/workflow/execute.py\n", + "txtai/src/python/txtai/workflow/base.py\n", + "txtai/src/python/txtai/workflow/__init__.py\n", + "[TRUNCATED...]\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 1: Duration 0.90 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 1: Duration 0.90 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 2 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 2\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'bash' with arguments: {'command': ['ls', '-la', 'txtai/src/python/txtai/pipeline/']}             │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'bash' with arguments: {'command': ['ls', '-la', 'txtai/src/python/txtai/pipeline/']} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: total 28\n",
+       "drwxr-xr-x.  8 user user  300 Mar 16 08:56 .\n",
+       "drwxr-xr-x. 20 user user  440 Mar 16 08:20 ..\n",
+       "drwxr-xr-x.  2 user user  200 Mar 16 08:20 audio\n",
+       "-rw-r--r--.  1 user user  536 Mar 16 08:20 base.py\n",
+       "drwxr-xr-x.  2 user user  180 Mar 16 08:20 data\n",
+       "-rw-r--r--.  1 user user 1782 Mar 16 08:20 factory.py\n",
+       "-rw-r--r--.  1 user user 3684 Mar 16 08:20 hfmodel.py\n",
+       "-rw-r--r--.  1 user user 3518 Mar 16 08:20 hfpipeline.py\n",
+       "drwxr-xr-x.  2 user user  120 Mar 16 08:20 image\n",
+       "-rw-r--r--.  1 user user  360 Mar 16 08:56 __init__.py\n",
+       "drwxr-xr-x.  2 user user  220 Mar 16 08:20 llm\n",
+       "-rw-r--r--.  1 user user  189 Mar 16 08:20 nop.py\n",
+       "-rw-r--r--.  1 user user 1058 Mar 16 08:20 tensors.py\n",
+       "drwxr-xr-x.  2 user user  240 Mar 16 08:56 text\n",
+       "drwxr-xr-x.  2 user user  120 Mar 16 08:20 train\n",
+       "
\n" + ], + "text/plain": [ + "Observations: total 28\n", + "drwxr-xr-x. 8 user user 300 Mar 16 08:56 .\n", + "drwxr-xr-x. 20 user user 440 Mar 16 08:20 ..\n", + "drwxr-xr-x. 2 user user 200 Mar 16 08:20 audio\n", + "-rw-r--r--. 1 user user 536 Mar 16 08:20 base.py\n", + "drwxr-xr-x. 2 user user 180 Mar 16 08:20 data\n", + "-rw-r--r--. 1 user user 1782 Mar 16 08:20 factory.py\n", + "-rw-r--r--. 1 user user 3684 Mar 16 08:20 hfmodel.py\n", + "-rw-r--r--. 1 user user 3518 Mar 16 08:20 hfpipeline.py\n", + "drwxr-xr-x. 2 user user 120 Mar 16 08:20 image\n", + "-rw-r--r--. 1 user user 360 Mar 16 08:56 __init__.py\n", + "drwxr-xr-x. 2 user user 220 Mar 16 08:20 llm\n", + "-rw-r--r--. 1 user user 189 Mar 16 08:20 nop.py\n", + "-rw-r--r--. 1 user user 1058 Mar 16 08:20 tensors.py\n", + "drwxr-xr-x. 2 user user 240 Mar 16 08:56 text\n", + "drwxr-xr-x. 2 user user 120 Mar 16 08:20 train\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 2: Duration 3.25 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 2: Duration 3.25 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 3 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 3\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'bash' with arguments: {'command': ['ls', '-la', 'txtai/src/python/txtai/pipeline/text/']}        │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'bash' with arguments: {'command': ['ls', '-la', 'txtai/src/python/txtai/pipeline/text/']} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: total 56\n",
+       "drwxr-xr-x. 2 user user  240 Mar 16 08:56 .\n",
+       "drwxr-xr-x. 8 user user  300 Mar 16 08:56 ..\n",
+       "-rw-r--r--. 1 user user 2619 Mar 16 08:20 crossencoder.py\n",
+       "-rw-r--r--. 1 user user 5132 Mar 16 08:20 entity.py\n",
+       "-rw-r--r--. 1 user user  317 Mar 16 08:56 __init__.py\n",
+       "-rw-r--r--. 1 user user 5392 Mar 16 08:20 labels.py\n",
+       "-rw-r--r--. 1 user user 3217 Mar 16 08:20 lateencoder.py\n",
+       "-rw-r--r--. 1 user user 1417 Mar 16 08:20 questions.py\n",
+       "-rw-r--r--. 1 user user 1794 Mar 16 08:20 reranker.py\n",
+       "-rw-r--r--. 1 user user 3051 Mar 16 08:20 similarity.py\n",
+       "-rw-r--r--. 1 user user 2880 Mar 16 08:20 summary.py\n",
+       "-rw-r--r--. 1 user user 9428 Mar 16 08:20 translation.py\n",
+       "
\n" + ], + "text/plain": [ + "Observations: total 56\n", + "drwxr-xr-x. 2 user user 240 Mar 16 08:56 .\n", + "drwxr-xr-x. 8 user user 300 Mar 16 08:56 ..\n", + "-rw-r--r--. 1 user user 2619 Mar 16 08:20 crossencoder.py\n", + "-rw-r--r--. 1 user user 5132 Mar 16 08:20 entity.py\n", + "-rw-r--r--. 1 user user 317 Mar 16 08:56 __init__.py\n", + "-rw-r--r--. 1 user user 5392 Mar 16 08:20 labels.py\n", + "-rw-r--r--. 1 user user 3217 Mar 16 08:20 lateencoder.py\n", + "-rw-r--r--. 1 user user 1417 Mar 16 08:20 questions.py\n", + "-rw-r--r--. 1 user user 1794 Mar 16 08:20 reranker.py\n", + "-rw-r--r--. 1 user user 3051 Mar 16 08:20 similarity.py\n", + "-rw-r--r--. 1 user user 2880 Mar 16 08:20 summary.py\n", + "-rw-r--r--. 1 user user 9428 Mar 16 08:20 translation.py\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 3: Duration 1.36 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 3: Duration 1.36 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 4 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 4\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'read' with arguments: {'path': 'txtai/src/python/txtai/pipeline/text/__init__.py'}               │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'read' with arguments: {'path': 'txtai/src/python/txtai/pipeline/text/__init__.py'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: \"\"\"\n",
+       "Text imports\n",
+       "\"\"\"\n",
+       "\n",
+       "from .crossencoder import CrossEncoder\n",
+       "from .entity import Entity\n",
+       "from .labels import Labels\n",
+       "from .lateencoder import LateEncoder\n",
+       "from .questions import Questions\n",
+       "from .reranker import Reranker\n",
+       "from .similarity import Similarity\n",
+       "from .summary import Summary\n",
+       "from .translation import Translation\n",
+       "
\n" + ], + "text/plain": [ + "Observations: \"\"\"\n", + "Text imports\n", + "\"\"\"\n", + "\n", + "from .crossencoder import CrossEncoder\n", + "from .entity import Entity\n", + "from .labels import Labels\n", + "from .lateencoder import LateEncoder\n", + "from .questions import Questions\n", + "from .reranker import Reranker\n", + "from .similarity import Similarity\n", + "from .summary import Summary\n", + "from .translation import Translation\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 4: Duration 1.24 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 4: Duration 1.24 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 5 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 5\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'read' with arguments: {'path': 'txtai/src/python/txtai/pipeline/__init__.py'}                    │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'read' with arguments: {'path': 'txtai/src/python/txtai/pipeline/__init__.py'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: \"\"\"\n",
+       "Pipeline imports\n",
+       "\"\"\"\n",
+       "\n",
+       "from .audio import *\n",
+       "from .base import Pipeline\n",
+       "from .data import *\n",
+       "from .factory import PipelineFactory\n",
+       "from .hfmodel import HFModel\n",
+       "from .hfpipeline import HFPipeline\n",
+       "from .image import *\n",
+       "from .llm import *\n",
+       "from .llm import RAG as Extractor\n",
+       "from .nop import Nop\n",
+       "from .text import *\n",
+       "from .tensors import Tensors\n",
+       "from .train import *\n",
+       "
\n" + ], + "text/plain": [ + "Observations: \"\"\"\n", + "Pipeline imports\n", + "\"\"\"\n", + "\n", + "from .audio import *\n", + "from .base import Pipeline\n", + "from .data import *\n", + "from .factory import PipelineFactory\n", + "from .hfmodel import HFModel\n", + "from .hfpipeline import HFPipeline\n", + "from .image import *\n", + "from .llm import *\n", + "from .llm import RAG as Extractor\n", + "from .nop import Nop\n", + "from .text import *\n", + "from .tensors import Tensors\n", + "from .train import *\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 5: Duration 0.93 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 5: Duration 0.93 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 6 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 6\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'read' with arguments: {'path': 'txtai/src/python/txtai/pipeline/base.py'}                        │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'read' with arguments: {'path': 'txtai/src/python/txtai/pipeline/base.py'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: \"\"\"\n",
+       "Pipeline module\n",
+       "\"\"\"\n",
+       "\n",
+       "class Pipeline:\n",
+       " \"\"\"\n",
+       " Base class for all Pipelines. The only interface requirement is to define a __call___ method.\n",
+       " \"\"\"\n",
+       "\n",
+       " def batch(self, data, size):\n",
+       " \"\"\"\n",
+       " Splits data into separate batch sizes specified by size.\n",
+       "\n",
+       " Args:\n",
+       " data: data elements\n",
+       " size: batch size\n",
+       "\n",
+       " Returns:\n",
+       " list of evenly sized batches with the last batch having the remaining elements\n",
+       " \"\"\"\n",
+       "\n",
+       " return |data|x : x + size] for x in range(0, len(data), size)]\n",
+       "
\n" + ], + "text/plain": [ + "Observations: \"\"\"\n", + "Pipeline module\n", + "\"\"\"\n", + "\n", + "class Pipeline:\n", + " \"\"\"\n", + " Base class for all Pipelines. The only interface requirement is to define a __call___ method.\n", + " \"\"\"\n", + "\n", + " def batch(self, data, size):\n", + " \"\"\"\n", + " Splits data into separate batch sizes specified by size.\n", + "\n", + " Args:\n", + " data: data elements\n", + " size: batch size\n", + "\n", + " Returns:\n", + " list of evenly sized batches with the last batch having the remaining elements\n", + " \"\"\"\n", + "\n", + " return |data|x : x + size] for x in range(0, len(data), size)]\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 6: Duration 0.97 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 6: Duration 0.97 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 7 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 7\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'read' with arguments: {'path': 'txtai/src/python/txtai/pipeline/factory.py'}                     │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'read' with arguments: {'path': 'txtai/src/python/txtai/pipeline/factory.py'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: \"\"\"\n",
+       "Pipeline factory module\n",
+       "\"\"\"\n",
+       "\n",
+       "import inspect\n",
+       "import sys\n",
+       "import types\n",
+       "\n",
+       "from ..util import Resolver\n",
+       "\n",
+       "from .base import Pipeline\n",
+       "\n",
+       "class PipelineFactory:\n",
+       " \"\"\"\n",
+       " Pipeline factory. Creates new Pipeline instances.\n",
+       " \"\"\"\n",
+       "\n",
+       " @staticmethod\n",
+       " def get(pipeline):\n",
+       " \"\"\"\n",
+       " Gets a new instance of pipeline class.\n",
+       "\n",
+       " Args:\n",
+       " pclass: Pipeline instance class\n",
+       "\n",
+       " Returns:\n",
+       " Pipeline class\n",
+       " \"\"\"\n",
+       "\n",
+       " # Local pipeline if no package\n",
+       " if \".\" not in pipeline:\n",
+       " return PipelineFactory.list()|pipeline]\n",
+       "\n",
+       " # Attempt to load custom pipeline\n",
+       " return Resolver()(pipeline)\n",
+       "\n",
+       " @staticmethod\n",
+       " def create(config, pipeline):\n",
+       " \"\"\"\n",
+       " Creates a new Pipeline instance.\n",
+       "\n",
+       " Args:\n",
+       " config: Pipeline configuration\n",
+       " pipeline: Pipeline instance class\n",
+       "\n",
+       " Returns:\n",
+       " Pipeline\n",
+       " \"\"\"\n",
+       "\n",
+       " # Resolve pipeline\n",
+       " pipeline = PipelineFactory.get(pipeline)\n",
+       "\n",
+       " # Return functions directly, otherwise create pipeline instance\n",
+       " return pipeline if isinstance(pipeline, types.FunctionType) else pipeline(**config)\n",
+       "\n",
+       " @staticmethod\n",
+       " def list():\n",
+       " \"\"\"\n",
+       " Lists callable pipelines.\n",
+       "\n",
+       " Returns:\n",
+       " {short name: pipeline class}\n",
+       " \"\"\"\n",
+       "\n",
+       " pipelines = {}\n",
+       "\n",
+       " # Get handle to pipeline module\n",
+       " pipeline = sys.modules|\".\".join(__name__.split(\".\")|:-1])]\n",
+       "\n",
+       " # Get list of callable pipelines\n",
+       " for x in inspect.getmembers(pipeline, inspect.isclass):\n",
+       " if issubclass(x|1], Pipeline) and |y for y, _ in inspect.getmembers(x|1], inspect.isfunction) if y == \"__call__\"]:\n",
+       " # short name: pipeline class\n",
+       " pipelines|x|0].lower()] = x|1]\n",
+       "\n",
+       " return pipelines\n",
+       "
\n" + ], + "text/plain": [ + "Observations: \"\"\"\n", + "Pipeline factory module\n", + "\"\"\"\n", + "\n", + "import inspect\n", + "import sys\n", + "import types\n", + "\n", + "from ..util import Resolver\n", + "\n", + "from .base import Pipeline\n", + "\n", + "class PipelineFactory:\n", + " \"\"\"\n", + " Pipeline factory. Creates new Pipeline instances.\n", + " \"\"\"\n", + "\n", + " @staticmethod\n", + " def get(pipeline):\n", + " \"\"\"\n", + " Gets a new instance of pipeline class.\n", + "\n", + " Args:\n", + " pclass: Pipeline instance class\n", + "\n", + " Returns:\n", + " Pipeline class\n", + " \"\"\"\n", + "\n", + " # Local pipeline if no package\n", + " if \".\" not in pipeline:\n", + " return PipelineFactory.list()|pipeline]\n", + "\n", + " # Attempt to load custom pipeline\n", + " return Resolver()(pipeline)\n", + "\n", + " @staticmethod\n", + " def create(config, pipeline):\n", + " \"\"\"\n", + " Creates a new Pipeline instance.\n", + "\n", + " Args:\n", + " config: Pipeline configuration\n", + " pipeline: Pipeline instance class\n", + "\n", + " Returns:\n", + " Pipeline\n", + " \"\"\"\n", + "\n", + " # Resolve pipeline\n", + " pipeline = PipelineFactory.get(pipeline)\n", + "\n", + " # Return functions directly, otherwise create pipeline instance\n", + " return pipeline if isinstance(pipeline, types.FunctionType) else pipeline(**config)\n", + "\n", + " @staticmethod\n", + " def list():\n", + " \"\"\"\n", + " Lists callable pipelines.\n", + "\n", + " Returns:\n", + " {short name: pipeline class}\n", + " \"\"\"\n", + "\n", + " pipelines = {}\n", + "\n", + " # Get handle to pipeline module\n", + " pipeline = sys.modules|\".\".join(__name__.split(\".\")|:-1])]\n", + "\n", + " # Get list of callable pipelines\n", + " for x in inspect.getmembers(pipeline, inspect.isclass):\n", + " if issubclass(x|1], Pipeline) and |y for y, _ in inspect.getmembers(x|1], inspect.isfunction) if y == \"__call__\"]:\n", + " # short name: pipeline class\n", + " pipelines|x|0].lower()] = x|1]\n", + "\n", + " return pipelines\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 7: Duration 0.90 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 7: Duration 0.90 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 8 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 8\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'read' with arguments: {'path': 'txtai/src/python/txtai/pipeline/text/summary.py'}                │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'read' with arguments: {'path': 'txtai/src/python/txtai/pipeline/text/summary.py'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: \"\"\"\n",
+       "Summary module\n",
+       "\"\"\"\n",
+       "\n",
+       "import re\n",
+       "\n",
+       "from ..hfpipeline import HFPipeline\n",
+       "\n",
+       "class Summary(HFPipeline):\n",
+       " \"\"\"\n",
+       " Summarizes text.\n",
+       " \"\"\"\n",
+       "\n",
+       " def __init__(self, path=None, quantize=False, gpu=True, model=None, **kwargs):\n",
+       " super().__init__(\"summarization\", path, quantize, gpu, model, **kwargs)\n",
+       "\n",
+       " def __call__(self, text, minlength=None, maxlength=None, workers=0):\n",
+       " \"\"\"\n",
+       " Runs a summarization model against a block of text.\n",
+       "\n",
+       " This method supports text as a string or a list. If the input is a string, the return\n",
+       " type is text. If text is a list, a list of text is returned with a row per block of text.\n",
+       "\n",
+       " Args:\n",
+       " text: text|list\n",
+       " minlength: minimum length for summary\n",
+       " maxlength: maximum length for summary\n",
+       " workers: number of concurrent workers to use for processing data, defaults to None\n",
+       "\n",
+       " Returns:\n",
+       " summary text\n",
+       " \"\"\"\n",
+       "\n",
+       " # Validate text length greater than max length\n",
+       " check = maxlength if maxlength else self.maxlength()\n",
+       "\n",
+       " # Skip text shorter than max length\n",
+       " texts = text if isinstance(text, list) else |text]\n",
+       " params = |(x, text if len(text) >= check else None) for x, text in enumerate(texts)]\n",
+       "\n",
+       " # Build keyword arguments\n",
+       " kwargs = self.args(minlength, maxlength)\n",
+       "\n",
+       " inputs = |text for _, text in params if text]\n",
+       " if inputs:\n",
+       " # Run summarization pipeline\n",
+       " results = self.pipeline(inputs, num_workers=workers, **kwargs)\n",
+       "\n",
+       " # Pull out summary text\n",
+       " results = iter(|self.clean(x|\"summary_text\"]) for x in results])\n",
+       " results = |next(results) if text else texts|x] for x, text in params]\n",
+       " else:\n",
+       " # Return original\n",
+       " results = texts\n",
+       "\n",
+       " return results|0] if isinstance(text, str) else results\n",
+       "\n",
+       " def clean(self, text):\n",
+       " \"\"\"\n",
+       " Applies a series of rules to clean extracted text.\n",
+       "\n",
+       " Args:\n",
+       " text: input text\n",
+       "\n",
+       " Returns:\n",
+       " clean text\n",
+       " \"\"\"\n",
+       "\n",
+       " text = re.sub(r\"\\s*\\.\\s*\", \". \", text)\n",
+       " text = text.strip()\n",
+       "\n",
+       " return text\n",
+       "\n",
+       " def args(self, minlength, maxlength):\n",
+       " \"\"\"\n",
+       " Builds keyword arguments.\n",
+       "\n",
+       " Args:\n",
+       " minlength: minimum length for summary\n",
+       " maxlength: maximum length for summary\n",
+       "\n",
+       " Returns:\n",
+       " keyword arguments\n",
+       " \"\"\"\n",
+       "\n",
+       " kwargs = {\"truncation\": True}\n",
+       " if minlength:\n",
+       " kwargs|\"min_length\"] = minlength\n",
+       " if maxlength:\n",
+       " kwargs|\"max_length\"] = maxlength\n",
+       " kwargs|\"max_new_tokens\"] = None\n",
+       "\n",
+       " # Default minlength if not provided or it's bigger than maxlength\n",
+       " if \"min_length\" not in kwargs or kwargs|\"min_length\"] > kwargs|\"max_length\"]:\n",
+       " kwargs|\"min_length\"] = kwargs|\"max_length\"]\n",
+       "\n",
+       " return kwargs\n",
+       "
\n" + ], + "text/plain": [ + "Observations: \"\"\"\n", + "Summary module\n", + "\"\"\"\n", + "\n", + "import re\n", + "\n", + "from ..hfpipeline import HFPipeline\n", + "\n", + "class Summary(HFPipeline):\n", + " \"\"\"\n", + " Summarizes text.\n", + " \"\"\"\n", + "\n", + " def __init__(self, path=None, quantize=False, gpu=True, model=None, **kwargs):\n", + " super().__init__(\"summarization\", path, quantize, gpu, model, **kwargs)\n", + "\n", + " def __call__(self, text, minlength=None, maxlength=None, workers=0):\n", + " \"\"\"\n", + " Runs a summarization model against a block of text.\n", + "\n", + " This method supports text as a string or a list. If the input is a string, the return\n", + " type is text. If text is a list, a list of text is returned with a row per block of text.\n", + "\n", + " Args:\n", + " text: text|list\n", + " minlength: minimum length for summary\n", + " maxlength: maximum length for summary\n", + " workers: number of concurrent workers to use for processing data, defaults to None\n", + "\n", + " Returns:\n", + " summary text\n", + " \"\"\"\n", + "\n", + " # Validate text length greater than max length\n", + " check = maxlength if maxlength else self.maxlength()\n", + "\n", + " # Skip text shorter than max length\n", + " texts = text if isinstance(text, list) else |text]\n", + " params = |(x, text if len(text) >= check else None) for x, text in enumerate(texts)]\n", + "\n", + " # Build keyword arguments\n", + " kwargs = self.args(minlength, maxlength)\n", + "\n", + " inputs = |text for _, text in params if text]\n", + " if inputs:\n", + " # Run summarization pipeline\n", + " results = self.pipeline(inputs, num_workers=workers, **kwargs)\n", + "\n", + " # Pull out summary text\n", + " results = iter(|self.clean(x|\"summary_text\"]) for x in results])\n", + " results = |next(results) if text else texts|x] for x, text in params]\n", + " else:\n", + " # Return original\n", + " results = texts\n", + "\n", + " return results|0] if isinstance(text, str) else results\n", + "\n", + " def clean(self, text):\n", + " \"\"\"\n", + " Applies a series of rules to clean extracted text.\n", + "\n", + " Args:\n", + " text: input text\n", + "\n", + " Returns:\n", + " clean text\n", + " \"\"\"\n", + "\n", + " text = re.sub(r\"\\s*\\.\\s*\", \". \", text)\n", + " text = text.strip()\n", + "\n", + " return text\n", + "\n", + " def args(self, minlength, maxlength):\n", + " \"\"\"\n", + " Builds keyword arguments.\n", + "\n", + " Args:\n", + " minlength: minimum length for summary\n", + " maxlength: maximum length for summary\n", + "\n", + " Returns:\n", + " keyword arguments\n", + " \"\"\"\n", + "\n", + " kwargs = {\"truncation\": True}\n", + " if minlength:\n", + " kwargs|\"min_length\"] = minlength\n", + " if maxlength:\n", + " kwargs|\"max_length\"] = maxlength\n", + " kwargs|\"max_new_tokens\"] = None\n", + "\n", + " # Default minlength if not provided or it's bigger than maxlength\n", + " if \"min_length\" not in kwargs or kwargs|\"min_length\"] > kwargs|\"max_length\"]:\n", + " kwargs|\"min_length\"] = kwargs|\"max_length\"]\n", + "\n", + " return kwargs\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 8: Duration 1.20 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 8: Duration 1.20 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 9 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 9\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'bash' with arguments: {'command': ['grep', '-r', 'replace',                                      │\n",
+       "│ 'txtai/src/python/txtai/pipeline/text/']}                                                                       │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'bash' with arguments: {'command': ['grep', '-r', 'replace', │\n", + "│ 'txtai/src/python/txtai/pipeline/text/']} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: txtai/src/python/txtai/pipeline/text/translation.py:        texts = |x.lower().replace(\"\\n\", \" \n",
+       "\").replace(\"\\r\\n\", \" \") for x in texts]\n",
+       "
\n" + ], + "text/plain": [ + "Observations: txtai/src/python/txtai/pipeline/text/translation.py: texts = |x.lower().replace(\"\\n\", \" \n", + "\").replace(\"\\r\\n\", \" \") for x in texts]\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 9: Duration 1.96 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 9: Duration 1.96 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 10 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 10\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'write' with arguments: {'path': 'txtai/src/python/txtai/pipeline/text/pattern.py', 'content':    │\n",
+       "│ '\"\"\"\\nPattern module\\n\"\"\"\\n\\nimport re\\n\\nfrom ..base import Pipeline\\n\\nclass Pattern(Pipeline):\\n    \"\"\"\\n    │\n",
+       "│ Replaces text with a new pattern.\\n    \"\"\"\\n\\n    def __init__(self, pattern=None, replacement=\"\", flags=0):\\n  │\n",
+       "│ \"\"\"\\n        Initializes the Pattern pipeline.\\n\\n        Args:\\n            pattern: Regular expression        │\n",
+       "│ pattern to match\\n            replacement: Replacement text\\n            flags: Regular expression flags\\n      │\n",
+       "│ \"\"\"\\n        self.pattern = pattern\\n        self.replacement = replacement\\n        self.flags = flags\\n\\n     │\n",
+       "│ def __call__(self, text):\\n        \"\"\"\\n        Replaces text with a new pattern.\\n\\n        Args:\\n            │\n",
+       "│ text: input text to process\\n\\n        Returns:\\n            text with pattern replaced\\n        \"\"\"\\n          │\n",
+       "│ if self.pattern is None:\\n            return text\\n        \\n        return re.sub(self.pattern,                │\n",
+       "│ self.replacement, text, flags=self.flags)\\n\\n    def batch(self, texts, size=100):\\n        \"\"\"\\n               │\n",
+       "│ Processes a batch of texts.\\n\\n        Args:\\n            texts: list of texts to process\\n            size:    │\n",
+       "│ batch size\\n\\n        Returns:\\n            list of processed texts\\n        \"\"\"\\n        return [self(text)    │\n",
+       "│ for text in texts]'}                                                                                            │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'write' with arguments: {'path': 'txtai/src/python/txtai/pipeline/text/pattern.py', 'content': │\n", + "│ '\"\"\"\\nPattern module\\n\"\"\"\\n\\nimport re\\n\\nfrom ..base import Pipeline\\n\\nclass Pattern(Pipeline):\\n \"\"\"\\n │\n", + "│ Replaces text with a new pattern.\\n \"\"\"\\n\\n def __init__(self, pattern=None, replacement=\"\", flags=0):\\n │\n", + "│ \"\"\"\\n Initializes the Pattern pipeline.\\n\\n Args:\\n pattern: Regular expression │\n", + "│ pattern to match\\n replacement: Replacement text\\n flags: Regular expression flags\\n │\n", + "│ \"\"\"\\n self.pattern = pattern\\n self.replacement = replacement\\n self.flags = flags\\n\\n │\n", + "│ def __call__(self, text):\\n \"\"\"\\n Replaces text with a new pattern.\\n\\n Args:\\n │\n", + "│ text: input text to process\\n\\n Returns:\\n text with pattern replaced\\n \"\"\"\\n │\n", + "│ if self.pattern is None:\\n return text\\n \\n return re.sub(self.pattern, │\n", + "│ self.replacement, text, flags=self.flags)\\n\\n def batch(self, texts, size=100):\\n \"\"\"\\n │\n", + "│ Processes a batch of texts.\\n\\n Args:\\n texts: list of texts to process\\n size: │\n", + "│ batch size\\n\\n Returns:\\n list of processed texts\\n \"\"\"\\n return [self(text) │\n", + "│ for text in texts]'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: None\n",
+       "
\n" + ], + "text/plain": [ + "Observations: None\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 10: Duration 5.65 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 10: Duration 5.65 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 11 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 11\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'edit' with arguments: {'path': 'txtai/src/python/txtai/pipeline/text/__init__.py', 'search':     │\n",
+       "│ 'from .crossencoder import CrossEncoder\\nfrom .entity import Entity\\nfrom .labels import Labels\\nfrom           │\n",
+       "│ .lateencoder import LateEncoder\\nfrom .questions import Questions\\nfrom .reranker import Reranker\\nfrom         │\n",
+       "│ .similarity import Similarity\\nfrom .summary import Summary\\nfrom .translation import Translation', 'replace':  │\n",
+       "│ 'from .crossencoder import CrossEncoder\\nfrom .entity import Entity\\nfrom .labels import Labels\\nfrom           │\n",
+       "│ .lateencoder import LateEncoder\\nfrom .questions import Questions\\nfrom .reranker import Reranker\\nfrom         │\n",
+       "│ .similarity import Similarity\\nfrom .summary import Summary\\nfrom .translation import Translation\\nfrom         │\n",
+       "│ .pattern import Pattern'}                                                                                       │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'edit' with arguments: {'path': 'txtai/src/python/txtai/pipeline/text/__init__.py', 'search': │\n", + "│ 'from .crossencoder import CrossEncoder\\nfrom .entity import Entity\\nfrom .labels import Labels\\nfrom │\n", + "│ .lateencoder import LateEncoder\\nfrom .questions import Questions\\nfrom .reranker import Reranker\\nfrom │\n", + "│ .similarity import Similarity\\nfrom .summary import Summary\\nfrom .translation import Translation', 'replace': │\n", + "│ 'from .crossencoder import CrossEncoder\\nfrom .entity import Entity\\nfrom .labels import Labels\\nfrom │\n", + "│ .lateencoder import LateEncoder\\nfrom .questions import Questions\\nfrom .reranker import Reranker\\nfrom │\n", + "│ .similarity import Similarity\\nfrom .summary import Summary\\nfrom .translation import Translation\\nfrom │\n", + "│ .pattern import Pattern'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: --- txtai/src/python/txtai/pipeline/text/__init__.py\n",
+       "+++ txtai/src/python/txtai/pipeline/text/__init__.py\n",
+       "@@ -11,3 +11,4 @@\n",
+       " from .similarity import Similarity\n",
+       " from .summary import Summary\n",
+       " from .translation import Translation\n",
+       "+from .pattern import Pattern\n",
+       "
\n" + ], + "text/plain": [ + "Observations: --- txtai/src/python/txtai/pipeline/text/__init__.py\n", + "+++ txtai/src/python/txtai/pipeline/text/__init__.py\n", + "@@ -11,3 +11,4 @@\n", + " from .similarity import Similarity\n", + " from .summary import Summary\n", + " from .translation import Translation\n", + "+from .pattern import Pattern\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 11: Duration 3.44 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 11: Duration 3.44 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 12 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 12\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'write' with arguments: {'path': 'txtai/src/python/txtai/pipeline/text/example_pattern.py',       │\n",
+       "│ 'content': '\"\"\"\\nExample usage of Pattern pipeline\\n\"\"\"\\n\\nfrom txtai.pipeline.text import Pattern\\n\\n# Create  │\n",
+       "│ a pattern replacement pipeline\\npipeline = Pattern(r\"\\\\d+\", \"[NUMBER]\")\\n\\n# Process text\\nresult = pipeline(\"I │\n",
+       "│ have 5 cats and 3 dogs\")\\nprint(result)  # Should output: I have [NUMBER] cats and [NUMBER] dogs\\n\\n# Process   │\n",
+       "│ batch\\nbatch_result = pipeline.batch([\"I have 5 cats\", \"She has 2 dogs\"])\\nprint(batch_result)  # Should        │\n",
+       "│ output: [\\'I have [NUMBER] cats\\', \\'She has [NUMBER] dogs\\']'}                                                 │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'write' with arguments: {'path': 'txtai/src/python/txtai/pipeline/text/example_pattern.py', │\n", + "│ 'content': '\"\"\"\\nExample usage of Pattern pipeline\\n\"\"\"\\n\\nfrom txtai.pipeline.text import Pattern\\n\\n# Create │\n", + "│ a pattern replacement pipeline\\npipeline = Pattern(r\"\\\\d+\", \"[NUMBER]\")\\n\\n# Process text\\nresult = pipeline(\"I │\n", + "│ have 5 cats and 3 dogs\")\\nprint(result) # Should output: I have [NUMBER] cats and [NUMBER] dogs\\n\\n# Process │\n", + "│ batch\\nbatch_result = pipeline.batch([\"I have 5 cats\", \"She has 2 dogs\"])\\nprint(batch_result) # Should │\n", + "│ output: [\\'I have [NUMBER] cats\\', \\'She has [NUMBER] dogs\\']'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: None\n",
+       "
\n" + ], + "text/plain": [ + "Observations: None\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 12: Duration 3.44 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 12: Duration 3.44 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 13 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[38;2;212;183;2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \u001b[0m\u001b[1;37mStep 13\u001b[0m\u001b[38;2;212;183;2m ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+       "│ Calling tool: 'final_answer' with arguments: {'answer': 'I have successfully added a new Pattern pipeline to    │\n",
+       "│ txtai that replaces input text with a new pattern. Here\\'s what I did:\\n\\n1. Created a new file                 │\n",
+       "│ `txtai/src/python/txtai/pipeline/text/pattern.py` with a Pattern class that inherits from Pipeline base         │\n",
+       "│ class\\n2. Implemented the `__call__` method to use Python\\'s `re.sub()` for pattern replacement\\n3. Added a     │\n",
+       "│ `batch` method to process multiple texts at once\\n4. Updated `txtai/src/python/txtai/pipeline/text/__init__.py` │\n",
+       "│ to export the new Pattern class\\n\\nThe new pipeline can be used like this:\\n```python\\nfrom txtai.pipeline.text │\n",
+       "│ import Pattern\\n\\n# Create a pattern replacement pipeline\\npipeline = Pattern(r\"\\\\d+\", \"[NUMBER]\")\\n\\n# Process │\n",
+       "│ text\\nresult = pipeline(\"I have 5 cats and 3 dogs\")\\n# Returns: \"I have [NUMBER] cats and [NUMBER] dogs\"\\n\\n#   │\n",
+       "│ Process batch\\nbatch_result = pipeline.batch([\"I have 5 cats\", \"She has 2 dogs\"])\\n# Returns: [\"I have [NUMBER] │\n",
+       "│ cats\", \"She has [NUMBER] dogs\"]\\n```'}                                                                          │\n",
+       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "
\n" + ], + "text/plain": [ + "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Calling tool: 'final_answer' with arguments: {'answer': 'I have successfully added a new Pattern pipeline to │\n", + "│ txtai that replaces input text with a new pattern. Here\\'s what I did:\\n\\n1. Created a new file │\n", + "│ `txtai/src/python/txtai/pipeline/text/pattern.py` with a Pattern class that inherits from Pipeline base │\n", + "│ class\\n2. Implemented the `__call__` method to use Python\\'s `re.sub()` for pattern replacement\\n3. Added a │\n", + "│ `batch` method to process multiple texts at once\\n4. Updated `txtai/src/python/txtai/pipeline/text/__init__.py` │\n", + "│ to export the new Pattern class\\n\\nThe new pipeline can be used like this:\\n```python\\nfrom txtai.pipeline.text │\n", + "│ import Pattern\\n\\n# Create a pattern replacement pipeline\\npipeline = Pattern(r\"\\\\d+\", \"[NUMBER]\")\\n\\n# Process │\n", + "│ text\\nresult = pipeline(\"I have 5 cats and 3 dogs\")\\n# Returns: \"I have [NUMBER] cats and [NUMBER] dogs\"\\n\\n# │\n", + "│ Process batch\\nbatch_result = pipeline.batch([\"I have 5 cats\", \"She has 2 dogs\"])\\n# Returns: [\"I have [NUMBER] │\n", + "│ cats\", \"She has [NUMBER] dogs\"]\\n```'} │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Observations: I have successfully added a new Pattern pipeline to txtai that replaces input text with a new \n",
+       "pattern. Here's what I did:\n",
+       "\n",
+       "1. Created a new file `txtai/src/python/txtai/pipeline/text/pattern.py` with a Pattern class that inherits from \n",
+       "Pipeline base class\n",
+       "2. Implemented the `__call__` method to use Python's `re.sub()` for pattern replacement\n",
+       "3. Added a `batch` method to process multiple texts at once\n",
+       "4. Updated `txtai/src/python/txtai/pipeline/text/__init__.py` to export the new Pattern class\n",
+       "\n",
+       "The new pipeline can be used like this:\n",
+       "```python\n",
+       "from txtai.pipeline.text import Pattern\n",
+       "\n",
+       "# Create a pattern replacement pipeline\n",
+       "pipeline = Pattern(r\"\\d+\", \"|NUMBER]\")\n",
+       "\n",
+       "# Process text\n",
+       "result = pipeline(\"I have 5 cats and 3 dogs\")\n",
+       "# Returns: \"I have |NUMBER] cats and |NUMBER] dogs\"\n",
+       "\n",
+       "# Process batch\n",
+       "batch_result = pipeline.batch(|\"I have 5 cats\", \"She has 2 dogs\"])\n",
+       "# Returns: |\"I have |NUMBER] cats\", \"She has |NUMBER] dogs\"]\n",
+       "```\n",
+       "
\n" + ], + "text/plain": [ + "Observations: I have successfully added a new Pattern pipeline to txtai that replaces input text with a new \n", + "pattern. Here's what I did:\n", + "\n", + "1. Created a new file `txtai/src/python/txtai/pipeline/text/pattern.py` with a Pattern class that inherits from \n", + "Pipeline base class\n", + "2. Implemented the `__call__` method to use Python's `re.sub()` for pattern replacement\n", + "3. Added a `batch` method to process multiple texts at once\n", + "4. Updated `txtai/src/python/txtai/pipeline/text/__init__.py` to export the new Pattern class\n", + "\n", + "The new pipeline can be used like this:\n", + "```python\n", + "from txtai.pipeline.text import Pattern\n", + "\n", + "# Create a pattern replacement pipeline\n", + "pipeline = Pattern(r\"\\d+\", \"|NUMBER]\")\n", + "\n", + "# Process text\n", + "result = pipeline(\"I have 5 cats and 3 dogs\")\n", + "# Returns: \"I have |NUMBER] cats and |NUMBER] dogs\"\n", + "\n", + "# Process batch\n", + "batch_result = pipeline.batch(|\"I have 5 cats\", \"She has 2 dogs\"])\n", + "# Returns: |\"I have |NUMBER] cats\", \"She has |NUMBER] dogs\"]\n", + "```\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Final answer: I have successfully added a new Pattern pipeline to txtai that replaces input text with a new \n",
+       "pattern. Here's what I did:\n",
+       "\n",
+       "1. Created a new file `txtai/src/python/txtai/pipeline/text/pattern.py` with a Pattern class that inherits from \n",
+       "Pipeline base class\n",
+       "2. Implemented the `__call__` method to use Python's `re.sub()` for pattern replacement\n",
+       "3. Added a `batch` method to process multiple texts at once\n",
+       "4. Updated `txtai/src/python/txtai/pipeline/text/__init__.py` to export the new Pattern class\n",
+       "\n",
+       "The new pipeline can be used like this:\n",
+       "```python\n",
+       "from txtai.pipeline.text import Pattern\n",
+       "\n",
+       "# Create a pattern replacement pipeline\n",
+       "pipeline = Pattern(r\"\\d+\", \"[NUMBER]\")\n",
+       "\n",
+       "# Process text\n",
+       "result = pipeline(\"I have 5 cats and 3 dogs\")\n",
+       "# Returns: \"I have [NUMBER] cats and [NUMBER] dogs\"\n",
+       "\n",
+       "# Process batch\n",
+       "batch_result = pipeline.batch([\"I have 5 cats\", \"She has 2 dogs\"])\n",
+       "# Returns: [\"I have [NUMBER] cats\", \"She has [NUMBER] dogs\"]\n",
+       "```\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[1;38;2;212;183;2mFinal answer: I have successfully added a new Pattern pipeline to txtai that replaces input text with a new \u001b[0m\n", + "\u001b[1;38;2;212;183;2mpattern. Here's what I did:\u001b[0m\n", + "\n", + "\u001b[1;38;2;212;183;2m1. Created a new file `txtai/src/python/txtai/pipeline/text/pattern.py` with a Pattern class that inherits from \u001b[0m\n", + "\u001b[1;38;2;212;183;2mPipeline base class\u001b[0m\n", + "\u001b[1;38;2;212;183;2m2. Implemented the `__call__` method to use Python's `re.sub()` for pattern replacement\u001b[0m\n", + "\u001b[1;38;2;212;183;2m3. Added a `batch` method to process multiple texts at once\u001b[0m\n", + "\u001b[1;38;2;212;183;2m4. Updated `txtai/src/python/txtai/pipeline/text/__init__.py` to export the new Pattern class\u001b[0m\n", + "\n", + "\u001b[1;38;2;212;183;2mThe new pipeline can be used like this:\u001b[0m\n", + "\u001b[1;38;2;212;183;2m```python\u001b[0m\n", + "\u001b[1;38;2;212;183;2mfrom txtai.pipeline.text import Pattern\u001b[0m\n", + "\n", + "\u001b[1;38;2;212;183;2m# Create a pattern replacement pipeline\u001b[0m\n", + "\u001b[1;38;2;212;183;2mpipeline = Pattern(r\"\\d+\", \"[NUMBER]\")\u001b[0m\n", + "\n", + "\u001b[1;38;2;212;183;2m# Process text\u001b[0m\n", + "\u001b[1;38;2;212;183;2mresult = pipeline(\"I have 5 cats and 3 dogs\")\u001b[0m\n", + "\u001b[1;38;2;212;183;2m# Returns: \"I have [NUMBER] cats and [NUMBER] dogs\"\u001b[0m\n", + "\n", + "\u001b[1;38;2;212;183;2m# Process batch\u001b[0m\n", + "\u001b[1;38;2;212;183;2mbatch_result = pipeline.batch([\"I have 5 cats\", \"She has 2 dogs\"])\u001b[0m\n", + "\u001b[1;38;2;212;183;2m# Returns: [\"I have [NUMBER] cats\", \"She has [NUMBER] dogs\"]\u001b[0m\n", + "\u001b[1;38;2;212;183;2m```\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
[Step 13: Duration 4.73 seconds]\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[2m[Step 13: Duration 4.73 seconds]\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "'I have successfully added a new Pattern pipeline to txtai that replaces input text with a new pattern. Here\\'s what I did:\\n\\n1. Created a new file `txtai/src/python/txtai/pipeline/text/pattern.py` with a Pattern class that inherits from Pipeline base class\\n2. Implemented the `__call__` method to use Python\\'s `re.sub()` for pattern replacement\\n3. Added a `batch` method to process multiple texts at once\\n4. Updated `txtai/src/python/txtai/pipeline/text/__init__.py` to export the new Pattern class\\n\\nThe new pipeline can be used like this:\\n```python\\nfrom txtai.pipeline.text import Pattern\\n\\n# Create a pattern replacement pipeline\\npipeline = Pattern(r\"\\\\d+\", \"[NUMBER]\")\\n\\n# Process text\\nresult = pipeline(\"I have 5 cats and 3 dogs\")\\n# Returns: \"I have [NUMBER] cats and [NUMBER] dogs\"\\n\\n# Process batch\\nbatch_result = pipeline.batch([\"I have 5 cats\", \"She has 2 dogs\"])\\n# Returns: [\"I have [NUMBER] cats\", \"She has [NUMBER] dogs\"]\\n```'" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "agent((\n", + " \"I want to update txtai to add a new pipeline. This pipeline should replace the input text with a new pattern. \"\n", + " \"Source code is in txtai/src directory. Don't worry about testing or running the pipeline.\"\n", + "))" + ] + }, + { + "cell_type": "markdown", + "id": "f7415bb3", + "metadata": {}, + "source": [ + "Pretty solid! Notice how the agent was able to figure out how TxtAI pipelines work, add a new pipeline and make the appropriate edits. \n", + "\n", + "A fully working TxtAI pipeline ready to submit as a PR." + ] + }, + { + "cell_type": "markdown", + "id": "2e3f2a4f", + "metadata": {}, + "source": [ + "# Wrapping up\n", + "\n", + "This example demonstrated the new agent tools capability coming to TxtAI. Keep in mind these tools can be combined with the existing toolset that enabled reading content from Embeddings databases. These embeddings databases often store business-specific domain knowledge and content.\n", + "\n", + "A compelling open and local-focused AI development platform is within reach!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "local", + "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", + "version": "3.10.19" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/85_Progressive_Distillation.ipynb b/examples/85_Progressive_Distillation.ipynb new file mode 100644 index 0000000..fa51dc2 --- /dev/null +++ b/examples/85_Progressive_Distillation.ipynb @@ -0,0 +1,337 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "40f124c0", + "metadata": {}, + "source": [ + "# Progressive Distillation\n", + "\n", + "Now that almost everyone has thought about or is actively integrating AI workflows into their projects, some might ask is this all worth the cost? Many think the current economics of the AI space don't scale and that there will be upward price movement. Others still might not be comfortable with sending their data to remote services for processing. Then there is the crowd that wants to deploy models in small spaces with limited compute.\n", + "\n", + "Are there ways we can deploy small models locally and run at a lower cost? Yes with [Knowledge Distillation](https://en.wikipedia.org/wiki/Knowledge_distillation). Knowledge distillation can get a bad rap due to it's questionable use in training some Large Language Models (LLMs). But it's a perfectly valid way to transfer performance from a larger model to a smaller one. Especially when both models are yours and/or open. \n", + "\n", + "This notebook will explore progressive distillation which is a technique to incrementally transfer knowledge from a series of larger teacher models into a smaller student." + ] + }, + { + "cell_type": "markdown", + "id": "f4aad867", + "metadata": {}, + "source": [ + "# Install dependencies\n", + "\n", + "Install `txtai` and all dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "16e2c760", + "metadata": {}, + "outputs": [], + "source": [ + "%%capture\n", + "!pip install git+https://github.com/neuml/txtai#egg=txtai[pipeline-train] datasets" + ] + }, + { + "cell_type": "markdown", + "id": "e3a23082", + "metadata": {}, + "source": [ + "# Setup the Training Pipeline\n", + "\n", + "The first step we need to do is setup up the training pipeline. We'll use the Hugging Face Training framework to build a series of models.\n", + "\n", + "The following code establishes a `train` method, `test` method and loads the classification training data." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ea7ee576", + "metadata": {}, + "outputs": [], + "source": [ + "from datasets import load_dataset\n", + "from transformers import AutoModelForSequenceClassification, AutoTokenizer\n", + "from txtai.pipeline import HFTrainer, Labels\n", + "\n", + "def train(teacher, student, distillation, **kwargs):\n", + " trainer = HFTrainer()\n", + "\n", + " model = AutoModelForSequenceClassification.from_pretrained(student, trust_remote_code=True)\n", + " tokenizer = AutoTokenizer.from_pretrained(student, trust_remote_code=True)\n", + "\n", + " return trainer(\n", + " (model, tokenizer),\n", + " ds[\"train\"],\n", + " columns=(\"sentence\", \"label\"),\n", + " maxlength=maxlength,\n", + " teacher=teacher,\n", + " distillation=distillation,\n", + " **kwargs\n", + " )\n", + "\n", + "def test(model):\n", + " labels = Labels(model, dynamic=False, trust_remote_code=True)\n", + "\n", + " # Determine accuracy on test set\n", + " results = [row[\"label\"] == labels(row[\"sentence\"], max_length=maxlength)[0][0] for row in ds[\"validation\"]]\n", + " print(sum(results) / len(ds[\"validation\"]))\n", + "\n", + "# Hugging Face dataset\n", + "ds = load_dataset(\"nyu-mll/glue\", \"sst2\")\n", + "maxlength = 128" + ] + }, + { + "cell_type": "markdown", + "id": "732fad7d", + "metadata": {}, + "source": [ + "# Progressive Distillation\n", + "\n", + "We're going to build a [bert-hash-femto](https://huggingface.co/NeuML/bert-hash-femto) classifier which is a extremely small 250K parameter model. This model was pretrained using the same recipe as [BERT](https://arxiv.org/abs/1810.04805). The paper [Well-Read Students Learn Better: On the Importance of Pre-training Compact Models](https://arxiv.org/abs/1908.08962) established that small models perform better with Knowledge Distillation tasks when they are pretrained. [This article](https://huggingface.co/blog/NeuML/bert-hash-embeddings) is also a good source for more information on the topic.\n", + "\n", + "The next series of steps will do the following:\n", + "\n", + "- Train a [BERT Mini](https://huggingface.co/google/bert_uncased_L-4_H-256_A-4) model using a [BERT Large](https://huggingface.co/assemblyai/bert-large-uncased-sst2) teacher\n", + "- Train a [BERT Tiny](https://huggingface.co/google/bert_uncased_L-2_H-128_A-2) model using the BERT Mini teacher above\n", + "- Train a [BERT Hash Femto](https://huggingface.co/NeuML/bert-hash-femto) model using the BERT Tiny teacher above\n", + "\n", + "This path goes from a 11M parameter model -> 4.4M parameter model -> 250K parameter model\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "43baccd6", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[transformers] \u001b[1mBertForSequenceClassification LOAD REPORT\u001b[0m from: google/bert_uncased_L-4_H-256_A-4\n", + "Key | Status | \n", + "-------------------------------------------+------------+-\n", + "cls.predictions.transform.dense.weight | UNEXPECTED | \n", + "cls.predictions.decoder.bias | UNEXPECTED | \n", + "cls.predictions.transform.LayerNorm.bias | UNEXPECTED | \n", + "cls.predictions.decoder.weight | UNEXPECTED | \n", + "cls.seq_relationship.bias | UNEXPECTED | \n", + "cls.seq_relationship.weight | UNEXPECTED | \n", + "cls.predictions.transform.dense.bias | UNEXPECTED | \n", + "cls.predictions.bias | UNEXPECTED | \n", + "cls.predictions.transform.LayerNorm.weight | UNEXPECTED | \n", + "classifier.weight | MISSING | \n", + "classifier.bias | MISSING | \n", + "\n", + "Notes:\n", + "- UNEXPECTED:\tcan be ignored when loading from different task/architecture; not ok if you expect identical arch.\n", + "- MISSING:\tthose params were newly initialized because missing from the checkpoint. Consider training on your downstream task.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.875\n" + ] + } + ], + "source": [ + "test(train(\n", + " \"assemblyai/bert-large-uncased-sst2\",\n", + " \"google/bert_uncased_L-4_H-256_A-4\",\n", + " (1.0, 1.0),\n", + " learning_rate=1e-4,\n", + " num_train_epochs=5,\n", + " per_device_train_batch_size=32,\n", + " output_dir=\"bert-mini-sst2\"\n", + "))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "9ba3158e", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[transformers] \u001b[1mBertForSequenceClassification LOAD REPORT\u001b[0m from: google/bert_uncased_L-2_H-128_A-2\n", + "Key | Status | \n", + "-------------------------------------------+------------+-\n", + "cls.predictions.transform.dense.weight | UNEXPECTED | \n", + "cls.predictions.transform.LayerNorm.bias | UNEXPECTED | \n", + "cls.seq_relationship.weight | UNEXPECTED | \n", + "cls.seq_relationship.bias | UNEXPECTED | \n", + "cls.predictions.transform.dense.bias | UNEXPECTED | \n", + "cls.predictions.bias | UNEXPECTED | \n", + "cls.predictions.transform.LayerNorm.weight | UNEXPECTED | \n", + "classifier.weight | MISSING | \n", + "classifier.bias | MISSING | \n", + "\n", + "Notes:\n", + "- UNEXPECTED:\tcan be ignored when loading from different task/architecture; not ok if you expect identical arch.\n", + "- MISSING:\tthose params were newly initialized because missing from the checkpoint. Consider training on your downstream task.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.8325688073394495\n" + ] + } + ], + "source": [ + "test(train(\n", + " \"bert-mini-sst2\",\n", + " \"google/bert_uncased_L-2_H-128_A-2\",\n", + " (1.0, 1.0),\n", + " learning_rate=1e-4,\n", + " num_train_epochs=5,\n", + " per_device_train_batch_size=32,\n", + " output_dir=\"bert-tiny-sst2\"\n", + "))" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "e98d950e", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[transformers] \u001b[1mBertHashForSequenceClassification LOAD REPORT\u001b[0m from: neuml/bert-hash-femto\n", + "Key | Status | \n", + "-------------------------+---------+-\n", + "bert.pooler.dense.bias | MISSING | \n", + "bert.pooler.dense.weight | MISSING | \n", + "classifier.bias | MISSING | \n", + "classifier.weight | MISSING | \n", + "\n", + "Notes:\n", + "- MISSING:\tthose params were newly initialized because missing from the checkpoint. Consider training on your downstream task.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.8084862385321101\n" + ] + } + ], + "source": [ + "test(train(\n", + " \"bert-tiny-sst2\",\n", + " \"neuml/bert-hash-femto\",\n", + " (1.0, 1.0),\n", + " learning_rate=3e-4,\n", + " num_train_epochs=5,\n", + " per_device_train_batch_size=32,\n", + " output_dir=\"bert-femto-sst2\"\n", + "))" + ] + }, + { + "cell_type": "markdown", + "id": "5f1c94e7", + "metadata": {}, + "source": [ + "Let's look at the performance. The 11M parameter model registered an accuracy score of `0.8750` on the SST2 dev set. Then the 4M parameter model scored `0.8326` and finally the tiny femto model scored `0.8085`. As we can see each score got progressively worse but each model has less capability. Let's train a femto model directly to compare." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "b89d8292", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[transformers] \u001b[1mBertHashForSequenceClassification LOAD REPORT\u001b[0m from: neuml/bert-hash-femto\n", + "Key | Status | \n", + "-------------------------+---------+-\n", + "bert.pooler.dense.bias | MISSING | \n", + "bert.pooler.dense.weight | MISSING | \n", + "classifier.bias | MISSING | \n", + "classifier.weight | MISSING | \n", + "\n", + "Notes:\n", + "- MISSING:\tthose params were newly initialized because missing from the checkpoint. Consider training on your downstream task.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.801605504587156\n" + ] + } + ], + "source": [ + "test(train(\n", + " None,\n", + " \"neuml/bert-hash-femto\",\n", + " None,\n", + " learning_rate=3e-4,\n", + " num_train_epochs=5,\n", + " per_device_train_batch_size=32,\n", + " output_dir=\"bert-femto-sst2\"\n", + "))" + ] + }, + { + "cell_type": "markdown", + "id": "4864b8d2", + "metadata": {}, + "source": [ + "This scored `0.8016` a sizable shift down from the progressively distilled model. Now keep in mind the femto model only has `250K` parameters but we gave it a sizable accuracy boost within it's capabilities." + ] + }, + { + "cell_type": "markdown", + "id": "e4e04226", + "metadata": {}, + "source": [ + "# Wrapping up\n", + "\n", + "This example showed how progressive distillation can boost overall model performance, especially for tiny models. Incrementally compressing knowledge into a series of smaller subsets enabled the final model to learn more efficiently than directly training the model on the dataset without distillation. Put progressive distillation in the toolkit when working with tiny models!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "local", + "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", + "version": "3.10.20" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/agent_quickstart.py b/examples/agent_quickstart.py new file mode 100644 index 0000000..5255264 --- /dev/null +++ b/examples/agent_quickstart.py @@ -0,0 +1,80 @@ +""" +Agent Quick Start +Easy to use way to get started with AI Agents. + +TxtAI has many example notebooks covering everything the framework provides +Examples: https://neuml.github.io/txtai/examples + +Install TxtAI + pip install txtai[agent] +""" + +# pylint: disable=C0103 +from datetime import datetime +from txtai import Agent + +# Step 1: Define your Embeddings database +# +# Replace provider/container with a path to a local Embeddings database +# See RAG Quickstart for an example of building your own custom database +embeddings = { + "name": "wikipedia", + "description": "Searches a Wikipedia database", + # "path": "path to your embeddings database" + "provider": "huggingface-hub", + "container": "neuml/txtai-wikipedia", +} + + +# Step 2: Define other tools +# +# Add any Python function. Just need to describe it. +def today() -> str: + """ + Gets the current date and time + + Returns: + current date and time + """ + + return datetime.today().isoformat() + + +# Step 3: Create a list of available tools +# +# Combine defined tools with default tools +tools = [ + embeddings, # Embeddings database with YOUR data + today, # Python function + "websearch", # Runs a websearch using default engine + "webview", # Loads a web page +] + +# Step 4: Set LLM configuration +# +# LLM APIs +# model = "gpt-5.1" +# model = "claude-opus-4-5-20251101" +# model = "gemini/gemini-3-pro-preview" +# +# Local LLMs +# model = "ollama/gpt-oss +# model = "openai/gpt-oss-20b" +# model = "unsloth/gpt-oss-20b-GGUF/gpt-oss-20b-Q4_K_M.gguf" +# +# Pass multiple options as a dictionary +# model = { +# "path": "unsloth/Qwen3-30B-A3B-Instruct-2507-GGUF/Qwen3-30B-A3B-Instruct-2507-Q4_K_M.gguf", +# "n_ctx": 25000 +# } +model = "Qwen/Qwen3-4B-Instruct-2507" + +# Step 4: Create an Agent +# +# Set LLM, tools and other configuration +# See this for more options: https://huggingface.co/docs/smolagents/reference/agents#agents +agent = Agent(model=model, tools=tools, max_steps=10) + +print(agent("Tell me about the Roman Empire")) +print(agent("What is the current date?")) +print(agent("Get the 5 top news stories for today", maxlength=25000)) diff --git a/examples/article.py b/examples/article.py new file mode 100644 index 0000000..b58ced5 --- /dev/null +++ b/examples/article.py @@ -0,0 +1,66 @@ +""" +Application that builds a summary of an article. + +Requires streamlit to be installed. + pip install streamlit +""" + +import os + +import streamlit as st + +from txtai.pipeline import Summary, Textractor +from txtai.workflow import UrlTask, Task, Workflow + + +class Application: + """ + Main application. + """ + + def __init__(self): + """ + Creates a new application. + """ + + textract = Textractor(paragraphs=True, minlength=100, join=True) + summary = Summary("sshleifer/distilbart-cnn-12-6") + + self.workflow = Workflow([UrlTask(textract), Task(summary)]) + + def run(self): + """ + Runs a Streamlit application. + """ + + st.title("Article Summary") + st.markdown("This application builds a summary of an article.") + + url = st.text_input("URL") + if url: + # Run workflow and get summary + summary = list(self.workflow([url]))[0] + + # Write results + st.write(summary) + st.markdown("*Source: " + url + "*") + + +@st.cache(allow_output_mutation=True) +def create(): + """ + Creates and caches a Streamlit application. + + Returns: + Application + """ + + return Application() + + +if __name__ == "__main__": + os.environ["TOKENIZERS_PARALLELISM"] = "false" + + # Create and run application + app = create() + app.run() diff --git a/examples/baseball.py b/examples/baseball.py new file mode 100644 index 0000000..ffb90c5 --- /dev/null +++ b/examples/baseball.py @@ -0,0 +1,712 @@ +""" +Baseball statistics application with txtai and Streamlit. + +Install txtai and streamlit (>= 1.23) to run: + pip install txtai streamlit +""" + +import datetime +import math +import os +import random + +import altair as alt +import numpy as np +import pandas as pd +import streamlit as st + +from txtai import Embeddings + + +class Stats: + """ + Base stats class. Contains methods for loading, indexing and searching baseball stats. + """ + + def __init__(self): + """ + Creates a new Stats instance. + """ + + # Load columns + self.columns = self.loadcolumns() + + # Load stats data + self.stats = self.load() + + # Load names + self.names = self.loadnames() + + # Build index + self.vectors, self.data, self.maxyear, self.embeddings = self.index() + + def loadcolumns(self): + """ + Returns a list of data columns. + + Returns: + list of columns + """ + + raise NotImplementedError + + def load(self): + """ + Loads and returns raw stats. + + Returns: + stats + """ + + raise NotImplementedError + + def metric(self): + """ + Primary metric column. + + Returns: + metric column name + """ + + raise NotImplementedError + + def vector(self, row): + """ + Build a vector for input row. + + Args: + row: input row + + Returns: + row vector + """ + + raise NotImplementedError + + def loadnames(self): + """ + Loads a name - player id dictionary. + + Returns: + {player name: player id} + """ + + # Get unique names + names = {} + rows = self.stats.sort_values(by=self.metric(), ascending=False)[["nameFirst", "nameLast", "playerID"]].drop_duplicates().reset_index() + for x, row in rows.iterrows(): + # Name key + key = f"{row['nameFirst']} {row['nameLast']}" + key += f" ({row['playerID']})" if key in names else "" + + if key not in names: + # Scale scores of top n players + exponent = 2 if ((len(rows) - x) / len(rows)) >= 0.95 else 1 + + # score = num seasons ^ exponent + score = math.pow(len(self.stats[self.stats["playerID"] == row["playerID"]]), exponent) + + # Save name key - values pair + names[key] = (row["playerID"], score) + + return names + + def index(self): + """ + Builds an embeddings index to stats data. Returns vectors, input data and embeddings index. + + Returns: + vectors, data, embeddings + """ + + # Build data dictionary + vectors = {f'{row["yearID"]}{row["playerID"]}': self.transform(row) for _, row in self.stats.iterrows()} + data = {f'{row["yearID"]}{row["playerID"]}': dict(row) for _, row in self.stats.iterrows()} + maxyear = max(row["yearID"] for _, row in self.stats.iterrows()) + + embeddings = Embeddings({"transform": Stats.transform}) + embeddings.index((uid, vectors[uid], None) for uid in vectors) + + return vectors, data, maxyear, embeddings + + def metrics(self, name): + """ + Looks up a player's active years, best statistical year and key metrics. + + Args: + name: player name + + Returns: + active, best, metrics + """ + + if name in self.names: + # Get player stats + stats = self.stats[self.stats["playerID"] == self.names[name][0]] + + # Build key metrics + metrics = stats[["yearID", self.metric()]] + + # Get best year, sort by primary metric + best = int(stats.sort_values(by=self.metric(), ascending=False)["yearID"].iloc[0]) + + # Get years active, best year, along with metric trends + return metrics["yearID"].tolist(), best, metrics + + return range(1871, datetime.datetime.today().year), 1950, None + + def search(self, name=None, year=None, window=None, row=None, limit=10): + """ + Runs an embeddings search. This method takes either a player-year or stats row as input. + + Args: + name: player name to search + year: year to search + window: limit to window recent seasons + row: row of stats to search + limit: max results to return + + Returns: + list of results + """ + + if row: + query = self.vector(row) + else: + # Lookup player key and build vector id + name = self.names.get(name) + query = f"{year}{name[0] if name else name}" + query = self.vectors.get(query) + + results, ids = [], set() + if query is not None: + candidates = limit * 100 if window else limit * 5 + for uid, _ in self.embeddings.search(query, candidates): + # Only add unique players + if uid[4:] not in ids: + result = self.data[uid].copy() + + # Add first player if this is a player comparison. Limit results to window, if necessary + if (not ids and not row) or not window or result["yearID"] > self.maxyear - window: + result["link"] = f'https://www.baseball-reference.com/players/{result["nameLast"].lower()[0]}/{result["bbrefID"]}.shtml' + results.append(result) + ids.add(uid[4:]) + + if len(ids) >= limit: + break + + return results + + def transform(self, row): + """ + Transforms a stats row into a vector. + + Args: + row: stats row + + Returns: + vector + """ + + if isinstance(row, np.ndarray): + return row + + return np.array([0.0 if not row[x] or np.isnan(row[x]) else row[x] for x in self.columns]) + + +class Batting(Stats): + """ + Batting stats. + """ + + def loadcolumns(self): + return [ + "birthMonth", + "yearID", + "age", + "height", + "weight", + "G", + "AB", + "R", + "H", + "1B", + "2B", + "3B", + "HR", + "RBI", + "SB", + "CS", + "BB", + "SO", + "IBB", + "HBP", + "SH", + "SF", + "GIDP", + "POS", + "AVG", + "OBP", + "TB", + "SLG", + "OPS", + "OPS+", + ] + + def load(self): + # Retrieve raw data + players = pd.read_csv("https://hf.co/datasets/neuml/baseballdata/resolve/main/People.csv") + batting = pd.read_csv("https://hf.co/datasets/neuml/baseballdata/resolve/main/Batting.csv") + fielding = pd.read_csv("https://hf.co/datasets/neuml/baseballdata/resolve/main/Fielding.csv") + + # Merge player data in + batting = pd.merge(players, batting, how="inner", on=["playerID"]) + + # Require player to have at least 350 plate appearances. + batting = batting[((batting["AB"] + batting["BB"]) >= 350) & (batting["stint"] == 1)] + + # Derive primary player positions + positions = self.positions(fielding) + + # Calculated columns + batting["age"] = batting["yearID"] - batting["birthYear"] + batting["POS"] = batting.apply(lambda row: self.position(positions, row), axis=1) + batting["AVG"] = batting["H"] / batting["AB"] + batting["OBP"] = (batting["H"] + batting["BB"]) / (batting["AB"] + batting["BB"]) + batting["1B"] = batting["H"] - batting["2B"] - batting["3B"] - batting["HR"] + batting["TB"] = batting["1B"] + 2 * batting["2B"] + 3 * batting["3B"] + 4 * batting["HR"] + batting["SLG"] = batting["TB"] / batting["AB"] + batting["OPS"] = batting["OBP"] + batting["SLG"] + batting["OPS+"] = 100 + (batting["OPS"] - batting["OPS"].mean()) * 100 + + return batting + + def metric(self): + return "OPS+" + + def vector(self, row): + row["TB"] = row["1B"] + 2 * row["2B"] + 3 * row["3B"] + 4 * row["HR"] + row["AVG"] = row["H"] / row["AB"] + row["OBP"] = (row["H"] + row["BB"]) / (row["AB"] + row["BB"]) + row["SLG"] = row["TB"] / row["AB"] + row["OPS"] = row["OBP"] + row["SLG"] + row["OPS+"] = 100 + (row["OPS"] - self.stats["OPS"].mean()) * 100 + + return self.transform(row) + + def positions(self, fielding): + """ + Derives primary positions for players. + + Args: + fielding: fielding data + + Returns: + {player id: (position, number of games)} + """ + + positions = {} + for _, row in fielding.iterrows(): + uid = f'{row["yearID"]}{row["playerID"]}' + position = row["POS"] if row["POS"] else 0 + if position == "P": + position = 1 + elif position == "C": + position = 2 + elif position == "1B": + position = 3 + elif position == "2B": + position = 4 + elif position == "3B": + position = 5 + elif position == "SS": + position = 6 + elif position == "OF": + position = 7 + + # Save position if not set or player played more at this position + if uid not in positions or positions[uid][1] < row["G"]: + positions[uid] = (position, row["G"]) + + return positions + + def position(self, positions, row): + """ + Looks up primary position for player row. + + Arg: + positions: all player positions + row: player row + + Returns: + primary player positions + """ + + uid = f'{row["yearID"]}{row["playerID"]}' + return positions[uid][0] if uid in positions else 0 + + +class Pitching(Stats): + """ + Pitching stats. + """ + + def loadcolumns(self): + return [ + "birthMonth", + "yearID", + "age", + "height", + "weight", + "W", + "L", + "G", + "GS", + "CG", + "SHO", + "SV", + "IPouts", + "H", + "ER", + "HR", + "BB", + "SO", + "BAOpp", + "ERA", + "IBB", + "WP", + "HBP", + "BK", + "BFP", + "GF", + "R", + "SH", + "SF", + "GIDP", + "WHIP", + "WADJ", + ] + + def load(self): + # Retrieve raw data + players = pd.read_csv("https://hf.co/datasets/neuml/baseballdata/resolve/main/People.csv") + pitching = pd.read_csv("https://hf.co/datasets/neuml/baseballdata/resolve/main/Pitching.csv") + + # Merge player data in + pitching = pd.merge(players, pitching, how="inner", on=["playerID"]) + + # Require player to have 20 appearances + pitching = pitching[(pitching["G"] >= 20) & (pitching["stint"] == 1)] + + # Calculated columns + pitching["age"] = pitching["yearID"] - pitching["birthYear"] + pitching["WHIP"] = (pitching["BB"] + pitching["H"]) / (pitching["IPouts"] / 3) + pitching["WADJ"] = (pitching["W"] + pitching["SV"]) / (pitching["ERA"] + pitching["WHIP"]) + + return pitching + + def metric(self): + return "WADJ" + + def vector(self, row): + row["WHIP"] = (row["BB"] + row["H"]) / (row["IPouts"] / 3) if row["IPouts"] else None + row["WADJ"] = (row["W"] + row["SV"]) / (row["ERA"] + row["WHIP"]) if row["ERA"] and row["WHIP"] else None + + return self.transform(row) + + +class Application: + """ + Main application. + """ + + def __init__(self): + """ + Creates a new application. + """ + + # Batting stats + self.batting = Batting() + + # Pitching stats + self.pitching = Pitching() + + def run(self): + """ + Runs a Streamlit application. + """ + + st.set_page_config(layout="wide", page_title="Baseball Stats", page_icon="⚾") + st.title("⚾ Baseball Stats") + st.markdown( + """ + This application finds the best matching players using vector search with [txtai](https://github.com/neuml/txtai). + Raw data is from the [Lahman Baseball Database](https://sabr.org/lahman-database/). Read [this + article](https://medium.com/neuml/explore-baseball-history-with-vector-search-5778d98d6846) for more details. + """ + ) + + player, search = st.tabs(["Player", "Search"]) + + # Player tab + with player: + self.player() + + # Search + with search: + self.search() + + def player(self): + """ + Player tab. + """ + + st.markdown("Match by player-season. Each player search defaults to the best season sorted by OPS or Wins Adjusted.") + + # Get parameters + params = self.params() + + # Category and stats + category = self.category(params.get("category"), "category") + stats = self.batting if category == "Batting" else self.pitching + + # Player name + name = self.name(stats.names, params.get("name")) + + # Limit player-year comparisons using this window + window = self.window(params.get("window"), "window") + + # Player metrics + active, best, metrics = stats.metrics(name) + + # Player year + year = self.year(active, params.get("year"), best) + + # Display metrics chart + if len(active) > 1: + self.chart(category, metrics) + + # Run search + results = stats.search(name, year, window) + + # Display results + self.table(results, ["link", "nameFirst", "nameLast", "teamID"] + stats.columns[1:]) + + # Save query parameters + st.query_params.clear() + for key, value in [("category", category), ("name", name), ("window", window), ("year", year)]: + if value: + st.query_params[key] = value + + def search(self): + """ + Stats search tab. + """ + + st.markdown("Find players with similar statistics.") + + stats, category = None, self.category("Batting", "searchcategory") + + # Limit player-year comparisons using this window + window = self.window(None, "searchwindow") + + with st.form("search"): + if category == "Batting": + stats, columns = self.batting, self.batting.columns[:-6] + elif category == "Pitching": + stats, columns = self.pitching, self.pitching.columns[:-2] + + # Enter stats with data editor + inputs = st.data_editor(pd.DataFrame([dict((column, None) for column in columns)]), hide_index=True).astype(float) + + submitted = st.form_submit_button("Search") + if submitted: + # Run search + results = stats.search(window=window, row=inputs.to_dict(orient="records")[0]) + + # Display table + self.table(results, ["link", "nameFirst", "nameLast", "teamID"] + stats.columns[1:]) + + def params(self): + """ + Get application parameters. This method combines URL parameters with session parameters. + + Returns: + parameters + """ + + # Get query parameters + params = {x: st.query_params.get(x) for x in ["category", "name", "window", "year"]} + + # Sync parameters with session state + if all(x in st.session_state for x in ["category", "name", "year"]): + # Copy session year if category and name are unchanged + params["year"] = str(st.session_state["year"]) if all(params.get(x) == st.session_state[x] for x in ["category", "name"]) else None + + # Copy category, name and window from session state + params["category"] = st.session_state["category"] + params["name"] = st.session_state["name"] + params["window"] = st.session_state["window"] + + return params + + def category(self, category, key): + """ + Builds category input widget. + + Args: + category: category parameter + key: widget key + + Returns: + category component + """ + + # List of stat categories + categories = ["Batting", "Pitching"] + + # Get category parameter, default if not available or valid + default = categories.index(category) if category and category in categories else 0 + + # Radio box component + return st.radio("Stat", categories, index=default, horizontal=True, key=key) + + def window(self, window, key): + """ + Limit results to last N seasons. + + Args: + window: limit to window seasons + key: widget key + + Returns: + window component + """ + + # Get window size + window = st.text_input("Limit to last N seasons", value=window, key=key) + + # Clear invalid input + if window and (not window.isnumeric() or int(window) < 1): + st.error("Window must be a number greater or equal to 1") + return None + + # Convert to int + return int(window) if window else window + + def name(self, names, name): + """ + Builds name input widget. + + Args: + names: list of all allowable names + + Returns: + name component + """ + + # Get name parameter, default to random weighted value if not valid + name = name if name and name in names else random.choices(list(names.keys()), weights=[names[x][1] for x in names])[0] + + # Sort names for display + names = sorted(names) + + # Select box component + return st.selectbox("Name", names, names.index(name), key="name") + + def year(self, years, year, best): + """ + Builds year input widget. + + Args: + years: active years for a player + year: year parameter + best: default to best year if year is invalid + + Returns: + year component + """ + + # Get year parameter, default if not available or valid + year = int(year) if year and year.isdigit() and int(year) in years else best + + # Slider component + return int(st.select_slider("Year", years, year, key="year") if len(years) > 1 else years[0]) + + def chart(self, category, metrics): + """ + Displays a metric chart. + + Args: + category: Batting or Pitching + metrics: player metrics to plot + """ + + # Key metric + metric = self.batting.metric() if category == "Batting" else self.pitching.metric() + + # Cast year to string + metrics["yearID"] = metrics["yearID"].astype(str) + + # Metric over years + chart = ( + alt.Chart(metrics) + .mark_line(interpolate="monotone", point=True, strokeWidth=2.5, opacity=0.75) + .encode(x=alt.X("yearID", title=""), y=alt.Y(metric, scale=alt.Scale(zero=False))) + ) + + # Create metric median rule line + rule = alt.Chart(metrics).mark_rule(color="gray", strokeDash=[3, 5], opacity=0.5).encode(y=f"median({metric})") + + # Layered chart configuration + chart = (chart + rule).encode(y=alt.Y(title=metric)).properties(height=200).configure_axis(grid=False) + + # Draw chart + st.altair_chart(chart + rule, theme="streamlit", width="stretch") + + def table(self, results, columns): + """ + Displays a list of results as a table. + + Args: + results: list of results + columns: column names + """ + + if results: + st.dataframe( + results, + column_order=columns, + column_config={ + "link": st.column_config.LinkColumn("Link", width="small", display_text=":material/open_in_new:"), + "yearID": st.column_config.NumberColumn("Year", format="%d"), + "nameFirst": "First", + "nameLast": "Last", + "teamID": "Team", + "age": "Age", + "weight": "Weight", + "height": "Height", + }, + ) + else: + st.write("Player-Year not found") + + +@st.cache_resource(show_spinner=False) +def create(): + """ + Creates and caches a Streamlit application. + + Returns: + Application + """ + + return Application() + + +if __name__ == "__main__": + os.environ["TOKENIZERS_PARALLELISM"] = "false" + + # Create and run application + app = create() + app.run() diff --git a/examples/benchmarks.py b/examples/benchmarks.py new file mode 100644 index 0000000..749ef42 --- /dev/null +++ b/examples/benchmarks.py @@ -0,0 +1,731 @@ +""" +Runs benchmark evaluations with the BEIR dataset. + +Install txtai and the following dependencies to run: + pip install txtai pytrec_eval rank-bm25 bm25s elasticsearch psutil +""" + +import argparse +import csv +import json +import os +import pickle +import sqlite3 +import time + +import psutil +import yaml + +import numpy as np + +from bm25s import BM25 as BM25Sparse +from elasticsearch import Elasticsearch +from elasticsearch.helpers import bulk +from pytrec_eval import RelevanceEvaluator +from rank_bm25 import BM25Okapi +from tqdm.auto import tqdm + +from txtai.embeddings import Embeddings +from txtai.pipeline import LLM, RAG, Similarity, Tokenizer +from txtai.scoring import ScoringFactory + + +class Index: + """ + Base index definition. Defines methods to index and search a dataset. + """ + + def __init__(self, path, config, output, refresh): + """ + Creates a new index. + + Args: + path: path to dataset + config: path to config file + output: path to store index + refresh: overwrites existing index if True, otherwise existing index is loaded + """ + + self.path = path + self.config = config + self.output = output + self.refresh = refresh + + # Build and save index + self.backend = self.index() + + def __call__(self, limit, filterscores=True): + """ + Main evaluation logic. Loads an index, runs the dataset queries and returns the results. + + Args: + limit: maximum results + filterscores: if exact matches should be filtered out + + Returns: + search results + """ + + uids, queries = self.load() + + # Run queries in batches + offset, results = 0, {} + for batch in self.batch(queries, 256): + for i, r in enumerate(self.search(batch, limit + 1)): + # Get result as list of (id, score) tuples + r = list(r) + r = [(x["id"], x["score"]) for x in r] if r and isinstance(r[0], dict) else r + + if filterscores: + r = [(uid, score) for uid, score in r if uid != uids[offset + i]][:limit] + + results[uids[offset + i]] = dict(r) + + # Increment offset + offset += len(batch) + + return results + + def search(self, queries, limit): + """ + Runs a search for a set of queries. + + Args: + queries: list of queries to run + limit: maximum results + + Returns: + search results + """ + + return self.backend.batchsearch(queries, limit) + + def index(self): + """ + Indexes a dataset. + """ + + raise NotImplementedError + + def rows(self): + """ + Iterates over the dataset yielding a row at a time for indexing. + """ + + # Data file + path = f"{self.path}/corpus.jsonl" + + # Get total count + with open(path, encoding="utf-8") as f: + total = sum(1 for _ in f) + + # Yield data + with open(path, encoding="utf-8") as f: + for line in tqdm(f, total=total): + row = json.loads(line) + text = f'{row["title"]}. {row["text"]}' if row.get("title") else row["text"] + if text: + yield (row["_id"], text, None) + + def load(self): + """ + Loads queries for the dataset. Returns a list of expected result ids and input queries. + + Returns: + (result ids, input queries) + """ + + with open(f"{self.path}/queries.jsonl", encoding="utf-8") as f: + data = [json.loads(query) for query in f] + uids, queries = [x["_id"] for x in data], [x["text"] for x in data] + + return uids, queries + + def batch(self, data, size): + """ + Splits data into equal sized batches. + + Args: + data: input data + size: batch size + + Returns: + data split into equal size batches + """ + + return [data[x : x + size] for x in range(0, len(data), size)] + + def readconfig(self, key, default): + """ + Reads configuration from a config file. Returns default configuration + if config file is not found or config key isn't present. + + Args: + key: configuration key to lookup + default: default configuration + + Returns: + config if found, otherwise returns default config + """ + + if self.config and os.path.exists(self.config): + # Read configuration + with open(self.config, "r", encoding="utf-8") as f: + # Check for config + config = yaml.safe_load(f) + if key in config: + return config[key] + + return default + + +class Embed(Index): + """ + Embeddings index using txtai. + """ + + def index(self): + if os.path.exists(self.output) and not self.refresh: + embeddings = Embeddings() + embeddings.load(self.output) + else: + # Read configuration + config = self.readconfig("embeddings", {"batch": 8192, "encodebatch": 128, "faiss": {"quantize": True, "sample": 0.05}}) + + # Build index + embeddings = Embeddings(config) + embeddings.index(self.rows()) + embeddings.save(self.output) + + return embeddings + + +class Hybrid(Index): + """ + Hybrid embeddings + BM25 index using txtai. + """ + + def index(self): + if os.path.exists(self.output) and not self.refresh: + embeddings = Embeddings() + embeddings.load(self.output) + else: + # Read configuration + config = self.readconfig( + "hybrid", + { + "batch": 8192, + "encodebatch": 128, + "faiss": {"quantize": True, "sample": 0.05}, + "scoring": {"method": "bm25", "terms": True, "normalize": True}, + }, + ) + + # Build index + embeddings = Embeddings(config) + embeddings.index(self.rows()) + embeddings.save(self.output) + + return embeddings + + +class RetrievalAugmentedGeneration(Embed): + """ + Retrieval augmented generation (RAG) using txtai. + """ + + def __init__(self, path, config, output, refresh): + # Parent logic + super().__init__(path, config, output, refresh) + + # Read LLM configuration + llm = self.readconfig("llm", {}) + + # Read RAG configuration + rag = self.readconfig("rag", {}) + + # Load RAG pipeline + self.rag = RAG(self.backend, LLM(**llm), output="reference", **rag) + + def search(self, queries, limit): + # Set context window size to limit and run + self.rag.context = limit + return [[(x["reference"], 1)] for x in self.rag(queries, maxlength=4096)] + + +class Score(Index): + """ + BM25 index using txtai. + """ + + def index(self): + # Read configuration + config = self.readconfig("scoring", {"method": "bm25", "terms": True}) + + # Create scoring instance + scoring = ScoringFactory.create(config) + + output = os.path.join(self.output, "scoring") + if os.path.exists(output) and not self.refresh: + scoring.load(output) + else: + scoring.index(self.rows()) + scoring.save(output) + + return scoring + + +class Similar(Index): + """ + Search data using a similarity pipeline. + """ + + def index(self): + # Load similarity pipeline + model = Similarity(**self.readconfig("similar", {})) + + # Get datasets + data = list(self.rows()) + ids = [x[0] for x in data] + texts = [x[1] for x in data] + + # Encode texts + data = model.encode(texts, "data") + + return (ids, data, model) + + def search(self, queries, limit): + # Unpack backend + ids, data, model = self.backend + + # Run model inference + results = [] + for result in model(queries, data, limit=limit): + results.append([(ids[x], score) for x, score in result]) + + return results + + +class Rerank(Embed): + """ + Embeddings index using txtai combined with a similarity pipeline + """ + + def index(self): + # Build embeddings index + embeddings = super().index() + + # Combine similar pipeline with embeddings + model = Similar(self.path, self.config, self.output, self.refresh) + return model.index() + (embeddings,) + + def search(self, queries, limit): + # Unpack backend + ids, data, model, embeddings = self.backend + + # Run initial query + indices = [] + for r in embeddings.batchsearch(queries, limit * 10): + indices.append({x: ids.index(uid) for x, (uid, _) in enumerate(r)}) + + # Run model inference + results = [] + for x, query in enumerate(queries): + queue = data[list(indices[x].values())] + if len(queue) > 0: + result = model(query, queue, limit=limit) + results.append([(ids[indices[x][i]], score) for i, score in result]) + + return results + + +class RankBM25(Index): + """ + BM25 index using rank-bm25. + """ + + def search(self, queries, limit): + ids, backend = self.backend + tokenizer, results = Tokenizer(), [] + for query in queries: + scores = backend.get_scores(tokenizer(query)) + topn = np.argsort(scores)[::-1][:limit] + results.append([(ids[x], scores[x]) for x in topn]) + + return results + + def index(self): + output = os.path.join(self.output, "rank") + if os.path.exists(output) and not self.refresh: + with open(output, "rb") as f: + ids, model = pickle.load(f) + else: + # Tokenize data + tokenizer, data = Tokenizer(), [] + for uid, text, _ in self.rows(): + data.append((uid, tokenizer(text))) + + ids = [uid for uid, _ in data] + model = BM25Okapi([text for _, text in data]) + + # Save model + with open(output, "wb") as out: + pickle.dump(model, out) + + return ids, model + + +class BM25S(Index): + """ + BM25 as implemented by bm25s + """ + + def __init__(self, path, config, output, refresh): + # Corpus ids + self.ids = None + + # Parent logic + super().__init__(path, config, output, refresh) + + def search(self, queries, limit): + tokenizer = Tokenizer() + results, scores = self.backend.retrieve([tokenizer(x) for x in queries], corpus=self.ids, k=limit) + + # List of queries => list of matches (id, score) + x = [] + for a, b in zip(results, scores): + x.append([(str(c), float(d)) for c, d in zip(a, b)]) + + return x + + def index(self): + tokenizer = Tokenizer() + ids, texts = [], [] + + for uid, text, _ in self.rows(): + ids.append(uid) + texts.append(text) + + self.ids = ids + + if os.path.exists(self.output) and not self.refresh: + model = BM25Sparse.load(self.output) + else: + model = BM25Sparse(method="lucene", k1=1.2, b=0.75) + model.index([tokenizer(x) for x in texts], leave_progress=False) + model.save(self.output) + + return model + + +class SQLiteFTS(Index): + """ + BM25 index using SQLite's FTS extension. + """ + + def search(self, queries, limit): + tokenizer, results = Tokenizer(), [] + for query in queries: + query = tokenizer(query) + query = " OR ".join([f'"{q}"' for q in query]) + + self.backend.execute( + f"SELECT id, bm25(textindex) * -1 score FROM textindex WHERE text MATCH ? ORDER BY bm25(textindex) LIMIT {limit}", [query] + ) + + results.append(list(self.backend)) + + return results + + def index(self): + if os.path.exists(self.output) and not self.refresh: + # Load existing database + connection = sqlite3.connect(self.output) + else: + # Delete existing database + if os.path.exists(self.output): + os.remove(self.output) + + # Create new database + connection = sqlite3.connect(self.output) + + # Tokenize data + tokenizer, data = Tokenizer(), [] + for uid, text, _ in self.rows(): + data.append((uid, " ".join(tokenizer(text)))) + + # Create table + connection.execute("CREATE VIRTUAL TABLE textindex using fts5(id, text)") + + # Load data and build index + connection.executemany("INSERT INTO textindex VALUES (?, ?)", data) + + connection.commit() + + return connection.cursor() + + +class Elastic(Index): + """ + BM25 index using Elasticsearch. + """ + + def search(self, queries, limit): + # Generate bulk queries + request = [] + for query in queries: + req_head = {"index": "textindex", "search_type": "dfs_query_then_fetch"} + req_body = { + "_source": False, + "query": {"multi_match": {"query": query, "type": "best_fields", "fields": ["text"], "tie_breaker": 0.5}}, + "size": limit, + } + request.extend([req_head, req_body]) + + # Run ES query + response = self.backend.msearch(body=request, request_timeout=600) + + # Read responses + results = [] + for resp in response["responses"]: + result = resp["hits"]["hits"] + results.append([(r["_id"], r["_score"]) for r in result]) + + return results + + def index(self): + es = Elasticsearch("http://localhost:9200") + + # Delete existing index + # pylint: disable=W0702 + try: + es.indices.delete(index="textindex") + except: + pass + + bulk(es, ({"_index": "textindex", "_id": uid, "text": text} for uid, text, _ in self.rows())) + es.indices.refresh(index="textindex") + + return es + + +def relevance(path): + """ + Loads relevance data for evaluation. + + Args: + path: path to dataset test file + + Returns: + relevance data + """ + + rel = {} + with open(f"{path}/qrels/test.tsv", encoding="utf-8") as f: + reader = csv.reader(f, delimiter="\t", quoting=csv.QUOTE_MINIMAL) + next(reader) + + for row in reader: + queryid, corpusid, score = row[0], row[1], int(row[2]) + if queryid not in rel: + rel[queryid] = {corpusid: score} + else: + rel[queryid][corpusid] = score + + return rel + + +def create(method, path, config, output, refresh): + """ + Creates a new index. + + Args: + method: indexing method + path: path to dataset + config: path to config file + output: path to store index + refresh: overwrites existing index if True, otherwise existing index is loaded + + Returns: + Index + """ + + if method == "hybrid": + return Hybrid(path, config, output, refresh) + if method == "rag": + return RetrievalAugmentedGeneration(path, config, output, refresh) + if method == "scoring": + return Score(path, config, output, refresh) + if method == "rank": + return RankBM25(path, config, output, refresh) + if method == "bm25s": + return BM25S(path, config, output, refresh) + if method == "sqlite": + return SQLiteFTS(path, config, output, refresh) + if method == "es": + return Elastic(path, config, output, refresh) + if method == "similar": + return Similar(path, config, output, refresh) + if method == "rerank": + return Rerank(path, config, output, refresh) + + # Default + return Embed(path, config, output, refresh) + + +def compute(results): + """ + Computes metrics using the results from an evaluation run. + + Args: + results: evaluation results + + Returns: + metrics + """ + + metrics = {} + for r in results: + for metric in results[r]: + if metric not in metrics: + metrics[metric] = [] + + metrics[metric].append(results[r][metric]) + + return {metric: round(np.mean(values), 5) for metric, values in metrics.items()} + + +def evaluate(methods, path, args): + """ + Runs an evaluation. + + Args: + methods: list of indexing methods to test + path: path to dataset + args: command line arguments + + Returns: + {calculated performance metrics} + """ + + print(f"------ {os.path.basename(path)} ------") + + # Performance stats + performance = {} + + # Calculate stats for each model type + topk = args.topk + evaluator = RelevanceEvaluator(relevance(path), {f"ndcg_cut.{topk}", f"map_cut.{topk}", f"recall.{topk}", f"P.{topk}"}) + for method in methods: + # Stats for this source + stats = {} + performance[method] = stats + + # Create index and get results + start = time.time() + output = args.output if args.output else f"{path}/{method}" + index = create(method, path, args.config, output, args.refresh) + + # Add indexing metrics + stats["index"] = round(time.time() - start, 2) + stats["memory"] = int(psutil.Process().memory_info().rss / (1024 * 1024)) + stats["disk"] = int(sum(d.stat().st_size for d in os.scandir(output) if d.is_file()) / 1024) if os.path.isdir(output) else 0 + + print("INDEX TIME =", time.time() - start) + print(f"MEMORY USAGE = {stats['memory']} MB") + print(f"DISK USAGE = {stats['disk']} KB") + + start = time.time() + results = index(topk) + + # Add search metrics + stats["search"] = round(time.time() - start, 2) + print("SEARCH TIME =", time.time() - start) + + # Calculate stats + metrics = compute(evaluator.evaluate(results)) + + # Add accuracy metrics + for stat in [f"ndcg_cut_{topk}", f"map_cut_{topk}", f"recall_{topk}", f"P_{topk}"]: + stats[stat] = metrics[stat] + + # Print model stats + print(f"------ {method} ------") + print(f"NDCG@{topk} =", metrics[f"ndcg_cut_{topk}"]) + print(f"MAP@{topk} =", metrics[f"map_cut_{topk}"]) + print(f"Recall@{topk} =", metrics[f"recall_{topk}"]) + print(f"P@{topk} =", metrics[f"P_{topk}"]) + + print() + return performance + + +def benchmarks(args): + """ + Main benchmark execution method. + + Args: + args: command line arguments + """ + + # Directory where BEIR datasets are stored + directory = args.directory if args.directory else "beir" + + if args.sources and args.methods: + sources, methods = args.sources.split(","), args.methods.split(",") + mode = "a" + else: + # Default sources and methods + sources = [ + "trec-covid", + "nfcorpus", + "nq", + "hotpotqa", + "fiqa", + "arguana", + "webis-touche2020", + "quora", + "dbpedia-entity", + "scidocs", + "fever", + "climate-fever", + "scifact", + ] + methods = ["embed", "hybrid", "rag", "scoring", "rank", "bm25s", "sqlite", "es", "similar", "rerank"] + mode = "w" + + # Run and save benchmarks + with open("benchmarks.json", mode, encoding="utf-8") as f: + for source in sources: + # Run evaluations + results = evaluate(methods, f"{directory}/{source}", args) + + # Save as JSON lines output + for method, stats in results.items(): + stats["source"] = source + stats["method"] = method + stats["name"] = args.name if args.name else method + + json.dump(stats, f) + f.write("\n") + + +if __name__ == "__main__": + # Command line parser + parser = argparse.ArgumentParser(description="Benchmarks") + parser.add_argument("-c", "--config", help="path to config file", metavar="CONFIG") + parser.add_argument("-d", "--directory", help="root directory path with datasets", metavar="DIRECTORY") + parser.add_argument("-m", "--methods", help="comma separated list of methods", metavar="METHODS") + parser.add_argument("-n", "--name", help="name to assign to this run, defaults to method name", metavar="NAME") + parser.add_argument("-o", "--output", help="index output directory path", metavar="OUTPUT") + parser.add_argument( + "-r", + "--refresh", + help="refreshes index if set, otherwise uses existing index if available", + action="store_true", + ) + parser.add_argument("-s", "--sources", help="comma separated list of data sources", metavar="SOURCES") + parser.add_argument("-t", "--topk", help="top k results to use for the evaluation", metavar="TOPK", type=int, default=10) + + # Calculate benchmarks + benchmarks(parser.parse_args()) diff --git a/examples/books.py b/examples/books.py new file mode 100644 index 0000000..895f3eb --- /dev/null +++ b/examples/books.py @@ -0,0 +1,186 @@ +""" +Search application using Open Library book data. Requires the following steps to be run: + +Install Streamlit + pip install streamlit + +Download and prepare data + mkdir openlibrary && cd openlibrary + wget -O works.txt.gz https://openlibrary.org/data/ol_dump_works_latest.txt.gz + gunzip works.txt.gz + grep "\"description\":" works.txt > filtered.txt + +Build index + python books.py openlibrary + +Run application + streamlit run books.py openlibrary +""" + +import json +import os +import sqlite3 +import sys + +import pandas as pd +import streamlit as st + +from txtai.embeddings import Embeddings + + +class Application: + """ + Main application. + """ + + def __init__(self, path): + """ + Creates a new application. + + Args: + path: root path to data + """ + + self.path = path + self.dbpath = os.path.join(self.path, "books") + + def rows(self, index): + """ + Iterates over dataset yielding each row. + + Args: + index: yields rows for embeddings indexing if True, otherwise yields database rows + """ + + with open(os.path.join(self.path, "filtered.txt"), encoding="utf-8") as infile: + for x, row in enumerate(infile): + if x % 1000 == 0: + print(f"Processed {x} rows", end="\r") + + row = row.split("\t") + uid, data = row[1], json.loads(row[4]) + + description = data["description"] + if isinstance(description, dict): + description = description["value"] + + if "title" in data: + if index: + yield (uid, data["title"] + ". " + description, None) + else: + cover = f"{data['covers'][0]}" if "covers" in data and data["covers"] else None + yield (uid, data["title"], description, cover) + + def database(self): + """ + Builds a SQLite database. + """ + + # Database file path + dbfile = os.path.join(self.dbpath, "books.sqlite") + + # Delete existing file + if os.path.exists(dbfile): + os.remove(dbfile) + + # Create output database + db = sqlite3.connect(dbfile) + + # Create database cursor + cur = db.cursor() + + cur.execute("CREATE TABLE books (Id TEXT PRIMARY KEY, Title TEXT, Description TEXT, Cover TEXT)") + + for uid, title, description, cover in self.rows(False): + cur.execute("INSERT INTO books (Id, Title, Description, Cover) VALUES (?, ?, ?, ?)", (uid, title, description, cover)) + + # Finish and close database + db.commit() + db.close() + + def build(self): + """ + Builds an embeddings index and database. + """ + + # Build embeddings index + embeddings = Embeddings({"path": "sentence-transformers/msmarco-distilbert-base-v4"}) + embeddings.index(self.rows(True)) + embeddings.save(self.dbpath) + + # Build SQLite DB + self.database() + + @st.cache(allow_output_mutation=True) + def load(self): + """ + Loads and caches embeddings index. + + Returns: + embeddings index + """ + + embeddings = Embeddings() + embeddings.load(self.dbpath) + + return embeddings + + def run(self): + """ + Runs a Streamlit application. + """ + + # Build embeddings index + embeddings = self.load() + + db = sqlite3.connect(os.path.join(self.dbpath, "books.sqlite")) + cur = db.cursor() + + st.title("Book search") + + st.markdown( + "This application builds a local txtai index using book data from [openlibrary.org](https://openlibrary.org). " + + "Links to the Open Library pages and covers are shown in the application." + ) + + query = st.text_input("Search query:") + if query: + ids = [uid for uid, score in embeddings.search(query, 10) if score >= 0.5] + + results = [] + for uid in ids: + cur.execute("SELECT Title, Description, Cover FROM books WHERE Id=?", (uid,)) + result = cur.fetchone() + + if result: + # Build cover image + cover = ( + f"" + if result[2] + else "" + ) + + # Append book link + cover = f"{cover}" + title = f"{result[0]}" + + results.append({"Cover": cover, "Title": title, "Description": result[1]}) + + st.write(pd.DataFrame(results).to_html(escape=False, index=False), unsafe_allow_html=True) + + db.close() + + +if __name__ == "__main__": + os.environ["TOKENIZERS_PARALLELISM"] = "false" + + # Application is used both to index and search + app = Application(sys.argv[1]) + + # pylint: disable=W0212 + if st._is_running_with_streamlit: + # Run application using existing index/db + app.run() + else: + # Not running through streamlit, build database/index + app.build() diff --git a/examples/images.py b/examples/images.py new file mode 100644 index 0000000..213e995 --- /dev/null +++ b/examples/images.py @@ -0,0 +1,105 @@ +""" +Builds a similarity index for a directory of images + +Requires streamlit to be installed. + pip install streamlit +""" + +import glob +import os +import sys + +import streamlit as st + +from PIL import Image + +from txtai.embeddings import Embeddings + + +class Application: + """ + Main application + """ + + def __init__(self, directory): + """ + Creates a new application. + + Args: + directory: directory of images + """ + + self.embeddings = self.build(directory) + + def build(self, directory): + """ + Builds an image embeddings index. + + Args: + directory: directory with images + + Returns: + Embeddings index + """ + + embeddings = Embeddings({"method": "sentence-transformers", "path": "clip-ViT-B-32"}) + embeddings.index(self.images(directory)) + + # Update model to support multilingual queries + embeddings.config["path"] = "sentence-transformers/clip-ViT-B-32-multilingual-v1" + embeddings.model = embeddings.loadvectors() + + return embeddings + + def images(self, directory): + """ + Generator that loops over each image in a directory. + + Args: + directory: directory with images + """ + + for path in glob.glob(directory + "/*jpg") + glob.glob(directory + "/*png"): + yield (path, Image.open(path), None) + + def run(self): + """ + Runs a Streamlit application. + """ + + st.title("Image search") + + st.markdown("This application shows how images and text can be embedded into the same space to support similarity search. ") + st.markdown( + "[sentence-transformers](https://github.com/UKPLab/sentence-transformers/tree/master/examples/applications/image-search) " + + "recently added support for the [OpenAI CLIP model](https://github.com/openai/CLIP). This model embeds text and images into " + + "the same space, enabling image similarity search. txtai can directly utilize these models." + ) + + query = st.text_input("Search query:") + if query: + index, _ = self.embeddings.search(query, 1)[0] + st.image(Image.open(index)) + + +@st.cache(allow_output_mutation=True) +def create(directory): + """ + Creates and caches a Streamlit application. + + Args: + directory: directory of images to index + + Returns: + Application + """ + + return Application(directory) + + +if __name__ == "__main__": + os.environ["TOKENIZERS_PARALLELISM"] = "false" + + # Create and run application + app = create(sys.argv[1]) + app.run() diff --git a/examples/rag_quickstart.py b/examples/rag_quickstart.py new file mode 100644 index 0000000..9db9603 --- /dev/null +++ b/examples/rag_quickstart.py @@ -0,0 +1,70 @@ +""" +RAG Quick Start +Easy to use way to get started with RAG using YOUR data + +For a complete application see this: https://github.com/neuml/rag + +TxtAI has many example notebooks covering everything the framework provides +Examples: https://neuml.github.io/txtai/examples + +Install TxtAI + pip install txtai[pipeline-data] +""" + +# pylint: disable=C0103 +import os + +from txtai import Embeddings, RAG +from txtai.pipeline import Textractor + +# Step 1: Collect files from local directory +# +# Defaults to "data". Set to whereever your files are. +path = "data" +files = [os.path.join(path, f) for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))] + +# Step 2: Text Extraction / Chunking +# +# Using section based chunking here. More complex options available such as semantic chunking, iterative chunking etc. +# Documentation: https://neuml.github.io/txtai/pipeline/data/textractor +# Supports Chonkie chunking as well: https://docs.chonkie.ai/oss/chunkers/overview +textractor = Textractor(backend="docling", sections=True) +chunks = [] +for f in files: + for chunk in textractor(f): + chunks.append((f, chunk)) + +# Step 3: Build an embeddings database +# +# The `path` parameter sets the vector embeddings model. Supports Hugging Face models, llama.cpp, Ollama, vLLM and more. +# Documentation: https://neuml.github.io/txtai/embeddings/ +embeddings = Embeddings(content=True, path="Qwen/Qwen3-Embedding-0.6B", maxlength=2048) +embeddings.index(chunks) + +# Step 4: Create RAG pipeline +# +# Combines an embeddings database and an LLM. +# Supports Hugging Face models, llama.cpp, Ollama, vLLM and more +# Documentation: https://neuml.github.io/txtai/pipeline/text/rag + +# User prompt template +template = """ + Answer the following question using the provided context. + + Question: + {question} + + Context: + {context} +""" + +rag = RAG( + embeddings, + "Qwen/Qwen3-0.6B", + system="You are a friendly assistant", + template=template, + output="flatten", +) + +question = "Summarize the main advancements made by BERT" +print(rag(question, maxlength=2048, stripthink=True)) diff --git a/examples/similarity.py b/examples/similarity.py new file mode 100644 index 0000000..530e348 --- /dev/null +++ b/examples/similarity.py @@ -0,0 +1,73 @@ +""" +Basic similarity search example. Used in the original txtai demo. + +Requires streamlit to be installed. + pip install streamlit +""" + +import os + +import streamlit as st + +from txtai.embeddings import Embeddings + + +class Application: + """ + Main application. + """ + + def __init__(self): + """ + Creates a new application. + """ + + # Create embeddings model, backed by sentence-transformers & transformers + self.embeddings = Embeddings({"path": "sentence-transformers/nli-mpnet-base-v2"}) + + def run(self): + """ + Runs a Streamlit application. + """ + + st.title("Similarity Search") + st.markdown("This application runs a basic similarity search that identifies the best matching row for a query.") + + data = [ + "US tops 5 million confirmed virus cases", + "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg", + "Beijing mobilises invasion craft along coast as Taiwan tensions escalate", + "The National Park Service warns against sacrificing slower friends in a bear attack", + "Maine man wins $1M from $25 lottery ticket", + "Make huge profits without work, earn up to $100,000 a day", + ] + + data = st.text_area("Data", value="\n".join(data)) + query = st.text_input("Query") + + data = data.split("\n") + + if query: + # Get index of best section that best matches query + uid = self.embeddings.similarity(query, data)[0][0] + st.write(data[uid]) + + +@st.cache(allow_output_mutation=True) +def create(): + """ + Creates and caches a Streamlit application. + + Returns: + Application + """ + + return Application() + + +if __name__ == "__main__": + os.environ["TOKENIZERS_PARALLELISM"] = "false" + + # Create and run application + app = create() + app.run() diff --git a/examples/wiki.py b/examples/wiki.py new file mode 100644 index 0000000..3eaefc9 --- /dev/null +++ b/examples/wiki.py @@ -0,0 +1,73 @@ +""" +Application that queries Wikipedia API and summarizes the top result. + +Requires streamlit to be installed. + pip install streamlit +""" + +import os +import urllib.parse + +import requests +import streamlit as st + +from txtai.pipeline import Summary + + +class Application: + """ + Main application. + """ + + SEARCH_TEMPLATE = "https://en.wikipedia.org/w/api.php?action=opensearch&search=%s&limit=1&namespace=0&format=json" + CONTENT_TEMPLATE = "https://en.wikipedia.org/w/api.php?format=json&action=query&prop=extracts&exintro&explaintext&redirects=1&titles=%s" + + def __init__(self): + """ + Creates a new application. + """ + + self.summary = Summary("sshleifer/distilbart-cnn-12-6") + + def run(self): + """ + Runs a Streamlit application. + """ + + st.title("Wikipedia") + st.markdown("This application queries the Wikipedia API and summarizes the top result.") + + query = st.text_input("Query") + + if query: + query = urllib.parse.quote_plus(query) + data = requests.get(Application.SEARCH_TEMPLATE % query).json() + if data and data[1]: + page = urllib.parse.quote_plus(data[1][0]) + content = requests.get(Application.CONTENT_TEMPLATE % page).json() + content = list(content["query"]["pages"].values())[0]["extract"] + + st.write(self.summary(content)) + st.markdown("*Source: " + data[3][0] + "*") + else: + st.markdown("*No results found*") + + +@st.cache(allow_output_mutation=True) +def create(): + """ + Creates and caches a Streamlit application. + + Returns: + Application + """ + + return Application() + + +if __name__ == "__main__": + os.environ["TOKENIZERS_PARALLELISM"] = "false" + + # Create and run application + app = create() + app.run() diff --git a/examples/workflow_quickstart.py b/examples/workflow_quickstart.py new file mode 100644 index 0000000..a79232d --- /dev/null +++ b/examples/workflow_quickstart.py @@ -0,0 +1,59 @@ +""" +Workflow Quick Start +Easy to use way to get started with deterministic workflows. + +TxtAI has many example notebooks covering everything the framework provides +Examples: https://neuml.github.io/txtai/examples + +Install TxtAI + pip install txtai[pipeline-data] +""" + +from txtai import LLM, Workflow +from txtai.pipeline import Summary, Textractor, Translation +from txtai.workflow import Task + +# Step 1: Define available pipelines +textractor = Textractor(backend="docling", headers={"user-agent": "Mozilla/5.0"}) +summary = Summary() +translate = Translation() + +# Step 2: Define workflow tasks +workflow = Workflow([Task(textractor), Task(summary), Task(lambda inputs: [translate(x, "fr") for x in inputs])]) + +# Step 3: Run the workflow +print(list(workflow(["https://neuml.com"]))) + +# Each component above is a single model that specializes in a task +# LLMs can also be used to accomplish the same tasks + + +# pylint: disable=E0102,C0116 +def summary(text): + return f""" +Summarize the following text in 40 words or less. + +{text} +""" + + +def translate(text, language): + return f""" +Translate the following text to {language}. + +{text} +""" + + +textractor = Textractor(backend="docling", headers={"user-agent": "Mozilla/5.0"}) +llm = LLM("Qwen/Qwen3-4B-Instruct-2507") + +workflow = Workflow( + [ + Task(textractor), + Task(lambda inputs: llm([summary(x) for x in inputs], maxlength=25000, defaultrole="user")), + Task(lambda inputs: llm([translate(x, "fr") for x in inputs], maxlength=25000, defaultrole="user")), + ] +) + +print(list(workflow(["https://neuml.com"]))) diff --git a/examples/workflows.py b/examples/workflows.py new file mode 100644 index 0000000..5bc9e62 --- /dev/null +++ b/examples/workflows.py @@ -0,0 +1,745 @@ +""" +Build txtai workflows. + +Requires streamlit to be installed. + pip install streamlit +""" + +import contextlib +import copy +import os +import re +import tempfile +import threading +import time + +import uvicorn +import yaml + +import pandas as pd +import streamlit as st + +import txtai.api.application +import txtai.app + + +class Server(uvicorn.Server): + """ + Threaded uvicorn server used to bring up an API service. + """ + + def __init__(self, application=None, host="127.0.0.1", port=8000, log_level="info"): + """ + Initialize server configuration. + """ + + config = uvicorn.Config(application, host=host, port=port, log_level=log_level) + super().__init__(config) + + def install_signal_handlers(self): + """ + Signal handlers no-op. + """ + + @contextlib.contextmanager + def service(self): + """ + Runs threaded server service. + """ + + # pylint: disable=W0201 + thread = threading.Thread(target=self.run) + thread.start() + try: + while not self.started: + time.sleep(1e-3) + yield + + finally: + self.should_exit = True + thread.join() + + +class Process: + """ + Container for an active Workflow process instance. + """ + + @staticmethod + @st.cache_resource(show_spinner=False) + def get(name, config): + """ + Lookup or creates a new workflow process instance. + + Args: + name: workflow name + config: application configuration + + Returns: + Process + """ + + process = Process() + + # Build workflow + with st.spinner("Building workflow...."): + process.build(name, config) + + return process + + def __init__(self): + """ + Creates a new Process. + """ + + # Application handle + self.application = None + + # Workflow name + self.name = None + + # Workflow data + self.data = None + + def build(self, name, config): + """ + Builds an application. + + Args: + name: workflow name + config: application configuration + """ + + # Create application + self.application = txtai.app.Application(config) + + # Workflow name + self.name = name + + def run(self, data): + """ + Runs a workflow using data as input. + + Args: + data: input data + """ + + if data and self.application: + # Build tuples for embedding index + if self.application.embeddings: + data = [(x, element, None) for x, element in enumerate(data)] + + # Process workflow + with st.spinner("Running workflow...."): + results = [] + for result in self.application.workflow(self.name, data): + # Store result + results.append(result) + + # Write result if this isn't an indexing workflow + if not self.application.embeddings: + st.write(result) + + # Store workflow results + self.data = results + + def search(self, query): + """ + Runs a search. + + Args: + query: input query + """ + + if self.application and query: + st.markdown( + """ + + """, + unsafe_allow_html=True, + ) + + results = [] + for result in self.application.search(query, 5): + # Text is only present when content is stored + if "text" not in result: + uid, score = result["id"], result["score"] + results.append({"text": self.find(uid), "score": f"{score:.2}"}) + else: + if "id" in result and "text" in result: + result["text"] = self.content(result.pop("id"), result["text"]) + if "score" in result and result["score"]: + result["score"] = f'{result["score"]:.2}' + + results.append(result) + + df = pd.DataFrame(results) + st.write(df.to_html(escape=False), unsafe_allow_html=True) + + def find(self, key): + """ + Lookup record from cached data by uid key. + + Args: + key: id to search for + + Returns: + text for matching id + """ + + # Lookup text by id + text = [text for uid, text, _ in self.data if uid == key][0] + return self.content(key, text) + + def content(self, uid, text): + """ + Builds a content reference for uid and text. + + Args: + uid: record id + text: record text + + Returns: + content + """ + + if uid and isinstance(uid, str) and uid.lower().startswith("http"): + return f"{text}" + + return text + + +class Application: + """ + Main application. + """ + + def load(self, components): + """ + Load an existing workflow file. + + Args: + components: list of components to load + + Returns: + (names of components loaded, workflow config, file changed) + """ + + workflow = st.file_uploader("Load workflow", type=["yml"]) + if workflow: + # Detect file upload change + upload = workflow.name != self.state("path") + st.session_state["path"] = workflow.name + + workflow = yaml.safe_load(workflow) + + st.markdown("---") + + # Get tasks for first workflow + tasks = list(workflow["workflow"].values())[0]["tasks"] + selected = [] + + for task in tasks: + name = task.get("action", task.get("task")) + if name in components: + selected.append(name) + elif name in ["index", "upsert"]: + selected.append("embeddings") + + return (selected, workflow, upload) + + return (None, None, None) + + def state(self, key): + """ + Lookup a session state variable. + + Args: + key: variable key + + Returns: + variable value + """ + + if key in st.session_state: + return st.session_state[key] + + return None + + def appsetting(self, workflow, name): + """ + Looks up an application configuration setting. + + Args: + workflow: workflow configuration + name: setting name + + Returns: + app setting value + """ + + if workflow: + config = workflow.get("app") + if config: + return config.get(name) + + return None + + def setting(self, config, name, default=None): + """ + Looks up a component configuration setting. + + Args: + config: component configuration + name: setting name + default: default setting value + + Returns: + setting value + """ + + return config.get(name, default) if config else default + + def text(self, label, component, config, name, default=None): + """ + Create a new text input field. + + Args: + label: field label + component: component name + config: component configuration + name: setting name + default: default setting value + + Returns: + text input field value + """ + + default = self.setting(config, name, default) + if not default: + default = "" + elif isinstance(default, list): + default = ",".join(default) + elif isinstance(default, dict): + default = ",".join(default.keys()) + + return st.text_input(label, value=default, key=component + name) + + def number(self, label, component, config, name, default=None): + """ + Creates a new numeric input field. + + Args: + label: field label + component: component name + config: component configuration + name: setting name + default: default setting value + + Returns: + numeric value + """ + + value = self.text(label, component, config, name, default) + return int(value) if value else None + + def boolean(self, label, component, config, name, default=False): + """ + Creates a new checkbox field. + + Args: + label: field label + component: component name + config: component configuration + name: setting name + default: default setting value + + Returns: + boolean value + """ + + default = self.setting(config, name, default) + return st.checkbox(label, value=default, key=component + name) + + def select(self, label, component, config, name, options, default=0): + """ + Creates a new select box field. + + Args: + label: field label + component: component name + config: component configuration + name: setting name + options: list of dropdown options + default: default setting value + + Returns: + boolean value + """ + + index = self.setting(config, name) + index = [x for x, option in enumerate(options) if option == default] + + # Derive default index + default = index[0] if index else default + + return st.selectbox(label, options, index=default, key=component + name) + + def split(self, text): + """ + Splits text on commas and returns a list. + + Args: + text: input text + + Returns: + list + """ + + return [x.strip() for x in text.split(",")] + + def options(self, component, workflow, index): + """ + Extracts component settings into a component configuration dict. + + Args: + component: component type + workflow: existing workflow, can be None + index: task index + + Returns: + dict with component settings + """ + + # pylint: disable=R0912, R0915 + options = {"type": component} + + st.markdown("---") + + # Lookup component configuration + # - Runtime components have config defined within tasks + # - Pipeline components have config defined at workflow root + config = None + if workflow: + if component in ["service", "translation"]: + # Service config is found in tasks section + tasks = list(workflow["workflow"].values())[0]["tasks"] + tasks = [task for task in tasks if task.get("task") == component or task.get("action") == component] + if tasks: + config = tasks[0] + else: + config = workflow.get(component) + + if component == "embeddings": + st.markdown(f"**{index + 1}.) Embeddings Index** \n*Index workflow output*") + options["index"] = self.text("Embeddings storage path", component, config, "index") + options["path"] = self.text("Embeddings model path", component, config, "path", "sentence-transformers/nli-mpnet-base-v2") + options["upsert"] = self.boolean("Upsert", component, config, "upsert") + options["content"] = self.boolean("Content", component, config, "content") + + elif component in ("segmentation", "textractor"): + if component == "segmentation": + st.markdown(f"**{index + 1}.) Segment** \n*Split text into semantic units*") + else: + st.markdown(f"**{index + 1}.) Textract** \n*Extract text from documents*") + + options["sentences"] = self.boolean("Split sentences", component, config, "sentences") + options["lines"] = self.boolean("Split lines", component, config, "lines") + options["paragraphs"] = self.boolean("Split paragraphs", component, config, "paragraphs") + options["join"] = self.boolean("Join tokenized", component, config, "join") + options["minlength"] = self.number("Min section length", component, config, "minlength") + + elif component == "service": + st.markdown(f"**{index + 1}.) Service** \n*Extract data from an API*") + options["url"] = self.text("URL", component, config, "url") + options["method"] = self.select("Method", component, config, "method", ["get", "post"], 0) + options["params"] = self.text("URL parameters", component, config, "params") + options["batch"] = self.boolean("Run as batch", component, config, "batch", True) + options["extract"] = self.text("Subsection(s) to extract", component, config, "extract") + + if options["params"]: + options["params"] = {key: None for key in self.split(options["params"])} + if options["extract"]: + options["extract"] = self.split(options["extract"]) + + elif component == "summary": + st.markdown(f"**{index + 1}.) Summary** \n*Abstractive text summarization*") + options["path"] = self.text("Model", component, config, "path", "sshleifer/distilbart-cnn-12-6") + options["minlength"] = self.number("Min length", component, config, "minlength") + options["maxlength"] = self.number("Max length", component, config, "maxlength") + + elif component == "tabular": + st.markdown(f"**{index + 1}.) Tabular** \n*Split tabular data into rows and columns*") + options["idcolumn"] = self.text("Id columns", component, config, "idcolumn") + options["textcolumns"] = self.text("Text columns", component, config, "textcolumns") + options["content"] = self.text("Content", component, config, "content") + + if options["textcolumns"]: + options["textcolumns"] = self.split(options["textcolumns"]) + + if options["content"]: + options["content"] = self.split(options["content"]) + if len(options["content"]) == 1 and options["content"][0] == "1": + options["content"] = options["content"][0] + + elif component == "transcription": + st.markdown(f"**{index + 1}.) Transcribe** \n*Transcribe audio to text*") + options["path"] = self.text("Model", component, config, "path", "facebook/wav2vec2-base-960h") + + elif component == "translation": + st.markdown(f"**{index + 1}.) Translate** \n*Machine translation*") + options["target"] = self.text("Target language code", component, config, "args", "en") + + return options + + def config(self, components): + """ + Builds configuration for components + + Args: + components: list of components to add to configuration + + Returns: + (workflow name, configuration) + """ + + data = {} + tasks = [] + name = None + + for component in components: + component = dict(component) + name = wtype = component.pop("type") + + if wtype == "embeddings": + index = component.pop("index") + upsert = component.pop("upsert") + + data[wtype] = component + data["writable"] = True + + if index: + data["path"] = index + + name = "index" + tasks.append({"action": "upsert" if upsert else "index"}) + + elif wtype == "segmentation": + data[wtype] = component + tasks.append({"action": wtype}) + + elif wtype == "service": + config = {**component} + config["task"] = wtype + tasks.append(config) + + elif wtype == "summary": + data[wtype] = {"path": component.pop("path")} + tasks.append({"action": wtype}) + + elif wtype == "tabular": + data[wtype] = component + tasks.append({"action": wtype}) + + elif wtype == "textractor": + data[wtype] = component + tasks.append({"action": wtype, "task": "url"}) + + elif wtype == "transcription": + data[wtype] = {"path": component.pop("path")} + tasks.append({"action": wtype, "task": "url"}) + + elif wtype == "translation": + data[wtype] = {} + tasks.append({"action": wtype, "args": list(component.values())}) + + # Add in workflow + data["workflow"] = {name: {"tasks": tasks}} + + # Return workflow name and application configuration + return (name, data) + + def api(self, config): + """ + Starts an internal uvicorn server to host an API service for the current workflow. + + Args: + config: workflow configuration as YAML string + """ + + # Generate workflow file + workflow = os.path.join(tempfile.gettempdir(), "workflow.yml") + with open(workflow, "w", encoding="utf-8") as f: + f.write(config) + + os.environ["CONFIG"] = workflow + txtai.api.application.start() + server = Server(txtai.api.application.app) + with server.service(): + uid = 0 + while True: + stop = st.empty() + click = stop.button("stop", key=uid) + if not click: + time.sleep(5) + uid += 1 + stop.empty() + + def inputs(self, selected, workflow): + """ + Generate process input fields. + + Args: + selected: list of selected components + workflow: workflow configuration + + Returns: + True if inputs changed, False otherwise + """ + + change, query = False, None + with st.expander("Data", expanded="embeddings" not in selected): + default = self.appsetting(workflow, "data") + default = default if default else "" + + data = st.text_area("Input", height=10, value=default) + + if selected and data and data != self.state("data"): + change = True + + # Save data and workflow state + st.session_state["data"] = data + + if "embeddings" in selected: + default = self.appsetting(workflow, "query") + default = default if default else "" + + # Set query and limit + query = st.text_input("Query", value=default) + + if selected and query and query != self.state("query"): + change = True + + # Save query state + st.session_state["query"] = query + + return change or self.state("api") or self.state("download") + + def data(self): + """ + Gets input data. + + Returns: + input data + """ + + data = self.state("data") + + # Split on newlines if urls detected, allows a list of urls to be processed + if re.match(r"^(http|https|file):\/\/", data): + return [x for x in data.split("\n") if x] + + return [data] + + def process(self, components, index): + """ + Processes the current application action. + + Args: + components: workflow components + index: True if this is an indexing workflow + """ + + # Generate application configuration + name, config = self.config(components) + + # Get workflow process + process = Process.get(name, copy.deepcopy(config)) + + # Run workflow process + process.run(self.data()) + + # Run search + if index: + process.search(self.state("query")) + + return name, config + + def run(self): + """ + Runs Streamlit application. + """ + + build = False + with st.sidebar: + st.image("https://github.com/neuml/txtai/raw/master/logo.png", width=256) + st.markdown("# Workflow builder \n*Build and apply workflows to data* ") + st.markdown("---") + + # Component configuration + labels = {"segmentation": "segment", "textractor": "textract", "transcription": "transcribe", "translation": "translate"} + components = ["embeddings", "segmentation", "service", "summary", "tabular", "textractor", "transcription", "translation"] + + selected, workflow, upload = self.load(components) + selected = st.multiselect("Select components", components, default=selected, format_func=lambda text: labels.get(text, text)) + + if selected: + st.markdown( + """ + + """, + unsafe_allow_html=True, + ) + + with st.form("workflow"): + # Get selected options + components = [self.options(component, workflow, x) for x, component in enumerate(selected)] + st.markdown("---") + + # Build or re-build workflow when build button clicked or new workflow loaded + build = st.form_submit_button("Build", help="Build the workflow and run within this application") + + # Generate input fields + inputs = self.inputs(selected, workflow) + + # Only execute if build button clicked, new workflow uploaded or inputs changed + if build or upload or inputs: + # Process current action + name, config = self.process(components, "embeddings" in selected) + + with st.sidebar: + with st.expander("Other Actions", expanded=True): + col1, col2 = st.columns(2) + + # Add state information to configuration and export to YAML string + config = config.copy() + config.update({"app": {"data": self.state("data"), "query": self.state("query")}}) + config = yaml.dump(config) + + api = col1.button("API", key="api", help="Start an API instance within this application") + if api: + with st.spinner(f"Running workflow '{name}' via API service, click stop to terminate"): + self.api(config) + + col2.download_button("Export", config, file_name="workflow.yml", key="download", help="Export the API workflow as YAML") + + +if __name__ == "__main__": + os.environ["TOKENIZERS_PARALLELISM"] = "false" + + # Create and run application + app = Application() + app.run() diff --git a/logo.png b/logo.png new file mode 100644 index 0000000..5495194 Binary files /dev/null and b/logo.png differ diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..6af5c53 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,143 @@ +# +# txtai Material for MkDocs configuration +# + +site_name: txtai +site_description: "txtai is an all-in-one open-source AI framework for semantic search, LLM orchestration and language model workflows" +repo_name: neuml/txtai +repo_url: https://github.com/neuml/txtai +copyright: © NeuML LLC, Apache-2.0 License +theme: + name: material + logo: images/logo.png + favicon: images/logo.png + custom_dir: docs/overrides + icon: + repo: fontawesome/brands/github + palette: + - media: "(prefers-color-scheme: light)" + scheme: default + primary: blue + accent: blue + toggle: + icon: material/toggle-switch-off-outline + name: Switch to dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: light blue + accent: light blue + toggle: + icon: material/toggle-switch + name: Switch to light mode + features: + - navigation.indexes + - navigation.instant +plugins: + - search + - mkdocstrings: + handlers: + python: + options: + show_root_full_path: false + show_root_heading: true + show_root_toc_entry: false + - redirects: + redirect_maps: + "pipeline/text/llm.md": "pipeline/llm/llm.md" + "pipeline/text/rag.md": "pipeline/llm/rag.md" + +markdown_extensions: + - admonition + - pymdownx.details + - pymdownx.highlight + - pymdownx.superfences +nav: + - Home: index.md + - Why txtai?: why.md + - Use Cases: usecases.md + - Installation: install.md + - Model Guide: models.md + - Embeddings: + - embeddings/index.md + - Configuration: + - embeddings/configuration/index.md + - ANN: embeddings/configuration/ann.md + - Cloud: embeddings/configuration/cloud.md + - Database: embeddings/configuration/database.md + - General: embeddings/configuration/general.md + - Graph: embeddings/configuration/graph.md + - Scoring: embeddings/configuration/scoring.md + - Vectors: embeddings/configuration/vectors.md + - Index Format: embeddings/format.md + - Index Guide: embeddings/indexing.md + - Methods: embeddings/methods.md + - Query Guide: embeddings/query.md + - Agent: + - agent/index.md + - Configuration: agent/configuration.md + - Methods: agent/methods.md + - Pipeline: + - pipeline/index.md + - Audio: + - Audio Mixer: pipeline/audio/audiomixer.md + - Audio Stream: pipeline/audio/audiostream.md + - Microphone: pipeline/audio/microphone.md + - Text To Audio: pipeline/audio/texttoaudio.md + - Text To Speech: pipeline/audio/texttospeech.md + - Transcription: pipeline/audio/transcription.md + - Data: + - File To HTML: pipeline/data/filetohtml.md + - HTML To Markdown: pipeline/data/htmltomd.md + - Segmentation: pipeline/data/segmentation.md + - Tabular: pipeline/data/tabular.md + - Textractor: pipeline/data/textractor.md + - Tokenizer: pipeline/data/tokenizer.md + - URL Retrieve: pipeline/data/urlretrieve.md + - Image: + - Caption: pipeline/image/caption.md + - Image Hash: pipeline/image/imagehash.md + - Objects: pipeline/image/objects.md + - LLM: + - LLM: pipeline/llm/llm.md + - RAG: pipeline/llm/rag.md + - Text: + - Entity: pipeline/text/entity.md + - Labels: pipeline/text/labels.md + - Reranker: pipeline/text/reranker.md + - Similarity: pipeline/text/similarity.md + - Summary: pipeline/text/summary.md + - Translation: pipeline/text/translation.md + - Train: + - HF ONNX: pipeline/train/hfonnx.md + - ML ONNX: pipeline/train/mlonnx.md + - Trainer: pipeline/train/trainer.md + - Workflow: + - workflow/index.md + - Schedule: workflow/schedule.md + - Tasks: + - workflow/task/index.md + - Console: workflow/task/console.md + - Export: workflow/task/export.md + - File: workflow/task/file.md + - Image: workflow/task/image.md + - Retrieve: workflow/task/retrieve.md + - Service: workflow/task/service.md + - Storage: workflow/task/storage.md + - Template: workflow/task/template.md + - Url: workflow/task/url.md + - Workflow: workflow/task/workflow.md + - API: + - api/index.md + - Cluster: api/cluster.md + - Configuration: api/configuration.md + - Customization: api/customization.md + - Methods: api/methods.md + - Model Context Protocol: api/mcp.md + - OpenAI: api/openai.md + - Security: api/security.md + - Cloud: cloud.md + - Examples: examples.md + - FAQ: faq.md + - Observability: observability.md + - Powered by txtai: poweredby.md + - Further Reading: further.md diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..6b313bc --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,2 @@ +[tool.black] +line-length = 150 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..c71b4d0 --- /dev/null +++ b/setup.py @@ -0,0 +1,206 @@ +# pylint: disable = C0103,C0111 +import os + +from setuptools import find_packages, setup + +with open("README.md", "r", encoding="utf-8") as f: + # Remove GitHub dark mode images + DESCRIPTION = "".join([line for line in f if "gh-dark-mode-only" not in line]) + +# Required dependencies +install = [] + +# Default dependencies +default = [ + "faiss-cpu>=1.7.1.post2", + "huggingface-hub>=0.34.0", + "msgpack>=1.0.7", + "numpy>=1.18.4", + "regex>=2022.8.17", + "pyyaml>=5.3", + "safetensors>=0.4.5", + "torch>=2.4", + "transformers>=5.9.0", +] + +# Optional dependencies +extras = {} + +# Package name +package = "txtai" + +if os.getenv("MINIMAL"): + # Add default extra + extras["default"] = default + + # Rename this package to have minimal suffix + package += "_minimal" +else: + # Default dependencies are required with standard install + install += default + +# Development dependencies - not included in "all" install +extras["dev"] = [ + "black", + "coverage", + "coveralls", + "httpx", + "mkdocs-material", + "mkdocs-redirects", + "mkdocstrings[python]", + "pre-commit", + "pylint", +] + +extras["agent"] = ["jinja2>=3.1.6", "mcpadapt>=0.1.0", "smolagents>=1.23"] + +extras["ann"] = [ + "annoy>=1.16.3", + "bitsandbytes>=0.42.0", + "ggml-python>=0.0.41", + "hnswlib>=0.5.0", + "pgvector>=0.4.1", + "scikit-learn>=0.23.1", + "scipy>=1.4.1", + "sqlalchemy>=2.0.20", + "sqlite-vec>=0.1.1", + "turbovec>=0.7.0", +] + +extras["api"] = [ + "aiohttp>=3.8.1", + "fastapi>=0.94.0", + "fastapi-mcp>=0.4.0", + "httpx>=0.28.1", + "pillow>=7.1.2", + "python-multipart>=0.0.7", + "uvicorn>=0.12.1", +] + +extras["cloud"] = ["apache-libcloud>=3.3.1", "fasteners>=0.14.1"] + +extras["console"] = ["rich>=12.0.1"] + +extras["database"] = ["duckdb>=0.8.0", "pillow>=7.1.2", "sqlalchemy>=2.0.20"] + +extras["graph"] = ["grand-cypher>=0.6.0", "grand-graph>=0.6.0", "networkx>=2.7.1", "sqlalchemy>=2.0.20"] + +extras["model"] = ["onnx>=1.11.0", "onnxruntime>=1.11.0"] + +extras["pipeline-audio"] = [ + "onnx>=1.11.0", + "onnxruntime>=1.11.0", + "scipy>=1.4.1", + "sounddevice>=0.5.0", + "soundfile>=0.10.3.post1", + "ttstokenizer>=1.1.0", + "webrtcvad-wheels>=2.0.14", +] + +extras["pipeline-data"] = [ + "beautifulsoup4>=4.9.3", + "chonkie>=1.0.2", + "docling>=2.8.2", + "liteparse>=2.1.1", + "nltk>=3.5", + "pandas>=1.1.0", + "tika>=1.24", +] + +extras["pipeline-image"] = ["imagehash>=4.2.1", "pillow>=7.1.2", "timm>=0.4.12"] + +extras["pipeline-llm"] = ["httpx>=0.28.1", "litert-lm-api>=0.11.0", "litellm>=1.37.16", "llama-cpp-python>=0.2.75"] + +extras["pipeline-text"] = ["gliner-py>=0.2.27", "sentencepiece>=0.1.91", "staticvectors>=0.2.0"] + +extras["pipeline-train"] = [ + "accelerate>=0.26.0", + "bitsandbytes>=0.42.0", + "onnx>=1.11.0", + "onnxmltools>=1.9.1", + "onnxruntime>=1.11.0", + "peft>=0.8.1", + "skl2onnx>=1.9.1", +] + +extras["pipeline"] = ( + extras["pipeline-audio"] + + extras["pipeline-data"] + + extras["pipeline-image"] + + extras["pipeline-llm"] + + extras["pipeline-text"] + + extras["pipeline-train"] +) + +extras["scoring"] = ["sqlalchemy>=2.0.20"] + +extras["vectors"] = [ + "ai-edge-litert>=2.1.5", + "litellm>=1.37.16", + "llama-cpp-python>=0.2.75", + "model2vec>=0.3.0", + "scikit-learn>=0.23.1", + "scipy>=1.4.1", + "sentence-transformers>=5.0.0", + "skops>=0.9.0", + "staticvectors>=0.2.0", + "tokenizers>=0.22.2", +] + +extras["workflow"] = [ + "apache-libcloud>=3.3.1", + "croniter>=1.2.0", + "openpyxl>=3.0.9", + "pandas>=1.1.0", + "pillow>=7.1.2", + "requests>=2.26.0", + "xmltodict>=0.12.0", +] + +# Backwards-compatible combination of ann and vectors extra +extras["similarity"] = extras["ann"] + extras["vectors"] + +extras["all"] = ( + extras["agent"] + + extras["api"] + + extras["cloud"] + + extras["console"] + + extras["database"] + + extras["graph"] + + extras["model"] + + extras["pipeline"] + + extras["scoring"] + + extras["similarity"] + + extras["workflow"] +) + +setup( + name=package, + version="9.12.0", + author="NeuML", + description="All-in-one open-source AI framework for semantic search, LLM orchestration and language model workflows", + long_description=DESCRIPTION, + long_description_content_type="text/markdown", + url="https://github.com/neuml/txtai", + project_urls={ + "Documentation": "https://github.com/neuml/txtai", + "Issue Tracker": "https://github.com/neuml/txtai/issues", + "Source Code": "https://github.com/neuml/txtai", + }, + license="Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0", + packages=find_packages(where="src/python"), + package_dir={"": "src/python"}, + keywords="search embedding machine-learning nlp", + python_requires=">=3.10", + install_requires=install, + extras_require=extras, + classifiers=[ + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Software Development", + "Topic :: Text Processing :: Indexing", + "Topic :: Utilities", + ], +) diff --git a/src/python/txtai/__init__.py b/src/python/txtai/__init__.py new file mode 100644 index 0000000..8f69816 --- /dev/null +++ b/src/python/txtai/__init__.py @@ -0,0 +1,16 @@ +""" +Base imports +""" + +import logging + +# Top-level imports +from .agent import Agent +from .app import Application +from .embeddings import Embeddings +from .pipeline import LLM, RAG, Textractor +from .workflow import Workflow + +# Configure logging per standard Python library recommendations +logger = logging.getLogger(__name__) +logger.addHandler(logging.NullHandler()) diff --git a/src/python/txtai/agent/__init__.py b/src/python/txtai/agent/__init__.py new file mode 100644 index 0000000..9691965 --- /dev/null +++ b/src/python/txtai/agent/__init__.py @@ -0,0 +1,12 @@ +""" +Agent imports +""" + +# Conditional import +try: + from .base import Agent + from .factory import ProcessFactory + from .model import PipelineModel + from .tool import * +except ImportError: + from .placeholder import Agent diff --git a/src/python/txtai/agent/base.py b/src/python/txtai/agent/base.py new file mode 100644 index 0000000..23463db --- /dev/null +++ b/src/python/txtai/agent/base.py @@ -0,0 +1,142 @@ +""" +Agent module +""" + +import os + +from collections import deque + +from jinja2.sandbox import SandboxedEnvironment + +from .factory import ProcessFactory + + +class Agent: + """ + An agent automatically creates workflows to answer multi-faceted user requests. Agents iteratively prompt and/or interface with tools to + step through a process and ultimately come to an answer for a request. + + Agents excel at complex tasks where multiple tools and/or methods are required. They incorporate a level of randomness similar to different + people working on the same task. When the request is simple and/or there is a rule-based process, other methods such as RAG and Workflows + should be explored. + """ + + def __init__(self, template=None, memory=None, **kwargs): + """ + Creates a new Agent. + + Args: + template: optional prompt jinja template, must include {{ text }} and {{ memory }} placeholders + memory: number of prior outputs to keep as "memory", defaults to None for no memory + kwargs: arguments to pass to the underlying Agent backend and LLM pipeline instance + """ + + # Ensure backwards compatibility + if "max_iterations" in kwargs: + kwargs["max_steps"] = kwargs.pop("max_iterations") + + # Custom instructions + if "instructions" in kwargs: + kwargs["instructions"] = self.instructions(kwargs) + + # Create agent process runner + self.process = ProcessFactory.create(kwargs) + + # Tools dictionary + self.tools = self.process.tools + + # Agent memory + self.memory = {} + self.window = memory + + # Create template + self.template = SandboxedEnvironment().from_string( + template + if template + else """{{ text }} +{% if memory %} +Use the following conversation history to help answer the question above. + +{{ memory }} + +If the history is irrelevant, forget it and use other tools to answer the question. +{% endif %} +""" + ) + + def __call__(self, text, maxlength=8192, stream=False, session=None, reset=False, **kwargs): + """ + Runs an agent loop. + + Args: + text: instructions to run + maxlength: maximum sequence length + stream: stream response if True, defaults to False + session: session id for stored memory, defaults to None which shares all memory + reset: clears previously stored memory if True, defaults to False + kwargs: additional keyword arguments + + Returns: + result + """ + + # Process parameters + self.process.model.parameters(maxlength) + + # Create memory, if necessary + if self.window and (session not in self.memory or reset): + self.memory[session] = deque(maxlen=self.window) + + # Run agent loop + output = self.process.run(self.prompt(text, session), stream=stream, **kwargs) + + # Add output to memory, if necessary + if session in self.memory: + self.memory[session].append((text, output)) + + return output + + def prompt(self, text, session): + """ + Generates the full agent prompt using the input instructions. Adds in agent memory + if available. + + Args: + text: instructions to run + session: session id for stored memory + + Returns: + formatted instructions + """ + + # pylint: disable=E1133 + memory = [] + if self.memory.get(session): + # Add memory context + for request, output in self.memory[session]: + memory.append(f"User: {request}\nAssistant: {output}") + + memory = "\n\n".join(memory) + + # Command template with memory + return self.template.render(text=text, memory=memory) + + def instructions(self, config): + """ + Reads and formats custom instructions. Supports agents.md files. + + Args: + config: agent configuration + + Returns: + agent instructions, if any + """ + + # Check if this is a file path (i.e. agents.md) + path = config.pop("instructions", None) + if path and os.path.isfile(path): + with open(path, encoding="utf-8") as f: + # Read entire file + return f"\nBelow is a set of general instructions to follow:\n\n{f.read()}" + + return path diff --git a/src/python/txtai/agent/factory.py b/src/python/txtai/agent/factory.py new file mode 100644 index 0000000..35f3f50 --- /dev/null +++ b/src/python/txtai/agent/factory.py @@ -0,0 +1,39 @@ +""" +Factory module +""" + +from smolagents import CodeAgent, ToolCallingAgent + +from .model import PipelineModel +from .tool import ToolFactory + + +class ProcessFactory: + """ + Methods to create agent processes. + """ + + @staticmethod + def create(config): + """ + Create an agent process runner. The agent process runner takes a list of tools and an LLM + and executes an agent process flow. + + Args: + config: agent configuration + + Returns: + agent process runner + """ + + constructor = ToolCallingAgent + method = config.pop("method", None) + if method == "code": + constructor = CodeAgent + + # Create model backed by LLM pipeline + model = config.pop("model", config.pop("llm", None)) + model = PipelineModel(**model) if isinstance(model, dict) else PipelineModel(model) + + # Create the agent process + return constructor(tools=ToolFactory.create(config), model=model, **config) diff --git a/src/python/txtai/agent/model.py b/src/python/txtai/agent/model.py new file mode 100644 index 0000000..623cfaa --- /dev/null +++ b/src/python/txtai/agent/model.py @@ -0,0 +1,104 @@ +""" +Model module +""" + +import re + +from enum import Enum + +from smolagents import ChatMessage, Model, get_clean_message_list, tool_role_conversions +from smolagents.models import get_tool_call_from_text, remove_content_after_stop_sequences + +from ..pipeline import LLM + + +class PipelineModel(Model): + """ + Model backed by a LLM pipeline. + """ + + def __init__(self, path=None, method=None, **kwargs): + """ + Creates a new LLM model. + + Args: + path: model path or instance + method: llm model framework, infers from path if not provided + kwargs: model keyword arguments + """ + + self.llm = path if isinstance(path, LLM) else LLM(path, method, **kwargs) + self.maxlength = 8192 + + # Call parent constructor + super().__init__(flatten_messages_as_text=not self.llm.isvision(), model_id=self.llm.generator.path, **kwargs) + + # pylint: disable=W0613 + def generate(self, messages, stop_sequences=None, response_format=None, tools_to_call_from=None, **kwargs): + """ + Runs LLM inference. This method signature must match the smolagents specification. + + Args: + messages: list of messages to run + stop_sequences: optional list of stop sequences + response_format: response format to use in the model's response. + tools_to_call_from: list of tools that the model can use to generate responses. + kwargs: additional keyword arguments + + Returns: + result + """ + + # Get clean message list + messages = self.clean(messages) + + # Get LLM output + response = self.llm(messages, maxlength=self.maxlength, stop=stop_sequences, **kwargs) + + # Remove stop sequences from LLM output + if stop_sequences is not None: + response = remove_content_after_stop_sequences(response, stop_sequences) + + # Load response into a chat message + message = ChatMessage(role="assistant", content=response) + + # Extract first tool action, if necessary + if tools_to_call_from: + message.tool_calls = [ + get_tool_call_from_text( + re.sub(r".*?Action:(.*?\n\}).*", r"\1", response, flags=re.DOTALL), self.tool_name_key, self.tool_arguments_key + ) + ] + + return message + + def parameters(self, maxlength): + """ + Set LLM inference parameters. + + Args: + maxlength: maximum sequence length + """ + + self.maxlength = maxlength + + def clean(self, messages): + """ + Gets a clean message list. + + Args: + messages: input messages + + Returns: + clean messages + """ + + # Get clean message list + messages = get_clean_message_list(messages, role_conversions=tool_role_conversions, flatten_messages_as_text=self.flatten_messages_as_text) + + # Ensure all roles are strings and not enums for compability across LLM frameworks + for message in messages: + if "role" in message: + message["role"] = message["role"].value if isinstance(message["role"], Enum) else message["role"] + + return messages diff --git a/src/python/txtai/agent/placeholder.py b/src/python/txtai/agent/placeholder.py new file mode 100644 index 0000000..5897a38 --- /dev/null +++ b/src/python/txtai/agent/placeholder.py @@ -0,0 +1,16 @@ +""" +Placeholder module +""" + + +class Agent: + """ + Agent placeholder stub for when smolagents isn't installed + """ + + def __init__(self, *args, **kwargs): + """ + Raises an exception that smolagents isn't installed. + """ + + raise ImportError('smolagents is not available - install "agent" extra to enable') diff --git a/src/python/txtai/agent/tool/__init__.py b/src/python/txtai/agent/tool/__init__.py new file mode 100644 index 0000000..c45c38e --- /dev/null +++ b/src/python/txtai/agent/tool/__init__.py @@ -0,0 +1,14 @@ +""" +Tool imports +""" + +from .embeddings import EmbeddingsTool +from .factory import ToolFactory +from .function import FunctionTool +from .bash import BashTool +from .edit import EditTool +from .glob import GlobTool +from .grep import GrepTool +from .read import ReadTool +from .skill import SkillTool +from .todo import TodoWriteTool diff --git a/src/python/txtai/agent/tool/bash.py b/src/python/txtai/agent/tool/bash.py new file mode 100644 index 0000000..deda1ce --- /dev/null +++ b/src/python/txtai/agent/tool/bash.py @@ -0,0 +1,55 @@ +""" +Bash imports +""" + +import subprocess + +from smolagents import Tool + + +class BashTool(Tool): + """ + The BashTool runs a command through a subprocess. This tool only allows a small subset of commands. + More can be added through configuration. + """ + + # pylint: disable=W0231 + def __init__(self, allowed=None): + """ + Creates a BashTool. + + Args: + allowed: list of allowed commands to run, has limited set of defaults which are a best effort not a sandbox + """ + + # Tool parameters + self.name = "bash" + self.description = "Implementation of a bash shell subprocess tool. Runs a shell command and returns the output." + self.inputs = { + "command": {"type": "array", "description": "Command to run. Follows Python subprocess.open pattern for command as a list of arguments."} + } + self.output_type = "any" + + # Default list of allowed commands + self.allowed = allowed if allowed else ["cat", "cut", "diff", "grep", "head", "ls", "tail"] + + # Validate parameters and initialize tool + super().__init__() + + # pylint: disable=W0221 + def forward(self, command): + """ + Runs a shell command as a subprocess. + + Args: + command: command arguments as a list + + Returns: + command output + """ + + output = None + if command and command[0] in self.allowed: + output = subprocess.run(command, capture_output=True, text=True, check=False).stdout + + return output diff --git a/src/python/txtai/agent/tool/edit.py b/src/python/txtai/agent/tool/edit.py new file mode 100644 index 0000000..1acd874 --- /dev/null +++ b/src/python/txtai/agent/tool/edit.py @@ -0,0 +1,59 @@ +""" +Edit imports +""" + +import difflib + +from smolagents import Tool + + +class EditTool(Tool): + """ + The EditTool modifies content in a file. + """ + + # pylint: disable=W0231 + def __init__(self): + """ + Creates an EditTool. + """ + + # Tool parameters + self.name = "edit" + self.description = "Implementation of a file edit tool. Makes in-place edits to content in a file." + self.inputs = { + "path": {"type": "string", "description": "Path of file to edit"}, + "search": {"type": "string", "description": "String to replace"}, + "replace": {"type": "string", "description": "Replacement string"}, + } + self.output_type = "any" + + # Validate parameters and initialize tool + super().__init__() + + # pylint: disable=W0221 + def forward(self, path, search, replace): + """ + Modifies content in a file. Returns a diff of the changes, if any. + + Args: + path: file to edit + search: string to replace + replace: replacement + + Returns: + diff + """ + + content = None + with open(path, "r", encoding="utf-8") as f: + content = f.read() + + # Make edits + modified = content.replace(search, replace) + if modified != content: + with open(path, "w", encoding="utf-8") as f: + f.write(modified) + + # Return diff of file edits + return "".join(difflib.unified_diff(content.splitlines(True), modified.splitlines(True), path, path)) diff --git a/src/python/txtai/agent/tool/embeddings.py b/src/python/txtai/agent/tool/embeddings.py new file mode 100644 index 0000000..c0a7532 --- /dev/null +++ b/src/python/txtai/agent/tool/embeddings.py @@ -0,0 +1,69 @@ +""" +Embeddings module +""" + +from smolagents import Tool + +from ...embeddings import Embeddings + + +class EmbeddingsTool(Tool): + """ + Tool to execute an Embeddings search. + """ + + def __init__(self, config): + """ + Creates a new EmbeddingsTool. + + Args: + config: embeddings tool configuration + """ + + # Tool parameters + self.name = config["name"] + self.description = f"""{config['description']}. Results are returned as a list of dict elements. +Each result has keys 'id', 'text', 'score'.""" + + # Input and output descriptions + self.inputs = {"query": {"type": "string", "description": "The search query to perform."}} + self.output_type = "any" + + # Load embeddings instance + self.embeddings = self.load(config) + + # Validate parameters and initialize tool + super().__init__() + + # pylint: disable=W0221 + def forward(self, query): + """ + Runs a search. + + Args: + query: input query + + Returns: + search results + """ + + return self.embeddings.search(query, 5) + + def load(self, config): + """ + Loads an embeddings instance from config. + + Args: + config: embeddings tool configuration + + Returns: + Embeddings + """ + + if "target" in config: + return config["target"] + + embeddings = Embeddings() + embeddings.load(**config) + + return embeddings diff --git a/src/python/txtai/agent/tool/factory.py b/src/python/txtai/agent/tool/factory.py new file mode 100644 index 0000000..06aa81e --- /dev/null +++ b/src/python/txtai/agent/tool/factory.py @@ -0,0 +1,165 @@ +""" +Factory module +""" + +import inspect + +from types import FunctionType, MethodType + +import mcpadapt.core + +from mcpadapt.smolagents_adapter import SmolAgentsAdapter +from smolagents import PythonInterpreterTool, Tool, tool as CreateTool, UserInputTool, WebSearchTool +from transformers.utils import chat_template_utils, TypeHintParsingException + +from ...embeddings import Embeddings +from .bash import BashTool +from .edit import EditTool +from .embeddings import EmbeddingsTool +from .function import FunctionTool +from .glob import GlobTool +from .grep import GrepTool +from .read import ReadTool +from .skill import SkillTool +from .todo import TodoWriteTool +from .write import WriteTool + + +class ToolFactory: + """ + Methods to create tools. + """ + + # Default toolkit + DEFAULTS = { + "bash": BashTool(), + "edit": EditTool(), + "glob": GlobTool(), + "grep": GrepTool(), + "python": PythonInterpreterTool(), + "question": UserInputTool(), + "read": ReadTool(), + "todowrite": TodoWriteTool(), + "websearch": WebSearchTool(), + "write": WriteTool(), + } + + # Backwards compatible mappings + DEFAULTS["webview"] = DEFAULTS["read"] + + @staticmethod + def create(config): + """ + Creates a new list of tools. This method iterates of the `tools` configuration option and creates a Tool instance + for each entry. This supports the following: + + - Tool instance + - Dictionary with `name`, `description`, `inputs`, `output` and `target` function configuration + - String with a tool alias name + + Returns: + list of tools + """ + + tools = [] + for tool in config.pop("tools", []): + # Create tool from function and it's documentation + if not isinstance(tool, Tool) and (isinstance(tool, (FunctionType, MethodType)) or hasattr(tool, "__call__")): + tool = ToolFactory.createtool(tool) + + # Create tool from input dictionary + elif isinstance(tool, dict): + # Get target function + target = tool.get("target") + + # Create tool from input dictionary + tool = ( + EmbeddingsTool(tool) + if isinstance(target, Embeddings) or any(x in tool for x in ["container", "path"]) + else ToolFactory.createtool(target, tool) + ) + + # Get default tool, if applicable + elif isinstance(tool, str) and tool in ToolFactory.DEFAULTS: + tool = ToolFactory.DEFAULTS[tool] + + # Get ALL default tools, if applicable + elif isinstance(tool, str) and tool == "defaults": + tools.extend(set(ToolFactory.DEFAULTS.values())) + tool = None + + # Support importing MCP tool collections + elif isinstance(tool, str) and tool.startswith("http"): + tools.extend(mcpadapt.core.MCPAdapt({"url": tool}, SmolAgentsAdapter()).tools()) + tool = None + + # Load skill.md files + elif isinstance(tool, str) and tool.endswith(".md"): + tool = SkillTool(tool) + + # Add tool + if tool: + tools.append(tool) + + return tools + + @staticmethod + def createtool(target, config=None): + """ + Creates a new Tool. + + Args: + target: target object or function + config: optional tool configuration + + Returns: + Tool + """ + + try: + # Try to create using CreateTool function - this fails when no annotations are available + return CreateTool(target) + except (TypeHintParsingException, TypeError): + return ToolFactory.fromdocs(target, config if config else {}) + + @staticmethod + def fromdocs(target, config): + """ + Creates a tool from method documentation. + + Args: + target: target object or function + config: tool configuration + + Returns: + Tool + """ + + # Get function name and target - use target if it's a function or method, else use target.__call__ + name = target.__name__ if isinstance(target, (FunctionType, MethodType)) or not hasattr(target, "__call__") else target.__class__.__name__ + target = target if isinstance(target, (FunctionType, MethodType)) or not hasattr(target, "__call__") else target.__call__ + + # Extract target documentation + doc = inspect.getdoc(target) + description, parameters, _ = chat_template_utils.parse_google_format_docstring(doc.strip()) if doc else (None, {}, None) + + # Get list of required parameters + signature = inspect.signature(target) + inputs = {} + for pname, param in signature.parameters.items(): + if ( + param.default == inspect.Parameter.empty + and pname in parameters + and param.kind not in (inspect.Parameter.VAR_KEYWORD, inspect.Parameter.VAR_POSITIONAL) + ): + inputs[pname] = {"type": "any", "description": parameters[pname]} + + # Create function tool + return FunctionTool( + { + "name": config.get("name", name.lower()), + "description": config.get("description", description), + "inputs": config.get("inputs", inputs), + "target": config.get("target", target), + } + ) diff --git a/src/python/txtai/agent/tool/function.py b/src/python/txtai/agent/tool/function.py new file mode 100644 index 0000000..7fd80dd --- /dev/null +++ b/src/python/txtai/agent/tool/function.py @@ -0,0 +1,48 @@ +""" +Function imports +""" + +from smolagents import Tool + + +class FunctionTool(Tool): + """ + A FunctionTool takes descriptive configuration and injects it along with a target function into an LLM prompt. + """ + + # pylint: disable=W0231 + def __init__(self, config): + """ + Creates a FunctionTool. + + Args: + config: `name`, `description`, `inputs`, `output` and `target` configuration + """ + + # Tool parameters + self.name = config["name"] + self.description = config["description"] + self.inputs = config["inputs"] + self.output_type = config.get("output", config.get("output_type", "any")) + self.target = config["target"] + + # pylint: disable=C0103 + # Skip forward signature validation + self.skip_forward_signature_validation = True + + # Validate parameters and initialize tool + super().__init__() + + def forward(self, *args, **kwargs): + """ + Runs target function. + + Args: + args: positional args + kwargs: keyword args + + Returns: + result + """ + + return self.target(*args, **kwargs) diff --git a/src/python/txtai/agent/tool/glob.py b/src/python/txtai/agent/tool/glob.py new file mode 100644 index 0000000..e49faa1 --- /dev/null +++ b/src/python/txtai/agent/tool/glob.py @@ -0,0 +1,49 @@ +""" +Glob imports +""" + +import glob as g +import os + +from smolagents import Tool + + +class GlobTool(Tool): + """ + The GlobTool finds matching file patterns. + """ + + # pylint: disable=W0231 + def __init__(self): + """ + Creates a GlobTool. + """ + + # Tool parameters + self.name = "glob" + self.description = "Implementation of a glob tool. Finds files that match glob patterns." + self.inputs = { + "files": {"type": "string", "description": "File or file glob pattern to search"}, + } + self.output_type = "any" + + # Validate parameters and initialize tool + super().__init__() + + # pylint: disable=W0221 + def forward(self, files): + """ + Lists files matching a glob pattern + + Args: + files: files glob pattern + + Returns: + list of matching files + """ + + # Format file pattern + files = os.path.join(files, "*") if os.path.isdir(files) else files + + # Iterate over each found file + return g.glob(files, recursive=True) diff --git a/src/python/txtai/agent/tool/grep.py b/src/python/txtai/agent/tool/grep.py new file mode 100644 index 0000000..860f673 --- /dev/null +++ b/src/python/txtai/agent/tool/grep.py @@ -0,0 +1,75 @@ +""" +Grep imports +""" + +import glob +import os +import re + +from smolagents import Tool + + +class GrepTool(Tool): + """ + The GrepTool "greps" for file matches. + """ + + # pylint: disable=W0231 + def __init__(self): + """ + Creates a GrepTool. + """ + + # Tool parameters + self.name = "grep" + self.description = ( + "Implementation of a grep tool. Searches a list of files for matches. " + "Returns a dictionary with the file path as the key and list of matches as the value." + ) + self.inputs = { + "search": {"type": "string", "description": "Search grep pattern"}, + "files": {"type": "string", "description": "File or file glob pattern to search"}, + } + self.output_type = "any" + + # Validate parameters and initialize tool + super().__init__() + + # pylint: disable=W0221 + def forward(self, search, files): + """ + Greps a set of files for a pattern. + + Args: + search: search pattern + files: file or file glob pattern to search + + Returns: + {path: [matches]} + """ + + # Compile the search pattern + pattern = re.compile(search) + + # Format file pattern + files = os.path.join(files, "*") if os.path.isdir(files) else files + + # Iterate over each found file + results = {} + for path in glob.glob(files, recursive=True): + if os.path.isfile(path): + try: + with open(path, "r", encoding="utf-8") as f: + # Search for matches line-by-line + matches = [] + for line in f: + if pattern.search(line): + matches.append(line) + + if matches: + results[path] = matches + + except (UnicodeDecodeError, FileNotFoundError): + pass + + return results diff --git a/src/python/txtai/agent/tool/read.py b/src/python/txtai/agent/tool/read.py new file mode 100644 index 0000000..4403306 --- /dev/null +++ b/src/python/txtai/agent/tool/read.py @@ -0,0 +1,59 @@ +""" +Read imports +""" + +import re + +from smolagents import Tool + +from ...pipeline import Textractor + + +class ReadTool(Tool): + """ + The ReadTool retrieves file or url content. This tool automatically extracts text content from + binary files using the Textractor pipeline. + """ + + # pylint: disable=W0231 + def __init__(self, maxlength=40000): + """ + Creates a ReadTool. + + Args: + maxlength: Truncate content above this maxlength + """ + + # Tool parameters + self.name = "read" + self.description = ( + "Implementation of a file read tool. Returns file content. Also supports reading web content. " + "Use this tool to browse webpages in addition to reading files." + ) + self.inputs = {"path": {"type": "string", "description": "File path or url"}} + self.output_type = "any" + + # Create textractor instance + self.textractor = Textractor() + self.maxlength = maxlength + + # Validate parameters and initialize tool + super().__init__() + + # pylint: disable=W0221 + def forward(self, path): + """ + Reads content from path. + + Args: + path: file path or url + + Returns: + content + """ + + content = self.textractor(path) + content = re.sub(r"\n{3,}", "\n\n", content) + + # Truncate content to max length + return content[: self.maxlength] diff --git a/src/python/txtai/agent/tool/skill.py b/src/python/txtai/agent/tool/skill.py new file mode 100644 index 0000000..04fa764 --- /dev/null +++ b/src/python/txtai/agent/tool/skill.py @@ -0,0 +1,81 @@ +""" +Skill imports +""" + +from smolagents import Tool + +# Core library imports +from ...util import Library + +yaml = Library().yaml() + + +class SkillTool(Tool): + """ + A SkillTool loads a skill.md file. + """ + + # pylint: disable=W0231 + def __init__(self, path): + """ + Creates a SkillTool. + + Args: + path: skill file path + """ + + # Load skill.md + metadata, content = self.load(path) + + # Tool parameters + self.name = metadata["name"] + self.description = metadata["description"] + self.inputs = {"request": {"type": "string", "description": "The user requested action"}} + self.output_type = "any" + self.target = content + + # Validate parameters and initialize tool + super().__init__() + + # pylint: disable=W0221 + def forward(self, request): + """ + Searchs the skill markdown for the best answer. + + Args: + request: user request + + Returns: + result + """ + + return f"""Given the request {request}, find the best answer using the content below. + +{self.target} +""" + + def load(self, path): + """ + Loads a skill.md file. + + Args: + path: path to skill.md + + Returns: + metadata, content + """ + + # Read file content + with open(path, "r", encoding="utf-8") as f: + content = f.read() + + # Stores frontmatter metadata, if any + metadata = {} + + # Split by "---"" to separate frontmatter and markdown + if content.startswith("---"): + _, frontmatter, content = content.split("---", 2) + metadata = yaml.safe_load(frontmatter) + + # Return parsed metadata and content + return metadata, content diff --git a/src/python/txtai/agent/tool/todo.py b/src/python/txtai/agent/tool/todo.py new file mode 100644 index 0000000..aa784ce --- /dev/null +++ b/src/python/txtai/agent/tool/todo.py @@ -0,0 +1,40 @@ +""" +Todo imports +""" + +from smolagents import Tool + + +class TodoWriteTool(Tool): + """ + The TodoWriteTool plans for task execution. + """ + + # pylint: disable=W0231 + def __init__(self): + """ + Creates a TodoWriteTool. + """ + + # Tool parameters + self.name = "todowrite" + self.description = ( + "Implementation of a todo write tool. Generates a structured task list to help organize complex tasks. " + "Only use this tool for complex tasks with multiple steps. Skip for simple tasks that can be done easily." + ) + self.inputs = {"plan": {"type": "string", "description": "The task plan"}} + self.output_type = "any" + + # Validate parameters and initialize tool + super().__init__() + + # pylint: disable=W0221 + def forward(self, plan): + """ + Returns the plan. + + Returns: + plan + """ + + return plan diff --git a/src/python/txtai/agent/tool/write.py b/src/python/txtai/agent/tool/write.py new file mode 100644 index 0000000..6cbc9e8 --- /dev/null +++ b/src/python/txtai/agent/tool/write.py @@ -0,0 +1,42 @@ +""" +Write imports +""" + +from smolagents import Tool + + +class WriteTool(Tool): + """ + The WriteTool writes file content. + """ + + # pylint: disable=W0231 + def __init__(self): + """ + Creates a WriteTool. + """ + + # Tool parameters + self.name = "write" + self.description = "Implementation of a file write tool. Writes content to file." + self.inputs = { + "path": {"type": "string", "description": "Output file path"}, + "content": {"type": "string", "description": "File content to write"}, + } + self.output_type = "any" + + # Validate parameters and initialize tool + super().__init__() + + # pylint: disable=W0221 + def forward(self, path, content): + """ + Writes content to path. + + Args: + path: output file path + content: content to write + """ + + with open(path, "w", encoding="utf-8") as f: + f.write(content) diff --git a/src/python/txtai/ann/__init__.py b/src/python/txtai/ann/__init__.py new file mode 100644 index 0000000..8a54db1 --- /dev/null +++ b/src/python/txtai/ann/__init__.py @@ -0,0 +1,7 @@ +""" +ANN imports +""" + +from .base import ANN +from .dense import * +from .sparse import * diff --git a/src/python/txtai/ann/base.py b/src/python/txtai/ann/base.py new file mode 100644 index 0000000..15beefc --- /dev/null +++ b/src/python/txtai/ann/base.py @@ -0,0 +1,153 @@ +""" +ANN (Approximate Nearest Neighbor) module +""" + +import datetime +import platform + +from ..version import __version__ + + +class ANN: + """ + Base class for ANN instances. This class builds vector indexes to support similarity search. + The built-in ANN backends store ids and vectors. Content storage is supported via database instances. + """ + + def __init__(self, config): + """ + Creates a new ANN. + + Args: + config: index configuration parameters + """ + + # ANN index + self.backend = None + + # ANN configuration + self.config = config + + def load(self, path): + """ + Loads an ANN at path. + + Args: + path: path to load ann index + """ + + raise NotImplementedError + + def index(self, embeddings): + """ + Builds an ANN index. + + Args: + embeddings: embeddings array + """ + + raise NotImplementedError + + def append(self, embeddings): + """ + Append elements to an existing index. + + Args: + embeddings: embeddings array + """ + + raise NotImplementedError + + def delete(self, ids): + """ + Deletes elements from existing index. + + Args: + ids: ids to delete + """ + + raise NotImplementedError + + def search(self, queries, limit): + """ + Searches ANN index for query. Returns topn results. + + Args: + queries: queries array + limit: maximum results + + Returns: + query results + """ + + raise NotImplementedError + + def count(self): + """ + Number of elements in the ANN index. + + Returns: + count + """ + + raise NotImplementedError + + def save(self, path): + """ + Saves an ANN index at path. + + Args: + path: path to save ann index + """ + + raise NotImplementedError + + def close(self): + """ + Closes this ANN. + """ + + self.backend = None + + def setting(self, name, default=None): + """ + Looks up backend specific setting. + + Args: + name: setting name + default: default value when setting not found + + Returns: + setting value + """ + + # Get the backend-specific config object + backend = self.config.get(self.config["backend"]) + + # Get setting value, set default value if not found + setting = backend.get(name) if backend else None + return setting if setting or (backend and name in backend) else default + + def metadata(self, settings=None): + """ + Adds index build metadata. + + Args: + settings: index build settings + """ + + # ISO 8601 timestamp + create = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") + + # Set build metadata if this is not an update + if settings: + self.config["build"] = { + "create": create, + "python": platform.python_version(), + "settings": settings, + "system": f"{platform.system()} ({platform.machine()})", + "txtai": __version__, + } + + # Set last update date + self.config["update"] = create diff --git a/src/python/txtai/ann/dense/__init__.py b/src/python/txtai/ann/dense/__init__.py new file mode 100644 index 0000000..2526fd0 --- /dev/null +++ b/src/python/txtai/ann/dense/__init__.py @@ -0,0 +1,12 @@ +""" +Dense ANN imports +""" + +from .annoy import Annoy +from .factory import ANNFactory +from .faiss import Faiss +from .hnsw import HNSW +from .numpy import NumPy +from .pgvector import PGVector +from .torch import Torch +from .turbovec import TurboVec diff --git a/src/python/txtai/ann/dense/annoy.py b/src/python/txtai/ann/dense/annoy.py new file mode 100644 index 0000000..eca8e86 --- /dev/null +++ b/src/python/txtai/ann/dense/annoy.py @@ -0,0 +1,72 @@ +""" +Annoy module +""" + +# Conditional import +try: + from annoy import AnnoyIndex + + ANNOY = True +except ImportError: + ANNOY = False + +from ..base import ANN + + +# pylint: disable=W0223 +class Annoy(ANN): + """ + Builds an ANN index using the Annoy library. + """ + + def __init__(self, config): + super().__init__(config) + + if not ANNOY: + raise ImportError('Annoy is not available - install "ann" extra to enable') + + def load(self, path): + # Load index + self.backend = AnnoyIndex(self.config["dimensions"], self.config["metric"]) + self.backend.load(path) + + def index(self, embeddings): + # Inner product is equal to cosine similarity on normalized vectors + self.config["metric"] = "dot" + + # Create index + self.backend = AnnoyIndex(self.config["dimensions"], self.config["metric"]) + + # Add items - position in embeddings is used as the id + for x in range(embeddings.shape[0]): + self.backend.add_item(x, embeddings[x]) + + # Build index + ntrees = self.setting("ntrees", 10) + self.backend.build(ntrees) + + # Add index build metadata + self.metadata({"ntrees": ntrees}) + + def search(self, queries, limit): + # Lookup search k setting + searchk = self.setting("searchk", -1) + + # Annoy doesn't have a built in batch query method + results = [] + for query in queries: + # Run the query + ids, scores = self.backend.get_nns_by_vector(query, n=limit, search_k=searchk, include_distances=True) + + # Map results to [(id, score)] + results.append(list(zip(ids, scores))) + + return results + + def count(self): + # Number of items in index + return self.backend.get_n_items() + + def save(self, path): + # Write index + self.backend.save(path) diff --git a/src/python/txtai/ann/dense/factory.py b/src/python/txtai/ann/dense/factory.py new file mode 100644 index 0000000..69d09a0 --- /dev/null +++ b/src/python/txtai/ann/dense/factory.py @@ -0,0 +1,82 @@ +""" +Factory module +""" + +from ...util import Resolver + +from .annoy import Annoy +from .faiss import Faiss, FAISS +from .ggml import GGML +from .hnsw import HNSW +from .numpy import NumPy +from .pgvector import PGVector +from .sqlite import SQLite +from .torch import Torch +from .turbovec import TurboVec + + +class ANNFactory: + """ + Methods to create ANN indexes. + """ + + @staticmethod + def create(config): + """ + Create an ANN. + + Args: + config: index configuration parameters + + Returns: + ANN + """ + + # ANN instance + ann = None + backend = config.get("backend", "faiss" if FAISS else "numpy") + + # Create ANN instance + if backend == "annoy": + ann = Annoy(config) + elif backend == "faiss": + ann = Faiss(config) + elif backend == "hnsw": + ann = HNSW(config) + elif backend == "ggml": + ann = GGML(config) + elif backend == "numpy": + ann = NumPy(config) + elif backend == "pgvector": + ann = PGVector(config) + elif backend == "sqlite": + ann = SQLite(config) + elif backend == "torch": + ann = Torch(config) + elif backend == "turbovec": + ann = TurboVec(config) + else: + ann = ANNFactory.resolve(backend, config) + + # Store config back + config["backend"] = backend + + return ann + + @staticmethod + def resolve(backend, config): + """ + Attempt to resolve a custom backend. + + Args: + backend: backend class + config: index configuration parameters + + Returns: + ANN + """ + + try: + return Resolver()(backend)(config) + except Exception as e: + raise ImportError(f"Unable to resolve ann backend: '{backend}'") from e diff --git a/src/python/txtai/ann/dense/faiss.py b/src/python/txtai/ann/dense/faiss.py new file mode 100644 index 0000000..7cdcdf9 --- /dev/null +++ b/src/python/txtai/ann/dense/faiss.py @@ -0,0 +1,252 @@ +""" +Faiss module +""" + +import math +import os +import platform + +# pylint: disable=C0413 +if platform.system() == "Darwin" or os.name == "nt": + # Workaround for a Faiss issue causing segmentation faults. See txtai FAQ for more. + os.environ["OMP_NUM_THREADS"] = os.environ.get("OMP_NUM_THREADS", "1") + + # Workaround for a Faiss issue with OMP: Error #15. See txtai FAQ for more. + os.environ["KMP_DUPLICATE_LIB_OK"] = os.environ.get("KMP_DUPLICATE_LIB_OK", "TRUE") + +# Conditional import +try: + from faiss import index_factory, IO_FLAG_MMAP, METRIC_INNER_PRODUCT, read_index, write_index + from faiss import index_binary_factory, read_index_binary, write_index_binary, IndexBinaryIDMap + + FAISS = True +except ImportError: + FAISS = False + +from ..base import ANN + +# Core library imports +from ...util import Library + +np = Library().numpy() + + +class Faiss(ANN): + """ + Builds an ANN index using the Faiss library. + """ + + def __init__(self, config): + super().__init__(config) + + if not FAISS: + raise ImportError("Faiss is not available - install faiss-cpu to enable") + + # Scalar quantization + quantize = self.config.get("quantize") + self.qbits = quantize if quantize and isinstance(quantize, int) and not isinstance(quantize, bool) else None + + def load(self, path): + # Get read function + readindex = read_index_binary if self.qbits else read_index + + # Load index + self.backend = readindex(path, IO_FLAG_MMAP if self.setting("mmap") is True else 0) + + def index(self, embeddings): + # Compute model training size + train, sample = embeddings, self.setting("sample") + if sample: + # Get sample for training + rng = np.random.default_rng(0) + indices = sorted(rng.choice(train.shape[0], int(sample * train.shape[0]), replace=False, shuffle=False)) + train = train[indices] + + # Configure embeddings index. Inner product is equal to cosine similarity on normalized vectors. + params = self.configure(embeddings.shape[0], train.shape[0]) + + # Create index + self.backend = self.create(embeddings, params) + + # Train model + self.backend.train(train) + + # Add embeddings - position in embeddings is used as the id + self.backend.add_with_ids(embeddings, np.arange(embeddings.shape[0], dtype=np.int64)) + + # Add id offset and index build metadata + self.config["offset"] = embeddings.shape[0] + self.metadata({"components": params}) + + def append(self, embeddings): + new = embeddings.shape[0] + + # Append new ids - position in embeddings + existing offset is used as the id + self.backend.add_with_ids(embeddings, np.arange(self.config["offset"], self.config["offset"] + new, dtype=np.int64)) + + # Update id offset and index metadata + self.config["offset"] += new + self.metadata() + + def delete(self, ids): + # Remove specified ids + self.backend.remove_ids(np.array(ids, dtype=np.int64)) + + def search(self, queries, limit): + # Set nprobe and nflip search parameters, if available + if hasattr(self.backend, "nprobe"): + self.backend.nprobe = self.nprobe() + + if hasattr(self.backend, "nflip"): + self.backend.nflip = self.setting("nflip", self.nprobe()) + + # Run the query + scores, ids = self.backend.search(queries, limit) + + # Map results to [(id, score)] + results = [] + for x, score in enumerate(scores): + # Transform scores and add results + results.append(list(zip(ids[x].tolist(), self.scores(score)))) + + return results + + def count(self): + return self.backend.ntotal + + def save(self, path): + # Get write function + writeindex = write_index_binary if self.qbits else write_index + + # Write index + writeindex(self.backend, path) + + def configure(self, count, train): + """ + Configures settings for a new index. + + Args: + count: initial number of embeddings rows + train: number of rows selected for model training + + Returns: + user-specified or generated components setting + """ + + # Lookup components setting + components = self.setting("components") + + if components: + # Format and return components string + return self.components(components, train) + + # Derive quantization. Prefer backend-specific setting. Fallback to root-level parameter. + quantize = self.setting("quantize", self.config.get("quantize")) + quantize = 8 if isinstance(quantize, bool) else quantize + + # Get storage setting + storage = f"SQ{quantize}" if quantize else "Flat" + + # Small index, use storage directly with IDMap + if count <= 5000: + return "BFlat" if self.qbits else f"IDMap,{storage}" + + x = self.cells(train) + components = f"BIVF{x}" if self.qbits else f"IVF{x},{storage}" + + return components + + def create(self, embeddings, params): + """ + Creates a new index. + + Args: + embeddings: embeddings to index + params: index parameters + + Returns: + new index + """ + + # Create binary index + if self.qbits: + index = index_binary_factory(embeddings.shape[1] * 8, params) + + # Wrap with BinaryIDMap, if necessary + if any(x in params for x in ["BFlat", "BHNSW"]): + index = IndexBinaryIDMap(index) + + return index + + # Create standard float index + return index_factory(embeddings.shape[1], params, METRIC_INNER_PRODUCT) + + def cells(self, count): + """ + Calculates the number of IVF cells for an IVF index. + + Args: + count: number of embeddings rows + + Returns: + number of IVF cells + """ + + # Calculate number of IVF cells where x = min(4 * sqrt(embeddings count), embeddings count / 39) + # Faiss requires at least 39 points per cluster + return max(min(round(4 * math.sqrt(count)), int(count / 39)), 1) + + def components(self, components, train): + """ + Formats a components string. This method automatically calculates the optimal number of IVF cells, if omitted. + + Args: + components: input components string + train: number of rows selected for model training + + Returns: + formatted components string + """ + + # Optimal number of IVF cells + x = self.cells(train) + + # Add number of IVF cells, if missing + components = [f"IVF{x}" if component == "IVF" else component for component in components.split(",")] + + # Return components string + return ",".join(components) + + def nprobe(self): + """ + Gets or derives the nprobe search parameter. + + Returns: + nprobe setting + """ + + # Get size of embeddings index + count = self.count() + + default = 6 if count <= 5000 else round(self.cells(count) / 16) + return self.setting("nprobe", default) + + def scores(self, scores): + """ + Calculates the index score from the input score. This method returns the hamming score + (1.0 - (hamming distance / total number of bits)) for binary indexes and the input + scores otherwise. + + Args: + scores: input scores + + Returns: + index scores + """ + + # Calculate hamming score, bound between 0.0 - 1.0 + if self.qbits: + return np.clip(1.0 - (scores / (self.config["dimensions"] * 8)), 0.0, 1.0).tolist() + + # Standard scoring + return scores.tolist() diff --git a/src/python/txtai/ann/dense/ggml.py b/src/python/txtai/ann/dense/ggml.py new file mode 100644 index 0000000..db4a693 --- /dev/null +++ b/src/python/txtai/ann/dense/ggml.py @@ -0,0 +1,574 @@ +""" +GGML module +""" + +import ctypes +import os + +# Conditional import +try: + import ggml + from ggml import utils + + LIBGGML = True +except ImportError: + LIBGGML = False + +from ..base import ANN + +# Core library imports +from ...util import Library + +np = Library().numpy() + + +class GGML(ANN): + """ + Builds an ANN index backed by GGML. + """ + + def __init__(self, config): + super().__init__(config) + + if not LIBGGML: + raise ImportError('GGML is not available - install "ann" extra to enable') + + def load(self, path): + # Create GGML Tensors + self.backend = GGMLTensors(self.setting("gpu", True), self.setting("querysize", 64), self.setting("quantize")) + + # Load existing GGUF file + self.backend.load(path) + + def index(self, embeddings): + # Create GGML Tensors + self.backend = GGMLTensors(self.setting("gpu", True), self.setting("querysize", 64), self.setting("quantize")) + + # Add embeddings data + self.backend.index(embeddings) + + # Add id offset and index build metadata + self.config["offset"] = embeddings.shape[0] + self.metadata(self.settings()) + + def append(self, embeddings): + # Append embeddings to existing tensors + self.backend.append(embeddings) + + # Update id offset and index metadata + self.config["offset"] += embeddings.shape[0] + self.metadata() + + def delete(self, ids): + self.backend.delete(ids) + + def search(self, queries, limit): + scores = self.backend.search(queries) + + # Get topn ids + ids = np.argsort(-scores)[:, :limit] + + # Map results to [(id, score)] + results = [] + for x, score in enumerate(scores): + # Add results + results.append(list(zip(ids[x].tolist(), score[ids[x]].tolist()))) + + return results + + def count(self): + return self.backend.count() + + def save(self, path): + self.backend.save(path) + + def close(self): + # Cleanup resources before setting backend to None + if self.backend: + self.backend.close() + + # Parent logic + super().close() + + def settings(self): + """ + Returns settings for this instance. + + Returns: + dict + """ + + return {"ggml": ggml.__version__} + + +class GGMLTensors: + """ + Interface to read and write GGML tensor data. + """ + + def __init__(self, gpu, querysize, quantize): + """ + Creates a new GGMLTensors. + + Args: + gpu: if GPU should be used + querysize: query buffer size + quantize: data quantization setting + """ + + # Settings + self.gpu, self.querysize, self.quantize = gpu, querysize, quantize + + # GGML parameters + self.context, self.backend = None, None + self.buffer, self.queries, self.data, self.deletes = None, None, None, [] + self.allocator, self.graph, self.output = None, None, None + + def __del__(self): + """ + Ensure resources are cleaned up. + """ + + # Cleanup resources + self.close() + + def load(self, path): + """ + Loads GGML tensors from a GGUF file at path. + + Args: + path: path to GGUF file + """ + + # Initialize GGML objects + self.context = self.createcontext() + self.backend = self.createbackend() + self.allocator = self.createallocator(self.backend) + + # Temporary context for GGUF + context = ctypes.c_void_p() + + # Cast as ggml_context** + params = ggml.gguf_init_params(ctx=ctypes.pointer(context), no_alloc=False) + + # Load GGUF file + gguf = ggml.gguf_init_from_file(path.encode("utf-8"), params) + + # Load tensors from GGUF file + self.loadtensors(context) + + # Create graph operation + self.graph, self.output = self.creategraph() + + # Cleanup temporary resources + ggml.gguf_free(gguf) + ggml.ggml_free(context) + + def index(self, embeddings): + """ + Indexes embeddings as GGML tensors. + + Args: + embeddings: embeddings array + """ + + # Initialize GGML objects + self.context = self.createcontext() + self.backend = self.createbackend() + self.allocator = self.createallocator(self.backend) + + # Create query buffer and data tensors + self.createtensors(embeddings) + + # Create graph operation + self.graph, self.output = self.creategraph() + + def append(self, embeddings): + """ + Appends embeddings to GGML tensors. + + Args: + embeddings: embeddings array + """ + + # Initialize GGML objects + context = self.createcontext() + backend = self.createbackend() + allocator = self.createallocator(backend) + + # Merge embeddings tensors + buffer, queries, data = self.mergetensors(context, backend, embeddings) + deletes = self.deletes + + # Free existing objects + self.close() + + # Store new objects + self.context, self.backend, self.allocator = (context, backend, allocator) + self.buffer, self.queries, self.data, self.deletes = (buffer, queries, data, deletes) + + # Create graph operation + self.graph, self.output = self.creategraph() + + def delete(self, ids): + """ + Delete ids from tensors. + + Args: + ids: ids to delete + """ + + shape = utils.get_shape(self.data) + + # Filter any index greater than size of array + ids = [x for x in ids if x < shape[1]] + self.deletes.extend(ids) + + def search(self, queries): + """ + Searches GGML tensors for the best query matches. + + Args: + queries: queries array + + Returns: + query results + """ + + # Process queries up to the query buffer size batches + batches = [] + for batch in self.chunk(queries): + # Copy queries to buffer + ggml.ggml_backend_tensor_set( + self.queries, + ctypes.cast(batch.ctypes.data, ctypes.c_void_p), + 0, + batch.nbytes, + ) + + # Run matrix multiplication operation + ggml.ggml_backend_graph_compute(self.backend, self.graph) + + # Get size of embeddings data + size = utils.get_shape(self.data) + size = size[1] if len(size) > 1 else 1 + + # Get and return results + results = np.zeros((batch.shape[0], size), dtype=np.float32) + ggml.ggml_backend_tensor_get(self.output, ctypes.cast(results.ctypes.data, ctypes.c_void_p), 0, results.nbytes) + + # Clear deleted rows and add results + results[:, self.deletes] = 0 + batches.append(results) + + # Combine batches and return as single result + return np.concatenate(batches, axis=0) + + def count(self): + """ + Number of elements in this GGML tensors. + + Returns: + count + """ + + return utils.get_shape(self.data)[1] - len(self.deletes) if self.data else 0 + + def save(self, path): + """ + Saves GGML tensors as GGUF to path. + + Args: + path: path to save + """ + + # Temporary buffer + buffer = None + + # Init and save data tensor + gguf = ggml.gguf_init_empty() + + # Add the data tensor + ggml.ggml_set_name(self.data, b"data") + ggml.gguf_add_tensor(gguf, self.data) + + # Optionally create and add the deletes tensor + if self.deletes: + deletes = np.array(self.deletes, dtype=np.int64) + tensor = ggml.ggml_new_tensor_1d(self.context, ggml.GGML_TYPE_I64, deletes.shape[0]) + buffer = ggml.ggml_backend_alloc_ctx_tensors(self.context, self.backend) + + ggml.ggml_backend_tensor_set( + tensor, + ctypes.cast(deletes.ctypes.data, ctypes.c_void_p), + 0, + deletes.nbytes, + ) + ggml.ggml_set_name(tensor, b"deletes") + ggml.gguf_add_tensor(gguf, tensor) + + # Write file and free resources + ggml.gguf_write_to_file(gguf, path.encode("utf-8"), False) + ggml.gguf_free(gguf) + + if buffer: + ggml.ggml_backend_buffer_free(buffer) + + def close(self): + """ + Closes this instance and frees resources. + """ + + if self.buffer: + ggml.ggml_backend_buffer_free(self.buffer) + self.buffer, self.queries, self.data, self.deletes = None, None, None, [] + + if self.allocator: + ggml.ggml_gallocr_free(self.allocator) + self.allocator, self.graph = None, None + + if self.backend: + ggml.ggml_backend_free(self.backend) + self.backend = None + + if self.context: + # Free quantization memory + ggml.ggml_quantize_free() + + # Free context + ggml.ggml_free(self.context) + self.context = None + + def createcontext(self): + """ + Creates a new GGML context. + + Returns: + context + """ + + # Base tensor storage + size = ggml.ggml_tensor_overhead() * 100 + + # Graph storage + size += ggml.ggml_tensor_overhead() * ggml.GGML_DEFAULT_GRAPH_SIZE + ggml.ggml_graph_overhead() + + # Create GGML context + params = ggml.ggml_init_params(mem_size=size, no_alloc=True) + context = ggml.ggml_init(params) + + return context + + def createbackend(self): + """ + Creates a new GGML backend. + + Returns: + backend + """ + + # Attempt to create an accelerated backend + backend = ggml.ggml_backend_init_by_type(ggml.GGML_BACKEND_DEVICE_TYPE_GPU, None) if self.gpu else None + + # Fall back to CPU backend + if not backend: + backend = ggml.ggml_backend_cpu_init() + ggml.ggml_backend_cpu_set_n_threads(backend, os.cpu_count()) + + return backend + + def createallocator(self, backend): + """ + Creates a new GGML allocator. + + Args: + backend: backend device + + Returns: + allocator + """ + + return ggml.ggml_gallocr_new(ggml.ggml_backend_get_default_buffer_type(backend)) + + def createtensors(self, data): + """ + Creates query and data tensors. + + Args: + data: embeddings data + """ + + # Derive embeddings data tensor type + tensortype = self.tensortype(data) + + # Queries + self.queries = ggml.ggml_new_tensor_2d(self.context, ggml.GGML_TYPE_F32, data.shape[1], self.querysize) + + # Embeddings data + self.data = ggml.ggml_new_tensor_2d(self.context, tensortype, data.shape[1], data.shape[0]) + + # Create buffer + self.buffer = ggml.ggml_backend_alloc_ctx_tensors(self.context, self.backend) + + # Copy embeddings data + self.copy(data, self.data, tensortype, 0) + + def loadtensors(self, context): + """ + Loads existing tensors from context. + + Args: + context: ggml context + """ + + # Load data tensor + data = ggml.ggml_get_tensor(context, b"data") + if data: + # Queries + shape = utils.get_shape(data) + self.queries = ggml.ggml_new_tensor_2d(self.context, ggml.GGML_TYPE_F32, shape[0], self.querysize) + + # Embeddings data + self.data = ggml.ggml_dup_tensor(self.context, data) + + # Create buffer + self.buffer = ggml.ggml_backend_alloc_ctx_tensors(self.context, self.backend) + + # Copy tensor data to backend + ggml.ggml_backend_tensor_set(self.data, ggml.ggml_get_data(data), 0, ggml.ggml_nbytes(data)) + + # Load deletes tensor + data = ggml.ggml_get_tensor(context, b"deletes") + if data: + # Convert to a NumPy array + shape = utils.get_shape(data) + deletes = np.ctypeslib.as_array(ctypes.cast(ggml.ggml_get_data(data), ctypes.POINTER(ctypes.c_int64)), (shape[0],)) + self.deletes = deletes.tolist() + + def mergetensors(self, context, backend, data): + """ + Merges new embeddings data. + + Args: + context: new context + backend: new backend + data: embeddings data + + Returns: + buffer, queries, data + """ + + # Derive embeddings data tensor type + tensortype = self.tensortype(data) + + # Queries + queries = ggml.ggml_new_tensor_2d(context, ggml.GGML_TYPE_F32, data.shape[1], self.querysize) + + # Embeddings data with space for both existing and new data + shape = utils.get_shape(self.data) + merge = ggml.ggml_new_tensor_2d(context, tensortype, data.shape[1], data.shape[0] + shape[1]) + + # Create new buffer + buffer = ggml.ggml_backend_alloc_ctx_tensors(context, backend) + + # Copy existing embeddings data + self.copy(self.data, merge, tensortype, 0) + + # Copy new embeddings data + self.copy(data, merge, tensortype, ggml.ggml_nbytes(self.data)) + + return buffer, queries, merge + + def creategraph(self): + """ + Creates a new GGML graph. + + Returns: + graph + """ + + # Create matrix multiply graph operation + graph = ggml.ggml_new_graph(self.context) + + # Graph operation + output = ggml.ggml_mul_mat(self.context, self.data, self.queries) + + # Setup and allocate graph storage + ggml.ggml_build_forward_expand(graph, output) + ggml.ggml_gallocr_alloc_graph(self.allocator, graph) + + return graph, output + + def tensortype(self, data): + """ + Gets the best matching tensor type for input data. + + Args: + data: embeddings data + + Returns: + best matching GGML data type + """ + + # Read tensor type + tensortype = self.quantize + tensortype = "Q8_0" if isinstance(tensortype, bool) else f"Q{int(tensortype)}_0" if isinstance(tensortype, int) else tensortype + tensortype = tensortype.upper() if tensortype else "F32" + + # Validate tensor type + if not hasattr(ggml, f"GGML_TYPE_{tensortype}"): + raise ValueError(f"Invalid tensor type {tensortype}") + + # Get tensor type + tensortype = getattr(ggml, f"GGML_TYPE_{tensortype}") + + # Validate quantization block size + blocksize = ggml.ggml_blck_size(tensortype) + if data.shape[1] % blocksize != 0: + raise ValueError( + f'Invalid quantization configuration "{self.quantize}" with {data.shape[1]} dimensions. Must be a multiple of {blocksize}.' + ) + + return tensortype + + def copy(self, inputs, outputs, tensortype, offset): + """ + Copies input data to backend. Quantizes to desired tensor type, if necessary. + + Args: + inputs: input tensor + outputs: output tensor + tensortype: desired tensor type + offset: data offset index for storage into outputs + """ + + if not isinstance(inputs, np.ndarray): + # GGML tensor + work, size = ggml.ggml_get_data(inputs), ggml.ggml_nbytes(inputs) + elif tensortype == ggml.GGML_TYPE_F32: + # No quantization needed + work, size = inputs.ctypes.data, inputs.nbytes + else: + # Work array will be garbage collected by Python + work = (ctypes.c_float * inputs.shape[0] * inputs.shape[1])() + + # Quantize vector data + size = ggml.ggml_quantize_chunk( + tensortype, ctypes.cast(inputs.ctypes.data, ctypes.POINTER(ctypes.c_float)), work, 0, inputs.shape[0], inputs.shape[1], None + ) + + # Copy data to tensor + ggml.ggml_backend_tensor_set(outputs, work, offset, size) + + def chunk(self, queries): + """ + Splits quries into separate batch sizes specified by size. + + Args: + queries: queries + + Returns: + list of evenly sized batches with the last batch having the remaining elements + """ + + return [queries[x : x + self.querysize] for x in range(0, len(queries), self.querysize)] diff --git a/src/python/txtai/ann/dense/hnsw.py b/src/python/txtai/ann/dense/hnsw.py new file mode 100644 index 0000000..598b6b1 --- /dev/null +++ b/src/python/txtai/ann/dense/hnsw.py @@ -0,0 +1,107 @@ +""" +HNSW module +""" + +# Conditional import +try: + # pylint: disable=E0611 + from hnswlib import Index + + HNSWLIB = True +except ImportError: + HNSWLIB = False + +from ..base import ANN + +# Core library imports +from ...util import Library + +np = Library().numpy() + + +class HNSW(ANN): + """ + Builds an ANN index using the hnswlib library. + """ + + def __init__(self, config): + super().__init__(config) + + if not HNSWLIB: + raise ImportError('HNSW is not available - install "ann" extra to enable') + + def load(self, path): + # Load index + self.backend = Index(dim=self.config["dimensions"], space=self.config["metric"]) + self.backend.load_index(path) + + def index(self, embeddings): + # Inner product is equal to cosine similarity on normalized vectors + self.config["metric"] = "ip" + + # Lookup index settings + efconstruction = self.setting("efconstruction", 200) + m = self.setting("m", 16) + seed = self.setting("randomseed", 100) + + # Create index + self.backend = Index(dim=self.config["dimensions"], space=self.config["metric"]) + self.backend.init_index(max_elements=embeddings.shape[0], ef_construction=efconstruction, M=m, random_seed=seed) + + # Add items - position in embeddings is used as the id + self.backend.add_items(embeddings, np.arange(embeddings.shape[0], dtype=np.int64)) + + # Add id offset, delete counter and index build metadata + self.config["offset"] = embeddings.shape[0] + self.config["deletes"] = 0 + self.metadata({"efconstruction": efconstruction, "m": m, "seed": seed}) + + def append(self, embeddings): + new = embeddings.shape[0] + + # Resize index + self.backend.resize_index(self.config["offset"] + new) + + # Append new ids - position in embeddings + existing offset is used as the id + self.backend.add_items(embeddings, np.arange(self.config["offset"], self.config["offset"] + new, dtype=np.int64)) + + # Update id offset and index metadata + self.config["offset"] += new + self.metadata() + + def delete(self, ids): + # Mark elements as deleted to omit from search results + for uid in ids: + try: + self.backend.mark_deleted(uid) + self.config["deletes"] += 1 + except RuntimeError: + # Ignore label not found error + continue + + def search(self, queries, limit): + # Set ef query param + ef = self.setting("efsearch") + if ef: + self.backend.set_ef(ef) + + # Run the query + ids, distances = self.backend.knn_query(queries, k=limit) + + # Map results to [(id, score)] + results = [] + for x, distance in enumerate(distances): + # Convert distances to similarity scores + scores = [1 - d for d in distance.tolist()] + + # Build (id, score) tuples, convert np.int64 to python int + results.append(list(zip(ids[x].tolist(), scores))) + + return results + + def count(self): + return self.backend.get_current_count() - self.config["deletes"] + + def save(self, path): + # Write index + self.backend.save_index(path) diff --git a/src/python/txtai/ann/dense/numpy.py b/src/python/txtai/ann/dense/numpy.py new file mode 100644 index 0000000..19ac6dc --- /dev/null +++ b/src/python/txtai/ann/dense/numpy.py @@ -0,0 +1,204 @@ +""" +NumPy module +""" + +from ...serialize import SerializeFactory + +from ..base import ANN + +# Core library imports +from ...util import Library + +library = Library() +np = library.numpy() +safetensors = library.safetensors() + + +class NumPy(ANN): + """ + Builds an ANN index backed by a NumPy array. + """ + + def __init__(self, config): + super().__init__(config) + + # Array function definitions + self.all, self.cat, self.dot, self.zeros = np.all, np.concatenate, np.dot, np.zeros + self.argsort, self.xor, self.clip = np.argsort, np.bitwise_xor, np.clip + + # Scalar quantization + quantize = self.config.get("quantize") + self.qbits = quantize if quantize and isinstance(quantize, int) and not isinstance(quantize, bool) else None + + def load(self, path): + # Load array from file + try: + if self.setting("safetensors"): + data = self.loadsafetensors(path).get("data") + else: + data = np.load(path, allow_pickle=False) + + self.backend = self.tensor(data) + except ValueError: + # Backwards compatible support for previously pickled data + self.backend = self.tensor(SerializeFactory.create("pickle").load(path)) + + def index(self, embeddings): + # Create index + self.backend = self.tensor(embeddings) + + # Add id offset and index build metadata + self.config["offset"] = embeddings.shape[0] + self.metadata(self.settings()) + + def append(self, embeddings): + # Append new data to array + self.backend = self.cat((self.backend, self.tensor(embeddings)), axis=0) + + # Update id offset and index metadata + self.config["offset"] += embeddings.shape[0] + self.metadata() + + def delete(self, ids): + # Filter any index greater than size of array + ids = [x for x in ids if x < self.backend.shape[0]] + + # Clear specified ids + self.backend[ids] = self.tensor(self.zeros((len(ids), self.backend.shape[1]))) + + def search(self, queries, limit): + if self.qbits: + # Calculate hamming score for integer vectors + scores = self.hammingscore(queries) + else: + # Dot product on normalized vectors is equal to cosine similarity + scores = self.dot(self.tensor(queries), self.backend.T) + + # Get topn ids + ids = self.argsort(-scores)[:, :limit] + + # Map results to [(id, score)] + results = [] + for x, score in enumerate(scores): + # Add results + results.append(list(zip(ids[x].tolist(), score[ids[x]].tolist()))) + + return results + + def count(self): + # Get count of non-zero rows (ignores deleted rows) + return self.backend[~self.all(self.backend == 0, axis=1)].shape[0] + + def save(self, path): + # Save array to file. Use stream to prevent ".npy" suffix being added. + if self.setting("safetensors"): + self.savesafetensors({"data": self.numpy(self.backend)}, path) + else: + with open(path, "wb") as handle: + np.save(handle, self.numpy(self.backend), allow_pickle=False) + + def tensor(self, array): + """ + Handles backend-specific code such as loading to a GPU device. + + Args: + array: data array + + Returns: + array with backend-specific logic applied + """ + + return array + + def numpy(self, array): + """ + Handles backend-specific code to convert an array to numpy + + Args: + array: data array + + Returns: + numpy array + """ + + return array + + def totype(self, array, dtype): + """ + Casts array to dtype. + + Args: + array: input array + dtype: dtype + + Returns: + array cast as dtype + """ + + return np.int64(array) if dtype == np.int64 else array + + def settings(self): + """ + Returns settings for this array. + + Returns: + dict + """ + + return {"numpy": np.__version__} + + def loadsafetensors(self, path): + """ + Loads data from a safetensors file. + + Args: + path: path to safetensors file + + Returns: + dict with metadata + tensors + """ + + # Merge metadata and tensors into single dictionary + with safetensors.safe_open(path, framework="np") as f: + return {**(f.metadata() if f.metadata() else {}), **{k: f.get_tensor(k) for k in f.keys()}} + + def savesafetensors(self, data, path, metadata=None): + """ + Saves data and metadata to a safetensors file. + + Args: + data: tensors to save + path: output file + metadata: additional metadata to save + """ + + safetensors.numpy.save_file(data, path, metadata) + + def hammingscore(self, queries): + """ + Calculates a hamming distance score. + + This is defined as: + + score = 1.0 - (hamming distance / total number of bits) + + Args: + queries: queries array + + Returns: + scores + """ + + # Build table of number of bits for each distinct uint8 value + table = 1 << np.arange(8) + table = self.tensor(np.array([np.count_nonzero(x & table) for x in np.arange(256)])) + + # Number of different bits + delta = self.xor(self.tensor(queries[:, None]), self.backend) + + # Cast to long array + delta = self.totype(delta, np.int64) + + # Calculate score as 1.0 - percentage of different bits + # Bound score from 0 to 1 + return self.clip(1.0 - (table[delta].sum(axis=2) / (self.config["dimensions"] * 8)), 0.0, 1.0) diff --git a/src/python/txtai/ann/dense/pgvector.py b/src/python/txtai/ann/dense/pgvector.py new file mode 100644 index 0000000..09d4b91 --- /dev/null +++ b/src/python/txtai/ann/dense/pgvector.py @@ -0,0 +1,326 @@ +""" +PGVector module +""" + +import os + +# Conditional import +try: + from pgvector.sqlalchemy import BIT, HALFVEC, VECTOR + + from sqlalchemy import create_engine, delete, func, text, Column, Index, Integer, MetaData, StaticPool, Table + from sqlalchemy.orm import Session + from sqlalchemy.schema import CreateSchema + + PGVECTOR = True +except ImportError: + PGVECTOR = False + +from ..base import ANN + +# Core library imports +from ...util import Library + +np = Library().numpy() + + +# pylint: disable=R0904 +class PGVector(ANN): + """ + Builds an ANN index backed by a Postgres database. + """ + + def __init__(self, config): + super().__init__(config) + + if not PGVECTOR: + raise ImportError('PGVector is not available - install "ann" extra to enable') + + # Database connection + self.engine, self.database, self.connection, self.table = None, None, None, None + + # Scalar quantization + quantize = self.config.get("quantize") + self.qbits = quantize if quantize and isinstance(quantize, int) and not isinstance(quantize, bool) else None + + def load(self, path): + # Initialize tables + self.initialize() + + def index(self, embeddings): + # Initialize tables + self.initialize(recreate=True) + + # Prepare embeddings and insert rows + self.database.execute(self.table.insert(), [{"indexid": x, "embedding": self.prepare(row)} for x, row in enumerate(embeddings)]) + + # Create index + self.createindex() + + # Add id offset and index build metadata + self.config["offset"] = embeddings.shape[0] + self.metadata(self.settings()) + + def append(self, embeddings): + # Prepare embeddings and insert rows + self.database.execute( + self.table.insert(), [{"indexid": x + self.config["offset"], "embedding": self.prepare(row)} for x, row in enumerate(embeddings)] + ) + + # Update id offset and index metadata + self.config["offset"] += embeddings.shape[0] + self.metadata() + + def delete(self, ids): + self.database.execute(delete(self.table).where(self.table.c["indexid"].in_(ids))) + + def search(self, queries, limit): + results = [] + for query in queries: + # Run query + query = self.database.query(self.table.c["indexid"], self.query(query)).order_by("score").limit(limit) + + # Calculate and collect scores + results.append([(indexid, self.score(score)) for indexid, score in query]) + + return results + + def count(self): + # pylint: disable=E1102 + return self.database.query(func.count(self.table.c["indexid"])).scalar() + + def save(self, path): + # Commit session and connection + self.database.commit() + self.connection.commit() + + def close(self): + # Parent logic + super().close() + + # Close database connection + if self.database: + self.database.close() + self.engine.dispose() + + def initialize(self, recreate=False): + """ + Initializes a new database session. + + Args: + recreate: Recreates the database tables if True + """ + + # Connect to database + self.connect() + + # Set the database schema + self.schema() + + # Table name + table = self.setting("table", self.defaulttable()) + + # Create vectors table object + self.table = Table(table, MetaData(), Column("indexid", Integer, primary_key=True, autoincrement=False), Column("embedding", self.column())) + + # Drop table, if necessary + if recreate: + self.table.drop(self.connection, checkfirst=True) + + # Create table, if necessary + self.table.create(self.connection, checkfirst=True) + + def createindex(self): + """ + Creates a index with the current settings. + """ + + # Table name + table = self.setting("table", self.defaulttable()) + + # Create ANN index - inner product is equal to cosine similarity on normalized vectors + index = Index( + f"{table}-index", + self.table.c["embedding"], + postgresql_using="hnsw", + postgresql_with=self.settings(), + postgresql_ops={"embedding": self.operation()}, + ) + + # Create or recreate index + index.drop(self.connection, checkfirst=True) + index.create(self.connection, checkfirst=True) + + def connect(self): + """ + Establishes a database connection. Cleans up any existing database connection first. + """ + + # Close existing connection + if self.database: + self.close() + + # Create engine + self.engine = create_engine(self.url(), poolclass=StaticPool, echo=False) + self.connection = self.engine.connect() + + # Start database session + self.database = Session(self.connection) + + # Initialize pgvector extension + self.sqldialect(text("CREATE EXTENSION IF NOT EXISTS vector")) + + def schema(self): + """ + Sets the database schema, if available. + """ + + # Set default schema, if necessary + schema = self.setting("schema") + if schema: + with self.engine.begin(): + self.sqldialect(CreateSchema(schema, if_not_exists=True)) + + self.sqldialect(text("SET search_path TO :schema,public"), {"schema": schema}) + + def settings(self): + """ + Returns settings for this index. + + Returns: + dict + """ + + return {"m": self.setting("m", 16), "ef_construction": self.setting("efconstruction", 200)} + + def sqldialect(self, sql, parameters=None): + """ + Executes a SQL statement based on the current SQL dialect. + + Args: + sql: SQL to execute + parameters: optional bind parameters + """ + + args = (sql, parameters) if self.engine.dialect.name == "postgresql" else (text("SELECT 1"),) + self.database.execute(*args) + + def defaulttable(self): + """ + Returns the default table name. + + Returns: + default table name + """ + + return "vectors" + + def url(self): + """ + Reads the database url parameter. + + Returns: + database url + """ + + return self.setting("url", os.environ.get("ANN_URL")) + + def column(self): + """ + Gets embedding column for the current settings. + + Returns: + embedding column definition + """ + + if self.qbits: + # If quantization is set, always return BIT vectors + return BIT(self.config["dimensions"] * 8) + + if self.setting("precision") == "half": + # 16-bit HALF precision vectors + return HALFVEC(self.config["dimensions"]) + + # Default is full 32-bit FULL precision vectors + return VECTOR(self.config["dimensions"]) + + def operation(self): + """ + Gets the index operation for the current settings. + + Returns: + index operation + """ + + if self.qbits: + # If quantization is set, always return BIT vectors + return "bit_hamming_ops" + + if self.setting("precision") == "half": + # 16-bit HALF precision vectors + return "halfvec_ip_ops" + + # Default is full 32-bit FULL precision vectors + return "vector_ip_ops" + + def prepare(self, data): + """ + Prepares data for the embeddings column. This method returns a bit string for bit vectors and + the input data unmodified for float vectors. + + Args: + data: input data + + Returns: + data ready for the embeddings column + """ + + # Transform to a bit string when vector quantization is enabled + if self.qbits: + return "".join(np.where(np.unpackbits(data), "1", "0")) + + # Return original data + return data + + def query(self, query): + """ + Creates a query statement from an input query. This method uses hamming distance for bit vectors and + the max_inner_product for float vectors. + + Args: + query: input query + + Returns: + query statement + """ + + # Prepare query embeddings + query = self.prepare(query) + + # Bit vector query + if self.qbits: + return self.table.c["embedding"].hamming_distance(query).label("score") + + # Float vector query + return self.table.c["embedding"].max_inner_product(query).label("score") + + def score(self, score): + """ + Calculates the index score from the input score. This method returns the hamming score + (1.0 - (hamming distance / total number of bits)) for bit vectors and the -score for + float vectors. + + Args: + score: input score + + Returns: + index score + """ + + # Calculate hamming score as 1.0 - (hamming distance / total number of bits) + # Bound score from 0 to 1 + if self.qbits: + return min(max(0.0, 1.0 - (score / (self.config["dimensions"] * 8))), 1.0) + + # pgvector returns negative inner product since Postgres only supports ASC order index scans on operators + return -score diff --git a/src/python/txtai/ann/dense/sqlite.py b/src/python/txtai/ann/dense/sqlite.py new file mode 100644 index 0000000..0dc185e --- /dev/null +++ b/src/python/txtai/ann/dense/sqlite.py @@ -0,0 +1,303 @@ +""" +SQLite module +""" + +import os +import sqlite3 + +# Conditional import +try: + import sqlite_vec + + SQLITEVEC = True +except ImportError: + SQLITEVEC = False + +from ..base import ANN + + +class SQLite(ANN): + """ + Builds an ANN index backed by a SQLite database. + """ + + def __init__(self, config): + super().__init__(config) + + if not SQLITEVEC: + raise ImportError('sqlite-vec is not available - install "ann" extra to enable') + + # Database parameters + self.connection, self.cursor, self.path = None, None, "" + + # Quantization setting + self.quantize = self.setting("quantize") + self.quantize = 8 if isinstance(self.quantize, bool) else int(self.quantize) if self.quantize else None + + def load(self, path): + self.path = path + + def index(self, embeddings): + # Initialize tables + self.initialize(recreate=True) + + # Add vectors + self.database().executemany(self.insertsql(), enumerate(embeddings)) + + # Add id offset and index build metadata + self.config["offset"] = embeddings.shape[0] + self.metadata(self.settings()) + + def append(self, embeddings): + self.database().executemany(self.insertsql(), [(x + self.config["offset"], row) for x, row in enumerate(embeddings)]) + + self.config["offset"] += embeddings.shape[0] + self.metadata() + + def delete(self, ids): + self.database().executemany(self.deletesql(), [(x,) for x in ids]) + + def search(self, queries, limit): + results = [] + for query in queries: + # Execute query + self.database().execute(self.searchsql(), [query, limit]) + + # Add query results + results.append(list(self.database())) + + return results + + def count(self): + self.database().execute(self.countsql()) + return self.cursor.fetchone()[0] + + def save(self, path): + # Temporary database + if not self.path: + # Save temporary database + self.connection.commit() + + # Copy data from current to new + connection = self.copy(path) + + # Close temporary database + self.connection.close() + + # Point connection to new connection + self.connection = connection + self.cursor = self.connection.cursor() + self.path = path + + # Paths are equal, commit changes + elif self.path == path: + self.connection.commit() + + # New path is different from current path, copy data and continue using current connection + else: + self.copy(path).close() + + def close(self): + # Parent logic + super().close() + + # Close database connection + if self.connection: + self.connection.close() + self.connection = None + + def initialize(self, recreate=False): + """ + Initializes a new database session. + + Args: + recreate: Recreates the database tables if True + """ + + # Create table + self.database().execute(self.tablesql()) + + # Clear data + if recreate: + self.database().execute(self.tosql("DELETE FROM {table}")) + + def settings(self): + """ + Returns settings for this index. + + Returns: + dict + """ + + sqlite, sqlitevec = self.database().execute("SELECT sqlite_version(), vec_version()").fetchone() + + return {"sqlite": sqlite, "sqlite-vec": sqlitevec} + + def database(self): + """ + Gets the current database cursor. Creates a new connection + if there isn't one. + + Returns: + cursor + """ + + if not self.connection: + self.connection = self.connect(self.path) + self.cursor = self.connection.cursor() + + return self.cursor + + def connect(self, path): + """ + Creates a new database connection. + + Args: + path: path to database file + + Returns: + database connection + """ + + # Create connection + connection = sqlite3.connect(path, check_same_thread=False) + + # Load sqlite-vec extension + connection.enable_load_extension(True) + sqlite_vec.load(connection) + connection.enable_load_extension(False) + + # Return connection and cursor + return connection + + def copy(self, path): + """ + Copies content from the current database into target. + + Args: + path: target database path + + Returns: + new database connection + """ + + # Delete existing file, if necessary + if os.path.exists(path): + os.remove(path) + + # Create new connection + connection = self.connect(path) + + if self.connection.in_transaction: + # Initialize connection + connection.execute(self.tablesql()) + + # The backup call will hang if there are uncommitted changes, need to copy over + # with iterdump (which is much slower) + for sql in self.connection.iterdump(): + if self.tosql('insert into "{table}"') in sql.lower(): + connection.execute(sql) + else: + # Database is up to date, can do a more efficient copy with SQLite C API + self.connection.backup(connection) + + return connection + + def tablesql(self): + """ + Builds a CREATE table statement for table. + + Returns: + CREATE TABLE + """ + + # Binary quantization + if self.quantize == 1: + embedding = f"embedding BIT[{self.config['dimensions']}]" + + # INT8 quantization + elif self.quantize == 8: + embedding = f"embedding INT8[{self.config['dimensions']}] distance=cosine" + + # Standard FLOAT32 + else: + embedding = f"embedding FLOAT[{self.config['dimensions']}] distance=cosine" + + # Return CREATE TABLE sql + return self.tosql(("CREATE VIRTUAL TABLE IF NOT EXISTS {table} USING vec0" "(indexid INTEGER PRIMARY KEY, " f"{embedding})")) + + def insertsql(self): + """ + Creates an INSERT SQL statement. + + Returns: + INSERT + """ + + return self.tosql(f"INSERT INTO {{table}}(indexid, embedding) VALUES (?, {self.embeddingsql()})") + + def deletesql(self): + """ + Creates a DELETE SQL statement. + + Returns: + DELETE + """ + + return self.tosql("DELETE FROM {table} WHERE indexid = ?") + + def searchsql(self): + """ + Creates a SELECT SQL statement for search. + + Returns: + SELECT + """ + + return self.tosql(("SELECT indexid, 1 - distance FROM {table} " f"WHERE embedding MATCH {self.embeddingsql()} AND k = ? ORDER BY distance")) + + def countsql(self): + """ + Creates a SELECT COUNT statement. + + Returns: + SELECT COUNT + """ + + return self.tosql("SELECT count(indexid) FROM {table}") + + def embeddingsql(self): + """ + Creates an embeddings column SQL snippet. + + Returns: + embeddings column SQL + """ + + # Binary quantization + if self.quantize == 1: + embedding = "vec_quantize_binary(?)" + + # INT8 quantization + elif self.quantize == 8: + embedding = "vec_quantize_int8(?, 'unit')" + + # Standard FLOAT32 + else: + embedding = "?" + + return embedding + + def tosql(self, sql): + """ + Creates a SQL statement substituting in the configured table name. + + Args: + sql: SQL statement with a {table} parameter + + Returns: + fully resolved SQL statement + """ + + table = self.setting("table", "vectors") + return sql.format(table=table) diff --git a/src/python/txtai/ann/dense/torch.py b/src/python/txtai/ann/dense/torch.py new file mode 100644 index 0000000..b2961bc --- /dev/null +++ b/src/python/txtai/ann/dense/torch.py @@ -0,0 +1,227 @@ +""" +PyTorch module +""" + +try: + from bitsandbytes import matmul_4bit + from bitsandbytes.functional import ( + QuantState, + int8_vectorwise_quant, + int8_vectorwise_dequant, + int8_linear_matmul, + int8_mm_dequant, + quantize_4bit, + dequantize_4bit, + ) + + BNB = True +except ImportError: + BNB = False + +from .numpy import NumPy + +# Core library imports +from ...util import Library + +library = Library() +np = library.numpy() +torch = library.torch() + + +class Torch(NumPy): + """ + Builds an ANN index backed by a PyTorch array. + """ + + def __init__(self, config): + super().__init__(config) + + # Define array functions + self.all, self.cat, self.dot, self.zeros = torch.all, torch.cat, torch.mm, torch.zeros + self.argsort, self.xor, self.clip = torch.argsort, torch.bitwise_xor, torch.clip + + # Quantization parameters + self.qstate, self.qdeleted = None, 0 + + # Initialize quantization + settings = self.qsettings() + if settings: + if not BNB: + raise ImportError('bitsandbytes is not available - install "ann" extra to enable') + + if settings.get("type") == "int8": + # Matrix multiply for 8 bit vectors + self.dot = self.matmul8bit + else: + # Matrix multiply for 4 bit vectors + self.dot = self.matmul4bit + + # Require safetensors storage + self.config[self.config["backend"]]["safetensors"] = True + + def index(self, embeddings): + with QuantizeContext(self): + super().index(embeddings) + + def append(self, embeddings): + with QuantizeContext(self): + super().append(embeddings) + + def delete(self, ids): + with QuantizeContext(self): + super().delete(ids) + + # Calculate deleted for quantized data, if necessary + if self.qstate: + self.qdeleted = self.qstate.shape[0] - super().count() + + def count(self): + return self.qstate.shape[0] - self.qdeleted if self.qstate else super().count() + + def tensor(self, array): + # Convert array to Tensor + if isinstance(array, np.ndarray): + array = torch.from_numpy(array) + + # Load to GPU device, if available + return array.cuda() if torch.cuda.is_available() else array + + def numpy(self, array): + return array.cpu().numpy() + + def totype(self, array, dtype): + return array.long() if dtype == np.int64 else array + + def settings(self): + return {"torch": torch.__version__} + + def loadsafetensors(self, path): + data = super().loadsafetensors(path) + + # Load quantization settings + if self.qsettings(): + self.qstate = QuantState( + absmax=self.tensor(data["absmax"]), + shape=torch.Size(data["shape"].tolist()), + code=self.tensor(data["code"]) if "code" in data else None, + blocksize=int(data["blocksize"]) if "blocksize" in data else None, + quant_type=data["quant_type"], + dtype=getattr(torch, data["dtype"]), + ) + self.qdeleted = int(data["qdeleted"]) + + return data + + def savesafetensors(self, data, path, metadata=None): + # Save quantization settings + if self.qstate: + # Required elements + data["absmax"] = self.qstate.absmax.cpu().numpy() + data["shape"] = np.array(list(self.qstate.shape)) + + metadata = { + "quant_type": str(self.qstate.quant_type), + "dtype": str(self.qstate.dtype).rsplit(".", maxsplit=1)[-1], + "qdeleted": str(self.qdeleted), + } + + # Add optional elements + if self.qstate.code is not None: + data["code"] = self.qstate.code.cpu().numpy() + + if self.qstate.blocksize: + metadata["blocksize"] = str(self.qstate.blocksize) + + super().savesafetensors(data, path, metadata) + + def quantize(self): + """ + Quantizes data if quantization if supported and enabled. + """ + + # Get quantization settings and quantize + settings = self.qsettings() + if settings: + if settings.get("type") == "int8": + # Get current backend config + shape, dtype = self.backend.shape, self.backend.dtype + + # 8-bit quantization + self.backend, absmax, _ = int8_vectorwise_quant(self.backend.half()) + self.qstate = QuantState(absmax=absmax, shape=shape, quant_type=settings["type"], dtype=dtype) + else: + # 4-bit quantization + self.backend, self.qstate = quantize_4bit( + self.backend, blocksize=settings.get("blocksize", 64), quant_type=settings.get("type", "nf4") + ) + + def dequantize(self): + """ + Dequantizes data if quantization is supported and enabled. + """ + + # Dequantize using current quantization state + if self.qstate: + if self.qstate.quant_type == "int8": + # 8-bit quantization + self.backend = int8_vectorwise_dequant(self.backend, self.qstate.absmax) + else: + # 4-bit quantization + self.backend = dequantize_4bit(self.backend, self.qstate) + + def qsettings(self): + """ + Parse quantization settings. Only read parameters if CUDA is available. + + Returns: + {quantization settings} + """ + + quantize = self.setting("quantize") + return {"quantize": True} if quantize and isinstance(quantize, bool) else quantize + + def matmul8bit(self, query, data): + """ + 8-bit integer matrix multiplication. + + Args: + query: query matrix + data: data matrix + + Returns: + query @ data + """ + + # Matrix multiplication method requires transposing data matrix + query, absmax, _ = int8_vectorwise_quant(query.half()) + return int8_mm_dequant(int8_linear_matmul(query, data.T), absmax, self.qstate.absmax).float() + + def matmul4bit(self, query, data): + """ + 4-bit float matrix multiplication. + + Args: + query: query matrix + data: data matrix + + Returns: + query @ data + """ + + # Matrix multiplication method transposes data already + return matmul_4bit(query, data, self.qstate) + + +class QuantizeContext: + """ + Quantization context. Facilitates modifications to quantized tensors. + """ + + def __init__(self, ann): + self.ann = ann + + def __enter__(self): + self.ann.dequantize() + + def __exit__(self, exc_type, exc_val, exc_tb): + self.ann.quantize() diff --git a/src/python/txtai/ann/dense/turbovec.py b/src/python/txtai/ann/dense/turbovec.py new file mode 100644 index 0000000..8a97261 --- /dev/null +++ b/src/python/txtai/ann/dense/turbovec.py @@ -0,0 +1,82 @@ +""" +TurboVec module +""" + +# Conditional import +try: + from turbovec import IdMapIndex + + TURBOVEC = True +except ImportError: + TURBOVEC = False + +from ..base import ANN + +# Core library imports +from ...util import Library + +np = Library().numpy() + + +class TurboVec(ANN): + """ + Builds an ANN index using the turbovec library. + """ + + def __init__(self, config): + super().__init__(config) + + if not TURBOVEC: + raise ImportError('turbovec is not available - install "ann" extra to enable') + + def load(self, path): + # Load index + self.backend = IdMapIndex.load(path) + + def index(self, embeddings): + # Lookup index settings + bitwidth = self.setting("bitwidth", 4) + + # Create index + self.backend = IdMapIndex(dim=self.config["dimensions"], bit_width=bitwidth) + + # Add items - position in embeddings is used as the id + self.backend.add_with_ids(embeddings, np.arange(embeddings.shape[0], dtype=np.uint64)) + + # Add id offset, delete counter and index build metadata + self.config["offset"] = embeddings.shape[0] + self.metadata({"bitwidth": bitwidth}) + + def append(self, embeddings): + new = embeddings.shape[0] + + self.backend.add_with_ids(embeddings, np.arange(self.config["offset"], self.config["offset"] + new, dtype=np.uint64)) + + # Update id offset and index metadata + self.config["offset"] += new + self.metadata() + + def delete(self, ids): + # Mark elements as deleted to omit from search results + for uid in ids: + self.backend.remove(uid) + + def search(self, queries, limit): + # Run the query + scores, ids = self.backend.search(queries=queries, k=limit) + + # Map results to [(id, score)] + results = [] + for x, score in enumerate(scores): + + # Build (id, score) tuples, convert to ints and floats + results.append(list(zip(ids[x].tolist(), score.tolist()))) + + return results + + def count(self): + return len(self.backend) + + def save(self, path): + # Write index + self.backend.write(path) diff --git a/src/python/txtai/ann/sparse/__init__.py b/src/python/txtai/ann/sparse/__init__.py new file mode 100644 index 0000000..616cdd4 --- /dev/null +++ b/src/python/txtai/ann/sparse/__init__.py @@ -0,0 +1,7 @@ +""" +Sparse ANN imports +""" + +from .factory import SparseANNFactory +from .ivfsparse import IVFSparse +from .pgsparse import PGSparse diff --git a/src/python/txtai/ann/sparse/factory.py b/src/python/txtai/ann/sparse/factory.py new file mode 100644 index 0000000..d7b42b2 --- /dev/null +++ b/src/python/txtai/ann/sparse/factory.py @@ -0,0 +1,61 @@ +""" +Factory module +""" + +from ...util import Resolver + +from .ivfsparse import IVFSparse +from .pgsparse import PGSparse + + +class SparseANNFactory: + """ + Methods to create Sparse ANN indexes. + """ + + @staticmethod + def create(config): + """ + Create an Sparse ANN. + + Args: + config: index configuration parameters + + Returns: + Sparse ANN + """ + + # ANN instance + ann = None + backend = config.get("backend", "ivfsparse") + + # Create ANN instance + if backend == "ivfsparse": + ann = IVFSparse(config) + elif backend == "pgsparse": + ann = PGSparse(config) + else: + ann = SparseANNFactory.resolve(backend, config) + + # Store config back + config["backend"] = backend + + return ann + + @staticmethod + def resolve(backend, config): + """ + Attempt to resolve a custom backend. + + Args: + backend: backend class + config: index configuration parameters + + Returns: + ANN + """ + + try: + return Resolver()(backend)(config) + except Exception as e: + raise ImportError(f"Unable to resolve sparse ann backend: '{backend}'") from e diff --git a/src/python/txtai/ann/sparse/ivfsparse.py b/src/python/txtai/ann/sparse/ivfsparse.py new file mode 100644 index 0000000..e9551f6 --- /dev/null +++ b/src/python/txtai/ann/sparse/ivfsparse.py @@ -0,0 +1,378 @@ +""" +IVFSparse module +""" + +import math +import os + +from multiprocessing.pool import ThreadPool + +# Conditional import +try: + from scipy.sparse import csr_matrix, vstack + from scipy.sparse.linalg import norm + from sklearn.cluster import MiniBatchKMeans + from sklearn.metrics import pairwise_distances_argmin_min + from sklearn.utils.extmath import safe_sparse_dot + + IVFSPARSE = True +except ImportError: + IVFSPARSE = False + +from ...serialize import SerializeFactory +from ...util import Library, SparseArray +from ..base import ANN + +# Core library imports +np = Library().numpy() + + +class IVFSparse(ANN): + """ + Inverted file (IVF) index with flat vector file storage and sparse array support. + + IVFSparse builds an IVF index and enables approximate nearest neighbor (ANN) search. + + This index is modeled after Faiss and supports many of the same parameters. + + See this link for more: https://github.com/facebookresearch/faiss/wiki/Faster-search + """ + + def __init__(self, config): + super().__init__(config) + + if not IVFSPARSE: + raise ImportError('IVFSparse is not available - install "ann" extra to enable') + + # Cluster centroids, if computed + self.centroids = None + + # Cluster id mapping + self.ids = None + + # Cluster data blocks - can be a single block with no computed centroids + self.blocks = None + + # Deleted ids + self.deletes = None + + def index(self, embeddings): + # Compute model training size + train, sample = embeddings, self.setting("sample") + if sample: + # Get sample for training + rng = np.random.default_rng(0) + indices = sorted(rng.choice(train.shape[0], int(sample * train.shape[0]), replace=False, shuffle=False)) + train = train[indices] + + # Get number of clusters. Note that final number of clusters could be lower due to filtering duplicate centroids + # and pruning of small clusters + clusters = self.nlist(embeddings.shape[0], train.shape[0]) + + # Build cluster centroids if approximate search is enabled + # A single cluster performs exact search + self.centroids = self.build(train, clusters) if clusters > 1 else None + + # Sort into clusters + ids = self.aggregate(embeddings) + + # Prune small clusters (less than minpoints parameter) and rebuild + indices = sorted(k for k, v in ids.items() if len(v) >= self.minpoints()) + if len(indices) > 0 and len(ids) > 1 and len(indices) != len(ids.keys()): + self.centroids = self.centroids[indices] + ids = self.aggregate(embeddings) + + # Sort clusters by id + self.ids = dict(sorted(ids.items(), key=lambda x: x[0])) + + # Create cluster data blocks + self.blocks = {k: embeddings[v] for k, v in self.ids.items()} + + # Calculate block max summary vectors and use as centroids + self.centroids = vstack([csr_matrix(x.max(axis=0)) for x in self.blocks.values()]) if self.centroids is not None else None + + # Initialize deletes + self.deletes = [] + + # Add id offset and index build metadata + self.config["offset"] = embeddings.shape[0] + self.metadata({"clusters": len(self.blocks)}) + + def append(self, embeddings): + # Get offset + offset = self.size() + + # Sort into clusters and merge + for cluster, ids in self.aggregate(embeddings).items(): + # Add new ids + self.ids[cluster].extend([x + offset for x in ids]) + + # Add new data + self.blocks[cluster] = vstack([self.blocks[cluster], embeddings[ids]]) + + # Update id offset and index metadata + self.config["offset"] += embeddings.shape[0] + self.metadata() + + def delete(self, ids): + # Set index ids as deleted + self.deletes.extend(ids) + + def search(self, queries, limit): + results = [] + + # Calculate number of threads using a thread batch size of 32 + threads = queries.shape[0] // 32 + threads = min(max(threads, 1), os.cpu_count()) + + # Approximate search + blockids = self.topn(queries, self.centroids, self.nprobe())[0] if self.centroids is not None else None + + # This method is able to run as multiple threads due to a number of numpy/scipy method calls that drop the GIL. + results = [] + with ThreadPool(threads) as pool: + for result in pool.starmap(self.scan, [(x, limit, blockids[i] if blockids is not None else None) for i, x in enumerate(queries)]): + results.append(result) + + return results + + def count(self): + return self.size() - len(self.deletes) + + def load(self, path): + # Create streaming serializer and limit read size to a byte at a time to ensure + # only msgpack data is consumed + serializer = SerializeFactory.create("msgpack", streaming=True, read_size=1) + + with open(path, "rb") as f: + # Read header + unpacker = serializer.loadstream(f) + header = next(unpacker) + + # Read cluster centroids, if available + self.centroids = SparseArray().load(f) if header["centroids"] else None + + # Read cluster ids + self.ids = dict(next(unpacker)) + + # Read cluster data blocks + self.blocks = {} + for key in self.ids: + self.blocks[key] = SparseArray().load(f) + + # Read deletes + self.deletes = next(unpacker) + + def save(self, path): + # IVFSparse storage format: + # - header msgpack + # - centroids sparse array (optional based on header parameters) + # - cluster ids msgpack + # - cluster data blocks list of sparse arrays + # - deletes msgpack + + # Create message pack serializer + serializer = SerializeFactory.create("msgpack") + + with open(path, "wb") as f: + # Write header + serializer.savestream({"centroids": self.centroids is not None, "count": self.count(), "blocks": len(self.blocks)}, f) + + # Write cluster centroids, if available + if self.centroids is not None: + SparseArray().save(f, self.centroids) + + # Write cluster id mapping + serializer.savestream(list(self.ids.items()), f) + + # Write cluster data blocks + for block in self.blocks.values(): + SparseArray().save(f, block) + + # Write deletes + serializer.savestream(self.deletes, f) + + def build(self, train, clusters): + """ + Builds a k-means cluster to calculate centroid points for aggregating data blocks. + + Args: + train: training data + clusters: number of clusters to create + + Returns: + cluster centroids + """ + + # Select top n most important features that contribute to L2 vector norm + indices = np.argsort(-norm(train, axis=0))[: self.setting("nfeatures", 25)] + data = train[:, indices] + data = train + + # Cluster data using k-means + kmeans = MiniBatchKMeans(n_clusters=clusters, random_state=0, n_init=5).fit(data) + + # Find closest points to each cluster center and use those as centroids + indices, _ = pairwise_distances_argmin_min(kmeans.cluster_centers_, data, metric="l2") + + # Filter out duplicate centroids and return cluster centroids + return train[np.unique(indices)] + + def aggregate(self, data): + """ + Aggregates input data array into clusters. This method sorts each data element into the + cluster with the highest L2 similarity centroid. + + Args: + data: input data + + Returns: + {cluster, ids} + """ + + # Exact search when only a single cluster + if self.centroids is None: + return {0: list(range(data.shape[0]))} + + # Map data to closest centroids + indices, _ = pairwise_distances_argmin_min(data, self.centroids, metric="l2") + + # Sort into clusters + ids = {} + for x, cluster in enumerate(indices.tolist()): + if cluster not in ids: + ids[cluster] = [] + + # Save id + ids[cluster].append(x) + + return ids + + def topn(self, queries, data, limit, deletes=None): + """ + Gets the top n most similar data elements for query. + + Args: + queries: queries array + data: data array + limit: top n + deletes: optional list of deletes to filter from results + + Returns: + list of matching (indices, scores) + """ + + # Dot product similarity + scores = safe_sparse_dot(queries, data.T, dense_output=True) + + # Clear deletes + if deletes is not None: + scores[:, deletes] = 0 + + # Get top n matching indices and scores + indices = np.argpartition(-scores, limit if limit < scores.shape[1] else scores.shape[1] - 1)[:, :limit] + scores = np.take_along_axis(scores, indices, axis=1) + + return indices, scores + + def scan(self, query, limit, blockids): + """ + Scans a list of blocks for top n ids that match query. + + Args: + query: input query + limit top n + blockids: block ids to scan + + Returns: + list of (id, scores) + """ + + if self.centroids is not None: + # Stack into single ids list + ids = np.concatenate([self.ids[x] for x in blockids if x in self.ids]) + + # Stack data rows + data = vstack([self.blocks[x] for x in blockids if x in self.blocks]) + else: + # Exact search + ids, data = np.array(self.ids[0]), self.blocks[0] + + # Get deletes + deletes = np.argwhere(np.isin(ids, self.deletes)).ravel() + + # Calculate similarity + indices, scores = self.topn(query, data, limit, deletes) + indices, scores = indices[0], scores[0] + + # Map data ids and return + return list(zip(ids[indices].tolist(), scores.tolist())) + + def nlist(self, count, train): + """ + Calculates the number of clusters for this IVFSparse index. Note that the final number of clusters + could be lower as duplicate cluster centroids are filtered out. + + Args: + count: initial dataset size + train: number of rows used to train + + Returns: + number of clusters + """ + + # Get data size + default = 1 if count <= 5000 else self.cells(train) + + # Number of clusters to create + return self.setting("nlist", default) + + def nprobe(self): + """ + Gets or derives the nprobe search parameter. + + Returns: + nprobe setting + """ + + # Get size of embeddings index + size = self.size() + + default = 6 if size <= 5000 else self.cells(size) // 16 + return self.setting("nprobe", default) + + def cells(self, count): + """ + Calculates the number of IVF cells for an IVFSparse index. + + Args: + count: number of rows + + Returns: + number of IVF cells + """ + + # Calculate number of IVF cells where x = min(4 * sqrt(count), count / minpoints) + return max(min(round(4 * math.sqrt(count)), int(count / self.minpoints())), 1) + + def size(self): + """ + Gets the total size of this index including deletes. + + Returns: + size + """ + + return sum(len(x) for x in self.ids.values()) + + def minpoints(self): + """ + Gets the minimum number of points per cluster. + + Returns: + minimum points per cluster + """ + + # Minimum number of points per cluster + # Match faiss default that requires at least 39 points per clusters + return self.setting("minpoints", 39) diff --git a/src/python/txtai/ann/sparse/pgsparse.py b/src/python/txtai/ann/sparse/pgsparse.py new file mode 100644 index 0000000..0147425 --- /dev/null +++ b/src/python/txtai/ann/sparse/pgsparse.py @@ -0,0 +1,59 @@ +""" +PGSparse module +""" + +import os + +# Conditional import +try: + from pgvector import SparseVector + from pgvector.sqlalchemy import SPARSEVEC + + PGSPARSE = True +except ImportError: + PGSPARSE = False + +from ..dense import PGVector + +# Core library imports +from ...util import Library + +np = Library().numpy() + + +class PGSparse(PGVector): + """ + Builds a Sparse ANN index backed by a Postgres database. + """ + + def __init__(self, config): + if not PGSPARSE: + raise ImportError('PGSparse is not available - install "ann" extra to enable') + + super().__init__(config) + + # Quantization not supported + self.qbits = None + + def defaulttable(self): + return "svectors" + + def url(self): + return self.setting("url", os.environ.get("SCORING_URL", os.environ.get("ANN_URL"))) + + def column(self): + return SPARSEVEC(self.config["dimensions"]) + + def operation(self): + return "sparsevec_ip_ops" + + def prepare(self, data): + # pgvector only allows 1000 non-zero values for sparse vectors + # Trim to top 1000 values, if necessary + if data.count_nonzero() > 1000: + value = -np.sort(-data[0, :].data)[1000] + data.data = np.where(data.data > value, data.data, 0) + data.eliminate_zeros() + + # Wrap as sparse vector + return SparseVector(data) diff --git a/src/python/txtai/api/__init__.py b/src/python/txtai/api/__init__.py new file mode 100644 index 0000000..6ef6cdf --- /dev/null +++ b/src/python/txtai/api/__init__.py @@ -0,0 +1,18 @@ +""" +API imports +""" + +# Conditional import +try: + from .authorization import Authorization + from .application import app, start + from .base import API + from .cluster import Cluster + from .extension import Extension + from .factory import APIFactory + from .responses import * + from .routers import * + from .route import EncodingAPIRoute +except ImportError as missing: + # pylint: disable=W0707 + raise ImportError('API is not available - install "api" extra to enable') from missing diff --git a/src/python/txtai/api/application.py b/src/python/txtai/api/application.py new file mode 100644 index 0000000..c9f2178 --- /dev/null +++ b/src/python/txtai/api/application.py @@ -0,0 +1,158 @@ +""" +FastAPI application module +""" + +import inspect +import os +import sys + +from fastapi import APIRouter, Depends, FastAPI +from fastapi_mcp import FastApiMCP +from httpx import AsyncClient, ASGITransport + +from .authorization import Authorization +from .base import API +from .factory import APIFactory + +from ..app import Application + + +def get(): + """ + Returns global API instance. + + Returns: + API instance + """ + + return INSTANCE + + +def create(): + """ + Creates a FastAPI instance. + """ + + # Application dependencies + dependencies = [] + + # Default implementation of token authorization + token = os.environ.get("TOKEN") + if token: + dependencies.append(Depends(Authorization(token))) + + # Add custom dependencies + deps = os.environ.get("DEPENDENCIES") + if deps: + for dep in deps.split(","): + # Create and add dependency + dep = APIFactory.get(dep.strip())() + dependencies.append(Depends(dep)) + + # Create FastAPI application + return FastAPI(lifespan=lifespan, dependencies=dependencies if dependencies else None) + + +def apirouters(): + """ + Lists available APIRouters. + + Returns: + {router name: router} + """ + + # Get handle to api module + api = sys.modules[".".join(__name__.split(".")[:-1])] + + available = {} + for name, rclass in inspect.getmembers(api, inspect.ismodule): + if hasattr(rclass, "router") and isinstance(rclass.router, APIRouter): + available[name.lower()] = rclass.router + + return available + + +def lifespan(application): + """ + FastAPI lifespan event handler. + + Args: + application: FastAPI application to initialize + """ + + # pylint: disable=W0603 + global INSTANCE + + # Load YAML settings + config = Application.read(os.environ.get("CONFIG")) + + # Instantiate API instance + api = os.environ.get("API_CLASS") + INSTANCE = APIFactory.create(config, api) if api else API(config) + + # Get all known routers + routers = apirouters() + + # Conditionally add routes based on configuration + for name, router in routers.items(): + if name in config: + application.include_router(router) + + # Special case for embeddings clusters + if "cluster" in config and "embeddings" not in config: + application.include_router(routers["embeddings"]) + + # Special case to add similarity instance for embeddings + if "embeddings" in config and "similarity" not in config: + application.include_router(routers["similarity"]) + + # Execute extensions if present + extensions = os.environ.get("EXTENSIONS") + if extensions: + for extension in extensions.split(","): + # Create instance and execute extension + extension = APIFactory.get(extension.strip())() + extension(application) + + # Add Model Context Protocol (MCP) service, if applicable + createmcp(application, config) + + yield + + +def createmcp(application, config): + """ + Create a MCP service if necessary. + + Args: + application: FastAPI Application + config: configuration + """ + + mcp = config.get("mcp") + if mcp: + # HTTP Client arguments + defaults = {"base_url": "http://apiserver", "timeout": 100} + clientargs = mcp.get("clientargs", {}) if isinstance(mcp, dict) else {} + clientargs = {**defaults, **clientargs} + + # Create HTTP client with custom options + client = AsyncClient(transport=ASGITransport(app=application, raise_app_exceptions=False), **clientargs) + + # MCP service arguments + mcpargs = mcp.get("mcpargs", {}) if isinstance(mcp, dict) else {} + + mcp = FastApiMCP(application, http_client=client, **mcpargs) + mcp.mount() + + +def start(): + """ + Runs application lifespan handler. + """ + + list(lifespan(app)) + + +# FastAPI instance txtai API instances +app, INSTANCE = create(), None diff --git a/src/python/txtai/api/authorization.py b/src/python/txtai/api/authorization.py new file mode 100644 index 0000000..21eda15 --- /dev/null +++ b/src/python/txtai/api/authorization.py @@ -0,0 +1,53 @@ +""" +Authorization module +""" + +import hashlib +import os + +from fastapi import Header, HTTPException + + +class Authorization: + """ + Basic token authorization. + """ + + def __init__(self, token=None): + """ + Creates a new Authorization instance. + + Args: + token: SHA-256 hash of token to check + """ + + self.token = token if token else os.environ.get("TOKEN") + + def __call__(self, authorization: str = Header(default=None)): + """ + Validates authorization header is present and equal to current token. + + Args: + authorization: authorization header + """ + + if not authorization or self.token != self.digest(authorization): + raise HTTPException(status_code=401, detail="Invalid Authorization Token") + + def digest(self, authorization): + """ + Computes a SHA-256 hash for input authorization token. + + Args: + authorization: authorization header + + Returns: + SHA-256 hash of authorization token + """ + + # Replace Bearer prefix + prefix = "Bearer " + token = authorization[len(prefix) :] if authorization.startswith(prefix) else authorization + + # Compute SHA-256 hash + return hashlib.sha256(token.encode("utf-8")).hexdigest() diff --git a/src/python/txtai/api/base.py b/src/python/txtai/api/base.py new file mode 100644 index 0000000..df2846f --- /dev/null +++ b/src/python/txtai/api/base.py @@ -0,0 +1,159 @@ +""" +API module +""" + +import json + +from .cluster import Cluster + +from ..app import Application + + +class API(Application): + """ + Base API template. The API is an extended txtai application, adding the ability to cluster API instances together. + + Downstream applications can extend this base template to add/modify functionality. + """ + + def __init__(self, config, loaddata=True): + super().__init__(config, loaddata) + + # Embeddings cluster + self.cluster = None + if self.config.get("cluster"): + self.cluster = Cluster(self.config["cluster"]) + + # pylint: disable=W0221 + def search(self, query, limit=None, weights=None, index=None, parameters=None, graph=False, request=None): + # When search is invoked via the API, limit is set from the request + # When search is invoked directly, limit is set using the method parameter + limit = self.limit(request.query_params.get("limit") if request and hasattr(request, "query_params") else limit) + weights = self.weights(request.query_params.get("weights") if request and hasattr(request, "query_params") else weights) + index = request.query_params.get("index") if request and hasattr(request, "query_params") else index + parameters = request.query_params.get("parameters") if request and hasattr(request, "query_params") else parameters + graph = request.query_params.get("graph") if request and hasattr(request, "query_params") else graph + + # Decode parameters + parameters = json.loads(parameters) if parameters and isinstance(parameters, str) else parameters + + if self.cluster: + return self.cluster.search(query, limit, weights, index, parameters, graph) + + return super().search(query, limit, weights, index, parameters, graph) + + def batchsearch(self, queries, limit=None, weights=None, index=None, parameters=None, graph=False): + if self.cluster: + return self.cluster.batchsearch(queries, self.limit(limit), weights, index, parameters, graph) + + return super().batchsearch(queries, limit, weights, index, parameters, graph) + + def add(self, documents): + """ + Adds a batch of documents for indexing. + + Downstream applications can override this method to also store full documents in an external system. + + Args: + documents: list of {id: value, text: value} + + Returns: + unmodified input documents + """ + + if self.cluster: + self.cluster.add(documents) + else: + super().add(documents) + + return documents + + def index(self): + """ + Builds an embeddings index for previously batched documents. + """ + + if self.cluster: + self.cluster.index() + else: + super().index() + + def upsert(self): + """ + Runs an embeddings upsert operation for previously batched documents. + """ + + if self.cluster: + self.cluster.upsert() + else: + super().upsert() + + def delete(self, ids): + """ + Deletes from an embeddings index. Returns list of ids deleted. + + Args: + ids: list of ids to delete + + Returns: + ids deleted + """ + + if self.cluster: + return self.cluster.delete(ids) + + return super().delete(ids) + + def reindex(self, config, function=None): + """ + Recreates this embeddings index using config. This method only works if document content storage is enabled. + + Args: + config: new config + function: optional function to prepare content for indexing + """ + + if self.cluster: + self.cluster.reindex(config, function) + else: + super().reindex(config, function) + + def count(self): + """ + Total number of elements in this embeddings index. + + Returns: + number of elements in embeddings index + """ + + if self.cluster: + return self.cluster.count() + + return super().count() + + def limit(self, limit): + """ + Parses the number of results to return from the request. Allows range of 1-250, with a default of 10. + + Args: + limit: limit parameter + + Returns: + bounded limit + """ + + # Return between 1 and 250 results, defaults to 10 + return max(1, min(250, int(limit) if limit else 10)) + + def weights(self, weights): + """ + Parses the weights parameter from the request. + + Args: + weights: weights parameter + + Returns: + weights + """ + + return float(weights) if weights else weights diff --git a/src/python/txtai/api/cluster.py b/src/python/txtai/api/cluster.py new file mode 100644 index 0000000..d98c7a4 --- /dev/null +++ b/src/python/txtai/api/cluster.py @@ -0,0 +1,295 @@ +""" +Cluster module +""" + +import asyncio +import json +import random +import urllib.parse +import zlib + +import aiohttp + +from ..database.sql import Aggregate + + +class Cluster: + """ + Aggregates multiple embeddings shards into a single logical embeddings instance. + """ + + # pylint: disable=W0231 + def __init__(self, config=None): + """ + Creates a new Cluster. + + Args: + config: cluster configuration + """ + + # Configuration + self.config = config + + # Embeddings shard urls + self.shards = None + if "shards" in self.config: + self.shards = self.config["shards"] + + # Query aggregator + self.aggregate = Aggregate() + + def search(self, query, limit=None, weights=None, index=None, parameters=None, graph=False): + """ + Finds documents most similar to the input query. This method will run either an index search + or an index + database search depending on if a database is available. + + Args: + query: input query + limit: maximum results + weights: hybrid score weights, if applicable + index: index name, if applicable + parameters: dict of named parameters to bind to placeholders + graph: return graph results if True + + Returns: + list of {id: value, score: value} for index search, list of dict for an index + database search + """ + + # Build URL + action = f"search?query={urllib.parse.quote_plus(query)}" + if limit: + action += f"&limit={limit}" + if weights: + action += f"&weights={weights}" + if index: + action += f"&index={index}" + if parameters: + action += f"¶meters={json.dumps(parameters) if isinstance(parameters, dict) else parameters}" + if graph is not None: + action += f"&graph={graph}" + + # Run query and flatten results into single results list + results = [] + for result in self.execute("get", action): + results.extend(result) + + # Combine aggregate functions and sort + results = self.aggregate(query, results) + + # Limit results + return results[: (limit if limit else 10)] + + def batchsearch(self, queries, limit=None, weights=None, index=None, parameters=None, graph=False): + """ + Finds documents most similar to the input queries. This method will run either an index search + or an index + database search depending on if a database is available. + + Args: + queries: input queries + limit: maximum results + weights: hybrid score weights, if applicable + index: index name, if applicable + parameters: list of dicts of named parameters to bind to placeholders + graph: return graph results if True + + Returns: + list of {id: value, score: value} per query for index search, list of dict per query for an index + database search + """ + + # POST parameters + params = {"queries": queries} + if limit: + params["limit"] = limit + if weights: + params["weights"] = weights + if index: + params["index"] = index + if parameters: + params["parameters"] = parameters + if graph is not None: + params["graph"] = graph + + # Run query + batch = self.execute("post", "batchsearch", [params] * len(self.shards)) + + # Combine results per query + results = [] + for x, query in enumerate(queries): + result = [] + for section in batch: + result.extend(section[x]) + + # Aggregate, sort and limit results + results.append(self.aggregate(query, result)[: (limit if limit else 10)]) + + return results + + def add(self, documents): + """ + Adds a batch of documents for indexing. + + Args: + documents: list of {id: value, text: value} + """ + + self.execute("post", "add", self.shard(documents)) + + def index(self): + """ + Builds an embeddings index for previously batched documents. + """ + + self.execute("get", "index") + + def upsert(self): + """ + Runs an embeddings upsert operation for previously batched documents. + """ + + self.execute("get", "upsert") + + def delete(self, ids): + """ + Deletes from an embeddings cluster. Returns list of ids deleted. + + Args: + ids: list of ids to delete + + Returns: + ids deleted + """ + + return [uid for ids in self.execute("post", "delete", [ids] * len(self.shards)) for uid in ids] + + def reindex(self, config, function=None): + """ + Recreates this embeddings index using config. This method only works if document content storage is enabled. + + Args: + config: new config + function: optional function to prepare content for indexing + """ + + self.execute("post", "reindex", [{"config": config, "function": function}] * len(self.shards)) + + def count(self): + """ + Total number of elements in this embeddings cluster. + + Returns: + number of elements in embeddings cluster + """ + + return sum(self.execute("get", "count")) + + def shard(self, documents): + """ + Splits documents into equal sized shards. + + Args: + documents: input documents + + Returns: + list of evenly sized shards with the last shard having the remaining elements + """ + + shards = [[] for _ in range(len(self.shards))] + for document in documents: + uid = document.get("id") if isinstance(document, dict) else document + if uid and isinstance(uid, str): + # Quick int hash of string to help derive shard id + uid = zlib.adler32(uid.encode("utf-8")) + elif uid is None: + # Get random shard id when uid isn't set + uid = random.randint(0, len(shards) - 1) + + shards[uid % len(self.shards)].append(document) + + return shards + + def execute(self, method, action, data=None): + """ + Executes a HTTP action asynchronously. + + Args: + method: get or post + action: url action to perform + data: post parameters + + Returns: + json results if any + """ + + # Get urls + urls = [f"{shard}/{action}" for shard in self.shards] + close = False + + # Use existing loop if available, otherwise create one + try: + loop = asyncio.get_event_loop() + except RuntimeError: + loop = asyncio.new_event_loop() + close = True + + try: + return loop.run_until_complete(self.run(urls, method, data)) + finally: + # Close loop if it was created in this method + if close: + loop.close() + + async def run(self, urls, method, data): + """ + Runs an async action. + + Args: + urls: run against this list of urls + method: get or post + data: list of data for each url or None + + Returns: + json results if any + """ + + async with aiohttp.ClientSession(raise_for_status=True) as session: + tasks = [] + + for x, url in enumerate(urls): + if method == "post": + if not data or data[x]: + tasks.append(asyncio.ensure_future(self.post(session, url, data[x] if data else None))) + else: + tasks.append(asyncio.ensure_future(self.get(session, url))) + + return await asyncio.gather(*tasks) + + async def get(self, session, url): + """ + Runs an async HTTP GET request. + + Args: + session: ClientSession + url: url + + Returns: + json results if any + """ + + async with session.get(url) as resp: + return await resp.json() + + async def post(self, session, url, data): + """ + Runs an async HTTP POST request. + + Args: + session: ClientSession + url: url + data: data to POST + + Returns: + json results if any + """ + + async with session.post(url, json=data) as resp: + return await resp.json() diff --git a/src/python/txtai/api/extension.py b/src/python/txtai/api/extension.py new file mode 100644 index 0000000..337f8d3 --- /dev/null +++ b/src/python/txtai/api/extension.py @@ -0,0 +1,19 @@ +""" +Extension module +""" + + +class Extension: + """ + Defines an API extension. API extensions can expose custom pipelines or other custom logic. + """ + + def __call__(self, app): + """ + Hook to register custom routing logic and/or modify the FastAPI instance. + + Args: + app: FastAPI application instance + """ + + return diff --git a/src/python/txtai/api/factory.py b/src/python/txtai/api/factory.py new file mode 100644 index 0000000..82a1d8a --- /dev/null +++ b/src/python/txtai/api/factory.py @@ -0,0 +1,40 @@ +""" +API factory module +""" + +from ..util import Resolver + + +class APIFactory: + """ + API factory. Creates new API instances. + """ + + @staticmethod + def get(api): + """ + Gets a new instance of api class. + + Args: + api: API instance class + + Returns: + API + """ + + return Resolver()(api) + + @staticmethod + def create(config, api): + """ + Creates a new API instance. + + Args: + config: API configuration + api: API instance class + + Returns: + API instance + """ + + return APIFactory.get(api)(config) diff --git a/src/python/txtai/api/responses/__init__.py b/src/python/txtai/api/responses/__init__.py new file mode 100644 index 0000000..3747f79 --- /dev/null +++ b/src/python/txtai/api/responses/__init__.py @@ -0,0 +1,7 @@ +""" +Responses imports +""" + +from .factory import ResponseFactory +from .json import JSONEncoder, JSONResponse +from .messagepack import MessagePackResponse diff --git a/src/python/txtai/api/responses/factory.py b/src/python/txtai/api/responses/factory.py new file mode 100644 index 0000000..a63bdb4 --- /dev/null +++ b/src/python/txtai/api/responses/factory.py @@ -0,0 +1,30 @@ +""" +Factory module +""" + +from .json import JSONResponse +from .messagepack import MessagePackResponse + + +class ResponseFactory: + """ + Methods to create Response classes. + """ + + @staticmethod + def create(request): + """ + Gets a response class for request using the Accept header. + + Args: + request: request + + Returns: + response class + """ + + # Get Accept header + accept = request.headers.get("Accept") + + # Get response class + return MessagePackResponse if accept == MessagePackResponse.media_type else JSONResponse diff --git a/src/python/txtai/api/responses/json.py b/src/python/txtai/api/responses/json.py new file mode 100644 index 0000000..2235d75 --- /dev/null +++ b/src/python/txtai/api/responses/json.py @@ -0,0 +1,56 @@ +""" +JSON module +""" + +import base64 +import json + +from io import BytesIO +from typing import Any + +import fastapi.responses + +from PIL.Image import Image + + +class JSONEncoder(json.JSONEncoder): + """ + Extended JSONEncoder that serializes images and byte streams as base64 encoded text. + """ + + def default(self, o): + # Convert Image to BytesIO + if isinstance(o, Image): + buffered = BytesIO() + o.save(buffered, format=o.format, quality="keep") + o = buffered + + # Unpack bytes from BytesIO + if isinstance(o, BytesIO): + o = o.getvalue() + + # Base64 encode bytes instances + if isinstance(o, bytes): + return base64.b64encode(o).decode("utf-8") + + # Default handler + return super().default(o) + + +class JSONResponse(fastapi.responses.JSONResponse): + """ + Extended JSONResponse that serializes images and byte streams as base64 encoded text. + """ + + def render(self, content: Any) -> bytes: + """ + Renders content to the response. + + Args: + content: input content + + Returns: + rendered content as bytes + """ + + return json.dumps(content, ensure_ascii=False, allow_nan=False, indent=None, separators=(",", ":"), cls=JSONEncoder).encode("utf-8") diff --git a/src/python/txtai/api/responses/messagepack.py b/src/python/txtai/api/responses/messagepack.py new file mode 100644 index 0000000..230a6f6 --- /dev/null +++ b/src/python/txtai/api/responses/messagepack.py @@ -0,0 +1,51 @@ +""" +MessagePack module +""" + +from io import BytesIO +from typing import Any + +import msgpack + +from fastapi import Response +from PIL.Image import Image + + +class MessagePackResponse(Response): + """ + Encodes responses with MessagePack. + """ + + media_type = "application/msgpack" + + def render(self, content: Any) -> bytes: + """ + Renders content to the response. + + Args: + content: input content + + Returns: + rendered content as bytes + """ + + return msgpack.packb(content, default=MessagePackEncoder()) + + +class MessagePackEncoder: + """ + Extended MessagePack encoder that converts images to bytes. + """ + + def __call__(self, o): + # Convert Image to bytes + if isinstance(o, Image): + buffered = BytesIO() + o.save(buffered, format=o.format, quality="keep") + o = buffered + + # Get bytes from BytesIO + if isinstance(o, BytesIO): + o = o.getvalue() + + return o diff --git a/src/python/txtai/api/route.py b/src/python/txtai/api/route.py new file mode 100644 index 0000000..68a23d4 --- /dev/null +++ b/src/python/txtai/api/route.py @@ -0,0 +1,47 @@ +""" +Route module +""" + +import json + +from fastapi.routing import APIRoute + +from .responses import ResponseFactory, MessagePackResponse + + +class EncodingAPIRoute(APIRoute): + """ + Extended APIRoute that encodes responses based on HTTP Accept header. + """ + + def get_route_handler(self): + """ + Resolves a response class based on the HTTP Accept header. + + Returns: + route handler function + """ + + # Get handle to the original route handler + original = super().get_route_handler() + + async def handler(request): + # Target response class + target = ResponseFactory.create(request) + request.state.response_class = target + + # Get response + response = await original(request) + + # Force MessagePackResponse when it's requested and response type doesn't match + return ( + target( + content=json.loads(response.body), + status_code=response.status_code, + headers={k: v for k, v in response.headers.items() if k.lower() not in ["content-length", "content-type"]}, + ) + if not isinstance(response, target) and target == MessagePackResponse + else response + ) + + return handler diff --git a/src/python/txtai/api/routers/__init__.py b/src/python/txtai/api/routers/__init__.py new file mode 100644 index 0000000..029db1d --- /dev/null +++ b/src/python/txtai/api/routers/__init__.py @@ -0,0 +1,25 @@ +""" +Router imports +""" + +from . import agent +from . import caption +from . import embeddings +from . import entity +from . import extractor +from . import labels +from . import llm +from . import objects +from . import openai +from . import rag +from . import reranker +from . import segmentation +from . import similarity +from . import summary +from . import tabular +from . import textractor +from . import texttospeech +from . import transcription +from . import translation +from . import workflow +from . import upload diff --git a/src/python/txtai/api/routers/agent.py b/src/python/txtai/api/routers/agent.py new file mode 100644 index 0000000..fd57052 --- /dev/null +++ b/src/python/txtai/api/routers/agent.py @@ -0,0 +1,38 @@ +""" +Defines API paths for agent endpoints. +""" + +from typing import Optional + +from fastapi import APIRouter, Body +from fastapi.responses import StreamingResponse + +from .. import application +from ..route import EncodingAPIRoute + +router = APIRouter(route_class=EncodingAPIRoute) + + +@router.post("/agent") +def agent(name: str = Body(...), text: str = Body(...), maxlength: Optional[int] = Body(default=None), stream: Optional[bool] = Body(default=None)): + """ + Executes a named agent for input text. + + Args: + name: agent name + text: instructions to run + maxlength: maximum sequence length + stream: stream response if True, defaults to False + + Returns: + response text + """ + + # Build keyword arguments + kwargs = {key: value for key, value in [("stream", stream), ("maxlength", maxlength)] if value} + + # Run agent + result = application.get().agent(name, text, **kwargs) + + # Handle both standard and streaming responses + return StreamingResponse(result) if stream else result diff --git a/src/python/txtai/api/routers/caption.py b/src/python/txtai/api/routers/caption.py new file mode 100644 index 0000000..698ca0e --- /dev/null +++ b/src/python/txtai/api/routers/caption.py @@ -0,0 +1,42 @@ +""" +Defines API paths for caption endpoints. +""" + +from typing import List + +from fastapi import APIRouter, Body + +from .. import application +from ..route import EncodingAPIRoute + +router = APIRouter(route_class=EncodingAPIRoute) + + +@router.get("/caption") +def caption(file: str): + """ + Builds captions for images. + + Args: + file: file to process + + Returns: + list of captions + """ + + return application.get().pipeline("caption", (file,)) + + +@router.post("/batchcaption") +def batchcaption(files: List[str] = Body(...)): + """ + Builds captions for images. + + Args: + files: list of files to process + + Returns: + list of captions + """ + + return application.get().pipeline("caption", (files,)) diff --git a/src/python/txtai/api/routers/embeddings.py b/src/python/txtai/api/routers/embeddings.py new file mode 100644 index 0000000..df139cd --- /dev/null +++ b/src/python/txtai/api/routers/embeddings.py @@ -0,0 +1,280 @@ +""" +Defines API paths for embeddings endpoints. +""" + +from io import BytesIO +from typing import List, Optional + +import PIL + +from fastapi import APIRouter, Body, File, Form, HTTPException, Request, UploadFile +from fastapi.encoders import jsonable_encoder + +from .. import application +from ..responses import ResponseFactory +from ..route import EncodingAPIRoute + +from ...app import ReadOnlyError, ReindexDisabledError +from ...graph import Graph + +router = APIRouter(route_class=EncodingAPIRoute) + + +@router.get("/search") +def search(query: str, request: Request): + """ + Finds documents most similar to the input query. This method will run either an index search + or an index + database search depending on if a database is available. + + Args: + query: input query + request: FastAPI request + + Returns: + list of {id: value, score: value} for index search, list of dict for an index + database search + """ + + # Execute search + results = application.get().search(query, request=request) + + # Encode using standard FastAPI encoder but skip certain classes + results = jsonable_encoder( + results, custom_encoder={bytes: lambda x: x, BytesIO: lambda x: x, PIL.Image.Image: lambda x: x, Graph: lambda x: x.savedict()} + ) + + # Return raw response to prevent duplicate encoding + response = ResponseFactory.create(request) + return response(results) + + +# pylint: disable=W0621 +@router.post("/batchsearch") +def batchsearch( + request: Request, + queries: List[str] = Body(...), + limit: int = Body(default=None), + weights: float = Body(default=None), + index: str = Body(default=None), + parameters: List[dict] = Body(default=None), + graph: bool = Body(default=False), +): + """ + Finds documents most similar to the input queries. This method will run either an index search + or an index + database search depending on if a database is available. + + Args: + queries: input queries + limit: maximum results + weights: hybrid score weights, if applicable + index: index name, if applicable + parameters: list of dicts of named parameters to bind to placeholders + graph: return graph results if True + + Returns: + list of {id: value, score: value} per query for index search, list of dict per query for an index + database search + """ + + # Execute search + results = application.get().batchsearch(queries, limit, weights, index, parameters, graph) + + # Encode using standard FastAPI encoder but skip certain classes + results = jsonable_encoder( + results, custom_encoder={bytes: lambda x: x, BytesIO: lambda x: x, PIL.Image.Image: lambda x: x, Graph: lambda x: x.savedict()} + ) + + # Return raw response to prevent duplicate encoding + response = ResponseFactory.create(request) + return response(results) + + +@router.post("/add") +def add(documents: List[dict] = Body(...)): + """ + Adds a batch of documents for indexing. + + Args: + documents: list of {id: value, text: value, tags: value} + """ + + try: + application.get().add(documents) + except ReadOnlyError as e: + raise HTTPException(status_code=403, detail=e.args[0]) from e + + +@router.post("/addobject") +def addobject(data: List[bytes] = File(), uid: List[str] = Form(default=None), field: str = Form(default=None)): + """ + Adds a batch of binary documents for indexing. + + Args: + data: list of binary objects + uid: list of corresponding ids + field: optional object field name + """ + + if uid and len(data) != len(uid): + raise HTTPException(status_code=422, detail="Length of data and document lists must match") + + try: + # Add objects + application.get().addobject(data, uid, field) + except ReadOnlyError as e: + raise HTTPException(status_code=403, detail=e.args[0]) from e + + +@router.post("/addimage") +def addimage(data: List[UploadFile] = File(), uid: List[str] = Form(), field: str = Form(default=None)): + """ + Adds a batch of images for indexing. + + Args: + data: list of images + uid: list of corresponding ids + field: optional object field name + """ + + if uid and len(data) != len(uid): + raise HTTPException(status_code=422, detail="Length of data and uid lists must match") + + try: + # Add images + application.get().addobject([PIL.Image.open(content.file) for content in data], uid, field) + except ReadOnlyError as e: + raise HTTPException(status_code=403, detail=e.args[0]) from e + + +@router.get("/index") +def index(): + """ + Builds an embeddings index for previously batched documents. + """ + + try: + application.get().index() + except ReadOnlyError as e: + raise HTTPException(status_code=403, detail=e.args[0]) from e + + +@router.get("/upsert") +def upsert(): + """ + Runs an embeddings upsert operation for previously batched documents. + """ + + try: + application.get().upsert() + except ReadOnlyError as e: + raise HTTPException(status_code=403, detail=e.args[0]) from e + + +@router.post("/delete") +def delete(ids: List = Body(...)): + """ + Deletes from an embeddings index. Returns list of ids deleted. + + Args: + ids: list of ids to delete + + Returns: + ids deleted + """ + + try: + return application.get().delete(ids) + except ReadOnlyError as e: + raise HTTPException(status_code=403, detail=e.args[0]) from e + + +@router.post("/reindex") +def reindex(config: dict = Body(...), function: str = Body(default=None)): + """ + Recreates this embeddings index using config. This method only works if document content storage is enabled. + + Args: + config: new config + function: optional function to prepare content for indexing + """ + + try: + application.get().reindex(config, function) + except (ReadOnlyError, ReindexDisabledError) as e: + raise HTTPException(status_code=403, detail=e.args[0]) from e + + +@router.get("/count") +def count(): + """ + Total number of elements in this embeddings index. + + Returns: + number of elements in embeddings index + """ + + return application.get().count() + + +@router.post("/explain") +def explain(query: str = Body(...), texts: List[str] = Body(default=None), limit: int = Body(default=None)): + """ + Explains the importance of each input token in text for a query. + + Args: + query: query text + texts: list of text + + Returns: + list of dict where a higher scores represents higher importance relative to the query + """ + + return application.get().explain(query, texts, limit) + + +@router.post("/batchexplain") +def batchexplain(queries: List[str] = Body(...), texts: List[str] = Body(default=None), limit: int = Body(default=None)): + """ + Explains the importance of each input token in text for a query. + + Args: + query: query text + texts: list of text + + Returns: + list of dict where a higher scores represents higher importance relative to the query + """ + + return application.get().batchexplain(queries, texts, limit) + + +@router.get("/transform") +def transform(text: str, category: Optional[str] = None, index: Optional[str] = None): + """ + Transforms text into an embeddings array. + + Args: + text: input text + category: category for instruction-based embeddings + index: index name, if applicable + + Returns: + embeddings array + """ + + return application.get().transform(text, category, index) + + +@router.post("/batchtransform") +def batchtransform(texts: List[str] = Body(...), category: Optional[str] = None, index: Optional[str] = None): + """ + Transforms list of text into embeddings arrays. + + Args: + texts: list of text + category: category for instruction-based embeddings + index: index name, if applicable + + Returns: + embeddings arrays + """ + + return application.get().batchtransform(texts, category, index) diff --git a/src/python/txtai/api/routers/entity.py b/src/python/txtai/api/routers/entity.py new file mode 100644 index 0000000..618cc5b --- /dev/null +++ b/src/python/txtai/api/routers/entity.py @@ -0,0 +1,42 @@ +""" +Defines API paths for entity endpoints. +""" + +from typing import List + +from fastapi import APIRouter, Body + +from .. import application +from ..route import EncodingAPIRoute + +router = APIRouter(route_class=EncodingAPIRoute) + + +@router.get("/entity") +def entity(text: str): + """ + Applies a token classifier to text. + + Args: + text: input text + + Returns: + list of (entity, entity type, score) per text element + """ + + return application.get().pipeline("entity", (text,)) + + +@router.post("/batchentity") +def batchentity(texts: List[str] = Body(...)): + """ + Applies a token classifier to text. + + Args: + texts: list of text + + Returns: + list of (entity, entity type, score) per text element + """ + + return application.get().pipeline("entity", (texts,)) diff --git a/src/python/txtai/api/routers/extractor.py b/src/python/txtai/api/routers/extractor.py new file mode 100644 index 0000000..750e7cd --- /dev/null +++ b/src/python/txtai/api/routers/extractor.py @@ -0,0 +1,28 @@ +""" +Defines API paths for extractor endpoints. +""" + +from typing import List, Optional + +from fastapi import APIRouter, Body + +from .. import application +from ..route import EncodingAPIRoute + +router = APIRouter(route_class=EncodingAPIRoute) + + +@router.post("/extract") +def extract(queue: List[dict] = Body(...), texts: Optional[List[str]] = Body(default=None)): + """ + Extracts answers to input questions. + + Args: + queue: list of {name: value, query: value, question: value, snippet: value} + texts: optional list of text + + Returns: + list of {name: value, answer: value} + """ + + return application.get().extract(queue, texts) diff --git a/src/python/txtai/api/routers/labels.py b/src/python/txtai/api/routers/labels.py new file mode 100644 index 0000000..493c94d --- /dev/null +++ b/src/python/txtai/api/routers/labels.py @@ -0,0 +1,47 @@ +""" +Defines API paths for labels endpoints. +""" + +from typing import List + +from fastapi import APIRouter, Body + +from .. import application +from ..route import EncodingAPIRoute + +router = APIRouter(route_class=EncodingAPIRoute) + + +@router.post("/label") +def label(text: str = Body(...), labels: List[str] = Body(...)): + """ + Applies a zero shot classifier to text using a list of labels. Returns a list of + {id: value, score: value} sorted by highest score, where id is the index in labels. + + Args: + text: input text + labels: list of labels + + Returns: + list of {id: value, score: value} per text element + """ + + return application.get().label(text, labels) + + +@router.post("/batchlabel") +def batchlabel(texts: List[str] = Body(...), labels: List[str] = Body(...)): + """ + Applies a zero shot classifier to list of text using a list of labels. Returns a list of + {id: value, score: value} sorted by highest score, where id is the index in labels per + text element. + + Args: + texts: list of text + labels: list of labels + + Returns: + list of {id: value score: value} per text element + """ + + return application.get().label(texts, labels) diff --git a/src/python/txtai/api/routers/llm.py b/src/python/txtai/api/routers/llm.py new file mode 100644 index 0000000..f30bb9d --- /dev/null +++ b/src/python/txtai/api/routers/llm.py @@ -0,0 +1,70 @@ +""" +Defines API paths for llm endpoints. +""" + +from typing import List, Optional + +from fastapi import APIRouter, Body +from fastapi.responses import StreamingResponse + +from .. import application +from ..route import EncodingAPIRoute + +router = APIRouter(route_class=EncodingAPIRoute) + + +@router.get("/llm") +def llm(text: str, maxlength: Optional[int] = None, stream: Optional[bool] = False, stripthink: Optional[bool] = None): + """ + Runs a LLM pipeline for the input text. + + Args: + text: input text + maxlength: optional response max length + stream: streams response if True + stripthink: strip thinking tags if True + + Returns: + response text + """ + + # Build keyword arguments + params = [("maxlength", maxlength), ("stream", stream), ("stripthink", stripthink)] + kwargs = {key: value for key, value in params if value} + + # Run pipeline + result = application.get().pipeline("llm", text, **kwargs) + + # Handle both standard and streaming responses + return StreamingResponse(result) if stream else result + + +@router.post("/batchllm") +def batchllm( + texts: List[str] = Body(...), + maxlength: Optional[int] = Body(default=None), + stream: Optional[bool] = Body(default=False), + stripthink: Optional[bool] = Body(default=None), +): + """ + Runs a LLM pipeline for the input texts. + + Args: + texts: input texts + maxlength: optional response max length + stream: streams response if True + stripthink: strip thinking tags if True + + Returns: + response texts + """ + + # Build keyword arguments + params = [("maxlength", maxlength), ("stream", stream), ("stripthink", stripthink)] + kwargs = {key: value for key, value in params if value} + + # Run pipeline + result = application.get().pipeline("llm", texts, **kwargs) + + # Handle both standard and streaming responses + return StreamingResponse(result) if stream else result diff --git a/src/python/txtai/api/routers/objects.py b/src/python/txtai/api/routers/objects.py new file mode 100644 index 0000000..2f35bd2 --- /dev/null +++ b/src/python/txtai/api/routers/objects.py @@ -0,0 +1,42 @@ +""" +Defines API paths for objects endpoints. +""" + +from typing import List + +from fastapi import APIRouter, Body + +from .. import application +from ..route import EncodingAPIRoute + +router = APIRouter(route_class=EncodingAPIRoute) + + +@router.get("/objects") +def objects(file: str): + """ + Applies object detection/image classification models to images. + + Args: + file: file to process + + Returns: + list of (label, score) elements + """ + + return application.get().pipeline("objects", (file,)) + + +@router.post("/batchobjects") +def batchobjects(files: List[str] = Body(...)): + """ + Applies object detection/image classification models to images. + + Args: + files: list of files to process + + Returns: + list of (label, score) elements + """ + + return application.get().pipeline("objects", (files,)) diff --git a/src/python/txtai/api/routers/openai.py b/src/python/txtai/api/routers/openai.py new file mode 100644 index 0000000..0a793ce --- /dev/null +++ b/src/python/txtai/api/routers/openai.py @@ -0,0 +1,191 @@ +""" +Defines an OpenAI-compatible API endpoint for txtai. + +See the following specification for more information: +https://github.com/openai/openai-openapi +""" + +import uuid +import json +import time + +from typing import List, Optional, Union + +from fastapi import APIRouter, Body, Form, UploadFile +from fastapi.responses import Response, StreamingResponse + +from .. import application +from ..route import EncodingAPIRoute + +router = APIRouter(route_class=EncodingAPIRoute) + + +# pylint: disable=W0622 +@router.post("/v1/chat/completions") +def chat( + messages: List[dict] = Body(...), + model: str = Body(...), + max_completion_tokens: Optional[int] = Body(default=None), + stream: Optional[bool] = Body(default=False), +): + """ + Runs a chat completion request. + + Args: + messages: list of messages [{"role": role, "content": content}] + model: agent name, workflow name, pipeline name or embeddings + max_completion_tokens: sets the max length to generate + stream: streams response if True + + Returns: + chat completion + """ + + # Build keyword arguments + kwargs = {key: value for key, value in [("stream", stream), ("maxlength", max_completion_tokens)] if value} + + # Get first message + message = messages[0]["content"] + + # Agent + if model in application.get().agents: + result = application.get().agent(model, message, **kwargs) + + # Embeddings search + elif model == "embeddings": + result = application.get().search(message, 1, **kwargs)[0]["text"] + + # Pipeline + elif model in application.get().pipelines and model != "llm": + result = application.get().pipeline(model, message, **kwargs) + + # Workflow + elif model in application.get().workflows: + result = list(application.get().workflow(model, [message], **kwargs))[0] + + # Default to running all messages through default LLM + else: + result = application.get().pipeline("llm", messages, **kwargs) + + # Write response + return StreamingResponse(StreamingChatResponse()(model, result)) if stream else ChatResponse()(model, result) + + +@router.post("/v1/embeddings") +def embeddings(input: Union[str, List[str]] = Body(...), model: str = Body(...)): + """ + Creates an embeddings vector for the input text. + + Args: + input: text|list + model: model name + + Returns: + list of embeddings vectors + """ + + # Convert to embeddings + result = application.get().batchtransform([input] if isinstance(input, str) else input) + + # Build and return response + data = [] + for index, embedding in enumerate(result): + data.append({"object": "embedding", "embedding": embedding, "index": index}) + + return {"object": "list", "data": data, "model": model} + + +@router.post("/v1/audio/speech") +def speech(input: str = Body(...), voice: str = Body(...), response_format: Optional[str] = Body(default="mp3")): + """ + Generates speech for the input text. + + Args: + input: input text + voice: speaker name + response_format: audio encoding format, defaults to mp3 + + Returns: + audio data + """ + + # Convert to audio + audio = application.get().pipeline("texttospeech", input, speaker=voice, encoding=response_format) + + # Write audio + return Response(audio) + + +@router.post("/v1/audio/transcriptions") +def transcribe(file: UploadFile, language: Optional[str] = Form(default=None), response_format: Optional[str] = Form(default="json")): + """ + Transcribes audio to text. + + Args: + file: audio input file + language: language of input audio + response_format: output format (json or text) + + Returns: + transcribed text + """ + + # Transcribe + text = application.get().pipeline("transcription", file.file, language=language, task="transcribe") + return text if response_format == "text" else {"text": text} + + +@router.post("/v1/audio/translations") +def translate( + file: UploadFile, + response_format: Optional[str] = Form(default="json"), +): + """ + Translates audio to English. + + Args: + file: audio input file + response_format: output format (json or text) + + Returns: + translated text + """ + + # Transcribe and translate to English + text = application.get().pipeline("transcription", file.file, language="English", task="translate") + return text if response_format == "text" else {"text": text} + + +class ChatResponse: + """ + Returns a chat response object. + """ + + def __call__(self, model, result): + return { + "id": str(uuid.uuid4()), + "object": "chat.completion", + "created": int(time.time() * 1000), + "model": model, + "choices": [{"id": 0, "message": {"role": "assistant", "content": result}, "finish_reason": "stop"}], + } + + +class StreamingChatResponse: + """ + Returns a streaming chat response object. + """ + + def __call__(self, model, result): + for chunk in result: + yield "data: " + json.dumps( + { + "id": str(uuid.uuid4()), + "object": "chat.completion.chunk", + "created": int(time.time() * 1000), + "model": model, + "choices": [{"id": 0, "delta": {"content": chunk}}], + } + ) + "\n\n" + + yield "data: [DONE]\n\n" diff --git a/src/python/txtai/api/routers/rag.py b/src/python/txtai/api/routers/rag.py new file mode 100644 index 0000000..2173d2e --- /dev/null +++ b/src/python/txtai/api/routers/rag.py @@ -0,0 +1,70 @@ +""" +Defines API paths for rag endpoints. +""" + +from typing import List, Optional + +from fastapi import APIRouter, Body +from fastapi.responses import StreamingResponse + +from .. import application +from ..route import EncodingAPIRoute + +router = APIRouter(route_class=EncodingAPIRoute) + + +@router.get("/rag") +def rag(query: str, maxlength: Optional[int] = None, stream: Optional[bool] = False, stripthink: Optional[bool] = None): + """ + Runs a RAG pipeline for the input query. + + Args: + query: input RAG query + maxlength: optional response max length + stream: streams response if True + stripthink: strip thinking tags if True + + Returns: + answer + """ + + # Build keyword arguments + params = [("maxlength", maxlength), ("stream", stream), ("stripthink", stripthink)] + kwargs = {key: value for key, value in params if value} + + # Run pipeline + result = application.get().pipeline("rag", query, **kwargs) + + # Handle both standard and streaming responses + return StreamingResponse(result) if stream else result + + +@router.post("/batchrag") +def batchrag( + queries: List[str] = Body(...), + maxlength: Optional[int] = Body(default=None), + stream: Optional[bool] = Body(default=False), + stripthink: Optional[bool] = Body(default=None), +): + """ + Runs a RAG pipeline for the input queries. + + Args: + queries: input RAG queries + maxlength: optional response max length + stream: streams response if True + stripthink: strip thinking tags if True + + Returns: + answers + """ + + # Build keyword arguments + params = [("maxlength", maxlength), ("stream", stream), ("stripthink", stripthink)] + kwargs = {key: value for key, value in params if value} + + # Run pipeline + result = application.get().pipeline("rag", queries, **kwargs) + + # Handle both standard and streaming responses + return StreamingResponse(result) if stream else result diff --git a/src/python/txtai/api/routers/reranker.py b/src/python/txtai/api/routers/reranker.py new file mode 100644 index 0000000..7f93864 --- /dev/null +++ b/src/python/txtai/api/routers/reranker.py @@ -0,0 +1,46 @@ +""" +Defines API paths for reranking endpoints. +""" + +from typing import List, Optional + +from fastapi import APIRouter, Body + +from .. import application +from ..route import EncodingAPIRoute + +router = APIRouter(route_class=EncodingAPIRoute) + + +@router.get("/rerank") +def rerank(query: str, limit: Optional[int] = 3, factor: Optional[int] = 10): + """ + Queries an embeddings database and reranks the results with a similarity pipeline. + + Args: + query: query text + limit: maximum results + factor: factor to multiply limit by for the initial embeddings search + + Returns: + query results + """ + + return application.get().pipeline("reranker", (query, limit, factor)) + + +@router.post("/batchrerank") +def batchrerank(queries: List[str] = Body(...), limit: Optional[int] = Body(default=3), factor: Optional[int] = Body(default=10)): + """ + Queries an embeddings database and reranks the results with a similarity pipeline. + + Args: + queries: list of queries + limit: maximum results + factor: factor to multiply limit by for the initial embeddings search + + Returns: + query results + """ + + return application.get().pipeline("reranker", (queries, limit, factor)) diff --git a/src/python/txtai/api/routers/segmentation.py b/src/python/txtai/api/routers/segmentation.py new file mode 100644 index 0000000..4199a94 --- /dev/null +++ b/src/python/txtai/api/routers/segmentation.py @@ -0,0 +1,42 @@ +""" +Defines API paths for segmentation endpoints. +""" + +from typing import List + +from fastapi import APIRouter, Body + +from .. import application +from ..route import EncodingAPIRoute + +router = APIRouter(route_class=EncodingAPIRoute) + + +@router.get("/segment") +def segment(text: str): + """ + Segments text into semantic units. + + Args: + text: input text + + Returns: + segmented text + """ + + return application.get().pipeline("segmentation", (text,)) + + +@router.post("/batchsegment") +def batchsegment(texts: List[str] = Body(...)): + """ + Segments text into semantic units. + + Args: + texts: list of texts to segment + + Returns: + list of segmented text + """ + + return application.get().pipeline("segmentation", (texts,)) diff --git a/src/python/txtai/api/routers/similarity.py b/src/python/txtai/api/routers/similarity.py new file mode 100644 index 0000000..ad63da2 --- /dev/null +++ b/src/python/txtai/api/routers/similarity.py @@ -0,0 +1,48 @@ +""" +Defines API paths for similarity endpoints. +""" + +from typing import List + +from fastapi import APIRouter, Body + +from .. import application +from ..route import EncodingAPIRoute + +router = APIRouter(route_class=EncodingAPIRoute) + + +@router.post("/similarity") +def similarity(query: str = Body(...), texts: List[str] = Body(...)): + """ + Computes the similarity between query and list of text. Returns a list of + {id: value, score: value} sorted by highest score, where id is the index + in texts. + + Args: + query: query text + texts: list of text + + Returns: + list of {id: value, score: value} + """ + + return application.get().similarity(query, texts) + + +@router.post("/batchsimilarity") +def batchsimilarity(queries: List[str] = Body(...), texts: List[str] = Body(...)): + """ + Computes the similarity between list of queries and list of text. Returns a list + of {id: value, score: value} sorted by highest score per query, where id is the + index in texts. + + Args: + queries: queries text + texts: list of text + + Returns: + list of {id: value, score: value} per query + """ + + return application.get().batchsimilarity(queries, texts) diff --git a/src/python/txtai/api/routers/summary.py b/src/python/txtai/api/routers/summary.py new file mode 100644 index 0000000..bc03d0f --- /dev/null +++ b/src/python/txtai/api/routers/summary.py @@ -0,0 +1,46 @@ +""" +Defines API paths for summary endpoints. +""" + +from typing import List, Optional + +from fastapi import APIRouter, Body + +from .. import application +from ..route import EncodingAPIRoute + +router = APIRouter(route_class=EncodingAPIRoute) + + +@router.get("/summary") +def summary(text: str, minlength: Optional[int] = None, maxlength: Optional[int] = None): + """ + Runs a summarization model against a block of text. + + Args: + text: text to summarize + minlength: minimum length for summary + maxlength: maximum length for summary + + Returns: + summary text + """ + + return application.get().pipeline("summary", (text, minlength, maxlength)) + + +@router.post("/batchsummary") +def batchsummary(texts: List[str] = Body(...), minlength: Optional[int] = Body(default=None), maxlength: Optional[int] = Body(default=None)): + """ + Runs a summarization model against a block of text. + + Args: + texts: list of text to summarize + minlength: minimum length for summary + maxlength: maximum length for summary + + Returns: + list of summary text + """ + + return application.get().pipeline("summary", (texts, minlength, maxlength)) diff --git a/src/python/txtai/api/routers/tabular.py b/src/python/txtai/api/routers/tabular.py new file mode 100644 index 0000000..3ae1117 --- /dev/null +++ b/src/python/txtai/api/routers/tabular.py @@ -0,0 +1,42 @@ +""" +Defines API paths for tabular endpoints. +""" + +from typing import List + +from fastapi import APIRouter, Body + +from .. import application +from ..route import EncodingAPIRoute + +router = APIRouter(route_class=EncodingAPIRoute) + + +@router.get("/tabular") +def tabular(file: str): + """ + Splits tabular data into rows and columns. + + Args: + file: file to process + + Returns: + list of (id, text, tag) elements + """ + + return application.get().pipeline("tabular", (file,)) + + +@router.post("/batchtabular") +def batchtabular(files: List[str] = Body(...)): + """ + Splits tabular data into rows and columns. + + Args: + files: list of files to process + + Returns: + list of (id, text, tag) elements + """ + + return application.get().pipeline("tabular", (files,)) diff --git a/src/python/txtai/api/routers/textractor.py b/src/python/txtai/api/routers/textractor.py new file mode 100644 index 0000000..638d8cf --- /dev/null +++ b/src/python/txtai/api/routers/textractor.py @@ -0,0 +1,42 @@ +""" +Defines API paths for textractor endpoints. +""" + +from typing import List + +from fastapi import APIRouter, Body + +from .. import application +from ..route import EncodingAPIRoute + +router = APIRouter(route_class=EncodingAPIRoute) + + +@router.get("/textract") +def textract(file: str): + """ + Extracts text from a file at path. + + Args: + file: file to extract text + + Returns: + extracted text + """ + + return application.get().pipeline("textractor", (file,)) + + +@router.post("/batchtextract") +def batchtextract(files: List[str] = Body(...)): + """ + Extracts text from a file at path. + + Args: + files: list of files to extract text + + Returns: + list of extracted text + """ + + return application.get().pipeline("textractor", (files,)) diff --git a/src/python/txtai/api/routers/texttospeech.py b/src/python/txtai/api/routers/texttospeech.py new file mode 100644 index 0000000..ef3bdbc --- /dev/null +++ b/src/python/txtai/api/routers/texttospeech.py @@ -0,0 +1,33 @@ +""" +Defines API paths for TTS endpoints +""" + +from typing import Optional + +from fastapi import APIRouter, Response + +from .. import application +from ..route import EncodingAPIRoute + +router = APIRouter(route_class=EncodingAPIRoute) + + +@router.get("/texttospeech") +def texttospeech(text: str, speaker: Optional[str] = None, encoding: Optional[str] = "mp3"): + """ + Generates speech from text. + + Args: + text: text + speaker: speaker id, defaults to 1 + encoding: optional audio encoding format + + Returns: + Audio data + """ + + # Convert to audio + audio = application.get().pipeline("texttospeech", text, speaker=speaker, encoding=encoding) + + # Write audio + return Response(audio, headers={"Content-Disposition": f"attachment;filename=speech.{encoding.lower()}"}) diff --git a/src/python/txtai/api/routers/transcription.py b/src/python/txtai/api/routers/transcription.py new file mode 100644 index 0000000..f0d1f75 --- /dev/null +++ b/src/python/txtai/api/routers/transcription.py @@ -0,0 +1,42 @@ +""" +Defines API paths for transcription endpoints. +""" + +from typing import List + +from fastapi import APIRouter, Body + +from .. import application +from ..route import EncodingAPIRoute + +router = APIRouter(route_class=EncodingAPIRoute) + + +@router.get("/transcribe") +def transcribe(file: str): + """ + Transcribes audio files to text. + + Args: + file: file to transcribe + + Returns: + transcribed text + """ + + return application.get().pipeline("transcription", (file,)) + + +@router.post("/batchtranscribe") +def batchtranscribe(files: List[str] = Body(...)): + """ + Transcribes audio files to text. + + Args: + files: list of files to transcribe + + Returns: + list of transcribed text + """ + + return application.get().pipeline("transcription", (files,)) diff --git a/src/python/txtai/api/routers/translation.py b/src/python/txtai/api/routers/translation.py new file mode 100644 index 0000000..4a7b3fb --- /dev/null +++ b/src/python/txtai/api/routers/translation.py @@ -0,0 +1,46 @@ +""" +Defines API paths for translation endpoints. +""" + +from typing import List, Optional + +from fastapi import APIRouter, Body + +from .. import application +from ..route import EncodingAPIRoute + +router = APIRouter(route_class=EncodingAPIRoute) + + +@router.get("/translate") +def translate(text: str, target: Optional[str] = "en", source: Optional[str] = None): + """ + Translates text from source language into target language. + + Args: + text: text to translate + target: target language code, defaults to "en" + source: source language code, detects language if not provided + + Returns: + translated text + """ + + return application.get().pipeline("translation", (text, target, source)) + + +@router.post("/batchtranslate") +def batchtranslate(texts: List[str] = Body(...), target: Optional[str] = Body(default="en"), source: Optional[str] = Body(default=None)): + """ + Translates text from source language into target language. + + Args: + texts: list of text to translate + target: target language code, defaults to "en" + source: source language code, detects language if not provided + + Returns: + list of translated text + """ + + return application.get().pipeline("translation", (texts, target, source)) diff --git a/src/python/txtai/api/routers/upload.py b/src/python/txtai/api/routers/upload.py new file mode 100644 index 0000000..2fff3d9 --- /dev/null +++ b/src/python/txtai/api/routers/upload.py @@ -0,0 +1,36 @@ +""" +Defines API paths for upload endpoints. +""" + +import shutil +import tempfile + +from typing import List + +from fastapi import APIRouter, File, Form, UploadFile + +from ..route import EncodingAPIRoute + + +router = APIRouter(route_class=EncodingAPIRoute) + + +@router.post("/upload") +def upload(files: List[UploadFile] = File(), suffix: str = Form(default=None)): + """ + Uploads files for local server processing. + + Args: + data: list of files to upload + + Returns: + list of server paths + """ + + paths = [] + for f in files: + with tempfile.NamedTemporaryFile(mode="wb", delete=False, suffix=suffix) as tmp: + shutil.copyfileobj(f.file, tmp) + paths.append(tmp.name) + + return paths diff --git a/src/python/txtai/api/routers/workflow.py b/src/python/txtai/api/routers/workflow.py new file mode 100644 index 0000000..647fd14 --- /dev/null +++ b/src/python/txtai/api/routers/workflow.py @@ -0,0 +1,28 @@ +""" +Defines API paths for workflow endpoints. +""" + +from typing import List + +from fastapi import APIRouter, Body + +from .. import application +from ..route import EncodingAPIRoute + +router = APIRouter(route_class=EncodingAPIRoute) + + +@router.post("/workflow") +def workflow(name: str = Body(...), elements: List = Body(...)): + """ + Executes a named workflow using elements as input. + + Args: + name: workflow name + elements: list of elements to run through workflow + + Returns: + list of processed elements + """ + + return application.get().workflow(name, elements) diff --git a/src/python/txtai/app/__init__.py b/src/python/txtai/app/__init__.py new file mode 100644 index 0000000..b7c0ff4 --- /dev/null +++ b/src/python/txtai/app/__init__.py @@ -0,0 +1,5 @@ +""" +App imports +""" + +from .base import Application, ReadOnlyError, ReindexDisabledError diff --git a/src/python/txtai/app/base.py b/src/python/txtai/app/base.py new file mode 100644 index 0000000..6e86897 --- /dev/null +++ b/src/python/txtai/app/base.py @@ -0,0 +1,838 @@ +""" +Application module +""" + +import os + +from multiprocessing.pool import ThreadPool +from threading import RLock + +from ..agent import Agent +from ..embeddings import Documents, Embeddings +from ..pipeline import PipelineFactory +from ..workflow import WorkflowFactory + +# Core library imports +from ..util import Library + +yaml = Library().yaml() + + +# pylint: disable=R0904 +class Application: + """ + Builds YAML-configured txtai applications. + """ + + @staticmethod + def read(data): + """ + Reads a YAML configuration file. + + Args: + data: input data + + Returns: + yaml + """ + + if isinstance(data, str): + if os.path.exists(data): + # Read yaml from file + with open(data, "r", encoding="utf-8") as f: + # Read configuration + return yaml.safe_load(f) + + # Attempt to read yaml from input + data = yaml.safe_load(data) + if not isinstance(data, str): + return data + + # File not found and input is not yaml, raise error + raise FileNotFoundError(f"Unable to load file '{data}'") + + # Return unmodified + return data + + def __init__(self, config, loaddata=True): + """ + Creates an Application instance, which encapsulates embeddings, pipelines and workflows. + + Args: + config: index configuration + loaddata: If True (default), load existing index data, if available. Otherwise, only load models. + """ + + # Initialize member variables + self.config, self.documents, self.embeddings = Application.read(config), None, None + + # Write lock - allows only a single thread to update embeddings + self.lock = RLock() + + # ThreadPool - runs scheduled workflows + self.pool = None + + # Create pipelines + self.createpipelines() + + # Create workflows + self.createworkflows() + + # Create agents + self.createagents() + + # Create embeddings index + self.indexes(loaddata) + + def __del__(self): + """ + Close threadpool when this object is garbage collected. + """ + + if hasattr(self, "pool") and self.pool: + self.pool.close() + self.pool = None + + def createpipelines(self): + """ + Create pipelines. + """ + + # Pipeline definitions + self.pipelines = {} + + # Default pipelines + pipelines = list(PipelineFactory.list().keys()) + + # Add custom pipelines + for key in self.config: + if "." in key: + pipelines.append(key) + + # Move dependent pipelines to end of list + dependent = ["similarity", "extractor", "rag", "reranker"] + pipelines = sorted(pipelines, key=lambda x: dependent.index(x) + 1 if x in dependent else 0) + + # Create pipelines + for pipeline in pipelines: + if pipeline in self.config: + config = self.config[pipeline] if self.config[pipeline] else {} + + # Add application reference, if requested + if "application" in config: + config["application"] = self + + # Custom pipeline parameters + if pipeline in ["extractor", "rag"]: + if "similarity" not in config: + # Add placeholder, will be set to embeddings index once initialized + config["similarity"] = None + + # Resolve reference pipelines + if config.get("similarity") in self.pipelines: + config["similarity"] = self.pipelines[config["similarity"]] + + if config.get("path") in self.pipelines: + config["path"] = self.pipelines[config["path"]] + + elif pipeline == "similarity" and "path" not in config and "labels" in self.pipelines: + config["model"] = self.pipelines["labels"] + + elif pipeline == "reranker": + config["embeddings"] = None + config["similarity"] = self.pipelines["similarity"] + + elif pipeline in ("textractor", "urlretrieve"): + # Default to safeopen enabled + config["safeopen"] = config.get("safeopen", True) + + self.pipelines[pipeline] = PipelineFactory.create(config, pipeline) + + def createworkflows(self): + """ + Create workflows. + """ + + # Workflow definitions + self.workflows = {} + + # Create workflows + if "workflow" in self.config: + for workflow, config in self.config["workflow"].items(): + # Create copy of config + config = config.copy() + + # Resolve callable functions + config["tasks"] = [self.resolvetask(task) for task in config["tasks"]] + + # Resolve stream functions + if "stream" in config: + config["stream"] = self.resolvetask(config["stream"]) + + # Get scheduler config + schedule = config.pop("schedule", None) + + # Create workflow + self.workflows[workflow] = WorkflowFactory.create(config, workflow) + + # Schedule job if necessary + if schedule: + # Create pool if necessary + if not self.pool: + self.pool = ThreadPool() + + self.pool.apply_async(self.workflows[workflow].schedule, kwds=schedule) + + def createagents(self): + """ + Create agents. + """ + + # Agent definitions + self.agents = {} + + # Create agents + if "agent" in self.config: + for agent, config in self.config["agent"].items(): + # Create copy of config + config = config.copy() + + # Resolve LLM + config["llm"] = self.function("llm") + + # Resolve tools + for tool in config.get("tools", []): + if isinstance(tool, dict) and "target" in tool: + tool["target"] = self.function(tool["target"]) + + # Create agent + self.agents[agent] = Agent(**config) + + def indexes(self, loaddata): + """ + Initialize an embeddings index. + + Args: + loaddata: If True (default), load existing index data, if available. Otherwise, only load models. + """ + + # Get embeddings configuration + config = self.config.get("embeddings") + if config: + # Resolve application functions in embeddings config + config = self.resolveconfig(config.copy()) + + # Load embeddings index if loaddata and index exists + if loaddata and Embeddings().exists(self.config.get("path"), self.config.get("cloud")): + # Initialize empty embeddings + self.embeddings = Embeddings() + + # Pass path and cloud settings. Set application functions as config overrides. + self.embeddings.load( + self.config.get("path"), + self.config.get("cloud"), + {key: config[key] for key in ["functions", "transform"] if key in config} if config else None, + ) + + elif "embeddings" in self.config: + # Create new embeddings with config + self.embeddings = Embeddings(config) + + # If an extractor pipeline is defined and the similarity attribute is None, set to embeddings index + for key in ["extractor", "rag"]: + pipeline = self.pipelines.get(key) + config = self.config.get(key) + + if pipeline and config is not None and config["similarity"] is None: + pipeline.similarity = self.embeddings + + # Attach embeddings to reranker + if "reranker" in self.pipelines: + self.pipelines["reranker"].embeddings = self.embeddings + + def resolvetask(self, task): + """ + Resolves callable functions for a task. + + Args: + task: input task config + """ + + # Check for task shorthand syntax + task = {"action": task} if isinstance(task, (str, list)) else task + + if "action" in task: + action = task["action"] + values = [action] if not isinstance(action, list) else action + + actions = [] + for a in values: + if a in ["index", "upsert"]: + # Add queue action to buffer documents to index + actions.append(self.add) + + # Override and disable unpacking for indexing actions + task["unpack"] = False + + # Add finalize to trigger indexing + task["finalize"] = self.upsert if a == "upsert" else self.index + elif a == "search": + actions.append(self.batchsearch) + elif a == "transform": + # Transform vectors + actions.append(self.batchtransform) + + # Override and disable one-to-many transformations + task["onetomany"] = False + else: + # Resolve action to callable function + actions.append(self.function(a)) + + # Save resolved action(s) + task["action"] = actions[0] if not isinstance(action, list) else actions + + # Resolve initializer + if "initialize" in task and isinstance(task["initialize"], str): + task["initialize"] = self.function(task["initialize"]) + + # Resolve finalizer + if "finalize" in task and isinstance(task["finalize"], str): + task["finalize"] = self.function(task["finalize"]) + + return task + + def resolveconfig(self, config): + """ + Resolves callable functions stored in embeddings configuration. + + Args: + config: embeddings config + + Returns: + resolved config + """ + + if "functions" in config: + # Resolve callable functions + functions = [] + for fn in config["functions"]: + original = fn + try: + if isinstance(fn, dict): + fn = fn.copy() + fn["function"] = self.function(fn["function"]) + else: + fn = self.function(fn) + + # pylint: disable=W0703 + except Exception: + # Not a resolvable function, pipeline or workflow - further resolution will happen in embeddings + fn = original + + functions.append(fn) + + config["functions"] = functions + + if "transform" in config: + # Resolve transform function + config["transform"] = self.function(config["transform"]) + + return config + + def function(self, function): + """ + Get a handle to a callable function. + + Args: + function: function name + + Returns: + resolved function + """ + + # Check if function is a pipeline + if function in self.pipelines: + return self.pipelines[function] + + # Check if function is a workflow + if function in self.workflows: + return self.workflows[function] + + # Attempt to resolve action as a callable function + return PipelineFactory.create({}, function) + + def search(self, query, limit=10, weights=None, index=None, parameters=None, graph=False): + """ + Finds documents most similar to the input query. This method will run either an index search + or an index + database search depending on if a database is available. + + Args: + query: input query + limit: maximum results + weights: hybrid score weights, if applicable + index: index name, if applicable + parameters: dict of named parameters to bind to placeholders + graph: return graph results if True + + Returns: + list of {id: value, score: value} for index search, list of dict for an index + database search + """ + + if self.embeddings: + with self.lock: + results = self.embeddings.search(query, limit, weights, index, parameters, graph) + + # Unpack (id, score) tuple, if necessary. Otherwise, results are dictionaries. + return results if graph else [{"id": r[0], "score": float(r[1])} if isinstance(r, tuple) else r for r in results] + + return None + + def batchsearch(self, queries, limit=10, weights=None, index=None, parameters=None, graph=False): + """ + Finds documents most similar to the input queries. This method will run either an index search + or an index + database search depending on if a database is available. + + Args: + queries: input queries + limit: maximum results + weights: hybrid score weights, if applicable + index: index name, if applicable + parameters: list of dicts of named parameters to bind to placeholders + graph: return graph results if True + + Returns: + list of {id: value, score: value} per query for index search, list of dict per query for an index + database search + """ + + if self.embeddings: + with self.lock: + search = self.embeddings.batchsearch(queries, limit, weights, index, parameters, graph) + + results = [] + for result in search: + # Unpack (id, score) tuple, if necessary. Otherwise, results are dictionaries. + results.append(result if graph else [{"id": r[0], "score": float(r[1])} if isinstance(r, tuple) else r for r in result]) + return results + + return None + + def add(self, documents): + """ + Adds a batch of documents for indexing. + + Args: + documents: list of {id: value, data: value, tags: value} + + Returns: + unmodified input documents + """ + + # Raise error if index is not writable + if not self.config.get("writable"): + raise ReadOnlyError("Attempting to add documents to a read-only index (writable != True)") + + if self.embeddings: + with self.lock: + # Create documents file if not already open + if not self.documents: + self.documents = Documents() + + # Add documents + self.documents.add(list(documents)) + + # Return unmodified input documents + return documents + + def addobject(self, data, uid, field): + """ + Helper method that builds a batch of object documents. + + Args: + data: object content + uid: optional list of corresponding uids + field: optional field to set + + Returns: + documents + """ + + # Raise error if index is not writable + if not self.config.get("writable"): + raise ReadOnlyError("Attempting to add documents to a read-only index (writable != True)") + + documents = [] + for x, content in enumerate(data): + if field: + row = {"id": uid[x], field: content} if uid else {field: content} + elif uid: + row = (uid[x], content) + else: + row = content + + documents.append(row) + + return self.add(documents) + + def index(self): + """ + Builds an embeddings index for previously batched documents. + """ + + # Raise error if index is not writable + if not self.config.get("writable"): + raise ReadOnlyError("Attempting to index a read-only index (writable != True)") + + if self.embeddings and self.documents: + with self.lock: + # Reset index + self.indexes(False) + + # Build scoring index if term weighting is enabled + if self.embeddings.isweighted(): + self.embeddings.score(self.documents) + + # Build embeddings index + self.embeddings.index(self.documents) + + # Save index if path available, otherwise this is an memory-only index + if self.config.get("path"): + self.embeddings.save(self.config["path"], self.config.get("cloud")) + + # Reset document stream + self.documents.close() + self.documents = None + + def upsert(self): + """ + Runs an embeddings upsert operation for previously batched documents. + """ + + # Raise error if index is not writable + if not self.config.get("writable"): + raise ReadOnlyError("Attempting to upsert a read-only index (writable != True)") + + if self.embeddings and self.documents: + with self.lock: + # Run upsert + self.embeddings.upsert(self.documents) + + # Save index if path available, otherwise this is an memory-only index + if self.config.get("path"): + self.embeddings.save(self.config["path"], self.config.get("cloud")) + + # Reset document stream + self.documents.close() + self.documents = None + + def delete(self, ids): + """ + Deletes from an embeddings index. Returns list of ids deleted. + + Args: + ids: list of ids to delete + + Returns: + ids deleted + """ + + # Raise error if index is not writable + if not self.config.get("writable"): + raise ReadOnlyError("Attempting to delete from a read-only index (writable != True)") + + if self.embeddings: + with self.lock: + # Run delete operation + deleted = self.embeddings.delete(ids) + + # Save index if path available, otherwise this is an memory-only index + if self.config.get("path"): + self.embeddings.save(self.config["path"], self.config.get("cloud")) + + # Return deleted ids + return deleted + + return None + + def reindex(self, config, function=None): + """ + Recreates embeddings index using config. This method only works if document content storage is enabled. + + Args: + config: new config + function: optional function to prepare content for indexing + """ + + # Raise error if index is not writable + if not self.config.get("writable"): + raise ReadOnlyError("Attempting to reindex a read-only index (writable != True)") + + # Raise error if not re-indexable + if not self.config.get("reindex"): + raise ReindexDisabledError("Attempting to reindex with reindexing disabled (reindex != True)") + + if self.embeddings: + with self.lock: + # Resolve function, if necessary + function = self.function(function) if function and isinstance(function, str) else function + + # Reindex + self.embeddings.reindex(config, function) + + # Save index if path available, otherwise this is an memory-only index + if self.config.get("path"): + self.embeddings.save(self.config["path"], self.config.get("cloud")) + + def count(self): + """ + Total number of elements in this embeddings index. + + Returns: + number of elements in embeddings index + """ + + if self.embeddings: + return self.embeddings.count() + + return None + + def similarity(self, query, texts): + """ + Computes the similarity between query and list of text. Returns a list of + {id: value, score: value} sorted by highest score, where id is the index + in texts. + + Args: + query: query text + texts: list of text + + Returns: + list of {id: value, score: value} + """ + + # Use similarity instance if available otherwise fall back to embeddings model + if "similarity" in self.pipelines: + return [{"id": uid, "score": float(score)} for uid, score in self.pipelines["similarity"](query, texts)] + if self.embeddings: + return [{"id": uid, "score": float(score)} for uid, score in self.embeddings.similarity(query, texts)] + + return None + + def batchsimilarity(self, queries, texts): + """ + Computes the similarity between list of queries and list of text. Returns a list + of {id: value, score: value} sorted by highest score per query, where id is the + index in texts. + + Args: + queries: queries text + texts: list of text + + Returns: + list of {id: value, score: value} per query + """ + + # Use similarity instance if available otherwise fall back to embeddings model + if "similarity" in self.pipelines: + return [[{"id": uid, "score": float(score)} for uid, score in r] for r in self.pipelines["similarity"](queries, texts)] + if self.embeddings: + return [[{"id": uid, "score": float(score)} for uid, score in r] for r in self.embeddings.batchsimilarity(queries, texts)] + + return None + + def explain(self, query, texts=None, limit=10): + """ + Explains the importance of each input token in text for a query. + + Args: + query: query text + texts: optional list of text, otherwise runs search query + limit: optional limit if texts is None + + Returns: + list of dict per input text where a higher token scores represents higher importance relative to the query + """ + + if self.embeddings: + with self.lock: + return self.embeddings.explain(query, texts, limit) + + return None + + def batchexplain(self, queries, texts=None, limit=10): + """ + Explains the importance of each input token in text for a list of queries. + + Args: + query: queries text + texts: optional list of text, otherwise runs search queries + limit: optional limit if texts is None + + Returns: + list of dict per input text per query where a higher token scores represents higher importance relative to the query + """ + + if self.embeddings: + with self.lock: + return self.embeddings.batchexplain(queries, texts, limit) + + return None + + def transform(self, text, category=None, index=None): + """ + Transforms text into embeddings arrays. + + Args: + text: input text + category: category for instruction-based embeddings + index: index name, if applicable + + Returns: + embeddings array + """ + + if self.embeddings: + return [float(x) for x in self.embeddings.transform(text, category, index)] + + return None + + def batchtransform(self, texts, category=None, index=None): + """ + Transforms list of text into embeddings arrays. + + Args: + texts: list of text + category: category for instruction-based embeddings + index: index name, if applicable + + Returns: + embeddings arrays + """ + + if self.embeddings: + return [[float(x) for x in result] for result in self.embeddings.batchtransform(texts, category, index)] + + return None + + def extract(self, queue, texts=None): + """ + Extracts answers to input questions. + + Args: + queue: list of {name: value, query: value, question: value, snippet: value} + texts: optional list of text + + Returns: + list of {name: value, answer: value} + """ + + if self.embeddings and "extractor" in self.pipelines: + # Get extractor instance + extractor = self.pipelines["extractor"] + + # Run extractor and return results as dicts + return extractor(queue, texts) + + return None + + def label(self, text, labels): + """ + Applies a zero shot classifier to text using a list of labels. Returns a list of + {id: value, score: value} sorted by highest score, where id is the index in labels. + + Args: + text: text|list + labels: list of labels + + Returns: + list of {id: value, score: value} per text element + """ + + if "labels" in self.pipelines: + # Text is a string + if isinstance(text, str): + return [{"id": uid, "score": float(score)} for uid, score in self.pipelines["labels"](text, labels)] + + # Text is a list + return [[{"id": uid, "score": float(score)} for uid, score in result] for result in self.pipelines["labels"](text, labels)] + + return None + + def pipeline(self, name, *args, **kwargs): + """ + Generic pipeline execution method. + + Args: + name: pipeline name + args: pipeline positional arguments + kwargs: pipeline keyword arguments + + Returns: + pipeline results + """ + + # Backwards compatible with previous pipeline function arguments + args = args[0] if args and len(args) == 1 and isinstance(args[0], tuple) else args + + if name in self.pipelines: + return self.pipelines[name](*args, **kwargs) + + return None + + def workflow(self, name, elements): + """ + Executes a workflow. + + Args: + name: workflow name + elements: elements to process + + Returns: + processed elements + """ + + if hasattr(elements, "__len__") and hasattr(elements, "__getitem__"): + # Convert to tuples and return as a list since input is sized + elements = [tuple(element) if isinstance(element, list) else element for element in elements] + else: + # Convert to tuples and return as a generator since input is not sized + elements = (tuple(element) if isinstance(element, list) else element for element in elements) + + # Execute workflow + return self.workflows[name](elements) + + def agent(self, name, *args, **kwargs): + """ + Executes an agent. + + Args: + name: agent name + args: agent positional arguments + kwargs: agent keyword arguments + """ + + if name in self.agents: + return self.agents[name](*args, **kwargs) + + return None + + def wait(self): + """ + Closes threadpool and waits for completion. + """ + + if self.pool: + self.pool.close() + self.pool.join() + self.pool = None + + +class ReadOnlyError(Exception): + """ + Error raised when trying to modify a read-only index + """ + + +class ReindexDisabledError(Exception): + """ + Error raised when trying to reindex with reindexing disabled + """ diff --git a/src/python/txtai/archive/__init__.py b/src/python/txtai/archive/__init__.py new file mode 100644 index 0000000..398bb58 --- /dev/null +++ b/src/python/txtai/archive/__init__.py @@ -0,0 +1,9 @@ +""" +Archive imports +""" + +from .base import Archive +from .compress import Compress +from .factory import ArchiveFactory +from .tar import Tar +from .zip import Zip diff --git a/src/python/txtai/archive/base.py b/src/python/txtai/archive/base.py new file mode 100644 index 0000000..8afd68d --- /dev/null +++ b/src/python/txtai/archive/base.py @@ -0,0 +1,104 @@ +""" +Archive module +""" + +import os + +from tempfile import TemporaryDirectory + +from .tar import Tar +from .zip import Zip + + +class Archive: + """ + Base class for archive instances. + """ + + def __init__(self, directory=None): + """ + Creates a new archive instance. + + Args: + directory: directory to use as working directory, defaults to a temporary directory + """ + + self.directory = directory + + def isarchive(self, path): + """ + Checks if path is an archive file based on the extension. + + Args: + path: path to check + + Returns: + True if the path ends with an archive extension, False otherwise + """ + + return path and any(path.lower().endswith(extension) for extension in [".tar.bz2", ".tar.gz", ".tar.xz", ".zip"]) + + def path(self): + """ + Gets the current working directory for this archive instance. + + Returns: + archive working directory + """ + + # Default to a temporary directory. All files created in this directory will be deleted + # when this archive instance goes out of scope. + if not self.directory: + # pylint: disable=R1732 + self.directory = TemporaryDirectory() + + return self.directory.name if isinstance(self.directory, TemporaryDirectory) else self.directory + + def load(self, path, compression=None): + """ + Extracts file at path to archive working directory. + + Args: + path: path to archive file + compression: compression format, infers from path if not provided + """ + + # Unpack compressed file + compress = self.create(path, compression) + compress.unpack(path, self.path()) + + def save(self, path, compression=None): + """ + Archives files in archive working directory to file at path. + + Args: + path: path to archive file + compression: compression format, infers from path if not provided + """ + + # Create output directory, if necessary + output = os.path.dirname(path) + if output: + os.makedirs(output, exist_ok=True) + + # Pack into compressed file + compress = self.create(path, compression) + compress.pack(self.path(), path) + + def create(self, path, compression): + """ + Method to construct a Compress instance. + + Args: + path: file path + compression: compression format, infers using file extension if not provided + + Returns: + Compress + """ + + # Infer compression format from path if not provided + compression = compression if compression else path.lower().split(".")[-1] + + # Create compression instance + return Zip() if compression == "zip" else Tar() diff --git a/src/python/txtai/archive/compress.py b/src/python/txtai/archive/compress.py new file mode 100644 index 0000000..2411ba7 --- /dev/null +++ b/src/python/txtai/archive/compress.py @@ -0,0 +1,51 @@ +""" +Compress module +""" + +import os + + +class Compress: + """ + Base class for Compress instances. + """ + + def pack(self, path, output): + """ + Compresses files in directory path to file output. + + Args: + path: input directory path + output: output file + """ + + raise NotImplementedError + + def unpack(self, path, output): + """ + Extracts all files in path to output. + + Args: + path: input file path + output: output directory + """ + + raise NotImplementedError + + def validate(self, directory, path): + """ + Validates path is under directory. + + Args: + directory: base directory + path: path to validate + + Returns: + True if path is under directory, False otherwise + """ + + directory = os.path.abspath(directory) + path = os.path.abspath(path) + prefix = os.path.commonpath([directory, path]) + + return prefix == directory diff --git a/src/python/txtai/archive/factory.py b/src/python/txtai/archive/factory.py new file mode 100644 index 0000000..57b63a9 --- /dev/null +++ b/src/python/txtai/archive/factory.py @@ -0,0 +1,25 @@ +""" +Factory module +""" + +from .base import Archive + + +class ArchiveFactory: + """ + Methods to create Archive instances. + """ + + @staticmethod + def create(directory=None): + """ + Create a new Archive instance. + + Args: + directory: optional default working directory, otherwise uses a temporary directory + + Returns: + Archive + """ + + return Archive(directory) diff --git a/src/python/txtai/archive/tar.py b/src/python/txtai/archive/tar.py new file mode 100644 index 0000000..37c5df4 --- /dev/null +++ b/src/python/txtai/archive/tar.py @@ -0,0 +1,53 @@ +""" +Tar module +""" + +import os +import tarfile + +from .compress import Compress + + +class Tar(Compress): + """ + Tar compression + """ + + def pack(self, path, output): + # Infer compression type + compression = self.compression(output) + + with tarfile.open(output, f"w:{compression}" if compression else "w") as tar: + tar.add(path, arcname=".") + + def unpack(self, path, output): + # Infer compression type + compression = self.compression(path) + + with tarfile.open(path, f"r:{compression}" if compression else "r") as tar: + # Validate paths + for member in tar.getmembers(): + fullpath = os.path.join(path, member.name) + + # Reject paths outside of base directory and links + if not self.validate(path, fullpath) or member.issym() or member.islnk(): + raise IOError(f"Invalid tar entry: {member.name}{'->' + member.linkname if member.linkname else ''}") + + # Unpack data. Apply default data filter to only allow basic TAR features. + kwargs = {"filter": "data"} if hasattr(tarfile, "data_filter") else {} + tar.extractall(output, **kwargs) + + def compression(self, path): + """ + Gets compression type for path. + + Args: + path: path to file + + Returns: + compression type + """ + + # Infer compression type from last path component. Limit to supported types. + compression = path.lower().split(".")[-1] + return compression if compression in ("bz2", "gz", "xz") else None diff --git a/src/python/txtai/archive/zip.py b/src/python/txtai/archive/zip.py new file mode 100644 index 0000000..e966020 --- /dev/null +++ b/src/python/txtai/archive/zip.py @@ -0,0 +1,36 @@ +""" +Zip module +""" + +import os + +from zipfile import ZipFile, ZIP_DEFLATED + +from .compress import Compress + + +class Zip(Compress): + """ + Zip compression + """ + + def pack(self, path, output): + with ZipFile(output, "w", ZIP_DEFLATED) as zfile: + for root, _, files in sorted(os.walk(path)): + for f in files: + # Generate archive name with relative path, if necessary + name = os.path.join(os.path.relpath(root, path), f) + + # Write file to zip + zfile.write(os.path.join(root, f), arcname=name) + + def unpack(self, path, output): + with ZipFile(path, "r") as zfile: + # Validate paths if directory specified + for fullpath in zfile.namelist(): + fullpath = os.path.join(path, fullpath) + if os.path.dirname(fullpath) and not self.validate(path, fullpath): + raise IOError(f"Invalid zip entry: {fullpath}") + + # Unpack data + zfile.extractall(output) diff --git a/src/python/txtai/cloud/__init__.py b/src/python/txtai/cloud/__init__.py new file mode 100644 index 0000000..e2fd6d7 --- /dev/null +++ b/src/python/txtai/cloud/__init__.py @@ -0,0 +1,8 @@ +""" +Cloud imports +""" + +from .base import Cloud +from .factory import CloudFactory +from .hub import HuggingFaceHub +from .storage import ObjectStorage diff --git a/src/python/txtai/cloud/base.py b/src/python/txtai/cloud/base.py new file mode 100644 index 0000000..0c5e53d --- /dev/null +++ b/src/python/txtai/cloud/base.py @@ -0,0 +1,106 @@ +""" +Cloud module +""" + +import os + +from ..archive import ArchiveFactory + + +class Cloud: + """ + Base class for cloud providers. Cloud providers sync content between local and remote storage. + """ + + def __init__(self, config): + """ + Creates a new cloud connection. + + Args: + config: cloud configuration + """ + + self.config = config + + def exists(self, path=None): + """ + Checks if path exists in cloud. If path is None, this method checks if the container exists. + + Args: + path: path to check + + Returns: + True if path or container exists, False otherwise + """ + + return self.metadata(path) is not None + + def metadata(self, path=None): + """ + Returns metadata for path from cloud. If path is None, this method returns metadata + for container. + + Args: + path: retrieve metadata for this path + + Returns: + path or container metadata if available, otherwise returns None + """ + + raise NotImplementedError + + def load(self, path=None): + """ + Retrieves content from cloud and stores locally. If path is empty, this method retrieves + all content in the container. + + Args: + path: path to retrieve + + Returns: + local path which can be different than input path + """ + + raise NotImplementedError + + def save(self, path): + """ + Sends local content stored in path to cloud. + + Args: + path: local path to sync + """ + + raise NotImplementedError + + def isarchive(self, path): + """ + Check if path is an archive file. + + Args: + path: path to check + + Returns: + True if path ends with an archive extension, false otherwise + """ + + return ArchiveFactory.create().isarchive(path) + + def listfiles(self, path): + """ + Lists files in path. If path is a file, this method returns a single element list + containing path. + + Args: + path: path to list + + Returns: + List of files + """ + + # List all files if path is a directory + if os.path.isdir(path): + return [os.path.join(path, f) for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))] + + # Path is a file + return [path] diff --git a/src/python/txtai/cloud/factory.py b/src/python/txtai/cloud/factory.py new file mode 100644 index 0000000..ff967b9 --- /dev/null +++ b/src/python/txtai/cloud/factory.py @@ -0,0 +1,70 @@ +""" +Factory module +""" + +from ..util import Resolver + +from .hub import HuggingFaceHub +from .storage import ObjectStorage, LIBCLOUD + + +class CloudFactory: + """ + Methods to create Cloud instances. + """ + + @staticmethod + def create(config): + """ + Creates a Cloud instance. + + Args: + config: cloud configuration + + Returns: + Cloud + """ + + # Cloud instance + cloud = None + + provider = config.get("provider", "") + + # Hugging Face Hub + if provider.lower() == "huggingface-hub": + cloud = HuggingFaceHub(config) + + # Cloud object storage + elif ObjectStorage.isprovider(provider): + cloud = ObjectStorage(config) + + # External provider + elif provider: + cloud = CloudFactory.resolve(provider, config) + + return cloud + + @staticmethod + def resolve(backend, config): + """ + Attempt to resolve a custom cloud backend. + + Args: + backend: backend class + config: configuration parameters + + Returns: + Cloud + """ + + try: + return Resolver()(backend)(config) + + except Exception as e: + # Failure message + message = f'Unable to resolve cloud backend: "{backend}".' + + # Append message if LIBCLOUD is not installed + message += ' Cloud storage is not available - install "cloud" extra to enable' if not LIBCLOUD else "" + + raise ImportError(message) from e diff --git a/src/python/txtai/cloud/hub.py b/src/python/txtai/cloud/hub.py new file mode 100644 index 0000000..f7f84fd --- /dev/null +++ b/src/python/txtai/cloud/hub.py @@ -0,0 +1,102 @@ +""" +Hugging Face Hub module +""" + +import os +import tempfile + +from .base import Cloud + +# Core library imports +from ..util import Library + +huggingface_hub = Library().huggingface_hub() + + +class HuggingFaceHub(Cloud): + """ + Hugging Face Hub cloud provider. + """ + + def metadata(self, path=None): + try: + # If this is an archive file path, get file metadata + if self.isarchive(path): + url = huggingface_hub.hf_hub_url( + repo_id=self.config["container"], filename=os.path.basename(path), revision=self.config.get("revision") + ) + + return huggingface_hub.get_hf_file_metadata(url=url, token=self.config.get("token")) + + # Otherwise return repository metadata + return huggingface_hub.model_info(repo_id=self.config["container"], revision=self.config.get("revision"), token=self.config.get("token")) + + except huggingface_hub.utils.RepositoryNotFoundError: + return None + + def load(self, path=None): + # Download archive file and return local path + if self.isarchive(path): + return huggingface_hub.hf_hub_download( + repo_id=self.config["container"], + filename=os.path.basename(path), + revision=self.config.get("revision"), + cache_dir=self.config.get("cache"), + token=self.config.get("token"), + ) + + # Download repository and return cached path + return huggingface_hub.snapshot_download( + repo_id=self.config["container"], revision=self.config.get("revision"), cache_dir=self.config.get("cache"), token=self.config.get("token") + ) + + def save(self, path): + # Get or create repository + huggingface_hub.create_repo( + repo_id=self.config["container"], token=self.config.get("token"), private=self.config.get("private", True), exist_ok=True + ) + + # Enable lfs-tracking of embeddings index files + self.lfstrack() + + # Upload files + for f in self.listfiles(path): + huggingface_hub.upload_file( + repo_id=self.config["container"], + revision=self.config.get("revision"), + token=self.config.get("token"), + path_or_fileobj=f, + path_in_repo=os.path.basename(f), + ) + + def lfstrack(self): + """ + Adds lfs-tracking of embeddings index files. This method adds tracking for documents and embeddings to .gitattributes. + """ + + # Get and read .gitattributes file + path = huggingface_hub.hf_hub_download( + repo_id=self.config["container"], filename=os.path.basename(".gitattributes"), token=self.config.get("token") + ) + + with open(path, "r", encoding="utf-8") as f: + content = f.read() + + # Check if index files are lfs-tracked. Update .gitattributes, if necessary. + if "embeddings " not in content: + # Add documents and embeddings to lfs tracking + content += "documents filter=lfs diff=lfs merge=lfs -text\n" + content += "embeddings filter=lfs diff=lfs merge=lfs -text\n" + + # pylint: disable=R1732 + with tempfile.NamedTemporaryFile(mode="w", delete=False) as tmp: + tmp.write(content) + attributes = tmp.name + + # Upload file + huggingface_hub.upload_file( + repo_id=self.config["container"], token=self.config.get("token"), path_or_fileobj=attributes, path_in_repo=os.path.basename(path) + ) + + # Remove temporary file + os.remove(attributes) diff --git a/src/python/txtai/cloud/storage.py b/src/python/txtai/cloud/storage.py new file mode 100644 index 0000000..e310874 --- /dev/null +++ b/src/python/txtai/cloud/storage.py @@ -0,0 +1,125 @@ +""" +Object storage module +""" + +import os + +# Conditional import +try: + from libcloud.storage.providers import get_driver, DRIVERS + from libcloud.storage.types import ContainerDoesNotExistError, ObjectDoesNotExistError + + LIBCLOUD = True +except ImportError: + LIBCLOUD, DRIVERS = False, None + + +from .base import Cloud + + +class ObjectStorage(Cloud): + """ + Object storage cloud provider backed by Apache libcloud. + """ + + @staticmethod + def isprovider(provider): + """ + Checks if this provider is an object storage provider. + + Args: + provider: provider name + + Returns: + True if this is an object storage provider + """ + + return LIBCLOUD and provider and provider.lower() in [x.lower() for x in DRIVERS] + + def __init__(self, config): + super().__init__(config) + + if not LIBCLOUD: + raise ImportError('Cloud object storage is not available - install "cloud" extra to enable') + + # Get driver for provider + driver = get_driver(config["provider"]) + + # Get client connection + self.client = driver( + config.get("key", os.environ.get("ACCESS_KEY")), + config.get("secret", os.environ.get("ACCESS_SECRET")), + **{field: config.get(field) for field in ["host", "port", "region", "token"] if config.get(field)}, + ) + + def metadata(self, path=None): + try: + # If this is an archive path, check if file exists + if self.isarchive(path): + return self.client.get_object(self.config["container"], self.objectname(path)) + + # Otherwise check if container exists + return self.client.get_container(self.config["container"]) + except (ContainerDoesNotExistError, ObjectDoesNotExistError): + return None + + def load(self, path=None): + # Download archive file + if self.isarchive(path): + obj = self.client.get_object(self.config["container"], self.objectname(path)) + + # Create local directory, if necessary + directory = os.path.dirname(path) + if directory: + os.makedirs(directory, exist_ok=True) + + obj.download(path, overwrite_existing=True) + + # Download files in container. Optionally filter with a provided prefix. + else: + container = self.client.get_container(self.config["container"]) + for obj in container.list_objects(prefix=self.config.get("prefix")): + # Derive local path and directory + localpath = os.path.join(path, obj.name) + directory = os.path.dirname(localpath) + + # Create local directory, if necessary + os.makedirs(directory, exist_ok=True) + + # Download file locally + obj.download(localpath, overwrite_existing=True) + + return path + + def save(self, path): + # Get or create container + try: + container = self.client.get_container(self.config["container"]) + except ContainerDoesNotExistError: + container = self.client.create_container(self.config["container"]) + + # Upload files + for f in self.listfiles(path): + with open(f, "rb") as iterator: + self.client.upload_object_via_stream(iterator=iterator, container=container, object_name=self.objectname(f)) + + def objectname(self, name): + """ + Derives an object name. This method checks if a prefix configuration parameter is present and combines + it with the input name parameter. + + Args: + name: input name + + Returns: + object name + """ + + # Get base name + name = os.path.basename(name) + + # Get optional prefix/folder + prefix = self.config.get("prefix") + + # Prepend prefix, if applicable + return f"{prefix}/{name}" if prefix else name diff --git a/src/python/txtai/console/__init__.py b/src/python/txtai/console/__init__.py new file mode 100644 index 0000000..890e1e2 --- /dev/null +++ b/src/python/txtai/console/__init__.py @@ -0,0 +1,5 @@ +""" +Console imports +""" + +from .base import Console diff --git a/src/python/txtai/console/__main__.py b/src/python/txtai/console/__main__.py new file mode 100644 index 0000000..c0a474d --- /dev/null +++ b/src/python/txtai/console/__main__.py @@ -0,0 +1,22 @@ +""" +Main module. +""" + +import sys + +from .base import Console + + +def main(path=None): + """ + Console execution loop. + + Args: + path: model path + """ + + Console(path).cmdloop() + + +if __name__ == "__main__": + main(sys.argv[1] if len(sys.argv) > 1 else None) diff --git a/src/python/txtai/console/base.py b/src/python/txtai/console/base.py new file mode 100644 index 0000000..9d734a9 --- /dev/null +++ b/src/python/txtai/console/base.py @@ -0,0 +1,264 @@ +""" +Console module +""" + +import os +import shlex + +from cmd import Cmd + +# Conditional import +try: + from rich import box + from rich.console import Console as RichConsole + from rich.table import Table + + RICH = True +except ImportError: + RICH = False + +from txtai.app import Application +from txtai.embeddings import Embeddings + + +class Console(Cmd): + """ + txtai console. + """ + + def __init__(self, path=None): + """ + Creates a new command line console. + + Args: + path: path to initial configuration, if any + """ + + super().__init__() + + if not RICH: + raise ImportError('Console is not available - install "console" extra to enable') + + self.prompt = ">>> " + + # Rich console + self.console = RichConsole() + + # App parameters + self.app = None + self.path = path + + # Parameters + self.vhighlight = None + self.vlimit = None + + def preloop(self): + """ + Loads initial configuration. + """ + + self.console.print("txtai console", style="#03a9f4") + + # Load default path + if self.path: + self.load(self.path) + + def default(self, line): + """ + Default event loop. + + Args: + line: command line + """ + + # pylint: disable=W0703 + try: + command = line.lower() + if command.startswith(".config"): + self.config() + elif command.startswith(".highlight"): + self.highlight(command) + elif command.startswith(".limit"): + self.limit(command) + elif command.startswith(".load"): + command = self.split(line) + self.path = command[1] + self.load(self.path) + elif command.startswith(".workflow"): + self.workflow(line) + else: + # Search is default action + self.search(line) + except Exception: + self.console.print_exception() + + def config(self): + """ + Processes .config command. + """ + + self.console.print(self.app.config) + + def highlight(self, command): + """ + Processes .highlight command. + + Args: + command: command line + """ + + _, action = self.split(command, "#ffff00") + self.vhighlight = action + self.console.print(f"Set highlight to {self.vhighlight}") + + def limit(self, command): + """ + Processes .limit command. + + Args: + command: command line + """ + + _, action = self.split(command, 10) + self.vlimit = int(action) + self.console.print(f"Set limit to {self.vlimit}") + + def load(self, path): + """ + Processes .load command. + + Args: + path: path to configuration + """ + + if self.isyaml(path): + self.console.print(f"Loading application {path}") + self.app = Application(path) + else: + self.console.print(f"Loading index {path}") + + # Load embeddings index + self.app = Embeddings() + self.app.load(path) + + def search(self, query): + """ + Runs a search query. + + Args: + query: query to run + """ + + if self.vhighlight: + results = self.app.explain(query, limit=self.vlimit) + else: + results = self.app.search(query, limit=self.vlimit) + + columns, table = {}, Table(box=box.SQUARE, style="#03a9f4") + + # Build column list + result = results[0] + if isinstance(result, tuple): + columns = dict.fromkeys(["id", "score"]) + else: + columns = dict(result) + + # Add columns to table + columns = list(x for x in columns if x != "tokens") + for column in columns: + table.add_column(column) + + # Add rows to table + for result in results: + if isinstance(result, tuple): + table.add_row(*(self.render(result, None, x) for x in result)) + else: + table.add_row(*(self.render(result, column, result.get(column)) for column in columns)) + + # Print table to console + self.console.print(table) + + def workflow(self, command): + """ + Processes .workflow command. + + Args: + command: command line + """ + + command = shlex.split(command) + if isinstance(self.app, Application): + self.console.print(list(self.app.workflow(command[1], command[2:]))) + + def isyaml(self, path): + """ + Checks if file at path is a valid YAML file. + + Args: + path: file to check + + Returns: + True if file is valid YAML, False otherwise + """ + + if os.path.exists(path) and os.path.isfile(path): + try: + return Application.read(path) + # pylint: disable=W0702 + except: + pass + + return False + + def split(self, command, default=None): + """ + Splits command by whitespace. + + Args: + command: command line + default: default command action + + Returns: + command action + """ + + values = command.split(" ", 1) + return values if len(values) > 1 else (command, default) + + def render(self, result, column, value): + """ + Renders a search result column value. + + Args: + result: result row + column: column name + value: column value + """ + + if isinstance(value, float): + return f"{value:.4f}" + + # Explain highlighting + if column == "text" and "tokens" in result: + spans = [] + for token, score in result["tokens"]: + color = None + if score >= 0.02: + color = f"b {self.vhighlight}" + + spans.append((token, score, color)) + + if result["score"] >= 0.05 and not [color for _, _, color in spans if color]: + mscore = max(score for _, score, _ in spans) + spans = [(token, score, f"b {self.vhighlight}" if score == mscore else color) for token, score, color in spans] + + output = "" + for token, _, color in spans: + if color: + output += f"[{color}]{token}[/{color}] " + else: + output += f"{token} " + + return output + + return str(value) diff --git a/src/python/txtai/data/__init__.py b/src/python/txtai/data/__init__.py new file mode 100644 index 0000000..7f25379 --- /dev/null +++ b/src/python/txtai/data/__init__.py @@ -0,0 +1,10 @@ +""" +Data imports +""" + +from .base import Data +from .labels import Labels +from .questions import Questions +from .sequences import Sequences +from .texts import Texts +from .tokens import Tokens diff --git a/src/python/txtai/data/base.py b/src/python/txtai/data/base.py new file mode 100644 index 0000000..86012e2 --- /dev/null +++ b/src/python/txtai/data/base.py @@ -0,0 +1,138 @@ +""" +Data module +""" + +from .tokens import Tokens + + +class Data: + """ + Base data tokenization class. + """ + + def __init__(self, tokenizer, columns, maxlength): + """ + Creates new base instance for tokenizing data. + + Args: + tokenizer: model tokenizer + columns: column names + maxlength: maximum sequence length + """ + + self.tokenizer = tokenizer + self.columns = columns + self.maxlength = maxlength + + def __call__(self, train, validation, workers): + """ + Tokenizes training and validation data and returns processed datasets. + + Args: + train: training data + validation: validation data + workers: number of concurrent tokenizers when processing datasets, only main process used when set to None + + Returns: + (train, validation) + """ + + return (self.prepare(train, self.process, workers), self.prepare(validation, self.process, workers) if validation else None) + + def prepare(self, data, fn, workers): + """ + Prepares and tokenizes data for training. + + Args: + data: input data + fn: tokenize processing function to apply + workers: number of concurrent tokenizers when processing datasets, only main process used when set to None + + Returns: + tokens + """ + + if hasattr(data, "map"): + # Hugging Face dataset + tokens = data.map(fn, batched=True, num_proc=workers, remove_columns=data.column_names) + else: + # Re-orient data into columns for efficient batch tokenization + columns = {} + if hasattr(data, "columns"): + # Polars/pandas DataFrame + for column in data.columns: + columns[column] = list(data[column]) + else: + # Iterable dicts + for row in data: + for column in row.keys(): + if column not in columns: + columns[column] = [] + + columns[column].append(row[column]) + + # Process column-oriented data + tokens = Tokens(fn(columns)) + + return tokens + + def labels(self, data): + """ + Extracts a list of unique labels from data. + + Args: + data: input data + + Returns: + list of unique labels + """ + + # Last column is label + column = self.columns[-1] + + # Return length of labels if it's an array + length = self.length(data[column][0] if hasattr(data, "columns") else data[0][column]) + if length: + return length + + if hasattr(data, "map"): + # Hugging Face dataset + labels = sorted(data.unique(self.columns[-1])) + elif hasattr(data, "columns"): + # Polars/pandas DataFrame + labels = sorted(data[self.columns[-1]].unique()) + else: + # Iterable dicts + labels = sorted({row[self.columns[-1]] for row in data}) + + # Labels are single numeric values per entry + # - Consider a regression task if at least one label isn't an integer + # - Otherwise use number of labels for a classification task + return 1 if [x for x in labels if float(x) != int(x)] else len(labels) + + def process(self, data): + """ + Tokenizes batch of input data + + Args: + data: input data batch + + Returns: + tokenized data + """ + + return data + + def length(self, value): + """ + Returns the length of value if value has a len function defined. Otherwise, + None is returned. + + Args: + value: value to check + + Returns: + length of value if available, otherwise returns None + """ + + return len(value) if hasattr(value, "__len__") else None diff --git a/src/python/txtai/data/labels.py b/src/python/txtai/data/labels.py new file mode 100644 index 0000000..5481694 --- /dev/null +++ b/src/python/txtai/data/labels.py @@ -0,0 +1,42 @@ +""" +Labels module +""" + +from .base import Data + + +class Labels(Data): + """ + Tokenizes text-classification datasets as input for training text-classification models. + """ + + def __init__(self, tokenizer, columns, maxlength): + """ + Creates a new instance for tokenizing Labels training data. + + Args: + tokenizer: model tokenizer + columns: tuple of columns to use for text/label + maxlength: maximum sequence length + """ + + super().__init__(tokenizer, columns, maxlength) + + # Standardize columns + if not self.columns: + self.columns = ("text", None, "label") + elif len(columns) < 3: + self.columns = (self.columns[0], None, self.columns[-1]) + + def process(self, data): + # Column keys + text1, text2, label = self.columns + + # Tokenizer inputs can be single string or string pair, depending on task + text = (data[text1], data[text2]) if text2 else (data[text1],) + + # Tokenize text and add label + inputs = self.tokenizer(*text, max_length=self.maxlength, padding=True, truncation=True) + inputs[label] = data[label] + + return inputs diff --git a/src/python/txtai/data/questions.py b/src/python/txtai/data/questions.py new file mode 100644 index 0000000..f47c7de --- /dev/null +++ b/src/python/txtai/data/questions.py @@ -0,0 +1,135 @@ +""" +Questions module +""" + +from .base import Data + + +class Questions(Data): + """ + Tokenizes question-answering datasets as input for training question-answering models. + """ + + def __init__(self, tokenizer, columns, maxlength, stride): + """ + Creates a new instance for tokenizing Questions training data. + + Args: + tokenizer: model tokenizer + columns: tuple of columns to use for question/context/answer + maxlength: maximum sequence length + stride: chunk size for splitting data for QA tasks + """ + + super().__init__(tokenizer, columns, maxlength) + + if not self.columns: + self.columns = ("question", "context", "answers") + + self.question, self.context, self.answer = self.columns + self.stride = stride + self.rpad = tokenizer.padding_side == "right" + + def process(self, data): + # Tokenize data + tokenized = self.tokenize(data) + + # Get mapping of overflowing tokens and answer offsets + samples = tokenized.pop("overflow_to_sample_mapping") + offsets = tokenized.pop("offset_mapping") + + # Start/end positions + tokenized["start_positions"] = [] + tokenized["end_positions"] = [] + + for x, offset in enumerate(offsets): + # Label NO ANSWER with CLS token + inputids = tokenized["input_ids"][x] + clstoken = inputids.index(self.tokenizer.cls_token_id) + + # Sequence ids + sequences = tokenized.sequence_ids(x) + + # Get and format answer + answers = self.answers(data, samples[x]) + + # If no answers are given, set cls token as answer. + if len(answers["answer_start"]) == 0: + tokenized["start_positions"].append(clstoken) + tokenized["end_positions"].append(clstoken) + else: + # Start/end character index of the answer in the text. + startchar = answers["answer_start"][0] + endchar = startchar + len(answers["text"][0]) + + # Start token index of the current span in the text. + start = 0 + while sequences[start] != (1 if self.rpad else 0): + start += 1 + + # End token index of the current span in the text. + end = len(inputids) - 1 + while sequences[end] != (1 if self.rpad else 0): + end -= 1 + + # Map start character and end character to matching token index + while start < len(offset) and offset[start][0] <= startchar: + start += 1 + tokenized["start_positions"].append(start - 1) + + while offset[end][1] >= endchar: + end -= 1 + tokenized["end_positions"].append(end + 1) + + return tokenized + + def tokenize(self, data): + """ + Tokenizes batch of data + + Args: + data: input data batch + + Returns: + tokenized data + """ + + # Trim question whitespace + data[self.question] = [x.lstrip() for x in data[self.question]] + + # Tokenize records + return self.tokenizer( + data[self.question if self.rpad else self.context], + data[self.context if self.rpad else self.question], + truncation="only_second" if self.rpad else "only_first", + max_length=self.maxlength, + stride=self.stride, + return_overflowing_tokens=True, + return_offsets_mapping=True, + padding=True, + ) + + def answers(self, data, index): + """ + Gets and formats an answer. + + Args: + data: input examples + index: answer index to retrieve + + Returns: + answers dict + """ + + # Answer mappings + answers = data[self.answer][index] + context = data[self.context][index] + + # Handle mapping string answers to dict + if not isinstance(answers, dict): + if not answers: + answers = {"text": [], "answer_start": []} + else: + answers = {"text": [answers], "answer_start": [context.index(answers)]} + + return answers diff --git a/src/python/txtai/data/sequences.py b/src/python/txtai/data/sequences.py new file mode 100644 index 0000000..a34596a --- /dev/null +++ b/src/python/txtai/data/sequences.py @@ -0,0 +1,47 @@ +""" +Sequences module +""" + +from .base import Data + + +class Sequences(Data): + """ + Tokenizes sequence-sequence datasets as input for training sequence-sequence models + """ + + def __init__(self, tokenizer, columns, maxlength, prefix): + """ + Creates a new instance for tokenizing Sequences training data. + + Args: + tokenizer: model tokenizer + columns: tuple of columns to use for text/label + maxlength: maximum sequence length + prefix: source prefix + """ + + super().__init__(tokenizer, columns, maxlength) + + # Standardize columns + if not self.columns: + self.columns = ("source", "target") + + # Save source prefix + self.prefix = prefix + + def process(self, data): + # Column keys + source, target = self.columns + + # Tokenize source + source = [self.prefix + x if self.prefix else x for x in data[source]] + inputs = self.tokenizer(source, max_length=self.maxlength, padding=False, truncation=True) + + # Tokenize targets + targets = self.tokenizer(data[target], max_length=self.maxlength, padding=False, truncation=True) + + # Combine inputs + inputs["labels"] = targets["input_ids"] + + return inputs diff --git a/src/python/txtai/data/texts.py b/src/python/txtai/data/texts.py new file mode 100644 index 0000000..4817efa --- /dev/null +++ b/src/python/txtai/data/texts.py @@ -0,0 +1,119 @@ +""" +Texts module +""" + +from itertools import chain + +from .base import Data + + +class Texts(Data): + """ + Tokenizes text datasets as input for training language models. + """ + + def __init__(self, tokenizer, columns, maxlength, merge): + """ + Creates a new instance for tokenizing Texts training data. + + Args: + tokenizer: model tokenizer + columns: tuple of columns to use for text + maxlength: maximum sequence length + merge: determines how chunks are combined for language modeling tasks - "concat" (default), "pack" or None + """ + + super().__init__(tokenizer, columns, maxlength) + + # Standardize columns + if not self.columns: + self.columns = ("text", None) + + # Method to combine chunks + self.merge = merge + + def process(self, data): + # Column keys + text1, text2 = self.columns + + # Tokenizer inputs can be single string or string pair, depending on task + text = (data[text1], data[text2]) if text2 else (data[text1],) + + # Tokenize text and add label + inputs = self.tokenizer(*text, return_special_tokens_mask=True) + + # Combine inputs based on parameters + return self.concat(inputs) if self.merge == "concat" else self.pack(inputs) if self.merge == "pack" else inputs + + def concat(self, inputs): + """ + Concatenates tokenized text into chunks of maxlength. This method guarantees that each chunk is maxlength + size and splits data across multiple chunks if needed. + + This is best with general language modeling tasks like masked language modeling that are streams of text. + + Args: + inputs: tokenized input + + Returns: + Chunks of tokenized text each with a size of maxlength + """ + + # Concatenate tokenized text + concat = {k: list(chain(*inputs[k])) for k in inputs.keys()} + + # Calculate total length + length = len(concat[list(inputs.keys())[0]]) + + # Ensure total is multiple of maxlength, drop last incomplete chunk + if length >= self.maxlength: + length = (length // self.maxlength) * self.maxlength + + # Split into chunks of maxlength + result = {k: [v[x : x + self.maxlength] for x in range(0, length, self.maxlength)] for k, v in concat.items()} + + return result + + def pack(self, inputs): + """ + Packs tokenized text into chunks up to maxlength. This method guarantees that data is never split across + multiple chunks. + + This is best with instruction/prompt learning where it's crucial to ensure entire records are preserved. + + Args: + inputs: tokenized input + + Returns: + Chunks of tokenized text each with a size of maxlength + """ + + # Sort lists by length descending + inputs = {k: sorted(v, key=len, reverse=True) for k, v in inputs.items()} + + # Inputs has lists of equal length per column + columns = list(inputs.keys()) + + # Create empty results dict + results = {column: [] for column in columns} + + # Iterate over values in first column since all column lengths per row are equal + length, index, rows = 0, 0, inputs[columns[0]] + for x, row in enumerate(rows): + length += len(row) + nextlength = len(rows[x + 1]) if x < len(rows) - 1 else 0 + + # New row + if (length + nextlength) >= self.maxlength: + for column in columns: + results[column].append(list(chain(*inputs[column][index : x + 1]))) + + # Reset length and index + length, index = 0, x + 1 + + # Last row + if length: + for column in columns: + results[column].append(list(chain(*inputs[column][index : len(rows)]))) + + return results diff --git a/src/python/txtai/data/tokens.py b/src/python/txtai/data/tokens.py new file mode 100644 index 0000000..6433851 --- /dev/null +++ b/src/python/txtai/data/tokens.py @@ -0,0 +1,31 @@ +""" +Tokens module +""" + +# Core library imports +from ..util import Library + +Dataset = Library().dataset() + + +class Tokens(Dataset): + """ + Default dataset used to hold tokenized data. + """ + + def __init__(self, columns): + self.data = [] + + # Map column-oriented data to rows + for column in columns: + for x, value in enumerate(columns[column]): + if len(self.data) <= x: + self.data.append({}) + + self.data[x][column] = value + + def __len__(self): + return len(self.data) + + def __getitem__(self, index): + return self.data[index] diff --git a/src/python/txtai/database/__init__.py b/src/python/txtai/database/__init__.py new file mode 100644 index 0000000..c564b2d --- /dev/null +++ b/src/python/txtai/database/__init__.py @@ -0,0 +1,14 @@ +""" +Database imports +""" + +from .base import Database +from .client import Client +from .duckdb import DuckDB +from .embedded import Embedded +from .encoder import * +from .factory import DatabaseFactory +from .rdbms import RDBMS +from .schema import * +from .sqlite import SQLite +from .sql import * diff --git a/src/python/txtai/database/base.py b/src/python/txtai/database/base.py new file mode 100644 index 0000000..04a51d8 --- /dev/null +++ b/src/python/txtai/database/base.py @@ -0,0 +1,347 @@ +""" +Database module +""" + +import logging +import types + +from .encoder import EncoderFactory +from .sql import SQL, SQLError, Token + +# Logging configuration +logger = logging.getLogger(__name__) + + +class Database: + """ + Base class for database instances. This class encapsulates a content database used for + storing field content as dicts and objects. The database instance works in conjuction + with a vector index to execute SQL-driven similarity search. + """ + + def __init__(self, config): + """ + Creates a new Database. + + Args: + config: database configuration + """ + + # Initialize configuration + self.configure(config) + + def load(self, path): + """ + Loads a database path. + + Args: + path: database url + """ + + raise NotImplementedError + + def insert(self, documents, index=0): + """ + Inserts documents into the database. + + Args: + documents: list of documents to save + index: indexid offset, used for internal ids + """ + + raise NotImplementedError + + def delete(self, ids): + """ + Deletes documents from database. + + Args: + ids: ids to delete + """ + + raise NotImplementedError + + def reindex(self, config): + """ + Reindexes internal database content and streams results back. This method must renumber indexids + sequentially as deletes could have caused indexid gaps. + + Args: + config: new configuration + """ + + raise NotImplementedError + + def save(self, path): + """ + Saves a database at path. + + Args: + path: path to write database + """ + + raise NotImplementedError + + def close(self): + """ + Closes this database. + """ + + raise NotImplementedError + + def ids(self, ids): + """ + Retrieves the internal indexids for a list of ids. Multiple indexids may be present for an id in cases + where data is segmented. + + Args: + ids: list of document ids + + Returns: + list of (indexid, id) + """ + + raise NotImplementedError + + def count(self): + """ + Retrieves the count of this database instance. + + Returns: + total database count + """ + + raise NotImplementedError + + def search(self, query, similarity=None, limit=None, parameters=None, indexids=False): + """ + Runs a search against the database. Supports the following methods: + + 1. Standard similarity query. This mode retrieves content for the ids in the similarity results + 2. Similarity query as SQL. This mode will combine similarity results and database results into + a single result set. Similarity queries are set via the SIMILAR() function. + 3. SQL with no similarity query. This mode runs a SQL query and retrieves the results without similarity queries. + + Example queries: + "natural language processing" - standard similarity only query + "select * from txtai where similar('natural language processing')" - similarity query as SQL + "select * from txtai where similar('nlp') and entry > '2021-01-01'" - similarity query with additional SQL clauses + "select id, text, score from txtai where similar('nlp')" - similarity query with additional SQL column selections + "select * from txtai where entry > '2021-01-01' - database only query + + Args: + query: input query + similarity: similarity results as [(indexid, score)] + limit: maximum number of results to return + parameters: dict of named parameters to bind to placeholders + + Returns: + query results as a list of dicts + list of ([indexid, score]) if indexids is True + """ + + # Parse query if necessary + if isinstance(query, str): + query = self.parse(query) + + # Add in similar results + where = query.get("where") + + if "select" in query and similarity: + for x in range(len(similarity)): + token = f"{Token.SIMILAR_TOKEN}{x}" + if where and token in where: + where = where.replace(token, self.embed(similarity, x)) + + elif similarity: + # Not a SQL query, load similarity results, if any + where = self.embed(similarity, 0) + + # Save where + query["where"] = where + + # Run query + return self.query(query, limit, parameters, indexids) + + def parse(self, query): + """ + Parses a query into query components. + + Args: + query: input query + + Returns: + dict of parsed query components + """ + + return self.sql(query) + + def resolve(self, name, alias=None): + """ + Resolves a query column name with the database column name. This method also builds alias expressions + if alias is set. + + Args: + name: query column name + alias: alias name, defaults to None + + Returns: + database column name + """ + + raise NotImplementedError + + def embed(self, similarity, batch): + """ + Embeds similarity query results into a database query. + + Args: + similarity: similarity results as [(indexid, score)] + batch: batch id + """ + + raise NotImplementedError + + def query(self, query, limit, parameters, indexids): + """ + Executes query against database. + + Args: + query: input query + limit: maximum number of results to return + parameters: dict of named parameters to bind to placeholders + indexids: results are returned as [(indexid, score)] regardless of select clause parameters if True + + Returns: + query results + """ + + raise NotImplementedError + + def configure(self, config): + """ + Initialize configuration. + + Args: + config: configuration + """ + + # Database configuration + self.config = config + + # SQL parser + self.sql = SQL(self) + + # Load objects encoder + encoder = self.config.get("objects") + self.encoder = EncoderFactory.create(encoder) if encoder else None + + # Columns configuration + columns = config.get("columns", {}) + self.text = columns.get("text", "text") + self.object = columns.get("object", "object") + + # JSON data storage. If not set, all columns are stored (default). + # Otherwise, only columns stored in store list are kept. + # If store is set to None, no columns are stored. + self.store = ([] if columns["store"] is None else columns["store"]) if "store" in columns else None + + # Custom functions and expressions + self.functions, self.expressions = None, None + + # Load custom functions + self.registerfunctions(self.config) + + # Load custom expressions + self.registerexpressions(self.config) + + def registerfunctions(self, config): + """ + Register custom functions. This method stores the function details for underlying + database implementations to handle. + + Args: + config: database configuration + """ + + inputs = config.get("functions") if config else None + if inputs: + functions = [] + for fn in inputs: + name, argcount, deterministic = None, -1, None + + # Optional function configuration + if isinstance(fn, dict): + name, argcount, fn, deterministic = (fn.get("name"), fn.get("argcount", -1), fn["function"], fn.get("deterministic")) + + # Determine if this is a callable object or a function + if not isinstance(fn, types.FunctionType) and hasattr(fn, "__call__"): + name = name if name else fn.__class__.__name__.lower() + fn = fn.__call__ + else: + name = name if name else fn.__name__.lower() + + # Store function details + functions.append((name, argcount, fn, deterministic)) + + # pylint: disable=W0201 + self.functions = functions + + def registerexpressions(self, config): + """ + Register custom expressions. This method parses and resolves expressions for later use in SQL queries. + + Args: + config: database configuration + """ + + inputs = config.get("expressions") if config else None + if inputs: + expressions = {} + for entry in inputs: + name = entry.get("name") + expression = entry.get("expression", name) + if name: + expressions[name] = {"expression": self.sql.snippet(expression), "index": entry.get("index", False)} + + # pylint: disable=W0201 + self.expressions = expressions + + def execute(self, function, *args): + """ + Executes a user query. This method has common error handling logic. + + Args: + function: database execute function + args: function arguments + + Returns: + result of function(args) + """ + + try: + # Debug log SQL + logger.debug(" ".join(["%s"] * len(args)), *args) + + return function(*args) + except Exception as e: + raise SQLError(e) from None + + def setting(self, name, default=None): + """ + Looks up database specific setting. + + Args: + name: setting name + default: default value when setting not found + + Returns: + setting value + """ + + # Get the database-specific config object + database = self.config.get(self.config["content"]) + + # Get setting value, set default value if not found + setting = database.get(name) if database else None + return setting if setting else default diff --git a/src/python/txtai/database/client.py b/src/python/txtai/database/client.py new file mode 100644 index 0000000..25efe7a --- /dev/null +++ b/src/python/txtai/database/client.py @@ -0,0 +1,227 @@ +""" +Client module +""" + +import os +import time + +# Conditional import +try: + from sqlalchemy import StaticPool, Text, cast, create_engine, insert, text as textsql + from sqlalchemy.orm import Session, aliased + from sqlalchemy.schema import CreateSchema + + from .schema import Base, Batch, Document, Object, Section, SectionBase, Score + + ORM = True +except ImportError: + ORM = False + +from .rdbms import RDBMS + + +class Client(RDBMS): + """ + Database client instance. This class connects to an external database using SQLAlchemy. It supports any database + that is supported by SQLAlchemy (PostgreSQL, MariaDB, etc) and has JSON support. + """ + + def __init__(self, config): + """ + Creates a new Database. + + Args: + config: database configuration parameters + """ + + super().__init__(config) + + if not ORM: + raise ImportError('SQLAlchemy is not available - install "database" extra to enable') + + # SQLAlchemy parameters + self.engine, self.dbconnection = None, None + + def save(self, path): + # Commit session and database connection + super().save(path) + + if self.dbconnection: + self.dbconnection.commit() + + def close(self): + super().close() + + # Dispose of engine, which also closes dbconnection + if self.engine: + self.engine.dispose() + + def reindexstart(self): + # Working table name + name = f"rebuild{round(time.time() * 1000)}" + + # Create working table metadata + type("Rebuild", (SectionBase,), {"__tablename__": name}) + Base.metadata.tables[name].create(self.dbconnection) + + return name + + def reindexend(self, name): + # Remove table object from metadata + Base.metadata.remove(Base.metadata.tables[name]) + + def jsonprefix(self): + # JSON column prefix + return "cast(" + + def jsoncolumn(self, name): + # Alias documents table + d = aliased(Document, name="d") + + # Build JSON column expression for column + return str(cast(d.data[name].as_string(), Text).compile(dialect=self.engine.dialect, compile_kwargs={"literal_binds": True})) + + def createtables(self): + # Create tables + Base.metadata.create_all(self.dbconnection, checkfirst=True) + + # Clear existing data - table schema is created upon connecting to database + for table in ["sections", "documents", "objects"]: + self.cursor.execute(f"DELETE FROM {table}") + + def finalize(self): + # Flush cached objects + self.connection.flush() + + def insertdocument(self, uid, data, tags, entry): + self.connection.add(Document(id=uid, data=data, tags=tags, entry=entry)) + + def insertobject(self, uid, data, tags, entry): + self.connection.add(Object(id=uid, object=data, tags=tags, entry=entry)) + + def insertsection(self, index, uid, text, tags, entry): + # Save text section + self.connection.add(Section(indexid=index, id=uid, text=text, tags=tags, entry=entry)) + + def createbatch(self): + # Create temporary batch table, if necessary + Base.metadata.tables["batch"].create(self.dbconnection, checkfirst=True) + + def insertbatch(self, indexids, ids, batch): + if indexids: + self.connection.execute(insert(Batch), [{"indexid": i, "batch": batch} for i in indexids]) + if ids: + self.connection.execute(insert(Batch), [{"id": str(uid), "batch": batch} for uid in ids]) + + def createscores(self): + # Create temporary scores table, if necessary + Base.metadata.tables["scores"].create(self.dbconnection, checkfirst=True) + + def insertscores(self, scores): + # Average scores by id + if scores: + self.connection.execute(insert(Score), [{"indexid": i, "score": sum(s) / len(s)} for i, s in scores.items()]) + + def connect(self, path=None): + # Connection URL + content = self.config.get("content") + + # Read ENV variable, if necessary + content = os.environ.get("CLIENT_URL") if content == "client" else content + + # Create engine using database URL + self.engine = create_engine(content, poolclass=StaticPool, echo=False, json_serializer=lambda x: x) + self.dbconnection = self.engine.connect() + + # Create database session + database = Session(self.dbconnection) + + # Set default schema, if necessary + schema = self.config.get("schema") + if schema: + with self.engine.begin(): + self.sqldialect(database, CreateSchema(schema, if_not_exists=True)) + + self.sqldialect(database, textsql("SET search_path TO :schema"), {"schema": schema}) + + return database + + def getcursor(self): + return Cursor(self.connection) + + def rows(self): + return self.cursor + + def addfunctions(self): + return + + def sqldialect(self, database, sql, parameters=None): + """ + Executes a SQL statement based on the current SQL dialect. + + Args: + database: current database + sql: SQL to execute + parameters: optional bind parameters + """ + + args = (sql, parameters) if self.engine.dialect.name == "postgresql" else (textsql("SELECT 1"),) + database.execute(*args) + + +class Cursor: + """ + Implements basic compatibility with the Python DB-API. + """ + + def __init__(self, connection): + self.connection = connection + self.result = None + + def __iter__(self): + return self.result + + def execute(self, statement, parameters=None): + """ + Executes statement. + + Args: + statement: statement to execute + parameters: optional dictionary with bind parameters + """ + + if isinstance(statement, str): + statement = textsql(statement) + + self.result = self.connection.execute(statement, parameters) + + def fetchall(self): + """ + Fetches all rows from the current result. + + Returns: + all rows from current result + """ + + return self.result.all() if self.result else None + + def fetchone(self): + """ + Fetches first row from current result. + + Returns: + first row from current result + """ + + return self.result.first() if self.result else None + + @property + def description(self): + """ + Returns columns for current result. + + Returns: + list of columns + """ + + return [(key,) for key in self.result.keys()] if self.result else None diff --git a/src/python/txtai/database/duckdb.py b/src/python/txtai/database/duckdb.py new file mode 100644 index 0000000..1c010ae --- /dev/null +++ b/src/python/txtai/database/duckdb.py @@ -0,0 +1,177 @@ +""" +DuckDB module +""" + +import os +import re + +from typing import get_type_hints +from tempfile import TemporaryDirectory + +# Conditional import +try: + import duckdb + + DUCKDB = True +except ImportError: + DUCKDB = False + +from .embedded import Embedded +from .schema import Statement + + +class DuckDB(Embedded): + """ + Database instance backed by DuckDB. + """ + + # Delete single document and object + DELETE_DOCUMENT = "DELETE FROM documents WHERE id = ?" + DELETE_OBJECT = "DELETE FROM objects WHERE id = ?" + + def __init__(self, config): + super().__init__(config) + + if not DUCKDB: + raise ImportError('DuckDB is not available - install "database" extra to enable') + + def execute(self, function, *args): + # Call parent method with DuckDB compatible arguments + return super().execute(function, *self.formatargs(args)) + + def insertdocument(self, uid, data, tags, entry): + # Delete existing document + self.cursor.execute(DuckDB.DELETE_DOCUMENT, [uid]) + + # Call parent method + super().insertdocument(uid, data, tags, entry) + + def insertobject(self, uid, data, tags, entry): + # Delete existing object + self.cursor.execute(DuckDB.DELETE_OBJECT, [uid]) + + # Call parent method + super().insertobject(uid, data, tags, entry) + + def connect(self, path=":memory:"): + # Create connection and start a transaction + # pylint: disable=I1101 + connection = duckdb.connect(path) + connection.begin() + + return connection + + def getcursor(self): + return self.connection + + def jsonprefix(self): + # Return json column prefix + return "json_extract_string(data" + + def jsoncolumn(self, name): + # Generate json column using json_extract function + return f"json_extract_string(data, '$.{name}')" + + def rows(self): + # Iteratively retrieve and yield rows + batch = 256 + rows = self.cursor.fetchmany(batch) + while rows: + yield from rows + rows = self.cursor.fetchmany(batch) + + def addfunctions(self): + self.loadfunctions(self.connection) + + def copy(self, path): + # Delete existing file, if necessary + if os.path.exists(path): + os.remove(path) + + # Create database connection + # pylint: disable=I1101 + connection = duckdb.connect(path) + + # List of tables + tables = ["documents", "objects", "sections"] + + with TemporaryDirectory() as directory: + # Export existing tables + for table in tables: + self.connection.execute(f"COPY {table} TO '{directory}/{table}.parquet' (FORMAT parquet)") + + # Create initial schema + for schema in [Statement.CREATE_DOCUMENTS, Statement.CREATE_OBJECTS, Statement.CREATE_SECTIONS % "sections"]: + connection.execute(schema) + + # Import tables into new schema + for table in tables: + connection.execute(f"COPY {table} FROM '{directory}/{table}.parquet' (FORMAT parquet)") + + # Copy functions + self.loadfunctions(connection) + + # Copy indexes + for (sql,) in self.connection.execute("SELECT sql FROM duckdb_indexes()").fetchall(): + connection.execute(sql) + + # Sync data to database file + connection.execute("CHECKPOINT") + + # Start transaction + connection.begin() + + return connection + + def formatargs(self, args): + """ + DuckDB doesn't support named parameters. This method replaces named parameters with question marks + and makes parameters a list. + + Args: + args: input arguments + + Returns: + DuckDB compatible args + """ + + if args and len(args) > 1: + # Unpack query args + query, parameters = args + + # Iterate over parameters + # - Replace named parameters with ?'s + # - Build list of value with position indexes + params = [] + for key, value in parameters.items(): + pattern = rf"\:{key}(?=\s|$)" + match = re.search(pattern, query) + if match: + query = re.sub(pattern, "?", query, count=1) + params.append((match.start(), value)) + + # Repack query and parameter list + args = (query, [value for _, value in sorted(params, key=lambda x: x[0])]) + + return args + + def loadfunctions(self, connection): + """ + Load database functions. + + Args: + connection: connection to create functions + """ + + if self.functions and connection: + for name, _, fn, deterministic in self.functions: + # Create function if it doesn't already exist + result = connection.execute("SELECT 1 FROM duckdb_functions() WHERE function_name = ?", [name]).fetchone() + if not result: + # Get function type hints + hints = get_type_hints(fn) + + # Create database functions + connection.create_function( + name, fn, return_type=hints.get("return", str), side_effects=not deterministic if deterministic is not None else False + ) diff --git a/src/python/txtai/database/embedded.py b/src/python/txtai/database/embedded.py new file mode 100644 index 0000000..a71b022 --- /dev/null +++ b/src/python/txtai/database/embedded.py @@ -0,0 +1,76 @@ +""" +Embedded module +""" + +from .rdbms import RDBMS + + +class Embedded(RDBMS): + """ + Base class for embedded relational databases. An embedded relational database stores all content in a local file. + """ + + def __init__(self, config): + """ + Creates a new Database. + + Args: + config: database configuration parameters + """ + + super().__init__(config) + + # Path to database file + self.path = None + + def load(self, path): + # Call parent logic + super().load(path) + + # Store path reference + self.path = path + + def save(self, path): + # Temporary database + if not self.path: + # Save temporary database + self.connection.commit() + + # Copy data from current to new + connection = self.copy(path) + + # Close temporary database + self.connection.close() + + # Point connection to new connection + self.session(connection=connection) + self.path = path + + # Paths are equal, commit changes + elif self.path == path: + self.connection.commit() + + # New path is different from current path, copy data and continue using current connection + else: + self.copy(path).close() + + def jsonprefix(self): + # Return json column prefix + return "json_extract(data" + + def jsoncolumn(self, name): + # Generate json column using json_extract function + return f"json_extract(data, '$.{name}')" + + def copy(self, path): + """ + Copies the current database into path. + + Args: + path: path to write database + + Returns: + new connection with data copied over + """ + + raise NotImplementedError diff --git a/src/python/txtai/database/encoder/__init__.py b/src/python/txtai/database/encoder/__init__.py new file mode 100644 index 0000000..0f8b3b4 --- /dev/null +++ b/src/python/txtai/database/encoder/__init__.py @@ -0,0 +1,8 @@ +""" +Encoder imports +""" + +from .base import Encoder +from .factory import EncoderFactory +from .image import ImageEncoder +from .serialize import SerializeEncoder diff --git a/src/python/txtai/database/encoder/base.py b/src/python/txtai/database/encoder/base.py new file mode 100644 index 0000000..7a02fc4 --- /dev/null +++ b/src/python/txtai/database/encoder/base.py @@ -0,0 +1,37 @@ +""" +Encoder module +""" + +from io import BytesIO + + +class Encoder: + """ + Encodes and decodes object content. The base encoder works only with byte arrays. It can be extended to encode different datatypes. + """ + + def encode(self, obj): + """ + Encodes an object to a byte array using the encoder. + + Args: + obj: object to encode + + Returns: + encoded object as a byte array + """ + + return obj + + def decode(self, data): + """ + Decodes input byte array into an object using this encoder. + + Args: + data: encoded data + + Returns: + decoded object + """ + + return BytesIO(data) if data else None diff --git a/src/python/txtai/database/encoder/factory.py b/src/python/txtai/database/encoder/factory.py new file mode 100644 index 0000000..559e7df --- /dev/null +++ b/src/python/txtai/database/encoder/factory.py @@ -0,0 +1,56 @@ +""" +Encoder factory module +""" + +from ...util import Resolver + +from .base import Encoder +from .serialize import SerializeEncoder + + +class EncoderFactory: + """ + Encoder factory. Creates new Encoder instances. + """ + + @staticmethod + def get(encoder): + """ + Gets a new instance of encoder class. + + Args: + encoder: Encoder instance class + + Returns: + Encoder class + """ + + # Local task if no package + if "." not in encoder: + # Get parent package + encoder = ".".join(__name__.split(".")[:-1]) + "." + encoder.capitalize() + "Encoder" + + return Resolver()(encoder) + + @staticmethod + def create(encoder): + """ + Creates a new Encoder instance. + + Args: + encoder: Encoder instance class + + Returns: + Encoder + """ + + # Return default encoder + if encoder is True: + return Encoder() + + # Supported serialization methods + if encoder in ["messagepack", "pickle"]: + return SerializeEncoder(encoder) + + # Get Encoder instance + return EncoderFactory.get(encoder)() diff --git a/src/python/txtai/database/encoder/image.py b/src/python/txtai/database/encoder/image.py new file mode 100644 index 0000000..d707b3a --- /dev/null +++ b/src/python/txtai/database/encoder/image.py @@ -0,0 +1,43 @@ +""" +ImageEncoder module +""" + +from io import BytesIO + +# Conditional import +try: + from PIL import Image + + PIL = True +except ImportError: + PIL = False + +from .base import Encoder + + +class ImageEncoder(Encoder): + """ + Encodes and decodes Image objects as compressed binary content, using the original image's algorithm. + """ + + def __init__(self): + """ + Creates a new ImageEncoder. + """ + + if not PIL: + raise ImportError('ImageEncoder is not available - install "database" extra to enable') + + def encode(self, obj): + # Create byte stream + output = BytesIO() + + # Write image to byte stream + obj.save(output, format=obj.format, quality="keep") + + # Return byte array + return output.getvalue() + + def decode(self, data): + # Return a PIL image + return Image.open(BytesIO(data)) if data else None diff --git a/src/python/txtai/database/encoder/serialize.py b/src/python/txtai/database/encoder/serialize.py new file mode 100644 index 0000000..1badc49 --- /dev/null +++ b/src/python/txtai/database/encoder/serialize.py @@ -0,0 +1,28 @@ +""" +SerializeEncoder module +""" + +from ...serialize import SerializeFactory + +from .base import Encoder + + +class SerializeEncoder(Encoder): + """ + Encodes and decodes objects using the internal serialize package. + """ + + def __init__(self, method): + # Parent constructor + super().__init__() + + # Pickle serialization + self.serializer = SerializeFactory.create(method) + + def encode(self, obj): + # Pickle object + return self.serializer.savebytes(obj) + + def decode(self, data): + # Unpickle to object + return self.serializer.loadbytes(data) diff --git a/src/python/txtai/database/factory.py b/src/python/txtai/database/factory.py new file mode 100644 index 0000000..d14b4ed --- /dev/null +++ b/src/python/txtai/database/factory.py @@ -0,0 +1,77 @@ +""" +Factory module +""" + +from urllib.parse import urlparse + +from ..util import Resolver + +from .client import Client +from .duckdb import DuckDB +from .sqlite import SQLite + + +class DatabaseFactory: + """ + Methods to create document databases. + """ + + @staticmethod + def create(config): + """ + Create a Database. + + Args: + config: database configuration parameters + + Returns: + Database + """ + + # Database instance + database = None + + # Enables document database + content = config.get("content") + + # Standardize content name + if content is True: + content = "sqlite" + + # Create document database instance + if content == "duckdb": + database = DuckDB(config) + elif content == "sqlite": + database = SQLite(config) + elif content: + # Check if content is a URL + url = urlparse(content) + if content == "client" or url.scheme: + # Connect to database server URL + database = Client(config) + else: + # Resolve custom database if content is not a URL + database = DatabaseFactory.resolve(content, config) + + # Store config back + config["content"] = content + + return database + + @staticmethod + def resolve(backend, config): + """ + Attempt to resolve a custom backend. + + Args: + backend: backend class + config: index configuration parameters + + Returns: + Database + """ + + try: + return Resolver()(backend)(config) + except Exception as e: + raise ImportError(f"Unable to resolve database backend: '{backend}'") from e diff --git a/src/python/txtai/database/rdbms.py b/src/python/txtai/database/rdbms.py new file mode 100644 index 0000000..3deadf8 --- /dev/null +++ b/src/python/txtai/database/rdbms.py @@ -0,0 +1,596 @@ +""" +RDBMS module +""" + +import datetime +import json + +from .base import Database +from .schema import Statement + + +# pylint: disable=R0904 +class RDBMS(Database): + """ + Base relational database class. A relational database uses SQL to insert, update, delete and select from a + database instance. + """ + + def __init__(self, config): + """ + Creates a new Database. + + Args: + config: database configuration parameters + """ + + super().__init__(config) + + # Database connection + self.connection = None + self.cursor = None + + def load(self, path): + # Load an existing database. Thread locking must be handled externally. + self.session(path) + + def insert(self, documents, index=0): + # Initialize connection if not open + self.initialize() + + # Get entry date + entry = datetime.datetime.now(datetime.timezone.utc) + + # Insert documents + for uid, document, tags in documents: + if isinstance(document, dict): + # Insert document and use return value for sections table + document = self.loaddocument(uid, document, tags, entry) + + if document is not None: + if isinstance(document, list): + # Join tokens to text + document = " ".join(document) + elif not isinstance(document, str): + # If object support is enabled, save object + self.loadobject(uid, document, tags, entry) + + # Clear section text for objects, even when objects aren't inserted + document = None + + # Save text section + self.loadsection(index, uid, document, tags, entry) + index += 1 + + # Post processing logic + self.finalize() + + def delete(self, ids): + if self.connection: + # Batch ids + self.batch(ids=ids) + + # Delete all documents, objects and sections by id + self.cursor.execute(Statement.DELETE_DOCUMENTS) + self.cursor.execute(Statement.DELETE_OBJECTS) + self.cursor.execute(Statement.DELETE_SECTIONS) + + def reindex(self, config): + if self.connection: + # Set new configuration + self.configure(config) + + # Resolve text column + select = self.resolve(self.text) + + # Initialize reindex operation + name = self.reindexstart() + + # Copy data over + self.cursor.execute(Statement.COPY_SECTIONS % (name, select)) + + # Stream new results + self.cursor.execute(Statement.STREAM_SECTIONS % name) + for uid, text, data, obj, tags in self.rows(): + if not text and self.encoder and obj: + yield (uid, self.encoder.decode(obj), tags) + else: + # Read JSON data, if provided + data = json.loads(data) if data and isinstance(data, str) else data + + # Stream data if available, otherwise use section text + yield (uid, data if data else text, tags) + + # Swap as new table + self.cursor.execute(Statement.DROP_SECTIONS) + self.cursor.execute(Statement.RENAME_SECTIONS % name) + + # Finish reindex operation + self.reindexend(name) + + def save(self, path): + if self.connection: + self.connection.commit() + + def close(self): + # Close connection + if self.connection: + self.connection.close() + + def ids(self, ids): + # Batch ids and run query + self.batch(ids=ids) + self.cursor.execute(Statement.SELECT_IDS) + + # Format and return results + return self.cursor.fetchall() + + def count(self): + self.cursor.execute(Statement.COUNT_IDS) + return self.cursor.fetchone()[0] + + def resolve(self, name, alias=None): + # Standard column names + sections = ["indexid", "id", "tags", "entry"] + noprefix = ["data", "object", "score", "text"] + + # Alias expression + if alias: + # Skip if name matches alias or alias is a standard column name + if name == alias or alias in sections: + return name + + # Build alias clause + return f'{name} as "{alias}"' + + # Resolve expression + if self.expressions and name in self.expressions: + return self.expressions[name]["expression"] + + # Name is already resolved, skip + if name.startswith(self.jsonprefix()) or any(f"s.{s}" == name for s in sections): + return name + + # Standard columns - need prefixes + if name.lower() in sections: + return f"s.{name}" + + # Standard columns - no prefixes + if name.lower() in noprefix: + return name + + # Other columns come from documents.data JSON + return self.jsoncolumn(name) + + def embed(self, similarity, batch): + # Load similarity results id batch + self.batch(indexids=[i for i, _ in similarity[batch]], batch=batch) + + # Average and load all similarity scores with first batch + if not batch: + self.scores(similarity) + + # Return ids clause placeholder + return Statement.IDS_CLAUSE % batch + + # pylint: disable=R0912 + def query(self, query, limit, parameters, indexids): + # Extract query components + select = query.get("select", self.defaults()) + where = query.get("where") + groupby, having = query.get("groupby"), query.get("having") + orderby, qlimit, offset = query.get("orderby"), query.get("limit"), query.get("offset") + similarity = query.get("similar") + + # Select "indexid, score" when indexids is True + if indexids: + select = f"{self.resolve('indexid')}, {self.resolve('score')}" + + # Use JOIN when documents table is used to utilize indexes, default to LEFT JOIN + join = "JOIN" if any(x and self.jsonprefix() in x for x in [where, groupby, orderby]) else "LEFT JOIN" + + # Build query text + query = Statement.TABLE_CLAUSE % (select, join) + if where is not None: + query += f" WHERE {where}" + if groupby is not None: + query += f" GROUP BY {groupby}" + if having is not None: + query += f" HAVING {having}" + if orderby is not None: + query += f" ORDER BY {orderby}" + + # Default ORDER BY if not provided and similarity scores are available + if similarity and orderby is None: + query += " ORDER BY score DESC" + + # Apply query limit + if qlimit is not None or limit: + query += f" LIMIT {qlimit if qlimit else limit}" + + # Apply offset + if offset is not None: + query += f" OFFSET {offset}" + + # Clear scores when no similar clauses present + if not similarity: + self.scores(None) + + # Runs a user query through execute method, which has common user query handling logic + args = (query, parameters) if parameters else (query,) + self.execute(self.cursor.execute, *args) + + # Retrieve column list from query + columns = [c[0] for c in self.cursor.description] + + # Map results and return + results = [] + for row in self.rows(): + result = {} + + # Copy columns to result. In cases with duplicate column names, find one with a value + for x, column in enumerate(columns): + if column not in result or result[column] is None: + # Decode object + if self.encoder and column == self.object: + result[column] = self.encoder.decode(row[x]) + else: + result[column] = row[x] + + results.append(result) + + # Transform results, if necessary + return [(x["indexid"], x["score"]) for x in results] if indexids else results + + def initialize(self): + """ + Creates connection and initial database schema if no connection exists. + """ + + if not self.connection: + # Create database session. Thread locking must be handled externally. + self.session() + + # Create initial table schema + self.createtables() + + # Create indexes + self.createindexes() + + def session(self, path=None, connection=None): + """ + Starts a new database session. + + Args: + path: path to database file + connection: existing connection to use + """ + + # Create database connection and cursor + self.connection = connection if connection else self.connect(path) if path else self.connect() + self.cursor = self.getcursor() + + # Register custom functions - session scope + self.addfunctions() + + # Create temporary tables - session scope + self.createbatch() + self.createscores() + + def createtables(self): + """ + Creates the initial table schema. + """ + + self.cursor.execute(Statement.CREATE_DOCUMENTS) + self.cursor.execute(Statement.CREATE_OBJECTS) + self.cursor.execute(Statement.CREATE_SECTIONS % "sections") + self.cursor.execute(Statement.CREATE_SECTIONS_INDEX) + + def createindexes(self): + """ + Creates expression indexes + """ + + if self.expressions: + for key, values in self.expressions.items(): + # Create index for expression, if enabled + if values["index"]: + # Get parameters + name = f"expression_{key}".lower() + expression = values["expression"] + table = "documents" if expression.startswith(self.jsonprefix()) else "sections" + + # Execute statement + self.cursor.execute(Statement.CREATE_EXPRESSION_INDEX % (name, table, expression)) + + def finalize(self): + """ + Post processing logic run after inserting a batch of documents. Default method is no-op. + """ + + def loaddocument(self, uid, document, tags, entry): + """ + Applies pre-processing logic and inserts a document. + + Args: + uid: unique id + document: input document dictionary + tags: document tags + entry: generated entry date + + Returns: + section value + """ + + # Make a copy of document before changing + document = document.copy() + + # Get and remove object field from document + obj = document.pop(self.object) if self.object in document else None + + if document: + # Apply data filters, if necessary + data = {key: value for key, value in document.items() if key in self.store} if self.store is not None else document + + # Insert document as JSON + if data: + self.insertdocument(uid, json.dumps(data, allow_nan=False), tags, entry) + + # If text and object are both available, load object as it won't otherwise be used + if self.text in document and obj: + self.loadobject(uid, obj, tags, entry) + + # Return value to use for section - use text if available otherwise use object + return document[self.text] if self.text in document else obj + + def insertdocument(self, uid, data, tags, entry): + """ + Inserts a document. + + Args: + uid: unique id + data: document data + tags: document tags + entry: generated entry date + """ + + self.cursor.execute(Statement.INSERT_DOCUMENT, [uid, data, tags, entry]) + + def loadobject(self, uid, obj, tags, entry): + """ + Applies pre-preprocessing logic and inserts an object. + + Args: + uid: unique id + obj: input object + tags: object tags + entry: generated entry date + """ + + # If object support is enabled, save object + if self.encoder: + self.insertobject(uid, self.encoder.encode(obj), tags, entry) + + def insertobject(self, uid, data, tags, entry): + """ + Inserts an object. + + Args: + uid: unique id + data: encoded data + tags: object tags + entry: generated entry date + """ + + self.cursor.execute(Statement.INSERT_OBJECT, [uid, data, tags, entry]) + + def loadsection(self, index, uid, text, tags, entry): + """ + Applies pre-processing logic and inserts a section. + + Args: + index: index id + uid: unique id + text: section text + tags: section tags + entry: generated entry date + """ + + self.insertsection(index, uid, text, tags, entry) + + def insertsection(self, index, uid, text, tags, entry): + """ + Inserts a section. + + Args: + index: index id + uid: unique id + text: section text + tags: section tags + entry: generated entry date + """ + + # Save text section + self.cursor.execute(Statement.INSERT_SECTION, [index, uid, text, tags, entry]) + + def reindexstart(self): + """ + Starts a reindex operation. + + Returns: + temporary working table name + """ + + # Working table name + name = "rebuild" + + # Create new table to hold reordered sections + self.cursor.execute(Statement.CREATE_SECTIONS % name) + + return name + + # pylint: disable=W0613 + def reindexend(self, name): + """ + Ends a reindex operation. + + Args: + name: working table name + """ + + self.cursor.execute(Statement.CREATE_SECTIONS_INDEX) + + def batch(self, indexids=None, ids=None, batch=None): + """ + Loads ids to a temporary batch table for efficient query processing. + + Args: + indexids: list of indexids + ids: list of ids + batch: batch index, used when statement has multiple subselects + """ + + # Delete batch when batch id is empty or for batch 0 + if not batch: + self.cursor.execute(Statement.DELETE_BATCH) + + # Add batch + self.insertbatch(indexids, ids, batch) + + def createbatch(self): + """ + Creates temporary batch table. + """ + + # Create or Replace temporary batch table + self.cursor.execute(Statement.CREATE_BATCH) + + def insertbatch(self, indexids, ids, batch): + """ + Inserts batch of ids. + """ + + if indexids: + self.cursor.executemany(Statement.INSERT_BATCH_INDEXID, [(i, batch) for i in indexids]) + if ids: + self.cursor.executemany(Statement.INSERT_BATCH_ID, [(str(uid), batch) for uid in ids]) + + def scores(self, similarity): + """ + Loads a batch of similarity scores to a temporary table for efficient query processing. + + Args: + similarity: similarity results as [(indexid, score)] + """ + + # Delete scores + self.cursor.execute(Statement.DELETE_SCORES) + + if similarity: + # Average scores per id, needed for multiple similar() clauses + scores = {} + for s in similarity: + for i, score in s: + if i not in scores: + scores[i] = [] + scores[i].append(score) + + # Add scores + self.insertscores(scores) + + def createscores(self): + """ + Creates temporary scores table. + """ + + # Create or Replace temporary scores table + self.cursor.execute(Statement.CREATE_SCORES) + + def insertscores(self, scores): + """ + Inserts a batch of scores. + + Args: + scores: scores to add + """ + + # Average scores by id + if scores: + self.cursor.executemany(Statement.INSERT_SCORE, [(i, sum(s) / len(s)) for i, s in scores.items()]) + + def defaults(self): + """ + Returns a list of default columns when there is no select clause. + + Returns: + list of default columns + """ + + return "s.id, text, score" + + def connect(self, path=None): + """ + Creates a new database connection. + + Args: + path: path to database file + + Returns: + connection + """ + + raise NotImplementedError + + def getcursor(self): + """ + Opens a cursor for current connection. + + Returns: + cursor + """ + + raise NotImplementedError + + def jsonprefix(self): + """ + Returns json column prefix to test for. + + Returns: + dynamic column prefix + """ + + raise NotImplementedError + + def jsoncolumn(self, name): + """ + Builds a json extract column expression for name. + + Args: + name: column name + + Returns: + dynamic column expression + """ + + raise NotImplementedError + + def rows(self): + """ + Returns current cursor row iterator for last executed query. + + Args: + cursor: cursor + + Returns: + iterable collection of rows + """ + + raise NotImplementedError + + def addfunctions(self): + """ + Adds custom functions in current connection. + """ + + raise NotImplementedError diff --git a/src/python/txtai/database/schema/__init__.py b/src/python/txtai/database/schema/__init__.py new file mode 100644 index 0000000..c91db78 --- /dev/null +++ b/src/python/txtai/database/schema/__init__.py @@ -0,0 +1,6 @@ +""" +Schema imports +""" + +from .orm import * +from .statement import Statement diff --git a/src/python/txtai/database/schema/orm.py b/src/python/txtai/database/schema/orm.py new file mode 100644 index 0000000..0815abf --- /dev/null +++ b/src/python/txtai/database/schema/orm.py @@ -0,0 +1,99 @@ +""" +ORM Module +""" + +# Conditional import +try: + from sqlalchemy import Column, DateTime, Float, JSON, Integer, LargeBinary, String, Text + from sqlalchemy.orm import DeclarativeBase + + ORM = True +except ImportError: + ORM = False + + +# Standard database schema using object relational mapping (ORM). +if ORM: + + def idcolumn(): + """ + Creates an id column. This method creates an unbounded text field for platforms that support it. + + Returns: + id column definition + """ + + return String(512).with_variant(Text(), "sqlite", "postgresql") + + class Base(DeclarativeBase): + """ + Base mapping. + """ + + class Batch(Base): + """ + Batch temporary table mapping. + """ + + __tablename__ = "batch" + __table_args__ = {"prefixes": ["TEMPORARY"]} + + autoid = Column(Integer, primary_key=True, autoincrement=True) + indexid = Column(Integer) + id = Column(idcolumn()) + batch = Column(Integer) + + class Score(Base): + """ + Scores temporary table mapping. + """ + + __tablename__ = "scores" + __table_args__ = {"prefixes": ["TEMPORARY"]} + + indexid = Column(Integer, primary_key=True, autoincrement=False) + score = Column(Float) + + class Document(Base): + """ + Documents table mapping. + """ + + __tablename__ = "documents" + + id = Column(idcolumn(), primary_key=True) + data = Column(JSON) + tags = Column(Text) + entry = Column(DateTime(timezone=True)) + + class Object(Base): + """ + Objects table mapping. + """ + + __tablename__ = "objects" + + id = Column(idcolumn(), primary_key=True) + object = Column(LargeBinary) + tags = Column(Text) + entry = Column(DateTime(timezone=True)) + + class SectionBase(Base): + """ + Generic sections table mapping. Allows multiple section table names for reindexing. + """ + + __abstract__ = True + + indexid = Column(Integer, primary_key=True, autoincrement=False) + id = Column(idcolumn(), index=True) + text = Column(Text) + tags = Column(Text) + entry = Column(DateTime(timezone=True)) + + class Section(SectionBase): + """ + Section table mapping. + """ + + __tablename__ = "sections" diff --git a/src/python/txtai/database/schema/statement.py b/src/python/txtai/database/schema/statement.py new file mode 100644 index 0000000..3652766 --- /dev/null +++ b/src/python/txtai/database/schema/statement.py @@ -0,0 +1,101 @@ +""" +Statement module +""" + + +class Statement: + """ + Standard database schema SQL statements. + """ + + # Temporary table for working with id batches + CREATE_BATCH = """ + CREATE TEMP TABLE IF NOT EXISTS batch ( + indexid INTEGER, + id TEXT, + batch INTEGER + ) + """ + + DELETE_BATCH = "DELETE FROM batch" + INSERT_BATCH_INDEXID = "INSERT INTO batch (indexid, batch) VALUES (?, ?)" + INSERT_BATCH_ID = "INSERT INTO batch (id, batch) VALUES (?, ?)" + + # Temporary table for joining similarity scores + CREATE_SCORES = """ + CREATE TEMP TABLE IF NOT EXISTS scores ( + indexid INTEGER PRIMARY KEY, + score REAL + ) + """ + + DELETE_SCORES = "DELETE FROM scores" + INSERT_SCORE = "INSERT INTO scores VALUES (?, ?)" + + # Documents - stores full content + CREATE_DOCUMENTS = """ + CREATE TABLE IF NOT EXISTS documents ( + id TEXT PRIMARY KEY, + data JSON, + tags TEXT, + entry DATETIME + ) + """ + + INSERT_DOCUMENT = "INSERT OR REPLACE INTO documents VALUES (?, ?, ?, ?)" + DELETE_DOCUMENTS = "DELETE FROM documents WHERE id IN (SELECT id FROM batch)" + + # Objects - stores binary content + CREATE_OBJECTS = """ + CREATE TABLE IF NOT EXISTS objects ( + id TEXT PRIMARY KEY, + object BLOB, + tags TEXT, + entry DATETIME + ) + """ + + INSERT_OBJECT = "INSERT OR REPLACE INTO objects VALUES (?, ?, ?, ?)" + DELETE_OBJECTS = "DELETE FROM objects WHERE id IN (SELECT id FROM batch)" + + # Sections - stores section text + CREATE_SECTIONS = """ + CREATE TABLE IF NOT EXISTS %s ( + indexid INTEGER PRIMARY KEY, + id TEXT, + text TEXT, + tags TEXT, + entry DATETIME + ) + """ + + CREATE_SECTIONS_INDEX = "CREATE INDEX section_id ON sections(id)" + INSERT_SECTION = "INSERT INTO sections VALUES (?, ?, ?, ?, ?)" + DELETE_SECTIONS = "DELETE FROM sections WHERE id IN (SELECT id FROM batch)" + COPY_SECTIONS = ( + "INSERT INTO %s SELECT (select count(*) - 1 from sections s1 where s.indexid >= s1.indexid) indexid, " + + "s.id, %s AS text, s.tags, s.entry FROM sections s LEFT JOIN documents d ON s.id = d.id ORDER BY indexid" + ) + STREAM_SECTIONS = ( + "SELECT s.id, s.text, data, object, s.tags FROM %s s " + + "LEFT JOIN documents d ON s.id = d.id " + + "LEFT JOIN objects o ON s.id = o.id ORDER BY indexid" + ) + DROP_SECTIONS = "DROP TABLE sections" + RENAME_SECTIONS = "ALTER TABLE %s RENAME TO sections" + + # Queries + SELECT_IDS = "SELECT indexid, id FROM sections WHERE id in (SELECT id FROM batch)" + COUNT_IDS = "SELECT count(indexid) FROM sections" + + # Partial sql clauses + TABLE_CLAUSE = ( + "SELECT %s FROM sections s " + + "%s documents d ON s.id = d.id " + + "LEFT JOIN objects o ON s.id = o.id " + + "LEFT JOIN scores sc ON s.indexid = sc.indexid" + ) + IDS_CLAUSE = "s.indexid in (SELECT indexid from batch WHERE batch=%s)" + + # Expression indexes + CREATE_EXPRESSION_INDEX = "CREATE INDEX IF NOT EXISTS %s ON %s(%s)" diff --git a/src/python/txtai/database/sql/__init__.py b/src/python/txtai/database/sql/__init__.py new file mode 100644 index 0000000..ba0ad0a --- /dev/null +++ b/src/python/txtai/database/sql/__init__.py @@ -0,0 +1,9 @@ +""" +SQL imports +""" + +from .aggregate import Aggregate +from .base import SQL +from .error import SQLError +from .expression import Expression +from .token import Token diff --git a/src/python/txtai/database/sql/aggregate.py b/src/python/txtai/database/sql/aggregate.py new file mode 100644 index 0000000..5e73db4 --- /dev/null +++ b/src/python/txtai/database/sql/aggregate.py @@ -0,0 +1,178 @@ +""" +Aggregate module +""" + +import itertools +import operator + +from .base import SQL + + +class Aggregate(SQL): + """ + Aggregates partial results from queries. Partial results come from queries when working with sharded indexes. + """ + + def __init__(self, database=None): + # Always return token lists as this method requires them + super().__init__(database, True) + + def __call__(self, query, results): + """ + Analyzes query results, combines aggregate function results and applies ordering. + + Args: + query: input query + results: query results + + Returns: + aggregated query results + """ + + # Parse query + query = super().__call__(query) + + # Check if this is a SQL query + if "select" in query: + # Get list of unique and aggregate columns. If no aggregate columns or order by found, skip + columns = list(results[0].keys()) + aggcolumns = self.aggcolumns(columns) + if aggcolumns or query["orderby"]: + # Merge aggregate columns + if aggcolumns: + results = self.aggregate(query, results, columns, aggcolumns) + + # Sort results and return + return self.orderby(query, results) if query["orderby"] else self.defaultsort(results) + + # Otherwise, run default sort + return self.defaultsort(results) + + def aggcolumns(self, columns): + """ + Filters columns for columns that have an aggregate function call. + + Args: + columns: list of columns + + Returns: + list of aggregate columns + """ + + aggregates = {} + for column in columns: + column = column.lower() + if column.startswith(("count(", "sum(", "total(")): + aggregates[column] = sum + elif column.startswith("max("): + aggregates[column] = max + elif column.startswith("min("): + aggregates[column] = min + elif column.startswith("avg("): + aggregates[column] = lambda x: sum(x) / len(x) + + return aggregates + + def aggregate(self, query, results, columns, aggcolumns): + """ + Merges aggregate columns in results. + + Args: + query: input query + results: query results + columns: list of select columns + aggcolumns: list of aggregate columns + + Returns: + results with aggregates merged + """ + + # Group data, if necessary + if query["groupby"]: + results = self.groupby(query, results, columns) + else: + results = [results] + + # Compute column values + rows = [] + for result in results: + # Calculate/copy column values + row = {} + for column in columns: + if column in aggcolumns: + # Calculate aggregate value + function = aggcolumns[column] + row[column] = function([r[column] for r in result]) + else: + # Non aggregate column value repeat, use first value + row[column] = result[0][column] + + # Add row using original query columns + rows.append(row) + + return rows + + def groupby(self, query, results, columns): + """ + Groups results using query group by clause. + + Args: + query: input query + results: query results + columns: list of select columns + + Returns: + results grouped using group by clause + """ + + groupby = [column for column in columns if column.lower() in query["groupby"]] + if groupby: + results = sorted(results, key=operator.itemgetter(*groupby)) + return [list(value) for _, value in itertools.groupby(results, operator.itemgetter(*groupby))] + + return [results] + + def orderby(self, query, results): + """ + Applies an order by clause to results. + + Args: + query: input query + results: query results + + Returns: + results ordered using order by clause + """ + + # Sort in reverse order + for clause in query["orderby"][::-1]: + # Order by columns must be selected + reverse = False + if clause.lower().endswith(" asc"): + clause = clause.rsplit(" ")[0] + elif clause.lower().endswith(" desc"): + clause = clause.rsplit(" ")[0] + reverse = True + + # Order by columns must be in select clause + if clause in query["select"]: + results = sorted(results, key=operator.itemgetter(clause), reverse=reverse) + + return results + + def defaultsort(self, results): + """ + Default sorting algorithm for results. Sorts by score descending, if available. + + Args: + results: query results + + Returns: + results ordered by score descending + """ + + # Sort standard query using score column, if present + if results and "score" in results[0]: + return sorted(results, key=lambda x: x["score"], reverse=True) + + return results diff --git a/src/python/txtai/database/sql/base.py b/src/python/txtai/database/sql/base.py new file mode 100644 index 0000000..5c36f08 --- /dev/null +++ b/src/python/txtai/database/sql/base.py @@ -0,0 +1,183 @@ +""" +SQL module +""" + +from io import StringIO +from shlex import shlex + +from .expression import Expression + + +class SQL: + """ + Translates txtai SQL statements into database native queries. + """ + + # List of clauses to parse + CLAUSES = ["select", "from", "where", "group", "having", "order", "limit", "offset"] + + def __init__(self, database=None, tolist=False): + """ + Creates a new SQL query parser. + + Args: + database: database instance that provides resolver callback, if any + tolist: outputs expression lists if True, expression text otherwise, defaults to False + """ + + # Expression parser + self.expression = Expression(database.resolve if database else self.defaultresolve, tolist) + + def __call__(self, query): + """ + Parses an input SQL query and normalizes column names in the query clauses. This method will also embed + similarity search placeholders into the query. + + Args: + query: input query + + Returns: + {clause name: clause text} + """ + + clauses = None + if self.issql(query): + # Ignore multiple statements + query = query.split(";")[0] + + # Tokenize query + tokens, positions = self.tokenize(query) + + # Alias clauses and similar queries + aliases, similar = {}, [] + + # Parse SQL clauses + clauses = { + "select": self.parse(tokens, positions, "select", alias=True, aliases=aliases), + "where": self.parse(tokens, positions, "where", aliases=aliases, similar=similar), + "groupby": self.parse(tokens, positions, "group", offset=2, aliases=aliases), + "having": self.parse(tokens, positions, "having", aliases=aliases), + "orderby": self.parse(tokens, positions, "order", offset=2, aliases=aliases), + "limit": self.parse(tokens, positions, "limit", aliases=aliases), + "offset": self.parse(tokens, positions, "offset", aliases=aliases), + } + + # Add parsed similar queries, if any + if similar: + clauses["similar"] = similar + + # Return clauses, default to full query if this is not a SQL query + return clauses if clauses else {"similar": [[query]]} + + # pylint: disable=W0613 + def defaultresolve(self, name, alias=None): + """ + Default resolve function. Performs no processing, only returns name. + + Args: + name: query column name + alias: alias name, defaults to None + + Returns: + name + """ + + return name + + def issql(self, query): + """ + Detects if this is a SQL query. + + Args: + query: input query + + Returns: + True if this is a valid SQL query, False otherwise + """ + + if isinstance(query, str): + # Reduce query to a lower-cased single line stripped of leading/trailing whitespace + query = query.lower().strip(";").replace("\n", " ").replace("\t", " ").strip() + + # Detect if this is a valid txtai SQL statement + return query.startswith("select ") and (" from txtai " in query or query.endswith(" from txtai")) + + return False + + def snippet(self, text): + """ + Parses a partial SQL snippet. + + Args: + text: SQL snippet + + Returns: + parsed snippet + """ + + tokens, _ = self.tokenize(text) + return self.expression(tokens) + + def tokenize(self, query): + """ + Tokenizes SQL query into tokens. + + Args: + query: input query + + Returns: + (tokenized query, token positions) + """ + + # Build a simple SQL lexer + # - Punctuation chars are parsed as standalone tokens which helps identify operators + # - Add additional wordchars to prevent splitting on those values + # - Disable comments + tokens = shlex(StringIO(query), punctuation_chars="=!<>+-*/%|") + tokens.wordchars += ":@#" + tokens.commenters = "" + tokens = list(tokens) + + # Identify sql clause token positions + positions = {} + + # Get position of clause keywords. For multi-term clauses, validate next token matches as well + for x, token in enumerate(tokens): + t = token.lower() + if t not in positions and t in SQL.CLAUSES and (t not in ["group", "order"] or (x + 1 < len(tokens) and tokens[x + 1].lower() == "by")): + positions[t] = x + + return (tokens, positions) + + def parse(self, tokens, positions, name, offset=1, alias=False, aliases=None, similar=None): + """ + Runs query column name to database column name mappings for clauses. This method will also + parse SIMILAR() function calls, extract parameters for those calls and leave a placeholder + to be filled in with similarity results. + + Args: + tokens: query tokens + positions: token positions - used to locate the start of sql clauses + name: current query clause name + offset: how many tokens are in the clause name + alias: True if terms in the clause should be aliased (i.e. column as alias) + aliases: dict of generated aliases, if present these tokens should NOT be resolved + similar: list where parsed similar clauses should be stored + + Returns: + formatted clause + """ + + clause = None + if name in positions: + # Find the next clause token + end = [positions.get(x, len(tokens)) for x in SQL.CLAUSES[SQL.CLAUSES.index(name) + 1 :]] + end = min(end) if end else len(tokens) + + # Start after current clause token and end before next clause or end of string + clause = tokens[positions[name] + offset : end] + + # Parse and resolve parameters + clause = self.expression(clause, alias, aliases, similar) + + return clause diff --git a/src/python/txtai/database/sql/error.py b/src/python/txtai/database/sql/error.py new file mode 100644 index 0000000..5745bea --- /dev/null +++ b/src/python/txtai/database/sql/error.py @@ -0,0 +1,9 @@ +""" +Error module +""" + + +class SQLError(Exception): + """ + Raised for errors generated by user SQL queries + """ diff --git a/src/python/txtai/database/sql/expression.py b/src/python/txtai/database/sql/expression.py new file mode 100644 index 0000000..85b3304 --- /dev/null +++ b/src/python/txtai/database/sql/expression.py @@ -0,0 +1,422 @@ +""" +Expression module +""" + +from .error import SQLError +from .token import Token + + +class Expression: + """ + Parses expression statements and runs a set of substitution/formatting rules. + """ + + def __init__(self, resolver, tolist): + """ + Creates a new expression parser. + + Args: + resolver: function to call to resolve query column names with database column names + tolist: outputs expression lists if True, text if False + """ + + self.resolver = resolver + self.tolist = tolist + + def __call__(self, tokens, alias=False, aliases=None, similar=None): + """ + Parses and formats a list of tokens as follows: + - Replaces query column names with database column names + - Adds similar query placeholders and extracts similar function parameters + - Rewrites expression and returns + + Args: + tokens: input expression + alias: if True, column aliases should be generated and added to aliases dict + aliases: dict of generated aliases, if present these tokens should NOT be resolved + similar: list of similar queries, if present new similar queries are appended to this list + + Returns: + rewritten clause + """ + + # Processes token expressions and applies a set of transformation rules + transformed = self.process(list(tokens), alias, aliases, similar) + + # Re-write alias expression and return + if alias and not self.tolist: + return self.buildalias(transformed, tokens, aliases) + + # Re-write input expression and return + return self.buildlist(transformed) if self.tolist is True else self.buildtext(transformed) + + def process(self, tokens, alias, aliases, similar): + """ + Replaces query column names with database column names, adds similar query placeholders and + extracts similar function parameters. + + Args: + tokens: input expression + alias: if True, column aliases should be generated and added to aliases dict + aliases: dict of generated aliases, if present these tokens should NOT be resolved + similar: list of similar queries, if present new similar queries are appended to this list + + Returns: + transformed tokens + """ + + # Create clause index and token iterator. Iterator skips distinct tokens. + index, iterator = 0, ((x, token) for x, token in enumerate(tokens) if not Token.isdistinct(token)) + for x, token in iterator: + # Check if separator, increment clause index + if Token.isseparator(token): + index += 1 + + # Check if token is a square bracket + elif Token.isbracket(token): + # Resolve bracket expression + self.bracket(iterator, tokens, x) + + # Check if token is a similar function + elif Token.issimilar(tokens, x, similar): + # Resolve similar expression + self.similar(iterator, tokens, x, similar) + + # Check if token is a function + elif Token.isfunction(tokens, x): + # Resolve function expression + self.function(iterator, tokens, token, aliases, similar) + + # Check for alias expression + elif Token.isalias(tokens, x, alias): + # Process alias expression + self.alias(iterator, tokens, x, aliases, index) + + # Check for attribute expression + elif Token.isattribute(tokens, x): + # Resolve attribute expression + self.attribute(tokens, x, aliases) + + # Check for compound expression + elif Token.iscompound(tokens, x): + # Resolve compound expression + self.compound(iterator, tokens, x, aliases, similar) + + # Remove replaced tokens + return [token for token in tokens if token] + + def buildtext(self, tokens): + """ + Builds a new expression from tokens. This method applies a set of rules to generate whitespace between tokens. + + Args: + tokens: input expression + + Returns: + expression text + """ + + # Rebuild expression + text = "" + for token in tokens: + # Write token with whitespace rules applied + text += Token.wrapspace(text, token) + + # Remove any leading/trailing whitespace and return + return text.strip() + + def buildlist(self, tokens): + """ + Builds a new expression from tokens. This method returns a list of expression components. These components can be joined together + on commas to form a text expression. + + Args: + tokens: input expression + + Returns: + expression list + """ + + parts, current, parens, brackets = [], [], 0, 0 + + for token in tokens: + # Create new part + if token == "," and not parens and not brackets: + parts.append(self.buildtext(current)) + current = [] + else: + # Accumulate tokens + if token == "(": + parens += 1 + elif token == ")": + parens -= 1 + elif token == "[": + brackets += 1 + elif token == "]": + brackets -= 1 + elif Token.issortorder(token): + token = f" {token}" + current.append(token) + + # Add last part + if current: + parts.append(self.buildtext(current)) + + return parts + + def buildalias(self, transformed, tokens, aliases): + """ + Builds new alias text expression from transformed and input tokens. + + Args: + transformed: transformed tokens + tokens: original input tokens + aliases: dict of column aliases + + Returns: + alias text expression + """ + + # Convert tokens to expressions + transformed = self.buildlist(transformed) + tokens = self.buildlist(tokens) + + expression = [] + for x, token in enumerate(transformed): + if x not in aliases.values(): + alias = tokens[x] + + # Strip leading/trailing brackets from alias name that doesn't have operators + if not any(Token.isoperator(t) for t in alias) and alias[0] in ("[", "(") and alias[-1] in ("]", ")"): + alias = alias[1:-1] + + # Strip leading distinct keyword + values = alias.split() + if len(values) > 0 and Token.isdistinct(values[0]): + alias = " ".join(values[1:]) + + # Resolve alias + token = self.resolver(token, alias) + + expression.append(token) + + # Build alias text expression + return ", ".join(expression) + + def bracket(self, iterator, tokens, x): + """ + Consumes a [bracket] expression. + + Args: + iterator: tokens iterator + tokens: input tokens + x: current position + """ + + # Function parameters + params = [] + + # Clear token from stream + token = tokens[x] + tokens[x] = None + + # Bracket counter (current token is an open bracket) + brackets = 1 + + # Read until token is a end bracket + while token and (token != "]" or brackets > 0): + x, token = self.readtoken(iterator) + + # Increase/decrease bracket counter + if token == "[": + brackets += 1 + elif token == "]": + brackets -= 1 + + # Accumulate tokens + if token != "]" or brackets > 0: + params.append(token) + + # Clear token from stream + tokens[x] = None + + # Set last token to resolved bracket expression + tokens[x] = self.resolve(self.buildtext(params).replace("'", "''"), None) + + def similar(self, iterator, tokens, x, similar): + """ + Substitutes a similar() function call with a placeholder that can later be used to add + embeddings query results as a filter. + + Args: + iterator: tokens iterator + tokens: input tokens + x: current position + similar: list where similar function call parameters are stored + """ + + # Function parameters + params = [] + + # Clear token from stream + token = tokens[x] + tokens[x] = None + + # Read until token is a closing paren + while token and token != ")": + x, token = self.readtoken(iterator) + if token and token not in ["(", ",", ")"]: + # Strip quotes and accumulate tokens + params.append(token.replace("'", "").replace('"', "")) + + # Clear token from stream + tokens[x] = None + + # Add placeholder for embedding similarity results + tokens[x] = f"{Token.SIMILAR_TOKEN}{len(similar)}" + + # Save parameters + similar.append(params) + + def function(self, iterator, tokens, token, aliases, similar): + """ + Resolves column names within the function's parameters. + + Args: + iterator: tokens iterator + tokens: input tokens + token: current token + aliases: dict of generated aliases, if present these tokens should NOT be resolved + similar: list where similar function call parameters are stored + """ + + # Consume function parameters + while token and token != ")": + x, token = self.readtoken(iterator) + + # Check if token is a square bracket + if Token.isbracket(token): + # Resolve bracket expression + self.bracket(iterator, tokens, x) + + # Check if token is a similar function + elif Token.issimilar(tokens, x, similar): + # Resolve similar expression + self.similar(iterator, tokens, x, similar) + + # Check if token is a function + elif Token.isfunction(tokens, x): + # Resolve function parameters that are functions + self.function(iterator, tokens, token, aliases, similar) + + # Check for attribute expression + elif Token.isattribute(tokens, x): + # Resolve attributes + self.attribute(tokens, x, aliases) + + # Check for compound expression + elif Token.iscompound(tokens, x): + # Resolve compound expressions + self.compound(iterator, tokens, x, aliases, similar) + + def alias(self, iterator, tokens, x, aliases, index): + """ + Reads an alias clause and stores it in aliases. + + Args: + iterator: tokens iterator + tokens: input tokens + x: current position + aliases: dict where aliases are stored - stores {alias: clause index} + index: clause index, used to match aliases with columns + """ + + token = tokens[x] + + # If this is an alias token, get next token + if token in Token.ALIAS: + x, token = next(iterator, (None, None)) + + # Consume tokens until end of stream or a separator is found. Evaluate next token to prevent consuming here. + while x + 1 < len(tokens) and not Token.isseparator(Token.get(tokens, x + 1)): + x, token = next(iterator, (None, None)) + + # Add normalized alias and clause index + aliases[Token.normalize(token)] = index + + def attribute(self, tokens, x, aliases): + """ + Resolves an attribute column name. + + Args: + tokens: input tokens + x: current token position + aliases: dict of generated aliases, if present these tokens should NOT be resolved + """ + + # Resolve attribute expression + tokens[x] = self.resolve(tokens[x], aliases) + + def compound(self, iterator, tokens, x, aliases, similar): + """ + Resolves column names in a compound expression (left side right side). + + Args: + iterator: tokens iterator + tokens: input tokens + x: current token position + aliases: dict of generated aliases, if present these tokens should NOT be resolved + similar: list where similar function call parameters are stored + """ + + # Resolve left side (left side already had function processing applied through standard loop) + if Token.iscolumn(tokens[x - 1]): + tokens[x - 1] = self.resolve(tokens[x - 1], aliases) + + # Consume operator(s), handle both single and compound operators, i.e. column NOT LIKE 1 + token = tokens[x] + while token and Token.isoperator(token): + x, token = next(iterator, (None, None)) + + # Resolve right side + if token and Token.iscolumn(token): + # Need to process functions since it hasn't went through the standard loop yet + if Token.isfunction(tokens, x): + self.function(iterator, tokens, token, aliases, similar) + else: + tokens[x] = self.resolve(token, aliases) + + def resolve(self, token, aliases): + """ + Resolves this token's value if it is not an alias or a bind parameter. + + Args: + token: token to resolve + aliases: dict of generated aliases, if present these tokens should NOT be resolved + + Returns: + resolved token value + """ + + # Check for alias or bind parameter + if (aliases and Token.normalize(token) in aliases) or (token.startswith(":")): + return token + + return self.resolver(token) + + def readtoken(self, iterator): + """ + Reads the next token from the token iterator. Raises a SQLError if the iterator has no more tokens. + + Args: + iterator: token iterator + + Returns: + (x, token) for the next token + """ + + x, token = next(iterator, (None, None)) + if x is None: + raise SQLError("Unterminated clause in SQL expression") + + return x, token diff --git a/src/python/txtai/database/sql/token.py b/src/python/txtai/database/sql/token.py new file mode 100644 index 0000000..f073430 --- /dev/null +++ b/src/python/txtai/database/sql/token.py @@ -0,0 +1,342 @@ +""" +Token module +""" + + +class Token: + """ + Methods to check for token type. + """ + + # Similar token replacement + SIMILAR_TOKEN = "__SIMILAR__" + + # Default distinct token + DISTINCT = ["distinct"] + + # Default alias token + ALIAS = ["as"] + + # Default list of comparison operators + OPERATORS = ["=", "!=", "<>", ">", ">=", "<", "<=", "+", "-", "*", "/", "%", "||", "not", "between", "like", "is", "null"] + + # Default list of logic separators + LOGIC_SEPARATORS = ["and", "or"] + + # Default list of sort order operators + SORT_ORDER = ["asc", "desc"] + + @staticmethod + def get(tokens, x): + """ + Gets token at position x. This method will validate position is valid within tokens. + + Args: + tokens: input tokens + x: position to retrieve + + Returns: + tokens[x] if x is a valid position, None otherwise + """ + + if 0 <= x < len(tokens): + return tokens[x] + + return None + + @staticmethod + def isalias(tokens, x, alias): + """ + Checks if tokens[x] is an alias keyword. + + Args: + tokens: input tokens + x: current position + alias: if column alias processing is enabled + + Returns: + True if tokens[x] is an alias token, False otherwise + """ + + prior = Token.get(tokens, x - 1) + token = tokens[x] + + # True if prior token is not a separator, grouping token or distinct token and current token is either a column token or quoted token + return ( + alias + and x > 0 + and not Token.isseparator(prior) + and not Token.isgroupstart(prior) + and not Token.isdistinct(prior) + and (Token.iscolumn(token) or Token.isquoted(token)) + ) + + @staticmethod + def isattribute(tokens, x): + """ + Checks if tokens[x] is an attribute. + + Args: + tokens: input tokens + x: current position + + Returns: + True if tokens[x] is an attribute, False otherwise + """ + + # True if token is a column and next token is not an operator + return Token.iscolumn(tokens[x]) and not Token.isoperator(Token.get(tokens, x + 1)) + + @staticmethod + def isbracket(token): + """ + Checks if token is an open bracket. + + Args: + token: token to test + + Returns: + True if token is an open bracket, False otherwise + """ + + # Token is a bracket + return token == "[" + + @staticmethod + def iscolumn(token): + """ + Checks if token is a column name. + + Args: + token: token to test + + Returns: + True if this token is a column name token, False otherwise + """ + + # Columns are not operators, logic separators, literals or sort order tokens + return ( + token + and not Token.isoperator(token) + and not Token.islogicseparator(token) + and not Token.isliteral(token) + and not Token.issortorder(token) + ) + + @staticmethod + def iscompound(tokens, x): + """ + Checks if tokens[x] is a compound expression. + + Args: + tokens: input tokens + x: current position + + Returns: + True if tokens[x] is a compound expression, False otherwise + """ + + # Compound expression is defined as: + return Token.isoperator(tokens[x]) and (Token.iscolumn(Token.get(tokens, x - 1)) or Token.iscolumn(Token.get(tokens, x + 1))) + + @staticmethod + def isdistinct(token): + """ + Checks if token is the distinct keyword. + + Args: + token: token to test + + Returns: + True if this token is a distinct keyword, False otherwise + """ + + # Token is the distinct keyword + return token and token.lower() in Token.DISTINCT + + @staticmethod + def isfunction(tokens, x): + """ + Checks if tokens[x] is a function. + + Args: + tokens: input tokens + x: current position + + Returns: + True if tokens[x] is a function, False otherwise + """ + + # True if a column token is followed by an open paren + return Token.iscolumn(tokens[x]) and Token.get(tokens, x + 1) == "(" + + @staticmethod + def isgroupstart(token): + """ + Checks if token is a group start token. + + Args: + token: token to test + + Returns: + True if token is a group start token, False otherwise + """ + + # Token is a paren + return token == "(" + + @staticmethod + def isliteral(token): + """ + Checks if token is a literal. + + Args: + token: token to test + + Returns: + True if this token is a literal, False otherwise + """ + + # Literals are wrapped in quotes, parens, wildcards or numeric. + return token and (token.startswith(("'", '"', ",", "(", ")", "*")) or token.replace(".", "", 1).isdigit()) + + @staticmethod + def islogicseparator(token): + """ + Checks if token is a logic separator token. + + Args: + token: token to test + + Returns: + True if this token is a logic separator, False otherwise + """ + + # Token is a logic separator + return token and token.lower() in Token.LOGIC_SEPARATORS + + @staticmethod + def isoperator(token): + """ + Checks if token is an operator token. + + Args: + token: token to test + + Returns: + True if this token is an operator, False otherwise + """ + + # Token is an operator + return token and token.lower() in Token.OPERATORS + + @staticmethod + def isquoted(token): + """ + Checks if token is quoted. + + Args: + token: token to test + + Returns: + True if this token is quoted, False otherwise + """ + + # Token is quoted + return token.startswith(("'", '"')) and token.endswith(("'", '"')) + + @staticmethod + def isseparator(token): + """ + Checks if token is a separator token. + + Args: + token to test + + Returns: + True if this token is a separator, False otherwise + """ + + # Token is a comma + return token == "," + + @staticmethod + def issimilar(tokens, x, similar): + """ + Checks if tokens[x] is a similar() function. + + Args: + tokens: input tokens + x: current position + similar: list where similar function call parameters are stored, can be None in which case similar processing is skipped + + Returns: + True if tokens[x] is a similar clause + """ + + # True if a "similar" token is followed by an open paren + return similar is not None and tokens[x].lower() == "similar" and Token.get(tokens, x + 1) == "(" + + @staticmethod + def issortorder(token): + """ + Checks if token is a sort order token. + + Args: + token: token to test + + Returns: + True if this token is a sort order operator, False otherwise + """ + + # Token is a sort order operator + return token and token.lower() in Token.SORT_ORDER + + @staticmethod + def normalize(token): + """ + Applies a normalization algorithm to the input token as follows: + - Strip single and double quotes + - Make lowercase + + Args: + token: input token + + Returns: + normalized token + """ + + # Lowercase, replace and return + return token.lower().replace("'", "").replace('"', "") + + @staticmethod + def wrapspace(text, token): + """ + Applies whitespace wrapping rules to token. + + Args: + text: current text buffer + token: token to add + + Returns: + token with whitespace rules applied + """ + + # Wildcards have no whitespace. Need special case since * is also multiply which does have whitespace. + if token in ["*"] and (not text or text.endswith((" ", "("))): + return token + + # Operator whitespace + if Token.isoperator(token) or Token.islogicseparator(token) or token.lower() in ["in"]: + return f" {token} " if not text.endswith(" ") else f"{token} " + + # Comma whitespace + if Token.isseparator(token): + return f"{token} " + + # No whitespace if any of the following is True + if not text or text.endswith((" ", "(", "[")) or token in ["(", "[", ")", "]"] or token.startswith("."): + return token + + # Default is to add leading whitespace + return f" {token}" diff --git a/src/python/txtai/database/sqlite.py b/src/python/txtai/database/sqlite.py new file mode 100644 index 0000000..b2082d2 --- /dev/null +++ b/src/python/txtai/database/sqlite.py @@ -0,0 +1,58 @@ +""" +SQLite module +""" + +import os +import sqlite3 + +from .embedded import Embedded + + +class SQLite(Embedded): + """ + Database instance backed by SQLite. + """ + + def connect(self, path=""): + # Create connection + connection = sqlite3.connect(path, check_same_thread=False) + + # Enable WAL mode, if necessary + if self.setting("wal"): + connection.execute("PRAGMA journal_mode=WAL") + + return connection + + def getcursor(self): + return self.connection.cursor() + + def rows(self): + return self.cursor + + def addfunctions(self): + if self.connection and self.functions: + # Enable callback tracebacks to show user-defined function errors + sqlite3.enable_callback_tracebacks(True) + + # Create database functions + for name, argcount, fn, deterministic in self.functions: + self.connection.create_function(name, argcount, fn, deterministic=deterministic if deterministic is not None else False) + + def copy(self, path): + # Delete existing file, if necessary + if os.path.exists(path): + os.remove(path) + + # Create database. Thread locking must be handled externally. + connection = self.connect(path) + + if self.connection.in_transaction: + # The backup call will hang if there are uncommitted changes, need to copy over + # with iterdump (which is much slower) + for sql in self.connection.iterdump(): + connection.execute(sql) + else: + # Database is up to date, can do a more efficient copy with SQLite C API + self.connection.backup(connection) + + return connection diff --git a/src/python/txtai/embeddings/__init__.py b/src/python/txtai/embeddings/__init__.py new file mode 100644 index 0000000..a5ed3d6 --- /dev/null +++ b/src/python/txtai/embeddings/__init__.py @@ -0,0 +1,7 @@ +""" +Embeddings imports +""" + +from .base import Embeddings +from .index import * +from .search import * diff --git a/src/python/txtai/embeddings/base.py b/src/python/txtai/embeddings/base.py new file mode 100644 index 0000000..fe7634d --- /dev/null +++ b/src/python/txtai/embeddings/base.py @@ -0,0 +1,1107 @@ +""" +Embeddings module +""" + +import json +import os +import tempfile + +from ..ann import ANNFactory +from ..archive import ArchiveFactory +from ..cloud import CloudFactory +from ..database import DatabaseFactory +from ..graph import GraphFactory +from ..scoring import ScoringFactory +from ..vectors import VectorsFactory + +from .index import Action, Configuration, Functions, Indexes, IndexIds, Reducer, Stream, Transform +from .search import Explain, Ids, Query, Search, Terms + + +# pylint: disable=C0302,R0904 +class Embeddings: + """ + Embeddings databases are the engine that delivers semantic search. Data is transformed into embeddings vectors where similar concepts + will produce similar vectors. Indexes both large and small are built with these vectors. The indexes are used to find results + that have the same meaning, not necessarily the same keywords. + """ + + # pylint: disable=W0231 + def __init__(self, config=None, models=None, **kwargs): + """ + Creates a new embeddings index. Embeddings indexes are thread-safe for read operations but writes must be synchronized. + + Args: + config: embeddings configuration + models: models cache, used for model sharing between embeddings + kwargs: additional configuration as keyword args + """ + + # Index configuration + self.config = None + + # Dimensionality reduction - word vectors only + self.reducer = None + + # Dense vector model - transforms data into similarity vectors + self.model = None + + # Approximate nearest neighbor index + self.ann = None + + # Index ids when content is disabled + self.ids = None + + # Document database + self.database = None + + # Resolvable functions + self.functions = None + + # Graph network + self.graph = None + + # Sparse vectors + self.scoring = None + + # Query model + self.query = None + + # Index archive + self.archive = None + + # Subindexes for this embeddings instance + self.indexes = None + + # Models cache + self.models = models + + # Merge configuration into single dictionary + config = {**config, **kwargs} if config and kwargs else kwargs if kwargs else config + + # Set initial configuration + self.configure(config) + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + def score(self, documents): + """ + Builds a term weighting scoring index. Only used by word vectors models. + + Args: + documents: iterable of (id, data, tags), (id, data) or data + """ + + # Build scoring index for word vectors term weighting + if self.isweighted(): + self.scoring.index(Stream(self)(documents)) + + def index(self, documents, reindex=False, checkpoint=None): + """ + Builds an embeddings index. This method overwrites an existing index. + + Args: + documents: iterable of (id, data, tags), (id, data) or data + reindex: if this is a reindex operation in which case database creation is skipped, defaults to False + checkpoint: optional checkpoint directory, enables indexing restart + """ + + # Initialize index + self.initindex(reindex) + + # Create transform and stream + transform = Transform(self, Action.REINDEX if reindex else Action.INDEX, checkpoint) + stream = Stream(self, Action.REINDEX if reindex else Action.INDEX) + + with tempfile.NamedTemporaryFile(mode="wb", suffix=".npy") as buffer: + # Load documents into database and transform to vectors + ids, dimensions, embeddings = transform(stream(documents), buffer) + if embeddings is not None: + # Build LSA model (if enabled). Remove principal components from embeddings. + if self.config.get("pca"): + self.reducer = Reducer(embeddings, self.config["pca"]) + self.reducer(embeddings) + + # Save index dimensions + self.config["dimensions"] = dimensions + + # Create approximate nearest neighbor index + self.ann = self.createann() + + # Add embeddings to the index + self.ann.index(embeddings) + + # Save indexids-ids mapping for indexes with no database, except when this is a reindex + if ids and not reindex and not self.database: + self.ids = self.createids(ids) + + # Index scoring, if necessary + # This must occur before graph index in order to be available to the graph + if self.issparse(): + self.scoring.index() + + # Index subindexes, if necessary + if self.indexes: + self.indexes.index() + + # Index graph, if necessary + if self.graph: + self.graph.index(Search(self, indexonly=True), Ids(self), self.batchsimilarity) + + def upsert(self, documents, checkpoint=None): + """ + Runs an embeddings upsert operation. If the index exists, new data is + appended to the index, existing data is updated. If the index doesn't exist, + this method runs a standard index operation. + + Args: + documents: iterable of (id, data, tags), (id, data) or data + checkpoint: optional checkpoint directory, enables indexing restart + """ + + # Run standard insert if index doesn't exist or it has no records + if not self.count(): + self.index(documents, checkpoint=checkpoint) + return + + # Create transform and stream + transform = Transform(self, Action.UPSERT, checkpoint=checkpoint) + stream = Stream(self, Action.UPSERT) + + with tempfile.NamedTemporaryFile(mode="wb", suffix=".npy") as buffer: + # Load documents into database and transform to vectors + ids, _, embeddings = transform(stream(documents), buffer) + if embeddings is not None: + # Remove principal components from embeddings, if necessary + if self.reducer: + self.reducer(embeddings) + + # Append embeddings to the index + self.ann.append(embeddings) + + # Save indexids-ids mapping for indexes with no database + if ids and not self.database: + self.ids = self.createids(self.ids + ids) + + # Scoring upsert, if necessary + # This must occur before graph upsert in order to be available to the graph + if self.issparse(): + self.scoring.upsert() + + # Subindexes upsert, if necessary + if self.indexes: + self.indexes.upsert() + + # Graph upsert, if necessary + if self.graph: + self.graph.upsert(Search(self, indexonly=True), Ids(self), self.batchsimilarity) + + def delete(self, ids): + """ + Deletes from an embeddings index. Returns list of ids deleted. + + Args: + ids: list of ids to delete + + Returns: + list of ids deleted + """ + + # List of internal indices for each candidate id to delete + indices = [] + + # List of deleted ids + deletes = [] + + if self.database: + # Retrieve indexid-id mappings from database + ids = self.database.ids(ids) + + # Parse out indices and ids to delete + indices = [i for i, _ in ids] + deletes = sorted(set(uid for _, uid in ids)) + + # Delete ids from database + self.database.delete(deletes) + elif self.ann or self.scoring: + # Find existing ids + for uid in ids: + indices.extend([index for index, value in enumerate(self.ids) if uid == value]) + + # Clear embeddings ids + for index in indices: + deletes.append(self.ids[index]) + self.ids[index] = None + + # Delete indices for all indexes and data stores + if indices: + # Delete ids from ann + if self.isdense(): + self.ann.delete(indices) + + # Delete ids from scoring + if self.issparse(): + self.scoring.delete(indices) + + # Delete ids from subindexes + if self.indexes: + self.indexes.delete(indices) + + # Delete ids from graph + if self.graph: + self.graph.delete(indices) + + return deletes + + def reindex(self, config=None, function=None, **kwargs): + """ + Recreates embeddings index using config. This method only works if document content storage is enabled. + + Args: + config: new config + function: optional function to prepare content for indexing + kwargs: additional configuration as keyword args + """ + + if self.database: + # Merge configuration into single dictionary + config = {**config, **kwargs} if config and kwargs else config if config else kwargs + + # Keep content and objects parameters to ensure database is preserved + config["content"] = self.config["content"] + if "objects" in self.config: + config["objects"] = self.config["objects"] + + # Reset configuration + self.configure(config) + + # Reset function references + if self.functions: + self.functions.reset() + + # Reindex + if function: + self.index(function(self.database.reindex(self.config)), True) + else: + self.index(self.database.reindex(self.config), True) + + def transform(self, document, category=None, index=None): + """ + Transforms document into an embeddings vector. + + Args: + documents: iterable of (id, data, tags), (id, data) or data + category: category for instruction-based embeddings + index: index name, if applicable + + Returns: + embeddings vector + """ + + return self.batchtransform([document], category, index)[0] + + def batchtransform(self, documents, category=None, index=None): + """ + Transforms documents into embeddings vectors. + + Args: + documents: iterable of (id, data, tags), (id, data) or data + category: category for instruction-based embeddings + index: index name, if applicable + + Returns: + embeddings vectors + """ + + # Initialize default parameters, if necessary + self.defaults() + + # Get vector model + model = self.findmodel(index) + + # Convert documents into embeddings + embeddings = model.batchtransform(Stream(self)(documents), category) + + # Reduce the dimensionality of the embeddings. Scale the embeddings using this + # model to reduce the noise of common but less relevant terms. + if self.reducer: + self.reducer(embeddings) + + return embeddings + + def count(self): + """ + Total number of elements in this embeddings index. + + Returns: + number of elements in this embeddings index + """ + + if self.ann: + return self.ann.count() + if self.scoring: + return self.scoring.count() + if self.database: + return self.database.count() + if self.ids: + return len([uid for uid in self.ids if uid is not None]) + + # Default to 0 when no suitable method found + return 0 + + def search(self, query, limit=None, weights=None, index=None, parameters=None, graph=False): + """ + Finds documents most similar to the input query. This method runs an index search, index + database search + or a graph search, depending on the embeddings configuration and query. + + Args: + query: input query + limit: maximum results + weights: hybrid score weights, if applicable + index: index name, if applicable + parameters: dict of named parameters to bind to placeholders + graph: return graph results if True + + Returns: + list of (id, score) for index search + list of dict for an index + database search + graph when graph is set to True + """ + + results = self.batchsearch([query], limit, weights, index, [parameters], graph) + return results[0] if results else results + + def batchsearch(self, queries, limit=None, weights=None, index=None, parameters=None, graph=False): + """ + Finds documents most similar to the input query. This method runs an index search, index + database search + or a graph search, depending on the embeddings configuration and query. + + Args: + queries: input queries + limit: maximum results + weights: hybrid score weights, if applicable + index: index name, if applicable + parameters: list of dicts of named parameters to bind to placeholders + graph: return graph results if True + + Returns: + list of (id, score) per query for index search + list of dict per query for an index + database search + list of graph per query when graph is set to True + """ + + # Determine if graphs should be returned + graph = graph if self.graph else False + + # Execute search + results = Search(self, indexids=graph)(queries, limit, weights, index, parameters) + + # Create subgraphs using results, if necessary + return [self.graph.filter(x) if isinstance(x, list) else x for x in results] if graph else results + + def similarity(self, query, data): + """ + Computes the similarity between query and list of data. Returns a list of + (id, score) sorted by highest score, where id is the index in data. + + Args: + query: input query + data: list of data + + Returns: + list of (id, score) + """ + + return self.batchsimilarity([query], data)[0] + + def batchsimilarity(self, queries, data): + """ + Computes the similarity between list of queries and list of data. Returns a list + of (id, score) sorted by highest score per query, where id is the index in data. + + Args: + queries: input queries + data: list of data + + Returns: + list of (id, score) per query + """ + + # Convert queries to embedding vectors + queries = self.batchtransform(((None, query, None) for query in queries), "query") + data = self.batchtransform(((None, row, None) for row in data), "data") + + # Get vector model + model = self.findmodel() + + # Dot product on normalized vectors is equal to cosine similarity + scores = model.dot(queries, data) + + # Add index and sort desc based on score + return [sorted(enumerate(score), key=lambda x: x[1], reverse=True) for score in scores] + + def explain(self, query, texts=None, limit=None): + """ + Explains the importance of each input token in text for a query. This method requires either content to be enabled + or texts to be provided. + + Args: + query: input query + texts: optional list of (text|list of tokens), otherwise runs search query + limit: optional limit if texts is None + + Returns: + list of dict per input text where a higher token scores represents higher importance relative to the query + """ + + results = self.batchexplain([query], texts, limit) + return results[0] if results else results + + def batchexplain(self, queries, texts=None, limit=None): + """ + Explains the importance of each input token in text for a list of queries. This method requires either content to be enabled + or texts to be provided. + + Args: + queries: input queries + texts: optional list of (text|list of tokens), otherwise runs search queries + limit: optional limit if texts is None + + Returns: + list of dict per input text per query where a higher token scores represents higher importance relative to the query + """ + + return Explain(self)(queries, texts, limit) + + def terms(self, query): + """ + Extracts keyword terms from a query. + + Args: + query: input query + + Returns: + query reduced down to keyword terms + """ + + return self.batchterms([query])[0] + + def batchterms(self, queries): + """ + Extracts keyword terms from a list of queries. + + Args: + queries: list of queries + + Returns: + list of queries reduced down to keyword term strings + """ + + return Terms(self)(queries) + + def exists(self, path=None, cloud=None, **kwargs): + """ + Checks if an index exists at path. + + Args: + path: input path + cloud: cloud storage configuration + kwargs: additional configuration as keyword args + + Returns: + True if index exists, False otherwise + """ + + # Check if this exists in a cloud instance + cloud = self.createcloud(cloud=cloud, **kwargs) + if cloud: + return cloud.exists(path) + + # Check if this is an archive file and exists + path, apath = self.checkarchive(path) + if apath: + return os.path.exists(apath) + + # Return true if path has a config.json or config file with an offset set + return path and (os.path.exists(f"{path}/config.json") or os.path.exists(f"{path}/config")) and "offset" in Configuration().load(path) + + def load(self, path=None, cloud=None, config=None, **kwargs): + """ + Loads an existing index from path. + + Args: + path: input path + cloud: cloud storage configuration + config: configuration overrides + kwargs: additional configuration as keyword args + + Returns: + Embeddings + """ + + # Load from cloud, if configured + cloud = self.createcloud(cloud=cloud, **kwargs) + if cloud: + path = cloud.load(path) + + # Check if this is an archive file and extract + path, apath = self.checkarchive(path) + if apath: + self.archive.load(apath) + + # Load index configuration + self.config = Configuration().load(path) + + # Apply config overrides + self.config = {**self.config, **config} if config else self.config + + # Approximate nearest neighbor index - stores dense vectors + self.ann = self.createann() + if self.ann: + self.ann.load(f"{path}/embeddings") + + # Dimensionality reduction model - word vectors only + if self.config.get("pca"): + self.reducer = Reducer() + self.reducer.load(f"{path}/lsa") + + # Index ids when content is disabled + self.ids = self.createids() + if self.ids: + self.ids.load(f"{path}/ids") + + # Document database - stores document content + self.database = self.createdatabase() + if self.database: + self.database.load(f"{path}/documents") + + # Sparse vectors - stores term sparse arrays + self.scoring = self.createscoring() + if self.scoring: + self.scoring.load(f"{path}/scoring") + + # Subindexes + self.indexes = self.createindexes() + if self.indexes: + self.indexes.load(f"{path}/indexes") + + # Graph network - stores relationships + self.graph = self.creategraph() + if self.graph: + self.graph.load(f"{path}/graph") + + # Dense vectors - transforms data to embeddings vectors + self.model = self.loadvectors() + + # Query model + self.query = self.loadquery() + + return self + + def save(self, path, cloud=None, **kwargs): + """ + Saves an index in a directory at path unless path ends with tar.gz, tar.bz2, tar.xz or zip. + In those cases, the index is stored as a compressed file. + + Args: + path: output path + cloud: cloud storage configuration + kwargs: additional configuration as keyword args + """ + + if self.config: + # Check if this is an archive file + path, apath = self.checkarchive(path) + + # Create output directory, if necessary + os.makedirs(path, exist_ok=True) + + # Save index configuration + Configuration().save(self.config, path) + + # Save approximate nearest neighbor index + if self.ann: + self.ann.save(f"{path}/embeddings") + + # Save dimensionality reduction model (word vectors only) + if self.reducer: + self.reducer.save(f"{path}/lsa") + + # Save index ids + if self.ids: + self.ids.save(f"{path}/ids") + + # Save document database + if self.database: + self.database.save(f"{path}/documents") + + # Save scoring index + if self.scoring: + self.scoring.save(f"{path}/scoring") + + # Save subindexes + if self.indexes: + self.indexes.save(f"{path}/indexes") + + # Save graph + if self.graph: + self.graph.save(f"{path}/graph") + + # If this is an archive, save it + if apath: + self.archive.save(apath) + + # Save to cloud, if configured + cloud = self.createcloud(cloud=cloud, **kwargs) + if cloud: + cloud.save(apath if apath else path) + + def close(self): + """ + Closes this embeddings index and frees all resources. + """ + + self.config, self.archive = None, None + self.reducer, self.query = None, None + self.ids = None + + # Close ANN + if self.ann: + self.ann.close() + self.ann = None + + # Close database + if self.database: + self.database.close() + self.database, self.functions = None, None + + # Close scoring + if self.scoring: + self.scoring.close() + self.scoring = None + + # Close graph + if self.graph: + self.graph.close() + self.graph = None + + # Close indexes + if self.indexes: + self.indexes.close() + self.indexes = None + + # Close vectors model + if self.model: + self.model.close() + self.model = None + + self.models = None + + def info(self): + """ + Prints the current embeddings index configuration. + """ + + if self.config: + # Print configuration + print(json.dumps(self.config, sort_keys=True, default=str, indent=2)) + + def issparse(self): + """ + Checks if this instance has an associated sparse keyword or sparse vectors scoring index. + + Returns: + True if scoring has an associated sparse keyword/vector index, False otherwise + """ + + return self.scoring and self.scoring.issparse() + + def isdense(self): + """ + Checks if this instance has an associated ANN instance. + + Returns: + True if this instance has an associated ANN, False otherwise + """ + + return self.ann is not None + + def isweighted(self): + """ + Checks if this instance has an associated scoring instance with term weighting enabled. + + Returns: + True if term weighting is enabled, False otherwise + """ + + return self.scoring and self.scoring.isweighted() + + def findmodel(self, index=None): + """ + Finds the primary vector model used by this instance. + + Returns: + Vectors + """ + + return ( + self.indexes.findmodel(index) + if index and self.indexes + else ( + self.model + if self.model + else self.scoring.findmodel() if self.scoring and self.scoring.findmodel() else self.indexes.findmodel() if self.indexes else None + ) + ) + + def configure(self, config): + """ + Sets the configuration for this embeddings index and loads config-driven models. + + Args: + config: embeddings configuration + """ + + # Configuration + self.config = config + + # Dimensionality reduction model + self.reducer = None + + # Create scoring instance for word vectors term weighting + scoring = self.config.get("scoring") if self.config else None + self.scoring = self.createscoring() if scoring and not self.hassparse() else None + + # Dense vectors - transforms data to embeddings vectors + self.model = self.loadvectors() if self.config else None + + # Query model + self.query = self.loadquery() if self.config else None + + def initindex(self, reindex): + """ + Initialize new index. + + Args: + reindex: if this is a reindex operation in which case database creation is skipped, defaults to False + """ + + # Initialize default parameters, if necessary + self.defaults() + + # Initialize index ids, only created when content is disabled + self.ids = None + + # Create document database, if necessary + if not reindex: + self.database = self.createdatabase() + + # Reset archive since this is a new index + self.archive = None + + # Close existing ANN, if necessary + if self.ann: + self.ann.close() + + # Initialize ANN, will be created after index transformations complete + self.ann = None + + # Create scoring only if the scoring config is for a sparse index + if self.hassparse(): + self.scoring = self.createscoring() + + # Create subindexes, if necessary + self.indexes = self.createindexes() + + # Create graph, if necessary + self.graph = self.creategraph() + + def defaults(self): + """ + Apply default parameters to current configuration. + + Returns: + configuration with default parameters set + """ + + self.config = self.config if self.config else {} + + # Expand sparse index shortcuts + if not self.config.get("scoring") and any(self.config.get(key) for key in ["keyword", "sparse", "hybrid"]): + self.defaultsparse() + + # Expand graph shortcuts + if self.config.get("graph") is True: + self.config["graph"] = {} + + # Check if default model should be loaded + if not self.model and (self.defaultallowed() or self.config.get("dense")): + self.config["path"] = "sentence-transformers/all-MiniLM-L6-v2" + + # Load dense vectors model + self.model = self.loadvectors() + + def defaultsparse(self): + """ + Logic to derive default sparse index configuration. + """ + + # Check for keyword and hybrid parameters + method = None + for x in ["keyword", "hybrid"]: + value = self.config.get(x) + if value: + method = value if isinstance(value, str) else "bm25" + + # Enable dense index when hybrid enabled + if x == "hybrid": + self.config["dense"] = True + + sparse = self.config.get("sparse", {}) + if sparse or method == "sparse": + # Sparse vector configuration + sparse = {"path": self.config.get("sparse")} if isinstance(sparse, str) else {} if isinstance(sparse, bool) else sparse + sparse["path"] = sparse.get("path", "opensearch-project/opensearch-neural-sparse-encoding-doc-v2-mini") + + # Merge in sparse parameters + self.config["scoring"] = {**{"method": "sparse"}, **sparse} + + elif method: + # Sparse keyword configuration + self.config["scoring"] = {"method": method, "terms": True, "normalize": True} + + def defaultallowed(self): + """ + Tests if this embeddings instance can use a default model if not otherwise provided. + + Returns: + True if a default model is allowed, False otherwise + """ + + params = [("keyword", False), ("sparse", False), ("defaults", True)] + return all(self.config.get(key, default) == default for key, default in params) + + def loadvectors(self): + """ + Loads a vector model set in config. + + Returns: + vector model + """ + + # Create model cache if subindexes are enabled + if "indexes" in self.config and self.models is None: + self.models = {} + + # Support path via dense parameter + dense = self.config.get("dense") + if not self.config.get("path") and dense and isinstance(dense, str): + self.config["path"] = dense + + # Load vector model + return VectorsFactory.create(self.config, self.scoring, self.models) + + def loadquery(self): + """ + Loads a query model set in config. + + Returns: + query model + """ + + if "query" in self.config: + return Query(**self.config["query"]) + + return None + + def checkarchive(self, path): + """ + Checks if path is an archive file. + + Args: + path: path to check + + Returns: + (working directory, current path) if this is an archive, original path otherwise + """ + + # Create archive instance, if necessary + self.archive = ArchiveFactory.create() + + # Check if path is an archive file + if self.archive.isarchive(path): + # Return temporary archive working directory and original path + return self.archive.path(), path + + return path, None + + def createcloud(self, **cloud): + """ + Creates a cloud instance from config. + + Args: + cloud: cloud configuration + """ + + # Merge keyword args and keys under the cloud parameter + config = cloud + if "cloud" in config and config["cloud"]: + config.update(config.pop("cloud")) + + # Create cloud instance from config and return + return CloudFactory.create(config) if config else None + + def createann(self): + """ + Creates an ANN from config. + + Returns: + new ANN, if enabled in config + """ + + # Free existing resources + if self.ann: + self.ann.close() + + return ANNFactory.create(self.config) if self.config.get("path") or self.defaultallowed() else None + + def createdatabase(self): + """ + Creates a database from config. This method will also close any existing database connection. + + Returns: + new database, if enabled in config + """ + + # Free existing resources + if self.database: + self.database.close() + + config = self.config.copy() + + # Create references to callable functions + self.functions = Functions(self) if "functions" in config else None + if self.functions: + config["functions"] = self.functions(config) + + # Create database from config and return + return DatabaseFactory.create(config) + + def creategraph(self): + """ + Creates a graph from config. + + Returns: + new graph, if enabled in config + """ + + # Free existing resources + if self.graph: + self.graph.close() + + if "graph" in self.config: + # Get or create graph configuration + config = self.config["graph"] if "graph" in self.config else {} + + # Create configuration with custom columns, if necessary + config = self.columns(config) + return GraphFactory.create(config) + + return None + + def createids(self, ids=None): + """ + Creates indexids when content is disabled. + + Args: + ids: optional ids to add + + Returns: + new indexids, if content disabled + """ + + # Load index ids when content is disabled + return IndexIds(self, ids) if not self.config.get("content") else None + + def createindexes(self): + """ + Creates subindexes from config. + + Returns: + list of subindexes + """ + + # Free existing resources + if self.indexes: + self.indexes.close() + + # Load subindexes + if "indexes" in self.config: + indexes = {} + for index, config in self.config["indexes"].items(): + # Create index with shared model cache + indexes[index] = Embeddings(config, models=self.models) + + # Wrap as Indexes object + return Indexes(self, indexes) + + return None + + def createscoring(self): + """ + Creates a scoring from config. + + Returns: + new scoring, if enabled in config + """ + + # Free existing resources + if self.scoring: + self.scoring.close() + + if "scoring" in self.config: + # Expand scoring to a dictionary, if necessary + config = self.config["scoring"] + config = config if isinstance(config, dict) else {"method": config} + + # Create configuration with custom columns, if necessary + config = self.columns(config) + return ScoringFactory.create(config, self.models) + + return None + + def hassparse(self): + """ + Checks is this embeddings database has an associated sparse index. + + Returns: + True if this embeddings has an associated scoring index + """ + + # Create scoring only if scoring is a sparse keyword/vector index + return ScoringFactory.issparse(self.config.get("scoring")) + + def columns(self, config): + """ + Adds custom text/object column information if it's provided. + + Args: + config: input configuration + + Returns: + config with column information added + """ + + # Add text/object columns if custom + if "columns" in self.config: + # Work on copy of configuration + config = config.copy() + + # Copy columns to config + config["columns"] = self.config["columns"] + + return config diff --git a/src/python/txtai/embeddings/index/__init__.py b/src/python/txtai/embeddings/index/__init__.py new file mode 100644 index 0000000..5b3a1f1 --- /dev/null +++ b/src/python/txtai/embeddings/index/__init__.py @@ -0,0 +1,14 @@ +""" +Index imports +""" + +from .action import Action +from .autoid import AutoId +from .configuration import Configuration +from .documents import Documents +from .functions import Functions +from .indexes import Indexes +from .indexids import IndexIds +from .reducer import Reducer +from .stream import Stream +from .transform import Transform diff --git a/src/python/txtai/embeddings/index/action.py b/src/python/txtai/embeddings/index/action.py new file mode 100644 index 0000000..75ffff1 --- /dev/null +++ b/src/python/txtai/embeddings/index/action.py @@ -0,0 +1,15 @@ +""" +Action module +""" + +from enum import Enum + + +class Action(Enum): + """ + Index action types + """ + + INDEX = 1 + UPSERT = 2 + REINDEX = 3 diff --git a/src/python/txtai/embeddings/index/autoid.py b/src/python/txtai/embeddings/index/autoid.py new file mode 100644 index 0000000..8ecb372 --- /dev/null +++ b/src/python/txtai/embeddings/index/autoid.py @@ -0,0 +1,92 @@ +""" +AutoId module +""" + +import inspect +import uuid + + +class AutoId: + """ + Generates unique ids. + """ + + def __init__(self, method=None): + """ + Creates a unique id generator. + + Args: + method: generation method - supports int sequence (default) or UUID function + """ + + # Initialize variables + self.method, self.function, self.value = None, None, None + + # Set id generation method + if not method or isinstance(method, int): + # Incrementing sequence (default) + self.method = self.sequence + self.value = method if method else 0 + else: + # UUID generation function + self.method = self.uuid + self.function = getattr(uuid, method) + + # Check if signature takes a namespace argument (deterministic) + args = inspect.getfullargspec(self.function).args if self.function else [] + self.deterministic = "namespace" in args + + def __call__(self, data=None): + """ + Generates a unique id. + + Args: + data: optional data to use for deterministic algorithms (i.e. uuid3, uuid5) + + Returns: + unique id + """ + + return self.method(data) + + # pylint: disable=W0613 + def sequence(self, data): + """ + Gets and increments sequence. + + Args: + data: not used + + Returns: + current sequence value + """ + + # Get and increment sequence + value = self.value + self.value += 1 + + return value + + def uuid(self, data): + """ + Generates a UUID and return as a string. + + Args: + data: used with determistic algorithms (uuid3, uuid5) + + Returns: + UUID string + """ + + uid = self.function(uuid.NAMESPACE_DNS, str(data)) if self.deterministic else self.function() + return str(uid) + + def current(self): + """ + Get the current sequence value. Only applicable for sequence ids, will be None for UUID methods. + + Returns: + current sequence value + """ + + return self.value diff --git a/src/python/txtai/embeddings/index/configuration.py b/src/python/txtai/embeddings/index/configuration.py new file mode 100644 index 0000000..bf8f317 --- /dev/null +++ b/src/python/txtai/embeddings/index/configuration.py @@ -0,0 +1,71 @@ +""" +Configuration module +""" + +import json +import os + +from ...serialize import SerializeFactory + + +class Configuration: + """ + Loads and saves index configuration. + """ + + def load(self, path): + """ + Loads index configuration. This method supports both config.json and config pickle files. + + Args: + path: path to directory + + Returns: + dict + """ + + # Configuration + config = None + + # Determine if config is json or pickle + jsonconfig = os.path.exists(f"{path}/config.json") + + # Set config file name + name = "config.json" if jsonconfig else "config" + + # Load configuration + with open(f"{path}/{name}", "r" if jsonconfig else "rb", encoding="utf-8" if jsonconfig else None) as handle: + # Load JSON, also backwards-compatible with pickle configuration + config = json.load(handle) if jsonconfig else SerializeFactory.create("pickle").loadstream(handle) + + # Add format parameter + config["format"] = "json" if jsonconfig else "pickle" + + return config + + def save(self, config, path): + """ + Saves index configuration. This method defaults to JSON and falls back to pickle. + + Args: + config: configuration to save + path: path to directory + + Returns: + dict + """ + + # Default to JSON config + jsonconfig = config.get("format", "json") == "json" + + # Set config file name + name = "config.json" if jsonconfig else "config" + + # Write configuration + with open(f"{path}/{name}", "w" if jsonconfig else "wb", encoding="utf-8" if jsonconfig else None) as handle: + if jsonconfig: + # Write config as JSON + json.dump(config, handle, default=str, indent=2) + else: + # Backwards compatible method to save pickle configuration + SerializeFactory.create("pickle").savestream(config, handle) diff --git a/src/python/txtai/embeddings/index/documents.py b/src/python/txtai/embeddings/index/documents.py new file mode 100644 index 0000000..31a48a7 --- /dev/null +++ b/src/python/txtai/embeddings/index/documents.py @@ -0,0 +1,86 @@ +""" +Documents module +""" + +import os +import tempfile + +from ...serialize import SerializeFactory + + +class Documents: + """ + Streams documents to temporary storage. Allows queuing large volumes of content for later indexing. + """ + + def __init__(self): + """ + Creates a new documents stream. + """ + + self.documents = None + self.batch = 0 + self.size = 0 + + # Pickle serialization - local temporary data + self.serializer = SerializeFactory.create("pickle", allowpickle=True) + + def __len__(self): + """ + Returns total number of queued documents. + """ + + return self.size + + def __iter__(self): + """ + Streams all queued documents. + """ + + # Close streaming file + self.documents.close() + + # Open stream file + with open(self.documents.name, "rb") as queue: + # Read each batch + for _ in range(self.batch): + documents = self.serializer.loadstream(queue) + + # Yield each document + yield from documents + + def add(self, documents): + """ + Adds a batch of documents for indexing. + + Args: + documents: list of (id, data, tag) tuples + + Returns: + documents + """ + + # Create documents file if not already open + # pylint: disable=R1732 + if not self.documents: + self.documents = tempfile.NamedTemporaryFile(mode="wb", suffix=".docs", delete=False) + + # Add batch + self.serializer.savestream(documents, self.documents) + self.batch += 1 + self.size += len(documents) + + return documents + + def close(self): + """ + Closes and resets this instance. New sets of documents can be added with additional calls to add. + """ + + # Cleanup stream file + os.remove(self.documents.name) + + # Reset document parameters + self.documents = None + self.batch = 0 + self.size = 0 diff --git a/src/python/txtai/embeddings/index/functions.py b/src/python/txtai/embeddings/index/functions.py new file mode 100644 index 0000000..28d1a91 --- /dev/null +++ b/src/python/txtai/embeddings/index/functions.py @@ -0,0 +1,155 @@ +""" +Functions module +""" + +from types import FunctionType, MethodType + + +class Functions: + """ + Resolves function configuration to function references. + """ + + def __init__(self, embeddings): + """ + Creates a new function resolver. + + Args: + embeddings: embeddings instance + """ + + self.embeddings = embeddings + + # Handle to all reference objects + self.references = None + + def __call__(self, config): + """ + Resolves a list of functions to function references. + + Args: + config: configuration + + Returns: + list of function references + """ + + # Initialize stored references array + self.references = [] + + # Resolve callable functions + functions = [] + for fn in config["functions"]: + if isinstance(fn, dict): + fn = fn.copy() + fn["function"] = self.function(fn["function"]) + else: + fn = self.function(fn) + functions.append(fn) + + return functions + + def reset(self): + """ + Clears all resolved references. + """ + + if self.references: + for reference in self.references: + reference.reset() + + def function(self, function): + """ + Resolves function configuration. If function is a string, it's split on '.' and each part + is separately resolved to an object, attribute or function. Each part is resolved upon the + first invocation of the function. Otherwise, the input is returned. + + Args: + function: function configuration + + Returns: + function reference + """ + + if isinstance(function, str): + parts = function.split(".") + + if hasattr(self.embeddings, parts[0]): + m = Reference(self.embeddings, parts[0]) + self.references.append(m) + else: + module = ".".join(parts[:-1]) + m = __import__(module) + + for comp in parts[1:]: + m = Reference(m, comp) + self.references.append(m) + + return m + + return function + + +class Reference: + """ + Stores a reference to an object attribute. This attribute is resolved by invoking the __call__ method. + This allows for functions to be independent of the initialization order of an embeddings instance. + """ + + def __init__(self, obj, attribute): + """ + Create a new reference. + + Args: + obj: object handle + attribute: attribute name + """ + + # Object handle and attribute + self.obj = obj + self.attribute = attribute + + # Keep a handle to the original inputs + self.inputs = (obj, attribute) + + # True if the object and attribute have been resolved + self.resolved = False + + # True if the attribute is a function + self.function = None + + def __call__(self, *args): + """ + Resolves an object attribute reference. If the attribute is a function, the function is executed. + Otherwise, the object attribute value is returned. + + Args: + args: list of function arguments to the object attribute, when attribute is a function + + Returns: + object attribute function result or object attribute value + """ + + # Resolve nested function arguments, if necessary + if not self.resolved: + self.obj = self.obj() if isinstance(self.obj, Reference) else self.obj + self.attribute = self.attribute() if isinstance(self.attribute, Reference) else self.attribute + self.resolved = True + + # Lookup attribute + attribute = getattr(self.obj, self.attribute) + + # Determine if attribute is a function + if self.function is None: + self.function = isinstance(attribute, (FunctionType, MethodType)) or (hasattr(attribute, "__call__") and args) + + # If attribute is a function, execute and return, otherwise return attribute + return attribute(*args) if self.function else attribute + + def reset(self): + """ + Clears resolved references. + """ + + self.obj, self.attribute = self.inputs + self.resolved = False diff --git a/src/python/txtai/embeddings/index/indexes.py b/src/python/txtai/embeddings/index/indexes.py new file mode 100644 index 0000000..721e945 --- /dev/null +++ b/src/python/txtai/embeddings/index/indexes.py @@ -0,0 +1,199 @@ +""" +Indexes module +""" + +import os + +from .documents import Documents + + +class Indexes: + """ + Manages a collection of subindexes for an embeddings instance. + """ + + def __init__(self, embeddings, indexes): + """ + Creates a new indexes instance. + + Args: + embeddings: embeddings instance + indexes: dict of subindexes to add + """ + + self.embeddings = embeddings + self.indexes = indexes + + self.documents = None + self.checkpoint = None + + # Transform columns + columns = embeddings.config.get("columns", {}) + self.text = columns.get("text", "text") + self.object = columns.get("object", "object") + + # Check if top-level indexing is enabled for this embeddings instance + self.indexing = embeddings.model or embeddings.scoring + + def __contains__(self, name): + """ + Returns True if name is in this instance, False otherwise. + + Returns: + True if name is in this instance, False otherwise + """ + + return name in self.indexes + + def __getitem__(self, name): + """ + Looks up an index by name. + + Args: + name: index name + + Returns: + index + """ + + return self.indexes[name] + + def __getattr__(self, name): + """ + Looks up an index by attribute name. + + Args: + name: index name + + Returns: + index + """ + + try: + return self.indexes[name] + except Exception as e: + raise AttributeError(e) from e + + def default(self): + """ + Gets the default/first index. + + Returns: + default index + """ + + return list(self.indexes.keys())[0] + + def findmodel(self, index=None): + """ + Finds a vector model. If index is empty, the first vector model is returned. + + Args: + index: index name to match + + Returns: + Vectors + """ + + # Find vector model + matches = [self.indexes[index].findmodel()] if index else [index.findmodel() for index in self.indexes.values() if index.findmodel()] + return matches[0] if matches else None + + def insert(self, documents, index=None, checkpoint=None): + """ + Inserts a batch of documents into each subindex. + + Args: + documents: list of (id, data, tags) + index: indexid offset + checkpoint: optional checkpoint directory, enables indexing restart + """ + + if not self.documents: + self.documents = Documents() + self.checkpoint = checkpoint + + # Create batch containing documents added to parent index + batch = [] + for _, document, _ in documents: + # Add to documents collection if text or object field is set + parent = document + if isinstance(parent, dict): + parent = parent.get(self.text, document.get(self.object)) + + # Add if field is available or top-level indexing is disabled + if parent is not None or not self.indexing: + batch.append((index, document, None)) + index += 1 + + # Add filtered documents batch + self.documents.add(batch) + + def delete(self, ids): + """ + Deletes ids from each subindex. + + Args: + ids: list of ids to delete + """ + + for index in self.indexes.values(): + index.delete(ids) + + def index(self): + """ + Builds each subindex. + """ + + for name, index in self.indexes.items(): + index.index(self.documents, checkpoint=f"{self.checkpoint}/{name}" if self.checkpoint else None) + + # Reset document stream + self.documents.close() + self.documents = None + self.checkpoint = None + + def upsert(self): + """ + Runs upsert for each subindex. + """ + + for index in self.indexes.values(): + index.upsert(self.documents) + + # Reset document stream + self.documents.close() + self.documents = None + + def load(self, path): + """ + Loads each subindex from path. + + Args: + path: directory path to load subindexes + """ + + for name, index in self.indexes.items(): + # Load subindex if it exists, subindexes aren't required to have data + directory = os.path.join(path, name) + if index.exists(directory): + index.load(directory) + + def save(self, path): + """ + Saves each subindex to path. + + Args: + path: directory path to save subindexes + """ + + for name, index in self.indexes.items(): + index.save(os.path.join(path, name)) + + def close(self): + """ + Close and free resources used by this instance. + """ + + for index in self.indexes.values(): + index.close() diff --git a/src/python/txtai/embeddings/index/indexids.py b/src/python/txtai/embeddings/index/indexids.py new file mode 100644 index 0000000..5935a8e --- /dev/null +++ b/src/python/txtai/embeddings/index/indexids.py @@ -0,0 +1,60 @@ +""" +IndexIds module +""" + +from ...serialize import Serializer + + +class IndexIds: + """ + Stores index ids when content is disabled. + """ + + def __init__(self, embeddings, ids=None): + """ + Creates an IndexIds instance. + + Args: + embeddings: embeddings instance + ids: ids to store + """ + + self.config = embeddings.config + self.ids = ids + + def __iter__(self): + yield from self.ids + + def __getitem__(self, index): + return self.ids[index] + + def __setitem__(self, index, value): + self.ids[index] = value + + def __add__(self, ids): + return self.ids + ids + + def load(self, path): + """ + Loads IndexIds from path. + + Args: + path: path to load + """ + + if "ids" in self.config: + # Legacy ids format + self.ids = self.config.pop("ids") + else: + # Standard ids format + self.ids = Serializer.load(path) + + def save(self, path): + """ + Saves IndexIds to path. + + Args: + path: path to save + """ + + Serializer.save(self.ids, path) diff --git a/src/python/txtai/embeddings/index/reducer.py b/src/python/txtai/embeddings/index/reducer.py new file mode 100644 index 0000000..b359913 --- /dev/null +++ b/src/python/txtai/embeddings/index/reducer.py @@ -0,0 +1,104 @@ +""" +Reducer module +""" + +from zipfile import BadZipFile + +# Conditionally import dimensionality reduction libraries as they aren't installed by default +try: + import skops.io as sio + + from sklearn.decomposition import TruncatedSVD + + REDUCER = True +except ImportError: + REDUCER = False + +from ...serialize import SerializeFactory + + +class Reducer: + """ + LSA dimensionality reduction model + """ + + def __init__(self, embeddings=None, components=None): + """ + Creates a dimensionality reduction model. + + Args: + embeddings: input embeddings matrix + components: number of model components + """ + + if not REDUCER: + raise ImportError('Dimensionality reduction is not available - install "vectors" extra to enable') + + self.model = self.build(embeddings, components) if embeddings is not None and components else None + + def __call__(self, embeddings): + """ + Applies a dimensionality reduction model to embeddings, removed the top n principal components. Operation applied + directly on array. + + Args: + embeddings: input embeddings matrix + """ + + pc = self.model.components_ + factor = embeddings.dot(pc.transpose()) + + # Apply LSA model + # Calculation is different if n_components = 1 + if pc.shape[0] == 1: + embeddings -= factor * pc + elif len(embeddings.shape) > 1: + # Apply model on a row-wise basis to limit memory usage + for x in range(embeddings.shape[0]): + embeddings[x] -= factor[x].dot(pc) + else: + # Single embedding + embeddings -= factor.dot(pc) + + def build(self, embeddings, components): + """ + Builds a LSA model. This model is used to remove the principal component within embeddings. This helps to + smooth out noisy embeddings (common words with less value). + + Args: + embeddings: input embeddings matrix + components: number of model components + + Returns: + LSA model + """ + + model = TruncatedSVD(n_components=components, random_state=0) + model.fit(embeddings) + + return model + + def load(self, path): + """ + Loads a Reducer object from path. + + Args: + path: directory path to load model + """ + + # Dimensionality reduction + try: + self.model = sio.load(path) + except (BadZipFile, KeyError): + # Backwards compatible support for pickled models + self.model = SerializeFactory.create("pickle").load(path) + + def save(self, path): + """ + Saves a Reducer object to path. + + Args: + path: directory path to save model + """ + + sio.dump(self.model, path) diff --git a/src/python/txtai/embeddings/index/stream.py b/src/python/txtai/embeddings/index/stream.py new file mode 100644 index 0000000..9cc641a --- /dev/null +++ b/src/python/txtai/embeddings/index/stream.py @@ -0,0 +1,67 @@ +""" +Stream module +""" + +from .autoid import AutoId +from .transform import Action + + +class Stream: + """ + Yields input document as standard (id, data, tags) tuples. + """ + + def __init__(self, embeddings, action=None): + """ + Create a new stream. + + Args: + embeddings: embeddings instance + action: optional index action + """ + + self.embeddings = embeddings + self.action = action + + # Alias embeddings attributes + self.config = embeddings.config + + # Get config parameters + self.offset = self.config.get("offset", 0) if action == Action.UPSERT else 0 + autoid = self.config.get("autoid", self.offset) + + # Create autoid generator, reset int sequence if this isn't an UPSERT + autoid = 0 if isinstance(autoid, int) and action != Action.UPSERT else autoid + self.autoid = AutoId(autoid) + + def __call__(self, documents): + """ + Yield (id, data, tags) tuples from a stream of documents. + + Args: + documents: input documents + """ + + # Iterate over documents and yield standard (id, data, tag) tuples + for document in documents: + if isinstance(document, dict): + # Create (id, data, tags) tuple from dictionary + document = document.get("id"), document, document.get("tags") + elif isinstance(document, tuple): + # Create (id, data, tags) tuple + document = document if len(document) >= 3 else (document[0], document[1], None) + else: + # Create (id, data, tags) tuple with empty fields + document = None, document, None + + # Set autoid if the action is set + if self.action and document[0] is None: + document = (self.autoid(document[1]), document[1], document[2]) + + # Yield (id, data, tags) tuple + yield document + + # Save autoid sequence if used + current = self.autoid.current() + if self.action and current: + self.config["autoid"] = current diff --git a/src/python/txtai/embeddings/index/transform.py b/src/python/txtai/embeddings/index/transform.py new file mode 100644 index 0000000..2b29013 --- /dev/null +++ b/src/python/txtai/embeddings/index/transform.py @@ -0,0 +1,208 @@ +""" +Transform module +""" + +from .action import Action + +# Core library imports +from ...util import Library + +np = Library().numpy() + + +class Transform: + """ + Executes a transform. Processes a stream of documents, loads batches into enabled data stores and vectorizes documents. + """ + + def __init__(self, embeddings, action, checkpoint=None): + """ + Creates a new transform. + + Args: + embeddings: embeddings instance + action: index action + checkpoint: optional checkpoint directory, enables indexing restart + """ + + self.embeddings = embeddings + self.action = action + self.checkpoint = checkpoint + + # Alias embeddings attributes + self.config = embeddings.config + self.delete = embeddings.delete + self.model = embeddings.model + self.database = embeddings.database + self.graph = embeddings.graph + self.indexes = embeddings.indexes + self.scoring = embeddings.scoring if embeddings.issparse() else None + + # Get config parameters + self.offset = embeddings.config.get("offset", 0) if action == Action.UPSERT else 0 + self.batch = embeddings.config.get("batch", 1024) + + # Scalar quantization + quantize = embeddings.config.get("quantize") + self.qbits = quantize if isinstance(quantize, int) and not isinstance(quantize, bool) else None + + # Transform columns + columns = embeddings.config.get("columns", {}) + self.text = columns.get("text", "text") + self.object = columns.get("object", "object") + + # Check if top-level indexing is enabled for this embeddings + self.indexing = embeddings.model or embeddings.scoring + + # List of deleted ids with this action + self.deletes = set() + + def __call__(self, documents, buffer): + """ + Processes an iterable collection of documents, handles any iterable including generators. + + This method loads a stream of documents into enabled data stores and vectorizes documents into an embeddings array. + + Args: + documents: iterable of (id, data, tags) + buffer: file path used for memmap buffer + + Returns: + (document ids, dimensions, embeddings) + """ + + # Return parameters + ids, dimensions, embeddings = None, None, None + + if self.model: + ids, dimensions, embeddings = self.vectors(documents, buffer) + else: + ids = self.ids(documents) + + return (ids, dimensions, embeddings) + + def vectors(self, documents, buffer): + """ + Runs a vectors transform operation when dense indexing is enabled. + + Args: + documents: iterable of (id, data, tags) + buffer: file path used for memmap buffer + + Returns: + (document ids, dimensions, embeddings) + """ + + # Determine dtype + dtype = np.uint8 if self.qbits else np.float32 + + # Transform documents into vectors + return self.model.vectors(self.stream(documents), self.batch, self.checkpoint, buffer, dtype) + + def ids(self, documents): + """ + Runs an ids transform operation when dense indexing is disabled. + + Args: + documents: iterable of (id, data, tags) + + Returns: + document ids + """ + + # Consume stream and build extract ids + ids = [] + for uid, _, _ in self.stream(documents): + ids.append(uid) + + # Save offset when dense indexing is disabled + self.config["offset"] = self.offset + + return ids + + def stream(self, documents): + """ + This method does two things: + + 1. Filter and yield data to vectorize + 2. Batch and load original documents into enabled data stores (database, graph, scoring) + + Documents are yielded for vectorization if one of the following is True: + - dict with a text or object field + - not a dict + + Otherwise, documents are only batched and inserted into data stores + + Args: + documents: iterable collection (id, data, tags) + """ + + # Batch and index offset. Index offset increments by count of documents streamed for vectorization + batch, offset = [], 0 + + # Iterate and process documents stream + for document in documents: + if isinstance(document[1], dict): + # Set text field to uid when top-level indexing is disabled and text empty + if not self.indexing and not document[1].get(self.text): + document[1][self.text] = str(document[0]) + + if self.text in document[1]: + yield (document[0], document[1][self.text], document[2]) + offset += 1 + elif self.object in document[1]: + yield (document[0], document[1][self.object], document[2]) + offset += 1 + else: + yield document + offset += 1 + + # Batch document + batch.append(document) + if len(batch) == self.batch: + self.load(batch, offset) + batch, offset = [], 0 + + # Final batch + if batch: + self.load(batch, offset) + + def load(self, batch, offset): + """ + Loads a document batch. This method deletes existing ids from an embeddings index and + loads into enabled data stores (database, graph, scoring). + + Args: + batch: list of (id, data, tags) + offset: index offset for batch + """ + + # Delete from embeddings index first (which deletes from underlying indexes and datastores) if this is an upsert + if self.action == Action.UPSERT: + # Get list of ids not yet seen and deleted + deletes = [uid for uid, _, _ in batch if uid not in self.deletes] + if deletes: + # Execute delete + self.delete(deletes) + + # Save deleted ids as a delete must only occur once per action + self.deletes.update(deletes) + + # Load batch into database except if this is a reindex + if self.database and self.action != Action.REINDEX: + self.database.insert(batch, self.offset) + + # Load batch into scoring + if self.scoring: + self.scoring.insert(batch, self.offset, self.checkpoint) + + # Load batch into subindex documents stream + if self.indexes: + self.indexes.insert(batch, self.offset, self.checkpoint) + + # Load batch into graph + if self.graph: + self.graph.insert(batch, self.offset) + + # Increment offset + self.offset += offset diff --git a/src/python/txtai/embeddings/search/__init__.py b/src/python/txtai/embeddings/search/__init__.py new file mode 100644 index 0000000..828b5ad --- /dev/null +++ b/src/python/txtai/embeddings/search/__init__.py @@ -0,0 +1,12 @@ +""" +Search imports +""" + +from .base import Search +from .errors import * +from .explain import Explain +from .hybrid import Hybrid +from .ids import Ids +from .query import Query +from .scan import Scan +from .terms import Terms diff --git a/src/python/txtai/embeddings/search/base.py b/src/python/txtai/embeddings/search/base.py new file mode 100644 index 0000000..2c62792 --- /dev/null +++ b/src/python/txtai/embeddings/search/base.py @@ -0,0 +1,327 @@ +""" +Search module +""" + +import logging + +from .errors import IndexNotFoundError +from .hybrid import Hybrid +from .scan import Scan + +# Logging configuration +logger = logging.getLogger(__name__) + + +class Search: + """ + Executes a batch search action. A search can be both index and/or database driven. + """ + + def __init__(self, embeddings, indexids=False, indexonly=False): + """ + Creates a new search action. + + Args: + embeddings: embeddings instance + indexids: searches return indexids when True, otherwise run standard search + indexonly: always runs an index search even when a database is available + """ + + self.embeddings = embeddings + self.indexids = indexids or indexonly + self.indexonly = indexonly + + # Alias embeddings attributes + self.ann = embeddings.ann + self.batchtransform = embeddings.batchtransform + self.database = embeddings.database + self.ids = embeddings.ids + self.indexes = embeddings.indexes + self.graph = embeddings.graph + self.query = embeddings.query + self.scoring = embeddings.scoring if embeddings.issparse() else None + + def __call__(self, queries, limit=None, weights=None, index=None, parameters=None): + """ + Executes a batch search for queries. This method will run either an index search or an index + database search + depending on if a database is available. + + Args: + queries: list of queries + limit: maximum results + weights: hybrid score weights + index: index name + parameters: list of dicts of named parameters to bind to placeholders + + Returns: + list of (id, score) per query for index search + list of dict per query for an index + database search + list of graph results for a graph index search + """ + + # Default input parameters + limit = limit if limit else 3 + weights = weights if weights is not None else 0.5 + + # Return empty results if there is no database and indexes + if not self.ann and not self.scoring and not self.indexes and not self.database: + return [[]] * len(queries) + + # Default index name if only subindexes set + if not index and not self.ann and not self.scoring and self.indexes: + index = self.indexes.default() + + # Graph search + if self.graph and self.graph.isquery(queries): + return self.graphsearch(queries, limit, weights, index) + + # Database search + if not self.indexonly and self.database: + return self.dbsearch(queries, limit, weights, index, parameters) + + # Default vector index query (sparse, dense or hybrid) + return self.search(queries, limit, weights, index) + + def search(self, queries, limit, weights, index): + """ + Executes an index search. When only a sparse index is enabled, this is a a keyword search. When only + a dense index is enabled, this is an ann search. When both are enabled, this is a hybrid search. + + This method will also query subindexes, if available. + + Args: + queries: list of queries + limit: maximum results + weights: hybrid score weights + index: index name + + Returns: + list of (id, score) per query + """ + + # Run against specified subindex + if index: + return self.subindex(queries, limit, weights, index) + + # Run against base indexes + hybrid = self.ann and self.scoring + dense = self.dense(queries, limit * 10 if hybrid else limit) if self.ann else None + sparse = self.sparse(queries, limit * 10 if hybrid else limit) if self.scoring else None + + # Combine scores together + if hybrid: + # Create weights array if single number passed + if isinstance(weights, (int, float)): + weights = [weights, 1 - weights] + + # Create weighted scores via hybrid fusion strategy + fusion = Hybrid(self.scoring) + return [fusion(vectors, weights, limit) for vectors in zip(dense, sparse)] + + # Raise an error if when no indexes are available + if not sparse and not dense: + raise IndexNotFoundError("No indexes available") + + # Return single query results + return dense if dense else sparse + + def subindex(self, queries, limit, weights, index): + """ + Executes a subindex search. + + Args: + queries: list of queries + limit: maximum results + weights: hybrid score weights + index: index name + + Returns: + list of (id, score) per query + """ + + # Check that index exists + if not self.indexes or index not in self.indexes: + raise IndexNotFoundError(f"Index '{index}' not found") + + # Run subindex search + results = self.indexes[index].batchsearch(queries, limit, weights) + return self.resolve(results) + + def dense(self, queries, limit): + """ + Executes an dense vector search with an approximate nearest neighbor index. + + Args: + queries: list of queries + limit: maximum results + + Returns: + list of (id, score) per query + """ + + # Convert queries to embedding vectors + embeddings = self.batchtransform((None, query, None) for query in queries) + + # Search approximate nearest neighbor index + results = self.ann.search(embeddings, limit) + + # Require scores to be greater than 0 + results = [[(i, score) for i, score in r if score > 0] for r in results] + + return self.resolve(results) + + def sparse(self, queries, limit): + """ + Executes a sparse vector search with a sparse keyword or sparse vector index. + + Args: + queries: list of queries + limit: maximum results + + Returns: + list of (id, score) per query + """ + + # Search sparse index + results = self.scoring.batchsearch(queries, limit) + + # Require scores to be greater than 0 + results = [[(i, score) for i, score in r if score > 0] for r in results] + + return self.resolve(results) + + def resolve(self, results): + """ + Resolves index ids. This is only executed when content is disabled. + + Args: + results: results + + Returns: + results with resolved ids + """ + + # Map indexids to ids if embeddings ids are available + if not self.indexids and self.ids: + return [[(self.ids[i], score) for i, score in r] for r in results] + + return results + + def dbsearch(self, queries, limit, weights, index, parameters): + """ + Executes an index + database search. + + Args: + queries: list of queries + limit: maximum results + weights: default hybrid score weights + index: default index name + parameters: list of dicts of named parameters to bind to placeholders + + Returns: + list of dict per query + """ + + # Parse queries + queries = self.parse(queries) + + # Override limit with query limit, if applicable + limit = max(limit, self.limit(queries)) + + # Bulk index scan + scan = Scan(self.search, limit, weights, index)(queries, parameters) + + # Combine index search results with database search results + results = [] + for x, query in enumerate(queries): + # Run the database query, get matching bulk searches for current query + result = self.database.search( + query, [r for y, r in scan if x == y], limit, parameters[x] if parameters and parameters[x] else None, self.indexids + ) + results.append(result) + + return results + + def parse(self, queries): + """ + Parses a list of database queries. + + Args: + queries: list of queries + + Returns: + parsed queries + """ + + # Parsed queries + parsed = [] + + for query in queries: + # Parse query + parse = self.database.parse(query) + + # Transform query if SQL not parsed and reparse + if self.query and "select" not in parse: + # Generate query + query = self.query(query) + logger.debug(query) + + # Reparse query + parse = self.database.parse(query) + + parsed.append(parse) + + return parsed + + def limit(self, queries): + """ + Parses the largest LIMIT clause from queries. + + Args: + queries: list of queries + + Returns: + largest limit number or 0 if not found + """ + + # Override limit with largest limit from database queries + qlimit = 0 + for query in queries: + # Parse out qlimit + l = query.get("limit") + if l and l.isdigit(): + l = int(l) + + qlimit = l if l and l > qlimit else qlimit + + return qlimit + + def graphsearch(self, queries, limit, weights, index): + """ + Executes an index + graph search. + + Args: + queries: list of queries + limit: maximum results + weights: default hybrid score weights + index: default index name + + Returns: + graph search results + """ + + # Parse queries + queries = [self.graph.parse(query) for query in queries] + + # Override limit with query limit, if applicable + limit = max(limit, self.limit(queries)) + + # Bulk index scan + scan = Scan(self.search, limit, weights, index)(queries, None) + + # Combine index search results with database search results + for x, query in enumerate(queries): + # Add search results to query + query["results"] = [r for y, r in scan if x == y] + + return self.graph.batchsearch(queries, limit, self.indexids) diff --git a/src/python/txtai/embeddings/search/errors.py b/src/python/txtai/embeddings/search/errors.py new file mode 100644 index 0000000..559000f --- /dev/null +++ b/src/python/txtai/embeddings/search/errors.py @@ -0,0 +1,9 @@ +""" +Errors module +""" + + +class IndexNotFoundError(Exception): + """ + Raised when an embeddings query fails to locate an index + """ diff --git a/src/python/txtai/embeddings/search/explain.py b/src/python/txtai/embeddings/search/explain.py new file mode 100644 index 0000000..917fcb3 --- /dev/null +++ b/src/python/txtai/embeddings/search/explain.py @@ -0,0 +1,123 @@ +""" +Explain module +""" + +# Core library imports +from ...util import Library + +np = Library().numpy() + + +class Explain: + """ + Explains the importance of each token in an input text element for a query. This method creates n permutations of the input text, where n + is the number of tokens in the input text. This effectively masks each token to determine its importance to the query. + """ + + def __init__(self, embeddings): + """ + Creates a new explain action. + + Args: + embeddings: embeddings instance + """ + + self.embeddings = embeddings + self.content = embeddings.config.get("content") + + # Alias embeddings attributes + self.database = embeddings.database + + def __call__(self, queries, texts, limit): + """ + Explains the importance of each input token in text for a list of queries. + + Args: + query: input queries + texts: optional list of (text|list of tokens), otherwise runs search queries + limit: optional limit if texts is None + + Returns: + list of dict per input text per query where a higher token scores represents higher importance relative to the query + """ + + # Construct texts elements per query + texts = self.texts(queries, texts, limit) + + # Explain each query-texts combination + return [self.explain(query, texts[x]) for x, query in enumerate(queries)] + + def texts(self, queries, texts, limit): + """ + Constructs lists of dict for each input query. + + Args: + queries: input queries + texts: optional list of texts + limit: optional limit if texts is None + + Returns: + lists of dict for each input query + """ + + # Calculate similarity scores per query if texts present + if texts: + results = [] + for scores in self.embeddings.batchsimilarity(queries, texts): + results.append([{"id": uid, "text": texts[uid], "score": score} for uid, score in scores]) + + return results + + # Query for results if texts is None and content is enabled + return self.embeddings.batchsearch(queries, limit) if self.content else [[]] * len(queries) + + def explain(self, query, texts): + """ + Explains the importance of each input token in text for a list of queries. + + Args: + query: input query + texts: list of text + + Returns: + list of {"id": value, "text": value, "score": value, "tokens": value} covering each input text element + """ + + # Explain results + results = [] + + # Parse out similar clauses, if necessary + if self.database: + # Parse query + query = self.database.parse(query) + + # Extract query from similar clause + query = " ".join([" ".join(clause) for clause in query["similar"]]) if "similar" in query else None + + # Return original texts if query, text or score not present + if not query or not texts or "score" not in texts[0] or "text" not in texts[0]: + return texts + + # Calculate result per input text element + for result in texts: + text = result["text"] + tokens = text if isinstance(text, list) else text.split() + + # Create permutations of input text, masking each token to determine importance + permutations = [] + for i in range(len(tokens)): + data = tokens.copy() + data.pop(i) + permutations.append([" ".join(data)]) + + # Calculate similarity for each input text permutation and get score delta as importance + scores = [(i, result["score"] - np.abs(s)) for i, s in self.embeddings.similarity(query, permutations)] + + # Append tokens to result + result["tokens"] = [(tokens[i], score) for i, score in sorted(scores, key=lambda x: x[0])] + + # Add data sorted in index order + results.append(result) + + # Sort score descending and return + return sorted(results, key=lambda x: x["score"], reverse=True) diff --git a/src/python/txtai/embeddings/search/hybrid.py b/src/python/txtai/embeddings/search/hybrid.py new file mode 100644 index 0000000..db36aef --- /dev/null +++ b/src/python/txtai/embeddings/search/hybrid.py @@ -0,0 +1,248 @@ +""" +Hybrid module +""" + +import math + + +class Hybrid: + """ + Hybrid score fusion strategies for combining dense and sparse search results. + + Selects a fusion method based on the sparse scoring configuration: + - Log-odds conjunction for Bayesian (BB25) normalized scores + - Convex combination for default normalized scores + - Reciprocal Rank Fusion (RRF) for unnormalized scores + """ + + def __init__(self, scoring): + """ + Creates a new Hybrid instance. + + Args: + scoring: sparse scoring instance + """ + + if scoring.isbayes(): + self.method = self.logodds + elif scoring.isnormalized(): + self.method = self.convex + else: + self.method = self.rrf + + def __call__(self, vectors, weights, limit): + """ + Fuses dense and sparse result vectors into a single ranked list. + + Args: + vectors: tuple of (dense_results, sparse_results) + weights: [dense_weight, sparse_weight] + limit: maximum results + + Returns: + sorted list of (uid, score) + """ + + return self.method(vectors, weights, limit) + + def logodds(self, vectors, weights, limit): + """ + Log-odds conjunction fusion for Bayesian (BB25) normalized scores. + + Args: + vectors: tuple of (dense results, sparse results) + weights: [dense weight, sparse weight] + limit: maximum results + + Returns: + sorted list (uid, score) + """ + + return LogOdds()(vectors, weights, limit) + + def convex(self, vectors, weights, limit): + """ + Convex combination fusion for default normalized scores. + + Args: + vectors: tuple of (dense_results, sparse_results) + weights: [dense_weight, sparse_weight] + limit: maximum results + + Returns: + sorted list of (uid, score) + """ + + uids = {} + for v, scores in enumerate(vectors): + for uid, score in scores if weights[v] > 0 else []: + if uid not in uids: + uids[uid] = 0.0 + uids[uid] += score * weights[v] + + return sorted(uids.items(), key=lambda x: x[1], reverse=True)[:limit] + + def rrf(self, vectors, weights, limit): + """ + Reciprocal Rank Fusion for unnormalized scores. + + Args: + vectors: tuple of (dense_results, sparse_results) + weights: [dense_weight, sparse_weight] + limit: maximum results + + Returns: + sorted list of (uid, score) + """ + + uids = {} + for v, scores in enumerate(vectors): + for r, (uid, _) in enumerate(scores if weights[v] > 0 else []): + if uid not in uids: + uids[uid] = 0.0 + uids[uid] += (1.0 / (r + 1)) * weights[v] + + return sorted(uids.items(), key=lambda x: x[1], reverse=True)[:limit] + + +class LogOdds: + """ + Log-odds conjunction fusion for Bayesian (BB25) normalized scores. + + Implements the framework from "From Bayesian Inference to Neural Computation" + (Jeong, 2026) with asymmetric dynamic calibration: + + 1. Calibrate dense cosine scores via per-query dynamic sigmoid + (beta=median, alpha_eff=1/std) to produce logits centered at 0. + 2. Convert sparse BB25 probabilities to logits. + 3. Fuse via weighted mean log-odds with confidence scaling. + """ + + # Numerical clamp for log-odds computation + EPSILON = 1e-10 + + def __call__(self, vectors, weights, limit): + """ + Log-odds fusion. + + Args: + vectors: tuple of (dense vectors, sparse vectors) + weights: [dense weights, sparse weights] + limit: maximum results + + Returns: + sorted list of (uid, score) where score is a fused logit + """ + + # Phase 1: Collect raw scores per document + uids, denseraw = self.rawscores(vectors, weights) + + # Phase 2: Compute per-query calibration parameters for dense cosine scores. + # Same approach as BB25: beta=median, alpha_eff=1/std. The logit for a dense + # score is alpha * (score - median), centering the median candidate at logit 0. + densemedian, densealpha = self.calibrate(denseraw) + + # Phase 3: Fuse via weighted mean log-odds with confidence scaling. + fused = self.fuse(uids, weights, densemedian, densealpha) + + return sorted(fused.items(), key=lambda x: x[1], reverse=True)[:limit] + + def rawscores(self, vectors, weights): + """ + Collects raw scores. + + Args: + vectors: tuple of (dense vectors, sparse vectors) + weights: [dense weights, sparse weights] + + Returns: + (uids, raw dense scores) + """ + + uids, denseraw = {}, [] + for v, scores in enumerate(vectors): + for uid, score in scores if weights[v] > 0 else []: + if uid not in uids: + uids[uid] = [None, None] + + if v == 0: + uids[uid][0] = score + denseraw.append(score) + else: + # Sparse BB25 score: already a calibrated probability + uids[uid][1] = score + + return uids, denseraw + + def calibrate(self, raw): + """ + Computes per-query calibration parameters for dense cosine scores. + + Uses the same approach as BB25: beta=median, alpha_eff=1/std so the + logit for a dense score is alpha * (score - median), centering the + median candidate at logit 0. + + Args: + raw: list of raw dense cosine scores + + Returns: + (median, alpha) calibration parameters + """ + + median, alpha = 0.0, 1.0 + + array = [s for s in raw if s > 0] + if array: + median = sorted(array)[len(array) // 2] + std = (sum((x - sum(array) / len(array)) ** 2 for x in array) / len(array)) ** 0.5 + alpha = 1.0 / std if std > 0 else 1.0 + + return median, alpha + + def fuse(self, uids, weights, densemedian, densealpha): + """ + Fuses scores together. + + Args: + uids: result ids + weights: [dense weights, sparse weights] + densemedian: dense median + densealpha: dense alpha + + Returns: + fused scores + """ + + fused, n, alpha = {}, 2, 0.5 + scale = n**alpha + + for uid, pair in uids.items(): + # Unpair scores + rawdense, psparse = pair + + if rawdense is not None and psparse is not None: + # Calibrate dense score via dynamic sigmoid + logitdense = densealpha * (rawdense - densemedian) + logitdense = max(min(logitdense, 500), -500) + + # Sparse BB25 score -> logit + psparse = min(max(psparse, LogOdds.EPSILON), 1.0 - LogOdds.EPSILON) + logitsparse = math.log(psparse / (1.0 - psparse)) + + # Weighted mean log-odds with confidence scaling (Paper 2, Def 4.2.1) + lbar = weights[0] * logitdense + weights[1] * logitsparse + fused[uid] = lbar * scale + + elif rawdense is not None: + # Only dense signal: calibrated logit scaled by weight + logitdense = densealpha * (rawdense - densemedian) + logitdense = max(min(logitdense, 500), -500) + fused[uid] = logitdense * weights[0] + + else: + # Only sparse signal: logit scaled by weight + psparse = min(max(psparse, LogOdds.EPSILON), 1.0 - LogOdds.EPSILON) + fused[uid] = math.log(psparse / (1.0 - psparse)) * weights[1] + + # Transform logits to probabilities + return {uid: 1 / (1 + math.exp(-score)) for uid, score in fused.items()} diff --git a/src/python/txtai/embeddings/search/ids.py b/src/python/txtai/embeddings/search/ids.py new file mode 100644 index 0000000..677dec8 --- /dev/null +++ b/src/python/txtai/embeddings/search/ids.py @@ -0,0 +1,61 @@ +""" +Ids module +""" + + +class Ids: + """ + Resolves internal ids for lists of ids. + """ + + def __init__(self, embeddings): + """ + Create a new ids action. + + Args: + embeddings: embeddings instance + """ + + self.database = embeddings.database + self.ids = embeddings.ids + + def __call__(self, ids): + """ + Resolve internal ids. + + Args: + ids: ids + + Returns: + internal ids + """ + + # Resolve ids using database if available, otherwise fallback to embeddings ids + results = self.database.ids(ids) if self.database else self.scan(ids) + + # Create dict of id: [iids] given there is a one to many relationship + ids = {} + for iid, uid in results: + if uid not in ids: + ids[uid] = [] + ids[uid].append(iid) + + return ids + + def scan(self, ids): + """ + Scans embeddings ids array for matches when content is disabled. + + Args: + ids: search ids + + Returns: + internal ids + """ + + # Find existing ids + indices = [] + for uid in ids: + indices.extend([(index, value) for index, value in enumerate(self.ids) if uid == value]) + + return indices diff --git a/src/python/txtai/embeddings/search/query.py b/src/python/txtai/embeddings/search/query.py new file mode 100644 index 0000000..fd54d48 --- /dev/null +++ b/src/python/txtai/embeddings/search/query.py @@ -0,0 +1,72 @@ +""" +Query module +""" + +# Core library imports +from ...util import Library + +transformers = Library().transformers() + + +class Query: + """ + Query translation model. + """ + + def __init__(self, path, prefix=None, maxlength=512): + """ + Creates a query translation model. + + Args: + path: path to query model + prefix: text prefix + maxlength: max sequence length to generate + """ + + self.tokenizer = transformers.AutoTokenizer.from_pretrained(path) + self.model = transformers.AutoModelForSeq2SeqLM.from_pretrained(path) + + # Default prefix if not provided for T5 models + if not prefix and isinstance(self.model, transformers.T5ForConditionalGeneration): + prefix = "translate English to SQL: " + + self.prefix = prefix + self.maxlength = maxlength + + def __call__(self, query): + """ + Runs query translation model. + + Args: + query: input query + + Returns: + transformed query + """ + + # Add prefix, if necessary + if self.prefix: + query = f"{self.prefix}{query}" + + # Tokenize and generate text using model + features = self.tokenizer([query], return_tensors="pt") + output = self.model.generate(input_ids=features["input_ids"], attention_mask=features["attention_mask"], max_length=self.maxlength) + + # Decode tokens to text + result = self.tokenizer.decode(output[0], skip_special_tokens=True) + + # Clean and return generated text + return self.clean(result) + + def clean(self, text): + """ + Applies a series of rules to clean generated text. + + Args: + text: input text + + Returns: + clean text + """ + + return text.replace("$=", "<=") diff --git a/src/python/txtai/embeddings/search/scan.py b/src/python/txtai/embeddings/search/scan.py new file mode 100644 index 0000000..a7803e7 --- /dev/null +++ b/src/python/txtai/embeddings/search/scan.py @@ -0,0 +1,196 @@ +""" +Scan module +""" + + +class Scan: + """ + Scans indexes for query matches. + """ + + def __init__(self, search, limit, weights, index): + """ + Creates a new scan instance. + + Args: + search: index search function + limit: maximum results + weights: default hybrid score weights + index: default index name + """ + + # Index search function + self.search = search + + # Default query limit + self.limit = limit + + # Default number of candidates + self.candidates = None + + # Default query weights + self.weights = weights + + # Default index + self.index = index + + def __call__(self, queries, parameters): + """ + Executes a scan for a list of queries. + + Args: + queries: list of queries to run + parameters: list of dicts of named parameters to bind to placeholders + + Returns: + list of (id, score) per query + """ + + # Query results group by unique query clause id + results = {} + + # Default number of candidates + default = None + + # Group by index and run + for index, iqueries in self.parse(queries, parameters).items(): + # Query limit to pass to batch search + candidates = [query.candidates for query in iqueries if query.candidates] + if not candidates and not default: + default = self.default(queries) + + candidates = max(candidates) if candidates else default + + # Query weights to pass to batch search + weights = [query.weights for query in iqueries if query.weights is not None] + weights = max(weights) if weights else self.weights + + # Index to run query against + index = index if index else self.index + + # Run index searches + for x, result in enumerate(self.search([query.text for query in iqueries], candidates, weights, index)): + # Save query id and results to later join to original query + results[iqueries[x].uid] = (iqueries[x].qid, result) + + # Sort by query uid and return results + return [result for _, result in sorted(results.items())] + + def parse(self, queries, parameters): + """ + Parse index query clauses from a list of parsed queries. + + Args: + queries: list of parsed queries + parameters: list of dicts of named parameters to bind to placeholders + + Returns: + index query clauses grouped by index + """ + + results, uid = {}, 0 + for x, query in enumerate(queries): + if "similar" in query: + # Extract similar query clauses + for params in query["similar"]: + # Resolve bind parameters + if parameters and parameters[x]: + params = self.bind(params, parameters[x]) + + # Parse query clause + clause = Clause(uid, x, params) + + # Create clause list for index + if clause.index not in results: + results[clause.index] = [] + + # Add query to index list, increment uid + results[clause.index].append(clause) + uid += 1 + + return results + + def bind(self, similar, parameters): + """ + Resolves bind parameters for a similar function call. + + Args: + similar: similar function call arguments + parameters: bind parameters + + Returns: + similar function call arguments with resolved bind parameters + """ + + resolved = [] + for p in similar: + # Resolve bind parameters + if isinstance(p, str) and p.startswith(":") and p[1:] in parameters: + resolved.append(parameters[p[1:]]) + else: + resolved.append(p) + + return resolved + + def default(self, queries): + """ + Derives the default number of candidates. The number of candidates are the number of results to bring back + from index queries. This is an optional argument to similar() clauses. + + For a single query filter clause, the default is the query limit. With multiple filtering clauses, the default is + 10x the query limit. This ensures that limit results are still returned with additional filtering after an index query. + + Args: + queries: list of queries + + Returns: + default candidate list size + """ + + multitoken = any(query.get("where") and len(query["where"].split()) > 1 for query in queries) + return self.limit * 10 if multitoken else self.limit + + +class Clause: + """ + Parses and stores query clause parameters. + """ + + def __init__(self, uid, qid, params): + """ + Creates a new query clause. + + Args: + uid: query clause id + qid: query id clause is a part of + params: query parameters to parse + """ + + self.uid, self.qid = uid, qid + self.text, self.index = params[0], None + self.candidates, self.weights = None, None + + # Parse additional similar clause parameters + if len(params) > 1: + self.parse(params[1:]) + + def parse(self, params): + """ + Parses clause parameters into this instance. + + Args: + params: query clause parameters + """ + + for param in params: + if (isinstance(param, str) and param.isdigit()) or isinstance(param, int): + # Number of query candidates + self.candidates = int(param) + + elif (isinstance(param, str) and param.replace(".", "").isdigit()) or isinstance(param, float): + # Hybrid score weights + self.weights = float(param) + + else: + # Target index + self.index = param diff --git a/src/python/txtai/embeddings/search/terms.py b/src/python/txtai/embeddings/search/terms.py new file mode 100644 index 0000000..eb3a771 --- /dev/null +++ b/src/python/txtai/embeddings/search/terms.py @@ -0,0 +1,46 @@ +""" +Terms module +""" + + +class Terms: + """ + Reduces a query statement down to keyword terms. This method extracts the query text from similar clauses if it's a SQL statement. + Otherwise, the original query is returned. + """ + + def __init__(self, embeddings): + """ + Create a new terms action. + + Args: + embeddings: embeddings instance + """ + + self.database = embeddings.database + + def __call__(self, queries): + """ + Extracts keyword terms from a list of queries. + + Args: + queries: list of queries + + Returns: + list of queries reduced down to keyword term strings + """ + + # Parse queries and extract keyword terms for each query + if self.database: + terms = [] + for query in queries: + # Parse query + parse = self.database.parse(query) + + # Join terms from similar clauses + terms.append(" ".join(" ".join(s) for s in parse["similar"])) + + return terms + + # Return original query when database is None + return queries diff --git a/src/python/txtai/graph/__init__.py b/src/python/txtai/graph/__init__.py new file mode 100644 index 0000000..1b0b3fb --- /dev/null +++ b/src/python/txtai/graph/__init__.py @@ -0,0 +1,10 @@ +""" +Graph imports +""" + +from .base import Graph +from .factory import GraphFactory +from .networkx import NetworkX +from .query import Query +from .rdbms import RDBMS +from .topics import Topics diff --git a/src/python/txtai/graph/base.py b/src/python/txtai/graph/base.py new file mode 100644 index 0000000..30449d9 --- /dev/null +++ b/src/python/txtai/graph/base.py @@ -0,0 +1,769 @@ +""" +Graph module +""" + +from collections import Counter + +from .topics import Topics + + +# pylint: disable=R0904 +class Graph: + """ + Base class for Graph instances. This class builds graph networks. Supports topic modeling + and relationship traversal. + """ + + def __init__(self, config): + """ + Creates a new Graph. + + Args: + config: graph configuration + """ + + # Graph configuration + self.config = config if config is not None else {} + + # Graph backend + self.backend = None + + # Topic modeling + self.categories = None + self.topics = None + + # Transform columns + columns = config.get("columns", {}) + self.text = columns.get("text", "text") + self.object = columns.get("object", "object") + + # Attributes to copy - skips text/object/relationship fields - set to True to copy all + self.copyattributes = config.get("copyattributes", False) + + # Relationships are manually-provided edges + self.relationships = columns.get("relationships", "relationships") + self.relations = {} + + def create(self): + """ + Creates the graph network. + """ + + raise NotImplementedError + + def count(self): + """ + Returns the total number of nodes in graph. + + Returns: + total nodes in graph + """ + + raise NotImplementedError + + def scan(self, attribute=None, data=False): + """ + Iterates over nodes that match a criteria. If no criteria specified, all nodes + are returned. + + Args: + attribute: if specified, nodes having this attribute are returned + data: if True, attribute data is also returned + + Returns: + node id iterator if data is False or (id, attribute dictionary) iterator if data is True + """ + + raise NotImplementedError + + def node(self, node): + """ + Get node by id. Returns None if not found. + + Args: + node: node id + + Returns: + graph node + """ + + raise NotImplementedError + + def addnode(self, node, **attrs): + """ + Adds a node to the graph. + + Args: + node: node id + attrs: node attributes + """ + + raise NotImplementedError + + def addnodes(self, nodes): + """ + Adds nodes to the graph. + + Args: + nodes: list of (node, attributes) to add + """ + + raise NotImplementedError + + def removenode(self, node): + """ + Removes a node and all it's edges from graph. + + Args: + node: node id + """ + + raise NotImplementedError + + def hasnode(self, node): + """ + Returns True if node found, False otherwise. + + Args: + node: node id + + Returns: + True if node found, False otherwise + """ + + raise NotImplementedError + + def attribute(self, node, field): + """ + Gets a node attribute. + + Args: + node: node id + field: attribute name + + Returns: + attribute value + """ + + raise NotImplementedError + + def addattribute(self, node, field, value): + """ + Adds an attribute to node. + + Args: + node: node id + field: attribute name + value: attribute value + """ + + raise NotImplementedError + + def removeattribute(self, node, field): + """ + Removes an attribute from node. + + Args: + node: node id + field: attribute name + + Returns: + attribute value or None if not present + """ + + raise NotImplementedError + + def edgecount(self): + """ + Returns the total number of edges. + + Returns: + total number of edges in graph + """ + + raise NotImplementedError + + def edges(self, node): + """ + Gets edges of node by id. + + Args: + node: node id + + Returns: + list of edge node ids + """ + + raise NotImplementedError + + def addedge(self, source, target, **attrs): + """ + Adds an edge to graph. + + Args: + source: node 1 id + target: node 2 id + """ + + raise NotImplementedError + + def addedges(self, edges): + """ + Adds an edge to graph. + + Args: + edges: list of (source, target, attributes) to add + """ + + raise NotImplementedError + + def hasedge(self, source, target=None): + """ + Returns True if edge found, False otherwise. If target is None, this method + returns True if any edge is found. + + Args: + source: node 1 id + target: node 2 id + + Returns: + True if edge found, False otherwise + """ + + raise NotImplementedError + + def centrality(self): + """ + Runs a centrality algorithm on the graph. + + Returns: + dict of {node id: centrality score} + """ + + raise NotImplementedError + + def pagerank(self): + """ + Runs the pagerank algorithm on the graph. + + Returns: + dict of {node id, page rank score} + """ + + raise NotImplementedError + + def showpath(self, source, target): + """ + Gets the shortest path between source and target. + + Args: + source: start node id + target: end node id + + Returns: + list of node ids representing the shortest path + """ + + raise NotImplementedError + + def isquery(self, queries): + """ + Checks if queries are supported graph queries. + + Args: + queries: queries to check + + Returns: + True if all the queries are supported graph queries, False otherwise + """ + + raise NotImplementedError + + def parse(self, query): + """ + Parses a graph query into query components. + + Args: + query: graph query + + Returns: + query components as a dictionary + """ + + raise NotImplementedError + + def search(self, query, limit=None, graph=False): + """ + Searches graph for nodes matching query. + + Args: + query: graph query + limit: maximum results + graph: return graph results if True + + Returns: + list of dict if graph is set to False + filtered graph if graph is set to True + """ + + raise NotImplementedError + + def batchsearch(self, queries, limit=None, graph=False): + """ + Searches graph for nodes matching query. + + Args: + query: graph query + limit: maximum results + graph: return graph results if True + + Returns: + list of dict if graph is set to False + filtered graph if graph is set to True + """ + + return [self.search(query, limit, graph) for query in queries] + + def communities(self, config): + """ + Run community detection on the graph. + + Args: + config: configuration + + Returns: + dictionary of {topic name:[ids]} + """ + + raise NotImplementedError + + def load(self, path): + """ + Loads a graph at path. + + Args: + path: path to graph + """ + + raise NotImplementedError + + def save(self, path): + """ + Saves a graph at path. + + Args: + path: path to save graph + """ + + raise NotImplementedError + + def loaddict(self, data): + """ + Loads data from input dictionary into this graph. + + Args: + data: input dictionary + """ + + raise NotImplementedError + + def savedict(self): + """ + Saves graph data to a dictionary. + + Returns: + dict + """ + + raise NotImplementedError + + def initialize(self): + """ + Initialize graph instance. + """ + + if not self.backend: + self.backend = self.create() + + def close(self): + """ + Closes this graph. + """ + + self.backend, self.categories, self.topics = None, None, None + + def insert(self, documents, index=0): + """ + Insert graph nodes for each document. + + Args: + documents: list of (id, data, tags) + index: indexid offset, used for node ids + """ + + # Initialize graph backend + self.initialize() + + nodes = [] + for uid, document, _ in documents: + # Manually provided relationships and attributes to copy + relations, attributes = None, {} + + # Extract data from dictionary + if isinstance(document, dict): + # Extract relationships + relations = document.get(self.relationships) + + # Attributes to copy, if any + search = self.copyattributes if isinstance(self.copyattributes, list) else [] + attributes = { + k: v + for k, v in document.items() + if k not in [self.text, self.object, self.relationships] and (self.copyattributes is True or k in search) + } + + # Require text or object field + document = document.get(self.text, document.get(self.object)) + + if document is not None: + if isinstance(document, list): + # Join tokens as text + document = " ".join(document) + + # Create node + nodes.append((index, {**{"id": uid, "data": document}, **attributes})) + + # Add relationships + self.addrelations(index, relations) + + index += 1 + + # Add nodes + self.addnodes(nodes) + + def delete(self, ids): + """ + Deletes ids from graph. + + Args: + ids: node ids to delete + """ + + for node in ids: + # Remove existing node, if it exists + if self.hasnode(node): + # Delete from topics + topic = self.attribute(node, "topic") + if topic and self.topics: + # Delete id from topic + self.topics[topic].remove(node) + + # Also delete topic, if it's empty + if not self.topics[topic]: + self.topics.pop(topic) + + # Delete node + self.removenode(node) + + def index(self, search, ids, similarity): + """ + Build relationships between graph nodes using a score-based search function. + + Args: + search: batch search function - takes a list of queries and returns lists of (id, scores) to use as edge weights + ids: ids function - internal id resolver + similarity: batch similarity function - takes a list of text and labels and returns best matches + """ + + # Add relationship edges + self.resolverelations(ids) + + # Infer node edges using search function + self.inferedges(self.scan(), search) + + # Label categories/topics + if "topics" in self.config: + self.addtopics(similarity) + + def upsert(self, search, ids, similarity=None): + """ + Adds relationships for new graph nodes using a score-based search function. + + Args: + search: batch search function - takes a list of queries and returns lists of (id, scores) to use as edge weights + ids: ids function - internal id resolver + similarity: batch similarity function - takes a list of text and labels and returns best matches + """ + + # Detect if topics processing is enabled + hastopics = "topics" in self.config + + # Add relationship edges + self.resolverelations(ids) + + # Infer node edges using new/updated nodes, set updated flag for topic processing, if necessary + self.inferedges(self.scan(attribute="data"), search, {"updated": True} if hastopics else None) + + # Infer topics with topics of connected nodes + if hastopics: + # Infer topics if there is at least one topic, otherwise rebuild + if self.topics: + self.infertopics() + else: + self.addtopics(similarity) + + def filter(self, nodes, graph=None): + """ + Creates a subgraph of this graph using the list of input nodes. This method creates a new graph + selecting only matching nodes, edges, topics and categories. + + Args: + nodes: nodes to select as a list of ids or list of (id, score) tuples + graph: optional graph used to store filtered results + + Returns: + graph + """ + + # Set graph if available, otherwise create a new empty graph of the same type + graph = graph if graph else type(self)(self.config) + + # Initalize subgraph + graph.initialize() + + nodeids = {node[0] if isinstance(node, tuple) else node for node in nodes} + for node in nodes: + # Unpack node and score, if available + node, score = node if isinstance(node, tuple) else (node, None) + + # Add nodes + graph.addnode(node, **self.node(node)) + + # Add score if present + if score is not None: + graph.addattribute(node, "score", score) + + # Add edges + edges = self.edges(node) + if edges: + for target, attributes in self.edges(node).items(): + if target in nodeids: + graph.addedge(node, target, **attributes) + + # Filter categories and topics + if self.topics: + topics = {} + for i, (topic, ids) in enumerate(self.topics.items()): + ids = [x for x in ids if x in nodeids] + if ids: + topics[topic] = (self.categories[i] if self.categories else None, ids) + + # Sort by number of nodes descending + topics = sorted(topics.items(), key=lambda x: len(x[1][1]), reverse=True) + + # Copy filtered categories and topics + graph.categories = [category for _, (category, _) in topics] if self.categories else None + graph.topics = {topic: ids for topic, (_, ids) in topics} + + return graph + + def addrelations(self, node, relations): + """ + Add manually-provided relationships. + + Args: + node: node id + relations: list of relationships to add + """ + + # Add relationships, if any + if relations: + if node not in self.relations: + self.relations[node] = [] + + # Add each relationship + for relation in relations: + # Support both dict and string ids + relation = {"id": relation} if not isinstance(relation, dict) else relation + self.relations[node].append(relation) + + def resolverelations(self, ids): + """ + Resolves ids and creates edges for manually-provided relationships. + + Args: + ids: internal id resolver + """ + + # Relationship edges + edges = [] + + # Resolve ids and create edges for relationships + for node, relations in self.relations.items(): + # Resolve internal ids + iids = ids(y["id"] for y in relations) + + # Add each edge + for relation in relations: + # Make copy of relation + relation = relation.copy() + + # Lookup targets for relationship + targets = iids.get(str(relation.pop("id"))) + + # Create edge for each instance of id - internal id pair + if targets: + for target in targets: + # Add weight, if not provided + relation["weight"] = relation.get("weight", 1.0) + + # Add edge and all other attributes + edges.append((node, target, relation)) + + # Add relationships + if edges: + self.addedges(edges) + + # Clear temporary relationship storage + self.relations = {} + + def inferedges(self, nodes, search, attributes=None): + """ + Infers edges for a list of nodes using a score-based search function. + + Args: + nodes: list of nodes + search: search function to use to identify edges + attribute: dictionary of attributes to add to each node + """ + + # Read graph parameters + batchsize, limit, minscore = self.config.get("batchsize", 256), self.config.get("limit", 15), self.config.get("minscore", 0.1) + approximate = self.config.get("approximate", True) + + batch = [] + for node in nodes: + # Get data attribute + data = self.removeattribute(node, "data") + + # Set text field when data is a string + if isinstance(data, str): + self.addattribute(node, "text", data) + + # Add additional attributes, if specified + if attributes: + for field, value in attributes.items(): + self.addattribute(node, field, value) + + # Skip nodes with existing edges when building an approximate network + if not approximate or not self.hasedge(node): + batch.append((node, data)) + + # Process batch + if len(batch) == batchsize: + self.addbatch(search, batch, limit, minscore) + batch = [] + + if batch: + self.addbatch(search, batch, limit, minscore) + + def addbatch(self, search, batch, limit, minscore): + """ + Adds batch of documents to graph. This method runs the search function for each item in batch + and adds node edges between the input and each search result. + + Args: + search: search function to use to identify edges + batch: batch to add + limit: max edges to add per node + minscore: min score to add node edge + """ + + edges = [] + for x, result in enumerate(search([data for _, data in batch], limit)): + # Get input node id + x, _ = batch[x] + + # Add edges for each input node id and result node id pair that meets specified criteria + for y, score in result: + if str(x) != str(y) and score > minscore: + edges.append((x, y, {"weight": score})) + + self.addedges(edges) + + def addtopics(self, similarity=None): + """ + Identifies and adds topics using community detection. + + Args: + similarity: similarity function for labeling categories + """ + + # Clear previous topics, if any + self.cleartopics() + + # Use community detection to get topics + topics = Topics(self.config["topics"]) + config = topics.config + self.topics = topics(self) + + # Label each topic with a higher level category + if "categories" in config and similarity: + self.categories = [] + results = similarity(self.topics.keys(), config["categories"]) + for result in results: + self.categories.append(config["categories"][result[0][0]]) + + # Add topic-related node attributes + for x, topic in enumerate(self.topics): + for r, node in enumerate(self.topics[topic]): + self.addattribute(node, "topic", topic) + self.addattribute(node, "topicrank", r) + + if self.categories: + self.addattribute(node, "category", self.categories[x]) + + def cleartopics(self): + """ + Clears topic fields from all nodes. + """ + + # Clear previous topics, if any + if self.topics: + for node in self.scan(): + self.removeattribute(node, "topic") + self.removeattribute(node, "topicrank") + + if self.categories: + self.removeattribute(node, "category") + + self.topics, self.categories = None, None + + def infertopics(self): + """ + Infers topics for all nodes with an "updated" attribute. This method analyzes the direct node + neighbors and set the most commonly occuring topic and category for each node. + """ + + # Iterate over nodes missing topic attribute (only occurs for new nodes) + for node in self.scan(attribute="updated"): + # Remove updated attribute + self.removeattribute(node, "updated") + + # Get list of neighboring nodes + ids = self.edges(node) + ids = ids.keys() if ids else None + + # Infer topic + topic = Counter(self.attribute(x, "topic") for x in ids).most_common(1)[0][0] if ids else None + if topic: + # Add id to topic list and set topic attribute + self.topics[topic].append(node) + self.addattribute(node, "topic", topic) + + # Set topic rank + self.addattribute(node, "topicrank", len(self.topics[topic]) - 1) + + # Infer category + category = Counter(self.attribute(x, "category") for x in ids).most_common(1)[0][0] + self.addattribute(node, "category", category) diff --git a/src/python/txtai/graph/factory.py b/src/python/txtai/graph/factory.py new file mode 100644 index 0000000..d03e7bd --- /dev/null +++ b/src/python/txtai/graph/factory.py @@ -0,0 +1,61 @@ +""" +Factory module +""" + +from ..util import Resolver + +from .networkx import NetworkX +from .rdbms import RDBMS + + +class GraphFactory: + """ + Methods to create graphs. + """ + + @staticmethod + def create(config): + """ + Create a Graph. + + Args: + config: graph configuration + + Returns: + Graph + """ + + # Graph instance + graph = None + backend = config.get("backend", "networkx") + + # Create graph instance + if backend == "networkx": + graph = NetworkX(config) + elif backend == "rdbms": + graph = RDBMS(config) + else: + graph = GraphFactory.resolve(backend, config) + + # Store config back + config["backend"] = backend + + return graph + + @staticmethod + def resolve(backend, config): + """ + Attempt to resolve a custom backend. + + Args: + backend: backend class + config: index configuration parameters + + Returns: + Graph + """ + + try: + return Resolver()(backend)(config) + except Exception as e: + raise ImportError(f"Unable to resolve graph backend: '{backend}'") from e diff --git a/src/python/txtai/graph/networkx.py b/src/python/txtai/graph/networkx.py new file mode 100644 index 0000000..d0a81f4 --- /dev/null +++ b/src/python/txtai/graph/networkx.py @@ -0,0 +1,275 @@ +""" +NetworkX module +""" + +import os + +from tempfile import TemporaryDirectory + +# Conditional import +try: + import networkx as nx + + from networkx.algorithms.community import asyn_lpa_communities, greedy_modularity_communities, louvain_partitions + from networkx.readwrite import json_graph + + NETWORKX = True +except ImportError: + NETWORKX = False + +from ..archive import ArchiveFactory +from ..serialize import SerializeError, SerializeFactory + +from .base import Graph +from .query import Query + + +# pylint: disable=R0904 +class NetworkX(Graph): + """ + Graph instance backed by NetworkX. + """ + + def __init__(self, config): + super().__init__(config) + + if not NETWORKX: + raise ImportError('NetworkX is not available - install "graph" extra to enable') + + def create(self): + return nx.Graph() + + def count(self): + return self.backend.number_of_nodes() + + def scan(self, attribute=None, data=False): + # Full graph + graph = self.backend + + # Filter graph to nodes having a specified attribute + if attribute: + graph = nx.subgraph_view(self.backend, filter_node=lambda x: attribute in self.node(x)) + + # Return either list of matching ids or tuple of (id, attribute dictionary) + return graph.nodes(data=True) if data else graph + + def node(self, node): + return self.backend.nodes.get(node) + + def addnode(self, node, **attrs): + self.backend.add_node(node, **attrs) + + def addnodes(self, nodes): + self.backend.add_nodes_from(nodes) + + def removenode(self, node): + if self.hasnode(node): + self.backend.remove_node(node) + + def hasnode(self, node): + return self.backend.has_node(node) + + def attribute(self, node, field): + return self.node(node).get(field) if self.hasnode(node) else None + + def addattribute(self, node, field, value): + if self.hasnode(node): + self.node(node)[field] = value + + def removeattribute(self, node, field): + return self.node(node).pop(field, None) if self.hasnode(node) else None + + def edgecount(self): + return self.backend.number_of_edges() + + def edges(self, node): + edges = self.backend.adj.get(node) + if edges: + return dict(sorted(edges.items(), key=lambda x: x[1].get("weight", 0), reverse=True)) + + return None + + def addedge(self, source, target, **attrs): + self.backend.add_edge(source, target, **attrs) + + def addedges(self, edges): + self.backend.add_edges_from(edges) + + def hasedge(self, source, target=None): + if target is None: + edges = self.backend.adj.get(source) + return len(edges) > 0 if edges else False + + return self.backend.has_edge(source, target) + + def centrality(self): + rank = nx.degree_centrality(self.backend) + return dict(sorted(rank.items(), key=lambda x: x[1], reverse=True)) + + def pagerank(self): + rank = nx.pagerank(self.backend, weight="weight") + return dict(sorted(rank.items(), key=lambda x: x[1], reverse=True)) + + def showpath(self, source, target): + # pylint: disable=E1121 + return nx.shortest_path(self.backend, source, target, self.distance) + + def isquery(self, queries): + return Query().isquery(queries) + + def parse(self, query): + return Query().parse(query) + + def search(self, query, limit=None, graph=False): + # Run graph query + results = Query()(self, query, limit) + + # Transform into filtered graph + if graph: + nodes = set() + for column in results.values(): + for value in column: + if isinstance(value, list): + # Path group + nodes.update([node for node in value if node and not isinstance(node, dict)]) + elif isinstance(value, dict): + # Nodes by id attribute + nodes.update(uid for uid, attr in self.scan(data=True) if attr["id"] == value["id"]) + elif value is not None: + # Single node id + nodes.add(value) + + return self.filter(list(nodes)) + + # Transform columnar structure into rows + keys = list(results.keys()) + rows, count = [], len(results[keys[0]]) + + for x in range(count): + rows.append({str(key): results[key][x] for key in keys}) + + return rows + + def communities(self, config): + # Get community detection algorithm + algorithm = config.get("algorithm") + + if algorithm == "greedy": + communities = greedy_modularity_communities(self.backend, weight="weight", resolution=config.get("resolution", 100)) + elif algorithm == "lpa": + communities = asyn_lpa_communities(self.backend, weight="weight", seed=0) + else: + communities = self.louvain(config) + + return communities + + def load(self, path): + try: + # Load graph data + data = SerializeFactory.create().load(path) + + # Add data to graph + self.backend = self.create() + self.backend.add_nodes_from(data["nodes"]) + self.backend.add_edges_from(data["edges"]) + + # Load categories + self.categories = data.get("categories") + + # Load topics + self.topics = data.get("topics") + + except SerializeError: + # Backwards compatible support for legacy TAR format + self.loadtar(path) + + def save(self, path): + # Save graph data + SerializeFactory.create().save( + { + "nodes": [(uid, self.node(uid)) for uid in self.scan()], + "edges": list(self.backend.edges(data=True)), + "categories": self.categories, + "topics": self.topics, + }, + path, + ) + + def loaddict(self, data): + self.backend = json_graph.node_link_graph(data, name="indexid") + self.categories, self.topics = data.get("categories"), data.get("topics") + + def savedict(self): + data = json_graph.node_link_data(self.backend, name="indexid") + data["categories"] = self.categories + data["topics"] = self.topics + + return data + + def louvain(self, config): + """ + Runs the Louvain community detection algorithm. + + Args: + config: topic configuration + + Returns: + list of [ids] per community + """ + + # Partition level to use + level = config.get("level", "best") + + # Run community detection + results = list(louvain_partitions(self.backend, weight="weight", resolution=config.get("resolution", 100), seed=0)) + + # Get partition level (first or best) + return results[0] if level == "first" else results[-1] + + # pylint: disable=W0613 + def distance(self, source, target, attrs): + """ + Computes distance between source and target nodes using weight. + + Args: + source: source node + target: target node + attrs: edge attributes + + Returns: + distance between source and target + """ + + # Distance is 1 - score. Skip minimal distances as they are near duplicates. + distance = max(1.0 - attrs["weight"], 0.0) + return distance if distance >= 0.15 else 1.00 + + def loadtar(self, path): + """ + Loads a graph from the legacy TAR file. + + Args: + path: path to graph + """ + + # Pickle serialization - backwards compatible + serializer = SerializeFactory.create("pickle") + + # Extract files to temporary directory and load content + with TemporaryDirectory() as directory: + # Unpack files + archive = ArchiveFactory.create(directory) + archive.load(path, "tar") + + # Load graph backend + self.backend = serializer.load(f"{directory}/graph") + + # Load categories, if necessary + path = f"{directory}/categories" + if os.path.exists(path): + self.categories = serializer.load(path) + + # Load topics, if necessary + path = f"{directory}/topics" + if os.path.exists(path): + self.topics = serializer.load(path) diff --git a/src/python/txtai/graph/query.py b/src/python/txtai/graph/query.py new file mode 100644 index 0000000..219f07b --- /dev/null +++ b/src/python/txtai/graph/query.py @@ -0,0 +1,181 @@ +""" +Query module +""" + +import logging +import re + +try: + from grandcypher import GrandCypher + + GRANDCYPHER = True +except ImportError: + GRANDCYPHER = False + +# Logging configuration +logger = logging.getLogger(__name__) + + +class Query: + """ + Runs openCypher graph queries using the GrandCypher library. This class also supports search functions. + """ + + # Similar token + SIMILAR = "__SIMILAR__" + + def __init__(self): + """ + Create a new graph query instance. + """ + + if not GRANDCYPHER: + raise ImportError('GrandCypher is not available - install "graph" extra to enable') + + def __call__(self, graph, query, limit): + """ + Runs a graph query. + + Args: + graph: graph instance + query: graph query, can be a full query string or a parsed query dictionary + limit: number of results + + Returns: + results + """ + + # Results by attribute and ids filter + attributes, uids = None, None + + # Build the query from a parsed query + if isinstance(query, dict): + query, attributes, uids = self.build(query) + + # Filter graph, if applicable + if uids: + graph = self.filter(graph, attributes, uids) + + # Debug log graph query + logger.debug(query) + + # Run openCypher query + return GrandCypher(graph.backend, limit if limit else 3).run(query) + + def isquery(self, queries): + """ + Checks a list of queries to see if all queries are openCypher queries. + + Args: + queries: list of queries to check + + Returns: + True if all queries are openCypher queries + """ + + # Check for required graph query clauses + return all(query and query.strip().startswith("MATCH ") and "RETURN " in query for query in queries) + + def parse(self, query): + """ + Parses a graph query. This method supports parsing search functions and replacing them with placeholders. + + Args: + query: graph query + + Returns: + parsed query as a dictionary + """ + + # Parameters + where, limit, nodes, similar = None, None, [], [] + + # Parse where clause + match = re.search(r"where(.+?)return", query, flags=re.DOTALL | re.IGNORECASE) + if match: + where = match.group(1).strip() + + # Parse limit clause + match = re.search(r"limit\s+(\d+)", query, flags=re.DOTALL | re.IGNORECASE) + if match: + limit = match.group(1) + + # Parse similar clauses + for x, match in enumerate(re.finditer(r"similar\((.+?)\)", query, flags=re.DOTALL | re.IGNORECASE)): + # Replace similar clause with placeholder + query = query.replace(match.group(0), f"{Query.SIMILAR}{x}") + + # Parse similar clause parameters + params = [param.strip().replace("'", "").replace('"', "") for param in match.group(1).split(",")] + nodes.append(params[0]) + similar.append(params[1:]) + + # Return parsed query + return { + "query": query, + "where": where, + "limit": limit, + "nodes": nodes, + "similar": similar, + } + + def build(self, parse): + """ + Constructs a full query from a parsed query. This method supports substituting placeholders with search results. + + Args: + parse: parsed query + + Returns: + graph query + """ + + # Get query. Initialize attributes and uids. + query, attributes, uids = parse["query"], {}, {} + + # Replace similar clause with id query + if "results" in parse: + for x, result in enumerate(parse["results"]): + # Get query node + node = parse["nodes"][x] + + # Add similar match attribute + attribute = f"match_{x}" + clause = f"{node}.{attribute} > 0" + + # Replace placeholder with earch results + query = query.replace(f"{Query.SIMILAR}{x}", f"{clause}") + + # Add uids and scores + for uid, score in result: + if uid not in uids: + uids[uid] = score + + # Add results by attribute matched + attributes[attribute] = result + + # Return query, results by attribute matched and ids filter + return query, attributes, uids.items() + + def filter(self, graph, attributes, uids): + """ + Filters the input graph by uids. This method also adds similar match attributes. + + Args: + graph: graph instance + attributes: results by attribute matched + uids: single list with all matching ids + + Returns: + filtered graph + """ + + # Filter the graph + graph = graph.filter(uids) + + # Add similar match attributes + for attribute, result in attributes.items(): + for uid, score in result: + graph.addattribute(uid, attribute, score) + + return graph diff --git a/src/python/txtai/graph/rdbms.py b/src/python/txtai/graph/rdbms.py new file mode 100644 index 0000000..5397dc4 --- /dev/null +++ b/src/python/txtai/graph/rdbms.py @@ -0,0 +1,113 @@ +""" +RDBMS module +""" + +import os + +# Conditional import +try: + from grand import Graph + from grand.backends import SQLBackend, InMemoryCachedBackend + + from sqlalchemy import create_engine, text, StaticPool + from sqlalchemy.schema import CreateSchema + + ORM = True +except ImportError: + ORM = False + +from .networkx import NetworkX + + +class RDBMS(NetworkX): + """ + Graph instance backed by a relational database. + """ + + def __init__(self, config): + # Check before super() in case those required libraries are also not available + if not ORM: + raise ImportError('RDBMS is not available - install "graph" extra to enable') + + super().__init__(config) + + # Graph and database instances + self.graph = None + self.database = None + + def __del__(self): + if hasattr(self, "database") and self.database: + self.database.close() + + def create(self): + # Create graph instance + self.graph, self.database = self.connect() + + # Clear previous graph, if available + for table in [self.config.get("nodes", "nodes"), self.config.get("edges", "edges")]: + self.database.execute(text(f"DELETE FROM {table}")) + + # Return NetworkX compatible backend + return self.graph.nx + + def scan(self, attribute=None, data=False): + if attribute: + for node in self.backend: + attributes = self.node(node) + if attribute in attributes: + yield (node, attributes) if data else node + else: + yield from super().scan(attribute, data) + + def load(self, path): + # Create graph instance + self.graph, self.database = self.connect() + + # Store NetworkX compatible backend + self.backend = self.graph.nx + + def save(self, path): + self.database.commit() + + def close(self): + # Parent logic + super().close() + + # Close database connection + self.database.close() + + def filter(self, nodes, graph=None): + return super().filter(nodes, graph if graph else NetworkX(self.config)) + + def connect(self): + """ + Connects to a graph backed by a relational database. + + Args: + Graph database instance + """ + + # Keyword arguments for SQLAlchemy + kwargs = {"poolclass": StaticPool, "echo": False} + url = self.config.get("url", os.environ.get("GRAPH_URL")) + + # Set default schema, if necessary + schema = self.config.get("schema") + if schema: + # Check that schema exists + engine = create_engine(url) + with engine.begin() as connection: + connection.execute(CreateSchema(schema, if_not_exists=True) if "postgresql" in url else text("SELECT 1")) + + # Set default schema + kwargs["connect_args"] = {"options": f'-c search_path="{schema}"'} if "postgresql" in url else {} + + backend = SQLBackend( + db_url=url, + node_table_name=self.config.get("nodes", "nodes"), + edge_table_name=self.config.get("edges", "edges"), + sqlalchemy_kwargs=kwargs, + ) + + # pylint: disable=W0212 + return Graph(backend=InMemoryCachedBackend(backend, maxsize=None)), backend._connection diff --git a/src/python/txtai/graph/topics.py b/src/python/txtai/graph/topics.py new file mode 100644 index 0000000..8c0dfde --- /dev/null +++ b/src/python/txtai/graph/topics.py @@ -0,0 +1,166 @@ +""" +Topics module +""" + +from ..pipeline import Tokenizer +from ..scoring import ScoringFactory + + +class Topics: + """ + Topic modeling using community detection. + """ + + def __init__(self, config): + """ + Creates a new Topics instance. + + Args: + config: topic configuration + """ + + self.config = config if config else {} + self.tokenizer = Tokenizer(stopwords=True) + + # Additional stopwords to ignore when building topic names + self.stopwords = set() + if "stopwords" in self.config: + self.stopwords.update(self.config["stopwords"]) + + def __call__(self, graph): + """ + Runs topic modeling for input graph. + + Args: + graph: Graph instance + + Returns: + dictionary of {topic name: [ids]} + """ + + # Detect communities + communities = graph.communities(self.config) + + # Sort by community size, largest to smallest + communities = sorted(communities, key=len, reverse=True) + + # Calculate centrality of graph + centrality = graph.centrality() + + # Score communities and generate topn terms + topics = [self.score(graph, x, community, centrality) for x, community in enumerate(communities)] + + # Merge duplicate topics and return + return self.merge(topics) + + def score(self, graph, index, community, centrality): + """ + Scores a community of nodes and generates the topn terms in the community. + + Args: + graph: Graph instance + index: community index + community: community of nodes + centrality: node centrality scores + + Returns: + (topn topic terms, topic ids sorted by score descending) + """ + + # Tokenize input and build scoring index + scoring = ScoringFactory.create({"method": self.config.get("labels", "bm25"), "terms": True}) + scoring.index(((node, self.tokenize(graph, node), None) for node in community)) + + # Check if scoring index has data + if scoring.idf: + # Sort by most commonly occurring terms (i.e. lowest score) + idf = sorted(scoring.idf, key=scoring.idf.get) + + # Term count for generating topic labels + topn = self.config.get("terms", 4) + + # Get topn terms + terms = self.topn(idf, topn) + + # Sort community by score descending + community = [uid for uid, _ in scoring.search(terms, len(community))] + else: + # No text found for topic, generate topic name + terms = ["topic", str(index)] + + # Sort community by centrality scores + community = sorted(community, key=lambda x: centrality[x], reverse=True) + + return (terms, community) + + def tokenize(self, graph, node): + """ + Tokenizes node text. + + Args: + graph: Graph instance + node: node id + + Returns: + list of node tokens + """ + + text = graph.attribute(node, "text") + return self.tokenizer(text) if text else [] + + def topn(self, terms, n): + """ + Gets topn terms. + + Args: + terms: list of terms + n: topn + + Returns: + topn terms + """ + + topn = [] + + for term in terms: + # Add terms that pass tokenization rules + if self.tokenizer(term) and term not in self.stopwords: + topn.append(term) + + # Break once topn terms collected + if len(topn) == n: + break + + return topn + + def merge(self, topics): + """ + Merges duplicate topics + + Args: + topics: list of (topn terms, topic ids) + + Returns: + dictionary of {topic name:[ids]} + """ + + merge, termslist = {}, {} + + for terms, uids in topics: + # Use topic terms as key + key = frozenset(terms) + + # Add key to merged topics, if necessary + if key not in merge: + merge[key], termslist[key] = [], terms + + # Merge communities + merge[key].extend(uids) + + # Sort communities largest to smallest since the order could have changed with merges + results = {} + for k, v in sorted(merge.items(), key=lambda x: len(x[1]), reverse=True): + # Create composite string key using topic terms and store ids + results["_".join(termslist[k])] = v + + return results diff --git a/src/python/txtai/models/__init__.py b/src/python/txtai/models/__init__.py new file mode 100644 index 0000000..2658504 --- /dev/null +++ b/src/python/txtai/models/__init__.py @@ -0,0 +1,9 @@ +""" +Models imports +""" + +from .models import Models +from .onnx import OnnxModel +from .pooling import * +from .registry import Registry +from .tokendetection import TokenDetection diff --git a/src/python/txtai/models/models.py b/src/python/txtai/models/models.py new file mode 100644 index 0000000..bc80a22 --- /dev/null +++ b/src/python/txtai/models/models.py @@ -0,0 +1,267 @@ +""" +Models module +""" + +import os + +from .onnx import OnnxModel + +# Conditional imports +from ..util import Library + +library = Library() +torch = library.torch() +transformers = library.transformers() + + +class Models: + """ + Utility methods for working with machine learning models + """ + + @staticmethod + def checklength(config, tokenizer): + """ + Checks the length for a Hugging Face Transformers tokenizer using a Hugging Face Transformers config. Copies the + max_position_embeddings parameter if the tokenizer has no max_length set. This helps with backwards compatibility + with older tokenizers. + + Args: + config: transformers config + tokenizer: transformers tokenizer + """ + + # Unpack nested config, handles passing model directly + if hasattr(config, "config"): + config = config.config + + if ( + hasattr(config, "max_position_embeddings") + and tokenizer + and hasattr(tokenizer, "model_max_length") + and tokenizer.model_max_length == int(1e30) + ): + tokenizer.model_max_length = config.max_position_embeddings + + @staticmethod + def maxlength(config, tokenizer): + """ + Gets the best max length to use for generate calls. This method will return config.max_length if it's set. Otherwise, it will return + tokenizer.model_max_length. + + Args: + config: transformers config + tokenizer: transformers tokenizer + """ + + # Unpack nested config, handles passing model directly + if hasattr(config, "config"): + config = config.config + + # Get non-defaulted fields + keys = config.to_diff_dict() + + # Use config.max_length if not set to default value, else use tokenizer.model_max_length if available + return config.max_length if "max_length" in keys or not hasattr(tokenizer, "model_max_length") else tokenizer.model_max_length + + @staticmethod + def deviceid(gpu): + """ + Translates input gpu argument into a device id. + + Args: + gpu: True/False if GPU should be enabled, also supports a device id/string/instance + + Returns: + device id + """ + + # Return if this is already a torch device + # pylint: disable=E1101 + if isinstance(gpu, torch.device): + return gpu + + # Always return -1 if gpu is None or an accelerator device is unavailable + if gpu is None or not Models.hasaccelerator(): + return -1 + + # Default to device 0 if gpu is True and not otherwise specified + if isinstance(gpu, bool): + return 0 if gpu else -1 + + # Return gpu as device id if gpu flag is an int + return int(gpu) + + @staticmethod + def device(deviceid): + """ + Gets a tensor device. + + Args: + deviceid: device id + + Returns: + tensor device + """ + + # Torch device + # pylint: disable=E1101 + return deviceid if isinstance(deviceid, torch.device) else torch.device(Models.reference(deviceid)) + + @staticmethod + def reference(deviceid): + """ + Gets a tensor device reference. + + Args: + deviceid: device id + + Returns: + device reference + """ + + return ( + deviceid + if isinstance(deviceid, str) + else ( + "cpu" + if deviceid < 0 + else f"cuda:{deviceid}" if torch.cuda.is_available() else "mps" if Models.hasmpsdevice() else Models.finddevice() + ) + ) + + @staticmethod + def acceleratorcount(): + """ + Gets the number of accelerator devices available. + + Returns: + number of accelerators available + """ + + return max(torch.cuda.device_count(), int(Models.hasaccelerator())) + + @staticmethod + def hasaccelerator(): + """ + Checks if there is an accelerator device available. + + Returns: + True if an accelerator device is available, False otherwise + """ + + return torch.cuda.is_available() or Models.hasmpsdevice() or bool(Models.finddevice()) + + @staticmethod + def hasmpsdevice(): + """ + Checks if there is a MPS device available. + + Returns: + True if a MPS device is available, False otherwise + """ + + return os.environ.get("PYTORCH_MPS_DISABLE") != "1" and torch.backends.mps.is_available() + + @staticmethod + def finddevice(): + """ + Attempts to find an alternative accelerator device. + + Returns: + name of first alternative accelerator available or None if not found + """ + + return next((device for device in ["xpu"] if hasattr(torch, device) and getattr(torch, device).is_available()), None) + + @staticmethod + def load(path, config=None, task="default", modelargs=None): + """ + Loads a machine learning model. Handles multiple model frameworks (ONNX, Transformers). + + Args: + path: path to model + config: path to model configuration + task: task name used to lookup model type + + Returns: + machine learning model + """ + + # Detect ONNX models + if isinstance(path, bytes) or (isinstance(path, str) and os.path.isfile(path)): + return OnnxModel(path, config) + + # Return path, if path isn't a string + if not isinstance(path, str): + return path + + # Transformer models + models = { + "default": transformers.AutoModel.from_pretrained, + "question-answering": transformers.AutoModelForQuestionAnswering.from_pretrained, + "summarization": transformers.AutoModelForSeq2SeqLM.from_pretrained, + "text2text-generation": transformers.AutoModelForSeq2SeqLM.from_pretrained, + "text-classification": transformers.AutoModelForSequenceClassification.from_pretrained, + "zero-shot-classification": transformers.AutoModelForSequenceClassification.from_pretrained, + } + + # Pass modelargs as keyword arguments + modelargs = modelargs if modelargs else {} + + # Load model for supported tasks. Return path for unsupported tasks. + return models[task](path, **modelargs) if task in models else path + + @staticmethod + def tokenizer(path, **kwargs): + """ + Loads a tokenizer from path. + + Args: + path: path to tokenizer + kwargs: optional additional keyword arguments + + Returns: + tokenizer + """ + + return transformers.AutoTokenizer.from_pretrained(path, **kwargs) if isinstance(path, str) else path + + @staticmethod + def task(path, **kwargs): + """ + Attempts to detect the model task from path. + + Args: + path: path to model + kwargs: optional additional keyword arguments + + Returns: + inferred model task + """ + + # Get model configuration + config = None + if isinstance(path, (list, tuple)) and hasattr(path[0], "config"): + config = path[0].config + elif isinstance(path, str): + config = transformers.AutoConfig.from_pretrained(path, **kwargs) + + # Image models + imagemodels = transformers.models.auto.modeling_auto.MODEL_FOR_IMAGE_TEXT_TO_TEXT_MAPPING_NAMES + + # Attempt to resolve task using configuration + task = None + if config: + architecture = config.architectures[0] if config.architectures else None + if architecture: + if architecture in imagemodels.values(): + task = "vision" + elif any(x for x in ["LMHead", "CausalLM"] if x in architecture): + task = "language-generation" + elif "QuestionAnswering" in architecture: + task = "question-answering" + elif "ConditionalGeneration" in architecture: + task = "sequence-sequence" + + return task diff --git a/src/python/txtai/models/onnx.py b/src/python/txtai/models/onnx.py new file mode 100644 index 0000000..eebc73f --- /dev/null +++ b/src/python/txtai/models/onnx.py @@ -0,0 +1,137 @@ +""" +ONNX module +""" + +# Conditional import +try: + import onnxruntime as ort + + ONNX_RUNTIME = True +except ImportError: + ONNX_RUNTIME = False + +from .registry import Registry + +# Core library imports +from ..util import Library + +library = Library() +np = library.numpy() +torch = library.torch() +transformers = library.transformers() +PretrainedConfig = library.config() +PreTrainedModel = library.model() + + +# pylint: disable=W0223 +class OnnxModel(PreTrainedModel): + """ + Provides a Transformers/PyTorch compatible interface for ONNX models. Handles casting inputs + and outputs with minimal to no copying of data. + """ + + def __init__(self, model, config=None): + """ + Creates a new OnnxModel. + + Args: + model: path to model or InferenceSession + config: path to model configuration + """ + + if not ONNX_RUNTIME: + raise ImportError('onnxruntime is not available - install "model" extra to enable') + + super().__init__(transformers.AutoConfig.from_pretrained(config) if config else OnnxConfig()) + + # Create ONNX session + self.model = ort.InferenceSession(model, ort.SessionOptions(), self.providers()) + + # Add references for this class to supported AutoModel classes + Registry.register(self) + + @property + def device(self): + """ + Returns model device id. + + Returns: + model device id + """ + + return -1 + + def providers(self): + """ + Returns a list of available and usable providers. + + Returns: + list of available and usable providers + """ + + # Create list of providers, prefer CUDA provider if available + # CUDA provider only available if GPU is available and onnxruntime-gpu installed + if torch.cuda.is_available() and "CUDAExecutionProvider" in ort.get_available_providers(): + return ["CUDAExecutionProvider", "CPUExecutionProvider"] + + # Default when CUDA provider isn't available + return ["CPUExecutionProvider"] + + def forward(self, **inputs): + """ + Runs inputs through an ONNX model and returns outputs. This method handles casting inputs + and outputs between torch tensors and numpy arrays as shared memory (no copy). + + Args: + inputs: model inputs + + Returns: + model outputs + """ + + inputs = self.parse(inputs) + + # Run inputs through ONNX model + results = self.model.run(None, inputs) + + # pylint: disable=E1101 + # Detect if logits is an output and return classifier output in that case + if any(x.name for x in self.model.get_outputs() if x.name == "logits"): + return transformers.modeling_outputs.SequenceClassifierOutput(logits=torch.from_numpy(np.array(results[0]))) + + return torch.from_numpy(np.array(results)) + + def parse(self, inputs): + """ + Parse model inputs and handle converting to ONNX compatible inputs. + + Args: + inputs: model inputs + + Returns: + ONNX compatible model inputs + """ + + features = {} + + # Select features from inputs + for key in ["input_ids", "attention_mask", "token_type_ids"]: + if key in inputs: + value = inputs[key] + + # Cast torch tensors to numpy + if hasattr(value, "cpu"): + value = value.cpu().numpy() + + # Cast to numpy array if not already one + # Only add valid model inputs + if any(x.name == key for x in self.model.get_inputs()): + features[key] = np.asarray(value) + + return features + + +class OnnxConfig(PretrainedConfig): + """ + Configuration for ONNX models. + """ diff --git a/src/python/txtai/models/pooling/__init__.py b/src/python/txtai/models/pooling/__init__.py new file mode 100644 index 0000000..36fbab8 --- /dev/null +++ b/src/python/txtai/models/pooling/__init__.py @@ -0,0 +1,10 @@ +""" +Pooling imports +""" + +from .base import Pooling +from .cls import ClsPooling +from .factory import PoolingFactory +from .last import LastPooling +from .late import LatePooling +from .mean import MeanPooling diff --git a/src/python/txtai/models/pooling/base.py b/src/python/txtai/models/pooling/base.py new file mode 100644 index 0000000..d69c96d --- /dev/null +++ b/src/python/txtai/models/pooling/base.py @@ -0,0 +1,202 @@ +""" +Pooling module +""" + +import json + +from ..models import Models + +# Core library imports +from ...util import Download, DownloadError, Library + +library = Library() +np = library.numpy() +torch = library.torch() +Module = library.module() + + +class Pooling(Module): + """ + Builds pooled vectors usings outputs from a transformers model. + """ + + def __init__(self, path, device, tokenizer=None, maxlength=None, loadprompts=None, modelargs=None): + """ + Creates a new Pooling model. + + Args: + path: path to model, accepts Hugging Face model hub id or local path + device: tensor device id + tokenizer: optional path to tokenizer + maxlength: max sequence length + loadprompts: whether instruction prompts should be loaded + modelargs: additional model arguments + """ + + super().__init__() + + self.model = Models.load(path, modelargs=modelargs) + self.tokenizer = Models.tokenizer(tokenizer if tokenizer else path) + self.device = Models.device(device) + + # Detect unbounded tokenizer typically found in older models + Models.checklength(self.model, self.tokenizer) + + # Set max length + self.maxlength = maxlength if maxlength else self.tokenizer.model_max_length if self.tokenizer.model_max_length != int(1e30) else None + + # Load stored prompts + self.prompts = self.loadprompts(path) if loadprompts else None + + # Move to device + self.to(self.device) + + def encode(self, documents, batch=32, category=None): + """ + Builds an array of pooled embeddings for documents. + + Args: + documents: list of documents used to build embeddings + batch: model batch size + category: embeddings category (query or data) + + Returns: + pooled embeddings + """ + + # Split documents into batches and process + results = [] + + # Apply pre encoding transformation logic + documents = self.preencode(documents, category) + + # Sort document indices from largest to smallest to enable efficient batching + # This performance tweak matches logic in sentence-transformers + lengths = np.argsort([-len(x) if x else 0 for x in documents]) + documents = [documents[x] for x in lengths] + + for chunk in self.chunk(documents, batch): + # Tokenize input + inputs = self.tokenizer(chunk, padding=True, truncation="longest_first", return_tensors="pt", max_length=self.maxlength) + + # Move inputs to device + inputs = inputs.to(self.device) + + # Run inputs through model + with torch.no_grad(): + outputs = self.forward(**inputs) + + # Add batch result + results.extend(outputs.cpu().to(torch.float32).numpy()) + + # Apply post encoding transformation logic + results = self.postencode(results, category) + + # Restore original order and return array + return np.asarray([results[x] for x in np.argsort(lengths)]) + + def chunk(self, texts, size): + """ + Splits texts into separate batch sizes specified by size. + + Args: + texts: text elements + size: batch size + + Returns: + list of evenly sized batches with the last batch having the remaining elements + """ + + return [texts[x : x + size] for x in range(0, len(texts), size)] + + def forward(self, **inputs): + """ + Runs inputs through transformers model and returns outputs. + + Args: + inputs: model inputs + + Returns: + model outputs + """ + + return self.model(**inputs)[0] + + # pylint: disable=W0613 + def preencode(self, documents, category): + """ + Applies pre encoding transformation logic. + + Args: + documents: list of documents used to build embeddings + category: embeddings category (query or data) + """ + + # Prepend prompt + prompt = self.prompts.get(category) if self.prompts else None + if prompt: + documents = [f"{prompt}{x}" if isinstance(x, str) else x for x in documents] + + return documents + + # pylint: disable=W0613 + def postencode(self, results, category): + """ + Applies post encoding transformation logic. + + Args: + results: list of results + category: embeddings category (query or data) + + Returns: + results with transformation logic applied + """ + + return results + + def load(self, path, name): + """ + Loads a JSON config file from the Hugging Face Hub. + + Args: + path: model path + name: file to load + + Returns: + config + """ + + # Download file and parse JSON + config = None + try: + path = Download()(path, name) + if path: + with open(path, encoding="utf-8") as f: + config = json.load(f) + + # Ignore this error - invalid repo or directory + except DownloadError: + pass + + return config + + def loadprompts(self, path): + """ + Loads prompts from a sentence transformers configuration file. + + Args: + path: model path + + Returns: + prompts dictionary, if available + """ + + prompts = None + config = self.load(path, "config_sentence_transformers.json") + if config: + # Copy document prompt to data + prompts = config.get("prompts") + if prompts and "document" in prompts: + prompts["data"] = prompts["document"] + + return prompts diff --git a/src/python/txtai/models/pooling/cls.py b/src/python/txtai/models/pooling/cls.py new file mode 100644 index 0000000..41f5742 --- /dev/null +++ b/src/python/txtai/models/pooling/cls.py @@ -0,0 +1,28 @@ +""" +CLS module +""" + +from .base import Pooling + + +class ClsPooling(Pooling): + """ + Builds CLS pooled vectors using outputs from a transformers model. + """ + + def forward(self, **inputs): + """ + Runs CLS pooling on token embeddings. + + Args: + inputs: model inputs + + Returns: + CLS pooled embeddings using output token embeddings (i.e. last hidden state) + """ + + # Run through transformers model + tokens = super().forward(**inputs) + + # CLS token pooling + return tokens[:, 0] diff --git a/src/python/txtai/models/pooling/factory.py b/src/python/txtai/models/pooling/factory.py new file mode 100644 index 0000000..0e635e7 --- /dev/null +++ b/src/python/txtai/models/pooling/factory.py @@ -0,0 +1,152 @@ +""" +Factory module +""" + +import json +import os + +from .base import Pooling +from .cls import ClsPooling +from .last import LastPooling +from .late import LatePooling +from .mean import MeanPooling + +from ...util import Download, DownloadError + + +class PoolingFactory: + """ + Method to create pooling models. + """ + + @staticmethod + def create(config): + """ + Create a Pooling model. + + Args: + config: pooling configuration + + Returns: + Pooling + """ + + # Unpack parameters + method, path, device, tokenizer, maxlength, loadprompts, modelargs = [ + config.get(x) for x in ["method", "path", "device", "tokenizer", "maxlength", "loadprompts", "modelargs"] + ] + + # Derive maxlength, if applicable + maxlength = PoolingFactory.maxlength(path) if isinstance(maxlength, bool) and maxlength else maxlength + + # Default pooling returns hidden state + if isinstance(path, bytes) or (isinstance(path, str) and os.path.isfile(path)) or method == "pooling": + return Pooling(path, device, tokenizer, maxlength, loadprompts, modelargs) + + # Derive pooling method if it's not specified and path is a string + if (not method or method not in ("clspooling", "meanpooling", "lastpooling", "latepooling")) and isinstance(path, str): + method = PoolingFactory.method(path) + + # Check for cls pooling + if method == "clspooling": + return ClsPooling(path, device, tokenizer, maxlength, loadprompts, modelargs) + + # Check for last pooling + if method == "lastpooling": + return LastPooling(path, device, tokenizer, maxlength, loadprompts, modelargs) + + # Check for late pooling + if method == "latepooling": + return LatePooling(path, device, tokenizer, maxlength, loadprompts, modelargs) + + # Default to mean pooling + return MeanPooling(path, device, tokenizer, maxlength, loadprompts, modelargs) + + @staticmethod + def method(path): + """ + Determines the pooling method using the sentence transformers pooling config. + + Args: + path: model path + + Returns: + pooling method + """ + + # Default method + method = "meanpooling" + + # Load 1_Pooling/config.json file + config = PoolingFactory.load(path, "1_Pooling/config.json") + + # Set to CLS pooling if it's enabled and mean pooling is disabled + if config and config.get("pooling_mode_cls_token") and not config["pooling_mode_mean_tokens"]: + method = "clspooling" + + # Set to last token pooling if it's enabled and mean pooling is disabled + if config and config.get("pooling_mode_lasttoken") and not config["pooling_mode_mean_tokens"]: + method = "lastpooling" + + # Check for late interaction pooling + if not config: + # Load 1_Dense/config.json + config = PoolingFactory.load(path, "1_Dense/config.json") + if config: + method = "latepooling" + + # Load config.json and check architecture + else: + config = PoolingFactory.load(path, "config.json") + if config and "HF_ColBERT" in config.get("architectures", []): + method = "latepooling" + + return method + + @staticmethod + def maxlength(path): + """ + Reads the max_seq_length parameter from sentence transformers config. + + Args: + path: model path + + Returns: + max sequence length + """ + + # Default length is unset + maxlength = None + + # Read max_seq_length from sentence_bert_config.json + config = PoolingFactory.load(path, "sentence_bert_config.json") + maxlength = config.get("max_seq_length") if config else maxlength + + return maxlength + + @staticmethod + def load(path, name): + """ + Loads a JSON config file from the Hugging Face Hub. + + Args: + path: model path + name: file to load + + Returns: + config + """ + + # Download file and parse JSON + config = None + try: + path = Download()(path, name) + if path: + with open(path, encoding="utf-8") as f: + config = json.load(f) + + # Ignore this error - invalid repo or directory + except DownloadError: + pass + + return config diff --git a/src/python/txtai/models/pooling/last.py b/src/python/txtai/models/pooling/last.py new file mode 100644 index 0000000..9c3baa5 --- /dev/null +++ b/src/python/txtai/models/pooling/last.py @@ -0,0 +1,52 @@ +""" +Last module +""" + +from .base import Pooling + +# Core library imports +from ...util import Library + +torch = Library().torch() + + +class LastPooling(Pooling): + """ + Builds last token pooled vectors usings outputs from a transformers model. + """ + + def forward(self, **inputs): + """ + Runs last pooling on token embeddings taking the input mask into account. + + Args: + inputs: model inputs + + Returns: + last pooled embeddings using output token embeddings (i.e. last hidden state) + """ + + # Run through transformers model + tokens = super().forward(**inputs) + mask = inputs["attention_mask"] + + # Last pooling logic from Sentence Transformers + _, sequence, dimensions = tokens.shape + + # Avoid tracing the argmax with int64 input that can not be handled by ONNX Runtime + mask = mask.to(torch.int32) if torch.jit.is_tracing() else mask + + # Use flip and max() to get the last index of 1 in the attention mask + values, indices = mask.flip(1).max(1) + indices = torch.where(values == 0, sequence - 1, indices) + gather = sequence - indices - 1 + + # Turn indices from shape [bs] --> [bs, 1, hidden_dim] + gather = gather.unsqueeze(-1).repeat(1, dimensions) + gather = gather.unsqueeze(1) + + # Expand mask to ignore 0 index attention masks + mask = mask.unsqueeze(-1).expand(tokens.size()).to(tokens.dtype) + + # Return last pooled embeddings + return torch.gather(tokens * mask, 1, gather).squeeze(dim=1) diff --git a/src/python/txtai/models/pooling/late.py b/src/python/txtai/models/pooling/late.py new file mode 100644 index 0000000..c46d7fa --- /dev/null +++ b/src/python/txtai/models/pooling/late.py @@ -0,0 +1,203 @@ +""" +Late module +""" + +from .base import Pooling +from .muvera import Muvera + +# Core library imports +from ...util import Download, Library + +library = Library() +np = library.numpy() +safetensors = library.safetensors() +torch = library.torch() +Module = library.module() + + +class LatePooling(Pooling): + """ + Builds late pooled vectors using outputs from a transformers model. + """ + + def __init__(self, path, device, tokenizer=None, maxlength=None, loadprompts=None, modelargs=None): + # Check if fixed dimensional encoder is enabled + modelargs = modelargs.copy() if modelargs else {} + muvera = modelargs.pop("muvera", {}) + self.encoder = Muvera(**muvera) if muvera is not None else None + + # Call parent initialization + super().__init__(path, device, tokenizer, maxlength, loadprompts, modelargs) + + # Get linear weights paths + config = self.load(path, "modules.json") + if config: + # PyLate weights format + models = [f"{x['path']}/model.safetensors" for x in config if x["path"].endswith("_Dense")] + else: + # Stanford weights format + models = ["model.safetensors"] + + # Read model settings + self.qprefix, self.qlength, self.dprefix, self.dlength = self.settings(path, config) + + # Load linear model + self.linear = self.loadlinear(path, models) + + def forward(self, **inputs): + """ + Runs late pooling on token embeddings. + + Args: + inputs: model inputs + + Returns: + Late pooled embeddings using output token embeddings (i.e. last hidden state) + """ + + # Run through transformers model + tokens = super().forward(**inputs) + + # Run through final linear layer and return + return self.linear(tokens) + + def preencode(self, documents, category): + """ + Apply prefixes and lengths to data. + + Args: + documents: list of documents used to build embeddings + category: embeddings category (query or data) + """ + + results = [] + + # Apply prefix + for text in documents: + prefix = self.qprefix if category == "query" else self.dprefix + if prefix: + text = f"{prefix}{text}" + + results.append(text) + + # Set maxlength + maxlength = self.qlength if category == "query" else self.dlength + if maxlength: + self.maxlength = maxlength + + return results + + def postencode(self, results, category): + """ + Normalizes and pads results. + + Args: + results: input results + + Returns: + normalized results with padding + """ + + length = 0 + for vectors in results: + # Get max length + if vectors.shape[0] > length: + length = vectors.shape[0] + + # Normalize vectors + vectors /= np.linalg.norm(vectors, axis=1)[:, np.newaxis] + + # Pad values + data = [] + for vectors in results: + data.append(np.pad(vectors, [(0, length - vectors.shape[0]), (0, 0)])) + + # Build NumPy array + data = np.asarray(data) + + # Apply fixed dimesional encoder, if necessary + return self.encoder(data, category) if self.encoder else data + + def settings(self, path, config): + """ + Reads model settings. + + Args: + path: model path + config: PyLate model format if provided, otherwise read from Stanford format + """ + + if config: + # PyLate format + config = self.load(path, "config_sentence_transformers.json") + params = ["query_prefix", "query_length", "document_prefix", "document_length"] + else: + # Stanford format + config = self.load(path, "artifact.metadata") + params = ["query_token_id", "query_maxlen", "doc_token_id", "doc_maxlen"] + + return [config.get(p) for p in params] + + def loadlinear(self, path, models): + """ + Loads linear model. + + Args: + path: model path + models: list of paths to each dense model + + Returns: + linear model + """ + + # Load dense layers as a sequential model + layers = [] + for model in models: + model = Download()(path, model) + with safetensors.safe_open(filename=model, framework="pt") as f: + dense = [] + for name in ["linear.weight", "residual.weight"]: + if name in f.keys(): + weights = f.get_tensor(name) + + # Load weights into linear layer + model = torch.nn.Linear(weights.shape[1], weights.shape[0], bias=False, device=self.device, dtype=weights.dtype) + with torch.no_grad(): + model.weight.copy_(weights) + + dense.append(model) + + layers.append(Dense(*dense)) + + return torch.nn.Sequential(*layers) + + +class Dense(Module): + """ + Dense layer. Supports multiple linear layers that sum into a final answer. + """ + + def __init__(self, *modules): + """ + Create a Dense layer. + + Args: + modules: list of modules to sum outputs + """ + + super().__init__() + self.layers = torch.nn.ModuleList(modules) + + def forward(self, x): + """ + Sums the outputs of each module for the input. + + Args: + x: input + + Returns: + sum of the outputs of each layer + """ + + # Compute sum of all module outputs + return sum(layer(x) for layer in self.layers) diff --git a/src/python/txtai/models/pooling/mean.py b/src/python/txtai/models/pooling/mean.py new file mode 100644 index 0000000..1b19381 --- /dev/null +++ b/src/python/txtai/models/pooling/mean.py @@ -0,0 +1,36 @@ +""" +Mean module +""" + +from .base import Pooling + +# Core library imports +from ...util import Library + +torch = Library().torch() + + +class MeanPooling(Pooling): + """ + Builds mean pooled vectors usings outputs from a transformers model. + """ + + def forward(self, **inputs): + """ + Runs mean pooling on token embeddings taking the input mask into account. + + Args: + inputs: model inputs + + Returns: + mean pooled embeddings using output token embeddings (i.e. last hidden state) + """ + + # Run through transformers model + tokens = super().forward(**inputs) + mask = inputs["attention_mask"] + + # Mean pooling + # pylint: disable=E1101 + mask = mask.unsqueeze(-1).expand(tokens.size()).float() + return torch.sum(tokens * mask, 1) / torch.clamp(mask.sum(1), min=1e-9) diff --git a/src/python/txtai/models/pooling/muvera.py b/src/python/txtai/models/pooling/muvera.py new file mode 100644 index 0000000..e8a0aff --- /dev/null +++ b/src/python/txtai/models/pooling/muvera.py @@ -0,0 +1,167 @@ +""" +Muvera module +""" + +# Core library imports +from ...util import Library + +np = Library().numpy() + + +class Muvera: + """ + Implements the MUVERA (Multi-Vector Retrieval via Fixed Dimensional Encodings) algorithm. This reduces late interaction multi-vector + outputs to a single fixed vector. + + The size of the output vectors are set using the following parameters + + output dimensions = repetitions * 2^hashes * projected + + For example, the default parameters create vectors with the following output dimensions. + + output dimensions = 20 * 2^5 * 16 = 10240 + + This code is based on the following: + - Paper: https://arxiv.org/abs/2405.19504 + - GitHub: https://github.com/google/graph-mining/tree/main/sketching/point_cloud + - Python port of the original C++ code: https://github.com/sigridjineth/muvera-py + """ + + def __init__(self, repetitions=20, hashes=5, projection=16, seed=42): + """ + Creates a Muvera instance. + + Args: + repetitions: number of iterations + hashes: number of simhash partitions as 2^hashes + projection: dimensionality reduction, uses an identity projection when set to None + seed: random seed + """ + + # Number of repetitions + self.repetitions = repetitions + + # Number of simhash projections + self.hashes = hashes + + # Optional number of projected dimensions + self.projection = projection + + # Seed + self.seed = seed + + def __call__(self, data, category): + """ + Transforms a list of multi-vector collections into single fixed vector outputs. + + Args: + data: array of multi-vector vectors + category: embeddings category (query or data) + """ + + # Get stats + dimension, length = data[0].shape[1], len(data) + + # Determine projection dimension + identity = not self.projection + projection = dimension if identity else self.projection + + # Number of simhash partitions + partitions = 2**self.hashes + + # Document tracking + lengths = np.array([len(doc) for doc in data], dtype=np.int32) + total = np.sum(lengths) + documents = np.repeat(np.arange(length), lengths) + + # Stack all vectors + points = np.vstack(data).astype(np.float32) + + # Output vectors + size = self.repetitions * partitions * projection + vectors = np.zeros((length, size), dtype=np.float32) + + # Process each repetition + for number in range(self.repetitions): + seed = self.seed + number + + # Calculate the simhash + sketches = points @ self.random(dimension, self.hashes, seed) + + # Dimensionality reduction, if necessary + projected = points if identity else (points @ self.reducer(dimension, projection, seed)) + + # Get partition indices + bits = (sketches > 0).astype(np.uint32) + indices = np.zeros(total, dtype=np.uint32) + + # Calculate vector indices + for x in range(self.hashes): + indices = (indices << 1) + (bits[:, x] ^ (indices & 1)) + + # Initialize storage + fdesum = np.zeros((length * partitions * projection,), dtype=np.float32) + counts = np.zeros((length, partitions), dtype=np.int32) + + # Count vectors per partition per document + np.add.at(counts, (documents, indices), 1) + + # Aggregate vectors using flattened indexing for efficiency + part = documents * partitions + indices + base = part * projection + + for d in range(projection): + flat = base + d + np.add.at(fdesum, flat, projected[:, d]) + + # Reshape for easier manipulation + # pylint: disable=E1121 + fdesum = fdesum.reshape(length, partitions, projection) + + # Convert sums to averages for data category + if category == "data": + # Safe division (avoid divide by zero) + counts = counts[:, :, np.newaxis] + np.divide(fdesum, counts, out=fdesum, where=counts > 0) + + # Save results + start = number * partitions * projection + vectors[:, start : start + partitions * projection] = fdesum.reshape(length, -1) + + return vectors + + def random(self, dimension, projection, seed): + """ + Generates a random matrix for simhash projections. + + Args: + dimensions: number of dimensions for input vectors + projections: number of projection dimensions + seed: random seed + + Returns: + random matrix for simhash projections + """ + + rng = np.random.default_rng(seed) + return rng.normal(loc=0.0, scale=1.0, size=(dimension, projection)).astype(np.float32) + + def reducer(self, dimension, projection, seed): + """ + Generates a random matrix for dimensionality reduction using the AMS sketch algorithm. + + Args: + dimension: number of input dimensions + projected: number of dimensions to project inputs to + + Returns: + Dimensionality reduced matrix + """ + + rng = np.random.default_rng(seed) + out = np.zeros((dimension, projection), dtype=np.float32) + indices = rng.integers(0, projection, size=dimension) + signs = rng.choice([-1.0, 1.0], size=dimension) + out[np.arange(dimension), indices] = signs + + return out diff --git a/src/python/txtai/models/registry.py b/src/python/txtai/models/registry.py new file mode 100644 index 0000000..6378348 --- /dev/null +++ b/src/python/txtai/models/registry.py @@ -0,0 +1,40 @@ +""" +Registry module +""" + +# Core library imports +from ..util import Library + +transformers = Library().transformers() + + +class Registry: + """ + Methods to register models and fully support pipelines. + """ + + @staticmethod + def register(model, config=None): + """ + Registers a model with auto model and tokenizer configuration to fully support pipelines. + + Args: + model: model to register + config: config class name + """ + + # Default config class to model class if not provided + config = config if config else model.__class__ + + # Default model config_class if empty + if hasattr(model.__class__, "config_class") and not model.__class__.config_class: + model.__class__.config_class = config + + # Add references for this class to supported AutoModel classes + for mapping in [transformers.AutoModel, transformers.AutoModelForQuestionAnswering, transformers.AutoModelForSequenceClassification]: + mapping.register(config, model.__class__) + + # Add references for this class to support pipeline AutoTokenizers + mappings = transformers.models.auto.tokenization_auto.TOKENIZER_MAPPING + if hasattr(model, "config") and type(model.config) not in mappings: + mappings.register(type(model.config), type(model.config).__name__) diff --git a/src/python/txtai/models/tokendetection.py b/src/python/txtai/models/tokendetection.py new file mode 100644 index 0000000..0861794 --- /dev/null +++ b/src/python/txtai/models/tokendetection.py @@ -0,0 +1,123 @@ +""" +Token Detection module +""" + +import inspect +import os + +# Core library imports +from ..util import Library + +library = Library() +torch = library.torch() +PreTrainedModel = library.model() + + +class TokenDetection(PreTrainedModel): + """ + Runs the replaced token detection training objective. This method was first proposed by the ELECTRA model. + The method consists of a masked language model generator feeding data to a discriminator that determines + which of the tokens are incorrect. More on this training objective can be found in the ELECTRA paper. + """ + + def __init__(self, generator, discriminator, tokenizer, weight=50.0): + """ + Creates a new TokenDetection class. + + Args: + generator: Generator model, must be a masked language model + discriminator: Discriminator model, must be a model that can detect replaced tokens. Any model can + can be customized for this task. See ElectraForPretraining for more. + """ + + # Initialize model with discriminator config + super().__init__(discriminator.config) + + self.generator = generator + self.discriminator = discriminator + + # Tokenizer to save with generator and discriminator + self.tokenizer = tokenizer + + # Discriminator weight + self.weight = weight + + # Share embeddings if both models are the same type + # Embeddings must be same size + if self.generator.config.model_type == self.discriminator.config.model_type: + self.discriminator.set_input_embeddings(self.generator.get_input_embeddings()) + + # Set attention mask present flags + self.gattention = "attention_mask" in inspect.signature(self.generator.forward).parameters + self.dattention = "attention_mask" in inspect.signature(self.discriminator.forward).parameters + + # pylint: disable=E1101 + def forward(self, input_ids=None, labels=None, attention_mask=None, token_type_ids=None): + """ + Runs a forward pass through the model. This method runs the masked language model then randomly samples + the generated tokens and builds a binary classification problem for the discriminator (detecting if each token is correct). + + Args: + input_ids: token ids + labels: token labels + attention_mask: attention mask + token_type_ids: segment token indices + + Returns: + (loss, generator outputs, discriminator outputs, discriminator labels) + """ + + # Copy input ids + dinputs = input_ids.clone() + + # Run inputs through masked language model + inputs = {"attention_mask": attention_mask} if self.gattention else {} + goutputs = self.generator(input_ids, labels=labels, token_type_ids=token_type_ids, **inputs) + + # Get predictions + preds = torch.softmax(goutputs[1], dim=-1) + preds = preds.view(-1, self.config.vocab_size) + + tokens = torch.multinomial(preds, 1).view(-1) + tokens = tokens.view(dinputs.shape[0], -1) + + # Labels have a -100 value to ignore loss from unchanged tokens + mask = labels.ne(-100) + + # Replace the masked out tokens of the input with the generator predictions + dinputs[mask] = tokens[mask] + + # Turn mask into new target labels - 1 (True) for corrupted, 0 otherwise. + # If the prediction was correct, mark it as uncorrupted. + correct = tokens == labels + dlabels = mask.long() + dlabels[correct] = 0 + + # Run token classification, predict whether each token was corrupted + inputs = {"attention_mask": attention_mask} if self.dattention else {} + doutputs = self.discriminator(dinputs, labels=dlabels, token_type_ids=token_type_ids, **inputs) + + # Compute combined loss + loss = goutputs[0] + self.weight * doutputs[0] + return loss, goutputs[1], doutputs[1], dlabels + + # pylint: disable=W0613 + def save_pretrained(self, output, state_dict=None, **kwargs): + """ + Saves current model to output directory. + + Args: + output: output directory + state_dict: model state + kwargs: additional keyword arguments + """ + + # Save generator tokenizer and model + gpath = os.path.join(output, "generator") + self.tokenizer.save_pretrained(gpath) + self.generator.save_pretrained(gpath) + + # Save discriminator tokenizer and model + dpath = os.path.join(output, "discriminator") + self.tokenizer.save_pretrained(dpath) + self.discriminator.save_pretrained(dpath) diff --git a/src/python/txtai/pipeline/__init__.py b/src/python/txtai/pipeline/__init__.py new file mode 100644 index 0000000..5c658be --- /dev/null +++ b/src/python/txtai/pipeline/__init__.py @@ -0,0 +1,17 @@ +""" +Pipeline imports +""" + +from .audio import * +from .base import Pipeline +from .data import * +from .factory import PipelineFactory +from .hfmodel import HFModel +from .hfpipeline import HFPipeline +from .image import * +from .llm import * +from .llm import RAG as Extractor +from .nop import Nop +from .text import * +from .tensors import Tensors +from .train import * diff --git a/src/python/txtai/pipeline/audio/__init__.py b/src/python/txtai/pipeline/audio/__init__.py new file mode 100644 index 0000000..e3a1d8e --- /dev/null +++ b/src/python/txtai/pipeline/audio/__init__.py @@ -0,0 +1,11 @@ +""" +Audio imports +""" + +from .audiomixer import AudioMixer +from .audiostream import AudioStream +from .microphone import Microphone +from .signal import Signal +from .texttoaudio import TextToAudio +from .texttospeech import TextToSpeech +from .transcription import Transcription diff --git a/src/python/txtai/pipeline/audio/audiomixer.py b/src/python/txtai/pipeline/audio/audiomixer.py new file mode 100644 index 0000000..3578f9e --- /dev/null +++ b/src/python/txtai/pipeline/audio/audiomixer.py @@ -0,0 +1,58 @@ +""" +AudioMixer module +""" + +from ..base import Pipeline +from .signal import Signal, SCIPY + + +class AudioMixer(Pipeline): + """ + Mixes multiple audio streams into a single stream. + """ + + def __init__(self, rate=None): + """ + Creates an AudioMixer pipeline. + + Args: + rate: optional target sample rate, otherwise uses input target rate with each audio segment + """ + + if not SCIPY: + raise ImportError('AudioMixer pipeline is not available - install "pipeline" extra to enable.') + + # Target sample rate + self.rate = rate + + def __call__(self, segment, scale1=1, scale2=1): + """ + Mixes multiple audio streams into a single stream. + + Args: + segment: ((audio1, sample rate), (audio2, sample rate))|list + scale1: optional scaling factor for segment1 + scale2: optional scaling factor for segment2 + + Returns: + list of (audio, sample rate) + """ + + # Convert single element to list + segments = [segment] if isinstance(segment, tuple) else segment + + results = [] + for segment1, segment2 in segments: + audio1, rate1 = segment1 + audio2, rate2 = segment2 + + # Resample audio, as necessary + target = self.rate if self.rate else rate1 + audio1 = Signal.resample(audio1, rate1, target) + audio2 = Signal.resample(audio2, rate2, target) + + # Mix audio into single segment + results.append((Signal.mix(audio1, audio2, scale1, scale2), target)) + + # Return single element if single element passed in + return results[0] if isinstance(segment, tuple) else results diff --git a/src/python/txtai/pipeline/audio/audiostream.py b/src/python/txtai/pipeline/audio/audiostream.py new file mode 100644 index 0000000..e0daf5a --- /dev/null +++ b/src/python/txtai/pipeline/audio/audiostream.py @@ -0,0 +1,94 @@ +""" +AudioStream module +""" + +from queue import Queue +from threading import Thread + +# Conditional import +try: + import sounddevice as sd + + from .signal import Signal, SCIPY + + AUDIOSTREAM = SCIPY +except (ImportError, OSError): + AUDIOSTREAM = False + +from ..base import Pipeline + + +class AudioStream(Pipeline): + """ + Threaded pipeline that streams audio segments to an output audio device. This pipeline is designed + to run on local machines given that it requires access to write to an output device. + """ + + # End of stream message + COMPLETE = (1, None) + + def __init__(self, rate=None): + """ + Creates an AudioStream pipeline. + + Args: + rate: optional target sample rate, otherwise uses input target rate with each audio segment + """ + + if not AUDIOSTREAM: + raise ImportError( + ( + 'AudioStream pipeline is not available - install "pipeline" extra to enable. ' + "Also check that the portaudio system library is available." + ) + ) + + # Target sample rate + self.rate = rate + + self.queue = Queue() + self.thread = Thread(target=self.play) + self.thread.start() + + def __call__(self, segment): + """ + Queues audio segments for the audio player. + + Args: + segment: (audio, sample rate)|list + + Returns: + segment + """ + + # Convert single element to list + segments = [segment] if isinstance(segment, tuple) else segment + + for x in segments: + self.queue.put(x) + + # Return single element if single element passed in + return segments[0] if isinstance(segment, tuple) else segments + + def wait(self): + """ + Waits for all input audio segments to be played. + """ + + self.thread.join() + + def play(self): + """ + Reads audio segments from queue. This method runs in a separate non-blocking thread. + """ + + audio, rate = self.queue.get() + while not isinstance(audio, int) or (audio, rate) != AudioStream.COMPLETE: + # Resample to target sample rate, if necessary + audio, rate = (Signal.resample(audio, rate, self.rate), self.rate) if self.rate else (audio, rate) + + # Play audio segment + sd.play(audio, rate, blocking=True) + + # Get next segment + audio, rate = self.queue.get() diff --git a/src/python/txtai/pipeline/audio/microphone.py b/src/python/txtai/pipeline/audio/microphone.py new file mode 100644 index 0000000..6c396b1 --- /dev/null +++ b/src/python/txtai/pipeline/audio/microphone.py @@ -0,0 +1,247 @@ +""" +Microphone module +""" + +import logging + +# Conditional import +try: + import sounddevice as sd + import webrtcvad + + from scipy.signal import butter, sosfilt + + from .signal import Signal, SCIPY + + MICROPHONE = SCIPY +except (ImportError, OSError): + MICROPHONE = False + +from ..base import Pipeline + +# Core library imports +from ...util import Library + +np = Library().numpy() + +# Logging configuration +logger = logging.getLogger(__name__) + + +class Microphone(Pipeline): + """ + Reads input speech from a microphone device. This pipeline is designed to run on local machines given + that it requires access to read from an input device. + """ + + def __init__(self, rate=16000, vadmode=3, vadframe=20, vadthreshold=0.6, voicestart=300, voiceend=3400, active=5, pause=8): + """ + Creates a new Microphone pipeline. + + Args: + rate: sample rate to record audio in, defaults to 16000 (16 kHz) + vadmode: aggressiveness of the voice activity detector (1 - 3), defaults to 3, which is the most aggressive filter + vadframe: voice activity detector frame size in ms, defaults to 20 + vadthreshold: percentage of frames (0.0 - 1.0) that must be voice to be considered speech, defaults to 0.6 + voicestart: starting frequency to use for voice filtering, defaults to 300 + voiceend: ending frequency to use for voice filtering, defaults to 3400 + active: minimum number of active speech chunks to require before considering this speech, defaults to 5 + pause: number of non-speech chunks to keep before considering speech complete, defaults to 8 + """ + + if not MICROPHONE: + raise ImportError( + ( + 'Microphone pipeline is not available - install "pipeline" extra to enable. ' + "Also check that the portaudio system library is available." + ) + ) + + # Sample rate + self.rate = rate + + # Voice activity detector + self.vad = webrtcvad.Vad(vadmode) + self.vadframe = vadframe + self.vadthreshold = vadthreshold + + # Voice spectrum + self.voicestart = voicestart + self.voiceend = voiceend + + # Audio chunks counts + self.active = active + self.pause = pause + + def __call__(self, device=None): + """ + Reads audio from an input device. + + Args: + device: optional input device id, otherwise uses system default + + Returns: + list of (audio, sample rate) + """ + + # Listen for audio + audio = self.listen(device[0] if isinstance(device, list) else device) + + # Return single element if single element passed in + return (audio, self.rate) if device is None or not isinstance(device, list) else [(audio, self.rate)] + + def listen(self, device): + """ + Listens for speech. Detected speech is converted to 32-bit floats for compatibility with + automatic speech recognition (ASR) pipelines. + + This method blocks until speech is detected. + + Args: + device: input device + + Returns: + audio + """ + + # Record in 100ms chunks + chunksize = self.rate // 10 + + # Open input stream + stream = sd.RawInputStream(device=device, samplerate=self.rate, channels=1, blocksize=chunksize, dtype=np.int16) + + # Start the input stream + stream.start() + + record, speech, nospeech, chunks = True, 0, 0, [] + while record: + # Read chunk + chunk, _ = stream.read(chunksize) + + # Detect speech using WebRTC VAD for audio chunk + detect = self.detect(chunk) + speech = speech + 1 if detect else speech + nospeech = 0 if detect else nospeech + 1 + + # Save chunk, if this is an active stream + if speech: + chunks.append(chunk) + + # Pause limit has been reached, check if this audio should be accepted + if nospeech >= self.pause: + logger.debug("Audio detected and being analyzed") + if speech >= self.active and self.isspeech(chunks[:-nospeech]): + # Disable recording + record = False + else: + # Reset parameters and keep recording + logger.debug("Speech not detected") + speech, nospeech, chunks = 0, 0, [] + + # Stop the input stream + stream.stop() + + # Convert to float32 and return + audio = np.frombuffer(b"".join(chunks), np.int16) + return Signal.float32(audio) + + def isspeech(self, chunks): + """ + Runs an ensemble of Voice Activity Detection (VAD) methods. Returns true if speech is + detected in the input audio chunks. + + Args: + chunks: input audio chunks as byte buffers + + Returns: + True if speech is detected, False otherwise + """ + + # Convert to NumPy array for processing + audio = np.frombuffer(b"".join(chunks), dtype=np.int16) + + # Ensemble of: + # - WebRTC VAD with a human voice range butterworth bandpass filter applied to the signal + # - FFT applied to detect the energy ratio for human voice range vs total range + return self.detectband(audio) and self.detectenergy(audio) + + def detect(self, buffer): + """ + Detect speech using the WebRTC Voice Activity Detector (VAD). + + Args: + buffer: input audio buffer frame as bytes + + Returns: + True if the number of audio frames with audio pass vadthreshold, False otherwise + """ + + n = int(self.rate * (self.vadframe / 1000.0) * 2) + offset = 0 + + detects = [] + while offset + n <= len(buffer): + detects.append(1 if self.vad.is_speech(buffer[offset : offset + n], self.rate) else 0) + offset += n + + # Calculate detection ratio and return + ratio = sum(detects) / len(detects) if detects else 0 + if ratio > 0: + logger.debug("DETECT %.4f", ratio) + + return ratio >= self.vadthreshold + + def detectband(self, audio): + """ + Detects speech using audio data filtered through a butterworth band filter + with the human voice range. + + Args: + audio: input audio data as an NumPy array + + Returns: + True if speech is detected, False otherwise + """ + + # Upsample to float32 + audio = Signal.float32(audio) + + # Human voice frequency range + low = self.voicestart / (0.5 * self.rate) + high = self.voiceend / (0.5 * self.rate) + + # Low and high pass filter using human voice range + sos = butter(5, Wn=[low, high], btype="band", output="sos") + audio = sosfilt(sos, audio) + + # Scale back to int16 + audio = Signal.int16(audio) + + # Pass filtered signal to WebRTC VAD + return self.detect(audio.tobytes()) + + def detectenergy(self, audio): + """ + Detects speech by comparing the signal energy of the human voice range + to the overall signal energy. + + Args: + audio: input audio data as an NumPy array + + Returns: + True if speech is detected, False otherwise + """ + + # Calculate signal energy + energyfreq = Signal.energy(audio, self.rate) + + # Sum speech energy + speechenergy = 0 + for f, e in energyfreq.items(): + if self.voicestart <= f <= self.voiceend: + speechenergy += e + + # Calculate ratio of speech energy to total energy and return + ratio = speechenergy / sum(energyfreq.values()) + logger.debug("SPEECH %.4f", ratio) + return ratio >= self.vadthreshold diff --git a/src/python/txtai/pipeline/audio/signal.py b/src/python/txtai/pipeline/audio/signal.py new file mode 100644 index 0000000..adda662 --- /dev/null +++ b/src/python/txtai/pipeline/audio/signal.py @@ -0,0 +1,189 @@ +""" +Signal module +""" + +# Conditional import +try: + from scipy import signal + from scipy.fft import rfft, rfftfreq + + SCIPY = True +except ImportError: + SCIPY = False + +# Core library imports +from ...util import Library + +np = Library().numpy() + + +class Signal: + """ + Utility methods for audio signal processing. + """ + + @staticmethod + def mono(audio): + """ + Convert stereo to mono audio. + + Args: + audio: audio data + + Returns: + audio data with a single channel + """ + + return audio.mean(axis=1) if len(audio.shape) > 1 else audio + + @staticmethod + def resample(audio, rate, target): + """ + Resample audio if the sample rate doesn't match the target sample rate. + + Args: + audio: audio data + rate: current sample rate + target: target sample rate + + Returns: + audio resampled if necessary or original audio + """ + + if rate != target: + # Transpose audio + audio = audio.T + + # Resample audio and tranpose back + samples = round(len(audio) * float(target) / rate) + audio = signal.resample(audio, samples).T + + return audio + + @staticmethod + def float32(audio): + """ + Converts an input NumPy array with 16-bit ints to 32-bit floats. + + Args: + audio: input audio array as 16-bit ints + + Returns: + audio array as 32-bit floats + """ + + i = np.iinfo(audio.dtype) + abs_max = 2 ** (i.bits - 1) + offset = i.min + abs_max + return (audio.astype(np.float32) - offset) / abs_max + + @staticmethod + def int16(audio): + """ + Converts an input NumPy array with 32-bit floats to 16-bit ints. + + Args: + audio: input audio array as 32-bit floats + + Returns: + audio array as 16-bit ints + """ + + i = np.iinfo(np.int16) + absmax = 2 ** (i.bits - 1) + offset = i.min + absmax + return (audio * absmax + offset).clip(i.min, i.max).astype(np.int16) + + @staticmethod + def mix(audio1, audio2, scale1=1, scale2=1): + """ + Mixes audio1 and audio 2 into a single output audio segment. + + Args: + audio1: audio segment 1 + audio2: audio segment 2 + scale1: scale factor for audio segment 1 + scale2: scale factor for audio segment 2 + """ + + # Reshape audio, as necessary + audio1 = audio1.reshape(1, -1) if len(audio1.shape) <= 1 else audio1 + audio2 = audio2.reshape(1, -1) if len(audio2.shape) <= 1 else audio2 + + # Scale audio + audio1 = audio1 * scale1 + audio2 = audio2 * scale2 + + # Make audio files the same length + large, small = (audio1, audio2) if audio1.shape[1] > audio2.shape[1] else (audio2, audio1) + small = np.tile(small, (large.shape[1] // small.shape[1]) + 1).take(axis=1, indices=range(0, large.shape[1])) + + # Mix audio together + return small + large + + @staticmethod + def energy(audio, rate): + """ + Calculates the signal energy for the input audio. Energy is defined as: + + Energy = 2 * Signal Amplitude + + Args: + audio: audio data + rate: sample rate + + Returns: + {frequency: energy at that frequency} + """ + + # Calculate signal frequency + frequency = rfftfreq(len(audio), 1.0 / rate) + frequency = frequency[1:] + + # Calculate signal energy using amplitude + energy = np.abs(rfft(audio)) + energy = energy[1:] + energy = energy**2 + + # Get energy for each frequency + energyfreq = {} + for x, freq in enumerate(frequency): + if abs(freq) not in energyfreq: + energyfreq[abs(freq)] = energy[x] * 2 + + return energyfreq + + @staticmethod + def trim(audio, rate, threshold=1, leading=True, trailing=True): + """ + Removes leading and trailing silence from audio data. + + Args: + audio: audio data + rate: sample rate + threshold: energy below this level will be considered silence, defaults to 1.0 + leading: trim leading silence, defaults to True + trailing: trim trailing silence, defauls to True + + Returns: + audio with silence removed + """ + + # Process in 20ms chunks + n, offset = int(rate * (20 / 1000.0) * 2), 0 + + chunks = [] + while offset + n <= len(audio): + # Calculate energy for chunk and detection result + chunk = audio[offset : offset + n] + energyfreq = Signal.energy(chunk, rate) + chunks.append((chunk, sum(energyfreq.values()) >= threshold)) + + offset += n + + # Find first and last active chunks + start = next((i for i, (_, active) in enumerate(chunks) if active), 0) if leading else 0 + end = (len(chunks) - next((i for i, (_, active) in enumerate(chunks[::-1]) if active), 0)) if trailing else len(chunks) + + # Concatenate active audio + return np.concatenate([chunk for chunk, _ in chunks[start:end]]) diff --git a/src/python/txtai/pipeline/audio/texttoaudio.py b/src/python/txtai/pipeline/audio/texttoaudio.py new file mode 100644 index 0000000..cfb700b --- /dev/null +++ b/src/python/txtai/pipeline/audio/texttoaudio.py @@ -0,0 +1,60 @@ +""" +TextToAudio module +""" + +from ..hfpipeline import HFPipeline +from .signal import Signal, SCIPY + + +class TextToAudio(HFPipeline): + """ + Generates audio from text. + """ + + def __init__(self, path=None, quantize=False, gpu=True, model=None, rate=None, **kwargs): + if not SCIPY: + raise ImportError('TextToAudio pipeline is not available - install "pipeline" extra to enable.') + + # Call parent constructor + super().__init__("text-to-audio", path, quantize, gpu, model, **kwargs) + + # Target sample rate, defaults to model sample rate + self.rate = rate + + def __call__(self, text, maxlength=512): + """ + Generates audio from text. + + This method supports text as a string or a list. If the input is a string, + the return type is a single audio output. If text is a list, the return type is a list. + + Args: + text: text|list + maxlength: maximum audio length to generate + + Returns: + list of (audio, sample rate) + """ + + # Format inputs + texts = [text] if isinstance(text, str) else text + + # Run pipeline + results = [self.convert(x) for x in self.pipeline(texts, forward_params={"max_new_tokens": maxlength})] + + # Extract results + return results[0] if isinstance(text, str) else results + + def convert(self, result): + """ + Converts audio result to target sample rate for this pipeline, if set. + + Args: + result: dict with audio samples and sample rate + + Returns: + (audio, sample rate) + """ + + audio, rate = result["audio"].squeeze(), result["sampling_rate"] + return (Signal.resample(audio, rate, self.rate), self.rate) if self.rate else (audio, rate) diff --git a/src/python/txtai/pipeline/audio/texttospeech.py b/src/python/txtai/pipeline/audio/texttospeech.py new file mode 100644 index 0000000..eaf62db --- /dev/null +++ b/src/python/txtai/pipeline/audio/texttospeech.py @@ -0,0 +1,556 @@ +""" +TextToSpeech module +""" + +# Conditional import +try: + import onnxruntime as ort + import soundfile as sf + + from ttstokenizer import IPATokenizer, TTSTokenizer + + from .signal import Signal, SCIPY + + TTS = SCIPY +except ImportError: + TTS = False + +import json +import logging + +from io import BytesIO + +from ..base import Pipeline + +# Core library imports +from ...util import Download, DownloadError, Library + +library = Library() +np = library.numpy() +torch = library.torch() +transformers = library.transformers() +yaml = library.yaml() + +# Logging configuration +logger = logging.getLogger(__name__) + + +class TextToSpeech(Pipeline): + """ + Generates speech from text. + """ + + def __init__(self, path=None, maxtokens=512, rate=22050): + """ + Creates a new TextToSpeech pipeline. + + Args: + path: optional model path + maxtokens: maximum number of tokens model can process, defaults to 512 + rate: target sample rate, defaults to 22050 + """ + + if not TTS: + raise ImportError('TextToSpeech pipeline is not available - install "pipeline" extra to enable') + + # Default path + path = path if path else "neuml/ljspeech-jets-onnx" + + # Target sample rate + self.rate = rate + + # Load target tts pipeline + self.pipeline = None + if self.hasfile(path, "model.onnx") and self.hasfile(path, "config.yaml"): + self.pipeline = ESPnet(path, maxtokens, self.providers()) + elif self.hasfile(path, "model.onnx") and self.hasfile(path, "voices.json"): + self.pipeline = Kokoro(path, maxtokens, self.providers()) + else: + self.pipeline = SpeechT5(path, maxtokens, self.providers()) + + def __call__(self, text, stream=False, speaker=1, encoding=None, **kwargs): + """ + Generates speech from text. Text longer than maxtokens will be batched and returned + as a single waveform per text input. + + This method supports text as a string or a list. If the input is a string, + the return type is audio. If text is a list, the return type is a list. + + Args: + text: text|list + stream: stream response if True, defaults to False + speaker: speaker id, defaults to 1 + encoding: optional audio encoding format + kwargs: additional keyword args + + Returns: + list of (audio, sample rate) or list of audio depending on encoding parameter + """ + + # Convert results to a list if necessary + texts = [text] if isinstance(text, str) else text + + # Streaming response + if stream: + return self.stream(texts, speaker, encoding) + + # Transform text to speech + results = [self.execute(x, speaker, encoding, **kwargs) for x in texts] + + # Return results + return results[0] if isinstance(text, str) else results + + def providers(self): + """ + Returns a list of available and usable providers. + + Returns: + list of available and usable providers + """ + + # Create list of providers, prefer CUDA provider if available + # CUDA provider only available if GPU is available and onnxruntime-gpu installed + if torch.cuda.is_available() and "CUDAExecutionProvider" in ort.get_available_providers(): + return [("CUDAExecutionProvider", {"cudnn_conv_algo_search": "DEFAULT"}), "CPUExecutionProvider"] + + # Default when CUDA provider isn't available + return ["CPUExecutionProvider"] + + def hasfile(self, path, name): + """ + Tests if a file exists in a local or remote repo. + + Args: + path: model path + name: file name + + Returns: + True if name exists in path, False otherwise + """ + + exists = False + try: + # Check if file exists + exists = Download()(path, name) is not None + except DownloadError: + return False + + return exists + + def stream(self, texts, speaker, encoding): + """ + Iterates over texts, splits into segments and yields snippets of audio. + This method is designed to integrate with streaming LLM generation. + + Args: + texts: list of input texts + speaker: speaker id + encoding: audio encoding format + + Returns: + snippets of audio as NumPy arrays or audio bytes depending on encoding parameter + """ + + buffer = [] + for x in texts: + buffer.append(x) + + if x == "\n" or (x.strip().endswith(".") and len([y for y in buffer if y]) > 2): + data, buffer = "".join(buffer), [] + yield self.execute(data, speaker, encoding) + + if buffer: + data = "".join(buffer) + yield self.execute(data, speaker, encoding) + + def execute(self, text, speaker, encoding, **kwargs): + """ + Executes model run for an input array of tokens. This method will build batches + of tokens when len(tokens) > maxtokens. + + Args: + text: text to tokenize and pass to model + speaker: speaker id + encoding: audio encoding format + kwargs: additional keyword args + + Returns: + (audio, sample rate) or audio bytes depending on encoding parameter + """ + + # Run pipeline model + audio, rate = self.pipeline(text, speaker, **kwargs) + + # Resample, if necessary and return + audio, rate = (Signal.resample(audio, rate, self.rate), self.rate) if self.rate else (audio, rate) + + # Encoding audio data + if encoding: + data = BytesIO() + sf.write(data, audio, rate, format=encoding) + return data.getvalue() + + # Default to (audio, rate) tuple + return (audio, rate) + + +class SpeechPipeline(Pipeline): + """ + Base class for speech pipelines + """ + + # pylint: disable=W0221 + def chunk(self, data, size, punctids): + """ + Batching method that takes punctuation into account. This method splits data up to size + chunks. But it also searches the batch and splits on the last punctuation token id. + + Args: + data: data + size: batch size + punctids: list of punctuation token ids + + Returns: + yields batches of data + """ + + # Iterate over each token + punct, index = 0, 0 + for i, x in enumerate(data): + # Check if token is a punctuation token + if x in punctids: + punct = i + + # Batch size reached, leave a spot for the punctuation token + if i - index >= (size - 1): + end = (punct if punct > index else i) + 1 + yield data[index:end] + index = end + + # Last batch + if index < len(data): + yield data[index : len(data)] + + +class ESPnet(SpeechPipeline): + """ + Text to Speech pipeline with an ESPnet ONNX model. + """ + + def __init__(self, path, maxtokens, providers): + """ + Creates a new ESPnet pipeline. + + Args: + path: model path + maxtokens: maximum number of tokens model can process + providers: list of supported ONNX providers + """ + + # Get path to model and config + download = Download() + config = download(path, "config.yaml") + model = download(path, "model.onnx") + + # Read yaml config + with open(config, "r", encoding="utf-8") as f: + config = yaml.safe_load(f) + + # Create tokenizer + tokens = config.get("token", {}).get("list") + self.tokenizer = TTSTokenizer(tokens) + + # Create ONNX Session + self.model = ort.InferenceSession(model, ort.SessionOptions(), providers) + + # Max number of input tokens model can handle + self.maxtokens = maxtokens + + # Get model input name, typically "text" + self.input = self.model.get_inputs()[0].name + + # Get parameter names + self.params = set(x.name for x in self.model.get_inputs()) + + def __call__(self, text, speaker): + """ + Executes a model run. This method will build batches of tokens when len(tokens) > maxtokens. + + Args: + text: text to tokenize and pass to model + speaker: speaker id + + Returns: + (audio, sample rate) + """ + + # Debug logging for input text + logger.debug("%s", text) + + # Sample rate + rate = 22050 + + # Tokenize input + tokens = self.tokenizer(text) + + # Split into batches and process + results = [] + for i, x in enumerate(self.chunk(tokens, self.maxtokens, self.tokenizer.punctuation())): + # Format input parameters + params = {self.input: x} + params = {**params, **{"sids": np.array([speaker])}} if "sids" in self.params else params + + # Run text through TTS model and save waveform + output = self.model.run(None, params) + results.append(Signal.trim(output[0], rate, trailing=False) if i > 0 else output[0]) + + # Concatenate results and return + return (np.concatenate(results), rate) + + +class Kokoro(SpeechPipeline): + """ + Text to Speech pipeline with an Kokoro ONNX model. + """ + + def __init__(self, path, maxtokens, providers): + """ + Creates a new Kokoro pipeline. + + Args: + path: model path + maxtokens: maximum number of tokens model can process + providers: list of supported ONNX providers + """ + + # Get path to model and config + download = Download() + voices = download(path, "voices.json") + model = download(path, "model.onnx") + + # Read voices config + with open(voices, "r", encoding="utf-8") as f: + self.voices = json.load(f) + + # Create tokenizer + self.tokenizer = IPATokenizer() + + # Create ONNX Session + self.model = ort.InferenceSession(model, ort.SessionOptions(), providers) + + # Max number of input tokens model can handle + self.maxtokens = min(maxtokens, 510) + + # Get model input name + self.input = self.model.get_inputs()[0].name + + # Get parameter names + self.params = set(x.name for x in self.model.get_inputs()) + + def __call__(self, text, speaker=None, speed=1.0, transcribe=True): + """ + Executes a model run. This method will build batches of tokens when len(tokens) > maxtokens. + + Args: + text: text to tokenize and pass to model + speaker: speaker id, defaults to first speaker + speed: defaults to 1.0 + transcribe: if text should be transcriped to IPA text, defaults to True + + Returns: + (audio, sample rate) + """ + + # Debug logging for input text + logger.debug("%s", text) + + # Sample rate + rate = 24000 + + # Looks up speaker, falls back to default + speaker = speaker if speaker in self.voices else next(iter(self.voices)) + speaker = np.array(self.voices[speaker], dtype=np.float32) + + # Tokenize input + self.tokenizer.transcribe = transcribe + tokens = self.tokenizer(text) + + # Split into batches and process + results = [] + for i, x in enumerate(self.chunk(tokens, self.maxtokens, self.tokenizer.punctuation())): + # Format input parameters + params = {self.input: [[0, *x, 0]], "style": speaker[len(x)], "speed": np.ones(1, dtype=np.float32) * speed} + + # Run text through TTS model and save waveform + output = self.model.run(None, params) + results.append(Signal.trim(output[0], rate, trailing=False) if i > 0 else output[0]) + + # Concatenate results and return + return (np.concatenate(results), rate) + + +class SpeechT5(SpeechPipeline): + """ + Text to Speech pipeline with a SpeechT5 ONNX model. + """ + + def __init__(self, path, maxtokens, providers): + """ + Creates a new SpeechT5 pipeline. + + Args: + path: model path + maxtokens: maximum number of tokens model can process + providers: list of supported ONNX providers + """ + + download = Download() + self.encoder = ort.InferenceSession(download(path, "encoder_model.onnx"), providers=providers) + self.decoder = ort.InferenceSession(download(path, "decoder_model_merged.onnx"), providers=providers) + self.vocoder = ort.InferenceSession(download(path, "decoder_postnet_and_vocoder.onnx"), providers=providers) + + self.processor = transformers.SpeechT5Processor.from_pretrained(path) + self.defaultspeaker = np.load(download(path, "speaker.npy"), allow_pickle=False) + + # Max number of input tokens model can handle + self.maxtokens = maxtokens + + # pylint: disable=E1101 + # Punctuation token ids + self.punctids = [v for k, v in self.processor.tokenizer.get_vocab().items() if k in ".,!?;"] + + def __call__(self, text, speaker): + """ + Executes a model run. This method will build batches of tokens when len(tokens) > maxtokens. + + Args: + text: text to tokenize and pass to model + speaker: speaker embeddings + + Returns: + (audio, sample rate) + """ + + # Debug logging for input text + logger.debug("%s", text) + + # Sample rate + rate = 16000 + + # Tokenize text + inputs = self.processor(text=text, return_tensors="np", normalize=True) + + # Split into batches and process + results = [] + for i, x in enumerate(self.chunk(inputs["input_ids"][0], self.maxtokens, self.punctids)): + # Run text through TTS model and save waveform + chunk = self.process(np.array([x], dtype=np.int64), speaker) + results.append(Signal.trim(chunk, rate, trailing=False) if i > 0 else chunk) + + # Concatenate results and return + return (np.concatenate(results), rate) + + def process(self, inputs, speaker): + """ + Runs model inference. + + Args: + inputs: input token ids + speaker: speaker embeddings + + Returns: + waveform as NumPy array + """ + + # Run through encoder model + outputs = self.encoder.run(None, {"input_ids": inputs}) + outputs = {key.name: outputs[x] for x, key in enumerate(self.encoder.get_outputs())} + + # Encoder outputs and parameters + hiddenstate, attentionmask = outputs["encoder_outputs"], outputs["encoder_attention_mask"] + minlenratio, maxlenratio = 0.0, 20.0 + reduction, threshold, melbins = 2, 0.5, 80 + + maxlen = int(hiddenstate.shape[1] * maxlenratio / reduction) + minlen = int(hiddenstate.shape[1] * minlenratio / reduction) + + # Main processing loop + spectrogram, index, crossattention, branch, outputs = [], 0, None, False, {} + while True: + index += 1 + + inputs = { + "use_cache_branch": np.array([branch]), + "encoder_attention_mask": attentionmask, + "speaker_embeddings": speaker if speaker is not None and isinstance(speaker, np.ndarray) else self.defaultspeaker, + } + + if index == 1: + inputs = self.placeholders(inputs) + inputs["output_sequence"] = np.zeros((1, 1, melbins)).astype(np.float32) + inputs["encoder_hidden_states"] = hiddenstate + branch = True + else: + inputs = self.inputs(inputs, outputs, crossattention) + inputs["output_sequence"] = outputs["output_sequence_out"] + inputs["encoder_hidden_states"] = np.zeros((1, 0, 768)).astype(np.float32) + + # Run inputs through decoder + outputs = self.decoder.run(None, inputs) + outputs = {key.name: outputs[x] for x, key in enumerate(self.decoder.get_outputs())} + + # Get cross attention with 1st pass + if index == 1: + crossattention = {key: val for key, val in outputs.items() if ("encoder" in key and "present" in key)} + + # Decoder outputs + prob = outputs["prob"] + spectrum = outputs["spectrum"] + spectrogram.append(spectrum) + + # Done when stop token or maximum length is reached. + if index >= minlen and (int(sum(prob >= threshold)) > 0 or index >= maxlen): + spectrogram = np.concatenate(spectrogram) + return self.vocoder.run(None, {"spectrogram": spectrogram})[0] + + def placeholders(self, inputs): + """ + Creates decoder model inputs for initial inference pass. + + Args: + inputs: current decoder inputs + + Returns: + updated decoder inputs + """ + + length = inputs["encoder_attention_mask"].shape[1] + + for x in range(6): + inputs[f"past_key_values.{x}.encoder.key"] = np.zeros((1, 12, length, 64)).astype(np.float32) + inputs[f"past_key_values.{x}.encoder.value"] = np.zeros((1, 12, length, 64)).astype(np.float32) + inputs[f"past_key_values.{x}.decoder.key"] = np.zeros((1, 12, 1, 64)).astype(np.float32) + inputs[f"past_key_values.{x}.decoder.value"] = np.zeros((1, 12, 1, 64)).astype(np.float32) + + return inputs + + def inputs(self, inputs, previous, crossattention): + """ + Creates decoder model inputs for follow-on inference passes. + + Args: + inputs: current decoder inputs + previous: previous decoder outputs + crossattention: crossattention parameters + + Returns: + updated decoder inputs + """ + + for x in range(6): + inputs[f"past_key_values.{x}.encoder.key"] = crossattention[f"present.{x}.encoder.key"] + inputs[f"past_key_values.{x}.encoder.value"] = crossattention[f"present.{x}.encoder.value"] + inputs[f"past_key_values.{x}.decoder.key"] = previous[f"present.{x}.decoder.key"] + inputs[f"past_key_values.{x}.decoder.value"] = previous[f"present.{x}.decoder.value"] + + return inputs diff --git a/src/python/txtai/pipeline/audio/transcription.py b/src/python/txtai/pipeline/audio/transcription.py new file mode 100644 index 0000000..e6af12c --- /dev/null +++ b/src/python/txtai/pipeline/audio/transcription.py @@ -0,0 +1,215 @@ +""" +Transcription module +""" + +# Conditional import +try: + import soundfile as sf + + from .signal import Signal, SCIPY + + TRANSCRIPTION = SCIPY +except (ImportError, OSError): + TRANSCRIPTION = False + +from ..hfpipeline import HFPipeline + +# Core library imports +from ...util import Library + +np = Library().numpy() + + +class Transcription(HFPipeline): + """ + Transcribes audio files or data to text. + """ + + def __init__(self, path=None, quantize=False, gpu=True, model=None, **kwargs): + if not TRANSCRIPTION: + raise ImportError( + 'Transcription pipeline is not available - install "pipeline" extra to enable. Also check that libsndfile is available.' + ) + + # Call parent constructor + super().__init__("automatic-speech-recognition", path, quantize, gpu, model, **kwargs) + + def __call__(self, audio, rate=None, chunk=10, join=True, **kwargs): + """ + Transcribes audio files or data to text. + + This method supports a single audio element or a list of audio. If the input is audio, the return + type is a string. If text is a list, a list of strings is returned + + Args: + audio: audio|list + rate: sample rate, only required with raw audio data + chunk: process audio in chunk second sized segments + join: if True (default), combine each chunk back together into a single text output. + When False, chunks are returned as a list of dicts, each having raw associated audio and + sample rate in addition to text + kwargs: generate keyword arguments + + Returns: + list of transcribed text + """ + + # Convert single element to list + values = [audio] if self.isaudio(audio) else audio + + # Read input audio + speech = self.read(values, rate) + + # Apply transformation rules and store results + results = self.batchprocess(speech, chunk, **kwargs) if chunk and not join else self.process(speech, chunk, **kwargs) + + # Return single element if single element passed in + return results[0] if self.isaudio(audio) else results + + def isaudio(self, audio): + """ + Checks if input is a single audio element. + + Args: + audio: audio|list + + Returns: + True if input is an audio element, False otherwise + """ + + return isinstance(audio, (str, tuple, np.ndarray)) or hasattr(audio, "read") + + def read(self, audio, rate): + """ + Read audio to raw waveforms and sample rates. + + Args: + audio: audio|list + rate: optional sample rate + + Returns: + list of (audio data, sample rate) + """ + + speech = [] + for x in audio: + if isinstance(x, str) or hasattr(x, "read"): + # Read file or file-like object + raw, samplerate = sf.read(x) + elif isinstance(x, tuple): + # Input is NumPy array and sample rate + raw, samplerate = x + else: + # Input is NumPy array + raw, samplerate = x, rate + + speech.append((raw, samplerate)) + + return speech + + def process(self, speech, chunk, **kwargs): + """ + Standard processing loop. Runs a single pipeline call for all speech inputs along + with the chunk size. Returns text for each input. + + Args: + speech: list of (audio data, sample rate) + chunk: split audio into chunk seconds sized segments for processing + kwargs: generate keyword arguments + + Returns: + list of transcribed text + """ + + results = [] + for result in self.pipeline([self.convert(*x) for x in speech], chunk_length_s=chunk, ignore_warning=True, generate_kwargs=kwargs): + # Store result + results.append(self.clean(result["text"])) + + return results + + def batchprocess(self, speech, chunk, **kwargs): + """ + Batch processing loop. Runs a pipeline call per speech input. Each speech input is split + into chunk duration segments. Each segment is individually transcribed and returned along with + the raw wav snippets. + + Args: + speech: list of (audio data, sample rate) + chunk: split audio into chunk seconds sized segments for processing + kwargs: generate keyword arguments + + Returns: + list of lists of dicts - each dict has text, raw wav data for text and sample rate + """ + + results = [] + + # Process each element individually to get time-sliced chunks + for raw, rate in speech: + # Get segments for current speech entry + segments = self.segments(raw, rate, chunk) + + # Process segments, store raw data before processing given pipeline modifies it + sresults = [] + for x, result in enumerate(self.pipeline([self.convert(*x) for x in segments], generate_kwargs=kwargs)): + sresults.append({"text": self.clean(result["text"]), "raw": segments[x][0], "rate": segments[x][1]}) + + results.append(sresults) + + return results + + def segments(self, raw, rate, chunk): + """ + Builds chunk duration batches. + + Args: + raw: raw audio data + rate: sample rate + chunk: chunk duration size + """ + + segments = [] + + # Split into batches, use sample rate * chunk seconds + for segment in self.batch(raw, rate * chunk): + segments.append((segment, rate)) + + return segments + + def convert(self, raw, rate): + """ + Converts input audio to mono with a sample rate equal to the pipeline model's + sample rate. + + Args: + raw: raw audio data + rate: target sample rate + + Returns: + audio data ready for pipeline model + """ + + # Convert stereo to mono, if necessary + raw = Signal.mono(raw) + + # Resample to target sample rate + target = self.pipeline.feature_extractor.sampling_rate + return {"raw": Signal.resample(raw, rate, target), "sampling_rate": target} + + def clean(self, text): + """ + Applies text normalization rules. + + Args: + text: input text + + Returns: + clean text + """ + + # Trim whitespace + text = text.strip() + + # Convert all upper case strings to capitalized case + return text.capitalize() if text.isupper() else text diff --git a/src/python/txtai/pipeline/base.py b/src/python/txtai/pipeline/base.py new file mode 100644 index 0000000..f42add8 --- /dev/null +++ b/src/python/txtai/pipeline/base.py @@ -0,0 +1,23 @@ +""" +Pipeline module +""" + + +class Pipeline: + """ + Base class for all Pipelines. The only interface requirement is to define a __call___ method. + """ + + def batch(self, data, size): + """ + Splits data into separate batch sizes specified by size. + + Args: + data: data elements + size: batch size + + Returns: + list of evenly sized batches with the last batch having the remaining elements + """ + + return [data[x : x + size] for x in range(0, len(data), size)] diff --git a/src/python/txtai/pipeline/data/__init__.py b/src/python/txtai/pipeline/data/__init__.py new file mode 100644 index 0000000..d82698c --- /dev/null +++ b/src/python/txtai/pipeline/data/__init__.py @@ -0,0 +1,11 @@ +""" +Segment imports +""" + +from .filetohtml import FileToHTML +from .htmltomd import HTMLToMarkdown +from .segmentation import Segmentation +from .tabular import Tabular +from .textractor import Textractor +from .tokenizer import Tokenizer +from .urlretrieve import URLRetrieve diff --git a/src/python/txtai/pipeline/data/filetohtml.py b/src/python/txtai/pipeline/data/filetohtml.py new file mode 100644 index 0000000..a875115 --- /dev/null +++ b/src/python/txtai/pipeline/data/filetohtml.py @@ -0,0 +1,274 @@ +""" +FileToHTML module +""" + +import os +import re + +from subprocess import Popen + +# Conditional import +try: + from tika import detector, parser + + TIKA = True +except ImportError: + TIKA = False + +# Conditional import +try: + from docling.document_converter import DocumentConverter + + DOCLING = True +except ImportError: + DOCLING = False + +# Conditional import +try: + import liteparse + + LITEPARSE = True +except ImportError: + LITEPARSE = False + +from ..base import Pipeline + + +class FileToHTML(Pipeline): + """ + File to HTML pipeline. + """ + + def __init__(self, backend="available"): + """ + Creates a new File to HTML pipeline. + + Args: + backend: backend to use to extract content, supports "tika", "docling", "liteparse" or + "available" (default) which finds the first available + """ + + # Lowercase backend parameter + backend = backend.lower() if backend else None + + # Check for available backend + if backend == "available": + backend = "tika" if Tika.available() else "docling" if Docling.available() else "liteparse" if LiteParse.available() else None + + # Create backend instance + self.backend = Tika() if backend == "tika" else Docling() if backend == "docling" else LiteParse() if backend == "liteparse" else None + + def __call__(self, path): + """ + Converts file at path to HTML. Returns None if no backend is available. + + Args: + path: input file path + + Returns: + html if a backend is available, otherwise returns None + """ + + return self.backend(path) if self.backend else None + + +class Backend: + """ + Base File to HTML conversion backend. + """ + + def ishtml(self, path): + """ + Detects if this file looks like HTML. + + Args: + path: file path + + Returns: + True if this is HTML + """ + + with open(path, "rb") as f: + # Read first 1024 bytes, ignore encoding errors and strip leading/trailing whitespace + content = f.read(1024) + content = content.decode("ascii", errors="ignore").lower().strip() + + # Check for HTML + return re.search(r"") + + # Normalize HTML and return + return self.normalize(html) + + def normalize(self, html): + """ + Applies normalization rules to make HTML consistent with other text extraction backends. + + Args: + html: input html + + Returns: + normalized html + """ + + # Wrap content with a body tag, if necessary + html = html.replace("", "").replace("", "") if "" not in html else html + + # Remove bullets from list items + html = re.sub(r"
  • \xb7 ", r"
  • ", html) + + # Add spacing between paragraphs + return html.replace("

    ", "

    ") + + +class LiteParse(Backend): + """ + File to HTML conversion via LiteParse. + """ + + @staticmethod + def available(): + """ + Checks if LiteParse is available. + + Returns: + True if LiteParse is available, False otherwise + """ + + return LITEPARSE + + def __init__(self): + """ + Creates a new LiteParse instance. + """ + + if not LiteParse.available(): + raise ImportError('LiteParse is not available - install "pipeline" extra to enable') + + self.liteparse = liteparse.LiteParse(output_format="markdown") + + def __call__(self, path): + """ + Parses content to HTML. + + Args: + path: file path + + Returns: + html + """ + + # Skip parsing if input is HTML + if self.ishtml(path): + return None + + # Output text + output = [""] + + # Iterate over each parsed page + for page in self.liteparse.parse(path).text.split("\n\n-----\n\n"): + output.append(f"

    {page}
    ") + + output.append("") + + return "\n".join(output) diff --git a/src/python/txtai/pipeline/data/htmltomd.py b/src/python/txtai/pipeline/data/htmltomd.py new file mode 100644 index 0000000..721610d --- /dev/null +++ b/src/python/txtai/pipeline/data/htmltomd.py @@ -0,0 +1,414 @@ +""" +HTMLToMarkdown module +""" + +import re + +# Conditional import +try: + from bs4 import BeautifulSoup, NavigableString + + SOUP = True +except ImportError: + SOUP = False + +from ..base import Pipeline + + +class HTMLToMarkdown(Pipeline): + """ + HTML to Markdown pipeline. + + Markdown formatting is applied for headings, blockquotes, lists, code, tables and text. Visual formatting is also + included (bold, italic etc). + + This pipeline searches for the best node that has relevant text, often found with an article, main or body tag. + """ + + def __init__(self, paragraphs=False, sections=False): + """ + Create a new Extract instance. + + Args: + paragraphs: True if paragraph parsing enabled, False otherwise + sections: True if section parsing enabled, False otherwise + """ + + if not SOUP: + raise ImportError('HTMLToMarkdown pipeline is not available - install "pipeline" extra to enable') + + self.paragraphs = paragraphs + self.sections = sections + + def __call__(self, html): + """ + Transforms input HTML into Markdown formatted text. + + Args: + html: input html + + Returns: + markdown formatted text + """ + + # HTML Parser + soup = BeautifulSoup(html, features="html.parser") + + # Ignore script and style tags + for script in soup.find_all(["script", "style"]): + script.decompose() + + # Check for article sections + article = next((x for x in ["article", "main"] if soup.find(x)), None) + + # Extract text from each section element + nodes = [] + for node in soup.find_all(article if article else "body"): + # Skip article sections without at least 1 paragraph + if not article or node.find("p"): + nodes.append(self.process(node, article)) + + # Return extracted text, fallback to default text extraction if no nodes found + return "\n".join(self.metadata(soup) + nodes) if nodes else self.default(soup) + + def process(self, node, article): + """ + Extracts text from a node. This method applies transforms for headings, blockquotes, lists, code, tables and text. + Page breaks are detected and reflected in the output text as a page break character. + + Args: + node: input node + article: True if the main section node is an article + + Returns: + node text + """ + + if self.isheader(node): + return self.header(node, article) + + if node.name in ("blockquote", "q"): + return self.block(node) + + if node.name in ("ul", "ol"): + return self.items(node, article) + + if node.name in ("code", "pre"): + return self.code(node) + + if node.name == "table": + return self.table(node, article) + + # Nodes to skip + if node.name in ("aside",) + (() if article else ("header", "footer")): + return "" + + # Get page break symbol, if available + page = node.name and node.get("class") and "page" in node.get("class") + + # Get node children + children = self.children(node) + + # Join elements into text + if self.iscontainer(node, children): + texts = [self.process(node, article) for node in children] + text = "\n".join(text for text in texts if text or not article) + else: + text = self.text(node, article) + + # Add page breaks, if section parsing enabled. Otherwise add node text. + return f"{text}\f" if page and self.sections else text + + def metadata(self, node): + """ + Builds a metadata section. The metadata section consists of the title and + description fields. + + Args: + node: input document node + + Returns: + metadata as a list + """ + + title = node.find("title") + metadata = [f"**{title.text.strip()}**"] if title and title.text else [] + + description = node.find("meta", attrs={"name": "description"}) + if description and description["content"]: + metadata.append(f"\n*{description['content'].strip()}*") + + # Add separator + if metadata: + metadata.append("\f" if self.sections else "\n\n") + + return metadata + + def default(self, soup): + """ + Default text handler when valid HTML isn't detected. + + Args: + soup: BeautifulSoup object + + Returns: + text + """ + + lines = [] + for line in soup.get_text().split("\n"): + # Detect markdown headings and add page breaks + lines.append(f"\f{line}" if self.sections and re.search(r"^#+ ", line) else line) + + return "\n".join(lines) + + def text(self, node, article): + """ + Text handler. This method flattens a node and it's children to text. + + Args: + node: input node + article: True if the main section node is an article + + Returns: + node text + """ + + # Get node children if available, otherwise use node as item + items = self.children(node) + items = items if items else [node] + + # Apply emphasis and link formatting + texts = [] + for x in items: + target, text = x if x.name else node, x.text + + if text.strip(): + if target.name in ("b", "strong"): + text = f"**{text.strip()}** " + elif target.name in ("i", "em"): + text = f"*{text.strip()}* " + elif target.name == "a": + text = f"[{text.strip()}]({target.get('href')}) " + + texts.append(text) + + # Join text elements + text = "".join(texts) + + # Article text processing + text = self.articletext(node, text) if article else text + + # Return text, strip leading/trailing whitespace if this is a string only node + text = text if node.name and text else text.strip() + + return text + + def header(self, node, article): + """ + Header handler. This method transforms a HTML heading into a Markdown formatted heading. + + Args: + node: input node + article: True if the main section node is an article + + Returns: + heading as markdown + """ + + # Get heading level and text + level = "#" * int(node.name[1]) + text = self.text(node, article) + + # Add section break or newline, if necessary + level = f"\f{level}" if self.sections else f"\n{level}" + + # Return formatted header. Remove leading whitespace as it was added before level in step above. + return f"{level} {text.lstrip()}" if text.strip() else "" + + def block(self, node): + """ + Blockquote handler. This method transforms a HTML blockquote or q block into a Markdown formatted + blockquote + + Args: + node: input node + + Returns: + block as markdown + """ + + text = "\n".join(f"> {x}" for x in node.text.strip().split("\n")) + return f"{text}\n\n" if self.paragraphs else f"{text}\n" + + def items(self, node, article): + """ + List handler. This method transforms a HTML ordered/unordered list into a Markdown formatted list. + + Args: + node: input node + article: True if the main section node is an article + + Returns: + list as markdown + """ + + elements = [] + for x, element in enumerate(node.find_all("li")): + # Unordered lists use dashes. Ordered lists use numbers. + prefix = "-" if node.name == "ul" else f"{x + 1}." + + # List item text + text = self.process(element, article) + + # Add list element + if text: + elements.append(f"{prefix} {text}") + + # Join elements together as string + return "\n".join(elements) + + def code(self, node): + """ + Code block handler. This method transforms a HTML pre or code block into a Markdown formatted + code block. + + Args: + node: input node + + Returns: + code as markdown + """ + + text = f"```\n{node.text.strip()}\n```" + return f"{text}\n\n" if self.paragraphs else f"{text}\n" + + def table(self, node, article): + """ + Table handler. This method transforms a HTML table into a Markdown formatted table. + + Args: + node: input node + article: True if the main section node is an article + + Returns: + table as markdown + """ + + elements, header = [], False + + # Process all rows + rows = node.find_all("tr") + for row in rows: + # Get list of columns for row + columns = row.find_all(lambda tag: tag.name in ("th", "td")) + + # Add columns with separator + elements.append(f"|{'|'.join(self.process(column, article) for column in columns)}|") + + # If there are multiple rows, add header format row + if not header and len(rows) > 1: + elements.append(f"{'|---' * len(columns)}|") + header = True + + # Join elements together as string + return "\n".join(elements) + + def iscontainer(self, node, children): + """ + Analyzes a node and it's children to determine if this is a container element. A container + element is defined as being a div, body, article or not having any string elements as children. + + Args: + node: input node + nodes: input node's children + + Returns: + True if this is a container element, False otherwise + """ + + return children and (node.name in ("div", "body", "article") or not any(isinstance(x, NavigableString) for x in children)) + + def children(self, node): + """ + Gets the node children, if available. + + Args: + node: input node + + Returns: + node children or None if not available + """ + + if node.name and node.contents: + # Iterate over children and remove whitespace-only string nodes + return [node for node in node.contents if node.name or node.text.strip()] + + return None + + def articletext(self, node, text): + """ + Transforms node text using article parsing rules. Article parsing is designed to extract text content from web articles. + It ignores navigation headers and other superfluous elements. + + Args: + node: input node + text: current text + + Returns: + article text + """ + + # List of valid text nodes + valid = ("p", "th", "td", "li", "a", "b", "strong", "i", "em") + + # Check if this node is valid or it's part of a table cell + valid = node.name in valid or (node.parent and node.parent.name in ("th", "td")) + + # Check if text is valid article text + text = text if (valid or self.isheader(node)) and not self.islink(node) else "" + if text: + # Replace non-breaking space plus newline with double newline + text = text.replace("\xa0\n", "\n\n") + + # Format paragraph whitespace + if node.name == "p": + text = f"{text.strip()}\n\n" if self.paragraphs else f"{text.strip()}\n" + + return text + + def isheader(self, node): + """ + Checks if node is a header node. + + Args: + node: input node + + Returns: + True if node is a header node, False otherwise + """ + + return node.name in ("h1", "h2", "h3", "h4", "h5", "h6") + + def islink(self, node): + """ + Checks if node is a link node. This method does not consider links without tables as link nodes. + + Args: + node: input node + + Returns: + True if node is a link node, False otherwise + """ + + # Check if this is a link node or link container + link, parent = False, node + while parent: + if parent.name == "a": + link = True + break + + parent = parent.parent + + # Return if this node or any parents are a link. Ignore links in table cells. + return link and node.parent.name not in ("th", "td") diff --git a/src/python/txtai/pipeline/data/segmentation.py b/src/python/txtai/pipeline/data/segmentation.py new file mode 100644 index 0000000..ddda16e --- /dev/null +++ b/src/python/txtai/pipeline/data/segmentation.py @@ -0,0 +1,198 @@ +""" +Segmentation module +""" + +import re + +# Conditional import +try: + from nltk import sent_tokenize + + NLTK = True +except ImportError: + NLTK = False + +# Conditional import +try: + import chonkie + + CHONKIE = True +except ImportError: + CHONKIE = False + +from ..base import Pipeline + + +class Segmentation(Pipeline): + """ + Segments text into logical units. + """ + + def __init__( + self, + sentences=False, + lines=False, + paragraphs=False, + minlength=None, + join=False, + sections=False, + cleantext=True, + chunker=None, + tuples=False, + **kwargs, + ): + """ + Creates a new Segmentation pipeline. + + Args: + sentences: tokenize text into sentences if True, defaults to False + lines: tokenizes text into lines if True, defaults to False + paragraphs: tokenizes text into paragraphs if True, defaults to False + minlength: require at least minlength characters per text element, defaults to None + join: joins tokenized sections back together if True, defaults to False + sections: tokenizes text into sections if True, defaults to False. Splits using section or page breaks, depending on what's available + cleantext: apply text cleaning rules, defaults to True + chunker: creates a third-party chunker to tokenize text if set, defaults to None + tuples: return (input, output) tuples, defaults to False + kwargs: additional keyword arguments + """ + + if not NLTK and sentences: + raise ImportError('NLTK is not available - install "pipeline" extra to enable') + + if not CHONKIE and chunker: + raise ImportError('Chonkie is not available - install "pipeline" extra to enable') + + self.sentences = sentences + self.lines = lines + self.paragraphs = paragraphs + self.sections = sections + self.minlength = minlength + self.join = join + self.cleantext = cleantext + + # Create a third-party chunker, if applicable + self.chunker = self.createchunker(chunker, **kwargs) if chunker else None + + # Return (input, output) tuples as output + self.tuples = tuples + + def __call__(self, text): + """ + Segments text into semantic units. + + This method supports text as a string or a list. If the input is a string, the return + type is text|list. If text is a list, a list of returned, this could be a + list of text or a list of lists depending on the tokenization strategy. + + Args: + text: text|list + + Returns: + segmented text + """ + + # Get inputs + texts = [text] if not isinstance(text, list) else text + + # Extract text for each input file + results = [] + for value in texts: + # Get text + result = self.text(value) + + # Parse and add extracted results + result = self.parse(result) + + # Wrap as tuple + if self.tuples: + result = [(value, x) for x in result] if isinstance(result, list) else (value, result) + + results.append(result) + + return results[0] if isinstance(text, str) else results + + def text(self, text): + """ + Hook to allow extracting text out of input text object. + + Args: + text: object to extract text from + """ + + return text + + def parse(self, text): + """ + Splits and cleans text based on the current parameters. + + Args: + text: input text + + Returns: + parsed and clean content + """ + + content = None + + if self.chunker: + # pylint: disable=E1102 + content = [self.clean(x.text) for x in self.chunker(text)] + elif self.sentences: + content = [self.clean(x) for x in sent_tokenize(text)] + elif self.lines: + content = [self.clean(x) for x in re.split(r"\n{1,}", text)] + elif self.paragraphs: + content = [self.clean(x) for x in re.split(r"\n{2,}", text)] + elif self.sections: + split = r"\f" if "\f" in text else r"\n{3,}" + content = [self.clean(x) for x in re.split(split, text)] + else: + content = self.clean(text) + + # Text tokenization enabled + if isinstance(content, list): + # Remove empty strings + content = [x for x in content if x] + return " ".join(content) if self.join else content + + # Default method that returns clean text + return content + + def clean(self, text): + """ + Applies a series of rules to clean text. + + Args: + text: input text + + Returns: + clean text + """ + + # Text cleaning disabled, return original text + if not self.cleantext: + return text + + # Collapse and remove excess whitespace + text = re.sub(r" +", " ", text) + text = text.strip() + + # If minlength enabled, require at least minlength chars + return text if not self.minlength or len(text) >= self.minlength else None + + def createchunker(self, chunker, **kwargs): + """ + Creates a new third-party chunker + + Args: + chunker: name of chunker to create + kwargs: additional keyword arguments + + Returns: + new chunker + """ + + # Resolve and create a third-party chunker + chunker = f"{chunker[0].upper() + chunker[1:]}Chunker" + return getattr(chonkie, chunker)(**kwargs) diff --git a/src/python/txtai/pipeline/data/tabular.py b/src/python/txtai/pipeline/data/tabular.py new file mode 100644 index 0000000..277949d --- /dev/null +++ b/src/python/txtai/pipeline/data/tabular.py @@ -0,0 +1,156 @@ +""" +Tabular module +""" + +import os + +# Conditional import +try: + import pandas as pd + + PANDAS = True +except ImportError: + PANDAS = False + +from ..base import Pipeline + + +class Tabular(Pipeline): + """ + Splits tabular data into rows and columns. + """ + + def __init__(self, idcolumn=None, textcolumns=None, content=False): + """ + Creates a new Tabular pipeline. + + Args: + idcolumn: column name to use for row id + textcolumns: list of columns to combine as a text field + content: if True, a dict per row is generated with all fields. If content is a list, a subset of fields + is included in the generated rows. + """ + + if not PANDAS: + raise ImportError('Tabular pipeline is not available - install "pipeline" extra to enable') + + self.idcolumn = idcolumn + self.textcolumns = textcolumns + self.content = content + + def __call__(self, data): + """ + Splits data into rows and columns. + + Args: + data: input data + + Returns: + list of (id, text, tag) + """ + + items = [data] if not isinstance(data, list) else data + + # Combine all rows into single return element + results = [] + dicts = [] + + for item in items: + # Local CSV file + if isinstance(item, str): + _, extension = os.path.splitext(item) + extension = extension.replace(".", "").lower() + + if extension == "csv" and os.path.exists(item): + df = pd.read_csv(item) + results.append(self.process(df)) + else: + raise ValueError(f"Invalid local file path or file not found: {item}") + + # Dict + if isinstance(item, dict): + dicts.append(item) + + # List of dicts + elif isinstance(item, list): + df = pd.DataFrame(item) + results.append(self.process(df)) + + if dicts: + df = pd.DataFrame(dicts) + results.extend(self.process(df)) + + return results[0] if not isinstance(data, list) else results + + def process(self, df): + """ + Extracts a list of (id, text, tag) tuples from a dataframe. + + Args: + df: DataFrame to extract content from + + Returns: + list of (id, text, tag) + """ + + rows = [] + + # Columns to use for text + columns = self.textcolumns + if not columns: + columns = list(df.columns) + if self.idcolumn: + columns.remove(self.idcolumn) + + # Transform into (id, text, tag) tuples + for index, row in df.iterrows(): + uid = row[self.idcolumn] if self.idcolumn else index + uid = uid if uid is not None else index + text = self.concat(row, columns) + + rows.append((uid, text, None)) + + # Also add row for content + if isinstance(self.content, list): + row = {column: self.column(value) for column, value in row.to_dict().items() if column in self.content} + rows.append((uid, row, None)) + elif self.content: + row = {column: self.column(value) for column, value in row.to_dict().items()} + rows.append((uid, row, None)) + + return rows + + def concat(self, row, columns): + """ + Builds a text field from row using columns. + + Args: + row: input row + columns: list of columns to join together + + Returns: + text + """ + + parts = [] + for column in columns: + column = self.column(row[column]) + if column: + parts.append(str(column)) + + return ". ".join(parts) if parts else None + + def column(self, value): + """ + Applies column standardization logic: + - Replace NaN values with None + + Args: + value: input value + + Returns: + formatted value + """ + + # Check for null - treat lists as not null + return None if not isinstance(value, list) and pd.isnull(value) else value diff --git a/src/python/txtai/pipeline/data/textractor.py b/src/python/txtai/pipeline/data/textractor.py new file mode 100644 index 0000000..e4ee99a --- /dev/null +++ b/src/python/txtai/pipeline/data/textractor.py @@ -0,0 +1,176 @@ +""" +Textractor module +""" + +import os +import tempfile + +from urllib.parse import urlparse + +from .filetohtml import FileToHTML +from .htmltomd import HTMLToMarkdown +from .segmentation import Segmentation +from .urlretrieve import URLRetrieve + + +class Textractor(Segmentation): + """ + Extracts text from files. + """ + + # pylint: disable=R0913 + def __init__( + self, + sentences=False, + lines=False, + paragraphs=False, + minlength=None, + join=False, + sections=False, + cleantext=True, + chunker=None, + headers=None, + backend="available", + safeopen=False, + **kwargs, + ): + super().__init__(sentences, lines, paragraphs, minlength, join, sections, cleantext, chunker, **kwargs) + + # Get backend parameter - handle legacy tika flag + backend = "tika" if "tika" in kwargs and kwargs["tika"] else None if "tika" in kwargs else backend + + # File to HTML pipeline + self.html = FileToHTML(backend) if backend else None + + # HTML to Markdown pipeline + self.markdown = HTMLToMarkdown(self.paragraphs, self.sections) + + # HTTP headers + self.headers = headers if headers else {} + + # Safe open mode. When set only local temp urls (or a specified directory) and non-private URLs are supported + self.safeopen = os.path.realpath(tempfile.gettempdir() if isinstance(safeopen, bool) else safeopen) if safeopen else safeopen + + # URL retriever + self.urlretrieve = URLRetrieve(self.headers, self.safeopen) + + def text(self, text): + # Check if text is a valid file path or url + path, exists = self.valid(text) + + if not path: + # Not a valid file path, treat input as data + html = text + + elif self.html: + # Use FileToHTML pipeline, if available + # Retrieve remote file, if necessary + path = path if exists else self.download(path) + + # Parse content to HTML + html = self.html(path) + + # FiletoHTML pipeline returns None when input is already HTML + html = html if html else self.retrieve(path) + + # Delete temporary file + if not exists: + os.remove(path) + + else: + # Read data from url/path + html = self.retrieve(path) + + # HTML to Markdown + return self.markdown(html) + + def valid(self, path): + """ + Checks if path is a valid local file or web url. Returns path if valid along with a flag + denoting if the path exists locally. + + Args: + path: path to check + + Returns: + (path, exists) + """ + + # Convert file urls to local paths + path = path.replace("file://", "") + + # Check if this is a local file path or local file url + exists = os.path.exists(path) + + # Safe open validation + if not self.safecheck(path): + path = path if urlparse(path).scheme else os.path.realpath(path) + raise IOError(f"Safeopen URL validation failed: path={path}, safeopen={self.safeopen}") + + # Consider local files and HTTP urls valid + return (path if exists or urlparse(path).scheme in ("http", "https") else None, exists) + + def download(self, url): + """ + Downloads content of url to a temporary file. + + Args: + url: input url + + Returns: + temporary file path + """ + + with tempfile.NamedTemporaryFile(mode="wb", delete=False) as output: + path = output.name + + # Retrieve and write data to temporary file + output.write(self.retrieve(url)) + + return path + + def retrieve(self, url): + """ + Retrieves content from url. + + Args: + url: input url + + Returns: + data + """ + + # Local file + if os.path.exists(url): + with open(url, "rb") as f: + return f.read() + + # Remote file + return self.urlretrieve(url) + + def safecheck(self, url): + """ + Safe open url validation. Validates a local path is within the safeopen directory and + that URLs are a public HTTP(s) URLs. + + Args: + url: input url + + Returns: + True if url is valid, false otherwise + """ + + # Default to allow all urls when safe open is disabled + valid = True + + if self.safeopen: + if os.path.exists(url): + # Validate local file is in safe path + path = os.path.realpath(url) + prefix = os.path.commonpath([self.safeopen, path]) + valid = prefix == self.safeopen + else: + # URL validation + valid = url.lower().startswith("http") and not self.urlretrieve.isprivateurl(url) + + return valid diff --git a/src/python/txtai/pipeline/data/tokenizer.py b/src/python/txtai/pipeline/data/tokenizer.py new file mode 100644 index 0000000..6dc575b --- /dev/null +++ b/src/python/txtai/pipeline/data/tokenizer.py @@ -0,0 +1,176 @@ +""" +Tokenizer module +""" + +import re +import string + +from ..base import Pipeline + +# Core library imports +from ...util import Library + +regex = Library().regex() + + +class Tokenizer(Pipeline): + """ + Tokenizes text into tokens using one of the following methods. + + 1. Split using word boundary rules from the Unicode Text Segmentation algorithm (see Unicode Standard Annex #29). + This is similar to the standard tokenizer in Apache Lucene and works well for most languages. + + 2. Tokenization method that only accepts alphanumeric tokens from the Latin alphabet. This is a backwards compatible mode + that was the default with older versions of txtai. + + 3. Tokenize on whitespace + + 4. Tokenize using a provided regular expression + """ + + # fmt: off + # English Stop Word List (Standard stop words used by Apache Lucene) + STOP_WORDS = {"a", "an", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", + "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", + "they", "this", "to", "was", "will", "with"} + # fmt: on + + @staticmethod + def tokenize(text, lowercase=True, emoji=True, alphanum=True, stopwords=True, whitespace=False, regexp=None, ngrams=None): + """ + Tokenizes text into a list of tokens. The default backwards compatible parameters filter out English stop words and only + accept alphanumeric tokens. + + Args: + text: input text + lowercase: lower cases all tokens if True, defaults to True + emoji: tokenize emoji in text if True, defaults to True + alphanum: requires 2+ character alphanumeric tokens if True, defaults to True + stopwords: removes provided stop words if a list, removes default English stop words if True, defaults to True + whitespace: tokenize on whitespace if True, defaults to False + regexp: tokenize using the provided regular expression, defaults to None + ngrams: tokenize into ngrams, defaults to None, supports int or dict + + Returns: + list of tokens + """ + + # Create a tokenizer with backwards compatible settings + return Tokenizer(lowercase, emoji, alphanum, stopwords, whitespace, regexp, ngrams)(text) + + def __init__(self, lowercase=True, emoji=True, alphanum=False, stopwords=False, whitespace=False, regexp=None, ngrams=None): + """ + Creates a new tokenizer. The default parameters segment text per Unicode Standard Annex #29. + + Args: + lowercase: lower cases all tokens if True, defaults to True + emoji: tokenize emoji in text if True, defaults to True + alphanum: requires 2+ character alphanumeric tokens if True, defaults to False + stopwords: removes provided stop words if a list, removes default English stop words if True, defaults to False + whitespace: tokenize on whitespace if True, defaults to False + regexp: tokenize using the provided regular expression, defaults to None + ngrams: tokenize into ngrams, defaults to None, supports int or dict + """ + + # Lowercase + self.lowercase = lowercase + + # Text segmentation + self.alphanum, self.whitespace, self.regexp, self.ngrams, self.segment = None, whitespace, None, None, None + if alphanum: + # Alphanumeric regex that accepts tokens that meet following rules: + # - Strings to be at least 2 characters long AND + # - At least 1 non-trailing alpha character in string + # Note: The standard Python re module is much faster than regex for this expression + self.alphanum = re.compile(r"^\d*[a-z][\-.0-9:_a-z]{1,}$") + elif regexp: + # Regular expression for tokenization + self.regexp = regex.compile(regexp) + elif ngrams: + # Ngram tokenization configuration + self.ngrams = ngrams if isinstance(ngrams, dict) else {"ngrams": ngrams} + else: + # Text segmentation per Unicode Standard Annex #29 + pattern = r"\w\p{Extended_Pictographic}\p{WB:RegionalIndicator}" if emoji else r"\w" + self.segment = regex.compile(rf"[{pattern}](?:\B\S)*", flags=regex.WORD) + + # Stop words + self.stopwords = stopwords if isinstance(stopwords, list) else Tokenizer.STOP_WORDS if stopwords else False + + def __call__(self, text): + """ + Tokenizes text into a list of tokens. + + Args: + text: input text + + Returns: + list of tokens + """ + + # Check for None and skip processing + if text is None: + return None + + # Lowercase + text = text.lower() if self.lowercase else text + + if self.alphanum: + # Text segmentation using standard split + tokens = [token.strip(string.punctuation) for token in text.split()] + + # Filter on alphanumeric strings. + tokens = [token for token in tokens if re.match(self.alphanum, token)] + elif self.whitespace: + # Text segmentation using whitespace + tokens = text.split() + elif self.regexp: + # Text segmentation using a custom regular expression + tokens = regex.findall(self.regexp, text) + elif self.ngrams: + # Ngram tokenizer + tokens = self.ngramtokenize(text) + else: + # Text segmentation per Unicode Standard Annex #29 + tokens = regex.findall(self.segment, text) + + # Stop words + if self.stopwords: + tokens = [token for token in tokens if token not in self.stopwords] + + return tokens + + def ngramtokenize(self, text): + """ + Tokenizes input text into ngrams. + + Args: + text: input text + + Return: + [ngrams] + """ + + # Ngram configuration + number = self.ngrams.get("ngrams", 3) + nmin = self.ngrams.get("nmin", number) + nmax = self.ngrams.get("nmax", number) + + lpad = self.ngrams.get("lpad", "") + rpad = self.ngrams.get("rpad", "") + unique = self.ngrams.get("unique", False) + edge = self.ngrams.get("edge", False) + + # Split on non-whitespace and apply optional word padding + words = [f"{lpad}{x}{rpad}" for x in re.split(r"\W+", text) if x.strip()] + + # Generate ngrams + ngrams = [] + for word in words: + for n in range(nmin, min(nmax, len(word)) + 1): + for x in range(0, len(word) - n + 1): + if not edge or x == 0: + ngrams.append(word[x : x + n]) + + # Reduce to unique ngrams, if necessary and return + return list(set(ngrams)) if unique else ngrams diff --git a/src/python/txtai/pipeline/data/urlretrieve.py b/src/python/txtai/pipeline/data/urlretrieve.py new file mode 100644 index 0000000..a6b2f7b --- /dev/null +++ b/src/python/txtai/pipeline/data/urlretrieve.py @@ -0,0 +1,254 @@ +""" +URLRetrieve module +""" + +import contextlib +import ipaddress +import socket + +from http.client import HTTPConnection, HTTPSConnection +from urllib.parse import urlparse +from urllib.request import ( + HTTPDefaultErrorHandler, + HTTPErrorProcessor, + HTTPHandler, + HTTPSHandler, + HTTPRedirectHandler, + OpenerDirector, + Request, + UnknownHandler, +) + +from ..base import Pipeline + + +class URLRetrieve(Pipeline): + """ + Retrieves content from HTTP(s) URLs. + """ + + def __init__(self, headers=None, safeopen=False, timeout=30, readlimit=100 * 1024 * 1024): + """ + Creates a new URLRetrieve pipeline. + + Args: + headers: http headers + safeopen: if safe validation checks should be enabled + timeout: default socket timeout + readlimit: default read limit + """ + + # HTTP headers + self.headers = headers if headers else {} + + # Safeopen mode + self.safeopen = safeopen + + # Socket timeout + self.timeout = timeout + + # Read limit + self.readlimit = readlimit + + # Create a blank opener + self.opener = OpenerDirector() + + # Register handlers + for handler in [ + UnknownHandler(), + HTTPDefaultErrorHandler(), + HTTPErrorProcessor(), + SafeHTTPHandler(self), + SafeHTTPSHandler(self), + SafeRedirectHandler(self), + ]: + self.opener.add_handler(handler) + + def __call__(self, url): + """ + Retrieves content from url. + + Args: + url: input url + + Returns: + data + """ + + with contextlib.closing(self.opener.open(Request(url, headers=self.headers), timeout=self.timeout)) as connection: + # Read up to readlimit bytes + return connection.read(self.readlimit) + + def isprivateurl(self, url): + """ + Checks if URL refers to a private/internal IP address. + + Args: + url: input url + + Returns: + True if this is a private url, false otherwise + """ + + # Assume the url is private until proven otherwise + private = True + + try: + host = urlparse(url).hostname + if host: + _, private = self.resolveip(host) + + # pylint: disable=W0718 + except Exception: + pass + + return private + + def resolveip(self, host): + """ + Resolves the IP Address for host. + + Args: + host: host name + + Returns: + (ip address, True if this is a private ip) + """ + + # Resolve IP Address + infos = socket.getaddrinfo(host, None, type=socket.SOCK_STREAM) + + # Collect all IP Addresses + addresses = [] + for family, _, _, _, sockaddr in infos: + ip = sockaddr[0] + addresses.append((family, ip, not ipaddress.ip_address(ip).is_global)) + + # Prefer IPv4 + private=False + _, ip, private = sorted(addresses, key=lambda x: (x[2], x[0]))[0] + + return (ip, private) + + +class SafeHTTPConnection(HTTPConnection): + """ + HTTPConnection that pins to a supplied IP. + """ + + def __init__(self, host, ip, **kwargs): + super().__init__(host, **kwargs) + self.ip = ip + + def connect(self): + self.sock = socket.create_connection( + (self.ip, self.port), + self.timeout, + self.source_address, + ) + + +class SafeHTTPSConnection(HTTPSConnection): + """ + HTTPSConnection that pins to a supplied IP. + """ + + def __init__(self, host, ip, **kwargs): + super().__init__(host, **kwargs) + self.ip = ip + + def connect(self): + raw = socket.create_connection( + (self.ip, self.port), + self.timeout, + self.source_address, + ) + + self.sock = self._context.wrap_socket(raw, server_hostname=self.host) + + +class SafeHTTPHandler(HTTPHandler): + """ + SafeHTTPHandler that validates and pins a connection to an IP. + """ + + def __init__(self, instance): + """ + Stores the current instance. + + Args: + instance: validation instance + """ + + # Call parent constructor + super().__init__() + + self.instance = instance + + def http_open(self, req): + host = urlparse(req.full_url).hostname + + # Validate and pin the IP Address + ip, private = self.instance.resolveip(host) + if self.instance.safeopen and private: + raise IOError(f"Safeopen URL validation failed: host={host}") + + return self.do_open( + lambda host, **kwargs: SafeHTTPConnection(host, ip, **kwargs), + req, + ) + + +class SafeHTTPSHandler(HTTPSHandler): + """ + SafeHTTPSHandler that validates and pins a connection to an IP. + """ + + def __init__(self, instance): + """ + Stores the current instance. + + Args: + instance: validation instance + """ + + # Call parent constructor + super().__init__() + + self.instance = instance + + def https_open(self, req): + host = urlparse(req.full_url).hostname + + # Validate and pin the IP Address + ip, private = self.instance.resolveip(host) + if self.instance.safeopen and private: + raise IOError(f"Safeopen URL validation failed: host={host}") + + return self.do_open( + lambda host, **kwargs: SafeHTTPSConnection(host, ip, **kwargs), + req, + ) + + +class SafeRedirectHandler(HTTPRedirectHandler): + """ + Custom HTTPRedirectHandler that runs safe open url validation for each redirect hop. + """ + + def __init__(self, instance): + """ + Stores the current instance. + + Args: + instance: validation instance + """ + + self.instance = instance + + def redirect_request(self, req, fp, code, msg, headers, newurl): + # Validate redirects are safe + if self.instance.safeopen and self.instance.isprivateurl(newurl): + raise IOError(f"Safeopen URL validation failed: url={newurl}") + + # Pass through when URL is safe + return super().redirect_request(req, fp, code, msg, headers, newurl) diff --git a/src/python/txtai/pipeline/factory.py b/src/python/txtai/pipeline/factory.py new file mode 100644 index 0000000..180e81a --- /dev/null +++ b/src/python/txtai/pipeline/factory.py @@ -0,0 +1,77 @@ +""" +Pipeline factory module +""" + +import inspect +import sys +import types + +from ..util import Resolver + +from .base import Pipeline + + +class PipelineFactory: + """ + Pipeline factory. Creates new Pipeline instances. + """ + + @staticmethod + def get(pipeline): + """ + Gets a new instance of pipeline class. + + Args: + pclass: Pipeline instance class + + Returns: + Pipeline class + """ + + # Local pipeline if no package + if "." not in pipeline: + return PipelineFactory.list()[pipeline] + + # Attempt to load custom pipeline + return Resolver()(pipeline) + + @staticmethod + def create(config, pipeline): + """ + Creates a new Pipeline instance. + + Args: + config: Pipeline configuration + pipeline: Pipeline instance class + + Returns: + Pipeline + """ + + # Resolve pipeline + pipeline = PipelineFactory.get(pipeline) + + # Return functions directly, otherwise create pipeline instance + return pipeline if isinstance(pipeline, types.FunctionType) else pipeline(**config) + + @staticmethod + def list(): + """ + Lists callable pipelines. + + Returns: + {short name: pipeline class} + """ + + pipelines = {} + + # Get handle to pipeline module + pipeline = sys.modules[".".join(__name__.split(".")[:-1])] + + # Get list of callable pipelines + for x in inspect.getmembers(pipeline, inspect.isclass): + if issubclass(x[1], Pipeline) and [y for y, _ in inspect.getmembers(x[1], inspect.isfunction) if y == "__call__"]: + # short name: pipeline class + pipelines[x[0].lower()] = x[1] + + return pipelines diff --git a/src/python/txtai/pipeline/hfmodel.py b/src/python/txtai/pipeline/hfmodel.py new file mode 100644 index 0000000..db5f3af --- /dev/null +++ b/src/python/txtai/pipeline/hfmodel.py @@ -0,0 +1,143 @@ +""" +Hugging Face Transformers model wrapper module +""" + +from ..models import Models +from ..util import Library + +from .tensors import Tensors + +# Core library imports +transformers = Library().transformers() + + +class HFModel(Tensors): + """ + Pipeline backed by a Hugging Face Transformers model. + """ + + def __init__(self, path=None, quantize=False, gpu=False, batch=64): + """ + Creates a new HFModel. + + Args: + path: optional path to model, accepts Hugging Face model hub id or local path, + uses default model for task if not provided + quantize: if model should be quantized, defaults to False + gpu: True/False if GPU should be enabled, also supports a GPU device id + batch: batch size used to incrementally process content + """ + + # Default model path + self.path = path + + # Quantization flag + self.quantization = quantize + + # Get tensor device reference + self.deviceid = Models.deviceid(gpu) + self.device = Models.device(self.deviceid) + + # Process batch size + self.batchsize = batch + + def load(self, path, task, **kwargs): + """ + Loads a HuggingFace model and tokenizer. + + Args: + path: model path + task: model task + kwargs: additional keyword args + """ + + # Unpack path + if isinstance(path, tuple): + model, tokenizer = path + else: + model, tokenizer = path, path + + # Load model + model = Models.load(model, task=task, modelargs=kwargs).to(self.device) + + # Apply model initialization routines + model = self.prepare(model) + + # Load tokenizer + tokenizer = transformers.AutoTokenizer.from_pretrained(tokenizer) if isinstance(tokenizer, str) else tokenizer + + return model, tokenizer + + def prepare(self, model): + """ + Prepares a model for processing. Applies dynamic quantization if necessary. + + Args: + model: input model + + Returns: + model + """ + + if self.deviceid == -1 and self.quantization: + model = self.quantize(model) + + return model + + def tokenize(self, tokenizer, texts): + """ + Tokenizes text using tokenizer. This method handles overflowing tokens and automatically splits + them into separate elements. Indices of each element is returned to allow reconstructing the + transformed elements after running through the model. + + Args: + tokenizer: Tokenizer + texts: list of text + + Returns: + (tokenization result, indices) + """ + + # Pre-process and split on newlines + batch, positions = [], [] + for x, text in enumerate(texts): + elements = [t + " " for t in text.split("\n") if t] + batch.extend(elements) + positions.extend([x] * len(elements)) + + # Run tokenizer + tokens = tokenizer(batch, padding=True) + + inputids, attention, indices = [], [], [] + for x, ids in enumerate(tokens["input_ids"]): + if len(ids) > tokenizer.model_max_length: + # Remove padding characters, if any + ids = [i for i in ids if i != tokenizer.pad_token_id] + + # Split into model_max_length chunks + for chunk in self.batch(ids, tokenizer.model_max_length - 1): + # Append EOS token if necessary + if chunk[-1] != tokenizer.eos_token_id: + chunk.append(tokenizer.eos_token_id) + + # Set attention mask + mask = [1] * len(chunk) + + # Append padding if necessary + if len(chunk) < tokenizer.model_max_length: + pad = tokenizer.model_max_length - len(chunk) + chunk.extend([tokenizer.pad_token_id] * pad) + mask.extend([0] * pad) + + inputids.append(chunk) + attention.append(mask) + indices.append(positions[x]) + else: + inputids.append(ids) + attention.append(tokens["attention_mask"][x]) + indices.append(positions[x]) + + tokens = {"input_ids": inputids, "attention_mask": attention} + + # pylint: disable=E1102 + return ({name: self.tensor(tensor).to(self.device) for name, tensor in tokens.items()}, indices) diff --git a/src/python/txtai/pipeline/hfpipeline.py b/src/python/txtai/pipeline/hfpipeline.py new file mode 100644 index 0000000..c125a5c --- /dev/null +++ b/src/python/txtai/pipeline/hfpipeline.py @@ -0,0 +1,88 @@ +""" +Hugging Face Transformers pipeline wrapper module +""" + +import inspect + + +from ..models import Models +from ..util import Library, Resolver + +from .tensors import Tensors + +# Core library imports +transformers = Library().transformers() + + +class HFPipeline(Tensors): + """ + Light wrapper around Hugging Face Transformers pipeline component for selected tasks. Adds support for model + quantization and minor interface changes. + """ + + def __init__(self, task, path=None, quantize=False, gpu=False, model=None, **kwargs): + """ + Loads a new pipeline model. + + Args: + task: pipeline task or category + path: optional path to model, accepts Hugging Face model hub id, local path or (model, tokenizer) tuple. + uses default model for task if not provided + quantize: if model should be quantized, defaults to False + gpu: True/False if GPU should be enabled, also supports a GPU device id + model: optional existing pipeline model to wrap + kwargs: additional keyword arguments to pass to pipeline model + """ + + if model: + # Check if input model is a Pipeline or a HF pipeline + self.pipeline = model.pipeline if isinstance(model, HFPipeline) else model + else: + # Get device + deviceid = Models.deviceid(gpu) if "device_map" not in kwargs else None + device = Models.device(deviceid) if deviceid is not None else None + + # Split into model args, pipeline args + modelargs, kwargs = self.parseargs(**kwargs) + + # Transformer pipeline task + if isinstance(path, (list, tuple)): + # Derive configuration, if possible + config = path[1] if path[1] and isinstance(path[1], str) else None + + # Load model + model = Models.load(path[0], config, task) + + self.pipeline = transformers.pipeline(task, model=model, tokenizer=path[1], device=device, model_kwargs=modelargs, **kwargs) + else: + self.pipeline = transformers.pipeline(task, model=path, device=device, model_kwargs=modelargs, **kwargs) + + # Model quantization. Compresses model to int8 precision, improves runtime performance. Only supported on CPU. + if deviceid == -1 and quantize: + # pylint: disable=E1101 + self.pipeline.model = self.quantize(self.pipeline.model) + + # Detect unbounded tokenizer typically found in older models + Models.checklength(self.pipeline.model, self.pipeline.tokenizer) + + def parseargs(self, **kwargs): + """ + Inspects the pipeline method and splits kwargs into model args and pipeline args. + + Args: + kwargs: all keyword arguments + + Returns: + (model args, pipeline args) + """ + + # Get pipeline method arguments + args = inspect.getfullargspec(transformers.pipeline).args + + # Resolve torch dtype, if necessary + dtype = kwargs.get("dtype") + if dtype and isinstance(dtype, str) and dtype != "auto": + kwargs["dtype"] = Resolver()(dtype) + + # Split into modelargs and kwargs + return ({arg: value for arg, value in kwargs.items() if arg not in args}, {arg: value for arg, value in kwargs.items() if arg in args}) diff --git a/src/python/txtai/pipeline/image/__init__.py b/src/python/txtai/pipeline/image/__init__.py new file mode 100644 index 0000000..1cdc0ec --- /dev/null +++ b/src/python/txtai/pipeline/image/__init__.py @@ -0,0 +1,7 @@ +""" +Image imports +""" + +from .caption import Caption +from .imagehash import ImageHash +from .objects import Objects diff --git a/src/python/txtai/pipeline/image/caption.py b/src/python/txtai/pipeline/image/caption.py new file mode 100644 index 0000000..cc54c62 --- /dev/null +++ b/src/python/txtai/pipeline/image/caption.py @@ -0,0 +1,79 @@ +""" +Caption module +""" + +# Conditional import +try: + from PIL import Image + + PIL = True +except ImportError: + PIL = False + +from ..hfmodel import HFModel + +# Core library imports +from ...util import Library + +transformers = Library().transformers() + + +class Caption(HFModel): + """ + Constructs captions for images. + """ + + def __init__(self, path=None, quantize=False, gpu=True, batch=64, **kwargs): + if not PIL: + raise ImportError('Captions pipeline is not available - install "pipeline" extra to enable') + + # Default model + path = path if path else "ydshieh/vit-gpt2-coco-en" + + # Call parent constructor + super().__init__(path, quantize, gpu, batch) + + # Captioning model + if isinstance(path, tuple): + self.model, self.tokenizer, self.processor = path + else: + self.model = transformers.AutoModelForImageTextToText.from_pretrained(path, **kwargs) + self.tokenizer = transformers.AutoTokenizer.from_pretrained(path) + self.processor = transformers.AutoImageProcessor.from_pretrained(path) + + # Move model to device + self.model = self.model.to(self.device) + + def __call__(self, images): + """ + Builds captions for images. + + This method supports a single image or a list of images. If the input is an image, the return + type is a string. If text is a list, a list of strings is returned + + Args: + images: image|list + + Returns: + list of captions + """ + + # Convert single element to list + values = [images] if not isinstance(images, list) else images + + # Open images if file strings + values = [Image.open(image) if isinstance(image, str) else image for image in values] + + # Get and clean captions + captions = [] + for image in values: + # Extract pixels + pixels = self.processor(images=image, return_tensors="pt").to(self.device).pixel_values + + # Generate the caption + outputs = self.model.generate(pixel_values=pixels, max_new_tokens=256) + caption = self.tokenizer.batch_decode(outputs, skip_special_tokens=True)[0].strip() + captions.append(caption) + + # Return single element if single element passed in + return captions[0] if not isinstance(images, list) else captions diff --git a/src/python/txtai/pipeline/image/imagehash.py b/src/python/txtai/pipeline/image/imagehash.py new file mode 100644 index 0000000..41ff40e --- /dev/null +++ b/src/python/txtai/pipeline/image/imagehash.py @@ -0,0 +1,93 @@ +""" +ImageHash module +""" + +# Conditional import +try: + from PIL import Image + import imagehash + + PIL = True +except ImportError: + PIL = False + +from ..base import Pipeline + +# Core library imports +from ...util import Library + +np = Library().numpy() + + +class ImageHash(Pipeline): + """ + Generates perceptual image hashes. These hashes can be used to detect near-duplicate images. This method is not + backed by machine learning models and not intended to find conceptually similar images. + """ + + def __init__(self, algorithm="average", size=8, strings=True): + """ + Creates an ImageHash pipeline. + + Args: + algorithm: image hashing algorithm (average, perceptual, difference, wavelet, color) + size: hash size + strings: outputs hex strings if True (default), otherwise the pipeline returns numpy arrays + """ + + if not PIL: + raise ImportError('ImageHash pipeline is not available - install "pipeline" extra to enable') + + self.algorithm = algorithm + self.size = size + self.strings = strings + + def __call__(self, images): + """ + Generates perceptual image hashes. + + Args: + images: image|list + + Returns: + list of hashes + """ + + # Convert single element to list + values = [images] if not isinstance(images, list) else images + + # Open images if file strings + values = [Image.open(image) if isinstance(image, str) else image for image in values] + + # Convert images to hashes + hashes = [self.ihash(image) for image in values] + + # Return single element if single element passed in + return hashes[0] if not isinstance(images, list) else hashes + + def ihash(self, image): + """ + Gets an image hash for image. + + Args: + image: PIL image + + Returns: + hash as hex string + """ + + # Apply hash algorithm + if self.algorithm == "perceptual": + data = imagehash.phash(image, self.size) + elif self.algorithm == "difference": + data = imagehash.dhash(image, self.size) + elif self.algorithm == "wavelet": + data = imagehash.whash(image, self.size) + elif self.algorithm == "color": + data = imagehash.colorhash(image, self.size) + else: + # Default to average hash + data = imagehash.average_hash(image, self.size) + + # Convert to output data type + return str(data) if self.strings else data.hash.astype(np.float32).reshape(-1) diff --git a/src/python/txtai/pipeline/image/objects.py b/src/python/txtai/pipeline/image/objects.py new file mode 100644 index 0000000..0dbaff4 --- /dev/null +++ b/src/python/txtai/pipeline/image/objects.py @@ -0,0 +1,80 @@ +""" +Objects module +""" + +# Conditional import +try: + from PIL import Image + + PIL = True +except ImportError: + PIL = False + +from ..hfpipeline import HFPipeline + + +class Objects(HFPipeline): + """ + Applies object detection models to images. Supports both object detection models and image classification models. + """ + + def __init__(self, path=None, quantize=False, gpu=True, model=None, classification=False, threshold=0.9, **kwargs): + if not PIL: + raise ImportError('Objects pipeline is not available - install "pipeline" extra to enable') + + super().__init__("image-classification" if classification else "object-detection", path, quantize, gpu, model, **kwargs) + + self.classification = classification + self.threshold = threshold + + def __call__(self, images, flatten=False, workers=0): + """ + Applies object detection/image classification models to images. Returns a list of (label, score). + + This method supports a single image or a list of images. If the input is an image, the return + type is a 1D list of (label, score). If text is a list, a 2D list of (label, score) is + returned with a row per image. + + Args: + images: image|list + flatten: flatten output to a list of objects + workers: number of concurrent workers to use for processing data, defaults to None + + Returns: + list of (label, score) + """ + + # Convert single element to list + values = [images] if not isinstance(images, list) else images + + # Open images if file strings + values = [Image.open(image) if isinstance(image, str) else image for image in values] + + # Run pipeline + results = ( + self.pipeline(values, num_workers=workers) + if self.classification + else self.pipeline(values, threshold=self.threshold, num_workers=workers) + ) + + # Build list of (id, score) + outputs = [] + for result in results: + # Convert to (label, score) tuples + result = [(x["label"], x["score"]) for x in result if x["score"] > self.threshold] + + # Sort by score descending + result = sorted(result, key=lambda x: x[1], reverse=True) + + # Deduplicate labels + unique = set() + elements = [] + for label, score in result: + if label not in unique: + elements.append(label if flatten else (label, score)) + unique.add(label) + + outputs.append(elements) + + # Return single element if single element passed in + return outputs[0] if not isinstance(images, list) else outputs diff --git a/src/python/txtai/pipeline/llm/__init__.py b/src/python/txtai/pipeline/llm/__init__.py new file mode 100644 index 0000000..3f8ba80 --- /dev/null +++ b/src/python/txtai/pipeline/llm/__init__.py @@ -0,0 +1,13 @@ +""" +LLM imports +""" + +from .factory import GenerationFactory +from .generation import Generation +from .huggingface import * +from .litellm import LiteLLM +from .litert import LiteRT +from .llama import LlamaCpp +from .llm import LLM +from .opencode import OpenCode +from .rag import RAG diff --git a/src/python/txtai/pipeline/llm/factory.py b/src/python/txtai/pipeline/llm/factory.py new file mode 100644 index 0000000..1f90575 --- /dev/null +++ b/src/python/txtai/pipeline/llm/factory.py @@ -0,0 +1,100 @@ +""" +Factory module +""" + +from ...util import Resolver + +from .huggingface import HFGeneration +from .litellm import LiteLLM +from .litert import LiteRT +from .llama import LlamaCpp +from .opencode import OpenCode + + +class GenerationFactory: + """ + Methods to create generative models. + """ + + @staticmethod + def create(path, method, **kwargs): + """ + Creates a new Generation instance. + + Args: + path: model path + method: llm framework + kwargs: model keyword arguments + """ + + # Derive method + method = GenerationFactory.method(path, method) + + # LiteLLM generation + if method == "litellm": + return LiteLLM(path, **kwargs) + + # LiteRT generation + if method == "litert": + return LiteRT(path, **kwargs) + + # llama.cpp generation + if method == "llama.cpp": + return LlamaCpp(path, **kwargs) + + # OpenCode generation + if method == "opencode": + return OpenCode(path, **kwargs) + + # Hugging Face Transformers generation + if method == "transformers": + return HFGeneration(path, **kwargs) + + # Resolve custom method + return GenerationFactory.resolve(path, method, **kwargs) + + @staticmethod + def method(path, method): + """ + Get or derives the LLM framework. + + Args: + path: model path + method: llm framework + + Return: + llm framework + """ + + if not method: + if LiteLLM.ismodel(path): + method = "litellm" + elif LiteRT.ismodel(path): + method = "litert" + elif LlamaCpp.ismodel(path): + method = "llama.cpp" + elif OpenCode.ismodel(path): + method = "opencode" + else: + method = "transformers" + + return method + + @staticmethod + def resolve(path, method, **kwargs): + """ + Attempt to resolve a custom LLM framework. + + Args: + path: model path + method: llm framework + kwargs: model keyword arguments + + Returns: + Generation instance + """ + + try: + return Resolver()(method)(path, **kwargs) + except Exception as e: + raise ImportError(f"Unable to resolve generation framework: '{method}'") from e diff --git a/src/python/txtai/pipeline/llm/generation.py b/src/python/txtai/pipeline/llm/generation.py new file mode 100644 index 0000000..6bd4997 --- /dev/null +++ b/src/python/txtai/pipeline/llm/generation.py @@ -0,0 +1,248 @@ +""" +Generation module +""" + +import re + +from ...util import TemplateFormatter + + +class Generation: + """ + Base class for generative models. This class has common logic for building prompts and cleaning model results. + """ + + def __init__(self, path=None, template=None, **kwargs): + """ + Creates a new Generation instance. + + Args: + path: model path + template: prompt template + kwargs: additional keyword arguments + """ + + self.path = path + self.template = template + self.kwargs = kwargs + + def __call__(self, text, maxlength, stream, stop, defaultrole, stripthink, **kwargs): + """ + Generates content. Supports the following input formats: + + - String or list of strings (instruction-tuned models must follow chat templates) + - List of dictionaries with `role` and `content` key-values or lists of lists + + Args: + text: text|list + maxlength: maximum sequence length + stream: stream response if True, defaults to False + stop: list of stop strings + defaultrole: default role to apply to text inputs (`auto` to infer (default), `user` for user chat messages or `prompt` for raw prompts) + stripthink: strip thinking tags, defaults to False if stream is enabled, True otherwise + kwargs: additional generation keyword arguments + + Returns: + generated content + """ + + # Format inputs + texts = [text] if isinstance(text, str) or isinstance(text[0], dict) else text + + # Apply template, if necessary + if self.template: + formatter = TemplateFormatter() + texts = [formatter.format(self.template, text=x) if isinstance(x, str) else x for x in texts] + + # Run pipeline + results = self.execute(self.format(texts, defaultrole), maxlength, stream, stop, **kwargs) + + # Streaming generation + if stream: + return self.cleanstream(results) if stripthink else results + + # Clean generated content + results = [self.clean(texts[x], result, stripthink) for x, result in enumerate(results)] + + # Extract results based on inputs + return results[0] if isinstance(text, str) or isinstance(text[0], dict) else results + + def ischat(self): + """ + Returns True if this LLM supports chat. + + Returns: + True if this a chat model + """ + + return True + + def isvision(self): + """ + Returns True if this LLM supports vision. + + Returns: + True if this is a vision model + """ + + return False + + def format(self, texts, defaultrole): + """ + Formats inputs for LLM inference. This method handles wrapping string inputs as chat messages using the following rules. + + - defaultrole == "user" OR + - defaultrole == "auto" AND model supports chat AND text doesn't start with an instruction token + + Args: + texts: list of inputs + defaultrole: default role to apply to text inputs (`auto` to infer (default), `user` for user chat messages or `prompt` for raw prompts) + + Returns: + inputs ready for inference + """ + + # Instruction tokens + instruct = ("<|im_start|>", "<|start|>", "<|start_of_role|>", "[INST]") + + results = [] + for text in texts: + # Format chat messages using following rules + # - defaultrole == "user" + # - defaultrole == "auto" and text doesn't start with a instruction token + if isinstance(text, str) and ( + defaultrole == "user" or (defaultrole == "auto" and self.ischat() and not text.strip().startswith(instruct)) + ): + text = [{"role": "user", "content": text}] + + results.append(text) + + return results + + def execute(self, texts, maxlength, stream, stop, **kwargs): + """ + Runs a list of prompts through a generative model. + + Args: + texts: list of prompts to run + maxlength: maximum sequence length + stream: stream response if True, defaults to False + stop: list of stop strings + kwargs: additional generation keyword arguments + + Returns: + generated content + """ + + # Streaming generation + if stream: + return self.stream(texts, maxlength, stream, stop, **kwargs) + + # Full response as content elements + return list(self.stream(texts, maxlength, stream, stop, **kwargs)) + + def clean(self, prompt, result, stripthink): + """ + Applies a series of rules to clean generated content. + + Args: + prompt: original input prompt + result: result text + stripthink: removes thinking text if true + + Returns: + clean content + """ + + # Replace input prompt + text = result.replace(prompt, "") if isinstance(prompt, str) else result + + # Replace thinking text, if necessary + if stripthink: + text = self.cleanthink(text) + + # Apply text cleaning rules + return text.replace("$=", "<=").strip() + + def cleanstream(self, results): + """ + Cleans thinking tokens from streaming results and streams the remaining results. + + Args: + results: results stream + """ + + # Consume "thinking" tokens + text, buffer = None, "" + for chunk in results: + buffer += chunk + text = self.cleanthink(buffer) + if text != buffer: + break + + # Yield remaining tokens + yield from text.lstrip() + yield from results + + def cleanthink(self, text): + """ + Clean thinking tokens from text. + + Args: + text: input text + + Returns: + text with thinking tokens removed + """ + + text = re.sub(r"(?s).+?", "", text) + text = text.split("<|channel|>final<|message|>", 1) + text = text[1] if len(text) > 1 else text[0] + return text + + def response(self, result): + """ + Parses response content from the result. This supports both standard and streaming + generation. + + For standard generation, the full response is returned. For streaming generation, + this method will stream chunks of content. + + Args: + result: LLM response + + Returns: + response + """ + + streamed = False + for chunk in result: + # Expects one of the following parameter paths + # - text + # - message.content + # - delta.content + data = chunk["choices"][0] + text = data.get("text", data.get("message", data.get("delta"))) + text = text if isinstance(text, str) else text.get("content") + + # Yield result if there is text AND it's not leading stream whitespace + if text is not None and (streamed or text.strip()): + yield (text.lstrip() if not streamed else text) + streamed = True + + def stream(self, texts, maxlength, stream, stop, **kwargs): + """ + Streams LLM responses. + + Args: + texts: list of prompts to run + maxlength: maximum sequence length + stream: stream response if True, defaults to False + stop: list of stop strings + kwargs: additional generation keyword arguments + + Returns: + responses + """ + + raise NotImplementedError diff --git a/src/python/txtai/pipeline/llm/huggingface.py b/src/python/txtai/pipeline/llm/huggingface.py new file mode 100644 index 0000000..b54d2b9 --- /dev/null +++ b/src/python/txtai/pipeline/llm/huggingface.py @@ -0,0 +1,303 @@ +""" +Hugging Face module +""" + +from threading import Thread + +from ...models import Models + +from ..hfmodel import HFModel +from ..hfpipeline import HFPipeline + +from .generation import Generation + +# Core library imports +from ...util import Library + +transformers = Library().transformers() + + +class HFGeneration(Generation): + """ + Hugging Face Transformers generative model. + """ + + def __init__(self, path, template=None, **kwargs): + # Call parent constructor + super().__init__(path, template, **kwargs) + + # Create HuggingFace LLM pipeline + self.llm = HFLLM(path, **kwargs) + + def ischat(self): + return self.llm.ischat() + + def isvision(self): + return self.llm.isvision() + + def stream(self, texts, maxlength, stream, stop, **kwargs): + yield from self.llm(texts, maxlength=maxlength, stream=stream, stop=stop, **kwargs) + + +class HFLLM(HFPipeline): + """ + Hugging Face Transformers large language model (LLM) pipeline. This pipeline autodetects if the model path + is a text generation or sequence to sequence model. + """ + + def __init__(self, path=None, quantize=False, gpu=True, model=None, task=None, **kwargs): + task = self.task(path, task, **kwargs) + + if task == "text2text-generation": + # Backwards compatible sequences pipeline + self.pipeline = SequencesPipeline(path, quantize, gpu) + else: + # Supported transformers pipeline + super().__init__(task, path, quantize, gpu, model, **kwargs) + + # Load tokenizer, if necessary + self.pipeline.tokenizer = self.pipeline.tokenizer if self.pipeline.tokenizer else Models.tokenizer(path, **kwargs) + + def __call__(self, text, prefix=None, maxlength=512, workers=0, stream=False, stop=None, **kwargs): + """ + Generates content. Supports the following input formats: + + - String or list of strings (instruction-tuned models must follow chat templates) + - List of dictionaries with `role` and `content` key-values or lists of lists + + Args: + text: text|list + prefix: optional prefix to prepend to text elements + maxlength: maximum sequence length + workers: number of concurrent workers to use for processing data, defaults to None + stream: stream response if True, defaults to False + stop: list of stop strings + kwargs: additional generation keyword arguments + + Returns: + generated content + """ + + # List of texts + texts = text if isinstance(text, list) else [text] + + # Add prefix, if necessary + if prefix: + texts = [f"{prefix}{x}" for x in texts] + + # Combine all keyword arguments + args, kwargs = self.parameters(texts, maxlength, workers, stop, **kwargs) + + # Stream response + if stream: + return StreamingResponse(self.pipeline, texts, stop, **kwargs)() + + # Run pipeline and extract generated text + results = [self.extract(result) for result in self.pipeline(*args, **kwargs)] + + return results[0] if isinstance(text, str) else results + + def ischat(self): + """ + Returns True if this model supports chat. + + Returns: + True if this a chat model + """ + + return self.pipeline.tokenizer.chat_template is not None + + def isvision(self): + """ + Returns True if this model supports vision. + + Returns: + True if this is a vision model + """ + + return isinstance(self.pipeline.model, transformers.AutoModelForImageTextToText) + + def parameters(self, texts, maxlength, workers, stop, **kwargs): + """ + Builds a list of arguments and a combined parameter dictionary to use as keyword arguments. + + Args: + texts: input texts + maxlength: maximum sequence length + workers: number of concurrent workers to use for processing data, defaults to None + stop: list of stop strings + kwargs: additional generation keyword arguments + + Returns: + args, kwargs + """ + + # Set defaults and get underlying model + defaults, model = {"max_length": maxlength, "max_new_tokens": None, "num_workers": workers}, self.pipeline.model + + # Set parameters for vision models and return + if self.pipeline.task == "image-text-to-text": + # Maxlength has to be large enough to accomodate images + defaults["max_length"] = max(maxlength, 2048) + + # Set default token id + tokenid = model.generation_config.pad_token_id + model.generation_config.pad_token_id = tokenid if tokenid else model.generation_config.eos_token_id + + # Vision models take all arguments as keyword arguments + return [], {**{"text": texts, "truncation": True}, **defaults, **kwargs} + + # Add pad token if it's missing from model config + if not model.config.pad_token_id: + tokenid = model.config.eos_token_id + tokenid = tokenid[0] if isinstance(tokenid, list) else tokenid + + # Set pad_token_id parameter + defaults["pad_token_id"] = tokenid + + # Update tokenizer for batching + if "batch_size" in kwargs and self.pipeline.tokenizer.pad_token_id is None: + self.pipeline.tokenizer.pad_token_id = tokenid + self.pipeline.tokenizer.padding_side = "left" + + # Set tokenizer when stop strings is set + if stop: + defaults["tokenizer"] = self.pipeline.tokenizer + + return [texts], {**defaults, **kwargs} + + def extract(self, result): + """ + Extracts generated content from a pipeline result. + + Args: + result: pipeline result + + Returns: + generated content + """ + + # Extract output from list, if necessary + result = result[0] if isinstance(result, list) else result + text = result["generated_text"] + return text[-1]["content"] if isinstance(text, list) else text + + def task(self, path, task, **kwargs): + """ + Get the pipeline task name. + + Args: + path: model path input + task: task name + kwargs: optional additional keyword arguments + + Returns: + pipeline task name + """ + + # Mapping from txtai to Hugging Face pipeline tasks + mapping = {"language-generation": "text-generation", "sequence-sequence": "text2text-generation", "vision": "image-text-to-text"} + + # Attempt to resolve task + if path and not task: + task = Models.task(path, **kwargs) + + # Map to Hugging Face task. Default to text2text-generation pipeline when task not resolved. + return mapping.get(task, "text2text-generation") + + +class Generator(HFLLM): + """ + Generates content with a causal language model. + """ + + def __init__(self, path=None, quantize=False, gpu=True, model=None, **kwargs): + super().__init__(path, quantize, gpu, model, "language-generation", **kwargs) + + +class Sequences(HFLLM): + """ + Generates content with a sequence-sequence model. + """ + + def __init__(self, path=None, quantize=False, gpu=True, model=None, **kwargs): + super().__init__(path, quantize, gpu, model, "sequence-sequence", **kwargs) + + +class StreamingResponse: + """ + Generates content as a streaming response. + """ + + def __init__(self, pipeline, texts, stop, **kwargs): + # Create streamer + self.stream = transformers.TextIteratorStreamer(pipeline.tokenizer, skip_prompt=True, skip_special_tokens=True, timeout=5) + kwargs["streamer"] = self.stream + kwargs["stop_strings"] = stop + + # Create thread + self.thread = Thread(target=pipeline, args=[texts], kwargs=kwargs) + + # Store number of inputs + self.length = len(texts) + + def __call__(self): + # Start the process + self.thread.start() + + return self + + def __iter__(self): + for _ in range(self.length): + yield from self.stream + + +class SequencesPipeline(HFModel): + """ + Backwards compatible pipeline to continue supporting Sequences LLM generation + """ + + def __init__(self, path=None, quantize=False, gpu=True, batch=64, **kwargs): + # Default model + path = path if path else "google-t5/t5-base" + + # Call parent constructor + super().__init__(path, quantize, gpu, batch) + + # Text2Text Generation model + self.model, self.tokenizer = self.load(path, "text2text-generation", **kwargs) + + # Set pipeline task + self.task = "text2text-generation" + + def __call__(self, texts, **kwargs): + # Remove unused args + for x in ["num_workers"]: + kwargs.pop(x, None) + + results = [] + for text in self.generate(texts, **kwargs): + results.append({"generated_text": text}) + + return results + + def generate(self, inputs, **kwargs): + """ + Generates outputs. + + Args: + input: tokenized input + kwargs: additional keyword arguments + + Returns: + generated output + """ + + # Tokenize inputs + tokens = self.tokenizer(inputs, truncation=kwargs.pop("truncation", False), return_tensors="pt").to(self.device) + + # Generate outputs + outputs = self.model.generate(**tokens, **kwargs) + + # Decode and return + return [self.tokenizer.decode(x, skip_special_tokens=True) for x in outputs] diff --git a/src/python/txtai/pipeline/llm/litellm.py b/src/python/txtai/pipeline/llm/litellm.py new file mode 100644 index 0000000..03098c2 --- /dev/null +++ b/src/python/txtai/pipeline/llm/litellm.py @@ -0,0 +1,90 @@ +""" +LiteLLM module +""" + +# Conditional import +try: + import litellm as api + + LITELLM = True +except ImportError: + LITELLM = False + +from .generation import Generation + +from ...util import Download + + +class LiteLLM(Generation): + """ + LiteLLM generative model. + """ + + @staticmethod + def ismodel(path): + """ + Checks if path is a LiteLLM model. + + Args: + path: input path + + Returns: + True if this is a LiteLLM model, False otherwise + """ + + # pylint: disable=W0702 + if isinstance(path, str) and LITELLM: + debug = api.suppress_debug_info + try: + # Suppress debug messages for this test + api.suppress_debug_info = True + return api.get_llm_provider(path) and not LiteLLM.ishub(path) + except: + return False + finally: + # Restore debug info value to original value + api.suppress_debug_info = debug + + return False + + @staticmethod + def ishub(path): + """ + Checks if path is available on the HF Hub. + + Args: + input path + + Returns: + True if this is a model on the HF Hub + """ + + # pylint: disable=W0702 + try: + return Download()(path, "config.json") is not None if "/" in path else False + except: + return False + + def __init__(self, path, template=None, **kwargs): + super().__init__(path, template, **kwargs) + + if not LITELLM: + raise ImportError('LiteLLM is not available - install "pipeline" extra to enable') + + # Ignore common pipeline parameters + self.kwargs = {k: v for k, v in self.kwargs.items() if k not in ["quantize", "gpu", "model", "task"]} + + def stream(self, texts, maxlength, stream, stop, **kwargs): + for text in texts: + # LLM API call + result = api.completion( + model=self.path, + messages=[{"content": text, "role": "prompt"}] if isinstance(text, str) else text, + max_tokens=maxlength, + stream=stream, + stop=stop, + **{**self.kwargs, **kwargs} + ) + + # Stream response + yield from self.response(result if stream else [result]) diff --git a/src/python/txtai/pipeline/llm/litert.py b/src/python/txtai/pipeline/llm/litert.py new file mode 100644 index 0000000..bc7d569 --- /dev/null +++ b/src/python/txtai/pipeline/llm/litert.py @@ -0,0 +1,92 @@ +""" +LiteRT module +""" + +import os + +# Conditional import +try: + import litert_lm + + LITERT = True +except ImportError: + LITERT = False + +from .generation import Generation + +from ...util import Download + + +class LiteRT(Generation): + """ + LiteRT generative model. + """ + + @staticmethod + def ismodel(path): + """ + Checks if path is a LiteRT model. + + Args: + path: input path + + Returns: + True if this is a LiteRT model, False otherwise + """ + + return isinstance(path, str) and path.lower().endswith(".litertlm") + + def __init__(self, path, template=None, **kwargs): + super().__init__(path, template, **kwargs) + + if not LITERT: + raise ImportError('LiteRT is not available - install "pipeline" extra to enable') + + # Set log level + litert_lm.set_min_log_severity(litert_lm.LogSeverity.INFO if kwargs.get("verbose") else litert_lm.LogSeverity.ERROR) + + # Check if this is a local path, otherwise download from the HF Hub + path = path if os.path.exists(path) else Download()(path) + + # Create the engine + self.engine = self.createengine(path, **kwargs) + + def stream(self, texts, maxlength, stream, stop, **kwargs): + for message in texts: + # LiteRT only takes the last message and needs the rest as input to the conversation + queue, message = (message[:-1], message[-1]) if len(message) > 1 else (None, message[0]) + + with self.engine.create_conversation(messages=queue) as conversation: + # LLM inference + yield from self.response(conversation.send_message_async(message) if stream else [conversation.send_message(message)]) + + def response(self, result): + for chunk in result: + for item in chunk.get("content", []): + if item.get("type") == "text": + yield item["text"] + + def createengine(self, path, **kwargs): + """ + Creates a new LiteRT engine. + + Args: + path: model path + kwargs: additional keyword args + """ + + # pylint: disable=W0702 + try: + # GPU Backend enabled + gpu = kwargs.get("gpu", True) + + return litert_lm.Engine( + path, + backend=litert_lm.Backend.GPU if gpu else litert_lm.Backend.CPU, + enable_speculative_decoding=kwargs.get("mtp", gpu), + max_num_tokens=kwargs.get("maxlength"), + ) + + except: + # Fallback to CPU backend + return litert_lm.Engine(path, backend=litert_lm.Backend.CPU) diff --git a/src/python/txtai/pipeline/llm/llama.py b/src/python/txtai/pipeline/llm/llama.py new file mode 100644 index 0000000..572cffd --- /dev/null +++ b/src/python/txtai/pipeline/llm/llama.py @@ -0,0 +1,132 @@ +""" +Llama module +""" + +import os + +# Conditional import +try: + import llama_cpp as llama + + LLAMA_CPP = True +except ImportError: + LLAMA_CPP = False + +from ...util import Download + +from .generation import Generation + + +class LlamaCpp(Generation): + """ + llama.cpp generative model. + """ + + @staticmethod + def ismodel(path): + """ + Checks if path is a llama.cpp model. + + Args: + path: input path + + Returns: + True if this is a llama.cpp model, False otherwise + """ + + return isinstance(path, str) and path.lower().endswith(".gguf") + + def __init__(self, path, template=None, **kwargs): + super().__init__(path, template, **kwargs) + + if not LLAMA_CPP: + raise ImportError('llama.cpp is not available - install "pipeline" extra to enable') + + # Check if this is a local path, otherwise download from the HF Hub + path = path if os.path.exists(path) else Download()(path) + + # Create llama.cpp instance + self.llm = self.create(path, **kwargs) + + def stream(self, texts, maxlength, stream, stop, **kwargs): + for text in texts: + yield from ( + self.messages(text, maxlength, stream, stop, **kwargs) + if isinstance(text, list) + else self.prompt(text, maxlength, stream, stop, **kwargs) + ) + + def create(self, path, **kwargs): + """ + Creates a new llama.cpp model instance. + + Args: + path: path to model + kwargs: additional keyword args + + Returns: + llama.cpp instance + """ + + # Default n_ctx=0 if not already set. This sets n_ctx = n_ctx_train. + kwargs["n_ctx"] = kwargs.get("n_ctx", 0) + + # Default GPU layers if not already set + kwargs["n_gpu_layers"] = kwargs.get("n_gpu_layers", -1 if kwargs.get("gpu", os.environ.get("LLAMA_NO_METAL") != "1") else 0) + + # Default verbose flag + kwargs["verbose"] = kwargs.get("verbose", False) + + # Create llama.cpp instance + try: + return llama.Llama(model_path=path, **kwargs) + except ValueError as e: + # Fallback to default n_ctx when not enough memory for n_ctx = n_ctx_train + if not kwargs["n_ctx"]: + kwargs.pop("n_ctx") + return llama.Llama(model_path=path, **kwargs) + + # Raise exception if n_ctx manually specified + raise e + + def messages(self, messages, maxlength, stream, stop, **kwargs): + """ + Processes a list of messages. + + Args: + messages: list of dictionaries with `role` and `content` key-values + maxlength: maximum sequence length + stream: stream response if True, defaults to False + stop: list of stop strings + kwargs: additional generation keyword arguments + + Returns: + generated content + """ + + # LLM call with messages + result = self.llm.create_chat_completion(messages=messages, max_tokens=maxlength, stream=stream, stop=stop, **kwargs) + + # Stream response + yield from self.response(result if stream else [result]) + + def prompt(self, text, maxlength, stream, stop, **kwargs): + """ + Processes a prompt. + + Args: + prompt: prompt text + maxlength: maximum sequence length + stream: stream response if True, defaults to False + stop: list of stop strings + kwargs: additional generation keyword arguments + + Returns: + generated content + """ + + # LLM call with prompt + result = self.llm(text, max_tokens=maxlength, stream=stream, stop=stop, **kwargs) + + # Stream response + yield from self.response(result if stream else [result]) diff --git a/src/python/txtai/pipeline/llm/llm.py b/src/python/txtai/pipeline/llm/llm.py new file mode 100644 index 0000000..a4364b1 --- /dev/null +++ b/src/python/txtai/pipeline/llm/llm.py @@ -0,0 +1,88 @@ +""" +LLM module +""" + +import logging + +from .factory import GenerationFactory + +from ..base import Pipeline + +# Logging configuration +logger = logging.getLogger(__name__) + + +class LLM(Pipeline): + """ + Pipeline for running large language models (LLMs). This class supports the following LLM backends: + + - Local LLMs with Hugging Face Transformers + - Local LLMs with llama.cpp + - Remote API LLMs with LiteLLM + - Custom generation implementations + """ + + def __init__(self, path=None, method=None, **kwargs): + """ + Creates a new LLM. + + Args: + path: model path + method: llm model framework, infers from path if not provided + kwargs: model keyword arguments + """ + + # Default LLM if not provided + path = path if path else "ibm-granite/granite-4.0-350m" + + # Generation instance + self.generator = GenerationFactory.create(path, method, **kwargs) + + def __call__(self, text, maxlength=512, stream=False, stop=None, defaultrole="auto", stripthink=None, **kwargs): + """ + Generates content. Supports the following input formats: + + - String or list of strings (instruction-tuned models must follow chat templates) + - List of dictionaries with `role` and `content` key-values or lists of lists + + Args: + text: text|list + maxlength: maximum sequence length + stream: stream response if True, defaults to False + stop: list of stop strings, defaults to None + defaultrole: default role to apply to text inputs (`auto` to infer (default), `user` for user chat messages or `prompt` for raw prompts) + stripthink: strip thinking tags, defaults to False if stream is enabled, True otherwise + kwargs: additional generation keyword arguments + + Returns: + generated content + """ + + # Debug logging + logger.debug(text) + + # Default stripthink to False when streaming, True otherwise + stripthink = not stream if stripthink is None else stripthink + + # Run LLM generation + return self.generator(text, maxlength, stream, stop, defaultrole, stripthink, **kwargs) + + def ischat(self): + """ + Returns True if this LLM supports chat. + + Returns: + True if this a chat model + """ + + return self.generator.ischat() + + def isvision(self): + """ + Returns True if this LLM supports vision operations. + + Returns: + True if this is a vision model + """ + + return self.generator.isvision() diff --git a/src/python/txtai/pipeline/llm/opencode.py b/src/python/txtai/pipeline/llm/opencode.py new file mode 100644 index 0000000..037b31f --- /dev/null +++ b/src/python/txtai/pipeline/llm/opencode.py @@ -0,0 +1,87 @@ +""" +OpenCode module +""" + +# Conditional import +try: + import httpx + + HTTPX = True +except ImportError: + HTTPX = False + + +from .generation import Generation + +from ...util import Download + + +class OpenCode(Generation): + """ + OpenCode generative model. + """ + + @staticmethod + def ismodel(path): + """ + Checks if path is an OpenCode model. + + Args: + path: input path + + Returns: + True if this is an OpenCode model, False otherwise + """ + + return isinstance(path, str) and path.lower().startswith("opencode") and not OpenCode.ishub(path) + + @staticmethod + def ishub(path): + """ + Checks if path is available on the HF Hub. + + Args: + input path + + Returns: + True if this is a model on the HF Hub + """ + + # pylint: disable=W0702 + try: + return Download()(path, "config.json") is not None if "/" in path else False + except: + return False + + def __init__(self, path, template=None, **kwargs): + super().__init__(path, template, **kwargs) + + if not HTTPX: + raise ImportError('OpenCode is not available - install "pipeline" extra to enable') + + # Get model and provider from path + self.provider, self.model = path.split("/", 1) if "/" in path else (None, None) + + # Get base url, default to `opencode serve` default + self.url = kwargs.get("url", "http://localhost:4096") + + # Start an OpenCode session + self.session = httpx.post(f"{self.url}/session").json() + + def stream(self, texts, maxlength, stream, stop, **kwargs): + for text in texts: + # Build text string + text = "\n".join([x["content"] for x in text]) if isinstance(text, list) else text + + # Add text to request + request = {"parts": [{"type": "text", "text": text}]} + + # Optionally add model, if available + if self.model: + request["model"] = {"providerID": self.provider, "modelID": self.model} + + # Submit request and read JSON response + response = httpx.post(f"{self.url}/session/{self.session['id']}/message", json=request, timeout=None).json() + + # Transform JSON into a text response + yield "\n".join([part["text"] for part in response["parts"] if part["type"] == "text"]) diff --git a/src/python/txtai/pipeline/llm/rag.py b/src/python/txtai/pipeline/llm/rag.py new file mode 100644 index 0000000..cf1d061 --- /dev/null +++ b/src/python/txtai/pipeline/llm/rag.py @@ -0,0 +1,480 @@ +""" +RAG module +""" + +from ...models import Models + +from ..base import Pipeline +from ..data import Tokenizer +from ..text import Questions +from ..text import Similarity + +from .factory import GenerationFactory +from .llm import LLM + + +class RAG(Pipeline): + """ + Extracts knowledge from content by joining a prompt, context data store and generative model together. The data store can be + an embeddings database or a similarity instance with associated input text. The generative model can be a prompt-driven large + language model (LLM), an extractive question-answering model or a custom pipeline. This is known as retrieval augmented generation (RAG). + """ + + # pylint: disable=R0913 + def __init__( + self, + similarity, + path, + quantize=False, + gpu=True, + model=None, + tokenizer=None, + minscore=None, + mintokens=None, + context=None, + task=None, + output="default", + template=None, + separator=" ", + system=None, + **kwargs, + ): + """ + Builds a new RAG pipeline. + + Args: + similarity: similarity instance (embeddings or similarity pipeline) + path: path to model, supports a LLM, Questions or custom pipeline + quantize: True if model should be quantized before inference, False otherwise. + gpu: if gpu inference should be used (only works if GPUs are available) + model: optional existing pipeline model to wrap + tokenizer: Tokenizer class + minscore: minimum score to include context match, defaults to None + mintokens: minimum number of tokens to include context match, defaults to None + context: topn context matches to include, defaults to 3 + task: model task (language-generation, sequence-sequence or question-answering), defaults to auto-detect + output: output format, 'default' returns (name, answer), 'flatten' returns answers and 'reference' returns (name, answer, reference) + template: prompt template, it must have a parameter for {question} and {context}, defaults to "{question} {context}" + separator: context separator + system: system prompt, defaults to None + kwargs: additional keyword arguments to pass to pipeline model + """ + + # Similarity instance + self.similarity = similarity + + # Model can be a LLM, Questions or custom pipeline + self.model = self.load(path, quantize, gpu, model, task, **kwargs) + + # Tokenizer class use default method if not set + self.tokenizer = tokenizer if tokenizer else Tokenizer() if hasattr(self.similarity, "scoring") and self.similarity.isweighted() else None + + # Minimum score to include context match + self.minscore = minscore if minscore is not None else 0.0 + + # Minimum number of tokens to include context match + self.mintokens = mintokens if mintokens is not None else 0.0 + + # Top n context matches to include for context + self.context = context if context else 3 + + # Output format + self.output = output + + # Prompt template + self.template = template if template else "{question} {context}" + + # Context separator + self.separator = separator + + # System prompt template + self.system = system + + def __call__(self, queue, texts=None, **kwargs): + """ + Finds answers to input questions. This method runs queries to find the top n best matches and uses that as the context. + A model is then run against the context for each input question, with the answer returned. + + Args: + queue: input question queue (name, query, question, snippet), can be list of tuples/dicts/strings or a single input element + texts: optional list of text for context, otherwise runs embeddings search + kwargs: additional keyword arguments to pass to pipeline model + + Returns: + list of answers matching input format (tuple or dict) containing fields as specified by output format + """ + + # Save original queue format + inputs = queue + + # Convert queue to list, if necessary + queue = queue if isinstance(queue, list) else [queue] + + # Convert dictionary inputs to tuples + if queue and isinstance(queue[0], dict): + # Convert dict to tuple + queue = [tuple(row.get(x) for x in ["name", "query", "question", "snippet"]) for row in queue] + + if queue and isinstance(queue[0], str): + # Convert string questions to tuple + queue = [(None, row, row, None) for row in queue] + + # Rank texts by similarity for each query + results = self.query([query for _, query, _, _ in queue], texts) + + # Build question-context pairs + names, queries, questions, contexts, topns, snippets = [], [], [], [], [], [] + for x, (name, query, question, snippet) in enumerate(queue): + # Get top n best matching segments + topn = sorted(results[x], key=lambda y: y[2], reverse=True)[: self.context] + + # Generate context using ordering from texts, if available, otherwise order by score + context = self.separator.join(text for _, text, _ in (sorted(topn, key=lambda y: y[0]) if texts else topn)) + + names.append(name) + queries.append(query) + questions.append(question) + contexts.append(context) + topns.append(topn) + snippets.append(snippet) + + # Run pipeline and return answers + answers = self.answers(questions, contexts, **kwargs) + + # Apply output formatting to answers and return + return self.apply(inputs, names, queries, answers, topns, snippets) if isinstance(answers, list) else answers + + def load(self, path, quantize, gpu, model, task, **kwargs): + """ + Loads a LLM, Questions or custom pipeline. + + Args: + path: path to model, supports a LLM, Questions or custom pipeline + quantize: True if model should be quantized before inference, False otherwise. + gpu: if gpu inference should be used (only works if GPUs are available) + model: optional existing pipeline model to wrap + task: model task (language-generation, sequence-sequence or question-answering), defaults to auto-detect + kwargs: additional keyword arguments to pass to pipeline model + + Returns: + LLM, Questions or custom pipeline + """ + + # Only try to load if path is a string + if not isinstance(path, str): + return path + + # Attempt to resolve task if not provided + task = GenerationFactory.method(path, task) + task = Models.task(path, **kwargs) if task == "transformers" else task + + # Load Questions pipeline + if task == "question-answering": + return Questions(path, quantize, gpu, model, **kwargs) + + # Load LLM pipeline + return LLM(path=path, quantize=quantize, gpu=gpu, model=model, task=task, **kwargs) + + def query(self, queries, texts): + """ + Rank texts by similarity for each query. If texts is empty, an embeddings search will be executed. + Returns results sorted by best match. + + Args: + queries: list of queries + texts: optional list of text + + Returns: + list of (id, data, score) per query + """ + + if not queries: + return [] + + # Score text against queries + scores, segments, tokenlist = self.score(queries, texts) + + # Build question-context pairs + results = [] + for i, query in enumerate(queries): + # Get list of required and prohibited tokens + must = [token.strip("+") for token in query.split() if token.startswith("+") and len(token) > 1] + mnot = [token.strip("-") for token in query.split() if token.startswith("-") and len(token) > 1] + + # Segment text is static when texts is passed in but different per query when an embeddings search is run + segment = segments if texts else segments[i] + tokens = tokenlist if texts else tokenlist[i] + + # List of matches + matches = [] + for y, (x, score) in enumerate(scores[i]): + # Segments and tokens are statically ordered when texts is passed in, need to resolve values with score id + # Scores, segments and tokens all share the same list ordering when an embeddings search is run + x = x if texts else y + + # Get segment text + text = segment[x][1] + + # Add result if: + # - all required tokens are present or there are not required tokens AND + # - all prohibited tokens are not present or there are not prohibited tokens + # - score is above minimum score required + # - number of tokens is above minimum number of tokens required + if (not must or all(token.lower() in text.lower() for token in must)) and ( + not mnot or all(token.lower() not in text.lower() for token in mnot) + ): + if score >= self.minscore and len(tokens[x]) >= self.mintokens: + matches.append(segment[x] + (score,)) + + # Add query matches sorted by highest score + results.append(matches) + + return results + + def score(self, queries, texts): + """ + Runs queries against texts (or an embeddings search if texts is empty) and builds list of + similarity scores for each query-text combination. + + Args: + queries: list of queries + texts: optional list of text + + Returns: + scores, segments, tokenlist + """ + + # Tokenize text + segments, tokenlist = [], [] + if texts: + for text in texts: + # Run tokenizer method, if available, otherwise returns original text + tokens = self.tokenize(text) + if tokens: + segments.append(text) + tokenlist.append(tokens) + + # Add index id to segments to preserve ordering after filters + segments = list(enumerate(segments)) + + # Get list of (id, score) - sorted by highest score per query + if isinstance(self.similarity, Similarity): + # Score using similarity pipeline + scores = self.similarity(queries, [t for _, t in segments]) + elif texts: + # Score using embeddings.batchsimilarity + scores = self.similarity.batchsimilarity([self.tokenize(x) for x in queries], tokenlist) + else: + # Score using embeddings.batchsearch + scores, segments, tokenlist = self.batchsearch(queries) + + return scores, segments, tokenlist + + def batchsearch(self, queries): + """ + Runs a batch embeddings search for a set of queries. + + Args: + queries: list of queries to run + + Returns: + scores, segments, tokenlist + """ + + # Derive target batchsearch method + batchsearch = self.similarity if callable(self.similarity) else self.similarity.batchsearch + + scores, segments, tokenlist = [], [], [] + for results in batchsearch([self.tokenize(x) for x in queries], self.context): + # Assume embeddings content is enabled and results are dictionaries + scores.append([(result["id"], result["score"]) for result in results]) + segments.append([(result["id"], result["text"]) for result in results]) + tokenlist.append([self.tokenize(result["text"]) for result in results]) + + return scores, segments, tokenlist + + def tokenize(self, text): + """ + Tokenizes text. Returns original text if tokenizer is not available. + + Args: + text: input text + + Returns: + tokens if tokenizer available otherwise original text + """ + + return self.tokenizer(text) if self.tokenizer else text + + def answers(self, questions, contexts, **kwargs): + """ + Executes pipeline and formats extracted answers. + + Args: + questions: questions + contexts: question context + kwargs: additional keyword arguments to pass to model + + Returns: + answers + """ + + # Run model inference with questions pipeline + if isinstance(self.model, Questions): + return self.model(questions, contexts) + + # Run generator pipeline + return self.model(self.prompts(questions, contexts), **kwargs) + + def prompts(self, questions, contexts): + """ + Builds a list of prompts using the passed in questions and contexts. + + Args: + questions: questions + contexts: question context + + Returns: + prompts + """ + + # Format prompts for generator pipeline + prompts = [] + for x, context in enumerate(contexts): + # Create input prompt + prompt = self.template.format(question=questions[x], context=context) + + # Add system prompt, if necessary + if self.system: + prompt = [ + {"role": "system", "content": self.system.format(question=questions[x], context=context)}, + {"role": "user", "content": prompt}, + ] + + prompts.append(prompt) + + return prompts + + def apply(self, inputs, names, queries, answers, topns, snippets): + """ + Applies the following formatting rules to answers. + - each answer row matches input format (tuple or dict) + - if output format is 'flatten' then this method flattens to a list of answers + - if output format is 'reference' then a list of (name, answer, reference) is returned + - otherwise, if output format is 'default' or anything else list of (name, answer) is returned + + Args: + inputs: original inputs + names: question identifiers/names + queries: list of input queries + answers: list of generated answers + topns: top n records used for context + snippets: flags to enable answer snippets per answer + + Returns: + list of answers matching input format (tuple or dict) containing fields as specified by output format + """ + + # Resolve answers as snippets + answers = self.snippets(names, answers, topns, snippets) + + # Flatten to list of answers and return + if self.output == "flatten": + answers = [answer for _, answer in answers] + else: + # Resolve id reference for each answer + if self.output == "reference": + answers = self.reference(queries, answers, topns) + + # Ensure output format matches input format + first = inputs[0] if inputs and isinstance(inputs, list) else inputs + if isinstance(first, (dict, str)): + # Add name if input queue had name field + fields = ["name", "answer", "reference"] if isinstance(first, dict) and "name" in first else [None, "answer", "reference"] + answers = [{fields[x]: column for x, column in enumerate(row) if fields[x]} for row in answers] + + # Unpack single answer, if necessary + return answers[0] if answers and isinstance(inputs, (tuple, dict, str)) else answers + + def snippets(self, names, answers, topns, snippets): + """ + Extracts text surrounding the answer within context. + + Args: + names: question identifiers/names + answers: list of generated answers + topns: top n records used for context + snippets: flags to enable answer snippets per answer + + Returns: + answers resolved as snippets per question, if necessary + """ + + # Extract and format answer + results = [] + + for x, answer in enumerate(answers): + # Resolve snippet if necessary + if answer and snippets[x]: + # Searches for first text element to contain answer + for _, text, _ in topns[x]: + if answer in text: + answer = text + break + + results.append((names[x], answer)) + + return results + + def reference(self, queries, answers, topns): + """ + Reference each answer with the best matching context element id. + + Args: + queries: list of input queries + answers: list of answers + topn: top n context elements as (id, data, tag) + + Returns: + list of (name, answer, reference) + """ + + # Convert queries to terms + terms = self.terms(queries) + + outputs = [] + for x, (name, answer) in enumerate(answers): + # Get matching topn + topn, reference = topns[x], None + + if topn: + # Build query from keyword terms and the answer text + query = f"{terms[x]} {answers[x][1]}" + + # Compare answer to topns to find best match + scores, _, _ = self.score([query], [text for _, text, _ in topn]) + + # Get top score index + index = scores[0][0][0] + + # Use matching topn id as reference + reference = topn[index][0] + + # Append (name, answer, reference) tuple + outputs.append((name, answer, reference)) + + return outputs + + def terms(self, queries): + """ + Extracts keyword terms from a list of queries using underlying similarity model. + + Args: + queries: list of queries + + Returns: + list of queries reduced down to keyword term strings + """ + + # Extract keyword terms from queries if underlying similarity model supports it + return self.similarity.batchterms(queries) if hasattr(self.similarity, "batchterms") else queries diff --git a/src/python/txtai/pipeline/nop.py b/src/python/txtai/pipeline/nop.py new file mode 100644 index 0000000..d4bf018 --- /dev/null +++ b/src/python/txtai/pipeline/nop.py @@ -0,0 +1,14 @@ +""" +No-Op module +""" + +from .base import Pipeline + + +class Nop(Pipeline): + """ + Simple no-op pipeline that returns inputs + """ + + def __call__(self, inputs): + return inputs diff --git a/src/python/txtai/pipeline/tensors.py b/src/python/txtai/pipeline/tensors.py new file mode 100644 index 0000000..15967f6 --- /dev/null +++ b/src/python/txtai/pipeline/tensors.py @@ -0,0 +1,55 @@ +""" +Tensor processing framework module +""" + +from .base import Pipeline + +# Core library imports +from ..util import Library + +torch = Library().torch() + + +class Tensors(Pipeline): + """ + Pipeline backed by a tensor processing framework. Currently supports PyTorch. + """ + + def quantize(self, model): + """ + Quantizes input model and returns. This only is supported for CPU devices. + + Args: + model: torch model + + Returns: + quantized torch model + """ + + # pylint: disable=E1101 + return torch.quantization.quantize_dynamic(model, {torch.nn.Linear}, dtype=torch.qint8) + + def tensor(self, data): + """ + Creates a tensor array. + + Args: + data: input data + + Returns: + tensor + """ + + # pylint: disable=E1102 + return torch.tensor(data) + + def context(self): + """ + Defines a context used to wrap processing with the tensor processing framework. + + Returns: + processing context + """ + + # pylint: disable=E1101 + return torch.no_grad() diff --git a/src/python/txtai/pipeline/text/__init__.py b/src/python/txtai/pipeline/text/__init__.py new file mode 100644 index 0000000..be6b979 --- /dev/null +++ b/src/python/txtai/pipeline/text/__init__.py @@ -0,0 +1,13 @@ +""" +Text imports +""" + +from .crossencoder import CrossEncoder +from .entity import Entity +from .labels import Labels +from .lateencoder import LateEncoder +from .questions import Questions +from .reranker import Reranker +from .similarity import Similarity +from .summary import Summary +from .translation import Translation diff --git a/src/python/txtai/pipeline/text/crossencoder.py b/src/python/txtai/pipeline/text/crossencoder.py new file mode 100644 index 0000000..3a80278 --- /dev/null +++ b/src/python/txtai/pipeline/text/crossencoder.py @@ -0,0 +1,73 @@ +""" +CrossEncoder module +""" + +from ..hfpipeline import HFPipeline + +# Core library imports +from ...util import Library + +np = Library().numpy() + + +class CrossEncoder(HFPipeline): + """ + Computes similarity between query and list of text using a cross-encoder model + """ + + def __init__(self, path=None, quantize=False, gpu=True, model=None, **kwargs): + super().__init__("text-classification", path, quantize, gpu, model, **kwargs) + + def __call__(self, query, texts, multilabel=True, workers=0): + """ + Computes the similarity between query and list of text. Returns a list of + (id, score) sorted by highest score, where id is the index in texts. + + This method supports query as a string or a list. If the input is a string, + the return type is a 1D list of (id, score). If text is a list, a 2D list + of (id, score) is returned with a row per string. + + Args: + query: query text|list + texts: list of text + multilabel: labels are independent if True, scores are normalized to sum to 1 per text item if False, raw scores returned if None + workers: number of concurrent workers to use for processing data, defaults to None + + Returns: + list of (id, score) + """ + + scores = [] + for q in [query] if isinstance(query, str) else query: + # Pass (query, text) pairs to model + result = self.pipeline([{"text": q, "text_pair": t} for t in texts], top_k=None, function_to_apply="none", num_workers=workers) + + # Apply score transform function + scores.append(self.function([r[0]["score"] for r in result], multilabel)) + + # Build list of (id, score) per query sorted by highest score + scores = [sorted(enumerate(row), key=lambda x: x[1], reverse=True) for row in scores] + + return scores[0] if isinstance(query, str) else scores + + def function(self, scores, multilabel): + """ + Applys an output transformation function based on value of multilabel. + + Args: + scores: input scores + multilabel: labels are independent if True, scores are normalized to sum to 1 per text item if False, raw scores returned if None + + Returns: + transformed scores + """ + + # Output functions + # pylint: disable=C3001 + identity = lambda x: x + sigmoid = lambda x: 1.0 / (1.0 + np.exp(-x)) + softmax = lambda x: np.exp(x) / np.sum(np.exp(x)) + function = identity if multilabel is None else sigmoid if multilabel else softmax + + # Apply output function + return function(np.array(scores)) diff --git a/src/python/txtai/pipeline/text/entity.py b/src/python/txtai/pipeline/text/entity.py new file mode 100644 index 0000000..57564a1 --- /dev/null +++ b/src/python/txtai/pipeline/text/entity.py @@ -0,0 +1,139 @@ +""" +Entity module +""" + +# Conditional import +try: + from gliner import GLiNER + + GLINER = True +except ImportError: + GLINER = False + +from ...models import Models +from ...util import Download, DownloadError + +from ..hfpipeline import HFPipeline + + +class Entity(HFPipeline): + """ + Applies a token classifier to text and extracts entity/label combinations. + """ + + def __init__(self, path=None, quantize=False, gpu=True, model=None, **kwargs): + # Create a new entity pipeline + self.gliner = self.isgliner(path) + if self.gliner: + if not GLINER: + raise ImportError('GLiNER is not available - install "pipeline" extra to enable') + + # GLiNER entity pipeline + self.pipeline = GLiNER.from_pretrained(path) + self.pipeline = self.pipeline.to(Models.device(Models.deviceid(gpu))) + else: + # Standard entity pipeline + super().__init__("token-classification", path, quantize, gpu, model, **kwargs) + + def __call__(self, text, labels=None, aggregate="simple", flatten=None, join=False, workers=0): + """ + Applies a token classifier to text and extracts entity/label combinations. + + Args: + text: text|list + labels: list of entity type labels to accept, defaults to None which accepts all + aggregate: method to combine multi token entities - options are "simple" (default), "first", "average" or "max" + flatten: flatten output to a list of labels if present. Accepts a boolean or float value to only keep scores greater than that number. + join: joins flattened output into a string if True, ignored if flatten not set + workers: number of concurrent workers to use for processing data, defaults to None + + Returns: + list of (entity, entity type, score) or list of entities depending on flatten parameter + """ + + # Run token classification pipeline + results = self.execute(text, labels, aggregate, workers) + + # Convert results to a list if necessary + if isinstance(text, str): + results = [results] + + # Score threshold when flatten is set + threshold = 0.0 if isinstance(flatten, bool) else flatten + + # Extract entities if flatten set, otherwise extract (entity, entity type, score) tuples + outputs = [] + for result in results: + if flatten: + output = [r["word"] for r in result if self.accept(r["entity_group"], labels) and r["score"] >= threshold] + outputs.append(" ".join(output) if join else output) + else: + outputs.append([(r["word"], r["entity_group"], float(r["score"])) for r in result if self.accept(r["entity_group"], labels)]) + + return outputs[0] if isinstance(text, str) else outputs + + def isgliner(self, path): + """ + Tests if path is a GLiNER model. + + Args: + path: model path + + Returns: + True if this is a GLiNER model, False otherwise + """ + + try: + # Test if this model has a gliner_config.json file + return Download()(path, "gliner_config.json") is not None + + # Ignore this error - invalid repo or directory + except DownloadError: + pass + + return False + + def execute(self, text, labels, aggregate, workers): + """ + Runs the entity extraction pipeline. + + Args: + text: text|list + labels: list of entity type labels to accept, defaults to None which accepts all + aggregate: method to combine multi token entities - options are "simple" (default), "first", "average" or "max" + workers: number of concurrent workers to use for processing data, defaults to None + + Returns: + list of entities and labels + """ + + if self.gliner: + # Extract entities with GLiNER. Use default CoNLL-2003 labels when not otherwise provided. + results = self.pipeline.batch_predict_entities( + text if isinstance(text, list) else [text], labels if labels else ["person", "organization", "location"] + ) + + # Map results to same format as Transformers token classifier + entities = [] + for result in results: + entities.append([{"word": x["text"], "entity_group": x["label"], "score": x["score"]} for x in result]) + + # Return extracted entities + return entities if isinstance(text, list) else entities[0] + + # Standard Transformers token classification pipeline + return self.pipeline(text, aggregation_strategy=aggregate, num_workers=workers) + + def accept(self, etype, labels): + """ + Determines if entity type is in valid entity type. + + Args: + etype: entity type + labels: list of entities to accept + + Returns: + if etype is accepted + """ + + return not labels or etype in labels diff --git a/src/python/txtai/pipeline/text/labels.py b/src/python/txtai/pipeline/text/labels.py new file mode 100644 index 0000000..e43d366 --- /dev/null +++ b/src/python/txtai/pipeline/text/labels.py @@ -0,0 +1,139 @@ +""" +Labels module +""" + +from ..hfpipeline import HFPipeline + + +class Labels(HFPipeline): + """ + Applies a text classifier to text. Supports zero shot and standard text classification models + """ + + def __init__(self, path=None, quantize=False, gpu=True, model=None, dynamic=True, **kwargs): + super().__init__("zero-shot-classification" if dynamic else "text-classification", path, quantize, gpu, model, **kwargs) + + # Set if labels are dynamic (zero shot) or fixed (standard text classification) + self.dynamic = dynamic + + def __call__(self, text, labels=None, multilabel=False, flatten=None, workers=0, **kwargs): + """ + Applies a text classifier to text. Returns a list of (id, score) sorted by highest score, + where id is the index in labels. For zero shot classification, a list of labels is required. + For text classification models, a list of labels is optional, otherwise all trained labels are returned. + + This method supports text as a string or a list. If the input is a string, the return + type is a 1D list of (id, score). If text is a list, a 2D list of (id, score) is + returned with a row per string. + + Args: + text: text|list + labels: list of labels + multilabel: labels are independent if True, scores are normalized to sum to 1 per text item if False, raw scores returned if None + flatten: flatten output to a list of labels if present. Accepts a boolean or float value to only keep scores greater than that number. + workers: number of concurrent workers to use for processing data, defaults to None + kwargs: additional keyword args + + Returns: + list of (id, score) or list of labels depending on flatten parameter + """ + + if self.dynamic: + # Run zero shot classification pipeline + results = self.pipeline(text, labels, multi_label=multilabel, truncation=True, num_workers=workers, **kwargs) + else: + # Set classification function based on inputs + function = "none" if multilabel is None else "sigmoid" if multilabel or len(self.labels()) == 1 else "softmax" + + # Run text classification pipeline + results = self.pipeline(text, top_k=None, function_to_apply=function, num_workers=workers, **kwargs) + + # Convert results to a list if necessary + if isinstance(text, str): + results = [results] + + # Build list of outputs and return + outputs = self.outputs(results, labels, flatten) + + # Stream outputs into list, if necessary + outputs = list(outputs) if isinstance(results, list) else outputs + + # Return format that matches input format + return outputs[0] if isinstance(text, str) else outputs + + def labels(self): + """ + Returns a list of all text classification model labels sorted in index order. + + Returns: + list of labels + """ + + return list(self.pipeline.model.config.id2label.values()) + + def outputs(self, results, labels, flatten): + """ + Processes pipeline results and builds outputs. + + Args: + results: pipeline results + labels: list of labels + flatten: flatten output to a list of labels if present. Accepts a boolean or float value to only keep scores greater than that number. + + Returns: + outputs + """ + + threshold = 0.0 if isinstance(flatten, bool) else flatten + + for result in results: + if self.dynamic: + if flatten: + result = [label for x, label in enumerate(result["labels"]) if result["scores"][x] >= threshold] + yield result[:1] if isinstance(flatten, bool) else result + else: + yield [(labels.index(label), result["scores"][x]) for x, label in enumerate(result["labels"])] + else: + if flatten: + result = [x["label"] for x in result if x["score"] >= threshold and (not labels or x["label"] in labels)] + yield result[:1] if isinstance(flatten, bool) else result + else: + # Filter results using labels, if provided + yield self.limit(result, labels) + + def limit(self, result, labels): + """ + Filter result using labels. If labels is None, original result is returned. + + Args: + result: results array sorted by score descending + labels: list of labels or None + + Returns: + filtered results + """ + + # Get config + config = self.pipeline.model.config + + # Resolve label ids for labels + result = [(config.label2id.get(x["label"], 0), x["score"]) for x in result] + + if labels: + matches = [] + for label in labels: + # Lookup label keys from model config + if label.isdigit(): + label = int(label) + keys = list(config.id2label.keys()) + else: + label = label.lower() + keys = [x.lower() for x in config.label2id.keys()] + + # Find and add label match + if label in keys: + matches.append(keys.index(label)) + + return [(label, score) for label, score in result if label in matches] + + return result diff --git a/src/python/txtai/pipeline/text/lateencoder.py b/src/python/txtai/pipeline/text/lateencoder.py new file mode 100644 index 0000000..e121315 --- /dev/null +++ b/src/python/txtai/pipeline/text/lateencoder.py @@ -0,0 +1,107 @@ +""" +Late encoder module +""" + +from ...models import Models, PoolingFactory +from ..base import Pipeline + +# Core library imports +from ...util import Library + +library = Library() +np = library.numpy() +torch = library.torch() + + +class LateEncoder(Pipeline): + """ + Computes similarity between query and list of text using a late interaction model. + """ + + def __init__(self, path=None, **kwargs): + # Get device + self.device = Models.device(Models.deviceid(kwargs.get("gpu", True))) + + # Load model + self.model = PoolingFactory.create( + { + "method": kwargs.get("method"), + "path": path if path else "colbert-ir/colbertv2.0", + "device": self.device, + "tokenizer": kwargs.get("tokenizer"), + "maxlength": kwargs.get("maxlength"), + "modelargs": {**kwargs.get("vectors", {}), **{"muvera": None}}, + } + ) + + def __call__(self, query, texts, limit=None): + """ + Computes the similarity between query and list of text. Returns a list of + (id, score) sorted by highest score, where id is the index in texts. + + This method supports query as a string or a list. If the input is a string, + the return type is a 1D list of (id, score). If text is a list, a 2D list + of (id, score) is returned with a row per string. + + Args: + query: query text|list + texts: list of text + limit: maximum comparisons to return, defaults to all + + Returns: + list of (id, score) + """ + + queries = [query] if isinstance(query, str) else query + + # Encode text to vectors + queries = self.encode(queries, "query") + data = self.encode(texts, "data") if isinstance(texts[0], str) else texts + + # Compute maximum similarity score + scores = [] + for q in queries: + scores.extend(self.score(q.unsqueeze(0), data, limit)) + + return scores[0] if isinstance(query, str) else scores + + def encode(self, data, category): + """ + Encodes a batch of data using the underlying model. + + Args: + data: input data + category: encoding category + + Returns: + encoded data + """ + + return torch.from_numpy(self.model.encode(data, category=category)).to(self.device) + + def score(self, queries, data, limit): + """ + Computes the maximum similarity score between query vectors and data vectors. + + Args: + queries: query vectors + data: data vectors + limit: query limit + + Returns: + list of (id, score) + """ + + # Compute bulk dot product using einstein notation + scores = torch.einsum("ash,bth->abst", queries, data).max(axis=-1).values.mean(axis=-1) + scores = scores.cpu().numpy() + + # Get top n matching indices and scores + indices = np.argpartition(-scores, limit if limit and limit < scores.shape[0] else scores.shape[0] - 1)[:, :limit] + scores = np.take_along_axis(scores, indices, axis=1) + + results = [] + for x, index in enumerate(indices): + results.append(list(zip(index.tolist(), scores[x].tolist()))) + + return results diff --git a/src/python/txtai/pipeline/text/questions.py b/src/python/txtai/pipeline/text/questions.py new file mode 100644 index 0000000..f343814 --- /dev/null +++ b/src/python/txtai/pipeline/text/questions.py @@ -0,0 +1,95 @@ +""" +Questions module +""" + +from ..hfmodel import HFModel + +# Core library imports +from ...util import Library + +torch = Library().torch() + + +class Questions(HFModel): + """ + Runs extractive QA for a series of questions and contexts. + """ + + def __init__(self, path=None, quantize=False, gpu=True, batch=64, **kwargs): + # Default model + path = path if path else "distilbert-base-cased-distilled-squad" + + # Call parent constructor + super().__init__(path, quantize, gpu, batch) + + # Load model and tokenizer + self.model, self.tokenizer = self.load(path, "question-answering", **kwargs) + + def __call__(self, questions, contexts, **kwargs): + """ + Runs a extractive question-answering model against each question-context pair, finding the best answers. + + Args: + questions: list of questions + contexts: list of contexts to pull answers from + kwargs: additional keyword arguments + + Returns: + list of answers + """ + + answers = [] + + for x, question in enumerate(questions): + if question and contexts[x]: + # Tokenize inputs + tokens = self.tokenizer(question, contexts[x], truncation="only_second", return_tensors="pt").to(self.device) + + # Generate outputs + with torch.no_grad(): + outputs = self.model(**tokens) + + # Unpack results + startlogits, endlogits = (outputs.start_logits, outputs.end_logits) if hasattr(outputs, "start_logits") else outputs + + # Get best span as answer + start = startlogits.argmax() + end = endlogits.argmax() + answer = self.answer(contexts[x], tokens, start, end) + + # Calculate span score + startprob = torch.nn.functional.softmax(startlogits, dim=-1)[0] + endprob = torch.nn.functional.softmax(endlogits, dim=-1)[0] + score = startprob[start] * endprob[end] + + # Require score to be at least 0.05 + if score < 0.05: + answer = None + + # Add answer + answers.append(answer) + else: + answers.append(None) + + return answers + + def answer(self, context, tokens, start, end): + """ + Extracts an answer snippet from context. + + Args: + context: context + tokens: tokenized inputs + start: start index of answer + end: end index of answer + + Returns: + answer + """ + + startword = tokens.token_to_word(start) + endword = tokens.token_to_word(end) + startindex = tokens.word_to_chars(startword, sequence_index=1)[0] + endindex = tokens.word_to_chars(endword, sequence_index=1)[1] + + return context[startindex:endindex] diff --git a/src/python/txtai/pipeline/text/reranker.py b/src/python/txtai/pipeline/text/reranker.py new file mode 100644 index 0000000..b0ffd9f --- /dev/null +++ b/src/python/txtai/pipeline/text/reranker.py @@ -0,0 +1,57 @@ +""" +Reranker module +""" + +from ..base import Pipeline + + +class Reranker(Pipeline): + """ + Runs embeddings queries and re-ranks them using a similarity pipeline. Note that content must be enabled with the + embeddings instance for this to work properly. + """ + + def __init__(self, embeddings, similarity): + """ + Creates a Reranker pipeline. + + Args: + embeddings: embeddings instance (content must be enabled) + similarity: similarity instance + """ + + self.embeddings, self.similarity = embeddings, similarity + + # pylint: disable=W0222 + def __call__(self, query, limit=3, factor=10, **kwargs): + """ + Runs an embeddings search and re-ranks the results using a Similarity pipeline. + + Args: + query: query text|list + limit: maximum results + factor: factor to multiply limit by for the initial embeddings search + kwargs: additional arguments to pass to embeddings search + + Returns: + list of query results rescored using a Similarity pipeline + """ + + queries = [query] if not isinstance(query, list) else query + + # Run searches + results = self.embeddings.batchsearch(queries, limit * factor, **kwargs) + + # Re-rank using similarity pipeline + ranked = [] + for x, result in enumerate(results): + texts = [row["text"] for row in result] + + # Score results and merge + for uid, score in self.similarity(queries[x], texts): + result[uid]["score"] = score + + # Sort and take top n sorted results + ranked.append(sorted(result, key=lambda row: row["score"], reverse=True)[:limit]) + + return ranked[0] if isinstance(query, str) else ranked diff --git a/src/python/txtai/pipeline/text/similarity.py b/src/python/txtai/pipeline/text/similarity.py new file mode 100644 index 0000000..d1f815a --- /dev/null +++ b/src/python/txtai/pipeline/text/similarity.py @@ -0,0 +1,86 @@ +""" +Similarity module +""" + +from .crossencoder import CrossEncoder +from .labels import Labels +from .lateencoder import LateEncoder + +# Core library imports +from ...util import Library + +np = Library().numpy() + + +class Similarity(Labels): + """ + Computes similarity between query and list of text using a transformers model. + """ + + def __init__(self, path=None, quantize=False, gpu=True, model=None, dynamic=True, crossencode=False, lateencode=False, **kwargs): + self.crossencoder, self.lateencoder = None, None + + if lateencode: + # Load a late interaction encoder if lateencode set to True + self.lateencoder = LateEncoder(path=path, gpu=gpu, **kwargs) + else: + # Use zero-shot classification if dynamic is True and crossencode is False, otherwise use standard text classification + super().__init__(path, quantize, gpu, model, False if crossencode else dynamic, **kwargs) + + # Load as a cross-encoder if crossencode set to True + self.crossencoder = CrossEncoder(model=self.pipeline) if crossencode else None + + # pylint: disable=W0222 + def __call__(self, query, texts, multilabel=True, **kwargs): + """ + Computes the similarity between query and list of text. Returns a list of + (id, score) sorted by highest score, where id is the index in texts. + + This method supports query as a string or a list. If the input is a string, + the return type is a 1D list of (id, score). If text is a list, a 2D list + of (id, score) is returned with a row per string. + + Args: + query: query text|list + texts: list of text + multilabel: labels are independent if True, scores are normalized to sum to 1 per text item if False, raw scores returned if None + kwargs: additional keyword args + + Returns: + list of (id, score) + """ + + if self.crossencoder: + # pylint: disable=E1102 + return self.crossencoder(query, texts, multilabel) + + if self.lateencoder: + return self.lateencoder(query, texts) + + # Call Labels pipeline for texts using input query as the candidate label + scores = super().__call__(texts, [query] if isinstance(query, str) else query, multilabel, **kwargs) + + # Sort on query index id + scores = [[score for _, score in sorted(row)] for row in scores] + + # Transpose axes to get a list of text scores for each query + scores = np.array(scores).T.tolist() + + # Build list of (id, score) per query sorted by highest score + scores = [sorted(enumerate(row), key=lambda x: x[1], reverse=True) for row in scores] + + return scores[0] if isinstance(query, str) else scores + + def encode(self, data, category): + """ + Encodes a batch of data using the underlying model. + + Args: + data: input data + category: encoding category + + Returns: + encoded data + """ + + return self.lateencoder.encode(data, category) if self.lateencoder else data diff --git a/src/python/txtai/pipeline/text/summary.py b/src/python/txtai/pipeline/text/summary.py new file mode 100644 index 0000000..bf7ba2f --- /dev/null +++ b/src/python/txtai/pipeline/text/summary.py @@ -0,0 +1,152 @@ +""" +Summary module +""" + +import re + +from ...models import Models +from ..hfmodel import HFModel + + +class Summary(HFModel): + """ + Summarizes text. + """ + + def __init__(self, path=None, quantize=False, gpu=True, batch=64, **kwargs): + # Default model + path = path if path else "sshleifer/distilbart-cnn-12-6" + + # Call parent constructor + super().__init__(path, quantize, gpu, batch) + + # Load model and tokenizer + self.model, self.tokenizer = self.load(path, "summarization", **kwargs) + + def __call__(self, text, minlength=None, maxlength=None, **kwargs): + """ + Runs a summarization model against a block of text. + + This method supports text as a string or a list. If the input is a string, the return + type is text. If text is a list, a list of text is returned with a row per block of text. + + Args: + text: text|list + minlength: minimum length for summary + maxlength: maximum length for summary + kwargs: additional keyword arguments + + Returns: + summary text + """ + + # Validate text length greater than max length + check = maxlength if maxlength else self.maxlength() + + # Skip text shorter than max length + texts = text if isinstance(text, list) else [text] + params = [(x, text if len(text) >= check else None) for x, text in enumerate(texts)] + + # Build keyword arguments + kwargs = self.args(minlength, maxlength) + + inputs = [text for _, text in params if text] + if inputs: + # Run summarization + results = self.generate(inputs, **kwargs) + + # Pull out summary text + results = iter([self.clean(x) for x in results]) + results = [next(results) if text else texts[x] for x, text in params] + else: + # Return original + results = texts + + return results[0] if isinstance(text, str) else results + + def maxlength(self): + """ + Gets the max length to use for generate calls. + + Returns: + max length + """ + + return Models.maxlength(self.model, self.tokenizer) + + def generate(self, inputs, **kwargs): + """ + Generates outputs. + + Args: + input: tokenized input + kwargs: additional keyword arguments + + Returns: + generated output + """ + + # Prepend prefix, if necessary + prefix = kwargs.pop("prefix", None) + if prefix: + inputs = [prefix + x for x in inputs] + + # Tokenize inputs + tokens = self.tokenizer(inputs, truncation=kwargs.pop("truncation", False), return_tensors="pt").to(self.device) + + # Generate outputs + outputs = self.model.generate(**tokens, **kwargs) + + # Decode and return + return [self.tokenizer.decode(x, skip_special_tokens=True) for x in outputs] + + def clean(self, text): + """ + Applies a series of rules to clean extracted text. + + Args: + text: input text + + Returns: + clean text + """ + + text = re.sub(r"\s*\.\s*", ". ", text) + text = text.strip() + + return text + + def args(self, minlength, maxlength): + """ + Builds keyword arguments. + + Args: + minlength: minimum length for summary + maxlength: maximum length for summary + + Returns: + keyword arguments + """ + + # Defaults + kwargs = {"truncation": True, "num_beams": 4, "max_new_tokens": 256} + + # Get default summary task parameters + params = getattr(self.model.config, "task_specific_params", None) + if params: + params = params.get("summarization", {}) + + # Ignore max length + kwargs.update({k: v for k, v in params.items() if k not in ["max_length"]}) + + if minlength: + kwargs["min_length"] = minlength + if maxlength: + kwargs["max_length"] = maxlength + kwargs["max_new_tokens"] = None + + # Default minlength if not provided or it's bigger than maxlength + if "min_length" not in kwargs or kwargs["min_length"] > kwargs["max_length"]: + kwargs["min_length"] = kwargs["max_length"] + + return kwargs diff --git a/src/python/txtai/pipeline/text/translation.py b/src/python/txtai/pipeline/text/translation.py new file mode 100644 index 0000000..e1d045c --- /dev/null +++ b/src/python/txtai/pipeline/text/translation.py @@ -0,0 +1,285 @@ +""" +Translation module +""" + +# Conditional import +try: + from staticvectors import StaticVectors + + STATICVECTORS = True +except ImportError: + STATICVECTORS = False + +from ...models import Models +from ..hfmodel import HFModel + +# Core library imports +from ...util import Library + +huggingface_hub = Library().huggingface_hub() + + +class Translation(HFModel): + """ + Translates text from source language into target language. + """ + + # Default language detection model + DEFAULT_LANG_DETECT = "neuml/language-id-quantized" + + def __init__(self, path=None, quantize=False, gpu=True, batch=64, langdetect=None, findmodels=True): + """ + Constructs a new language translation pipeline. + + Args: + path: optional path to model, accepts Hugging Face model hub id or local path, + uses default model for task if not provided + quantize: if model should be quantized, defaults to False + gpu: True/False if GPU should be enabled, also supports a GPU device id + batch: batch size used to incrementally process content + langdetect: set a custom language detection function, method must take a list of strings and return + language codes for each, uses default language detector if not provided + findmodels: True/False if the Hugging Face Hub will be searched for source-target translation models + """ + + # Call parent constructor + super().__init__(path if path else "facebook/m2m100_418M", quantize, gpu, batch) + + # Language detection + self.detector = None + self.langdetect = langdetect + self.findmodels = findmodels + + # Language models + self.models = {} + self.ids = None + + def __call__(self, texts, target="en", source=None, showmodels=False): + """ + Translates text from source language into target language. + + This method supports texts as a string or a list. If the input is a string, + the return type is string. If text is a list, the return type is a list. + + Args: + texts: text|list + target: target language code, defaults to "en" + source: source language code, detects language if not provided + + Returns: + list of translated text + """ + + values = [texts] if not isinstance(texts, list) else texts + + # Detect source languages + languages = self.detect(values) if not source else [source] * len(values) + unique = set(languages) + + # Build a dict from language to list of (index, text) + langdict = {} + for x, lang in enumerate(languages): + if lang not in langdict: + langdict[lang] = [] + langdict[lang].append((x, values[x])) + + results = {} + for language in unique: + # Get all indices and text values for a language + inputs = langdict[language] + + # Translate text in batches + outputs = [] + for chunk in self.batch([text for _, text in inputs], self.batchsize): + outputs.extend(self.translate(chunk, language, target, showmodels)) + + # Store output value + for y, (x, _) in enumerate(inputs): + if showmodels: + model, op = outputs[y] + results[x] = (op.strip(), language, model) + else: + results[x] = outputs[y].strip() + + # Return results in same order as input + results = [results[x] for x in sorted(results)] + return results[0] if isinstance(texts, str) else results + + def modelids(self): + """ + Runs a query to get a list of available language models from the Hugging Face API. + + Returns: + list of source-target language model ids + """ + + ids = [x.id for x in huggingface_hub.hf_api.HfApi().list_models(author="Helsinki-NLP")] if self.findmodels else [] + return set(ids) + + def detect(self, texts): + """ + Detects the language for each element in texts. + + Args: + texts: list of text + + Returns: + list of languages + """ + + # Default detector + if not self.langdetect or isinstance(self.langdetect, str): + return self.defaultdetect(texts) + + # Call external language detector + return self.langdetect(texts) + + def defaultdetect(self, texts): + """ + Default language detection model. + + Args: + texts: list of text + + Returns: + list of languages + """ + + if not self.detector: + if not STATICVECTORS: + raise ImportError('Language detection is not available - install "pipeline" extra to enable') + + # Get model path + path = self.langdetect if self.langdetect else Translation.DEFAULT_LANG_DETECT + + # Load language detection model + self.detector = StaticVectors(path) + + # Transform texts to format expected by language detection model + texts = [x.lower().replace("\n", " ").replace("\r\n", " ") for x in texts] + + # Detect languages + return [x[0][0] for x in self.detector.predict(texts)] + + def translate(self, texts, source, target, showmodels=False): + """ + Translates text from source to target language. + + Args: + texts: list of text + source: source language code + target: target language code + + Returns: + list of translated text + """ + + # Return original if already in target language + if source == target: + return texts + + # Load model and tokenizer + path, model, tokenizer = self.lookup(source, target) + + model.to(self.device) + indices = None + maxlength = Models.maxlength(model, tokenizer) + + with self.context(): + if hasattr(tokenizer, "lang_code_to_id"): + source = self.langid(tokenizer.lang_code_to_id, source) + target = self.langid(tokenizer.lang_code_to_id, target) + + tokenizer.src_lang = source + tokens, indices = self.tokenize(tokenizer, texts) + + translated = model.generate(**tokens, forced_bos_token_id=tokenizer.lang_code_to_id[target], max_length=maxlength) + else: + tokens, indices = self.tokenize(tokenizer, texts) + translated = model.generate(**tokens, max_length=maxlength) + + # Decode translations + translated = tokenizer.batch_decode(translated, skip_special_tokens=True) + + # Combine translations - handle splits on large text from tokenizer + results, last = [], -1 + for x, i in enumerate(indices): + if i == last: + if showmodels: + # Concatenate text portion of (path, text) tuple + prev = results[-1] + results[-1] = (prev[0], prev[1] + translated[x]) + else: + results[-1] += translated[x] + else: + results.append((path, translated[x]) if showmodels else translated[x]) + + last = i + + return results + + def lookup(self, source, target): + """ + Retrieves a translation model for source->target language. This method caches each model loaded. + + Args: + source: source language code + target: target language code + + Returns: + (model, tokenizer) + """ + + # Determine best translation model to use, load if necessary and return + path = self.modelpath(source, target) + if path not in self.models: + self.models[path] = self.load(path, "text2text-generation") + + return (path,) + self.models[path] + + def modelpath(self, source, target): + """ + Derives a translation model path given source and target languages. + + Args: + source: source language code + target: target language code + + Returns: + model path + """ + + # Lazy load model ids + if self.ids is None: + self.ids = self.modelids() + + # First try direct model + template = "Helsinki-NLP/opus-mt-%s-%s" + path = template % (source, target) + if path in self.ids: + return path + + # Use multi-language - english model + if self.findmodels and target == "en": + return template % ("mul", target) + + # Default model if no suitable model found + return self.path + + def langid(self, languages, target): + """ + Searches a list of languages for a prefix match on target. + + Args: + languages: list of languages + target: target language code + + Returns: + best match or None if no match found + """ + + for lang in languages: + if lang.startswith(target): + return lang + + return None diff --git a/src/python/txtai/pipeline/train/__init__.py b/src/python/txtai/pipeline/train/__init__.py new file mode 100644 index 0000000..b2bff6d --- /dev/null +++ b/src/python/txtai/pipeline/train/__init__.py @@ -0,0 +1,7 @@ +""" +Train imports +""" + +from .hfonnx import HFOnnx +from .hftrainer import HFTrainer +from .mlonnx import MLOnnx diff --git a/src/python/txtai/pipeline/train/hfonnx.py b/src/python/txtai/pipeline/train/hfonnx.py new file mode 100644 index 0000000..7637373 --- /dev/null +++ b/src/python/txtai/pipeline/train/hfonnx.py @@ -0,0 +1,198 @@ +""" +Hugging Face Transformers ONNX export module +""" + +from collections import OrderedDict +from io import BytesIO +from itertools import chain +from tempfile import NamedTemporaryFile + +# Conditional import +try: + from onnxruntime.quantization import quantize_dynamic + + ONNX_RUNTIME = True +except ImportError: + ONNX_RUNTIME = False + +from ...models import PoolingFactory +from ..tensors import Tensors + +# Core library imports +from ...util import Library + +library = Library() +torch = library.torch() +transformers = library.transformers() +Module = library.module() + + +class HFOnnx(Tensors): + """ + Exports a Hugging Face Transformer model to ONNX. + """ + + def __call__(self, path, task="default", output=None, quantize=False, opset=14): + """ + Exports a Hugging Face Transformer model to ONNX. + + Args: + path: path to model, accepts Hugging Face model hub id, local path or (model, tokenizer) tuple + task: optional model task or category, determines the model type and outputs, defaults to export hidden state + output: optional output model path, defaults to return byte array if None + quantize: if model should be quantized (requires onnx to be installed), defaults to False + opset: onnx opset, defaults to 14 + + Returns: + path to model output or model as bytes depending on output parameter + """ + + inputs, outputs, model = self.parameters(task) + + if isinstance(path, (list, tuple)): + model, tokenizer = path + model = model.cpu() + else: + model = model(path) + tokenizer = transformers.AutoTokenizer.from_pretrained(path) + + # Generate dummy inputs + dummy = dict(tokenizer(["test inputs"], return_tensors="pt")) + + # Default to BytesIO if no output file provided + output = output if output else BytesIO() + + # Export model to ONNX + torch.onnx.export( + ModelWrapper(model), + (dummy,), + output, + opset_version=opset, + do_constant_folding=True, + input_names=list(inputs.keys()), + output_names=list(outputs.keys()), + dynamic_axes=dict(chain(inputs.items(), outputs.items())), + dynamo=False, + ) + + # Quantize model + if quantize: + if not ONNX_RUNTIME: + raise ImportError('onnxruntime is not available - install "pipeline" extra to enable') + + output = self.quantization(output) + + if isinstance(output, BytesIO): + # Reset stream and return bytes + output.seek(0) + output = output.read() + + return output + + def quantization(self, output): + """ + Quantizes an ONNX model. + + Args: + output: path to ONNX model or BytesIO with model data + + Returns: + quantized model as file path or bytes + """ + + temp = None + if isinstance(output, BytesIO): + with NamedTemporaryFile(suffix=".quant", delete=False) as tmpfile: + temp = tmpfile.name + + with open(temp, "wb") as f: + f.write(output.getbuffer()) + + output = temp + + # Quantize model + quantize_dynamic(output, output, extra_options={"MatMulConstBOnly": False}) + + # Read file back to bytes if temp file was created + if temp: + with open(temp, "rb") as f: + output = f.read() + + return output + + def parameters(self, task): + """ + Defines inputs and outputs for an ONNX model. + + Args: + task: task name used to lookup model configuration + + Returns: + (inputs, outputs, model function) + """ + + inputs = OrderedDict( + [ + ("input_ids", {0: "batch", 1: "sequence"}), + ("attention_mask", {0: "batch", 1: "sequence"}), + ("token_type_ids", {0: "batch", 1: "sequence"}), + ] + ) + + config = { + "default": (OrderedDict({"last_hidden_state": {0: "batch", 1: "sequence"}}), transformers.AutoModel.from_pretrained), + "pooling": (OrderedDict({"embeddings": {0: "batch", 1: "sequence"}}), lambda x: PoolingFactory.create({"path": x, "device": -1})), + "question-answering": ( + OrderedDict( + { + "start_logits": {0: "batch", 1: "sequence"}, + "end_logits": {0: "batch", 1: "sequence"}, + } + ), + transformers.AutoModelForQuestionAnswering.from_pretrained, + ), + "text-classification": (OrderedDict({"logits": {0: "batch"}}), transformers.AutoModelForSequenceClassification.from_pretrained), + } + + # Aliases + config["zero-shot-classification"] = config["text-classification"] + + return (inputs,) + config[task] + + +class ModelWrapper(Module): + """ + Wraps an model to control named inputs for export. + """ + + def __init__(self, model): + """ + Creates a new ModelWrapper. + + Args: + model: model to export + """ + + super().__init__() + + self.model = model.eval() + + # pylint: disable=W0221 + def forward(self, input_ids=None, attention_mask=None, token_type_ids=None): + """ + Runs inputs through model and returns outputs. + + Args: + inputs: model inputs + + Returns: + model outputs + """ + + # Build list of arguments dynamically since some models take token_type_ids + # and others don't + inputs = {"input_ids": input_ids, "attention_mask": attention_mask} + if token_type_ids is not None: + inputs["token_type_ids"] = token_type_ids + + return self.model.forward(**inputs) diff --git a/src/python/txtai/pipeline/train/hftrainer.py b/src/python/txtai/pipeline/train/hftrainer.py new file mode 100644 index 0000000..3267684 --- /dev/null +++ b/src/python/txtai/pipeline/train/hftrainer.py @@ -0,0 +1,471 @@ +""" +Hugging Face Transformers trainer wrapper module +""" + +import os +import sys + +# Conditional import +try: + from peft import LoraConfig, TaskType, get_peft_model, prepare_model_for_kbit_training + + # pylint: disable=C0412 + from transformers import BitsAndBytesConfig + + PEFT = True +except ImportError: + PEFT = False + +from ...data import Labels, Questions, Sequences, Texts +from ...models import Models, TokenDetection +from ..tensors import Tensors + +# Core library imports +from ...util import Library + +library = Library() +torch = library.torch() +transformers = library.transformers() +HFTrainingArguments = library.arguments() +Trainer = library.trainer() + + +class HFTrainer(Tensors): + """ + Trains a new Hugging Face Transformer model using the Trainer framework. + """ + + # pylint: disable=R0913 + def __call__( + self, + base, + train, + validation=None, + columns=None, + maxlength=None, + stride=128, + task="text-classification", + prefix=None, + metrics=None, + tokenizers=None, + checkpoint=None, + quantize=None, + lora=None, + merge="concat", + teacher=None, + distillation=(1.0, 0.5), + **args + ): + """ + Builds a new model using arguments. + + Args: + base: path to base model, accepts Hugging Face model hub id, local path or (model, tokenizer) tuple + train: training data + validation: validation data + columns: tuple of columns to use for text/label, defaults to (text, None, label) + maxlength: maximum sequence length, defaults to tokenizer.model_max_length + stride: chunk size for splitting data for QA tasks + task: optional model task or category, determines the model type, defaults to "text-classification" + prefix: optional source prefix + metrics: optional function that computes and returns a dict of evaluation metrics + tokenizers: optional number of concurrent tokenizers, defaults to None + checkpoint: optional resume from checkpoint flag or path to checkpoint directory, defaults to None + quantize: quantization configuration to pass to base model + lora: lora configuration to pass to PEFT model + merge: determines how chunks are combined for language modeling tasks - "concat" (default), "pack" or None + teacher: path to teacher model for distillation, supports same options as base + distillation: distillation parameters for (temperature, alpha), defaults to (1.0, 0.5) + args: training arguments + + Returns: + (model, tokenizer) + """ + + # Quantization / LoRA support + if (quantize or lora) and not PEFT: + raise ImportError('PEFT is not available - install "pipeline" extra to enable') + + # Parse TrainingArguments + args = self.parse(args) + + # Set seed for model reproducibility + transformers.set_seed(args.seed) + + # Load model configuration, tokenizer and max sequence length + config, tokenizer, maxlength = self.load(base, maxlength) + + # Default tokenizer pad token if it's not set + tokenizer.pad_token = tokenizer.pad_token if tokenizer.pad_token is not None else tokenizer.eos_token + + # Prepare parameters + process, collator, labels = self.prepare(task, train, tokenizer, columns, maxlength, stride, prefix, merge, args) + + # Tokenize training and validation data + train, validation = process(train, validation, os.cpu_count() if tokenizers and isinstance(tokenizers, bool) else tokenizers) + + # Create model to train + model = self.model(task, base, config, labels, tokenizer, quantize) + + # Default config pad token if it's not set + model.config.pad_token_id = model.config.pad_token_id if model.config.pad_token_id is not None else model.config.eos_token_id + + # Load as PEFT model, if necessary + model = self.peft(task, lora, model) + + # Add model to collator + if collator: + collator.model = model + + # Distillation trainer + if teacher: + teacher = self.model(task, teacher, None, None, tokenizer, None) + trainer, distillation = DistillationTrainer, {"teacher": teacher, "distillation": distillation} + + # Standard trainer + else: + trainer, distillation = transformers.Trainer, {} + + # Build trainer + trainer = trainer( + model=model, + processing_class=tokenizer, + data_collator=collator, + args=args, + train_dataset=train, + eval_dataset=validation if validation else None, + compute_metrics=metrics, + **distillation + ) + + # Run training + trainer.train(resume_from_checkpoint=checkpoint) + + # Run evaluation + if validation: + trainer.evaluate() + + # Save model outputs + if args.should_save: + trainer.save_model() + trainer.save_state() + + # Put model in eval mode to disable weight updates and return (model, tokenizer) + return (model.eval(), tokenizer) + + def parse(self, updates): + """ + Parses and merges custom arguments with defaults. + + Args: + updates: custom arguments + + Returns: + TrainingArguments + """ + + # Default training arguments + args = {"output_dir": "", "save_strategy": "no", "report_to": "none", "log_level": "warning", "use_cpu": not Models.hasaccelerator()} + + # Apply custom arguments + args.update(updates) + + return TrainingArguments(**args) + + def load(self, base, maxlength): + """ + Loads the base config and tokenizer. + + Args: + base: base model - supports a file path or (model, tokenizer) tuple + maxlength: maximum sequence length + + Returns: + (config, tokenizer, maxlength) + """ + + if isinstance(base, (list, tuple)): + # Unpack existing config and tokenizer + model, tokenizer = base + config = model.config + else: + # Load config + config = transformers.AutoConfig.from_pretrained(base) + + # Load tokenizer + tokenizer = transformers.AutoTokenizer.from_pretrained(base) + + # Detect unbounded tokenizer + Models.checklength(config, tokenizer) + + # Derive max sequence length + maxlength = min(maxlength if maxlength else sys.maxsize, tokenizer.model_max_length) + + return (config, tokenizer, maxlength) + + def prepare(self, task, train, tokenizer, columns, maxlength, stride, prefix, merge, args): + """ + Prepares data for model training. + + Args: + task: optional model task or category, determines the model type, defaults to "text-classification" + train: training data + tokenizer: model tokenizer + columns: tuple of columns to use for text/label, defaults to (text, None, label) + maxlength: maximum sequence length, defaults to tokenizer.model_max_length + stride: chunk size for splitting data for QA tasks + prefix: optional source prefix + merge: determines how chunks are combined for language modeling tasks - "concat" (default), "pack" or None + args: training arguments + """ + + process, collator, labels = None, None, None + + # 16-bit training + fp16 = args.fp16 or args.bf16 + + if task == "language-generation": + process = Texts(tokenizer, columns, maxlength, merge) + collator = transformers.DataCollatorForLanguageModeling(tokenizer, mlm=False, pad_to_multiple_of=8 if fp16 else None) + elif task in ("language-modeling", "token-detection"): + process = Texts(tokenizer, columns, maxlength, merge) + collator = transformers.DataCollatorForLanguageModeling(tokenizer, pad_to_multiple_of=8 if fp16 else None) + elif task == "question-answering": + process = Questions(tokenizer, columns, maxlength, stride) + elif task == "sequence-sequence": + process = Sequences(tokenizer, columns, maxlength, prefix) + collator = transformers.DataCollatorForSeq2Seq(tokenizer, pad_to_multiple_of=8 if fp16 else None) + else: + process = Labels(tokenizer, columns, maxlength) + labels = process.labels(train) + + return process, collator, labels + + def model(self, task, base, config, labels, tokenizer, quantize): + """ + Loads the base model to train. + + Args: + task: optional model task or category, determines the model type, defaults to "text-classification" + base: base model - supports a file path or (model, tokenizer) tuple + config: model configuration + labels: number of labels + tokenizer: model tokenizer + quantize: quantization config + + Returns: + model + """ + + if labels is not None: + # Add number of labels to config + config.update({"num_labels": labels}) + + # Format quantization configuration + quantization = self.quantization(quantize) + + # Clear quantization configuration if GPU is not available + quantization = quantization if torch.cuda.is_available() else None + + # pylint: disable=E1120 + # Unpack existing model or create new model from config + if isinstance(base, (list, tuple)) and not isinstance(base[0], str): + return base[0] + if task == "language-generation": + return transformers.AutoModelForCausalLM.from_pretrained(base, config=config, quantization_config=quantization) + if task == "language-modeling": + return transformers.AutoModelForMaskedLM.from_pretrained(base, config=config, quantization_config=quantization) + if task == "question-answering": + return transformers.AutoModelForQuestionAnswering.from_pretrained(base, config=config, quantization_config=quantization) + if task == "sequence-sequence": + return transformers.AutoModelForSeq2SeqLM.from_pretrained(base, config=config, quantization_config=quantization) + if task == "token-detection": + return TokenDetection( + transformers.AutoModelForMaskedLM.from_pretrained(base, config=config, quantization_config=quantization, attn_implementation="eager"), + transformers.AutoModelForPreTraining.from_pretrained( + base, config=config, quantization_config=quantization, attn_implementation="eager" + ), + tokenizer, + ) + + # Default task + return transformers.AutoModelForSequenceClassification.from_pretrained(base, config=config, quantization_config=quantization) + + def quantization(self, quantize): + """ + Formats and returns quantization configuration. + + Args: + quantize: input quantization configuration + + Returns: + formatted quantization configuration + """ + + if quantize: + # Default quantization settings when set to True + if isinstance(quantize, bool): + quantize = { + "load_in_4bit": True, + "bnb_4bit_use_double_quant": True, + "bnb_4bit_quant_type": "nf4", + "bnb_4bit_compute_dtype": "bfloat16", + } + + # Load dictionary configuration + if isinstance(quantize, dict): + quantize = BitsAndBytesConfig(**quantize) + + return quantize if quantize else None + + def peft(self, task, lora, model): + """ + Wraps the input model as a PEFT model if lora configuration is set. + + Args: + task: optional model task or category, determines the model type, defaults to "text-classification" + lora: lora configuration + model: transformers model + + Returns: + wrapped model if lora configuration set, otherwise input model is returned + """ + + if lora: + # Format LoRA configuration + config = self.lora(task, lora) + + # Wrap as PeftModel + model = prepare_model_for_kbit_training(model) + model = get_peft_model(model, config) + model.print_trainable_parameters() + + return model + + def lora(self, task, lora): + """ + Formats and returns LoRA configuration. + + Args: + task: optional model task or category, determines the model type, defaults to "text-classification" + lora: lora configuration + + Returns: + formatted lora configuration + """ + + if lora: + # Default lora settings when set to True + if isinstance(lora, bool): + lora = {"r": 16, "lora_alpha": 8, "target_modules": "all-linear", "lora_dropout": 0.05, "bias": "none"} + + # Load dictionary configuration + if isinstance(lora, dict): + # Set task type if missing + if "task_type" not in lora: + lora["task_type"] = self.loratask(task) + + lora = LoraConfig(**lora) + + return lora + + def loratask(self, task): + """ + Looks up the corresponding LoRA task for input task. + + Args: + task: optional model task or category, determines the model type, defaults to "text-classification" + + Returns: + lora task + """ + + # Task mapping + tasks = { + "language-generation": TaskType.CAUSAL_LM, + "language-modeling": TaskType.FEATURE_EXTRACTION, + "question-answering": TaskType.QUESTION_ANS, + "sequence-sequence": TaskType.SEQ_2_SEQ_LM, + "text-classification": TaskType.SEQ_CLS, + "token-detection": TaskType.FEATURE_EXTRACTION, + } + + # Default task + task = task if task in tasks else "text-classification" + + # Lookup and return task + return tasks[task] + + +class TrainingArguments(HFTrainingArguments): + """ + Extends standard TrainingArguments to make the output directory optional for transient models. + """ + + @property + def should_save(self): + """ + Override should_save to disable model saving when output directory is None. + + Returns: + If model should be saved + """ + + return super().should_save if self.output_dir else False + + +class DistillationTrainer(Trainer): + """ + Knowledge distillation trainer. This is a custom trainer that adds a blended KLDiv loss target from a + teacher model. + """ + + def __init__(self, *args, teacher=None, distillation=None, **kwargs): + super().__init__(*args, **kwargs) + + # Move teacher to the same device as the student + self.teacher = teacher + self.teacher.eval().to(self.model.device) + + # Store distillation parameters + self.temperature, self.alpha = distillation + + # pylint: disable=W0221,W0613 + def compute_loss(self, model, inputs, return_outputs=False, num_items_in_batch=None): + """ + Custom loss function. Computes loss as a blend of the standard cross entropy loss in combination + with KLDiv loss from the teacher. + + Args: + model: student model + input: model inputs + + Returns: + computed loss + """ + + # Extract true labels + labels = inputs.pop("labels") + + # Forward pass student + outputs = model(**inputs) + logits = outputs.get("logits") + + # Forward pass teacher + with torch.no_grad(): + toutputs = self.teacher(**inputs) + tlogits = toutputs.get("logits") + + # Calculate Cross Entropy Loss (hard labels) + lossce = torch.nn.functional.cross_entropy(logits, labels) + + # Calculate Distillation Loss (soft labels with temperature) + # Scale logits by temperature and calculate KL divergence + losskd = torch.nn.KLDivLoss(reduction="batchmean")( + torch.nn.functional.log_softmax(logits / self.temperature, dim=-1), torch.nn.functional.softmax(tlogits / self.temperature, dim=-1) + ) * (self.temperature**2) + + # Combine losses + loss = (1.0 - self.alpha) * lossce + self.alpha * losskd + + return (loss, outputs) if return_outputs else loss diff --git a/src/python/txtai/pipeline/train/mlonnx.py b/src/python/txtai/pipeline/train/mlonnx.py new file mode 100644 index 0000000..6160dd2 --- /dev/null +++ b/src/python/txtai/pipeline/train/mlonnx.py @@ -0,0 +1,63 @@ +""" +Machine learning model to ONNX export module +""" + +from ..base import Pipeline + +try: + from onnxmltools import convert_sklearn + + from skl2onnx.common.data_types import StringTensorType + from skl2onnx.helpers.onnx_helper import save_onnx_model, select_model_inputs_outputs + + ONNX_MLTOOLS = True +except ImportError: + ONNX_MLTOOLS = False + + +class MLOnnx(Pipeline): + """ + Exports a machine learning model to ONNX using ONNXMLTools. + """ + + def __init__(self): + """ + Creates a new MLOnnx pipeline. + """ + + if not ONNX_MLTOOLS: + raise ImportError('MLOnnx pipeline is not available - install "pipeline" extra to enable') + + def __call__(self, model, task="default", output=None, opset=12): + """ + Exports a machine learning model to ONNX using ONNXMLTools. + + Args: + model: model to export + task: optional model task or category + output: optional output model path, defaults to return byte array if None + opset: onnx opset, defaults to 12 + + Returns: + path to model output or model as bytes depending on output parameter + """ + + # Convert scikit-learn model to ONNX + model = convert_sklearn(model, task, initial_types=[("input_ids", StringTensorType([None, None]))], target_opset=opset) + + # Prune model graph down to only output probabilities + model = select_model_inputs_outputs(model, outputs="probabilities") + + # pylint: disable=E1101 + # Rename output to logits for consistency with other models + model.graph.output[0].name = "logits" + + # Find probabilities output node and rename to logits + for node in model.graph.node: + for x, _ in enumerate(node.output): + if node.output[x] == "probabilities": + node.output[x] = "logits" + + # Save model to specified output path or return bytes + model = save_onnx_model(model, output) + return output if output else model diff --git a/src/python/txtai/scoring/__init__.py b/src/python/txtai/scoring/__init__.py new file mode 100644 index 0000000..db4bca3 --- /dev/null +++ b/src/python/txtai/scoring/__init__.py @@ -0,0 +1,13 @@ +""" +Scoring imports +""" + +from .base import Scoring +from .bm25 import BM25 +from .factory import ScoringFactory +from .normalize import Normalize +from .pgtext import PGText +from .sif import SIF +from .sparse import Sparse +from .terms import Terms +from .tfidf import TFIDF diff --git a/src/python/txtai/scoring/base.py b/src/python/txtai/scoring/base.py new file mode 100644 index 0000000..233b557 --- /dev/null +++ b/src/python/txtai/scoring/base.py @@ -0,0 +1,198 @@ +""" +Scoring module +""" + + +class Scoring: + """ + Base scoring. + """ + + def __init__(self, config=None): + """ + Creates a new Scoring instance. + + Args: + config: input configuration + """ + + # Scoring configuration + self.config = config if config is not None else {} + + # Transform columns + columns = self.config.get("columns", {}) + self.text = columns.get("text", "text") + self.object = columns.get("object", "object") + + # Vector model, if available + self.model = None + + def insert(self, documents, index=None, checkpoint=None): + """ + Inserts documents into the scoring index. + + Args: + documents: list of (id, dict|text|tokens, tags) + index: indexid offset + checkpoint: optional checkpoint directory, enables indexing restart + """ + + raise NotImplementedError + + def delete(self, ids): + """ + Deletes documents from scoring index. + + Args: + ids: list of ids to delete + """ + + raise NotImplementedError + + def index(self, documents=None): + """ + Indexes a collection of documents using a scoring method. + + Args: + documents: list of (id, dict|text|tokens, tags) + """ + + # Insert documents + if documents: + self.insert(documents) + + def upsert(self, documents=None): + """ + Convience method for API clarity. Calls index method. + + Args: + documents: list of (id, dict|text|tokens, tags) + """ + + self.index(documents) + + def weights(self, tokens): + """ + Builds a weights vector for each token in input tokens. + + Args: + tokens: input tokens + + Returns: + list of weights for each token + """ + + raise NotImplementedError + + def search(self, query, limit=3): + """ + Search index for documents matching query. + + Args: + query: input query + limit: maximum results + + Returns: + list of (id, score) or (data, score) if content is enabled + """ + + raise NotImplementedError + + def batchsearch(self, queries, limit=3, threads=True): + """ + Search index for documents matching queries. + + Args: + queries: queries to run + limit: maximum results + threads: run as threaded search if True and supported + """ + + raise NotImplementedError + + def count(self): + """ + Returns the total number of documents indexed. + + Returns: + total number of documents indexed + """ + + raise NotImplementedError + + def load(self, path): + """ + Loads a saved Scoring object from path. + + Args: + path: directory path to load scoring index + """ + + raise NotImplementedError + + def save(self, path): + """ + Saves a Scoring object to path. + + Args: + path: directory path to save scoring index + """ + + raise NotImplementedError + + def close(self): + """ + Closes this Scoring object. + """ + + raise NotImplementedError + + def findmodel(self): + """ + Returns the associated vector model used by this scoring instance, if any. + + Returns: + associated vector model + """ + + return self.model + + def issparse(self): + """ + Check if this scoring instance has an associated sparse keyword or sparse vector index. + + Returns: + True if this index has an associated sparse index + """ + + raise NotImplementedError + + def isweighted(self): + """ + Check if this scoring instance is for term weighting (i.e.) it has no associated sparse index. + + Returns: + True if this index is for term weighting + """ + + return not self.issparse() + + def isnormalized(self): + """ + Check if this scoring instance returns normalized scores. + + Returns: + True if normalize is enabled, False otherwise + """ + + raise NotImplementedError + + def isbayes(self): + """ + Check if this scoring instance uses Bayesian (BB25) normalization. + + Returns: + True if BB25/Bayesian normalization is active, False otherwise + """ + + raise NotImplementedError diff --git a/src/python/txtai/scoring/bm25.py b/src/python/txtai/scoring/bm25.py new file mode 100644 index 0000000..24b7aac --- /dev/null +++ b/src/python/txtai/scoring/bm25.py @@ -0,0 +1,32 @@ +""" +BM25 module +""" + +from .tfidf import TFIDF + +# Core library imports +from ..util import Library + +np = Library().numpy() + + +class BM25(TFIDF): + """ + Best matching (BM25) scoring. + """ + + def __init__(self, config=None): + super().__init__(config) + + # BM25 configurable parameters + self.k1 = self.config.get("k1", 1.2) + self.b = self.config.get("b", 0.75) + + def computeidf(self, freq): + # Calculate BM25 IDF score + return np.log(1 + (self.total - freq + 0.5) / (freq + 0.5)) + + def score(self, freq, idf, length): + # Calculate BM25 score + k = self.k1 * ((1 - self.b) + self.b * length / self.avgdl) + return idf * (freq * (self.k1 + 1)) / (freq + k) diff --git a/src/python/txtai/scoring/factory.py b/src/python/txtai/scoring/factory.py new file mode 100644 index 0000000..70cccf7 --- /dev/null +++ b/src/python/txtai/scoring/factory.py @@ -0,0 +1,95 @@ +""" +Factory module +""" + +from ..util import Resolver + +from .bm25 import BM25 +from .pgtext import PGText +from .sif import SIF +from .sparse import Sparse +from .tfidf import TFIDF + + +class ScoringFactory: + """ + Methods to create Scoring indexes. + """ + + @staticmethod + def create(config, models=None): + """ + Factory method to construct a Scoring instance. + + Args: + config: scoring configuration parameters + models: models cache + + Returns: + Scoring + """ + + # Scoring instance + scoring = None + + # Support string and dict configuration + if isinstance(config, str): + config = {"method": config} + + # Get scoring method + method = config.get("method", "bm25") + + if method == "bm25": + scoring = BM25(config) + elif method == "pgtext": + scoring = PGText(config) + elif method == "sif": + scoring = SIF(config) + elif method == "sparse": + scoring = Sparse(config, models) + elif method == "tfidf": + scoring = TFIDF(config) + else: + # Resolve custom method + scoring = ScoringFactory.resolve(method, config) + + # Store config back + config["method"] = method + + return scoring + + @staticmethod + def issparse(config): + """ + Checks if this scoring configuration builds a sparse index. + + Args: + config: scoring configuration + + Returns: + True if this config is for a sparse index + """ + + # Types that are always a sparse index + indexes = ["pgtext", "sparse"] + + # True if this config is for a sparse index + return config and isinstance(config, dict) and (config.get("method") in indexes or config.get("terms")) + + @staticmethod + def resolve(backend, config): + """ + Attempt to resolve a custom backend. + + Args: + backend: backend class + config: index configuration parameters + + Returns: + Scoring + """ + + try: + return Resolver()(backend)(config) + except Exception as e: + raise ImportError(f"Unable to resolve scoring backend: '{backend}'") from e diff --git a/src/python/txtai/scoring/normalize.py b/src/python/txtai/scoring/normalize.py new file mode 100644 index 0000000..5b01e77 --- /dev/null +++ b/src/python/txtai/scoring/normalize.py @@ -0,0 +1,120 @@ +""" +Normalize module +""" + +# Core library imports +from ..util import Library + +np = Library().numpy() + + +class Normalize: + """ + Applies score normalization methods. + + Bayesian mode supports BB25-style score calibration aliases ("bayes", "bb25", "bayesian-bm25"). + Reference implementations: + - https://github.com/instructkr/bb25 + - https://github.com/cognica-io/bayesian-bm25 + """ + + def __init__(self, config): + """ + Creates a new Normalize instance. + + Args: + config: normalize configuration + """ + + # Normalize settings + self.config = config if isinstance(config, dict) else {} + method = self.config.get("method", config if isinstance(config, str) else "default") + self.method = str(method).lower() + + # Bayesian settings + self.alpha = float(self.config.get("alpha", 1.0)) + self.beta = self.config.get("beta") + self.beta = float(self.beta) if self.beta is not None else self.beta + + def isbayes(self): + """ + Checks if Bayesian normalization mode is active. + + Returns: + True if using BB25/Bayesian normalization + """ + + return self.method in ("bayes", "bayesian", "bayesian-bm25", "bb25") + + def __call__(self, scores, avgscore): + """ + Normalizes scores. + + Args: + scores: list of (id, score) + avgscore: average score across index + + Returns: + normalized scores + """ + + return self.bayes(scores) if self.isbayes() else self.default(scores, avgscore) + + def default(self, scores, avgscore): + """ + Default normalization implementation. + + Args: + scores: list of (id, score) + avgscore: average score across index + + Returns: + normalized scores + """ + + # Use average index score in max score calculation + maxscore = min(scores[0][1] + avgscore, 6 * avgscore) + + # Normalize scores between 0 - 1 using maxscore + return [(uid, min(score / maxscore, 1.0)) for uid, score in scores] + + def bayes(self, scores): + """ + BB25/Bayesian normalization implementation. + + Args: + scores: list of (id, score) + + Returns: + normalized scores + """ + + # Convert scores to numpy array + values = np.array([score for _, score in scores], dtype=np.float32) + probabilities = np.zeros(values.shape[0], dtype=np.float32) + + # Follow BB25 candidate-set behavior: + # - estimate statistics on positive-score candidates only + # - assign zero-score candidates a final score of 0.0 + positive = values > 0.0 + if not np.any(positive): + return [(uid, 0.0) for uid, _ in scores] + + candidates = values[positive] + + # Dynamically derive beta using candidate score distribution, if not configured + beta = self.beta if self.beta is not None else float(np.median(candidates)) + + # Scale alpha by standard deviation for score-range invariance + std = float(np.std(candidates)) + alpha = abs(self.alpha / std if std > 0 else self.alpha) + + # Compute sigmoid likelihood for positive-score candidates. + # In this generic normalize flow, term-structure priors (tf/doc length) are not available, + # so we use the likelihood-only BB25 transform (equivalent to a flat prior of 0.5). + logits = np.clip(alpha * (candidates - beta), -500, 500) + probabilities[positive] = 1.0 / (1.0 + np.exp(-logits)) + probabilities = np.clip(probabilities, 0.0, 1.0) + + # Convert back to score tuples + return [(uid, float(probabilities[x])) for x, (uid, _) in enumerate(scores)] diff --git a/src/python/txtai/scoring/pgtext.py b/src/python/txtai/scoring/pgtext.py new file mode 100644 index 0000000..68b56d1 --- /dev/null +++ b/src/python/txtai/scoring/pgtext.py @@ -0,0 +1,188 @@ +""" +PGText module +""" + +import os +import re + +# Conditional import +try: + from sqlalchemy import create_engine, desc, delete, func, text + from sqlalchemy import Column, Computed, Index, Integer, MetaData, StaticPool, Table, Text + from sqlalchemy.dialects.postgresql import TSVECTOR + from sqlalchemy.orm import Session + from sqlalchemy.schema import CreateSchema + + PGTEXT = True +except ImportError: + PGTEXT = False + +from .base import Scoring + + +class PGText(Scoring): + """ + Postgres full text search (FTS) based scoring. + """ + + def __init__(self, config=None): + super().__init__(config) + + if not PGTEXT: + raise ImportError('PGText is not available - install "scoring" extra to enable') + + # Database connection + self.engine, self.database, self.connection, self.table = None, None, None, None + + # Language + self.language = self.config.get("language", "english") + + def insert(self, documents, index=None, checkpoint=None): + # Initialize tables + self.initialize(recreate=True) + + # Collection of rows to insert + rows = [] + + # Collect rows + for uid, document, _ in documents: + # Extract text, if necessary + if isinstance(document, dict): + document = document.get(self.text, document.get(self.object)) + + if document is not None: + # If index is passed, use indexid, otherwise use id + uid = index if index is not None else uid + + # Add row if the data type is accepted + if isinstance(document, (str, list)): + rows.append((uid, " ".join(document) if isinstance(document, list) else document)) + + # Increment index + index = index + 1 if index is not None else None + + # Insert rows + self.database.execute(self.table.insert(), [{"indexid": x, "text": text} for x, text in rows]) + + def delete(self, ids): + self.database.execute(delete(self.table).where(self.table.c["indexid"].in_(ids))) + + def weights(self, tokens): + # Not supported + return None + + def search(self, query, limit=3): + # Replace wildcards with prefix wildcard term + query = re.sub(r"(? 1e-5] + + def batchsearch(self, queries, limit=3, threads=True): + return [self.search(query, limit) for query in queries] + + def count(self): + # pylint: disable=E1102 + return self.database.query(func.count(self.table.c["indexid"])).scalar() + + def load(self, path): + # Reset database to original checkpoint + if self.database: + self.database.rollback() + self.connection.rollback() + + # Initialize tables + self.initialize() + + def save(self, path): + # Commit session and connection + if self.database: + self.database.commit() + self.connection.commit() + + def close(self): + if self.database: + self.database.close() + self.engine.dispose() + + def issparse(self): + return True + + def isnormalized(self): + return True + + def isbayes(self): + return False + + def initialize(self, recreate=False): + """ + Initializes a new database session. + + Args: + recreate: Recreates the database tables if True + """ + + if not self.database: + # Create engine, connection and session + self.engine = create_engine(self.config.get("url", os.environ.get("SCORING_URL")), poolclass=StaticPool, echo=False) + self.connection = self.engine.connect() + self.database = Session(self.connection) + + # Set default schema, if necessary + schema = self.config.get("schema") + if schema: + with self.engine.begin(): + self.sqldialect(CreateSchema(schema, if_not_exists=True)) + + self.sqldialect(text("SET search_path TO :schema"), {"schema": schema}) + + # Table name + table = self.config.get("table", "scoring") + + # Create vectors table + self.table = Table( + table, + MetaData(), + Column("indexid", Integer, primary_key=True, autoincrement=False), + Column("text", Text), + ( + Column("vector", TSVECTOR, Computed(f"to_tsvector('{self.language}', text)", persisted=True)) + if self.engine.dialect.name == "postgresql" + else Column("vector", Integer) + ), + ) + + # Create text index + index = Index( + f"{table}-index", + self.table.c["vector"], + postgresql_using="gin", + ) + + # Drop and recreate table + if recreate: + self.table.drop(self.connection, checkfirst=True) + index.drop(self.connection, checkfirst=True) + + # Create table and index + self.table.create(self.connection, checkfirst=True) + index.create(self.connection, checkfirst=True) + + def sqldialect(self, sql, parameters=None): + """ + Executes a SQL statement based on the current SQL dialect. + + Args: + sql: SQL to execute + parameters: optional bind parameters + """ + + args = (sql, parameters) if self.engine.dialect.name == "postgresql" else (text("SELECT 1"),) + self.database.execute(*args) diff --git a/src/python/txtai/scoring/sif.py b/src/python/txtai/scoring/sif.py new file mode 100644 index 0000000..c57cd50 --- /dev/null +++ b/src/python/txtai/scoring/sif.py @@ -0,0 +1,35 @@ +""" +SIF module +""" + +from .tfidf import TFIDF + +# Core library imports +from ..util import Library + +np = Library().numpy() + + +class SIF(TFIDF): + """ + Smooth Inverse Frequency (SIF) scoring. + """ + + def __init__(self, config=None): + super().__init__(config) + + # SIF configurable parameters + self.a = self.config.get("a", 1e-3) + + def computefreq(self, tokens): + # Default method computes frequency for a single entry + # SIF uses word frequencies across entire index + return {token: self.wordfreq[token] for token in tokens} + + def score(self, freq, idf, length): + # Set freq to word frequencies across entire index when freq and idf shape don't match + if isinstance(freq, np.ndarray) and freq.shape != np.array(idf).shape: + freq.fill(freq.sum()) + + # Calculate SIF score + return self.a / (self.a + freq / self.tokens) diff --git a/src/python/txtai/scoring/sparse.py b/src/python/txtai/scoring/sparse.py new file mode 100644 index 0000000..64e17b6 --- /dev/null +++ b/src/python/txtai/scoring/sparse.py @@ -0,0 +1,239 @@ +""" +Sparse module +""" + +from queue import Queue +from threading import Thread + +from ..ann import SparseANNFactory +from ..vectors import SparseVectorsFactory + +from .base import Scoring +from .normalize import Normalize + + +class Sparse(Scoring): + """ + Sparse vector scoring. + """ + + # End of stream message + COMPLETE = 1 + + def __init__(self, config=None, models=None): + super().__init__(config) + + # Vector configuration + mapping = {"vectormethod": "method", "vectornormalize": "normalize"} + config = {k: v for k, v in config.items() if k not in mapping.values()} + for k, v in mapping.items(): + if k in config: + config[v] = config[k] + + # Load the SparseVectors model + self.model = SparseVectorsFactory.create(config, models) + + # Normalize search outputs if vectors are not normalized already + # Supports: True (default linear, scale=30.0), float (custom scale), + # "bb25"/"bayes" (Bayesian sigmoid calibration), False (disabled) + self.isnormalize = self.config.get("normalize", True) + + # Create Bayesian normalizer when a Bayesian method is configured + self.normalizer = None + if isinstance(self.isnormalize, (str, dict)): + normalizer = Normalize(self.isnormalize) + if normalizer.isbayes(): + self.normalizer = normalizer + + # Sparse ANN + self.ann = None + + # Encoding processing parameters + self.batch = self.config.get("batch", 1024) + self.thread, self.queue, self.data = None, None, None + + def insert(self, documents, index=None, checkpoint=None): + # Start processing thread, if necessary + self.start(checkpoint) + + data = [] + for uid, document, tags in documents: + # Extract text, if necessary + if isinstance(document, dict): + document = document.get(self.text, document.get(self.object)) + + if document is not None: + # Add data + data.append((uid, " ".join(document) if isinstance(document, list) else document, tags)) + + # Add batch of data + self.queue.put(data) + + def delete(self, ids): + self.ann.delete(ids) + + def index(self, documents=None): + # Insert documents, if provided + if documents: + self.insert(documents) + + # Create ANN, if there is pending data + embeddings = self.stop() + if embeddings is not None: + self.ann = SparseANNFactory.create(self.config) + self.ann.index(embeddings) + + def upsert(self, documents=None): + # Insert documents, if provided + if documents: + self.insert(documents) + + # Check for existing index and pending data + if self.ann: + embeddings = self.stop() + if embeddings is not None: + self.ann.append(embeddings) + else: + self.index() + + def weights(self, tokens): + # Not supported + return None + + def search(self, query, limit=3): + return self.batchsearch([query], limit)[0] + + def batchsearch(self, queries, limit=3, threads=True): + # Convert queries to embedding vectors + embeddings = self.model.batchtransform((None, query, None) for query in queries) + + # Run ANN search + scores = self.ann.search(embeddings, limit) + + # Normalize scores if normalization IS enabled AND vector normalization IS NOT enabled + return self.normalize(embeddings, scores) if self.isnormalize and not self.model.isnormalize else scores + + def count(self): + return self.ann.count() + + def load(self, path): + self.ann = SparseANNFactory.create(self.config) + self.ann.load(path) + + def save(self, path): + # Save Sparse ANN + if self.ann: + self.ann.save(path) + + def close(self): + # Close Sparse ANN + if self.ann: + self.ann.close() + + # Clear parameters + self.model, self.ann, self.thread, self.queue = None, None, None, None + + def issparse(self): + return True + + def isnormalized(self): + return self.isnormalize or self.model.isnormalize + + def isbayes(self): + return self.normalizer is not None + + def start(self, checkpoint): + """ + Starts an encoding processing thread. + + Args: + checkpoint: checkpoint directory + """ + + if not self.thread: + self.queue = Queue(5) + self.thread = Thread(target=self.encode, args=(checkpoint,)) + self.thread.start() + + def stop(self): + """ + Stops an encoding processing thread. Return processed results. + + Returns: + results + """ + + results = None + if self.thread: + # Send EOS message + self.queue.put(Sparse.COMPLETE) + + self.thread.join() + self.thread, self.queue = None, None + + # Get return value + results = self.data + self.data = None + + return results + + def encode(self, checkpoint): + """ + Encodes streaming data. + + Args: + checkpoint: checkpoint directory + """ + + # Streaming encoding of data + _, dimensions, self.data = self.model.vectors(self.stream(), self.batch, checkpoint) + + # Save number of dimensions + self.config["dimensions"] = dimensions + + def stream(self): + """ + Streams data from an input queue until end of stream message received. + """ + + batch = self.queue.get() + while batch != Sparse.COMPLETE: + yield from batch + batch = self.queue.get() + + def normalize(self, queries, scores): + """ + Normalize query results. + + When Bayesian normalization is configured, applies sigmoid calibration + with per-query adaptive parameters (beta=median, alpha=1/std) to produce + calibrated probabilities in [0, 1]. Otherwise, applies linear normalization + using the max query score. + + Args: + queries: query vectors + scores: query results + + Returns: + normalized query results + """ + + # Bayesian sigmoid calibration + if self.normalizer: + return [self.normalizer.bayes(result) if result else [] for result in scores] + + # Default linear normalization + scale = 30.0 if isinstance(self.isnormalize, bool) else self.isnormalize + + # Normalize scores using max scores + maxscores = self.model.dot(queries, queries) + + # Normalize results and return + results = [] + for x, result in enumerate(scores): + maxscore = max(maxscores[x][x] / scale, scale) + maxscore = max(maxscore, result[0][1]) if result else maxscore + + results.append([(uid, score / maxscore) for uid, score in result]) + + return results diff --git a/src/python/txtai/scoring/terms.py b/src/python/txtai/scoring/terms.py new file mode 100644 index 0000000..4d06c63 --- /dev/null +++ b/src/python/txtai/scoring/terms.py @@ -0,0 +1,557 @@ +""" +Terms module +""" + +import functools +import logging +import os +import sqlite3 +import sys + +from array import array +from collections import Counter +from threading import RLock + +# Core library imports +from ..util import Library + +np = Library().numpy() + +# Logging configuration +logger = logging.getLogger(__name__) + + +class Terms: + """ + Builds, searches and stores memory efficient term frequency sparse arrays for a scoring instance. + """ + + # Term frequency sparse arrays + CREATE_TERMS = """ + CREATE TABLE IF NOT EXISTS terms ( + term TEXT PRIMARY KEY, + ids BLOB, + freqs BLOB + ) + """ + + INSERT_TERM = "INSERT OR REPLACE INTO terms VALUES (?, ?, ?)" + SELECT_TERMS = "SELECT ids, freqs FROM terms WHERE term = ?" + WILDCARD_TERMS = "SELECT term FROM terms WHERE term LIKE ?" + + # Documents table + CREATE_DOCUMENTS = """ + CREATE TABLE IF NOT EXISTS documents ( + indexid INTEGER PRIMARY KEY, + id TEXT, + deleted INTEGER, + length INTEGER + ) + """ + + DELETE_DOCUMENTS = "DELETE FROM documents" + INSERT_DOCUMENT = "INSERT OR REPLACE INTO documents VALUES (?, ?, ?, ?)" + SELECT_DOCUMENTS = "SELECT indexid, id, deleted, length FROM documents ORDER BY indexid" + + # Operators + ASTERISK = "__asterisk__" + + def __init__(self, config, score, idf): + """ + Creates a new terms index. + + Args: + config: configuration + score: score function + idf: idf weights + """ + + # Terms index configuration + self.config = config if isinstance(config, dict) else {} + self.cachelimit = self.config.get("cachelimit", 250000000) + self.cutoff = self.config.get("cutoff", 0.1) + + # Scoring function + self.score, self.idf = score, idf + + # Document attributes + self.ids, self.deletes, self.lengths = [], [], array("q") + + # Terms cache + self.terms, self.cachesize = {}, 0 + + # Terms database + self.connection, self.cursor, self.path = None, None, None + + # Database thread lock + self.lock = RLock() + + def insert(self, uid, terms): + """ + Insert term into index. + + Args: + uid: document id + terms: document terms + """ + + # Initialize database, if necessary + self.initialize() + + # Get next internal index id + indexid = len(self.ids) + + # Calculate term frequency and document length + freqs, length = Counter(terms), len(terms) + + # Add document terms + for term, count in freqs.items(): + # Add term entry + self.add(indexid, term, count) + + # Each term and freq is a 8-bit signed long long + self.cachesize += 16 + + # Flush cached terms to the database + if self.cachesize >= self.cachelimit: + self.index() + + # Save id and length + self.ids.append(uid) + self.lengths.append(length) + + def delete(self, ids): + """ + Mark ids as deleted. This prevents deleted results from showing up in search results. + The data is not removed from the underlying term frequency sparse arrays. + + Args: + ids: ids to delete + """ + + # Set index ids as deleted + self.deletes.extend([self.ids.index(i) for i in ids]) + + def index(self): + """ + Saves any remaining cached terms to the database. + """ + + for term, (nuids, nfreqs) in self.terms.items(): + # Retrieve existing uids/freqs + uids, freqs = self.lookup(term) + + if uids: + uids.extend(nuids) + freqs.extend(nfreqs) + else: + uids, freqs = nuids, nfreqs + + # Always save as little endian + if sys.byteorder == "big": + uids.byteswap() + freqs.byteswap() + + # Insert or replace term + self.cursor.execute(Terms.INSERT_TERM, [term, uids.tobytes(), freqs.tobytes()]) + + # Clear cached weights + self.weights.cache_clear() + + # Reset term cache size + self.terms, self.cachesize = {}, 0 + + def search(self, terms, limit): + """ + Searches term index a term-at-a-time. Each term frequency sparse array is retrieved + and used to calculate term match scores. + + This method calculates term scores in two steps as shown below. + + 1. Query and score less common term scores first + 2. Merge in common term scores for all documents matching the first query + + This is similar to the common terms query in Apache Lucene. + + Args: + terms: query terms + limit: maximum results + + Returns: + list of (id, score) + """ + + # Initialize scores array + scores = np.zeros(len(self.ids), dtype=np.float32) + + # Apply term expansion + with self.lock: + terms = self.expand(terms) + + # Debug logging for terms + logger.debug(" ".join(["%s"] * len(terms)), *terms) + + # Score less common terms + terms, skipped, hasscores = Counter(terms), {}, False + for term, freq in terms.items(): + # Compute or lookup term weights + uids, weights = self.weights(term) + if uids is not None: + # Term considered common if it appears in more than 10% of index + if len(uids) <= self.cutoff * len(self.ids): + # Add scores + scores[uids] += freq * weights + + # Set flag that scores have been calculated for at least one term + hasscores = True + else: + skipped[term] = freq + + # Merge in common term scores and return top n matches + return self.topn(scores, limit, hasscores, skipped) + + def escape(self, query): + """ + Escapes query operators. + + Args: + query: query + + Returns: + query with query operators escaped + """ + + return query.replace("*", Terms.ASTERISK) + + def count(self): + """ + Number of elements in the scoring index. + + Returns: + count + """ + + return len(self.ids) - len(self.deletes) + + def load(self, path): + """ + Loads terms database from path. This method loads document attributes into memory. + + Args: + path: path to read terms database + """ + + # Load an existing terms database + self.connection = self.connect(path) + self.cursor = self.connection.cursor() + self.path = path + + # Load document attributes + self.ids, self.deletes, self.lengths = [], [], array("q") + + self.cursor.execute(Terms.SELECT_DOCUMENTS) + for indexid, uid, deleted, length in self.cursor: + # Index id - id + self.ids.append(uid) + + # Deleted flag + if deleted: + self.deletes.append(indexid) + + # Index id - length + self.lengths.append(length) + + # Cast ids to int if every id is an integer + if all(uid.isdigit() for uid in self.ids): + self.ids = [int(uid) for uid in self.ids] + + # Clear cache + self.weights.cache_clear() + + def save(self, path): + """ + Saves terms database to path. This method creates or replaces document attributes into the database. + + Args: + path: path to write terms database + """ + + # Clear documents table + self.cursor.execute(Terms.DELETE_DOCUMENTS) + + # Save document attributes + for i, uid in enumerate(self.ids): + self.cursor.execute(Terms.INSERT_DOCUMENT, [i, uid, 1 if i in self.deletes else 0, self.lengths[i]]) + + # Temporary database + if not self.path: + # Save temporary database + self.connection.commit() + + # Copy data from current to new + connection = self.copy(path) + + # Close temporary database + self.connection.close() + + # Point connection to new connection + self.connection = connection + self.cursor = self.connection.cursor() + self.path = path + + # Paths are equal, commit changes + elif self.path == path: + self.connection.commit() + + # New path is different from current path, copy data and continue using current connection + else: + self.copy(path).close() + + def close(self): + """ + Close and free resources used by this instance. + """ + + # Close connection + if self.connection: + self.connection.close() + + def initialize(self): + """ + Creates connection and initial database schema if no connection exists. + """ + + if not self.connection: + # Create term database + self.connection = self.connect() + self.cursor = self.connection.cursor() + + # Create initial schema + self.cursor.execute(Terms.CREATE_TERMS) + self.cursor.execute(Terms.CREATE_DOCUMENTS) + + def connect(self, path=""): + """ + Creates a new term database connection. + + Args: + path: path to term database file + + Returns: + connection + """ + + connection = sqlite3.connect(path, check_same_thread=False) + + # Enable WAL mode, if necessary + if self.config.get("wal"): + connection.execute("PRAGMA journal_mode=WAL") + + return connection + + def copy(self, path): + """ + Copies content from current terms database into target. + + Args: + path: target database path + + Returns: + new database connection + """ + + # Delete existing file, if necessary + if os.path.exists(path): + os.remove(path) + + # Create new connection + connection = self.connect(path) + + if self.connection.in_transaction: + # The backup call will hang if there are uncommitted changes, need to copy over + # with iterdump (which is much slower) + for sql in self.connection.iterdump(): + connection.execute(sql) + else: + # Database is up to date, can do a more efficient copy with SQLite C API + self.connection.backup(connection) + + return connection + + def add(self, indexid, term, freq): + """ + Adds a term frequency entry. + + Args: + indexid: internal index id + term: term + freq: term frequency + """ + + # Get or create uids and freqs arrays + if term not in self.terms: + self.terms[term] = (array("q"), array("q")) + + # Append uids and freqs + ids, freqs = self.terms[term] + ids.append(indexid) + freqs.append(freq) + + def lookup(self, term): + """ + Retrieves a term frequency sparse array. + + Args: + term: term to lookup + + Returns: + term frequency sparse array + """ + + uids, freqs = None, None + + result = self.cursor.execute(Terms.SELECT_TERMS, [term]).fetchone() + if result: + uids, freqs = (array("q"), array("q")) + uids.frombytes(result[0]) + freqs.frombytes(result[1]) + + # Storage format is always little endian + if sys.byteorder == "big": + uids.byteswap() + freqs.byteswap() + + return uids, freqs + + def expand(self, terms): + """ + Expands a list of terms using the following term expansion rules. + + - Asterisks (*) for wildcard expressions + + Args: + terms: list of terms + + Returns: + expanded terms list + """ + + results = [] + for term in terms: + # Wilcard expressions + if Terms.ASTERISK in term: + # Require a prefix or suffix + if term.replace(Terms.ASTERISK, "").strip(): + term = term.replace(Terms.ASTERISK, "%") + result = self.cursor.execute(Terms.WILDCARD_TERMS, [term]) + results.extend([t for t, in result]) + else: + results.append(term) + + return results + + @functools.lru_cache(maxsize=500) + def weights(self, term): + """ + Computes a term weights sparse array for term. This method is wrapped with a least recently used cache, + which will return common term weights from the cache. + + Args: + term: term + + Returns: + term weights sparse array + """ + + lengths = np.frombuffer(self.lengths, dtype=np.int64) + + with self.lock: + uids, freqs = self.lookup(term) + weights = None + + if uids: + uids = np.frombuffer(uids, dtype=np.int64) + weights = self.score(np.frombuffer(freqs, dtype=np.int64), self.idf[term], lengths[uids]).astype(np.float32) + + return uids, weights + + def topn(self, scores, limit, hasscores, skipped): + """ + Get topn scores from an partial scores array. + + Args: + scores: partial scores array with scores for less common terms + limit: maximum results + hasscores: True if partial scores array has any nonzero scores, False otherwise + skipped: terms skipped in initial query + + Returns: + topn scores + """ + + # Calculate topn candidates to consider + # Require at least one positive score, set topn to smaller of limit * 5 or number of scores + topn = min(len(scores), limit * 5) + + # Get topn candidates, allows for score shifting when adding in common term scores + matches = self.candidates(scores, topn) + + # Merge in scores for more common terms + self.merge(scores, matches, hasscores, skipped) + + # Get topn candidates since it was initially skipped above + if not hasscores: + matches = self.candidates(scores, topn) + + # Reorder matches using updated scores + matches = matches[np.argsort(-scores[matches])] + + # Combine ids with scores. Require score > 0. + return [(self.ids[x], float(scores[x])) for x in matches[:limit] if scores[x] > 0] + + def merge(self, scores, matches, hasscores, terms): + """ + Merges common term scores into scores array. + + Args: + scores: partial scores array + matches: current matches, if any + hasscores: True if scores has current matches, False otherwise + terms: common terms + """ + + for term, freq in terms.items(): + # Compute or lookup term weights + uids, weights = self.weights(term) + + # Filter to topn matches when partial scores array has nonzero scores + if hasscores: + # Find indices in match ids for uids + indices = np.searchsorted(uids, matches) + + # Filter matches that don't exist in uids + indices = [x for i, x in enumerate(indices) if x < len(uids) and uids[x] == matches[i]] + + # Filter to matching uids and weights + uids, weights = uids[indices], weights[indices] + + # Update scores + scores[uids] += freq * weights + + def candidates(self, scores, topn): + """ + Gets the topn scored candidates. This method ignores deleted documents. + + Args: + scores: scores array + topn: topn elements + + Returns: + topn scored candidates + """ + + # Clear deletes + scores[self.deletes] = 0 + + # Get topn candidates + return np.argpartition(scores, -topn)[-topn:] diff --git a/src/python/txtai/scoring/tfidf.py b/src/python/txtai/scoring/tfidf.py new file mode 100644 index 0000000..69f518d --- /dev/null +++ b/src/python/txtai/scoring/tfidf.py @@ -0,0 +1,367 @@ +""" +TFIDF module +""" + +import math +import os + +from collections import Counter +from multiprocessing.pool import ThreadPool + +from ..pipeline import Tokenizer +from ..serialize import Serializer + +from .base import Scoring +from .normalize import Normalize +from .terms import Terms + +# Core library imports +from ..util import Library + +np = Library().numpy() + + +class TFIDF(Scoring): + """ + Term frequency-inverse document frequency (TF-IDF) scoring. + """ + + def __init__(self, config=None): + super().__init__(config) + + # Document stats + self.total = 0 + self.tokens = 0 + self.avgdl = 0 + + # Word frequency + self.docfreq = Counter() + self.wordfreq = Counter() + self.avgfreq = 0 + + # IDF index + self.idf = {} + self.avgidf = 0 + + # Tag boosting + self.tags = Counter() + + # Tokenizer, lazily loaded as needed + self.tokenizer = None + + # Term index + self.terms = Terms(self.config["terms"], self.score, self.idf) if self.config.get("terms") else None + + # Document data + self.documents = {} if self.config.get("content") else None + + # Normalize scores + self.normalize = self.config.get("normalize") + self.normalizer = Normalize(self.normalize) if self.normalize else None + self.avgscore = None + + def insert(self, documents, index=None, checkpoint=None): + # Insert documents, calculate word frequency, total tokens and total documents + for uid, document, tags in documents: + # Extract text, if necessary + if isinstance(document, dict): + document = document.get(self.text, document.get(self.object)) + + if document is not None: + # If index is passed, use indexid, otherwise use id + uid = index if index is not None else uid + + # Add entry to index if the data type is accepted + if isinstance(document, (str, list)): + # Store content + if self.documents is not None: + self.documents[uid] = document + + # Convert to tokens, if necessary + tokens = self.tokenize(document) if isinstance(document, str) else document + + # Add tokens for id to term index + if self.terms is not None: + self.terms.insert(uid, tokens) + + # Add tokens and tags to stats + self.addstats(tokens, tags) + + # Increment index + index = index + 1 if index is not None else None + + def delete(self, ids): + # Delete from terms index + if self.terms: + self.terms.delete(ids) + + # Delete content + if self.documents: + for uid in ids: + self.documents.pop(uid) + + def index(self, documents=None): + # Call base method + super().index(documents) + + # Build index if tokens parsed + if self.wordfreq: + # Calculate total token frequency + self.tokens = sum(self.wordfreq.values()) + + # Calculate average frequency per token + self.avgfreq = self.tokens / len(self.wordfreq.values()) + + # Calculate average document length in tokens + self.avgdl = self.tokens / self.total + + # Compute IDF scores + idfs = self.computeidf(np.array(list(self.docfreq.values()))) + for x, word in enumerate(self.docfreq): + self.idf[word] = float(idfs[x]) + + # Average IDF score per token + self.avgidf = float(np.mean(idfs)) + + # Calculate average score across index + self.avgscore = self.score(self.avgfreq, self.avgidf, self.avgdl) + + # Filter for tags that appear in at least 1% of the documents + self.tags = Counter({tag: number for tag, number in self.tags.items() if number >= self.total * 0.005}) + + # Index terms, if available + if self.terms: + self.terms.index() + + def weights(self, tokens): + # Document length + length = len(tokens) + + # Calculate token counts + freq = self.computefreq(tokens) + freq = np.array([freq[token] for token in tokens]) + + # Get idf scores + idf = np.array([self.idf[token] if token in self.idf else self.avgidf for token in tokens]) + + # Calculate score for each token, use as weight + weights = self.score(freq, idf, length).tolist() + + # Boost weights of tag tokens to match the largest weight in the list + if self.tags: + tags = {token: self.tags[token] for token in tokens if token in self.tags} + if tags: + maxWeight = max(weights) + maxTag = max(tags.values()) + + weights = [max(maxWeight * (tags[tokens[x]] / maxTag), weight) if tokens[x] in tags else weight for x, weight in enumerate(weights)] + + return weights + + def search(self, query, limit=3): + # Check if term index available + if self.terms: + # Escape query operators + query = self.terms.escape(query) if isinstance(query, str) else [self.terms.escape(q) for q in query] + + # Parse query into terms + query = self.tokenize(query) if isinstance(query, str) else query + + # Get topn term query matches + scores = self.terms.search(query, limit) + + # Normalize scores, if enabled + if self.normalizer and scores: + scores = self.normalizer(scores, self.avgscore) + + # Add content, if available + return self.results(scores) + + return None + + def batchsearch(self, queries, limit=3, threads=True): + # Calculate number of threads using a thread per 25k records in index + threads = math.ceil(self.count() / 25000) if isinstance(threads, bool) and threads else int(threads) + threads = min(max(threads, 1), os.cpu_count()) + + # This method is able to run as multiple threads due to a number of regex and numpy method calls that drop the GIL. + results = [] + with ThreadPool(threads) as pool: + for result in pool.starmap(self.search, [(x, limit) for x in queries]): + results.append(result) + + return results + + def count(self): + return self.terms.count() if self.terms else self.total + + def load(self, path): + # Load scoring + state = Serializer.load(path) + + # Convert to Counter instances + for key in ["docfreq", "wordfreq", "tags"]: + state[key] = Counter(state[key]) + + # Convert documents to dict + state["documents"] = dict(state["documents"]) if state["documents"] else state["documents"] + + # Set parameters on this object + self.__dict__.update(state) + + # Recreate normalizer + self.normalizer = Normalize(self.normalize) if self.normalize else None + + # Load terms + if self.config.get("terms"): + self.terms = Terms(self.config["terms"], self.score, self.idf) + self.terms.load(path + ".terms") + + def save(self, path): + # Don't serialize following fields + skipfields = ("config", "terms", "tokenizer", "normalizer") + + # Get object state + state = {key: value for key, value in self.__dict__.items() if key not in skipfields} + + # Update documents to tuples + state["documents"] = list(state["documents"].items()) if state["documents"] else state["documents"] + + # Save scoring + Serializer.save(state, path) + + # Save terms + if self.terms: + self.terms.save(path + ".terms") + + def close(self): + if self.terms: + self.terms.close() + + def issparse(self): + return self.terms is not None + + def isnormalized(self): + return self.normalize + + def isbayes(self): + return self.normalizer is not None and self.normalizer.isbayes() + + def computefreq(self, tokens): + """ + Computes token frequency. Used for token weighting. + + Args: + tokens: input tokens + + Returns: + {token: count} + """ + + return Counter(tokens) + + def computeidf(self, freq): + """ + Computes an idf score for word frequency. + + Args: + freq: word frequency + + Returns: + idf score + """ + + return np.log((self.total + 1) / (freq + 1)) + 1 + + # pylint: disable=W0613 + def score(self, freq, idf, length): + """ + Calculates a score for each token. + + Args: + freq: token frequency + idf: token idf score + length: total number of tokens in source document + + Returns: + token score + """ + + return idf * np.sqrt(freq) * (1 / np.sqrt(length)) + + def addstats(self, tokens, tags): + """ + Add tokens and tags to stats. + + Args: + tokens: list of tokens + tags: list of tags + """ + + # Total number of times token appears, count all tokens + self.wordfreq.update(tokens) + + # Total number of documents a token is in, count unique tokens + self.docfreq.update(set(tokens)) + + # Get list of unique tags + if tags: + self.tags.update(tags.split()) + + # Total document count + self.total += 1 + + def tokenize(self, text): + """ + Tokenizes text using default tokenizer. + + Args: + text: input text + + Returns: + tokens + """ + + # Load tokenizer + if not self.tokenizer: + self.tokenizer = self.loadtokenizer() + + return self.tokenizer(text) + + def loadtokenizer(self): + """ + Load default tokenizer. + + Returns: + tokenize method + """ + + # Custom tokenizer settings + if self.config.get("tokenizer"): + return Tokenizer(**self.config.get("tokenizer")) + + # Terms index use a standard tokenizer + if self.config.get("terms"): + return Tokenizer() + + # Standard scoring index without a terms index uses backwards compatible static tokenize method + return Tokenizer.tokenize + + def results(self, scores): + """ + Resolves a list of (id, score) with document content, if available. Otherwise, the original input is returned. + + Args: + scores: list of (id, score) + + Returns: + resolved results + """ + + # Convert to Python values + scores = [(x, float(score)) for x, score in scores] + + if self.documents: + return [{"id": x, "text": self.documents[x], "score": score} for x, score in scores] + + return scores diff --git a/src/python/txtai/serialize/__init__.py b/src/python/txtai/serialize/__init__.py new file mode 100644 index 0000000..72eb3d9 --- /dev/null +++ b/src/python/txtai/serialize/__init__.py @@ -0,0 +1,10 @@ +""" +Serialize imports +""" + +from .base import Serialize +from .errors import SerializeError +from .factory import SerializeFactory +from .messagepack import MessagePack +from .pickle import Pickle +from .serializer import Serializer diff --git a/src/python/txtai/serialize/base.py b/src/python/txtai/serialize/base.py new file mode 100644 index 0000000..76410da --- /dev/null +++ b/src/python/txtai/serialize/base.py @@ -0,0 +1,85 @@ +""" +Serialize module +""" + + +class Serialize: + """ + Base class for Serialize instances. This class serializes data to files, streams and bytes. + """ + + def load(self, path): + """ + Loads data from path. + + Args: + path: input path + + Returns: + deserialized data + """ + + with open(path, "rb") as handle: + return self.loadstream(handle) + + def save(self, data, path): + """ + Saves data to path. + + Args: + data: data to save + path: output path + """ + + with open(path, "wb") as handle: + self.savestream(data, handle) + + def loadstream(self, stream): + """ + Loads data from stream. + + Args: + stream: input stream + + Returns: + deserialized data + """ + + raise NotImplementedError + + def savestream(self, data, stream): + """ + Saves data to stream. + + Args: + data: data to save + stream: output stream + """ + + raise NotImplementedError + + def loadbytes(self, data): + """ + Loads data from bytes. + + Args: + data: input bytes + + Returns: + deserialized data + """ + + raise NotImplementedError + + def savebytes(self, data): + """ + Saves data as bytes. + + Args: + data: data to save + + Returns: + serialized data + """ + + raise NotImplementedError diff --git a/src/python/txtai/serialize/errors.py b/src/python/txtai/serialize/errors.py new file mode 100644 index 0000000..18d2d8c --- /dev/null +++ b/src/python/txtai/serialize/errors.py @@ -0,0 +1,9 @@ +""" +Errors module +""" + + +class SerializeError(Exception): + """ + Raised when data serialization fails + """ diff --git a/src/python/txtai/serialize/factory.py b/src/python/txtai/serialize/factory.py new file mode 100644 index 0000000..2d1bfcc --- /dev/null +++ b/src/python/txtai/serialize/factory.py @@ -0,0 +1,29 @@ +""" +Factory module +""" + +from .messagepack import MessagePack +from .pickle import Pickle + + +class SerializeFactory: + """ + Methods to create data serializers. + """ + + @staticmethod + def create(method=None, **kwargs): + """ + Creates a new Serialize instance. + + Args: + method: serialization method + kwargs: additional keyword arguments to pass to serialize instance + """ + + # Pickle serialization + if method == "pickle": + return Pickle(**kwargs) + + # Default serialization + return MessagePack(**kwargs) diff --git a/src/python/txtai/serialize/messagepack.py b/src/python/txtai/serialize/messagepack.py new file mode 100644 index 0000000..dd95244 --- /dev/null +++ b/src/python/txtai/serialize/messagepack.py @@ -0,0 +1,51 @@ +""" +MessagePack module +""" + +# Conditional import +try: + import msgpack + from msgpack import Unpacker + from msgpack.exceptions import ExtraData + + MSGPACK = True +except ImportError: + MSGPACK = False + +from .base import Serialize +from .errors import SerializeError + + +class MessagePack(Serialize): + """ + MessagePack serialization. + """ + + def __init__(self, streaming=False, **kwargs): + # Parent constructor + super().__init__() + + if not MSGPACK: + raise ImportError("MessagePack is not available - install msgpack to enable") + + # Streaming unpacker + self.streaming = streaming + + # Additional streaming unpacker keyword arguments + self.kwargs = kwargs + + def loadstream(self, stream): + try: + # Support both streaming and non-streaming unpacking of data + return Unpacker(stream, **self.kwargs) if self.streaming else msgpack.unpack(stream) + except ExtraData as e: + raise SerializeError(e) from e + + def savestream(self, data, stream): + msgpack.pack(data, stream) + + def loadbytes(self, data): + return msgpack.unpackb(data) + + def savebytes(self, data): + return msgpack.packb(data) diff --git a/src/python/txtai/serialize/pickle.py b/src/python/txtai/serialize/pickle.py new file mode 100644 index 0000000..1091e59 --- /dev/null +++ b/src/python/txtai/serialize/pickle.py @@ -0,0 +1,98 @@ +""" +Pickle module +""" + +import os +import logging +import pickle +import warnings + +from .base import Serialize + +# Logging configuration +logger = logging.getLogger(__name__) + + +class Pickle(Serialize): + """ + Pickle serialization. + """ + + def __init__(self, allowpickle=False): + """ + Creates a new instance for Pickle serialization. + + This class ensures the allowpickle parameter or the `ALLOW_PICKLE` environment variable is True. All methods will + raise errors if this isn't the case. + + Pickle serialization is OK for local data but it isn't recommended when sharing data externally. + + Args: + allowpickle: default pickle allow mode, only True with methods that generate local temporary data + """ + + # Parent constructor + super().__init__() + + # Default allow pickle mode + self.allowpickle = allowpickle + + # Current pickle protocol + self.version = 4 + + def load(self, path): + # Load pickled data from path, if allowed + return super().load(path) if self.allow(path) else None + + def save(self, data, path): + # Save pickled data to path, if allowed + if self.allow(): + super().save(data, path) + + def loadstream(self, stream): + # Load pickled data from stream, if allowed + return pickle.load(stream) if self.allow() else None + + def savestream(self, data, stream): + # Save pickled data to stream, if allowed + if self.allow(): + pickle.dump(data, stream, protocol=self.version) + + def loadbytes(self, data): + # Load pickled data from bytes, if allowed + return pickle.loads(data) if self.allow() else None + + def savebytes(self, data): + # Save pickled data to stream, if allowed + return pickle.dumps(data, protocol=self.version) if self.allow() else None + + def allow(self, path=None): + """ + Checks if loading and saving pickled data is allowed. Raises an error if it's not allowed. + + Args: + path: optional path to add to generated error messages + """ + + enablepickle = self.allowpickle or os.environ.get("ALLOW_PICKLE", "False") in ("True", "1") + if not enablepickle: + raise ValueError( + ( + "Loading of pickled index data is disabled. " + f"`{path if path else 'stream'}` was not loaded. " + "Set the env variable `ALLOW_PICKLE=True` to enable loading pickled index data. " + "This should only be done for trusted and/or local data." + ) + ) + + if not self.allowpickle: + warnings.warn( + ( + "Loading of pickled data enabled through `ALLOW_PICKLE=True` env variable. " + "This setting should only be used with trusted and/or local data. " + "Saving this index will replace pickled index data formats with the latest index formats and remove this warning." + ), + RuntimeWarning, + ) + + return enablepickle diff --git a/src/python/txtai/serialize/serializer.py b/src/python/txtai/serialize/serializer.py new file mode 100644 index 0000000..37de81d --- /dev/null +++ b/src/python/txtai/serialize/serializer.py @@ -0,0 +1,46 @@ +""" +Serializer module +""" + +from .errors import SerializeError +from .factory import SerializeFactory + + +class Serializer: + """ + Methods to serialize and deserialize data. + """ + + @staticmethod + def load(path): + """ + Loads data from path. This method first tries to load the default serialization format. + If that fails, it will fallback to pickle format for backwards-compatability purposes. + + Note that loading pickle files requires the env variable `ALLOW_PICKLE=True`. + + Args: + path: data to load + + Returns: + data + """ + + try: + return SerializeFactory.create().load(path) + except SerializeError: + # Backwards compatible check for pickled data + return SerializeFactory.create("pickle").load(path) + + @staticmethod + def save(data, path): + """ + Saves data to path. + + Args: + data: data to save + path: output path + """ + + # Save using default serialization method + SerializeFactory.create().save(data, path) diff --git a/src/python/txtai/util/__init__.py b/src/python/txtai/util/__init__.py new file mode 100644 index 0000000..af762ef --- /dev/null +++ b/src/python/txtai/util/__init__.py @@ -0,0 +1,9 @@ +""" +Utility imports +""" + +from .download import * +from .library import Library +from .resolver import Resolver +from .sparsearray import SparseArray +from .template import TemplateFormatter diff --git a/src/python/txtai/util/download.py b/src/python/txtai/util/download.py new file mode 100644 index 0000000..b1f6918 --- /dev/null +++ b/src/python/txtai/util/download.py @@ -0,0 +1,59 @@ +""" +Download module +""" + +import os + +# Core library imports +from .library import Library + +library = Library() +huggingface_hub = library.huggingface_hub() +HFValidationError = library.hferror() + + +class Download: + """ + Downloads files from the Hugging Face Hub. This method also supports local file paths that exist. + """ + + def __call__(self, path, name=None): + """ + Downloads path from the Hugging Face Hub. Supports local file paths. + + Args: + path: model path or repo + name: file name + + Returns: + local cached model path, if available + """ + + # Reject invalid input + if not isinstance(path, str) or (name and not isinstance(name, str)): + return None + + # Split into repo and name components + if not name: + # Split into parts + parts = path.split("/") + + # Calculate repo id split + repo = 2 if len(parts) > 2 else 1 + + # Set path and name components + path, name = "/".join(parts[:repo]), "/".join(parts[repo:]) + + # Download (if necessary) and return local file path + try: + local = os.path.join(path, name) + return local if os.path.exists(local) else huggingface_hub.hf_hub_download(repo_id=path, filename=name) + + except (HFValidationError, OSError) as e: + raise DownloadError(f"Error locating file with parameters: path={path}, name={name}") from e + + +class DownloadError(Exception): + """ + Exception raised when a local or remote file path is not valid. + """ diff --git a/src/python/txtai/util/library.py b/src/python/txtai/util/library.py new file mode 100644 index 0000000..7b22099 --- /dev/null +++ b/src/python/txtai/util/library.py @@ -0,0 +1,319 @@ +""" +Library module +""" + + +# pylint: disable=C0415 +class Library: + """ + Imports core but optional dependencies with fallbacks when the library is not installed. + """ + + def arguments(self): + """ + Imports transformers.TrainingArguments. + """ + + try: + from transformers import TrainingArguments + + except ImportError: + + class TrainingArguments: + """ + Stub for TrainingArguments + """ + + return TrainingArguments + + def config(self): + """ + Imports transformers.configuration_utils.PretrainedConfig. + + Returns: + PreTrainedConfig + """ + + try: + from transformers.configuration_utils import PretrainedConfig + + except ImportError: + + class PretrainedConfig: + """ + Stub for PretrainedConfig + """ + + return PretrainedConfig + + def dataset(self): + """ + Import torch.utils.data.Dataset. + + Returns: + Dataset + """ + + try: + from torch.utils.data import Dataset + + except ImportError: + + class Dataset: + """ + Stub for Dataset + """ + + return Dataset + + def hferror(self): + """ + Import huggingface_hub.errors.HFValidationError. + """ + + try: + from huggingface_hub.errors import HFValidationError + + except ImportError: + + class HFValidationError(Exception): + """ + Stub for HFValidationError + """ + + return HFValidationError + + def huggingface_hub(self): + """ + Imports huggingface_hub. + + Returns: + torch + """ + + try: + import huggingface_hub + + except ImportError: + + class HuggingFaceHub: + """ + Stub for HuggingFaceHub + """ + + def __getattr__(self, name): + raise ImportError("Hugging Face Hub is not installed, install huggingface-hub to use this module") + + huggingface_hub = HuggingFaceHub() + + return huggingface_hub + + def model(self): + """ + Imports transformers.modeling_utils.PreTrainedModel. + + Returns: + PreTrainedModel + """ + + try: + from transformers.modeling_utils import PreTrainedModel + + except ImportError: + + class PreTrainedModel: + """ + Stub for PreTrainedModel + """ + + return PreTrainedModel + + def module(self): + """ + Imports torch.nn.Module. + + Returns: + Module + """ + + try: + import torch.nn + + # pylint: disable=C0103 + Module = torch.nn.Module + + except ImportError: + + class Module: + """ + Stub for Module + """ + + return Module + + def numpy(self): + """ + Imports numpy. + + Returns: + numpy + """ + + try: + import numpy + + except ImportError: + + class NumPy: + """ + Stub for NumPy + """ + + def __getattr__(self, name): + raise ImportError("NumPy is not installed, install numpy to use this module") + + numpy = NumPy() + + return numpy + + def regex(self): + """ + Imports regex. + + Returns: + regex + """ + + try: + import regex + + except ImportError: + + class Regex: + """ + Stub for Regex + """ + + def __getattr__(self, name): + raise ImportError("Regex is not installed, install regex to use this module") + + regex = Regex() + + return regex + + def safetensors(self): + """ + Imports safetensors. + + Returns: + safetensors + """ + + try: + import safetensors + + except ImportError: + + class Safetensors: + """ + Stub for Safetensors + """ + + def __getattr__(self, name): + raise ImportError("Safetensors is not installed, install safetensors to use this module") + + safetensors = Safetensors() + + return safetensors + + def torch(self): + """ + Imports torch. + + Returns: + torch + """ + + try: + import torch + + except ImportError: + + class Torch: + """ + Stub for torch + """ + + def __getattr__(self, name): + raise ImportError("Torch is not installed, install torch to use this module") + + torch = Torch() + + return torch + + def trainer(self): + """ + Import transformers.Trainer + """ + + try: + from transformers import Trainer + + except ImportError: + + class Trainer(Exception): + """ + Stub for Trainfer + """ + + return Trainer + + def transformers(self): + """ + Imports transformers. + + Returns: + transformers + """ + + try: + import transformers + + except ImportError: + + class Transformers: + """ + Stub for transformers + """ + + def __getattr__(self, name): + raise ImportError("Transformers is not installed, install transformers to use this module") + + transformers = Transformers() + + return transformers + + def yaml(self): + """ + Imports yaml. + + Returns: + yaml + """ + + try: + import yaml + + except ImportError: + + class YAML: + """ + Stub for yaml + """ + + def __getattr__(self, name): + raise ImportError("PyYAML is not installed, install pyyaml to use this module") + + yaml = YAML() + + return yaml diff --git a/src/python/txtai/util/resolver.py b/src/python/txtai/util/resolver.py new file mode 100644 index 0000000..b6768b3 --- /dev/null +++ b/src/python/txtai/util/resolver.py @@ -0,0 +1,32 @@ +""" +Resolver module +""" + + +class Resolver: + """ + Resolves a Python class path + """ + + def __call__(self, path): + """ + Class instance to resolve. + + Args: + path: path to class + + Returns: + class instance + """ + + # Split into path components + parts = path.split(".") + + # Resolve each path component + module = ".".join(parts[:-1]) + m = __import__(module) + for comp in parts[1:]: + m = getattr(m, comp) + + # Return class instance + return m diff --git a/src/python/txtai/util/sparsearray.py b/src/python/txtai/util/sparsearray.py new file mode 100644 index 0000000..0d0db45 --- /dev/null +++ b/src/python/txtai/util/sparsearray.py @@ -0,0 +1,65 @@ +""" +SparseArray module +""" + +# Conditional import +try: + from scipy.sparse import csr_matrix + + SCIPY = True +except ImportError: + SCIPY = False + +# Core library imports +from .library import Library + +np = Library().numpy() + + +class SparseArray: + """ + Methods to load and save sparse arrays to file. + """ + + def __init__(self): + """ + Creates a SparseArray instance. + """ + + if not SCIPY: + raise ImportError("SciPy is not available - install scipy to enable") + + def load(self, f): + """ + Loads a sparse array from file. + + Args: + f: input file handle + + Returns: + sparse array + """ + + # Load raw data + data, indices, indptr, shape = ( + np.load(f, allow_pickle=False), + np.load(f, allow_pickle=False), + np.load(f, allow_pickle=False), + np.load(f, allow_pickle=False), + ) + + # Load data into sparse array + return csr_matrix((data, indices, indptr), shape=shape) + + def save(self, f, array): + """ + Saves a sparse array to file. + + Args: + f: output file handle + array: sparse array + """ + + # Save sparse array to file + for x in [array.data, array.indices, array.indptr, array.shape]: + np.save(f, x, allow_pickle=False) diff --git a/src/python/txtai/util/template.py b/src/python/txtai/util/template.py new file mode 100644 index 0000000..374c34c --- /dev/null +++ b/src/python/txtai/util/template.py @@ -0,0 +1,16 @@ +""" +Template module +""" + +from string import Formatter + + +class TemplateFormatter(Formatter): + """ + Custom Formatter that requires each argument to be consumed. + """ + + def check_unused_args(self, used_args, args, kwargs): + difference = set(kwargs).difference(used_args) + if difference: + raise KeyError(difference) diff --git a/src/python/txtai/vectors/__init__.py b/src/python/txtai/vectors/__init__.py new file mode 100644 index 0000000..13c6478 --- /dev/null +++ b/src/python/txtai/vectors/__init__.py @@ -0,0 +1,8 @@ +""" +Vectors imports +""" + +from .base import Vectors +from .dense import * +from .recovery import Recovery +from .sparse import * diff --git a/src/python/txtai/vectors/base.py b/src/python/txtai/vectors/base.py new file mode 100644 index 0000000..0c07c47 --- /dev/null +++ b/src/python/txtai/vectors/base.py @@ -0,0 +1,479 @@ +""" +Vectors module +""" + +import json +import os +import tempfile +import uuid + +from ..pipeline import Tokenizer + +from .recovery import Recovery + +# Core library imports +from ..util import Library + +np = Library().numpy() + + +class Vectors: + """ + Base class for vector models. Vector models transform input content into numeric vectors. + """ + + def __init__(self, config, scoring, models): + """ + Creates a new vectors instance. + + Args: + config: vector configuration + scoring: optional scoring instance for term weighting + models: models cache + """ + + # Store parameters + self.config = config + self.scoring = scoring + self.models = models + + if config: + # Detect if this is an initialized configuration + self.initialized = "dimensions" in config + + # Enables optional string tokenization + self.tokenize = config.get("tokenize") + + # Load model + self.model = self.load(config.get("path")) + + # Encode batch size - controls underlying model batch size when encoding vectors + self.encodebatch = config.get("encodebatch", 32) + + # Embeddings instructions + self.instructions = config.get("instructions") + + # Truncate embeddings to this dimensionality + self.dimensionality = config.get("dimensionality") + + # Scalar quantization - supports 1-bit through 8-bit quantization + quantize = config.get("quantize") + self.qbits = max(min(quantize, 8), 1) if isinstance(quantize, int) and not isinstance(quantize, bool) else None + + def loadmodel(self, path): + """ + Loads vector model at path. + + Args: + path: path to vector model + + Returns: + vector model + """ + + raise NotImplementedError + + def encode(self, data, category=None): + """ + Encodes a batch of data using vector model. + + Args: + data: batch of data + category: optional category for instruction-based embeddings + + Return: + transformed data + """ + + raise NotImplementedError + + def load(self, path): + """ + Loads a model using the current configuration. This method will return previously cached models + if available. + + Returns: + model + """ + + # Check if model is cached + if self.models and path in self.models: + return self.models[path] + + # Create new model + model = self.loadmodel(path) + + # Store model in cache + if self.models is not None and path: + self.models[path] = model + + return model + + def index(self, documents, batchsize=500, checkpoint=None): + """ + Converts a list of documents to a temporary file with embeddings arrays. Returns a tuple of document ids, + number of dimensions and temporary file with embeddings. + + Args: + documents: list of (id, data, tags) + batchsize: index batch size + checkpoint: optional checkpoint directory, enables indexing restart + + Returns: + (ids, dimensions, batches, stream) + """ + + ids, dimensions, batches, stream = [], None, 0, None + + # Generate recovery config if checkpoint is set + vectorsid = self.vectorsid() if checkpoint else None + recovery = Recovery(checkpoint, vectorsid, self.loadembeddings) if checkpoint else None + + # Convert all documents to embedding arrays, stream embeddings to disk to control memory usage + with self.spool(checkpoint, vectorsid) as output: + stream = output.name + batch = [] + for document in documents: + batch.append(document) + + if len(batch) == batchsize: + # Convert batch to embeddings + uids, dimensions = self.batch(batch, output, recovery) + ids.extend(uids) + batches += 1 + + batch = [] + + # Final batch + if batch: + uids, dimensions = self.batch(batch, output, recovery) + ids.extend(uids) + batches += 1 + + return (ids, dimensions, batches, stream) + + def vectors(self, documents, batchsize=500, checkpoint=None, buffer=None, dtype=None): + """ + Bulk encodes documents into vectors using index(). Return the data as a mmap-ed array. + + Args: + documents: list of (id, data, tags) + batchsize: index batch size + checkpoint: optional checkpoint directory, enables indexing restart + buffer: file path used for memmap buffer + dtype: dtype for buffer + + Returns: + (ids, dimensions, embeddings) + """ + + # Consume stream and transform documents to vectors + ids, dimensions, batches, stream = self.index(documents, batchsize, checkpoint) + + # Check that embeddings are available and load as a memmap + embeddings = None + if ids: + # Write batches + embeddings = np.memmap(buffer, dtype=dtype, shape=(len(ids), dimensions), mode="w+") + with open(stream, "rb") as queue: + x = 0 + for _ in range(batches): + batch = self.loadembeddings(queue) + embeddings[x : x + batch.shape[0]] = batch + x += batch.shape[0] + + # Remove temporary file (if checkpointing is disabled) + if not checkpoint: + os.remove(stream) + + return (ids, dimensions, embeddings) + + def close(self): + """ + Closes this vectors instance. + """ + + self.model = None + + def transform(self, document): + """ + Transforms document into an embeddings vector. + + Args: + document: (id, data, tags) + + Returns: + embeddings vector + """ + + # Prepare input document for vectors model and build embeddings + return self.batchtransform([document])[0] + + def batchtransform(self, documents, category=None): + """ + Transforms batch of documents into embeddings vectors. + + Args: + documents: list of documents used to build embeddings + category: category for instruction-based embeddings + + Returns: + embeddings vectors + """ + + # Prepare input documents for vectors model + documents = [self.prepare(data, category) for _, data, _ in documents] + + # Skip encoding data if it's already an array + if documents and isinstance(documents[0], np.ndarray): + return np.array(documents, dtype=np.float32) + + return self.vectorize(documents, category) + + def dot(self, queries, data): + """ + Calculates the dot product similarity between queries and documents. This method + assumes each of the inputs are normalized. + + Args: + queries: queries + data: search data + + Returns: + dot product scores + """ + + return np.dot(queries, data.T).tolist() + + def vectorsid(self): + """ + Generates vectors uid for this vectors instance. + + Returns: + vectors uid + """ + + # Select config options that determine uniqueness + select = ["path", "method", "tokenizer", "maxlength", "tokenize", "instructions", "dimensionality", "quantize"] + config = {k: v for k, v in self.config.items() if k in select} + config.update(self.config.get("vectors", {})) + + # Generate a deterministic UUID + return str(uuid.uuid5(uuid.NAMESPACE_DNS, json.dumps(config, sort_keys=True))) + + def spool(self, checkpoint, vectorsid): + """ + Opens a spool file for queuing generated vectors. + + Args: + checkpoint: optional checkpoint directory, enables indexing restart + vectorsid: vectors uid for current configuration + + Returns: + vectors spool file + """ + + # Spool to vectors checkpoint file + if checkpoint: + os.makedirs(checkpoint, exist_ok=True) + return open(f"{checkpoint}/{vectorsid}", "wb") + + # Spool to temporary file + return tempfile.NamedTemporaryFile(mode="wb", suffix=".npy", delete=False) + + def batch(self, documents, output, recovery): + """ + Builds a batch of embeddings. + + Args: + documents: list of documents used to build embeddings + output: output temp file to store embeddings + recovery: optional recovery instance + + Returns: + (ids, dimensions) list of ids and number of dimensions in embeddings + """ + + # Extract ids and prepare input documents for vectors model + ids = [uid for uid, _, _ in documents] + documents = [self.prepare(data, "data") for _, data, _ in documents] + dimensions = None + + # Attempt to read embeddings from a recovery file + embeddings = recovery() if recovery else None + embeddings = self.vectorize(documents, "data") if embeddings is None else embeddings + if embeddings is not None: + dimensions = embeddings.shape[1] + self.saveembeddings(output, embeddings) + + return (ids, dimensions) + + def prepare(self, data, category=None): + """ + Prepares input data for vector model. + + Args: + data: input data + category: category for instruction-based embeddings + + Returns: + data formatted for vector model + """ + + # Prepares tokens for the model + data = self.tokens(data) + + # Default instruction category + category = category if category else "query" + + # Prepend instructions, if applicable + if self.instructions and category in self.instructions and isinstance(data, str): + # Prepend category instruction + data = f"{self.instructions[category]}{data}" + + return data + + def tokens(self, data): + """ + Prepare data as tokens model can accept. + + Args: + data: input data + + Returns: + tokens formatted for model + """ + + # Optional string tokenization + if self.tokenize and isinstance(data, str): + data = Tokenizer.tokenize(data) + + # Convert token list to string + if isinstance(data, list): + data = " ".join(data) + + return data + + def vectorize(self, data, category=None): + """ + Runs data vectorization, which consists of the following steps. + + 1. Encode data into vectors using underlying model + 2. Truncate vectors, if necessary + 3. Normalize vectors + 4. Quantize vectors, if necessary + + Args: + data: input data + category: category for instruction-based embeddings + + Returns: + embeddings vectors + """ + + # Default instruction category + category = category if category else "query" + + # Transform data into vectors + embeddings = self.encode(data, category) + + if embeddings is not None: + # Truncate embeddings, if necessary + if self.dimensionality and self.dimensionality < embeddings.shape[1]: + embeddings = self.truncate(embeddings) + + # Normalize data + embeddings = self.normalize(embeddings) + + # Apply quantization, if necessary + if self.qbits: + embeddings = self.quantize(embeddings) + + return embeddings + + def loadembeddings(self, f): + """ + Loads embeddings from file. + + Args: + f: file to load from + + Returns: + embeddings + """ + + return np.load(f, allow_pickle=False) + + def saveembeddings(self, f, embeddings): + """ + Saves embeddings to output. + + Args: + f: output file + embeddings: embeddings to save + """ + + np.save(f, embeddings, allow_pickle=False) + + def truncate(self, embeddings): + """ + Truncates embeddings to the configured dimensionality. + + This is only useful for models trained to store more important information in + earlier dimensions such as Matryoshka Representation Learning (MRL). + + Args: + embeddings: input embeddings + + Returns: + truncated embeddings + """ + + return embeddings[:, : self.dimensionality] + + def normalize(self, embeddings): + """ + Normalizes embeddings using L2 normalization. Operation applied directly on array. + + Args: + embeddings: input embeddings + + Returns: + embeddings + """ + + # Calculation is different for matrices vs vectors + if len(embeddings.shape) > 1: + embeddings /= np.linalg.norm(embeddings, axis=1)[:, np.newaxis] + else: + embeddings /= np.linalg.norm(embeddings) + + return embeddings + + def quantize(self, embeddings): + """ + Quantizes embeddings using scalar quantization. + + Args: + embeddings: input embeddings + + Returns: + quantized embeddings + """ + + # Scale factor is midpoint in range + factor = 2 ** (self.qbits - 1) + + # Quantize to uint8 + scalars = embeddings * factor + scalars = scalars.clip(-factor, factor - 1) + factor + scalars = scalars.astype(np.uint8) + + # Transform uint8 to bits + bits = np.unpackbits(scalars.reshape(-1, 1), axis=1) + + # Remove unused bits (i.e. for 3-bit quantization, the leading 5 bits are removed) + bits = bits[:, -self.qbits :] + + # Reshape using original data dimensions and pack bits into uint8 array + return np.packbits(bits.reshape(embeddings.shape[0], embeddings.shape[1] * self.qbits), axis=1) diff --git a/src/python/txtai/vectors/dense/__init__.py b/src/python/txtai/vectors/dense/__init__.py new file mode 100644 index 0000000..c783143 --- /dev/null +++ b/src/python/txtai/vectors/dense/__init__.py @@ -0,0 +1,12 @@ +""" +Dense vectors imports +""" + +from .external import External +from .factory import VectorsFactory +from .huggingface import HFVectors +from .litellm import LiteLLM +from .llama import LlamaCpp +from .m2v import Model2Vec +from .sbert import STVectors +from .words import WordVectors diff --git a/src/python/txtai/vectors/dense/external.py b/src/python/txtai/vectors/dense/external.py new file mode 100644 index 0000000..47c76bc --- /dev/null +++ b/src/python/txtai/vectors/dense/external.py @@ -0,0 +1,56 @@ +""" +External module +""" + +import types + +from ...util import Library, Resolver + +from ..base import Vectors + +# Core library imports +np = Library().numpy() + + +class External(Vectors): + """ + Builds vectors using an external method. This can be a local function or an external API call. + """ + + def __init__(self, config, scoring, models): + super().__init__(config, scoring, models) + + # Lookup and resolve transform function + self.transform = self.resolve(config.get("transform")) + + def loadmodel(self, path): + return None + + def encode(self, data, category=None): + # Call external transform function, if available and data not already an array + # Batching is handed by the external transform function + if self.transform and data and not isinstance(data[0], np.ndarray): + data = self.transform(data) + + # Cast to float32 + return data.astype(np.float32) if isinstance(data, np.ndarray) else np.array(data, dtype=np.float32) + + def resolve(self, transform): + """ + Resolves a transform function. + + Args: + transform: transform function + + Returns: + resolved transform function + """ + + if transform: + # Resolve transform instance, if necessary + transform = Resolver()(transform) if transform and isinstance(transform, str) else transform + + # Get function or callable instance + transform = transform if isinstance(transform, types.FunctionType) else transform() + + return transform diff --git a/src/python/txtai/vectors/dense/factory.py b/src/python/txtai/vectors/dense/factory.py new file mode 100644 index 0000000..859e6cf --- /dev/null +++ b/src/python/txtai/vectors/dense/factory.py @@ -0,0 +1,128 @@ +""" +Factory module +""" + +from ...util import Resolver + +from .external import External +from .huggingface import HFVectors +from .litellm import LiteLLM +from .litert import LiteRT +from .llama import LlamaCpp +from .m2v import Model2Vec +from .sbert import STVectors +from .words import WordVectors + + +class VectorsFactory: + """ + Methods to create dense vector models. + """ + + @staticmethod + def create(config, scoring=None, models=None): + """ + Create a Vectors model instance. + + Args: + config: vector configuration + scoring: scoring instance + models: models cache + + Returns: + Vectors + """ + + # Determine vector method + method = VectorsFactory.method(config) + + # External vectors + if method == "external": + return External(config, scoring, models) + + # LiteLLM vectors + if method == "litellm": + return LiteLLM(config, scoring, models) + + # LiteRT vectors + if method == "litert": + return LiteRT(config, scoring, models) + + # llama.cpp vectors + if method == "llama.cpp": + return LlamaCpp(config, scoring, models) + + # Model2vec vectors + if method == "model2vec": + return Model2Vec(config, scoring, models) + + # Sentence Transformers vectors + if method == "sentence-transformers": + return STVectors(config, scoring, models) if config and config.get("path") else None + + # Word vectors + if method == "words": + return WordVectors(config, scoring, models) + + # Transformers vectors + if HFVectors.ismethod(method): + return HFVectors(config, scoring, models) if config and config.get("path") else None + + # Resolve custom method + return VectorsFactory.resolve(method, config, scoring, models) if method else None + + @staticmethod + def method(config): + """ + Get or derive the vector method. + + Args: + config: vector configuration + + Returns: + vector method + """ + + # Determine vector method + method = config.get("method") + path = config.get("path") + + # Infer method from path, if blank + if not method: + if path: + if LiteLLM.ismodel(path): + method = "litellm" + elif LiteRT.ismodel(path): + method = "litert" + elif LlamaCpp.ismodel(path): + method = "llama.cpp" + elif Model2Vec.ismodel(path): + method = "model2vec" + elif WordVectors.ismodel(path): + method = "words" + else: + method = "transformers" + elif config.get("transform"): + method = "external" + + return method + + @staticmethod + def resolve(backend, config, scoring, models): + """ + Attempt to resolve a custom backend. + + Args: + backend: backend class + config: vector configuration + scoring: scoring instance + models: models cache + + Returns: + Vectors + """ + + try: + return Resolver()(backend)(config, scoring, models) + except Exception as e: + raise ImportError(f"Unable to resolve vectors backend: '{backend}'") from e diff --git a/src/python/txtai/vectors/dense/huggingface.py b/src/python/txtai/vectors/dense/huggingface.py new file mode 100644 index 0000000..cd2fc24 --- /dev/null +++ b/src/python/txtai/vectors/dense/huggingface.py @@ -0,0 +1,45 @@ +""" +Hugging Face module +""" + +from ...models import Models, PoolingFactory + +from ..base import Vectors + + +class HFVectors(Vectors): + """ + Builds vectors using the Hugging Face transformers library. + """ + + @staticmethod + def ismethod(method): + """ + Checks if this method uses local transformers-based models. + + Args: + method: input method + + Returns: + True if this is a local transformers-based model, False otherwise + """ + + return method in ("transformers", "pooling", "clspooling", "meanpooling") + + def loadmodel(self, path): + # Build embeddings with transformers pooling + return PoolingFactory.create( + { + "method": self.config.get("method"), + "path": path, + "device": Models.deviceid(self.config.get("gpu", True)), + "tokenizer": self.config.get("tokenizer"), + "maxlength": self.config.get("maxlength"), + "loadprompts": "instructions" not in self.config, + "modelargs": self.config.get("vectors", {}), + } + ) + + def encode(self, data, category=None): + # Encode data using vectors model + return self.model.encode(data, batch=self.encodebatch, category=category) diff --git a/src/python/txtai/vectors/dense/litellm.py b/src/python/txtai/vectors/dense/litellm.py new file mode 100644 index 0000000..3833644 --- /dev/null +++ b/src/python/txtai/vectors/dense/litellm.py @@ -0,0 +1,87 @@ +""" +LiteLLM module +""" + +# Conditional import +try: + import litellm as api + + LITELLM = True +except ImportError: + LITELLM = False + +from ...util import Download, Library + +from ..base import Vectors + +# Core library imports +np = Library().numpy() + + +class LiteLLM(Vectors): + """ + Builds vectors using an external embeddings API via LiteLLM. + """ + + @staticmethod + def ismodel(path): + """ + Checks if path is a LiteLLM model. + + Args: + path: input path + + Returns: + True if this is a LiteLLM model, False otherwise + """ + + # pylint: disable=W0702 + if isinstance(path, str) and LITELLM: + debug = api.suppress_debug_info + try: + # Suppress debug messages for this test + api.suppress_debug_info = True + return api.get_llm_provider(path) and not LiteLLM.ishub(path) + except: + return False + finally: + # Restore debug info value to original value + api.suppress_debug_info = debug + + return False + + @staticmethod + def ishub(path): + """ + Checks if path is available on the HF Hub. + + Args: + input path + + Returns: + True if this is a model on the HF Hub + """ + + # pylint: disable=W0702 + try: + return Download()(path, "config.json") is not None if "/" in path else False + except: + return False + + def __init__(self, config, scoring, models): + # Check before parent constructor since it calls loadmodel + if not LITELLM: + raise ImportError('LiteLLM is not available - install "vectors" extra to enable') + + super().__init__(config, scoring, models) + + def loadmodel(self, path): + return None + + def encode(self, data, category=None): + # Call external embeddings API using LiteLLM + # Batching is handled server-side + response = api.embedding(model=self.config.get("path"), input=data, **self.config.get("vectors", {})) + + # Read response into a NumPy array + return np.array([x["embedding"] for x in response.data], dtype=np.float32) diff --git a/src/python/txtai/vectors/dense/litert.py b/src/python/txtai/vectors/dense/litert.py new file mode 100644 index 0000000..26c06e2 --- /dev/null +++ b/src/python/txtai/vectors/dense/litert.py @@ -0,0 +1,154 @@ +""" +LiteRT module +""" + +import os + +# Conditional import +try: + from ai_edge_litert.compiled_model import CompiledModel, HardwareAccelerator + from tokenizers import Tokenizer + + LITERT = True +except ImportError: + LITERT = False + +from ...util import Download, Library + +from ..base import Vectors + +# Core library imports +np = Library().numpy() + + +class LiteRT(Vectors): + """ + Builds vectors using LiteRT. + """ + + @staticmethod + def ismodel(path): + """ + Checks if path is a LiteRT model. + + Args: + path: input path + + Returns: + True if this is a LiteRT model, False otherwise + """ + + return isinstance(path, str) and path.lower().endswith(".tflite") + + def __init__(self, config, scoring, models): + # Check before parent constructor since it calls loadmodel + if not LITERT: + raise ImportError('LiteRT is not available - install "vectors" extra to enable') + + super().__init__(config, scoring, models) + + def loadmodel(self, path): + # Check if this is a local path, otherwise download from the HF Hub + model = path if os.path.exists(path) else Download()(path) + + # Also local tokenizer file + tokenizer = os.path.dirname(path) + "/" + "tokenizer.json" + tokenizer = tokenizer if os.path.exists(tokenizer) else Download()(tokenizer) + + # Load tokenizer and model + tokenizer = Tokenizer.from_file(tokenizer) + model = CompiledModel.from_file( + model, + HardwareAccelerator.GPU | HardwareAccelerator.NPU | HardwareAccelerator.CPU if self.config.get("gpu", True) else HardwareAccelerator.CPU, + ) + + # Calculate model batch size and max length + buffers = model.create_input_buffers(0) + batchsize, maxlength = buffers[0].get_tensor_details()["shape"] + + # Set maxlength on tokenizer + tokenizer.enable_padding(length=maxlength) + tokenizer.enable_truncation(max_length=maxlength) + + return (model, tokenizer, batchsize) + + def encode(self, data, category=None): + # Tokenize input + ids, masks, types = self.tokenizer(data) + + # Generate embeddings + return self.embed(ids, masks, types) + + def tokenizer(self, data): + """ + Tokenizes data. + + Args: + data: batch of data + + Returns: + tokenized data + """ + + # Unpack tokenizer + _, tokenizer, _ = self.model + + encoding = tokenizer.encode_batch(data) + return ( + np.array([e.ids for e in encoding], dtype=np.int32), + np.array([e.attention_mask for e in encoding], dtype=np.int32), + np.array([e.type_ids for e in encoding], dtype=np.int32), + ) + + def embed(self, ids, masks, types): + """ + Generates embeddings from tokenized input. + + Args: + ids: token ids + masks: attention mask + types: token type ids + + Returns: + embeddings + """ + + # Unpack model + model, _, batchsize = self.model + + results = [] + + # Create tensor buffers + inputs = model.create_input_buffers(0) + outputs = model.create_output_buffers(0) + + # Create batch memory + bids = np.zeros((batchsize, ids.shape[1]), dtype=np.int32) + bmasks = np.zeros((batchsize, masks.shape[1]), dtype=np.int32) + btypes = np.zeros((batchsize, types.shape[1]), dtype=np.int32) + + for start in range(0, len(ids), batchsize): + # Batch parameters + end = start + batchsize + chunk = slice(start, end) + actual = len(ids[chunk]) + + # Copy batch to numpy buffer + bids[:actual], bmasks[:actual], btypes[:actual] = ids[chunk], masks[chunk], types[chunk] + + # Copy to tensor buffer + for x, data in enumerate([bids, bmasks, btypes]): + inputs[x].write(data) + + # Run model + model.run_by_index(0, inputs, outputs) + + # Get details of output buffer + details = outputs[0].get_tensor_details() + count = int(np.prod(details["shape"])) + + # Append result + result = outputs[0].read(count, details["dtype"]) + results.append(result.reshape(details["shape"])[:actual]) + + return np.vstack(results) diff --git a/src/python/txtai/vectors/dense/llama.py b/src/python/txtai/vectors/dense/llama.py new file mode 100644 index 0000000..7b344e3 --- /dev/null +++ b/src/python/txtai/vectors/dense/llama.py @@ -0,0 +1,74 @@ +""" +Llama module +""" + +import os + +# Conditional import +try: + import llama_cpp as llama + + LLAMA_CPP = True +except ImportError: + LLAMA_CPP = False + +from ...util import Download, Library + +from ..base import Vectors + +# Core library imports +np = Library().numpy() + + +class LlamaCpp(Vectors): + """ + Builds vectors using llama.cpp. + """ + + @staticmethod + def ismodel(path): + """ + Checks if path is a llama.cpp model. + + Args: + path: input path + + Returns: + True if this is a llama.cpp model, False otherwise + """ + + return isinstance(path, str) and path.lower().endswith(".gguf") + + def __init__(self, config, scoring, models): + # Check before parent constructor since it calls loadmodel + if not LLAMA_CPP: + raise ImportError('llama.cpp is not available - install "vectors" extra to enable') + + super().__init__(config, scoring, models) + + def loadmodel(self, path): + # Check if this is a local path, otherwise download from the HF Hub + path = path if os.path.exists(path) else Download()(path) + + # Additional model arguments + modelargs = self.config.get("vectors", {}) + + # Default n_ctx to maxlength if available. Otherwise default n_ctx=0, which sets n_ctx=n_ctx_train. + modelargs["n_ctx"] = modelargs.get("n_ctx", self.config.get("maxlength", 0)) + + # Default n_batch to encode batch + modelargs["n_batch"] = modelargs.get("n_batch", self.config.get("encodebatch", 64)) + + # Default GPU layers if not already set + modelargs["n_gpu_layers"] = modelargs.get("n_gpu_layers", -1 if self.config.get("gpu", os.environ.get("LLAMA_NO_METAL") != "1") else 0) + + # Default verbose flag + modelargs["verbose"] = modelargs.get("verbose", False) + + # Create llama.cpp instance + return llama.Llama(model_path=path, embedding=True, **modelargs) + + def encode(self, data, category=None): + # Generate embeddings and return as a NumPy array + # llama-cpp-python has it's own batching built-in using n_batch parameter + return np.array(self.model.embed(data), dtype=np.float32) diff --git a/src/python/txtai/vectors/dense/m2v.py b/src/python/txtai/vectors/dense/m2v.py new file mode 100644 index 0000000..20bfaf2 --- /dev/null +++ b/src/python/txtai/vectors/dense/m2v.py @@ -0,0 +1,65 @@ +""" +Model2Vec module +""" + +import json + +# Conditional import +try: + from model2vec import StaticModel + + MODEL2VEC = True +except ImportError: + MODEL2VEC = False + +from ...util import Download, DownloadError +from ..base import Vectors + + +class Model2Vec(Vectors): + """ + Builds vectors using Model2Vec. + """ + + @staticmethod + def ismodel(path): + """ + Checks if path is a Model2Vec model. + + Args: + path: input path + + Returns: + True if this is a Model2Vec model, False otherwise + """ + + try: + # Download file and parse JSON + path = Download()(path, "config.json") + if path: + with open(path, encoding="utf-8") as f: + config = json.load(f) + return config.get("model_type") == "model2vec" + + # Ignore this error - invalid repo or directory + except DownloadError: + pass + + return False + + def __init__(self, config, scoring, models): + # Check before parent constructor since it calls loadmodel + if not MODEL2VEC: + raise ImportError('Model2Vec is not available - install "vectors" extra to enable') + + super().__init__(config, scoring, models) + + def loadmodel(self, path): + return StaticModel.from_pretrained(path) + + def encode(self, data, category=None): + # Additional model arguments + modelargs = self.config.get("vectors", {}) + + # Encode data + return self.model.encode(data, batch_size=self.encodebatch, **modelargs) diff --git a/src/python/txtai/vectors/dense/sbert.py b/src/python/txtai/vectors/dense/sbert.py new file mode 100644 index 0000000..93906c8 --- /dev/null +++ b/src/python/txtai/vectors/dense/sbert.py @@ -0,0 +1,92 @@ +""" +Sentence Transformers module +""" + +# Conditional import +try: + from sentence_transformers import SentenceTransformer + + SENTENCE_TRANSFORMERS = True +except ImportError: + SENTENCE_TRANSFORMERS = False + +from ...models import Models + +from ..base import Vectors + + +class STVectors(Vectors): + """ + Builds vectors using sentence-transformers (aka SBERT). + """ + + def __init__(self, config, scoring, models): + # Check before parent constructor since it calls loadmodel + if not SENTENCE_TRANSFORMERS: + raise ImportError('sentence-transformers is not available - install "vectors" extra to enable') + + # Pool parameter created here since loadmodel is called from parent constructor + self.pool = None + + super().__init__(config, scoring, models) + + def loadmodel(self, path): + # Get target device + gpu, pool = self.config.get("gpu", True), False + + # Default mode uses a single GPU. Setting to all spawns a process per GPU. + if isinstance(gpu, str) and gpu == "all": + # Get number of accelerator devices available + devices = Models.acceleratorcount() + + # Enable multiprocessing pooling only when multiple devices are available + gpu, pool = devices <= 1, devices > 1 + + # Tensor device id + deviceid = Models.deviceid(gpu) + + # Additional model arguments + modelargs = self.config.get("vectors", {}) + + # Load sentence-transformers encoder + model = self.loadencoder(path, device=Models.device(deviceid), **modelargs) + + # Start process pool for multiple GPUs + if pool: + self.pool = model.start_multi_process_pool() + + # Return model + return model + + def encode(self, data, category=None): + # Get encode method based on input category + encode = self.model.encode_query if category == "query" else self.model.encode_document if category == "data" else self.model.encode + + # Additional encoding arguments + encodeargs = self.config.get("encodeargs", {}) + + # Encode with sentence transformers encoder + return encode(data, pool=self.pool, batch_size=self.encodebatch, **encodeargs) + + def close(self): + # Close pool before model is closed in parent method + if self.pool: + self.model.stop_multi_process_pool(self.pool) + self.pool = None + + super().close() + + def loadencoder(self, path, device, **kwargs): + """ + Loads the embeddings encoder model from path. + + Args: + path: model path + device: tensor device + kwargs: additional keyword args + + Returns: + embeddings encoder + """ + + return SentenceTransformer(path, device=device, **kwargs) diff --git a/src/python/txtai/vectors/dense/words.py b/src/python/txtai/vectors/dense/words.py new file mode 100644 index 0000000..dc21e32 --- /dev/null +++ b/src/python/txtai/vectors/dense/words.py @@ -0,0 +1,210 @@ +""" +Word Vectors module +""" + +import json +import logging +import os +import tempfile + +from multiprocessing import Pool + +# Conditional import +try: + from staticvectors import Database, StaticVectors + + STATICVECTORS = True +except ImportError: + STATICVECTORS = False + +from ...pipeline import Tokenizer +from ...util import Download, DownloadError, Library + +from ..base import Vectors + +# Core library imports +np = Library().numpy() + +# Logging configuration +logger = logging.getLogger(__name__) + +# Multiprocessing helper methods +# pylint: disable=W0603 +PARAMETERS, VECTORS = None, None + + +def create(config, scoring): + """ + Multiprocessing helper method. Creates a global embeddings object to be accessed in a new subprocess. + + Args: + config: vector configuration + scoring: scoring instance + """ + + global PARAMETERS + global VECTORS + + # Store model parameters for lazy loading + PARAMETERS, VECTORS = (config, scoring, None), None + + +def transform(document): + """ + Multiprocessing helper method. Transforms document into an embeddings vector. + + Args: + document: (id, data, tags) + + Returns: + (id, embedding) + """ + + # Lazy load vectors model + global VECTORS + if not VECTORS: + VECTORS = WordVectors(*PARAMETERS) + + return (document[0], VECTORS.transform(document)) + + +class WordVectors(Vectors): + """ + Builds vectors using weighted word embeddings. + """ + + @staticmethod + def ismodel(path): + """ + Checks if path is a WordVectors model. + + Args: + path: input path + + Returns: + True if this is a WordVectors model, False otherwise + """ + + # Check if this is a SQLite database + if WordVectors.isdatabase(path): + return True + + try: + # Download file and parse JSON + path = Download()(path, "config.json") + if path: + with open(path, encoding="utf-8") as f: + config = json.load(f) + return config.get("model_type") == "staticvectors" + + # Ignore this error - invalid repo or directory + except DownloadError: + pass + + return False + + @staticmethod + def isdatabase(path): + """ + Checks if this is a SQLite database file which is the file format used for word vectors databases. + + Args: + path: path to check + + Returns: + True if this is a SQLite database + """ + + return isinstance(path, str) and STATICVECTORS and Database.isdatabase(path) + + def __init__(self, config, scoring, models): + # Check before parent constructor since it calls loadmodel + if not STATICVECTORS: + raise ImportError('staticvectors is not available - install "vectors" extra to enable') + + super().__init__(config, scoring, models) + + def loadmodel(self, path): + return StaticVectors(path) + + def encode(self, data, category=None): + # Iterate over each data element, tokenize (if necessary) and build an aggregated embeddings vector + embeddings = [] + for tokens in data: + # Convert to tokens, if necessary. If tokenized list is empty, use input string. + if isinstance(tokens, str): + tokenlist = Tokenizer.tokenize(tokens) + tokens = tokenlist if tokenlist else [tokens] + + # Generate weights for each vector using a scoring method + weights = self.scoring.weights(tokens) if self.scoring else None + + # pylint: disable=E1133 + if weights and [x for x in weights if x > 0]: + # Build weighted average embeddings vector. Create weights array as float32 to match embeddings precision. + embedding = np.average(self.lookup(tokens), weights=np.array(weights, dtype=np.float32), axis=0) + else: + # If no weights, use mean + embedding = np.mean(self.lookup(tokens), axis=0) + + embeddings.append(embedding) + + return np.array(embeddings, dtype=np.float32) + + def index(self, documents, batchsize=500, checkpoint=None): + # Derive number of parallel processes + parallel = self.config.get("parallel", True) + parallel = os.cpu_count() if parallel and isinstance(parallel, bool) else int(parallel) + + # Use default single process indexing logic + if not parallel: + return super().index(documents, batchsize) + + # Customize indexing logic with multiprocessing pool to efficiently build vectors + ids, dimensions, batches, stream = [], None, 0, None + + # Shared objects with Pool + args = (self.config, self.scoring) + + # Convert all documents to embedding arrays, stream embeddings to disk to control memory usage + with Pool(parallel, initializer=create, initargs=args) as pool: + with tempfile.NamedTemporaryFile(mode="wb", suffix=".npy", delete=False) as output: + stream = output.name + embeddings = [] + for uid, embedding in pool.imap(transform, documents, self.encodebatch): + if not dimensions: + # Set number of dimensions for embeddings + dimensions = embedding.shape[0] + + ids.append(uid) + embeddings.append(embedding) + + if len(embeddings) == batchsize: + np.save(output, np.array(embeddings, dtype=np.float32), allow_pickle=False) + batches += 1 + + embeddings = [] + + # Final embeddings batch + if embeddings: + np.save(output, np.array(embeddings, dtype=np.float32), allow_pickle=False) + batches += 1 + + return (ids, dimensions, batches, stream) + + def lookup(self, tokens): + """ + Queries word vectors for given list of input tokens. + + Args: + tokens: list of tokens to query + + Returns: + word vectors array + """ + + return self.model.embeddings(tokens) + + def tokens(self, data): + # Skip tokenization rules + return data diff --git a/src/python/txtai/vectors/recovery.py b/src/python/txtai/vectors/recovery.py new file mode 100644 index 0000000..f0dfb7c --- /dev/null +++ b/src/python/txtai/vectors/recovery.py @@ -0,0 +1,57 @@ +""" +Recovery module +""" + +import os +import shutil + + +class Recovery: + """ + Vector embeddings recovery. This class handles streaming embeddings from a vector checkpoint file. + """ + + def __init__(self, checkpoint, vectorsid, load): + """ + Creates a Recovery instance. + + Args: + checkpoint: checkpoint directory + vectorsid: vectors uid for current configuration + load: load embeddings method + """ + + self.spool, self.path, self.load = None, None, load + + # Get unique file id + path = f"{checkpoint}/{vectorsid}" + if os.path.exists(path): + # Generate recovery path + self.path = f"{checkpoint}/recovery" + + # Copy current checkpoint to recovery + shutil.copyfile(path, self.path) + + # Open file an return + # pylint: disable=R1732 + self.spool = open(self.path, "rb") + + def __call__(self): + """ + Reads and returns the next batch of embeddings. + + Returns + batch of embeddings + """ + + try: + return self.load(self.spool) if self.spool else None + except EOFError: + # End of spool file, cleanup + self.spool.close() + os.remove(self.path) + + # Clear parameters + self.spool, self.path = None, None + + return None diff --git a/src/python/txtai/vectors/sparse/__init__.py b/src/python/txtai/vectors/sparse/__init__.py new file mode 100644 index 0000000..dfce5a8 --- /dev/null +++ b/src/python/txtai/vectors/sparse/__init__.py @@ -0,0 +1,7 @@ +""" +Sparse vectors imports +""" + +from .base import SparseVectors +from .factory import SparseVectorsFactory +from .sbert import SparseSTVectors diff --git a/src/python/txtai/vectors/sparse/base.py b/src/python/txtai/vectors/sparse/base.py new file mode 100644 index 0000000..b0be71c --- /dev/null +++ b/src/python/txtai/vectors/sparse/base.py @@ -0,0 +1,90 @@ +""" +SparseVectors module +""" + +# Conditional import +try: + from scipy.sparse import csr_matrix, vstack + from sklearn.preprocessing import normalize + from sklearn.utils.extmath import safe_sparse_dot + + SPARSE = True +except ImportError: + SPARSE = False + +from ...util import SparseArray +from ..base import Vectors + + +# pylint: disable=W0223 +class SparseVectors(Vectors): + """ + Base class for sparse vector models. Vector models transform input content into sparse arrays. + """ + + def __init__(self, config, scoring, models): + # Check before parent constructor since it calls loadmodel + if not SPARSE: + raise ImportError('SparseVectors is not available - install "vectors" extra to enable') + + super().__init__(config, scoring, models) + + # Get normalization setting + self.isnormalize = self.config.get("normalize", self.defaultnormalize()) if self.config else None + + def encode(self, data, category=None): + # Encode data to embeddings + embeddings = super().encode(data, category) + + # Get sparse torch vector attributes + embeddings = embeddings.cpu().coalesce() + indices = embeddings.indices().numpy() + values = embeddings.values().numpy() + + # Return as SciPy CSR Matrix + return csr_matrix((values, indices), shape=embeddings.size()) + + def vectors(self, documents, batchsize=500, checkpoint=None, buffer=None, dtype=None): + # Run indexing + ids, dimensions, batches, stream = self.index(documents, batchsize, checkpoint) + + # Rebuild sparse array + embeddings = None + with open(stream, "rb") as queue: + for _ in range(batches): + # Read in array batch + data = self.loadembeddings(queue) + embeddings = vstack((embeddings, data)) if embeddings is not None else data + + # Return sparse array + return (ids, dimensions, embeddings) + + def dot(self, queries, data): + return safe_sparse_dot(queries, data.T, dense_output=True).tolist() + + def loadembeddings(self, f): + return SparseArray().load(f) + + def saveembeddings(self, f, embeddings): + SparseArray().save(f, embeddings) + + def truncate(self, embeddings): + raise ValueError("Truncate is not supported for sparse vectors") + + def normalize(self, embeddings): + # Optionally normalize embeddings using method that supports sparse vectors + return normalize(embeddings, copy=False) if self.isnormalize else embeddings + + def quantize(self, embeddings): + raise ValueError("Quantize is not supported for sparse vectors") + + def defaultnormalize(self): + """ + Returns the default normalization setting. + + Returns: + default normalization setting + """ + + # Sparse vector embeddings typically perform better as unnormalized + return False diff --git a/src/python/txtai/vectors/sparse/factory.py b/src/python/txtai/vectors/sparse/factory.py new file mode 100644 index 0000000..e924071 --- /dev/null +++ b/src/python/txtai/vectors/sparse/factory.py @@ -0,0 +1,55 @@ +""" +Factory module +""" + +from ...util import Resolver + +from .sbert import SparseSTVectors + + +class SparseVectorsFactory: + """ + Methods to create sparse vector models. + """ + + @staticmethod + def create(config, models=None): + """ + Create a Vectors model instance. + + Args: + config: vector configuration + models: models cache + + Returns: + Vectors + """ + + # Get vector method + method = config.get("method", "sentence-transformers") + + # Sentence Transformers vectors + if method == "sentence-transformers": + return SparseSTVectors(config, None, models) if config and config.get("path") else None + + # Resolve custom method + return SparseVectorsFactory.resolve(method, config, models) if method else None + + @staticmethod + def resolve(backend, config, models): + """ + Attempt to resolve a custom backend. + + Args: + backend: backend class + config: vector configuration + models: models cache + + Returns: + Vectors + """ + + try: + return Resolver()(backend)(config, None, models) + except Exception as e: + raise ImportError(f"Unable to resolve sparse vectors backend: '{backend}'") from e diff --git a/src/python/txtai/vectors/sparse/sbert.py b/src/python/txtai/vectors/sparse/sbert.py new file mode 100644 index 0000000..b387ffc --- /dev/null +++ b/src/python/txtai/vectors/sparse/sbert.py @@ -0,0 +1,34 @@ +""" +Sparse Sentence Transformers module +""" + +# Conditional import +try: + from sentence_transformers import SparseEncoder + + SENTENCE_TRANSFORMERS = True +except ImportError: + SENTENCE_TRANSFORMERS = False + +from ..dense.sbert import STVectors +from .base import SparseVectors + + +class SparseSTVectors(SparseVectors, STVectors): + """ + Builds sparse vectors using sentence-transformers (aka SBERT). + """ + + def __init__(self, config, scoring, models): + # Check before parent constructor since it calls loadmodel + if not SENTENCE_TRANSFORMERS: + raise ImportError('sentence-transformers is not available - install "vectors" extra to enable') + + super().__init__(config, scoring, models) + + def loadencoder(self, path, device, **kwargs): + return SparseEncoder(path, device=device, **kwargs) + + def defaultnormalize(self): + # Enable normalization by default if similarity function is cosine + return self.model and self.model.similarity_fn_name == "cosine" diff --git a/src/python/txtai/version.py b/src/python/txtai/version.py new file mode 100644 index 0000000..a7a5f04 --- /dev/null +++ b/src/python/txtai/version.py @@ -0,0 +1,6 @@ +""" +Version strings +""" + +# Current version tag +__version__ = "9.12.0" diff --git a/src/python/txtai/workflow/__init__.py b/src/python/txtai/workflow/__init__.py new file mode 100644 index 0000000..24e56ef --- /dev/null +++ b/src/python/txtai/workflow/__init__.py @@ -0,0 +1,8 @@ +""" +Workflow imports +""" + +from .base import Workflow +from .execute import Execute +from .factory import WorkflowFactory +from .task import * diff --git a/src/python/txtai/workflow/base.py b/src/python/txtai/workflow/base.py new file mode 100644 index 0000000..61d597b --- /dev/null +++ b/src/python/txtai/workflow/base.py @@ -0,0 +1,184 @@ +""" +Workflow module +""" + +import logging +import time +import traceback + +from datetime import datetime + +# Conditional import +try: + from croniter import croniter + + CRONITER = True +except ImportError: + CRONITER = False + +from .execute import Execute + +# Logging configuration +logger = logging.getLogger(__name__) + + +class Workflow: + """ + Base class for all workflows. + """ + + def __init__(self, tasks, batch=100, workers=None, name=None, stream=None): + """ + Creates a new workflow. Workflows are lists of tasks to execute. + + Args: + tasks: list of workflow tasks + batch: how many items to process at a time, defaults to 100 + workers: number of concurrent workers + name: workflow name + stream: workflow stream processor + """ + + self.tasks = tasks + self.batch = batch + self.workers = workers + self.name = name + self.stream = stream + + # Set default number of executor workers to max number of actions in a task + self.workers = max(len(task.action) for task in self.tasks) if not self.workers else self.workers + + def __call__(self, elements): + """ + Executes a workflow for input elements. This method returns a generator that yields transformed + data elements. + + Args: + elements: iterable data elements + + Returns: + generator that yields transformed data elements + """ + + # Create execute instance for this run + with Execute(self.workers) as executor: + # Run task initializers + self.initialize() + + # Process elements with stream processor, if available + elements = self.stream(elements) if self.stream else elements + + # Process elements in batches + for batch in self.chunk(elements): + yield from self.process(batch, executor) + + # Run task finalizers + self.finalize() + + def schedule(self, cron, elements, iterations=None): + """ + Schedules a workflow using a cron expression and elements. + + Args: + cron: cron expression + elements: iterable data elements passed to workflow each call + iterations: number of times to run workflow, defaults to run indefinitely + """ + + # Check that croniter is installed + if not CRONITER: + raise ImportError('Workflow scheduling is not available - install "workflow" extra to enable') + + logger.info("'%s' scheduler started with schedule %s", self.name, cron) + + maxiterations = iterations + while iterations is None or iterations > 0: + # Schedule using localtime + schedule = croniter(cron, datetime.now().astimezone()).get_next(datetime) + logger.info("'%s' next run scheduled for %s", self.name, schedule.isoformat()) + time.sleep(schedule.timestamp() - time.time()) + + # Run workflow + # pylint: disable=W0703 + try: + for _ in self(elements): + pass + except Exception: + logger.error(traceback.format_exc()) + + # Decrement iterations remaining, if necessary + if iterations is not None: + iterations -= 1 + + logger.info("'%s' max iterations (%d) reached", self.name, maxiterations) + + def initialize(self): + """ + Runs task initializer methods (if any) before processing data. + """ + + # Run task initializers + for task in self.tasks: + if task.initialize: + task.initialize() + + def chunk(self, elements): + """ + Splits elements into batches. This method efficiently processes both fixed size inputs and + dynamically generated inputs. + + Args: + elements: iterable data elements + + Returns: + evenly sized batches with the last batch having the remaining elements + """ + + # Build batches by slicing elements, more efficient for fixed sized inputs + if hasattr(elements, "__len__") and hasattr(elements, "__getitem__"): + for x in range(0, len(elements), self.batch): + yield elements[x : x + self.batch] + + # Build batches by iterating over elements when inputs are dynamically generated (i.e. generators) + else: + batch = [] + for x in elements: + batch.append(x) + + if len(batch) == self.batch: + yield batch + batch = [] + + # Final batch + if batch: + yield batch + + def process(self, elements, executor): + """ + Processes a batch of data elements. + + Args: + elements: iterable data elements + executor: execute instance, enables concurrent task actions + + Returns: + transformed data elements + """ + + # Run elements through each task + for x, task in enumerate(self.tasks): + logger.debug("Running Task #%d", x) + elements = task(elements, executor) + + # Yield results processed by all tasks + yield from elements + + def finalize(self): + """ + Runs task finalizer methods (if any) after all data processed. + """ + + # Run task finalizers + for task in self.tasks: + if task.finalize: + task.finalize() diff --git a/src/python/txtai/workflow/execute.py b/src/python/txtai/workflow/execute.py new file mode 100644 index 0000000..e5dafb4 --- /dev/null +++ b/src/python/txtai/workflow/execute.py @@ -0,0 +1,102 @@ +""" +Execute module +""" + +from multiprocessing.pool import Pool, ThreadPool + +# Core library imports +from ..util import Library + +torch = Library().torch() + + +class Execute: + """ + Supports sequential, multithreading and multiprocessing based execution of tasks. + """ + + def __init__(self, workers=None): + """ + Creates a new execute instance. Functions can be executed sequentially, in a thread pool + or in a process pool. Once created, the thread and/or process pool will stay open until the + close method is called. + + Args: + workers: number of workers for thread/process pools + """ + + # Number of workers to use in thread/process pools + self.workers = workers + + self.thread = None + self.process = None + + def __del__(self): + self.close() + + def __enter__(self): + return self + + def __exit__(self, etype, value, traceback): + self.close() + + def run(self, method, function, args): + """ + Runs multiple calls of function for each tuple in args. The method parameter controls if the calls are + sequential (method = None), multithreaded (method = "thread") or with multiprocessing (method="process"). + + Args: + method: run method - "thread" for multithreading, "process" for multiprocessing, otherwise runs sequentially + function: function to run + args: list of tuples with arguments to each call + """ + + # Concurrent processing + if method and len(args) > 1: + pool = self.pool(method) + if pool: + return pool.starmap(function, args, 1) + + # Sequential processing + return [function(*arg) for arg in args] + + def pool(self, method): + """ + Gets a handle to a concurrent processing pool. This method will create the pool if it doesn't already exist. + + Args: + method: pool type - "thread" or "process" + + Returns: + concurrent processing pool or None if no pool of that type available + """ + + if method == "thread": + if not self.thread: + self.thread = ThreadPool(self.workers) + + return self.thread + + if method == "process": + if not self.process: + # Importing torch.multiprocessing will register torch shared memory serialization for cuda + self.process = Pool(self.workers, context=torch.multiprocessing.get_context("spawn")) + + return self.process + + return None + + def close(self): + """ + Closes concurrent processing pools. + """ + + if hasattr(self, "thread") and self.thread: + self.thread.close() + self.thread.join() + self.thread = None + + if hasattr(self, "process") and self.process: + self.process.close() + self.process.join() + self.process = None diff --git a/src/python/txtai/workflow/factory.py b/src/python/txtai/workflow/factory.py new file mode 100644 index 0000000..506f077 --- /dev/null +++ b/src/python/txtai/workflow/factory.py @@ -0,0 +1,42 @@ +""" +Workflow factory module +""" + +from .base import Workflow +from .task import TaskFactory + + +class WorkflowFactory: + """ + Workflow factory. Creates new Workflow instances. + """ + + @staticmethod + def create(config, name): + """ + Creates a new Workflow instance. + + Args: + config: Workflow configuration + name: Workflow name + + Returns: + Workflow + """ + + # Resolve workflow tasks + tasks = [] + for tconfig in config["tasks"]: + task = tconfig.pop("task") if "task" in tconfig else "" + tasks.append(TaskFactory.create(tconfig, task)) + + config["tasks"] = tasks + + if "stream" in config: + sconfig = config["stream"] + task = sconfig.pop("task") if "task" in sconfig else "stream" + + config["stream"] = TaskFactory.create(sconfig, task) + + # Create workflow + return Workflow(**config, name=name) diff --git a/src/python/txtai/workflow/task/__init__.py b/src/python/txtai/workflow/task/__init__.py new file mode 100644 index 0000000..996790c --- /dev/null +++ b/src/python/txtai/workflow/task/__init__.py @@ -0,0 +1,18 @@ +""" +Task imports +""" + +from .base import Task +from .console import ConsoleTask +from .export import ExportTask +from .factory import TaskFactory +from .file import FileTask +from .image import ImageTask +from .retrieve import RetrieveTask +from .service import ServiceTask +from .storage import StorageTask +from .stream import StreamTask +from .template import * +from .template import RagTask as ExtractorTask +from .url import UrlTask +from .workflow import WorkflowTask diff --git a/src/python/txtai/workflow/task/base.py b/src/python/txtai/workflow/task/base.py new file mode 100644 index 0000000..4fc80a7 --- /dev/null +++ b/src/python/txtai/workflow/task/base.py @@ -0,0 +1,494 @@ +""" +Task module +""" + +import logging +import re +import types + +# Core library imports +from ...util import Library + +library = Library() +np = library.numpy() +torch = library.torch() + +# Logging configuration +logger = logging.getLogger(__name__) + + +class Task: + """ + Base class for all workflow tasks. + """ + + def __init__( + self, + action=None, + select=None, + unpack=True, + column=None, + merge="hstack", + initialize=None, + finalize=None, + concurrency=None, + onetomany=True, + **kwargs, + ): + """ + Creates a new task. A task defines two methods, type of data it accepts and the action to execute + for each data element. Action is a callable function or list of callable functions. + + Args: + action: action(s) to execute on each data element + select: filter(s) used to select data to process + unpack: if data elements should be unpacked or unwrapped from (id, data, tag) tuples + column: column index to select if element is a tuple, defaults to all + merge: merge mode for joining multi-action outputs, defaults to hstack + initialize: action to execute before processing + finalize: action to execute after processing + concurrency: sets concurrency method when execute instance available + valid values: "thread" for thread-based concurrency, "process" for process-based concurrency + onetomany: if one-to-many data transformations should be enabled, defaults to True + kwargs: additional keyword arguments + """ + + # Standardize into list of actions + if not action: + action = [] + elif not isinstance(action, list): + action = [action] + + self.action = action + self.select = select + self.unpack = unpack + self.column = column + self.merge = merge + self.initialize = initialize + self.finalize = finalize + self.concurrency = concurrency + self.onetomany = onetomany + + # Check for custom registration. Adds additional instance members and validates required dependencies available. + if hasattr(self, "register"): + self.register(**kwargs) + elif kwargs: + # Raise error if additional keyword arguments passed in without register method + kwargs = ", ".join(f"'{kw}'" for kw in kwargs) + raise TypeError(f"__init__() got unexpected keyword arguments: {kwargs}") + + def __call__(self, elements, executor=None): + """ + Executes action for a list of data elements. + + Args: + elements: iterable data elements + executor: execute instance, enables concurrent task actions + + Returns: + transformed data elements + """ + + if isinstance(elements, list): + return self.filteredrun(elements, executor) + + return self.run(elements, executor) + + def filteredrun(self, elements, executor): + """ + Executes a filtered run, which will tag all inputs with a process id, filter elements down to elements the + task can handle and execute on that subset. Items not selected for processing will be returned unmodified. + + Args: + elements: iterable data elements + executor: execute instance, enables concurrent task actions + + Returns: + transformed data elements + """ + + # Build list of elements with unique process ids + indexed = list(enumerate(elements)) + + # Filter data down to data this task handles + data = [(x, self.upack(element)) for x, element in indexed if self.accept(self.upack(element, True))] + + # Get list of filtered process ids + ids = [x for x, _ in data] + + # Prepare elements and execute task action(s) + results = self.execute([self.prepare(element) for _, element in data], executor) + + # Pack results back into elements + if self.merge: + elements = self.filteredpack(results, indexed, ids) + else: + elements = [self.filteredpack(r, indexed, ids) for r in results] + + return elements + + def filteredpack(self, results, indexed, ids): + """ + Processes and packs results back into original input elements. + + Args: + results: task results + indexed: original elements indexed by process id + ids: process ids accepted by this task + + Returns: + packed elements + """ + + # Update with transformed elements. Handle one to many transformations. + elements = [] + for x, element in indexed: + if x in ids: + # Get result for process id + result = results[ids.index(x)] + + if isinstance(result, OneToMany): + # One to many transformations + elements.extend([self.pack(element, r) for r in result]) + else: + # One to one transformations + elements.append(self.pack(element, result)) + else: + # Pass unprocessed elements through + elements.append(element) + + return elements + + def run(self, elements, executor): + """ + Executes a task run for elements. A standard run processes all elements. + + Args: + elements: iterable data elements + executor: execute instance, enables concurrent task actions + + Returns: + transformed data elements + """ + + # Execute task actions + results = self.execute(elements, executor) + + # Handle one to many transformations + if isinstance(results, list): + elements = [] + for result in results: + if isinstance(result, OneToMany): + # One to many transformations + elements.extend(result) + else: + # One to one transformations + elements.append(result) + + return elements + + return results + + def accept(self, element): + """ + Determines if this task can handle the input data format. + + Args: + element: input data element + + Returns: + True if this task can process this data element, False otherwise + """ + + return (isinstance(element, str) and re.search(self.select, element.lower())) if element is not None and self.select else True + + def upack(self, element, force=False): + """ + Unpacks data for processing. + + Args: + element: input data element + force: if True, data is unpacked even if task has unpack set to False + + Returns: + data + """ + + # Extract data from (id, data, tag) formatted elements + if (self.unpack or force) and isinstance(element, tuple) and len(element) > 1: + return element[1] + + return element + + def pack(self, element, data): + """ + Packs data after processing. + + Args: + element: transformed data element + data: item to pack element into + + Returns: + packed data + """ + + # Pack data into (id, data, tag) formatted elements + if self.unpack and isinstance(element, tuple) and len(element) > 1: + # If new data is a (id, data, tag) tuple use that except for multi-action "hstack" merges which produce tuples + if isinstance(data, tuple) and (len(self.action) <= 1 or self.merge != "hstack"): + return data + + # Create a copy of tuple, update data element and return + element = list(element) + element[1] = data + return tuple(element) + + return data + + def prepare(self, element): + """ + Method that allows downstream tasks to prepare data element for processing. + + Args: + element: input data element + + Returns: + data element ready for processing + """ + + return element + + def execute(self, elements, executor): + """ + Executes action(s) on elements. + + Args: + elements: list of data elements + executor: execute instance, enables concurrent task actions + + Returns: + transformed data elements + """ + + if self.action: + # Run actions + outputs = [] + for x, action in enumerate(self.action): + # Filter elements by column index if necessary - supports a single int or an action index to column index mapping + index = self.column[x] if isinstance(self.column, dict) else self.column + inputs = [self.extract(e, index) for e in elements] if index is not None else elements + + # Queue arguments for executor, process immediately if no executor available + outputs.append((action, inputs) if executor else self.process(action, inputs)) + + # Run with executor if available + if executor: + outputs = executor.run(self.concurrency, self.process, outputs) + + # Run post process operations + return self.postprocess(outputs) + + return elements + + def extract(self, element, index): + """ + Extracts a column from element by index if the element is a tuple. + + Args: + element: input element + index: column index + + Returns: + extracted column + """ + + if isinstance(element, tuple): + if not self.unpack and len(element) == 3 and isinstance(element[1], tuple): + return (element[0], element[1][index], element[2]) + + return element[index] + + return element + + def process(self, action, inputs): + """ + Executes action using inputs as arguments. + + Args: + action: callable object + inputs: action inputs + + Returns: + action outputs + """ + + # Log inputs + logger.debug("Inputs: %s", inputs) + + # Execute action and get outputs + outputs = action(inputs) + + # Consume generator output, if necessary + if isinstance(outputs, types.GeneratorType): + outputs = list(outputs) + + # Log outputs + logger.debug("Outputs: %s", outputs) + + return outputs + + def postprocess(self, outputs): + """ + Runs post process routines after a task action. + + Args: + outputs: task outputs + + Returns: + postprocessed outputs + """ + + # Unpack single action tasks + if len(self.action) == 1: + return self.single(outputs[0]) + + # Return unmodified outputs when merge set to None + if not self.merge: + return outputs + + if self.merge == "vstack": + return self.vstack(outputs) + if self.merge == "concat": + return self.concat(outputs) + + # Default mode is hstack + return self.hstack(outputs) + + def single(self, outputs): + """ + Post processes and returns single action outputs. + + Args: + outputs: outputs from a single task + + Returns: + post processed outputs + """ + + if self.onetomany and isinstance(outputs, list): + # Wrap one to many transformations + outputs = [OneToMany(output) if isinstance(output, list) else output for output in outputs] + + return outputs + + def vstack(self, outputs): + """ + Merges outputs row-wise. Returns a list of lists which will be interpreted as a one to many transformation. + + Row-wise merge example (2 actions) + + Inputs: [a, b, c] + + Outputs => [[a1, b1, c1], [a2, b2, c2]] + + Row Merge => [[a1, a2], [b1, b2], [c1, c2]] = [a1, a2, b1, b2, c1, c2] + + Args: + outputs: task outputs + + Returns: + list of aggregated/zipped outputs as one to many transforms (row-wise) + """ + + # If all outputs are numpy arrays, use native method + if all(isinstance(output, np.ndarray) for output in outputs): + return np.concatenate(np.stack(outputs, axis=1)) + + # If all outputs are torch tensors, use native method + # pylint: disable=E1101 + if all(torch.is_tensor(output) for output in outputs): + return torch.cat(tuple(torch.stack(outputs, axis=1))) + + # Flatten into lists of outputs per input row. Wrap as one to many transformation. + merge = [] + for x in zip(*outputs): + combine = [] + for y in x: + if isinstance(y, list): + combine.extend(y) + else: + combine.append(y) + + merge.append(OneToMany(combine)) + + return merge + + def hstack(self, outputs): + """ + Merges outputs column-wise. Returns a list of tuples which will be interpreted as a one to one transformation. + + Column-wise merge example (2 actions) + + Inputs: [a, b, c] + + Outputs => [[a1, b1, c1], [a2, b2, c2]] + + Column Merge => [(a1, a2), (b1, b2), (c1, c2)] + + Args: + outputs: task outputs + + Returns: + list of aggregated/zipped outputs as tuples (column-wise) + """ + + # If all outputs are numpy arrays, use native method + if all(isinstance(output, np.ndarray) for output in outputs): + return np.stack(outputs, axis=1) + + # If all outputs are torch tensors, use native method + # pylint: disable=E1101 + if all(torch.is_tensor(output) for output in outputs): + return torch.stack(outputs, axis=1) + + return list(zip(*outputs)) + + def concat(self, outputs): + """ + Merges outputs column-wise and concats values together into a string. Returns a list of strings. + + Concat merge example (2 actions) + + Inputs: [a, b, c] + + Outputs => [[a1, b1, c1], [a2, b2, c2]] + + Concat Merge => [(a1, a2), (b1, b2), (c1, c2)] => ["a1. a2", "b1. b2", "c1. c2"] + + Args: + outputs: task outputs + + Returns: + list of concat outputs + """ + + return [". ".join([str(y) for y in x if y]) for x in self.hstack(outputs)] + + +class OneToMany: + """ + Encapsulates list output for a one to many transformation. + """ + + def __init__(self, values): + """ + Creates a new OneToMany transformation. + + Args: + values: list of outputs + """ + + self.values = values + + def __iter__(self): + return self.values.__iter__() diff --git a/src/python/txtai/workflow/task/console.py b/src/python/txtai/workflow/task/console.py new file mode 100644 index 0000000..482290c --- /dev/null +++ b/src/python/txtai/workflow/task/console.py @@ -0,0 +1,24 @@ +""" +ConsoleTask module +""" + +import json + +from .base import Task + + +class ConsoleTask(Task): + """ + Task that prints task elements to the console. + """ + + def __call__(self, elements, executor=None): + # Run task + outputs = super().__call__(elements, executor) + + # Print inputs and outputs to console + print("Inputs:", json.dumps(elements, indent=2)) + print("Outputs:", json.dumps(outputs, indent=2)) + + # Return results + return outputs diff --git a/src/python/txtai/workflow/task/export.py b/src/python/txtai/workflow/task/export.py new file mode 100644 index 0000000..f0c5601 --- /dev/null +++ b/src/python/txtai/workflow/task/export.py @@ -0,0 +1,64 @@ +""" +ExportTask module +""" + +import datetime +import os + +# Conditional import +try: + import pandas as pd + + PANDAS = True +except ImportError: + PANDAS = False + +from .base import Task + + +class ExportTask(Task): + """ + Task that exports task elements using Pandas. + """ + + def register(self, output=None, timestamp=None): + """ + Add export parameters to task. Checks if required dependencies are installed. + + Args: + output: output file path + timestamp: true if output file should be timestamped + """ + + if not PANDAS: + raise ImportError('ExportTask is not available - install "workflow" extra to enable') + + # pylint: disable=W0201 + self.output = output + self.timestamp = timestamp + + def __call__(self, elements, executor=None): + # Run task + outputs = super().__call__(elements, executor) + + # Get output file extension + output = self.output + parts = list(os.path.splitext(output)) + extension = parts[-1].lower() + + # Add timestamp to filename + if self.timestamp: + timestamp = datetime.datetime.now(datetime.timezone.utc).strftime("%Y%m%dT%H%M%SZ") + parts[-1] = timestamp + parts[-1] + + # Create full path to output file + output = ".".join(parts) + + # Write output + if extension == ".xlsx": + pd.DataFrame(outputs).to_excel(output, index=False) + else: + pd.DataFrame(outputs).to_csv(output, index=False) + + # Return results + return outputs diff --git a/src/python/txtai/workflow/task/factory.py b/src/python/txtai/workflow/task/factory.py new file mode 100644 index 0000000..0f60105 --- /dev/null +++ b/src/python/txtai/workflow/task/factory.py @@ -0,0 +1,89 @@ +""" +Task factory module +""" + +import functools + +from ...util import Resolver + + +class TaskFactory: + """ + Task factory. Creates new Task instances. + """ + + @staticmethod + def get(task): + """ + Gets a new instance of task class. + + Args: + task: Task instance class + + Returns: + Task class + """ + + # Local task if no package + if "." not in task: + # Get parent package + task = ".".join(__name__.split(".")[:-1]) + "." + task.capitalize() + "Task" + + # Attempt to load custom task + return Resolver()(task) + + @staticmethod + def create(config, task): + """ + Creates a new Task instance. + + Args: + config: Task configuration + task: Task instance class + + Returns: + Task + """ + + # Create lambda function if additional arguments present + if "args" in config: + args = config.pop("args") + action = config["action"] + if action: + if isinstance(action, list): + config["action"] = [Partial.create(a, args[i]) for i, a in enumerate(action)] + else: + # Accept keyword or positional arguments + config["action"] = lambda x: action(x, **args) if isinstance(args, dict) else action(x, *args) + + # Get Task instance + return TaskFactory.get(task)(**config) + + +class Partial(functools.partial): + """ + Modifies functools.partial to prepend arguments vs append. + """ + + @staticmethod + def create(action, args): + """ + Creates a new Partial function. + + Args: + action: action to execute + args: arguments + + Returns: + Partial + """ + + return Partial(action, **args) if isinstance(args, dict) else Partial(action, *args) if args else Partial(action) + + def __call__(self, *args, **kwargs): + # Update keyword arguments + kw = self.keywords.copy() + kw.update(kwargs) + + # Execute function with new arguments prepended to default arguments + return self.func(*(args + self.args), **kw) diff --git a/src/python/txtai/workflow/task/file.py b/src/python/txtai/workflow/task/file.py new file mode 100644 index 0000000..66d3424 --- /dev/null +++ b/src/python/txtai/workflow/task/file.py @@ -0,0 +1,28 @@ +""" +FileTask module +""" + +import os +import re + +from .base import Task + + +class FileTask(Task): + """ + Task that processes file paths + """ + + # File prefix + FILE = r"file:\/\/" + + def accept(self, element): + # Replace file prefixes + element = re.sub(FileTask.FILE, "", element) + + # Only accept file paths that exist + return super().accept(element) and isinstance(element, str) and os.path.exists(element) + + def prepare(self, element): + # Replace file prefixes + return re.sub(FileTask.FILE, "", element) diff --git a/src/python/txtai/workflow/task/image.py b/src/python/txtai/workflow/task/image.py new file mode 100644 index 0000000..f6a2e9f --- /dev/null +++ b/src/python/txtai/workflow/task/image.py @@ -0,0 +1,36 @@ +""" +ImageTask module +""" + +import re + +# Conditional import +try: + from PIL import Image + + PIL = True +except ImportError: + PIL = False + +from .file import FileTask + + +class ImageTask(FileTask): + """ + Task that processes image file urls + """ + + def register(self): + """ + Checks if required dependencies are installed. + """ + + if not PIL: + raise ImportError('ImageTask is not available - install "workflow" extra to enable') + + def accept(self, element): + # Only accept image files + return super().accept(element) and re.search(r"\.(gif|bmp|jpg|jpeg|png|webp)$", element.lower()) + + def prepare(self, element): + return Image.open(super().prepare(element)) diff --git a/src/python/txtai/workflow/task/retrieve.py b/src/python/txtai/workflow/task/retrieve.py new file mode 100644 index 0000000..d68fd98 --- /dev/null +++ b/src/python/txtai/workflow/task/retrieve.py @@ -0,0 +1,61 @@ +""" +RetrieveTask module +""" + +import os +import tempfile + +from urllib.request import urlretrieve +from urllib.parse import urlparse + +from .url import UrlTask + + +class RetrieveTask(UrlTask): + """ + Task that retrieves urls (local or remote) to a local directory. + """ + + def register(self, directory=None, flatten=True): + """ + Adds retrieve parameters to task. + + Args: + directory: local directory used to store retrieved files + flatten: flatten input directory structure, defaults to True + """ + + # pylint: disable=W0201 + # Create default temporary directory if not specified + if not directory: + # Save tempdir to prevent content from being deleted until this task is out of scope + # pylint: disable=R1732 + self.tempdir = tempfile.TemporaryDirectory() + directory = self.tempdir.name + + # Create output directory if necessary + os.makedirs(directory, exist_ok=True) + + self.directory = directory + self.flatten = flatten + + def prepare(self, element): + # Extract file path from URL + path = urlparse(element).path + + if self.flatten: + # Flatten directory structure (default) + path = os.path.join(self.directory, os.path.basename(path)) + else: + # Derive output path + path = os.path.join(self.directory, os.path.normpath(path.lstrip("/"))) + directory = os.path.dirname(path) + + # Create local directory, if necessary + os.makedirs(directory, exist_ok=True) + + # Retrieve URL + urlretrieve(element, path) + + # Return new file path + return path diff --git a/src/python/txtai/workflow/task/service.py b/src/python/txtai/workflow/task/service.py new file mode 100644 index 0000000..12557fd --- /dev/null +++ b/src/python/txtai/workflow/task/service.py @@ -0,0 +1,102 @@ +""" +ServiceTask module +""" + +# Conditional import +try: + import requests + import xmltodict + + XML_TO_DICT = True +except ImportError: + XML_TO_DICT = False + +from .base import Task + + +class ServiceTask(Task): + """ + Task to runs requests against remote service urls. + """ + + def register(self, url=None, method=None, params=None, batch=True, extract=None): + """ + Adds service parameters to task. Checks if required dependencies are installed. + + Args: + url: url to connect to + method: http method, GET or POST + params: default query parameters + batch: if True, all elements are passed in a single batch request, otherwise a service call is executed per element + extract: list of sections to extract from response + """ + + if not XML_TO_DICT: + raise ImportError('ServiceTask is not available - install "workflow" extra to enable') + + # pylint: disable=W0201 + # Save URL, method and parameter defaults + self.url = url + self.method = method + self.params = params + + # If True, all elements are passed in a single batch request, otherwise a service call is executed per element + self.batch = batch + + # Save sections to extract. Supports both a single string and a hierarchical list of sections. + self.extract = extract + if self.extract: + self.extract = [self.extract] if isinstance(self.extract, str) else self.extract + + def execute(self, elements, executor=None): + if self.batch: + elements = self.request(elements) + else: + elements = [self.request(element) for element in elements] + + return super().execute(elements, executor) + + def request(self, data): + """ + Execute service request. + + Args: + url: service url + method: method (get or post) + params: dict of constant parameters to pass to request + data: dynamic data for this specific request + + Returns: + response as JSON + """ + + if not self.params: + params = data + else: + # Create copy of parameters + params = self.params.copy() + + # Add data to parameters + for key in params: + if not params[key]: + params[key] = data + + # Run request + if self.method and self.method.lower() == "get": + response = requests.get(self.url, params=params) + else: + response = requests.post(self.url, json=params) + + # Parse data based on content-type + mimetype = response.headers["Content-Type"].split(";")[0] + if mimetype.lower().endswith("xml"): + data = xmltodict.parse(response.text) + else: + data = response.json() + + # Extract content from response, if necessary + if self.extract: + for tag in self.extract: + data = data[tag] + + return data diff --git a/src/python/txtai/workflow/task/storage.py b/src/python/txtai/workflow/task/storage.py new file mode 100644 index 0000000..24ca8fd --- /dev/null +++ b/src/python/txtai/workflow/task/storage.py @@ -0,0 +1,110 @@ +""" +StorageTask module +""" + +import os +import re + +# Conditional import +try: + from libcloud.storage.providers import get_driver + + LIBCLOUD = True +except ImportError: + LIBCLOUD = False + +from .base import Task + + +class StorageTask(Task): + """ + Task that processes object storage buckets. Supports local and cloud providers in Apache libcloud. + """ + + # URL prefix + PREFIX = r"(\w+):\/\/.*" + PATH = r"\w+:\/\/(.*)" + + def register(self, key=None, secret=None, host=None, port=None, token=None, region=None): + """ + Checks if required dependencies are installed. Reads in cloud storage parameters. + + Args: + key: provider-specific access key + secret: provider-specific access secret + host: server host name + port: server port + token: temporary session token + region: storage region + """ + + if not LIBCLOUD: + raise ImportError('StorageTask is not available - install "workflow" extra to enable') + + # pylint: disable=W0201 + self.key = key + self.secret = secret + self.host = host + self.port = port + self.token = token + self.region = region + + def __call__(self, elements, executor=None): + # Create aggregated directory listing for all elements + outputs = [] + for element in elements: + if self.matches(element): + # Get directory listing and run actions + outputs.extend(super().__call__(self.list(element), executor)) + else: + outputs.append(element) + + return outputs + + def matches(self, element): + """ + Determines if this element is a storage element. + + Args: + element: input storage element + + Returns: + True if this is a storage element + """ + + # Only accept file URLs + return re.match(StorageTask.PREFIX, self.upack(element, True).lower()) + + def list(self, element): + """ + Gets a list of urls for a object container. + + Args: + element: object container + + Returns: + list of urls + """ + + provider = re.sub(StorageTask.PREFIX, r"\1", element.lower()) + path = re.sub(StorageTask.PATH, r"\1", element) + + # Load key and secret, if applicable + key = self.key if self.key is not None else os.environ.get("ACCESS_KEY") + secret = self.secret if self.secret is not None else os.environ.get("ACCESS_SECRET") + + # Parse key and container + key, container = (os.path.dirname(path), os.path.basename(path)) if key is None else (key, path) + + # Parse optional prefix from container + parts = container.split("/", 1) + container, prefix = (parts[0], parts[1]) if len(parts) > 1 else (container, None) + + # Get driver for provider + driver = get_driver(provider) + + # Get client connection + client = driver(key, secret, **{field: getattr(self, field) for field in ["host", "port", "region", "token"] if getattr(self, field)}) + + container = client.get_container(container_name=container) + return [client.get_object_cdn_url(obj) for obj in client.list_container_objects(container=container, prefix=prefix)] diff --git a/src/python/txtai/workflow/task/stream.py b/src/python/txtai/workflow/task/stream.py new file mode 100644 index 0000000..93c2efa --- /dev/null +++ b/src/python/txtai/workflow/task/stream.py @@ -0,0 +1,33 @@ +""" +StreamTask module +""" + +from .base import Task + + +class StreamTask(Task): + """ + Task that calls a task action and yields results. + """ + + def register(self, batch=False): + """ + Adds stream parameters to task. + + Args: + batch: all elements are passed to a single action call if True, otherwise an action call is executed per element, defaults to False + """ + + # pylint: disable=W0201 + # All elements are passed to a single action call if True, otherwise an action call is executed per element, defaults to False + self.batch = batch + + def __call__(self, elements, executor=None): + for action in self.action: + if self.batch: + # Single batch call + yield from action(elements) + else: + # Call action for each element + for x in elements: + yield from action(x) diff --git a/src/python/txtai/workflow/task/template.py b/src/python/txtai/workflow/task/template.py new file mode 100644 index 0000000..7b3e9ba --- /dev/null +++ b/src/python/txtai/workflow/task/template.py @@ -0,0 +1,116 @@ +""" +Template module +""" + +from string import Formatter + +from ...util import TemplateFormatter +from .file import Task + + +class TemplateTask(Task): + """ + Task that generates text from a template and task inputs. Templates can be used to prepare data for a number of tasks + including generating large language model (LLM) prompts. + """ + + def register(self, template=None, rules=None, strict=True): + """ + Read template parameters. + + Args: + template: prompt template + rules: parameter rules + strict: requires all task inputs to be consumed by template, defaults to True + """ + + # pylint: disable=W0201 + # Template text + self.template = template if template else self.defaulttemplate() + + # Template processing rules + self.rules = rules if rules else self.defaultrules() + + # Create formatter + self.formatter = TemplateFormatter() if strict else Formatter() + + def prepare(self, element): + # Check if element matches any processing rules + match = self.match(element) + if match: + return match + + # Apply template processing, if applicable + if self.template: + # Pass dictionary as named prompt template parameters + if isinstance(element, dict): + return self.formatter.format(self.template, **element) + + # Pass tuple as prompt template parameters (arg0 - argN) + if isinstance(element, tuple): + return self.formatter.format(self.template, **{f"arg{i}": x for i, x in enumerate(element)}) + + # Default behavior is to use input as {text} parameter in prompt template + return self.formatter.format(self.template, text=element) + + # Return original inputs when no prompt provided + return element + + def defaulttemplate(self): + """ + Generates a default template for this task. Base method returns None. + + Returns: + default template + """ + + return None + + def defaultrules(self): + """ + Generates a default rules for this task. Base method returns an empty dictionary. + + Returns: + default rules + """ + + return {} + + def match(self, element): + """ + Check if element matches any processing rules. + + Args: + element: input element + + Returns: + matching value if found, None otherwise + """ + + if self.rules and isinstance(element, dict): + # Check if any rules are matched + for key, value in self.rules.items(): + if element[key] == value: + return element[key] + + return None + + +class RagTask(TemplateTask): + """ + Template task that prepares input for a rag pipeline. + """ + + def prepare(self, element): + # Apply prompt template using all variables except "query" and use output as question + if isinstance(element, dict): + # Make a copy without query and run through template + params = dict(element) + params.pop("query", None) + params["text"] = params.pop("question") + + element["question"] = super().prepare(params) + return element + + # Default mode is to use element text for both query and question + return {"query": element, "question": super().prepare(element)} diff --git a/src/python/txtai/workflow/task/url.py b/src/python/txtai/workflow/task/url.py new file mode 100644 index 0000000..056d563 --- /dev/null +++ b/src/python/txtai/workflow/task/url.py @@ -0,0 +1,20 @@ +""" +UrlTask module +""" + +import re + +from .base import Task + + +class UrlTask(Task): + """ + Task that processes urls + """ + + # URL prefix + PREFIX = r"\w+:\/\/" + + def accept(self, element): + # Only accept elements that start with a url prefix + return super().accept(element) and re.match(UrlTask.PREFIX, element.lower()) diff --git a/src/python/txtai/workflow/task/workflow.py b/src/python/txtai/workflow/task/workflow.py new file mode 100644 index 0000000..f05a4c9 --- /dev/null +++ b/src/python/txtai/workflow/task/workflow.py @@ -0,0 +1,14 @@ +""" +WorkflowTask module +""" + +from .base import Task + + +class WorkflowTask(Task): + """ + Task that executes a separate Workflow + """ + + def process(self, action, inputs): + return list(super().process(action, inputs)) diff --git a/test/python/testagent.py b/test/python/testagent.py new file mode 100644 index 0000000..1b6bdcf --- /dev/null +++ b/test/python/testagent.py @@ -0,0 +1,229 @@ +""" +Agent module tests +""" + +import os +import tempfile +import unittest + +from unittest.mock import patch + +from datetime import datetime + +from smolagents import CodeAgent, PythonInterpreterTool + +from txtai.agent import Agent +from txtai.embeddings import Embeddings + +# agents.md content +AGENTS = """ +Basic instructions here +""" + +# Sample skill.md content +SKILL = """--- +name: hello +description: says hello world +--- + +Says hello world +""" + + +class TestAgent(unittest.TestCase): + """ + Agent tests. + """ + + def testExecute(self): + """ + Test executing main agent loop + """ + + agent = Agent(llm="hf-internal-testing/tiny-random-LlamaForCausalLM", max_steps=1) + + # Patch LLM to generate answer + agent.process.model.llm = lambda *args, **kwargs: 'Action:\n{"name": "final_answer", "arguments": "Hi"}' + + self.assertEqual(agent("Hello"), "Hi") + + def testInstructions(self): + """ + Test loading an agents.md file + """ + + # Test loading instructions from file + agents = os.path.join(tempfile.gettempdir(), "agents.md") + with open(agents, "w", encoding="utf-8") as output: + output.write(AGENTS) + + agent = Agent(instructions=agents, llm="hf-internal-testing/tiny-random-LlamaForCausalLM", max_iterations=1) + agent.process.model.llm = lambda *args, **kwargs: 'Action:\n{"name": "final_answer", "arguments": "Hi"}' + self.assertEqual(agent("Hello"), "Hi") + + # Test loading from memory + agent = Agent(instructions=AGENTS, llm="hf-internal-testing/tiny-random-LlamaForCausalLM", max_iterations=1) + agent.process.model.llm = lambda *args, **kwargs: 'Action:\n{"name": "final_answer", "arguments": "Hi"}' + self.assertEqual(agent("Hello"), "Hi") + + def testMemory(self): + """ + Test agent memory + """ + + agent = Agent(llm="hf-internal-testing/tiny-random-LlamaForCausalLM", max_steps=1, memory=5) + + # Patch LLM to generate answer + agent.process.model.llm = lambda *args, **kwargs: 'Action:\n{"name": "final_answer", "arguments": "Hi"}' + + self.assertEqual(agent("Hello"), "Hi") + self.assertEqual(agent("Hello"), "Hi") + + # Test that results are stored in shared memory + self.assertEqual(len(agent.memory.get(None)), 2) + + # Test resetting shared memory + self.assertEqual(agent("Hello", reset=True), "Hi") + self.assertEqual(len(agent.memory.get(None)), 1) + + # Test session memory + self.assertEqual(agent("Hello", session="session-0"), "Hi") + self.assertEqual(len(agent.memory.get("session-0")), 1) + + # Test resetting session memory + self.assertEqual(agent("Hello", session="session-0", reset=True), "Hi") + self.assertEqual(len(agent.memory.get("session-0")), 1) + self.assertEqual(len(agent.memory.get(None)), 1) + + def testMethod(self): + """ + Test agent process methods + """ + + agent = Agent(method="code", llm="hf-internal-testing/tiny-random-LlamaForCausalLM", max_iterations=1) + self.assertIsInstance(agent.process, CodeAgent) + + def testSkill(self): + """ + Test running a skill from a skill.md file + """ + + skill = os.path.join(tempfile.gettempdir(), "skill.md") + with open(skill, "w", encoding="utf-8") as output: + output.write(SKILL) + + agent = Agent(tools=[skill], llm="hf-internal-testing/tiny-random-LlamaForCausalLM", max_iterations=1) + + self.assertIsInstance(agent.tools["hello"]("say hello"), str) + + def testToolsBasic(self): + """ + Test adding basic function tools + """ + + class DateTime: + """ + Date time instance + """ + + def __call__(self, iso): + """ + Gets the current date and time + + Args: + iso: date will be converted to iso format if True + + Returns: + current date and time + """ + + return datetime.today().isoformat() if iso else datetime.today() + + today = {"name": "today", "description": "Gets the current date and time", "target": DateTime()} + + def current(iso: str) -> str: + """ + Gets the current date and time + + Args: + iso: date will be converted to iso format if True + + Returns: + current date and time + """ + + return datetime.today().isoformat() if iso else datetime.today() + + agent = Agent(tools=[today, current, "websearch"], llm="hf-internal-testing/tiny-random-LlamaForCausalLM", max_steps=1) + + self.assertIsNotNone(agent) + self.assertIsInstance(agent.tools["today"](True), str) + self.assertIsInstance(agent.tools["current"](True), str) + + def testToolsDefaults(self): + """ + Test default toolkit tools + """ + + agent = Agent(tools=["defaults"], llm="hf-internal-testing/tiny-random-LlamaForCausalLM", max_steps=1) + + # Working directory + work = tempfile.gettempdir() + + # Test file + path = os.path.join(work, "agent_tools.txt") + agent.tools["write"](path, "hello world") + + # Test default tools + self.assertIsNotNone(agent.tools["bash"](["ls", work])) + self.assertGreater(len(agent.tools["glob"](work)), 0) + self.assertGreater(len(agent.tools["grep"]("world", "*")), 0) + self.assertEqual(agent.tools["todowrite"]("plan"), "plan") + + agent.tools["edit"](path, "hello", "goodbye") + self.assertEqual(agent.tools["read"](path), "goodbye world".strip()) + + def testToolsEmbeddings(self): + """ + Test adding Embeddings as a tool + """ + + embeddings = Embeddings() + embeddings.index(["test"]) + + # Generate temp file path and save + index = os.path.join(tempfile.gettempdir(), "embeddings.agent") + embeddings.save(index) + + embeddings1 = { + "name": "embeddings1", + "description": "Searches a test database", + "target": embeddings, + } + + embeddings2 = {"name": "embeddings2", "description": "Searches a test database", "path": index} + + agent = Agent(tools=[embeddings1, embeddings2], llm="hf-internal-testing/tiny-random-LlamaForCausalLM", max_steps=1) + + self.assertIsNotNone(agent) + self.assertIsInstance(agent.tools["embeddings1"]("test"), list) + + # pylint: disable=C0115,C0116 + @patch("mcpadapt.core.MCPAdapt") + def testToolsMCP(self, mcp): + """ + Test adding a MCP tool collection + """ + + class MCPAdapt: + def __init__(self, *args): + self.args = args + + def tools(self): + return [PythonInterpreterTool()] + + # Patch MCP adapter for testing + mcp.side_effect = MCPAdapt + + agent = Agent(tools=["http://localhost:8000/mcp"], llm="hf-internal-testing/tiny-random-LlamaForCausalLM", max_steps=1) + self.assertEqual(len(agent.tools), 2) diff --git a/test/python/testann/__init__.py b/test/python/testann/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/python/testann/testdense.py b/test/python/testann/testdense.py new file mode 100644 index 0000000..23ce586 --- /dev/null +++ b/test/python/testann/testdense.py @@ -0,0 +1,537 @@ +""" +Dense ANN module tests +""" + +import os +import platform +import sys +import tempfile +import unittest + +from unittest.mock import patch + +import numpy as np + +from txtai.ann import ANNFactory, ANN +from txtai.serialize import SerializeFactory + + +# pylint: disable=R0904 +class TestDense(unittest.TestCase): + """ + Dense ANN tests. + """ + + def testAnnoy(self): + """ + Test Annoy backend + """ + + self.runTests("annoy", None, False) + + def testAnnoyCustom(self): + """ + Test Annoy backend with custom settings + """ + + # Test with custom settings + self.runTests("annoy", {"annoy": {"ntrees": 2, "searchk": 1}}, False) + + def testCustomBackend(self): + """ + Test resolving a custom backend + """ + + self.runTests("txtai.ann.Faiss") + + def testCustomBackendNotFound(self): + """ + Test resolving an unresolvable backend + """ + + with self.assertRaises(ImportError): + ANNFactory.create({"backend": "notfound.ann"}) + + def testFaiss(self): + """ + Test Faiss backend + """ + + self.runTests("faiss") + + def testFaissBinary(self): + """ + Test Faiss backend with a binary hash index + """ + + ann = ANNFactory.create({"backend": "faiss", "quantize": 1, "dimensions": 240 * 8, "faiss": {"components": "BHash32"}}) + + # Generate and index dummy data + data = np.random.rand(100, 240).astype(np.uint8) + ann.index(data) + + # Generate query vector and test search + query = np.random.rand(240).astype(np.uint8) + self.assertGreater(ann.search(np.array([query]), 1)[0][0][1], 0) + + def testFaissCustom(self): + """ + Test Faiss backend with custom settings + """ + + # Test with custom settings + self.runTests("faiss", {"faiss": {"nprobe": 2, "components": "PCA16,IDMap,SQ8", "sample": 1.0}}, False) + self.runTests("faiss", {"faiss": {"components": "IVF,SQ8"}}, False) + + @patch("platform.system") + def testFaissMacOS(self, system): + """ + Test Faiss backend with macOS + """ + + # Run test + system.return_value = "Darwin" + + # pylint: disable=C0415, W0611 + # Force reload of class + name = "txtai.ann.dense.faiss" + module = sys.modules[name] + del sys.modules[name] + import txtai.ann.dense.faiss + + # Run tests + self.runTests("faiss") + + # Restore original module + sys.modules[name] = module + + @unittest.skipIf(os.name == "nt", "mmap not supported on Windows") + def testFaissMmap(self): + """ + Test Faiss backend with mmap enabled + """ + + # Test to with mmap enabled + self.runTests("faiss", {"faiss": {"mmap": True}}, False) + + def testGGML(self): + """ + Test GGML backend + """ + + self.runTests("ggml") + + def testGGMLQuantization(self): + """ + Test GGML backend with quantization enabled + """ + + ann = ANNFactory.create({"backend": "ggml", "ggml": {"quantize": "Q4_0"}}) + + # Generate and index dummy data + data = np.random.rand(100, 256).astype(np.float32) + ann.index(data) + + # Test save and load + index = os.path.join(tempfile.gettempdir(), "ggml.q4_0.v1") + ann.save(index) + ann.load(index) + + # Generate query vector and test search + query = np.random.rand(256).astype(np.float32) + self.normalize(query) + self.assertGreater(ann.search(np.array([query]), 1)[0][0][1], 0) + + # Validate count + self.assertEqual(ann.count(), 100) + + # Test delete + ann.delete([0]) + self.assertEqual(ann.count(), 99) + + # Save updated index with deletes and reload + index = os.path.join(tempfile.gettempdir(), "ggml.q4_0.v2") + ann.save(index) + ann.load(index) + ann.index(data) + + def testGGMLInvalid(self): + """ + Test invalid GGML configurations + """ + + data = np.random.rand(100, 240).astype(np.float32) + + with self.assertRaises(ValueError): + ann = ANNFactory.create({"backend": "ggml", "ggml": {"quantize": "NOEXIST", "gpu": False}}) + ann.index(data) + + with self.assertRaises(ValueError): + ann = ANNFactory.create({"backend": "ggml", "ggml": {"quantize": "Q4_K"}}) + ann.index(data) + + def testHnsw(self): + """ + Test Hnswlib backend + """ + + self.runTests("hnsw") + + def testHnswCustom(self): + """ + Test Hnswlib backend with custom settings + """ + + # Test with custom settings + self.runTests("hnsw", {"hnsw": {"efconstruction": 100, "m": 4, "randomseed": 0, "efsearch": 5}}) + + def testNotImplemented(self): + """ + Test exceptions for non-implemented methods + """ + + ann = ANN({}) + + self.assertRaises(NotImplementedError, ann.load, None) + self.assertRaises(NotImplementedError, ann.index, None) + self.assertRaises(NotImplementedError, ann.append, None) + self.assertRaises(NotImplementedError, ann.delete, None) + self.assertRaises(NotImplementedError, ann.search, None, None) + self.assertRaises(NotImplementedError, ann.count) + self.assertRaises(NotImplementedError, ann.save, None) + + def testNumPy(self): + """ + Test NumPy backend + """ + + self.runTests("numpy") + + @patch.dict(os.environ, {"ALLOW_PICKLE": "True"}) + def testNumPyLegacy(self): + """ + Test NumPy backend with legacy pickled data + """ + + serializer = SerializeFactory.create("pickle", allowpickle=True) + + # Create output directory + output = os.path.join(tempfile.gettempdir(), "ann.npy") + path = os.path.join(output, "embeddings") + os.makedirs(output, exist_ok=True) + + # Generate data and save as pickle + data = np.random.rand(100, 240).astype(np.float32) + serializer.save(data, path) + + ann = ANNFactory.create({"backend": "numpy"}) + ann.load(path) + + # Validate count + self.assertEqual(ann.count(), 100) + + def testNumPySafetensors(self): + """ + Test NumPy backend with safetensors storage + """ + + ann = ANNFactory.create({"backend": "numpy", "numpy": {"safetensors": True}}) + + # Generate and index dummy data + data = np.random.rand(100, 240).astype(np.float32) + ann.index(data) + + # Test save and load + index = os.path.join(tempfile.gettempdir(), "numpy.safetensors") + ann.save(index) + ann.load(index) + + # Generate query vector and test search + query = np.random.rand(240).astype(np.float32) + self.normalize(query) + self.assertGreater(ann.search(np.array([query]), 1)[0][0][1], 0) + + # Validate count + self.assertEqual(ann.count(), 100) + + @patch("sqlalchemy.orm.Query.limit") + def testPGVector(self, query): + """ + Test PGVector backend + """ + + # Generate test record + data = np.random.rand(1, 240).astype(np.float32) + + # Mock database query + query.return_value = [(x, -1.0) for x in range(data.shape[0])] + + configs = [ + ("full", {"dimensions": 240}, {}, data), + ("half", {"dimensions": 240}, {"precision": "half"}, data), + ("binary", {"quantize": 1, "dimensions": 240 * 8}, {}, data.astype(np.uint8)), + ] + + # Create ANN + for name, config, pgvector, data in configs: + path = os.path.join(tempfile.gettempdir(), f"pgvector.{name}.sqlite") + ann = ANNFactory.create( + {**{"backend": "pgvector", "pgvector": {**{"url": f"sqlite:///{path}", "schema": "txtai"}, **pgvector}}, **config} + ) + + # Test indexing + ann.index(data) + ann.append(data) + + # Validate search results + self.assertEqual(ann.search(data, 1), [[(0, 1.0)]]) + + # Validate save/load/delete + ann.save(None) + ann.load(None) + + # Validate count + self.assertEqual(ann.count(), 2) + + # Test delete + ann.delete([0]) + self.assertEqual(ann.count(), 1) + + # Close ANN + ann.close() + + @unittest.skipIf(platform.system() == "Darwin", "SQLite extensions not supported on macOS") + def testSQLite(self): + """ + Test SQLite backend + """ + + self.runTests("sqlite") + + @unittest.skipIf(platform.system() == "Darwin", "SQLite extensions not supported on macOS") + def testSQLiteCustom(self): + """ + Test SQLite backend with custom settings + """ + + # Test with custom settings + self.runTests("sqlite", {"sqlite": {"quantize": 1}}) + self.runTests("sqlite", {"sqlite": {"quantize": 8}}) + + # Test saving to a new path + model = self.backend("sqlite") + expected = model.count() - 1 + + # Test save variations + index = os.path.join(tempfile.gettempdir(), "ann.sqlite") + new = os.path.join(tempfile.gettempdir(), "ann.sqlite.new") + + # Save new + model.save(index) + + # Save to same path + model.save(index) + + # Delete id + model.delete([0]) + + # Save to another path + model.load(index) + model.save(new) + + self.assertEqual(model.count(), expected) + + def testTorch(self): + """ + Test Torch backend + """ + + self.runTests("torch") + + @unittest.skipIf(platform.system() == "Darwin", "Torch quantization not supported on macOS") + def testTorchQuantization(self): + """ + Test Torch backend with quantization enabled + """ + + for qtype in ["fp4", "nf4", "int8"]: + ann = ANNFactory.create({"backend": "torch", "torch": {"quantize": {"type": qtype}}}) + + # Generate and index dummy data + data = np.random.rand(100, 240).astype(np.float32) + ann.index(data) + + # Test save and load + index = os.path.join(tempfile.gettempdir(), f"{qtype}.safetensors") + ann.save(index) + ann.load(index) + + # Generate query vector and test search + query = np.random.rand(240).astype(np.float32) + self.normalize(query) + self.assertGreater(ann.search(np.array([query]), 1)[0][0][1], 0) + + # Validate count + self.assertEqual(ann.count(), 100) + + # Test delete + ann.delete([0]) + self.assertEqual(ann.count(), 99) + + def testTurboVec(self): + """ + Test turbovec backend + """ + + self.runTests("turbovec") + + def runTests(self, name, params=None, update=True): + """ + Runs a series of standard backend tests. + + Args: + name: backend name + params: additional config parameters + update: If append/delete options should be tested + """ + + self.assertEqual(self.backend(name, params).config["backend"], name) + self.assertEqual(self.save(name, params).count(), 10000) + + if update: + self.assertEqual(self.append(name, params, 500).count(), 10500) + self.assertEqual(self.delete(name, params, [0, 1]).count(), 9998) + self.assertEqual(self.delete(name, params, [100000]).count(), 10000) + + self.assertGreater(self.search(name, params), 0) + + def backend(self, name, params=None, length=10000): + """ + Test a backend. + + Args: + name: backend name + params: additional config parameters + length: number of rows to generate + + Returns: + ANN model + """ + + # Generate test data + data = np.random.rand(length, 240).astype(np.float32) + self.normalize(data) + + config = {"backend": name, "dimensions": data.shape[1]} + if params: + config.update(params) + + model = ANNFactory.create(config) + model.index(data) + + return model + + def append(self, name, params=None, length=500): + """ + Appends new data to index. + + Args: + name: backend name + params: additional config parameters + length: number of rows to generate + + Returns: + ANN model + """ + + # Initial model + model = self.backend(name, params) + + # Generate test data + data = np.random.rand(length, 240).astype(np.float32) + self.normalize(data) + + model.append(data) + + return model + + def delete(self, name, params=None, ids=None): + """ + Deletes data from index. + + Args: + name: backend name + params: additional config parameters + ids: ids to delete + + Returns: + ANN model + """ + + # Initial model + model = self.backend(name, params) + model.delete(ids) + + return model + + def save(self, name, params=None): + """ + Test save/load. + + Args: + name: backend name + params: additional config parameters + + Returns: + ANN model + """ + + model = self.backend(name, params) + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), "ann") + + # Save and close index + model.save(index) + model.close() + + # Reload index + model.load(index) + + return model + + def search(self, name, params=None): + """ + Test ANN search. + + Args: + name: backend name + params: additional config parameters + + Returns: + search results + """ + + # Generate ANN index + model = self.backend(name, params) + + # Generate query vector + query = np.random.rand(240).astype(np.float32) + self.normalize(query) + + # Ensure top result has similarity > 0 + return model.search(np.array([query]), 1)[0][0][1] + + def normalize(self, embeddings): + """ + Normalizes embeddings using L2 normalization. Operation applied directly on array. + + Args: + embeddings: input embeddings matrix + """ + + # Calculation is different for matrices vs vectors + if len(embeddings.shape) > 1: + embeddings /= np.linalg.norm(embeddings, axis=1)[:, np.newaxis] + else: + embeddings /= np.linalg.norm(embeddings) diff --git a/test/python/testann/testsparse.py b/test/python/testann/testsparse.py new file mode 100644 index 0000000..f3c4bcc --- /dev/null +++ b/test/python/testann/testsparse.py @@ -0,0 +1,165 @@ +""" +Sparse ANN module tests +""" + +import os +import tempfile +import unittest + +from unittest.mock import patch + +from scipy.sparse import random +from sklearn.preprocessing import normalize + +from txtai.ann import SparseANNFactory + + +class TestSparse(unittest.TestCase): + """ + Sparse ANN tests. + """ + + def testCustomBackend(self): + """ + Test resolving a custom backend + """ + + self.assertIsNotNone(SparseANNFactory.create({"backend": "txtai.ann.IVFSparse"})) + + def testCustomBackendNotFound(self): + """ + Test resolving an unresolvable backend + """ + + with self.assertRaises(ImportError): + SparseANNFactory.create({"backend": "notfound.ann"}) + + def testIVFSparse(self): + """ + Test IVFSparse backend + """ + + # Generate test record + insert = self.generate(500, 30522) + append = self.generate(500, 30522) + + # Count of records + count = insert.shape[0] + append.shape[0] + + # Create ANN + path = os.path.join(tempfile.gettempdir(), "ivfsparse") + ann = SparseANNFactory.create({"backend": "ivfsparse", "ivfsparse": {"nlist": 2, "nprobe": 2, "sample": 1.0}}) + + # Test indexing + ann.index(insert) + ann.append(append) + + # Validate search results + results = [x[0] for x in ann.search(insert[5], 10)[0]] + self.assertIn(5, results) + + # Validate save/load/delete + ann.save(path) + ann.load(path) + + # Validate count + self.assertEqual(ann.count(), count) + + # Test delete + ann.delete([0]) + self.assertEqual(ann.count(), count - 1) + + # Re-validate search results + results = [x[0] for x in ann.search(append[0], 10)[0]] + self.assertIn(insert.shape[0], results) + + # Close ANN + ann.close() + + # Test cluster pruning + ann = SparseANNFactory.create({"backend": "ivfsparse", "ivfsparse": {"nlist": 15, "nprobe": 1, "sample": 1.0}}) + ann.index(insert) + self.assertLess(len(ann.blocks), 15) + ann.close() + + def testIVFSparseTopnOverLimit(self): + """ + Test IVFSparse topn when limit exceeds the number of indexed documents + """ + + # Generate a small dataset (5 documents) + data = self.generate(5, 30522) + + ann = SparseANNFactory.create({"backend": "ivfsparse"}) + ann.index(data) + + # Search with limit (10) greater than document count (5) + results = ann.search(data[0], 10) + self.assertGreater(len(results[0]), 0) + + # Batch search with multiple queries exceeding document count + results = ann.search(data, 10) + self.assertEqual(len(results), data.shape[0]) + for result in results: + self.assertGreater(len(result), 0) + + ann.close() + + @patch("sqlalchemy.orm.Query.limit") + def testPGSparse(self, query): + """ + Test Sparse Postgres backend + """ + + # Generate test record + data = self.generate(1, 30522) + + # Mock database query + query.return_value = [(x, -1.0) for x in range(data.shape[0])] + + # Create ANN + path = os.path.join(tempfile.gettempdir(), "pgsparse.sqlite") + ann = SparseANNFactory.create({"backend": "pgsparse", "dimensions": 30522, "pgsparse": {"url": f"sqlite:///{path}", "schema": "txtai"}}) + + # Test indexing + ann.index(data) + ann.append(data) + + # Validate search results + self.assertEqual(ann.search(data, 1), [[(0, 1.0)]]) + + # Validate save/load/delete + ann.save(None) + ann.load(None) + + # Validate count + self.assertEqual(ann.count(), 2) + + # Test delete + ann.delete([0]) + self.assertEqual(ann.count(), 1) + + # Test > 1000 dimensions + data = random(1, 30522, format="csr", density=0.1) + ann.index(data) + self.assertEqual(ann.count(), 1) + + # Close ANN + ann.close() + + def generate(self, m, n): + """ + Generates random normalized sparse data. + + Args: + m, n: shape of the matrix + + Returns: + csr matrix + """ + + # Generate random csr matrix + data = random(m, n, format="csr") + + # Normalize and return + return normalize(data) diff --git a/test/python/testapi/__init__.py b/test/python/testapi/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/python/testapi/testapiagent.py b/test/python/testapi/testapiagent.py new file mode 100644 index 0000000..6ea7c46 --- /dev/null +++ b/test/python/testapi/testapiagent.py @@ -0,0 +1,91 @@ +""" +Agent API module tests +""" + +import os +import tempfile +import unittest + +from unittest.mock import patch + +from fastapi.testclient import TestClient + +from txtai.api import API, application + +# Configuration for agents +AGENTS = """ +agent: + test: + max_iterations: 1 + tools: + - name: testtool + description: Test tool + target: testapi.testapiagent.TestTool + +llm: + path: hf-internal-testing/tiny-random-LlamaForCausalLM +""" + + +# pylint: disable=R0904 +class TestAgent(unittest.TestCase): + """ + API tests for agents. + """ + + @staticmethod + @patch.dict(os.environ, {"CONFIG": os.path.join(tempfile.gettempdir(), "testapi.yml"), "API_CLASS": "txtai.api.API"}) + def start(): + """ + Starts a mock FastAPI client. + """ + + config = os.path.join(tempfile.gettempdir(), "testapi.yml") + + with open(config, "w", encoding="utf-8") as output: + output.write(AGENTS) + + # Create new application and set on client + application.app = application.create() + client = TestClient(application.app) + application.start() + + # Patch LLM to generate answer + agent = application.get().agents["test"] + agent.process.model.llm = lambda *args, **kwargs: 'Action:\n{"name": "final_answer", "arguments": "Hi"}' + + return client + + @classmethod + def setUpClass(cls): + """ + Create API client on creation of class. + """ + + cls.client = TestAgent.start() + + def testAgent(self): + """ + Test agent via API + """ + + results = self.client.post("agent", json={"name": "test", "text": "Hello"}).json() + self.assertEqual(results, "Hi") + + def testEmpty(self): + """ + Test empty API configuration + """ + + api = API({}) + + self.assertIsNone(api.agent("junk", "test")) + + +class TestTool: + """ + Class to test agent tools + """ + + def __call__(self): + pass diff --git a/test/python/testapi/testapiembeddings.py b/test/python/testapi/testapiembeddings.py new file mode 100644 index 0000000..c144ad6 --- /dev/null +++ b/test/python/testapi/testapiembeddings.py @@ -0,0 +1,502 @@ +""" +Embeddings API module tests +""" + +import os +import tempfile +import unittest +import urllib.parse + +from unittest.mock import patch + +from fastapi.testclient import TestClient + +from txtai.api import API, application + +# Configuration for a read/write embeddings index +INDEX = """ +# Index file path +path: %s + +# Allow indexing of documents +writable: True + +# Allow reindexing +reindex: True + +# Questions settings +questions: + path: distilbert-base-cased-distilled-squad + +# Embeddings settings +embeddings: + path: sentence-transformers/nli-mpnet-base-v2 + +# Extractor settings +extractor: + path: questions +""" + +# Configuration for a read-only embeddings index +READONLY = """ +# Index file path +path: %s + +# Allow indexing of documents +writable: False + +# Embeddings settings +embeddings: +""" + +# Configuration for an index with custom functions +FUNCTIONS = """ +# Ignore existing index +pathignore: %s + +# Allow indexing of documents +writable: True + +# Embeddings settings +embeddings: + path: sentence-transformers/nli-mpnet-base-v2 + content: True + functions: + - testapi.testapiembeddings.Elements + - name: length + argcount: 1 + function: testapi.testapiembeddings.length + - name: ann + function: ann + transform: testapi.testapiembeddings.transform +""" + +# Configuration for RAG +RAG = """ +# Ignore existing index +pathignore: %s + +# Allow indexing of documents +writable: True + +# Embeddings settings +embeddings: + path: sentence-transformers/nli-mpnet-base-v2 + content: True + +# LLM +llm: + path: hf-internal-testing/tiny-random-gpt2 + task: language-generation + +# RAG settings +rag: + path: llm + output: flatten +""" + +# Configuration for reindexing disabled +REINDEXDISABLED = """ +# Index file path +path: %s + +# Allow indexing of documents +writable: True + +# Embeddings settings +embeddings: + path: sentence-transformers/nli-mpnet-base-v2 + content: True +""" + +# Configuration for reranker +RERANK = """ +# Index file path +path: %s + +# Allow indexing of documents +writable: True + +# Embeddings settings +embeddings: + path: sentence-transformers/nli-mpnet-base-v2 + content: True + +# Similarity and Reranking settings +similarity: + path: neuml/colbert-bert-tiny + lateencode: True + +reranker: +""" + + +# pylint: disable=R0904 +class TestEmbeddings(unittest.TestCase): + """ + API tests for embeddings indices. + """ + + @staticmethod + @patch.dict(os.environ, {"CONFIG": os.path.join(tempfile.gettempdir(), "testapi.yml"), "API_CLASS": "txtai.api.API"}) + def start(yaml, path="testapi"): + """ + Starts a mock FastAPI client. + + Args: + yaml: input configuration + path: output path + """ + + config = os.path.join(tempfile.gettempdir(), "testapi.yml") + index = os.path.join(tempfile.gettempdir(), path) + + with open(config, "w", encoding="utf-8") as output: + output.write(yaml % index) + + # Create new application and set on client + application.app = application.create() + client = TestClient(application.app) + application.start() + + return client + + @classmethod + def setUpClass(cls): + """ + Create API client on creation of class. + """ + + cls.client = TestEmbeddings.start(INDEX, "testapi") + + cls.data = [ + "US tops 5 million confirmed virus cases", + "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg", + "Beijing mobilises invasion craft along coast as Taiwan tensions escalate", + "The National Park Service warns against sacrificing slower friends in a bear attack", + "Maine man wins $1M from $25 lottery ticket", + "Make huge profits without work, earn up to $100,000 a day", + ] + + # Index data + cls.client.post("add", json=[{"id": x, "text": row} for x, row in enumerate(cls.data)]) + cls.client.get("index") + + def testCount(self): + """ + Test count via API + """ + + self.assertEqual(self.client.get("count").json(), 6) + + def testDelete(self): + """ + Test delete via API + """ + + # Delete best match + ids = self.client.post("delete", json=[4]).json() + self.assertEqual(ids, [4]) + + # Search for best match + query = urllib.parse.quote("feel good story") + uid = self.client.get(f"search?query={query}&limit=1").json()[0]["id"] + + self.assertEqual(self.client.get("count").json(), 5) + self.assertEqual(uid, 5) + + # Reset data + self.client.post("add", json=[{"id": x, "text": row} for x, row in enumerate(self.data)]) + self.client.get("index") + + def testEmpty(self): + """ + Test empty API configuration + """ + + api = API({"writable": True}) + + self.assertIsNone(api.search("test", None)) + self.assertIsNone(api.batchsearch(["test"], None)) + self.assertIsNone(api.delete(["test"])) + self.assertIsNone(api.count()) + self.assertIsNone(api.similarity("test", ["test"])) + self.assertIsNone(api.batchsimilarity(["test"], ["test"])) + self.assertIsNone(api.explain("test")) + self.assertIsNone(api.batchexplain(["test"])) + self.assertIsNone(api.transform("test")) + self.assertIsNone(api.batchtransform(["test"])) + self.assertIsNone(api.extract(["test"], ["test"])) + + def testExtractor(self): + """ + Test qa extraction via API + """ + + data = [ + "Giants hit 3 HRs to down Dodgers", + "Giants 5 Dodgers 4 final", + "Dodgers drop Game 2 against the Giants, 5-4", + "Blue Jays beat Red Sox final score 2-1", + "Red Sox lost to the Blue Jays, 2-1", + "Blue Jays at Red Sox is over. Score: 2-1", + "Phillies win over the Braves, 5-0", + "Phillies 5 Braves 0 final", + "Final: Braves lose to the Phillies in the series opener, 5-0", + "Lightning goaltender pulled, lose to Flyers 4-1", + "Flyers 4 Lightning 1 final", + "Flyers win 4-1", + ] + + questions = ["What team won the game?", "What was score?"] + + # pylint: disable=C3001 + execute = lambda query: self.client.post( + "extract", + json={"queue": [{"name": question, "query": query, "question": question, "snippet": False} for question in questions], "texts": data}, + ).json() + + answers = execute("Red Sox - Blue Jays") + self.assertEqual("Blue Jays", answers[0]["answer"]) + self.assertEqual("2-1", answers[1]["answer"]) + + # Ad-hoc questions + question = "What hockey team won?" + + answers = self.client.post( + "extract", json={"queue": [{"name": question, "query": question, "question": question, "snippet": False}], "texts": data} + ).json() + self.assertEqual("Flyers", answers[0]["answer"]) + + def testReindex(self): + """ + Test reindex via API + """ + + # Reindex data + self.client.post("reindex", json={"config": {"path": "sentence-transformers/nli-mpnet-base-v2"}}) + + # Search for best match + query = urllib.parse.quote("feel good story") + uid = self.client.get(f"search?query={query}&limit=1").json()[0]["id"] + + self.assertEqual(uid, 4) + + # Reset data + self.client.post("add", json=[{"id": x, "text": row} for x, row in enumerate(self.data)]) + self.client.get("index") + + def testSearch(self): + """ + Test search via API + """ + + query = urllib.parse.quote("feel good story") + uid = self.client.get(f"search?query={query}&limit=1").json()[0]["id"] + self.assertEqual(uid, 4) + + def testSearchBatch(self): + """ + Test batch search via API + """ + + results = self.client.post("batchsearch", json={"queries": ["feel good story", "climate change"], "limit": 1}).json() + + uids = [result[0]["id"] for result in results] + self.assertEqual(uids, [4, 1]) + + def testSimilarity(self): + """ + Test similarity via API + """ + + uid = self.client.post("similarity", json={"query": "feel good story", "texts": self.data}).json()[0]["id"] + + self.assertEqual(uid, 4) + + def testSimilarityBatch(self): + """ + Test batch similarity via API + """ + + results = self.client.post("batchsimilarity", json={"queries": ["feel good story", "climate change"], "texts": self.data}).json() + + uids = [result[0]["id"] for result in results] + self.assertEqual(uids, [4, 1]) + + def testTransform(self): + """ + Test embeddings transform via API + """ + + self.assertEqual(len(self.client.get("transform?text=testembed").json()), 768) + + def testTransformBatch(self): + """ + Test batch embeddings transform via API + """ + + embeddings = self.client.post("batchtransform", json=self.data).json() + + self.assertEqual(len(embeddings), len(self.data)) + self.assertEqual(len(embeddings[0]), 768) + + def testUpsert(self): + """ + Test upsert via API + """ + + # Update data + self.client.post("add", json=[{"id": 0, "text": "Feel good story: baby panda born"}]) + self.client.get("upsert") + + # Search for best match + query = urllib.parse.quote("feel good story") + uid = self.client.get(f"search?query={query}&limit=1").json()[0]["id"] + + self.assertEqual(uid, 0) + + # Reset data + self.client.post("add", json=[{"id": x, "text": row} for x, row in enumerate(self.data)]) + self.client.get("index") + + def testViewOnly(self): + """ + Test read-only API instance + """ + + # Re-create application with a read-only index + self.client = TestEmbeddings.start(READONLY) + + # Test search + query = urllib.parse.quote("feel good story") + uid = self.client.get(f"search?query={query}&limit=1").json()[0]["id"] + self.assertEqual(uid, 4) + + # Test similarity + uid = self.client.post("similarity", json={"query": "feel good story", "texts": self.data}).json()[0]["id"] + self.assertEqual(uid, 4) + + # Test errors raised for write operations + self.assertEqual(self.client.post("add", json=[{"id": 0, "text": "test"}]).status_code, 403) + self.assertEqual(self.client.get("index").status_code, 403) + self.assertEqual(self.client.get("upsert").status_code, 403) + self.assertEqual(self.client.post("delete", json=[0]).status_code, 403) + self.assertEqual(self.client.post("reindex", json={"config": {"path": "sentence-transformers/nli-mpnet-base-v2"}}).status_code, 403) + + def testXFunctions(self): + """ + Test API instance with custom functions + """ + + # Re-create application with custom functions + self.client = TestEmbeddings.start(FUNCTIONS) + + # Index data + self.client.post("add", json=[{"id": x, "text": row} for x, row in enumerate(self.data)]) + self.client.get("index") + + query = urllib.parse.quote("select elements('text') length from txtai limit 1") + self.assertEqual(self.client.get(f"search?query={query}").json()[0]["length"], 4) + + query = urllib.parse.quote("select length('text') length from txtai limit 1") + self.assertEqual(self.client.get(f"search?query={query}").json()[0]["length"], 4) + + def testXPlain(self): + """ + Test API instance with explain methods + """ + + results = self.client.post("explain", json={"query": "feel good story", "limit": 1}).json() + + self.assertEqual(results[0]["text"], self.data[4]) + self.assertIsNotNone(results[0].get("tokens")) + + def testXPlainBatch(self): + """ + Test batch query explain via API + """ + + results = self.client.post("batchexplain", json={"queries": ["feel good story", "climate change"], "limit": 1}).json() + + text = [result[0]["text"] for result in results] + self.assertEqual(text, [self.data[4], self.data[1]]) + self.assertIsNotNone(results[0][0].get("tokens")) + + def testXRAG(self): + """ + Test RAG via API + """ + + # Re-create application with a RAG pipeline + self.client = TestEmbeddings.start(RAG) + + # Index data + self.client.post("add", json=[{"id": x, "text": row} for x, row in enumerate(self.data)]) + self.client.get("index") + + response = self.client.get("rag?query=bear").json() + self.assertIsInstance(response, str) + + response = self.client.post("batchrag", json={"queries": ["bear", "bear"]}).json() + self.assertEqual(len(response), 2) + + def testXReindexDisabled(self): + """ + Test reindexing is disabled + """ + + # Re-create application with reindexing disabled + self.client = TestEmbeddings.start(REINDEXDISABLED, "testapi-reindex") + + # Index data + self.client.post("add", json=[{"id": x, "text": row} for x, row in enumerate(self.data)]) + self.client.get("index") + + # Assert error raised + self.assertEqual(self.client.post("reindex", json={"config": {"path": "sentence-transformers/nli-mpnet-base-v2"}}).status_code, 403) + + def testXRerank(self): + """ + Test rerank via API + """ + + # Re-create application with a reranker pipeline + self.client = TestEmbeddings.start(RERANK, "testapi-rerank") + + # Index data + self.client.post("add", json=[{"id": x, "text": row} for x, row in enumerate(self.data)]) + self.client.get("index") + + uid = self.client.get("rerank?query=bear").json()[0]["id"] + self.assertEqual(uid, "3") + + results = self.client.post("batchrerank", json={"queries": ["bear", "bear"]}).json() + + uids = [result[0]["id"] for result in results] + self.assertEqual(uids, ["3", "3"]) + + +class Elements: + """ + Custom SQL function as callable object. + """ + + def __call__(self, text): + return length(text) + + +def transform(document): + """ + Custom transform function. + """ + + return document + + +def length(text): + """ + Custom SQL function. + """ + + return len(text) diff --git a/test/python/testapi/testapipipeline.py b/test/python/testapi/testapipipeline.py new file mode 100644 index 0000000..8bf3fb2 --- /dev/null +++ b/test/python/testapi/testapipipeline.py @@ -0,0 +1,391 @@ +""" +Pipeline API module tests +""" + +import os +import tempfile +import unittest +import urllib + +from unittest.mock import patch + +from fastapi.testclient import TestClient + +from txtai.api import API, application + +# pylint: disable=C0411 +from utils import Utils + +# Configuration for pipelines +PIPELINES = """ +# Image captions +caption: + +# Entity extraction +entity: + path: dslim/bert-base-NER + +# Extractor settings +extractor: + similarity: similarity + path: llm + +# Label settings +labels: + path: prajjwal1/bert-medium-mnli + +# LLM settings +llm: + path: hf-internal-testing/tiny-random-gpt2 + task: language-generation + +# Image objects +objects: + +# Text segmentation +segmentation: + sentences: true + +# Enable pipeline similarity backed by zero shot classifier +similarity: + +# Summarization +summary: + path: t5-small + +# Tabular +tabular: + +# Text extraction +textractor: + safeopen: /tmp/txtai + +# Text to speech +texttospeech: + +# Transcription +transcription: + +# Translation: +translation: + +# Enable file uploads +upload: +""" + + +# pylint: disable=R0904 +class TestPipeline(unittest.TestCase): + """ + API tests for pipelines. + """ + + @staticmethod + @patch.dict(os.environ, {"CONFIG": os.path.join(tempfile.gettempdir(), "testapi.yml"), "API_CLASS": "txtai.api.API"}) + def start(): + """ + Starts a mock FastAPI client. + """ + + config = os.path.join(tempfile.gettempdir(), "testapi.yml") + + with open(config, "w", encoding="utf-8") as output: + output.write(PIPELINES) + + # Create new application and set on client + application.app = application.create() + client = TestClient(application.app) + application.start() + + return client + + @classmethod + def setUpClass(cls): + """ + Create API client on creation of class. + """ + + cls.client = TestPipeline.start() + + cls.data = [ + "US tops 5 million confirmed virus cases", + "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg", + "Beijing mobilises invasion craft along coast as Taiwan tensions escalate", + "The National Park Service warns against sacrificing slower friends in a bear attack", + "Maine man wins $1M from $25 lottery ticket", + "Make huge profits without work, earn up to $100,000 a day", + ] + + cls.text = ( + "Search is the base of many applications. Once data starts to pile up, users want to be able to find it. It's the foundation " + "of the internet and an ever-growing challenge that is never solved or done. The field of Natural Language Processing (NLP) is " + "rapidly evolving with a number of new developments. Large-scale general language models are an exciting new capability " + "allowing us to add amazing functionality quickly with limited compute and people. Innovation continues with new models " + "and advancements coming in at what seems a weekly basis. This article introduces txtai, an AI-powered search engine " + "that enables Natural Language Understanding (NLU) based search in any application." + ) + + # Create invalid test file + directory = os.path.join(tempfile.gettempdir(), "txtai-1") + os.makedirs(directory, exist_ok=True) + with open(os.path.join(directory, "test"), "w", encoding="utf-8") as output: + output.write("123") + + def testCaption(self): + """ + Test caption via API + """ + + caption = self.client.get(f"caption?file={Utils.PATH}/books.jpg").json() + + self.assertEqual(caption, "a book shelf filled with books and a stack of books") + + def testCaptionBatch(self): + """ + Test batch caption via API + """ + + path = Utils.PATH + "/books.jpg" + + captions = self.client.post("batchcaption", json=[path, path]).json() + self.assertEqual(captions, ["a book shelf filled with books and a stack of books"] * 2) + + def testEntity(self): + """ + Test entity extraction via API + """ + + entities = self.client.get(f"entity?text={self.data[1]}").json() + self.assertEqual([e[0] for e in entities], ["Canada", "Manhattan"]) + + def testEntityBatch(self): + """ + Test batch entity via API + """ + + entities = self.client.post("batchentity", json=[self.data[1]]).json() + self.assertEqual([e[0] for e in entities[0]], ["Canada", "Manhattan"]) + + def testEmpty(self): + """ + Test empty API configuration + """ + + api = API({}) + + self.assertIsNone(api.label("test", ["test"])) + self.assertIsNone(api.pipeline("junk", "test")) + + def testLabel(self): + """ + Test label via API + """ + + labels = self.client.post("label", json={"text": "this is the best sentence ever", "labels": ["positive", "negative"]}).json() + + self.assertEqual(labels[0]["id"], 0) + + def testLabelBatch(self): + """ + Test batch label via API + """ + + labels = self.client.post( + "batchlabel", json={"texts": ["this is the best sentence ever", "This is terrible"], "labels": ["positive", "negative"]} + ).json() + + results = [l[0]["id"] for l in labels] + self.assertEqual(results, [0, 1]) + + def testLLM(self): + """ + Test LLM inference via API + """ + + response = self.client.get("llm?text=test").json() + self.assertIsInstance(response, str) + + def testLLMBatch(self): + """ + Test batch LLM inference via API + """ + + response = self.client.post("batchllm", json={"texts": ["test", "test"]}).json() + self.assertEqual(len(response), 2) + + def testObjects(self): + """ + Test objects via API + """ + + objects = self.client.get(f"objects?file={Utils.PATH}/books.jpg").json() + + self.assertEqual(objects[0][0], "book") + + def testObjectsBatch(self): + """ + Test batch objects via API + """ + + path = Utils.PATH + "/books.jpg" + + objects = self.client.post("batchobjects", json=[path, path]).json() + self.assertEqual([o[0][0] for o in objects], ["book"] * 2) + + def testSegment(self): + """ + Test segmentation via API + """ + + text = self.client.get("segment?text=This is a test. And another test.").json() + + # Check array length is 2 + self.assertEqual(len(text), 2) + + def testSegmentBatch(self): + """ + Test batch segmentation via API + """ + + text = "This is a test. And another test." + texts = self.client.post("batchsegment", json=[text, text]).json() + + # Check array length is 2 and first element length is 2 + self.assertEqual(len(texts), 2) + self.assertEqual(len(texts[0]), 2) + + def testSimilarity(self): + """ + Test similarity via API + """ + + uid = self.client.post("similarity", json={"query": "feel good story", "texts": self.data}).json()[0]["id"] + + self.assertEqual(self.data[uid], self.data[4]) + + def testSimilarityBatch(self): + """ + Test batch similarity via API + """ + + results = self.client.post("batchsimilarity", json={"queries": ["feel good story", "climate change"], "texts": self.data}).json() + + uids = [result[0]["id"] for result in results] + self.assertEqual(uids, [4, 1]) + + def testSummary(self): + """ + Test summary via API + """ + + summary = self.client.get(f"summary?text={urllib.parse.quote(self.text)}&minlength=15&maxlength=15").json() + self.assertEqual(summary, "the field of natural language processing (NLP) is rapidly evolving") + + def testSummaryBatch(self): + """ + Test batch summary via API + """ + + summaries = self.client.post("batchsummary", json={"texts": [self.text, self.text], "minlength": 15, "maxlength": 15}).json() + self.assertEqual(summaries, ["the field of natural language processing (NLP) is rapidly evolving"] * 2) + + def testTabular(self): + """ + Test tabular via API + """ + + results = self.client.get(f"tabular?file={Utils.PATH}/tabular.csv").json() + + # Check length of results is as expected + self.assertEqual(len(results), 6) + + def testTabularBatch(self): + """ + Test batch tabular via API + """ + + path = Utils.PATH + "/tabular.csv" + + results = self.client.post("batchtabular", json=[path, path]).json() + self.assertEqual((len(results[0]), len(results[1])), (6, 6)) + + def testTextractor(self): + """ + Test textractor via API + """ + + text = self.client.get(f"textract?file={Utils.PATH}/article.pdf").json() + + # Check length of text is as expected + self.assertEqual(len(text), 2471) + + # Check invalid URLs + for url in ["http://192.168.1.1/path", "http://127.0.0.1/path", "http://invalid", "/etc/config", "/tmp/txtai-1/test"]: + with self.assertRaises(IOError): + self.client.get(f"textract?file={url}").json() + + def testTextractorBatch(self): + """ + Test batch textractor via API + """ + + path = Utils.PATH + "/article.pdf" + + texts = self.client.post("batchtextract", json=[path, path]).json() + self.assertEqual((len(texts[0]), len(texts[1])), (2471, 2471)) + + def testTextToSpeech(self): + """ + Test text to speech + """ + + # Generate audio and check for WAV signature + audio = self.client.get("texttospeech?text=hello&encoding=wav").content + self.assertTrue(audio[0:4] == b"RIFF") + + def testTranscribe(self): + """ + Test transcribe via API + """ + + text = self.client.get(f"transcribe?file={Utils.PATH}/Make_huge_profits.wav").json() + + # Check length of text is as expected + self.assertEqual(text, "Make huge profits without working make up to one hundred thousand dollars a day") + + def testTranscribeBatch(self): + """ + Test batch transcribe via API + """ + + path = Utils.PATH + "/Make_huge_profits.wav" + + texts = self.client.post("batchtranscribe", json=[path, path]).json() + self.assertEqual(texts, ["Make huge profits without working make up to one hundred thousand dollars a day"] * 2) + + def testTranslate(self): + """ + Test translate via API + """ + + translation = self.client.get(f"translate?text={urllib.parse.quote('This is a test translation into Spanish')}&target=es").json() + self.assertEqual(translation, "Esta es una traducción de prueba al español") + + def testTranslateBatch(self): + """ + Test batch translate via API + """ + + text = "This is a test translation into Spanish" + translations = self.client.post("batchtranslate", json={"texts": [text, text], "target": "es"}).json() + self.assertEqual(translations, ["Esta es una traducción de prueba al español"] * 2) + + def testUpload(self): + """ + Test file upload + """ + + path = Utils.PATH + "/article.pdf" + with open(path, "rb") as f: + path = self.client.post("upload", files={"files": f}).json()[0] + self.assertTrue(os.path.exists(path)) diff --git a/test/python/testapi/testapiworkflow.py b/test/python/testapi/testapiworkflow.py new file mode 100644 index 0000000..0bcf528 --- /dev/null +++ b/test/python/testapi/testapiworkflow.py @@ -0,0 +1,259 @@ +""" +Workflow API module tests +""" + +import json +import os +import tempfile +import unittest + +from http.server import HTTPServer, BaseHTTPRequestHandler +from multiprocessing.pool import ThreadPool +from threading import Thread +from unittest.mock import patch + +from fastapi.testclient import TestClient + +from txtai.api import API, application + +# Configuration for workflows +WORKFLOWS = """ +# Embeddings index +writable: true +embeddings: + path: sentence-transformers/nli-mpnet-base-v2 + +# Labels +labels: + path: prajjwal1/bert-medium-mnli + +nop: + +# Text segmentation +segmentation: + sentences: true + +# Workflow definitions +workflow: + labels: + tasks: + - action: labels + args: [[positive, negative]] + multiaction: + tasks: + - action: + - labels + - nop + initialize: testapi.testapiworkflow.TestInitFinal + finalize: testapi.testapiworkflow.TestInitFinal + merge: concat + args: + - [[positive, negative], false, True] + - null + schedule: + schedule: + cron: '* * * * * *' + elements: + - This is a test sentence. And another sentence to split. + iterations: 1 + tasks: + - action: segmentation + segment: + tasks: + - action: segmentation + - action: index + get: + tasks: + - task: service + url: http://127.0.0.1:8001/testget + method: get + params: + text: + post: + tasks: + - task: service + url: http://127.0.0.1:8001/testpost + params: + + xml: + tasks: + - task: service + url: http://127.0.0.1:8001/xml + method: get + batch: false + extract: row + params: + text: +""" + + +class RequestHandler(BaseHTTPRequestHandler): + """ + Test HTTP handler. + """ + + def do_GET(self): + """ + GET request handler. + """ + + self.send_response(200) + + if self.path.startswith("/xml"): + response = "test".encode("utf-8") + mime = "application/xml" + else: + response = '[{"text": "test"}]'.encode("utf-8") + mime = "application/json" + + self.send_header("content-type", mime) + self.send_header("content-length", len(response)) + self.end_headers() + + self.wfile.write(response) + self.wfile.flush() + + def do_POST(self): + """ + POST request handler. + """ + + length = int(self.headers["content-length"]) + data = json.loads(self.rfile.read(length)) + + response = json.dumps([[y for y in x.split(".") if y] for x in data]).encode("utf-8") + + self.send_response(200) + self.send_header("content-type", "application/json") + self.send_header("content-length", len(response)) + self.end_headers() + + self.wfile.write(response) + self.wfile.flush() + + +class TestWorkflow(unittest.TestCase): + """ + API tests for workflows. + """ + + @staticmethod + @patch.dict(os.environ, {"CONFIG": os.path.join(tempfile.gettempdir(), "testapi.yml"), "API_CLASS": "txtai.api.API"}) + def start(): + """ + Starts a mock FastAPI client. + """ + + config = os.path.join(tempfile.gettempdir(), "testapi.yml") + + with open(config, "w", encoding="utf-8") as output: + output.write(WORKFLOWS) + + # Create new application and set on client + application.app = application.create() + client = TestClient(application.app) + application.start() + + return client + + @classmethod + def setUpClass(cls): + """ + Create API client on creation of class. + """ + + cls.client = TestWorkflow.start() + + cls.httpd = HTTPServer(("127.0.0.1", 8001), RequestHandler) + + server = Thread(target=cls.httpd.serve_forever, daemon=True) + server.start() + + @classmethod + def tearDownClass(cls): + """ + Shutdown mock http server. + """ + + cls.httpd.shutdown() + + def testAPICleanup(self): + """ + Test API threadpool closed when __del__ called. + """ + + api = API({}) + api.pool = ThreadPool() + + # pylint: disable=C2801 + api.__del__() + + self.assertIsNone(api.pool) + + def testServiceGet(self): + """ + Test workflow with ServiceTask GET via API + """ + + text = "This is a test sentence. And another sentence to split." + results = self.client.post("workflow", json={"name": "get", "elements": [text]}).json() + + self.assertEqual(len(results), 1) + self.assertEqual(len(results[0]), 1) + + def testServicePost(self): + """ + Test workflow with ServiceTask POST via API + """ + + text = "This is a test sentence. And another sentence to split." + results = self.client.post("workflow", json={"name": "post", "elements": [text]}).json() + + self.assertEqual(len(results), 1) + self.assertEqual(len(results[0]), 2) + + def testServiceXml(self): + """ + Test workflow with ServiceTask GET via API and XML response + """ + + text = "This is a test sentence. And another sentence to split." + results = self.client.post("workflow", json={"name": "xml", "elements": [text]}).json() + + self.assertEqual(len(results), 1) + self.assertEqual(len(results[0]), 1) + + def testWorkflowLabels(self): + """ + Test workflow with labels via API + """ + + text = "This is the best" + + results = self.client.post("workflow", json={"name": "labels", "elements": [text]}).json() + self.assertEqual(results[0][0], 0) + + results = self.client.post("workflow", json={"name": "multiaction", "elements": [text]}).json() + self.assertEqual(results[0], "['positive']. This is the best") + + def testWorkflowSegment(self): + """ + Test workflow with segmentation via API + """ + + text = "This is a test sentence. And another sentence to split." + + results = self.client.post("workflow", json={"name": "segment", "elements": [text]}).json() + self.assertEqual(len(results), 2) + + results = self.client.post("workflow", json={"name": "segment", "elements": [[0, text]]}).json() + self.assertEqual(len(results), 2) + + +class TestInitFinal: + """ + Class to test task initialize and finalize calls. + """ + + def __call__(self): + pass diff --git a/test/python/testapi/testauthorization.py b/test/python/testapi/testauthorization.py new file mode 100644 index 0000000..7be9b14 --- /dev/null +++ b/test/python/testapi/testauthorization.py @@ -0,0 +1,73 @@ +""" +Authorization module tests +""" + +import hashlib +import os +import tempfile +import unittest + +from unittest.mock import patch + +from fastapi.testclient import TestClient + +from txtai.api import application + + +class TestAuthorization(unittest.TestCase): + """ + API tests for token authorization. + """ + + @staticmethod + @patch.dict( + os.environ, + { + "CONFIG": os.path.join(tempfile.gettempdir(), "testapi.yml"), + "DEPENDENCIES": "txtai.api.Authorization", + "TOKEN": hashlib.sha256("token".encode("utf-8")).hexdigest(), + }, + ) + def start(): + """ + Starts a mock FastAPI client. + """ + + config = os.path.join(tempfile.gettempdir(), "testapi.yml") + + with open(config, "w", encoding="utf-8") as output: + output.write("embeddings:\n") + + # Create new application and set on client + application.app = application.create() + client = TestClient(application.app) + application.start() + + return client + + @classmethod + def setUpClass(cls): + """ + Create API client on creation of class. + """ + + cls.client = TestAuthorization.start() + + def testInvalid(self): + """ + Test invalid authorization + """ + + response = self.client.get("search?query=test") + self.assertEqual(response.status_code, 401) + + response = self.client.get("search?query=test", headers={"Authorization": "Bearer invalid"}) + self.assertEqual(response.status_code, 401) + + def testValid(self): + """ + Test valid authorization + """ + + results = self.client.get("search?query=test", headers={"Authorization": "Bearer token"}).json() + self.assertEqual(results, []) diff --git a/test/python/testapi/testcluster.py b/test/python/testapi/testcluster.py new file mode 100644 index 0000000..1510821 --- /dev/null +++ b/test/python/testapi/testcluster.py @@ -0,0 +1,261 @@ +""" +Cluster API module tests +""" + +import json +import os +import tempfile +import unittest +import urllib.parse + +from http.server import HTTPServer, BaseHTTPRequestHandler +from threading import Thread +from unittest.mock import patch + +from fastapi.testclient import TestClient + +from txtai.api import application + +# Configuration for an embeddings cluster +CLUSTER = """ +cluster: + shards: + - http://127.0.0.1:8002 + - http://127.0.0.1:8003 +""" + + +class RequestHandler(BaseHTTPRequestHandler): + """ + Test HTTP handler. + """ + + def do_GET(self): + """ + GET request handler. + """ + + if self.path == "/count": + response = 26 + elif self.path.startswith("/search?query=select"): + if "group+by+id" in self.path: + response = [{"count(*)": 26}] + elif "group+by+text" in self.path: + response = [{"count(*)": 12, "text": "This is a test"}, {"count(*)": 14, "text": "And another test"}] + elif "group+by+txt" in self.path: + response = [{"count(*)": 12, "txt": "This is a test"}, {"count(*)": 14, "txt": "And another test"}] + else: + if self.server.server_port == 8002: + response = [{"count(*)": 12, "min(indexid)": 0, "max(indexid)": 11, "avg(indexid)": 6.3}] + else: + response = [{"count(*)": 16, "min(indexid)": 2, "max(indexid)": 14, "avg(indexid)": 6.7}] + elif self.path.startswith("/search"): + response = [{"id": 4, "score": 0.40}] + else: + response = {"result": "ok"} + + # Convert response to string + response = json.dumps(response).encode("utf-8") + + self.send_response(200) + self.send_header("content-type", "application/json") + self.send_header("content-length", len(response)) + self.end_headers() + + self.wfile.write(response) + self.wfile.flush() + + def do_POST(self): + """ + POST request handler. + """ + + if self.path.startswith("/batchsearch"): + response = [[{"id": 4, "score": 0.40}], [{"id": 1, "score": 0.40}]] + elif self.path.startswith("/delete"): + if self.server.server_port == 8002: + response = [0] + else: + response = [] + else: + response = {"result": "ok"} + + response = json.dumps(response).encode("utf-8") + + self.send_response(200) + self.send_header("content-type", "application/json") + self.send_header("content-length", len(response)) + self.end_headers() + + self.wfile.write(response) + self.wfile.flush() + + +@unittest.skipIf(os.name == "nt", "TestCluster skipped on Windows") +class TestCluster(unittest.TestCase): + """ + API tests for embeddings clusters + """ + + @staticmethod + @patch.dict(os.environ, {"CONFIG": os.path.join(tempfile.gettempdir(), "testapi.yml"), "API_CLASS": "txtai.api.API"}) + def start(): + """ + Starts a mock FastAPI client. + """ + + config = os.path.join(tempfile.gettempdir(), "testapi.yml") + + with open(config, "w", encoding="utf-8") as output: + output.write(CLUSTER) + + # Create new application and set on client + application.app = application.create() + client = TestClient(application.app) + application.start() + + return client + + @classmethod + def setUpClass(cls): + """ + Create API client on creation of class. + """ + + cls.client = TestCluster.start() + + cls.httpd1 = HTTPServer(("127.0.0.1", 8002), RequestHandler) + + server1 = Thread(target=cls.httpd1.serve_forever, daemon=True) + server1.start() + + cls.httpd2 = HTTPServer(("127.0.0.1", 8003), RequestHandler) + + server2 = Thread(target=cls.httpd2.serve_forever, daemon=True) + server2.start() + + # Index data + cls.client.post("add", json=[{"id": 0, "text": "test"}]) + cls.client.get("index") + + @classmethod + def tearDownClass(cls): + """ + Shutdown mock http server. + """ + + cls.httpd1.shutdown() + cls.httpd2.shutdown() + + def testCount(self): + """ + Test cluster count + """ + + self.assertEqual(self.client.get("count").json(), 52) + + def testDelete(self): + """ + Test cluster delete + """ + + self.assertEqual(self.client.post("delete", json=[0]).json(), [0]) + + def testDeleteString(self): + """ + Test cluster delete with string id + """ + + self.assertEqual(self.client.post("delete", json=["0"]).json(), [0]) + + def testIds(self): + """ + Test id configurations + """ + + # String ids + self.client.post("add", json=[{"id": "0", "text": "test"}]) + self.assertEqual(self.client.get("index").status_code, 200) + + # Auto ids + self.client.post("add", json=[{"text": "test"}]) + self.assertEqual(self.client.get("index").status_code, 200) + + def testReindex(self): + """ + Test cluster reindex + """ + + self.assertEqual(self.client.post("reindex", json={"config": {"path": "sentence-transformers/nli-mpnet-base-v2"}}).status_code, 200) + + def testSearch(self): + """ + Test cluster search + """ + + # Encode parameters + params = json.dumps({"x": 1}) + + query = urllib.parse.quote("feel good story") + uid = self.client.get(f"search?query={query}&limit=1&weights=0.5&index=default¶meters={params}&graph=False").json()[0]["id"] + self.assertEqual(uid, 4) + + def testSearchBatch(self): + """ + Test cluster batch search + """ + + results = self.client.post( + "batchsearch", + json={ + "queries": ["feel good story", "climate change"], + "limit": 1, + "weights": 0.5, + "index": "default", + "parameters": [{"x": 1}, {"x": 2}], + "graph": False, + }, + ).json() + + uids = [result[0]["id"] for result in results] + self.assertEqual(uids, [4, 1]) + + def testSQL(self): + """ + Test cluster SQL statement + """ + + query = urllib.parse.quote("select count(*), min(indexid), max(indexid), avg(indexid) from txtai where text='This is a test'") + self.assertEqual( + self.client.get(f"search?query={query}").json(), [{"count(*)": 28, "min(indexid)": 0, "max(indexid)": 14, "avg(indexid)": 6.5}] + ) + + query = urllib.parse.quote("select count(*), text txt from txtai group by txt order by count(*) desc") + self.assertEqual( + self.client.get(f"search?query={query}").json(), + [{"count(*)": 28, "txt": "And another test"}, {"count(*)": 24, "txt": "This is a test"}], + ) + + query = urllib.parse.quote("select count(*), text from txtai group by text order by count(*) asc") + self.assertEqual( + self.client.get(f"search?query={query}").json(), + [{"count(*)": 24, "text": "This is a test"}, {"count(*)": 28, "text": "And another test"}], + ) + + query = urllib.parse.quote("select count(*) from txtai group by id order by count(*)") + self.assertEqual(self.client.get(f"search?query={query}").json(), [{"count(*)": 52}]) + + def testUpsert(self): + """ + Test cluster upsert + """ + + # Update data + self.client.post("add", json=[{"id": 4, "text": "Feel good story: baby panda born"}]) + self.client.get("upsert") + + # Search for best match + query = "feel good story" + uid = self.client.get(f"search?query={query}&limit=1").json()[0]["id"] + + self.assertEqual(uid, 4) diff --git a/test/python/testapi/testencoding.py b/test/python/testapi/testencoding.py new file mode 100644 index 0000000..1b7cfe9 --- /dev/null +++ b/test/python/testapi/testencoding.py @@ -0,0 +1,163 @@ +""" +Encoding module tests +""" + +import base64 +import os +import tempfile +import unittest +import urllib.parse + +from io import BytesIO +from unittest.mock import patch + +import msgpack +import numpy as np +import PIL + +from fastapi.testclient import TestClient + +from txtai.api import application +from txtai.api.responses import JSONEncoder + +# pylint: disable=C0411 +from utils import Utils + +# Configuration for image storage +INDEX = """ +# Allow indexing of documents +writable: %s + +embeddings: + defaults: False + content: True + objects: %s +""" + + +class TestEncoding(unittest.TestCase): + """ + API tests for response encoding + """ + + @staticmethod + @patch.dict(os.environ, {"CONFIG": os.path.join(tempfile.gettempdir(), "testapi.yml"), "API_CLASS": "txtai.api.API"}) + def start(yaml): + """ + Starts a mock FastAPI client. + + Args: + yaml: input configuration + """ + + config = os.path.join(tempfile.gettempdir(), "testapi.yml") + + with open(config, "w", encoding="utf-8") as output: + output.write(yaml) + + # Create new application and set on client + application.app = application.create() + client = TestClient(application.app) + application.start() + + return client + + @classmethod + def setUpClass(cls): + """ + Create API client on creation of class. + """ + + cls.client = TestEncoding.start(INDEX % ("True", "image")) + + def testImages(self): + """ + Test image encoding + """ + + with open(Utils.PATH + "/books.jpg", "rb") as f: + self.client.post("addimage", data={"uid": 0}, files={"data": f}) + self.client.get("index") + + query = urllib.parse.quote_plus("select id, object from txtai limit 1") + results = self.client.get(f"search?query={query}").json() + + # Test reading image + self.assertIsInstance(PIL.Image.open(BytesIO(base64.b64decode(results[0]["object"]))), PIL.Image.Image) + + def testInvalidInputs(self): + """ + Test invalid parameter inputs + """ + + response = self.client.post("addimage", data={"uid": [0, 1]}, files={"data": b"123"}) + self.assertEqual(response.status_code, 422) + + response = self.client.post("addobject", data={"uid": [0, 1]}, files={"data": b"123"}) + self.assertEqual(response.status_code, 422) + + def testInvalidJSON(self): + """ + Test that invalid JSON raises an exception + """ + + with self.assertRaises(TypeError): + JSONEncoder().encode(np.random.rand(1, 1)) + + def testMessagePack(self): + """ + Test message pack encoding + """ + + # Validate binary encoding + results = self.client.get("count", headers={"Accept": "application/msgpack"}).content + self.assertEqual(results, b"\x01") + + # Validate query result + query = urllib.parse.quote_plus("select id, object from txtai limit 1") + results = self.client.get(f"search?query={query}", headers={"Accept": "application/msgpack"}).content + results = msgpack.unpackb(results) + + # Test reading image + self.assertIsInstance(PIL.Image.open(BytesIO(results[0]["object"])), PIL.Image.Image) + + def testObjects(self): + """ + Test object encoding + """ + + # Recreate model with standard object encoding + self.client = TestEncoding.start(INDEX % ("True", "True")) + + # Test various formats + self.client.post("addobject", data={"uid": "id0"}, files={"data": b"1234"}) + self.client.post("addobject", files={"data": b"ABC"}) + self.client.post("addobject", data={"uid": "id1", "field": "object"}, files={"data": b"A1234"}) + self.client.get("index") + + query = urllib.parse.quote_plus("select id, object from txtai where id = 'id0' limit 1") + results = self.client.get(f"search?query={query}").json() + self.assertEqual(base64.b64decode(results[0]["object"]), b"1234") + + # Test with messagepack encoding + results = self.client.get(f"search?query={query}", headers={"Accept": "application/msgpack"}).content + results = msgpack.unpackb(results) + self.assertEqual(results[0]["object"], b"1234") + + count = self.client.get("count").json() + self.assertEqual(count, 3) + + def testReadOnly(self): + """ + Test read only indexes + """ + + # Recreate model with standard object encoding + self.client = TestEncoding.start(INDEX % ("False", "True")) + + # Test errors raised for write operations + with open(Utils.PATH + "/books.jpg", "rb") as f: + response = self.client.post("addimage", data={"uid": 0}, files={"data": f}) + self.assertEqual(response.status_code, 403) + + self.assertEqual(self.client.post("addobject", data={"uid": 0}, files={"data": b"1234"}).status_code, 403) diff --git a/test/python/testapi/testextension.py b/test/python/testapi/testextension.py new file mode 100644 index 0000000..3fed08b --- /dev/null +++ b/test/python/testapi/testextension.py @@ -0,0 +1,117 @@ +""" +Extension module tests +""" + +import os +import tempfile +import unittest + +from unittest.mock import patch + +from fastapi import APIRouter +from fastapi.testclient import TestClient + +from txtai.api import application, Extension +from txtai.pipeline import Pipeline + +# Example pipeline extension +PIPELINES = """ +testapi.testextension.SamplePipeline: +""" + + +class SampleRouter: + """ + Sample API router. + """ + + router = APIRouter() + + @staticmethod + @router.get("/sample") + def sample(text: str): + """ + Calls sample pipeline. + + Args: + text: input text + + Returns: + formatted text + """ + + return application.get().pipeline("testapi.testextension.SamplePipeline", (text,)) + + +class SampleExtension(Extension): + """ + Sample API extension. + """ + + def __call__(self, app): + app.include_router(SampleRouter().router) + + +class SamplePipeline(Pipeline): + """ + Sample pipeline. + """ + + def __call__(self, text): + return text.lower() + + +class TestExtension(unittest.TestCase): + """ + API tests for extensions. + """ + + @staticmethod + @patch.dict( + os.environ, + { + "CONFIG": os.path.join(tempfile.gettempdir(), "testapi.yml"), + "API_CLASS": "txtai.api.API", + "EXTENSIONS": "testapi.testextension.SampleExtension", + }, + ) + def start(): + """ + Starts a mock FastAPI client. + """ + + config = os.path.join(tempfile.gettempdir(), "testapi.yml") + + with open(config, "w", encoding="utf-8") as output: + output.write(PIPELINES) + + # Create new application and set on client + application.app = application.create() + client = TestClient(application.app) + application.start() + + return client + + @classmethod + def setUpClass(cls): + """ + Create API client on creation of class. + """ + + cls.client = TestExtension.start() + + def testEmpty(self): + """ + Test an empty extension + """ + + extension = Extension() + self.assertIsNone(extension(None)) + + def testExtension(self): + """ + Test a pipeline extension + """ + + text = self.client.get("sample?text=Test%20String").json() + self.assertEqual(text, "test string") diff --git a/test/python/testapi/testmcp.py b/test/python/testapi/testmcp.py new file mode 100644 index 0000000..cb3e3c6 --- /dev/null +++ b/test/python/testapi/testmcp.py @@ -0,0 +1,59 @@ +""" +Agent API module tests +""" + +import os +import tempfile +import unittest + +from unittest.mock import patch + +from fastapi.testclient import TestClient + +from txtai.api import application + +# Configuration for agents +MCP = """ +mcp: True +""" + + +# pylint: disable=R0904 +class TestMCP(unittest.TestCase): + """ + API tests for model context protocol (MCP) + """ + + @staticmethod + @patch.dict(os.environ, {"CONFIG": os.path.join(tempfile.gettempdir(), "testapi.yml"), "API_CLASS": "txtai.api.API"}) + def start(): + """ + Starts a mock FastAPI client. + """ + + config = os.path.join(tempfile.gettempdir(), "testapi.yml") + + with open(config, "w", encoding="utf-8") as output: + output.write(MCP) + + # Create new application and set on client + application.app = application.create() + client = TestClient(application.app) + application.start() + + return client + + @classmethod + def setUpClass(cls): + """ + Create API client on creation of class. + """ + + cls.client = TestMCP.start() + + def testMCP(self): + """ + Test that application a /mcp route + """ + + self.assertTrue(any(route.path == "/mcp" for route in self.client.app.routes)) diff --git a/test/python/testapi/testopenai.py b/test/python/testapi/testopenai.py new file mode 100644 index 0000000..0fba6d4 --- /dev/null +++ b/test/python/testapi/testopenai.py @@ -0,0 +1,201 @@ +""" +OpenAI API module tests +""" + +import os +import tempfile +import unittest + +from unittest.mock import patch + +from fastapi.testclient import TestClient + +from txtai.api import application + +# pylint: disable=C0411 +from utils import Utils + +# API Configuration +CONFIG = """ +# Enable OpenAI-compatible API +openai: True + +# Allow indexing of documents +writable: True + +# Agent configuration +agent: + hello: + max_iterations: 1 + +# Embeddings settings +embeddings: + path: sentence-transformers/nli-mpnet-base-v2 + content: True + +# LLM configuration +llm: + path: hf-internal-testing/tiny-random-LlamaForCausalLM + +# Text segmentation +segmentation: + +# Text to speech +texttospeech: + +# Transcription +transcription: + +# Workflow +workflow: + echo: + tasks: + - task: console +""" + + +# pylint: disable=R0904 +class TestOpenAI(unittest.TestCase): + """ + Tests for OpenAI-compatible API endpoint for txtai. + """ + + @staticmethod + @patch.dict(os.environ, {"CONFIG": os.path.join(tempfile.gettempdir(), "testopenai.yml"), "API_CLASS": "txtai.api.API"}) + def start(): + """ + Starts a mock FastAPI client. + """ + + config = os.path.join(tempfile.gettempdir(), "testopenai.yml") + + with open(config, "w", encoding="utf-8") as output: + output.write(CONFIG) + + # Create new application and set on client + application.app = application.create() + client = TestClient(application.app) + application.start() + + # Patch LLM to generate answer + agent = application.get().agents["hello"] + agent.process.model.llm = lambda *args, **kwargs: 'Action:\n{"name": "final_answer", "arguments": "Hi"}' + + return client + + @classmethod + def setUpClass(cls): + """ + Create API client on creation of class. + """ + + cls.client = TestOpenAI.start() + + cls.data = [ + "US tops 5 million confirmed virus cases", + "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg", + "Beijing mobilises invasion craft along coast as Taiwan tensions escalate", + "The National Park Service warns against sacrificing slower friends in a bear attack", + "Maine man wins $1M from $25 lottery ticket", + "Make huge profits without work, earn up to $100,000 a day", + ] + + # Index data + cls.client.post("add", json=[{"id": x, "text": row} for x, row in enumerate(cls.data)]) + cls.client.get("index") + + def testChatAgent(self): + """ + Test a chat completion with an agent + """ + + response = self.client.post("/v1/chat/completions", json={"messages": [{"role": "user", "content": "Hello"}], "model": "hello"}).json() + + self.assertEqual(response["choices"][0]["message"]["content"], "Hi") + + def testChatLLM(self): + """ + Test a chat completion with a LLM + """ + + response = self.client.post("/v1/chat/completions", json={"messages": [{"role": "user", "content": "Hello"}], "model": "llm"}).json() + + self.assertIsNotNone(response["choices"][0]["message"]["content"]) + + def testChatPipeline(self): + """ + Test a chat completion with a pipeline + """ + + response = self.client.post("/v1/chat/completions", json={"messages": [{"role": "user", "content": "Hello"}], "model": "segmentation"}).json() + + self.assertEqual(response["choices"][0]["message"]["content"], "Hello") + + def testChatSearch(self): + """ + Test a chat completion with an embeddings search + """ + + response = self.client.post( + "/v1/chat/completions", json={"messages": [{"role": "user", "content": "feel good story"}], "model": "embeddings"} + ).json() + + self.assertEqual(response["choices"][0]["message"]["content"], self.data[4]) + + def testChatStream(self): + """ + Test a chat completion with a LLM + """ + + response = self.client.post("/v1/chat/completions", json={"messages": [{"role": "user", "content": "Hello"}], "model": "llm", "stream": True}) + + self.assertGreater(len(response.text.split("\n\n")), 0) + + def testChatWorkflow(self): + """ + Test a chat completion with a workflow + """ + + response = self.client.post("/v1/chat/completions", json={"messages": [{"role": "user", "content": "Hello"}], "model": "echo"}).json() + + self.assertEqual(response["choices"][0]["message"]["content"], "Hello") + + def testEmbeddings(self): + """ + Test generating embeddings vectors + """ + + response = self.client.post("/v1/embeddings", json={"input": "text to embed", "model": "nli-mpnet-base-v2"}).json() + + self.assertEqual(len(response["data"][0]["embedding"]), 768) + + def testSpeech(self): + """ + Test generating speech for input text + """ + + response = self.client.post( + "/v1/audio/speech", json={"model": "tts", "input": "text to speak", "voice": "default", "response_format": "wav"} + ).content + + self.assertTrue(response[0:4] == b"RIFF") + + def testTranscribe(self): + """ + Test audio to text transcription + """ + + path = Utils.PATH + "/Make_huge_profits.wav" + with open(path, "rb") as f: + text = self.client.post("/v1/audio/transcriptions", files={"file": f}).json()["text"] + self.assertEqual(text, "Make huge profits without working make up to one hundred thousand dollars a day") + + def testTranslate(self): + """ + Test audio translation + """ + + path = Utils.PATH + "/Make_huge_profits.wav" + with open(path, "rb") as f: + text = self.client.post("/v1/audio/translations", files={"file": f}).json()["text"] + self.assertEqual(text, "Make huge profits without working make up to one hundred thousand dollars a day") diff --git a/test/python/testapp.py b/test/python/testapp.py new file mode 100644 index 0000000..7e26680 --- /dev/null +++ b/test/python/testapp.py @@ -0,0 +1,91 @@ +""" +Application module tests +""" + +import unittest +import types + +from txtai.app import Application +from txtai.pipeline import Pipeline + + +class TestApp(unittest.TestCase): + """ + Application tests. + """ + + def testConfig(self): + """ + Test a file not found config exception + """ + + with self.assertRaises(FileNotFoundError): + Application.read("No file here") + + def testParameter(self): + """ + Test resolving application parameter + """ + + app = Application( + """ + testapp.TestPipeline: + application: + """ + ) + + # Check that application instance is not None + self.assertIsNotNone(app.pipelines["testapp.TestPipeline"].application) + + def testStream(self): + """ + Test workflow streams + """ + + app = Application( + """ + workflow: + stream: + stream: + action: testapp.TestStream + tasks: + - nop + batchstream: + stream: + action: testapp.TestStream + batch: True + tasks: + - nop + """ + ) + + def generator(): + yield 10 + + # Test single stream + self.assertEqual(list(app.workflow("stream", [10])), list(range(10))) + + # Test batch stream + self.assertEqual(list(app.workflow("batchstream", generator())), list(range(10))) + + +class TestPipeline(Pipeline): + """ + Test pipeline with an application parameter. + """ + + def __init__(self, application): + self.application = application + + +class TestStream: + """ + Test workflow stream + """ + + def __call__(self, arg): + if isinstance(arg, types.GeneratorType): + for x in arg: + yield from range(int(x)) + else: + yield from range(int(arg)) diff --git a/test/python/testarchive.py b/test/python/testarchive.py new file mode 100644 index 0000000..e89e111 --- /dev/null +++ b/test/python/testarchive.py @@ -0,0 +1,116 @@ +""" +Compress module tests +""" + +import os +import tarfile +import tempfile +import unittest + +from zipfile import ZipFile, ZIP_DEFLATED + +from txtai.archive import ArchiveFactory, Compress + +# pylint: disable=C0411 +from utils import Utils + + +class TestArchive(unittest.TestCase): + """ + Archive tests. + """ + + def testDirectory(self): + """ + Test directory included in compressed files + """ + + for extension in ["tar", "zip"]: + # Create archive instance + archive = ArchiveFactory.create() + + # Create subdirectory in archive working path + path = os.path.join(archive.path(), "dir") + os.makedirs(path, exist_ok=True) + + # Create file in archive working path + with open(os.path.join(path, "test"), "w", encoding="utf-8") as f: + f.write("test") + + # Save archive + path = os.path.join(tempfile.gettempdir(), f"subdir.{extension}") + archive.save(path) + + # Extract files from archive + archive = ArchiveFactory.create() + archive.load(path) + + # Check if file properly extracted + path = os.path.join(archive.path(), "dir", "test") + self.assertTrue(os.path.exists(path)) + + def testInvalidTarLink(self): + """ + Test invalid tar file with symlinks + """ + + symlink = os.path.join(tempfile.gettempdir(), "link") + + # Remove symlink if it already exists + try: + os.remove(symlink) + except OSError: + pass + + # Create symlink and add to TAR file + os.symlink(os.path.join(tempfile.gettempdir(), "noexist"), symlink) + + path = os.path.join(tempfile.gettempdir(), "badtarlink") + with tarfile.open(path, "w") as tar: + tar.add(symlink, arcname="l") + + archive = ArchiveFactory.create() + + # Validate error is thrown for file + with self.assertRaises(IOError): + archive.load(path, "tar") + + def testInvalidTarPath(self): + """ + Test invalid tar file with a path outside of base directory + """ + + path = os.path.join(tempfile.gettempdir(), "badtarpath") + with tarfile.open(path, "w") as tar: + tar.add(Utils.PATH, arcname="..") + + archive = ArchiveFactory.create() + + # Validate error is thrown for file + with self.assertRaises(IOError): + archive.load(path, "tar") + + def testInvalidZipPath(self): + """ + Test invalid zip file with a path outside of base directory + """ + + path = os.path.join(tempfile.gettempdir(), "badzippath") + with ZipFile(path, "w", ZIP_DEFLATED) as zfile: + zfile.write(Utils.PATH + "/article.pdf", arcname="../article.pdf") + + archive = ArchiveFactory.create() + + # Validate error is thrown for file + with self.assertRaises(IOError): + archive.load(path, "zip") + + def testNotImplemented(self): + """ + Test exceptions for non-implemented methods + """ + + compress = Compress() + + self.assertRaises(NotImplementedError, compress.pack, None, None) + self.assertRaises(NotImplementedError, compress.unpack, None, None) diff --git a/test/python/testcloud.py b/test/python/testcloud.py new file mode 100644 index 0000000..81a700c --- /dev/null +++ b/test/python/testcloud.py @@ -0,0 +1,181 @@ +""" +Cloud module tests +""" + +import os +import tempfile +import time +import unittest + +from unittest.mock import patch + +from huggingface_hub import hf_hub_download +from txtai.cloud import Cloud +from txtai.embeddings import Embeddings + + +class TestCloud(unittest.TestCase): + """ + Cloud tests. + """ + + @classmethod + def setUpClass(cls): + """ + Initialize test data. + """ + + cls.data = [ + "US tops 5 million confirmed virus cases", + "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg", + "Beijing mobilises invasion craft along coast as Taiwan tensions escalate", + "The National Park Service warns against sacrificing slower friends in a bear attack", + "Maine man wins $1M from $25 lottery ticket", + "Make huge profits without work, earn up to $100,000 a day", + ] + + # Create embeddings model, backed by sentence-transformers & transformers + cls.embeddings = Embeddings({"format": "json", "path": "sentence-transformers/nli-mpnet-base-v2", "content": True}) + + @classmethod + def tearDownClass(cls): + """ + Cleanup data. + """ + + if cls.embeddings: + cls.embeddings.close() + + def testCustom(self): + """ + Test custom provider + """ + + # pylint: disable=E1120 + self.runHub("txtai.cloud.HuggingFaceHub") + + def testHub(self): + """ + Test huggingface-hub integration + """ + + # pylint: disable=E1120 + self.runHub("huggingface-hub") + + def testInvalidProvider(self): + """ + Test invalid provider identifier + """ + + # Test invalid external provider + with self.assertRaises(ImportError): + embeddings = Embeddings() + embeddings.load(provider="ProviderNoExist", container="Invalid") + + def testNotImplemented(self): + """ + Test exceptions for non-implemented methods + """ + + cloud = Cloud({}) + + self.assertRaises(NotImplementedError, cloud.exists, None) + self.assertRaises(NotImplementedError, cloud.load, None) + self.assertRaises(NotImplementedError, cloud.save, None) + + def testObjectStorage(self): + """ + Test object storage integration + """ + + # Run tests with uncompressed and compressed index + for path in ["cloud.object", "cloud.object.tar.gz"]: + self.runTests(path, {"provider": "local", "container": f"cloud.{time.time()}", "key": tempfile.gettempdir()}) + + @patch("huggingface_hub.hf_hub_download") + @patch("huggingface_hub.get_hf_file_metadata") + @patch("huggingface_hub.upload_file") + @patch("huggingface_hub.create_repo") + def runHub(self, provider, create, upload, metadata, download): + """ + Run huggingface-hub tests. This method mocks write operations since a token won't be available. + """ + + def filemeta(url, token): + return (url, token) if "Invalid" not in url else None + + def filedownload(**kwargs): + if "Invalid" in kwargs["repo_id"]: + raise FileNotFoundError + + # Check for .gitattributes file + if kwargs["filename"] == ".gitattributes": + return attributes + + # Check for cloud index path + if any(kwargs["filename"] == x for x in paths): + return index + + # Use original method for all other requests + return hf_hub_download(**kwargs) + + # Patch write methods since token will not be available + create.return_value = None + upload.return_value = None + metadata.side_effect = filemeta + download.side_effect = filedownload + + # Create dummy index + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), f"cloud.{provider}.tar.gz") + self.embeddings.save(index) + + # Initialize attributes file + # pylint: disable=R1732 + with tempfile.NamedTemporaryFile(mode="w", delete=False) as tmp: + tmp.write("*.bin filter=lfs diff=lfs merge=lfs -text\n") + attributes = tmp.name + + # Run tests with uncompressed and compressed index + paths = [f"cloud.{provider}", f"cloud.{provider}.tar.gz"] + for path in paths: + self.runTests(path, {"provider": provider, "container": "neuml/txtai-intro"}) + + def runTests(self, path, cloud): + """ + Runs a series of cloud sync tests. + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), path) + + # Test exists handles missing cloud storage object + invalid = cloud.copy() + invalid["container"] = "InvalidPathToTest" + self.assertFalse(self.embeddings.exists(index, invalid)) + + # Test exception raised when trying to load index and doesn't exist in cloud storage + # pylint: disable=W0719 + with self.assertRaises(Exception): + self.embeddings.load(index, invalid) + + # Save index + self.embeddings.save(index, cloud) + + # Test object exists in cloud storage + self.assertTrue(self.embeddings.exists(index, cloud)) + + # Test object exists locally + self.assertTrue(self.embeddings.exists(index)) + + # Test index can be reloaded + self.embeddings.load(index, cloud) + + # Search for best match + result = self.embeddings.search("feel good story", 1)[0] + self.assertEqual(result["text"], self.data[4]) diff --git a/test/python/testconsole.py b/test/python/testconsole.py new file mode 100644 index 0000000..0df82bb --- /dev/null +++ b/test/python/testconsole.py @@ -0,0 +1,175 @@ +""" +Console module tests +""" + +import contextlib +import io +import os +import tempfile +import unittest + +from txtai.console import Console +from txtai.embeddings import Embeddings + +APPLICATION = """ +path: %s + +workflow: + test: + tasks: + - task: console +""" + + +class TestConsole(unittest.TestCase): + """ + Console tests. + """ + + @classmethod + def setUpClass(cls): + """ + Initialize test data. + """ + + cls.data = [ + "US tops 5 million confirmed virus cases", + "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg", + "Beijing mobilises invasion craft along coast as Taiwan tensions escalate", + "The National Park Service warns against sacrificing slower friends in a bear attack", + "Maine man wins $1M from $25 lottery ticket", + "Make huge profits without work, earn up to $100,000 a day", + ] + + # Create embeddings model, backed by sentence-transformers & transformers + cls.embeddings = Embeddings({"path": "sentence-transformers/nli-mpnet-base-v2", "content": True}) + + # Create an index for the list of text + cls.embeddings.index([(uid, text, None) for uid, text in enumerate(cls.data)]) + + # Create app paths + cls.apppath = os.path.join(tempfile.gettempdir(), "console.yml") + cls.embedpath = os.path.join(tempfile.gettempdir(), "embeddings.console") + + # Create app.yml + with open(cls.apppath, "w", encoding="utf-8") as out: + out.write(APPLICATION % cls.embedpath) + + # Save index as uncompressed and compressed + cls.embeddings.save(cls.embedpath) + cls.embeddings.save(f"{cls.embedpath}.tar.gz") + + # Create console + cls.console = Console(cls.embedpath) + + def testApplication(self): + """ + Test application + """ + + self.assertNotIn("Traceback", self.command(f".load {self.apppath}")) + self.assertIn("1", self.command(".limit 1")) + self.assertIn("Maine man wins", self.command("feel good story")) + + def testConfig(self): + """ + Test .config command + """ + + self.assertIn("tasks", self.command(".config")) + + def testEmbeddings(self): + """ + Test embeddings index + """ + + self.assertNotIn("Traceback", self.command(f".load {self.embedpath}.tar.gz")) + self.assertNotIn("Traceback", self.command(f".load {self.embedpath}")) + self.assertIn("1", self.command(".limit 1")) + self.assertIn("Maine man wins", self.command("feel good story")) + + def testEmbeddingsNoDatabase(self): + """ + Test embeddings with no database/content + """ + + console = Console() + + # Create embeddings model, backed by sentence-transformers & transformers + embeddings = Embeddings({"path": "sentence-transformers/nli-mpnet-base-v2"}) + + # Create an index for the list of text + embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Set embeddings on console + console.app = embeddings + self.assertIn("4", self.command("feel good story", console)) + + def testEmpty(self): + """ + Test empty console instance + """ + + console = Console() + self.assertIn("AttributeError", self.command("search", console)) + + def testHighlight(self): + """ + Test .highlight command + """ + + self.assertIn("highlight", self.command(".highlight")) + self.assertIn("wins", self.command("feel good story")) + self.assertIn("Taiwan", self.command("asia")) + + def testPreloop(self): + """ + Test preloop + """ + + self.assertIn("txtai console", self.preloop()) + + def testWorkflow(self): + """ + Test .workflow command + """ + + self.command(f".load {self.apppath}") + self.assertIn("echo", self.command(".workflow test echo")) + + def command(self, command, console=None): + """ + Runs a console command. + + Args: + command: command to run + console: console instance, defaults to self.console + + Returns: + command output + """ + + # Run info + output = io.StringIO() + with contextlib.redirect_stdout(output): + if not console: + console = self.console + + console.onecmd(command) + + return output.getvalue() + + def preloop(self): + """ + Runs console.preloop and redirects stdout. + + Returns: + preloop output + """ + + # Run info + output = io.StringIO() + with contextlib.redirect_stdout(output): + self.console.preloop() + + return output.getvalue() diff --git a/test/python/testdatabase/__init__.py b/test/python/testdatabase/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/python/testdatabase/testclient.py b/test/python/testdatabase/testclient.py new file mode 100644 index 0000000..0d092e5 --- /dev/null +++ b/test/python/testdatabase/testclient.py @@ -0,0 +1,71 @@ +""" +Client module tests +""" + +import os +import time +import tempfile + +from txtai.embeddings import Embeddings + +from .testrdbms import Common + + +# pylint: disable=R0904 +class TestClient(Common.TestRDBMS): + """ + Embeddings with content stored in a client RDBMS. + """ + + @classmethod + def setUpClass(cls): + """ + Initialize test data. + """ + + cls.data = [ + "US tops 5 million confirmed virus cases", + "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg", + "Beijing mobilises invasion craft along coast as Taiwan tensions escalate", + "The National Park Service warns against sacrificing slower friends in a bear attack", + "Maine man wins $1M from $25 lottery ticket", + "Make huge profits without work, earn up to $100,000 a day", + ] + + # Content backend + cls.backend = None + + # Create embeddings model, backed by sentence-transformers & transformers + cls.embeddings = Embeddings({"path": "sentence-transformers/nli-mpnet-base-v2"}) + + @classmethod + def tearDownClass(cls): + """ + Cleanup data. + """ + + if cls.embeddings: + cls.embeddings.close() + + def setUp(self): + """ + Set unique database path for each test. + """ + + # Generate unique database path and set on embeddings + path = os.path.join(tempfile.gettempdir(), f"{int(time.time() * 1000)}.sqlite") + self.backend = f"sqlite:///{path}" + + self.embeddings.config["content"] = self.backend + + def testSchema(self): + """ + Test database creation with a specified schema + """ + + # Default sequence id + embeddings = Embeddings(path="sentence-transformers/nli-mpnet-base-v2", content=self.backend, schema="txtai") + embeddings.index(self.data) + + result = embeddings.search("feel good story", 1)[0] + self.assertEqual(result["text"], self.data[4]) diff --git a/test/python/testdatabase/testcustom.py b/test/python/testdatabase/testcustom.py new file mode 100644 index 0000000..755cb77 --- /dev/null +++ b/test/python/testdatabase/testcustom.py @@ -0,0 +1,29 @@ +""" +Custom database tests +""" + +import unittest + +from txtai.database import DatabaseFactory + + +class TestCustom(unittest.TestCase): + """ + Custom database backend tests. + """ + + def testCustomBackend(self): + """ + Test resolving a custom backend + """ + + database = DatabaseFactory.create({"content": "txtai.database.SQLite"}) + self.assertIsNotNone(database) + + def testCustomBackendNotFound(self): + """ + Test resolving an unresolvable backend + """ + + with self.assertRaises(ImportError): + DatabaseFactory.create({"content": "notfound.database"}) diff --git a/test/python/testdatabase/testdatabase.py b/test/python/testdatabase/testdatabase.py new file mode 100644 index 0000000..9261882 --- /dev/null +++ b/test/python/testdatabase/testdatabase.py @@ -0,0 +1,32 @@ +""" +Database tests +""" + +import unittest + +from txtai.database import Database + + +class TestDatabase(unittest.TestCase): + """ + Base database tests. + """ + + def testNotImplemented(self): + """ + Test exceptions for non-implemented methods + """ + + database = Database({}) + + self.assertRaises(NotImplementedError, database.load, None) + self.assertRaises(NotImplementedError, database.insert, None) + self.assertRaises(NotImplementedError, database.delete, None) + self.assertRaises(NotImplementedError, database.reindex, None) + self.assertRaises(NotImplementedError, database.save, None) + self.assertRaises(NotImplementedError, database.close) + self.assertRaises(NotImplementedError, database.ids, None) + self.assertRaises(NotImplementedError, database.count) + self.assertRaises(NotImplementedError, database.resolve, None, None) + self.assertRaises(NotImplementedError, database.embed, None, None) + self.assertRaises(NotImplementedError, database.query, None, None, None, None) diff --git a/test/python/testdatabase/testduckdb.py b/test/python/testdatabase/testduckdb.py new file mode 100644 index 0000000..59a92d0 --- /dev/null +++ b/test/python/testdatabase/testduckdb.py @@ -0,0 +1,84 @@ +""" +DuckDB module tests +""" + +import os +import unittest + +from txtai.embeddings import Embeddings + +from .testrdbms import Common + + +# pylint: disable=R0904 +class TestDuckDB(Common.TestRDBMS): + """ + Embeddings with content stored in DuckDB. + """ + + @classmethod + def setUpClass(cls): + """ + Initialize test data. + """ + + cls.data = [ + "US tops 5 million confirmed virus cases", + "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg", + "Beijing mobilises invasion craft along coast as Taiwan tensions escalate", + "The National Park Service warns against sacrificing slower friends in a bear attack", + "Maine man wins $1M from $25 lottery ticket", + "Make huge profits without work, earn up to $100,000 a day", + ] + + # Content backend + cls.backend = "duckdb" + + # Create embeddings model, backed by sentence-transformers & transformers + cls.embeddings = Embeddings({"path": "sentence-transformers/nli-mpnet-base-v2", "content": cls.backend}) + + @classmethod + def tearDownClass(cls): + """ + Cleanup data. + """ + + if cls.embeddings: + cls.embeddings.close() + + @unittest.skipIf(os.name == "nt", "testArchive skipped on Windows") + def testArchive(self): + """ + Test embeddings index archiving + """ + + super().testArchive() + + def testFunction(self): + """ + Test custom functions + """ + + embeddings = Embeddings( + { + "path": "sentence-transformers/nli-mpnet-base-v2", + "content": self.backend, + "functions": [{"name": "textlength", "function": "testdatabase.testduckdb.length"}], + } + ) + + # Create an index for the list of text + embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Search for best match + result = embeddings.search("select textlength(text) length from txtai where id = 0", 1)[0] + + self.assertEqual(int(result["length"]), 39) + + +def length(text): + """ + Custom SQL function. + """ + + return len(text) diff --git a/test/python/testdatabase/testencoder.py b/test/python/testdatabase/testencoder.py new file mode 100644 index 0000000..352499d --- /dev/null +++ b/test/python/testdatabase/testencoder.py @@ -0,0 +1,155 @@ +""" +Test encoding/decoding database objects +""" + +import glob +import os +import unittest +import tempfile + +from unittest.mock import patch + +from io import BytesIO + +from PIL import Image + +from txtai.embeddings import Embeddings + +# pylint: disable=C0411 +from utils import Utils + + +class TestEncoder(unittest.TestCase): + """ + Encoder tests. + """ + + @classmethod + def setUpClass(cls): + """ + Initialize test data. + """ + + cls.data = [] + for path in glob.glob(Utils.PATH + "/*jpg"): + cls.data.append((path, {"object": Image.open(path)}, None)) + + # Create embeddings model, backed by sentence-transformers & transformers + cls.embeddings = Embeddings( + {"method": "sentence-transformers", "path": "sentence-transformers/clip-ViT-B-32", "content": True, "objects": "image"} + ) + + @classmethod + def tearDownClass(cls): + """ + Cleanup data. + """ + + if cls.embeddings: + cls.embeddings.close() + + def testDefault(self): + """ + Test an index with default encoder + """ + + try: + # Set default encoder + self.embeddings.config["objects"] = True + + # Test all database providers + for content in ["duckdb", "sqlite"]: + self.embeddings.config["content"] = content + + data = [(0, {"object": bytearray([1, 2, 3]), "text": "default test"}, None)] + + # Create an index + self.embeddings.index(data) + + result = self.embeddings.search("select object from txtai limit 1")[0] + + self.assertEqual(result["object"].getvalue(), bytearray([1, 2, 3])) + finally: + self.embeddings.config["objects"] = "image" + self.embeddings.config["content"] = True + + def testImages(self): + """ + Test an index with image encoder + """ + + # Create an index for the list of images + self.embeddings.index(self.data) + + result = self.embeddings.search("select id, object from txtai where similar('universe') limit 1")[0] + + self.assertTrue(result["id"].endswith("stars.jpg")) + self.assertTrue(isinstance(result["object"], Image.Image)) + + @patch.dict(os.environ, {"ALLOW_PICKLE": "True"}) + def testPickle(self): + """ + Test an index with pickle encoder + """ + + try: + # Set pickle encoder + self.embeddings.config["objects"] = "pickle" + data = [(0, {"object": [1, 2, 3, 4, 5], "text": "default test"}, None)] + + # Create an index + self.embeddings.index(data) + + result = self.embeddings.search("select object from txtai limit 1")[0] + + self.assertEqual(result["object"], [1, 2, 3, 4, 5]) + finally: + self.embeddings.config["objects"] = "image" + + def testReindex(self): + """ + Test reindex with objects + """ + + # Create an index for the list of images + self.embeddings.index(self.data) + + # Reindex images + self.embeddings.reindex({"method": "sentence-transformers", "path": "sentence-transformers/clip-ViT-B-32"}) + + result = self.embeddings.search("select id, object from txtai where similar('universe') limit 1")[0] + + self.assertTrue(result["id"].endswith("stars.jpg")) + self.assertTrue(isinstance(result["object"], Image.Image)) + + def testReindexFunction(self): + """ + Test reindex with objects and a function + """ + + try: + # Streaming function that loads images on the fly + def prepare(documents): + for uid, data, tags in documents: + yield (uid, Image.open(data), tags) + + # Create an index for the list of images + self.embeddings.index(self.data) + + # Set default encoder and use function to load images + self.embeddings.config["objects"] = True + + # Save and load index to force default encoder + index = os.path.join(tempfile.gettempdir(), "objects") + self.embeddings.save(index) + self.embeddings.load(index) + + # Reindex images + self.embeddings.reindex({"method": "sentence-transformers", "path": "sentence-transformers/clip-ViT-B-32"}, function=prepare) + + result = self.embeddings.search("select id, object from txtai where similar('universe') limit 1")[0] + + self.assertTrue(result["id"].endswith("stars.jpg")) + self.assertTrue(isinstance(result["object"], BytesIO)) + finally: + self.embeddings.config["objects"] = "image" diff --git a/test/python/testdatabase/testrdbms.py b/test/python/testdatabase/testrdbms.py new file mode 100644 index 0000000..1d57688 --- /dev/null +++ b/test/python/testdatabase/testrdbms.py @@ -0,0 +1,935 @@ +""" +Common file database module tests +""" + +import contextlib +import io +import os +import tempfile +import unittest + +from unittest.mock import patch + +from txtai.embeddings import Embeddings, IndexNotFoundError +from txtai.database import Embedded, RDBMS, SQLError + + +class Common: + """ + Wraps common file database tests to prevent unit test discovery for this class. + """ + + # pylint: disable=R0904 + class TestRDBMS(unittest.TestCase): + """ + Embeddings with content stored in a file database tests. + """ + + @classmethod + def setUpClass(cls): + """ + Initialize test data. + """ + + cls.data = [ + "US tops 5 million confirmed virus cases", + "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg", + "Beijing mobilises invasion craft along coast as Taiwan tensions escalate", + "The National Park Service warns against sacrificing slower friends in a bear attack", + "Maine man wins $1M from $25 lottery ticket", + "Make huge profits without work, earn up to $100,000 a day", + ] + + # Content backend + cls.backend = None + + # Create embeddings model, backed by sentence-transformers & transformers + cls.embeddings = Embeddings({"path": "sentence-transformers/nli-mpnet-base-v2", "content": cls.backend}) + + @classmethod + def tearDownClass(cls): + """ + Cleanup data. + """ + + if cls.embeddings: + cls.embeddings.close() + + def testArchive(self): + """ + Test embeddings index archiving + """ + + for extension in ["tar.bz2", "tar.gz", "tar.xz", "zip"]: + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), f"embeddings.{self.category()}.{extension}") + + self.embeddings.save(index) + self.embeddings.load(index) + + # Search for best match + result = self.embeddings.search("feel good story", 1)[0] + + self.assertEqual(result["text"], self.data[4]) + + # Test offsets still work after save/load + self.embeddings.upsert([(0, "Looking out into the dreadful abyss", None)]) + self.assertEqual(self.embeddings.count(), len(self.data)) + + def testAutoId(self): + """ + Test auto id generation + """ + + # Default sequence id + embeddings = Embeddings(path="sentence-transformers/nli-mpnet-base-v2", content=self.backend) + embeddings.index(self.data) + + result = embeddings.search("feel good story", 1)[0] + self.assertEqual(result["text"], self.data[4]) + + # UUID + embeddings.config["autoid"] = "uuid4" + embeddings.index(self.data) + + result = embeddings.search(self.data[4], 1)[0] + self.assertEqual(len(result["id"]), 36) + + def testCheckpoint(self): + """ + Test embeddings index checkpoints + """ + + # Checkpoint directory + checkpoint = os.path.join(tempfile.gettempdir(), f"embeddings.{self.category()}.checkpoint") + + # Save embeddings checkpoint + self.embeddings.index(self.data, checkpoint=checkpoint) + + # Reindex with checkpoint + self.embeddings.index(self.data, checkpoint=checkpoint) + + # Search for best match + result = self.embeddings.search("feel good story", 1)[0] + self.assertEqual(result["text"], self.data[4]) + + def testColumns(self): + """ + Test custom text/object columns + """ + + embeddings = Embeddings({"keyword": True, "content": self.backend, "columns": {"text": "value"}}) + data = [{"value": x} for x in self.data] + embeddings.index([(uid, text, None) for uid, text in enumerate(data)]) + + # Run search + result = embeddings.search("lottery", 1)[0] + self.assertEqual(result["text"], self.data[4]) + + def testClose(self): + """ + Test embeddings close + """ + + embeddings = None + + # Create index twice to test open/close and ensure resources are freed + for _ in range(2): + embeddings = Embeddings( + {"path": "sentence-transformers/nli-mpnet-base-v2", "scoring": {"method": "bm25", "terms": True}, "content": self.backend} + ) + + # Add record to index + embeddings.index([(0, "Close test", None)]) + + # Save index + index = os.path.join(tempfile.gettempdir(), f"embeddings.{self.category()}.close") + embeddings.save(index) + + # Close index + embeddings.close() + + # Test embeddings is empty + self.assertIsNone(embeddings.ann) + self.assertIsNone(embeddings.database) + + def testData(self): + """ + Test content storage and retrieval + """ + + data = self.data + [{"date": "2021-01-01", "text": "Baby panda", "flag": 1}] + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(data)]) + + # Search for best match + result = self.embeddings.search("feel good story", 1)[0] + self.assertEqual(result["text"], data[-1]["text"]) + + def testDelete(self): + """ + Test delete + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Delete best match + self.embeddings.delete([4]) + + # Search for best match + result = self.embeddings.search("feel good story", 1)[0] + + self.assertEqual(self.embeddings.count(), 5) + self.assertEqual(result["text"], self.data[5]) + + def testEmpty(self): + """ + Test empty index + """ + + # Test search against empty index + embeddings = Embeddings({"path": "sentence-transformers/nli-mpnet-base-v2", "content": self.backend}) + self.assertEqual(embeddings.search("test"), []) + + # Test index with no data + embeddings.index([]) + self.assertIsNone(embeddings.ann) + + # Test upsert with no data + embeddings.index([(0, "this is a test", None)]) + embeddings.upsert([]) + self.assertIsNotNone(embeddings.ann) + + def testEmptyString(self): + """ + Test empty string indexing + """ + + # Test empty string + self.embeddings.index([(0, "", None)]) + self.assertTrue(self.embeddings.search("test")) + + # Test empty string with dict + self.embeddings.index([(0, {"text": ""}, None)]) + self.assertTrue(self.embeddings.search("test")) + + def testExplain(self): + """ + Test query explain + """ + + # Test explain with similarity + result = self.embeddings.explain("feel good story", self.data)[0] + self.assertEqual(result["text"], self.data[4]) + self.assertEqual(len(result.get("tokens")), 8) + + def testExplainBatch(self): + """ + Test query explain batch + """ + + # Test explain with query + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + result = self.embeddings.batchexplain(["feel good story"], limit=1)[0][0] + self.assertEqual(result["text"], self.data[4]) + self.assertEqual(len(result.get("tokens")), 8) + + def testExplainEmpty(self): + """ + Test query explain with no filtering criteria + """ + + self.assertEqual(self.embeddings.explain("select * from txtai limit 1")[0]["id"], "0") + + def testExpressions(self): + """ + Test expressions + """ + + # Test indexed expressions + embeddings = Embeddings( + path="sentence-transformers/nli-mpnet-base-v2", + content=self.backend, + expressions=[{"name": "textlength", "expression": "length(text)", "index": True}], + ) + embeddings.index(self.data) + + result = embeddings.search("SELECT textlength FROM txtai WHERE id = 0", 1)[0] + self.assertEqual(result["textlength"], len(self.data[0])) + + def testGenerator(self): + """ + Test index with a generator + """ + + def documents(): + for uid, text in enumerate(self.data): + yield (uid, text, None) + + # Create an index for the list of text + self.embeddings.index(documents()) + + # Search for best match + result = self.embeddings.search("feel good story", 1)[0] + + self.assertEqual(result["text"], self.data[4]) + + def testHybrid(self): + """ + Test hybrid search + """ + + # Build data array + data = [(uid, text, None) for uid, text in enumerate(self.data)] + + # Index data with sparse + dense vectors. + embeddings = Embeddings({"path": "sentence-transformers/nli-mpnet-base-v2", "hybrid": True, "content": self.backend}) + embeddings.index(data) + + # Run search + result = embeddings.search("feel good story", 1)[0] + self.assertEqual(result["text"], data[4][1]) + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), f"embeddings.{self.category()}.hybrid") + + # Test load/save + embeddings.save(index) + embeddings.load(index) + + # Run search + result = embeddings.search("feel good story", 1)[0] + self.assertEqual(result["text"], data[4][1]) + + # Index data with sparse + dense vectors and unnormalized scores. + embeddings.config["scoring"]["normalize"] = False + embeddings.index(data) + + # Run search + result = embeddings.search("feel good story", 1)[0] + self.assertEqual(result["text"], data[4][1]) + + # Index data with sparse + dense vectors and bb25 normalized scores + embeddings.config["scoring"]["normalize"] = "bb25" + embeddings.index(data) + + # Run search + result = embeddings.search("canada intact iceberg a", 1)[0] + self.assertEqual(result["text"], data[1][1]) + + # Test upsert + data[0] = (0, "Feel good story: baby panda born", None) + embeddings.upsert([data[0]]) + + result = embeddings.search("feel good story", 1)[0] + self.assertEqual(result["text"], data[0][1]) + + def testIndex(self): + """ + Test index + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Search for best match + result = self.embeddings.search("feel good story", 1)[0] + + self.assertEqual(result["text"], self.data[4]) + + def testIndexTokens(self): + """ + Test index with tokens + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text.split(), None) for uid, text in enumerate(self.data)]) + + # Search for best match + result = self.embeddings.search("feel good story", 1)[0] + + self.assertEqual(result["text"], self.data[4]) + + def testInfo(self): + """ + Test info + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + output = io.StringIO() + with contextlib.redirect_stdout(output): + self.embeddings.info() + + self.assertIn("txtai", output.getvalue()) + + def testInstructions(self): + """ + Test indexing with instruction prefixes. + """ + + embeddings = Embeddings( + { + "path": "sentence-transformers/nli-mpnet-base-v2", + "content": self.backend, + "instructions": {"query": "query: ", "data": "passage: "}, + } + ) + + embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Search for best match + result = embeddings.search("feel good story", 1)[0] + + self.assertEqual(result["text"], self.data[4]) + + def testInvalidData(self): + """ + Test invalid JSON data + """ + + # Test invalid JSON value + with self.assertRaises(ValueError): + self.embeddings.index([(0, {"text": "This is a test", "flag": float("NaN")}, None)]) + + def testKeyword(self): + """ + Test keyword only (sparse) search + """ + + # Build data array + data = [(uid, text, None) for uid, text in enumerate(self.data)] + + # Index data with sparse keyword vectors + embeddings = Embeddings({"keyword": True, "content": self.backend}) + embeddings.index(data) + + # Run search + result = embeddings.search("lottery ticket", 1)[0] + self.assertEqual(result["text"], data[4][1]) + + # Test count method + self.assertEqual(embeddings.count(), len(data)) + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), f"embeddings.{self.category()}.keyword") + + # Test load/save + embeddings.save(index) + embeddings.load(index) + + # Run search + result = embeddings.search("lottery ticket", 1)[0] + self.assertEqual(result["text"], data[4][1]) + + # Update data + data[0] = (0, "Feel good story: baby panda born", None) + embeddings.upsert([data[0]]) + + # Search for best match + result = embeddings.search("feel good story", 1)[0] + self.assertEqual(result["text"], data[0][1]) + + def testMultiData(self): + """ + Test indexing with multiple data types (text, documents) + """ + + embeddings = Embeddings({"path": "sentence-transformers/nli-mpnet-base-v2", "content": self.backend, "batch": len(self.data)}) + + # Create an index using mixed data (text and documents) + data = [] + for uid, text in enumerate(self.data): + data.append((uid, text, None)) + data.append((uid, {"content": text}, None)) + + embeddings.index(data) + + # Search for best match + result = embeddings.search("feel good story", 1)[0] + + self.assertEqual(result["text"], self.data[4]) + + def testMultiSave(self): + """ + Test multiple successive saves + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Save original index + index = os.path.join(tempfile.gettempdir(), f"embeddings.{self.category()}.insert") + self.embeddings.save(index) + + # Modify index + self.embeddings.upsert([(0, "Looking out into the dreadful abyss", None)]) + + # Save to a different location + indexupdate = os.path.join(tempfile.gettempdir(), f"embeddings.{self.category()}.update") + self.embeddings.save(indexupdate) + + # Save to same location + self.embeddings.save(index) + + # Test all indexes match + result = self.embeddings.search("feel good story", 1)[0] + self.assertEqual(result["text"], self.data[4]) + + self.embeddings.load(index) + result = self.embeddings.search("feel good story", 1)[0] + self.assertEqual(result["text"], self.data[4]) + + self.embeddings.load(indexupdate) + result = self.embeddings.search("feel good story", 1)[0] + self.assertEqual(result["text"], self.data[4]) + + def testNoIndex(self): + """ + Test an embeddings instance with no available indexes + """ + + # Disable top-level indexing + embeddings = Embeddings( + { + "content": self.backend, + "defaults": False, + } + ) + embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + with self.assertRaises(IndexNotFoundError): + embeddings.search("select id, text, score from txtai where similar('feel good story')") + + def testNotImplemented(self): + """ + Test exceptions for non-implemented methods + """ + + db = RDBMS({}) + + self.assertRaises(NotImplementedError, db.connect, None) + self.assertRaises(NotImplementedError, db.getcursor) + self.assertRaises(NotImplementedError, db.jsonprefix) + self.assertRaises(NotImplementedError, db.jsoncolumn, None) + self.assertRaises(NotImplementedError, db.rows) + self.assertRaises(NotImplementedError, db.addfunctions) + + db = Embedded({}) + self.assertRaises(NotImplementedError, db.copy, None) + + def testObject(self): + """ + Test object field + """ + + # Encode object + embeddings = Embeddings({"defaults": False, "content": self.backend, "objects": True}) + embeddings.index([{"object": "binary data".encode("utf-8")}]) + + # Decode and test extracted object + obj = embeddings.search("select object from txtai where id = 0")[0]["object"] + self.assertEqual(str(obj.getvalue(), "utf-8"), "binary data") + + @patch.dict(os.environ, {"ALLOW_PICKLE": "True"}) + def testPickle(self): + """ + Test pickle configuration + """ + + embeddings = Embeddings( + { + "format": "pickle", + "path": "sentence-transformers/nli-mpnet-base-v2", + "content": self.backend, + } + ) + + embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), f"embeddings.{self.category()}.pickle") + + embeddings.save(index) + + # Check that config exists + self.assertTrue(os.path.exists(os.path.join(index, "config"))) + + # Check that index can be reloaded + embeddings.load(index) + self.assertEqual(embeddings.count(), 6) + + def testQuantize(self): + """ + Test scalar quantization + """ + + # Index data with 1-bit scalar quantization + embeddings = Embeddings({"path": "sentence-transformers/nli-mpnet-base-v2", "quantize": 1, "content": self.backend}) + embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Search for best match + result = self.embeddings.search("feel good story", 1)[0] + self.assertEqual(result["text"], self.data[4]) + + def testQueryModel(self): + """ + Test index + """ + + embeddings = Embeddings( + {"path": "sentence-transformers/nli-mpnet-base-v2", "content": self.backend, "query": {"path": "neuml/t5-small-txtsql"}} + ) + + # Create an index for the list of text + embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Search for best match + result = embeddings.search("feel good story with win in text", 1)[0] + + self.assertEqual(result["text"], self.data[4]) + + def testReindex(self): + """ + Test reindex + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Delete records to test indexids still match + self.embeddings.delete(([0, 1])) + + # Reindex + self.embeddings.reindex({"path": "sentence-transformers/nli-mpnet-base-v2"}) + + # Search for best match + result = self.embeddings.search("feel good story", 1)[0] + + self.assertEqual(result["text"], self.data[4]) + + def testSave(self): + """ + Test save + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), f"embeddings.{self.category()}") + + self.embeddings.save(index) + self.embeddings.load(index) + + # Search for best match + result = self.embeddings.search("feel good story", 1)[0] + + self.assertEqual(result["text"], self.data[4]) + + # Test offsets still work after save/load + self.embeddings.upsert([(0, "Looking out into the dreadful abyss", None)]) + self.assertEqual(self.embeddings.count(), len(self.data)) + + def testSettings(self): + """ + Test custom SQLite settings + """ + + # Index with write-ahead logging enabled + embeddings = Embeddings({"path": "sentence-transformers/nli-mpnet-base-v2", "content": self.backend, "sqlite": {"wal": True}}) + + # Create an index for the list of text + embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Search for best match + result = embeddings.search("feel good story", 1)[0] + + self.assertEqual(result["text"], self.data[4]) + + def testSQL(self): + """ + Test running a SQL query + """ + + # Create an index for the list of text + self.embeddings.index([(uid, {"text": text, "length": len(text), "attribute": f"ID{uid}"}, None) for uid, text in enumerate(self.data)]) + + # Test similar + result = self.embeddings.search( + "select text, score from txtai where similar('feel good story') group by text, score having count(*) > 0 order by score desc", 1 + )[0] + self.assertEqual(result["text"], self.data[4]) + + # Test similar with limits + result = self.embeddings.search("select * from txtai where similar('feel good story', 1) limit 1")[0] + self.assertEqual(result["text"], self.data[4]) + + # Test similar with offset + result = self.embeddings.search("select * from txtai where similar('feel good story') offset 1")[0] + self.assertEqual(result["text"], self.data[5]) + + # Test where + result = self.embeddings.search("select * from txtai where text like '%iceberg%'", 1)[0] + self.assertEqual(result["text"], self.data[1]) + + # Test count + result = self.embeddings.search("select count(*) from txtai")[0] + self.assertEqual(list(result.values())[0], len(self.data)) + + # Test columns + result = self.embeddings.search("select id, text, length, data, entry from txtai")[0] + self.assertEqual(sorted(result.keys()), ["data", "entry", "id", "length", "text"]) + + # Test column filtering + result = self.embeddings.search("select text from txtai where attribute = 'ID4'", 1)[0] + self.assertEqual(result["text"], self.data[4]) + + # Test SQL parse error + with self.assertRaises(SQLError): + self.embeddings.search("select * from txtai where bad,query") + + def testSQLBind(self): + """ + Test SQL statements with bind parameters + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Test similar clause bind parameters + result = self.embeddings.search("select id, text, score from txtai where similar(:x)", parameters={"x": "feel good story"})[0] + self.assertEqual(result["text"], self.data[4]) + + # Test similar clause bind and non-bind parameters + result = self.embeddings.search("select id, text, score from txtai where similar(:x, 0.5)", parameters={"x": "feel good story"})[0] + self.assertEqual(result["text"], self.data[4]) + + # Test where filtering with bind parameters + result = self.embeddings.search("select * from txtai where text like :x", parameters={"x": "%iceberg%"})[0] + self.assertEqual(result["text"], self.data[1]) + + def testSparse(self): + """ + Test sparse vector search + """ + + # Build data array + data = [(uid, text, None) for uid, text in enumerate(self.data)] + + # Index data with sparse vectors + embeddings = Embeddings({"sparse": "sparse-encoder-testing/splade-bert-tiny-nq", "content": self.backend}) + embeddings.index(data) + + # Run search + result = embeddings.search("lottery ticket", 1)[0] + self.assertEqual(result["text"], data[4][1]) + + # Test count method + self.assertEqual(embeddings.count(), len(data)) + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), f"embeddings.{self.category()}.sparse") + + # Test load/save + embeddings.save(index) + embeddings.load(index) + + # Run search + result = embeddings.search("lottery ticket", 1)[0] + self.assertEqual(result["text"], data[4][1]) + + # Update data + data[0] = (0, "Feel good story: baby panda born", None) + embeddings.upsert([data[0]]) + + # Search for best match + result = embeddings.search("feel good story", 1)[0] + self.assertEqual(result["text"], data[0][1]) + + def testSubindex(self): + """ + Test subindex + """ + + # Build data array + data = [(uid, text, None) for uid, text in enumerate(self.data)] + + # Disable top-level indexing and create subindex + embeddings = Embeddings( + {"content": self.backend, "defaults": False, "indexes": {"index1": {"path": "sentence-transformers/nli-mpnet-base-v2"}}} + ) + embeddings.index(data) + + # Test transform + self.assertEqual(embeddings.transform("feel good story").shape, (768,)) + + # Run search + result = embeddings.search("feel good story", 1)[0] + self.assertEqual(result["text"], data[4][1]) + + # Run SQL search + result = embeddings.search("select id, text, score from txtai where similar('feel good story', 10, 0.5)")[0] + self.assertEqual(result["text"], data[4][1]) + + # Test missing index + with self.assertRaises(IndexNotFoundError): + embeddings.search("select id, text, score from txtai where similar('feel good story', 'notindex')") + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), f"embeddings.{self.category()}.subindex") + + # Test load/save + embeddings.save(index) + embeddings.load(index) + + # Run search + result = embeddings.search("feel good story", 1)[0] + self.assertEqual(result["text"], data[4][1]) + + # Update data + data[0] = (0, "Feel good story: baby panda born", None) + embeddings.upsert([data[0]]) + + # Search for best match + result = embeddings.search("feel good story", 1)[0] + self.assertEqual(result["text"], data[0][1]) + + # Check missing text is set to id when top-level indexing is disabled + embeddings.upsert([(embeddings.count(), {"content": "empty text"}, None)]) + result = embeddings.search(f"{embeddings.count() - 1}", 1)[0] + self.assertEqual(result["text"], str(embeddings.count() - 1)) + + # Close embeddings + embeddings.close() + + def testSubindexEmpty(self): + """ + Test loading an empty subindex + """ + + # Build data array + data = [(uid, {"column1": text}, None) for uid, text in enumerate(self.data)] + + # Disable top-level indexing and create subindexes + embeddings = Embeddings( + { + "content": self.backend, + "defaults": False, + "indexes": { + "index1": {"path": "sentence-transformers/nli-mpnet-base-v2", "columns": {"text": "column1"}}, + "index2": {"path": "sentence-transformers/nli-mpnet-base-v2", "columns": {"text": "column2"}}, + }, + } + ) + embeddings.index(data) + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), f"embeddings.{self.category()}.subindexempty") + + # Save index + embeddings.save(index) + + # Test exists + self.assertTrue(embeddings.exists(index)) + + # Load index + embeddings.load(index) + + # Test search + result = embeddings.search("feel good story", 1)[0] + self.assertEqual(result["text"], data[4][1]["text"]) + + def testTerms(self): + """ + Test extracting keyword terms from query + """ + + result = self.embeddings.terms("select * from txtai where similar('keyword terms')") + self.assertEqual(result, "keyword terms") + + def testTruncate(self): + """ + Test dimensionality truncation + """ + + # Truncate vectors to a specified number of dimensions + embeddings = Embeddings( + {"path": "sentence-transformers/nli-mpnet-base-v2", "dimensionality": 750, "content": self.backend, "vectors": {"revision": "main"}} + ) + embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Search for best match + result = self.embeddings.search("feel good story", 1)[0] + self.assertEqual(result["text"], self.data[4]) + + def testUpsert(self): + """ + Test upsert + """ + + # Build data array + data = [(uid, text, None) for uid, text in enumerate(self.data)] + + # Reset embeddings for test + self.embeddings.ann = None + self.embeddings.database = None + + # Create an index for the list of text + self.embeddings.upsert(data) + + # Update data + data[0] = (0, "Feel good story: baby panda born", None) + self.embeddings.upsert([data[0]]) + + # Search for best match + result = self.embeddings.search("feel good story", 1)[0] + self.assertEqual(result["text"], data[0][1]) + + def testUpsertBatch(self): + """ + Test upsert batch + """ + + try: + # Build data array + data = [(uid, text, None) for uid, text in enumerate(self.data)] + + # Reset embeddings for test + self.embeddings.ann = None + self.embeddings.database = None + + # Create an index for the list of text + self.embeddings.upsert(data) + + # Set batch size to 1 + self.embeddings.config["batch"] = 1 + + # Update data + data[0] = (0, "Feel good story: baby panda born", None) + data[1] = (0, "Not good news", None) + self.embeddings.upsert([data[0], data[1]]) + + # Search for best match + result = self.embeddings.search("feel good story", 1)[0] + + self.assertEqual(result["text"], data[0][1]) + finally: + del self.embeddings.config["batch"] + + def category(self): + """ + Content backend category. + + Returns: + category + """ + + return self.__class__.__name__.lower().replace("test", "") diff --git a/test/python/testdatabase/testsql.py b/test/python/testdatabase/testsql.py new file mode 100644 index 0000000..9739ce1 --- /dev/null +++ b/test/python/testdatabase/testsql.py @@ -0,0 +1,325 @@ +""" +SQL module tests +""" + +import unittest + +from txtai.database import DatabaseFactory, SQL, SQLError + + +class TestSQL(unittest.TestCase): + """ + Test SQL parsing and generation. + """ + + @classmethod + def setUpClass(cls): + """ + Initialize test data. + """ + + # Create SQL parser for SQLite + cls.db = DatabaseFactory.create({"content": True}) + cls.db.initialize() + + cls.sql = SQL(cls.db) + + def testAlias(self): + """ + Test alias clauses + """ + + self.assertSql("select", "select a as a1 from txtai", "json_extract(data, '$.a') as a1") + self.assertSql("select", "select a 'a1' from txtai", "json_extract(data, '$.a') 'a1'") + self.assertSql("select", 'select a "a1" from txtai', "json_extract(data, '$.a') \"a1\"") + self.assertSql("select", "select a a1 from txtai", "json_extract(data, '$.a') a1") + self.assertSql( + "select", + "select a, b as b1, c, d + 1 as 'd1' from txtai", + "json_extract(data, '$.a') as \"a\", json_extract(data, '$.b') as b1, " + + "json_extract(data, '$.c') as \"c\", json_extract(data, '$.d') + 1 as 'd1'", + ) + self.assertSql("select", "select id as myid from txtai", "s.id as myid") + self.assertSql("select", "select length(a) t from txtai", "length(json_extract(data, '$.a')) t") + + self.assertSql("where", "select id as myid from txtai where myid != 3 and a != 1", "myid != 3 and json_extract(data, '$.a') != 1") + self.assertSql("where", "select txt T from txtai where t LIKE '%abc%'", "t LIKE '%abc%'") + self.assertSql("where", "select txt 'T' from txtai where t LIKE '%abc%'", "t LIKE '%abc%'") + self.assertSql("where", "select txt \"T\" from txtai where t LIKE '%abc%'", "t LIKE '%abc%'") + self.assertSql("where", "select txt as T from txtai where t LIKE '%abc%'", "t LIKE '%abc%'") + self.assertSql("where", "select txt as 'T' from txtai where t LIKE '%abc%'", "t LIKE '%abc%'") + self.assertSql("where", "select txt as \"T\" from txtai where t LIKE '%abc%'", "t LIKE '%abc%'") + + self.assertSql("groupby", "select id as myid, count(*) from txtai group by myid, a", "myid, json_extract(data, '$.a')") + self.assertSql("orderby", "select id as myid from txtai order by myid, a", "myid, json_extract(data, '$.a')") + + def testBadSQL(self): + """ + Test invalid SQL + """ + + with self.assertRaises(SQLError): + self.db.search("select * from txtai where order by") + + with self.assertRaises(SQLError): + self.db.search("select * from txtai where groupby order by") + + with self.assertRaises(SQLError): + self.db.search("select * from txtai where a(1)") + + with self.assertRaises(SQLError): + self.db.search("select a b c from txtai where id match id") + + def testBracket(self): + """ + Test bracket expressions + """ + + self.assertSql("select", "select [a] from txtai", "json_extract(data, '$.a') as \"a\"") + self.assertSql("select", "select [a] ab from txtai", "json_extract(data, '$.a') ab") + self.assertSql("select", "select [abc] from txtai", "json_extract(data, '$.abc') as \"abc\"") + self.assertSql("select", "select [id], text, score from txtai", "s.id, text, score") + self.assertSql("select", "select [ab cd], text, score from txtai", "json_extract(data, '$.ab cd') as \"ab cd\", text, score") + self.assertSql("select", "select [a[0]] from txtai", "json_extract(data, '$.a[0]') as \"a[0]\"") + self.assertSql("select", "select [a[0].ab] from txtai", "json_extract(data, '$.a[0].ab') as \"a[0].ab\"") + self.assertSql("select", "select [a[0].c[0]] from txtai", "json_extract(data, '$.a[0].c[0]') as \"a[0].c[0]\"") + self.assertSql("select", "select avg([a]) from txtai", "avg(json_extract(data, '$.a')) as \"avg([a])\"") + + # Test single quote escaping in bracket expressions + self.assertSql("select", "select [field'] from txtai", "json_extract(data, '$.field''') as \"field'\"") + + self.assertSql("where", "select * from txtai where [a b] < 1 or a > 1", "json_extract(data, '$.a b') < 1 or json_extract(data, '$.a') > 1") + self.assertSql("where", "select [a[0].c[0]] a from txtai where a < 1", "a < 1") + self.assertSql("groupby", "select * from txtai group by [a]", "json_extract(data, '$.a')") + self.assertSql("orderby", "select * from txtai where order by [a]", "json_extract(data, '$.a')") + + def testDistinct(self): + """ + Test distinct expressions + """ + + # Attributes + self.assertSql("select", "select distinct id from txtai", "distinct s.id") + self.assertSql("select", "select distinct id as myid from txtai", "distinct s.id as myid") + self.assertSql("select", "select distinct a from txtai", "distinct json_extract(data, '$.a') as \"a\"") + self.assertSql("select", "select distinct a.b from txtai", "distinct json_extract(data, '$.a.b') as \"a.b\"") + + # Bracket expression + self.assertSql("select", "select distinct [ab cd] from txtai", "distinct json_extract(data, '$.ab cd') as \"distinct[ab cd]\"") + + # Function expression + self.assertSql("select", "select distinct(id) from txtai", 'distinct(s.id) as "distinct(id)"') + self.assertSql("select", "select count(distinct id) from txtai", 'count(distinct s.id) as "count(distinct id)"') + self.assertSql("select", "select count(distinct a) from txtai", "count(distinct json_extract(data, '$.a')) as \"count(distinct a)\"") + self.assertSql("select", "select count(distinct avg(id)) from txtai", 'count(distinct avg(s.id)) as "count(distinct avg(id))"') + self.assertSql( + "select", "select count(distinct avg(a)) from txtai", "count(distinct avg(json_extract(data, '$.a'))) as \"count(distinct avg(a))\"" + ) + + # Compound expression + self.assertSql("select", "select distinct a/1 from txtai", "distinct json_extract(data, '$.a') / 1 as \"a / 1\"") + self.assertSql("select", "select distinct(a/1) from txtai", "distinct(json_extract(data, '$.a') / 1) as \"distinct(a / 1)\"") + + def testGroupby(self): + """ + Test group by clauses + """ + + prefix = "select count(*), flag from txtai " + + self.assertSql("groupby", prefix + "group by text", "text") + self.assertSql("groupby", prefix + "group by distinct(a)", "distinct(json_extract(data, '$.a'))") + self.assertSql("groupby", prefix + "where a > 1 group by text", "text") + + def testHaving(self): + """ + Test having clauses + """ + + prefix = "select count(*), flag from txtai " + + self.assertSql("having", prefix + "group by text having count(*) > 1", "count(*) > 1") + self.assertSql("having", prefix + "where flag = 1 group by text having count(*) > 1", "count(*) > 1") + + def testIsSQL(self): + """ + Test SQL detection method. + """ + + self.assertTrue(self.sql.issql("select text from txtai where id = 1")) + self.assertFalse(self.sql.issql(1234)) + + def testLimit(self): + """ + Test limit clauses + """ + + prefix = "select count(*) from txtai " + + self.assertSql("limit", prefix + "limit 100", "100") + + def testOffset(self): + """ + Test offset clauses + """ + + prefix = "select count(*) from txtai " + + self.assertSql("offset", prefix + "limit 100 offset 50", "50") + self.assertSql("offset", prefix + "offset 50", "50") + + def testOrderby(self): + """ + Test order by clauses + """ + + prefix = "select * from txtai " + + self.assertSql("orderby", prefix + "order by id", "s.id") + self.assertSql("orderby", prefix + "order by id, text", "s.id, text") + self.assertSql("orderby", prefix + "order by id asc", "s.id asc") + self.assertSql("orderby", prefix + "order by id desc", "s.id desc") + self.assertSql("orderby", prefix + "order by id asc, text desc", "s.id asc, text desc") + + def testSelectBasic(self): + """ + Test basic select clauses + """ + + self.assertSql("select", "select id, indexid, tags from txtai", "s.id, s.indexid, s.tags") + self.assertSql("select", "select id, indexid, flag from txtai", "s.id, s.indexid, json_extract(data, '$.flag') as \"flag\"") + self.assertSql("select", "select id, indexid, a.b.c from txtai", "s.id, s.indexid, json_extract(data, '$.a.b.c') as \"a.b.c\"") + self.assertSql("select", "select 'id', [id], (id) from txtai", "'id', s.id, (s.id)") + self.assertSql("select", "select * from txtai", "*") + + def testSelectCompound(self): + """ + Test compound select clauses + """ + + self.assertSql("select", "select a + 1 from txtai", "json_extract(data, '$.a') + 1 as \"a + 1\"") + self.assertSql("select", "select 1 * a from txtai", "1 * json_extract(data, '$.a') as \"1 * a\"") + self.assertSql("select", "select a/1 from txtai", "json_extract(data, '$.a') / 1 as \"a / 1\"") + self.assertSql("select", "select avg(a-b) from txtai", "avg(json_extract(data, '$.a') - json_extract(data, '$.b')) as \"avg(a - b)\"") + self.assertSql("select", "select distinct(text) from txtai", "distinct(text)") + self.assertSql("select", "select id, score, (a/2)*3 from txtai", "s.id, score, (json_extract(data, '$.a') / 2) * 3 as \"(a / 2) * 3\"") + self.assertSql("select", "select id, score, (a/2*3) from txtai", "s.id, score, (json_extract(data, '$.a') / 2 * 3) as \"(a / 2 * 3)\"") + self.assertSql( + "select", + "select func(func2(indexid + 1), a) from txtai", + "func(func2(s.indexid + 1), json_extract(data, '$.a')) as \"func(func2(indexid + 1), a)\"", + ) + self.assertSql("select", "select func(func2(indexid + 1), a) a from txtai", "func(func2(s.indexid + 1), json_extract(data, '$.a')) a") + self.assertSql("select", "select 'prefix' || id from txtai", "'prefix' || s.id as \"'prefix' || id\"") + self.assertSql("select", "select 'prefix' || id id from txtai", "'prefix' || s.id id") + self.assertSql("select", "select 'prefix' || a a from txtai", "'prefix' || json_extract(data, '$.a') a") + + def testSimilar(self): + """ + Test similar functions + """ + + prefix = "select * from txtai " + + self.assertSql("where", prefix + "where similar('abc')", "__SIMILAR__0") + self.assertSql("similar", prefix + "where similar('abc')", [["abc"]]) + + self.assertSql("where", prefix + "where similar('abc') AND id = 1", "__SIMILAR__0 AND s.id = 1") + self.assertSql("similar", prefix + "where similar('abc')", [["abc"]]) + + self.assertSql("where", prefix + "where similar('abc') and similar('def')", "__SIMILAR__0 and __SIMILAR__1") + self.assertSql("similar", prefix + "where similar('abc') and similar('def')", [["abc"], ["def"]]) + + self.assertSql("where", prefix + "where similar('abc', 1000)", "__SIMILAR__0") + self.assertSql("similar", prefix + "where similar('abc', 1000)", [["abc", "1000"]]) + + self.assertSql("where", prefix + "where similar('abc', 1000) and similar('def', 10)", "__SIMILAR__0 and __SIMILAR__1") + self.assertSql("similar", prefix + "where similar('abc', 1000) and similar('def', 10)", [["abc", "1000"], ["def", "10"]]) + + self.assertSql("where", prefix + "where coalesce(similar('abc'), similar('abc'))", "coalesce(__SIMILAR__0, __SIMILAR__1)") + self.assertSql("similar", prefix + "where coalesce(similar('abc'), similar('abc'))", [["abc"], ["abc"]]) + + def testUnterminated(self): + """ + Test unterminated clauses + """ + + # Unterminated bracket expressions + with self.assertRaises(SQLError): + self.db.search("select [a from txtai") + + with self.assertRaises(SQLError): + self.db.search("select avg([a) from txtai") + + with self.assertRaises(SQLError): + self.db.search("select [a[0] from txtai") + + # Unterminated function expressions + with self.assertRaises(SQLError): + self.db.search("select func(a from txtai") + + with self.assertRaises(SQLError): + self.db.search("select * from txtai where coalesce(a") + + # Unterminated similar clause + with self.assertRaises(SQLError): + self.db.search("select * from txtai where similar('abc'") + + def testUpper(self): + """ + Test SQL statements are case insensitive. + """ + + self.assertSql("groupby", "SELECT * FROM TXTAI WHERE a = 1 GROUP BY id", "s.id") + self.assertSql("orderby", "SELECT * FROM TXTAI WHERE a = 1 ORDER BY id", "s.id") + + def testWhereBasic(self): + """ + Test basic where clauses + """ + + prefix = "select * from txtai " + + self.assertSql("where", prefix + "where a = b", "json_extract(data, '$.a') = json_extract(data, '$.b')") + self.assertSql("where", prefix + "where abc = def", "json_extract(data, '$.abc') = json_extract(data, '$.def')") + self.assertSql("where", prefix + "where a = b.value", "json_extract(data, '$.a') = json_extract(data, '$.b.value')") + self.assertSql("where", prefix + "where a = 1", "json_extract(data, '$.a') = 1") + self.assertSql("where", prefix + "WHERE 1 = a", "1 = json_extract(data, '$.a')") + self.assertSql("where", prefix + "WHERE a LIKE 'abc'", "json_extract(data, '$.a') LIKE 'abc'") + self.assertSql("where", prefix + "WHERE a NOT LIKE 'abc'", "json_extract(data, '$.a') NOT LIKE 'abc'") + self.assertSql("where", prefix + "WHERE a IN (1, 2, 3, b)", "json_extract(data, '$.a') IN (1, 2, 3, json_extract(data, '$.b'))") + self.assertSql("where", prefix + "WHERE a is not null", "json_extract(data, '$.a') is not null") + self.assertSql("where", prefix + "WHERE score >= 0.15", "score >= 0.15") + + def testWhereCompound(self): + """ + Test compound where clauses + """ + + prefix = "select * from txtai " + + self.assertSql("where", prefix + "where a > (b + 1)", "json_extract(data, '$.a') > (json_extract(data, '$.b') + 1)") + self.assertSql("where", prefix + "where a > func('abc')", "json_extract(data, '$.a') > func('abc')") + self.assertSql( + "where", prefix + "where (id = 1 or id = 2) and a like 'abc'", "(s.id = 1 or s.id = 2) and json_extract(data, '$.a') like 'abc'" + ) + self.assertSql( + "where", + prefix + "where a > f(d(b, c, 1),1)", + "json_extract(data, '$.a') > f(d(json_extract(data, '$.b'), json_extract(data, '$.c'), 1), 1)", + ) + self.assertSql("where", prefix + "where (id = 1 AND id = 2) OR indexid = 3", "(s.id = 1 AND s.id = 2) OR s.indexid = 3") + self.assertSql("where", prefix + "where f(id) = b(id)", "f(s.id) = b(s.id)") + self.assertSql("where", prefix + "WHERE f(id)", "f(s.id)") + + def assertSql(self, clause, query, expected): + """ + Helper method to assert a query clause is as expected. + + Args: + clause: clause to select + query: input query + expected: expected transformed query value + """ + + self.assertEqual(self.sql(query)[clause], expected) diff --git a/test/python/testdatabase/testsqlite.py b/test/python/testdatabase/testsqlite.py new file mode 100644 index 0000000..732a6ef --- /dev/null +++ b/test/python/testdatabase/testsqlite.py @@ -0,0 +1,73 @@ +""" +SQLite module tests +""" + +from txtai.embeddings import Embeddings + +from .testrdbms import Common + + +# pylint: disable=R0904 +class TestSQLite(Common.TestRDBMS): + """ + Embeddings with content stored in SQLite tests. + """ + + @classmethod + def setUpClass(cls): + """ + Initialize test data. + """ + + cls.data = [ + "US tops 5 million confirmed virus cases", + "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg", + "Beijing mobilises invasion craft along coast as Taiwan tensions escalate", + "The National Park Service warns against sacrificing slower friends in a bear attack", + "Maine man wins $1M from $25 lottery ticket", + "Make huge profits without work, earn up to $100,000 a day", + ] + + # Content backend + cls.backend = "sqlite" + + # Create embeddings model, backed by sentence-transformers & transformers + cls.embeddings = Embeddings({"path": "sentence-transformers/nli-mpnet-base-v2", "content": cls.backend}) + + @classmethod + def tearDownClass(cls): + """ + Cleanup data. + """ + + if cls.embeddings: + cls.embeddings.close() + + def testFunction(self): + """ + Test custom functions + """ + + embeddings = Embeddings( + { + "path": "sentence-transformers/nli-mpnet-base-v2", + "content": self.backend, + "functions": [{"name": "textlength", "function": "testdatabase.testsqlite.length"}], + } + ) + + # Create an index for the list of text + embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Search for best match + result = embeddings.search("select textlength(text) length from txtai where id = 0", 1)[0] + + self.assertEqual(int(result["length"]), 39) + + +def length(text): + """ + Custom SQL function. + """ + + return len(text) diff --git a/test/python/testembeddings.py b/test/python/testembeddings.py new file mode 100644 index 0000000..7834e89 --- /dev/null +++ b/test/python/testembeddings.py @@ -0,0 +1,679 @@ +""" +Embeddings module tests +""" + +import json +import os +import tempfile +import unittest + +from unittest.mock import patch + +import numpy as np + +from txtai.embeddings import Embeddings, Reducer +from txtai.serialize import SerializeFactory + + +# pylint: disable=R0904 +class TestEmbeddings(unittest.TestCase): + """ + Embeddings tests. + """ + + @classmethod + def setUpClass(cls): + """ + Initialize test data. + """ + + cls.data = [ + "US tops 5 million confirmed virus cases", + "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg", + "Beijing mobilises invasion craft along coast as Taiwan tensions escalate", + "The National Park Service warns against sacrificing slower friends in a bear attack", + "Maine man wins $1M from $25 lottery ticket", + "Make huge profits without work, earn up to $100,000 a day", + ] + + # Create embeddings model, backed by sentence-transformers & transformers + cls.embeddings = Embeddings({"path": "sentence-transformers/nli-mpnet-base-v2"}) + + @classmethod + def tearDownClass(cls): + """ + Cleanup data. + """ + + if cls.embeddings: + cls.embeddings.close() + + def testAutoId(self): + """ + Test auto id generation + """ + + # Default sequence id + embeddings = Embeddings() + embeddings.index(self.data) + + uid = embeddings.search(self.data[4], 1)[0][0] + self.assertEqual(uid, 4) + + # UUID + embeddings = Embeddings(autoid="uuid4") + embeddings.index(self.data) + + uid = embeddings.search(self.data[4], 1)[0][0] + self.assertEqual(len(uid), 36) + + def testColumns(self): + """ + Test custom text/object columns + """ + + embeddings = Embeddings({"keyword": True, "columns": {"text": "value"}}) + data = [{"value": x} for x in self.data] + embeddings.index([(uid, text, None) for uid, text in enumerate(data)]) + + # Run search + uid = embeddings.search("lottery", 1)[0][0] + self.assertEqual(uid, 4) + + def testContext(self): + """ + Test embeddings context manager + """ + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), "embeddings.context") + + with Embeddings() as embeddings: + embeddings.index(self.data) + embeddings.save(index) + + with Embeddings().load(index) as embeddings: + uid = embeddings.search(self.data[4], 1)[0][0] + self.assertEqual(uid, 4) + + def testDefaults(self): + """ + Test default configuration + """ + + # Run index with no config which will fall back to default configuration + embeddings = Embeddings() + embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + self.assertEqual(embeddings.count(), 6) + + def testDelete(self): + """ + Test delete + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Delete best match + self.embeddings.delete([4]) + + # Search for best match + uid = self.embeddings.search("feel good story", 1)[0][0] + + self.assertEqual(self.embeddings.count(), 5) + self.assertEqual(uid, 5) + + def testDense(self): + """ + Test dense alias + """ + + # Dense flag is an alias for path + embeddings = Embeddings(dense="sentence-transformers/nli-mpnet-base-v2") + embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + self.assertEqual(embeddings.count(), 6) + + def testEmpty(self): + """ + Test empty index + """ + + # Test search against empty index + embeddings = Embeddings({"path": "sentence-transformers/nli-mpnet-base-v2"}) + self.assertEqual(embeddings.search("test"), []) + + # Test index with no data + embeddings.index([]) + self.assertIsNone(embeddings.ann) + + # Test upsert with no data + embeddings.index([(0, "this is a test", None)]) + embeddings.upsert([]) + self.assertIsNotNone(embeddings.ann) + + def testEmptyString(self): + """ + Test empty string indexing + """ + + # Test empty string + self.embeddings.index([(0, "", None)]) + self.assertTrue(self.embeddings.search("test")) + + # Test empty string with dict + self.embeddings.index([(0, {"text": ""}, None)]) + self.assertTrue(self.embeddings.search("test")) + + def testExternal(self): + """ + Test embeddings backed by external vectors + """ + + def transform(data): + embeddings = [] + for text in data: + # Create dummy embedding using sum and mean of character ordinals + ordinals = [ord(c) for c in text] + embeddings.append(np.array([sum(ordinals), np.mean(ordinals)])) + + return embeddings + + # Index data using simple embeddings transform method + embeddings = Embeddings({"method": "external", "transform": transform}) + embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Run search + uid = embeddings.search(self.data[4], 1)[0][0] + self.assertEqual(uid, 4) + + def testExternalPrecomputed(self): + """ + Test embeddings backed by external pre-computed vectors + """ + + # Test with no transform function + data = np.random.rand(5, 10).astype(np.float32) + + embeddings = Embeddings({"method": "external"}) + embeddings.index([(uid, row, None) for uid, row in enumerate(data)]) + + # Run search + uid = embeddings.search(data[4], 1)[0][0] + self.assertEqual(uid, 4) + + def testHybrid(self): + """ + Test hybrid search + """ + + # Build data array + data = [(uid, text, None) for uid, text in enumerate(self.data)] + + # Index data with sparse + dense vectors + embeddings = Embeddings({"path": "sentence-transformers/nli-mpnet-base-v2", "hybrid": True}) + embeddings.index(data) + + # Run search + uid = embeddings.search("feel good story", 1)[0][0] + self.assertEqual(uid, 4) + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), "embeddings.hybrid") + + # Test load/save + embeddings.save(index) + embeddings.load(index) + + # Run search + uid = embeddings.search("feel good story", 1)[0][0] + self.assertEqual(uid, 4) + + # Index data with sparse + dense vectors and unnormalized scores + embeddings.config["scoring"]["normalize"] = False + embeddings.index(data) + + # Run search + uid = embeddings.search("feel good story", 1)[0][0] + self.assertEqual(uid, 4) + + # Index data with sparse + dense vectors and bb25 normalization + embeddings.config["scoring"]["normalize"] = "bb25" + embeddings.index(data) + + # Run search + uid = embeddings.search("canada intact iceberg a", 1)[0][0] + self.assertEqual(uid, 1) + + # Test upsert + data[0] = (0, "Feel good story: baby panda born", None) + embeddings.upsert([data[0]]) + + uid = embeddings.search("feel good story", 1)[0][0] + self.assertEqual(uid, 0) + + def testIds(self): + """ + Test legacy config ids loading + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), "embeddings.ids") + + # Save index + self.embeddings.save(index) + + # Set ids on config to simulate legacy ids format + with open(f"{index}/config.json", "r", encoding="utf-8") as handle: + config = json.load(handle) + config["ids"] = list(range(len(self.data))) + + with open(f"{index}/config.json", "w", encoding="utf-8") as handle: + json.dump(config, handle, default=str, indent=2) + + # Reload index + self.embeddings.load(index) + + # Run search + uid = self.embeddings.search("feel good story", 1)[0][0] + self.assertEqual(uid, 4) + + # Check that ids is not in config + self.assertTrue("ids" not in self.embeddings.config) + + @patch.dict(os.environ, {"ALLOW_PICKLE": "True"}) + def testIdsPickle(self): + """ + Test legacy pickle ids + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), "embeddings.idspickle") + + # Save index + self.embeddings.save(index) + + # Create ids as pickle + path = os.path.join(tempfile.gettempdir(), "embeddings.idspickle", "ids") + serializer = SerializeFactory.create("pickle", allowpickle=True) + serializer.save(self.embeddings.ids.ids, path) + + with self.assertWarns(RuntimeWarning): + self.embeddings.load(index) + + # Run search + uid = self.embeddings.search("feel good story", 1)[0][0] + self.assertEqual(uid, 4) + + def testIndex(self): + """ + Test index + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Search for best match + uid = self.embeddings.search("feel good story", 1)[0][0] + + self.assertEqual(uid, 4) + + def testKeyword(self): + """ + Test keyword only (sparse) search + """ + + # Build data array + data = [(uid, text, None) for uid, text in enumerate(self.data)] + + # Index data with sparse keyword vectors + embeddings = Embeddings({"keyword": True}) + embeddings.index(data) + + # Run search + uid = embeddings.search("lottery ticket", 1)[0][0] + self.assertEqual(uid, 4) + + # Test count method + self.assertEqual(embeddings.count(), len(data)) + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), "embeddings.keyword") + + # Test load/save + embeddings.save(index) + embeddings.load(index) + + # Run search + uid = embeddings.search("lottery ticket", 1)[0][0] + self.assertEqual(uid, 4) + + # Update data + data[0] = (0, "Feel good story: baby panda born", None) + embeddings.upsert([data[0]]) + + # Search for best match + uid = embeddings.search("feel good story", 1)[0][0] + self.assertEqual(uid, 0) + + def testQuantize(self): + """ + Test scalar quantization + """ + + for ann in ["faiss", "numpy", "torch"]: + # Index data with 1-bit scalar quantization + embeddings = Embeddings({"path": "sentence-transformers/nli-mpnet-base-v2", "quantize": 1, "backend": ann}) + embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Search for best match + uid = embeddings.search("feel good story", 1)[0][0] + self.assertEqual(uid, 4) + + def testReducer(self): + """ + Test reducer model + """ + + # Test model with single PCA component + data = np.random.rand(5, 5).astype(np.float32) + reducer = Reducer(data, 1) + + # Generate query and keep original data to ensure it changes + query = np.random.rand(1, 5).astype(np.float32) + original = query.copy() + + # Run test + reducer(query) + self.assertFalse(np.array_equal(query, original)) + + # Test model with multiple PCA components + reducer = Reducer(data, 3) + + # Generate query and keep original data to ensure it changes + query = np.random.rand(5).astype(np.float32) + original = query.copy() + + # Run test + reducer(query) + self.assertFalse(np.array_equal(query, original)) + + @patch.dict(os.environ, {"ALLOW_PICKLE": "True"}) + def testReducerLegacy(self): + """ + Test reducer model with legacy model format + """ + + # Test model with single PCA component + data = np.random.rand(5, 5).astype(np.float32) + reducer = Reducer(data, 1) + + # Save legacy format + path = os.path.join(tempfile.gettempdir(), "reducer") + serializer = SerializeFactory.create("pickle", allowpickle=True) + serializer.save(reducer.model, path) + + # Load legacy format + reducer = Reducer() + reducer.load(path) + + # Generate query and keep original data to ensure it changes + query = np.random.rand(1, 5).astype(np.float32) + original = query.copy() + + # Run test + reducer(query) + self.assertFalse(np.array_equal(query, original)) + + def testSave(self): + """ + Test save + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), "embeddings.base") + + self.embeddings.save(index) + self.embeddings.load(index) + + # Search for best match + uid = self.embeddings.search("feel good story", 1)[0][0] + + self.assertEqual(uid, 4) + + # Test offsets still work after save/load + self.embeddings.upsert([(0, "Looking out into the dreadful abyss", None)]) + self.assertEqual(self.embeddings.count(), len(self.data)) + + def testShortcuts(self): + """ + Test embeddings creation shortcuts + """ + + tests = [ + ({"keyword": True}, ["scoring"]), + ({"keyword": "sif"}, ["scoring"]), + ({"sparse": True}, ["scoring"]), + ({"dense": True}, ["ann"]), + ({"hybrid": True}, ["ann", "scoring"]), + ({"hybrid": "tfidf"}, ["ann", "scoring"]), + ({"hybrid": "sparse"}, ["ann", "scoring"]), + ({"graph": True}, ["graph"]), + ] + + for config, checks in tests: + embeddings = Embeddings(config) + embeddings.index(["test"]) + + for attr in checks: + self.assertIsNotNone(getattr(embeddings, attr)) + + def testSimilarity(self): + """ + Test similarity + """ + + # Get best matching id + uid = self.embeddings.similarity("feel good story", self.data)[0][0] + + self.assertEqual(uid, 4) + + def testSparse(self): + """ + Test sparse vector search + """ + + # Build data array + data = [(uid, text, None) for uid, text in enumerate(self.data)] + + # Index data with sparse vectors + embeddings = Embeddings({"sparse": "sparse-encoder-testing/splade-bert-tiny-nq"}) + embeddings.index(data) + + # Run search + uid = embeddings.search("lottery ticket", 1)[0][0] + self.assertEqual(uid, 4) + + # Test count method + self.assertEqual(embeddings.count(), len(data)) + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), "embeddings.sparse") + + # Test load/save + embeddings.save(index) + embeddings.load(index) + + # Run search + uid = embeddings.search("lottery ticket", 1)[0][0] + self.assertEqual(uid, 4) + + # Test similarity + uid = embeddings.similarity("lottery ticket", self.data)[0][0] + self.assertEqual(uid, 4) + + # Update data + data[0] = (0, "Feel good story: baby panda born", None) + embeddings.upsert([data[0]]) + + # Search for best match + uid = embeddings.search("feel good story", 1)[0][0] + self.assertEqual(uid, 0) + + def testSubindex(self): + """ + Test subindex + """ + + # Build data array + data = [(uid, text, None) for uid, text in enumerate(self.data)] + + # Disable top-level indexing and create subindex + embeddings = Embeddings({"defaults": False, "indexes": {"index1": {"path": "sentence-transformers/nli-mpnet-base-v2"}}}) + embeddings.index(data) + + # Test transform + self.assertEqual(embeddings.transform("feel good story").shape, (768,)) + self.assertEqual(embeddings.transform("feel good story", index="index1").shape, (768,)) + with self.assertRaises(KeyError): + embeddings.transform("feel good story", index="index2") + + # Run search + uid = embeddings.search("feel good story", 1)[0][0] + self.assertEqual(uid, 4) + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), "embeddings.subindex") + + # Test load/save + embeddings.save(index) + embeddings.load(index) + + # Run search + uid = embeddings.search("feel good story", 1)[0][0] + self.assertEqual(uid, 4) + + # Update data + data[0] = (0, "Feel good story: baby panda born", None) + embeddings.upsert([data[0]]) + + # Search for best match + uid = embeddings.search("feel good story", 10)[0][0] + self.assertEqual(uid, 0) + + # Check missing text is set to id when top-level indexing is disabled + embeddings.upsert([(embeddings.count(), {"content": "empty text"}, None)]) + uid = embeddings.search(f"{embeddings.count() - 1}", 1)[0][0] + self.assertEqual(uid, embeddings.count() - 1) + + # Close embeddings + embeddings.close() + + def testTruncate(self): + """ + Test dimensionality truncation + """ + + # Truncate vectors to a specified number of dimensions + embeddings = Embeddings({"path": "sentence-transformers/nli-mpnet-base-v2", "dimensionality": 750, "vectors": {"revision": "main"}}) + embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Search for best match + uid = embeddings.search("feel good story", 1)[0][0] + self.assertEqual(uid, 4) + + def testUpsert(self): + """ + Test upsert + """ + + # Build data array + data = [(uid, text, None) for uid, text in enumerate(self.data)] + + # Reset embeddings for test + self.embeddings.ann = None + self.embeddings.ids = None + + # Create an index for the list of text + self.embeddings.upsert(data) + + # Update data + data[0] = (0, "Feel good story: baby panda born", None) + self.embeddings.upsert([data[0]]) + + # Search for best match + uid = self.embeddings.search("feel good story", 1)[0][0] + + self.assertEqual(uid, 0) + + @patch("os.cpu_count") + def testWords(self, cpucount): + """ + Test embeddings backed by word vectors + """ + + # Mock CPU count + cpucount.return_value = 1 + + # Create dataset + data = [(x, row.split(), None) for x, row in enumerate(self.data)] + + # Create embeddings model, backed by word vectors + embeddings = Embeddings({"path": "neuml/glove-6B-quantized", "scoring": "bm25", "pca": 3, "quantize": True}) + + # Call scoring and index methods + embeddings.score(data) + embeddings.index(data) + + # Test search + self.assertIsNotNone(embeddings.search("win", 1)) + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), "embeddings.wordvectors") + + # Test save/load + embeddings.save(index) + embeddings.load(index) + + # Test search + self.assertIsNotNone(embeddings.search("win", 1)) + + @patch("os.cpu_count") + def testWordsUpsert(self, cpucount): + """ + Test embeddings backed by word vectors with upserts + """ + + # Mock CPU count + cpucount.return_value = 1 + + # Create dataset + data = [(x, row.split(), None) for x, row in enumerate(self.data)] + + # Create embeddings model, backed by word vectors + embeddings = Embeddings({"path": "neuml/glove-6B/model.sqlite", "scoring": "bm25", "pca": 3}) + + # Call scoring and index methods + embeddings.score(data) + embeddings.index(data) + + # Now upsert and override record + data = [(0, "win win", None)] + + # Update scoring and run upsert + embeddings.score(data) + embeddings.upsert(data) + + # Test search after upsert + uid = embeddings.search("win", 1)[0][0] + self.assertEqual(uid, 0) diff --git a/test/python/testgraph.py b/test/python/testgraph.py new file mode 100644 index 0000000..62d311b --- /dev/null +++ b/test/python/testgraph.py @@ -0,0 +1,592 @@ +""" +Graph module tests +""" + +import os +import itertools +import tempfile +import unittest + +from unittest.mock import patch + +from txtai.archive import ArchiveFactory +from txtai.embeddings import Embeddings +from txtai.graph import Graph, GraphFactory +from txtai.serialize import SerializeFactory + + +# pylint: disable=R0904 +class TestGraph(unittest.TestCase): + """ + Graph tests. + """ + + @classmethod + def setUpClass(cls): + """ + Initialize test data. + """ + + cls.data = [ + "US tops 5 million confirmed virus cases", + "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg", + "Beijing mobilises invasion craft along coast as Taiwan tensions escalate", + "The National Park Service warns against sacrificing slower friends in a bear attack", + "Maine man wins $1M from $25 lottery ticket", + "Make huge profits without work, earn up to $100,000 a day", + ] + + cls.config = { + "path": "sentence-transformers/nli-mpnet-base-v2", + "content": True, + "functions": [{"name": "graph", "function": "graph.attribute"}], + "expressions": [ + {"name": "category", "expression": "graph(indexid, 'category')"}, + {"name": "topic", "expression": "graph(indexid, 'topic')"}, + {"name": "topicrank", "expression": "graph(indexid, 'topicrank')"}, + ], + "graph": {"limit": 5, "minscore": 0.2, "batchsize": 4, "approximate": False, "topics": {"categories": ["News"], "stopwords": ["the"]}}, + } + + # Create embeddings instance + cls.embeddings = Embeddings(cls.config) + + def testAnalysis(self): + """ + Test analysis methods + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Graph centrality + graph = self.embeddings.graph + centrality = graph.centrality() + self.assertEqual(list(centrality.keys())[0], 5) + + # Page Rank + pagerank = graph.pagerank() + self.assertEqual(list(pagerank.keys())[0], 5) + + # Path between nodes + path = graph.showpath(4, 5) + self.assertEqual(len(path), 2) + + def testCommunity(self): + """ + Test community detection + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Get graph reference + graph = self.embeddings.graph + + # Rebuild topics with updated graph settings + graph.config = {"topics": {"algorithm": "greedy"}} + graph.addtopics() + self.assertEqual(sum((len(graph.topics[x]) for x in graph.topics)), 6) + + graph.config = {"topics": {"algorithm": "lpa"}} + graph.addtopics() + self.assertEqual(sum((len(graph.topics[x]) for x in graph.topics)), 4) + + def testCustomBackend(self): + """ + Test resolving a custom backend + """ + + graph = GraphFactory.create({"backend": "txtai.graph.NetworkX"}) + graph.initialize() + self.assertIsNotNone(graph) + + def testCustomBackendNotFound(self): + """ + Test resolving an unresolvable backend + """ + + with self.assertRaises(ImportError): + graph = GraphFactory.create({"backend": "notfound.graph"}) + graph.initialize() + + def testDatabase(self): + """ + Test creating a Graph backed by a relational database + """ + + # Generate graph database + path = os.path.join(tempfile.gettempdir(), "graph.sqlite") + graph = GraphFactory.create({"backend": "rdbms", "url": f"sqlite:///{path}", "schema": "txtai"}) + + # Initialize the graph + graph.initialize() + + for x in range(5): + graph.addnode(x, field=x) + + for x, y in itertools.combinations(range(5), 2): + graph.addedge(x, y) + + # Test methods + self.assertEqual(list(graph.scan()), [str(x) for x in range(5)]) + self.assertEqual(list(graph.scan(attribute="field")), [str(x) for x in range(5)]) + self.assertEqual(list(graph.filter([0]).scan()), [0]) + + # Test save/load + graph.save(None) + graph.load(None) + self.assertEqual(list(graph.scan()), [str(x) for x in range(5)]) + + # Test remove node + graph.delete([0]) + self.assertFalse(graph.hasnode(0)) + self.assertFalse(graph.hasedge(0)) + + # Close graph + graph.close() + + def testDefault(self): + """ + Test embeddings default graph setting + """ + + embeddings = Embeddings(content=True, graph=True) + embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + self.assertEqual(embeddings.graph.count(), len(self.data)) + + def testDelete(self): + """ + Test delete + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Delete row + self.embeddings.delete([4]) + + # Validate counts + graph = self.embeddings.graph + self.assertEqual(graph.count(), 5) + self.assertEqual(graph.edgecount(), 1) + self.assertEqual(sum((len(graph.topics[x]) for x in graph.topics)), 5) + self.assertEqual(len(graph.categories), 6) + + def testEdges(self): + """ + Test edges + """ + + # Create graph + graph = GraphFactory.create({}) + graph.initialize() + graph.addedge(0, 1) + + # Test edge exists + self.assertTrue(graph.hasedge(0)) + self.assertTrue(graph.hasedge(0, 1)) + + def testFilter(self): + """ + Test creating filtered subgraphs + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Validate counts + graph = self.embeddings.search("feel good story", graph=True) + self.assertEqual(graph.count(), 3) + self.assertEqual(graph.edgecount(), 2) + + def testFunction(self): + """ + Test running graph functions with SQL + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Test function + result = self.embeddings.search("select category, topic, topicrank from txtai where id = 0", 1)[0] + + # Check columns have a value + self.assertIsNotNone(result["category"]) + self.assertIsNotNone(result["topic"]) + self.assertIsNotNone(result["topicrank"]) + + def testFunctionReindex(self): + """ + Test running graph functions with SQL after reindex + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Test functions reset with a reindex + self.embeddings.reindex(self.embeddings.config) + + # Test function + result = self.embeddings.search("select category, topic, topicrank from txtai where id = 0", 1)[0] + + # Check columns have a value + self.assertIsNotNone(result["category"]) + self.assertIsNotNone(result["topic"]) + self.assertIsNotNone(result["topicrank"]) + + def testIndex(self): + """ + Test index + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Validate counts + graph = self.embeddings.graph + self.assertEqual(graph.count(), 6) + self.assertEqual(graph.edgecount(), 2) + self.assertEqual(len(graph.topics), 6) + self.assertEqual(len(graph.categories), 6) + + @patch.dict(os.environ, {"ALLOW_PICKLE": "True"}) + def testLegacy(self): + """ + Test loading a legacy graph in TAR format + """ + + # Create graph + graph = GraphFactory.create({}) + graph.initialize() + graph.addedge(0, 1) + + categories = ["C1"] + topics = {"T1": [0, 1]} + + serializer = SerializeFactory.create("pickle", allowpickle=True) + + # Save files to temporary directory and combine into TAR + path = os.path.join(tempfile.gettempdir(), "graph.tar") + with tempfile.TemporaryDirectory() as directory: + # Save graph + serializer.save(graph.backend, f"{directory}/graph") + + # Save categories, if necessary + serializer.save(categories, f"{directory}/categories") + + # Save topics, if necessary + serializer.save(topics, f"{directory}/topics") + + # Pack files + archive = ArchiveFactory.create(directory) + archive.save(path, "tar") + + # Load loading legacy format + graph = GraphFactory.create({}) + graph.load(path) + + # Validate graph data is correct + self.assertEqual(graph.count(), 2) + self.assertEqual(graph.edgecount(), 1) + self.assertEqual(graph.topics, topics) + self.assertEqual(graph.categories, categories) + + def testNotImplemented(self): + """ + Test exceptions for non-implemented methods + """ + + graph = Graph({}) + + self.assertRaises(NotImplementedError, graph.create) + self.assertRaises(NotImplementedError, graph.count) + self.assertRaises(NotImplementedError, graph.scan, None) + self.assertRaises(NotImplementedError, graph.node, None) + self.assertRaises(NotImplementedError, graph.addnode, None) + self.assertRaises(NotImplementedError, graph.addnodes, None) + self.assertRaises(NotImplementedError, graph.removenode, None) + self.assertRaises(NotImplementedError, graph.hasnode, None) + self.assertRaises(NotImplementedError, graph.attribute, None, None) + self.assertRaises(NotImplementedError, graph.addattribute, None, None, None) + self.assertRaises(NotImplementedError, graph.removeattribute, None, None) + self.assertRaises(NotImplementedError, graph.edgecount) + self.assertRaises(NotImplementedError, graph.edges, None) + self.assertRaises(NotImplementedError, graph.addedge, None, None) + self.assertRaises(NotImplementedError, graph.addedges, None) + self.assertRaises(NotImplementedError, graph.hasedge, None, None) + self.assertRaises(NotImplementedError, graph.centrality) + self.assertRaises(NotImplementedError, graph.pagerank) + self.assertRaises(NotImplementedError, graph.showpath, None, None) + self.assertRaises(NotImplementedError, graph.isquery, None) + self.assertRaises(NotImplementedError, graph.parse, None) + self.assertRaises(NotImplementedError, graph.search, None) + self.assertRaises(NotImplementedError, graph.communities, None) + self.assertRaises(NotImplementedError, graph.load, None) + self.assertRaises(NotImplementedError, graph.save, None) + self.assertRaises(NotImplementedError, graph.loaddict, None) + self.assertRaises(NotImplementedError, graph.savedict) + + def testRelationships(self): + """ + Test manually-provided relationships + """ + + # Create relationships for id 0 + relationships = [{"id": f"ID{x}"} for x in range(1, len(self.data))] + + # Test with content enabled + self.embeddings.index({"id": f"ID{i}", "text": x, "relationships": relationships if i == 0 else None} for i, x in enumerate(self.data)) + self.assertEqual(len(self.embeddings.graph.edges(0)), len(self.data) - 1) + + # Test with content disabled + config = self.config.copy() + config["content"] = False + + embeddings = Embeddings(config) + embeddings.index({"id": f"ID{i}", "text": x, "relationships": relationships if i == 0 else None} for i, x in enumerate(self.data)) + self.assertEqual(len(embeddings.graph.edges(0)), len(self.data) - 1) + embeddings.close() + + def testRelationshipsInvalid(self): + """ + Test manually-provided relationships with no matching id + """ + + # Create relationships for id 0 + relationships = [{"id": "INVALID"}] + + # Index with invalid relationship + self.embeddings.index({"text": x, "relationships": relationships if i == 0 else None} for i, x in enumerate(self.data)) + + # Validate only relationship is semantically-derived + edges = list(self.embeddings.graph.edges(0)) + self.assertTrue(len(edges) == 1 and edges[0] != "INVALID") + + def testResetTopics(self): + """ + Test resetting of topics + """ + + # Create an index for the list of text + self.embeddings.index([(1, "text", None)]) + self.embeddings.upsert([(1, "graph", None)]) + self.assertEqual(list(self.embeddings.graph.topics.keys()), ["graph"]) + + def testSave(self): + """ + Test save + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), "graph") + + # Save and reload index + self.embeddings.save(index) + self.embeddings.load(index) + + # Validate counts + graph = self.embeddings.graph + self.assertEqual(graph.count(), 6) + self.assertEqual(graph.edgecount(), 2) + self.assertEqual(sum((len(graph.topics[x]) for x in graph.topics)), 6) + self.assertEqual(len(graph.categories), 6) + + def testSaveDict(self): + """ + Test loading and saving to dictionaries + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Validate counts + graph = self.embeddings.graph + count, edgecount = graph.count(), graph.edgecount() + + # Save and reload graph as dict + data = graph.savedict() + graph.loaddict(data) + + # Validate counts + self.assertEqual(graph.count(), count) + self.assertEqual(graph.edgecount(), edgecount) + + def testSearch(self): + """ + Test search + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Run standard search + results = self.embeddings.search( + """ + MATCH (A)-[]->(B) + RETURN A, B + """ + ) + self.assertEqual(len(results), 3) + + # Run path search + results = self.embeddings.search( + """ + MATCH P=()-[]->() + RETURN P + """ + ) + self.assertEqual(len(results), 3) + + # Run graph search + g = self.embeddings.search( + """ + MATCH (A)-[]->(B) + RETURN A, ID(B) + """, + graph=True, + ) + self.assertEqual(g.count(), 3) + + # Run path search + results = self.embeddings.search( + """ + MATCH P=()-[]->() + RETURN P + """, + graph=True, + ) + self.assertEqual(g.count(), 3) + + # Run similar search + results = self.embeddings.search( + """ + MATCH P=(A)-[]->() + WHERE SIMILAR(A, "feel good story") + RETURN A + ORDER BY A.score DESC + LIMIT 1 + """, + graph=True, + ) + self.assertEqual(list(results.scan())[0], 4) + + def testSearchBatch(self): + """ + Test batch search + """ + + # Create an index for the list of text + self.embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + # Run standard search + results = self.embeddings.batchsearch( + [ + """ + MATCH (A)-[]->(B) + RETURN A, B + """ + ] + ) + self.assertEqual(len(results[0]), 3) + + def testSimple(self): + """ + Test creating a simple graph + """ + + graph = GraphFactory.create({"topics": {}}) + + # Initialize the graph + graph.initialize() + + for x in range(5): + graph.addnode(x) + + for x, y in itertools.combinations(range(5), 2): + graph.addedge(x, y) + + # Validate counts + self.assertEqual(graph.count(), 5) + self.assertEqual(graph.edgecount(), 10) + + # Test missing edge + self.assertIsNone(graph.edges(100)) + + # Test topics with no text + graph.addtopics() + self.assertEqual(len(graph.topics), 5) + + def testSubindex(self): + """ + Test subindex + """ + + # Build data array + data = [(uid, text, None) for uid, text in enumerate(self.data)] + + embeddings = Embeddings( + { + "content": True, + "functions": [{"name": "graph", "function": "indexes.index1.graph.attribute"}], + "expressions": [ + {"name": "category", "expression": "graph(indexid, 'category')"}, + {"name": "topic", "expression": "graph(indexid, 'topic')"}, + {"name": "topicrank", "expression": "graph(indexid, 'topicrank')"}, + ], + "indexes": { + "index1": { + "path": "sentence-transformers/nli-mpnet-base-v2", + "graph": { + "limit": 5, + "minscore": 0.2, + "batchsize": 4, + "approximate": False, + "topics": {"categories": ["News"], "stopwords": ["the"]}, + }, + } + }, + } + ) + + # Create an index for the list of text + embeddings.index(data) + + # Test function + result = embeddings.search("select id, category, topic, topicrank from txtai where id = 0", 1)[0] + + # Check columns have a value + self.assertIsNotNone(result["category"]) + self.assertIsNotNone(result["topic"]) + self.assertIsNotNone(result["topicrank"]) + + # Update data + data[0] = (0, "Feel good story: lottery winner announced", None) + embeddings.upsert([data[0]]) + + # Test function + result = embeddings.search("select id, category, topic, topicrank from txtai where id = 0", 1)[0] + + # Check columns have a value + self.assertIsNotNone(result["category"]) + self.assertIsNotNone(result["topic"]) + self.assertIsNotNone(result["topicrank"]) + + def testUpsert(self): + """ + Test upsert + """ + + # Update data + self.embeddings.upsert([(0, {"text": "Canadian ice shelf collapses".split()}, None)]) + + # Validate counts + graph = self.embeddings.graph + self.assertEqual(graph.count(), 6) + self.assertEqual(graph.edgecount(), 2) + self.assertEqual(sum((len(graph.topics[x]) for x in graph.topics)), 6) + self.assertEqual(len(graph.categories), 6) diff --git a/test/python/testlibrary.py b/test/python/testlibrary.py new file mode 100644 index 0000000..c2010c0 --- /dev/null +++ b/test/python/testlibrary.py @@ -0,0 +1,102 @@ +""" +Library module tests +""" + +import sys +import unittest + +# pylint: disable=C0415,W0611,W0621 +import txtai + + +class TestLibrary(unittest.TestCase): + """ + Simulates core libraries not being installed. + """ + + @classmethod + def setUpClass(cls): + """ + Simulates core libraries not being installed + """ + + modules = [ + "huggingface_hub", + "huggingface_hub.errors", + "numpy", + "regex", + "safetensors", + "transformers", + "transformers.configuration_utils", + "transformers.modeling_utils", + "transformers.modeling_outputs", + "torch", + "torch.nn", + "torch.onnx", + "torch.utils.data", + "yaml", + ] + + # Get handle to all currently loaded txtai modules + modules = modules + [key for key in sys.modules if key.startswith("txtai")] + cls.modules = {module: None for module in modules} + + # Replace loaded modules with stubs. Save modules for later reloading + for module in cls.modules: + if module in sys.modules: + cls.modules[module] = sys.modules[module] + + # Remove txtai modules. Set optional dependencies to None to prevent reloading. + if "txtai" in module: + if module in sys.modules: + del sys.modules[module] + else: + sys.modules[module] = None + + @classmethod + def tearDownClass(cls): + """ + Resets modules environment back to initial state. + """ + + # Reset replaced modules in setup + for key, value in cls.modules.items(): + if value: + sys.modules[key] = value + else: + del sys.modules[key] + + def testLibrary(self): + """ + Test core libraries not being installed + """ + + # pylint: disable=W0106 + from txtai.util import Library + + lib = Library() + + # Test transformers stubs + for x in [lib.arguments(), lib.config(), lib.dataset(), lib.hferror(), lib.module(), lib.model()]: + self.assertTrue(x.__module__.endswith("library")) + + with self.assertRaises(ImportError): + lib.huggingface_hub().hf_hub_download + + with self.assertRaises(ImportError): + lib.numpy().dot + + with self.assertRaises(ImportError): + lib.regex().compile + + with self.assertRaises(ImportError): + lib.safetensors().safe_open + + with self.assertRaises(ImportError): + lib.torch().device + + with self.assertRaises(ImportError): + lib.transformers().AutoModel + + with self.assertRaises(ImportError): + lib.yaml().safe_open diff --git a/test/python/testmodels/__init__.py b/test/python/testmodels/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/python/testmodels/testmodels.py b/test/python/testmodels/testmodels.py new file mode 100644 index 0000000..c67667b --- /dev/null +++ b/test/python/testmodels/testmodels.py @@ -0,0 +1,48 @@ +""" +Models module tests +""" + +import unittest + +from unittest.mock import patch + +import torch + +from txtai.models import Models + + +class TestModels(unittest.TestCase): + """ + Models tests. + """ + + @patch("torch.cuda.is_available") + def testDeviceid(self, cuda): + """ + Test the deviceid method + """ + + cuda.return_value = True + self.assertEqual(Models.deviceid(True), 0) + self.assertEqual(Models.deviceid(False), -1) + self.assertEqual(Models.deviceid(0), 0) + self.assertEqual(Models.deviceid(1), 1) + + # Test direct torch device + # pylint: disable=E1101 + self.assertEqual(Models.deviceid(torch.device("cpu")), torch.device("cpu")) + + cuda.return_value = False + self.assertEqual(Models.deviceid(True), -1) + self.assertEqual(Models.deviceid(False), -1) + self.assertEqual(Models.deviceid(0), -1) + self.assertEqual(Models.deviceid(1), -1) + + def testDevice(self): + """ + Test the device method + """ + + # pylint: disable=E1101 + self.assertEqual(Models.device("cpu"), torch.device("cpu")) + self.assertEqual(Models.device(torch.device("cpu")), torch.device("cpu")) diff --git a/test/python/testmodels/testpooling.py b/test/python/testmodels/testpooling.py new file mode 100644 index 0000000..4dfac10 --- /dev/null +++ b/test/python/testmodels/testpooling.py @@ -0,0 +1,123 @@ +""" +Pooling module tests +""" + +import unittest + +from txtai.models import Models, ClsPooling, LastPooling, MeanPooling, PoolingFactory + + +class TestPooling(unittest.TestCase): + """ + Pooling tests. + """ + + @classmethod + def setUpClass(cls): + """ + Initialize device + """ + + # Device id + cls.device = Models.deviceid(True) + + def testCLS(self): + """ + Test CLS pooling + """ + + # Test CLS pooling + pooling = PoolingFactory.create({"path": "flax-sentence-embeddings/multi-qa_v1-MiniLM-L6-cls_dot", "device": self.device}) + self.assertEqual(type(pooling), ClsPooling) + + pooling = PoolingFactory.create({"method": "clspooling", "path": "sentence-transformers/nli-mpnet-base-v2", "device": self.device}) + self.assertEqual(type(pooling), ClsPooling) + + # Test CLS pooling encoding + self.assertEqual(pooling.encode(["test"])[0].shape, (768,)) + + def testLast(self): + """ + Test last pooling + """ + + # Test last pooling + pooling = PoolingFactory.create({"path": "neuml/bert-tiny-sts-last-pooling", "device": self.device}) + self.assertEqual(type(pooling), LastPooling) + + pooling = PoolingFactory.create({"method": "lastpooling", "path": "sentence-transformers/nli-mpnet-base-v2", "device": self.device}) + self.assertEqual(type(pooling), LastPooling) + + # Test last pooling encoding + self.assertEqual(pooling.encode(["test"])[0].shape, (768,)) + + def testLength(self): + """ + Test pooling with max_seq_length + """ + + # Test reading max_seq_length parmaeter + pooling = PoolingFactory.create({"path": "sentence-transformers/nli-mpnet-base-v2", "device": self.device, "maxlength": True}) + self.assertEqual(pooling.maxlength, 75) + + # Test specified maxlength + pooling = PoolingFactory.create({"path": "sentence-transformers/nli-mpnet-base-v2", "device": self.device, "maxlength": 256}) + self.assertEqual(pooling.maxlength, 256) + + # Test max_seq_length is ignored when parameter is omitted + pooling = PoolingFactory.create({"path": "sentence-transformers/nli-mpnet-base-v2", "device": self.device}) + self.assertEqual(pooling.maxlength, 512) + + # Test maxlength when max_seq_length not present + pooling = PoolingFactory.create({"path": "hf-internal-testing/tiny-random-gpt2", "device": self.device, "maxlength": True}) + self.assertEqual(pooling.maxlength, 1024) + + def testMean(self): + """ + Test mean pooling + """ + + # Test mean pooling + pooling = PoolingFactory.create({"path": "sentence-transformers/nli-mpnet-base-v2", "device": self.device}) + self.assertEqual(type(pooling), MeanPooling) + + pooling = PoolingFactory.create( + {"method": "meanpooling", "path": "flax-sentence-embeddings/multi-qa_v1-MiniLM-L6-cls_dot", "device": self.device} + ) + self.assertEqual(type(pooling), MeanPooling) + + def testMuvera(self): + """ + Test late pooling with MUVERA fixed dimensional encoding + """ + + # Test MUVERA encoding + for model in ["neuml/colbert-bert-tiny", "neuml/pylate-bert-tiny"]: + # Test defaults + pooling = PoolingFactory.create({"path": model, "device": self.device}) + self.assertEqual(pooling.encode(["test"], category="query").shape, (1, 10240)) + + # Test custom settings + pooling = PoolingFactory.create( + {"path": model, "device": self.device, "modelargs": {"muvera": {"repetitions": 5, "hashes": 2, "projection": 8}}} + ) + self.assertEqual(pooling.encode(["test"], category="data").shape, (1, 160)) + + def testPrompts(self): + """ + Test instruction prompts + """ + + # Load model with prompts + pooling = PoolingFactory.create({"path": "neuml/bert-tiny-prompts", "device": self.device, "loadprompts": True}) + + # Test prompts are prepended + self.assertEqual(pooling.preencode(["abc"], "query")[0], "query: abc") + self.assertEqual(pooling.preencode(["text"], "data")[0], "document: text") + + # Load model with prompts disabled (default) + pooling = PoolingFactory.create({"path": "neuml/bert-tiny-prompts", "device": self.device}) + + # Test that prompts are not prepended + self.assertEqual(pooling.preencode(["abc"], "query")[0], "abc") + self.assertEqual(pooling.preencode(["text"], "data")[0], "text") diff --git a/test/python/testoptional.py b/test/python/testoptional.py new file mode 100644 index 0000000..560d6f2 --- /dev/null +++ b/test/python/testoptional.py @@ -0,0 +1,414 @@ +""" +Optional module tests +""" + +import sys +import unittest + +# pylint: disable=C0415,W0611,W0621 +import timm +import txtai + + +class TestOptional(unittest.TestCase): + """ + Optional tests. Simulates optional dependencies not being installed. + """ + + @classmethod + def setUpClass(cls): + """ + Simulate optional packages not being installed + """ + + modules = [ + "ai_edge_litert.compiled_model", + "annoy", + "bitsandbytes", + "bs4", + "chonkie", + "croniter", + "docling.document_converter", + "duckdb", + "faiss", + "fastapi", + "ggml", + "gliner", + "grandcypher", + "grand", + "hnswlib", + "httpx", + "imagehash", + "libcloud.storage.providers", + "litellm", + "liteparse", + "litert_lm", + "llama_cpp", + "model2vec", + "msgpack", + "networkx", + "nltk", + "onnxmltools", + "onnxruntime", + "onnxruntime.quantization", + "pandas", + "peft", + "pgvector", + "PIL", + "rich", + "scipy", + "scipy.sparse", + "sentence_transformers", + "sklearn.decomposition", + "smolagents", + "sounddevice", + "soundfile", + "sqlalchemy", + "sqlite_vec", + "staticvectors", + "tika", + "ttstokenizer", + "turbovec", + "xmltodict", + ] + + # Get handle to all currently loaded txtai modules + modules = modules + [key for key in sys.modules if key.startswith("txtai")] + cls.modules = {module: None for module in modules} + + # Replace loaded modules with stubs. Save modules for later reloading + for module in cls.modules: + if module in sys.modules: + cls.modules[module] = sys.modules[module] + + # Remove txtai modules. Set optional dependencies to None to prevent reloading. + if "txtai" in module: + if module in sys.modules: + del sys.modules[module] + else: + sys.modules[module] = None + + @classmethod + def tearDownClass(cls): + """ + Resets modules environment back to initial state. + """ + + # Reset replaced modules in setup + for key, value in cls.modules.items(): + if value: + sys.modules[key] = value + else: + del sys.modules[key] + + def testAgent(self): + """ + Test missing agent dependencies + """ + + from txtai.agent import Agent + + with self.assertRaises(ImportError): + Agent(llm="hf-internal-testing/tiny-random-LlamaForCausalLM", max_steps=1) + + def testANN(self): + """ + Test missing ANN dependencies + """ + + from txtai.ann import ANNFactory, SparseANNFactory + + # Test dense methods + with self.assertRaises(ImportError): + ANNFactory.create({"backend": "annoy"}) + + with self.assertRaises(ImportError): + ANNFactory.create({"backend": "faiss"}) + + with self.assertRaises(ImportError): + ANNFactory.create({"backend": "ggml"}) + + with self.assertRaises(ImportError): + ANNFactory.create({"backend": "hnsw"}) + + with self.assertRaises(ImportError): + ANNFactory.create({"backend": "pgvector"}) + + with self.assertRaises(ImportError): + ANNFactory.create({"backend": "sqlite"}) + + with self.assertRaises(ImportError): + ANNFactory.create({"backend": "torch", "torch": {"quantize": True}}) + + with self.assertRaises(ImportError): + ANNFactory.create({"backend": "turbovec"}) + + # Test sparse methods + with self.assertRaises(ImportError): + SparseANNFactory.create({"backend": "ivfsparse"}) + + with self.assertRaises(ImportError): + SparseANNFactory.create({"backend": "pgsparse"}) + + def testApi(self): + """ + Test missing api dependencies + """ + + with self.assertRaises(ImportError): + import txtai.api + + def testConsole(self): + """ + Test missing console dependencies + """ + + from txtai.console import Console + + with self.assertRaises(ImportError): + Console() + + def testCloud(self): + """ + Test missing cloud dependencies + """ + + from txtai.cloud import ObjectStorage + + with self.assertRaises(ImportError): + ObjectStorage(None) + + def testDatabase(self): + """ + Test missing database dependencies + """ + + from txtai.database import Client, DuckDB, ImageEncoder + + with self.assertRaises(ImportError): + Client({}) + + with self.assertRaises(ImportError): + DuckDB({}) + + with self.assertRaises(ImportError): + ImageEncoder() + + def testGraph(self): + """ + Test missing graph dependencies + """ + + from txtai.graph import GraphFactory, Query + + with self.assertRaises(ImportError): + GraphFactory.create({"backend": "networkx"}) + + with self.assertRaises(ImportError): + GraphFactory.create({"backend": "rdbms"}) + + with self.assertRaises(ImportError): + Query() + + def testModel(self): + """ + Test missing model dependencies + """ + + from txtai.embeddings import Reducer + from txtai.models import OnnxModel + + with self.assertRaises(ImportError): + Reducer() + + with self.assertRaises(ImportError): + OnnxModel(None) + + # pylint: disable=R0915 + def testPipeline(self): + """ + Test missing pipeline dependencies + """ + + from txtai.pipeline import ( + AudioMixer, + AudioStream, + Caption, + Entity, + FileToHTML, + HFOnnx, + HFTrainer, + HTMLToMarkdown, + ImageHash, + LiteLLM, + LiteRT, + LlamaCpp, + Microphone, + MLOnnx, + Objects, + OpenCode, + Segmentation, + Tabular, + TextToAudio, + TextToSpeech, + Transcription, + Translation, + ) + + with self.assertRaises(ImportError): + AudioMixer() + + with self.assertRaises(ImportError): + AudioStream() + + with self.assertRaises(ImportError): + Caption() + + with self.assertRaises(ImportError): + Entity("neuml/gliner-bert-tiny") + + with self.assertRaises(ImportError): + FileToHTML(backend="docling") + + with self.assertRaises(ImportError): + FileToHTML(backend="liteparse") + + with self.assertRaises(ImportError): + FileToHTML(backend="tika") + + with self.assertRaises(ImportError): + HFOnnx()("google/bert_uncased_L-2_H-128_A-2", quantize=True) + + with self.assertRaises(ImportError): + HFTrainer()(None, None, lora=True) + + with self.assertRaises(ImportError): + HTMLToMarkdown() + + with self.assertRaises(ImportError): + ImageHash() + + with self.assertRaises(ImportError): + LiteLLM("huggingface/t5-small") + + with self.assertRaises(ImportError): + LiteRT("litert-community/SmolLM2-360M-Instruct/SmolLM2_360M_instruct.litertlm") + + with self.assertRaises(ImportError): + LlamaCpp("TheBloke/TinyLlama-1.1B-Chat-v0.3-GGUF/tinyllama-1.1b-chat-v0.3.Q2_K.gguf") + + with self.assertRaises(ImportError): + Microphone() + + with self.assertRaises(ImportError): + MLOnnx() + + with self.assertRaises(ImportError): + Objects() + + with self.assertRaises(ImportError): + OpenCode("opencode") + + with self.assertRaises(ImportError): + Segmentation(sentences=True) + + with self.assertRaises(ImportError): + Segmentation(chunker="token") + + with self.assertRaises(ImportError): + Tabular() + + with self.assertRaises(ImportError): + TextToAudio() + + with self.assertRaises(ImportError): + TextToSpeech() + + with self.assertRaises(ImportError): + Transcription() + + with self.assertRaises(ImportError): + Translation().detect(["test"]) + + def testSerialize(self): + """ + Test missing msgpack dependency + """ + + from txtai.serialize import MessagePack + + with self.assertRaises(ImportError): + MessagePack() + + def testScoring(self): + """ + Test missing scoring dependencies + """ + + from txtai.scoring import ScoringFactory + + with self.assertRaises(ImportError): + ScoringFactory.create({"method": "pgtext"}) + + def testVectors(self): + """ + Test missing vector dependencies + """ + + from txtai.vectors import SparseVectors, VectorsFactory, SparseVectorsFactory + from txtai.util import SparseArray + + # Test dense vectors + with self.assertRaises(ImportError): + VectorsFactory.create({"method": "litellm", "path": "huggingface/sentence-transformers/all-MiniLM-L6-v2"}, None) + + with self.assertRaises(ImportError): + VectorsFactory.create({"method": "litert", "path": "neuml/bert-hash-nano-embeddings-litert/bert-hash-nano-embeddings-int4.tflite"}, None) + + with self.assertRaises(ImportError): + VectorsFactory.create({"method": "llama.cpp", "path": "nomic-ai/nomic-embed-text-v1.5-GGUF/nomic-embed-text-v1.5.Q2_K.gguf"}, None) + + with self.assertRaises(ImportError): + VectorsFactory.create({"method": "model2vec", "path": "minishlab/M2V_base_output"}, None) + + with self.assertRaises(ImportError): + VectorsFactory.create({"method": "sentence-transformers", "path": "sentence-transformers/nli-mpnet-base-v2"}, None) + + with self.assertRaises(ImportError): + VectorsFactory.create({"method": "words"}, None) + + # Test default model + model = VectorsFactory.create({"path": "sentence-transformers/all-MiniLM-L6-v2"}, None) + self.assertIsNotNone(model) + + # Test sparse vectors + with self.assertRaises(ImportError): + SparseVectors(None, None, None) + + with self.assertRaises(ImportError): + SparseVectorsFactory.create({"method": "sentence-transformers", "path": "sparse-encoder-testing/splade-bert-tiny-nq"}, None) + + with self.assertRaises(ImportError): + SparseArray() + + def testWorkflow(self): + """ + Test missing workflow dependencies + """ + + from txtai.workflow import ExportTask, ImageTask, ServiceTask, StorageTask, Workflow + + with self.assertRaises(ImportError): + ExportTask() + + with self.assertRaises(ImportError): + ImageTask() + + with self.assertRaises(ImportError): + ServiceTask() + + with self.assertRaises(ImportError): + StorageTask() + + with self.assertRaises(ImportError): + Workflow([], workers=1).schedule(None, []) diff --git a/test/python/testpipeline/__init__.py b/test/python/testpipeline/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/python/testpipeline/testaudio/__init__.py b/test/python/testpipeline/testaudio/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/python/testpipeline/testaudio/testaudiomixer.py b/test/python/testpipeline/testaudio/testaudiomixer.py new file mode 100644 index 0000000..8cd3289 --- /dev/null +++ b/test/python/testpipeline/testaudio/testaudiomixer.py @@ -0,0 +1,29 @@ +""" +AudioMixer module tests +""" + +import unittest + +import numpy as np + +from txtai.pipeline import AudioMixer + + +class TestAudioStream(unittest.TestCase): + """ + AudioStream tests. + """ + + def testAudioStream(self): + """ + Test mixing audio streams + """ + + audio1 = np.random.rand(2, 5000), 100 + audio2 = np.random.rand(2, 5000), 100 + + mixer = AudioMixer() + audio, rate = mixer((audio1, audio2)) + + self.assertEqual(audio.shape, (2, 5000)) + self.assertEqual(rate, 100) diff --git a/test/python/testpipeline/testaudio/testaudiostream.py b/test/python/testpipeline/testaudio/testaudiostream.py new file mode 100644 index 0000000..18eb54b --- /dev/null +++ b/test/python/testpipeline/testaudio/testaudiostream.py @@ -0,0 +1,37 @@ +""" +AudioStream module tests +""" + +import unittest + +from unittest.mock import patch + +import soundfile as sf + +from txtai.pipeline import AudioStream + +# pylint: disable=C0411 +from utils import Utils + + +class TestAudioStream(unittest.TestCase): + """ + AudioStream tests. + """ + + @patch("sounddevice.play") + def testAudioStream(self, play): + """ + Test playing audio + """ + + play.return_value = True + + # Read audio data + audio, rate = sf.read(Utils.PATH + "/Make_huge_profits.wav") + + stream = AudioStream() + self.assertIsNotNone(stream([(audio, rate), AudioStream.COMPLETE])) + + # Wait for completion + stream.wait() diff --git a/test/python/testpipeline/testaudio/testmicrophone.py b/test/python/testpipeline/testaudio/testmicrophone.py new file mode 100644 index 0000000..7dac5dc --- /dev/null +++ b/test/python/testpipeline/testaudio/testmicrophone.py @@ -0,0 +1,81 @@ +""" +Microphone module tests +""" + +import unittest + +from unittest.mock import patch + +import numpy as np +import soundfile as sf + +from txtai.pipeline import Microphone + +# pylint: disable=C0411 +from utils import Utils + + +class TestMicrophone(unittest.TestCase): + """ + Microphone tests. + """ + + # pylint: disable=C0115,C0116 + @patch("sounddevice.RawInputStream") + def testMicrophone(self, inputstream): + """ + Test listening to microphone + """ + + class RawInputStream: + def __init__(self, **kwargs): + self.args = kwargs + + # Read audio data + self.index, self.passes = 0, 0 + audio, self.samplerate = sf.read(Utils.PATH + "/Make_huge_profits.wav") + + # Convert data to PCM + self.audio = self.int16(audio) + + # Start with random data to test that speech is not detected + self.data = np.concatenate((self.audio * 50, np.zeros(shape=self.audio.shape, dtype=np.int16))) + + def start(self): + pass + + def stop(self): + pass + + def read(self, size): + # Get chunk + chunk = self.data[self.index : self.index + size] + self.index += size + + # Initial pass is random data, 2nd pass is speech data + if self.index > len(self.data): + if not self.passes: + self.index, self.passes = 0, self.passes + 1 + self.data = self.audio + elif self.index >= len(self.audio) * 10: + # Break out of loop if speech continues to not be detected + raise IOError("Data exhausted") + + return chunk, False + + def int16(self, data): + i = np.iinfo(np.int16) + absmax = 2 ** (i.bits - 1) + offset = i.min + absmax + return (data * absmax + offset).clip(i.min, i.max).astype(np.int16) + + # Mock input stream + inputstream.side_effect = RawInputStream + + # Create microphone pipeline and read data + pipeline = Microphone() + data, rate = pipeline() + + # Validate sample rate and length of data + self.assertEqual(len(data), 91220) + self.assertEqual(rate, 16000) diff --git a/test/python/testpipeline/testaudio/testtexttoaudio.py b/test/python/testpipeline/testaudio/testtexttoaudio.py new file mode 100644 index 0000000..2369fa6 --- /dev/null +++ b/test/python/testpipeline/testaudio/testtexttoaudio.py @@ -0,0 +1,26 @@ +""" +TextToAudio module tests +""" + +import unittest + +from txtai.pipeline import TextToAudio + + +class TestTextToAudio(unittest.TestCase): + """ + TextToAudio tests. + """ + + def testTextToAudio(self): + """ + Test generating audio for text + """ + + tta = TextToAudio("hf-internal-testing/tiny-random-MusicgenForConditionalGeneration") + + # Check that data is generated + audio, rate = tta("This is a test") + + self.assertGreater(len(audio), 0) + self.assertEqual(rate, 24000) diff --git a/test/python/testpipeline/testaudio/testtexttospeech.py b/test/python/testpipeline/testaudio/testtexttospeech.py new file mode 100644 index 0000000..09f6d70 --- /dev/null +++ b/test/python/testpipeline/testaudio/testtexttospeech.py @@ -0,0 +1,82 @@ +""" +TextToSpeech module tests +""" + +import unittest + +from unittest.mock import patch + +from txtai.pipeline import TextToSpeech + + +class TestTextToSpeech(unittest.TestCase): + """ + TextToSpeech tests. + """ + + def testESPnet(self): + """ + Test generating speech for text with an ESPnet model + """ + + tts = TextToSpeech() + + # Check that data is generated + speech, rate = tts("This is a test") + + self.assertGreater(len(speech), 0) + self.assertEqual(rate, 22050) + + def testKokoro(self): + """ + Test generating speech for text with a Kokoro model + """ + + tts = TextToSpeech("neuml/kokoro-int8-onnx", maxtokens=2) + + # Check that data is generated + speech, rate = tts("This is a test") + + self.assertGreater(len(speech), 0) + self.assertEqual(rate, 22050) + + @patch("onnxruntime.get_available_providers") + @patch("torch.cuda.is_available") + def testProviders(self, cuda, providers): + """ + Test that GPU provider is detected + """ + + # Test CUDA and onnxruntime-gpu installed + cuda.return_value = True + providers.return_value = ["CUDAExecutionProvider", "CPUExecutionProvider"] + + tts = TextToSpeech() + self.assertEqual(tts.providers()[0][0], "CUDAExecutionProvider") + + def testSpeechT5(self): + """ + Test generating speech for text with a SpeechT5 model + """ + + tts = TextToSpeech("neuml/txtai-speecht5-onnx") + + # Check that data is generated + speech, rate = tts("This is a test") + + self.assertGreater(len(speech), 0) + self.assertEqual(rate, 22050) + + def testStreaming(self): + """ + Test streaming speech generation + """ + + tts = TextToSpeech() + + # Check that data is generated + speech, rate = list(tts("This is a test. And another".split(), stream=True))[0] + + # Check that data is generated + self.assertGreater(len(speech), 0) + self.assertEqual(rate, 22050) diff --git a/test/python/testpipeline/testaudio/testtranscription.py b/test/python/testpipeline/testaudio/testtranscription.py new file mode 100644 index 0000000..ca75056 --- /dev/null +++ b/test/python/testpipeline/testaudio/testtranscription.py @@ -0,0 +1,104 @@ +""" +Transcription module tests +""" + +import unittest + +import numpy as np +import soundfile as sf + +from scipy import signal + +from txtai.pipeline import Transcription + +# pylint: disable=C0411 +from utils import Utils + + +class TestTranscription(unittest.TestCase): + """ + Transcription tests. + """ + + def testArray(self): + """ + Test audio data to text transcription + """ + + transcribe = Transcription() + + # Read audio data + raw, samplerate = sf.read(Utils.PATH + "/Make_huge_profits.wav") + + self.assertEqual(transcribe((raw, samplerate)), "Make huge profits without working make up to one hundred thousand dollars a day") + self.assertEqual(transcribe(raw, samplerate), "Make huge profits without working make up to one hundred thousand dollars a day") + + def testChunks(self): + """ + Test splitting transcription into chunks + """ + + transcribe = Transcription() + + result = transcribe(Utils.PATH + "/Make_huge_profits.wav", join=False)[0] + + self.assertIsInstance(result["raw"], np.ndarray) + self.assertIsNotNone(result["rate"]) + self.assertEqual(result["text"], "Make huge profits without working make up to one hundred thousand dollars a day") + + def testFile(self): + """ + Test audio file to text transcription + """ + + transcribe = Transcription() + + self.assertEqual( + transcribe(Utils.PATH + "/Make_huge_profits.wav"), "Make huge profits without working make up to one hundred thousand dollars a day" + ) + + def testGenerateArguments(self): + """ + Test transcription with generation keyword arguments + """ + + transcribe = Transcription() + + # Read audio data + raw, samplerate = sf.read(Utils.PATH + "/Make_huge_profits.wav") + + self.assertEqual( + transcribe(raw, samplerate, language="English", task="transcribe"), + "Make huge profits without working make up to one hundred thousand dollars a day", + ) + + def testResample(self): + """ + Test resampled audio file to text transcription + """ + + transcribe = Transcription() + + # Read audio data + raw, samplerate = sf.read(Utils.PATH + "/Make_huge_profits.wav") + + # Resample for testing + samples = round(len(raw) * float(22050) / samplerate) + raw, samplerate = signal.resample(raw, samples), 22050 + + self.assertEqual(transcribe(raw, samplerate), "Make huge profits without working make up to one hundred thousand dollars a day") + + def testStereo(self): + """ + Test audio file in stereo to text transcription + """ + + transcribe = Transcription() + + # Read audio data + raw, samplerate = sf.read(Utils.PATH + "/Make_huge_profits.wav") + + # Convert mono to stereo + raw = np.column_stack((raw, raw)) + + self.assertEqual(transcribe(raw, samplerate), "Make huge profits without working make up to one hundred thousand dollars a day") diff --git a/test/python/testpipeline/testdata/__init__.py b/test/python/testpipeline/testdata/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/python/testpipeline/testdata/testfiletohtml.py b/test/python/testpipeline/testdata/testfiletohtml.py new file mode 100644 index 0000000..4e4c5ff --- /dev/null +++ b/test/python/testpipeline/testdata/testfiletohtml.py @@ -0,0 +1,24 @@ +""" +FileToHTML module tests +""" + +import os +import unittest + +from unittest.mock import patch + +from txtai.pipeline.data.filetohtml import Tika + + +class TestFileToHTML(unittest.TestCase): + """ + FileToHTML tests. + """ + + @patch.dict(os.environ, {"TIKA_JAVA": "1112444abc"}) + def testTika(self): + """ + Test the Tika.available returns False when Java is not available + """ + + self.assertFalse(Tika.available()) diff --git a/test/python/testpipeline/testdata/testtabular.py b/test/python/testpipeline/testdata/testtabular.py new file mode 100644 index 0000000..2d70d55 --- /dev/null +++ b/test/python/testpipeline/testdata/testtabular.py @@ -0,0 +1,123 @@ +""" +Tabular module tests +""" + +import unittest + +from txtai.pipeline import Tabular + +# pylint: disable=C0411 +from utils import Utils + + +class TestTabular(unittest.TestCase): + """ + Tabular tests. + """ + + @classmethod + def setUpClass(cls): + """ + Create single tabular instance + """ + + cls.tabular = Tabular("id", ["text"]) + + def testContent(self): + """ + Test parsing additional content + """ + + tabular = Tabular("id", ["text"], True) + + row = {"id": 0, "text": "This is a test", "flag": 1} + + # When content is enabled, both (uid, text, tags) and (uid, data, tags) rows are generated + # given that data doesn't necessarily include the text to index + rows = tabular([row]) + uid, data, _ = rows[1] + + # Data should contain the entire input row + self.assertEqual(uid, 0) + self.assertEqual(data, row) + + # Only select flag field + tabular.content = ["flag"] + rows = tabular([row]) + uid, data, _ = rows[1] + + # Data should only contain a single field, flag + self.assertEqual(uid, 0) + self.assertTrue(list(data.keys()) == ["flag"]) + self.assertEqual(data["flag"], 1) + + def testCSV(self): + """ + Test parsing a CSV file + """ + + rows = self.tabular([Utils.PATH + "/tabular.csv"]) + uid, text, _ = rows[0][0] + + self.assertEqual(uid, 0) + self.assertEqual(text, "The first sentence") + + def testDict(self): + """ + Test parsing a dict + """ + + rows = self.tabular([{"id": 0, "text": "This is a test"}]) + uid, text, _ = rows[0] + + self.assertEqual(uid, 0) + self.assertEqual(text, "This is a test") + + def testInvalid(self): + """ + Test invalid file paths + """ + + with self.assertRaises(ValueError): + self.tabular([Utils.PATH + "/article.pdf"]) + + with self.assertRaises(ValueError): + self.tabular(["https://invalid.path"]) + + def testList(self): + """ + Test parsing a list + """ + + rows = self.tabular([[{"id": 0, "text": "This is a test"}]]) + uid, text, _ = rows[0][0] + + self.assertEqual(uid, 0) + self.assertEqual(text, "This is a test") + + def testMissingColumns(self): + """ + Test rows with uneven or missing columns + """ + + tabular = Tabular("id", ["text"], True) + + rows = tabular([{"id": 0, "text": "This is a test", "metadata": "meta"}, {"id": 1, "text": "This is a test"}]) + + # When content is enabled both (id, text, tag) and (id, data, tag) tuples are generated given that + # data doesn't necessarily include the text to index + _, data, _ = rows[3] + + self.assertIsNone(data["metadata"]) + + def testNoColumns(self): + """ + Test creating text without specifying columns + """ + + tabular = Tabular("id") + rows = tabular([{"id": 0, "text": "This is a test", "summary": "Describes text in more detail"}]) + uid, text, _ = rows[0] + + self.assertEqual(uid, 0) + self.assertEqual(text, "This is a test. Describes text in more detail") diff --git a/test/python/testpipeline/testdata/testtextractor.py b/test/python/testpipeline/testdata/testtextractor.py new file mode 100644 index 0000000..1c2acf2 --- /dev/null +++ b/test/python/testpipeline/testdata/testtextractor.py @@ -0,0 +1,253 @@ +""" +Textractor module tests +""" + +import platform +import unittest + +from txtai.pipeline import Textractor + +# pylint: disable=C0411 +from utils import Utils + + +class TestTextractor(unittest.TestCase): + """ + Textractor tests. + """ + + def testClean(self): + """ + Test text cleaning method + """ + + # Default text cleaning + textractor = Textractor() + self.assertEqual(textractor(" a b c "), "a b c") + + # Require text to be minlength + textractor = Textractor(minlength=10) + self.assertEqual(textractor(" a b c "), None) + + # Disable text cleaning + textractor = Textractor(cleantext=False, minlength=10) + self.assertEqual(textractor(" a b c "), " a b c ") + + def testChonkie(self): + """ + Test a chonkie chunker + """ + + # Test chonkie chunking + textractor = Textractor(chunker="sentence", chunk_size=5, chunk_overlap=0) + self.assertEqual(textractor("This is a test. And another test."), ["This is a test.", "And another test."]) + + # Test bad chunker throws an exception + with self.assertRaises(AttributeError): + textractor = Textractor(chunker="badchunker") + + def testDefault(self): + """ + Test default text extraction + """ + + # Text input + textractor = Textractor(backend=None) + text = textractor(Utils.PATH + "/tabular.csv") + self.assertEqual(len(text), 125) + + # Markdown input + textractor = Textractor(sections=True) + sections = textractor("# Heading 1\nText1\n\n# Heading 2\nText2\n") + + # Check number of sections is as expected + self.assertEqual(len(sections), 2) + + @unittest.skipIf(platform.system() == "Darwin", "Docling skipped on macOS to avoid MPS issues") + def testDocling(self): + """ + Test docling backend + """ + + textractor = Textractor(backend="docling") + + # Extract text and check for Markdown formatting + text = textractor(Utils.PATH + "/article.pdf") + self.assertTrue("## Introducing txtai" in text) + + def testLines(self): + """ + Test extraction to lines + """ + + textractor = Textractor(lines=True) + + # Extract text as lines + lines = textractor(Utils.PATH + "/article.pdf") + + # Check number of lines is as expected + self.assertEqual(len(lines), 35) + + def testLiteParse(self): + """ + Test liteparse backend + """ + + textractor = Textractor(backend="liteparse") + + # Extract text and check for Markdown formatting + text = textractor(Utils.PATH + "/article.pdf") + self.assertTrue("# Introducing txtai" in text) + + def testHTML(self): + """ + Test HTML to Markdown + """ + + # Headings + self.assertMarkdown("

    This is a test

    ", "# This is a test") + self.assertMarkdown("
    This is a test
    ", "###### This is a test") + + # Blockquotes + self.assertMarkdown("
    This is a test
    ", "> This is a test") + + # Lists + self.assertMarkdown("
    • Test1
    • Test2
    ", "- Test1\n- Test2") + self.assertMarkdown("
    1. Test1
    2. Test2
    ", "1. Test1\n2. Test2") + + # Code + self.assertMarkdown("This is a test", "```\nThis is a test\n```") + self.assertMarkdown("
    This is a test
    ", "```\nThis is a test\n```") + + # Tables + self.assertMarkdown( + "
    Header1Header2
    Test1Test2
    ", + "|Header1|Header2|\n|---|---|\n|Test1|Test2|", + ) + + # Ignore list + self.assertMarkdown("", "") + + # Text formatting + self.assertMarkdown("

    This is a test

    ", "This is a test") + self.assertMarkdown("

    This is a test", "This is a **test**") + self.assertMarkdown("

    This is a test

    ", "This is a **test**") + self.assertMarkdown("

    This is a test

    ", "This is a *test*") + self.assertMarkdown("

    This is a test

    ", "This is a *test*") + self.assertMarkdown("

    This is a test", "This is a [test](link)") + + # Collapse to outer tag + self.assertMarkdown("

    This is a test

    ", "This is a **test**") + self.assertMarkdown("

    This is a test

    ", "This is a *test*") + + def testParagraphs(self): + """ + Test extraction to paragraphs + """ + + textractor = Textractor(paragraphs=True) + + # Extract text as paragraphs + paragraphs = textractor(Utils.PATH + "/article.pdf") + + # Check number of paragraphs is as expected + self.assertEqual(len(paragraphs), 11) + + def testSections(self): + """ + Test extraction to sections + """ + + textractor = Textractor(sections=True) + + # Extract as sections + sections = textractor(Utils.PATH + "/document.pdf") + + # Check number of sections is as expected + self.assertEqual(len(sections), 3) + + def testSentences(self): + """ + Test extraction to sentences + """ + + textractor = Textractor(sentences=True) + + # Extract text as sentences + sentences = textractor(Utils.PATH + "/article.pdf") + + # Check number of sentences is as expected + self.assertEqual(len(sentences), 17) + + def testSingle(self): + """ + Test a single extraction with no tokenization of the results + """ + + textractor = Textractor() + + # Extract text as a single block + text = textractor(Utils.PATH + "/article.pdf") + + # Check length of text is as expected + self.assertEqual(len(text), 2471) + + def testTable(self): + """ + Test table extraction + """ + + textractor = Textractor() + + # Extract text as a single block + for name in ["document.docx", "spreadsheet.xlsx"]: + text = textractor(f"{Utils.PATH}/{name}") + + # Check for table header + self.assertTrue("|---|" in text) + + def testTikaFlag(self): + """ + Test legacy tika flag + """ + + textractor = Textractor(tika=True) + self.assertIsNotNone(textractor.html) + + textractor = Textractor(tika=False) + self.assertIsNone(textractor.html) + + def testTuples(self): + """ + Test output tuples + """ + + # Default text cleaning + textractor = Textractor(tuples=True) + + path, text = textractor(Utils.PATH + "/article.pdf") + self.assertEqual(path, Utils.PATH + "/article.pdf") + self.assertEqual(len(text), 2471) + + def testURL(self): + """ + Test parsing a remote URL + """ + + # Test parsing URLs for each backend + for backend in ["docling", "liteparse", "tika"]: + textractor = Textractor(backend=backend) + text = textractor("https://github.com/neuml/txtai") + self.assertTrue("txtai is an all-in-one AI framework" in text) + + def assertMarkdown(self, html, expected): + """ + Helper method to assert generated markdown is as expected. + + Args: + html: input html snippet + expected: expected markdown text + """ + + textractor = Textractor() + self.assertEqual(textractor(f"{html}"), expected) diff --git a/test/python/testpipeline/testdata/testtokenizer.py b/test/python/testpipeline/testdata/testtokenizer.py new file mode 100644 index 0000000..b73f727 --- /dev/null +++ b/test/python/testpipeline/testdata/testtokenizer.py @@ -0,0 +1,68 @@ +""" +Tokenizer module tests +""" + +import unittest + +from txtai.pipeline import Tokenizer + + +class TestTokenizer(unittest.TestCase): + """ + Tokenizer tests. + """ + + def testAlphanumTokenize(self): + """ + Test alphanumeric tokenization + """ + + # Alphanumeric tokenization through backwards compatible static method + self.assertEqual(Tokenizer.tokenize("Y this is a test!"), ["test"]) + self.assertEqual(Tokenizer.tokenize("abc123 ABC 123"), ["abc123", "abc"]) + + def testEmptyTokenize(self): + """ + Test handling empty and None inputs + """ + + # Test that parser can handle empty or None strings + self.assertEqual(Tokenizer.tokenize(""), []) + self.assertEqual(Tokenizer.tokenize(None), None) + + def testStandardTokenize(self): + """ + Test standard tokenization + """ + + # Default standard tokenizer parameters + tokenizer = Tokenizer() + + # Define token tests + tests = [ + ("Y this is a test!", ["y", "this", "is", "a", "test"]), + ("abc123 ABC 123", ["abc123", "abc", "123"]), + ("Testing hy-phenated words", ["testing", "hy", "phenated", "words"]), + ("111-111-1111", ["111", "111", "1111"]), + ("Test.1234", ["test", "1234"]), + ] + + # Run through tests + for test, result in tests: + # Unicode Text Segmentation per Unicode Annex #29 + self.assertEqual(tokenizer(test), result) + + def testNgramTokenize(self): + """ + Test ngram tokenization + """ + + # Standard ngram tokenization + tokenizer = Tokenizer(lowercase=True, ngrams=3) + result = tokenizer("NGRAM TEST") + self.assertIn("ngr", result) + + # Case sensitive ngram tokenization + tokenizer = Tokenizer(lowercase=False, ngrams=3) + result = tokenizer("NGRAM TEST") + self.assertIn("NGR", result) diff --git a/test/python/testpipeline/testdata/testurlretrieve.py b/test/python/testpipeline/testdata/testurlretrieve.py new file mode 100644 index 0000000..5a5ad39 --- /dev/null +++ b/test/python/testpipeline/testdata/testurlretrieve.py @@ -0,0 +1,115 @@ +""" +URLRetrieve module tests +""" + +import contextlib +import unittest + +from http.server import HTTPServer, BaseHTTPRequestHandler +from threading import Thread +from urllib.request import build_opener + +from txtai.pipeline import URLRetrieve +from txtai.pipeline.data.urlretrieve import SafeRedirectHandler + + +class RequestHandler(BaseHTTPRequestHandler): + """ + Test HTTP handler. + """ + + def do_GET(self): + """ + GET request handler. + """ + + if self.path == "/valid": + redirect = "https://github.com/neuml/txtai" + elif self.path == "/invalid": + redirect = "http://127.0.0.1" + else: + redirect = None + + if redirect: + self.send_response(301) + self.send_header("Location", redirect) + self.end_headers() + else: + response = "test".encode("utf-8") + + self.send_response(200) + self.send_header("content-type", "text/plain") + self.send_header("content-length", len(response)) + self.end_headers() + + self.wfile.write(response) + self.wfile.flush() + + +class TestURLRetrieve(unittest.TestCase): + """ + URLRetrieve tests. + """ + + @classmethod + def setUpClass(cls): + """ + Create mock http server + """ + + cls.httpd = HTTPServer(("127.0.0.1", 8006), RequestHandler) + + server = Thread(target=cls.httpd.serve_forever, daemon=True) + server.start() + + @classmethod + def tearDownClass(cls): + """ + Shutdown mock http server. + """ + + cls.httpd.shutdown() + + def testRedirect(self): + """ + Test redirects + """ + + urlretrieve = URLRetrieve(safeopen=True) + + # Test redirect handler + opener = build_opener(SafeRedirectHandler(urlretrieve)) + + # Test valid direct + with contextlib.closing(opener.open("http://127.0.0.1:8006/valid")) as connection: + self.assertTrue("txtai is an all-in-one AI framework" in str(connection.read())) + + # Test invalid redirect + with self.assertRaises(IOError): + contextlib.closing(opener.open("http://127.0.0.1:8006/invalid")) + + def testRetrieve(self): + """ + Test retrieval + """ + + urlretrieve = URLRetrieve() + data = urlretrieve("http://127.0.0.1:8006/data") + self.assertEqual(data, b"test") + + def testSafeopen(self): + """ + Test safeopen checks + """ + + urlretrieve = URLRetrieve(safeopen=True) + + # Verify that local ip addresses fail + with self.assertRaises(IOError): + urlretrieve("http://127.0.0.1") + + with self.assertRaises(IOError): + urlretrieve("https://127.0.0.1") + + with self.assertRaises(IOError): + urlretrieve("https://[::1]") diff --git a/test/python/testpipeline/testimage/__init__.py b/test/python/testpipeline/testimage/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/python/testpipeline/testimage/testcaption.py b/test/python/testpipeline/testimage/testcaption.py new file mode 100644 index 0000000..02241b9 --- /dev/null +++ b/test/python/testpipeline/testimage/testcaption.py @@ -0,0 +1,36 @@ +""" +Caption module tests +""" + +import unittest + +from PIL import Image + +from transformers import AutoModelForImageTextToText, AutoImageProcessor, AutoTokenizer +from txtai.pipeline import Caption + +# pylint: disable=C0411 +from utils import Utils + + +class TestCaption(unittest.TestCase): + """ + Caption tests. + """ + + def testCaption(self): + """ + Test captions + """ + + caption = Caption() + self.assertEqual(caption(Image.open(Utils.PATH + "/books.jpg")), "a book shelf filled with books and a stack of books") + + # Load passing models directly + path = "ydshieh/vit-gpt2-coco-en" + model = AutoModelForImageTextToText.from_pretrained(path) + tokenizer = AutoTokenizer.from_pretrained(path) + processor = AutoImageProcessor.from_pretrained(path) + + caption = Caption((model, tokenizer, processor)) + self.assertEqual(caption(Image.open(Utils.PATH + "/books.jpg")), "a book shelf filled with books and a stack of books") diff --git a/test/python/testpipeline/testimage/testimagehash.py b/test/python/testpipeline/testimage/testimagehash.py new file mode 100644 index 0000000..40ecd40 --- /dev/null +++ b/test/python/testpipeline/testimage/testimagehash.py @@ -0,0 +1,74 @@ +""" +ImageHash module tests +""" + +import unittest + +from PIL import Image + +from txtai.pipeline import ImageHash + +# pylint: disable=C0411 +from utils import Utils + + +class TestImageHash(unittest.TestCase): + """ + ImageHash tests. + """ + + @classmethod + def setUpClass(cls): + """ + Caches an image to hash + """ + + cls.image = Image.open(Utils.PATH + "/books.jpg") + + def testArray(self): + """ + Test numpy return type + """ + + ihash = ImageHash(strings=False) + self.assertEqual(ihash(self.image).shape, (64,)) + + def testAverage(self): + """ + Test average hash + """ + + ihash = ImageHash("average") + self.assertIn(ihash(self.image), ["0859dd04bfbfbf00", "0859dd04ffbfbf00"]) + + def testColor(self): + """ + Test color hash + """ + + ihash = ImageHash("color") + self.assertIn(ihash(self.image), ["1ffffe02000e000c0e0000070000", "1ff8fe03000e00070e0000070000"]) + + def testDifference(self): + """ + Test difference hash + """ + + ihash = ImageHash("difference") + self.assertEqual(ihash(self.image), "d291996d6969686a") + + def testPerceptual(self): + """ + Test perceptual hash + """ + + ihash = ImageHash("perceptual") + self.assertEqual(ihash(self.image), "8be8418577b331b9") + + def testWavelet(self): + """ + Test wavelet hash + """ + + ihash = ImageHash("wavelet") + self.assertEqual(ihash(Utils.PATH + "/books.jpg"), "68015d85bfbf3f00") diff --git a/test/python/testpipeline/testimage/testobjects.py b/test/python/testpipeline/testimage/testobjects.py new file mode 100644 index 0000000..3beeb13 --- /dev/null +++ b/test/python/testpipeline/testimage/testobjects.py @@ -0,0 +1,40 @@ +""" +Objects module tests +""" + +import unittest + +from txtai.pipeline import Objects + +# pylint: disable=C0411 +from utils import Utils + + +class TestObjects(unittest.TestCase): + """ + Object detection tests. + """ + + def testClassification(self): + """ + Test object detection using an image classification model + """ + + objects = Objects(classification=True, threshold=0.3) + self.assertEqual(objects(Utils.PATH + "/books.jpg")[0][0], "library") + + def testDetection(self): + """ + Test object detection using an object detection model + """ + + objects = Objects() + self.assertEqual(objects(Utils.PATH + "/books.jpg")[0][0], "book") + + def testFlatten(self): + """ + Test object detection using an object detection model, flatten to return only objects + """ + + objects = Objects() + self.assertEqual(objects(Utils.PATH + "/books.jpg", flatten=True)[0], "book") diff --git a/test/python/testpipeline/testllm/__init__.py b/test/python/testpipeline/testllm/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/python/testpipeline/testllm/testgenerator.py b/test/python/testpipeline/testllm/testgenerator.py new file mode 100644 index 0000000..af07856 --- /dev/null +++ b/test/python/testpipeline/testllm/testgenerator.py @@ -0,0 +1,24 @@ +""" +Generator module tests +""" + +import unittest + +from txtai.pipeline import Generator + + +class TestGenerator(unittest.TestCase): + """ + Sequences tests. + """ + + def testGeneration(self): + """ + Test text pipeline generation + """ + + model = Generator("hf-internal-testing/tiny-random-gpt2") + start = "Hello, how are" + + # Test that text is generated + self.assertIsNotNone(model(start)) diff --git a/test/python/testpipeline/testllm/testlitellm.py b/test/python/testpipeline/testllm/testlitellm.py new file mode 100644 index 0000000..fca0c24 --- /dev/null +++ b/test/python/testpipeline/testllm/testlitellm.py @@ -0,0 +1,115 @@ +""" +LiteLLM module tests +""" + +import json +import os +import time +import unittest +import uuid + +from unittest.mock import patch + +from http.server import HTTPServer, BaseHTTPRequestHandler +from threading import Thread + +from txtai.pipeline import LLM + + +class RequestHandler(BaseHTTPRequestHandler): + """ + Test HTTP handler. + """ + + def do_POST(self): + """ + POST request handler. + """ + + # Parse input headers + length = int(self.headers["content-length"]) + data = json.loads(self.rfile.read(length)) + + if data.get("stream"): + # Mock streaming response + content = "application/octet-stream" + response = ( + "data: " + + json.dumps( + { + "id": str(uuid.uuid4()), + "object": "chat.completion.chunk", + "created": int(time.time() * 1000), + "model": "test", + "choices": [{"id": 0, "delta": {"content": "blue"}}], + } + ) + + "\n\ndata: [DONE]\n\n" + ) + else: + # Mock standard response + content = "application/json" + response = json.dumps( + { + "id": str(uuid.uuid4()), + "object": "chat.completion", + "created": int(time.time() * 1000), + "model": "test", + "choices": [{"id": 0, "message": {"role": "assistant", "content": "blue"}, "finish_reason": "stop"}], + } + ) + + # Encode response as bytes + response = response.encode("utf-8") + + self.send_response(200) + self.send_header("content-type", content) + self.send_header("content-length", len(response)) + self.end_headers() + + self.wfile.write(response) + self.wfile.flush() + + +class TestLiteLLM(unittest.TestCase): + """ + LiteLLM tests. + """ + + @classmethod + def setUpClass(cls): + """ + Create mock http server. + """ + + cls.httpd = HTTPServer(("127.0.0.1", 8000), RequestHandler) + + server = Thread(target=cls.httpd.serve_forever, daemon=True) + server.start() + + @classmethod + def tearDownClass(cls): + """ + Shutdown mock http server. + """ + + cls.httpd.shutdown() + + @patch.dict(os.environ, {"OPENAI_API_KEY": "test"}) + def testGeneration(self): + """ + Test generation with LiteLLM + """ + + # Test model generation with LiteLLM + model = LLM("openai/gpt-4o", api_base="http://127.0.0.1:8000") + self.assertEqual(model("The sky is"), "blue") + + # Test default role + self.assertEqual(model("The sky is", defaultrole="user"), "blue") + + # Test streaming + self.assertEqual(" ".join(x for x in model("The sky is", stream=True)), "blue") + + # Test vision + self.assertEqual(model.isvision(), False) diff --git a/test/python/testpipeline/testllm/testlitert.py b/test/python/testpipeline/testllm/testlitert.py new file mode 100644 index 0000000..3f35f92 --- /dev/null +++ b/test/python/testpipeline/testllm/testlitert.py @@ -0,0 +1,31 @@ +""" +LiteRT module tests +""" + +import unittest + +from txtai.pipeline import LLM + + +class TestLiteRT(unittest.TestCase): + """ + LiteRT tests. + """ + + def testGeneration(self): + """ + Test generation with LiteRT + """ + + # Test model generation with LiteRT + model = LLM("neuml/gemma-4-tiny-random-litert-lm/gemma-4-tiny-random.litertlm", mtp=False, maxlength=25) + + # Test standard + self.assertIsNotNone(model("Hello")) + + # Test streaming + self.assertIsNotNone(list(model("Hello", stream=True))) + + # Test CPU fallback + model = LLM("neuml/gemma-4-tiny-random-litert-lm/gemma-4-tiny-random.litertlm", mtp=True, maxlength=25) + self.assertIsNotNone(model("Hello")) diff --git a/test/python/testpipeline/testllm/testllama.py b/test/python/testpipeline/testllm/testllama.py new file mode 100644 index 0000000..79636a3 --- /dev/null +++ b/test/python/testpipeline/testllm/testllama.py @@ -0,0 +1,76 @@ +""" +Llama module tests +""" + +import unittest + +from unittest.mock import patch + +from txtai.pipeline import LLM + + +class TestLlama(unittest.TestCase): + """ + llama.cpp tests. + """ + + @patch("llama_cpp.Llama") + def testContext(self, llama): + """ + Test n_ctx with llama.cpp + """ + + class Llama: + """ + Mock llama.cpp instance to test invalid context + """ + + def __init__(self, **kwargs): + if kwargs.get("n_ctx") == 0 or kwargs.get("n_ctx", 0) >= 10000: + raise ValueError("Failed to create context") + + # Save parameters + self.params = kwargs + + # Mock llama.cpp instance + llama.side_effect = Llama + + # Model to test + path = "TheBloke/TinyLlama-1.1B-Chat-v0.3-GGUF/tinyllama-1.1b-chat-v0.3.Q2_K.gguf" + + # Test omitting n_ctx falls back to default settings + llm = LLM(path) + self.assertNotIn("n_ctx", llm.generator.llm.params) + + # Test n_ctx=0 falls back to default settings + llm = LLM(path, n_ctx=0) + self.assertNotIn("n_ctx", llm.generator.llm.params) + + # Test n_ctx manually set + llm = LLM(path, n_ctx=1024) + self.assertEqual(llm.generator.llm.params["n_ctx"], 1024) + + # Mock a value for n_ctx that's too big + with self.assertRaises(ValueError): + llm = LLM(path, n_ctx=10000) + + def testGeneration(self): + """ + Test generation with llama.cpp + """ + + # Test model generation with llama.cpp + model = LLM("TheBloke/TinyLlama-1.1B-Chat-v0.3-GGUF/tinyllama-1.1b-chat-v0.3.Q2_K.gguf", chat_format="chatml") + + # Test with prompt + self.assertEqual(model("2 + 2 = ", maxlength=10, seed=0, stop=["."], defaultrole="prompt")[0], "4") + + # Test with list of messages + messages = [{"role": "system", "content": "You are a helpful assistant. You answer math problems."}, {"role": "user", "content": "2+2?"}] + self.assertIsNotNone(model(messages, maxlength=10, seed=0, stop=["."])) + + # Test default role + self.assertIsNotNone(model("2 + 2 = ", maxlength=10, seed=0, stop=["."], defaultrole="user")) + + # Test streaming + self.assertEqual(" ".join(x for x in model("2 + 2 = ", maxlength=10, stream=True, seed=0, stop=["."], defaultrole="prompt"))[0], "4") diff --git a/test/python/testpipeline/testllm/testllm.py b/test/python/testpipeline/testllm/testllm.py new file mode 100644 index 0000000..d2dc2d9 --- /dev/null +++ b/test/python/testpipeline/testllm/testllm.py @@ -0,0 +1,185 @@ +""" +LLM module tests +""" + +import unittest + +import torch + +from transformers import AutoModelForCausalLM, AutoTokenizer + +from txtai.pipeline import LLM, Generation + +# pylint: disable=C0411 +from utils import Utils + + +class TestLLM(unittest.TestCase): + """ + LLM tests. + """ + + def testArguments(self): + """ + Test pipeline keyword arguments + """ + + start = "Hello, how are" + + # Test that text is generated with custom parameters + model = LLM("hf-internal-testing/tiny-random-gpt2", task="language-generation", dtype="torch.float32") + self.assertIsNotNone(model(start)) + + model = LLM("hf-internal-testing/tiny-random-gpt2", task="language-generation", dtype=torch.float32) + self.assertIsNotNone(model(start)) + + def testBatchSize(self): + """ + Test batch size + """ + + model = LLM("sshleifer/tiny-gpt2") + self.assertIsNotNone(model(["Hello, how are"] * 2, batch_size=2)) + + def testCustom(self): + """ + Test custom LLM framework + """ + + model = LLM("hf-internal-testing/tiny-random-gpt2", task="language-generation", method="txtai.pipeline.HFGeneration") + self.assertIsNotNone(model("Hello, how are")) + + def testCustomNotFound(self): + """ + Test resolving an unresolvable LLM framework + """ + + with self.assertRaises(ImportError): + LLM("hf-internal-testing/tiny-random-gpt2", method="notfound.generation") + + def testDefaultRole(self): + """ + Test default role + """ + + model = LLM("hf-internal-testing/tiny-random-LlamaForCausalLM") + generator = model.generator + + # Validate that the LLM supports chat messages + self.assertEqual(model.ischat(), True) + + messages = [ + ("Hello", list), + ("\n<|im_start|>Hello<|im_end|>", str), + ("<|start|>Hello<|end|>", str), + ("<|start_of_role|>system<|end_of_role|>", str), + ("[INST]Hello[/INST]", str), + ] + + for message, expected in messages: + # Test auto detection of formats + self.assertEqual(type(generator.format([message], "auto")[0]), expected) + + # Test always setting user chat messages + self.assertEqual(type(generator.format([message], "user")[0]), list) + + # Test always keeping as prompt text + self.assertEqual(type(generator.format([message], "prompt")[0]), str) + + def testExternal(self): + """ + Test externally loaded model + """ + + model = AutoModelForCausalLM.from_pretrained("hf-internal-testing/tiny-random-gpt2") + tokenizer = AutoTokenizer.from_pretrained("hf-internal-testing/tiny-random-gpt2") + + model = LLM((model, tokenizer), template="{text}") + start = "Hello, how are" + + # Test that text is generated + self.assertIsNotNone(model(start)) + + def testMaxLength(self): + """ + Test max length + """ + + model = LLM("sshleifer/tiny-gpt2") + self.assertIsInstance(model("Hello, how are", maxlength=10), str) + + def testNotImplemented(self): + """ + Test exceptions for non-implemented methods + """ + + generation = Generation() + self.assertRaises(NotImplementedError, generation.stream, None, None, None, None) + + def testStop(self): + """ + Test stop strings + """ + + model = LLM("sshleifer/tiny-gpt2") + self.assertIsNotNone(model("Hello, how are", stop=["you"])) + + def testStream(self): + """ + Test streaming generation + """ + + model = LLM("sshleifer/tiny-gpt2") + self.assertIsInstance(" ".join(x for x in model("Hello, how are", stream=True)), str) + + def testStripThink(self): + """ + Test stripthink parameter + """ + + # pylint: disable=W0613 + def execute1(*args, **kwargs): + return ["testyou"] + + def execute2(*args, **kwargs): + return ["<|channel|>final<|message|> you"] + + model = LLM("hf-internal-testing/tiny-random-LlamaForCausalLM") + + for method in [execute1, execute2]: + # Override execute method + model.generator.execute = method + self.assertEqual(model("Hello, how are", stripthink=True), "you") + self.assertEqual(model("Hello, how are", stripthink=False), method()[0]) + + def testStripThinkStream(self): + """ + Test stripthink parameter with streaming output + """ + + # pylint: disable=W0613 + def execute1(*args, **kwargs): + yield from "testyou" + + def execute2(*args, **kwargs): + yield from "<|channel|>final<|message|>you" + + model = LLM("hf-internal-testing/tiny-random-LlamaForCausalLM") + + for method in [execute1, execute2]: + # Override execute method + model.generator.execute = method + self.assertEqual("".join(model("Hello, how are", stripthink=True, stream=True)), "you") + self.assertEqual("".join(model("Hello, how are", stripthink=False, stream=True)), "".join(list(method()))) + + def testVision(self): + """ + Test vision LLM + """ + + model = LLM("neuml/tiny-random-qwen2vl") + result = model( + [{"role": "user", "content": [{"type": "text", "text": "What is in this image?"}, {"type": "image", "image": Utils.PATH + "/books.jpg"}]}] + ) + + self.assertIsNotNone(result) diff --git a/test/python/testpipeline/testllm/testopencode.py b/test/python/testpipeline/testllm/testopencode.py new file mode 100644 index 0000000..8e2f949 --- /dev/null +++ b/test/python/testpipeline/testllm/testopencode.py @@ -0,0 +1,71 @@ +""" +OpenCode module tests +""" + +import json +import unittest + +from http.server import HTTPServer, BaseHTTPRequestHandler +from threading import Thread + +from txtai.pipeline import LLM + + +class RequestHandler(BaseHTTPRequestHandler): + """ + Test HTTP handler. + """ + + def do_POST(self): + """ + POST request handler. + """ + + # Mock response + content = "application/json" + response = json.dumps({"id": "0", "parts": [{"type": "text", "text": "blue"}]}) + + # Encode response as bytes + response = response.encode("utf-8") + + self.send_response(200) + self.send_header("content-type", content) + self.send_header("content-length", len(response)) + self.end_headers() + + self.wfile.write(response) + self.wfile.flush() + + +class TestOpenCode(unittest.TestCase): + """ + OpenCode tests. + """ + + @classmethod + def setUpClass(cls): + """ + Create mock http server. + """ + + cls.httpd = HTTPServer(("127.0.0.1", 8005), RequestHandler) + + server = Thread(target=cls.httpd.serve_forever, daemon=True) + server.start() + + @classmethod + def tearDownClass(cls): + """ + Shutdown mock http server. + """ + + cls.httpd.shutdown() + + def testGeneration(self): + """ + Test generation with OpenCode + """ + + # Test model generation with LiteLLM + model = LLM("opencode/big-pickle", url="http://127.0.0.1:8005") + self.assertEqual(model("The sky is"), "blue") diff --git a/test/python/testpipeline/testllm/testrag.py b/test/python/testpipeline/testllm/testrag.py new file mode 100644 index 0000000..6de48b9 --- /dev/null +++ b/test/python/testpipeline/testllm/testrag.py @@ -0,0 +1,225 @@ +""" +RAG module tests +""" + +import platform +import unittest + +from txtai.embeddings import Embeddings +from txtai.pipeline import Questions, RAG, Similarity + + +class TestRAG(unittest.TestCase): + """ + RAG tests. + """ + + @classmethod + def setUpClass(cls): + """ + Create single rag instance. + """ + + cls.data = [ + "Giants hit 3 HRs to down Dodgers", + "Giants 5 Dodgers 4 final", + "Dodgers drop Game 2 against the Giants, 5-4", + "Blue Jays beat Red Sox final score 2-1", + "Red Sox lost to the Blue Jays, 2-1", + "Blue Jays at Red Sox is over. Score: 2-1", + "Phillies win over the Braves, 5-0", + "Phillies 5 Braves 0 final", + "Final: Braves lose to the Phillies in the series opener, 5-0", + "Lightning goaltender pulled, lose to Flyers 4-1", + "Flyers 4 Lightning 1 final", + "Flyers win 4-1", + ] + + # Create embeddings model, backed by sentence-transformers & transformers + cls.embeddings = Embeddings({"path": "sentence-transformers/nli-mpnet-base-v2"}) + + # Create rag instance + cls.rag = RAG(cls.embeddings, "distilbert-base-cased-distilled-squad") + + @classmethod + def tearDownClass(cls): + """ + Cleanup data. + """ + + if cls.embeddings: + cls.embeddings.close() + + def testAnswer(self): + """ + Test qa extraction with an answer + """ + + questions = ["What team won the game?", "What was score?"] + + # pylint: disable=C3001 + execute = lambda query: self.rag([(question, query, question, False) for question in questions], self.data) + + answers = execute("Red Sox - Blue Jays") + self.assertEqual("Blue Jays", answers[0][1]) + self.assertEqual("2-1", answers[1][1]) + + # Ad-hoc questions + question = "What hockey team won?" + + answers = self.rag([(question, question, question, False)], self.data) + self.assertEqual("Flyers", answers[0][1]) + + def testEmptyQuery(self): + """ + Test an empty queries list + """ + + self.assertEqual(self.rag.query(None, None), []) + + def testNoAnswer(self): + """ + Test qa extraction with no answer + """ + + question = "" + + answers = self.rag([(question, question, question, False)], self.data) + self.assertIsNone(answers[0][1]) + + question = "abcdef" + answers = self.rag([(question, question, question, False)], self.data) + self.assertIsNone(answers[0][1]) + + @unittest.skipIf(platform.system() == "Darwin", "Quantized models not supported on macOS") + def testQuantize(self): + """ + Test qa extraction backed by a quantized model + """ + + rag = RAG(self.embeddings, "distilbert-base-cased-distilled-squad", True) + + question = "How many home runs?" + + answers = rag([(question, question, question, True)], self.data) + self.assertIsNotNone(answers[0][1]) + + def testOutputs(self): + """ + Test output formatting rules + """ + + question = "How many home runs?" + + # Test flatten to list of answers + rag = RAG(self.embeddings, "distilbert-base-cased-distilled-squad", output="flatten") + answers = rag([(question, question, question, True)], self.data) + self.assertTrue(answers[0].startswith("Giants hit 3 HRs")) + + # Test reference field + rag = RAG(self.embeddings, "distilbert-base-cased-distilled-squad", output="reference") + answers = rag([(question, question, question, True)], self.data) + self.assertTrue(self.data[answers[0][2]].startswith("Giants hit 3 HRs")) + + def testPrompt(self): + """ + Test a user prompt with templating + """ + + embeddings = Embeddings({"path": "sentence-transformers/nli-mpnet-base-v2", "content": True}) + embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + rag = RAG( + embeddings, + "google/flan-t5-small", + template=""" + Answer the following question and return a number. + Question: {question} + Context:{context}""", + output="flatten", + ) + + self.assertEqual(rag("How many HRs"), "3") + + def testPromptTemplates(self): + """ + Test system and user prompt templates + """ + + rag = RAG( + self.embeddings, + "sshleifer/tiny-gpt2", + system="You are a friendly assistant", + template=""" + Answer the following question and return a number. + Question: {question} + Context:{context}""", + ) + + prompts = rag.prompts(["How many HRs?"], [self.data])[0] + self.assertEqual([x["role"] for x in prompts], ["system", "user"]) + + def testSearch(self): + """ + Test qa extraction with an embeddings search for context + """ + + embeddings = Embeddings({"path": "sentence-transformers/nli-mpnet-base-v2", "content": True}) + embeddings.index([(uid, text, None) for uid, text in enumerate(self.data)]) + + rag = RAG(embeddings, "distilbert-base-cased-distilled-squad") + + question = "How many home runs?" + + answers = rag([(question, question, question, True)]) + self.assertTrue(answers[0][1].startswith("Giants hit 3 HRs")) + + def testSimilarity(self): + """ + Test qa extraction using a Similarity pipeline to build context + """ + + # Create rag instance + rag = RAG(Similarity("prajjwal1/bert-medium-mnli"), Questions("distilbert-base-cased-distilled-squad")) + + question = "How many home runs?" + + answers = rag([(question, "HRs", question, True)], self.data) + self.assertTrue(answers[0][1].startswith("Giants hit 3 HRs")) + + def testSnippet(self): + """ + Test qa extraction with a full answer snippet + """ + + question = "How many home runs?" + + answers = self.rag([(question, question, question, True)], self.data) + self.assertTrue(answers[0][1].startswith("Giants hit 3 HRs")) + + def testSnippetEmpty(self): + """ + Test snippet method can handle empty parameters + """ + + self.assertEqual(self.rag.snippets(["name"], [None], [None], [None]), [("name", None)]) + + def testStringInput(self): + """ + Test with single string input + """ + + result = self.rag("How many home runs?", self.data) + self.assertEqual(result["answer"], "3") + + def testTasks(self): + """ + Test loading models with task parameter + """ + + for task, model in [ + ("language-generation", "hf-internal-testing/tiny-random-gpt2"), + ("sequence-sequence", "hf-internal-testing/tiny-random-t5"), + ]: + rag = RAG(self.embeddings, model, task=task) + self.assertIsNotNone(rag) diff --git a/test/python/testpipeline/testllm/testsequences.py b/test/python/testpipeline/testllm/testsequences.py new file mode 100644 index 0000000..8888484 --- /dev/null +++ b/test/python/testpipeline/testllm/testsequences.py @@ -0,0 +1,21 @@ +""" +Sequences module tests +""" + +import unittest + +from txtai.pipeline import Sequences + + +class TestSequences(unittest.TestCase): + """ + Sequences tests. + """ + + def testGeneration(self): + """ + Test text2text pipeline generation + """ + + model = Sequences("t5-small") + self.assertEqual(model("Testing the model", prefix="translate English to German: "), "Das Modell zu testen") diff --git a/test/python/testpipeline/testtext/__init__.py b/test/python/testpipeline/testtext/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/python/testpipeline/testtext/testentity.py b/test/python/testpipeline/testtext/testentity.py new file mode 100644 index 0000000..9868f7e --- /dev/null +++ b/test/python/testpipeline/testtext/testentity.py @@ -0,0 +1,63 @@ +""" +Entity module tests +""" + +import unittest + +from txtai.pipeline import Entity + + +class TestEntity(unittest.TestCase): + """ + Entity tests. + """ + + @classmethod + def setUpClass(cls): + """ + Create entity instance. + """ + + cls.entity = Entity("dslim/bert-base-NER") + + def testEntity(self): + """ + Test entity + """ + + # Run entity extraction + entities = self.entity("Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg") + self.assertEqual([e[0] for e in entities], ["Canada", "Manhattan"]) + + def testEntityFlatten(self): + """ + Test entity with flattened output + """ + + # Test flatten + entities = self.entity("Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg", flatten=True) + self.assertEqual(entities, ["Canada", "Manhattan"]) + + # Test flatten with join + entities = self.entity( + "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg", flatten=True, join=True + ) + self.assertEqual(entities, "Canada Manhattan") + + def testEntityTypes(self): + """ + Test entity type filtering + """ + + # Run entity extraction + entities = self.entity("Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg", labels=["PER"]) + self.assertFalse(entities) + + def testGliner(self): + """ + Test entity pipeline with a GLiNER model + """ + + entity = Entity("neuml/gliner-bert-tiny") + entities = entity("My name is John Smith.", flatten=True) + self.assertEqual(entities, ["John Smith"]) diff --git a/test/python/testpipeline/testtext/testlabels.py b/test/python/testpipeline/testtext/testlabels.py new file mode 100644 index 0000000..029316f --- /dev/null +++ b/test/python/testpipeline/testtext/testlabels.py @@ -0,0 +1,85 @@ +""" +Labels module tests +""" + +import unittest + +from txtai.pipeline import Labels + + +class TestLabels(unittest.TestCase): + """ + Labels tests. + """ + + @classmethod + def setUpClass(cls): + """ + Create single labels instance. + """ + + cls.data = [ + "US tops 5 million confirmed virus cases", + "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg", + "Beijing mobilises invasion craft along coast as Taiwan tensions escalate", + "The National Park Service warns against sacrificing slower friends in a bear attack", + "Maine man wins $1M from $25 lottery ticket", + "Make huge profits without work, earn up to $100,000 a day", + ] + + cls.labels = Labels("prajjwal1/bert-medium-mnli") + + def testLabel(self): + """ + Test labels with single text input + """ + + self.assertEqual(self.labels("This is the best sentence ever", ["positive", "negative"])[0][0], 0) + + def testLabelFlatten(self): + """ + Test labels with single text input, flattened to top text labels + """ + + self.assertEqual(self.labels("This is the best sentence ever", ["positive", "negative"], flatten=True)[0], "positive") + + def testLabelBatch(self): + """ + Test labels with multiple text inputs + """ + + results = [l[0][0] for l in self.labels(["This is the best sentence ever", "This is terrible"], ["positive", "negative"])] + self.assertEqual(results, [0, 1]) + + def testLabelBatchFlatten(self): + """ + Test labels with multiple text inputs, flattened to top text labels + """ + + results = [l[0] for l in self.labels(["This is the best sentence ever", "This is terrible"], ["positive", "negative"], flatten=True)] + self.assertEqual(results, ["positive", "negative"]) + + def testLabelFixed(self): + """ + Test labels with a fixed label text classification model + """ + + labels = Labels(dynamic=False) + + # Get index of "POSITIVE" label + index = labels.labels().index("POSITIVE") + + # Verify results + self.assertEqual(labels("This is the best sentence ever")[0][0], index) + self.assertEqual(labels("This is the best sentence ever", multilabel=True)[0][0], index) + + def testLabelFixedFlatten(self): + """ + Test labels with a fixed label text classification model, flattened to top text labels + """ + + labels = Labels(dynamic=False) + + # Verify results + self.assertEqual(labels("This is the best sentence ever", flatten=True)[0], "POSITIVE") + self.assertEqual(labels("This is the best sentence ever", multilabel=True, flatten=True)[0], "POSITIVE") diff --git a/test/python/testpipeline/testtext/testreranker.py b/test/python/testpipeline/testtext/testreranker.py new file mode 100644 index 0000000..5850570 --- /dev/null +++ b/test/python/testpipeline/testtext/testreranker.py @@ -0,0 +1,42 @@ +""" +Reranker module tests +""" + +import unittest + +from txtai import Embeddings +from txtai.pipeline import Reranker, Similarity + + +class TestReranker(unittest.TestCase): + """ + Reranker tests. + """ + + @classmethod + def setUpClass(cls): + """ + Create single labels instance. + """ + + cls.data = [ + "US tops 5 million confirmed virus cases", + "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg", + "Beijing mobilises invasion craft along coast as Taiwan tensions escalate", + "The National Park Service warns against sacrificing slower friends in a bear attack", + "Maine man wins $1M from $25 lottery ticket", + "Make huge profits without work, earn up to $100,000 a day", + ] + + def testRanker(self): + """ + Test re-ranking pipeline + """ + + embeddings = Embeddings(content=True) + embeddings.index(self.data) + + similarity = Similarity("neuml/colbert-bert-tiny", lateencode=True) + + ranker = Reranker(embeddings, similarity) + self.assertEqual(ranker("lottery winner")[0]["id"], "4") diff --git a/test/python/testpipeline/testtext/testsimilarity.py b/test/python/testpipeline/testtext/testsimilarity.py new file mode 100644 index 0000000..145d5f0 --- /dev/null +++ b/test/python/testpipeline/testtext/testsimilarity.py @@ -0,0 +1,105 @@ +""" +Similarity module tests +""" + +import unittest + +from txtai.pipeline import Similarity + + +class TestSimilarity(unittest.TestCase): + """ + Similarity tests. + """ + + @classmethod + def setUpClass(cls): + """ + Create single labels instance. + """ + + cls.data = [ + "US tops 5 million confirmed virus cases", + "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg", + "Beijing mobilises invasion craft along coast as Taiwan tensions escalate", + "The National Park Service warns against sacrificing slower friends in a bear attack", + "Maine man wins $1M from $25 lottery ticket", + "Make huge profits without work, earn up to $100,000 a day", + ] + + cls.similarity = Similarity("prajjwal1/bert-medium-mnli") + + def testCrossEncoder(self): + """ + Test cross-encoder similarity model + """ + + similarity = Similarity("cross-encoder/ms-marco-MiniLM-L-2-v2", crossencode=True) + uid = similarity("Who won the lottery?", self.data)[0][0] + self.assertEqual(self.data[uid], self.data[4]) + + def testCrossEncoderBatch(self): + """ + Test cross-encoder similarity model with multiple inputs + """ + + similarity = Similarity("cross-encoder/ms-marco-MiniLM-L-2-v2", crossencode=True) + results = [r[0][0] for r in similarity(["Who won the lottery?", "Where did an iceberg collapse?"], self.data)] + self.assertEqual(results, [4, 1]) + + def testLateEncoder(self): + """ + Test late-encoder similarity model + """ + + similarity = Similarity("neuml/pylate-bert-tiny", lateencode=True) + uid = similarity("Who won the lottery?", self.data)[0][0] + self.assertEqual(self.data[uid], self.data[4]) + + # Test encode method + # pylint: disable=E1101 + self.assertEqual(similarity.encode(["Who won the lottery?"], "data").shape, (1, 8, 128)) + + def testLateEncoderBatch(self): + """ + Test late-encoder similarity model with multiple inputs + """ + + similarity = Similarity("neuml/colbert-bert-tiny", lateencode=True) + results = [r[0][0] for r in similarity(["Who won the lottery?", "Where did an iceberg collapse?"], self.data)] + self.assertEqual(results, [4, 1]) + + def testSimilarity(self): + """ + Test similarity with single query + """ + + uid = self.similarity("feel good story", self.data)[0][0] + self.assertEqual(self.data[uid], self.data[4]) + + def testSimilarityBatch(self): + """ + Test similarity with multiple queries + """ + + results = [r[0][0] for r in self.similarity(["feel good story", "climate change"], self.data)] + self.assertEqual(results, [4, 1]) + + def testSimilarityFixed(self): + """ + Test similarity with a fixed label text classification model + """ + + similarity = Similarity(dynamic=False) + + # Test with query as label text and label id + self.assertLessEqual(similarity("negative", ["This is the best sentence ever"])[0][1], 0.1) + self.assertLessEqual(similarity("0", ["This is the best sentence ever"])[0][1], 0.1) + + def testSimilarityLong(self): + """ + Test similarity with long text + """ + + uid = self.similarity("other", ["Very long text " * 1000, "other text"])[0][0] + self.assertEqual(uid, 1) diff --git a/test/python/testpipeline/testtext/testsummary.py b/test/python/testpipeline/testtext/testsummary.py new file mode 100644 index 0000000..c3f74a5 --- /dev/null +++ b/test/python/testpipeline/testtext/testsummary.py @@ -0,0 +1,64 @@ +""" +Summary module tests +""" + +import unittest + +from txtai.pipeline import Summary + + +class TestSummary(unittest.TestCase): + """ + Summary tests. + """ + + @classmethod + def setUpClass(cls): + """ + Create single summary instance. + """ + + cls.text = ( + "Search is the base of many applications. Once data starts to pile up, users want to be able to find it. It's the foundation " + "of the internet and an ever-growing challenge that is never solved or done. The field of Natural Language Processing (NLP) is " + "rapidly evolving with a number of new developments. Large-scale general language models are an exciting new capability " + "allowing us to add amazing functionality quickly with limited compute and people. Innovation continues with new models " + "and advancements coming in at what seems a weekly basis. This article introduces txtai, an AI-powered search engine " + "that enables Natural Language Understanding (NLU) based search in any application." + ) + + cls.summary = Summary("t5-small") + + def testSummary(self): + """ + Test summarization of text + """ + + self.assertEqual(self.summary(self.text, minlength=15, maxlength=15), "the field of natural language processing (NLP) is rapidly evolving") + + def testSummaryBatch(self): + """ + Test batch summarization of text + """ + + summaries = self.summary([self.text, self.text], maxlength=15) + self.assertEqual(len(summaries), 2) + + def testSummaryNoLength(self): + """ + Test summary with no max length set + """ + + self.assertEqual( + self.summary(self.text + self.text), + "search is the base of many applications. Once data starts to pile up, users want to be able to find it. " + + "Large-scale general language models are an exciting new capability allowing us to add amazing functionality quickly " + + "with limited compute and people.", + ) + + def testSummaryShort(self): + """ + Test that summarization is skipped + """ + + self.assertEqual(self.summary("Text", maxlength=15), "Text") diff --git a/test/python/testpipeline/testtext/testtranslation.py b/test/python/testpipeline/testtext/testtranslation.py new file mode 100644 index 0000000..e42b69f --- /dev/null +++ b/test/python/testpipeline/testtext/testtranslation.py @@ -0,0 +1,175 @@ +""" +Translation module tests +""" + +import unittest +import time + +import requests + +from txtai.pipeline import Translation + + +class TestTranslation(unittest.TestCase): + """ + Translation tests. + """ + + @classmethod + def setUpClass(cls): + """ + Create single translation instance. + """ + + cls.translate = Translation() + + # Preload list of models. Handle HF Hub errors. + complete, wait = False, 1 + while not complete: + try: + cls.translate.lookup("en", "es") + complete = True + except requests.exceptions.HTTPError: + # Exponential backoff + time.sleep(wait) + + # Wait up to 16 seconds + wait = min(wait * 2, 16) + + def testDetect(self): + """ + Test language detection + """ + + test = ["This is a test language detection."] + language = self.translate.detect(test) + + self.assertListEqual(language, ["en"]) + + def testDetectWithCustomFunc(self): + """ + Test language detection with custom function + """ + + def dummy_func(text): + return ["en" for x in text] + + translate = Translation(langdetect=dummy_func) + + test = ["This is a test language detection."] + language = translate.detect(test) + + self.assertListEqual(language, ["en"]) + + def testLongTranslation(self): + """ + Test a translation longer than max tokenization length + """ + + text = "This is a test translation to Spanish. " * 100 + translation = self.translate(text, "es") + + # Validate translation text + self.assertIsNotNone(translation) + + def testM2M100Translation(self): + """ + Test a translation using M2M100 models + """ + + text = self.translate("This is a test translation to Croatian", "hr") + + # Validate translation text + self.assertEqual(text, "Ovo je testni prijevod na hrvatski") + + def testMarianTranslation(self): + """ + Test a translation using Marian models + """ + + text = "This is a test translation into Spanish" + translation = self.translate(text, "es") + + # Validate translation text + self.assertEqual(translation, "Esta es una traducción de prueba al español") + + # Validate translation back + translation = self.translate(translation, "en") + self.assertEqual(translation, text) + + def testNoLang(self): + """ + Test no matching language id + """ + + self.assertIsNone(self.translate.langid([], "zz")) + + def testNoModel(self): + """ + Test no known available model found + """ + + self.assertEqual(self.translate.modelpath("zz", "en"), "Helsinki-NLP/opus-mt-mul-en") + + def testNoTranslation(self): + """ + Test translation skipped when text already in destination language + """ + + text = "This is a test translation to English" + translation = self.translate(text, "en") + + # Validate no translation + self.assertEqual(text, translation) + + def testShowmodelsChunked(self): + """ + Test a long translation with showmodels flag. When text is chunked + by the tokenizer, results should still be properly concatenated as + a 3-tuple (translation, language, model) rather than a malformed tuple. + """ + + text = "This is a test translation to Spanish. " * 100 + result = self.translate(text, "es", showmodels=True) + + # Result should be a tuple of exactly 3 elements + self.assertIsInstance(result, tuple) + self.assertEqual(len(result), 3) + + translation, language, modelpath = result + + # Translation should be a single string, not a nested tuple + self.assertIsInstance(translation, str) + self.assertIsNotNone(translation) + self.assertGreater(len(translation), 0) + + # Language and model should be valid strings + self.assertEqual(language, "en") + self.assertIsInstance(modelpath, str) + + def testTranslationWithShowmodels(self): + """ + Test a translation using Marian models and showmodels flag to return + model and language. + """ + + text = "This is a test translation into Spanish" + result = self.translate(text, "es", showmodels=True) + + translation, language, modelpath = result + # Validate translation text + self.assertEqual(translation, "Esta es una traducción de prueba al español") + # Validate detected language + self.assertEqual(language, "en") + # Validate model + self.assertEqual(modelpath, "Helsinki-NLP/opus-mt-en-es") + + # Validate translation back + result = self.translate(translation, "en", showmodels=True) + + translation, language, modelpath = result + self.assertEqual(translation, text) + # Validate detected language + self.assertEqual(language, "es") + # Validate model + self.assertEqual(modelpath, "Helsinki-NLP/opus-mt-es-en") diff --git a/test/python/testpipeline/testtrain/__init__.py b/test/python/testpipeline/testtrain/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/python/testpipeline/testtrain/testonnx.py b/test/python/testpipeline/testtrain/testonnx.py new file mode 100644 index 0000000..4269723 --- /dev/null +++ b/test/python/testpipeline/testtrain/testonnx.py @@ -0,0 +1,161 @@ +""" +ONNX module tests +""" + +import os +import tempfile +import unittest + +from unittest.mock import patch + +from sklearn.feature_extraction.text import TfidfVectorizer +from sklearn.linear_model import LogisticRegression +from sklearn.pipeline import Pipeline + +from txtai.embeddings import Embeddings +from txtai.models import OnnxModel +from txtai.pipeline import HFOnnx, HFTrainer, Labels, MLOnnx, Questions + + +class TestOnnx(unittest.TestCase): + """ + ONNX tests. + """ + + @classmethod + def setUpClass(cls): + """ + Create default datasets. + """ + + cls.data = [{"text": "Dogs", "label": 0}, {"text": "dog", "label": 0}, {"text": "Cats", "label": 1}, {"text": "cat", "label": 1}] * 100 + + def testDefault(self): + """ + Test exporting an ONNX model with default parameters + """ + + # Export model to ONNX, use default parameters + onnx = HFOnnx() + model = onnx("google/bert_uncased_L-2_H-128_A-2") + + # Validate model has data + self.assertGreater(len(model), 0) + + # Validate model device properly works + self.assertEqual(OnnxModel(model).device, -1) + + def testClassification(self): + """ + Test exporting a classification model to ONNX and running inference + """ + + path = "google/bert_uncased_L-2_H-128_A-2" + + trainer = HFTrainer() + model, tokenizer = trainer(path, self.data) + + # Output file path + output = os.path.join(tempfile.gettempdir(), "onnx") + + # Export model to ONNX + onnx = HFOnnx() + model = onnx((model, tokenizer), "text-classification", output, True) + + # Test classification + labels = Labels((model, path), dynamic=False) + self.assertEqual(labels("cat")[0][0], 1) + + @patch("onnxruntime.get_available_providers") + @patch("torch.cuda.is_available") + def testPooling(self, cuda, providers): + """ + Test exporting a pooling model to ONNX and running inference + """ + + path = "sentence-transformers/paraphrase-MiniLM-L3-v2" + + # Export model to ONNX + onnx = HFOnnx() + model = onnx(path, "pooling", quantize=True) + + # Test no CUDA and onnxruntime installed + cuda.return_value = False + providers.return_value = ["CPUExecutionProvider"] + + embeddings = Embeddings({"path": model, "tokenizer": path}) + self.assertEqual(embeddings.similarity("animal", ["dog", "book", "rug"])[0][0], 0) + + # Test no CUDA and onnxruntime-gpu installed + cuda.return_value = False + providers.return_value = ["CUDAExecutionProvider", "CPUExecutionProvider"] + + embeddings = Embeddings({"path": model, "tokenizer": path}) + self.assertIsNotNone(embeddings) + + # Test CUDA and only onnxruntime installed + cuda.return_value = True + providers.return_value = ["CPUExecutionProvider"] + + embeddings = Embeddings({"path": model, "tokenizer": path}) + self.assertIsNotNone(embeddings) + + # Test CUDA and onnxruntime-gpu installed + cuda.return_value = True + providers.return_value = ["CUDAExecutionProvider", "CPUExecutionProvider"] + + embeddings = Embeddings({"path": model, "tokenizer": path}) + self.assertIsNotNone(embeddings) + + def testQA(self): + """ + Test exporting a QA model to ONNX and running inference + """ + + path = "distilbert-base-cased-distilled-squad" + + # Export model to ONNX + onnx = HFOnnx() + model = onnx(path, "question-answering") + + questions = Questions((model, path)) + self.assertEqual(questions(["What is the price?"], ["The price is $30"])[0], "$30") + + def testScikit(self): + """ + Test exporting a scikit-learn model to ONNX and running inference + """ + + # pylint: disable=W0613 + def tokenizer(inputs, **kwargs): + if isinstance(inputs, str): + inputs = [inputs] + + return {"input_ids": [[x] for x in inputs]} + + # Train a scikit-learn model + model = Pipeline([("tfidf", TfidfVectorizer()), ("lr", LogisticRegression())]) + model.fit([x["text"] for x in self.data], [x["label"] for x in self.data]) + + # Export model to ONNX + onnx = MLOnnx() + model = onnx(model) + + # Test classification + labels = Labels((model, tokenizer), dynamic=False) + self.assertEqual(labels("cat")[0][0], 1) + + def testZeroShot(self): + """ + Test exporting a zero shot classification model to ONNX and running inference + """ + + path = "prajjwal1/bert-medium-mnli" + + # Export model to ONNX + onnx = HFOnnx() + model = onnx(path, "zero-shot-classification", quantize=True) + + # Test zero shot classification + labels = Labels((model, path)) + self.assertEqual(labels("That is great news", ["negative", "positive"])[0][0], 1) diff --git a/test/python/testpipeline/testtrain/testquantization.py b/test/python/testpipeline/testtrain/testquantization.py new file mode 100644 index 0000000..cda9017 --- /dev/null +++ b/test/python/testpipeline/testtrain/testquantization.py @@ -0,0 +1,35 @@ +""" +Quantization module tests +""" + +import platform +import unittest + +from transformers import AutoModel + +from txtai.pipeline import HFModel, HFPipeline + + +class TestQuantization(unittest.TestCase): + """ + Quantization tests. + """ + + @unittest.skipIf(platform.system() == "Darwin", "Quantized models not supported on macOS") + def testModel(self): + """ + Test quantizing a model through HFModel. + """ + + model = HFModel(quantize=True, gpu=False) + model = model.prepare(AutoModel.from_pretrained("google/bert_uncased_L-2_H-128_A-2")) + self.assertIsNotNone(model) + + @unittest.skipIf(platform.system() == "Darwin", "Quantized models not supported on macOS") + def testPipeline(self): + """ + Test quantizing a model through HFPipeline. + """ + + pipeline = HFPipeline("text-classification", "google/bert_uncased_L-2_H-128_A-2", True, False) + self.assertIsNotNone(pipeline) diff --git a/test/python/testpipeline/testtrain/testtrainer.py b/test/python/testpipeline/testtrain/testtrainer.py new file mode 100644 index 0000000..d92ce8c --- /dev/null +++ b/test/python/testpipeline/testtrain/testtrainer.py @@ -0,0 +1,335 @@ +""" +Trainer module tests +""" + +import os +import unittest +import tempfile + +import torch + +from transformers import AutoTokenizer, AutoModelForSequenceClassification + +from txtai.data import Data +from txtai.pipeline import HFTrainer, Labels, Questions, Sequences + + +class TestTrainer(unittest.TestCase): + """ + Trainer tests. + """ + + @classmethod + def setUpClass(cls): + """ + Create default datasets. + """ + + cls.data = [{"text": "Dogs", "label": 0}, {"text": "dog", "label": 0}, {"text": "Cats", "label": 1}, {"text": "cat", "label": 1}] * 100 + + def testBasic(self): + """ + Test training a model with basic parameters + """ + + trainer = HFTrainer() + model, tokenizer = trainer("google/bert_uncased_L-2_H-128_A-2", self.data) + + labels = Labels((model, tokenizer), dynamic=False) + self.assertEqual(labels("cat")[0][0], 1) + + def testCLM(self): + """ + Test training a model with causal language modeling + """ + + trainer = HFTrainer() + + # Test default parameters + model, _ = trainer("hf-internal-testing/tiny-random-gpt2", self.data, maxlength=16, task="language-generation") + self.assertIsNotNone(model) + + # Test pack merging + model, _ = trainer("hf-internal-testing/tiny-random-gpt2", self.data, maxlength=16, task="language-generation", merge="pack") + self.assertIsNotNone(model) + + # Test no merging + model, _ = trainer("hf-internal-testing/tiny-random-gpt2", self.data, maxlength=16, task="language-generation", merge=None) + self.assertIsNotNone(model) + + def testCustom(self): + """ + Test training a model with custom parameters + """ + + # pylint: disable=E1120 + model = AutoModelForSequenceClassification.from_pretrained("google/bert_uncased_L-2_H-128_A-2") + tokenizer = AutoTokenizer.from_pretrained("google/bert_uncased_L-2_H-128_A-2") + + trainer = HFTrainer() + model, tokenizer = trainer( + (model, tokenizer), + self.data, + self.data, + columns=("text", "label"), + do_eval=True, + output_dir=os.path.join(tempfile.gettempdir(), "trainer"), + ) + + labels = Labels((model, tokenizer), dynamic=False) + self.assertEqual(labels("cat")[0][0], 1) + + def testDataFrame(self): + """ + Test training a model with a mock pandas DataFrame + """ + + class TestDataFrame: + """ + Test DataFrame + """ + + def __init__(self, data): + # Get list of columns + self.columns = list(data[0].keys()) + + # Build columnar data view + self.data = {} + for column in self.columns: + self.data[column] = Values([row[column] for row in data]) + + def __getitem__(self, column): + return self.data[column] + + class Values: + """ + Test values list + """ + + def __init__(self, values): + self.values = list(values) + + def __getitem__(self, index): + return self.values[index] + + def unique(self): + """ + Returns a list of unique values. + + Returns: + unique list of values + """ + + return set(self.values) + + # Mock DataFrame + df = TestDataFrame(self.data) + + trainer = HFTrainer() + model, tokenizer = trainer("google/bert_uncased_L-2_H-128_A-2", df) + + labels = Labels((model, tokenizer), dynamic=False) + self.assertEqual(labels("cat")[0][0], 1) + + def testDataset(self): + """ + Test training a model with a mock Hugging Face Dataset + """ + + class TestDataset(torch.utils.data.Dataset): + """ + Test Dataset + """ + + def __init__(self, data): + self.data = data + self.unique = lambda _: [0, 1] + + def __len__(self): + return len(self.data) + + def __getitem__(self, index): + return self.data[index] + + def column_names(self): + """ + Returns column names for this dataset + + Returns: + list of columns + """ + + return ["text", "label"] + + # pylint: disable=W0613 + def map(self, fn, batched, num_proc, remove_columns): + """ + Map each dataset row using fn. + + Args: + fn: function + batched: batch records + + Returns: + updated Dataset + """ + + self.data = [fn(x) for x in self.data] + return self + + ds = TestDataset(self.data) + + trainer = HFTrainer() + model, tokenizer = trainer("google/bert_uncased_L-2_H-128_A-2", ds) + + labels = Labels((model, tokenizer), dynamic=False) + self.assertEqual(labels("cat")[0][0], 1) + + def testEmpty(self): + """ + Test an empty training data object + """ + + self.assertIsNone(Data(None, None, None).process(None)) + + def testKD(self): + """ + Test knowledge distillation + """ + + # Base model + trainer = HFTrainer() + model, tokenizer = trainer("google/bert_uncased_L-2_H-128_A-2", self.data) + + # Train with knowledge distillation + model, tokenizer = trainer("google/bert_uncased_L-2_H-128_A-2", self.data, teacher=(model, tokenizer)) + + labels = Labels((model, tokenizer), dynamic=False) + self.assertEqual(labels("cat")[0][0], 1) + + def testMLM(self): + """ + Test training a model with masked language modeling. + """ + + trainer = HFTrainer() + model, _ = trainer("hf-internal-testing/tiny-random-bert", self.data, task="language-modeling") + + # Test model completed successfully + self.assertIsNotNone(model) + + def testMultiLabel(self): + """ + Test training model with labels provided as a list + """ + + data = [] + for x in self.data: + data.append({"text": x["text"], "label": [0.0, 1.0] if x["label"] else [1.0, 0.0]}) + + trainer = HFTrainer() + model, tokenizer = trainer("google/bert_uncased_L-2_H-128_A-2", data) + + labels = Labels((model, tokenizer), dynamic=False) + self.assertEqual(labels("cat")[0][0], 1) + + def testPEFT(self): + """ + Test training a model with causal language modeling and PEFT + """ + + trainer = HFTrainer() + model, _ = trainer( + "hf-internal-testing/tiny-random-gpt2", + self.data, + maxlength=16, + task="language-generation", + quantize=True, + lora=True, + ) + + # Test model completed successfully + self.assertIsNotNone(model) + + def testQA(self): + """ + Test training a QA model + """ + + # Training data + data = [ + {"question": "What ingredient?", "context": "1 can whole tomatoes", "answers": "tomatoes"}, + {"question": "What ingredient?", "context": "Crush 1 tomato", "answers": "tomato"}, + {"question": "What ingredient?", "context": "1 yellow onion", "answers": "onion"}, + {"question": "What ingredient?", "context": "Unwrap 2 red onions", "answers": "onions"}, + {"question": "What ingredient?", "context": "1 red pepper", "answers": "pepper"}, + {"question": "What ingredient?", "context": "Clean 3 red peppers", "answers": "peppers"}, + {"question": "What ingredient?", "context": "1 clove garlic", "answers": "garlic"}, + {"question": "What ingredient?", "context": "Unwrap 3 cloves of garlic", "answers": "garlic"}, + {"question": "What ingredient?", "context": "3 pieces of ginger", "answers": "ginger"}, + {"question": "What ingredient?", "context": "Peel 1 orange", "answers": "orange"}, + {"question": "What ingredient?", "context": "1/2 lb beef", "answers": "beef"}, + {"question": "What ingredient?", "context": "Roast 3 lbs of beef", "answers": "beef"}, + {"question": "What ingredient?", "context": "1 pack of chicken", "answers": "chicken"}, + {"question": "What ingredient?", "context": "Forest through the trees", "answers": None}, + ] + + trainer = HFTrainer() + model, tokenizer = trainer("google/bert_uncased_L-2_H-128_A-2", data, data, task="question-answering", num_train_epochs=40) + + questions = Questions((model, tokenizer), gpu=True) + self.assertTrue("onion" in questions(["What ingredient?"], ["Peel 1 onion"])[0]) + + def testRegression(self): + """ + Test training a model with a regression (continuous) output + """ + + data = [] + for x in self.data: + data.append({"text": x["text"], "label": x["label"] + 0.1}) + + trainer = HFTrainer() + model, tokenizer = trainer("google/bert_uncased_L-2_H-128_A-2", data) + + labels = Labels((model, tokenizer), dynamic=False) + + # Regression tasks return a single entry with the regression output + self.assertGreater(labels("cat")[0][1], 0.5) + + def testRTD(self): + """ + Test training a language model with replaced token detection + """ + + # Save directory + output = os.path.join(tempfile.gettempdir(), "trainer.rtd") + + trainer = HFTrainer() + model, _ = trainer("hf-internal-testing/tiny-random-electra", self.data, task="token-detection", output_dir=output) + + # Test model completed successfully + self.assertIsNotNone(model) + + # Test output directories exist + self.assertTrue(os.path.exists(os.path.join(output, "generator"))) + self.assertTrue(os.path.exists(os.path.join(output, "discriminator"))) + + def testSeqSeq(self): + """ + Test training a sequence-sequence model + """ + + data = [ + {"source": "Running again", "target": "Sleeping again"}, + {"source": "Run", "target": "Sleep"}, + {"source": "running", "target": "sleeping"}, + ] + + trainer = HFTrainer() + model, tokenizer = trainer("t5-small", data, task="sequence-sequence", prefix="translate Run to Sleep: ", learning_rate=1e-3) + + # Run run-sleep translation + sequences = Sequences((model, tokenizer)) + result = sequences("translate Run to Sleep: run") + self.assertEqual(result.lower(), "sleep") diff --git a/test/python/testscoring/__init__.py b/test/python/testscoring/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/python/testscoring/testkeyword.py b/test/python/testscoring/testkeyword.py new file mode 100644 index 0000000..048b514 --- /dev/null +++ b/test/python/testscoring/testkeyword.py @@ -0,0 +1,503 @@ +""" +Keyword scoring tests +""" + +import os +import tempfile +import unittest + +from unittest.mock import patch + +from txtai.scoring import Normalize, ScoringFactory, Scoring + + +# pylint: disable=R0904 +class TestKeyword(unittest.TestCase): + """ + Sparse keyword scoring tests. + """ + + @classmethod + def setUpClass(cls): + """ + Initialize test data. + """ + + cls.data = [ + "US tops 5 million confirmed virus cases", + "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg", + "Beijing mobilises invasion craft along coast as Taiwan tensions escalate", + "The National Park Service warns against sacrificing slower friends in a bear attack", + "Maine man wins $1M from $25 lottery ticket", + "wins wins wins", + "Make huge profits without work, earn up to $100,000 a day", + ] + + cls.data = [(uid, x, None) for uid, x in enumerate(cls.data)] + + def testBM25(self): + """ + Test bm25 + """ + + self.runTests("bm25") + + def testCustom(self): + """ + Test custom method + """ + + self.runTests("txtai.scoring.BM25") + + def testCustomNotFound(self): + """ + Test unresolvable custom method + """ + + with self.assertRaises(ImportError): + ScoringFactory.create("notfound.scoring") + + def testNotImplemented(self): + """ + Test exceptions for non-implemented methods + """ + + scoring = Scoring() + + self.assertRaises(NotImplementedError, scoring.insert, None, None) + self.assertRaises(NotImplementedError, scoring.delete, None) + self.assertRaises(NotImplementedError, scoring.weights, None) + self.assertRaises(NotImplementedError, scoring.search, None, None) + self.assertRaises(NotImplementedError, scoring.batchsearch, None, None, None) + self.assertRaises(NotImplementedError, scoring.count) + self.assertRaises(NotImplementedError, scoring.load, None) + self.assertRaises(NotImplementedError, scoring.save, None) + self.assertRaises(NotImplementedError, scoring.close) + self.assertRaises(NotImplementedError, scoring.issparse) + self.assertRaises(NotImplementedError, scoring.isnormalized) + self.assertRaises(NotImplementedError, scoring.isbayes) + + @patch("sqlalchemy.orm.Query.params") + def testPGText(self, query): + """ + Test PGText + """ + + # Mock database query + query.return_value = [(3, 1.0)] + + # Create scoring + path = os.path.join(tempfile.gettempdir(), "pgtext.sqlite") + scoring = ScoringFactory.create({"method": "pgtext", "url": f"sqlite:///{path}", "schema": "txtai"}) + scoring.index((uid, {"text": text}, tags) for uid, text, tags in self.data) + + # Run search and validate correct result returned + index, _ = scoring.search("bear", 1)[0] + self.assertEqual(index, 3) + + # Run batch search + index, _ = scoring.batchsearch(["bear"], 1)[0][0] + self.assertEqual(index, 3) + + # Validate save/load/delete + scoring.save(None) + scoring.load(None) + + # Validate count + self.assertEqual(scoring.count(), len(self.data)) + + # Test delete + scoring.delete([0]) + self.assertEqual(scoring.count(), len(self.data) - 1) + + # PGText is a normalized sparse index + self.assertTrue(scoring.issparse() and scoring.isnormalized() and not scoring.isbayes()) + self.assertIsNone(scoring.weights("This is a test".split())) + + # Close scoring + scoring.close() + + def testSIF(self): + """ + Test sif + """ + + self.runTests("sif") + + def testTFIDF(self): + """ + Test tfidf + """ + + self.runTests("tfidf") + + def runTests(self, method): + """ + Runs a series of tests for a scoring method. + + Args: + method: scoring method + """ + + config = {"method": method} + + self.index(config) + self.upsert(config) + self.weights(config) + self.search(config) + self.delete(config) + self.normalize(config) + self.content(config) + self.empty(config) + self.copy(config) + self.settings(config) + self.tokenization(config) + + def index(self, config, data=None): + """ + Test scoring index method. + + Args: + config: scoring config + data: data to index with scoring method + + Returns: + scoring + """ + + # Derive input data + data = data if data else self.data + + scoring = ScoringFactory.create(config) + scoring.index(data) + + keys = [k for k, v in sorted(scoring.idf.items(), key=lambda x: x[1])] + + # Test count + self.assertEqual(scoring.count(), len(data)) + + # Win should be lowest score + self.assertEqual(keys[0], "wins") + + # Test save/load + self.assertIsNotNone(self.save(scoring, config, f"scoring.{config['method']}.index")) + + # Test search returns none when terms disabled (default) + self.assertIsNone(scoring.search("query")) + + return scoring + + def upsert(self, config): + """ + Test scoring upsert method + """ + + scoring = ScoringFactory.create({**config, **{"tokenizer": {"alphanum": True, "stopwords": True}}}) + scoring.upsert(self.data) + + # Test count + self.assertEqual(scoring.count(), len(self.data)) + + # Test stop word is removed + self.assertFalse("and" in scoring.idf) + + def save(self, scoring, config, name): + """ + Test scoring index save/load. + + Args: + scoring: scoring index + config: scoring config + name: output file name + + Returns: + scoring + """ + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), "scoring") + os.makedirs(index, exist_ok=True) + + # Save scoring instance + scoring.save(f"{index}/{name}") + + # Reload scoring instance + scoring = ScoringFactory.create(config) + scoring.load(f"{index}/{name}") + + return scoring + + def weights(self, config): + """ + Test standard and tag weighted scores. + + Args: + config: scoring config + """ + + document = (1, ["bear", "wins"], None) + + scoring = self.index(config) + weights = scoring.weights(document[1]) + + # Default weights + self.assertNotEqual(weights[0], weights[1]) + + data = self.data[:] + + uid, text, _ = data[3] + data[3] = (uid, text, "wins") + + scoring = self.index(config, data) + weights = scoring.weights(document[1]) + + # Modified weights + self.assertEqual(weights[0], weights[1]) + + def search(self, config): + """ + Test scoring search. + + Args: + config: scoring config + """ + + # Create combined config + config = {**config, **{"terms": True}} + + # Create scoring instance + scoring = ScoringFactory.create(config) + scoring.index(self.data) + + # Run search and validate correct result returned + index, _ = scoring.search("bear", 1)[0] + self.assertEqual(index, 3) + + # Run batch search + index, _ = scoring.batchsearch(["bear"], 1)[0][0] + self.assertEqual(index, 3) + + # Run wildcard search + index, _ = scoring.search("bea*", 1)[0] + self.assertEqual(index, 3) + + # Test save/reload + self.save(scoring, config, f"scoring.{config['method']}.search") + + # Re-run search and validate correct result returned + index, _ = scoring.search("bear", 1)[0] + self.assertEqual(index, 3) + + def delete(self, config): + """ + Test delete. + """ + + # Create combined config + config = {**config, **{"terms": True, "content": True}} + + # Create scoring instance + scoring = ScoringFactory.create(config) + scoring.index(self.data) + + # Run search and validate correct result returned + index = scoring.search("bear", 1)[0]["id"] + + # Delete result and validate the query no longer returns results + scoring.delete([index]) + self.assertFalse(scoring.search("bear", 1)) + + # Save and validate count + self.save(scoring, config, f"scoring.{config['method']}.delete") + self.assertEqual(scoring.count(), len(self.data) - 1) + + def normalize(self, config): + """ + Test scoring search with normalized scores. + + Args: + method: scoring method + """ + + # Default normalization + scoring = ScoringFactory.create({**config, **{"terms": True, "normalize": True}}) + scoring.index(self.data) + + # Run search and validate correct result returned + index, score = scoring.search(self.data[3][1], 1)[0] + self.assertEqual(index, 3) + self.assertEqual(score, 1.0) + + # Bayesian normalization with default dynamic alpha/beta settings + baseline = ScoringFactory.create({**config, **{"terms": True}}) + baseline.index(self.data) + + scoring = ScoringFactory.create({**config, **{"terms": True, "normalize": "bayes"}}) + scoring.index(self.data) + + query = "wins" + base = baseline.search(query, 3) + bayes = scoring.search(query, 3) + + # Bayesian normalization should preserve ranking order while mapping scores to [0, 1] + self.assertEqual([uid for uid, _ in base], [uid for uid, _ in bayes]) + self.assertTrue(all(0.0 <= score <= 1.0 for _, score in bayes)) + + # BB25 alias should resolve to Bayesian normalization + scoring = ScoringFactory.create({**config, **{"terms": True, "normalize": "bb25"}}) + scoring.index(self.data) + bb25 = scoring.search(query, 3) + self.assertEqual([uid for uid, _ in base], [uid for uid, _ in bb25]) + self.assertTrue(all(0.0 <= score <= 1.0 for _, score in bb25)) + + # BB25 candidate-set behavior: zero scores remain 0, positive scores are transformed + normalizer = Normalize("bb25") + scores = normalizer([(0, 0.0), (1, 1.0), (2, 2.0)], scoring.avgscore) + self.assertEqual(scores[0][1], 0.0) + self.assertGreater(scores[1][1], 0.0) + self.assertGreater(scores[2][1], scores[1][1]) + + # Test negative scores + scores = normalizer([(0, -100.0)], scoring.avgscore) + self.assertEqual(scores[0][1], 0.0) + + # Bayesian normalization with custom parameters + config = {**config, **{"terms": True, "normalize": {"method": "bayes", "alpha": 2.0}}} + scoring = ScoringFactory.create(config) + scoring.index(self.data) + + custom = scoring.search(query, 3) + self.assertEqual([uid for uid, _ in base], [uid for uid, _ in custom]) + self.assertTrue(all(0.0 <= score <= 1.0 for _, score in custom)) + + def content(self, config): + """ + Test scoring search with content. + + Args: + config: scoring config + """ + + scoring = ScoringFactory.create({**config, **{"terms": True, "content": True}}) + scoring.index(self.data) + + # Test text with content + text = "Great news today" + scoring.index([(scoring.total, text, None)]) + + # Run search and validate correct result returned + result = scoring.search("great news", 1)[0]["text"] + self.assertEqual(result, text) + + # Test reading text from dictionary + text = "Feel good story: baby panda born" + scoring.index([(scoring.total, {"text": text}, None)]) + + # Run search and validate correct result returned + result = scoring.search("feel good story", 1)[0]["text"] + self.assertEqual(result, text) + + def empty(self, config): + """ + Test scoring index properly handles an index call when no data present. + + Args: + config: scoring config + """ + + # Create scoring index with no data + scoring = ScoringFactory.create(config) + scoring.index([]) + + # Assert index call returns and index has a count of 0 + self.assertEqual(scoring.total, 0) + + def copy(self, config): + """ + Test scoring index copy method. + """ + + # Create scoring instance + scoring = ScoringFactory.create({**config, **{"terms": True}}) + scoring.index(self.data) + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), "scoring") + os.makedirs(index, exist_ok=True) + + # Create file to test replacing existing file + path = f"{index}/scoring.{config['method']}.copy" + with open(f"{index}.terms", "w", encoding="utf-8") as f: + f.write("TEST") + + # Save scoring instance + scoring.save(path) + self.assertTrue(os.path.exists(path)) + + @patch("sys.byteorder", "big") + def settings(self, config): + """ + Test various settings. + + Args: + config: scoring config + """ + + # Create combined config + config = {**config, **{"terms": {"cachelimit": 0, "cutoff": 0.25, "wal": True}}} + + # Create scoring instance + scoring = ScoringFactory.create(config) + scoring.index(self.data) + + # Save/load index + self.save(scoring, config, f"scoring.{config['method']}.settings") + + index, _ = scoring.search("bear bear bear wins", 1)[0] + self.assertEqual(index, 3) + + # Save to same path + self.save(scoring, config, f"scoring.{config['method']}.settings") + + # Save to different path + self.save(scoring, config, f"scoring.{config['method']}.move") + + # Validate counts + self.assertEqual(scoring.count(), len(self.data)) + + def tokenization(self, config): + """ + Test tokenization methods. + + Args: + config: scoring config + """ + + # Test whitespace tokenization + config = {**config, **{"terms": True, "tokenizer": {"whitespace": True}}} + + # Create scoring instance + scoring = ScoringFactory.create(config) + scoring.index([(0, "abc-def-123", None)]) + + self.assertEqual(scoring.search("abc-def-123")[0][0], 0) + + # Test regular expression tokenization + config = {**config, **{"tokenizer": {"regexp": r"\w{5,}"}}} + + # Create scoring instance + scoring = ScoringFactory.create(config) + scoring.index([(0, "hello test", None)]) + + self.assertEqual(scoring.search("hello")[0][0], 0) + self.assertFalse(scoring.search("test")) + + # Test ngram tokenization + ngrams = {"ngrams": 3, "lpad": " ", "rpad": " ", "unique": True} + config = {**config, **{"tokenizer": {"ngrams": ngrams}}} + + # Create scoring instance + scoring = ScoringFactory.create(config) + scoring.index([(0, "hello test", None)]) + + self.assertEqual(scoring.search("hello")[0][0], 0) diff --git a/test/python/testscoring/testsparse.py b/test/python/testscoring/testsparse.py new file mode 100644 index 0000000..9d54613 --- /dev/null +++ b/test/python/testscoring/testsparse.py @@ -0,0 +1,208 @@ +""" +Sparse module tests +""" + +import os +import platform +import tempfile +import unittest + +from unittest.mock import patch + +from txtai.scoring import ScoringFactory + + +# pylint: disable=R0904 +class TestSparse(unittest.TestCase): + """ + Sparse vector scoring tests. + """ + + @classmethod + def setUpClass(cls): + """ + Initialize test data. + """ + + cls.data = [ + "US tops 5 million confirmed virus cases", + "Canada's last fully intact ice shelf has suddenly collapsed, forming a Manhattan-sized iceberg", + "Beijing mobilises invasion craft along coast as Taiwan tensions escalate", + "The National Park Service warns against sacrificing slower friends in a bear attack", + "Maine man wins $1M from $25 lottery ticket", + "Make huge profits without work, earn up to $100,000 a day", + ] + + cls.data = [(uid, x, None) for uid, x in enumerate(cls.data)] + + def testGeneral(self): + """ + Test general sparse vector operations + """ + + # Models cache + models = {} + + # Test sparse scoring + scoring = ScoringFactory.create({"method": "sparse", "path": "sparse-encoder-testing/splade-bert-tiny-nq"}, models=models) + scoring.index((uid, {"text": text}, tags) for uid, text, tags in self.data) + + # Run search and validate correct result returned + index, _ = scoring.search("lottery ticket", 1)[0] + self.assertEqual(index, 4) + + # Run batch search + index, _ = scoring.batchsearch(["lottery ticket"], 1)[0][0] + self.assertEqual(index, 4) + + # Validate count + self.assertEqual(scoring.count(), len(self.data)) + + # Test delete + scoring.delete([4]) + self.assertEqual(scoring.count(), len(self.data) - 1) + + # Run search after delete + index, _ = scoring.search("lottery ticket", 1)[0] + self.assertEqual(index, 5) + + # Sparse vectors is a normalized sparse index + self.assertTrue(scoring.issparse() and scoring.isnormalized() and not scoring.isbayes()) + self.assertIsNone(scoring.weights("This is a test".split())) + + # Close scoring + scoring.close() + + # Test model caching + scoring = ScoringFactory.create({"method": "sparse", "path": "sparse-encoder-testing/splade-bert-tiny-nq"}, models=models) + self.assertIsNotNone(scoring.model) + scoring.close() + + def testEmpty(self): + """ + Test empty sparse vectors + """ + + scoring = ScoringFactory.create({"method": "sparse", "path": "sparse-encoder-testing/splade-bert-tiny-nq"}) + scoring.upsert((uid, {"text": text}, tags) for uid, text, tags in self.data) + self.assertEqual(scoring.count(), len(self.data)) + + @unittest.skipIf(platform.system() == "Darwin", "Torch memory sharing not supported on macOS") + @patch("torch.cuda.device_count") + def testGPU(self, count): + """ + Test sparse vectors with GPU encoding + """ + + # Mock accelerator count + count.return_value = 2 + + # Test multiple gpus + scoring = ScoringFactory.create({"method": "sparse", "path": "sparse-encoder-testing/splade-bert-tiny-nq", "gpu": "all"}) + self.assertIsNotNone(scoring) + scoring.close() + + def testBayes(self): + """ + Test BB25 Bayesian normalization for sparse scoring + """ + + config = { + "method": "sparse", + "path": "sparse-encoder-testing/splade-bert-tiny-nq", + "normalize": "bb25", + } + scoring = ScoringFactory.create(config) + scoring.index((uid, {"text": text}, tags) for uid, text, tags in self.data) + + # Verify Bayesian mode flags + self.assertTrue(scoring.isbayes()) + self.assertTrue(scoring.isnormalized()) + + # Search and validate scores are calibrated probabilities in [0, 1] + results = scoring.search("lottery ticket", 3) + self.assertGreater(len(results), 0) + for _, score in results: + self.assertGreaterEqual(score, 0.0) + self.assertLessEqual(score, 1.0) + + # Batch search + results = scoring.batchsearch(["lottery ticket", "ice shelf"], 3) + self.assertEqual(len(results), 2) + for query_results in results: + for _, score in query_results: + self.assertGreaterEqual(score, 0.0) + self.assertLessEqual(score, 1.0) + + scoring.close() + + def testBayesDict(self): + """ + Test BB25 normalization with dict config + """ + + config = { + "method": "sparse", + "path": "sparse-encoder-testing/splade-bert-tiny-nq", + "normalize": {"method": "bb25", "alpha": 2.0}, + } + scoring = ScoringFactory.create(config) + scoring.index((uid, {"text": text}, tags) for uid, text, tags in self.data) + + self.assertTrue(scoring.isbayes()) + + results = scoring.search("lottery ticket", 3) + self.assertGreater(len(results), 0) + for _, score in results: + self.assertGreaterEqual(score, 0.0) + self.assertLessEqual(score, 1.0) + + scoring.close() + + def testBayesNonBayes(self): + """ + Test that non-Bayesian string normalize values do not activate Bayesian mode + """ + + config = { + "method": "sparse", + "path": "sparse-encoder-testing/splade-bert-tiny-nq", + "normalize": "default", + } + scoring = ScoringFactory.create(config) + self.assertFalse(scoring.isbayes()) + scoring.close() + + def testIVFFlat(self): + """ + Test sparse vectors with IVFFlat clustering + """ + + # Expand dataset + data = self.data * 1000 + + # Test higher volume IVFFlat index with clustering + config = { + "method": "sparse", + "vectormethod": "sentence-transformers", + "path": "sparse-encoder-testing/splade-bert-tiny-nq", + "ivfsparse": {"sample": 1.0}, + } + scoring = ScoringFactory.create(config) + scoring.index((uid, {"text": text}, tags) for uid, text, tags in data) + + # Generate temp file path + index = os.path.join(tempfile.gettempdir(), "scoring") + os.makedirs(index, exist_ok=True) + + # Save scoring instance + scoring.save(f"{index}/scoring.sparse.index") + + # Reload scoring instance + scoring = ScoringFactory.create(config) + scoring.load(f"{index}/scoring.sparse.index") + + # Run search and validate correct result returned + results = scoring.search("lottery ticket", 1) + self.assertGreater(len(results), 0) + scoring.close() diff --git a/test/python/testserialize.py b/test/python/testserialize.py new file mode 100644 index 0000000..ad70bdd --- /dev/null +++ b/test/python/testserialize.py @@ -0,0 +1,61 @@ +""" +Serialize module tests +""" + +import os +import unittest + +from unittest.mock import patch + +from txtai.serialize import Serialize, SerializeFactory + + +class TestSerialize(unittest.TestCase): + """ + Serialize tests. + """ + + def testNotImplemented(self): + """ + Test exceptions for non-implemented methods + """ + + serialize = Serialize() + + self.assertRaises(NotImplementedError, serialize.loadstream, None) + self.assertRaises(NotImplementedError, serialize.savestream, None, None) + self.assertRaises(NotImplementedError, serialize.loadbytes, None) + self.assertRaises(NotImplementedError, serialize.savebytes, None) + + def testMessagePack(self): + """ + Test MessagePack encoder + """ + + serializer = SerializeFactory.create() + self.assertEqual(serializer.loadbytes(serializer.savebytes("test")), "test") + + def testPickleDisabled(self): + """ + Test disabled pickle serialization + """ + + # Validate an error is raised + with self.assertRaises(ValueError): + serializer = SerializeFactory.create("pickle", allowpickle=True) + data = serializer.savebytes("Test") + + serializer = SerializeFactory.create("pickle") + serializer.loadbytes(data) + + @patch.dict(os.environ, {"ALLOW_PICKLE": "True"}) + def testPickleEnabled(self): + """ + Test enabled pickle serialization + """ + + # Validate a warning is raised + with self.assertWarns(RuntimeWarning): + serializer = SerializeFactory.create("pickle") + data = serializer.savebytes("Test") + serializer.loadbytes(data) diff --git a/test/python/testvectors/__init__.py b/test/python/testvectors/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/python/testvectors/testdense/__init__.py b/test/python/testvectors/testdense/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/python/testvectors/testdense/testcustom.py b/test/python/testvectors/testdense/testcustom.py new file mode 100644 index 0000000..b533f0c --- /dev/null +++ b/test/python/testvectors/testdense/testcustom.py @@ -0,0 +1,51 @@ +""" +Custom module tests +""" + +import os +import unittest + +import numpy as np + +from txtai.vectors import VectorsFactory + + +class TestCustom(unittest.TestCase): + """ + Custom vectors tests + """ + + @classmethod + def setUpClass(cls): + """ + Create custom vectors instance. + """ + + cls.model = VectorsFactory.create({"method": "txtai.vectors.HFVectors", "path": "sentence-transformers/nli-mpnet-base-v2"}, None) + + def testIndex(self): + """ + Test transformers indexing + """ + + # Generate enough volume to test batching + documents = [(x, "This is a test", None) for x in range(1000)] + + ids, dimension, batches, stream = self.model.index(documents) + + self.assertEqual(len(ids), 1000) + self.assertEqual(dimension, 768) + self.assertEqual(batches, 2) + self.assertIsNotNone(os.path.exists(stream)) + + # Test shape of serialized embeddings + with open(stream, "rb") as queue: + self.assertEqual(np.load(queue).shape, (500, 768)) + + def testNotFound(self): + """ + Test unresolvable vector backend + """ + + with self.assertRaises(ImportError): + VectorsFactory.create({"method": "notfound.vectors"}) diff --git a/test/python/testvectors/testdense/testexternal.py b/test/python/testvectors/testdense/testexternal.py new file mode 100644 index 0000000..db3ae4a --- /dev/null +++ b/test/python/testvectors/testdense/testexternal.py @@ -0,0 +1,54 @@ +""" +External module tests +""" + +import os +import unittest + +import numpy as np + +from txtai.vectors import External, VectorsFactory + + +class TestExternal(unittest.TestCase): + """ + External vectors tests + """ + + @classmethod + def setUpClass(cls): + """ + Create External vectors instance. + """ + + cls.model = VectorsFactory.create({"method": "external"}, None) + + def testIndex(self): + """ + Test indexing with external vectors + """ + + # Generate dummy data + data = np.random.rand(1000, 768).astype(np.float32) + + # Generate enough volume to test batching + documents = [(x, data[x], None) for x in range(1000)] + + ids, dimension, batches, stream = self.model.index(documents) + + self.assertEqual(len(ids), 1000) + self.assertEqual(dimension, 768) + self.assertEqual(batches, 2) + self.assertIsNotNone(os.path.exists(stream)) + + # Test shape of serialized embeddings + with open(stream, "rb") as queue: + self.assertEqual(np.load(queue).shape, (500, 768)) + + def testMethod(self): + """ + Test method is derived when transform function passed + """ + + model = VectorsFactory.create({"transform": lambda x: x}, None) + self.assertTrue(isinstance(model, External)) diff --git a/test/python/testvectors/testdense/testhuggingface.py b/test/python/testvectors/testdense/testhuggingface.py new file mode 100644 index 0000000..6fbb07c --- /dev/null +++ b/test/python/testvectors/testdense/testhuggingface.py @@ -0,0 +1,99 @@ +""" +Huggingface module tests +""" + +import os +import unittest + +import numpy as np + +from txtai.vectors import VectorsFactory + + +class TestHFVectors(unittest.TestCase): + """ + HFVectors tests + """ + + @classmethod + def setUpClass(cls): + """ + Create HFVectors instance. + """ + + cls.model = VectorsFactory.create({"path": "sentence-transformers/nli-mpnet-base-v2"}, None) + + def testIndex(self): + """ + Test transformers indexing + """ + + # Generate enough volume to test batching + documents = [(x, "This is a test", None) for x in range(1000)] + + ids, dimension, batches, stream = self.model.index(documents) + + self.assertEqual(len(ids), 1000) + self.assertEqual(dimension, 768) + self.assertEqual(batches, 2) + self.assertIsNotNone(os.path.exists(stream)) + + # Test shape of serialized embeddings + with open(stream, "rb") as queue: + self.assertEqual(np.load(queue).shape, (500, 768)) + + def testText(self): + """ + Test transformers text conversion + """ + + self.model.tokenize = True + self.assertEqual(self.model.prepare("Y 123 This is a test!"), "test") + self.assertEqual(self.model.prepare(["This", "is", "a", "test"]), "This is a test") + + self.model.tokenize = False + self.assertEqual(self.model.prepare("Y 123 This is a test!"), "Y 123 This is a test!") + self.assertEqual(self.model.prepare(["This", "is", "a", "test"]), "This is a test") + + def testTransform(self): + """ + Test transformers transform + """ + + # Sample documents: one where tokenizer changes text and one with no changes to text + documents = [(0, "This is a test and has no tokenization", None), (1, "test tokenization", None)] + + # Run with tokenization enabled + self.model.tokenize = True + embeddings1 = [self.model.transform(d) for d in documents] + + # Run with tokenization disabled + self.model.tokenize = False + embeddings2 = [self.model.transform(d) for d in documents] + + self.assertFalse(np.array_equal(embeddings1[0], embeddings2[0])) + self.assertTrue(np.array_equal(embeddings1[1], embeddings2[1])) + + def testTransformArray(self): + """ + Test transformers skips transforming NumPy arrays + """ + + # Generate data and run through vector model + data1 = np.random.rand(5, 5).astype(np.float32) + data2 = self.model.transform((0, data1, None)) + + # Test transform method returns original data + self.assertTrue(np.array_equal(data1, data2)) + + def testTransformLong(self): + """ + Test transformers transform on long text + """ + + # Sample documents: short text and longer text + documents = [(0, "This is long text " * 512, None), (1, "This is short text", None)] + + # Run transform and ensure it completes without errors + embeddings = [self.model.transform(d) for d in documents] + self.assertIsNotNone(embeddings) diff --git a/test/python/testvectors/testdense/testlitellm.py b/test/python/testvectors/testdense/testlitellm.py new file mode 100644 index 0000000..5ff334f --- /dev/null +++ b/test/python/testvectors/testdense/testlitellm.py @@ -0,0 +1,83 @@ +""" +LiteLLM module tests +""" + +import json +import os +import unittest + +from http.server import HTTPServer, BaseHTTPRequestHandler +from threading import Thread + +import numpy as np + +from txtai.vectors import VectorsFactory + + +class RequestHandler(BaseHTTPRequestHandler): + """ + Test HTTP handler. + """ + + def do_POST(self): + """ + POST request handler. + """ + + # Generate mock response + response = [[0.0] * 768] + response = json.dumps(response).encode("utf-8") + + self.send_response(200) + self.send_header("content-type", "application/json") + self.send_header("content-length", len(response)) + self.end_headers() + + self.wfile.write(response) + self.wfile.flush() + + +class TestLiteLLM(unittest.TestCase): + """ + LiteLLM vectors tests + """ + + @classmethod + def setUpClass(cls): + """ + Create mock http server. + """ + + cls.httpd = HTTPServer(("127.0.0.1", 8004), RequestHandler) + + server = Thread(target=cls.httpd.serve_forever, daemon=True) + server.start() + + @classmethod + def tearDownClass(cls): + """ + Shutdown mock http server. + """ + + cls.httpd.shutdown() + + def testIndex(self): + """ + Test indexing with LiteLLM vectors + """ + + # LiteLLM vectors instance + model = VectorsFactory.create( + {"path": "huggingface/sentence-transformers/all-MiniLM-L6-v2", "vectors": {"api_base": "http://127.0.0.1:8004"}}, None + ) + + ids, dimension, batches, stream = model.index([(0, "test", None)]) + + self.assertEqual(len(ids), 1) + self.assertEqual(dimension, 768) + self.assertEqual(batches, 1) + self.assertIsNotNone(os.path.exists(stream)) + + # Test shape of serialized embeddings + with open(stream, "rb") as queue: + self.assertEqual(np.load(queue).shape, (1, 768)) diff --git a/test/python/testvectors/testdense/testlitert.py b/test/python/testvectors/testdense/testlitert.py new file mode 100644 index 0000000..7bf9a85 --- /dev/null +++ b/test/python/testvectors/testdense/testlitert.py @@ -0,0 +1,42 @@ +""" +LiteRT module tests +""" + +import os +import unittest + +import numpy as np + +from txtai.vectors import VectorsFactory + + +class TestLiteRT(unittest.TestCase): + """ + LiteRT vectors tests + """ + + @classmethod + def setUpClass(cls): + """ + Create LiteRT instance. + """ + + cls.model = VectorsFactory.create( + {"path": "neuml/bert-hash-nano-embeddings-litert/bert-hash-nano-embeddings-int4.tflite", "gpu": False}, None + ) + + def testIndex(self): + """ + Test indexing with LiteRT vectors + """ + + ids, dimension, batches, stream = self.model.index([(0, "test", None)]) + + self.assertEqual(len(ids), 1) + self.assertEqual(dimension, 128) + self.assertEqual(batches, 1) + self.assertIsNotNone(os.path.exists(stream)) + + # Test shape of serialized embeddings + with open(stream, "rb") as queue: + self.assertEqual(np.load(queue).shape, (1, 128)) diff --git a/test/python/testvectors/testdense/testllama.py b/test/python/testvectors/testdense/testllama.py new file mode 100644 index 0000000..87e3203 --- /dev/null +++ b/test/python/testvectors/testdense/testllama.py @@ -0,0 +1,40 @@ +""" +Llama module tests +""" + +import os +import unittest + +import numpy as np + +from txtai.vectors import VectorsFactory + + +class TestLlamaCpp(unittest.TestCase): + """ + llama.cpp vectors tests + """ + + @classmethod + def setUpClass(cls): + """ + Create LlamaCpp instance. + """ + + cls.model = VectorsFactory.create({"path": "nomic-ai/nomic-embed-text-v1.5-GGUF/nomic-embed-text-v1.5.Q2_K.gguf"}, None) + + def testIndex(self): + """ + Test indexing with LlamaCpp vectors + """ + + ids, dimension, batches, stream = self.model.index([(0, "test", None)]) + + self.assertEqual(len(ids), 1) + self.assertEqual(dimension, 768) + self.assertEqual(batches, 1) + self.assertIsNotNone(os.path.exists(stream)) + + # Test shape of serialized embeddings + with open(stream, "rb") as queue: + self.assertEqual(np.load(queue).shape, (1, 768)) diff --git a/test/python/testvectors/testdense/testm2v.py b/test/python/testvectors/testdense/testm2v.py new file mode 100644 index 0000000..6e226a5 --- /dev/null +++ b/test/python/testvectors/testdense/testm2v.py @@ -0,0 +1,40 @@ +""" +Model2Vec module tests +""" + +import os +import unittest + +import numpy as np + +from txtai.vectors import VectorsFactory + + +class TestModel2Vec(unittest.TestCase): + """ + Model2vec vectors tests + """ + + @classmethod + def setUpClass(cls): + """ + Create Model2Vec instance. + """ + + cls.model = VectorsFactory.create({"path": "minishlab/potion-base-8M"}, None) + + def testIndex(self): + """ + Test indexing with Model2Vec vectors + """ + + ids, dimension, batches, stream = self.model.index([(0, "test", None)]) + + self.assertEqual(len(ids), 1) + self.assertEqual(dimension, 256) + self.assertEqual(batches, 1) + self.assertIsNotNone(os.path.exists(stream)) + + # Test shape of serialized embeddings + with open(stream, "rb") as queue: + self.assertEqual(np.load(queue).shape, (1, 256)) diff --git a/test/python/testvectors/testdense/testsbert.py b/test/python/testvectors/testdense/testsbert.py new file mode 100644 index 0000000..cd72598 --- /dev/null +++ b/test/python/testvectors/testdense/testsbert.py @@ -0,0 +1,61 @@ +""" +Sentence Transformers module tests +""" + +import os +import platform +import unittest + +from unittest.mock import patch + +import numpy as np + +from txtai.vectors import VectorsFactory + + +class TestSTVectors(unittest.TestCase): + """ + STVectors tests + """ + + def testIndex(self): + """ + Test indexing with sentence-transformers vectors + """ + + model = VectorsFactory.create({"method": "sentence-transformers", "path": "paraphrase-MiniLM-L3-v2"}, None) + ids, dimension, batches, stream = model.index([(0, "test", None)]) + + self.assertEqual(len(ids), 1) + self.assertEqual(dimension, 384) + self.assertEqual(batches, 1) + self.assertIsNotNone(os.path.exists(stream)) + + # Test shape of serialized embeddings + with open(stream, "rb") as queue: + self.assertEqual(np.load(queue).shape, (1, 384)) + + @unittest.skipIf(platform.system() == "Darwin", "Torch memory sharing not supported on macOS") + @patch("torch.cuda.device_count") + def testMultiGPU(self, count): + """ + Test multiple gpu encoding + """ + + # Mock accelerator count + count.return_value = 2 + + model = VectorsFactory.create({"method": "sentence-transformers", "path": "paraphrase-MiniLM-L3-v2", "gpu": "all"}, None) + ids, dimension, batches, stream = model.index([(0, "test", None)]) + + self.assertEqual(len(ids), 1) + self.assertEqual(dimension, 384) + self.assertEqual(batches, 1) + self.assertIsNotNone(os.path.exists(stream)) + + # Test shape of serialized embeddings + with open(stream, "rb") as queue: + self.assertEqual(np.load(queue).shape, (1, 384)) + + # Close the multiprocessing pool + model.close() diff --git a/test/python/testvectors/testdense/testvectors.py b/test/python/testvectors/testdense/testvectors.py new file mode 100644 index 0000000..e3cb0dc --- /dev/null +++ b/test/python/testvectors/testdense/testvectors.py @@ -0,0 +1,68 @@ +""" +Vectors module tests +""" + +import os +import tempfile +import unittest + +import numpy as np + +from txtai.vectors import Vectors, Recovery + + +class TestVectors(unittest.TestCase): + """ + Vectors tests. + """ + + def testNotImplemented(self): + """ + Test exceptions for non-implemented methods + """ + + vectors = Vectors(None, None, None) + + self.assertRaises(NotImplementedError, vectors.load, None) + self.assertRaises(NotImplementedError, vectors.encode, None) + + def testNormalize(self): + """ + Test batch normalize and single input normalize are equal + """ + + vectors = Vectors(None, None, None) + + # Generate data + data1 = np.random.rand(5, 5).astype(np.float32) + data2 = data1.copy() + + # Keep original data to ensure it changed + original = data1.copy() + + # Normalize data + vectors.normalize(data1) + for x in data2: + vectors.normalize(x) + + # Test both data arrays are the same and changed from original + self.assertTrue(np.allclose(data1, data2)) + self.assertFalse(np.allclose(data1, original)) + + def testRecovery(self): + """ + Test vectors recovery failure + """ + + # Checkpoint directory + checkpoint = os.path.join(tempfile.gettempdir(), "recovery") + os.makedirs(checkpoint, exist_ok=True) + + # Create empty file + # pylint: disable=R1732 + f = open(os.path.join(checkpoint, "id"), "w", encoding="utf-8") + f.close() + + # Create the recovery instance with an empty checkpoint file + recovery = Recovery(checkpoint, "id", np.load) + self.assertIsNone(recovery()) diff --git a/test/python/testvectors/testdense/testwordvectors.py b/test/python/testvectors/testdense/testwordvectors.py new file mode 100644 index 0000000..f378cf8 --- /dev/null +++ b/test/python/testvectors/testdense/testwordvectors.py @@ -0,0 +1,163 @@ +""" +WordVectors module tests +""" + +import os +import tempfile +import unittest + +from unittest.mock import patch + +import numpy as np + +from huggingface_hub.errors import HFValidationError +from txtai.vectors import VectorsFactory +from txtai.vectors.dense.words import create, transform, WordVectors + + +class TestWordVectors(unittest.TestCase): + """ + Vectors tests. + """ + + @classmethod + def setUpClass(cls): + """ + Sets the pretrained model to use + """ + + # Test with pretrained glove quantized vectors + cls.path = "neuml/glove-6B-quantized" + + @patch("os.cpu_count") + def testIndex(self, cpucount): + """ + Test word vectors indexing + """ + + # Mock CPU count + cpucount.return_value = 1 + + # Generate data + documents = [(x, "This is a test", None) for x in range(1000)] + + model = VectorsFactory.create({"path": self.path, "parallel": True}, None) + + ids, dimension, batches, stream = model.index(documents, 1) + + self.assertEqual(len(ids), 1000) + self.assertEqual(dimension, 300) + self.assertEqual(batches, 1000) + self.assertIsNotNone(os.path.exists(stream)) + + # Test shape of serialized embeddings + with open(stream, "rb") as queue: + self.assertEqual(np.load(queue).shape, (1, 300)) + + @patch("os.cpu_count") + def testIndexBatch(self, cpucount): + """ + Test word vectors indexing with batch size set + """ + + # Mock CPU count + cpucount.return_value = 1 + + # Generate data + documents = [(x, "This is a test", None) for x in range(1000)] + + model = VectorsFactory.create({"path": self.path, "parallel": True}, None) + + ids, dimension, batches, stream = model.index(documents, 512) + + self.assertEqual(len(ids), 1000) + self.assertEqual(dimension, 300) + self.assertEqual(batches, 2) + self.assertIsNotNone(os.path.exists(stream)) + + # Test shape of serialized embeddings + with open(stream, "rb") as queue: + self.assertEqual(np.load(queue).shape, (512, 300)) + self.assertEqual(np.load(queue).shape, (488, 300)) + + def testIndexSerial(self): + """ + Test word vector indexing in single process mode + """ + + # Generate data + documents = [(x, "This is a test", None) for x in range(1000)] + + model = VectorsFactory.create({"path": self.path, "parallel": False}, None) + + ids, dimension, batches, stream = model.index(documents, 1) + + self.assertEqual(len(ids), 1000) + self.assertEqual(dimension, 300) + self.assertEqual(batches, 1000) + self.assertIsNotNone(os.path.exists(stream)) + + # Test shape of serialized embeddings + with open(stream, "rb") as queue: + self.assertEqual(np.load(queue).shape, (1, 300)) + + def testIndexSerialBatch(self): + """ + Test word vector indexing in single process mode with batch size set + """ + + # Generate data + documents = [(x, "This is a test", None) for x in range(1000)] + + model = VectorsFactory.create({"path": self.path, "parallel": False}, None) + + ids, dimension, batches, stream = model.index(documents, 512) + + self.assertEqual(len(ids), 1000) + self.assertEqual(dimension, 300) + self.assertEqual(batches, 2) + self.assertIsNotNone(os.path.exists(stream)) + + # Test shape of serialized embeddings + with open(stream, "rb") as queue: + self.assertEqual(np.load(queue).shape, (512, 300)) + self.assertEqual(np.load(queue).shape, (488, 300)) + + def testLookup(self): + """ + Test word vector lookup + """ + + model = VectorsFactory.create({"path": self.path}, None) + self.assertEqual(model.lookup(["txtai", "embeddings", "sentence"]).shape, (3, 300)) + + def testMultiprocess(self): + """ + Test multiprocess helper methods + """ + + create({"path": self.path}, None) + + uid, vector = transform((0, "test", None)) + self.assertEqual(uid, 0) + self.assertEqual(vector.shape, (300,)) + + def testNoExist(self): + """ + Test loading model that doesn't exist + """ + + # Test non-existent local path raises an exception + with self.assertRaises((IOError, HFValidationError)): + VectorsFactory.create({"method": "words", "path": os.path.join(tempfile.gettempdir(), "noexist")}, None) + + # Test non-existent hub path is handled properly + self.assertFalse(WordVectors.ismodel("invalid/user/noexist")) + + def testTransform(self): + """ + Test word vector transform + """ + + model = VectorsFactory.create({"path": self.path}, None) + self.assertEqual(len(model.transform((None, ["txtai"], None))), 300) diff --git a/test/python/testvectors/testsparse/__init__.py b/test/python/testvectors/testsparse/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/python/testvectors/testsparse/testsbert.py b/test/python/testvectors/testsparse/testsbert.py new file mode 100644 index 0000000..f18af43 --- /dev/null +++ b/test/python/testvectors/testsparse/testsbert.py @@ -0,0 +1,32 @@ +""" +Sparse Sentence Transformers module tests +""" + +import os +import unittest + +from txtai.vectors import SparseVectorsFactory +from txtai.util import SparseArray + + +class TestSparseSTVectors(unittest.TestCase): + """ + SparseSTVectors tests + """ + + def testIndex(self): + """ + Test indexing with sentence-transformers vectors + """ + + model = SparseVectorsFactory.create({"method": "sentence-transformers", "path": "sparse-encoder-testing/splade-bert-tiny-nq"}) + ids, dimension, batches, stream = model.index([(0, "test", None)]) + + self.assertEqual(len(ids), 1) + self.assertEqual(dimension, 30522) + self.assertEqual(batches, 1) + self.assertIsNotNone(os.path.exists(stream)) + + # Test shape of serialized embeddings + with open(stream, "rb") as queue: + self.assertEqual(SparseArray().load(queue).shape, (1, 30522)) diff --git a/test/python/testvectors/testsparse/testvectors.py b/test/python/testvectors/testsparse/testvectors.py new file mode 100644 index 0000000..6fc114a --- /dev/null +++ b/test/python/testvectors/testsparse/testvectors.py @@ -0,0 +1,48 @@ +""" +Sparse Vectors module tests +""" + +import unittest + +from txtai.vectors import SparseVectors, SparseVectorsFactory + + +class TestSparseVectors(unittest.TestCase): + """ + Sparse Vectors tests. + """ + + def testCustom(self): + """ + Test custom sparse vectors instance + """ + + self.assertIsNotNone( + SparseVectorsFactory.create({"method": "txtai.vectors.SparseSTVectors", "path": "sparse-encoder-testing/splade-bert-tiny-nq"}) + ) + + def testDefaultNormalize(self): + """ + Test defaultnormalize method + """ + + vectors = SparseVectors(None, None, None) + self.assertFalse(vectors.defaultnormalize()) + + def testNotSupported(self): + """ + Test exceptions for unsupported methods + """ + + vectors = SparseVectors(None, None, None) + + self.assertRaises(ValueError, vectors.truncate, None) + self.assertRaises(ValueError, vectors.quantize, None) + + def testNotFound(self): + """ + Test unresolvable vector backend + """ + + with self.assertRaises(ImportError): + SparseVectorsFactory.create({"method": "notfound.vectors"}) diff --git a/test/python/testworkflow.py b/test/python/testworkflow.py new file mode 100644 index 0000000..8a14805 --- /dev/null +++ b/test/python/testworkflow.py @@ -0,0 +1,609 @@ +""" +Workflow module tests +""" + +import contextlib +import glob +import io +import os +import tempfile +import sys +import unittest + +import numpy as np +import torch + +from txtai.api import API +from txtai.embeddings import Documents, Embeddings +from txtai.pipeline import Nop, Segmentation, Summary, Translation, Textractor +from txtai.workflow import ( + Workflow, + Task, + ConsoleTask, + ExportTask, + FileTask, + ImageTask, + RagTask, + RetrieveTask, + StorageTask, + TemplateTask, + WorkflowTask, +) + +# pylint: disable=C0411 +from utils import Utils + + +# pylint: disable=R0904 +class TestWorkflow(unittest.TestCase): + """ + Workflow tests. + """ + + @classmethod + def setUpClass(cls): + """ + Initialize test data. + """ + + # Default YAML workflow configuration + cls.config = """ + # Embeddings index + writable: true + embeddings: + scoring: bm25 + path: google/bert_uncased_L-2_H-128_A-2 + content: true + + # Text segmentation + segmentation: + sentences: true + + # Workflow definitions + workflow: + index: + tasks: + - action: segmentation + - action: index + search: + tasks: + - search + transform: + tasks: + - transform + """ + + def testBaseWorkflow(self): + """ + Test a basic workflow + """ + + translate = Translation() + + # Workflow that translate text to Spanish + workflow = Workflow([Task(lambda x: translate(x, "es"))]) + + results = list(workflow(["The sky is blue", "Forest through the trees"])) + + self.assertEqual(len(results), 2) + + def testChainWorkflow(self): + """ + Test a chain of workflows + """ + + workflow1 = Workflow([Task(lambda x: [y * 2 for y in x])]) + workflow2 = Workflow([Task(lambda x: [y - 1 for y in x])], batch=4) + + results = list(workflow2(workflow1([1, 2, 4, 8, 16, 32]))) + self.assertEqual(results, [1, 3, 7, 15, 31, 63]) + + def testComplexWorkflow(self): + """ + Test a complex workflow + """ + + textractor = Textractor(paragraphs=True, minlength=150, join=True) + summary = Summary("t5-small") + + embeddings = Embeddings({"path": "sentence-transformers/nli-mpnet-base-v2"}) + documents = Documents() + + def index(x): + documents.add(x) + return x + + # Extract text and summarize articles + articles = Workflow([FileTask(textractor), Task(lambda x: summary(x, maxlength=15))]) + + # Complex workflow that extracts text, runs summarization then loads into an embeddings index + tasks = [WorkflowTask(articles, r".\.pdf$"), Task(index, unpack=False)] + + data = ["file://" + Utils.PATH + "/article.pdf", "Workflows can process audio files, documents and snippets"] + + # Convert file paths to data tuples + data = [(x, element, None) for x, element in enumerate(data)] + + # Execute workflow, discard results as they are streamed + workflow = Workflow(tasks) + data = list(workflow(data)) + + # Build the embeddings index + embeddings.index(documents) + + # Cleanup temporary storage + documents.close() + + # Run search and validate result + index, _ = embeddings.search("search text", 1)[0] + self.assertEqual(index, 0) + self.assertEqual(data[0][1], "txtai builds an AI-powered index over sections") + + def testConcurrentWorkflow(self): + """ + Test running concurrent task actions + """ + + nop = Nop() + + workflow = Workflow([Task([nop, nop], concurrency="thread")]) + results = list(workflow([2, 4])) + self.assertEqual(results, [(2, 2), (4, 4)]) + + workflow = Workflow([Task([nop, nop], concurrency="process")]) + results = list(workflow([2, 4])) + self.assertEqual(results, [(2, 2), (4, 4)]) + + workflow = Workflow([Task([nop, nop], concurrency="unknown")]) + results = list(workflow([2, 4])) + self.assertEqual(results, [(2, 2), (4, 4)]) + + def testConsoleWorkflow(self): + """ + Test a console task + """ + + # Excel export + workflow = Workflow([ConsoleTask()]) + + output = io.StringIO() + with contextlib.redirect_stdout(output): + list(workflow([{"id": 1, "text": "Sentence 1"}, {"id": 2, "text": "Sentence 2"}])) + + self.assertIn("Sentence 2", output.getvalue()) + + def testExportWorkflow(self): + """ + Test an export task + """ + + # Excel export + path = os.path.join(tempfile.gettempdir(), "export.xlsx") + workflow = Workflow([ExportTask(output=path)]) + list(workflow([{"id": 1, "text": "Sentence 1"}, {"id": 2, "text": "Sentence 2"}])) + self.assertGreater(os.path.getsize(path), 0) + + # Export CSV + path = os.path.join(tempfile.gettempdir(), "export.csv") + workflow = Workflow([ExportTask(output=path)]) + list(workflow([{"id": 1, "text": "Sentence 1"}, {"id": 2, "text": "Sentence 2"}])) + self.assertGreater(os.path.getsize(path), 0) + + # Export CSV with timestamp + path = os.path.join(tempfile.gettempdir(), "export-timestamp.csv") + workflow = Workflow([ExportTask(output=path, timestamp=True)]) + list(workflow([{"id": 1, "text": "Sentence 1"}, {"id": 2, "text": "Sentence 2"}])) + + # Find timestamped file and ensure it has data + path = glob.glob(os.path.join(tempfile.gettempdir(), "export-timestamp*.csv"))[0] + self.assertGreater(os.path.getsize(path), 0) + + def testExtractWorkflow(self): + """ + Test column extraction tasks + """ + + workflow = Workflow([Task(lambda x: x, unpack=False, column=0)], batch=1) + + results = list(workflow([(0, 1)])) + self.assertEqual(results[0], 0) + + results = list(workflow([(0, (1, 2), None)])) + self.assertEqual(results[0], (0, 1, None)) + + results = list(workflow([1])) + self.assertEqual(results[0], 1) + + def testImageWorkflow(self): + """ + Test an image task + """ + + workflow = Workflow([ImageTask()]) + + results = list(workflow([Utils.PATH + "/books.jpg"])) + + self.assertEqual(results[0].size, (1024, 682)) + + def testInvalidWorkflow(self): + """ + Test task with invalid parameters + """ + + with self.assertRaises(TypeError): + Task(invalid=True) + + def testMergeWorkflow(self): + """ + Test merge tasks + """ + + task = Task([lambda x: [pow(y, 2) for y in x], lambda x: [pow(y, 3) for y in x]], merge="hstack") + + # Test hstack (column-wise) merge + workflow = Workflow([task]) + results = list(workflow([2, 4])) + self.assertEqual(results, [(4, 8), (16, 64)]) + + # Test vstack (row-wise) merge + task.merge = "vstack" + results = list(workflow([2, 4])) + self.assertEqual(results, [4, 8, 16, 64]) + + # Test concat (values joined into single string) merge + task.merge = "concat" + results = list(workflow([2, 4])) + self.assertEqual(results, ["4. 8", "16. 64"]) + + # Test no merge + task.merge = None + results = list(workflow([2, 4, 6])) + self.assertEqual(results, [[4, 16, 36], [8, 64, 216]]) + + # Test generated (id, data, tag) tuples are properly returned + workflow = Workflow([Task(lambda x: [(0, y, None) for y in x])]) + results = list(workflow([(1, "text", "tags")])) + self.assertEqual(results[0], (0, "text", None)) + + def testMergeUnbalancedWorkflow(self): + """ + Test merge tasks with unbalanced outputs (i.e. one action produce more output than another for same input). + """ + + nop = Nop() + segment1 = Segmentation(sentences=True) + + task = Task([nop, segment1]) + + # Test hstack + workflow = Workflow([task]) + results = list(workflow(["This is a test sentence. And another sentence to split."])) + self.assertEqual( + results, [("This is a test sentence. And another sentence to split.", ["This is a test sentence.", "And another sentence to split."])] + ) + + # Test vstack + task.merge = "vstack" + workflow = Workflow([task]) + results = list(workflow(["This is a test sentence. And another sentence to split."])) + self.assertEqual( + results, ["This is a test sentence. And another sentence to split.", "This is a test sentence.", "And another sentence to split."] + ) + + def testNumpyWorkflow(self): + """ + Test a numpy workflow + """ + + task = Task([lambda x: np.power(x, 2), lambda x: np.power(x, 3)], merge="hstack") + + # Test hstack (column-wise) merge + workflow = Workflow([task]) + results = list(workflow(np.array([2, 4]))) + self.assertTrue(np.array_equal(np.array(results), np.array([[4, 8], [16, 64]]))) + + # Test vstack (row-wise) merge + task.merge = "vstack" + results = list(workflow(np.array([2, 4]))) + self.assertEqual(results, [4, 8, 16, 64]) + + # Test no merge + task.merge = None + results = list(workflow(np.array([2, 4, 6]))) + self.assertTrue(np.array_equal(np.array(results), np.array([[4, 16, 36], [8, 64, 216]]))) + + def testRetrieveWorkflow(self): + """ + Test a retrieve task + """ + + # Test retrieve with generated temporary directory + workflow = Workflow([RetrieveTask()]) + results = list(workflow(["file://" + Utils.PATH + "/books.jpg"])) + self.assertTrue(results[0].endswith("books.jpg")) + + # Test retrieve with specified temporary directory + workflow = Workflow([RetrieveTask(directory=os.path.join(tempfile.gettempdir(), "retrieve"))]) + results = list(workflow(["file://" + Utils.PATH + "/books.jpg"])) + self.assertTrue(results[0].endswith("books.jpg")) + + # Test with directory structures + workflow = Workflow([RetrieveTask(flatten=False)]) + results = list(workflow(["file://" + Utils.PATH + "/books.jpg"])) + self.assertTrue(results[0].endswith("books.jpg") and "txtai" in results[0]) + + def testScheduleWorkflow(self): + """ + Test workflow schedules + """ + + # Test workflow schedule with Python + workflow = Workflow([Task()]) + workflow.schedule("* * * * * *", ["test"], 1) + self.assertEqual(len(workflow.tasks), 1) + + # Test workflow schedule with YAML + workflow = """ + segmentation: + sentences: true + workflow: + segment: + schedule: + cron: '* * * * * *' + elements: + - a sentence to segment + iterations: 1 + tasks: + - action: segmentation + task: console + """ + + output = io.StringIO() + with contextlib.redirect_stdout(output): + app = API(workflow) + app.wait() + + self.assertIn("a sentence to segment", output.getvalue()) + + def testScheduleErrorWorkflow(self): + """ + Test workflow schedules with errors + """ + + def action(elements): + raise FileNotFoundError + + # Test workflow proceeds after exception raised + with self.assertLogs() as logs: + workflow = Workflow([Task(action=action)]) + workflow.schedule("* * * * * *", ["test"], 1) + + self.assertIn("FileNotFoundError", " ".join(logs.output)) + + def testStorageWorkflow(self): + """ + Test a storage task + """ + + workflow = Workflow([StorageTask()]) + + results = list(workflow(["local://" + Utils.PATH, "test string"])) + + self.assertEqual(len(results), 22) + + def testTemplateInput(self): + """ + Test template task input + """ + + workflow = Workflow([TemplateTask(template="This is a {text}")]) + + # Test with string inputs + results = list(workflow(["prompt"])) + self.assertEqual(results[0], "This is a prompt") + + # Test with dict inputs + results = list(workflow([{"text": "prompt"}])) + self.assertEqual(results[0], "This is a prompt") + + # Test with tuple inputs + workflow = Workflow([TemplateTask(template="This is a {arg0}", unpack=False)]) + results = list(workflow([("prompt",)])) + self.assertEqual(results[0], "This is a prompt") + + # Test invalid inputs + with self.assertRaises(KeyError): + workflow = Workflow([TemplateTask(template="No variables")]) + results = list(workflow([{"unused": "prompt"}])) + + # Test no template + workflow = Workflow([TemplateTask()]) + results = list(workflow(["prompt"])) + self.assertEqual(results[0], "prompt") + + def testTemplateRules(self): + """ + Test template task rules + """ + + # Test rule applied + workflow = Workflow([TemplateTask(template="This is a {text}", rules={"text": "Test skip"})]) + results = list(workflow([{"text": "Test skip"}])) + self.assertEqual(results[0], "Test skip") + + # Test rule not applied + results = list(workflow([{"text": "prompt"}])) + self.assertEqual(results[0], "This is a prompt") + + def testTemplateRag(self): + """ + Test rag template task + """ + + # Test outputs + workflow = Workflow([RagTask(template="This is a {text}")]) + results = list(workflow(["prompt"])) + self.assertEqual(results[0], {"query": "prompt", "question": "This is a prompt"}) + + # Test partial outputs + workflow = Workflow([RagTask(template="This is a {text}")]) + results = list(workflow([{"query": "query", "question": "prompt"}])) + self.assertEqual(results[0], {"query": "query", "question": "This is a prompt"}) + + # Test additional template parameters + workflow = Workflow([RagTask(template="This is a {text} with another {param}")]) + results = list(workflow([{"query": "query", "question": "prompt", "param": "value"}])) + self.assertEqual(results[0], {"query": "query", "question": "This is a prompt with another value", "param": "value"}) + + def testTensorTransformWorkflow(self): + """ + Test a tensor workflow with list transformations + """ + + # Test one-one list transformation + task = Task(lambda x: x.tolist()) + workflow = Workflow([task]) + results = list(workflow(np.array([2]))) + self.assertEqual(results, [2]) + + # Test one-many list transformation + task = Task(lambda x: [x.tolist() * 2]) + workflow = Workflow([task]) + results = list(workflow(np.array([2]))) + self.assertEqual(results, [2, 2]) + + def testTorchWorkflow(self): + """ + Test a torch workflow + """ + + # pylint: disable=E1101,E1102 + task = Task([lambda x: torch.pow(x, 2), lambda x: torch.pow(x, 3)], merge="hstack") + + # Test hstack (column-wise) merge + workflow = Workflow([task]) + results = np.array([x.numpy() for x in workflow(torch.tensor([2, 4]))]) + self.assertTrue(np.array_equal(results, np.array([[4, 8], [16, 64]]))) + + # Test vstack (row-wise) merge + task.merge = "vstack" + results = list(workflow(torch.tensor([2, 4]))) + self.assertEqual(results, [4, 8, 16, 64]) + + # Test no merge + task.merge = None + results = np.array([x.numpy() for x in workflow(torch.tensor([2, 4, 6]))]) + self.assertTrue(np.array_equal(np.array(results), np.array([[4, 16, 36], [8, 64, 216]]))) + + def testYamlFunctionWorkflow(self): + """ + Test YAML workflow with a function action + """ + + # Create function and add to module + def action(elements): + return [x * 2 for x in elements] + + sys.modules[__name__].action = action + + workflow = """ + workflow: + run: + tasks: + - testworkflow.action + """ + + app = API(workflow) + self.assertEqual(list(app.workflow("run", [1, 2])), [2, 4]) + + def testYamlIndexWorkflow(self): + """ + Test reading a YAML index workflow in Python. + """ + + app = API(self.config) + self.assertEqual( + list(app.workflow("index", ["This is a test sentence. And another sentence to split."])), + ["This is a test sentence.", "And another sentence to split."], + ) + + # Read from file + path = os.path.join(tempfile.gettempdir(), "workflow.yml") + with open(path, "w", encoding="utf-8") as f: + f.write(self.config) + + app = API(path) + self.assertEqual( + list(app.workflow("index", ["This is a test sentence. And another sentence to split."])), + ["This is a test sentence.", "And another sentence to split."], + ) + + # Read from YAML object + app = API(API.read(self.config)) + self.assertEqual( + list(app.workflow("index", ["This is a test sentence. And another sentence to split."])), + ["This is a test sentence.", "And another sentence to split."], + ) + + def testYamlSearchWorkflow(self): + """ + Test reading a YAML search workflow in Python. + """ + + # Test search + app = API(self.config) + list(app.workflow("index", ["This is a test sentence. And another sentence to split."])) + self.assertEqual( + list(app.workflow("search", ["another"]))[0]["text"], + "And another sentence to split.", + ) + + def testYamlWorkflowTask(self): + """ + Test YAML workflow with a workflow task + """ + + # Create function and add to module + def action(elements): + return [x * 2 for x in elements] + + sys.modules[__name__].action = action + + workflow = """ + workflow: + run: + tasks: + - testworkflow.action + flow: + tasks: + - run + """ + + app = API(workflow) + self.assertEqual(list(app.workflow("flow", [1, 2])), [2, 4]) + + def testYamlTransformWorkflow(self): + """ + Test reading a YAML transform workflow in Python. + """ + + # Test search + app = API(self.config) + self.assertEqual(len(list(app.workflow("transform", ["text"]))[0]), 128) + + def testYamlError(self): + """ + Test reading a YAML workflow with errors. + """ + + # Read from string + config = """ + # Workflow definitions + workflow: + error: + tasks: + - action: error + """ + + with self.assertRaises(KeyError): + API(config) diff --git a/test/python/utils.py b/test/python/utils.py new file mode 100644 index 0000000..7ecec3d --- /dev/null +++ b/test/python/utils.py @@ -0,0 +1,11 @@ +""" +Utils module +""" + + +class Utils: + """ + Utility constants and methods + """ + + PATH = "/tmp/txtai"